User Mode Drivers

Want to support your device without becoming a kernel hacker? Bryce shows how.

PCI is this wonderful interface that automatically configures the hardware and automatically assigns the device interrupts and up to six base addresses. You can plug in a card and forget about it--until you need to write the driver. Well fortunately, there's another great cheat to help with this task (drumroll, please): /proc/bus/pci/devices. When you view this file in the /proc pseudo-filesystem, what appears is a bunch of hexadecimal numbers. Here's what my machine says (ignoring the other devices):

0168   148aac05   b   f6400008   f6aff008   00000000
00000000   00000000   00000000   00000000   00400000
00001000   00000000   00000000   00000000   00000000

For example, I installed a parallel I/O card manufactured by my employer (part number PCI-AC5) into my computer. Looking at this last entry (starting with the 0168), the important fields are the ones that say 148aac05, f6400008 (base address register zero or BAR0) and f6aff008 (BAR1). A vendor and device ID identify each PCI device. The 0x148aac05 indicates this device has a vendor ID of 0x148a (Opto 22) and a device ID of 0xac05. Vendor IDs are managed by the PCISIG organization; Opto 22 chose the specific device ID.

Continuing with the example, the PCI-AC5 I/O card has two configured base address registers (addresses 0xf6400000 and 0xf6aff000). The lowest nibble (least significant four bits) is ignored (set to zero). The least significant bit indicates whether the base address is I/O mapped or memory mapped in the case of x86 architectures. PCI devices may have up to six base addresses and an interrupt (the b in the list). User-mode drivers can't take advantage of the interrupt. A simple fscanf() program can read these values and open the appropriate areas for access.

In the event that you have multiple identical cards, you may find it difficult to know who's who. Do not rely on the order /proc/bus/pci/devices gives compared to the silkscreen slot numbers printed on the motherboard to identify which card is which. To identify the card, I've written applications to toggle some indicators on the cards. This application asks which device index to find and test.


While user-mode drivers are simple, there are a few shortcomings you should watch out for:

  • Multiple access to the hardware must be made thread safe. That is, two applications or threads cannot randomly share the device unless the hardware itself is not prone to uncoordinated (non-atomic) accesses.

  • Writing to a wrong I/O address may have disastrous effects on the computer or hardware components. I recommend validating the port address arithmetic before attempting a port read or port write. Use some printfs of the I/O addresses before finding out something is wrong.

  • Improper memory accesses as a result of improper pointer arithmetic may freeze the computer. Try some pointer printouts before finding out the hard way. Once the pointer is cast, the most common problem is errors created due to improperly sized pointer arithmetic. Be very careful with pointer arithmetic.

  • While there are mechanisms to determine the size of a PCI base address block, don't rely on them. Determine the actual register locations of the device by locating real hardware documentation. Writing to an undefined location in the allocated block may result in a hard-frozen computer.


If you're thinking about porting that old DOS application, testing out a new hardware device or just want to create a quick-and-dirty hardware application, consider using a user-mode driver. It's ideal for hardware devices that require a simple interface and don't have timing requirements, and it may just help you meet that impossible deadline.

Bryce Nakatani ( is an engineer at Opto 22, a manufacturer of automation components in Temecula, California. He specializes in real-time controls, software design, analog and digital design, network architecture and instrumentation.