]> git.proxmox.com Git - mirror_smartmontools-debian.git/blobdiff - scsicmds.cpp
Updated changelog
[mirror_smartmontools-debian.git] / scsicmds.cpp
index afb7bdb0e915b54425eeaa3283168703f1c21938..30a935641f4a46e196a046e3bd12a1bb95114534 100644 (file)
@@ -3,11 +3,11 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
  *
  * Additional SCSI work:
- * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
+ * Copyright (C) 2003-10 Douglas Gilbert <dgilbert@interlog.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 
 #include <stdio.h>
 #include <string.h>
+#include <errno.h>
 
 #include "config.h"
 #include "int64.h"
-#include "extern.h"
 #include "scsicmds.h"
+#include "atacmds.h" // FIXME: for smart_command_set only
+#include "dev_interface.h"
 #include "utility.h"
 
-const char *scsicmds_c_cvsid="$Id: scsicmds.cpp,v 1.91 2006/09/12 00:25:44 dpgilbert Exp $"
-CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+const char *scsicmds_c_cvsid="$Id: scsicmds.cpp 3302 2011-03-25 23:04:36Z dpgilbert $"
+  SCSICMDS_H_CVSID;
 
-/* for passing global control variables */
-extern smartmonctrl *con;
+// Print SCSI debug messages?
+unsigned char scsi_debugmode = 0;
 
 /* output binary in hex and optionally ascii */
 void dStrHex(const char* str, int len, int no_ascii)
@@ -116,24 +118,35 @@ static struct scsi_opcode_name opcode_name_arr[] = {
     {TEST_UNIT_READY, "test unit ready"},       /* 0x00 */
     {REQUEST_SENSE, "request sense"},           /* 0x03 */
     {INQUIRY, "inquiry"},                       /* 0x12 */
-    {MODE_SELECT, "mode select"},               /* 0x15 */
-    {MODE_SENSE, "mode sense"},                 /* 0x1a */
+    {MODE_SELECT, "mode select(6)"},            /* 0x15 */
+    {MODE_SENSE, "mode sense(6)"},              /* 0x1a */
+    {START_STOP_UNIT, "start stop unit"},       /* 0x1b */
     {RECEIVE_DIAGNOSTIC, "receive diagnostic"}, /* 0x1c */
     {SEND_DIAGNOSTIC, "send diagnostic"},       /* 0x1d */
+    {READ_CAPACITY_10, "read capacity(10)"},    /* 0x25 */
     {READ_DEFECT_10, "read defect list(10)"},   /* 0x37 */
+    {LOG_SELECT, "log select"},                 /* 0x4c */
     {LOG_SENSE, "log sense"},                   /* 0x4d */
     {MODE_SELECT_10, "mode select(10)"},        /* 0x55 */
     {MODE_SENSE_10, "mode sense(10)"},          /* 0x5a */
     {SAT_ATA_PASSTHROUGH_16, "ata pass-through(16)"}, /* 0x85 */
+    {READ_CAPACITY_16, "read capacity(16)"},    /* 0x9e,0x10 */
+    {REPORT_LUNS, "report luns"},               /* 0xa0 */
     {SAT_ATA_PASSTHROUGH_12, "ata pass-through(12)"}, /* 0xa1 */
 };
 
+static const char * vendor_specific = "<vendor specific>";
+
+/* Need to expand to take service action into account. For commands
+ * of interest the service action is in the 2nd command byte */
 const char * scsi_get_opcode_name(UINT8 opcode)
 {
     int k;
     int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]);
     struct scsi_opcode_name * onp;
 
+    if (opcode >= 0xc0)
+        return vendor_specific;
     for (k = 0; k < len; ++k) {
         onp = &opcode_name_arr[k];
         if (opcode == onp->opcode)
@@ -171,6 +184,9 @@ void scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf,
 int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo)
 {
     switch (sinfo->sense_key) {
+    case SCSI_SK_NO_SENSE:
+    case SCSI_SK_RECOVERED_ERR:
+        return SIMPLE_NO_ERROR;
     case SCSI_SK_NOT_READY:
         if (SCSI_ASC_NO_MEDIUM == sinfo->asc) 
             return SIMPLE_ERR_NO_MEDIUM;
@@ -195,8 +211,10 @@ int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo)
             return SIMPLE_ERR_BAD_PARAM;    /* all other illegal request */
     case SCSI_SK_UNIT_ATTENTION:
         return SIMPLE_ERR_TRY_AGAIN;
+    case SCSI_SK_ABORTED_COMMAND:
+        return SIMPLE_ERR_ABORTED_COMMAND;
     default:
-        return SIMPLE_NO_ERROR;
+        return SIMPLE_ERR_UNKNOWN;
     }
 }
 
