Linux之 USB驱动框架-usb U盘驱动源码分析(8)

一、U盘驱动

USB Mass Storage是一类USB存储设备,这些设备包括USB磁盘、USB硬盘、USB磁带机、USB光驱、U盘、记忆棒、智能卡和一些USB摄像头等,这类设备由USB协议支持。
首先我想去看看/driver/usb/storage/Makefile

ccflags-y := -I $(srctree)/drivers/scsi

ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=USB_STORAGE

obj-$(CONFIG_USB_UAS)           += uas.o
obj-$(CONFIG_USB_STORAGE)       += usb-storage.o

usb-storage-y := scsiglue.o protocol.o transport.o usb.o
usb-storage-y += initializers.o sierra_ms.o option_ms.o
usb-storage-y += usual-tables.o
usb-storage-$(CONFIG_USB_STORAGE_DEBUG) += debug.o

obj-$(CONFIG_USB_STORAGE_ALAUDA)        += ums-alauda.o
obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += ums-cypress.o
obj-$(CONFIG_USB_STORAGE_DATAFAB)       += ums-datafab.o
obj-$(CONFIG_USB_STORAGE_ENE_UB6250)    += ums-eneub6250.o
obj-$(CONFIG_USB_STORAGE_FREECOM)       += ums-freecom.o
obj-$(CONFIG_USB_STORAGE_ISD200)        += ums-isd200.o
obj-$(CONFIG_USB_STORAGE_JUMPSHOT)      += ums-jumpshot.o
obj-$(CONFIG_USB_STORAGE_KARMA)         += ums-karma.o
obj-$(CONFIG_USB_STORAGE_ONETOUCH)      += ums-onetouch.o
obj-$(CONFIG_USB_STORAGE_REALTEK)       += ums-realtek.o
obj-$(CONFIG_USB_STORAGE_SDDR09)        += ums-sddr09.o
obj-$(CONFIG_USB_STORAGE_SDDR55)        += ums-sddr55.o
obj-$(CONFIG_USB_STORAGE_USBAT)         += ums-usbat.o

ums-alauda-y            := alauda.o
ums-cypress-y           := cypress_atacb.o
ums-datafab-y           := datafab.o
ums-eneub6250-y         := ene_ub6250.o
ums-freecom-y           := freecom.o
ums-isd200-y            := isd200.o
ums-jumpshot-y          := jumpshot.o
ums-karma-y             := karma.o
ums-onetouch-y          := onetouch.o
ums-realtek-y           := realtek_cr.o
ums-sddr09-y            := sddr09.o
ums-sddr55-y            := sddr55.o
ums-usbat-y             := shuttle_usbat.o
 

 Makefile中前几行代码,在此我进行一个说明。第一行,-I选项表示需要编译的目录。当本Makefile文件被编译器读取时,会先判断/driver/scsi目录下的文件是否已经被编译,如果没有被编译,则先编译该目录下的文件后,再转到该Makefile文件中。第三行就是USB Mass Storage选项,是总指挥。第四、五行说明了这个文件夹也就是usb-storage模块必须包含的文件,这些文件将是主要分析的对象。第六行是调试部分。目前我们分析USB驱动,所以重点去分析这些文件中的usb.c

开始-》

static struct usb_driver usb_storage_driver = {
        .name =         DRV_NAME,
        .probe =        storage_probe,
        .disconnect =   usb_stor_disconnect,
        .suspend =      usb_stor_suspend,
        .resume =       usb_stor_resume,
        .reset_resume = usb_stor_reset_resume,
        .pre_reset =    usb_stor_pre_reset,
        .post_reset =   usb_stor_post_reset,
        .id_table =     usb_storage_usb_ids,
        .supports_autosuspend = 1,
        .soft_unbind =  1,
};

module_usb_stor_driver(usb_storage_driver, usb_stor_host_template, DRV_NAME);
 

 #define module_usb_stor_driver(__driver, __sht, __name) \
static int __init __driver##_init(void) \
{ \
        usb_stor_host_template_init(&(__sht), __name, THIS_MODULE); \
        return usb_register(&(__driver)); \
} \

 usb_stor_host_template_init

void usb_stor_host_template_init(struct scsi_host_template *sht,
                                 const char *name, struct module *owner)
{
        *sht = usb_stor_host_template;
        sht->name = name;
        sht->proc_name = name;
        sht->module = owner;
}
EXPORT_SYMBOL_GPL(usb_stor_host_template_init);
 

 下面重点我们来看看这个probe函数:

