Writing Portable Device Drivers
Processors store internal data in one of two ways: little-endian or big-endian. Little-endian processors store data with the right-most bytes (those with a higher address value) being the most significant, while big-endian processors store data with the left-most bytes (those with a lower address value) being the most significant.
For example, Table 2 shows how the decimal value 684686 is stored in a 4-byte integer on the two different processor types (684686 decimal = a72be hex = 00000000 00001010 01110010 10001110 binary).
Table 2. How the Decimal Value 684686 is Stored in a 4-Byte Integer
Intel processors, for example the i386 and IA-64 series, are little-endian machines, whereas the SPARC processors are big-endian. The PowerPC processors can be run in either little- or big-endian mode, but for Linux, they are defined as running in big-endian mode. The ARM processor can be either, depending on the specific ARM chip being used, but usually it also runs in big-endian mode.
Because of the different endian types of processors, you need to be aware of data you receive from external sources and the order in which it appears. For example, the USB specification dictates that all multibyte data fields are in little-endian form. So if you have a USB driver that reads a multibyte field from the USB connection, you need to convert that data into the processor's native format. Code that assumes the processor is little-endian could ignore the data format coming from the USB connection successfully. But this same code would not work on PowerPC or ARM processors and is the leading cause of drivers that are broken on different platforms.
Thankfully, there are a number of helpful macros that have been created to make this an easy task. All of the following macros can be found in the asm/byteorder.h header file.
To convert from the processor's native format into little-endian form you can use the following functions:
u64 cpu_to_le64 (u64); u32 cpu_to_le32 (u32); u16 cpu_to_le16 (u16);
To convert from little-endian format into the processor's native format you should use these functions:
u64 le64_to_cpu (u64); u32 le32_to_cpu (u32); u16 le16_to_cpu (u16);For big-endian forms, the following functions are available:
u64 cpu_to_be64 (u64); u32 cpu_to_be32 (u32); u16 cpu_to_be16 (u16); u64 be64_to_cpu (u64); u32 be32_to_cpu (u32); u16 be16_to_cpu (u16);If you have a pointer to the value to convert, then you should use the following functions:
u64 cpu_to_le64p (u64 *); u32 cpu_to_le32p (u32 *); u16 cpu_to_le16p (u16 *); u64 le64_to_cpup (u64 *); u32 le32_to_cpup (u32 *); u16 le16_to_cpup (u16 *); u64 cpu_to_be64p (u64 *); u32 cpu_to_be32p (u32 *); u16 cpu_to_be16p (u16 *); u64 be64_to_cpup (u64 *); u32 be32_to_cpup (u32 *); u16 be16_to_cpup (u16 *);If you want to convert the value within a variable and store the modified value in the same variable (in situ), then you should use the following functions:
void cpu_to_le64s (u64 *); void cpu_to_le32s (u32 *); void cpu_to_le16s (u16 *); void le64_to_cpus (u64 *); void le32_to_cpus (u32 *); void le16_to_cpus (u16 *); void cpu_to_be64s (u64 *); void cpu_to_be32s (u32 *); void cpu_to_be16s (u16 *); void be64_to_cpus (u64 *); void be32_to_cpus (u32 *); void be16_to_cpus (u16 *);As stated before, the USB protocol is in little-endian format. The code snippet from drivers/usb/serial/visor.c presented in Listing 2 shows how a structure is read from the USB connection and then converted into the proper CPU format.
Listing 2. How a structure is read from the USB connection and converted into the proper CPU format.
The gcc compiler typically aligns individual fields of a structure on whatever byte boundary it likes in order to provide faster execution. For example, consider the code and resulting output shown in Listing 3.
Listing 3. Alignment of Individual Fields of a Structure
The output shows that the compiler aligned fields b and c in the struct foo on even byte boundaries. This is not a good thing when we want to overlay a structure on top of a memory location. Typically driver data structures do not have even byte padding for the individual fields. Because of this, the gcc attribute (packed) is used to tell the compiler not to place any ``memory holes'' within a structure.
If we change the struct foo structure to use the packed attribute like this:
struct foo {
char a;
short b;
int c;
} __attribute__ ((packed));
Then the output of the program changes to:
offset A = 0 offset B = 1 offset C = 3Now there are no more memory holes in the structure.
This packed attribute can be used to pack an entire structure, as shown above, or it can be used only to pack a number of specific fields within a structure.
For example, the struct usb_ctrlrequest is defined in include/usb.h as the following:
struct usb_ctrlrequest {
__u8 bRequestType;
__u8 bRequest;
__u16 wValue;
__u16 wIndex;
__u16 wLength;
} __attribute__ ((packed));
This ensures that the entire structure is packed, so that it can be used to write data directly to a USB connection.
But the definition of the struct usb_endpoint_descriptor looks like:
struct usb_endpoint_descriptor {
__u8 bLength __attribute__ ((packed));
__u8 bDescriptorType __attribute__ ((packed));
__u8 bEndpointAddress __attribute__ ((packed));
__u8 bmAttributes __attribute__ ((packed));
__u16 wMaxPacketSize __attribute__ ((packed));
__u8 bInterval __attribute__ ((packed));
__u8 bRefresh __attribute__ ((packed));
__u8 bSynchAddress __attribute__ ((packed));
unsigned char *extra; /* Extra descriptors */
int extralen;
};
This ensures that the first part of the structure is packed and can be used to read directly from a USB connection, but the extra and extralen fields of the structure can be aligned to whatever the compiler thinks will be fastest to access.
Today’s modular x86 servers are compute-centric, designed as a least common denominator to support a wide range of IT workloads. Those generic, virtualized IT workloads have much different resource optimization requirements than hyperscale and cloud applications. They have resulted in a “one size fits all” enterprise IT architecture that is not optimized for a specific set of IT workloads, and especially not emerging hyperscale workloads, such as web applications, big data, and object storage. In this report, you will learn how shifting the focus from traditional compute-centric IT architectures to an innovative disaggregated fabric-based architecture can optimize and scale your data center.
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
| 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 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
| Trying to Tame the Tablet | May 08, 2013 |
| Dart: a New Web Programming Experience | May 07, 2013 |
- RSS Feeds
- New Products
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Home, My Backup Data Center
- A Topic for Discussion - Open Source Feature-Richness?
- Dart: a New Web Programming Experience
- Developer Poll
- What's the tweeting protocol?
- May 2013 Issue of Linux Journal: Raspberry Pi
- Reply to comment | Linux Journal
21 min 24 sec ago - great post
56 min 17 sec ago - Google Docs
1 hour 18 min ago - Reply to comment | Linux Journal
6 hours 7 min ago - Reply to comment | Linux Journal
6 hours 54 min ago - Web Hosting IQ
8 hours 27 min ago - Thanks for taking the time to
10 hours 4 min ago - Linux is good
12 hours 2 min ago - Reply to comment | Linux Journal
12 hours 19 min ago - Web Hosting IQ
12 hours 49 min ago
Enter to Win an Adafruit Prototyping Pi Plate 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 Prototyping Pi Plate 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
- Next winner announced on 5-21-13!
Free Webinar: Linux Backup and Recovery
Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.
In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.



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