libusb API完全参考手册:从libusb_init到libusb_close全解析

libusb API完全参考手册:从libusb_init到libusb_close全解析

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

引言:解决USB设备开发的痛点

你是否在开发跨平台USB应用时遇到以下问题?设备兼容性差、系统API差异大、调试困难?libusb(USB设备访问跨平台库)通过统一API解决了这些挑战。本文将系统解析libusb核心API,从初始化到设备操作再到资源释放,助你掌握USB设备开发精髓。

读完本文你将获得:

  • 完整的libusb上下文生命周期管理能力
  • 设备枚举与描述符解析的实战技巧
  • 4种传输类型(控制/批量/中断/等时)的实现方案
  • 错误处理与资源管理的最佳实践
  • 跨平台兼容性处理的关键要点

1. 核心概念与架构

1.1 libusb核心组件关系

mermaid

1.2 数据结构内存布局

libusb采用灵活的内存布局设计,以适应不同平台需求:

[OS私有数据] -> [struct usbi_transfer] -> [struct libusb_transfer] -> [iso数据包数组]

关键转换宏:

  • LIBUSB_TRANSFER_TO_USBI_TRANSFER: 公共传输结构体转内部结构体
  • USBI_TRANSFER_TO_LIBUSB_TRANSFER: 内部结构体转公共传输结构体

2. 上下文管理API

2.1 初始化与退出

libusb_init
int libusb_init(libusb_context **ctx);

功能:初始化libusb库,创建上下文环境
参数

  • ctx: 输出参数,返回创建的上下文指针,传NULL使用默认上下文

返回值

  • 0: 成功
  • 非0: 错误码(LIBUSB_ERROR_NO_MEM表示内存不足)

示例

libusb_context *ctx = NULL;
int r = libusb_init(&ctx);
if (r < 0) {
    fprintf(stderr, "初始化失败: %s\n", libusb_strerror(r));
    return 1;
}
libusb_exit
void libusb_exit(libusb_context *ctx);

功能:释放libusb上下文及相关资源
参数

  • ctx: 要释放的上下文,NULL表示释放默认上下文

注意事项

  • 必须在所有设备操作完成后调用
  • 会自动取消所有未完成的传输并关闭设备句柄

2.2 上下文配置

libusb_set_option
int libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);

常用选项

  • LIBUSB_OPTION_LOG_LEVEL: 设置日志级别(0-4,对应ERROR到DEBUG)
  • LIBUSB_OPTION_LOG_CB: 设置自定义日志回调函数

示例

// 设置日志级别为DEBUG
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);

// 自定义日志回调
void my_log_cb(libusb_context *ctx, libusb_log_level level, 
              const char *function, const char *format, va_list args) {
    printf("[%s] ", function);
    vprintf(format, args);
}
libusb_set_option(ctx, LIBUSB_OPTION_LOG_CB, my_log_cb);

3. 设备枚举与发现

3.1 设备列表获取

libusb_get_device_list
ssize_t libusb_get_device_list(libusb_context *ctx, libusb_device ***list);

功能:获取系统中的USB设备列表
参数

  • ctx: libusb上下文
  • list: 输出参数,返回设备指针数组

返回值

  • 设备数量(≥0)
  • 错误码(<0)

示例

libusb_device **devs;
ssize_t cnt = libusb_get_device_list(ctx, &devs);
if (cnt < 0) {
    fprintf(stderr, "设备枚举失败: %s\n", libusb_strerror(cnt));
    return 1;
}

// 遍历设备列表
for (ssize_t i = 0; i < cnt; i++) {
    libusb_device *dev = devs[i];
    // 处理设备...
}

// 释放设备列表(不释放设备本身)
libusb_free_device_list(devs, 1); // 第二个参数为1表示解除设备引用

3.2 设备描述符解析

libusb_get_device_descriptor
int libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc);

设备描述符结构

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;  // 配置数量
};

示例

struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) {
    fprintf(stderr, "获取设备描述符失败: %s\n", libusb_strerror(r));
    continue;
}

printf("厂商ID: 0x%04x, 产品ID: 0x%04x\n", 
       desc.idVendor, desc.idProduct);
