urb的使用

来源:互联网 发布:淘宝类目在哪里 编辑:程序博客网 时间:2024/05/29 13:10

Linux系统中,所有的 USB 设备通讯使用称为 urb 的东西( USB request block). 这个请求块用 struct urb 结构描述并且可在 include/linux/usb.h 中找到

下面就来看看urb怎么去使用

  • 结构体的定义
struct urb{/* 私有的:只能由USB核心和主机控制器访问的字段 */struct kref kref; /*urb引用计数 */spinlock_t lock; /* urb锁 */void *hcpriv; /* 主机控制器私有数据 */int bandwidth; /* INT/ISO请求的带宽 */atomic_t use_count; /* 并发传输计数 */u8 reject; /* 传输将失败*//* 公共的: 可以被驱动使用的字段 */struct list_head urb_list; /* 链表头*/struct usb_device *dev; /* 关联的USB设备 */unsigned int pipe; /* 管道信息 */int status; /* URB的当前状态 */unsigned int transfer_flags; /* URB_SHORT_NOT_OK | ...*/void *transfer_buffer; /* 发送数据到设备或从设备接收数据的缓冲区 */dma_addr_t transfer_dma; /*用来以DMA方式向设备传输数据的缓冲区 */int transfer_buffer_length;/*transfer_buffer或transfer_dma 指向缓冲区的大小 */int actual_length; /* URB结束后,发送或接收数据的实际长度 */unsigned char *setup_packet; /* 指向控制URB的设置数据包的指针*/dma_addr_t setup_dma; /*控制URB的设置数据包的DMA缓冲区*/int start_frame; /*等时传输中用于设置或返回初始帧*/int number_of_packets; /*等时传输中等时缓冲区数据 */int interval; /* URB被轮询到的时间间隔(对中断和等时urb有效) */int error_count;  /* 等时传输错误数量 */void *context; /* completion函数上下文 */usb_complete_t complete; /* 当URB被完全传输或发生错误时,被调用 */struct usb_iso_packet_descriptor iso_frame_desc[0];/*单个URB一次可定义多个等时传输时,描述各个等时传输 */};
struct usb_device *dev
指向这个 urb 要发送到的 struct usb_device 的指针. 这个变量必须被 USB 驱动初始化, 在这个 urb 被发送到 USB 核心之前.
unsigned int pipe
端点消息,将这个 urb 发送到的特定 struct usb_device。这个变量必须被 USB 驱动初始化, 在这个 urb 被发送到 USB 核心之前。
为设置这个结构的成员,驱动必须依据端口的类型和流动的方向,使用恰当的函数,注意每个端点只可是一个类型
unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint)//指定一个控制 OUT 端点给特定的带有特定端点号的 USB 设备.unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)//指定一个控制 IN 端点给带有特定端点号的特定 USB 设备.unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)//指定一个块 OUT 端点给带有特定端点号的特定 USB 设备unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)//指定一个块 IN 端点给带有特定端点号的特定 USB 设备unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)//指定一个中断 OUT 端点给带有特定端点号的特定 USB 设备unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)//指定一个中断 IN 端点给带有特定端点号的特定 USB 设备unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)//指定一个同步 OUT 端点给带有特定端点号的特定 USB 设备unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)//指定一个同步 IN 端点给带有特定端点号的特定 USB 设备
unsigned int transfer_flags
这个变量可被设置为不同位值, 根据这个 USB 驱动想这个 urb 发生什么. 可用的值是:
URB_SHORT_NOT_OK
当置位, 它表示在某个 IN 端点上可能发生的短读, 应当被 USB 当作一个错误. 这个值只对从 USB 设备读 urb 有用, 不包括写 urbs.
URB_ISO_ASAP
如果这个 urb 是同步的, 这个位可被置位. 如果驱动想这个 urb 被调度, 只要带宽允许它这样, 并且在此点设置这个 urb 中的 start_frame 变量. 如果对于同步 urb 这个位没有被置位, 驱动必须指定 start_frame 值并且必须能够正确恢复, 如果没有在那个时刻启动. 见下面的章节关于同步 urb 更多的消息.
URB_NO_TRANSFER_DMA_MAP
应当被置位, 当 urb 包含一个要被发送的 DMA 缓冲. USB 核心使用这个被 transfer_dma 变量指向的缓冲, 不是被 transfer_buffer 变量指向的缓冲.
URB_NO_SETUP_DMA_MAP
同 URB_NO_TRANSFER_DMA_MAP 位, 这个位用来控制有一个已经建立的 urb DMA 缓冲. 如果它被置位, USB 核心使用这个被 setup_dma 变量而不是setup_packet 变量指向的缓冲.
URB_ASYNC_UNLINK
如果置位, 给这个 urb 的对 usb_unlink_urb 的调用几乎立刻返回, 并且这个 urb 在后面被解除连接. 否则, 这个函数等待直到 urb 完全被去链并且在返回前结束. 小心使用这个位, 因为它可有非常难于调试的同步问题.
URB_NO_FSBR
只有 UHCI USB 主机控制器驱动使用, 并且告诉它不要试图做 Front Side Bus Reclamation 逻辑. 这个位通常应当不设置, 因为有 UHCI 主机控制器的机器创建了许多 CPU 负担, 并且 PCI 总线被等待设置了这个位的 urb 所饱和.
URB_ZERO_PACKET
如果置位, 一个块 OUT urb 通过发送不包含数据的短报文而结束, 当数据对齐到一个端点报文边界. 这被一些坏掉的 USB 设备所需要(例如一些 USB 到 IR 的设备) 为了正确的工作..
URB_NO_INTERRUPT
如果置位, 硬件当 urb 结束时可能不产生一个中断. 这个位应当小心使用并且只在排队多个到相同端点的 urb 时使用. USB 核心函数使用这个为了做 DMA 缓冲传送.
void *transfer_buffer
指向用在发送数据到设备(对一个 OUT urb)或者从设备中获取数据(对于一个 IN urb)的缓冲的指针. 对主机控制器为了正确存取这个缓冲, 它必须被使用一个对 kmalloc 调用来创建, 不是在堆栈或者静态地. 对控制端点, 这个缓冲是给发送的数据阶段.
dma_addr_t transfer_dma
用来使用 DMA 传送数据到 USB 设备的缓冲.
int transfer_buffer_length
缓冲的长度, 被 transfer_buffer 或者 transfer_dma 变量指向(由于只有一个可被一个 urb 使用). 如果这是 0, 没有传送缓冲被 USB 核心所使用.
对于一个 OUT 端点, 如果这个端点最大的大小比这个变量指定的值小, 对这个 USB 设备的传送被分成更小的块为了正确的传送数据. 这种大的传送发生在连续的 USB 帧. 提交一个大块数据在一个 urb 中是非常快, 并且使 USB 主机控制器去划分为更小的快, 比以连续的顺序发送小缓冲.
unsigned char *setup_packet
指向给一个控制 urb 的 setup 报文的指针. 它在位于传送缓冲中的数据之前被传送. 这个变量只对控制 urb 有效.
dma_addr_t setup_dma
给控制 urb 的 setupt 报文的 DMA 缓冲. 在位于正常传送缓冲的数据之前被传送. 这个变量只对控制 urb 有效.
usb_complete_t complete
指向完成处理者函数的指针, 它被 USB 核心调用当这个 urb 被完全传送或者当 urb 发生一个错误. 在这个函数中, USB 驱动可检查这个 urb, 释放它, 或者重新提交它给另一次传送.(见"completingUrbs: 完成回调处理者", 关于完成处理者的更多细节).
usb_complete_t 类型定义如此:
typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
void *context
指向数据点的指针, 它可被 USB 驱动设置. 它可在完成处理者中使用当 urb 被返回到驱动. 关于这个变量的细节见后续章节.
int actual_length
当这个 urb 被完成, 这个变量被设置为数据的真实长度, 或者由这个 urb (对于 OUT urb)发送或者由这个 urb(对于 IN urb)接受. 对于 IN urb, 这个必须被用来替代 transfer_buffer_length 变量, 因为接收的数据可能比整个缓冲大小小.
int status
当这个 urb 被结束, 或者开始由 USB 核心处理, 这个变量被设置为 urb 的当前状态. 一个 USB 驱动可安全存取这个变量的唯一时间是在 urb 完成处理者函数中(在"CompletingUrbs: 完成回调处理者"一节中描述). 这个限制是阻止竞争情况, 发生在这个 urb 被 USB 核心处理当中. 对于同步 urb, 在这个变量中的一个成功的值(0)只指示是否这个 urb 已被去链. 为获得在同步 urb 上的详细状态, 应当检查 iso_frame_desc 变量.
这个变量的有效值包括:
0
这个 urb 传送是成功的.
-ENOENT
这个 urb 被对 usb_kill_urb 的调用停止.
-ECONNRESET
urb 被对 usb_unlink_urb 的调用去链, 并且 transfer_flags 变量被设置为 URB_ASYNC_UNLINK.
-EINPROGRESS
这个 urb 仍然在被 USB 主机控制器处理中. 如果你的驱动曾见到这个值, 它是一个你的驱动中的 bug.
-EPROTO
这个 urb 发生下面一个错误:
  • 一个 bitstuff 错误在传送中发生.
  • 硬件没有及时收到响应帧.
