天天看点

USB驱动及其源码分析

一.USB理论部分

1.USB概述

    USB1.0版本速度1.5Mbps(低速USB)、 USB1.1版本速度12Mbps(全速USB)、 USB2.0版本速度480Mbps(高速USB)、USB3.0版本速度5.0GMbps(超高速USB)。

USB驱动由USB主机控制器驱动和USB设备驱动组成。USB主机控制器是用来控制USB设备和CPU之间通信的,USB主机控制器驱动主要用来驱动芯片上的主机控制器硬件。USB设备驱动主要是指具体的例如USB鼠标,USB键盘灯设备的驱动。

一般的通用的Linux设备,如U盘、USB鼠标、USB键盘,都不需要工程师再编写驱动,需要编写的是特定厂商、特定芯片的驱动,而且往往也可以参考内核中已经提供的驱动模板。 USB只是一个总线,真正的USB设备驱动的主体工作仍然是USB设备本身所属类型的驱动,如字符设备、tty设备、块设备、输入设备等。

2.USB主机控制器

    USB主机控制器属于南桥芯片的一部分,通过PCI总线和处理器通信。USB主机控制器分为UHCI(英特尔提出)、OHCI(康柏和微软提出)、 EHCI。其中OHCI驱动程序用来为非PC系统上以及带有SiS和ALi芯片组的PC主办上的USB芯片提供支持。UHCI驱动程序多用来为大多数其他PC主板(包括Intel和Via)上的USB芯片提供支持。ENCI兼容OHCI和UHCI。UHCI的硬件线路比OHCI简单,所以成本较低,但需要较复杂的驱动程序,CPU负荷稍重。主机控制器驱动程序完成的功能主要包括:解析和维护URB,根据不同的端点进行分类缓存URB;负责不同USB传输类型的调度工作;负责USB数据的实际传输工作;实现虚拟跟HUB的功能。

3.USB设备与USB驱动的匹配

    USB设备与USB驱动怎么匹配的呢?实际上USB设备中有一个模块叫固件,是固件信息和USB驱动进行的匹配。固件是固化在集成电路内部的程序代码,USB固件中包含了USB设备的出厂信息,标识该设备的厂商ID、产品ID、主版本号和次版本号等。另外固件中还包含一组程序,这组程序主要完成USB协议的处理和设备的读写操作。USB设备固件和USB驱动之间通信的规范是通过USB协议来完成的。

4.USB设备的逻辑结构和端点的传输方式

    USB设备的逻辑结构包括设备、配置、接口和端点,分别用usb_device、usb_host_config、 usb_interface、usb_host_endpoint表示。

    端点的传输方式包括控制传输、中断传输、批量传输、等时传输。

    控制传输主要用于向设备发送配置信息、获取设备信息、发送命令道设备,或者获取设备的状态报告。控制传输一般发送的数据量较小,当USB设备插入时,USB核心使用端点0对设备进行配置,另外,端口0与其他端点不一样,端点0可以双向传输。

    中断传输就是中断端点以一个固定的速度来传输较少的数据, USB键盘和鼠标就是使用这个传输方式。这里说的中断和硬件上下文中的中断不一样,它不是设备主动发送一个中断请求,而是主机控制器在保证不大于某个时间间隔内安排一次传输。中断传输对时间要求比较严格,所以可以用中断传输来不断地检测某个设备,当条件满足后再使用批量传输传输大量的数据。

    批量传输通常用在数据量大、对数据实时性要求不高的场合,例如 USB打印机、扫描仪、大容量存储设备、U盘等。

    等时传输同样可以传输大批量数据,但是对数据是否到达没有保证,它对实时性的要求很高,例如 音频、视频等设备。

5.USB的URB请求块

    USB请求块(USB request block,urb)是USB主机控制器和设备通信的主要数据结构,主机和设备之间通过urb进行数据传输。当主机控制器需要与设备交互时,只需要填充一个urb结构,然后将其提交给USB核心,由USB核心负责对其进行处理。

   URB处理流程:

   Step1:创建一个URB结构体 usb_alloc_urb()

   Step2:初始化,被安排一个特定的USB设备的特定端点。fill_int/bulk/control_urb()

   Step3:被USB设备驱动提交给USB核心usb_submit_urb(),注意GPF_ATOMIC,GPF_NOIO,GPF_KERNEL的使用区别。

   Step4:提交由USB核心指定的USB主机控制器驱动,被主机控制器驱动处理,进行一次到USB设备的传输,该过程由USB核心和主机控制器完成,不受USB设备驱动控制

   Step5:当urb完成,USB主机控制器驱动通知USB设备驱动。

    简单的批量与控制URB:

    有时候USB驱动程序只是从USB设备上接收或发送一些简单的数据,这时候可以使用usb_bulk/control_msg()完成,这两个函数是同步的,因此不能在中断上下文和持有自旋锁的情况下使用。