printf("USB版本: %d.%d\n", 
       (desc.bcdUSB >> 8), (desc.bcdUSB & 0xff));
printf("设备类: 0x%02x, 子类: 0x%02x\n", 
       desc.bDeviceClass, desc.bDeviceSubClass);

3.3 设备打开与关闭

libusb_open_device_with_vid_pid
libusb_device_handle *libusb_open_device_with_vid_pid(
    libusb_context *ctx, uint16_t vendor_id, uint16_t product_id);

功能:通过厂商ID和产品ID打开设备
参数

  • ctx: libusb上下文(NULL使用默认)
  • vendor_id: 厂商ID
  • product_id: 产品ID

返回值:设备句柄(成功)或NULL(失败)

libusb_open与libusb_close
int libusb_open(libusb_device *dev, libusb_device_handle **handle);
void libusb_close(libusb_device_handle *handle);

设备打开流程示例

// 方法1: 使用VID/PID直接打开
libusb_device_handle *handle = libusb_open_device_with_vid_pid(ctx, 0x0483, 0x5750);
if (!handle) {
    fprintf(stderr, "无法打开设备\n");
    return 1;
}

// 方法2: 从设备列表打开
libusb_device_handle *handle;
int r = libusb_open(dev, &handle);
if (r < 0) {
    fprintf(stderr, "打开设备失败: %s\n", libusb_strerror(r));
    return 1;
}

// 使用设备...

// 关闭设备
libusb_close(handle);

4. 配置与接口管理

4.1 配置描述符获取

libusb_get_config_descriptor
int libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index,
                                 struct libusb_config_descriptor **config);

配置描述符结构

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为单位)
    const struct libusb_interface *interface; // 接口数组
    const unsigned char *extra;   // 额外描述符
    int extra_length;             // 额外描述符长度
};

示例

struct libusb_config_descriptor *config;
int r = libusb_get_config_descriptor(dev, 0, &config);
if (r < 0) {
    fprintf(stderr, "获取配置描述符失败: %s\n", libusb_strerror(r));
    return 1;
}

printf("配置值: %d, 接口数量: %d\n", 
       config->bConfigurationValue, config->bNumInterfaces);
printf("最大功耗: %dmA\n", config->MaxPower * 2);

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

4.2 接口声明与释放

libusb_claim_interface与libusb_release_interface
int libusb_claim_interface(libusb_device_handle *dev_handle, int interface_number);
int libusb_release_interface(libusb_device_handle *dev_handle, int interface_number);

示例

// 声明接口
int r = libusb_claim_interface(handle, 0);
if (r < 0) {
    // 尝试分离内核驱动后重试
    if (libusb_kernel_driver_active(handle, 0) == 1) {
        if (libusb_detach_kernel_driver(handle, 0) < 0) {
            fprintf(stderr, "分离内核驱动失败\n");
            libusb_close(handle);
            return 1;
        }
        // 再次尝试声明接口
        r = libusb_claim_interface(handle, 0);
    }
    if (r < 0) {
        fprintf(stderr, "声明接口失败: %s\n", libusb_strerror(r));
        libusb_close(handle);
        return 1;
    }
}

// 使用接口...

// 释放接口
r = libusb_release_interface(handle, 0);
if (r < 0) {
    fprintf(stderr, "释放接口失败: %s\n", libusb_strerror(r));
}

5. 传输操作API详解

5.1 控制传输

libusb_control_transfer
int libusb_control_transfer(libusb_device_handle *dev_handle, uint8_t bmRequestType,
                           uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
                           unsigned char *data, uint16_t wLength, unsigned int timeout);

