next up previous contents
Next: Troubleshooting Up: Linux I/O port programming Previous: Contents

I/O ports in C programs, the normal way

Routines for accessing I/O ports are in /usr/include/asm/io.h (or linux/include/asm-i386/io.h in the kernel source distribution). The routines there are inline macros, so it is enough to #include <asm/io.h>; you do not need any additional libraries.

Because of a limitation in gcc (present at least in 2.7.0 and below), you compile any source code using these routines with optimization turned on (i.e. gcc -O). Because of another limitation in gcc, you cannot compile with both optimization and debugging (-g). This means that if you want to use gdb on programs using I/O ports, it might be a good idea to put the I/O port-using routines in a separate source file, and, when you debug, compile that source file with optimization and the rest with debugging.

Before you access any ports, you must give your program permission to do that. This is done by calling the ioperm(2) function (declared in unistd.h, and defined in the kernel) somewhere near the start of your program (before any I/O port accesses). The syntax is ioperm(from,num,turn_on), where from is the first port number to give access to, and num the number of consecutive ports to give access to. For example, ioperm(0x300,5,1); would give access to ports 0x300 through 0x304 (a total of 5 ports). The last argument is a Boolean value specifying whether to give access to the program to the ports (true (1)) or to remove access (false (0)). You may call ioperm() multiple times to enable multiple non-consecutive ports. See the ioperm(2) manual page for details on the syntax.

The ioperm() call requires your program to have root privileges; thus you need to either run it as user root, or make it setuid root. You should be able to (I haven't tested this; please e-mail me if you have) drop the root privileges after you have called ioperm() to enable any ports you want to use. You are not required to explicitly drop your port access privileges with ioperm(...,0); at the end of your program, it is done automatically.

Ioperm() priviledges are transferred across fork()s and exec()s, and across a setuid to a non-root user.

Ioperm() can only give access to ports 0x000 through 0x3ff; for higher ports, you need to use iopl(2) (which gives you access to all ports at once); I have not done this, so see the manual page for details. I suspect the level argument 3 will be needed to enable the port access. Please e-mail me if you have details on this.

Then, to actually accessing the ports... To input a byte from a port, call inb(port);, it returns the byte it got. To output a byte, call outb(value, port); (notice the order of the parameters). To input a word from ports x and x+1 (one byte from each to form the word, just like the assembler instruction INW), call inw(x);. To output a word to the two ports, outw(value,x);.

The inb_p(), outb_p(), inw_p(), and outw_p() macros work otherwise identically to the ones above, but they do a short (about one microsecond) delay after the port access; you can make the delay four microseconds by #defining
REALLY_SLOW_IO before including asm/io.h. These macros normally (unless you #define SLOW_IO_BY_JUMPING, which probably isn't accurate) use a port output to port 0x80 for their delay, so you need to give access to port 0x80 with ioperm() first (outputs to port 0x80 should not affect any part of the system). For more versatile methods of delaying, read on.

Man pages for these macros are forthcoming in a future release of Linux man-pages.

next up previous contents
Next: Troubleshooting Up: Linux I/O port programming Previous: Contents

John Purcell
Content-type: text/plain Sun Jun 9 20:27:59 EDT 1996