6.USB的枚举过程

    内核辅助线程khubd用来监视与该集线器连接的所有端口,通常情况下,该线程处于休眠状态,当集线器驱动程序检测到USB端口状态变化后,该内核线程立马唤醒。

    USB的枚举过程:USB的枚举过程是热插拔USB设备的起始步骤,该过程中,主机控制器获取设备的相关信息并配置好设备,集线器驱动程序负责该枚举过程。枚举过程主要分如下几步:

    Step1:根集线器报告插入设备导致的端口电流变化,集线器驱动程序检测到这一状态变化后,唤醒khubd线程。

    Step2:khubd识别出电流变化的那个端口。

    Step3:khubd通过给控制端点0发送控制URB来实现从1-127中选出一个数作为插入设备的批量端点。

    Step4:khubd利用端口0使用的控制URB从插入的设备那里获得设备描述符,然后获得配置描述符,并选择一个合适的。

    Step5:khubd请求USB核心把对应的客户驱动程序和该USB设备挂钩。

二.USB驱动分析

    内核代码分析包括USB驱动框架、鼠标驱动、键盘驱动、U盘驱动:

USB驱动编写的主要框架/drivers/usb/usb-skeleton.c

USB鼠标驱动 /drivers/hid/usbhid/usbmouse.c

USB键盘驱动动 /drivers/hid/usbhid/usbkbd.c

USB Mass Storage是一类USB存储设备, U盘便是其中之一,主要分析的驱动文件是/drivers/usb/storage/usb.c

1.USB驱动框架usb-skeleton.c

    USB骨架程序可以看做一个最简单的USB设备驱动的实例,其分析流程大致如下:

module_init(usb_skel_init)---->usb_skel_init()中的usb_register(&skel_driver)---->(驱动中的id_table和设备配置能对上号)skel_driver中的 .probe = skel_probe

---->probe函数中usb_register_dev(interface, &skel_class)语句---->skel_class 中的.fops = &skel_fops---->skel_fops中就是系统调用对应的函数指针,对应的函数

实现了系统调用函数的具体功能,这些函数仅当进行系统调用的时候被执行。

    首先看看USB骨架程序的usb_driver的定义:

static struct usb_driver skel_driver = {

    .name =        "skeleton",

    .probe =    skel_probe,  //设备探测

    .disconnect =    skel_disconnect,

    .suspend =    skel_suspend,

    .resume =    skel_resume,

    .pre_reset =    skel_pre_reset,

    .post_reset =    skel_post_reset,

    .id_table =    skel_table,  //设备支持项

    .supports_autosuspend = 1,

};

#define USB_SKEL_VENDOR_ID    0xfff0

#define USB_SKEL_PRODUCT_ID    0xfff0

static const struct usb_device_id skel_table[] = {

    { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },

    { }                    

};

MODULE_DEVICE_TABLE(usb, skel_table);

由上面代码可见,通过USB_DEVICE宏定义了设备支持项。

对上面usb_driver的注册和注销发送在USB骨架程序的模块加载和卸载函数中。

static int __init usb_skel_init(void)

{

    int result;

    result = usb_register(&skel_driver);  //将该驱动挂在USB总线上

    if (result)

        err("usb_register failed. Error number %d", result);

    return result;

}

一个设备被安装或者有设备插入后,当USB总线上经过match匹配成功,就会调用设备驱动程序中的probe探测函数,向探测函数传递设备的信息,以便确定驱动程序是否支持该设备。

static int skel_probe(struct usb_interface *interface,

              const struct usb_device_id *id)

{

    struct usb_skel *dev;  //特定设备结构体

    struct usb_host_interface *iface_desc;  //接口设置

    struct usb_endpoint_descriptor *endpoint;  //端点描述符

    size_t buffer_size;

    int i;

    int retval = -ENOMEM;

    dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配内存

    if (!dev) {

        err("Out of memory");

        goto error;

    }

    kref_init(&dev->kref);  //引用计数初始化

    sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);  //初始化信号量

    mutex_init(&dev->io_mutex);  //初始化互斥锁

    spin_lock_init(&dev->err_lock);  //初始化自旋锁

    init_usb_anchor(&dev->submitted);

    init_completion(&dev->bulk_in_completion);  //初始化完成量

    dev->udev = usb_get_dev(interface_to_usbdev(interface)); //获取usb_device结构体

    dev->interface = interface;  //获取usb_interface结构体

    iface_desc = interface->cur_altsetting;  //由接口获取当前设置

    for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {  //根据端点个数逐一扫描端点

        endpoint = &iface_desc->endpoint[i].desc;  //由设置获取端点描述符

        if (!dev->bulk_in_endpointAddr &&

            usb_endpoint_is_bulk_in(endpoint)) {  //如果端点为批量输入端点

            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);  //缓冲大小

            dev->bulk_in_size = buffer_size;  //缓冲大小

            dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;  //端点地址

            dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);  //缓冲区

            if (!dev->bulk_in_buffer) {

                err("Could not allocate bulk_in_buffer");

                goto error;

            }

            dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);  //分配urb空间

            if (!dev->bulk_in_urb) {

                err("Could not allocate bulk_in_urb");

                goto error;

            }

        }

        if (!dev->bulk_out_endpointAddr &&

            usb_endpoint_is_bulk_out(endpoint)) {  //如果该端点为批量输出端点

            dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;  //端点地址

        }

    }

    if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {  //都不是批量端点

        err("Could not find both bulk-in and bulk-out endpoints");

        goto error;

    }

    usb_set_intfdata(interface, dev);  //将特定设备结构体设置为接口的私有数据

    retval = usb_register_dev(interface, &skel_class);  //注册 USB设备

    if (retval) {

        err("Not able to get a minor for this device.");

        usb_set_intfdata(interface, NULL);

        goto error;

    }

    dev_info(&interface->dev,

         "USB Skeleton device now attached to USBSkel-%d",

         interface->minor);

    return 0;

