libusb API完全参考手册:从libusb_init到libusb_close全解析
引言:解决USB设备开发的痛点
你是否在开发跨平台USB应用时遇到以下问题?设备兼容性差、系统API差异大、调试困难?libusb(USB设备访问跨平台库)通过统一API解决了这些挑战。本文将系统解析libusb核心API,从初始化到设备操作再到资源释放,助你掌握USB设备开发精髓。
读完本文你将获得:
- 完整的libusb上下文生命周期管理能力
- 设备枚举与描述符解析的实战技巧
- 4种传输类型(控制/批量/中断/等时)的实现方案
- 错误处理与资源管理的最佳实践
- 跨平台兼容性处理的关键要点
1. 核心概念与架构
1.1 libusb核心组件关系
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: 厂商IDproduct_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 异步传输
异步传输工作流程
异步传输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 性能优化技巧
-
批量传输大小优化
- 使用端点最大包大小的倍数作为缓冲区大小
- 避免过小的传输,减少USB总线协议开销
-
异步传输使用
- 对实时性要求高的应用使用异步传输
- 合理设置超时时间,避免不必要的等待
-
减少设备枚举频率
- 缓存设备描述符信息
- 避免频繁枚举设备列表
-
多线程处理
- 将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设备通信。本文涵盖了从初始化到设备操作的完整流程,包括:
- 上下文管理:
libusb_init/libusb_exit构成了libusb操作的基础 - 设备枚举:通过
libusb_get_device_list和描述符获取设备信息 - 设备操作:打开设备、声明接口、配置设备
- 数据传输:控制、批量、中断和异步传输的实现
- 事件处理:热插拔事件和异步传输回调
- 错误处理:错误码解析和调试技巧
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设备固件升级实现》
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



