Linux那些事儿 之 戏说USB(23)设备的生命线(二)

来源:互联网 发布:软件静默安装参数 编辑:程序博客网 时间:2024/04/28 16:06

现在设备的struct usb_device结构体已经准备好了,只是还不怎么饱满,hub接下来就会给它做做整容手术,往里边儿塞点什么,充实一些内容,比如:将设备的状态设置为Powered,也就是加电状态;因为此时还不知道设备支持的速度,于是将设备的speed成员暂时先设置为USB_SPEED_UNKNOWN;设备的级别level当然会被设置为hublevel加上1了;还有为设备能够从hub那里获得的电流赋值;为了保证通信畅通,hub还会为设备在总上选择一个独一无二的地址。

经历了几千里路的云和月,属于设备的那个struct usb_device结构体现在又有了多少的功名尘与土?给张表吧,集中列了下到目前为止,设备结构体里成员的状况,这里不用藏着掖着,整就整了,不都是为了生活么。里面的taken只是表示赋过值了,好像期末考试前你去突击自习,这时一个ppmm走到你旁边,用一个美妙的声音问你“Is this seat taken?”,你怎么回答?当然是“No, No, please.”,那如果星爷片子里面的如花过来问你那?你回答什么?对头,taken就是这个意思。那赋了什么值?没必要知道的那么详细,给别人留点自尊,歌里唱的好,人人都需要一点隐私。

devnum

taken

devpath[16]

taken

state

USB_STATE_POWERED

speed

USB_SPEED_UNKNOWN

parent

设备连接的那个hub

bus

设备连接的那条总线

ep0

ep0.urb_list,描述符长度/类型

dev

dev.busdev.typedev.dma_mask,dev.parentdev.bus_id

ep_in[16]

ep_in[0]

ep_out[16]

ep_out[0]

bus_mA

hub->mA_per_port

portnum

设备连接在hub上的那个端口

level

hdev->level + 1

filelist

taken

pm_mutex

taken

autosuspend

taken

autosuspend_delay

2 * HZ

还记不记得无间道里的那个傻强?“我是傻强,我是傻的。”这种经典的台词儿也不是什么时候都有的。那即使是傻强过来瞅瞅,也知道你的设备现在已经处在了Powered状态。前面讲过的,设备要想从Powered状态发展到下一个状态Default,必须收到一个复位信号并成功复位。就好像你要想和你mm从暧昧关系发展到你梦寐以求的以身相许的地步,不是随随便便就可以的,情调、money一个都不能少。那hub接下来的动作就很明显了,复位设备,复位成功后,设备就会进入Default状态。

白天停水,晚上停电,发不出工资,买不起面,打开邓选找到答案,原来是社会主义初级阶段,翻到最后,我靠,一百年不变。设备复位不需要一百年那么久,咱们也等不起,顺利的话也就那么几十毫秒的功夫,都不值得秒针动一下。不顺利的话,它会多尝试几次,难道你追mm时,表白一次不成功就撤退了?那也忒……了些,白受了这么多年教育。不过如果试了几次都复位不成,那就不用试了,这条设备的生命线就算提前玩完儿了。同样的道理,如果你表白了很多次都不成,那还是换个目标吧,虽说失败是成功他妈,挫折本身并不可怕,但可怕的是成功他妈也太多了些了。

现在就算设备成功复位了,大步迈进了Default状态,同时,hub也会获得设备真正的速度,低速、全速也好,高速也罢,总算是浮出水面了,speed也终于知道了自己的真正身份,不用再是UNKNOWN了。那根据这个速度,咱们能知道些什么?起码能够知道端点0一次能够处理的最大数据长度啊,协议里说,对于高速设备,这个值为为64字节,对于低速设备为8字节,而对于全速设备可能为8163264其中的一个。遇到这种模棱两可的答案,写代码的哥们儿不会满足,咱们也不会满足,所以hub还要通过一个蜿蜒曲折的过程去获得这个确定的值,至于怎么个曲折法儿,此处省略2008字。

