File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / smartmontools / scsicmds.cpp
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 07:54:04 2013 UTC (10 years, 7 months ago) by misho
Branches: smartmontools, elwix, MAIN
CVS tags: v6_2, HEAD
v 6.2

    1: /*
    2:  * scsicmds.cpp
    3:  *
    4:  * Home page of code is: http://smartmontools.sourceforge.net
    5:  *
    6:  * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
    7:  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
    8:  *
    9:  * Additional SCSI work:
   10:  * Copyright (C) 2003-13 Douglas Gilbert <dgilbert@interlog.com>
   11:  *
   12:  * This program is free software; you can redistribute it and/or modify
   13:  * it under the terms of the GNU General Public License as published by
   14:  * the Free Software Foundation; either version 2, or (at your option)
   15:  * any later version.
   16:  *
   17:  * You should have received a copy of the GNU General Public License
   18:  * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
   19:  *
   20:  * This code was originally developed as a Senior Thesis by Michael Cornwell
   21:  * at the Concurrent Systems Laboratory (now part of the Storage Systems
   22:  * Research Center), Jack Baskin School of Engineering, University of
   23:  * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
   24:  *
   25:  *
   26:  * In the SCSI world "SMART" is a dead or withdrawn standard. In recent
   27:  * SCSI standards (since SCSI-3) it goes under the awkward name of
   28:  * "Informational Exceptions" ["IE" or "IEC" (with the "C" for "control")].
   29:  * The relevant information is spread around several SCSI draft
   30:  * standards available at http://www.t10.org . Reference is made in the
   31:  * code to the following acronyms:
   32:  *      - SAM [SCSI Architectural model, versions 2 or 3]
   33:  *      - SPC [SCSI Primary commands, versions 2 or 3]
   34:  *      - SBC [SCSI Block commands, versions 2]
   35:  *
   36:  * Some SCSI disk vendors have snippets of "SMART" information in their
   37:  * product manuals.
   38:  */
   39: 
   40: #include <stdio.h>
   41: #include <string.h>
   42: #include <errno.h>
   43: #include <ctype.h>
   44: 
   45: #include "config.h"
   46: #include "int64.h"
   47: #include "scsicmds.h"
   48: #include "atacmds.h" // FIXME: for smart_command_set only
   49: #include "dev_interface.h"
   50: #include "utility.h"
   51: 
   52: const char *scsicmds_c_cvsid="$Id: scsicmds.cpp,v 1.1.1.3 2013/10/14 07:54:04 misho Exp $"
   53:   SCSICMDS_H_CVSID;
   54: 
   55: // Print SCSI debug messages?
   56: unsigned char scsi_debugmode = 0;
   57: 
   58: supported_vpd_pages * supported_vpd_pages_p = NULL;
   59: 
   60: 
   61: supported_vpd_pages::supported_vpd_pages(scsi_device * device) : num_valid(0)
   62: {
   63:     unsigned char b[0x1fc];     /* size chosen for old INQUIRY command */
   64:     int n;
   65: 
   66:     memset(b, 0, sizeof(b));
   67:     if (device && (0 == scsiInquiryVpd(device, SCSI_VPD_SUPPORTED_VPD_PAGES,
   68:                    b, sizeof(b)))) {
   69:         num_valid = (b[2] << 8) + b[3];
   70:         n = sizeof(pages);
   71:         if (num_valid > n)
   72:             num_valid = n;
   73:         memcpy(pages, b + 4, num_valid);
   74:     }
   75: }
   76: 
   77: bool
   78: supported_vpd_pages::is_supported(int vpd_page_num) const
   79: {
   80:     /* Supported VPD pages numbers start at offset 4 and should be in
   81:      * ascending order but don't assume that. */
   82:     for (int k = 0; k < num_valid; ++k) {
   83:         if (vpd_page_num == pages[k])
   84:             return true;
   85:     }
   86:     return false;
   87: }
   88: 
   89: /* output binary in hex and optionally ascii */
   90: void
   91: dStrHex(const char* str, int len, int no_ascii)
   92: {
   93:     const char* p = str;
   94:     unsigned char c;
   95:     char buff[82];
   96:     int a = 0;
   97:     const int bpstart = 5;
   98:     const int cpstart = 60;
   99:     int cpos = cpstart;
  100:     int bpos = bpstart;
  101:     int i, k;
  102: 
  103:     if (len <= 0) return;
  104:     memset(buff,' ',80);
  105:     buff[80]='\0';
  106:     k = snprintf(buff+1, sizeof(buff)-1, "%.2x", a);
  107:     buff[k + 1] = ' ';
  108:     if (bpos >= ((bpstart + (9 * 3))))
  109:         bpos++;
  110: 
  111:     for(i = 0; i < len; i++)
  112:     {
  113:         c = *p++;
  114:         bpos += 3;
  115:         if (bpos == (bpstart + (9 * 3)))
  116:             bpos++;
  117:         snprintf(buff+bpos, sizeof(buff)-bpos, "%.2x", (int)(unsigned char)c);
  118:         buff[bpos + 2] = ' ';
  119:         if (no_ascii)
  120:             buff[cpos++] = ' ';
  121:         else {
  122:             if ((c < ' ') || (c >= 0x7f))
  123:                 c='.';
  124:             buff[cpos++] = c;
  125:         }
  126:         if (cpos > (cpstart+15))
  127:         {
  128:             pout("%s\n", buff);
  129:             bpos = bpstart;
  130:             cpos = cpstart;
  131:             a += 16;
  132:             memset(buff,' ',80);
  133:             k = snprintf(buff+1, sizeof(buff)-1, "%.2x", a);
  134:             buff[k + 1] = ' ';
  135:         }
  136:     }
  137:     if (cpos > cpstart)
  138:     {
  139:         pout("%s\n", buff);
  140:     }
  141: }
  142: 
  143: struct scsi_opcode_name {
  144:     UINT8 opcode;
  145:     const char * name;
  146: };
  147: 
  148: static struct scsi_opcode_name opcode_name_arr[] = {
  149:     /* in ascending opcode order */
  150:     {TEST_UNIT_READY, "test unit ready"},       /* 0x00 */
  151:     {REQUEST_SENSE, "request sense"},           /* 0x03 */
  152:     {INQUIRY, "inquiry"},                       /* 0x12 */
  153:     {MODE_SELECT, "mode select(6)"},            /* 0x15 */
  154:     {MODE_SENSE, "mode sense(6)"},              /* 0x1a */
  155:     {START_STOP_UNIT, "start stop unit"},       /* 0x1b */
  156:     {RECEIVE_DIAGNOSTIC, "receive diagnostic"}, /* 0x1c */
  157:     {SEND_DIAGNOSTIC, "send diagnostic"},       /* 0x1d */
  158:     {READ_CAPACITY_10, "read capacity(10)"},    /* 0x25 */
  159:     {READ_DEFECT_10, "read defect list(10)"},   /* 0x37 */
  160:     {LOG_SELECT, "log select"},                 /* 0x4c */
  161:     {LOG_SENSE, "log sense"},                   /* 0x4d */
  162:     {MODE_SELECT_10, "mode select(10)"},        /* 0x55 */
  163:     {MODE_SENSE_10, "mode sense(10)"},          /* 0x5a */
  164:     {SAT_ATA_PASSTHROUGH_16, "ata pass-through(16)"}, /* 0x85 */
  165:     {READ_CAPACITY_16, "read capacity(16)"},    /* 0x9e,0x10 */
  166:     {REPORT_LUNS, "report luns"},               /* 0xa0 */
  167:     {SAT_ATA_PASSTHROUGH_12, "ata pass-through(12)"}, /* 0xa1 */
  168:     {READ_DEFECT_12, "read defect list(12)"},   /* 0xb7 */
  169: };
  170: 
  171: static const char * vendor_specific = "<vendor specific>";
  172: 
  173: /* Need to expand to take service action into account. For commands
  174:  * of interest the service action is in the 2nd command byte */
  175: const char *
  176: scsi_get_opcode_name(UINT8 opcode)
  177: {
  178:     int k;
  179:     int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]);
  180:     struct scsi_opcode_name * onp;
  181: 
  182:     if (opcode >= 0xc0)
  183:         return vendor_specific;
  184:     for (k = 0; k < len; ++k) {
  185:         onp = &opcode_name_arr[k];
  186:         if (opcode == onp->opcode)
  187:             return onp->name;
  188:         else if (opcode < onp->opcode)
  189:             return NULL;
  190:     }
  191:     return NULL;
  192: }
  193: 
  194: void
  195: scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf,
  196:                      struct scsi_sense_disect * out)
  197: {
  198:     int resp_code;
  199: 
  200:     memset(out, 0, sizeof(struct scsi_sense_disect));
  201:     if (SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) {
  202:         resp_code = (io_buf->sensep[0] & 0x7f);
  203:         out->resp_code = resp_code;
  204:         if (resp_code >= 0x72) {
  205:             out->sense_key = (io_buf->sensep[1] & 0xf);
  206:             out->asc = io_buf->sensep[2];
  207:             out->ascq = io_buf->sensep[3];
  208:         } else if (resp_code >= 0x70) {
  209:             out->sense_key = (io_buf->sensep[2] & 0xf);
  210:             if (io_buf->resp_sense_len > 13) {
  211:                 out->asc = io_buf->sensep[12];
  212:                 out->ascq = io_buf->sensep[13];
  213:             }
  214:         }
  215:     }
  216: }
  217: 
  218: int
  219: scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo)
  220: {
  221:     switch (sinfo->sense_key) {
  222:     case SCSI_SK_NO_SENSE:
  223:     case SCSI_SK_RECOVERED_ERR:
  224:         return SIMPLE_NO_ERROR;
  225:     case SCSI_SK_NOT_READY:
  226:         if (SCSI_ASC_NO_MEDIUM == sinfo->asc)
  227:             return SIMPLE_ERR_NO_MEDIUM;
  228:         else if (SCSI_ASC_NOT_READY == sinfo->asc) {
  229:             if (0x1 == sinfo->ascq)
  230:                 return SIMPLE_ERR_BECOMING_READY;
  231:             else
  232:                 return SIMPLE_ERR_NOT_READY;
  233:         } else
  234:             return SIMPLE_ERR_NOT_READY;
  235:     case SCSI_SK_MEDIUM_ERROR:
  236:     case SCSI_SK_HARDWARE_ERROR:
  237:         return SIMPLE_ERR_MEDIUM_HARDWARE;
  238:     case SCSI_SK_ILLEGAL_REQUEST:
  239:         if (SCSI_ASC_UNKNOWN_OPCODE == sinfo->asc)
  240:             return SIMPLE_ERR_BAD_OPCODE;
  241:         else if (SCSI_ASC_INVALID_FIELD == sinfo->asc)
  242:             return SIMPLE_ERR_BAD_FIELD;
  243:         else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc)
  244:             return SIMPLE_ERR_BAD_PARAM;
  245:         else
  246:             return SIMPLE_ERR_BAD_PARAM;    /* all other illegal request */
  247:     case SCSI_SK_UNIT_ATTENTION:
  248:         return SIMPLE_ERR_TRY_AGAIN;
  249:     case SCSI_SK_ABORTED_COMMAND:
  250:         return SIMPLE_ERR_ABORTED_COMMAND;
  251:     default:
  252:         return SIMPLE_ERR_UNKNOWN;
  253:     }
  254: }
  255: 
  256: const char *
  257: scsiErrString(int scsiErr)
  258: {
  259:     if (scsiErr < 0)
  260:         return strerror(-scsiErr);
  261:     switch (scsiErr) {
  262:         case SIMPLE_NO_ERROR:
  263:             return "no error";
  264:         case SIMPLE_ERR_NOT_READY:
  265:             return "device not ready";
  266:         case SIMPLE_ERR_BAD_OPCODE:
  267:             return "unsupported scsi opcode";
  268:         case SIMPLE_ERR_BAD_FIELD:
  269:             return "unsupported field in scsi command";
  270:         case SIMPLE_ERR_BAD_PARAM:
  271:             return "badly formed scsi parameters";
  272:         case SIMPLE_ERR_BAD_RESP:
  273:             return "scsi response fails sanity test";
  274:         case SIMPLE_ERR_NO_MEDIUM:
  275:             return "no medium present";
  276:         case SIMPLE_ERR_BECOMING_READY:
  277:             return "device will be ready soon";
  278:         case SIMPLE_ERR_TRY_AGAIN:
  279:             return "unit attention reported, try again";
  280:         case SIMPLE_ERR_MEDIUM_HARDWARE:
  281:             return "medium or hardware error (serious)";
  282:         case SIMPLE_ERR_UNKNOWN:
  283:             return "unknown error (unexpected sense key)";
  284:         case SIMPLE_ERR_ABORTED_COMMAND:
  285:             return "aborted command";
  286:         default:
  287:             return "unknown error";
  288:     }
  289: }
  290: 
  291: /* Iterates to next designation descriptor in the device identification
  292:  * VPD page. The 'initial_desig_desc' should point to start of first
  293:  * descriptor with 'page_len' being the number of valid bytes in that
  294:  * and following descriptors. To start, 'off' should point to a negative
  295:  * value, thereafter it should point to the value yielded by the previous
  296:  * call. If 0 returned then 'initial_desig_desc + *off' should be a valid
  297:  * descriptor; returns -1 if normal end condition and -2 for an abnormal
  298:  * termination. Matches association, designator_type and/or code_set when
  299:  * any of those values are greater than or equal to zero. */
  300: int
  301: scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len,
  302:                      int * off, int m_assoc, int m_desig_type, int m_code_set)
  303: {
  304:     const unsigned char * ucp;
  305:     int k, c_set, assoc, desig_type;
  306: 
  307:     for (k = *off, ucp = initial_desig_desc ; (k + 3) < page_len; ) {
  308:         k = (k < 0) ? 0 : (k + ucp[k + 3] + 4);
  309:         if ((k + 4) > page_len)
  310:             break;
  311:         c_set = (ucp[k] & 0xf);
  312:         if ((m_code_set >= 0) && (m_code_set != c_set))
  313:             continue;
  314:         assoc = ((ucp[k + 1] >> 4) & 0x3);
  315:         if ((m_assoc >= 0) && (m_assoc != assoc))
  316:             continue;
  317:         desig_type = (ucp[k + 1] & 0xf);
  318:         if ((m_desig_type >= 0) && (m_desig_type != desig_type))
  319:             continue;
  320:         *off = k;
  321:         return 0;
  322:     }
  323:     return (k == page_len) ? -1 : -2;
  324: }
  325: 
  326: /* Decode VPD page 0x83 logical unit designator into a string. If both
  327:  * numeric address and SCSI name string present, prefer the former.
  328:  * Returns 0 on success, -1 on error with error string in s. */
  329: int
  330: scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
  331:                       int * transport)
  332: {
  333:     int m, c_set, assoc, desig_type, i_len, naa, off, u, have_scsi_ns;
  334:     const unsigned char * ucp;
  335:     const unsigned char * ip;
  336:     int si = 0;
  337: 
  338:     if (transport)
  339:         *transport = -1;
  340:     if (slen < 32) {
  341:         if (slen > 0)
  342:             s[0] = '\0';
  343:         return -1;
  344:     }
  345:     have_scsi_ns = 0;
  346:     s[0] = '\0';
  347:     off = -1;
  348:     while ((u = scsi_vpd_dev_id_iter(b, blen, &off, -1, -1, -1)) == 0) {
  349:         ucp = b + off;
  350:         i_len = ucp[3];
  351:         if ((off + i_len + 4) > blen) {
  352:             snprintf(s+si, slen-si, "error: designator length");
  353:             return -1;
  354:         }
  355:         assoc = ((ucp[1] >> 4) & 0x3);
  356:         if (transport && assoc && (ucp[1] & 0x80) && (*transport < 0))
  357:             *transport = (ucp[0] >> 4) & 0xf;
  358:         if (0 != assoc)
  359:             continue;
  360:         ip = ucp + 4;
  361:         c_set = (ucp[0] & 0xf);
  362:         desig_type = (ucp[1] & 0xf);
  363: 
  364:         switch (desig_type) {
  365:         case 0: /* vendor specific */
  366:         case 1: /* T10 vendor identification */
  367:             break;
  368:         case 2: /* EUI-64 based */
  369:             if ((8 != i_len) && (12 != i_len) && (16 != i_len)) {
  370:                 snprintf(s+si, slen-si, "error: EUI-64 length");
  371:                 return -1;
  372:             }
  373:             if (have_scsi_ns)
  374:                 si = 0;
  375:             si += snprintf(s+si, slen-si, "0x");
  376:             for (m = 0; m < i_len; ++m)
  377:                 si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
  378:             break;
  379:         case 3: /* NAA */
  380:             if (1 != c_set) {
  381:                 snprintf(s+si, slen-si, "error: NAA bad code_set");
  382:                 return -1;
  383:             }
  384:             naa = (ip[0] >> 4) & 0xff;
  385:             if ((naa < 2) || (naa > 6) || (4 == naa)) {
  386:                 snprintf(s+si, slen-si, "error: unexpected NAA");
  387:                 return -1;
  388:             }
  389:             if (have_scsi_ns)
  390:                 si = 0;
  391:             if (2 == naa) {             /* NAA IEEE Extended */
  392:                 if (8 != i_len) {
  393:                     snprintf(s+si, slen-si, "error: NAA 2 length");
  394:                     return -1;
  395:                 }
  396:                 si += snprintf(s+si, slen-si, "0x");
  397:                 for (m = 0; m < 8; ++m)
  398:                     si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
  399:             } else if ((3 == naa ) || (5 == naa)) {
  400:                 /* NAA=3 Locally assigned; NAA=5 IEEE Registered */
  401:                 if (8 != i_len) {
  402:                     snprintf(s+si, slen-si, "error: NAA 3 or 5 length");
  403:                     return -1;
  404:                 }
  405:                 si += snprintf(s+si, slen-si, "0x");
  406:                 for (m = 0; m < 8; ++m)
  407:                     si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
  408:             } else if (6 == naa) {      /* NAA IEEE Registered extended */
  409:                 if (16 != i_len) {
  410:                     snprintf(s+si, slen-si, "error: NAA 6 length");
  411:                     return -1;
  412:                 }
  413:                 si += snprintf(s+si, slen-si, "0x");
  414:                 for (m = 0; m < 16; ++m)
  415:                     si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
  416:             }
  417:             break;
  418:         case 4: /* Relative target port */
  419:         case 5: /* (primary) Target port group */
  420:         case 6: /* Logical unit group */
  421:         case 7: /* MD5 logical unit identifier */
  422:             break;
  423:         case 8: /* SCSI name string */
  424:             if (3 != c_set) {
  425:                 snprintf(s+si, slen-si, "error: SCSI name string");
  426:                 return -1;
  427:             }
  428:             /* does %s print out UTF-8 ok?? */
  429:             if (si == 0) {
  430:                 si += snprintf(s+si, slen-si, "%s", (const char *)ip);
  431:                 ++have_scsi_ns;
  432:             }
  433:             break;
  434:         default: /* reserved */
  435:             break;
  436:         }
  437:     }
  438:     if (-2 == u) {
  439:         snprintf(s+si, slen-si, "error: bad structure");
  440:         return -1;
  441:     }
  442:     return 0;
  443: }
  444: 
  445: /* Sends LOG SENSE command. Returns 0 if ok, 1 if device NOT READY, 2 if
  446:    command not supported, 3 if field (within command) not supported or
  447:    returns negated errno.  SPC-3 sections 6.6 and 7.2 (rec 22a).
  448:    N.B. Sets PC==1 to fetch "current cumulative" log pages.
  449:    If known_resp_len > 0 then a single fetch is done for this response
  450:    length. If known_resp_len == 0 then twin fetches are performed, the
  451:    first to deduce the response length, then send the same command again
  452:    requesting the deduced response length. This protects certain fragile
  453:    HBAs. The twin fetch technique should not be used with the TapeAlert
  454:    log page since it clears its state flags after each fetch. */
  455: int
  456: scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
  457:              int bufLen, int known_resp_len)
  458: {
  459:     struct scsi_cmnd_io io_hdr;
  460:     struct scsi_sense_disect sinfo;
  461:     UINT8 cdb[10];
  462:     UINT8 sense[32];
  463:     int pageLen;
  464:     int status, res;
  465: 
  466:     if (known_resp_len > bufLen)
  467:         return -EIO;
  468:     if (known_resp_len > 0)
  469:         pageLen = known_resp_len;
  470:     else {
  471:         /* Starting twin fetch strategy: first fetch to find respone length */
  472:         pageLen = 4;
  473:         if (pageLen > bufLen)
  474:             return -EIO;
  475:         else
  476:             memset(pBuf, 0, pageLen);
  477: 
  478:         memset(&io_hdr, 0, sizeof(io_hdr));
  479:         memset(cdb, 0, sizeof(cdb));
  480:         io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
  481:         io_hdr.dxfer_len = pageLen;
  482:         io_hdr.dxferp = pBuf;
  483:         cdb[0] = LOG_SENSE;
  484:         cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
  485:         cdb[3] = subpagenum;
  486:         cdb[7] = (pageLen >> 8) & 0xff;
  487:         cdb[8] = pageLen & 0xff;
  488:         io_hdr.cmnd = cdb;
  489:         io_hdr.cmnd_len = sizeof(cdb);
  490:         io_hdr.sensep = sense;
  491:         io_hdr.max_sense_len = sizeof(sense);
  492:         io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
  493: 
  494:         if (!device->scsi_pass_through(&io_hdr))
  495:           return -device->get_errno();
  496:         scsi_do_sense_disect(&io_hdr, &sinfo);
  497:         if ((res = scsiSimpleSenseFilter(&sinfo)))
  498:             return res;
  499:         /* sanity check on response */
  500:         if ((SUPPORTED_LPAGES != pagenum) && ((pBuf[0] & 0x3f) != pagenum))
  501:             return SIMPLE_ERR_BAD_RESP;
  502:         if (0 == ((pBuf[2] << 8) + pBuf[3]))
  503:             return SIMPLE_ERR_BAD_RESP;
  504:         pageLen = (pBuf[2] << 8) + pBuf[3] + 4;
  505:         if (4 == pageLen)  /* why define a lpage with no payload? */
  506:             pageLen = 252; /* some IBM tape drives don't like double fetch */
  507:         /* some SCSI HBA don't like "odd" length transfers */
  508:         if (pageLen % 2)
  509:             pageLen += 1;
  510:         if (pageLen > bufLen)
  511:             pageLen = bufLen;
  512:     }
  513:     memset(pBuf, 0, 4);
  514:     memset(&io_hdr, 0, sizeof(io_hdr));
  515:     memset(cdb, 0, sizeof(cdb));
  516:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
  517:     io_hdr.dxfer_len = pageLen;
  518:     io_hdr.dxferp = pBuf;
  519:     cdb[0] = LOG_SENSE;
  520:     cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
  521:     cdb[7] = (pageLen >> 8) & 0xff;
  522:     cdb[8] = pageLen & 0xff;
  523:     io_hdr.cmnd = cdb;
  524:     io_hdr.cmnd_len = sizeof(cdb);
  525:     io_hdr.sensep = sense;
  526:     io_hdr.max_sense_len = sizeof(sense);
  527:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
  528: 
  529:     if (!device->scsi_pass_through(&io_hdr))
  530:       return -device->get_errno();
  531:     scsi_do_sense_disect(&io_hdr, &sinfo);
  532:     status = scsiSimpleSenseFilter(&sinfo);
  533:     if (0 != status)
  534:         return status;
  535:     /* sanity check on response */
  536:     if ((SUPPORTED_LPAGES != pagenum) && ((pBuf[0] & 0x3f) != pagenum))
  537:         return SIMPLE_ERR_BAD_RESP;
  538:     if (0 == ((pBuf[2] << 8) + pBuf[3]))
  539:         return SIMPLE_ERR_BAD_RESP;
  540:     return 0;
  541: }
  542: 
  543: /* Sends a LOG SELECT command. Can be used to set log page values
  544:  * or reset one log page (or all of them) to its defaults (typically zero).
  545:  * Returns 0 if ok, 1 if NOT READY, 2 if command not supported, * 3 if
  546:  * field in command not supported, * 4 if bad parameter to command or
  547:  * returns negated errno. SPC-4 sections 6.5 and 7.2 (rev 20) */
  548: int
  549: scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,
  550:               int subpagenum, UINT8 *pBuf, int bufLen)
  551: {
  552:     struct scsi_cmnd_io io_hdr;
  553:     struct scsi_sense_disect sinfo;
  554:     UINT8 cdb[10];
  555:     UINT8 sense[32];
  556: 
  557:     memset(&io_hdr, 0, sizeof(io_hdr));
  558:     memset(cdb, 0, sizeof(cdb));
  559:     io_hdr.dxfer_dir = DXFER_TO_DEVICE;
  560:     io_hdr.dxfer_len = bufLen;
  561:     io_hdr.dxferp = pBuf;
  562:     cdb[0] = LOG_SELECT;
  563:     cdb[1] = (pcr ? 2 : 0) | (sp ? 1 : 0);
  564:     cdb[2] = ((pc << 6) & 0xc0) | (pagenum & 0x3f);
  565:     cdb[3] = (subpagenum & 0xff);
  566:     cdb[7] = ((bufLen >> 8) & 0xff);
  567:     cdb[8] = (bufLen & 0xff);
  568:     io_hdr.cmnd = cdb;
  569:     io_hdr.cmnd_len = sizeof(cdb);
  570:     io_hdr.sensep = sense;
  571:     io_hdr.max_sense_len = sizeof(sense);
  572:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
  573: 
  574:     if (!device->scsi_pass_through(&io_hdr))
  575:       return -device->get_errno();
  576:     scsi_do_sense_disect(&io_hdr, &sinfo);
  577:     return scsiSimpleSenseFilter(&sinfo);
  578: }
  579: 
  580: /* Send MODE SENSE (6 byte) command. Returns 0 if ok, 1 if NOT READY,
  581:  * 2 if command not supported (then MODE SENSE(10) should be supported),
  582:  * 3 if field in command not supported or returns negated errno.
  583:  * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */
  584: int
  585: scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
  586:               UINT8 *pBuf, int bufLen)
  587: {
  588:     struct scsi_cmnd_io io_hdr;
  589:     struct scsi_sense_disect sinfo;
  590:     UINT8 cdb[6];
  591:     UINT8 sense[32];
  592:     int status;
  593: 
  594:     if ((bufLen < 0) || (bufLen > 255))
  595:         return -EINVAL;
  596:     memset(&io_hdr, 0, sizeof(io_hdr));
  597:     memset(cdb, 0, sizeof(cdb));
  598:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
  599:     io_hdr.dxfer_len = bufLen;
  600:     io_hdr.dxferp = pBuf;
  601:     cdb[0] = MODE_SENSE;
  602:     cdb[2] = (pc << 6) | (pagenum & 0x3f);
  603:     cdb[3] = subpagenum;
  604:     cdb[4] = bufLen;
  605:     io_hdr.cmnd = cdb;
  606:     io_hdr.cmnd_len = sizeof(cdb);
  607:     io_hdr.sensep = sense;
  608:     io_hdr.max_sense_len = sizeof(sense);
  609:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
  610: 
  611:     if (!device->scsi_pass_through(&io_hdr))
  612:       return -device->get_errno();
  613:     scsi_do_sense_disect(&io_hdr, &sinfo);
  614:     status = scsiSimpleSenseFilter(&sinfo);
  615:     if (SIMPLE_ERR_TRY_AGAIN == status) {
  616:         if (!device->scsi_pass_through(&io_hdr))
  617:           return -device->get_errno();
  618:         scsi_do_sense_disect(&io_hdr, &sinfo);
  619:         status = scsiSimpleSenseFilter(&sinfo);
  620:     }
  621:     if ((0 == status) && (ALL_MODE_PAGES != pagenum)) {
  622:         int offset;
  623: 
  624:         offset = scsiModePageOffset(pBuf, bufLen, 0);
  625:         if (offset < 0)
  626:             return SIMPLE_ERR_BAD_RESP;
  627:         else if (pagenum != (pBuf[offset] & 0x3f))
  628:             return SIMPLE_ERR_BAD_RESP;
  629:     }
  630:     return status;
  631: }
  632: 
  633: /* Sends a 6 byte MODE SELECT command. Assumes given pBuf is the response
  634:  * from a corresponding 6 byte MODE SENSE command. Such a response should
  635:  * have a 4 byte header followed by 0 or more 8 byte block descriptors
  636:  * (normally 1) and then 1 mode page. Returns 0 if ok, 1 if NOT READY,
  637:  * 2 if command not supported (then MODE SELECT(10) may be supported),
  638:  * 3 if field in command not supported, 4 if bad parameter to command
  639:  * or returns negated errno. SPC-3 sections 6.7 and 7.4 (rev 22a) */
  640: int
  641: scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
  642: {
  643:     struct scsi_cmnd_io io_hdr;
  644:     struct scsi_sense_disect sinfo;
  645:     UINT8 cdb[6];
  646:     UINT8 sense[32];
  647:     int pg_offset, pg_len, hdr_plus_1_pg;
  648: 
  649:     pg_offset = 4 + pBuf[3];
  650:     if (pg_offset + 2 >= bufLen)
  651:         return -EINVAL;
  652:     pg_len = pBuf[pg_offset + 1] + 2;
  653:     hdr_plus_1_pg = pg_offset + pg_len;
  654:     if (hdr_plus_1_pg > bufLen)
  655:         return -EINVAL;
  656:     pBuf[0] = 0;    /* Length of returned mode sense data reserved for SELECT */
  657:     pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
  658:     memset(&io_hdr, 0, sizeof(io_hdr));
  659:     memset(cdb, 0, sizeof(cdb));
  660:     io_hdr.dxfer_dir = DXFER_TO_DEVICE;
  661:     io_hdr.dxfer_len = hdr_plus_1_pg;
  662:     io_hdr.dxferp = pBuf;
  663:     cdb[0] = MODE_SELECT;
  664:     cdb[1] = 0x10 | (sp & 1);      /* set PF (page format) bit always */
  665:     cdb[4] = hdr_plus_1_pg; /* make sure only one page sent */
  666:     io_hdr.cmnd = cdb;
  667:     io_hdr.cmnd_len = sizeof(cdb);
  668:     io_hdr.sensep = sense;
  669:     io_hdr.max_sense_len = sizeof(sense);
  670:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
  671: 
  672:     if (!device->scsi_pass_through(&io_hdr))
  673:       return -device->get_errno();
  674:     scsi_do_sense_disect(&io_hdr, &sinfo);
  675:     return scsiSimpleSenseFilter(&sinfo);
  676: }
  677: 
  678: /* MODE SENSE (10 byte). Returns 0 if ok, 1 if NOT READY, 2 if command
  679:  * not supported (then MODE SENSE(6) might be supported), 3 if field in
  680:  * command not supported or returns negated errno.
  681:  * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */
  682: int
  683: scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
  684:                 UINT8 *pBuf, int bufLen)
  685: {
  686:     struct scsi_cmnd_io io_hdr;
  687:     struct scsi_sense_disect sinfo;
  688:     UINT8 cdb[10];
  689:     UINT8 sense[32];
  690:     int status;
  691: 
  692:     memset(&io_hdr, 0, sizeof(io_hdr));
  693:     memset(cdb, 0, sizeof(cdb));
  694:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
  695:     io_hdr.dxfer_len = bufLen;
  696:     io_hdr.dxferp = pBuf;
  697:     cdb[0] = MODE_SENSE_10;
  698:     cdb[2] = (pc << 6) | (pagenum & 0x3f);
  699:     cdb[3] = subpagenum;
  700:     cdb[7] = (bufLen >> 8) & 0xff;
  701:     cdb[8] = bufLen & 0xff;
  702:     io_hdr.cmnd = cdb;
  703:     io_hdr.cmnd_len = sizeof(cdb);
  704:     io_hdr.sensep = sense;
  705:     io_hdr.max_sense_len = sizeof(sense);
  706:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
  707: 
  708:     if (!device->scsi_pass_through(&io_hdr))
  709:       return -device->get_errno();
  710:     scsi_do_sense_disect(&io_hdr, &sinfo);
  711:     status = scsiSimpleSenseFilter(&sinfo);
  712:     if (SIMPLE_ERR_TRY_AGAIN == status) {
  713:         if (!device->scsi_pass_through(&io_hdr))
  714:           return -device->get_errno();
  715:         scsi_do_sense_disect(&io_hdr, &sinfo);
  716:         status = scsiSimpleSenseFilter(&sinfo);
  717:     }
  718:     if ((0 == status) && (ALL_MODE_PAGES != pagenum)) {
  719:         int offset;
  720: 
  721:         offset = scsiModePageOffset(pBuf, bufLen, 1);
  722:         if (offset < 0)
  723:             return SIMPLE_ERR_BAD_RESP;
  724:         else if (pagenum != (pBuf[offset] & 0x3f))
  725:             return SIMPLE_ERR_BAD_RESP;
  726:     }
  727:     return status;
  728: }
  729: 
  730: /* Sends a 10 byte MODE SELECT command. Assumes given pBuf is the response
  731:  * from a corresponding 10 byte MODE SENSE command. Such a response should
  732:  * have a 8 byte header followed by 0 or more 8 byte block descriptors
  733:  * (normally 1) and then 1 mode page. Returns 0 if ok, 1 NOT REAFY, 2 if
  734:  * command not supported (then MODE SELECT(6) may be supported), 3 if field
  735:  * in command not supported, 4 if bad parameter to command or returns
  736:  * negated errno. SPC-3 sections 6.8 and 7.4 (rev 22a) */
  737: int
  738: scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
  739: {
  740:     struct scsi_cmnd_io io_hdr;
  741:     struct scsi_sense_disect sinfo;
  742:     UINT8 cdb[10];
  743:     UINT8 sense[32];
  744:     int pg_offset, pg_len, hdr_plus_1_pg;
  745: 
  746:     pg_offset = 8 + (pBuf[6] << 8) + pBuf[7];
  747:     if (pg_offset + 2 >= bufLen)
  748:         return -EINVAL;
  749:     pg_len = pBuf[pg_offset + 1] + 2;
  750:     hdr_plus_1_pg = pg_offset + pg_len;
  751:     if (hdr_plus_1_pg > bufLen)
  752:         return -EINVAL;
  753:     pBuf[0] = 0;
  754:     pBuf[1] = 0; /* Length of returned mode sense data reserved for SELECT */
  755:     pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
  756:     memset(&io_hdr, 0, sizeof(io_hdr));
  757:     memset(cdb, 0, sizeof(cdb));
  758:     io_hdr.dxfer_dir = DXFER_TO_DEVICE;
  759:     io_hdr.dxfer_len = hdr_plus_1_pg;
  760:     io_hdr.dxferp = pBuf;
  761:     cdb[0] = MODE_SELECT_10;
  762:     cdb[1] = 0x10 | (sp & 1);      /* set PF (page format) bit always */
  763:     cdb[8] = hdr_plus_1_pg; /* make sure only one page sent */
  764:     io_hdr.cmnd = cdb;
  765:     io_hdr.cmnd_len = sizeof(cdb);
  766:     io_hdr.sensep = sense;
  767:     io_hdr.max_sense_len = sizeof(sense);
  768:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
  769: 
  770:     if (!device->scsi_pass_through(&io_hdr))
  771:       return -device->get_errno();
  772:     scsi_do_sense_disect(&io_hdr, &sinfo);
  773:     return scsiSimpleSenseFilter(&sinfo);
  774: }
  775: 
  776: /* Standard INQUIRY returns 0 for ok, anything else is a major problem.
  777:  * bufLen should be 36 for unsafe devices (like USB mass storage stuff)
  778:  * otherwise they can lock up! SPC-3 sections 6.4 and 7.6 (rev 22a) */
  779: int
  780: scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen)
  781: {
  782:     struct scsi_sense_disect sinfo;
  783:     struct scsi_cmnd_io io_hdr;
  784:     UINT8 cdb[6];
  785:     UINT8 sense[32];
  786: 
  787:     if ((bufLen < 0) || (bufLen > 1023))
  788:         return -EINVAL;
  789:     memset(&io_hdr, 0, sizeof(io_hdr));
  790:     memset(cdb, 0, sizeof(cdb));
  791:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
  792:     io_hdr.dxfer_len = bufLen;
  793:     io_hdr.dxferp = pBuf;
  794:     cdb[0] = INQUIRY;
  795:     cdb[3] = (bufLen >> 8) & 0xff;
  796:     cdb[4] = (bufLen & 0xff);
  797:     io_hdr.cmnd = cdb;
  798:     io_hdr.cmnd_len = sizeof(cdb);
  799:     io_hdr.sensep = sense;
  800:     io_hdr.max_sense_len = sizeof(sense);
  801:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
  802: 
  803:     if (!device->scsi_pass_through(&io_hdr))
  804:       return -device->get_errno();
  805:     scsi_do_sense_disect(&io_hdr, &sinfo);
  806:     return scsiSimpleSenseFilter(&sinfo);
  807: }
  808: 
  809: /* INQUIRY to fetch Vital Page Data.  Returns 0 if ok, 1 if NOT READY
  810:  * (unlikely), 2 if command not supported, 3 if field in command not
  811:  * supported, 5 if response indicates that EVPD bit ignored or returns
  812:  * negated errno. SPC-3 section 6.4 and 7.6 (rev 22a) */
  813: int
  814: scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen)
  815: {
  816:     struct scsi_cmnd_io io_hdr;
  817:     struct scsi_sense_disect sinfo;
  818:     UINT8 cdb[6];
  819:     UINT8 sense[32];
  820:     int res;
  821: 
  822:     /* Assume SCSI_VPD_SUPPORTED_VPD_PAGES is first VPD page fetched */
  823:     if ((SCSI_VPD_SUPPORTED_VPD_PAGES != vpd_page) &&
  824:         supported_vpd_pages_p &&
  825:         (! supported_vpd_pages_p->is_supported(vpd_page)))
  826:         return 3;
  827: 
  828:     if ((bufLen < 0) || (bufLen > 1023))
  829:         return -EINVAL;
  830: try_again:
  831:     memset(&io_hdr, 0, sizeof(io_hdr));
  832:     memset(cdb, 0, sizeof(cdb));
  833:     if (bufLen > 1)
  834:         pBuf[1] = 0x0;
  835:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
  836:     io_hdr.dxfer_len = bufLen;
  837:     io_hdr.dxferp = pBuf;
  838:     cdb[0] = INQUIRY;
  839:     cdb[1] = 0x1;       /* set EVPD bit (enable Vital Product Data) */
  840:     cdb[2] = vpd_page;
  841:     cdb[3] = (bufLen >> 8) & 0xff;
  842:     cdb[4] = (bufLen & 0xff);
  843:     io_hdr.cmnd = cdb;
  844:     io_hdr.cmnd_len = sizeof(cdb);
  845:     io_hdr.sensep = sense;
  846:     io_hdr.max_sense_len = sizeof(sense);
  847:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
  848: 
  849:     if (!device->scsi_pass_through(&io_hdr))
  850:       return -device->get_errno();
  851:     scsi_do_sense_disect(&io_hdr, &sinfo);
  852:     if ((SCSI_STATUS_CHECK_CONDITION == io_hdr.scsi_status) &&
  853:         (SCSI_SK_ILLEGAL_REQUEST == sinfo.sense_key) &&
  854:         (SCSI_ASC_INVALID_FIELD == sinfo.asc) &&
  855:         (cdb[3] > 0)) {
  856:         bufLen &= 0xff; /* make sure cdb[3] is 0 next time around */
  857:         goto try_again;
  858:     }
  859: 
  860:     if ((res = scsiSimpleSenseFilter(&sinfo)))
  861:         return res;
  862:     /* Guard against devices that ignore EVPD bit and do standard INQUIRY */
  863:     if (bufLen > 1) {
  864:         if (vpd_page == pBuf[1]) {
  865:             if ((0x80 == vpd_page) && (bufLen > 2) && (0x0 != pBuf[2]))
  866:                 return SIMPLE_ERR_BAD_RESP;
  867:         } else
  868:             return SIMPLE_ERR_BAD_RESP;
  869:     }
  870:     return 0;
  871: }
  872: 
  873: /* REQUEST SENSE command. Returns 0 if ok, anything else major problem.
  874:  * SPC-3 section 6.27 (rev 22a) */
  875: int
  876: scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
  877: {
  878:     struct scsi_cmnd_io io_hdr;
  879:     UINT8 cdb[6];
  880:     UINT8 sense[32];
  881:     UINT8 buff[18];
  882:     int len;
  883:     UINT8 resp_code;
  884: 
  885:     memset(&io_hdr, 0, sizeof(io_hdr));
  886:     memset(cdb, 0, sizeof(cdb));
  887:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
  888:     io_hdr.dxfer_len = sizeof(buff);
  889:     io_hdr.dxferp = buff;
  890:     cdb[0] = REQUEST_SENSE;
  891:     cdb[4] = sizeof(buff);
  892:     io_hdr.cmnd = cdb;
  893:     io_hdr.cmnd_len = sizeof(cdb);
  894:     io_hdr.sensep = sense;
  895:     io_hdr.max_sense_len = sizeof(sense);
  896:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
  897: 
  898:     if (!device->scsi_pass_through(&io_hdr))
  899:       return -device->get_errno();
  900:     if (sense_info) {
  901:         resp_code = buff[0] & 0x7f;
  902:         sense_info->resp_code = resp_code;
  903:         sense_info->sense_key = buff[2] & 0xf;
  904:         sense_info->asc = 0;
  905:         sense_info->ascq = 0;
  906:         if ((0x70 == resp_code) || (0x71 == resp_code)) {
  907:             len = buff[7] + 8;
  908:             if (len > 13) {
  909:                 sense_info->asc = buff[12];
  910:                 sense_info->ascq = buff[13];
  911:             }
  912:         }
  913:     // fill progrss indicator, if available
  914:     sense_info->progress = -1;
  915:     switch (resp_code) {
  916:       const unsigned char * ucp;
  917:       int sk, sk_pr;
  918:       case 0x70:
  919:       case 0x71:
  920:           sk = (buff[2] & 0xf);
  921:           if ((sizeof(buff) < 18) ||
  922:               ((SCSI_SK_NO_SENSE != sk) && (SCSI_SK_NOT_READY != sk))) {
  923:               break;
  924:           }
  925:           if (buff[15] & 0x80) {        /* SKSV bit set */
  926:               sense_info->progress = (buff[16] << 8) + buff[17];
  927:               break;
  928:           } else {
  929:               break;
  930:           }
  931:       case 0x72:
  932:       case 0x73:
  933:           /* sense key specific progress (0x2) or progress descriptor (0xa) */
  934:           sk = (buff[1] & 0xf);
  935:           sk_pr = (SCSI_SK_NO_SENSE == sk) || (SCSI_SK_NOT_READY == sk);
  936:           if (sk_pr && ((ucp = sg_scsi_sense_desc_find(buff, sizeof(buff), 2))) &&
  937:               (0x6 == ucp[1]) && (0x80 & ucp[4])) {
  938:               sense_info->progress = (ucp[5] << 8) + ucp[6];
  939:               break;
  940:           } else if (((ucp = sg_scsi_sense_desc_find(buff, sizeof(buff), 0xa))) &&
  941:                      ((0x6 == ucp[1]))) {
  942:               sense_info->progress = (ucp[6] << 8) + ucp[7];
  943:               break;
  944:           } else
  945:               break;
  946:       default:
  947:           return 0;
  948:       }
  949:     }
  950:     return 0;
  951: }
  952: 
  953: /* SEND DIAGNOSTIC command.  Returns 0 if ok, 1 if NOT READY, 2 if command
  954:  * not supported, 3 if field in command not supported or returns negated
  955:  * errno. SPC-3 section 6.28 (rev 22a) */
  956: int
  957: scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf,
  958:                    int bufLen)
  959: {
  960:     struct scsi_cmnd_io io_hdr;
  961:     struct scsi_sense_disect sinfo;
  962:     UINT8 cdb[6];
  963:     UINT8 sense[32];
  964: 
  965:     memset(&io_hdr, 0, sizeof(io_hdr));
  966:     memset(cdb, 0, sizeof(cdb));
  967:     io_hdr.dxfer_dir = bufLen ? DXFER_TO_DEVICE: DXFER_NONE;
  968:     io_hdr.dxfer_len = bufLen;
  969:     io_hdr.dxferp = pBuf;
  970:     cdb[0] = SEND_DIAGNOSTIC;
  971:     if (SCSI_DIAG_DEF_SELF_TEST == functioncode)
  972:         cdb[1] = 0x4;  /* SelfTest bit */
  973:     else if (SCSI_DIAG_NO_SELF_TEST != functioncode)
  974:         cdb[1] = (functioncode & 0x7) << 5; /* SelfTest _code_ */
  975:     else   /* SCSI_DIAG_NO_SELF_TEST == functioncode */
  976:         cdb[1] = 0x10;  /* PF bit */
  977:     cdb[3] = (bufLen >> 8) & 0xff;
  978:     cdb[4] = bufLen & 0xff;
  979:     io_hdr.cmnd = cdb;
  980:     io_hdr.cmnd_len = sizeof(cdb);
  981:     io_hdr.sensep = sense;
  982:     io_hdr.max_sense_len = sizeof(sense);
  983:     /* worst case is an extended foreground self test on a big disk */
  984:     io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST;
  985: 
  986:     if (!device->scsi_pass_through(&io_hdr))
  987:       return -device->get_errno();
  988:     scsi_do_sense_disect(&io_hdr, &sinfo);
  989:     return scsiSimpleSenseFilter(&sinfo);
  990: }
  991: 
  992: /* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if
  993:  * command not supported, 3 if field in command not supported or returns
  994:  * negated errno. SPC-3 section 6.18 (rev 22a) */
  995: int
  996: scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf,
  997:                       int bufLen)
  998: {
  999:     struct scsi_cmnd_io io_hdr;
 1000:     struct scsi_sense_disect sinfo;
 1001:     UINT8 cdb[6];
 1002:     UINT8 sense[32];
 1003: 
 1004:     memset(&io_hdr, 0, sizeof(io_hdr));
 1005:     memset(cdb, 0, sizeof(cdb));
 1006:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
 1007:     io_hdr.dxfer_len = bufLen;
 1008:     io_hdr.dxferp = pBuf;
 1009:     cdb[0] = RECEIVE_DIAGNOSTIC;
 1010:     cdb[1] = pcv;
 1011:     cdb[2] = pagenum;
 1012:     cdb[3] = (bufLen >> 8) & 0xff;
 1013:     cdb[4] = bufLen & 0xff;
 1014:     io_hdr.cmnd = cdb;
 1015:     io_hdr.cmnd_len = sizeof(cdb);
 1016:     io_hdr.sensep = sense;
 1017:     io_hdr.max_sense_len = sizeof(sense);
 1018:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 1019: 
 1020:     if (!device->scsi_pass_through(&io_hdr))
 1021:       return -device->get_errno();
 1022:     scsi_do_sense_disect(&io_hdr, &sinfo);
 1023:     return scsiSimpleSenseFilter(&sinfo);
 1024: }
 1025: 
 1026: /* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */
 1027: static int
 1028: _testunitready(scsi_device * device, struct scsi_sense_disect * sinfo)
 1029: {
 1030:     struct scsi_cmnd_io io_hdr;
 1031:     UINT8 cdb[6];
 1032:     UINT8 sense[32];
 1033: 
 1034:     memset(&io_hdr, 0, sizeof(io_hdr));
 1035:     memset(cdb, 0, sizeof(cdb));
 1036:     io_hdr.dxfer_dir = DXFER_NONE;
 1037:     io_hdr.dxfer_len = 0;
 1038:     io_hdr.dxferp = NULL;
 1039:     cdb[0] = TEST_UNIT_READY;
 1040:     io_hdr.cmnd = cdb;
 1041:     io_hdr.cmnd_len = sizeof(cdb);
 1042:     io_hdr.sensep = sense;
 1043:     io_hdr.max_sense_len = sizeof(sense);
 1044:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 1045: 
 1046:     if (!device->scsi_pass_through(&io_hdr))
 1047:       return -device->get_errno();
 1048:     scsi_do_sense_disect(&io_hdr, sinfo);
 1049:     return 0;
 1050: }
 1051: 
 1052: /* Returns 0 for device responds and media ready, 1 for device responds and
 1053:    media not ready, or returns a negated errno value */
 1054: int
 1055: scsiTestUnitReady(scsi_device * device)
 1056: {
 1057:     struct scsi_sense_disect sinfo;
 1058:     int status;
 1059: 
 1060:     status = _testunitready(device, &sinfo);
 1061:     if (0 != status)
 1062:         return status;
 1063:     status = scsiSimpleSenseFilter(&sinfo);
 1064:     if (SIMPLE_ERR_TRY_AGAIN == status) {
 1065:         /* power on reset, media changed, ok ... try again */
 1066:         status = _testunitready(device, &sinfo);
 1067:         if (0 != status)
 1068:             return status;
 1069:         status = scsiSimpleSenseFilter(&sinfo);
 1070:     }
 1071:     return status;
 1072: }
 1073: 
 1074: /* READ DEFECT (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
 1075:  * command not supported, 3 if field in command not supported or returns
 1076:  * negated errno. SBC-2 section 5.12 (rev 16) */
 1077: int
 1078: scsiReadDefect10(scsi_device * device, int req_plist, int req_glist,
 1079:                  int dl_format, UINT8 *pBuf, int bufLen)
 1080: {
 1081:     struct scsi_cmnd_io io_hdr;
 1082:     struct scsi_sense_disect sinfo;
 1083:     UINT8 cdb[10];
 1084:     UINT8 sense[32];
 1085: 
 1086:     memset(&io_hdr, 0, sizeof(io_hdr));
 1087:     memset(cdb, 0, sizeof(cdb));
 1088:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
 1089:     io_hdr.dxfer_len = bufLen;
 1090:     io_hdr.dxferp = pBuf;
 1091:     cdb[0] = READ_DEFECT_10;
 1092:     cdb[2] = (unsigned char)(((req_plist << 4) & 0x10) |
 1093:                ((req_glist << 3) & 0x8) | (dl_format & 0x7));
 1094:     cdb[7] = (bufLen >> 8) & 0xff;
 1095:     cdb[8] = bufLen & 0xff;
 1096:     io_hdr.cmnd = cdb;
 1097:     io_hdr.cmnd_len = sizeof(cdb);
 1098:     io_hdr.sensep = sense;
 1099:     io_hdr.max_sense_len = sizeof(sense);
 1100:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 1101: 
 1102:     if (!device->scsi_pass_through(&io_hdr))
 1103:       return -device->get_errno();
 1104:     scsi_do_sense_disect(&io_hdr, &sinfo);
 1105:     return scsiSimpleSenseFilter(&sinfo);
 1106: }
 1107: 
 1108: /* READ DEFECT (12) command. Returns 0 if ok, 1 if NOT READY, 2 if
 1109:  * command not supported, 3 if field in command not supported or returns
 1110:  * negated errno. SBC-3 section 5.18 (rev 35; vale Mark Evans) */
 1111: int
 1112: scsiReadDefect12(scsi_device * device, int req_plist, int req_glist,
 1113:                  int dl_format, int addrDescIndex, UINT8 *pBuf, int bufLen)
 1114: {
 1115:     struct scsi_cmnd_io io_hdr;
 1116:     struct scsi_sense_disect sinfo;
 1117:     UINT8 cdb[12];
 1118:     UINT8 sense[32];
 1119: 
 1120:     memset(&io_hdr, 0, sizeof(io_hdr));
 1121:     memset(cdb, 0, sizeof(cdb));
 1122:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
 1123:     io_hdr.dxfer_len = bufLen;
 1124:     io_hdr.dxferp = pBuf;
 1125:     cdb[0] = READ_DEFECT_12;
 1126:     cdb[1] = (unsigned char)(((req_plist << 4) & 0x10) |
 1127:                ((req_glist << 3) & 0x8) | (dl_format & 0x7));
 1128:     cdb[2] = (addrDescIndex >> 24) & 0xff;
 1129:     cdb[3] = (addrDescIndex >> 16) & 0xff;
 1130:     cdb[4] = (addrDescIndex >> 8) & 0xff;
 1131:     cdb[5] = addrDescIndex & 0xff;
 1132:     cdb[6] = (bufLen >> 24) & 0xff;
 1133:     cdb[7] = (bufLen >> 16) & 0xff;
 1134:     cdb[8] = (bufLen >> 8) & 0xff;
 1135:     cdb[9] = bufLen & 0xff;
 1136:     io_hdr.cmnd = cdb;
 1137:     io_hdr.cmnd_len = sizeof(cdb);
 1138:     io_hdr.sensep = sense;
 1139:     io_hdr.max_sense_len = sizeof(sense);
 1140:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 1141: 
 1142:     if (!device->scsi_pass_through(&io_hdr))
 1143:       return -device->get_errno();
 1144:     scsi_do_sense_disect(&io_hdr, &sinfo);
 1145:     return scsiSimpleSenseFilter(&sinfo);
 1146: }
 1147: 
 1148: /* READ CAPACITY (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
 1149:  * command not supported, 3 if field in command not supported or returns
 1150:  * negated errno. SBC-3 section 5.15 (rev 26) */
 1151: int
 1152: scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap,
 1153:                    unsigned int * lb_sizep)
 1154: {
 1155:     int res;
 1156:     struct scsi_cmnd_io io_hdr;
 1157:     struct scsi_sense_disect sinfo;
 1158:     UINT8 cdb[10];
 1159:     UINT8 sense[32];
 1160:     UINT8 resp[8];
 1161: 
 1162:     memset(&io_hdr, 0, sizeof(io_hdr));
 1163:     memset(cdb, 0, sizeof(cdb));
 1164:     memset(resp, 0, sizeof(resp));
 1165:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
 1166:     io_hdr.dxfer_len = sizeof(resp);
 1167:     io_hdr.dxferp = resp;
 1168:     cdb[0] = READ_CAPACITY_10;
 1169:     io_hdr.cmnd = cdb;
 1170:     io_hdr.cmnd_len = sizeof(cdb);
 1171:     io_hdr.sensep = sense;
 1172:     io_hdr.max_sense_len = sizeof(sense);
 1173:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 1174: 
 1175:     if (!device->scsi_pass_through(&io_hdr))
 1176:       return -device->get_errno();
 1177:     scsi_do_sense_disect(&io_hdr, &sinfo);
 1178:     res = scsiSimpleSenseFilter(&sinfo);
 1179:     if (res)
 1180:         return res;
 1181:     if (last_lbap)
 1182:         *last_lbap = (resp[0] << 24) | (resp[1] << 16) | (resp[2] << 8) |
 1183:                      resp[3];
 1184:     if (lb_sizep)
 1185:         *lb_sizep = (resp[4] << 24) | (resp[5] << 16) | (resp[6] << 8) |
 1186:                     resp[7];
 1187:     return 0;
 1188: }
 1189: 
 1190: /* READ CAPACITY (16) command. The bufLen argument should be 32. Returns 0
 1191:  * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command
 1192:  * not supported or returns negated errno. SBC-3 section 5.16 (rev 26) */
 1193: int
 1194: scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen)
 1195: {
 1196:     struct scsi_cmnd_io io_hdr;
 1197:     struct scsi_sense_disect sinfo;
 1198:     UINT8 cdb[16];
 1199:     UINT8 sense[32];
 1200: 
 1201:     memset(&io_hdr, 0, sizeof(io_hdr));
 1202:     memset(cdb, 0, sizeof(cdb));
 1203:     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
 1204:     io_hdr.dxfer_len = bufLen;
 1205:     io_hdr.dxferp = pBuf;
 1206:     cdb[0] = READ_CAPACITY_16;
 1207:     cdb[1] = SAI_READ_CAPACITY_16;
 1208:     cdb[10] = (bufLen >> 24) & 0xff;
 1209:     cdb[11] = (bufLen >> 16) & 0xff;
 1210:     cdb[12] = (bufLen >> 8) & 0xff;
 1211:     cdb[13] = bufLen & 0xff;
 1212:     io_hdr.cmnd = cdb;
 1213:     io_hdr.cmnd_len = sizeof(cdb);
 1214:     io_hdr.sensep = sense;
 1215:     io_hdr.max_sense_len = sizeof(sense);
 1216:     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 1217: 
 1218:     if (!device->scsi_pass_through(&io_hdr))
 1219:       return -device->get_errno();
 1220:     scsi_do_sense_disect(&io_hdr, &sinfo);
 1221:     return scsiSimpleSenseFilter(&sinfo);
 1222: }
 1223: 
 1224: /* Return number of bytes of storage in 'device' or 0 if error. If
 1225:  * successful and lb_sizep is not NULL then the logical block size
 1226:  * in bytes is written to the location pointed to by lb_sizep. */
 1227: uint64_t
 1228: scsiGetSize(scsi_device * device, unsigned int * lb_sizep,
 1229:             int * lb_per_pb_expp)
 1230: {
 1231:     unsigned int last_lba = 0, lb_size = 0;
 1232:     int k, res;
 1233:     uint64_t ret_val = 0;
 1234:     UINT8 rc16resp[32];
 1235: 
 1236:     res = scsiReadCapacity10(device, &last_lba, &lb_size);
 1237:     if (res) {
 1238:         if (scsi_debugmode)
 1239:             pout("scsiGetSize: READ CAPACITY(10) failed, res=%d\n", res);
 1240:         return 0;
 1241:     }
 1242:     if (0xffffffff == last_lba) {
 1243:         res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp));
 1244:         if (res) {
 1245:             if (scsi_debugmode)
 1246:                 pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);
 1247:             return 0;
 1248:         }
 1249:         for (k = 0; k < 8; ++k) {
 1250:             if (k > 0)
 1251:                 ret_val <<= 8;
 1252:             ret_val |= rc16resp[k + 0];
 1253:         }
 1254:         if (lb_per_pb_expp)
 1255:             *lb_per_pb_expp = (rc16resp[13] & 0xf);
 1256:     } else {
 1257:         ret_val = last_lba;
 1258:         if (lb_per_pb_expp)
 1259:             *lb_per_pb_expp = 0;
 1260:     }
 1261:     if (lb_sizep)
 1262:         *lb_sizep = lb_size;
 1263:     ++ret_val;  /* last_lba is origin 0 so need to bump to get number of */
 1264:     return ret_val * lb_size;
 1265: }
 1266: 
 1267: /* Gets drive Protection and Logical/Physical block information. Writes
 1268:  * back bytes 12 to 31 from a READ CAPACITY 16 command to the rc16_12_31p
 1269:  * pointer. So rc16_12_31p should point to an array of 20 bytes. Returns 0
 1270:  * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command
 1271:  * not supported or returns negated errno. */
 1272: int
 1273: scsiGetProtPBInfo(scsi_device * device, unsigned char * rc16_12_31p)
 1274: {
 1275:     int res;
 1276:     UINT8 rc16resp[32];
 1277: 
 1278:     res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp));
 1279:     if (res) {
 1280:         if (scsi_debugmode)
 1281:             pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);
 1282:         return res;
 1283:     }
 1284:     if (rc16_12_31p)
 1285:         memcpy(rc16_12_31p, rc16resp + 12, 20);
 1286:     return 0;
 1287: }
 1288: 
 1289: /* Offset into mode sense (6 or 10 byte) response that actual mode page
 1290:  * starts at (relative to resp[0]). Returns -1 if problem */
 1291: int
 1292: scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
 1293: {
 1294:     int resp_len, bd_len;
 1295:     int offset = -1;
 1296: 
 1297:     if (resp) {
 1298:         if (10 == modese_len) {
 1299:             resp_len = (resp[0] << 8) + resp[1] + 2;
 1300:             bd_len = (resp[6] << 8) + resp[7];
 1301:             offset = bd_len + 8;
 1302:         } else {
 1303:             resp_len = resp[0] + 1;
 1304:             bd_len = resp[3];
 1305:             offset = bd_len + 4;
 1306:         }
 1307:         if ((offset + 2) > len) {
 1308:             pout("scsiModePageOffset: raw_curr too small, offset=%d "
 1309:                  "resp_len=%d bd_len=%d\n", offset, resp_len, bd_len);
 1310:             offset = -1;
 1311:         } else if ((offset + 2) > resp_len) {
 1312:              if ((resp_len > 2) || scsi_debugmode)
 1313:                 pout("scsiModePageOffset: response length too short, "
 1314:                      "resp_len=%d offset=%d bd_len=%d\n", resp_len,
 1315:                      offset, bd_len);
 1316:             offset = -1;
 1317:         }
 1318:     }
 1319:     return offset;
 1320: }
 1321: 
 1322: /* IEC mode page byte 2 bit masks */
 1323: #define DEXCPT_ENABLE   0x08
 1324: #define EWASC_ENABLE    0x10
 1325: #define DEXCPT_DISABLE  0xf7
 1326: #define EWASC_DISABLE   0xef
 1327: #define TEST_DISABLE    0xfb
 1328: 
 1329: /* Fetches the Informational Exceptions Control mode page. First tries
 1330:  * the 6 byte MODE SENSE command and if that fails with an illegal opcode
 1331:  * tries a 10 byte MODE SENSE command. Returns 0 if successful, a positive
 1332:  * number if a known error (see  SIMPLE_ERR_ ...) or a negative errno
 1333:  * value. */
 1334: int
 1335: scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp,
 1336:                   int modese_len)
 1337: {
 1338:     int err = 0;
 1339: 
 1340:     memset(iecp, 0, sizeof(*iecp));
 1341:     iecp->modese_len = modese_len;
 1342:     iecp->requestedCurrent = 1;
 1343:     if (iecp->modese_len <= 6) {
 1344:         if ((err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
 1345:                                  0, MPAGE_CONTROL_CURRENT,
 1346:                                  iecp->raw_curr, sizeof(iecp->raw_curr)))) {
 1347:             if (SIMPLE_ERR_BAD_OPCODE == err)
 1348:                 iecp->modese_len = 10;
 1349:             else {
 1350:                 iecp->modese_len = 0;
 1351:                 return err;
 1352:             }
 1353:         } else if (0 == iecp->modese_len)
 1354:             iecp->modese_len = 6;
 1355:     }
 1356:     if (10 == iecp->modese_len) {
 1357:         err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
 1358:                               0, MPAGE_CONTROL_CURRENT,
 1359:                               iecp->raw_curr, sizeof(iecp->raw_curr));
 1360:         if (err) {
 1361:             iecp->modese_len = 0;
 1362:             return err;
 1363:         }
 1364:     }
 1365:     iecp->gotCurrent = 1;
 1366:     iecp->requestedChangeable = 1;
 1367:     if (10 == iecp->modese_len)
 1368:         err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
 1369:                               0, MPAGE_CONTROL_CHANGEABLE,
 1370:                               iecp->raw_chg, sizeof(iecp->raw_chg));
 1371:     else if (6 == iecp->modese_len)
 1372:         err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
 1373:                             0, MPAGE_CONTROL_CHANGEABLE,
 1374:                             iecp->raw_chg, sizeof(iecp->raw_chg));
 1375:     if (err)
 1376:         return err;
 1377:     iecp->gotChangeable = 1;
 1378:     return 0;
 1379: }
 1380: 
 1381: int
 1382: scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp)
 1383: {
 1384:     int offset;
 1385: 
 1386:     if (iecp && iecp->gotCurrent) {
 1387:         offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
 1388:                                     iecp->modese_len);
 1389:         if (offset >= 0)
 1390:             return (iecp->raw_curr[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
 1391:         else
 1392:             return 0;
 1393:     } else
 1394:         return 0;
 1395: }
 1396: 
 1397: int
 1398: scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp)
 1399: {
 1400:     int offset;
 1401: 
 1402:     if (iecp && iecp->gotCurrent) {
 1403:         offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
 1404:                                     iecp->modese_len);
 1405:         if (offset >= 0)
 1406:             return (iecp->raw_curr[offset + 2] & EWASC_ENABLE) ? 1 : 0;
 1407:         else
 1408:             return 0;
 1409:     } else
 1410:         return 0;
 1411: }
 1412: 
 1413: /* set EWASC and clear PERF, EBF, DEXCPT TEST and LOGERR */
 1414: #define SCSI_IEC_MP_BYTE2_ENABLED 0x10
 1415: #define SCSI_IEC_MP_BYTE2_TEST_MASK 0x4
 1416: /* exception/warning via an unrequested REQUEST SENSE command */
 1417: #define SCSI_IEC_MP_MRIE 6
 1418: #define SCSI_IEC_MP_INTERVAL_T 0
 1419: #define SCSI_IEC_MP_REPORT_COUNT 1
 1420: 
 1421: /* Try to set (or clear) both Exception Control and Warning in the IE
 1422:  * mode page subject to the "changeable" mask. The object pointed to
 1423:  * by iecp is (possibly) inaccurate after this call, therefore
 1424:  * scsiFetchIECmpage() should be called again if the IEC mode page
 1425:  * is to be re-examined.
 1426:  * When -r ioctl is invoked 3 or more time on 'smartctl -s on ...'
 1427:  * then set the TEST bit (causes asc,ascq pair of 0x5d,0xff). */
 1428: int
 1429: scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
 1430:                                   const struct scsi_iec_mode_page *iecp)
 1431: {
 1432:     int k, offset, resp_len;
 1433:     int err = 0;
 1434:     UINT8 rout[SCSI_IECMP_RAW_LEN];
 1435:     int sp, eCEnabled, wEnabled;
 1436: 
 1437:     if ((! iecp) || (! iecp->gotCurrent))
 1438:         return -EINVAL;
 1439:     offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
 1440:                                 iecp->modese_len);
 1441:     if (offset < 0)
 1442:         return -EINVAL;
 1443:     memcpy(rout, iecp->raw_curr, SCSI_IECMP_RAW_LEN);
 1444:     if (10 == iecp->modese_len) {
 1445:         resp_len = (rout[0] << 8) + rout[1] + 2;
 1446:         rout[3] &= 0xef;    /* for disks mask out DPOFUA bit */
 1447:     } else {
 1448:         resp_len = rout[0] + 1;
 1449:         rout[2] &= 0xef;    /* for disks mask out DPOFUA bit */
 1450:     }
 1451:     sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
 1452:     if (enabled) {
 1453:         rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED;
 1454:         if (scsi_debugmode > 2)
 1455:             rout[offset + 2] |= SCSI_IEC_MP_BYTE2_TEST_MASK;
 1456:         rout[offset + 3] = SCSI_IEC_MP_MRIE;
 1457:         rout[offset + 4] = (SCSI_IEC_MP_INTERVAL_T >> 24) & 0xff;
 1458:         rout[offset + 5] = (SCSI_IEC_MP_INTERVAL_T >> 16) & 0xff;
 1459:         rout[offset + 6] = (SCSI_IEC_MP_INTERVAL_T >> 8) & 0xff;
 1460:         rout[offset + 7] = SCSI_IEC_MP_INTERVAL_T & 0xff;
 1461:         rout[offset + 8] = (SCSI_IEC_MP_REPORT_COUNT >> 24) & 0xff;
 1462:         rout[offset + 9] = (SCSI_IEC_MP_REPORT_COUNT >> 16) & 0xff;
 1463:         rout[offset + 10] = (SCSI_IEC_MP_REPORT_COUNT >> 8) & 0xff;
 1464:         rout[offset + 11] = SCSI_IEC_MP_REPORT_COUNT & 0xff;
 1465:         if (iecp->gotChangeable) {
 1466:             UINT8 chg2 = iecp->raw_chg[offset + 2];
 1467: 
 1468:             rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) :
 1469:                                       iecp->raw_curr[offset + 2];
 1470:             for (k = 3; k < 12; ++k) {
 1471:                 if (0 == iecp->raw_chg[offset + k])
 1472:                     rout[offset + k] = iecp->raw_curr[offset + k];
 1473:             }
 1474:         }
 1475:         if (0 == memcmp(&rout[offset + 2], &iecp->raw_chg[offset + 2], 10)) {
 1476:             if (scsi_debugmode > 0)
 1477:                 pout("scsiSetExceptionControlAndWarning: already enabled\n");
 1478:             return 0;
 1479:         }
 1480:     } else { /* disabling Exception Control and (temperature) Warnings */
 1481:         eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
 1482:         wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0;
 1483:         if ((! eCEnabled) && (! wEnabled)) {
 1484:             if (scsi_debugmode > 0)
 1485:                 pout("scsiSetExceptionControlAndWarning: already disabled\n");
 1486:             return 0;   /* nothing to do, leave other setting alone */
 1487:         }
 1488:         if (wEnabled)
 1489:             rout[offset + 2] &= EWASC_DISABLE;
 1490:         if (eCEnabled) {
 1491:             if (iecp->gotChangeable &&
 1492:                 (iecp->raw_chg[offset + 2] & DEXCPT_ENABLE))
 1493:                 rout[offset + 2] |= DEXCPT_ENABLE;
 1494:                 rout[offset + 2] &= TEST_DISABLE;/* clear TEST bit for spec */
 1495:         }
 1496:     }
 1497:     if (10 == iecp->modese_len)
 1498:         err = scsiModeSelect10(device, sp, rout, resp_len);
 1499:     else if (6 == iecp->modese_len)
 1500:         err = scsiModeSelect(device, sp, rout, resp_len);
 1501:     return err;
 1502: }
 1503: 
 1504: int
 1505: scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp)
 1506: {
 1507:     UINT8 tBuf[252];
 1508:     int err;
 1509: 
 1510:     memset(tBuf, 0, sizeof(tBuf));
 1511:     if ((err = scsiLogSense(device, TEMPERATURE_LPAGE, 0, tBuf,
 1512:                             sizeof(tBuf), 0))) {
 1513:         *currenttemp = 0;
 1514:         *triptemp = 0;
 1515:         pout("Log Sense for temperature failed [%s]\n", scsiErrString(err));
 1516:         return err;
 1517:     }
 1518:     *currenttemp = tBuf[9];
 1519:     *triptemp = tBuf[15];
 1520:     return 0;
 1521: }
 1522: 
 1523: /* Read informational exception log page or Request Sense response.
 1524:  * Fetching asc/ascq code potentially flagging an exception or warning.
 1525:  * Returns 0 if ok, else error number. A current temperature of 255
 1526:  * (Celsius) implies that the temperature not available. */
 1527: int
 1528: scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
 1529:             UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp)
 1530: {
 1531:     UINT8 tBuf[252];
 1532:     struct scsi_sense_disect sense_info;
 1533:     int err;
 1534:     int temperatureSet = 0;
 1535:     unsigned short pagesize;
 1536:     UINT8 currTemp, trTemp;
 1537: 
 1538:     *asc = 0;
 1539:     *ascq = 0;
 1540:     *currenttemp = 0;
 1541:     *triptemp = 0;
 1542:     memset(tBuf,0,sizeof(tBuf)); // need to clear stack space of junk
 1543:     memset(&sense_info, 0, sizeof(sense_info));
 1544:     if (hasIELogPage) {
 1545:         if ((err = scsiLogSense(device, IE_LPAGE, 0, tBuf,
 1546:                                 sizeof(tBuf), 0))) {
 1547:             pout("Log Sense failed, IE page [%s]\n", scsiErrString(err));
 1548:             return err;
 1549:         }
 1550:         // pull out page size from response, don't forget to add 4
 1551:         pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4;
 1552:         if ((pagesize < 4) || tBuf[4] || tBuf[5]) {
 1553:             pout("Log Sense failed, IE page, bad parameter code or length\n");
 1554:             return SIMPLE_ERR_BAD_PARAM;
 1555:         }
 1556:         if (tBuf[7] > 1) {
 1557:             sense_info.asc = tBuf[8];
 1558:             sense_info.ascq = tBuf[9];
 1559:             if (! hasTempLogPage) {
 1560:                 if (tBuf[7] > 2)
 1561:                     *currenttemp = tBuf[10];
 1562:                 if (tBuf[7] > 3)        /* IBM extension in SMART (IE) lpage */
 1563:                     *triptemp = tBuf[11];
 1564:             }
 1565:         }
 1566:     }
 1567:     if (0 == sense_info.asc) {
 1568:         /* ties in with MRIE field of 6 in IEC mode page (0x1c) */
 1569:         if ((err = scsiRequestSense(device, &sense_info))) {
 1570:             pout("Request Sense failed, [%s]\n", scsiErrString(err));
 1571:             return err;
 1572:         }
 1573:     }
 1574:     *asc = sense_info.asc;
 1575:     *ascq = sense_info.ascq;
 1576:     if ((! temperatureSet) && hasTempLogPage) {
 1577:         if (0 == scsiGetTemp(device, &currTemp, &trTemp)) {
 1578:             *currenttemp = currTemp;
 1579:             *triptemp = trTemp;
 1580:         }
 1581:     }
 1582:     return 0;
 1583: }
 1584: 
 1585: // The first character (W, C, I) tells the severity
 1586: static const char * TapeAlertsMessageTable[]= {
 1587:     " ",
 1588:     /* 0x01 */
 1589:    "W: The tape drive is having problems reading data. No data has been lost,\n"
 1590:        "  but there has been a reduction in the performance of the tape.",
 1591:     /* 0x02 */
 1592:    "W: The tape drive is having problems writing data. No data has been lost,\n"
 1593:        "  but there has been a reduction in the capacity of the tape.",
 1594:     /* 0x03 */
 1595:    "W: The operation has stopped because an error has occurred while reading\n"
 1596:        "  or writing data that the drive cannot correct.",
 1597:     /* 0x04 */
 1598:    "C: Your data is at risk:\n"
 1599:        "  1. Copy any data you require from this tape. \n"
 1600:        "  2. Do not use this tape again.\n"
 1601:        "  3. Restart the operation with a different tape.",
 1602:     /* 0x05 */
 1603:    "C: The tape is damaged or the drive is faulty. Call the tape drive\n"
 1604:        "  supplier helpline.",
 1605:     /* 0x06 */
 1606:    "C: The tape is from a faulty batch or the tape drive is faulty:\n"
 1607:        "  1. Use a good tape to test the drive.\n"
 1608:        "  2. If problem persists, call the tape drive supplier helpline.",
 1609:     /* 0x07 */
 1610:    "W: The tape cartridge has reached the end of its calculated useful life:\n"
 1611:        "  1. Copy data you need to another tape.\n"
 1612:        "  2. Discard the old tape.",
 1613:     /* 0x08 */
 1614:    "W: The tape cartridge is not data-grade. Any data you back up to the tape\n"
 1615:        "  is at risk. Replace the cartridge with a data-grade tape.",
 1616:     /* 0x09 */
 1617:    "C: You are trying to write to a write-protected cartridge. Remove the\n"
 1618:        "  write-protection or use another tape.",
 1619:     /* 0x0a */
 1620:    "I: You cannot eject the cartridge because the tape drive is in use. Wait\n"
 1621:        "  until the operation is complete before ejecting the cartridge.",
 1622:     /* 0x0b */
 1623:    "I: The tape in the drive is a cleaning cartridge.",
 1624:     /* 0x0c */
 1625:    "I: You have tried to load a cartridge of a type which is not supported\n"
 1626:        "  by this drive.",
 1627:     /* 0x0d */
 1628:    "C: The operation has failed because the tape in the drive has experienced\n"
 1629:        "  a mechanical failure:\n"
 1630:        "  1. Discard the old tape.\n"
 1631:        "  2. Restart the operation with a different tape.",
 1632:     /* 0x0e */
 1633:    "C: The operation has failed because the tape in the drive has experienced\n"
 1634:        "  a mechanical failure:\n"
 1635:        "  1. Do not attempt to extract the tape cartridge\n"
 1636:        "  2. Call the tape drive supplier helpline.",
 1637:     /* 0x0f */
 1638:    "W: The memory in the tape cartridge has failed, which reduces\n"
 1639:        "  performance. Do not use the cartridge for further write operations.",
 1640:     /* 0x10 */
 1641:    "C: The operation has failed because the tape cartridge was manually\n"
 1642:        "  de-mounted while the tape drive was actively writing or reading.",
 1643:     /* 0x11 */
 1644:    "W: You have loaded a cartridge of a type that is read-only in this drive.\n"
 1645:        "  The cartridge will appear as write-protected.",
 1646:     /* 0x12 */
 1647:    "W: The tape directory on the tape cartridge has been corrupted. File\n"
 1648:        "  search performance will be degraded. The tape directory can be rebuilt\n"
 1649:        "  by reading all the data on the cartridge.",
 1650:     /* 0x13 */
 1651:    "I: The tape cartridge is nearing the end of its calculated life. It is\n"
 1652:        "  recommended that you:\n"
 1653:        "  1. Use another tape cartridge for your next backup.\n"
 1654:        "  2. Store this tape in a safe place in case you need to restore "
 1655:        "  data from it.",
 1656:     /* 0x14 */
 1657:    "C: The tape drive needs cleaning:\n"
 1658:        "  1. If the operation has stopped, eject the tape and clean the drive.\n"
 1659:        "  2. If the operation has not stopped, wait for it to finish and then\n"
 1660:        "  clean the drive.\n"
 1661:        "  Check the tape drive users manual for device specific cleaning instructions.",
 1662:     /* 0x15 */
 1663:    "W: The tape drive is due for routine cleaning:\n"
 1664:        "  1. Wait for the current operation to finish.\n"
 1665:        "  2. The use a cleaning cartridge.\n"
 1666:        "  Check the tape drive users manual for device specific cleaning instructions.",
 1667:     /* 0x16 */
 1668:    "C: The last cleaning cartridge used in the tape drive has worn out:\n"
 1669:        "  1. Discard the worn out cleaning cartridge.\n"
 1670:        "  2. Wait for the current operation to finish.\n"
 1671:        "  3. Then use a new cleaning cartridge.",
 1672:     /* 0x17 */
 1673:    "C: The last cleaning cartridge used in the tape drive was an invalid\n"
 1674:        "  type:\n"
 1675:        "  1. Do not use this cleaning cartridge in this drive.\n"
 1676:        "  2. Wait for the current operation to finish.\n"
 1677:        "  3. Then use a new cleaning cartridge.",
 1678:     /* 0x18 */
 1679:    "W: The tape drive has requested a retention operation",
 1680:     /* 0x19 */
 1681:    "W: A redundant interface port on the tape drive has failed",
 1682:     /* 0x1a */
 1683:    "W: A tape drive cooling fan has failed",
 1684:     /* 0x1b */
 1685:    "W: A redundant power supply has failed inside the tape drive enclosure.\n"
 1686:        "  Check the enclosure users manual for instructions on replacing the\n"
 1687:        "  failed power supply.",
 1688:     /* 0x1c */
 1689:    "W: The tape drive power consumption is outside the specified range.",
 1690:     /* 0x1d */
 1691:    "W: Preventive maintenance of the tape drive is required. Check the tape\n"
 1692:        "  drive users manual for device specific preventive maintenance\n"
 1693:        "  tasks or call the tape drive supplier helpline.",
 1694:     /* 0x1e */
 1695:    "C: The tape drive has a hardware fault:\n"
 1696:        "  1. Eject the tape or magazine.\n"
 1697:        "  2. Reset the drive.\n"
 1698:        "  3. Restart the operation.",
 1699:     /* 0x1f */
 1700:    "C: The tape drive has a hardware fault:\n"
 1701:        "  1. Turn the tape drive off and then on again.\n"
 1702:        "  2. Restart the operation.\n"
 1703:     "  3. If the problem persists, call the tape drive supplier helpline.",
 1704:     /* 0x20 */
 1705:    "W: The tape drive has a problem with the application client interface:\n"
 1706:        "  1. Check the cables and cable connections.\n"
 1707:        "  2. Restart the operation.",
 1708:     /* 0x21 */
 1709:    "C: The operation has failed:\n"
 1710:        "  1. Eject the tape or magazine.\n"
 1711:        "  2. Insert the tape or magazine again.\n"
 1712:        "  3. Restart the operation.",
 1713:     /* 0x22 */
 1714:    "W: The firmware download has failed because you have tried to use the\n"
 1715:        "  incorrect firmware for this tape drive. Obtain the correct\n"
 1716:        "  firmware and try again.",
 1717:     /* 0x23 */
 1718:    "W: Environmental conditions inside the tape drive are outside the\n"
 1719:        "  specified humidity range.",
 1720:     /* 0x24 */
 1721:    "W: Environmental conditions inside the tape drive are outside the\n"
 1722:        "  specified temperature range.",
 1723:     /* 0x25 */
 1724:    "W: The voltage supply to the tape drive is outside the specified range.",
 1725:     /* 0x26 */
 1726:    "C: A hardware failure of the tape drive is predicted. Call the tape\n"
 1727:        "  drive supplier helpline.",
 1728:     /* 0x27 */
 1729:    "W: The tape drive may have a hardware fault. Run extended diagnostics to\n"
 1730:        "  verify and diagnose the problem. Check the tape drive users manual for\n"
 1731:        "  device specific instructions on running extended diagnostic tests.",
 1732:     /* 0x28 */
 1733:    "C: The changer mechanism is having difficulty communicating with the tape\n"
 1734:        "  drive:\n"
 1735:        "  1. Turn the autoloader off then on.\n"
 1736:        "  2. Restart the operation.\n"
 1737:        "  3. If problem persists, call the tape drive supplier helpline.",
 1738:     /* 0x29 */
 1739:    "C: A tape has been left in the autoloader by a previous hardware fault:\n"
 1740:        "  1. Insert an empty magazine to clear the fault.\n"
 1741:        "  2. If the fault does not clear, turn the autoloader off and then\n"
 1742:        "  on again.\n"
 1743:        "  3. If the problem persists, call the tape drive supplier helpline.",
 1744:     /* 0x2a */
 1745:    "W: There is a problem with the autoloader mechanism.",
 1746:     /* 0x2b */
 1747:    "C: The operation has failed because the autoloader door is open:\n"
 1748:        "  1. Clear any obstructions from the autoloader door.\n"
 1749:        "  2. Eject the magazine and then insert it again.\n"
 1750:        "  3. If the fault does not clear, turn the autoloader off and then\n"
 1751:        "  on again.\n"
 1752:        "  4. If the problem persists, call the tape drive supplier helpline.",
 1753:     /* 0x2c */
 1754:    "C: The autoloader has a hardware fault:\n"
 1755:        "  1. Turn the autoloader off and then on again.\n"
 1756:        "  2. Restart the operation.\n"
 1757:        "  3. If the problem persists, call the tape drive supplier helpline.\n"
 1758:        "  Check the autoloader users manual for device specific instructions\n"
 1759:        "  on turning the device power on and off.",
 1760:     /* 0x2d */
 1761:    "C: The autoloader cannot operate without the magazine,\n"
 1762:        "  1. Insert the magazine into the autoloader.\n"
 1763:        "  2. Restart the operation.",
 1764:     /* 0x2e */
 1765:    "W: A hardware failure of the changer mechanism is predicted. Call the\n"
 1766:        "  tape drive supplier helpline.",
 1767:     /* 0x2f */
 1768:    "I: Reserved.",
 1769:     /* 0x30 */
 1770:    "I: Reserved.",
 1771:     /* 0x31 */
 1772:    "I: Reserved.",
 1773:     /* 0x32 */
 1774:    "W: Media statistics have been lost at some time in the past",
 1775:     /* 0x33 */
 1776:    "W: The tape directory on the tape cartridge just unloaded has been\n"
 1777:        "  corrupted. File search performance will be degraded. The tape\n"
 1778:        "  directory can be rebuilt by reading all the data.",
 1779:     /* 0x34 */
 1780:    "C: The tape just unloaded could not write its system area successfully:\n"
 1781:        "  1. Copy data to another tape cartridge.\n"
 1782:        "  2. Discard the old cartridge.",
 1783:     /* 0x35 */
 1784:    "C: The tape system are could not be read successfully at load time:\n"
 1785:     "  1. Copy data to another tape cartridge.\n",
 1786:     /* 0x36 */
 1787:    "C: The start or data could not be found on the tape:\n"
 1788:        "  1. Check you are using the correct format tape.\n"
 1789:        "  2. Discard the tape or return the tape to your supplier",
 1790:     /* 0x37 */
 1791:     "C: The operation has failed because the media cannot be loaded\n"
 1792:         "  and threaded.\n"
 1793:         "  1. Remove the cartridge, inspect it as specified in the product\n"
 1794:         "  manual, and retry the operation.\n"
 1795:         "  2. If the problem persists, call the tape drive supplier help line.",
 1796:     /* 0x38 */
 1797:     "C: The operation has failed because the medium cannot be unloaded:\n"
 1798:         "  1. Do not attempt to extract the tape cartridge.\n"
 1799:         "  2. Call the tape driver supplier help line.",
 1800:     /* 0x39 */
 1801:     "C: The tape drive has a problem with the automation interface:\n"
 1802:         "  1. Check the power to the automation system.\n"
 1803:         "  2. Check the cables and cable connections.\n"
 1804:         "  3. Call the supplier help line if problem persists.",
 1805:     /* 0x3a */
 1806:     "W: The tape drive has reset itself due to a detected firmware\n"
 1807:         "  fault. If problem persists, call the supplier help line.",
 1808:     };
 1809: 
 1810: const char *
 1811: scsiTapeAlertsTapeDevice(unsigned short code)
 1812: {
 1813:     const int num = sizeof(TapeAlertsMessageTable) /
 1814:                         sizeof(TapeAlertsMessageTable[0]);
 1815: 
 1816:     return (code < num) ?  TapeAlertsMessageTable[code] : "Unknown Alert";
 1817: }
 1818: 
 1819: // The first character (W, C, I) tells the severity
 1820: static const char * ChangerTapeAlertsMessageTable[]= {
 1821:     " ",
 1822:     /* 0x01 */
 1823:     "C: The library mechanism is having difficulty communicating with the\n"
 1824:         "  drive:\n"
 1825:         "  1. Turn the library off then on.\n"
 1826:         "  2. Restart the operation.\n"
 1827:         "  3. If the problem persists, call the library supplier help line.",
 1828:     /* 0x02 */
 1829:     "W: There is a problem with the library mechanism. If problem persists,\n"
 1830:         "  call the library supplier help line.",
 1831:     /* 0x03 */
 1832:     "C: The library has a hardware fault:\n"
 1833:         "  1. Reset the library.\n"
 1834:         "  2. Restart the operation.\n"
 1835:         "  Check the library users manual for device specific instructions on resetting\n"
 1836:         "  the device.",
 1837:     /* 0x04 */
 1838:     "C: The library has a hardware fault:\n"
 1839:         "  1. Turn the library off then on again.\n"
 1840:         "  2. Restart the operation.\n"
 1841:         "  3. If the problem persists, call the library supplier help line.\n"
 1842:         "  Check the library users manual for device specific instructions on turning the\n"
 1843:         "  device power on and off.",
 1844:     /* 0x05 */
 1845:     "W: The library mechanism may have a hardware fault.\n"
 1846:         "  Run extended diagnostics to verify and diagnose the problem. Check the library\n"
 1847:         "  users manual for device specific instructions on running extended diagnostic\n"
 1848:         "  tests.",
 1849:     /* 0x06 */
 1850:     "C: The library has a problem with the host interface:\n"
 1851:         "  1. Check the cables and connections.\n"
 1852:         "  2. Restart the operation.",
 1853:     /* 0x07 */
 1854:     "W: A hardware failure of the library is predicted. Call the library\n"
 1855:         "  supplier help line.",
 1856:     /* 0x08 */
 1857:     "W: Preventive maintenance of the library is required.\n"
 1858:         "  Check the library users manual for device specific preventative maintenance\n"
 1859:         "  tasks, or call your library supplier help line.",
 1860:     /* 0x09 */
 1861:     "C: General environmental conditions inside the library are outside the\n"
 1862:         "  specified humidity range.",
 1863:     /* 0x0a */
 1864:     "C: General environmental conditions inside the library are outside the\n"
 1865:         "  specified temperature range.",
 1866:     /* 0x0b */
 1867:     "C: The voltage supply to the library is outside the specified range.\n"
 1868:         "  There is a potential problem with the power supply or failure of\n"
 1869:         "  a redundant power supply.",
 1870:     /* 0x0c */
 1871:     "C: A cartridge has been left inside the library by a previous hardware\n"
 1872:         "  fault:\n"
 1873:         "  1. Insert an empty magazine to clear the fault.\n"
 1874:         "  2. If the fault does not clear, turn the library off and then on again.\n"
 1875:         "  3. If the problem persists, call the library supplier help line.",
 1876:     /* 0x0d */
 1877:     "W: There is a potential problem with the drive ejecting cartridges or\n"
 1878:         "  with the library mechanism picking a cartridge from a slot.\n"
 1879:         "  1. No action needs to be taken at this time.\n"
 1880:         "  2. If the problem persists, call the library supplier help line.",
 1881:     /* 0x0e */
 1882:     "W: There is a potential problem with the library mechanism placing a\n"
 1883:         "  cartridge into a slot.\n"
 1884:         "  1. No action needs to be taken at this time.\n"
 1885:         "  2. If the problem persists, call the library supplier help line.",
 1886:     /* 0x0f */
 1887:     "W: There is a potential problem with the drive or the library mechanism\n"
 1888:         "  loading cartridges, or an incompatible cartridge.",
 1889:     /* 0x10 */
 1890:     "C: The library has failed because the door is open:\n"
 1891:         "  1. Clear any obstructions from the library door.\n"
 1892:         "  2. Close the library door.\n"
 1893:         "  3. If the problem persists, call the library supplier help line.",
 1894:     /* 0x11 */
 1895:     "C: There is a mechanical problem with the library media import/export\n"
 1896:         "  mailslot.",
 1897:     /* 0x12 */
 1898:     "C: The library cannot operate without the magazine.\n"
 1899:         "  1. Insert the magazine into the library.\n"
 1900:         "  2. Restart the operation.",
 1901:     /* 0x13 */
 1902:     "W: Library security has been compromised.",
 1903:     /* 0x14 */
 1904:     "I: The library security mode has been changed.\n"
 1905:         "  The library has either been put into secure mode, or the library has exited\n"
 1906:         "  the secure mode.\n"
 1907:         "  This is for information purposes only. No action is required.",
 1908:     /* 0x15 */
 1909:     "I: The library has been manually turned offline and is unavailable for use.",
 1910:     /* 0x16 */
 1911:     "I: A drive inside the library has been taken offline.\n"
 1912:         "  This is for information purposes only. No action is required.",
 1913:     /* 0x17 */
 1914:     "W: There is a potential problem with the bar code label or the scanner\n"
 1915:         "  hardware in the library mechanism.\n"
 1916:         "  1. No action needs to be taken at this time.\n"
 1917:         "  2. If the problem persists, call the library supplier help line.",
 1918:     /* 0x18 */
 1919:     "C: The library has detected an inconsistency in its inventory.\n"
 1920:         "  1. Redo the library inventory to correct inconsistency.\n"
 1921:         "  2. Restart the operation.\n"
 1922:         "  Check the applications users manual or the hardware users manual for\n"
 1923:         "  specific instructions on redoing the library inventory.",
 1924:     /* 0x19 */
 1925:     "W: A library operation has been attempted that is invalid at this time.",
 1926:     /* 0x1a */
 1927:     "W: A redundant interface port on the library has failed.",
 1928:     /* 0x1b */
 1929:     "W: A library cooling fan has failed.",
 1930:     /* 0x1c */
 1931:     "W: A redundant power supply has failed inside the library. Check the\n"
 1932:         "  library users manual for instructions on replacing the failed power supply.",
 1933:     /* 0x1d */
 1934:     "W: The library power consumption is outside the specified range.",
 1935:     /* 0x1e */
 1936:     "C: A failure has occurred in the cartridge pass-through mechanism between\n"
 1937:         "  two library modules.",
 1938:     /* 0x1f */
 1939:     "C: A cartridge has been left in the pass-through mechanism from a\n"
 1940:         "  previous hardware fault. Check the library users guide for instructions on\n"
 1941:         "  clearing this fault.",
 1942:     /* 0x20 */
 1943:     "I: The library was unable to read the bar code on a cartridge.",
 1944: };
 1945: 
 1946: const char *
 1947: scsiTapeAlertsChangerDevice(unsigned short code)
 1948: {
 1949:     const int num = sizeof(ChangerTapeAlertsMessageTable) /
 1950:                         sizeof(ChangerTapeAlertsMessageTable[0]);
 1951: 
 1952:     return (code < num) ?  ChangerTapeAlertsMessageTable[code] : "Unknown Alert";
 1953: }
 1954: 
 1955: 
 1956: /* this is a subset of the SCSI additional sense code strings indexed
 1957:  * by "ascq" for the case when asc==SCSI_ASC_IMPENDING_FAILURE (0x5d)
 1958:  */
 1959: static const char * strs_for_asc_5d[] = {
 1960:    /* 0x00 */   "FAILURE PREDICTION THRESHOLD EXCEEDED",
 1961:         "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED",
 1962:         "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED",
 1963:         "SPARE AREA EXHAUSTION PREDICTION THRESHOLD EXCEEDED",
 1964:         "",
 1965:         "",
 1966:         "",
 1967:         "",
 1968:         "",
 1969:         "",
 1970:         "",
 1971:         "",
 1972:         "",
 1973:         "",
 1974:         "",
 1975:         "",
 1976:    /* 0x10 */   "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
 1977:         "HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
 1978:         "HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
 1979:         "HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
 1980:         "HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
 1981:         "HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
 1982:         "HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
 1983:         "HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
 1984:         "HARDWARE IMPENDING FAILURE CONTROLLER DETECTED",
 1985:         "HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
 1986:         "HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
 1987:         "HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
 1988:         "HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
 1989:         "",
 1990:         "",
 1991:         "",
 1992:    /* 0x20 */   "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
 1993:         "CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
 1994:         "CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
 1995:         "CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
 1996:         "CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
 1997:         "CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH",
 1998:         "CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH",
 1999:         "CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS",
 2000:         "CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED",
 2001:         "CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE",
 2002:         "CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE",
 2003:         "CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT",
 2004:         "CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
 2005:         "",
 2006:         "",
 2007:         "",
 2008:    /* 0x30 */   "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
 2009:         "DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
 2010:         "DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
 2011:         "DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
 2012:         "DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
 2013:         "DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH",
 2014:         "DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH",
 2015:         "DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS",
 2016:         "DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED",
 2017:         "DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE",
 2018:         "DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE",
 2019:         "DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT",
 2020:         "DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
 2021:         "",
 2022:         "",
 2023:         "",
 2024:    /* 0x40 */   "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
 2025:         "SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
 2026:         "SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
 2027:         "SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
 2028:         "SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
 2029:         "SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH",
 2030:         "SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH",
 2031:         "SERVO IMPENDING FAILURE CHANNEL PARAMETRICS",
 2032:         "SERVO IMPENDING FAILURE CONTROLLER DETECTED",
 2033:         "SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE",
 2034:         "SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE",
 2035:         "SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT",
 2036:         "SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
 2037:         "",
 2038:         "",
 2039:         "",
 2040:    /* 0x50 */   "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
 2041:         "SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
 2042:         "SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
 2043:         "SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
 2044:         "SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
 2045:         "SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
 2046:         "SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
 2047:         "SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS",
 2048:         "SPINDLE IMPENDING FAILURE CONTROLLER DETECTED",
 2049:         "SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
 2050:         "SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE",
 2051:         "SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT",
 2052:         "SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
 2053:         "",
 2054:         "",
 2055:         "",
 2056:    /* 0x60 */   "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
 2057:         "FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
 2058:         "FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
 2059:         "FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
 2060:         "FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
 2061:         "FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
 2062:         "FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
 2063:         "FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
 2064:         "FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED",
 2065:         "FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
 2066:         "FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
 2067:         "FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
 2068:    /* 0x6c */   "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT"};
 2069: 
 2070: 
 2071: /* this is a subset of the SCSI additional sense code strings indexed
 2072:  *  * by "ascq" for the case when asc==SCSI_ASC_WARNING (0xb)
 2073:  *   */
 2074: static const char * strs_for_asc_b[] = {
 2075:        /* 0x00 */   "WARNING",
 2076:                "WARNING - SPECIFIED TEMPERATURE EXCEEDED",
 2077:                "WARNING - ENCLOSURE DEGRADED"};
 2078: 
 2079: static char spare_buff[128];
 2080: 
 2081: const char *
 2082: scsiGetIEString(UINT8 asc, UINT8 ascq)
 2083: {
 2084:     const char * rp;
 2085: 
 2086:     if (SCSI_ASC_IMPENDING_FAILURE == asc) {
 2087:         if (ascq == 0xff)
 2088:             return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)";
 2089:         else if (ascq <
 2090:                  (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) {
 2091:             rp = strs_for_asc_5d[ascq];
 2092:             if (strlen(rp) > 0)
 2093:                 return rp;
 2094:         }
 2095:         snprintf(spare_buff, sizeof(spare_buff),
 2096:                  "FAILURE PREDICTION THRESHOLD EXCEEDED: ascq=0x%x", ascq);
 2097:         return spare_buff;
 2098:     } else if (SCSI_ASC_WARNING == asc) {
 2099:         if (ascq < (sizeof(strs_for_asc_b) / sizeof(strs_for_asc_b[0]))) {
 2100:             rp = strs_for_asc_b[ascq];
 2101:             if (strlen(rp) > 0)
 2102:                 return rp;
 2103:         }
 2104:         snprintf(spare_buff, sizeof(spare_buff), "WARNING: ascq=0x%x", ascq);
 2105:         return spare_buff;
 2106:     }
 2107:     return NULL;        /* not a IE additional sense code */
 2108: }
 2109: 
 2110: 
 2111: /* This is not documented in t10.org, page 0x80 is vendor specific */
 2112: /* Some IBM disks do an offline read-scan when they get this command. */
 2113: int
 2114: scsiSmartIBMOfflineTest(scsi_device * device)
 2115: {
 2116:     UINT8 tBuf[256];
 2117:     int res;
 2118: 
 2119:     memset(tBuf, 0, sizeof(tBuf));
 2120:     /* Build SMART Off-line Immediate Diag Header */
 2121:     tBuf[0] = 0x80; /* Page Code */
 2122:     tBuf[1] = 0x00; /* Reserved */
 2123:     tBuf[2] = 0x00; /* Page Length MSB */
 2124:     tBuf[3] = 0x04; /* Page Length LSB */
 2125:     tBuf[4] = 0x03; /* SMART Revision */
 2126:     tBuf[5] = 0x00; /* Reserved */
 2127:     tBuf[6] = 0x00; /* Off-line Immediate Time MSB */
 2128:     tBuf[7] = 0x00; /* Off-line Immediate Time LSB */
 2129:     res = scsiSendDiagnostic(device, SCSI_DIAG_NO_SELF_TEST, tBuf, 8);
 2130:     if (res)
 2131:         pout("IBM offline test failed [%s]\n", scsiErrString(res));
 2132:     return res;
 2133: }
 2134: 
 2135: int
 2136: scsiSmartDefaultSelfTest(scsi_device * device)
 2137: {
 2138:     int res;
 2139: 
 2140:     res = scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0);
 2141:     if (res)
 2142:         pout("Default self test failed [%s]\n", scsiErrString(res));
 2143:     return res;
 2144: }
 2145: 
 2146: int
 2147: scsiSmartShortSelfTest(scsi_device * device)
 2148: {
 2149:     int res;
 2150: 
 2151:     res = scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0);
 2152:     if (res)
 2153:         pout("Short offline self test failed [%s]\n", scsiErrString(res));
 2154:     return res;
 2155: }
 2156: 
 2157: int
 2158: scsiSmartExtendSelfTest(scsi_device * device)
 2159: {
 2160:     int res;
 2161: 
 2162:     res = scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, NULL, 0);
 2163:     if (res)
 2164:         pout("Long (extended) offline self test failed [%s]\n",
 2165:              scsiErrString(res));
 2166:     return res;
 2167: }
 2168: 
 2169: int
 2170: scsiSmartShortCapSelfTest(scsi_device * device)
 2171: {
 2172:     int res;
 2173: 
 2174:     res = scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0);
 2175:     if (res)
 2176:         pout("Short foreground self test failed [%s]\n", scsiErrString(res));
 2177:     return res;
 2178: }
 2179: 
 2180: int
 2181: scsiSmartExtendCapSelfTest(scsi_device * device)
 2182: {
 2183:     int res;
 2184: 
 2185:     res = scsiSendDiagnostic(device, SCSI_DIAG_FG_EXTENDED_SELF_TEST, NULL, 0);
 2186:     if (res)
 2187:         pout("Long (extended) foreground self test failed [%s]\n",
 2188:              scsiErrString(res));
 2189:     return res;
 2190: }
 2191: 
 2192: int
 2193: scsiSmartSelfTestAbort(scsi_device * device)
 2194: {
 2195:     int res;
 2196: 
 2197:     res = scsiSendDiagnostic(device, SCSI_DIAG_ABORT_SELF_TEST, NULL, 0);
 2198:     if (res)
 2199:         pout("Abort self test failed [%s]\n", scsiErrString(res));
 2200:     return res;
 2201: }
 2202: 
 2203: /* Returns 0 and the expected duration of an extended self test (in seconds)
 2204:    if successful; any other return value indicates a failure. */
 2205: int
 2206: scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
 2207:                               int modese_len)
 2208: {
 2209:     int err, offset, res;
 2210:     UINT8 buff[64];
 2211: 
 2212:     memset(buff, 0, sizeof(buff));
 2213:     if (modese_len <= 6) {
 2214:         if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
 2215:                                  MPAGE_CONTROL_CURRENT,
 2216:                                  buff, sizeof(buff)))) {
 2217:             if (SIMPLE_ERR_BAD_OPCODE == err)
 2218:                 modese_len = 10;
 2219:             else
 2220:                 return err;
 2221:         } else if (0 == modese_len)
 2222:             modese_len = 6;
 2223:     }
 2224:     if (10 == modese_len) {
 2225:         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
 2226:                               MPAGE_CONTROL_CURRENT,
 2227:                               buff, sizeof(buff));
 2228:         if (err)
 2229:             return err;
 2230:     }
 2231:     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
 2232:     if (offset < 0)
 2233:         return -EINVAL;
 2234:     if (buff[offset + 1] >= 0xa) {
 2235:         res = (buff[offset + 10] << 8) | buff[offset + 11];
 2236:         *durationSec = res;
 2237:         return 0;
 2238:     }
 2239:     else
 2240:         return -EINVAL;
 2241: }
 2242: 
 2243: void
 2244: scsiDecodeErrCounterPage(unsigned char * resp, struct scsiErrorCounter *ecp)
 2245: {
 2246:     int k, j, num, pl, pc;
 2247:     unsigned char * ucp;
 2248:     unsigned char * xp;
 2249:     uint64_t * ullp;
 2250: 
 2251:     memset(ecp, 0, sizeof(*ecp));
 2252:     num = (resp[2] << 8) | resp[3];
 2253:     ucp = &resp[0] + 4;
 2254:     while (num > 3) {
 2255:         pc = (ucp[0] << 8) | ucp[1];
 2256:         pl = ucp[3] + 4;
 2257:         switch (pc) {
 2258:             case 0:
 2259:             case 1:
 2260:             case 2:
 2261:             case 3:
 2262:             case 4:
 2263:             case 5:
 2264:             case 6:
 2265:                 ecp->gotPC[pc] = 1;
 2266:                 ullp = &ecp->counter[pc];
 2267:                 break;
 2268:         default:
 2269:                 ecp->gotExtraPC = 1;
 2270:                 ullp = &ecp->counter[7];
 2271:                 break;
 2272:         }
 2273:         k = pl - 4;
 2274:         xp = ucp + 4;
 2275:         if (k > (int)sizeof(*ullp)) {
 2276:             xp += (k - sizeof(*ullp));
 2277:             k = sizeof(*ullp);
 2278:         }
 2279:         *ullp = 0;
 2280:         for (j = 0; j < k; ++j) {
 2281:             if (j > 0)
 2282:                 *ullp <<= 8;
 2283:             *ullp |= xp[j];
 2284:         }
 2285:         num -= pl;
 2286:         ucp += pl;
 2287:     }
 2288: }
 2289: 
 2290: void
 2291: scsiDecodeNonMediumErrPage(unsigned char *resp,
 2292:                            struct scsiNonMediumError *nmep)
 2293: {
 2294:     int k, j, num, pl, pc, szof;
 2295:     unsigned char * ucp;
 2296:     unsigned char * xp;
 2297: 
 2298:     memset(nmep, 0, sizeof(*nmep));
 2299:     num = (resp[2] << 8) | resp[3];
 2300:     ucp = &resp[0] + 4;
 2301:     szof = sizeof(nmep->counterPC0);
 2302:     while (num > 3) {
 2303:         pc = (ucp[0] << 8) | ucp[1];
 2304:         pl = ucp[3] + 4;
 2305:         switch (pc) {
 2306:             case 0:
 2307:                 nmep->gotPC0 = 1;
 2308:                 k = pl - 4;
 2309:                 xp = ucp + 4;
 2310:                 if (k > szof) {
 2311:                     xp += (k - szof);
 2312:                     k = szof;
 2313:                 }
 2314:                 nmep->counterPC0 = 0;
 2315:                 for (j = 0; j < k; ++j) {
 2316:                     if (j > 0)
 2317:                         nmep->counterPC0 <<= 8;
 2318:                     nmep->counterPC0 |= xp[j];
 2319:                 }
 2320:                 break;
 2321:             case 0x8009:
 2322:                 nmep->gotTFE_H = 1;
 2323:                 k = pl - 4;
 2324:                 xp = ucp + 4;
 2325:                 if (k > szof) {
 2326:                     xp += (k - szof);
 2327:                     k = szof;
 2328:                 }
 2329:                 nmep->counterTFE_H = 0;
 2330:                 for (j = 0; j < k; ++j) {
 2331:                     if (j > 0)
 2332:                         nmep->counterTFE_H <<= 8;
 2333:                     nmep->counterTFE_H |= xp[j];
 2334:                 }
 2335:                 break;
 2336:             case 0x8015:
 2337:                 nmep->gotPE_H = 1;
 2338:                 k = pl - 4;
 2339:                 xp = ucp + 4;
 2340:                 if (k > szof) {
 2341:                     xp += (k - szof);
 2342:                     k = szof;
 2343:                 }
 2344:                 nmep->counterPE_H = 0;
 2345:                 for (j = 0; j < k; ++j) {
 2346:                     if (j > 0)
 2347:                         nmep->counterPE_H <<= 8;
 2348:                     nmep->counterPE_H |= xp[j];
 2349:                 }
 2350:                 break;
 2351:         default:
 2352:                 nmep->gotExtraPC = 1;
 2353:                 break;
 2354:         }
 2355:         num -= pl;
 2356:         ucp += pl;
 2357:     }
 2358: }
 2359: 
 2360: /* Counts number of failed self-tests. Also encodes the poweron_hour
 2361:    of the most recent failed self-test. Return value is negative if
 2362:    this function has a problem (typically -1), otherwise the bottom 8
 2363:    bits are the number of failed self tests and the 16 bits above that
 2364:    are the poweron hour of the most recent failure. Note: aborted self
 2365:    tests (typically by the user) and self tests in progress are not
 2366:    considered failures. See Working Draft SCSI Primary Commands - 3
 2367:    (SPC-3) section 7.2.10 T10/1416-D (rev 22a) */
 2368: int
 2369: scsiCountFailedSelfTests(scsi_device * fd, int noisy)
 2370: {
 2371:     int num, k, n, err, res, fails, fail_hour;
 2372:     UINT8 * ucp;
 2373:     unsigned char resp[LOG_RESP_SELF_TEST_LEN];
 2374: 
 2375:     if ((err = scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp,
 2376:                             LOG_RESP_SELF_TEST_LEN, 0))) {
 2377:         if (noisy)
 2378:             pout("scsiCountSelfTests Failed [%s]\n", scsiErrString(err));
 2379:         return -1;
 2380:     }
 2381:     if ((resp[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
 2382:         if (noisy)
 2383:             pout("Self-test Log Sense Failed, page mismatch\n");
 2384:         return -1;
 2385:     }
 2386:     // compute page length
 2387:     num = (resp[2] << 8) + resp[3];
 2388:     // Log sense page length 0x190 bytes
 2389:     if (num != 0x190) {
 2390:         if (noisy)
 2391:             pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n", num);
 2392:         return -1;
 2393:     }
 2394:     fails = 0;
 2395:     fail_hour = 0;
 2396:     // loop through the twenty possible entries
 2397:     for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) {
 2398: 
 2399:         // timestamp in power-on hours (or zero if test in progress)
 2400:         n = (ucp[6] << 8) | ucp[7];
 2401: 
 2402:         // The spec says "all 20 bytes will be zero if no test" but
 2403:         // DG has found otherwise.  So this is a heuristic.
 2404:         if ((0 == n) && (0 == ucp[4]))
 2405:             break;
 2406:         res = ucp[4] & 0xf;
 2407:         if ((res > 2) && (res < 8)) {
 2408:             fails++;
 2409:             if (1 == fails)
 2410:                 fail_hour = (ucp[6] << 8) + ucp[7];
 2411:         }
 2412:     }
 2413:     return (fail_hour << 8) + fails;
 2414: }
 2415: 
 2416: /* Returns 0 if able to read self test log page; then outputs 1 into
 2417:    *inProgress if self test still in progress, else outputs 0. */
 2418: int
 2419: scsiSelfTestInProgress(scsi_device * fd, int * inProgress)
 2420: {
 2421:     int num;
 2422:     UINT8 * ucp;
 2423:     unsigned char resp[LOG_RESP_SELF_TEST_LEN];
 2424: 
 2425:     if (scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp,
 2426:                      LOG_RESP_SELF_TEST_LEN, 0))
 2427:         return -1;
 2428:     if (resp[0] != SELFTEST_RESULTS_LPAGE)
 2429:         return -1;
 2430:     // compute page length
 2431:     num = (resp[2] << 8) + resp[3];
 2432:     // Log sense page length 0x190 bytes
 2433:     if (num != 0x190) {
 2434:         return -1;
 2435:     }
 2436:     ucp = resp + 4;
 2437:     if (inProgress)
 2438:         *inProgress = (0xf == (ucp[4] & 0xf)) ? 1 : 0;
 2439:     return 0;
 2440: }
 2441: 
 2442: /* Returns a negative value if failed to fetch Contol mode page or it was
 2443:    malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD
 2444:    bit is set. Examines default mode page when current==0 else examines
 2445:    current mode page. */
 2446: int
 2447: scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current)
 2448: {
 2449:     int err, offset;
 2450:     UINT8 buff[64];
 2451:     int pc = current ? MPAGE_CONTROL_CURRENT : MPAGE_CONTROL_DEFAULT;
 2452: 
 2453:     memset(buff, 0, sizeof(buff));
 2454:     if (modese_len <= 6) {
 2455:         if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0, pc,
 2456:                                  buff, sizeof(buff)))) {
 2457:             if (SIMPLE_ERR_BAD_OPCODE == err)
 2458:                 modese_len = 10;
 2459:             else
 2460:                 return -EINVAL;
 2461:         } else if (0 == modese_len)
 2462:             modese_len = 6;
 2463:     }
 2464:     if (10 == modese_len) {
 2465:         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0, pc,
 2466:                               buff, sizeof(buff));
 2467:         if (err)
 2468:             return -EINVAL;
 2469:     }
 2470:     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
 2471:     if ((offset >= 0) && (buff[offset + 1] >= 0xa))
 2472:         return (buff[offset + 2] & 2) ? 1 : 0;
 2473:     return -EINVAL;
 2474: }
 2475: 
 2476: /* Returns a negative value on error, 0 if unknown and 1 if SSD,
 2477:  * otherwise the positive returned value is the speed in rpm. First checks
 2478:  * the Block Device Characteristics VPD page and if that fails it tries the
 2479:  * RIGID_DISK_DRIVE_GEOMETRY_PAGE mode page. */
 2480: 
 2481: int
 2482: scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp)
 2483: {
 2484:     int err, offset, speed;
 2485:     UINT8 buff[64];
 2486:     int pc = MPAGE_CONTROL_DEFAULT;
 2487: 
 2488:     memset(buff, 0, sizeof(buff));
 2489:     if ((0 == scsiInquiryVpd(device, SCSI_VPD_BLOCK_DEVICE_CHARACTERISTICS,
 2490:                              buff, sizeof(buff))) &&
 2491:         (((buff[2] << 8) + buff[3]) > 2)) {
 2492:         speed = (buff[4] << 8) + buff[5];
 2493:         if (form_factorp)
 2494:             *form_factorp = buff[7] & 0xf;
 2495:         return speed;
 2496:     }
 2497:     if (form_factorp)
 2498:         *form_factorp = 0;
 2499:     if (modese_len <= 6) {
 2500:         if ((err = scsiModeSense(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc,
 2501:                                  buff, sizeof(buff)))) {
 2502:             if (SIMPLE_ERR_BAD_OPCODE == err)
 2503:                 modese_len = 10;
 2504:             else
 2505:                 return -EINVAL;
 2506:         } else if (0 == modese_len)
 2507:             modese_len = 6;
 2508:     }
 2509:     if (10 == modese_len) {
 2510:         err = scsiModeSense10(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc,
 2511:                               buff, sizeof(buff));
 2512:         if (err)
 2513:             return -EINVAL;
 2514:     }
 2515:     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
 2516:     return (buff[offset + 20] << 8) | buff[offset + 21];
 2517: }
 2518: 
 2519: /* Returns a non-zero value in case of error, wcep/rcdp == -1 - get value,
 2520:    0 - clear bit, 1 - set bit  */
 2521: 
 2522: int
 2523: scsiGetSetCache(scsi_device * device,  int modese_len, short int * wcep,
 2524:                 short int * rcdp)
 2525: {
 2526:     int err, offset, resp_len, sp;
 2527:     UINT8 buff[64], ch_buff[64];
 2528:     short set_wce = *wcep;
 2529:     short set_rcd = *rcdp;
 2530: 
 2531:     memset(buff, 0, sizeof(buff));
 2532:     if (modese_len <= 6) {
 2533:         if ((err = scsiModeSense(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT,
 2534:                                  buff, sizeof(buff)))) {
 2535:             if (SIMPLE_ERR_BAD_OPCODE == err)
 2536:                 modese_len = 10;
 2537:             else {
 2538:                 device->set_err(EINVAL, "SCSI MODE SENSE failed");
 2539:                 return -EINVAL;
 2540:             }
 2541:         } else if (0 == modese_len)
 2542:             modese_len = 6;
 2543:     }
 2544: 
 2545:     if (10 == modese_len) {
 2546:         err = scsiModeSense10(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT,
 2547:                               buff, sizeof(buff));
 2548:         if (err) {
 2549:             device->set_err(EINVAL, "SCSI MODE SENSE failed");
 2550:             return -EINVAL;
 2551:         }
 2552:     }
 2553:     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
 2554:     if ((offset < 0) || (buff[offset + 1] < 0xa)) {
 2555:         device->set_err(EINVAL, "Bad response");
 2556:         return SIMPLE_ERR_BAD_RESP;
 2557:     }
 2558: 
 2559:     *wcep = ((buff[offset + 2] & 0x04) != 0);
 2560:     *rcdp = ((buff[offset + 2] & 0x01) != 0);
 2561: 
 2562:     if((*wcep == set_wce || set_wce == -1)
 2563:           && ((*rcdp == set_rcd) || set_rcd == -1))
 2564:       return 0; // no changes needed or nothing to set
 2565: 
 2566:     if (modese_len == 6)
 2567:         err = scsiModeSense(device, CACHING_PAGE, 0,
 2568:                             MPAGE_CONTROL_CHANGEABLE,
 2569:                             ch_buff, sizeof(ch_buff));
 2570:     else
 2571:         err = scsiModeSense10(device, CACHING_PAGE, 0,
 2572:                               MPAGE_CONTROL_CHANGEABLE,
 2573:                               ch_buff, sizeof(ch_buff));
 2574:     if (err) {
 2575:         device->set_err(EINVAL, "WCE/RCD bits not changable");
 2576:         return err;
 2577:     }
 2578: 
 2579:     // set WCE bit
 2580:     if(set_wce >= 0 && *wcep != set_wce) {
 2581:        if (0 == (ch_buff[offset + 2] & 0x04)) {
 2582:          device->set_err(EINVAL, "WCE bit not changable");
 2583:          return 1;
 2584:        }
 2585:        if(set_wce)
 2586:           buff[offset + 2] |= 0x04; // set bit
 2587:        else
 2588:           buff[offset + 2] &= 0xfb; // clear bit
 2589:     }
 2590:     // set RCD bit
 2591:     if(set_rcd >= 0 && *rcdp != set_rcd) {
 2592:        if (0 == (ch_buff[offset + 2] & 0x01)) {
 2593:          device->set_err(EINVAL, "RCD bit not changable");
 2594:          return 1;
 2595:        }
 2596:        if(set_rcd)
 2597:           buff[offset + 2] |= 0x01; // set bit
 2598:        else
 2599:           buff[offset + 2] &= 0xfe; // clear bit
 2600:     }
 2601: 
 2602:     if (10 == modese_len) {
 2603:         resp_len = (buff[0] << 8) + buff[1] + 2;
 2604:         buff[3] &= 0xef;    /* for disks mask out DPOFUA bit */
 2605:     } else {
 2606:         resp_len = buff[0] + 1;
 2607:         buff[2] &= 0xef;    /* for disks mask out DPOFUA bit */
 2608:     }
 2609:     sp = 0; /* Do not change saved values */
 2610:     if (10 == modese_len)
 2611:         err = scsiModeSelect10(device, sp, buff, resp_len);
 2612:     else if (6 == modese_len)
 2613:         err = scsiModeSelect(device, sp, buff, resp_len);
 2614:     if(err)
 2615:       device->set_err(EINVAL, "MODE SELECT command failed");
 2616:     return err;
 2617: }
 2618: 
 2619: 
 2620: /* Attempts to set or clear GLTSD bit in Control mode page. If enabled is
 2621:    0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if
 2622:    successful, negative if low level error, > 0 if higher level error (e.g.
 2623:    SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */
 2624: int
 2625: scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len)
 2626: {
 2627:     int err, offset, resp_len, sp;
 2628:     UINT8 buff[64];
 2629:     UINT8 ch_buff[64];
 2630: 
 2631:     memset(buff, 0, sizeof(buff));
 2632:     if (modese_len <= 6) {
 2633:         if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
 2634:                                  MPAGE_CONTROL_CURRENT,
 2635:                                  buff, sizeof(buff)))) {
 2636:             if (SIMPLE_ERR_BAD_OPCODE == err)
 2637:                 modese_len = 10;
 2638:             else
 2639:                 return err;
 2640:         } else if (0 == modese_len)
 2641:             modese_len = 6;
 2642:     }
 2643:     if (10 == modese_len) {
 2644:         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
 2645:                               MPAGE_CONTROL_CURRENT,
 2646:                               buff, sizeof(buff));
 2647:         if (err)
 2648:             return err;
 2649:     }
 2650:     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
 2651:     if ((offset < 0) || (buff[offset + 1] < 0xa))
 2652:         return SIMPLE_ERR_BAD_RESP;
 2653: 
 2654:     if (enabled)
 2655:         enabled = 2;
 2656:     if (enabled == (buff[offset + 2] & 2))
 2657:         return 0;       /* GLTSD already in wanted state so nothing to do */
 2658: 
 2659:     if (modese_len == 6)
 2660:         err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
 2661:                             MPAGE_CONTROL_CHANGEABLE,
 2662:                             ch_buff, sizeof(ch_buff));
 2663:     else
 2664:         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
 2665:                               MPAGE_CONTROL_CHANGEABLE,
 2666:                               ch_buff, sizeof(ch_buff));
 2667:     if (err)
 2668:         return err;
 2669:     if (0 == (ch_buff[offset + 2] & 2))
 2670:         return SIMPLE_ERR_BAD_PARAM;  /* GLTSD bit not chageable */
 2671: 
 2672:     if (10 == modese_len) {
 2673:         resp_len = (buff[0] << 8) + buff[1] + 2;
 2674:         buff[3] &= 0xef;    /* for disks mask out DPOFUA bit */
 2675:     } else {
 2676:         resp_len = buff[0] + 1;
 2677:         buff[2] &= 0xef;    /* for disks mask out DPOFUA bit */
 2678:     }
 2679:     sp = (buff[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
 2680:     if (enabled)
 2681:         buff[offset + 2] |= 0x2;    /* set GLTSD bit */
 2682:     else
 2683:         buff[offset + 2] &= 0xfd;   /* clear GLTSD bit */
 2684:     if (10 == modese_len)
 2685:         err = scsiModeSelect10(device, sp, buff, resp_len);
 2686:     else if (6 == modese_len)
 2687:         err = scsiModeSelect(device, sp, buff, resp_len);
 2688:     return err;
 2689: }
 2690: 
 2691: /* Returns a negative value if failed to fetch Protocol specific port mode
 2692:    page or it was malformed. Returns transport protocol identifier when
 2693:    value >= 0 . */
 2694: int
 2695: scsiFetchTransportProtocol(scsi_device * device, int modese_len)
 2696: {
 2697:     int err, offset;
 2698:     UINT8 buff[64];
 2699: 
 2700:     memset(buff, 0, sizeof(buff));
 2701:     if (modese_len <= 6) {
 2702:         if ((err = scsiModeSense(device, PROTOCOL_SPECIFIC_PORT_PAGE, 0,
 2703:                                  MPAGE_CONTROL_CURRENT,
 2704:                                  buff, sizeof(buff)))) {
 2705:             if (SIMPLE_ERR_BAD_OPCODE == err)
 2706:                 modese_len = 10;
 2707:             else
 2708:                 return -EINVAL;
 2709:         } else if (0 == modese_len)
 2710:             modese_len = 6;
 2711:     }
 2712:     if (10 == modese_len) {
 2713:         err = scsiModeSense10(device, PROTOCOL_SPECIFIC_PORT_PAGE, 0,
 2714:                               MPAGE_CONTROL_CURRENT,
 2715:                               buff, sizeof(buff));
 2716:         if (err)
 2717:             return -EINVAL;
 2718:     }
 2719:     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
 2720:     if ((offset >= 0) && (buff[offset + 1] > 1)) {
 2721:         if ((0 == (buff[offset] & 0x40)) &&       /* SPF==0 */
 2722:             (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f)))
 2723:                 return (buff[offset + 2] & 0xf);
 2724:     }
 2725:     return -EINVAL;
 2726: }
 2727: 
 2728: const unsigned char *
 2729: sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len,
 2730:                         int desc_type)
 2731: {
 2732:     int add_sen_len, add_len, desc_len, k;
 2733:     const unsigned char * descp;
 2734: 
 2735:     if ((sense_len < 8) || (0 == (add_sen_len = sensep[7])))
 2736:         return NULL;
 2737:     if ((sensep[0] < 0x72) || (sensep[0] > 0x73))
 2738:         return NULL;
 2739:     add_sen_len = (add_sen_len < (sense_len - 8)) ?
 2740:                          add_sen_len : (sense_len - 8);
 2741:     descp = &sensep[8];
 2742:     for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
 2743:         descp += desc_len;
 2744:         add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
 2745:         desc_len = add_len + 2;
 2746:         if (descp[0] == desc_type)
 2747:             return descp;
 2748:         if (add_len < 0) /* short descriptor ?? */
 2749:             break;
 2750:     }
 2751:     return NULL;
 2752: }
 2753: 
 2754: // Convenience function for formatting strings from SCSI identify
 2755: void
 2756: scsi_format_id_string(char * out, const unsigned char * in, int n)
 2757: {
 2758:   char tmp[65];
 2759:   n = n > 64 ? 64 : n;
 2760:   strncpy(tmp, (const char *)in, n);
 2761:   tmp[n] = '\0';
 2762: 
 2763:   // Find the first non-space character (maybe none).
 2764:   int first = -1;
 2765:   int i;
 2766:   for (i = 0; tmp[i]; i++)
 2767:     if (!isspace((int)tmp[i])) {
 2768:       first = i;
 2769:       break;
 2770:     }
 2771: 
 2772:   if (first == -1) {
 2773:     // There are no non-space characters.
 2774:     out[0] = '\0';
 2775:     return;
 2776:   }
 2777: 
 2778:   // Find the last non-space character.
 2779:   for (i = strlen(tmp)-1; i >= first && isspace((int)tmp[i]); i--);
 2780:   int last = i;
 2781: 
 2782:   strncpy(out, tmp+first, last-first+1);
 2783:   out[last-first+1] = '\0';
 2784: }

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