-EILSEQ
在这个 urb 传送中有一个 CRC 不匹配.
-EPIPE
这个端点现在被停止. 如果这个包含的端点不是一个控制端点, 这个错误可被清除通过一个对函数 usb_clear_halt 的调用.
-ECOMM
在传送中数据接收快于能被写入系统内存. 这个错误值只对 IN urb.
-ENOSR
在传送中数据不能从系统内存中获取得足够快, 以便可跟上请求的 USB 数据速率. 这个错误只对 OUT urb.
-EOVERFLOW
这个 urb 发生一个"babble"错误. 一个"babble"错误发生当端点接受数据多于端点的特定最大报文大小.
-EREMOTEIO
只发生在当 URB_SHORT_NOT_OK 标志被设置在 urb 的 transfer_flags 变量, 并且意味着 urb 请求的完整数量的数据没有收到.
-ENODEV
这个 USB 设备现在从系统中消失.
-EXDEV
只对同步 urb 发生, 并且意味着传送只部分完成. 为了决定传送什么, 驱动必须看单独的帧状态.
-EINVAL
这个 urb 发生了非常坏的事情. USB 内核文档描述了这个值意味着什么:
  • ISO 疯了, 如果发生这个: 退出并回家.
  • 它也可发生, 如果一个参数在 urb 结构中被不正确地设置了, 或者如果在提交这个 urb 给 USB 核心的 usb_submit_urb 调用中, 有一个不正确的函数参数.