hub也辛苦的蛮久了,设备也该进入Address状态了,也要知恩图报啊。好像任小强们辛苦了那么久盖了那么多的房子之后,给咱们说,你们快进入Address吧,不然兄弟们撑不下去了,那你怎么办,上下三四代,左右五六家的凑呗,于是21世纪的奇观房奴大军形成了。

咱们的设备要想进入Address状态没那么费劲儿,只要hub使用core里定义的一个函数usb_control_msg,发送SET_ADDRESS请求给设备,设备就兴高采烈的迈进Address了。那么设备的这个address是什么,就是上面的devnum啊,说过的。不用羡慕它们太久,经过了10年,百年,千年,咱们也会Address的。

那现在咱就来说说这个usb_control_msg函数,它在drivers/usb/core/message.c里定义

94 /**
95  *      usb_control_msg - Builds a control urb, sends it off and waits for completion
96  *      @dev: pointer to the usb device to send the message to
97  *      @pipe: endpoint "pipe" to send the message to
98  *      @request: USB message request value
99  *      @requesttype: USB message request type value
100  *      @value: USB message value
101  *      @index: USB message index value
102  *      @data: pointer to the data to send
103  *      @size: length in bytes of the data to send
104  *      @timeout: time in msecs to wait for the message to complete before
105  *              timing out (if 0 the wait is forever)
106  *      Context: !in_interrupt ()
107  *
108  *      This function sends a simple control message to a specified endpoint
109  *      and waits for the message to complete, or timeout.
110  *     
111  *      If successful, it returns the number of bytes transferred, otherwise a negative error number.
112  *
113  *      Don't use this function from within an interrupt context, like a
114  *      bottom half handler.  If you need an asynchronous message, or need to send
115  *      a message from within interrupt context, use usb_submit_urb()
116  *      If a thread in your driver uses this call, make sure your disconnect()
117  *      method can wait for it to complete.  Since you don't have a handle on
118  *      the URB used, you can't cancel the request.
119  */
120 int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
121                          __u16 value, __u16 index, void *data, __u16 size, int timeout)
122 {
123         struct usb_ctrlrequest *dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
124         int ret;
125        
126         if (!dr)
127                 return -ENOMEM;
128
129         dr->bRequestType= requesttype;
130         dr->bRequest = request;
131         dr->wValue = cpu_to_le16p(&value);
132         dr->wIndex = cpu_to_le16p(&index);
133         dr->wLength = cpu_to_le16p(&size);
134
135         //dbg("usb_control_msg");       
136
137         ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
138
139         kfree(dr);
140
141         return ret;
142 }

这个函数主要目的是创建一个控制urb,并把它发送给usb设备,然后等待它完成。urb是什么?忘了么,前面提到过的,你要想和你的usb通信,就得创建一个urb,并且为它赋好值,交给usb core,它会找到合适的host controller,从而进行具体的数据传输。而且我还说会挑选一个黄道吉日,对它大书特书。那现在是不是那个黄道吉日?甭着急,现在还不是,一步一步来,到了我会说的,俺可是一诺千斤石头的,对自己的rp有信心。题外话,你知道最差的rp是什么?最差的rp莫过于痴痴地盯着一个丑女看半晌,然后叹口气说:“靠,这恐龙做得太像真的了……”

123行,为一个struct usb_ctrlrequest结构体申请了内存,这里又出现了一个新生事物,郭天王早提醒我们了,这里的结构说不完。它在include/linux/usb/ch9.h文件里定义

123 /**
124  * struct usb_ctrlrequest - SETUP data for a USB device control request
125  * @bRequestType: matches the USB bmRequestType field
126  * @bRequest: matches the USB bRequest field
127  * @wValue: matches the USB wValue field (le16 byte order)
128  * @wIndex: matches the USB wIndex field (le16 byte order)
129  * @wLength: matches the USB wLength field (le16 byte order)
130  *
131  * This structure is used to send control requests to a USB device.  It matches
132  * the different fields of the USB 2.0 Spec section 9.3, table 9-2.  See the
133  * USB spec for a fuller description of the different fields, and what they are
134  * used for.
135  *
136  * Note that the driver for any interface can issue control requests.
137  * For most devices, interfaces don't coordinate with each other, so
138  * such requests may be made at any time.
139  */
140 struct usb_ctrlrequest {
141         __u8 bRequestType;
142         __u8 bRequest;
143         __le16 wValue;
144         __le16 wIndex;
145         __le16 wLength;
146 } __attribute__ ((packed));