@@ -225,11 +243,168 @@ const char * scsiErrString(int scsiErr)
             return "unit attention reported, try again";
         case SIMPLE_ERR_MEDIUM_HARDWARE: 
             return "medium or hardware error (serious)";
+        case SIMPLE_ERR_UNKNOWN: 
+            return "unknown error (unexpected sense key)";
+        case SIMPLE_ERR_ABORTED_COMMAND: 
+            return "aborted command";
         default:
             return "unknown error";
     }
 }
 
+/* Iterates to next designation descriptor in the device identification
+ * VPD page. The 'initial_desig_desc' should point to start of first
+ * descriptor with 'page_len' being the number of valid bytes in that
+ * and following descriptors. To start, 'off' should point to a negative
+ * value, thereafter it should point to the value yielded by the previous
+ * call. If 0 returned then 'initial_desig_desc + *off' should be a valid
+ * descriptor; returns -1 if normal end condition and -2 for an abnormal
+ * termination. Matches association, designator_type and/or code_set when
+ * any of those values are greater than or equal to zero. */
+int scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc,
+                        int page_len, int * off, int m_assoc,
+                        int m_desig_type, int m_code_set)
+{
+    const unsigned char * ucp;
+    int k, c_set, assoc, desig_type;
+
+    for (k = *off, ucp = initial_desig_desc ; (k + 3) < page_len; ) {
+        k = (k < 0) ? 0 : (k + ucp[k + 3] + 4);
+        if ((k + 4) > page_len)
+            break;
+        c_set = (ucp[k] & 0xf);
+        if ((m_code_set >= 0) && (m_code_set != c_set))
+            continue;
+        assoc = ((ucp[k + 1] >> 4) & 0x3);
+        if ((m_assoc >= 0) && (m_assoc != assoc))
+            continue;
+        desig_type = (ucp[k + 1] & 0xf);
+        if ((m_desig_type >= 0) && (m_desig_type != desig_type))
+            continue;
+        *off = k;
+        return 0;
+    }
+    return (k == page_len) ? -1 : -2;
+}
+
+/* Decode VPD page 0x83 logical unit designator into a string. If both
+ * numeric address and SCSI name string present, prefer the former.
+ * Returns 0 on success, -1 on error with error string in s. */
+int scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s,
+                         int slen, int * transport)
+{
+    int m, c_set, assoc, desig_type, i_len, naa, off, u, have_scsi_ns;
+    const unsigned char * ucp;
+    const unsigned char * ip;
+    char * orig_s = s;
+
+    if (transport)
+       *transport = -1;
+    if (slen < 32) {
+       if (slen > 0)
+           s[0] = '\0';
+       return -1;
+    }
+    have_scsi_ns = 0;
+    s[0] = '\0';
+    off = -1;
+    while ((u = scsi_vpd_dev_id_iter(b, blen, &off, -1, -1, -1)) == 0) {
+        ucp = b + off;
+        i_len = ucp[3];
+        if ((off + i_len + 4) > blen) {
+           s += sprintf(s, "error: designator length");
+           return -1;
+        }
+        assoc = ((ucp[1] >> 4) & 0x3);
+       if (transport && assoc && (ucp[1] & 0x80) && (*transport < 0))
+           *transport = (ucp[0] >> 4) & 0xf;
+       if (0 != assoc)
+           continue;
+        ip = ucp + 4;
+        c_set = (ucp[0] & 0xf);
+        desig_type = (ucp[1] & 0xf);
+
+        switch (desig_type) {
+        case 0: /* vendor specific */
+        case 1: /* T10 vendor identification */
+            break;
+        case 2: /* EUI-64 based */
+            if ((8 != i_len) && (12 != i_len) && (16 != i_len)) {
+               s += sprintf(s, "error: EUI-64 length");
+               return -1;
+           }
+           if (have_scsi_ns)
+               s = orig_s;
+            s += sprintf(s, "0x");
+            for (m = 0; m < i_len; ++m)
+                s += sprintf(s, "%02x", (unsigned int)ip[m]);
+            break;
+        case 3: /* NAA */
+            if (1 != c_set) {
+               s += sprintf(s, "error: NAA bad code_set");
+               return -1;
+           }
+            naa = (ip[0] >> 4) & 0xff;
+            if ((naa < 2) || (naa > 6) || (4 == naa)) {
+               s += sprintf(s, "error: unexpected NAA");
+               return -1;
+            }
+           if (have_scsi_ns)
+               s = orig_s;
+            if (2 == naa) {             /* NAA IEEE Extended */
+                if (8 != i_len) {
+                   s += sprintf(s, "error: NAA 2 length");
+                   return -1;
+                }
+                s += sprintf(s, "0x");
+                for (m = 0; m < 8; ++m)
+                    s += sprintf(s, "%02x", (unsigned int)ip[m]);
+            } else if ((3 == naa ) || (5 == naa)) {
+                /* NAA=3 Locally assigned; NAA=5 IEEE Registered */
+                if (8 != i_len) {
+                   s += sprintf(s, "error: NAA 3 or 5 length");
+                   return -1;
+                }
+                s += sprintf(s, "0x");
+                for (m = 0; m < 8; ++m)
+                    s += sprintf(s, "%02x", (unsigned int)ip[m]);
+            } else if (6 == naa) {      /* NAA IEEE Registered extended */
+                if (16 != i_len) {
+                   s += sprintf(s, "error: NAA 6 length");
+                   return -1;
+                }
+                s += sprintf(s, "0x");
+                for (m = 0; m < 16; ++m)
+                    s += sprintf(s, "%02x", (unsigned int)ip[m]);
+            }
+            break;
+        case 4: /* Relative target port */
+        case 5: /* (primary) Target port group */
+        case 6: /* Logical unit group */
+        case 7: /* MD5 logical unit identifier */
+            break;
+        case 8: /* SCSI name string */
+            if (3 != c_set) {
+               s += sprintf(s, "error: SCSI name string");
+               return -1;
+            }
+            /* does %s print out UTF-8 ok?? */
+           if (orig_s == s) {
+                s += sprintf(s, "%s", (const char *)ip);
+               ++have_scsi_ns;
+           }
+            break;
+        default: /* reserved */
+            break;
+        }
+    }
+    if (-2 == u) {
+        s += sprintf(s, "error: bad structure");
+       return -1;
+    }
+    return 0;
+}
+
 /* Sends LOG SENSE command. Returns 0 if ok, 1 if device NOT READY, 2 if
    command not supported, 3 if field (within command) not supported or
    returns negated errno.  SPC-3 sections 6.6 and 7.2 (rec 22a).
@@ -240,7 +415,7 @@ const char * scsiErrString(int scsiErr)
    requesting the deduced response length. This protects certain fragile 
    HBAs. The twin fetch technique should not be used with the TapeAlert
    log page since it clears its state flags after each fetch. */
