Writing Device Drivers

Conversion Notes

identify(9E)

identify(9E) is obsolete and no longer required. identify(9E) was used to determine whether a driver drove the device pointed to by dip. identify(9E) is currently supported only to provide backward compatibility with older drivers and should not be implemented. Set this entry point to nulldev(9F).


Note -

The framework now handles unit counting. To get the unit number in any routine, call ddi_get_instance(9F). Do not count units anywhere.


probe(9E)

SunOS 4.1 system:

	int xxprobe(reg, unit)
 	caddr_t reg;
 	int		unit;

SunOS 5.7 system:

	int xxprobe(dev_info_t *dip)

probe(9E) is still expected to determine if a device exists, but now the routine might be called any number of times, so it must be stateless (free anything it allocates).

attach(9E)

SunOS 4.1 system:

	int xxattach(devinfo)
 							struct dev_info *devinfo;

SunOS 5.7 system

	int xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)

Drivers are not allowed to count instances anywhere. Use ddi_get_instance(9F) to get the assigned instance number.

new_kmem_alloc( ) and new_kmem_zalloc( ) have become kmem_alloc(9F) and kmem_zalloc(9F). In SunOS 4.1 sleep flags were KMEM_SLEEP and KMEM_NOSLEEP; now they are KM_SLEEP and KM_NOSLEEP. Consider using KM_SLEEP only on small requests, as larger requests could deadlock the driver if there is not (or there will not be) enough memory. Instead, use KM_NOSLEEP, possibly shrink the request, and try again.

Any required memory should be dynamically allocated, as the driver should handle all occurrences of its device rather than a fixed number of them (if possible). Instead of statically allocating an array of controller state structures, each should now be allocated dynamically.

Remember to call ddi_create_minor_node(9F) for each minor device name that should be visible to applications.

The module loading process turns the information in any driver.conf(4) file into properties. Information that used to pass in the config file (such as flags) should now be passed as properties.

getinfo(9E)

SunOS 5.7 system:

	int xxgetinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
 		void *arg, void **resultp)

Make sure that the minor number to instance number and the reverse translation is static, since getinfo(9E) may be called when the device is not attached. For example:

	#define XXINST(dev) (getminor(dev) >> 3)

This is a required entry point; it cannot be replaced with nulldev(9F) or nodev(9F).

open(9E)

SunOS 4.1 system:

	int xxopen(dev, flag)
 	dev_t		dev;
 	int		flag;

SunOS 5.7 system:

	int xxopen(dev_t *devp, int flag, int otyp, cred_t *credp)

The first argument to open(9E) is a pointer to a dev_t. The rest of the cb_ops(9S) routines receive a dev_t.

Verify that the open type is one that the driver actually supports. This is normally OTYP_CHR for character devices, or OTYP_BLK for block devices. This prevents the driver from allowing future open types that it does not support.

If the driver used to check for root privileges using suser(), it should now use driv_priv(9F) instead on the passed credential pointer.

psize()

This entry point does not exist. Instead, block devices should support the nblocks property. This property may be created in attach(9E) if its value will not change. A prop_op(9E) entry point may be required if the value cannot be determined at attach time (such as if the device supports removable media). See "Properties" for more information.

read(9E) and write(9E)

SunOS 4.1 system:

	int xxread(dev, uio)
 	int xxwrite(dev, uio)
 	dev_t		dev;
 	struct uio *uio;

SunOS 5.7 system:

	int xxread(dev_t dev, uio_t *uiop, cred_t *credp);
 	int xxwrite(dev_t dev, uio_t *uiop, cred_t *credp);

physio(9F) should no longer be called with the address of a statically allocated buf(9S) structure. Instead, pass a NULL pointer as the second argument, which causes physio(9F) to allocate a buf structure. The address of the allocated buf structure should always be saved in strategy(9E), as it is needed to call biodone(9F). An alternative is to use getrbuf(9F) to allocate the buf(9S) structure, and freerbuf(9F) to free it.

ioctl(9E)

SunOS 4.1 system:

	int xxioctl(dev, cmd, data, flag)
 	dev_t		dev;
 	int		cmd, flag;
 	caddr_t		data;

SunOS 5.7 system:

	int xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
 		cred_t *credp, int *rvalp);

In the SunOS 4.1 system, ioctl(9E) command arguments were defined as follows:

	#define XXIOCTL1  _IOR(m, 1, u_int)

The _IOR( ), _IOW( ), and _IOWR( ) macros were used to encode the direction and size of the data transfer. The kernel would then automatically copy the data into or out of the kernel. This is no longer the case. To do a data transfer, the driver is now required to use ddi_copyin(9F) and ddi_copyout(9F) explicitly. Do not dereference arg directly.

In addition, use the new method of a left-shifted letter OR'ed with number:

	#define XXIOC     (`x'<<8)
 	#define XXIOCTL1  (XXIOC | 1)

The credential pointer can be used to check credentials on the call (with drv_priv(9F)), and the return value pointer can be used to return a value that has meaning (as opposed to the old method of always getting zero back for success). This number should be positive to avoid confusion with applications that check for ioctl(2) returning a negative value for failure.

strategy(9E)

SunOS 4.1 system:

	int xxstrategy(buf)
 	struct buf *bp;

SunOS 5.7 system;

	int xxstrategy(struct buf *bp);

Retrieving the minor number from the b_dev field of the buf(9S) structure no longer works (or will work occasionally, and fail in new and notable ways at other times). Use the b_edev field instead.

If the driver allocated buffers uncached, it should now use ddi_dma_sync(9F) whenever consistent view of the buffer is required.

mmap(9E)

SunOS 4.1 system:

	int xxmmap(dev, off, prot)
 	dev_t		dev;
 	off_t		off;
 	int		prot;

SunOS 5.7 system:

	int xxdevmap(dev_t dev, devmap_cookie_t handle, offset_t off,
 	size_t len, size_t *maplen, u_int model);

In Solaris 7 and subsequent releases, the recommended way for applications to map kernel or device memory is with the devmap(9E) interface. For information on devmap(9E), see Chapter 11, Mapping Device or Kernel Memory.

If the driver checked for root privileges using suser()(), it should now use drv_priv(9F). Because there is no credential pointer passed to devmap(9E), the driver must use ddi_get_cred(9F) to retrieve the credential pointer.

chpoll(9E)

chpoll(9E) is similar in operation to select()( ), but there are more conditions that can be examined. See "Multiplexing I/O on File Descriptors " for details.