参数解析

  • bmRequestType: 请求类型(方向、类型、接收者)

    • 位7: 方向(0=输出,1=输入)
    • 位6-5: 类型(0=标准,1=类,2=厂商)
    • 位4-0: 接收者(0=设备,1=接口,2=端点,3=其他)
  • bRequest: 请求码(标准请求见enum libusb_standard_request

  • wValue: 请求值

  • wIndex: 索引值(通常为接口或端点号)

  • data: 数据缓冲区

  • wLength: 数据长度

  • timeout: 超时时间(毫秒)

标准设备请求示例

// 获取字符串描述符
unsigned char data[256];
int len = libusb_control_transfer(handle, 
    LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
    LIBUSB_REQUEST_GET_DESCRIPTOR, 
    (LIBUSB_DT_STRING << 8) | 1, // 描述符类型为字符串,索引为1
    0x0409, // 语言ID(英文)
    data, sizeof(data), 1000);

if (len < 0) {
    fprintf(stderr, "获取字符串描述符失败: %s\n", libusb_strerror(len));
} else {
    // 转换UTF-16LE到UTF-8
    printf("厂商名称: ");
    for (int i = 2; i < len; i += 2) {
        printf("%c", data[i]);
    }
    printf("\n");
}

5.2 批量传输

libusb_bulk_transfer
int libusb_bulk_transfer(libusb_device_handle *dev_handle, uint8_t endpoint,
                        unsigned char *data, int length, int *transferred,
                        unsigned int timeout);

批量传输示例

// 批量传输端点地址(输入端点)
#define BULK_IN_ENDPOINT  0x81
// 批量传输端点地址(输出端点)
#define BULK_OUT_ENDPOINT 0x02
// 端点最大包大小
#define BULK_PACKET_SIZE  64

// 发送数据
unsigned char send_data[BULK_PACKET_SIZE] = "Hello USB Device";
int transferred;
int r = libusb_bulk_transfer(handle, BULK_OUT_ENDPOINT, send_data, 
                            strlen((char*)send_data) + 1, &transferred, 1000);
if (r < 0) {
    fprintf(stderr, "批量发送失败: %s\n", libusb_strerror(r));
} else {
    printf("发送了 %d 字节\n", transferred);
}

// 接收数据
unsigned char recv_data[BULK_PACKET_SIZE];
r = libusb_bulk_transfer(handle, BULK_IN_ENDPOINT, recv_data, 
                        sizeof(recv_data), &transferred, 1000);
if (r < 0) {
    fprintf(stderr, "批量接收失败: %s\n", libusb_strerror(r));
} else {
    printf("接收了 %d 字节: %s\n", transferred, recv_data);
}

5.3 中断传输

libusb_interrupt_transfer
int libusb_interrupt_transfer(libusb_device_handle *dev_handle, uint8_t endpoint,
                             unsigned char *data, int length, int *transferred,
                             unsigned int timeout);

中断传输示例

// 中断输入端点
#define INT_IN_ENDPOINT 0x83
// 中断传输间隔(毫秒)
#define INT_INTERVAL    10

unsigned char data[64];
int transferred;

// 读取中断数据
int r = libusb_interrupt_transfer(handle, INT_IN_ENDPOINT, data, 
                                 sizeof(data), &transferred, 1000);
if (r < 0) {
    fprintf(stderr, "中断传输失败: %s\n", libusb_strerror(r));
} else {
    printf("中断传输接收 %d 字节: ", transferred);
    for (int i = 0; i < transferred; i++) {
        printf("%02x ", data[i]);
    }
    printf("\n");
}

5.4 异步传输

异步传输工作流程

mermaid

异步传输API
libusb_transfer *libusb_alloc_transfer(int iso_packets);
void libusb_free_transfer(libusb_transfer *transfer);
void libusb_fill_bulk_transfer(libusb_transfer *transfer, libusb_device_handle *dev_handle,
                              uint8_t endpoint, unsigned char *buffer, int length,
                              libusb_transfer_cb_fn callback, void *user_data,
                              unsigned int timeout);
int libusb_submit_transfer(libusb_transfer *transfer);
int libusb_cancel_transfer(libusb_transfer *transfer);

异步批量传输示例

// 传输完成回调函数
void transfer_callback(struct libusb_transfer *transfer) {
    if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
        fprintf(stderr, "传输失败: %s\n", 
                libusb_error_name(transfer->status));
    } else {
        printf("异步传输完成,传输了 %d 字节\n", transfer->actual_length);
        // 处理接收到的数据...
    }
    
    // 释放传输结构体
    libusb_free_transfer(transfer);
}

