使用libusb读取鼠标数据
使用libusb读取鼠标数据
文章目录
- 使用libusb读取鼠标数据
- 1. HID协议
- 1.1 描述符
- 1.2 数据格式
- 1.2.1 键盘
- 1.2.2 LED
- 1.2.3 鼠标
- 1.2.4 扫描码
- 2. 使用同步接口读取鼠标数据
- 2.1 编写源码
- 2.2 上机实验
- 2.2.1 在Ubuntu上实验
- 2.2.2 在IMX6ULL开发板上实验
- 3. 使用异步接口读取鼠标数据
- 3.1 编写源码
- 致谢
1. HID协议
HID: Human Interface Devices, 人类用来跟计算机交互的设备。就是鼠标、键盘、游戏手柄等设备。
对于USB接口的HID设备,有一套协议。
1.1 描述符
HID设备有如下描述符:
- HID设备的"设备描述符"并无实际意义,没有使用"设备描述符"来表示自己是HID设备。
- HID设备只有一个配置,所以只有一个配置描述符
- 接口描述符
- bInterfaceClass为3,表示它是HID设备
- bInterfaceSubClass是0或1,1表示它支持"Boot Interface"(表示PC的BIOS能识别、使用它),0表示必须等操作系统启动后通过驱动程序来使用它。
- bInterfaceProtocol:0-None, 1-键盘, 2-鼠标
- 端点描述符:HID设备有一个控制端点、一个中断端点
对于鼠标,HOST可以通过中断端点读到数据。
1.2 数据格式
1.2.1 键盘
通过中断传输可以读到键盘数据,它是8字节的数据,格式如下:
偏移 | 大小 | 描述 |
---|---|---|
0 | 1字节 | “Modifier keys status”,就是ctrl、alt、shift等按键的状态 |
1 | 1字节 | 保留 |
2 | 1字节 | 第1个按键的键值 |
3 | 1字节 | 第2个按键的键值 |
4 | 1字节 | 第3个按键的键值 |
5 | 1字节 | 第4个按键的键值 |
6 | 1字节 | 第5个按键的键值 |
7 | 1字节 | 第6个按键的键值 |
第0个字节中每一位都表示一个按键的状态,某位等于1时,表示对应的按键被按下,格式如下:
位 | 长度 | 描述 |
---|---|---|
0 | 1 | Left Ctrl |
1 | 1 | Left Shift |
2 | 1 | Left Alt |
3 | 1 | Left GUI(Windows/Super key) |
4 | 1 | Right Ctrl |
5 | 1 | Right Shift |
6 | 1 | Right Alt |
7 | 1 | Right GUI(Windows/Super key) |
读到的键盘数据里有6个按键值,每个按键值都是8位的数据。如果某个按键值不等于0,就表示某个按键被按下了。
按键值跟按键的对应关系,请看后面的《1.2.4 扫描码》。
示例:按键"A"、“B”、“C”、"X"的按键值分别是4、5、6、0x1B。
按下了"A",USB键盘上报的数据为:
00 00 04 00 00 00 00 00
松开"A",USB键盘上报的数据为:
00 00 00 00 00 00 00 00
按下"A"、“B”,USB键盘上报的数据为:
00 00 04 05 00 00 00 00
保持"A"、“B"不松开,继续按下"C”,USB键盘上报的数据为:
00 00 04 05 06 00 00 00
松开"A",但是保持"B"、"C"不松开,USB键盘上报的数据为:
00 00 05 06 00 00 00 00
USB键盘上报的数据里,哪个按键先被按下,就先记录它的按键值。在上面的例子里,“A"松开后只有"B”、"C"这两个按键,“B”、"C"的按键值挪到了前面。
按下"Left shift"、并且按下"X",USB键盘上报的数据为:
02 00 1B 00 00 00 00 00
USB键盘只能上报6个按键值,如果有超过6个按键被按下,那么它讲上报"phantom condition"(6个按键值都是1),但是"Modifier keys status"还是有效的。比如"Right Shift"被按下,另外超过6个的按键也被按下时,USB键盘上报的数据为:
20 00 01 01 01 01 01 01
1.2.2 LED
我们还可控制键盘的LED,需要发出一个控制传输请求:SetReport ,使用这个请求发送一个字节的数据。
这个字节的数据格式如下,某位为1时,会点亮相应的LED:
位 | 长度 | 描述 |
---|---|---|
0 | 1 | Num Lock |
1 | 1 | Caps Lock |
2 | 1 | Scroll Lock |
3 | 1 | Compose |
4 | 1 | Kana |
5 | 1 | 保留,写为0 |
发出的SetReport,是一个控制传输的"setup packet",格式如下:
以libusb的函数描述它的参数,如下:
int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle,uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,unsigned char *data, uint16_t wLength, unsigned int timeout);/* 示例代码 */
unsigned char data = (1<<1); /* 点亮Caps Lock */
uint16_t wValue = (0x02<<8)|0; // 0x02: 发给设备, 0: report ID
uint16_t wIndex = 0; // 一般是0, the interface number of the USB keyboard
libusb_control_transfer(dev_handle, 0x21, 0x09, wValue, wIndex, &data, 1, timeout);
1.2.3 鼠标
通过中断传输可以读到鼠标数据,它是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位数据表示。
1.2.4 扫描码
USB规范里为每个按键定义了16位的按键值,注意:它是16位的,但是USB键盘只使用8位表示按键值。所以有些按键需要通过"Modifier keys status"来确定。比如"Left Ctrl"的按键值是224,这无法通过8位数据来表示,在USB键盘上报的数据里,使用第0字节的bit4来表示。
2. 使用同步接口读取鼠标数据
2.1 编写源码
#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 found = 0;int transferred;int count = 0;unsigned char buffer[16];struct libusb_config_descriptor *config_desc;struct libusb_device_handle *dev_handle = NULL;/* 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];/* parse interface descriptor, find usb mouse */ 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");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;if (intf_desc->bInterfaceClass != 3 || intf_desc->bInterfaceProtocol != 2)continue;else{/* 找到了USB鼠标 */fprintf(stdout, "find usb mouse ok\n");for (int ep = 0; ep < intf_desc->bNumEndpoints; ep++){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);}if (found){/* libusb_open */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, 16, &transferred, 5000);if (!err) {/* parser data */printf("%04d datas: ", count++);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 {fprintf(stderr, "libusb_interrupt_transfer err : %d\n", err);//exit(1);}}/* libusb_close */libusb_release_interface(dev_handle, interface_num);libusb_close(dev_handle);libusb_exit(NULL);
}
2.2 上机实验
2.2.1 在Ubuntu上实验
// 1. 安装开发包
$ sudo apt install libusb-1.0-0-dev// 2. 修改源码,包含libusb.h 头文件时用如下代码
#include <libusb-1.0/libusb.h>// 3. 编译程序指定库
gcc -o readmouse readmouse.c -lusb-1.0
2.2.2 在IMX6ULL开发板上实验
交叉编译libusb
sudo apt-get install libtoolunzip libusb-1.0.26.zip cd libusb-1.0.26 ./autogen.sh./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmpmakemake installls tmp/ include lib
安装库、头文件到工具链的目录里
libusb-1.0.26/tmp/lib$ cp * -rfd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib/libusb-1.0.26/tmp/include$ cp libusb-1.0 -rf /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include/
交叉编译app
arm-buildroot-linux-gnueabihf-gcc -o readmouse.c -lusb-1.0
在开发板上插入USB鼠标,执行命令
./readmouse
3. 使用异步接口读取鼠标数据
3.1 编写源码
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libusb-1.0/libusb.h>struct usb_mouse {struct libusb_device_handle *handle;int interface;int endpoint;unsigned char buf[16];int transferred;struct libusb_transfer *transfer;struct usb_mouse *next;
};static struct usb_mouse *usb_mouse_list;void free_usb_mouses(struct usb_mouse *usb_mouse_list)
{struct usb_mouse *pnext;while (usb_mouse_list){pnext = usb_mouse_list->next;free(usb_mouse_list);usb_mouse_list = pnext;}
}/* */
int get_usb_mouses(libusb_device **devs, int num_devices, struct usb_mouse **usb_mouse_list)
{int err;libusb_device *dev;int endpoint;int interface_num;struct libusb_config_descriptor *config_desc;struct libusb_device_handle *dev_handle = NULL;struct usb_mouse *pmouse;struct usb_mouse *list = NULL;int mouse_cnt = 0;/* for each device, get config descriptor */for (int i = 0; i < num_devices; i++) {dev = devs[i];/* parse interface descriptor, find usb mouse */ 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");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;if (intf_desc->bInterfaceClass != 3 || intf_desc->bInterfaceProtocol != 2)continue;else{/* 找到了USB鼠标 */fprintf(stdout, "find usb mouse ok\n");for (int ep = 0; ep < intf_desc->bNumEndpoints; ep++){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;/* libusb_open */err = libusb_open(dev, &dev_handle);if (err){fprintf(stderr, "failed to open usb mouse\n");return -1;}fprintf(stdout, "libusb_open ok\n");/* 记录下来: 放入链表 */pmouse = malloc(sizeof(struct usb_mouse));if (!pmouse){fprintf(stderr, "can not malloc\n");return -1;}pmouse->endpoint = endpoint;pmouse->interface = interface_num;pmouse->handle = dev_handle;pmouse->next = NULL;if (!list)list = pmouse;else{pmouse->next = list;list = pmouse;}mouse_cnt++;break;}}}}libusb_free_config_descriptor(config_desc);}*usb_mouse_list = list;return mouse_cnt;
}static void mouse_irq(struct libusb_transfer *transfer)
{static int count = 0;if (transfer->status == LIBUSB_TRANSFER_COMPLETED){/* parser data */printf("%04d datas: ", count++);for (int i = 0; i < transfer->actual_length; i++){printf("%02x ", transfer->buffer[i]);}printf("\n");}if (libusb_submit_transfer(transfer) < 0){fprintf(stderr, "libusb_submit_transfer err\n");}
}int main(int argc, char **argv)
{int err;libusb_device **devs;int num_devices, num_mouse;struct usb_mouse *pmouse;/* 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");/* get usb mouse */num_mouse = get_usb_mouses(devs, num_devices, &usb_mouse_list);if (num_mouse <= 0){/* free device list */libusb_free_device_list(devs, 1);libusb_exit(NULL);exit(1);}fprintf(stdout, "get %d mouses\n", num_mouse);/* free device list */libusb_free_device_list(devs, 1);/* claim interface */pmouse = usb_mouse_list;while (pmouse){libusb_set_auto_detach_kernel_driver(pmouse->handle, 1); err = libusb_claim_interface(pmouse->handle, pmouse->interface);if (err){fprintf(stderr, "failed to libusb_claim_interface\n");exit(1);}pmouse = pmouse->next;}fprintf(stdout, "libusb_claim_interface ok\n");/* for each mouse, alloc transfer, fill transfer, submit transfer */pmouse = usb_mouse_list;while (pmouse){/* alloc transfer */pmouse->transfer = libusb_alloc_transfer(0);/* fill transfer */libusb_fill_interrupt_transfer(pmouse->transfer, pmouse->handle, pmouse->endpoint, pmouse->buf,sizeof(pmouse->buf), mouse_irq, pmouse, 0);/* submit transfer */libusb_submit_transfer(pmouse->transfer);pmouse = pmouse->next;}/* handle events */while (1) {struct timeval tv = { 5, 0 };int r;r = libusb_handle_events_timeout(NULL, &tv);if (r < 0) {fprintf(stderr, "libusb_handle_events_timeout err\n");break;}}/* libusb_close */pmouse = usb_mouse_list;while (pmouse){libusb_release_interface(pmouse->handle, pmouse->interface);libusb_close(pmouse->handle); pmouse = pmouse->next;}free_usb_mouses(usb_mouse_list);libusb_exit(NULL);
}
致谢
以上笔记源自
韦东山
老师的视频课程,感谢韦老师,韦老师是嵌入式培训界一股清流,为嵌入式linux开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!
在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!
使用libusb读取鼠标数据相关推荐
- python读数据-如何用Python读取开放数据?
当你开始接触丰富多彩的开放数据集时,CSV.JSON和XML等格式名词就会奔涌而来.如何用Python高效地读取它们,为后续的整理和分析做准备呢?本文为你一步步展示过程,你自己也可以动手实践. 需求 ...
- 树莓派 rfid_树莓派工控机做Modbus RTU主站读取RFID数据
KUNBUS Revpi Core 3是工业级的树莓派,可作为小型的工业PC用,外观十分小巧,操作简单,DIN导轨模块化安装,RevPi core 3能与RevPi IO连接,能实时对这些IO的控制. ...
- 如何在QT中读取串口数据
总是能在别人的博客中学到太多太多,谢谢各位对知识的无私共享,谢谢大家 前言 去年我使用Qt编写串口通信程序时,将自己的学习过程写成了教程(Qt编写串口通信程序全程图文讲解),但是由于时间等原因,我只实 ...
- stm32读取驾驶模拟器数据 stm32F407读取joystick数据
需求 实习工作,老板要求用单片机读取驾驶模拟器(Joystick)返回的数据,驾驶模拟器usb输出,输出信息包括:方向盘转角.左右拨杆.按键等. 硬件 采用正点原子探索者开发板,即插即用,硬件不需要改 ...
- 【TensorFlow学习笔记】完美解决 pip3 install tensorflow 没有models库,读取PTB数据
安装tensorflow 我使用的是最最最简单的容易的 pip3 install <TensorFlow学习笔记> 一. 安装win10下python3.6的tensorflow的CPU版 ...
- [Rx86OS-IX] 解读鼠标数据 移动鼠标
平台 处理器:Intel Celeron(R) Dual-Core CPU 操作系统:Windows7 专业版 x86 阅读书籍:<30天自制操作系统>-川合秀实[2015.03.23 ] ...
- TensorFlow csv读取文件数据(代码实现)
TensorFlow csv读取文件数据(代码实现) 大多数人了解 Pandas 及其在处理大数据文件方面的实用性.TensorFlow 提供了读取这种文件的方法. 前面章节中,介绍了如何在 Tens ...
- SharePoint2010沙盒解决方案基础开发——关于TreeView树形控件读取列表数据(树形导航)的webpart开发及问题...
转:http://blog.csdn.net/miragesky2049/article/details/7204882 SharePoint2010沙盒解决方案基础开发--关于TreeView树形控 ...
- Kinect V1读取图像数据(For Windows)
Kinect V1读取图像数据(For Windows) 这篇博客 Kinect V1介绍 数据读取的基本流程 运行代码和注释 结尾 这篇博客 刚好有一台现成的Kinect V1相机,所以就拿过来学 ...
最新文章
- Windows下更改MySQL 数据库文件存放位置
- WPF ListView DoubleClick
- Struts2的Action和Servlet有什么联系,区别?
- Spring集成Junit步骤和代码实现
- 如何在Hexo中实现自适应响应式相册功能
- linux 启动nacos报错_Spring Cloud:Alibaba 之 Nacos
- 大数据_Spark_VS_Hadoop_框架---Spark工作笔记0002
- Matlab imfilter函数
- Ice_cream's world I( 并查集 + 判环 )
- 深度学习这些年那些超重要的idea回顾总结
- 力扣 2104. 子数组范围和
- 通过google插件Thumbnails实现图片指定大小压缩
- C语言课程设计-满分作业
- 干货来袭!java怎么创建包和类
- 少儿图形编程语言哪个最好
- 「软件测试4」一文详解四大典型的白盒测试方法
- 微软做好了放弃Flash Player的准备
- Redis之地理坐标
- 交换机的vlan划分和VLAN Trunk技术
- 牛客网:接雨水的双指针问题
热门文章
- 树莓派3b+安装ubuntu 16.04+ROS kinetic过程详解及踩坑总结
- AMBA总线—APB总线协议详解
- 南卡NANK Runner CC3 耳机评测:骨传导耳机入门级别最强款
- [C]数字炸弹小游戏
- C++中的内存管理、内存泄漏和内存回收
- Supervisor守护Java进程_使用Supervisor来守护我们的服务
- mysql icp特性_MySQL:关于ICP特性的说明(未完)
- 深圳首辆数字人民币主题观光巴士亮相
- 第七次网页前端培训(JavaScript)
- java.lang.IllegalArgumentException的问题解决