libusb设备描述符解析实战:从struct libusb_device_descriptor到接口配置

libusb设备描述符解析实战:从struct libusb_device_descriptor到接口配置

【免费下载链接】libusb A cross-platform library to access USB devices 【免费下载链接】libusb 项目地址: https://gitcode.com/gh_mirrors/li/libusb

引言:USB设备识别的痛点与解决方案

你是否曾在开发USB设备应用时,面对复杂的设备描述符结构感到困惑?是否在解析USB设备信息时,因不了解描述符层级关系而无从下手?本文将系统讲解libusb中设备描述符的解析方法,从基础的设备描述符(struct libusb_device_descriptor)到复杂的接口配置,帮助你全面掌握USB设备信息的获取与解析技巧。

读完本文,你将能够:

  • 理解USB描述符的层级结构及各字段含义
  • 掌握使用libusb获取设备描述符的方法
  • 解析配置描述符、接口描述符和端点描述符
  • 处理设备字符串描述符,获取厂商、产品等信息
  • 编写实用的USB设备枚举工具

USB描述符层级结构概述

USB设备描述符采用层级结构组织,从设备到配置,再到接口和端点,形成一个清晰的树形结构。libusb通过一系列结构体来表示这些描述符,主要包括:

mermaid

USB描述符类型与关系

USB设备描述符体系包含以下主要类型,它们之间形成层级关系:

  1. 设备描述符(Device Descriptor):最高层级,描述设备的整体信息
  2. 配置描述符(Configuration Descriptor):描述设备的配置信息,一个设备可以有多个配置
  3. 接口描述符(Interface Descriptor):描述设备的功能接口,一个配置可以包含多个接口
  4. 端点描述符(Endpoint Descriptor):描述接口的通信端点,一个接口可以有多个端点
  5. 字符串描述符(String Descriptor):描述设备的文本信息,如厂商名称、产品名称等

设备描述符(struct libusb_device_descriptor)详解

设备描述符是USB设备的顶层描述符,包含了设备的基本信息。在libusb中,设备描述符由struct libusb_device_descriptor结构体表示。

结构体定义与字段说明

struct libusb_device_descriptor {
    uint8_t  bLength;                 // 描述符长度(字节)
    uint8_t  bDescriptorType;         // 描述符类型(LIBUSB_DT_DEVICE)
    uint16_t bcdUSB;                  // USB规范版本号(BCD码)
    uint8_t  bDeviceClass;            // 设备类代码
    uint8_t  bDeviceSubClass;         // 设备子类代码
    uint8_t  bDeviceProtocol;         // 设备协议代码
    uint8_t  bMaxPacketSize0;         // 端点0最大数据包大小
    uint16_t idVendor;                // 厂商ID
    uint16_t idProduct;               // 产品ID
    uint16_t bcdDevice;               // 设备版本号(BCD码)
    uint8_t  iManufacturer;           // 厂商字符串描述符索引
    uint8_t  iProduct;                // 产品字符串描述符索引
    uint8_t  iSerialNumber;           // 序列号字符串描述符索引
    uint8_t  bNumConfigurations;      // 配置数量
};

重要字段解析

字段名长度说明示例值
bLength1字节描述符长度,固定为18字节0x12
bDescriptorType1字节描述符类型,设备描述符固定为0x01LIBUSB_DT_DEVICE
bcdUSB2字节USB规范版本号,BCD码0x0200表示USB 2.0
bDeviceClass1字节设备类代码0x00表示每个接口独立指定类
idVendor2字节厂商ID,由USB-IF分配0x0483(STMicroelectronics)
idProduct2字节产品ID,由厂商分配0x5740(STM32 USB设备)
bNumConfigurations1字节设备支持的配置数量0x01表示1个配置

获取设备描述符的方法

使用libusb获取设备描述符的基本步骤:

// 初始化libusb上下文
libusb_init(NULL);

// 获取设备列表
libusb_device **devs;
ssize_t cnt = libusb_get_device_list(NULL, &devs);

