开发环境
核心板:IMX6
内核版本:linux4.1.5
问题
通过USB扩展出来的串口接收数据会出现截断现象,而且每次截断的大小都一样。而核心板提供的串口UART就没有这个现象。
核心板自带串口正常的现象:
扩展串口异常现象:
问题分析
首先客户提出这个问题,我的第一个想法是这是个正常现象,因为我在做应用APP的时候,其实串口发送数据指令的时候,也会偶尔出现一条指令突然截断,先接收到帧头+命令字+类型,然后再接收到长度+数据内容+帧尾这类的情况,就此我们常常会在接收串口的函数中加以判断是否是一条完整的指令。如果不是就不再往下执行。
然后再回到客户这个问题,如果从应用层解决是非常简单的。只需要在读取数据的时候,加以等待函数就可以读到整条指令了,以QT应用为例:
connect(scmCOM3, SIGNAL(readyRead()), this, SLOT(scmReadyReadData()));
void CSerialThread::scmReadyReadData()
{
//从接收缓冲区中读取串口数据
// static QByteArray scmRecviceBuff;//静态变量static
QByteArray tempBuff;//临时接收储存数据,因为串口收到的数据可能是不完整的
scmCOM3->waitForReadyRead(50);
tempBuff = scmCOM3->readAll();
// scmRecviceBuff.append(tempBuff);
qDebug()<<__func__<< __LINE__ << "scmRecviceBuff:"<<tempBuff.toHex();
}
如果不考虑改变应用层的代码的话,那么只能通过尝试修改驱动层代码来兼容了。如果是驱动层的话,我第一个想法是会不会跟读取缓冲区的大小有关呢? 为什么要这样的猜测呢,因为每次截断的大小都是128个字节。如果说是上报数据的速度快慢问题的话,应该会不固定截断位置的。
先在内核源码中找一下驱动ch344的驱动文件,定位到driver/usb/class/cdc_acm.c驱动文件。
static int acm_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_cdc_union_desc *union_header = NULL;
struct usb_cdc_country_functional_desc *cfd = NULL;
unsigned char *buffer = intf->altsetting->extra;
int buflen = intf->altsetting->extralen;
struct usb_interface *control_interface;
struct usb_interface *data_interface;
struct usb_endpoint_descriptor *epctrl = NULL;
struct usb_endpoint_descriptor *epread = NULL;
struct usb_endpoint_descriptor *epwrite = NULL;
struct usb_device *usb_dev = interface_to_usbdev(intf);
struct acm *acm;
int minor;
int ctrlsize, readsize;
u8 *buf;
u8 ac_management_function = 0;
u8 call_management_function = 0;
int call_interface_num = -1;
int data_interface_num = -1;
unsigned long quirks;
int num_rx_buf;
//.......省略中间部分
ctrlsize = usb_endpoint_maxp(epctrl) ;
readsize = usb_endpoint_maxp(epread) *
(quirks == SINGLE_RX_URB ? 1 : 2);
printk("epctrl,epread,usb_endpoint_maxp(epread):%d,%d,%d\n",epctrl,epread,usb_endpoint_maxp(epread));
acm->combined_interfaces = combined_interfaces;
acm->writesize = usb_endpoint_maxp(epwrite) * 20;
acm->control = control_interface;
acm->data = data_interface;
acm->minor = minor;
acm->dev = usb_dev;
acm->ctrl_caps = ac_management_function;
if (quirks & NO_CAP_LINE)
acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
acm->ctrlsize = ctrlsize;
acm->readsize = readsize;
acm->rx_buflimit = num_rx_buf;
printk("acm->ctrlsize,acm->readsize,acm->rx_buflimit:%d,%d,%d\n",acm->ctrlsize,acm->readsize,acm->rx_buflimit);
INIT_WORK(&acm->work, acm_softint);
init_waitqueue_head(&acm->wioctl);
spin_lock_init(&acm->write_lock);
//.....省略
}
驱动加载时打印的信息如下:
ctrlsize,readsize,usb_endpoint_maxp(epread):-666437376,-666557136,64
acm->ctrlsize,acm->readsize,acm->rx_buflimit:16,128,16
cdc_acm 1-1.4:1.0: ttyACM0: USB ACM device
ctrlsize,readsize,usb_endpoint_maxp(epread):-666437312,-666557008,64
acm->ctrlsize,acm->readsize,acm->rx_buflimit:16,128,16
cdc_acm 1-1.4:1.2: ttyACM1: USB ACM device
ctrlsize,readsize,usb_endpoint_maxp(epread):-666437248,-666556880,64
acm->ctrlsize,acm->readsize,acm->rx_buflimit:16,128,16
cdc_acm 1-1.4:1.4: ttyACM2: USB ACM device
ctrlsize,readsize,usb_endpoint_maxp(epread):-666437184,-666556752,64
acm->ctrlsize,acm->readsize,acm->rx_buflimit:16,128,16
cdc_acm 1-1.4:1.6: ttyACM3: USB ACM device
果不其然,看到acm->readsize=128,跟现象截断的字节数刚好一样,然后我尝试单单修改acm->readsize=256,串口无法正常使用,证明readsize肯定跟其他值相关联的,仔细看了下代码,我认为跟这个有关系。然后将ctrlsize的值也成倍增加,如下:
//原来的代码:
ctrlsize = usb_endpoint_maxp(epctrl) ;
readsize = usb_endpoint_maxp(epread) *
(quirks == SINGLE_RX_URB ? 1 : 2);
//修改后的代码:
ctrlsize = usb_endpoint_maxp(epctrl)*2 ;
readsize = usb_endpoint_maxp(epread) *
(quirks == SINGLE_RX_URB ? 1 : 4);
成功后的打印信息:
usb 1-1.4: new full-speed USB device number 3 using ci_hdrc
ctrlsize,readsize:-666437376,-666557136,64
acm->ctrlsize,acm->readsize,acm->rx_buflimit:32,256,16
cdc_acm 1-1.4:1.0: ttyACM0: USB ACM device
ctrlsize,readsize:-666437312,-666557008,64
acm->ctrlsize,acm->readsize,acm->rx_buflimit:32,256,16
cdc_acm 1-1.4:1.2: ttyACM1: USB ACM device
ctrlsize,readsize:-666437248,-666556880,64
acm->ctrlsize,acm->readsize,acm->rx_buflimit:32,256,16
cdc_acm 1-1.4:1.4: ttyACM2: USB ACM device
ctrlsize,readsize:-666437184,-666556752,64
acm->ctrlsize,acm->readsize,acm->rx_buflimit:32,256,16
cdc_acm 1-1.4:1.6: ttyACM3: USB ACM device
X_MAX: 4096, Y_MAX: 4096, TRIGGER: 0x01
通过以上的方式,最终也解决了客户的问题。不管是应用层还是驱动层,只要分析到问题出现的点在哪,都能解决。