Writing Device Drivers

tran_setcap(9E)

The tran_setcap(9E) entry point for a SCSI HBA driver is called when a target driver calls scsi_ifsetcap(9F) to change the current one of a set of SCSA-defined capabilities.

The target driver may request that the new value be set for a particular target by setting the whom parameter to nonzero. A whom value of 0 means the request is to set the new value for the SCSI bus or for adapter hardware in general.

tran_setcap(9E) should return -1 for undefined capabilities, 0 if the HBA driver cannot set the capability to the requested value, or 1 if the HBA driver is able to set the capability to the requested value.

The HBA driver may use the function scsi_hba_lookup_capstr(9F) to compare the capability string against the canonical set of defined capabilities.


Example 14-13 HBA Driver tran_setcap(9E) Entry Point

static int
isp_scsi_setcap(
	struct scsi_address	*ap,
	char					*cap,
	int					value,
	int					whom)
{
	struct isp					*isp;
	int					rval = 0;
	u_char					tgt = ap->a_target;
	int					update_isp = 0;

	/*
	 * We don't allow setting capabilities for other targets
	 */
	if (cap == NULL || whom == 0) {
		return (-1);
	}

	isp = (struct isp *)ap->a_hba_tran->tran_hba_private;

	ISP_MUTEX_ENTER(isp);

	switch (scsi_hba_lookup_capstr(cap)) {

	case SCSI_CAP_DMA_MAX:
	case SCSI_CAP_MSG_OUT:
	case SCSI_CAP_PARITY:
	case SCSI_CAP_UNTAGGED_QING:
	case SCSI_CAP_LINKED_CMDS:
	case SCSI_CAP_RESET_NOTIFICATION:
		/*
		 * None of these are settable via
		 * the capability interface.
		 */
		break;
	case SCSI_CAP_DISCONNECT:
		if ((isp->isp_target_scsi_options[tgt] &
			SCSI_OPTIONS_DR) == 0) {
			break;
		} else {
			if (value) {
				isp->isp_cap[tgt] |= ISP_CAP_DISCONNECT;
			} else {
				isp->isp_cap[tgt] &= ~ISP_CAP_DISCONNECT;
			}
		}
		rval = 1;
		break;
	case SCSI_CAP_SYNCHRONOUS:
		if ((isp->isp_target_scsi_options[tgt] &
			SCSI_OPTIONS_SYNC) == 0) {
			break;
		} else {
			if (value) {
				isp->isp_cap[tgt] |= ISP_CAP_SYNC;
			} else {
				isp->isp_cap[tgt] &= ~ISP_CAP_SYNC;
			}
		}
		rval = 1;
		break;
	case SCSI_CAP_TAGGED_QING:
		if ((isp->isp_target_scsi_options[tgt] &
			SCSI_OPTIONS_DR) == 0 ||
		    (isp->isp_target_scsi_options[tgt] &
			SCSI_OPTIONS_TAG) == 0) {
			break;
		} else {
			if (value) {
				isp->isp_cap[tgt] |= ISP_CAP_TAG;
			} else {
				isp->isp_cap[tgt] &= ~ISP_CAP_TAG;
			}
		}
		rval = 1;
		break;
	case SCSI_CAP_WIDE_XFER:
		if ((isp->isp_target_scsi_options[tgt] &
			SCSI_OPTIONS_WIDE) == 0) {
			break;
		} else {
			if (value) {
				isp->isp_cap[tgt] |= ISP_CAP_WIDE;
			} else {
				isp->isp_cap[tgt] &= ~ISP_CAP_WIDE;
			}
		}
		rval = 1;
		break;
	case SCSI_CAP_INITIATOR_ID:
		if (value < N_ISP_TARGETS_WIDE) {
			struct isp_mbox_cmd mbox_cmd;

			isp->isp_initiator_id = (u_short) value;

			/*
			 * set Initiator SCSI ID
			 */
			isp_i_mbox_cmd_init(isp, &mbox_cmd, 2, 2,
			    ISP_MBOX_CMD_SET_SCSI_ID,
			    isp->isp_initiator_id,
			    0, 0, 0, 0);
			if (isp_i_mbox_cmd_start(isp, &mbox_cmd) == 0) {
				rval = 1;
			}
		}
		break;
	case SCSI_CAP_ARQ:
		if (value) {
			isp->isp_cap[tgt] |= ISP_CAP_AUTOSENSE;
		} else {
			isp->isp_cap[tgt] &= ~ISP_CAP_AUTOSENSE;
		}
		rval = 1;
		break;

	default:
		rval = -1;
		break;
	}
	ISP_MUTEX_EXIT(isp);

	return (rval);
}