前面我们完成了接口和端点信息的解析与显示,现在就可以选择任意端点对其进行数据的收发了。由于批量传输和中断传输逻辑上差不多所以把他们放在一起。后面的同步传输逻辑上差异较大,单独再以一文分享。
我们还是参考libusb的api,我们从以下地址可以看到相关api
https://libusb.sourceforge.io/api-1.0/libusb_api.html
可以很快的找到相对应的两个API
libusb_bulk_transfer()
libusb_interrupt_transfer()
注意传输之前必须打开接口,调用libusb_claim_interface
操作完之后释放接口libusb_release_interface。
否则上述传输操作会返回-5.
我们查看批量传输和中断传输其原型也是一样的, 我们对其进行封装,屏蔽设备相关参数。
Usbdev.c中添加接口实现
int usbdev_bulk_transfer(int itf_id,unsigned char endpoint, unsigned char *data, int length, int* transferred, unsigned int timeout)
{
int res;
if(s_opened_handle == NULL)
{
return -1;
}
if(0 != libusb_claim_interface(s_opened_handle,itf_id))
{
return -1;
}
res = libusb_bulk_transfer(s_opened_handle, endpoint, data, length, transferred, timeout);
libusb_release_interface(s_opened_handle,itf_id);
return res;
}
int usbdev_interrupt_transfer(int itf_id,unsigned char endpoint, unsigned char *data, int length, int* transferred, unsigned int timeout)
{
int res;
if(s_opened_handle == NULL)
{
return -1;
}
if(0 != libusb_claim_interface(s_opened_handle,itf_id))
{
return -1;
}
res = libusb_interrupt_transfer(s_opened_handle, endpoint, data, length, transferred, timeout);
libusb_release_interface(s_opened_handle,itf_id);
return res;
}
Usbdev.h中添加接口声明
int usbdev_bulk_transfer(int itf_id,unsigned char endpoint, unsigned char *data, int length, int* transferred, unsigned int timeout);
int usbdev_interrupt_transfer(int itf_id,unsigned char endpoint, unsigned char *data, int length, int* transferred, unsigned int timeout);
右键点击发送区”清除显示”按钮->转到槽
添加clicked()处理
void MainWindow::on_pushButton_3_clicked()
{
}
实现如下
void MainWindow::on_pushButton_3_clicked()
{
ui->textEdit_3->setText("");
}
同样的接收区添加”清除显示”按钮的clicked()处理
void MainWindow::on_pushButton_4_clicked()
{
ui->textEdit_4->setText("");
}
“开始发送”按钮添加clicked()处理函数
void MainWindow::on_pushButton_2_clicked()
{
}
需要根据是否选中”HEX”来决定是否需要对发送编辑框的内容转为16进制,
需要获取发送长度编辑框中的长度信息。
然后根据端点下拉框选中的端点,判断是否是OUT端点,且根据是BULK还是INTERRUPT端点调用不同的发送接口。
实现如下:
void MainWindow::on_pushButton_2_clicked()
{
bool ishex = ui->checkBox->isChecked();
int len = ui->textEdit->toPlainText().toInt();
int textlen;
int ep_index;
int itf_index;
int address;
int type;
int mps;
int transferred;
int res;
int itf_id;
int itf_alt_id;
QString data_str = ui->textEdit_3->toPlainText();
QByteArray data;
if(ishex)
{
data = QByteArray::fromHex(data_str.toLatin1().data());
}
else
{
data = QByteArray::fromRawData(data_str.toLatin1().data(),data_str.size());
}
textlen = data.size();
/* 分配指定的缓存 */
unsigned char* buffer = (unsigned char*)calloc(len,1);
if(buffer == NULL)
{
return;
}
/* 拷贝数据到缓存 */
memcpy(buffer, data, (textlen>len)?len:textlen);
/* 获取当前选中的接口和端点 */
ep_index = ui->comboBox_2->currentIndex();
itf_index = ui->comboBox_3->currentIndex();
usbdev_get_interface_id(itf_index, &itf_id, &itf_alt_id);
if(0 == usbdev_get_endpoint_info(itf_index, ep_index, &address, &type, &mps))
{
if((address & 0x80) == 0)
{
switch(type & 0x03)
{
case 0:
/* 控制传输 */
break;
case 1:
/* 同步传输 */
break;
case 2:
/* 批量传输 */
printf("BULK OUT:%02X, %d\r\n",address,len);
for(int i=0; i
{
printf("%02x ",buffer[i]);
}
printf("\r\n");
if(0 != (res = usbdev_bulk_transfer(itf_id,address, buffer, len, &transferred, 1000)))
{
QMessageBox::information(nullptr, "information", "批量传输失败:"+QString::number(res),
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton);
}
printf("transferred:%d\r\n");
break;
case 3:
/* 中断传输 */
printf("BULK OUT:%02X, %d\r\n",address,len);
for(int i=0; i
{
printf("%02x ",buffer[i]);
}
printf("\r\n");
if(0 != (res = usbdev_interrupt_transfer(itf_id,address, buffer, len, &transferred, 1000)))
{
QMessageBox::information(nullptr, "information", "中断传输失败"+QString::number(res),
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton);
}
printf("transferred:%d\r\n");
break;
}
}
else
{
QMessageBox::information(nullptr, "information", "不是OUT端点,不能发送",
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton);
}
}
/* 释放缓存 */
free(buffer);
}
“开始发送”按钮添加clicked()处理函数
void MainWindow::on_pushButton_5_clicked()
{
}
需要根据是否选中”HEX”来决定是否需要对接收编辑框的内容转为16进制显示,
需要获取接收长度编辑框中的长度信息。
然后根据端点下拉框选中的端点,判断是否是IN端点,且根据是BULK还是INTERRUPT端点调用不同的发送接口。
实现如下:
void MainWindow::on_pushButton_5_clicked()
{
bool ishex = ui->checkBox_2->isChecked();
int len = ui->textEdit_2->toPlainText().toInt();
int textlen;
int ep_index;
int itf_index;
int address;
int type;
int mps;
int transferred;
int res;
int itf_id;
int itf_alt_id;
QByteArray data;
QString data_str;
/* 分配指定的缓存 */
unsigned char* buffer = (unsigned char*)calloc(len,1);
if(buffer == NULL)
{
return;
}
/* 获取当前选中的接口和端点 */
ep_index = ui->comboBox_5->currentIndex();
itf_index = ui->comboBox_4->currentIndex();
usbdev_get_interface_id(itf_index, &itf_id, &itf_alt_id);
if(0 == usbdev_get_endpoint_info(itf_index, ep_index, &address, &type, &mps))
{
if((address & 0x80) != 0)
{
switch(type & 0x03)
{
case 0:
/* 控制传输 */
break;
case 1:
/* 同步传输 */
break;
case 2:
/* 批量传输 */
if(0 != (res = usbdev_bulk_transfer(itf_id,address, buffer, len, &transferred, 1000)))
{
QMessageBox::information(nullptr, "information", "批量传输失败"+QString::number(res),
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton);
}
break;
case 3:
/* 中断传输 */
if(0 != (res = usbdev_interrupt_transfer(itf_id,address, buffer, len, &transferred, 1000)))
{
QMessageBox::information(nullptr, "information", "中断传输失败"+QString::number(res),
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton);
}
break;
}
}
else
{
QMessageBox::information(nullptr, "information", "不是IN端点,不能接收",
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton);
}
}
/* 将收到的数据显示到编辑框 */
data.resize(transferred);
memcpy(data.data(), buffer, transferred);
if(ishex)
{
data_str = ByteArrayToHexString(data);
}
else
{
data_str = QString(data);
}
ui->textEdit_4->setText(data_str);
/* 释放缓存 */
free(buffer);
}
控制写之前没有测试过,这里补上,测试时发现总是提示发送长度不对,看代码是
QByteArray ctrl_data = QByteArray::fromHex (setup_str.toLatin1().data());
这里写错了改为
QByteArray ctrl_data = QByteArray::fromHex (ctrl_str.toLatin1().data());
找一个cdc设备测试设置line codling如下,控制写测试OK,无数据控制传输也OK,那么三种控制传输形式(有数据写,有数据读,无数据)都测试OK了。
我这里有一个自定义设备
描述符很简单,就是一个0接口,有一个OUT端点0x01,和一个IN端点0x83,都是BULK。
设备里的逻辑是OUT端点收到数据从IN端点原样返回。
测试工具从OUT端点发送10字节,然后接收10字节,可以看到发送和接收的内容一致。
手头暂时没有对应设备,后面再单独测试
前面HEX收发测试了,继续测试ASCII收发,如下也正确
清除显示如下,测试OK
如下如果不是对应方向的端点能提示
比如如果未申请接口就直接传输,提示-5的错误
至此我们已经完成了控制传输,批量/中断传输的功能,工具基金初步具备了实用价值,已经可以作为测试用了。 后面再继续增加同步传输功能。后面就是不断完善了,比如增加保存到文件,比如根据模板选择控制传输而不是手动输入16进制数,支持脚本控制等等。
后面不断完善将其打造成瑞士军刀级别的测试工具。