-ESHUTDOWN
这个 USB 主机控制器驱动有严重的错误; 它现在已被禁止, 或者设备和系统去掉连接, 并且这个urb 在设备被去除后被提交. 它也可发生当这个设备的配置改变, 而这个 urb 被提交给设备.
通常, 错误值 -EPROTO, -EILSEQ, 和 -EOVERFLOW 指示设备的硬件问题, 设备固件, 或者连接设备到计算机的线缆.
int start_frame
设置或返回同步传送要使用的初始帧号.
int interval
urb 被轮询的间隔. 这只对中断或者同步 urb 有效. 这个值的单位依据设备速度而不同. 对于低速和高速的设备, 单位是帧, 它等同于毫秒. 对于设备, 单位是宏帧的设备, 它等同于 1/8 微秒单位. 这个值必须被 USB 驱动设置给同步或者中断 urb, 在这个 urb被发送到 USB 核心之前.
int number_of_packets
只对同步 urb 有效, 并且指定这个 urb 要处理的同步传送缓冲的编号. 这个值必须被 USB 驱动设置给同步 urb, 在这个 urb 发送给 USB 核心之前.
int error_count
被 USB 核心设置, 只给同步 urb 在它们完成之后. 它指定报告任何类型错误的同步传送的号码.
struct usb_iso_packet_descriptor iso_frame_desc[0]
只对同步 urb 有效. 这个变量是组成这个 urb 的一个 struct usb_iso_packet_descriptor 结构数组. 这个结构允许单个 urb 来一次定义多个同步传送. 它也用来收集每个单独传送的传送状态.
结构 usb_iso_packet_descriptor 由下列成员组成:
unsigned int offset
报文数据所在的传送缓冲中的偏移(第一个字节从 0 开始).
unsigned int length
这个报文的传送缓冲的长度.
unsigned int actual_length
接收到给这个同步报文的传送缓冲的数据长度.
unsigned int status
这个报文的单独同步传送的状态. 它可采用同样的返回值如同主 struct urb 结构的状态变量.
  • urb处理流程

