Writing Device Drivers

Data Structures

The data structures illustrated in Figure 5-1 must be provided and initialized correctly for the driver to load and for its routines to be called. If an operation is not supported by the driver, the address of the routine nodev(9F) can be used to fill it in. If the driver supports the entry point, but does not need to do anything except return success, the address of the routine nulldev(9F) can be used.

Note -

These structures should be initialized at compile-time. They should not be accessed or changed by the driver at any other time.

modlinkage Structure

	int		ml_rev;
 	void		*ml_linkage[4];

The modlinkage(9S) structure is exported to the kernel when the driver is loaded. The ml_rev field indicates the revision number of the loadable module system, which should be set to MODREV_1. Drivers can only support one module, so only the first element of ml_linkage should be set to the address of a modldrv(9S) structure. ml_linkage[1] should be set to NULL.

modldrv Structure

	struct mod_ops					*drv_modops;
 	char							*drv_linkinfo;
 	struct dev_ops				*drv_dev_ops;

This structure describes the module in more detail. The drv_modops field points to a structure describing the module operations, which is &mod_driverops for a device driver. The drv_linkinfo field is displayed by the modinfo(1M) command and should be an informative string identifying the device driver. The drv_dev_ops field points to the next structure in the chain, the dev_ops(9S) structure.

dev_ops Structure

	int		devo_rev;
 	int		devo_refcnt;
 	int		(*devo_getinfo)(dev_info_t *dip,ddi_info_cmd_t infocmd,
 				void *arg, void **result);
 	int		(*devo_identify)(dev_info_t *dip);
 	int		(*devo_probe)(dev_info_t *dip);
 	int		(*devo_attach)(dev_info_t *dip, ddi_attach_cmd_t cmd);
 	int		(*devo_detach)(dev_info_t *dip, ddi_detach_cmd_t cmd);
 	int		(*devo_reset)(dev_info_t *dip, ddi_reset_cmd_t cmd);
 	int		(*devo_power)(dev_info_t *dip, int component,
 				int level);
 	struct cb_ops				*devo_cb_ops;
 	struct bus_ops				*devo_bus_ops;

The dev_ops(9S) structure allows the kernel to find the autoconfiguration entry points of the device driver. The devo_rev field identifies the revision number of the structure itself, and must be set to DEVO_REV. The devo_refcnt field must be initialized to zero. The function address fields should be filled in with the address of the appropriate driver entry point. Exceptions are:

The devo_cb_ops member should include the address of the cb_ops(9S) structure. The devo_bus_ops field must be set to NULL.

cb_ops Structure

	int		(*cb_open)(dev_t *devp, int flag, int otyp,
 				cred_t *credp);
 	int		(*cb_close)(dev_t dev, int flag, int otyp,
 				cred_t *credp);
 	int		(*cb_strategy)(struct buf *bp);
 	int		(*cb_print)(dev_t dev, char *str);
 	int		(*cb_dump)(dev_t dev, caddr_t addr, daddr_t blkno,
 				int nblk);
 	int		(*cb_read)(dev_t dev, struct uio *uiop, cred_t *credp);
 	int		(*cb_write)(dev_t dev, struct uio *uiop, cred_t *credp);
 	int		(*cb_ioctl)(dev_t dev, int cmd, intptr_t arg, int mode,
 				cred_t *credp, int *rvalp);
 	int		(*cb_devmap)(dev_t dev, devmap_cookie_t dhp,
 				offset_t off, size_t len, size_t *maplen,
 				uint_t model);
 	int		(*cb_mmap)(dev_t dev, off_t off, int prot);
 	int		(*cb_segmap)(dev_t dev, off_t off, struct as *asp,
 				addr_t *addrp, off_t len, unsigned int prot,
 				unsigned int maxprot, unsigned int flags,
 				cred_t *credp);
 	int		(*cb_chpoll)(dev_t dev, short events, int anyyet,
 				short *reventsp, struct pollhead **phpp);
 	int		(*cb_prop_op)(dev_t dev, dev_info_t *dip,
 				ddi_prop_op_t prop_op, int mod_flags,
 				char *name, caddr_t valuep, int *length);
 	struct streamtab					*cb_str;   /* STREAMS information */
 	int		cb_flag;
 	int		cb_rev;
 	int		(*cb_aread)(dev_t dev, struct aio_req *aio,
 				cred_t *credp);
 	int		(*cb_awrite)(dev_t dev, struct aio_req *aio,
 				cred_t *credp);

The cb_ops(9S) structure contains the entry points for the character and block operations of the device driver. Any entry points the driver does not support should be initialized to nodev(9F). For example, character device drivers should set all the block-only fields (such as cb_stategy to nodev(9F)). Note that the mmap(9E) entry point is maintained for compatibility with previous releases, and drivers should use the devmap(9E) entry point for device memory mapping. If devmap(9E) is supported, set mmap(9E) to nodev(9F).

The cb_str field is used to determine if this is a STREAMS-based driver. The device drivers discussed in this book are not STREAMS based. For a non-STREAMS-based driver, cb_str must be set to NULL.

The cb_flag member contains the following flags:

cb_rev is the cb_ops(9S) structure revision number. This field must be set to CB_REV.