Linux那些事儿之我是U盘(29)将控制传输进行到底

其实usb_stor_clear_halt这个函数的作用很简单,就是spec里边规定了,usb设备中,有两类端点,必须具有一个叫做Halt的特征,啥是Halt?查金山词霸去,中断,停止,暂停,怎么解释呢,你把手机关了,就不能给超级女生发短信投票了吧,你把电脑关了,就不能上黄色网站了吧,你把电视机关了,就不能看中国之队在亚洲杯上的精彩表演了吧.对于usb设备来说,其中端端点和Bulk端点就有这么一个特征,叫做Halt,其实就是寄存器里的某一位,设为1,就表示设置了Halt的特征,那就是表示这个端点不工作了.要想让端点重新工作,很简单,把这一位设置为0就可以了.

关于Halt,用我们行话说,这叫做一个feature,其实就是一个特征,坊间更喜欢说feature.usb设备实际上有很多feature.确切的说,有的feature是算devicefeature,有些是interfacefeature,有些是endpointfeature.Haltendpointfeature. usb spec规定了一些请求,比如SET_FEATURE,以及CLEAR_FEATURE,顾名思义,就是设置一个feature或者清除一个feature.那么我们这里发生的是clear halt,实际上就是执行CLEAR_FEATURE,清除halt这个feature.刚才通过usb_stor_Bulk_max_lun()函数我们已经看到了对于一次控制传输,我们作为设备驱动需要做哪些工作,这里和刚才的区别仅仅在于,刚才发送的请求是GET MAX LUN,而现在要发送的请求是CLEAR FEATURE.另一方面呢,GET MAX LUNusb mass storage spec专门给它们这一小类设备定义的,CLEAR FEATURE那是所有的usb设备都通用的,因为它是usb spec所规定的.

那么什么时候我们需要调用这个函数呢?先不说我们这里的上下文,实际上usb spec规定了,对于设备的bulk端点,每当设备在reset之后,需要清除halt这个feature然后端点才能正常工作.所以之后我们会看到,reset相关的函数里我们会调用这个函数,那么我们此刻所遇到的这个函数是处于什么情景呢?如果不看注释你就能看懂,那么我只能说,你他妈的太有才了!开源社区需要你这样伟大的自由主义战士!注释里说得很清楚,有些变态的设备,它就是不跟你按常理出牌,人家能正常响应GetMaxLUN这个request,它偏要耍个性,就是不认spec,你发送GetMaxLUN请求过来,它不予回复,它出现STALL的特点,什么是STALL?其实就是Halt,端点挂起,或者通俗一点理解,就是死机了.所以,毫无疑问,我们要把这个halt给清掉,否则设别没有办法工作了.

Ok,是时候该看看函数内部了,这是一个定义于drivers/usb/storage/transport.c中的函数.usb_stor_clear_halt():

243 /* This is a version of usb_clear_halt() that allows early termination and
    244  * doesn't read the status from the device -- this is because some devices
    245  * crash their internal firmware when the status is requested after a halt.
    246  *
    247  * A definitive list of these 'bad' devices is too difficult to maintain or
    248  * make complete enough to be useful.  This problem was first observed on the
    249  * Hagiwara FlashGate DUAL unit.  However, bus traces reveal that neither
    250  * MacOS nor Windows checks the status after clearing a halt.
    251  *
    252  * Since many vendors in this space limit their testing to interoperability
    253  * with these two OSes, specification violations like this one are common.
    254  */
    255 int usb_stor_clear_halt(struct us_data *us, unsigned int pipe)
    256 {
    257         int result;
    258         int endp = usb_pipeendpoint(pipe);
    259
    260         if (usb_pipein (pipe))
    261                 endp |= USB_DIR_IN;
    262
    263         result = usb_stor_control_msg(us, us->send_ctrl_pipe,
    264                 USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
    265                 USB_ENDPOINT_HALT, endp,
    266                 NULL, 0, 3*HZ);
    267
    268         /* reset the endpoint toggle */
    269         usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
    270                 usb_pipeout(pipe), 0);
    271
    272         US_DEBUGP("%s: result = %d/n", __FUNCTION__, result);
    273         return result;
    274 }

258,usb_pipeendpoint,定义于include/linux/usb.h,

   1091 #define usb_pipeendpoint(pipe)  (((pipe) >> 15) & 0xf)

很简单,右移15,然后与0xf相与,得到的自然就是原来pipe里边的1518. 我们曾经讲过,一个pipe15位至18位是endpoint,(一共16endpoint,)所以很显然,这里就是得到endpoint.然后把她赋给了endp.然后usb_pipein()也定义于同一文件中,

 1088 #define usb_pipein(pipe)        ((pipe) & USB_DIR_IN)
   1089 #define usb_pipeout(pipe)       (!usb_pipein(pipe))

显然,就是判断她是不是IN的管道.如果是IN,那么她返回1,反之,返回0.usb_pipeout则相反.261,如果是,就或上.

