Linux应用编程(五)USB应用开发-libusb库

      

目录

一、基础知识 

1. USB接口是什么?

2. USB命名规则

3. USB HOST和USB OTG

4. USB集线器(hub)

5. USB描述符

6. 传输模式

7. USB数据格式

(1)域(Domain)

(2)包(Packet)

(3)事务(Transaction)

(4)传输(Transfer)

8. USB枚举

二、应用编程 

1. libusb库安装

2. 使用libusb库编写应用程序

实验(一):打印USB设备的ID。

 实验(二):操作USB设备。(同步传输)

 实验(三):操作USB设备。(异步传输)


 

一、基础知识 

1. USB接口是什么?

        USB接口(Universal Serial Bus)是一种通用串行总线,广泛使用的接口标准,主要用于连接计算机与外围设备(如键盘、鼠标、打印机、存储设备等)之间的数据传输和电力供应。它旨在简化计算机与外部设备之间的连接方式,同时提供更高的传输速度和更好的兼容性。

         在1990年代初,计算机外围设备通常使用专用接口,如鼠标和键盘常见的PS/2接口,打印机使用的LPT接口(并口)等。这些接口虽然在当时各自具有一定的专用性和高效性,但也存在很多问题,随着技术的发展,通用性更强的接口逐渐成为主流,USB(Universal Serial Bus)便是这一发展潮流的代表。 

        1996年,由Intel微软康柏DECIBMNEC北方电信公司等七家业界巨头组成的非盈利组织USB标准化组织(USB Implementers Forum,简称USB-IF)开始推动USB(Universal Serial Bus)接口的标准化工作。USB的诞生标志着计算机外围设备连接方式的革命性转变。

USB 1.0/1.1最早的版本,传输速度为12 Mbps。
USB 2.0

提供更高的传输速度(最高480 Mbps),广泛应用于各种设备。

USB 3.0/3.1提供更高的数据传输速率,最高可达5 Gbps及以上,支持更大的电流供电。
USB 4.0最新的USB版本,支持更高的传输速率,最高可达40 Gbps,并且兼容Thunderbolt 3协议。

2. USB命名规则

        在2013年,USB 3.0USB 3.1推出后,USB实现了更高的传输速度和更多功能,但随着技术的进步和市场的需求,USB-IF(USB Implementers Forum)为了更清晰地区分不同的版本和性能,进行了命名上的调整。

第一次命名改变:

                USB 3.0 改名为 USB 3.1 Gen 1

                USB 3.1 改名为 USB 3.1 Gen 2

第二次命名改变:

                USB 3.1 Gen1 = USB 3.2 Gen1
                USB 3.1 Gen2 = USB 3.2 Gen2
                USB 3.2 = USB 3.2 Gen2x2

        其本质上就是, USB 3.0 就是 USB 3.2 Gen1 ,USB 3.1是USB 3.2 Gen2,USB 3.2是USB 3.2 Gen2x2。

3. USB HOST和USB OTG

        USB Host(主设备):是指具备控制和管理USB总线的设备。它负责控制所有USB连接的设备,发起数据传输,并管理设备间的通信。USB Host的核心功能是控制和调度USB数据的流向。典型的USB Host设备包括:计算机(台式机、笔记本电脑等)、打印机(具有USB接口的)等。在USB通信中,Host设备通过发送命令来请求从设备发送或接收数据。Host设备管理USB总线上的电源供给和数据传输。

        USB Slave(从设备):是指依赖于主设备控制的设备。USB从设备由主设备管理,它没有独立的数据传输能力,所有的数据传输都必须经过主设备发起或协调。USB从设备通常是外设设备,例如:键盘、鼠标、U盘等。

        USB OTG(On-The-Go):是一项功能,使得USB设备在需要时可以切换角色,既可以作为主设备,也可以作为从设备,从而直接与其他USB设备进行通信。USB OTG标准通常适用于移动设备,如智能手机、平板电脑等。它允许用户无需通过传统的主设备(如计算机)即可实现设备间的直接连接和数据交换。例如,一台支持OTG的手机可以在连接U盘时充当主设备,而在连接键盘时又可以充当从设备。