error:

    if (dev)

        kref_put(&dev->kref, skel_delete);

    return retval;

}

通过上面分析,我们知道,usb_driver的probe函数中根据usb_interface的成员寻找第一个批量输入和输出的端点,将端点地址、缓冲区等信息存入USB骨架程序定义的usb_skel结构体中,并将usb_skel通过usb_set_intfdata传为USB接口的私有数据,最后注册USB设备。

我们来看看这个USB骨架程序定义的usb_skel结构体

struct usb_skel {

    struct usb_device    *udev;            

    struct usb_interface    *interface;        

    struct semaphore    limit_sem;        

    struct usb_anchor    submitted;        

    struct urb        *bulk_in_urb;        

    unsigned char           *bulk_in_buffer;    

    size_t            bulk_in_size;        

    size_t            bulk_in_filled;        

    size_t            bulk_in_copied;        

    __u8            bulk_in_endpointAddr;    

    __u8            bulk_out_endpointAddr;    

    int            errors;            

    int            open_count;        

    bool            ongoing_read;        

    bool            processed_urb;        

    spinlock_t        err_lock;        

    struct kref        kref;

    struct mutex        io_mutex;        

    struct completion    bulk_in_completion;    

};

好了看完了probe,我们再看看disconnect函数

static void skel_disconnect(struct usb_interface *interface)

{

    struct usb_skel *dev;

    int minor = interface->minor;  //获得接口的次设备号

    dev = usb_get_intfdata(interface);  //获得接口的私有数据

    usb_set_intfdata(interface, NULL);  //设置接口的私有数据为空

    usb_deregister_dev(interface, &skel_class);  //注销USB设备

    mutex_lock(&dev->io_mutex);

    dev->interface = NULL;

    mutex_unlock(&dev->io_mutex);

    usb_kill_anchored_urbs(&dev->submitted);

    kref_put(&dev->kref, skel_delete);

    dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);

}

我们在skel_probe中最后执行了usb_register_dev(interface, &skel_class)来注册了一个USB设备,我们看看skel_class的定义

static struct usb_class_driver skel_class = {

    .name =        "skel%d",

    .fops =        &skel_fops,

    .minor_base =    USB_SKEL_MINOR_BASE,

};

static const struct file_operations skel_fops = {

    .owner =    THIS_MODULE,

    .read =        skel_read,

    .write =    skel_write,

    .open =        skel_open,

    .release =    skel_release,

    .flush =    skel_flush,

    .llseek =    noop_llseek,

};

根据上面代码我们知道,其实我们在probe中注册USB设备的时候使用的skel_class是一个包含file_operations的结构体,而这个结构体正是字符设备文件操作结构体。

我们先来看看这个file_operations中open函数的实现

static int skel_open(struct inode *inode, struct file *file)

{

    struct usb_skel *dev;

    struct usb_interface *interface;

    int subminor;

    int retval = 0;

    subminor = iminor(inode);  //获得次设备号

    interface = usb_find_interface(&skel_driver, subminor);  //根据 usb_driver和次设备号获取设备的接口

    if (!interface) {

        err("%s - error, can't find device for minor %d",

             __func__, subminor);

        retval = -ENODEV;

        goto exit;

    }

    dev = usb_get_intfdata(interface);  //获取接口的私有数据 usb_skel

    if (!dev) {

        retval = -ENODEV;

        goto exit;

    }

    kref_get(&dev->kref);

    mutex_lock(&dev->io_mutex);

    if (!dev->open_count++) {

        retval = usb_autopm_get_interface(interface);

            if (retval) {

                dev->open_count--;

                mutex_unlock(&dev->io_mutex);

                kref_put(&dev->kref, skel_delete);

                goto exit;

            }

    }

    file->private_data = dev;  //将 usb_skel设置为文件的私有数据

    mutex_unlock(&dev->io_mutex);

exit:

    return retval;

}

