Linux设备驱动之USB hub驱动(续)

Bertha 。 2022-09-19 00:29 452阅读 0赞

5.2.2:接口驱动中的hub_thread()函数
我们之前在分析usb_hub_init()的代码的时候,忽略掉了一部份.
代码片段如下所示:
int usb_hub_init(void)
{
……
khubd_task = kthread_run(hub_thread, NULL, “khubd”);
……
}
Kthread_run()是kernel中用来启动一个新kernel线程的接口,它所要执行的函数就是后面跟的第一个参数.在这里,也就是hub_thread().另外,顺带提一句,要终止kthread_run()创建的线程,可以调用kthread_stop().

Hub_thread()的代码如下:
static int hub_thread(void *__unused)
{
set_freezable();
do {
hub_events();
wait_event_freezable(khubd_wait,
!list_empty(&hub_event_list) ||
kthread_should_stop());
} while (!kthread_should_stop() || !list_empty(&hub_event_list));

  1. pr\_debug("%s: khubd exiting\\n", usbcore\_name);
  2. return 0;

}
在上面的代码中, kthread_should_stop()用来判断是否有kthread_stop()将其终止.
在这里,我们终止看到,我们在前面要唤醒的等待队列khubd_wait,也就是在这个地方了.
这个函数的核心处理是hub_events().分段分析代码,如下:
static void hub_events(void)
{
struct list_head *tmp;
struct usb_device *hdev;
struct usb_interface *intf;
struct usb_hub *hub;
struct device *hub_dev;
u16 hubstatus;
u16 hubchange;
u16 portstatus;
u16 portchange;
int i, ret;
int connect_change;

  1. /\*
  2. \* We restart the list every time to avoid a deadlock with
  3. \* deleting hubs downstream from this one. This should be
  4. \* safe since we delete the hub from the event list.
  5. \* Not the most efficient, but avoids deadlocks.
  6. \*/
  7. while (1) \{
  8. /\* Grab the first entry at the beginning of the list \*/
  9. //如果hub\_event\_list为空,退出
  10. spin\_lock\_irq(&hub\_event\_lock);
  11. if (list\_empty(&hub\_event\_list)) \{
  12. spin\_unlock\_irq(&hub\_event\_lock);
  13. break;
  14. \}
  15. //取hub\_event\_list中的后一个元素,并将其断链
  16. tmp = hub\_event\_list.next;
  17. list\_del\_init(tmp);
  18. hub = list\_entry(tmp, struct usb\_hub, event\_list);
  19. kref\_get(&hub->kref);
  20. spin\_unlock\_irq(&hub\_event\_lock);
  21. hdev = hub->hdev;
  22. hub\_dev = hub->intfdev;
  23. intf = to\_usb\_interface(hub\_dev);
  24. dev\_dbg(hub\_dev, "state %d ports %d chg %04x evt %04x\\n",
  25. hdev->state, hub->descriptor
  26. ? hub->descriptor->bNbrPorts
  27. : 0,
  28. /\* NOTE: expects max 15 ports... \*/
  29. (u16) hub->change\_bits\[0\],
  30. (u16) hub->event\_bits\[0\]);
  31. /\* Lock the device, then check to see if we were
  32. \* disconnected while waiting for the lock to succeed. \*/
  33. usb\_lock\_device(hdev);
  34. //如果hub断开了,继续hub\_event\_list中的下一个
  35. if (unlikely(hub->disconnected))
  36. goto loop;
  37. /\* If the hub has died, clean up after it \*/
  38. //设备没有连接上
  39. if (hdev->state == USB\_STATE\_NOTATTACHED) \{
  40. hub->error = -ENODEV;
  41. //将下面的子设备全部disable
  42. hub\_pre\_reset(intf);
  43. goto loop;
  44. \}
  45. /\* Autoresume \*/
  46. ret = usb\_autopm\_get\_interface(intf);
  47. if (ret) \{
  48. dev\_dbg(hub\_dev, "Can't autoresume: %d\\n", ret);
  49. goto loop;
  50. \}
  51. /\* If this is an inactive hub, do nothing \*/
  52. //hub 暂停
  53. if (hub->quiescing)
  54. goto loop\_autopm;
  55. //hub 有错误发生?
  56. if (hub->error) \{
  57. dev\_dbg (hub\_dev, "resetting for error %d\\n",
  58. hub->error);
  59. ret = usb\_reset\_composite\_device(hdev, intf);
  60. if (ret) \{
  61. dev\_dbg (hub\_dev,
  62. "error resetting hub: %d\\n", ret);
  63. goto loop\_autopm;
  64. \}
  65. hub->nerrors = 0;
  66. hub->error = 0;
  67. \}

首先,从hub_event_list摘下第一个元素,根据我们之前在接口驱动probe过程的kick_khubd()函数分析中,有将hub-> event_list添加到hub_event_list.因此,就可以顺藤摸瓜找到hub,再根据hub结构,找到接口结构和所属的usb 设备结构.
然后,进行第一个重要的判断.如果hub被断开了,则,断开hub下面所连接的所有端口,这是在hub_pre_reset()中完成的.
最后,进行第二个重要的判断,如果hub发生了错误,则reset它下面的所有端口,这是在usb_reset_composite_device()中完成的.

  1. /\* deal with port status changes \*/
  2. //遍历hub中的每一个port
  3. for (i = 1; i descriptor->bNbrPorts; i++) \{

{
if (test_bit(i, hub->busy_bits))
continue;
connect_change = test_bit(i, hub->change_bits);
if (!test_and_clear_bit(i, hub->event_bits) &&
!connect_change && !hub->activating)
continue;

  1. //Get\_Port\_Status:取得端口状态.
  2. //会取得port的改变值和状态值
  3. ret = hub\_port\_status(hub, i,
  4. &portstatus, &portchange);
  5. if (ret
  6. continue;
  7. //如果对应端口没有在设备树上,且端口显示已经连接上
  8. //将connect\_change置为1
  9. if (hub->activating && !hdev->children\[i-1\] &&
  10. (portstatus &
  11. USB\_PORT\_STAT\_CONNECTION))
  12. connect\_change = 1;
  13. //端口的连接状态发生了改变.需要发送Clear\_Feature
  14. if (portchange & USB\_PORT\_STAT\_C\_CONNECTION) \{
  15. clear\_port\_feature(hdev, i,
  16. USB\_PORT\_FEAT\_C\_CONNECTION);
  17. connect\_change = 1;
  18. \}
  19. //端口的状态从enable 变为了disable
  20. if (portchange & USB\_PORT\_STAT\_C\_ENABLE) \{
  21. if (!connect\_change)
  22. dev\_dbg (hub\_dev,
  23. "port %d enable change, "
  24. "status %08x\\n",
  25. i, portstatus);
  26. clear\_port\_feature(hdev, i,
  27. USB\_PORT\_FEAT\_C\_ENABLE);
  28. /\*
  29. \* EM interference sometimes causes badly
  30. \* shielded USB devices to be shutdown by
  31. \* the hub, this hack enables them again.
  32. \* Works at least with mouse driver.
  33. \*/
  34. //端口已经被停止了,且端口已经被连在设备树中.
  35. //需要重启一下此端口
  36. if (!(portstatus & USB\_PORT\_STAT\_ENABLE)
  37. && !connect\_change
  38. && hdev->children\[i-1\]) \{
  39. dev\_err (hub\_dev,
  40. "port %i "
  41. "disabled by hub (EMI?), "
  42. "re-enabling...\\n",
  43. i);
  44. connect\_change = 1;
  45. \}
  46. \}
  47. //Resume完成
  48. if (portchange & USB\_PORT\_STAT\_C\_SUSPEND) \{
  49. clear\_port\_feature(hdev, i,
  50. USB\_PORT\_FEAT\_C\_SUSPEND);
  51. //如果端口连接了设备,就将设备唤醒
  52. if (hdev->children\[i-1\]) \{
  53. ret = remote\_wakeup(hdev->
  54. children\[i-1\]);
  55. if (ret
  56. connect\_change = 1;
  57. \}
  58. //如果端口没有连接设备,就将端口禁用
  59. else \{
  60. ret = -ENODEV;
  61. hub\_port\_disable(hub, i, 1);
  62. \}
  63. dev\_dbg (hub\_dev,
  64. "resume on port %d, status %d\\n",
  65. i, ret);
  66. \}
  67. //有过流保护,需要对hub power on
  68. if (portchange & USB\_PORT\_STAT\_C\_OVERCURRENT) \{
  69. dev\_err (hub\_dev,
  70. "over-current change on port %d\\n",
  71. i);
  72. clear\_port\_feature(hdev, i,
  73. USB\_PORT\_FEAT\_C\_OVER\_CURRENT);
  74. hub\_power\_on(hub);
  75. \}
  76. //Reset状态已经完成了
  77. if (portchange & USB\_PORT\_STAT\_C\_RESET) \{
  78. dev\_dbg (hub\_dev,
  79. "reset change on port %d\\n",
  80. i);
  81. clear\_port\_feature(hdev, i,
  82. USB\_PORT\_FEAT\_C\_RESET);
  83. \}
  84. if (connect\_change)
  85. hub\_port\_connect\_change(hub, i,
  86. portstatus, portchange);
  87. \}

这段代码就是最核心的操作了,首先要说明的是,在struct usb_dev中,有一个struct usb_device *children[USB_MAXCHILDREN]的成员,它是表示对应端口序号上所连接的usb设备.
在这里,它遍历hub上的每一个端口,如果端口的连接会生了改变(connect_change等于1)的情况,就会调用hub_port_connect_change().我们来看一下,什么情况下, hub_port_connect_change才会被设为1.
1:端口在hub->change_bits中被置位.搜索整个代码树,发生在设置hub->change_bits的地方,只有在hub_port_logical_disconnect()中手动将端口禁用,会将对应位置1.
2:hub上没有这个设备树上没有这个端口上的设备.但显示端口已经连上了设备
3:hub这个端口上的连接发生了改变,从端口有设备连接变为无设备连接,或者从无设备连接变为有设备连接.
4:hub的端口变为了disable,此时这个端口上连接了设备,但被显示该端口已经变禁用,需要将connect_change设为1.
5:端口状态从SUSPEND变成了RESUME,远程唤醒端口上的设备失败,就需要将connect_change设为1.
另外hub_port_connect_change()函数我们放在后面再来讨论

  1. //对HUB的处理
  2. /\* deal with hub status changes \*/
  3. //如果hub状态末变化,不需要做任何处理
  4. if (test\_and\_clear\_bit(0, hub->event\_bits) == 0)
  5. ; /\* do nothing \*/
  6. //Get\_hub\_status 失败?
  7. else if (hub\_hub\_status(hub, &hubstatus, &hubchange)
  8. dev\_err (hub\_dev, "get\_hub\_status failed\\n");
  9. else \{
  10. //这里是对应hub 状态发生了改变,且Get\_hub\_status正常返回的情况
  11. //如果hub的本地电源供电发生了改变
  12. if (hubchange & HUB\_CHANGE\_LOCAL\_POWER) \{
  13. dev\_dbg (hub\_dev, "power change\\n");
  14. clear\_hub\_feature(hdev, C\_HUB\_LOCAL\_POWER);
  15. //如果是本地电源供电
  16. if (hubstatus & HUB\_STATUS\_LOCAL\_POWER)
  17. /\* FIXME: Is this always true? \*/
  18. hub->limited\_power = 1;
  19. //如果本电源不供电
  20. else
  21. hub->limited\_power = 0;
  22. \}
  23. //如果hub 发生过电源保护,需要对hub power on
  24. if (hubchange & HUB\_CHANGE\_OVERCURRENT) \{
  25. dev\_dbg (hub\_dev, "overcurrent change\\n");
  26. msleep(500); /\* Cool down \*/
  27. clear\_hub\_feature(hdev, C\_HUB\_OVER\_CURRENT);
  28. hub\_power\_on(hub);
  29. \}
  30. \}
  31. hub->activating = 0;
  32. /\* If this is a root hub, tell the HCD it's okay to
  33. \* re-enable port-change interrupts now. \*/
  34. if (!hdev->parent && !hub->busy\_bits\[0\])
  35. usb\_enable\_root\_hub\_irq(hdev->bus);

loop_autopm:
/* Allow autosuspend if we’re not going to run again */
if (list_empty(&hub->event_list))
usb_autopm_enable(intf);
loop:
usb_unlock_device(hdev);
kref_put(&hub->kref, hub_release);

  1. \} /\* end while (1) \*/

}
处理完hub上的port之后,就要来处理hub本身的状态改变了,结合代码中的注释应该很容易看懂,在这里主要是清除hub的对应Feature.
之后,将 hub->activating设为了0,如果hub是root hub,需要重新打开root hub的中断.
这个函数到这里就完成了.不过,其中的几个子函数,涉及到的操作很重要,现分析如下:
1: hub_pre_reset()函数.
该函数在设备断开连接的时候,将其下挂载的所有子设备全部注销掉,代码如下所示:
static int hub_pre_reset(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
struct usb_device *hdev = hub->hdev;
int i;

  1. /\* Disconnect all the children \*/
  2. for (i = 0; i maxchild; ++i) \{
  3. if (hdev->children*)
  4. usb\_disconnect(&hdev->children);
  5. \}
  6. hub\_quiesce(hub);
  7. return 0;

}
它将设备上所挂载的所有设备全部都调用usb_disconnect()来断开联接.之后,再对hub调用hub_quiesce().
hub_quiesce()是和hub_activate()相对应的一个函数, hub_activate()在前面已经分析过了,现在来对hub_quiesce()进行分析.
代码如下:
static void hub_quiesce(struct usb_hub \
hub)
{
/* (nonblocking) khubd and related activity won’t re-trigger */
hub->quiescing = 1;
hub->activating = 0;

  1. /\* (blocking) stop khubd and related activity \*/
  2. usb\_kill\_urb(hub->urb);
  3. if (hub->has\_indicators)
  4. cancel\_delayed\_work\_sync(&hub->leds);
  5. if (hub->tt.hub)
  6. cancel\_work\_sync(&hub->tt.kevent);

}
首先,它调hub->quiescing置为1,而activating置为0.这和hub_activate()刚好是相反的动作.之后,取消hub的中断传输出URB.取得TT和LED的工作队列.
我们在后面分析的HUB中断URB传输,可以知道,如果将这个URB禁用,那么,就不会将hub->event_list添加到hub_event_list.因此,也不会进入到hub_events()函数.

usb_disconnect()用来断开某个设备,代码如下:
void usb_disconnect(struct usb_device **pdev)
{
struct usb_device *udev = *pdev;
int i;

  1. if (!udev) \{
  2. pr\_debug ("%s nodev\\n", \_\_FUNCTION\_\_);
  3. return;
  4. \}
  5. /\* mark the device as inactive, so any further urb submissions for
  6. \* this device (and any of its children) will fail immediately.
  7. \* this quiesces everyting except pending urbs.
  8. \*/
  9. usb\_set\_device\_state(udev, USB\_STATE\_NOTATTACHED);
  10. dev\_info (&udev->dev, "USB disconnect, address %d\\n", udev->devnum);
  11. usb\_lock\_device(udev);
  12. /\* Free up all the children before we remove this device \*/
  13. for (i = 0; i
  14. if (udev->children)
  15. usb\_disconnect(&udev->children);
  16. \}
  17. /\* deallocate hcd/hardware state ... nuking all pending urbs and
  18. \* cleaning up all state associated with the current configuration
  19. \* so that the hardware is now fully quiesced.
  20. \*/
  21. dev\_dbg (&udev->dev, "unregistering device\\n");
  22. usb\_disable\_device(udev, 0);
  23. usb\_unlock\_device(udev);
  24. /\* Unregister the device. The device driver is responsible
  25. \* for removing the device files from usbfs and sysfs and for
  26. \* de-configuring the device.
  27. \*/
  28. device\_del(&udev->dev);
  29. /\* Free the device number and delete the parent's children\[\]
  30. \* (or root\_hub) pointer.
  31. \*/
  32. release\_address(udev);
  33. /\* Avoid races with recursively\_mark\_NOTATTACHED() \*/
  34. spin\_lock\_irq(&device\_state\_lock);
  35. \*pdev = NULL;
  36. spin\_unlock\_irq(&device\_state\_lock);
  37. usb\_stop\_pm(udev);
  38. put\_device(&udev->dev);

}
很容易看出.这个函数采用深度遍历算法,它依次遍历udev->children[]下的子设备,然后依然调用usb_disconnect().
这个函数中的另外几个子函数有的在前面已经分析过,有的是设备模型中的基础函数.很有是跟PM相关的,在这里就不做详细分析,来看一下release_address()函数,顾名思意,它用来释放设备的地址,如下示:
static void release_address(struct usb_device *udev)
{
if (udev->devnum > 0) {
clear_bit(udev->devnum, udev->bus->devmap.devicemap);
udev->devnum = -1;
}
}
我们在分析UHCI中,有关root hub的初始化时说明,设各号都是保存在bus->devmap数组中的.在这里,只需要将该设备号在数组中的某位清了即可.
hub_pre_reset()函数就分析到这里了.
注意到这里调用的put_device(&udev->dev)没.根据Linux设备模型的分析,这时它会调用跟它绑定的driver的remove()接口,对应的,这个函数会将操作回溯到usb_driver-> disconnect().可以自行查阅这个过程.
或许,有人的疑问又来了?要是这个usb_dev没有跟usb_driver绑定怎么办呢?
不要忘记我们之前的分析了,对于usb_generic_driver这个驱动是会适用所有的usb_dev的.^_^,也是说,无论如何,usb_dev都会绑定到usb_generic_driver.

2: hub_port_connect_change()函数
这个函数是一个很核心的操作,它的代码如下:
static void hub_port_connect_change(struct usb_hub *hub, int port1,
u16 portstatus, u16 portchange)
{
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
int status, i;

  1. dev\_dbg (hub\_dev,
  2. "port %d, status %04x, change %04x, %s\\n",
  3. port1, portstatus, portchange, portspeed (portstatus));
  4. //HUB LED
  5. if (hub->has\_indicators) \{
  6. set\_port\_led(hub, port1, HUB\_LED\_AUTO);
  7. hub->indicator\[port1-1\] = INDICATOR\_AUTO;
  8. \}
  9. /\* Disconnect any existing devices under this port \*/
  10. //如果对应端口已经有设备连接,先将其断开
  11. if (hdev->children\[port1-1\])
  12. usb\_disconnect(&hdev->children\[port1-1\]);
  13. //将hub\_change\_bits中的对应位清零,以免下次进来的时候,还会检测到
  14. //hub\_port\_logical\_disconnect()对该值的设置
  15. clear\_bit(port1, hub->change\_bits);

#ifdef CONFIG_USB_OTG
/* during HNP, don’t repeat the debounce */
if (hdev->bus->is_b_host)
portchange &= ~USB_PORT_STAT_C_CONNECTION;
#endif

  1. //连接发生改变
  2. //连接反弹的处理,实际上就是除抖动
  3. if (portchange & USB\_PORT\_STAT\_C\_CONNECTION) \{
  4. status = hub\_port\_debounce(hub, port1);
  5. if (status
  6. if (printk\_ratelimit())
  7. dev\_err (hub\_dev, "connect-debounce failed, "
  8. "port %d disabled\\n", port1);
  9. goto done;
  10. \}
  11. portstatus = status;
  12. \}

在这里,我们忽略掉HUB LED灯的操作,然后,将HUB对应端口下面挂载的设备断开.经过前面的分析,进入到这个函数的可能有多种情况(在hub_events()中分析的五种情况).可以分为三大类:
一类是之前有连接之后没联接的,在这里,将hub 对应端口下的设备全部断开是无可非议的.
第二类是之前没有,之后有连接的,在这里,if(hdev->children[port-1])的判断是不会满足的.
第三类是需要重置的端口,在这里先将设备断开,然后再将它联连上去好了.

接下来,将hub->change_bits的对应位清掉,该位是在函数hub_port_logical_disconnect()中被置的,在这里将其清除,免得下次在进入hub_events()的时候,再次检测到这个位发生改变.

忽略掉CONFIG_USB_OTG的处理,这个宏我们在前面分析过很多次了,这里不再赘述.
如果该端口的连接发生改变(从有连接到无接接,或者从无连接到有连接),就有一个除抖动的过程,usb2.0 spec上规定,除抖动的时间为100ms.
也许有人会有这样的想法: 那检测到移除了一个设备,但它在100ms又插上去了,这里适不适合这里的抖动检测的情况呢?
我们先从代码的流程看,检测到连接发生改变,进入到hub_port_connect_change(),它首先就会将端口上的设备移除.这样,就算你在100ms上连接上去了,也得要再次建立.
从usb2.0的协议看来,设备移除后,usb设备里保存的信息(例如选择的配置,给它分配的地址)全部都丢失了,必须要重新进行配置过程才能够使用.
在这里,顺便将hub_port_debounce()列出,来看一下具体的除抖过程是怎么样实现的.
static int hub_port_debounce(struct usb_hub *hub, int port1)
{
int ret;
int total_time, stable_time = 0;
u16 portchange, portstatus;
unsigned connection = 0xffff;

  1. for (total\_time = 0; ; total\_time += HUB\_DEBOUNCE\_STEP) \{
  2. ret = hub\_port\_status(hub, port1, &portstatus, &portchange);
  3. if (ret
  4. return ret;
  5. if (!(portchange & USB\_PORT\_STAT\_C\_CONNECTION) &&
  6. (portstatus & USB\_PORT\_STAT\_CONNECTION) == connection) \{
  7. stable\_time += HUB\_DEBOUNCE\_STEP;
  8. if (stable\_time >= HUB\_DEBOUNCE\_STABLE)
  9. break;
  10. \} else \{
  11. stable\_time = 0;
  12. connection = portstatus & USB\_PORT\_STAT\_CONNECTION;
  13. \}
  14. if (portchange & USB\_PORT\_STAT\_C\_CONNECTION) \{
  15. clear\_port\_feature(hub->hdev, port1,
  16. USB\_PORT\_FEAT\_C\_CONNECTION);
  17. \}
  18. if (total\_time >= HUB\_DEBOUNCE\_TIMEOUT)
  19. break;
  20. msleep(HUB\_DEBOUNCE\_STEP);
  21. \}
  22. dev\_dbg (hub->intfdev,
  23. "debounce: port %d: total %dms stable %dms status 0x%x\\n",
  24. port1, total\_time, stable\_time, portstatus);
  25. if (stable\_time
  26. return -ETIMEDOUT;
  27. return portstatus;

}
函数中的stable_time表示隐定的时间.在hub_events()的代码分析时,我们看到了,在检测到连接状态发生改变的时候,会发送Clear_Feature.因此,如果在这里检测到有USB_PORT_STAT_C_CONNECTION,就说明之后又有一次连接状态发生改变了.
分析这个函数的时候,要注意有这样的情况,端口的连接状态,一直在波动,即时有连接,时末有连接.
还有注意, connection的初始值是0xffff, 所以(portstatus & USB_PORT_STAT_CONNECTION) == connection这个判断是肯定不会满足的,因为hub_port_status()取得的portstatus里面还有一些保留位.所以,在第一次进入这个循环的时候,就会进入到else中,就会将stable_time置0,而connection也保存了这一次的连接信息.
如果端口维持前一个状态,那循环中的流程就会满足第一个if,在这个if的操作里,会增加stable_time的值.
如果端口的状态发生了改变,那循环中的流程就会满足else,又将stable_time和connection初始化了.另外,要记得在状态发生改变的时候,要发送Clear_Feature,将状态清除.
在函数里,定义的测试时间是1500ms.如果在这个时间内,端口还末处于稳定状态,就会返回-ETIMEDOUT.
如果已经处于稳定状态了,就会返回稳定状态下的portstatus.

/* Return now if nothing is connected */
//如果接口上没有连接了,可以直接退出了
if (!(portstatus & USB_PORT_STAT_CONNECTION)) {

  1. /\* maybe switch power back on (e.g. root hub was reset) \*/
  2. if ((wHubCharacteristics & HUB\_CHAR\_LPSM)
  3. && !(portstatus & (1
  4. set\_port\_feature(hdev, port1, USB\_PORT\_FEAT\_POWER);
  5. if (portstatus & USB\_PORT\_STAT\_ENABLE)
  6. goto done;
  7. return;
  8. \}

经过去抖后,端口稳定的处于断开连接状态.说明端口已经没有设备了.然后,再判断hub是否有电源开关((wHubCharacteristics & HUB_CHAR_LPSM)
如果端口依然处理enable状态,就会跳转到标号done处,就端口disalbe.

  1. //如果接口上面有了联接,需要为联接在端口上设备建立连接
  2. for (i = 0; i
  3. struct usb\_device \*udev;
  4. /\* reallocate for each attempt, since references
  5. \* to the previous one can escape in various ways
  6. \*/
  7. udev = usb\_alloc\_dev(hdev, hdev->bus, port1);
  8. if (!udev) \{
  9. dev\_err (hub\_dev,
  10. "couldn't allocate port %d usb\_device\\n",
  11. port1);
  12. goto done;
  13. \}
  14. usb\_set\_device\_state(udev, USB\_STATE\_POWERED);
  15. udev->speed = USB\_SPEED\_UNKNOWN;
  16. udev->bus\_mA = hub->mA\_per\_port;
  17. udev->level = hdev->level + 1;
  18. /\* set the address \*/
  19. choose\_address(udev);
  20. if (udev->devnum
  21. status = -ENOTCONN; /\* Don't retry \*/
  22. goto loop;
  23. \}
  24. /\* reset and get descriptor \*/
  25. status = hub\_port\_init(hub, udev, port1, i);
  26. if (status
  27. goto loop;
  28. /\* consecutive bus-powered hubs aren't reliable; they can
  29. \* violate the voltage drop budget. if the new child has
  30. \* a "powered" LED, users should notice we didn't enable it
  31. \* (without reading syslog), even without per-port LEDs
  32. \* on the parent.
  33. \*/
  34. if (udev->descriptor.bDeviceClass == USB\_CLASS\_HUB
  35. && udev->bus\_mA
  36. u16 devstat;
  37. status = usb\_get\_status(udev, USB\_RECIP\_DEVICE, 0,
  38. &devstat);
  39. if (status
  40. dev\_dbg(&udev->dev, "get status %d ?\\n", status);
  41. goto loop\_disable;
  42. \}
  43. le16\_to\_cpus(&devstat);
  44. if ((devstat & (1
  45. dev\_err(&udev->dev,
  46. "can't connect bus-powered hub "
  47. "to this port\\n");
  48. if (hub->has\_indicators) \{
  49. hub->indicator\[port1-1\] =
  50. INDICATOR\_AMBER\_BLINK;
  51. schedule\_delayed\_work (&hub->leds, 0);
  52. \}
  53. status = -ENOTCONN; /\* Don't retry \*/
  54. goto loop\_disable;
  55. \}
  56. \}
  57. /\* check for devices running slower than they could \*/
  58. if (le16\_to\_cpu(udev->descriptor.bcdUSB) >= 0x0200
  59. && udev->speed == USB\_SPEED\_FULL
  60. && highspeed\_hubs != 0)
  61. check\_highspeed (hub, udev, port1);
  62. /\* Store the parent's children\[\] pointer. At this point
  63. \* udev becomes globally accessible, although presumably
  64. \* no one will look at it until hdev is unlocked.
  65. \*/
  66. status = 0;
  67. /\* We mustn't add new devices if the parent hub has
  68. \* been disconnected; we would race with the
  69. \* recursively\_mark\_NOTATTACHED() routine.
  70. \*/
  71. spin\_lock\_irq(&device\_state\_lock);
  72. if (hdev->state == USB\_STATE\_NOTATTACHED)
  73. status = -ENOTCONN;
  74. else
  75. hdev->children\[port1-1\] = udev;
  76. spin\_unlock\_irq(&device\_state\_lock);
  77. /\* Run it through the hoops (find a driver, etc) \*/
  78. if (!status) \{
  79. status = usb\_new\_device(udev);
  80. if (status) \{
  81. spin\_lock\_irq(&device\_state\_lock);
  82. hdev->children\[port1-1\] = NULL;
  83. spin\_unlock\_irq(&device\_state\_lock);
  84. \}
  85. \}
  86. if (status)
  87. goto loop\_disable;
  88. status = hub\_power\_remaining(hub);
  89. if (status)
  90. dev\_dbg(hub\_dev, "%dmA power budget left\\n", status);
  91. return;

loop_disable:
hub_port_disable(hub, port1, 1);
loop:
ep0_reinit(udev);
release_address(udev);
usb_put_dev(udev);
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
break;
}
如果端口隐定处于连接状态,那就需要连接端口下的设备了.首先看到的是一个for循环,是用来配置设备的两种方式.我们知道,在配置设备的时候,首先要去取设备的描述符,这个过程是在ep0上完成的.而这个ep0支持的最大传输出数据又是在设备描述符的bMaxPacketSize0中所定义的.
因此就对应有两种处理方式:
第一种是传输8个字节,取得描述符的前面一部份,从而就可以取得bMaxPacketSize0.此后再reset设备,再根据这个bMaxPacketSize0的长度去取它的设备描述符.
第二种是一次传输64字节,取得设备描述符的bMaxPacketSize0字段
关于这两种方式的描述,详见fudan_abc的>.
有关这个for循环的作用就解释到这里.
在这段代码里,它首先分配一个usb_dev的结构,然后将其置为USB_STATE_POWERED状态.接着,为设备指定一个地址.
然后就调用hub_port_init()对这个usb_dev结构进行一系的初始化,在这个函数中会处理:Get_Description,Set_address.等操作,这个函数接下来我们再详细分析.
接着,将分配的struct usb_dev结构跟他的父结构关联起来,也就是说添加到它的父结构的usb_dev-> children[]数组.
最后再调用usb_new_device()来取这个设备的配置项.这个函数我们在分析UHCI的时候已经分析过了.
中间是关于一些电流的判断处理,这部份比较简单,自行查看就可以看懂,这里不再分析.
注意,这里在分配usb_dev结构的时候,跟root hub是不相同的,如下示:
udev = usb_alloc_dev(hdev, hdev->bus, port1)
在为root hub分配struct usb_dev的时候,它的第一个参数,也就是它的父结点是为NULL.
我们来观察一下它在sysfs中的命名方式:
如下所示:
在没有插入U盘之前:
[root@localhost devices]# pwd
/sys/bus/usb/devices
[root@localhost devices]# ls
1-0:1.0 usb1
[root@localhost devices]#
插入U盘之后:
[root@localhost devices]# ls
1-0:1.0 1-1 1-1:1.0 usb1
增加的两个目是:
1-1和1-1:1.0
表示,U盘对应的设备目录是1-1.结合之前UHCI分析中,对usb_alloc_dev()应该很容易理解.
1-1:1.0 :只有这样的目录,表示该U盘只有一个接口,当前选取的是第0号设置项.

done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
hcd->driver->relinquish_port(hcd, port1);
}
Done标号是对应上述处理失败的处理,它禁用掉该端口(因为该端口没有连接设备或者是端口上的设备配置失败),如果是root hub,且USB控制器器驱动中又定义了relinquish_port.调用它.

照例,还是分析一下这个函数中涉及到的重要的子函数.
第一个要分析的函数是choose_address()
该函数用来为设备选择一个地址,代码如下所示:
static void choose_address(struct usb_device *udev)
{
int devnum;
struct usb_bus *bus = udev->bus;

  1. /\* If khubd ever becomes multithreaded, this will need a lock \*/
  2. /\* Try to allocate the next devnum beginning at bus->devnum\_next. \*/

//从bus->devnum_next开始找到一个末被使用的位
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
bus->devnum_next);
//如果搜索到了最末尾,(128是不能被占用的),则从1起开始搜索
if (devnum >= 128)
devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
//更新bus->devnum_next
bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
//如果找到了合适位,将该位设为占用,然后更新udev->devnum为找到的设备号
if (devnum
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
}
}
这个函数的原理我们在之前说过了多次,它是到所属的usb bus的bus->devmap中找到没有使用的那一位.在这里设置bus->devnum_next项是一个搜索的优化,它不必每次都从第1位起开始搜索.最后将找到的值存放在udev->devnum中.

第二个要分析的函数是hub_port_disable().
这个函数将hub对应的端口禁用,代码如下:
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
{
struct usb_device *hdev = hub->hdev;
int ret = 0;
//将接在该端口下的设备设为末连接
if (hdev->children[port1-1] && set_state)
usb_set_device_state(hdev->children[port1-1],
USB_STATE_NOTATTACHED);
//发送enable 的Clear_Feature请求.
if (!hub->error)
ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
if (ret)
dev_err(hub->intfdev, “cannot disable port %d (err = %d)\n”,
port1, ret);
return ret;
}
该函数的逻辑很简单,就是该端点下的联接设备断开,如果端口有设备连接的话.然后清除端口的enable.

第三个要分析的函数是hub_port_init().
将它列到最后,并不是因为它最轻微,而是因为它太复杂.^_^
代码分段分析如下:
static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int retry_counter)
{
static DEFINE_MUTEX(usb_address0_mutex);

  1. struct usb\_device \*hdev = hub->hdev;
  2. int i, j, retval;
  3. unsigned delay = HUB\_SHORT\_RESET\_TIME;
  4. enum usb\_device\_speed oldspeed = udev->speed;
  5. char \*speed, \*type;
  6. int devnum = udev->devnum;
  7. /\* root hub ports have a slightly longer reset period
  8. \* (from USB 2.0 spec, section 7.1.7.5)
  9. \*/
  10. //设置port 的重置等待时间
  11. if (!hdev->parent) \{
  12. delay = HUB\_ROOT\_RESET\_TIME;
  13. if (port1 == hdev->bus->otg\_port)
  14. hdev->bus->b\_hnp\_enable = 0;
  15. \}
  16. /\* Some low speed devices have problems with the quick delay, so \*/
  17. /\* be a bit pessimistic with those devices. RHbug \#23670 \*/
  18. if (oldspeed == USB\_SPEED\_LOW)
  19. delay = HUB\_LONG\_RESET\_TIME;
  20. mutex\_lock(&usb\_address0\_mutex);
  21. /\* Reset the device; full speed may morph to high speed \*/
  22. //将port reset
  23. retval = hub\_port\_reset(hub, port1, udev, delay);
  24. if (retval
  25. goto fail;
  26. /\* success, speed is known \*/
  27. retval = -ENODEV;
  28. //在设备之前的设速已经确定的情况下
  29. //如果设备的速度发生了改变,肯定是发生了错误
  30. if (oldspeed != USB\_SPEED\_UNKNOWN && oldspeed != udev->speed) \{
  31. dev\_dbg(&udev->dev, "device reset changed speed!\\n");
  32. goto fail;
  33. \}
  34. oldspeed = udev->speed;

首先为端口重置选择一个合适的延时,即在这个延时过后,端口的Reset应该完成了.usb2.0 spec上规定,root hub的延时值是50ms,高速设备是10ms,而低速设备是100ms.从代码上看,这个延时都是从udev参数中来的,这个参数就是表示在端口上连接的设备.其实,所谓的Reset端口,就是Reset端口上连接的设备.
由于我们现在要对这个设备进行配置,因此,先将它复原成初始值.
另外,如果重置之后,设备的speed发生了变化,这肯定是错误的.

  1. /\* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
  2. \* it's fixed size except for full speed devices.
  3. \* For Wireless USB devices, ep0 max packet is always 512 (tho
  4. \* reported as 0xff in the device descriptor). WUSB1.0\[4.8.1\].
  5. \*/
  6. switch (udev->speed) \{
  7. case USB\_SPEED\_VARIABLE: /\* fixed at 512 \*/
  8. udev->ep0.desc.wMaxPacketSize = \_\_constant\_cpu\_to\_le16(512);
  9. break;
  10. case USB\_SPEED\_HIGH: /\* fixed at 64 \*/
  11. udev->ep0.desc.wMaxPacketSize = \_\_constant\_cpu\_to\_le16(64);
  12. break;
  13. case USB\_SPEED\_FULL: /\* 8, 16, 32, or 64 \*/
  14. /\* to determine the ep0 maxpacket size, try to read
  15. \* the device descriptor to get bMaxPacketSize0 and
  16. \* then correct our initial guess.
  17. \*/
  18. udev->ep0.desc.wMaxPacketSize = \_\_constant\_cpu\_to\_le16(64);
  19. break;
  20. case USB\_SPEED\_LOW: /\* fixed at 8 \*/
  21. udev->ep0.desc.wMaxPacketSize = \_\_constant\_cpu\_to\_le16(8);
  22. break;
  23. default:
  24. goto fail;
  25. \}

根据设备的speed来设定ep0的MaxPacketSize.这个只是spec上规定的值.另外对于Full Speed的设来说,它的MaxPacketSize有四种情况,即8.16.32和64实际的值要在设备描述符的bMaxPacketSize0字段才能知道.

  1. type = "";
  2. switch (udev->speed) \{
  3. case USB\_SPEED\_LOW: speed = "low"; break;
  4. case USB\_SPEED\_FULL: speed = "full"; break;
  5. case USB\_SPEED\_HIGH: speed = "high"; break;
  6. case USB\_SPEED\_VARIABLE:
  7. speed = "variable";
  8. type = "Wireless ";
  9. break;
  10. default: speed = "?"; break;
  11. \}
  12. dev\_info (&udev->dev,
  13. "%s %s speed %sUSB device using %s and address %d\\n",
  14. (udev->config) ? "reset" : "new", speed, type,
  15. udev->bus->controller->driver->name, devnum);

这段代码无关紧要,只是打印出了一个Debug信息,

  1. /\* Set up TT records, if needed \*/
  2. if (hdev->tt) \{
  3. udev->tt = hdev->tt;
  4. udev->ttport = hdev->ttport;
  5. \} else if (udev->speed != USB\_SPEED\_HIGH
  6. && hdev->speed == USB\_SPEED\_HIGH) \{
  7. udev->tt = &hub->tt;
  8. udev->ttport = port1;
  9. \}
  10. /\* Why interleave GET\_DESCRIPTOR and SET\_ADDRESS this way?
  11. \* Because device hardware and firmware is sometimes buggy in
  12. \* this area, and this is how Linux has done it for ages.
  13. \* Change it cautiously.
  14. \*
  15. \* NOTE: If USE\_NEW\_SCHEME() is true we will start by issuing
  16. \* a 64-byte GET\_DESCRIPTOR request. This is what Windows does,
  17. \* so it may help with some non-standards-compliant devices.
  18. \* Otherwise we start with SET\_ADDRESS and then try to read the
  19. \* first 8 bytes of the device descriptor to get the ep0 maxpacket
  20. \* value.
  21. \*/
  22. for (i = 0; i
  23. if (USE\_NEW\_SCHEME(retry\_counter)) \{
  24. struct usb\_device\_descriptor \*buf;
  25. int r = 0;

#define GET_DESCRIPTOR_BUFSIZE 64
buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
if (!buf) {
retval = -ENOMEM;
continue;
}

  1. /\* Retry on all errors; some devices are flakey.
  2. \* 255 is for WUSB devices, we actually need to use
  3. \* 512 (WUSB1.0\[4.8.1\]).
  4. \*/
  5. for (j = 0; j
  6. buf->bMaxPacketSize0 = 0;
  7. r = usb\_control\_msg(udev, usb\_rcvaddr0pipe(),
  8. USB\_REQ\_GET\_DESCRIPTOR, USB\_DIR\_IN,
  9. USB\_DT\_DEVICE
  10. buf, GET\_DESCRIPTOR\_BUFSIZE,
  11. USB\_CTRL\_GET\_TIMEOUT);
  12. switch (buf->bMaxPacketSize0) \{
  13. case 8: case 16: case 32: case 64: case 255:
  14. if (buf->bDescriptorType ==
  15. USB\_DT\_DEVICE) \{
  16. r = 0;
  17. break;
  18. \}
  19. /\* FALL THROUGH \*/
  20. default:
  21. if (r == 0)
  22. r = -EPROTO;
  23. break;
  24. \}
  25. if (r == 0)
  26. break;
  27. \}
  28. udev->descriptor.bMaxPacketSize0 =
  29. buf->bMaxPacketSize0;
  30. kfree(buf);
  31. retval = hub\_port\_reset(hub, port1, udev, delay);
  32. if (retval
  33. goto fail;
  34. if (oldspeed != udev->speed) \{
  35. dev\_dbg(&udev->dev,
  36. "device reset changed speed!\\n");
  37. retval = -ENODEV;
  38. goto fail;
  39. \}
  40. if (r) \{
  41. dev\_err(&udev->dev, "device descriptor "
  42. "read/%s, error %d\\n",
  43. "64", r);
  44. retval = -EMSGSIZE;
  45. continue;
  46. \}

#undef GET_DESCRIPTOR_BUFSIZE
}

  1. for (j = 0; j
  2. retval = hub\_set\_address(udev, devnum);
  3. if (retval >= 0)
  4. break;
  5. msleep(200);
  6. \}
  7. if (retval
  8. dev\_err(&udev->dev,
  9. "device not accepting address %d, error %d\\n",
  10. devnum, retval);
  11. goto fail;
  12. \}
  13. /\* cope with hardware quirkiness:
  14. \* - let SET\_ADDRESS settle, some device hardware wants it
  15. \* - read ep0 maxpacket even for high and low speed,
  16. \*/
  17. msleep(10);
  18. if (USE\_NEW\_SCHEME(retry\_counter))
  19. break;
  20. retval = usb\_get\_device\_descriptor(udev, 8);
  21. if (retval
  22. dev\_err(&udev->dev, "device descriptor "
  23. "read/%s, error %d\\n",
  24. "8", retval);
  25. if (retval >= 0)
  26. retval = -EMSGSIZE;
  27. \} else \{
  28. retval = 0;
  29. break;
  30. \}
  31. \}

这个for循环是一个很重要的操作,首先,我们来看一下USE_NEW_SCHEME宏的定义.如下示:
((i) / 2 == old_scheme_first), old_scheme_first默认为0,也就是说,当i为0,1的时候,这个宏会返回1.那就是说,对于之前分析的两种机制,每种机制尝试两次.
区分一下这两种机制的不同:
对于第一种机制,它先用64的buffer去取设备描述符.而第二种机制,是以长度8的缓存区,取设备描述符的前半部份.
另外,第一种机制,去取设备描述符之前没有设置设备的地址,因此使用地址0来表示设备的地址,在代码中,用usb_rcvaddr0pipe()表示.而在第二种机制中,它在取设备描述符之前已经设置了设备的地址.
疑问:可能有人就有这样的疑问,既然地址0可以表示没有设置地址的设备地址,那如果有多个没有set address的设备,这个地址0到底是表示那个设备呢?
实际上,从代码上看,Linux是每打开一个hub的端口就初始连在这个端口上的设备.之后这连接上的设备设置好地址之后再打开hub的另外的端口进行配置,因此,在同一条usb bus上,不会出现多个末配置的活动设备.

  1. if (retval)
  2. goto fail;
  3. i = udev->descriptor.bMaxPacketSize0 == 0xff?
  4. 512 : udev->descriptor.bMaxPacketSize0;
  5. if (le16\_to\_cpu(udev->ep0.desc.wMaxPacketSize) != i) \{
  6. if (udev->speed != USB\_SPEED\_FULL ||
  7. !(i == 8 || i == 16 || i == 32 || i == 64)) \{
  8. dev\_err(&udev->dev, "ep0 maxpacket = %d\\n", i);
  9. retval = -EMSGSIZE;
  10. goto fail;
  11. \}
  12. dev\_dbg(&udev->dev, "ep0 maxpacket = %d\\n", i);
  13. udev->ep0.desc.wMaxPacketSize = cpu\_to\_le16(i);
  14. ep0\_reinit(udev);
  15. \}
  16. retval = usb\_get\_device\_descriptor(udev, USB\_DT\_DEVICE\_SIZE);
  17. if (retval descriptor)) \{
  18. dev\_err(&udev->dev, "device descriptor read/%s, error %d\\n",
  19. "all", retval);
  20. if (retval >= 0)
  21. retval = -ENOMSG;
  22. goto fail;
  23. \}
  24. retval = 0;

fail:
if (retval) {
hub_port_disable(hub, port1, 0);
udev->devnum = devnum; /* for disconnect processing */
}
mutex_unlock(&usb_address0_mutex);
return retval;
}
在上面获得的设备描述符的bMaxPacketSize0字段,也就是ep0的MaxPacketSize.但如果这个值不和我们之前根据spec为ep0设定的MaxPacketSize值相等,且不是Full speed的话,就会有错误了.因为只有Full Speed的设备的ep0 的MaxPacketSize在spec上并没有一个明确的定义值.
有了确定的ep0 的MaxPacketSize值,就可以取得完整的设备描述符了.

第四个要分析的函数是hub_port_reset().
这个函数将端口重置并等待端口重置完成.代码如下:
static int hub_port_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, unsigned int delay)
{
int i, status;

  1. /\* Block EHCI CF initialization during the port reset.
  2. \* Some companion controllers don't like it when they mix.
  3. \*/
  4. down\_read(&ehci\_cf\_port\_reset\_rwsem);
  5. /\* Reset the port \*/
  6. //尝试5次
  7. for (i = 0; i
  8. //发送Reset 的Set\_Feature
  9. status = set\_port\_feature(hub->hdev,
  10. port1, USB\_PORT\_FEAT\_RESET);
  11. //发送错误
  12. if (status)
  13. dev\_err(hub->intfdev,
  14. "cannot reset port %d (err = %d)\\n",
  15. port1, status);
  16. else \{
  17. //发送Clear\_Feature成功,等待端口重置完成
  18. status = hub\_port\_wait\_reset(hub, port1, udev, delay);
  19. if (status && status != -ENOTCONN)
  20. dev\_dbg(hub->intfdev,
  21. "port\_wait\_reset: err = %d\\n",
  22. status);
  23. \}
  24. /\* return on disconnect or reset \*/
  25. switch (status) \{
  26. //成功
  27. case 0:
  28. /\* TRSTRCY = 10 ms; plus some extra \*/
  29. msleep(10 + 40);
  30. udev->devnum = 0; /\* Device now at address 0 \*/
  31. /\* FALL THROUGH \*/
  32. //端口没有连接
  33. case -ENOTCONN:
  34. //要发送的设备不存在
  35. case -ENODEV:
  36. clear\_port\_feature(hub->hdev,
  37. port1, USB\_PORT\_FEAT\_C\_RESET);
  38. /\* FIXME need disconnect() for NOTATTACHED device \*/
  39. usb\_set\_device\_state(udev, status
  40. ? USB\_STATE\_NOTATTACHED
  41. : USB\_STATE\_DEFAULT);
  42. goto done;
  43. \}
  44. dev\_dbg (hub->intfdev,
  45. "port %d not enabled, trying reset again...\\n",
  46. port1);
  47. //将延迟设至最长,再试一次
  48. delay = HUB\_LONG\_RESET\_TIME;
  49. \}
  50. dev\_err (hub->intfdev,
  51. "Cannot enable port %i. Maybe the USB cable is bad?\\n",
  52. port1);

done:
up_read(&ehci_cf_port_reset_rwsem);
return status;
}
这个函数的代码看清淅,首先将端口重置,然后等待端口重置完成.在成功返回或者是发错致命错误的时候就会在清除掉RESET Feature,设置设备状态之后返回.这个所谓的致命包括:
1:发送Clear_Feature时,返回-ENODEV,表示设备不存在
2:在hub_port_wait_reset()后返回的-ENOTCONN,表示端口上末连接设备.
另外,在这里哆嗦的重复一句,只有在设备有这个Feature的时候,才能Clear_Feature.在上面的代码中,只有代码中,如果Reset不成功,是不需要Clear USB_PORT_FEAT_C_RESET 这个Feature的.只有在已经设置成功的情况,才能将其Clear(-ENODEV的情况,无所谓,这个错误在submit urb前期就能测检出来,不会跟硬件交互,而-ENOTCONN则表示端口Reset完成,但尚末检测到连接设备,这种情况下,也是需要Clear_Feature的).
另外,里面还调用了一个子函数, hub_port_wait_reset().代码如下:
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, unsigned int delay)
{
int delay_time, ret;
u16 portstatus;
u16 portchange;

  1. //最长等待时间是500
  2. for (delay\_time = 0;
  3. delay\_time
  4. delay\_time += delay) \{
  5. /\* wait to give the device a chance to reset \*/
  6. msleep(delay);
  7. /\* read and decode port status \*/
  8. ret = hub\_port\_status(hub, port1, &portstatus, &portchange);
  9. if (ret
  10. return ret;
  11. /\* Device went away? \*/
  12. //端口已经没有连接了,说明连接的设备在某个时刻被拨下来了
  13. if (!(portstatus & USB\_PORT\_STAT\_CONNECTION))
  14. return -ENOTCONN;
  15. /\* bomb out completely if the connection bounced \*/
  16. //连接状态发生了改变,则说明连接状态不稳定.因为断开之后,再联上是需要重新配置的
  17. //退出
  18. if ((portchange & USB\_PORT\_STAT\_C\_CONNECTION))
  19. return -ENOTCONN;
  20. /\* if we\`ve finished resetting, then break out of the loop \*/
  21. //如果Reset已经完成,且端口处于enable状态,设置speed成员就可以返回了
  22. if (!(portstatus & USB\_PORT\_STAT\_RESET) &&
  23. (portstatus & USB\_PORT\_STAT\_ENABLE)) \{
  24. if (hub\_is\_wusb(hub))
  25. udev->speed = USB\_SPEED\_VARIABLE;
  26. else if (portstatus & USB\_PORT\_STAT\_HIGH\_SPEED)
  27. udev->speed = USB\_SPEED\_HIGH;
  28. else if (portstatus & USB\_PORT\_STAT\_LOW\_SPEED)
  29. udev->speed = USB\_SPEED\_LOW;
  30. else
  31. udev->speed = USB\_SPEED\_FULL;
  32. return 0;
  33. \}
  34. /\* switch to the long delay after two short delay failures \*/
  35. //失败两次,将延时时间设为最长的时间
  36. if (delay\_time >= 2 \* HUB\_SHORT\_RESET\_TIME)
  37. delay = HUB\_LONG\_RESET\_TIME;
  38. dev\_dbg (hub->intfdev,
  39. "port %d not reset yet, waiting %dms\\n",
  40. port1, delay);
  41. \}
  42. return -EBUSY;

}
注意到在上面为speed成员赋值的时候,出现了一个hub_is_wusb().该宏用来判断hcd是否是一个无线的USB主机控制器.如果hcd 是一个无线的,那其下的所有设备的speed均为USB_SPEED_VARIABLE.这个是属于usb2.5 spec里面定义的.
到这里,hub_thread()函数已经分析完了.它已经将hub下连接的所有新设备都初始化并添加进了设备模型.

5.2.3:HUB中断URB传输完成的处理
在之前分析中断URB初始化的时候,曾分析到,如果中断URB传输完成,就会调用hub_irq().在分析这个函数之前,我们先从spec上了解一下,对于hub的中断传输到底会传些什么样的东西:
如下图所示:
080926174945.jpg

Bit0表示hub的连接状态发生了改变,而bit1~bitN表示的是各端口连接状态的改变.如果1表示改变,为0表示末改变.
现在可以看该函数的代码了,如下:
static void hub_irq(struct urb *urb)
{
struct usb_hub *hub = urb->context;
int status = urb->status;
int i;
unsigned long bits;

  1. switch (status) \{
  2. case -ENOENT: /\* synchronous unlink \*/
  3. case -ECONNRESET: /\* async unlink \*/
  4. case -ESHUTDOWN: /\* hardware going away \*/
  5. return;
  6. default: /\* presumably an error \*/
  7. /\* Cause a hub reset after 10 consecutive errors \*/
  8. dev\_dbg (hub->intfdev, "transfer --> %d\\n", status);
  9. if ((++hub->nerrors error)
  10. goto resubmit;
  11. hub->error = status;
  12. /\* FALL THROUGH \*/
  13. /\* let khubd handle things \*/
  14. case 0: /\* we got data: port status changed \*/
  15. bits = 0;
  16. for (i = 0; i actual\_length; ++i)
  17. bits |= ((unsigned long) ((\*hub->buffer)))
  18. hub->event\_bits\[0\] = bits;
  19. break;
  20. \}
  21. hub->nerrors = 0;
  22. /\* Something happened, let khubd figure it out \*/
  23. kick\_khubd(hub);

resubmit:
if (hub->quiescing)
return;

  1. if ((status = usb\_submit\_urb (hub->urb, GFP\_ATOMIC)) != 0
  2. && status != -ENODEV && status != -EPERM)
  3. dev\_err (hub->intfdev, "resubmit --> %d\\n", status);

}
从上面的代码可以看出,就将是设HUB中断传输的信息保存在hub->event_bits中,然后又将此URB再次提交,再次提交的结果是,可以轮询获得hub的状态,另外,还会调用kick_khubd().这样, hub_events()就又会调用,又可以处理HUB端口的状态改变.

六:小结
在本小结里,对HUB的处理过程做了一个详尽的分析,在这一节里,也了解到了USB的驱动架构以及USB设备的枚举过程.
在下一节里,我们以特定的USB设备分例,来分析USB驱动程序的架构.

发表评论

表情:
评论列表 (有 0 条评论,452人围观)

还没有评论,来说两句吧...

相关阅读