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

来源:互联网 发布:即时聊天软件 编辑:程序博客网 时间:2024/05/01 00:17

在开始第三个函数前,2492行至2494行还有三行代码,udev中的speed,bus_mA,level进行赋值.

先说一下,bus_mA,struct usb_device中的成员,unsigned short bus_mA,记录的是能够从总线上获得的电流,毫无疑问就是咱们前面算出来的hub上的那个mA_per_port.上头能给多少咱们就要多少.

再说level,级别,表征usb设备树的级连关系.Root Hub当然其level就是0,其下面一层就是level 1,再下面一层就是level 2,依此类推.

然后说speed,include/linux/usb/ch9.h中定义了这么一个枚举类型的变量:

    548 /* USB 2.0 defines three speeds, here's how Linux identifies them */

    549

    550 enum usb_device_speed {

    551         USB_SPEED_UNKNOWN = 0,                  /* enumerating */

    552         USB_SPEED_LOW, USB_SPEED_FULL,          /* usb 1.1 */

    553         USB_SPEED_HIGH,                         /* usb 2.0 */

    554         USB_SPEED_VARIABLE,                     /* wireless (usb 2.5) */

    555 };

很明显的含义,用来标志设备的速度.众所周知,USB设备有三种速度,低速,全速,高速.USB1.1那会儿只有低速,全速,后来才出现了高速,高速就是所谓的480Mbps/s,不过细心的你或许注意到这里还有一个USB_SPEED_VARIABLE.两千零五年那个五月,Intel等公司推出了Wireless USB spec 1.0,即所谓的无线USB技术,江湖上也把这个usb技术称为usb 2.5.无线技术的推出必然会让设备的速度不再稳定,当年这个标准推出的时候是号称在3米范围内,能够提供480Mbps的理论传输速度,而在10米范围左右出现递减,据说是10米内110Mbps/s.那时正值英特尔中国20周年,所以中国这边的员工每人发了一个无线USB鼠标.其实就是一个USB接头,接在电脑的usb端口上,而鼠标这边没有线,鼠标和接头之间的通信是无线的,使用传说中蓝牙技术.我的那个无线鼠标基本上四五米之外就不能用了.总之,这里的变量usb_device_speed就是用来表征设备速度的,现阶段还不知道这个设备究竟是什么速度的,所以先设置为UNKNOWN.等到知道了以后再进行真正的设置.

第三个函数,choose_address().

这个函数的目的就是为设备选择一个地址.很显然,要通信就要有地址,你要给人写情书表白,你首先得知道人家的通信地址,或者电子邮箱地址.

   1132 static void choose_address(struct usb_device *udev)

   1133 {

   1134         int             devnum;

   1135         struct usb_bus  *bus = udev->bus;

   1136

   1137         /* If khubd ever becomes multithreaded, this will need a lock */

   1138

   1139         /* Try to allocate the next devnum beginning at bus->devnum_next. */

   1140         devnum = find_next_zero_bit(bus->devmap.devicemap, 128,

   1141                         bus->devnum_next);

   1142         if (devnum >= 128)

   1143                 devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);

   1144

   1145         bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);

   1146

   1147         if (devnum < 128) {

   1148                 set_bit(devnum, bus->devmap.devicemap);

   1149                 udev->devnum = devnum;

   1150         }

   1151 }

那么现在是时候让我们来认识一下usb子系统里面关于地址的游戏规则了.在悠悠岁月中,一个神话就是浪花一朵,一滴苦酒就是史书一册,而在usb世界里,一条总线就是大树一棵,一个设备就是叶子一片.为了记录这棵树上的每一个叶子节点,每条总线设有一个地址映射表,struct usb_bus结构体里有一个成员struct usb_devmap devmap,

    268 /* USB device number allocation bitmap */

    269 struct usb_devmap {

    270         unsigned long devicemap[128 / (8*sizeof(unsigned long))];

271 };

同时struct usb_bus结构体里面还有一个成员,int devnum_next,在总线初始化的时候,devnum_next被设置为1,而在struct usb_device中有一个int devnum,咱们这个choose_address函数的基本思想就是一个轮询的算法.

我们来介绍一下这段代码背后的哲学.首先,bus上面不是有这么一张表嘛,假设unsigned long=4bytes,那么unsigned long devicemap[128/(8*sizeof(unsigned long)]]就等价于unsigned long devicemap[128/(8*4)],进而等价于unsigned long devicemap[4],4bytes就是32bits,因此这个数组最终表示的就是128bits.而这也对应于一条总线可以连接128usb设备.之所以这里使用sizeof(unsigned long),就是为了跨平台应用,不管unsigned long到底是几,总之这个devicemap数组最终可以表示128.

128bits,每当加入一个设备,就先找到下一位为0bit,然后把该bit设置为1,同时把struct usb_device中的devnum设置为该数字,比如我们找到第19位为0,那么就把devnum设置为19,同时把bit 19设置为1,struct usb_bus中的devnum_next就设置为20.

那么所谓轮询,即如果这个编号超过了128,那么就从1开始继续搜索,因为也许开始那段的号码原来分配给某个设备但后来这个设备撤掉了,所以这个号码将被设置为0,于是再次可用.

弄清楚了这些基本思想后,我们再来看代码就很简单了.这时候相信你可以自豪的和杨振宁先生一样,高呼:”我能!”当然,我不会像翁帆女士一样喊:”你不能,我能让你能!”

find_next_zero_bit()的意思很明显,名字解释了一切.不同的体系结构提供了自己不同的函数实现,比如i386,这个函数就定义于arch/i386/lib/bitops.c,x8664则对应于arch/x86_64/lib/bitops.c,利用这个函数我们就可以找到这128位中下一个为0的那一位.这个函数的第三个参数表征从哪里开始寻找,我们注意到第一次我们是从devnum_next开始找,如果最终返回值暴掉了(大于或者等于128),那么就从1开始再找一次.bus->devnum_next也是按我们说的那样设置,正常就是devnum+1,但如果devnum已经达到127,那么就从头再来,设置为1.

如果devnum正常,那么就把bus中的device map中的那一位设置为1.同时把udev->devnum设置为devnum.然后这个函数就可以返回了.如果128bits都被占用了,devnum就将是零或者负的错误码,于是choose_address返回之后我们就要进行判断,如果真的是满掉了,那么咱们有心杀贼无力回天,也就不用往下走了,不过你要真的能连满128个设备,那你也蛮狠的.不得不把陈小春的那首<<算你狠>>送给你!