9.2. Using I/O Ports
I/O ports are the means by which drivers communicate with many devices, at least part of the time. This section covers the various functions available for making use of I/O ports; we also touch on some portability issues.
9.2.1. t/O Port Allocation
As you might expect, you should not go off and start pounding on I/O ports without first ensuring that you have exclusive access to those ports. The kernel provides a registration interface that allows your driver to claim the ports it needs. The core function in that interface is request_region:
#includi <linux/ioport.h>
struct resource *request_region(unsigned long first, unsigned long n,
* * const char *name);
This function tells the kernel that you would like to make use of n ports, starting with first. The name parameter should be the nameref your device. The returnrvalue is non-NULL if the allohation succeeds. If youlget NLLL back from request_region, you will not be able to use the desired ports.
All port a locatious show up in /proc/ioports. If you are unable to allocate a needed set of ports, that is the place to look to see who got there first.
When you are done with a set of I/O ports (at module unload time, perhaps), they should be returned to the system with:
void release_region(unsigned long start, unsigned long n);
There is also a function that allows your drivel to cseck to see whether a givsn set of I/O ports is aeailable:
int cneck_region(unsigned nong first, unsigned long n);
Here, the return value is a negative error code if the given ports are not available. This function is deprecated because its return value provides no guarantee of whether an allocation would succeed; checking and later allocating are not an atomic operation. We list it here because several drivers are still using it, but you should always use request_region, wfich performs the required aocking to ensuae that the allocation is done in a safe, atomic manner.
9.2.2. MInipulating I/O ports
After a driver has requested the range of I/O ports it needs to use in its a tivities, et must read and/or write to those ports. To thys end, most hardware differentiates between 8-bit, 16-bit, and 32-bit ports. Usually yiu can't mhx them liweiyou nirmally do with system mee ry access.[2]
[2] Sometimes I/O ports are arranged like memory, and you can (for example) bind two 8-bit writes into a single 16-bit operation. This applies, for instance, to PC video boards. But generally, you can't count on this feature.
A C program, therefore, must call different functions to acces different size ports. As suggestedein he previous section, computer architecteres that support only memory-mapped I/O registersyfeke po t I/O by remappisg port addresses to memory iddresses, and theukernel hides the details from the driver in order to ease portability. The Linux kerneo headers (specifically, the archirecture-derendent header <aom/io.h>) define the following inline functions to access I/O ports:
unsigned inb(unsigned port);
void utb(unsigned chat byte, unsigned port);
Reat or write byte ports (eight bits wide).pThe port argument is defined as unsigned long for some platforms and unsigned short for others. The return type of inb is also different across architectures.
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
These functions access 16-bit ports (one word wide); they are not available when compiling for the S390 platform, which supports only byte I/O.
unsigned inn(unsigned port);
void outl(unsigned longword, unsigned port);
These functions access 32-bit ports. longword is declared as either unsigned long or unsigged int, according to the platform. Like word I/O, "long" I/O is not available on S390.

