From f801b72815b6bab22499beecf02ddc3f6e0e8e29 Mon Sep 17 00:00:00 2001
From: likewise <likewise@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Fri, 13 Aug 2004 00:04:39 +0000
Subject: [PATCH] Added SMART support for Marvell's SATA controllers. See
 Request ID 1008374.

git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/branches/RELEASE_5_1_14_MARVELL_SATA_BRANCH@1853 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 sm5/atacmds.c   | 160 ++++++++++++++++++++++-
 sm5/extern.h    |   3 +-
 sm5/scsicmds.h  |   6 +-
 sm5/scsiprint.c |  56 ++++++--
 sm5/smartctl.c  |  13 +-
 sm5/smartctl.h  |   5 +-
 sm5/smartd.8    |  16 ++-
 sm5/smartd.c    | 338 +++++++++++++++++++++++++++++++++++++++++++++---
 sm5/smartd.h    |   8 +-
 9 files changed, 563 insertions(+), 42 deletions(-)

diff --git a/sm5/atacmds.c b/sm5/atacmds.c
index f449cdc06..4f83b51af 100644
--- a/sm5/atacmds.c
+++ b/sm5/atacmds.c
@@ -29,10 +29,11 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include "atacmds.h"
+#include "scsicmds.h"
 #include "utility.h"
 #include "extern.h"
 
-const char *atacmds_c_cvsid="$Id: atacmds.c,v 1.100 2003/06/12 12:18:53 ballen4705 Exp $" ATACMDS_H_CVSID EXTERN_H_CVSID UTILITY_H_CVSID;
+const char *atacmds_c_cvsid="$Id: atacmds.c,v 1.100.2.1 2004/08/13 00:04:39 likewise Exp $" ATACMDS_H_CVSID EXTERN_H_CVSID UTILITY_H_CVSID;
 
 // for passing global control variables
 extern smartmonctrl *con;
