Вы находитесь на странице: 1из 3

_ _ _

DEVELOPMENT TOOLS

Writing and debugging device


drivers in user space
by Peter Weber, QNX Software Systems

Running device drivers in


user space can dramatically
simplify debugging efforts.
Even if your OS doesn’t directly
support user-space drivers, it
will probably let you write
and debug significant parts
of a driver in user space.

I Anyone who has debugged a device driver OS can cleanly terminate it and reclaim all the whatever additional permissions that drivers re-
knows how slow and tedious the process can be. resources it was using — there is no need to quire. For example, in the QNX Neutrino
Because drivers typically run in the same rebuild and reboot. As an added benefit, you RTOS, a driver will call ThreadCtrl(), which
memory address space as the operating system can debug a user-space driver with same gives the driver full I/O and interrupt privileges.
kernel, a single bug in any driver can easily process-level debugger that you use to debug On Linux, an equivalent function is iopl().
crash the entire system. Consequently, develop- regular applications. You don’t need to learn
ers waste a lot of time rebuilding and rebooting how to use kernel-debug tools and you don’t
Step 2: Gain Access to the Device
the system when they could be testing or have to halt the entire system while the driver is
debugging code. To complicate matters, many being debugged. All other software processes can At this point, the driver has the privileges to ac-
embedded systems lack the non-volatile storage continue to run normally. Even if your OS does- cess hardware and, on some CPU platforms,
needed to save a kernel core dump between n’t directly support user-space drivers, it will can start using instructions to access I/O ports
reboots. This makes post-mortem debugging, probably let you write and debug significant or specific addresses in physical memory. In the
which would help pinpoint the source of the parts of a driver in user space. You can then latter case, the driver must ask the operating en-
system failure, nearly impossible. move the finished driver into the kernel to gain vironment to set up a mapping between a phys-
access to system services that don’t have user- ical-address region and the driver’s virtual ad-
As a further problem, kernel debuggers typical- space interfaces. But whether your driver ulti- dress space. This mapping is required since, as
ly halt the entire system while the developer mately runs in kernel space or user space, it a user-space process, the driver runs in virtual
inspects the code or data of the driver being must still do the following: manipulate hardware memory. You may also have to disable caching
debugged. Because everything must run in registers, access specific memory locations, han- in the physical-address region to ensure that the
lockstep with the debugger, the developer can dle interrupts, interact with other parts of the driver reads and writes directly to the device.
easily miss bugs that would occur in a live system. Since these attributes aren’t normally
system, where events happen asynchronously. associated with processes running in user If you’re working with a POSIX-compliant OS,
space, you’ll need to take several steps to ensure you can use a standard call, mmap(), to map
Fortunately, not all operating systems force you that the user-space driver operates correctly. address spaces. (Many non-POSIX systems
to write kernel-level drivers. The QNX Neutri- provide an equivalent call.) Most developers
no microkernel operating system, for instance, with a UNIX background are familiar with
Step 1: Gain Basic Privileges
allows every driver to run in memory-protect- mmap() as a way to map a file on disk into the
ed user space. In this architecture, a device driv- First, your user-space driver must gain appro- address space of a process. But mmap() can also
er is virtually indistinguishable from a regular priate privileges from the operating environ- map known, physical addresses into a process’s
application: it can stop and start dynamically, ment. On a POSIX or UNIX system, the driv- address space, although the manner in which
and it can access the same APIs available to reg- er will need to run as root (UID 0) and may mmap() does this isn’t defined in the POSIX
ular applications. Moreover, if a driver fails, the also need to invoke a system-specific call to gain standard. Nonetheless, most POSIX systems fol-

May 2007 26
_ _ _

DEVELOPMENT TOOLS

