Recent Changes - Search:
NTLUG

Linux is free.
Life is good.

Linux Training
10am on Meeting Days!

1825 Monetary Lane Suite #104 Carrollton, TX

Do a presentation at NTLUG.

What is the Linux Installation Project?

Real companies using Linux!

Not just for business anymore.

Providing ready to run platforms on Linux

Being written...

Hotplugging, Udev, HAL and D-BUS

Ode to /dev

It is very common for people to discuss Unix and Linux as an operating system where files are used extensively as interface points even for devices connected to the system. There is a lot of truth to that statement. Historically, the /dev directory on a *ix platform housed a static set of files for all well known devices that might be attached to a machine. Then, if a new device came out, one would typically compile a device driver into the kernel and probably also create one or more /dev device files to act as interfaces to the device.

Device files were created at install time statically and/or by using a program script often called makedev or MAKEDEV. That script would create all of the commonly used static devices in /dev using a program called mknod. The mknod command is a command line utility that allows you to create character and/or block special files along with a major and minor number. Whether or not a device needs to be character or blocked depends on what kind of device it is. Streaming devices, terminal input and serial devices are usually character based. Devices that need can operate on blocks of data and use random I/O are typically block devices. Good examples of block devices are things like hard disks. The major number usually indicates a class of device and the minor number is a subclass of that device. However, it could be that a major number is a device and the minor number is an instantiation of the device. Regardless, usually the major number is what registers a particular device file with a corresponding driver in the kernel.

Did you know...

