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

get_hub_descriptor()结束了,然后就返回hub_configure()中来.635642,判断刚才的返回值,小于零当然是出错了,大于零也还要多判断一次, 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还大,如果是,那么对不起,出错了.bHubContrCurrentHub控制器的最大电流需求,DeviceRemoveable是用来判断这个端口连接的设备是否是可以移除的,每一个bit代表一个端口,如果该bit0,则说明可以被移除,1,就说明不可以移除.wHubCharacteristics就相对来说麻烦一点了,它记录了很多信息,后面有相当一部分的代码都是在判断这个值,我们这里把hub spechub 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       */

结合上面这张图,意思很明显.650661,如果是复合设备,符合设备就是说这个设备它可能是几种设备绑在一起的,比如既可以当hub用又可以有别的功能,那么就用一个数组portstr[]来记录每一个端口的设备是否可以被移除.然后打印出调试信息来.不要说你看不懂,i0,1,2,3这些数代入进去就明白了.

663674, HUB_CHAR_LPSM, 表征电源切换的方式,不同的hub有不同的power switching的方式,ganged power switching指的是所有port一次性上电,individual port power switching当然就是各人自扫门前雪,哪管他人瓦上霜.usb 1.0hub压根儿就没有power switching这么一个说法.

676687, HUB_CHAR_OCPM,表征过流保护模式,其实也没啥,不明白也无所谓,这几行无非就是打印一些调试信息.

689691,先是初始化一个自旋锁,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.大多数hubsingle TT,因为一个hub一个TT就够了,国家资源那么紧张,何必铺张浪费.使用single TT就是支持国家反腐倡廉!

692行的switch, hdev->descriptor.bDeviceProtocol,别看走眼了,刚才咱们一直是判断hub->descriptor,而这里是hdev->descriptor,hdevstruct usb_device结构体指针,咱们一进入这个hub_configure()函数就赋了值的,其实就是和这个hub相关的那个struct usb_device指针.所以这里判断的描述符是标准的usb设备描述符,而其中bDeviceProtocol的含义在hub spec里有专门的规定.这一段就是为了设置tt,对照hub spec可知,full/low speedhubbDeviceProtocol0,这种hub就没有tt.所以直接break,啥也不用设.对于high speedhub,bDeviceProtocol1表示是single tt.2表示是multiple tt.对于case 2.这里调用了usb_set_interface,根据usb spec 2.0,11.23.1一节,对于full/low speedhub,device descriptor中的bDeviceProtocol0,interface descriptor中的bInterfaceProtocol也为0.而对于high speedhub,其中,single TThubdevice descriptor中的bDeviceProtocol1,interface descriptorbInterfaceProtocol则是0.然而,multiple TT hub另外还有一个interface descriptor以及相应的一个endpoint descriptor,它的device descriptorbDeviceProtocol必须设置成2.其第一个interface descriptorbInterfaceProtocol1.而第二个interface descriptorbInterfaceProtocol则是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 tthub.

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这个值我们就没用过.

746749, 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_STATUSUSB的标准请求之一,

这个请求的类型有三种,一种是获得Device的状态,一种是获得Interface的状态,一种是获得端点的状态,这里咱们传递的是USB_RECIP_DEVICE,也就是获得Device的状态.那么函数返回之后,Device的状态就被保存在了hubstatus里面了.

763788,就是一个if/elseif/else组合.首先if判断当年这个device是不是root hub,如果是root hub,然后判断hdev->bus_mA,这个值是在host controller的驱动程序中设置的,通常来讲,计算机的usb端口可以提供500mA的电流,不过host controller那边有一个成员power_budget,host controller的驱动程序中,root hubhdev->bus_mA被设置为500mApower_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_port500mA,mA_per_port就是提供给每一个port的电流.那么如果说bus_mA0500之间的某个数,那么说明这个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有四个口,maxchild4,那么你最起码你得剩下个400mA电流吧,因为如果某个端口电流小于100mA的话,设备是没法正常工作的.然后,782,警告归警告,最终还是设置mA_per_port100mA.

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自己定义的,其格式如下图所示:

最后状态保存在statuschange里面,hubstatushubchange里面.而函数hub_hub_status的返回值正常就是0,否则就是负的错误码.

799807行都是打印调试信息.飘过.

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->buffersize.

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值