-int scsiLogSense(int device, int pagenum, int subpagenum, UINT8 *pBuf,
+int scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
                  int bufLen, int known_resp_len)
 {
     struct scsi_cmnd_io io_hdr;
@@ -277,19 +452,20 @@ int scsiLogSense(int device, int pagenum, int subpagenum, UINT8 *pBuf,
         io_hdr.sensep = sense;
         io_hdr.max_sense_len = sizeof(sense);
         io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-    
-        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-        if (0 != status)
-            return status;
+
+        if (!device->scsi_pass_through(&io_hdr))
+          return -device->get_errno();
         scsi_do_sense_disect(&io_hdr, &sinfo);
         if ((res = scsiSimpleSenseFilter(&sinfo)))
             return res;
         /* sanity check on response */
-        if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum))
+        if ((SUPPORTED_LPAGES != pagenum) && ((pBuf[0] & 0x3f) != pagenum))
             return SIMPLE_ERR_BAD_RESP;
         if (0 == ((pBuf[2] << 8) + pBuf[3]))
             return SIMPLE_ERR_BAD_RESP;
         pageLen = (pBuf[2] << 8) + pBuf[3] + 4;
+        if (4 == pageLen)  /* why define a lpage with no payload? */
+            pageLen = 252; /* some IBM tape drives don't like double fetch */
         /* some SCSI HBA don't like "odd" length transfers */
         if (pageLen % 2)
             pageLen += 1;   