/* The main probe routine for standard devices */
static int storage_probe(struct usb_interface *intf,
                         const struct usb_device_id *id)
{
        const struct us_unusual_dev *unusual_dev;
        struct us_data *us;
        int result;
        int size;

        /* If uas is enabled and this device can do uas then ignore it. */
#if IS_ENABLED(CONFIG_USB_UAS)
        if (uas_use_uas_driver(intf, id, NULL))  //检测匹配
                return -ENXIO;
#endif

        /*
         * If the device isn't standard (is handled by a subdriver
         * module) then don't accept it.
         */
        if (usb_usual_ignore_device(intf))
                return -ENXIO;

        /*
         * Call the general probe procedures.
         *
         * The unusual_dev_list array is parallel to the usb_storage_usb_ids
         * table, so we use the index of the id entry to find the
         * corresponding unusual_devs entry.
         */

        size = ARRAY_SIZE(us_unusual_dev_list);
        if (id >= usb_storage_usb_ids && id < usb_storage_usb_ids + size) {
                unusual_dev = (id - usb_storage_usb_ids) + us_unusual_dev_list;
        } else {
                unusual_dev = &for_dynamic_ids;

                dev_dbg(&intf->dev, "Use Bulk-Only transport with the Transparent SCSI protocol for dynamic id: 0x%04x 0x%04x\n",
                        id->idVendor, id->idProduct);
        }

        result = usb_stor_probe1(&us, intf, id, unusual_dev,
                                 &usb_stor_host_template);/探测的第一部分
        if (result)
                return result;

        /* No special transport or protocol settings in the main module */

        result = usb_stor_probe2(us);//探测的第二部分
        return result;
}

 我们发现U盘驱动的探测分为两个部分,我们先来看看第一个部分usb_stor_probe1

usb_stor_probe1

/* First part of general USB mass-storage probing */
int usb_stor_probe1(struct us_data **pus,
                struct usb_interface *intf,
                const struct usb_device_id *id,
                const struct us_unusual_dev *unusual_dev,
                struct scsi_host_template *sht)
{
        struct Scsi_Host *host;
        struct us_data *us;
        int result;

        dev_info(&intf->dev, "USB Mass Storage device detected\n");

        /*
         * 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(sht, sizeof(*us)); //分配Scsi_Host结构体
        if (!host) {
                dev_warn(&intf->dev, "Unable to allocate the scsi host\n");
                return -ENOMEM;
        }

        /*
         * Allow 16-byte CDBs and thus > 2TB
         */
        host->max_cmd_len = 16;
        host->sg_tablesize = usb_stor_sg_tablesize(intf);
        *pus = us = host_to_us(host);         /从host结构体中提取出us_data结构体
        mutex_init(&(us->dev_mutex));
        us_set_lock_class(&us->dev_mutex, intf);
        init_completion(&us->cmnd_ready);        //初始化完成量
        init_completion(&(us->notify));
        init_waitqueue_head(&us->delay_wait);        //初始化等待队列头
        INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork);

        /* Associate the us_data structure with the USB device */
        result = associate_dev(us, intf);       //将us_data与USB设备相关联
        if (result)
                goto BadDevice;

        /* Get the unusual_devs entries and the descriptors */
        result = get_device_info(us, id, unusual_dev);  ;//获取设备信息
        if (result)
                goto BadDevice;

        /* Get standard transport and protocol settings */
        get_transport(us);                        //获取传输方式
        get_protocol(us);                        //获取传输协议

        /*
         * Give the caller a chance to fill in specialized transport
         * or protocol settings.
         */
        return 0;

BadDevice:
        usb_stor_dbg(us, "storage_probe() failed\n");
        release_everything(us);
        return result;
}
EXPORT_SYMBOL_GPL(usb_stor_probe1);
 

 U盘驱动的探测的第二部分usb_stor_probe2