// 启动异步传输
void start_async_transfer(libusb_device_handle *handle) {
    const int BUFFER_SIZE = 512;
    unsigned char *buffer = malloc(BUFFER_SIZE);
    if (!buffer) {
        fprintf(stderr, "内存分配失败\n");
        return;
    }
    
    // 创建传输结构体(非等时传输,iso_packets为0)
    libusb_transfer *transfer = libusb_alloc_transfer(0);
    if (!transfer) {
        fprintf(stderr, "创建传输结构体失败\n");
        free(buffer);
        return;
    }
    
    // 填充批量传输参数
    libusb_fill_bulk_transfer(transfer, handle, BULK_IN_ENDPOINT,
                             buffer, BUFFER_SIZE, transfer_callback,
                             NULL, 1000);
    
    // 提交传输
    int r = libusb_submit_transfer(transfer);
    if (r < 0) {
        fprintf(stderr, "提交传输失败: %s\n", libusb_strerror(r));
        libusb_free_transfer(transfer);
        free(buffer);
    }
}

// 事件处理循环
void event_loop(libusb_context *ctx) {
    struct timeval timeout = {1, 0}; // 1秒超时
    while (1) {
        int r = libusb_handle_events_timeout(ctx, &timeout);
        if (r < 0 && r != LIBUSB_ERROR_TIMEOUT) {
            fprintf(stderr, "事件处理错误: %s\n", libusb_strerror(r));
            break;
        }
        // 检查退出条件...
    }
}

6. 错误处理与调试

6.1 错误码与错误信息

libusb定义了以下常见错误码(完整列表见libusb.h):

错误码描述可能原因
LIBUSB_SUCCESS (0)成功操作完成
LIBUSB_ERROR_IO (-1)I/O错误USB总线错误、设备断开
LIBUSB_ERROR_INVALID_PARAM (-2)参数无效无效的设备句柄、端点号
LIBUSB_ERROR_ACCESS (-3)访问被拒绝权限不足、设备被占用
LIBUSB_ERROR_NO_DEVICE (-4)设备不存在设备已拔出
LIBUSB_ERROR_NOT_FOUND (-5)未找到请求的资源不存在
LIBUSB_ERROR_BUSY (-6)资源被占用
LIBUSB_ERROR_TIMEOUT (-7)超时传输未在指定时间内完成
LIBUSB_ERROR_OVERFLOW (-8)溢出数据溢出
LIBUSB_ERROR_PIPE (-9)管道错误端点禁用或不存在

错误信息获取

int r = libusb_some_function();
if (r < 0) {
    fprintf(stderr, "操作失败: %s (%d)\n", 
            libusb_strerror(r), r);
    // 更详细的错误描述
    fprintf(stderr, "错误名称: %s\n", libusb_error_name(r));
}

6.2 调试技巧

设置日志级别
// 设置全局日志级别
libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);

// 自定义日志回调
void my_log_callback(libusb_context *ctx, libusb_log_level level, 
                    const char *function, const char *format, va_list args) {
    const char *level_str;
    switch (level) {
        case LIBUSB_LOG_LEVEL_ERROR: level_str = "ERROR"; break;
        case LIBUSB_LOG_LEVEL_WARNING: level_str = "WARNING"; break;
        case LIBUSB_LOG_LEVEL_INFO: level_str = "INFO"; break;
        case LIBUSB_LOG_LEVEL_DEBUG: level_str = "DEBUG"; break;
        default: level_str = "UNKNOWN";
    }
    
    printf("[%s] %s: ", level_str, function);
    vprintf(format, args);
}

// 设置自定义日志回调
libusb_set_option(ctx, LIBUSB_OPTION_LOG_CB, my_log_callback);

7. 热插拔事件处理

7.1 热插拔API

int libusb_hotplug_register_callback(libusb_context *ctx, libusb_hotplug_event events,
                                    libusb_hotplug_flag flags, uint16_t vendor_id,
                                    uint16_t product_id, uint8_t dev_class,
                                    libusb_hotplug_callback_fn cb, void *user_data,
                                    libusb_hotplug_callback_handle *handle);