这个open函数实现非常简单,它根据usb_driver和次设备号通过usb_find_interface获取USB接口,然后通过usb_get_intfdata获得接口的私有数据并赋值给文件。

好了,我们看看write函数,在write函数中,我们进行了urb的分配、初始化和提交的操作

static ssize_t skel_write(struct file *file, const char *user_buffer,

              size_t count, loff_t *ppos)

{

    struct usb_skel *dev;

    int retval = 0;

    struct urb *urb = NULL;

    char *buf = NULL;

    size_t writesize = min(count, (size_t)MAX_TRANSFER);  //待写数据大小

    dev = file->private_data;  //获取文件的私有数据

    if (count == 0)

        goto exit;

    if (!(file->f_flags & O_NONBLOCK)) {  //如果文件采用非阻塞方式

        if (down_interruptible(&dev->limit_sem)) {  //获取限制读的次数的信号量

            retval = -ERESTARTSYS;

            goto exit;

        }

    } else {

        if (down_trylock(&dev->limit_sem)) {

            retval = -EAGAIN;

            goto exit;

        }

    }

    spin_lock_irq(&dev->err_lock);  //关中断

    retval = dev->errors;

    if (retval < 0) {

        dev->errors = 0;

        retval = (retval == -EPIPE) ? retval : -EIO;

    }

    spin_unlock_irq(&dev->err_lock);  //开中断

    if (retval < 0)

        goto error;

    urb = usb_alloc_urb(0, GFP_KERNEL);  //分配urb

    if (!urb) {

        retval = -ENOMEM;

        goto error;

    }

    buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,

                 &urb->transfer_dma);  //分配写缓存

    if (!buf) {

        retval = -ENOMEM;

        goto error;

    }

    if (copy_from_user(buf, user_buffer, writesize)) {  //将用户空间数据拷贝到缓冲区

        retval = -EFAULT;

        goto error;

    }

    mutex_lock(&dev->io_mutex);

    if (!dev->interface) {        

        mutex_unlock(&dev->io_mutex);

        retval = -ENODEV;

        goto error;

    }

    usb_fill_bulk_urb(urb, dev->udev,

              usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),

              buf, writesize, skel_write_bulk_callback, dev);  //填充urb

    urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    usb_anchor_urb(urb, &dev->submitted);  //提交urb

    retval = usb_submit_urb(urb, GFP_KERNEL);

    mutex_unlock(&dev->io_mutex);

    if (retval) {

        err("%s - failed submitting write urb, error %d", __func__,

            retval);

        goto error_unanchor;

    }

    usb_free_urb(urb);

    return writesize;

error_unanchor:

    usb_unanchor_urb(urb);

error:

    if (urb) {

        usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);

        usb_free_urb(urb);

    }

    up(&dev->limit_sem);

exit:

    return retval;

}

首先说明一个问题,填充urb后,设置了transfer_flags标志,当transfer_flags中的URB_NO_TRANSFER_DMA_MAP被设置,USB核心使用transfer_dma指向的缓冲区而不是使用transfer_buffer指向的缓冲区,这表明即将传输DMA缓冲区。当transfer_flags中的URB_NO_SETUP_DMA_MAP被设置,如果控制urb有DMA缓冲区,USB核心将使用setup_dma指向的缓冲区而不是使用setup_packet指向的缓冲区。

另外,通过上面这个write函数我们知道,当写函数发起的urb结束后,其完成函数skel_write_bulk_callback会被调用,我们继续跟踪

static void skel_write_bulk_callback(struct urb *urb)

{

    struct usb_skel *dev;

    dev = urb->context;

    if (urb->status) {

        if (!(urb->status == -ENOENT ||

            urb->status == -ECONNRESET ||

            urb->status == -ESHUTDOWN))

            err("%s - nonzero write bulk status received: %d",

                __func__, urb->status);  //出错显示

        spin_lock(&dev->err_lock);

        dev->errors = urb->status;

        spin_unlock(&dev->err_lock);

    }

    usb_free_coherent(urb->dev, urb->transfer_buffer_length,

              urb->transfer_buffer, urb->transfer_dma);  //释放urb空间

    up(&dev->limit_sem);

}

很明显,skel_write_bulk_callback主要对urb->status进行判断,根据错误提示显示错误信息,然后释放urb空间。

接着,我们看看USB骨架程序的字符设备的read函数

static ssize_t skel_read(struct file *file, char *buffer, size_t count,

             loff_t *ppos)

