linux usb设备驱动二

来源:互联网 发布:php 设置session路径 编辑:程序博客网 时间:2024/04/28 01:50

urb介绍和定义

USB 设备驱动代码通过urb和所有的 USB 设备通讯。
urb用 struct urb 结构描述(include/linux/usb.h )。
urb以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个 USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。

一个 urb 的典型生命循环如下:
(1)被创建;
(2)被分配给一个特定 USB 设备的特定端点;
(3)被提交给 USB 核心;
(4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动;
(5)被 USB 主机控制器驱动处理, 并传送到设备;
(6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。

urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。
struct urb :

struct urb {    /* private: usb core and host controller only fields in the urb */    struct kref kref;        /* URB引用计数 */    void *hcpriv;            /* host控制器的私有数据 */    atomic_t use_count;        /* 当前提交计数 */    atomic_t reject;        /* 提交失败计数 */    int unlinked;            /* 连接失败代码 */    /* public: documented fields in the urb that can be used by drivers */    struct list_head urb_list;    /* list head for use by the urb's                     * current owner */    struct list_head anchor_list;    /* the URB may be anchored */    struct usb_anchor *anchor;    struct usb_device *dev;     /* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.*/    struct usb_host_endpoint *ep;    /* (internal) pointer to endpoint */    unsigned int pipe;        /* 这个 urb 所要发送到的特定struct usb_device的端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.必须由下面的函数生成*/    int status;            /*当 urb开始由 USB 核心处理或处理结束, 这个变量被设置为 urb 的当前状态. USB 驱动可安全访问这个变量的唯一时间是在 urb 结束处理例程函数中. 这个限制是为防止竞态. 对于等时 urb, 在这个变量中成功值(0)只表示这个 urb 是否已被去链. 为获得等时 urb 的详细状态, 应当检查 iso_frame_desc 变量. */    unsigned int transfer_flags;    /* 传输设置*/    void *transfer_buffer;        /* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。 对控制端点, 这个缓冲区用于数据中转*/    dma_addr_t transfer_dma;    /* 用于以 DMA 方式传送数据到 USB 设备的缓冲区*/    int transfer_buffer_length;    /* transfer_buffer 或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个 USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/    int actual_length;        /* 当这个 urb 完成后, 该变量被设置为这个 urb (对于 OUT urb)发送或(对于 IN urb)接受数据的真实长度.对于 IN urb, 必须是用此变量而非 transfer_buffer_length , 因为接收的数据可能比整个缓冲小*/    unsigned char *setup_packet;    /* 指向控制urb的设置数据包指针.它在传送缓冲中的数据之前被传送(用于控制 urb)*/    dma_addr_t setup_dma;        /* 控制 urb 用于设置数据包的 DMA 缓冲区地址,它在传送普通缓冲区中的数据之前被传送(用于控制 urb)*/    int start_frame;        /* 设置或返回初始的帧数量(用于等时urb) */    int number_of_packets;        /* 指定urb所处理的等时传输缓冲区的数量(用于等时urb,在urb被发送到USB核心前,必须设置) */    int interval;            /*urb 被轮询的时间间隔. 仅对中断或等时 urb 有效. 这个值的单位依据设备速度而不同. 对于低速和高速的设备, 单位是帧, 它等同于毫秒. 对于其他设备, 单位是微帧, 等同于 1/8 毫秒. 在 urb被发送到 USB 核心之前,此值必须设置.*/    int error_count;        /* 等时urb的错误计数,由USB核心设置 */    void *context;            /* 指向一个可以被USB驱动模块设置的数据块. 当 urb 被返回到驱动时,可在结束处理例程中使用. */    usb_complete_t complete;    /* 结束处理例程函数指针, 当 urb 被完全传送或发生错误,它将被 USB 核心调用. 此函数检查这个 urb, 并决定释放它或重新提交给另一个传输中*/    struct usb_iso_packet_descriptor iso_frame_desc[0];                    /* (仅用于等时urb)usb_iso_packet_descriptor结构体允许单个urb一次定义许多等时传输,它用于收集每个单独的传输状态*/    };    struct usb_iso_packet_descriptor {    unsigned int offset;        /* 该数据包的数据在传输缓冲区中的偏移量(第一个字节为0) */    unsigned int length;        /* 该数据包的传输缓冲区大小 */    unsigned int actual_length;    /* 等时数据包接收到传输缓冲区中的数据长度 */    int status;            /* 该数据包的单个等时传输状态。它可以把相同的返回值作为主struct urb 结构体的状态变量 */};typedef void (*usb_complete_t)(struct urb *);

上述结构体中unsigned int pipe;的生成函数定义:

    static inline unsigned int __create_pipe(struct usb_device *dev,        unsigned int endpoint){    return (dev->devnum << 8) | (endpoint << 15);}/* Create various pipes... */#define usb_sndctrlpipe(dev,endpoint)    \    ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint))#define usb_rcvctrlpipe(dev,endpoint)    \    ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)#define usb_sndisocpipe(dev,endpoint)    \    ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint))#define usb_rcvisocpipe(dev,endpoint)    \    ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)#define usb_sndbulkpipe(dev,endpoint)    \    ((PIPE_BULK << 30) | __create_pipe(dev, endpoint))#define usb_rcvbulkpipe(dev,endpoint)    \    ((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)#define usb_sndintpipe(dev,endpoint)    \    ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint))#define usb_rcvintpipe(dev,endpoint)    \    ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)//snd:OUT rcv:IN  ctrl:控制  isoc:等时  bulk:批量 int:中断

上述结构体中unsigned int transfer_flags;的值域:

/* * urb->transfer_flags: * * Note: URB_DIR_IN/OUT is automatically set in usb_submit_urb(). */#define URB_SHORT_NOT_OK    0x0001    /* 置位时,任何在 IN 端点上发生的简短读取, 被 USB 核心当作错误. 仅对从 USB 设备读取的 urb 有用 */#define URB_ISO_ASAP        0x0002    /* 若为等时 urb , 驱动想调度这个 urb 时,可置位该位, 只要带宽允许且想在此时设置 urb 中的 start_frame 变量. 若没有置位,则驱动必须指定 start_frame 值,且传输如果不能在当时启动的话,必须能够正确恢复 */#define URB_NO_TRANSFER_DMA_MAP    0x0004    /* 当 urb 包含要被发送的 DMA 缓冲时,应被置位.USB 核心使用就会使用 transfer_dma 变量指向的缓冲, 而不是被 transfer_buffer 变量指向的缓冲. */#define URB_NO_SETUP_DMA_MAP    0x0008    /* 和 URB_NO_TRANSFER_DMA_MAP 类似, 这个位用来控制 DMA 缓冲已经建立的 urb. 如果它被置位, USB 核心使用 setup_dma 变量而不是 setup_packet 变量指向的缓冲. */#define URB_NO_FSBR        0x0020    /* 仅 UHCI USB 主机控制器驱动使用, 并且告诉它不要试图使用前端总线回收( Front Side Bus Reclamation) 逻辑. 这个位通常应不设置, 因为有 UHCI 主机控制器的机器会增加 CPU 负担, 且PCI 总线会忙于等待设置了这个位的 urb */#define URB_ZERO_PACKET        0x0040    /* 如果置位, 批量 OUT urb 通过发送不包含数据的短包来结束, 这时数据对齐到一个端点数据包边界. 这被一些掉线的 USB 设备需要该位才能正确工作 */#define URB_NO_INTERRUPT    0x0080    /* 如果置位, 当 urb 结束时硬件可能不产生一个中断. 该位应当小心使用并且只在多个 urb 排队到相同端点时才使用. USB 核心函数使用该位进行 DMA 缓冲传送. */#define URB_FREE_BUFFER        0x0100    /* Free transfer buffer with the URB */#define URB_DIR_IN        0x0200    /* Transfer from device to host */#define URB_DIR_OUT        0#define URB_DIR_MASK        URB_DIR_IN

上述结构体中int status;的常用值(in include/asm-generic/errno.h and errno_base.h) :

// 0     表示 urb 传送成功*///以下各个定义在使用时为负值#define    ENOENT         2    /* urb 被 usb_kill_urb 停止 */#define    ECONNRESET    104    /* urb 被 usb_unlink_urb 去链, 且 transfer_flags 被设为 URB_ASYNC_UNLINK */#define    EINPROGRESS    115    /* urb 仍在 USB 主机控制器处理 */#define    EPROTO        71    /* urb 发生错误: 在传送中发生bitstuff 错误或硬件没有及时收到响应帧 */#define    EILSEQ        84    /* urb 传送中出现 CRC 较验错 */#define    EPIPE        32    /* 端点被停止. 若此端点不是控制端点, 则这个错误可通过函数 usb_clear_halt 清除 */#define    ECOMM        70    /* 数据传输时的接收速度快于写入系统内存的速度. 此错误仅出现在 IN urb */#define    ENOSR        63    /* 从系统内存中获取数据的速度赶不上USB 数据传送速度,此错误仅出现在 OUT urb. */#define    EOVERFLOW    75    /* urb 发生"babble"(串扰)错误:端点接受的数据大于端点的最大数据包大小 */#define    EREMOTEIO    181    /* 当 urb 的 transfer_flags 变量的 URB_SHORT_NOT_OK 标志被设置, urb 请求的数据没有完整地收到 */#define    ENODEV        19    /* USB 设备从系统中拔出 */#define    EXDEV        18    /* 仅发生在等时 urb 中, 表示传送部分完成. 为了确定所传输的内容, 驱动必须看单独的帧状态. */#define    EINVAL        22    /* 如果urb的一个参数设置错误或在提交 urb 给 USB 核心的 usb_submit_urb 调用中, 有不正确的参数,则可能发生次错误 */#define    ESHUTDOWN    108    /* USB 主机控制器驱动有严重错误,它已被禁止, 或者设备从系统中拔出。且这个urb 在设备被移除后被提交. 它也可能发生在 urb 被提交给设备时,设备的配置已被改变*/

创建和注销 urb

struct urb 结构不能静态创建,必须使用 usb_alloc_urb 函数创建. 函数原型:

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);//int iso_packets :urb 包含等时数据包的数目。如果不使用等时urb,则为0//gfp_t mem_flags :与传递给 kmalloc 函数调用来从内核分配内存的标志类型相同//返回值:如果成功分配足够内存给 urb,返回值为指向 urb 的指针. 如果返回值是 NULL, 则在 USB 核心中发生了错误, 且驱动需要进行适当清理

如果驱动已经对 urb 使用完毕, 必须调用usb_free_urb 函数,释放urb。函数原型:

void usb_free_urb(struct urb *urb);//struct urb *urb : 要释放的 struct urb 指针

根据内核源码,可以通过自己kmalloc一个空间来创建urb,然后必须使用

void usb_init_urb(struct urb *urb);

进行初始化后才可以继续使用。

其实usb_alloc_urb函数就是这样实现的,所以我当然不推荐这种自找麻烦的做法。

初始化 urb

static inline void usb_fill_int_urb(struct urb *urb,                 struct usb_device *dev,                 unsigned int pipe,                 void *transfer_buffer,                 int buffer_length,                 usb_complete_t complete_fn,                 void *context,                 int interval);static inline void usb_fill_bulk_urb(struct urb *urb,                 struct usb_device *dev,                 unsigned int pipe,                 void *transfer_buffer,                 int buffer_length,                 usb_complete_t complete_fn,                 void *context);static inline void usb_fill_control_urb(struct urb *urb,                    struct usb_device *dev,                    unsigned int pipe,                    unsigned char *setup_packet,                    void *transfer_buffer,                    int buffer_length,                    usb_complete_t complete_fn,                    void *context);//struct urb *urb :指向要被初始化的 urb 的指针//struct usb_device *dev :指向 urb 要发送到的 USB 设备.//unsigned int pipe : urb 要被发送到的 USB 设备的特定端点. 必须使用前面提过的 usb_******pipe 函数创建//void *transfer_buffer :指向外发数据或接收数据的缓冲区的指针.注意:不能是静态缓冲,必须使用 kmalloc 来创建.//int buffer_length :transfer_buffer 指针指向的缓冲区的大小//usb_complete_t complete :指向 urb 结束处理例程函数指针//void *context :指向一个小数据块的指针, 被添加到 urb 结构中,以便被结束处理例程函数获取使用.//int interval :中断 urb 被调度的间隔.//函数不设置 urb 中的 transfer_flags 变量, 因此对这个成员的修改必须由驱动手动完成/*等时 urb 没有初始化函数,必须手动初始化,以下为一个例子*/urb->dev = dev;urb->context = uvd;urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1);urb->interval = 1;urb->transfer_flags = URB_ISO_ASAP;urb->transfer_buffer = cam->sts_buf[i];urb->complete = konicawc_isoc_irq;urb->number_of_packets = FRAMES_PER_DESC;urb->transfer_buffer_length = FRAMES_PER_DESC;for (j=0; j < FRAMES_PER_DESC; j++) {        urb->iso_frame_desc[j].offset = j;        urb->iso_frame_desc[j].length = 1;}

其实那三个初始化函数只是简单的包装,是inline函数。所以其实和等时的urb手动初始化没什么大的区别。

提交 urb

一旦 urb 被正确地创建并初始化, 它就可以提交给 USB 核心以发送出到 USB 设备. 这通过调用函数 usb_submit_urb 实现:

int usb_submit_urb(struct urb *urb, gfp_t mem_flags);//struct urb *urb :指向被提交的 urb 的指针 //gfp_t mem_flags :使用传递给 kmalloc 调用同样的参数, 用来告诉 USB 核心如何及时分配内存缓冲/*因为函数 usb_submit_urb 可被在任何时候被调用(包括从一个中断上下文), mem_flags 变量必须正确设置. 根据 usb_submit_urb 被调用的时间,只有 3 个有效值可用:GFP_ATOMIC 只要满足以下条件,就应当使用此值:1.调用者处于一个 urb 结束处理例程,中断处理例程,底半部,tasklet或者一个定时器回调函数.2.调用者持有自旋锁或者读写锁. 注意如果正持有一个信号量, 这个值不必要.3.current->state 不是 TASK_RUNNING. 除非驱动已自己改变 current 状态,否则状态应该一直是 TASK_RUNNING .GFP_NOIO 驱动处于块 I/O 处理过程中. 它还应当用在所有的存储类型的错误处理过程中.GFP_KERNEL 所有不属于之前提到的其他情况*/

在 urb 被成功提交给 USB 核心之后, 直到结束处理例程函数被调用前,都不能访问 urb 结构的任何成员.

urb结束处理例程

如果usb_submit_urb被成功调用, 并把对 urb 的控制权传递给 USB 核心, 函数返回 0; 否则返回一个负的错误代码. 如果函数调用成功, 当 urb 被结束的时候结束处理例程会被调用一次.当这个函数被调用时, USB 核心就完成了这个urb, 并将它的控制权返回给设备驱动.

只有 3 种结束urb并调用结束处理例程的情况:
(1)urb 被成功发送给设备, 且设备返回正确的确认.如果这样, urb 中的status变量被设置为 0.
(2)发生错误, 错误值记录在 urb 结构中的 status 变量.
(3)urb 从 USB 核心unlink. 这发生在要么当驱动通过调用 usb_unlink_urb 或者 usb_kill_urb告知 USB 核心取消一个已提交的 urb,或者在一个 urb 已经被提交给它时设备从系统中去除.
取消 urb
使用以下函数停止一个已经提交给 USB 核心的 urb:

void usb_kill_urb(struct urb *urb)int usb_unlink_urb(struct urb *urb);

如果调用usb_kill_urb函数,则 urb 的生命周期将被终止. 这通常在设备从系统移除时,在断开回调函数(disconnect callback)中调用.
对一些驱动, 应当调用 usb_unlink_urb 函数来使 USB 核心停止 urb. 这个函数不会等待 urb 完全停止才返回. 这对于在中断处理例程中或者持有一个自旋锁时去停止 urb 是很有用的, 因为等待一个 urb 完全停止需要 USB 核心有使调用进程休眠的能力(wait_event()函数).

usb_device_id

驱动程序把驱动对象注册到 USB 子系统中,之后使用供应商(idVendor)和设备(idProduct)标识来判断对应的硬件是否已经安装.

驱动的设备支持列表

struct usb_device_id结构提供了这个驱动支持的不同类型 USB 设备的列表. USB 核心通过此列表用来决定设备对应的驱动,热插拔脚本也通过此列表来决定当特定设备被插入系统时,应该自动加载的驱动.

struct usb_device_id {    /* 确定设备信息去和结构体中哪几个字段匹配来判断驱动的适用性 */    __u16        match_flags;    /* Used for product specific matches; range is inclusive */    __u16        idVendor;    //USB设备的制造商ID,须向www.usb.org申请    __u16        idProduct;    //USB设备的产品ID,有制造商自定    __u16        bcdDevice_lo;    /* USB设备的产品版本号最低值*/    __u16        bcdDevice_hi;    /* 和最高值,以BCD码来表示。*/    /* 分别定义设备的类,子类和协议,他们由 USB 论坛分配并定义在 USB 规范中. 这些值指定这个设备的行为, 包括设备上所有的接口 */    __u8        bDeviceClass;        __u8        bDeviceSubClass;    __u8        bDeviceProtocol;    /* 分别定义单个接口的类,子类和协议,他们由 USB 论坛分配并定义在 USB 规范中 */    __u8        bInterfaceClass;    __u8        bInterfaceSubClass;    __u8        bInterfaceProtocol;    /* 这个值不用来匹配驱动的, 驱动用它来在 USB 驱动的探测回调函数中区分不同的设备 */    kernel_ulong_t    driver_info;};//以上结构体中__u16 match_flags;所使用的define://include/linux/mod_devicetable.h/* Some useful macros to use to create struct usb_device_id */#define USB_DEVICE_ID_MATCH_VENDOR        0x0001#define USB_DEVICE_ID_MATCH_PRODUCT        0x0002#define USB_DEVICE_ID_MATCH_DEV_LO        0x0004#define USB_DEVICE_ID_MATCH_DEV_HI        0x0008#define USB_DEVICE_ID_MATCH_DEV_CLASS        0x0010#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS    0x0020#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL    0x0040#define USB_DEVICE_ID_MATCH_INT_CLASS        0x0080#define USB_DEVICE_ID_MATCH_INT_SUBCLASS    0x0100#define USB_DEVICE_ID_MATCH_INT_PROTOCOL    0x0200//include/linux/usb.h#define USB_DEVICE_ID_MATCH_DEVICE \        (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)#define USB_DEVICE_ID_MATCH_DEV_RANGE \        (USB_DEVICE_ID_MATCH_DEV_LO | USB_DEVICE_ID_MATCH_DEV_HI)#define USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION \        (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_RANGE)#define USB_DEVICE_ID_MATCH_DEV_INFO \        (USB_DEVICE_ID_MATCH_DEV_CLASS | \        USB_DEVICE_ID_MATCH_DEV_SUBCLASS | \        USB_DEVICE_ID_MATCH_DEV_PROTOCOL)#define USB_DEVICE_ID_MATCH_INT_INFO \        (USB_DEVICE_ID_MATCH_INT_CLASS | \        USB_DEVICE_ID_MATCH_INT_SUBCLASS | \        USB_DEVICE_ID_MATCH_INT_PROTOCOL)//这个结构体一般不用手动赋值,以下的宏可以实现赋值:/** * USB_DEVICE - macro used to describe a specific usb device * @vend: the 16 bit USB Vendor ID * @prod: the 16 bit USB Product ID * * This macro is used to create a struct usb_device_id that matches a * specific device. *///仅和指定的制造商和产品ID匹配,用于需要特定驱动的设备#define USB_DEVICE(vend,prod) \    .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \    .idVendor = (vend), \    .idProduct = (prod)/** * USB_DEVICE_VER - describe a specific usb device with a version range * @vend: the 16 bit USB Vendor ID * @prod: the 16 bit USB Product ID * @lo: the bcdDevice_lo value * @hi: the bcdDevice_hi value * * This macro is used to create a struct usb_device_id that matches a * specific device, with a version range. *///仅和某版本范围内的指定的制造商和产品ID匹配#define USB_DEVICE_VER(vend, prod, lo, hi) \    .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, \    .idVendor = (vend), \    .idProduct = (prod), \    .bcdDevice_lo = (lo), \    .bcdDevice_hi = (hi)/** * USB_DEVICE_INTERFACE_PROTOCOL - describe a usb device with a specific interface protocol * @vend: the 16 bit USB Vendor ID * @prod: the 16 bit USB Product ID * @pr: bInterfaceProtocol value * * This macro is used to create a struct usb_device_id that matches a * specific interface protocol of devices. *///仅和指定的接口协议、制造商和产品ID匹配#define USB_DEVICE_INTERFACE_PROTOCOL(vend, prod, pr) \    .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \         USB_DEVICE_ID_MATCH_INT_PROTOCOL, \    .idVendor = (vend), \    .idProduct = (prod), \    .bInterfaceProtocol = (pr)/** * USB_DEVICE_INFO - macro used to describe a class of usb devices * @cl: bDeviceClass value * @sc: bDeviceSubClass value * @pr: bDeviceProtocol value * * This macro is used to create a struct usb_device_id that matches a * specific class of devices. *///仅和指定的设备类型相匹配#define USB_DEVICE_INFO(cl, sc, pr) \    .match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, \    .bDeviceClass = (cl), \    .bDeviceSubClass = (sc), \    .bDeviceProtocol = (pr)/** * USB_INTERFACE_INFO - macro used to describe a class of usb interfaces * @cl: bInterfaceClass value * @sc: bInterfaceSubClass value * @pr: bInterfaceProtocol value * * This macro is used to create a struct usb_device_id that matches a * specific class of interfaces. *///仅和指定的接口类型相匹配#define USB_INTERFACE_INFO(cl, sc, pr) \    .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \    .bInterfaceClass = (cl), \    .bInterfaceSubClass = (sc), \    .bInterfaceProtocol = (pr)/** * USB_DEVICE_AND_INTERFACE_INFO - describe a specific usb device with a class of usb interfaces * @vend: the 16 bit USB Vendor ID * @prod: the 16 bit USB Product ID * @cl: bInterfaceClass value * @sc: bInterfaceSubClass value * @pr: bInterfaceProtocol value * * This macro is used to create a struct usb_device_id that matches a * specific device with a specific class of interfaces. * * This is especially useful when explicitly matching devices that have * vendor specific bDeviceClass values, but standards-compliant interfaces. *///仅和指定的制造商、产品ID和接口类型相匹配#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, cl, sc, pr) \    .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \        | USB_DEVICE_ID_MATCH_DEVICE, \    .idVendor = (vend), \    .idProduct = (prod), \    .bInterfaceClass = (cl), \    .bInterfaceSubClass = (sc), \    .bInterfaceProtocol = (pr)/* -------------------------------------------------------------- */

对于一个只为一个供应商的一个 USB 设备的简单 USB 设备驱动, 其 struct usb_device_id 可定义如下:

/* 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 struct usb_device_id skel_table [] = {    { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },    { }                    /* Terminating entry */};MODULE_DEVICE_TABLE(usb, skel_table);

MODULE_DEVICE_TABLE宏是必需的,它允许用户空间工具判断该驱动可控制什么设备. 对于 USB 驱动, 这个宏中的第一个值必须是 usb .

如果你需要这个驱动被系统中每个 USB 设备调用, 创建仅需设置 driver_info成员:

static struct usb_device_id usb_ids[] = { {.driver_info = 42}, {} };

注册USB驱动程序

所有 USB 驱动都必须创建的主要结构是 struct usb_driver. 这个结构必须被 USB 驱动程序手动填充并且包含多个回调函数和变量, 并向 USB 核心描述 USB 驱动程序:

struct usb_driver {    const char *name;        /*指向驱动程序名字的指针. 它必须在内核所有的 USB 驱动中是唯一的(通常被设为和驱动模块名相同).当驱动在内核中运行时,会出现在/sys/bus/usb/drivers目录中 */    int (*probe) (struct usb_interface *intf,         const struct usb_device_id *id);    /*指向 USB 驱动中探测函数指针. 当USB 核心认为它有一个本驱动可处理的 struct usb_interface时此函数将被调用. USB 核心用来做判断的 struct usb_device_id 指针也被传递给此函数.如果这个 USB 驱动确认传递给它的 struct usb_interface, 它应当正确地初始化设备并返回 0. 如果驱动没有确认这个设备或发生错误,则返回负错误值 */    void (*disconnect) (struct usb_interface *intf);    /*指向 USB 驱动的断开函数指针.当 struct usb_interface 从系统中清除或驱动 USB 核心卸载时,函数将被 USB 核心调用*/    int (*ioctl) (struct usb_interface *intf, unsigned int code,            void *buf);    /*指向 USB 驱动的 ioctl 函数指针. 若此函数存在, 在用户空间程序对usbfs 文件系统关联的设备调用 ioctl 时,此函数将被调用. 实际上,当前只有 USB 集线器驱动使用这个 ioctl*/    int (*suspend) (struct usb_interface *intf, pm_message_t message);    /*指向 USB 驱动中挂起函数的指针*/    int (*resume) (struct usb_interface *intf);    /*指向 USB 驱动中恢复函数的指针*/    int (*reset_resume)(struct usb_interface *intf);    /*要复位一个已经被挂起的USB设备时调用此函数*/    int (*pre_reset)(struct usb_interface *intf);    /*在设备被复位之前由usb_reset_composite_device()调用*/    int (*post_reset)(struct usb_interface *intf);    /*在设备被复位之后由usb_reset_composite_device()调用*/    const struct usb_device_id *id_table;    /*指向 struct usb_device_id 表的指针*/    struct usb_dynids dynids;    struct usbdrv_wrap drvwrap;    /*是struct device_driver driver的再包装,struct device_driver 包含 struct module *owner;*/    unsigned int no_dynamic_id:1;    unsigned int supports_autosuspend:1;    unsigned int soft_unbind:1;};#define    to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)

创建一个简单的 struct usb_driver 结构, 只有 4 个成员需要初始化:

static struct usb_driver skel_driver = { .name = "skeleton", .id_table = skel_table, .probe = skel_probe, .disconnect = skel_disconnect, };//向 USB 核心注册 struct usb_driver static int __init usb_skel_init(void){        int result;        /* register this driver with the USB subsystem */        result = usb_register(&skel_driver);        if (result)                err("usb_register failed. Error number %d", result);        return result;}/*当 USB 驱动被卸载, struct usb_driver 需要从内核注销(代码如下). 当以下调用发生, 当前绑定到这个驱动的任何 USB 接口将会断开, 并调用断开函数*/static void __exit usb_skel_exit(void){        /* deregister this driver with the USB subsystem */        usb_deregister(&skel_driver);}

usb设备探测和断开的细节

struct usb_driver 结构中, 有 2 个 USB 核心在适当的时候调用的函数:
(1)当设备安装时, 如果 USB 核心认为这个驱动可以处理,则调用探测(probe)函数,探测函数检查传递给它的设备信息, 并判断驱动是否真正合适这个设备.
(2)由于某些原因,设备被移除或驱动不再控制设备时,调用断开(disconnect)函数,做适当清理.

探测和断开回调函数都在 USB 集线器内核线程上下文中被调用, 因此它们休眠是合法的. 为了缩短 USB 探测时间,大部分工作尽可能在设备打开时完成.这是因为 USB 核心是在一个线程中处理 USB 设备的添加和移除, 因此任何慢设备驱动都可能使 USB 设备探测时间变长。

探测函数分析

在探测回调函数中, USB 驱动应当初始化它可能用来管理 USB 设备的所有本地结构并保存所有需要的设备信息到本地结构, 因为在此时做这些通常更容易.为了和设备通讯,USB 驱动通常要探测设备的端点地址和缓冲大小. 以下是usb-skeleton的probe函数中的探测代码:

    /* set up the endpoint information */    /* use only the first bulk-in and bulk-out endpoints */    iface_desc = interface->cur_altsetting;        //从输入的interface中提取当前接口的端点总数    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;            }        }        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;    }//这里端点判断的函数给我们的编程带来了方便:/*-------------------------------------------------------------------------*//** * usb_endpoint_num - get the endpoint's number * @epd: endpoint to be checked * * Returns @epd's number: 0 to 15. */static inline int usb_endpoint_num(const struct usb_endpoint_descriptor *epd){    return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;}/** * usb_endpoint_type - get the endpoint's transfer type * @epd: endpoint to be checked * * Returns one of USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT} according * to @epd's transfer type. */static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd){    return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;}/** * usb_endpoint_dir_in - check if the endpoint has IN direction * @epd: endpoint to be checked * * Returns true if the endpoint is of type IN, otherwise it returns false. */static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd){    return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);}/** * usb_endpoint_dir_out - check if the endpoint has OUT direction * @epd: endpoint to be checked * * Returns true if the endpoint is of type OUT, otherwise it returns false. */static inline int usb_endpoint_dir_out(                const struct usb_endpoint_descriptor *epd){    return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);}/** * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type * @epd: endpoint to be checked * * Returns true if the endpoint is of type bulk, otherwise it returns false. */static inline int usb_endpoint_xfer_bulk(                const struct usb_endpoint_descriptor *epd){    return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==        USB_ENDPOINT_XFER_BULK);}/** * usb_endpoint_xfer_control - check if the endpoint has control transfer type * @epd: endpoint to be checked * * Returns true if the endpoint is of type control, otherwise it returns false. */static inline int usb_endpoint_xfer_control(                const struct usb_endpoint_descriptor *epd){    return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==        USB_ENDPOINT_XFER_CONTROL);}/** * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type * @epd: endpoint to be checked * * Returns true if the endpoint is of type interrupt, otherwise it returns * false. */static inline int usb_endpoint_xfer_int(                const struct usb_endpoint_descriptor *epd){    return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==        USB_ENDPOINT_XFER_INT);}/** * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type * @epd: endpoint to be checked * * Returns true if the endpoint is of type isochronous, otherwise it returns * false. */static inline int usb_endpoint_xfer_isoc(                const struct usb_endpoint_descriptor *epd){    return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==        USB_ENDPOINT_XFER_ISOC);}/** * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN * @epd: endpoint to be checked * * Returns true if the endpoint has bulk transfer type and IN direction, * otherwise it returns false. */static inline int usb_endpoint_is_bulk_in(                const struct usb_endpoint_descriptor *epd){    return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd));}/** * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT * @epd: endpoint to be checked * * Returns true if the endpoint has bulk transfer type and OUT direction, * otherwise it returns false. */static inline int usb_endpoint_is_bulk_out(                const struct usb_endpoint_descriptor *epd){    return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd));}/** * usb_endpoint_is_int_in - check if the endpoint is interrupt IN * @epd: endpoint to be checked * * Returns true if the endpoint has interrupt transfer type and IN direction, * otherwise it returns false. */static inline int usb_endpoint_is_int_in(                const struct usb_endpoint_descriptor *epd){    return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));}/** * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT * @epd: endpoint to be checked * * Returns true if the endpoint has interrupt transfer type and OUT direction, * otherwise it returns false. */static inline int usb_endpoint_is_int_out(                const struct usb_endpoint_descriptor *epd){    return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));}/** * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN * @epd: endpoint to be checked * * Returns true if the endpoint has isochronous transfer type and IN direction, * otherwise it returns false. */static inline int usb_endpoint_is_isoc_in(                const struct usb_endpoint_descriptor *epd){    return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd));}/** * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT * @epd: endpoint to be checked * * Returns true if the endpoint has isochronous transfer type and OUT direction, * otherwise it returns false. */static inline int usb_endpoint_is_isoc_out(                const struct usb_endpoint_descriptor *epd){    return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd));}/*--------------------------------------------------------------*/

在设备注册之后,USB 驱动的后续操作都是通过struct usb_interface 获得设备的端点信息,所以要使用usb_set_intfdata 将前面获得的端点信息保存到struct usb_interface下的struct device中的void *driver_data;中,以方便以后的操作。在usb-skeleton的probe函数中的代码:

/* save our data pointer in this interface device */usb_set_intfdata(interface, dev);

之后在USB的驱动程序中的打开函数和断开函数中调用usb_get_intfdata来获取端点数据。由于这 2 个函数, USB 驱动不需要为系统中所有当前的设备各保持一个静态指针数组来保存单个设备结构. 对设备信息的非直接引用使得任何 USB 驱动都支持不限数量的设备.

若这个 USB 驱动没有和另一种处理用户和设备交互的子系统(如 input, tty, video……)关联, 驱动可使用 USB 主设备号,以便在用户空间使用传统的字符驱动接口. 为此, USB 驱动必须在探测函数中调用usb_register_dev函数, 以注册一个设备到 USB 核心. 在usb-skeleton的probe函数中的代码:

    /* we can register the device now, as it is ready */    retval = usb_register_dev(interface, &skel_class);    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;    }//其中使用到的 struct usb_class_driver 结构体如下:/** * struct usb_class_driver - identifies a USB driver that wants to use the USB major number * @name: the usb class device name for this driver. Will show up in sysfs. * @fops: pointer to the struct file_operations of this driver. * @minor_base: the start of the minor range for this driver. * * This structure is used for the usb_register_dev() and * usb_unregister_dev() functions, to consolidate a number of the * parameters used for them. */struct usb_class_driver {    char *name;    //sysfs 用来描述设备的名字    const struct file_operations *fops;    // struct file_operations 结构指针, 驱动定义来注册为字符设备    int minor_base;    /*给这个驱动安排的次设备号的起始. 所有和这个驱动相关的设备被创建为从这个值开始的唯一的, 递增的次设备号. 只有 16 个设备被允许在任何时刻和这个驱动关联, 除非 CONFIG_USB_DYNAMIC_MINORS 配置选项被打开. 如果这样, 忽略这个变量, 并且这个设备的所有的次设备号会以先来先服务的方式分配. 建议打开了这个选项的系统使用类似 udev 的程序来产生系统中的设备节点, 因为一个静态的 /dev 树不会正确工作.*/};//而在usb-skeleton的设置如下: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,};/* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with the driver core */static struct usb_class_driver skel_class = {    .name =        "skel%d",    .fops =        &skel_fops,    .minor_base =    USB_SKEL_MINOR_BASE,};

