Linux设备驱动程序学习(18)-USB 驱动程序(三) (2009-07-14 11:45) 分类:Linux设备驱动程序USB urb (USB request block)内核使用2.6.29.4USB 设备驱动代码通过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 urbstruct 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 要发送的目标 structusb_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 pipe;的生成函数(define):上述结构体中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 主机控制器驱动使用,上述结构体中int status;的常用值(in include/asm-generic/errno.h and errno_base.h) :创建和注销 urbstruct urb 结构不能静态创建,必须使用 usb_alloc_urb 函数创建. 函数原型:如果驱动已经对 urb 使用完毕, 必须调用 usb_free_urb 函数,释放urb。
函数原型:根据内核源码,可以通过自己kmalloc一个空间来创建urb,然后必须使用进行初始化后才可以继续使用。
其实usb_alloc_urb函数就是这样实现的,所以我当然不推荐这种自找麻烦的做法。
初始化 urbstatic 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);其实那三个初始化函数只是简单的包装,是inline函数。