usb Mass Strorage分析(1)


接上一篇: usb-skeleton.c到 usb core层的分析

对于存储设备的USB,Linux源码中有关于USB MASS Storage 的驱动程序(/drivers/usb/storage下),其中/drivers/usb/storage/usb.c 实现了驱动初始化,和usb-skeleton.c 例子以一样,调用    retval = usb_register(&usb_storage_driver); 注册usb设备,

static struct usb_driver usb_storage_driver = {
    .name =        "usb-storage",
    .probe =    storage_probe,
    .disconnect =    storage_disconnect,
#ifdef CONFIG_PM
    .suspend =    storage_suspend,
    .resume =    storage_resume,
    .reset_resume =    storage_reset_resume,
#endif
    .pre_reset =    storage_pre_reset,
    .post_reset =    storage_post_reset,
    .id_table =    storage_usb_ids,
    .soft_unbind =    1,
};

static int storage_probe(struct usb_interface *intf,
             const struct usb_device_id *id)
{


.....................

    /*
     * Ask the SCSI layer to allocate a host structure, with extra
     * space at the end for our private us_data structure.
     */
    host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));
    if (!host) {
        printk(KERN_WARNING USB_STORAGE
            "Unable to allocate the scsi host\n");
        return -ENOMEM;
    }

 
    result = get_device_info(us, id);
    if (result)
        goto BadDevice;

    result = get_transport(us);   //获得usb设备端的的数据,并进行相应设置,见下面
    if (result)
        goto BadDevice;
    result = get_protocol(us);   //获得usb设备端协议的数据,并进行相应设置,见下面
    if (result)
        goto BadDevice;
    result = get_pipes(us);    //获得endpoint管道的数据,并进行相应设置,见下面
    if (result)
        goto BadDevice;
.........................

    result = usb_stor_acquire_resources(us);   //获得资源,开启了一个控制内核的线程static int usb_stor_control_thread(void * __us),用于接收上层命令,该线程会在有命令的时候(休眠等待),调用struct us_data(在storage中定义的数据)中的proto_handler方法,从而调用transport,向usbcore发送命令。非常重要,见下面代码
    if (result)
        goto BadDevice;
    result = scsi_add_host(host, &intf->dev);    增加到SCSI host
    if (result) {
        printk(KERN_WARNING USB_STORAGE
            "Unable to add the scsi host\n");
        goto BadDevice;
    }

    /* Start up the thread for delayed SCSI-device scanning */
    th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan");
   
}

流程结构图:

usb Mass Strorage分析(1)_第1张图片

以下是进行usb设备端信息获取,同时设置us参数的函数get_transport()、get_protocal()、getPipes(),对应着上图中的Protocal Layer 和 Transfer Layer,分别在/drivers/usb/storage/protocal.c、transport.c中实现。初始化完成了交给SCSI层的接口后就由上层处理。