4. USB集线器(hub)

        USB Hub(集线器)是一种设备,允许多个USB设备通过一个USB端口与计算机进行连接。它实际上是一个多端口的USB接口扩展器,可以将计算机的一个USB端口扩展为多个端口,方便用户连接多个USB设备。 集线器是为了扩展更多的接口。但是并不是可以无休止的可以扩展,USB2.0协议中最多扩展七层。每一层所有设备相加不能超过127个(包括集线器) 。

        所有从机都必须经过集线器(hub)才能与主机连接,也就是设备不能直接和主机相连接。根集线器:与主机直接连接的集线器叫做根集线器,用户外接的叫做普通集线器。

下图的集线器就是普通集线器。

下图中集线器就是根集线器, 例如U盘的插入就是连接到根集线器上。

     

5. USB描述符

        描述符通常是一种数据结构,在 Linux里面就是一个结构体,使用结构体来描述当前的设备有哪些特征,用于描述某个对象或设备的属性和特征。在USB和其他通信协议中,描述符扮演着重要的角色,它们包含了设备的基本信息,如设备类别、供应商ID、产品ID等,这些信息对于主机来说至关重要,因为主机需要依靠这些信息来识别和管理连接的设备。例如下述内容。

/* USB Device Descriptor */
struct usb_device_descriptor {
    uint8_t  bLength;              // 描述符的长度(以字节为单位),对于设备描述符来说总是18字节
    uint8_t  bDescriptorType;      // 描述符类型,对于设备描述符来说总是0x01
    uint16_t bcdUSB;               // USB规范版本号,以二进制编码的十进制数表示
    uint8_t  bDeviceClass;         // 设备类别代码
    uint8_t  bDeviceSubClass;      // 设备子类代码
    uint8_t  bDeviceProtocol;      // 设备协议代码
    uint8_t  bMaxPacketSize0;      // 端点0的最大数据包大小(以字节为单位)
    uint16_t idVendor;             // 供应商ID
    uint16_t idProduct;            // 产品ID
    uint16_t bcdDevice;            // 设备版本号,以二进制编码的十进制数表示
    uint8_t  iManufacturer;        // 供应商字符串描述符的索引
    uint8_t  iProduct;             // 产品字符串描述符的索引
    uint8_t  iSerialNumber;        // 序列号字符串描述符的索引
    uint8_t  bNumConfigurations;   // 支持的配置数量
} __attribute__((packed));

        USB 描述符主要有:设备描述符,配置描述符,接口描述符,端点描述符。这些描述符共同构建了USB设备的特征和功能,通过读取这些描述符,系统可以配置驱动和参数,使得操作系统可以正确识别设备和通信。

(1)设备描述符:是USB设备中最重要的描述符之一,它包含了设备的基本信息,如设备类型、供应商ID、产品ID、设备版本等。这个描述符通常位于设备的EEPROM或闪存中,并在设备枚举过程中被主机读取。通过读取设备描述符,主机可以了解设备的基本属性,并决定如何与设备进行通信。设备描述符是设备连接到主机时第一个被请求和返回的信息。它提供了设备的本特征。

(2)配置描述符:描述设备支持的不同配置。

(3)接口描述符:描述了配置中的一个接口。

(4)端点描述符:描述接口中的一个端点。端点是数据在设备和主机之间传输的终点。一个具体的端点只能属于四种传输模式中的一种。

注意:
        一个USB设备有1个设备描述符。
        一个USB设备有1个或多个配置描述符。
        一个USB配置有1个或多个接口描述符。
        一个USB接口有0个或多个点描述符(不括端点0)。

6. 传输模式

        数据通过USB总线传输,通常是通过端点(Endpoints)进行单向或双向的传输。USB使用一种层次化的协议栈,有四种传输模式:批量传输、中断传输、实时传输、控制传输。

