Diff for /embedaddon/smartmontools/scsicmds.cpp between versions 1.1.1.1 and 1.1.1.2

version 1.1.1.1, 2012/02/21 16:32:16 version 1.1.1.2, 2013/07/22 01:17:35
Line 7 Line 7
  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>   * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
  *   *
  * Additional SCSI work:   * Additional SCSI work:
 * Copyright (C) 2003-10 Douglas Gilbert <dgilbert@interlog.com> * Copyright (C) 2003-13 Douglas Gilbert <dgilbert@interlog.com>
  *   *
  * This program is free software; you can redistribute it and/or modify   * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by   * it under the terms of the GNU General Public License as published by
Line 15 Line 15
  * any later version.   * any later version.
  *   *
  * You should have received a copy of the GNU General Public License   * You should have received a copy of the GNU General Public License
 * (for example COPYING); if not, write to the Free * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *   *
  * This code was originally developed as a Senior Thesis by Michael Cornwell   * This code was originally developed as a Senior Thesis by Michael Cornwell
  * at the Concurrent Systems Laboratory (now part of the Storage Systems   * at the Concurrent Systems Laboratory (now part of the Storage Systems
Line 55  const char *scsicmds_c_cvsid="$Id$" Line 54  const char *scsicmds_c_cvsid="$Id$"
 // Print SCSI debug messages?  // Print SCSI debug messages?
 unsigned char scsi_debugmode = 0;  unsigned char scsi_debugmode = 0;
   
   supported_vpd_pages * supported_vpd_pages_p = NULL;
   
   
   supported_vpd_pages::supported_vpd_pages(scsi_device * device) : num_valid(0)
   {
       unsigned char b[0x1fc];     /* size chosen for old INQUIRY command */
       int n;
   
       memset(b, 0, sizeof(b));
       if (device && (0 == scsiInquiryVpd(device, SCSI_VPD_SUPPORTED_VPD_PAGES,
                      b, sizeof(b)))) {
           num_valid = (b[2] << 8) + b[3];
           n = sizeof(pages);
           if (num_valid > n)
               num_valid = n;
           memcpy(pages, b + 4, num_valid);
       }
   }
   
   bool
   supported_vpd_pages::is_supported(int vpd_page_num) const
   {
       /* Supported VPD pages numbers start at offset 4 and should be in
        * ascending order but don't assume that. */
       for (int k = 0; k < num_valid; ++k) {
           if (vpd_page_num == pages[k])
               return true;
       }
       return false;
   }
   
 /* output binary in hex and optionally ascii */  /* output binary in hex and optionally ascii */
void dStrHex(const char* str, int len, int no_ascii)void
 dStrHex(const char* str, int len, int no_ascii)
 {  {
     const char* p = str;      const char* p = str;
     unsigned char c;      unsigned char c;
Line 67  void dStrHex(const char* str, int len, int no_ascii) Line 98  void dStrHex(const char* str, int len, int no_ascii)
     int cpos = cpstart;      int cpos = cpstart;
     int bpos = bpstart;      int bpos = bpstart;
     int i, k;      int i, k;
    
     if (len <= 0) return;      if (len <= 0) return;
     memset(buff,' ',80);      memset(buff,' ',80);
     buff[80]='\0';      buff[80]='\0';
    k = sprintf(buff + 1, "%.2x", a);    k = snprintf(buff+1, sizeof(buff)-1, "%.2x", a);
     buff[k + 1] = ' ';      buff[k + 1] = ' ';
     if (bpos >= ((bpstart + (9 * 3))))      if (bpos >= ((bpstart + (9 * 3))))
         bpos++;          bpos++;
Line 82  void dStrHex(const char* str, int len, int no_ascii) Line 113  void dStrHex(const char* str, int len, int no_ascii)
         bpos += 3;          bpos += 3;
         if (bpos == (bpstart + (9 * 3)))          if (bpos == (bpstart + (9 * 3)))
             bpos++;              bpos++;
        sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c);        snprintf(buff+bpos, sizeof(buff)-bpos, "%.2x", (int)(unsigned char)c);
         buff[bpos + 2] = ' ';          buff[bpos + 2] = ' ';
         if (no_ascii)          if (no_ascii)
             buff[cpos++] = ' ';              buff[cpos++] = ' ';
Line 98  void dStrHex(const char* str, int len, int no_ascii) Line 129  void dStrHex(const char* str, int len, int no_ascii)
             cpos = cpstart;              cpos = cpstart;
             a += 16;              a += 16;
             memset(buff,' ',80);              memset(buff,' ',80);
            k = sprintf(buff + 1, "%.2x", a);            k = snprintf(buff+1, sizeof(buff)-1, "%.2x", a);
             buff[k + 1] = ' ';              buff[k + 1] = ' ';
         }          }
     }      }
Line 133  static struct scsi_opcode_name opcode_name_arr[] = { Line 164  static struct scsi_opcode_name opcode_name_arr[] = {
     {READ_CAPACITY_16, "read capacity(16)"},    /* 0x9e,0x10 */      {READ_CAPACITY_16, "read capacity(16)"},    /* 0x9e,0x10 */
     {REPORT_LUNS, "report luns"},               /* 0xa0 */      {REPORT_LUNS, "report luns"},               /* 0xa0 */
     {SAT_ATA_PASSTHROUGH_12, "ata pass-through(12)"}, /* 0xa1 */      {SAT_ATA_PASSTHROUGH_12, "ata pass-through(12)"}, /* 0xa1 */
       {READ_DEFECT_12, "read defect list(12)"},   /* 0xb7 */
 };  };
   
 static const char * vendor_specific = "<vendor specific>";  static const char * vendor_specific = "<vendor specific>";
   
 /* Need to expand to take service action into account. For commands  /* Need to expand to take service action into account. For commands
  * of interest the service action is in the 2nd command byte */   * of interest the service action is in the 2nd command byte */
const char * scsi_get_opcode_name(UINT8 opcode)const char *
 scsi_get_opcode_name(UINT8 opcode)
 {  {
     int k;      int k;
     int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]);      int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]);
Line 157  const char * scsi_get_opcode_name(UINT8 opcode) Line 190  const char * scsi_get_opcode_name(UINT8 opcode)
     return NULL;      return NULL;
 }  }
   
