*
* Home page of code is: http://smartmontools.sourceforge.net
*
- * Copyright (C) 2003-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2003-8 Doug Gilbert <dougg@torque.net>
- * Copyright (C) 2008 Hank Wu <hank@areca.com.tw>
- * Copyright (C) 2008 Oliver Bock <brevilo@users.sourceforge.net>
- * Copyright (C) 2008-9 Christian Franke <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2008 Jordan Hargrave <jordan_hargrave@dell.com>
+ * Copyright (C) 2003-10 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-10 Doug Gilbert <dougg@torque.net>
+ * Copyright (C) 2008 Hank Wu <hank@areca.com.tw>
+ * Copyright (C) 2008 Oliver Bock <brevilo@users.sourceforge.net>
+ * Copyright (C) 2008-10 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_XXXX_c_cvsid="$Id: os_linux.cpp 2915 2009-09-18 21:17:37Z chrfranke $" \
+const char *os_XXXX_c_cvsid="$Id: os_linux.cpp 3076 2010-03-12 22:23:08Z chrfranke $" \
ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_LINUX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
/* for passing global control variables */
#define SG_IO_RESP_SENSE_LEN 64 /* large enough see buffer */
#define LSCSI_DRIVER_MASK 0xf /* mask out "suggestions" */
#define LSCSI_DRIVER_SENSE 0x8 /* alternate CHECK CONDITION indication */
+#define LSCSI_DID_ERROR 0x7 /* Need to work around aacraid driver quirk */
#define LSCSI_DRIVER_TIMEOUT 0x6
#define LSCSI_DID_TIME_OUT 0x3
#define LSCSI_DID_BUS_BUSY 0x2
(LSCSI_DID_TIME_OUT == io_hdr.host_status))
return -ETIMEDOUT;
else
- return -EIO; /* catch all */
+ /* Check for DID_ERROR - workaround for aacraid driver quirk */
+ if (LSCSI_DID_ERROR != io_hdr.host_status) {
+ return -EIO; /* catch all if not DID_ERR */
+ }
}
if (0 != masked_driver_status) {
if (LSCSI_DRIVER_TIMEOUT == masked_driver_status)
public /*extends*/ linux_smart_device
{
public:
- linux_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
+ linux_scsi_device(smart_interface * intf, const char * dev_name,
+ const char * req_type, bool scanning = false);
virtual smart_device * autodetect_open();
virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+
+private:
+ bool m_scanning; ///< true if created within scan_smart_devices
};
linux_scsi_device::linux_scsi_device(smart_interface * intf,
- const char * dev_name, const char * req_type)
+ const char * dev_name, const char * req_type, bool scanning /*= false*/)
: smart_device(intf, dev_name, "scsi", req_type),
- linux_smart_device(O_RDWR | O_NONBLOCK, O_RDONLY | O_NONBLOCK)
+ // If opened with O_RDWR, a SATA disk in standby mode
+ // may spin-up after device close().
+ linux_smart_device(O_RDONLY | O_NONBLOCK),
+ m_scanning(scanning)
{
}
smart_device * linux_megaraid_device::autodetect_open()
{
+ int report = con->reportscsiioctl;
+
// Open device
if (!open())
return this;
if (len < 36)
return this;
- printf("Got MegaRAID inquiry.. %s\n", req_buff+8);
+ if (report)
+ printf("Got MegaRAID inquiry.. %s\n", req_buff+8);
// Use INQUIRY to detect type
- smart_device * newdev = 0;
- try {
+ {
// SAT or USB ?
- newdev = smi()->autodetect_sat_device(this, req_buff, len);
+ ata_device * 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;
char line[128];
int mjr, n1;
FILE *fp;
+ int report = con->reportscsiioctl;
if (!linux_smart_device::open())
return false;
else if (ioctl(get_fd(), SCSI_IOCTL_GET_BUS_NUMBER, &m_hba) != 0) {
int err = errno;
linux_smart_device::close();
- return set_err(err, "can't get hba");
+ return set_err(err, "can't get bus number");
}
/* Perform mknod of device ioctl node */
n1=0;
if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) {
n1=mknod("/dev/megaraid_sas_ioctl_node", S_IFCHR, makedev(mjr, 0));
- printf("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno);
+ if(report > 0)
+ printf("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno);
if (n1 >= 0 || errno == EEXIST)
break;
}
else if (sscanf(line, "%d megadev%n", &mjr, &n1) == 1 && n1 == 11) {
n1=mknod("/dev/megadev0", S_IFCHR, makedev(mjr, 0));
- printf("Creating /dev/megadev0 = %d\n", n1 >= 0 ? 0 : errno);
+ if(report > 0)
+ printf("Creating /dev/megadev0 = %d\n", n1 >= 0 ? 0 : errno);
if (n1 >= 0 || errno == EEXIST)
break;
}
if (iop->cmnd[0] == 0x00)
return true;
if (iop->cmnd[0] == 0x85 && iop->cmnd[1] == 0x06) {
- pout("Rejecting SMART/ATA command to controller\n");
+ if(report > 0)
+ pout("Rejecting SMART/ATA command to controller\n");
+ // Emulate SMART STATUS CHECK drive reply
+ // smartctl fail to work without this
+ if(iop->cmnd[2]==0x2c) {
+ iop->resp_sense_len=22; // copied from real response
+ iop->sensep[0]=0x72; // descriptor format
+ iop->sensep[7]=0x0e; // additional length
+ iop->sensep[8]=0x09; // description pointer
+ iop->sensep[17]=0x4f; // low cylinder GOOD smart status
+ iop->sensep[19]=0xc2; // high cylinder GOOD smart status
+ }
return true;
}
passthru->size++;
}
else
- set_err(EINVAL);
+ return set_err(EINVAL);
// Now send the command down through an ioctl()
int ioctlreturn;
}
// Return register values
- {
+ if (passthru) {
ata_out_regs_48bit & r = out.out_regs;
r.error = passthru->features;
r.sector_count_16 = passthru->sector_count;
// look for nonexistent devices/ports
if ( in.in_regs.command == ATA_IDENTIFY_DEVICE
- && !nonempty((unsigned char *)in.buffer, in.size)) {
+ && !nonempty(in.buffer, in.size)) {
return set_err(ENODEV, "No drive on port %d", m_disknum);
}
return this;
// No Autodetection if device type was specified by user
- if (*get_req_type())
- return this;
+ bool sat_only = false;
+ if (*get_req_type()) {
+ // Detect SAT if device object was created by scan_smart_devices().
+ if (!(m_scanning && !strcmp(get_req_type(), "sat")))
+ return this;
+ sat_only = true;
+ }
// The code below is based on smartd.cpp:SCSIFilterKnown()
int avail_len = req_buff[4] + 5;
int len = (avail_len < req_len ? avail_len : req_len);
- if (len < 36)
- return this;
+ if (len < 36) {
+ if (sat_only) {
+ close();
+ set_err(EIO, "INQUIRY too short for SAT");
+ }
+ return this;
+ }
// Use INQUIRY to detect type
- smart_device * newdev = 0;
- try {
+ if (!sat_only) {
+
// 3ware ?
if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) {
close();
return this;
}
+ // DELL?
+ if (!memcmp(req_buff + 8, "DELL PERC", 12) || !memcmp(req_buff + 8, "MegaRAID", 8)) {
+ close();
+ set_err(EINVAL, "DELL or MegaRaid controller, please try adding '-d megaraid,N'");
+ return this;
+ }
+
// Marvell ?
if (len >= 42 && !memcmp(req_buff + 36, "MVSATA", 6)) {
//pout("Device %s: using '-d marvell' for ATA disk with Marvell driver\n", get_dev_name());
close();
- newdev = new linux_marvell_device(smi(), get_dev_name(), get_req_type());
+ smart_device_auto_ptr newdev(
+ new linux_marvell_device(smi(), get_dev_name(), get_req_type())
+ );
newdev->open(); // TODO: Can possibly pass open fd
delete this;
- return newdev;
+ return newdev.release();
}
+ }
- // SAT or USB ?
- newdev = smi()->autodetect_sat_device(this, req_buff, len);
+ // SAT or USB ?
+ {
+ smart_device * 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
+
+ if (sat_only) {
+ close();
+ set_err(EIO, "Not a SAT device");
+ }
return this;
}
if (autodetect)
dev = autodetect_smart_device(name);
else if (is_scsi)
- dev = new linux_scsi_device(this, name, req_type);
+ dev = new linux_scsi_device(this, name, req_type, true /*scanning*/);
else
dev = new linux_ata_device(this, name, req_type);
if (dev) // autodetect_smart_device() may return nullptr.
- devlist.add(dev);
+ devlist.push_back(dev);
}
}
type = "";
bool scan_ata = (!*type || !strcmp(type, "ata" ));
- bool scan_scsi = (!*type || !strcmp(type, "scsi"));
+ // "sat" detection will be later handled in linux_scsi_device::autodetect_open()
+ bool scan_scsi = (!*type || !strcmp(type, "scsi") || !strcmp(type, "sat"));
if (!(scan_ata || scan_scsi))
return true;
if (scan_ata)
get_dev_list(devlist, "/dev/hd[a-t]", true, false, type, false);
- if (scan_scsi) // Try USB autodetection if no type specifed
- get_dev_list(devlist, "/dev/sd[a-z]", false, true, type, !*type);
+ if (scan_scsi) {
+ bool autodetect = !*type; // Try USB autodetection if no type specifed
+ get_dev_list(devlist, "/dev/sd[a-z]", false, true, type, autodetect);
+ // Support up to 104 devices
+ get_dev_list(devlist, "/dev/sd[a-c][a-z]", false, true, type, autodetect);
+ }
// if we found traditional links, we are done
if (devlist.size() > 0)