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

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

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