]> git.proxmox.com Git - mirror_smartmontools-debian.git/blobdiff - os_linux.cpp
Imported Upstream version 5.42+svn3561
[mirror_smartmontools-debian.git] / os_linux.cpp
index c46b756f63dcb65f12f881c070ddaaa3534596c7..b68575a12e348a995accad3021b1808b025b0a06 100644 (file)
@@ -5,9 +5,9 @@
  *
  * Copyright (C) 2003-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2003-11 Doug Gilbert <dgilbert@interlog.com>
- * Copyright (C) 2008    Hank Wu <hank@areca.com.tw>
+ * Copyright (C) 2008-12 Hank Wu <hank@areca.com.tw>
  * Copyright (C) 2008    Oliver Bock <brevilo@users.sourceforge.net>
- * Copyright (C) 2008-11 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2008    Jordan Hargrave <jordan_hargrave@dell.com>
  *
  *  Parts of this file are derived from code that was
@@ -89,7 +89,7 @@
 
 #define ARGUSED(x) ((void)(x))
 
-const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp 3317 2011-04-19 19:42:54Z chrfranke $"
+const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp 3558 2012-06-05 16:42:05Z chrfranke $"
   OS_LINUX_H_CVSID;
 
 
@@ -196,8 +196,9 @@ static const char  smartctl_examples[] =
                  "  smartctl --all --device=hpt,1/1/3 /dev/sda\n"
                  "          (Prints all SMART info for the SATA disk attached to the 3rd PMPort\n"
                  "           of the 1st channel on the 1st HighPoint RAID controller)\n"
-                 "  smartctl --all --device=areca,3 /dev/sg2\n"
-                 "          (Prints all SMART info for 3rd ATA disk on Areca RAID controller)\n"
+                 "  smartctl --all --device=areca,3/1 /dev/sg2\n"
+                 "          (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
+                 "           on Areca RAID controller)\n"
   ;
 
 
@@ -605,7 +606,7 @@ static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
         }
     }
 
