linux-usb子系统的核心描述

描述

一、导读

本文将描述linux-usb子系统的核心,主要分析其核心的初始化流程,文中源码基于内核版本:4.1.15。

linux usb子系统框架的核心位于/drivers/usb/core/目录中,文件结构如下图所示:

LINUX内核

目录中各文件功能大致如下:

LINUX内核

二、USB核心的初始化

在linux内核的启动阶段USB模块最早输出日志信息如下:

 

(1)[    0.475284] usbcore: registered new interface driver usbfs
(2)[    0.475348] usbcore: registered new interface driver hub
(3)[    0.475400] usbcore: registered new device driver usb

 

从上述输出日志可知:第(1)行表示成功注册USB文件系统,且在系统正常启动后,会生成对应的/sys/bus/usb/目录。第(2)行表示成功注册USB HUB驱动。第(3)行表明成功注册USB通用设备驱动,即usb_generic_driver。通常USB设备都是以设备的身份先与usb_generic_driver匹配,匹配成功后,会分裂出接口,当对接口调用 device_add()后,会触发USB接口和USB驱动的匹配。

USB核心由/drivers/usb/core/usb.c文件描述。该文件以linux内核模块的方式设计,使用subsys_initcall(usb_init);将usb核心模块导出。其中,usb核心的初始化由usb_init()完成,实现如下:

 

static int __init usb_init(void)
{
 int retval;
 if (usb_disabled()) {
  pr_info("%s: USB support disabled
", usbcore_name);
  return 0;
 }
 usb_init_pool_max();  //初始化pool_max参数

 usb_debugfs_init();  //初始化usb的调试文件系统debugfs

 usb_acpi_register(); //初始化acpi
 retval = bus_register(&usb_bus_type); //向linux内核注册usb总线类型
 if (retval)
  goto bus_register_failed;
    //注册usb通知器
 retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
 if (retval)
  goto bus_notifier_failed;
  //usb设备号初始化
 retval = usb_major_init();
 if (retval)
  goto major_init_failed;
  //注册usbfs驱动程序
 retval = usb_register(&usbfs_driver);
 if (retval)
  goto driver_register_failed;
    //初始化usb的devio
 retval = usb_devio_init();
 if (retval)
  goto usb_devio_init_failed;
    //初始化usb的hub
 retval = usb_hub_init();
 if (retval)
  goto hub_init_failed;
    //注册通用usb设备驱动
 retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
 if (!retval)
  goto out;

 usb_hub_cleanup();
hub_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:
 usb_acpi_unregister();
 usb_debugfs_cleanup();
out:
 return retval;
}

 

从上述代码可见,usb_init()中执行了如下操作:

(1)判断linux内核是否开启了对USB的支持,如果不支持则直接返回。

(2)初始化调试文件系统关于usb的目录和文件:

 

static int usb_debugfs_init(void)
{
  //创建usb目录
 usb_debug_root = debugfs_create_dir("usb", NULL);
 if (!usb_debug_root)
  return -ENOENT;
  //在usb目录下创建devices文件
 usb_debug_devices = debugfs_create_file("devices", 0444,
           usb_debug_root, NULL,
           &usbfs_devices_fops);
 if (!usb_debug_devices)
 {
  debugfs_remove(usb_debug_root);
  usb_debug_root = NULL;
  return -ENOENT;
 }

 return 0;
}

 

(3)初始化usb与acpi相关的参数:

LINUX内核

(4)向linux内核注册usb总线类型,usb总线类型定义如下:

 

struct bus_type usb_bus_type = {
 .name =  "usb",
 .match = usb_device_match,
 .uevent = usb_uevent,
};

 

(5)注册usb总线通知器

(6)初始化usb主设备号:

 

int usb_major_init(void)
{
 int error;
  //注册usb字符设备
 error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
 if (error)
  printk(KERN_ERR "Unable to get major %d for usb devices
",
         USB_MAJOR);

 return error;
}

 

(7)注册usbfs驱动程序,usbfs以usb驱动程序的形式进行定义:

 

struct usb_driver usbfs_driver = {
 .name =  "usbfs",
 .probe = driver_probe,
 .disconnect = driver_disconnect,
 .suspend = driver_suspend,
 .resume = driver_resume,
};

 

(8)初始化usb的devio,实质上是初始化USB字符设备,devio用于USB设备与用户空间进行通信:

 

int __init usb_devio_init(void)
{
 int retval;

 retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
     "usb_device");
 if (retval) {
  printk(KERN_ERR "Unable to register minors for usb_device
");
  goto out;
 }
 cdev_init(&usb_device_cdev, &usbdev_file_operations);
 retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
 if (retval) {
  printk(KERN_ERR "Unable to get usb_device major %d
",
         USB_DEVICE_MAJOR);
  goto error_cdev;
 }
  //注册usbdev_nb通知器
 usb_register_notify(&usbdev_nb);
out:
 return retval;

error_cdev:
 unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
 goto out;
}

 

(9)初始化usb的hub,hub也是以USB驱动方式进行设计(/drivers/usb/core/hub.c):

 

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
",
   usbcore_name);
  return -1;
 }

 /*
  * The workqueue needs to be freezable to avoid interfering 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.
  */
 hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
 if (hub_wq)
  return 0;

 /* Fall through if kernel_thread failed */
 usb_deregister(&hub_driver);
 pr_err("%s: can't allocate workqueue for usb hub
", usbcore_name);

 return -1;
}

 

(10)注册通用usb设备驱动usb_generic_driver,定义如下(/drivers/usb/core/generic.c):

 

struct usb_device_driver usb_generic_driver = {
 .name = "usb",
 .probe = generic_probe,
 .disconnect = generic_disconnect,
#ifdef CONFIG_PM
 .suspend = generic_suspend,
 .resume = generic_resume,
#endif
 .supports_autosuspend = 1,
};

 

​从前文描述可知:在linux启动过程中或者是在插入USB设备后,通常USB设备都是以设备的身份先与usb_generic_driver匹配,这时候由.probe指向的generic_peobe()会执行,当匹配成功后,会分裂出接口,当对接口调用device_add()后,会触发USB接口和USB接口驱动的匹配。

generic_probe()函数实现如下:

 

static int generic_probe(struct usb_device *udev)
{
 int err, c;

 /* Choose and set the configuration.  This registers the interfaces
  * with the driver core and lets interface drivers bind to them.
  */
 if (udev->authorized == 0)
  dev_err(&udev->dev, "Device is not authorized for usage
");
 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
",
     c, err);
    /* This need not be fatal.  The user can try to
     * set other configurations. */
   }
  }
 }
 /* USB device state == configured ... usable */
 usb_notify_add_device(udev);  //通知usb设备添加,对应注册的回调函数会被执行。

 return 0;
}

 

三、总结

本文主要描述linux-usb核心的初始化,usb核心是usb子系统的内层,其他的usb模块都基于这个内层再设计。除此之外,着重描述了usb_init()函数的具体执行步骤,如下图所示:

LINUX内核





审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分