(1)批量传输:适用于大数据量传输,且对实时性要求不高。主要用于传输不需要定时保证的数据,传输时没有时间限制,可以在空闲带宽下传输数据。常见应用:U盘、外部硬盘等。

(2)中断传输:用于传输少量、具有时间限制的数据,主机定期请求数据,传输间隔由设备和主机协商。实时性要求较高,但数据量较小,且数据传输频率是固定的。常见应用:鼠标、键盘、游戏手柄等输入设备。

(3)实时传输:适用于需要固定传输速率的场景,实时性要求高,但允许一定程度的丢包或误差。一般用于音频、视频等流式数据传输,确保数据能够以固定速率传输,尽管可能会丢失一些数据。常见应用:音视频设备、USB音响、摄像头等。

(4)控制传输(Control Transfer):用于设备配置、查询和命令的传输,数据量较小,且是双向传输。常用于 USB 设备的初始化和命令交换,传输的内容通常较为简单。常见应用:设备配置、读取设备状态、设置设备功能等。控制传输分为三个阶段:①建立阶段。②数据阶段。③确认阶段。

7. USB数据格式

        US8通信数据格式由域、包、事务、传输组成。多个域组成包,多个包组成事务,多个事务组成传输。所以域是 USB 数据传输中最小的单位。事务是最基本的单位,对于我们开发人员来说关注事务和传输就可以了。

(1)域(Domain)

  • 是 USB 数据传输中的最小单位。每个域有特定的作用和格式,可以包含一个或多个字段。
  • 七种类型的域
    • 同步域(SYNC):用于同步传输时钟,确保数据传输的时序性。
    • 地址域(ADDR):标识 USB 设备的地址,通常用于指定数据的目标设备。
    • 端点域(ENDPT):指定数据的目标端点,用于区分设备的不同数据流。
    • 帧号域(FRAME):用于标识当前传输的帧号,在多帧传输中起到区分作用。
    • 标识域(ID):用于标识不同的传输或者数据包。
    • 数据域(Data):实际的数据内容区域,是传输的核心部分。
    • 校验域(CRC):包含用于检验数据完整性的校验码,确保数据传输过程中没有错误。

(2)包(Packet)

        包是由多个域组成的数据单元。根据包的不同类型,域的结构会有所不同。USB 包有四种主要类型:

令牌包(Token Packet):用于向设备发送请求或命令,包含地址和端点等信息。

数据包(Data Packet):用于传输实际的数据,包含数据域和校验域。

握手包(Handshake Packet):用于确认数据传输的结果,常见的有 ACK(确认)和 NAK(未确认)等。

特殊包(Special Packet):用于特殊的控制或管理操作,例如 RESET 或 SOF(帧开始信号)。

        

        在 USB 中,PID 代表 Packet ID,即 数据包标识符。它用于标识 USB 数据包的类型,并且是在令牌包(Token Packet)和握手包(Handshake Packet)中必不可少的字段。常见的 PID 类型:

  • 令牌包(Token Packet)

    • OUT:用于主机向设备传输数据。
    • IN:用于设备向主机传输数据。
    • SETUP:用于设置设备的控制传输。
  • 数据包(Data Packet)

    • DATA0:表示数据包的第一个数据包,常用于控制传输。
    • DATA1:表示数据包的第二个数据包。
  • 握手包(Handshake Packet)

    • ACK:确认数据包传输成功。
    • NAK:表示数据包传输失败。
    • STALL:表示设备在处理数据时发生错误,无法完成请求。
  • 特殊包(Special Packet)

    • PRE:预留包(用于扩展等特殊用途)。

(3)事务(Transaction)

  • 事务是 USB 数据传输的基本单位,是由令牌包、数据包和握手包组成的。事务是传输过程中最小的可操作单元。
  • 事务的结构
    • 令牌包(Token Packet):发起事务,标识目标设备、端点等信息。
    • 数据包(Data Packet):可选,用于传输实际的数据。
    • 握手包(Handshake Packet):可选,用于确认数据传输是否成功。
  • 对开发者而言,事务是关注的重点,因为它直接影响数据的传输成功与否。

