Annotation of embedaddon/smartmontools/scsicmds.cpp, revision 1.1.1.1

1.1       misho       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 3302 2011-03-25 23:04:36Z dpgilbert $"
                     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>