@@ -312,26 +488,61 @@ int scsiLogSense(int device, int pagenum, int subpagenum, UINT8 *pBuf,
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     status = scsiSimpleSenseFilter(&sinfo);
     if (0 != status)
         return status;
     /* sanity check on response */
-    if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum))
+    if ((SUPPORTED_LPAGES != pagenum) && ((pBuf[0] & 0x3f) != pagenum))
         return SIMPLE_ERR_BAD_RESP;
     if (0 == ((pBuf[2] << 8) + pBuf[3]))
         return SIMPLE_ERR_BAD_RESP;
     return 0;
 }
 
+/* Sends a LOG SELECT command. Can be used to set log page values
+ * or reset one log page (or all of them) to its defaults (typically zero).
+ * Returns 0 if ok, 1 if NOT READY, 2 if command not supported, * 3 if
+ * field in command not supported, * 4 if bad parameter to command or
+ * returns negated errno. SPC-4 sections 6.5 and 7.2 (rev 20) */
+int scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,
+                  int subpagenum, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = LOG_SELECT;
+    cdb[1] = (pcr ? 2 : 0) | (sp ? 1 : 0);
+    cdb[2] = ((pc << 6) & 0xc0) | (pagenum & 0x3f);
+    cdb[3] = (subpagenum & 0xff);
+    cdb[7] = ((bufLen >> 8) & 0xff);
+    cdb[8] = (bufLen & 0xff);
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
 /* Send MODE SENSE (6 byte) command. Returns 0 if ok, 1 if NOT READY,
  * 2 if command not supported (then MODE SENSE(10) should be supported),
  * 3 if field in command not supported or returns negated errno. 
  * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */
-int scsiModeSense(int device, int pagenum, int subpagenum, int pc,
+int scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
                   UINT8 *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
@@ -357,15 +568,13 @@ int scsiModeSense(int device, int pagenum, int subpagenum, int pc,
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     status = scsiSimpleSenseFilter(&sinfo);
     if (SIMPLE_ERR_TRY_AGAIN == status) {
-        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-        if (0 != status)
-            return status;
+        if (!device->scsi_pass_through(&io_hdr))
+          return -device->get_errno();
         scsi_do_sense_disect(&io_hdr, &sinfo);
         status = scsiSimpleSenseFilter(&sinfo);
     }
@@ -388,13 +597,13 @@ int scsiModeSense(int device, int pagenum, int subpagenum, int pc,
  * 2 if command not supported (then MODE SELECT(10) may be supported), 
  * 3 if field in command not supported, 4 if bad parameter to command
  * or returns negated errno. SPC-3 sections 6.7 and 7.4 (rev 22a) */
-int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen)
+int scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
     UINT8 cdb[6];
     UINT8 sense[32];
-    int status, pg_offset, pg_len, hdr_plus_1_pg;
+    int pg_offset, pg_len, hdr_plus_1_pg;
 
     pg_offset = 4 + pBuf[3];
     if (pg_offset + 2 >= bufLen)
@@ -419,9 +628,8 @@ int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen)
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
@@ -430,7 +638,7 @@ int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen)
  * not supported (then MODE SENSE(6) might be supported), 3 if field in
  * command not supported or returns negated errno.  
  * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */
-int scsiModeSense10(int device, int pagenum, int subpagenum, int pc,
+int scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
                     UINT8 *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
@@ -455,15 +663,13 @@ int scsiModeSense10(int device, int pagenum, int subpagenum, int pc,
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     status = scsiSimpleSenseFilter(&sinfo);
     if (SIMPLE_ERR_TRY_AGAIN == status) {
-        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-        if (0 != status)
-            return status;
+        if (!device->scsi_pass_through(&io_hdr))
+          return -device->get_errno();
         scsi_do_sense_disect(&io_hdr, &sinfo);
         status = scsiSimpleSenseFilter(&sinfo);
     }
@@ -486,13 +692,13 @@ int scsiModeSense10(int device, int pagenum, int subpagenum, int pc,
  * command not supported (then MODE SELECT(6) may be supported), 3 if field
  * in command not supported, 4 if bad parameter to command or returns
  * negated errno. SPC-3 sections 6.8 and 7.4 (rev 22a) */
-int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen)
+int scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
     UINT8 cdb[10];
     UINT8 sense[32];
-    int status, pg_offset, pg_len, hdr_plus_1_pg;
+    int pg_offset, pg_len, hdr_plus_1_pg;
 
     pg_offset = 8 + (pBuf[6] << 8) + pBuf[7];
     if (pg_offset + 2 >= bufLen)
@@ -518,9 +724,8 @@ int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen)
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
@@ -528,13 +733,12 @@ int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen)
 /* Standard INQUIRY returns 0 for ok, anything else is a major problem.
  * bufLen should be 36 for unsafe devices (like USB mass storage stuff)
  * otherwise they can lock up! SPC-3 sections 6.4 and 7.6 (rev 22a) */