{

    struct usb_skel *dev;

    int rv;

    bool ongoing_io;

    dev = file->private_data;  //获得文件私有数据

    if (!dev->bulk_in_urb || !count)  //正在写的时候禁止读操作

        return 0;

    rv = mutex_lock_interruptible(&dev->io_mutex);  //获得锁

    if (rv < 0)

        return rv;

    if (!dev->interface) {        

        rv = -ENODEV;

        goto exit;

    }

retry:

    spin_lock_irq(&dev->err_lock);

    ongoing_io = dev->ongoing_read;

    spin_unlock_irq(&dev->err_lock);

    if (ongoing_io) {  //USB核正在读取数据中,数据没准备好

        if (file->f_flags & O_NONBLOCK) {  //如果为非阻塞,则结束

            rv = -EAGAIN;

            goto exit;

        }

        rv = wait_for_completion_interruptible(&dev->bulk_in_completion);  //等待

        if (rv < 0)

            goto exit;

        dev->bulk_in_copied = 0;  //拷贝到用户空间操作已成功

        dev->processed_urb = 1;  //目前已处理好 urb

    }

    if (!dev->processed_urb) {  //目前还没已处理好 urb

        wait_for_completion(&dev->bulk_in_completion);  //等待完成

        dev->bulk_in_copied = 0;

        dev->processed_urb = 1;

    }

    rv = dev->errors;

    if (rv < 0) {

        dev->errors = 0;

        rv = (rv == -EPIPE) ? rv : -EIO;

        dev->bulk_in_filled = 0;

        goto exit;

    }

    if (dev->bulk_in_filled) {  //缓冲区有内容

        size_t available = dev->bulk_in_filled - dev->bulk_in_copied;  //可读数据大小为缓冲区内容减去已经拷贝到用户空间的数据大小

        size_t chunk = min(available, count);  //真正读取数据大小

        if (!available) {

            rv = skel_do_read_io(dev, count);  //没可读数据则调用 IO操作

            if (rv < 0)

                goto exit;

            else

                goto retry;

        }

        if (copy_to_user(buffer,

                 dev->bulk_in_buffer + dev->bulk_in_copied,

                 chunk))  //拷贝缓冲区数据到用户空间

            rv = -EFAULT;

        else

            rv = chunk;

        dev->bulk_in_copied += chunk;  //目前拷贝完成的数据大小

        if (available < count)  //剩下可用数据小于用户需要的数据

            skel_do_read_io(dev, count - chunk);  //调用 IO操作

    } else {

        rv = skel_do_read_io(dev, count);    //缓冲区没数据则调用 IO操作

        if (rv < 0)

            goto exit;

        else if (!(file->f_flags & O_NONBLOCK))

            goto retry;

        rv = -EAGAIN;

    }

exit:

    mutex_unlock(&dev->io_mutex);

    return rv;

}

通过上面read函数,我们知道,在读取数据时候,如果发现缓冲区没有数据,或者缓冲区的数据小于用户需要读取的数据量时,则会调用IO操作,也就是skel_do_read_io函数。

static int skel_do_read_io(struct usb_skel *dev, size_t count)

{

    int rv;

    usb_fill_bulk_urb(dev->bulk_in_urb,

            dev->udev,

            usb_rcvbulkpipe(dev->udev,

                dev->bulk_in_endpointAddr),

            dev->bulk_in_buffer,

            min(dev->bulk_in_size, count),

            skel_read_bulk_callback,

            dev);  //填充 urb

    spin_lock_irq(&dev->err_lock);

    dev->ongoing_read = 1;  //标志正在读取数据中

    spin_unlock_irq(&dev->err_lock);

    rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);  //提交 urb

    if (rv < 0) {

        err("%s - failed submitting read urb, error %d",

            __func__, rv);

        dev->bulk_in_filled = 0;

        rv = (rv == -ENOMEM) ? rv : -EIO;

        spin_lock_irq(&dev->err_lock);

        dev->ongoing_read = 0;

        spin_unlock_irq(&dev->err_lock);

    }

    return rv;

}

好了,其实skel_do_read_io只是完成了urb的填充和提交,USB核心读取到了数据后,会调用填充urb时设置的回调函数skel_read_bulk_callback。

static void skel_read_bulk_callback(struct urb *urb)

{

    struct usb_skel *dev;

    dev = urb->context;

    spin_lock(&dev->err_lock);

    if (urb->status) {  //根据返回状态判断是否出错

        if (!(urb->status == -ENOENT ||

            urb->status == -ECONNRESET ||

            urb->status == -ESHUTDOWN))

            err("%s - nonzero write bulk status received: %d",

                __func__, urb->status);

        dev->errors = urb->status;

    } else {

        dev->bulk_in_filled = urb->actual_length;  //记录缓冲区的大小

    }

    dev->ongoing_read = 0;  //已经读取数据完毕

    spin_unlock(&dev->err_lock);

    complete(&dev->bulk_in_completion);  //唤醒 skel_read函数

}

好了,到目前为止,我们已经把USB驱动框架usb-skeleton.c分析完了,总结下,其实很简单,在模块加载里面注册usb_driver,然后在probe函数里初始化一些参数,最重要的是注册了USB设备,这个USB设备相当于一个字符设备,提供file_operations接口。然后设计open,close,read,write函数,这个open里基本没做什么事情,在write中,通过分配urb、填充urb和提交urb。注意读的urb的分配在probe里申请空间,写的urb的分配在write里申请空间。在这个驱动程序中,我们重点掌握usb_fill_bulk_urb的设计。

