首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux那些事儿之我是Hub(15)一个都不能少

Linux那些事儿之我是Hub(15)一个都不能少

烟波江声里,何处是江南.一晃神,一转眼,我们就这样垂垂老去.可是我们直到现在还根本就没有看明白hub驱动究竟是怎么工作的.但我相信,红莲即将绽放,双星终会汇聚,命运的轮转已经开始,我们只需耐心的等待.
2686,苦苦追寻之后,终于发现从这里开始针对端口进行分析了,有几个端口就对几个端口进行分析,分析每一个端口的状态变化,一个都不能少,很显然,这就是我们期待看到的代码,马上我们就可以知道,当我们把一个usb设备插入usb端口之后究竟会发生什么,知道usb设备提供的那些接口函数究竟是如何被调用的,特别是那个probe函数.这一刻,红领巾迎着太阳,阳光洒在海面上,水中鱼儿望着我们,悄悄的听我们愉快的歌唱,小船儿轻轻飘荡在水中,迎面吹来了凉爽的风!
bNbrports是前面我们获得的hub descriptor的一个成员,表征这个hub有几个端口.很显然,月可无光,星可无语,usb设备却不可以没有描述符.这里就是遍历每一个端口.busy_bitsstruct 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实际上只有在resetresume的函数内部才会设置,所以这里我们先不用管,而这里的意思是,如果眼下这个端口正在执行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(),portstatusportchange是我们在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 StatusHub的一个标准请求,对我们来说就是一次控制传输就可以搞定.这个请求的格式如下图所示:

其中这个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_msgrequest就是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()里面,14261427两行我们就得到了Status BitsStatus Change Bits.get_port_status()返回值就是GET PORT STATUS请求的返回数据的长度,它至少应该能够保存wPortStatuswPortChange,所以至少不能小于4,所以1420行有这么一个错误判断.这样,hub_port_status()就返回了,statuschange这两个指针也算是满载而归了,正常的话返回值就是0.
继续往下走,2699,children[i-1],这么一个冬冬我们从没有见过,但是我想白痴都知道,正是像parentchildren这样的指针才能把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.0spec里面对这些宏的意义说得很清楚,USB_PORT_STAT_CONNECTION的意思的确是表征是否有设备连接在这个端口上,我们不妨假设有,那么portstatus和它相与的结果就是1,usb spec里面,这一位叫做Current Connect Status,于是这里我们会看到connect_change被设置成了1.而接下来,USB_PORT_STAT_C_CONNECTION则是表征这个端口的Current Connect Status位是否有变化,如果有变化,那么portchangeUSB_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_FEATUREusb 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_change1.
继续,每个端口都有一个开关,这叫做enable或者disable一个端口.portchangeUSB_PORT_STAT_C_ENABLE相与如果为1的话,说明端口开关有变化.和刚才一样,首先我们要做的是,清除掉这个变化的feature.但是这里需要注意,spec里对这个feature是这样规定的,如果portchangeUSB_PORT_STAT_C_ENABLE1,说明这个port是从enable状态进入了disable状态.为什么呢?因为在spec规定了,Hub的端口是不可以直接设置成enable.通常让Hub端口enable的方法是reset hub port.spec的话说,这叫做发送另一个request,名为SET_FEATURE.SET_FEATURECLEAR_FEATURE是对应的,一个设置一个清除. 对于PORT_ENABLE这一位,spec里的话说,This bit may be set only as a result of a SetPortFeature(PORT_RESET) request,PORT_RESET是为hub定义的众多feature中的一种.最后提醒一点,27112715这段if语句仅仅是为了打印调试信息的,就是说如果port enable改变了,但是端口连接没有改变,那么打印出信息来通知调试者,不要把clear_port_feature这一行也纳入到if语句里去了.因为port enable的改变有多种可能,其中一种可能就是由于检测到了disconnection,但是对于这种情况,我们下面要处理的,所以甭急.
下面这段代码就比较帅了,电磁干扰都给扯进来了.EMI,就是电磁干扰.就是说有的时候hub portenable变成disable有可能是由于电磁干扰造成的,这个if条件判断的是,端口被disable,但是连接没有变化,并且hdev->children还有值,这就说明明明有子设备连在端口上,可是端口却被disable,基本上这种情况就是电磁干扰造成的,否则hub端口不会有这种抽风的举动.那么这种情况就设置connect_change1.因为接下来我们会看到对于connect_change1的情况,我们会专门进行处理,而更犀利更一针见血的说法就是,hub_events()其实最重要的任务就是对于端口连接有变化的情况进行处理,或者说进行响应.
再往下,portchangeUSB_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 }
到今天我算是看明白了,内核里面这些函数,没有最变态只有更变态,变态哪都有,可是开源社区尤其多!你们他妈的不是我的冤家派来故意玩我的吧?面对这个函数,我真的想吐血!我打算不像过去那样一行一行讲了,我必须先来个提纲挈领,必须先开门见山把这个函数的哲学思想讲清楚,否则一行一行往下讲肯定晕菜.
返回列表