Linux USB驱动注册方式

 更新时间:2025年07月22日 09:28:26   作者:大肥周  
USB驱动通过structusb_driver注册,填充name、probe、disconnect和id_table字段,插入时触发设备匹配与初始化,拔出时清理资源并解绑驱动,实现设备动态管理

注册接口

  #define usb_register(driver) \
              usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
  int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
                     const char *mod_name)

注册的结构体是struct usb_driver,主要填充name、probe、disconnect和id_table字段,其中name是驱动名称,到时会在/sys/bus/usb/drivers显示这个驱动名称,probe和disconnect会在USB设备插入和拔出时调用,id_table就是匹配列表,可以通过VID、PID匹配,也可以通过USB设备类型匹配。

示例代码

static int zslusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
       struct usb_device *usbdev;
       struct usb_host_interface *interface;
       struct usb_endpoint_descriptor *endpoint;
       int i = 0;

       usbdev = interface_to_usbdev(intf);
       interface = intf->cur_altsetting;

       printk(KERN_INFO "%s:num:%d \n",__func__,interface->desc.bNumEndpoints);

       for (i = 0;i<interface->desc.bNumEndpoints;i++)
       {
              endpoint = &interface->endpoint[0].desc;
              printk(KERN_INFO "%s: %x %x\n",__func__,endpoint->bEndpointAddress,endpoint->bmAttributes);
              printk(KERN_INFO "%s: %x %x %x\n",__func__,interface->desc.bInterfaceClass,interface->desc.bInterfaceSubClass,interface->desc.bInterfaceProtocol);
       }

       return 0;
}

static void zslusb_disconnect(struct usb_interface *intf)
{
       printk(KERN_INFO "%s: \n",__func__);
       dump_stack();
}

static struct usb_device_id zslusb_id_table [] = {
       { USB_DEVICE(0x413D, 0x2113) },
       { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_KEYBOARD)},
       { }    /* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, zsl_usb_id_table);

static struct usb_driver zslusb_driver = {
       .name            = "zslusb",
       .probe           = zslusb_probe,       /* usb设备插入时,id_table信息匹配成功则调用 */
       .disconnect    = zslusb_disconnect,  /* usb设备拔出时调用 */
       .id_table  = zslusb_id_table,    /* id_table,用于匹配设备描述符 */
};

void zslusb_init(void)
{     
       usb_register(&zslusb_driver);    /* 注册usb设备驱动,不申请设备号 */
}

执行后运行,USB设备插入和拔出就会调用zslusb_probe和zslusb_disconnect接口。

结果如下:

  • 插入设备

  • 拔掉设备

调用关系

设备注册是调用usb_register接口注册,本质是usb_register_driver,在这个接口里主要是填充struct usb_driver结构体,把bus = &usb_bus_type,然后调用driver_register注册驱动。

设备插入时如果主机控制器处于休眠状态,会先通过 hcd_resume_work 恢复供电,调用 rpm_resume 和 usb_runtime_resume 唤醒 Hub和恢复设备。随后 Hub 检测到端口变化,通过hub工作队列 hub_wq 触发 hub_event,从而usb_new_device初始化新USB设备并device_add把USB设备注册到内核设备树,触发首次总线探测,bus_probe_device里遍历所有驱动,对每个驱动执行__device_attach_driver,通过match匹配到设备后调用通用USB驱动usb_generic_driver_probe,确保设备能被识别成基本USB设备,读取设备描述符和分配资源。配置完成后,为每个接口注册新设备(device_add),触发二次总线探测,匹配后调用入口函数usb_probe_interface。

设备插入
├─ 休眠状态? → hcd_resume_work → rpm_resume → usb_runtime_resume → hub_resume
└─ Hub检测 → hub_wq → hub_event → usb_new_device → device_add
    ├─ 首次探测 → usb_generic_driver_probe(基础初始化)
    └─ 接口注册 → device_add → usb_probe_interface(驱动加载)

设备拔出是由硬件中断触发hub_irq,标记事件并触发软中断tasklet_hi_action,下半部Tasklet调用 usb_giveback_urb_bh调度hub工作队列 hub_wq触发hub_event,调用usb_disconnect清理USB设备,禁用端点,device_del调用device_release_driver解绑驱动(释放软件资源),禁用设备(停用硬件端点),释放设备内存。

设备拔出 → hub_irq → tasklet_hi_action → usb_giveback_urb_bh → hub_wq → hub_event
    ├─ usb_disconnect → usb_disable_device(禁用端点+终止URB)
    └─ device_del → device_release_driver → zslusb_disconnect(驱动清理) → usb_put_dev(内存释放)

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • PHP程序员玩转Linux系列 Linux和Windows安装nginx

    PHP程序员玩转Linux系列 Linux和Windows安装nginx

    这篇文章主要为大家详细介绍了PHP程序员玩转Linux系列文章,Linux和Windows安装nginx教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • 服务器安装宝塔面板无法远程连接数据库的解决方法

    服务器安装宝塔面板无法远程连接数据库的解决方法

    这篇文章主要介绍了服务器安装宝塔面板无法远程连接数据库的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • 解决xampp自启动和mysql.sock问题

    解决xampp自启动和mysql.sock问题

    Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’
    2010-10-10
  • 详解Centos7.2安装Nginx实现负载平衡

    详解Centos7.2安装Nginx实现负载平衡

    本篇文章主要介绍了详解Centos7.2安装Nginx实现负载平衡,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • 在Ubuntu 24.04(Noble)上配置阿里源的操作步骤

    在Ubuntu 24.04(Noble)上配置阿里源的操作步骤

    在使用 Ubuntu 系统时,选择一个快速且稳定的软件源(Repository)对于系统更新和软件安装至关重要,阿里云提供的 Ubuntu 镜像源在国内用户中非常受欢迎,本文将介绍如何在 Ubuntu 24.04 (Noble) 上配置阿里源,需要的朋友可以参考下
    2025-07-07
  • centos查找已安装的jdk路径的方法

    centos查找已安装的jdk路径的方法

    本篇文章主要介绍了centos查找已安装的jdk路径的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • Apache RewriteBase 指令使用介绍

    Apache RewriteBase 指令使用介绍

    RewriteBase指令显式地设置了目录级重写的基准URL。在下文中,你将看到RewriteRule可以用于目录级的配置文件中(.htaccess)并在局部范围内起作用,即规则实际处理的只是剥离了本地路径前缀的一部分
    2016-04-04
  • Linux中大内存页Oracle数据库优化的方法

    Linux中大内存页Oracle数据库优化的方法

    这篇文章主要给大家介绍了关于Linux中大内存页Oracle数据库优化的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • linux查找大文件指定内容的实现方法

    linux查找大文件指定内容的实现方法

    今天小编就为大家分享一篇linux查找大文件指定内容的实现方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • Kafka使用入门教程

    Kafka使用入门教程

    Kafka是一个分布式的、可分区的、可复制的消息系统。它提供了普通消息系统的功能,但具有自己独特的设计。这个独特的设计是什么样的呢
    2015-12-12

最新评论