做嵌入式开发,尤其在网关、路由器或者其他支持USB设备的终端上,为了提高用户体验,我们常常需要支持自动识别并挂载USB设备功能。某些应用程序,在使用USB设备的过程中,也希望能够侦测到USB断开事件,不至于某些工作因为USB已经不存在而白做。在Linux下,我们主要有两种办法检测USB热插拔。
第一种便是定时检查/proc/scsi/scsi文件,该文件内会按照标准格式保存着当前设备内挂载的存储介质基本信息,如果在PC端,除了硬盘(ATA)、光驱(CD-ROM)外,就是USB设备(Direct-Access)了,轮询该scsi文件,检查文件内是否新增或减少数据便可实现自动侦测USB热插拔的效果。但是这种方法对于热插拔(hotplug)设备,如U盘,效果就没那么理想了,因为我们不知道设备什么时候插上,又是什么时候被拔掉了,只能验证当前是否已经插上或者已经拔除的事实。于是便有了另一种办法,我们采用一种特殊类的的文件描述符(套结字)专门用于Linux内核跟用户空间之间的异步通信,这种技术通常被成为NETLINK。
由于NETLINK是linux内置功能,所以使用起来很简单:创建一个AF_NETLINK协议族下NETLINK_KOBJECT_UEVENT类型的特殊文件描述符(套结字)CppLive,然后利用setsocketopt允许该文件描述符(套结字)复用其他端口,再利用band函数将自身进程绑定到特殊文件描述符(套结字)CppLive,最后利用select在while循环内监听CppLive是否可读,如果可读则调用recv接收Linux系统内核传递过来的数据并打印出来,这些输出便是USB热插拔信息。当然你也可以个性化地处理来自内核的热插拔信息,让程序变得更加智能以及人性化。
利用NETLINK检测USB热插拔的C语言实现代码如下:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <fcntl.h>
- #include <sys/socket.h>
- #include <linux/netlink.h>
- #define UEVENT_BUFFER_SIZE 2048
- int main(void)
- {
- struct sockaddr_nl client;
- struct timeval tv;
- int CppLive, rcvlen, ret;
- fd_set fds;
- int buffersize = 1024;
- CppLive = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
- memset(&client, 0, sizeof(client));
- client.nl_family = AF_NETLINK;
- client.nl_pid = getpid();
- client.nl_groups = 1; /* receive broadcast message*/
- setsockopt(CppLive, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize));
- bind(CppLive, (struct sockaddr*)&client, sizeof(client));
- while (1) {
- char buf[UEVENT_BUFFER_SIZE] = { 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)
- continue;
- if(!(ret > 0 && FD_ISSET(CppLive, &fds)))
- continue;
- /* receive data */
- rcvlen = recv(CppLive, &buf, sizeof(buf), 0);
- if (rcvlen > 0) {
- printf(“%s\n”, buf);
- /*You can do something here to make the program more perfect!!!*/
- }
- }
- close(CppLive);
- return 0;
- }
运行程序,测试U盘插入/拔除,输出如下:
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0
- add@/module/usb_storage
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/scsi_host/host6
- add@/bus/usb/drivers/usb-storage
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/scsi_disk/6:0:0:0
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/scsi_device/6:0:0:0
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/scsi_generic/sg2
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/bsg/6:0:0:0
- change@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/block/sdb
- add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/block/sdb/sdb4
- add@/devices/virtual/bdi/8:16
- add@/module/fat
- add@/kernel/slab/fat_cache
- add@/kernel/slab/fat_inode_cache
- add@/module/vfat
- add@/module/nls_cp437
- add@/module/nls_iso8859_1
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/bsg/6:0:0:0
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/scsi_generic/sg2
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/scsi_device/6:0:0:0
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/scsi_disk/6:0:0:0
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/block/sdb/sdb4
- remove@/devices/virtual/bdi/8:16
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/block/sdb
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6/scsi_host/host6
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host6
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0
- remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1
- remove@/host6/target6:0:0