通过libusb实现USB设备数据的异步传输主要有如下几步:
- 创建libusb_transfer对象
- 根据不同的传输方式填充相应信息给libusb_transfer对象
- 提交libusb_transfer对象给libusb API进行执行
- 在传输回调函数中检查libusb_transfer对象中的状态信息
- 释放libusb_transfer对象
下面我们来依次介绍
1. 创建libusb_transfer对象
可以使用libusb_alloc_transfer函数来创建libusb_transfer对象,函数定义如下:
struct libusb_transfer * libusb_alloc_transfer (int iso_packets)
其中:
iso_packets:用于指明在实时传输时需要传输的包的数量。对于其它三种传输方式设为0即可
返回值:libusb_transfer对象指针
2. 填充libusb_transfer对象
填充libusb_transfer对象的函数根据传输方式各不相同。
2.1 控制传输
控制传输主要使用libusb_fill_control_setup和libusb_fill_control_transfer函数把控制传输所需信息填充到libusb_transfer对象中。
static void libusb_fill_control_setup (
unsigned char *buffer,
uint8_t bmRequestType,
uint8_t bRequest,
uint16_t wValue,
uint16_t wIndex,
uint16_t wLength)
大多数的参数和同步模式传输函数libusb_control_transfer中的类似,可参考上一节的内容。其中:
buffer:为待生成的Setup命令的Buffer指针,大小必须是2的倍数
bmRequestType: 为Control Setup包的request type部分
bRequest: 为Control Setup包的request命令
wValue: 为Control Setup包的value部分,该值需要根据Request命令设定相应的值
wIndex: 为Control Setup包的index部分,设置方式和wValue类似
wLength:为Control Setup包请求读取数据时传入buffer的最大长度,只有从USB设备读取数据时需要。
而后需要使用函数libusb_fill_control_transfer把使用libusb_fill_control_setup构建的Setup包和额外数据buffer填充给libusb_transfer对象。libusb_fill_control_transfer函数定义如下:
static void libusb_fill_control_transfer (
struct libusb_transfer *transfer,
libusb_device_handle *dev_handle,
unsigned char *buffer,
libusb_transfer_cb_fn callback,
void *user_data,
unsigned int timeout)
其中:
transfer:为待填充的libusb_transfer对象,后续不再赘述
dev_handle:为待传输的USB设备的设备句柄,后续不再赘述
buffer: 为刚刚构建的Setup包和额外的数据Buffer,额外的数据Buffer追加在Setup包后
callback: 为本次传输的回调函数,关于libusb_transfer_cb_fn回调函数后续会详细介绍
user_data: 为传入回调函数的user data
timeout:为传输的超时时间,单位ms。0表示永不超时
2.2 中断传输
中断传输使用libusb_fill_interrupt_transfer来填充信息到libusb_transfer对象中。函数定义如下:
static 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)
其中:
endpoint:为端口地址
buffer:为待传输的buffer指针
length:为待传输buffer的长度
callback:为本次传输的回调函数
user_data: 为传入回调函数的user data
timeout:为传输的超时时间,单位ms。0表示永不超时
2.3 批量传输
批量传输使用libusb_fill_bulk_transfer来填充信息到libusb_transfer对象中。函数定义如下:
static 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)
函数参数和中断传输使用的函数libusb_fill_interrupt_transfer中的参数类似,这里不再赘述。
2.4 实时传输
实时传输使用libusb_fill_iso_transfer来填充信息到libusb_transfer对象中,然后需要使用libusb_set_iso_packet_lengths设定每个iso包的长度。
libusb_fill_iso_transfer函数定义如下:
static void libusb_fill_iso_transfer (
struct libusb_transfer *transfer,
libusb_device_handle *dev_handle,
unsigned char endpoint,
unsigned char *buffer,
int length,
int num_iso_packets,
libusb_transfer_cb_fn callback,
void *user_data,
unsigned int timeout)
和前面两种传输函数相比,只多了num_iso_packets参数。它用于指定待传输的iso包的个数。并且该参数也影响libusb_set_iso_packet_lengths函数需要设定的值。
libusb_set_iso_packet_lengths函数的定义如下:
static void libusb_set_iso_packet_lengths (
struct libusb_transfer *transfer,
unsigned int length)
其中:
length:用于设定iso包的长度,一般此长度等于libusb_fill_iso_transfer函数中设定的待传输buffer的长度除以设定的iso包个数num_iso_packets。
3. 提交libusb_transfer对象
我们可以使用libusb_submit_transfer函数来把填充了传输信息的libusb_transfer对象提交给libusb API进行执行。libusb_submit_transfer函数定义如下:
int libusb_submit_transfer (struct libusb_transfer *transfer)
函数很简单,传入一个libusb_transfer对象。返回值为LIBUSB_SUCCESS表示成功,否则返回一个LIBUSB_ERROR_XXX的错误码。
4. 检查回调函数libusb_transfer_cb_fn
libusb通过在各个填充函数中传入的回调函数libusb_transfer_cb_fn来返回libusb_transfer对象的执行情况。libusb_transfer_cb_fn回调函数的定义如下:
typedef void(* libusb_transfer_cb_fn) (struct libusb_transfer *transfer)
所以在回调发生时,它会传回libusb_transfer对象。传输的结果就保存在libusb_transfer对象中。所以让我们看看libusb_transfer对象的定义:
struct libusb_transfer {
libusb_device_handle *dev_handle;
uint8_t flags;
unsigned char endpoint;
unsigned char type;
unsigned int timeout;
enum libusb_transfer_status status;
int length;
int actual_length;
libusb_transfer_cb_fn callback;
void *user_data;
unsigned char *buffer;
int num_iso_packets;
struct libusb_iso_packet_descriptor iso_packet_desc[LIBUSB_FLEXIBLE_ARRAY]
};
其中的成员变量大多是通过填充函数进行填充的:
dev_handle:就是填充函数提供的相关USB设备的句柄
flags:用于手动设定一些特殊的传输特性,这些特性可以使用bitwise或来进行组合。比如,LIBUSB_TRANSFER_FREE_BUFFER: 设定在调用libusb_free_transfer时自动free传输的buffer指针;LIBUSB_TRANSFER_FREE_TRANSFER: 在回调结束时自动调用libusb_free_transfer释放libusb_transfer对象。
endpoint: 就是填充函数提供的端口地址
type: 表示此libusb_transfer对象的传输方式,控制(LIBUSB_TRANSFER_TYPE_CONTROL )、中断(LIBUSB_TRANSFER_TYPE_INTERRUPT)、批量(LIBUSB_TRANSFER_TYPE_BULK)、批量流(LIBUSB_TRANSFER_TYPE_BULK_STREAM)和实时(LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
buffer,length:分别是填充的待传输buffer指针和buffer最大长度
actual_length:是buffer实际传输长度
callback:就是填充的回调函数
status: 表示传输结果(对于实时传输来说,还需要查看各个iso包的状态)。LIBUSB_TRANSFER_COMPLETED表示传输成功,LIBUSB_TRANSFER_ERROR表示传输失败。具体可参见libusb_transfer_status枚举类型
num_iso_packets:就是填充的iso包个数
iso_packet_desc: 用于表示每个iso包的传输信息
iso_packet_desc->status:表示单个iso包的传输结果
iso_packet_desc->length: 表示填充时设定的单个iso包的长度
iso_packet_desc->actual_length: 表示实际传输的iso包的长度
我们在处理回调函数时,一般通过读取libusb_transfer对象中的status变量来判断传输结果。对于实时传输,则还需要判断iso_packet_desc中各个iso包的结果来进行判断。如果是读取传输则需要复制buffer指针所指向的内容,复制的长度由actual_length决定。
5. 释放libusb_transfer对象
上面已经提到过,我们通过libusb_free_transfer函数来释放libusb_transfer对象,函数定义如下:
void libusb_free_transfer (struct libusb_transfer *transfer)
上面5步就是整个异步传输过程。还需要注意一点:异步传输因为和USB Hotplug一样使用回调函数来处理异步操作,所以必须另起线程调用Event处理函数libusb_handle_events系列函数来保证回调函数的正常工作。并且需要注意它和Hotplug事件是合用一个Event处理函数的,所以只有两者都停止的情况下才能退出Event处理线程。
当然如果你喜欢使用单一线程控制整个USB处理,也可以把Event处理过程放在主线程中。还可以使用libusb的libusb_get_pollfds函数获取需要监听的fd列表,然后使用polling方式来监听回调事件,然后调用libusb_handle_events系列函数来触发回调,但注意这个方法并不适用于Windows平台。
至此,整个异步传输的实现方法已介绍完毕。下面是几个使用异步模式实现的例程:
1)异步模式控制传输:
static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer)
{
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fprintf(stderr, "mode change transfer not completed!\n");
request_exit(2);
}
printf("async cb_mode_changed length=%d actual_length=%d\n",
transfer->length, transfer->actual_length);
if (next_state() < 0)
request_exit(2);
libusb_free_transfer(transfer);
}
static int set_mode_async(unsigned char data) {
unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1);
struct libusb_transfer *transfer;
if (!buf) {
errno = ENOMEM;
return -1;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
free(buf);
errno = ENOMEM;
return -1;
}
printf("async set mode %02x\n", data);
libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1);
buf[LIBUSB_CONTROL_SETUP_SIZE] = data;
libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL,
1000);
transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK
| LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER;
return libusb_submit_transfer(transfer);
}
2) 异步模式中断传输
void InterruptTransPktAsync(u8 ep, u8* buffer, int length, u32 timeout) {
if (mDevHandle == nullptr) {
LOGE("Failed to transfer packet async as device isn't initialized properly");
return;
}
libusb_transfer *transfer = libusb_alloc_transfer(0);
if (transfer == nullptr) {
LOGE("Failed to alloc usb transfer object");
return;
}
libusb_fill_interrupt_transfer(transfer, mDevHandle, ep, buffer, length, [](libusb_transfer *transfer){
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
LOGE("Transfer packet async failed, status: %d", transfer->status);
} else {
if ((transfer->endpoint & 0x80) == LIBUSB_ENDPOINT_IN) {
LOGD("Input packet async successfully, data: %s", bufferToString(transfer->buffer, transfer->actual_length).c_str());
} else if ((transfer->endpoint & 0x80) == LIBUSB_ENDPOINT_OUT) {
LOGD("Ouput packet async successfully, length: %d", transfer->actual_length);
}
}
libusb_free_transfer(transfer);
}, nullptr, timeout);
int ret = libusb_submit_transfer(transfer);
if (ret != LIBUSB_SUCCESS) {
LOGE("Failed to submit transfer packet, error %d", ret);
}
}
3)异步模式实时传输
static void LIBUSB_CALL cb_xfr(struct libusb_transfer *xfr)
{
int i;
if (xfr->status != LIBUSB_TRANSFER_COMPLETED) {
fprintf(stderr, "transfer status %d\n", xfr->status);
libusb_free_transfer(xfr);
exit(3);
}
if (xfr->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
for (i = 0; i < xfr->num_iso_packets; i++) {
struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i];
if (pack->status != LIBUSB_TRANSFER_COMPLETED) {
fprintf(stderr, "Error: pack %d status %d\n", i, pack->status);
exit(5);
}
printf("pack%d length:%u, actual_length:%u\n", i, pack->length, pack->actual_length);
}
}
printf("length:%u, actual_length:%u\n", xfr->length, xfr->actual_length);
for (i = 0; i < xfr->actual_length; i++) {
printf("%02x", xfr->buffer[i]);
if (i % 16)
printf("\n");
else if (i % 8)
printf(" ");
else
printf(" ");
}
num_bytes += xfr->actual_length;
num_xfer++;
if (libusb_submit_transfer(xfr) < 0) {
fprintf(stderr, "error re-submitting URB\n");
exit(1);
}
}
static int benchmark_in(uint8_t ep)
{
static uint8_t buf[2048];
static struct libusb_transfer *xfr;
int num_iso_pack = 0;
if (ep == EP_ISO_IN)
num_iso_pack = 16;
xfr = libusb_alloc_transfer(num_iso_pack);
if (!xfr) {
errno = ENOMEM;
return -1;
}
if (ep == EP_ISO_IN) {
libusb_fill_iso_transfer(xfr, devh, ep, buf,
sizeof(buf), num_iso_pack, cb_xfr, NULL, 0);
libusb_set_iso_packet_lengths(xfr, sizeof(buf)/num_iso_pack);
} else
libusb_fill_bulk_transfer(xfr, devh, ep, buf,
sizeof(buf), cb_xfr, NULL, 0);
get_timestamp(&tv_start);
return libusb_submit_transfer(xfr);
}
&spm=1001.2101.3001.5002&articleId=145548697&d=1&t=3&u=ffe2e5bc1dd34e6290d01d34d98f69c1)
1386

被折叠的 条评论
为什么被折叠?