2.USB鼠标驱动usbmouse.c

下面我们分析下USB鼠标驱动,鼠标输入HID类型,其数据传输采用中断URB,鼠标端点类型为IN。好了,我们先看看这个驱动的模块加载部分。

static int __init usb_mouse_init(void)

{

    int retval = usb_register(&usb_mouse_driver);

    if (retval == 0)

        printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"

                DRIVER_DESC "\n");

    return retval;

}

模块加载部分仍然是调用usb_register注册USB驱动,我们跟踪看看被注册的usb_mouse_drive。

static struct usb_driver usb_mouse_driver = {

    .name        = "usbmouse",

    .probe        = usb_mouse_probe,

    .disconnect    = usb_mouse_disconnect,

    .id_table    = usb_mouse_id_table,

};

关于设备支持项我们前面已经讨论过了

static struct usb_device_id usb_mouse_id_table [] = {

    { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,

        USB_INTERFACE_PROTOCOL_MOUSE) },

    { }    

};

再细细看看USB_INTERFACE_INFO宏的定义

#define USB_INTERFACE_INFO(cl, sc, pr) \

    .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \

    .bInterfaceClass = (cl), \

    .bInterfaceSubClass = (sc), \

    .bInterfaceProtocol = (pr)

根据宏,我们知道,我们设置的支持项包括接口类,接口子类,接口协议三个匹配项。

好了,我们主要看看usb_driver中定义的probe函数

static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)

{

    struct usb_device *dev = interface_to_usbdev(intf);//由接口获取 usb_device 

    struct usb_host_interface *interface; //设置

    struct usb_endpoint_descriptor *endpoint; //端点描述符

    struct usb_mouse *mouse; //本驱动私有结构体

    struct input_dev *input_dev; //输入结构体

    int pipe, maxp;

    int error = -ENOMEM;

    interface = intf->cur_altsetting; //获取设置

    if (interface->desc.bNumEndpoints != 1) //鼠标端点只有 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)); //返回端点能传输的最大的数据包,鼠标的返回的最大数据包为 4个字节

    mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); //分配 mouse结构体

    input_dev = input_allocate_device(); //分配 input设备空间

    if (!mouse || !input_dev)

        goto fail1;

    mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma); //分配缓冲区

    if (!mouse->data)

        goto fail1;

    mouse->irq = usb_alloc_urb(0, GFP_KERNEL); //分配 urb

    if (!mouse->irq)

        goto fail2;

    mouse->usbdev = dev; //填充 mouse的 usb_device结构体

    mouse->dev = input_dev; //填充 mouse的 input结构体

    if (dev->manufacturer) //拷贝厂商 ID

        strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));

    if (dev->product) { //拷贝产品 ID

        if (dev->manufacturer)

            strlcat(mouse->name, " ", sizeof(mouse->name));

        strlcat(mouse->name, dev->product, sizeof(mouse->name));

    }

    if (!strlen(mouse->name)) //拷贝产品 ID

        snprintf(mouse->name, sizeof(mouse->name),

             "USB HIDBP Mouse %04x:%04x",

             le16_to_cpu(dev->descriptor.idVendor),

             le16_to_cpu(dev->descriptor.idProduct));

    usb_make_path(dev, mouse->phys, sizeof(mouse->phys));

    strlcat(mouse->phys, "/input0", sizeof(mouse->phys));

    input_dev->name = mouse->name; //将鼠标名赋给内嵌 input结构体

    input_dev->phys = mouse->phys; //将鼠标设备节点名赋给内嵌 input结构体

    usb_to_input_id(dev, &input_dev->id); //将 usb_driver的支持项拷贝给 input

    input_dev->dev.parent = &intf->dev;

    input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); //evbit表明支持按键事件 (EV_KEY)和相对坐标事件 (EV_REL)             

    input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |

    BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); //keybit表明按键值包括左键、右键和中键

    input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); //relbit表明相对坐标事件值包括 X坐标和 Y坐标

    input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |

        BIT_MASK(BTN_EXTRA); //keybit表明除了左键、右键和中键,还支持其他按键

    input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); //relbit表明除了 X坐标和 Y坐标,还支持中键滚轮的滚动值

    input_set_drvdata(input_dev, mouse); //将 mouse设置为 input的私有数据

    input_dev->open = usb_mouse_open; //input设备的 open

    input_dev->close = usb_mouse_close;

    usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,

             (maxp > 8 ? 8 : maxp),

             usb_mouse_irq, mouse, endpoint->bInterval); //填充 urb

    mouse->irq->transfer_dma = mouse->data_dma;

    mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /使用 transfer_dma

    error = input_register_device(mouse->dev); //注册 input设备

    if (error)

        goto fail3;

    usb_set_intfdata(intf, mouse);

    return 0;

