项目开发中,在安装linux系统的ARM9板上,需要实时检测USB口与Windows客户端的连接和断开情况,从而中断命令的发送,由于USB的连接和数据交互使用的串口通讯,在板子启动的脚本就直接加载了g_serial.ko的模块,在dev目录下生成了ttyGS0的端口号。串口通讯中断端口地址也不会消失,因此,只有通过在一定时间内检测端口的读写状态来判断串口通讯是够正常,如超时则认为连接断开。
首先,打开串口
//以读写方式打开usb,尝试5次
int fd = -1;
for(int i=0;i<5;i++)
{
fd = open("/dev/ttyGS0", O_RDWR|O_NOCTTY);
if(fd<=0)
continue;//retry until reach 5 times
else
break;
}
由于使用g_serial.ko加载串口模块,不需要额外设置串口参数。
清空端口缓存
tcflush(fd, TCIOFLUSH); //clear port caches
创建线程收发端口数据
//创建USB收发线程
pthread_t tid;
pthread_create(&tid, NULL, Thread_USB_SendRecv, this);
线程函数如下
void* Thread_USB_SendRecv(void *pData)
{
usbserial* pUSBSerial = (usbserial*) pData;
while(true)
{
int nRet = pUSBSerial->ProcData();
if(nRet < 0)
{
//重启USB
pUSBSerial->ResetUSB();
}
}
QPRINT("Thread_USB_SendRecv DEAD!!!!");
return 0;
}
由于串口通讯使用的阻塞IO的模式,当返回的nRet<0时,认为usb通讯错误或断开了连接,因此需重启USB,重启操作即重新打开/dev/ttyGS0端口,如果能正常通讯则继续收发数据,这里不作为USB断开或拔出的判断。
USB拔出的判断只能在另一个监控线程处理超时,即规定时间内没有读取到数据则认为通讯断开或USB线拔出。这里使用一个计数器来设置,在read到数据后重置计数器。
ssize_t ReadForUSBHost(int fd, void* buf, size_t nbytes)
{
ssize_t nRead = read(fd, buf, nbytes);
if(nRead>0) m_nIdleCount = 0;//reset counter
return nRead;
}
监控线程即重新开辟一个线程每隔1秒钟比对一下计数器
int usbserial::CheckUSBPortConStatus()
{
//use m_nIdleCount to set timeout.
bool bRet = (m_nIdleCount++) < m_nTimeout;//超时时间,单位s
if( bRet != USB_GetLink() )
{
USB_SetLink( bRet );
if(bRet == false) // lose link
QPRINT("USB link timeout, only set flag....");
else // link again
QPRINT("USB get data again, ...");
}
return 1;
}