-    if (io_hdr.info | SG_INFO_CHECK) { /* error or warning */
+    if (io_hdr.info & SG_INFO_CHECK) { /* error or warning */
         int masked_driver_status = (LSCSI_DRIVER_MASK & io_hdr.driver_status);
 
         if (0 != io_hdr.host_status) {
@@ -940,9 +941,13 @@ smart_device * linux_megaraid_device::autodetect_open()
   {
     // SAT or USB ?
     ata_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
-    if (newdev)
+    if (newdev) {
       // NOTE: 'this' is now owned by '*newdev'
+      newdev->close();
+      newdev->set_err(ENOSYS, "SATA device detected,\n"
+        "MegaRAID SAT layer is reportedly buggy, use '-d sat+megaraid,N' to try anyhow");
       return newdev;
+    }
   }
 
   // Nothing special found
@@ -1050,11 +1055,17 @@ bool linux_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop)
   if (iop->cmnd[0] == 0x00)
     return true;
 
-  if (iop->cmnd[0] == 0xa1 || iop->cmnd[0] == 0x85) { // SAT_ATA_PASSTHROUGH_12/16
+  if (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 || iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16) { 
     // Controller does not return ATA output registers in SAT sense data
     if (iop->cmnd[2] & (1 << 5)) // chk_cond
       return set_err(ENOSYS, "ATA return descriptor not supported by controller firmware");
   }
+  // SMART WRITE LOG SECTOR causing media errors
+  if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 && iop->cmnd[14] == ATA_SMART_CMD 
+       && iop->cmnd[3]==0 && iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) || 
+      (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 && iop->cmnd[9] == ATA_SMART_CMD &&
+        iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) 
+    return set_err(ENOSYS, "SMART WRITE LOG SECTOR command is not supported by controller firmware"); 
 
   if (pt_cmd == NULL)
     return false;
@@ -1115,14 +1126,11 @@ bool linux_megaraid_device::megasas_cmd(int cdbLen, void *cdb,
 /* Issue passthrough scsi commands to PERC2/3/4 controllers */
 bool linux_megaraid_device::megadev_cmd(int cdbLen, void *cdb, 
   int dataLen, void *data,
-  int senseLen, void *sense, int /*report*/)
+  int /*senseLen*/, void * /*sense*/, int /*report*/)
 {
   struct uioctl_t uio;
   int rc;
 
-  sense = NULL;
-  senseLen = 0;
-
   /* Don't issue to the controller */
   if (m_disknum == 7)
     return false;
@@ -1641,17 +1649,18 @@ bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out
 /// Areca RAID support
 
 class linux_areca_device
-: public /*implements*/ ata_device_with_command_set,
+: public /*implements*/ ata_device,
   public /*extends*/ linux_smart_device
 {
 public:
-  linux_areca_device(smart_interface * intf, const char * dev_name, int disknum);
+  linux_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
 
 protected:
-  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); 
 
 private:
   int m_disknum; ///< Disk number.
+  int m_encnum;  ///< Enclosure number.
 };
 
 
@@ -1957,17 +1966,26 @@ static int arcmsr_command_handler(int fd, unsigned long arcmsr_cmd, unsigned cha
 }
 
 
-linux_areca_device::linux_areca_device(smart_interface * intf, const char * dev_name, int disknum)
+linux_areca_device::linux_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
 : smart_device(intf, dev_name, "areca", "areca"),
   linux_smart_device(O_RDWR | O_EXCL | O_NONBLOCK),
-  m_disknum(disknum)
+  m_disknum(disknum),
+  m_encnum(encnum)
 {
-  set_info().info_name = strprintf("%s [areca_%02d]", dev_name, disknum);
+  set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
 }
 
 // Areca RAID Controller
-int linux_areca_device::ata_command_interface(smart_command_set command, int select, char * data)
+// int linux_areca_device::ata_command_interface(smart_command_set command, int select, char * data)
+bool linux_areca_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) 
 {
+if (!ata_cmd_is_ok(in, 
+    true, // data_out_support 
+    false, // TODO: multi_sector_support 
+    true) // ata_48bit_support 
+    )
+    return false; 
+
        // ATA input registers
        typedef struct _ATA_INPUT_REGISTERS
        {
@@ -2041,115 +2059,45 @@ int linux_areca_device::ata_command_interface(smart_command_set command, int sel
        areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff);
        areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command
 
-
        // ----- BEGIN TO SETUP PAYLOAD DATA -----
-
        memcpy(&areca_packet[7], "SmrT", 4);    // areca defined password
-
        ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12];
-       ata_cmd->cylinder_low    = 0x4F;
-       ata_cmd->cylinder_high   = 0xC2;
-
 
-       if ( command == READ_VALUES     ||
-                command == READ_THRESHOLDS ||
-                command == READ_LOG ||
-                command == IDENTIFY ||
-                command == PIDENTIFY )
-       {
-               // the commands will return data
-               areca_packet[6] = 0x13;
-               ata_cmd->sector_count = 0x1;
+       // Set registers
+        {
+           const ata_in_regs_48bit & r = in.in_regs;
+           ata_cmd->features     = r.features_16;
+           ata_cmd->sector_count  = r.sector_count_16;
+           ata_cmd->sector_number = r.lba_low_16;
+           ata_cmd->cylinder_low  = r.lba_mid_16;
+           ata_cmd->cylinder_high = r.lba_high_16;
+           ata_cmd->device_head   = r.device;
+           ata_cmd->command      = r.command;
        }
-       else if ( command == WRITE_LOG )
-       {
-               // the commands will write data
-               areca_packet[6] = 0x14;
+       bool readdata = false; 
+       if (in.direction == ata_cmd_in::data_in) { 
+           readdata = true;
+           // the command will read data
+           areca_packet[6] = 0x13;
        }
-       else
+       else if ( in.direction == ata_cmd_in::no_data )
        {
                // the commands will return no data
                areca_packet[6] = 0x15;
        }
-
-
-       ata_cmd->command = ATA_SMART_CMD;
-       // Now set ATA registers depending upon command
-       switch ( command )
+       else if (in.direction == ata_cmd_in::data_out) 
        {
-       case CHECK_POWER_MODE:  
-               //printf("command = CHECK_POWER_MODE\n");
-               ata_cmd->command = ATA_CHECK_POWER_MODE;        
-               break;
-       case READ_VALUES:
-               //printf("command = READ_VALUES\n");
-               ata_cmd->features = ATA_SMART_READ_VALUES;
-               break;
-       case READ_THRESHOLDS:    
-               //printf("command = READ_THRESHOLDS\n");
-               ata_cmd->features = ATA_SMART_READ_THRESHOLDS;
-               break;
-       case READ_LOG: 
-               //printf("command = READ_LOG\n");
-               ata_cmd->features = ATA_SMART_READ_LOG_SECTOR;
-               ata_cmd->sector_number = select;        
-               break;
-       case WRITE_LOG:        
-               //printf("command = WRITE_LOG\n");    
-               ata_cmd->features = ATA_SMART_WRITE_LOG_SECTOR;
-               memcpy(ata_cmd->data, data, 512);
-               ata_cmd->sector_count = 1;
-               ata_cmd->sector_number = select;
-               break;
-       case IDENTIFY:
-               //printf("command = IDENTIFY\n");   
-               ata_cmd->command = ATA_IDENTIFY_DEVICE;         
-               break;
-       case PIDENTIFY:
-               //printf("command = PIDENTIFY\n");
-               errno=ENODEV;
-               return -1;
-       case ENABLE:
-               //printf("command = ENABLE\n");
-               ata_cmd->features = ATA_SMART_ENABLE;
-               break;
-       case DISABLE:
-               //printf("command = DISABLE\n");
-               ata_cmd->features = ATA_SMART_DISABLE;
-               break;
-       case AUTO_OFFLINE:
-               //printf("command = AUTO_OFFLINE\n");
-               ata_cmd->features = ATA_SMART_AUTO_OFFLINE;
-               // Enable or disable?
-               ata_cmd->sector_count = select;
-               break;
-       case AUTOSAVE:
-               //printf("command = AUTOSAVE\n");
-               ata_cmd->features = ATA_SMART_AUTOSAVE;
-               // Enable or disable?
-               ata_cmd->sector_count = select;
-               break;
-       case IMMEDIATE_OFFLINE:
-               //printf("command = IMMEDIATE_OFFLINE\n");
-               ata_cmd->features = ATA_SMART_IMMEDIATE_OFFLINE;
-               // What test type to run?
-               ata_cmd->sector_number = select;
-               break;
-       case STATUS_CHECK:
-               //printf("command = STATUS_CHECK\n");
-               ata_cmd->features = ATA_SMART_STATUS;           
-               break;
-       case STATUS:
-               //printf("command = STATUS\n");
-               ata_cmd->features = ATA_SMART_STATUS;       
-               break;
-       default:
-               //printf("command = UNKNOWN\n");
-               errno=ENOSYS;
-               return -1;
-       };
+               // the commands will write data
+               memcpy(ata_cmd->data, in.buffer, in.size);
+               areca_packet[6] = 0x14;
+       }
+       else {
+           // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE
+           return set_err(ENOTSUP, "DATA OUT not supported for this Areca controller type");
+       }
 
-       areca_packet[11] = m_disknum - 1;                  // drive number
+       areca_packet[11] = m_disknum - 1;  // disk#
+       areca_packet[19] = m_encnum - 1;   // enc#
 
        // ----- BEGIN TO SETUP CHECKSUM -----
        for ( int loop = 3; loop < areca_packet_len - 1; loop++ )
@@ -2166,7 +2114,7 @@ int linux_areca_device::ata_command_interface(smart_command_set command, int sel
        expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0, NULL);
         if (expected==-3) {
            find_areca_in_proc(NULL);
-           return -1;
+           return set_err(EIO);
        }
 
        expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0, NULL);
@@ -2189,45 +2137,36 @@ int linux_areca_device::ata_command_interface(smart_command_set command, int sel
 
        if ( return_buff[expected - 1] != cs )
        {
-               errno = EIO;
-               return -1;
+               return set_err(EIO);
        }
 
        sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ;
        if ( ata_out->status )
        {
-               if ( command == IDENTIFY )
-               {
-                       pout("The firmware of your Areca RAID controller appears to be outdated!\n" \
-                                "Please update your controller to firmware version 1.46 or later.\n" \
-                                "You may download it here: ftp://ftp.areca.com.tw/RaidCards/BIOS_Firmware\n\n");
-               }
-               errno = EIO;
-               return -1;
+               if ( in.in_regs.command == ATA_IDENTIFY_DEVICE
+                && !nonempty((unsigned char *)in.buffer, in.size)) 
+                {
+                   return set_err(ENODEV, "No drive on port %d", m_disknum);
+                } 
        }
 
        // returns with data
-       if ( command == READ_VALUES     ||
-                command == READ_THRESHOLDS ||
-                command == READ_LOG ||
-                command == IDENTIFY ||
-                command == PIDENTIFY )
-       {
-               memcpy(data, &return_buff[7], 512); 
-       }
-
-       if ( command == CHECK_POWER_MODE )
+       if (readdata)
        {
-               data[0] = ata_out->sector_count;
+               memcpy(in.buffer, &return_buff[7], in.size); 
        }
 
-       if ( command == STATUS_CHECK &&
-                ( ata_out->cylinder_low == 0xF4 && ata_out->cylinder_high == 0x2C ) )
+       // Return register values
        {
-               return 1;
+           ata_out_regs_48bit & r = out.out_regs;
+           r.error           = ata_out->error;
+           r.sector_count_16 = ata_out->sector_count;
+           r.lba_low_16      = ata_out->sector_number;
+           r.lba_mid_16      = ata_out->cylinder_low;
+           r.lba_high_16     = ata_out->cylinder_high;
+           r.status          = ata_out->status;
        }
-
-       return 0;
+       return true;
 }
 
 
@@ -2992,12 +2931,6 @@ smart_device * linux_smart_interface::missing_option(const char * opt)
   return 0;
 }
 
-// Return true if STR starts with PREFIX.
-static inline bool str_starts_with(const char * str, const char * prefix)
-{
-  return !strncmp(str, prefix, strlen(prefix));
-}
-
 // Return kernel release as integer ("2.6.31" -> 206031)
 static unsigned get_kernel_release()
 {
@@ -3125,16 +3058,17 @@ smart_device * linux_smart_interface::get_custom_smart_device(const char * name,
 
   // 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");
+  int encnum = 1;
+  if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) {
+    if (!(1 <= disknum && disknum <= 128)) {
+      set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum);
       return 0;
     }
-    if (!(1 <= disknum && disknum <= 24)) {
-      set_err(EINVAL, "Option -d areca,N (N=%d) must have 1 <= N <= 24", disknum);
+    if (!(1 <= encnum && encnum <= 8)) {
+      set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum);
       return 0;
     }
-    return new linux_areca_device(this, name, disknum);
+    return new linux_areca_device(this, name, disknum, encnum);
   }
 
   // Highpoint ?
@@ -3150,7 +3084,7 @@ smart_device * linux_smart_interface::get_custom_smart_device(const char * name,
       set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied");
       return 0;
     }
-    if (!(1 <= channel && channel <= 8)) {
+    if (!(1 <= channel && channel <= 16)) {
       set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied");
       return 0;
     }
@@ -3186,7 +3120,7 @@ smart_device * linux_smart_interface::get_custom_smart_device(const char * name,
 
 std::string linux_smart_interface::get_valid_custom_dev_types_str()
 {
-  return "marvell, areca,N, 3ware,N, hpt,L/M/N, megaraid,N"
+  return "marvell, areca,N/E, 3ware,N, hpt,L/M/N, megaraid,N"
 #ifdef HAVE_LINUX_CCISS_IOCTL_H
                                               ", cciss,N"
 #endif