// 遍历设备列表
for (ssize_t i = 0; i < cnt; i++) {
    libusb_device *dev = devs[i];
    
    // 获取设备描述符
    struct libusb_device_descriptor desc;
    int r = libusb_get_device_descriptor(dev, &desc);
    if (r < 0) {
        fprintf(stderr, "获取设备描述符失败: %d\n", r);
        continue;
    }
    
    // 解析设备描述符信息
    printf("厂商ID: 0x%04x, 产品ID: 0x%04x\n", desc.idVendor, desc.idProduct);
    // ... 解析其他字段
}

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

// 退出libusb
libusb_exit(NULL);

配置描述符(struct libusb_config_descriptor)解析

配置描述符描述了USB设备的一个特定配置,包括该配置下的接口数量、电源需求等信息。一个USB设备可以有多个配置,但同一时间只能激活一个配置。

结构体定义与字段说明

struct libusb_config_descriptor {
    uint8_t  bLength;                 // 描述符长度
    uint8_t  bDescriptorType;         // 描述符类型(LIBUSB_DT_CONFIG)
    uint16_t wTotalLength;            // 配置描述符总长度(包括接口和端点描述符)
    uint8_t  bNumInterfaces;          // 接口数量
    uint8_t  bConfigurationValue;     // 配置值(用于选择此配置)
    uint8_t  iConfiguration;          // 配置字符串描述符索引
    uint8_t  bmAttributes;            // 配置特性
    uint8_t  MaxPower;                // 最大功耗(单位:2mA)
    
    // 接口数组,长度由bNumInterfaces决定
    const struct libusb_interface *interface;
    
    // 额外描述符
    const unsigned char *extra;
    int extra_length;
};

配置特性(bmAttributes)解析

配置特性字段bmAttributes是一个8位标志,用于描述配置的电源管理特性:

说明
D7必须为1,表示总线供电
D61表示自供电,0表示总线供电
D51表示远程唤醒,0表示不支持远程唤醒
D4-D0保留,必须为0

最大功耗(MaxPower)计算

MaxPower字段表示设备在该配置下的最大功耗,单位为2mA。例如:

  • MaxPower = 0x32(50)表示最大功耗为50 × 2mA = 100mA
  • MaxPower = 0xFA(250)表示最大功耗为250 × 2mA = 500mA(USB 2.0设备的最大允许功耗)

获取配置描述符的方法

// 假设已经获取了设备列表和设备描述符
libusb_device *dev = devs[i];
struct libusb_device_descriptor desc;
libusb_get_device_descriptor(dev, &desc);

// 打开设备
libusb_device_handle *handle;
int r = libusb_open(dev, &handle);
if (r < 0) {
    fprintf(stderr, "打开设备失败: %d\n", r);
    continue;
}

// 获取配置描述符
struct libusb_config_descriptor *config;
r = libusb_get_config_descriptor(dev, 0, &config);  // 获取第一个配置
if (r < 0) {
    fprintf(stderr, "获取配置描述符失败: %d\n", r);
    libusb_close(handle);
    continue;
}

// 解析配置描述符信息
printf("配置数量: %d, 接口数量: %d\n", desc.bNumConfigurations, config->bNumInterfaces);
printf("最大功耗: %dmA\n", config->MaxPower * 2);

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

// 关闭设备
libusb_close(handle);

接口描述符与端点描述符详解

接口描述符(struct libusb_interface_descriptor)

接口描述符描述了USB设备配置中的一个功能接口。一个配置可以包含多个接口,每个接口代表设备的一个独立功能。

struct libusb_interface_descriptor {
    uint8_t  bLength;                 // 描述符长度
    uint8_t  bDescriptorType;         // 描述符类型(LIBUSB_DT_INTERFACE)
    uint8_t  bInterfaceNumber;        // 接口号
    uint8_t  bAlternateSetting;       // 备用设置号
    uint8_t  bNumEndpoints;           // 端点数(不包括端点0)
    uint8_t  bInterfaceClass;         // 接口类代码
    uint8_t  bInterfaceSubClass;      // 接口子类代码
    uint8_t  bInterfaceProtocol;      // 接口协议代码
    uint8_t  iInterface;              // 接口字符串描述符索引
    