/* Second part of general USB mass-storage probing */
int usb_stor_probe2(struct us_data *us)
{
        int result;
        struct device *dev = &us->pusb_intf->dev;

        /* Make sure the transport and protocol have both been set */
        if (!us->transport || !us->proto_handler) {
                result = -ENXIO;
                goto BadDevice;
        }
        usb_stor_dbg(us, "Transport: %s\n", us->transport_name);
        usb_stor_dbg(us, "Protocol: %s\n", us->protocol_name);

        if (us->fflags & US_FL_SCM_MULT_TARG) {
                /*
                 * SCM eUSCSI bridge devices can have different numbers
                 * of LUNs on different targets; allow all to be probed.
                 */
                us->max_lun = 7;
                /* The eUSCSI itself has ID 7, so avoid scanning that */
                us_to_host(us)->this_id = 7;
                /* max_id is 8 initially, so no need to set it here */
        } else {
                /* In the normal case there is only a single target */
                us_to_host(us)->max_id = 1;
                /*
                 * Like Windows, we won't store the LUN bits in CDB[1] for
                 * SCSI-2 devices using the Bulk-Only transport (even though
                 * this violates the SCSI spec).
                 */
                if (us->transport == usb_stor_Bulk_transport)
                        us_to_host(us)->no_scsi2_lun_in_cdb = 1;
        }

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

        /* Find the endpoints and calculate pipe values */
        result = get_pipes(us);   ;//获得管道
        if (result)
                goto BadDevice;

        /*
         * If the device returns invalid data for the first READ(10)
         * command, indicate the command should be retried.
         */
        if (us->fflags & US_FL_INITIAL_READ10)
                set_bit(US_FLIDX_REDO_READ10, &us->dflags);

        /* Acquire all the other resources and add the host */
        result = usb_stor_acquire_resources(us);    //获取资源
        if (result)
                goto BadDevice;
        usb_autopm_get_interface_no_resume(us->pusb_intf);
        snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s",
                                        dev_name(&us->pusb_intf->dev));
        result = scsi_add_host(us_to_host(us), dev);   //添加scsi
        if (result) {
                dev_warn(dev,
                                "Unable to add the scsi host\n");
                goto HostAddErr;
        }

        /* Submit the delayed_work for SCSI-device scanning */
        set_bit(US_FLIDX_SCAN_PENDING, &us->dflags);

        if (delay_use > 0)
                dev_dbg(dev, "waiting for device to settle before scanning\n");
        queue_delayed_work(system_freezable_wq, &us->scan_dwork,
                        delay_use * HZ);  //工作内容加入工作队列
        return 0;

        /* We come here if there are any problems */
HostAddErr:
        usb_autopm_put_interface_no_suspend(us->pusb_intf);
BadDevice:
        usb_stor_dbg(us, "storage_probe() failed\n");
        release_everything(us);
        return result;
}
EXPORT_SYMBOL_GPL(usb_stor_probe2);
 

 主要通过assocaite_dev(),get_device_info(),get_transport(),get_protocol(),get_pipes()五个函数来为us结构体赋值,然后调用usb_stor_acquire_resources()来得到设备需要的动态资源。最后创建扫描工作 &us->scan_dwork,让用户能通过cat /proc/scsi/scsi看到U盘设备。现在我们一个个分析下这里提到了每个函数。

1 、为us结构体赋值的设备关联函数associate_dev的实现

/* Associate our private data with the USB device */
static int associate_dev(struct us_data *us, struct usb_interface *intf)
{
        /* Fill in the device-related fields */
        us->pusb_dev = interface_to_usbdev(intf);  //由接口获取设备
        us->pusb_intf = intf;
        us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;  //接口数量
        usb_stor_dbg(us, "Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n",
                     le16_to_cpu(us->pusb_dev->descriptor.idVendor),
                     le16_to_cpu(us->pusb_dev->descriptor.idProduct),
                     le16_to_cpu(us->pusb_dev->descriptor.bcdDevice));
        usb_stor_dbg(us, "Interface Subclass: 0x%02x, Protocol: 0x%02x\n",
                     intf->cur_altsetting->desc.bInterfaceSubClass,
                     intf->cur_altsetting->desc.bInterfaceProtocol);

        /* Store our private data in the interface */
        usb_set_intfdata(intf, us);  /把us设置为接口的私有数据

        /* Allocate the control/setup and DMA-mapped buffers */
        us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL);    //分配控制urb的控制字符空间
        if (!us->cr)
                return -ENOMEM;

        us->iobuf = usb_alloc_coherent(us->pusb_dev, US_IOBUF_SIZE,
                        GFP_KERNEL, &us->iobuf_dma);   /分配urb的缓冲区
        if (!us->iobuf) {
                usb_stor_dbg(us, "I/O buffer allocation failed\n");
                return -ENOMEM;
        }
        return 0;
}

 继续看获得设备信息函数get_device_info的实现

