ubuntu系统 usbmouse 驱动代码分析


author: hjjdebug

date: 2026年 05月 20日 星期三 19:44:38 CST

descrip: ubuntu系统 usbmouse 驱动代码分析


文章目录

  • [1. 何时调用usb_mouse_probe 函数?](#1. 何时调用usb_mouse_probe 函数?)
  • [2. usb_mouse_probe 函数, 它的任务是什么?](#2. usb_mouse_probe 函数, 它的任务是什么?)
  • [3. usb_mouse_open,usb_mouse_close 函数说明.](#3. usb_mouse_open,usb_mouse_close 函数说明.)
  • [4. URB 回调函数 usb_mouse_irq](#4. URB 回调函数 usb_mouse_irq)
  • [5. 附录: usbmouse 源码带注释和自修正变量(基于5.14.156版本注释改编)](#5. 附录: usbmouse 源码带注释和自修正变量(基于5.14.156版本注释改编))

用lsusb -t 查看, 可知usb鼠标驱动是usbhid
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/11p, 480M
|__ Port 1: Dev 6, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M

usbhid.ko 是由hid-core.o,hiddev.o,hid-pidff.o

用它去驱动usb鼠标太复杂了.

我们用usbmouse.ko 来驱动鼠标,代码简单,才232行

准备: 卸载usbhid驱动,

当我把/lib/modules/5.4.0-150-generic/kernel/drivers/hid/usbhid/usbhid.ko改名为

/lib/modules/5.4.0-150-generic/kernel/drivers/hid/usbhid/usbhid.ko.bak

结果发现上电还是安装了usbhid.ko

那它是从那来的呢?

会不会是缓存中的, 用depmod -a 重新生成依赖. 结果上电还是安装了usbhid.ko

可见depmod -a 只是生成依赖,不会保留驱动.

后来查到应该是内核启动时ramfs 系统就加载了,所以需要depmod -a 后,再更新内存文件系统

sudo update-initramfs -u -k $(uname -r)

再上电启动,则usb鼠标就没有驱动了, usb鼠标也不工作了.

用modprobe usbmouse 可以是usb鼠标正常工作.

现在开始, 专门编译usbmouse.c,生成usbmouse.ko

删除系统usbmouse.ko, rmmod usbmouse

安装我们新编译的模块, insmod ./usbmouse.ko

驱动研究开始, 代码见附录,加了注释,部分修改了变量名称,代码逻辑没有变.

1. 何时调用usb_mouse_probe 函数?

由id_table 匹配表决定, 当满足接口类时HID,子类为boot,接口协议为鼠标时,调用probe 函数.

要想理解probe 函数, 首先需要了解它的调用流程.(简单了解即可)

插入鼠标,D+,D-线上会有电平变化, usb控制器会发出设备插入中断请求.计算机响应,进入usb枚举流程.

usb 枚举过程,是通过usb主控器的驱动来完成数据交互.

主机会问: 你是谁?(就是问你的设备表述符是什么?) 你有几个配置(1般就一个),你有几个接口,一般也是一个

因为一个接口就对应着一种功能, 除非你这个设备有多种功能, 例如即当键盘,又当鼠标,那就可以设2个接口.

再问每个接口有几个端点, 端点决定了用什么方式传递数据?

总之,usb控制器驱动已经把数据给我们传上来了. 这不需要我们关心.

底层把描述符提交给上层,上层会继续分析数据, 我这里提供两个调用栈

用实例说明两种操作,它们的执行路径是不一样的,

  1. 先插设备,再装驱动的调用栈
    0 in usb_mouse_probe of /home/hjj/test/usbmouse/usbmouse.c:178
    1 in usb_probe_interface of drivers/usb/core/driver.c:361
    2 in really_probe of drivers/base/dd.c:558
    3 in driver_probe_device of drivers/base/dd.c:727
    4 in device_driver_attach of drivers/base/dd.c:1003
    5 in __driver_attach of drivers/base/dd.c:1080
    6 in bus_for_each_dev of drivers/base/bus.c:304
    7 in driver_attach of drivers/base/dd.c:1096
    8 in bus_add_driver of drivers/base/bus.c:621
    9 in driver_register of drivers/base/driver.c:170
    10 in usb_register_driver of drivers/usb/core/driver.c:962
    11 in usb_mouse_driver_init of /home/hjj/test/usbmouse/usbmouse.c:322
    12 in do_one_initcall
    13 in do_init_module of kernel/module.c:3723
    14 in load_module of kernel/module.c:4103
    15 in __do_sys_finit_module of kernel/module.c:4194
    16 in __se_sys_finit_module of kernel/module.c:4170
    17 in __x64_sys_finit_module of kernel/module.c:4170
    18 in do_syscall_64
    19 in entry_SYSCALL_64 of arch/x86/entry/entry_64.S:175
    20 in ??

先有设备,再有驱动,走的调用短. 驱动注册的时候, 驱动去依次匹配device.

嗯!, 匹配上了, 直接调用probe 函数.

  1. 先装驱动,再插设备的调用栈
    0 in usb_mouse_probe of /home/hjj/test/usbmouse/usbmouse.c:178
    1 in usb_probe_interface of drivers/usb/core/driver.c:361
    2 in really_probe of drivers/base/dd.c:558
    3 in driver_probe_device of drivers/base/dd.c:727
    4 in __device_attach_driver of drivers/base/dd.c:834
    5 in bus_for_each_drv of drivers/base/bus.c:430
    6 in __device_attach of drivers/base/dd.c:902
    7 in device_initial_probe of drivers/base/dd.c:949
    8 in bus_probe_device of drivers/base/bus.c:490
    9 in device_add of drivers/base/core.c:2216
    10 in usb_set_configuration of drivers/usb/core/message.c:2029
    11 in generic_probe of drivers/usb/core/generic.c:210
    12 in usb_probe_device of drivers/usb/core/driver.c:266
    13 in really_probe of drivers/base/dd.c:558
    14 in driver_probe_device of drivers/base/dd.c:727
    15 in __device_attach_driver of drivers/base/dd.c:834
    16 in bus_for_each_drv of drivers/base/bus.c:430
    17 in __device_attach of drivers/base/dd.c:902
    18 in device_initial_probe of drivers/base/dd.c:949
    19 in bus_probe_device of drivers/base/bus.c:490
    20 in device_add of drivers/base/core.c:2216
    21 in usb_new_device of drivers/usb/core/hub.c:2559
    22 in hub_port_connect of drivers/usb/core/hub.c:5172
    23 in hub_port_connect_change of drivers/usb/core/hub.c:5287
    24 in port_event of drivers/usb/core/hub.c:5433
    25 in hub_event of drivers/usb/core/hub.c:5513
    26 in process_one_work
    27 in worker_thread
    28 in kthread
    29 in ret_from_fork of arch/x86/entry/entry_64.S:352
    30 in ??

先有驱动,后有设备.

则驱动安装时,匹配不到设备,所以不会走到probe回调. 就休息了.

等到再安装设备时.在第一个hub,第一个端口上发现了一个新的usb设备,把它添加上来device_add,然后为这个设备找驱动,

先在usb总线上找,找到usb驱动,读取它的配置(usb_set_configuration),发现它是

hid设备, 添加这个设备,第二次device_add, 找它的驱动,找到了mouse驱动,执行mouse_probe

2. usb_mouse_probe 函数, 它的任务是什么?

它要完成什么功能?

从上边调用知,不管怎么操作,最后都要汇总到driver_probe_device,即设备和驱动匹配上了,而调用到usb_mouse_probe 函数.

它要完成的功能通过代码来说明.

  1. 函数的2个输入参数.
    第2个参数 usb_device_id *id, 它指出是匹配了id_table表中的哪一项. 例子中只有一项,故肯定是匹配到了接口类3,接口子类1,协议类1 这一项
    第1个参数. struct usb_interface *intf
    我们知道,usb总线是星形拓扑,每个设备都连在一个端口上. 就是说靠总线-端口来找到设备.
    那这到底插的是什么设备呢? 这要看它的interface. interface 描述了一种功能.
    它可能是键盘,可能是鼠标,可能是u盘,说明它们是不同的interface.
    按照usb协议,一个设备对应一个设备描述符,设备描述符可以包含多个配置描述符,
    不过一般都是一个配置描述符.
    一个配置描述符可以包含多个接口描述符,但一般都是一个功能描述符. 例如,你造了一个设备即当键盘,又当U盘,那就可以用2个接口描述符.
    这种奇怪的设备我还没见过,因为没有这种要求吧.
    但一个配置包含一个键盘,一个鼠标这两个接口的设备还是有的,只是极少见. 像联想的一种少见的键盘,中间有一个红色的按钮可上下左右摇晃充当鼠标功能.
    不过一般还是一个配置只一个接口的据多数.
    这样我们说请的接口的概念. 而且probe时接口指针是有值的. 那是usb驱动给传来的数据,此是已经知道接口是什么了.

首先检查接口的端点个数, 不为1返回.

if (interface->desc.bNumEndpoints != 1) return ;

然后检查端点的类型,不是中断类型,返回

if (!usb_endpoint_is_int_in(endpoint)) return

分配鼠标私有数据,并初始化私有数据结构.

mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);

mouse->usb_dev = usb_dev;

mouse->in_dev = input_allocate_device();

mouse->data = usb_alloc_coherent(usb_dev, 8, GFP_ATOMIC, &mouse->data_dma);

mouse->irq = usb_alloc_urb(0, GFP_KERNEL);

mouse->name 赋值

mouse->phys 赋值

初始化input_dev.

input_dev->name

input_dev->phys

input_dev->id

input_dev->keybit(left,right,middle,side)

input_dev->relbit(X,Y,W)

input_dev->open

input_dev->close

初始化mouse->irq

usb_fill_int_urb(...)

注册input设备: input_register_device()

关联私有数据到usb接口: usb_set_intfdata()

probe 的功能是分配资源,进行初始化,并注册输入子设备,设置回调函数. 使usb鼠标处于可运行状态

probe 的 逆操作是disconnect, 释放所分配的资源.

3. usb_mouse_open,usb_mouse_close 函数说明.

这2个函数是用户进程调用的驱动函数

当打开输入设备时,会调用到open.

功能,提交中断URB, 开始接受鼠标数据.

close 则时停止接受鼠标数据

4. URB 回调函数 usb_mouse_irq

实际上是中断响应回调函数usb_mouse_irq

何时调用usb_mouse_irq, 为什么叫irq, 它完成什么工作?

0 in usb_mouse_irq of /home/hjj/test/usbmouse/usbmouse.c:62

1 in __usb_hcd_giveback_urb of drivers/usb/core/hcd.c:1654

2 in usb_hcd_giveback_urb of drivers/usb/core/hcd.c:1719

3 in uhci_giveback_urb of drivers/usb/host/uhci-q.c:1556

4 in uhci_scan_qh of drivers/usb/host/uhci-q.c:1601

5 in uhci_scan_schedule of drivers/usb/host/uhci-q.c:1769

6 in uhci_scan_schedule of drivers/usb/host/uhci-hcd.c:506

7 in uhci_irq of drivers/usb/host/uhci-hcd.c:500

8 in usb_hcd_irq of drivers/usb/core/hcd.c:2326

9 in __handle_irq_event_percpu of kernel/irq/handle.c:149

10 in handle_irq_event_percpu of kernel/irq/handle.c:189

11 in handle_irq_event of kernel/irq/handle.c:206

12 in handle_fasteoi_irq of kernel/irq/chip.c:728

13 in generic_handle_irq_desc of ./include/linux/irqdesc.h:156

14 in do_IRQ of arch/x86/kernel/irq.c:250

15 in common_interrupt of arch/x86/entry/entry_64.S:607

16 in ??

这个链路是中断响应函数, irq实际是起名不当,不过也就这么用了.

从总的中断服务入口common_interrupt, 到usb控制器的中断服务入口usb_hcd_irq

都是中断响应的过程,最后到usb_mouse_irq回调, irq起名中断请求当中断响应来理解

就是说,数据上来了,usb控制器发了中断,在中断响应程序中,数据提交到了驱动这里.

数据在参数struct urb *urb 中. 交由驱动函数usb_mouse_irq( urb) 来处理.

函数要实现的功能.

要把鼠标数据缓冲区数据,上报给输入子系统设备.

复制代码
input_report_key(in_dev, BTN_LEFT,   data[0] & 0x01);    // 左键
input_report_key(in_dev, BTN_RIGHT,  data[0] & 0x02);    // 右键
input_report_key(in_dev, BTN_MIDDLE, data[0] & 0x04);    // 中键
input_report_key(in_dev, BTN_SIDE,   data[0] & 0x08);    // 侧键1
input_report_key(in_dev, BTN_EXTRA,  data[0] & 0x10);    // 侧键2
input_report_rel(in_dev, REL_X,     data[1]);            // X轴位移
input_report_rel(in_dev, REL_Y,     data[2]);            // Y轴位移
input_report_rel(in_dev, REL_WHEEL, data[3]);            // 滚轮位移
input_sync(in_dev);

再此提交urb, 告诉主机控制器,有数往这里填.

问题: usb 鼠标多长时间执行一次中断?

这由端点描述符中的bInterval 决定,一般时10ms 一次

总结:

鼠标驱动的任务就是周期性的把接受到的数据(usb鼠标设备产生的),

按输入子系统的要求把数据交给上层虚拟输入设备mouse->in_dev

5. 附录: usbmouse 源码带注释和自修正变量(基于5.14.156版本注释改编)

cpp 复制代码
$ cat usbmouse.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Copyright (c) 1999-2001 Vojtech Pavlik
 *
 *  USB HIDBP Mouse support
 *  USB HID Boot Protocol(HIDBP)鼠标驱动实现
 */

/*
 *
 * Should you need to contact me, the author, you can do so either by
 * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
 * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
 */

#include <linux/kernel.h>     // 内核核心功能头文件
#include <linux/slab.h>       // 内存分配/释放函数(kzalloc/kfree等)
#include <linux/module.h>     // 模块加载/卸载相关
#include <linux/init.h>       // 初始化/清理宏(module_init/module_exit)
#include <linux/usb/input.h>  // USB输入设备核心头文件
#include <linux/hid.h>        // HID设备相关头文件

#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
/*
 * Version Information
 * 驱动版本信息定义
 */
#define DRIVER_VERSION "v1.6"                  // 驱动版本
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"  // 驱动作者
#define DRIVER_DESC "USB HID Boot Protocol mouse driver" // 驱动描述

// 模块元信息声明
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");  // 开源协议

/*
 * 定义USB鼠标设备私有数据结构
 * 存储鼠标设备的核心信息和资源句柄
 */
struct usb_mouse {
	char name[128];          // 设备名称(厂商+产品名)
	char phys[64];           // 设备物理路径(如usbX/Y/Z/input0)
	struct usb_device *usb_dev;  // 指向USB设备结构体的指针
	struct input_dev *in_dev;   // 指向输入子系统设备的指针
	struct urb *irq;         // 中断URB(用于接收鼠标数据)

	signed char *data;       // 接收鼠标数据的缓冲区
	dma_addr_t data_dma;     // data缓冲区的DMA地址
};

/*
 * USB鼠标中断URB回调函数
 * 当USB控制器接收到鼠标中断数据时调用
 * @urb: 触发回调的URB结构体指针
 */
static void usb_mouse_irq(struct urb *urb)
{
	struct usb_mouse *mouse = urb->context;  // 获取鼠标私有数据
	signed char *data = mouse->data;         // 鼠标数据缓冲区
	struct input_dev *in_dev = mouse->in_dev;      // 输入子系统设备
	int status;                              // 函数返回状态

	// 检查URB传输状态
	switch (urb->status) {
	case 0:			/* 传输成功 */
		break;
	case -ECONNRESET:	/* 连接重置 */
	case -ENOENT:        /* 设备不存在 */
	case -ESHUTDOWN:     /* 设备关闭 */
		return;          // 这些状态无需重试,直接返回
	/* -EPIPE: 需清除端点暂停状态(此处未处理,直接重试) */
	default:		/* 其他错误,跳转到重新提交URB */
		goto resubmit;
	}

	// 上报鼠标按键事件
	input_report_key(in_dev, BTN_LEFT,   data[0] & 0x01);    // 左键
	input_report_key(in_dev, BTN_RIGHT,  data[0] & 0x02);    // 右键
	input_report_key(in_dev, BTN_MIDDLE, data[0] & 0x04);    // 中键
	input_report_key(in_dev, BTN_SIDE,   data[0] & 0x08);    // 侧键1
	input_report_key(in_dev, BTN_EXTRA,  data[0] & 0x10);    // 侧键2

	// 上报鼠标相对位移事件
	input_report_rel(in_dev, REL_X,     data[1]);            // X轴位移
	input_report_rel(in_dev, REL_Y,     data[2]);            // Y轴位移
	input_report_rel(in_dev, REL_WHEEL, data[3]);            // 滚轮位移

	// 同步输入事件(告知输入子系统数据已完整)
	input_sync(in_dev);

resubmit:
	// 重新提交URB以继续接收下一次中断数据
	status = usb_submit_urb (urb, GFP_ATOMIC);
	if (status)  // 提交失败则打印错误日志
		dev_err(&mouse->usb_dev->dev,
			"can't resubmit intr, %s-%s/input0, status %d\n",
			mouse->usb_dev->bus->bus_name,
			mouse->usb_dev->devpath, status);
}

/*
 * 输入设备打开函数
 * 当输入子系统打开设备时调用,启动鼠标数据接收
 * @in_dev: 输入子系统设备指针
 * 返回:0成功,非0失败
 */
static int usb_mouse_open(struct input_dev *in_dev)
{
	// 从输入设备中获取鼠标私有数据
	struct usb_mouse *mouse = input_get_drvdata(in_dev);

	// 绑定URB到USB设备
	mouse->irq->dev = mouse->usb_dev;
	// 提交中断URB,开始接收鼠标数据
	if (usb_submit_urb(mouse->irq, GFP_KERNEL))
		return -EIO;  // 提交失败返回I/O错误

	return 0;
}

/*
 * 输入设备关闭函数
 * 当输入子系统关闭设备时调用,停止鼠标数据接收
 * @in_dev: 输入子系统设备指针
 */
static void usb_mouse_close(struct input_dev *in_dev)
{
	// 从输入设备中获取鼠标私有数据
	struct usb_mouse *mouse = input_get_drvdata(in_dev);

	// 终止URB传输(停止接收鼠标数据)
	usb_kill_urb(mouse->irq);
}

/*
 * USB设备探测函数
 * 当系统匹配到USB鼠标设备时调用,由tableID 匹配, 
 * 完成设备初始化
 * @intf: USB接口结构体指针
 * @id: 匹配的USB设备ID表项
 * 返回:0成功,非0失败
 */
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	int error = -ENOMEM;                                 // 默认错误码(内存不足)

	// 获取当前接口的活动设置
	struct usb_host_interface *interface;                // USB接口设置
	interface = intf->cur_altsetting;

	// 检查端点数量:HID鼠标仅需1个中断端点
	struct usb_endpoint_descriptor *endpoint;            // 端点描述符
	if (interface->desc.bNumEndpoints != 1)
		return -ENODEV;

	// 获取端点描述符
	endpoint = &interface->endpoint[0].desc;
	// 检查端点类型:必须是中断输入端点
	if (!usb_endpoint_is_int_in(endpoint))
		return -ENODEV;


	// 分配鼠标私有数据和
	struct usb_mouse *mouse;                             // 鼠标私有数据
	mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
	if (!mouse)
		goto fail1;  // 内存分配失败,跳转到清理流程

	// 初始化鼠标私有数据
	struct usb_device *usb_dev = interface_to_usbdev(intf);  // USB设备结构体
	mouse->usb_dev = usb_dev;
	//输入设备内存
	struct input_dev *input_dev;                         // 输入子系统设备
	input_dev = input_allocate_device();
	if (!input_dev)
		goto fail1;  // 内存分配失败,跳转到清理流程
	mouse->in_dev = input_dev;

	// 分配DMA一致的缓冲区(用于接收鼠标数据)
	mouse->data = usb_alloc_coherent(usb_dev, 8, GFP_ATOMIC, &mouse->data_dma);
	if (!mouse->data)
		goto fail1;


	// 分配中断URB
	mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
	if (!mouse->irq)
		goto fail2;


	// 构建设备名称(优先使用厂商+产品名)
	if (usb_dev->manufacturer)
		strlcpy(mouse->name, usb_dev->manufacturer, sizeof(mouse->name));

	if (usb_dev->product) {
		if (usb_dev->manufacturer)
			strlcat(mouse->name, " ", sizeof(mouse->name));
		strlcat(mouse->name, usb_dev->product, sizeof(mouse->name));
	}

	// 如果无厂商/产品名,使用VID/PID构建默认名称
	if (!strlen(mouse->name))
		snprintf(mouse->name, sizeof(mouse->name),
			 "USB HIDBP Mouse %04x:%04x",
			 le16_to_cpu(usb_dev->descriptor.idVendor),  // 厂商ID(小端转主机序)
			 le16_to_cpu(usb_dev->descriptor.idProduct)); // 产品ID(小端转主机序)

	// 构建设备物理路径
	usb_make_path(usb_dev, mouse->phys, sizeof(mouse->phys));
	strlcat(mouse->phys, "/input0", sizeof(mouse->phys));

	// 初始化输入设备属性
	input_dev->name = mouse->name;                // 设备名称
	input_dev->phys = mouse->phys;                // 物理路径
	usb_to_input_id(usb_dev, &input_dev->id);         // 转换USB ID到输入设备ID
	input_dev->dev.parent = &intf->dev;           // 父设备指向USB接口

	// 设置输入设备支持的事件类型
	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);  // 按键+相对位移
	// 设置支持的鼠标按键
	input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
		BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
	// 设置支持的相对位移轴
	input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
	// 追加支持的侧键
	input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
		BIT_MASK(BTN_EXTRA);
	// 追加支持的滚轮
	input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);

	// 将鼠标私有数据关联到输入设备
	input_set_drvdata(input_dev, mouse);

	// 设置输入设备的打开/关闭回调
	input_dev->open = usb_mouse_open;
	input_dev->close = usb_mouse_close;

	// 创建中断输入管道
	int pipe, maxp;                                      // 管道号、最大包长度
	pipe = usb_rcvintpipe(usb_dev, endpoint->bEndpointAddress);
	// 获取端点最大包长度
	maxp = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe));

	// 填充中断URB参数
	usb_fill_int_urb(mouse->irq, usb_dev, pipe, mouse->data,
			 (maxp > 8 ? 8 : maxp),  // 数据长度(最多8字节)
			 usb_mouse_irq,          // URB回调函数
			 mouse,                  // 回调函数上下文(鼠标私有数据)
			 endpoint->bInterval);   // 中断端点轮询间隔
	mouse->irq->transfer_dma = mouse->data_dma;  // DMA地址
	// 标记无需映射DMA(已通过usb_alloc_coherent分配)
	mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	// 注册输入设备到输入子系统
	error = input_register_device(mouse->in_dev);
	if (error)
		goto fail3;

	// 将鼠标私有数据关联到USB接口
	usb_set_intfdata(intf, mouse);
	return 0;  // 探测成功