当 USB 设备断开, 所有关联到这个设备的资源都应被释放,如果已在探测函数中调用usb_register_dev分配了 USB 设备的次设备号, 必须调用函数 usb_deregister_dev来将次设备号还回 USB 核心.在断开函数中, 需要从接口获取之前调用 usb_set_intfdata所设置的数据,然后设置struct usb_interface 结构指针为 NULL,以防止错误的访问.而在usb-skeleton的源码如下:

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);    /* 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);    info("USB Skeleton #%d now disconnected", minor);}

当一个 USB 设备调用 disconnect 函数时, 所有当前正被传送的 urb 可自动被 USB 核心取消, 不必显式调用usb_kill_urb. 在USB设备被断开之后,如果驱动试图调用 usb_submit_urb提交urb , 将会失败, 错误值为-EPIPE.

提交和控制 urb 的过程

以usb-skeleton源码中的写函数为例:

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 = (struct usb_skel *)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 (down_interruptible(&dev->limit_sem)) {        retval = -ERESTARTSYS;        goto exit;    }    spin_lock_irq(&dev->err_lock);    if ((retval = dev->errors) < 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 */    /*当驱动有数据发送到 USB 设备,首先分配一个 urb */    urb = usb_alloc_urb(0, GFP_KERNEL);    if (!urb) {        retval = -ENOMEM;        goto error;    }    /*以最有效的方式是创建一个 DMA 缓冲区来发送数据到设备, 并拷贝数据到缓冲区*/    buf = usb_buffer_alloc(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 */    /*在将urb提交给 USB 核心之前,正确初始化 urb */    usb_fill_bulk_urb(urb, dev->udev,             usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),             buf, writesize, skel_write_bulk_callback, dev);    urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;    usb_anchor_urb(urb, &dev->submitted);    /* send the data out the bulk port */    /*提交 urb 给 USB 核心, 由它将 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;    }    /* 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_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);        usb_free_urb(urb);    }    up(&dev->limit_sem);exit:    return retval;}/*当urb被成功传递到 USB 设备(或者在传输中发生了错误), urb 回调函数将被 USB 核心调用.也就是上面初始化 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 */    /*检查 urb 的状态,判断这个 urb 是否成功完成传输*/    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 */    /*释放分配给这个 urb 的缓冲区.*/    usb_buffer_free(urb->dev, urb->transfer_buffer_length,            urb->transfer_buffer, urb->transfer_dma);    up(&dev->limit_sem);}

