Linux下的USB总线驱动(三)

版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127

4.U盘驱动分析

USB Mass Storage是一类USB存储设备,这些设备包括USB磁盘、USB硬盘、USB磁带机、USB光驱、U盘、记忆棒、智能卡和一些USB摄像头等,这类设备由USB协议支持。

首先我想去看看/driver/usb/storage/Makefile

EXTRA_CFLAGS       := -Idrivers/scsi

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

usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG)   += debug.o

usb-storage-objs :=      scsiglue.o protocol.o transport.o usb.o \

                   initializers.o sierra_ms.o option_ms.o $(usb-storage-obj-y)

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

同样,我们先看看usb.c中的模块加载部分

static int __init usb_stor_init(void)

{

     int retval;

     printk(KERN_INFO "Initializing USB Mass Storage driver...\n");

     retval = usb_register(&usb_storage_driver);  //注册usb_driver驱动

     if (retval == 0) {

            printk(KERN_INFO "USB Mass Storage support registered.\n");

            usb_usual_set_present(USB_US_TYPE_STOR);

     }

     return retval;

}

static struct usb_driver usb_storage_driver = {

     .name =          "usb-storage",

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

     .soft_unbind =       1,

};

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

static int storage_probe(struct usb_interface *intf,

                    const struct usb_device_id *id)

{

     struct us_data *us;

     int result;

    //检测匹配

     if (usb_usual_check_type(id, USB_US_TYPE_STOR) ||usb_usual_ignore_device(intf))

            return -ENXIO;               

     //探测的第一部分

     result = usb_stor_probe1(&us, intf, id,(id - usb_storage_usb_ids) + us_unusual_dev_list);

     if (result)

            return result;

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

     return result;

}

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

int usb_stor_probe1(struct us_data **pus,struct usb_interface *intf,

            const struct usb_device_id *id,struct us_unusual_dev *unusual_dev)

{

     struct Scsi_Host *host;

     struct us_data *us;

     int result;

     US_DEBUGP("USB Mass Storage device detected\n");

     host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));  //分配Scsi_Host结构体

     if (!host) {

            printk(KERN_WARNING USB_STORAGE

                   "Unable to allocate the scsi host\n");

            return -ENOMEM;

     }

     host->max_cmd_len = 16;

     *pus = us = host_to_us(host);        //host结构体中提取出us_data结构体

     memset(us, 0, sizeof(struct us_data));

     mutex_init(&(us->dev_mutex));

     init_completion(&us->cmnd_ready);  //初始化完成量

     init_completion(&(us->notify));

     init_waitqueue_head(&us->delay_wait);  //初始化等待队列头

     init_completion(&us->scanning_done);   //初始化完成量

     result = associate_dev(us, intf);         //us_dataUSB设备相关联

     if (result)

            goto BadDevice;

     result = get_device_info(us, id, unusual_dev);  //获取设备信息

     if (result)

            goto BadDevice;

     get_transport(us);                        //获取传输方式

     get_protocol(us);                         //获取传输协议

     return 0;

BadDevice:

     US_DEBUGP("storage_probe() failed\n");

     release_everything(us);

     return result;

}

我们再看看U盘驱动的探测的第二部分usb_stor_probe2

int usb_stor_probe2(struct us_data *us)

{

     struct task_struct *th;

     int result;

     if (!us->transport || !us->proto_handler) {

            result = -ENXIO;

            goto BadDevice;

     }

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

     US_DEBUGP("Protocol: %s\n", us->protocol_name);

     if (us->fflags & US_FL_SINGLE_LUN)

            us->max_lun = 0;

     result = get_pipes(us);   //获得管道

     if (result)

            goto BadDevice;

     result = usb_stor_acquire_resources(us);   //获取资源

     if (result)

            goto BadDevice;

     result = scsi_add_host(us_to_host(us), &us->pusb_intf->dev);  //添加scsi

     if (result) {

            printk(KERN_WARNING USB_STORAGE

                   "Unable to add the scsi host\n");

            goto BadDevice;

     }

     th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan");  //创建线程

     if (IS_ERR(th)) {

            printk(KERN_WARNING USB_STORAGE

                   "Unable to start the device-scanning thread\n");

            complete(&us->scanning_done);

            quiesce_and_remove_host(us);

            result = PTR_ERR(th);

            goto BadDevice;

     }

     wake_up_process(th);   //唤醒usb_stor_scan_thread线程

     return 0;

BadDevice:

     US_DEBUGP("storage_probe() failed\n");

     release_everything(us);

     return result;

}

