零.声明
本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下:
第一篇:蓝牙综合介绍 ,主要介绍蓝牙的一些概念,产生背景,发展轨迹,市面蓝牙介绍,以及蓝牙开发板介绍。
第二篇:Transport层介绍,主要介绍蓝牙协议栈跟蓝牙芯片之前的硬件传输协议,比如基于UART的H4,H5,BCSP,基于USB的H2等
第三篇:传统蓝牙controller介绍,主要介绍传统蓝牙芯片的介绍,包括射频层(RF),基带层(baseband),链路管理层(LMP)等
第四篇:传统蓝牙host介绍,主要介绍传统蓝牙的协议栈,比如HCI,L2CAP,SDP,RFCOMM,HFP,SPP,HID,AVDTP,AVCTP,A2DP,AVRCP,OBEX,PBAP,MAP等等一系列的协议吧。
第五篇:低功耗蓝牙controller介绍,主要介绍低功耗蓝牙芯片,包括物理层(PHY),链路层(LL)
第六篇:低功耗蓝牙host介绍,低功耗蓝牙协议栈的介绍,包括HCI,L2CAP,ATT,GATT,SM等
第七篇:蓝牙芯片介绍,主要介绍一些蓝牙芯片的初始化流程,基于HCI vendor command的扩展
第八篇:附录,主要介绍以上常用名词的介绍以及一些特殊流程的介绍等。
另外,开发板如下所示,对于想学习蓝牙协议栈的最好人手一套。以便更好的学习蓝牙协议栈,相信我,学完这一套视频你将拥有修改任何协议栈的能力(比如Linux下的bluez,Android下的bluedroid)。
-------------------------------------------------------------------------------------------------------------------------
蓝牙视频教程(跟韦东山老师合作):
https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22329603896.20.5aeb41f98e267j&id=693788592796
蓝牙交流扣扣群:765961169
Github代码:GitHub - sj15712795029/bluetooth_stack: 这是一个开源的双模蓝牙协议栈(bluetooth.stack)(btstack),可以运行在STM32,Linux.,包含HCI,L2CAP,SDP,RFCOMM,HFP,SPP,A2DP,AVRCP,AVDTP,AVCTP,OBEX,PBAP等协议,后续会继续维护,以达到商用的目的
入手开发板:https://shop220811498.taobao.com/category-1542116976.htm?spm=a1z10.5-c-s.w4010-22329603913.7.39ca7dbe2EA0K3&search=y&catName=%C0%B6%D1%C0%BF%AA%B7%A2%B0%E5#bd
蓝牙学习目录:一篇文章足够你学习蓝牙技术,提供史上最全的蓝牙技术(传统蓝牙/低功耗蓝牙)文章总结,文档下载总结(2020/12/11更新)_Wireless_Link的博客-CSDN博客_蓝牙eir
--------------------------------------------------------------------------------------------------------------------------
本文的目的就是在ubuntu上驱动CSR8510,大概有两种做法:
- 原生的linux默认免驱方式
- 使用驱动方式(USB转tty) + 蓝牙协议栈方式来驱动
- 使用libusb + 蓝牙协议栈方式来驱动
其中免驱方式我们已经在剖析Linux蓝牙子系统来驱动USB接口的蓝牙(CSR8510蓝牙dongle)原理-CSDN博客介绍过了,所以不再赘述,我们本文的目的是最后一种用libusb来驱动
一. CSR8510介绍
CSR8510 是 Qualcomm(高通)旗下CSR公司推出的一款 蓝牙无线通信芯片,主要应用于蓝牙适配器(USB Dongle)或嵌入式设备中,支持蓝牙数据传输和音频传输功能。以下是其关键信息:
1. 主要特性
- 蓝牙版本:支持 蓝牙4.0(兼容蓝牙2.1+EDR、3.0、4.0 BLE),支持双模(经典蓝牙 + 低功耗蓝牙 BLE)。
- 接口:集成 USB 2.0 控制器,可直接通过USB接口与主机设备(如PC、智能设备)连接。
- 协议支持:
-
- 经典蓝牙协议:HID(键鼠)、A2DP(音频传输)、HFP(通话)、AVRCP(遥控)、SPP(串口)等。
- 低功耗蓝牙(BLE):适用于物联网设备、传感器等低功耗场景。
- 传输速率:理论最大速率 3 Mbps(EDR增强速率)。
- 传输距离:约 10米(视环境干扰情况而定)。
2. 典型应用场景
- 蓝牙适配器:用于为无蓝牙功能的电脑、电视等设备添加蓝牙连接能力。
- 无线音频设备:连接蓝牙耳机、音箱,支持A2DP高品质音频传输。
- 外设连接:无线键鼠、游戏手柄、打印机等(通过HID/SPP协议)。
- 物联网(IoT):通过BLE与传感器、智能家居设备通信。
3. 技术优势
- 低功耗:BLE模式显著降低能耗,适合电池供电设备。
- 兼容性:广泛支持Windows、Linux、macOS等操作系统。
- 集成度高:单芯片集成射频、基带和协议栈,简化设计。
4. 注意事项
- 驱动依赖:在部分系统中(如Windows 10/11)可能需手动安装驱动。
- 版本差异:不同厂商的CSR8510适配器可能阉割部分功能(如仅支持经典蓝牙,未启用BLE)。
- 性能限制:蓝牙4.0的传输速率和延迟不如更新的蓝牙5.x,若需更高性能需选择新款芯片。
二. 卸载原生的驱动
目前有两种方式来禁用掉跟libusb的冲突
1. 卸载驱动方式
在Ubuntu系统中,CSR8510蓝牙适配器的驱动通常由内核模块(如 btusb
)提供,属于系统默认集成的一部分。所以如果要做后两种方式,需要先把原生的免驱方式彻底屏蔽掉,也就是不让系统自动加载btusb驱动
a. 确认驱动信息
首先,确认系统是否已识别设备及当前加载的驱动:
lsmod | grep -i btusb
若输出显示 btusb
模块,则表明系统正使用该驱动。
b. 尝试临时卸载驱动
可临时移除 btusb
模块(需root权限):
sudo modprobe -r btusb
卸载后我们再查看btusb驱动已经不存在了
在这里我们就不用永久方式卸载btusb驱动了哈,风险比较大,有的小伙伴可能恢复不过来,所以我就不介绍了,但是只能告诉你们是可行的哈
2. libusb函数禁用到驱动
我们就直接上代码了哈,不用做更多的解释
if (libusb_kernel_driver_active(vnd_usb.dev_handle, 0) == 1) {result = libusb_detach_kernel_driver(vnd_usb.dev_handle, 0);if (result != 0) {ALOGE("libusb_detach_kernel_driver fail: %s", libusb_error_name(result));libusb_close(vnd_usb.dev_handle);libusb_exit(vnd_usb.usb_context);return;}
}
三. USB蓝牙的概念
Core spec定义如下
可以看到
hci command是用Control类型来控制,interface endpoint是0
hci event是用Interrupt类型来接收,interface endpoint是0x81
hci acl是用Bulk类型来收发数据,interface endpoint是0x82(IN),0x02(OUT)
hci sco是用isoch类型来收发数据,interface endpoint是0x83(IN),0x03(OUT)
HCI 数据类型 | 包类型标识 | 传输方向 | USB 端点 | libusb 函数 |
Command | 0x01 | Host→Dev | EP2 OUT (0x02) | libusb_control_transfer |
Event | 0x04 | Dev→Host | EP1 IN (0x81) |
|
ACL Data | 0x02 | 双向 | EP2 OUT (0x02)/IN (0x82) |
|
SCO Data | 0x03 | 双向 | (需确认是否支持) |
|
NOTED:需要注意的是H4跟H2出了uart跟usb的差异外,h2没有h4 transport的packet type哈,为啥呢?因为每种type都有EP地址,所以不需要,这个很好理解!
四. 使用libusb驱动
libusb是一个开源的、跨平台的用户态 USB 设备驱动库(USB host角色),允许开发者在不依赖操作系统内核驱动的情况下直接与 USB 设备通信。它广泛应用于嵌入式系统、逆向工程、数据采集、自定义设备控制等领域。
- 跨平台支持:
-
- Linux / macOS / Windows / Android 等。
- 统一 API,无需为不同平台编写不同代码。
- 用户态驱动:
-
- 无需编写内核驱动,直接通过用户态程序操作 USB 设备。
- 开发效率高,调试方便。
- 异步 & 同步传输:
-
- 支持同步阻塞式操作(如
libusb_bulk_transfer
)。 - 提供异步非阻塞接口(如
libusb_submit_transfer
),适合高性能场景。
- 支持同步阻塞式操作(如
- 设备枚举与管理:
-
- 支持 USB 设备发现、打开、关闭、配置接口等。
- 获取设备描述符(厂商 ID、产品 ID、序列号等)。
- 传输模式:
-
- 控制传输(Control Transfer):用于设备配置(如
libusb_control_transfer
)。 - 批量传输(Bulk Transfer):大数据量传输(如文件读写)。
- 中断传输(Interrupt Transfer):实时性要求高的场景(如 HID 设备)。
- 等时传输(Isochronous Transfer):音视频流传输(需硬件支持)。
- 控制传输(Control Transfer):用于设备配置(如
我们就是用libusb来驱动CSR8510
整个架构包含以下组件:
- 蓝牙协议栈:这个不用做过多解释哈·
- libusb:这个充当usb host角色,直接用libusb来操作usb设备,不需要内核介入
- CSR8510:蓝牙dongle
下面我们就来说明下来驱动csr8510的步骤吧
1. libusb的初始化
libusb_context *usb_context;result = libusb_init(&vnd_usb.usb_context);
if (result < 0) {ALOGE("usb_vendor_open fail: %s", libusb_error_name(result));return;
}
libusb_init
是 libusb 库的初始化函数,用于创建并初始化 libusb 的上下文(Context)。它是所有 libusb 操作的起点,必须在调用其他 libusb 函数前执行。以下是详细解释:
函数原型
int libusb_init(libusb_context **ctx);
参数
ctx
:指向libusb_context
指针的指针。如果传入NULL
,libusb 会使用默认的全局上下文;如果传入非NULL
的指针地址,则会创建独立的上下文(适用于多线程或复杂场景)。
返回值
LIBUSB_SUCCESS
(0):初始化成功。- 负数:错误码(如内存不足、权限问题等)。
2. libusb匹配特定的usb设备(csr8510)
libusb_device_handle *dev_handle;vnd_usb.dev_handle = libusb_open_device_with_vid_pid(vnd_usb.usb_context, CSR8510_USB_VID, CSR8510_USB_PID);
if (!vnd_usb.dev_handle) {ALOGE("usb_vendor_open can not find 0x%04x:0x%04x device",CSR8510_USB_VID,CSR8510_USB_PID);libusb_exit(vnd_usb.usb_context);return;
}
libusb_open_device_with_vid_pid
是 libusb 库中的一个便捷函数,用于通过 厂商ID(VID) 和 产品ID(PID) 快速打开匹配的 USB 设备。它简化了设备发现和初始化的流程,适用于已知目标设备 VID/PID 的场景。
函数原型
libusb_device_handle* libusb_open_device_with_vid_pid(libusb_context* ctx, // libusb 上下文(通常填 NULL)uint16_t vendor_id, // 设备厂商ID(VID)uint16_t product_id // 设备产品ID(PID)
);
参数
ctx
:libusb 上下文指针。传入NULL
表示使用默认全局上下文。vendor_id
:目标设备的 16 位厂商 ID(VID),例如0x1234
。product_id
:目标设备的 16 位产品 ID(PID),例如0x5678
。
返回值
- 成功:返回已打开的 USB 设备句柄(
libusb_device_handle*
)。 - 失败:返回
NULL
(设备未找到或打开失败)。
其中csr8510的vid/pid定义如下:
#define CSR8510_USB_VID 0x0a12
#define CSR8510_USB_PID 0x0001
我们是通过lsusb来查看的哈
3. libusb卸载内核驱动
if (libusb_kernel_driver_active(vnd_usb.dev_handle, 0) == 1) {result = libusb_detach_kernel_driver(vnd_usb.dev_handle, 0);if (result != 0) {ALOGE("libusb_detach_kernel_driver fail: %s", libusb_error_name(result));libusb_close(vnd_usb.dev_handle);libusb_exit(vnd_usb.usb_context);return;}
}
a. libusb_kernel_driver_active
功能
- 检测内核驱动是否绑定了设备的某个接口。
- 当操作系统内核已为 USB 设备的某个接口加载了驱动(例如 USB 串口驱动、HID 驱动等),用户态程序需要通过此函数确认驱动状态。
原型
int libusb_kernel_driver_active(libusb_device_handle *dev_handle, int interface_number);
参数
dev_handle
:已打开的 USB 设备句柄。interface_number
:要检查的接口编号(通常为 0, 1, 2...)。
返回值
1
:内核驱动已绑定该接口。0
:内核驱动未绑定。- 负数:错误码(如
LIBUSB_ERROR_NO_DEVICE
表示设备已断开)。
b. libusb_detach_kernel_driver
功能
- 解除内核驱动对 USB 设备接口的绑定。
- 用户态程序需要通过此函数释放内核驱动对接口的控制权,才能通过 libusb 直接操作设备。
原型
int libusb_detach_kernel_driver(libusb_device_handle *dev_handle, int interface_number);
参数
dev_handle
:已打开的 USB 设备句柄。interface_number
:要解除绑定的接口编号。
返回值
0
:成功解除绑定。- 负数:错误码(如
LIBUSB_ERROR_NOT_FOUND
表示驱动未绑定)。
4. libusb发现usb interface/声明
其中发现usb interface是可选的操作,正常你如果想做成一个通用的,鉴于不同的芯片USB EP addr可能有细微差异,所以正常应该需要扫描USB EP addr,但是我们csr8510是固定的,所以我们就不做这个工作了,但是声明还是要做的,我们来看下代码
result = libusb_claim_interface(vnd_usb.dev_handle, 0);if (result < 0) {ALOGE("libusb_claim_interface: %s", libusb_error_name(result));libusb_close(vnd_usb.dev_handle);libusb_exit(vnd_usb.usb_context);return;}
我们来介绍下libusb_claim_interface
函数
功能
- 声明对 USB 设备某个接口的独占访问权。
- 在操作 USB 设备前,必须通过此函数声明要使用的接口(Interface),否则无法进行数据传输。
原型
int libusb_claim_interface(libusb_device_handle *dev_handle, int interface_number);
参数
dev_handle
:已打开的 USB 设备句柄。interface_number
:接口编号(通常为0
,1
,2
...,根据设备描述符确定)。
返回值
0
:成功声明接口。- 负数:错误码(如
LIBUSB_ERROR_BUSY
表示接口已被占用)。
关键作用
- 解除内核驱动占用(Linux 特有):
在调用前需确保内核驱动已解除绑定(通过libusb_detach_kernel_driver
)。 - 独占访问:
同一时刻只能有一个程序(或线程)操作该接口。 - 资源管理:
必须与libusb_release_interface
配对使用。
5. libusb发送hci command/acl数据
a. 发送hci command
void send_hci_command_packet(uint8_t *data, int32_t len)
{int retry_count = 0;int result = 0;const char* func_name = __func__;do {result = libusb_control_transfer(vnd_usb.dev_handle,LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,0x00, 0x0000, 0x0000, data, len, 1000);if (result >= 0) {if (result == len) {return;}ALOGW("%s: Partial transfer: sent %d/%d bytes (retry %d/%d)", func_name, result, len, retry_count+1, MAX_RETRY_ATTEMPTS);result = LIBUSB_ERROR_IO;} else {ALOGW("%s: Attempt %d/%d failed: %s (%d)", func_name, retry_count+1, MAX_RETRY_ATTEMPTS,libusb_error_name(result), result);}if (result == LIBUSB_ERROR_NO_DEVICE || result == LIBUSB_ERROR_NOT_FOUND ||result == LIBUSB_ERROR_ACCESS) {break;}if (retry_count < MAX_RETRY_ATTEMPTS) {usleep(RETRY_DELAY_MS * 1000);}} while (retry_count++ < MAX_RETRY_ATTEMPTS);ALOGE("%s: Failed after %d attempts: %s (%d), last sent %d/%d bytes",func_name, retry_count, libusb_error_name(result), result, (result > 0) ? result : 0, len);
}
我们发送hci comamnd主要通过libusb_control_transfer
函数,下面我们来介绍下这个函数的功能
功能
- 执行 USB 控制传输(Control Transfer)。
- 控制传输用于设备配置、命令发送、状态查询等,是 USB 协议中的基础传输类型。
原型
int libusb_control_transfer(libusb_device_handle *dev_handle,uint8_t bmRequestType, // 请求类型(方向/类型/接收方)uint8_t bRequest, // 请求码(如 GET_DESCRIPTOR)uint16_t wValue, // 请求参数(根据请求码定义)uint16_t wIndex, // 接口/端点索引(根据请求码定义)unsigned char *data, // 数据缓冲区uint16_t wLength, // 数据长度unsigned int timeout // 超时时间(毫秒)
);
返回值
- 成功:实际传输的字节数。
- 失败:负数错误码(如
LIBUSB_ERROR_TIMEOUT
)。
典型用途
- 获取设备描述符(如
GET_DESCRIPTOR
请求)。 - 设置设备配置(如
SET_CONFIGURATION
)。 - 发送厂商自定义命令(Vendor-Specific Commands)。
b. 发送hci acl
void send_hci_acl_packet(uint8_t *data, int32_t len)
{int retry_count = 0;int32_t transferred = 0;int result = 0;const char* func_name = __func__;do {result = libusb_bulk_transfer(vnd_usb.dev_handle, BT_USB_ACL_OUT_EP_ADDR, data, len, &transferred, 1000);if (result == LIBUSB_SUCCESS) {if (transferred == len) {return;}ALOGW("%s: Partial transfer: sent %d/%d bytes (retry %d/%d)", func_name, transferred, len, retry_count+1, MAX_RETRY_ATTEMPTS);result = LIBUSB_ERROR_IO;} else {ALOGW("%s: Attempt %d/%d failed: %s (%d)", func_name, retry_count+1, MAX_RETRY_ATTEMPTS,libusb_error_name(result), result);}switch (result) {case LIBUSB_ERROR_PIPE:libusb_clear_halt(vnd_usb.dev_handle, BT_USB_ACL_OUT_EP_ADDR);break;case LIBUSB_ERROR_NO_DEVICE:case LIBUSB_ERROR_NOT_FOUND:goto final_error;}if (retry_count < MAX_RETRY_ATTEMPTS) {usleep(RETRY_DELAY_MS * 1000);}} while (retry_count++ < MAX_RETRY_ATTEMPTS);final_error:ALOGE("%s: Failed after %d attempts: %s (%d), last sent %d/%d bytes",func_name, retry_count, libusb_error_name(result), result, transferred, len);
}
我们发送hci acl数据主要通过libusb_bulk_transfer
这个函数,下面我们来介绍下这个函数的功能
功能
- 执行 USB 批量传输(Bulk Transfer)。
- 批量传输用于可靠的大数据量传输(如文件读写),无实时性要求但保证数据完整性。
原型
int libusb_bulk_transfer(libusb_device_handle *dev_handle,unsigned char endpoint, // 端点地址(含方向,如 0x81 表示 IN 端点)unsigned char *data, // 数据缓冲区int length, // 数据长度int *transferred, // 实际传输的字节数(输出参数)unsigned int timeout // 超时时间(毫秒)
);
返回值
0
:传输成功。- 负数:错误码(如
LIBUSB_ERROR_PIPE
表示端点错误)。
关键特性
- 方向标识:
端点地址的最高位表示方向(0x80
表示 IN,0x00
表示 OUT)。 - 可靠性:
数据传输保证完整性,出错时会重试。 - 大块数据:
适合传输文件、固件等大数据块。
6. libusb接收hci event/acl数据
libusb有同步跟异步接收数据的方式,同步方式比较简单,就是while loop一直读取event跟acl数据就可以了,但是这种方式对于实时性无法得到保证,对于播放音乐的场景就比较局限,所以我们直接采取异步方式了,我们先来看下event跟acl的EP
所以我们直接贴代码
typedef struct {struct libusb_transfer* event_transfer;struct libusb_transfer* acl_transfer;bool async_transfer_running;pthread_mutex_t lock;
} async_usb_ctx_t;static void async_transfer_cb(struct libusb_transfer* transfer)
{int result;async_usb_ctx_t* ctx = (async_usb_ctx_t*)transfer->user_data;if (transfer->status == LIBUSB_TRANSFER_STALL){ALOGE("Transfer stalled, trying again");result = libusb_clear_halt(vnd_usb.dev_handle, transfer->endpoint);if (result) {ALOGE("libusb_clear_halt fail: %s", libusb_error_name(result));}result = libusb_submit_transfer(transfer);if (result) {ALOGE("re-submitting transfer fail: %s", libusb_error_name(result));}}if(transfer->status == LIBUSB_TRANSFER_COMPLETED){if(transfer->endpoint == BT_USB_EVENT_EP_ADDR){uint8_t pkt_with_type[transfer->actual_length + 1];pkt_with_type[0] = HCI_EVENT_PKT;memcpy(pkt_with_type + 1, transfer->buffer, transfer->actual_length);write(((osi_serial_i_t*)vnd_usb.vendor_fd)->fd, pkt_with_type, transfer->actual_length + 1);}else if(transfer->endpoint == BT_USB_ACL_IN_EP_ADDR){uint8_t pkt_with_type[transfer->actual_length + 1];pkt_with_type[0] = HCI_ACLDATA_PKT;memcpy(pkt_with_type + 1, transfer->buffer, transfer->actual_length);write(((osi_serial_i_t*)vnd_usb.vendor_fd)->fd,pkt_with_type,transfer->actual_length + 1);}}pthread_mutex_lock(&ctx->lock);if (ctx->async_transfer_running) {libusb_submit_transfer(transfer);}pthread_mutex_unlock(&ctx->lock);}async_usb_ctx_t* async_usb_init(libusb_device_handle* dev_handle) {async_usb_ctx_t* ctx = calloc(1, sizeof(async_usb_ctx_t));if (!ctx) return NULL;ctx->event_transfer = libusb_alloc_transfer(0);if (!ctx->event_transfer) goto error;ctx->acl_transfer = libusb_alloc_transfer(0);if (!ctx->acl_transfer) goto error;libusb_fill_interrupt_transfer(ctx->event_transfer,dev_handle,BT_USB_EVENT_EP_ADDR,data_recv,sizeof(data_recv),async_transfer_cb,ctx,100);libusb_fill_bulk_transfer(ctx->acl_transfer,dev_handle,BT_USB_ACL_IN_EP_ADDR,data_recv,sizeof(data_recv),async_transfer_cb,ctx,100);pthread_mutex_init(&ctx->lock, NULL);return ctx;error:if (ctx->event_transfer) libusb_free_transfer(ctx->event_transfer);if (ctx->acl_transfer) libusb_free_transfer(ctx->acl_transfer);free(ctx);return NULL;
}void async_usb_start(async_usb_ctx_t* ctx) {pthread_mutex_lock(&ctx->lock);ctx->async_transfer_running = true;int result = libusb_submit_transfer(ctx->event_transfer);if (result != LIBUSB_SUCCESS) {ALOGE("libusb_submit_transfer fail: %s", libusb_error_name(result));}result = libusb_submit_transfer(ctx->acl_transfer);if (result != LIBUSB_SUCCESS) {ALOGE("libusb_submit_transfer fail: %s", libusb_error_name(result));}pthread_mutex_unlock(&ctx->lock);
}void async_usb_stop(async_usb_ctx_t* ctx) {pthread_mutex_lock(&ctx->lock);ctx->async_transfer_running = false;if (ctx->event_transfer) {libusb_cancel_transfer(ctx->event_transfer);}if (ctx->acl_transfer) {libusb_cancel_transfer(ctx->acl_transfer);}pthread_mutex_unlock(&ctx->lock);
}void async_usb_cleanup(async_usb_ctx_t* ctx) {async_usb_stop(ctx);libusb_free_transfer(ctx->event_transfer);libusb_free_transfer(ctx->acl_transfer);pthread_mutex_destroy(&ctx->lock);free(ctx);
}
然后在接收线程中使用while loop来监听
while (vnd_usb.u2s_thread_runing) {struct timeval tv = {0, 100000}; // 100mslibusb_handle_events_timeout_completed(NULL, &tv, NULL);}
我们来解释下其中用到的各个API
a. libusb_alloc_transfer
功能
- 分配并初始化一个异步传输结构体(
libusb_transfer
)。 - 用于准备异步传输(如批量、中断、控制或等时传输)所需的内存资源。
原型
struct libusb_transfer* libusb_alloc_transfer(int iso_packets);
参数
iso_packets
:等时传输(Isochronous Transfer)的包数量,非等时传输填0
。
返回值
- 成功:指向
libusb_transfer
结构体的指针。 - 失败:
NULL
(内存不足)。
b. libusb_fill_interrupt_transfer
功能
- 填充中断传输(Interrupt Transfer)参数到
libusb_transfer
结构体。 - 用于配置中断端点的异步传输。
原型
void libusb_fill_interrupt_transfer(struct libusb_transfer *transfer, // 已分配的传输结构体libusb_device_handle *dev_handle, // 设备句柄unsigned char endpoint, // 端点地址(含方向)unsigned char *buffer, // 数据缓冲区int length, // 缓冲区长度libusb_transfer_cb_fn callback, // 传输完成回调函数void *user_data, // 用户自定义数据(传递给回调)unsigned int timeout // 超时时间(毫秒)
);
c. libusb_fill_bulk_transfer
功能
- 填充批量传输(Bulk Transfer)参数到
libusb_transfer
结构体。 - 用于配置批量端点的异步传输。
原型
void libusb_fill_bulk_transfer(struct libusb_transfer *transfer,libusb_device_handle *dev_handle,unsigned char endpoint,unsigned char *buffer,int length,libusb_transfer_cb_fn callback,void *user_data,unsigned int timeout
);
d. libusb_submit_transfer
功能
- 提交异步传输请求到 libusb 事件系统。
- 传输将在后台执行,完成后触发回调函数。
原型
int libusb_submit_transfer(struct libusb_transfer *transfer);
返回值
0
:提交成功。- 负数:错误码(如
LIBUSB_ERROR_NO_DEVICE
设备已断开)。
e. libusb_cancel_transfer
功能
- 取消已提交但未完成的传输。
- 若传输正在进行中,会尝试终止传输并触发回调函数。
原型
int libusb_cancel_transfer(struct libusb_transfer *transfer);
返回值
0
:取消请求已提交(不保证传输立即停止)。- 负数:传输已完成或已取消。
f. libusb_free_transfer
功能
- 释放由
libusb_alloc_transfer
分配的传输结构体。 - 必须在传输完成或取消后调用,否则会导致内存泄漏。
原型
void libusb_free_transfer(struct libusb_transfer *transfer);
g. 关键注意事项
生命周期管理:
-
- 传输结构体必须在回调中或明确取消后释放。
- 缓冲区内存需在传输期间保持有效。
线程安全:
-
- 异步操作依赖事件循环线程,需确保
libusb_handle_events/libusb_handle_events_timeout_completed
在独立线程运行。 - 回调函数中避免阻塞操作。
- 异步操作依赖事件循环线程,需确保
错误处理:
-
- 在回调中检查
transfer->status
。 - 处理
LIBUSB_TRANSFER_CANCELLED
和LIBUSB_TRANSFER_ERROR
状态。
- 在回调中检查
性能优化:
-
- 批量传输适合大数据量,中断传输适合小数据实时传输。
- 使用多个并发的传输结构体提高吞吐量。
h. 函数对比总结
函数 | 作用 | 调用时机 |
| 分配传输结构体 | 传输开始前 |
| 配置传输参数 | 提交传输前 |
| 提交异步请求 | 参数配置完成后 |
| 取消进行中的传输 | 需要提前终止传输时 |
| 释放传输结构体 | 传输完成或取消后 |
掌握这些 API 可实现高性能、非阻塞的 USB 通信,适用于实时数据采集或低延迟控制场景。