Before we go on to more advanced topics, we need to stop for a quick note on portability issues. The difference between versions 1.2 and 2.0 of Linux lies in the addition of multiplatform capabilities; as a result, most source-level portability problems have been eliminated. This means that a serious Linux driver should be multiplatform as well.
But a core issue with kernel code is being able to both access data items of known length (for example, filesystem data structures or registers on device boards) and to exploit the capabilities of different processors (32-bit and 64-bit architectures, and possibly 16-bit as well).
Several problems encountered by kernel developers while porting x86 code to new architectures have been related to incorrect data typing. Adherence to strict data typing and compiling with the -Wall -Wstrict-prototypes flags can prevent most bugs.
Data types used by kernel data are divided into three main classes:
standard C types like int
, explicitly sized types like
u32
, and interface-specific types, like pid_t
. We are
going to see when and how each of the three typing classes should be
used. The final sections of the chapter talk about some other typical
problems you might run into when porting driver code from the x86 to other
platforms.
If you follow the guidelines I provide, your driver will compile and run even on platforms where you are unable to test it.
While most programmers are accustomed to freely using standard types like
int
and long
, writing device drivers requires some care
to avoid typing conflicts and obscure bugs.
The problem is that you can’t use the standard types when you need ``a
2-byte filler'' or ``something representing a 4-byte string'' because
the normal C data types are not the same size on all architectures. For
example, long
integers and pointers are a different size on
the x86 than on the Alpha, as shown by the following screen snapshots:
morgana%./datasize
system/machine: Linux i486 sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 sizeof(long) = 4 sizeof(longlong) = 8 sizeof(pointer) = 4 wolf%./datasize
system/machine: Linux alpha sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 sizeof(long) = 8 sizeof(longlong) = 8 sizeof(pointer) = 8 sandra%./datasize
system/machine: Linux sparc sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 sizeof(long) = 4 sizeof(longlong) = 8 sizeof(pointer) = 4
The datasize program is a short program available in the
files provided on the O’Reilly FTP site, in the directory
misc-progs
.
While you must be careful when mixing int
and long
,
sometimes there are good reasons to do so. One such situation is for
memory addresses, which are special as far as the kernel is concerned.
Although conceptually addresses are pointers, memory administration is
better accomplished by using an integer type; the kernel treats
physical memory like a huge array, and a memory address is just
an index into the array. Furthermore, a pointer is easily
dereferenced, and using integers for memory addresses prevents them
from being dereferenced, which is what you want. Therefore,
addresses in the kernel are unsigned long
, exploiting
the fact that pointers and long integers are always the same size,
at least on all the platforms curently supported by Linux. We’ll have to wait
and see what happens when Linux is ported to a platform breaking this
rule.
3.19.31.73