- Linux那些事儿之我是USB
- 肖林甫 肖季东 任桥伟
- 1897字
- 2020-08-27 00:52:24
34.驱动的生命线(四)
设备自从有了Address,拿到了各种描述符,就在那儿看usb_generic_driver忙活了,不过还算没白忙,设备总算是幸福地进入“configured”了。
Address有点儿像你刚买的一套新房子,如果不装修,它对你来说的意义就是可以对人说你的家庭地址在哪儿了,可实际上你在里边儿什么也干不了,你还得想办法去装修它,configured就像是已经装修好的房子,下面咱就看一看usb_generic_driver又对设备做了些什么。
1525行,nintf就是配置里接口的数目,那么这个for循环显然就是在对配置里的每个接口做处理。
1530行,这里要明白的是,cp里的两个数组interface和intf_cache,一个是没有初始化的,一个是已经动过手术很饱满的。正像前面提到的,这里需要为interface动手术,拿intf_cache的内容去充实它。
1536行,获得这个接口的0号设置。因为某些厂商有特殊的癖好,导致了struct usb_host_config结构中的数组interface并不一定是按照接口号的顺序存储的,必须使用usb_ifnum_to_if()来获得指定接口号对应的struct usb_interface结构体。现在咱们需要再多知道一点,接口里的altsetting数组也不一定是按照设置编号来顺序存储的,必须使用usb_altnum_to_altsetting()来获得接口里的指定设置,它在usb.c中定义。
118 struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf, 119 unsigned int altnum) 120 { 121 int i; 122 123 for (i = 0; i < intf->num_altsetting; i++) { 124 if (intf->altsetting[i].desc.bAlternateSetting == altnum) 125 return &intf->altsetting[i]; 126 } 127 return NULL; 128 }
这个函数依靠一个for循环来解决问题,就是轮询接口里的每个设置,比较它们的编号与你指定的编号是不是一样。原理简单,操作也简单,不简单的是前面调用它时为什么指定编号0,也就是获得0号设置。这还要归咎于在spec里,接口的默认设置总是0号设置,所以这里的目的就是获得接口的默认设置,如果没有拿到设置0,接下来就在1544行拿altsetting数组里的第一项来充数。
1546行,指定刚刚拿到的设置为当前要使用的设置。
1547行,前面遇到过device和endpoint的disable函数,这里遇到interface的enable函数,同样在message.c中定义。
1111 static void usb_enable_interface(struct usb_device *dev, 1112 struct usb_interface *intf) 1113 { 1114 struct usb_host_interface *alt = intf->cur_altsetting; 1115 int i; 1116 1117 for (i = 0; i < alt->desc.bNumEndpoints; ++i) 1118 usb_enable_endpoint(dev, &alt->endpoint[i]); 1119 }
这个函数同样也是靠一个for循环来解决问题,轮询前面获得的那个接口设置使用到的每个端点,调用message.c里的usb_enable_endpoint()将它们enable。
1085 static void 1086 usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) 1087 { 1088 unsigned int epaddr = ep->desc.bEndpointAddress; 1089 unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK; 1090 int is_control; 1091 1092 is_control = ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_ MASK) 1093 == USB_ENDPOINT_XFER_CONTROL); 1094 if (usb_endpoint_out(epaddr) || is_control) { 1095 usb_settoggle(dev, epnum, 1, 0); 1096 dev->ep_out[epnum] = ep; 1097 } 1098 if (!usb_endpoint_out(epaddr) || is_control) { 1099 usb_settoggle(dev, epnum, 0, 0); 1100 dev->ep_in[epnum] = ep; 1101 } 1102 }
这个函数的前面几行没什么好说的,分别获得端点地址,端点号,还有是不是控制端点。后面的两个if函数,它们分别根据端点的方向来初始化设备的ep_in和ep_out数组,还有就是调用了include/linux/usb.h里的一个叫usb_settoggle的宏。
1425 #define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1) 1426 #define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << (ep))) 1427 #define usb_settoggle(dev, ep, out, bit) \ 1428 ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | \ 1429 ((bit) << (ep)))
这三个宏都是用来处理端点的toggle位的,也就是struct usb_device里的数组toggle[2]。toggle[0]对应的是IN端点,toggle[1]对应的是OUT端点,上面宏参数中的out用来指定端点是IN还是OUT,ep指的不是端点的结构体,而仅仅是端点号。
usb_gettoggle用来得到端点对应的toggle值,usb_dotoggle用来将端点的toggle位取反,也就是原来为1就置为0,原来为0就置为1,usb_settoggle看起来就要复杂点儿,意思是如果bit为0,则将ep所对应的toggle位reset成0,如果bit为1,则reset为1。((dev)->toggle[out] & ~(1<<(ep)))就是把1左移ep位,比如ep为3,那么就是得到了1000,然后取反,得到0111,(当然高位还有更多个1),然后(dev)->toggle[out]和0111相与,这就是使得toggle[out]的第3位清零而其他位都不变。这里调用usb_settoggle时,bit传递的是0,用来将端点的toggle位清零,原因就是对于批量传输、控制传输和中断传输来说,数据包最开始都是被初始化为DATA0的,然后才一次传DATA0,一次传DATA1。
现在看一看为什么两个if语句里都有is_control。控制传输使用的是message管道,message管道必须对应两个相同号码的端点,一个用来“in”,一个用来“out”。这里使用两个if,而不是if-else组合,目的就是加进去一个is_control,表示只要是控制端点,就将它的端点号对应的IN和OUT两个方向上的toggle位还有ep_int和ep_out都给初始化。当然,所谓的控制端点一般也就是指端点0。
endpoint的enable函数要比disable函数简单得多,disable时还要深入到HCD的腹地去撤销挂在它上面的各个urb,而enable时就是简单设置toggle位还有那两个数组就好了,要知道它的urb队列urb_list是早在从设备那里获取配置描述符并去解析那一大堆数据时就初始化好了的。enable之后,接口里的各个端点便都处于了可用状态,你就可以在驱动里向指定的端点提交urb了。当然,目前这个时候接口还仍然是接口,驱动(接口驱动)还仍然是驱动。
然后看一看跟在usb_enable_interface()后面的那几行,接口所属的总线类型仍然为usb_bus_type,设备类型变为usb_if_device_type,dma_mask被设置为你的设备的dma_mask,而你设备的dma_mask很早以前就被设置为了host controller的dma_mask。
1553行,device_initialize在初始化设备struct usb_device结构体时遇到过一次,这里初始化接口时又遇到了。
1554行,将接口的is_active标志初始化为0,表示它还没有与任何驱动绑定,就是还没有找到另一半。
1559行,for循环结束了,new_interfaces的历史使命也就结束了。这里的kfree释放的只是new_interfaces指针数组的内存,而不包括它里面各个指针所指向的内存,至于那些数据,都已经在前面被赋给配置里的interface数组了。
1561行,获得配置的字符串描述符。
1570行,这个for循环结束,usb_set_configuration()的三个阶段也就算结束了,设备和usb_generic_driver上上下下忙活了这么久也都很累了,接下来就该接口和接口驱动去忙活了。
这个for循环将前面那个for循环准备好的每个接口送给设备模型,Linux设备模型会根据总线类型usb_bus_type将接口添加到USB总线的那条有名的设备链表里,然后去轮询USB总线的另外一条有名的驱动链表,针对每个找到的驱动去调用USB总线的match函数,也就是usb_device_match(),去为接口寻找另一个匹配的半圆。你说这个时候设备和接口两条路它应该走哪条?它的类型已经设置成usb_if_device_type了,设备那条路“看门儿”的根本就不会让它进,所以它必须得去走接口那条路。