文章转自 http://my.csdn.net/weiqing1981127
一 . USB 键盘
跟USB鼠标类型,USB键盘也属于HID类型,代码在/dirver/hid/usbhid/usbkbd.c下。USB键盘除了提交中断URB外,还需要提交控制URB。不多话,我们看代码
static int __init usb_kbd_init(void)
{
int result = usb_register(&usb_kbd_driver); //注册USB驱动
if (result == 0)
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
return result;
}
static struct usb_driver usb_kbd_driver = {
.name = "usbkbd",
.probe = usb_kbd_probe,
.disconnect = usb_kbd_disconnect,
.id_table = usb_kbd_id_table,
};
大家都懂,下面跟踪usb_driver中的probe
static int usb_kbd_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(iface); //由接口获得设备
struct usb_host_interface *interface; //设置
struct usb_endpoint_descriptor *endpoint; //端点描述符
struct usb_kbd *kbd; //私有数据
struct input_dev *input_dev; //input设备
int i, pipe, maxp;
int error = -ENOMEM;
interface = iface->cur_altsetting; //获得设置
if (interface->desc.bNumEndpoints != 1) //只有一个端点
return -ENODEV;
endpoint = &interface->endpoint[0].desc; //获取端点描述符
if (!usb_endpoint_is_int_in(endpoint)) //端点必须是中断输入端点
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //建立中断输入端点
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //获取返回字节大小
kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); //分配私有数据空间
input_dev = input_allocate_device(); //分配input设备空间
if (!kbd || !input_dev)
goto fail1;
if (usb_kbd_alloc_mem(dev, kbd)) //分配urb空间和其他缓冲空间
goto fail2;
kbd->usbdev = dev; //给内嵌结构体赋值
kbd->dev = input_dev; //给内嵌结构体赋值
if (dev->manufacturer)
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(kbd->name, " ", sizeof(kbd->name));
strlcat(kbd->name, dev->product, sizeof(kbd->name));
}
if (!strlen(kbd->name))
snprintf(kbd->name, sizeof(kbd->name),
"USB HIDBP Keyboard %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
input_dev->name = kbd->name;
input_dev->phys = kbd->phys;
usb_to_input_id(dev, &input_dev->id); //复制usb_driver的支持项给input的支持项
input_dev->dev.parent = &iface->dev;
input_set_drvdata(input_dev, kbd); //将kbd设置为input的私有数据
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
BIT_MASK(EV_REP);
input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |
BIT_MASK(LED_KANA);
for (i = 0; i < 255; i++)
set_bit(usb_kbd_keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit);
input_dev->event = usb_kbd_event; //定义event函数
input_dev->open = usb_kbd_open;
input_dev->close = usb_kbd_close;
usb_fill_int_urb(kbd->irq, dev, pipe,kbd->new, (maxp > 8 ? 8 : maxp),
usb_kbd_irq, kbd, endpoint->bInterval); //填充中断urb
kbd->irq->transfer_dma = kbd->new_dma;
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //dma方式传输
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
kbd->cr->bRequest = 0x09; //设置控制请求的格式
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
kbd->cr->wLength = cpu_to_le16(1);
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0), (void *) kbd->cr, kbd->leds, 1,
usb_kbd_led, kbd); //填充控制urb
kbd->led->setup_dma = kbd->cr_dma;
kbd->led->transfer_dma = kbd->leds_dma;
kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); //设置dma和setup_dma有效
error = input_register_device(kbd->dev); //注册input设备
if (error)
goto fail2;
usb_set_intfdata(iface, kbd);
return 0;
fail2:
usb_kbd_free_mem(dev, kbd);
fail1:
input_free_device(input_dev);
kfree(kbd);
return error;
}
在上面的probe中,我们主要是初始化一些结构体,然后提交中断urb和控制urb,并注册input设备。其中有几个地方需要细看下,其一,usb_kbd_alloc_mem的实现。其二,设置控制请求的格式。
先来看看usb_kbd_alloc_mem的实现
static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL))) //分配中断urb
return -1;
if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL))) //分配控制urb
return -1;
if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma))) return -1;
if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma))) //分配控制urb使用的控制请求描述符
return -1;
if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))
return -1; //分配中断urb使用的缓冲区
return 0;
}
这里我们需要明白中断urb和控制urb需要分配不同的urb结构体,同时在提交urb之前,需要填充的内容也不同,中断urb填充的是缓冲区和中断处理函数,控制urb填充的是控制请求描述符合回调函数。
好了,接着我们解决第二个问题,设置控制请求的格式。cr是struct usb_ctrlrequest结构的指针,USB协议中规定一个控制请求的格式为一个8个字节的数据包,其定义如下
struct usb_ctrlrequest {
__u8 bRequestType; //设定传输方向、请求类型等
__u8 bRequest; //指定哪个请求,可以是规定的标准值也可以是厂家定义的值
__le16 wValue; //即将写到寄存器的数据
__le16 wIndex; //接口数量,也就是寄存器的偏移地址
__le16 wLength; //数据传输阶段传输多少个字节
} __attribute__ ((packed));
USB协议中规定,所有的USB设备都会响应主机的一些请求,这些请求来自USB主机控制器,主机控制器通过设备的默认控制管道发出这些请求。默认的管道为0号端口对应的那个管道。
同样这个input设备首先由用户层调用open函数,所以先看看input中定义的open
static int usb_kbd_open(struct input_dev *dev)
{
struct usb_kbd *kbd = input_get_drvdata(dev);
kbd->irq->dev = kbd->usbdev;
if (usb_submit_urb(kbd->irq, GFP_KERNEL)) //提交中断urb
return -EIO;
return 0;
}
因为这个驱动里面有一个中断urb一个控制urb,我们先看中断urb的处理流程。中断urb在input的open中被提交后,当USB核心处理完毕,会通知这个USB设备驱动,然后执行回调函数,也就是中断处理函数usb_kbd_irq
- /*
- * status 值为 0 表示 urb 成功返回,直接跳出循环把鼠标事件报告给输入子系统。
- * ECONNRESET 出错信息表示 urb 被 usb_unlink_urb 函数给 unlink 了,ENOENT 出错信息表示 urb 被
- * usb_kill_urb 函数给 kill 了。usb_kill_urb 表示彻底结束 urb 的生命周期,而 usb_unlink_urb 则
- * 是停止 urb,这个函数不等 urb 完全终止就会返回给回调函数。这在运行中断处理程序时或者等待某自旋锁
- * 时非常有用,在这两种情况下是不能睡眠的,而等待一个 urb 完全停止很可能会出现睡眠的情况。
- * ESHUTDOWN 这种错误表示 USB 主控制器驱动程序发生了严重的错误,或者提交完 urb 的一瞬间设备被拔出。
- * 遇见除了以上三种错误以外的错误,将申请重传 urb。
- */
static void usb_kbd_irq(struct urb *urb)
{
struct usb_kbd *kbd = urb->context;
int i;
switch (urb->status) {
case 0: //成功
break;
case -ECONNRESET: //未连接
case -ENOENT:
case -ESHUTDOWN:
return;
default:
goto resubmit; //出错就再次提交中断urb
}
for (i = 0; i < 8; i++) //向input子系统报告
input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
for (i = 2; i < 8; i++) {
if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
if (usb_kbd_keycode[kbd->old[i]])
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
else
dev_info(&urb->dev->dev,
"Unknown key (scancode %#x) released.\n", kbd->old[i]);
}
if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
if (usb_kbd_keycode[kbd->new[i]])
input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
else
dev_info(&urb->dev->dev,
"Unknown key (scancode %#x) released.\n", kbd->new[i]);
}
}
input_sync(kbd->dev);
memcpy(kbd->old, kbd->new, 8);
resubmit:
i = usb_submit_urb (urb, GFP_ATOMIC); //再次提交中断urb
if (i)
err_hid ("can't resubmit intr, %s-%s/input0, status %d",
kbd->usbdev->bus->bus_name,
kbd->usbdev->devpath, i);
}
这个就是中断urb的处理流程,跟前面讲的的USB鼠标中断处理流程类似。好了,我们再来看看剩下的控制urb处理流程吧。
我们有个疑问,我们知道在probe中,我们填充了中断urb和控制urb,但是在input的open中,我们只提交了中断urb,那么控制urb什么时候提交呢?
我们知道对于input子系统,如果有事件被响应,我们会调用事件处理层的event函数,而该函数最终调用的是input下的event。所以,对于input设备,我们在USB键盘驱动中只设置了支持LED选项,也就是ledbit项,这是怎么回事呢?刚才我们分析的那个中断urb其实跟这个input基本没啥关系,中断urb并不是像讲键盘input实现的那样属于input下的中断。我们在USB键盘驱动中的input子系统中只设计了LED选项,那么当input子系统有按键选项的时候必然会使得内核调用调用事件处理层的event函数,最终调用input下的event。好了,那我们来看看input下的event干了些什么。
static int usb_kbd_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value)
{
struct usb_kbd *kbd = input_get_drvdata(dev);
if (type != EV_LED) //不是LED事件就返回
return -1;
//将当前的LED值保存在kbd->newleds中
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |(!!test_bit(LED_NUML, dev->led));
if (kbd->led->status == -EINPROGRESS)
return 0;
if (*(kbd->leds) == kbd->newleds)
return 0;
*(kbd->leds) = kbd->newleds;
kbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC)) //提交控制urb
err_hid("usb_submit_urb(leds) failed");
return 0;
}
当在input的event里提交了控制urb后,经过URB处理流程,最后返回给USB设备驱动的回调函数,也就是在probe中定义的usb_kbd_led
static void usb_kbd_led(struct urb *urb)
{
struct usb_kbd *kbd = urb->context;
if (urb->status) //提交失败显示
dev_warn(&urb->dev->dev, "led urb status %d received\n",
urb->status);
//比较kbd->leds和kbd->newleds,如果发生变化,则更新kbd->leds
if (*(kbd->leds) == kbd->newleds)
return;
*(kbd->leds) = kbd->newleds;
kbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC)) //再次提交控制urb
err_hid("usb_submit_urb(leds) failed");
}
总结下,我们的控制urb走的是先由input的event提交,触发后由控制urb的回调函数再次提交。好了,通过USB键盘,我们已经知道了控制urb和中断urb的设计和处理流程。
二 . 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)" //%04x 表示按16进制输出数据,最小输出宽度为4个字符,右对齐,如果输出的数据小于4个字符,前补0
" 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