One of the problems in early Linux with using major and minor numbered device files is that they were 8 bit values which meant that only a total of 255 device classes and 255 subclasses could ever be defined. This limited space meant that assigning new kernel major numbers had to be controlled. The Linux Assigned Names and Numbers Authority (LANANA, http://lanana.org) was given this responsibility. This is a huge limitation and therefore most *ix platforms no longer have limited/restricted 8 bit major/minor style device files (Linux kernel 2.6 changed to a 32 bit value for devices with a 12 bit major and 20 bit minor). But with even more potential /dev devices the act of creating, managing and maintaining /dev device files becomes unwieldy.

DevFS Flattery

Commercial Unix systems and BSD created an idea of a device filesystem where device files would be managed dynamically. For example, in Solaris, the command drvconfig could find new devices that were dynamically added (e.g. via a fibre SAN) and the command disks would create the necessary /dev files. Alternatively, a reconfiguration reboot on Solaris would also created the /dev files and links. Later versions of Solaris used devfsadm instead of using the combination of drvconfig/disks. Solaris's implementation is often called a "pseudo devfs" because it had limited scope (mostly used for just storage devices).

Hats off to DevFS

Linux DevFS was an attempt to provide dynamic /dev management via the kernel itself for all devices. As new devices came online, the device drivers could "notify" DevFS to create the appropriate /dev file for the device.

Unfortunately, DevFS meant some pretty extensive changes throughout the kernel and in the end, not all Linux distributions used it. Using DevFS meant that the kernel decided on how /dev files were named which was one of the most referenced weaknesses with DevFS. However, technically, there were also race condition problems with DevFS that worried some and arguably were more important. With the vast majority of kernel developers hating the DevFS implementation, DevFS was a short lived addition to the kernel that has since been entirely removed (2.6 kernel). However, with that said, DevFS did show the need for some kind of dynamic /dev management.

Linux Userspace Udev

The replacement for DevFS was to push device making policies out of the Linux kernel and into userspace. Now when the kernel detects a new device a message can be sent to a userspace daemon called udevd that can do whatever is necessary to create the device file dynamically, set up permissions, symbolic links and even run programs needed for the handling of the device. Because the implementation is in userspace, the door is opened for many possibilities.

Udev Implementation

Early solutions for hotplugging involved something called udev, but it was far from the structure in newer 2.6 kernels. The old hotplug mechanism was not terribly flexible and often times created more problems than it solved. The udevd daemon solution in new 2.6 kernels is a better framework though it is still being developed.

Hotplug (2.4, older 2.6 kernels)

Originally, udev was a utility invoked as a part of the hotplug architecture. The program defined at /proc/sys/kernel/hotplug was invoked, usually that program was /sbin/hotplug and it was passed information about the kernel "object" that wanted something done via environment variables. The hotplug program used scripts and configuration files found in /etc/hotplug to determine what to do with the events... eventually even calling a userspace program called udev but with many limitations, and not at all like the new udev daemon of today.

Udev Daemon (2.6, 2.6.16+ hotplug style deprecated)

Even before looking at the udev daemon itself, we need to know how messages can make it from the kernel into userspace. In kernel 2.6, a special IPC was created called netlink. This is a multicast socket which allows potentially multiple userspace programs to receive messages from the kernel. Kernel messages are sent with information about which kernel object is making the request, what action is to be done (e.g. add, remove, mount, unmount, online, offline) and then an optional payload area for additional information.

Udev uses a filesystem to manage /dev device files. Usually /dev is a tmpfs. Tmpfs is an in memory/swap filesystem. It is not persistent. However, udev can actually use any kind of filesystem for /dev. It's probably wise to use tmpfs since the whole idea of a udev managed /dev is dynamic creation and deletion.

The udev daemon, udevd, listens to kernel event messages (UEVENT) and passes information on for udev use. Udev uses rule files and sysfs to determine how to create/remove devices and what kinds of permissions the dynamically created device should have. The rules can make use of helper programs which means that udev can actually do more than create and manipulate device file.

What is a Sysfs?

Sysfs is similar to /dev in that it is an in memory filesystem. The kernel exports information and attributes of devices it sees into the hierarchy under /sys. Why? Well, for one thing, it helps move this kind of information out of /proc which is where everyone had been storing their information (arguably the wrong place, but the only place at one time).

Just before sysfs, there was a type of sysfs called ddfs (Device Driver File System) which used procfs (/proc) to store its data. It was used primarily for debugging until it was deemed to be very useful and eventually turned into the sysfs we have today.

The udev daemon can take a configuration file (/etc/udev/udev.conf) to determine where device files should go (/dev) and where the udev rules files reside (/etc/udev/rules.d) and what kind of log level to use for logging. The rule files are what determine the action that udev takes when a new device is added to the system.

2.6 Udev Rule Format

When a kernel event is processed by udev through the rules, (in general) all of the rules are applied to see what matches and what action to take. A match does not necessarily imply any action. For example:

 SUBSYSTEM=="video4linux",       GROUP="video"

If the SUBSYSTEM of the kernel device event is video4linux, then set the GROUP name to video. Set it on what? Well, we do not know that right now, we just know when some later rule gets done, unless another later rule overrides this one, that the permission of any resulting device file that might be created will have the group set to video.

Notice that key matches use == and assignments use just one =. This can be important since not use == will likely mean that any rule you create will not match anything and you might create an interesting side effect that was not intended. Keys are either used in searches or in assignments and in some cases can be used in both.

Comparison Match Operators:

==
True if there is a comparison match.
!=
True if there is no comparison match.

Simple globing style pattern matches can be used:

*
Matches zero, or any number of characters.
?
Matches any single character.
[range-or-set]
Matches a single character in range (e.g. [0-9], [a-z]) or set (e.g. [2-9AJQK]). If the first character of the set is a !, then match anything that is not contained in the set.

Common keys (elements you can do a comparison match against):

KERNEL
match against the kernel name for the device.
SUBSYSTEM
match against the subsystem of the device.
DRIVER
match against the name of the kernel driver of the device.

Other comparison keys:

ACTION
match against the type of event from the kernel.
DEVPATH
match against the device path of the device.
ATTR{sysfs-attribute}
match against the value of a sysfs (/sys) attribute of the device.*
ENV{variable-name}
match against the value of an environment variable.*
PROGRAM
execute an external program and if result is true, process the rule. Program receives the environment variables of the event.
RESULT
matches the return result of a prior PROGRAM call.

* ATTR and ENV can also be used in assignments.

Assignment Operators:

=
Assign value on the right to the expression on the left.
:=
Assign value on the right to the expression on the left and make immutable.
+=
Append the value on the right to the expression (list) on the left.

Assignment Keys:

NAME
The name of the node to be created. Unlike some keys, only one rule can set the NAME, and once set it is good for the duration of the rest of rule processing.
SYMLINK
The name of a symbolic link to a node. This one can take multiple elements seperated by space and can be appended to/reset by future rules.
OWNER, GROUP, MODE
These 3 items set the permissions for the device node file. MODE uses chmod octal format.
ATTR{sysfs-attribute}
Set the sysfs attribute to a particular value.*
ENV{variable-name}
Set an environment variable to a particular value.*
RUN
The name of a program to run for the node information being worked on. This can be a list of programs. Warning: Long running programs may cause udev to miss other events.
LABEL
Declare a label that can be used as a target for GOTO.
GOTO
Jump to the next LABEL with a matching name.
IMPORT{type}
Import a set of environment variables from a source determined by type.
program
Execute an external program which returns VAR=VALUE tuples.
file
Retrieve VAR=VALUE tuples from a text file argument.
parent
Get environment values from a parent device by reading its database of values. The value is a matching string of the same format as a key comparison.
WAIT_FOR_SYSFS
Wait for the specified sysfs file to be created before continuing. Needed only in special cases where there are timing issues.
OPTIONS
Set this to these fixed values:
last_rule
Prevents further rule processing for node.
ignore_device
Ignore the device completely.
ignore_remove
Ignore future remove events for the device.
all_partitions
Forces the creating of all possible block device partition nodes. Use this for block devices where media changes are not detectable.

* ATTR and ENV can also be used in comparisons.

Some keys can use special substitution macros inside of them. Those keys are NAME, SYMLINK, PROGRAM, OWNER, GROUP and RUN. Substitutions are made when the rule is done with the exception of RUN, in that case the substitution is not made until all rules have been done (this allows for side effects to get done prior to invoking a program). The substitution macros look a lot like printf macros or alternatively shell substitutions:

$kernel, %k
The kernel name for this device.
$number, %n
The kernel number for this device. For example sda3 has a kernel number of '3'.
$devpath, %p
The devpath of the device.
$id, %b
The name of the device matched while searching the devpath upwards for SUBSYSTEMS, KERNELS, DRIVERS and ATTRS.
$attr{file}, %s{file}
The value of a sysfs attribute called file. If not found, it will search upwards in the sysfs hierarchy looking for a match. If file is a symbolic link, then the last part of the link is returned.
$env{variable-name}, %E{varable-name}
The value of an environment variable.
$major, %M
Kernel major number for the device.
$minor, %m
Kernel minor number for the device.
$result, %c or $result{number}, %c{number}
Returns the string returned by the PROGRAM used in the comparison for the rule. If used in the form of $c{number}, treat each space separated word of the returned string as a field selection by number. You can use number+ to specify a field and all elements following that field.
$parent, %P
The node name of the parent device.
$root, %r
The udev_root value.
$tempnode, %N
The name of a created temporary device node to provide access to the device from an external program before the real node is created.
%%
A percent sign.
$$
A dolar sign.

The common keys also have a plural form which says to search the sysfs starting at the device path upwards for a match (KERNELS, SUBSYSTEMS, DRIVERS). For example consider:

 KERNEL=="hd*[!0-9]", ATTR{removable}=="1", DRIVERS=="ide-cs|ide-floppy", GOTO="persistent_storage_end"

This matches ide device hda, hdb, etc. (any ide device not ending in a numeric digit) where there is a sysfs attribute of removable. However if anything in the sysfs tree from that device path upwards identifies the driver as ide-cs or ide-floppy, then this rule forces rule processing to continue at the label called persistent_storage_end.

The rules files in /etc/udev/rules.d are parsed in lexical order. The best way to add your own custom rules is with a new file. In general, your rules should precede any of the default rules on your system, however, this is not always true. A careful examination of the rules is really the best way to determine where you want your custom rules to go. And the best way to learn more about writing rules is to look at some.

Example 1, permissions on a usb printer node.

 SUBSYSTEM=="usb", KERNEL=="lp*", NAME="usb/%k", SYMLINK+="usb%k", GROUP="lp"

This sets the node name to usb/%k (which for example could be lp0). Then it also ensures a symbolic link named usblp0 points to it, and that the node device file has group ownership by the group lp.

 $ ls -ld /dev/usb/lp0 /dev/usblp0
 lrwxrwxrwx 1 root root      7 2008-02-03 00:33 /dev/usblp0 -> usb/lp0
 crw-rw---- 1 root lp   180, 0 2008-02-03 00:33 /dev/usb/lp0

Example 2, the console devices.

 KERNEL=="console", MODE="600", OPTIONS="last_rule"

For the kernel device called console set the permissions so that only the owner can read/write and make sure this is the last rule to be done on the device.

Example 3, using a helper program to load firmware for some device (e.g. a wireless network adapter)

 SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware.sh"

We do not have to know exactly what kind of device we are dealing with... this rule only seeks to find out if the event is requesting some kind of firmware load for the device. The helper script is at /lib/udev/firmware.sh and is run with the environmental data for the device (e.g. DEVPATH, FIRMWARE).

Example 4, A SCSI disk (the basics, device and generic device)

 SUBSYSTEM=="block", GROUP="disk", MODE="0640"
 SUBSYSTEM=="scsi", ACTION=="add", ATTR{type}=="0|7|14", ATTR{timeout}="60"
 SUBSYSTEM=="scsi_device", ACTION=="add", ATTRS{type}=="0|7|14", RUN+="/sbin/modprobe sd_mod"
 SUBSYSTEM=="scsi_device", ACTION=="add", RUN+="/sbin/modprobe sg"
 KERNEL=="sg*", GROUP="disk", MODE="640"

This will set the block device permissions to use group disk and permissions 640 (rw-r-----). The sysfs timeout for the device will get set to 60. The module sd_mod will get loaded as well as the module sg and finally, the node for the sg device will also have group disk and permissions 640.

Writing Udev Rules Via Inspection

Looking at the rules in /etc/udev/rules.d gives some insight into how to make rules, but probably not enough information to write some rules from scratch. There are several utilities that can be used to help inspect and test rules.

udevmonitor
Shows the live kernel events and udev messages sent to D-BUS.
udevinfo
Shows detailed information about device nodes by path or by name. Excellent tool for discovering certain device attributes.
udevtest
Command that does not alter anything, but merely allows you to trace what would happen rule wise if udev were invoked for a particular device node.

Using udevmonitor

In order to get a better understanding of how to write rules, we can monitor the kernel UEVENT messages and the udev messages to the D-BUS. The name of the command is udevmonitor. If executed without options, you will get simple summary line of each message:

Examining udevmonitor Output for my Sony Camcorder:

 # udevmonitor
 udevmonitor prints the received event from the kernel [UEVENT]
 and the event which udev sends out after rule processing [UDEV]

 UEVENT[1201927194.229522] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4
 UEVENT[1201927194.229580] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/usbdev2.5_ep00
 UEVENT[1201927194.230896] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0
 UEVENT[1201927194.230910] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/usbdev2.5_ep81
 UEVENT[1201927194.230917] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/usbdev2.5_ep02
 UEVENT[1201927194.230925] add@/class/usb_device/usbdev2.5
 UDEV  [1201927194.269830] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4
 UDEV  [1201927194.271191] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/usbdev2.5_ep00
 UEVENT[1201927194.323936] add@/module/usb_storage
 UDEV  [1201927194.324860] add@/module/usb_storage
 UEVENT[1201927194.325649] add@/bus/usb/drivers/usb-storage
 UEVENT[1201927194.325927] add@/class/scsi_host/host5
 UDEV  [1201927194.326870] add@/bus/usb/drivers/usb-storage
 UDEV  [1201927194.327593] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0
 UDEV  [1201927194.329072] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/usbdev2.5_ep81
 UDEV  [1201927194.329793] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/usbdev2.5_ep02
 UDEV  [1201927194.330761] add@/class/scsi_host/host5
 UDEV  [1201927194.337678] add@/class/usb_device/usbdev2.5
 UEVENT[1201927195.331170] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0
 UEVENT[1201927195.331212] add@/class/scsi_disk/5:0:0:0
 UEVENT[1201927195.743389] add@/block/sdd
 UEVENT[1201927195.743414] add@/block/sdd/sdd1
 UEVENT[1201927195.743422] add@/class/scsi_device/5:0:0:0
 UEVENT[1201927195.743430] add@/class/scsi_generic/sg4
 UDEV  [1201927195.765792] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0
 UDEV  [1201927195.766554] add@/class/scsi_disk/5:0:0:0
 UDEV  [1201927195.768189] add@/class/scsi_generic/sg4
 UDEV  [1201927195.776489] add@/class/scsi_device/5:0:0:0
 UDEV  [1201927195.784801] add@/block/sdd
 UEVENT[1201927195.938454] add@/module/nls_cp437
 UDEV  [1201927195.939543] add@/module/nls_cp437
 UEVENT[1201927195.950031] add@/module/nls_iso8859_1
 UDEV  [1201927195.951002] add@/module/nls_iso8859_1
 UEVENT[1201927195.955314] mount@/block/sdd/sdd1
 UDEV  [1201927195.957249] add@/block/sdd/sdd1
 UDEV  [1201927195.958031] mount@/block/sdd/sdd1

The UEVENT lines are coming from the kernel when we hotplug the Sony Camcorder in. One of the first things we see is the Sony Camcorder USB interface endpoints. The first endpoint iusbdev2.5_ep00. The _ep00 endpoint is always present and represents the control interface. In our example _ep02 and _ep81 represent the I/O sink/source(s) for the USB device. Then the kernel lets us know that the modules for usb-storage are needed and finally we start seeing more familiar SCSI related matter with regards to the block device and the partition on the device.

As nice as all of this is, it's not enough information really. Oh, you can take a look through the /sys directory and probably gain even more insight, but there might be an easier way. Execute udevmonitor --env. Now when we plug in our Sony Camcorder we see more information since we now get to see the environment variables in addition to the summary messages.

Examining udevmonitor --env Output for my Sony Camcorder:

 # udevmonitor --env
 udevmonitor prints the received event from the kernel [UEVENT]
 and the event which udev sends out after rule processing [UDEV]

 UEVENT[1201927350.094357] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4
 ACTION=add
 DEVPATH=/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4
 SUBSYSTEM=usb
 SEQNUM=1669
 PHYSDEVBUS=usb
 PHYSDEVDRIVER=usb

 UEVENT[1201927350.094444] add@/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/usbdev2.6_ep00
 ACTION=add
 DEVPATH=/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/usbdev2.6_ep00
 SUBSYSTEM=usb_endpoint
 SEQNUM=1670
 MAJOR=442
 MINOR=2053

... etc....

The latter gives much more information. Even shows some attribute level information. udevmonitor is a great way to see the live event processing happening as the kernel sends out notifications via netlink for udev.

Using udevinfo

If you know the device node of the resulting device that has been added to the system, you can use udevinfo to query the device node name, or you can use the devpath (e.g. /block/sdd) to the device and retrieve a lot of information. In the case of the Sony Camcorder, I noticed that the filesytem that was mounted was off of /dev/sdd. You can use the udevinfo command to directly query information about a device node by device name (for example).

 # udevinfo --query=all --name=/dev/sdd
 P: /block/sdd
 N: sdd
 S: disk/by-id/usb-Sony_Sony_Camcorder_088xxxxxxxxxx
 S: disk/by-path/pci-0000:00:02.1-usb-0:5.4:1.0-scsi-0:0:0:0
 E: ID_VENDOR=Sony
 E: ID_MODEL=Sony_Camcorder
 E: ID_REVISION=0400
 E: ID_SERIAL=Sony_Sony_Camcorder_088xxxxxxxxxx
 E: ID_TYPE=floppy
 E: ID_BUS=usb
 E: ID_PATH=pci-0000:00:02.1-usb-0:5.4:1.0-scsi-0:0:0:0

However, this tool can really, really help out with rule writing if allowed to walk the entire tree upwards from the device. The --attribute-walk option can really help us to find out what we can actually query for in our rules.

 # udevinfo  --attribute-walk --name=/dev/sdd

 Udevinfo starts with the device specified by the devpath and then
 walks up the chain of parent devices. It prints for every device
 found, all possible attributes in the udev rules key format.
 A rule to match, can be composed by the attributes of the device
 and the attributes from one single parent device.

  looking at device '/block/sdd':
    KERNEL=="sdd"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{stat}=="  54  470  587  684    0    0    0    0    0  596  688"
    ATTR{size}=="78126048"
    ATTR{removable}=="1"
    ATTR{range}=="16"
    ATTR{dev}=="8:48"

  looking at parent device '/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0':
    KERNELS=="5:0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS=="sd"
    ATTRS{ioerr_cnt}=="0x2"
    ATTRS{iodone_cnt}=="0x74a"
    ATTRS{iorequest_cnt}=="0x74a"
    ATTRS{iocounterbits}=="32"
    ATTRS{timeout}=="60"
    ATTRS{state}=="running"
    ATTRS{rev}=="1.00"
    ATTRS{model}=="Camcorder       "
    ATTRS{vendor}=="Sony    "
    ATTRS{scsi_level}=="3"
    ATTRS{type}=="0"
    ATTRS{queue_type}=="none"
    ATTRS{queue_depth}=="1"
    ATTRS{device_blocked}=="0"
    ATTRS{max_sectors}=="240"

  looking at parent device '/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0':
    KERNELS=="target5:0:0"
    SUBSYSTEMS==""
    DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5':
    KERNELS=="host5"
    SUBSYSTEMS==""
    DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0':
    KERNELS=="2-5.4:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb-storage"
    ATTRS{modalias}=="usb:v054Cp02CBd0400dc00dsc00dp00ic08isc05ip50"
    ATTRS{bInterfaceProtocol}=="50"
    ATTRS{bInterfaceSubClass}=="05"
    ATTRS{bInterfaceClass}=="08"
    ATTRS{bNumEndpoints}=="02"
    ATTRS{bAlternateSetting}==" 0"
    ATTRS{bInterfaceNumber}=="00"

  looking at parent device '/devices/pci0000:00/0000:00:02.1/usb2/2-5/2-5.4':
    KERNELS=="2-5.4"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{configuration}==""
    ATTRS{serial}=="088xxxxxxxxxx"
    ATTRS{product}=="Sony Camcorder"
    ATTRS{manufacturer}=="Sony"
    ATTRS{maxchild}=="0"
    ATTRS{version}==" 2.00"
    ATTRS{devnum}=="6"
    ATTRS{speed}=="480"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceClass}=="00"
    ATTRS{bcdDevice}=="0400"
    ATTRS{idProduct}=="02cb"
    ATTRS{idVendor}=="054c"
    ATTRS{bMaxPower}=="  2mA"
    ATTRS{bmAttributes}=="c0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bNumInterfaces}==" 1"

  looking at parent device '/devices/pci0000:00/0000:00:02.1/usb2/2-5':
    KERNELS=="2-5"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{configuration}==""
    ATTRS{maxchild}=="4"
    ATTRS{version}==" 2.00"
    ATTRS{devnum}=="3"
    ATTRS{speed}=="480"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bDeviceProtocol}=="02"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceClass}=="09"
    ATTRS{bcdDevice}=="0009"
    ATTRS{idProduct}=="6560"
    ATTRS{idVendor}=="04b4"
    ATTRS{bMaxPower}=="100mA"
    ATTRS{bmAttributes}=="e0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bNumInterfaces}==" 1"

  looking at parent device '/devices/pci0000:00/0000:00:02.1/usb2':
    KERNELS=="usb2"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{configuration}==""
    ATTRS{serial}=="0000:00:02.1"
    ATTRS{product}=="EHCI Host Controller"
    ATTRS{manufacturer}=="Linux 2.6.18.8-0.7-default ehci_hcd"
    ATTRS{maxchild}=="10"
    ATTRS{version}==" 2.00"
    ATTRS{devnum}=="1"
    ATTRS{speed}=="480"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bDeviceProtocol}=="01"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceClass}=="09"
    ATTRS{bcdDevice}=="0206"
    ATTRS{idProduct}=="0000"
    ATTRS{idVendor}=="0000"
    ATTRS{bMaxPower}=="  0mA"
    ATTRS{bmAttributes}=="e0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bNumInterfaces}==" 1"

  looking at parent device '/devices/pci0000:00/0000:00:02.1':
    KERNELS=="0000:00:02.1"
    SUBSYSTEMS=="pci"
    DRIVERS=="ehci_hcd"
    ATTRS{broken_parity_status}=="0"
    ATTRS{enable}=="1"
    ATTRS{modalias}=="pci:v000010DEd0000005Bsv000010DEsd0000CB84bc0Csc03i20"
    ATTRS{local_cpus}=="00000000,00000000,00000000,0000000f"
    ATTRS{irq}=="225"
    ATTRS{class}=="0x0c0320"
    ATTRS{subsystem_device}=="0xcb84"
    ATTRS{subsystem_vendor}=="0x10de"
    ATTRS{device}=="0x005b"
    ATTRS{vendor}=="0x10de"

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