// 错误处理流程:逐级释放已分配的资源
fail3:	
	usb_free_urb(mouse->irq);  // 释放URB
fail2:	
	// 释放DMA缓冲区
	usb_free_coherent(usb_dev, 8, mouse->data, mouse->data_dma);
fail1:	
	input_free_device(input_dev);  // 释放输入设备
	kfree(mouse);                  // 释放鼠标私有数据
	return error;                  // 返回错误码
}

/*
 * USB设备断开连接回调函数
 * 当USB鼠标设备断开时调用,释放相关资源
 * @intf: USB接口结构体指针
 */
static void usb_mouse_disconnect(struct usb_interface *intf)
{
	// 从USB接口获取鼠标私有数据
	struct usb_mouse *mouse = usb_get_intfdata (intf);

	// 清除接口关联的私有数据
	usb_set_intfdata(intf, NULL);
	if (mouse) {
		usb_kill_urb(mouse->irq);  // 终止URB传输
		usb_free_urb(mouse->irq);  // 释放URB
		input_unregister_device(mouse->in_dev);  // 注销输入设备
		// 释放DMA缓冲区
		usb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
		kfree(mouse);  // 释放鼠标私有数据
	}
}

/*
 * USB设备ID匹配表
 * 匹配符合HID Boot Protocol的鼠标设备
 */