urb 回调函数是在中断上下文运行, 因此它不应做任何内存分配, 持有任何信号量, 或任何可导致进程休眠的事情. 如果从回调中提交 urb 并需要分配新内存块, 需使用 GFP_ATOMIC 标志来告知 USB 核心不要休眠.

使用简单的函数接口(urb函数的包装)

有时只是要发送或接受一些简单的 USB 数据,可以使用简单的函数接口:

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,         void *data, int len, int *actual_length, int timeout)/*创建批量 urb 并发送到指定的设备, 接着在返回之前等待完成.*///struct usb_device *usb_dev :目标 USB 设备指针//unsigned int pipe :目标 USB 设备的特定端点. 必须使用特定的宏创建.//void *data :如果是 OUT 端点, 指向要发送到设备的数据的指针. 如果是 IN 端点, 这是从设备读取的数据的缓冲区指针.//int len : data 参数指向的缓冲的长度//int *actual_length :指向函数放置真实字节数的指针,根据端点方向,这些字节要么是被发送到设备的,要么是从设备中读取的.//int timeout :时钟嘀哒数, 应等待的时间. 如果为 0, 函数永远等待操作完成.//成功返回0,actual_length 参数包含被传送或从设备中读取的字节数.否则返回负的错误值.int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,         __u8 requesttype, __u16 value, __u16 index, void *data,         __u16 size, int timeout)/*创建控制 urb 并发送到指定的设备, 接着在返回之前等待完成.*///struct usb_device *usb_dev :目标 USB 设备指针//unsigned int pipe :目标 USB 设备的特定端点. 必须使用特定的宏创建.//__u8 request :控制消息的 USB 请求值.//__u8 requesttype :控制消息的 USB 请求类型.//__u16 value :控制消息的 USB 消息值.//__u16 index :控制消息的 USB 消息索引值.//void *data :如果是 OUT 端点, 指向要发送到设备的数据的指针. 如果是 IN 端点, 这是从设备读取的数据的缓冲区指针.//__u16 size : data 参数指向的缓冲的长度//int timeout :时钟嘀哒数, 应等待的时间. 如果为 0, 函数永远等待操作完成.//成功返回被传送到或从设备读取的字节数.否则返回负的错误值.int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)/*创建中断 urb 并发送到指定的设备, 接着在返回之前等待完成.其实就是usb_bulk_msg的包装,所有参数和usb_bulk_msg一样使用*/

以上的函数不能在中断上下文或持有自旋锁时调用. 这些函数不能被取消, 所以小心使用; 确保驱动的 disconnect 函数了解足够的信息, 在允许它自己从内存被卸载之前等待调用结束.

其他 USB 函数

USB 核心中的一些辅助函数用来从所有的 USB 设备中获取标准信息. 这些函数不能在中断上下文或者持有自旋锁时调用,因为他们内部都是使用上面介绍的简单的接口函数.这里就不一一介绍了,这些函数的用法在/drivers/usb/core/message.c都有。

1 0