6.1 Considerations for Porting Device Drivers

The porting of device drivers between platforms can be complicated for various reasons:

  • Even if the kernel data structures have the same name, additional members might provide enhanced functionality, some members might be missing, or the type or size of members might be different.

  • Even if a kernel function has the same name, the calling syntax or return syntax might be different or there might be different prerequisites or side effects when calling the function.

  • Drivers might not use existing kernel functions, but instead rely on undocumented side effects that are unique to the operating system.

  • Architectural differences, such as word size and the size of data types such as short, int, and long can result in porting issues. The sizes of an address pointer and int are also not necessarily the same. Opaque data types such as dev_t and atomic_t might also not be the same size on the source and target systems so it is not safe to convert them to a non-opaque data type. The default signedness of char can also vary between platforms.

  • Some architectures permit the use of unaligned data while other do not. For portability, all types should be naturally aligned on memory addresses that are multiples of their size.

  • Structure padding might differ on source and target systems, which can cause porting issues if the code makes assumptions about how a structure is padded.

  • The endianness of the system architecture, as defined in <asm/byteorder.h> on Linux systems, can cause porting problems if data structures are passed between systems, or if assumptions are made about the default byte order when processing data.

  • The number of timer interrupts per second and the internal representation of system time can vary between platforms. You should use the value of HZ (the number of timer interrupts per second, defined via CONFIG_HZ in <config/auto.conf>) to scale time units. For example, the time in seconds from booting the system can be found by dividing the value of the global variable jiffies by HZ.

  • The memory page size can differ between platforms, and some architectures can support more than one page size.

  • Different architectures can have different process ordering rules for controlling the order in which memory reads and writes happen. You can use the rmb(), wmb(), and mb() methods to implement barriers on reordering of memory reads and writes.

  • Code that was not written for an SMP system must be modified to use locking where appropriate to protect critical sections where shared data is updated or to protect against kernel preemption if it is to run on an SMP system. The methods that are provided for locking might also differ between platforms and there might be different constraints on when certain types of locking can or should be used with driver methods and helper routines.

  • When mapping and unmapping page structures to kernel address space, the kmap() and kunmap() functions should be used to allow for systems where high memory is not permanently mapped.

  • Architecture-specific code might be scattered throughout the driver. Isolating the portable code from non-portable code can help reduce porting issues.