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…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...