-int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen)
+int scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen)
 {
     struct scsi_sense_disect sinfo;
     struct scsi_cmnd_io io_hdr;
     UINT8 cdb[6];
     UINT8 sense[32];
-    int status;
 
     if ((bufLen < 0) || (bufLen > 255))
         return -EINVAL;
@@ -551,9 +755,8 @@ int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen)
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
@@ -562,13 +765,13 @@ int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen)
  * (unlikely), 2 if command not supported, 3 if field in command not 
  * supported, 5 if response indicates that EVPD bit ignored or returns
  * negated errno. SPC-3 section 6.4 and 7.6 (rev 22a) */
-int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen)
+int scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
     UINT8 cdb[6];
     UINT8 sense[32];
-    int status, res;
+    int res;
 
     if ((bufLen < 0) || (bufLen > 255))
         return -EINVAL;
@@ -589,9 +792,8 @@ int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen)
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     if ((res = scsiSimpleSenseFilter(&sinfo)))
         return res;
@@ -608,13 +810,13 @@ int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen)
 
 /* REQUEST SENSE command. Returns 0 if ok, anything else major problem.
  * SPC-3 section 6.27 (rev 22a) */
-int scsiRequestSense(int device, struct scsi_sense_disect * sense_info)
+int scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
 {
     struct scsi_cmnd_io io_hdr;
     UINT8 cdb[6];
     UINT8 sense[32];
     UINT8 buff[18];
-    int status, len;
+    int len;
     UINT8 ecode;
 
     memset(&io_hdr, 0, sizeof(io_hdr));
@@ -630,8 +832,9 @@ int scsiRequestSense(int device, struct scsi_sense_disect * sense_info)
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if ((0 == status) && (sense_info)) {
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
+    if (sense_info) {
         ecode = buff[0] & 0x7f;
         sense_info->error_code = ecode;
         sense_info->sense_key = buff[2] & 0xf;
@@ -645,19 +848,18 @@ int scsiRequestSense(int device, struct scsi_sense_disect * sense_info)
             }
         }
     }
-    return status;
+    return 0;
 }
 
 /* SEND DIAGNOSTIC command.  Returns 0 if ok, 1 if NOT READY, 2 if command
  * not supported, 3 if field in command not supported or returns negated
  * errno. SPC-3 section 6.28 (rev 22a) */
-int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen)
+int scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
     UINT8 cdb[6];
     UINT8 sense[32];
-    int status;
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -680,9 +882,8 @@ int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen)
     /* worst case is an extended foreground self test on a big disk */
     io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST;
     
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
@@ -690,14 +891,13 @@ int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen)
 /* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if
  * command not supported, 3 if field in command not supported or returns
  * negated errno. SPC-3 section 6.18 (rev 22a) */
-int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf, 
+int scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf,
                       int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
     UINT8 cdb[6];
     UINT8 sense[32];
-    int status;
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -715,20 +915,18 @@ int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf,
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
 
 /* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */
-static int _testunitready(int device, struct scsi_sense_disect * sinfo)
+static int _testunitready(scsi_device * device, struct scsi_sense_disect * sinfo)
 {
     struct scsi_cmnd_io io_hdr;
     UINT8 cdb[6];
     UINT8 sense[32];
-    int status;
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -742,16 +940,15 @@ static int _testunitready(int device, struct scsi_sense_disect * sinfo)
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, sinfo);
     return 0;
 }
 
 /* Returns 0 for device responds and media ready, 1 for device responds and
    media not ready, or returns a negated errno value */