low common conventions, including support of user-space interrupt simulation.User-space user-space applications use signal handlers to
for a special device, /dev/mem, which repre- interrupt handling, when supported, is very handle asynchronous events.) Since the real in-
sents the entire physical address space of the specific to the OS. For instance, the QNX Neu- terrupt can’t be asserted in the driver itself, the
machine. A driver can open /dem/mem in the trino RTOS provides APIs for different inter- driver must poll the hardware to detect events
same way as a file, and then invoke mmap() to rupt-handling modes. Because there’s no stan- that would normally cause an interrupt to
bring the desired section of physical address dard method, let’s look instead at how to sim- occur.
space into the driver’s virtual address space. ulate user-space interrupts on a system that
Let’s consider a simple example, where the code doesn’t support them. This will let you debug To do this, you can set up a signal handler and
“maps in” the text-mode address space of a the driver in user space, even if it must ulti- create a timer. When the timer “times out,” the
traditional VGA card on an x86 machine. mately run as part of the kernel. Interrupt han- signal handler will be invoked. The handler will
See Code Sample 1. dlers are simply callback functions that are in- poll the hardware to see if a real interrupt has
voked asynchronously from the rest of the op- occurred or if the conditions that would cause
erating environment when the hardware being a real interrupt have occurred. Good starting
Step 3: Handle Interrupts
driven needs to be serviced or completes an op- values for polling range from 10 to 100 millisec-
If your device driver doesn’t have to deal with eration. (This process is very similar to how onds. This is just an estimate, however: if you
interrupts, you can write a production-quality
driver in user space regardless of your operat-
ing system. Graphics drivers, for instance, can
easily be implemented in user-space; the
XFree86 project represents a good example.
Nonetheless, most drivers must support inter-
rupts. Unfortunately, most OSs don’t provide
facilities to propagate interrupt events from ker-
nel space into user space. In such cases, your
user-space driver will simplify initial develop-
ment and debugging, but the final version will
have to run in the kernel to avoid the overhead

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>

/* VGA Text Mode Segment Details */


#define MY_MAP_ADDR (0xb0000)
#define MY_MAP_SIZE (1024*64)

int main( void )


{
int fd;
void *ptr;

fd = open( “/dev/mem”, O_RDWR );


if( fd == -1 )
return EXIT_FAILURE;

ptr = mmap( 0, MY_MAP_SIZE,


PROT_READ | PROT_WRITE,
MAP_SHARED,
fd, MY_MAP_ADDR );

if( ptr == MAP_FAILED )


return EXIT_FAILURE;

/* Do something to the frame buffer */

munmap( ptr, MY_MAP_SIZE );


close( fd );
return EXIT_SUCCESS;
}

Code Sample 1. Mapping a VGA text buffer

27 May 2007
_ _ _

DEVELOPMENT TOOLS

use sockets (TCP or UNIX), SystemV IPC,


#include <stdlib.h> named pipes, or shared memory. The first
#include <unistd.h> method, sockets, offers two notable benefits:
#include <signal.h> easy-to-use bidirectional communication and
#include <stdio.h> a standard, cross-platform API. Although the
#include <time.h> exact details of writing a client-server socket
application extend beyond the scope of our
static int PollCount = 0; discussion, the basic flow is simple:

void interrupt_handler( int signo ) 1) When the user-space driver starts, it creates
{ a socket, binds that socket to a known address,
/* Poll hardware and check for interrupt. Process if found. */ then waits for requests to service.
printf( “Polling Hardware: %d\n”, PollCount++ );
} 2) When an application needs to interact with
the driver, it simply opens a connection to the
int main( void ) driver’s socket and begins to pass data back and
{
forth over the connection. You can achieve the
int ret;
same results using POSIX or SystemV message
timer_t timerid;
queues or, for that matter, any other form of
struct sigevent sigev;
IPC supported by the operating environment.
struct itimerspec itime;
An OS may provide additional facilities to sim-
/* Install the virtual interrupt handler */
plify development of user-space drivers. The
signal( SIGUSR1, interrupt_handler );
QNX Neutrino RTOS, for instance, supports
driver development kits for a variety of device
/* Create the timer, and have it raise SIGUSR1 when it expires */
memset( &sigev, 0, sizeof( sigev ) ); types, including audio, character, disk, graphics,
sigev.sigev_notify = SIGEV_SIGNAL; input, networking, parallel, printer, serial, and
sigev.sigev_signo = SIGUSR1; USB. The kits include detailed documentation,
ready-to-customize source code, and a frame-
ret = timer_create( CLOCK_REALTIME, &sigev, &timerid ); work that implements all higher-level, device-
if( ret < 0 ) independent code in libraries - the only code
return EXIT_FAILURE; you have to write is the hardware-specific
code for your device. The DDKs are available in
/* Set the timer’s timeout value, 100ms */ the QNX Momentics development suite Profes-
memset( &itime, 0, sizeof( itime ) ); sional Edition, which provides a variety of di-
itime.it_value.tv_sec = 0; agnostic tools (e.g. debugger, application pro-
itime.it_value.tv_nsec = 100000000; filer, system profiler, application profiler, mem-
memcpy( &itime.it_interval, &itime.it_value, sizeof( itime.it_value ) ); ory analysis tool, code coverage tool) to help
you debug and optimize driver code.
ret = timer_settime( timerid, 0, &itime, NULL );
if( ret < 0 ) Writing drivers in user-space isn’t difficult. In
return EXIT_FAILURE; fact, it has many benefits, even if you can use
the user-space version only for initial develop-
/* Normally would be blocking for requests, simulate by simply blocking */ ment. Nonetheless, if your OS allows fully func-
while( 1 ) tional user-space drivers to run in your final
sleep( 10 ); product, you can achieve an additional benefit:
greater system availability. This availability
return EXIT_SUCCESS; comes from the ease with which a system can
} automatically restart faulty drivers, often with-
in milliseconds, without operator intervention
Code Sample 2. Simulating an interrupt by polling and without reboots. Systems can, in effect, heal
themselves of driver failures.
set the frequency too high the system will be- under /dev. When I/O operations occur on the
come overloaded, and if you set it too low you special file, generally via ioctl() calls, the kernel For many embedded systems, this self-healing
could miss events and fail to get realistic per- will route the data and requests to the driver capability makes it far easier to address
formance numbers out of your driver. Code and from the driver to the application. In OSs customer demands for constant availability of
Sample 2 shows how to set up this simulation like QNX Neutrino, where user-space drivers services. And for systems where downtime
on a POSIX system. are the norm, the OS will provide an equivalent can result in loss of income or loss of life, the
mechanism to route messages to and from the ability to recover quickly from driver failures
driver process. But in OSs where user-space can become an absolute necessity. As an added
Step 4: Handle System Interactions
drivers aren’t the norm, the user-space driver benefit, user-space drivers can be replaced
When writing kernel-space drivers, you must must rely on an existing form of interprocess dynamically with new versions, allowing a
ask the kernel to handle a special device file on communication (IPC) and wrap a custom API system to provide continuous service even
the driver’s behalf. This file normally resides on the transport. For instance, the driver could while being upgraded with new functionality. I

May 2007 28

Вам также может понравиться