首页 > 其他 > 详细

usb驱动开发14之设备生命线

时间:2014-04-02 12:05:23      阅读:542      评论:0      收藏:0      [点我收藏+]

直接看代码吧。

bubuko.com,布布扣

函数虽长目标却很简单,就是对urb做些前期处理后扔给HCD。现在只对这个函数分析主要部分。

这个函数的开头儿就要履行一下常规的检验,判读urb为空,都没有初始化是不可以提交给core的,core很生气,后果很严重,hcpriv本来说好了留给HCD用的,你得保证送过去的时候它还是贞洁的,自己不能偷偷先用了,HCD很生气,后果也会很严重。complete,每个urb结束了都必须的调用一次complete代表的函数,这是真理,你必须得让它存在。接下来是对urb的目的地usb设备的检验,或者设备甚至还没进入USB_STATE_DEFAULT状态,管道的另一端还都是堵着的怎么过去,早就说过要想让设备回应你,它起码得达到Default状态。再然后就是usb_dev结构中存放着IN,OUT类型的endpoint指针数组,通过urb的pipe来获取endpoint的方向和端口,来确定要接收urb的endpoint。

设备编号devnum肯定是不能为负的了,那为0为什么也不行那?到现在,地球人都知道了,Token包的地址域里有7位是表示设备地址的,也就是说总共可以有128个地址来分配给设备,但是其中0号地址是被保留作为缺省地址用的,任何一个设备处于Default状态还没有进入Address状态的时候都需要通过这个缺省地址来响应主机的请求,所以0号地址不能分配给任何一个设备,hub为设备选择一个地址的时候,只有选择到一个大于0的地址,设备的生命线才会继续,才会走到这里,因此说这里的devnum是不可能也不应该为0的,如果为0的话,那就是中间哪里谁暗地里动了手脚,就没必要往下走了。

因为咱们看到这里是因为要设置设备的地址,让设备进入Address状态,所以针对SET_ADDRESS请求再看看这个devnum。主机向设备发送SET_ADDRESS请求时,如果设备处于Default状态,就是它现在处的状态,指定一个非0值时,设备将进入Adress状态,指定0值时,设备仍然会处于Default状态,所以说即使从这个角度看,这里的devnum也是不能为0的,不然就是吃饱饭没事干故意找抽。如果设备已经处于Adress状态,指定一个非0值时,设备仍然会处于Address状态,只是将使用新分配的地址,一个设备只能占用一个地址,是包分配的,真正的居者有其屋,如果指定了一个0值,则设备将离开Address状态退回到Default状态。

power,power_state,event,还有PM_EVENT_ON都是电源管理核心里的东西,这里的目的是判断设备所在的那条总线的主机控制器有没有挂起,然后再判断设备本身是不是处于Suspended状态,如果挂起了都不欢迎你,还死皮赖脸去个什么劲儿,回去得了。

常规检查都做完了,core和HCD已经认同了这个urb,就将它的状态设置为-EINPROGRESS,表示从现在开始urb的控制权就在core和HCD手里边儿了,驱动那里是看不到这个状态的。

这时还没开始传输,实际传输的数据长度当然为0了,这里初始化这么一下,也是为了防止以后哪里出错返回了,驱动里好检查。

接下来的代码主要是 获得管道的类型还有方向。

在设备进入Configured状态之前,主机只能使用控制传输,通过缺省管道,也就是管道0来和设备进行交流。

usb_maxpacket行获得端点的wMaxPacketSize,看看include/linux/usb.h里定义的这个函数

bubuko.com,布布扣

这个函数不管从理论上还是实际上都是很简单的。咱们可以先问下自己,根据现有的信息如何获得一个端点的wMaxPacketSize?当然是必须得获得该端点的描述符,我们知道每个struct usb_device结构体里都有两个数组ep_out和ep_in保存了各个端点对应的struct usb_host_endpoint结构体,只要知道该端点对应了这两个数组里的哪个元素就可以获得它的描述符了,这就需要知道该端点的端点号和方向,而端点的方向就管道的方向,端点号也在pipe里保存有。你是不是会担心ep_out或ep_in数组都还空着,或者说没有保存对应的端点信息?倒不用担心它还是空的,即使是现在设备还刚从Powered走到Default,即使连自己的Address都没有,但是在使用usb_alloc_dev构造这个设备的struct usb_device的时候就把它里面端点0的struct usb_host_endpoint结构体ep0指定给ep_out[0]和ep_in[0]了,而且后来还对ep0的wMaxPacketSize指定了值。不过如果真的没有从它们里面找到想要的那个端点的信息,那肯定就是哪里出错了,指定了错误的端点号,或其它什么原因。

如果是等时传输要做一些特别的处理。接下来涉及到高速、高带宽端点(high speed,high bandwidth endpoint)。前面提到interval的时候,说过每一帧或微帧最多只能有一次等时传输,完成一次等时transaction,那时这么说主要是因为还没遇到高速高带宽的等时端点。高速高带宽等时端点每个微帧可以进行2到3次等时transaction,它和一般等时端点的主要区别也就在这儿,没必要专门为它搞个描述符类型,端点描述符wMaxPacketSize字段的bit 11~12就是用来指定可以额外有几次等时transaction的,00表示没有额外的transaction,01表示额外有1次,10表示额外有两次,11被保留。wMaxPacketSize字段的前10位就是实际的端点每次能够处理的最大字节数。所以这几行意思就是如果是高速等时端点,获得它允许的额外的等时transaction次数,和每次能够处理的最大字节数,再将它们相乘就得出了该等时端点每个微帧的所能传输的最大字节数。

