File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / smartmontools / scsicmds.cpp
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 16:32:16 2012 UTC (12 years, 4 months ago) by misho
Branches: smartmontools, elwix, MAIN
CVS tags: v5_43, v5_42, HEAD
smartmontools

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

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