Writing Device Drivers

Interrupt Handler and Command Completion

The interrupt handler must check the status of the device to be sure the device is generating the interrupt in question. It must also check for any errors that may have occurred and service any interrupts generated by the device.

If data was transferred, the hardware should be checked to determine how much data was actually transferred, and the pkt_resid field in the scsi_pkt(9S) structure should be set to the residual of the transfer.

For commands marked with the PKT_CONSISTENT flag when DMA resources were allocated through tran_init_pkt(9E), the HBA driver must ensure that the data transfer for the command is correctly synchronized before the target driver's command completion callback is performed.

Once a command has completed, there are two requirements:

It is important to start a new command on the hardware, if possible, before calling the PKT_COMP command completion callback. The command completion handling may take considerable time, as the target driver will typically call functions such as biodone(9F) and possibly scsi_transport(9F) to begin a new command.

The interrupt handler must return DDI_INTR_CLAIMED if this interrupt is claimed by this driver; otherwise, the handler returns DDI_INTR_UNCLAIMED.

Example 14-11 shows an interrupt handler for the SCSI HBA isp driver. The caddr_t argument is the parameter set up when the interrupt handler was added in attach(9E) and is typically a pointer to the state structure allocated per instance.

Example 14-11 HBA Driver Interrupt Handler

static u_int
isp_intr(caddr_t arg)
	struct isp_cmd						*sp;
	struct isp_cmd						*head, 	*tail;
	u_short						response_in;
	struct isp_response						*resp;
	struct isp						*isp 	= (struct isp *)arg;
	struct isp_slot						*isp_slot;
	int						n;

	if (ISP_INT_PENDING(isp) == 0) {

	do {
		 * head list collects completed packets for callback later
		head = tail = NULL;

		 * Assume no mailbox events (e.g. mailbox cmds, asynch
		 * events, and isp dma errors) as common case.
		if (ISP_CHECK_SEMAPHORE_LOCK(isp) == 0) {

			 * Loop through completion response queue and post
			 * completed pkts.  Check response queue again
			 * afterwards in case there are more
			isp->isp_response_in =
			    response_in = ISP_GET_RESPONSE_IN(isp);

			 * Calculate the number of requests in the queue
			n = response_in - isp->isp_response_out;
			if (n < 0) {
				    isp->isp_response_out + response_in;

			while (n-- > 0) {
				sp = (struct isp_cmd *)resp->resp_token;

				 * copy over response packet in sp
				isp_i_get_response(isp, resp, sp);

				if (head) {
					tail->cmd_forw = sp;
					tail = sp;
					tail->cmd_forw = NULL;
				} else {
					tail = head = sp;
					sp->cmd_forw = NULL;


			if (head) {
				isp_i_call_pkt_comp(isp, head);
		} else {
			if (isp_i_handle_mbox_cmd(isp) != ISP_AEN_SUCCESS) {
				return (DDI_INTR_CLAIMED);
			 * if there was a reset then check the response
			 * queue again
			goto again;	

	} while (ISP_INT_PENDING(isp));


static void
	struct isp					*isp,
	struct isp_cmd					*head)
	struct isp					*isp;
	struct isp_cmd					*sp;
	struct scsi_pkt					*pkt;
	struct isp_response	*resp;
	u_char					status;

	while (head) {
		sp = head;
		pkt = sp->cmd_pkt;
		head = sp->cmd_forw;

		ASSERT(sp->cmd_flags & CFLAG_FINISHED);

		resp = &sp->cmd_isp_response;

		pkt->pkt_scbp[0] = (u_char)resp->resp_scb;
		pkt->pkt_state = ISP_GET_PKT_STATE(resp->resp_state);
		pkt->pkt_statistics = (u_long)
		pkt->pkt_resid = (long)resp->resp_resid;

		 * if data was xferred and this is a consistent pkt,
		 * we need to do a dma sync
		if ((sp->cmd_flags & CFLAG_CMDIOPB) &&
		    (pkt->pkt_state & STATE_XFERRED_DATA)) {

			(void) ddi_dma_sync(sp->cmd_dmahandle,
			    sp->cmd_dma_offset, sp->cmd_dma_len,

		sp->cmd_flags = (sp->cmd_flags & ~CFLAG_IN_TRANSPORT) |

		 * Call packet completion routine if FLAG_NOINTR is not set.
		if (((pkt->pkt_flags & FLAG_NOINTR) == 0) &&
		    pkt->pkt_comp) {