一种usb转串口热拔插支持方法

博客介绍了硬件热插拔对电源部分的要求,指出DB9串口热插拔可能影响系统。软件上,操作系统对USB设备有良好识别机制,串口类设备则无。对于USB转串口设备,可通过USB热拔插支持实现串口拔插,还给出创建进程实现的方法及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    硬件上来说,可热插拔的设备一般都会对电源部分格外重视,除了包含防止插拔的过程中对正负极可能造成的意外短路之外,热插拔还要保证电源负极先于其他引脚连接进系统,提供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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒江独钓2009

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值