USB驱动及其源码分析

时间:2021-01-07 16:13:00

一.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 these values to match your devices */
#define USB_SKEL_VENDOR_ID    0xfff0
#define USB_SKEL_PRODUCT_ID    0xfff0

/* table of devices that work with this driver */
static const struct usb_device_id skel_table[] = {
    { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
    { }                    /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, skel_table);

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

对上面usb_driver的注册和注销发送在USB骨架程序的模块加载和卸载函数中。
static int __init usb_skel_init(void)
{
    int result;

    /* register this driver with the USB subsystem */
    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;

    /* allocate memory for our device state and initialize it */
    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结构体

    /* set up the endpoint information */
    /* use only the first bulk-in and bulk-out endpoints */
    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)) {  //如果端点为批量输入端点
            /* we found a 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)) {  //如果该端点为批量输出端点
            /* we found a 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;
    }

    /* save our data pointer in this interface device */
    usb_set_intfdata(interface, dev);  //将特定设备结构体设置为接口的私有数据

    /* we can register the device now, as it is ready */
    retval = usb_register_dev(interface, &skel_class);  //注册 USB设备
    if (retval) {
        /* something prevented us from registering this driver */
        err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
    }

    /* let the user know what node this device is now attached to */
    dev_info(&interface->dev,
         "USB Skeleton device now attached to USBSkel-%d",
         interface->minor);
    return 0;

error:
    if (dev)
        /* this frees allocated memory */
        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;            /* the usb device for this device */
    struct usb_interface    *interface;        /* the interface for this device */
    struct semaphore    limit_sem;        /* limiting the number of writes in progress */
    struct usb_anchor    submitted;        /* in case we need to retract our submissions */
    struct urb        *bulk_in_urb;        /* the urb to read data with */
    unsigned char           *bulk_in_buffer;    /* the buffer to receive data */
    size_t            bulk_in_size;        /* the size of the receive buffer */
    size_t            bulk_in_filled;        /* number of bytes in the buffer */
    size_t            bulk_in_copied;        /* already copied to user space */
    __u8            bulk_in_endpointAddr;    /* the address of the bulk in endpoint */
    __u8            bulk_out_endpointAddr;    /* the address of the bulk out endpoint */
    int            errors;            /* the last request tanked */
    int            open_count;        /* count the number of openers */
    bool            ongoing_read;        /* a read is going on */
    bool            processed_urb;        /* indicates we haven't processed the urb */
    spinlock_t        err_lock;        /* lock for errors */
    struct kref        kref;
    struct mutex        io_mutex;        /* synchronize I/O with disconnect */
    struct completion    bulk_in_completion;    /* to wait for an ongoing read */
};
好了看完了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);  //设置接口的私有数据为空

    /* give back our minor */
    usb_deregister_dev(interface, &skel_class);  //注销USB设备

    /* prevent more I/O from starting */
    mutex_lock(&dev->io_mutex);
    dev->interface = NULL;
    mutex_unlock(&dev->io_mutex);

    usb_kill_anchored_urbs(&dev->submitted);

    /* decrement our usage count */
    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;
    }

    /* increment our usage count for the device */
    kref_get(&dev->kref);

    /* lock the device to allow correctly handling errors
     * in resumption */
    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;
            }
    } /* else { //uncomment this block if you want exclusive open
        retval = -EBUSY;
        dev->open_count--;
        mutex_unlock(&dev->io_mutex);
        kref_put(&dev->kref, skel_delete);
        goto exit;
    } */
    /* prevent the device from being autosuspended */

    /* save our object in the file's private structure */
    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;  //获取文件的私有数据

    /* verify that we actually have some data to write */
    if (count == 0)
        goto exit;

    /*
     * limit the number of URBs in flight to stop a user from using up all
     * RAM
     */
    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) {
        /* any error is reported once */
        dev->errors = 0;
        /* to preserve notifications about reset */
        retval = (retval == -EPIPE) ? retval : -EIO;
    }
    spin_unlock_irq(&dev->err_lock);  //开中断
    if (retval < 0)
        goto error;

    /* create a urb, and a buffer for it, and copy the data to the urb */
    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;
    }

    /* this lock makes sure we don't submit URBs to gone devices */
    mutex_lock(&dev->io_mutex);
    if (!dev->interface) {        /* disconnect() was called */
        mutex_unlock(&dev->io_mutex);
        retval = -ENODEV;
        goto error;
    }

    /* initialize the urb properly */
    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

    /* send the data out the bulk port */
    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;
    }

    /*
     * release our reference to this urb, the USB core will eventually free
     * it entirely
     */
    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;

    /* sync/async unlink faults aren't errors */
    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);
    }

    /* free up our allocated buffer */
    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 we cannot read at all, return EOF */
    if (!dev->bulk_in_urb || !count)  //正在写的时候禁止读操作
        return 0;

    /* no concurrent readers */
    rv = mutex_lock_interruptible(&dev->io_mutex);  //获得锁
    if (rv < 0)
        return rv;

    if (!dev->interface) {        /* disconnect() was called */
        rv = -ENODEV;
        goto exit;
    }

    /* if IO is under way, we must not touch things */