这个结构完全对应于spec里的Table 9-2,描述了主机通过控制传输发送给设备的请求(Device Requests)。一直都在羡慕它们,这会儿到体现出咱们的优越了,主机向设备请求些信息必须得按照协议里规定好的格式,不然设备就会不明白主机是嘛意思,而咱们就不一样了,不用填这个值那个值,你只要唱一句你是风儿我是沙,你mm就明明白白你的心了。不过还是要感慨一下,要是爱情也有固定的格式该多好,这样世界上就少了许多无谓的误会和争吵,把这种精力转化到生产力上,国民生产总值又要提高多少个百分点,绝对比房地产这根柱子的贡献大多了。

这个结构描述的request都在Setup包里发送,Setup包是前面某处说到的Token PID类型中的一种,为了你好理解,这里细说一下控制传输底层的packet情况。控制传输最少要有两个阶段的transactionSETUPSTATUSSETUPSTATUS中间的那个DATA阶段是可有可无的。Transaction这个词儿在很多地方都有,也算是个跨地区跨学科的热门词汇了,在这里你称它为事务也好,会话也罢,我还是直呼它的原名transaction,可以理解为主机和设备之间形成的一次完整的交流,比如2004小姐环球大赛总决赛上评委蔡澜和陕西选手姚佳雯之间的对话:

要老公还是要钱?要钱。

这就可以算是一次transaction,还有

      要父母还是要钱?要父母。

要国家还是要钱?要钱。

这些也都算是transactionsusbtransaction要比上面的对话复杂,起码要过过脑子,它可以包括一个Token包、一个Data包和一个Handshake包。

TokenDataHandshake都属于四种PID类型中的,前面说到时提到的一个包里的那些部分,如SYNCPID、地址域、DATACRC,并不是所有PID类型的包都会全部包括的。Token包只包括SYNCPID、地址域、CRC,并没有DATA字段,它的名字起的很形象,就是用来标记所在transaction里接下来动作的,对于OutSetup Token包,里面的地址域指明了接下来要接收Data包的端点,对于In Token包,地址域指明了接下来哪个端点要发送Data包。还有,只有主机才有权利发送Token包,协议里就这么规定的。别嫌spec规定太多,又管这儿又管那儿的,没有规矩不成方圆,连咱们首都机场的的边检民警们都规定了,微笑服务必须露出8颗牙齿,上边儿四颗下边儿四颗,多一颗少一颗都不合格直接kick,那嘴小的看不到8颗咋办?嘴巴剪大点,嘴大的露的太多咋办?那就缝住,那虎牙咋办?这可是技术难题,不好解决啊,不是中科院、自然科学基金等这几家正在征募10000科学难题么,你可以拿它去申报一下,不定还能拿点资助什么的。

Token包相比,Data包里没了地址域,多了Data字段,这个Data字段对于低速设备最大为8字节,对于全速设备最大为1023字节,对于高速设备最大为1024字节。里里外外看过去,它就是躲在Token后边儿用来传输数据的。Handshake包的成分就非常的简单了,简直和那位姚佳雯的回答一样简单,除了SYNC,它就只包含了一个PID,通过PID取不同的值来报告一个transaction的状态,比如数据已经成功接收了等。

俗话说,兔有三窟,人有三急,控制传输的SETUP transaction一般来说也有三个阶段,就是主机向设备发送Setup Token包、然后发送Data0包,如果一切顺利,设备回应ACK Handshake包表示OK,为什么加上一般?如果中间的那个Data0包由于某种不可知因素被损坏了,设备就什么都不会回应,这时就成俩阶段了。SETUP transaction之后,接下来如果控制传输有DATA transaction的话,那就Data0Data1这样交叉的发送数据包,前面说过这是为了实现data toggle。最后是STATUS transaction,向主机汇报前面SETUPDATA阶段的结果,比如表示主机下达的命令已经完成了,或者主机下达的命令没有完成,或者设备正忙着那没功夫去理会主机的那些命令。