    // 端点数组,长度由bNumEndpoints决定
    const struct libusb_endpoint_descriptor *endpoint;
    
    // 额外描述符
    const unsigned char *extra;
    int extra_length;
};

接口类代码(bInterfaceClass)是识别接口功能的重要依据,常见值包括:

类代码说明
0x00接口类由端点或接口描述符指定
0x01音频类
0x02通信类
0x03人机接口设备类(HID)
0x06图像类(如扫描仪)
0x07打印机类
0x08大容量存储类
0x09集线器类
0x0ACDC数据类
0xFF厂商特定类

端点描述符(struct libusb_endpoint_descriptor)

端点描述符描述了接口的通信端点,包括端点地址、传输类型、最大数据包大小等信息。

struct libusb_endpoint_descriptor {
    uint8_t  bLength;                 // 描述符长度
    uint8_t  bDescriptorType;         // 描述符类型(LIBUSB_DT_ENDPOINT)
    uint8_t  bEndpointAddress;        // 端点地址
    uint8_t  bmAttributes;            // 端点属性
    uint16_t wMaxPacketSize;          // 最大数据包大小
    uint8_t  bInterval;               // 轮询间隔(单位:ms)
    uint8_t  bRefresh;                // 音频设备专用:刷新速率
    uint8_t  bSynchAddress;           // 音频设备专用:同步端点地址
    
    // 额外描述符
    const unsigned char *extra;
    int extra_length;
};
端点地址(bEndpointAddress)解析

端点地址是一个8位字段,格式如下:

  • 位7:方向,0=输出端点(主机到设备),1=输入端点(设备到主机)
  • 位6-4:保留
  • 位3-0:端点号(0-15)

示例:

  • 0x01:端点1,输出端点
  • 0x81:端点1,输入端点
端点属性(bmAttributes)解析

端点属性字段描述了端点的传输类型和特性:

  • 位1-0:传输类型
    • 00:控制传输(Control)
    • 01:等时传输(Isochronous)
    • 10:批量传输(Bulk)
    • 11:中断传输(Interrupt)
  • 位3-2:同步类型(仅用于等时传输)
  • 位5-4:用法类型(仅用于等时传输)
  • 位7-6:保留
最大数据包大小(wMaxPacketSize)

对于高速(High-Speed)端点,此字段的高两位表示每微帧(125us)可以传输的事务数:

  • 低10位:每事务的最大字节数
  • 高2位:事务数减1(00=1事务,01=2事务,10=3事务)

示例:0x0200表示每微帧2个事务,每个事务最大256字节,总容量为512字节/微帧。

字符串描述符解析

字符串描述符包含了设备的文本信息,如厂商名称、产品名称和序列号等,以UTF-16LE编码表示。

常见的字符串描述符类型

索引描述
iManufacturer厂商名称字符串索引
iProduct产品名称字符串索引
iSerialNumber序列号字符串索引
iConfiguration配置名称字符串索引
iInterface接口名称字符串索引

获取字符串描述符的方法

// 假设已经打开设备,获取了设备句柄handle
char manufacturer[256], product[256], serial[256];

// 获取厂商名称
if (desc.iManufacturer) {
    int r = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, 
                                              (unsigned char*)manufacturer, sizeof(manufacturer));
    if (r > 0) {
        printf("厂商: %s\n", manufacturer);
    }
}

// 获取产品名称
if (desc.iProduct) {
    int r = libusb_get_string_descriptor_ascii(handle, desc.iProduct,
                                              (unsigned char*)product, sizeof(product));
    if (r > 0) {
        printf("产品: %s\n", product);
    }
}

// 获取序列号
if (desc.iSerialNumber) {
    int r = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber,
                                              (unsigned char*)serial, sizeof(serial));
    if (r > 0) {
        printf("序列号: %s\n", serial);
    }
}

