USB——usb core 初始化 - awokezhou/LinuxPage GitHub Wiki
本文分析usb core的初始化过程,梳理总结初始化的关键步骤,以及在整个USB子系统中涉及到的重要的数据结构和处理过程
usb core代码位于/driver/usb/core路径下,其初始化代码在usb.c文件中。usb core在整个USB子系统中的位置如下图
usb core位于USB Host Controller Driver和USB Device Driver之间,一些通用的处理过程和数据结构都放在core中来做
static int __init usb_init(void)
{
int retval;
if (nousb) {
pr_info("%s: USB support disabled\n", usbcore_name);
return 0;
}
retval = usb_debugfs_init();
if (retval)
goto out;
retval = ksuspend_usb_init();
if (retval)
goto out;
retval = bus_register(&usb_bus_type);
if (retval)
goto bus_register_failed;
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
if (retval)
goto bus_notifier_failed;
retval = usb_major_init();
if (retval)
goto major_init_failed;
retval = usb_register(&usbfs_driver);
if (retval)
goto driver_register_failed;
retval = usb_devio_init();
if (retval)
goto usb_devio_init_failed;
retval = usbfs_init();
if (retval)
goto fs_init_failed;
retval = usb_hub_init();
if (retval)
goto hub_init_failed;
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
if (!retval)
goto out;
usb_hub_cleanup();
hub_init_failed:
usbfs_cleanup();
fs_init_failed:
usb_devio_cleanup();
usb_devio_init_failed:
usb_deregister(&usbfs_driver);
driver_register_failed:
usb_major_cleanup();
major_init_failed:
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
bus_notifier_failed:
bus_unregister(&usb_bus_type);
bus_register_failed:
ksuspend_usb_cleanup();
out:
return retval;
}
/*
* Cleanup
*/
static void __exit usb_exit(void)
{
/* This will matter if shutdown/reboot does exitcalls. */
if (nousb)
return;
usb_deregister_device_driver(&usb_generic_driver);
usb_major_cleanup();
usbfs_cleanup();
usb_deregister(&usbfs_driver);
usb_devio_cleanup();
usb_hub_cleanup();
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
bus_unregister(&usb_bus_type);
ksuspend_usb_cleanup();
usb_debugfs_cleanup();
}
subsys_initcall(usb_init);
module_exit(usb_exit);
代码中通过subsys_initcall()
将其声明为一个子系统。usb_init
函数中主要做了以下几件事
-
调用
usb_debugfs_init()
创建在/sys/kernel/debug下创建用于调试的文件 -
调用
bus_register()
向系统注册了usb_bus_type
总线类型 -
调用
bus_register_notifier()
向系统注册了通知,用于模块间通信 -
调用
usb_register()
注册了usbfs_driver
驱动 -
调用
usb_devio_init()
进行了I/O初始化,其实就是注册了字符设备和类驱动 -
调用
usb_hub_init()
进行了集线器初始化 -
调用
usb_register_device_driver()
注册了usb_generic_driver驱动
其中比较重要的是usb_bus_type
类型总线的注册和集线器的初始化,下面重点来分析
usb_bus_type的定义如下
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
定义总线名称为usb,总线匹配函数为usb_device_match,此函数在向系统的usb总线中注册了设备或者驱动后就会调用这个匹配函数,匹配成功后就会调用驱动函数的probe
集线器初始化代码如下
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < 0) {
printk(KERN_ERR "%s: can't register hub driver\n",
usbcore_name);
return -1;
}
khubd_task = kthread_run(hub_thread, NULL, "khubd");
if (!IS_ERR(khubd_task))
return 0;
/* Fall through if kernel_thread failed */
usb_deregister(&hub_driver);
printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);
return -1;
}
其实就是做了两件事情,注册了hub_driver驱动,创建了一个内核线程khubd。hub_driver是通过usb_register()函数来注册的,需要分析一下usb_register()函数的执行过程。内核线程的执行函数为hub_thread,也需要分析该函数主要做什么工作
usb驱动的注册函数为usb_register(),该函数其实是一个封装
static inline int usb_register(struct usb_driver *driver)
{
return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
}
其实是调用usb_register_driver()来完成注册的
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
const char *mod_name)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
new_driver->drvwrap.for_devices = 0;
new_driver->drvwrap.driver.name = (char *) new_driver->name;
new_driver->drvwrap.driver.bus = &usb_bus_type;
new_driver->drvwrap.driver.probe = usb_probe_interface;
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
retval = driver_register(&new_driver->drvwrap.driver);
if (!retval) {
pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
usbfs_update_special();
usb_create_newid_file(new_driver);
} else {
printk(KERN_ERR "%s: error %d registering interface "
" driver %s\n",
usbcore_name, retval, new_driver->name);
}
return retval;
}
要看明白是怎么注册的,必须要搞清楚usb_driver这个数据结构的内容
usb_driver结构代码如下
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
int (*ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
int (*reset_resume)(struct usb_interface *intf);
int (*pre_reset)(struct usb_interface *intf);
int (*post_reset)(struct usb_interface *intf);
const struct usb_device_id *id_table;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int soft_unbind:1;
};
struct usbdrv_wrap {
struct device_driver driver;
int for_devices;
};
usb_driver
结构有一些函数指针,还有一个drvwrap
成员,封装了一个driver
和一个整形变量。注释中说for_device
为0表示一个接口驱动,不为0表示一个设备驱动,接口的概念在USB概念介绍的文章中专门来说明,本文不做介绍。在注册函数中,该变量被赋值为0,表明注册的这个驱动的确是一个接口驱动。代码中对这个封装的driver
进行了一些赋值,比如name、probe、remove等,然后通过标准的driver_register()
函数将这个封装的driver注册到了系统中。所以外层的usb_driver
结构体属于USB子系统私有的结构体,在子系统内部使用的usb_driver
,向系统中注册的是usb_driver
封装的标准driver
,当向系统中注册了相同名称的设备device
时,其probe
函数也就是usb_probe_interface()
会调用。当然,现在系统中还没有对应的device注册,现在只是注册了driver,device会在usb host controller driver的初始化中注册,这里不做分析。
总结一下,数据结构的关系如下图所示
刚刚注册的hub_driver定义如下
static struct usb_driver hub_driver = {
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
.suspend = hub_suspend,
.resume = hub_resume,
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
.ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
其中挂接了狠多操作函数,但是这些函数会在什么时候调用,目前还不知道。
前面分析到,集线器初始化主要就是干了两件事,一个是注册hub_driver,另一个就是创建了一个内核线程。线程函数为
static int hub_thread(void *__unused)
{
/* khubd needs to be freezable to avoid intefering with USB-PERSIST
* port handover. Otherwise it might see that a full-speed device
* was gone before the EHCI controller had handed its port over to
* the companion full-speed controller.
*/
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));
pr_debug("%s: khubd exiting\n", usbcore_name);
return 0;
}
可以看到,处理的核心是hub_events()函数,和其相关的有一个hub_event_list链表,可以猜测hub_event_list上会挂接很多事件,线程不断的轮询这个链表上的事件,并进行处理。
代码贴上
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;
/*
* We restart the list every time to avoid a deadlock with
* deleting hubs downstream from this one. This should be
* safe since we delete the hub from the event list.
* Not the most efficient, but avoids deadlocks.
*/
while (1) {
/* Grab the first entry at the beginning of the list */
spin_lock_irq(&hub_event_lock);
if (list_empty(&hub_event_list)) {
spin_unlock_irq(&hub_event_lock);
break;
}
tmp = hub_event_list.next;
list_del_init(tmp);
hub = list_entry(tmp, struct usb_hub, event_list);
kref_get(&hub->kref);
spin_unlock_irq(&hub_event_lock);
hdev = hub->hdev;
hub_dev = hub->intfdev;
intf = to_usb_interface(hub_dev);
dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
hdev->state, hub->descriptor
? hub->descriptor->bNbrPorts
: 0,
/* NOTE: expects max 15 ports... */
(u16) hub->change_bits[0],
(u16) hub->event_bits[0]);
/* Lock the device, then check to see if we were
* disconnected while waiting for the lock to succeed. */
usb_lock_device(hdev);
if (unlikely(hub->disconnected))
goto loop;
/* If the hub has died, clean up after it */
if (hdev->state == USB_STATE_NOTATTACHED) {
hub->error = -ENODEV;
hub_quiesce(hub, HUB_DISCONNECT);
goto loop;
}
/* Autoresume */
ret = usb_autopm_get_interface(intf);
if (ret) {
dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
goto loop;
}
/* If this is an inactive hub, do nothing */
if (hub->quiescing)
goto loop_autopm;
if (hub->error) {
dev_dbg (hub_dev, "resetting for error %d\n",
hub->error);
ret = usb_reset_device(hdev);
if (ret) {
dev_dbg (hub_dev,
"error resetting hub: %d\n", ret);
goto loop_autopm;
}
hub->nerrors = 0;
hub->error = 0;
}
/* deal with port status changes */
for (i = 1; i <= hub->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)
continue;
ret = hub_port_status(hub, i,
&portstatus, &portchange);
if (ret < 0)
continue;
if (portchange & USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_CONNECTION);
connect_change = 1;
}
if (portchange & USB_PORT_STAT_C_ENABLE) {
if (!connect_change)
dev_dbg (hub_dev,
"port %d enable change, "
"status %08x\n",
i, portstatus);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_ENABLE);
/*
* EM interference sometimes causes badly
* shielded USB devices to be shutdown by
* the hub, this hack enables them again.
* Works at least with mouse driver.
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
&& hdev->children[i-1]) {
dev_err (hub_dev,
"port %i "
"disabled by hub (EMI?), "
"re-enabling...\n",
i);
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) {
/* TRSMRCY = 10 msec */
msleep(10);
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);
}
dev_dbg (hub_dev,
"resume on port %d, status %d\n",
i, ret);
}
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
dev_err (hub_dev,
"over-current change on port %d\n",
i);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_OVER_CURRENT);
hub_power_on(hub, true);
}
if (portchange & USB_PORT_STAT_C_RESET) {
dev_dbg (hub_dev,
"reset change on port %d\n",
i);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_RESET);
}
if (connect_change)
hub_port_connect_change(hub, i,
portstatus, portchange);
} /* end for i */
/* deal with hub status changes */
if (test_and_clear_bit(0, hub->event_bits) == 0)
; /* do nothing */
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) {
dev_dbg (hub_dev, "power change\n");
clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
if (hubstatus & HUB_STATUS_LOCAL_POWER)
/* FIXME: Is this always true? */
hub->limited_power = 1;
else
hub->limited_power = 0;
}
if (hubchange & HUB_CHANGE_OVERCURRENT) {
dev_dbg (hub_dev, "overcurrent change\n");
msleep(500); /* Cool down */
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
hub_power_on(hub, true);
}
}
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);
} /* end while (1) */
}
首先可以注意到,tmp指针指向链表的next,然后每走一次循环,就销毁一个链表中的事件。调用list_entry()获取链表中的每一个hub,这里可以看出来hub_event线程主要处理的是集线器上发生的事件。经过了一些判断之后,循环遍历该hub上的每个端口,并通过hub_port_status()获取每个端口的状态,最后会根据状态的变化情况调用hub_port_connect_change()进行处理。
至此,大概了解了usb core初始化过程中主要是做了三件事情。
-
第一是向系统中注册了usb总线类型
-
第二是向usb总线注册了集线器驱动,usbfs驱动,和usb驱动,但是设备没有注册,所以其驱动的probe函数目前不会调用。
-
第三是创建了一个集线器相关的事件处理线程,主要用于处理所有集线器的端口状态变化,但是都有什么变化,这些变化对应着什么处理,目前也还不清楚。