版权所有,转载请说明转自 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_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

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->protocot为US_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->subclass为US_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结构体成员,在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之后,我们还创建了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; //默认只有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); //向设备发送一个命令

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_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核心之前,需要手动进行初始化。

三. U盘驱动测试

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

实验环境:内核linux2.6.32.2,arm-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

Linux下的USB总线驱动(三) u盘驱动分析相关推荐

  1. Linux下的USB总线驱动 mouse

    Linux下的USB总线驱动(03)--USB鼠标驱动 usbmouse.c USB鼠标驱动 usbmouse.c 原文链接:http://www.linuxidc.com/Linux/2012-12 ...

  2. *Linux下的USB总线驱动 u盘驱动分析*

    Linux下的USB总线驱动(三) u盘驱动分析 版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 https://www.xuebuyuan.com/13 ...

  3. Linux下的USB总线驱动 1

    版权所有,转载请说明转自 http://my.csdn.NET/weiqing1981127 一.USB理论 1.      USB概念概述 USB1.0版本速度1.5Mbps(低速USB) USB1 ...

  4. linux usb 总线驱动程序,Linux下的USB总线驱动

    一.USB理论 1.      USB概念概述 USB1.0版本速度1.5Mbps(低速USB) USB1.1版本速度12Mbps(全速USB)  USB2.0版本速度480Mbps(高速USB) U ...

  5. Linux下的USB总线驱动 3

    版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 4.U盘驱动分析 USB Mass Storage是一类USB存储设备,这些设备包括USB磁盘.USB硬盘 ...

  6. Linux下的USB总线驱动(04)——USB键盘驱动 usbkbd.c

    原文链接地址:http://www.linuxidc.com/Linux/2012-12/76197p9.htm 跟USB鼠标类型一样,USB键盘也属于HID类型,代码在/dirver/hid/usb ...

  7. Linux下的USB总线驱动(03)——USB鼠标驱动 usbmouse.c

    USB鼠标驱动 usbmouse.c 原文链接:http://www.linuxidc.com/Linux/2012-12/76197p7.htm drivers/hid/usbhid/usbmous ...

  8. linux usb root hub,Linux下的USB HUB驱动

    Linux下的USB HUB驱动 [日期:2012-07-29] 来源:Linux社区 作者:zhengmeifu [字体:大 中 小] 接下来,就要对设备进行配置了,首先,将设备唤醒.回忆一下我们在 ...

  9. 在Fedora 16 linux下安装USB无线网卡驱动88x2bu

    在Fedora 16 linux下安装USB无线网卡驱动88x2bu USB无线网卡翼联EP-AC1610兼容linux系统 我之前已经买了一个USB无线网卡是水星mw150us,但是没有linux驱 ...

最新文章

  1. 「Linux」作怪的网络
  2. php 回收周期(Collecting Cycles)
  3. 虚拟机ubuntu使用笔记之samba安装
  4. 学习 git clone 几种不同的协议
  5. [区块链与密码学][王小云院士][部分PPT][20200507]
  6. [vc中文绿色版本]33.8 MB下载
  7. gallery代码怎么运行_自学Python进阶-把代码变成程序、软件
  8. SQL那些事儿(八)--oracle用户、表、表空间之间的关系
  9. splay tree成段更新,成段查询poj3466
  10. QMessageBox::information 自定义按钮
  11. 视音频处理大神-雷霄骅
  12. 阿里云(企业云解析DNS)让你的博客飞起来
  13. 如何利用python整合excel_如何使用Python处理Excel
  14. 字符数组 - 数组名
  15. 计算机网络基础知识总结及思维导图(一)概述
  16. rtthread 线程
  17. 数据库 day60,61 Oracle入门,单行函数,多表查询,子查询,事物处理,约束,rownum分页,视图,序列,索引
  18. 《大唐姻缘传》(李治篇) 第一章 天妒人眷 皇限将至
  19. 51单片机空间的存储
  20. 必应输入法VS搜狗拼音

热门文章

  1. 可编程电源输出过冲怎么办?解决方案在这里
  2. html 判断checkbox,javascript判断checkbox是否被选中
  3. 判断复选框是否被选中
  4. 010 MATLAB奇淫技巧之实现图片扫描
  5. case when 用法
  6. 三菱数控CNC学习路线图
  7. 三菱m80润滑参数_三菱M80/M800功能指令参数说明一览表
  8. R语言构建分位数回归(Quantile Regression)并计算R方指标实战
  9. 对技术的本质,你是怎么理解的
  10. java/php/net/python智慧景区票务系统设计