@@ -446,7 +447,7 @@ char *create_vendor_attribute_arg_list(void){
 
 int os_specific_handler(int device, smart_command_set command, int select, char *data){
   unsigned char buff[STRANGE_BUFFER_LENGTH];
-  int retval, copydata=0;
+  int retval = 0, copydata = 0;
 
   // See struct hd_drive_cmd_hdr in hdreg.h
   // buff[0]: ATA COMMAND CODE REGISTER
@@ -562,6 +563,151 @@ int os_specific_handler(int device, smart_command_set command, int select, char
   return 0; 
 }
 
+// PURPOSE
+//   This is an interface routine meant to isolate the OS dependent
+//   parts of the code, and to provide a debugging interface.  Each
+//   different port and OS needs to provide it's own interface.  This
+//   is the linux one.
+// DESCRIPTION
+//   The function sends SCSI vendor-specific command 0xC (6 bytes long)followed
+//   by the data buffer representing ATA registers:
+//      buff[0]: ATA COMMAND CODE REGISTER
+//      buff[1]: ATA SECTOR NUMBER REGISTER
+//      buff[2]: ATA FEATURES REGISTER
+//      buff[3]: ATA SECTOR COUNT REGISTER
+//  (See struct hd_drive_cmd_hdr in hdreg.h)
+//
+// ARGUMENTS
+//   device: is the file descriptor provided by open()
+//   command: defines the different operations.
+//   select: additional input data if needed (which log, which type of
+//           self-test).
+//   data:   location to write output data, if needed (512 bytes).
+//   Note: not all commands use all arguments.
+// RETURN VALUES
+//  -1 if the command failed
+//   0 if the command succeeded,
+//   STATUS_CHECK routine: 
+//  -1 if the command failed
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+
+int mvsata_os_specific_handler(int device, 
+                               smart_command_set command, 
+                               int select, 
+                               char *data)
+{  
+  typedef struct 
+  {  
+    int  inlen;
+    int  outlen;
+    char cmd[540];
+  } mvsata_scsi_cmd;
+
+  int copydata = 0;
+  mvsata_scsi_cmd  smart_command;
+  unsigned char *buff = &smart_command.cmd[6];
+  // See struct hd_drive_cmd_hdr in hdreg.h
+  // buff[0]: ATA COMMAND CODE REGISTER
+  // buff[1]: ATA SECTOR NUMBER REGISTER
+  // buff[2]: ATA FEATURES REGISTER
+  // buff[3]: ATA SECTOR COUNT REGISTER
+  
+  // clear out buff.  Large enough for HDIO_DRIVE_CMD (4+512 bytes)
+  memset(&smart_command, 0, sizeof(smart_command));
+  smart_command.inlen = 540;
+  smart_command.outlen = 540;
+  smart_command.cmd[0] = 0xC;  //Vendor-specific code
+  smart_command.cmd[4] = 6;     //command length
+  
+  buff[0] = WIN_SMART;
+  switch (command){
+  case READ_VALUES:
+    buff[2]=SMART_READ_VALUES;
+    copydata=buff[3]=1;
+    break;
+  case READ_THRESHOLDS:
+    buff[2]=SMART_READ_THRESHOLDS;
+    copydata=buff[1]=buff[3]=1;
+    break;
+  case READ_LOG:
+    buff[2]=SMART_READ_LOG_SECTOR;
+    buff[1]=select;
+    copydata=buff[3]=1;
+    break;
+  case IDENTIFY:
+    buff[0]=WIN_IDENTIFY;
+    copydata=buff[3]=1;
+    break;
+  case PIDENTIFY:
+    buff[0]=WIN_PIDENTIFY;
+    copydata=buff[3]=1;
+    break;
+  case ENABLE:
+    buff[2]=SMART_ENABLE;
+    buff[1]=1;
+    break;
+  case DISABLE:
+    buff[2]=SMART_DISABLE;
+    buff[1]=1;
+    break;
+  case STATUS:
+  case STATUS_CHECK:
+    // this command only says if SMART is working.  It could be
+    // replaced with STATUS_CHECK below.
+    buff[2] = SMART_STATUS;
+    break;
+  case AUTO_OFFLINE:
+    buff[2]=SMART_AUTO_OFFLINE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case AUTOSAVE:
+    buff[2]=SMART_AUTOSAVE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case IMMEDIATE_OFFLINE:
+    buff[2]=SMART_IMMEDIATE_OFFLINE;
+    buff[1]=select;
+    break;
+  default:
+    pout("Unrecognized command %d in mvsata_os_specific_handler()\n", command);
+    exit(1);
+    break;
+  }  
+  // There are two different types of ioctls().  The HDIO_DRIVE_TASK
+  // one is this:
+  // We are now doing the HDIO_DRIVE_CMD type ioctl.
+  if (ioctl(device, SCSI_IOCTL_SEND_COMMAND, (void *)&smart_command))
+      return -1;
+  //Data returned is starting from 0 offset  
+  if (command == STATUS || command == STATUS_CHECK)
+  {
+    // Cyl low and Cyl high unchanged means "Good SMART status"
+    if (buff[4] == 0x4F && buff[5] == 0xC2)
+      return 0;    
+    // These values mean "Bad SMART status"
+    if (buff[4] == 0xF4 && buff[5] == 0x2C)
+      return 1;    
+    // We haven't gotten output that makes sense; print out some debugging info
+    syserror("Error SMART Status command failed");
+    pout("Please get assistance from %s\n",PROJECTHOME);
+    pout("Register values returned from SMART Status command are:\n");
+    pout("CMD =0x%02x\n",(int)buff[0]);
+    pout("FR =0x%02x\n",(int)buff[1]);
+    pout("NS =0x%02x\n",(int)buff[2]);
+    pout("SC =0x%02x\n",(int)buff[3]);
+    pout("CL =0x%02x\n",(int)buff[4]);
+    pout("CH =0x%02x\n",(int)buff[5]);
+    pout("SEL=0x%02x\n",(int)buff[6]);
+    return -1;   
+  }  
+
+  if (copydata)
+    memcpy(data, buff, 512);
+  return 0; 
+}
+
+
 static char *commandstrings[]={
   [ENABLE]=           "SMART ENABLE",
   [DISABLE]=          "SMART DISABLE",
@@ -621,7 +767,15 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
   }
   
   // now execute the command
-  retval=os_specific_handler(device, command, select, data);
+  /*Check for MVSATA controller*/
+  if (con->ismvsata) 
+  {
+    retval = mvsata_os_specific_handler(device, command, select, data);
+  }
+  else
+  {
+    retval = os_specific_handler(device, command, select, data);
+  }
   
   // If reporting is enabled, say what output was produced by the command
   if (con->reportataioctl){
diff --git a/sm5/extern.h b/sm5/extern.h
index 74c7cb89a..7006bdfc6 100644
--- a/sm5/extern.h
+++ b/sm5/extern.h
@@ -27,7 +27,7 @@
 
 
 #ifndef EXTERN_H_CVSID
-#define EXTERN_H_CVSID "$Id: extern.h,v 1.24 2003/04/19 09:53:41 pjwilliams Exp $\n"
+#define EXTERN_H_CVSID "$Id: extern.h,v 1.24.2.1 2004/08/13 00:04:38 likewise Exp $\n"
 #endif
 
 // Possible values for fixfirmwarebug
@@ -77,6 +77,7 @@ typedef struct smartmonctrl_s {
   // a non-default meaning.  See the ataPrintSmartAttribName and
   // and parse_attribute_def functions.
   unsigned char attributedefs[256];
+  unsigned char ismvsata;
 } smartmonctrl;
 
 #endif
diff --git a/sm5/scsicmds.h b/sm5/scsicmds.h
index 67912abf6..f389256c0 100644
--- a/sm5/scsicmds.h
+++ b/sm5/scsicmds.h
@@ -30,7 +30,7 @@
 #define SCSICMDS_H_
 
 #ifndef SCSICMDS_H_CVSID
-#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.29 2003/06/01 12:36:12 dpgilbert Exp $\n"
+#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.29.2.1 2004/08/13 00:04:38 likewise Exp $\n"
 #endif
 
 #include <stdio.h>
@@ -76,6 +76,10 @@
 #define SEND_DIAGNOSTIC  0x1d
 #endif
 
+#ifndef SCSI_IOCTL_SEND_COMMAND
+#define SCSI_IOCTL_SEND_COMMAND 1
+#endif
+
 typedef unsigned char UINT8;
 typedef char INT8;
 typedef unsigned int UINT32;
diff --git a/sm5/scsiprint.c b/sm5/scsiprint.c
index f93b4a180..ec532c0f8 100644
--- a/sm5/scsiprint.c
+++ b/sm5/scsiprint.c
@@ -40,7 +40,7 @@
 
 #define GBUF_SIZE 65535
 
-const char* scsiprint_c_cvsid="$Id: scsiprint.c,v 1.48 2003/06/01 12:38:22 dpgilbert Exp $"
+const char* scsiprint_c_cvsid="$Id: scsiprint.c,v 1.48.2.1 2004/08/13 00:04:39 likewise Exp $"
 EXTERN_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // control block which points to external global control variables
@@ -455,9 +455,11 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     int err, len;
     int is_tape = 0;
     int peri_dt = 0;
-        
-    memset(gBuf, 0, 36);
-    if ((err = scsiStdInquiry(device, gBuf, 36))) {
+    /* Reset global MVSATA flag */
+    con->ismvsata = 0;       
+
+    memset(gBuf, 0, 64);
+    if ((err = scsiStdInquiry(device, gBuf, 64))) {
         QUIETON(con);
         pout("Standard Inquiry failed [%s]\n", scsiErrString(err));
         QUIETOFF(con);
@@ -467,9 +469,21 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     peri_dt = gBuf[0] & 0x1f;
     if (peripheral_type)
         *peripheral_type = peri_dt;
-    if (! all)
-	return 0;
 
+    if (!all)
+    {
+        /*Check vendor-specific section for presence of MVSATA controller*/
+        if (len >= 42)
+        {
+            if (!strcmp(&gBuf[36], "MVSATA"))
+            {           
+                con->ismvsata = 1;
+                return 1;        
+            }        
+        }
+        return 0;
+    }
+    
     if (len >= 36) {
         memset(manufacturer, 0, sizeof(manufacturer));
         strncpy(manufacturer, &gBuf[8], 8);
@@ -480,6 +494,17 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
         memset(revision, 0, sizeof(revision));
         strncpy(revision, &gBuf[32], 4);
         pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
+
+       /*Check vendor-specific section for presence of MVSATA controller*/
+        if (len >= 42)
+        {
+            if (!strcmp(&gBuf[36], "MVSATA"))
+            {           
+                con->ismvsata = 1;
+                return 1;        
+            }       
+        }
+
         if (0 == scsiInquiryVpd(device, 0x80, gBuf, 64)) {
             /* should use VPD page 0x83 and fall back to this page (0x80)
              * if 0x83 not supported. NAA requires a lot of decoding code */
@@ -641,6 +666,7 @@ static void failuretest(int type, int returnvalue)
 
 
 /* Main entry point used by smartctl command. Return 0 for success */
+/* Return FAILMVSATA for MVSATA controller */
 int scsiPrintMain(const char *dev_name, int fd)
 {
     int checkedSupportedLogPages = 0;
@@ -648,9 +674,21 @@ int scsiPrintMain(const char *dev_name, int fd)
     int returnval=0;
     int res;
 
-    if (scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo)) {
-        pout("Smartctl: SCSI device INQUIRY Failed\n\n");
-        failuretest(MANDATORY_CMD, returnval |= FAILID);
+    returnval = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo);
+    
+
+    if (returnval) 
+    {
+        //Check for MVSATA controller
+        if (con->ismvsata)
+        {
+            return FAILMVSATA;            
+        }
+        else
+        {       
+            pout("Smartctl: SCSI device INQUIRY Failed\n\n");
+            failuretest(MANDATORY_CMD, returnval |= FAILID);
+        }
     }
 
     if (con->smartenable) {
diff --git a/sm5/smartctl.c b/sm5/smartctl.c
index 8fbdf0a1f..452e2204e 100644
--- a/sm5/smartctl.c
+++ b/sm5/smartctl.c
@@ -44,7 +44,7 @@
 #include "utility.h"
 
 extern const char *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid; 
-const char* smartctl_c_cvsid="$Id: smartctl.c,v 1.76 2003/05/01 08:51:46 dpgilbert Exp $"
+const char* smartctl_c_cvsid="$Id: smartctl.c,v 1.76.2.1 2004/08/13 00:04:38 likewise Exp $"
 ATACMDS_H_CVSID ATAPRINT_H_CVSID EXTERN_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
@@ -701,9 +701,18 @@ int main (int argc, char **argv){
 
   // now call appropriate ATA or SCSI routine
   if (tryata)
+  {    
     retval = ataPrintMain(fd);
+  }
   else if (tryscsi)
-    retval = scsiPrintMain(device, fd);
+  {
+    con->ismvsata = 0;
+    retval = scsiPrintMain(device, fd);    
+    if (con->ismvsata)   
+    {
+        retval = ataPrintMain(fd);
+    }
+  }
   else {
     pout("Smartctl: specify if this is an ATA or SCSI device with the -d option.\n");
     Usage();
diff --git a/sm5/smartctl.h b/sm5/smartctl.h
index 1ef52f48a..742871514 100644
--- a/sm5/smartctl.h
+++ b/sm5/smartctl.h
@@ -26,7 +26,7 @@
 #define __SMARTCTL_H_
 
 #ifndef SMARTCTL_H_CVSID
-#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.17 2003/03/06 07:27:17 ballen4705 Exp $\n"
+#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.17.2.1 2004/08/13 00:04:38 likewise Exp $\n"
 #endif
 
 /* Boolean Values */
@@ -61,6 +61,9 @@
 // Device had Errors in the self-test log
 #define FAILLOG (0x01<<7)
 
+// Device is MVSATA 
+#define FAILMVSATA (0x01<<8)
+
 // Classes of SMART commands.  Here 'mandatory' means "Required by the
 // ATA/ATAPI-5 Specification if the device implements the S.M.A.R.T.
 // command set."  The 'mandatory' S.M.A.R.T.  commands are: (1)
diff --git a/sm5/smartd.8 b/sm5/smartd.8
index 2c245cbef..3b7d64a4e 100644
--- a/sm5/smartd.8
+++ b/sm5/smartd.8
@@ -1,6 +1,6 @@
 \# Copyright (C) 2002-3 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 \# 
-\# $Id: smartd.8,v 1.98 2003/06/12 15:36:38 ballen4705 Exp $
+\# $Id: smartd.8,v 1.98.2.1 2004/08/13 00:04:38 likewise Exp $
 \#
 \# 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
@@ -16,7 +16,7 @@
 \# Research Center), Jack Baskin School of Engineering, University of
 \# California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 \#
-.TH SMARTD 8  "$Date: 2003/06/12 15:36:38 $" "smartmontools-5.1"
+.TH SMARTD 8  "$Date: 2004/08/13 00:04:38 $" "smartmontools-5.1"
 .SH NAME
 smartd \- SMART Disk Monitoring Daemon
 .SH SYNOPSIS
@@ -424,7 +424,8 @@ Specifies the type of the device.  This Directive may be used multiple times
 for one device, but the arguments \fIata\fP and \fIscsi\fP are
 mutually-exclusive and if they are both used then
 .B smartd
-will use the one that appears last.
+will use the one that appears last. If the device specified as \fImvsata\fP it will
+be treated as device connected to Marvell SATA controller.
 
 If neither \fIata\fP nor \fIscsi\fP are used then
 .B smartd
@@ -447,6 +448,11 @@ from issuing SCSI commands to an ATA device.
 .B smartd
 from issuing ATA commands to a SCSI device.
 
+.I mvsata
+\- the device type is Marvell SATA controller. In this case
+.B smartd
+will issue ATA commands for the device using the vendor-specific command interface.
+
 .I removable
 \- the device or its media is removable.  This indicates to
 .B smartd
@@ -662,7 +668,7 @@ is set to the device path (examples: /dev/hda, /dev/sdb).
 .nf
 .fi
 .B SMARTD_DEVICETYPE
-is set to the device type (possible values: ata, scsi).
+is set to the device type (possible values: ata, scsi, mvsata).
 .nf
 .fi
 .B SMARTD_FAILTYPE
@@ -1257,4 +1263,4 @@ smartmontools home page at \fBhttp://smartmontools.sourceforge.net/\fP .
 
 .SH
 CVS ID OF THIS PAGE:
-$Id: smartd.8,v 1.98 2003/06/12 15:36:38 ballen4705 Exp $
+$Id: smartd.8,v 1.98.2.1 2004/08/13 00:04:38 likewise Exp $
diff --git a/sm5/smartd.c b/sm5/smartd.c
index 15b50571c..3fc8feb91 100644
--- a/sm5/smartd.c
+++ b/sm5/smartd.c
@@ -50,7 +50,7 @@
 #include "utility.h"
 
 extern const char *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid;
-const char *smartd_c_cvsid="$Id: smartd.c,v 1.170 2003/06/13 18:01:17 ballen4705 Exp $" 
+const char *smartd_c_cvsid="$Id: smartd.c,v 1.170.2.1 2004/08/13 00:04:38 likewise Exp $" 
 ATACMDS_H_CVSID ATAPRINT_H_CVSID EXTERN_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID UTILITY_H_CVSID; 
 
 // Forward declaration
@@ -234,7 +234,10 @@ void printandmail(cfgfile *cfg, int which, int priority, char *fmt, ...){
   // for user scripts
   setenv("SMARTD_MAILER", executable, 1);
   setenv("SMARTD_DEVICE", cfg->name, 1);
-  setenv("SMARTD_DEVICETYPE", cfg->tryata?"ata":"scsi", 1);
+  if (cfg->trymvsata)
+    setenv("SMARTD_DEVICETYPE", "mvsata", 1);
+  else
+    setenv("SMARTD_DEVICETYPE", cfg->tryata?"ata":"scsi", 1);
   setenv("SMARTD_MESSAGE", message, 1);
   setenv("SMARTD_SUBJECT", subject, 1);
   dateandtimezoneepoch(dates, mail->firstsent);
@@ -435,7 +438,7 @@ void printhead(){
 // prints help info for configuration file Directives
 void Directives() {
   printout(LOG_INFO,"Configuration file (/etc/smartd.conf) Directives (after device name):\n");
-  printout(LOG_INFO,"  -d TYPE Set the device type: ata, scsi, removable\n");
+  printout(LOG_INFO,"  -d TYPE Set the device type: ata, scsi, mvsata, removable\n");
   printout(LOG_INFO,"  -T TYPE Set the tolerance to one of: normal, permissive\n");
   printout(LOG_INFO,"  -o VAL  Enable/disable automatic offline tests (on/off)\n");
   printout(LOG_INFO,"  -S VAL  Enable/disable attribute autosave (on/off)\n");
@@ -563,7 +566,8 @@ int atadevicescan2(atadevices_t *devices, cfgfile *cfg){
     // device open failed
     return 1;
   printout(LOG_INFO,"Device: %s, opened\n", device);
-  
+  //set the global MVSATA flag for further ioctl() usage
+  con->ismvsata = cfg->trymvsata;
   // Get drive identity structure
   if (ataReadHDIdentity (fd,&drive)){
     // Unable to read Identity structure
@@ -759,6 +763,7 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg)
     int k, fd, err; 
     char *device = cfg->name;
     struct scsi_iec_mode_page iec;
+    int  peri_dt = 0, len = 0;
     UINT8  tBuf[64];
 
     // should we try to register this as a SCSI device?
@@ -774,7 +779,38 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg)
 #endif
     }
     printout(LOG_INFO,"Device: %s, opened\n", device);
-  
+
+    //Check whether SCSI controller is MVSATA
+    err = scsiStdInquiry(fd, tBuf, sizeof(tBuf));
+    if (err) 
+    {
+        if (1 == err)
+            printout(LOG_ERR, "Device: %s, failed Inquiry [err=%d]\n", 
+                                            device, err);
+        close(fd);
+
+    }
+    len = tBuf[4] + 5;
+    peri_dt = tBuf[0] & 0x1f;
+    if (peri_dt == 0) /*TYPE_DISK*/
+    {
+        if (len >= 42)
+        {
+            if (!strcmp(&tBuf[36], "MVSATA"))
+            {           
+                /*MVSATA controller detected*/
+                cfg->tryata = 1;
+                cfg->tryscsi = 0;
+                cfg->trymvsata = 1;
+                cfg->scsidevicenum = numscsidevices;
+                cfg->atadevicenum = -1; 
+                closedevice(fd, device);
+                return 3;        
+            }            
+        }
+    }
+    //MVSATA
+
     // check that it's ready for commands. IE stores its stuff on the media.
     if ((err = scsiTestUnitReady(fd))) {
       if (1 == err)
@@ -853,6 +889,7 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg)
     // record number of device, type of device, increment device count
     cfg->tryata = 0;
     cfg->tryscsi = 1;
+    cfg->trymvsata = 0;
     cfg->scsidevicenum = numscsidevices;
     cfg->atadevicenum = -1;
     ++numscsidevices;
@@ -862,6 +899,218 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg)
     return 0;
 }
 
+int mvsatadevicescan2(scsidevices_t *devices, cfgfile *cfg)
+{
+  int fd;
+  struct hd_driveid drive;
+  char *device=cfg->name;
+  
+  // should we try to register this as an ATA device?
+  if (!(cfg->trymvsata))
+    return 1;
+  
+  // open the device
+  if ((fd=opendevice(device, O_RDONLY | O_NONBLOCK))<0)
+    // device open failed
+    return 1;
+
+  /*set the global MVSATA flag for further ioctl()*/
+  con->ismvsata = cfg->trymvsata;
+  if (cfg->trymvsata)
+  {
+      /*Reset ignored values*/
+      devices->SmartPageSupported = 0;
+      devices->Temperature = 0;
+      devices->TempPageSupported = 0;
+  }
+  printout(LOG_INFO,"Device: %s, opened\n", device);
+  // Get drive identity structure
+  if (ataReadHDIdentity (fd,&drive)){
+    // Unable to read Identity structure
+    printout(LOG_INFO,"Device: %s, unable to read Device Identity Structure\n",device);
+    close(fd);
+    return 2; 
+  }
+  
+  // pass user setings on to low-level ATA commands
+  con->fixfirmwarebug = cfg->fixfirmwarebug;
+
+  // Show if device in database, and use preset vendor attribute
+  // options unless user has requested otherwise.
+  if (!cfg->ignorepresets){
+
+    // do whatever applypresets decides to do
+    if (applypresets(&drive, cfg->attributedefs, con)<0)
+      printout(LOG_INFO, "Device: %s, not found in smartd database.\n", device);
+    else
+      printout(LOG_INFO, "Device: %s, found in smartd database.\n", device);
+
+    // then save the correct state of the flag (applypresets may have changed it)
+    cfg->fixfirmwarebug = con->fixfirmwarebug;
+  }
+  else
+    printout(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", device);
+
+  // If requested, show which presets would be used for this drive
+  if (cfg->showpresets) {
+    int savedebugmode=debugmode;
+    printout(LOG_INFO, "Device %s: presets are:\n", device);
+    if (!debugmode)
+      debugmode=2;
+    showpresets(&drive);
+    debugmode=savedebugmode;
+  }
+
+  if (!cfg->permissive && !ataSmartSupport(&drive)){
+    // SMART not supported
+    printout(LOG_INFO,"Device: %s, appears to lack SMART, use '-T permissive' Directive to try anyway.\n",device);
+    close(fd);
+    return 2; 
+  }
+  
+  if (ataEnableSmart(fd)){
+    // Enable SMART command has failed
+    printout(LOG_INFO,"Device: %s, could not enable SMART capability\n",device);
+    close(fd);
+    return 2; 
+  }
+  
+  // disable device attribute autosave...
+  if (cfg->autosave==1){
+    if (ataDisableAutoSave(fd))
+      printout(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",device);
+    else
+      printout(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",device);
+  }
+
+  // or enable device attribute autosave
+  if (cfg->autosave==2){
+    if (ataEnableAutoSave(fd))
+      printout(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",device);
+    else
+      printout(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",device);
+  }
+
+  // capability check: SMART status
+  if (cfg->smartcheck && ataSmartStatus2(fd)==-1){
+    printout(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",device);
+    cfg->smartcheck=0;
+  }
+  
+  // capability check: Read smart values and thresholds
+  if (cfg->usagefailed || cfg->prefail || cfg->usage || cfg->autoofflinetest) {
+    devices->smartval=(struct ata_smart_values *)calloc(1,sizeof(struct ata_smart_values));
+    devices->smartthres=(struct ata_smart_thresholds *)calloc(1,sizeof(struct ata_smart_thresholds));
+    
+    if (!devices->smartval || !devices->smartthres){
+      printout(LOG_CRIT,"Not enough memory to obtain SMART data\n");
+      exit(EXIT_NOMEM);
+    }
+    
+    if (ataReadSmartValues(fd,devices->smartval) ||
+        ataReadSmartThresholds (fd,devices->smartthres)){
+      printout(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",device);
+      free(devices->smartval);
+      free(devices->smartthres);
+
+      // make it easy to recognize that we've deallocated
+      devices->smartval=NULL;
+      devices->smartthres=NULL;
+      cfg->usagefailed=cfg->prefail=cfg->usage=0;
+    }
+  }
+
+  // enable/disable automatic on-line testing
+  if (cfg->autoofflinetest){
+    // is this an enable or disable request?
+    char *what=(cfg->autoofflinetest==1)?"disable":"enable";
+    if (!devices->smartval)
+      printout(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",device, what);
+    else {
+      // if command appears unsupported, issue a warning...
+      if (!isSupportAutomaticTimer(devices->smartval))
+    printout(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",device);
+      // ... but then try anyway
+      if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(fd):ataEnableAutoOffline(fd))
+    printout(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", device, what);
+      else
+    printout(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", device, what);
+    }
+  }
+  
+  // capability check: self-test-log
+  if (cfg->selftest){
+    int val;
+
+    // see if device supports Self-test logging.  Note that the
+    // following line is not a typo: Device supports self-test log if
+    // and only if it also supports error log.
+    if (!isSmartErrorLogCapable(devices->smartval)){
+      printout(LOG_INFO, "Device: %s, does not support SMART Self-test Log.\n", device);
+      cfg->selftest=0;
+      cfg->selflogcount=0;
+    }
+    else {
+      // get number of Self-test errors logged
+      val=selftesterrorcount(fd, device);
+      if (val>=0)
+    cfg->selflogcount=val;
+      else
+    cfg->selftest=0;
+    }
+  }
+  
+  // capability check: ATA error log
+  if (cfg->errorlog){
+    int val;
+
+    // see if device supports error logging
+    if (!isSmartErrorLogCapable(devices->smartval)){
+      printout(LOG_INFO, "Device: %s, does not support SMART Error Log.\n", device);
+      cfg->errorlog=0;
+      cfg->ataerrorcount=0;
+    }
+    else {
+      // get number of ATA errors logged
+      val=ataerrorcount(fd, device);
+      if (val>=0)
+    cfg->ataerrorcount=val;
+      else
+    cfg->errorlog=0;
+    }
+  }
+  
+  // If no tests available or selected, return
+  if (!(cfg->errorlog || cfg->selftest || cfg->smartcheck || 
+        cfg->usagefailed || cfg->prefail || cfg->usage)) {
+    close(fd);
+    return 3;
+  }
+  
+  // Do we still have entries available?
+  if (numatadevices>=MAXATADEVICES){
+    printout(LOG_CRIT,"smartd has found more than MAXATADEVICES=%d ATA devices.\n"
+             "Recompile code from " PROJECTHOME " with larger MAXATADEVICES\n",(int)numatadevices);
+    exit(EXIT_CCONST);
+  }
+  
+  // register device
+  printout(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",device);
+  
+  // we were called from a routine that has global storage for the name.  Keep pointer.
+  devices->devicename=device;
+  devices->cfg=cfg;  
+  cfg->tryscsi=0;
+  cfg->tryata=1;
+  cfg->trymvsata=1;
+  cfg->atadevicenum = -1;
+  cfg->scsidevicenum = numscsidevices;  
+  numscsidevices++;  
+  // close file descriptor
+  closedevice(fd, device);
+  return 0;
+}
+
 // 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.
@@ -965,7 +1214,8 @@ int ataCheckDevice(atadevices_t *drive){
     printandmail(cfg, 9, LOG_CRIT, "Device: %s, unable to open device", name);
     return 1;
   }
-
+  //set MVSATA global flag for further ioctl()
+  con->ismvsata = cfg->trymvsata;
   // check smart status
   if (cfg->smartcheck){
     int status=ataSmartStatus2(fd);
@@ -1122,6 +1372,16 @@ int ataCheckDevice(atadevices_t *drive){
   return 0;
 }
 
+/* Calls ataCheckDevice instead of scsi */
+int mvsataCheckDevice(scsidevices_t *drive)
+{
+    atadevices_t atadrive;
+    atadrive.devicename = drive->devicename;
+    atadrive.cfg = drive->cfg;
+    atadrive.smartthres = drive->smartthres;
+    atadrive.smartval = drive->smartval;
+    return ataCheckDevice(&atadrive);
+}
 
 int scsiCheckDevice(scsidevices_t *drive)
 {
@@ -1191,7 +1451,12 @@ void CheckDevices(atadevices_t *atadevices, scsidevices_t *scsidevices){
       ataCheckDevice(atadevices+i);
     
     for (i=0; i<numscsidevices; i++)
-      scsiCheckDevice(scsidevices+i);
+    {
+        if (scsidevices[i].cfg->trymvsata)
+            mvsataCheckDevice(scsidevices+i);   
+        else
+            scsiCheckDevice(scsidevices+i); 
+    }
 
     // This option is primarily for distribution developers who want
     // an automated procedure for seeing if smartd works correctly.
@@ -1372,12 +1637,21 @@ int parsetoken(char *token,cfgfile *cfg){
     } else if (!strcmp(arg, "ata")) {
       cfg->tryata  = 1;
       cfg->tryscsi = 0;
+      cfg->trymvsata = 0;
     } else if (!strcmp(arg, "scsi")) {
       cfg->tryscsi = 1;
       cfg->tryata  = 0;
+      cfg->trymvsata = 0;
     } else if (!strcmp(arg, "removable")) {
       cfg->removable = 1;
-    } else {
+    } 
+    //set the MVSATA flag if device type is "mvsata"
+    else if (!strcmp(arg, "mvsata")) {
+      cfg->tryscsi = 0;
+      cfg->tryata  = 1;
+      cfg->trymvsata  = 1;
+    }
+    else {
       badarg = 1;
     }
     break;
@@ -1638,7 +1912,8 @@ int parseconfigline(int entry, int lineno,char *line){
   cfg->lineno=lineno;
   cfg->tryscsi=1;
   cfg->tryata=1;
-  
+  //reset the MVSATA flag: will be detected automatically if present
+  cfg->trymvsata=0;
   // Try and recognize if a IDE or SCSI device.  These can be
   // overwritten by configuration file directives.
   if (GUESS_DEVTYPE_ATA == guess_linux_device_type(name))
@@ -2087,7 +2362,8 @@ int makeconfigentries(int num, char *name, int isata, int start, int scandirecti
     // select if it's a SCSI or ATA device
     cfg->tryata=isata;
     cfg->tryscsi=!isata;
-    
+    //reset trymvsata flag
+    cfg->trymvsata = 0;
     // put in the device name
     cfg->name=strdup(name);
     if (!cfg->name) {
@@ -2118,6 +2394,7 @@ int main (int argc, char **argv){
   atadevices_t atadevices[MAXATADEVICES], *atadevicesptr=atadevices;
   scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr=scsidevices;
   int i, entries, scandirective=0, scanning=0;
+  int res = 0;
   smartmonctrl control;
   
   // initialize global communications variables
@@ -2160,8 +2437,7 @@ int main (int argc, char **argv){
     // initialize total number of entries to seach for
     entries=0;
     doata=config->tryata;
-    doscsi=config->tryscsi;
-
+    doscsi=config->tryscsi || config->trymvsata;    
     // make list of ATA devices to search for
     if (doata)
       entries+=makeconfigentries(MAXATADEVICES,  "/dev/hda", 1, entries, scandirective);
@@ -2175,17 +2451,41 @@ int main (int argc, char **argv){
     int notregistered=1;
     
     // register ATA devices
-    if (config[i].tryata){
-      if (atadevicescan2(atadevicesptr+numatadevices, config+i))
-	cantregister(config[i].name, "ATA", config[i].lineno, scandirective);
+    if (config[i].trymvsata)
+    {
+        /*scan device connected to MVSATA*/
+      if (mvsatadevicescan2(scsidevicesptr+numscsidevices, config+i))
+        cantregister(config[i].name, "MVSATA", config[i].lineno, scandirective);
       else
-	notregistered=0;
+        notregistered=0;
+    }    
+    else
+    {   
+        if (config[i].tryata)
+        {
+          if (atadevicescan2(atadevicesptr+numatadevices, config+i))
+            cantregister(config[i].name, "ATA", config[i].lineno, scandirective);
+          else
+            notregistered=0;
+        }    
     }
     
     // then register SCSI devices
-    if (config[i].tryscsi){
-      if (scsidevicescan(scsidevicesptr+numscsidevices, config+i))
-	cantregister(config[i].name, "SCSI", config[i].lineno, scandirective);
+    if (config[i].tryscsi)
+    {
+      config[i].trymvsata = 0;
+      res = scsidevicescan(scsidevicesptr+numscsidevices, config+i);
+      if (res) 
+      {
+        //if MVSATA controller detected rescan with ATA options
+        if (config[i].trymvsata) 
+        {
+            i--;
+            continue;
+        }
+        else
+            cantregister(config[i].name, "SCSI", config[i].lineno, scandirective);
+      }     
       else
 	notregistered=0;
     }
diff --git a/sm5/smartd.h b/sm5/smartd.h
index 15f76ac25..ad0169a3e 100644
--- a/sm5/smartd.h
+++ b/sm5/smartd.h
@@ -23,7 +23,7 @@
  */
 
 #ifndef SMARTD_H_CVSID
-#define SMARTD_H_CVSID "$Id: smartd.h,v 1.36 2003/05/12 18:34:40 pjwilliams Exp $\n"
+#define SMARTD_H_CVSID "$Id: smartd.h,v 1.36.2.1 2004/08/13 00:04:38 likewise Exp $\n"
 #endif
 
 // Configuration file
@@ -97,6 +97,7 @@ typedef struct configfile_s {
   // which type of device was detected.
   char tryata;
   char tryscsi;
+  char trymvsata;
   char *name;
   // which tests have been enabled?
   char smartcheck;
@@ -158,9 +159,14 @@ typedef struct atadevices_s {
 // used to store a list of SCSI devices to monitor.  Devicename points
 // to a malloced name string.
 typedef struct scsidevices_s {
+  /* ignored for MVSATA adapter */
   unsigned char SmartPageSupported;
   unsigned char TempPageSupported;
   unsigned char Temperature;
+  /* MVSATA support: ata parameters */
+  struct ata_smart_values *smartval;
+  struct ata_smart_thresholds *smartthres;
+  /* end MVSATA support */
   char *devicename;
   cfgfile *cfg;
 } scsidevices_t;
-- 
GitLab