USB子系统学习(四)使用libusb读取鼠标数据

news2025/2/9 5:57:44

文章目录

  • 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字节的数据,格式如下:

偏移大小描述
01字节
11字节按键状态
22字节X位移
42字节Y位移
61字节或2字节滚轮

按键状态里,每一位对应鼠标的一个按键,等1时表示对应按键被点击了,格式如下:

长度描述
01鼠标的左键
11鼠标的右键
21鼠标的中间键
35保留,设备自己定义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

移动鼠标:

滚轮滑动:

按键状态:

另外,每个鼠标的数据格式是不一样的。以上测试结果只是我使用的鼠标。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2295097.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度剖析 Redisson 分布式锁:原理、实现与应用实践

文章目录 写在文章开头详解Redisson 分布式锁使用和实现前置准备工作分布式锁的基本使用公平锁的使用联锁的使用读写锁基本使用常见问题Redisson和Jedis有什么区别redisson如何实现分布式锁redisson如何实现分布式锁的可重入redisson如何实现公平锁Redisson的watchdog机制是什么…

基于微信小程序的医院预约挂号系统的设计与实现

hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生…

仅128个token达到ImageNet生成SOTA性能!MAETok:有效的扩散模型的关键是什么?(卡内基梅隆港大等)

论文链接&#xff1a;https://arxiv.org/pdf/2502.03444 项目链接&#xff1a;https://github.com/Hhhhhhao/continuous_tokenizer 亮点直击 理论与实验分析&#xff1a;通过实验和理论分析建立了潜空间结构与扩散模型性能之间的联系。揭示了具有更少高斯混合模型&#xff08;G…

示例:JAVA调用deepseek

近日&#xff0c;国产AI DeepSeek在中国、美国的科技圈受到广泛关注&#xff0c;甚至被认为是大模型行业的最大“黑马”。在外网&#xff0c;DeepSeek被不少人称为“神秘的东方力量”。1月27日&#xff0c;DeepSeek应用登顶苹果美国地区应用商店免费APP下载排行榜&#xff0c;在…

Linux系统命令无法使用(glib库相关问题)

1.背景描述 Yum强制安装了一些软件&#xff0c;安装软件成功无报错&#xff0c;完成后不久突然发现系统出问题了&#xff0c;所有的命令无法使用了&#xff0c;如ls、mv、cat等基本命令报错。 relocation error&#xff1a; /lib64/libpthread.so.0: symbol_libc_dl_error_tsd …

电脑黑屏按什么键恢复?电脑黑屏的解决办法

电脑黑屏的原因有很多&#xff0c;可能是硬件、软件、系统或者病毒等方面造成的。那么&#xff0c;当我们遇到电脑黑屏时&#xff0c;应该怎么做呢&#xff1f;有没有什么快捷的方法可以恢复正常呢&#xff1f;本文将为您介绍一些常见的电脑黑屏情况及其解决办法。 一、电脑开机…

思翼遥控器疑问?

1.地面端与遥控端对频&#xff0c;地面端选择数传2为串口&#xff0c;天空端的UART2通过USB转TTL模块连接电脑&#xff0c;通过串口助手观察得有1Hz输出帧&#xff08;开启遥控器APP时间段为10Hz&#xff09;&#xff0c;共21字节&#xff0c;请问&#xff0c;这个是什么含义&a…

anaconda中可以import cv2,但是notebook中cv2 module not found

一、问题 anaconda中成功import cv2 但是jupyter notebook中却无法导入cv2 二、排查 anaconda中使用python路径如下&#xff1a; jupyter notebook中使用python路径如下&#xff1a; 可以发现路径不一致。 三、解决 ①查看可用的kernel ②选中想要修改的kernel&#xff0c;打…

如何解决 Linux 文件系统挂载失败的问题

当遇到Linux文件系统挂载失败的问题时&#xff0c;您可以通过以下步骤来解决问题&#xff1a; 解决方法&#xff1a; 检查挂载点&#xff1a; 确保要挂载的目标文件系统存在&#xff0c;并且挂载点是正确的。检查挂载点是否已经被其他文件系统占用。 检查文件系统状态&#x…

PHP填表统计预约打卡表单系统小程序