static const struct usb_device_id usb_mouse_id_table[] = {
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,        // 接口类:HID
		USB_INTERFACE_SUBCLASS_BOOT,                   // 接口子类:Boot协议
		USB_INTERFACE_PROTOCOL_MOUSE) },               // 接口协议:鼠标
	{ }	/* 结束表项 */
};

// 声明USB设备ID表(用于模块自动加载)
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);

/*
 * USB驱动结构体
 * 定义USB鼠标驱动的核心回调和属性
 */
static struct usb_driver usb_mouse_driver = {
	.name		= "usbmouse",               // 驱动名称
	.probe		= usb_mouse_probe,          // 设备探测函数
	.disconnect	= usb_mouse_disconnect,     // 设备断开函数
	.id_table	= usb_mouse_id_table,       // 设备ID匹配表
};

// 注册USB驱动(简化宏,替代module_init/module_exit)
module_usb_driver(usb_mouse_driver);
相关推荐
认真的薛薛5 小时前
Linux运维:Jenkins+Argocd
linux·运维·jenkins
神秘剑客_CN5 小时前
ubuntu26.04音频转srt字幕
ubuntu·conda·whisperx
windawdaysss5 小时前
使用VMware Workstation Pro安装Ubuntu虚拟机教程
linux·运维·ubuntu
宋浮檀s5 小时前
Linux后门持久化排查
linux·运维·服务器
xiaobobo33305 小时前
ubuntu14.04搭建Samba服务器实现文件共享
ubuntu·samba·共享文件夹
xuhaoyu_cpp_java5 小时前
Linux学习(一)
linux·经验分享·笔记·学习
小此方5 小时前
Re: Linux系统篇(十八)进程篇·三:深度硬核!全面起底 Linux 进程状态变化与内核链表动态解绑
linux·驱动开发·链表
米高梅狮子14 小时前
03.网络类服务实践
linux·运维·服务器·网络·kubernetes·centos·openstack
June`14 小时前
网络编程时内核究竟做了什么???
linux·服务器·网络