何为udev?
从linux2.6起,引入了udev作为device manager。
udev运行在user space,user可以通过udev rules管理设备。udev提供了一个动态设备目录,目录中仅包含连接到系统中的设备的节点。创建的设备文件在/dev目录下。早期linux利用/sbin/hotplug监听设备插拔,hotplug在2.5之后被移除,直接由udev利用netlink监听插拔events。
为何引入udev?
引入udev主要解决三个问题:
1、problems identifying the exact hardware device for a device node in /dev
kernel在检测到一个设备的时候,会分配给它一个主设备号和次设备号,在/dev下名字为/dev/hda和/dev/hdb,然而如果交换设备,则主次设备号会产生变化。这导致了识别设备困难。udev可以提供给设备一个固定的名字,名字与设备一一对应,方便识别。
2、Huge number of device node in /dev
3、Not enough major/minor number pairs
利用udev获取设备信息
$udevadm info -a /dev/XXX
将user制定的rules作用于设备
$udevadm control --reload-rules
也可以利用udev实现runtime的监听功能,参考tutorial
http://www.signal11.us/oss/udev/udev_example.c
利用libudev和sysfs实现监听设备hotplug event,并返回指定的消息:
#include
#include
#include
#include
#include
int main (void)
{
struct udev *udev;
struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *dev_list_entry;
struct udev_device *dev;
struct udev_monitor *mon;
int fd;
/* Create the udev object */
udev = udev_new();
if (!udev) {
printf("Can't create udev\n");
exit(1);
}
/* Set up a monitor to monitor hidraw devices */
mon = udev_monitor_new_from_netlink(udev, "udev");
udev_monitor_filter_add_match_subsystem_devtype(mon, "hidraw", NULL);
udev_monitor_enable_receiving(mon);
/* Get the file descriptor (fd) for the monitor.
This fd will get passed to select() */
fd = udev_monitor_get_fd(mon);
/* Create a list of the devices in the 'hidraw' subsystem. */
enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "hidraw");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
/* For each item enumerated, print out its information.
udev_list_entry_foreach is a macro which expands to
a loop. The loop will be executed for each member in
devices, setting dev_list_entry to a list entry
which contains the device's path in /sys. */
udev_list_entry_foreach(dev_list_entry, devices) {
const char *path;
/* Get the filename of the /sys entry for the device
and create a udev_device object (dev) representing it */
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev, path);
/* usb_device_get_devnode() returns the path to the device node
itself in /dev. */
printf("Device Node Path: %s\n", udev_device_get_devnode(dev));
/* The device pointed to by dev contains information about
the hidraw device. In order to get information about the
USB device, get the parent device with the
subsystem/devtype pair of "usb"/"usb_device". This will
be several levels up the tree, but the function will find
it.*/
dev = udev_device_get_parent_with_subsystem_devtype(
dev,
"usb",
"usb_device");
if (!dev) {
printf("Unable to find parent usb device.");
exit(1);
}
/* From here, we can call get_sysattr_value() for each file
in the device's /sys entry. The strings passed into these
functions (idProduct, idVendor, serial, etc.) correspond
directly to the files in the /sys directory which
represents the USB device. Note that USB strings are
Unicode, UCS2 encoded, but the strings returned from
udev_device_get_sysattr_value() are UTF-8 encoded. */
printf(" VID/PID: %s %s\n",
udev_device_get_sysattr_value(dev,"idVendor"),
udev_device_get_sysattr_value(dev, "idProduct"));
printf(" %s\n %s\n",
udev_device_get_sysattr_value(dev,"manufacturer"),
udev_device_get_sysattr_value(dev,"product"));
printf(" serial: %s\n",
udev_device_get_sysattr_value(dev, "serial"));
udev_device_unref(dev);
}
/* Free the enumerator object */
udev_enumerate_unref(enumerate);
/* Begin polling for udev events. Events occur when devices
attached to the system are added, removed, or change state.
udev_monitor_receive_device() will return a device
object representing the device which changed and what type of
change occured.
The select() system call is used to ensure that the call to
udev_monitor_receive_device() will not block.
The monitor was set up earler in this file, and monitoring is
already underway.
This section will run continuously, calling usleep() at the end
of each pass. This is to demonstrate how to use a udev_monitor
in a non-blocking way. */
while (1) {
/* Set up the call to select(). In this case, select() will
only operate on a single file descriptor, the one
associated with our udev_monitor. Note that the timeval
object is set to 0, which will cause select() to not
block. */
fd_set fds;
struct timeval tv;
int ret;
FD_ZERO(&fds);
FD_SET(fd, &fds);
tv.tv_sec = 0;
tv.tv_usec = 0;
ret = select(fd+1, &fds, NULL, NULL, &tv);
/* Check if our file descriptor has received data. */
if (ret > 0 && FD_ISSET(fd, &fds)) {
printf("\nselect() says there should be data\n");
/* Make the call to receive the device.
select() ensured that this will not block. */
dev = udev_monitor_receive_device(mon);
if (dev) {
printf("Got Device\n");
printf(" Node: %s\n", udev_device_get_devnode(dev));
printf(" Subsystem: %s\n", udev_device_get_subsystem(dev));
printf(" Devtype: %s\n", udev_device_get_devtype(dev));
printf(" Action: %s\n", udev_device_get_action(dev));
udev_device_unref(dev);
}
else {
printf("No Device from receive_device(). An error occured.\n");
}
}
usleep(250*1000);
printf(".");
fflush(stdout);
}
udev_unref(udev);
return 0;
}