实战:USB设备枚举与描述符解析工具

下面我们将综合运用前面所学知识,编写一个完整的USB设备枚举与描述符解析工具,该工具能够:

  • 列出系统中的所有USB设备
  • 显示设备描述符信息
  • 解析配置、接口和端点描述符
  • 获取并显示字符串描述符

完整代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libusb.h>

// USB设备类代码到名称的映射
const char* usb_class_to_string(uint8_t class_code) {
    switch (class_code) {
        case 0x00: return "设备定义的类";
        case 0x01: return "音频类";
        case 0x02: return "通信类";
        case 0x03: return "人机接口设备类(HID)";
        case 0x05: return "物理类";
        case 0x06: return "图像类";
        case 0x07: return "打印机类";
        case 0x08: return "大容量存储类";
        case 0x09: return "集线器类";
        case 0x0A: return "CDC数据类";
        case 0x0B: return "智能卡类";
        case 0x0D: return "内容安全类";
        case 0x0E: return "视频类";
        case 0x0F: return "个人医疗类";
        case 0xDC: return "诊断设备类";
        case 0xE0: return "无线控制器类";
        case 0xEF: return "杂项类";
        case 0xFE: return "应用类";
        case 0xFF: return "厂商特定类";
        default: return "未知类";
    }
}

// USB传输类型到名称的映射
const char* transfer_type_to_string(uint8_t attributes) {
    switch (attributes & 0x03) {
        case 0x00: return "控制传输";
        case 0x01: return "等时传输";
        case 0x02: return "批量传输";
        case 0x03: return "中断传输";
        default: return "未知传输类型";
    }
}

// 打印设备描述符信息
void print_device_descriptor(struct libusb_device_descriptor *desc) {
    printf("===== 设备描述符信息 =====\n");
    printf("USB版本: %d.%d\n", (desc->bcdUSB >> 8), (desc->bcdUSB & 0xFF));
    printf("设备类: 0x%02X (%s)\n", desc->bDeviceClass, usb_class_to_string(desc->bDeviceClass));
    printf("子类: 0x%02X, 协议: 0x%02X\n", desc->bDeviceSubClass, desc->bDeviceProtocol);
    printf("端点0最大数据包大小: %d bytes\n", desc->bMaxPacketSize0);
    printf("厂商ID: 0x%04X, 产品ID: 0x%04X\n", desc->idVendor, desc->idProduct);
    printf("设备版本: %d.%d\n", (desc->bcdDevice >> 8), (desc->bcdDevice & 0xFF));
    printf("配置数量: %d\n", desc->bNumConfigurations);
    printf("==========================\n");
}

// 打印配置描述符信息
void print_config_descriptor(struct libusb_config_descriptor *config) {
    printf("\n===== 配置描述符信息 (配置值: %d) =====\n", config->bConfigurationValue);
    printf("总长度: %d bytes\n", config->wTotalLength);
    printf("接口数量: %d\n", config->bNumInterfaces);
    
    // 解析配置特性
    printf("配置特性: 0x%02X\n", config->bmAttributes);
    printf("  - 自供电: %s\n", (config->bmAttributes & 0x40) ? "是" : "否");
    printf("  - 远程唤醒: %s\n", (config->bmAttributes & 0x20) ? "是" : "否");
    
    // 计算最大功耗
    printf("最大功耗: %dmA\n", config->MaxPower * 2);
    printf("=======================================\n");
}