这样经过SETUPDATASTATUS这三个transaction阶段,一个完整的控制传输完成了。主机接下来可以规划下一次的控制传输。

现在对隐藏在控制传输背后的是是非非摸了个底儿,群众的眼睛是雪亮的,咱们现在应该可以看出之前说requests都在Setup包里发送是有问题的,因为Setup包本身并没有数据字段,严格来说它们应该都是在SETUP transaction阶段里Setup包后的Data0包里发送的。还有点糊涂?唉,女人之美,在于蠢得无怨无悔;男人之美,在于说得白日见鬼;诗歌之美,在于煽动男女出轨;学问之美,在于使人一头雾水。那就带着雾水向下看吧,可能会有那么云开雾明的一天。

141行,bRequestType,这个字段别看就一个字节,内容很丰富的,大道理往往都包含这种在小地方。它的bit7就表示了控制传输中DATA transaction阶段的方向,当然,如果有DATA阶段的话。bit5~6表示request的类型,是标准的,class-specific的还是vendor-specific的。bit0~4表示了这个请求针对的是设备,接口,还是端点。内核为它们专门量身定做了一批掩码,也在ch9.h文件里,

42 /*
43  * USB directions
44  *
45  * This bit flag is used in endpoint descriptors' bEndpointAddress field.
46  * It's also one of three fields in control requests bRequestType.
47  */
48 #define USB_DIR_OUT                     0               /* to device */
49 #define USB_DIR_IN                      0x80            /* to host */
50
51 /*
52  * USB types, the second of three bRequestType fields
53  */
54 #define USB_TYPE_MASK                   (0x03 << 5)
55 #define USB_TYPE_STANDARD               (0x00 << 5)
56 #define USB_TYPE_CLASS                  (0x01 << 5)
57 #define USB_TYPE_VENDOR                 (0x02 << 5)
58 #define USB_TYPE_RESERVED               (0x03 << 5)
59
60 /*
61  * USB recipients, the third of three bRequestType fields
62  */
63 #define USB_RECIP_MASK                  0x1f
64 #define USB_RECIP_DEVICE                0x00
65 #define USB_RECIP_INTERFACE             0x01
66 #define USB_RECIP_ENDPOINT              0x02
67 #define USB_RECIP_OTHER                 0x03
68 /* From Wireless USB 1.0 */
69 #define USB_RECIP_PORT                  0x04
70 #define USB_RECIP_RPIPE                 0x05

142行,bRequest,表示具体是哪个request

143行,wValue,这个字段是request的参数,request不同,wValue就不同。

144行,wIndex,也是request的参数,bRequestType指明request针对的是设备上的某个接口或端点的时候,wIndex就用来指明是哪个接口或端点。

145行,wLength,控制传输中DATA transaction阶段的长度,方向已经在bRequestType那儿指明了。如果这个值为0,就表示没有DATA transaction阶段,bRequestType的方向位也就无效了。

struct usb_ctrlrequest的约会暂时就到这里,回到usb_control_msg函数里。很明显要进行控制传输,得首先创建一个struct usb_ctrlrequest结构体,填上请求的内容。129133行就是来使用传递过来的参数初始化这个结构体的。对于刚开始提到的SET_ADDRESS来说,bRequest的值就是USB_REQ_SET_ADDRESS,标准请求之一,ch9.h里定义有。因为SET_ADDRESS请求并不需要DATA阶段,所以wLength0,而且这个请求是针对设备的,所以wIndex也为0。这么一来,bRequestType的值也只能为0了。因为是设置设备地址的,总得把要设置的地址发给设备,不然设备会比咱们还一头雾水不知道主机是嘛个意思,所以请求的参数wValue就是之前hub已经你的设备指定好的devnum。其实SET_ADDRESS请求各个部分的值spec 9.4.6里都有规定,就和我这里说的一样,不信你去看看。

接下来先看139行,走到这儿就表示成也好败也好,总之这次通信已经完成了,那么struct usb_ctrlrequest结构体也就没用了,没用的东西要好不犹豫的精简掉。

回头看137行,这是引领咱们往深处走了,不过不怕,路有多远,咱们看下去的决心就有多远。