Part of the WritingPortableDrivers section
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 this code and resulting output:
1 #include <stdio.h>
2 #include <stddef.h>
3
4 struct foo {
5 char a;
6 short b;
7 int c;
8 };
9
10 #define OFFSET_A offsetof(struct foo, a)
11 #define OFFSET_B offsetof(struct foo, b)
12 #define OFFSET_C offsetof(struct foo, c)
13
14 int main ()
15 {
16 printf ("offset A = %d\n", OFFSET_A);
17 printf ("offset B = %d\n", OFFSET_B);
18 printf ("offset C = %d\n", OFFSET_C);
19 return 0;
20 }
Running the program gives:
offset A = 0 offset B = 2 offset C = 4
The output shows that the compiler aligned fields b and c in 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:
Then the output of the program changes to:
offset A = 0 offset B = 1 offset C = 3
Now 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:
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:
1 struct usb_endpoint_descriptor {
2 __u8 bLength __attribute__((packed));
3 __u8 bDescriptorType __attribute__((packed));
4 __u8 bEndpointAddress __attribute__((packed));
5 __u8 bmAttributes __attribute__((packed));
6 __le16 wMaxPacketSize __attribute__((packed));
7 __u8 bInterval __attribute__((packed));
8 __u8 bRefresh __attribute__((packed));
9 __u8 bSynchAddress __attribute__((packed));
10 unsigned char *extra; /* Extra descriptors */
11 int extralen;
12 };
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.