fail3:    

    usb_free_urb(mouse->irq);

fail2:    

    usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);

fail1:    

    input_free_device(input_dev);

    kfree(mouse);

    return error;

}

其实上面这个probe主要是初始化usb设备和input设备,终极目标是为了完成urb的提交和input设备的注册。由于注册为input设备类型,那么当用户层open打开设备时候,最终会调用input中的open实现打开,我们看看input中open的实现

static int usb_mouse_open(struct input_dev *dev)

{

    struct usb_mouse *mouse = input_get_drvdata(dev); //获取私有数据

    mouse->irq->dev = mouse->usbdev; //获取 urb指针

    if (usb_submit_urb(mouse->irq, GFP_KERNEL)) //提交 urb

        return -EIO;

    return 0;

}

好了,当用户层open打开这个USB鼠标后,我们就已经将urb提交给了USB核心,那么根据USB数据处理流程知道,当处理完毕后,USB核心会通知USB设备驱动程序,这里我们是响应中断服务程序,这就相当于该URB的回调函数。我们在提交urb时候定义了中断服务程序usb_mouse_irq,我们跟踪看看

static void usb_mouse_irq(struct urb *urb)

{

    struct usb_mouse *mouse = urb->context;

    signed char *data = mouse->data;

    struct input_dev *dev = mouse->dev;

    int status;

    switch (urb->status) {

    case 0:            

        break;

    case -ECONNRESET:    

    case -ENOENT:

    case -ESHUTDOWN:

        return;

    default:        

        goto resubmit;

    }

    input_report_key(dev, BTN_LEFT,   data[0] & 0x01);//鼠标左键

    input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);

    input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);

    input_report_key(dev, BTN_SIDE,   data[0] & 0x08);

    input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);

    input_report_rel(dev, REL_X,     data[1]);

    input_report_rel(dev, REL_Y,     data[2]);

    input_report_rel(dev, REL_WHEEL, data[3]);

    input_sync(dev);

resubmit:

    status = usb_submit_urb (urb, GFP_ATOMIC);//再次提交urb,等待下次响应

    if (status)

        err ("can't resubmit intr, %s-%s/input0, status %d",

                mouse->usbdev->bus->bus_name,

                mouse->usbdev->devpath, status);

}

根据上面的中断服务程序,我们应该知道,系统是周期性地获取鼠标的事件信息,因此在URB回调函数的末尾再次提交URB请求块,这样又会调用新的回调函数,周而复始。在回调函数中提交URB只能是GFP_ATOMIC优先级,因为URB回调函数运行于中断上下文中禁止导致睡眠的行为。而在提交URB过程中可能会需要申请内存、保持信号量,这些操作或许会导致USB内核睡眠。

最后我们再看看这个驱动的私有数据mouse的定义

struct usb_mouse {

    char name[128];//名字

    char phys[64];//设备节点

    struct usb_device *usbdev;//内嵌usb_device设备

    struct input_dev *dev;//内嵌input_dev设备

    struct urb *irq;//urb结构体

    signed char *data;//transfer_buffer缓冲区

    dma_addr_t data_dma;// transfer _dma缓冲区

};

在上面这个结构体中,每一个成员的作用都应该很清楚了,尤其最后两个的使用区别和作用,前面也已经说过。

如果最终需要测试这个USB鼠标驱动,需要在内核中配置USB支持、对HID接口的支持、对OHCI HCD驱动的支持。另外,将驱动移植到开发板之后,由于采用的是input设备模型,所以还需要开发板带LCD屏才能测试。

3.USB键盘驱动usbkbd.c

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

    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,

};

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;

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

    strlcat(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->transfer_dma = kbd->leds_dma;

    kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //设置dma和setup_dma有效

    error = input_register_device(kbd->dev);//注册input设备

    if (error)

        goto fail2;

    usb_set_intfdata(iface, kbd);

    device_set_wakeup_enable(&dev->dev, 1);

    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的实现。其二,设置控制请求的格式。

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_alloc_coherent(dev, 8, GFP_ATOMIC, &kbd->new_dma)))

        return -1;

    if (!(kbd->cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL))) //分配控制urb使用的控制请求描述符

        return -1;

    if (!(kbd->leds = usb_alloc_coherent(dev, 1, GFP_ATOMIC, &kbd->leds_dma))) //分配控制urb使用的缓冲区

        return -1;

    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

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_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);//向input子系统报告

    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

                hid_info(urb->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

                hid_info(urb->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)

        hid_err(urb->dev, "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;

    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));//将当前的LED值保存在kbd->newleds中

    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

        pr_err("usb_submit_urb(leds) failed\n");

    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)//提交失败显示

        hid_warn(urb->dev, "led urb status %d received\n",

             urb->status);

    if (*(kbd->leds) == kbd->newleds)//比较kbd->leds和kbd->newleds,如果发生变化,则更新kbd->leds

        return;

    *(kbd->leds) = kbd->newleds;

    kbd->led->dev = kbd->usbdev;

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))//再次提交控制urb

        hid_err(urb->dev, "usb_submit_urb(leds) failed\n");

}

