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 in all versions I know of, including egcs), you
have to
compile any source code that uses these routines with optimisation turned on (
gcc -O1
or higher), or alternatively use
#define extern static
before you
#include <asm/io.h>
(remember to
#undef extern
afterwards).
For debugging, you can use
gcc -g -O
(at least with modern versions of gcc), though optimisation can sometimes make the debugger behave a bit strangely. If this bothers you, put the routines that use I/O port access in a separate source file and compile only that with optimisation turned on.
Before you access any ports, you must give your program permission to do so. This is done by calling the
ioperm()
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 can 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 the root user, or make it setuid root. You can drop the root privileges after you have called
ioperm()
to enable the 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; this is done automatically as the process exits.
A
setuid()
to a non-root user does not disable the port access granted by
ioperm()
, but a
fork()
does (the child process does not get access, but the parent retains it).
ioperm()
can only give access to ports 0x000 through 0x3ff; for higher ports, you need to use
iopl()
(which gives you access to all ports at once). Use the level argument 3 (i.e.,
iopl(3)
) to give your program access to
all
I/O ports (so be careful --- accessing the wrong ports can do all sorts of nasty things to your computer). Again, you need root privileges to call
iopl()
. See the
iopl(2)
manual page for details.
To input a byte (8 bits) from a port, call
inb(port)
, it returns the byte it got. To output a byte, call
outb(value, port)
(please note the order of the parameters). To input a word (16 bits) from ports
x
and
x+1
(one byte from each to form the word, using the assembler instruction
inw
), call
inw(x)
. To output a word to the two ports, use
outw(value, x)
. If you're unsure of which port instructions (byte or word) to use, you probably want
inb()
and
outb()
--- most devices are designed for bytewise port access. Note that all port access instructions take at least about a microsecond to execute.
The
inb_p()
,
outb_p()
,
inw_p()
, and
outw_p()
macros work otherwise identically to the ones above, but they do an additional short (about one microsecond) delay after the port access; you can make the delay about four microseconds with
#define REALLY_SLOW_IO
before you
#include <asm/io.h>
. These macros normally (unless you
#define SLOW_IO_BY_JUMPING
, which is probably less 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.
There are manual pages for
ioperm(2)
,
iopl(2)
, and the above macros in reasonably recent releases of the Linux manual page collection.
/dev/port
Another way to access I/O ports is to
open()
/dev/port
(a character device, major number 1, minor 4) for reading and/or writing (the stdio
f*()
functions have internal buffering, so avoid them). Then
lseek()
to the appropriate byte in the file (file position 0 = port 0x00, file position 1 = port 0x01, and so on), and
read()
or
write()
a byte or word from or to it.
Naturally, for this to work your program needs read/write access to
/dev/port
. This method is probably slower than the normal method above, but does not need compiler optimisation nor
ioperm()
. It doesn't need root access either, if you give a non-root user or group access to
/dev/port
--- but this is a very bad thing to do in terms of system security, since it is possible to hurt the system, perhaps even gain root access, by using
/dev/port
to access hard disks, network cards, etc. directly.
You cannot use
select(2)
or
poll(2)
to read /dev/port, because the hardware does not have a facility for notifying the CPU when a value in an input port changes.