Where are the device attributes stored? In sysfs of course! But marching through sysfs can be a chore. Here's a simple shell script that makes viewing sysfs somewhat easier. Warning: By default showsysfs will output a lot of data.

(:source lang=bash:) #! /bin/sh # showsysfs: Display /sys attributes showpathnames="" bold="" b="" n="" while [ $# -gt 0 ]; do case "$1" in -b) # show attribute values in bold b=`tput bold` n=`tput sgr0` ;; -p) # Show pathnames showpathnames=1 ;; *) break ;; esac shift done dirs="$*" dirs=${dirs:=/sys} for dir in $dirs; do find "$dir" -print0 2>/dev/null | if [ "$showpathnames" ]; then xargs -n1 -0 grep '.' /dev/null 2>/dev/null else xargs -n1 -t -0 grep '.' /dev/null 2>&1 | sed -e 's,grep . /dev/null ,,' -e 's,[^/]*/,| ,g' \ -e 's,\([^ /|]\),\\- \1,' fi | sed "s/:\(.*\)/: $b\1$n/" done

Using showsysfs we can see some of the more distinctive attributes of the Sony Camcorder.

...

 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/serial:088xxxxxxxxxx
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/product:Sony Camcorder
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/manufacturer:Sony

...

Searching sysfs can also be a long process... so if you know just a piece of information, we can make an easier more direct query about the device. In the case of the Sony Camcorder, I noticed that the filesytem that was mounted was off of /dev/sdd. You can use the udevinfo command to directly query information about a device node by name (for example).

 # udevinfo --query=all --name=/dev/sdd
 P: /block/sdd
 N: sdd
 S: disk/by-id/usb-Sony_Sony_Camcorder_088xxxxxxxxxx
 S: disk/by-path/pci-0000:00:02.1-usb-0:5.4:1.0-scsi-0:0:0:0
 E: ID_VENDOR=Sony
 E: ID_MODEL=Sony_Camcorder
 E: ID_REVISION=0400
 E: ID_SERIAL=Sony_Sony_Camcorder_088xxxxxxxxxx
 E: ID_TYPE=floppy
 E: ID_BUS=usb
 E: ID_PATH=pci-0000:00:02.1-usb-0:5.4:1.0-scsi-0:0:0:0