// 打印接口和端点描述符信息
void print_interface_descriptors(struct libusb_config_descriptor *config) {
    for (int i = 0; i < config->bNumInterfaces; i++) {
        const struct libusb_interface *interface = &config->interface[i];
        
        for (int j = 0; j < interface->num_altsetting; j++) {
            const struct libusb_interface_descriptor *if_desc = &interface->altsetting[j];
            
            printf("\n===== 接口描述符信息 (接口号: %d, 备用设置: %d) =====\n", 
                   if_desc->bInterfaceNumber, if_desc->bAlternateSetting);
            printf("接口类: 0x%02X (%s)\n", if_desc->bInterfaceClass, usb_class_to_string(if_desc->bInterfaceClass));
            printf("子类: 0x%02X, 协议: 0x%02X\n", if_desc->bInterfaceSubClass, if_desc->bInterfaceProtocol);
            printf("端点数: %d\n", if_desc->bNumEndpoints);
            printf("===============================================\n");
            
            // 打印端点信息
            for (int k = 0; k < if_desc->bNumEndpoints; k++) {
                const struct libusb_endpoint_descriptor *ep_desc = &if_desc->endpoint[k];
                
                printf("\n--- 端点描述符 %d ---\n", k+1);
                printf("端点地址: 0x%02X\n", ep_desc->bEndpointAddress);
                printf("  - 方向: %s\n", (ep_desc->bEndpointAddress & 0x80) ? "输入(设备到主机)" : "输出(主机到设备)");
                printf("  - 端点号: %d\n", (ep_desc->bEndpointAddress & 0x0F));
                
                printf("传输类型: %s\n", transfer_type_to_string(ep_desc->bmAttributes));
                
                // 对于等时传输,解析同步类型和用法类型
                if ((ep_desc->bmAttributes & 0x03) == 0x01) {
                    uint8_t sync_type = (ep_desc->bmAttributes >> 2) & 0x03;
                    uint8_t usage_type = (ep_desc->bmAttributes >> 4) & 0x03;
                    
                    printf("同步类型: ");
                    switch (sync_type) {
                        case 0: printf("无同步"); break;
                        case 1: printf("异步"); break;
                        case 2: printf("自适应"); break;
                        case 3: printf("同步"); break;
                        default: printf("未知");
                    }
                    printf("\n");
                    
                    printf("用法类型: ");
                    switch (usage_type) {
                        case 0: printf("数据端点"); break;
                        case 1: printf("反馈端点"); break;
                        case 2: printf("隐式反馈数据端点"); break;
                        default: printf("未知");
                    }
                    printf("\n");
                }
                
                // 解析最大数据包大小
                printf("最大数据包大小: %d bytes\n", ep_desc->wMaxPacketSize & 0x07FF);
                
                // 对于高速端点,显示每微帧的事务数
                if ((ep_desc->wMaxPacketSize & 0x0800) && ((ep_desc->bmAttributes & 0x03) == 0x02)) {
                    int transactions = ((ep_desc->wMaxPacketSize >> 11) & 0x03) + 1;
                    printf("每微帧事务数: %d\n", transactions);
                }
                
                printf("轮询间隔: %dms\n", ep_desc->bInterval);
                printf("-------------------------\n");
            }
        }
    }
}

// 获取并打印字符串描述符
void print_string_descriptors(libusb_device_handle *handle, struct libusb_device_descriptor *desc) {
    char buffer[256];
    
    printf("\n===== 字符串描述符信息 =====\n");
    
    // 获取厂商名称
    if (desc->iManufacturer) {
        int r = libusb_get_string_descriptor_ascii(handle, desc->iManufacturer, 
                                                  (unsigned char*)buffer, sizeof(buffer));
        if (r > 0) {
            printf("厂商名称: %s\n", buffer);
        } else {
            printf("厂商名称: 无法获取\n");
        }
    } else {
        printf("厂商名称: 未提供\n");
    }
    
    // 获取产品名称
    if (desc->iProduct) {
        int r = libusb_get_string_descriptor_ascii(handle, desc->iProduct,
                                                  (unsigned char*)buffer, sizeof(buffer));
        if (r > 0) {
            printf("产品名称: %s\n", buffer);
        } else {
            printf("产品名称: 无法获取\n");
        }
    } else {
        printf("产品名称: 未提供\n");
    }
    
    // 获取序列号
    if (desc->iSerialNumber) {
        int r = libusb_get_string_descriptor_ascii(handle, desc->iSerialNumber,
                                                  (unsigned char*)buffer, sizeof(buffer));
        if (r > 0) {
            printf("序列号: %s\n", buffer);
        } else {
            printf("序列号: 无法获取\n");
        }
    } else {
        printf("序列号: 未提供\n");
    }
    printf("===========================\n");
}

