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

文章目录

  • 1、声明
  • 2、HID协议
    • 2.1、描述符
    • 2.2、鼠标数据格式
  • 3、应用程序
  • 4、编译应用程序
  • 5、测试
  • 6、其它

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

移动鼠标:

滚轮滑动:

按键状态:

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

6、其它

以下是使用异步接口读取鼠标数据的测试程序。

#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_mouse(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;        // 记录该接口描述符的编号(编号是从0开始)

            if (intf_desc->bInterfaceClass != 3 || intf_desc->bInterfaceProtocol != 2)      // 判断是否是HID设备和是否是鼠标协议    
                continue;
            else
            {
                /* 找到了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;

                        /* 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_mouse(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);

}

运行程序前,先把多个鼠标插入开发板,然后运行测试程序,移动鼠标,查看打印结果。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据安全:守护数据的坚固防线

在数字化时代&#xff0c;数据已成为企业和组织的核心资产。然而&#xff0c;数据的安全性问题也日益凸显&#xff0c;数据泄露、数据滥用等事件频发&#xff0c;给企业和个人带来了巨大的损失。今天&#xff0c;让我们深入《DAMA数据管理知识体系指南&#xff08;第二版&#…

PyQt学习记录

0. 安装配置 0.1 安装相关库 首先打开你的PyCharm程序&#xff0c;然后新建一个目录用于学习&#xff0c;其次在terminal中输入 pip install pyqt5如果你不具有科学上网能力&#xff0c;请改为国内源 pip install pyqt5 -i https://pypi.douban.com/simple然后安装pyqt相关…

对“云原生”的初印象

一、背景 最近因为在工作中以及一些技术博客中听的比较火的一个关键词 "云原生"&#xff0c;于是产生了好奇&#xff0c;云原生到底是什么东西&#xff1f;自己对云原生也是一个纯小白&#xff0c;于是带着这个问题去好好了解一下&#xff0c;什么是"云原生&qu…

SystemVerilog基础:disable fork语句

相关阅读 SystemVerilog基础https://blog.csdn.net/weixin_45791458/category_12517449.html?spm1001.2014.3001.5482 一、进程的概念 在学习disable fork语句之前&#xff0c;首先的了解SystemVerilog中的进程概念&#xff1a;进程是一系列可以独立执行的一个或多个表达式。…

富芮坤FR8003硬件:VDDIO供电有工作不正常的情况从VBAT供电正常

从事嵌入式单片机的工作算是符合我个人兴趣爱好的,当面对一个新的芯片我即想把芯片尽快搞懂完成项目赚钱,也想着能够把自己遇到的坑和注意事项记录下来,即方便自己后面查阅也可以分享给大家,这是一种冲动,但是这个或许并不是原厂希望的,尽管这样有可能会牺牲一些时间也有哪天原…

IBM服务器刀箱Blade安装Hyper-V Server 2019 操作系统

案例:刀箱某一blade,例如 blade 5 安装 Hyper-V Server 2019 操作系统(安装进硬盘) 刀箱USB插入安装系统U盘,登录192.168... IBM BlandeCenter Restart Blande 5,如果Restart 没反应,那就 Power Off Blade 然后再 Power On 重启后进入BIOS界面设置usb存储为开机启动项 …

【大模型】本地部署DeepSeek-R1:8b大模型及搭建Open-WebUI交互页面

本地部署DeepSeek-R1:8b大模型 一、摘要及版本选择说明1.1 摘要1.2 版本选择 二、下载并安装Ollama三、运行DeepSeek-R1:8b大模型四、安装Open WebUI增强交互体验五、关闭Ollama开机自动启动六、DeepSeek大模型启停步骤 一、摘要及版本选择说明 1.1 摘要 作为一名对 AI 和生成…

6、使用one-api管理统一管理大模型,并开始使用本地大模型

文章目录 本节内容介绍集中接入&#xff1a;将大模型统一管理起来当使用了大模型代理大模型代理示例 开源模型&#xff1a;如何使用Hugging Face上的模型modelscope使用 pipeline 调用模型用底层实现调用模型流式输出 如何在项目中使用开源模型使用 LangChain使用集中接入开始使…

绕组电感 - Ansys Maxwell 磁通链与电流

在本博客中&#xff0c;我将演示如何使用 Ansys Maxwell 中磁瞬态求解器的磁通链和电流结果来计算绕组电感。Ansys Maxwell 磁瞬态求解器在场计算中考虑了涡流效应&#xff0c;我将展示一种使用磁通链和电流结果来计算绕组电感的简单方法。 实际上&#xff0c;电感是非线性的…

【图片转换PDF】多个文件夹里图片逐个批量转换成多个pdf软件,子文件夹单独合并转换,子文件夹单独批量转换,基于Py的解决方案

建筑设计公司在项目执行过程中&#xff0c;会产生大量的设计图纸、效果图、实景照片等图片资料。这些资料按照项目名称、阶段、专业等维度存放在多个文件夹和子文件夹中。 操作需求&#xff1a;为了方便内部管理和向客户交付完整的设计方案&#xff0c;公司需要将每个项目文件…

Formality:探针(Probe Point)的设置与使用

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 一般情况下&#xff0c;verify命令会对参考设计和实现设计所有匹配的比较点各自进行验证&#xff0c;但有些时候为了调试&#xff0c;可能需要验证参考设计和实现设…

Cherry Studio之DeepSeek联网/本地,建属于自己的AI助理!

上一篇文章&#xff0c;讲了DeepSeek-R1部署到本地的方法。这一篇文章&#xff0c;我们让DeepSeek再一次升级&#xff0c;通过图形化界面来交互&#xff0c;从而变成我们的AI助理&#xff0c;让DeepSeek R1发挥最大实力&#xff01; 首选需要借助硅基流动的API接口&#xff0c…

HarmonyOS Next 方舟字节码文件格式介绍

在开发中&#xff0c;可读的编程语言要编译成二进制的字节码格式才能被机器识别。在HarmonyOS Next开发中&#xff0c;arkts会编译成方舟字节码。方舟字节码长什么样呢&#xff1f;我们以一个demo编译出的abc文件&#xff1a; 二进制就是长这样&#xff0c;怎么去理解呢&…

AMD 8845HS 780M核显部署本地deepseek大模型的性能

测试了一下笔记本电脑AMD 8845HS的780M核显是否能本地部署deepseek大模型。 测试软件环境&#xff1a;LM Studio 0.3.9 、Windows 11 24H2 硬件&#xff1a;荣耀X16笔记本 CPU&#xff1a;AMD 8845HS 显卡&#xff1a;780M核显&#xff0c;显存为共享内存自动分配模式&…

实验7 路由器之间IPsec VPN配置

实验7 路由器之间IPsec VPN配置 1.实验目的 通过在两台路由器之间配置IPsec VPN连接&#xff0c;掌握IPsec VPN配置方法&#xff0c;加深对IPsec协议的理解。 2.实验内容 &#xff08;1&#xff09;按照实验拓扑搭建实验环境。 &#xff08;2&#xff09;在路由器R1和R4配置IP…

【FPGA】模型机下载FPGA设计

目录 模型机下载FPGA设计 框架 仿真 代码 MIOC.v IO.v SoC.v 模型机下载FPGA设计 32位MIPS地址空间采用内存与IO统一编址方式&#xff0c;总共232个存储单元&#xff0c;每个单元默认存放1个字节&#xff0c;即总共4GB。划分为:用户空间和内核空间。 &#xff08;1&#xff09;…

【真一键部署脚本】——一键部署deepseek

目录 deepseek一键部署脚本说明 0 必要前提 1 使用方法 1.1 使用默认安装配置 1.1 .1 使用其它ds模型 1.2 使用自定义安装 2 附录&#xff1a;deepseek模型手动下载 3 脚本下载地址 deepseek一键部署脚本说明 0 必要前提 linux环境 python>3.10 1 使用方法 1.1 …

学习笔记:在华为云ModelArts上运行MindSpore扩散模型教程

目录 一、背景与目的 二、环境搭建 三、模型原理学习 1. 类定义与初始化 2. 初始卷积层 3. 时间嵌入模块 4. 下采样模块 5. 中间模块 6. 上采样模块 7. 最终卷积层 8. 前向传播 9. 关键点总结 四、代码实现与运行 五、遇到的问题及解决方法 六、总结与展望 教程来源&#xff1a…

以 Python 为工具搭建的热门旅游景点数据分析系统

系统介绍&#xff1a; 随着计算机技术的飞速发展&#xff0c;计算机系统的应用已广泛渗透到社会的各个领域。大数据的大量应用为人们的生活带来了极大的便利。在此背景下&#xff0c;将热门旅游景点数据分析管理与互联网相结合&#xff0c;利用计算机搭建热门旅游景点数据分析…

如何实现图片式按钮的功能

文章目录 1. 概念介绍2. 使用方法2.1 filled风格2.2 filledTonal风格2.3 outlined风格 3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了"如何修改NavigationBar组件的形状"相关的内容&#xff0c;本章回中将介绍IconButtion组件.闲话休提…