好了,我们已经把probe大致阅读了一下,主要通过assocaite_dev()get_device_info()get_transport()get_protocol()get_pipes()五个函数来为us结构体赋值,然后调用usb_stor_acquire_resources()来得到设备需要的动态资源。最后创建扫描线程usb_stor_scan_thread,让用户能通过cat /proc/scsi/scsi看到U盘设备。现在我们一个个分析下这里提到了每个函数。

首先我们看看来为us结构体赋值的设备关联函数associate_dev的实现

static int associate_dev(struct us_data *us, struct usb_interface *intf)

{

     US_DEBUGP("-- %s\n", __func__);

     us->pusb_dev = interface_to_usbdev(intf); //由接口获取设备

     us->pusb_intf = intf;                  //接口赋值

     us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber; //接口数量

     US_DEBUGP("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));

     US_DEBUGP("Interface Subclass: 0x%02x, Protocol: 0x%02x\n",

                   intf->cur_altsetting->desc.bInterfaceSubClass,

                   intf->cur_altsetting->desc.bInterfaceProtocol);

     usb_set_intfdata(intf, us);      //us设置为接口的私有数据

     //分配控制urb的控制字符空间

us->cr = usb_buffer_alloc(us->pusb_dev, sizeof(*us->cr),GFP_KERNEL, &us->cr_dma);

     if (!us->cr) {

            US_DEBUGP("usb_ctrlrequest allocation failed\n");

            return -ENOMEM;

     }

     us->iobuf = usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE,

                   GFP_KERNEL, &us->iobuf_dma); //分配urb的缓冲区

     if (!us->iobuf) {

            US_DEBUGP("I/O buffer allocation failed\n");

            return -ENOMEM;

     }

     return 0;

}

然后我们继续看获得设备信息函数get_device_info的实现

static int get_device_info(struct us_data *us, const struct usb_device_id *id,

            struct us_unusual_dev *unusual_dev)

{

     struct usb_device *dev = us->pusb_dev;

     struct usb_interface_descriptor *idesc =&us->pusb_intf->cur_altsetting->desc;

     us->unusual_dev = unusual_dev;  //不常用的设备

     //找到USB设备支持的子类和协议

     us->subclass = (unusual_dev->useProtocol == US_SC_DEVICE) ?

                   idesc->bInterfaceSubClass :unusual_dev->useProtocol;

     us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ?

                   idesc->bInterfaceProtocol :unusual_dev->useTransport;

     us->fflags = USB_US_ORIG_FLAGS(id->driver_info);

     adjust_quirks(us);

     if (us->fflags & US_FL_IGNORE_DEVICE) {//USB设备不能被系统识别则退出

            printk(KERN_INFO USB_STORAGE "device ignored\n");

            return -ENODEV;

     }

     if (dev->speed != USB_SPEED_HIGH)  //USB设备不支持高速则改为低速

            us->fflags &= ~US_FL_GO_SLOW;

     //根据生产厂商和产品号来设置协议、传输类型等参数

     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 != US_SC_DEVICE &&

