SG Scsi Generic
drivers/scsi/Makefile
obj-$(CONFIG_CHR_DEV_SG) += sg.o
scsi_mod-$(CONFIG_BLK_DEV_BSG) += scsi_bsg.o
driver/scsi/Kconfig
config CHR_DEV_SG
tristate "SCSI generic support"
depends on SCSI
help
If you want to use SCSI scanners, synthesizers or CD-writers or just
about anything having "SCSI" in its name other than hard disks,
CD-ROMs or tapes, say Y here. These won't be supported by the kernel
directly, so you need some additional software which knows how to
talk to these devices using the SCSI protocol:
For scanners, look at SANE (<http://www.sane-project.org/>). For CD
writer software look at Cdrtools
(<http://cdrtools.sourceforge.net/>)
and for burning a "disk at once": CDRDAO
(<http://cdrdao.sourceforge.net/>). Cdparanoia is a high
quality digital reader of audio CDs (<http://www.xiph.org/paranoia/>)
For other devices, it's possible that you'll have to write the
driver software yourself. Please read the file
<file:Documentation/scsi/scsi-generic.rst> for more information.
To compile this driver as a module, choose M here and read
<file:Documentation/scsi/scsi.rst>. The module will be called sg.
If unsure, say N.
config BLK_DEV_BSG
bool "/dev/bsg support (SG v4)"
depends on SCSI
default y
help
Saying Y here will enable generic SG (SCSI generic) v4 support
for any SCSI device.
This option is required by UDEV to access device serial numbers, etc.
If unsure, say Y.
勾选后,会生成用户态节点/dev/bsg/ufs-bsg0
使用工具ufs-utils
即可控制host发送UIC command操作UFS PHY
JESD220E中 chpater9 UFS UIC Layer: MIPI UNIPRO
ufs-utils工具包
https://github.com/westerndigitalcorporation/ufs-utils
uic命令举例
" Eg :\n" " 1. Set local PA_TxTrailingClocks:\n" " %s uic -t 1 -w 0x44 -i 0x1564 --local -p /dev/ufs-bsg\n" " 2. Read peer and local PA_TxTrailingClocks:\n" " %s uic -t 1 -r -i 0x1564 -p /dev/ufs-bsg\n"; 读取所以寄存器 ufs-utils uic -t 0 -a -p /dev/bsg/ufs-bsg0
参考第一条写
ufs-utils -t 1 -w 0x44 -i 0x1564 --locl -p /dev/ufs-bsg 如果配置rmmi的0xa为0xd ufs-utils -t 4(新增type, 3为0 xd开头,4为0x8开头) -w 0xd(写的内容) -i 0x800a --local -p /dev/ufs-bsg
Select 是有特定的参数-s指定的
Ufsutils uic命令的原理
Ufsutils uic命令支持读写Unipro Layer的各种属性信息.这些属性信息最终是通过dme_set(get)获取的.
命令:
ufs-utils uic -t 0 -r -i 0xc1 -s 4 -p /dev/bsg/ufs-bsg0
用户态节点
/dev/bsg/ufs-bsg0
默认即有/dev/bsg路径(应该和宏BLK_DEV_BSG默认开始有关,相关描述在/driver/scsi/Kconfig中), 其相关的文件为/driver/scsi/scsi_bsg.c, Makefile的描述为:
scsi_mod-$(CONFIG_BLK_DEV_BSG) += scsi_bsg.o
如果打开宏CONFIG_SCSI_UFS_BSG, 则/dev/bsg/下会出现ufs-bsg0/1的子节点
这个宏在MAKEFILE中关联的.c文件为/driver/ufs/core/ufs_bsg.c:
ufshcd-core-$(CONFIG_SCSI_UFS_BSG) += ufs_bsg.o
关键函数
-
unipro.c
ufs-utils工具函数ufshcd_dme_get_attr直接返回读到的内容
static int ufshcd_dme_get_attr(int fd, __u32 attr_sel, __u8 peer)
{
...
uic_cmd->command = peer ? UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
uic_cmd->argument1 = attr_sel;
bsg_req.msgcode = UPIU_TRANSACTION_UIC_CMD;//在ufs_bsg_probe用到了
do {
rt = send_bsg_scsi_trs(fd, &bsg_req, &bsg_rsp,
sizeof(struct ufs_bsg_request),
sizeof(struct ufs_bsg_reply), 0, 0, 0);
if (rt) {
print_error("%s: bsg request failed", __func__);
rt = ERROR;
continue;
}
} while (rt && (uic_cmd->command == UIC_CMD_DME_PEER_GET) && --retries);
//如果rt非0,则执行错误流程
if (rt)
goto out;
//拷贝io后的upiu_rsp,并拷贝到uic_rsq
memcpy(&uic_rsq, &bsg_rsp.upiu_rsp.uc, UIC_CMD_SIZE);
//检查argment2是否返回正确的标志
res_code = uic_rsq.argument2 & MASK_UIC_COMMAND_RESULT;
if (res_code) {
//错误处理
} else {
//如果正常就返回rt,即读到的内容
rt = uic_rsq.argument3;
}
out:
return rt;
}
2. scsi_bsg_utils.c
返回值:成功与否
/**
* send_bsg_scsi_trs - Utility function for SCSI transport cmd sending
* @fd: ufs bsg driver file descriptor
* @request_buff: pointer to the Query Request
* @reply_buff: pointer to the Query Response
* @req_buf_len: Query Request data length
* @reply_buf_len: Query Response data length
* @data_buf: pointer to the data buffer
* @write: If data_buff is not NULL, it indicates that this is a data write request or data read
*
* The function using ufs bsg infrastructure in linux kernel (/dev/ufs-bsg)
* in order to send Query request command
**/
int send_bsg_scsi_trs(int fd, void *request_buff, void *reply_buff, __u32 req_buf_len,
__u32 reply_buf_len, __u32 data_buf_len, __u8 *data_buf, bool write) {
//将传入的参数组装成io_hdr_v4结构体
struct sg_io_v4 io_hdr_v4 = { 0 };
...
io_hdr_v4.guard = 'Q';
io_hdr_v4.protocol = BSG_PROTOCOL_SCSI;
io_hdr_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT;
io_hdr_v4.response = (__u64)reply_buff;
io_hdr_v4.max_response_len = reply_buf_len;
io_hdr_v4.request_len = req_buf_len;
io_hdr_v4.request = (__u64)request_buff;
...
//陷入内核态,内核scsi_ioctl.c处理
while (((ret = ioctl(fd, SG_IO, &io_hdr_v4)) < 0) &&
((errno == EINTR) || (errno == EAGAIN)))
;
}
对应与bsg_ioctl.c +102
static long bsg_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
/*
* SCSI/sg ioctls
*/
case SG_IO:
return bsg_sg_io(bd, file->f_mode, uarg);
}
调用
static int bsg_sg_io(struct bsg_device *bd, fmode_t mode, void __user *uarg)
{
struct sg_io_v4 hdr;
int ret;
if (copy_from_user(&hdr, uarg, sizeof(hdr)))
return -EFAULT;
if (hdr.guard != 'Q')
return -EINVAL;
ret = bd->sg_io_fn(bd->queue, &hdr, mode, bsg_timeout(bd, &hdr));
if (!ret && copy_to_user(uarg, &hdr, sizeof(hdr)))
return -EFAULT;
return ret;
}
bd->sg_io_fn函数赋值逻辑 bsg.c +185:
struct bsg_device *bsg_register_queue(struct request_queue *q,
struct device *parent, const char *name, bsg_sg_io_fn *sg_io_fn)
{
struct bsg_device *bd;
int ret;
bd = kzalloc(sizeof(*bd), GFP_KERNEL);
if (!bd)
return ERR_PTR(-ENOMEM);
bd->max_queue = BSG_DEFAULT_CMDS;
bd->reserved_size = INT_MAX;
bd->queue = q;
bd->sg_io_fn = sg_io_fn;
由如下函数调用 bsg-lib.c +353
struct request_queue *bsg_setup_queue(struct device *dev, const char *name,
bsg_job_fn *job_fn, bsg_timeout_fn *timeout, int dd_job_size)
{
bset->bd = bsg_register_queue(q, dev, name, bsg_transport_sg_io_fn);
}
其中的bsg_transport_sg_io_fn是一个 bsg-lib.c +28定义的函数,指定了队列成员排到后具体的hook实现
bsg_setup_queue又是被
int ufs_bsg_probe(struct ufs_hba *hba) {
q = bsg_setup_queue(bsg_dev, dev_name(bsg_dev), ufs_bsg_request, NULL, 0);
}
调用,这样和下面的
描述对应起来了
3. 关于第一步中用到的bsg_req.msgcode = UPIU_TRANSACTION_UIC_CMD;
开启宏CONFIG_SCSI_UFS_BSG的条件下ufs_bsg.c +200
int ufs_bsg_probe(struct ufs_hba *hba)
{
...
dev_set_name(bsg_dev, "ufs-bsg%u", shost->host_no);
...
q = bsg_setup_queue(bsg_dev, dev_name(bsg_dev), ufs_bsg_request, NULL, 0);
}
static int ufs_bsg_request(struct bsg_job *job)
{
struct ufs_bsg_request *bsg_request = job->request;
...
msgcode = bsg_request->msgcode;
switch (msgcode) {
case UPIU_TRANSACTION_UIC_CMD:
memcpy(&uc, &bsg_request->upiu_req.uc, UIC_CMD_SIZE);
ret = ufshcd_send_uic_cmd(hba, &uc);
if (ret)
dev_err(hba->dev,
"send uic cmd: error code %d\n", ret);
memcpy(&bsg_reply->upiu_rsp.uc, &uc, UIC_CMD_SIZE);
...
}
ufshcd_send_uic_cmd
__ufshcd_send_uic_cmd
ufshcd_dispatch_uic_cmd
/**
* ufshcd_dispatch_uic_cmd - Dispatch an UIC command to the Unipro layer
* @hba: per adapter instance
* @uic_cmd: UIC command
*/
static inline void
ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
{
lockdep_assert_held(&hba->uic_cmd_mutex);
WARN_ON(hba->active_uic_cmd);
hba->active_uic_cmd = uic_cmd;
/* Write Args */
ufshcd_writel(hba, uic_cmd->argument1, REG_UIC_COMMAND_ARG_1);
ufshcd_writel(hba, uic_cmd->argument2, REG_UIC_COMMAND_ARG_2);
ufshcd_writel(hba, uic_cmd->argument3, REG_UIC_COMMAND_ARG_3);
ufshcd_add_uic_command_trace(hba, uic_cmd, UFS_CMD_SEND);
/* Write UIC Cmd */
ufshcd_writel(hba, uic_cmd->command & COMMAND_OPCODE_MASK,
REG_UIC_COMMAND);
}
注意区分bsg_setup_queue函数的第三个参数bsg_job_fn *job_fn(bsg job handler)
/**
* bsg_setup_queue - Create and add the bsg hooks so we can receive requests
* @dev: device to attach bsg device to
* @name: device to give bsg device
* @job_fn: bsg job handler
* @timeout: timeout handler function pointer
* @dd_job_size: size of LLD data needed for each job
*/
struct request_queue *bsg_setup_queue(struct device *dev, const char *name,
bsg_job_fn *job_fn, bsg_timeout_fn *timeout, int dd_job_size)
和在bsg_setup_queue函数里面调用的
bset->bd = bsg_register_queue(q, dev, name, bsg_transport_sg_io_fn);
里的第四个参数bsg_transport_sg_io_fn(bsg-lib.c内部实现函数,表示)
调用关系为:
ufs_bsg_probe
bsg_setup_queue(bsg_dev, dev_name(bsg_dev), ufs_bsg_request, NULL, 0);
bset->bd = bsg_register_queue(q, dev, name, bsg_transport_sg_io_fn);
最后需要重点关注函数
bsg_transport_sg_io_fn
构建队列的基本处理流程
https://blog.csdn.net/fudan_abc/article/details/1966538
机制和下面的描述基本一致
https://blog.csdn.net/brucexu1978/article/details/7392602