*
* 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
#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;
" 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"
;
}
}
- 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) {
{
// 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
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;
/* 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;
/// 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.
};
}
-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
{
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++ )
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);
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;
}
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()
{
// 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 ?
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;
}
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