263,再一次调用usb_stor_control_msg来传递信息了.USB_REQ_CLEAR_FEATURE对应的usb spec的一个标准请求命令CLEAR_FEATURE(即凡是usb设备就应该支持的命令),表示清除一个设备的某种特征,USB_ENDPOINT_HALT则对应usb的端点特征,每个端点都有这么一个特征,ENDPOINT_HALT,她指出端点是否处于停止状态.CLEAR_FEATURE命令用来清除该端点的停止状态.说明了CLEAR_FEATURE清除的是端点的特征.结合usb_stor_control_msg形参实参来看,usb spec(Table 9-3)规定对于这个请求,wValue要被设置为被Feature Selector,赋值为USB_ENDPOINT_HALT,即选择的FeatureENDPOINT_HALT,wIndex要被设置为指定一个Endpoint,参考usb2.0规范,在指定一个EndpointwIndex的格式,可知,低四位为端点号(D3~D0),D7为方向,(IN/OUT),其余各位为保留位.实际上赋值为endp,正是包含了方向和端点号这两个信息.wLength要求被设置为0,data设置为NULL,这些都没错.超时设了3s.酱紫,就可以清除这个EndpointENDPOINT_HALT这个flag.关于usb_stor_control_msg我们当然就不用再讲了,忘记了的回头去看吧,反正一样的天一样的脸,一样的函数就在你面前.唯一不同的只是传递的参数不同罢了,也许这就是曾经沧海难为水吧,我们的人生也是如此,只能是一条不归路,走上去,就回不了头,谁也没有办法重走一遍曾经的路.

需要特别注意一下,上次GETMaxLUN调用usb_stor_control_msg的时候,我们倒数第四个参数是设了0,而这里我们传递了一个endp,这是因为不同的请求spec里边规定好了的,虽然这两个命令控制的对象不一样,但是作为控制传输,主机总是和控制端点在发生关系.并不因为这里是清楚bulk端点的Halt Feature就要发送给bulk端点,控制传输永远都只是发生在主机和控制端点之间.而真正要控制bulk端点,正是通过我们这里这个endp这么一传递,设备自然就知道该干嘛了.

  接下来,269,又是一个定义于include/linux/usb.h中的宏,

   1101 #define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | ((bit) << (ep)))

  devstruct usb_device结构体指针,直到现在才知道,struct usb_device结构体中有unsigned int toggle[2]这么一个数组,这个数组有两个元素,对应endpointINOUT.OUT来说,每一个endpoint在这里占一位触发位,usb控制传输的数据传输时,每一个包的头部是交替的,有两种包头,DATA0DATA1,为了保证传输的正确性,一次用DATA0,一次用DATA1,一旦哪次没有交叉,host就知道出错了.这里所谓的usb_settoggle,就是对指定的ep所对应的那个togglereset0,然后如果bit不为0,则把bit左移到ep对应的那位再和toggle或上,也就是说,把这个togglereset'bit',比如bit1,那么就是reset1,如果bit0,那么就是reset0,大多数情况下reset都是复位成0,但有时也会不是0,这些都得看心情而定了.((dev)->toggle[out] & ~(1<<(ep)))就是把1左移ep,比如ep3,那么就是得到了1000,然后取反,得到0111,(当然高位还有更多个1),然后(dev)->toggle[out]0111相与,这就是使得toggle[out]的第3位清零而其她位都不变.然后咱们这里bit传递进来的是0,所以就不起什么作用,还是reset0.总之,269行做的事情就是把指定的Endpoint的和指定的pipe对应的那位toggle位给清零.)

当然细心的人会看一下spec,spec里面说了,对于使用data toggleendpoint,不管其halt feature是否被设置了,总之只要你调用Clear Feature,那么其data toggle总是会被初始化为Data0).所以有人就奇怪了,既然调用Clear Feature就已经把data toggle位初始化为0,那这里为什么还要再次作一次set toggle?

事实上是这样的,其实这个世界上有两个toggle bits,不是两个toggle bit,是两个toggle bit_s_,单复数别看错了,其实设备里边是有一个toggle bits,而我们这里软件层次上,也定义了toggle bits,这个toggle bits是给host用的,设备里边的那个toggle bitsclear feature之后,没错,是被初始化成Data0,但是host这边他也想记录下这么一个序列,所以写代码的哥们儿就定义了这个一个数组,而这里调用set toggle的目的无非就是想让这个数组和设备中物理上的那个toggle bits保持同步.

到这里这个函数也就结束了,返回的是这次控制传输的结果,不过我们注意到调用这个函数的上下文,并没有人会care这个返回值,也许这里再判断返回值的意义不大了吧,本来就是在处理出错的代码中.

至此, usb_stor_Bulk_max_lun这个函数也终于要返回了.于是我们终于,终于再一次回到了usb_stor_acquire_resources函数中,我容易吗?别说我了,歌神张学友在看到Linux内核代码如此复杂也不得不感慨说,这代码是一张无边无际的网,轻易就将我困在网中央,我越陷越深越迷惘,路越走越远越漫长,如何我才能锁住这个函数...

776,us->max_lun等于刚才usb_stor_Bulk_max_lun()的返回值.接下来,我们将看到一行具有划时代意义的代码.从此我们唱着东方红,走进新时代,这就是伟大的S-C-S-I.

你可能感兴趣的:(linux,工作,struct,interface,resources,testing)