diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG
index 9abb33e16f3816054476a2b717d756b77b14bdbb..e02f29f9cd0ac94867261eb9efd6142ce70cbf8c 100644
--- a/sm5/CHANGELOG
+++ b/sm5/CHANGELOG
@@ -1,6 +1,6 @@
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.695 2008/07/21 19:32:02 ballen4705 Exp $
+$Id: CHANGELOG,v 1.696 2008/07/25 21:16:00 chrfranke Exp $
 
 The most recent version of this file is:
 http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/CHANGELOG?view=markup
@@ -38,6 +38,15 @@ NOTES FOR FUTURE RELEASES: see TODO file.
 
 <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
 
+  [CF] Add new object oriented interface to access ATA and SCSI devices.
+       smartctl and smartd are modified to use the new classes in
+       'dev_interface.{h,cpp}'. The template class in 'dev_tunnelled.h'
+       is used in 'scsiata.cpp'. The code in 'dev_ata_cmd_set.{h,cpp}'
+       supports migration from old function 'ata_command_interface()'.
+       All existing 'os_*.cpp' modules should still work without any changes.
+       The required adapter classes from 'dev_legacy.cpp' are automatically
+       added by configure if necessary.
+
   [BA] Updated smartd and smartctl and smartd.conf man-page documentation
        to reflect support for Areca SATA RAID controller cards.
 
diff --git a/sm5/Makefile.am b/sm5/Makefile.am
index 99e9008db0502f959d3f33790f786b81f50908d7..8f90effecf2175f41b2102a77b2380a43f8c6479 100644
--- a/sm5/Makefile.am
+++ b/sm5/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 #
-# $Id: Makefile.am,v 1.86 2008/05/30 19:35:04 chrfranke Exp $
+# $Id: Makefile.am,v 1.87 2008/07/25 21:16:00 chrfranke Exp $
 #
 
 @SET_MAKE@
@@ -24,6 +24,11 @@ smartd_SOURCES =  smartd.cpp      \
                   atacmds.h       \
                   ataprint.cpp    \
                   ataprint.h      \
+                  dev_ata_cmd_set.cpp \
+                  dev_ata_cmd_set.h   \
+                  dev_interface.cpp   \
+                  dev_interface.h     \
+                  dev_tunnelled.h     \
                   extern.h        \
                   int64.h         \
                   knowndrives.cpp \
@@ -57,7 +62,8 @@ EXTRA_smartd_SOURCES = os_darwin.cpp    \
                        os_generic.cpp   \
                        os_generic.h     \
                        cciss.cpp        \
-                       cciss.h
+                       cciss.h          \
+                       dev_legacy.cpp
 
 
 if OS_WIN32_MINGW
@@ -89,6 +95,11 @@ smartctl_SOURCES= smartctl.cpp    \
                   atacmds.h       \
                   ataprint.cpp    \
                   ataprint.h      \
+                  dev_ata_cmd_set.cpp \
+                  dev_ata_cmd_set.h   \
+                  dev_interface.cpp   \
+                  dev_interface.h     \
+                  dev_tunnelled.h     \
                   extern.h        \
                   int64.h         \
                   knowndrives.cpp \
@@ -102,6 +113,7 @@ smartctl_SOURCES= smartctl.cpp    \
                   utility.cpp     \
                   utility.h
 
+
 smartctl_LDADD = @os_deps@ @os_libs@
 smartctl_DEPENDENCIES = @os_deps@
 
@@ -119,7 +131,8 @@ EXTRA_smartctl_SOURCES = os_linux.cpp \
                        os_generic.cpp \
                        os_generic.h   \
                        cciss.cpp      \
-                       cciss.h
+                       cciss.h        \
+                       dev_legacy.cpp
 
 if OS_WIN32_MINGW
 
diff --git a/sm5/atacmds.cpp b/sm5/atacmds.cpp
index c3679c26fde98ccba66259dcf46f6e914cbb5808..454b9b790ef603662192ad46c7ba476d37b0416a 100644
--- a/sm5/atacmds.cpp
+++ b/sm5/atacmds.cpp
@@ -35,13 +35,17 @@
 #include "scsiata.h"
 #include "extern.h"
 #include "utility.h"
+#include "dev_ata_cmd_set.h" // for parsed_ata_device
 
-const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.196 2008/07/17 23:37:15 ballen4705 Exp $"
+const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.197 2008/07/25 21:16:00 chrfranke Exp $"
 ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSIATA_H_CVSID UTILITY_H_CVSID;
 
 // for passing global control variables
 extern smartmonctrl *con;
 
+#define SMART_CYL_LOW  0x4F
+#define SMART_CYL_HI   0xC2
+
 // These Drive Identity tables are taken from hdparm 5.2, and are also
 // given in the ATA/ATAPI specs for the IDENTIFY DEVICE command.  Note
 // that SMART was first added into the ATA/ATAPI-3 Standard with
@@ -548,6 +552,33 @@ static const char * const commandstrings[]={
   "WARNING (UNDEFINED COMMAND -- CONTACT DEVELOPERS AT " PACKAGE_BUGREPORT ")\n"
 };
 