retry:
    spin_lock_irq(&dev->err_lock);
    ongoing_io = dev->ongoing_read;
    spin_unlock_irq(&dev->err_lock);

    if (ongoing_io) {  //USB核正在读取数据中,数据没准备好
        /* nonblocking IO shall not wait */
        if (file->f_flags & O_NONBLOCK) {  //如果为非阻塞,则结束
            rv = -EAGAIN;
            goto exit;
        }
        /*
         * IO may take forever
         * hence wait in an interruptible state
         */
        rv = wait_for_completion_interruptible(&dev->bulk_in_completion);  //等待
        if (rv < 0)
            goto exit;
        /*
         * by waiting we also semiprocessed the urb
         * we must finish now
         */
        dev->bulk_in_copied = 0;  //拷贝到用户空间操作已成功
        dev->processed_urb = 1;  //目前已处理好 urb
    }

    if (!dev->processed_urb) {  //目前还没已处理好 urb
        /*
         * the URB hasn't been processed
         * do it now
         */
        wait_for_completion(&dev->bulk_in_completion);  //等待完成
        dev->bulk_in_copied = 0;
        dev->processed_urb = 1;
    }

    /* errors must be reported */
    rv = dev->errors;
    if (rv < 0) {
        /* any error is reported once */
        dev->errors = 0;
        /* to preserve notifications about reset */
        rv = (rv == -EPIPE) ? rv : -EIO;
        /* no data to deliver */
        dev->bulk_in_filled = 0;
        /* report it */
        goto exit;
    }

    /*
     * if the buffer is filled we may satisfy the read
     * else we need to start IO
     */

    if (dev->bulk_in_filled) {  //缓冲区有内容
        /* we had read data */
        size_t available = dev->bulk_in_filled - dev->bulk_in_copied;  //可读数据大小为缓冲区内容减去已经拷贝到用户空间的数据大小
        size_t chunk = min(available, count);  //真正读取数据大小

        if (!available) {
            /*
             * all data has been used
             * actual IO needs to be done
             */
            rv = skel_do_read_io(dev, count);  //没可读数据则调用 IO操作
            if (rv < 0)
                goto exit;
            else
                goto retry;
        }
        /*
         * data is available
         * chunk tells us how much shall be copied
         */

        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 we are asked for more than we have,
         * we start IO but don't wait
         */
        if (available < count)  // 剩下可用数据小于用户需要的数据
            skel_do_read_io(dev, count - chunk);  //调用 IO操作
    } else {
        /* no data in the buffer */
        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;

    /* prepare a read */
    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
    /* tell everybody to leave the URB alone */
    spin_lock_irq(&dev->err_lock);
    dev->ongoing_read = 1;  //标志正在读取数据中
    spin_unlock_irq(&dev->err_lock);

    /* do it */
    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);
    /* sync/async unlink faults aren't errors */
    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) },
    { }    /* Terminating entry */
};

再细细看看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; // 填充 mouseusb_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:            /* success */
        break;
    case -ECONNRESET:    /* unlink */
    case -ENOENT:
    case -ESHUTDOWN:
        return;
    /* -EPIPE:  should clear the halt */
    default:        /* error */
        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; //设置dmasetup_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:            /* success */
        break;
    case -ECONNRESET:    /* unlink */
    case -ENOENT:
    case -ESHUTDOWN:
        return;
    /* -EPIPE:  should clear the halt */
    default:        /* error */
        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->ledskbd->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");

    /* register the driver, return usb_register return code if error */
    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函数
