Writing Device Drivers

Capability Management

The following sections discuss capability management.

tran_getcap() Entry Point

The tran_getcap(9E) entry point for a SCSI HBA driver is called by scsi_ifgetcap(9F). The target driver calls scsi_ifgetcap() to determine the current value of one of a set of SCSA-defined capabilities.

The target driver can request the current setting of the capability for a particular target by setting the whom parameter to nonzero. A whom value of zero indicates a request for the current setting of the general capability for the SCSI bus or for adapter hardware.

The tran_getcap() entry point should return -1 for undefined capabilities or the current value of the requested capability.

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


Example 18–10 HBA Driver tran_getcap(9E) Entry Point

static int
isp_scsi_getcap(
    struct scsi_address    *ap,
    char                   *cap,
    int                    whom)
{
    struct isp             *isp;
    int                    rval = 0;
    u_char                 tgt = ap->a_target;
    /*
      * We don't allow getting 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:
          rval = 1 << 24; /* Limit to 16MB max transfer */
          break;
      case SCSI_CAP_MSG_OUT:
          rval = 1;
          break;
      case SCSI_CAP_DISCONNECT:
          if ((isp->isp_target_scsi_options[tgt] &
            SCSI_OPTIONS_DR) == 0) {
              break;
          } else if (
              (isp->isp_cap[tgt] & ISP_CAP_DISCONNECT) == 0) {
                  break;
          }
          rval = 1;
          break;
      case SCSI_CAP_SYNCHRONOUS:
          if ((isp->isp_target_scsi_options[tgt] &
            SCSI_OPTIONS_SYNC) == 0) {
              break;
          } else if (
              (isp->isp_cap[tgt] & ISP_CAP_SYNC) == 0) {
                  break;
          }
          rval = 1;
          break;
      case SCSI_CAP_WIDE_XFER:
          if ((isp->isp_target_scsi_options[tgt] &
            SCSI_OPTIONS_WIDE) == 0) {
              break;
          } else if (
              (isp->isp_cap[tgt] & ISP_CAP_WIDE) == 0) {
                  break;
          }
          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 (
              (isp->isp_cap[tgt] & ISP_CAP_TAG) == 0) {
                  break;
          }
          rval = 1;
          break;
      case SCSI_CAP_UNTAGGED_QING:
          rval = 1;
          break;
      case SCSI_CAP_PARITY:
          if (isp->isp_target_scsi_options[tgt] &
            SCSI_OPTIONS_PARITY) {
              rval = 1;
          }
          break;
      case SCSI_CAP_INITIATOR_ID:
          rval = isp->isp_initiator_id;
          break;
      case SCSI_CAP_ARQ:
          if (isp->isp_cap[tgt] & ISP_CAP_AUTOSENSE) {
              rval = 1;
          }
          break;
      case SCSI_CAP_LINKED_CMDS:
          break;
      case SCSI_CAP_RESET_NOTIFICATION:
         rval = 1;
          break;
      case SCSI_CAP_GEOMETRY:
          rval = (64 << 16) | 32;
          break;
      default:
          rval = -1;
          break;
    }
    ISP_MUTEX_EXIT(isp);
    return (rval);
}

tran_setcap() Entry Point

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

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

tran_setcap() should return the following values as appropriate:

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


Example 18–11 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 through
           * 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);
}