硬件上来说,可热插拔的设备一般都会对电源部分格外重视,除了包含防止插拔的过程中对正负极可能造成的意外短路之外,热插拔还要保证电源负极先于其他引脚连接进系统,提供ESD放电回路。然后连接进的是电源正极,为系统供电。稍稍延时后,再将整个电路连接进主系统。尽量确保在数据线上不会产生有害的浪涌损坏设备。而对于DB9串口来说,它的所有脚是同时引入系统的。而且在接口电路中没有任何保护措施,这样的话在热插拔之后,可能会对系统造成不良影响甚至损坏系统。
软件方面,以USB为例。操作系统对于USB设备有着非常良好的接入/移除识别机制,可以检测到设备的连入和移除,并对其进行识别和驱动加载。而对于串口类设备,没有这样的枚举机制。如果你安装了一个新的设备就必须手动对其进行操作,没有特别考虑的应用程序也无从得知串口是否连入了硬件,所以在软件层面还无法实现对设备的监测。
而对于usb转串口设备,串口的热拔插不支持,可通过usb热拔插支持,通过usb插拔的系统通知,从软件上实现串口的拔插支持。以下提供一种方法,创建进程A:监视usb插拔的系统通知;通过通知动态地 创建/删除 进程B:串口的数据处理。
代码示例如下(以下源码兼容先识别 ttyS0):
//main.c
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/input.h>
#include <linux/netlink.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>
//#define DEBUG
#define DEBUG
bool debug=false;
#ifdef DEBUG
#define Debug(fmt,...) ({if(debug) printf("%s:%s:%05d=>" fmt"",__FILE__,__func__,__LINE__,##__VA_ARGS__);})
#else
#define Debug(fmt,...)
#endif
static int init_hotplug_sock(void)
{
struct sockaddr_nl snl;
const int buffersize = 16 * 1024 * 1024;
int retval;
memset(&snl, 0x00, sizeof(struct sockaddr_nl));
snl.nl_family = AF_NETLINK;
snl.nl_pid = getpid();
snl.nl_groups = 1;
int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (hotplug_sock == -1)
{
printf("error getting socket: %s", strerror(errno));
return -1;
}
/* set receive buffersize */
setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
retval = bind(hotplug_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));
if (retval < 0) {
printf("bind failed: %s", strerror(errno));
close(hotplug_sock);
hotplug_sock = -1;
return -1;
}
return hotplug_sock;
}
int serialPortHandle(const char *tty)
{
//for(;;){printf("hanlde receive uart data\n");}
return 0;
}
int main(int argc, char *argv[])
{
char *ttyAMA0 = "/dev/ttyS0";
char *ttyBase = "/dev/ttyUSB";
char tty[64];
char ctmp[4];
int hotplug_sock;
char uevent_buf[512];
pid_t pid = -1;
int max_tty = 10;
if(argc == 2 && strcmp(argv[1],"debug") == 0){
debug = true;
}
for(int i=0; i<max_tty; i++){
memset(ctmp, '\0', sizeof(ctmp));
memset(tty, '\0', sizeof(tty));
strcpy(tty, ttyBase);
sprintf(ctmp, "%d", i);
strcat(tty, ctmp);
if(0 == access(tty, F_OK)){
break;
}
}
if(0 == access(tty, F_OK)){
Debug("access %s OK.\n", tty);
pid = fork();
if(pid < 0){
perror("fork fail ");
exit(1);
}else if(pid == 0){ //子进程
Debug("child pid:[%d].\n", getpid());
serialPortHandle(tty); // loop here.
}else{ //父进程
Debug("I am father, my child:[%d].\n", pid);
}
}else if(0 == access(ttyAMA0, F_OK)){
Debug("access %s OK.\n", ttyAMA0);
serialPortHandle(ttyAMA0); // loop here.
while(1);
return 0;
}
hotplug_sock = init_hotplug_sock();
for(;;){
recv(hotplug_sock, &uevent_buf, sizeof(uevent_buf), 0); //block here until receive uevent.
/* 一次插拔,只会匹配以下二者之一 */
bool match = false;
if((NULL != strstr(uevent_buf, "bind@")) && (NULL != strstr(uevent_buf, "ttyUSB")) && (NULL == strstr(uevent_buf, "unbind@"))){
match = true;
}
if(match){ // USB转串口线插入
pid = fork();
if(pid < 0){
perror("fork fail ");
exit(1);
}else if(pid == 0){ //子进程
Debug("Child Pid:[%d].\n", getpid());
int i;
for(i=0; i<max_tty; i++){
memset(ctmp, '\0', sizeof(ctmp));
memset(tty, '\0', sizeof(tty));
strcpy(tty, ttyBase);
sprintf(ctmp, "%d", i);
strcat(tty, ctmp);
if(0 == access(tty, F_OK)){
break;
}
}
if(i < max_tty){
serialPortHandle(tty); // loop here.
}
}else{
Debug("I am Father, my Child:[%d].\n", pid);
}
}
match = false;
if((NULL != strstr(uevent_buf, "unbind@")) && (NULL != strstr(uevent_buf, "ttyUSB"))){
match = true;
}
if(match){ // USB转串口线拔除
Debug("kill child %d.\n", pid);
kill(pid,SIGTERM);//杀死 pid
wait(NULL);
}
}
return 0;
}
编译:
$ gcc main.c -o ttyAutoPlug
运行:
$ ./ttyAutoPlug debug
main.c:main:00143=>I am Father, my Child:[9378].
main.c:main:00127=>Child Pid:[9378].
main.c:main:00152=>kill child 9378.
main.c:main:00143=>I am Father, my Child:[9406].
main.c:main:00127=>Child Pid:[9406].
main.c:main:00152=>kill child 9406.