2012年12月19日 星期三

利用 libudev 實作 USB 熱拔插偵測功能

udev 是 Linux kernel 的裝置管理服務,我們可以用它提供的 libudev 撰寫裝置熱拔插相關的程式。以下是一個實際範例程式。它每隔一小段時間就會檢查是否有收到 USB 熱拔插事件,並且將收到事件內容顯示到螢幕上。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libudev.h>

#define PROBE_INTERVAL (500)

int main(int argc, char** argv)
{
	struct udev* udev = NULL;
	struct udev_monitor* udev_monitor = NULL;
	struct udev_device* dev = NULL;
	int udev_fd = -1;
	fd_set fds;
	struct timeval tv;
	int ret;
	char node_string[64] = {};
	char serial_string[10] = {};
	const char* tmp = NULL;

	udev = udev_new();
	udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
	udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", NULL);
	udev_monitor_enable_receiving(udev_monitor);
	udev_fd = udev_monitor_get_fd(udev_monitor);

	while(1)
	{
		FD_ZERO(&fds);
		FD_SET(udev_fd, &fds);
		tv.tv_sec = 0;
		tv.tv_usec = 0;

		ret = select(udev_fd + 1, &fds, NULL, NULL, &tv);

		if(ret > 0 && FD_ISSET(udev_fd, &fds))
		{
			dev = udev_monitor_receive_device(udev_monitor);

			tmp = udev_device_get_devtype(dev);

			if(dev && tmp && !strcmp(tmp, "usb_device"))
			{
				if(!strcmp(udev_device_get_action(dev), "add"))
				{
					serial_string[0] = 0;
					strcat(serial_string, udev_device_get_sysattr_value(dev,"idVendor"));
					strcat(serial_string, udev_device_get_sysattr_value(dev,"idProduct"));
					fprintf(stderr, "USB Device [%s] added\n", serial_string);
					fprintf(stderr, "Node \"%s\" created\n", udev_device_get_devnode(dev));

				}else if(!strcmp(udev_device_get_action(dev), "remove"))
				{
					fprintf(stderr, "USB Device removed\n", serial_string);
					fprintf(stderr, "Node \"%s\" deleted\n", udev_device_get_devnode(dev));
				}

				udev_device_unref(dev);
			}
		}

		usleep(1000 * PROBE_INTERVAL);
	}

	udev_monitor_unref(udev_monitor);
	udev_unref(udev);
	udev = NULL;
	udev_monitor = NULL;

	return EXIT_SUCCESS;
}