分类: LINUX
2011-03-22 11:18:13
谨以此文纪念过往的岁月
一.前言
在前文中我们看过了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设备进行处理了,我们在这里就不说了。