本文使用linux6.4内核版本代码,从以下几个方面来学习usb子系统
1、usb总线注册与通用驱动注册
2、usb总线match回调函数分析
3、usb host 控制器HCD驱动加载以ehci_hcd为例
4、hub设备驱动(通用设备驱动)的probe
5、hub interface驱动的probe
6、hub_event的触发
usb总线注册与通用驱动注册
源码位置:
kernel/driver/usb/core/usb.c
usb_init函数截图
这个初始化完成了几个重要功能:
1、usb总线注册
2、hub interface驱动注册
3、usb通用设备驱动注册
其中后面两个驱动贯穿了usb子系统整个框架,把这两个驱动搞明白就差不对usb有个整体理解了。
usb总线match回调函数分析
下面来看看usb总线的match函数实现,其主要作用就是将usb device和driver进行匹配
const struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.need_parent_lock = true,
};
来看看match函数
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
struct usb_device *udev;
struct usb_device_driver *udrv;
//如果是接口驱动直接返回失败
if (!is_usb_device_driver(drv))
return 0;
udev = to_usb_device(dev);
udrv = to_usb_device_driver(drv);
//如果没有table也没有定义match则一定会匹配上
这个就跟早期版本没区别了
if (!udrv->id_table && !udrv->match)
return 1;
//如果既定义了table也定义了match,则需两个都能
匹配上才算匹配成功。如果只定义了其中一个,只需
定义的那个匹配成功即可。
return usb_driver_applicable(udev, udrv);
} else if (is_usb_interface(dev)) {
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
/* device drivers never match interfaces */
if (is_usb_device_driver(drv))
return 0;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
id = usb_match_id(intf, usb_drv->id_table);
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
usb总线的匹配分为设备驱动和接口驱动匹配,其驱动类型根据type来区分
static inline int is_usb_device(const struct device *dev)
{
return dev->type == &usb_device_type;
}
static inline int is_usb_interface(const struct device *dev)
{
return dev->type == &usb_if_device_type;
}
一、对于设备匹配,更清晰的理解可以看早期的内核版本源码比如4.14。早期版本只要是设备驱动则默认就是匹配上了,返回1。所以对于usb所有的设备device来说都默认走同一个设备驱动程序(即上面所说的usb通用设备驱动),因为如果有多个设备驱动那么都会走这样不合理。
二、最新的版本(当前6.14)增加部分代码,主要是增加了设备device与驱动的匹配规则,后面会分析通用驱动的匹配规则。为后续厂家实现自己的设备驱动提供了依据,但是一般情况下用不到这个。
三、由厂家提供的usb驱动基本都是interface类驱动,其匹配规则如下:
int usb_match_one_id_intf(struct usb_device *dev,
struct usb_host_interface *intf,
const struct usb_device_id *id)
{
/* The interface class, subclass, protocol and number should never be
* checked for a match if the device class is Vendor Specific,
* unless the match record specifies the Vendor ID. */
if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS |
USB_DEVICE_ID_MATCH_INT_PROTOCOL |
USB_DEVICE_ID_MATCH_INT_NUMBER)))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
(id->bInterfaceClass != intf->desc.bInterfaceClass))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
(id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
(id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&
(id->bInterfaceNumber != intf->desc.bInterfaceNumber))
return 0;
return 1;
}
usb host 控制器HCD驱动加载
USB HCD是主机控制器驱动(Host Controller Driver)的缩写,以ehci_hcd为例ehci-hcd是usb2.0控制器,下面是大体的调用流程图

各个总线的控制器一般都是platform总线加载的usb也不例外,这里只关注重点部分的代码。
1、ehci-hcd回调函数初始化
void ehci_init_driver(struct hc_driver *drv,
const struct ehci_driver_overrides *over)
{
/* Copy the generic table to drv and then apply the overrides */
*drv = ehci_hc_driver;
if (over) {
drv->hcd_priv_size += over->extra_priv_size;
if (over->reset)
drv->reset = over->reset;
if (over->port_power)
drv->port_power = over->port_power;
}
}

2、hcd设备的创建
hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));
创建hcd比较简单,大体分几个点:
一、为hcd分配内存空间
二、数据成员初始化
三、初始化定时器,这个是比较重要的。使用定时器会直接触发hub-event的调用
四、绑定hcd的回调处理函数
3 hcd的添加
//注意传的参数,包括刚刚创建的hcd、中断号、中断标置:此中断号与其它设备共享
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
usb_add_hcd是一个核心函数,下面切片来分析该函数的各个功能
一、usb phy硬件的初始化、上电等这里不关注。
二、调用usb_alloc_dev函数生成一个设备device
rhdev = usb_alloc_dev(NULL, &hcd->self, 0);

