Writing Device Drivers

Autoconfiguration Entry Points

Associated with each device driver is a dev_ops(9S) structure, which allows the kernel to locate the autoconfiguration entry points of the driver. A complete description of these autoconfiguration routines is given in Chapter 5, Autoconfiguration. This section describes only those entry points associated with operations performed by SCSI HBA drivers. These include attach(9E) and detach(9E).

attach(9E)

The attach(9E) entry point for a SCSI HBA driver must perform a number of tasks to configure and attach an instance of the driver for the device. For a typical driver of real devices, the following operating system and hardware concerns must be addressed:

Soft State Structure

The driver should allocate the per-device-instance soft state structure, being careful to clean up properly if an error occurs.

instance = ddi_get_instance(dip);
 if (ddi_soft_state_zalloc(isp_state,
 		instance) != DDI_SUCCESS) {
 	return (DDI_FAILURE);
 }
 isp = ddi_get_soft_state(isp_state, instance);

DMA

If the driver provides DMA, for example, it must specify DMA attributes describing the capabilities and limitations of its DMA engine.


Note -

In the Solaris 7 system, the HBA driver must provide DMA.


	
			static ddi_dma_attr_t isp_dma_attr = {
 		DMA_ATTR_V0,		/* ddi_dma_attr version */
 		0,						/* low address */
 		0xffffffff,			/* high address */
 		0x00ffffff,			/* counter upper bound */
 		1,						/* alignment requirements */
 		0x3f,					/* burst sizes */
 		1,						/* minimum DMA access */
 		0xffffffff,			/* maximum DMA access */
 		(1<<24)-1,			/* segment boundary restrictions */
 		1,						/* scatter/gather list length */
 		512,					/* device granularity */
 		0						/* DMA flags */
 	};

The driver, if providing DMA, should also check that its hardware is installed in a DMA-capable slot:

		if (ddi_slaveonly(dip) == DDI_SUCCESS) {
 			return (DDI_FAILURE);
 		}

Transport Structure

The driver should further allocate and initialize a transport structure for this instance. The tran_hba_private field is set to point to this instance's soft-state structure. tran_tgt_probe may be set to NULL to achieve the default behavior, if no special probe customization is needed.

	tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);

 	isp->isp_tran							= tran;
 	isp->isp_dip							= dip;

 	tran->tran_hba_private				= isp;
 	tran->tran_tgt_private				= NULL;
 	tran->tran_tgt_init					= isp_tran_tgt_init;
 	tran->tran_tgt_probe				= scsi_hba_probe;
 	tran->tran_tgt_free					= (void (*)())NULL;

 	tran->tran_start						= isp_scsi_start;
 	tran->tran_abort						= isp_scsi_abort;
 	tran->tran_reset						= isp_scsi_reset;
 	tran->tran_getcap						= isp_scsi_getcap;
 	tran->tran_setcap						= isp_scsi_setcap;
 	tran->tran_init_pkt					= isp_scsi_init_pkt;
 	tran->tran_destroy_pkt				= isp_scsi_destroy_pkt;
 	tran->tran_dmafree					= isp_scsi_dmafree;
 	tran->tran_sync_pkt					= isp_scsi_sync_pkt;
 	tran->tran_reset_notify			= isp_scsi_reset_notify;

Attaching an HBA Driver

The driver should attach this instance of the device and perform error cleanup if necessary.

	i = scsi_hba_attach_setup(dip, &isp_dma_attr, tran, 0);
 	if (i != DDI_SUCCESS) {
 		do error recovery 	
			return (DDI_FAILURE);
 	}

Register Mapping

The driver should map in its device's registers, specifying the index of the register set, the data access characteristics of the device and the size of the register set to be mapped.

		ddi_device_acc_attr_t						dev_attributes;

 	dev_attributes.devacc_attr_version = DDI_DEVICE_ATTR_V0;
 	dev_attributes.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
 	dev_attributes.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;

 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&isp->isp_reg,
 	    0, sizeof (struct ispregs), &dev_attributes,
 	    &isp->isp_acc_handle) != DDI_SUCCESS) {
 		do error recovery
			return (DDI_FAILURE);
 	}

Adding an Interrupt Handler

The driver should determine if a high-level interrupt handler is required. If a high-level handler is required and the driver is not coded to provide one, the driver must be rewritten to either include a high-level interrupt or fail the attach.

In the following example, a high-level interrupt is required but not provided by the driver. Consequently, the driver fails the attach.

	if (ddi_intr_hilevel(dip, 0) != 0) {
 		return (DDI_FAILURE);
 	}

The driver must first obtain the iblock cookie to initialize mutexes used in the driver handler. Only after those mutexes have been initialized can the interrupt handler be added.

	i = ddi_get_iblock_cookie(dip, 0, &isp->iblock_cookie};
 	if (i != DDI_SUCCESS) {
 		do error recovery
			return (DDI_FAILURE);
 	}

 	mutex_init(&isp->mutex, "isp_mutex", MUTEX_DRIVER,
 		(void *)isp->iblock_cookie);
 	i = ddi_add_intr(dip, 0, &isp->iblock_cookie,
 		0, isp_intr, (caddr_t)isp);
 	if (i != DDI_SUCCESS) {
 		do error recovery
			return (DDI_FAILURE);
 	}

Report Attachment Status

Finally, the driver should report that this instance of the device is attached and return success.

	ddi_report_dev(dip);
 	return (DDI_SUCCESS);

detach(9E)

The Solaris 7 DDI/DKI does not support detaching an HBA driver, although target driver children of an HBA can detach. For best results, the HBA driver should fail a detach request. It's better to fail the detach than to include code that cannot be tested.

Example 14-3 provides an example of the xx_detach() function.


Example 14-3 isp_detach

static int
isp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
	switch (cmd) {
	case DDI_DETACH:
	   	/*
		    * At present, detaching HBA drivers is not supported
		    */
	    	return (DDI_FAILURE);
	case DDI_PM_RESUME:
	    	For information, see Chapter 8, Power Management	case DDI_RESUME:
	     	For information, see Chapter 8, Power Management	default:
	    	return (DDI_FAILURE);
	}
}