/* Get the unusual_devs entries and the string descriptors */
static int get_device_info(struct us_data *us, const struct usb_device_id *id,
                const struct us_unusual_dev *unusual_dev)
{
        struct usb_device *dev = us->pusb_dev;
        struct usb_interface_descriptor *idesc =
                &us->pusb_intf->cur_altsetting->desc;
        struct device *pdev = &us->pusb_intf->dev;

        /* Store the entries */
        us->unusual_dev = unusual_dev;   //不常用的设备

//找到USB设备支持的子类和协议
        us->subclass = (unusual_dev->useProtocol == USB_SC_DEVICE) ?
                        idesc->bInterfaceSubClass :
                        unusual_dev->useProtocol;
        us->protocol = (unusual_dev->useTransport == USB_PR_DEVICE) ?
                        idesc->bInterfaceProtocol :
                        unusual_dev->useTransport;
        us->fflags = id->driver_info;
        usb_stor_adjust_quirks(us->pusb_dev, &us->fflags);

        if (us->fflags & US_FL_IGNORE_DEVICE) {
                dev_info(pdev, "device ignored\n");
                return -ENODEV;//USB设备不能被系统识别则退出
        }

        /*
         * This flag is only needed when we're in high-speed, so let's
         * disable it if we're in full-speed
         */
        if (dev->speed != USB_SPEED_HIGH)
                us->fflags &= ~US_FL_GO_SLOW; //USB设备不支持高速则改为低速

        if (us->fflags)
                dev_info(pdev, "Quirks match for vid %04x pid %04x: %lx\n",
                                le16_to_cpu(dev->descriptor.idVendor),
                                le16_to_cpu(dev->descriptor.idProduct),
                                us->fflags);

        /*
         * Log a message if a non-generic unusual_dev entry contains an
         * unnecessary subclass or protocol override.  This may stimulate
         * reports from users that will help us remove unneeded entries
         * from the unusual_devs.h table.
         */

//根据生产厂商和产品号来设置协议、传输类型等参数
        if (id->idVendor || id->idProduct) {
                static const char *msgs[3] = {
                        "an unneeded SubClass entry",
                        "an unneeded Protocol entry",
                        "unneeded SubClass and Protocol entries"};
                struct usb_device_descriptor *ddesc = &dev->descriptor;
                int msg = -1;

                if (unusual_dev->useProtocol != USB_SC_DEVICE &&
                        us->subclass == idesc->bInterfaceSubClass)
                        msg += 1;
                if (unusual_dev->useTransport != USB_PR_DEVICE &&
                        us->protocol == idesc->bInterfaceProtocol)
                        msg += 2;
                if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE))
                        dev_notice(pdev, "This device "
                                        "(%04x,%04x,%04x S %02x P %02x)"
                                        " has %s in unusual_devs.h (kernel"
                                        " %s)\n"
                                        "   Please send a copy of this message to "
                                        "<linux-usb@vger.kernel.org> and "
                                        "<usb-storage@lists.one-eyed-alien.net>\n",
                                        le16_to_cpu(ddesc->idVendor),
                                        le16_to_cpu(ddesc->idProduct),
                                        le16_to_cpu(ddesc->bcdDevice),
                                        idesc->bInterfaceSubClass,
                                        idesc->bInterfaceProtocol,
                                        msgs[msg],
                                        utsname()->release);
        }

        return 0;
}
 

 继续看得到传输方式函数get_transport,这个函数主要获得USB设备支持的通信协议,并设置USB驱动的传输类型。对于U盘,USB协议规定它属于Bulk-only的传输方式,也就是它的us->protocot为US_PR_BULK

/* Get the transport settings */
static void get_transport(struct us_data *us)
{
        switch (us->protocol) {
        case USB_PR_CB:
                us->transport_name = "Control/Bulk";
                us->transport = usb_stor_CB_transport;
                us->transport_reset = usb_stor_CB_reset;
                us->max_lun = 7;
                break;

        case USB_PR_CBI:
                us->transport_name = "Control/Bulk/Interrupt";
                us->transport = usb_stor_CB_transport;
                us->transport_reset = usb_stor_CB_reset;
                us->max_lun = 7;
                break;

        case USB_PR_BULK:
                us->transport_name = "Bulk";
                us->transport = usb_stor_Bulk_transport;  //传输函数
                us->transport_reset = usb_stor_Bulk_reset;
                break;
        }
}

 接着我们看获得协议信息的get_protocol函数,该函数根据不同的协议,用来设置协议的传输函数。对于U盘,USB协议规定us->subclass为US_SC_SCSI

