urb数据结构

一、 transfer_flags

/*
 * urb->transfer_flags:
 *
 * Note: URB_DIR_IN/OUT is automatically set in usb_submit_urb().
 */
#define URB_SHORT_NOT_OK 0x0001   /* report short reads as errors */
#define URB_ISO_ASAP  0x0002            /* iso-only, urb->start_frame ignored */
#define URB_NO_TRANSFER_DMA_MAP 0x0004    /* urb->transfer_dma valid on submit */
#define URB_NO_SETUP_DMA_MAP 0x0008     /* urb->setup_dma valid on submit */
#define URB_NO_FSBR  0x0020      /* UHCI-specific */
#define URB_ZERO_PACKET  0x0040    /* Finish bulk OUT with short packet */
#define URB_NO_INTERRUPT 0x0080   /* HINT: no non-error interrupt  needed */
#define URB_FREE_BUFFER  0x0100   /* Free transfer buffer with the URB */

#define URB_DIR_IN  0x0200       /* Transfer from device to host */
#define URB_HCD_DRIVER_TEST 0xFFFF   /* Do NOT hand back or free this URB */
#define URB_DIR_OUT  0
#define URB_DIR_MASK  URB_DIR_IN

 

1. URB_SHORT_NOT_OK

这个标记只对用来从IN 端点读取数据的urb 有效,意思就是说如果从一个IN 端点那里读取了一个比较短的数据包,就可以认为是错误的。那么这里的


short 究竟short 到什么程度?之前说到端点的时候,就知道端点描述符里有一个叫wMaxPacketSize 这样的东东,指明了端点一次能够处理的最大字节数。每个端点描述符里的wMaxPacketSize 所表示的最大字节数都包括了哪些部分?是整个packet的长度么?我可以负责任的告诉你,它只包括了Data 包里面数据字段,俗称datapayload,其它那些七大姑八大姨什么的都是协议本身需要的信息,和TCP/IP 里的报头差不多。


wMaxPacketSize 与short 有什么关系? 关系还不小, short 不short 就是与wMaxPacketSize 相比的,如果从IN 端点那儿收到了一个比wMaxPacketSize 要短的包,同时也设置了URB_SHORT_NOT_OK 这个标志,那么就可以认为传输出错了。本来如果收到一个比较短的包是意味着这次传输到此为止就结束了,你想想data payload 的长度最
大必须为wMaxPacketSize 这个规定是不可违背的了,但是如果端点想给你的数据不止那么多,怎么办?就需要分成多个wMaxPacketSize 大小的data payload 来传输,事情有时不会那么凑巧,刚好能平分成多个整份,这时,最后一个data payload 的长度就会比wMaxPacketSize 要小,这种情况本来意味着端点已经传完了它想传的,释放完了自己的需求,这次传输就该结束了,不过如果你设置了URB_SHORT_NOT_OK 标志,HCD 这边就会认为错误发生了。

 

2. URB_ISO_ASAP
这个标志只是为了方便等时传输用的。等时传输和中断传输在spec 里都被认为是periodic transfers,也就是周期传输,咱们都知道在usb 的世界里都是主机占主导地位,设备是没多少发言权的,但是对于等时传输和中断传输,端点可以对主机表达自己一种美好的期望,希望主机能够隔多长时间访问自己一次,这个期望的时间就是这里说的周期。当然,期望与现实是有一段距离的,如果期望的都能成为现实,咱们还研究usb干吗。端点的这个期望能不能得到满足,要看主机控制器答应不答应。对于等时传输,一般来说也就一帧(微帧)一次,主机那儿也很忙,再多也抽不出空儿来。那么如果你有个用于等时传输的urb,你提交给HCD 的时候,就得告诉HCD 它应该从哪一帧开始的,就要对下面要说的那个start_frame 赋值,也就是说告诉HCD 等时传输开始的那一帧(微帧)的帧号,如果你留心,应该还会记得前面说过在每帧或微帧(Mircoframe)的开始都会有个SOF Token 包,这个包里就含有个帧号字段,记录了那一帧的编号。这样的话,一是比较烦,还要去设置这个start_frame,你说烦不烦,二是到你设置的那一帧的时候,如果主机控制器没空开始等时传输,你说怎么办,要知道usb 的世界里它可是老大。于是,就出现了URB_ISO_ASAP,它的意思就是告诉HCD 啥时候不忙就啥时候开始,就不用指定什么开始的帧号了,是不是感觉特轻松?所以说,你如果想进行等时传输,又不想标新立异的话,就还是把它给设置了吧。

 

3. URB_NO_TRANSFER_DMA_MAP & URB_NO_SETUP_DMA_MAP
这两个标志都是有关DMA 的,什么是DMA?就是外设,比如咱们的usb 摄像头,和内存之间直接进行数据交换,把CPU 给撇一边儿了。

一般来说,都是驱动里提供了kmalloc 等分配的缓冲区,HCD 做一定的DMA 映射处理,DMA 映射是干吗的?外设和内存之间进行数据交换,总要互相认识吧,外设是通过各种总线连到主机里边儿的,使用的是总线地址,而内存使用的是虚拟地址,它们之间本来就是两条互不相交的平行线,要让它们中间产生连接点,必须得将一个地址转化为另一个地址,这样才能找得到对方,才能互通有无,而DMA 映射就是干这个的。为了分担点HCD 的压力,于是就有了这里的两个标志,告诉HCD 不要再自己做DMA 映射了,驱动提供的urb 里已经提供有DMA 缓冲区地址,为领导分忧解难是咱们这些小百姓应该做的事情。具体提供了哪些DMA 缓冲区?就涉及到下面的transfer_buffer,transfer_dma,还有setup_packet,setup_dma 这两对儿了。

 