-int scsiTestUnitReady(int device)
+int scsiTestUnitReady(scsi_device * device)
 {
     struct scsi_sense_disect sinfo;
     int status;
@@ -773,14 +970,13 @@ int scsiTestUnitReady(int device)
 /* READ DEFECT (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
  * command not supported, 3 if field in command not supported or returns
  * negated errno. SBC-2 section 5.12 (rev 16) */
-int scsiReadDefect10(int device, int req_plist, int req_glist, int dl_format,
+int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, int dl_format,
                      UINT8 *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
     UINT8 cdb[10];
     UINT8 sense[32];
-    int status;
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -798,13 +994,123 @@ int scsiReadDefect10(int device, int req_plist, int req_glist, int dl_format,
     io_hdr.max_sense_len = sizeof(sense);
     io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
 
+/* READ CAPACITY (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
+ * command not supported, 3 if field in command not supported or returns
+ * negated errno. SBC-3 section 5.15 (rev 26) */
+int scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap,
+                       unsigned int * lb_sizep)
+{
+    int res;
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+    UINT8 resp[8];
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    memset(resp, 0, sizeof(resp));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = sizeof(resp);
+    io_hdr.dxferp = resp;
+    cdb[0] = READ_CAPACITY_10;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    res = scsiSimpleSenseFilter(&sinfo);
+    if (res)
+        return res;
+    if (last_lbap)
+        *last_lbap = (resp[0] << 24) | (resp[1] << 16) | (resp[2] << 8) |
+                     resp[3];
+    if (lb_sizep)
+        *lb_sizep = (resp[4] << 24) | (resp[5] << 16) | (resp[6] << 8) |
+                    resp[7];
+    return 0;
+}
+
+/* READ CAPACITY (16) command. The bufLen argument should be 32. Returns 0
+ * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command
+ * not supported or returns negated errno. SBC-3 section 5.16 (rev 26) */
+int scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[16];
+    UINT8 sense[32];
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = READ_CAPACITY_16;
+    cdb[1] = SAI_READ_CAPACITY_16;
+    cdb[10] = (bufLen >> 24) & 0xff;
+    cdb[11] = (bufLen >> 16) & 0xff;
+    cdb[12] = (bufLen >> 8) & 0xff;
+    cdb[13] = bufLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* Return number of bytes of storage in 'device' or 0 if error. If
+ * successful and lb_sizep is not NULL then the logical block size
+ * in bytes is written to the location pointed to by lb_sizep. */
+uint64_t scsiGetSize(scsi_device * device, unsigned int * lb_sizep)
+{
+    unsigned int last_lba, lb_size;
+    int k, res;
+    uint64_t ret_val = 0;
+    UINT8 rc16resp[32];
+
+    res = scsiReadCapacity10(device, &last_lba, &lb_size);
+    if (res) {
+       if (scsi_debugmode)
+           pout("scsiGetSize: READ CAPACITY(10) failed, res=%d\n", res);
+       return ret_val;
+    }
+    if (0xffffffff == last_lba) {
+       res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp));
+        if (res) {
+           if (scsi_debugmode)
+               pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);
+           return ret_val;
+       }
+       for (k = 0; k < 8; ++k) {
+           if (k > 0)
+                ret_val <<= 8;
+            ret_val |= rc16resp[k + 0];
+        }
+    } else
+       ret_val = last_lba;
+    if (lb_sizep)
+       *lb_sizep = lb_size;
+    ++ret_val; /* last_lba is origin 0 so need to bump to get number of */
+    return ret_val * lb_size;
+}
+
+
 /* Offset into mode sense (6 or 10 byte) response that actual mode page
  * starts at (relative to resp[0]). Returns -1 if problem */
 int scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
@@ -827,7 +1133,7 @@ int scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
                  "resp_len=%d bd_len=%d\n", offset, resp_len, bd_len);
             offset = -1;
         } else if ((offset + 2) > resp_len) {
-             if ((resp_len > 2) || con->reportscsiioctl)
+             if ((resp_len > 2) || scsi_debugmode)
                 pout("scsiModePageOffset: response length too short, "
                      "resp_len=%d offset=%d bd_len=%d\n", resp_len,
                      offset, bd_len);
@@ -849,7 +1155,7 @@ int scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
  * tries a 10 byte MODE SENSE command. Returns 0 if successful, a positive
  * number if a known error (see  SIMPLE_ERR_ ...) or a negative errno
  * value. */
-int scsiFetchIECmpage(int device, struct scsi_iec_mode_page *iecp, int modese_len)
+int scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp, int modese_len)
 {
     int err = 0;
 
@@ -939,7 +1245,7 @@ int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp)
  * is to be re-examined.
  * When -r ioctl is invoked 3 or more time on 'smartctl -s on ...'
  * then set the TEST bit (causes asc,ascq pair of 0x5d,0xff). */
