smartmontools SVN Rev 3317
Utility to control and monitor storage systems with "S.M.A.R.T."
scsicmds.cpp
Go to the documentation of this file.
00001 /*
00002  * scsicmds.cpp
00003  *
00004  * Home page of code is: http://smartmontools.sourceforge.net
00005  *
00006  * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
00007  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
00008  *
00009  * Additional SCSI work:
00010  * Copyright (C) 2003-13 Douglas Gilbert <dgilbert@interlog.com>
00011  *
00012  * This program is free software; you can redistribute it and/or modify
00013  * it under the terms of the GNU General Public License as published by
00014  * the Free Software Foundation; either version 2, or (at your option)
00015  * any later version.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
00019  *
00020  * This code was originally developed as a Senior Thesis by Michael Cornwell
00021  * at the Concurrent Systems Laboratory (now part of the Storage Systems
00022  * Research Center), Jack Baskin School of Engineering, University of
00023  * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
00024  *
00025  *
00026  * In the SCSI world "SMART" is a dead or withdrawn standard. In recent
00027  * SCSI standards (since SCSI-3) it goes under the awkward name of
00028  * "Informational Exceptions" ["IE" or "IEC" (with the "C" for "control")].
00029  * The relevant information is spread around several SCSI draft
00030  * standards available at http://www.t10.org . Reference is made in the
00031  * code to the following acronyms:
00032  *      - SAM [SCSI Architectural model, versions 2 or 3]
00033  *      - SPC [SCSI Primary commands, versions 2 or 3]
00034  *      - SBC [SCSI Block commands, versions 2]
00035  *
00036  * Some SCSI disk vendors have snippets of "SMART" information in their
00037  * product manuals.
00038  */
00039 
00040 #include <stdio.h>
00041 #include <string.h>
00042 #include <errno.h>
00043 #include <ctype.h>
00044 
00045 #include "config.h"
00046 #include "int64.h"
00047 #include "scsicmds.h"
00048 #include "atacmds.h" // FIXME: for smart_command_set only
00049 #include "dev_interface.h"
00050 #include "utility.h"
00051 
00052 const char *scsicmds_c_cvsid="$Id: scsicmds.cpp 3915 2014-06-19 18:24:57Z dpgilbert $"
00053   SCSICMDS_H_CVSID;
00054 
00055 // Print SCSI debug messages?
00056 unsigned char scsi_debugmode = 0;
00057 
00058 supported_vpd_pages * supported_vpd_pages_p = NULL;
00059 
00060 
00061 supported_vpd_pages::supported_vpd_pages(scsi_device * device) : num_valid(0)
00062 {
00063     unsigned char b[0xfc];     /* pre SPC-3 INQUIRY max response size */
00064     int n;
00065 
00066     memset(b, 0, sizeof(b));
00067     if (device && (0 == scsiInquiryVpd(device, SCSI_VPD_SUPPORTED_VPD_PAGES,
00068                    b, sizeof(b)))) {
00069         num_valid = (b[2] << 8) + b[3];
00070         n = sizeof(pages);
00071         if (num_valid > n)
00072             num_valid = n;
00073         memcpy(pages, b + 4, num_valid);
00074     }
00075 }
00076 
00077 bool
00078 supported_vpd_pages::is_supported(int vpd_page_num) const
00079 {
00080     /* Supported VPD pages numbers start at offset 4 and should be in
00081      * ascending order but don't assume that. */
00082     for (int k = 0; k < num_valid; ++k) {
00083         if (vpd_page_num == pages[k])
00084             return true;
00085     }
00086     return false;
00087 }
00088 
00089 /* output binary in hex and optionally ascii */
00090 void
00091 dStrHex(const char* str, int len, int no_ascii)
00092 {
00093     const char* p = str;
00094     unsigned char c;
00095     char buff[82];
00096     int a = 0;
00097     const int bpstart = 5;
00098     const int cpstart = 60;
00099     int cpos = cpstart;
00100     int bpos = bpstart;
00101     int i, k;
00102 
00103     if (len <= 0) return;
00104     memset(buff,' ',80);
00105     buff[80]='\0';
00106     k = snprintf(buff+1, sizeof(buff)-1, "%.2x", a);
00107     buff[k + 1] = ' ';
00108     if (bpos >= ((bpstart + (9 * 3))))
00109         bpos++;
00110 
00111     for(i = 0; i < len; i++)
00112     {
00113         c = *p++;
00114         bpos += 3;
00115         if (bpos == (bpstart + (9 * 3)))
00116             bpos++;
00117         snprintf(buff+bpos, sizeof(buff)-bpos, "%.2x", (int)(unsigned char)c);
00118         buff[bpos + 2] = ' ';
00119         if (no_ascii)
00120             buff[cpos++] = ' ';
00121         else {
00122             if ((c < ' ') || (c >= 0x7f))
00123                 c='.';
00124             buff[cpos++] = c;
00125         }
00126         if (cpos > (cpstart+15))
00127         {
00128             pout("%s\n", buff);
00129             bpos = bpstart;
00130             cpos = cpstart;
00131             a += 16;
00132             memset(buff,' ',80);
00133             k = snprintf(buff+1, sizeof(buff)-1, "%.2x", a);
00134             buff[k + 1] = ' ';
00135         }
00136     }
00137     if (cpos > cpstart)
00138     {
00139         pout("%s\n", buff);
00140     }
00141 }
00142 
00143 struct scsi_opcode_name {
00144     UINT8 opcode;
00145     const char * name;
00146 };
00147 
00148 static struct scsi_opcode_name opcode_name_arr[] = {
00149     /* in ascending opcode order */
00150     {TEST_UNIT_READY, "test unit ready"},       /* 0x00 */
00151     {REQUEST_SENSE, "request sense"},           /* 0x03 */
00152     {INQUIRY, "inquiry"},                       /* 0x12 */
00153     {MODE_SELECT, "mode select(6)"},            /* 0x15 */
00154     {MODE_SENSE, "mode sense(6)"},              /* 0x1a */
00155     {START_STOP_UNIT, "start stop unit"},       /* 0x1b */
00156     {RECEIVE_DIAGNOSTIC, "receive diagnostic"}, /* 0x1c */
00157     {SEND_DIAGNOSTIC, "send diagnostic"},       /* 0x1d */
00158     {READ_CAPACITY_10, "read capacity(10)"},    /* 0x25 */
00159     {READ_DEFECT_10, "read defect list(10)"},   /* 0x37 */
00160     {LOG_SELECT, "log select"},                 /* 0x4c */
00161     {LOG_SENSE, "log sense"},                   /* 0x4d */
00162     {MODE_SELECT_10, "mode select(10)"},        /* 0x55 */
00163     {MODE_SENSE_10, "mode sense(10)"},          /* 0x5a */
00164     {SAT_ATA_PASSTHROUGH_16, "ata pass-through(16)"}, /* 0x85 */
00165     {READ_CAPACITY_16, "read capacity(16)"},    /* 0x9e,0x10 */
00166     {REPORT_LUNS, "report luns"},               /* 0xa0 */
00167     {SAT_ATA_PASSTHROUGH_12, "ata pass-through(12)"}, /* 0xa1 */
00168     {READ_DEFECT_12, "read defect list(12)"},   /* 0xb7 */
00169 };
00170 
00171 static const char * vendor_specific = "<vendor specific>";
00172 
00173 /* Need to expand to take service action into account. For commands
00174  * of interest the service action is in the 2nd command byte */
00175 const char *
00176 scsi_get_opcode_name(UINT8 opcode)
00177 {
00178     int k;
00179     int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]);
00180     struct scsi_opcode_name * onp;
00181 
00182     if (opcode >= 0xc0)
00183         return vendor_specific;
00184     for (k = 0; k < len; ++k) {
00185         onp = &opcode_name_arr[k];
00186         if (opcode == onp->opcode)
00187             return onp->name;
00188         else if (opcode < onp->opcode)
00189             return NULL;
00190     }
00191     return NULL;
00192 }
00193 
00194 void
00195 scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf,
00196                      struct scsi_sense_disect * out)
00197 {
00198     int resp_code;
00199 
00200     memset(out, 0, sizeof(struct scsi_sense_disect));
00201     if (SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) {
00202         resp_code = (io_buf->sensep[0] & 0x7f);
00203         out->resp_code = resp_code;
00204         if (resp_code >= 0x72) {
00205             out->sense_key = (io_buf->sensep[1] & 0xf);
00206             out->asc = io_buf->sensep[2];
00207             out->ascq = io_buf->sensep[3];
00208         } else if (resp_code >= 0x70) {
00209             out->sense_key = (io_buf->sensep[2] & 0xf);
00210             if (io_buf->resp_sense_len > 13) {
00211                 out->asc = io_buf->sensep[12];
00212                 out->ascq = io_buf->sensep[13];
00213             }
00214         }
00215     }
00216 }
00217 
00218 int
00219 scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo)
00220 {
00221     switch (sinfo->sense_key) {
00222     case SCSI_SK_NO_SENSE:
00223     case SCSI_SK_RECOVERED_ERR:
00224         return SIMPLE_NO_ERROR;
00225     case SCSI_SK_NOT_READY:
00226         if (SCSI_ASC_NO_MEDIUM == sinfo->asc)
00227             return SIMPLE_ERR_NO_MEDIUM;
00228         else if (SCSI_ASC_NOT_READY == sinfo->asc) {
00229             if (0x1 == sinfo->ascq)
00230                 return SIMPLE_ERR_BECOMING_READY;
00231             else
00232                 return SIMPLE_ERR_NOT_READY;
00233         } else
00234             return SIMPLE_ERR_NOT_READY;
00235     case SCSI_SK_MEDIUM_ERROR:
00236     case SCSI_SK_HARDWARE_ERROR:
00237         return SIMPLE_ERR_MEDIUM_HARDWARE;
00238     case SCSI_SK_ILLEGAL_REQUEST:
00239         if (SCSI_ASC_UNKNOWN_OPCODE == sinfo->asc)
00240             return SIMPLE_ERR_BAD_OPCODE;
00241         else if (SCSI_ASC_INVALID_FIELD == sinfo->asc)
00242             return SIMPLE_ERR_BAD_FIELD;
00243         else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc)
00244             return SIMPLE_ERR_BAD_PARAM;
00245         else
00246             return SIMPLE_ERR_BAD_PARAM;    /* all other illegal request */
00247     case SCSI_SK_UNIT_ATTENTION:
00248         return SIMPLE_ERR_TRY_AGAIN;
00249     case SCSI_SK_ABORTED_COMMAND:
00250         return SIMPLE_ERR_ABORTED_COMMAND;
00251     default:
00252         return SIMPLE_ERR_UNKNOWN;
00253     }
00254 }
00255 
00256 const char *
00257 scsiErrString(int scsiErr)
00258 {
00259     if (scsiErr < 0)
00260         return strerror(-scsiErr);
00261     switch (scsiErr) {
00262         case SIMPLE_NO_ERROR:
00263             return "no error";
00264         case SIMPLE_ERR_NOT_READY:
00265             return "device not ready";
00266         case SIMPLE_ERR_BAD_OPCODE:
00267             return "unsupported scsi opcode";
00268         case SIMPLE_ERR_BAD_FIELD:
00269             return "unsupported field in scsi command";
00270         case SIMPLE_ERR_BAD_PARAM:
00271             return "badly formed scsi parameters";
00272         case SIMPLE_ERR_BAD_RESP:
00273             return "scsi response fails sanity test";
00274         case SIMPLE_ERR_NO_MEDIUM:
00275             return "no medium present";
00276         case SIMPLE_ERR_BECOMING_READY:
00277             return "device will be ready soon";
00278         case SIMPLE_ERR_TRY_AGAIN:
00279             return "unit attention reported, try again";
00280         case SIMPLE_ERR_MEDIUM_HARDWARE:
00281             return "medium or hardware error (serious)";
00282         case SIMPLE_ERR_UNKNOWN:
00283             return "unknown error (unexpected sense key)";
00284         case SIMPLE_ERR_ABORTED_COMMAND:
00285             return "aborted command";
00286         default:
00287             return "unknown error";
00288     }
00289 }
00290 
00291 /* Iterates to next designation descriptor in the device identification
00292  * VPD page. The 'initial_desig_desc' should point to start of first
00293  * descriptor with 'page_len' being the number of valid bytes in that
00294  * and following descriptors. To start, 'off' should point to a negative
00295  * value, thereafter it should point to the value yielded by the previous
00296  * call. If 0 returned then 'initial_desig_desc + *off' should be a valid
00297  * descriptor; returns -1 if normal end condition and -2 for an abnormal
00298  * termination. Matches association, designator_type and/or code_set when
00299  * any of those values are greater than or equal to zero. */
00300 int
00301 scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len,
00302                      int * off, int m_assoc, int m_desig_type, int m_code_set)
00303 {
00304     const unsigned char * ucp;
00305     int k, c_set, assoc, desig_type;
00306 
00307     for (k = *off, ucp = initial_desig_desc ; (k + 3) < page_len; ) {
00308         k = (k < 0) ? 0 : (k + ucp[k + 3] + 4);
00309         if ((k + 4) > page_len)
00310             break;
00311         c_set = (ucp[k] & 0xf);
00312         if ((m_code_set >= 0) && (m_code_set != c_set))
00313             continue;
00314         assoc = ((ucp[k + 1] >> 4) & 0x3);
00315         if ((m_assoc >= 0) && (m_assoc != assoc))
00316             continue;
00317         desig_type = (ucp[k + 1] & 0xf);
00318         if ((m_desig_type >= 0) && (m_desig_type != desig_type))
00319             continue;
00320         *off = k;
00321         return 0;
00322     }
00323     return (k == page_len) ? -1 : -2;
00324 }
00325 
00326 /* Decode VPD page 0x83 logical unit designator into a string. If both
00327  * numeric address and SCSI name string present, prefer the former.
00328  * Returns 0 on success, -1 on error with error string in s. */
00329 int
00330 scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
00331                       int * transport)
00332 {
00333     int m, c_set, assoc, desig_type, i_len, naa, off, u, have_scsi_ns;
00334     const unsigned char * ucp;
00335     const unsigned char * ip;
00336     int si = 0;
00337 
00338     if (transport)
00339         *transport = -1;
00340     if (slen < 32) {
00341         if (slen > 0)
00342             s[0] = '\0';
00343         return -1;
00344     }
00345     have_scsi_ns = 0;
00346     s[0] = '\0';
00347     off = -1;
00348     while ((u = scsi_vpd_dev_id_iter(b, blen, &off, -1, -1, -1)) == 0) {
00349         ucp = b + off;
00350         i_len = ucp[3];
00351         if ((off + i_len + 4) > blen) {
00352             snprintf(s+si, slen-si, "error: designator length");
00353             return -1;
00354         }
00355         assoc = ((ucp[1] >> 4) & 0x3);
00356         if (transport && assoc && (ucp[1] & 0x80) && (*transport < 0))
00357             *transport = (ucp[0] >> 4) & 0xf;
00358         if (0 != assoc)
00359             continue;
00360         ip = ucp + 4;
00361         c_set = (ucp[0] & 0xf);
00362         desig_type = (ucp[1] & 0xf);
00363 
00364         switch (desig_type) {
00365         case 0: /* vendor specific */
00366         case 1: /* T10 vendor identification */
00367             break;
00368         case 2: /* EUI-64 based */
00369             if ((8 != i_len) && (12 != i_len) && (16 != i_len)) {
00370                 snprintf(s+si, slen-si, "error: EUI-64 length");
00371                 return -1;
00372             }
00373             if (have_scsi_ns)
00374                 si = 0;
00375             si += snprintf(s+si, slen-si, "0x");
00376             for (m = 0; m < i_len; ++m)
00377                 si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
00378             break;
00379         case 3: /* NAA */
00380             if (1 != c_set) {
00381                 snprintf(s+si, slen-si, "error: NAA bad code_set");
00382                 return -1;
00383             }
00384             naa = (ip[0] >> 4) & 0xff;
00385             if ((naa < 2) || (naa > 6) || (4 == naa)) {
00386                 snprintf(s+si, slen-si, "error: unexpected NAA");
00387                 return -1;
00388             }
00389             if (have_scsi_ns)
00390                 si = 0;
00391             if (2 == naa) {             /* NAA IEEE Extended */
00392                 if (8 != i_len) {
00393                     snprintf(s+si, slen-si, "error: NAA 2 length");
00394                     return -1;
00395                 }
00396                 si += snprintf(s+si, slen-si, "0x");
00397                 for (m = 0; m < 8; ++m)
00398                     si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
00399             } else if ((3 == naa ) || (5 == naa)) {
00400                 /* NAA=3 Locally assigned; NAA=5 IEEE Registered */
00401                 if (8 != i_len) {
00402                     snprintf(s+si, slen-si, "error: NAA 3 or 5 length");
00403                     return -1;
00404                 }
00405                 si += snprintf(s+si, slen-si, "0x");
00406                 for (m = 0; m < 8; ++m)
00407                     si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
00408             } else if (6 == naa) {      /* NAA IEEE Registered extended */
00409                 if (16 != i_len) {
00410                     snprintf(s+si, slen-si, "error: NAA 6 length");
00411                     return -1;
00412                 }
00413                 si += snprintf(s+si, slen-si, "0x");
00414                 for (m = 0; m < 16; ++m)
00415                     si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
00416             }
00417             break;
00418         case 4: /* Relative target port */
00419         case 5: /* (primary) Target port group */
00420         case 6: /* Logical unit group */
00421         case 7: /* MD5 logical unit identifier */
00422             break;
00423         case 8: /* SCSI name string */
00424             if (3 != c_set) {
00425                 snprintf(s+si, slen-si, "error: SCSI name string");
00426                 return -1;
00427             }
00428             /* does %s print out UTF-8 ok?? */
00429             if (si == 0) {
00430                 si += snprintf(s+si, slen-si, "%s", (const char *)ip);
00431                 ++have_scsi_ns;
00432             }
00433             break;
00434         default: /* reserved */
00435             break;
00436         }
00437     }
00438     if (-2 == u) {
00439         snprintf(s+si, slen-si, "error: bad structure");
00440         return -1;
00441     }
00442     return 0;
00443 }
00444 
00445 /* Sends LOG SENSE command. Returns 0 if ok, 1 if device NOT READY, 2 if
00446    command not supported, 3 if field (within command) not supported or
00447    returns negated errno.  SPC-3 sections 6.6 and 7.2 (rec 22a).
00448    N.B. Sets PC==1 to fetch "current cumulative" log pages.
00449    If known_resp_len > 0 then a single fetch is done for this response
00450    length. If known_resp_len == 0 then twin fetches are performed, the
00451    first to deduce the response length, then send the same command again
00452    requesting the deduced response length. This protects certain fragile
00453    HBAs. The twin fetch technique should not be used with the TapeAlert
00454    log page since it clears its state flags after each fetch. */
00455 int
00456 scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
00457              int bufLen, int known_resp_len)
00458 {
00459     struct scsi_cmnd_io io_hdr;
00460     struct scsi_sense_disect sinfo;
00461     UINT8 cdb[10];
00462     UINT8 sense[32];
00463     int pageLen;
00464     int status, res;
00465 
00466     if (known_resp_len > bufLen)
00467         return -EIO;
00468     if (known_resp_len > 0)
00469         pageLen = known_resp_len;
00470     else {
00471         /* Starting twin fetch strategy: first fetch to find respone length */
00472         pageLen = 4;
00473         if (pageLen > bufLen)
00474             return -EIO;
00475         else
00476             memset(pBuf, 0, pageLen);
00477 
00478         memset(&io_hdr, 0, sizeof(io_hdr));
00479         memset(cdb, 0, sizeof(cdb));
00480         io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
00481         io_hdr.dxfer_len = pageLen;
00482         io_hdr.dxferp = pBuf;
00483         cdb[0] = LOG_SENSE;
00484         cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
00485         cdb[3] = subpagenum;
00486         cdb[7] = (pageLen >> 8) & 0xff;
00487         cdb[8] = pageLen & 0xff;
00488         io_hdr.cmnd = cdb;
00489         io_hdr.cmnd_len = sizeof(cdb);
00490         io_hdr.sensep = sense;
00491         io_hdr.max_sense_len = sizeof(sense);
00492         io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
00493 
00494         if (!device->scsi_pass_through(&io_hdr))
00495           return -device->get_errno();
00496         scsi_do_sense_disect(&io_hdr, &sinfo);
00497         if ((res = scsiSimpleSenseFilter(&sinfo)))
00498             return res;
00499         /* sanity check on response */
00500         if ((SUPPORTED_LPAGES != pagenum) && ((pBuf[0] & 0x3f) != pagenum))
00501             return SIMPLE_ERR_BAD_RESP;
00502         if (0 == ((pBuf[2] << 8) + pBuf[3]))
00503             return SIMPLE_ERR_BAD_RESP;
00504         pageLen = (pBuf[2] << 8) + pBuf[3] + 4;
00505         if (4 == pageLen)  /* why define a lpage with no payload? */
00506             pageLen = 252; /* some IBM tape drives don't like double fetch */
00507         /* some SCSI HBA don't like "odd" length transfers */
00508         if (pageLen % 2)
00509             pageLen += 1;
00510         if (pageLen > bufLen)
00511             pageLen = bufLen;
00512     }
00513     memset(pBuf, 0, 4);
00514     memset(&io_hdr, 0, sizeof(io_hdr));
00515     memset(cdb, 0, sizeof(cdb));
00516     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
00517     io_hdr.dxfer_len = pageLen;
00518     io_hdr.dxferp = pBuf;
00519     cdb[0] = LOG_SENSE;
00520     cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
00521     cdb[7] = (pageLen >> 8) & 0xff;
00522     cdb[8] = pageLen & 0xff;
00523     io_hdr.cmnd = cdb;
00524     io_hdr.cmnd_len = sizeof(cdb);
00525     io_hdr.sensep = sense;
00526     io_hdr.max_sense_len = sizeof(sense);
00527     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
00528 
00529     if (!device->scsi_pass_through(&io_hdr))
00530       return -device->get_errno();
00531     scsi_do_sense_disect(&io_hdr, &sinfo);
00532     status = scsiSimpleSenseFilter(&sinfo);
00533     if (0 != status)
00534         return status;
00535     /* sanity check on response */
00536     if ((SUPPORTED_LPAGES != pagenum) && ((pBuf[0] & 0x3f) != pagenum))
00537         return SIMPLE_ERR_BAD_RESP;
00538     if (0 == ((pBuf[2] << 8) + pBuf[3]))
00539         return SIMPLE_ERR_BAD_RESP;
00540     return 0;
00541 }
00542 
00543 /* Sends a LOG SELECT command. Can be used to set log page values
00544  * or reset one log page (or all of them) to its defaults (typically zero).
00545  * Returns 0 if ok, 1 if NOT READY, 2 if command not supported, * 3 if
00546  * field in command not supported, * 4 if bad parameter to command or
00547  * returns negated errno. SPC-4 sections 6.5 and 7.2 (rev 20) */
00548 int
00549 scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,
00550               int subpagenum, UINT8 *pBuf, int bufLen)
00551 {
00552     struct scsi_cmnd_io io_hdr;
00553     struct scsi_sense_disect sinfo;
00554     UINT8 cdb[10];
00555     UINT8 sense[32];
00556 
00557     memset(&io_hdr, 0, sizeof(io_hdr));
00558     memset(cdb, 0, sizeof(cdb));
00559     io_hdr.dxfer_dir = DXFER_TO_DEVICE;
00560     io_hdr.dxfer_len = bufLen;
00561     io_hdr.dxferp = pBuf;
00562     cdb[0] = LOG_SELECT;
00563     cdb[1] = (pcr ? 2 : 0) | (sp ? 1 : 0);
00564     cdb[2] = ((pc << 6) & 0xc0) | (pagenum & 0x3f);
00565     cdb[3] = (subpagenum & 0xff);
00566     cdb[7] = ((bufLen >> 8) & 0xff);
00567     cdb[8] = (bufLen & 0xff);
00568     io_hdr.cmnd = cdb;
00569     io_hdr.cmnd_len = sizeof(cdb);
00570     io_hdr.sensep = sense;
00571     io_hdr.max_sense_len = sizeof(sense);
00572     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
00573 
00574     if (!device->scsi_pass_through(&io_hdr))
00575       return -device->get_errno();
00576     scsi_do_sense_disect(&io_hdr, &sinfo);
00577     return scsiSimpleSenseFilter(&sinfo);
00578 }
00579 
00580 /* Send MODE SENSE (6 byte) command. Returns 0 if ok, 1 if NOT READY,
00581  * 2 if command not supported (then MODE SENSE(10) should be supported),
00582  * 3 if field in command not supported or returns negated errno.
00583  * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */
00584 int
00585 scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
00586               UINT8 *pBuf, int bufLen)
00587 {
00588     struct scsi_cmnd_io io_hdr;
00589     struct scsi_sense_disect sinfo;
00590     UINT8 cdb[6];
00591     UINT8 sense[32];
00592     int status;
00593 
00594     if ((bufLen < 0) || (bufLen > 255))
00595         return -EINVAL;
00596     memset(&io_hdr, 0, sizeof(io_hdr));
00597     memset(cdb, 0, sizeof(cdb));
00598     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
00599     io_hdr.dxfer_len = bufLen;
00600     io_hdr.dxferp = pBuf;
00601     cdb[0] = MODE_SENSE;
00602     cdb[2] = (pc << 6) | (pagenum & 0x3f);
00603     cdb[3] = subpagenum;
00604     cdb[4] = bufLen;
00605     io_hdr.cmnd = cdb;
00606     io_hdr.cmnd_len = sizeof(cdb);
00607     io_hdr.sensep = sense;
00608     io_hdr.max_sense_len = sizeof(sense);
00609     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
00610 
00611     if (!device->scsi_pass_through(&io_hdr))
00612       return -device->get_errno();
00613     scsi_do_sense_disect(&io_hdr, &sinfo);
00614     status = scsiSimpleSenseFilter(&sinfo);
00615     if (SIMPLE_ERR_TRY_AGAIN == status) {
00616         if (!device->scsi_pass_through(&io_hdr))
00617           return -device->get_errno();
00618         scsi_do_sense_disect(&io_hdr, &sinfo);
00619         status = scsiSimpleSenseFilter(&sinfo);
00620     }
00621     if ((0 == status) && (ALL_MODE_PAGES != pagenum)) {
00622         int offset;
00623 
00624         offset = scsiModePageOffset(pBuf, bufLen, 0);
00625         if (offset < 0)
00626             return SIMPLE_ERR_BAD_RESP;
00627         else if (pagenum != (pBuf[offset] & 0x3f))
00628             return SIMPLE_ERR_BAD_RESP;
00629     }
00630     return status;
00631 }
00632 
00633 /* Sends a 6 byte MODE SELECT command. Assumes given pBuf is the response
00634  * from a corresponding 6 byte MODE SENSE command. Such a response should
00635  * have a 4 byte header followed by 0 or more 8 byte block descriptors
00636  * (normally 1) and then 1 mode page. Returns 0 if ok, 1 if NOT READY,
00637  * 2 if command not supported (then MODE SELECT(10) may be supported),
00638  * 3 if field in command not supported, 4 if bad parameter to command
00639  * or returns negated errno. SPC-3 sections 6.7 and 7.4 (rev 22a) */
00640 int
00641 scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
00642 {
00643     struct scsi_cmnd_io io_hdr;
00644     struct scsi_sense_disect sinfo;
00645     UINT8 cdb[6];
00646     UINT8 sense[32];
00647     int pg_offset, pg_len, hdr_plus_1_pg;
00648 
00649     pg_offset = 4 + pBuf[3];
00650     if (pg_offset + 2 >= bufLen)
00651         return -EINVAL;
00652     pg_len = pBuf[pg_offset + 1] + 2;
00653     hdr_plus_1_pg = pg_offset + pg_len;
00654     if (hdr_plus_1_pg > bufLen)
00655         return -EINVAL;
00656     pBuf[0] = 0;    /* Length of returned mode sense data reserved for SELECT */
00657     pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
00658     memset(&io_hdr, 0, sizeof(io_hdr));
00659     memset(cdb, 0, sizeof(cdb));
00660     io_hdr.dxfer_dir = DXFER_TO_DEVICE;
00661     io_hdr.dxfer_len = hdr_plus_1_pg;
00662     io_hdr.dxferp = pBuf;
00663     cdb[0] = MODE_SELECT;
00664     cdb[1] = 0x10 | (sp & 1);      /* set PF (page format) bit always */
00665     cdb[4] = hdr_plus_1_pg; /* make sure only one page sent */
00666     io_hdr.cmnd = cdb;
00667     io_hdr.cmnd_len = sizeof(cdb);
00668     io_hdr.sensep = sense;
00669     io_hdr.max_sense_len = sizeof(sense);
00670     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
00671 
00672     if (!device->scsi_pass_through(&io_hdr))
00673       return -device->get_errno();
00674     scsi_do_sense_disect(&io_hdr, &sinfo);
00675     return scsiSimpleSenseFilter(&sinfo);
00676 }
00677 
00678 /* MODE SENSE (10 byte). Returns 0 if ok, 1 if NOT READY, 2 if command
00679  * not supported (then MODE SENSE(6) might be supported), 3 if field in
00680  * command not supported or returns negated errno.
00681  * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */
00682 int
00683 scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
00684                 UINT8 *pBuf, int bufLen)
00685 {
00686     struct scsi_cmnd_io io_hdr;
00687     struct scsi_sense_disect sinfo;
00688     UINT8 cdb[10];
00689     UINT8 sense[32];
00690     int status;
00691 
00692     memset(&io_hdr, 0, sizeof(io_hdr));
00693     memset(cdb, 0, sizeof(cdb));
00694     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
00695     io_hdr.dxfer_len = bufLen;
00696     io_hdr.dxferp = pBuf;
00697     cdb[0] = MODE_SENSE_10;
00698     cdb[2] = (pc << 6) | (pagenum & 0x3f);
00699     cdb[3] = subpagenum;
00700     cdb[7] = (bufLen >> 8) & 0xff;
00701     cdb[8] = bufLen & 0xff;
00702     io_hdr.cmnd = cdb;
00703     io_hdr.cmnd_len = sizeof(cdb);
00704     io_hdr.sensep = sense;
00705     io_hdr.max_sense_len = sizeof(sense);
00706     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
00707 
00708     if (!device->scsi_pass_through(&io_hdr))
00709       return -device->get_errno();
00710     scsi_do_sense_disect(&io_hdr, &sinfo);
00711     status = scsiSimpleSenseFilter(&sinfo);
00712     if (SIMPLE_ERR_TRY_AGAIN == status) {
00713         if (!device->scsi_pass_through(&io_hdr))
00714           return -device->get_errno();
00715         scsi_do_sense_disect(&io_hdr, &sinfo);
00716         status = scsiSimpleSenseFilter(&sinfo);
00717     }
00718     if ((0 == status) && (ALL_MODE_PAGES != pagenum)) {
00719         int offset;
00720 
00721         offset = scsiModePageOffset(pBuf, bufLen, 1);
00722         if (offset < 0)
00723             return SIMPLE_ERR_BAD_RESP;
00724         else if (pagenum != (pBuf[offset] & 0x3f))
00725             return SIMPLE_ERR_BAD_RESP;
00726     }
00727     return status;
00728 }
00729 
00730 /* Sends a 10 byte MODE SELECT command. Assumes given pBuf is the response
00731  * from a corresponding 10 byte MODE SENSE command. Such a response should
00732  * have a 8 byte header followed by 0 or more 8 byte block descriptors
00733  * (normally 1) and then 1 mode page. Returns 0 if ok, 1 NOT REAFY, 2 if
00734  * command not supported (then MODE SELECT(6) may be supported), 3 if field
00735  * in command not supported, 4 if bad parameter to command or returns
00736  * negated errno. SPC-3 sections 6.8 and 7.4 (rev 22a) */
00737 int
00738 scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
00739 {
00740     struct scsi_cmnd_io io_hdr;
00741     struct scsi_sense_disect sinfo;
00742     UINT8 cdb[10];
00743     UINT8 sense[32];
00744     int pg_offset, pg_len, hdr_plus_1_pg;
00745 
00746     pg_offset = 8 + (pBuf[6] << 8) + pBuf[7];
00747     if (pg_offset + 2 >= bufLen)
00748         return -EINVAL;
00749     pg_len = pBuf[pg_offset + 1] + 2;
00750     hdr_plus_1_pg = pg_offset + pg_len;
00751     if (hdr_plus_1_pg > bufLen)
00752         return -EINVAL;
00753     pBuf[0] = 0;
00754     pBuf[1] = 0; /* Length of returned mode sense data reserved for SELECT */
00755     pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
00756     memset(&io_hdr, 0, sizeof(io_hdr));
00757     memset(cdb, 0, sizeof(cdb));
00758     io_hdr.dxfer_dir = DXFER_TO_DEVICE;
00759     io_hdr.dxfer_len = hdr_plus_1_pg;
00760     io_hdr.dxferp = pBuf;
00761     cdb[0] = MODE_SELECT_10;
00762     cdb[1] = 0x10 | (sp & 1);      /* set PF (page format) bit always */
00763     cdb[8] = hdr_plus_1_pg; /* make sure only one page sent */
00764     io_hdr.cmnd = cdb;
00765     io_hdr.cmnd_len = sizeof(cdb);
00766     io_hdr.sensep = sense;
00767     io_hdr.max_sense_len = sizeof(sense);
00768     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
00769 
00770     if (!device->scsi_pass_through(&io_hdr))
00771       return -device->get_errno();
00772     scsi_do_sense_disect(&io_hdr, &sinfo);
00773     return scsiSimpleSenseFilter(&sinfo);
00774 }
00775 
00776 /* Standard INQUIRY returns 0 for ok, anything else is a major problem.
00777  * bufLen should be 36 for unsafe devices (like USB mass storage stuff)
00778  * otherwise they can lock up! SPC-3 sections 6.4 and 7.6 (rev 22a) */
00779 int
00780 scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen)
00781 {
00782     struct scsi_sense_disect sinfo;
00783     struct scsi_cmnd_io io_hdr;
00784     UINT8 cdb[6];
00785     UINT8 sense[32];
00786 
00787     if ((bufLen < 0) || (bufLen > 1023))
00788         return -EINVAL;
00789     memset(&io_hdr, 0, sizeof(io_hdr));
00790     memset(cdb, 0, sizeof(cdb));
00791     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
00792     io_hdr.dxfer_len = bufLen;
00793     io_hdr.dxferp = pBuf;
00794     cdb[0] = INQUIRY;
00795     cdb[3] = (bufLen >> 8) & 0xff;
00796     cdb[4] = (bufLen & 0xff);
00797     io_hdr.cmnd = cdb;
00798     io_hdr.cmnd_len = sizeof(cdb);
00799     io_hdr.sensep = sense;
00800     io_hdr.max_sense_len = sizeof(sense);
00801     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
00802 
00803     if (!device->scsi_pass_through(&io_hdr))
00804       return -device->get_errno();
00805     scsi_do_sense_disect(&io_hdr, &sinfo);
00806     return scsiSimpleSenseFilter(&sinfo);
00807 }
00808 
00809 /* INQUIRY to fetch Vital Page Data.  Returns 0 if ok, 1 if NOT READY
00810  * (unlikely), 2 if command not supported, 3 if field in command not
00811  * supported, 5 if response indicates that EVPD bit ignored or returns
00812  * negated errno. SPC-3 section 6.4 and 7.6 (rev 22a) */
00813 int
00814 scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen)
00815 {
00816     struct scsi_cmnd_io io_hdr;
00817     struct scsi_sense_disect sinfo;
00818     UINT8 cdb[6];
00819     UINT8 sense[32];
00820     int res;
00821 
00822     /* Assume SCSI_VPD_SUPPORTED_VPD_PAGES is first VPD page fetched */
00823     if ((SCSI_VPD_SUPPORTED_VPD_PAGES != vpd_page) &&
00824         supported_vpd_pages_p &&
00825         (! supported_vpd_pages_p->is_supported(vpd_page)))
00826         return 3;
00827 
00828     if ((bufLen < 0) || (bufLen > 1023))
00829         return -EINVAL;
00830 try_again:
00831     memset(&io_hdr, 0, sizeof(io_hdr));
00832     memset(cdb, 0, sizeof(cdb));
00833     if (bufLen > 1)
00834         pBuf[1] = 0x0;
00835     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
00836     io_hdr.dxfer_len = bufLen;
00837     io_hdr.dxferp = pBuf;
00838     cdb[0] = INQUIRY;
00839     cdb[1] = 0x1;       /* set EVPD bit (enable Vital Product Data) */
00840     cdb[2] = vpd_page;
00841     cdb[3] = (bufLen >> 8) & 0xff;
00842     cdb[4] = (bufLen & 0xff);
00843     io_hdr.cmnd = cdb;
00844     io_hdr.cmnd_len = sizeof(cdb);
00845     io_hdr.sensep = sense;
00846     io_hdr.max_sense_len = sizeof(sense);
00847     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
00848 
00849     if (!device->scsi_pass_through(&io_hdr))
00850       return -device->get_errno();
00851     scsi_do_sense_disect(&io_hdr, &sinfo);
00852     if ((SCSI_STATUS_CHECK_CONDITION == io_hdr.scsi_status) &&
00853         (SCSI_SK_ILLEGAL_REQUEST == sinfo.sense_key) &&
00854         (SCSI_ASC_INVALID_FIELD == sinfo.asc) &&
00855         (cdb[3] > 0)) {
00856         bufLen &= 0xff; /* make sure cdb[3] is 0 next time around */
00857         goto try_again;
00858     }
00859 
00860     if ((res = scsiSimpleSenseFilter(&sinfo)))
00861         return res;
00862     /* Guard against devices that ignore EVPD bit and do standard INQUIRY */
00863     if (bufLen > 1) {
00864         if (vpd_page == pBuf[1]) {
00865             if ((0x80 == vpd_page) && (bufLen > 2) && (0x0 != pBuf[2]))
00866                 return SIMPLE_ERR_BAD_RESP;
00867         } else
00868             return SIMPLE_ERR_BAD_RESP;
00869     }
00870     return 0;
00871 }
00872 
00873 /* REQUEST SENSE command. Returns 0 if ok, anything else major problem.
00874  * SPC-3 section 6.27 (rev 22a) */
00875 int
00876 scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
00877 {
00878     struct scsi_cmnd_io io_hdr;
00879     UINT8 cdb[6];
00880     UINT8 sense[32];
00881     UINT8 buff[18];
00882     int len;
00883     UINT8 resp_code;
00884 
00885     memset(&io_hdr, 0, sizeof(io_hdr));
00886     memset(cdb, 0, sizeof(cdb));
00887     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
00888     io_hdr.dxfer_len = sizeof(buff);
00889     io_hdr.dxferp = buff;
00890     cdb[0] = REQUEST_SENSE;
00891     cdb[4] = sizeof(buff);
00892     io_hdr.cmnd = cdb;
00893     io_hdr.cmnd_len = sizeof(cdb);
00894     io_hdr.sensep = sense;
00895     io_hdr.max_sense_len = sizeof(sense);
00896     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
00897 
00898     if (!device->scsi_pass_through(&io_hdr))
00899       return -device->get_errno();
00900     if (sense_info) {
00901         resp_code = buff[0] & 0x7f;
00902         sense_info->resp_code = resp_code;
00903         sense_info->sense_key = buff[2] & 0xf;
00904         sense_info->asc = 0;
00905         sense_info->ascq = 0;
00906         if ((0x70 == resp_code) || (0x71 == resp_code)) {
00907             len = buff[7] + 8;
00908             if (len > 13) {
00909                 sense_info->asc = buff[12];
00910                 sense_info->ascq = buff[13];
00911             }
00912         }
00913     // fill progrss indicator, if available
00914     sense_info->progress = -1;
00915     switch (resp_code) {
00916       const unsigned char * ucp;
00917       int sk, sk_pr;
00918       case 0x70:
00919       case 0x71:
00920           sk = (buff[2] & 0xf);
00921           if ((sizeof(buff) < 18) ||
00922               ((SCSI_SK_NO_SENSE != sk) && (SCSI_SK_NOT_READY != sk))) {
00923               break;
00924           }
00925           if (buff[15] & 0x80) {        /* SKSV bit set */
00926               sense_info->progress = (buff[16] << 8) + buff[17];
00927               break;
00928           } else {
00929               break;
00930           }
00931       case 0x72:
00932       case 0x73:
00933           /* sense key specific progress (0x2) or progress descriptor (0xa) */
00934           sk = (buff[1] & 0xf);
00935           sk_pr = (SCSI_SK_NO_SENSE == sk) || (SCSI_SK_NOT_READY == sk);
00936           if (sk_pr && ((ucp = sg_scsi_sense_desc_find(buff, sizeof(buff), 2))) &&
00937               (0x6 == ucp[1]) && (0x80 & ucp[4])) {
00938               sense_info->progress = (ucp[5] << 8) + ucp[6];
00939               break;
00940           } else if (((ucp = sg_scsi_sense_desc_find(buff, sizeof(buff), 0xa))) &&
00941                      ((0x6 == ucp[1]))) {
00942               sense_info->progress = (ucp[6] << 8) + ucp[7];
00943               break;
00944           } else
00945               break;
00946       default:
00947           return 0;
00948       }
00949     }
00950     return 0;
00951 }
00952 
00953 /* SEND DIAGNOSTIC command.  Returns 0 if ok, 1 if NOT READY, 2 if command
00954  * not supported, 3 if field in command not supported or returns negated
00955  * errno. SPC-3 section 6.28 (rev 22a) */
00956 int
00957 scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf,
00958                    int bufLen)
00959 {
00960     struct scsi_cmnd_io io_hdr;
00961     struct scsi_sense_disect sinfo;
00962     UINT8 cdb[6];
00963     UINT8 sense[32];
00964 
00965     memset(&io_hdr, 0, sizeof(io_hdr));
00966     memset(cdb, 0, sizeof(cdb));
00967     io_hdr.dxfer_dir = bufLen ? DXFER_TO_DEVICE: DXFER_NONE;
00968     io_hdr.dxfer_len = bufLen;
00969     io_hdr.dxferp = pBuf;
00970     cdb[0] = SEND_DIAGNOSTIC;
00971     if (SCSI_DIAG_DEF_SELF_TEST == functioncode)
00972         cdb[1] = 0x4;  /* SelfTest bit */
00973     else if (SCSI_DIAG_NO_SELF_TEST != functioncode)
00974         cdb[1] = (functioncode & 0x7) << 5; /* SelfTest _code_ */
00975     else   /* SCSI_DIAG_NO_SELF_TEST == functioncode */
00976         cdb[1] = 0x10;  /* PF bit */
00977     cdb[3] = (bufLen >> 8) & 0xff;
00978     cdb[4] = bufLen & 0xff;
00979     io_hdr.cmnd = cdb;
00980     io_hdr.cmnd_len = sizeof(cdb);
00981     io_hdr.sensep = sense;
00982     io_hdr.max_sense_len = sizeof(sense);
00983     /* worst case is an extended foreground self test on a big disk */
00984     io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST;
00985 
00986     if (!device->scsi_pass_through(&io_hdr))
00987       return -device->get_errno();
00988     scsi_do_sense_disect(&io_hdr, &sinfo);
00989     return scsiSimpleSenseFilter(&sinfo);
00990 }
00991 
00992 /* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if
00993  * command not supported, 3 if field in command not supported or returns
00994  * negated errno. SPC-3 section 6.18 (rev 22a) */
00995 int
00996 scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf,
00997                       int bufLen)
00998 {
00999     struct scsi_cmnd_io io_hdr;
01000     struct scsi_sense_disect sinfo;
01001     UINT8 cdb[6];
01002     UINT8 sense[32];
01003 
01004     memset(&io_hdr, 0, sizeof(io_hdr));
01005     memset(cdb, 0, sizeof(cdb));
01006     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
01007     io_hdr.dxfer_len = bufLen;
01008     io_hdr.dxferp = pBuf;
01009     cdb[0] = RECEIVE_DIAGNOSTIC;
01010     cdb[1] = pcv;
01011     cdb[2] = pagenum;
01012     cdb[3] = (bufLen >> 8) & 0xff;
01013     cdb[4] = bufLen & 0xff;
01014     io_hdr.cmnd = cdb;
01015     io_hdr.cmnd_len = sizeof(cdb);
01016     io_hdr.sensep = sense;
01017     io_hdr.max_sense_len = sizeof(sense);
01018     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
01019 
01020     if (!device->scsi_pass_through(&io_hdr))
01021       return -device->get_errno();
01022     scsi_do_sense_disect(&io_hdr, &sinfo);
01023     return scsiSimpleSenseFilter(&sinfo);
01024 }
01025 
01026 /* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */
01027 static int
01028 _testunitready(scsi_device * device, struct scsi_sense_disect * sinfo)
01029 {
01030     struct scsi_cmnd_io io_hdr;
01031     UINT8 cdb[6];
01032     UINT8 sense[32];
01033 
01034     memset(&io_hdr, 0, sizeof(io_hdr));
01035     memset(cdb, 0, sizeof(cdb));
01036     io_hdr.dxfer_dir = DXFER_NONE;
01037     io_hdr.dxfer_len = 0;
01038     io_hdr.dxferp = NULL;
01039     cdb[0] = TEST_UNIT_READY;
01040     io_hdr.cmnd = cdb;
01041     io_hdr.cmnd_len = sizeof(cdb);
01042     io_hdr.sensep = sense;
01043     io_hdr.max_sense_len = sizeof(sense);
01044     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
01045 
01046     if (!device->scsi_pass_through(&io_hdr))
01047       return -device->get_errno();
01048     scsi_do_sense_disect(&io_hdr, sinfo);
01049     return 0;
01050 }
01051 
01052 /* Returns 0 for device responds and media ready, 1 for device responds and
01053    media not ready, or returns a negated errno value */
01054 int
01055 scsiTestUnitReady(scsi_device * device)
01056 {
01057     struct scsi_sense_disect sinfo;
01058     int status;
01059 
01060     status = _testunitready(device, &sinfo);
01061     if (0 != status)
01062         return status;
01063     status = scsiSimpleSenseFilter(&sinfo);
01064     if (SIMPLE_ERR_TRY_AGAIN == status) {
01065         /* power on reset, media changed, ok ... try again */
01066         status = _testunitready(device, &sinfo);
01067         if (0 != status)
01068             return status;
01069         status = scsiSimpleSenseFilter(&sinfo);
01070     }
01071     return status;
01072 }
01073 
01074 /* READ DEFECT (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
01075  * command not supported, 3 if field in command not supported or returns
01076  * negated errno. SBC-2 section 5.12 (rev 16) */
01077 int
01078 scsiReadDefect10(scsi_device * device, int req_plist, int req_glist,
01079                  int dl_format, UINT8 *pBuf, int bufLen)
01080 {
01081     struct scsi_cmnd_io io_hdr;
01082     struct scsi_sense_disect sinfo;
01083     UINT8 cdb[10];
01084     UINT8 sense[32];
01085 
01086     memset(&io_hdr, 0, sizeof(io_hdr));
01087     memset(cdb, 0, sizeof(cdb));
01088     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
01089     io_hdr.dxfer_len = bufLen;
01090     io_hdr.dxferp = pBuf;
01091     cdb[0] = READ_DEFECT_10;
01092     cdb[2] = (unsigned char)(((req_plist << 4) & 0x10) |
01093                ((req_glist << 3) & 0x8) | (dl_format & 0x7));
01094     cdb[7] = (bufLen >> 8) & 0xff;
01095     cdb[8] = bufLen & 0xff;
01096     io_hdr.cmnd = cdb;
01097     io_hdr.cmnd_len = sizeof(cdb);
01098     io_hdr.sensep = sense;
01099     io_hdr.max_sense_len = sizeof(sense);
01100     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
01101 
01102     if (!device->scsi_pass_through(&io_hdr))
01103       return -device->get_errno();
01104     scsi_do_sense_disect(&io_hdr, &sinfo);
01105     return scsiSimpleSenseFilter(&sinfo);
01106 }
01107 
01108 /* READ DEFECT (12) command. Returns 0 if ok, 1 if NOT READY, 2 if
01109  * command not supported, 3 if field in command not supported or returns
01110  * negated errno. SBC-3 section 5.18 (rev 35; vale Mark Evans) */
01111 int
01112 scsiReadDefect12(scsi_device * device, int req_plist, int req_glist,
01113                  int dl_format, int addrDescIndex, UINT8 *pBuf, int bufLen)
01114 {
01115     struct scsi_cmnd_io io_hdr;
01116     struct scsi_sense_disect sinfo;
01117     UINT8 cdb[12];
01118     UINT8 sense[32];
01119 
01120     memset(&io_hdr, 0, sizeof(io_hdr));
01121     memset(cdb, 0, sizeof(cdb));
01122     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
01123     io_hdr.dxfer_len = bufLen;
01124     io_hdr.dxferp = pBuf;
01125     cdb[0] = READ_DEFECT_12;
01126     cdb[1] = (unsigned char)(((req_plist << 4) & 0x10) |
01127                ((req_glist << 3) & 0x8) | (dl_format & 0x7));
01128     cdb[2] = (addrDescIndex >> 24) & 0xff;
01129     cdb[3] = (addrDescIndex >> 16) & 0xff;
01130     cdb[4] = (addrDescIndex >> 8) & 0xff;
01131     cdb[5] = addrDescIndex & 0xff;
01132     cdb[6] = (bufLen >> 24) & 0xff;
01133     cdb[7] = (bufLen >> 16) & 0xff;
01134     cdb[8] = (bufLen >> 8) & 0xff;
01135     cdb[9] = bufLen & 0xff;
01136     io_hdr.cmnd = cdb;
01137     io_hdr.cmnd_len = sizeof(cdb);
01138     io_hdr.sensep = sense;
01139     io_hdr.max_sense_len = sizeof(sense);
01140     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
01141 
01142     if (!device->scsi_pass_through(&io_hdr))
01143       return -device->get_errno();
01144     scsi_do_sense_disect(&io_hdr, &sinfo);
01145     return scsiSimpleSenseFilter(&sinfo);
01146 }
01147 
01148 /* READ CAPACITY (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
01149  * command not supported, 3 if field in command not supported or returns
01150  * negated errno. SBC-3 section 5.15 (rev 26) */
01151 int
01152 scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap,
01153                    unsigned int * lb_sizep)
01154 {
01155     int res;
01156     struct scsi_cmnd_io io_hdr;
01157     struct scsi_sense_disect sinfo;
01158     UINT8 cdb[10];
01159     UINT8 sense[32];
01160     UINT8 resp[8];
01161 
01162     memset(&io_hdr, 0, sizeof(io_hdr));
01163     memset(cdb, 0, sizeof(cdb));
01164     memset(resp, 0, sizeof(resp));
01165     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
01166     io_hdr.dxfer_len = sizeof(resp);
01167     io_hdr.dxferp = resp;
01168     cdb[0] = READ_CAPACITY_10;
01169     io_hdr.cmnd = cdb;
01170     io_hdr.cmnd_len = sizeof(cdb);
01171     io_hdr.sensep = sense;
01172     io_hdr.max_sense_len = sizeof(sense);
01173     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
01174 
01175     if (!device->scsi_pass_through(&io_hdr))
01176       return -device->get_errno();
01177     scsi_do_sense_disect(&io_hdr, &sinfo);
01178     res = scsiSimpleSenseFilter(&sinfo);
01179     if (res)
01180         return res;
01181     if (last_lbap)
01182         *last_lbap = (resp[0] << 24) | (resp[1] << 16) | (resp[2] << 8) |
01183                      resp[3];
01184     if (lb_sizep)
01185         *lb_sizep = (resp[4] << 24) | (resp[5] << 16) | (resp[6] << 8) |
01186                     resp[7];
01187     return 0;
01188 }
01189 
01190 /* READ CAPACITY (16) command. The bufLen argument should be 32. Returns 0
01191  * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command
01192  * not supported or returns negated errno. SBC-3 section 5.16 (rev 26) */
01193 int
01194 scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen)
01195 {
01196     struct scsi_cmnd_io io_hdr;
01197     struct scsi_sense_disect sinfo;
01198     UINT8 cdb[16];
01199     UINT8 sense[32];
01200 
01201     memset(&io_hdr, 0, sizeof(io_hdr));
01202     memset(cdb, 0, sizeof(cdb));
01203     io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
01204     io_hdr.dxfer_len = bufLen;
01205     io_hdr.dxferp = pBuf;
01206     cdb[0] = READ_CAPACITY_16;
01207     cdb[1] = SAI_READ_CAPACITY_16;
01208     cdb[10] = (bufLen >> 24) & 0xff;
01209     cdb[11] = (bufLen >> 16) & 0xff;
01210     cdb[12] = (bufLen >> 8) & 0xff;
01211     cdb[13] = bufLen & 0xff;
01212     io_hdr.cmnd = cdb;
01213     io_hdr.cmnd_len = sizeof(cdb);
01214     io_hdr.sensep = sense;
01215     io_hdr.max_sense_len = sizeof(sense);
01216     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
01217 
01218     if (!device->scsi_pass_through(&io_hdr))
01219       return -device->get_errno();
01220     scsi_do_sense_disect(&io_hdr, &sinfo);
01221     return scsiSimpleSenseFilter(&sinfo);
01222 }
01223 
01224 /* Return number of bytes of storage in 'device' or 0 if error. If
01225  * successful and lb_sizep is not NULL then the logical block size
01226  * in bytes is written to the location pointed to by lb_sizep. */
01227 uint64_t
01228 scsiGetSize(scsi_device * device, unsigned int * lb_sizep,
01229             int * lb_per_pb_expp)
01230 {
01231     unsigned int last_lba = 0, lb_size = 0;
01232     int k, res;
01233     uint64_t ret_val = 0;
01234     UINT8 rc16resp[32];
01235 
01236     res = scsiReadCapacity10(device, &last_lba, &lb_size);
01237     if (res) {
01238         if (scsi_debugmode)
01239             pout("scsiGetSize: READ CAPACITY(10) failed, res=%d\n", res);
01240         return 0;
01241     }
01242     if (0xffffffff == last_lba) {
01243         res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp));
01244         if (res) {
01245             if (scsi_debugmode)
01246                 pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);
01247             return 0;
01248         }
01249         for (k = 0; k < 8; ++k) {
01250             if (k > 0)
01251                 ret_val <<= 8;
01252             ret_val |= rc16resp[k + 0];
01253         }
01254         if (lb_per_pb_expp)
01255             *lb_per_pb_expp = (rc16resp[13] & 0xf);
01256     } else {
01257         ret_val = last_lba;
01258         if (lb_per_pb_expp)
01259             *lb_per_pb_expp = 0;
01260     }
01261     if (lb_sizep)
01262         *lb_sizep = lb_size;
01263     ++ret_val;  /* last_lba is origin 0 so need to bump to get number of */
01264     return ret_val * lb_size;
01265 }
01266 
01267 /* Gets drive Protection and Logical/Physical block information. Writes
01268  * back bytes 12 to 31 from a READ CAPACITY 16 command to the rc16_12_31p
01269  * pointer. So rc16_12_31p should point to an array of 20 bytes. Returns 0
01270  * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command
01271  * not supported or returns negated errno. */
01272 int
01273 scsiGetProtPBInfo(scsi_device * device, unsigned char * rc16_12_31p)
01274 {
01275     int res;
01276     UINT8 rc16resp[32];
01277 
01278     res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp));
01279     if (res) {
01280         if (scsi_debugmode)
01281             pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);
01282         return res;
01283     }
01284     if (rc16_12_31p)
01285         memcpy(rc16_12_31p, rc16resp + 12, 20);
01286     return 0;
01287 }
01288 
01289 /* Offset into mode sense (6 or 10 byte) response that actual mode page
01290  * starts at (relative to resp[0]). Returns -1 if problem */
01291 int
01292 scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
01293 {
01294     int resp_len, bd_len;
01295     int offset = -1;
01296 
01297     if (resp) {
01298         if (10 == modese_len) {
01299             resp_len = (resp[0] << 8) + resp[1] + 2;
01300             bd_len = (resp[6] << 8) + resp[7];
01301             offset = bd_len + 8;
01302         } else {
01303             resp_len = resp[0] + 1;
01304             bd_len = resp[3];
01305             offset = bd_len + 4;
01306         }
01307         if ((offset + 2) > len) {
01308             pout("scsiModePageOffset: raw_curr too small, offset=%d "
01309                  "resp_len=%d bd_len=%d\n", offset, resp_len, bd_len);
01310             offset = -1;
01311         } else if ((offset + 2) > resp_len) {
01312              if ((resp_len > 2) || scsi_debugmode)
01313                 pout("scsiModePageOffset: response length too short, "
01314                      "resp_len=%d offset=%d bd_len=%d\n", resp_len,
01315                      offset, bd_len);
01316             offset = -1;
01317         }
01318     }
01319     return offset;
01320 }
01321 
01322 /* IEC mode page byte 2 bit masks */
01323 #define DEXCPT_ENABLE   0x08
01324 #define EWASC_ENABLE    0x10
01325 #define DEXCPT_DISABLE  0xf7
01326 #define EWASC_DISABLE   0xef
01327 #define TEST_DISABLE    0xfb
01328 
01329 /* Fetches the Informational Exceptions Control mode page. First tries
01330  * the 6 byte MODE SENSE command and if that fails with an illegal opcode
01331  * tries a 10 byte MODE SENSE command. Returns 0 if successful, a positive
01332  * number if a known error (see  SIMPLE_ERR_ ...) or a negative errno
01333  * value. */
01334 int
01335 scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp,
01336                   int modese_len)
01337 {
01338     int err = 0;
01339 
01340     memset(iecp, 0, sizeof(*iecp));
01341     iecp->modese_len = modese_len;
01342     iecp->requestedCurrent = 1;
01343     if (iecp->modese_len <= 6) {
01344         if ((err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
01345                                  0, MPAGE_CONTROL_CURRENT,
01346                                  iecp->raw_curr, sizeof(iecp->raw_curr)))) {
01347             if (SIMPLE_ERR_BAD_OPCODE == err)
01348                 iecp->modese_len = 10;
01349             else {
01350                 iecp->modese_len = 0;
01351                 return err;
01352             }
01353         } else if (0 == iecp->modese_len)
01354             iecp->modese_len = 6;
01355     }
01356     if (10 == iecp->modese_len) {
01357         err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
01358                               0, MPAGE_CONTROL_CURRENT,
01359                               iecp->raw_curr, sizeof(iecp->raw_curr));
01360         if (err) {
01361             iecp->modese_len = 0;
01362             return err;
01363         }
01364     }
01365     iecp->gotCurrent = 1;
01366     iecp->requestedChangeable = 1;
01367     if (10 == iecp->modese_len)
01368         err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
01369                               0, MPAGE_CONTROL_CHANGEABLE,
01370                               iecp->raw_chg, sizeof(iecp->raw_chg));
01371     else if (6 == iecp->modese_len)
01372         err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
01373                             0, MPAGE_CONTROL_CHANGEABLE,
01374                             iecp->raw_chg, sizeof(iecp->raw_chg));
01375     if (err)
01376         return err;
01377     iecp->gotChangeable = 1;
01378     return 0;
01379 }
01380 
01381 int
01382 scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp)
01383 {
01384     int offset;
01385 
01386     if (iecp && iecp->gotCurrent) {
01387         offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
01388                                     iecp->modese_len);
01389         if (offset >= 0)
01390             return (iecp->raw_curr[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
01391         else
01392             return 0;
01393     } else
01394         return 0;
01395 }
01396 
01397 int
01398 scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp)
01399 {
01400     int offset;
01401 
01402     if (iecp && iecp->gotCurrent) {
01403         offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
01404                                     iecp->modese_len);
01405         if (offset >= 0)
01406             return (iecp->raw_curr[offset + 2] & EWASC_ENABLE) ? 1 : 0;
01407         else
01408             return 0;
01409     } else
01410         return 0;
01411 }
01412 
01413 /* set EWASC and clear PERF, EBF, DEXCPT TEST and LOGERR */
01414 #define SCSI_IEC_MP_BYTE2_ENABLED 0x10
01415 #define SCSI_IEC_MP_BYTE2_TEST_MASK 0x4
01416 /* exception/warning via an unrequested REQUEST SENSE command */
01417 #define SCSI_IEC_MP_MRIE 6
01418 #define SCSI_IEC_MP_INTERVAL_T 0
01419 #define SCSI_IEC_MP_REPORT_COUNT 1
01420 
01421 /* Try to set (or clear) both Exception Control and Warning in the IE
01422  * mode page subject to the "changeable" mask. The object pointed to
01423  * by iecp is (possibly) inaccurate after this call, therefore
01424  * scsiFetchIECmpage() should be called again if the IEC mode page
01425  * is to be re-examined.
01426  * When -r ioctl is invoked 3 or more time on 'smartctl -s on ...'
01427  * then set the TEST bit (causes asc,ascq pair of 0x5d,0xff). */
01428 int
01429 scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
01430                                   const struct scsi_iec_mode_page *iecp)
01431 {
01432     int k, offset, resp_len;
01433     int err = 0;
01434     UINT8 rout[SCSI_IECMP_RAW_LEN];
01435     int sp, eCEnabled, wEnabled;
01436 
01437     if ((! iecp) || (! iecp->gotCurrent))
01438         return -EINVAL;
01439     offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
01440                                 iecp->modese_len);
01441     if (offset < 0)
01442         return -EINVAL;
01443     memcpy(rout, iecp->raw_curr, SCSI_IECMP_RAW_LEN);
01444     /* mask out DPOFUA device specific (disk) parameter bit */
01445     if (10 == iecp->modese_len) {
01446         resp_len = (rout[0] << 8) + rout[1] + 2;
01447         rout[3] &= 0xef;
01448     } else {
01449         resp_len = rout[0] + 1;
01450         rout[2] &= 0xef;
01451     }
01452     sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
01453     if (enabled) {
01454         rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED;
01455         if (scsi_debugmode > 2)
01456             rout[offset + 2] |= SCSI_IEC_MP_BYTE2_TEST_MASK;
01457         rout[offset + 3] = SCSI_IEC_MP_MRIE;
01458         rout[offset + 4] = (SCSI_IEC_MP_INTERVAL_T >> 24) & 0xff;
01459         rout[offset + 5] = (SCSI_IEC_MP_INTERVAL_T >> 16) & 0xff;
01460         rout[offset + 6] = (SCSI_IEC_MP_INTERVAL_T >> 8) & 0xff;
01461         rout[offset + 7] = SCSI_IEC_MP_INTERVAL_T & 0xff;
01462         rout[offset + 8] = (SCSI_IEC_MP_REPORT_COUNT >> 24) & 0xff;
01463         rout[offset + 9] = (SCSI_IEC_MP_REPORT_COUNT >> 16) & 0xff;
01464         rout[offset + 10] = (SCSI_IEC_MP_REPORT_COUNT >> 8) & 0xff;
01465         rout[offset + 11] = SCSI_IEC_MP_REPORT_COUNT & 0xff;
01466         if (iecp->gotChangeable) {
01467             UINT8 chg2 = iecp->raw_chg[offset + 2];
01468 
01469             rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) :
01470                                       iecp->raw_curr[offset + 2];
01471             for (k = 3; k < 12; ++k) {
01472                 if (0 == iecp->raw_chg[offset + k])
01473                     rout[offset + k] = iecp->raw_curr[offset + k];
01474             }
01475         }
01476         if (0 == memcmp(&rout[offset + 2], &iecp->raw_chg[offset + 2], 10)) {
01477             if (scsi_debugmode > 0)
01478                 pout("scsiSetExceptionControlAndWarning: already enabled\n");
01479             return 0;
01480         }
01481     } else { /* disabling Exception Control and (temperature) Warnings */
01482         eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
01483         wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0;
01484         if ((! eCEnabled) && (! wEnabled)) {
01485             if (scsi_debugmode > 0)
01486                 pout("scsiSetExceptionControlAndWarning: already disabled\n");
01487             return 0;   /* nothing to do, leave other setting alone */
01488         }
01489         if (wEnabled)
01490             rout[offset + 2] &= EWASC_DISABLE;
01491         if (eCEnabled) {
01492             if (iecp->gotChangeable &&
01493                 (iecp->raw_chg[offset + 2] & DEXCPT_ENABLE))
01494                 rout[offset + 2] |= DEXCPT_ENABLE;
01495                 rout[offset + 2] &= TEST_DISABLE;/* clear TEST bit for spec */
01496         }
01497     }
01498     if (10 == iecp->modese_len)
01499         err = scsiModeSelect10(device, sp, rout, resp_len);
01500     else if (6 == iecp->modese_len)
01501         err = scsiModeSelect(device, sp, rout, resp_len);
01502     return err;
01503 }
01504 
01505 int
01506 scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp)
01507 {
01508     UINT8 tBuf[252];
01509     int err;
01510 
01511     memset(tBuf, 0, sizeof(tBuf));
01512     if ((err = scsiLogSense(device, TEMPERATURE_LPAGE, 0, tBuf,
01513                             sizeof(tBuf), 0))) {
01514         *currenttemp = 0;
01515         *triptemp = 0;
01516         pout("Log Sense for temperature failed [%s]\n", scsiErrString(err));
01517         return err;
01518     }
01519     *currenttemp = tBuf[9];
01520     *triptemp = tBuf[15];
01521     return 0;
01522 }
01523 
01524 /* Read informational exception log page or Request Sense response.
01525  * Fetching asc/ascq code potentially flagging an exception or warning.
01526  * Returns 0 if ok, else error number. A current temperature of 255
01527  * (Celsius) implies that the temperature not available. */
01528 int
01529 scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
01530             UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp)
01531 {
01532     UINT8 tBuf[252];
01533     struct scsi_sense_disect sense_info;
01534     int err;
01535     int temperatureSet = 0;
01536     unsigned short pagesize;
01537     UINT8 currTemp, trTemp;
01538 
01539     *asc = 0;
01540     *ascq = 0;
01541     *currenttemp = 0;
01542     *triptemp = 0;
01543     memset(tBuf,0,sizeof(tBuf)); // need to clear stack space of junk
01544     memset(&sense_info, 0, sizeof(sense_info));
01545     if (hasIELogPage) {
01546         if ((err = scsiLogSense(device, IE_LPAGE, 0, tBuf,
01547                                 sizeof(tBuf), 0))) {
01548             pout("Log Sense failed, IE page [%s]\n", scsiErrString(err));
01549             return err;
01550         }
01551         // pull out page size from response, don't forget to add 4
01552         pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4;
01553         if ((pagesize < 4) || tBuf[4] || tBuf[5]) {
01554             pout("Log Sense failed, IE page, bad parameter code or length\n");
01555             return SIMPLE_ERR_BAD_PARAM;
01556         }
01557         if (tBuf[7] > 1) {
01558             sense_info.asc = tBuf[8];
01559             sense_info.ascq = tBuf[9];
01560             if (! hasTempLogPage) {
01561                 if (tBuf[7] > 2)
01562                     *currenttemp = tBuf[10];
01563                 if (tBuf[7] > 3)        /* IBM extension in SMART (IE) lpage */
01564                     *triptemp = tBuf[11];
01565             }
01566         }
01567     }
01568     if (0 == sense_info.asc) {
01569         /* ties in with MRIE field of 6 in IEC mode page (0x1c) */
01570         if ((err = scsiRequestSense(device, &sense_info))) {
01571             pout("Request Sense failed, [%s]\n", scsiErrString(err));
01572             return err;
01573         }
01574     }
01575     *asc = sense_info.asc;
01576     *ascq = sense_info.ascq;
01577     if ((! temperatureSet) && hasTempLogPage) {
01578         if (0 == scsiGetTemp(device, &currTemp, &trTemp)) {
01579             *currenttemp = currTemp;
01580             *triptemp = trTemp;
01581         }
01582     }
01583     return 0;
01584 }
01585 
01586 // The first character (W, C, I) tells the severity
01587 static const char * TapeAlertsMessageTable[]= {
01588     " ",
01589     /* 0x01 */
01590    "W: The tape drive is having problems reading data. No data has been lost,\n"
01591        "  but there has been a reduction in the performance of the tape.",
01592     /* 0x02 */
01593    "W: The tape drive is having problems writing data. No data has been lost,\n"
01594        "  but there has been a reduction in the capacity of the tape.",
01595     /* 0x03 */
01596    "W: The operation has stopped because an error has occurred while reading\n"
01597        "  or writing data that the drive cannot correct.",
01598     /* 0x04 */
01599    "C: Your data is at risk:\n"
01600        "  1. Copy any data you require from this tape. \n"
01601        "  2. Do not use this tape again.\n"
01602        "  3. Restart the operation with a different tape.",
01603     /* 0x05 */
01604    "C: The tape is damaged or the drive is faulty. Call the tape drive\n"
01605        "  supplier helpline.",
01606     /* 0x06 */
01607    "C: The tape is from a faulty batch or the tape drive is faulty:\n"
01608        "  1. Use a good tape to test the drive.\n"
01609        "  2. If problem persists, call the tape drive supplier helpline.",
01610     /* 0x07 */
01611    "W: The tape cartridge has reached the end of its calculated useful life:\n"
01612        "  1. Copy data you need to another tape.\n"
01613        "  2. Discard the old tape.",
01614     /* 0x08 */
01615    "W: The tape cartridge is not data-grade. Any data you back up to the tape\n"
01616        "  is at risk. Replace the cartridge with a data-grade tape.",
01617     /* 0x09 */
01618    "C: You are trying to write to a write-protected cartridge. Remove the\n"
01619        "  write-protection or use another tape.",
01620     /* 0x0a */
01621    "I: You cannot eject the cartridge because the tape drive is in use. Wait\n"
01622        "  until the operation is complete before ejecting the cartridge.",
01623     /* 0x0b */
01624    "I: The tape in the drive is a cleaning cartridge.",
01625     /* 0x0c */
01626    "I: You have tried to load a cartridge of a type which is not supported\n"
01627        "  by this drive.",
01628     /* 0x0d */
01629    "C: The operation has failed because the tape in the drive has experienced\n"
01630        "  a mechanical failure:\n"
01631        "  1. Discard the old tape.\n"
01632        "  2. Restart the operation with a different tape.",
01633     /* 0x0e */
01634    "C: The operation has failed because the tape in the drive has experienced\n"
01635        "  a mechanical failure:\n"
01636        "  1. Do not attempt to extract the tape cartridge\n"
01637        "  2. Call the tape drive supplier helpline.",
01638     /* 0x0f */
01639    "W: The memory in the tape cartridge has failed, which reduces\n"
01640        "  performance. Do not use the cartridge for further write operations.",
01641     /* 0x10 */
01642    "C: The operation has failed because the tape cartridge was manually\n"
01643        "  de-mounted while the tape drive was actively writing or reading.",
01644     /* 0x11 */
01645    "W: You have loaded a cartridge of a type that is read-only in this drive.\n"
01646        "  The cartridge will appear as write-protected.",
01647     /* 0x12 */
01648    "W: The tape directory on the tape cartridge has been corrupted. File\n"
01649        "  search performance will be degraded. The tape directory can be rebuilt\n"
01650        "  by reading all the data on the cartridge.",
01651     /* 0x13 */
01652    "I: The tape cartridge is nearing the end of its calculated life. It is\n"
01653        "  recommended that you:\n"
01654        "  1. Use another tape cartridge for your next backup.\n"
01655        "  2. Store this tape in a safe place in case you need to restore "
01656        "  data from it.",
01657     /* 0x14 */
01658    "C: The tape drive needs cleaning:\n"
01659        "  1. If the operation has stopped, eject the tape and clean the drive.\n"
01660        "  2. If the operation has not stopped, wait for it to finish and then\n"
01661        "  clean the drive.\n"
01662        "  Check the tape drive users manual for device specific cleaning instructions.",
01663     /* 0x15 */
01664    "W: The tape drive is due for routine cleaning:\n"
01665        "  1. Wait for the current operation to finish.\n"
01666        "  2. The use a cleaning cartridge.\n"
01667        "  Check the tape drive users manual for device specific cleaning instructions.",
01668     /* 0x16 */
01669    "C: The last cleaning cartridge used in the tape drive has worn out:\n"
01670        "  1. Discard the worn out cleaning cartridge.\n"
01671        "  2. Wait for the current operation to finish.\n"
01672        "  3. Then use a new cleaning cartridge.",
01673     /* 0x17 */
01674    "C: The last cleaning cartridge used in the tape drive was an invalid\n"
01675        "  type:\n"
01676        "  1. Do not use this cleaning cartridge in this drive.\n"
01677        "  2. Wait for the current operation to finish.\n"
01678        "  3. Then use a new cleaning cartridge.",
01679     /* 0x18 */
01680    "W: The tape drive has requested a retention operation",
01681     /* 0x19 */
01682    "W: A redundant interface port on the tape drive has failed",
01683     /* 0x1a */
01684    "W: A tape drive cooling fan has failed",
01685     /* 0x1b */
01686    "W: A redundant power supply has failed inside the tape drive enclosure.\n"
01687        "  Check the enclosure users manual for instructions on replacing the\n"
01688        "  failed power supply.",
01689     /* 0x1c */
01690    "W: The tape drive power consumption is outside the specified range.",
01691     /* 0x1d */
01692    "W: Preventive maintenance of the tape drive is required. Check the tape\n"
01693        "  drive users manual for device specific preventive maintenance\n"
01694        "  tasks or call the tape drive supplier helpline.",
01695     /* 0x1e */
01696    "C: The tape drive has a hardware fault:\n"
01697        "  1. Eject the tape or magazine.\n"
01698        "  2. Reset the drive.\n"
01699        "  3. Restart the operation.",
01700     /* 0x1f */
01701    "C: The tape drive has a hardware fault:\n"
01702        "  1. Turn the tape drive off and then on again.\n"
01703        "  2. Restart the operation.\n"
01704     "  3. If the problem persists, call the tape drive supplier helpline.",
01705     /* 0x20 */
01706    "W: The tape drive has a problem with the application client interface:\n"
01707        "  1. Check the cables and cable connections.\n"
01708        "  2. Restart the operation.",
01709     /* 0x21 */
01710    "C: The operation has failed:\n"
01711        "  1. Eject the tape or magazine.\n"
01712        "  2. Insert the tape or magazine again.\n"
01713        "  3. Restart the operation.",
01714     /* 0x22 */
01715    "W: The firmware download has failed because you have tried to use the\n"
01716        "  incorrect firmware for this tape drive. Obtain the correct\n"
01717        "  firmware and try again.",
01718     /* 0x23 */
01719    "W: Environmental conditions inside the tape drive are outside the\n"
01720        "  specified humidity range.",
01721     /* 0x24 */
01722    "W: Environmental conditions inside the tape drive are outside the\n"
01723        "  specified temperature range.",
01724     /* 0x25 */
01725    "W: The voltage supply to the tape drive is outside the specified range.",
01726     /* 0x26 */
01727    "C: A hardware failure of the tape drive is predicted. Call the tape\n"
01728        "  drive supplier helpline.",
01729     /* 0x27 */
01730    "W: The tape drive may have a hardware fault. Run extended diagnostics to\n"
01731        "  verify and diagnose the problem. Check the tape drive users manual for\n"
01732        "  device specific instructions on running extended diagnostic tests.",
01733     /* 0x28 */
01734    "C: The changer mechanism is having difficulty communicating with the tape\n"
01735        "  drive:\n"
01736        "  1. Turn the autoloader off then on.\n"
01737        "  2. Restart the operation.\n"
01738        "  3. If problem persists, call the tape drive supplier helpline.",
01739     /* 0x29 */
01740    "C: A tape has been left in the autoloader by a previous hardware fault:\n"
01741        "  1. Insert an empty magazine to clear the fault.\n"
01742        "  2. If the fault does not clear, turn the autoloader off and then\n"
01743        "  on again.\n"
01744        "  3. If the problem persists, call the tape drive supplier helpline.",
01745     /* 0x2a */
01746    "W: There is a problem with the autoloader mechanism.",
01747     /* 0x2b */
01748    "C: The operation has failed because the autoloader door is open:\n"
01749        "  1. Clear any obstructions from the autoloader door.\n"
01750        "  2. Eject the magazine and then insert it again.\n"
01751        "  3. If the fault does not clear, turn the autoloader off and then\n"
01752        "  on again.\n"
01753        "  4. If the problem persists, call the tape drive supplier helpline.",
01754     /* 0x2c */
01755    "C: The autoloader has a hardware fault:\n"
01756        "  1. Turn the autoloader off and then on again.\n"
01757        "  2. Restart the operation.\n"
01758        "  3. If the problem persists, call the tape drive supplier helpline.\n"
01759        "  Check the autoloader users manual for device specific instructions\n"
01760        "  on turning the device power on and off.",
01761     /* 0x2d */
01762    "C: The autoloader cannot operate without the magazine,\n"
01763        "  1. Insert the magazine into the autoloader.\n"
01764        "  2. Restart the operation.",
01765     /* 0x2e */
01766    "W: A hardware failure of the changer mechanism is predicted. Call the\n"
01767        "  tape drive supplier helpline.",
01768     /* 0x2f */
01769    "I: Reserved.",
01770     /* 0x30 */
01771    "I: Reserved.",
01772     /* 0x31 */
01773    "I: Reserved.",
01774     /* 0x32 */
01775    "W: Media statistics have been lost at some time in the past",
01776     /* 0x33 */
01777    "W: The tape directory on the tape cartridge just unloaded has been\n"
01778        "  corrupted. File search performance will be degraded. The tape\n"
01779        "  directory can be rebuilt by reading all the data.",
01780     /* 0x34 */
01781    "C: The tape just unloaded could not write its system area successfully:\n"
01782        "  1. Copy data to another tape cartridge.\n"
01783        "  2. Discard the old cartridge.",
01784     /* 0x35 */
01785    "C: The tape system are could not be read successfully at load time:\n"
01786     "  1. Copy data to another tape cartridge.\n",
01787     /* 0x36 */
01788    "C: The start or data could not be found on the tape:\n"
01789        "  1. Check you are using the correct format tape.\n"
01790        "  2. Discard the tape or return the tape to your supplier",
01791     /* 0x37 */
01792     "C: The operation has failed because the media cannot be loaded\n"
01793         "  and threaded.\n"
01794         "  1. Remove the cartridge, inspect it as specified in the product\n"
01795         "  manual, and retry the operation.\n"
01796         "  2. If the problem persists, call the tape drive supplier help line.",
01797     /* 0x38 */
01798     "C: The operation has failed because the medium cannot be unloaded:\n"
01799         "  1. Do not attempt to extract the tape cartridge.\n"
01800         "  2. Call the tape driver supplier help line.",
01801     /* 0x39 */
01802     "C: The tape drive has a problem with the automation interface:\n"
01803         "  1. Check the power to the automation system.\n"
01804         "  2. Check the cables and cable connections.\n"
01805         "  3. Call the supplier help line if problem persists.",
01806     /* 0x3a */
01807     "W: The tape drive has reset itself due to a detected firmware\n"
01808         "  fault. If problem persists, call the supplier help line.",
01809     };
01810 
01811 const char *
01812 scsiTapeAlertsTapeDevice(unsigned short code)
01813 {
01814     const int num = sizeof(TapeAlertsMessageTable) /
01815                         sizeof(TapeAlertsMessageTable[0]);
01816 
01817     return (code < num) ?  TapeAlertsMessageTable[code] : "Unknown Alert";
01818 }
01819 
01820 // The first character (W, C, I) tells the severity
01821 static const char * ChangerTapeAlertsMessageTable[]= {
01822     " ",
01823     /* 0x01 */
01824     "C: The library mechanism is having difficulty communicating with the\n"
01825         "  drive:\n"
01826         "  1. Turn the library off then on.\n"
01827         "  2. Restart the operation.\n"
01828         "  3. If the problem persists, call the library supplier help line.",
01829     /* 0x02 */
01830     "W: There is a problem with the library mechanism. If problem persists,\n"
01831         "  call the library supplier help line.",
01832     /* 0x03 */
01833     "C: The library has a hardware fault:\n"
01834         "  1. Reset the library.\n"
01835         "  2. Restart the operation.\n"
01836         "  Check the library users manual for device specific instructions on resetting\n"
01837         "  the device.",
01838     /* 0x04 */
01839     "C: The library has a hardware fault:\n"
01840         "  1. Turn the library off then on again.\n"
01841         "  2. Restart the operation.\n"
01842         "  3. If the problem persists, call the library supplier help line.\n"
01843         "  Check the library users manual for device specific instructions on turning the\n"
01844         "  device power on and off.",
01845     /* 0x05 */
01846     "W: The library mechanism may have a hardware fault.\n"
01847         "  Run extended diagnostics to verify and diagnose the problem. Check the library\n"
01848         "  users manual for device specific instructions on running extended diagnostic\n"
01849         "  tests.",
01850     /* 0x06 */
01851     "C: The library has a problem with the host interface:\n"
01852         "  1. Check the cables and connections.\n"
01853         "  2. Restart the operation.",
01854     /* 0x07 */
01855     "W: A hardware failure of the library is predicted. Call the library\n"
01856         "  supplier help line.",
01857     /* 0x08 */
01858     "W: Preventive maintenance of the library is required.\n"
01859         "  Check the library users manual for device specific preventative maintenance\n"
01860         "  tasks, or call your library supplier help line.",
01861     /* 0x09 */
01862     "C: General environmental conditions inside the library are outside the\n"
01863         "  specified humidity range.",
01864     /* 0x0a */
01865     "C: General environmental conditions inside the library are outside the\n"
01866         "  specified temperature range.",
01867     /* 0x0b */
01868     "C: The voltage supply to the library is outside the specified range.\n"
01869         "  There is a potential problem with the power supply or failure of\n"
01870         "  a redundant power supply.",
01871     /* 0x0c */
01872     "C: A cartridge has been left inside the library by a previous hardware\n"
01873         "  fault:\n"
01874         "  1. Insert an empty magazine to clear the fault.\n"
01875         "  2. If the fault does not clear, turn the library off and then on again.\n"
01876         "  3. If the problem persists, call the library supplier help line.",
01877     /* 0x0d */
01878     "W: There is a potential problem with the drive ejecting cartridges or\n"
01879         "  with the library mechanism picking a cartridge from a slot.\n"
01880         "  1. No action needs to be taken at this time.\n"
01881         "  2. If the problem persists, call the library supplier help line.",
01882     /* 0x0e */
01883     "W: There is a potential problem with the library mechanism placing a\n"
01884         "  cartridge into a slot.\n"
01885         "  1. No action needs to be taken at this time.\n"
01886         "  2. If the problem persists, call the library supplier help line.",
01887     /* 0x0f */
01888     "W: There is a potential problem with the drive or the library mechanism\n"
01889         "  loading cartridges, or an incompatible cartridge.",
01890     /* 0x10 */
01891     "C: The library has failed because the door is open:\n"
01892         "  1. Clear any obstructions from the library door.\n"
01893         "  2. Close the library door.\n"
01894         "  3. If the problem persists, call the library supplier help line.",
01895     /* 0x11 */
01896     "C: There is a mechanical problem with the library media import/export\n"
01897         "  mailslot.",
01898     /* 0x12 */
01899     "C: The library cannot operate without the magazine.\n"
01900         "  1. Insert the magazine into the library.\n"
01901         "  2. Restart the operation.",
01902     /* 0x13 */
01903     "W: Library security has been compromised.",
01904     /* 0x14 */
01905     "I: The library security mode has been changed.\n"
01906         "  The library has either been put into secure mode, or the library has exited\n"
01907         "  the secure mode.\n"
01908         "  This is for information purposes only. No action is required.",
01909     /* 0x15 */
01910     "I: The library has been manually turned offline and is unavailable for use.",
01911     /* 0x16 */
01912     "I: A drive inside the library has been taken offline.\n"
01913         "  This is for information purposes only. No action is required.",
01914     /* 0x17 */
01915     "W: There is a potential problem with the bar code label or the scanner\n"
01916         "  hardware in the library mechanism.\n"
01917         "  1. No action needs to be taken at this time.\n"
01918         "  2. If the problem persists, call the library supplier help line.",
01919     /* 0x18 */
01920     "C: The library has detected an inconsistency in its inventory.\n"
01921         "  1. Redo the library inventory to correct inconsistency.\n"
01922         "  2. Restart the operation.\n"
01923         "  Check the applications users manual or the hardware users manual for\n"
01924         "  specific instructions on redoing the library inventory.",
01925     /* 0x19 */
01926     "W: A library operation has been attempted that is invalid at this time.",
01927     /* 0x1a */
01928     "W: A redundant interface port on the library has failed.",
01929     /* 0x1b */
01930     "W: A library cooling fan has failed.",
01931     /* 0x1c */
01932     "W: A redundant power supply has failed inside the library. Check the\n"
01933         "  library users manual for instructions on replacing the failed power supply.",
01934     /* 0x1d */
01935     "W: The library power consumption is outside the specified range.",
01936     /* 0x1e */
01937     "C: A failure has occurred in the cartridge pass-through mechanism between\n"
01938         "  two library modules.",
01939     /* 0x1f */
01940     "C: A cartridge has been left in the pass-through mechanism from a\n"
01941         "  previous hardware fault. Check the library users guide for instructions on\n"
01942         "  clearing this fault.",
01943     /* 0x20 */
01944     "I: The library was unable to read the bar code on a cartridge.",
01945 };
01946 
01947 const char *
01948 scsiTapeAlertsChangerDevice(unsigned short code)
01949 {
01950     const int num = sizeof(ChangerTapeAlertsMessageTable) /
01951                         sizeof(ChangerTapeAlertsMessageTable[0]);
01952 
01953     return (code < num) ?  ChangerTapeAlertsMessageTable[code] : "Unknown Alert";
01954 }
01955 
01956 
01957 /* this is a subset of the SCSI additional sense code strings indexed
01958  * by "ascq" for the case when asc==SCSI_ASC_IMPENDING_FAILURE (0x5d)
01959  */
01960 static const char * strs_for_asc_5d[] = {
01961    /* 0x00 */   "FAILURE PREDICTION THRESHOLD EXCEEDED",
01962         "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED",
01963         "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED",
01964         "SPARE AREA EXHAUSTION PREDICTION THRESHOLD EXCEEDED",
01965         "",
01966         "",
01967         "",
01968         "",
01969         "",
01970         "",
01971         "",
01972         "",
01973         "",
01974         "",
01975         "",
01976         "",
01977    /* 0x10 */   "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
01978         "HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
01979         "HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
01980         "HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
01981         "HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
01982         "HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
01983         "HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
01984         "HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
01985         "HARDWARE IMPENDING FAILURE CONTROLLER DETECTED",
01986         "HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
01987         "HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
01988         "HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
01989         "HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
01990         "",
01991         "",
01992         "",
01993    /* 0x20 */   "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
01994         "CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
01995         "CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
01996         "CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
01997         "CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
01998         "CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH",
01999         "CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH",
02000         "CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS",
02001         "CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED",
02002         "CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE",
02003         "CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE",
02004         "CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT",
02005         "CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
02006         "",
02007         "",
02008         "",
02009    /* 0x30 */   "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
02010         "DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
02011         "DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
02012         "DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
02013         "DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
02014         "DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH",
02015         "DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH",
02016         "DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS",
02017         "DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED",
02018         "DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE",
02019         "DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE",
02020         "DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT",
02021         "DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
02022         "",
02023         "",
02024         "",
02025    /* 0x40 */   "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
02026         "SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
02027         "SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
02028         "SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
02029         "SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
02030         "SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH",
02031         "SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH",
02032         "SERVO IMPENDING FAILURE CHANNEL PARAMETRICS",
02033         "SERVO IMPENDING FAILURE CONTROLLER DETECTED",
02034         "SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE",
02035         "SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE",
02036         "SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT",
02037         "SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
02038         "",
02039         "",
02040         "",
02041    /* 0x50 */   "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
02042         "SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
02043         "SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
02044         "SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
02045         "SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
02046         "SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
02047         "SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
02048         "SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS",
02049         "SPINDLE IMPENDING FAILURE CONTROLLER DETECTED",
02050         "SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
02051         "SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE",
02052         "SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT",
02053         "SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
02054         "",
02055         "",
02056         "",
02057    /* 0x60 */   "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
02058         "FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
02059         "FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
02060         "FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
02061         "FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
02062         "FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
02063         "FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
02064         "FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
02065         "FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED",
02066         "FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
02067         "FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
02068         "FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
02069    /* 0x6c */   "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT"};
02070 
02071 
02072 /* this is a subset of the SCSI additional sense code strings indexed
02073  *  * by "ascq" for the case when asc==SCSI_ASC_WARNING (0xb)
02074  *   */
02075 static const char * strs_for_asc_b[] = {
02076        /* 0x00 */   "WARNING",
02077                "WARNING - SPECIFIED TEMPERATURE EXCEEDED",
02078                "WARNING - ENCLOSURE DEGRADED"};
02079 
02080 static char spare_buff[128];
02081 
02082 const char *
02083 scsiGetIEString(UINT8 asc, UINT8 ascq)
02084 {
02085     const char * rp;
02086 
02087     if (SCSI_ASC_IMPENDING_FAILURE == asc) {
02088         if (ascq == 0xff)
02089             return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)";
02090         else if (ascq <
02091                  (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) {
02092             rp = strs_for_asc_5d[ascq];
02093             if (strlen(rp) > 0)
02094                 return rp;
02095         }
02096         snprintf(spare_buff, sizeof(spare_buff),
02097                  "FAILURE PREDICTION THRESHOLD EXCEEDED: ascq=0x%x", ascq);
02098         return spare_buff;
02099     } else if (SCSI_ASC_WARNING == asc) {
02100         if (ascq < (sizeof(strs_for_asc_b) / sizeof(strs_for_asc_b[0]))) {
02101             rp = strs_for_asc_b[ascq];
02102             if (strlen(rp) > 0)
02103                 return rp;
02104         }
02105         snprintf(spare_buff, sizeof(spare_buff), "WARNING: ascq=0x%x", ascq);
02106         return spare_buff;
02107     }
02108     return NULL;        /* not a IE additional sense code */
02109 }
02110 
02111 
02112 /* This is not documented in t10.org, page 0x80 is vendor specific */
02113 /* Some IBM disks do an offline read-scan when they get this command. */
02114 int
02115 scsiSmartIBMOfflineTest(scsi_device * device)
02116 {
02117     UINT8 tBuf[256];
02118     int res;
02119 
02120     memset(tBuf, 0, sizeof(tBuf));
02121     /* Build SMART Off-line Immediate Diag Header */
02122     tBuf[0] = 0x80; /* Page Code */
02123     tBuf[1] = 0x00; /* Reserved */
02124     tBuf[2] = 0x00; /* Page Length MSB */
02125     tBuf[3] = 0x04; /* Page Length LSB */
02126     tBuf[4] = 0x03; /* SMART Revision */
02127     tBuf[5] = 0x00; /* Reserved */
02128     tBuf[6] = 0x00; /* Off-line Immediate Time MSB */
02129     tBuf[7] = 0x00; /* Off-line Immediate Time LSB */
02130     res = scsiSendDiagnostic(device, SCSI_DIAG_NO_SELF_TEST, tBuf, 8);
02131     if (res)
02132         pout("IBM offline test failed [%s]\n", scsiErrString(res));
02133     return res;
02134 }
02135 
02136 int
02137 scsiSmartDefaultSelfTest(scsi_device * device)
02138 {
02139     int res;
02140 
02141     res = scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0);
02142     if (res)
02143         pout("Default self test failed [%s]\n", scsiErrString(res));
02144     return res;
02145 }
02146 
02147 int
02148 scsiSmartShortSelfTest(scsi_device * device)
02149 {
02150     int res;
02151 
02152     res = scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0);
02153     if (res)
02154         pout("Short offline self test failed [%s]\n", scsiErrString(res));
02155     return res;
02156 }
02157 
02158 int
02159 scsiSmartExtendSelfTest(scsi_device * device)
02160 {
02161     int res;
02162 
02163     res = scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, NULL, 0);
02164     if (res)
02165         pout("Long (extended) offline self test failed [%s]\n",
02166              scsiErrString(res));
02167     return res;
02168 }
02169 
02170 int
02171 scsiSmartShortCapSelfTest(scsi_device * device)
02172 {
02173     int res;
02174 
02175     res = scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0);
02176     if (res)
02177         pout("Short foreground self test failed [%s]\n", scsiErrString(res));
02178     return res;
02179 }
02180 
02181 int
02182 scsiSmartExtendCapSelfTest(scsi_device * device)
02183 {
02184     int res;
02185 
02186     res = scsiSendDiagnostic(device, SCSI_DIAG_FG_EXTENDED_SELF_TEST, NULL, 0);
02187     if (res)
02188         pout("Long (extended) foreground self test failed [%s]\n",
02189              scsiErrString(res));
02190     return res;
02191 }
02192 
02193 int
02194 scsiSmartSelfTestAbort(scsi_device * device)
02195 {
02196     int res;
02197 
02198     res = scsiSendDiagnostic(device, SCSI_DIAG_ABORT_SELF_TEST, NULL, 0);
02199     if (res)
02200         pout("Abort self test failed [%s]\n", scsiErrString(res));
02201     return res;
02202 }
02203 
02204 /* Returns 0 and the expected duration of an extended self test (in seconds)
02205    if successful; any other return value indicates a failure. */
02206 int
02207 scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
02208                               int modese_len)
02209 {
02210     int err, offset, res;
02211     UINT8 buff[64];
02212 
02213     memset(buff, 0, sizeof(buff));
02214     if (modese_len <= 6) {
02215         if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
02216                                  MPAGE_CONTROL_CURRENT,
02217                                  buff, sizeof(buff)))) {
02218             if (SIMPLE_ERR_BAD_OPCODE == err)
02219                 modese_len = 10;
02220             else
02221                 return err;
02222         } else if (0 == modese_len)
02223             modese_len = 6;
02224     }
02225     if (10 == modese_len) {
02226         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
02227                               MPAGE_CONTROL_CURRENT,
02228                               buff, sizeof(buff));
02229         if (err)
02230             return err;
02231     }
02232     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02233     if (offset < 0)
02234         return -EINVAL;
02235     if (buff[offset + 1] >= 0xa) {
02236         res = (buff[offset + 10] << 8) | buff[offset + 11];
02237         *durationSec = res;
02238         return 0;
02239     }
02240     else
02241         return -EINVAL;
02242 }
02243 
02244 void
02245 scsiDecodeErrCounterPage(unsigned char * resp, struct scsiErrorCounter *ecp)
02246 {
02247     int k, j, num, pl, pc;
02248     unsigned char * ucp;
02249     unsigned char * xp;
02250     uint64_t * ullp;
02251 
02252     memset(ecp, 0, sizeof(*ecp));
02253     num = (resp[2] << 8) | resp[3];
02254     ucp = &resp[0] + 4;
02255     while (num > 3) {
02256         pc = (ucp[0] << 8) | ucp[1];
02257         pl = ucp[3] + 4;
02258         switch (pc) {
02259             case 0:
02260             case 1:
02261             case 2:
02262             case 3:
02263             case 4:
02264             case 5:
02265             case 6:
02266                 ecp->gotPC[pc] = 1;
02267                 ullp = &ecp->counter[pc];
02268                 break;
02269         default:
02270                 ecp->gotExtraPC = 1;
02271                 ullp = &ecp->counter[7];
02272                 break;
02273         }
02274         k = pl - 4;
02275         xp = ucp + 4;
02276         if (k > (int)sizeof(*ullp)) {
02277             xp += (k - sizeof(*ullp));
02278             k = sizeof(*ullp);
02279         }
02280         *ullp = 0;
02281         for (j = 0; j < k; ++j) {
02282             if (j > 0)
02283                 *ullp <<= 8;
02284             *ullp |= xp[j];
02285         }
02286         num -= pl;
02287         ucp += pl;
02288     }
02289 }
02290 
02291 void
02292 scsiDecodeNonMediumErrPage(unsigned char *resp,
02293                            struct scsiNonMediumError *nmep)
02294 {
02295     int k, j, num, pl, pc, szof;
02296     unsigned char * ucp;
02297     unsigned char * xp;
02298 
02299     memset(nmep, 0, sizeof(*nmep));
02300     num = (resp[2] << 8) | resp[3];
02301     ucp = &resp[0] + 4;
02302     szof = sizeof(nmep->counterPC0);
02303     while (num > 3) {
02304         pc = (ucp[0] << 8) | ucp[1];
02305         pl = ucp[3] + 4;
02306         switch (pc) {
02307             case 0:
02308                 nmep->gotPC0 = 1;
02309                 k = pl - 4;
02310                 xp = ucp + 4;
02311                 if (k > szof) {
02312                     xp += (k - szof);
02313                     k = szof;
02314                 }
02315                 nmep->counterPC0 = 0;
02316                 for (j = 0; j < k; ++j) {
02317                     if (j > 0)
02318                         nmep->counterPC0 <<= 8;
02319                     nmep->counterPC0 |= xp[j];
02320                 }
02321                 break;
02322             case 0x8009:
02323                 nmep->gotTFE_H = 1;
02324                 k = pl - 4;
02325                 xp = ucp + 4;
02326                 if (k > szof) {
02327                     xp += (k - szof);
02328                     k = szof;
02329                 }
02330                 nmep->counterTFE_H = 0;
02331                 for (j = 0; j < k; ++j) {
02332                     if (j > 0)
02333                         nmep->counterTFE_H <<= 8;
02334                     nmep->counterTFE_H |= xp[j];
02335                 }
02336                 break;
02337             case 0x8015:
02338                 nmep->gotPE_H = 1;
02339                 k = pl - 4;
02340                 xp = ucp + 4;
02341                 if (k > szof) {
02342                     xp += (k - szof);
02343                     k = szof;
02344                 }
02345                 nmep->counterPE_H = 0;
02346                 for (j = 0; j < k; ++j) {
02347                     if (j > 0)
02348                         nmep->counterPE_H <<= 8;
02349                     nmep->counterPE_H |= xp[j];
02350                 }
02351                 break;
02352         default:
02353                 nmep->gotExtraPC = 1;
02354                 break;
02355         }
02356         num -= pl;
02357         ucp += pl;
02358     }
02359 }
02360 
02361 /* Counts number of failed self-tests. Also encodes the poweron_hour
02362    of the most recent failed self-test. Return value is negative if
02363    this function has a problem (typically -1), otherwise the bottom 8
02364    bits are the number of failed self tests and the 16 bits above that
02365    are the poweron hour of the most recent failure. Note: aborted self
02366    tests (typically by the user) and self tests in progress are not
02367    considered failures. See Working Draft SCSI Primary Commands - 3
02368    (SPC-3) section 7.2.10 T10/1416-D (rev 22a) */
02369 int
02370 scsiCountFailedSelfTests(scsi_device * fd, int noisy)
02371 {
02372     int num, k, n, err, res, fails, fail_hour;
02373     UINT8 * ucp;
02374     unsigned char resp[LOG_RESP_SELF_TEST_LEN];
02375 
02376     if ((err = scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp,
02377                             LOG_RESP_SELF_TEST_LEN, 0))) {
02378         if (noisy)
02379             pout("scsiCountSelfTests Failed [%s]\n", scsiErrString(err));
02380         return -1;
02381     }
02382     if ((resp[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
02383         if (noisy)
02384             pout("Self-test Log Sense Failed, page mismatch\n");
02385         return -1;
02386     }
02387     // compute page length
02388     num = (resp[2] << 8) + resp[3];
02389     // Log sense page length 0x190 bytes
02390     if (num != 0x190) {
02391         if (noisy)
02392             pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n", num);
02393         return -1;
02394     }
02395     fails = 0;
02396     fail_hour = 0;
02397     // loop through the twenty possible entries
02398     for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) {
02399 
02400         // timestamp in power-on hours (or zero if test in progress)
02401         n = (ucp[6] << 8) | ucp[7];
02402 
02403         // The spec says "all 20 bytes will be zero if no test" but
02404         // DG has found otherwise.  So this is a heuristic.
02405         if ((0 == n) && (0 == ucp[4]))
02406             break;
02407         res = ucp[4] & 0xf;
02408         if ((res > 2) && (res < 8)) {
02409             fails++;
02410             if (1 == fails)
02411                 fail_hour = (ucp[6] << 8) + ucp[7];
02412         }
02413     }
02414     return (fail_hour << 8) + fails;
02415 }
02416 
02417 /* Returns 0 if able to read self test log page; then outputs 1 into
02418    *inProgress if self test still in progress, else outputs 0. */
02419 int
02420 scsiSelfTestInProgress(scsi_device * fd, int * inProgress)
02421 {
02422     int num;
02423     UINT8 * ucp;
02424     unsigned char resp[LOG_RESP_SELF_TEST_LEN];
02425 
02426     if (scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp,
02427                      LOG_RESP_SELF_TEST_LEN, 0))
02428         return -1;
02429     if (resp[0] != SELFTEST_RESULTS_LPAGE)
02430         return -1;
02431     // compute page length
02432     num = (resp[2] << 8) + resp[3];
02433     // Log sense page length 0x190 bytes
02434     if (num != 0x190) {
02435         return -1;
02436     }
02437     ucp = resp + 4;
02438     if (inProgress)
02439         *inProgress = (0xf == (ucp[4] & 0xf)) ? 1 : 0;
02440     return 0;
02441 }
02442 
02443 /* Returns a negative value if failed to fetch Contol mode page or it was
02444    malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD
02445    bit is set. Examines default mode page when current==0 else examines
02446    current mode page. */
02447 int
02448 scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current)
02449 {
02450     int err, offset;
02451     UINT8 buff[64];
02452     int pc = current ? MPAGE_CONTROL_CURRENT : MPAGE_CONTROL_DEFAULT;
02453 
02454     memset(buff, 0, sizeof(buff));
02455     if (modese_len <= 6) {
02456         if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0, pc,
02457                                  buff, sizeof(buff)))) {
02458             if (SIMPLE_ERR_BAD_OPCODE == err)
02459                 modese_len = 10;
02460             else
02461                 return -EINVAL;
02462         } else if (0 == modese_len)
02463             modese_len = 6;
02464     }
02465     if (10 == modese_len) {
02466         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0, pc,
02467                               buff, sizeof(buff));
02468         if (err)
02469             return -EINVAL;
02470     }
02471     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02472     if ((offset >= 0) && (buff[offset + 1] >= 0xa))
02473         return (buff[offset + 2] & 2) ? 1 : 0;
02474     return -EINVAL;
02475 }
02476 
02477 /* Returns a negative value on error, 0 if unknown and 1 if SSD,
02478  * otherwise the positive returned value is the speed in rpm. First checks
02479  * the Block Device Characteristics VPD page and if that fails it tries the
02480  * RIGID_DISK_DRIVE_GEOMETRY_PAGE mode page. */
02481 
02482 int
02483 scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp,
02484            int * haw_zbcp)
02485 {
02486     int err, offset, speed;
02487     UINT8 buff[64];
02488     int pc = MPAGE_CONTROL_DEFAULT;
02489 
02490     memset(buff, 0, sizeof(buff));
02491     if ((0 == scsiInquiryVpd(device, SCSI_VPD_BLOCK_DEVICE_CHARACTERISTICS,
02492                              buff, sizeof(buff))) &&
02493         (((buff[2] << 8) + buff[3]) > 2)) {
02494         speed = (buff[4] << 8) + buff[5];
02495         if (form_factorp)
02496             *form_factorp = buff[7] & 0xf;
02497         if (haw_zbcp)
02498             *haw_zbcp = !!(0x10 & buff[8]);
02499         return speed;
02500     }
02501     if (form_factorp)
02502         *form_factorp = 0;
02503     if (haw_zbcp)
02504         *haw_zbcp = 0;
02505     if (modese_len <= 6) {
02506         if ((err = scsiModeSense(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc,
02507                                  buff, sizeof(buff)))) {
02508             if (SIMPLE_ERR_BAD_OPCODE == err)
02509                 modese_len = 10;
02510             else
02511                 return -EINVAL;
02512         } else if (0 == modese_len)
02513             modese_len = 6;
02514     }
02515     if (10 == modese_len) {
02516         err = scsiModeSense10(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc,
02517                               buff, sizeof(buff));
02518         if (err)
02519             return -EINVAL;
02520     }
02521     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02522     return (buff[offset + 20] << 8) | buff[offset + 21];
02523 }
02524 
02525 /* Returns a non-zero value in case of error, wcep/rcdp == -1 - get value,
02526    0 - clear bit, 1 - set bit  */
02527 
02528 int
02529 scsiGetSetCache(scsi_device * device,  int modese_len, short int * wcep,
02530                 short int * rcdp)
02531 {
02532     int err, offset, resp_len, sp;
02533     UINT8 buff[64], ch_buff[64];
02534     short set_wce = *wcep;
02535     short set_rcd = *rcdp;
02536 
02537     memset(buff, 0, sizeof(buff));
02538     if (modese_len <= 6) {
02539         if ((err = scsiModeSense(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT,
02540                                  buff, sizeof(buff)))) {
02541             if (SIMPLE_ERR_BAD_OPCODE == err)
02542                 modese_len = 10;
02543             else {
02544                 device->set_err(EINVAL, "SCSI MODE SENSE failed");
02545                 return -EINVAL;
02546             }
02547         } else if (0 == modese_len)
02548             modese_len = 6;
02549     }
02550 
02551     if (10 == modese_len) {
02552         err = scsiModeSense10(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT,
02553                               buff, sizeof(buff));
02554         if (err) {
02555             device->set_err(EINVAL, "SCSI MODE SENSE failed");
02556             return -EINVAL;
02557         }
02558     }
02559     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02560     if ((offset < 0) || (buff[offset + 1] < 0xa)) {
02561         device->set_err(EINVAL, "Bad response");
02562         return SIMPLE_ERR_BAD_RESP;
02563     }
02564 
02565     *wcep = ((buff[offset + 2] & 0x04) != 0);
02566     *rcdp = ((buff[offset + 2] & 0x01) != 0);
02567 
02568     if((*wcep == set_wce || set_wce == -1)
02569           && ((*rcdp == set_rcd) || set_rcd == -1))
02570       return 0; // no changes needed or nothing to set
02571 
02572     if (modese_len == 6)
02573         err = scsiModeSense(device, CACHING_PAGE, 0,
02574                             MPAGE_CONTROL_CHANGEABLE,
02575                             ch_buff, sizeof(ch_buff));
02576     else
02577         err = scsiModeSense10(device, CACHING_PAGE, 0,
02578                               MPAGE_CONTROL_CHANGEABLE,
02579                               ch_buff, sizeof(ch_buff));
02580     if (err) {
02581         device->set_err(EINVAL, "WCE/RCD bits not changable");
02582         return err;
02583     }
02584 
02585     // set WCE bit
02586     if(set_wce >= 0 && *wcep != set_wce) {
02587        if (0 == (ch_buff[offset + 2] & 0x04)) {
02588          device->set_err(EINVAL, "WCE bit not changable");
02589          return 1;
02590        }
02591        if(set_wce)
02592           buff[offset + 2] |= 0x04; // set bit
02593        else
02594           buff[offset + 2] &= 0xfb; // clear bit
02595     }
02596     // set RCD bit
02597     if(set_rcd >= 0 && *rcdp != set_rcd) {
02598        if (0 == (ch_buff[offset + 2] & 0x01)) {
02599          device->set_err(EINVAL, "RCD bit not changable");
02600          return 1;
02601        }
02602        if(set_rcd)
02603           buff[offset + 2] |= 0x01; // set bit
02604        else
02605           buff[offset + 2] &= 0xfe; // clear bit
02606     }
02607 
02608     /* mask out DPOFUA device specific (disk) parameter bit */
02609     if (10 == modese_len) {
02610         resp_len = (buff[0] << 8) + buff[1] + 2;
02611         buff[3] &= 0xef;
02612     } else {
02613         resp_len = buff[0] + 1;
02614         buff[2] &= 0xef;
02615     }
02616     sp = 0; /* Do not change saved values */
02617     if (10 == modese_len)
02618         err = scsiModeSelect10(device, sp, buff, resp_len);
02619     else if (6 == modese_len)
02620         err = scsiModeSelect(device, sp, buff, resp_len);
02621     if(err)
02622       device->set_err(EINVAL, "MODE SELECT command failed");
02623     return err;
02624 }
02625 
02626 
02627 /* Attempts to set or clear GLTSD bit in Control mode page. If enabled is
02628    0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if
02629    successful, negative if low level error, > 0 if higher level error (e.g.
02630    SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */
02631 int
02632 scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len)
02633 {
02634     int err, offset, resp_len, sp;
02635     UINT8 buff[64];
02636     UINT8 ch_buff[64];
02637 
02638     memset(buff, 0, sizeof(buff));
02639     if (modese_len <= 6) {
02640         if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
02641                                  MPAGE_CONTROL_CURRENT,
02642                                  buff, sizeof(buff)))) {
02643             if (SIMPLE_ERR_BAD_OPCODE == err)
02644                 modese_len = 10;
02645             else
02646                 return err;
02647         } else if (0 == modese_len)
02648             modese_len = 6;
02649     }
02650     if (10 == modese_len) {
02651         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
02652                               MPAGE_CONTROL_CURRENT,
02653                               buff, sizeof(buff));
02654         if (err)
02655             return err;
02656     }
02657     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02658     if ((offset < 0) || (buff[offset + 1] < 0xa))
02659         return SIMPLE_ERR_BAD_RESP;
02660 
02661     if (enabled)
02662         enabled = 2;
02663     if (enabled == (buff[offset + 2] & 2))
02664         return 0;       /* GLTSD already in wanted state so nothing to do */
02665 
02666     if (modese_len == 6)
02667         err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
02668                             MPAGE_CONTROL_CHANGEABLE,
02669                             ch_buff, sizeof(ch_buff));
02670     else
02671         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
02672                               MPAGE_CONTROL_CHANGEABLE,
02673                               ch_buff, sizeof(ch_buff));
02674     if (err)
02675         return err;
02676     if (0 == (ch_buff[offset + 2] & 2))
02677         return SIMPLE_ERR_BAD_PARAM;  /* GLTSD bit not changeable */
02678 
02679     /* mask out DPOFUA device specific (disk) parameter bit */
02680     if (10 == modese_len) {
02681         resp_len = (buff[0] << 8) + buff[1] + 2;
02682         buff[3] &= 0xef;    
02683     } else {
02684         resp_len = buff[0] + 1;
02685         buff[2] &= 0xef;
02686     }
02687     sp = (buff[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
02688     if (enabled)
02689         buff[offset + 2] |= 0x2;    /* set GLTSD bit */
02690     else
02691         buff[offset + 2] &= 0xfd;   /* clear GLTSD bit */
02692     if (10 == modese_len)
02693         err = scsiModeSelect10(device, sp, buff, resp_len);
02694     else if (6 == modese_len)
02695         err = scsiModeSelect(device, sp, buff, resp_len);
02696     return err;
02697 }
02698 
02699 /* Returns a negative value if failed to fetch Protocol specific port mode
02700    page or it was malformed. Returns transport protocol identifier when
02701    value >= 0 . */
02702 int
02703 scsiFetchTransportProtocol(scsi_device * device, int modese_len)
02704 {
02705     int err, offset;
02706     UINT8 buff[64];
02707 
02708     memset(buff, 0, sizeof(buff));
02709     if (modese_len <= 6) {
02710         if ((err = scsiModeSense(device, PROTOCOL_SPECIFIC_PORT_PAGE, 0,
02711                                  MPAGE_CONTROL_CURRENT,
02712                                  buff, sizeof(buff)))) {
02713             if (SIMPLE_ERR_BAD_OPCODE == err)
02714                 modese_len = 10;
02715             else
02716                 return -EINVAL;
02717         } else if (0 == modese_len)
02718             modese_len = 6;
02719     }
02720     if (10 == modese_len) {
02721         err = scsiModeSense10(device, PROTOCOL_SPECIFIC_PORT_PAGE, 0,
02722                               MPAGE_CONTROL_CURRENT,
02723                               buff, sizeof(buff));
02724         if (err)
02725             return -EINVAL;
02726     }
02727     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02728     if ((offset >= 0) && (buff[offset + 1] > 1)) {
02729         if ((0 == (buff[offset] & 0x40)) &&       /* SPF==0 */
02730             (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f)))
02731                 return (buff[offset + 2] & 0xf);
02732     }
02733     return -EINVAL;
02734 }
02735 
02736 const unsigned char *
02737 sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len,
02738                         int desc_type)
02739 {
02740     int add_sen_len, add_len, desc_len, k;
02741     const unsigned char * descp;
02742 
02743     if ((sense_len < 8) || (0 == (add_sen_len = sensep[7])))
02744         return NULL;
02745     if ((sensep[0] < 0x72) || (sensep[0] > 0x73))
02746         return NULL;
02747     add_sen_len = (add_sen_len < (sense_len - 8)) ?
02748                          add_sen_len : (sense_len - 8);
02749     descp = &sensep[8];
02750     for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
02751         descp += desc_len;
02752         add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
02753         desc_len = add_len + 2;
02754         if (descp[0] == desc_type)
02755             return descp;
02756         if (add_len < 0) /* short descriptor ?? */
02757             break;
02758     }
02759     return NULL;
02760 }
02761 
02762 // Convenience function for formatting strings from SCSI identify
02763 void
02764 scsi_format_id_string(char * out, const unsigned char * in, int n)
02765 {
02766   char tmp[65];
02767   n = n > 64 ? 64 : n;
02768   strncpy(tmp, (const char *)in, n);
02769   tmp[n] = '\0';
02770 
02771   // Find the first non-space character (maybe none).
02772   int first = -1;
02773   int i;
02774   for (i = 0; tmp[i]; i++)
02775     if (!isspace((int)tmp[i])) {
02776       first = i;
02777       break;
02778     }
02779 
02780   if (first == -1) {
02781     // There are no non-space characters.
02782     out[0] = '\0';
02783     return;
02784   }
02785 
02786   // Find the last non-space character.
02787   for (i = strlen(tmp)-1; i >= first && isspace((int)tmp[i]); i--);
02788   int last = i;
02789 
02790   strncpy(out, tmp+first, last-first+1);
02791   out[last-first+1] = '\0';
02792 }