原创 Linux那些事儿之我是Hub(10)While You Were Sleeping(三)

2010-7-13 07:01 2859 6 6 分类: MCU/ 嵌入式

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中断传输究竟是怎么进行的.


 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/fudan_abc/archive/2007/08/23/1756728.aspx

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
6
关闭 站长推荐上一条 /3 下一条