Writing Device Drivers

Driver Interfaces

The kernel expects device drivers to provide certain routines that must perform certain operations; these routines are called entry points. This is similar to the requirement that application programs have a start() entry point or that C applications have the more familiar main() routine.

Entry Points

Each device driver defines a standard set of functions called entry points, which are defined in the Solaris 2.7 Reference Manual. Drivers for different types of devices have different sets of entry points according to the kinds of operations the devices perform. A driver for a memory-mapped character-oriented device, for example, supports a devmap(9E) entry point, while a block driver does not.

Some operations are common to all drivers, such as the functions that are required for module loading (_init(9E), _info(9E), and _fini(9E)), and the required autoconfiguration entry points attach(9E) and getinfo(9E). Drivers may also support the optional autoconfiguration entry points for probe(9E) and detach(9E). Most drivers have open(9E) and close(9E) entry points to control access to their devices.

Traditionally, all driver function and variable names have some prefix added to them. Usually, this is the name of the driver, such as xxopen() for the open(9E) routine of driver xx. In subsequent examples, xx is used as the driver prefix.

Note -

In the SunOS 5.7 system, only the loadable module routines must be visible outside the driver object module. Other routines can have the storage class static.

Loadable Module Routines

	int _init(void);
 	int _info(struct modinfo *modinfop);
 	int _fini(void);

All drivers must implement the _init(9E), _fini(9E) and _info(9E) entry points to load, unload and report information about the driver module. The driver is single-threaded when the kernel calls _init(9E). No other thread will enter a driver routine until mod_install(9F) returns success.

The driver should allocate and initialize any global resources in _init(9E) before calling mod_install(9F) and release global resources in _fini(9E) after mod_remove(9F) returns success.

Note -

Drivers must use these names, and they must not be declared static, unlike the other entry points where the names and storage classes are determined by the driver.

Autoconfiguration Entry Points

	static int xxprobe(dev_info_t *dip);
 	static int xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd);
 	static int xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd);
 	static int xxgetinfo(dev_info_t *dip,ddi_info_cmd_t infocmd,
 					void *arg, void **result);

Any per-device resources should be allocated in attach(9E) and released in detach(9E). No resources global to the driver should be allocated in attach(9E). For information on autoconfiguration entry points, see Chapter 5, Autoconfiguration.

Block Driver Entry Points

	int xxopen(dev_t *devp, int flag, int otyp, cred_t *credp);
 	int xxclose(dev_t dev, int flag, int otyp, cred_t *credp);
 	int xxstrategy(struct buf *bp);
 	int xxprint(dev_t dev, char *str);
 	int xxdump(dev_t dev, caddr_t addr, daddr_t blkno, int nblk);
 	int xxprop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
 				   int mod_flags, char *name, caddr_t valuep,
 				   int *length);

For information on block driver entry points, see Chapter 10, Drivers for Block Devices.

Character Driver Entry Points

	int xxopen(dev_t *devp, int flag, int otyp, cred_t *credp);
 	int xxclose(dev_t dev, int flag, int otyp, cred_t *credp);
 	int xxread(dev_t dev, struct uio *uiop, cred_t *credp);
 	int xxwrite(dev_t dev, struct uio *uiop, cred_t *credp);
 	int xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
 				cred_t *credp, int *rvalp);
 	int xxdevmap(dev_t dev, devmap_cookie_t dhp, offset_t off,
 				size_t len, size_t *maplen, uint_t model);
 	int xxmmap(dev_t dev, off_t off, int prot);
 	int xxsegmap(dev_t dev, off_t off, struct as *asp,
 				caddr_t *addrp, off_t len, unsigned int prot,
 				unsigned int maxprot, unsigned int flags,
 				cred_t *credp);
 	int xxchpoll(dev_t dev, short events, int anyyet,
 				short *reventsp, struct pollhead **phpp);
 	int xxprop_op(dev_t dev, dev_info_t *dip,
 				ddi_prop_op_t prop_op, int mod_flags,
 				char *name, caddr_t valuep, int *length);
 	int xxaread(dev_t dev, struct aio_req *aio, cred_t *credp);
 	int xxawrite(dev_t dev, struct aio_req *aio, cred_t *credp);

For information on character driver entry points, see Chapter 9, Drivers for Character Devices.

Power Management Entry Point

	int xxpower(dev_info_t *dip, int component, int level);

Drivers for hardware devices that provide Power Management functionality may support the optional power(9E) entry point. See Chapter 8, Power Managementfor details about this entry point.

Driver Structure Overview

Figure 3-1 shows data structures and routines that may define the structure of a character or block device driver. Such drivers typically include a device loadable driver section, device configuration section, and device access section.

Figure 3-1 Device Driver Roadmap


Note -

The first two sections in Figure 3-1 are discussed in Chapter 5, Autoconfiguration; the third section is discussed in Chapter 9, Drivers for Character Devices and Chapter 10, Drivers for Block Devices.