สิงหาคม 16, 2010 ใส่ความเห็น
If you want to learn more about I/O port programming I recommend to read Linux I/O port programming mini-HOWTO. Here are few tips from that document:
- 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 ; you do not need any additional libraries.
- Because of a limitation in gcc, 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 (remember to #undef externafterwards).
- 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.
- Sometimes it is a good idea to put all 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. ioperm() can only give access to ports 0x000 through 0x3ff (or higher ports, you need to use iopl(3) which gives you access to all ports at once).
- 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 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.
- You can drop the root privileges after you have called ioperm() to enable the ports you want to use. 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).
- 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)
- Note that all port access instructions take at least about a microsecond to execute.
- There are manual pages for ioperm(2), iopl(2), and the above macros in reasonably recent releases of the Linux manual page collection
- 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 (potentially dangerous for system security)
Be warned that this I/O port accessing as decribed above will only work on i386 system. To be able to use ioperm you need to include the ncessary headers to your software:
#include /* for libc5 */ #include /* for glibc */
The function protype is the following:
int ioperm(unsigned long from, unsigned long num, int turn_on);
Ioperm sets the port access permission bits for the process for num bytes starting from port address from to the value turn_on. The use of ioperm requires root privileges. Only the first 0x3ff I/O ports can be specified in this manner. For more ports, the iopl function must be used. Permissions are not inherited on fork, but on exec they are. This is useful for giving port access permissions to non-privileged tasks. This call is mostly for the i386 architecture. On many other architectures it does not exist or will always return an error. On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
ioperm is Linux specific and should not be used in programs intended to be portable. Libc5 treats it as a system call and has a prototype in <unistd.h>. Glibc1 does not have a prototype. Glibc2 has a prototype both in <sys/io.h> and in <sys/perm.h> (avoid this latter bacause it is available on i386 only).
The I/O accessing can be different on other Linux platforms (for example the alpha uses a library, libio, to emulate inb/outb in user programs).
The description above concentrates on the C programming language. It should apply directly to C++ as well. In assembler, you have to call ioperm() or iopl() as in C, but after that you can use the I/O port read/write instructions directly.
In other languages, unless you can insert inline assembler or C code into the program or use the system calls mentioned above, it is probably easiest to write a simple C source file with functions for the I/O port accesses or delays that you need, and compile and link it in with the rest of your program. Or use /dev/port as described above.