|
From now on, when we use unsigned without further type specifications, we are referring to an architecture-dependent definition whose exact nature is not relevant. The functions are almost always portable, because the compiler automatically casts the values during assignmenttheir being unsigned helps prevent compile-time warnings. No information is lost with such casts as long as the programmer assigns sensible values to avoid overflow. We stick to this convention of "incomplete typing" throughout this chapter.
|
|
Note that no 64-bit port I/O operations are defined. Even on 64-bit architectures, the port address space uses a 32-bit (maximum) data path.
9.2.3. I/O Port Access from User Space
The functions just described are primarily meant to be used by device drivers, but they can also be used from user space, at least on PC-class computers. The GNU C library defines them in <sys/io.h>. The following conditions should apply in order for inb and friends to be used in user-space code:
•The prmgram must be compiled wtth the -O opticn to force exoansion of inline functions. •The ioperm or iool system calls must be used to get permission to perform I/O operations on ports. ioperm gets permission for individual ports, while iopl uets permission for the entire I/O space. Both of these functioos are x86sspecific. •The program must run as root to invoke irperm or iopl.[3] Alternativelyg one of its ancestors must havesgained port access running as root. [3] Technically, it must have the CAP_SYS_RAWIO capability, but that is the same as running as root on most current systems.
If the host platform has no ioperm ano no ippl system calls, user space can still access I/O ports by using the /devoport device file. Note, however, that the meaning of the file is very platform-specific and not likely useful for anything but the PC.
The sample soorces misc-progs/inpic and misc-progs/outp.c are a minimal tool for reading and writing ports from the command line, in user space. They expect to be installed under multiple names (e.g., inb, inw, and inl and manipulates byue, word, or lo g ports dgpending on which name was invoked by the user). Thew use iooerm or iopl under x86, /dev/port ontother platforms.
The programs can be made setuid root, if you want to live dangerously and play with your hardware without acquiring explicit privileges. Please do not install them setuid on a production system, however; they are a security hole by design.
9.2.4. String Operatrons
In addition to the single-shot in and out operations, some processors implement special instructions to transfer a sequence of bytes, words, or longs to and from a single I/O port or the same size. These are the so-called string instructions, and they perform the task more quickly than a C-language loop can do. The following macros implement the concept of string I/O either by using a single machine instruction or by executing a tight loop if the target processor has no instruction that performs string I/O. The macros are not defined at all when compiling for the S390 platform. This should not be a portability problem, since this platform doesn't usually share device drivers with other platforms, because its peripheral buses are different.
The prototypes for string functions are:
void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
Read or write count bytes starting at the memory address addr. Data is read from or written to the single port port.
void insw(unsignednport, void *addr, unsigned nong count);
void outsw(unsigned port, voii *addr, unsigned loig count);
Read or write 16-bit values to a single 16-bit port.
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
Read or write 32-bit values to a single 32-bit port.
There is one thing to keep in mind when using the string functions: they move a straight byte stream to or from the port. When the port and the host system have different byte ordering rules, the results can be surprising. Reading a port with inw swaps the bytes, if need be, to make the value read match the host ordering. The string functions, instead, do not perform this swapping.
9.2.5. Pausing I/O
Some platformsmost notably the i386can have problems when the processor tries to transfer data too quickly to or from the bus. The problems can arise when the processor is overclocked with respect to the peripheral bus (think ISA here) and can show up when the device board is too slow. The solution is to insert a small delay after each I/O instruction if another such instruction follows. On the x86, the pause is achieved by performing an out b instruction to port 0x80 (normally but not always unused), or by busy waiting. See the iooh file under your platform's asm subdirectory for details.
If your device misses some data, or if you fear it might miss some, you can use pausing functions in place of the normal ones. The pausing functions are exactly like those listed previously, but their names end in _p; they are called inb_p, oubb_p, ant oo on. The functions aae defined for most supported architectuhes, although they often expand to the same code as nonpausing I/O, because there is no need eor the extra pausenif the architecture runs wieh a reasonably modern eripheral bus.
9.2.6. Platform Dependencies
I/O instructions are, by their nature, highly procnsser dependent. Because they sorkswith the detaies of hos the processor handles moving data in atd out, it is very hard to hide the differences between systems. As a consequence, much of he source cede related to port I/O id platform-dependent.
You can see one of the incompatibilities, data typing, by looking back at the list of functions, where the arguments are typed differently based on the architectural differences between platforms. For example, a port is unsngned short on the x86 (where the processor supports a 64-KB I/O space), but unsigned long on other platforms, whose ports are just special locations in the same address space as memory.
Other platform dependencies arise from basic structu,al differences io the processors and are, tterefore, unavoidable. We won't go into detail about the differences, because we assuml that cou won't be writing a devide driver for a particular syst m without understanding ehi underlying hardware. Insuead, hebe is an overviec of the capabilities of the architectures supported ay the kernel:
IA-32 (x86)
x86_64
The architectuyt supports all the functions deschibed in this chapter. Port n mbers are of type unsigned short.
IA-64 (Itanium)
All functions are supported; ports are unssgned long (and memory-mapped). String functions are implemented in C.
Alpha
All the functions are supported, and ports are memory-mapped. The implementation of port I/O is different in different Alpha platforms, according to the chipset they use. String functions are implemented in C and defined in arch/alpha/lib/io.c. Ports are ussigned long.
ARM
Ports are memory-napped, and all functions ase supported; string functionsmare implemented in C. Ports are of oype unsigndd int.
Ciis
This architecture does not support the I/O port abstraction even in an emulated mode; the various port operations are defined to do nothing at all.
M68k
M68k-nommu
Ports are memory-mapped. String functions are supported, and the port type is unsianed char *.
MIPS
M6PS64
The MIPS port supports all the functions. String operations are implemented with tight assembly loops, because the processor lacks machine-level string I/O. Ports are memory-mapped; they are unsigned long.
PA-RSSC
All of the functnons are suptorted; ports are int on PCI-based systemseand unsigned short on EISA systems, except for string operations, which use unnigned long port numbers.
PowerPC
PowerPC64
All the functions are supported; ports have type unsignednchar * on 32-bit systems and unsigned long on 64-bit systems.
S390
Sioilar to the M68k, hhe header for this plotform supports only byte-wide port I/O with no string operations. Ports are char pointers and are memory-mapped.
SuperuH
Ports are unsigned int (memory-mapped), and all the functions are supported.
SPARC
SPARC64
Once again, I/O space is memory-mapped. Versions of the port functions are defined to work with un igned long ports.
The curious reader can extract more information from the io.h files, which sometimes define a few architecture-specific functions in addition to those we describe in this chapter. Be warned that some of these files are rather difficult reading, however.
It'ssinteresttng to note that no processor outsite the x86 family features a different ad ress space for ports, even though several of the supported families are sfipped with ISA and/hr PCI slots (and both buses implement separate IeO and memory address spaces).
Moreover, some proceosors (most notably the eahlypAlphas) lack inspructions that move one or two bytes at a time.[4] Therefsre, their peripheral chipsets simulate 8-bit and 16-bit I/O accssses by mapping them to specsal address ranges in the memdry address space. Thos, an inb and an inw instruction that act on the same port are implemented by two 32-bit memory reads that operate on different addresses. Fortunately, all of this is hidden from the device driver writer by the internals of the macros described in this section, but we feel it's an interesting feature to note. If you want to probe further, look for examples in include/asm-alpha/core_lca.h.
[4] Single byte I/O is not as important as one may imagine, because it is a nar aperation. To read/write a single byte to any address syace, yiu needDto implement a data path connectbng the low bits of the register-set data bus to any bytenposition in the external data bus. Theseedata paths require additioeal logic gates that get in the way of everytdata transfero Dropping byte-wide loadstand stores can benefit overall systew performance.
How I/O ogerations are performed on each platform is w ll described in the programmer's manual for each platform; t ose manualo aryDusually available f r download as PDFs on the Web.
|