|
staticint usb_stor_scan_thread(void * __us)
{
structus_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 "
"tosettle 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完成的是一次控制传输。
intusb_stor_Bulk_max_lun(struct us_data *us)
{
intresult;
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("GetMaxLUNcommand result is %d, data is %d\n",
result,us->iobuf[0]);
if(result > 0)
returnus->iobuf[0];
return 0;
}
我们看看里面usb_stor_control_msg的实现
intusb_stor_control_msg(struct us_data *us, unsigned int pipe,
u8request, u8 requesttype, u16 value, u16 index,
void*data, u16 size, int timeout)
{
intstatus;
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,
(unsignedchar*) 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;
returnstatus;
}
继续往下看usb_stor_msg_common的实现
staticint usb_stor_msg_common(struct us_data *us, int timeout)
{
structcompletion urb_done;
longtimeleft;
intstatus;
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) {
returnstatus;
}
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);
}
returnus->current_urb->status;
}
通过对上面这个usb_stor_msg_common函数的分析,我们现在已经把控制urb提交给USB内核了,当处理完,就会通知USB设备驱动,调用其回调函数,该回调函数在填充控制urb时已经说明,也就是usb_stor_blocking_completion函数
staticvoid usb_stor_blocking_completion(struct urb *urb)
{
structcompletion *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 支持。在 DeviceDrivers菜单里面,选择SCSIdevicesupport。(2)选择 USB support,按回车进入USB support 菜单,找到并选中USB MassStorage support。(3)另外,现在的优盘等移动存储器使用的大都是FAT/FAT32 格式的,因此我们还需要添加FAT32 文件系统的支持,在内核配置主菜单下进入FAT32 文件系统配置子菜单,为了支持中英文的编码,在File systems菜单下选择Nativelanguage support 。
接上面的步骤,在内核源代码根目录下执行:makezImage,把生成的新内核烧写到开发板中,先不要插入优盘(这样做是为了看插入时的打印信息),等系统启动后,进入命令行控制台,此时优盘,可以看到其厂家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
|
|