Chapter 8. Hardware Management

While playing with scull and similar toys can be a pleasant way to become familiar with the software interface of a Linux device driver, testing a real device requires hardware. The driver is the abstraction layer between software concepts and hardware circuitry; as such, it needs to talk with both of them. Up to now, we have examined the internals of software concepts; this chapter should complete the picture by showing you how a driver can access I/O ports and I/O memory, while being portable across Linux platforms.

As usual, I won’t bind the sample code to a particular device. However, we can no longer use a memory-based device like scull. Instead, the examples in this chapter use the parallel port to show I/O instructions and the standard video buffer of text-mode VGA boards to show memory-mapped I/O.

I chose the parallel port because it offers direct input and output of several bits of information. Data bits written to the device appear on the output pins, and voltage levels on the input pins are directly accessible by the processor. In practice, you have to connect LEDs to the port to actually see the results of an I/O operation. The parallel port is easy to program, much easier than the serial port, and almost every computer (even the Alpha) has a parallel port that works like the one in the PC.

As far as memory-mapped I/O is concerned, text-mode VGA is the most standardized memory-mapped device, and almost every computer has a VGA-compatible text mode. Unfortunately, not every Alpha has a VGA video adapter, and the Sparc definitely doesn’t, so our VGA-related code won’t be as portable as the parallel port example. Also, you’ll have to switch the computer to text mode in order to run the sample code, which shouldn’t be a serious constraint. The biggest problem with experimenting using VGA memory is that the sample driver will unavoidably trash the foreground virtual console.

Using I/O Ports

In some sense, I/O ports are like memory locations: they can be read and written by means of the same electrical signals that memory chips receive. But they are not exactly the same; port operations talk directly to peripheral devices, which are often less flexible than RAM. In particular, there are 8-bit ports, 16-bit ports, and 32-bit ports, and you can’t mix them.[23]

A C program, therefore, must call different functions to access different size ports. The Linux kernel headers (specifically, the architecture-dependent header <asm/io.h>) define the inline functions listed below.

Note

From now on, when I use unsigned without further type specifications, I am referring to an architecture-dependent definition whose exact nature is not relevant. The functions are almost portable because the compiler automatically casts the values during assignment--their being unsigned helps prevent compilation-time warnings. No information is lost with such casts as long as the programmer assigns sensible values to avoid overflow. I’ll stick to this convention of ``incomplete typing'' for the rest of the chapter.

unsigned inb(unsigned port); , void outb(unsigned char byte, unsigned port);

Read or write byte ports (8 bits wide). The 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 (``word-wide''), they are not available in the M68k version of Linux because the processor supports byte I/O, but neither word nor long operations.

unsigned inl(unsigned port); , void outl(unsigned doubleword, unsigned port);

These functions access 32-bit ports. doubleword is either declared as unsigned long or unsigned int, according to the platform.

In addition to the single-shot in and out operations, most processors implement special instructions to transfer a sequence of bytes, words, or longs to and from a single I/O port. These are the so-called ``string instructions,'' which are introduced in Section 8.1.3, later in this chapter.

Note that no 64-bit I/O operations are defined. Even on 64-bit architectures, I/O ports only use a 32-bit data path.