One of the attributes is the serial number of the device. That could be useful in creating rules that are specific to a particular Camcorder if you owned multiple ones. Let's say that you have 3 different Camcorders, all alike. Using udev rules, we can create specific symbolic links named after the person that actually owns the device. It would also be possible to change the device node permissions to owned by a particular username.

We can inspect the rules that would be done for a given event without executing or modifying anything by using udevtest. This command might be useful for testing out any rule modification we make.

 # udevtest /block/sdd
 This program is for debugging only, it does not create any node,
 or run any program specified by a RUN key. It may show incorrect results,
 if rules match against subsystem specfic kernel event variables.

 main: looking at device '/block/sdd' from subsystem 'block'
 run_program: 'usb_id -x'
 run_program: '/lib/udev/usb_id' (stdout) 'ID_VENDOR=Sony'
 run_program: '/lib/udev/usb_id' (stdout) 'ID_MODEL=Sony_Camcorder'
 run_program: '/lib/udev/usb_id' (stdout) 'ID_REVISION=0400'
 run_program: '/lib/udev/usb_id' (stdout) 'ID_SERIAL=Sony_Sony_Camcorder_088xxxxxxxxxx'
 run_program: '/lib/udev/usb_id' (stdout) 'ID_TYPE=floppy'
 run_program: '/lib/udev/usb_id' (stdout) 'ID_BUS=usb'
 run_program: '/lib/udev/usb_id' returned with status 0
 udev_rules_get_name: add symlink 'disk/by-id/usb-Sony_Sony_Camcorder_088xxxxxxxxxx'
 run_program: 'path_id /block/sdd'
 run_program: '/lib/udev/path_id' (stdout) 'ID_PATH=pci-0000:00:02.1-usb-0:5.4:1.0-scsi-0:0:0:0'
 run_program: '/lib/udev/path_id' returned with status 0
 udev_rules_get_name: add symlink 'disk/by-path/pci-0000:00:02.1-usb-0:5.4:1.0-scsi-0:0:0:0'
 udev_rules_get_name: no node name set, will use kernel name 'sdd'
 udev_device_event: device '/block/sdd' already in database, validate currently present symlinks
 udev_node_add: creating device node '/dev/sdd', major = '8', minor = '48', mode = '0640', uid = '0', gid = '6'
 udev_node_add: creating symlink '/dev/disk/by-id/usb-Sony_Sony_Camcorder_088xxxxxxxxxx' to '../../sdd'
 udev_node_add: creating symlink '/dev/disk/by-path/pci-0000:00:02.1-usb-0:5.4:1.0-scsi-0:0:0:0' to '../../sdd'
 main: run: 'socket:/org/freedesktop/hal/udev_event'
 main: run: 'socket:/org/kernel/udev/monitor'

...

 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/ioerr_cnt:0x2
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/iodone_cnt:0x71
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/iorequest_cnt:0x71
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/iocounterbits:32
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/timeout:60
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/state:running
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/rev:1.00
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/model:Camcorder
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/vendor:Sony
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/scsi_level:3
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/type:0
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/queue_type:none
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/queue_depth:1
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/device_blocked:0
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/max_sectors:240
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/5:0:0:0/power/state:0
 /sys/devices/pci0000: 00/0000:00:02.1/usb2/2-5/2-5.4/2-5.4:1.0/host5/target5:0:0/power/state:0

...

Page last modified on November 03, 2009, at 04:19 AM