/* The main probe routine for standard devices */
static int storage_probe(struct usb_interface *intf,
             const struct usb_device_id *id)
{
    struct us_data *us;
    int result;

    /*
     * If libusual is configured, let it decide whether a standard
     * device should be handled by usb-storage or by ub.
     * If the device isn't standard (is handled by a subdriver
     * module) then don't accept it.
     */
    if (usb_usual_check_type(id, USB_US_TYPE_STOR) ||
            usb_usual_ignore_device(intf))//检测匹配
        return -ENXIO;

    /*
     * Call the general probe procedures.
     *
     * The unusual_dev_list array is parallel to the usb_storage_usb_ids
     * table, so we use the index of the id entry to find the
     * corresponding unusual_devs entry.
     */
    result = usb_stor_probe1(&us, intf, id,
            (id - usb_storage_usb_ids) + us_unusual_dev_list);//探测的第一部分
    if (result)
        return result;

    /* No special transport or protocol settings in the main module */

    result = usb_stor_probe2(us);//探测的第二部分
    return result;
}
我们发现U盘驱动的探测分为两个部分,我们先来看看第一个部分usb_stor_probe1
/* First part of general USB mass-storage probing */
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");

    /*
     * Ask the SCSI layer to allocate a host structure, with extra
     * space at the end for our private us_data structure.
     */
    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;
    }

    /*
     * Allow 16-byte CDBs and thus > 2TB
     */
    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);

    /* Associate the us_data structure with the USB device */
    result = associate_dev(us, intf);//us_dataUSB设备相关联
    if (result)
        goto BadDevice;

    /* Get the unusual_devs entries and the descriptors */
    result = get_device_info(us, id, unusual_dev);//获取设备信息
    if (result)
        goto BadDevice;

    /* Get standard transport and protocol settings */
    get_transport(us);//获取传输方式
    get_protocol(us);//获取传输协议

    /* Give the caller a chance to fill in specialized transport
     * or protocol settings.
     */
    return 0;

BadDevice:
    US_DEBUGP("storage_probe() failed\n");
    release_everything(us);
    return result;
}

我们再看看U盘驱动的探测的第二部分usb_stor_probe2
/* Second part of general USB mass-storage probing */
int usb_stor_probe2(struct us_data *us)
{
    struct task_struct *th;
    int result;
    struct device *dev = &us->pusb_intf->dev;

    /* Make sure the transport and protocol have both been set */
    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);

    /* fix for single-lun devices */
    if (us->fflags & US_FL_SINGLE_LUN)
        us->max_lun = 0;

    /* Find the endpoints and calculate pipe values */
    result = get_pipes(us);//获得管道
    if (result)
        goto BadDevice;

    /*
     * If the device returns invalid data for the first READ(10)
     * command, indicate the command should be retried.
     */
    if (us->fflags & US_FL_INITIAL_READ10)
        set_bit(US_FLIDX_REDO_READ10, &us->dflags);

    /* Acquire all the other resources and add the host */
    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;
    }

    /* Start up the thread for delayed SCSI-device scanning */
    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;

    /* We come here if there are any problems */
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的实现
/* Associate our private data with the USB device */
static int associate_dev(struct us_data *us, struct usb_interface *intf)
{
    US_DEBUGP("-- %s\n", __func__);

    /* Fill in the device-related fields */
    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);

    /* Store our private data in the interface */
    usb_set_intfdata(intf, us);//us设置为接口的私有数据

    /* Allocate the control/setup and DMA-mapped buffers */
    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的实现
/* Get the unusual_devs entries and the string descriptors */
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;

    /* Store the entries */
    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;
    }

    /*
     * This flag is only needed when we're in high-speed, so let's
     * disable it if we're in full-speed
     */
    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);

    /* Log a message if a non-generic unusual_dev entry contains an
     * unnecessary subclass or protocol override.  This may stimulate
     * reports from users that will help us remove unneeded entries
     * from the unusual_devs.h table.
     */

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

    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 "
                    "<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

/* Get the transport settings */
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
/* Get the protocol settings */
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函数。
/* Get the pipe settings */
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;//定义中断端点描述符

    /*
     * Find the first endpoint of each type we need.
     * We are expecting a minimum of 2 endpoints - in and out (bulk).
     * An optional interrupt-in is OK (necessary for CBI protocol).
     * We will ignore any others.
     */
    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;
    }

    /* Calculate and store the pipe values */
    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了,这个函数主要功能是初始化设备,并创建数据传输的控制线程。