(4)传输(Transfer)

  • 传输是更高层次的概念,通常由多个事务组成,多个事务之间在时间上有一定的顺序。
  • 一个完整的传输可能涉及多个包、事务和域,最终确保数据从主机到设备(或反向)成功传送。

举例:

8. USB枚举

        USB枚举(USB Enumeration)是指USB设备连接到计算机时,操作系统对该设备进行识别和配置的过程。这个过程包括一系列步骤,确保计算机能够正确识别设备、安装必要的驱动程序并为设备分配资源。

        在USB枚举过程中主要使用控制传输模式。通常用于设备和主机之间的命令和状态交换。它的工作流程分为三个阶段:建立阶段(Setup)数据阶段(Data)确认阶段(Status)

(1)建立阶段。

        由USB主机发起,该阶段使用的是一个SETUP数据包,其结构包含了请求的命令、参数、设备的目标等信息。通过SETUP数据包,主机向设备发送命令,或者设备准备接收或发送数据。

        如果是输入请求(例如获取设备信息),则设备会准备好数据供主机接收。如果是输出请求(例如配置设备),则设备会准备好数据接收主机发出的命令。

(2)数据阶段。

        如果建立阶段是输入请求,则数据阶段由设备向主机传输数据。如果建立阶段是输出请求,则数据阶段由主机向设备传输数据。如果没有实际的数据传输需求(即请求的长度为零),此阶段依然会发送一个长度为0的数据包来表示数据传输完成。

(3)确认阶段。

        确认阶段刚好跟建立阶段相反。如果数据阶段是输入请求(即主机接收设备数据),那么确认阶段是设备向主机发送一个输出数据包。如果数据阶段是输出请求(即主机向设备发送数据),那么确认阶段是主机向设备发送一个输入数据包。

        确认阶段的主要目的是验证数据是否已经正确传输。如果传输成功,确认阶段不会包含任何数据;它只是作为一种状态确认,以告知数据传输是否正常完成。通常,成功的确认阶段只包含一个零长度数据包,这意味着数据传输完成且没有错误。

查看USB枚举过程中的数据内容,如下所示:

(1)传输。

(2) 传输和事务。

(3) 传输、事务、包和域。

 

 ★举例USB枚举过程:

二、应用编程 

1. libusb库安装

  libusb 是一个用 C 语言编写的开源库,主要用于简化 USB 设备的操作。通过 libusb,开发者可以方便地与 USB 设备进行通信,而不需要深入了解 USB 协议的底层细节。

  libusb 提供了跨平台的 API,可以在 Linux、macOS 和 Windows 等操作系统中使用,极大地提升了应用的可移植性。libusb 提供了一套简洁的 API,支持从 USB 1.0 到 USB 3.1 等不同版本的 USB 协议。无论是在传输数据、控制 USB 设备还是处理设备的其他操作时,API 接口保持一致,使用起来非常方便。

(1)下载。

        下载地址:libusb官网。如下图所示,下载最新的版本即可。

(2)编译安装(X86下)。

        我们下载所得到的就是libusb库的源码包,我们需要对源码包进行编译操作。在不同平台上使用的编译器不同,例如X86使用gcc,ARM使用交叉编译器。

<1> 安装依赖包。

sudo apt install -y libudev-dev

<2> 指定编译环境,这里为x86_64-linux 架构。

./configure --build=x86_64-linux

<3> 编译和安装。

make
sudo make install

<4> 验证:进入/usr/local/lib查看。

(3)编译安装(ARM下)。 

 <1> 安装依赖包。

sudo apt install -y libudev-dev

<2> 指定编译环境,这里为ARM架构。(先在源码包路径下新建install文件夹,用于存放编译后的libusb库)

