home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
BURKS 2
/
BURKS_AUG97.ISO
/
BURKS
/
LINUX
/
HOWTO
/
mini
/
ioport.txt
< prev
next >
Wrap
Text File
|
1997-07-07
|
22KB
|
490 lines
Linux I/O port programming mini-HOWTO
Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
Last modified: Mar 30 1997
This document is Copyright 1995-1997 Riku Saikkonen. See the normal Linux
HOWTO COPYRIGHT for details.
This HOWTO document describes programming hardware I/O ports and waiting for
small periods of time in user-mode Linux programs running on an Intel x86
processor. This document is a descendant of the very small IO-Port
mini-HOWTO by the same author.
If you have corrections or something to add, feel free to e-mail me
(Riku.Saikkonen@hut.fi)...
Changes from the previous version (Aug 26 1996):
Author's e-mail address changed.
Ioperm() privileges are not transferred across fork()s, as I had thought.
Added pointers (URLs) to information on quite a few topics.
Other minor changes.
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.2.1 and below), you
_have to_ compile any source code that uses these routines with optimisation
turned on (gcc -O1 or higher), or alternatively #define extern to be
empty before #including <asm/io.h>.
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 it.
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 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, it is done
automatically as the program exits.
A setuid() to a non-root user does not disable the port access granted by
ioperm(), but a fork() does.
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). 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().
Then, to actually accessing the ports... 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); (notice 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, just like
the assembler instruction INW), call inw(x);. To output a word to the two
ports, outw(value,x);. If you're unsure of which port instructions
(byte/word) to use, you probably want inb() and outb() -- most devices are
designed for bytewise port access. Note that all port 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 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.
There are man pages for ioperm(), iopl(), and the above macros in reasonably
recent releases of the Linux man-pages distribution.
An alternate method for I/O port access
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 0, file position 1 =
port 1, and so on), and read() or write() a byte or word from or to it.
Of course, for this your program needs read/write access to /dev/port. This
method is probably slower than the normal method above, but does not need
optimisation nor ioperm() (nor root access, if you give a non-root user or
group access to /dev/port).
Interrupts (IRQs) and DMA access
You cannot use IRQs or DMA directly from a user-mode program. You need to
write a kernel driver; see the Linux Kernel Hacker's Guide
(<URL:http://www.redhat.com:8080/HyperNews/get/khg.html>) for details and
the kernel source code for examples.
You also cannot disable interrupts from within a user-mode program.
High-resolution timing: Delays
First of all, I should say that you cannot guarantee user-mode processes to
have exact control of timing because of the multi-tasking, pre-emptive
nature of Linux. Your process might be scheduled out at any time for
anything from about 10 milliseconds to a few seconds (on a system with very
high load). However, for most applications using I/O ports, this does not
really matter. To minimise this, you may want to nice your process to a
high-priority value (see the nice(2) manual page).
If you want more precise timing than normal user-mode processes give you,
there are some provisions for user-mode `real time' support. Linux 2.x
kernels have soft real time support; see the man page for
sched_setscheduler(2) for details. There is a special kernel that supports
hard real time; see <URL:http://luz.cs.nmt.edu/~rtlinux/> for more
information on this.
Now, let me start with the easier timing calls. For delays of multiple
seconds, your best bet is probably to use sleep(3). For delays of at least
tens of milliseconds (about 10 ms seems to be the minimum delay), usleep(3)
should work. These functions give the CPU to other processes, so CPU time
isn't wasted. See the manual pages for details.
For delays of under about 50 milliseconds (depending on the speed of your
processor and machine, and the system load), giving up the CPU doesn't work
because the Linux scheduler usually takes at least about 10-30 milliseconds
before it returns control to your process. Due to this, in small delays,
usleep(3) usually delays somewhat more than the amount that you specify in
the parameters, and at least about 10 ms.
For short delays (tens of us to 50 ms or so), a versatile method is to
use udelay(), defined in /usr/include/asm/delay.h (linux/include/asm-i386/
delay.h). Udelay() takes the number of microseconds to delay (an unsigned
long) as its sole parameter, and returns nothing. It may take up to a few
microseconds more time than the parameter specifies because of the overhead
in the calculation of how long to wait (see delay.h for details).
To use udelay() outside of the kernel, you need to have the unsigned long
variable loops_per_sec defined with the correct value. As far as I know, the
only way to get this value from the kernel is to read /proc/cpuinfo for the
BogoMips value and multiply that by 500000 to get (an imprecise)
loops_per_sec.
In the 2.0.x series of Linux kernels, there is a ne