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 3820 2013-06-17 08:45:10Z samm2 $"
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[0x1fc];     /* size chosen for old INQUIRY command */
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     if (10 == iecp->modese_len) {
01445         resp_len = (rout[0] << 8) + rout[1] + 2;
01446         rout[3] &= 0xef;    /* for disks mask out DPOFUA bit */
01447     } else {
01448         resp_len = rout[0] + 1;
01449         rout[2] &= 0xef;    /* for disks mask out DPOFUA bit */
01450     }
01451     sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
01452     if (enabled) {
01453         rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED;
01454         if (scsi_debugmode > 2)
01455             rout[offset + 2] |= SCSI_IEC_MP_BYTE2_TEST_MASK;
01456         rout[offset + 3] = SCSI_IEC_MP_MRIE;
01457         rout[offset + 4] = (SCSI_IEC_MP_INTERVAL_T >> 24) & 0xff;
01458         rout[offset + 5] = (SCSI_IEC_MP_INTERVAL_T >> 16) & 0xff;
01459         rout[offset + 6] = (SCSI_IEC_MP_INTERVAL_T >> 8) & 0xff;
01460         rout[offset + 7] = SCSI_IEC_MP_INTERVAL_T & 0xff;
01461         rout[offset + 8] = (SCSI_IEC_MP_REPORT_COUNT >> 24) & 0xff;
01462         rout[offset + 9] = (SCSI_IEC_MP_REPORT_COUNT >> 16) & 0xff;
01463         rout[offset + 10] = (SCSI_IEC_MP_REPORT_COUNT >> 8) & 0xff;
01464         rout[offset + 11] = SCSI_IEC_MP_REPORT_COUNT & 0xff;
01465         if (iecp->gotChangeable) {
01466             UINT8 chg2 = iecp->raw_chg[offset + 2];
01467 
01468             rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) :
01469                                       iecp->raw_curr[offset + 2];
01470             for (k = 3; k < 12; ++k) {
01471                 if (0 == iecp->raw_chg[offset + k])
01472                     rout[offset + k] = iecp->raw_curr[offset + k];
01473             }
01474         }
01475         if (0 == memcmp(&rout[offset + 2], &iecp->raw_chg[offset + 2], 10)) {
01476             if (scsi_debugmode > 0)
01477                 pout("scsiSetExceptionControlAndWarning: already enabled\n");
01478             return 0;
01479         }
01480     } else { /* disabling Exception Control and (temperature) Warnings */
01481         eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
01482         wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0;
01483         if ((! eCEnabled) && (! wEnabled)) {
01484             if (scsi_debugmode > 0)
01485                 pout("scsiSetExceptionControlAndWarning: already disabled\n");
01486             return 0;   /* nothing to do, leave other setting alone */
01487         }
01488         if (wEnabled)
01489             rout[offset + 2] &= EWASC_DISABLE;
01490         if (eCEnabled) {
01491             if (iecp->gotChangeable &&
01492                 (iecp->raw_chg[offset + 2] & DEXCPT_ENABLE))
01493                 rout[offset + 2] |= DEXCPT_ENABLE;
01494                 rout[offset + 2] &= TEST_DISABLE;/* clear TEST bit for spec */
01495         }
01496     }
01497     if (10 == iecp->modese_len)
01498         err = scsiModeSelect10(device, sp, rout, resp_len);
01499     else if (6 == iecp->modese_len)
01500         err = scsiModeSelect(device, sp, rout, resp_len);
01501     return err;
01502 }
01503 
01504 int
01505 scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp)
01506 {
01507     UINT8 tBuf[252];
01508     int err;
01509 
01510     memset(tBuf, 0, sizeof(tBuf));
01511     if ((err = scsiLogSense(device, TEMPERATURE_LPAGE, 0, tBuf,
01512                             sizeof(tBuf), 0))) {
01513         *currenttemp = 0;
01514         *triptemp = 0;
01515         pout("Log Sense for temperature failed [%s]\n", scsiErrString(err));
01516         return err;
01517     }
01518     *currenttemp = tBuf[9];
01519     *triptemp = tBuf[15];
01520     return 0;
01521 }
01522 
01523 /* Read informational exception log page or Request Sense response.
01524  * Fetching asc/ascq code potentially flagging an exception or warning.
01525  * Returns 0 if ok, else error number. A current temperature of 255
01526  * (Celsius) implies that the temperature not available. */
01527 int
01528 scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
01529             UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp)
01530 {
01531     UINT8 tBuf[252];
01532     struct scsi_sense_disect sense_info;
01533     int err;
01534     int temperatureSet = 0;
01535     unsigned short pagesize;
01536     UINT8 currTemp, trTemp;
01537 
01538     *asc = 0;
01539     *ascq = 0;
01540     *currenttemp = 0;
01541     *triptemp = 0;
01542     memset(tBuf,0,sizeof(tBuf)); // need to clear stack space of junk
01543     memset(&sense_info, 0, sizeof(sense_info));
01544     if (hasIELogPage) {
01545         if ((err = scsiLogSense(device, IE_LPAGE, 0, tBuf,
01546                                 sizeof(tBuf), 0))) {
01547             pout("Log Sense failed, IE page [%s]\n", scsiErrString(err));
01548             return err;
01549         }
01550         // pull out page size from response, don't forget to add 4
01551         pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4;
01552         if ((pagesize < 4) || tBuf[4] || tBuf[5]) {
01553             pout("Log Sense failed, IE page, bad parameter code or length\n");
01554             return SIMPLE_ERR_BAD_PARAM;
01555         }
01556         if (tBuf[7] > 1) {
01557             sense_info.asc = tBuf[8];
01558             sense_info.ascq = tBuf[9];
01559             if (! hasTempLogPage) {
01560                 if (tBuf[7] > 2)
01561                     *currenttemp = tBuf[10];
01562                 if (tBuf[7] > 3)        /* IBM extension in SMART (IE) lpage */
01563                     *triptemp = tBuf[11];
01564             }
01565         }
01566     }
01567     if (0 == sense_info.asc) {
01568         /* ties in with MRIE field of 6 in IEC mode page (0x1c) */
01569         if ((err = scsiRequestSense(device, &sense_info))) {
01570             pout("Request Sense failed, [%s]\n", scsiErrString(err));
01571             return err;
01572         }
01573     }
01574     *asc = sense_info.asc;
01575     *ascq = sense_info.ascq;
01576     if ((! temperatureSet) && hasTempLogPage) {
01577         if (0 == scsiGetTemp(device, &currTemp, &trTemp)) {
01578             *currenttemp = currTemp;
01579             *triptemp = trTemp;
01580         }
01581     }
01582     return 0;
01583 }
01584 
01585 // The first character (W, C, I) tells the severity
01586 static const char * TapeAlertsMessageTable[]= {
01587     " ",
01588     /* 0x01 */
01589    "W: The tape drive is having problems reading data. No data has been lost,\n"
01590        "  but there has been a reduction in the performance of the tape.",
01591     /* 0x02 */
01592    "W: The tape drive is having problems writing data. No data has been lost,\n"
01593        "  but there has been a reduction in the capacity of the tape.",
01594     /* 0x03 */
01595    "W: The operation has stopped because an error has occurred while reading\n"
01596        "  or writing data that the drive cannot correct.",
01597     /* 0x04 */
01598    "C: Your data is at risk:\n"
01599        "  1. Copy any data you require from this tape. \n"
01600        "  2. Do not use this tape again.\n"
01601        "  3. Restart the operation with a different tape.",
01602     /* 0x05 */
01603    "C: The tape is damaged or the drive is faulty. Call the tape drive\n"
01604        "  supplier helpline.",
01605     /* 0x06 */
01606    "C: The tape is from a faulty batch or the tape drive is faulty:\n"
01607        "  1. Use a good tape to test the drive.\n"
01608        "  2. If problem persists, call the tape drive supplier helpline.",
01609     /* 0x07 */
01610    "W: The tape cartridge has reached the end of its calculated useful life:\n"
01611        "  1. Copy data you need to another tape.\n"
01612        "  2. Discard the old tape.",
01613     /* 0x08 */
01614    "W: The tape cartridge is not data-grade. Any data you back up to the tape\n"
01615        "  is at risk. Replace the cartridge with a data-grade tape.",
01616     /* 0x09 */
01617    "C: You are trying to write to a write-protected cartridge. Remove the\n"
01618        "  write-protection or use another tape.",
01619     /* 0x0a */
01620    "I: You cannot eject the cartridge because the tape drive is in use. Wait\n"
01621        "  until the operation is complete before ejecting the cartridge.",
01622     /* 0x0b */
01623    "I: The tape in the drive is a cleaning cartridge.",
01624     /* 0x0c */
01625    "I: You have tried to load a cartridge of a type which is not supported\n"
01626        "  by this drive.",
01627     /* 0x0d */
01628    "C: The operation has failed because the tape in the drive has experienced\n"
01629        "  a mechanical failure:\n"
01630        "  1. Discard the old tape.\n"
01631        "  2. Restart the operation with a different tape.",
01632     /* 0x0e */
01633    "C: The operation has failed because the tape in the drive has experienced\n"
01634        "  a mechanical failure:\n"
01635        "  1. Do not attempt to extract the tape cartridge\n"
01636        "  2. Call the tape drive supplier helpline.",
01637     /* 0x0f */
01638    "W: The memory in the tape cartridge has failed, which reduces\n"
01639        "  performance. Do not use the cartridge for further write operations.",
01640     /* 0x10 */
01641    "C: The operation has failed because the tape cartridge was manually\n"
01642        "  de-mounted while the tape drive was actively writing or reading.",
01643     /* 0x11 */
01644    "W: You have loaded a cartridge of a type that is read-only in this drive.\n"
01645        "  The cartridge will appear as write-protected.",
01646     /* 0x12 */
01647    "W: The tape directory on the tape cartridge has been corrupted. File\n"
01648        "  search performance will be degraded. The tape directory can be rebuilt\n"
01649        "  by reading all the data on the cartridge.",
01650     /* 0x13 */
01651    "I: The tape cartridge is nearing the end of its calculated life. It is\n"
01652        "  recommended that you:\n"
01653        "  1. Use another tape cartridge for your next backup.\n"
01654        "  2. Store this tape in a safe place in case you need to restore "
01655        "  data from it.",
01656     /* 0x14 */
01657    "C: The tape drive needs cleaning:\n"
01658        "  1. If the operation has stopped, eject the tape and clean the drive.\n"
01659        "  2. If the operation has not stopped, wait for it to finish and then\n"
01660        "  clean the drive.\n"
01661        "  Check the tape drive users manual for device specific cleaning instructions.",
01662     /* 0x15 */
01663    "W: The tape drive is due for routine cleaning:\n"
01664        "  1. Wait for the current operation to finish.\n"
01665        "  2. The use a cleaning cartridge.\n"
01666        "  Check the tape drive users manual for device specific cleaning instructions.",
01667     /* 0x16 */
01668    "C: The last cleaning cartridge used in the tape drive has worn out:\n"
01669        "  1. Discard the worn out cleaning cartridge.\n"
01670        "  2. Wait for the current operation to finish.\n"
01671        "  3. Then use a new cleaning cartridge.",
01672     /* 0x17 */
01673    "C: The last cleaning cartridge used in the tape drive was an invalid\n"
01674        "  type:\n"
01675        "  1. Do not use this cleaning cartridge in this drive.\n"
01676        "  2. Wait for the current operation to finish.\n"
01677        "  3. Then use a new cleaning cartridge.",
01678     /* 0x18 */
01679    "W: The tape drive has requested a retention operation",
01680     /* 0x19 */
01681    "W: A redundant interface port on the tape drive has failed",
01682     /* 0x1a */
01683    "W: A tape drive cooling fan has failed",
01684     /* 0x1b */
01685    "W: A redundant power supply has failed inside the tape drive enclosure.\n"
01686        "  Check the enclosure users manual for instructions on replacing the\n"
01687        "  failed power supply.",
01688     /* 0x1c */
01689    "W: The tape drive power consumption is outside the specified range.",
01690     /* 0x1d */
01691    "W: Preventive maintenance of the tape drive is required. Check the tape\n"
01692        "  drive users manual for device specific preventive maintenance\n"
01693        "  tasks or call the tape drive supplier helpline.",
01694     /* 0x1e */
01695    "C: The tape drive has a hardware fault:\n"
01696        "  1. Eject the tape or magazine.\n"
01697        "  2. Reset the drive.\n"
01698        "  3. Restart the operation.",
01699     /* 0x1f */
01700    "C: The tape drive has a hardware fault:\n"
01701        "  1. Turn the tape drive off and then on again.\n"
01702        "  2. Restart the operation.\n"
01703     "  3. If the problem persists, call the tape drive supplier helpline.",
01704     /* 0x20 */
01705    "W: The tape drive has a problem with the application client interface:\n"
01706        "  1. Check the cables and cable connections.\n"
01707        "  2. Restart the operation.",
01708     /* 0x21 */
01709    "C: The operation has failed:\n"
01710        "  1. Eject the tape or magazine.\n"
01711        "  2. Insert the tape or magazine again.\n"
01712        "  3. Restart the operation.",
01713     /* 0x22 */
01714    "W: The firmware download has failed because you have tried to use the\n"
01715        "  incorrect firmware for this tape drive. Obtain the correct\n"
01716        "  firmware and try again.",
01717     /* 0x23 */
01718    "W: Environmental conditions inside the tape drive are outside the\n"
01719        "  specified humidity range.",
01720     /* 0x24 */
01721    "W: Environmental conditions inside the tape drive are outside the\n"
01722        "  specified temperature range.",
01723     /* 0x25 */
01724    "W: The voltage supply to the tape drive is outside the specified range.",
01725     /* 0x26 */
01726    "C: A hardware failure of the tape drive is predicted. Call the tape\n"
01727        "  drive supplier helpline.",
01728     /* 0x27 */
01729    "W: The tape drive may have a hardware fault. Run extended diagnostics to\n"
01730        "  verify and diagnose the problem. Check the tape drive users manual for\n"
01731        "  device specific instructions on running extended diagnostic tests.",
01732     /* 0x28 */
01733    "C: The changer mechanism is having difficulty communicating with the tape\n"
01734        "  drive:\n"
01735        "  1. Turn the autoloader off then on.\n"
01736        "  2. Restart the operation.\n"
01737        "  3. If problem persists, call the tape drive supplier helpline.",
01738     /* 0x29 */
01739    "C: A tape has been left in the autoloader by a previous hardware fault:\n"
01740        "  1. Insert an empty magazine to clear the fault.\n"
01741        "  2. If the fault does not clear, turn the autoloader off and then\n"
01742        "  on again.\n"
01743        "  3. If the problem persists, call the tape drive supplier helpline.",
01744     /* 0x2a */
01745    "W: There is a problem with the autoloader mechanism.",
01746     /* 0x2b */
01747    "C: The operation has failed because the autoloader door is open:\n"
01748        "  1. Clear any obstructions from the autoloader door.\n"
01749        "  2. Eject the magazine and then insert it again.\n"
01750        "  3. If the fault does not clear, turn the autoloader off and then\n"
01751        "  on again.\n"
01752        "  4. If the problem persists, call the tape drive supplier helpline.",
01753     /* 0x2c */
01754    "C: The autoloader has a hardware fault:\n"
01755        "  1. Turn the autoloader off and then on again.\n"
01756        "  2. Restart the operation.\n"
01757        "  3. If the problem persists, call the tape drive supplier helpline.\n"
01758        "  Check the autoloader users manual for device specific instructions\n"
01759        "  on turning the device power on and off.",
01760     /* 0x2d */
01761    "C: The autoloader cannot operate without the magazine,\n"
01762        "  1. Insert the magazine into the autoloader.\n"
01763        "  2. Restart the operation.",
01764     /* 0x2e */
01765    "W: A hardware failure of the changer mechanism is predicted. Call the\n"
01766        "  tape drive supplier helpline.",
01767     /* 0x2f */
01768    "I: Reserved.",
01769     /* 0x30 */
01770    "I: Reserved.",
01771     /* 0x31 */
01772    "I: Reserved.",
01773     /* 0x32 */
01774    "W: Media statistics have been lost at some time in the past",
01775     /* 0x33 */
01776    "W: The tape directory on the tape cartridge just unloaded has been\n"
01777        "  corrupted. File search performance will be degraded. The tape\n"
01778        "  directory can be rebuilt by reading all the data.",
01779     /* 0x34 */
01780    "C: The tape just unloaded could not write its system area successfully:\n"
01781        "  1. Copy data to another tape cartridge.\n"
01782        "  2. Discard the old cartridge.",
01783     /* 0x35 */
01784    "C: The tape system are could not be read successfully at load time:\n"
01785     "  1. Copy data to another tape cartridge.\n",
01786     /* 0x36 */
01787    "C: The start or data could not be found on the tape:\n"
01788        "  1. Check you are using the correct format tape.\n"
01789        "  2. Discard the tape or return the tape to your supplier",
01790     /* 0x37 */
01791     "C: The operation has failed because the media cannot be loaded\n"
01792         "  and threaded.\n"
01793         "  1. Remove the cartridge, inspect it as specified in the product\n"
01794         "  manual, and retry the operation.\n"
01795         "  2. If the problem persists, call the tape drive supplier help line.",
01796     /* 0x38 */
01797     "C: The operation has failed because the medium cannot be unloaded:\n"
01798         "  1. Do not attempt to extract the tape cartridge.\n"
01799         "  2. Call the tape driver supplier help line.",
01800     /* 0x39 */
01801     "C: The tape drive has a problem with the automation interface:\n"
01802         "  1. Check the power to the automation system.\n"
01803         "  2. Check the cables and cable connections.\n"
01804         "  3. Call the supplier help line if problem persists.",
01805     /* 0x3a */
01806     "W: The tape drive has reset itself due to a detected firmware\n"
01807         "  fault. If problem persists, call the supplier help line.",
01808     };
01809 
01810 const char *
01811 scsiTapeAlertsTapeDevice(unsigned short code)
01812 {
01813     const int num = sizeof(TapeAlertsMessageTable) /
01814                         sizeof(TapeAlertsMessageTable[0]);
01815 
01816     return (code < num) ?  TapeAlertsMessageTable[code] : "Unknown Alert";
01817 }
01818 
01819 // The first character (W, C, I) tells the severity
01820 static const char * ChangerTapeAlertsMessageTable[]= {
01821     " ",
01822     /* 0x01 */
01823     "C: The library mechanism is having difficulty communicating with the\n"
01824         "  drive:\n"
01825         "  1. Turn the library off then on.\n"
01826         "  2. Restart the operation.\n"
01827         "  3. If the problem persists, call the library supplier help line.",
01828     /* 0x02 */
01829     "W: There is a problem with the library mechanism. If problem persists,\n"
01830         "  call the library supplier help line.",
01831     /* 0x03 */
01832     "C: The library has a hardware fault:\n"
01833         "  1. Reset the library.\n"
01834         "  2. Restart the operation.\n"
01835         "  Check the library users manual for device specific instructions on resetting\n"
01836         "  the device.",
01837     /* 0x04 */
01838     "C: The library has a hardware fault:\n"
01839         "  1. Turn the library off then on again.\n"
01840         "  2. Restart the operation.\n"
01841         "  3. If the problem persists, call the library supplier help line.\n"
01842         "  Check the library users manual for device specific instructions on turning the\n"
01843         "  device power on and off.",
01844     /* 0x05 */
01845     "W: The library mechanism may have a hardware fault.\n"
01846         "  Run extended diagnostics to verify and diagnose the problem. Check the library\n"
01847         "  users manual for device specific instructions on running extended diagnostic\n"
01848         "  tests.",
01849     /* 0x06 */
01850     "C: The library has a problem with the host interface:\n"
01851         "  1. Check the cables and connections.\n"
01852         "  2. Restart the operation.",
01853     /* 0x07 */
01854     "W: A hardware failure of the library is predicted. Call the library\n"
01855         "  supplier help line.",
01856     /* 0x08 */
01857     "W: Preventive maintenance of the library is required.\n"
01858         "  Check the library users manual for device specific preventative maintenance\n"
01859         "  tasks, or call your library supplier help line.",
01860     /* 0x09 */
01861     "C: General environmental conditions inside the library are outside the\n"
01862         "  specified humidity range.",
01863     /* 0x0a */
01864     "C: General environmental conditions inside the library are outside the\n"
01865         "  specified temperature range.",
01866     /* 0x0b */
01867     "C: The voltage supply to the library is outside the specified range.\n"
01868         "  There is a potential problem with the power supply or failure of\n"
01869         "  a redundant power supply.",
01870     /* 0x0c */
01871     "C: A cartridge has been left inside the library by a previous hardware\n"
01872         "  fault:\n"
01873         "  1. Insert an empty magazine to clear the fault.\n"
01874         "  2. If the fault does not clear, turn the library off and then on again.\n"
01875         "  3. If the problem persists, call the library supplier help line.",
01876     /* 0x0d */
01877     "W: There is a potential problem with the drive ejecting cartridges or\n"
01878         "  with the library mechanism picking a cartridge from a slot.\n"
01879         "  1. No action needs to be taken at this time.\n"
01880         "  2. If the problem persists, call the library supplier help line.",
01881     /* 0x0e */
01882     "W: There is a potential problem with the library mechanism placing a\n"
01883         "  cartridge into a slot.\n"
01884         "  1. No action needs to be taken at this time.\n"
01885         "  2. If the problem persists, call the library supplier help line.",
01886     /* 0x0f */
01887     "W: There is a potential problem with the drive or the library mechanism\n"
01888         "  loading cartridges, or an incompatible cartridge.",
01889     /* 0x10 */
01890     "C: The library has failed because the door is open:\n"
01891         "  1. Clear any obstructions from the library door.\n"
01892         "  2. Close the library door.\n"
01893         "  3. If the problem persists, call the library supplier help line.",
01894     /* 0x11 */
01895     "C: There is a mechanical problem with the library media import/export\n"
01896         "  mailslot.",
01897     /* 0x12 */
01898     "C: The library cannot operate without the magazine.\n"
01899         "  1. Insert the magazine into the library.\n"
01900         "  2. Restart the operation.",
01901     /* 0x13 */
01902     "W: Library security has been compromised.",
01903     /* 0x14 */
01904     "I: The library security mode has been changed.\n"
01905         "  The library has either been put into secure mode, or the library has exited\n"
01906         "  the secure mode.\n"
01907         "  This is for information purposes only. No action is required.",
01908     /* 0x15 */
01909     "I: The library has been manually turned offline and is unavailable for use.",
01910     /* 0x16 */
01911     "I: A drive inside the library has been taken offline.\n"
01912         "  This is for information purposes only. No action is required.",
01913     /* 0x17 */
01914     "W: There is a potential problem with the bar code label or the scanner\n"
01915         "  hardware in the library mechanism.\n"
01916         "  1. No action needs to be taken at this time.\n"
01917         "  2. If the problem persists, call the library supplier help line.",
01918     /* 0x18 */
01919     "C: The library has detected an inconsistency in its inventory.\n"
01920         "  1. Redo the library inventory to correct inconsistency.\n"
01921         "  2. Restart the operation.\n"
01922         "  Check the applications users manual or the hardware users manual for\n"
01923         "  specific instructions on redoing the library inventory.",
01924     /* 0x19 */
01925     "W: A library operation has been attempted that is invalid at this time.",
01926     /* 0x1a */
01927     "W: A redundant interface port on the library has failed.",
01928     /* 0x1b */
01929     "W: A library cooling fan has failed.",
01930     /* 0x1c */
01931     "W: A redundant power supply has failed inside the library. Check the\n"
01932         "  library users manual for instructions on replacing the failed power supply.",
01933     /* 0x1d */
01934     "W: The library power consumption is outside the specified range.",
01935     /* 0x1e */
01936     "C: A failure has occurred in the cartridge pass-through mechanism between\n"
01937         "  two library modules.",
01938     /* 0x1f */
01939     "C: A cartridge has been left in the pass-through mechanism from a\n"
01940         "  previous hardware fault. Check the library users guide for instructions on\n"
01941         "  clearing this fault.",
01942     /* 0x20 */
01943     "I: The library was unable to read the bar code on a cartridge.",
01944 };
01945 
01946 const char *
01947 scsiTapeAlertsChangerDevice(unsigned short code)
01948 {
01949     const int num = sizeof(ChangerTapeAlertsMessageTable) /
01950                         sizeof(ChangerTapeAlertsMessageTable[0]);
01951 
01952     return (code < num) ?  ChangerTapeAlertsMessageTable[code] : "Unknown Alert";
01953 }
01954 
01955 
01956 /* this is a subset of the SCSI additional sense code strings indexed
01957  * by "ascq" for the case when asc==SCSI_ASC_IMPENDING_FAILURE (0x5d)
01958  */
01959 static const char * strs_for_asc_5d[] = {
01960    /* 0x00 */   "FAILURE PREDICTION THRESHOLD EXCEEDED",
01961         "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED",
01962         "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED",
01963         "SPARE AREA EXHAUSTION PREDICTION THRESHOLD EXCEEDED",
01964         "",
01965         "",
01966         "",
01967         "",
01968         "",
01969         "",
01970         "",
01971         "",
01972         "",
01973         "",
01974         "",
01975         "",
01976    /* 0x10 */   "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
01977         "HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
01978         "HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
01979         "HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
01980         "HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
01981         "HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
01982         "HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
01983         "HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
01984         "HARDWARE IMPENDING FAILURE CONTROLLER DETECTED",
01985         "HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
01986         "HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
01987         "HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
01988         "HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
01989         "",
01990         "",
01991         "",
01992    /* 0x20 */   "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
01993         "CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
01994         "CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
01995         "CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
01996         "CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
01997         "CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH",
01998         "CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH",
01999         "CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS",
02000         "CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED",
02001         "CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE",
02002         "CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE",
02003         "CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT",
02004         "CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
02005         "",
02006         "",
02007         "",
02008    /* 0x30 */   "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
02009         "DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
02010         "DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
02011         "DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
02012         "DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
02013         "DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH",
02014         "DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH",
02015         "DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS",
02016         "DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED",
02017         "DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE",
02018         "DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE",
02019         "DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT",
02020         "DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
02021         "",
02022         "",
02023         "",
02024    /* 0x40 */   "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
02025         "SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
02026         "SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
02027         "SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
02028         "SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
02029         "SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH",
02030         "SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH",
02031         "SERVO IMPENDING FAILURE CHANNEL PARAMETRICS",
02032         "SERVO IMPENDING FAILURE CONTROLLER DETECTED",
02033         "SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE",
02034         "SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE",
02035         "SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT",
02036         "SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
02037         "",
02038         "",
02039         "",
02040    /* 0x50 */   "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
02041         "SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
02042         "SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
02043         "SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
02044         "SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
02045         "SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
02046         "SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
02047         "SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS",
02048         "SPINDLE IMPENDING FAILURE CONTROLLER DETECTED",
02049         "SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
02050         "SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE",
02051         "SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT",
02052         "SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
02053         "",
02054         "",
02055         "",
02056    /* 0x60 */   "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
02057         "FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
02058         "FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
02059         "FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
02060         "FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
02061         "FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
02062         "FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
02063         "FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
02064         "FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED",
02065         "FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
02066         "FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
02067         "FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
02068    /* 0x6c */   "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT"};
02069 
02070 
02071 /* this is a subset of the SCSI additional sense code strings indexed
02072  *  * by "ascq" for the case when asc==SCSI_ASC_WARNING (0xb)
02073  *   */
02074 static const char * strs_for_asc_b[] = {
02075        /* 0x00 */   "WARNING",
02076                "WARNING - SPECIFIED TEMPERATURE EXCEEDED",
02077                "WARNING - ENCLOSURE DEGRADED"};
02078 
02079 static char spare_buff[128];
02080 
02081 const char *
02082 scsiGetIEString(UINT8 asc, UINT8 ascq)
02083 {
02084     const char * rp;
02085 
02086     if (SCSI_ASC_IMPENDING_FAILURE == asc) {
02087         if (ascq == 0xff)
02088             return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)";
02089         else if (ascq <
02090                  (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) {
02091             rp = strs_for_asc_5d[ascq];
02092             if (strlen(rp) > 0)
02093                 return rp;
02094         }
02095         snprintf(spare_buff, sizeof(spare_buff),
02096                  "FAILURE PREDICTION THRESHOLD EXCEEDED: ascq=0x%x", ascq);
02097         return spare_buff;
02098     } else if (SCSI_ASC_WARNING == asc) {
02099         if (ascq < (sizeof(strs_for_asc_b) / sizeof(strs_for_asc_b[0]))) {
02100             rp = strs_for_asc_b[ascq];
02101             if (strlen(rp) > 0)
02102                 return rp;
02103         }
02104         snprintf(spare_buff, sizeof(spare_buff), "WARNING: ascq=0x%x", ascq);
02105         return spare_buff;
02106     }
02107     return NULL;        /* not a IE additional sense code */
02108 }
02109 
02110 
02111 /* This is not documented in t10.org, page 0x80 is vendor specific */
02112 /* Some IBM disks do an offline read-scan when they get this command. */
02113 int
02114 scsiSmartIBMOfflineTest(scsi_device * device)
02115 {
02116     UINT8 tBuf[256];
02117     int res;
02118 
02119     memset(tBuf, 0, sizeof(tBuf));
02120     /* Build SMART Off-line Immediate Diag Header */
02121     tBuf[0] = 0x80; /* Page Code */
02122     tBuf[1] = 0x00; /* Reserved */
02123     tBuf[2] = 0x00; /* Page Length MSB */
02124     tBuf[3] = 0x04; /* Page Length LSB */
02125     tBuf[4] = 0x03; /* SMART Revision */
02126     tBuf[5] = 0x00; /* Reserved */
02127     tBuf[6] = 0x00; /* Off-line Immediate Time MSB */
02128     tBuf[7] = 0x00; /* Off-line Immediate Time LSB */
02129     res = scsiSendDiagnostic(device, SCSI_DIAG_NO_SELF_TEST, tBuf, 8);
02130     if (res)
02131         pout("IBM offline test failed [%s]\n", scsiErrString(res));
02132     return res;
02133 }
02134 
02135 int
02136 scsiSmartDefaultSelfTest(scsi_device * device)
02137 {
02138     int res;
02139 
02140     res = scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0);
02141     if (res)
02142         pout("Default self test failed [%s]\n", scsiErrString(res));
02143     return res;
02144 }
02145 
02146 int
02147 scsiSmartShortSelfTest(scsi_device * device)
02148 {
02149     int res;
02150 
02151     res = scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0);
02152     if (res)
02153         pout("Short offline self test failed [%s]\n", scsiErrString(res));
02154     return res;
02155 }
02156 
02157 int
02158 scsiSmartExtendSelfTest(scsi_device * device)
02159 {
02160     int res;
02161 
02162     res = scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, NULL, 0);
02163     if (res)
02164         pout("Long (extended) offline self test failed [%s]\n",
02165              scsiErrString(res));
02166     return res;
02167 }
02168 
02169 int
02170 scsiSmartShortCapSelfTest(scsi_device * device)
02171 {
02172     int res;
02173 
02174     res = scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0);
02175     if (res)
02176         pout("Short foreground self test failed [%s]\n", scsiErrString(res));
02177     return res;
02178 }
02179 
02180 int
02181 scsiSmartExtendCapSelfTest(scsi_device * device)
02182 {
02183     int res;
02184 
02185     res = scsiSendDiagnostic(device, SCSI_DIAG_FG_EXTENDED_SELF_TEST, NULL, 0);
02186     if (res)
02187         pout("Long (extended) foreground self test failed [%s]\n",
02188              scsiErrString(res));
02189     return res;
02190 }
02191 
02192 int
02193 scsiSmartSelfTestAbort(scsi_device * device)
02194 {
02195     int res;
02196 
02197     res = scsiSendDiagnostic(device, SCSI_DIAG_ABORT_SELF_TEST, NULL, 0);
02198     if (res)
02199         pout("Abort self test failed [%s]\n", scsiErrString(res));
02200     return res;
02201 }
02202 
02203 /* Returns 0 and the expected duration of an extended self test (in seconds)
02204    if successful; any other return value indicates a failure. */
02205 int
02206 scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
02207                               int modese_len)
02208 {
02209     int err, offset, res;
02210     UINT8 buff[64];
02211 
02212     memset(buff, 0, sizeof(buff));
02213     if (modese_len <= 6) {
02214         if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
02215                                  MPAGE_CONTROL_CURRENT,
02216                                  buff, sizeof(buff)))) {
02217             if (SIMPLE_ERR_BAD_OPCODE == err)
02218                 modese_len = 10;
02219             else
02220                 return err;
02221         } else if (0 == modese_len)
02222             modese_len = 6;
02223     }
02224     if (10 == modese_len) {
02225         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
02226                               MPAGE_CONTROL_CURRENT,
02227                               buff, sizeof(buff));
02228         if (err)
02229             return err;
02230     }
02231     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02232     if (offset < 0)
02233         return -EINVAL;
02234     if (buff[offset + 1] >= 0xa) {
02235         res = (buff[offset + 10] << 8) | buff[offset + 11];
02236         *durationSec = res;
02237         return 0;
02238     }
02239     else
02240         return -EINVAL;
02241 }
02242 
02243 void
02244 scsiDecodeErrCounterPage(unsigned char * resp, struct scsiErrorCounter *ecp)
02245 {
02246     int k, j, num, pl, pc;
02247     unsigned char * ucp;
02248     unsigned char * xp;
02249     uint64_t * ullp;
02250 
02251     memset(ecp, 0, sizeof(*ecp));
02252     num = (resp[2] << 8) | resp[3];
02253     ucp = &resp[0] + 4;
02254     while (num > 3) {
02255         pc = (ucp[0] << 8) | ucp[1];
02256         pl = ucp[3] + 4;
02257         switch (pc) {
02258             case 0:
02259             case 1:
02260             case 2:
02261             case 3:
02262             case 4:
02263             case 5:
02264             case 6:
02265                 ecp->gotPC[pc] = 1;
02266                 ullp = &ecp->counter[pc];
02267                 break;
02268         default:
02269                 ecp->gotExtraPC = 1;
02270                 ullp = &ecp->counter[7];
02271                 break;
02272         }
02273         k = pl - 4;
02274         xp = ucp + 4;
02275         if (k > (int)sizeof(*ullp)) {
02276             xp += (k - sizeof(*ullp));
02277             k = sizeof(*ullp);
02278         }
02279         *ullp = 0;
02280         for (j = 0; j < k; ++j) {
02281             if (j > 0)
02282                 *ullp <<= 8;
02283             *ullp |= xp[j];
02284         }
02285         num -= pl;
02286         ucp += pl;
02287     }
02288 }
02289 
02290 void
02291 scsiDecodeNonMediumErrPage(unsigned char *resp,
02292                            struct scsiNonMediumError *nmep)
02293 {
02294     int k, j, num, pl, pc, szof;
02295     unsigned char * ucp;
02296     unsigned char * xp;
02297 
02298     memset(nmep, 0, sizeof(*nmep));
02299     num = (resp[2] << 8) | resp[3];
02300     ucp = &resp[0] + 4;
02301     szof = sizeof(nmep->counterPC0);
02302     while (num > 3) {
02303         pc = (ucp[0] << 8) | ucp[1];
02304         pl = ucp[3] + 4;
02305         switch (pc) {
02306             case 0:
02307                 nmep->gotPC0 = 1;
02308                 k = pl - 4;
02309                 xp = ucp + 4;
02310                 if (k > szof) {
02311                     xp += (k - szof);
02312                     k = szof;
02313                 }
02314                 nmep->counterPC0 = 0;
02315                 for (j = 0; j < k; ++j) {
02316                     if (j > 0)
02317                         nmep->counterPC0 <<= 8;
02318                     nmep->counterPC0 |= xp[j];
02319                 }
02320                 break;
02321             case 0x8009:
02322                 nmep->gotTFE_H = 1;
02323                 k = pl - 4;
02324                 xp = ucp + 4;
02325                 if (k > szof) {
02326                     xp += (k - szof);
02327                     k = szof;
02328                 }
02329                 nmep->counterTFE_H = 0;
02330                 for (j = 0; j < k; ++j) {
02331                     if (j > 0)
02332                         nmep->counterTFE_H <<= 8;
02333                     nmep->counterTFE_H |= xp[j];
02334                 }
02335                 break;
02336             case 0x8015:
02337                 nmep->gotPE_H = 1;
02338                 k = pl - 4;
02339                 xp = ucp + 4;
02340                 if (k > szof) {
02341                     xp += (k - szof);
02342                     k = szof;
02343                 }
02344                 nmep->counterPE_H = 0;
02345                 for (j = 0; j < k; ++j) {
02346                     if (j > 0)
02347                         nmep->counterPE_H <<= 8;
02348                     nmep->counterPE_H |= xp[j];
02349                 }
02350                 break;
02351         default:
02352                 nmep->gotExtraPC = 1;
02353                 break;
02354         }
02355         num -= pl;
02356         ucp += pl;
02357     }
02358 }
02359 
02360 /* Counts number of failed self-tests. Also encodes the poweron_hour
02361    of the most recent failed self-test. Return value is negative if
02362    this function has a problem (typically -1), otherwise the bottom 8
02363    bits are the number of failed self tests and the 16 bits above that
02364    are the poweron hour of the most recent failure. Note: aborted self
02365    tests (typically by the user) and self tests in progress are not
02366    considered failures. See Working Draft SCSI Primary Commands - 3
02367    (SPC-3) section 7.2.10 T10/1416-D (rev 22a) */
02368 int
02369 scsiCountFailedSelfTests(scsi_device * fd, int noisy)
02370 {
02371     int num, k, n, err, res, fails, fail_hour;
02372     UINT8 * ucp;
02373     unsigned char resp[LOG_RESP_SELF_TEST_LEN];
02374 
02375     if ((err = scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp,
02376                             LOG_RESP_SELF_TEST_LEN, 0))) {
02377         if (noisy)
02378             pout("scsiCountSelfTests Failed [%s]\n", scsiErrString(err));
02379         return -1;
02380     }
02381     if ((resp[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
02382         if (noisy)
02383             pout("Self-test Log Sense Failed, page mismatch\n");
02384         return -1;
02385     }
02386     // compute page length
02387     num = (resp[2] << 8) + resp[3];
02388     // Log sense page length 0x190 bytes
02389     if (num != 0x190) {
02390         if (noisy)
02391             pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n", num);
02392         return -1;
02393     }
02394     fails = 0;
02395     fail_hour = 0;
02396     // loop through the twenty possible entries
02397     for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) {
02398 
02399         // timestamp in power-on hours (or zero if test in progress)
02400         n = (ucp[6] << 8) | ucp[7];
02401 
02402         // The spec says "all 20 bytes will be zero if no test" but
02403         // DG has found otherwise.  So this is a heuristic.
02404         if ((0 == n) && (0 == ucp[4]))
02405             break;
02406         res = ucp[4] & 0xf;
02407         if ((res > 2) && (res < 8)) {
02408             fails++;
02409             if (1 == fails)
02410                 fail_hour = (ucp[6] << 8) + ucp[7];
02411         }
02412     }
02413     return (fail_hour << 8) + fails;
02414 }
02415 
02416 /* Returns 0 if able to read self test log page; then outputs 1 into
02417    *inProgress if self test still in progress, else outputs 0. */
02418 int
02419 scsiSelfTestInProgress(scsi_device * fd, int * inProgress)
02420 {
02421     int num;
02422     UINT8 * ucp;
02423     unsigned char resp[LOG_RESP_SELF_TEST_LEN];
02424 
02425     if (scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp,
02426                      LOG_RESP_SELF_TEST_LEN, 0))
02427         return -1;
02428     if (resp[0] != SELFTEST_RESULTS_LPAGE)
02429         return -1;
02430     // compute page length
02431     num = (resp[2] << 8) + resp[3];
02432     // Log sense page length 0x190 bytes
02433     if (num != 0x190) {
02434         return -1;
02435     }
02436     ucp = resp + 4;
02437     if (inProgress)
02438         *inProgress = (0xf == (ucp[4] & 0xf)) ? 1 : 0;
02439     return 0;
02440 }
02441 
02442 /* Returns a negative value if failed to fetch Contol mode page or it was
02443    malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD
02444    bit is set. Examines default mode page when current==0 else examines
02445    current mode page. */
02446 int
02447 scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current)
02448 {
02449     int err, offset;
02450     UINT8 buff[64];
02451     int pc = current ? MPAGE_CONTROL_CURRENT : MPAGE_CONTROL_DEFAULT;
02452 
02453     memset(buff, 0, sizeof(buff));
02454     if (modese_len <= 6) {
02455         if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0, pc,
02456                                  buff, sizeof(buff)))) {
02457             if (SIMPLE_ERR_BAD_OPCODE == err)
02458                 modese_len = 10;
02459             else
02460                 return -EINVAL;
02461         } else if (0 == modese_len)
02462             modese_len = 6;
02463     }
02464     if (10 == modese_len) {
02465         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0, pc,
02466                               buff, sizeof(buff));
02467         if (err)
02468             return -EINVAL;
02469     }
02470     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02471     if ((offset >= 0) && (buff[offset + 1] >= 0xa))
02472         return (buff[offset + 2] & 2) ? 1 : 0;
02473     return -EINVAL;
02474 }
02475 
02476 /* Returns a negative value on error, 0 if unknown and 1 if SSD,
02477  * otherwise the positive returned value is the speed in rpm. First checks
02478  * the Block Device Characteristics VPD page and if that fails it tries the
02479  * RIGID_DISK_DRIVE_GEOMETRY_PAGE mode page. */
02480 
02481 int
02482 scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp)
02483 {
02484     int err, offset, speed;
02485     UINT8 buff[64];
02486     int pc = MPAGE_CONTROL_DEFAULT;
02487 
02488     memset(buff, 0, sizeof(buff));
02489     if ((0 == scsiInquiryVpd(device, SCSI_VPD_BLOCK_DEVICE_CHARACTERISTICS,
02490                              buff, sizeof(buff))) &&
02491         (((buff[2] << 8) + buff[3]) > 2)) {
02492         speed = (buff[4] << 8) + buff[5];
02493         if (form_factorp)
02494             *form_factorp = buff[7] & 0xf;
02495         return speed;
02496     }
02497     if (form_factorp)
02498         *form_factorp = 0;
02499     if (modese_len <= 6) {
02500         if ((err = scsiModeSense(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc,
02501                                  buff, sizeof(buff)))) {
02502             if (SIMPLE_ERR_BAD_OPCODE == err)
02503                 modese_len = 10;
02504             else
02505                 return -EINVAL;
02506         } else if (0 == modese_len)
02507             modese_len = 6;
02508     }
02509     if (10 == modese_len) {
02510         err = scsiModeSense10(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc,
02511                               buff, sizeof(buff));
02512         if (err)
02513             return -EINVAL;
02514     }
02515     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02516     return (buff[offset + 20] << 8) | buff[offset + 21];
02517 }
02518 
02519 /* Returns a non-zero value in case of error, wcep/rcdp == -1 - get value,
02520    0 - clear bit, 1 - set bit  */
02521 
02522 int
02523 scsiGetSetCache(scsi_device * device,  int modese_len, short int * wcep,
02524                 short int * rcdp)
02525 {
02526     int err, offset, resp_len, sp;
02527     UINT8 buff[64], ch_buff[64];
02528     short set_wce = *wcep;
02529     short set_rcd = *rcdp;
02530 
02531     memset(buff, 0, sizeof(buff));
02532     if (modese_len <= 6) {
02533         if ((err = scsiModeSense(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT,
02534                                  buff, sizeof(buff)))) {
02535             if (SIMPLE_ERR_BAD_OPCODE == err)
02536                 modese_len = 10;
02537             else {
02538                 device->set_err(EINVAL, "SCSI MODE SENSE failed");
02539                 return -EINVAL;
02540             }
02541         } else if (0 == modese_len)
02542             modese_len = 6;
02543     }
02544 
02545     if (10 == modese_len) {
02546         err = scsiModeSense10(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT,
02547                               buff, sizeof(buff));
02548         if (err) {
02549             device->set_err(EINVAL, "SCSI MODE SENSE failed");
02550             return -EINVAL;
02551         }
02552     }
02553     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02554     if ((offset < 0) || (buff[offset + 1] < 0xa)) {
02555         device->set_err(EINVAL, "Bad response");
02556         return SIMPLE_ERR_BAD_RESP;
02557     }
02558 
02559     *wcep = ((buff[offset + 2] & 0x04) != 0);
02560     *rcdp = ((buff[offset + 2] & 0x01) != 0);
02561 
02562     if((*wcep == set_wce || set_wce == -1)
02563           && ((*rcdp == set_rcd) || set_rcd == -1))
02564       return 0; // no changes needed or nothing to set
02565 
02566     if (modese_len == 6)
02567         err = scsiModeSense(device, CACHING_PAGE, 0,
02568                             MPAGE_CONTROL_CHANGEABLE,
02569                             ch_buff, sizeof(ch_buff));
02570     else
02571         err = scsiModeSense10(device, CACHING_PAGE, 0,
02572                               MPAGE_CONTROL_CHANGEABLE,
02573                               ch_buff, sizeof(ch_buff));
02574     if (err) {
02575         device->set_err(EINVAL, "WCE/RCD bits not changable");
02576         return err;
02577     }
02578 
02579     // set WCE bit
02580     if(set_wce >= 0 && *wcep != set_wce) {
02581        if (0 == (ch_buff[offset + 2] & 0x04)) {
02582          device->set_err(EINVAL, "WCE bit not changable");
02583          return 1;
02584        }
02585        if(set_wce)
02586           buff[offset + 2] |= 0x04; // set bit
02587        else
02588           buff[offset + 2] &= 0xfb; // clear bit
02589     }
02590     // set RCD bit
02591     if(set_rcd >= 0 && *rcdp != set_rcd) {
02592        if (0 == (ch_buff[offset + 2] & 0x01)) {
02593          device->set_err(EINVAL, "RCD bit not changable");
02594          return 1;
02595        }
02596        if(set_rcd)
02597           buff[offset + 2] |= 0x01; // set bit
02598        else
02599           buff[offset + 2] &= 0xfe; // clear bit
02600     }
02601 
02602     if (10 == modese_len) {
02603         resp_len = (buff[0] << 8) + buff[1] + 2;
02604         buff[3] &= 0xef;    /* for disks mask out DPOFUA bit */
02605     } else {
02606         resp_len = buff[0] + 1;
02607         buff[2] &= 0xef;    /* for disks mask out DPOFUA bit */
02608     }
02609     sp = 0; /* Do not change saved values */
02610     if (10 == modese_len)
02611         err = scsiModeSelect10(device, sp, buff, resp_len);
02612     else if (6 == modese_len)
02613         err = scsiModeSelect(device, sp, buff, resp_len);
02614     if(err)
02615       device->set_err(EINVAL, "MODE SELECT command failed");
02616     return err;
02617 }
02618 
02619 
02620 /* Attempts to set or clear GLTSD bit in Control mode page. If enabled is
02621    0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if
02622    successful, negative if low level error, > 0 if higher level error (e.g.
02623    SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */
02624 int
02625 scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len)
02626 {
02627     int err, offset, resp_len, sp;
02628     UINT8 buff[64];
02629     UINT8 ch_buff[64];
02630 
02631     memset(buff, 0, sizeof(buff));
02632     if (modese_len <= 6) {
02633         if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
02634                                  MPAGE_CONTROL_CURRENT,
02635                                  buff, sizeof(buff)))) {
02636             if (SIMPLE_ERR_BAD_OPCODE == err)
02637                 modese_len = 10;
02638             else
02639                 return err;
02640         } else if (0 == modese_len)
02641             modese_len = 6;
02642     }
02643     if (10 == modese_len) {
02644         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
02645                               MPAGE_CONTROL_CURRENT,
02646                               buff, sizeof(buff));
02647         if (err)
02648             return err;
02649     }
02650     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02651     if ((offset < 0) || (buff[offset + 1] < 0xa))
02652         return SIMPLE_ERR_BAD_RESP;
02653 
02654     if (enabled)
02655         enabled = 2;
02656     if (enabled == (buff[offset + 2] & 2))
02657         return 0;       /* GLTSD already in wanted state so nothing to do */
02658 
02659     if (modese_len == 6)
02660         err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
02661                             MPAGE_CONTROL_CHANGEABLE,
02662                             ch_buff, sizeof(ch_buff));
02663     else
02664         err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
02665                               MPAGE_CONTROL_CHANGEABLE,
02666                               ch_buff, sizeof(ch_buff));
02667     if (err)
02668         return err;
02669     if (0 == (ch_buff[offset + 2] & 2))
02670         return SIMPLE_ERR_BAD_PARAM;  /* GLTSD bit not chageable */
02671 
02672     if (10 == modese_len) {
02673         resp_len = (buff[0] << 8) + buff[1] + 2;
02674         buff[3] &= 0xef;    /* for disks mask out DPOFUA bit */
02675     } else {
02676         resp_len = buff[0] + 1;
02677         buff[2] &= 0xef;    /* for disks mask out DPOFUA bit */
02678     }
02679     sp = (buff[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
02680     if (enabled)
02681         buff[offset + 2] |= 0x2;    /* set GLTSD bit */
02682     else
02683         buff[offset + 2] &= 0xfd;   /* clear GLTSD bit */
02684     if (10 == modese_len)
02685         err = scsiModeSelect10(device, sp, buff, resp_len);
02686     else if (6 == modese_len)
02687         err = scsiModeSelect(device, sp, buff, resp_len);
02688     return err;
02689 }
02690 
02691 /* Returns a negative value if failed to fetch Protocol specific port mode
02692    page or it was malformed. Returns transport protocol identifier when
02693    value >= 0 . */
02694 int
02695 scsiFetchTransportProtocol(scsi_device * device, int modese_len)
02696 {
02697     int err, offset;
02698     UINT8 buff[64];
02699 
02700     memset(buff, 0, sizeof(buff));
02701     if (modese_len <= 6) {
02702         if ((err = scsiModeSense(device, PROTOCOL_SPECIFIC_PORT_PAGE, 0,
02703                                  MPAGE_CONTROL_CURRENT,
02704                                  buff, sizeof(buff)))) {
02705             if (SIMPLE_ERR_BAD_OPCODE == err)
02706                 modese_len = 10;
02707             else
02708                 return -EINVAL;
02709         } else if (0 == modese_len)
02710             modese_len = 6;
02711     }
02712     if (10 == modese_len) {
02713         err = scsiModeSense10(device, PROTOCOL_SPECIFIC_PORT_PAGE, 0,
02714                               MPAGE_CONTROL_CURRENT,
02715                               buff, sizeof(buff));
02716         if (err)
02717             return -EINVAL;
02718     }
02719     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
02720     if ((offset >= 0) && (buff[offset + 1] > 1)) {
02721         if ((0 == (buff[offset] & 0x40)) &&       /* SPF==0 */
02722             (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f)))
02723                 return (buff[offset + 2] & 0xf);
02724     }
02725     return -EINVAL;
02726 }
02727 
02728 const unsigned char *
02729 sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len,
02730                         int desc_type)
02731 {
02732     int add_sen_len, add_len, desc_len, k;
02733     const unsigned char * descp;
02734 
02735     if ((sense_len < 8) || (0 == (add_sen_len = sensep[7])))
02736         return NULL;
02737     if ((sensep[0] < 0x72) || (sensep[0] > 0x73))
02738         return NULL;
02739     add_sen_len = (add_sen_len < (sense_len - 8)) ?
02740                          add_sen_len : (sense_len - 8);
02741     descp = &sensep[8];
02742     for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
02743         descp += desc_len;
02744         add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
02745         desc_len = add_len + 2;
02746         if (descp[0] == desc_type)
02747             return descp;
02748         if (add_len < 0) /* short descriptor ?? */
02749             break;
02750     }
02751     return NULL;
02752 }
02753 
02754 // Convenience function for formatting strings from SCSI identify
02755 void
02756 scsi_format_id_string(char * out, const unsigned char * in, int n)
02757 {
02758   char tmp[65];
02759   n = n > 64 ? 64 : n;
02760   strncpy(tmp, (const char *)in, n);
02761   tmp[n] = '\0';
02762 
02763   // Find the first non-space character (maybe none).
02764   int first = -1;
02765   int i;
02766   for (i = 0; tmp[i]; i++)
02767     if (!isspace((int)tmp[i])) {
02768       first = i;
02769       break;
02770     }
02771 
02772   if (first == -1) {
02773     // There are no non-space characters.
02774     out[0] = '\0';
02775     return;
02776   }
02777 
02778   // Find the last non-space character.
02779   for (i = strlen(tmp)-1; i >= first && isspace((int)tmp[i]); i--);
02780   int last = i;
02781 
02782   strncpy(out, tmp+first, last-first+1);
02783   out[last-first+1] = '\0';
02784 }