void
void scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf,scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf,
                          struct scsi_sense_disect * out)                     struct scsi_sense_disect * out)
 {  {
     int resp_code;      int resp_code;
   
     memset(out, 0, sizeof(struct scsi_sense_disect));      memset(out, 0, sizeof(struct scsi_sense_disect));
     if (SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) {      if (SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) {
         resp_code = (io_buf->sensep[0] & 0x7f);          resp_code = (io_buf->sensep[0] & 0x7f);
        out->error_code = resp_code;        out->resp_code = resp_code;
         if (resp_code >= 0x72) {          if (resp_code >= 0x72) {
             out->sense_key = (io_buf->sensep[1] & 0xf);              out->sense_key = (io_buf->sensep[1] & 0xf);
             out->asc = io_buf->sensep[2];              out->asc = io_buf->sensep[2];
Line 181  void scsi_do_sense_disect(const struct scsi_cmnd_io *  Line 214  void scsi_do_sense_disect(const struct scsi_cmnd_io * 
     }      }
 }  }
   
int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo)int
 scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo)
 {  {
     switch (sinfo->sense_key) {      switch (sinfo->sense_key) {
     case SCSI_SK_NO_SENSE:      case SCSI_SK_NO_SENSE:
     case SCSI_SK_RECOVERED_ERR:      case SCSI_SK_RECOVERED_ERR:
         return SIMPLE_NO_ERROR;          return SIMPLE_NO_ERROR;
     case SCSI_SK_NOT_READY:      case SCSI_SK_NOT_READY:
        if (SCSI_ASC_NO_MEDIUM == sinfo->asc)         if (SCSI_ASC_NO_MEDIUM == sinfo->asc)
             return SIMPLE_ERR_NO_MEDIUM;              return SIMPLE_ERR_NO_MEDIUM;
         else if (SCSI_ASC_NOT_READY == sinfo->asc) {          else if (SCSI_ASC_NOT_READY == sinfo->asc) {
             if (0x1 == sinfo->ascq)              if (0x1 == sinfo->ascq)
Line 203  int scsiSimpleSenseFilter(const struct scsi_sense_dise Line 237  int scsiSimpleSenseFilter(const struct scsi_sense_dise
     case SCSI_SK_ILLEGAL_REQUEST:      case SCSI_SK_ILLEGAL_REQUEST:
         if (SCSI_ASC_UNKNOWN_OPCODE == sinfo->asc)          if (SCSI_ASC_UNKNOWN_OPCODE == sinfo->asc)
             return SIMPLE_ERR_BAD_OPCODE;              return SIMPLE_ERR_BAD_OPCODE;
        else if (SCSI_ASC_UNKNOWN_FIELD == sinfo->asc)        else if (SCSI_ASC_INVALID_FIELD == sinfo->asc)
             return SIMPLE_ERR_BAD_FIELD;              return SIMPLE_ERR_BAD_FIELD;
         else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc)          else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc)
             return SIMPLE_ERR_BAD_PARAM;              return SIMPLE_ERR_BAD_PARAM;
Line 218  int scsiSimpleSenseFilter(const struct scsi_sense_dise Line 252  int scsiSimpleSenseFilter(const struct scsi_sense_dise
     }      }
 }  }
   
const char * scsiErrString(int scsiErr)const char *
 scsiErrString(int scsiErr)
 {  {
     if (scsiErr < 0)      if (scsiErr < 0)
         return strerror(-scsiErr);          return strerror(-scsiErr);
     switch (scsiErr) {      switch (scsiErr) {
        case SIMPLE_NO_ERROR:         case SIMPLE_NO_ERROR:
             return "no error";              return "no error";
        case SIMPLE_ERR_NOT_READY:         case SIMPLE_ERR_NOT_READY:
             return "device not ready";              return "device not ready";
        case SIMPLE_ERR_BAD_OPCODE:         case SIMPLE_ERR_BAD_OPCODE:
             return "unsupported scsi opcode";              return "unsupported scsi opcode";
        case SIMPLE_ERR_BAD_FIELD:         case SIMPLE_ERR_BAD_FIELD:
             return "unsupported field in scsi command";              return "unsupported field in scsi command";
        case SIMPLE_ERR_BAD_PARAM:         case SIMPLE_ERR_BAD_PARAM:
             return "badly formed scsi parameters";              return "badly formed scsi parameters";
        case SIMPLE_ERR_BAD_RESP:         case SIMPLE_ERR_BAD_RESP:
             return "scsi response fails sanity test";              return "scsi response fails sanity test";
        case SIMPLE_ERR_NO_MEDIUM:         case SIMPLE_ERR_NO_MEDIUM:
             return "no medium present";              return "no medium present";
        case SIMPLE_ERR_BECOMING_READY:         case SIMPLE_ERR_BECOMING_READY:
             return "device will be ready soon";              return "device will be ready soon";
        case SIMPLE_ERR_TRY_AGAIN:         case SIMPLE_ERR_TRY_AGAIN:
             return "unit attention reported, try again";              return "unit attention reported, try again";
        case SIMPLE_ERR_MEDIUM_HARDWARE:         case SIMPLE_ERR_MEDIUM_HARDWARE:
             return "medium or hardware error (serious)";              return "medium or hardware error (serious)";
        case SIMPLE_ERR_UNKNOWN:         case SIMPLE_ERR_UNKNOWN:
             return "unknown error (unexpected sense key)";              return "unknown error (unexpected sense key)";
        case SIMPLE_ERR_ABORTED_COMMAND:         case SIMPLE_ERR_ABORTED_COMMAND:
             return "aborted command";              return "aborted command";
         default:          default:
             return "unknown error";              return "unknown error";
Line 261  const char * scsiErrString(int scsiErr) Line 296  const char * scsiErrString(int scsiErr)
  * descriptor; returns -1 if normal end condition and -2 for an abnormal   * descriptor; returns -1 if normal end condition and -2 for an abnormal
  * termination. Matches association, designator_type and/or code_set when   * termination. Matches association, designator_type and/or code_set when
  * any of those values are greater than or equal to zero. */   * any of those values are greater than or equal to zero. */
int scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc,int
                         int page_len, int * off, int m_assoc,scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len,
                         int m_desig_type, int m_code_set)                     int * off, int m_assoc, int m_desig_type, int m_code_set)
 {  {
     const unsigned char * ucp;      const unsigned char * ucp;
     int k, c_set, assoc, desig_type;      int k, c_set, assoc, desig_type;
Line 290  int scsi_vpd_dev_id_iter(const unsigned char * initial Line 325  int scsi_vpd_dev_id_iter(const unsigned char * initial
 /* Decode VPD page 0x83 logical unit designator into a string. If both  /* Decode VPD page 0x83 logical unit designator into a string. If both
  * numeric address and SCSI name string present, prefer the former.   * numeric address and SCSI name string present, prefer the former.
  * Returns 0 on success, -1 on error with error string in s. */   * Returns 0 on success, -1 on error with error string in s. */
int scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s,int
                          int slen, int * transport)scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
                       int * transport)
 {  {
     int m, c_set, assoc, desig_type, i_len, naa, off, u, have_scsi_ns;      int m, c_set, assoc, desig_type, i_len, naa, off, u, have_scsi_ns;
     const unsigned char * ucp;      const unsigned char * ucp;
     const unsigned char * ip;      const unsigned char * ip;
    char * orig_s = s;    int si = 0;
   
     if (transport)      if (transport)
        *transport = -1;        *transport = -1;
     if (slen < 32) {      if (slen < 32) {
        if (slen > 0)        if (slen > 0)
            s[0] = '\0';            s[0] = '\0';
        return -1;        return -1;
     }      }
     have_scsi_ns = 0;      have_scsi_ns = 0;
     s[0] = '\0';      s[0] = '\0';
Line 312  int scsi_decode_lu_dev_id(const unsigned char * b, int Line 348  int scsi_decode_lu_dev_id(const unsigned char * b, int
         ucp = b + off;          ucp = b + off;
         i_len = ucp[3];          i_len = ucp[3];
         if ((off + i_len + 4) > blen) {          if ((off + i_len + 4) > blen) {
            s += sprintf(s, "error: designator length");            snprintf(s+si, slen-si, "error: designator length");
            return -1;            return -1;
         }          }
         assoc = ((ucp[1] >> 4) & 0x3);          assoc = ((ucp[1] >> 4) & 0x3);
        if (transport && assoc && (ucp[1] & 0x80) && (*transport < 0))        if (transport && assoc && (ucp[1] & 0x80) && (*transport < 0))
            *transport = (ucp[0] >> 4) & 0xf;            *transport = (ucp[0] >> 4) & 0xf;
        if (0 != assoc)        if (0 != assoc)
            continue;            continue;
         ip = ucp + 4;          ip = ucp + 4;
         c_set = (ucp[0] & 0xf);          c_set = (ucp[0] & 0xf);
         desig_type = (ucp[1] & 0xf);          desig_type = (ucp[1] & 0xf);
Line 330  int scsi_decode_lu_dev_id(const unsigned char * b, int Line 366  int scsi_decode_lu_dev_id(const unsigned char * b, int
             break;              break;
         case 2: /* EUI-64 based */          case 2: /* EUI-64 based */
             if ((8 != i_len) && (12 != i_len) && (16 != i_len)) {              if ((8 != i_len) && (12 != i_len) && (16 != i_len)) {
                s += sprintf(s, "error: EUI-64 length");                snprintf(s+si, slen-si, "error: EUI-64 length");
                return -1;                return -1;
            }            }
            if (have_scsi_ns)            if (have_scsi_ns)
                s = orig_s;                si = 0;
            s += sprintf(s, "0x");            si += snprintf(s+si, slen-si, "0x");
             for (m = 0; m < i_len; ++m)              for (m = 0; m < i_len; ++m)
                s += sprintf(s, "%02x", (unsigned int)ip[m]);                si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
             break;              break;
         case 3: /* NAA */          case 3: /* NAA */
             if (1 != c_set) {              if (1 != c_set) {
                s += sprintf(s, "error: NAA bad code_set");                snprintf(s+si, slen-si, "error: NAA bad code_set");
                return -1;                return -1;
            }            }
             naa = (ip[0] >> 4) & 0xff;              naa = (ip[0] >> 4) & 0xff;
             if ((naa < 2) || (naa > 6) || (4 == naa)) {              if ((naa < 2) || (naa > 6) || (4 == naa)) {
                s += sprintf(s, "error: unexpected NAA");                snprintf(s+si, slen-si, "error: unexpected NAA");
                return -1;                return -1;
             }              }
            if (have_scsi_ns)            if (have_scsi_ns)
                s = orig_s;                si = 0;
             if (2 == naa) {             /* NAA IEEE Extended */              if (2 == naa) {             /* NAA IEEE Extended */
                 if (8 != i_len) {                  if (8 != i_len) {
                    s += sprintf(s, "error: NAA 2 length");                    snprintf(s+si, slen-si, "error: NAA 2 length");
                    return -1;                    return -1;
                 }                  }
                s += sprintf(s, "0x");                si += snprintf(s+si, slen-si, "0x");
                 for (m = 0; m < 8; ++m)                  for (m = 0; m < 8; ++m)
                    s += sprintf(s, "%02x", (unsigned int)ip[m]);                    si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
             } else if ((3 == naa ) || (5 == naa)) {              } else if ((3 == naa ) || (5 == naa)) {
                 /* NAA=3 Locally assigned; NAA=5 IEEE Registered */                  /* NAA=3 Locally assigned; NAA=5 IEEE Registered */
                 if (8 != i_len) {                  if (8 != i_len) {
                    s += sprintf(s, "error: NAA 3 or 5 length");                    snprintf(s+si, slen-si, "error: NAA 3 or 5 length");
                    return -1;                    return -1;
                 }                  }
                s += sprintf(s, "0x");                si += snprintf(s+si, slen-si, "0x");
                 for (m = 0; m < 8; ++m)                  for (m = 0; m < 8; ++m)
                    s += sprintf(s, "%02x", (unsigned int)ip[m]);                    si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
             } else if (6 == naa) {      /* NAA IEEE Registered extended */              } else if (6 == naa) {      /* NAA IEEE Registered extended */
                 if (16 != i_len) {                  if (16 != i_len) {
                    s += sprintf(s, "error: NAA 6 length");                    snprintf(s+si, slen-si, "error: NAA 6 length");
                    return -1;                    return -1;
                 }                  }
                s += sprintf(s, "0x");                si += snprintf(s+si, slen-si, "0x");
                 for (m = 0; m < 16; ++m)                  for (m = 0; m < 16; ++m)
                    s += sprintf(s, "%02x", (unsigned int)ip[m]);                    si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
             }              }
             break;              break;
         case 4: /* Relative target port */          case 4: /* Relative target port */
Line 385  int scsi_decode_lu_dev_id(const unsigned char * b, int Line 421  int scsi_decode_lu_dev_id(const unsigned char * b, int
             break;              break;
         case 8: /* SCSI name string */          case 8: /* SCSI name string */
             if (3 != c_set) {              if (3 != c_set) {
                s += sprintf(s, "error: SCSI name string");                snprintf(s+si, slen-si, "error: SCSI name string");
                return -1;                return -1;
             }              }
             /* does %s print out UTF-8 ok?? */              /* does %s print out UTF-8 ok?? */
            if (orig_s == s) {            if (si == 0) {
                s += sprintf(s, "%s", (const char *)ip);                si += snprintf(s+si, slen-si, "%s", (const char *)ip);
                ++have_scsi_ns;                ++have_scsi_ns;
            }            }
             break;              break;
         default: /* reserved */          default: /* reserved */
             break;              break;
         }          }
     }      }
     if (-2 == u) {      if (-2 == u) {
        s += sprintf(s, "error: bad structure");        snprintf(s+si, slen-si, "error: bad structure");
        return -1;        return -1;
     }      }
     return 0;      return 0;
 }  }
Line 412  int scsi_decode_lu_dev_id(const unsigned char * b, int Line 448  int scsi_decode_lu_dev_id(const unsigned char * b, int
    If known_resp_len > 0 then a single fetch is done for this response     If known_resp_len > 0 then a single fetch is done for this response
    length. If known_resp_len == 0 then twin fetches are performed, the     length. If known_resp_len == 0 then twin fetches are performed, the
    first to deduce the response length, then send the same command again     first to deduce the response length, then send the same command again
   requesting the deduced response length. This protects certain fragile    requesting the deduced response length. This protects certain fragile
    HBAs. The twin fetch technique should not be used with the TapeAlert     HBAs. The twin fetch technique should not be used with the TapeAlert
    log page since it clears its state flags after each fetch. */     log page since it clears its state flags after each fetch. */
int scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,int
                 int bufLen, int known_resp_len)scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
              int bufLen, int known_resp_len)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
Line 468  int scsiLogSense(scsi_device * device, int pagenum, in Line 505  int scsiLogSense(scsi_device * device, int pagenum, in
             pageLen = 252; /* some IBM tape drives don't like double fetch */              pageLen = 252; /* some IBM tape drives don't like double fetch */
         /* some SCSI HBA don't like "odd" length transfers */          /* some SCSI HBA don't like "odd" length transfers */
         if (pageLen % 2)          if (pageLen % 2)
            pageLen += 1;               pageLen += 1;
         if (pageLen > bufLen)          if (pageLen > bufLen)
             pageLen = bufLen;              pageLen = bufLen;
     }      }
Line 507  int scsiLogSense(scsi_device * device, int pagenum, in Line 544  int scsiLogSense(scsi_device * device, int pagenum, in
  * Returns 0 if ok, 1 if NOT READY, 2 if command not supported, * 3 if   * Returns 0 if ok, 1 if NOT READY, 2 if command not supported, * 3 if
  * field in command not supported, * 4 if bad parameter to command or   * field in command not supported, * 4 if bad parameter to command or
  * returns negated errno. SPC-4 sections 6.5 and 7.2 (rev 20) */   * returns negated errno. SPC-4 sections 6.5 and 7.2 (rev 20) */
int scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,int
                  int subpagenum, UINT8 *pBuf, int bufLen)scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,
               int subpagenum, UINT8 *pBuf, int bufLen)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
Line 540  int scsiLogSelect(scsi_device * device, int pcr, int s Line 578  int scsiLogSelect(scsi_device * device, int pcr, int s
   
 /* Send MODE SENSE (6 byte) command. Returns 0 if ok, 1 if NOT READY,  /* Send MODE SENSE (6 byte) command. Returns 0 if ok, 1 if NOT READY,
  * 2 if command not supported (then MODE SENSE(10) should be supported),   * 2 if command not supported (then MODE SENSE(10) should be supported),
 * 3 if field in command not supported or returns negated errno.  * 3 if field in command not supported or returns negated errno.
  * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */   * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */
int scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,int
                  UINT8 *pBuf, int bufLen)scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
               UINT8 *pBuf, int bufLen)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
Line 594  int scsiModeSense(scsi_device * device, int pagenum, i Line 633  int scsiModeSense(scsi_device * device, int pagenum, i
  * from a corresponding 6 byte MODE SENSE command. Such a response should   * from a corresponding 6 byte MODE SENSE command. Such a response should
  * have a 4 byte header followed by 0 or more 8 byte block descriptors   * have a 4 byte header followed by 0 or more 8 byte block descriptors
  * (normally 1) and then 1 mode page. Returns 0 if ok, 1 if NOT READY,   * (normally 1) and then 1 mode page. Returns 0 if ok, 1 if NOT READY,
 * 2 if command not supported (then MODE SELECT(10) may be supported),  * 2 if command not supported (then MODE SELECT(10) may be supported),
  * 3 if field in command not supported, 4 if bad parameter to command   * 3 if field in command not supported, 4 if bad parameter to command
  * or returns negated errno. SPC-3 sections 6.7 and 7.4 (rev 22a) */   * or returns negated errno. SPC-3 sections 6.7 and 7.4 (rev 22a) */
int scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)int
 scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
Line 634  int scsiModeSelect(scsi_device * device, int sp, UINT8 Line 674  int scsiModeSelect(scsi_device * device, int sp, UINT8
     return scsiSimpleSenseFilter(&sinfo);      return scsiSimpleSenseFilter(&sinfo);
 }  }
   
/* MODE SENSE (10 byte). Returns 0 if ok, 1 if NOT READY, 2 if command /* MODE SENSE (10 byte). Returns 0 if ok, 1 if NOT READY, 2 if command
  * not supported (then MODE SENSE(6) might be supported), 3 if field in   * not supported (then MODE SENSE(6) might be supported), 3 if field in
 * command not supported or returns negated errno.   * command not supported or returns negated errno.
  * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */   * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */
int scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,int
                    UINT8 *pBuf, int bufLen)scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
                 UINT8 *pBuf, int bufLen)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
Line 688  int scsiModeSense10(scsi_device * device, int pagenum, Line 729  int scsiModeSense10(scsi_device * device, int pagenum,
 /* Sends a 10 byte MODE SELECT command. Assumes given pBuf is the response  /* Sends a 10 byte MODE SELECT command. Assumes given pBuf is the response
  * from a corresponding 10 byte MODE SENSE command. Such a response should   * from a corresponding 10 byte MODE SENSE command. Such a response should
  * have a 8 byte header followed by 0 or more 8 byte block descriptors   * have a 8 byte header followed by 0 or more 8 byte block descriptors
 * (normally 1) and then 1 mode page. Returns 0 if ok, 1 NOT REAFY, 2 if  * (normally 1) and then 1 mode page. Returns 0 if ok, 1 NOT REAFY, 2 if
  * command not supported (then MODE SELECT(6) may be supported), 3 if field   * command not supported (then MODE SELECT(6) may be supported), 3 if field
  * in command not supported, 4 if bad parameter to command or returns   * in command not supported, 4 if bad parameter to command or returns
  * negated errno. SPC-3 sections 6.8 and 7.4 (rev 22a) */   * negated errno. SPC-3 sections 6.8 and 7.4 (rev 22a) */
int scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)int
 scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
Line 707  int scsiModeSelect10(scsi_device * device, int sp, UIN Line 749  int scsiModeSelect10(scsi_device * device, int sp, UIN
     hdr_plus_1_pg = pg_offset + pg_len;      hdr_plus_1_pg = pg_offset + pg_len;
     if (hdr_plus_1_pg > bufLen)      if (hdr_plus_1_pg > bufLen)
         return -EINVAL;          return -EINVAL;
    pBuf[0] = 0;        pBuf[0] = 0;
     pBuf[1] = 0; /* Length of returned mode sense data reserved for SELECT */      pBuf[1] = 0; /* Length of returned mode sense data reserved for SELECT */
     pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */      pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
     memset(&io_hdr, 0, sizeof(io_hdr));      memset(&io_hdr, 0, sizeof(io_hdr));
Line 733  int scsiModeSelect10(scsi_device * device, int sp, UIN Line 775  int scsiModeSelect10(scsi_device * device, int sp, UIN
 /* Standard INQUIRY returns 0 for ok, anything else is a major problem.  /* Standard INQUIRY returns 0 for ok, anything else is a major problem.
  * bufLen should be 36 for unsafe devices (like USB mass storage stuff)   * bufLen should be 36 for unsafe devices (like USB mass storage stuff)
  * otherwise they can lock up! SPC-3 sections 6.4 and 7.6 (rev 22a) */   * otherwise they can lock up! SPC-3 sections 6.4 and 7.6 (rev 22a) */
int scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen)int
 scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen)
 {  {
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     UINT8 cdb[6];      UINT8 cdb[6];
     UINT8 sense[32];      UINT8 sense[32];
   
    if ((bufLen < 0) || (bufLen > 255))    if ((bufLen < 0) || (bufLen > 1023))
         return -EINVAL;          return -EINVAL;
     memset(&io_hdr, 0, sizeof(io_hdr));      memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));      memset(cdb, 0, sizeof(cdb));
Line 748  int scsiStdInquiry(scsi_device * device, UINT8 *pBuf,  Line 791  int scsiStdInquiry(scsi_device * device, UINT8 *pBuf, 
     io_hdr.dxfer_len = bufLen;      io_hdr.dxfer_len = bufLen;
     io_hdr.dxferp = pBuf;      io_hdr.dxferp = pBuf;
     cdb[0] = INQUIRY;      cdb[0] = INQUIRY;
    cdb[4] = bufLen;    cdb[3] = (bufLen >> 8) & 0xff;
     cdb[4] = (bufLen & 0xff);
     io_hdr.cmnd = cdb;      io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);      io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;      io_hdr.sensep = sense;
Line 762  int scsiStdInquiry(scsi_device * device, UINT8 *pBuf,  Line 806  int scsiStdInquiry(scsi_device * device, UINT8 *pBuf, 
 }  }
   
 /* INQUIRY to fetch Vital Page Data.  Returns 0 if ok, 1 if NOT READY  /* INQUIRY to fetch Vital Page Data.  Returns 0 if ok, 1 if NOT READY
 * (unlikely), 2 if command not supported, 3 if field in command not  * (unlikely), 2 if command not supported, 3 if field in command not
  * supported, 5 if response indicates that EVPD bit ignored or returns   * supported, 5 if response indicates that EVPD bit ignored or returns
  * negated errno. SPC-3 section 6.4 and 7.6 (rev 22a) */   * negated errno. SPC-3 section 6.4 and 7.6 (rev 22a) */
int scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen)int
 scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
Line 773  int scsiInquiryVpd(scsi_device * device, int vpd_page, Line 818  int scsiInquiryVpd(scsi_device * device, int vpd_page,
     UINT8 sense[32];      UINT8 sense[32];
     int res;      int res;
   
    if ((bufLen < 0) || (bufLen > 255))    /* Assume SCSI_VPD_SUPPORTED_VPD_PAGES is first VPD page fetched */
     if ((SCSI_VPD_SUPPORTED_VPD_PAGES != vpd_page) &&
         supported_vpd_pages_p &&
         (! supported_vpd_pages_p->is_supported(vpd_page)))
         return 3;
 
     if ((bufLen < 0) || (bufLen > 1023))
         return -EINVAL;          return -EINVAL;
   try_again:
     memset(&io_hdr, 0, sizeof(io_hdr));      memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));      memset(cdb, 0, sizeof(cdb));
     if (bufLen > 1)      if (bufLen > 1)
Line 785  int scsiInquiryVpd(scsi_device * device, int vpd_page, Line 837  int scsiInquiryVpd(scsi_device * device, int vpd_page,
     cdb[0] = INQUIRY;      cdb[0] = INQUIRY;
     cdb[1] = 0x1;       /* set EVPD bit (enable Vital Product Data) */      cdb[1] = 0x1;       /* set EVPD bit (enable Vital Product Data) */
     cdb[2] = vpd_page;      cdb[2] = vpd_page;
    cdb[4] = bufLen;    cdb[3] = (bufLen >> 8) & 0xff;
     cdb[4] = (bufLen & 0xff);
     io_hdr.cmnd = cdb;      io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);      io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;      io_hdr.sensep = sense;
Line 795  int scsiInquiryVpd(scsi_device * device, int vpd_page, Line 848  int scsiInquiryVpd(scsi_device * device, int vpd_page,
     if (!device->scsi_pass_through(&io_hdr))      if (!device->scsi_pass_through(&io_hdr))
       return -device->get_errno();        return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);      scsi_do_sense_disect(&io_hdr, &sinfo);
       if ((SCSI_STATUS_CHECK_CONDITION == io_hdr.scsi_status) &&
           (SCSI_SK_ILLEGAL_REQUEST == sinfo.sense_key) &&
           (SCSI_ASC_INVALID_FIELD == sinfo.asc) &&
           (cdb[3] > 0)) {
           bufLen &= 0xff; /* make sure cdb[3] is 0 next time around */
           goto try_again;
       }
   
     if ((res = scsiSimpleSenseFilter(&sinfo)))      if ((res = scsiSimpleSenseFilter(&sinfo)))
         return res;          return res;
     /* Guard against devices that ignore EVPD bit and do standard INQUIRY */      /* Guard against devices that ignore EVPD bit and do standard INQUIRY */
Line 810  int scsiInquiryVpd(scsi_device * device, int vpd_page, Line 871  int scsiInquiryVpd(scsi_device * device, int vpd_page,
   
 /* REQUEST SENSE command. Returns 0 if ok, anything else major problem.  /* REQUEST SENSE command. Returns 0 if ok, anything else major problem.
  * SPC-3 section 6.27 (rev 22a) */   * SPC-3 section 6.27 (rev 22a) */
int scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)int
 scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     UINT8 cdb[6];      UINT8 cdb[6];
     UINT8 sense[32];      UINT8 sense[32];
     UINT8 buff[18];      UINT8 buff[18];
     int len;      int len;
    UINT8 ecode;    UINT8 resp_code;
   
     memset(&io_hdr, 0, sizeof(io_hdr));      memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));      memset(cdb, 0, sizeof(cdb));
Line 835  int scsiRequestSense(scsi_device * device, struct scsi Line 897  int scsiRequestSense(scsi_device * device, struct scsi
     if (!device->scsi_pass_through(&io_hdr))      if (!device->scsi_pass_through(&io_hdr))
       return -device->get_errno();        return -device->get_errno();
     if (sense_info) {      if (sense_info) {
        ecode = buff[0] & 0x7f;        resp_code = buff[0] & 0x7f;
        sense_info->error_code = ecode;        sense_info->resp_code = resp_code;
         sense_info->sense_key = buff[2] & 0xf;          sense_info->sense_key = buff[2] & 0xf;
         sense_info->asc = 0;          sense_info->asc = 0;
         sense_info->ascq = 0;          sense_info->ascq = 0;
        if ((0x70 == ecode) || (0x71 == ecode)) {        if ((0x70 == resp_code) || (0x71 == resp_code)) {
             len = buff[7] + 8;              len = buff[7] + 8;
             if (len > 13) {              if (len > 13) {
                 sense_info->asc = buff[12];                  sense_info->asc = buff[12];
                 sense_info->ascq = buff[13];                  sense_info->ascq = buff[13];
             }              }
         }          }
       // fill progrss indicator, if available
       sense_info->progress = -1;
       switch (resp_code) {
         const unsigned char * ucp;
         int sk, sk_pr;
         case 0x70:
         case 0x71:
             sk = (buff[2] & 0xf);
             if ((sizeof(buff) < 18) ||
                 ((SCSI_SK_NO_SENSE != sk) && (SCSI_SK_NOT_READY != sk))) {
                 break;
             }
             if (buff[15] & 0x80) {        /* SKSV bit set */
                 sense_info->progress = (buff[16] << 8) + buff[17];
                 break;
             } else {
                 break;
             }
         case 0x72:
         case 0x73:
             /* sense key specific progress (0x2) or progress descriptor (0xa) */
             sk = (buff[1] & 0xf);
             sk_pr = (SCSI_SK_NO_SENSE == sk) || (SCSI_SK_NOT_READY == sk);
             if (sk_pr && ((ucp = sg_scsi_sense_desc_find(buff, sizeof(buff), 2))) &&
                 (0x6 == ucp[1]) && (0x80 & ucp[4])) {
                 sense_info->progress = (ucp[5] << 8) + ucp[6];
                 break;
             } else if (((ucp = sg_scsi_sense_desc_find(buff, sizeof(buff), 0xa))) &&
                        ((0x6 == ucp[1]))) {
                 sense_info->progress = (ucp[6] << 8) + ucp[7];
                 break;
             } else
                 break;
         default:
             return 0;
         }
     }      }
     return 0;      return 0;
 }  }
Line 854  int scsiRequestSense(scsi_device * device, struct scsi Line 952  int scsiRequestSense(scsi_device * device, struct scsi
 /* SEND DIAGNOSTIC command.  Returns 0 if ok, 1 if NOT READY, 2 if command  /* SEND DIAGNOSTIC command.  Returns 0 if ok, 1 if NOT READY, 2 if command
  * not supported, 3 if field in command not supported or returns negated   * not supported, 3 if field in command not supported or returns negated
  * errno. SPC-3 section 6.28 (rev 22a) */   * errno. SPC-3 section 6.28 (rev 22a) */
int scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf, int bufLen)int
 scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf,
                    int bufLen)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
Line 881  int scsiSendDiagnostic(scsi_device * device, int funct Line 981  int scsiSendDiagnostic(scsi_device * device, int funct
     io_hdr.max_sense_len = sizeof(sense);      io_hdr.max_sense_len = sizeof(sense);
     /* worst case is an extended foreground self test on a big disk */      /* worst case is an extended foreground self test on a big disk */
     io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST;      io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST;
    
     if (!device->scsi_pass_through(&io_hdr))      if (!device->scsi_pass_through(&io_hdr))
       return -device->get_errno();        return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);      scsi_do_sense_disect(&io_hdr, &sinfo);
Line 891  int scsiSendDiagnostic(scsi_device * device, int funct Line 991  int scsiSendDiagnostic(scsi_device * device, int funct
 /* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if  /* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if
  * command not supported, 3 if field in command not supported or returns   * command not supported, 3 if field in command not supported or returns
  * negated errno. SPC-3 section 6.18 (rev 22a) */   * negated errno. SPC-3 section 6.18 (rev 22a) */
int scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf,int
 scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf,
                       int bufLen)                        int bufLen)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
Line 922  int scsiReceiveDiagnostic(scsi_device * device, int pc Line 1023  int scsiReceiveDiagnostic(scsi_device * device, int pc
 }  }
   
 /* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */  /* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */
static int _testunitready(scsi_device * device, struct scsi_sense_disect * sinfo)static int
 _testunitready(scsi_device * device, struct scsi_sense_disect * sinfo)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     UINT8 cdb[6];      UINT8 cdb[6];
Line 948  static int _testunitready(scsi_device * device, struct Line 1050  static int _testunitready(scsi_device * device, struct
   
 /* Returns 0 for device responds and media ready, 1 for device responds and  /* Returns 0 for device responds and media ready, 1 for device responds and
    media not ready, or returns a negated errno value */     media not ready, or returns a negated errno value */
int scsiTestUnitReady(scsi_device * device)int
 scsiTestUnitReady(scsi_device * device)
 {  {
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
     int status;      int status;
Line 959  int scsiTestUnitReady(scsi_device * device) Line 1062  int scsiTestUnitReady(scsi_device * device)
     status = scsiSimpleSenseFilter(&sinfo);      status = scsiSimpleSenseFilter(&sinfo);
     if (SIMPLE_ERR_TRY_AGAIN == status) {      if (SIMPLE_ERR_TRY_AGAIN == status) {
         /* power on reset, media changed, ok ... try again */          /* power on reset, media changed, ok ... try again */
        status = _testunitready(device, &sinfo);                status = _testunitready(device, &sinfo);
         if (0 != status)          if (0 != status)
             return status;              return status;
         status = scsiSimpleSenseFilter(&sinfo);          status = scsiSimpleSenseFilter(&sinfo);
Line 970  int scsiTestUnitReady(scsi_device * device) Line 1073  int scsiTestUnitReady(scsi_device * device)
 /* READ DEFECT (10) command. Returns 0 if ok, 1 if NOT READY, 2 if  /* READ DEFECT (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
  * command not supported, 3 if field in command not supported or returns   * command not supported, 3 if field in command not supported or returns
  * negated errno. SBC-2 section 5.12 (rev 16) */   * negated errno. SBC-2 section 5.12 (rev 16) */
int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, int dl_format,int
                     UINT8 *pBuf, int bufLen)scsiReadDefect10(scsi_device * device, int req_plist, int req_glist,
                  int dl_format, UINT8 *pBuf, int bufLen)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
Line 1000  int scsiReadDefect10(scsi_device * device, int req_pli Line 1104  int scsiReadDefect10(scsi_device * device, int req_pli
     return scsiSimpleSenseFilter(&sinfo);      return scsiSimpleSenseFilter(&sinfo);
 }  }
   
   /* READ DEFECT (12) command. Returns 0 if ok, 1 if NOT READY, 2 if
    * command not supported, 3 if field in command not supported or returns
    * negated errno. SBC-3 section 5.18 (rev 35; vale Mark Evans) */
   int
   scsiReadDefect12(scsi_device * device, int req_plist, int req_glist,
                    int dl_format, int addrDescIndex, UINT8 *pBuf, int bufLen)
   {
       struct scsi_cmnd_io io_hdr;
       struct scsi_sense_disect sinfo;
       UINT8 cdb[12];
       UINT8 sense[32];
   
       memset(&io_hdr, 0, sizeof(io_hdr));
       memset(cdb, 0, sizeof(cdb));
       io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
       io_hdr.dxfer_len = bufLen;
       io_hdr.dxferp = pBuf;
       cdb[0] = READ_DEFECT_12;
       cdb[1] = (unsigned char)(((req_plist << 4) & 0x10) |
                  ((req_glist << 3) & 0x8) | (dl_format & 0x7));
       cdb[2] = (addrDescIndex >> 24) & 0xff;
       cdb[3] = (addrDescIndex >> 16) & 0xff;
       cdb[4] = (addrDescIndex >> 8) & 0xff;
       cdb[5] = addrDescIndex & 0xff;
       cdb[6] = (bufLen >> 24) & 0xff;
       cdb[7] = (bufLen >> 16) & 0xff;
       cdb[8] = (bufLen >> 8) & 0xff;
       cdb[9] = bufLen & 0xff;
       io_hdr.cmnd = cdb;
       io_hdr.cmnd_len = sizeof(cdb);
       io_hdr.sensep = sense;
       io_hdr.max_sense_len = sizeof(sense);
       io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
   
       if (!device->scsi_pass_through(&io_hdr))
         return -device->get_errno();
       scsi_do_sense_disect(&io_hdr, &sinfo);
       return scsiSimpleSenseFilter(&sinfo);
   }
   
 /* READ CAPACITY (10) command. Returns 0 if ok, 1 if NOT READY, 2 if  /* READ CAPACITY (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
  * command not supported, 3 if field in command not supported or returns   * command not supported, 3 if field in command not supported or returns
  * negated errno. SBC-3 section 5.15 (rev 26) */   * negated errno. SBC-3 section 5.15 (rev 26) */
int scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap,int
                       unsigned int * lb_sizep)scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap,
                    unsigned int * lb_sizep)
 {  {
     int res;      int res;
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
Line 1044  int scsiReadCapacity10(scsi_device * device, unsigned  Line 1189  int scsiReadCapacity10(scsi_device * device, unsigned 
 /* READ CAPACITY (16) command. The bufLen argument should be 32. Returns 0  /* READ CAPACITY (16) command. The bufLen argument should be 32. Returns 0
  * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command   * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command
  * not supported or returns negated errno. SBC-3 section 5.16 (rev 26) */   * not supported or returns negated errno. SBC-3 section 5.16 (rev 26) */
int scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen)int
 scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen)
 {  {
     struct scsi_cmnd_io io_hdr;      struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;      struct scsi_sense_disect sinfo;
Line 1077  int scsiReadCapacity16(scsi_device * device, UINT8 *pB Line 1223  int scsiReadCapacity16(scsi_device * device, UINT8 *pB
 /* Return number of bytes of storage in 'device' or 0 if error. If  /* Return number of bytes of storage in 'device' or 0 if error. If
  * successful and lb_sizep is not NULL then the logical block size   * successful and lb_sizep is not NULL then the logical block size
  * in bytes is written to the location pointed to by lb_sizep. */   * in bytes is written to the location pointed to by lb_sizep. */
uint64_t scsiGetSize(scsi_device * device, unsigned int * lb_sizep)uint64_t
 scsiGetSize(scsi_device * device, unsigned int * lb_sizep,
             int * lb_per_pb_expp)
 {  {
     unsigned int last_lba, lb_size;      unsigned int last_lba, lb_size;
     int k, res;      int k, res;
Line 1086  uint64_t scsiGetSize(scsi_device * device, unsigned in Line 1234  uint64_t scsiGetSize(scsi_device * device, unsigned in
   
     res = scsiReadCapacity10(device, &last_lba, &lb_size);      res = scsiReadCapacity10(device, &last_lba, &lb_size);
     if (res) {      if (res) {
        if (scsi_debugmode)        if (scsi_debugmode)
            pout("scsiGetSize: READ CAPACITY(10) failed, res=%d\n", res);            pout("scsiGetSize: READ CAPACITY(10) failed, res=%d\n", res);
        return ret_val;        return 0;
     }      }
     if (0xffffffff == last_lba) {      if (0xffffffff == last_lba) {
        res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp));        res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp));
         if (res) {          if (res) {
            if (scsi_debugmode)            if (scsi_debugmode)
                pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);                pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);
            return ret_val;            return 0;
        }        }
        for (k = 0; k < 8; ++k) {        for (k = 0; k < 8; ++k) {
            if (k > 0)            if (k > 0)
                 ret_val <<= 8;                  ret_val <<= 8;
             ret_val |= rc16resp[k + 0];              ret_val |= rc16resp[k + 0];
         }          }
    } else        if (lb_per_pb_expp)
        ret_val = last_lba;            *lb_per_pb_expp = (rc16resp[13] & 0xf);
     } else {
         ret_val = last_lba;
         if (lb_per_pb_expp)
             *lb_per_pb_expp = 0;
     }
     if (lb_sizep)      if (lb_sizep)
        *lb_sizep = lb_size;        *lb_sizep = lb_size;
    ++ret_val;  /* last_lba is origin 0 so need to bump to get number of */    ++ret_val;  /* last_lba is origin 0 so need to bump to get number of */
     return ret_val * lb_size;      return ret_val * lb_size;
 }  }
   
   /* Gets drive Protection and Logical/Physical block information. Writes
    * back bytes 12 to 31 from a READ CAPACITY 16 command to the rc16_12_31p
    * pointer. So rc16_12_31p should point to an array of 20 bytes. Returns 0
    * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command
    * not supported or returns negated errno. */
   int
   scsiGetProtPBInfo(scsi_device * device, unsigned char * rc16_12_31p)
   {
       int res;
       UINT8 rc16resp[32];
   
       res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp));
       if (res) {
           if (scsi_debugmode)
               pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);
           return res;
       }
       if (rc16_12_31p)
           memcpy(rc16_12_31p, rc16resp + 12, 20);
       return 0;
   }
   
 /* Offset into mode sense (6 or 10 byte) response that actual mode page  /* Offset into mode sense (6 or 10 byte) response that actual mode page
  * starts at (relative to resp[0]). Returns -1 if problem */   * starts at (relative to resp[0]). Returns -1 if problem */
int scsiModePageOffset(const UINT8 * resp, int len, int modese_len)int
 scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
 {  {
     int resp_len, bd_len;      int resp_len, bd_len;
     int offset = -1;      int offset = -1;
Line 1155  int scsiModePageOffset(const UINT8 * resp, int len, in Line 1330  int scsiModePageOffset(const UINT8 * resp, int len, in
  * tries a 10 byte MODE SENSE command. Returns 0 if successful, a positive   * tries a 10 byte MODE SENSE command. Returns 0 if successful, a positive
  * number if a known error (see  SIMPLE_ERR_ ...) or a negative errno   * number if a known error (see  SIMPLE_ERR_ ...) or a negative errno
  * value. */   * value. */
int scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp, int modese_len)int
 scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp,
                   int modese_len)
 {  {
     int err = 0;      int err = 0;
   
Line 1183  int scsiFetchIECmpage(scsi_device * device, struct scs Line 1360  int scsiFetchIECmpage(scsi_device * device, struct scs
             iecp->modese_len = 0;              iecp->modese_len = 0;
             return err;              return err;
         }          }
    }     }
     iecp->gotCurrent = 1;      iecp->gotCurrent = 1;
     iecp->requestedChangeable = 1;      iecp->requestedChangeable = 1;
     if (10 == iecp->modese_len)      if (10 == iecp->modese_len)
Line 1191  int scsiFetchIECmpage(scsi_device * device, struct scs Line 1368  int scsiFetchIECmpage(scsi_device * device, struct scs
                               0, MPAGE_CONTROL_CHANGEABLE,                                0, MPAGE_CONTROL_CHANGEABLE,
                               iecp->raw_chg, sizeof(iecp->raw_chg));                                iecp->raw_chg, sizeof(iecp->raw_chg));
     else if (6 == iecp->modese_len)      else if (6 == iecp->modese_len)
        err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,         err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
                            0, MPAGE_CONTROL_CHANGEABLE,                             0, MPAGE_CONTROL_CHANGEABLE,
                             iecp->raw_chg, sizeof(iecp->raw_chg));                              iecp->raw_chg, sizeof(iecp->raw_chg));
     if (err)      if (err)
         return err;          return err;
Line 1200  int scsiFetchIECmpage(scsi_device * device, struct scs Line 1377  int scsiFetchIECmpage(scsi_device * device, struct scs
     return 0;      return 0;
 }  }
   
int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp)int
 scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp)
 {  {
     int offset;      int offset;
   
Line 1215  int scsi_IsExceptionControlEnabled(const struct scsi_i Line 1393  int scsi_IsExceptionControlEnabled(const struct scsi_i
         return 0;          return 0;
 }  }
   
int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp)int
 scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp)
 {  {
     int offset;      int offset;
   
Line 1231  int scsi_IsWarningEnabled(const struct scsi_iec_mode_p Line 1410  int scsi_IsWarningEnabled(const struct scsi_iec_mode_p
 }  }
   
 /* set EWASC and clear PERF, EBF, DEXCPT TEST and LOGERR */  /* set EWASC and clear PERF, EBF, DEXCPT TEST and LOGERR */
#define SCSI_IEC_MP_BYTE2_ENABLED 0x10 #define SCSI_IEC_MP_BYTE2_ENABLED 0x10
 #define SCSI_IEC_MP_BYTE2_TEST_MASK 0x4  #define SCSI_IEC_MP_BYTE2_TEST_MASK 0x4
 /* exception/warning via an unrequested REQUEST SENSE command */  /* exception/warning via an unrequested REQUEST SENSE command */
#define SCSI_IEC_MP_MRIE 6      #define SCSI_IEC_MP_MRIE 6
 #define SCSI_IEC_MP_INTERVAL_T 0  #define SCSI_IEC_MP_INTERVAL_T 0
 #define SCSI_IEC_MP_REPORT_COUNT 1  #define SCSI_IEC_MP_REPORT_COUNT 1
   
Line 1245  int scsi_IsWarningEnabled(const struct scsi_iec_mode_p Line 1424  int scsi_IsWarningEnabled(const struct scsi_iec_mode_p
  * is to be re-examined.   * is to be re-examined.
  * When -r ioctl is invoked 3 or more time on 'smartctl -s on ...'   * When -r ioctl is invoked 3 or more time on 'smartctl -s on ...'
  * then set the TEST bit (causes asc,ascq pair of 0x5d,0xff). */   * then set the TEST bit (causes asc,ascq pair of 0x5d,0xff). */
int scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,int
                                      const struct scsi_iec_mode_page *iecp)scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
                                   const struct scsi_iec_mode_page *iecp)
 {  {
     int k, offset, resp_len;      int k, offset, resp_len;
     int err = 0;      int err = 0;
Line 1304  int scsiSetExceptionControlAndWarning(scsi_device * de Line 1484  int scsiSetExceptionControlAndWarning(scsi_device * de
                 pout("scsiSetExceptionControlAndWarning: already disabled\n");                  pout("scsiSetExceptionControlAndWarning: already disabled\n");
             return 0;   /* nothing to do, leave other setting alone */              return 0;   /* nothing to do, leave other setting alone */
         }          }
        if (wEnabled)         if (wEnabled)
             rout[offset + 2] &= EWASC_DISABLE;              rout[offset + 2] &= EWASC_DISABLE;
         if (eCEnabled) {          if (eCEnabled) {
            if (iecp->gotChangeable &&             if (iecp->gotChangeable &&
                 (iecp->raw_chg[offset + 2] & DEXCPT_ENABLE))                  (iecp->raw_chg[offset + 2] & DEXCPT_ENABLE))
                 rout[offset + 2] |= DEXCPT_ENABLE;                  rout[offset + 2] |= DEXCPT_ENABLE;
                 rout[offset + 2] &= TEST_DISABLE;/* clear TEST bit for spec */                  rout[offset + 2] &= TEST_DISABLE;/* clear TEST bit for spec */
Line 1320  int scsiSetExceptionControlAndWarning(scsi_device * de Line 1500  int scsiSetExceptionControlAndWarning(scsi_device * de
     return err;      return err;
 }  }
   
int scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp)int
 scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp)
 {  {
     UINT8 tBuf[252];      UINT8 tBuf[252];
     int err;      int err;
Line 1342  int scsiGetTemp(scsi_device * device, UINT8 *currentte Line 1523  int scsiGetTemp(scsi_device * device, UINT8 *currentte
  * Fetching asc/ascq code potentially flagging an exception or warning.   * Fetching asc/ascq code potentially flagging an exception or warning.
  * Returns 0 if ok, else error number. A current temperature of 255   * Returns 0 if ok, else error number. A current temperature of 255
  * (Celsius) implies that the temperature not available. */   * (Celsius) implies that the temperature not available. */
int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,int
                UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp,scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
                UINT8 *triptemp)            UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp)
 {  {
     UINT8 tBuf[252];      UINT8 tBuf[252];
     struct scsi_sense_disect sense_info;      struct scsi_sense_disect sense_info;
Line 1352  int scsiCheckIE(scsi_device * device, int hasIELogPage Line 1533  int scsiCheckIE(scsi_device * device, int hasIELogPage
     int temperatureSet = 0;      int temperatureSet = 0;
     unsigned short pagesize;      unsigned short pagesize;
     UINT8 currTemp, trTemp;      UINT8 currTemp, trTemp;
 
     *asc = 0;      *asc = 0;
     *ascq = 0;      *ascq = 0;
     *currenttemp = 0;      *currenttemp = 0;
Line 1366  int scsiCheckIE(scsi_device * device, int hasIELogPage Line 1547  int scsiCheckIE(scsi_device * device, int hasIELogPage
             return err;              return err;
         }          }
         // pull out page size from response, don't forget to add 4          // pull out page size from response, don't forget to add 4
        pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4;         pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4;
         if ((pagesize < 4) || tBuf[4] || tBuf[5]) {          if ((pagesize < 4) || tBuf[4] || tBuf[5]) {
             pout("Log Sense failed, IE page, bad parameter code or length\n");              pout("Log Sense failed, IE page, bad parameter code or length\n");
             return SIMPLE_ERR_BAD_PARAM;              return SIMPLE_ERR_BAD_PARAM;
         }          }
         if (tBuf[7] > 1) {          if (tBuf[7] > 1) {
            sense_info.asc = tBuf[8];             sense_info.asc = tBuf[8];
             sense_info.ascq = tBuf[9];              sense_info.ascq = tBuf[9];
             if (! hasTempLogPage) {              if (! hasTempLogPage) {
                if (tBuf[7] > 2)                 if (tBuf[7] > 2)
                     *currenttemp = tBuf[10];                      *currenttemp = tBuf[10];
                 if (tBuf[7] > 3)        /* IBM extension in SMART (IE) lpage */                  if (tBuf[7] > 3)        /* IBM extension in SMART (IE) lpage */
                     *triptemp = tBuf[11];                      *triptemp = tBuf[11];
             }              }
        }         }
     }      }
    if (0 == sense_info.asc) {        if (0 == sense_info.asc) {
         /* ties in with MRIE field of 6 in IEC mode page (0x1c) */          /* ties in with MRIE field of 6 in IEC mode page (0x1c) */
         if ((err = scsiRequestSense(device, &sense_info))) {          if ((err = scsiRequestSense(device, &sense_info))) {
             pout("Request Sense failed, [%s]\n", scsiErrString(err));              pout("Request Sense failed, [%s]\n", scsiErrString(err));
Line 1401  int scsiCheckIE(scsi_device * device, int hasIELogPage Line 1582  int scsiCheckIE(scsi_device * device, int hasIELogPage
 }  }
   
 // The first character (W, C, I) tells the severity  // The first character (W, C, I) tells the severity
static const char * TapeAlertsMessageTable[]= {  static const char * TapeAlertsMessageTable[]= {
     " ",      " ",
     /* 0x01 */      /* 0x01 */
    "W: The tape drive is having problems reading data. No data has been lost,\n"     "W: The tape drive is having problems reading data. No data has been lost,\n"
Line 1625  static const char * TapeAlertsMessageTable[]= {   Line 1806  static const char * TapeAlertsMessageTable[]= {  
         "  fault. If problem persists, call the supplier help line.",          "  fault. If problem persists, call the supplier help line.",
     };      };
   
const char * scsiTapeAlertsTapeDevice(unsigned short code)const char *
 scsiTapeAlertsTapeDevice(unsigned short code)
 {  {
     const int num = sizeof(TapeAlertsMessageTable) /      const int num = sizeof(TapeAlertsMessageTable) /
                         sizeof(TapeAlertsMessageTable[0]);                          sizeof(TapeAlertsMessageTable[0]);
   
    return (code < num) ?  TapeAlertsMessageTable[code] : "Unknown Alert";     return (code < num) ?  TapeAlertsMessageTable[code] : "Unknown Alert";
 }  }
   
 // The first character (W, C, I) tells the severity  // The first character (W, C, I) tells the severity
static const char * ChangerTapeAlertsMessageTable[]= {  static const char * ChangerTapeAlertsMessageTable[]= {
     " ",      " ",
     /* 0x01 */      /* 0x01 */
     "C: The library mechanism is having difficulty communicating with the\n"      "C: The library mechanism is having difficulty communicating with the\n"
Line 1760  static const char * ChangerTapeAlertsMessageTable[]= { Line 1942  static const char * ChangerTapeAlertsMessageTable[]= {
     "I: The library was unable to read the bar code on a cartridge.",      "I: The library was unable to read the bar code on a cartridge.",
 };  };
   
const char * scsiTapeAlertsChangerDevice(unsigned short code)const char *
 scsiTapeAlertsChangerDevice(unsigned short code)
 {  {
     const int num = sizeof(ChangerTapeAlertsMessageTable) /      const int num = sizeof(ChangerTapeAlertsMessageTable) /
                         sizeof(ChangerTapeAlertsMessageTable[0]);                          sizeof(ChangerTapeAlertsMessageTable[0]);
   
    return (code < num) ?  ChangerTapeAlertsMessageTable[code] : "Unknown Alert";     return (code < num) ?  ChangerTapeAlertsMessageTable[code] : "Unknown Alert";
 }  }
   
   
Line 1894  static const char * strs_for_asc_b[] = { Line 2077  static const char * strs_for_asc_b[] = {
   
 static char spare_buff[128];  static char spare_buff[128];
   
const char * scsiGetIEString(UINT8 asc, UINT8 ascq)const char *
 scsiGetIEString(UINT8 asc, UINT8 ascq)
 {  {
     const char * rp;      const char * rp;
   
     if (SCSI_ASC_IMPENDING_FAILURE == asc) {      if (SCSI_ASC_IMPENDING_FAILURE == asc) {
         if (ascq == 0xff)          if (ascq == 0xff)
             return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)";              return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)";
        else if (ascq <         else if (ascq <
                  (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) {                   (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) {
             rp = strs_for_asc_5d[ascq];              rp = strs_for_asc_5d[ascq];
             if (strlen(rp) > 0)              if (strlen(rp) > 0)
Line 1925  const char * scsiGetIEString(UINT8 asc, UINT8 ascq) Line 2109  const char * scsiGetIEString(UINT8 asc, UINT8 ascq)
   
 /* This is not documented in t10.org, page 0x80 is vendor specific */  /* This is not documented in t10.org, page 0x80 is vendor specific */
 /* Some IBM disks do an offline read-scan when they get this command. */  /* Some IBM disks do an offline read-scan when they get this command. */
int scsiSmartIBMOfflineTest(scsi_device * device)int
{       scsiSmartIBMOfflineTest(scsi_device * device)
 {
     UINT8 tBuf[256];      UINT8 tBuf[256];
     int res;      int res;
        
     memset(tBuf, 0, sizeof(tBuf));      memset(tBuf, 0, sizeof(tBuf));
     /* Build SMART Off-line Immediate Diag Header */      /* Build SMART Off-line Immediate Diag Header */
     tBuf[0] = 0x80; /* Page Code */      tBuf[0] = 0x80; /* Page Code */
Line 1946  int scsiSmartIBMOfflineTest(scsi_device * device) Line 2131  int scsiSmartIBMOfflineTest(scsi_device * device)
     return res;      return res;
 }  }
   
int scsiSmartDefaultSelfTest(scsi_device * device)int
{       scsiSmartDefaultSelfTest(scsi_device * device)
 {
     int res;      int res;
   
     res = scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0);      res = scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0);
Line 1956  int scsiSmartDefaultSelfTest(scsi_device * device) Line 2142  int scsiSmartDefaultSelfTest(scsi_device * device)
     return res;      return res;
 }  }
   
int scsiSmartShortSelfTest(scsi_device * device)int
{       scsiSmartShortSelfTest(scsi_device * device)
 {
     int res;      int res;
   
     res = scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0);      res = scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0);
Line 1966  int scsiSmartShortSelfTest(scsi_device * device) Line 2153  int scsiSmartShortSelfTest(scsi_device * device)
     return res;      return res;
 }  }
   
int scsiSmartExtendSelfTest(scsi_device * device)int
{       scsiSmartExtendSelfTest(scsi_device * device)
 {
     int res;      int res;
   
     res = scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, NULL, 0);      res = scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, NULL, 0);
Line 1977  int scsiSmartExtendSelfTest(scsi_device * device) Line 2165  int scsiSmartExtendSelfTest(scsi_device * device)
     return res;      return res;
 }  }
   
int scsiSmartShortCapSelfTest(scsi_device * device)int
{       scsiSmartShortCapSelfTest(scsi_device * device)
 {
     int res;      int res;
   
     res = scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0);      res = scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0);
Line 1987  int scsiSmartShortCapSelfTest(scsi_device * device) Line 2176  int scsiSmartShortCapSelfTest(scsi_device * device)
     return res;      return res;
 }  }
   
int scsiSmartExtendCapSelfTest(scsi_device * device)int
 scsiSmartExtendCapSelfTest(scsi_device * device)
 {  {
     int res;      int res;
   
Line 1998  int scsiSmartExtendCapSelfTest(scsi_device * device) Line 2188  int scsiSmartExtendCapSelfTest(scsi_device * device)
     return res;      return res;
 }  }
   
int scsiSmartSelfTestAbort(scsi_device * device)int
 scsiSmartSelfTestAbort(scsi_device * device)
 {  {
     int res;      int res;
   
Line 2010  int scsiSmartSelfTestAbort(scsi_device * device) Line 2201  int scsiSmartSelfTestAbort(scsi_device * device)
   
 /* Returns 0 and the expected duration of an extended self test (in seconds)  /* Returns 0 and the expected duration of an extended self test (in seconds)
    if successful; any other return value indicates a failure. */     if successful; any other return value indicates a failure. */
int scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec, int modese_len)int
 scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
                               int modese_len)
 {  {
     int err, offset, res;      int err, offset, res;
     UINT8 buff[64];      UINT8 buff[64];
Line 2029  int scsiFetchExtendedSelfTestTime(scsi_device * device Line 2222  int scsiFetchExtendedSelfTestTime(scsi_device * device
     }      }
     if (10 == modese_len) {      if (10 == modese_len) {
         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,          err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
                              MPAGE_CONTROL_CURRENT,                               MPAGE_CONTROL_CURRENT,
                               buff, sizeof(buff));                                buff, sizeof(buff));
         if (err)          if (err)
             return err;              return err;
    }     }
     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);      offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
     if (offset < 0)      if (offset < 0)
         return -EINVAL;          return -EINVAL;
Line 2046  int scsiFetchExtendedSelfTestTime(scsi_device * device Line 2239  int scsiFetchExtendedSelfTestTime(scsi_device * device
         return -EINVAL;          return -EINVAL;
 }  }
   
void scsiDecodeErrCounterPage(unsigned char * resp, void
                              struct scsiErrorCounter *ecp)scsiDecodeErrCounterPage(unsigned char * resp, struct scsiErrorCounter *ecp)
 {  {
     int k, j, num, pl, pc;      int k, j, num, pl, pc;
     unsigned char * ucp;      unsigned char * ucp;
Line 2061  void scsiDecodeErrCounterPage(unsigned char * resp,  Line 2254  void scsiDecodeErrCounterPage(unsigned char * resp, 
         pc = (ucp[0] << 8) | ucp[1];          pc = (ucp[0] << 8) | ucp[1];
         pl = ucp[3] + 4;          pl = ucp[3] + 4;
         switch (pc) {          switch (pc) {
            case 0:             case 0:
            case 1:             case 1:
            case 2:             case 2:
            case 3:             case 3:
            case 4:             case 4:
            case 5:             case 5:
            case 6:             case 6:
                 ecp->gotPC[pc] = 1;                  ecp->gotPC[pc] = 1;
                 ullp = &ecp->counter[pc];                  ullp = &ecp->counter[pc];
                 break;                  break;
        default:         default:
                 ecp->gotExtraPC = 1;                  ecp->gotExtraPC = 1;
                 ullp = &ecp->counter[7];                  ullp = &ecp->counter[7];
                 break;                  break;
Line 2093  void scsiDecodeErrCounterPage(unsigned char * resp,  Line 2286  void scsiDecodeErrCounterPage(unsigned char * resp, 
     }      }
 }  }
   
void scsiDecodeNonMediumErrPage(unsigned char *resp, void
                                struct scsiNonMediumError *nmep)scsiDecodeNonMediumErrPage(unsigned char *resp,
                            struct scsiNonMediumError *nmep)
 {  {
     int k, j, num, pl, pc, szof;      int k, j, num, pl, pc, szof;
     unsigned char * ucp;      unsigned char * ucp;
Line 2108  void scsiDecodeNonMediumErrPage(unsigned char *resp,  Line 2302  void scsiDecodeNonMediumErrPage(unsigned char *resp, 
         pc = (ucp[0] << 8) | ucp[1];          pc = (ucp[0] << 8) | ucp[1];
         pl = ucp[3] + 4;          pl = ucp[3] + 4;
         switch (pc) {          switch (pc) {
            case 0:             case 0:
                 nmep->gotPC0 = 1;                  nmep->gotPC0 = 1;
                 k = pl - 4;                  k = pl - 4;
                 xp = ucp + 4;                  xp = ucp + 4;
Line 2123  void scsiDecodeNonMediumErrPage(unsigned char *resp,  Line 2317  void scsiDecodeNonMediumErrPage(unsigned char *resp, 
                     nmep->counterPC0 |= xp[j];                      nmep->counterPC0 |= xp[j];
                 }                  }
                 break;                  break;
            case 0x8009:             case 0x8009:
                 nmep->gotTFE_H = 1;                  nmep->gotTFE_H = 1;
                 k = pl - 4;                  k = pl - 4;
                 xp = ucp + 4;                  xp = ucp + 4;
Line 2138  void scsiDecodeNonMediumErrPage(unsigned char *resp,  Line 2332  void scsiDecodeNonMediumErrPage(unsigned char *resp, 
                     nmep->counterTFE_H |= xp[j];                      nmep->counterTFE_H |= xp[j];
                 }                  }
                 break;                  break;
            case 0x8015:             case 0x8015:
                 nmep->gotPE_H = 1;                  nmep->gotPE_H = 1;
                 k = pl - 4;                  k = pl - 4;
                 xp = ucp + 4;                  xp = ucp + 4;
Line 2153  void scsiDecodeNonMediumErrPage(unsigned char *resp,  Line 2347  void scsiDecodeNonMediumErrPage(unsigned char *resp, 
                     nmep->counterPE_H |= xp[j];                      nmep->counterPE_H |= xp[j];
                 }                  }
                 break;                  break;
        default:         default:
                 nmep->gotExtraPC = 1;                  nmep->gotExtraPC = 1;
                 break;                  break;
         }          }
Line 2167  void scsiDecodeNonMediumErrPage(unsigned char *resp,  Line 2361  void scsiDecodeNonMediumErrPage(unsigned char *resp, 
    this function has a problem (typically -1), otherwise the bottom 8     this function has a problem (typically -1), otherwise the bottom 8
    bits are the number of failed self tests and the 16 bits above that     bits are the number of failed self tests and the 16 bits above that
    are the poweron hour of the most recent failure. Note: aborted self     are the poweron hour of the most recent failure. Note: aborted self
   tests (typically by the user) and self tests in progress are not    tests (typically by the user) and self tests in progress are not
   considered failures. See Working Draft SCSI Primary Commands - 3    considered failures. See Working Draft SCSI Primary Commands - 3
    (SPC-3) section 7.2.10 T10/1416-D (rev 22a) */     (SPC-3) section 7.2.10 T10/1416-D (rev 22a) */
int scsiCountFailedSelfTests(scsi_device * fd, int noisy)int
 scsiCountFailedSelfTests(scsi_device * fd, int noisy)
 {  {
     int num, k, n, err, res, fails, fail_hour;      int num, k, n, err, res, fails, fail_hour;
     UINT8 * ucp;      UINT8 * ucp;
Line 2210  int scsiCountFailedSelfTests(scsi_device * fd, int noi Line 2405  int scsiCountFailedSelfTests(scsi_device * fd, int noi
         res = ucp[4] & 0xf;          res = ucp[4] & 0xf;
         if ((res > 2) && (res < 8)) {          if ((res > 2) && (res < 8)) {
             fails++;              fails++;
            if (1 == fails)             if (1 == fails)
                 fail_hour = (ucp[6] << 8) + ucp[7];                  fail_hour = (ucp[6] << 8) + ucp[7];
         }          }
     }      }
Line 2219  int scsiCountFailedSelfTests(scsi_device * fd, int noi Line 2414  int scsiCountFailedSelfTests(scsi_device * fd, int noi
   
 /* Returns 0 if able to read self test log page; then outputs 1 into  /* Returns 0 if able to read self test log page; then outputs 1 into
    *inProgress if self test still in progress, else outputs 0. */     *inProgress if self test still in progress, else outputs 0. */
int scsiSelfTestInProgress(scsi_device * fd, int * inProgress)int
 scsiSelfTestInProgress(scsi_device * fd, int * inProgress)
 {  {
     int num;      int num;
     UINT8 * ucp;      UINT8 * ucp;
Line 2246  int scsiSelfTestInProgress(scsi_device * fd, int * inP Line 2442  int scsiSelfTestInProgress(scsi_device * fd, int * inP
    malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD     malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD
    bit is set. Examines default mode page when current==0 else examines     bit is set. Examines default mode page when current==0 else examines
    current mode page. */     current mode page. */
int scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current)int
 scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current)
 {  {
     int err, offset;      int err, offset;
     UINT8 buff[64];      UINT8 buff[64];
Line 2268  int scsiFetchControlGLTSD(scsi_device * device, int mo Line 2465  int scsiFetchControlGLTSD(scsi_device * device, int mo
                               buff, sizeof(buff));                                buff, sizeof(buff));
         if (err)          if (err)
             return -EINVAL;              return -EINVAL;
    }     }
     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);      offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
     if ((offset >= 0) && (buff[offset + 1] >= 0xa))      if ((offset >= 0) && (buff[offset + 1] >= 0xa))
         return (buff[offset + 2] & 2) ? 1 : 0;          return (buff[offset + 2] & 2) ? 1 : 0;
     return -EINVAL;      return -EINVAL;
 }  }
   
   /* Returns a negative value on error, 0 if unknown and 1 if SSD,
    * otherwise the positive returned value is the speed in rpm. First checks
    * the Block Device Characteristics VPD page and if that fails it tries the
    * RIGID_DISK_DRIVE_GEOMETRY_PAGE mode page. */
   
   int
   scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp)
   {
       int err, offset, speed;
       UINT8 buff[64];
       int pc = MPAGE_CONTROL_DEFAULT;
   
       memset(buff, 0, sizeof(buff));
       if ((0 == scsiInquiryVpd(device, SCSI_VPD_BLOCK_DEVICE_CHARACTERISTICS,
                                buff, sizeof(buff))) &&
           (((buff[2] << 8) + buff[3]) > 2)) {
           speed = (buff[4] << 8) + buff[5];
           if (form_factorp)
               *form_factorp = buff[7] & 0xf;
           return speed;
       }
       if (form_factorp)
           *form_factorp = 0;
       if (modese_len <= 6) {
           if ((err = scsiModeSense(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc,
                                    buff, sizeof(buff)))) {
               if (SIMPLE_ERR_BAD_OPCODE == err)
                   modese_len = 10;
               else
                   return -EINVAL;
           } else if (0 == modese_len)
               modese_len = 6;
       }
       if (10 == modese_len) {
           err = scsiModeSense10(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc,
                                 buff, sizeof(buff));
           if (err)
               return -EINVAL;
       }
       offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
       return (buff[offset + 20] << 8) | buff[offset + 21];
   }
   
   /* Returns a non-zero value in case of error, wcep/rcdp == -1 - get value,
      0 - clear bit, 1 - set bit  */
   
   int
   scsiGetSetCache(scsi_device * device,  int modese_len, short int * wcep,
                   short int * rcdp)
   {
       int err, offset, resp_len, sp;
       UINT8 buff[64], ch_buff[64];
       short set_wce = *wcep;
       short set_rcd = *rcdp;
   
       memset(buff, 0, sizeof(buff));
       if (modese_len <= 6) {
           if ((err = scsiModeSense(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT,
                                    buff, sizeof(buff)))) {
               if (SIMPLE_ERR_BAD_OPCODE == err)
                   modese_len = 10;
               else {
                   device->set_err(EINVAL, "SCSI MODE SENSE failed");
                   return -EINVAL;
               }
           } else if (0 == modese_len)
               modese_len = 6;
       }
   
       if (10 == modese_len) {
           err = scsiModeSense10(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT,
                                 buff, sizeof(buff));
           if (err) {
               device->set_err(EINVAL, "SCSI MODE SENSE failed");
               return -EINVAL;
           }
       }
       offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
       if ((offset < 0) || (buff[offset + 1] < 0xa)) {
           device->set_err(EINVAL, "Bad response");
           return SIMPLE_ERR_BAD_RESP;
       }
   
       *wcep = ((buff[offset + 2] & 0x04) != 0);
       *rcdp = ((buff[offset + 2] & 0x01) != 0);
   
       if((*wcep == set_wce || set_wce == -1)
             && ((*rcdp == set_rcd) || set_rcd == -1))
         return 0; // no changes needed or nothing to set
   
       if (modese_len == 6)
           err = scsiModeSense(device, CACHING_PAGE, 0,
                               MPAGE_CONTROL_CHANGEABLE,
                               ch_buff, sizeof(ch_buff));
       else
           err = scsiModeSense10(device, CACHING_PAGE, 0,
                                 MPAGE_CONTROL_CHANGEABLE,
                                 ch_buff, sizeof(ch_buff));
       if (err) {
           device->set_err(EINVAL, "WCE/RCD bits not changable");
           return err;
       }
   
       // set WCE bit
       if(set_wce >= 0 && *wcep != set_wce) {
          if (0 == (ch_buff[offset + 2] & 0x04)) {
            device->set_err(EINVAL, "WCE bit not changable");
            return 1;
          }
          if(set_wce)
             buff[offset + 2] |= 0x04; // set bit
          else
             buff[offset + 2] &= 0xfb; // clear bit
       }
       // set RCD bit
       if(set_rcd >= 0 && *rcdp != set_rcd) {
          if (0 == (ch_buff[offset + 2] & 0x01)) {
            device->set_err(EINVAL, "RCD bit not changable");
            return 1;
          }
          if(set_rcd)
             buff[offset + 2] |= 0x01; // set bit
          else
             buff[offset + 2] &= 0xfe; // clear bit
       }
   
       if (10 == modese_len) {
           resp_len = (buff[0] << 8) + buff[1] + 2;
           buff[3] &= 0xef;    /* for disks mask out DPOFUA bit */
       } else {
           resp_len = buff[0] + 1;
           buff[2] &= 0xef;    /* for disks mask out DPOFUA bit */
       }
       sp = 0; /* Do not change saved values */
       if (10 == modese_len)
           err = scsiModeSelect10(device, sp, buff, resp_len);
       else if (6 == modese_len)
           err = scsiModeSelect(device, sp, buff, resp_len);
       if(err)
         device->set_err(EINVAL, "MODE SELECT command failed");
       return err;
   }
   
   
 /* Attempts to set or clear GLTSD bit in Control mode page. If enabled is  /* Attempts to set or clear GLTSD bit in Control mode page. If enabled is
    0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if     0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if
    successful, negative if low level error, > 0 if higher level error (e.g.     successful, negative if low level error, > 0 if higher level error (e.g.
    SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */     SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */
int scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len)int
 scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len)
 {  {
     int err, offset, resp_len, sp;      int err, offset, resp_len, sp;
     UINT8 buff[64];      UINT8 buff[64];
Line 2303  int scsiSetControlGLTSD(scsi_device * device, int enab Line 2645  int scsiSetControlGLTSD(scsi_device * device, int enab
                               buff, sizeof(buff));                                buff, sizeof(buff));
         if (err)          if (err)
             return err;              return err;
    }     }
     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);      offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
     if ((offset < 0) || (buff[offset + 1] < 0xa))      if ((offset < 0) || (buff[offset + 1] < 0xa))
         return SIMPLE_ERR_BAD_RESP;          return SIMPLE_ERR_BAD_RESP;
Line 2325  int scsiSetControlGLTSD(scsi_device * device, int enab Line 2667  int scsiSetControlGLTSD(scsi_device * device, int enab
         return err;          return err;
     if (0 == (ch_buff[offset + 2] & 2))      if (0 == (ch_buff[offset + 2] & 2))
         return SIMPLE_ERR_BAD_PARAM;  /* GLTSD bit not chageable */          return SIMPLE_ERR_BAD_PARAM;  /* GLTSD bit not chageable */
    
     if (10 == modese_len) {      if (10 == modese_len) {
         resp_len = (buff[0] << 8) + buff[1] + 2;          resp_len = (buff[0] << 8) + buff[1] + 2;
         buff[3] &= 0xef;    /* for disks mask out DPOFUA bit */          buff[3] &= 0xef;    /* for disks mask out DPOFUA bit */
Line 2345  int scsiSetControlGLTSD(scsi_device * device, int enab Line 2687  int scsiSetControlGLTSD(scsi_device * device, int enab
     return err;      return err;
 }  }
   
/* Returns a negative value if failed to fetch Protocol specific port mode /* Returns a negative value if failed to fetch Protocol specific port mode
    page or it was malformed. Returns transport protocol identifier when     page or it was malformed. Returns transport protocol identifier when
    value >= 0 . */     value >= 0 . */
int scsiFetchTransportProtocol(scsi_device * device, int modese_len)int
 scsiFetchTransportProtocol(scsi_device * device, int modese_len)
 {  {
     int err, offset;      int err, offset;
     UINT8 buff[64];      UINT8 buff[64];
Line 2371  int scsiFetchTransportProtocol(scsi_device * device, i Line 2714  int scsiFetchTransportProtocol(scsi_device * device, i
                               buff, sizeof(buff));                                buff, sizeof(buff));
         if (err)          if (err)
             return -EINVAL;              return -EINVAL;
    }     }
     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);      offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
     if ((offset >= 0) && (buff[offset + 1] > 1)) {      if ((offset >= 0) && (buff[offset + 1] > 1)) {
         if ((0 == (buff[offset] & 0x40)) &&       /* SPF==0 */          if ((0 == (buff[offset] & 0x40)) &&       /* SPF==0 */
            (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f)))             (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f)))
                 return (buff[offset + 2] & 0xf);                  return (buff[offset + 2] & 0xf);
     }      }
     return -EINVAL;      return -EINVAL;
   }
   
   const unsigned char *
   sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len,
                           int desc_type)
   {
       int add_sen_len, add_len, desc_len, k;
       const unsigned char * descp;
   
       if ((sense_len < 8) || (0 == (add_sen_len = sensep[7])))
           return NULL;
       if ((sensep[0] < 0x72) || (sensep[0] > 0x73))
           return NULL;
       add_sen_len = (add_sen_len < (sense_len - 8)) ?
                            add_sen_len : (sense_len - 8);
       descp = &sensep[8];
       for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
           descp += desc_len;
           add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
           desc_len = add_len + 2;
           if (descp[0] == desc_type)
               return descp;
           if (add_len < 0) /* short descriptor ?? */
               break;
       }
       return NULL;
   }
   
   // Convenience function for formatting strings from SCSI identify
   void
   scsi_format_id_string(char * out, const unsigned char * in, int n)
   {
     char tmp[65];
     n = n > 64 ? 64 : n;
     strncpy(tmp, (const char *)in, n);
     tmp[n] = '\0';
   
     // Find the first non-space character (maybe none).
     int first = -1;
     int i;
     for (i = 0; tmp[i]; i++)
       if (!isspace((int)tmp[i])) {
         first = i;
         break;
       }
   
     if (first == -1) {
       // There are no non-space characters.
       out[0] = '\0';
       return;
     }
   
     // Find the last non-space character.
     for (i = strlen(tmp)-1; i >= first && isspace((int)tmp[i]); i--);
     int last = i;
   
     strncpy(out, tmp+first, last-first+1);
     out[last-first+1] = '\0';
 }  }

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.2


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>