/* Get the protocol settings */
static void get_protocol(struct us_data *us)
{
        switch (us->subclass) {
        case USB_SC_RBC:
                us->protocol_name = "Reduced Block Commands (RBC)";
                us->proto_handler = usb_stor_transparent_scsi_command;  //协议处理函数
                break;

        case USB_SC_8020:
                us->protocol_name = "8020i";
                us->proto_handler = usb_stor_pad12_command;
                us->max_lun = 0;
                break;

        case USB_SC_QIC:
                us->protocol_name = "QIC-157";
                us->proto_handler = usb_stor_pad12_command;
                us->max_lun = 0;
                break;

        case USB_SC_8070:
                us->protocol_name = "8070i";
                us->proto_handler = usb_stor_pad12_command;
                us->max_lun = 0;
                break;

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

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

 最后一个初始化us的函数是获得管道信息的get_pipes函数。

/* Get the pipe settings */
static int get_pipes(struct us_data *us)
{
        struct usb_host_interface *alt = us->pusb_intf->cur_altsetting;  //获取设置
        struct usb_endpoint_descriptor *ep_in; //定义输入端点描述符
        struct usb_endpoint_descriptor *ep_out; //定义输出端点描述符
        struct usb_endpoint_descriptor *ep_int;  //定义中断端点描述符
        int res;

        /*
         * 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.
         */
        res = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL);
        if (res) {
                usb_stor_dbg(us, "bulk endpoints not found\n");
                return res;
        }

        res = usb_find_int_in_endpoint(alt, &ep_int); //是否是中断端点
        if (res && us->protocol == USB_PR_CBI) {
                usb_stor_dbg(us, "interrupt endpoint not found\n");
                return res;
        }

        /* 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,  /建立输出批量传输端点
                usb_endpoint_num(ep_out));
        us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev,  //建立输入批量传输端点
    if (ep_int) {
                usb_endpoint_num(ep_in));
        if (ep_int) {
                us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev,
                        usb_endpoint_num(ep_int)); //建立中断传输端点
                us->ep_bInterval = ep_int->bInterval;  /设置中断间隔时间
        }
        return 0;
}

 析完上面get_pipes的代码,需要补充说明的是,在我们的U盘中只有输入批量传输和输出批量传输两个端点,不存在控制端点,如果出现控制端点,那么设备支持CBI协议,即Control/Bulk/Interrupt协议,另外U盘也没有中断端点。

 分析完上面五个对cr初始化的函数后,我们接着需要看usb_stor_acquire_resources了,这个函数主要功能是初始化设备,并创建数据传输的控制线程。

/* Initialize all the dynamic resources we need */
static int usb_stor_acquire_resources(struct us_data *us)
{
        int p;
        struct task_struct *th;

        us->current_urb = usb_alloc_urb(0, GFP_KERNEL);  //申请urb
        if (!us->current_urb)
                return -ENOMEM;

        /*
         * Just before we start our control thread, initialize
         * the device if it needs initialization
         */
        if (us->unusual_dev->initFunction) { //特殊设备的初始化函数
                p = us->unusual_dev->initFunction(us);
                if (p)
                        return p;
        }

        /* Start up our control thread */
        th = kthread_run(usb_stor_control_thread, us, "usb-storage");  /创建并执行控制线程
        if (IS_ERR(th)) {
                dev_warn(&us->pusb_intf->dev,
                                "Unable to start control thread\n");
                return PTR_ERR(th);
        }
        us->ctl_thread = th;  //保存线程号

        return 0;
}
 

 在上面这个usb_stor_acquire_resources函数中,我们创建并执行了usb_stor_control_thread这个内核线程,这个控制线程用来完成数据的接收和发送,它会一直运行,直到驱动程序退出。

来看看这个控制线程。

static int usb_stor_control_thread(void * __us)
{
        struct us_data *us = (struct us_data *)__us;
        struct Scsi_Host *host = us_to_host(us);
        struct scsi_cmnd *srb;

        for (;;) {
                usb_stor_dbg(us, "*** thread sleeping\n");
                if (wait_for_completion_interruptible(&us->cmnd_ready))  //等待用户层SCSI命令唤醒
                        break;

                usb_stor_dbg(us, "*** 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 */
                srb = us->srb;
                if (srb == NULL) {  //为循环中超时后的退出
                        scsi_unlock(host);
                        mutex_unlock(&us->dev_mutex);
                        usb_stor_dbg(us, "-- exiting\n");
                        break;
                }

                /* has the command timed out *already* ? */
                if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {//直接跳到超时判断去
                        srb->result = DID_ABORT << 16;
                        goto SkipForAbort;
                }

                scsi_unlock(host);

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

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

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

                /*
                 * Handle those devices which need us to fake
                 * their inquiry data
                 */
                else if ((srb->cmnd[0] == INQUIRY) &&
                            (us->fflags & US_FL_FIX_INQUIRY)) {//如果SCSI是请求命令的处理
                        unsigned char data_ptr[36] = {
                            0x00, 0x80, 0x02, 0x02,
                            0x1F, 0x00, 0x00, 0x00};

                        usb_stor_dbg(us, "Faking INQUIRY command\n");
                        fill_inquiry_response(us, data_ptr, 36);  //填充一个请求命令
                        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(srb, us);//数据传输
                        usb_mark_last_busy(us->pusb_dev);
                }

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

                /* was the command aborted? */
                if (srb->result == DID_ABORT << 16) {
SkipForAbort:

          usb_stor_dbg(us, "scsi command aborted\n");
                        srb = NULL;     /* Don't call srb->scsi_done() */
                }

                /*
                 * 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);

                /* now that the locks are released, notify the SCSI core */
                if (srb) {
                        usb_stor_dbg(us, "scsi cmd done, result=0x%x\n",
                                        srb->result);
                        srb->scsi_done(srb);
                }
        } /* 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;
}

对于上面这个控制线程,首先该函数执行了一个for(;;),这是一个死循环,也就是这个函数作为一些线程用可以不停息的运行。同时,根据刚开始wait_for_completion_interruptible代码,我们知道开始就进入睡眠状态了,只有唤醒us->cmnd_ready这个控制线程才能继续执行下去,那我们要知道什么时候释放这把锁来唤醒下面的程序呢?

 其实有两个地方,一个是模块卸载的时候,另一个就是有SCSI命令发过来。每一次应用层发过来SCSI命令了,比如你去读写/dev/sda,最终SCSI核心层就会调用与该主机对应的queuecommand函数,这个函数是scsi_host_template结构体成员,在probe中scsi_host_alloc时候注册的。下面是queuecommand函数的实现。

static int queuecommand(struct scsi_cmnd *srb,void (*done)(struct scsi_cmnd *))
{
  struct us_data *us = host_to_us(srb->device->host);
  US_DEBUGP("%s called\n", __func__);
  if (us->srb != NULL) {
      printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n",__func__, us->srb);
      return SCSI_MLQUEUE_HOST_BUSY;
   }
  if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
  US_DEBUGP("Fail command during disconnect\n");
  srb->result = DID_NO_CONNECT << 16;
  done(srb);
  return 0;
  }
  srb->scsi_done = done;
  us->srb = srb;
  complete(&us->cmnd_ready); //释放锁,唤醒控制线程

  return 0;
}

 用户层有了SCSI命令,就会执行我们驱动中这个控制线程。这个死循环首先会做一些判断,然后一直进行数据通信。那么这个死循环也是会退出的,什么时候呢?当执行这个死循环的最后一个if语句,会进行超时处理,如果超时会将us->srb=NULL,而我们在这个控制线程的死循环中发现,当获取us->cmnd_ready锁后,第一个执行的代码就是判断us->srb是否为NULL,如果us->srb=NULL就会执行break语句,从而跳出第一个死循环。接下来进入第二个死循环,这个死循环首先判断是否真的该结束了,如果真的结束了,那么就break,彻底退出这个控制线程,如果不是应该彻底结束,那进行schedule重新调度控制子线程。

我们的控制线程就已经分析完了,不过我们发现,这个控制线程是在usb_stor_acquire_resources中定义的,在usb_stor_acquire_resources之后,我们还创建了 &us->scan_dwork 工作,这是一个扫描工作。

在usb_stor_probe1 中,INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork);

usb_stor_scan_dwork

/* Delayed-work routine to carry out SCSI-device scanning */
static void usb_stor_scan_dwork(struct work_struct *work)
{
        struct us_data *us = container_of(work, struct us_data,
                        scan_dwork.work);
        struct device *dev = &us->pusb_intf->dev;

        dev_dbg(dev, "starting scan\n");

        /* For bulk-only devices, determine the max LUN value */
        if (us->protocol == USB_PR_BULK &&
            !(us->fflags & US_FL_SINGLE_LUN) &&
            !(us->fflags & US_FL_SCM_MULT_TARG)) {
                mutex_lock(&us->dev_mutex);
                us->max_lun = usb_stor_Bulk_max_lun(us);  /询问设备支持多少个LUN
                /*
                 * Allow proper scanning of devices that present more than 8 LUNs
                 * While not affecting other devices that may need the previous
                 * behavior
                 */
                if (us->max_lun >= 8)
                        us_to_host(us)->max_lun = us->max_lun+1;
                mutex_unlock(&us->dev_mutex);
        }
        scsi_scan_host(us_to_host(us));
        dev_dbg(dev, "scan complete\n");

        /* Should we unbind if no devices were detected? */

        usb_autopm_put_interface(us->pusb_intf);
        clear_bit(US_FLIDX_SCAN_PENDING, &us->dflags); 
}
 

 对于上面这个扫描线程,里面的usb_stor_Bulk_max_lun函数完成了主机控制器与设备之间的第一次通信。USB驱动程序首先发送一个命令,然后设备根据命令返回一些信息,这里显示的是一个表示LUN个数的数字,usb_stor_Bulk_max_lun完成的是一次控制传输。

usb_stor_Bulk_max_lun

 

/* Determine what the maximum LUN supported is */
int usb_stor_Bulk_max_lun(struct us_data *us)
{
        int result;

        /* issue the command */
        us->iobuf[0] = 0;   //默认只有0个LUN
        result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
                                 US_BULK_GET_MAX_LUN,
                                 USB_DIR_IN | USB_TYPE_CLASS |
                                 USB_RECIP_INTERFACE,
                                 0, us->ifnum, us->iobuf, 1, 10*HZ);  /向设备发送一个命令

        usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n",
                     result, us->iobuf[0]);

        /*
         * If we have a successful request, return the result if valid. The
         * CBW LUN field is 4 bits wide, so the value reported by the device
         * should fit into that.
         */
        if (result > 0) {
                if (us->iobuf[0] < 16) {
                        return us->iobuf[0];
                } else {
                        dev_info(&us->pusb_intf->dev,
                                 "Max LUN %d is not valid, using 0 instead",
                                 us->iobuf[0]);
                }
        }

        /*
         * Some devices don't like GetMaxLUN.  They may STALL the control
         * pipe, they may return a zero-length result, they may do nothing at
         * all and timeout, or they may fail in even more bizarrely creative
         * ways.  In these cases the best approach is to use the default
         * value: only one LUN.
         */
        return 0;
}
 

 usb_stor_control_msg的实现

/*
 * Transfer one control message, with timeouts, and allowing early
 * termination.  Return codes are usual -Exxx, *not* USB_STOR_XFER_xxx.
 */
int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
                 u8 request, u8 requesttype, u16 value, u16 index,
                 void *data, u16 size, int timeout)
{
        int status;

        usb_stor_dbg(us, "rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n",
                     request, requesttype, value, index, size);

        /* fill in the devrequest structure */
        us->cr->bRequestType = requesttype;    //初始化us->cr
        us->cr->bRequest = request;
        us->cr->wValue = cpu_to_le16(value);
        us->cr->wIndex = cpu_to_le16(index);
        us->cr->wLength = cpu_to_le16(size);

        /* fill and submit the URB */
        usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe,
                         (unsigned char*) us->cr, data, size,
                         usb_stor_blocking_completion, NULL); //填充控制urb
        status = usb_stor_msg_common(us, timeout);   //继续填充控制urb并提交

        /* return the actual length of the data transferred if no error */
        if (status == 0)
                status = us->current_urb->actual_length;
        return status;
}
EXPORT_SYMBOL_GPL(usb_stor_control_msg);
 

 继续往下看usb_stor_msg_common的实现

