前面我们的工具已实现了控制传输和批量传输,中断传输,还剩ISO传输未添加,我们在此基础上添加最后一个基础功能:ISO传输。这样我们的工具基础功能至此就完结了,后面就是根据需求增加一些其他功能了,比如接收数据保存到文件,从文件发送等。这一篇还顺便实现一下速度测试,即开始和停止发送接收之间的时间段内收发的数据量。
官方API在线文档https://libusb.sourceforge.io/api-1.0/libusb_api.html中搜索iso。可以找到如下isoc相关接口,其他的传输相关的alloc和submit,release接口和批量,中断传输一样。
libusb_fill_iso_transfer()
libusb_get_iso_packet_buffer()
libusb_get_iso_packet_buffer_simple()
libusb_get_max_iso_packet_size()
libusb_set_iso_packet_lengths()
这里注重介绍下iso传输和批量,中断传输的不一样的地方
首先我们来看
struct libusb_transfer的数据结构
最后一项
struct libusb_iso_packet_descriptor iso_packet_desc [LIBUSB_FLEXIBLE_ARRAY]
是专门给iso传输用的,其中iso_packet_desc是一个0长(变长)数组
所以首先alloc传输,批量和中断传输是
libusb_alloc_transfer(0)
而ISO传输最后要指定一次传输的iso包数,这样才会根据int iso_packets这个参数去malloc对应的iso_packet_desc空间。
libusb_alloc_transfer((s_tx_size+mps-1)/mps);
libusb_alloc_transfer((s_rx_size+mps-1)/mps);
然后是填充传输,要调用libusb_set_iso_packet_lengths设置iso包长
libusb_fill_iso_transfer(transfer,s_opened_handle,address,buffer,s_tx_size,(s_tx_size+mps-1)/mps,&tx_cb,0,100);
libusb_set_iso_packet_lengths(transfer,mps);
libusb_fill_iso_transfer(transfer,s_opened_handle,address,buffer,s_rx_size,(s_rx_size+mps-1)/mps,&rx_cb,0,100);
libusb_set_iso_packet_lengths(transfer,mps);
最重要的一点,对于uvc,uac等iso传输一般会使用alt接口来实现开关,比如有一个alt=0的0带宽接口,有一个alt=1的传输数据的接口,设置接口alt=0则为关闭流,设置alt非0则是打开流,此时就需要手动调用libusb_set_interface_alt_setting设置活动的alt接口,注意这里不能自己调用控制传输实现设置接口,因为系统底层需要当前活动的alt接口信息,所以必须只能调用该接口,很多网上的资料会忽略这部分介绍。
所以申请接口后设置alt为对应的alt
usbdev_get_interface_id(itf_index, &itf_id, &itf_alt_id);
if(0 == libusb_claim_interface(s_opened_handle,itf_id))
{
libusb_set_interface_alt_setting(s_opened_handle,itf_id,itf_alt_id);
}
释放接口前设置alt=0
libusb_set_interface_alt_setting(s_opened_handle,itf_id,0);
libusb_release_interface(s_opened_handle,itf_id);
最后就是回调函数中对于数据的处理,不是使用传输结构体的actual_length获取收发数据的长度,而是使用iso_packet_desc去解析,每一包传输的大小和缓存内容。
void tx_cb(struct libusb_transfer *transfer)
{
if(transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
{
for(int i=0; i
num_iso_packets; i++) {
for(int i=0; i
num_iso_packets; i++) {
unsigned char *buffer;
unsigned int len;
if (transfer->iso_packet_desc[i].status == LIBUSB_TRANSFER_COMPLETED)
{
/* 成功 */
printf("iso[id] tx_cb ok\r\n",i);
}
else
{
/* 失败 */
printf("iso[id] tx_cb err %d\r\n",i,transfer->iso_packet_desc[i].status);
}
len = transfer->iso_packet_desc[i].actual_length;
if(len > 0)
{
s_tx_len += len;
}
}
}
}
else
{
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
{
/* 成功 */
printf("tx_cb ok\r\n");
}
else
{
/* 失败 */
printf("tx_cb err %d\r\n",transfer->status);
//libusb_submit_transfer(transfer);
}
if(transfer->actual_length > 0)
{
s_tx_len += transfer->actual_length;
}
}
free(transfer->buffer); /* 在free传输之前 */
libusb_free_transfer(transfer);
s_tx_busy = 0;
}
void rx_cb(struct libusb_transfer *transfer)
{
if(transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
{
for(int i=0; i
num_iso_packets; i++) {
unsigned char *buffer;
unsigned int len;
if (transfer->iso_packet_desc[i].status == LIBUSB_TRANSFER_COMPLETED)
{
/* 成功 */
printf("iso[id] rx_cb ok\r\n",i);
}
else
{
/* 失败 */
printf("iso[id] rx_cb err %d\r\n",i,transfer->iso_packet_desc[i].status);
}
len = transfer->iso_packet_desc[i].actual_length;
if(len > 0)
{
buffer = libusb_get_iso_packet_buffer_simple(transfer,i);
s_rx_len += len;
usbdev_rx_fifo_put(buffer,len);
}
}
}
else
{
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
{
/* 成功 */
printf("rx_cb ok\r\n");
}
else
{
/* 失败 */
printf("rx_cb err %d\r\n",transfer->status);
}
if(transfer->actual_length > 0)
{
printf("rx len %d\r\n",transfer->actual_length);
usbdev_rx_fifo_put(transfer->buffer,transfer->actual_length);
s_rx_len += transfer->actual_length;
}
}
free(transfer->buffer); /* 在free传输之前 */
libusb_free_transfer(transfer);
s_rx_busy = 0;
}
static void* usb_handle_thread(void *arg)
{
while(1)
{
/* 发送处理 */
if((s_tx_itf_idx != -1) && (s_tx_busy == 0))
{
int address;
int type;
int mps;
int rc = 0;
uint32_t sendlen;
struct libusb_transfer* transfer;
uint8_t* buffer = malloc(s_tx_size);
if(buffer != NULL)
{
sendlen = usbdev_tx_fifo_datalen();
if(sendlen > s_tx_size)
{
sendlen = usbdev_tx_fifo_get(buffer,s_tx_size);
if(0 == usbdev_get_endpoint_info(s_tx_itf_idx, s_tx_ep_idx, &address, &type, &mps))
{
if((type & 0x03) == 1)
{
transfer = libusb_alloc_transfer((s_tx_size+mps-1)/mps);
}
else
{
transfer = libusb_alloc_transfer(0);
}
if(transfer != NULL)
{
switch(type & 0x03)
{
case 0:
//epstr = epstr + "CTRL";
break;
case 1:
libusb_fill_iso_transfer(transfer,s_opened_handle,address,buffer,s_tx_size,(s_tx_size+mps-1)/mps,&tx_cb,0,100);
libusb_set_iso_packet_lengths(transfer,mps);
break;
case 2:
libusb_fill_bulk_transfer(transfer,s_opened_handle,address,buffer,s_tx_size,&tx_cb,0,100);
break;
case 3:
libusb_fill_interrupt_transfer(transfer,s_opened_handle,address,buffer,s_tx_size,&tx_cb,0,100);
break;
}
s_tx_busy = 1;
if(libusb_submit_transfer(transfer) < 0)
{
s_tx_busy = 0;
libusb_free_transfer(transfer);
free(buffer);
}
else
{
}
}
else
{
free(buffer);
}
}
else
{
free(buffer);
}
}
else
{
free(buffer);
}
}
}
/* 接收处理 */
if((s_rx_itf_idx != -1) && (s_rx_busy == 0))
{
int address;
int type;
int mps;
uint32_t sendlen;
struct libusb_transfer* transfer;
uint8_t* buffer = malloc(s_rx_size);
if(buffer != NULL)
{
if(0 == usbdev_get_endpoint_info(s_rx_itf_idx, s_rx_ep_idx, &address, &type, &mps))
{
if((type & 0x03) == 1)
{
transfer = libusb_alloc_transfer((s_rx_size+mps-1)/mps);
}
else
{
transfer = libusb_alloc_transfer(0);
}
if(transfer != NULL)
{
switch(type & 0x03)
{
case 0:
//epstr = epstr + "CTRL";
break;
case 1:
libusb_fill_iso_transfer(transfer,s_opened_handle,address,buffer,s_rx_size,(s_rx_size+mps-1)/mps,&rx_cb,0,100);
libusb_set_iso_packet_lengths(transfer,mps);
break;
case 2:
libusb_fill_bulk_transfer(transfer,s_opened_handle,address,buffer,s_rx_size,&rx_cb,0,100);
break;
case 3:
libusb_fill_interrupt_transfer(transfer,s_opened_handle,address,buffer,s_rx_size,&rx_cb,0,100);
break;
}
s_rx_busy = 1;
if(libusb_submit_transfer(transfer) < 0)
{
s_rx_busy = 0;
libusb_free_transfer(transfer);
free(buffer);
}
else
{
}
}
else
{
free(buffer);
}
}
else
{
free(buffer);
}
}
}
const struct timespec interval=
{
.tv_nsec = 1000000,
.tv_sec = 0,
};
pthread_delay_np(&interval);
}
return 0;
}
见前面
int usbdev_start_tx_transfer(int itf_index, int ep_index, uint32_t size)
{
if(itf_index == -1)
{
return -1;
}
int itf_id;
int itf_alt_id;
s_tx_size = size;
if(itf_index==s_tx_itf_idx)
{
/* 如果和tx接口一样,且已经打开(s_tx_itf_idx!=-1),无需处理接口,只需要修改端点即可 */
s_tx_ep_idx = ep_index;
}
else
{
/* 如果和tx接口不一样,要看是不是和rx接口一样 */
if(itf_index==s_rx_itf_idx)
{
/* 如果和rx接口一样,说明接口也已经打开,无需再打开
* 此时如果原来tx接口是打开的需要关闭,并更新
*/
s_tx_ep_idx = ep_index;
if(s_tx_itf_idx != -1)
{
usbdev_get_interface_id(s_tx_itf_idx, &itf_id, &itf_alt_id);
libusb_set_interface_alt_setting(s_opened_handle,itf_id,0);
libusb_release_interface(s_opened_handle,itf_id);
}
s_tx_itf_idx = itf_index;
}
else
{
/* 如果和rx接口也不一样,则是全新的接口,需要先关闭原来的tx接口,再打开新的接口
* 此时如果原来tx接口是打开的需要关闭,并更新
*/
if(s_tx_itf_idx != -1)
{
usbdev_get_interface_id(s_tx_itf_idx, &itf_id, &itf_alt_id);
libusb_set_interface_alt_setting(s_opened_handle,itf_id,0);
libusb_release_interface(s_opened_handle,itf_id);
}
usbdev_get_interface_id(itf_index, &itf_id, &itf_alt_id);
if(0 == libusb_claim_interface(s_opened_handle,itf_id))
{
libusb_set_interface_alt_setting(s_opened_handle,itf_id,itf_alt_id);
//libusb_clear_halt(s_opened_handle,s_interface_info[itf_index].endpoints[ep_index].bEndpointAddress);
s_tx_itf_idx = itf_index;
s_tx_ep_idx = ep_index;
}
else
{
s_tx_itf_idx = -1;
return -1;
}
}
}
return 0;
}
int usbdev_stop_tx_transfer(void)
{
int itf_id;
int itf_alt_id;
s_tx_busy = 0;
/* 只有tx接口已经打开,且rx没有在用才关闭*/
if(s_tx_itf_idx != -1)
{
if(s_tx_itf_idx != s_rx_itf_idx)
{
usbdev_get_interface_id(s_tx_itf_idx, &itf_id, &itf_alt_id);
libusb_set_interface_alt_setting(s_opened_handle,itf_id,0);
libusb_release_interface(s_opened_handle,itf_id);
s_tx_itf_idx = -1;
}
}
return 0;
}
int usbdev_start_rx_transfer(int itf_index, int ep_index, uint32_t size)
{
if(itf_index == -1)
{
return -1;
}
int itf_id;
int itf_alt_id;
s_rx_size = size;
if(itf_index==s_rx_itf_idx)
{
/* 如果和rx接口一样,且已经打开(s_rx_itf_idx!=-1),无需处理接口,只需要修改端点即可 */
s_rx_ep_idx = ep_index;
}
else
{
/* 如果和rx接口不一样,要看是不是和tx接口一样 */
if(itf_index==s_tx_itf_idx)
{
/* 如果和tx接口一样,说明接口也已经打开,无需再打开
* 此时如果原来rx接口是打开的需要关闭,并更新
*/
s_rx_ep_idx = ep_index;
if(s_rx_itf_idx != -1)
{
usbdev_get_interface_id(s_rx_itf_idx, &itf_id, &itf_alt_id);
libusb_set_interface_alt_setting(s_opened_handle,itf_id,0);
libusb_release_interface(s_opened_handle,itf_id);
}
s_rx_itf_idx = itf_index;
}
else
{
/* 如果和tx接口也不一样,则是全新的接口,需要先关闭原来的rx接口,再打开新的接口
* 此时如果原来rx接口是打开的需要关闭,并更新
*/
if(s_rx_itf_idx != -1)
{
usbdev_get_interface_id(s_rx_itf_idx, &itf_id, &itf_alt_id);
libusb_set_interface_alt_setting(s_opened_handle,itf_id,0);
libusb_release_interface(s_opened_handle,itf_id);
}
usbdev_get_interface_id(itf_index, &itf_id, &itf_alt_id);
if(0 == libusb_claim_interface(s_opened_handle,itf_id))
{
libusb_set_interface_alt_setting(s_opened_handle,itf_id,itf_alt_id);
//libusb_clear_halt(s_opened_handle,s_interface_info[itf_index].endpoints[ep_index].bEndpointAddress);
s_rx_itf_idx = itf_index;
s_rx_ep_idx = ep_index;
}
else
{
s_rx_itf_idx = -1;
return -1;
}
}
}
return 0;
}
int usbdev_stop_rx_transfer(void)
{
int itf_id;
int itf_alt_id;
s_rx_busy = 0;
/* 只有rx接口已经打开,且tx没有在用才关闭*/
if(s_rx_itf_idx != -1)
{
if(s_rx_itf_idx != s_tx_itf_idx)
{
usbdev_get_interface_id(s_rx_itf_idx, &itf_id, &itf_alt_id);
libusb_set_interface_alt_setting(s_opened_handle,itf_id,0);
libusb_release_interface(s_opened_handle,itf_id);
s_rx_itf_idx = -1;
}
}
return 0;
}
这里使用我们之前的uac spk+mic的案例
https://mp.weixin.qq.com/s/bLSLwPjl5cC_8X-YxZo89Q
用zadig-2.8将uac设备驱动改为winusb
选择对应设备接口,设置传输大小,点击开始接收
可以看到会自动设置接口alt=指定alt
停止接收会自动设置接口alt=0
和bushound抓包对比
和bushound抓包数据对比
Usbdev.h中声明接口
uint32_t usbdev_get_tx_len(void);
uint32_t usbdev_get_rx_len(void);
uint32_t usbdev_clr_tx_len(void);
uint32_t usbdev_clr_rx_len(void);
Usbdev.c中实现,在rx_cb和tx_cb中分别递增计数
static uint32_t s_tx_len = 0;
static uint32_t s_rx_len = 0;
uint32_t usbdev_get_tx_len(void)
{
return s_tx_len;
}
uint32_t usbdev_get_rx_len(void)
{
return s_rx_len;
}
uint32_t usbdev_clr_tx_len(void)
{
s_tx_len = 0;
}
uint32_t usbdev_clr_rx_len(void)
{
s_rx_len = 0;
}
mainwindow.h类中添加时间戳
int64_t tx_stamp;
int64_t rx_stamp;
实现获取时间戳函数
#include
#include
int64_t timeStampMs()
{
struct timeval tv;
SYSTEMTIME sys;
GetLocalTime(&sys);
struct tm _tm;
_tm.tm_year = sys.wYear - 1900;
_tm.tm_mon = sys.wMonth - 1;
_tm.tm_mday = sys.wDay;
_tm.tm_hour = sys.wHour;
_tm.tm_min = sys.wMinute;
_tm.tm_sec = sys.wSecond;
_tm.tm_isdst = -1;
tv.tv_sec = (time_t)mktime(&_tm);
tv.tv_usec = sys.wMilliseconds * 1000;
return ((int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec / 1000);
}
开始发送处处理
usbdev_clr_tx_len();
tx_stamp = timeStampMs();
停止发送处处理
int64_t t = timeStampMs();
uint32_t len = usbdev_get_tx_len();
t = t-tx_stamp;
QMessageBox::information(nullptr, "information", "发送速率:"+QString::number((len*1000)/t)+"B/S",
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton);
开始接收处处理
usbdev_clr_rx_len();
rx_stamp = timeStampMs();
停止接收处处理
int64_t t = timeStampMs();
uint32_t len = usbdev_get_rx_len();
t = t-rx_stamp;
QMessageBox::information(nullptr, "information", "接收速率:"+QString::number((len*1000)/t)+"B/S",
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton);
本篇是本系列文章的完结篇,至此我们就step by step完成了一个完整的USB调试助手的开发。本工具支持所有四种传输,支持控制传输按照向导填充SETUP,支持速度测试,支持HEX、ASCII显示。以后可以继续在此基础上进行不断开发,打造成瑞士军刀级别的工具,作为USB开发的得力助手。