老实说,从函数一个开始的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 }

到今天我算是看明白了,内核里面这些函数,没有最变态只有更变态,变态哪都有,可是开源社区尤其多!你们他妈的不是我的冤家派来故意玩我的吧?面对这个函数,我真的想吐血!我打算不像过去那样一行一行讲了,我必须先来个提纲挈领,必须先开门见山把这个函数的哲学思想讲清楚,否则一行一行往下讲肯定晕菜.

Linux那些事儿之我是Hub(9)While You Were Sleeping(二)相关推荐

  1. Linux那些事儿之我是Hub(4)

    这一节我们讲队列. 从前在乡下的时候是不用排队的,村里的人们都很谦让,而且人本来又不多.后来到了县城里,县城里不大,大家去走亲戚去串门去逛街不用坐车不用排队,除了街上的游戏厅人多一点以外,别的地方人都 ...

  2. 【转】Linux那些事儿之我是Hub(7)蝴蝶效应

    朋友,你相信,一只蝴蝶在北京拍拍翅膀,将使得纽约几个月后出现比狂风还厉害的龙卷风吗?看过那部经典的影片蝴蝶效应的朋友们一定会说,这不就是蝴蝶效应吗.没错.蝴蝶效应其实是混沌学理论中的一个概念.它是指对 ...

  3. Linux那些事儿之我是Hub(1)跟我走吧,现在就出发

    最早知道hub是在大学里,复旦的4人间宿舍,条件真好,每个人一张书桌,书桌下面一个网口,但是有时候网口坏了,那可急死人了,要知道当初我们买电脑初衷虽说是为了学习C语言,可是买了之后,C倒是没学,先学了 ...

  4. Linux那些事儿之我是Hub(25)不说代码说理论

    当女作家们越来越多的使用下半身来告诉我什么是文学的时候,当模特们越来越多的使用裸体来告诉我什么是人体艺术的时候,我开始对这个社会困惑了,当行为艺术家们越来越多的使用垃圾堆砌来告诉我什么是波谱的时候,当 ...

  5. Linux那些事儿之我是Hub(2)

    莎士比亚曾经说过,不懂hub是怎么工作的就等于不知道usb设备驱动是怎么工作的.这句话一点没错,因为usb设备的初始化都是hub这边发起的,通常我们写usb设备驱动程序都是在已经得到了一个struct ...

  6. Linux那些事儿之我是Hub(19)八大重量级函数闪亮登场(三)

    在开始第三个函数前,2492行至2494行还有三行代码,对udev中的speed,bus_mA,level进行赋值. 先说一下,bus_mA,struct usb_device中的成员,unsigne ...

  7. Linux那些事儿之我是U盘(50)跟着感觉走(二)

    回到usb_stor_invoke_transport()中来,540行,还是老套路,又问是不是命令被放弃了,放弃了当然下面的就别执行了.goto Handle_Abort去. 546行,如果有错误, ...

  8. Linux设备之我是usb,linux那些事儿之我是usb

    linux那些事儿之我是usb,复旦大学教授肖林甫先生给学生们解说的linux操作系统的一些硬件驱动开发的事儿. 内核说明: 我是U盘 说的是2.6.10的内核 我是Sysfs 说的是2.6.10的内 ...

  9. Linux系统USB驱动目录,Linux那些事儿之我是USB 目录

    目录 第1篇  Linux那些事儿之我是USB Core 1.引子 2 2.它从哪里来 2 3.PK 3 4.漫漫辛酸路 3 5.我型我秀 4 6.我是一棵树 5 7.我是谁 9 8.好戏开始了 11 ...

最新文章

  1. 提高班第五周周记(国庆第二天)
  2. Iterator 和 ListIterator 有什么区别?
  3. 用JS写的取存款功能
  4. 信息学奥赛一本通 1179:奖学金 | 1938:【07NOIP普及组】奖学金 | OpenJudge NOI 1.10 04 | 洛谷 P1093 [NOIP2007 普及组] 奖学金
  5. sql中怎么根据汉字的拼音首字母查询
  6. (转)商城系统商品属性的数据库设计思路
  7. 45.分支算法练习:  7622:求排列的逆序数
  8. js语法、关键保留字、变量、数据类型
  9. 经常会用到的ocr文字识别工具:Text Scanner for Mac
  10. ML三(人工神经网络)
  11. 虚拟化实战——存储(二)
  12. [水晶报表]水晶报表的使用经验和资料总结
  13. PHP语言编程魔方,编程和魔方
  14. Android Studio:Type mismatch: inferred type is Int but Unit was expected
  15. html5相册制作成视频,怎么把照片制作成视频,视频相册制作免费软件|特效多多...
  16. IT十大名言 |IT历史上被引述最多的10句名人名言
  17. Windows下C++通过Hooks记录键盘敲击记录的代码
  18. Libra R-CNN理解
  19. 翻转棋c语言算法,有没有人懂黑白棋(翻转棋)的核心算法
  20. 西航计算机学院学生会,“智”行千里,逐梦远航丨西航职院人工智能学院召开2020年度总结表彰暨新年联欢会...

热门文章

  1. Altium Designer 手机设计之PCB布局 - 系列三
  2. Flurry analytics SDK集成步骤及功能简介
  3. 什么是SASO认证,SASO认证程序,SASO认证收费
  4. 空气能热水器的SASO认证
  5. Xshell Xftp国外网址下载 免破解 直接使用
  6. AVProVideo视频插件使用
  7. 虚拟现实中的眼动应用
  8. 《毛毛虫组》【Alpha】Scrum meeting 3
  9. 航空软件静态测试标准,静态试验
  10. 百家讲坛 雍正十三年(上部)