Annotation of embedaddon/smartmontools/scsicmds.cpp, revision 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>