                   us->subclass == idesc->bInterfaceSubClass)

                   msg += 1;

            if (unusual_dev->useTransport != US_PR_DEVICE &&

                   us->protocol == idesc->bInterfaceProtocol)

                   msg += 2;

            if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE))

                   printk(KERN_NOTICE USB_STORAGE "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->protocotUS_PR_BULK

static void 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;

            us->max_lun = 7;

            break;

     case US_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 US_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->subclassUS_SC_SCSI

static void 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;

            break;

     case US_SC_8020:

            us->protocol_name = "8020i";

            us->proto_handler = usb_stor_pad12_command;

            us->max_lun = 0;

            break;

     case US_SC_QIC:

            us->protocol_name = "QIC-157";

            us->proto_handler = usb_stor_pad12_command;

            us->max_lun = 0;

            break;

     case US_SC_8070:

            us->protocol_name = "8070i";

            us->proto_handler = usb_stor_pad12_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;

     }

}

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

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; //定义中断端点描述符

     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;

     }

     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, 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了,这个函数主要功能是初始化设备,并创建数据传输的控制线程。

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) {

            US_DEBUGP("URB allocation failed\n");

            return -ENOMEM;

     }

     if (us->unusual_dev->initFunction) {    //特殊设备的初始化函数

            p = us->unusual_dev->initFunction(us);

            if (p)

                   return p;

     }

     th = kthread_run(usb_stor_control_thread, us, "usb-storage"); //创建并执行控制线程

     if (IS_ERR(th)) {

            printk(KERN_WARNING USB_STORAGE

                   "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);

     for(;;) {

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

            if (wait_for_completion_interruptible(&us->cmnd_ready)) //等待用户层SISI命令唤醒

                   break;

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

            mutex_lock(&(us->dev_mutex));

            scsi_lock(host);

            if (us->srb == NULL) {  //为循环中超时后的退出

                   scsi_unlock(host);

                   mutex_unlock(&us->dev_mutex);

                   US_DEBUGP("-- exiting\n");

                   break;

            }

            if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { //直接跳到超时判断去

                   us->srb->result = DID_ABORT << 16;

                   goto SkipForAbort;

            }

            scsi_unlock(host);

            if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) { //方向

                   US_DEBUGP("UNKNOWN data direction\n");

                   us->srb->result = DID_ERROR << 16;

            }

            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;

            }

            else if ((us->srb->cmnd[0] == INQUIRY) &&  (us->fflags & US_FL_FIX_INQUIRY)) {                              //如果SCSI是请求命令的处理

                   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;

            }

            else {

                   US_DEBUG(usb_stor_show_command(us->srb));

                   us->proto_handler(us->srb, us);   //数据传输

            }

            scsi_lock(host);

            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 (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {  //超时处理

                   complete(&(us->notify));

                   clear_bit(US_FLIDX_ABORTING, &us->dflags);

                   clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);

            }

            us->srb = NULL;

            scsi_unlock(host);

            mutex_unlock(&us->dev_mutex);

     }

     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结构体成员,在probescsi_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之后,我们还创建了usb_stor_scan_thread线程,这是一个扫描线程。

static int usb_stor_scan_thread(void * __us)

{

     struct us_data *us = (struct us_data *)__us;

     printk(KERN_DEBUG

            "usb-storage: device found at %d\n", us->pusb_dev->devnum);

     set_freezable();  //设备在一定时间内没有响应,会挂起

     if (delay_use > 0) {  // delay_use秒后如果U盘没拔出则继续执行,否则执行disconnect

            printk(KERN_DEBUG "usb-storage: waiting for device "

                          "to settle before scanning\n");

                   wait_event_freezable_timeout(us->delay_wait,test_bit(US_FLIDX_DONT_SCAN, &us->dflags), delay_use * HZ);

     }

     if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {

            if (us->protocol == US_PR_BULK &&

                          !(us->fflags & US_FL_SINGLE_LUN)) {

                   mutex_lock(&us->dev_mutex);

                   us->max_lun = usb_stor_Bulk_max_lun(us);  //询问设备支持多少个LUN

                   mutex_unlock(&us->dev_mutex);

            }

            scsi_scan_host(us_to_host(us));

            printk(KERN_DEBUG "usb-storage: device scan complete\n");

     }

     complete_and_exit(&us->scanning_done, 0);  //本进程结束,唤醒disconnect中的进程

}

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

int usb_stor_Bulk_max_lun(struct us_data *us)

