Linux Device Drivers (3rd Edition) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Linux Device Drivers (3rd Edition) [Electronic resources] - نسخه متنی

Jonathan Corbet, Greg Kroah-Hartman, Alessandro Rubini

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








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. I/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:

#include <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 name of your
device. The return value is non-NULL if the
allocation succeeds. If you get NULL back from
request_region, you will not be able to use the
desired ports.

All port allocations 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 driver to check to see
whether a given set of I/O ports is available:

int check_region(unsigned long 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, which performs the
required locking to ensure that the allocation is done in a safe,
atomic manner.


9.2.2. Manipulating I/O ports




After a driver has requested the range of
I/O ports it needs to use in its activities, it must read and/or
write to those ports. To this end, most hardware differentiates
between 8-bit, 16-bit, and 32-bit ports. Usually you
can't mix them like you normally do with system
memory 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 access
different size ports. As suggested in the previous section, computer
architectures that support only memory-mapped I/O registers fake port
I/O by remapping port addresses to memory addresses, and the kernel
hides the details from the driver in order to ease portability. The
Linux kernel headers (specifically, the architecture-dependent header
<asm/io.h>) define the following inline
functions to access I/O ports:

unsigned inb(unsigned port);

void outb(unsigned char byte, unsigned port);




Read
or write byte ports (eight 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 (one word wide); they are not available
when compiling for the S390 platform, which supports only byte I/O.


unsigned inl(unsigned port);

void outl(unsigned longword, unsigned port);




These
functions access 32-bit ports. longword is
declared as either unsigned long or
unsigned 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 program must be compiled with the -O option
    to force expansion of inline functions.

  • The ioperm or iopl system
    calls 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 of these functions are x86-specific.

  • The program must run as root to invoke ioperm or
    iopl.[3]
    Alternatively, one of its ancestors must have gained 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 and no
iopl system calls, user space can still access
I/O ports by using the /dev/port 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 sources misc-progs/inp.c 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 byte, word, or long ports depending on which name was
invoked by the user). They use ioperm or
iopl under x86, /dev/port
on other 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 Operations




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(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.



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 io.h 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, outb_p, and so on.
The functions are defined for most supported architectures, although
they often expand to the same code as nonpausing I/O, because there
is no need for the extra pause if the architecture runs with a
reasonably modern peripheral bus.


9.2.6. Platform Dependencies


I/O instructions are, by their
nature, highly processor dependent. Because they work with the
details of how the processor handles moving data in and out, it is
very hard to hide the differences between systems. As a consequence,
much of the source code related to port I/O is 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 unsigned 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 structural differences
in the processors and are, therefore, unavoidable. We
won't go into detail about the differences, because
we assume that you won't be writing a device driver
for a particular system without understanding the underlying
hardware. Instead, here is an overview of the capabilities of the
architectures supported by the kernel:

IA-32 (x86)

x86_64


The architecture supports all the
functions described in this chapter. Port numbers are of type
unsigned short.


IA-64 (Itanium)


All functions are supported; ports are
unsigned 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
unsigned long.


ARM


Ports are memory-mapped, and all functions
are supported; string functions are implemented in C. Ports are of
type unsigned int.


Cris


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 unsigned char
*
.


MIPS

MIPS64


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-RISC


All of the functions are supported; ports are int
on PCI-based systems and unsigned short on EISA
systems, except for string operations, which use unsigned
long
port numbers.


PowerPC

PowerPC64



All the functions are supported; ports
have type unsigned char * on 32-bit systems and
unsigned long on 64-bit systems.


S390


Similar to the M68k, the header for this
platform supports only byte-wide port I/O with no string operations.
Ports are char pointers and are memory-mapped.


Super-H



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 unsigned 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's interesting to note that no processor outside
the x86 family features a different address space for ports, even
though several of the supported families are shipped with ISA and/or
PCI slots (and both buses implement separate I/O and memory address
spaces).

Moreover, some processors (most notably the early Alphas) lack
instructions that move one or two bytes at a time.[4] Therefore,
their peripheral chipsets simulate 8-bit and 16-bit I/O accesses by
mapping them to special address ranges in the memory address space.
Thus, 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 rare operation. To read/write a single byte to any address
space, you need to implement a data path connecting the low bits of
the register-set data bus to any byte position in the external data
bus. These data paths require additional logic gates that get in the
way of every data transfer. Dropping byte-wide loads and stores can
benefit overall system performance.


How I/O operations are performed on each platform is well described
in the programmer's manual for each platform; those
manuals are usually available for download as PDFs on the Web.


    / 202