The functions described above are primarily meant to be used by device drivers, but they can also be used from user space (the preprocessor definitions or inline declarations are not protected by #ifdef __KERNEL__). The following conditions should, however, apply in order for inb and friends to be used in user-space code:

  • The program must be compiled with the -O option to force expansion of inline functions.

  • ioperm or iopl must be used to get permission to perform I/O operations on ports. ioperm gets permission for individual ports, while iopl gets permission for the entire I/O space. Both these functions are Intel-specific.

  • The program must run as root to invoke ioperm. Alternatively, one of its ancestors must have gained port access running as root.

The sample sources misc-progs/inp.c and misc-progs/outp.c are a minimal tool for reading and writing 8-bit ports from the command line, in user space. I have run them successfully on my PC. They don’t run on other plaftorms due to the missing ioperm capability. The programs can be made set-uid, if you want to live dangerously and play with your hardware without acquiring explicit privileges.

Platform Dependencies

If you are looking for porting problems, you’ll find that I/O instructions are the most processor-dependent of all computer instructions. As a consequence, much of the source code related to port I/O is platform-dependent.

The Linux system, though portable, isn’t completely transparent to processor peculiarities. Most hardware devices are not portable across platforms, and driver writers don’t generally address more than two or three architectures in the same module.

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 unsigned short on the x86 (where the processor supports a 64KB-byte I/O space), but unsigned long on the Alpha, whose ports are just special locations in the same address space as memory; the Alpha, by design, has no I/O address space, and its ports are folded to non-cacheable memory addresses.

I/O typing is one part of the kernel that still needs some cleaning up, although things work correctly now. The best solution to ambiguous typing would be to define an architecture-specific port_t type and use u8, u16, and u32 for the data items (see Section 10.2 in Chapter 10). Nobody has taken care of the problem yet, however, as the issue is mostly cosmetic.

Other platform dependencies arise from basic structural differences in the processors and thus are unavoidable. I won’t go into detail about the differences, because I assume that you won’t be writing a device driver for a particular system without understanding the underlying hardware. Instead, the following is an overview of the capabilities of the supported architectures:

x86

The architecture supports all the functions described in this chapter.

Alpha

All the functions are supported, but there are differences in the implementation of port I/O for different Alpha platforms. String functions are implemented in C and defined in arch/alpha/lib/io.c. Unfortunately, only word and long string operations are exported in 2.0 kernels till 2.0.29; therefore, insb and outsb are not available to modules. This problem has been fixed in version 2.0.30 and 2.1.3.

Sparc

The Sparc doesn’t have special I/O instructions. I/O space is memory-mapped and is marked by flags in the page-table entry. The header defines empty functions for inb and the other functions to prevent the compiler from complaining when first porting drivers to the Sparc architecture.

M68k

Only inb, outb, and their pausing counterparts (see below) are supported. No string functions are defined for the 68000, nor are readb, writeb, and friends defined.

Mips

The Mips port supports all the functions. String operations are implemented with tight assembly loops, as the processor lacks machine-level string I/O.

PowerPC

All the functions except string I/O are supported.

The curious reader can extract more information from the io.h files, which sometimes define a few architecture-specific functions in addition to those I describe in this chapter.

It’s interesting to note that the Alpha processors don’t feature a different address space for ports, even though AXP computers are often shipped with ISA and PCI slots, and both buses feature signal lines to differentiate memory and I/O operations. Alpha-based PCs implement the Intel-compatible I/O abstraction through specific interface chips that translate references to particular memory addresses into I/O port access.

I/O operations on the Alpha are well described in the ``Alpha Reference Manual,'' which is available free from Digital Equipment Corporation. The manual thoroughly describes the I/O issue and tells how the AXP processors divide virtual addresses into ``memory-like'' and ``non-memory-like'' regions; the latter are used for memory-mapped I/O.

Pausing I/O

Some platforms--most notably the i386--can have problems when the processor tries to transfer data too quickly to or from the bus. The problems can arise because the processor is over-clocked with respect to the ISA bus, and can show up when the device board is too slow; the solution is to insert a small delay after an I/O instruction if another such instruction follows. 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 above, but their names end in _p; they are called inb_p, outb_p, and so on. For all the supported architectures, when the non-pausing function is defined, the pausing equivalent is defined as well, even in cases where they expand to the same code.

If you want to explicitly insert a small pause in your driver (smaller than you’d get with udelay), you can use the explicit SLOW_DOWN_IO statement. This macro expands to instructions that do nothing except delay execution. You might want to insert the statement at critical points in your source. SLOW_DOWN_IO actually executes the same code as that added to outb when outb_p is expanded.

The definition of SLOW_DOWN_IO (and thus the _p pause) depends on whether SLOW_IO_BY_JUMPING and/or REALLY_SLOW_IO are defined before <asm/io.h> is included. Fortunately, new hardware doesn’t require the programmer to deal with these questions, so I won’t talk about pausing any more. The interested reader is urged to browse <asm/io.h>. As a driver writer, you should nonetheless remember that SLOW_DOWN_IO is undefined for the Sparc and M68k architectures (though pausing calls like outb_p are defined, with the limitations outlined in Section 8.1.1, earlier in this chapter).

String Operations

The Linux headers define functions to perform string operations, which can be used by some drivers to get better performance than a C-language loop. The Linux implementation for string I/O maps either to a single machine instruction or to a tight loop, or it is missing altogether, depending on the capabilities of the target processor or platform.

The prototypes for string functions are the following:

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(unsigned port, void *addr, unsigned long count); , void outsw(unsigned port, void *addr, unsigned long 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.



[23] As a matter of fact, 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 in general you can’t count on this feature.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.137.164