这里是该函数的部分截图,上面清晰的表明此次分配的usb dev是一个设备device
三、刚刚分配的dev将会用作root hub的设备device
mutex_lock(&usb_port_peer_mutex);
hcd->self.root_hub = rhdev;
mutex_unlock(&usb_port_peer_mutex);
四、hcd的速度配置决定了hub的速度上限
五、初始下两个tasklet:高低优先级
init_giveback_urb_bh(&hcd->high_prio_bh);
hcd->high_prio_bh.high_prio = true;
init_giveback_urb_bh(&hcd->low_prio_bh);
static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
{
spin_lock_init(&bh->lock);
INIT_LIST_HEAD(&bh->head);
tasklet_setup(&bh->bh, usb_giveback_urb_bh);
}

这里的tasklet就是将hcd产生的各种事件交由hub处理的关键中转环节
六、为hcd注册中断处理函数
if (usb_hcd_is_primary_hcd(hcd) && irqnum) {
retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
if (retval)
goto err_request_irq;
}
static int usb_hcd_request_irqs(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
int retval;
if (hcd->driver->irq) {
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
hcd->driver->description, hcd->self.busnum);
retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
hcd->irq_descr, hcd);
……
return 0;
}
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
irqreturn_t rc;
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
rc = IRQ_NONE;
else if (hcd->driver->irq(hcd) == IRQ_NONE)
rc = IRQ_NONE;
else
rc = IRQ_HANDLED;
return rc;
}
这里的中断号和中断回调处理函数都由ehci-hcd提供
七:hub 设备device的注册
if (!HCD_DEFER_RH_REGISTER(hcd)) {
retval = register_root_hub(hcd);
if (retval != 0)
goto err_register_root_hub;
if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
usb_hcd_poll_rh_status(hcd);
}
register_root_hub ->usb_new_device->device_add
此时就会将第二步生成的dev注册到usb bus的klist_devices链表上。到此时hcd的工作就结束了,剩下就是设备驱动、接口驱动和root hub的各种处理了。这里要注意的是由于此时此刻注册了usb设备device,所以会触发device和drvier的匹配。下面就来分析下设备device的匹配
hub设备驱动(usb通用设备驱动)的probe
这里从以下几个方面来学习通用设备驱动的加载过程
一、设备驱动和接口驱动的区分
二、设备驱动的match过程
三、通用设备驱动probe的学习
通用设备驱动以下代码进行注册
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);

通用设备驱动的定义:
struct usb_device_driver usb_generic_driver = {
.name = "usb",
.match = usb_generic_driver_match,
.probe = usb_generic_driver_probe,
.disconnect = usb_generic_driver_disconnect,
#ifdef CONFIG_PM
.suspend = usb_generic_driver_suspend,
.resume = usb_generic_driver_resume,
#endif
.supports_autosuspend = 1,
};
先来分析其match规则:

通用设备驱动的probe:
上面已经说过hcd会主动生成第一个设备驱动即root hub设备驱动,根据匹配规则这里会直接与usb_generic_driver匹配上,调用其probe函数。现在借hub来分析通用设备驱动的probe函数
int usb_generic_driver_probe(struct usb_device *udev)
{
int err, c;
if (udev->authorized == 0)
dev_err(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
if (c >= 0) {
err = usb_set_configuration(udev, c);
if (err && err != -ENODEV) {
dev_err(&udev->dev, "can't set config #%d, error %d\n",
c, err);
}
}
}
/* USB device state == configured ... usable */
usb_notify_add_device(udev);
return 0;
}
这个probe比较明白,有两点:
一、从设备的众多配置信息中找出最合适的个,返回其下标
二、根据找的设备配置信息,分配interface设备并添加
第二点是问题的关键,只有这里生成的usb interface device才可能与我们的接口驱动进行匹配。比如usb网卡有时候注册不上,查看interface驱动是存在的就是设备没有,问题就出在这上面。这里对usb_set_configuration函数进行切片分析
if (dev->authorized == 0 || configuration == -1)
configuration = 0;
else {
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
if (dev->config[i].desc.bConfigurationValue ==
configuration) {
cp = &dev->config[i];
break;
}
}
}
从众多配置中,将最合适的配置信息取出来
if (cp) {
nintf = cp->desc.bNumInterfaces;
new_interfaces = kmalloc_array(nintf, sizeof(*new_interfaces),
GFP_NOIO);
if (!new_interfaces)
return -ENOMEM;
for (; n < nintf; ++n) {
new_interfaces[n] = kzalloc(
sizeof(struct usb_interface),
GFP_NOIO);
if (!new_interfaces[n]) {
ret = -ENOMEM;
free_interfaces:
while (--n >= 0)
kfree(new_interfaces[n]);
kfree(new_interfaces);
return ret;
}
}
i = dev->bus_mA - usb_get_max_power(dev, cp);
if (i < 0)
dev_warn(&dev->dev, "new config #%d exceeds power "
"limit by %dmA\n",
configuration, -i);
}
根据配置信息里告诉的接口数量分配同等数量的usb_interface设备
for (i = 0; i < nintf; ++i) {
struct usb_interface_cache *intfc;
struct usb_interface *intf;
struct usb_host_interface *alt;
u8 ifnum;
cp->interface[i] = intf = new_interfaces[i];
intfc = cp->intf_cache[i];
intf->altsetting = intfc->altsetting;
intf->num_altsetting = intfc->num_altsetting;
intf->authorized = !!HCD_INTF_AUTHORIZED(hcd);
kref_get(&intfc->ref);
alt = usb_altnum_to_altsetting(intf, 0);
if (!alt)
alt = &intf->altsetting[0];
ifnum = alt->desc.bInterfaceNumber;
intf->intf_assoc = find_iad(dev, cp, ifnum);
intf->cur_altsetting = alt;
usb_enable_interface(dev, intf, true);
intf->dev.parent = &dev->dev;
if (usb_of_has_combined_node(dev)) {
device_set_of_node_from_dev(&intf->dev, &dev->dev);
} else {
intf->dev.of_node = usb_of_get_interface_node(dev,
configuration, ifnum);
}
ACPI_COMPANION_SET(&intf->dev, ACPI_COMPANION(&dev->dev));
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
intf->dev.groups = usb_interface_groups;
INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
INIT_WORK(&intf->wireless_status_work, __usb_wireless_status_intf);
intf->minor = -1;
device_initialize(&intf->dev);
pm_runtime_no_callbacks(&intf->dev);
dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum,
dev->devpath, configuration, ifnum);
usb_get_dev(dev);
}
对所有分配的usb_interface进行初始化,注意这里的device类型都是usb_if_device_type即接口device,要关注下of_node是如何赋值的。
for (i = 0; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i];
if (intf->dev.of_node &&
!of_device_is_available(intf->dev.of_node)) {
dev_info(&dev->dev, "skipping disabled interface %d\n",
intf->cur_altsetting->desc.bInterfaceNumber);
continue;
}
dev_dbg(&dev->dev,
"adding %s (config #%d, interface %d)\n",
dev_name(&intf->dev), configuration,
intf->cur_altsetting->desc.bInterfaceNumber);
device_enable_async_suspend(&intf->dev);
ret = device_add(&intf->dev);
if (ret != 0) {
dev_err(&dev->dev, "device_add(%s) --> %d\n",
dev_name(&intf->dev), ret);
continue;
}
create_intf_ep_devs(intf);
}
将所有的usb_interface添加到设备中去,也就是接口设备。将会给所有的interface驱动提供匹配的机会,这里的hub设备驱动生成的自然就是其interface设备,自然会匹配上前面说到的hub驱动。
hub interface驱动的probe
hub interface驱动的注册
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,
.unlocked_ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
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;
}
hub_probe函数主要是初始化hub,其中非常重要的就是hub_event队列的初始化
kref_init(&hub->kref);
hub->intfdev = &intf->dev;
hub->hdev = hdev;
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
INIT_WORK(&hub->events, hub_event);
INIT_LIST_HEAD(&hub->onboard_hub_devs);
spin_lock_init(&hub->irq_urb_lock);
timer_setup(&hub->irq_urb_retry, hub_retry_irq_urb, 0);
usb_get_intf(intf);
usb_get_dev(hdev);
usb_set_intfdata(intf, hub);
intf->needs_remote_wakeup = 1;
pm_suspend_ignore_children(&intf->dev, true);
if (hdev->speed == USB_SPEED_HIGH)
highspeed_hubs++;
if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND)
hub->quirk_check_port_auto_suspend = 1;
if (id->driver_info & HUB_QUIRK_DISABLE_AUTOSUSPEND) {
hub->quirk_disable_autosuspend = 1;
usb_autopm_get_interface_no_resume(intf);
}
if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) {
onboard_hub_create_pdevs(hdev, &hub->onboard_hub_devs);
return 0;
}
和hub_irq对hub->urb的填充,hub_irq主要作用就是触发hub_event调用
hub_configure函数太过庞大这里就不具体分析了,把谓主主要流程搞清楚即可,细节需要带着问题才能更好的搞明白。

如上所示当hub接口驱动起来后,其内部的hub_event函数将持续工作,处理所有usb的插拨,将调用流程后
hub_event->port_event->hub_port_connect_change->hub_port_connect->usb_alloc_dev
hub_event收到port变化后进行相关处理,最后条件满足所用usb_alloc_dev函数生成设备device,此device再与通用驱动匹配上然后生成接口device再与我们外设提供的接口驱动匹配上完成一个轮回。
hub_event的触发
最后就只剩下一件事了,既然hub_event是后续所有usb设备生成的核心起点,那么它是如何被触发调用的呢。对于ehci-hcd来说大体流程如下图

前面说过在hcd添加的时候会注册中断处理函数,其最终调用的就是ehci-hcd提供的ehci_irq回调函数,后续最终会由tasklet下半部进行处理。

而tasklet的初始下如下:
static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
{
spin_lock_init(&bh->lock);
INIT_LIST_HEAD(&bh->head);
tasklet_setup(&bh->bh, usb_giveback_urb_bh);
}


到这里基本上整个逻辑就闭环了。usb子系统过于复杂,后续需要带着问题仔细分析哪个环节出了问题,但是只要对整个流程有个大体的了解,不至于一出问题就一头雾水。
553

被折叠的 条评论
为什么被折叠?