/* Initialize all the dynamic resources we need */
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;
    }

    /* Just before we start our control thread, initialize
     * the device if it needs initialization */
    if (us->unusual_dev->initFunction) { //特殊设备的初始化函数
        p = us->unusual_dev->initFunction(us);
        if (p)
            return p;
    }

    /* Start up our control thread */
    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");

        /* lock the device pointers */
        mutex_lock(&(us->dev_mutex));

        /* lock access to the state */
        scsi_lock(host);

        /* When we are called with no command pending, we're done */
        if (us->srb == NULL) {//为循环中超时后的退出
            scsi_unlock(host);
            mutex_unlock(&us->dev_mutex);
            US_DEBUGP("-- exiting\n");
            break;
        }

        /* has the command timed out *already* ? */
        if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {  //直接跳到超时判断去
            us->srb->result = DID_ABORT << 16;
            goto SkipForAbort;
        }

        scsi_unlock(host);

        /* reject the command if the direction indicator
         * is UNKNOWN
         */
        if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) {//方向
            US_DEBUGP("UNKNOWN data direction\n");
            us->srb->result = DID_ERROR << 16;
        }

        /* reject if target != 0 or if LUN is higher than
         * the maximum known LUN
         */
        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;
        }

        /* Handle those devices which need us to fake
         * their inquiry data */
        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;
        }

        /* we've got a command, let's do it! */
        else {
            US_DEBUG(usb_stor_show_command(us->srb));
            us->proto_handler(us->srb, us); //数据传输
            usb_mark_last_busy(us->pusb_dev);
        }

        /* lock access to the state */
        scsi_lock(host);

        /* indicate that the command is done */
        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 an abort request was received we need to signal that
         * the abort has finished.  The proper test for this is
         * the TIMED_OUT flag, not srb->result == DID_ABORT, because
         * the timeout might have occurred after the command had
         * already completed with a different result code. */
        if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {//超时处理
            complete(&(us->notify));

            /* Allow USB transfers to resume */
            clear_bit(US_FLIDX_ABORTING, &us->dflags);
            clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);
        }

        /* finished working on this command */
        us->srb = NULL;
        scsi_unlock(host);

        /* unlock the device pointers */
        mutex_unlock(&us->dev_mutex);
    } /* for (;;) */

    /* Wait until we are told to stop */
    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线程,这是一个扫描线程。
/* Thread to carry out delayed SCSI-device scanning */
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();//设备在一定时间内没有响应,会挂起
    /* Wait for the timeout to expire or for a disconnect */
    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 the device is still connected, perform the scanning */
    if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {

        /* For bulk-only devices, determine the max LUN value */
        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");

        /* Should we unbind if no devices were detected? */
    }

    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完成的是一次控制传输。
/* Determine what the maximum LUN supported is */
int usb_stor_Bulk_max_lun(struct us_data *us)
{
    int result;

    /* issue the command */
    us->iobuf[0] = 0; //默认只有0LUN
    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 we have a successful request, return the result */
    if (result > 0)
        return us->iobuf[0];

    /*
     * Some devices don't like GetMaxLUN.  They may STALL the control
     * pipe, they may return a zero-length result, they may do nothing at
     * all and timeout, or they may fail in even more bizarrely creative
     * ways.  In these cases the best approach is to use the default
     * value: only one LUN.
     */
    return 0;
}
我们看看里面usb_stor_control_msg的实现
/*
 * Transfer one control message, with timeouts, and allowing early
 * termination.  Return codes are usual -Exxx, *not* USB_STOR_XFER_xxx.
 */
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);

    /* fill in the devrequest structure */
    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);

    /* fill and submit the URB */
    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并提交

    /* return the actual length of the data transferred if no error */
    if (status == 0)
        status = us->current_urb->actual_length;
    return status;
}
继续往下看usb_stor_msg_common的实现
/* This is the common part of the URB message submission code
 *
 * All URBs from the usb-storage driver involved in handling a queued scsi
 * command _must_ pass through this function (or something like it) for the
 * abort mechanisms to work properly.
 */
static int usb_stor_msg_common(struct us_data *us, int timeout)
{
    struct completion urb_done;
    long timeleft;
    int status;

    /* don't submit URBs during abort processing */
    if (test_bit(US_FLIDX_ABORTING, &us->dflags))//设备处于放弃状态则结束
        return -EIO;

    /* set up data structures for the wakeup system */
    init_completion(&urb_done); //初始化完成量

    /* fill the common fields in the URB */
    us->current_urb->context = &urb_done;
    us->current_urb->transfer_flags = 0;

    /* we assume that if transfer_buffer isn't us->iobuf then it
     * hasn't been mapped for DMA.  Yes, this is clunky, but it's
     * easier than always having the caller tell us whether the
     * transfer buffer has already been mapped. */
    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;

    /* submit the URB */
    status = usb_submit_urb(us->current_urb, GFP_NOIO);//提交控制urb
    if (status) {
        /* something went wrong */
        return status;
    }

    /* since the URB has been submitted successfully, it's now okay
     * to cancel it */
    set_bit(US_FLIDX_URB_ACTIVE, &us->dflags);

    /* did an abort occur during the submission? */
    if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {//当前还没取消urb时,取消urb请求

        /* cancel the URB, if it hasn't been cancelled already */
        if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {
            US_DEBUGP("-- cancelling URB\n");
            usb_unlink_urb(us->current_urb);
        }
    }
    /* wait for the completion of the 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 the URB status */
    return us->current_urb->status;
}
  通过对上面这个usb_stor_msg_common函数的分析,我们现在已经把控制urb提交给USB内核了,当处理完,就会通知USB设备驱动,调用其回调函数,该回调函数在填充控制urb时已经说明,也就是usb_stor_blocking_completion函数
/* This is the completion handler which will wake us up when an URB
 * completes.
 */
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