+
+static const char * preg(const ata_register & r, char * buf)
+{
+  if (!r.is_set())
+    //return "n/a ";
+    return "....";
+  sprintf(buf, "0x%02x", r.val()); return buf;
+}
+
+void print_regs(const char * prefix, const ata_in_regs & r, const char * suffix = "\n")
+{
+  char bufs[7][4+1+13];
+  pout("%s FR=%s, SC=%s, LL=%s, LM=%s, LH=%s, DEV=%s, CMD=%s%s", prefix,
+    preg(r.features, bufs[0]), preg(r.sector_count, bufs[1]), preg(r.lba_low, bufs[2]),
+    preg(r.lba_mid, bufs[3]), preg(r.lba_high, bufs[4]), preg(r.device, bufs[5]),
+    preg(r.command, bufs[6]), suffix);
+}
+
+void print_regs(const char * prefix, const ata_out_regs & r, const char * suffix = "\n")
+{
+  char bufs[7][4+1+13];
+  pout("%sERR=%s, SC=%s, LL=%s, LM=%s, LH=%s, DEV=%s, STS=%s%s", prefix,
+    preg(r.error, bufs[0]), preg(r.sector_count, bufs[1]), preg(r.lba_low, bufs[2]),
+    preg(r.lba_mid, bufs[3]), preg(r.lba_high, bufs[4]), preg(r.device, bufs[5]),
+    preg(r.device, bufs[6]), suffix);
+}
+
 static void prettyprint(const unsigned char *p, const char *name){
   pout("\n===== [%s] DATA START (BASE-16) =====\n", name);
   for (int i=0; i<512; i+=16, p+=16)
@@ -560,14 +591,11 @@ static void prettyprint(const unsigned char *p, const char *name){
   pout("===== [%s] DATA END (512 Bytes) =====\n\n", name);
 }
 
-static int parsedev_command_interface(int fd, smart_command_set command, int select, char * data);
-
 // This function provides the pretty-print reporting for SMART
 // commands: it implements the various -r "reporting" options for ATA
 // ioctls.
-int smartcommandhandler(int device, smart_command_set command, int select, char *data){
-  int retval;
-
+int smartcommandhandler(ata_device * device, smart_command_set command, int select, char *data){
+  // TODO: Rework old stuff below
   // This conditional is true for commands that return data
   int getsdata=(command==PIDENTIFY || 
                 command==IDENTIFY || 
@@ -587,7 +615,7 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
                          command==IMMEDIATE_OFFLINE ||
                          command==WRITE_LOG);
                   
-    pout("\nREPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]);
+    pout("\nREPORT-IOCTL: Device=%s Command=%s", device->get_dev_name(), commandstrings[command]);
     if (usesparam)
       pout(" InputParameter=%d\n", select);
     else
@@ -612,42 +640,125 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
 
   // if requested, pretty-print the input data structure
   if (con->reportataioctl>1 && sendsdata)
+    //pout("REPORT-IOCTL: Device=%s Command=%s\n", device->get_dev_name(), commandstrings[command]);
     prettyprint((unsigned char *)data, commandstrings[command]);
 
-  // In case the command produces an error, we'll want to know what it is:
-  errno=0;
-  
   // now execute the command
-  switch (con->controller_type) {
-  case CONTROLLER_3WARE_678K:
-  case CONTROLLER_3WARE_678K_CHAR:
-  case CONTROLLER_3WARE_9000_CHAR:
-    retval=escalade_command_interface(device, con->controller_port-1, con->controller_type, command, select, data);
-    if (retval &&  con->controller_port<=0)
-      pout("WARNING: apparently missing '-d 3ware,N' disk specification\n");
-    break;
-  case CONTROLLER_ARECA:
-    retval=areca_command_interface(device, con->controller_port-1, command, select, data);
-    if (retval &&  con->controller_port<=0)
-      pout("WARNING: apparently missing '-d areca,N' disk specification\n");
-    break;
-  case CONTROLLER_MARVELL_SATA:
-    retval=marvell_command_interface(device, command, select, data);
-    break;
-  case CONTROLLER_SAT:
-    retval=sat_command_interface(device, command, select, data);
-    break;
-  case CONTROLLER_USBCYPRESS:
-    retval=usbcypress_command_interface(device, command, select, data);
-    break;
-  case CONTROLLER_HPT:
-    retval=highpoint_command_interface(device, command, select, data);
-    break;
-  case CONTROLLER_PARSEDEV:
-    retval=parsedev_command_interface(device, command, select, data);
-    break;
-  default:
-    retval=ata_command_interface(device, command, select, data);
+  int retval = -1;
+  {
+    ata_cmd_in in;
+    // Set common register values
+    switch (command) {
+      default: // SMART commands
+        in.in_regs.command = ATA_SMART_CMD;
+        in.in_regs.lba_high = SMART_CYL_HI; in.in_regs.lba_mid = SMART_CYL_LOW;
+        break;
+      case IDENTIFY: case PIDENTIFY: case CHECK_POWER_MODE: // Non SMART commands
+        break;
+    }
+    // Set specific values
+    switch (command) {
+      case IDENTIFY:
+        in.in_regs.command = ATA_IDENTIFY_DEVICE;
+        in.set_data_in(1);
+        break;
+      case PIDENTIFY:
+        in.in_regs.command = ATA_IDENTIFY_PACKET_DEVICE;
+        in.set_data_in(1);
+        break;
+      case CHECK_POWER_MODE:
+        in.in_regs.command = ATA_CHECK_POWER_MODE;
+        in.out_needed.sector_count = true; // Powermode returned here
+        break;
+      case READ_VALUES:
+        in.in_regs.features = ATA_SMART_READ_VALUES;
+        in.set_data_in(1);
+        break;
+      case READ_THRESHOLDS:
+        in.in_regs.features = ATA_SMART_READ_THRESHOLDS;
+        in.in_regs.lba_low = 1; // TODO: CORRECT ???
+        in.set_data_in(1);
+        break;
+      case READ_LOG:
+        in.in_regs.features = ATA_SMART_READ_LOG_SECTOR;
+        in.in_regs.lba_low = select;
+        in.set_data_in(1);
+        break;
+      case WRITE_LOG:
+        in.in_regs.features = ATA_SMART_WRITE_LOG_SECTOR;
+        in.in_regs.lba_low = select;
+        in.set_data_out(1);
+        break;
+      case ENABLE:
+        in.in_regs.features = ATA_SMART_ENABLE;
+        in.in_regs.lba_low = 1; // TODO: CORRECT ???
+        break;
+      case DISABLE:
+        in.in_regs.features = ATA_SMART_DISABLE;
+        in.in_regs.lba_low = 1;  // TODO: CORRECT ???
+        break;
+      case STATUS_CHECK:
+        in.out_needed.lba_high = in.out_needed.lba_mid = true; // Status returned here
+      case STATUS:
+        in.in_regs.features = ATA_SMART_STATUS;
+        break;
+      case AUTO_OFFLINE:
+        in.in_regs.features = ATA_SMART_AUTO_OFFLINE;
+        in.in_regs.sector_count = select;  // Caution: Non-DATA command!
+        break;
+      case AUTOSAVE:
+        in.in_regs.features = ATA_SMART_AUTOSAVE;
+        in.in_regs.sector_count = select;  // Caution: Non-DATA command!
+        break;
+      case IMMEDIATE_OFFLINE:
+        in.in_regs.features = ATA_SMART_IMMEDIATE_OFFLINE;
+        in.in_regs.lba_low = select;
+        break;
+      default:
+        pout("Unrecognized command %d in smartcommandhandler()\n"
+             "Please contact " PACKAGE_BUGREPORT "\n", command);
+        device->set_err(ENOSYS);
+        errno = ENOSYS;
+        return -1;
+    }
+    if (in.direction)
+      in.buffer = data;
+
+    if (con->reportataioctl)
+      print_regs(" Input:  ", in.in_regs,
+        (in.direction==ata_cmd_in::data_in ? " IN\n":
+         in.direction==ata_cmd_in::data_out ? " OUT\n":"\n"));
+
+    ata_cmd_out out;
+    bool ok = device->ata_pass_through(in, out);
+
+    if (con->reportataioctl && out.out_regs.is_set())
+      print_regs(" Output: ", out.out_regs);
+
+    if (ok) switch (command) {
+      default:
+        retval = 0;
+        break;
+      case CHECK_POWER_MODE:
+        data[0] = out.out_regs.sector_count;
+        retval = 0;
+        break;
+      case STATUS_CHECK:
+        // Cyl low and Cyl high unchanged means "Good SMART status"
+        if (out.out_regs.lba_high == SMART_CYL_HI && out.out_regs.lba_mid == SMART_CYL_LOW)
+          retval = 0;
+        // These values mean "Bad SMART status"
+        else if (out.out_regs.lba_high == 0x2c && out.out_regs.lba_mid == 0xf4)
+          retval = 1;
+        else {
+          // We haven't gotten output that makes sense; print out some debugging info
+          pout("Error SMART Status command failed\n"
+               "Please get assistance from %s\n", PACKAGE_HOMEPAGE);
+          errno = EIO;
+          retval = -1;
+        }
+        break;
+    }
   }
 
   // If requested, invalidate serial number before any printing is done
@@ -656,12 +767,13 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
 
   // If reporting is enabled, say what output was produced by the command
   if (con->reportataioctl){
-    if (errno)
-      pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d errno=%d [%s]\n", 
-           device, commandstrings[command], retval, errno, strerror(errno));
+    if (device->get_errno())
+      pout("REPORT-IOCTL: Device=%s Command=%s returned %d errno=%d [%s]\n",
+           device->get_dev_name(), commandstrings[command], retval,
+           device->get_errno(), device->get_errmsg());
     else
-      pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d\n",
-           device, commandstrings[command], retval);
+      pout("REPORT-IOCTL: Device=%s Command=%s returned %d\n",
+           device->get_dev_name(), commandstrings[command], retval);
     
     // if requested, pretty-print the output data structure
     if (con->reportataioctl>1 && getsdata) {
@@ -671,6 +783,8 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
 	prettyprint((unsigned char *)data, commandstrings[command]);
     }
   }
+
+  errno = device->get_errno(); // TODO: Callers should not call syserror()
   return retval;
 }
 
@@ -694,7 +808,7 @@ unsigned char checksum(unsigned char *buffer){
 //   80h device is in Idle mode.
 //   FFh device is in Active mode or Idle mode.
 
-int ataCheckPowerMode(int device) {
+int ataCheckPowerMode(ata_device * device) {
   unsigned char result;
 
   if ((smartcommandhandler(device, CHECK_POWER_MODE, 0, (char *)&result)))
@@ -715,7 +829,7 @@ int ataCheckPowerMode(int device) {
 // capable).  The value of the integer helps identify the type of
 // Packet device, which is useful so that the user can connect the
 // formal device number with whatever object is inside their computer.
-int ataReadHDIdentity (int device, struct ata_identify_device *buf){
+int ataReadHDIdentity (ata_device * device, struct ata_identify_device *buf){
   unsigned short *rawshort=(unsigned short *)buf;
   unsigned char  *rawbyte =(unsigned char  *)buf;
 
@@ -863,7 +977,7 @@ int ataIsSmartEnabled(struct ata_identify_device *drive){
 
 
 // Reads SMART attributes into *data
-int ataReadSmartValues(int device, struct ata_smart_values *data){      
+int ataReadSmartValues(ata_device * device, struct ata_smart_values *data){
   
   if (smartcommandhandler(device, READ_VALUES, 0, (char *)data)){
     syserror("Error SMART Values Read failed");
@@ -910,7 +1024,7 @@ void fixsamsungselftestlog(struct ata_smart_selftestlog *data){
 }
 
 // Reads the Self Test Log (log #6)
-int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){
+int ataReadSelfTestLog (ata_device * device, struct ata_smart_selftestlog *data){
 
   // get data from device
   if (smartcommandhandler(device, READ_LOG, 0x06, (char *)data)){
@@ -942,7 +1056,7 @@ int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){
 
 
 // Reads the Log Directory (log #0).  Note: NO CHECKSUM!!
-int ataReadLogDirectory (int device, struct ata_smart_log_directory *data){     
+int ataReadLogDirectory (ata_device * device, struct ata_smart_log_directory *data){
   
   // get data from device
   if (smartcommandhandler(device, READ_LOG, 0x00, (char *)data)){
@@ -959,7 +1073,7 @@ int ataReadLogDirectory (int device, struct ata_smart_log_directory *data){
 
 
 // Reads the selective self-test log (log #9)
-int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *data){  
+int ataReadSelectiveSelfTestLog(ata_device * device, struct ata_selective_self_test_log *data){
   
   // get data from device
   if (smartcommandhandler(device, READ_LOG, 0x09, (char *)data)){
@@ -992,7 +1106,7 @@ int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *
 }
 
 // Writes the selective self-test log (log #9)
-int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv, uint64_t num_sectors){   
+int ataWriteSelectiveSelfTestLog(ata_device * device, struct ata_smart_values *sv, uint64_t num_sectors){
 
   // Disk size must be known
   if (!num_sectors) {
@@ -1187,7 +1301,7 @@ void fixsamsungerrorlog2(struct ata_smart_errorlog *data){
 // Reads the Summary SMART Error Log (log #1). The Comprehensive SMART
 // Error Log is #2, and the Extended Comprehensive SMART Error log is
 // #3
-int ataReadErrorLog (int device, struct ata_smart_errorlog *data){      
+int ataReadErrorLog (ata_device * device, struct ata_smart_errorlog *data){
   
   // get data from device
   if (smartcommandhandler(device, READ_LOG, 0x01, (char *)data)){
@@ -1227,7 +1341,7 @@ int ataReadErrorLog (int device, struct ata_smart_errorlog *data){
   return 0;
 }
 
-int ataReadSmartThresholds (int device, struct ata_smart_thresholds_pvt *data){
+int ataReadSmartThresholds (ata_device * device, struct ata_smart_thresholds_pvt *data){
   
   // get data from device
   if (smartcommandhandler(device, READ_THRESHOLDS, 0, (char *)data)){
@@ -1246,7 +1360,7 @@ int ataReadSmartThresholds (int device, struct ata_smart_thresholds_pvt *data){
   return 0;
 }
 
-int ataEnableSmart (int device ){       
+int ataEnableSmart (ata_device * device ){
   if (smartcommandhandler(device, ENABLE, 0, NULL)){
     syserror("Error SMART Enable failed");
     return -1;
@@ -1254,7 +1368,7 @@ int ataEnableSmart (int device ){
   return 0;
 }
 
-int ataDisableSmart (int device ){      
+int ataDisableSmart (ata_device * device ){
   
   if (smartcommandhandler(device, DISABLE, 0, NULL)){
     syserror("Error SMART Disable failed");
@@ -1263,7 +1377,7 @@ int ataDisableSmart (int device ){
   return 0;
 }
 
-int ataEnableAutoSave(int device){  
+int ataEnableAutoSave(ata_device * device){
   if (smartcommandhandler(device, AUTOSAVE, 241, NULL)){
     syserror("Error SMART Enable Auto-save failed");
     return -1;
@@ -1271,7 +1385,7 @@ int ataEnableAutoSave(int device){
   return 0;
 }
 
-int ataDisableAutoSave(int device){
+int ataDisableAutoSave(ata_device * device){
   
   if (smartcommandhandler(device, AUTOSAVE, 0, NULL)){
     syserror("Error SMART Disable Auto-save failed");
@@ -1284,7 +1398,7 @@ int ataDisableAutoSave(int device){
 // marked "OBSOLETE". It is defined in SFF-8035i Revision 2, and most
 // vendors still support it for backwards compatibility. IBM documents
 // it for some drives.
-int ataEnableAutoOffline (int device ){ 
+int ataEnableAutoOffline (ata_device * device){
   
   /* timer hard coded to 4 hours */  
   if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){
@@ -1296,7 +1410,7 @@ int ataEnableAutoOffline (int device ){
 
 // Another Obsolete Command.  See comments directly above, associated
 // with the corresponding Enable command.
-int ataDisableAutoOffline (int device ){        
+int ataDisableAutoOffline (ata_device * device){
   
   if (smartcommandhandler(device, AUTO_OFFLINE, 0, NULL)){
     syserror("Error SMART Disable Automatic Offline failed");
@@ -1309,7 +1423,7 @@ int ataDisableAutoOffline (int device ){
 // guaranteed to return 1, else zero.  Note that it should return 1
 // regardless of whether the disk's SMART status is 'healthy' or
 // 'failing'.
-int ataDoesSmartWork(int device){
+int ataDoesSmartWork(ata_device * device){
   int retval=smartcommandhandler(device, STATUS, 0, NULL);
 
   if (-1 == retval)
@@ -1320,13 +1434,13 @@ int ataDoesSmartWork(int device){
 
 // This function uses a different interface (DRIVE_TASK) than the
 // other commands in this file.
-int ataSmartStatus2(int device){
+int ataSmartStatus2(ata_device * device){
   return smartcommandhandler(device, STATUS_CHECK, 0, NULL);  
 }
 
 // This is the way to execute ALL tests: offline, short self-test,
 // extended self test, with and without captive mode, etc.
-int ataSmartTest(int device, int testtype, struct ata_smart_values *sv, uint64_t num_sectors)
+int ataSmartTest(ata_device * device, int testtype, struct ata_smart_values *sv, uint64_t num_sectors)
 {
   char cmdmsg[128]; const char *type, *captive;
   int errornum, cap, retval, select=0;
@@ -2079,8 +2193,9 @@ unsigned char ATAReturnTemperatureValue(/*const*/ struct ata_smart_values *data,
   return 0;
 }
 
+
 // Read SCT Status
-int ataReadSCTStatus(int device, ata_sct_status_response * sts)
+int ataReadSCTStatus(ata_device * device, ata_sct_status_response * sts)
 {
   // read SCT status via SMART log 0xe0
   memset(sts, 0, sizeof(*sts));
@@ -2110,7 +2225,7 @@ int ataReadSCTStatus(int device, ata_sct_status_response * sts)
 }
 
 // Read SCT Temperature History Table and Status
-int ataReadSCTTempHist(int device, ata_sct_temperature_history_table * tmh,
+int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * tmh,
                        ata_sct_status_response * sts)
 {
   // Check initial status
@@ -2170,7 +2285,7 @@ int ataReadSCTTempHist(int device, ata_sct_temperature_history_table * tmh,
 }
 
 // Set SCT Temperature Logging Interval
-int ataSetSCTTempInterval(int device, unsigned interval, bool persistent)
+int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persistent)
 {
   // Check initial status
   ata_sct_status_response sts;
@@ -2216,21 +2331,45 @@ int ataSetSCTTempInterval(int device, unsigned interval, bool persistent)
 // Pseudo-device to parse "smartctl -r ataioctl,2 ..." output and simulate
 // an ATA device with same behaviour
 
-// Table of parsed commands, return value, data
-struct parsed_ata_command
-{ 
-  smart_command_set command;
-  int select;
-  int retval, errval;
-  char * data;
-};
+namespace {
+
+class parsed_ata_device
+: public /*implements*/ ata_device_with_command_set
+{
+public:
+  parsed_ata_device(smart_interface * intf, const char * dev_name);
+
+  virtual ~parsed_ata_device() throw();
+
+  virtual bool is_open() const;
 
-const int max_num_parsed_commands = 32;
-static parsed_ata_command parsed_command_table[max_num_parsed_commands];
-static int num_parsed_commands;
-static int next_replay_command;
-static bool replay_out_of_sync;
+  virtual bool open();
 
+  virtual bool close();
+
+  virtual bool ata_identify_is_cached() const;
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  // Table of parsed commands, return value, data
+  struct parsed_ata_command
+  {
+    smart_command_set command;
+    int select;
+    int retval, errval;
+    char * data;
+  };
+
+  enum { max_num_commands = 32 };
+  parsed_ata_command m_command_table[max_num_commands];
+
+  int m_num_commands;
+  int m_next_replay_command;
+  bool m_replay_out_of_sync;
+  bool m_ata_identify_is_cached;
+};
 
 static const char * nextline(const char * s, int & lineno)
 {
@@ -2270,13 +2409,32 @@ static inline int matchtoi(const char * src, const regmatch_t & srcmatch, int de
   return atoi(src + srcmatch.rm_so);
 }
 
+parsed_ata_device::parsed_ata_device(smart_interface * intf, const char * dev_name)
+: smart_device(intf, dev_name, "ata", ""),
+  m_num_commands(0),
+  m_next_replay_command(0),
+  m_replay_out_of_sync(false),
+  m_ata_identify_is_cached(false)
+{
+  memset(m_command_table, 0, sizeof(m_command_table));
+}
+
+parsed_ata_device::~parsed_ata_device() throw()
+{
+  close();
+}
+
+bool parsed_ata_device::is_open() const
+{
+  return (m_num_commands > 0);
+}
 
 // Parse stdin and build command table
-int parsedev_open(const char * pathname)
+bool parsed_ata_device::open()
 {
-  if (strcmp(pathname, "-")) {
-    errno = EINVAL; return -1;
-  }
+  const char * pathname = get_dev_name();
+  if (strcmp(pathname, "-"))
+    return set_err(EINVAL);
   pathname = "<stdin>";
   // Fill buffer
   char buffer[64*1024];
@@ -2287,20 +2445,16 @@ int parsedev_open(const char * pathname)
       break;
     size += nr;
   }
-  if (size <= 0) {
-    pout("%s: Unexpected EOF\n", pathname);
-    errno = ENOENT; return -1;
-  }
-  if (size >= (int)sizeof(buffer)) {
-    pout("%s: Buffer overflow\n", pathname);
-    errno = EIO; return -1;
-  }
+  if (size <= 0)
+    return set_err(ENOENT, "%s: Unexpected EOF", pathname);
+  if (size >= (int)sizeof(buffer))
+    return set_err(EIO, "%s: Buffer overflow", pathname);
   buffer[size] = 0;
 
   // Regex to match output from "-r ataioctl,2"
   static const char pattern[] = "^"
   "(" // (1
-    "REPORT-IOCTL: DeviceFD=[0-9]+ Command=([A-Z ]*[A-Z])" // (2)
+    "REPORT-IOCTL: DeviceF?D?=[^ ]+ Command=([A-Z ]*[A-Z])" // (2)
     "(" // (3
       "( InputParameter=([0-9]+))?" // (4 (5))
     "|"
@@ -2309,22 +2463,23 @@ int parsedev_open(const char * pathname)
     "[\r\n]" // EOL match necessary to match optional parts above
   "|"
     "===== \\[([A-Z ]*[A-Z])\\] DATA START " // (10)
+  "|"
+    "    *(En|Dis)abled status cached by OS, " // (11)
   ")"; // )
 
   // Compile regex
   regex_t rex;
-  if (compileregex(&rex, pattern, REG_EXTENDED)) {
-    errno = EIO; return -1;
-  }
+  if (compileregex(&rex, pattern, REG_EXTENDED))
+    return set_err(EIO, "invalid regex");
 
   // Parse buffer
   const char * errmsg = 0;
   int i = -1, state = 0, lineno = 1;
   for (const char * line = buffer; *line; line = nextline(line, lineno)) {
     // Match line
-    if (!(line[0] == 'R' || line[0] == '='))
+    if (!(line[0] == 'R' || line[0] == '=' || line[0] == ' '))
       continue;
-    const int nmatch = 1+10;
+    const int nmatch = 1+11;
     regmatch_t match[nmatch];
     if (regexec(&rex, line, nmatch, match, 0))
       continue;
@@ -2340,27 +2495,27 @@ int parsedev_open(const char * pathname)
         if (!(state == 0 || state == 2)) {
           errmsg = "Missing REPORT-IOCTL result"; break;
         }
-        if (++i >= max_num_parsed_commands) {
+        if (++i >= max_num_commands) {
           errmsg = "Too many ATA commands"; break;
         }
-        parsed_command_table[i].command = (smart_command_set)nc;
-        parsed_command_table[i].select = matchtoi(line, match[5], 0); // "InputParameter=%d"
+        m_command_table[i].command = (smart_command_set)nc;
+        m_command_table[i].select = matchtoi(line, match[5], 0); // "InputParameter=%d"
         state = 1;
       }
       else {
         // End of command
-        if (!(state == 1 && (int)parsed_command_table[i].command == nc)) {
+        if (!(state == 1 && (int)m_command_table[i].command == nc)) {
           errmsg = "Missing REPORT-IOCTL start"; break;
         }
-        parsed_command_table[i].retval = matchtoi(line, match[7], -1); // "returned %d"
-        parsed_command_table[i].errval = matchtoi(line, match[9], 0); // "errno=%d"
+        m_command_table[i].retval = matchtoi(line, match[7], -1); // "returned %d"
+        m_command_table[i].errval = matchtoi(line, match[9], 0); // "errno=%d"
         state = 2;
       }
     }
     else if (matchcpy(cmdname, sizeof(cmdname), line, match[10])) { // "===== [%s] DATA START "
       // Start of sector hexdump
       int nc = name2command(cmdname);
-      if (!(state == (nc == WRITE_LOG ? 1 : 2) && (int)parsed_command_table[i].command == nc)) {
+      if (!(state == (nc == WRITE_LOG ? 1 : 2) && (int)m_command_table[i].command == nc)) {
           errmsg = "Unexpected DATA START"; break;
       }
       line = nextline(line, lineno);
@@ -2385,10 +2540,13 @@ int parsedev_open(const char * pathname)
         free(data);
         errmsg = "Incomplete sector hex dump"; break;
       }
-      parsed_command_table[i].data = data;
+      m_command_table[i].data = data;
       if (nc != WRITE_LOG)
         state = 0;
     }
+    else if (match[11].rm_so > 0) { // "(En|Dis)abled status cached by OS"
+      m_ata_identify_is_cached = true;
+    }
   }
 
   if (!(state == 0 || state == 2))
@@ -2397,58 +2555,66 @@ int parsedev_open(const char * pathname)
   if (!errmsg && i < 0)
     errmsg = "No information found";
 
-  num_parsed_commands = i+1;
-  next_replay_command = 0;
-  replay_out_of_sync = false;
+  m_num_commands = i+1;
+  m_next_replay_command = 0;
+  m_replay_out_of_sync = false;
 
   if (errmsg) {
-    pout("%s(%d): Syntax error: %s\n", pathname, lineno, errmsg);
-    errno = EIO;
-    parsedev_close(0);
-    return -1;
+    close();
+    return set_err(EIO, "%s(%d): Syntax error: %s", pathname, lineno, errmsg);
   }
-  return 0;
+  return true;
 }
 
 // Report warnings and free command table 
-void parsedev_close(int /*fd*/)
+bool parsed_ata_device::close()
 {
-  if (replay_out_of_sync)
+  if (m_replay_out_of_sync)
       pout("REPLAY-IOCTL: Warning: commands replayed out of sync\n");
-  else if (next_replay_command != 0)
-      pout("REPLAY-IOCTL: Warning: %d command(s) not replayed\n", num_parsed_commands-next_replay_command);
+  else if (m_next_replay_command != 0)
+      pout("REPLAY-IOCTL: Warning: %d command(s) not replayed\n", m_num_commands-m_next_replay_command);
 
-  for (int i = 0; i < num_parsed_commands; i++) {
-    if (parsed_command_table[i].data) {
-      free(parsed_command_table[i].data); parsed_command_table[i].data = 0;
+  for (int i = 0; i < m_num_commands; i++) {
+    if (m_command_table[i].data) {
+      free(m_command_table[i].data); m_command_table[i].data = 0;
     }
   }
-  num_parsed_commands = 0;
+  m_num_commands = 0;
+  m_next_replay_command = 0;
+  m_replay_out_of_sync = false;
+  return true;
+}
+
+
+bool parsed_ata_device::ata_identify_is_cached() const
+{
+  return m_ata_identify_is_cached;
 }
 
+
 // Simulate ATA command from command table
-static int parsedev_command_interface(int /*fd*/, smart_command_set command, int select, char * data)
+int parsed_ata_device::ata_command_interface(smart_command_set command, int select, char * data)
 {
-  // Find command, try round-robin of out of sync
-  int i = next_replay_command;
+  // Find command, try round-robin if out of sync
+  int i = m_next_replay_command;
   for (int j = 0; ; j++) {
-    if (j >= num_parsed_commands) {
+    if (j >= m_num_commands) {
       pout("REPLAY-IOCTL: Warning: Command not found\n");
       errno = ENOSYS;
       return -1;
     }
-    if (parsed_command_table[i].command == command && parsed_command_table[i].select == select)
+    if (m_command_table[i].command == command && m_command_table[i].select == select)
       break;
-    if (!replay_out_of_sync) {
-      replay_out_of_sync = true;
+    if (!m_replay_out_of_sync) {
+      m_replay_out_of_sync = true;
       pout("REPLAY-IOCTL: Warning: Command #%d is out of sync\n", i+1);
     }
-    if (++i >= num_parsed_commands)
+    if (++i >= m_num_commands)
       i = 0;
   }
-  next_replay_command = i;
-  if (++next_replay_command >= num_parsed_commands)
-    next_replay_command = 0;
+  m_next_replay_command = i;
+  if (++m_next_replay_command >= m_num_commands)
+    m_next_replay_command = 0;
 
   // Return command data
   switch (command) {
@@ -2457,11 +2623,11 @@ static int parsedev_command_interface(int /*fd*/, smart_command_set command, int
     case READ_VALUES:
     case READ_THRESHOLDS:
     case READ_LOG:
-      if (parsed_command_table[i].data)
-        memcpy(data, parsed_command_table[i].data, 512);
+      if (m_command_table[i].data)
+        memcpy(data, m_command_table[i].data, 512);
       break;
     case WRITE_LOG:
-      if (!(parsed_command_table[i].data && !memcmp(data, parsed_command_table[i].data, 512)))
+      if (!(m_command_table[i].data && !memcmp(data, m_command_table[i].data, 512)))
         pout("REPLAY-IOCTL: Warning: WRITE LOG data does not match\n");
       break;
     case CHECK_POWER_MODE:
@@ -2470,7 +2636,14 @@ static int parsedev_command_interface(int /*fd*/, smart_command_set command, int
       break;
   }
 
-  if (parsed_command_table[i].errval)
-    errno = parsed_command_table[i].errval;
-  return parsed_command_table[i].retval;
+  if (m_command_table[i].errval)
+    errno = m_command_table[i].errval;
+  return m_command_table[i].retval;
+}
+
+} // namespace
+
+ata_device * get_parsed_ata_device(smart_interface * intf, const char * dev_name)
+{
+  return new parsed_ata_device(intf, dev_name);
 }
diff --git a/sm5/atacmds.h b/sm5/atacmds.h
index 0da11f58a084817fc76b1676eb8191c14579d5f8..5a1625eecea5998c7eb90055c6f2957b5c8420c3 100644
--- a/sm5/atacmds.h
+++ b/sm5/atacmds.h
@@ -25,7 +25,9 @@
 #ifndef ATACMDS_H_
 #define ATACMDS_H_
 
-#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.92 2008/06/12 21:46:31 ballen4705 Exp $\n"
+#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.93 2008/07/25 21:16:00 chrfranke Exp $\n"
+
+#include "dev_interface.h" // ata_device
 
 // Macro to check expected size of struct at compile time using a
 // dummy typedef.  On size mismatch, compiler reports a negative array
@@ -372,6 +374,8 @@ ASSERT_SIZEOF_STRUCT(ata_selective_self_test_log, 512);
 #define SELECTIVE_FLAG_PENDING (0x0008)
 #define SELECTIVE_FLAG_ACTIVE  (0x0010)
 
+class ata_device;
+
 
 // SCT (SMART Command Transport) data structures
 // See Sections 8.2 and 8.3 of:
@@ -459,51 +463,51 @@ ASSERT_SIZEOF_STRUCT(ata_sct_temperature_history_table, 512);
 
 
 // Get information from drive
-int ataReadHDIdentity(int device, struct ata_identify_device *buf);
-int ataCheckPowerMode(int device);
+int ataReadHDIdentity(ata_device * device, struct ata_identify_device *buf);
+int ataCheckPowerMode(ata_device * device);
 
 /* Read S.M.A.R.T information from drive */
-int ataReadSmartValues(int device,struct ata_smart_values *);
-int ataReadSmartThresholds(int device, struct ata_smart_thresholds_pvt *);
-int ataReadErrorLog(int device, struct ata_smart_errorlog *);
-int ataReadSelfTestLog(int device, struct ata_smart_selftestlog *);
-int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *data);
-int ataSmartStatus(int device);
-int ataSetSmartThresholds(int device, struct ata_smart_thresholds_pvt *);
-int ataReadLogDirectory(int device, struct ata_smart_log_directory *);
+int ataReadSmartValues(ata_device * device,struct ata_smart_values *);
+int ataReadSmartThresholds(ata_device * device, struct ata_smart_thresholds_pvt *);
+int ataReadErrorLog(ata_device * device, struct ata_smart_errorlog *);
+int ataReadSelfTestLog(ata_device * device, struct ata_smart_selftestlog *);
+int ataReadSelectiveSelfTestLog(ata_device * device, struct ata_selective_self_test_log *data);
+int ataSmartStatus(ata_device * device);
+int ataSetSmartThresholds(ata_device * device, struct ata_smart_thresholds_pvt *);
+int ataReadLogDirectory(ata_device * device, struct ata_smart_log_directory *);
 
 // Read SCT information
-int ataReadSCTStatus(int device, ata_sct_status_response * sts);
-int ataReadSCTTempHist(int device, ata_sct_temperature_history_table * tmh,
+int ataReadSCTStatus(ata_device * device, ata_sct_status_response * sts);
+int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * tmh,
                        ata_sct_status_response * sts);
 // Set SCT temperature logging interval
-int ataSetSCTTempInterval(int device, unsigned interval, bool persistent);
+int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persistent);
 
 
 /* Enable/Disable SMART on device */
-int ataEnableSmart ( int device );
-int ataDisableSmart (int device );
-int ataEnableAutoSave(int device);
-int ataDisableAutoSave(int device);
+int ataEnableSmart (ata_device * device);
+int ataDisableSmart (ata_device * device);
+int ataEnableAutoSave(ata_device * device);
+int ataDisableAutoSave(ata_device * device);
 
 /* Automatic Offline Testing */
-int ataEnableAutoOffline ( int device );
-int ataDisableAutoOffline (int device );
+int ataEnableAutoOffline (ata_device * device);
+int ataDisableAutoOffline (ata_device * device);
 
 /* S.M.A.R.T. test commands */
-int ataSmartOfflineTest (int device);
-int ataSmartExtendSelfTest (int device);
-int ataSmartShortSelfTest (int device);
-int ataSmartShortCapSelfTest (int device);
-int ataSmartExtendCapSelfTest (int device);
-int ataSmartSelfTestAbort (int device);
+int ataSmartOfflineTest (ata_device * device);
+int ataSmartExtendSelfTest (ata_device * device);
+int ataSmartShortSelfTest (ata_device * device);
+int ataSmartShortCapSelfTest (ata_device * device);
+int ataSmartExtendCapSelfTest (ata_device * device);
+int ataSmartSelfTestAbort (ata_device * device);
 
 // Returns the latest compatibility of ATA/ATAPI Version the device
 // supports. Returns -1 if Version command is not supported
 int ataVersionInfo (const char **description, struct ata_identify_device *drive, unsigned short *minor);
 
 // If SMART supported, this is guaranteed to return 1 if SMART is enabled, else 0.
-int ataDoesSmartWork(int device);
+int ataDoesSmartWork(ata_device * device);
 
 // returns 1 if SMART supported, 0 if not supported or can't tell
 int ataSmartSupport ( struct ata_identify_device *drive);
@@ -519,7 +523,7 @@ int ataIsSmartEnabled(struct ata_identify_device *drive);
 // onlyfailed=1:  are any prefailure attributes <= threshold now
 int ataCheckSmart ( struct ata_smart_values *data, struct ata_smart_thresholds_pvt *thresholds, int onlyfailed);
 
-int ataSmartStatus2(int device);
+int ataSmartStatus2(ata_device * device);
 
 // int isOfflineTestTime ( struct ata_smart_values data)
 //  returns S.M.A.R.T. Offline Test Time in seconds
@@ -558,7 +562,7 @@ inline bool isSCTFeatureControlCapable(const ata_identify_device *drive)
 inline bool isSCTDataTableCapable(const ata_identify_device *drive)
   { return ((drive->words088_255[206-88] & 0x21) == 0x21); } // 0x20 = SCT Data Table support
 
-int ataSmartTest(int device, int testtype, struct ata_smart_values *data, uint64_t num_sectors);
+int ataSmartTest(ata_device * device, int testtype, struct ata_smart_values *data, uint64_t num_sectors);
 
 int TestTime(struct ata_smart_values *data,int testtype);
 
@@ -625,24 +629,22 @@ char *create_vendor_attribute_arg_list(void);
 
 // These are two of the functions that are defined in os_*.c and need
 // to be ported to get smartmontools onto another OS.
-int ata_command_interface(int device, smart_command_set command, int select, char *data);
-int escalade_command_interface(int fd, int escalade_port, int escalade_type, smart_command_set command, int select, char *data);
-int marvell_command_interface(int device, smart_command_set command, int select, char *data);
-int highpoint_command_interface(int device, smart_command_set command, int select, char *data);
-int areca_command_interface(int fd, int escalade_port, smart_command_set command, int select, char *data);
+// Moved to C++ interface
+//int ata_command_interface(int device, smart_command_set command, int select, char *data);
+//int escalade_command_interface(int fd, int escalade_port, int escalade_type, smart_command_set command, int select, char *data);
+//int marvell_command_interface(int device, smart_command_set command, int select, char *data);
+//int highpoint_command_interface(int device, smart_command_set command, int select, char *data);
+//int areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data);
 
-// "smartctl -r ataioctl,2 ..." output parser pseudo-device
-int parsedev_open(const char * name);
-void parsedev_close(int fd);
 
 // Optional functions of os_*.c
 #ifdef HAVE_ATA_IDENTIFY_IS_CACHED
 // Return true if OS caches the ATA identify sector
-int ata_identify_is_cached(int fd);
+//int ata_identify_is_cached(int fd);
 #endif
 
 // This function is exported to give low-level capability
-int smartcommandhandler(int device, smart_command_set command, int select, char *data);
+int smartcommandhandler(ata_device * device, smart_command_set command, int select, char *data);
 
 // Utility routines.
 void swap2(char *location);
@@ -656,4 +658,8 @@ inline void swapx(unsigned int * p)
 inline void swapx(uint64_t * p)
   { swap8((char*)p); }
 
+// Return pseudo-device to parse "smartctl -r ataioctl,2 ..." output
+// and simulate an ATA device with same behaviour
+ata_device * get_parsed_ata_device(smart_interface * intf, const char * dev_name);
+
 #endif /* ATACMDS_H_ */
diff --git a/sm5/ataprint.cpp b/sm5/ataprint.cpp
index fe7a968a9b7f9b5450438bd85e924a9a38ee163a..ec50e8787f676fb7ed7ae882a2883b598688f200 100644
--- a/sm5/ataprint.cpp
+++ b/sm5/ataprint.cpp
@@ -35,13 +35,14 @@
 #include "int64.h"
 #include "atacmdnames.h"
 #include "atacmds.h"
+#include "dev_interface.h"
 #include "ataprint.h"
 #include "smartctl.h"
 #include "extern.h"
 #include "utility.h"
 #include "knowndrives.h"
 
-const char *ataprint_c_cvsid="$Id: ataprint.cpp,v 1.188 2008/07/17 23:37:15 ballen4705 Exp $"
+const char *ataprint_c_cvsid="$Id: ataprint.cpp,v 1.189 2008/07/25 21:16:00 chrfranke Exp $"
 ATACMDNAMES_H_CVSID ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // for passing global control variables
@@ -1579,7 +1580,7 @@ struct ata_smart_thresholds_pvt smartthres;
 struct ata_smart_errorlog smarterror;
 struct ata_smart_selftestlog smartselftest;
 
-int ataPrintMain (int fd){
+int ataPrintMain (ata_device * device){
   int timewait,code;
   int returnval=0, retid=0, supported=0, needupdate=0, known=0;
   const char * powername = 0; char powerchg = 0;
@@ -1587,7 +1588,7 @@ int ataPrintMain (int fd){
   // If requested, check power mode first
   if (con->powermode) {
     unsigned char powerlimit = 0xff;
-    int powermode = ataCheckPowerMode(fd);
+    int powermode = ataCheckPowerMode(device);
     switch (powermode) {
       case -1:
         if (errno == ENOSYS) {
@@ -1615,7 +1616,7 @@ int ataPrintMain (int fd){
   }
 
   // Start by getting Drive ID information.  We need this, to know if SMART is supported.
-  if ((retid=ataReadHDIdentity(fd,&drive))<0){
+  if ((retid=ataReadHDIdentity(device,&drive))<0){
     pout("Smartctl: Device Read Identity Failed (not an ATA/ATAPI device)\n\n");
     failuretest(MANDATORY_CMD, returnval|=FAILID);
   }
@@ -1663,7 +1664,7 @@ int ataPrintMain (int fd){
       pout("                  Checking for SMART support by trying SMART ENABLE command.\n");
     }
 
-    if (ataEnableSmart(fd)){
+    if (ataEnableSmart(device)){
       pout("                  SMART ENABLE failed - this establishes that this device lacks SMART functionality.\n");
       failuretest(MANDATORY_CMD, returnval|=FAILSMART);
       supported=0;
@@ -1684,17 +1685,15 @@ int ataPrintMain (int fd){
       failuretest(MANDATORY_CMD, returnval|=FAILSMART);
       // check SMART support by trying a command
       pout("                  Checking to be sure by trying SMART RETURN STATUS command.\n");
-      isenabled=ataDoesSmartWork(fd);
+      isenabled=ataDoesSmartWork(device);
     }
     else {
       pout("SMART support is: Available - device has SMART capability.\n");
-#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
-      if (ata_identify_is_cached(fd)) {
+      if (device->ata_identify_is_cached()) {
         pout("                  %sabled status cached by OS, trying SMART RETURN STATUS cmd.\n",
                     (isenabled?"En":"Dis"));
-        isenabled=ataDoesSmartWork(fd);
+        isenabled=ataDoesSmartWork(device);
       }
-#endif
     }
 
     if (isenabled)
@@ -1719,7 +1718,7 @@ int ataPrintMain (int fd){
   
   // Enable/Disable SMART commands
   if (con->smartenable){
-    if (ataEnableSmart(fd)) {
+    if (ataEnableSmart(device)) {
       pout("Smartctl: SMART Enable Failed.\n\n");
       failuretest(MANDATORY_CMD, returnval|=FAILSMART);
     }
@@ -1728,14 +1727,14 @@ int ataPrintMain (int fd){
   }
   
   // From here on, every command requires that SMART be enabled...
-  if (!ataDoesSmartWork(fd)) {
+  if (!ataDoesSmartWork(device)) {
     pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n");
     return returnval;
   }
   
   // Turn off SMART on device
   if (con->smartdisable){    
-    if (ataDisableSmart(fd)) {
+    if (ataDisableSmart(device)) {
       pout( "Smartctl: SMART Disable Failed.\n\n");
       failuretest(MANDATORY_CMD,returnval|=FAILSMART);
     }
@@ -1744,13 +1743,13 @@ int ataPrintMain (int fd){
   }
   
   // Let's ALWAYS issue this command to get the SMART status
-  code=ataSmartStatus2(fd);
+  code=ataSmartStatus2(device);
   if (code==-1)
     failuretest(MANDATORY_CMD, returnval|=FAILSMART);
 
   // Enable/Disable Auto-save attributes
   if (con->smartautosaveenable){
-    if (ataEnableAutoSave(fd)){
+    if (ataEnableAutoSave(device)){
       pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n");
       failuretest(MANDATORY_CMD, returnval|=FAILSMART);
     }
@@ -1758,7 +1757,7 @@ int ataPrintMain (int fd){
       pout("SMART Attribute Autosave Enabled.\n");
   }
   if (con->smartautosavedisable){
-    if (ataDisableAutoSave(fd)){
+    if (ataDisableAutoSave(device)){
       pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n");
       failuretest(MANDATORY_CMD, returnval|=FAILSMART);
     }
@@ -1767,11 +1766,11 @@ int ataPrintMain (int fd){
   }
   
   // for everything else read values and thresholds are needed
-  if (ataReadSmartValues(fd, &smartval)){
+  if (ataReadSmartValues(device, &smartval)){
     pout("Smartctl: SMART Read Values failed.\n\n");
     failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
   }
-  if (ataReadSmartThresholds(fd, &smartthres)){
+  if (ataReadSmartThresholds(device, &smartthres)){
     pout("Smartctl: SMART Read Thresholds failed.\n\n");
     failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
   }
@@ -1783,7 +1782,7 @@ int ataPrintMain (int fd){
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
     needupdate=1;
-    if (ataEnableAutoOffline(fd)){
+    if (ataEnableAutoOffline(device)){
       pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
@@ -1796,7 +1795,7 @@ int ataPrintMain (int fd){
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
     needupdate=1;
-    if (ataDisableAutoOffline(fd)){
+    if (ataDisableAutoOffline(device)){
       pout("Smartctl: SMART Disable Automatic Offline Failed.\n\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
@@ -1804,7 +1803,7 @@ int ataPrintMain (int fd){
       pout("SMART Automatic Offline Testing Disabled.\n");
   }
 
-  if (needupdate && ataReadSmartValues(fd, &smartval)){
+  if (needupdate && ataReadSmartValues(device, &smartval)){
     pout("Smartctl: SMART Read Values failed.\n\n");
     failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
   }
@@ -1924,7 +1923,7 @@ int ataPrintMain (int fd){
     else {
       PRINT_ON(con);
       pout("Log Directory Supported\n");
-      if (ataReadLogDirectory(fd, &smartlogdirectory)){
+      if (ataReadLogDirectory(device, &smartlogdirectory)){
         PRINT_OFF(con);
         pout("Read Log Directory failed.\n\n");
         failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
@@ -1941,7 +1940,7 @@ int ataPrintMain (int fd){
       pout("Warning: device does not support Error Logging\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
-    if (ataReadErrorLog(fd, &smarterror)){
+    if (ataReadErrorLog(device, &smarterror)){
       pout("Smartctl: SMART Error Log Read Failed\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
@@ -1959,7 +1958,7 @@ int ataPrintMain (int fd){
       pout("Warning: device does not support Self Test Logging\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }    
-    if(ataReadSelfTestLog(fd, &smartselftest)){
+    if(ataReadSelfTestLog(device, &smartselftest)){
       pout("Smartctl: SMART Self Test Log Read Failed\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
@@ -1978,7 +1977,7 @@ int ataPrintMain (int fd){
     
     if (!isSupportSelectiveSelfTest(&smartval))
       pout("Device does not support Selective Self Tests/Logging\n");
-    else if(ataReadSelectiveSelfTestLog(fd, &log)) {
+    else if(ataReadSelectiveSelfTestLog(device, &log)) {
       pout("Smartctl: SMART Selective Self Test Log Read Failed\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
@@ -2003,7 +2002,7 @@ int ataPrintMain (int fd){
         ata_sct_temperature_history_table tmh;
         if (!con->scttemphist) {
           // Read SCT status only
-          if (ataReadSCTStatus(fd, &sts)) {
+          if (ataReadSCTStatus(device, &sts)) {
             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
             break;
           }
@@ -2015,7 +2014,7 @@ int ataPrintMain (int fd){
             break;
           }
           // Read SCT status and temperature history
-          if (ataReadSCTTempHist(fd, &tmh, &sts)) {
+          if (ataReadSCTTempHist(device, &tmh, &sts)) {
             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
             break;
           }
@@ -2033,7 +2032,7 @@ int ataPrintMain (int fd){
           failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
           break;
         }
-        if (ataSetSCTTempInterval(fd, con->scttempint, !!con->scttempintp)) {
+        if (ataSetSCTTempInterval(device, con->scttempint, !!con->scttempintp)) {
           failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
           break;
         }
@@ -2089,7 +2088,7 @@ int ataPrintMain (int fd){
 
   // Now do the test.  Note ataSmartTest prints its own error/success
   // messages
-  if (ataSmartTest(fd, con->testcase, &smartval, get_num_sectors(&drive)))
+  if (ataSmartTest(device, con->testcase, &smartval, get_num_sectors(&drive)))
     failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
   else {  
     // Tell user how long test will take to complete.  This is tricky
@@ -2100,7 +2099,7 @@ int ataPrintMain (int fd){
     if (con->testcase==OFFLINE_FULL_SCAN){
       if (isSupportOfflineAbort(&smartval))
 	pout("Note: giving further SMART commands will abort Offline testing\n");
-      else if (ataReadSmartValues(fd, &smartval)){
+      else if (ataReadSmartValues(device, &smartval)){
 	pout("Smartctl: SMART Read Values failed.\n");
 	failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
       }
diff --git a/sm5/ataprint.h b/sm5/ataprint.h
index 82b533de71e713bd9dedc37d6034d0347c1d5025..a81236ef6eac20eee05e33240e406d0dfb8cca1b 100644
--- a/sm5/ataprint.h
+++ b/sm5/ataprint.h
@@ -25,7 +25,7 @@
 #ifndef ATAPRINT_H_
 #define ATAPRINT_H_
 
-#define ATAPRINT_H_CVSID "$Id: ataprint.h,v 1.31 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define ATAPRINT_H_CVSID "$Id: ataprint.h,v 1.32 2008/07/25 21:16:00 chrfranke Exp $\n"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -56,6 +56,6 @@ void ataPseudoCheckSmart(struct ata_smart_values *, struct ata_smart_thresholds_
 // Convenience function for formatting strings from ata_identify_device.
 void format_ata_string(char *out, const char *in, int n);
 
-int ataPrintMain(int fd);
+int ataPrintMain(ata_device * device);
 
 #endif
diff --git a/sm5/configure.in b/sm5/configure.in
index d002f798c0901ce8d0bd7a79500dce89b17406b6..0615f42f6563b1b1d47ba8760e3a3d8525cd9821 100644
--- a/sm5/configure.in
+++ b/sm5/configure.in
@@ -1,5 +1,5 @@
 #
-# $Id: configure.in,v 1.140 2008/04/27 16:30:09 chrfranke Exp $
+# $Id: configure.in,v 1.141 2008/07/25 21:16:00 chrfranke Exp $
 #
 dnl Process this file with autoconf to produce a configure script.
 AC_PREREQ(2.50)
@@ -7,7 +7,7 @@ AC_INIT(smartmontools, 5.39, smartmontools-support@lists.sourceforge.net)
 AC_CONFIG_SRCDIR(smartctl.cpp)
 
 smartmontools_configure_date=`date -u +"%Y/%m/%d %T %Z"`
-smartmontools_cvs_tag=`echo '$Id: configure.in,v 1.140 2008/04/27 16:30:09 chrfranke Exp $'`
+smartmontools_cvs_tag=`echo '$Id: configure.in,v 1.141 2008/07/25 21:16:00 chrfranke Exp $'`
 smartmontools_release_date=2008/03/10
 smartmontools_release_time="10:44:07 GMT"
 
@@ -219,6 +219,17 @@ case "${os_deps}" in
     AC_DEFINE(HAVE_GET_OS_VERSION_STR, 1, [Define to 1 if you have the `get_os_version_str' function in os_*.c.]) ;;
 esac
 
+# Check if we need adapter to old interface (dev_legacy.cpp)
+os_src=`echo "${os_deps}"|sed -n 's,^\([[^ .]]*\)\.o.*$,\1.cpp,p'`
+AC_MSG_CHECKING([whether ${os_src} uses new interface])
+if grep "smart_interface" "${srcdir}/${os_src}" >/dev/null 2>&1; then
+  os_new_interface=yes
+else
+  os_new_interface=no
+  os_deps="${os_deps} dev_legacy.o"
+fi
+AC_MSG_RESULT([$os_new_interface])
+
 dnl Define platform-specific symbol.
 AM_CONDITIONAL(OS_DARWIN, [echo $host_os | grep '^darwin' > /dev/null])
 AM_CONDITIONAL(OS_SOLARIS, [echo $host_os | grep '^solaris' > /dev/null])
diff --git a/sm5/dev_ata_cmd_set.cpp b/sm5/dev_ata_cmd_set.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..85dc08cee8a93afa1ded75c320a2690fe7f32562
--- /dev/null
+++ b/sm5/dev_ata_cmd_set.cpp
@@ -0,0 +1,127 @@
+/*
+ * dev_ata_cmd_set.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "dev_ata_cmd_set.h"
+
+#include <errno.h>
+
+const char * dev_ata_cmd_set_cpp_cvsid = "$Id: dev_ata_cmd_set.cpp,v 1.1 2008/07/25 21:16:00 chrfranke Exp $"
+  DEV_ATA_CMD_SET_H_CVSID;
+
+#define SMART_CYL_LOW  0x4F
+#define SMART_CYL_HI   0xC2
+
+/////////////////////////////////////////////////////////////////////////////
+// ata_device_with_command_set
+
+// Adapter routine to implement new ATA pass through with old interface
+
+bool ata_device_with_command_set::ata_pass_through_28bit(const ata_cmd_in & in, ata_cmd_out & out)
+{
+  smart_command_set command = (smart_command_set)-1;
+  int select = 0;
+  char * data = (char *)in.buffer;
+  char buffer[512];
+  switch (in.in_regs.command) {
+    case ATA_IDENTIFY_DEVICE:
+      command = IDENTIFY;
+      break;
+    case ATA_IDENTIFY_PACKET_DEVICE:
+      command = PIDENTIFY;
+      break;
+    case ATA_CHECK_POWER_MODE:
+      command = CHECK_POWER_MODE;
+      data = buffer; data[0] = 0;
+      break;
+    case ATA_SMART_CMD:
+      switch (in.in_regs.features) {
+        case ATA_SMART_ENABLE:
+          command = ENABLE;
+          break;
+        case ATA_SMART_READ_VALUES:
+          command = READ_VALUES;
+          break;
+        case ATA_SMART_READ_THRESHOLDS:
+          command = READ_THRESHOLDS;
+          break;
+        case ATA_SMART_READ_LOG_SECTOR:
+          command = READ_LOG;
+          select = in.in_regs.lba_low;
+          break;
+        case ATA_SMART_WRITE_LOG_SECTOR:
+          command = WRITE_LOG;
+          select = in.in_regs.lba_low;
+          break;
+        case ATA_SMART_DISABLE:
+          command = DISABLE;
+          break;
+        case ATA_SMART_STATUS:
+          command = (in.out_needed.lba_high ? STATUS_CHECK : STATUS);
+          break;
+        case ATA_SMART_AUTO_OFFLINE:
+          command = AUTO_OFFLINE;
+          select = in.in_regs.sector_count;
+          break;
+        case ATA_SMART_AUTOSAVE:
+          command = AUTOSAVE;
+          select = in.in_regs.sector_count;
+          break;
+        case ATA_SMART_IMMEDIATE_OFFLINE:
+          command = IMMEDIATE_OFFLINE;
+          select = in.in_regs.lba_low;
+          break;
+        default:
+          set_err(ENOSYS);
+          return false;
+      }
+      break;
+    default:
+      set_err(ENOSYS);
+      return false;
+  }
+
+  clear_err(); errno = 0;
+  int rc = ata_command_interface(command, select, data);
+  if (rc < 0) {
+    if (!get_errno())
+      set_err(errno);
+    return false;
+  }
+
+  switch (command) {
+    case CHECK_POWER_MODE:
+      out.out_regs.sector_count = data[0];
+      break;
+    case STATUS_CHECK:
+      switch (rc) {
+        case 0: // Good SMART status
+          out.out_regs.lba_high = SMART_CYL_HI; out.out_regs.lba_mid = SMART_CYL_LOW;
+          break;
+        case 1: // Bad SMART status
+          out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
+          break;
+      }
+      break;
+    default:
+      break;
+  }
+  return true;
+};
+
diff --git a/sm5/dev_ata_cmd_set.h b/sm5/dev_ata_cmd_set.h
new file mode 100644
index 0000000000000000000000000000000000000000..dfe963f4741a04811f573f53e52a3abd48faa3dc
--- /dev/null
+++ b/sm5/dev_ata_cmd_set.h
@@ -0,0 +1,45 @@
+/*
+ * dev_ata_cmd_set.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef DEV_ATA_CMD_SET_H
+#define DEV_ATA_CMD_SET_H
+
+#define DEV_ATA_CMD_SET_H_CVSID "$Id: dev_ata_cmd_set.h,v 1.1 2008/07/25 21:16:00 chrfranke Exp $\n"
+
+#include "atacmds.h" // smart_command_set
+#include "dev_interface.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// ata_device_with_command_set
+
+/// Adapter class to implement new ATA pass through old interface.
+
+class ata_device_with_command_set
+: public /*implements*/ ata_device
+{
+protected:
+  /// 28-bit ATA pass through mapped to ata_command_interface().
+  virtual bool ata_pass_through_28bit(const ata_cmd_in & in, ata_cmd_out & out);
+
+  /// Old ATA interface called by ata_pass_through_28bit.
+  virtual int ata_command_interface(smart_command_set command, int select, char * data) = 0;
+
+  ata_device_with_command_set()
+    : smart_device(never_called) { }
+};
+
+#endif // DEV_ATA_CMD_SET_H
diff --git a/sm5/dev_interface.cpp b/sm5/dev_interface.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7d657d0a0c3c677fa827ce8d75ed1b8b174279b0
--- /dev/null
+++ b/sm5/dev_interface.cpp
@@ -0,0 +1,275 @@
+/*
+ * dev_interface.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "dev_interface.h"
+#include "dev_tunnelled.h"
+#include "utility.h"
+
+#include <stdexcept>
+
+const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp,v 1.1 2008/07/25 21:16:00 chrfranke Exp $"
+  DEV_INTERFACE_H_CVSID;
+
+/////////////////////////////////////////////////////////////////////////////
+// smart_device
+
+smart_device::smart_device(smart_interface * intf, const char * dev_name,
+    const char * dev_type, const char * req_type)
+: m_intf(intf), m_info(dev_name, dev_type, req_type),
+  m_ata_ptr(0), m_scsi_ptr(0)
+{
+}
+
+smart_device::smart_device(do_not_use_in_implementation_classes)
+: m_intf(0), m_ata_ptr(0), m_scsi_ptr(0)
+{
+  throw std::logic_error("smart_device: wrong constructor called in implementation class");
+}
+
+smart_device::~smart_device() throw()
+{
+}
+
+bool smart_device::set_err(int no, const char * msg, ...)
+{
+  if (!msg)
+    return set_err(no);
+  m_err.no = no;
+  va_list ap; va_start(ap, msg);
+  m_err.msg = vstrprintf(msg, ap);
+  va_end(ap);
+  return false;
+}
+
+bool smart_device::set_err(int no)
+{
+  smi()->set_err_var(&m_err, no);
+  return false;
+}
+
+smart_device * smart_device::autodetect_open()
+{
+  open();
+  return this;
+}
+
+bool smart_device::owns(const smart_device * /*dev*/) const
+{
+  return false;
+}
+
+void smart_device::release(const smart_device * /*dev*/)
+{
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ata_device
+
+ata_cmd_in::ata_cmd_in()
+: direction(no_data),
+  buffer(0),
+  size(0)
+{
+}
+
+ata_cmd_out::ata_cmd_out()
+{
+}
+
+bool ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+{
+  if (!in.in_regs.is_48bit_cmd())
+    return ata_pass_through_28bit(in, out);
+  else
+    return ata_pass_through_48bit(in, out);
+}
+
+bool ata_device::ata_pass_through(const ata_cmd_in & in)
+{
+  ata_cmd_out dummy;
+  return ata_pass_through(in, dummy);
+}
+
+bool ata_device::ata_pass_through_48bit(const ata_cmd_in & /*in*/, ata_cmd_out & /*out*/)
+{
+  return set_err(ENOSYS, "48-bit ATA commands not supported");
+}
+
+bool ata_device::ata_identify_is_cached() const
+{
+  return false;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// tunnelled_device_base
+
+tunnelled_device_base::tunnelled_device_base(smart_device * tunnel_dev)
+: smart_device(never_called),
+  m_tunnel_base_dev(tunnel_dev)
+{
+}
+
+tunnelled_device_base::~tunnelled_device_base() throw()
+{
+  delete m_tunnel_base_dev;
+}
+
+bool tunnelled_device_base::is_open() const
+{
+  return (m_tunnel_base_dev && m_tunnel_base_dev->is_open());
+}
+
+bool tunnelled_device_base::open()
+{
+  if (!m_tunnel_base_dev)
+    return set_err(ENOSYS);
+  if (!m_tunnel_base_dev->open())
+    return set_err(m_tunnel_base_dev->get_err());
+  return true;
+}
+
+bool tunnelled_device_base::close()
+{
+  if (!m_tunnel_base_dev)
+    return true;
+  if (!m_tunnel_base_dev->close())
+    return set_err(m_tunnel_base_dev->get_err());
+  return true;
+}
+
+bool tunnelled_device_base::owns(const smart_device * dev) const
+{
+  return (m_tunnel_base_dev && (m_tunnel_base_dev == dev));
+}
+
+void tunnelled_device_base::release(const smart_device * dev)
+{
+  if (m_tunnel_base_dev == dev)
+    m_tunnel_base_dev = 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// smart_interface
+
+// Pointer to (usually singleton) interface object returned by ::smi()
+smart_interface * smart_interface::s_instance;
+
+const char * smart_interface::get_os_version_str()
+{
+  return SMARTMONTOOLS_BUILD_HOST;
+}
+
+const char * smart_interface::get_valid_dev_types_str()
+{
+  static char buf[80+1];
+  if (buf[0])
+    return buf;
+  // default
+  strcpy(buf, "ata, scsi, sat[,N]");
+  // append custom
+  const char * add = get_valid_custom_dev_types_str();
+  if (!add || !*add)
+    return buf;
+  strcat(buf, ", ");
+  strcat(buf, add);
+  return buf;
+}
+
+const char * smart_interface::get_app_examples(const char * /*appname*/)
+{
+  return 0;
+}
+
+void smart_interface::set_err(int no, const char * msg, ...)
+{
+  if (!msg) {
+    set_err(no); return;
+  }
+  m_err.no = no;
+  va_list ap; va_start(ap, msg);
+  m_err.msg = vstrprintf(msg, ap);
+  va_end(ap);
+}
+
+void smart_interface::set_err(int no)
+{
+  set_err_var(&m_err, no);
+}
+
+void smart_interface::set_err_var(smart_device::error_info * err, int no)
+{
+  err->no = no;
+  err->msg = get_msg_for_errno(no);
+  if (err->msg.empty() && no != 0)
+    err->msg = strprintf("Unknown error %d", no);
+}
+
+const char * smart_interface::get_msg_for_errno(int no)
+{
+  return strerror(no);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Default device factory
+
+smart_device * smart_interface::get_smart_device(const char * name, const char * type)
+{
+  clear_err();
+  if (!type || !*type) {
+    smart_device * dev = autodetect_smart_device(name);
+    if (!dev && !get_errno())
+      set_err(EINVAL, "Unable to detect device type");
+    return dev;
+  }
+
+  smart_device * dev = get_custom_smart_device(name, type);
+  if (dev || get_errno())
+    return dev;
+
+  if (!strcmp(type, "ata"))
+    dev = get_ata_device(name, type);
+  else if (!strcmp(type, "scsi"))
+    dev = get_scsi_device(name, type);
+  else if (   (!strncmp(type, "sat", 3) && (!type[3] || type[3] == ','))
+           || (!strncmp(type, "usb", 3)))
+    dev = get_sat_device(name, type /*, 0*/);
+  else {
+    set_err(EINVAL, "Unknown device type '%s'", type);
+    return 0;
+  }
+  if (!dev && !get_errno())
+    set_err(EINVAL, "Not a device of type '%s'", type);
+  return dev;
+}
+
+smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/)
+{
+  return 0;
+}
+
+const char * smart_interface::get_valid_custom_dev_types_str()
+{
+  return 0;
+}
diff --git a/sm5/dev_interface.h b/sm5/dev_interface.h
new file mode 100644
index 0000000000000000000000000000000000000000..304bcfdad0b9ec1b8577669f8b28e80435ca64f9
--- /dev/null
+++ b/sm5/dev_interface.h
@@ -0,0 +1,630 @@
+/*
+ * dev_interface.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef DEV_INTERFACE_H
+#define DEV_INTERFACE_H
+
+#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h,v 1.1 2008/07/25 21:16:00 chrfranke Exp $\n"
+
+#include <stdarg.h>
+#include <string>
+#include <vector>
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)  /**/
+#endif
+
+#ifdef _MSC_VER // Disable MSVC warning
+#pragma warning(disable:4250) // 'class1' : inherits 'class2::member' via dominance
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Common functionality for all device types
+
+// Forward declarations
+class smart_interface;
+class ata_device;
+class scsi_device;
+
+/// Base class for all devices
+class smart_device
+{
+// Types
+public:
+  /// Device info strings
+  struct device_info {
+    device_info()
+      { }
+    device_info(const char * d_name, const char * d_type, const char * r_type)
+      : dev_name(d_name), info_name(d_name),
+        dev_type(d_type), req_type(r_type)
+      { }
+
+    std::string dev_name;  ///< Device (path)name
+    std::string info_name; ///< Informal name
+    std::string dev_type;  ///< Actual device type
+    std::string req_type;  ///< Device type requested by user, empty if none
+  };
+
+  /// Error (number,message) pair
+  struct error_info {
+    explicit error_info(int n = 0)
+      : no(n) { }
+    error_info(int n, const char * m)
+      : no(n), msg(m) { }
+    void clear()
+      { no = 0; msg.erase(); }
+
+    int no;          ///< Error number
+    std::string msg; ///< Error message
+  };
+
+// Construction
+protected:
+  /// Constructor to init interface and device info.
+  /// Must be called in implementation classes.
+  smart_device(smart_interface * intf, const char * dev_name,
+    const char * dev_type, const char * req_type);
+
+  /// Dummy enum for dummy constructor.
+  enum do_not_use_in_implementation_classes { never_called };
+  /// Dummy constructor for abstract classes.
+  /// Must never be called in implementation classes.
+  smart_device(do_not_use_in_implementation_classes);
+
+public:
+  virtual ~smart_device() throw();
+
+// Attributes
+public:
+  ///////////////////////////////////////////////
+  // Dynamic downcasts to actual device flavor
+
+  /// Return true if ATA device
+  bool is_ata() const
+    { return !!m_ata_ptr; }
+  /// Return true if SCSI device
+  bool is_scsi() const
+    { return !!m_scsi_ptr; }
+
+  /// Downcast to ATA device.
+  ata_device * to_ata()
+    { return m_ata_ptr; }
+  /// Downcast to ATA device (const).
+  const ata_device * to_ata() const
+    { return m_ata_ptr; }
+  /// Downcast to SCSI device.
+  scsi_device * to_scsi()
+    { return m_scsi_ptr; }
+  /// Downcast to ATA device (const).
+  const scsi_device * to_scsi() const
+    { return m_scsi_ptr; }
+
+  ///////////////////////////////////////////////
+  // Device information
+
+  /// Get device info struct.
+  const device_info & get_info() const
+    { return m_info; }
+
+  /// Get device (path)name.
+  const char * get_dev_name() const
+    { return m_info.dev_name.c_str(); }
+  /// Get informal name.
+  const char * get_info_name() const
+    { return m_info.info_name.c_str(); }
+  /// Get device type.
+  const char * get_dev_type() const
+    { return m_info.dev_type.c_str(); }
+  /// Get type requested by user, empty if none.
+  const char * get_req_type() const
+    { return m_info.req_type.c_str(); }
+
+protected:
+  /// R/W access to device info struct.
+  device_info & set_info()
+    { return m_info; }
+
+public:
+  ///////////////////////////////////////////////
+  // Last error information
+
+  /// Get last error info struct.
+  const error_info & get_err() const
+    { return m_err; }
+  /// Get last error number.
+  int get_errno() const
+    { return m_err.no; }
+  /// Get last error message.
+  const char * get_errmsg() const
+    { return m_err.msg.c_str(); }
+
+  /// Set last error number and message.
+  /// Printf()-like formatting is supported.
+  /// Returns false always to allow use as a return expression.
+  bool set_err(int no, const char * msg, ...)
+    __attribute__ ((format (printf, 3, 4)));
+
+  /// Set last error info struct.
+  bool set_err(const error_info & err)
+    { m_err = err; return false; }
+
+  /// Clear last error info.
+  void clear_err()
+    { m_err.clear(); }
+
+  /// Set last error number and default message.
+  /// Message is retrieved from interface's get_msg_for_errno(no).
+  bool set_err(int no);
+
+// Operations
+public:
+  ///////////////////////////////////////////////
+  // Device open/close
+  // Must be implemented in derived class
+
+  /// Return true if device is open.
+  virtual bool is_open() const = 0;
+
+  /// Open device, return false on error.
+  virtual bool open() = 0;
+
+  /// Close device, return false on error.
+  virtual bool close() = 0;
+
+  /// Open device with autodetection support.
+  /// May return another device for further access.
+  /// In this case, the original pointer is no longer valid.
+  /// Default Implementation calls 'open()' and returns 'this'.
+  virtual smart_device * autodetect_open();
+
+  ///////////////////////////////////////////////
+  // Support for tunnelled devices
+
+  /// Return true if other device is owned by this device.
+  /// Default implementation returns false.
+  virtual bool owns(const smart_device * dev) const;
+
+  /// Release ownership of other device.
+  /// Default implementation does nothing.
+  virtual void release(const smart_device * dev);
+
+protected:
+  /// Set dynamic downcast for ATA
+  void this_is_ata(ata_device * ata);
+    // {see below;}
+
+  /// Set dynamic downcast for SCSI
+  void this_is_scsi(scsi_device * scsi);
+    // {see below;}
+
+  /// Get interface which produced this object.
+  smart_interface * smi()
+    { return m_intf; }
+  /// Get interface which produced this object (const).
+  const smart_interface * smi() const
+    { return m_intf; }
+
+// Implementation
+private:
+  smart_interface * m_intf;
+  device_info m_info;
+  ata_device * m_ata_ptr;
+  scsi_device * m_scsi_ptr;
+  error_info m_err;
+
+  // Prevent copy/assigment
+  smart_device(const smart_device &);
+  void operator=(const smart_device &);
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ATA specific interface
+
+/// ATA register value and info whether is has been ever set
+// (Automatically set by first assignment)
+class ata_register
+{
+public:
+  ata_register()
+    : m_val(0x00), m_is_set(false) { }
+  ata_register & operator=(unsigned char val)
+    { m_val = val; m_is_set = true; return * this; }
+  unsigned char val() const
+    { return m_val; }
+  operator unsigned char() const
+    { return m_val; }
+  bool is_set() const
+    { return m_is_set; }
+
+private:
+  unsigned char m_val; ///< Register value
+  bool m_is_set; ///< true if set
+};
+
+/// ATA Input registers (for 28-bit commands)
+struct ata_in_regs
+{
+  // ATA-6/7 register names  // ATA-3/4/5        // ATA-8
+  ata_register features;     // features         // features
+  ata_register sector_count; // sector count     // count
+  ata_register lba_low;      // sector number    // ]
+  ata_register lba_mid;      // cylinder low     // ] lba
+  ata_register lba_high;     // cylinder high    // ]
+  ata_register device;       // device/head      // device
+  ata_register command;      // command          // command
+
+  /// Return true if any register is set
+  bool is_set() const
+    { return (features.is_set() || sector_count.is_set()
+      || lba_low.is_set() || lba_mid.is_set() || lba_high.is_set()
+      || device.is_set() || command.is_set());                    }
+};
+
+/// ATA Output registers (for 28-bit commands)
+struct ata_out_regs
+{
+  ata_register error;
+  ata_register sector_count;
+  ata_register lba_low;
+  ata_register lba_mid;
+  ata_register lba_high;
+  ata_register device;
+  ata_register status;
+
+  /// Return true if any register is set
+  bool is_set() const
+    { return (error.is_set() || sector_count.is_set()
+      || lba_low.is_set() || lba_mid.is_set() || lba_high.is_set()
+      || device.is_set() || status.is_set());                      }
+};
+
+
+/// ATA Input registers for 48-bit commands
+// See section 4.14 of T13/1532D Volume 1 Revision 4b
+//
+// Uses ATA-6/7 method to specify 16-bit registers as
+// recent (low byte) and previous (high byte) content of
+// 8-bit registers.
+//
+// (ATA-8 ACS does not longer follow this scheme, it uses
+// abstract registers with sufficient size and leaves the
+// actual mapping to the transport layer.)
+//
+struct ata_in_regs_48bit
+: public ata_in_regs   // "most recently written" registers
+{
+  ata_in_regs prev;  ///< "previous content"
+
+  /// Return true if 48-bit command
+  bool is_48bit_cmd() const
+    { return prev.is_set(); }
+
+  ata_in_regs_48bit()
+    { }
+  // Allow initialization from ata_in_regs (leaves prev unset)
+  ata_in_regs_48bit(const ata_in_regs & r)
+    : ata_in_regs(r) { }
+};
+
+
+/// ATA Output registers for 48-bit commands
+struct ata_out_regs_48bit
+: public ata_out_regs   // read with HOB=0
+{
+  ata_out_regs prev;  ///< read with HOB=1
+};
+
+
+/// Flags for each ATA output register
+struct ata_out_regs_flags
+{
+  bool error, sector_count, lba_low, lba_mid, lba_high, device, status;
+
+  ata_out_regs_flags()
+    : error(false), sector_count(false), lba_low(false), lba_mid(false),
+      lba_high(false), device(false), status(false) { }
+};
+
+
+/// ATA pass through input parameters
+struct ata_cmd_in
+{
+  ata_in_regs_48bit in_regs;  ///< Input registers
+  ata_out_regs_flags out_needed; ///< True if output register value needed
+  enum { no_data = 0, data_in, data_out } direction; ///< I/O direction
+  void * buffer; ///< Pointer to data buffer
+  unsigned size; ///< Size of buffer
+
+  void set_data_in(unsigned nsectors)
+    { in_regs.sector_count = nsectors; direction = data_in; size = 512*nsectors; }
+  void set_data_out(unsigned nsectors)
+    { in_regs.sector_count = nsectors; direction = data_out; size = 512*nsectors; }
+
+  ata_cmd_in();
+};
+
+/// ATA pass through output parameters
+struct ata_cmd_out
+{
+  ata_out_regs_48bit out_regs; ///< Output registers
+
+  ata_cmd_out();
+};
+
+/// ATA device access
+class ata_device
+: virtual public /*extends*/ smart_device
+{
+public:
+  /// ATA pass through.
+  /// Return false on error.
+  /// Default implementation calls ata_pass_through_28bit or _48bit.
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+
+  /// ATA pass through without output registers.
+  /// Return false on error.
+  /// Calls ata_pass_through(in, dummy), cannot be reimplemented.
+  bool ata_pass_through(const ata_cmd_in & in);
+
+  /// Return true if OS caches ATA identify sector.
+  /// Default implementation returns false.
+  virtual bool ata_identify_is_cached() const;
+
+protected:
+  /// 28-bit ATA pass through.
+  /// Must be implemented in derived class.
+  virtual bool ata_pass_through_28bit(const ata_cmd_in & in, ata_cmd_out & out) = 0;
+
+  /// 48-bit ATA pass through.
+  /// Default implementation returns false and sets errno to ENOSYS.
+  virtual bool ata_pass_through_48bit(const ata_cmd_in & in, ata_cmd_out & out);
+
+  /// Default constructor, registers device as ATA.
+  ata_device()
+    : smart_device(never_called)
+    { this_is_ata(this); }
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// SCSI specific interface
+
+struct scsi_cmnd_io;
+
+/// SCSI device access
+class scsi_device
+: virtual public /*extends*/ smart_device
+{
+public:
+  /// SCSI pass through.
+  /// Returns false on error.
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop) = 0;
+
+protected:
+  /// Default constructor, registers device as SCSI.
+  scsi_device()
+    : smart_device(never_called)
+    { this_is_scsi(this); }
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Set dynamic downcasts
+// Note that due to virtual inheritance,
+// (ata == this) does not imply ((void*)ata == (void*)this))
+
+inline void smart_device::this_is_ata(ata_device * ata)
+{
+  m_ata_ptr = (ata == this ? ata : 0);
+}
+
+inline void smart_device::this_is_scsi(scsi_device * scsi)
+{
+  m_scsi_ptr = (scsi == this ? scsi : 0);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// smart_device_list
+
+/// List of devices for DEVICESCAN
+class smart_device_list
+{
+// Construction
+public:
+  smart_device_list()
+    { }
+
+  ~smart_device_list() throw()
+    {
+      for (unsigned i = 0; i < m_list.size(); i++)
+        delete m_list[i];
+    }
+
+// Attributes
+  int size() const
+    { return m_list.size(); }
+
+// Operations
+  void add(smart_device * dev)
+    { m_list.push_back(dev); }
+
+  smart_device * get(int i)
+    { return m_list.at(i); }
+
+  smart_device * detach(int i)
+    {
+      smart_device * dev = m_list.at(i);
+      m_list[i] = 0;
+      return dev;
+    }
+
+// Implementation
+private:
+  std::vector<smart_device *> m_list;
+
+  // Prevent copy/assigment
+  smart_device_list(const smart_device_list &);
+  void operator=(const smart_device_list &);
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// smart_interface
+
+/// The platform interface abstraction
+class smart_interface
+{
+public:
+  /// Initialize platform interface and register with smi().
+  /// Must be implemented by platform module and register interface with set()
+  static void init();
+
+  smart_interface()
+    { }
+
+  virtual ~smart_interface() throw()
+    { }
+
+  /// Return build host and OS version as static string
+  virtual const char * get_os_version_str();
+
+  /// Return valid args for device type option/directive.
+  /// Default implementation returns "ata, scsi" concatenated
+  /// with result from get_valid_custom_dev_types_str() below.
+  virtual const char * get_valid_dev_types_str();
+
+  /// Return example string for program 'appname'.
+  /// Default implementation returns 0.
+  /// For the migration of print_smartctl_examples(),
+  /// function is allowed to print examples to stdout.
+  /// TODO: Remove this hack.
+  virtual const char * get_app_examples(const char * appname);
+
+  ///////////////////////////////////////////////
+  // Last error information
+
+  /// Get last error info struct.
+  const smart_device::error_info & get_err() const
+    { return m_err; }
+  /// Get last error number.
+  int get_errno() const
+    { return m_err.no; }
+  /// Get last error message.
+  const char * get_errmsg() const
+    { return m_err.msg.c_str(); }
+
+  /// Set last error number and message.
+  /// Printf()-like formatting is supported.
+  void set_err(int no, const char * msg, ...)
+    __attribute__ ((format (printf, 3, 4)));
+
+  /// Set last error info struct.
+  void set_err(const smart_device::error_info & err)
+    { m_err = err; }
+
+  /// Clear last error info.
+  void clear_err()
+    { m_err.clear(); }
+
+  /// Set last error number and default message.
+  /// Message is retrieved from get_msg_for_errno(no).
+  void set_err(int no);
+
+  /// Set last error number and default message to any error_info.
+  /// Used by set_err(no).
+  void set_err_var(smart_device::error_info * err, int no);
+
+  /// Convert error number into message, used by set_err(no).
+  /// Default implementation returns strerror(no).
+  virtual const char * get_msg_for_errno(int no);
+
+  ///////////////////////////////////////////////////////////////////////////
+  // Device factory:
+
+  /// Return device object for device 'name' with some 'type'.
+  /// 'type' is 0 if not specified by user.
+  /// Return 0 on error.
+  /// Default implementation selects between ata, scsi and custom device.
+  virtual smart_device * get_smart_device(const char * name, const char * type);
+
+  /// Fill 'devlist' with devices of some 'type' with devices names.
+  /// specified by some optional 'pattern'.
+  /// Return false on error.
+  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
+    const char * pattern = 0) = 0;
+
+protected:
+  /// Return standard ATA device.
+  virtual ata_device * get_ata_device(const char * name, const char * type) = 0;
+
+  /// Return standard SCSI device.
+  virtual scsi_device * get_scsi_device(const char * name, const char * type) = 0;
+
+  /// Autodetect device if no device type specified.
+  virtual smart_device * autodetect_smart_device(const char * name) = 0;
+
+  /// Return device for platform specific 'type'.
+  /// Default implementation returns 0.
+  virtual smart_device * get_custom_smart_device(const char * name, const char * type);
+
+  /// Return valid 'type' args accepted by above.
+  /// This is called in get_valid_dev_types_str().
+  /// Default implementation returns 0.
+  virtual const char * get_valid_custom_dev_types_str();
+
+  /// Return ATA->SCSI filter for SAT or USB.
+  /// Override only if platform needs special handling.
+  virtual ata_device * get_sat_device(const char * name, const char * type, scsi_device * scsidev = 0);
+  //{ implemented in scsiata.cpp }
+
+public:
+  /// Try to detect a SAT device behind a SCSI interface.
+  /// Inquiry data can be passed if available.
+  /// Return appropriate device if yes, otherwise 0.
+  /// Override only if platform needs special handling.
+  virtual ata_device * autodetect_sat_device(scsi_device * scsidev,
+    const unsigned char * inqdata, unsigned inqsize);
+  //{ implemented in scsiata.cpp }
+
+protected:
+  /// Set interface to use, must be called from init().
+  static void set(smart_interface * intf)
+    { s_instance = intf; }
+
+// Implementation
+private:
+  smart_device::error_info m_err;
+
+  friend smart_interface * smi(); // below
+  static smart_interface * s_instance; ///< Pointer to the interface object.
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// smi()
+
+/// Global access to the (usually singleton) smart_interface
+inline smart_interface * smi()
+  { return smart_interface::s_instance; }
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif // DEV_INTERFACE_H
diff --git a/sm5/dev_legacy.cpp b/sm5/dev_legacy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2763907a4cd2bad2912259560b61ee4c5931531d
--- /dev/null
+++ b/sm5/dev_legacy.cpp
@@ -0,0 +1,667 @@
+/*
+ * dev_legacy.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "extern.h"
+#include "utility.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "dev_interface.h"
+#include "dev_ata_cmd_set.h"
+
+const char * dev_legacy_cpp_cvsid = "$Id: dev_legacy.cpp,v 1.1 2008/07/25 21:16:00 chrfranke Exp $"
+  DEV_INTERFACE_H_CVSID;
+
+extern smartmonctrl * con; // con->reportscsiioctl
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Legacy interface declarations (now commented out globally):
+
+// from utility.h:
+int guess_device_type(const char * dev_name);
+int make_device_names (char ***devlist, const char* name);
+int deviceopen(const char *pathname, char *type);
+int deviceclose(int fd);
+#ifdef HAVE_GET_OS_VERSION_STR
+const char * get_os_version_str(void);
+#endif
+
+// from atacmds.h:
+int ata_command_interface(int device, smart_command_set command, int select, char *data);
+int escalade_command_interface(int fd, int escalade_port, int escalade_type, smart_command_set command, int select, char *data);
+int marvell_command_interface(int device, smart_command_set command, int select, char *data);
+int highpoint_command_interface(int device, smart_command_set command, int select, char *data);
+int areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data);
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+int ata_identify_is_cached(int fd);
+#endif
+
+// from scsicmds.h:
+int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
+
+// from smartctl.h:
+void print_smartctl_examples();
+
+/////////////////////////////////////////////////////////////////////////////
+
+namespace os { // No need to publish anything, name provided for Doxygen
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement shared open/close routines with old functions.
+
+class legacy_smart_device
+: virtual public /*implements*/ smart_device
+{
+public:
+  explicit legacy_smart_device(const char * mode)
+    : smart_device(never_called),
+      m_fd(-1), m_mode(mode) { }
+
+  virtual ~legacy_smart_device() throw();
+
+  virtual bool is_open() const;
+
+  virtual bool open();
+
+  virtual bool close();
+
+protected:
+  /// Return filedesc for derived classes.
+  int get_fd() const
+    { return m_fd; }
+
+private:
+  int m_fd; ///< filedesc, -1 if not open.
+  const char * m_mode; ///< Mode string for deviceopen().
+};
+
+
+legacy_smart_device::~legacy_smart_device() throw()
+{
+  if (m_fd >= 0)
+    ::deviceclose(m_fd);
+}
+
+bool legacy_smart_device::is_open() const
+{
+  return (m_fd >= 0);
+}
+
+bool legacy_smart_device::open()
+{
+  m_fd = ::deviceopen(get_dev_name(), (char*)m_mode);
+  if (m_fd < 0) {
+    set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno);
+    return false;
+  }
+  return true;
+}
+
+bool legacy_smart_device::close()
+{
+  int fd = m_fd; m_fd = -1;
+  if (::deviceclose(fd) < 0) {
+    set_err(errno);
+    return false;
+  }
+  return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement standard ATA support with old functions
+
+class legacy_ata_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+  virtual bool ata_identify_is_cached() const;
+#endif
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+};
+
+legacy_ata_device::legacy_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "ata", req_type),
+  legacy_smart_device("ATA")
+{
+}
+
+int legacy_ata_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+  return ::ata_command_interface(get_fd(), command, select, data);
+}
+
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+bool legacy_ata_device::ata_identify_is_cached() const
+{
+  return !!::ata_identify_is_cached(get_fd());
+}
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement AMCC/3ware RAID support with old functions
+
+class legacy_escalade_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_escalade_device(smart_interface * intf, const char * dev_name,
+    int escalade_type, int disknum);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  int m_escalade_type; ///< Type string for escalade_command_interface().
+  int m_disknum; ///< Disk number.
+};
+
+legacy_escalade_device::legacy_escalade_device(smart_interface * intf, const char * dev_name,
+    int escalade_type, int disknum)
+: smart_device(intf, dev_name, "3ware", "3ware"),
+  legacy_smart_device(
+    escalade_type==CONTROLLER_3WARE_9000_CHAR ? "ATA_3WARE_9000" :
+    escalade_type==CONTROLLER_3WARE_678K_CHAR ? "ATA_3WARE_678K" :
+    /*             CONTROLLER_3WARE_678K     */ "ATA"             ),
+  m_escalade_type(escalade_type), m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum);
+}
+
+int legacy_escalade_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+  return ::escalade_command_interface(get_fd(), m_disknum, m_escalade_type, command, select, data);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement Areca RAID support with old functions
+
+class legacy_areca_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_areca_device(smart_interface * intf, const char * dev_name, int disknum);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  int m_disknum; ///< Disk number.
+};
+
+legacy_areca_device::legacy_areca_device(smart_interface * intf, const char * dev_name, int disknum)
+: smart_device(intf, dev_name, "areca", "areca"),
+  legacy_smart_device("ATA_ARECA"),
+  m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [areca_%02d]", dev_name, disknum);
+}
+
+int legacy_areca_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+  return ::areca_command_interface(get_fd(), m_disknum, command, select, data);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement Marvell support with old functions
+
+class legacy_marvell_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_marvell_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+};
+
+
+legacy_marvell_device::legacy_marvell_device(smart_interface * intf,
+  const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "marvell", req_type),
+  legacy_smart_device("ATA")
+{
+}
+
+int legacy_marvell_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+  return ::marvell_command_interface(get_fd(), command, select, data);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement Highpoint RAID support with old functions
+
+class legacy_highpoint_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_highpoint_device(smart_interface * intf, const char * dev_name,
+    unsigned char controller, unsigned char channel, unsigned char port);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  unsigned char m_hpt_data[3]; ///< controller/channel/port
+};
+
+
+legacy_highpoint_device::legacy_highpoint_device(smart_interface * intf, const char * dev_name,
+  unsigned char controller, unsigned char channel, unsigned char port)
+: smart_device(intf, dev_name, "hpt", "hpt"),
+  legacy_smart_device("ATA")
+{
+  m_hpt_data[0] = controller; m_hpt_data[1] = channel; m_hpt_data[2] = port;
+  set_info().info_name = strprintf("%s [hpt_disk_%u/%u/%u]", dev_name, m_hpt_data[0], m_hpt_data[1], m_hpt_data[2]);
+}
+
+int legacy_highpoint_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+  unsigned char old_hpt_data[3];
+  memcpy(old_hpt_data, con->hpt_data, 3);
+  memcpy(con->hpt_data, m_hpt_data, 3);
+  int status = ::highpoint_command_interface(get_fd(), command, select, data);
+  memcpy(con->hpt_data, old_hpt_data, 3);
+  return status;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement standard SCSI support with old functions
+
+class legacy_scsi_device
+: public /*implements*/ scsi_device,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+  virtual smart_device * autodetect_open();
+
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+};
+
+legacy_scsi_device::legacy_scsi_device(smart_interface * intf,
+  const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "scsi", req_type),
+  legacy_smart_device("SCSI")
+{
+}
+
+bool legacy_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+  unsigned char oldtype = con->controller_type, oldport = con->controller_port;
+  con->controller_type = CONTROLLER_SCSI; con->controller_port = 0;
+  int status = ::do_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl);
+  con->controller_type = oldtype; con->controller_port = oldport;
+  if (status < 0) {
+      set_err(-status);
+      return false;
+  }
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement CCISS RAID support with old functions
+
+class legacy_cciss_device
+: public /*implements*/ scsi_device,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_cciss_device(smart_interface * intf, const char * name, unsigned char disknum);
+
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+
+private:
+  unsigned char m_disknum; ///< Disk number.
+};
+
+
+legacy_cciss_device::legacy_cciss_device(smart_interface * intf,
+  const char * dev_name, unsigned char disknum)
+: smart_device(intf, dev_name, "cciss", "cciss"),
+  legacy_smart_device("SCSI"),
+  m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum);
+}
+
+bool legacy_cciss_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+  // See os_linux.cpp
+  unsigned char oldtype = con->controller_type, oldport = con->controller_port;
+  con->controller_type = CONTROLLER_CCISS; con->controller_port = m_disknum+1;
+  int status = ::do_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl);
+  con->controller_type = oldtype; con->controller_port = oldport;
+  if (status < 0) {
+      set_err(-status);
+      return false;
+  }
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// SCSI open with autodetection support
+
+smart_device * legacy_scsi_device::autodetect_open()
+{
+  // Open device
+  if (!open())
+    return this;
+
+  // No Autodetection if device type was specified by user
+  if (*get_req_type())
+    return this;
+
+  // The code below is based on smartd.cpp:SCSIFilterKnown()
+
+  // Get INQUIRY
+  unsigned char req_buff[64] = {0, };
+  int req_len = 36;
+  if (scsiStdInquiry(this, req_buff, req_len)) {
+    // Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices
+    // watch this spot ... other devices could lock up here
+    req_len = 64;
+    if (scsiStdInquiry(this, req_buff, req_len)) {
+      // device doesn't like INQUIRY commands
+      close();
+      set_err(EIO, "INQUIRY failed");
+      return this;
+    }
+  }
+
+  int avail_len = req_buff[4] + 5;
+  int len = (avail_len < req_len ? avail_len : req_len);
+  if (len < 36)
+      return this;
+
+  // Use INQUIRY to detect type
+  smart_device * newdev = 0;
+  try {
+    // 3ware ?
+    if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) {
+      close();
+#if defined(_WIN32) || defined(__CYGWIN__)
+      set_err(EINVAL, "AMCC/3ware controller, please try changing device to %s,N", get_dev_name());
+#else
+      set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n"
+                      "you may need to replace %s with /dev/twaN or /dev/tweN", get_dev_name());
+#endif
+      return this;
+    }
+
+    // Marvell ?
+    if (len >= 42 && !memcmp(req_buff + 36, "MVSATA", 6)) { // TODO: Linux-specific?
+      //pout("Device %s: using '-d marvell' for ATA disk with Marvell driver\n", get_dev_name());
+      close();
+      newdev = new legacy_marvell_device(smi(), get_dev_name(), get_req_type());
+      newdev->open(); // TODO: Can possibly pass open fd
+      delete this;
+      return newdev;
+    }
+
+    // SAT or USB ?
+    newdev = smi()->autodetect_sat_device(this, req_buff, len);
+    if (newdev)
+      // NOTE: 'this' is now owned by '*newdev'
+      return newdev;
+  }
+  catch (...) {
+    // Cleanup if exception occurs after newdev was allocated
+    delete newdev;
+    throw;
+  }
+
+  // Nothing special found
+  return this;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement platform interface with old functions.
+
+class legacy_smart_interface
+: public /*implements*/ smart_interface
+{
+public:
+#ifdef HAVE_GET_OS_VERSION_STR
+  virtual const char * get_os_version_str();
+#endif
+
+  virtual const char * get_app_examples(const char * appname);
+
+  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
+    const char * pattern = 0);
+
+protected:
+  virtual ata_device * get_ata_device(const char * name, const char * type);
+
+  virtual scsi_device * get_scsi_device(const char * name, const char * type);
+
+  virtual smart_device * autodetect_smart_device(const char * name);
+
+  virtual smart_device * get_custom_smart_device(const char * name, const char * type);
+
+  virtual const char * get_valid_custom_dev_types_str();
+};
+
+
+//////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_GET_OS_VERSION_STR
+const char * legacy_smart_interface::get_os_version_str()
+{
+  return ::get_os_version_str();
+}
+#endif
+
+const char * legacy_smart_interface::get_app_examples(const char * appname)
+{
+  if (!strcmp(appname, "smartctl"))
+    ::print_smartctl_examples(); // this prints to stdout ...
+  return 0; // ... so don't print again.
+}
+
+ata_device * legacy_smart_interface::get_ata_device(const char * name, const char * type)
+{
+  return new legacy_ata_device(this, name, type);
+}
+
+scsi_device * legacy_smart_interface::get_scsi_device(const char * name, const char * type)
+{
+  return new legacy_scsi_device(this, name, type);
+}
+
+
+smart_device * legacy_smart_interface::autodetect_smart_device(const char * name)
+{
+  switch (::guess_device_type(name)) {
+    case CONTROLLER_ATA : return new legacy_ata_device(this, name, "");
+    case CONTROLLER_SCSI: return new legacy_scsi_device(this, name, "");
+  }
+  // TODO: Test autodetect device here
+  return 0;
+}
+
+
+static void free_devnames(char * * devnames, int numdevs)
+{
+  static const char version[] = "$Id: dev_legacy.cpp,v 1.1 2008/07/25 21:16:00 chrfranke Exp $";
+  for (int i = 0; i < numdevs; i++)
+    FreeNonZero(devnames[i], -1,__LINE__, version);
+  FreeNonZero(devnames, (sizeof (char*) * numdevs),__LINE__, version);
+}
+
+bool legacy_smart_interface::scan_smart_devices(smart_device_list & devlist,
+  const char * type, const char * pattern /*= 0*/)
+{
+  if (pattern) {
+    set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
+    return false;
+  }
+
+  // Make namelists
+  char * * atanames = 0; int numata = 0;
+  if (!type || !strcmp(type, "ata")) {
+    numata = ::make_device_names(&atanames, "ATA");
+    if (numata < 0) {
+      set_err(ENOMEM);
+      return false;
+    }
+  }
+
+  char * * scsinames = 0; int numscsi = 0;
+  if (!type || !strcmp(type, "scsi")) {
+    numscsi = ::make_device_names(&scsinames, "SCSI");
+    if (numscsi < 0) {
+      free_devnames(atanames, numata);
+      set_err(ENOMEM);
+      return false;
+    }
+  }
+
+  // Add to devlist
+  int i;
+  for (i = 0; i < numata; i++) {
+    ata_device * atadev = get_ata_device(atanames[i], type);
+    if (atadev)
+      devlist.add(atadev);
+  }
+  free_devnames(atanames, numata);
+
+  for (i = 0; i < numscsi; i++) {
+    scsi_device * scsidev = get_scsi_device(scsinames[i], type);
+    if (scsidev)
+      devlist.add(scsidev);
+  }
+  free_devnames(scsinames, numscsi);
+  return true;
+}
+
+
+smart_device * legacy_smart_interface::get_custom_smart_device(const char * name, const char * type)
+{
+  // Marvell ?
+  if (!strcmp(type, "marvell"))
+    return new legacy_marvell_device(this, name, type);
+
+  // 3Ware ?
+  int disknum = -1, n1 = -1, n2 = -1;
+  if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(0 <= disknum && disknum <= 15)) {
+      set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 15", disknum);
+      return 0;
+    }
+    int contr = ::guess_device_type(name);
+    if (contr != CONTROLLER_3WARE_9000_CHAR && contr != CONTROLLER_3WARE_678K_CHAR)
+      contr = CONTROLLER_3WARE_678K;
+    return new legacy_escalade_device(this, name, contr, disknum);
+  }
+
+  // Areca?
+  disknum = n1 = n2 = -1;
+  if (sscanf(type, "areca,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d areca,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(1 <= disknum && disknum <= 24)) {
+      set_err(EINVAL, "Option -d areca,N (N=%d) must have 1 <= N <= 24", disknum);
+      return 0;
+    }
+    return new legacy_areca_device(this, name, disknum);
+  }
+
+  // Highpoint ?
+  int controller = -1, channel = -1; disknum = 1;
+  n1 = n2 = -1; int n3 = -1;
+  if (sscanf(type, "hpt,%n%d/%d%n/%d%n", &n1, &controller, &channel, &n2, &disknum, &n3) >= 2 || n1 == 4) {
+    int len = strlen(type);
+    if (!(n2 == len || n3 == len)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' supports 2-3 items");
+      return 0;
+    }
+    if (!(1 <= controller && controller <= 8)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied");
+      return 0;
+    }
+    if (!(1 <= channel && channel <= 8)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied");
+      return 0;
+    }
+    if (!(1 <= disknum && disknum <= 15)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid pmport number N supplied");
+      return 0;
+    }
+    return new legacy_highpoint_device(this, name, controller, channel, disknum);
+  }
+
+  // CCISS ?
+  disknum = n1 = n2 = -1;
+  if (sscanf(type, "cciss,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(0 <= disknum && disknum <= 15)) {
+      set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 15", disknum);
+      return 0;
+    }
+    return new legacy_cciss_device(this, name, disknum);
+  }
+
+  return 0;
+}
+
+const char * legacy_smart_interface::get_valid_custom_dev_types_str()
+{
+  return "marvell, areca,N, 3ware,N, hpt,L/M/N, cciss,N";
+}
+
+} // namespace
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Initialize platform interface and register with smi()
+
+void smart_interface::init()
+{
+  static os::legacy_smart_interface the_interface;
+  smart_interface::set(&the_interface);
+}
diff --git a/sm5/dev_tunnelled.h b/sm5/dev_tunnelled.h
new file mode 100644
index 0000000000000000000000000000000000000000..173c6f4401ff4038e6d601b90621cc94cdd564bd
--- /dev/null
+++ b/sm5/dev_tunnelled.h
@@ -0,0 +1,92 @@
+/*
+ * dev_tunnelled.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef DEV_TUNNELLED_H
+#define DEV_TUNNELLED_H
+
+#define DEV_TUNNELLED_H_CVSID "$Id: dev_tunnelled.h,v 1.1 2008/07/25 21:16:00 chrfranke Exp $\n"
+
+#include "dev_interface.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// tunnelled_device_base
+
+/// Common functionality for all tunnelled_device classes.
+
+class tunnelled_device_base
+: virtual public /*implements*/ smart_device
+{
+protected:
+  explicit tunnelled_device_base(smart_device * tunnel_dev);
+
+public:
+  virtual ~tunnelled_device_base() throw();
+
+  virtual bool is_open() const;
+
+  virtual bool open();
+
+  virtual bool close();
+
+  virtual bool owns(const smart_device * dev) const;
+
+  virtual void release(const smart_device * dev);
+
+private:
+  smart_device * m_tunnel_base_dev;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// tunnelled_device
+
+/// Implement a device by tunneling through another device
+
+template <class BaseDev, class TunnelDev>
+class tunnelled_device
+: public BaseDev,
+  public tunnelled_device_base
+{
+public:
+  typedef TunnelDev tunnel_device_type;
+
+protected:
+  explicit tunnelled_device(tunnel_device_type * tunnel_dev)
+    : smart_device(smart_device::never_called),
+      tunnelled_device_base(tunnel_dev),
+      m_tunnel_dev(tunnel_dev)
+    { }
+
+public:
+  virtual void release(const smart_device * dev)
+    {
+      if (m_tunnel_dev == dev)
+        m_tunnel_dev = 0;
+      tunnelled_device_base::release(dev);
+    }
+
+  tunnel_device_type * get_tunnel_dev()
+    { return m_tunnel_dev; }
+
+  const tunnel_device_type * get_tunnel_dev() const
+    { return m_tunnel_dev; }
+
+private:
+  tunnel_device_type * m_tunnel_dev;
+};
+
+#endif // DEV_TUNNELLED_H
diff --git a/sm5/extern.h b/sm5/extern.h
index d4f90ff9c2d3e03aa16074247eb40e2251a87dc7..7fb831fd6519154dac78ac2787941eb117c8b84e 100644
--- a/sm5/extern.h
+++ b/sm5/extern.h
@@ -25,7 +25,7 @@
 #ifndef EXTERN_H_
 #define EXTERN_H_
 
-#define EXTERN_H_CVSID "$Id: extern.h,v 1.56 2008/04/30 17:59:40 mat-c Exp $\n"
+#define EXTERN_H_CVSID "$Id: extern.h,v 1.57 2008/07/25 21:16:00 chrfranke Exp $\n"
 
 // Possible values for fixfirmwarebug.  If user has NOT specified -F at
 // all, then value is 0.
@@ -100,13 +100,11 @@ typedef struct smartmonctrl_s {
   // Controller type (device type) has been specified explicitly
   unsigned char controller_explicit;
   // 3Ware controller type, but also extensible to other contoller types
-  unsigned char controller_type;
+  unsigned char controller_type; // TODO: Only needed for os_linux.cpp
   // For 3Ware controllers, nonzero value is 1 plus the disk number
-  unsigned char controller_port;
+  unsigned char controller_port;  // TODO: Only needed for os_linux.cpp
   // combined controller/channle/pmport for highpoint rocketraid controller
-  unsigned char hpt_data[3];
-  // usbcypress scsi command
-  unsigned char usbcypress_signature;
+  unsigned char hpt_data[3]; // TODO: Only needed for os_linux.cpp
   unsigned char ignorepresets;
   unsigned char showpresets;
   // The i'th entry in this array will modify the printed meaning of
diff --git a/sm5/os_win32/smartctl_vc8.vcproj b/sm5/os_win32/smartctl_vc8.vcproj
index 06638ebda2760e837dcffc26fefdfd32f2034e5e..a2abd6f429a1ae36962c03651d2c767a0a9fe0b8 100644
--- a/sm5/os_win32/smartctl_vc8.vcproj
+++ b/sm5/os_win32/smartctl_vc8.vcproj
@@ -421,6 +421,46 @@
 			RelativePath="..\autogen.sh"
 			>
 		</File>
+		<File
+			RelativePath="..\cciss.cpp"
+			>
+			<FileConfiguration
+				Name="Debug|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCLCompilerTool"
+				/>
+			</FileConfiguration>
+			<FileConfiguration
+				Name="Release|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCLCompilerTool"
+				/>
+			</FileConfiguration>
+		</File>
+		<File
+			RelativePath="..\cciss.h"
+			>
+			<FileConfiguration
+				Name="Debug|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCustomBuildTool"
+				/>
+			</FileConfiguration>
+			<FileConfiguration
+				Name="Release|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCustomBuildTool"
+				/>
+			</FileConfiguration>
+		</File>
 		<File
 			RelativePath="..\CHANGELOG"
 			>
@@ -438,7 +478,7 @@
 				<Tool
 					Name="VCCustomBuildTool"
 					Description="Copy $(InputPath) config.h"
-					CommandLine="copy $(InputPath) config.h"
+					CommandLine="copy $(InputPath) config.h&#x0D;&#x0A;"
 					Outputs="config.h"
 				/>
 			</FileConfiguration>
@@ -448,7 +488,7 @@
 				<Tool
 					Name="VCCustomBuildTool"
 					Description="Copy $(InputPath) config.h"
-					CommandLine="copy $(InputPath) config.h"
+					CommandLine="copy $(InputPath) config.h&#x0D;&#x0A;"
 					Outputs="config.h"
 				/>
 			</FileConfiguration>
@@ -466,7 +506,7 @@
 				<Tool
 					Name="VCCustomBuildTool"
 					Description="Copy $(InputPath) cvsversion.h"
-					CommandLine="copy $(InputPath) cvsversion.h"
+					CommandLine="copy $(InputPath) cvsversion.h&#x0D;&#x0A;"
 					Outputs="cvsversion.h"
 				/>
 			</FileConfiguration>
@@ -476,15 +516,43 @@
 				<Tool
 					Name="VCCustomBuildTool"
 					Description="Copy $(InputPath) cvsversion.h"
-					CommandLine="copy $(InputPath) cvsversion.h"
+					CommandLine="copy $(InputPath) cvsversion.h&#x0D;&#x0A;"
 					Outputs="cvsversion.h"
 				/>
 			</FileConfiguration>
 		</File>
+		<File
+			RelativePath="..\dev_ata_cmd_set.cpp"
+			>
+		</File>
+		<File
+			RelativePath="..\dev_ata_cmd_set.h"
+			>
+		</File>
+		<File
+			RelativePath="..\dev_interface.cpp"
+			>
+		</File>
+		<File
+			RelativePath="..\dev_interface.h"
+			>
+		</File>
+		<File
+			RelativePath="..\dev_legacy.cpp"
+			>
+		</File>
+		<File
+			RelativePath="..\dev_tunnelled.h"
+			>
+		</File>
 		<File
 			RelativePath="..\do_release"
 			>
 		</File>
+		<File
+			RelativePath="..\Doxyfile"
+			>
+		</File>
 		<File
 			RelativePath="..\extern.h"
 			>
@@ -793,6 +861,46 @@
 				/>
 			</FileConfiguration>
 		</File>
+		<File
+			RelativePath="..\os_qnxnto.cpp"
+			>
+			<FileConfiguration
+				Name="Debug|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCLCompilerTool"
+				/>
+			</FileConfiguration>
+			<FileConfiguration
+				Name="Release|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCLCompilerTool"
+				/>
+			</FileConfiguration>
+		</File>
+		<File
+			RelativePath="..\os_qnxnto.h"
+			>
+			<FileConfiguration
+				Name="Debug|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCustomBuildTool"
+				/>
+			</FileConfiguration>
+			<FileConfiguration
+				Name="Release|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCustomBuildTool"
+				/>
+			</FileConfiguration>
+		</File>
 		<File
 			RelativePath="..\os_solaris.cpp"
 			>
diff --git a/sm5/os_win32/smartd_vc8.vcproj b/sm5/os_win32/smartd_vc8.vcproj
index 7e415e18e272537b397899261fe85593c7fb455c..7316aedf9bb1b056f048e91bf4c3b073c2a04851 100644
--- a/sm5/os_win32/smartd_vc8.vcproj
+++ b/sm5/os_win32/smartd_vc8.vcproj
@@ -325,6 +325,46 @@
 			RelativePath="..\autogen.sh"
 			>
 		</File>
+		<File
+			RelativePath="..\cciss.cpp"
+			>
+			<FileConfiguration
+				Name="Debug|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCLCompilerTool"
+				/>
+			</FileConfiguration>
+			<FileConfiguration
+				Name="Release|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCLCompilerTool"
+				/>
+			</FileConfiguration>
+		</File>
+		<File
+			RelativePath="..\cciss.h"
+			>
+			<FileConfiguration
+				Name="Debug|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCustomBuildTool"
+				/>
+			</FileConfiguration>
+			<FileConfiguration
+				Name="Release|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCustomBuildTool"
+				/>
+			</FileConfiguration>
+		</File>
 		<File
 			RelativePath="..\CHANGELOG"
 			>
@@ -342,7 +382,7 @@
 				<Tool
 					Name="VCCustomBuildTool"
 					Description="Copy $(InputPath) config.h"
-					CommandLine="copy $(InputPath) config.h"
+					CommandLine="copy $(InputPath) config.h&#x0D;&#x0A;"
 					Outputs="config.h"
 				/>
 			</FileConfiguration>
@@ -352,7 +392,7 @@
 				<Tool
 					Name="VCCustomBuildTool"
 					Description="Copy $(InputPath) config.h"
-					CommandLine="copy $(InputPath) config.h"
+					CommandLine="copy $(InputPath) config.h&#x0D;&#x0A;"
 					Outputs="config.h"
 				/>
 			</FileConfiguration>
@@ -370,7 +410,7 @@
 				<Tool
 					Name="VCCustomBuildTool"
 					Description="Copy $(InputPath) cvsversion.h"
-					CommandLine="copy $(InputPath) cvsversion.h"
+					CommandLine="copy $(InputPath) cvsversion.h&#x0D;&#x0A;"
 					Outputs="cvsversion.h"
 				/>
 			</FileConfiguration>
@@ -380,15 +420,43 @@
 				<Tool
 					Name="VCCustomBuildTool"
 					Description="Copy $(InputPath) cvsversion.h"
-					CommandLine="copy $(InputPath) cvsversion.h"
+					CommandLine="copy $(InputPath) cvsversion.h&#x0D;&#x0A;"
 					Outputs="cvsversion.h"
 				/>
 			</FileConfiguration>
 		</File>
+		<File
+			RelativePath="..\dev_ata_cmd_set.cpp"
+			>
+		</File>
+		<File
+			RelativePath="..\dev_ata_cmd_set.h"
+			>
+		</File>
+		<File
+			RelativePath="..\dev_interface.cpp"
+			>
+		</File>
+		<File
+			RelativePath="..\dev_interface.h"
+			>
+		</File>
+		<File
+			RelativePath="..\dev_legacy.cpp"
+			>
+		</File>
+		<File
+			RelativePath="..\dev_tunnelled.h"
+			>
+		</File>
 		<File
 			RelativePath="..\do_release"
 			>
 		</File>
+		<File
+			RelativePath="..\Doxyfile"
+			>
+		</File>
 		<File
 			RelativePath="..\extern.h"
 			>
@@ -697,6 +765,46 @@
 				/>
 			</FileConfiguration>
 		</File>
+		<File
+			RelativePath="..\os_qnxnto.cpp"
+			>
+			<FileConfiguration
+				Name="Debug|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCLCompilerTool"
+				/>
+			</FileConfiguration>
+			<FileConfiguration
+				Name="Release|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCLCompilerTool"
+				/>
+			</FileConfiguration>
+		</File>
+		<File
+			RelativePath="..\os_qnxnto.h"
+			>
+			<FileConfiguration
+				Name="Debug|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCustomBuildTool"
+				/>
+			</FileConfiguration>
+			<FileConfiguration
+				Name="Release|Win32"
+				ExcludedFromBuild="true"
+				>
+				<Tool
+					Name="VCCustomBuildTool"
+				/>
+			</FileConfiguration>
+		</File>
 		<File
 			RelativePath="..\os_solaris.cpp"
 			>
diff --git a/sm5/scsiata.cpp b/sm5/scsiata.cpp
index ba72260cbb0daa3824db47fabb0ace9aa26a1609..be6551663a69505719ff5d02ce34fb0e8e217679 100644
--- a/sm5/scsiata.cpp
+++ b/sm5/scsiata.cpp
@@ -3,7 +3,7 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2006 Douglas Gilbert <dougg@torque.net>
+ * Copyright (C) 2006-8 Douglas Gilbert <dougg@torque.net>
  *
  * 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
@@ -39,15 +39,18 @@
 #include <string.h>
 #include <ctype.h>
 
-#include "ataprint.h"
 #include "config.h"
 #include "int64.h"
 #include "extern.h"
 #include "scsicmds.h"
 #include "scsiata.h"
+#include "ataprint.h" // ataReadHDIdentity()
 #include "utility.h"
+#include "dev_interface.h"
+#include "dev_ata_cmd_set.h" // ata_device_with_command_set
+#include "dev_tunnelled.h" // tunnelled_device<>
 
-const char *scsiata_c_cvsid="$Id: scsiata.cpp,v 1.12 2008/06/15 21:23:11 mat-c Exp $"
+const char *scsiata_c_cvsid="$Id: scsiata.cpp,v 1.13 2008/07/25 21:16:00 chrfranke Exp $"
 CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIATA_H_CVSID UTILITY_H_CVSID;
 
 /* for passing global control variables */
@@ -56,6 +59,43 @@ extern smartmonctrl *con;
 #define DEF_SAT_ATA_PASSTHRU_SIZE 16
 #define ATA_RETURN_DESCRIPTOR 9
 
+namespace sat { // no need to publish anything, name provided for Doxygen
+
+/// SAT support.
+/// Implements ATA by tunnelling through SCSI.
+
+class sat_device
+: public tunnelled_device<
+    /*implements*/ ata_device_with_command_set
+    /*by tunnelling through a*/, scsi_device
+  >
+{
+public:
+  sat_device(smart_interface * intf, scsi_device * scsidev,
+    const char * req_type, int passthrulen = 0);
+
+  virtual ~sat_device() throw();
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+  int m_passthrulen;
+};
+
+
+sat_device::sat_device(smart_interface * intf, scsi_device * scsidev,
+  const char * req_type, int passthrulen /*= 0*/)
+: smart_device(intf, scsidev->get_dev_name(), "sat", req_type),
+  tunnelled_device<ata_device_with_command_set, scsi_device>(scsidev),
+  m_passthrulen(passthrulen)
+{
+  set_info().info_name = strprintf("%s [SAT]", scsidev->get_info_name());
+}
+
+sat_device::~sat_device() throw()
+{
+}
+
 
 // cdb[0]: ATA PASS THROUGH (16) SCSI command opcode byte (0x85)
 // cdb[1]: multiple_count, protocol + extend
@@ -114,7 +154,7 @@ extern smartmonctrl *con;
 //   This interface routine takes ATA SMART commands and packages
 //   them in the SAT-defined ATA PASS THROUGH SCSI commands. There are
 //   two available SCSI commands: a 12 byte and 16 byte variant; the
-//   one used is chosen via con->satpassthrulen .
+//   one used is chosen via this->m_passthrulen .
 // DETAILED DESCRIPTION OF ARGUMENTS
 //   device: is the file descriptor provided by (a SCSI dvice type) open()
 //   command: defines the different ATA operations.
@@ -130,8 +170,7 @@ extern smartmonctrl *con;
 //   0 if the command succeeded and disk SMART status is "OK"
 //   1 if the command succeeded and disk SMART status is "FAILING"
 
-int sat_command_interface(int device, smart_command_set command, int select,
-                          char *data)
+int sat_device::ata_command_interface(smart_command_set command, int select, char *data)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
@@ -245,7 +284,7 @@ int sat_command_interface(int device, smart_command_set command, int select,
         ck_cond = 1;
         break;
     default:
-        pout("Unrecognized command %d in sat_command_interface()\n"
+        pout("Unrecognized command %d in sat_device::ata_command_interface()\n"
              "Please contact " PACKAGE_BUGREPORT "\n", command);
         errno=ENOSYS;
         return -1;
@@ -255,9 +294,9 @@ int sat_command_interface(int device, smart_command_set command, int select,
         lba_high = 0xc2;
     }
 
-    if ((SAT_ATA_PASSTHROUGH_12LEN == con->satpassthrulen) ||
-        (SAT_ATA_PASSTHROUGH_16LEN == con->satpassthrulen))
-        passthru_size = con->satpassthrulen;
+    if ((SAT_ATA_PASSTHROUGH_12LEN == m_passthrulen) ||
+        (SAT_ATA_PASSTHROUGH_16LEN == m_passthrulen))
+        passthru_size = m_passthrulen;
     cdb[0] = (SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ?
              SAT_ATA_PASSTHROUGH_12 : SAT_ATA_PASSTHROUGH_16;
 
@@ -291,11 +330,12 @@ int sat_command_interface(int device, smart_command_set command, int select,
     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) {
+    scsi_device * scsidev = get_tunnel_dev();
+    if (!scsidev->scsi_pass_through(&io_hdr)) {
         if (con->reportscsiioctl > 0)
-            pout("sat_command_interface: do_scsi_cmnd_io() failed, "
-                 "status=%d\n", status);
+            pout("sat_device::ata_command_interface: scsi_pass_through() failed, "
+                 "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg());
+        set_err(scsidev->get_err());
         return -1;
     }
     ardp = NULL;
@@ -318,7 +358,7 @@ int sat_command_interface(int device, smart_command_set command, int select,
         status = scsiSimpleSenseFilter(&sinfo);
         if (0 != status) {
             if (con->reportscsiioctl > 0) {
-                pout("sat_command_interface: scsi error: %s\n",
+                pout("sat_device::ata_command_interface: scsi error: %s\n",
                      scsiErrString(status));
                 if (ardp && (con->reportscsiioctl > 1)) {
                     pout("Values from ATA Return Descriptor are:\n");
@@ -377,21 +417,26 @@ int sat_command_interface(int device, smart_command_set command, int select,
     return 0;
 }
 
+} // namespace
+
+/////////////////////////////////////////////////////////////////////////////
+
 /* Attempt an IDENTIFY DEVICE ATA command via SATL when packet_interface
-   is 0 otherwise attempt IDENTIFY PACKET DEVICE. If successful
-   return 1, else 0 */
-int has_sat_pass_through(int device, int packet_interface)
+   is false otherwise attempt IDENTIFY PACKET DEVICE. If successful
+   return true, else false */
+
+static bool has_sat_pass_through(ata_device * dev, bool packet_interface = false)
 {
+    ata_cmd_in in;
+    in.in_regs.command = (packet_interface ? ATA_IDENTIFY_PACKET_DEVICE : ATA_IDENTIFY_DEVICE);
+    in.set_data_in(1);
     char data[512];
-    smart_command_set command;
-
-    command = packet_interface ? PIDENTIFY : IDENTIFY;
-    if (0 == sat_command_interface(device, command, 0, data))
-        return 1;
-    else
-        return 0;
+    in.buffer = data;
+    return dev->ata_pass_through(in);
 }
 
+/////////////////////////////////////////////////////////////////////////////
+
 /* Next two functions are borrowed from sg_lib.c in the sg3_utils
    package. Same copyrght owner, same license as this file. */
 int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len,
@@ -454,15 +499,53 @@ const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
     return NULL;
 }
 
+
+/////////////////////////////////////////////////////////////////////////////
+
+namespace sat {
+
+/// Cypress USB Brigde support.
+
+class usbcypress_device
+: public tunnelled_device<
+    /*implements*/ ata_device_with_command_set
+    /*by tunnelling through a*/, scsi_device
+  >
+{
+public:
+  usbcypress_device(smart_interface * intf, scsi_device * scsidev,
+    const char * req_type, unsigned char signature);
+
+  virtual ~usbcypress_device() throw();
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+  unsigned char m_signature;
+};
+
+
+usbcypress_device::usbcypress_device(smart_interface * intf, scsi_device * scsidev,
+  const char * req_type, unsigned char signature)
+: smart_device(intf, scsidev->get_dev_name(), "sat", req_type),
+  tunnelled_device<ata_device_with_command_set, scsi_device>(scsidev),
+  m_signature(signature)
+{
+  set_info().info_name = strprintf("%s [USB Cypress]", scsidev->get_info_name());
+}
+
+usbcypress_device::~usbcypress_device() throw()
+{
+}
+
+
 /* see cy7c68300c_8.pdf for more information */
 #define USBCYPRESS_PASSTHROUGH_LEN 16
-int usbcypress_command_interface(int device, smart_command_set command, int select,
-                          char *data)
+int usbcypress_device::ata_command_interface(smart_command_set command, int select, char *data)
 {
     struct scsi_cmnd_io io_hdr;
     unsigned char cdb[USBCYPRESS_PASSTHROUGH_LEN];
     unsigned char sense[32];
-    int status;
     int copydata = 0;
     int outlen = 0;
     int ck_cond = 0;    /* set to 1 to read register(s) back */
@@ -567,7 +650,7 @@ int usbcypress_command_interface(int device, smart_command_set command, int sele
         ck_cond = 1;
         break;
     default:
-        pout("Unrecognized command %d in sat_command_interface()\n"
+        pout("Unrecognized command %d in usbcypress_device::ata_command_interface()\n"
              "Please contact " PACKAGE_BUGREPORT "\n", command);
         errno=ENOSYS;
         return -1;
@@ -577,7 +660,7 @@ int usbcypress_command_interface(int device, smart_command_set command, int sele
         lba_high = 0xc2;
     }
 
-    cdb[0] = con->usbcypress_signature; // bVSCBSignature : vendor-specific command
+    cdb[0] = m_signature; // bVSCBSignature : vendor-specific command
     cdb[1] = 0x24; // bVSCBSubCommand : 0x24 for ATACB
     cdb[2] = 0x0;
     if (ata_command == ATA_IDENTIFY_DEVICE || ata_command == ATA_IDENTIFY_PACKET_DEVICE)
@@ -614,11 +697,12 @@ int usbcypress_command_interface(int device, smart_command_set command, int sele
     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) {
+    scsi_device * scsidev = get_tunnel_dev();
+    if (!scsidev->scsi_pass_through(&io_hdr)) {
         if (con->reportscsiioctl > 0)
-            pout("sat_command_interface: do_scsi_cmnd_io() failed, "
-                 "status=%d\n", status);
+            pout("usbcypress_device::ata_command_interface: scsi_pass_through() failed, "
+                 "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg());
+        set_err(scsidev->get_err());
         return -1;
     }
 
@@ -656,11 +740,11 @@ int usbcypress_command_interface(int device, smart_command_set command, int sele
         io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 
 
-        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-        if (0 != status) {
+        if (!scsidev->scsi_pass_through(&io_hdr)) {
             if (con->reportscsiioctl > 0)
-                pout("sat_command_interface: do_scsi_cmnd_io() failed, "
-                        "status=%d\n", status);
+                pout("usbcypress_device::ata_command_interface: scsi_pass_through() failed, "
+                     "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg());
+            set_err(scsidev->get_err());
             return -1;
         }
         // if there is a sense the command failed or the
@@ -706,21 +790,17 @@ static int isprint_string(const char *s)
     }
     return 1;
 }
+
 /* Attempt an IDENTIFY DEVICE ATA or IDENTIFY PACKET DEVICE command
    If successful return 1, else 0 */
-static int has_pass_through(int device, int controller_type,
-        char *manufacturer, char *product)
+// TODO: Combine with has_sat_pass_through above
+static int has_usbcypress_pass_through(ata_device * atadev, const char *manufacturer, const char *product)
 {
     struct ata_identify_device drive;
     char model[40], serial[20], firm[8];
-    int old_type = con->controller_type;
-    int retid;
 
-    con->controller_type = controller_type;
     /* issue the command and do a checksum if possible */
-    retid = ataReadHDIdentity(device,&drive);
-    con->controller_type = old_type;
-    if (retid < 0)
+    if (ataReadHDIdentity(atadev, &drive) < 0)
         return 0;
 
     /* check if model string match, revision doesn't work for me */
@@ -744,9 +824,84 @@ static int has_pass_through(int device, int controller_type,
     return 1;
 }
 
-int has_usbcypress_pass_through(int device, char *manufacturer, char *product)
+} // namespace
+
+using namespace sat;
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Return ATA->SCSI filter for SAT or USB.
+
+ata_device * smart_interface::get_sat_device(const char * name, const char * type, scsi_device * scsidev /* = 0*/)
 {
-    con->usbcypress_signature = 0x24;
-    return has_pass_through(device, CONTROLLER_USBCYPRESS,
-            manufacturer, product);
+  if (!scsidev) {
+    scsidev = get_scsi_device(name, "scsi");
+    if (!scsidev)
+      return 0;
+  }
+  if (!strncmp(type, "sat", 3)) {
+    int ptlen = 0, n1 = -1, n2 = -1;
+    if (!(((sscanf(type, "sat%n,%d%n", &n1, &ptlen, &n2) == 1 && n2 == (int)strlen(type)) || n1 == (int)strlen(type))
+        && (ptlen == 0 || ptlen == 12 || ptlen == 16))) {
+      set_err(EINVAL, "Option '-d sat,<n>' requires <n> to be 0, 12 or 16");
+      return 0;
+    }
+    return new sat_device(this, scsidev, type, ptlen);
+  }
+  else if (!strncmp(type, "usbcypress", 10)) {
+    unsigned signature = 0x24; int n1 = -1, n2 = -1;
+    if (!(((sscanf(type, "usbcypress%n,0x%x%n", &n1, &signature, &n2) == 1 && n2 == (int)strlen(type)) || n1 == (int)strlen(type))
+          && signature <= 0xff)) {
+      set_err(EINVAL, "Option '-d usbcypress,<n>' requires <n> to be "
+                      "an hexadecimal number between 0x0 and 0xff");
+      return 0;
+    }
+    return new usbcypress_device(this, scsidev, type, signature);
+  }
+  else {
+    set_err(EINVAL, "Unknown USB device type '%s'", type);
+    return 0;
+  }
+}
+
+// Try to detect a SAT device behind a SCSI interface.
+
+ata_device * smart_interface::autodetect_sat_device(scsi_device * scsidev,
+  const unsigned char * inqdata, unsigned inqsize)
+{
+  if (!scsidev->is_open())
+    return 0;
+
+  ata_device * atadev = 0;
+  try {
+    // SAT ?
+    if (inqdata && inqsize >= 36 && !memcmp(inqdata + 8, "ATA     ", 8)) { // TODO: Linux-specific?
+      atadev = new sat_device(this, scsidev, "");
+      if (has_sat_pass_through(atadev))
+        return atadev; // Detected SAT
+      atadev->release(scsidev);
+      delete atadev;
+    }
+
+    // USB ?
+    {
+      atadev = new usbcypress_device(this, scsidev, "", 0x24);
+      if (has_usbcypress_pass_through(atadev,
+            (inqdata && inqsize >= 36 ? (const char*)inqdata  + 8 : 0),
+            (inqdata && inqsize >= 36 ? (const char*)inqdata + 16 : 0) ))
+        return atadev; // Detected USB
+      atadev->release(scsidev);
+      delete atadev;
+    }
+  }
+  catch (...) {
+    if (atadev) {
+      atadev->release(scsidev);
+      delete atadev;
+    }
+    throw;
+  }
+
+  return 0;
 }
diff --git a/sm5/scsiata.h b/sm5/scsiata.h
index 079f256394cbfe976e217c7c155d203f812c0334..b69e5ab1323ddc9a896ce706d79cc772201dd99e 100644
--- a/sm5/scsiata.h
+++ b/sm5/scsiata.h
@@ -3,7 +3,7 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2006 Douglas Gilbert <dougg@torque.net>
+ * Copyright (C) 2006-8 Douglas Gilbert <dougg@torque.net>
  *
  * 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
@@ -20,7 +20,7 @@
 #ifndef SCSIATA_H_
 #define SCSIATA_H_
 
-#define SCSIATA_H_CVSID "$Id: scsiata.h,v 1.5 2008/06/15 21:23:11 mat-c Exp $\n"
+#define SCSIATA_H_CVSID "$Id: scsiata.h,v 1.6 2008/07/25 21:16:00 chrfranke Exp $\n"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -32,13 +32,10 @@
 #define SAT_ATA_PASSTHROUGH_12LEN 12
 #define SAT_ATA_PASSTHROUGH_16LEN 16
 
-extern int sat_command_interface(int device, smart_command_set command,
-                                 int select, char *data);
-
-/* Attempt an IDENTIFY DEVICE ATA command via SATL when packet_interface
-   is 0 otherwise attempt IDENTIFY PACKET DEVICE. If successful
-   return 1, else 0 */
-extern int has_sat_pass_through(int device, int packet_interface);
+// Moved to C++ interface
+//extern int sat_command_interface(int device, smart_command_set command,
+//                                 int select, char *data);
+//extern int has_sat_pass_through(int device, int packet_interface);
 
 /* This is a slightly stretched SCSI sense "descriptor" format header.
    The addition is to allow the 0x70 and 0x71 response codes. The idea
@@ -74,13 +71,9 @@ extern int sg_scsi_normalize_sense(const unsigned char * sensep,
 extern const unsigned char * sg_scsi_sense_desc_find(
                 const unsigned char * sensep, int sense_len, int desc_type);
 
-extern int usbcypress_command_interface(int device, smart_command_set command,
-                                 int select, char *data);
-/* Attempt an IDENTIFY DEVICE ATA command via USBCYPRESS when packet_interface
-   is 0 otherwise attempt IDENTIFY PACKET DEVICE. If successful
-   return 1, else 0 */
-extern int has_usbcypress_pass_through(int device,
-		char *manufacturer, char *product);
+// Moved to C++ interface
+//extern int usbcypress_command_interface(int device, smart_command_set command,
+//                                 int select, char *data);
 
 #endif
 
diff --git a/sm5/scsicmds.cpp b/sm5/scsicmds.cpp
index cdf5b8aa372cdfb76261b2258ab7d3a176e7edf0..5ec946bb42fb46c5c776faeccdc48943bc72afd9 100644
--- a/sm5/scsicmds.cpp
+++ b/sm5/scsicmds.cpp
@@ -45,9 +45,11 @@
 #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.96 2008/03/04 22:09:47 ballen4705 Exp $"
+const char *scsicmds_c_cvsid="$Id: scsicmds.cpp,v 1.97 2008/07/25 21:16:00 chrfranke Exp $"
 CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 /* for passing global control variables */
@@ -249,7 +251,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;
@@ -286,10 +288,9 @@ 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;
@@ -323,9 +324,8 @@ 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)
@@ -342,7 +342,7 @@ int scsiLogSense(int device, int pagenum, int subpagenum, UINT8 *pBuf,
  * 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;
@@ -368,15 +368,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);
     }
@@ -399,13 +397,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)
@@ -430,9 +428,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);
 }
@@ -441,7 +438,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;
@@ -466,15 +463,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);
     }
@@ -497,13 +492,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)
@@ -529,9 +524,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);
 }
@@ -539,13 +533,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;
@@ -562,9 +555,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);
 }
@@ -573,13 +565,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;
@@ -600,9 +592,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;
@@ -619,13 +610,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));
@@ -641,8 +632,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;
@@ -656,19 +648,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));
@@ -691,9 +682,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);
 }
@@ -701,14 +691,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));
@@ -726,20 +715,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));
@@ -753,16 +740,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;
@@ -784,14 +770,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));
@@ -809,9 +794,8 @@ 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);
 }
@@ -860,7 +844,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;
 
@@ -950,7 +934,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;
@@ -1025,7 +1009,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;
@@ -1047,7 +1031,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)
 {
@@ -1630,7 +1614,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;
@@ -1651,7 +1635,7 @@ int scsiSmartIBMOfflineTest(int device)
     return res;
 }
 
-int scsiSmartDefaultSelfTest(int device)
+int scsiSmartDefaultSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1661,7 +1645,7 @@ int scsiSmartDefaultSelfTest(int device)
     return res;
 }
 
-int scsiSmartShortSelfTest(int device)
+int scsiSmartShortSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1671,7 +1655,7 @@ int scsiSmartShortSelfTest(int device)
     return res;
 }
 
-int scsiSmartExtendSelfTest(int device)
+int scsiSmartExtendSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1682,7 +1666,7 @@ int scsiSmartExtendSelfTest(int device)
     return res;
 }
 
-int scsiSmartShortCapSelfTest(int device)
+int scsiSmartShortCapSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1692,7 +1676,7 @@ int scsiSmartShortCapSelfTest(int device)
     return res;
 }
 
-int scsiSmartExtendCapSelfTest(int device)
+int scsiSmartExtendCapSelfTest(scsi_device * device)
 {
     int res;
 
@@ -1703,7 +1687,7 @@ int scsiSmartExtendCapSelfTest(int device)
     return res;
 }
 
-int scsiSmartSelfTestAbort(int device)
+int scsiSmartSelfTestAbort(scsi_device * device)
 {
     int res;
 
@@ -1715,7 +1699,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];
@@ -1875,7 +1859,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;
@@ -1924,7 +1908,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;
@@ -1951,7 +1935,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];
@@ -1984,7 +1968,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];
@@ -2053,7 +2037,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];
diff --git a/sm5/scsicmds.h b/sm5/scsicmds.h
index f449425b36a342af8df830845196b841083420f9..521e6df373333dc5dc2a7a376961c8cba1ac29dc 100644
--- a/sm5/scsicmds.h
+++ b/sm5/scsicmds.h
@@ -32,7 +32,7 @@
 #ifndef SCSICMDS_H_
 #define SCSICMDS_H_
 
-#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.66 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.67 2008/07/25 21:16:00 chrfranke Exp $\n"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -277,6 +277,8 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */
 
 #define LOGPAGEHDRSIZE  4
 
+class scsi_device;
+
 void scsi_do_sense_disect(const struct scsi_cmnd_io * in,
                           struct scsi_sense_disect * out);
 
@@ -285,73 +287,73 @@ int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo);
 const char * scsiErrString(int scsiErr);
 
 /* STANDARD SCSI Commands  */
-int scsiTestUnitReady(int device);
+int scsiTestUnitReady(scsi_device * device);
 
-int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen);
+int scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen);
 
-int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen);
+int scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen);
 
-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);
 
-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);
 
-int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen);
+int scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen);
 
-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);
 
-int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen);
+int scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen);
 
 int scsiModePageOffset(const UINT8 * resp, int len, int modese_len);
 
-int scsiRequestSense(int device, struct scsi_sense_disect * sense_info);
+int scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info);
 
-int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen);
+int scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf, int bufLen);
 
-int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf,
+int scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf,
                       int bufLen);
 
-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);
 
 /* SMART specific commands */
-int scsiCheckIE(int device, int hasIELogPage, int hasTempLogPage, UINT8 *asc,
+int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage, UINT8 *asc,
                 UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp);
 
-int scsiFetchIECmpage(int device, struct scsi_iec_mode_page *iecp, 
+int scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp,
                       int modese_len);
 int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp);
 int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp);
-int scsiSetExceptionControlAndWarning(int device, int enabled,
+int scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
                             const struct scsi_iec_mode_page *iecp);
 void scsiDecodeErrCounterPage(unsigned char * resp,
                               struct scsiErrorCounter *ecp);
 void scsiDecodeNonMediumErrPage(unsigned char * resp,
                                 struct scsiNonMediumError *nmep);
-int scsiFetchExtendedSelfTestTime(int device, int * durationSec, 
+int scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
                                   int modese_len);
-int scsiCountFailedSelfTests(int fd, int noisy);
-int scsiSelfTestInProgress(int fd, int * inProgress);
-int scsiFetchControlGLTSD(int device, int modese_len, int current);
-int scsiSetControlGLTSD(int device, int enabled, int modese_len);
-int scsiFetchTransportProtocol(int device, int modese_len);
+int scsiCountFailedSelfTests(scsi_device * device, int noisy);
+int scsiSelfTestInProgress(scsi_device * device, int * inProgress);
+int scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current);
+int scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len);
+int scsiFetchTransportProtocol(scsi_device * device, int modese_len);
 
 /* T10 Standard IE Additional Sense Code strings taken from t10.org */
 
 const char* scsiGetIEString(UINT8 asc, UINT8 ascq);
-int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp);
+int scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp);
 
 
-int scsiSmartIBMOfflineTest(int device);
+int scsiSmartIBMOfflineTest(scsi_device * device);
 
-int scsiSmartDefaultSelfTest(int device);
-int scsiSmartShortSelfTest(int device);
-int scsiSmartExtendSelfTest(int device);
-int scsiSmartShortCapSelfTest(int device);
-int scsiSmartExtendCapSelfTest(int device);
-int scsiSmartSelfTestAbort(int device);
+int scsiSmartDefaultSelfTest(scsi_device * device);
+int scsiSmartShortSelfTest(scsi_device * device);
+int scsiSmartExtendSelfTest(scsi_device * device);
+int scsiSmartShortCapSelfTest(scsi_device * device);
+int scsiSmartExtendCapSelfTest(scsi_device * device);
+int scsiSmartSelfTestAbort(scsi_device * device);
 
 const char * scsiTapeAlertsTapeDevice(unsigned short code);
 const char * scsiTapeAlertsChangerDevice(unsigned short code);
@@ -370,7 +372,9 @@ inline void dStrHex(const unsigned char* str, int len, int no_ascii)
  * (e.g. CHECK CONDITION). If the SCSI command could not be issued
  * (e.g. device not present or not a SCSI device) or some other problem
  * arises (e.g. timeout) then returns a negative errno value. */
-int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
+// Moved to C++ interface
+//int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
+
 
 
 #endif
diff --git a/sm5/scsiprint.cpp b/sm5/scsiprint.cpp
index db8e97c91a1b4ce82d9d84edaf4cc71e41daef39..866b48d213e1ac82b5db5d24454ed3a21e096029 100644
--- a/sm5/scsiprint.cpp
+++ b/sm5/scsiprint.cpp
@@ -35,6 +35,8 @@
 #include "int64.h"
 #include "extern.h"
 #include "scsicmds.h"
+#include "atacmds.h" // smart_command_set
+#include "dev_interface.h"
 #include "scsiprint.h"
 #include "smartctl.h"
 #include "utility.h"
@@ -42,7 +44,7 @@
 
 #define GBUF_SIZE 65535
 
-const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.124 2008/06/15 21:23:11 mat-c Exp $"
+const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.125 2008/07/25 21:16:00 chrfranke Exp $"
 CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // control block which points to external global control variables
@@ -78,7 +80,7 @@ static int modese_len = 0;
 // simply returns to the calling routine.
 extern void failuretest(int type, int returnvalue);
 
-static void scsiGetSupportedLogPages(int device)
+static void scsiGetSupportedLogPages(scsi_device * device)
 {
     int i, err;
 
@@ -140,7 +142,7 @@ static void scsiGetSupportedLogPages(int device)
 
 /* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
    (or at least something to report). */
-static int scsiGetSmartData(int device, int attribs)
+static int scsiGetSmartData(scsi_device * device, int attribs)
 {
     UINT8 asc;
     UINT8 ascq;
@@ -186,7 +188,7 @@ static int scsiGetSmartData(int device, int attribs)
 // TapeAlerts fails
 static const char * const severities = "CWI";
 
-static int scsiGetTapeAlertsData(int device, int peripheral_type)
+static int scsiGetTapeAlertsData(scsi_device * device, int peripheral_type)
 {
     unsigned short pagelength;
     unsigned short parametercode;
@@ -234,7 +236,7 @@ static int scsiGetTapeAlertsData(int device, int peripheral_type)
     return failures;
 }
 
-static void scsiGetStartStopData(int device)
+static void scsiGetStartStopData(scsi_device * device)
 {
     UINT32 u;
     int err, len, k, extra, pc;
@@ -295,7 +297,7 @@ static void scsiGetStartStopData(int device)
     }
 } 
 
-static void scsiPrintGrownDefectListLen(int device)
+static void scsiPrintGrownDefectListLen(scsi_device * device)
 {
     int err, dl_format, dl_len, div;
 
@@ -344,7 +346,7 @@ static void scsiPrintGrownDefectListLen(int device)
     }
 }
 
-static void scsiPrintSeagateCacheLPage(int device)
+static void scsiPrintSeagateCacheLPage(scsi_device * device)
 {
     int k, j, num, pl, pc, err, len;
     unsigned char * ucp;
@@ -419,7 +421,7 @@ static void scsiPrintSeagateCacheLPage(int device)
     }
 }
 
-static void scsiPrintSeagateFactoryLPage(int device)
+static void scsiPrintSeagateFactoryLPage(scsi_device * device)
 {
     int k, j, num, pl, pc, len, err, good, bad;
     unsigned char * ucp;
@@ -513,7 +515,7 @@ static void scsiPrintSeagateFactoryLPage(int device)
     }
 }
 
-static void scsiPrintErrorCounterLog(int device)
+static void scsiPrintErrorCounterLog(scsi_device * device)
 {
     struct scsiErrorCounter errCounterArr[3];
     struct scsiErrorCounter * ecp;
@@ -657,7 +659,7 @@ static const char * self_test_result[] = {
 // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
 // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
 // FAILSMART is returned.
-static int scsiPrintSelfTest(int device)
+static int scsiPrintSelfTest(scsi_device * device)
 {
     int num, k, n, res, err, durationSec;
     int noheader = 1;
@@ -794,7 +796,7 @@ static int scsiPrintSelfTest(int device)
         pout("No self-tests have been logged\n");
     else
         pout("\n");
-    if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, 
+    if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
                         modese_len)) && (durationSec > 0)) {
         pout("Long (extended) Self Test duration: %d seconds "
              "[%.1f minutes]\n", durationSec, durationSec / 60.0);
@@ -830,7 +832,7 @@ static const char * reassign_status[] = {
 // Returns 0 if ok else FAIL* bitmask. Note can have a status entry
 // and up to 2048 events (although would hope to have less). May set
 // FAILLOG if serious errors detected (in the future).
-static int scsiPrintBackgroundResults(int device)
+static int scsiPrintBackgroundResults(scsi_device * device)
 {
     int num, j, m, err, pc, pl, truncated;
     int noheader = 1;
@@ -970,7 +972,7 @@ static const char * transport_proto_arr[] = {
 };
 
 /* Returns 0 on success, 1 on general error and 2 for early, clean exit */
-static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
+static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, int all)
 {
     char manufacturer[9];
     char product[17];
@@ -1022,42 +1024,11 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     if (all && (0 != strncmp(manufacturer, "ATA", 3)))
         pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
 
-    if (0 == strncmp(manufacturer, "3ware", 5) || 0 == strncmp(manufacturer, "AMCC", 4) ) {
-#if defined(_WIN32) || defined(__CYGWIN__)
-        pout("please try changing device to /dev/hdX,N\n");
-#else
-        pout("please try adding '-d 3ware,N'\n");
-        pout("you may also need to change device to /dev/twaN or /dev/tweN\n");
-#endif
-        return 2;
-    } else if ((len >= 42) &&
-	       (0 == strncmp((const char *)(gBuf + 36), "MVSATA", 6))) {
-        pout("please try '-d marvell'\n");
-        return 2;
-    } else if ((0 == con->controller_explicit) &&
-	       (0 == strncmp(manufacturer, "ATA     ", 8)) &&
-               has_sat_pass_through(device, 0)) {
-        con->controller_type = CONTROLLER_SAT;
-        if (con->reportscsiioctl > 0) {
-            PRINT_ON(con);
-            pout("Detected SAT interface, switch to device type 'sat'\n");
-            PRINT_OFF(con);
-        }
-	return 2;
-    } else if ((0 == con->controller_explicit) &&
+    if (!*device->get_req_type()/*no type requested*/ &&
                (0 == strncmp(manufacturer, "ATA", 3))) {
         pout("\nProbable ATA device behind a SAT layer\n"
              "Try an additional '-d ata' or '-d sat' argument.\n");
         return 2;
-    } else if ((0 == con->controller_explicit) &&
-               has_usbcypress_pass_through(device, manufacturer, product)) {
-        con->controller_type = CONTROLLER_USBCYPRESS;
-        if (con->reportscsiioctl > 0) {
-            PRINT_ON(con);
-            pout("Detected USBCYPRESS interface, switch to device type 'usbcypress'\n");
-            PRINT_OFF(con);
-        }
-	return 2;
     }
     if (! all)
         return 0;
@@ -1160,7 +1131,7 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     return 0;
 }
 
-static int scsiSmartEnable(int device)
+static int scsiSmartEnable(scsi_device * device)
 {
     struct scsi_iec_mode_page iec;
     int err;
@@ -1196,7 +1167,7 @@ static int scsiSmartEnable(int device)
     return 0;
 }
         
-static int scsiSmartDisable(int device)
+static int scsiSmartDisable(scsi_device * device)
 {
     struct scsi_iec_mode_page iec;
     int err;
@@ -1232,7 +1203,7 @@ static int scsiSmartDisable(int device)
     return 0;
 }
 
-static void scsiPrintTemp(int device)
+static void scsiPrintTemp(scsi_device * device)
 {
     UINT8 temp = 0;
     UINT8 trip = 0;
@@ -1251,14 +1222,14 @@ static void scsiPrintTemp(int device)
 }
 
 /* Main entry point used by smartctl command. Return 0 for success */
-int scsiPrintMain(int fd)
+int scsiPrintMain(scsi_device * device)
 {
     int checkedSupportedLogPages = 0;
     UINT8 peripheral_type = 0;
     int returnval = 0;
     int res, durationSec;
 
-    res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo);
+    res = scsiGetDriveInfo(device, &peripheral_type, con->driveinfo);
     if (res) {
         if (2 == res)
             return 0;
@@ -1267,44 +1238,44 @@ int scsiPrintMain(int fd)
     }
 
     if (con->smartenable) {
-        if (scsiSmartEnable(fd))
+        if (scsiSmartEnable(device))
             failuretest(MANDATORY_CMD, returnval |= FAILSMART);
     }
 
     if (con->smartdisable) {
-        if (scsiSmartDisable(fd))
+        if (scsiSmartDisable(device))
             failuretest(MANDATORY_CMD,returnval |= FAILSMART);
     }
     
     if (con->smartautosaveenable) {
-      if (scsiSetControlGLTSD(fd, 0, modese_len)) {
+      if (scsiSetControlGLTSD(device, 0, modese_len)) {
         pout("Enable autosave (clear GLTSD bit) failed\n");
         failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
       }
     }
     
     if (con->smartautosavedisable) {
-      if (scsiSetControlGLTSD(fd, 1, modese_len)) {
+      if (scsiSetControlGLTSD(device, 1, modese_len)) {
         pout("Disable autosave (set GLTSD bit) failed\n");
         failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
       }
     }
     
     if (con->checksmart) {
-        scsiGetSupportedLogPages(fd);
+        scsiGetSupportedLogPages(device);
         checkedSupportedLogPages = 1;
         if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
             (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
             if (gTapeAlertsLPage) {
                 if (con->driveinfo)
                     pout("TapeAlert Supported\n");
-                if (-1 == scsiGetTapeAlertsData(fd, peripheral_type))
+                if (-1 == scsiGetTapeAlertsData(device, peripheral_type))
                     failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
             }
             else
                 pout("TapeAlert Not Supported\n");
         } else { /* disk, cd/dvd, enclosure, etc */
-            if ((res = scsiGetSmartData(fd, con->smartvendorattrib))) {
+            if ((res = scsiGetSmartData(device, con->smartvendorattrib))) {
                 if (-2 == res)
                     returnval |= FAILSTATUS;
                 else
@@ -1314,36 +1285,36 @@ int scsiPrintMain(int fd)
     }   
     if (con->smartvendorattrib) {
         if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
+            scsiGetSupportedLogPages(device);
         if (gTempLPage) {
             if (con->checksmart)
                 pout("\n");
-            scsiPrintTemp(fd);         
+            scsiPrintTemp(device);
         }
         if (gStartStopLPage)
-            scsiGetStartStopData(fd);
+            scsiGetStartStopData(device);
         if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
-            scsiPrintGrownDefectListLen(fd);
+            scsiPrintGrownDefectListLen(device);
             if (gSeagateCacheLPage)
-                scsiPrintSeagateCacheLPage(fd);
+                scsiPrintSeagateCacheLPage(device);
             if (gSeagateFactoryLPage)
-                scsiPrintSeagateFactoryLPage(fd);
+                scsiPrintSeagateFactoryLPage(device);
         }
     }
     if (con->smarterrorlog) {
         if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
-        scsiPrintErrorCounterLog(fd);
-        if (1 == scsiFetchControlGLTSD(fd, modese_len, 1))
+            scsiGetSupportedLogPages(device);
+        scsiPrintErrorCounterLog(device);
+        if (1 == scsiFetchControlGLTSD(device, modese_len, 1))
             pout("\n[GLTSD (Global Logging Target Save Disable) set. "
                  "Enable Save with '-S on']\n");
     }
     if (con->smartselftestlog) {
         if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
+            scsiGetSupportedLogPages(device);
         res = 0;
         if (gSelfTestLPage)
-            res = scsiPrintSelfTest(fd);
+            res = scsiPrintSelfTest(device);
         else {
             pout("Device does not support Self Test logging\n");
             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
@@ -1353,10 +1324,10 @@ int scsiPrintMain(int fd)
     }
     if (con->smartbackgroundlog) {
         if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
+            scsiGetSupportedLogPages(device);
         res = 0;
         if (gBackgroundResultsLPage)
-            res = scsiPrintBackgroundResults(fd);
+            res = scsiPrintBackgroundResults(device);
         else {
             pout("Device does not support Background scan results logging\n");
             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
@@ -1365,26 +1336,26 @@ int scsiPrintMain(int fd)
             failuretest(OPTIONAL_CMD, returnval|=res);
     }
     if (con->smartexeoffimmediate) {
-        if (scsiSmartDefaultSelfTest(fd))
+        if (scsiSmartDefaultSelfTest(device))
             return returnval | FAILSMART;
         pout("Default Self Test Successful\n");
     }
     if (con->smartshortcapselftest) {
-        if (scsiSmartShortCapSelfTest(fd))
+        if (scsiSmartShortCapSelfTest(device))
             return returnval | FAILSMART;
         pout("Short Foreground Self Test Successful\n");
     }
     if (con->smartshortselftest ) { 
-        if (scsiSmartShortSelfTest(fd))
+        if (scsiSmartShortSelfTest(device))
             return returnval | FAILSMART;
         pout("Short Background Self Test has begun\n");
         pout("Use smartctl -X to abort test\n");
     }
     if (con->smartextendselftest) {
-        if (scsiSmartExtendSelfTest(fd))
+        if (scsiSmartExtendSelfTest(device))
             return returnval | FAILSMART;
         pout("Extended Background Self Test has begun\n");
-        if ((0 == scsiFetchExtendedSelfTestTime(fd, &durationSec, 
+        if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
                         modese_len)) && (durationSec > 0)) {
             time_t t = time(NULL);
 
@@ -1396,12 +1367,12 @@ int scsiPrintMain(int fd)
         pout("Use smartctl -X to abort test\n");        
     }
     if (con->smartextendcapselftest) {
-        if (scsiSmartExtendCapSelfTest(fd))
+        if (scsiSmartExtendCapSelfTest(device))
             return returnval | FAILSMART;
         pout("Extended Foreground Self Test Successful\n");
     }
     if (con->smartselftestabort) {
-        if (scsiSmartSelfTestAbort(fd))
+        if (scsiSmartSelfTestAbort(device))
             return returnval | FAILSMART;
         pout("Self Test returned without error\n");
     }           
diff --git a/sm5/scsiprint.h b/sm5/scsiprint.h
index 699bef5b6c8a787c7a4c2c688be5d3a185372f40..14dcea912a25fb6807ec60bfce43329a5165cd67 100644
--- a/sm5/scsiprint.h
+++ b/sm5/scsiprint.h
@@ -30,8 +30,8 @@
 #ifndef SCSI_PRINT_H_
 #define SCSI_PRINT_H_
 
-#define SCSIPRINT_H_CVSID "$Id: scsiprint.h,v 1.21 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define SCSIPRINT_H_CVSID "$Id: scsiprint.h,v 1.22 2008/07/25 21:16:00 chrfranke Exp $\n"
 
-int scsiPrintMain(int fd);
+int scsiPrintMain(scsi_device * device);
 
 #endif
diff --git a/sm5/smartctl.cpp b/sm5/smartctl.cpp
index 2a15268c28519117876d87402124499d89a5491b..61ace414469b917efb1f20e73904e62f48fd7b4d 100644
--- a/sm5/smartctl.cpp
+++ b/sm5/smartctl.cpp
@@ -48,6 +48,7 @@
 
 #include "int64.h"
 #include "atacmds.h"
+#include "dev_interface.h"
 #include "ataprint.h"
 #include "extern.h"
 #include "knowndrives.h"
@@ -63,7 +64,7 @@ extern const char *os_solaris_ata_s_cvsid;
 extern const char *cciss_c_cvsid;
 #endif
 extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid;
-const char* smartctl_c_cvsid="$Id: smartctl.cpp,v 1.180 2008/06/12 21:46:31 ballen4705 Exp $"
+const char* smartctl_c_cvsid="$Id: smartctl.cpp,v 1.181 2008/07/25 21:16:00 chrfranke Exp $"
 ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // This is a block containing all the "control variables".  We declare
@@ -149,7 +150,7 @@ void Usage (void){
 "  -q TYPE, --quietmode=TYPE                                           (ATA)\n"
 "         Set smartctl quiet mode to one of: errorsonly, silent, noserial\n\n"
 "  -d TYPE, --device=TYPE\n"
-"         Specify device type to one of: ata, scsi, marvell, sat, areca,N, 3ware,N\n\n"
+"         Specify device type to one of: %s\n\n"
 "  -T TYPE, --tolerance=TYPE                                           (ATA)\n"
 "         Tolerance: normal, conservative, permissive, verypermissive\n\n"
 "  -b TYPE, --badsum=TYPE                                              (ATA)\n"
@@ -157,18 +158,18 @@ void Usage (void){
 "  -r TYPE, --report=TYPE\n"
 "         Report transactions (see man page)\n\n"
 "  -n MODE, --nocheck=MODE                                             (ATA)\n"
-"         No check if: never, sleep, standby, idle (see man page)\n\n"
-  );
+"         No check if: never, sleep, standby, idle (see man page)\n\n",
+  smi()->get_valid_dev_types_str());
 #else
   printf(
-"  -q TYPE   Set smartctl quiet mode to one of: errorsonly, silent,    (ATA)\n"
+"  -q TYPE   Set smartctl quiet mode to one of: errorsonly, silent     (ATA)\n"
 "                                               noserial\n"
-"  -d TYPE   Specify device type to one of: ata, scsi, 3ware,N, areca,N\n"
-"  -T TYPE   Tolerance: normal, conservative,permissive,verypermissive (ATA)\n"
+"  -d TYPE   Specify device type to one of: %s\n"
+"  -T TYPE   Tolerance: normal, conservative,permissive,verypermissive (ATA\n"
 "  -b TYPE   Set action on bad checksum to one of: warn, exit, ignore  (ATA)\n"
 "  -r TYPE   Report transactions (see man page)\n"
-"  -n MODE   No check if: never, sleep, standby, idle (see man page)   (ATA)\n\n"
-  );
+"  -n MODE   No check if: never, sleep, standby, idle (see man page)   (ATA)\n\n",
+  smi()->get_valid_dev_types_str());
 #endif
   printf("============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n");
 #ifdef HAVE_GETOPT_LONG
@@ -239,8 +240,9 @@ void Usage (void){
 "  -X        Abort any non-captive test\n\n"
   );
 #endif
-  print_smartctl_examples();
-  return;
+  const char * examples = smi()->get_app_examples("smartctl");
+  if (examples)
+    printf("%s\n", examples);
 }
 
 /* Returns a pointer to a static string containing a formatted list of the valid
@@ -250,7 +252,7 @@ const char *getvalidarglist(char opt) {
   case 'q':
     return "errorsonly, silent, noserial";
   case 'd':
-    return "ata, scsi, marvell, sat, areca,N, 3ware,N, hpt,L/M/N cciss,N";
+    return smi()->get_valid_dev_types_str();
   case 'T':
     return "normal, conservative, permissive, verypermissive";
   case 'b':
@@ -307,7 +309,7 @@ void printvalidarglistmessage(char opt) {
 }
 
 /*      Takes command options and sets features to be run */    
-void ParseOpts (int argc, char** argv){
+const char * ParseOpts (int argc, char** argv){
   int optchar;
   int badarg;
   int captive;
@@ -357,6 +359,8 @@ void ParseOpts (int argc, char** argv){
   opterr=optopt=0;
   badarg = captive = FALSE;
   
+  const char * type = 0; // set to -d optarg
+
   // This miserable construction is needed to get emacs to do proper indenting. Sorry!
   while (-1 != (optchar = 
 #ifdef HAVE_GETOPT_LONG
@@ -386,153 +390,7 @@ void ParseOpts (int argc, char** argv){
       }
       break;
     case 'd':
-      con->controller_explicit = 1;
-      if (!strcmp(optarg,"ata")) {
-        con->controller_type = CONTROLLER_ATA;
-        con->controller_port = 0;
-      } else if (!strcmp(optarg,"scsi")) {
-        con->controller_type = CONTROLLER_SCSI;
-        con->controller_port = 0;
-      } else if (!strcmp(optarg,"marvell")) {
-        con->controller_type = CONTROLLER_MARVELL_SATA;
-        con->controller_port = 0;
-      } else if (!strncmp(optarg, "usbcypress", 10)) {
-        con->controller_type = CONTROLLER_USBCYPRESS;
-        con->controller_port = 0;
-        con->usbcypress_signature = 0x24;
-        if (strlen(optarg) > 10) {
-          int k;
-          char * cp;
-
-          cp = strchr(optarg, ',');
-          if (cp && (1 == sscanf(cp + 1, "0x%x", &k)) &&
-              ((0 <= k) && (0xff >= k)))
-            con->usbcypress_signature = k;
-          else {
-            sprintf(extraerror, "Option '-d usbcypress,<n>' requires <n> to be "
-                    "an hexadecimal number between 0x0 and 0xff\n");
-            badarg = TRUE;
-          }
-        }
-      } else if (!strncmp(optarg, "sat", 3)) {
-        con->controller_type = CONTROLLER_SAT;
-        con->controller_port = 0;
-        con->satpassthrulen = 0;
-        if (strlen(optarg) > 3) {
-          int k;
-          char * cp;
-
-          cp = strchr(optarg, ',');
-          if (cp && (1 == sscanf(cp + 1, "%d", &k)) &&
-              ((0 == k) || (12 == k) || (16 == k)))
-            con->satpassthrulen = k;
-          else {
-            sprintf(extraerror, "Option '-d sat,<n>' requires <n> to be "
-                    "0, 12 or 16\n");
-            badarg = TRUE;
-          }
-        }
-      } else if (!strncmp(optarg, "hpt", 3)){
-        unsigned char i, slash = 0;
-        con->hpt_data[0] = 0;
-        con->hpt_data[1] = 0;
-        con->hpt_data[2] = 0;
-        con->controller_type = CONTROLLER_HPT;
-        for (i=4; i < strlen(optarg); i++) {
-          if(optarg[i] == '/') {
-            slash++;
-            if(slash == 3) {
-              sprintf(extraerror, "Option '-d hpt,L/M/N' supports 2-3 items\n");
-              badarg = TRUE;
-              break;
-            }
-          }
-          else if ((optarg[i])>='0' && (optarg[i])<='9') {
-            if (con->hpt_data[slash]>1) { /* hpt_data[x] max 19 */
-              badarg = TRUE;
-              break;
-            }
-            con->hpt_data[slash] = con->hpt_data[slash]*10 + optarg[i] - '0';
-          }
-          else {
-            badarg = TRUE;
-            break;
-          }
-        }
-        if (slash == 0) {
-          sprintf(extraerror, "Option '-d hpt,L/M/N' requires 2-3 items\n");
-          badarg = TRUE;
-        } else if (badarg != TRUE) {
-          if (con->hpt_data[0]==0 || con->hpt_data[0]>8){
-            sprintf(extraerror, "Option '-d hpt,L/M/N' no/invalid controller id L supplied\n");
-            badarg = TRUE;
-          }
-          if (con->hpt_data[1]==0 || con->hpt_data[1]>8){
-            sprintf(extraerror, "Option '-d hpt,L/M/N' no/invalid channel number M supplied\n");
-            badarg = TRUE;
-          }
-          if (slash==2) {
-            if ( con->hpt_data[2]==0 || con->hpt_data[2]>15) {
-              sprintf(extraerror, "Option '-d hpt,L/M/N' no/invalid pmport number N supplied\n");
-              badarg = TRUE;
-            }
-          } else {
-            con->hpt_data[2]=1;
-          }
-        }
-      } else {
-        // look for RAID-type device
-        int i;
-        char *s;
-        
-        // make a copy of the string to mess with
-        if (!(s = strdup(optarg))) {
-          con->dont_print = FALSE;
-          throw std::bad_alloc();
-        } else if (!strncmp(s,"3ware,",6)) {
-            if (split_report_arg2(s, &i)) {
-                 sprintf(extraerror, "Option -d 3ware,N requires N to be a non-negative integer\n");
-                 badarg = TRUE;
-            } else if (i<0 || i>127) {
-                 sprintf(extraerror, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127\n", i);
-                 badarg = TRUE;
-            } else {
- 	        // NOTE: controller_port == disk number + 1
- 	        con->controller_type = CONTROLLER_3WARE;
-                 con->controller_port = i+1;
-            }
- 	    free(s);
-
-	} else if (!strncmp(s,"areca,",6)) {
-	    if (split_report_arg2(s, &i)) {
-	         sprintf(extraerror, "Option -d areca,N requires N to be a positive integer\n");
-	         badarg = TRUE;
-	    } else if (i<1 || i>24) {
-	         sprintf(extraerror, "Option -d areca,N (N=%d) must have 1 <= N <= 24\n", i);
-	         badarg = TRUE;
-	    } else {
-	       	 // NOTE: controller_port == disk number + 1
-	       	 con->controller_type = CONTROLLER_ARECA;
-	         con->controller_port = i+1;
-	    }
-	    free(s);
-
-        } else if (!strncmp(s,"cciss,",6)) {
-             if (split_report_arg2(s, &i)) {
-                 sprintf(extraerror, "Option -d cciss,N requires N to be a non-negative integer\n");
-                 badarg = TRUE;
-             } else if (i<0 || i>127) {
-                 sprintf(extraerror, "Option -d cciss,N (N=%d) must have 0 <= N <= 127\n", i);
-                 badarg = TRUE;
-             } else {
-               // NOTE: controller_port == drive number
-               con->controller_type = CONTROLLER_CCISS;
-               con->controller_port = i+1;
-             }
-             free(s);
-        } else
- 	    badarg=TRUE;
-      }
+      type = optarg;
       break;
     case 'T':
       if (!strcmp(optarg,"normal")) {
@@ -935,7 +793,8 @@ void ParseOpts (int argc, char** argv){
       pout("%s\n",argv[optind+i]);
     UsageSummary();
     EXIT(FAILCMD);
-  }  
+  }
+  return type;
 }
 
 // Printing function (controlled by global con->dont_print) 
@@ -981,116 +840,89 @@ void PrintOut(int priority, const char *fmt, ...) {
 // Main program without exception handling
 int main_worker(int argc, char **argv)
 {
-  int fd,retval=0;
-  char *device;
-  smartmonctrl control;
-  const char *mode = 0;
+  // Initialize interface
+  smart_interface::init();
+  if (!smi())
+    return 1;
 
-  // define control block for external functions
-  con=&control;
+  int retval = 0;
 
-  // Part input arguments
-  ParseOpts(argc,argv);
+  smart_device * dev = 0;
+  try {
+    // define control block for external functions
+    smartmonctrl control;
+    con=&control;
+
+    // Parse input arguments
+    const char * type = ParseOpts(argc,argv);
 
-  device = argv[argc-1];
+    const char * name = argv[argc-1];
 
-  // Device name "-": Parse "smartctl -r ataioctl,2 ..." output
-  if (!strcmp(device,"-")) {
-    if (con->controller_type != CONTROLLER_UNKNOWN) {
-      pout("Smartctl: -d option is not allowed in conjunction with device name \"-\".\n");
+    if (!strcmp(name,"-")) {
+      // Parse "smartctl -r ataioctl,2 ..." output from stdin
+      if (type) {
+        pout("Smartctl: -d option is not allowed in conjunction with device name \"-\".\n");
+        UsageSummary();
+        return FAILCMD;
+      }
+      dev = get_parsed_ata_device(smi(), name);
+    }
+    else
+      // get device of appropriate type
+      dev = smi()->get_smart_device(name, type);
+
+    if (!dev) {
+      pout("%s: %s\n", name, smi()->get_errmsg());
+      if (type)
+        printvalidarglistmessage('d');
+      else
+        pout("Smartctl: please specify device type with the -d option.\n");
       UsageSummary();
       return FAILCMD;
     }
-    con->controller_type = CONTROLLER_PARSEDEV;
-  }
 
-  // If use has specified 3ware controller, determine which interface 
-  if (con->controller_type == CONTROLLER_3WARE) {
-    con->controller_type=guess_device_type(device);
-    if (con->controller_type!=CONTROLLER_3WARE_9000_CHAR && con->controller_type!=CONTROLLER_3WARE_678K_CHAR)
-      con->controller_type = CONTROLLER_3WARE_678K;
-  }
+    // open device - SCSI devices are opened (O_RDWR | O_NONBLOCK) so the
+    // scsi generic device can be used (needs write permission for MODE
+    // SELECT command) plus O_NONBLOCK to stop open hanging if media not
+    // present (e.g. with st).  Opening is retried O_RDONLY if read-only
+    // media prevents opening O_RDWR (it cannot happen for scsi generic
+    // devices, but it can for the others).
+    // TODO: The above comment is probably linux related ;-)
+
+    // Open device
+    {
+      // Save old info
+      smart_device::device_info oldinfo = dev->get_info();
+
+      // Open with autodetect support, may return 'better' device
+      dev = dev->autodetect_open();
+
+      // Report if type has changed
+      if (type && oldinfo.dev_type != dev->get_dev_type())
+        pout("%s: Device type changed from '%s' to '%s'\n",
+          name, oldinfo.dev_type.c_str(), dev->get_dev_type());
+    }
+    if (!dev->is_open()) {
+      pout("Smartctl open device: %s failed: %s\n", dev->get_info_name(), dev->get_errmsg());
+      delete dev;
+      return FAILDEV;
+    }
 
-  if (con->controller_type == CONTROLLER_UNKNOWN)
-    con->controller_type=guess_device_type(device);
-  
-  if (con->controller_type == CONTROLLER_UNKNOWN) {
-    pout("Smartctl: please specify device type with the -d option.\n");
-    UsageSummary();
-    return FAILCMD;
-  }
-  
-  // set up mode for open() call.  SCSI case is:
-  switch (con->controller_type) {
-  case CONTROLLER_SCSI:
-  case CONTROLLER_SAT:
-  case CONTROLLER_USBCYPRESS:
-    mode="SCSI";
-    break;
-  case CONTROLLER_3WARE_9000_CHAR:
-    mode="ATA_3WARE_9000";
-    break;
-  case CONTROLLER_3WARE_678K_CHAR:
-    mode="ATA_3WARE_678K";
-    break;
-  case CONTROLLER_CCISS:
-    mode="CCISS";
-    break;
-  case CONTROLLER_ARECA:
-    mode="ATA_ARECA";
-    break;
-  default:
-    mode="ATA";
-    break;
-  }
-  
-  // open device - SCSI devices are opened (O_RDWR | O_NONBLOCK) so the
-  // scsi generic device can be used (needs write permission for MODE 
-  // SELECT command) plus O_NONBLOCK to stop open hanging if media not
-  // present (e.g. with st).  Opening is retried O_RDONLY if read-only
-  // media prevents opening O_RDWR (it cannot happen for scsi generic
-  // devices, but it can for the others).
-  if (con->controller_type != CONTROLLER_PARSEDEV)
-    fd = deviceopen(device, (char*)mode); // TODO: const
-  else
-    fd = parsedev_open(device);
-  if (fd<0) {
-    char errmsg[256];
-    snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]);
-    errmsg[255]='\0';
-    syserror(errmsg);
-    return FAILDEV;
-  }
+    // now call appropriate ATA or SCSI routine
+    if (dev->is_ata())
+      retval = ataPrintMain(dev->to_ata());
+    else if (dev->is_scsi())
+      retval = scsiPrintMain(dev->to_scsi());
+    else
+      ; // we should never fall into this branch!
 
-  // now call appropriate ATA or SCSI routine
-  switch (con->controller_type) {
-  case CONTROLLER_UNKNOWN:
-    // we should never fall into this branch!
-    pout("Smartctl: please specify device type with the -d option.\n");
-    UsageSummary();
-    retval = FAILCMD;
-    break;
-  case CONTROLLER_SCSI:
-    retval = scsiPrintMain(fd);
-    if ((0 == retval) && 
-			(CONTROLLER_SAT == con->controller_type) || (CONTROLLER_USBCYPRESS == con->controller_type))
-        retval = ataPrintMain(fd);
-    break;
-  case CONTROLLER_CCISS:
-    // route the cciss command through scsiPrintMain. 
-    // cciss pass-throughs will separeate from the SCSI data-path.
-    retval = scsiPrintMain(fd);
-    break;
-  default:
-    retval = ataPrintMain(fd);
-    break;
+    dev->close();
+    delete dev;
+  }
+  catch (...) {
+    delete dev;
+    throw;
   }
-  
-  if (con->controller_type != CONTROLLER_PARSEDEV)
-    deviceclose(fd);
-  else
-    parsedev_close(fd);
-
   return retval;
 }
 
diff --git a/sm5/smartctl.h b/sm5/smartctl.h
index 781062a499c32d00077d94e5666090f222e88122..958320c18161f2ab18aa9866bb0d53cd1316b0e3 100644
--- a/sm5/smartctl.h
+++ b/sm5/smartctl.h
@@ -25,7 +25,7 @@
 #ifndef SMARTCTL_H_
 #define SMARTCTL_H_
 
-#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.25 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.26 2008/07/25 21:16:00 chrfranke Exp $\n"
 
 /* Boolean Values */
 #define TRUE 0x01
@@ -72,6 +72,7 @@
 #define OPTIONAL_CMD 1
 #define MANDATORY_CMD 2
 
-void print_smartctl_examples();
+// Moved to C++ interface
+//void print_smartctl_examples();
 
 #endif
diff --git a/sm5/smartd.cpp b/sm5/smartd.cpp
index 73361d75ebcba26b11fcb1e20bb2afa9d866ad70..6eff68aa20ba0a17619bc6af7bcde60c679c4538 100644
--- a/sm5/smartd.cpp
+++ b/sm5/smartd.cpp
@@ -79,6 +79,7 @@ extern "C" int __stdcall FreeConsole(void);
 // locally included files
 #include "int64.h"
 #include "atacmds.h"
+#include "dev_interface.h"
 #include "ataprint.h"
 #include "extern.h"
 #include "knowndrives.h"
@@ -119,7 +120,7 @@ extern "C" int getdomainname(char *, int); // no declaration in header files!
 extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *escalade_c_cvsid, 
                   *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid;
 
-static const char *filenameandversion="$Id: smartd.cpp,v 1.410 2008/07/21 14:53:05 brevilo Exp $";
+static const char *filenameandversion="$Id: smartd.cpp,v 1.411 2008/07/25 21:16:00 chrfranke Exp $";
 #ifdef _HAVE_CCISS
 extern const char *cciss_c_cvsid;
 #endif
@@ -129,7 +130,7 @@ extern const char *os_solaris_ata_s_cvsid;
 #ifdef _WIN32
 extern const char *daemon_win32_c_cvsid, *hostname_win32_c_cvsid, *syslog_win32_c_cvsid;
 #endif
-const char *smartd_c_cvsid="$Id: smartd.cpp,v 1.410 2008/07/21 14:53:05 brevilo Exp $" 
+const char *smartd_c_cvsid="$Id: smartd.cpp,v 1.411 2008/07/25 21:16:00 chrfranke Exp $" 
 ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID
 #ifdef DAEMON_WIN32_H_CVSID
 DAEMON_WIN32_H_CVSID
@@ -361,6 +362,7 @@ void RmConfigEntry(cfgfile **anentry, int whatline){
 
   // entry exists -- free all of its memory  
   cfg->name            = FreeNonZero(cfg->name,           -1,__LINE__,filenameandversion);
+  cfg->dev_type        = FreeNonZero(cfg->dev_type,       -1,__LINE__,filenameandversion);
   cfg->smartthres      = FreeNonZero(cfg->smartthres,      sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion);
   cfg->smartval        = FreeNonZero(cfg->smartval,        sizeof(struct ata_smart_values),__LINE__,filenameandversion);
   cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion);
@@ -717,81 +719,8 @@ void MailWarning(cfgfile *cfg, int which, const char *fmt, ...){
     exportenv(environ_strings[6], "SMARTD_ADDRESS", address);
   exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg->name);
 
-  switch (cfg->controller_type) {
-  case CONTROLLER_3WARE_678K:
-  case CONTROLLER_3WARE_9000_CHAR:
-  case CONTROLLER_3WARE_678K_CHAR:
-    {
-      char *s,devicetype[16];
-      sprintf(devicetype, "3ware,%d", cfg->controller_port-1);
-      exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
-      if ((s=strchr(cfg->name, ' ')))
-	*s='\0';
-      exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-      if (s)
-	*s=' ';
-    }
-    break;
-  case CONTROLLER_CCISS:
-    {
-      char *s,devicetype[16];
-      sprintf(devicetype, "cciss,%d", cfg->controller_port-1);
-      exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
-      if ((s=strchr(cfg->name, ' ')))
-	*s='\0';
-      exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-      if (s)
-	*s=' ';
-    }
-    break;
-  case CONTROLLER_ATA:
-    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "ata");
-    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-    break;
-  case CONTROLLER_MARVELL_SATA:
-    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "marvell");
-    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-    break;
-  case CONTROLLER_SCSI:
-    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "scsi");
-    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-    break;
-  case CONTROLLER_SAT:
-    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "sat");
-    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-    break;
-  case CONTROLLER_HPT:
-    {
-      char *s,devicetype[16];
-      sprintf(devicetype, "hpt,%d/%d/%d", cfg->hpt_data[0],
-              cfg->hpt_data[1], cfg->hpt_data[2]);
-      exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
-      if ((s=strchr(cfg->name, ' ')))
-        *s='\0';
-      exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-      if (s)
-        *s=' ';
-    }
-    break;
-  case CONTROLLER_USBCYPRESS:
-    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "usbcypress");
-    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-    break;
-  case CONTROLLER_ARECA:
-    {
-      char *s,devicetype[16];
-      // NOTE: controller_port == disk number + 1
-      sprintf(devicetype, "areca,%d", cfg->controller_port-1);
-      exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
-      if ((s=strchr(cfg->name, ' '))) {
-        *s='\0';
-      }
-      exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-      if (s) {
-        *s=' ';
-      }
-    }
-  }
+  exportenv(environ_strings[8], "SMARTD_DEVICETYPE", cfg->dev_type);
+  exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
 
   snprintf(fullmessage, 1024,
              "This email was generated by the smartd daemon running on:\n\n"
@@ -988,7 +917,7 @@ void pout(const char *fmt, ...){
 #endif
     vprintf(fmt,ap);
   // in debug==2 mode we print output from knowndrives.o functions
-  else if (debugmode==2 || con->reportataioctl || con->reportscsiioctl || con->controller_port) {
+  else if (debugmode==2 || con->reportataioctl || con->reportscsiioctl /*|| con->controller_port???*/) {
     openlog("smartd", LOG_PID, facility);
     vsyslog(LOG_INFO, fmt, ap);
     closelog();
@@ -1165,7 +1094,7 @@ void PrintHead(){
 void Directives() {
   PrintOut(LOG_INFO,
            "Configuration file (%s) Directives (after device name):\n"
-           "  -d TYPE Set the device type: ata, scsi, marvell, removable, sat, 3ware,N, areca,N, hpt,L/M/N, cciss,N\n"
+           "  -d TYPE Set the device type: %s\n"
            "  -T TYPE Set the tolerance to one of: normal, permissive\n"
            "  -o VAL  Enable/disable automatic offline tests (on/off)\n"
            "  -S VAL  Enable/disable attribute autosave (on/off)\n"
@@ -1195,7 +1124,7 @@ void Directives() {
            "Attribute ID is a decimal integer 1 <= ID <= 255\n"
 	   "Use ID = 0 to turn off -C and/or -U Directives\n"
            "Example: /dev/hda -a\n", 
-           configfile);
+           configfile, smi()->get_valid_dev_types_str());
   return;
 }
 
@@ -1277,45 +1206,9 @@ void Usage (void){
 #endif
 }
 
-// returns negative if problem, else fd>=0
-static int OpenDevice(/*const*/ char *device, const char *mode, int scanning) {
-  int fd;
-  char *s=device;
-  
-  // If there is an ASCII "space" character in the device name,
-  // terminate string there.  This is for 3ware, areca and highpoint devices only.
-  if ((s=strchr(device,' ')))
-    *s='\0';
-
-  // open the device
-  fd = deviceopen(device, (char*)mode); // TODO: const
-
-  // if we removed a space, put it back in please
-  if (s)
-    *s=' ';
-
-  // if we failed to open the device, complain!
-  if (fd < 0) {
-
-    // For linux+devfs, a nonexistent device gives a strange error
-    // message.  This makes the error message a bit more sensible.
-    // If no debug and scanning - don't print errors
-    if (debugmode || !scanning) {
-      if (errno==ENOENT || errno==ENOTDIR)
-        errno=ENODEV;
-
-      PrintOut(LOG_INFO,"Device: %s, %s, open() failed\n",
-	       device, strerror(errno));
-    }
-    return -1;
-  }
-  // device opened sucessfully
-  return fd;
-}
-
-int CloseDevice(int fd, const char *name){
-  if (deviceclose(fd)){
-    PrintOut(LOG_INFO,"Device: %s, %s, close(%d) failed\n", name, strerror(errno), fd);
+int CloseDevice(smart_device * device, char *name){
+  if (!device->close()){
+    PrintOut(LOG_INFO,"Device: %s, %s, close() failed\n", name, device->get_errmsg());
     return 1;
   }
   // device sucessfully closed
@@ -1323,10 +1216,10 @@ int CloseDevice(int fd, const char *name){
 }
 
 // returns <0 on failure
-int ATAErrorCount(int fd, char *name){
+int ATAErrorCount(ata_device * device, char *name){
   struct ata_smart_errorlog log;
   
-  if (-1==ataReadErrorLog(fd,&log)){
+  if (-1==ataReadErrorLog(device,&log)){
     PrintOut(LOG_INFO,"Device: %s, Read SMART Error Log Failed\n",name);
     return -1;
   }
@@ -1337,10 +1230,10 @@ int ATAErrorCount(int fd, char *name){
 
 // returns <0 if problem.  Otherwise, bottom 8 bits are the self test
 // error count, and top bits are the power-on hours of the last error.
-int SelfTestErrorCount(int fd, char *name){
+int SelfTestErrorCount(ata_device * device, char *name){
   struct ata_smart_selftestlog log;
 
-  if (-1==ataReadSelfTestLog(fd,&log)){
+  if (-1==ataReadSelfTestLog(device,&log)){
     PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name);
     return -1;
   }
@@ -1350,68 +1243,27 @@ int SelfTestErrorCount(int fd, char *name){
 }
 
 // scan to see what ata devices there are, and if they support SMART
-int ATADeviceScan(cfgfile *cfg, int scanning){
-  int fd, supported=0;
+int ATADeviceScan(cfgfile *cfg){
+  int supported=0;
   struct ata_identify_device drive;
   /*const*/ char *name = cfg->name;
   int retainsmartdata=0;
   int retid;
-  const char *mode;
-  
-  // should we try to register this as an ATA device?
-  switch (cfg->controller_type) {
-  case CONTROLLER_ATA:
-  case CONTROLLER_3WARE_678K:
-  case CONTROLLER_MARVELL_SATA:
-  case CONTROLLER_HPT:
-  case CONTROLLER_USBCYPRESS:
-  case CONTROLLER_UNKNOWN:
-    mode="ATA";
-    break;
-  case CONTROLLER_3WARE_678K_CHAR:
-    mode="ATA_3WARE_678K";
-    break;
-  case CONTROLLER_3WARE_9000_CHAR:
-    mode="ATA_3WARE_9000";
-    break;
-  case CONTROLLER_SAT:
-    mode="SCSI";
-    break;
-  case CONTROLLER_ARECA:
-    mode="ATA_ARECA";
-    break;
-  default:
-    // not a recognized ATA or SATA device.  We should never enter
-    // this branch.
-    return 1;
-  }
-  
-  // open the device
-  if ((fd=OpenDevice(name, mode, scanning))<0)
-    // device open failed
-    return 1;
-  PrintOut(LOG_INFO,"Device: %s, opened\n", name);
-  
+
+  // Device must be open
+
   // pass user settings on to low-level ATA commands
-  con->controller_port=cfg->controller_port;
-  con->hpt_data[0]=cfg->hpt_data[0];
-  con->hpt_data[1]=cfg->hpt_data[1];
-  con->hpt_data[2]=cfg->hpt_data[2];
-  con->controller_type=cfg->controller_type;
-  con->controller_explicit=cfg->controller_explicit;
   con->fixfirmwarebug = cfg->fixfirmwarebug;
-  con->satpassthrulen = cfg->satpassthrulen;
-  con->usbcypress_signature = cfg->usbcypress_signature;
   
   // Get drive identity structure
-  if ((retid=ataReadHDIdentity (fd,&drive))){
+  if ((retid=ataReadHDIdentity (cfg->atadev,&drive))){
     if (retid<0)
       // Unable to read Identity structure
       PrintOut(LOG_INFO,"Device: %s, not ATA, no IDENTIFY DEVICE Structure\n",name);
     else
       PrintOut(LOG_INFO,"Device: %s, packet devices [this device %s] not SMART capable\n",
                name, packetdevicetype(retid-1));
-    CloseDevice(fd, name);
+    CloseDevice(cfg->device, name);
     return 2; 
   }
 
@@ -1457,21 +1309,21 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
     }
     else {
       PrintOut(LOG_INFO,"Device: %s, to proceed anyway, use '-T permissive' Directive.\n",name);
-      CloseDevice(fd, name);
+      CloseDevice(cfg->device, name);
       return 2;
     }
   }
   
-  if (ataEnableSmart(fd)){
+  if (ataEnableSmart(cfg->atadev)){
     // Enable SMART command has failed
     PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name);
-    CloseDevice(fd, name);
+    CloseDevice(cfg->device, name);
     return 2; 
   }
   
   // disable device attribute autosave...
   if (cfg->autosave==1){
-    if (ataDisableAutoSave(fd))
+    if (ataDisableAutoSave(cfg->atadev))
       PrintOut(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",name);
     else
       PrintOut(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",name);
@@ -1479,14 +1331,14 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
 
   // or enable device attribute autosave
   if (cfg->autosave==2){
-    if (ataEnableAutoSave(fd))
+    if (ataEnableAutoSave(cfg->atadev))
       PrintOut(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",name);
     else
       PrintOut(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",name);
   }
 
   // capability check: SMART status
-  if (cfg->smartcheck && ataSmartStatus2(fd)==-1){
+  if (cfg->smartcheck && ataSmartStatus2(cfg->atadev)==-1){
     PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name);
     cfg->smartcheck=0;
   }
@@ -1513,8 +1365,8 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
       EXIT(EXIT_NOMEM);
     }
     
-    if (ataReadSmartValues(fd,cfg->smartval) ||
-        ataReadSmartThresholds (fd,cfg->smartthres)){
+    if (ataReadSmartValues(cfg->atadev,cfg->smartval) ||
+        ataReadSmartThresholds (cfg->atadev,cfg->smartthres)){
       PrintOut(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",name);
       retainsmartdata=cfg->usagefailed=cfg->prefail=cfg->usage=0;
       cfg->tempdiff = cfg->tempinfo = cfg->tempcrit = 0;
@@ -1557,7 +1409,7 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
       if (!isSupportAutomaticTimer(cfg->smartval))
         PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name);
       // ... but then try anyway
-      if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(fd):ataEnableAutoOffline(fd))
+      if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(cfg->atadev):ataEnableAutoOffline(cfg->atadev))
         PrintOut(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", name, what);
       else
         PrintOut(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", name, what);
@@ -1577,7 +1429,7 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
       PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log (SMART READ DATA failed); disabling -l selftest\n", name);
     else if (!cfg->permissive && !isSmartTestLogCapable(cfg->smartval, &drive))
       PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Self-Test log; disabling -l selftest (override with -T permissive Directive)\n", name);
-    else if ((retval=SelfTestErrorCount(fd, name))<0)
+    else if ((retval=SelfTestErrorCount(cfg->atadev, name))<0)
       PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log; remove -l selftest Directive from smartd.conf\n", name);
     else {
       cfg->selftest=1;
@@ -1598,7 +1450,7 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
       PrintOut(LOG_INFO, "Device: %s, no SMART Error log (SMART READ DATA failed); disabling -l error\n", name);
     else if (!cfg->permissive && !isSmartErrorLogCapable(cfg->smartval, &drive))
       PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Error log; disabling -l error (override with -T permissive Directive)\n", name);
-    else if ((val=ATAErrorCount(fd, name))<0)
+    else if ((val=ATAErrorCount(cfg->atadev, name))<0)
       PrintOut(LOG_INFO, "Device: %s, no SMART Error log; remove -l error Directive from smartd.conf\n", name);
     else {
         cfg->errorlog=1;
@@ -1620,7 +1472,7 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
 
   // capabilities check -- does it support powermode?
   if (cfg->powermode) {
-    int powermode=ataCheckPowerMode(fd);
+    int powermode=ataCheckPowerMode(cfg->atadev);
     
     if (-1 == powermode) {
       PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name);
@@ -1637,7 +1489,7 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
   if (!(cfg->errorlog    || cfg->selftest || cfg->smartcheck || 
         cfg->usagefailed || cfg->prefail  || cfg->usage      ||
         cfg->tempdiff    || cfg->tempinfo || cfg->tempcrit     )) {
-    CloseDevice(fd, name);
+    CloseDevice(cfg->atadev, name);
     return 3;
   }
   
@@ -1649,112 +1501,27 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
   PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",name);
   
     // record number of device, type of device, increment device count
-  if (cfg->controller_type == CONTROLLER_UNKNOWN)
-    cfg->controller_type=CONTROLLER_ATA;
+  if (!cfg->dev_type)
+    cfg->dev_type = CustomStrDup("ata", 1, __LINE__,filenameandversion);
   
   // close file descriptor
-  CloseDevice(fd, name);
+  CloseDevice(cfg->atadev, name);
   return 0;
 }
 
-// Returns 0 if normal SCSI device.
-// Returns -1 if INQUIRY fails.
-// Returns 2 if ATA device detected behind SAT layer.
-// Returns 3 if ATA device detected behind Marvell controller.
-// Returns 1 if other device detected that we don't want to treat
-//     as a normal SCSI device.
-static int SCSIFilterKnown(int fd, char * device)
-{
-  char req_buff[256];
-  int req_len, avail_len, len;
-
-  memset(req_buff, 0, 96);
-  req_len = 36;
-  if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) {
-    /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
-    /* watch this spot ... other devices could lock up here */
-    req_len = 64;
-    if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) {
-      PrintOut(LOG_INFO, "Device: %s, failed on INQUIRY; skip device\n", device);
-      // device doesn't like INQUIRY commands
-      return SCSIFK_FAILED;
-    }
-  }
-  avail_len = req_buff[4] + 5;
-  len = (avail_len < req_len) ? avail_len : req_len;
-  if (len >= 36) {
-    if (0 == strncmp(req_buff + 8, "3ware", 5) || 0 == strncmp(req_buff + 8, "AMCC", 4) ) {
-      PrintOut(LOG_INFO, "Device %s, please try adding '-d 3ware,N'\n", device);
-      PrintOut(LOG_INFO, "Device %s, you may need to replace %s with /dev/twaN or /dev/tweN\n", device, device);
-      return SCSIFK_3WARE;
-    } else if ((len >= 42) && (0 == strncmp(req_buff + 36, "MVSATA", 6))) {
-      PrintOut(LOG_INFO, "Device %s: using '-d marvell' for ATA disk with Marvell driver\n", device);
-      return SCSIFK_MARVELL;
-    } else if ((avail_len >= 36) &&
-               (0 == strncmp(req_buff + 8, "ATA     ", 8)) &&
-	       has_sat_pass_through(fd, 0 /* non-packet dev */)) {
-      PrintOut(LOG_INFO, "Device %s: using '-d sat' for ATA disk behind SAT layer.\n",
-               device);
-      return SCSIFK_SAT;
-    } else if ((avail_len >= 36) &&
-	       has_usbcypress_pass_through(fd, NULL, NULL)) {
-      PrintOut(LOG_INFO, "Device %s: using '-d usbcypress' for ATA disk behind USBCYPRESS layer.\n",
-               device);
-      return SCSIFK_USBCYPRESS;
-    }
-  }
-  return SCSIFK_NORMAL;
-}
-
 // on success, return 0. On failure, return >0.  Never return <0,
 // please.
-static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
-    int k, fd, err, retval; 
+static int SCSIDeviceScan(cfgfile *cfg) {
+  int k, err;
   /*const*/ char *device = cfg->name;
   struct scsi_iec_mode_page iec;
   UINT8  tBuf[64];
-  const char *mode = 0;
-  
-  // should we try to register this as a SCSI device?
-  switch (cfg->controller_type) {
-  case CONTROLLER_SCSI:
-  case CONTROLLER_UNKNOWN:
-    mode="SCSI";
-    break;
-  case CONTROLLER_CCISS:
-    mode="CCISS";
-    break;
-  default:
-    return 1;
-  }
-  // pass user settings on to low-level SCSI commands
-  con->controller_port=cfg->controller_port;
-  con->controller_type=cfg->controller_type;
-  
-  // open the device
-  if ((fd = OpenDevice(device, mode, scanning)) < 0)
-    return 1;
-  PrintOut(LOG_INFO,"Device: %s, opened\n", device);
-
-  // early skip if device known and needs to be handled by some other
-  // device type (e.g. '-d 3ware,<n>')
-  if ((retval = SCSIFilterKnown(fd, device))) {
-    CloseDevice(fd, device);
 
-    if (retval==SCSIFK_SAT)
-	// SATA Device behind SAT layer
-	return SCSIFK_SAT;
+  // Device must be open
 
-    if (retval==SCSIFK_MARVELL)
-        // ATA/SATA device behind Marvell driver
-	return SCSIFK_MARVELL;
-    
-    return 2;
-  }
-    
   // check that device is ready for commands. IE stores its stuff on
   // the media.
-  if ((err = scsiTestUnitReady(fd))) {
+  if ((err = scsiTestUnitReady(cfg->scsidev))) {
     if (SIMPLE_ERR_NOT_READY == err)
       PrintOut(LOG_INFO, "Device: %s, NOT READY (e.g. spun down); skip device\n", device);
     else if (SIMPLE_ERR_NO_MEDIUM == err)
@@ -1763,7 +1530,7 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
       PrintOut(LOG_INFO, "Device: %s, BECOMING (but not yet) READY; skip device\n", device);
     else
       PrintOut(LOG_CRIT, "Device: %s, failed Test Unit Ready [err=%d]\n", device, err);
-    CloseDevice(fd, device);
+    CloseDevice(cfg->scsidev, device);
     return 2; 
   }
   
@@ -1773,7 +1540,7 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
   // that various USB devices that malform the response will lock up
   // if asked for a log page (e.g. temperature) so it is best to
   // bail out now.
-  if (!(err = scsiFetchIECmpage(fd, &iec, cfg->modese_len)))
+  if (!(err = scsiFetchIECmpage(cfg->scsidev, &iec, cfg->modese_len)))
     cfg->modese_len = iec.modese_len;
   else if (SIMPLE_ERR_BAD_FIELD == err)
     ;  /* continue since it is reasonable not to support IE mpage */
@@ -1781,7 +1548,7 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
     PrintOut(LOG_INFO, 
              "Device: %s, Bad IEC (SMART) mode page, err=%d, skip device\n", 
              device, err);
-    CloseDevice(fd, device);
+    CloseDevice(cfg->scsidev, device);
     return 3;
   }
   
@@ -1791,7 +1558,7 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
     PrintOut(LOG_INFO, "Device: %s, IE (SMART) not enabled, skip device\n"
 	               "Try 'smartctl -s on %s' to turn on SMART features\n", 
                         device, device);
-    CloseDevice(fd, device);
+    CloseDevice(cfg->scsidev, device);
     return 3;
   }
   
@@ -1801,7 +1568,7 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
   
   // Flag that certain log pages are supported (information may be
   // available from other sources).
-  if (0 == scsiLogSense(fd, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 0)) {
+  if (0 == scsiLogSense(cfg->scsidev, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 0)) {
     for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) {
       switch (tBuf[k]) { 
       case TEMPERATURE_LPAGE:
@@ -1817,8 +1584,8 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
   }
   
   // record type of device
-  if (cfg->controller_type == CONTROLLER_UNKNOWN)
-          cfg->controller_type = CONTROLLER_SCSI;
+  if (!cfg->dev_type)
+    cfg->dev_type = CustomStrDup("scsi", 1, __LINE__,filenameandversion);
   
   // get rid of allocated memory only needed for ATA devices.  These
   // might have been allocated if the user specified Ignore options or
@@ -1835,7 +1602,7 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
     UINT8 currenttemp = 0;
     UINT8 triptemp = 0;
     
-    if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
+    if (scsiCheckIE(cfg->scsidev, cfg->SmartPageSupported, cfg->TempPageSupported,
                     &asc, &ascq, &currenttemp, &triptemp)) {
       PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device);
       cfg->SuppressReport = 1;
@@ -1848,7 +1615,7 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
   
   // capability check: self-test-log
   if (cfg->selftest){
-    int retval=scsiCountFailedSelfTests(fd, 0);
+    int retval=scsiCountFailedSelfTests(cfg->scsidev, 0);
     if (retval<0) {
       // no self-test log, turn off monitoring
       PrintOut(LOG_INFO, "Device: %s, does not support SMART Self-Test Log.\n", device);
@@ -1865,7 +1632,7 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
   
   // disable autosave (set GLTSD bit)
   if (cfg->autosave==1){
-    if (scsiSetControlGLTSD(fd, 1, cfg->modese_len))
+    if (scsiSetControlGLTSD(cfg->scsidev, 1, cfg->modese_len))
       PrintOut(LOG_INFO,"Device: %s, could not disable autosave (set GLTSD bit).\n",device);
     else
       PrintOut(LOG_INFO,"Device: %s, disabled autosave (set GLTSD bit).\n",device);
@@ -1873,7 +1640,7 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
 
   // or enable autosave (clear GLTSD bit)
   if (cfg->autosave==2){
-    if (scsiSetControlGLTSD(fd, 0, cfg->modese_len))
+    if (scsiSetControlGLTSD(cfg->scsidev, 0, cfg->modese_len))
       PrintOut(LOG_INFO,"Device: %s, could not enable autosave (clear GLTSD bit).\n",device);
     else
       PrintOut(LOG_INFO,"Device: %s, enabled autosave (cleared GLTSD bit).\n",device);
@@ -1883,33 +1650,10 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
   PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device);
   
   // close file descriptor
-  CloseDevice(fd, device);
+  CloseDevice(cfg->scsidev, device);
   return 0;
 }
 
-// modified treatment of SCSI device behind SAT layer
-static int SCSIandSATDeviceScan(cfgfile *cfg, int scanning) {
-  int retval = SCSIDeviceScan(cfg, scanning);
-  cfg->WhichCheckDevice=1; // default SCSI device
-  
-  if (retval==SCSIFK_SAT) {
-    // found SATA device behind SAT translation layer	
-    cfg->controller_type=CONTROLLER_SAT;
-    cfg->WhichCheckDevice=0; // actually SATA device!
-    return ATADeviceScan(cfg, scanning);
-  }
-
-  if (retval==SCSIFK_MARVELL) {
-    // found SATA device behind Marvell controller
-    cfg->controller_type=CONTROLLER_MARVELL_SATA;
-    cfg->WhichCheckDevice=0; // actually SATA device!
-    return ATADeviceScan(cfg, scanning);
-  }
-
-  return retval; 
-}
-
-
 // We compare old and new values of the n'th attribute.  Note that n
 // is NOT the attribute ID number.. If (Normalized & Raw) equal,
 // then return 0, else nonzero.
@@ -2142,7 +1886,7 @@ void PrintTestSchedule(cfgfile **ATAandSCSIdevices){
     time_t testtime = now + seconds;
     for (i=0; i<numdev; i++) {
 	cfg = ATAandSCSIdevices[i];
-      for (t=0; t<(cfg->WhichCheckDevice==0?4:2); t++) {
+      for (t=0; t<(cfg->atadev?4:2); t++) {
         char testtype = "LSCO"[t];
         if (DoTestNow(cfg, testtype, testtime)) {
           // Report at most 5 tests of each type
@@ -2161,7 +1905,7 @@ void PrintTestSchedule(cfgfile **ATAandSCSIdevices){
   PrintOut(LOG_INFO, "\nTotals [%s - %s]:\n", datenow, date);
   for (i=0; i<numdev; i++) {
       cfg = ATAandSCSIdevices[i];
-    for (t=0; t<(cfg->WhichCheckDevice==0?4:2); t++) {
+    for (t=0; t<(cfg->atadev?4:2); t++) {
       PrintOut(LOG_INFO, "Device: %s, will do %3d test%s of type %c\n", cfg->name, testcnts[i][t],
         (testcnts[i][t]==1?"":"s"), "LSCO"[t]);
     }
@@ -2172,13 +1916,13 @@ void PrintTestSchedule(cfgfile **ATAandSCSIdevices){
 
 // Return zero on success, nonzero on failure. Perform offline (background)
 // short or long (extended) self test on given scsi device.
-int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) {
+int DoSCSISelfTest(scsi_device * device, cfgfile *cfg, char testtype) {
   int retval = 0;
   const char *testname = 0;
   const char *name = cfg->name;
   int inProgress;
 
-  if (scsiSelfTestInProgress(fd, &inProgress)) {
+  if (scsiSelfTestInProgress(device, &inProgress)) {
     PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name);
     cfg->testdata->not_cap_short=cfg->testdata->not_cap_long=1;
     return 1;
@@ -2193,11 +1937,11 @@ int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) {
   switch (testtype) {
   case 'S':
     testname = "Short Self";
-    retval = scsiSmartShortSelfTest(fd);
+    retval = scsiSmartShortSelfTest(device);
     break;
   case 'L':
     testname = "Long Self";
-    retval = scsiSmartExtendSelfTest(fd);
+    retval = scsiSmartExtendSelfTest(device);
     break;
   }
   // If we can't do the test, exit
@@ -2230,7 +1974,7 @@ int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) {
 
 // Do an offline immediate or self-test.  Return zero on success,
 // nonzero on failure.
-int DoATASelfTest(int fd, cfgfile *cfg, char testtype) {
+int DoATASelfTest(ata_device * device, cfgfile *cfg, char testtype) {
   
   struct ata_smart_values data;
   const char *testname = 0;
@@ -2238,7 +1982,7 @@ int DoATASelfTest(int fd, cfgfile *cfg, char testtype) {
   const char *name = cfg->name;
   
   // Read current smart data and check status/capability
-  if (ataReadSmartValues(fd, &data) || !(data.offline_data_collection_capability)) {
+  if (ataReadSmartValues(device, &data) || !(data.offline_data_collection_capability)) {
     PrintOut(LOG_CRIT, "Device: %s, not capable of Offline or Self-Testing.\n", name);
     return 1;
   }
@@ -2294,7 +2038,7 @@ int DoATASelfTest(int fd, cfgfile *cfg, char testtype) {
   }
 
   // else execute the test, and return status
-  if ((retval=smartcommandhandler(fd, IMMEDIATE_OFFLINE, dotest, NULL)))
+  if ((retval=smartcommandhandler(device, IMMEDIATE_OFFLINE, dotest, NULL)))
     PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname);
   else
     PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname);
@@ -2355,42 +2099,28 @@ static void CheckTemperature(cfgfile * cfg, unsigned char currtemp, unsigned cha
 }
 
 int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
-  int fd,i;
+  int i;
   /*const*/ char *name = cfg->name;
-  const char *mode = "ATA";
+  //char *mode="ATA";
   char testtype=0;
   
   // fix firmware bug if requested
   con->fixfirmwarebug=cfg->fixfirmwarebug;
-  con->controller_port=cfg->controller_port;
-  con->controller_type=cfg->controller_type;
-  con->controller_explicit=cfg->controller_explicit;
-  // Highpoint-specific data
-  con->hpt_data[0]=cfg->hpt_data[0];
-  con->hpt_data[1]=cfg->hpt_data[1];
-  con->hpt_data[2]=cfg->hpt_data[2];
 
   // If user has asked, test the email warning system
   if (cfg->mailwarn && cfg->mailwarn->emailtest)
     MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name);
 
-  if (cfg->controller_type == CONTROLLER_3WARE_9000_CHAR)
-    mode="ATA_3WARE_9000";
-  
-  if (cfg->controller_type == CONTROLLER_3WARE_678K_CHAR)
-    mode="ATA_3WARE_678K";
-
-  if (cfg->controller_type == CONTROLLER_ARECA)
-    mode="ATA_ARECA";
-
   // if we can't open device, fail gracefully rather than hard --
   // perhaps the next time around we'll be able to open it.  ATAPI
   // cd/dvd devices will hang awaiting media if O_NONBLOCK is not
   // given (see linux cdrom driver).
-  if ((fd=OpenDevice(name, mode, 0))<0){
+  if (!cfg->atadev->open()) {
+    PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", name, cfg->atadev->get_errmsg());
     MailWarning(cfg, 9, "Device: %s, unable to open device", name);
     return 1;
-  }
+  } else if (debugmode)
+    PrintOut(LOG_INFO,"Device: %s, opened ATA device\n", name);
 
   // if the user has asked, and device is capable (or we're not yet
   // sure) check whether a self test should be done now.
@@ -2415,13 +2145,13 @@ int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
   // alone if it is in idle or sleeping mode.  In this case check the
   // power mode and exit without check if needed
   if (cfg->powermode){
-    int dontcheck=0, powermode=ataCheckPowerMode(fd);
+    int dontcheck=0, powermode=ataCheckPowerMode(cfg->atadev);
     char *mode=NULL;
     if (0 <= powermode && powermode < 0xff) {
       // wait for possible spin up and check again
       int powermode2;
       sleep(5);
-      powermode2 = ataCheckPowerMode(fd);
+      powermode2 = ataCheckPowerMode(cfg->atadev);
       if (powermode2 > powermode)
         PrintOut(LOG_INFO, "Device: %s, CHECK POWER STATUS spins up disk (0x%02x -> 0x%02x)\n", name, powermode, powermode2);
       powermode = powermode2;
@@ -2462,7 +2192,7 @@ int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
     if (dontcheck){
       // but ignore powermode on scheduled selftest
       if (!testtype) {
-        CloseDevice(fd, name);
+        CloseDevice(cfg->atadev, name);
         if (!cfg->powerskipcnt && !cfg->powerquiet) // report first only and avoid waking up system disk
           PrintOut(LOG_INFO, "Device: %s, is in %s mode, suspending checks\n", name, mode);
         cfg->powerskipcnt++;
@@ -2481,7 +2211,7 @@ int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
 
   // check smart status
   if (cfg->smartcheck){
-    int status=ataSmartStatus2(fd);
+    int status=ataSmartStatus2(cfg->atadev);
     if (status==-1){
       PrintOut(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name);
       MailWarning(cfg, 5, "Device: %s, not capable of SMART self-check", name);
@@ -2499,7 +2229,7 @@ int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
     struct ata_smart_thresholds_pvt *thresh=cfg->smartthres;
     
     // Read current attribute values. *drive contains old values and thresholds
-    if (ataReadSmartValues(fd,&curval)){
+    if (ataReadSmartValues(cfg->atadev,&curval)){
       PrintOut(LOG_CRIT, "Device: %s, failed to read SMART Attribute Data\n", name);
       MailWarning(cfg, 6, "Device: %s, failed to read SMART Attribute Data", name);
     }
@@ -2607,7 +2337,7 @@ int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
   
   // check if number of selftest errors has increased (note: may also DECREASE)
   if (cfg->selftest)
-    CheckSelfTestLogs(cfg, SelfTestErrorCount(fd, name));
+    CheckSelfTestLogs(cfg, SelfTestErrorCount(cfg->atadev, name));
   
   // check if number of ATA errors has increased
   if (cfg->errorlog){
@@ -2615,7 +2345,7 @@ int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
     int newc,oldc=cfg->ataerrorcount;
 
     // new number of errors
-    newc=ATAErrorCount(fd, name);
+    newc=ATAErrorCount(cfg->atadev, name);
 
     // did command fail?
     if (newc<0)
@@ -2637,11 +2367,11 @@ int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
   
   // carry out scheduled self-test
   if (testtype)
-    DoATASelfTest(fd, cfg, testtype);
+    DoATASelfTest(cfg->atadev, cfg, testtype);
   
   // Don't leave device open -- the OS/user may want to access it
   // before the next smartd cycle!
-  CloseDevice(fd, name);
+  CloseDevice(cfg->atadev, name);
   return 0;
 }
 
@@ -2650,27 +2380,8 @@ int SCSICheckDevice(cfgfile *cfg, bool allow_selftests)
     UINT8 asc, ascq;
     UINT8 currenttemp;
     UINT8 triptemp;
-    int fd;
     /*const*/ char *name=cfg->name;
     const char *cp;
-    const char *mode = 0;
-
-    // should we try to register this as a SCSI device?
-    switch (cfg->controller_type) {
-    case CONTROLLER_CCISS:
-      mode="CCISS";
-      break;
-    case CONTROLLER_SCSI:
-    case CONTROLLER_UNKNOWN:
-      mode="SCSI";
-      break;
-    default:
-      return 1;
-    }
-
-    // pass user settings on to low-level SCSI commands
-    con->controller_port=cfg->controller_port;
-    con->controller_type=cfg->controller_type;
 
     // If the user has asked for it, test the email warning system
     if (cfg->mailwarn && cfg->mailwarn->emailtest)
@@ -2678,8 +2389,8 @@ int SCSICheckDevice(cfgfile *cfg, bool allow_selftests)
 
     // if we can't open device, fail gracefully rather than hard --
     // perhaps the next time around we'll be able to open it
-    if ((fd=OpenDevice(name, mode, 0))<0) {
-      // Lack of PrintOut() here is intentional!
+    if (!cfg->scsidev->open()) {
+      PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", name, cfg->scsidev->get_errmsg());
       MailWarning(cfg, 9, "Device: %s, unable to open device", name);
       return 1;
     } else if (debugmode)
@@ -2688,7 +2399,7 @@ int SCSICheckDevice(cfgfile *cfg, bool allow_selftests)
     asc = 0;
     ascq = 0;
     if (! cfg->SuppressReport) {
-        if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
+        if (scsiCheckIE(cfg->scsidev, cfg->SmartPageSupported, cfg->TempPageSupported,
                         &asc, &ascq, &currenttemp, &triptemp)) {
             PrintOut(LOG_INFO, "Device: %s, failed to read SMART values\n",
                       name);
@@ -2713,17 +2424,17 @@ int SCSICheckDevice(cfgfile *cfg, bool allow_selftests)
 
     // check if number of selftest errors has increased (note: may also DECREASE)
     if (cfg->selftest)
-      CheckSelfTestLogs(cfg, scsiCountFailedSelfTests(fd, 0));
+      CheckSelfTestLogs(cfg, scsiCountFailedSelfTests(cfg->scsidev, 0));
     
     if (allow_selftests && cfg->testdata) {
       // long (extended) background test
       if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0)
-        DoSCSISelfTest(fd, cfg, 'L');
+        DoSCSISelfTest(cfg->scsidev, cfg, 'L');
       // short background test
       else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0)
-        DoSCSISelfTest(fd, cfg, 'S');
+        DoSCSISelfTest(cfg->scsidev, cfg, 'S');
     }
-    CloseDevice(fd, name);
+    CloseDevice(cfg->scsidev, name);
     return 0;
 }
 
@@ -2732,7 +2443,7 @@ void CheckDevicesOnce(cfgfile **ATAandSCSIdevices, bool allow_selftests){
   int i;
   
   for (i=0; i<numdevata+numdevscsi; i++) {
-      if (ATAandSCSIdevices[i]->WhichCheckDevice==1)
+      if (ATAandSCSIdevices[i]->scsidev)
 	  SCSICheckDevice(ATAandSCSIdevices[i], allow_selftests);
       else
 	  ATACheckDevice(ATAandSCSIdevices[i], allow_selftests);
@@ -2874,7 +2585,7 @@ void printoutvaliddirectiveargs(int priority, char d) {
     PrintOut(priority, "valid_regular_expression");
     break;
   case 'd':
-    PrintOut(priority, "ata, scsi, marvell, removable, sat, 3ware,N, areca,N, hpt,L/M/N");
+    PrintOut(priority, "%s", smi()->get_valid_dev_types_str());
     break;
   case 'T':
     PrintOut(priority, "normal, permissive");
@@ -3048,170 +2759,12 @@ int ParseToken(char *token,cfgfile *cfg){
     break;
   case 'd':
     // specify the device type
-    cfg->controller_explicit = 1;
     if ((arg = strtok(NULL, delim)) == NULL) {
       missingarg = 1;
-    } else if (!strcmp(arg, "ata")) {
-      cfg->controller_port = 0;
-      cfg->controller_type = CONTROLLER_ATA;
-    } else if (!strcmp(arg, "scsi")) {
-      cfg->controller_port =0;
-      cfg->controller_type = CONTROLLER_SCSI;
-    } else if (!strcmp(arg, "marvell")) {
-      cfg->controller_port =0;
-      cfg->controller_type = CONTROLLER_MARVELL_SATA;
-    } else if (!strncmp(arg, "usbcypress", 10)) {
-      cfg->controller_type = CONTROLLER_USBCYPRESS;
-      cfg->controller_port = 0;
-      cfg->usbcypress_signature = 0x24;
-      if (strlen(arg) > 10) {
-        int k;
-        const char * cp = strchr(arg, ',');
-        if (cp && (1 == sscanf(cp + 1, "0x%x", &k)) &&
-            ((0 <= k) && (0xff >= k)))
-          cfg->usbcypress_signature = k;
-        else {
-          PrintOut(LOG_CRIT, "Option '-d usbcypress,<n>' requires <n> to be "
-              "an hexadecimal number between 0x0 and 0xff\n");
-          badarg = 1;
-        }
-      }
-    } else if (!strncmp(arg, "sat", 3)) {
-      cfg->controller_type = CONTROLLER_SAT;
-      cfg->controller_port = 0;
-      cfg->satpassthrulen = 0;
-      if (strlen(arg) > 3) {
-        int k;
-        const char * cp;
-
-        cp = strchr(arg, ',');
-        if (cp && (1 == sscanf(cp + 1, "%d", &k)) &&
-            ((0 == k) || (12 == k) || (16 == k)))
-          cfg->satpassthrulen = k;
-        else {
-          PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
-                   "'-d sat,<n>' requires <n> to be 0, 12 or 16\n",
-                   configfile, lineno, name);
-          badarg = 1;
-        }
-      }
-    } else if (!strncmp(arg, "hpt", 3)){
-      unsigned char i, slash = 0;
-      cfg->hpt_data[0] = 0;
-      cfg->hpt_data[1] = 0;
-      cfg->hpt_data[2] = 0;
-      cfg->controller_type = CONTROLLER_HPT;
-      for (i=4; i < strlen(arg); i++) {
-        if(arg[i] == '/') {
-          slash++;
-          if(slash == 3) {
-            PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
-                     "'-d hpt,L/M/N' supports 2-3 items\n",
-                     configfile, lineno, name);
-            badarg = TRUE;
-            break;
-          }
-        }
-        else if ((arg[i])>='0' && (arg[i])<='9') {
-          if (cfg->hpt_data[slash]>1) { /* hpt_data[x] max 19 */
-            badarg = TRUE;
-            break;
-          }
-          cfg->hpt_data[slash] = cfg->hpt_data[slash]*10 + arg[i] - '0';
-        }
-        else {
-          badarg = TRUE;
-          break;
-        }
-      }
-      if ( slash == 0 ) {
-        badarg = TRUE;
-      } else if (badarg != TRUE) {
-        if (cfg->hpt_data[0]==0 || cfg->hpt_data[0]>8){
-           PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
-                    "'-d hpt,L/M/N' no/invalid controller id L supplied\n",
-                    configfile, lineno, name);
-           badarg = TRUE;
-        }
-        if (cfg->hpt_data[1]==0 || cfg->hpt_data[1]>8){
-          PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
-                   "'-d hpt,L/M/N' no/invalid channel number M supplied\n",
-                   configfile, lineno, name);
-          badarg = TRUE;
-        }
-        if (slash==2){
-          if (cfg->hpt_data[2]==0 || cfg->hpt_data[2]>15){
-            PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
-                     "'-d hpt,L/M/N' no/invalid pmport number N supplied\n",
-                     configfile, lineno, name);
-            badarg = TRUE;
-          }
-        } else { /* no pmport device */
-          cfg->hpt_data[2]=1;
-        }
-      }
     } else if (!strcmp(arg, "removable")) {
       cfg->removable = 1;
     } else {
-      // look 3ware,N areca,N, cciss,N, RAID device
-      int i;
-      char *s;
-      
-      // make a copy of the string to mess with
-      if (!(s = strdup(arg))) {
-        PrintOut(LOG_CRIT,
-                 "No memory to copy argument to -d option - exiting\n");
-        EXIT(EXIT_NOMEM);
-      } else if (!strncmp(s,"3ware,",6)) {
-          if (split_report_arg2(s, &i)){
-              PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N requires N integer\n",
-               	       configfile, lineno, name);
-              badarg=1;
-          } else if ( i<0 || i>127) {
-              PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N (N=%d) must have 0 <= N <= 127\n",
-                       configfile, lineno, name, i);
-              badarg=1;
-          } else {
-	      // determine type of escalade device from name of device
-	      cfg->controller_type = guess_device_type(name);
-	      if (cfg->controller_type!=CONTROLLER_3WARE_9000_CHAR && cfg->controller_type!=CONTROLLER_3WARE_678K_CHAR)
-	           cfg->controller_type=CONTROLLER_3WARE_678K;
-	    
-              // NOTE: controller_port == disk number + 1
-              cfg->controller_port = i+1;
-          }
-      } else if (!strncmp(s,"cciss,",6)) {
-          if (split_report_arg2(s, &i)){
-              PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d cciss,N requires N integer\n",
-               	       configfile, lineno, name);
-              badarg=1;
-          } else if ( i<0 || i>127) {
-              PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d cciss,N (N=%d) must have 0 <= N <= 127\n",
-                       configfile, lineno, name, i);
-              badarg=1;
-          } else {
-              // NOTE: controller_port == disk number + 1
-              cfg->controller_type = CONTROLLER_CCISS;
-              cfg->controller_port = i+1;
-          }
-      } else if (!strncmp(s,"areca,",6)) {
-          if (split_report_arg2(s, &i)){
-              PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d areca,N requires N to be a positive integer\n",
-               	       configfile, lineno, name);
-              badarg=1;
-          } else if ( i<1 || i>24) {
-              PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d areca,N (N=%d) must have 1 <= N <= 24\n",
-                       configfile, lineno, name, i);
-              badarg=1;
-          } else {
-              // NOTE: controller_port == disk number + 1
-              cfg->controller_type = CONTROLLER_ARECA;
-              cfg->controller_port = i+1;
-          }
-      } else {
-          badarg=1;
-      }
-      s=CheckFree(s, __LINE__,filenameandversion);
+      cfg->dev_type = CustomStrDup(arg, 1, __LINE__,filenameandversion);
     }
     break;
   case 'F':
@@ -3602,12 +3155,7 @@ int ParseConfigLine(int entry, int lineno,char *line){
 
   // Store line number, and by default check for both device types.
   cfg->lineno=lineno;
-  
-  // Try and recognize if a IDE or SCSI device.  These can be
-  // overwritten by configuration file directives.
-  if (cfg->controller_type==CONTROLLER_UNKNOWN)
-    cfg->controller_type = guess_device_type(cfg->name);
-  
+
   // parse tokens one at a time from the file.
   while ((token=strtok(NULL,delim))){
     int retval=ParseToken(token,cfg);
@@ -3630,59 +3178,6 @@ int ParseConfigLine(int entry, int lineno,char *line){
     }
   }
   
-  // If we found 3ware/cciss/areca controller, then modify device name by adding a SPACE
-  if (cfg->controller_port) {
-    const int addlen = sizeof(" [3ware_disk_127]")-1;
-    int len = strlen(cfg->name) + addlen + 1;
-    char *newname;
-    
-    if (devscan){
-      PrintOut(LOG_CRIT, "smartd: can not scan for 3ware/cciss/areca devices (line %d of file %s)\n",
-               lineno, configfile);
-      return -2;
-    }
-    
-    if (!(newname=(char *)calloc(len,1))) {
-      PrintOut(LOG_INFO,"No memory to parse file: %s line %d, %s\n", configfile, lineno, strerror(errno));
-      EXIT(EXIT_NOMEM);
-    }
-    
-    // Make new device name by adding a space then RAID disk number
-    if (cfg->controller_type == CONTROLLER_CCISS)
-	    snprintf(newname, len, "%s [cciss_disk_%02d]", cfg->name, cfg->controller_port - 1);
-    else if (cfg->controller_type == CONTROLLER_ARECA)
-	    snprintf(newname, len, "%s [areca_disk_%02d]", cfg->name, cfg->controller_port - 1);
-    else
-	    snprintf(newname, len, "%s [3ware_disk_%03d]", cfg->name, cfg->controller_port - 1);
-  
-    cfg->name=CheckFree(cfg->name, __LINE__,filenameandversion);
-    cfg->name=newname;
-    bytes += addlen;
-  }
-
-  if (cfg->hpt_data[0]) {
-      int len=17+strlen(cfg->name);
-      char *newname;
-
-      if (devscan){
-        PrintOut(LOG_CRIT, "smartd: can not scan for highpoint devices (line %d of file %s)\n",
-                 lineno, configfile);
-        return -2;
-      }
-
-      if (!(newname=(char *)calloc(len,1))) {
-        PrintOut(LOG_INFO,"No memory to parse file: %s line %d, %s\n", configfile, lineno, strerror(errno));
-        EXIT(EXIT_NOMEM);
-      }
-
-      // Make new device name by adding a space then RAID disk number
-      snprintf(newname, len, "%s [hpt_%d/%d/%d]", cfg->name, cfg->hpt_data[0],
-               cfg->hpt_data[1], cfg->hpt_data[2]);
-      cfg->name=CheckFree(cfg->name, __LINE__,filenameandversion);
-      cfg->name=newname;
-      bytes+=16;
-  }
-
   // If NO monitoring directives are set, then set all of them.
   if (!(cfg->smartcheck || cfg->usagefailed || cfg->prefail  || 
         cfg->usage      || cfg->selftest    || cfg->errorlog ||  
@@ -4182,33 +3677,31 @@ void ParseOpts(int argc, char **argv){
 // SCANDIRECTIVE Directive was found.  It makes entries for device
 // names returned by make_device_names() in os_OSNAME.c
 int MakeConfigEntries(const char *type, int start){
-  int i;
-  int num;
-  char** devlist = NULL;
   cfgfile *first=cfgentries[0],*cfg=first;
 
   // Hack! This is to make DEVICESCAN work on ATA devices behind
   // a SCSI to ATA Translation (SAT) Layer.
   // This will work on a general OS if the way that SAT devices are
   // named is the same as SCSI devices.
+  // TODO:
   // The BETTER solution is to modify make_device_names to recognize
   // the additional type "SAT".  This requires changing os_*.cpp.
 
   const char *basetype = type;
-  if (!strcmp(type,"SAT") )
-    basetype = "SCSI";
+  if (!strcmp(type,"sat"))
+    basetype = "scsi";
 
   // make list of devices
-  if ((num=make_device_names(&devlist,basetype))<0)
+  smart_device_list devlist;
+  if (!smi()->scan_smart_devices(devlist, basetype))
     PrintOut(LOG_CRIT,"Problem creating device name scan list\n");
   
   // if no devices, or error constructing list, return
-  if (num<=0)
+  if (devlist.size() <= 0)
     return 0;
-  
+
   // loop over entries to create
-  for (i=0; i<num; i++){
-    
+  for (int i = 0; i < devlist.size(); i++) {
     // make storage and copy for all but first entry
     if (start+i) {
       // allocate more storage if needed
@@ -4218,28 +3711,20 @@ int MakeConfigEntries(const char *type, int start){
     }
 
     // ATA or SCSI?
-    if (!strcmp(type,"ATA") )
-      cfg->controller_type = CONTROLLER_ATA;
-    if (!strcmp(type,"SCSI") ) 
-      cfg->controller_type = CONTROLLER_SCSI;
-    if (!strcmp(type,"SAT") )
-      cfg->controller_type = CONTROLLER_SAT;
+    cfg->dev_type = CustomStrDup(type, 1, __LINE__,filenameandversion);
     
     // remove device name, if it's there, and put in correct one
     cfg->name=FreeNonZero(cfg->name, -1,__LINE__,filenameandversion);
-    // save pointer to the device name created within
-    // make_device_names
-    cfg->name=devlist[i];
+
+    // save pointer to the device object created within scan_smart_devices
+    smart_device * dev = devlist.detach(i);
+
+    cfg->device = dev;
+    // cfg->atadev and cfg->scsidev set after autodetect_open().
+    cfg->name = CustomStrDup(dev->get_info_name(), 1, __LINE__,filenameandversion);
   }
   
-  // If needed, free memory used for devlist: pointers now in
-  // cfgentries[]->names.  If num==0 we never get to this point, but
-  // that's OK.  If we realloc()d the array length in
-  // make_device_names() that was ALREADY equivalent to calling
-  // free().
-  devlist = FreeNonZero(devlist,(sizeof (char*) * num),__LINE__, filenameandversion);
-  
-  return num;
+  return devlist.size();
 }
  
 void CanNotRegister(const char *name, const char *type, int line, int scandirective){
@@ -4288,26 +3773,15 @@ int ReadOrMakeConfigEntries(int *scanning){
     // that were set
     cfgfile *first=cfgentries[0];
 
-    // By default scan for ATA, SCSI and SAT devices
-    int doata=1, doscsi=1, dosat=1;
-
-    if (first->controller_type==CONTROLLER_SCSI) {
-      doata = 0;
-      dosat = 0;
-    } else if (first->controller_type==CONTROLLER_ATA) {
-      doscsi = 0;
-      dosat = 0;
-    } else if (first->controller_type==CONTROLLER_SAT) {
-      doata = 0;
-      doscsi = 0;
-    } else
-      dosat = 0;
-// The code in this block has been neutered by D. Gilbert
-// on 20070226. smartd can't cope ATA disk behind a SAT
-// transport seamlessly _without_ a bigger restructuring
-// of smartd than this code tried. It made ATA disks
-// behind a SAT interface automatically detected only by
-// killing support for real SCSI disks. Sorry, no.
+    bool doata = false, doscsi = false, dosat = false;
+    if (!first->dev_type)
+      doata = doscsi = true;
+    else if (!strcmp(first->dev_type, "ata"))
+      doata = true;
+    else if (!strcmp(first->dev_type, "scsi"))
+      doscsi = true;
+    else if (!strcmp(first->dev_type, "sat"))
+      dosat = true;
 
     *scanning=1;
     
@@ -4318,12 +3792,12 @@ int ReadOrMakeConfigEntries(int *scanning){
     
     // make config list of ATA devices to search for
     if (doata)
-      entries+=MakeConfigEntries("ATA", entries);
+      entries+=MakeConfigEntries("ata", entries);
     // make config list of SCSI devices to search for
     if (doscsi)
-      entries+=MakeConfigEntries("SCSI", entries);
+      entries+=MakeConfigEntries("scsi", entries);
     if (dosat)
-      entries+=MakeConfigEntries("SAT", entries);
+      entries+=MakeConfigEntries("sat", entries);
 
     // warn user if scan table found no devices
     if (!entries) {
@@ -4357,28 +3831,71 @@ void RegisterDevices(int scanning){
     // skip any NULL entries (holes)
     if (!ent)
       continue;
-    
+
+    // get device of appropriate type
+    // (may be already set by MakeConfigEntries)
+    smart_device * dev = ent->device;
+    if (!dev) {
+      dev = smi()->get_smart_device(ent->name, ent->dev_type);
+      if (!dev) {
+        if (!ent->dev_type)
+          PrintOut(LOG_INFO,"Device: %s, unable to autodetect device type\n",ent->name);
+        else
+          PrintOut(LOG_INFO,"Device: %s, unsupported device type '%s'\n",ent->name, ent->dev_type);
+        continue;
+      }
+    }
+
+    // Save old info
+    smart_device::device_info oldinfo = dev->get_info();
+
+    // Open with autodetect support, may return 'better' device
+    dev = dev->autodetect_open();
+    ent->device = dev; // old entry may be no longer valid
+
+    // Report if type has changed
+    if (/* ent->dev_type && */ oldinfo.dev_type != dev->get_dev_type())
+      PrintOut(LOG_INFO,"Device: %s, type changed from '%s' to '%s'\n",
+        ent->name, oldinfo.dev_type.c_str(), dev->get_dev_type());
+
+    if (!dev->is_open()) {
+      // For linux+devfs, a nonexistent device gives a strange error
+      // message.  This makes the error message a bit more sensible.
+      // If no debug and scanning - don't print errors
+      if (debugmode || !scanning)
+        PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", dev->get_info_name(), dev->get_errmsg());
+      delete dev; ent->device = 0;
+      continue;
+    }
+
+    // Update informal name
+    ent->name = FreeNonZero(ent->name, -1, __LINE__, filenameandversion);
+    ent->name = CustomStrDup(dev->get_info_name(), 1, __LINE__,filenameandversion);
+    PrintOut(LOG_INFO, "Device: %s, opened\n", ent->name);
+
     // register ATA devices
-    if (ent->controller_type!=CONTROLLER_SCSI && ent->controller_type!=CONTROLLER_CCISS){
-      if (ATADeviceScan(ent, scanning))
+    if (dev->is_ata()){
+      ent->atadev = dev->to_ata(); ent->scsidev = 0;
+      if (ATADeviceScan(ent)) {
+        delete ent->device; ent->device = ent->atadev = 0;
         CanNotRegister(ent->name, "ATA", ent->lineno, scanning);
+      }
       else {
         // move onto the list of ata devices
         cfgentries[i]=NULL;
         while (numdevata+numdevscsi>=ATAandSCSIdevlist_max)
 	    ATAandSCSIdevlist=AllocateMoreSpace(ATAandSCSIdevlist, &ATAandSCSIdevlist_max, "ATA and SCSI devices");
-	ent->WhichCheckDevice=0;
         ATAandSCSIdevlist[numdevscsi+numdevata]=ent;
         numdevata++;
       }
     }
     
-    // then register SCSI devices
-    if (ent->controller_type==CONTROLLER_SCSI || ent->controller_type==CONTROLLER_CCISS || 
-        ent->controller_type==CONTROLLER_UNKNOWN){
+    // or register SCSI devices
+    else if (dev->is_scsi()){
+      ent->scsidev = dev->to_scsi(); ent->atadev = 0;
       int retscsi=0;
 
-#if SCSITIMEOUT
+#if 0 // SCSITIMEOUT // TODO: Handle in dev_legacy.cpp or os_*.cpp
       struct sigaction alarmAction, defaultaction;
 
       // Set up an alarm handler to catch USB devices that hang on
@@ -4388,7 +3905,7 @@ void RegisterDevices(int scanning){
       if (sigaction(SIGALRM, &alarmAction, &defaultaction)) {
         // if we can't set timeout, just scan device
         PrintOut(LOG_CRIT, "Unable to initialize SCSI timeout mechanism.\n");
-        retscsi=SCSIandSATDeviceScan(ent, scanning);
+        retscsi=SCSIDeviceScan(ent);
       }
       else {
         // prepare return point in case of bad SCSI device
@@ -4398,7 +3915,7 @@ void RegisterDevices(int scanning){
         else {
         // Set alarm, make SCSI call, reset alarm
           alarm(SCSITIMEOUT);
-          retscsi=SCSIandSATDeviceScan(ent, scanning);
+          retscsi=SCSIDeviceScan(ent);
           alarm(0);
         }
         if (sigaction(SIGALRM, &defaultaction, NULL)){
@@ -4406,13 +3923,14 @@ void RegisterDevices(int scanning){
         }
       }
 #else
-      retscsi=SCSIandSATDeviceScan(ent, scanning);
+      retscsi=SCSIDeviceScan(ent);
 #endif   
 
       // Now scan SCSI device...
       if (retscsi){
         if (retscsi<0)
           PrintOut(LOG_CRIT, "Device %s timed out (poorly-implemented USB device?)\n", ent->name);
+        delete ent->device; ent->device = ent->scsidev = 0;
         CanNotRegister(ent->name, "SCSI", ent->lineno, scanning);
       }
       else {
@@ -4447,6 +3965,11 @@ void RegisterDevices(int scanning){
 // Main program without exception handling
 int main_worker(int argc, char **argv)
 {
+  // Initialize interface
+  smart_interface::init();
+  if (!smi())
+    return 1;
+
   // external control variables for ATA disks
   smartmonctrl control;
 
diff --git a/sm5/smartd.h b/sm5/smartd.h
index 5afbb0cdf1f2eab17030c4d568ec0b3e9b9be349..04061c79ab52d4a29b416bc114df6c145246a7eb 100644
--- a/sm5/smartd.h
+++ b/sm5/smartd.h
@@ -32,7 +32,7 @@
 
 
 #ifndef SMARTD_H_CVSID
-#define SMARTD_H_CVSID "$Id: smartd.h,v 1.88 2008/06/15 21:23:12 mat-c Exp $\n"
+#define SMARTD_H_CVSID "$Id: smartd.h,v 1.89 2008/07/25 21:16:00 chrfranke Exp $\n"
 #endif
 
 // Configuration file
@@ -168,12 +168,10 @@ typedef struct configfile_s {
   // REGISTERED AND WE LEARN ITS CAPABILITIES.
   int lineno;                             // Line number of entry in file
   char *name;                             // Device name (+ optional [3ware_disk_XX])
-  unsigned char controller_explicit;      // Controller (device) type has been specified explicitly
-  unsigned char controller_type;          // Controller type, ATA/SCSI/SAT/3Ware/(more to come)
-  unsigned char controller_port;          // 1 + (disk number in controller). 0 means controller only handles one disk.
-  unsigned char hpt_data[3];              // combined controller/channle/pmport for highpoint rocketraid controller
-  unsigned char satpassthrulen;           // length of SAT ata pass through scsi commands (12, 16 or 0 (platform choice))
-  unsigned char usbcypress_signature;     // usbcypress scsi command
+  char *dev_type;                         // Device type argument from -d directive, 0 if none
+  smart_device * device;                  // Device object
+  ata_device * atadev;                    // downcast to ATA interface or 0
+  scsi_device * scsidev;                  // downcast to SCSI interface or 0
   char smartcheck;                        // Check SMART status
   char usagefailed;                       // Check for failed Usage Attributes
   char prefail;                           // Track changes in Prefail Attributes
@@ -234,9 +232,6 @@ typedef struct configfile_s {
   struct ata_smart_values *smartval;       // Pointer to SMART data
   struct ata_smart_thresholds_pvt *smartthres; // Pointer to SMART thresholds
 
-  // Added to distinguish ATA and SCSI entries on list
-  int WhichCheckDevice;
-
 } cfgfile;
 
 
@@ -301,16 +296,6 @@ export NJAMD_TRACE_LIBS=1
 // if cfg->pending has this value, dont' monitor
 #define DONT_MONITOR_UNC (256*OFF_UNC_DEFAULT+CUR_UNC_DEFAULT)
 
-// Some return values from SCSIFilterKnown(), used to detect known
-// device types hidden behind SCSI devices.
-
-#define SCSIFK_FAILED   -1
-#define SCSIFK_NORMAL    0
-#define SCSIFK_3WARE    11
-#define SCSIFK_SAT      12
-#define SCSIFK_MARVELL  13
-#define SCSIFK_USBCYPRESS  14
-
 // Make additions BEFORE this line.  The line is the end of
 // double-inclusion protection and should remain the final line.
 #endif  // #ifndef SMARTD_H_
diff --git a/sm5/utility.cpp b/sm5/utility.cpp
index 6eedc6229e07da732d090c707875b779c6f6a0d1..a76a2c1aad3f4af30adbe56a2650681105e44c87 100644
--- a/sm5/utility.cpp
+++ b/sm5/utility.cpp
@@ -44,8 +44,11 @@
 #include "int64.h"
 #include "utility.h"
 
+#include "atacmds.h"
+#include "dev_interface.h"
+
 // Any local header files should be represented by a CVSIDX just below.
-const char* utility_c_cvsid="$Id: utility.cpp,v 1.68 2008/04/27 16:30:09 chrfranke Exp $"
+const char* utility_c_cvsid="$Id: utility.cpp,v 1.69 2008/07/25 21:16:00 chrfranke Exp $"
 CONFIG_H_CVSID INT64_H_CVSID UTILITY_H_CVSID;
 
 const char * packet_types[] = {
@@ -82,16 +85,11 @@ unsigned char debugmode = 0;
 // Make version information string
 const char *format_version_info(const char *progname)
 {
-#ifdef HAVE_GET_OS_VERSION_STR
-  const char * osver = get_os_version_str();
-#else
-  const char * osver = SMARTMONTOOLS_BUILD_HOST;
-#endif
   static char info[200];
   snprintf(info, sizeof(info),
     "%s %s %s [%s] %s\n"
     "Copyright (C) 2002-8 by Bruce Allen, http://smartmontools.sourceforge.net\n",
-    progname, PACKAGE_VERSION, SMARTMONTOOLS_CVS_DATE_TIME, osver, BUILD_INFO
+    progname, PACKAGE_VERSION, SMARTMONTOOLS_CVS_DATE_TIME, smi()->get_os_version_str(), BUILD_INFO
   );
   return info;
 }
@@ -690,6 +688,24 @@ void MsecToText(unsigned int msec, char *txt){
   return;
 }
 
+// return (v)sprintf() formatted std::string
+
+std::string vstrprintf(const char * fmt, va_list ap)
+{
+  char buf[512];
+  vsnprintf(buf, sizeof(buf), fmt, ap);
+  buf[sizeof(buf)-1] = 0;
+  return buf;
+}
+
+std::string strprintf(const char * fmt, ...)
+{
+  va_list ap; va_start(ap, fmt);
+  std::string str = vstrprintf(fmt, ap);
+  va_end(ap);
+  return str;
+}
+
 
 #ifndef HAVE_WORKING_SNPRINTF
 // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
diff --git a/sm5/utility.h b/sm5/utility.h
index 6073614d01b1d28784815d404179e5990e7ee67a..58b44d57567a6f96121fb191850ad2b083a9977c 100644
--- a/sm5/utility.h
+++ b/sm5/utility.h
@@ -25,19 +25,30 @@
 #ifndef UTILITY_H_
 #define UTILITY_H_
 
-#define UTILITY_H_CVSID "$Id: utility.h,v 1.56 2008/06/12 21:46:32 ballen4705 Exp $\n"
+#define UTILITY_H_CVSID "$Id: utility.h,v 1.57 2008/07/25 21:16:00 chrfranke Exp $\n"
 
 #include <time.h>
 #include <sys/types.h> // for regex.h (according to POSIX)
 #include <regex.h>
+#include <stdarg.h>
+#include <string>
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)  /**/
+#endif
 
 // Make version information string
 const char *format_version_info(const char *prog_name);
 
+// return (v)sprintf() formated std::string
+std::string strprintf(const char * fmt, ...)
+    __attribute__ ((format (printf, 1, 2)));
+std::string vstrprintf(const char * fmt, va_list ap);
+
 #ifndef HAVE_WORKING_SNPRINTF
 // Substitute by safe replacement functions
-#include <stdarg.h>
 int safe_snprintf(char *buf, int size, const char *fmt, ...);
+    __attribute__ ((format (printf, 3, 4)));
 int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap);
 #define snprintf  safe_snprintf
 #define vsnprintf safe_vsnprintf
@@ -59,9 +70,6 @@ void printone(char *block, const char *cvsid);
 // although the prototype is given here in utility.h, the function
 // itself is defined differently in smartctl and smartd.  So the
 // function definition(s) are in smartd.c and in smartctl.c.
-#ifndef __GNUC__
-#define __attribute__(x)      /* nothing */
-#endif
 void pout(const char *fmt, ...)  
      __attribute__ ((format (printf, 1, 2)));
 
@@ -92,11 +100,13 @@ int split_selective_arg(char *s, uint64_t *start, uint64_t *stop, int *mode);
 // Guess device type (ata or scsi) based on device name 
 // Guessing will now use Controller Type defines below
 
-int guess_device_type(const char * dev_name);
+// Moved to C++ interface
+//int guess_device_type(const char * dev_name);
 
 // Create and return the list of devices to probe automatically
 // if the DEVICESCAN option is in the smartd config file
-int make_device_names (char ***devlist, const char* name);
+// Moved to C++ interface
+//int make_device_names (char ***devlist, const char* name);
 
 // Replacement for exit(status)
 // (exit is not compatible with C++ destructors)
@@ -145,14 +155,15 @@ int isbigendian();
 // the ATA standard for packet devices to define the device type.
 const char *packetdevicetype(int type);
 
-int deviceopen(const char *pathname, char *type);
+// Moved to C++ interface
+//int deviceopen(const char *pathname, char *type);
 
-int deviceclose(int fd);
+//int deviceclose(int fd);
 
 // Optional functions of os_*.c
 #ifdef HAVE_GET_OS_VERSION_STR
 // Return build host and OS version as static string
-const char * get_os_version_str(void);
+//const char * get_os_version_str(void);
 #endif
 
 // returns 1 if any of the n bytes are nonzero, else zero.