UFS BSG

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

源码:GitHub - westerndigitalcorporation/ufs-utils: The UFS Tool project have been created to allow access UFS device from user space, and perform basic set of UFS operations: Read and write UFS device configuration (flags, attributes, descriptors), FFU, etc... The set of UFS Tool features is co-existing and updated beside BSG and SG infrastructure in Linux Kernel. The source code of ufs-tool is almost ready and will be uploaded soon!

即可控制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

关键函数

  1. 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值