A Survey of Linux Device Drivers
A Survey of Linux Device Drivers
co m
Character Devices T he character device driver implements a byte-oriented interf ace that can be read f rom or written to. T he interf ace is also stream-oriented, meaning that you cannot "seek" f orwards or backwards through the data the way you can with an ordinary data f ile. Character devices are appropriate f or things like serial port drivers, etc. T he most important data structure used by character device drivers is the f ile_operations structure, a portion of which appears below: Character device drivers at a minimum must implement the open() and release() methods, but usually also implement the read() and write() methods as well. T he poll() method assists applications in "sleeping" until data is available, and the ioctl() method provides an out-of -band channel of ten used f or things like specif ying the bit rate and parity settings in a UART driver's associated hardware device. About struct cdev Later Linux-2.6 kernels of f er a struct cdev structure, which encapsulates the f ile_operations structure and some other important driver inf ormation. By using cdev, character devices gain a more exible and unif orm interf ace to the kernel's internal character device resources, including proc f ilesystem entries and udev. All new character drivers are expected to use cdev; existing drivers are being migrated as time permits. ioctl() T he ioctl() entry point in a character device driver provides f or out-of -band communications with the device driver itself . Common uses f or this capability are to assert bit rate and parity settings f or serial port hardware, and to change f ramebuf f er modes. A character device driver's ioctl method looks like this: T his example prints the parameters passed via the ioctl() system call. Device drivers that of f er ioctls provide enumerations f or the cmd parameter, and when those commands need arguments, the arg parameter can pass either an unsigned long parameter value, or the address of a data structure containing a collection of parameter values. An application with a tweak_example_device() f unction that sends a hypothetical MYDRIVER_IOCT L1 command to a driver might look like this: A common usage f or ioctls is to enable the "user interrupt" f eature of the real-time clocks used in many PCs. T he code to do that might look like this: Interrupt Handlers Under Linux, interrupt handlers are not conf ined to device
drivers. Functions not associated with device drivers may also bind to interrupt sources. An LED that blinks when a button is pressed is most easily done using a simple interrupt handler, f or example. Interrupt handler f unctions bind to interrupt sources using the kernel's request_irq() f unction. Under Linux interrupt handlers are ordinary C f unctions, but they are restricted in the kernel f eatures they may use. Interrupt handlers cannot block on semaphores, f or example. A simple interrupt handler f or a hypothetical interrupt source named GPIO_PIN_PB29 looks like this: T his interrupt handler disables the associated interrupt source when ten interrupts are detected (a button is pressed ten times, f or example). Assuming the host processor provides a suitable def inition f or GPIO_PIN_PB29, the handler may be bound to the interrupt source using something similar to the f ollowing code: A call to request_irq() may f ail if a previouslyregistered interrupt handler did not permit the source to be shared with other handlers. Block Devices A block device driver implements an abstraction commonly associated with hard drives and other datablock-oriented media. A block device of f ers persistent storage, which (generally) allows applications to "seek" data within the device. Properly-implemented block device drivers can be controlled by the kernel's Virtual File System f unctionality, allowing all manner of devices to be used to store nearly all kernelsupported f ilesystems with little additional ef f ort. Block device drivers use the same f ile_operations structure used by character device drivers, but use structure members that are ignored by character devices. Block device drivers are more complex than character device drivers. T he best example of a block device driver is "sbull", f rom Rubini's Linux Device Drivers. MT D Chips and Maps T he Memory Technology Devices (MT D) implementation uses a layered driver approach. Chip drivers control the memory device itself , and map drivers use chip drivers to interact with memory chips using either a block or character device API. T he JFFS2 f ilesystem depends on MT D, but MT D may also be used with other f ilesystems.
Among other things, MT D chip device drivers can probe ash memory chips and similar media to identif y their geometry. T his inf ormation allows MT D to properly erase and program the device. With the rise of the Common Flash Interf ace (CFI) protocol, the need to implement new chip device drivers is f ast becoming history. MT D map drivers provide the lowest-level read/write f unctionality f or ash media, which is usef ul f or hardware that makes ash memory available in pages rather than one large window in the host's memory space (very large ash chips may exceed the memory space accessible by the hardware). Map drivers also tell MT D the physical addresses of available ash memory, and can be used to limit MT D's accesses to only certain areas of that memory. MT D block device nodes are usually named /dev/mtdblock. MT D character device nodes are usually named /dev/mtd. Framebuf f ers Framebuf f er device drivers provide f or direct userspace access to video frame buffers: the memory space used by an LCD controller to store the image actually visible on the LCD panel. XFree86, Qtopia, Microwindows and other GUI libraries interact directly with f rame buf f er memory. Framebuf f er drivers are mostly inf ormational; they provide standardized interf aces that allow applications to set and query video modes and ref resh rates. User applications use the f ramebuf f er driver's mmap() system call to locate f rame buf f er memory. T he properties of a f ramebuf f er driver's associated hardware are stored in a struct f b_inf o structure. T his structure also contains the physical address of the f rame buf f er memory, and the physical addresses of the registers that control display modes, geometry and timing. Framebuf f er drivers also use a struct f b_ops structure, which is roughly equivalent to the struct f ile_operations structure used by character and block device drivers. Applications like f bset use a standardized set of ioctls to invoke these f rame buf f er "operations". Framebuf f er device nodes are usually named /dev/f b. One way to tell if your kernel contains a working f ramebuf f er driver is to look f or Tux, the Linux mascot, on the display panel during kernel startup. I2C Bus Interf aces and Chips T he Linux I2C implementation uses a layered stack of device drivers to control chips connected to an I2Ccompatible bus. At the lowest level are "algorithm" and "bus" drivers, which provide a unif orm abstraction f or interacting with the physical I2C media. I2C bus implementations vary, f rom simple "bit bang" GPIO-based signaling to high-perf ormance dedicated peripherals. I2C "chip" drivers control a member of an I2C bus, including temperature sensors, GPIO controllers, ADCs, and so f orth. T he unif ormity provided by I2C algorithm and bus drivers allow the same chip driver to be used regardless of how the actual I2C bus is implemented. I2C chip drivers provide a detect() f unction that allows the I2C system to conf irm the presence of the associated device. If the device is f ound, the driver registers itself with the I2C system using the i2c_attach_client() f unction. Once registered, I2C chip drivers vary widely in the kernel resources they use. Touch screen controller chip drivers, f or example, may interact with the kernel's input subsystem, interrupt handlers, proc f ilesystem, and other f unctionality. Temperature and f an speed sensors are more unif orm, so that userspace monitoring applications will work across the wide range of devices employed by PCs and embedded hardware. Ethernet Devices Ethernet drivers provide an Ethernet-specif ic abstraction that f ocuses almost exclusively on data exchange with the network media access controller (MAC). Other network-related f unctions, like IP packet assembly and decoding, are handled within the kernel's network protocol stacks and do not directly inf luence the
implementation of an Ethernet device driver. T he most common reason f or getting involved with an Ethernet device driver is to add support f or a new physical access controller (PHY). In an existing Ethernet device driver's probe() f unction, the PHY must of ten be identif ied so that the Ethernet network controller can be properly conf igured. T he network controller generally provides control registers f or communicating with an attached PHY, including reading the device's ID register. Of ten, the only change required is to add an enumeration so that the new PHY will be recognized during probing. T he Platf orm Model T he Platf orm Model of f ered by kernel versions 2.6 and beyond provides a ref ined way to attach devices to drivers that eliminates the need f or device drivers to contain hard coded physical addresses of the devices they control. T he platf orm model also prevents resource conf licts and improves kernel portability, helps get startup ordering right, and integrates with the kernel's power management f eatures. Under the platf orm model, device drivers know how to control a device once inf ormed of its physical location and interrupt lines. T his inf ormation is provided to the driver in the f orm of a \resource list" passed to the driver during probing. A f ramebuf f er driver needs to know the physical addresses of the f rame buf f er memory and control registers. T he resource list might look like this:
Once the resources are def ined, that inf ormation is merged into a platf orm_device structure: Finally, the resource list and associated driver name are registered: platf orm_device_register(&lcd_panel_dev); At some point during kernel startup, a device driver named "s1d13xxxf b" may register itself . Af ter registration, the platf orm model implementation invokes the driver's probe() f unction, passing it the associated resource list. Helper f unctions allow the driver to extract resource entries f rom the list and thereby locate the device in question. Bill Gatlif f is a f reelance embedded developer and training consultant with 10 years of experience using GNU and other tools f or building embedded systems targeting automotive, aerospace, and medical instrumentation applications. He is a contributing editor f or Embedded Systems Design, author of the Embedded GNU Jumpstart and Embedded Linux Jumpstart series of training materials. T his paper was
written f or and presented at the Embedded Systems Conf erence Silicon Valley 2006. For more inf ormation, please visit www.embedded.com/esc/sv inShare0