//--build:编译平台
//--host:目标平台
//--prefix:安装路径
//CC: 编译器
//CXX:g++编译器
//--disable-udev:用于禁用对 udev 的支持
./configure --build=x86_64-linux --host=arm-linux --prefix=/home/qjl/libusb/libusb-1.0.27/install CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ --disable-udev

<3> 编译和安装。

make
sudo make install

<4> 使用验证。

        将源码目录下的example中的一个测试源码(例如listdevs.c)拷贝到install文件夹中。然后使用交叉编译器编译这个源码生成可执行文件。如下所示。将生产的可执行文件使用命令file来查看格式是否正确。将可执行文件传至开发板运行。

//-I ./include/libusb-1.0 :头文件路径
//-L ./lib :静态库路径
//-lusb-1.0 -lpthread -static 连接usb静态库 和线程thread库

arm-linux-gnueabihf-gcc listdevs.c -o listdevs -I ./include/libusb-1.0  -L ./lib  -lusb-1.0 -lpthread -static

2. 使用libusb库编写应用程序

        编写应用程序,在编译时和第一小节编译验证类似,使用交叉编译器,并且需要连接静态库和头文件。注意:USB每次传输8个字节的数据!!

实验(一):打印USB设备的ID。

#include <stdio.h>
#include <libusb-1.0/libusb.h>

int main() {
    libusb_device **device_list;  //定义设备列表,包含ID等各种信息
    libusb_device *dev; 
    struct libusb_device_descriptor desc; 
    int ret, i = 0;
    ssize_t cnt;

    //初始化libusb库
    ret = libusb_init(NULL);
    if (ret < 0) {
        printf("libusb init error\n");
        return ret;
    }

    //获取设备列表数量
    cnt = libusb_get_device_list(NULL, &device_list);
    if (cnt < 0) {
        printf("libusb get device list error\n");
        return (int)cnt;
    }

    // Iterate through the devices
    while ((dev = device_list[i++]) != NULL) {
        ret = libusb_get_device_descriptor(dev, &desc);
        if (ret < 0) {
            printf("libusb get device descriptor error\n");
            return ret;
        }

        // 打印厂商ID和产品ID
        printf("Device %d: Vendor ID: 0x%04x, Product ID: 0x%04x\n", i, desc.idVendor, desc.idProduct);
    }

    // 释放设备列表资源
    libusb_free_device_list(device_list, 1);

    // 退出libusb
    libusb_exit(NULL);

    return 0;
}

 实验(二):操作USB设备。(同步传输)

        将键盘插到开发板的USB接口,读取键盘按下的数据。同步传输:只有执行完上一次,才可以执行下一次。注意:USB每次传输8个字节的数据!!

        总体流程:初始化 libusb → 2. 获取设备列表 → 3. 遍历设备并查找中断传输端点 → 4. 打开设备并分离内核驱动 → 5. 进行中断传输 → 6. 打印接收到的数据并继续传输 → 7. 释放接口并关闭设备 → 8. 释放设备列表和退出 libusb

#include <stdio.h>
#include <libusb-1.0/libusb.h>

