USB——usb core 初始化 - awokezhou/LinuxPage GitHub Wiki

usb core 初始化

本文分析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总线类型注册

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驱动的注册函数为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结构

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,
};

其中挂接了狠多操作函数,但是这些函数会在什么时候调用,目前还不知道。

khubd

前面分析到,集线器初始化主要就是干了两件事,一个是注册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上会挂接很多事件,线程不断的轮询这个链表上的事件,并进行处理。

hub_event

代码贴上

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函数目前不会调用。

  • 第三是创建了一个集线器相关的事件处理线程,主要用于处理所有集线器的端口状态变化,但是都有什么变化,这些变化对应着什么处理,目前也还不清楚。

⚠️ **GitHub.com Fallback** ⚠️