1.创建urb结构体

struct urb 结构在驱动中必须不被静态创建, 或者在另一个结构中, 因为这可能破坏 USB 核心给 urb 使用的引用计数方法
<span style="font-size:14px;">struct urb *usb_alloc_urb(int iso_packets, int mem_flags);</span>
intiso_packets
这个 urb 应当包含的同步报文的数目. 如果你不想创建一个同步 urb, 这个变量应当被设置为 0
intmem_flags:
和传递给 kmalloc 函数调用来从内核分配内存的相同的标志类型

2.初始化urb

    • 中断urb
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,void *context, int interval);
struct urb *urb

指向要被初始化的 urb 的指针.

struct usb_device *dev

这个 urb 要发送到的 USB 设备.

unsigned int pipe

这个 urb 要被发送到的 USB 设备的特定端点. 这个值被创建, 使用 usb_sndintpipe 或者 usb_rcvintpipe 函数.

void *transfer_buffer

指向缓冲的指针, 从那里外出的数据被获取或者进入数据被接受. 注意这不能是一个静态的缓冲并且必须使用 kmalloc 调用来创建.

int buffer_length

缓冲的长度, 被 transfer_buffer 指针指向.

usb_complete_t complete

指针, 指向当这个 urb 完成时被调用的完成处理者.

void *context

指向数据块的指针, 它被添加到这个 urb 结构为以后被完成处理者函数获取.

int interval

这个 urb 应当被调度的间隔. 见之前的 struct urb 结构的描述, 来找到这个值的正确单位.


usb_fill_int_urb 函数不设置 urb 中的 transfer_flags 变量,因此任何对这个成员的修改不得不由这个驱动自己完成。
    • 块urb
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,void *context);

函数参数和 usb_fill_int_urb 函数的都相同。

没有 interval 参数是因为 bulk urb 没有间隔值。

unsiged int pipe 变量必须使用 usb_sndbulkpipe 或者 usb_rcvbulkpipe 函数进行初始化。

    • 控制urb
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, void *context);

unsigned char *setup_packet

它必须指向要发送给端点的 setup 报文数据。


函数参数和 usb_fill_bulk_urb 函数都相同。

unsiged int pipe 变量必须使用 usb_sndctrlpipe 或者usb_rcvictrlpipe 函数进行初始化。

usb_fill_control_urb 函数不设置 transfer_flags 变量在 urb 中, 因此任何对这个成员的修改必须游驱动自己完成。

大部分驱动不使用这个函数,因为使用在"USB 传送不用 urb"一节中介绍的同步 API 调用更简单.

    • 同步urb

不幸的是,同步 urb 没有一个象中断,控制,和块 urb 的初始化函数。因此它们必须在驱动中"手动"初始化,在它们可被提交给 USB 核心之前。下面是一个如何正确初始化这类 urb 的例子。它是从 konicawc.c 内核驱动中取得的,它位于主内核源码树的 drivers/usb/media 目录。

<pre name="code" class="cpp">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;}
unsiged int pipe 变量必须使用 usb_sndisocpipe或者 usb_rcvisocpipe 函数进行初始化。

3.提交urb

一旦 urb 被正确地创建,并且被 USB 驱动初始化,它就可以被提交给 USB 核心来发送出到 USB 设备
int usb_submit_urb(struct urb *urb, int mem_flags);
struct urb *urb 
参数是一个指向 urb 的指针,它指向要被发送到的设备。
mem_flags 
参数等同于传递给 kmalloc 调用的参数,用来告诉 USB 核心这个时间如何及时分配任何内存缓冲。
GFP_ATOMIC
在中断处理函数、底半部、tasklet、定时器处理函数以及urb完成函数中,和在调用者持有自旋锁或者读写锁时以及当驱动将current->state修改为非 TASK_ RUNNING时,应使用此标志。
GFP_NOIO
在存储设备的块I/O和错误处理路径中,应使用此标志。
GFP_KERNEL
如果没有任何理由使用GFP_ATOMIC和GFP_NOIO,就使用GFP_ KERNEL。
在提交urb到USB核心后,直到完成函数被调用之前,不要访问urb中的任何成员。

