目录

嵌入式linux网口和USB热插拔检测

目录

【嵌入式linux】网口和USB热插拔检测

在Linux常常需要对网口和USB等外设接口进行插拔检测,从而执行部分初始化操作。下面简要介绍Linux的Netlink机制 ,并在C程序中使用Linux的Netlink 机制完成网口和USB口插拔检测Netlink 是 Linux 内核与用户空间进程通信的一种机制,主要用于内核模块和用户程序之间的数据传输 。它比传统的通信方式(如 ioctl、procfs、sysfs)更灵活高效。 一、主要特点 双向通信 :支持内核与用户空间的双向数据传输。 多协议支持 :通过不同的协议类型(如 NETLINK_ROUTE、NETLINK_SOCK_DIAG)满足多种通信需求。 多播支持 :允许一个消息同时发送给多个接收者。 异步通信 :支持异步消息传递,适合事件驱动场景。 二、使用场景 网络配置 :如 iproute2 工具通过 Netlink 配置网络接口和路由。 防火墙和策略路由 :如 iptables 和 nftables 使用 Netlink 配置规则。 设备管理 :如 udev 通过 Netlink 监控设备事件。 三、基本使用步骤 1.创建套接字:用户空间使用 socket() 创建 Netlink 套接字。 int sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 2.绑定地址:绑定 Netlink 地址。 struct sockaddr_nl addr; memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); // 通常使用进程ID bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)); 3.发送和接收消息:使用 sendmsg() 和 recvmsg() 进行消息传递。 4.处理消息:解析和处理接收到的消息。 Demo1:使用Netlink实现网口热插拔。(ZYNQ-7020平台,linux内核为4.19) #include #include #include #include typedef void (network_status_callback)(char eth_name,int status) ; void network_link_monitor(network_status_callback arg) { int sock; int ret; struct sockaddr_nl sa; int len=4096; char buf[4096]={0}; struct nlmsghdr nh; struct ifinfomsg ifinfo; struct rtattr attr; network_status_callback nm=arg; int i; memset(&sa,0,sizeof(sa)); sa.nl_family=AF_NETLINK; sa.nl_groups=RTNLGRP_LINK; //sa.nl_pid=getpid(); //创建套接字和绑定Netlink地址,这里使用NETLINK_ROUTE sock=socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE); if(sock<0) printf(“socket err %d\n”,LINE); setsockopt(sock,SOL_SOCKET,SO_RCVBUF,&len,sizeof(len)); if(bind(sock,(struct sockaddr)&sa,sizeof(sa))<0) printf(“bind err %d\n”,LINE); //阻塞接收内核的Netlink消息,可根据应用需求使用非阻塞IO读 while((ret=read(sock,buf,sizeof(buf)))>0) { for(nh=(struct nlmsghdr)buf;NLMSG_OK(nh,ret);nh=NLMSG_NEXT(nh,ret)) { //解析和处理接收到的内核消息 if(nh->nlmsg_type==NLMSG_DONE) break; else if(nh->nlmsg_type==NLMSG_ERROR) { printf(“read err %d\n”,LINE); return ; } else if (nh->nlmsg_type!=RTM_NEWLINK) { printf(“nlmsg_type err %d\n”,LINE); continue; } ifinfo=NLMSG_DATA(nh); //printf("%u : %s \n",ifinfo->ifi_index,(ifinfo->ifi_flags&IFF_LOWER_UP)?“UP”:“DOWN”); attr=(struct rtattr)(((char)nh)+NLMSG_SPACE(sizeof(ifinfo))); len=nh->nlmsg_len-NLMSG_SPACE(sizeof(ifinfo)); for(;RTA_OK(attr,len);attr=RTA_NEXT(attr,len)) { if(attr->rta_type==IFLA_IFNAME) { //执行热插拔检测回调函数 //printf("%s : %s\n",(char)RTA_DATA(attr),(ifinfo->ifi_flags&IFF_LOWER_UP)?“up”:“down”); if(ifinfo->ifi_flags&IFF_LOWER_UP) nm((char)RTA_DATA(attr),LINK_UP); else nm((char)RTA_DATA(attr),LINK_DOWN); break; } } } } } /* 网络插拔检测回调 / void monitor_callback(char eth_name,int status)//status–1 up { printf("%s %s\n",eth_name,status?“up”:“down”); } int main() { printf(“net test\n”); network_link_monitor(monitor_callback); while(1)sleep(1); return 0; } 编译Demo生成net_test拷贝到板子上 测试结果:重复插拔网口可看到内核打印信息和应用程序打印网口up和down信息 https://i-blog.csdnimg.cn/direct/05187638732e44a785f7cf938274191d.png Demo2:使用Netlink实现USB口热插拔。(ZYNQ-7020平台,linux内核为4.19) static void usb_host_thread(void arg) { char rcv_buf[32]={0}; int link_status=0; int ret=0; //判断USB口初始拔插状态 ret=usb_exec(USB_HOST_CMD); if(ret==0) link_status=1; else link_status=0; struct sockaddr_nl client; struct timeval tv; int CppLive,rcvlen; fd_set fds; int buffersize=1024; //创建套接字和绑定Netlink地址,这里使用NETLINK_KOBJECT_UEVENT CppLive=socket(AF_NETLINK,SOCK_RAW,NETLINK_KOBJECT_UEVENT); if(CppLive<0) { printf(“socket err %d\n”,LINE); return; } memset(&client,0,sizeof(client)); client.nl_family=AF_NETLINK; //client.nl_pid=getpid(); client.nl_groups=1; setsockopt(CppLive,SOL_SOCKET,SO_RCVBUF,&buffersize,sizeof(buffersize)); bind(CppLive,(struct sockaddr)&client,sizeof(client)); while(1) { char buf[2048]={0}; / FD_ZERO(&fds); FD_SET(CppLive,&fds); tv.tv_sec=0; tv.tv_usec=100 * 1000; ret=select(CppLive+1,&fds,NULL,NULL,&tv); if(ret<0) { printf(“select err %d\n”,LINE); continue; } if(!(ret>0 && FD_ISSET(CppLive, &fds))) { printf(“select err %d\n”,LINE); continue; } / //阻塞接收内核的Netlink消息,可根据应用需求使用非阻塞IO读 rcvlen=recv(CppLive,buf,sizeof(buf),0); if(rcvlen > 0) { //解析和处理接收到的内核消息 //printf("%s\n",buf); if(strstr(buf,“add”)!=NULL) { if(link_status==0) { ((void ()(int))arg)(1); } link_status=1; } else if(strstr(buf,“remove”)!=NULL) { if(link_status==1) { ((void ()(int))arg)(0); } link_status=0; } } } } void callback(int status) { if(status==1) printf(“usb host up\n”); if(status==0) printf(“usb host down\n”); } int main() { printf(“usb test\n”); usb_host_monitor(callback); while(1)sleep(1); return 0; } 编译Demo生成usb_test拷贝到板子上 测试结果:重复插拔USB host口可看到应用程序打印USB口的up和down信息 https://i-blog.csdnimg.cn/direct/29a9d2c08ef2484ebefa5c444cee0b3f.png