number_of_packets不大于0就表示这个等时urb没有指定任何一次等时传输,可以直接返回了。

对等时urb里指定的各次等时传输也要分别做处理。如果它们预期传输的数据长度比上面算出来的max还要大,要求太过分了,返回吧。然后将它们实际传输的数据长度先置为0,状态都先初始化为-EXDEV,表示这次等时传输仅仅部分完成了,因为走到这里时传输都还没开始那。

transfer_buffer_length长度不能小于0,等于0倒是可以的,毕竟不是什么时候都是有数据要传的。

见到#ifdef DEBUG这意味着直到下面对应的#endif,之间的代码给人调试时用的,说明对整体无关痛痒。

temp是上面计算出来的管道类型,那下面的各个case肯定是针对四种传输类型的了。不过经过实地考察,我们可以发现,这里只case了等时传输和中断传输两种周期性的传输类型,因为是关于interval的处理,所以就没控制传输和批量传输什么事儿了。

这里保证等时和中断urb的interval必须是大于0的,不然主机那边儿看不懂你这是表示什么意思,究竟要不要去访问你,搞个负数和0含含糊糊的,日里万鸡的主机没功夫去猜你心思。这里的switch根据目的设备的速度去case,速度有三种,case也有三个。我们前面已经说过,不同的速度,urb->interval可以取不同的范围,不过你可能会发现那时说的最大值要比这里的限制要大一些,这是因为协议归协议,实现归实现,比如,对于UHCI来说,中断传输的interval不能比128更大,而协议规定的最大值为255。那么现在的问题是,temp又是做什么用的?要注意urb->interval的单位是帧或者微帧,temp只是为了调整它的值为2的次幂。

最后将urb扔给HCD,然后就进入HCD的片儿区了。

本来usb_submit_urb函数到此应该结束了,但是它对于写驱动的来说太重要了,驱动里做的所有铺垫就是为了使用usb_submit_urb提交一个合适的urb给设备,然后满怀期待的等待着设备回馈你需要的信息,再然后才有你接下来的处理,不然你的usb驱动只是一纸空谈毫无用处。所以有必要多说一些。

首先还是要再次强调一下,在调用usb_submit_urb提交你的urb之前,一定必须不得不要对它正确的初始化,对于控制/中断/批量传输,core都提供了usb_fill_control_urb的几个孪生兄弟供你初始化使用,对于等时传输要自己手工一步一步小心翼翼的对urb的相关元素逐个赋值。下层基础决定上层建筑,你的urb决定了你的整个usb驱动能否顺利运转。

第二,对于驱动来说,usb_submit_urb是异步的,也就是说不用等传输完全完成就返回了,只要usb_submit_urb的返回值表示为0,就表示已经提交成功了,你的urb已经被core和HCD认可了,接下来core和HCD怎么处理就是它们的事了,驱动该干吗干吗去。只要你提交成功了,不管是中间出了差错还是顺利完成,你指定的结束处理函数总是会调用,只有到这个时候,你才能够重新拿到urb的控制权,检查它是不是出错了,需要不需要释放或者是重新提交。

第三,什么时候需要在结束处理函数里重新提交这个urb?其实,我更想问的是对于中断/等时传输,是怎么实现让主机按一定周期去访问端点的?端点的描述符里已经指定了这个间隔时间是没错儿,urb里也有interval描述了这个间隔周期,更没错儿,可是咱们的urb一次只完成一次传输,即使等时传输也只完成有限次的传输,然后就在结束处理函数里返回了,urb的控制权就完全属于驱动了,接下来的周期访问是怎么做到的?难道脱离urb主机自己就去智能化的自动的与端点通信了?OK,即使是这样了,那通信的数据又在哪里,你又怎么去得到这些数据?事实上,你第一次提交一个中断或等时的urb的时候,HCD会根据interval判断一下自己是否能够满足你的需要,如果不能够安排足够的带宽来完成这种周期性的传输,它是不可能会批准你的请求的,如果它估量一下觉得自己可以满足,就会为你保留足够的带宽。但是这并不是就表明万事大吉了,HCD是为你保留带宽了,可是驱动得保证在对应端点要处理的那个urb队列里总是有urb,不能是空的,否则这个保留的带宽就会被cancel掉。那么问题就变成,对于中断/等时传输,如何保证对应端点的urb队列里总是会有urb?这就回到最开始的问题了,驱动需要在结束处理函数里重新初始化和提交刚刚完成的urb,友情提醒一下,这个时候你是不能够修改interval的值的,否则等待你的只能是错误。中断传输的例子可以去看看触摸屏驱动,等时传输的可以去看看摄像头驱动,看看它们在结束处理函数里都做了些什么,你就悟道了。

第四个要说的是,对于控制/批量/中断传输,实际上很多时候你可以不用创建urb,不用对它初始化,不用调用usb_submit_urb来提交,core将这个过程分别封装在了usb_control_msg、usb_bulk_msg和usb_interrupt_msg这三个函数里,不同的是它们的实现是同步的,会去等待传输的完全结束。咱们就是从usb_control_msg走过来的,另外两个暂时不分析了。以后用到再说。

usb驱动开发14之设备生命线,布布扣,bubuko.com

usb驱动开发14之设备生命线

原文:http://www.cnblogs.com/myblesh/p/3636542.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!