// 枚举并解析USB设备
void enumerate_usb_devices() {
    libusb_device **devs;
    ssize_t cnt;
    
    // 初始化libusb
    int r = libusb_init(NULL);
    if (r < 0) {
        fprintf(stderr, "libusb初始化失败: %d\n", r);
        return;
    }
    
    // 获取设备列表
    cnt = libusb_get_device_list(NULL, &devs);
    if (cnt < 0) {
        fprintf(stderr, "获取设备列表失败: %zd\n", cnt);
        libusb_exit(NULL);
        return;
    }
    
    printf("找到 %zd 个USB设备:\n\n", cnt);
    
    // 遍历设备列表
    for (ssize_t i = 0; i < cnt; i++) {
        libusb_device *dev = devs[i];
        struct libusb_device_descriptor desc;
        libusb_device_handle *handle = NULL;
        
        // 获取设备描述符
        r = libusb_get_device_descriptor(dev, &desc);
        if (r < 0) {
            fprintf(stderr, "获取设备描述符失败: %d\n", r);
            continue;
        }
        
        // 打印设备基本信息
        printf("=============================================\n");
        printf("设备 %zd: 总线 %d, 地址 %d\n", i+1, 
               libusb_get_bus_number(dev), libusb_get_device_address(dev));
        printf("厂商ID: 0x%04X, 产品ID: 0x%04X\n", desc.idVendor, desc.idProduct);
        printf("=============================================\n");
        
        // 打印设备描述符详细信息
        print_device_descriptor(&desc);
        
        // 打开设备以获取更多信息
        r = libusb_open(dev, &handle);
        if (r == 0) {
            // 获取并打印字符串描述符
            print_string_descriptors(handle, &desc);
            
            // 获取并打印配置描述符
            for (int j = 0; j < desc.bNumConfigurations; j++) {
                struct libusb_config_descriptor *config;
                r = libusb_get_config_descriptor(dev, j, &config);
                if (r < 0) {
                    fprintf(stderr, "获取配置描述符 %d 失败: %d\n", j, r);
                    continue;
                }
                
                print_config_descriptor(config);
                print_interface_descriptors(config);
                
                // 释放配置描述符
                libusb_free_config_descriptor(config);
            }
            
            // 关闭设备
            libusb_close(handle);
        } else {
            printf("\n无法打开设备,可能需要root权限或适当的驱动程序\n");
            printf("错误代码: %d\n", r);
        }
        
        printf("\n-------------------------------------------------\n\n");
    }
    
    // 释放设备列表
    libusb_free_device_list(devs, 1);
    
    // 退出libusb
    libusb_exit(NULL);
}

int main(int argc, char *argv[]) {
    enumerate_usb_devices();
    return 0;
}

代码解析与使用说明

上述代码实现了一个完整的USB设备枚举与描述符解析工具,主要功能包括:

  1. 设备枚举:使用libusb_get_device_list()获取系统中的USB设备列表
  2. 设备描述符解析:通过libusb_get_device_descriptor()获取设备基本信息
  3. 配置描述符解析:通过libusb_get_config_descriptor()获取设备配置信息
  4. 接口和端点解析:遍历配置中的接口和端点描述符
  5. 字符串描述符解析:使用libusb_get_string_descriptor_ascii()获取文本信息

编译命令:

gcc -o usb_descriptor_parser usb_descriptor_parser.c -lusb-1.0

使用方法:

# 需要root权限以访问所有设备
sudo ./usb_descriptor_parser

高级应用:基于描述符的设备筛选与配置

在实际应用中,我们通常需要根据设备描述符信息筛选特定设备或配置设备。以下是一些常见的高级应用场景。

根据厂商ID和产品ID筛选设备