int main() {
    libusb_device **device_list;  //定义设备列表
    libusb_device *dev;   //定义设备
    struct libusb_device_descriptor desc;  //定义设备描述
    struct libusb_config_descriptor *config_desc;  //定义配置描述
    const struct libusb_interface_descriptor *interface_desc;  //接口描述
    libusb_device_handle *dev_handle;  //设备句柄
    unsigned char buffer[16];  //数据缓冲区
    int ret, i = 0, j, k, flag = 0;
    ssize_t cnt;
    int endpoint;  // 假设是设备的输入端点地址
    ssize_t transferred;

    // 初始化 libusb
    ret = libusb_init(NULL);
    if (ret < 0) {
        printf("libusb 初始化失败\n");
        return ret;
    }

    // 获取设备列表
    cnt = libusb_get_device_list(NULL, &device_list);
    if (cnt < 0) {
        printf("获取设备列表失败\n");
        return (int)cnt;
    }

    // 遍历设备列表
    while ((dev = device_list[i++]) != NULL) {
/*
        ret = libusb_get_device_descriptor(dev, &desc);  
        if (ret < 0) {
            printf("获取设备描述符失败\n");
            return ret;
        }
*/

        // 获取设备的配置描述符
        ret = libusb_get_config_descriptor(dev, 0, &config_desc);
        if (ret < 0) {
            printf("获取配置描述符失败\n");
            return ret;
        }

        // 遍历接口
        for (k = 0; k < config_desc->bNumInterfaces; k++) {
            interface_desc = &config_desc->interface[k].altsetting[0];

            // 检查接口是否为人机设备接口(比如鼠标或键盘)
            if (interface_desc->bInterfaceClass == 3 || interface_desc->bInterfaceProtocol == 1) {
                // 遍历端点
                for (j = 0; j < interface_desc->bNumEndpoints; j++) {
                    if ((interface_desc->endpoint[j].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_INTERRUPT) {
                        // 找到中断传输类型的端点
                        printf("找到中断端点,端点地址: 0x%02x\n", interface_desc->endpoint[j].bEndpointAddress);
                        endpoint = interface_desc->endpoint[j].bEndpointAddress;  // 设置实际的端点地址
                        flag = 1;
                        break;
                    }
                }
            }
            if (flag) break;
        }
        if (flag) break;

        // 释放配置描述符
        libusb_free_config_descriptor(config_desc);
    }

    if (!flag) {
        printf("未找到支持中断传输的端点\n");
        libusb_free_device_list(device_list, 1);
        libusb_exit(NULL);
        return -1;
    }

    // 打开设备
    ret = libusb_open(dev, &dev_handle);
    if (ret < 0) {
        printf("打开设备失败\n");
        return ret;
    }

    // 自动分离内核驱动
    ret = libusb_set_auto_detach_kernel_driver(dev_handle, 1);
    if (ret < 0) {
        printf("自动分离内核驱动失败\n");
        return ret;
    }

    // 声明接口
    ret = libusb_claim_interface(dev_handle, interface_desc->bInterfaceNumber);
    if (ret < 0) {
        printf("声明接口失败\n");
        return ret;
    }

    // 进行中断传输
    while (1) {
               ret = libusb_interrupt_transfer(dev_handle, endpoint, buffer, sizeof(buffer), &transferred, 5000);  // 5秒超时
              if (ret < 0) {
                printf("中断传输失败\n");
                break;
              }

              // 打印接收到的数据
              printf("接收到的数据: ");
              for (i = 0; i < transferred; i++) {
                 printf("%02x ", buffer[i]);
               }
              printf("\n");
      }

    // 释放接口
    libusb_release_interface(dev_handle, interface_desc->bInterfaceNumber);

    // 关闭设备
    libusb_close(dev_handle);

    // 释放设备列表
    libusb_free_device_list(device_list, 1);

    // 退出 libusb
    libusb_exit(NULL);

    return 0;
}

实验结果: 

 实验(三):操作USB设备。(异步传输)

        将键盘插到开发板的USB接口,读取键盘按下的数据。异步传输:程序能够异步地接收数据,而不需要每次都等待传输完成,可以有效地提高程序的响应能力。注意:USB每次传输8个字节的数据!!

        总体流程:初始化 libusb → 2. 获取设备列表 → 3. 遍历设备并查找合适接口和端点 → 4. 打开设备并分离内核驱动 → 5. 配置并分配传输对象 → 6. 提交传输并处理事件 → 7. 回调函数处理接收数据并重新提交传输 → 8. 释放资源并退出

#include <stdio.h>
#include <libusb-1.0/libusb.h>

struct libusb_transfer *keyboard_transfer = NULL;  // 用于传输的对象
#define BUFFER_SIZE 16
unsigned char buffer[BUFFER_SIZE];  // 数据缓冲区

// 回调函数,处理接收到的数据
void callback_revc(struct libusb_transfer *transfer) {
    int i;
    int ret;
    if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
        if (transfer->actual_length > 0) {
            // 打印接收到的数据
            unsigned char *buffer = transfer->buffer;
            for (i = 0; i < transfer->actual_length; i++) {
                printf("%02x ", buffer[i]);
            }
            printf("\n");
        }

        // 重新提交传输,继续接收数据
        ret = libusb_submit_transfer(transfer);
        if (ret < 0) {
            printf("重新提交传输失败\n");
            libusb_cancel_transfer(transfer);
            libusb_free_transfer(transfer);
        }
    } else {
        printf("传输状态错误: %d\n", transfer->status);
        libusb_cancel_transfer(transfer);
        libusb_free_transfer(transfer);
    }
}

