Writing Device Drivers

Device Register Structure

Using pointer accesses to communicate with the device results in unreadable code. For example, the code that reads the data register when a transfer has been completed might look like this:

	uint8_t data;
 	uint8_t status;
 	/* get status */
 	status = ddi_get8(data_access_handle, (uint8_t *)reg_addr);
 	if (status & TRANSFER_COMPLETE) {
 		data = ddi_get8(data_access_handle,
 			(uint8_t *)reg_addr + 1); /* read data */
 	}

To make the code more readable, it is common to define a structure that matches the layout of the device registers. In this case, the structure could look like this:

	struct device_reg {
 		uint8_t csr;
 		uint8_t data;
 	};

The driver then maps the registers into memory and refers to them through a pointer to the structure:

	struct device_reg *regp;
	...
 	ddi_regs_map_setup(..., (caddr_t *)&regp, ... ,
 		&access_attributes, &data_access_handle);
 	...

The code that reads the data register upon a completed transfer now looks like this:

	uint8_t data;
 	uint8_t status;
 	/* get status */
 	status = ddi_get8(data_access_handle, &regp->csr); 	
 	if (status & TRANSFER_COMPLETE) {
 		/* read data */
 		data = ddi_get8(data_access_handle, &regp->data); 	
 	}

Structure Padding

A device that has a 1-byte command and status register followed by a 4-byte data register might lead to the following structure layout:

	struct device_reg {
 		uint8_t			csr;
 		uint32	_t		data;
 	};

This structure is not correct, because the compiler places padding between the two fields. For example, the SPARC processor requires each type to be on its natural boundary, which is 1-byte alignment for the csr field, but 4-byte alignment for the data field. This results in three unused bytes between the two fields. When the driver accesses a data register, it will be three bytes off. Consequently, this layout should not be used.

Finding Padding

The ANSI C offsetof(3C) macro may be used in a test program to determine the offset of each element in the structure. Knowing the offset and the size of each element, the location and size of any padding can be determined.


Example 3-2 Structure Padding

#include <sys/types.h>
#include <stdio.h>
#include <stddef.h>

struct device_reg {
	uint8_t			csr;
	uint32_t			data;
};

int main(void)
{
	printf("The offset of csr is %d, its size is %d.\n",
			offsetof(struct device_reg, csr), sizeof (uint8_t));
	printf("The offset of data is %d, its size is %d.\n",
			offsetof(struct device_reg, data), sizeof (uint32_t));
	return (0);
}

Here is a sample compilation with Sun WorkShopTM Compiler C version 4.2 and a subsequent run of the program:

test% cc -Xa c.c

test% a.out

The offset of csr is 0, its size is 1.

The offset of data is 4, its size is 4.

Be aware that padding is dependent not only on the processor but also on the compiler.