// 筛选特定厂商和产品的设备
libusb_device *find_device_by_vid_pid(uint16_t vid, uint16_t pid) {
    libusb_device **devs;
    ssize_t cnt;
    libusb_device *found = NULL;
    
    cnt = libusb_get_device_list(NULL, &devs);
    if (cnt < 0) return NULL;
    
    for (ssize_t i = 0; i < cnt; i++) {
        libusb_device *dev = devs[i];
        struct libusb_device_descriptor desc;
        
        if (libusb_get_device_descriptor(dev, &desc) < 0)
            continue;
            
        if (desc.idVendor == vid && desc.idProduct == pid) {
            found = dev;
            break;
        }
    }
    
    // 如果找到了设备,增加引用计数,否则释放设备列表
    if (found)
        libusb_ref_device(found);
    else
        libusb_free_device_list(devs, 1);
        
    return found;
}

配置设备接口和备用设置

// 配置设备接口和备用设置
int configure_device_interface(libusb_device_handle *handle, int interface_num, int alt_setting) {
    // 首先分离内核驱动(如果存在)
    int r = libusb_kernel_driver_active(handle, interface_num);
    if (r == 1) {
        r = libusb_detach_kernel_driver(handle, interface_num);
        if (r < 0 && r != LIBUSB_ERROR_NOT_SUPPORTED) {
            fprintf(stderr, "分离内核驱动失败: %d\n", r);
            return r;
        }
    }
    
    // 声明接口
    r = libusb_claim_interface(handle, interface_num);
    if (r < 0) {
        fprintf(stderr, "声明接口失败: %d\n", r);
        return r;
    }
    
    // 设置备用设置
    if (alt_setting > 0) {
        r = libusb_set_interface_alt_setting(handle, interface_num, alt_setting);
        if (r < 0) {
            fprintf(stderr, "设置备用设置失败: %d\n", r);
            libusb_release_interface(handle, interface_num);
            return r;
        }
    }
    
    return 0;
}

常见问题与解决方案

权限问题

问题:无法打开设备,错误代码为LIBUSB_ERROR_ACCESS(-3) 解决方案

  1. 使用root权限运行程序
  2. 创建udev规则文件(/etc/udev/rules.d/99-usb-device.rules),添加设备权限:
    SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="5740", MODE="0666"
    
  3. 重新加载udev规则:sudo udevadm control --reload-rules && sudo udevadm trigger

内核驱动冲突

问题:设备被内核驱动占用,无法访问端点 解决方案

  1. 使用libusb_kernel_driver_active()检查驱动是否激活
  2. 使用libusb_detach_kernel_driver()分离内核驱动
  3. 操作完成后使用libusb_attach_kernel_driver()重新附加驱动

描述符解析错误

问题:获取描述符失败或解析结果异常 解决方案

  1. 检查libusb版本是否支持设备的USB规范版本
  2. 验证设备是否正常工作,尝试更换USB端口或线缆
  3. 使用libusb_get_string_descriptor()替代ASCII版本,处理非ASCII字符

总结与展望

本文详细介绍了libusb中设备描述符的层级结构和解析方法,从设备描述符到配置、接口和端点描述符,全面覆盖了USB设备信息的获取与解析技巧。通过实际代码示例,展示了如何使用libusb库函数来枚举设备、获取描述符和解析设备信息。

掌握USB描述符解析是开发USB设备应用的基础,后续可以进一步学习:

  1. USB控制传输:使用控制端点与设备进行配置和管理交互
  2. 批量/中断传输:实现设备与主机之间的高速数据传输
  3. 异步I/O操作:提高USB数据传输的效率和响应性
  4. 热插拔检测:实时监测USB设备的连接和断开事件

通过深入理解USB描述符和libusb库的使用,你将能够开发出功能强大的USB设备应用程序,满足各种USB设备的通信需求。

参考资料

  1. libusb官方文档
  2. USB 3.0规范
  3. USB设备类定义
  4. libusb示例代码

【免费下载链接】libusb A cross-platform library to access USB devices 【免费下载链接】libusb 项目地址: https://gitcode.com/gh_mirrors/li/libusb

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值