Writing Portable Device Drivers
Almost all Linux kernel device drivers work on more than just one type of processor. This only happens because device-driver writers adhere to a few important rules. These rules include using the proper variable types, not relying on specific memory page sizes, being aware of endian issues with external data, setting up proper data alignment and accessing device memory locations through the proper interface. This article explains these rules, shows why it is important that they be followed and gives examples of them in use.
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:
s32 i2c_smbus_write_byte(struct i2c_client
*client, u8 value);
s32 i2c_smbus_read_byte_data(struct i2c_client
*client, u8 command);
s32 i2c_smbus_write_byte_data(struct i2c_client
*client, u8 command, u8 value);
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
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:
struct usbdevfs_ctrltransfer {
__u8 requesttype;
__u8 request;
__u16 value;
__u16 index;
__u16 length;
__u32 timeout; /* in milliseconds */
void *data;
};
#define USBDEVFS_CONTROL_IOWR('U', 0, struct
usbdevfs_ctrltransfer)
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:
static unsigned char *tmp_buf;
unsigned long page;
if (!tmp_buf) {
page = get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
if (tmp_buf)
free_page(page);
else
tmp_buf = (unsigned char *)page;
}
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.
As we saw above in the example taken from drivers/char/serial.c, you can ask the kernel for a memory page. The size of a memory page is not always 4KB of data (as it is on i386). If you are going to be referencing memory pages, you need to use the PAGE_SHIFT and PAGE_SIZE defines.
PAGE_SHIFT is the number of bits to shift one bit left to get the PAGE_SIZE value. Different architectures define this to different values. Table 1 shows a short list of some architectures and the values of PAGE_SHIFT and the resulting value for PAGE_SIZE.
Table 1. Some Architectures and the Values of PAGE_SHIFT and the Resulting Value for PAGE_SIZE
Even on the same base architecture type, you can have different page sizes. This depends sometimes on a configuration option (like IA-64) or is due to different variants of the processor type (like on ARM).
The code snippet from drivers/usb/audio.c in Listing 1 shows how PAGE_SHIFT and PAGE_SIZE are used when accessing memory directly.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Designing Electronics with Linux | May 22, 2013 |
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
- Designing Electronics with Linux
- New Products
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Dynamic DNS—an Object Lesson in Problem Solving
- Using Salt Stack and Vagrant for Drupal Development
- Validate an E-Mail Address with PHP, the Right Way
- Tech Tip: Really Simple HTTP Server with Python
- Why Python?
- Build a Skype Server for Your Home Phone System
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Reply to comment | Linux Journal
51 min 4 sec ago - Reply to comment | Linux Journal
1 hour 41 min ago - Not free anymore
5 hours 43 min ago - Great
9 hours 30 min ago - Reply to comment | Linux Journal
9 hours 38 min ago - Understanding the Linux Kernel
11 hours 53 min ago - General
14 hours 22 min ago - Kernel Problem
1 day 25 min ago - BASH script to log IPs on public web server
1 day 4 hours ago - DynDNS
1 day 8 hours ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Featured Jobs
| Linux Systems Administrator | Houston and Austin, Texas | Host Gator |
| Senior Perl Developer | Austin, Texas | Host Gator |
| Technical Support Rep | Houston and Austin, Texas | Host Gator |
| UX Designer | Austin, Texas | Host Gator |
| Web & UI Developer (JavaScript & j Query) | Austin, Texas | Host Gator |
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?



Comments
re: interact wit pci using linux
hi,
i work on linux ver 2.2, i would like to interact with the pci bus and pci devices what apis and structures should i use and in which header file should i include for its proper functioning.
thanks and regards,
sup
Why all the kernel drivers
This is a fine article on kernel drivers, but why write all these things as kernel drivers? The I2C interface (and the USB interface) both provide for access to devices from user-space. For many devices, this is all that is needed. Yes for file systems and network, etc. a kernel driver is needed, but why clutter up the kernel for every gadget that comes along?
It's a must read document
It's a must read document for beginners :)
Re: Writing Portable Device Drivers
It's really a helpful article !
Re: Writing Portable Device Drivers
Its a good artical for newbies entering into driver deveopment.
The artical is very cear and understanding.
subash
Re: Writing Portable Device Drivers
this is a good article specific to writing efficient and portable Linux device driver. It has cleared some of my confusion as well as raised a few.
I appreciate it.
nikhil bhargava
Hi,i wrote a device driver
Hi,
i wrote a device driver for USB for FC6. after writing the code and compiling it, how could i make it start when the usb is plugged in? should i also use insmod and rmmod?
thanks