本帖最后由 黄土马家 于 2017-9-14 14:21 编辑
先看看/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.otransport.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 __initusb_stor_init(void) { int retval; printk(KERN_INFO "Initializing USBMass Storage driver...\n"); retval =usb_register(&usb_storage_driver); //注册usb_driver驱动 if (retval == 0) { printk(KERN_INFO "USB Mass Storagesupport registered.\n"); usb_usual_set_present(USB_US_TYPE_STOR); } return retval; } static structusb_driverusb_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 intstorage_probe(structusb_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 intusb_stor_probe1(struct us_data**pus,struct usb_interface *intf, const struct usb_device_id *id,structus_unusual_dev *unusual_dev) { struct Scsi_Host *host; struct us_data *us; int result; US_DEBUGP("USB Mass Storage devicedetected\n"); host =scsi_host_alloc(&usb_stor_host_template, sizeof(*us)); //分配Scsi_Host结构体 if (!host) { printk(KERN_WARNING USB_STORAGE "Unable to allocate the scsihost\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_data与USB设备相关联 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 intusb_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 scsihost\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 thedevice-scanningthread\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 intassociate_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_ctrlrequestallocation 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 allocationfailed\n"); return -ENOMEM; } return 0; } 然后我们继续看获得设备信息函数get_device_info的实现 static intget_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 Protocolentries"}; 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 thismessage to " 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 static voidget_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->subclass为US_SC_SCSI static voidget_protocol(struct us_data*us) { switch (us->subclass) { case US_SC_RBC: us->protocol_name = "ReducedBlock 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 = "TransparentSCSI"; us->proto_handler =usb_stor_transparent_scsi_command; //协议处理函数 break; case US_SC_UFI: us->protocol_name = "UniformFloppy Interface (UFI)"; us->proto_handler =usb_stor_ufi_command; break; } } 最后一个初始化us的函数是获得管道信息的get_pipes函数。 static intget_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 desc.bNumEndpoints; i++) { ep = &altsetting->endpoint.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 checkfailed! 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了,这个函数主要功能是初始化设备,并创建数据传输的控制线程。 staticintusb_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 allocationfailed\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 startcontrolthread\n"); return PTR_ERR(th); } us->ctl_thread = th; //保存线程号 return 0; } 在上面这个usb_stor_acquire_resources函数中,我们创建并执行了usb_stor_control_thread这个内核线程,这个控制线程用来完成数据的接收和发送,它会一直运行,直到驱动程序退出。 我们来看看这个控制线程。 static intusb_stor_control_thread(void *__us) { struct us_data *us = (struct us_data*)__us; struct Scsi_Host *host = us_to_host(us); for(;;) { US_DEBUGP("*** threadsleeping.\n"); if (wait_for_completion_interruptible(&us->cmnd_ready))//等待用户层SISI命令唤醒 break; US_DEBUGP("*** threadawakened.\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 datadirection\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 INQUIRYcommand\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 commandaborted\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结构体成员,在probe中scsi_host_alloc时候注册的。下面是queuecommand函数的实现。 static intqueuecommand(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 "Errorin %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 duringdisconnect\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线程,这是一个扫描线程。 在下一篇文章里将继续讲解这个线程。
|