void libusb_hotplug_deregister_callback(libusb_context *ctx, libusb_hotplug_callback_handle handle);

7.2 热插拔事件处理示例

// 热插拔回调函数
int hotplug_callback(libusb_context *ctx, libusb_device *dev,
                    libusb_hotplug_event event, void *user_data) {
    struct libusb_device_descriptor desc;
    int r = libusb_get_device_descriptor(dev, &desc);
    if (r < 0) {
        fprintf(stderr, "获取设备描述符失败\n");
        return 0;
    }
    
    if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
        printf("设备插入: VID=0x%04x, PID=0x%04x\n", desc.idVendor, desc.idProduct);
        // 打开设备并进行初始化...
    } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
        printf("设备拔出: VID=0x%04x, PID=0x%04x\n", desc.idVendor, desc.idProduct);
        // 清理资源...
    }
    
    return 0;
}

// 注册热插拔回调
void setup_hotplug(libusb_context *ctx) {
    libusb_hotplug_callback_handle handle;
    int r;
    
    // 检查热插拔支持
    if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
        fprintf(stderr, "系统不支持热插拔\n");
        return;
    }
    
    // 注册热插拔回调(匹配所有设备)
    r = libusb_hotplug_register_callback(ctx, 
        LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
        LIBUSB_HOTPLUG_ENUMERATE, // 枚举已连接设备
        LIBUSB_HOTPLUG_MATCH_ANY, // 任意厂商ID
        LIBUSB_HOTPLUG_MATCH_ANY, // 任意产品ID
        LIBUSB_HOTPLUG_MATCH_ANY, // 任意设备类
        hotplug_callback, NULL, &handle);
    
    if (r != LIBUSB_SUCCESS) {
        fprintf(stderr, "注册热插拔回调失败: %s\n", libusb_strerror(r));
        return;
    }
    
    printf("热插拔回调已注册\n");
    // 进入事件循环以接收热插拔事件
    while (1) {
        r = libusb_handle_events(ctx);
        if (r < 0) {
            fprintf(stderr, "事件处理错误: %s\n", libusb_strerror(r));
        }
    }
    
    // 注销回调(实际应用中通常不需要,因为事件循环会一直运行)
    libusb_hotplug_deregister_callback(ctx, handle);
}

8. 跨平台注意事项

8.1 平台特定代码处理

#ifdef _WIN32
// Windows特定代码
#include <windows.h>

// Windows需要处理WinUSB驱动
void windows_specific_setup(libusb_device_handle *handle) {
    // 可能需要设置特定于Windows的选项
}
#elif defined(__linux__)
// Linux特定代码
#include <unistd.h>

// Linux需要处理udev规则和权限
void linux_specific_setup() {
    // 检查设备权限
    struct stat st;
    if (stat("/dev/bus/usb/001/001", &st) == 0) {
        if ((st.st_mode & S_IRUSR) == 0 || (st.st_mode & S_IWUSR) == 0) {
            fprintf(stderr, "警告: 设备权限不足\n");
        }
    }
}
#elif defined(__APPLE__)
// macOS特定代码
#include <IOKit/IOKitLib.h>

// macOS需要处理USB设备访问权限
void macos_specific_setup() {
    // macOS特定初始化
}
#endif

8.2 编译配置

CMakeLists.txt示例

cmake_minimum_required(VERSION 3.10)
project(usb_app)

# 查找libusb库
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)

# 添加可执行文件
add_executable(usb_app main.c)

# 链接libusb库
target_link_libraries(usb_app ${LIBUSB_LIBRARIES})

# 添加头文件路径
target_include_directories(usb_app PUBLIC ${LIBUSB_INCLUDE_DIRS})

# 添加编译选项
target_compile_options(usb_app PUBLIC ${LIBUSB_CFLAGS_OTHER})

9. 最佳实践与性能优化

9.1 资源管理

资源管理检查清单

  •  确保每个libusb_init对应一个libusb_exit
  •  确保每个libusb_open对应一个libusb_close
  •  确保每个libusb_claim_interface对应一个libusb_release_interface
  •  确保每个libusb_alloc_transfer对应一个libusb_free_transfer
  •  确保每个libusb_get_config_descriptor对应一个libusb_free_config_descriptor

