Writing Device Drivers

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);