-int scsiSetExceptionControlAndWarning(int device, int enabled,
+int scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
                                       const struct scsi_iec_mode_page *iecp)
 {
     int k, offset, resp_len;
@@ -964,7 +1270,7 @@ int scsiSetExceptionControlAndWarning(int device, int enabled,
     sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
     if (enabled) {
         rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED;
-        if (con->reportscsiioctl > 2)
+        if (scsi_debugmode > 2)
             rout[offset + 2] |= SCSI_IEC_MP_BYTE2_TEST_MASK;
         rout[offset + 3] = SCSI_IEC_MP_MRIE;
         rout[offset + 4] = (SCSI_IEC_MP_INTERVAL_T >> 24) & 0xff;
@@ -986,7 +1292,7 @@ int scsiSetExceptionControlAndWarning(int device, int enabled,
             }
         }
         if (0 == memcmp(&rout[offset + 2], &iecp->raw_chg[offset + 2], 10)) {
-            if (con->reportscsiioctl > 0)
+            if (scsi_debugmode > 0)
                 pout("scsiSetExceptionControlAndWarning: already enabled\n");
             return 0;
         }
@@ -994,7 +1300,7 @@ int scsiSetExceptionControlAndWarning(int device, int enabled,
         eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
         wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0;
         if ((! eCEnabled) && (! wEnabled)) {
-            if (con->reportscsiioctl > 0)
+            if (scsi_debugmode > 0)
                 pout("scsiSetExceptionControlAndWarning: already disabled\n");
             return 0;   /* nothing to do, leave other setting alone */
         }
@@ -1014,7 +1320,7 @@ int scsiSetExceptionControlAndWarning(int device, int enabled,
     return err;
 }
 
-int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp)
+int scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp)
 {
     UINT8 tBuf[252];
     int err;
@@ -1036,7 +1342,7 @@ int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp)
  * Fetching asc/ascq code potentially flagging an exception or warning.
  * Returns 0 if ok, else error number. A current temperature of 255
  * (Celsius) implies that the temperature not available. */
-int scsiCheckIE(int device, int hasIELogPage, int hasTempLogPage,
+int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
                 UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp,
                 UINT8 *triptemp)
 {
@@ -1619,7 +1925,7 @@ const char * scsiGetIEString(UINT8 asc, UINT8 ascq)
 
 /* This is not documented in t10.org, page 0x80 is vendor specific */
 /* Some IBM disks do an offline read-scan when they get this command. */
-int scsiSmartIBMOfflineTest(int device)
+int scsiSmartIBMOfflineTest(scsi_device * device)
 {       
     UINT8 tBuf[256];
     int res;
@@ -1640,7 +1946,7 @@ int scsiSmartIBMOfflineTest(int device)
     return res;
 }
 
-int scsiSmartDefaultSelfTest(int device)
+int scsiSmartDefaultSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1650,7 +1956,7 @@ int scsiSmartDefaultSelfTest(int device)
     return res;
 }
 
-int scsiSmartShortSelfTest(int device)
+int scsiSmartShortSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1660,7 +1966,7 @@ int scsiSmartShortSelfTest(int device)
     return res;
 }
 
-int scsiSmartExtendSelfTest(int device)
+int scsiSmartExtendSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1671,7 +1977,7 @@ int scsiSmartExtendSelfTest(int device)
     return res;
 }
 
-int scsiSmartShortCapSelfTest(int device)
+int scsiSmartShortCapSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1681,7 +1987,7 @@ int scsiSmartShortCapSelfTest(int device)
     return res;
 }
 
-int scsiSmartExtendCapSelfTest(int device)
+int scsiSmartExtendCapSelfTest(scsi_device * device)
 {
     int res;
 
@@ -1692,7 +1998,7 @@ int scsiSmartExtendCapSelfTest(int device)
     return res;
 }
 