资源管理封装示例

// 使用RAII思想封装设备操作(C++示例)
class USBDevice {
private:
    libusb_context *ctx;
    libusb_device_handle *handle;
    bool claimed;
    int interface;
    
public:
    USBDevice(uint16_t vid, uint16_t pid) : ctx(nullptr), handle(nullptr), 
                                           claimed(false), interface(0) {
        int r = libusb_init(&ctx);
        if (r < 0) throw std::runtime_error("初始化失败");
        
        handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
        if (!handle) {
            libusb_exit(ctx);
            throw std::runtime_error("打开设备失败");
        }
    }
    
    ~USBDevice() {
        if (claimed) {
            libusb_release_interface(handle, interface);
        }
        if (handle) {
            libusb_close(handle);
        }
        if (ctx) {
            libusb_exit(ctx);
        }
    }
    
    void claim_interface(int iface) {
        int r = libusb_claim_interface(handle, iface);
        if (r < 0) throw std::runtime_error("声明接口失败");
        claimed = true;
        interface = iface;
    }
    
    // 其他USB操作方法...
};

9.2 性能优化技巧

  1. 批量传输大小优化

    • 使用端点最大包大小的倍数作为缓冲区大小
    • 避免过小的传输,减少USB总线协议开销
  2. 异步传输使用

    • 对实时性要求高的应用使用异步传输
    • 合理设置超时时间,避免不必要的等待
  3. 减少设备枚举频率

    • 缓存设备描述符信息
    • 避免频繁枚举设备列表
  4. 多线程处理

    • 将USB操作放在单独线程,避免阻塞UI
    • 使用事件处理线程处理异步传输回调

性能优化示例

// 优化的批量传输函数
int optimized_bulk_transfer(libusb_device_handle *handle, uint8_t endpoint,
                           unsigned char *data, int length, int *transferred,
                           unsigned int timeout) {
    const int MAX_PACKET_SIZE = 512; // 根据端点实际最大包大小调整
    int total_transferred = 0;
    int remaining = length;
    unsigned char *ptr = data;
    
    while (remaining > 0) {
        int chunk_size = MIN(remaining, MAX_PACKET_SIZE);
        int r = libusb_bulk_transfer(handle, endpoint, ptr, chunk_size,
                                    transferred, timeout);
        if (r < 0) return r;
        
        total_transferred += *transferred;
        ptr += *transferred;
        remaining -= *transferred;
        
        if (*transferred != chunk_size) break; // 可能是最后一个包
    }
    
    *transferred = total_transferred;
    return LIBUSB_SUCCESS;
}

10. 完整示例:USB设备信息查看器

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

// 打印设备描述符信息
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, 子类: 0x%02X, 协议: 0x%02X\n",
           desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol);
    printf("端点0最大包大小: %d\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("字符串索引: 厂商=%d, 产品=%d, 序列号=%d\n",
           desc->iManufacturer, desc->iProduct, desc->iSerialNumber);
}

