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

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

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