Proposal: Linux Device Registry (DevReg) ======================================== July 29th, tim@tjansen.de Summary ------- The device registry provides a directory in /proc that contains a list of all connected physical devices. Each list entry includes (if available) the name and vendor of the device, its location/topological information, the DevFS nodes (/dev) that the driver(s) have registered and a unique DeviceID that makes it possible to find the device after a reboot. Reasons for the Device Registry ------------------------------- Finding the available devices with the current Linux kernel is difficult and not always possible. For example the only way to get all printers on Linux is to open all devices in /dev that could represent one, but this has some limitations. The first problem is that the user-space app must know which files should be probed. The /dev/lpX files are candidates and for USB printers also /dev/usb/lpX. Everytime a new bus for printers is added there is a new range of device files that must be probed, for example for IEEE1394/FireWire printers user-space apps would have to probe the /dev/ieee1394/lpX (or whatever their names will be). This makes it impossible to write forward-compatible user-space applications. Even if the application knows which devices are actual printers they must be presented to the user so that he can select one of them, and for this things like the printers name and vendor are neccessary. Unfortunately every bus has a different facility to provide these information. Parallel ports expose them in /proc/sys/dev/parport; USB shows them in usbdevfs, but it is not possible to figure out which /dev/usb/lpX file belongs to which physical printer. And as IEEE1394 will have to implement another method to get information about the printer device, user-apps today have no chance to get show them when kernel support is ready. The next problem is associated with hotplugging. If you have two printers attached to the USB bus, say an Epson and a HP printer, you dont know their device files. If you turn on the Epson printer and then the HP printer, the Epson will be /dev/usb/lp0 and the HP /dev/usb/lp1. When you first power on the HP on the next reboot (or turn off the Epson before you turn the HP on) it will be on /dev/usb/lp0. Sometimes devices have several independent interfaces that belong together. For example there are USB scanners with buttons that are intended to trigger actions by user-space applications like starting a scan. The USB driver uses two separate drivers for them, in the case of a scanner it would use a HID driver (there is a patch available for non-keyboards/mice/joystick input devices) and a regular scanner driver. A similar setup is also common for USB audio devices. With the current implementation it is not possible to find out which device files represent which physical device. Solutions provided by the Device Registry ----------------------------------------- The Device Registry (DevReg) provides a list of all physical devices in /proc/devreg. Each entry consists of: - the model and vendor name of the device (if available) - the type of the device (like hub/usb) - the bus/location of the device (like COM2 or USB) - topology information (to build a tree of all devices) - the unique DeviceID to find the device after a reboot - the device files and the interface that they implement (like OSS or V4L) This should be sufficient for the following use cases: - find all devices that implement a interface (for example all OSS devices) and present them to the user he can chose one - get the DeviceID of a device so its device file can be found after a reboot - find a device file for a DeviceID - show all devices in the computer Finding a device with a DeviceID instead of a device file name is neither supported by current applications nor by other Unix-like operating systems. Therefore, to stay compatible with existing software, a user-space daemon could take care of creating stable names for devices. For example, if you have the Epson and HP printer from the example above, the daemon could create two symlinks called /dev/lpEpson and /dev/lpHP that always point to the correct device file as long as the physical device is connected. As the format of /proc/devreg is relatively complex a user-space library is available that implements the use cases given above. DeviceID -------- Each device get a unique device id. This id is a string in the format "bus-type/location/model/serialnum". bus-type is "usb" or "pci" for the currently supported busses. location is "busnumber:slotnumber" for PCI devices and "busnumber[:1st-hub-port-number:[:2nd-hub-port-number..]]:device-port-num" for USB devices. The format of the model is "vendorID:deviceID" for both USB and PCI. The serial number is driver and device specific, ethernet adapters would take their hardware address here. For those devices that cannot be identified the field should be empty. Order of matching criteria for finding a device id: * try exact match (same bus-type, location, model & serialnum) * if serialnum is not empty: try to match same bus-type, model & serialnum * if serialnum is empty: try to match same bus-type, location & model * if serialnum is empty: try to match same bus-type & model An application that uses the device id may split the device id into its four parts and use them separately (for example the bus-type and model part could be used to find a driver), but it must not interpret the content. Device Type ----------- To display the devices for the user an identification of the type (or class) of the device is needed. The format is "base-type[/sub-type]", for example "network-adapter/ethernet" or "controller/scsi". The classes should be managed centrally to avoid duplicate entries. The device type is intentionally simple and does not reflect all possible devices. This makes it possible to provide icons for all device types, but also prevents uncommon devices from being categorized. An integrated keyboard and pointing device could only be represented as a generic input device (unless they get really popular and a they new class is created for them). Therefore an application that tries to find a device should always search for interfaces and not for the device types. Subdevices ---------- A device can have subdevices. This is useful for multi-functional devices like an integrated USB and FireWire controller. In this case there is a list entry for the card itself with the device-type "multi-functional". The device files are associated with the sub-device, not with the super device. Device File Interface --------------------- To describe the interface of a device file a simple string in the format "category/apiname[/devicename]" is used. The devicename field is empty if the API only uses a single file per device. For example, the OSS sound API would use the string "sound/oss/dsp" for its "/dev/dsp" file. ALSA device files start with "sound/alsa". Device Registry Output ---------------------- The device registry can be read as a number of files in the /proc/devreg directory. Each file contains exactly one value that may be an integer, an unsigned long, a string or a enum key. When things have to be enumerated dynamic directories are used, a capability that the patch adds to the proc file system. Each physical device has got its own directory called /proc/devreg/ where device-number is a 0 for the first device, 1 for the second and so on. The numbers are not persistent and can change when the user unplugs a device. The device directory contains the following files: bus - contains the id of the device's bus (for example "usb" or "pci") deviceid - the deviceid location - the location description as a text. It is intended that this description can be shown to the technical user; it should be understandable in all languages, commonly used acronyms are prefered (for example "com1" or "pci") locationdesc - a enum key that can be "internal" for devices that are inside the case, "external" for external devices, "virtual" for software devices and "unknown" if this unknown model - a text description of the device model. If possible this should come from the device itself (USB) or a database (PCI), otherwise a standard name should be set by the driver. This value can be presented to the user. parent - the deviceid of the parent device if there is one, otherwise empty superdevice - the deviceid of the superdevice (if this is a subdevice), otherwise empty type - the predefined device type id as described above vendor - if available the name of the vendor (see model), otherwise empty. Value can be presented to the user. Each device directory also contains at least one subdirectory that with a list of the device files. Each device file description is contained in its own dynamic directory, so the path is /proc/devreg//devicenodes/. The device file directory contains the following files: name - the name of the file. This is relative to the root of devfs, so the file /dev/input/mice will have the name input/mice. interface - the device file interface as described above Additionally the drivers that handle the device can add additional device- or bus-specific directories for the physical device. For example usb devices have the extra directory /proc/devreg/22/usb that contains usb-specific values. Device Registry Change Count ---------------------------- The device registry can change while it is read so that the user-space program gets corrupted output. To prevent this a file called /proc/devreg/changecount has been added that contains an integer that will be changed after each modification in the device registry. You should always read this file using a single read with a buffer of sufficient size because the changecount could change while you are reading it. The recommended way to use the device registry is to read the changecount, then make a snapshot of the device registry db containing all values that you need and then read the changecount again. If it has changed you must re-read the snapshot until you have success. You also have to be prepared for errors of all kinds while reading the files because the device could disappear while you are reading a directory. In case of an error you should re-read the db. To get notified of registry changes without constantly checking the changecount you can use poll(2) or select(2) on the changecount file. A POLLERR event (for poll) or an exception event (for select) will occur under the following conditions: 1. when you have opened the device and call poll/select directly it will trigger the event if the changecount has changed since it has been opened 2. when you have read the first byte of the changecount the poll/select event will be triggered when the changecount is different than the one that has been read The usual way to use this is to read the changecount number and then call poll/select. It will return immediately when the changecount has been changed since the number has been read or otherwise block until the there is a change. After you handled the change in the registry set the file to position 0 and start from the beginning. The latest version of this document as well as the patch can be downloaded from http://www.tjansen.de/devreg