// 打印配置描述符信息
void print_config_descriptor(struct libusb_config_descriptor *config) {
    printf("\n=== 配置描述符 ===\n");
    printf("配置值: %d, 接口数量: %d\n",
           config->bConfigurationValue, config->bNumInterfaces);
    printf("属性: 0x%02X", config->bmAttributes);
    if (config->bmAttributes & 0x80) printf(" (自供电)");
    if (config->bmAttributes & 0x40) printf(" (远程唤醒)");
    printf("\n");
    printf("最大功耗: %dmA\n", config->MaxPower * 2);
    
    // 打印接口信息
    for (int i = 0; i < config->bNumInterfaces; i++) {
        const struct libusb_interface *iface = &config->interface[i];
        printf("\n--- 接口 %d ---\n", i);
        printf("备用设置数量: %d\n", iface->num_altsetting);
        
        for (int j = 0; j < iface->num_altsetting; j++) {
            const struct libusb_interface_descriptor *altsetting = &iface->altsetting[j];
            printf("  备用设置 %d:\n", altsetting->bAlternateSetting);
            printf("    接口类: 0x%02X, 子类: 0x%02X, 协议: 0x%02X\n",
                   altsetting->bInterfaceClass, altsetting->bInterfaceSubClass,
                   altsetting->bInterfaceProtocol);
            printf("    端点数量: %d\n", altsetting->bNumEndpoints);
            
            // 打印端点信息
            for (int k = 0; k < altsetting->bNumEndpoints; k++) {
                const struct libusb_endpoint_descriptor *ep = &altsetting->endpoint[k];
                printf("    端点 %d: 地址=0x%02X", k, ep->bEndpointAddress);
                printf(", 方向: %s", (ep->bEndpointAddress & LIBUSB_ENDPOINT_IN) ? "IN" : "OUT");
                
                const char *transfer_type;
                switch (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) {
                    case LIBUSB_TRANSFER_TYPE_CONTROL: transfer_type = "控制"; break;
                    case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: transfer_type = "等时"; break;
                    case LIBUSB_TRANSFER_TYPE_BULK: transfer_type = "批量"; break;
                    case LIBUSB_TRANSFER_TYPE_INTERRUPT: transfer_type = "中断"; break;
                    default: transfer_type = "未知";
                }
                printf(", 类型: %s", transfer_type);
                
                printf(", 最大包大小: %d", ep->wMaxPacketSize);
                printf(", 间隔: %d\n", ep->bInterval);
            }
        }
    }
}

// 获取并打印字符串描述符
void print_string_descriptors(libusb_device_handle *handle, struct libusb_device_descriptor *desc) {
    printf("\n=== 字符串描述符 ===\n");
    
    // 厂商字符串
    if (desc->iManufacturer > 0) {
        unsigned char *data = NULL;
        int r = libusb_get_string_descriptor_ascii(handle, desc->iManufacturer, NULL, 0);
        if (r > 0) {
            data = malloc(r);
            if (data) {
                r = libusb_get_string_descriptor_ascii(handle, desc->iManufacturer, data, r);
                if (r > 0) {
                    printf("厂商: %s\n", data);
                }
                free(data);
            }
        }
    }
    
    // 产品字符串
    if (desc->iProduct > 0) {
        unsigned char *data = NULL;
        int r = libusb_get_string_descriptor_ascii(handle, desc->iProduct, NULL, 0);
        if (r > 0) {
            data = malloc(r);
            if (data) {
                r = libusb_get_string_descriptor_ascii(handle, desc->iProduct, data, r);
                if (r > 0) {
                    printf("产品: %s\n", data);
                }
                free(data);
            }
        }
    }
    
    // 序列号字符串
    if (desc->iSerialNumber > 0) {
        unsigned char *data = NULL;
        int r = libusb_get_string_descriptor_ascii(handle, desc->iSerialNumber, NULL, 0);
        if (r > 0) {
            data = malloc(r);
            if (data) {
                r = libusb_get_string_descriptor_ascii(handle, desc->iSerialNumber, data, r);
                if (r > 0) {
                    printf("序列号: %s\n", data);
                }
                free(data);
            }
        }
    }
}

