前言
USB(Universal Serial Bus)通用串行总线的缩写,是一个外部总线标准,用于规范主机与外围设备的连接以及通讯,目前使用较多的版本有USB1.1、USB2.0、USB3.0等。USB接口常用在诸如USB串行设备驱动(3G/4G上网卡、蓝牙设备、串口设备)、USB大容量磁盘驱动(U盘、移动硬盘)、USB主机控制器驱动(嵌入式otg,dwc_otg)、USB键盘鼠标等,这一些的USB功能支持可以通过内核配置来实现,内核会管理这些USB设备的信息(lsusb 命令可以查看USB设备的情况)。上层应用开发则可以通过监听内核socket来获取设备的热拔插信息,进而利用此信息来确定相关的挂载操作或者其他的一些上层应用的业务逻辑。
USB驱动框图
为了加深对USB的理解,我们且看看USB2.0 Host逻辑框图,以达到对USB设备与主机对接接口及协议有一个比较全面的理解。可以看出最右边的PHY0、PHY1是用来实现与外部设备进行物理连接的,然后通过serial interface与USB控制单元相连接,USB控制单元里面又分为EHCI Host Controller和OHCI Host Controller以实现USB2.0、USB1.1的兼容,EHCI Host Controller和OHCI Host Controller则通过AHB BUS(在PC机则是PCI总线)总线与Memory和CPU相连。整个过程看起来并不是十分复杂,但是实际应用中有大量的控制寄存器需要配置来实现与不同的外围设备进行相连,但是这一切的繁杂工作内核已经帮我们实现了。
USB热拔插
热插拔(hot-plugging或Hot Swap)即带电插拔,它的诞生提高了系统与外围设备的交互能力。那么我们如何获取USB的热拔插事件呢,这里可以通过与内核建立socket连接,然后对socket进行监听来获取USB拔插信息,接着对监听的信息进行处理,对于大容量存储设备则决定设备的挂载目录(mount /dev/sdb /usb)或者设备卸载(umount -l /USB)、对于诸如3G/4G等上网卡设备,则可以通过监听拔插信息来决定拨号上网或者接听电话等。下面通过例子说明如何通过内核socket获取设备热拔插信息的。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <stddef.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <mntent.h>static int CreateHotPlugSock(void)
{struct sockaddr_nl snl;const int buffersize = 16*1024;int retval = 0;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 get socket:%s",strerror(errno));return -1;}setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));retval = bind(hotplug_sock,(struct sockaddr *)&snl,sizeof(struct sockaddr_nl));if(0 > retval){printf("bind failed:%s",strerror(errno));close(hotplug_sock);hotplug_sock = -1;return -1;}return hotplug_sock;
}int main(int argc, char* argv[])
{int sockfd;int sum,size;char buf[1024] = {0};char *str;sockfd = CreateHotPlugSock();while(1){sum = 0;memset(buf, 0, sizeof(buf));size = recv(sockfd, buf, sizeof(buf), 0);// fprintf(stderr,"size=%d, buf=[%s], strlen(buf)=%d\n", size, buf, strlen(buf));while(sum < size){str = buf + sum;sum += strlen(str);buf[sum] = '\n';}buf[sum] = 0;//输出热拔插socket监听的信息fprintf(stderr,"buf=[%s]\n\n", buf);usleep(100*1000);}
}
下面是样例代码socket监听鼠标设备拔插的部分打印信息
总结
正如linux下一切都是文件的哲学理念一样,现在的外围设备则一切以USB为标准看齐,在应用程序开发过程中,经常需要对这些USB外围设备的热拔插事件进行监听处理来实现上层软件的业务逻辑,内核socket很好解决了这个问题,通过它可以很方便地与这些USB热拔插设备交互。原创不易,转载请说明出处。
文章参考:
https://www.cnblogs.com/oracleloyal/p/5333276.html
https://blog.csdn.net/gladyoucame/article/details/8768731