USB子系统学习(四)使用libusb读取鼠标数据
文章目录
- 1、声明
- 2、HID协议
- 2.1、描述符
- 2.2、鼠标数据格式
- 3、应用程序
- 4、编译应用程序
- 5、测试
1、声明
本文是在学习韦东山《驱动大全》USB子系统时,为梳理知识点和自己回看而记录,全部内容高度复制粘贴。
韦老师的《驱动大全》:商品详情
其对应的讲义资料:https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
libusb api:https://libusb.sourceforge.io/api-1.0/libusb_api.html
2、HID协议
HID:Human Interface Devices, 人类用来跟计算机交互的设备。就是鼠标、键盘、游戏手柄等设备。对于USB接口的HID设备,有一套协议。
2.1、描述符
HID设备有如下描述符:

- HID设备的"设备描述符"并无实际意义,没有使用"设备描述符"来表示自己是HID设备。
- HID设备只有一个配置,所以只有一个配置描述符。
- 接口描述符:
- bInterfaceClass为3,表示它是HID设备。
- bInterfaceSubClass是0或1,1表示它支持"Boot Interface"(表示PC的BIOS能识别、使用它),0表示必须等操作系统启动后通过驱动程序来使用它。
- bInterfaceProtocol:0-None, 1-键盘, 2-鼠标。
- 端点描述符:HID设备有一个控制端点、一个中断端点。

对于鼠标,HOST可以通过中断端点读到数据。
2.2、鼠标数据格式
通过中断传输可以读到鼠标数据,它是8字节的数据,格式如下:
| 偏移 | 大小 | 描述 |
|---|---|---|
| 0 | 1字节 | |
| 1 | 1字节 | 按键状态 |
| 2 | 2字节 | X位移 |
| 4 | 2字节 | Y位移 |
| 6 | 1字节或2字节 | 滚轮 |
按键状态里,每一位对应鼠标的一个按键,等1时表示对应按键被点击了,格式如下:
| 位 | 长度 | 描述 |
|---|---|---|
| 0 | 1 | 鼠标的左键 |
| 1 | 1 | 鼠标的右键 |
| 2 | 1 | 鼠标的中间键 |
| 3 | 5 | 保留,设备自己定义bit3: 鼠标的侧边按键bit4: |
X位移、Y位移都是8位的有符号数。对于X位移,负数表示鼠标向左移动,正数表示鼠标向右移动,移动的幅度就使用这个8位数据表示。对于Y位移,负数表示鼠标向上移动,正数表示鼠标向下移动,移动的幅度就使用这个8位数据表示。
3、应用程序
本次应用程序是使用同步接口读取鼠标数据。
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libusb-1.0/libusb.h>int main(int argc, char **argv)
{int err;libusb_device *dev, **devs;int num_devices;int endpoint;int interface_num;int transferred;int count = 0;unsigned char buffer[8];struct libusb_config_descriptor *config_desc;struct libusb_device_handle *dev_handle = NULL;int found = 0;/* libusb init */err = libusb_init(NULL);if (err < 0) {fprintf(stderr, "failed to initialise libusb %d - %s\n", err, libusb_strerror(err));exit(1);}/* get device list */if ((num_devices = libusb_get_device_list(NULL, &devs)) < 0) // 获取设备描述符列表(函数返回设备描述符数量){fprintf(stderr, "libusb_get_device_list() failed\n");libusb_exit(NULL);exit(1);} fprintf(stdout, "libusb_get_device_list() ok\n");/* for each device, get config descriptor */for (int i = 0; i < num_devices; i++){dev = devs[i];err = libusb_get_config_descriptor(dev, 0, &config_desc); // 获取配置描述符if (err) {fprintf(stderr, "could not get configuration descriptor\n");continue;}fprintf(stdout, "libusb_get_config_descriptor() ok\n");/* parse interface descriptor, find usb mouse */for (int interface = 0; interface < config_desc->bNumInterfaces; interface++) // 枚举所有接口描述符{const struct libusb_interface_descriptor *intf_desc = &config_desc->interface[interface].altsetting[0]; // 获取配置描述符里的第一个接口描述符interface_num = intf_desc->bInterfaceNumber; // 记录该接口描述符的编号(编号是从0开始)if (intf_desc->bInterfaceClass != 3 || intf_desc->bInterfaceProtocol != 2) // 判断是否是HID设备和是否是鼠标协议continue;/* 找到了USB鼠标 */fprintf(stdout, "find usb mouse ok\n");for (int ep = 0; ep < intf_desc->bNumEndpoints; ep++) // 枚举所有端点描述符{// 判断是否是中断传输,是否是输入端点(输入输出都是以USB Host来讨论,所以该端点是USB Device输出到USB Host)if ((intf_desc->endpoint[ep].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_INTERRUPT || (intf_desc->endpoint[ep].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN){/* 找到了输入的中断端点 */fprintf(stdout, "find in int endpoint\n");endpoint = intf_desc->endpoint[ep].bEndpointAddress;found = 1;break;}}if (found)break;}libusb_free_config_descriptor(config_desc);if (found)break; }if (!found){/* free device list */libusb_free_device_list(devs, 1);libusb_exit(NULL);exit(1);}/* libusb open */if (found){err = libusb_open(dev, &dev_handle);if (err){fprintf(stderr, "failed to open usb mouse\n");exit(1);}fprintf(stdout, "libusb_open ok\n");}/* free device list */libusb_free_device_list(devs, 1);/* claim interface */libusb_set_auto_detach_kernel_driver(dev_handle, 1); err = libusb_claim_interface(dev_handle, interface_num);if (err){fprintf(stderr, "failed to libusb_claim_interface\n");exit(1);}fprintf(stdout, "libusb_claim_interface ok\n");/* libusb interrupt transfer */while (1){err = libusb_interrupt_transfer(dev_handle, endpoint, buffer, 8, &transferred, 5000); // 发起中断传输,阻塞等待,5s超时时间if (!err) {/* parser data */printf("%04d datas: ", count++);printf("recv datas len = %d\n", transferred);for (int i = 0; i < transferred; i++){printf("%02x ", buffer[i]);}printf("\n");} else if (err == LIBUSB_ERROR_TIMEOUT){fprintf(stderr, "libusb_interrupt_transfer timout\n");} else {const char *errname = libusb_error_name(err);fprintf(stderr, "libusb_interrupt_transfer err : %d, %s\n", err, errname);//exit(1);}}/* libusb close */libusb_release_interface(dev_handle, interface_num);libusb_close(dev_handle);libusb_exit(NULL);
}
4、编译应用程序
假设你的开发板是ubuntu系统:
# 安装libusb库
$ sudo apt install libusb-1.0-0-dev# 编译程序
$ gcc -o readmouse readmouse.c -lusb-1.0
5、测试
将usb鼠标插入开发板:

执行程序:
$ sudo ./readmouse

移动鼠标:

滚轮滑动:

按键状态:

另外,每个鼠标的数据格式是不一样的。以上测试结果只是我使用的鼠标。
相关文章:
USB子系统学习(四)使用libusb读取鼠标数据
文章目录 1、声明2、HID协议2.1、描述符2.2、鼠标数据格式 3、应用程序4、编译应用程序5、测试 1、声明 本文是在学习韦东山《驱动大全》USB子系统时,为梳理知识点和自己回看而记录,全部内容高度复制粘贴。 韦老师的《驱动大全》:商品详情 …...
【产品小白】用户调研的需求是否都采纳?
在用户调研中,并非所有需求都应被直接采纳,而应通过系统分析转化为符合产品战略的有效决策。以下是关键思考框架: 1. 用户需求 ≠ 产品需求 矛盾性:用户个体需求可能相互冲突(如A功能的去留),需…...
软件测试就业
文章目录 2.6 初识一、软件测试理论二、软件的生产过程三、软件测试概述四、软件测试目的五、软件开发与软件测试的区别?六、学习内容 2.7 理解一、软件测试的定义二、软件测试的生命周期三、软件测试的原则四、软件测试分类五、软件的开发与测试模型1.软件开发模型…...
qt部分核心机制
作业 1> 手动将登录项目实现,不要使用拖拽编程 并且,当点击登录按钮时,后台会判断账号和密码是否相等,如果相等给出登录成功的提示,并且关闭当前界面,发射一个跳转信号,如果登录失败&#…...
【RocketMQ】RocketMq之ConsumeQueue深入研究
目录 一:RocketMq 整体文件存储介绍 二:ConsumeQueue 的文件结构 三:ConsumeQueue 写入和查询流程 一:RocketMq 整体文件存储介绍 存储⽂件主要分为三个部分: CommitLog:存储消息的元数据。所有消息都会…...
如今物联网的快速发展对hmi的更新有哪些积极影响
一、功能更加丰富 物联网的快速发展使得 HMI(人机界面)能够连接更多的设备和系统,从而实现更加丰富的功能。例如,通过与传感器网络的连接,HMI 可以实时显示设备的运行状态、环境参数等信息,为用户提供更加…...
linux 性能60秒分析
linux 60秒分析 需要运行的工具是 1、uptime 2、dmesg | tail 3、vmstat 1 4、mpstat -P ALL 1 5、pidstat 1 6、iostat -xz 1 7、free -m 8、sar -n DEV 1 9、sar -n TCP,ETCP 1 10、topuptime 快速检查平均负载 [rootaaaaaa ~]# uptime15:17:20 up 3 days, 14 min, 7 us…...
Redisson全面解析:从使用方法到工作原理的深度探索
文章目录 写在文章开头详解Redisson基本数据类型基础配置字符串操作列表操作映射集阻塞队列延迟队列更多关于Redisson详解Redisson 中的原子类详解redisson中的发布订阅模型小结参考写在文章开头 Redisson是基于原生redis操作指令上进一步的封装,屏蔽了redis数据结构的实现细…...
neo4j-解决导入数据后出现:Database ‘xxxx‘ is unavailable. Run :sysinfo for more info.
目录 问题描述 解决方法 重新导入 问题描述 最近在linux上部署了neo4j,参照之前写的博客:neo4j-数据的导出和导入_neo4j数据导入导出-CSDN博客 进行了数据导出、导入操作。但是在进行导入后,重新登录网页版neo4j,发现对应的数据库状态变…...
51单片机之引脚图(详解)
8051单片机引脚分类与功能笔记 1. 电源引脚 VCC(第40脚):接入5V电源,为单片机提供工作电压。GND(第20脚):接地端,确保电路的电位参考点。 2.时钟引脚 XTAL1(第19脚&a…...
Hangfire.NET:.NET任务调度
引言:为何选择 Hangfire? 在开发.NET 应用程序时,我们常常会遇到这样的场景:应用程序需要定期发送报告,像财务报表,每日业务数据汇总报告等,这些报告需要定时生成并发送给相关人员;…...
深入解析:React 事件处理的秘密与高效实践
在 React 中,事件处理是构建交互式应用的核心。本文将带你深入探索 React 事件处理的机制、最佳实践以及如何避免常见陷阱,助你写出更高效、更健壮的代码。 1. React 事件处理的独特之处 合成事件(SyntheticEvent) React 使用合…...
开源像素字体,可用于独立游戏开发
方舟像素字体 / Ark Pixel Font 开源的泛中日韩像素字体,使用 SIL 开放字体许可证 第1.1版(SIL Open Font License 1.1)授权。 支持 10、12 和 16 像素尺寸。 支持语言特殊字形:中文-中国大陆、中文-香港特别行政区、中文-台湾…...
【论文阅读】Comment on the Security of “VOSA“
Comment on the Security of Verifiable and Oblivious Secure Aggregation for Privacy-Preserving Federated Learning -- 关于隐私保护联邦中可验证与遗忘的安全聚合的安全性 论文来源摘要Introduction回顾 VOSA 方案对VOSA不可伪造性的攻击对于类型 I 的攻击对于类型 II 的…...
了解传输层TCP协议
目录 一、TCP协议段格式 二、TCP原理 1.确认应答 2.超时重传 3.连接管理 建立连接 断开连接 4.滑动窗口 5.流量控制 6.拥塞控制 7.延时应答 8.捎带应答 9.面向字节流 10.TCP异常情况 TCP,即Transmission Control Protocol,传输控制协议。人如…...
flask实现用户名查重,重复的用户名阻止注册,以及如何优化
在 Flask 中实现用户名查重,并阻止重复的用户名进行注册,可以使用数据库(如 SQLite、MySQL、PostgreSQL)存储用户信息,并在注册时检查用户名是否已存在。以下是实现步骤: 1. 安装 Flask 及 SQLAlchemy 确保…...
ASP.NET Core对JWT的封装
目录 JWT封装 [Authorize]的注意事项 JWT封装 NuGet 库 |Microsoft.AspNetCore.Authentication.JwtBearer 9.0.1https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBearerhttps://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBea…...
wordpressAI工具,已接入Deepseek 支持自动生成文章、生成图片、生成长尾关键词、前端AI窗口互动、批量采集等
基于关键词或现有内容生成SEO优化的文章,支持多种AI服务(如OpenAI、百度文心一言、智谱AI等),并提供定时任务、内容采集、关键词生成等功能。 核心功能 文章生成 关键词生成:根据输入的关键词生成高质量文章。 内容…...
Ollama部署 DeepSeek-R1:70B 模型的详细步骤
1. 确认环境准备 (1) 硬件要求 显存需求:70B 参数的模型需要大量显存。若使用 NVIDIA T4(16GB 显存),需多卡并行(如 8 卡)或开启量化(如 q4_0、q8_0)。内存需求:建议至…...
PAT乙级( 1009 说反话 1010 一元多项式求导)C语言版本超详细解析
1009 说反话 给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出。 输入格式: 测试输入包含一个测试用例,在一行内给出总长度不超过 80的字符串。字符串由若干单词和若干空格组成,其中单词是由英文字母&#x…...
s2-pro效果展示:多说话人语音合成(同一模型切换不同音色)
s2-pro效果展示:多说话人语音合成(同一模型切换不同音色) 1. 专业级语音合成效果展示 s2-pro作为Fish Audio开源的专业级语音合成模型,其最惊艳的能力在于同一模型支持多种音色切换。通过上传不同的参考音频,模型可以…...
RimWorld开局定制利器:EdB Prepare Carefully深度应用指南
RimWorld开局定制利器:EdB Prepare Carefully深度应用指南 【免费下载链接】EdBPrepareCarefully EdB Prepare Carefully, a RimWorld mod 项目地址: https://gitcode.com/gh_mirrors/ed/EdBPrepareCarefully 在RimWorld的殖民挑战中,开局配置往往…...
爱毕业aibye精选6大AI论文平台榜单:助力高效写作与智能降重,科研工作者的得力助手!
工具名称 核心功能 特色优势 Aibiye 论文生成降AI率 全学科覆盖、仿写优化、自动图表生成 Aicheck AI检测文献综述辅助 精准查新、3分钟高效成文 GPT学术版 润色/翻译/代码解释 多模型协同、PDF深度解析 摆平论文 大纲生成降重改写 三步出稿、本硕博通用 QuillB…...
PyTorch 2.8镜像多场景落地:智慧农业病虫害识别模型田间部署方案
PyTorch 2.8镜像多场景落地:智慧农业病虫害识别模型田间部署方案 1. 田间AI的迫切需求 现代农业正面临病虫害防治的严峻挑战。传统人工巡查方式效率低下,一个熟练的技术员每天最多能检查3-5亩作物,而大型农场往往需要数十人同时作业。更棘手…...
从if-else到assign:聊聊RTL代码风格如何影响X态传播与电路质量
从if-else到assign:RTL代码风格对X态传播与电路质量的深层影响 在数字IC设计领域,X态就像电路中的"幽灵信号",它无声无息地潜伏在设计中,直到某个关键时刻突然显现,引发难以追踪的异常行为。对于RTL工程师而…...
NsEmuTools:开源模拟器管理工具的质量保障与工程实践
NsEmuTools:开源模拟器管理工具的质量保障与工程实践 【免费下载链接】ns-emu-tools 一个用于安装/更新 NS 模拟器的工具 项目地址: https://gitcode.com/gh_mirrors/ns/ns-emu-tools 在开源项目的生命周期中,如何在快速迭代与代码质量之间找到平…...
中文句子相似度分析神器:StructBERT本地部署全流程详解(附代码)
中文句子相似度分析神器:StructBERT本地部署全流程详解(附代码) 1. 工具概览与核心价值 中文语义相似度分析是自然语言处理中的一项基础但关键的任务。无论是智能客服中的问题匹配,还是内容平台的文章查重,都需要准确…...
毫米波雷达测速的“火眼金睛”:从汽车ACC到手势识别,Doppler FFT如何分辨不同速度的目标?
毫米波雷达测速的“火眼金睛”:从汽车ACC到手势识别,Doppler FFT如何分辨不同速度的目标? 在自动驾驶汽车的前方,一辆卡车突然减速,而右侧车道有摩托车正在加速超车——毫米波雷达如何在这复杂的场景中,准确…...
Llama-3.2V-11B-cot部署案例:中小企业低成本构建AI图文分析工作台
Llama-3.2V-11B-cot部署案例:中小企业低成本构建AI图文分析工作台 1. 项目概述 Llama-3.2V-11B-cot是基于Meta最新多模态大模型开发的专业级视觉推理工具,专为中小企业打造的低成本AI图文分析解决方案。该工具针对双卡RTX 4090环境进行了深度优化&…...
屠龙刀法35--使用SQL查询器批量生成insert语句
很多网友认为SQL查询器的语句不都是人工输入或者从外面粘贴进去的吗?用查询器批量生成Insert语句感觉有点魔幻哦。的确听起来不太科学,但是对于DBCS来说这个功能的确非常好用。下面我们就举例一步步告诉大家,如何使用这个功能。 第一步&…...
