Linux usb hub处理
谨以此文纪念过往的岁月
一.前言
在前文中我们看过了usb
hub的probe对于其初始化应该有了一定的了解,那在该文中我们来看usb hub守护程序。
二. Hub守护程序
话说前文中在kick_khubd中将khubd_wait唤醒,该工作队列在守护程序中被等待。不出什么特殊情况的话,该守护进程将伴随系统一生。
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));
return
0;
}
Events的code如下,也许大家会很惊讶其巨大,不要怕,咱们一起看下去。不过最好是读者手头有源码还有source insight。
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;
while
(1) {
spin_lock_irq(&hub_event_lock);--锁定event自旋锁
if
(list_empty(&hub_event_list)) {--如果事件链表为空跳出,继续等待。
spin_unlock_irq(&hub_event_lock);
break;
}
tmp
= .next;
list_del_init(tmp);--将tmp从中删除并且初始化
hub
= list_entry(tmp, struct usb_hub, event_list); --根据tmp获取hub,也许有人会疑惑上面都删除和初始化了,还能获得不?答案是能,因为该链表是从hub_event_list中删除并且初始化了,但是其地址还是不变的,所以还是能得到的。
kref_get(&hub->kref);--增加hub计数
spin_unlock_irq(&hub_event_lock);
--解锁
hdev
= hub->hdev;
hub_dev
= hub->intfdev;
intf
= to_usb_interface(hub_dev);
usb_lock_device(hdev);
if
(unlikely(hub->disconnected))
goto
loop;
if
(hdev->state == USB_STATE_NOTATTACHED) { --如果hub不在了,将所有的子断开,同时删除urb
hub->error
= -ENODEV;
hub_quiesce(hub,
HUB_DISCONNECT);
goto
loop;
}
ret
= usb_autopm_get_interface(intf); --自动将hub唤醒
if
(ret) {
goto
loop;
}
if
(hub->quiescing)--如果hub本身就是不活动的,什么也要做回吧。
goto
loop_autopm;
if
(hub->error) {--如果hub有错误。
ret
= usb_reset_device(hdev); --重新复位hub
if
(ret) {
goto
loop_autopm;
}
hub->nerrors
= 0;
hub->error
= 0;
}
for
(i = 1; i <= hub->descriptor->bNbrPorts; i++) { --处理端口状态改变
if
(test_bit(i, hub->busy_bits))--检测端口是否忙
continue;
connect_change
= test_bit(i, hub->); -- change_bits会在hub第一次初始化时被赋值。而event_bits则在hub_irq中改变
if
(!test_and_clear_bit(i, hub->) &&!connect_change)—如果都没有改变,继续测试下一个端口。
continue;
ret = hub_port_status(hub, i,&portstatus,
&portchange); --获取第i个端口的状态和改变寄存器,其具体说明在usb 2.0 spec 11.24.2.7中说明。其实也就是获得OHCI中第i个端口的HcRhPortStatus的寄存器值
if
(ret < 0)
continue;
if
(portchange & USB_PORT_STAT_) { --是否有设备在该端口,存在则将
clear_port_feature(hdev,
i,);
--将对应的portstatus的16位设置为1.关于其具体的含义在OHCI的section 7中说明
connect_change
= 1; --连接改变
}
if
(portchange & USB_PORT_STAT_C_ENABLE) {
if
(!connect_change)
clear_port_feature(hdev, i,USB_PORT_FEAT_C_ENABLE);
if
(!(portstatus & USB_PORT_STAT_ENABLE)&& !connect_change
&& hdev->children[i-1]) {
connect_change
= 1;
}
}
if
(portchange & USB_PORT_STAT_C_SUSPEND) {
struct
usb_device *udev;
clear_port_feature(hdev,
i,USB_PORT_FEAT_C_SUSPEND);
udev
= hdev->children[i-1];
if
(udev) {
usb_lock_device(udev);
ret
= remote_wakeup(hdev->children[i-1]);
usb_unlock_device(udev);
if
(ret < 0)
connect_change
= 1;
}
else {
ret
= -ENODEV;
hub_port_disable(hub,
i, 1);
}
}
if
(portchange & USB_PORT_STAT_C_OVERCURRENT) {
clear_port_feature(hdev,
i,USB_PORT_FEAT_C_OVER_CURRENT);
hub_power_on(hub,
true);
}
--关于上面的几个状态说明将在以后的学习中来看。
if
(portchange & USB_PORT_STAT_C_RESET) {
clear_port_feature(hdev,
i,USB_PORT_FEAT_C_RESET);
}
--在上面对port状态的种种检测后,终于到了port的真正处理的函数。
if
()--处理port改变
hub_port_connect_change(hub,
i,portstatus, portchange);
}
if (test_and_clear_bit(0, hub->)
== 0);
else
if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
dev_err
(hub_dev, "get_hub_status failed\n");
else
{
if
(hubchange & HUB_CHANGE_LOCAL_POWER) {
clear_hub_feature(hdev,
C_HUB_LOCAL_POWER);
if
(hubstatus & HUB_STATUS_LOCAL_POWER)
hub->limited_power
= 1;
else
hub->limited_power
= 0;
}
if
(hubchange & HUB_CHANGE_OVERCURRENT) {
msleep(500);
clear_hub_feature(hdev,
C_HUB_OVER_CURRENT);
hub_power_on(hub, true);
}
}
loop_autopm:
if
(list_empty(&hub->event_list))
usb_autopm_enable(intf);
loop:
usb_unlock_device(hdev);
kref_put(&hub->kref,
hub_release);
}
}
在这之后就是具体的对某一个usb设备进行处理了,我们在这里就不说了。