static int get_transport(struct us_data *us)
{
    switch (us->protocol) {
    case US_PR_CB:
        us->transport_name = "Control/Bulk";
        us->transport = usb_stor_CB_transport;
        us->transport_reset = usb_stor_CB_reset;   //在transport.c中定义 usb_stor_CB_reset
        us->max_lun = 7;
        break;

    case US_PR_CBI:
        us->transport_name = "Control/Bulk/Interrupt";
        us->transport = usb_stor_CBI_transport;
        us->transport_reset = usb_stor_CB_reset;  //同上
        us->max_lun = 7;
        break;

    case US_PR_BULK:
        us->transport_name = "Bulk";
        us->transport = usb_stor_Bulk_transport;
        us->transport_reset = usb_stor_Bulk_reset;//同上
        break;

#ifdef CONFIG_USB_STORAGE_USBAT
    case US_PR_USBAT:
        us->transport_name = "Shuttle USBAT";
        us->transport = usbat_transport;
        us->transport_reset = usb_stor_CB_reset;//同上
        us->max_lun = 1;
        break;
#endif

#ifdef CONFIG_USB_STORAGE_SDDR09
    case US_PR_EUSB_SDDR09:
        us->transport_name = "EUSB/SDDR09";
        us->transport = sddr09_transport;
        us->transport_reset = usb_stor_CB_reset;
        us->max_lun = 0;
        break;
#endif

#ifdef CONFIG_USB_STORAGE_SDDR55
    case US_PR_SDDR55:
        us->transport_name = "SDDR55";
        us->transport = sddr55_transport;
        us->transport_reset = sddr55_reset;
        us->max_lun = 0;
        break;
#endif

#ifdef CONFIG_USB_STORAGE_DPCM
    case US_PR_DPCM_USB:
        us->transport_name = "Control/Bulk-EUSB/SDDR09";
        us->transport = dpcm_transport;
        us->transport_reset = usb_stor_CB_reset;
        us->max_lun = 1;
        break;
#endif

#ifdef CONFIG_USB_STORAGE_FREECOM
    case US_PR_FREECOM:
        us->transport_name = "Freecom";
        us->transport = freecom_transport;
        us->transport_reset = usb_stor_freecom_reset;
        us->max_lun = 0;
        break;
#endif

#ifdef CONFIG_USB_STORAGE_DATAFAB
    case US_PR_DATAFAB:
        us->transport_name  = "Datafab Bulk-Only";
        us->transport = datafab_transport;
        us->transport_reset = usb_stor_Bulk_reset;
        us->max_lun = 1;
        break;
#endif

#ifdef CONFIG_USB_STORAGE_JUMPSHOT
    case US_PR_JUMPSHOT:
        us->transport_name  = "Lexar Jumpshot Control/Bulk";
        us->transport = jumpshot_transport;
        us->transport_reset = usb_stor_Bulk_reset;
        us->max_lun = 1;
        break;
#endif

#ifdef CONFIG_USB_STORAGE_ALAUDA
    case US_PR_ALAUDA:
        us->transport_name  = "Alauda Control/Bulk";
        us->transport = alauda_transport;
        us->transport_reset = usb_stor_Bulk_reset;
        us->max_lun = 1;
        break;
#endif

#ifdef CONFIG_USB_STORAGE_KARMA
    case US_PR_KARMA:
        us->transport_name = "Rio Karma/Bulk";
        us->transport = rio_karma_transport;
        us->transport_reset = usb_stor_Bulk_reset;
        break;
#endif

    default:
        return -EIO;
    }
    US_DEBUGP("Transport: %s\n", us->transport_name);

    /* fix for single-lun devices */
    if (us->fflags & US_FL_SINGLE_LUN)
        us->max_lun = 0;
    return 0;
}


static int get_protocol(struct us_data *us)
{
    switch (us->subclass) {
    case US_SC_RBC:
        us->protocol_name = "Reduced Block Commands (RBC)";
        us->proto_handler = usb_stor_transparent_scsi_command;   //在protocal.c中定义
        break;

    case US_SC_8020:
        us->protocol_name = "8020i";
        us->proto_handler = usb_stor_ATAPI_command;             //同上
        us->max_lun = 0;
        break;

    case US_SC_QIC:
        us->protocol_name = "QIC-157";
        us->proto_handler = usb_stor_qic157_command;    //同上
        us->max_lun = 0;
        break;

    case US_SC_8070:
        us->protocol_name = "8070i";
        us->proto_handler = usb_stor_ATAPI_command;
        us->max_lun = 0;
        break;

    case US_SC_SCSI:
        us->protocol_name = "Transparent SCSI";
        us->proto_handler = usb_stor_transparent_scsi_command;
        break;

    case US_SC_UFI:
        us->protocol_name = "Uniform Floppy Interface (UFI)";
        us->proto_handler = usb_stor_ufi_command;
        break;

#ifdef CONFIG_USB_STORAGE_ISD200
    case US_SC_ISD200:
        us->protocol_name = "ISD200 ATA/ATAPI";
        us->proto_handler = isd200_ata_command;
        break;
#endif

#ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB
    case US_SC_CYP_ATACB:
        us->protocol_name = "Transparent SCSI with Cypress ATACB";
        us->proto_handler = cypress_atacb_passthrough;
        break;
#endif

    default:
        return -EIO;
    }
    US_DEBUGP("Protocol: %s\n", us->protocol_name);
    return 0;
}