总结下,我们的控制urb走的是先由input的event提交,触发后由控制urb的回调函数再次提交。好了,通过USB鼠标,我们已经知道了控制urb和中断urb的设计和处理流程。

4.U盘驱动分析

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

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

ccflags-y := -Idrivers/scsi

obj-$(CONFIG_USB_UAS)        += uas.o

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

usb-storage-y := scsiglue.o protocol.o transport.o usb.o

usb-storage-y += initializers.o sierra_ms.o option_ms.o

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

这是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;

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

    retval = usb_register(&usb_storage_driver);

    if (retval == 0) {

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

    .supports_autosuspend = 1,

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

        dev_warn(&intf->dev,

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

        return -ENOMEM;

    }

    host->max_cmd_len = 16;

    host->sg_tablesize = usb_stor_sg_tablesize(intf);

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

    struct device *dev = &us->pusb_intf->dev;

    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;

    if (us->fflags & US_FL_INITIAL_READ10)

        set_bit(US_FLIDX_REDO_READ10, &us->dflags);

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

    if (result)

        goto BadDevice;

    snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s",

                    dev_name(&us->pusb_intf->dev));

    result = scsi_add_host(us_to_host(us), dev);//添加scsi

    if (result) {

        dev_warn(dev,

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

        goto BadDevice;

    }

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

    if (IS_ERR(th)) {

        dev_warn(dev,

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

        complete(&us->scanning_done);

        quiesce_and_remove_host(us);

        result = PTR_ERR(th);

        goto BadDevice;

    }

    usb_autopm_get_interface_no_resume(us->pusb_intf);

    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设置为接口的私有数据

    us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL);//分配控制urb的控制字符空间

    if (!us->cr) {

        US_DEBUGP("usb_ctrlrequest allocation failed\n");

        return -ENOMEM;

    }

    us->iobuf = usb_alloc_coherent(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;

    struct device *pdev = &us->pusb_intf->dev;

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

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

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

            idesc->bInterfaceSubClass :

            unusual_dev->useProtocol;

    us->protocol = (unusual_dev->useTransport == USB_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设备不能被系统识别则退出

        dev_info(pdev, "device ignored\n");

        return -ENODEV;

    }

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

        us->fflags &= ~US_FL_GO_SLOW;

    if (us->fflags)

        dev_info(pdev, "Quirks match for vid %04x pid %04x: %lx\n",

                le16_to_cpu(dev->descriptor.idVendor),

                le16_to_cpu(dev->descriptor.idProduct),

                us->fflags);

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

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

            us->subclass == idesc->bInterfaceSubClass)

            msg += 1;

        if (unusual_dev->useTransport != USB_PR_DEVICE &&

            us->protocol == idesc->bInterfaceProtocol)

            msg += 2;

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

            dev_notice(pdev, "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 "

                    "<[email protected]> and "

                    "<[email protected]>\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 USB_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 USB_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 USB_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 USB_SC_RBC:

        us->protocol_name = "Reduced Block Commands (RBC)";

        us->proto_handler = usb_stor_transparent_scsi_command;//协议处理函数

        break;

    case USB_SC_8020:

        us->protocol_name = "8020i";

        us->proto_handler = usb_stor_pad12_command;

        us->max_lun = 0;

        break;

    case USB_SC_QIC:

        us->protocol_name = "QIC-157";

        us->proto_handler = usb_stor_pad12_command;

        us->max_lun = 0;

        break;

    case USB_SC_8070:

        us->protocol_name = "8070i";

        us->proto_handler = usb_stor_pad12_command;

        us->max_lun = 0;

        break;

    case USB_SC_SCSI:

        us->protocol_name = "Transparent SCSI";

        us->proto_handler = usb_stor_transparent_scsi_command;

        break;

    case USB_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 == USB_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)) {

        dev_warn(&us->pusb_intf->dev,

                "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))//等待用户层SCSI命令唤醒

            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); //数据传输

            usb_mark_last_busy(us->pusb_dev);

        }

        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;

    struct device *dev = &us->pusb_intf->dev;

    dev_dbg(dev, "device found\n");

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

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

        dev_dbg(dev, "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 == USB_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));

        dev_dbg(dev, "scan complete\n");

    }

    usb_autopm_put_interface(us->pusb_intf);

    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->transfer_flags = 0;

    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;

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

    if (status) {

        return status;

    }

    set_bit(US_FLIDX_URB_ACTIVE, &us->dflags);

    if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {//当前还没取消urb时,取消urb请求

        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

继续阅读