static int usb_stor_msg_common(struct us_data *us, int timeout)
{
        struct completion urb_done;
        long timeleft;
        int status;

        /* don't submit URBs during abort processing */
        if (test_bit(US_FLIDX_ABORTING, &us->dflags))  //设备处于放弃状态则结束
                return -EIO;

        /* set up data structures for the wakeup system */
        init_completion(&urb_done);                 //初始化完成量

        /* fill the common fields in the URB */
        us->current_urb->context = &urb_done;
        us->current_urb->transfer_flags = 0;

        /*
         * we assume that if transfer_buffer isn't us->iobuf then it
         * hasn't been mapped for DMA.  Yes, this is clunky, but it's
         * easier than always having the caller tell us whether the
         * transfer buffer has already been mapped.
         */
        if (us->current_urb->transfer_buffer == us->iobuf)
                us->current_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        us->current_urb->transfer_dma = us->iobuf_dma;

        /* submit the URB */
        status = usb_submit_urb(us->current_urb, GFP_NOIO);   //提交控制urb
        if (status) {
                /* something went wrong */
                return status;
        }

        /*
         * since the URB has been submitted successfully, it's now okay
         * to cancel it
         */
        set_bit(US_FLIDX_URB_ACTIVE, &us->dflags);

        /* did an abort occur during the submission? */
        if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {

                /* cancel the URB, if it hasn't been cancelled already */ //当前还没取消urb时,取消urb请求
                if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {
                        usb_stor_dbg(us, "-- cancelling URB\n");
                        usb_unlink_urb(us->current_urb);
                }
        }

        /* wait for the completion of the URB */

 //等待直到urb完成,如果1秒时间到进程没有被信号唤醒,则自动唤醒
        timeleft = wait_for_completion_interruptible_timeout(
                        &urb_done, timeout ? : MAX_SCHEDULE_TIMEOUT);

        clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags);

        if (timeleft <= 0) {
                usb_stor_dbg(us, "%s -- cancelling URB\n",
                             timeleft == 0 ? "Timeout" : "Signal");
                usb_kill_urb(us->current_urb);
        }

        /* return the URB status */
        return us->current_urb->status;
}
 

   通过对上面这个usb_stor_msg_common函数的分析,我们现在已经把控制urb提交给USB内核了,当处理完,就会通知USB设备驱动,调用其回调函数,该回调函数在填充控制urb时已经说明,也就是usb_stor_blocking_completion函数

 usb_stor_blocking_completion

