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控制器驱动已经把数据给我们传上来了. 这不需要我们关心.
底层把描述符提交给上层,上层会继续分析数据, 我这里提供两个调用栈
用实例说明两种操作,它们的执行路径是不一样的,
- 先插设备,再装驱动的调用栈
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 函数.
- 先装驱动,再插设备的调用栈
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 函数.
它要完成的功能通过代码来说明.
- 函数的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);