老实说,从函数一个开始的598行直到627行都没有什么可说的.其中需要一提的是,606行,调用usb_buffer_alloc()申请内存,赋给hub->buffer.614行,调用kmalloc()申请内存,赋给hub->status.622行,调用kmalloc()申请内存,赋给hub->descriptor.当然也别忘了这中间的某行,初始化一把互斥锁,hub->status_mutex.以后咱们会用得着的,到时候我们就会看到mutex_lock/mutex_unlock()这么一对函数来获得互斥锁/释放互斥锁.不过这把锁用得不多,总共也就两个函数里调用了.咱们走着瞧.
633行,get_hub_descriptor().应该说,从这一刻起,我们将跨入一个新的时代,这一行是里程碑的一行,其意义就好比1992年有一位老人在中国的南海边画了一个圈,其重要性是不言而喻的.这一行,带领我们步入了hub协议.从此摆在我们面前的将不仅仅是usb协议本身了,又加了另一座大山,那就是hub协议,其实hub协议也算usb协议,但是由于hub作为一种特殊的usb设备,在usb spec中,专门有一章是关于hub的,而这一章有150多页.这就是usb spec 2.0中的第十一章.简明起见,下面我将把这一章称作hub协议.而接下来我们的一言一行,都必须遵守hub协议了.来,先看get_hub_descriptor,这就是发送一个request,或者说一个控制传输的控制请求,以获得hub的描述符.基本上hub驱动的这些函数,大多都是来自drivers/usb/core/hub.c中:
137 /* USB 2.0 spec Section 11.24.4.5 */
138 static int get_hub_descriptor(struct usb_device *hdev, void *data, int size)
139 {
140 int i, ret;
141
142 for (i = 0; i < 3; i++) {
143 ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
144 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
145 USB_DT_HUB << 8, 0, data, size,
146 USB_CTRL_GET_TIMEOUT);
147 if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
148 return ret;
149 }
150 return -EINVAL;
151 }
看过usb-storage的你,一定能看懂这个函数,不过有一点我说出来一定会让你吃惊,usb_control_msg()是我们第一次遇到,不信的话可以回去查一下,usb-storage里面根本没有用到这个函数,事实上usb-storage里面自己写了这么一个函数,叫做usb_stor_control_msg().理由是,usb_control_msg()不允许你在调用这个函数的时候取消一个request,而在usb-storage里我们经常做这样的事情,或者说我们有这样的需求,那么我们当然就得自己写一个函数了.不过hub里面没这么多乱七八糟的需求,插拔U盘是一件很正常的事情,可是你说谁没事总是插拔hub?吃多了哈药六厂的新盖中盖吗?
不过有一个好消息,即usb_control_msg和我们讲过的usb_stor_control_msg的参数是一模一样的,所以我们无需再多说.每一个request怎么设置都是在spec里边规定的.对于GET_DESCRIPTOR这个request,如图所示:
hub spec规定, bmRequestType必须是10100000B,normally,GET_DESCRIPTOR的时候,bmRequestType应该等于10000000B.D7为方向位,1就说明时Device-to-host,即IN,D6…5这两位表示Request的类型,可以是标准的类型,或者是Class特定的,或者是Vendor特定的,01就表示Class特定的.D4…0表示接受者,可以是设备,可以是接口,可以是端点.这里为0表示接收者是设备.(这里就体现了相同的request,不同的requesttype的意义了.)
USB_DT_HUB等于29,而hub spec规定GET_DESCRIPTOR这个请求的wValue就该是Descriptor Type和Descriptor Index,wValue作为一个word,有16位,所以高8位放Descriptor Type,而低8位放Descriptor Index,usb 2.0 spec 11.24.2.10里规定了,hub descriptor的descriptor index必须为0.而实际上,usb spec 9.4.3里也规定了,对于非configuration descriptor和string descriptor的其他几种标准descriptor,其descriptor index必须为0.而对于configuration descriptor和string descriptor来说,这个descriptor index用来表征他们的编号,比如一个设备可以有多个configuration descriptor.编号从0开始.所以对于hub descriptor来说,wValue就是高8位表示Descriptor Type,而低8位设成0就可以了.这就是为什么”<<8”了,而Descriptor Type,spec中Table 9-5定义了各种Descriptor的type,如图:
比如Device是1,Configuration是2,而hub这里规定了,它的Descriptor type是29h.而USB_DT_HUB=(USB_TYPE_CLASS|0x09),USB_TYPE_CLASS就是0x01<<5.所以这里USB_DT_HUB就是0x29,即29h,至于他为什么不直接写成29h那就只有天知道了.也许写代码的人想故意迷惑一下读代码的人吧,因为事实上usb 2.0 spec里边很清楚地直接用29h来表示hub的descriptor type,根本不存在两个咚咚的组合.这样写代码完全是对广大观众的不负责,你说我们是不是该鄙视一下这些写代码的同志.
USB_CTRL_GET_TIMEOUT是一个宏,值为5000,即表示5秒超时.
USB_DT_HUB_NONVAR_SIZE也是一个宏,值为7,为啥为7呢?首先请你明白,我们这里要获得的是hub descriptor,这是只有hub才有的一个descriptor,就是说对于hub来说,除了通常的usb设备有的那些设备描述符,接口描述符,端点描述符以外,hub spec自己也定义了一个描述符,这就叫hub描述符.而hub描述符的前7个字节是固定的,表示什么意思也是确定的,但从第八个字节开始,一切就都充满了变数.所以前7个字节被称为非变量,即NON-Variable,而从第7个开始以后的被称为变量,即Variable.这些变量取决于hub上面端口的个数.所谓的变量不是说字节的内容本身是变化的,而是说descriptor具体有几个字节是变化的,比如hub上面有两个端口,那么这个hub的descriptor的字节数和hub上面有4个端口的情况就是不一样的,显然,有四个端口就要纪录更多的信息,当然描述符里的内容就多一些,从而描述符的字节长度也不一样,这超好理解.usb_control_msg()如果执行成功,那么返回值将是成功传输的字节长度,对这里来说,就是传输的hub描述符的字节长度.结合hub spec来看,这个长度至少应该是9,所以这里判断如果大于等于9,那就返回.当然你要问为什么这里要循环三次,这是为了防止通信错误.hub驱动中很多地方都这样做了,主要是因为很多设备在发送它们的描述符的时候总是出错.所以咱们没辙了,多试几次呗.
get_hub_descriptor()结束了,然后就返回hub_configure()中来.635到642行,判断刚才的返回值,小于零当然是出错了,大于零也还要多判断一次, USB_MAXCHILDREN是咱们自己定义的一个宏,值为31.看include/linux/usb.h:
324 #define USB_MAXCHILDREN (31)
其实hub可以接一共255个端口,不过实际上遇到的usb hub最多的也就是说自己支持10个端口的.所以31基本上够用了.当然你要是心血来潮把这个宏改成100,200,那也不会出事,我就不信你能在一台机器上连上百个usb设备.真要那样,你绝对是没事找抽型的.
我们来看一下hub描述符对应的数据结构,struct usb_hub中有一个成员,struct usb_hub_descriptor *descriptor,就是表征hub描述符的,来看struct usb_hub_descriptor,定义于drivers/usb/core/hub.h,
130 struct usb_hub_descriptor {
131 __u8 bDescLength;
132 __u8 bDescriptorType;
133 __u8 bNbrPorts;
134 __le16 wHubCharacteristics;
135 __u8 bPwrOn2PwrGood;
136 __u8 bHubContrCurrent;
137 /* add 1 bit for hub status change; round to bytes */
138 __u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
139 __u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
140 } __attribute__ ((packed));
看见了没有,至少9个字节吧,接下来我们会用到bNbrPorts,它代表Number of downstream facing ports that this hub supports,就是说这个hub所支持的下行端口,刚才这里判断的就是这个值是不是比31还大,如果是,那么对不起,出错了.bHubContrCurrent是Hub控制器的最大电流需求,DeviceRemoveable是用来判断这个端口连接的设备是否是可以移除的,每一个bit代表一个端口,如果该bit为0,则说明可以被移除,为1,就说明不可以移除.而wHubCharacteristics就相对来说麻烦一点了,它记录了很多信息,后面有相当一部分的代码都是在判断这个值,我们这里把hub spec里hub descriptor的定义给贴出来,如下图所示,一会儿让我们对照这张图来看wHubCharacteristics相关的代码:
648行,用一个临时变量wHubCharacteristics来代替描述符里的那个wHubCharacteristics,从650行就开始判断了,首先判断是不是复合设备.在drivers/usb/core/hub.h中定义了如下一些宏,
95 /*
96 * wHubCharacteristics (masks)
97 * See USB 2.0 spec Table 11-13, offset 3
98 */
99 #define HUB_CHAR_LPSM 0x0003 /* D1 .. D0 */
100 #define HUB_CHAR_COMPOUND 0x0004 /* D2 */
101 #define HUB_CHAR_OCPM 0x0018 /* D4 .. D3 */
102 #define HUB_CHAR_TTTT 0x0060 /* D6 .. D5 */
103 #define HUB_CHAR_PORTIND 0x0080 /* D7 */
结合上面这张图,意思很明显.650到661行,如果是复合设备,符合设备就是说这个设备它可能是几种设备绑在一起的,比如既可以当hub用又可以有别的功能,那么就用一个数组portstr[]来记录每一个端口的设备是否可以被移除.然后打印出调试信息来.不要说你看不懂,把i用0,1,2,3这些数代入进去就明白了.
663至674行, HUB_CHAR_LPSM, 表征电源切换的方式,不同的hub有不同的power switching的方式,ganged power switching指的是所有port一次性上电,individual port power switching当然就是各人自扫门前雪,哪管他人瓦上霜.而usb 1.0的hub压根儿就没有power switching这么一个说法.
676到687行, HUB_CHAR_OCPM,表征过流保护模式,其实也没啥,不明白也无所谓,这几行无非就是打印一些调试信息.
689到691行,先是初始化一个自旋锁,hub->tt.lock,struct usb_hub中的成员,struct usb_tt tt,
166 /*
167 * As of USB 2.0, full/low speed devices are segregated into trees.
168 * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
169 * The other type grows from high speed hubs when they connect to
170 * full/low speed devices using "Transaction Translators" (TTs).
171 *
172 * TTs should only be known to the hub driver, and high speed bus
173 * drivers (only EHCI for now). They affect periodic scheduling and
174 * sometimes control/bulk error recovery.
175 */
176 struct usb_tt {
177 struct usb_device *hub; /* upstream highspeed hub */
178 int multi; /* true means one TT per port */
179 unsigned think_time; /* think time in ns */
180
181 /* for control/bulk error recovery (CLEAR_TT_BUFFER) */
182 spinlock_t lock;
183 struct list_head clear_list; /* of usb_tt_clear */
184 struct work_struct kevent;
185 };
知道tt干嘛的吗?tt叫做transaction translator.你可以把它想成一块特殊的电路,是hub里面的电路,确切的说是高速hub中的电路,我们知道usb设备有三种速度的,分别是low speed,full speed,high speed.即所谓的低速/全速/高速,抗日战争那会儿,这个世界上只有low speed/full speed的设备,没有high speed的设备,后来解放后,国民生产力的大幅度提升催生了一种high speed的设备,包括主机控制器,以前只有两种接口的,OHCI/UHCI,这都是在usb spec 1.0的时候,后来2.0推出了EHCI,高速设备应运而生.Hub也有高速hub和过去的hub,但是这里就有一个兼容性问题了,高速的hub是否能够支持低速/全速的设备呢?一般来说是不支持的,于是有了一个叫做TT的电路,它就负责高速和低速/全速的数据转换,于是,如果一个高速设备里有这么一个TT,那么就可以连接低速/全速设备,如不然,那低速/全速设备没法用,只能连接到OHCI/UHCI那边出来的hub口里.我们先不多说,看代码.690行,初始化一个队列,hub->tt.clear_list.然后691行,yeah,终于见到了INIT_WORK(),我没忽悠你吧, hub->tt.kevent是一个struct work_struct的结构体,而hub_tt_kevent是我们定义的函数,将会在未来某年某月的某一天去执行.另外,tt有两种,一种是single tt,一种是multi tt.前者表示整个hub就是一个TT,而multi tt表示每个端口都配了一个TT.大多数hub是single TT,因为一个hub一个TT就够了,国家资源那么紧张,何必铺张浪费.使用single TT就是支持国家反腐倡廉!
692行的switch, hdev->descriptor.bDeviceProtocol,别看走眼了,刚才咱们一直是判断hub->descriptor,而这里是hdev->descriptor,hdev是struct usb_device结构体指针,咱们一进入这个hub_configure()函数就赋了值的,其实就是和这个hub相关的那个struct usb_device指针.所以这里判断的描述符是标准的usb设备描述符,而其中bDeviceProtocol的含义在hub spec里有专门的规定.这一段就是为了设置tt,对照hub spec可知,full/low speed的hub的bDeviceProtocol是0,这种hub就没有tt.所以直接break,啥也不用设.对于high speed的hub,其bDeviceProtocol为1表示是single tt的.为2表示是multiple tt的.对于case 2.这里调用了usb_set_interface,根据usb spec 2.0,11.23.1一节,对于full/low speed的hub,其device descriptor中的bDeviceProtocol为0,而interface descriptor中的bInterfaceProtocol也为0.而对于high speed的hub,其中,single TT的hub其device descriptor中的bDeviceProtocol是1,而interface descriptor的bInterfaceProtocol则是0.然而,multiple TT hub另外还有一个interface descriptor以及相应的一个endpoint descriptor,它的device descriptor的bDeviceProtocol必须设置成2.其第一个interface descriptor的bInterfaceProtocol为1.而第二个interface descriptor的bInterfaceProtocol则是2.hubs只有一个interface,但是可以有两种setting.usb_set_interface就是把这个interface(interface 0)设置成setting 1.因为默认都是setting 0.关于SET_INTERFACE这个request,是usb spec 2.0的一个错误,errata里边有更正.另外,hub->tt.hub就是struct usb_device的结构体指针.hub->tt.multi就是一个int型的flag,设为1就表示这是一个multi tt的hub.
716行, HUB_CHAR_TTTT,后两个TT就是think time的意思.就是说TT在处理两个低速/全速的交易之间需要一点点时间来缓冲.这个最大的间隔就叫做TT think time.这个时间当然不会很长.不过需要注意,这里用的单位是FS bit time,我们知道FS就是Full Speed,其速度是12Mbps,其实也就是1200 0000bps,8 FS bit time就是8bits / 1200 0000 bits per second,即约等于666ns.所以这里就用666ns来记录了.不过以后你会发现,其实think_time这个值我们就没用过.
746到749行, HUB_CHAR_PORTIND,这个表征一个叫做port indicator的冬冬.0说明不支持,1说明支持.indicator是干嘛用的?考考你.虽说六级只考了69分,可是我还是知道indicator就是hub上面的那个指示灯,一闪一闪亮晶晶的指示灯.通常是两种颜色,绿色和琥珀色.你是不是还经常看见红色?这我不发表评论,其实什么颜色无所谓,萝卜白菜各有所爱,不过usb spec上面是给出的这两种颜色.具体实现其实就是一个LED灯,提供两种颜色,或者两个LED灯.有一定生活常识的人就应该知道,其实大多数hub是有指示灯的,不管usb hub还是别的hub,或者switch什么的,统统有指示灯,因为指示灯对于工程师调试硬件产品是很有帮助的.产品出了问题,首先看看指示灯也许就知道怎么回事了,我记得以前在家里上网的时候,网络坏了,打上海电信的电话,人家首先就是问我那几个指示灯是如何亮的.所以说,不支持port indicator的公司一定是脑子进水了.
757行,usb_get_status(),来自drivers/usb/core/message.c:
878 /**
879 * usb_get_status - issues a GET_STATUS call
880 * @dev: the device whose status is being checked
881 * @type: USB_RECIP_*; for device, interface, or endpoint
882 * @target: zero (for device), else interface or endpoint number
883 * @data: pointer to two bytes of bitmap data
884 * Context: !in_interrupt ()
885 *
886 * Returns device, interface, or endpoint status. Normally only of
887 * interest to see if the device is self powered, or has enabled the
888 * remote wakeup facility; or whether a bulk or interrupt endpoint
889 * is halted ("stalled").
890 *
891 * Bits in these status bitmaps are set using the SET_FEATURE request,
892 * and cleared using the CLEAR_FEATURE request. The usb_clear_halt()
893 * function should be used to clear halt ("stall") status.
894 *
895 * This call is synchronous, and may not be used in an interrupt context.
896 *
897 * Returns the number of bytes received on success, or else the status code
898 * returned by the underlying usb_control_msg() call.
899 */
900 int usb_get_status(struct usb_device *dev, int type, int target, void *data)
901 {
902 int ret;
903 u16 *status = kmalloc(sizeof(*status), GFP_KERNEL);
904
905 if (!status)
906 return -ENOMEM;
907
908 ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
909 USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, status,
910 sizeof(*status), USB_CTRL_GET_TIMEOUT);
911
912 *(u16 *)data = *status;
913 kfree(status);
914 return ret;
915 }
又是一个控制传输,发送的是一个控制请求,GET_STATUS是USB的标准请求之一,
这个请求的类型有三种,一种是获得Device的状态,一种是获得Interface的状态,一种是获得端点的状态,这里咱们传递的是USB_RECIP_DEVICE,也就是获得Device的状态.那么函数返回之后,Device的状态就被保存在了hubstatus里面了.
763至788行,就是一个if/elseif/else组合.首先if判断当年这个device是不是root hub,如果是root hub,然后判断hdev->bus_mA,这个值是在host controller的驱动程序中设置的,通常来讲,计算机的usb端口可以提供500mA的电流,不过host controller那边有一个成员power_budget,在host controller的驱动程序中,root hub的hdev->bus_mA被设置为500mA与power_budget中较小的那一个,即如果你有兴趣看一下drivers/usb/core/hcd.c,你会注意到在usb_add_hcd这个函数中有这么两行,
1637 /* starting here, usbcore will pay attention to this root hub */
1638 rhdev->bus_mA = min(500u, hcd->power_budget);
power_budget是一个host controller自己提供的,它可以是0,如果是0,表示没有限制.所以我们这里判断是不是等于0,或者是不是大于等于500mA,如果是的,那么就设置hub->mA_per_port为500mA,mA_per_port就是提供给每一个port的电流.那么如果说bus_mA是0到500之间的某个数,那么说明这个hub没法提供达到500mA的电流,就是host controller那边提供不了这么大的电流,那么hub->mA_per_port就设置为hdev->bus_mA,这种小市民的想法很简单,钱多就多花点,钱少就少花点,没钱就不花.同时,对于这种host controller那边限制了电流的情况,记录下来, hub->limited_power这么一个标志位设置为1.
那么如果不是root hub呢,又有两种情况, USB_DEVICE_SELF_POWERED,hubstatus里的这一位表征这个hub是不是自己供电的,因为外接的hub也有两种供电方式,自己供电或者选择吃大锅饭的形式,即请求总线供电.770行如果满足,那就说明这又是一个要总线供电的hub,于是limited_power也设置为1.774行,maxchild>0那简直是一定的.然后定义了一个int变量remaining来记录剩下多少电流,hdev->bus_mA就是这个hub(不是root hub)上行口的电流,而bHubContrCurrent我们说过了,就是hub需要的电流.两者相减就是剩下的.但是在usb端口上,最小的电流负载就是100mA,这个叫做单元负载(unit load),这个我还是比较清楚的,怎么说大学四年第一次考进系里前5名的课程就是模拟电子线路的期中考试,至今还记得,88分.不吹了,继续,所以778行的意思很显然,比如你这个hub有四个口,即maxchild为4,那么你最起码你得剩下个400mA电流吧,因为如果某个端口电流小于100mA的话,设备是没法正常工作的.然后,782行,警告归警告,最终还是设置mA_per_port为100mA.
784行,如果是自己供电的那种hub,那没得说,直接设置为500mA吧.
793行,hub_hub_status(),这个函数还是来自drivers/usb/core/hub.c:
531 static int hub_hub_status(struct usb_hub *hub,
532 u16 *status, u16 *change)
533 {
534 int ret;
535
536 mutex_lock(&hub->status_mutex);
537 ret = get_hub_status(hub->hdev, &hub->status->hub);
538 if (ret < 0)
539 dev_err (hub->intfdev,
540 "%s failed (err = %d)\n", __FUNCTION__, ret);
541 else {
542 *status = le16_to_cpu(hub->status->hub.wHubStatus);
543 *change = le16_to_cpu(hub->status->hub.wHubChange);
544 ret = 0;
545 }
546 mutex_unlock(&hub->status_mutex);
547 return ret;
548 }
和刚才那个get_hub_status不一样的是,刚才那个GET_STATUS是标准的usb设备请求,每个设备都会有的,但是现在这个请求是hub自己定义的,其格式如下图所示:
最后状态保存在status和change里面,即hubstatus和hubchange里面.而函数hub_hub_status的返回值正常就是0,否则就是负的错误码.
799至807行都是打印调试信息.飘过.
809行开始就是真正的干正经事了.我们知道usb-storage里面最常见的传输方式就是control/bulk传输,而对于hub,它的传输方式就是control/interrupt,而最有特色的正是它的中断传输.注意咱们在调用hub_configure的时候传递进来的第二个参数是endpoint,前面我们已经说了,这正是代表着hub的中断端点,所以815行,一路走过来的兄弟们应该一眼就能看出这一行就是获得连接host与这个端点的这条管道,这条中断传输的管道.不过注意,hub里面的中断端点一定是IN的而不是OUT的.别问我,usb spec就这么规定的,我想改人家也不同意.
816行,usb_maxpacket我们倒是第一次遇见,其实它就是获得一个endpoint描述符里面的wMaxPacketSize,赋给maxp,一个端点一次最多传输的数据就是wMaxPacketSize.不可以超过它.咱们前面为hub->buffer申请了内存,这里maxp如果大于这个size,那么不可以,就让它等于hub->buffer的size.
然后821行开始就是老套路了,申请一个urb,然后填充一个urb,usb_alloc_urb()不用多说,usb_fill_int_urb()可以看一下,来自include/linux/usb.h:
1224 /**
1225 * usb_fill_int_urb - macro to help initialize a interrupt urb
1226 * @urb: pointer to the urb to initialize.
1227 * @dev: pointer to the struct usb_device for this urb.
1228 * @pipe: the endpoint pipe
1229 * @transfer_buffer: pointer to the transfer buffer
1230 * @buffer_length: length of the transfer buffer
1231 * @complete_fn: pointer to the usb_complete_t function
1232 * @context: what to set the urb context to.
1233 * @interval: what to set the urb interval to, encoded like
1234 * the endpoint descriptor's bInterval value.
1235 *
1236 * Initializes a interrupt urb with the proper information needed to submit
1237 * it to a device.
1238 * Note that high speed interrupt endpoints use a logarithmic encoding of
1239 * the endpoint interval, and express polling intervals in microframes
1240 * (eight per millisecond) rather than in frames (one per millisecond).
1241 */
1242 static inline void usb_fill_int_urb (struct urb *urb,
1243 struct usb_device *dev,
1244 unsigned int pipe,
1245 void *transfer_buffer,
1246 int buffer_length,
1247 usb_complete_t complete_fn,
1248 void *context,
1249 int interval)
1250 {
1251 spin_lock_init(&urb->lock);
1252 urb->dev = dev;
1253 urb->pipe = pipe;
1254 urb->transfer_buffer = transfer_buffer;
1255 urb->transfer_buffer_length = buffer_length;
1256 urb->complete = complete_fn;
1257 urb->context = context;
1258 if (dev->speed == USB_SPEED_HIGH)
1259 urb->interval = 1 << (interval - 1);
1260 else
1261 urb->interval = interval;
1262 urb->start_frame = -1;
1263 }
对比一下形参和实参,重点关注这么几项,transfer_buffer就是hub->buffer,transfer_buffer_length就是maxp,complete就是hub_irq,context被赋为了hub,而interval被赋为endpoint->bInterval,不过这里做了一次判断,如果是高速设备(高速hub),那么interval就等于多少多少,否则interval又等于多少多少.至于为什么不一样,其实是因为单位不一样,早年,提到usb协议,人们会提到frame,即帧,改革开放之后,出现了一个新的名词,叫做微帧,即microframe.一个帧是1毫秒,而一个微帧是八分之一毫秒,也就是125微秒.要知道我们刚才说了,这里传递进来的interval实际上是endpoint->bInterval,要深刻认识这段代码背后的哲学意义,需要知道usb中断传输究竟是怎么进行的.
我们说过,hub里面的中断端点是IN的,不是OUT的.但这并不说明凡是中断传输数据一定是从设备到主机,没这种说法,别起哄.不过hub需要的确实只是IN的传输.首先,每一个男人都应该知道,中断是由设备产生的.在usb的世界里两个重要角色,主机,设备.主机就像一个人民公仆,设备就像人民群众,公仆常常日理万机,或者日理万鸡,他也许不会去理会每一个子民的水深火热,群众如果要想引起公仆的注意,只能做一些有创意的事情,他们知道,像许茹芸唱的那样,”没有星星的夜里,我用泪光吸引你”,其实是没有意义的,他们知道只有一些诸如山西黑砖窑事件,如无锡绿藻泛滥事件,如九江大桥坍塌事件,如唐山的黑军车事件,如安徽的粽子事件,才是有意义的,一次事件就可以引发公仆的一次中断,会引发一些事情(数据传输).
那么什么是interval呢?interval就是间隔期,啥叫间隔期?首先你要明白,中断传输绝对不是一种周期性的传输,你说黑砖窑事件会不会一个月来一次?绿藻泛滥事件会不会?黑军车事件会不会?肯定不会对不对.真要是每个月总有几天发生这些事件,那大家都别活了,也甭买安尔乐护舒宝了.那为什么还要有一个间隔期呢?实际上是这样的,尽管中断本身不会定期发生,但是有一个事情是周期性的,对于IN的中断端点,主机会定期向设备问寒问暖,就好比一个父母官定期微服私访,去民间了解民情,那么如果人民生活很艰苦,饱受通货膨胀之苦,买不起房子,人民哭爹叫娘的,父母官就会知道,然后就会进行处理,会想办法去拯救万民于水火之中,会严惩万恶的房地产商.只要每一个当官的都能这样做,何愁天下不太平,人民不安居不乐业.
那么具体来讲,在usb的世界里,体现的是一种设计者们美好的愿望.就是说,设计者们眼看着现实世界中的公仆们都很让人失望,所以在设计spec的时候是这么规定的,host必须要体恤民情,而且,很重要的一点,人民可以提出自己的愿望,比如你希望父母官多久来探望你一次,一个端点的端点描述符里bInterval这么一项写的很清楚,就是她渴望的总线访问周期,或者说她期望host隔多久就能看望一下她.设备要进行中断传输,需要提交一个urb,里面注明这个探访周期,这算是一种民意,而host答应不答应呢,这个在host controller的驱动程序里才知道,我们不管,host当然有一个标准,中断传输可以用在低速/全速/高速设备中,而高速传输可以接受在每个微帧有多达80%的时间是进行定期传输,(包括中断传输和等时传输),而全速/低速传输则可以接受每个帧最多有90%的时间来进行定期传输.所以呢,host那边会统一来安排,他会听取每一个群众(设备)的意见或者说愿望,然后他来统一安排,日程安排得过来就给安排,安排不过来那么就返回错误.这些都是host controller驱动做的,暂且不表.那么如果说host同意,或者说host controller接受了群众的意见,比如你告诉他你希望他每周来看你一次,那好,他每周来一次,来问候你一下,问你有没有什么困难(中断).
从技术的角度来讲,就是,host controller定期给你发送一个IN token,就是说发送这么一个包, 而你如果有中断等在那里,那你就告诉她,你有了(中断).同时你会把与这个中断相关的数据发送给主机,这就是中断传输的数据阶段,显然,这就是IN方向的传输.然后主机接收到数据之后他会发送回来一个包,向你确认一下.那么如果host controller发送给你一个IN token的时候,你没有中断,那怎么办呢?你还是要回应一声,说你没有.当然还有另一种情况是,你可能人不在家,你出差去了,那么你可以在家留个条,告诉人家你没法回答.以上三种情况,对应专业术语来说就是,第一种,你回应的是DATA,主机回应的是ACK或者是Error.第二种,你回应的是NAK,第三种,你回应的是STALL.咱们别玩深沉的,没必要说这些专业术语.就这么说吧,用一段电话对白来解释中断传输的过程中,设备需要做些什么:
对话一: 问:复旦有处女吗? 答:有.在女生肚子里. (DATA)
对话二: 问:复旦有处女吗? 答:没有. (NAK)
对话三: 问:复旦有处女吗? 答:对不起,您所呼叫的用户不在服务区,暂时无法接通. (STALL)
三种情况,你都会有回应,但是意义明显不同.
顺便解释一下OUT类型的中断端点是如何进行数据传输,虽然Hub里根本就没有这种款式的端点.分三步走,第一步,host发送一个OUT token包,然后第二步就直接发送数据包,第三步,设备回应,也许回应ACK,表示成功接收到了数据,也许回应NAK,表示失败了,也许回应STALL,表示设备端点本身有问题,传输没法进行.
Ok,现在可以回到刚才那个interval的问题来了,因为不同速度的interval的单位不一样,所以同样一个数字表达的意思也不一样,那么对于高速设备来说,比如它的端点的bInterval的值为n,那么这表示它渴望的周期是2的(n-1)次方个微帧,比如n为4,那么就表示2的3次方个微帧,即8个125微秒,换句话说就是1毫秒.对于高速设备来说,spec里规定,n的取值必须在1到16之间,而对于全设备来说,其渴望周期在spec里有规定,必须是在1毫秒到255毫秒之间,对于低速设备来说,其渴望周期必须在10毫秒到255毫秒之间.可见,对于全速/低速设备来说,不存在这种指数关系,所以urb->interval直接被赋值为bInterval,而高速设备由于这种指数关系,bInterval的含义就不是那么直接,而是表示那个幂指数.而start_frame是专门给等时传输用的,所以我们不用管了,这里当然直接设置为-1即可.
终于,我们明白了这个中断传输,明白了这个usb_fill_int_urb()函数,于是我们再次回到hub_configure()函数中来,830和831行,这个没什么好说的,usb-storage里面也就这么设的,能用DMA传输当然要用DMA传输.
834行,has_indicators不用说了,刚刚才介绍的,有就设置为了1,没有就是0,不过hub驱动里提供了一个参数,叫做blinkenlights,指示灯这东西有两种特性,一个是亮,一个是闪,我们常说的一闪一闪亮晶晶,有灯了,亮了,但是不一定会闪,所以blinkenlights就表示闪不闪,这个参数可以在我们加载模块的时候设置,默认值是0,在drivers/usb/core/hub.c中有定义:
90 /* cycle leds on hubs that aren't blinking for attention */
91 static int blinkenlights = 0;
92 module_param (blinkenlights, bool, S_IRUGO);
93 MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");
以上都是和模块参数有关的.如果这两个条件都满足,就设置hub->indicator [0]为INDICATOR_CYCLE.这么设置有什么用,咱们以后会看到.不过老实说,指示灯这东西怎么工作根本不是我们感兴趣的,作为一个有志青年我们更应该关心关心国家大事,关心关心巴以冲突,关心关心韩国人质,哪有闲功夫去理睬这种指示灯的事呢.
837行,hub_power_on(),这个函数的意图司马昭之心,路人皆知.无非就是相当于打开电视机开关.不过这里涉及到一个重要的函数,暂且不细讲,稍后会专门讲.
838行, hub_activate().在讲这个函数之前,先看一下hub_configure()中剩下的最后几行,hub_activate()之后就return 0,返回了.841行的fail是行标,之前那些出错的地方都有goto fail语句跳转过来,而且错误码也记录在了ret里面,于是返回ret.好,让我们来看一下hub_activate().这个函数不长,依然来自drivers/usb/core/hub.c:
514 static void hub_activate(struct usb_hub *hub)
515 {
516 int status;
517
518 hub->quiescing = 0;
519 hub->activating = 1;
520
521 status = usb_submit_urb(hub->urb, GFP_NOIO);
522 if (status < 0)
523 dev_err(hub->intfdev, "activate --> %d\n", status);
524 if (hub->has_indicators && blinkenlights)
525 schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
526
527 /* scan all ports ASAP */
528 kick_khubd(hub);
529 }
quiescing和activating就是两个标志符.activating这个标志的意思不言自明,而quiescing这个标志的意思就容易让人疑惑了,永垂不朽的国产软件金山词霸告诉我们,quiescing是停顿,停止,停息的意思,咋一看activating和quiescing就是一对反义词,可是如果真的是起相反的作用,那么一个变量不就够了么?可以为1,可以为0,不就表达了两种意思了吗?嗯,套用小龙-人的歌词,就不告诉你,就不告诉你.至少现在不用告诉你,后面用上了再说.
512行,这个我们太熟悉了,非常熟悉,相当熟悉.前面我们调用usb_fill_int_urb()填充好了一个urb,这会儿就该提交了,然后host controller就知道了,然后如果一切顺利的话,host controller就会定期来询问hub,问它有没有中断,有的话就进行中断传输,这个我们前面讲过了.
524行,又和刚才一样的判断,不过这次判断条件满足了以后就会执行一个函数,schedule_delayed_work(),终于看到这个函数被调用了,不枉费我们前面专门分析的蝴蝶效应吧,前面分析清楚了,这里一看我们就知道,延时调用,调用的是leds对应的那个work函数,即我们当初注册的那个led_work().这里LED_CYCLE_PERIOD就是一个宏,表明延时多久,这个宏在drivers/usb/core/hub.c里定义好了,
208 #define LED_CYCLE_PERIOD ((2*HZ)/3)
关于这个指示灯的代码我们以后再分析,趁着年轻,我们应该赶紧把该做的事情做了,像指示灯相关的代码以后再看也不迟,我们真正需要花时间关注的是hub作为一个特殊的usb设备它是如何扮演好连接主机和设备的作用的.席慕蓉说:这个世界上有许多事情,你以为明天一定可以再继续做的,有很多人,你以为一定可以再见到面的.于是,在你暂时放下手,或者暂时转过身的时候,你心中所想的,只是明日又将重聚的希望.有时候,甚至连这种希望都感觉不到.因为,你以为日子既然这样一天一天过来,当然也应该就这样一天一天过去.昨天,今天,明天应该是没有什么不同的.但是,就会有那么一次,在你一放手,一转身的一刹那,有的事情就完全改变了.太阳落下去,而在它重新开始以前,有些人有些事就从此和你永别.
唉,怎么说着说着这么伤感,继续看,528行,kick_khubd(hub),来自drivers/usb/core/hub.c,
316 static void kick_khubd(struct usb_hub *hub)
317 {
318 unsigned long flags;
319
320 /* Suppress autosuspend until khubd runs */
321 to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
322
323 spin_lock_irqsave(&hub_event_lock, flags);
324 if (list_empty(&hub->event_list)) {
325 list_add_tail(&hub->event_list, &hub_event_list);
326 wake_up(&khubd_wait);
327 }
328 spin_unlock_irqrestore(&hub_event_lock, flags);
329 }
这才是我们真正期待的一个函数,看见wake_up()函数了吧,一看到这里就知道怎么回事了吧,先看321行,int pm_usage_cnt是struct usb_interface的一个成员,pm就是电源管理,usage_cnt就是使用计数,这里的意图很明显,hub要使用了,就别让电源管理把它给挂起来了,不明白?用过笔记本吧?2005年进Intel的时候每人发一本IBM T42,我发现很多时候我合上笔记本它就会自动进入休眠,这叫autosuspend,可是有时候我会发现我合上笔记本以后,笔记本并没有休眠,后来总结出来规律了,下A片的时候基本上计算机是不会进入睡眠的,理由很简单,它发现你有活动着的网络线程,那时候用的是Windows XP,当然Windows也知道计数,别把人家想的那么傻.不过,这里,我们计数的目的不是记录网络线程,而是告诉usb core,咱们拒绝自动挂起,具体的处理会由usb core来统一操作,不用咱们瞎操心,usb core那边自然会判断pm_usage_cnt的,只要我们这里设置了就可以了.
324到327行这段if判断语句,很显然,现在我们是第一次来到这里, 不用说,hub->event_list是空的,所以,条件满足,于是list_add_tail()会被执行,关于队列操作函数,咱们也讲过了,所以这里也不用困惑了,就是往那个总的队列hub_event_list里面加入hub->event_list,然后调用wake_up(&khubd_wait)去唤醒那个昏睡了N多年的hub_thread().如我们前面说过的那样,从此hub_events()函数将再次被执行.
323行和328行,关于自旋锁,老规矩,放到最后面去讲.统一讲.
至此为止,整个关于hub的配置就讲完了,从现在开始,hub就可以正式上班了. 而我们也终于完成了一个目标,唤醒了那个该死的hub_thread(),进入hub_events().
Linux那些事儿之我是Hub(12)再向虎山行 | 发布时间:2010-07-13 07:06:18 |
技术类别:嵌入式 |
需要确认注册邮箱后才能下载,
立即确认我的邮箱
|
徐志摩说:轻轻的我穿衣,正如我轻轻的脱;
后来徐志摩又说:轻轻的我走了,正如我轻轻的来.
hub_events(),没错,胡汉三又回来了.
再一次进入while这个(该)死(的)循环.
第一次来这里的时候,hub_event_list是空的,可是这一次不是了,我们刚刚在kick_khubd()里面才执行了往这个队列里插入的操作,所以我们不会再像第一次一样,从2621行的break跳出循环.相反,我们直接走到2624行,把刚才插入队列的那个节点取出来,存为tmp,然后把tmp从队列里删除掉.(是从队列里删除,不是把tmp本身给删除.)
2627行,list_entry(),这个经典的函数,或者说宏,就像复旦南区食堂的大排,永恒的经典.通过这个宏这里得到的是那个触发hub_events()的hub.然后2628行,同时用局部变量hdev记录hub->hdev.2629行,又得到对应的struct usb_interface和struct device,这下好了,什么都得到了,该付出了吧.
2640行, usb_get_intf(),看仔细了,别和我们当年在usb-storage里面调用的那个usb_get_intfdata()混淆了,这里usb_get_intf只是一个引用计数.是usb core提供的一个函数,以前黑客们推荐用另一个引用计数的函数usb_get_dev(),但在当今世界这样一种现状下,随着一个usb device越来越成为多个interface耦合的情况的出现,struct usb_device实际上已经快淡出历史舞台了,现在在驱动程序里关注的最多的就是interface,而不是device.和usb_get_intf()对应的有另一个函数,叫做usb_put_intf(),很显然,一个是增加引用计数一个减少引用计数.这个函数我们马上就能看到.
前面我们贴hub_events()只贴到2641行,现在继续贴,贴完这个粉恐怖的函数.
2642
2643 /* Lock the device, then check to see if we were
2644 * disconnected while waiting for the lock to succeed. */
2645 if (locktree(hdev) < 0) {
2646 usb_put_intf(intf);
2647 continue;
2648 }
2649 if (hub != usb_get_intfdata(intf))
2650 goto loop;
2651
2652 /* If the hub has died, clean up after it */
2653 if (hdev->state == USB_STATE_NOTATTACHED) {
2654 hub->error = -ENODEV;
2655 hub_pre_reset(intf);
2656 goto loop;
2657 }
2658
2659 /* Autoresume */
2660 ret = usb_autopm_get_interface(intf);
2661 if (ret) {
2662 dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
2663 goto loop;
2664 }
2665
2666 /* If this is an inactive hub, do nothing */
2667 if (hub->quiescing)
2668 goto loop_autopm;
2669
2670 if (hub->error) {
2671 dev_dbg (hub_dev, "resetting for error %d\n",
2672 hub->error);
2673
2674 ret = usb_reset_composite_device(hdev, intf);
2675 if (ret) {
2676 dev_dbg (hub_dev,
2677 "error resetting hub: %d\n", ret);
2678 goto loop_autopm;
2679 }
2680
2681 hub->nerrors = 0;
2682 hub->error = 0;
2683 }
2684
2685 /* deal with port status changes */
2686 for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
2687 if (test_bit(i, hub->busy_bits))
2688 continue;
2689 connect_change = test_bit(i, hub->change_bits);
2690 if (!test_and_clear_bit(i, hub->event_bits) &&
2691 !connect_change && !hub->activating)
2692 continue;
2693
2694 ret = hub_port_status(hub, i,
2695 &portstatus, &portchange);
2696 if (ret < 0)
2697 continue;
2698
2699 if (hub->activating && !hdev->children[i-1] &&
2700 (portstatus &
2701 USB_PORT_STAT_CONNECTION))
2702 connect_change = 1;
2703
2704 if (portchange & USB_PORT_STAT_C_CONNECTION) {
2705 clear_port_feature(hdev, i,
2706 USB_PORT_FEAT_C_CONNECTION);
2707 connect_change = 1;
2708 }
2709
2710 if (portchange & USB_PORT_STAT_C_ENABLE) {
2711 if (!connect_change)
2712 dev_dbg (hub_dev,
2713 "port %d enable change, "
2714 "status %08x\n",
2715 i, portstatus);
2716 clear_port_feature(hdev, i,
2717 USB_PORT_FEAT_C_ENABLE);
2718
2719 /*
2720 * EM interference sometimes causes badly
2721 * shielded USB devices to be shutdown by
2722 * the hub, this hack enables them again.
2723 * Works at least with mouse driver.
2724 */
2725 if (!(portstatus & USB_PORT_STAT_ENABLE)
2726 && !connect_change
2727 && hdev->children[i-1]) {
2728 dev_err (hub_dev,
2729 "port %i "
2730 "disabled by hub (EMI?), "
2731 "re-enabling...\n",
2732 i);
2733 connect_change = 1;
2734 }
2735 }
2736
2737 if (portchange & USB_PORT_STAT_C_SUSPEND) {
2738 clear_port_feature(hdev, i,
2739 USB_PORT_FEAT_C_SUSPEND);
2740 if (hdev->children[i-1]) {
2741 ret = remote_wakeup(hdev->
2742 children[i-1]);
2743 if (ret < 0)
2744 connect_change = 1;
2745 } else {
2746 ret = -ENODEV;
2747 hub_port_disable(hub, i, 1);
2748 }
2749 dev_dbg (hub_dev,
2750 "resume on port %d, status %d\n",
2751 i, ret);
2752 }
2753
2754 if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
2755 dev_err (hub_dev,
2756 "over-current change on port %d\n",
2757 i);
2758 clear_port_feature(hdev, i,
2759 USB_PORT_FEAT_C_OVER_CURRENT);
2760 hub_power_on(hub);
2761 }
2762
2763 if (portchange & USB_PORT_STAT_C_RESET) {
2764 dev_dbg (hub_dev,
2765 "reset change on port %d\n",
2766 i);
2767 clear_port_feature(hdev, i,
2768 USB_PORT_FEAT_C_RESET);
2769 }
2770
2771 if (connect_change)
2772 hub_port_connect_change(hub, i,
2773 portstatus, portchange);
2774 } /* end for i */
2775
2776 /* deal with hub status changes */
2777 if (test_and_clear_bit(0, hub->event_bits) == 0)
2778 ; /* do nothing */
2779 else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
2780 dev_err (hub_dev, "get_hub_status failed\n");
2781 else {
2782 if (hubchange & HUB_CHANGE_LOCAL_POWER) {
2783 dev_dbg (hub_dev, "power change\n");
2784 clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
2785 if (hubstatus & HUB_STATUS_LOCAL_POWER)
2786 /* FIXME: Is this always true? */
2787 hub->limited_power = 0;
2788 else
2789 hub->limited_power = 1;
2790 }
2791 if (hubchange & HUB_CHANGE_OVERCURRENT) {
2792 dev_dbg (hub_dev, "overcurrent change\n");
2793 msleep(500); /* Cool down */
2794 clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
2795 hub_power_on(hub);
2796 }
2797 }
2798
2799 hub->activating = 0;
2800
2801 /* If this is a root hub, tell the HCD it's okay to
2802 * re-enable port-change interrupts now. */
2803 if (!hdev->parent && !hub->busy_bits[0])
2804 usb_enable_root_hub_irq(hdev->bus);
2805
2806 loop_autopm:
2807 /* Allow autosuspend if we're not going to run again */
2808 if (list_empty(&hub->event_list))
2809 usb_autopm_enable(intf);
2810 loop:
2811 usb_unlock_device(hdev);
2812 usb_put_intf(intf);
2813
2814 } /* end while (1) */
2815 }
我真想诚恳的问候一下写代码的人的女性长辈.当裴多菲说:"若为自由故,两者皆可抛",我懂得了作为人的价值;当鲁迅说:"不在沉默中爆发,就在沉默中灭亡",我懂得人应具有反抗精神;当白朗宁说:"拿走爱,世界将变成一座坟墓",我懂得了为他人奉献爱心的重要;当简爱说:"我们是平等的,我不是无感情的机器",我懂得了作为女性的自尊;当我看到这段代码,我却完全不懂写代码的人为什么总要写一些恐怖的函数出来.一定要吓唬吓唬我他们就开心么?我是复旦的,不是厦(吓)大的.
同学们,今天我们来讲一棵树.
记得小时候我们看<<白眉大侠>>,记得那段精彩的对白:刀,是什么样的刀?金丝大环刀!剑,是什么样的剑?闭月羞光剑!招,是什么样的招?天地阴阳招!人,是什么样的人?飞檐走壁的人!情,是什么样的情?美女爱英雄!
而今天我们要问的是:树,是什么样的树?答:USB设备树.这是怎样一棵树?让我慢慢的道来.
苏格拉底曾经说过:为人不识谭浩强,精通内核也枉然.
还记得谭浩强大哥书中那个汉诺塔的例子么?那时候我天真的以为这个例子没什么意义,可是今天我终于明白了.谭大哥早就知道我们学了他的书一定会在今后的生活工作中用得到,这不,hub_events()里面第2645行,locktree(),用的就是汉诺塔里的那个经典思想,递归.谭大哥您要是穿裙子的话,我一定第一个拜倒在您的石榴裙下!locktree()定义于drivers/usb/core/hub.c:
985 /* grab device/port lock, returning index of that port (zero based).
986 * protects the upstream link used by this device from concurrent
987 * tree operations like suspend, resume, reset, and disconnect, which
988 * apply to everything downstream of a given port.
989 */
990 static int locktree(struct usb_device *udev)
991 {
992 int t;
993 struct usb_device *hdev;
994
995 if (!udev)
996 return -ENODEV;
997
998 /* root hub is always the first lock in the series */
999 hdev = udev->parent;
1000 if (!hdev) {
1001 usb_lock_device(udev);
1002 return 0;
1003 }
1004
1005 /* on the path from root to us, lock everything from
1006 * top down, dropping parent locks when not needed
1007 */
1008 t = locktree(hdev);
1009 if (t < 0)
1010 return t;
1011
1012 /* everything is fail-fast once disconnect
1013 * processing starts
1014 */
1015 if (udev->state == USB_STATE_NOTATTACHED) {
1016 usb_unlock_device(hdev);
1017 return -ENODEV;
1018 }
1019
1020 /* when everyone grabs locks top->bottom,
1021 * non-overlapping work may be concurrent
1022 */
1023 usb_lock_device(udev);
1024 usb_unlock_device(hdev);
1025 return udev->portnum;
1026 }
咱们传递进来的是咱们这个hub对应的struct usb_device指针,995行自然不必说,就是防止那些唯恐天下不乱的人调用这个函数.
999行,parent,struct usb_device结构体的parent自然也是一个struct usb_device指针.1000行,判断udev的parent指针,你一定觉得奇怪,好像之前从来没有看到过parent指针啊,为何它突然之间出现了?它指向什么呀?对此我只能说抱歉,其实生活中发生的一切,都是那么的突然.其实我也没有办法,Hub驱动作为一个驱动程序,它并非是孤立存在的,没有主机控制器的驱动,没有usb core,Hub驱动的存在将没有任何意义.其实我以前就说过,Hub,准确的说应该是说,Root Hub,它和Host Controller是绑定在一起的,专业一点说叫做集成在一起的,所以它根本不会像我一样除了拥有孤独其它的一无所有.有时候我真的想知道,当全世界孤独的人团结在一起,孤独是会加深,还是消失…
因为Hub驱动不孤立,所以具体来说,作为Root Hub,它的parent指针在Host Controller的驱动程序中就已经赋了值,这个值就是NULL,换句话说,对于Root Hub,它不需要再有父指针了,这个父指针本来就是给从Root Hub连出来的节点用的,让我们打开天窗说亮话,这里这个函数名字叫做locktree,这个意思就很直接了,locktree嘛,顾名思义,锁住一棵树,这棵树就是USB设备树.很显然,USB设备是从Root Hub开始,一个一个往外面连的,比如Root Hub有4个口,每个口连一个USB设备,比如其中有一个是Hub,那么这个Hub有可以继续有多个口,于是一级一级的往下连,最终肯定会连成一棵树,八卦两句,自从某年某月某一天,我家Intel提出了EHCI的规范以来,当今USB世界的发展趋势是,硬件厂商们总是让EHCI主机控制器里面拥有尽可能多的端口,换言之,理想情况就是希望大家别再用外接的Hub了,有一个Root Hub就够用了,也就是说,真的到了那种情况,USB设备树的就不太像树了,顶多就是两级,一级是Root Hub,下一级就是普通设备,严格来说,对于咱们普通人来说,这样子也就够用了,假设你的Root Hub有8个口,你说你够用不够用?鼠标,键盘,音响,U盘,存储卡,还有啥啊?8个口对正常人来说肯定够了,当然你要是心理变态那么另当别论,说不准就有人愿意往一台电脑里插入个三五十个USB设备的,林子大了,什么鸟没有?
所以说写代码也不是一件容易的事情,除了保证你的代码能让正常人正常使用,还得保证变态也能使用.locktree()的想法就是这样,在hub_events()里面加入locktree()的理由很简单,如果你的电脑里有两个hub,一个叫parent一个叫child,child接在parent的某个口上,那么parent在执行下面这段代码的时候,child hub就不要去执行这段代码,否则会引起混乱,为何会引起混乱?要知道,对于一个hub来说,其所有正常的工作都是在hub_events()这个函数里进行的,那么这些工作就比如,一种情况是,删除一个子设备, 这将有可能直接导致USB设备树的拓扑结构发生变化,或者另一种情况,遍历整个子树去执行一个resume或者reset之类的操作,那么很显然,在这种情况下,一个parent hub在进行这些操作的时候,不希望受到child hub的影响,所以在这样一个政治背景下,2004年的夏天,作为Linux内核开发中USB子系统的三剑客之一的David Brownell大侠,决定加入locktree这么一个函数,这个函数的哲学思想很简单,实际上就是借用了我国古代军事思想中的擒贼仙擒王,用David Brownell本人的话说,就是”lock parent first”.每一个Hub在执行hub_events()中下面的那些代码时,(特指locktree那个括号以下的那些代码.)都得获得一把锁,锁住自己,而在锁住自己之前,又先得获得父亲的锁,确切的说,是尝试获得父亲的锁,如果能够获得父亲的锁,那么说明父亲当前没有执行hub_events()(因为否则就没有办法获得父亲的锁),那么这种情况下子hub才可以执行自己的hub_events(),但是需要注意,在执行自己的代码之前,先把父hub的锁给释放掉,因为我们说了,我们的目的是尝试获得父亲的锁,这个尝试的目的是为了保证在我们执行hub_events()之前的那一时刻,父hub并不是正在执行hub_events(),而至于我们已经开始执行了hub_events(),我们就不在乎父hub是否也想开始执行hub_events()了.
那么有人问,父hub复父hub,父hub何其多?刚才不是说了吗?Root Hub就是整棵树的根,Root Hub就没有父hub,所以,整个递归到了父Hub就可以终止了.这也正是为什么1000行那句if语句,在判断出该设备是一个Root Hub之后,马上就执行锁住该设备.而如果不是Root Hub,那么继续往下走,递归调用locktree(),对于locktree(),正常情况下它的返回值大于等于0,所以小于0就算出错了.
然后1015行判断一下,如果我们把父hub锁住了,可是自己却被断开了,即disconnect函数被执行了,那么就立刻停止,把父hub的锁释放,然后返回吧错误代码-ENODEV.
最后1023行,锁住自己,1024行,释放父设备.
1025行,返回当前设备的portnum.portnum就是端口号,您一定奇怪,没见过什么时候为portnum赋值了啊?这就不好意思了,别忘了咱们走到今天这里是在讨论Root Hub,对于Root Hub来说,它本身没有portnum这么一个概念,因为它不插在别的Hub的任何一个口上.所以对于Root Hub来说,它的portnum在Host Controller的驱动程序里给设置成了0.而对于普通的Hub,它的portnum在哪里赋的值呢?我们后面就会看到的.别急.不过友情提醒一下,对于Root Hub来说,这里根本就不会执行到1025行来,刚才说了,对于Root Hub,实际上1002行那里就返回了,而且返回值就是0.
就这样,我们看完了locktree()这个函数,接下来我们又该返回到hub_events()里面去了.再次爆料一下,2004年,当时的内核还只是2.6.8,David Brownell大侠在那个夏天提出来locktree()的时候,遭到了另一位同是三剑客之一的Alan Stern大侠强烈反对,当时Alan Stern认为David Brownell的算法并不好,没能真正解决并发事件的相互干扰问题,Alan Stern自己有他的算法,于是两个人各自坚持自己的意见,在开源社区轰轰烈烈争论了N久,差点没打起来.那个夏天这两个人从7月30日一直吵到了8月18日,要不是8月15日我在珠江电影制片厂外景地看张柏芝拍大运摩托的那个广告,我说不准就会上去劝架,不过像我这样的人去劝架估计人家也不会理睬,人家会问:你丫谁呀?
最终locktree()被加入到了内核里面,而且不止加了一处,除了加入到了hub_events()之外,在另外两个函数usb_suspend_device()和usb_resume_device()里面也有调用locktree().有趣的是,从2.6.9一直到2.6.22.1的内核,我们都能看到locktree()这么一个函数,但是后来,usb_suspend_device/usb_resume_device中没有了这个函数,就比如我们现在看到的2.6.22.1,搜索整个内核,只有hub_events()这一个地方调用了locktree(),对此,Alan Stern给出的说法是,内核中关于USB挂起的支持有了新的改进,不需要再调用locktree了.但是从2.6.23的内核开始,估计整个内核中就不会再有locktree了,Alan Stern大侠在这个夏天,提交了一个patch,最终把locktree相关的代码全部从内核中移除了.如果您对锁机制特别感兴趣,那么研究一下Linux 2.6以来Hub驱动的历史发展,这个过程锁机制的变更,或者专业一点说,叫同步机制算法的不断改进,足以让你写一篇能在国内核心刊物发表的文章来,要知道首都有一所叫做清华的名牌大学,其硕士研究生也就是一篇国内核心就可以毕业了
看着这代码,空虚的代码,麻木的走在崩溃边缘.最讨厌这种没完没了的判断了.记得有一次在中信泰富广场去摩托罗拉中国研发中心面试,也是问了一道挺简单的题目,我就把基本的算法说了一下,然后面试官就说为什么没有错误判断.你说像我这种根本不怎么懂编程的人好不容易能回答出一道题,已经很不错了,为何那些企业要求都这么高呢?一个人因为没有工作经验而不能得到一个工作,但是他又因为没有一个工作而得不到工作经验.算了,现实充满残忍,何必太认真.算法没错,程序没错,世界没错,我错了.
2645行我们返回了,前面说了,正常都是返回0或者正数,如果小于0那就说明失败了,前面我们还说了,我们在使用interface之前会调用usb_get_intf()来增加引用计数,而与之对应的是usb_put_intf(),这里我们就调用了usb_put_intf()来减少引用计数.continue的意思开始新的一轮while循环,就是踏上奈何桥,喝下孟婆汤,卸下前世的情仇,忘却今生的爱人,睁开眼又是一生,如果hub_event_list里还有东西的话就继续处理,没有那就歇息吧,日子还是像原来那样一天天的过着.
2649行,usb_get_intfdata(),判断一下,得到的是不是hub,你问为什么得到的是hub?回过去看hub_probe(),别忘了那时候我们调用过usb_set_intfdata从而把intf和hub联系了起来的,这两个函数当年我们在usb-storage里面就这样用的,不过当时我们不会判断usb_get_intfdata()得到的到底是什么,而这里我们却要判断,因为在hub_disconnect()中,又这么一句,usb_set_intfdata(intf,NULL),而hub_events()和hub_disconnect()是异步执行的,就是说你执行你的,我执行我的,换言之,当咱们这里hub_events()正执行着呢,hub_disconnect()那边可能就已经取消了intf和hub之间建立起来的那层美好的关系,所以咱们这里需要判断一下.
2653行,现在是时候该说一说USB_STATE_NOTATTACHED这个宏了,在include/linux/usb/ch9.h中:
557 enum usb_device_state {
558 /* NOTATTACHED isn't in the USB spec, and this state acts
559 * the same as ATTACHED ... but it's clearer this way.
560 */
561 USB_STATE_NOTATTACHED = 0,
562
563 /* chapter 9 and authentication (wireless) device states */
564 USB_STATE_ATTACHED,
565 USB_STATE_POWERED, /* wired */
566 USB_STATE_UNAUTHENTICATED, /* auth */
567 USB_STATE_RECONNECTING, /* auth */
568 USB_STATE_DEFAULT, /* limited function */
569 USB_STATE_ADDRESS,
570 USB_STATE_CONFIGURED, /* most functions */
571
572 USB_STATE_SUSPENDED
573
574 /* NOTE: there are actually four different SUSPENDED
575 * states, returning to POWERED, DEFAULT, ADDRESS, or
576 * CONFIGURED respectively when SOF tokens flow again.
577 */
578 };
定义了这么一堆的宏,其中USB_STATE_NOTATTACHED的意思很明显,设备没有插在端口上,在代码里,有几个函数会把设备的状态设置成这个,一个是汇报Host Controller异常死机的函数,usb_hc_died(),一个是咱们hub驱动自己提供的函数,hub_port_disable(),用于关掉一个端口的函数,还有就是用来断开设备的函数usb_disconnect(),总之这几个函数没一个是好鸟,只要它们执行了,那么咱们的设备肯定就没法工作了,所以这里在干正经事之前,先判断设备的状态是不是USB_STATE_NOTATTACHED,如果是的那么就设置错误代码为-ENODEV,然后调用hub_pre_reset(),这个函数是与reset相关的,咱们先不理睬.以后再来看.
2660行,usb_autopm_get_interface(),这个函数是usb core提供的,又是一个电源管理的函数,这个函数所做的事情就是让这个usb interface的电源引用计数加一,也就是说,只要这个引用计数大于0,这个设备就不允许autosuspend.autosuspend就是当用户在指定的时间内没有什么活动的话,就自动挂起.应该说,在usb中引入autosuspend/autoresume这还是最近的事情了,最初有这个想法是在去年,2006年的5月底,Alan Stern大侠在开源社区提出,同志们,俺最近打算开始在USB里面实现对autosuspend/autoresume的支持.所谓的autosuspend/autoresume,实际上是一种运行时的电源管理方式.而这些事情将由驱动程序来负责,即,当驱动程序觉得它的设备闲置了,它就会触发suspend事件,而当驱动程序要使用一个设备了,但该设备正处于suspended状态,那么驱动程序就会触发一个resume事件,suspend事件和resume事件很显然是相对应的,一个挂起一个恢复.这里有一个很关键的理念,又涉及到了前面讲的那个设备树,即,当一个设备挂起的时候,它必须通知它的parent,而parent就会决定看parent是不是也自动挂起,反过来,如果一个设备需要resume,那么它必须要求它的parent也resume,就是说这里有这么一种逻辑关系,一个parent要想suspend,只有在它的children都suspend它才可以suspend,而一个child想要resume,只有在它的parent先resume了它才可以resume.还不明白?举个例子,我和我的室友放寒假在复旦南区澡堂洗澡,每人一个水龙头,洗着洗着,管理员说国家下发了文件说要建设节约型社会,考虑到寒假期间洗澡的人数比较少,决定把水龙头的总闸调小,但是也不能让我们正在洗的人洗不了,所以,它就得先问我们,只有满足了我们在洗的人的那几个水龙头的水量,才可以关小总闸,否则我们肯定得跟他急.同样,开学了以后,大家都来洗澡,可是总闸还是那么小,那大家不干了,但是不干了你得跟管理员说调整总闸,你不能说每个人就调整自己的那个开关,那样肯定没用,总的流量就那么小,时不时还来点侧漏,你说你能洗吗?所以这种情况下就得先开了总闸你单个的开关的调节才有意义.
对于hub来说,当hub_events()处于运行的状态,那么这个hub interface就是在使用,这种情况下是不可以进行autosuspend的.对于咱们这个上下文来说,usb_autopm_get_interface()返回的就是0.但是如果咱们的hub是处于suspended状态,那么这里首先就会把hub唤醒,即会执行resume.先不多说了,继续往下看吧.
2667行,判断quiescing,以前咱们说过,struct usb_hub里面有两个成员,quiescing和activiating,并且咱们在hub_activate()中已经看到了,我们把quiescing设置成了0,而把activating设置成了1.现在是时候来说一说这两个变量的含义了.我们说了quiescing是停止的意思,在reset的时候我们会设置它为1,在suspend的时候我们也会把它设置为1,一旦把它设置成了1,那么hub驱动程序就不会再提交任何URB,而如果我们把activating,那么hub驱动程序就会给每个端口发送一个叫做Get Port Status的请求,通常情况下,hub驱动只有在一个端口发生了状态变化的情况下才会去发送Get Port Status从而去获得端口的状态.所以就是说,正常情况下,这两个flag都是不会设置的.即正常情况下这两个flag都应该是0.
2671行,以咱们这个情景来到这里,hub->error当然是0,但是如果今后我们正式工作以后,再次来到这里的话,hub->error可能就不再是0了.对于那种情况,咱们需要调用usb_reset_composite_device(),这个函数是咱们自己定义的,目的就是把设备reset,我们来具体看一下,来自drivers/usb/core/hub.c:
3041 /**
3042 * usb_reset_composite_device - warn interface drivers and perform a USB port reset
3043 * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
3044 * @iface: interface bound to the driver making the request (optional)
3045 *
3046 * Warns all drivers bound to registered interfaces (using their pre_reset
3047 * method), performs the port reset, and then lets the drivers know that
3048 * the reset is over (using their post_reset method).
3049 *
3050 * Return value is the same as for usb_reset_device().
3051 *
3052 * The caller must own the device lock. For example, it's safe to use
3053 * this from a driver probe() routine after downloading new firmware.
3054 * For calls that might not occur during probe(), drivers should lock
3055 * the device using usb_lock_device_for_reset().
3056 *
3057 * The interface locks are acquired during the pre_reset stage and released
3058 * during the post_reset stage. However if iface is not NULL and is
3059 * currently being probed, we assume that the caller already owns its
3060 * lock.
3061 */
3062 int usb_reset_composite_device(struct usb_device *udev,
3063 struct usb_interface *iface)
3064 {
3065 int ret;
3066 struct usb_host_config *config = udev->actconfig;
3067
3068 if (udev->state == USB_STATE_NOTATTACHED ||
3069 udev->state == USB_STATE_SUSPENDED) {
3070 dev_dbg(&udev->dev, "device reset not allowed in state %d\n",
3071 udev->state);
3072 return -EINVAL;
3073 }
3074
3075 /* Prevent autosuspend during the reset */
3076 usb_autoresume_device(udev);
3077
3078 if (iface && iface->condition != USB_INTERFACE_BINDING)
3079 iface = NULL;
3080
3081 if (config) {
3082 int i;
3083 struct usb_interface *cintf;
3084 struct usb_driver *drv;
3085
3086 for (i = 0; i < config->desc.bNumInterfaces; ++i) {
3087 cintf = config->interface[i];
3088 if (cintf != iface)
3089 down(&cintf->dev.sem);
3090 if (device_is_registered(&cintf->dev) &&
3091 cintf->dev.driver) {
3092 drv = to_usb_driver(cintf->dev.driver);
3093 if (drv->pre_reset)
3094 (drv->pre_reset)(cintf);
3095 }
3096 }
3097 }
3098
3099 ret = usb_reset_device(udev);
3100
3101 if (config) {
3102 int i;
3103 struct usb_interface *cintf;
3104 struct usb_driver *drv;
3105
3106 for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
3107 cintf = config->interface[i];
3108 if (device_is_registered(&cintf->dev) &&
3109 cintf->dev.driver) {
3110 drv = to_usb_driver(cintf->dev.driver);
3111 if (drv->post_reset)
3112 (drv->post_reset)(cintf);
3113 }
3114 if (cintf != iface)
3115 up(&cintf->dev.sem);
3116 }
3117 }
3118
3119 usb_autosuspend_device(udev);
3120 return ret;
3121 }
usb_autoresume_device()增加device的引用计数,禁止设备autosuspend的发生.
后面的那个usb_autosuspend_device()则刚好相反,减少设备的引用计数,并且使得设备可以被autosuspend.
3068行,在设备处于USB_STATE_NOTATTACHED或者USB_STATE_SUSPENDED的状态时,reset是不被允许的.
3078行,别忘了我们传递给usb_reset_composite_device的有两个参数,一个是设备,一个是interface.而USB_INTERFACE_BINDING是一个宏,来自include/linux/usb.h:
83 enum usb_interface_condition {
84 USB_INTERFACE_UNBOUND = 0,
85 USB_INTERFACE_BINDING,
86 USB_INTERFACE_BOUND,
87 USB_INTERFACE_UNBINDING,
88 };
这是表征interface的状态的,BINDING就表示正在和driver绑定,有些事情不是我故意瞒着你,而是我的确不想多说,确切的说,我也是一言难尽哪.最开始,在usb core发现初始化设备的时候,但是在hub_probe被调用之前,INTERFACE是处于USB_INTERFACE_BINDING状态的,直到hub_probe结束了之后,INTERFACE则是处于USB_INTERFACE_BOUND状态,即所谓的绑定好了,而如果hub_probe出了错,那么INTERFACE就将处于USB_INTERFACE_UNBOUND状态.我们这里config是struct usb_host_config结构体指针,被赋为udev->actconfig,对于一个设备来说,它使用的是什么配置,这个在初始化的时候就设好了的.对于Root Hub我们先不管它究竟设置为什么了,对于普通的Hub我们以后会看到的.struct usb_host_config结构体中有一个结构体struct usb_config_descriptor desc,表示configuration描述符,还有一个struct usb_interface *interface[USB_MAXINTERFACES]数组,USB_MAXINTERFACES定义为32,这些我们当年在usb-storage里面也都见过.所以config->desc.bNumInterfaces以及config->interface[]数组的意思就很明确了,3086行开始的这个for循环就是说,这个device有几个interface就一个一个遍历,cintf作为临时变量来表征每一个struct usb_interface.首先我们要明白, usb_reset_composite_device()这个函数我们可是既指定了设备又指定了interface的,那么3088行判断cintf等不等于iface是什么意思呢?回到刚才那个3078行,我们设置了,如果iface不处在BINDING的情况下,我们将iface设置为NULL了,而这里cintf是从config->interface[]数组里得出来的值,它肯定不为NULL,那么这里如果cintf不等于iface,那就说明iface之前是不处于BINDING的状态,对于这种情况我们需要执行3089行,down(&cintf->dev.sem),这样做的原因是为了等待,dev是struct device结构体,是struct usb_interface结构体的一个成员,而sem是struct device结构体的一个信号量,struct semaphore sem,这个信号量专门用于同步,之所以这里需要信号量,原因如下:既然我们现在针对的是一个device多个interface的情况,那么势必就有这么一种可能,一个设备多个interface,每个interface对应一个driver,那么当设备fail的时候,有可能每个driver都希望能够reset,那么对于这种情况,我们当然需要保证这个过程不要出现混乱,于是设置一个信号量就ok了,你reset的时候我就不能reset,我reset的时候你就不能reset.那么为什么要单独把USB_INTERFACE_BINDING列出来呢?注释里说得很清楚了,当一个interface的状态处于BINDING的时候,其实就是这个interface对应的driver的probe()函数正在执行,这个时候实际上是已经获得了锁的.你问为什么?看代码去,probe函数是咱们提供给usb core调用的,咱们自己不会调用它,usb core中,调用probe()的前后也有这么一对down()/up().因为probe()这个操作也忌讳被别人影响.所以说这里对于正处于BINDING状态的interface,就不需要再获得锁了,或者说不需要获得信号量了.否则就将是一道经典的死锁问题,我第一次遇到内核Bug就是一次死锁问题,在8250串口驱动中,明明已经有锁了,还要再次去获取锁,结果系统就挂了.而这正是锁的哲学,即走别人的路,让别人无路可走.
另一个问题需要清楚的是,usb_reset_composite_device()这个函数的诞生是因为目前越来越多的设备都是复合设备,即一个设备里面有多个interface的那种.内核中引入这个函数是在2006年夏天,在德国世界杯开幕前不久,在我离开Intel的那一天,确切的说是,2006年5月26日,Alan Stern大侠又一次向社区里提出一个新的理念.即,原本,对于每个设备来说,都可以调用函数usb_reset_device来执行reset操作,而当今天下发展趋势是让一个设备包含多个接口,即一个device包含多个interface,而我们知道一个driver对应一个interface,于是就出现了多个drivers对应一个设备的情况,那么一个usb_reset_device()函数就有可能对所有的interface都造成影响,于是,Alan利用这样两个函数,struct usb_driver结构体中两个成员函数pre_reset和post_reset,我们知道每个struct usb_driver都有两个成员pre_reset和post_reset,而Alan的理念是每个driver定义自己的pre_reset和post_reset,当我们在调用usb_reset_device之前,先遍历调用每个driver的pre_reset,Alan称这些个pre_reset为给每个绑定在该设备上的drivers一个警告,告诉它们,要reset了.在执行完usb_reset_device之后,再遍历调用每个driver的post_reset,post_reset的作用是让每个driver知道,reset完成了,另一方面,post_reset还有一个作用,因为reset会把设备原来的状态都给清除掉,所以post_reset就担负了这么一个使命,即重新初始化设备.但是你得明白,并不是每一个设备驱动都定义了pre_reset和post_reset,大家有没有必要执行这两个操作那都是自己决定,你要是无所谓,觉得别人reset整个device对你这个interface没什么影响,那就不为这两个指针赋值,那也ok.usb core为你提供了这么一个机制,你用不用自己看着办.这就相当于Intel允许报销医药费,但是可能我没有买药的需求,那么我就不用报销.这也就是为什么在3093行和3111行这两处要判断,判断这两个指针是否被赋了值,只有赋了值才去执行相应的函数,否则就没有必要瞎起哄.
继续看,device_is_registered()是一个内联函数,就是判断struct device结构体指针的一个成员is_registered是否为1,这个值对于Root Hub来说,在Host Controller驱动程序中初始化时把它设置为1,对于普通的设备来说,以后我们会看到,在Hub驱动为其作初始化的时候也会设置为1.而dev.driver也是在初始化的时候会赋好值,特别的,对于咱们的hub,这个dev.driver就是与之对应的struct device_driver结构体,而3092行这个to_usb_driver()是一个宏,它得到的就是与之对应的struct usb_driver结构体,而对于hub来说,这就是struct usb_driver hub_driver,所以,我们就不难知道,3094行以及后面3112行所做的就是调用hub_driver里面的两个成员函数,它们是hub_pre_reset()和hub_post_reset().
Ok,暂时打住,越来越偏离主线了.总之,3094行,3099行,3012行,这三个函数的调用,就是真正的完成了一次hub的reset.就相当于计算机的一次重起,重起的原因是因为我们遇见了hub->error.这三个函数的细节我们先暂时不看,以后再看.需要强调的一点是,3101到3117行这一段,和3081到3097行这一段,基本上是对称的.
3120行,return ret返回了.返回值就是usb_reset_device的返回值.
回到hub_events()之后,立刻把hub->nerrors和hub->error给复位了,设置为0,想开心就要舍得伤心,想忘记就要一切归零.其中nerrors是记录发生错误的次数的,nerrors就是number of errors,要是连续发生错误就每次让nerrors加一.
从下面开始就进入hub驱动中真正干正经事的代码了.可以说hub_events()中,此前的每一行代码都显得非常的枯燥,让人根本看不明白hub驱动究竟是干嘛的,直到下面这些代码才真正诠释了一个hub驱动应该做的事情.我们需要明白,Hub的存在不是为了它自己,我们不是为了用Hub而买Hub,我们是为了让Hub连接我们真正想用的设备.设备是Hub生命中的过客,而Hub点缀了设备的人生.
烟波江声里,何处是江南.一晃神,一转眼,我们就这样垂垂老去.可是我们直到现在还根本就没有看明白hub驱动究竟是怎么工作的.但我相信,红莲即将绽放,双星终会汇聚,命运的轮转已经开始,我们只需耐心的等待.
2686行,苦苦追寻之后,终于发现从这里开始针对端口进行分析了,有几个端口就对几个端口进行分析,分析每一个端口的状态变化,一个都不能少,很显然,这就是我们期待看到的代码,马上我们就可以知道,当我们把一个usb设备插入usb端口之后究竟会发生什么,知道usb设备提供的那些接口函数究竟是如何被调用的,特别是那个probe函数.这一刻,红领巾迎着太阳,阳光洒在海面上,水中鱼儿望着我们,悄悄的听我们愉快的歌唱,小船儿轻轻飘荡在水中,迎面吹来了凉爽的风!
bNbrports是前面我们获得的hub descriptor的一个成员,表征这个hub有几个端口.很显然,月可无光,星可无语,usb设备却不可以没有描述符.这里就是遍历每一个端口.busy_bits是struct usb_hub的一个成员,unsigned long busy_bits[1],接下来的event_bits也是,change_bits也是,unsigned long event_bits[1],unsigned long change_bits[1],test_bit()我们太熟悉了,在usb-storage里面到处都是,只不过当时我们测试的是us->flags里面的某个flag是否设置了,而这里我们要测试的有三个东东,首先测试busy_bits,这个flag实际上只有在reset和resume的函数内部才会设置,所以这里我们先不用管,而这里的意思是,如果眼下这个端口正在执行reset或者resume操作,那么咱们就跳过去,不予理睬.
2689行,测试change_bits.结合2690,2691,2692行一起看.如果这个端口对应的change_bits没有设置,event_bits没有设置过,hub->activating也为0,那么这里就执行continue,不过我们想都不用想,因为我们就是从hub_activate进来的.我们来的时候activating就是设置成了1的,所以这里的continue我们是不用执行的.换言之,我们继续往下走.
2694行,hub_port_status(),portstatus和portchange是我们在hub_events()伊始定义的两个变量,u16 portstatus,u16 portchange,即两个都是16位.尽管说了N遍了,但是我还是得说第N+1遍,这个函数仍然是来自drivers/usb/core/hub.c:
1413 static int hub_port_status(struct usb_hub *hub, int port1,
1414 u16 *status, u16 *change)
1415 {
1416 int ret;
1417
1418 mutex_lock(&hub->status_mutex);
1419 ret = get_port_status(hub->hdev, port1, &hub->status->port);
1420 if (ret < 4) {
1421 dev_err (hub->intfdev,
1422 "%s failed (err = %d)\n", __FUNCTION__, ret);
1423 if (ret >= 0)
1424 ret = -EIO;
1425 } else {
1426 *status = le16_to_cpu(hub->status->port.wPortStatus);
1427 *change = le16_to_cpu(hub->status->port.wPortChange);
1428 ret = 0;
1429 }
1430 mutex_unlock(&hub->status_mutex);
1431 return ret;
1432 }
重要的是其中的那个get_port_status()函数.
300 /*
301 * USB 2.0 spec Section 11.24.2.7
302 */
303 static int get_port_status(struct usb_device *hdev, int port1,
304 struct usb_port_status *data)
305 {
306 int i, status = -ETIMEDOUT;
307
308 for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {
309 status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
310 USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
311 data, sizeof(*data), USB_STS_TIMEOUT);
312 }
313 return status;
314 }
一路从泥泞走到美景,我们再也不会对usb_control_msg()函数陌生了,这个函数做什么勾当我们完全是一目了然.Get Port Status是Hub的一个标准请求,对我们来说就是一次控制传输就可以搞定.这个请求的格式如下图所示:
其中这个GET_STATUS的对应具体的数值可以在下面这张表中对比得到,
而关于各种请求,咱们在include/linux/usb/ch9.h中也定义了相应的宏,
72 /*
73 * Standard requests, for the bRequest field of a SETUP packet.
74 *
75 * These are qualified by the bRequestType field, so that for example
76 * TYPE_CLASS or TYPE_VENDOR specific feature flags could be retrieved
77 * by a GET_STATUS request.
78 */
79 #define USB_REQ_GET_STATUS 0x00
80 #define USB_REQ_CLEAR_FEATURE 0x01
81 #define USB_REQ_SET_FEATURE 0x03
82 #define USB_REQ_SET_ADDRESS 0x05
83 #define USB_REQ_GET_DESCRIPTOR 0x06
84 #define USB_REQ_SET_DESCRIPTOR 0x07
85 #define USB_REQ_GET_CONFIGURATION 0x08
86 #define USB_REQ_SET_CONFIGURATION 0x09
87 #define USB_REQ_GET_INTERFACE 0x0A
88 #define USB_REQ_SET_INTERFACE 0x0B
89 #define USB_REQ_SYNCH_FRAME 0x0C
90
91 #define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */
92 #define USB_REQ_GET_ENCRYPTION 0x0E
93 #define USB_REQ_RPIPE_ABORT 0x0E
94 #define USB_REQ_SET_HANDSHAKE 0x0F
95 #define USB_REQ_RPIPE_RESET 0x0F
96 #define USB_REQ_GET_HANDSHAKE 0x10
97 #define USB_REQ_SET_CONNECTION 0x11
98 #define USB_REQ_SET_SECURITY_DATA 0x12
99 #define USB_REQ_GET_SECURITY_DATA 0x13
100 #define USB_REQ_SET_WUSB_DATA 0x14
101 #define USB_REQ_LOOPBACK_DATA_WRITE 0x15
102 #define USB_REQ_LOOPBACK_DATA_READ 0x16
103 #define USB_REQ_SET_INTERFACE_DS 0x17
比如这里咱们传递给usb_control_msg的request就是USB_REQ_GET_STATUS,它的值为0,和usb spec中定义的GET_STATUS的值是对应的.这个请求返回两个咚咚,一个称为Port Status,一个称为Port Change Status.usb_control_msg()的调用注定了返回值将保存在struct usb_port_status *data里面,这个结构体定义与drivers/usb/core/hub.h中:
58 /*
59 * Hub Status and Hub Change results
60 * See USB 2.0 spec Table 11-19 and Table 11-20
61 */
62 struct usb_port_status {
63 __le16 wPortStatus;
64 __le16 wPortChange;
65 } __attribute__ ((packed));
很显然这个格式是和实际的spec规范对应的,我们给get_port_status()传递的实参是&hub->status->port,port也是一个struct usb_port_status结构体变量,所以在hub_port_status()里面,1426和1427两行我们就得到了Status Bits和Status Change Bits.get_port_status()返回值就是GET PORT STATUS请求的返回数据的长度,它至少应该能够保存wPortStatus和wPortChange,所以至少不能小于4,所以1420行有这么一个错误判断.这样,hub_port_status()就返回了,而status和change这两个指针也算是满载而归了,正常的话返回值就是0.
继续往下走,2699行,children[i-1],这么一个冬冬我们从没有见过,但是我想白痴都知道,正是像parent和children这样的指针才能把USB树给建立起来,而我们才刚上路,肯定还没有设置children,所以对我们来说,至少目前children数组肯定为空,而我们又知道hub->activating这时候肯定为1,所以就看第三个条件了,portstatus&USB_PORT_STAT_CONNECTION,这是啥意思?稍有悟性的人就能看出来,这表明这个端口连了设备,没错,USB_PORT_STAT_CONNECTION这个宏定义于drivers/usb/core/hub.h中:
67 /*
68 * wPortStatus bit field
69 * See USB 2.0 spec Table 11-21
70 */
71 #define USB_PORT_STAT_CONNECTION 0x0001
72 #define USB_PORT_STAT_ENABLE 0x0002
73 #define USB_PORT_STAT_SUSPEND 0x0004
74 #define USB_PORT_STAT_OVERCURRENT 0x0008
75 #define USB_PORT_STAT_RESET 0x0010
76 /* bits 5 to 7 are reserved */
77 #define USB_PORT_STAT_POWER 0x0100
78 #define USB_PORT_STAT_LOW_SPEED 0x0200
79 #define USB_PORT_STAT_HIGH_SPEED 0x0400
80 #define USB_PORT_STAT_TEST 0x0800
81 #define USB_PORT_STAT_INDICATOR 0x1000
82 /* bits 13 to 15 are reserved */
83
84 /*
85 * wPortChange bit field
86 * See USB 2.0 spec Table 11-22
87 * Bits 0 to 4 shown, bits 5 to 15 are reserved
88 */
89 #define USB_PORT_STAT_C_CONNECTION 0x0001
90 #define USB_PORT_STAT_C_ENABLE 0x0002
91 #define USB_PORT_STAT_C_SUSPEND 0x0004
92 #define USB_PORT_STAT_C_OVERCURRENT 0x0008
93 #define USB_PORT_STAT_C_RESET 0x0010
这都是这两个变量对应的宏,usb 2.0的spec里面对这些宏的意义说得很清楚,USB_PORT_STAT_CONNECTION的意思的确是表征是否有设备连接在这个端口上,我们不妨假设有,那么portstatus和它相与的结果就是1,在usb spec里面,这一位叫做Current Connect Status位,于是这里我们会看到connect_change被设置成了1.而接下来,USB_PORT_STAT_C_CONNECTION则是表征这个端口的Current Connect Status位是否有变化,如果有变化,那么portchange和USB_PORT_STAT_C_CONNECTION相与的结果就是1,对于这种情况,我们需要发送另一个请求以清除这个flag,并且将connect_change也设置为1.这个请求叫做Clear Port Feature.这个请求也是Hub的标准请求,
它的作用就是reset hub端口的某种feature.clear_port_feature()定义于drivers/usb/core/hub.c:
162 /*
163 * USB 2.0 spec Section 11.24.2.2
164 */
165 static int clear_port_feature(struct usb_device *hdev, int port1, int feature)
166 {
167 return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
168 USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
169 NULL, 0, 1000);
170 }
对比上面贴出来的定义可知,USB_REQ_CLEAR_FEATURE和usb spec中的CLEAR_FEATURE这个请求是对应的,那么一共有些什么Feature呢?在drivers/usb/core/hub.h中是这样定义的,
38 /*
39 * Port feature numbers
40 * See USB 2.0 spec Table 11-17
41 */
42 #define USB_PORT_FEAT_CONNECTION 0
43 #define USB_PORT_FEAT_ENABLE 1
44 #define USB_PORT_FEAT_SUSPEND 2
45 #define USB_PORT_FEAT_OVER_CURRENT 3
46 #define USB_PORT_FEAT_RESET 4
47 #define USB_PORT_FEAT_POWER 8
48 #define USB_PORT_FEAT_LOWSPEED 9
49 #define USB_PORT_FEAT_HIGHSPEED 10
50 #define USB_PORT_FEAT_C_CONNECTION 16
51 #define USB_PORT_FEAT_C_ENABLE 17
52 #define USB_PORT_FEAT_C_SUSPEND 18
53 #define USB_PORT_FEAT_C_OVER_CURRENT 19
54 #define USB_PORT_FEAT_C_RESET 20
55 #define USB_PORT_FEAT_TEST 21
56 #define USB_PORT_FEAT_INDICATOR 22
而在usb spec中,有一张与之相对应的表格,定义了许多的feature,如图所示:
所以,我们清除的是C_PORT_CONNECTION这一个feature.spec里面说了,清除一个状态改变的feature就等于承认这么一个feature.(clearing that status change acknowledges the change)理由很简单,每次你检测到一个flag被设置之后,你都应该清除掉它,以便下次人家一设你就知道是人家设了,否则你不清你下次判断你就不知道是不是又有人设了.同理,接下来的每个与portchange相关的判断语句都要这么做.所以如果portchange与上USB_PORT_STAT_C_CONNECTION确实为1,那么我们就要清除这个feature.同时我们当然也要记录connect_change为1.
继续,每个端口都有一个开关,这叫做enable或者disable一个端口.portchange和USB_PORT_STAT_C_ENABLE相与如果为1的话,说明端口开关有变化.和刚才一样,首先我们要做的是,清除掉这个变化的feature.但是这里需要注意,spec里对这个feature是这样规定的,如果portchange和USB_PORT_STAT_C_ENABLE为1,说明这个port是从enable状态进入了disable状态.为什么呢?因为在spec规定了,Hub的端口是不可以直接设置成enable的.通常让Hub端口enable的方法是reset hub port.用spec的话说,这叫做发送另一个request,名为SET_FEATURE.SET_FEATURE和CLEAR_FEATURE是对应的,一个设置一个清除. 对于PORT_ENABLE这一位,用spec里的话说,This bit may be set only as a result of a SetPortFeature(PORT_RESET) request,PORT_RESET是为hub定义的众多feature中的一种.最后提醒一点,2711至2715这段if语句仅仅是为了打印调试信息的,就是说如果port enable改变了,但是端口连接没有改变,那么打印出信息来通知调试者,不要把clear_port_feature这一行也纳入到if语句里去了.因为port enable的改变有多种可能,其中一种可能就是由于检测到了disconnection,但是对于这种情况,我们下面要处理的,所以甭急.
下面这段代码就比较帅了,电磁干扰都给扯进来了.EMI,就是电磁干扰.就是说有的时候hub port的enable变成disable有可能是由于电磁干扰造成的,这个if条件判断的是,端口被disable了,但是连接没有变化,并且hdev->children[i]还有值,这就说明明明有子设备连在端口上,可是端口却被disable了,基本上这种情况就是电磁干扰造成的,否则hub端口不会有这种抽风的举动.那么这种情况就设置connect_change为1.因为接下来我们会看到对于connect_change为1的情况,我们会专门进行处理,而更犀利更一针见血的说法就是,hub_events()其实最重要的任务就是对于端口连接有变化的情况进行处理,或者说进行响应.
再往下,portchange和USB_PORT_STAT_C_SUSPEND相与如果为1,表明连在该端口的设备的suspend状态有变化,并且是从suspended状态出来,也就是说resume完成.(别问我为什么,spec就这么规定的,没什么理由,一定要问理由那你问郑源去,他不是唱那什么如果你真的需要什么理由,一万个够不够吗.)那么首先我们就调用clear_port_feature清掉这个feature.接下来这个if牵扯到的东西比较高深,涉及到电源管理中很华丽的部分,我们只能先跳过.否则深陷其中难免会走火入魔欲罢不能.总之这里做的就是对于该端口连了子设备的情况就把子设备唤醒,否则如果端口没有连子设备,那么就把端口disable掉.
2754行,portchange如果和USB_PORT_STAT_C_OVERCURRENT相与结果为1的话,说明这个端口可能曾经存在电流过大的情况,而现在这种情况不存在了,或者本来不存在而现在存在了.对此我们能做的就是首先清除这个feature.有一种比较特别的情况是,如果其它的端口电流过大,那么将会导致本端口断电,即hub上一个端口出现over-current条件将有可能引起hub上其它端口陷入powered off的状态.不管怎么说,对于over-current的情况我们都把hub重新上电,执行hub_power_on().
2763行,portchange如果和USB_PORT_STAT_C_RESET相与为1的话,这叫做一个端口从Resetting状态进入到Enabled状态.
2771行,connect_change如果为1,就执行hub_port_connect_change(),啥也不说了,这是每一个看hub驱动的人最期待的函数,因为这正是我们的原始动机,即当一个usb设备插入usb接口之后究竟会发生什么,usb设备驱动程序提供那个probe函数究竟是如何被调用的.这些疑问统统会在这个函数里得到答案.来自drivers/usb/core/hub.c:
2404 /* Handle physical or logical connection change events.
2405 * This routine is called when:
2406 * a port connection-change occurs;
2407 * a port enable-change occurs (often caused by EMI);
2408 * usb_reset_device() encounters changed descriptors (as from
2409 * a firmware download)
2410 * caller already locked the hub
2411 */
2412 static void hub_port_connect_change(struct usb_hub *hub, int port1,
2413 u16 portstatus, u16 portchange)
2414 {
2415 struct usb_device *hdev = hub->hdev;
2416 struct device *hub_dev = hub->intfdev;
2417 u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
2418 int status, i;
2419
2420 dev_dbg (hub_dev,
2421 "port %d, status %04x, change %04x, %s\n",
2422 port1, portstatus, portchange, portspeed (portstatus));
2423
2424 if (hub->has_indicators) {
2425 set_port_led(hub, port1, HUB_LED_AUTO);
2426 hub->indicator[port1-1] = INDICATOR_AUTO;
2427 }
2428
2429 /* Disconnect any existing devices under this port */
2430 if (hdev->children[port1-1])
2431 usb_disconnect(&hdev->children[port1-1]);
2432 clear_bit(port1, hub->change_bits);
2433
2434 #ifdef CONFIG_USB_OTG
2435 /* during HNP, don't repeat the debounce */
2436 if (hdev->bus->is_b_host)
2437 portchange &= ~USB_PORT_STAT_C_CONNECTION;
2438 #endif
2439
2440 if (portchange & USB_PORT_STAT_C_CONNECTION) {
2441 status = hub_port_debounce(hub, port1);
2442 if (status < 0) {
2443 if (printk_ratelimit())
2444 dev_err (hub_dev, "connect-debounce failed, "
2445 "port %d disabled\n", port1);
2446 goto done;
2447 }
2448 portstatus = status;
2449 }
2450
2451 /* Return now if nothing is connected */
2452 if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
2453
2454 /* maybe switch power back on (e.g. root hub was reset) */
2455 if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
2456 && !(portstatus & (1 << USB_PORT_FEAT_POWER)))
2457 set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
2458
2459 if (portstatus & USB_PORT_STAT_ENABLE)
2460 goto done;
2461 return;
2462 }
2463
2464 #ifdef CONFIG_USB_SUSPEND
2465 /* If something is connected, but the port is suspended, wake it up. */
2466 if (portstatus & USB_PORT_STAT_SUSPEND) {
2467 status = hub_port_resume(hub, port1, NULL);
2468 if (status < 0) {
2469 dev_dbg(hub_dev,
2470 "can't clear suspend on port %d; %d\n",
2471 port1, status);
2472 goto done;
2473 }
2474 }
2475 #endif
2476
2477 for (i = 0; i < SET_CONFIG_TRIES; i++) {
2478 struct usb_device *udev;
2479
2480 /* reallocate for each attempt, since references
2481 * to the previous one can escape in various ways
2482 */
2483 udev = usb_alloc_dev(hdev, hdev->bus, port1);
2484 if (!udev) {
2485 dev_err (hub_dev,
2486 "couldn't allocate port %d usb_device\n",
2487 port1);
2488 goto done;
2489 }
2490
2491 usb_set_device_state(udev, USB_STATE_POWERED);
2492 udev->speed = USB_SPEED_UNKNOWN;
2493 udev->bus_mA = hub->mA_per_port;
2494 udev->level = hdev->level + 1;
2495
2496 /* set the address */
2497 choose_address(udev);
2498 if (udev->devnum <= 0) {
2499 status = -ENOTCONN; /* Don't retry */
2500 goto loop;
2501 }
2502
2503 /* reset and get descriptor */
2504 status = hub_port_init(hub, udev, port1, i);
2505 if (status < 0)
2506 goto loop;
2507
2508 /* consecutive bus-powered hubs aren't reliable; they can
2509 * violate the voltage drop budget. if the new child has
2510 * a "powered" LED, users should notice we didn't enable it
2511 * (without reading syslog), even without per-port LEDs
2512 * on the parent.
2513 */
2514 if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
2515 && udev->bus_mA <= 100) {
2516 u16 devstat;
2517
2518 status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
2519 &devstat);
2520 if (status < 2) {
2521 dev_dbg(&udev->dev, "get status %d ?\n", status);
2522 goto loop_disable;
2523 }
2524 le16_to_cpus(&devstat);
2525 if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
2526 dev_err(&udev->dev,
2527 "can't connect bus-powered hub "
2528 "to this port\n");
2529 if (hub->has_indicators) {
2530 hub->indicator[port1-1] =
2531 INDICATOR_AMBER_BLINK;
2532 schedule_delayed_work (&hub->leds, 0);
2533 }
2534 status = -ENOTCONN; /* Don't retry */
2535 goto loop_disable;
2536 }
2537 }
2538
2539 /* check for devices running slower than they could */
2540 if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
2541 && udev->speed == USB_SPEED_FULL
2542 && highspeed_hubs != 0)
2543 check_highspeed (hub, udev, port1);
2544
2545 /* Store the parent's children[] pointer. At this point
2546 * udev becomes globally accessible, although presumably
2547 * no one will look at it until hdev is unlocked.
2548 */
2549 status = 0;
2550
2551 /* We mustn't add new devices if the parent hub has
2552 * been disconnected; we would race with the
2553 * recursively_mark_NOTATTACHED() routine.
2554 */
2555 spin_lock_irq(&device_state_lock);
2556 if (hdev->state == USB_STATE_NOTATTACHED)
2557 status = -ENOTCONN;
2558 else
2559 hdev->children[port1-1] = udev;
2560 spin_unlock_irq(&device_state_lock);
2561
2562 /* Run it through the hoops (find a driver, etc) */
2563 if (!status) {
2564 status = usb_new_device(udev);
2565 if (status) {
2566 spin_lock_irq(&device_state_lock);
2567 hdev->children[port1-1] = NULL;
2568 spin_unlock_irq(&device_state_lock);
2569 }
2570 }
2571
2572 if (status)
2573 goto loop_disable;
2574
2575 status = hub_power_remaining(hub);
2576 if (status)
2577 dev_dbg(hub_dev, "%dmA power budget left\n", status);
2578
2579 return;
2580
2581 loop_disable:
2582 hub_port_disable(hub, port1, 1);
2583 loop:
2584 ep0_reinit(udev);
2585 release_address(udev);
2586 usb_put_dev(udev);
2587 if (status == -ENOTCONN)
2588 break;
2589 }
2590
2591 done:
2592 hub_port_disable(hub, port1, 1);
2593 }
到今天我算是看明白了,内核里面这些函数,没有最变态只有更变态,变态哪都有,可是开源社区尤其多!你们他妈的不是我的冤家派来故意玩我的吧?面对这个函数,我真的想吐血!我打算不像过去那样一行一行讲了,我必须先来个提纲挈领,必须先开门见山把这个函数的哲学思想讲清楚,否则一行一行往下讲肯定晕菜.