static void usb_stor_blocking_completion(struct urb *urb)
{
        struct completion *urb_done_ptr = urb->context;

        complete(urb_done_ptr);
}
 

当一次通信完成,执行了回调函数后,就会释放锁,这样stor_msg_common函数中的wait_for_completion_interruptible_timeout处就会被唤醒,至此一次通信完毕。 

 

最后需要补充说明一个问题,在上面提交控制urb时,flag标志使用的是GFP_NOIO。GFP_NOIO标志的意思是不能在申请内存的时候进行I/O操作,原因是usb_submit_urb()提交之后,会读取磁盘或者U盘中的数据,这种情况下,由于虚拟内存的原因,申请内存的函数还需要读取磁盘。所以不允许在usb_submit_urb()提交urb时进行I/O操作。

总结下,USB设备驱动主要围绕URB请求块,也就是控制URB、中断URB、批量URB、实时URB。USB骨骼框架是批量URB的例子。USB鼠标是一个中断URB和input子系统结合的例子。USB键盘是一个控制URB和中断URB结合input子系统的例子。USB的U盘是批量URB和控制URB结合的例子。不幸的是,等时URB没有填充函数,因此等时URB在被提交给USB核心之前,需要手动进行初始化。

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
linux系统下USB键盘驱动源码+使用文档说明 如何编写Linux下的USB键盘驱动 1. 指定USB键盘驱动所需的头文件: #include /*内核头文件,含有内核一些常用函数的原型定义*/ #include /*定义内存分配的一些函数*/ #include /*模块编译必须的头文件*/ #include /*输入设备相关函数的头文件*/ #include /*linux初始化模块函数定义*/ #include /*USB设备相关函数定义*/ 2. 定义键盘码表数组: /*使用第一套键盘扫描码表:A-1E;B-30;C-2E…*/ static unsigned char usb_kbd_keycode[256] = { 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190, 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113, 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0, 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, 150,158,159,128,136,177,178,176,142,152,173,140 }; 3. 编写设备ID表: static struct usb_device_id usb_kbd_id_table [] = { { USB_INTERFACE_INFO(3, 1, 1) },/*3,1,1分别表示接口类,接口子类,接口协议;3,1,1为键盘接口类;鼠标为3,1,2*/ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);/*指定设备ID表*/ 4. 定义USB键盘结构体: struct usb_kbd { struct input_dev *dev; /*定义一个输入设备*/ struct usb_device *usbdev;/*定义一个usb设备*/ unsigned char old[8]; /*按键离开时所用之数据缓冲区*/ struct urb *irq/*usb键盘之中断请求块*/, *led/*usb键盘之指示灯请求块*/; unsigned char newleds;/*目标指定灯状态*/ char name[128];/*存放厂商名字及产品名字*/ char phys[64]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值