int main(int argc, char **argv) {
    int r;
    libusb_context *ctx = NULL;
    libusb_device **devs;
    ssize_t cnt;
    
    // 初始化libusb
    r = libusb_init(&ctx);
    if (r < 0) {
        fprintf(stderr, "初始化libusb失败: %s\n", libusb_strerror(r));
        return 1;
    }
    
    // 设置日志级别
    libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_WARNING);
    
    // 获取设备列表
    cnt = libusb_get_device_list(ctx, &devs);
    if (cnt < 0) {
        fprintf(stderr, "获取设备列表失败: %s\n", libusb_strerror(cnt));
        libusb_exit(ctx);
        return 1;
    }
    
    printf("找到 %zd 个USB设备\n\n", cnt);
    
    // 遍历设备列表
    for (ssize_t i = 0; i < cnt; i++) {
        libusb_device *dev = devs[i];
        struct libusb_device_descriptor desc;
        char path[256];
        
        // 获取设备路径
        libusb_get_port_path(ctx, dev, path, sizeof(path));
        
        // 获取设备描述符
        r = libusb_get_device_descriptor(dev, &desc);
        if (r < 0) {
            fprintf(stderr, "获取设备描述符失败: %s\n", libusb_strerror(r));
            continue;
        }
        
        // 打印设备基本信息
        printf("========================================\n");
        printf("设备 %zd: 路径=%s\n", i+1, path);
        printf("总线号: %d, 地址: %d, 速度: ",
               libusb_get_bus_number(dev), libusb_get_device_address(dev));
        
        switch (libusb_get_device_speed(dev)) {
            case LIBUSB_SPEED_LOW: printf("低速 (1.5Mbps)"); break;
            case LIBUSB_SPEED_FULL: printf("全速 (12Mbps)"); break;
            case LIBUSB_SPEED_HIGH: printf("高速 (480Mbps)"); break;
            case LIBUSB_SPEED_SUPER: printf("超高速 (5Gbps)"); break;
            case LIBUSB_SPEED_SUPER_PLUS: printf("超高速+ (10Gbps)"); break;
            default: printf("未知");
        }
        printf("\n");
        
        print_device_descriptor(&desc);
        
        // 打开设备以获取更多信息
        libusb_device_handle *handle = NULL;
        r = libusb_open(dev, &handle);
        if (r == 0) {
            // 获取配置描述符
            struct libusb_config_descriptor *config = NULL;
            r = libusb_get_config_descriptor(dev, 0, &config);
            if (r == 0) {
                print_config_descriptor(config);
                libusb_free_config_descriptor(config);
            } else {
                fprintf(stderr, "获取配置描述符失败: %s\n", libusb_strerror(r));
            }
            
            // 获取字符串描述符
            print_string_descriptors(handle, &desc);
            
            // 关闭设备
            libusb_close(handle);
        } else {
            fprintf(stderr, "无法打开设备: %s\n", libusb_strerror(r));
        }
        
        printf("========================================\n\n");
    }
    
    // 释放设备列表
    libusb_free_device_list(devs, 1);
    
    // 退出libusb
    libusb_exit(ctx);
    
    return 0;
}

11. 总结与进阶

11.1 核心知识点回顾

libusb提供了一套强大而灵活的API,用于跨平台USB设备通信。本文涵盖了从初始化到设备操作的完整流程,包括:

  1. 上下文管理libusb_init/libusb_exit构成了libusb操作的基础
  2. 设备枚举:通过libusb_get_device_list和描述符获取设备信息
  3. 设备操作:打开设备、声明接口、配置设备
  4. 数据传输:控制、批量、中断和异步传输的实现
  5. 事件处理:热插拔事件和异步传输回调
  6. 错误处理:错误码解析和调试技巧

11.2 进阶学习资源

  • 官方文档libusb.info
  • 源代码示例:libusb源码中的examples目录
  • 调试工具
    • lsusb(Linux):列出USB设备信息
    • usbview(Windows):USB设备查看器
    • Wireshark:USB流量捕获与分析
  • 相关规范

11.3 常见问题解答

Q: 如何处理不同操作系统下的设备权限问题?
A: Linux下可通过udev规则设置设备权限,Windows下需要正确的驱动程序,macOS下可能需要系统扩展权限。

Q: 如何提高USB传输速度?
A: 使用批量传输类型、优化缓冲区大小、减少传输次数、使用异步传输、确保设备工作在高速/超高速模式。

Q: 如何处理USB设备热插拔?
A: 使用libusb的热插拔API注册回调函数,在设备插入时打开设备,在设备拔出时清理资源。

Q: 如何调试USB传输问题?
A: 使用libusb的调试日志、USB协议分析仪、Wireshark捕获USB流量,检查设备描述符和端点配置。


希望本文能帮助你掌握libusb API的使用。如有任何问题或建议,请在评论区留言。如果你觉得本文有帮助,请点赞、收藏并关注,获取更多USB开发技巧和最佳实践!

下一篇预告:《libusb高级应用:USB设备固件升级实现》

【免费下载链接】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、付费专栏及课程。

余额充值