/* Get the pipe settings */
static int get_pipes(struct us_data *us)
{
    struct usb_host_interface *altsetting =
        us->pusb_intf->cur_altsetting;
    int i;
    struct usb_endpoint_descriptor *ep;
    struct usb_endpoint_descriptor *ep_in = NULL;
    struct usb_endpoint_descriptor *ep_out = NULL;
    struct usb_endpoint_descriptor *ep_int = NULL;

    /*
     * Find the first endpoint of each type we need.
     * We are expecting a minimum of 2 endpoints - in and out (bulk).
     * An optional interrupt-in is OK (necessary for CBI protocol).
     * We will ignore any others.
     */
    for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
        ep = &altsetting->endpoint[i].desc;

        if (usb_endpoint_xfer_bulk(ep)) {
            if (usb_endpoint_dir_in(ep)) {
                if (!ep_in)
                    ep_in = ep;
            } else {
                if (!ep_out)
                    ep_out = ep;
            }
        }

        else if (usb_endpoint_is_int_in(ep)) {
            if (!ep_int)
                ep_int = ep;
        }
    }

    if (!ep_in || !ep_out || (us->protocol == US_PR_CBI && !ep_int)) {
        US_DEBUGP("Endpoint sanity check failed! Rejecting dev.\n");
        return -EIO;
    }

    /* Calculate and store the pipe values */
    us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0);
    us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
    us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev,
        ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
    us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev,
        ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
    if (ep_int) {
        us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev,
            ep_int->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
        us->ep_bInterval = ep_int->bInterval;
    }
    return 0;
}


static int usb_stor_control_thread(void * __us)      //drivers/usb/storage/usb.c中
{
    struct us_data *us = (struct us_data *)__us;
    struct Scsi_Host *host = us_to_host(us);

    for(;;) {
        US_DEBUGP("*** thread sleeping.\n");
        if (wait_for_completion_interruptible(&us->cmnd_ready))   // 加入等待队列,cmnd_ready表示有命令到来,那什么时候通知呢,下面解释
            break;

        US_DEBUGP("*** thread awakened.\n");

        /* lock the device pointers */
        mutex_lock(&(us->dev_mutex));

        /* lock access to the state */
        scsi_lock(host);

        /* When we are called with no command pending, we're done */
        if (us->srb == NULL) {
            scsi_unlock(host);
            mutex_unlock(&us->dev_mutex);
            US_DEBUGP("-- exiting\n");
            break;
        }

        /* has the command timed out *already* ? */
        if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
            us->srb->result = DID_ABORT << 16;
            goto SkipForAbort;
        }

        scsi_unlock(host);

        /* reject the command if the direction indicator
         * is UNKNOWN
         */
        if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) {
            US_DEBUGP("UNKNOWN data direction\n");
            us->srb->result = DID_ERROR << 16;
        }

        /* reject if target != 0 or if LUN is higher than
         * the maximum known LUN
         */
        else if (us->srb->device->id &&
                !(us->fflags & US_FL_SCM_MULT_TARG)) {
            US_DEBUGP("Bad target number (%d:%d)\n",
                  us->srb->device->id, us->srb->device->lun);
            us->srb->result = DID_BAD_TARGET << 16;
        }

        else if (us->srb->device->lun > us->max_lun) {
            US_DEBUGP("Bad LUN (%d:%d)\n",
                  us->srb->device->id, us->srb->device->lun);
            us->srb->result = DID_BAD_TARGET << 16;
        }

        /* Handle those devices which need us to fake
         * their inquiry data */
        else if ((us->srb->cmnd[0] == INQUIRY) &&
                (us->fflags & US_FL_FIX_INQUIRY)) {
            unsigned char data_ptr[36] = {
                0x00, 0x80, 0x02, 0x02,
                0x1F, 0x00, 0x00, 0x00};

            US_DEBUGP("Faking INQUIRY command\n");
            fill_inquiry_response(us, data_ptr, 36);
            us->srb->result = SAM_STAT_GOOD;
        }

        /* we've got a command, let's do it! */
        else {
            US_DEBUG(usb_stor_show_command(us->srb));
            us->proto_handler(us->srb, us);
        }

        /* lock access to the state */
        scsi_lock(host);

        /* indicate that the command is done */
        if (us->srb->result != DID_ABORT << 16) {
            US_DEBUGP("scsi cmd done, result=0x%x\n",
                   us->srb->result);
            us->srb->scsi_done(us->srb);
        } else {
SkipForAbort:
            US_DEBUGP("scsi command aborted\n");
        }

        /* If an abort request was received we need to signal that
         * the abort has finished.  The proper test for this is
         * the TIMED_OUT flag, not srb->result == DID_ABORT, because
         * the timeout might have occurred after the command had
         * already completed with a different result code. */
        if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
            complete(&(us->notify));

            /* Allow USB transfers to resume */
            clear_bit(US_FLIDX_ABORTING, &us->dflags);
            clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);
        }

        /* finished working on this command */
        us->srb = NULL;
        scsi_unlock(host);

        /* unlock the device pointers */
        mutex_unlock(&us->dev_mutex);
    } /* for (;;) */

    /* Wait until we are told to stop */
    for (;;) {
        set_current_state(TASK_INTERRUPTIBLE);
        if (kthread_should_stop())
            break;
        schedule();
    }
    __set_current_state(TASK_RUNNING);
    return 0;
}   
跟踪以下cmnd_ready在哪里被通知有命令到来,发现了在scsiglue.c中的
queuecommand函数(该函数实现了获取命令和唤醒usb_stor_control_thread内核线程)调用了它,那谁又调用了queuecommand函数呢,发现了在scsiglue.c中有一个结构体在很多地方都用到了:

struct scsi_host_template usb_stor_host_template = {
     。。。。。。。。。。。。。
    /* command interface -- queued only */
    .queuecommand =            queuecommand,    //注册quequecommand函数
 
    。。。。。。。。。。
}

usb_stor_host_template在/drivers/usb/storage/usb.c的probe函数中storage_probe的

host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us)); 语句被加到了Scsi_Host中,后来通过scsi_add_host注册到了SCSI层,因此,调用的顺序为上层的SCSI 的scsi_dispatch_cmd(structscsi_cmnd*cmd) -> queuecommand->complete(cmnd_ready)唤醒usb_stor_control_thread处理SCSI Command set。


 USB Mass Storage层与SCSI层沟通结构体:Struct Scsi_Host、scsi_host_template, 这两个结构体一般通过SCSI层的scsi_host_alloc函数与Mass Storage的struct us_data结构体关联,通过scsi_add_host注册到SCSI层,scsi_host_template中除了queuecommand函数外,还有其他类似函数被注册到SCSI层。

针对Storage层自己实现的机制多线程,将命令接收下来进入队列,只是为了提高效率而已,也可将usb_stor_control_thread线程 与queuecommand函数合并处理(不考虑效率)。scsi_host_template的完整定义为:

struct scsi_host_template usb_stor_host_template = {
    /* basic userland interface stuff */
    .name =                "usb-storage",
    .proc_name =            "usb-storage",
    .proc_info =            proc_info,
    .info =                host_info,

    /* command interface -- queued only */
    .queuecommand =            queuecommand,

    /* error and abort handlers */
    .eh_abort_handler =        command_abort,
    .eh_device_reset_handler =    device_reset,
    .eh_bus_reset_handler =        bus_reset,

    /* queue commands only, only one command per LUN */
    .can_queue =            1,
    .cmd_per_lun =            1,

    /* unknown initiator id */
    .this_id =            -1,

    .slave_alloc =            slave_alloc,
    .slave_configure =        slave_configure,

    /* lots of sg segments can be handled */
    .sg_tablesize =            SG_ALL,

    /* limit the total size of a transfer to 120 KB */
    .max_sectors =                  240,

    /* merge commands... this seems to help performance, but
     * periodically someone should test to see which setting is more
     * optimal.
     */
    .use_clustering =        1,

    /* emulated HBA */
    .emulated =            1,

    /* we do our own delay after a device or bus reset */
    .skip_settle_delay =        1,

    /* sysfs device attributes */
    .sdev_attrs =            sysfs_device_attr_list,

    /* module management */
    .module =            THIS_MODULE
};




你可能感兴趣的:(usb Mass Strorage分析(1))