-int scsiSmartSelfTestAbort(int device)
+int scsiSmartSelfTestAbort(scsi_device * device)
 {
     int res;
 
@@ -1704,7 +2010,7 @@ int scsiSmartSelfTestAbort(int device)
 
 /* Returns 0 and the expected duration of an extended self test (in seconds)
    if successful; any other return value indicates a failure. */
-int scsiFetchExtendedSelfTestTime(int device, int * durationSec, int modese_len)
+int scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec, int modese_len)
 {
     int err, offset, res;
     UINT8 buff[64];
@@ -1864,7 +2170,7 @@ void scsiDecodeNonMediumErrPage(unsigned char *resp,
    tests (typically by the user) and self tests in progress are not 
    considered failures. See Working Draft SCSI Primary Commands - 3 
    (SPC-3) section 7.2.10 T10/1416-D (rev 22a) */
-int scsiCountFailedSelfTests(int fd, int noisy)
+int scsiCountFailedSelfTests(scsi_device * fd, int noisy)
 {
     int num, k, n, err, res, fails, fail_hour;
     UINT8 * ucp;
@@ -1913,7 +2219,7 @@ int scsiCountFailedSelfTests(int fd, int noisy)
 
 /* Returns 0 if able to read self test log page; then outputs 1 into
    *inProgress if self test still in progress, else outputs 0. */
-int scsiSelfTestInProgress(int fd, int * inProgress)
+int scsiSelfTestInProgress(scsi_device * fd, int * inProgress)
 {
     int num;
     UINT8 * ucp;
@@ -1940,7 +2246,7 @@ int scsiSelfTestInProgress(int fd, int * inProgress)
    malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD
    bit is set. Examines default mode page when current==0 else examines
    current mode page. */
-int scsiFetchControlGLTSD(int device, int modese_len, int current)
+int scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current)
 {
     int err, offset;
     UINT8 buff[64];
@@ -1973,7 +2279,7 @@ int scsiFetchControlGLTSD(int device, int modese_len, int current)
    0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if
    successful, negative if low level error, > 0 if higher level error (e.g.
    SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */
-int scsiSetControlGLTSD(int device, int enabled, int modese_len)
+int scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len)
 {
     int err, offset, resp_len, sp;
     UINT8 buff[64];
@@ -2042,7 +2348,7 @@ int scsiSetControlGLTSD(int device, int enabled, int modese_len)
 /* Returns a negative value if failed to fetch Protocol specific port mode 
    page or it was malformed. Returns transport protocol identifier when
    value >= 0 . */
-int scsiFetchTransportProtocol(int device, int modese_len)
+int scsiFetchTransportProtocol(scsi_device * device, int modese_len)
 {
     int err, offset;
     UINT8 buff[64];
@@ -2074,58 +2380,3 @@ int scsiFetchTransportProtocol(int device, int modese_len)
     }
     return -EINVAL;
 }
-
-
-/* This is Linux specific code to look for the libata ATA-SCSI
-   simulator in the vendor device identification page.
-   Returns 1 if found else 0. */
-int isLinuxLibAta(unsigned char * vpd_di_buff, int len)
-{
-    int k, id_len, c_set, assoc, id_type, i_len;
-    unsigned char * ucp;
-    unsigned char * ip;
-    unsigned char buff1[20];
-    unsigned char buff2[20];
-
-    if (len < 4) {
-        /* Device identification VPD page length too short */
-        return 0;
-    }
-    buff1[0] = '\0';
-    buff2[0] = '\0';
-    len -= 4;
-    ucp = vpd_di_buff + 4;
-    for (k = 0; k < len; k += id_len, ucp += id_len) {
-        i_len = ucp[3];
-        id_len = i_len + 4;
-        if ((k + id_len) > len) {
-            /* short descriptor, badly formed */
-            return 0;
-        }
-        ip = ucp + 4;
-        c_set = (ucp[0] & 0xf);
-        assoc = ((ucp[1] >> 4) & 0x3);
-        id_type = (ucp[1] & 0xf);
-        if ((0 == id_type) && (2 == c_set) && (0 == assoc)) {
-            /* assoc=lu, c_set=ascii, id_type=vendor */
-            if (0 == strncmp((const char *)ip,
-                             "Linux ATA-SCSI simulator", i_len)) {
-                /* until lk 2.6.16 */
-                return 1;
-            }
-            memcpy(buff1, ip, sizeof(buff1));
-        }
-        if ((1 == id_type) && (2 == c_set) && (0 == assoc)) {
-            /* assoc=lu, c_set=ascii, id_type=t10 vendor id */
-            if (0 == strncmp((const char *)ip, "ATA", 3))
-                memcpy(buff2, ip + 48, sizeof(buff2));
-        }
-    }
-    if (buff1[0] && buff2[0]) {
-        if (0 == memcmp(buff1, buff2, sizeof(buff1))) {
-            /* after lk 2.6.16, look for serial number match */
-            return 1;
-        }
-    }
-    return 0;
-}