Part of the WritingPortableDrivers section
One of the most basic rules to remember when writing portable code is to be aware of how big you need to make your variables. Different processors define different variable sizes for int and long data types. They also differ in specifying whether a variable size is signed or unsigned. Because of this, if you know your variable size has to be a specific number of bits, and it has to be signed or unsigned, then you need to use the built-in data types. The following typedefs can be used anywhere in kernel code and are defined in the linux/types.h header file:
u8 unsigned byte (8 bits) u16 unsigned word (16 bits) u32 unsigned 32-bit value u64 unsigned 64-bit value s8 signed byte (8 bits) s16 signed word (16 bits) s32 signed 32-bit value s64 signed 64-bit value
For example, the i2c driver subsystem has a number of functions that are used to send and receive data on the i2c bus:
All of these functions return a signed 32-bit value and take an unsigned 8-bit value for either a value or command parameter. Because these data types are used, this code is portable to any processor type.
If your variables are going to be used in any code that can be seen by user-space programs, then you need to use the following exportable data types. Examples of this are data structures that get passed through ioctl() calls. Once again they are defined in the linux/types.h header file:
__u8 unsigned byte (8 bits) __u16 unsigned word (16 bits) __u32 unsigned 32-bit value __u64 unsigned 64-bit value __s8 signed byte (8 bits) __s16 signed word (16 bits) __s32 signed 32-bit value __s64 signed 64-bit value
FIXME add stuff for __le16 and friends
For example, the usbdevice_fs.h header file defines a number of different structures that are used to talk to USB devices directly from user-space programs. Here is the definition of the ioctl that is used to send a USB control message to the device:
One thing that has caused a lot of problems, as 64-bit machines are getting more popular, is the fact that the size of a pointer is not the same as the size of an unsigned integer. The size of a pointer is equal to the size of an unsigned long. This can be seen in the prototype for get_zeroed_page():
extern unsigned long FASTCALL (get_zeroed_page(unsigned int gfp_mask))
get_zeroed_page() returns a free memory page that has already been wiped clean with zeros. It returns an unsigned long that should be cast to the specific data type that you need. The following code snippet from the drivers/char/serial.c file in the rs_open() function shows how this is done:
FIXME this is an old example from the 2.4 days
There are some native kernel data types that you should use instead of trying to use an unsigned long. Some of these are: pid_t, key_t, gid_t, size_t, ssize_t, ptrdiff_t, time_t, clock_t, and caddr_t. If you need to use any of these types in your code, please use the given data types; it will prevent a lot of problems.