4.完成urb

如果对 usb_submit_urb 的调用成功,这个函数返回 0;否则,一个负错误值被返回。如果函数成功,urb 的完成处理者(如同被完成函数指针指定的)被确切地调用一次,当 urb 被完成。当这个函数被调用, USB 核心完成这个 urb, 并且对它的控制现在返回给设备驱动.

只有 3 个方法, 一个urb 可被结束并且使完成函数被调用:

  • urb 被成功发送给设备, 并且设备返回正确的确认. 对于一个 OUT urb, 数据被成功发送, 对于一个 IN urb, 请求的数据被成功收到. 如果发生这个, urb 中的状态变量被设置为 0.

  • 一些错误连续发生, 当发送或者接受数据从设备中. 被 urb 结构中的 status 变量中的错误值所记录.

  • 这个 urb 被从 USB 核心去链. 这发生在要么当驱动告知 USB 核心取消一个已提交的 urb 通过调用 usb_unlink_urb 或者 usb_kill_urb, 要么当设备从系统中去除, 以及一个 urb 已经被提交给它.

5.取消urb

为停止一个已经提交给 USB 核心的 urb, 函数 usb_kill_urb 或者 usb_unlink_urb 应当被调用:

<pre name="code" class="cpp">int usb_kill_urb(struct urb *urb); int usb_unlink_urb(struct urb *urb);

当函数是 usb_kill_urb,这个 urb 的生命循环就停止了。 这个函数常常在设备从系统去除时被使用,在去连接回调中。在usb_submit_urb 函数之后调用

对一些驱动,应当用 usb_unlink_urb 函数来告知 USB 去停止 urb。这个函数在返回到调用者之前不等待这个 urb 完全停止。这对于在中断处理或者持有一个自旋锁时停止 urb 时是有用的,因为等待一个 urb 完全停止需要 USB 有能力使调用进程睡眠。为了正确工作这个函数要求 URB_ASYNC_UNLINK 标志值被设置在正被要求停止的 urb 中。

6.简单的批量与控制urb

有时USB驱动程序只是从USB设备上接收或向USB设备发送一些简单的数据,这时候,没有必要将urb创建、初始化、提交、完成处理的整个流程走一遍,而可以使用两个更简单的函数,如下所示:
  • 简单的的批量urb
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout);
struct usb_device *usb_dev
参数为批量消息要发送的USB 设备的指针。
unsigned int pipe
为批量消息要发送到的USB设备的端点。
void *data
参数为指向要发送或接收的数据缓冲区的指针。
int len
参数为data参数所指向的缓冲区的长度。
int *actual_length
用于返回实际发送或接收的字节数。
int timeout
是发送超时,以jiffies为单位,0意味着永远等待。

  • 简单的控制urb

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

struct usb_device *dev

指向控制消息发往的USB设备。

unsigned int pipe

控制消息要发往的USB设备的端点。

__u8 request

这个控制消息的USB请求值。

__u8 requesttype

这个控制消息的USB请求类型。

__u16 value

这个控制消息的USB消息值。

__u16 index

这个控制消息的USB消息索引值。

void *data

指向要发送或接收的数据缓冲区。

__u16 size

data参数所指向的缓冲区的大小。

int timeout

发送超时,以jiffies为单位,0意味着永远等待。

它们是同步的,因此不能在中断上下文和持有自旋锁的情况下使用。而且,该函数也不能被任何其他函数取消,因此,务必要使得驱动程序的 disconnect 函数掌握足够的信息,以判断和等待该调用的结束。

7.销毁urb

为了告诉 USB 核心驱动用完这个 urb,驱动必须调用 usb_free_urb 函数,在这个函数被调用之后,urb 结构消失,驱动不能再存取它。
void usb_free_urb(struct urb *urb);







参考:http://blog.csdn.net/kaizi318/article/details/7996073

0 0
原创粉丝点击