int main() {
    libusb_device **device_list;  // 定义设备列表
    libusb_device *dev;           // 定义设备
    struct libusb_device_descriptor desc;  // 设备描述
    struct libusb_config_descriptor *config_desc;  // 配置描述
    struct libusb_interface_descriptor *interface_desc;  // 接口描述
    libusb_device_handle *dev_handle;  // 设备句柄
    int ret, i = 0, j, k, flag = 0;
    ssize_t cnt;
    int endpoint;  // 假设是设备的输入端点地址

    // 初始化 libusb
    ret = libusb_init(NULL);
    // 获取设备列表
    cnt = libusb_get_device_list(NULL, &device_list);

    // 遍历设备列表
    while ((dev = device_list[i++]) != NULL) {
        ret = libusb_get_device_descriptor(dev, &desc);  
        // 获取设备的配置描述符
        ret = libusb_get_config_descriptor(dev, 0, &config_desc);
        // 遍历接口
        for (k = 0; k < config_desc->bNumInterfaces; k++) {
            interface_desc = &config_desc->interface[k].altsetting[0];

            // 检查接口是否为人机设备接口(比如鼠标或键盘)
            if (interface_desc->bInterfaceClass == 3 || interface_desc->bInterfaceProtocol == 1) {
                // 遍历端点
                for (j = 0; j < interface_desc->bNumEndpoints; j++) {
                    if ((interface_desc->endpoint[j].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_INTERRUPT) {
                        // 找到中断传输类型的端点
                        printf("找到中断端点,端点地址: 0x%02x\n", interface_desc->endpoint[j].bEndpointAddress);
                        endpoint = interface_desc->endpoint[j].bEndpointAddress;  // 设置实际的端点地址
                        flag = 1;
                        break;
                    }
                }
            }
            if (flag) break;
        }
        if (flag) break;

        // 释放配置描述符
        libusb_free_config_descriptor(config_desc);
    }

    if (!flag) {
        printf("未找到支持中断传输的端点\n");
        libusb_free_device_list(device_list, 1);
        libusb_exit(NULL);
        return -1;
    }

    // 打开设备
    ret = libusb_open(dev, &dev_handle);
    // 自动分离内核驱动
    ret = libusb_set_auto_detach_kernel_driver(dev_handle, 1);
    // 声明接口
    ret = libusb_claim_interface(dev_handle, interface_desc->bInterfaceNumber);
    // 分配传输对象
    keyboard_transfer = libusb_alloc_transfer(0);
    // 配置中断传输
    libusb_fill_interrupt_transfer(keyboard_transfer, dev_handle, endpoint, buffer, BUFFER_SIZE, callback_revc, keyboard_transfer, 0);
    // 提交传输
    ret = libusb_submit_transfer(keyboard_transfer);

    // 处理事件
    while (1) {
        // libusb_handle_events 处理传输完成的事件
        libusb_handle_events(NULL);
    }

    libusb_release_interface(dev_handle, interface_desc->bInterfaceNumber); // 释放接口
    libusb_close(dev_handle);  // 关闭设备
    libusb_free_device_list(device_list, 1);  // 释放设备列表
    libusb_exit(NULL);   // 退出 libusb
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值