首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux系统中USB驱动程序的工作流程详解(2)

Linux系统中USB驱动程序的工作流程详解(2)

8.1 提交 urb
一旦 urb 被正确地创建并初始化, 它就可以提交给 USB 核心以发送出到 USB 设备. 这通过调用函数sb_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 结构的任何成员
8.2 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 已经被提交给它时设备从系统中去除.
9. 探测和断开
在 struct usb_driver 结构中, 有 2 个 USB 核心在适当的时候调用的函数:
(1)当设备插入时, 如果 USB 核心认为这个驱动可以处理(USB核心使用一个列表(是一个包含制造商ID和设备号ID的一个结构体)来判断对于一个设备该使用哪一个驱动程序),则调用探测(probe)函数,探测函数检查传递给它的设备信息, 并判断驱动是否真正合适这个设备.
(2)由于某些原因,设备被移除或驱动不再控制设备时,调用断开(disconnect)函数,做适当清理.
探测和断开回调函数都在USB集线器内核线程上下文中被调用, 因此它们休眠是合法的. 为了缩短 USB 探测时间,大部分工作尽可能在设备打开时完成.这是因为 USB 核心是在一个线程中处理 USB 设备的添加和移除, 因此任何慢设备驱动都可能使 USB 设备探测时间变长。
9.1探测函数分析
在探测回调函数中, USB设备驱动应当初始化它可能用来管理 USB 设备的所有本地结构并保存所有需要的设备信息到本地结构, 因为在此时做这些通常更容易.为了和设备通讯,USB 驱动通常要探测设备的端点地址和缓冲大小.     

PS:Linux USB驱动相关细节知识补充
1. 在usb_fill_bulk_urb,usb_fill_int_urb,usb_fill_control_urb都需要指定回调函数,当此URB请求完成时,usb core回调用此函数。
注意:urb 回调函数是在中断上下文运行, 因此它不应做任何内存分配, 持有任何信号量, 或任何可导致进程休眠的事情. 如果从回调中提交 urb 并需要分配新内存块, 需使用 GFP_ATOMIC 标志来告知 USB 核心不要休眠.
2. urb封装函数:
(1)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 参数包含被传送或从设备中读取的字节数.否则返回负的错误值.
(2)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, 函数永远等待操作完成.
返回值:成功返回被传送到或从设备读取的字节数.否则返回负的错误值.
(3)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一样使用
继承事业,薪火相传
返回列表