{

     int result;

     us->iobuf[0] = 0; //默认只有0LUN

     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); //向设备发送一个命令

     US_DEBUGP("GetMaxLUN command result is %d, data is %d\n",

              result, us->iobuf[0]);

     if (result > 0)

            return us->iobuf[0];

     return 0;

}

我们看看里面usb_stor_control_msg的实现

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;

     US_DEBUGP("%s: rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n",

                   __func__, request, requesttype,

                   value, index, size);

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

     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并提交

     if (status == 0)

            status = us->current_urb->actual_length;

     return status;

}

继续往下看usb_stor_msg_common的实现

static int usb_stor_msg_common(struct us_data *us, int timeout)

{

     struct completion urb_done;

     long timeleft;

     int status;

     if (test_bit(US_FLIDX_ABORTING, &us->dflags))  //设备处于放弃状态则结束

            return -EIO;

     init_completion(&urb_done);  //初始化完成量

     us->current_urb->context = &urb_done;

     us->current_urb->actual_length = 0;

     us->current_urb->error_count = 0;

     us->current_urb->status = 0;

     us->current_urb->transfer_flags = URB_NO_SETUP_DMA_MAP;

     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;

     us->current_urb->setup_dma = us->cr_dma;

     status = usb_submit_urb(us->current_urb, GFP_NOIO);  //提交控制urb

     if (status) {

            return status;

     }

     set_bit(US_FLIDX_URB_ACTIVE, &us->dflags);

     //当前还没取消urb时,取消urb请求

     if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {

            if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {

                   US_DEBUGP("-- cancelling URB\n");

                   usb_unlink_urb(us->current_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) {

            US_DEBUGP("%s -- cancelling URB\n",

                     timeleft == 0 ? "Timeout" : "Signal");

            usb_kill_urb(us->current_urb);

     }

     return us->current_urb->status;

}

通过对上面这个usb_stor_msg_common函数的分析,我们现在已经把控制urb提交给USB内核了,当处理完,就会通知USB设备驱动,调用其回调函数,该回调函数在填充控制urb时已经说明,也就是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_NOIOGFP_NOIO标志的意思是不能在申请内存的时候进行I/O操作,原因是usb_submit_urb()提交之后,会读取磁盘或者U盘中的数据,这种情况下,由于虚拟内存的原因,,申请内存的函数还需要读取磁盘。所以不允许在usb_submit_urb()提交urb时进行I/O操作。

 

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

 

三.           U盘驱动测试

Mini2440开发板具有两种USB 接口,一个是USB Host,它和普通PCUSB接口是一样的,可以接USB 摄像头、USB 键盘、USB 鼠标、优盘等常见的USB外设,另外一种是USB Slave,我们一般使用它来下载程序到目标板。对于U盘的测试,要配置内核后才能进行测试。

 

实验环境:内核linux2.6.32.2arm-linux-gcc交叉编译器,mini2440开发板。

内核配置:(1)因为优盘用到了 SCSI 命令,所以我们先增加SCSI 支持。在 Device Drivers 菜单里面,选择SCSI device support。(2)选择 USB support,按回车进入USB support 菜单,找到并选中USB Mass Storage support。(3)另外,现在的优盘等移动存储器使用的大都是FAT/FAT32 格式的,因此我们还需要添加FAT32 文件系统的支持,在内核配置主菜单下进入FAT32 文件系统配置子菜单,为了支持中英文的编码,在File systems菜单下选择Native language support 

 

接上面的步骤,在内核源代码根目录下执行:make zImage,把生成的新内核烧写到开发板中,先不要插入优盘(这样做是为了看插入时的打印信息),等系统启动后,进入命令行控制台,此时优盘,可以看到其厂家ID和产品ID,以及存储设备uba1信息。

(1) 执行cat /proc/partitions查看磁盘分区信息

(2) 挂载U盘,在mnt目录下建立usb目录

执行mkdir /mnt/usb   mount /dev/uba1  /mnt/usb/

(3) 查看U盘信息 ls /mnt/usb –l

(4) 查看挂载后的分区信息 df –h

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值