&#x1f4cb; 填表统计预约打卡表单系统——专属定制&#xff0c;信息互动新纪元 &#x1f4ca; 填表统计预约打卡表单系统&#xff0c;一款专为现代快节奏生活量身打造的多元化自定义表单统计小程序&#xff0c;集信息填表、预约报名、签到打卡、活动通知、报名投票、班级统…

PAT乙级( 1009 说反话 1010 一元多项式求导)C语言版本超详细解析

1009 说反话 给定一句英语&#xff0c;要求你编写程序&#xff0c;将句中所有单词的顺序颠倒输出。 输入格式&#xff1a; 测试输入包含一个测试用例&#xff0c;在一行内给出总长度不超过 80的字符串。字符串由若干单词和若干空格组成&#xff0c;其中单词是由英文字母&#x…

LVSNAT服务搭建

LVSNAT实验环境搭建 在虚拟机上&#xff0c;我的NAT模式ip划分为&#xff1a;172.25.254.0 仅主机模式IP为&#xff1a;192.168.0.0 拓补图如下 配置服务&#xff1a;LVS服务端添加两个网卡&#xff0c;分别为NAT模式和仅主机模式 LVS服务端配置&#xff1a; systemctl st…

apisix网关ip-restriction插件使用说明

ip-restriction插件可以在网关层进行客户端请求ip拦截。 当然了&#xff0c;一般不推荐使用该方法&#xff0c;专业的事专业工具做。建议有条件&#xff0c;还是上防火墙或者waf来做。 官方文档&#xff1a;ip-restriction | Apache APISIX -- Cloud-Native API Gateway whit…

html 列动态布局

样式说明&#xff1a; /* 列动态布局&#xff0c;列之间以空格填充 */ li {display: flex;/* flex-direction: column; */justify-content: space-between; }

C++小等于的所有奇数和=最大奇数除2加1的平方。

缘由 三种思路解题&#xff1a;依据算术推导得到一个规律&#xff1a;小等于的所有奇数和等于最大奇数除以2加1的平方。将在后续发布&#xff0c;总计有十种推导出来的实现代码。 int a 0,aa 1,aaa 0;cin >> a; while (aa<a) aaa aa, aa 2;cout << aaa;i…

政采云业务网关实践:使用 Higress 统一替代 APISIX/Kong/Istio Ingress

作者&#xff1a;政采云基础架构团队技术专家 朱海峰&#xff08;片风&#xff09; 业务网关项目背景 由于一些历史的背景&#xff0c;政采云平台在网关建设上遇到一些问题&#xff1a; 容器网关配置较多&#xff0c;配置方式多样&#xff0c;运维压力较大&#xff1a; 配置…

【嵌入式 Linux 音视频+ AI 实战项目】瑞芯微 Rockchip 系列 RK3588-基于深度学习的人脸门禁+ IPC 智能安防监控系统

前言 本文主要介绍我最近开发的一个个人实战项目&#xff0c;“基于深度学习的人脸门禁 IPC 智能安防监控系统”&#xff0c;全程满帧流畅运行。这个项目我目前全网搜了一圈&#xff0c;还没发现有相关类型的开源项目。这个项目只要稍微改进下&#xff0c;就可以变成市面上目前…

C语言:深入了解指针4(超级详细)

看之前必须得掌握有一定指针的知识&#xff0c;不然会看不懂&#xff0c;如果有不懂的可以看我博客 指针1&#xff0c;指针2&#xff0c;指针3 这三个讲了指针全部的基础知识超级详细&#xff0c;这篇只要是讲一些指针练习题也是非常详细 1. sizeof和strlen的对⽐ 1. 基本定义…

CEF132 编译指南 Windows 篇 - 拉取 CEF 源码 (五)

1. 引言 获取 CEF 132 源码是开始编译工作的前提和关键步骤。在完成 depot_tools 的安装和配置后&#xff0c;我们需要通过正确的方式下载和同步 CEF 的源代码。由于 CEF 项目依赖于 Chromium 的大量组件&#xff0c;因此源码的获取过程需要特别注意同步策略和版本管理&#x…

DeepSeek与llama本地部署(含WebUI)

DeepSeek从2025年1月起开始火爆&#xff0c;成为全球最炙手可热的大模型&#xff0c;各大媒体争相报道。我们可以和文心一言一样去官网进行DeepSeek的使用&#xff0c;那如果有读者希望将大模型部署在本地应该怎么做呢&#xff1f;本篇文章将会教你如何在本地傻瓜式的部署我们的…