4. URB_ZERO_PACKET
这个标志表示批量的OUT 传输必须使用一个short packet 来结束。批量传输的数据大于批量端点的wMaxPacketSize 时,需要分成多个Data 包来传输,最后一个data payload 的长度可能等于wMaxPacketSize,也可能小于,当等于wMaxPacketSize 时,如果同时设置了URB_ZERO_PACKET 标志,就需要再发送一个长度为0 的数据包来结束这次传输,如果小于wMaxPacketSize 就没必要多此一举了。你要问,当批量传输的数据小于wMaxPacketSize 时那?也没必要再发送0 长的数据包,因为此时发送的这个数据包本身就是一个short packet。

 

5. URB_NO_INTERRUPT
这个标志用来告诉HCD,在URB 完成后,不要请求一个硬件中断,当然这就意味着你的结束处理函数可能不会在urb 完成后立即被调用,而是在之后的某个时间被调用,咱们的usb core 会保证为每个urb 调用一次结束处理函数。

 

二、transfer_buffer & transfer_dma & transfer_buffer_length

管道的一端是主机上的缓冲区,一端是设备上的端点,这三个家伙就是描述主机上的那个缓冲区的。transfer_buffer 是使用kmalloc 分配的缓冲区,transfer_dma 是使用usb_buffer_alloc分配的dma 缓冲区,HCD 不会同时使用它们两个,如果你的urb 自带了transfer_dma,就要同时设置URB_NO_TRANSFER_DMA_MAP 来告诉HCD 一声,不用它再费心做DMA 映射了。transfer_buffer 是必须要设置的,因为不是所有的主机控制器都能够使用DMA 的,万一遇到这样的情况,也好有个备用。transfer_buffer_length指的就是transfer_buffer 或transfer_dma 的长度。

 

三、setup_packet & setup_dma

同样是两个缓冲区,一个是kmalloc分配的,一个是用usb_buffer_alloc分配的,不过,这两个缓冲区是控制传输专用的,记得struct usb_ctrlrequest不?它们保存的就是一个struct usb_ctrlrequest结构体,如果你的urb设置了setup_dma,同样要设置URB_NO_SETUP_DMA_MAP标志来告诉HCD。如果进行的是控制传输,setup_packet是必须要设置的,也是为了防止出现主机控制器不能使用DMA的情况。

 

四、start_frame

如果你没有指定URB_ISO_ASAP 标志,就必须自己设置start_frame,指定等时传输在哪帧或微帧开始。如果指定了URB_ISO_ASAP,urb 结束时会使用这个值返回实际的开始帧号。

 

五、interval

等时和中断传输专用。interval 间隔时间的意思,什么的间隔时间?就是上面说的端点希望主机轮询自己的时间间隔。这个值和端点描述符里的bInterval 是一样的,你不能随便儿的指定一个,协议里对你能指定的值是有范围限制的,对于中断传输,全速时,这个范围为1~255ms,低速是为10~255ms,高速时为1~16,这个1~16 只是bInterval 可以取的值,实际的间隔时间需要计算一下,为2 的(bInterval-1)次方乘以125 微妙,也就是2 的(bInterval-1)次方个微帧。对于等时传输,没有低速了,等时传输根本就不是低速端点负担得起的,有多大能耐就做多大事,人有多大胆地有多大产的时代早就已经过去了,对于全速和高速,这个范围也是为1~16,间隔时间由2 的(bInterval-1)次方算出来,单位为帧或微帧。这样看来,每一帧或微帧里,你最多只能期望有一次等时和中断传输,不能再多了,你只能期望房价涨的慢点,要是希望它跌下去,那要求就太过分了,它可是经济的柱子,要是倒了,那不是陷国人的生活于困境么,所以咱们要爱国啊,要送钱给ZF 还有任小强们啊同志们。

  

六、context
驱动设置了给下面的结束处理函数用的。比如可以将自己驱动里描述自己设备的结构体放在里边儿,在结束处理函数里就可以取出来。

complete
一个指向结束处理函数的指针,传输成功完成,或者中间发生错误的时候就会调用它,驱动可以在这里边儿检查urb 的状态,并做一些处理,比如可以释放这个urb,或者重新提交给HCD。就说摄像头吧,你向HCD 提交了个等时的urb 从摄像头那里读取视频数据,传输完成的时候调用了你指定的这个结束处理函数,并在里面取出了urb 里面获得的数据进行解码等处理,然后怎么着?总不会这一个urb 读取的数据就够你向mm 表白了吧,你的爱慕之情可是犹如滔滔江水连绵不绝,所以需要获得更多的数据,那你也总不会再去创建、初始化一个等时的urb 吧,即使再穷极无聊的人也不会那么做,明显刚刚的那个可以继续用的,只要将它再次提交给HCD 就可以了。这个函数指针的定义在include/linux/usb.h
typedef void (*usb_complete_t)(struct urb *);

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值