X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=os_linux.cpp;h=6374a5b6b255484f143b19812da382208df56c49;hb=bcade6c14a06cfc842b41df91fdc5b9577cd68f1;hp=5635eaa01454552dcd1608f1638e26e80a8d6697;hpb=4b9bef51f96a4fc1bc03c7b3ed5073be2bcc6739;p=mirror_smartmontools-debian.git diff --git a/os_linux.cpp b/os_linux.cpp index 5635eaa..6374a5b 100644 --- a/os_linux.cpp +++ b/os_linux.cpp @@ -1,20 +1,27 @@ /* * os_linux.cpp * - * Home page of code is: http://smartmontools.sourceforge.net + * Home page of code is: http://www.smartmontools.org * - * Copyright (C) 2003-8 Bruce Allen - * Copyright (C) 2003-8 Doug Gilbert - * Copyright (C) 2008 Hank Wu - * Copyright (C) 2008 Oliver Bock - * Copyright (C) 2008-9 Christian Franke - * Copyright (C) 2008 Jordan Hargrave + * Copyright (C) 2003-11 Bruce Allen + * Copyright (C) 2003-11 Doug Gilbert + * Copyright (C) 2008-15 Christian Franke * - * Parts of this file are derived from code that was + * Original AACRaid code: + * Copyright (C) 2014 Raghava Aditya + * + * Original Areca code: + * Copyright (C) 2008-12 Hank Wu + * Copyright (C) 2008 Oliver Bock + * + * Original MegaRAID code: + * Copyright (C) 2008 Jordan Hargrave + * + * 3ware code was derived from code that was: * * Written By: Adam Radford * Modifications By: Joel Jacobson - * Arnaldo Carvalho de Melo + * Arnaldo Carvalho de Melo * Brad Strand * * Copyright (C) 1999-2003 3ware Inc. @@ -60,10 +67,12 @@ #include #include #include -#include +#include #include +#include // for offsetof() #include #include +#include #ifndef makedev // old versions of types.h do not include sysmacros.h #include #endif @@ -73,16 +82,16 @@ #include "int64.h" #include "atacmds.h" -#include "extern.h" #include "os_linux.h" #include "scsicmds.h" #include "utility.h" -#include "extern.h" #include "cciss.h" #include "megaraid.h" +#include "aacraid.h" #include "dev_interface.h" #include "dev_ata_cmd_set.h" +#include "dev_areca.h" #ifndef ENOTSUP #define ENOTSUP ENOSYS @@ -90,13 +99,9 @@ #define ARGUSED(x) ((void)(x)) -const char *os_XXXX_c_cvsid="$Id: os_linux.cpp 2951 2009-10-08 23:43:46Z samm2 $" \ -ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_LINUX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; - -/* for passing global control variables */ -// (con->reportscsiioctl only) -extern smartmonctrl *con; - +const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp 4157 2015-10-20 16:03:57Z chrfranke $" + OS_LINUX_H_CVSID; +extern unsigned char failuretest_permissive; namespace os_linux { // No need to publish anything, name provided for Doxygen @@ -126,13 +131,15 @@ protected: int get_fd() const { return m_fd; } + void set_fd(int fd) + { m_fd = fd; } + private: int m_fd; ///< filedesc, -1 if not open. int m_flags; ///< Flags for ::open() int m_retry_flags; ///< Flags to retry ::open(), -1 if no retry }; - linux_smart_device::~linux_smart_device() throw() { if (m_fd >= 0) @@ -187,24 +194,25 @@ bool linux_smart_device::close() // examples for smartctl static const char smartctl_examples[] = "=================================================== SMARTCTL EXAMPLES =====\n\n" - " smartctl --all /dev/hda (Prints all SMART information)\n\n" - " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n" + " smartctl --all /dev/sda (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n" " (Enables SMART on first disk)\n\n" - " smartctl --test=long /dev/hda (Executes extended disk self-test)\n\n" - " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n" + " smartctl --test=long /dev/sda (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n" " (Prints Self-Test & Attribute errors)\n" " smartctl --all --device=3ware,2 /dev/sda\n" " smartctl --all --device=3ware,2 /dev/twe0\n" " smartctl --all --device=3ware,2 /dev/twa0\n" + " smartctl --all --device=3ware,2 /dev/twl0\n" " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" " 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" ; - ///////////////////////////////////////////////////////////////////////////// /// Linux ATA support @@ -245,7 +253,6 @@ linux_ata_device::linux_ata_device(smart_interface * intf, const char * dev_name // 0 if the command succeeded and disk SMART status is "OK" // 1 if the command succeeded and disk SMART status is "FAILING" - #define BUFFER_LENGTH (4+512) int linux_ata_device::ata_command_interface(smart_command_set command, int select, char * data) @@ -349,7 +356,6 @@ int linux_ata_device::ata_command_interface(smart_command_set command, int selec unsigned char task[sizeof(ide_task_request_t)+512]; ide_task_request_t *reqtask=(ide_task_request_t *) task; task_struct_t *taskfile=(task_struct_t *) reqtask->io_ports; - int retval; memset(task, 0, sizeof(task)); @@ -370,8 +376,8 @@ int linux_ata_device::ata_command_interface(smart_command_set command, int selec // copy user data into the task request structure memcpy(task+sizeof(ide_task_request_t), data, 512); - if ((retval=ioctl(get_fd(), HDIO_DRIVE_TASKFILE, task))) { - if (retval==-EINVAL) + if (ioctl(get_fd(), HDIO_DRIVE_TASKFILE, task)) { + if (errno==-EINVAL) pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n"); return -1; } @@ -381,8 +387,6 @@ int linux_ata_device::ata_command_interface(smart_command_set command, int selec // There are two different types of ioctls(). The HDIO_DRIVE_TASK // one is this: if (command==STATUS_CHECK || command==AUTOSAVE || command==AUTO_OFFLINE){ - int retval; - // NOT DOCUMENTED in /usr/src/linux/include/linux/hdreg.h. You // have to read the IDE driver source code. Sigh. // buff[0]: ATA COMMAND CODE REGISTER @@ -398,8 +402,8 @@ int linux_ata_device::ata_command_interface(smart_command_set command, int selec buff[4]=normal_lo; buff[5]=normal_hi; - if ((retval=ioctl(get_fd(), HDIO_DRIVE_TASK, buff))) { - if (retval==-EINVAL) { + if (ioctl(get_fd(), HDIO_DRIVE_TASK, buff)) { + if (errno==-EINVAL) { pout("Error SMART Status command via HDIO_DRIVE_TASK failed"); pout("Rebuild older linux 2.2 kernels with HDIO_DRIVE_TASK support added\n"); } @@ -493,6 +497,7 @@ int linux_ata_device::ata_command_interface(smart_command_set command, int selec #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 @@ -608,7 +613,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) { @@ -617,7 +622,10 @@ static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report, (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) @@ -827,29 +835,272 @@ class linux_scsi_device 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) { } - bool linux_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) { - int status = do_normal_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl); + int status = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); if (status < 0) return set_err(-status); return true; } +///////////////////////////////////////////////////////////////////////////// +/// PMC AacRAID support + +class linux_aacraid_device +:public scsi_device, + public /*extends */ linux_smart_device +{ +public: + linux_aacraid_device(smart_interface *intf, const char *dev_name, + unsigned int host, unsigned int channel, unsigned int device); + + virtual ~linux_aacraid_device() throw(); + + virtual bool open(); + + virtual bool scsi_pass_through(scsi_cmnd_io *iop); + +private: + //Device Host number + int aHost; + + //Channel(Lun) of the device + int aLun; + + //Id of the device + int aId; + +}; + +linux_aacraid_device::linux_aacraid_device(smart_interface *intf, + const char *dev_name, unsigned int host, unsigned int channel, unsigned int device) + : smart_device(intf,dev_name,"aacraid","aacraid"), + linux_smart_device(O_RDWR|O_NONBLOCK), + aHost(host), aLun(channel), aId(device) +{ + set_info().info_name = strprintf("%s [aacraid_disk_%02d_%02d_%d]",dev_name,aHost,aLun,aId); + set_info().dev_type = strprintf("aacraid,%d,%d,%d",aHost,aLun,aId); +} + +linux_aacraid_device::~linux_aacraid_device() throw() +{ +} + +bool linux_aacraid_device::open() +{ + //Create the character device name based on the host number + //Required for get stats from disks connected to different controllers + char dev_name[128]; + snprintf(dev_name, sizeof(dev_name), "/dev/aac%d", aHost); + + //Initial open of dev name to check if it exsists + int afd = ::open(dev_name,O_RDWR); + + if(afd < 0 && errno == ENOENT) { + + FILE *fp = fopen("/proc/devices","r"); + if(NULL == fp) + return set_err(errno,"cannot open /proc/devices:%s", + strerror(errno)); + + char line[256]; + int mjr = -1; + + while(fgets(line,sizeof(line),fp) !=NULL) { + int nc = -1; + if(sscanf(line,"%d aac%n",&mjr,&nc) == 1 + && nc > 0 && '\n' == line[nc]) + break; + mjr = -1; + } + + //work with /proc/devices is done + fclose(fp); + + if (mjr < 0) + return set_err(ENOENT, "aac entry not found in /proc/devices"); + + //Create misc device file in /dev/ used for communication with driver + if(mknod(dev_name,S_IFCHR,makedev(mjr,aHost))) + return set_err(errno,"cannot create %s:%s",dev_name,strerror(errno)); + + afd = ::open(dev_name,O_RDWR); + } + + if(afd < 0) + return set_err(errno,"cannot open %s:%s",dev_name,strerror(errno)); + + set_fd(afd); + return true; +} + +bool linux_aacraid_device::scsi_pass_through(scsi_cmnd_io *iop) +{ + int report = scsi_debugmode; + + if (report > 0) { + int k, j; + const unsigned char * ucp = iop->cmnd; + const char * np; + char buff[256]; + const int sz = (int)sizeof(buff); + + np = scsi_get_opcode_name(ucp[0]); + j = snprintf(buff, sz, " [%s: ", np ? np : ""); + for (k = 0; k < (int)iop->cmnd_len; ++k) + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " + "data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((const char *)iop->dxferp, + (trunc ? 256 : iop->dxfer_len) , 1); + } + else + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); + + pout("%s", buff); + } + + + //return test commands + if (iop->cmnd[0] == 0x00) + return true; + + user_aac_reply *pReply; + + #ifdef ENVIRONMENT64 + // Create user 64 bit request + user_aac_srb64 *pSrb; + uint8_t aBuff[sizeof(user_aac_srb64) + sizeof(user_aac_reply)] = {0,}; + + pSrb = (user_aac_srb64*)aBuff; + pSrb->count = sizeof(user_aac_srb64) - sizeof(user_sgentry64); + + #elif defined(ENVIRONMENT32) + //Create user 32 bit request + user_aac_srb32 *pSrb; + uint8_t aBuff[sizeof(user_aac_srb32) + sizeof(user_aac_reply)] = {0,}; + + pSrb = (user_aac_srb32*)aBuff; + pSrb->count = sizeof(user_aac_srb32) - sizeof(user_sgentry32); + #endif + + pSrb->function = SRB_FUNCTION_EXECUTE_SCSI; + //channel is 0 always + pSrb->channel = 0; + pSrb->id = aId; + pSrb->lun = aLun; + pSrb->timeout = 0; + + pSrb->retry_limit = 0; + pSrb->cdb_size = iop->cmnd_len; + + switch(iop->dxfer_dir) { + case DXFER_NONE: + pSrb->flags = SRB_NoDataXfer; + break; + case DXFER_FROM_DEVICE: + pSrb->flags = SRB_DataIn; + break; + case DXFER_TO_DEVICE: + pSrb->flags = SRB_DataOut; + break; + default: + pout("aacraid: bad dxfer_dir\n"); + return set_err(EINVAL, "aacraid: bad dxfer_dir\n"); + } + + if(iop->dxfer_len > 0) { + + #ifdef ENVIRONMENT64 + pSrb->sg64.count = 1; + pSrb->sg64.sg64[0].addr64.lo32 = ((intptr_t)iop->dxferp) & + 0x00000000ffffffff; + pSrb->sg64.sg64[0].addr64.hi32 = ((intptr_t)iop->dxferp) >> 32; + + pSrb->sg64.sg64[0].length = (uint32_t)iop->dxfer_len; + pSrb->count += pSrb->sg64.count * sizeof(user_sgentry64); + #elif defined(ENVIRONMENT32) + pSrb->sg32.count = 1; + pSrb->sg32.sg32[0].addr32 = (intptr_t)iop->dxferp; + + pSrb->sg32.sg32[0].length = (uint32_t)iop->dxfer_len; + pSrb->count += pSrb->sg32.count * sizeof(user_sgentry32); + #endif + + } + + pReply = (user_aac_reply*)(aBuff+pSrb->count); + + memcpy(pSrb->cdb,iop->cmnd,iop->cmnd_len); + + int rc = 0; + errno = 0; + rc = ioctl(get_fd(),FSACTL_SEND_RAW_SRB,pSrb); + + if (rc != 0) + return set_err(errno, "aacraid send_raw_srb: %d.%d = %s", + aLun, aId, strerror(errno)); + +/* see kernel aacraid.h and MSDN SCSI_REQUEST_BLOCK documentation */ +#define SRB_STATUS_SUCCESS 0x1 +#define SRB_STATUS_ERROR 0x4 +#define SRB_STATUS_NO_DEVICE 0x08 +#define SRB_STATUS_SELECTION_TIMEOUT 0x0a +#define SRB_STATUS_AUTOSENSE_VALID 0x80 + + iop->scsi_status = pReply->scsi_status; + + if (pReply->srb_status == (SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR) + && iop->scsi_status == SCSI_STATUS_CHECK_CONDITION) { + memcpy(iop->sensep, pReply->sense_data, pReply->sense_data_size); + iop->resp_sense_len = pReply->sense_data_size; + return true; /* request completed with sense data */ + } + + switch (pReply->srb_status & 0x3f) { + + case SRB_STATUS_SUCCESS: + return true; /* request completed successfully */ + + case SRB_STATUS_NO_DEVICE: + return set_err(EIO, "aacraid: Device %d %d does not exist", aLun, aId); + + case SRB_STATUS_SELECTION_TIMEOUT: + return set_err(EIO, "aacraid: Device %d %d not responding", aLun, aId); + + default: + return set_err(EIO, "aacraid result: %d.%d = 0x%x", + aLun, aId, pReply->srb_status); + } +} + + ///////////////////////////////////////////////////////////////////////////// /// LSI MegaRAID support @@ -867,7 +1118,7 @@ public: virtual bool open(); virtual bool close(); - + virtual bool scsi_pass_through(scsi_cmnd_io *iop); private: @@ -877,11 +1128,11 @@ private: int m_fd; bool (linux_megaraid_device::*pt_cmd)(int cdblen, void *cdb, int dataLen, void *data, - int senseLen, void *sense, int report); + int senseLen, void *sense, int report, int direction); bool megasas_cmd(int cdbLen, void *cdb, int dataLen, void *data, - int senseLen, void *sense, int report); + int senseLen, void *sense, int report, int direction); bool megadev_cmd(int cdbLen, void *cdb, int dataLen, void *data, - int senseLen, void *sense, int report); + int senseLen, void *sense, int report, int direction); }; linux_megaraid_device::linux_megaraid_device(smart_interface *intf, @@ -892,6 +1143,7 @@ linux_megaraid_device::linux_megaraid_device(smart_interface *intf, m_fd(-1), pt_cmd(0) { set_info().info_name = strprintf("%s [megaraid_disk_%02d]", dev_name, m_disknum); + set_info().dev_type = strprintf("megaraid,%d", tgt); } linux_megaraid_device::~linux_megaraid_device() throw() @@ -902,6 +1154,8 @@ linux_megaraid_device::~linux_megaraid_device() throw() smart_device * linux_megaraid_device::autodetect_open() { + int report = scsi_debugmode; + // Open device if (!open()) return this; @@ -924,67 +1178,60 @@ smart_device * linux_megaraid_device::autodetect_open() if (len < 36) return this; - printf("Got MegaRAID inquiry.. %s\n", req_buff+8); + if (report) + pout("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); - if (newdev) - // NOTE: 'this' is now owned by '*newdev' + { + // SAT? + 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; } - bool linux_megaraid_device::open() { char line[128]; - int mjr, n1; - FILE *fp; - int report = con->reportscsiioctl; - - if (!linux_smart_device::open()) - return false; + int mjr; + int report = scsi_debugmode; - /* Get device HBA */ - struct sg_scsi_id sgid; - if (ioctl(get_fd(), SG_GET_SCSI_ID, &sgid) == 0) { - m_hba = sgid.host_no; - } - else if (ioctl(get_fd(), SCSI_IOCTL_GET_BUS_NUMBER, &m_hba) != 0) { - int err = errno; + if (sscanf(get_dev_name(), "/dev/bus/%u", &m_hba) == 0) { + if (!linux_smart_device::open()) + return false; + /* Get device HBA */ + struct sg_scsi_id sgid; + if (ioctl(get_fd(), SG_GET_SCSI_ID, &sgid) == 0) { + m_hba = sgid.host_no; + } + 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 bus number"); + } // we dont need this device anymore linux_smart_device::close(); - return set_err(err, "can't get bus number"); } - /* Perform mknod of device ioctl node */ - fp = fopen("/proc/devices", "r"); + FILE * fp = fopen("/proc/devices", "r"); while (fgets(line, sizeof(line), fp) != NULL) { - 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)); - 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)); - if(report > 0) - printf("Creating /dev/megadev0 = %d\n", n1 >= 0 ? 0 : errno); - if (n1 >= 0 || errno == EEXIST) - break; - } + int 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)); + if(report > 0) + pout("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)); + if(report > 0) + pout("Creating /dev/megadev0 = %d\n", n1 >= 0 ? 0 : errno); + if (n1 >= 0 || errno == EEXIST) + break; + } } fclose(fp); @@ -1000,7 +1247,7 @@ bool linux_megaraid_device::open() linux_smart_device::close(); return set_err(err, "cannot open /dev/megaraid_sas_ioctl_node or /dev/megadev0"); } - + set_fd(m_fd); return true; } @@ -1009,12 +1256,13 @@ bool linux_megaraid_device::close() if (m_fd >= 0) ::close(m_fd); m_fd = -1; m_hba = 0; pt_cmd = 0; - return linux_smart_device::close(); + set_fd(m_fd); + return true; } bool linux_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop) { - int report = con->reportscsiioctl; + int report = scsi_debugmode; if (report > 0) { int k, j; @@ -1042,43 +1290,41 @@ bool linux_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop) pout("%s", buff); } - /* Controller rejects Enable SMART and Test Unit Ready */ + // Controller rejects Test Unit Ready if (iop->cmnd[0] == 0x00) return true; - if (iop->cmnd[0] == 0x85 && iop->cmnd[1] == 0x06) { - 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; - iop->sensep[0]=0x72; // response code - iop->sensep[7]=0x0e; // no idea what it is, copied from sat device answer - iop->sensep[8]=0x09; // - iop->sensep[17]=0x4f; // lm - iop->sensep[19]=0xc2; // lh - } - return true; - } + 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 // SAT16 WRITE LOG + && 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 // SAT12 WRITE LOG + && iop->cmnd[9] == ATA_SMART_CMD && iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) + { + if(!failuretest_permissive) + return set_err(ENOSYS, "SMART WRITE LOG SECTOR may cause problems, try with -T permissive to force"); + } if (pt_cmd == NULL) return false; - return (this->*pt_cmd)(iop->cmnd_len, iop->cmnd, + return (this->*pt_cmd)(iop->cmnd_len, iop->cmnd, iop->dxfer_len, iop->dxferp, - iop->max_sense_len, iop->sensep, report); + iop->max_sense_len, iop->sensep, report, iop->dxfer_dir); } /* Issue passthrough scsi command to PERC5/6 controllers */ bool linux_megaraid_device::megasas_cmd(int cdbLen, void *cdb, int dataLen, void *data, - int /*senseLen*/, void * /*sense*/, int /*report*/) + int /*senseLen*/, void * /*sense*/, int /*report*/, int dxfer_dir) { struct megasas_pthru_frame *pthru; struct megasas_iocpacket uio; - int rc; memset(&uio, 0, sizeof(uio)); - pthru = (struct megasas_pthru_frame *)uio.frame.raw; + pthru = &uio.frame.pthru; pthru->cmd = MFI_CMD_PD_SCSI_IO; pthru->cmd_status = 0xFF; pthru->scsi_status = 0x0; @@ -1086,22 +1332,39 @@ bool linux_megaraid_device::megasas_cmd(int cdbLen, void *cdb, pthru->lun = 0; pthru->cdb_len = cdbLen; pthru->timeout = 0; - pthru->flags = MFI_FRAME_DIR_READ; - pthru->sge_count = 1; - pthru->data_xfer_len = dataLen; - pthru->sgl.sge32[0].phys_addr = (intptr_t)data; - pthru->sgl.sge32[0].length = (uint32_t)dataLen; + switch (dxfer_dir) { + case DXFER_NONE: + pthru->flags = MFI_FRAME_DIR_NONE; + break; + case DXFER_FROM_DEVICE: + pthru->flags = MFI_FRAME_DIR_READ; + break; + case DXFER_TO_DEVICE: + pthru->flags = MFI_FRAME_DIR_WRITE; + break; + default: + pout("megasas_cmd: bad dxfer_dir\n"); + return set_err(EINVAL, "megasas_cmd: bad dxfer_dir\n"); + } + + if (dataLen > 0) { + pthru->sge_count = 1; + pthru->data_xfer_len = dataLen; + pthru->sgl.sge32[0].phys_addr = (intptr_t)data; + pthru->sgl.sge32[0].length = (uint32_t)dataLen; + } memcpy(pthru->cdb, cdb, cdbLen); uio.host_no = m_hba; - uio.sge_count = 1; - uio.sgl_off = offsetof(struct megasas_pthru_frame, sgl); - uio.sgl[0].iov_base = data; - uio.sgl[0].iov_len = dataLen; + if (dataLen > 0) { + uio.sge_count = 1; + uio.sgl_off = offsetof(struct megasas_pthru_frame, sgl); + uio.sgl[0].iov_base = data; + uio.sgl[0].iov_len = dataLen; + } - rc = 0; errno = 0; - rc = ioctl(m_fd, MEGASAS_IOC_FIRMWARE, &uio); + int rc = ioctl(m_fd, MEGASAS_IOC_FIRMWARE, &uio); if (pthru->cmd_status || rc != 0) { if (pthru->cmd_status == 12) { return set_err(EIO, "megasas_cmd: Device %d does not exist\n", m_disknum); @@ -1116,14 +1379,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*/, int /* dir */) { struct uioctl_t uio; int rc; - sense = NULL; - senseLen = 0; - /* Don't issue to the controller */ if (m_disknum == 7) return false; @@ -1189,7 +1449,7 @@ linux_cciss_device::linux_cciss_device(smart_interface * intf, bool linux_cciss_device::scsi_pass_through(scsi_cmnd_io * iop) { - int status = cciss_io_interface(get_fd(), m_disknum, iop, con->reportscsiioctl); + int status = cciss_io_interface(get_fd(), m_disknum, iop, scsi_debugmode); if (status < 0) return set_err(-status); return true; @@ -1208,7 +1468,8 @@ public: enum escalade_type_t { AMCC_3WARE_678K, AMCC_3WARE_678K_CHAR, - AMCC_3WARE_9000_CHAR + AMCC_3WARE_9000_CHAR, + AMCC_3WARE_9700_CHAR }; linux_escalade_device(smart_interface * intf, const char * dev_name, @@ -1236,7 +1497,8 @@ linux_escalade_device::linux_escalade_device(smart_interface * intf, const char #define MAJOR_STRING_LENGTH 3 #define DEVICE_STRING_LENGTH 32 #define NODE_STRING_LENGTH 16 -int setup_3ware_nodes(const char *nodename, const char *driver_name) { +static int setup_3ware_nodes(const char *nodename, const char *driver_name) +{ int tw_major = 0; int index = 0; char majorstring[MAJOR_STRING_LENGTH+1]; @@ -1252,7 +1514,6 @@ int setup_3ware_nodes(const char *nodename, const char *driver_name) { int selinux_enforced = security_getenforce(); #endif - /* First try to open up /proc/devices */ if (!(file = fopen("/proc/devices", "r"))) { pout("Error opening /proc/devices to check/create 3ware device nodes\n"); @@ -1292,7 +1553,7 @@ int setup_3ware_nodes(const char *nodename, const char *driver_name) { #endif /* Now check if nodes are correct */ for (index=0; index<16; index++) { - sprintf(nodestring, "/dev/%s%d", nodename, index); + snprintf(nodestring, sizeof(nodestring), "/dev/%s%d", nodename, index); #ifdef WITH_SELINUX /* Get context of the node and set it as the default */ if (selinux_enabled) { @@ -1381,12 +1642,17 @@ int setup_3ware_nodes(const char *nodename, const char *driver_name) { bool linux_escalade_device::open() { - if (m_escalade_type == AMCC_3WARE_9000_CHAR || m_escalade_type == AMCC_3WARE_678K_CHAR) { + if (m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR || + m_escalade_type == AMCC_3WARE_678K_CHAR) { // the device nodes for these controllers are dynamically assigned, // so we need to check that they exist with the correct major // numbers and if not, create them - const char * node = (m_escalade_type == AMCC_3WARE_9000_CHAR ? "twa" : "twe" ); - const char * driver = (m_escalade_type == AMCC_3WARE_9000_CHAR ? "3w-9xxx": "3w-xxxx"); + const char * node = (m_escalade_type == AMCC_3WARE_9700_CHAR ? "twl" : + m_escalade_type == AMCC_3WARE_9000_CHAR ? "twa" : + "twe" ); + const char * driver = (m_escalade_type == AMCC_3WARE_9700_CHAR ? "3w-sas" : + m_escalade_type == AMCC_3WARE_9000_CHAR ? "3w-9xxx" : + "3w-xxxx" ); if (setup_3ware_nodes(node, driver)) return set_err((errno ? errno : ENXIO), "setup_3ware_nodes(\"%s\", \"%s\") failed", node, driver); } @@ -1420,7 +1686,6 @@ bool linux_escalade_device::open() // 0 if the command succeeded and disk SMART status is "OK" // 1 if the command succeeded and disk SMART status is "FAILING" - /* 512 is the max payload size: increase if needed */ #define BUFFER_LEN_678K ( sizeof(TW_Ioctl) ) // 1044 unpacked, 1041 packed #define BUFFER_LEN_678K_CHAR ( sizeof(TW_New_Ioctl)+512-1 ) // 1539 unpacked, 1536 packed @@ -1453,7 +1718,7 @@ bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE); // TODO: Handle controller differences by different classes - if (m_escalade_type==AMCC_3WARE_9000_CHAR) { + if (m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) { tw_ioctl_apache = (TW_Ioctl_Buf_Apache *)ioctl_buffer; tw_ioctl_apache->driver_command.control_code = TW_IOCTL_FIRMWARE_PASS_THROUGH; tw_ioctl_apache->driver_command.buffer_length = 512; /* payload size */ @@ -1515,7 +1780,8 @@ bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out // in dwords by 1 to account for the 64-bit single sgl 'address' // field. Note that this doesn't agree with the typedefs but it's // right (agree with kernel driver behavior/typedefs). - if (m_escalade_type==AMCC_3WARE_9000_CHAR && sizeof(long)==8) + if ((m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) + && sizeof(long) == 8) passthru->size++; } else if (in.direction == ata_cmd_in::no_data) { @@ -1527,7 +1793,7 @@ bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out passthru->sector_count = 0x0; } else if (in.direction == ata_cmd_in::data_out) { - if (m_escalade_type == AMCC_3WARE_9000_CHAR) + if (m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) memcpy(tw_ioctl_apache->data_buffer, in.buffer, in.size); else if (m_escalade_type == AMCC_3WARE_678K_CHAR) memcpy(tw_ioctl_char->data_buffer, in.buffer, in.size); @@ -1540,15 +1806,16 @@ bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out passthru->byte0.sgloff = 0x5; passthru->size = 0x7; // TODO: Other value for multi-sector ? passthru->param = 0xF; // PIO data write - if (m_escalade_type==AMCC_3WARE_9000_CHAR && sizeof(long)==8) + if ((m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) + && sizeof(long) == 8) passthru->size++; } else - set_err(EINVAL); + return set_err(EINVAL); // Now send the command down through an ioctl() int ioctlreturn; - if (m_escalade_type==AMCC_3WARE_9000_CHAR) + if (m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) ioctlreturn=ioctl(get_fd(), TW_IOCTL_FIRMWARE_PASS_THROUGH, tw_ioctl_apache); else if (m_escalade_type==AMCC_3WARE_678K_CHAR) ioctlreturn=ioctl(get_fd(), TW_CMD_PACKET_WITH_DATA, tw_ioctl_char); @@ -1599,7 +1866,7 @@ bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out // If this is a read data command, copy data to output buffer if (readdata) { - if (m_escalade_type==AMCC_3WARE_9000_CHAR) + if (m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) memcpy(in.buffer, tw_ioctl_apache->data_buffer, in.size); else if (m_escalade_type==AMCC_3WARE_678K_CHAR) memcpy(in.buffer, tw_ioctl_char->data_buffer, in.size); @@ -1608,7 +1875,7 @@ bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out } // Return register values - { + if (passthru) { ata_out_regs_48bit & r = out.out_regs; r.error = passthru->features; r.sector_count_16 = passthru->sector_count; @@ -1621,91 +1888,47 @@ bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out // 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 true; } - ///////////////////////////////////////////////////////////////////////////// /// Areca RAID support -class linux_areca_device -: public /*implements*/ ata_device_with_command_set, +/////////////////////////////////////////////////////////////////// +// SATA(ATA) device behind Areca RAID Controller +class linux_areca_ata_device +: public /*implements*/ areca_ata_device, public /*extends*/ linux_smart_device { public: - linux_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. + linux_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + virtual smart_device * autodetect_open(); + virtual bool arcmsr_lock(); + virtual bool arcmsr_unlock(); + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); }; - -// 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 interface to the Areca "arcmsr" driver. It allows ATA -// commands to be passed through the SCSI driver. -// DETAILED DESCRIPTION OF ARGUMENTS -// fd: is the file descriptor provided by open() -// disknum is the disk number (0 to 15) in the RAID array -// 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" - - -/*DeviceType*/ -#define ARECA_SATA_RAID 0x90000000 -/*FunctionCode*/ -#define FUNCTION_READ_RQBUFFER 0x0801 -#define FUNCTION_WRITE_WQBUFFER 0x0802 -#define FUNCTION_CLEAR_RQBUFFER 0x0803 -#define FUNCTION_CLEAR_WQBUFFER 0x0804 - -/* ARECA IO CONTROL CODE*/ -#define ARCMSR_IOCTL_READ_RQBUFFER (ARECA_SATA_RAID | FUNCTION_READ_RQBUFFER) -#define ARCMSR_IOCTL_WRITE_WQBUFFER (ARECA_SATA_RAID | FUNCTION_WRITE_WQBUFFER) -#define ARCMSR_IOCTL_CLEAR_RQBUFFER (ARECA_SATA_RAID | FUNCTION_CLEAR_RQBUFFER) -#define ARCMSR_IOCTL_CLEAR_WQBUFFER (ARECA_SATA_RAID | FUNCTION_CLEAR_WQBUFFER) -#define ARECA_SIG_STR "ARCMSR" - -// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver -typedef struct _SRB_IO_CONTROL -{ - unsigned int HeaderLength; - unsigned char Signature[8]; - unsigned int Timeout; - unsigned int ControlCode; - unsigned int ReturnCode; - unsigned int Length; -} sSRB_IO_CONTROL; - -typedef struct _SRB_BUFFER +/////////////////////////////////////////////////////////////////// +// SAS(SCSI) device behind Areca RAID Controller +class linux_areca_scsi_device +: public /*implements*/ areca_scsi_device, + public /*extends*/ linux_smart_device { - sSRB_IO_CONTROL srbioctl; - unsigned char ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware -} sSRB_BUFFER; +public: + linux_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + virtual smart_device * autodetect_open(); + virtual bool arcmsr_lock(); + virtual bool arcmsr_unlock(); + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); +}; // Looks in /proc/scsi to suggest correct areca devices -// If hint not NULL, return device path guess -int find_areca_in_proc(char *hint) { - +static int find_areca_in_proc() +{ const char* proc_format_string="host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n"; // check data formwat @@ -1743,9 +1966,6 @@ int find_areca_in_proc(char *hint) { dev++; if (id == 16 && type == 3) { // devices with id=16 and type=3 might be Areca controllers - if (!found && hint) { - sprintf(hint, "/dev/sg%d", dev); - } pout("Device /dev/sg%d appears to be an Areca controller.\n", dev); found++; } @@ -1754,473 +1974,115 @@ int find_areca_in_proc(char *hint) { return 0; } - - -void dumpdata( unsigned char *block, int len) -{ - int ln = (len / 16) + 1; // total line# - unsigned char c; - int pos = 0; - - printf(" Address = %p, Length = (0x%x)%d\n", block, len, len); - printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII \n"); - printf("=====================================================================\n"); - - for ( int l = 0; l < ln && len; l++ ) - { - // printf the line# and the HEX data - // if a line data length < 16 then append the space to the tail of line to reach 16 chars - printf("%02X | ", l); - for ( pos = 0; pos < 16 && len; pos++, len-- ) - { - c = block[l*16+pos]; - printf("%02X ", c); - } - - if ( pos < 16 ) - { - for ( int loop = pos; loop < 16; loop++ ) - { - printf(" "); - } - } - - // print ASCII char - for ( int loop = 0; loop < pos; loop++ ) - { - c = block[l*16+loop]; - if ( c >= 0x20 && c <= 0x7F ) - { - printf("%c", c); - } - else - { - printf("."); - } - } - printf("\n"); - } - printf("=====================================================================\n"); +// Areca RAID Controller(SATA Disk) +linux_areca_ata_device::linux_areca_ata_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) +{ + set_disknum(disknum); + set_encnum(encnum); + set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } +smart_device * linux_areca_ata_device::autodetect_open() +{ + // autodetect device type + int is_ata = arcmsr_get_dev_type(); + if(is_ata < 0) + { + set_err(EIO); + return this; + } + if(is_ata == 1) + { + // SATA device + return this; + } -int arcmsr_command_handler(int fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len, void *ext_data /* reserved for further use */) -{ - ARGUSED(ext_data); - - int ioctlreturn = 0; - sSRB_BUFFER sBuf; - struct scsi_cmnd_io io_hdr; - int dir = DXFER_TO_DEVICE; - - UINT8 cdb[10]; - UINT8 sense[32]; - - unsigned char *areca_return_packet; - int total = 0; - int expected = -1; - unsigned char return_buff[2048]; - unsigned char *ptr = &return_buff[0]; - memset(return_buff, 0, sizeof(return_buff)); - - memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); - memset(&io_hdr, 0, sizeof(io_hdr)); - memset(cdb, 0, sizeof(cdb)); - memset(sense, 0, sizeof(sense)); - - - sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL); - memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR)); - sBuf.srbioctl.Timeout = 10000; - sBuf.srbioctl.ControlCode = ARCMSR_IOCTL_READ_RQBUFFER; - - switch ( arcmsr_cmd ) - { - // command for writing data to driver - case ARCMSR_IOCTL_WRITE_WQBUFFER: - if ( data && data_len ) - { - sBuf.srbioctl.Length = data_len; - memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); - } - // commands for clearing related buffer of driver - case ARCMSR_IOCTL_CLEAR_RQBUFFER: - case ARCMSR_IOCTL_CLEAR_WQBUFFER: - cdb[0] = 0x3B; //SCSI_WRITE_BUF command; - break; - // command for reading data from driver - case ARCMSR_IOCTL_READ_RQBUFFER: - cdb[0] = 0x3C; //SCSI_READ_BUF command; - dir = DXFER_FROM_DEVICE; - break; - default: - // unknown arcmsr commands - return -1; - } - - cdb[1] = 0x01; - cdb[2] = 0xf0; - // - // cdb[5][6][7][8] areca defined command code( to/from driver ) - // - cdb[5] = (char)( arcmsr_cmd >> 24); - cdb[6] = (char)( arcmsr_cmd >> 16); - cdb[7] = (char)( arcmsr_cmd >> 8); - cdb[8] = (char)( arcmsr_cmd & 0x0F ); - - io_hdr.dxfer_dir = dir; - io_hdr.dxfer_len = sizeof(sBuf); - io_hdr.dxferp = (unsigned char *)&sBuf; - io_hdr.cmnd = cdb; - io_hdr.cmnd_len = sizeof(cdb); - io_hdr.sensep = sense; - io_hdr.max_sense_len = sizeof(sense); - io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; - - while ( 1 ) - { - ioctlreturn = do_normal_scsi_cmnd_io(fd, &io_hdr, 0); - if ( ioctlreturn || io_hdr.scsi_status ) - { - // errors found - break; - } - - if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER ) - { - // if succeeded, just returns the length of outgoing data - return data_len; - } - - if ( sBuf.srbioctl.Length ) - { - //dumpdata(&sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); - memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); - ptr += sBuf.srbioctl.Length; - total += sBuf.srbioctl.Length; - // the returned bytes enough to compute payload length ? - if ( expected < 0 && total >= 5 ) - { - areca_return_packet = (unsigned char *)&return_buff[0]; - if ( areca_return_packet[0] == 0x5E && - areca_return_packet[1] == 0x01 && - areca_return_packet[2] == 0x61 ) - { - // valid header, let's compute the returned payload length, - // we expected the total length is - // payload + 3 bytes header + 2 bytes length + 1 byte checksum - expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; - } - } - - if ( total >= 7 && total >= expected ) - { - //printf("total bytes received = %d, expected length = %d\n", total, expected); - - // ------ Okay! we received enough -------- - break; - } - } - } + // SAS device + smart_device_auto_ptr newdev(new linux_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum())); + close(); + delete this; + newdev->open(); // TODO: Can possibly pass open fd - // Deal with the different error cases - if ( ioctlreturn ) - { - printf("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn); - return -2; - } + return newdev.release(); +} +int linux_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + int ioctlreturn = 0; - if ( io_hdr.scsi_status ) - { - printf("io_hdr.scsi_status with write buffer failed code = %x\n", io_hdr.scsi_status); - return -3; - } + if(!is_open()) { + if(!open()){ + find_areca_in_proc(); + } + } + ioctlreturn = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); + if ( ioctlreturn || iop->scsi_status ) + { + // errors found + return -1; + } - if ( data ) - { - memcpy(data, return_buff, total); - } + return ioctlreturn; +} - return total; +bool linux_areca_ata_device::arcmsr_lock() +{ + return true; } +bool linux_areca_ata_device::arcmsr_unlock() +{ + return true; +} -linux_areca_device::linux_areca_device(smart_interface * intf, const char * dev_name, int disknum) +// Areca RAID Controller(SAS Device) +linux_areca_scsi_device::linux_areca_scsi_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) + linux_smart_device(O_RDWR | O_EXCL | O_NONBLOCK) { - set_info().info_name = strprintf("%s [areca_%02d]", dev_name, disknum); + set_disknum(disknum); + set_encnum(encnum); + 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) -{ - // ATA input registers - typedef struct _ATA_INPUT_REGISTERS - { - unsigned char features; - unsigned char sector_count; - unsigned char sector_number; - unsigned char cylinder_low; - unsigned char cylinder_high; - unsigned char device_head; - unsigned char command; - unsigned char reserved[8]; - unsigned char data[512]; // [in/out] buffer for outgoing/incoming data - } sATA_INPUT_REGISTERS; - - // ATA output registers - // Note: The output registers is re-sorted for areca internal use only - typedef struct _ATA_OUTPUT_REGISTERS - { - unsigned char error; - unsigned char status; - unsigned char sector_count; - unsigned char sector_number; - unsigned char cylinder_low; - unsigned char cylinder_high; - }sATA_OUTPUT_REGISTERS; - - // Areca packet format for outgoing: - // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 - // B[3~4] : 2 bytes command length + variant data length, little endian - // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c - // B[6~last-1] : variant bytes payload data - // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) - // - // - // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte - // +--------------------------------------------------------------------------------+ - // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | - // +--------------------------------------------------------------------------------+ - // - - //Areca packet format for incoming: - // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 - // B[3~4] : 2 bytes payload length, little endian - // B[5~last-1] : variant bytes returned payload data - // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) - // - // - // header 3 bytes length 2 bytes payload data x bytes cs 1 byte - // +-------------------------------------------------------------------+ - // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | - // +-------------------------------------------------------------------+ - unsigned char areca_packet[640]; - int areca_packet_len = sizeof(areca_packet); - unsigned char cs = 0; - - sATA_INPUT_REGISTERS *ata_cmd; - - // For debugging -#if 0 - memset(sInq, 0, sizeof(sInq)); - scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq)); - dumpdata((unsigned char *)sInq, sizeof(sInq)); -#endif - memset(areca_packet, 0, areca_packet_len); - - // ----- BEGIN TO SETUP HEADERS ------- - areca_packet[0] = 0x5E; - areca_packet[1] = 0x01; - areca_packet[2] = 0x61; - areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); - 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; - } - else if ( command == WRITE_LOG ) - { - // the commands will write data - areca_packet[6] = 0x14; - } - else - { - // 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 ) - { - 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; - }; - - areca_packet[11] = m_disknum - 1; // drive number - - // ----- BEGIN TO SETUP CHECKSUM ----- - for ( int loop = 3; loop < areca_packet_len - 1; loop++ ) - { - cs += areca_packet[loop]; - } - areca_packet[areca_packet_len-1] = cs; - - // ----- BEGIN TO SEND TO ARECA DRIVER ------ - int expected = 0; - unsigned char return_buff[2048]; - memset(return_buff, 0, sizeof(return_buff)); - - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0, NULL); - if (expected==-3) { - find_areca_in_proc(NULL); - return -1; - } - - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0, NULL); - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len, NULL); - if ( expected > 0 ) - { - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff), NULL); - } - if ( expected < 0 ) - { - return -1; - } - - // ----- VERIFY THE CHECKSUM ----- - cs = 0; - for ( int loop = 3; loop < expected - 1; loop++ ) - { - cs += return_buff[loop]; - } - - if ( return_buff[expected - 1] != cs ) - { - errno = EIO; - return -1; - } +smart_device * linux_areca_scsi_device::autodetect_open() +{ + return this; +} - 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; - } +int linux_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + int ioctlreturn = 0; - // returns with data - if ( command == READ_VALUES || - command == READ_THRESHOLDS || - command == READ_LOG || - command == IDENTIFY || - command == PIDENTIFY ) - { - memcpy(data, &return_buff[7], 512); - } + if(!is_open()) { + if(!open()){ + find_areca_in_proc(); + } + } - if ( command == CHECK_POWER_MODE ) - { - data[0] = ata_out->sector_count; - } + ioctlreturn = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); + if ( ioctlreturn || iop->scsi_status ) + { + // errors found + return -1; + } - if ( command == STATUS_CHECK && - ( ata_out->cylinder_low == 0xF4 && ata_out->cylinder_high == 0x2C ) ) - { - return 1; - } + return ioctlreturn; +} - return 0; +bool linux_areca_scsi_device::arcmsr_lock() +{ + return true; } +bool linux_areca_scsi_device::arcmsr_unlock() +{ + return true; +} ///////////////////////////////////////////////////////////////////////////// /// Marvell support @@ -2369,7 +2231,6 @@ int linux_marvell_device::ata_command_interface(smart_command_set command, int s return 0; } - ///////////////////////////////////////////////////////////////////////////// /// Highpoint RAID support @@ -2500,17 +2361,17 @@ int linux_highpoint_device::ata_command_interface(smart_command_set command, int if (command==WRITE_LOG) { unsigned char task[4*sizeof(int)+sizeof(ide_task_request_t)+512]; - unsigned int *hpt = (unsigned int *)task; + unsigned int *hpt_tf = (unsigned int *)task; ide_task_request_t *reqtask = (ide_task_request_t *)(&task[4*sizeof(int)]); task_struct_t *taskfile = (task_struct_t *)reqtask->io_ports; int retval; memset(task, 0, sizeof(task)); - hpt[0] = m_hpt_data[0]; // controller id - hpt[1] = m_hpt_data[1]; // channel number - hpt[3] = m_hpt_data[2]; // pmport number - hpt[2] = HDIO_DRIVE_TASKFILE; // real hd ioctl + hpt_tf[0] = m_hpt_data[0]; // controller id + hpt_tf[1] = m_hpt_data[1]; // channel number + hpt_tf[3] = m_hpt_data[2]; // pmport number + hpt_tf[2] = HDIO_DRIVE_TASKFILE; // real hd ioctl taskfile->data = 0; taskfile->feature = ATA_SMART_WRITE_LOG_SECTOR; @@ -2577,13 +2438,13 @@ int linux_highpoint_device::ata_command_interface(smart_command_set command, int #if 1 if (command==IDENTIFY || command==PIDENTIFY) { unsigned char deviceid[4*sizeof(int)+512*sizeof(char)]; - unsigned int *hpt = (unsigned int *)deviceid; + unsigned int *hpt_id = (unsigned int *)deviceid; - hpt[0] = m_hpt_data[0]; // controller id - hpt[1] = m_hpt_data[1]; // channel number - hpt[3] = m_hpt_data[2]; // pmport number + hpt_id[0] = m_hpt_data[0]; // controller id + hpt_id[1] = m_hpt_data[1]; // channel number + hpt_id[3] = m_hpt_data[2]; // pmport number - hpt[2] = HDIO_GET_IDENTITY; + hpt_id[2] = HDIO_GET_IDENTITY; if (!ioctl(get_fd(), HPTIO_CTL, deviceid) && (deviceid[4*sizeof(int)] & 0x8000)) buff[0]=(command==IDENTIFY)?ATA_IDENTIFY_PACKET_DEVICE:ATA_IDENTIFY_DEVICE; } @@ -2602,7 +2463,6 @@ int linux_highpoint_device::ata_command_interface(smart_command_set command, int return 0; } - #if 0 // TODO: Migrate from 'smart_command_set' to 'ata_in_regs' OR remove the function // Utility function for printing warnings void printwarning(smart_command_set command){ @@ -2634,7 +2494,6 @@ void printwarning(smart_command_set command){ } #endif - ///////////////////////////////////////////////////////////////////////////// /// SCSI open with autodetection support @@ -2645,8 +2504,13 @@ smart_device * linux_scsi_device::autodetect_open() 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() @@ -2667,53 +2531,64 @@ smart_device * linux_scsi_device::autodetect_open() 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(); 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()); + "you may need to replace %s with /dev/twlN, /dev/twaN or /dev/tweN", get_dev_name()); return this; } + // DELL? - if (!memcmp(req_buff + 8, "DELL PERC", 12) || !memcmp(req_buff + 8, "MegaRAID", 8)) { + if (!memcmp(req_buff + 8, "DELL PERC", 12) || !memcmp(req_buff + 8, "MegaRAID", 8) + || !memcmp(req_buff + 16, "PERC H700", 9) || !memcmp(req_buff + 8, "LSI\0",4) + ) { 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; } - ////////////////////////////////////////////////////////////////////// // USB bridge ID detection @@ -2762,12 +2637,11 @@ static bool get_usb_id(const char * name, unsigned short & vendor_id, && read_id(dir + "/bcdDevice", version) )) return false; - if (con->reportscsiioctl > 1) + if (scsi_debugmode > 1) pout("USB ID = 0x%04x:0x%04x (0x%03x)\n", vendor_id, product_id, version); return true; } - ////////////////////////////////////////////////////////////////////// /// Linux interface @@ -2775,6 +2649,8 @@ class linux_smart_interface : public /*implements*/ smart_interface { public: + virtual std::string get_os_version_str(); + virtual std::string get_app_examples(const char * appname); virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, @@ -2794,10 +2670,22 @@ protected: private: bool get_dev_list(smart_device_list & devlist, const char * pattern, bool scan_ata, bool scan_scsi, const char * req_type, bool autodetect); - + bool get_dev_megasas(smart_device_list & devlist); smart_device * missing_option(const char * opt); + int megasas_dcmd_cmd(int bus_no, uint32_t opcode, void *buf, + size_t bufsize, uint8_t *mbox, size_t mboxlen, uint8_t *statusp); + int megasas_pd_add_list(int bus_no, smart_device_list & devlist); }; +std::string linux_smart_interface::get_os_version_str() +{ + struct utsname u; + if (!uname(&u)) + return strprintf("%s-linux-%s", u.machine, u.release); + else + return SMARTMONTOOLS_BUILD_HOST; +} + std::string linux_smart_interface::get_app_examples(const char * appname) { if (!strcmp(appname, "smartctl")) @@ -2805,10 +2693,8 @@ std::string linux_smart_interface::get_app_examples(const char * appname) return ""; } - // we are going to take advantage of the fact that Linux's devfs will only -// have device entries for devices that exist. So if we get the equivalent of -// ls /dev/hd[a-t], we have all the ATA devices on the system +// have device entries for devices that exist. bool linux_smart_interface::get_dev_list(smart_device_list & devlist, const char * pattern, bool scan_ata, bool scan_scsi, const char * req_type, bool autodetect) @@ -2839,7 +2725,7 @@ bool linux_smart_interface::get_dev_list(smart_device_list & devlist, } // did we find too many paths? - const int max_pathc = 32; + const int max_pathc = 1024; int n = (int)globbuf.gl_pathc; if (n > max_pathc) { pout("glob(3) found %d > MAX=%d devices matching pattern %s: ignoring %d paths\n", @@ -2892,17 +2778,70 @@ bool linux_smart_interface::get_dev_list(smart_device_list & devlist, 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); } } // free memory globfree(&globbuf); + return true; +} + +// getting devices from LSI SAS MegaRaid, if available +bool linux_smart_interface::get_dev_megasas(smart_device_list & devlist) +{ + /* Scanning of disks on MegaRaid device */ + /* Perform mknod of device ioctl node */ + int mjr, n1; + char line[128]; + bool scan_megasas = false; + FILE * fp = fopen("/proc/devices", "r"); + while (fgets(line, sizeof(line), fp) != NULL) { + n1=0; + if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) { + scan_megasas = true; + n1=mknod("/dev/megaraid_sas_ioctl_node", S_IFCHR, makedev(mjr, 0)); + if(scsi_debugmode > 0) + pout("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno); + if (n1 >= 0 || errno == EEXIST) + break; + } + } + fclose(fp); + if(!scan_megasas) + return false; + + // getting bus numbers with megasas devices + // we are using sysfs to get list of all scsi hosts + DIR * dp = opendir ("/sys/class/scsi_host/"); + if (dp != NULL) + { + struct dirent *ep; + while ((ep = readdir (dp)) != NULL) { + unsigned int host_no = 0; + if (!sscanf(ep->d_name, "host%u", &host_no)) + continue; + /* proc_name should be megaraid_sas */ + char sysfsdir[256]; + snprintf(sysfsdir, sizeof(sysfsdir) - 1, + "/sys/class/scsi_host/host%u/proc_name", host_no); + if((fp = fopen(sysfsdir, "r")) == NULL) + continue; + if(fgets(line, sizeof(line), fp) != NULL && !strncmp(line,"megaraid_sas",12)) { + megasas_pd_add_list(host_no, devlist); + } + fclose(fp); + } + (void) closedir (dp); + } else { /* sysfs not mounted ? */ + for(unsigned i = 0; i <=16; i++) // trying to add devices on first 16 buses + megasas_pd_add_list(i, devlist); + } return true; } @@ -2918,14 +2857,21 @@ bool linux_smart_interface::scan_smart_devices(smart_device_list & devlist, 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); + // get device list from the megaraid device + get_dev_megasas(devlist); + } // if we found traditional links, we are done if (devlist.size() > 0) @@ -2952,77 +2898,160 @@ smart_device * linux_smart_interface::missing_option(const char * opt) return 0; } -// Return true if STR starts with PREFIX. -static bool str_starts_with(const char * str, const char * prefix) +int +linux_smart_interface::megasas_dcmd_cmd(int bus_no, uint32_t opcode, void *buf, + size_t bufsize, uint8_t *mbox, size_t mboxlen, uint8_t *statusp) +{ + struct megasas_iocpacket ioc; + + if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) || + (mbox == NULL && mboxlen != 0)) + { + errno = EINVAL; + return (-1); + } + + bzero(&ioc, sizeof(ioc)); + struct megasas_dcmd_frame * dcmd = &ioc.frame.dcmd; + ioc.host_no = bus_no; + if (mbox) + bcopy(mbox, dcmd->mbox.w, mboxlen); + dcmd->cmd = MFI_CMD_DCMD; + dcmd->timeout = 0; + dcmd->flags = 0; + dcmd->data_xfer_len = bufsize; + dcmd->opcode = opcode; + + if (bufsize > 0) { + dcmd->sge_count = 1; + dcmd->data_xfer_len = bufsize; + dcmd->sgl.sge32[0].phys_addr = (intptr_t)buf; + dcmd->sgl.sge32[0].length = (uint32_t)bufsize; + ioc.sge_count = 1; + ioc.sgl_off = offsetof(struct megasas_dcmd_frame, sgl); + ioc.sgl[0].iov_base = buf; + ioc.sgl[0].iov_len = bufsize; + } + + int fd; + if ((fd = ::open("/dev/megaraid_sas_ioctl_node", O_RDWR)) <= 0) { + return (errno); + } + + int r = ioctl(fd, MEGASAS_IOC_FIRMWARE, &ioc); + ::close(fd); + if (r < 0) { + return (r); + } + + if (statusp != NULL) + *statusp = dcmd->cmd_status; + else if (dcmd->cmd_status != MFI_STAT_OK) { + fprintf(stderr, "command %x returned error status %x\n", + opcode, dcmd->cmd_status); + errno = EIO; + return (-1); + } + return (0); +} + +int +linux_smart_interface::megasas_pd_add_list(int bus_no, smart_device_list & devlist) +{ + /* + * Keep fetching the list in a loop until we have a large enough + * buffer to hold the entire list. + */ + megasas_pd_list * list = 0; + for (unsigned list_size = 1024; ; ) { + list = reinterpret_cast(realloc(list, list_size)); + if (!list) + throw std::bad_alloc(); + bzero(list, list_size); + if (megasas_dcmd_cmd(bus_no, MFI_DCMD_PD_GET_LIST, list, list_size, NULL, 0, + NULL) < 0) + { + free(list); + return (-1); + } + if (list->size <= list_size) + break; + list_size = list->size; + } + + // adding all SCSI devices + for (unsigned i = 0; i < list->count; i++) { + if(list->addr[i].scsi_dev_type) + continue; /* non disk device found */ + char line[128]; + snprintf(line, sizeof(line) - 1, "/dev/bus/%d", bus_no); + smart_device * dev = new linux_megaraid_device(this, line, 0, list->addr[i].device_id); + devlist.push_back(dev); + } + free(list); + return (0); +} + +// Return kernel release as integer ("2.6.31" -> 206031) +static unsigned get_kernel_release() { - return !strncmp(str, prefix, strlen(prefix)); + struct utsname u; + if (uname(&u)) + return 0; + unsigned x = 0, y = 0, z = 0; + if (!(sscanf(u.release, "%u.%u.%u", &x, &y, &z) == 3 + && x < 100 && y < 100 && z < 1000 )) + return 0; + return x * 100000 + y * 1000 + z; } // Guess device type (ata or scsi) based on device name (Linux // specific) SCSI device name in linux can be sd, sr, scd, st, nst, // osst, nosst and sg. -static const char * lin_dev_prefix = "/dev/"; -static const char * lin_dev_ata_disk_plus = "h"; -static const char * lin_dev_ata_devfs_disk_plus = "ide/"; -static const char * lin_dev_scsi_devfs_disk_plus = "scsi/"; -static const char * lin_dev_scsi_disk_plus = "s"; -static const char * lin_dev_scsi_tape1 = "ns"; -static const char * lin_dev_scsi_tape2 = "os"; -static const char * lin_dev_scsi_tape3 = "nos"; -static const char * lin_dev_3ware_9000_char = "twa"; -static const char * lin_dev_3ware_678k_char = "twe"; -static const char * lin_dev_cciss_dir = "cciss/"; -static const char * lin_dev_areca = "sg"; - smart_device * linux_smart_interface::autodetect_smart_device(const char * name) { - const char * dev_name = name; // TODO: Remove this hack - int dev_prefix_len = strlen(lin_dev_prefix); + const char * test_name = name; - // if dev_name null, or string length zero - int len; - if (!dev_name || !(len = strlen(dev_name))) - return 0; - - // Dereference if /dev/disk/by-*/* symlink - char linkbuf[100]; - if ( str_starts_with(dev_name, "/dev/disk/by-") - && readlink(dev_name, linkbuf, sizeof(linkbuf)) > 0 - && str_starts_with(linkbuf, "../../")) { - dev_name = linkbuf + sizeof("../../")-1; + // Dereference symlinks + struct stat st; + std::string pathbuf; + if (!lstat(name, &st) && S_ISLNK(st.st_mode)) { + char * p = realpath(name, (char *)0); + if (p) { + pathbuf = p; + free(p); + test_name = pathbuf.c_str(); + } } + // Remove the leading /dev/... if it's there - else if (!strncmp(lin_dev_prefix, dev_name, dev_prefix_len)) { - if (len <= dev_prefix_len) - // if nothing else in the string, unrecognized - return 0; - // else advance pointer to following characters - dev_name += dev_prefix_len; - } + static const char dev_prefix[] = "/dev/"; + if (str_starts_with(test_name, dev_prefix)) + test_name += strlen(dev_prefix); // form /dev/h* or h* - if (!strncmp(lin_dev_ata_disk_plus, dev_name, - strlen(lin_dev_ata_disk_plus))) + if (str_starts_with(test_name, "h")) return new linux_ata_device(this, name, ""); // form /dev/ide/* or ide/* - if (!strncmp(lin_dev_ata_devfs_disk_plus, dev_name, - strlen(lin_dev_ata_devfs_disk_plus))) + if (str_starts_with(test_name, "ide/")) return new linux_ata_device(this, name, ""); // form /dev/s* or s* - if (!strncmp(lin_dev_scsi_disk_plus, dev_name, - strlen(lin_dev_scsi_disk_plus))) { + if (str_starts_with(test_name, "s")) { // Try to detect possible USB->(S)ATA bridge unsigned short vendor_id = 0, product_id = 0, version = 0; - if (get_usb_id(dev_name, vendor_id, product_id, version)) { + if (get_usb_id(test_name, vendor_id, product_id, version)) { const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version); if (!usbtype) return 0; - // Linux USB layer does not support 16 byte SAT pass through command - if (!strcmp(usbtype, "sat")) + + // Kernels before 2.6.29 do not support the sense data length + // required for SAT ATA PASS-THROUGH(16) + if (!strcmp(usbtype, "sat") && get_kernel_release() < 206029) usbtype = "sat,12"; + // Return SAT/USB device for this type // (Note: linux_scsi_device::autodetect_open() will not be called in this case) return get_sat_device(usbtype, new linux_scsi_device(this, name, "")); @@ -3033,45 +3062,29 @@ smart_device * linux_smart_interface::autodetect_smart_device(const char * name) } // form /dev/scsi/* or scsi/* - if (!strncmp(lin_dev_scsi_devfs_disk_plus, dev_name, - strlen(lin_dev_scsi_devfs_disk_plus))) + if (str_starts_with(test_name, "scsi/")) return new linux_scsi_device(this, name, ""); // form /dev/ns* or ns* - if (!strncmp(lin_dev_scsi_tape1, dev_name, - strlen(lin_dev_scsi_tape1))) + if (str_starts_with(test_name, "ns")) return new linux_scsi_device(this, name, ""); // form /dev/os* or os* - if (!strncmp(lin_dev_scsi_tape2, dev_name, - strlen(lin_dev_scsi_tape2))) + if (str_starts_with(test_name, "os")) return new linux_scsi_device(this, name, ""); // form /dev/nos* or nos* - if (!strncmp(lin_dev_scsi_tape3, dev_name, - strlen(lin_dev_scsi_tape3))) + if (str_starts_with(test_name, "nos")) return new linux_scsi_device(this, name, ""); - // form /dev/twa* - if (!strncmp(lin_dev_3ware_9000_char, dev_name, - strlen(lin_dev_3ware_9000_char))) - return missing_option("-d 3ware,N"); - - // form /dev/twe* - if (!strncmp(lin_dev_3ware_678k_char, dev_name, - strlen(lin_dev_3ware_678k_char))) + // form /dev/tw[ael]* or tw[ael]* + if (str_starts_with(test_name, "tw") && strchr("ael", test_name[2])) return missing_option("-d 3ware,N"); - // form /dev/cciss* - if (!strncmp(lin_dev_cciss_dir, dev_name, - strlen(lin_dev_cciss_dir))) + // form /dev/cciss/* or cciss/* + if (str_starts_with(test_name, "cciss/")) return missing_option("-d cciss,N"); - // form /dev/sg* - if ( !strncmp(lin_dev_areca, dev_name, - strlen(lin_dev_areca)) ) - return missing_option("-d areca,N"); - // we failed to recognize any of the forms return 0; } @@ -3094,7 +3107,9 @@ smart_device * linux_smart_interface::get_custom_smart_device(const char * name, return 0; } - if (!strncmp(name, "/dev/twa", 8)) + if (!strncmp(name, "/dev/twl", 8)) + return new linux_escalade_device(this, name, linux_escalade_device::AMCC_3WARE_9700_CHAR, disknum); + else if (!strncmp(name, "/dev/twa", 8)) return new linux_escalade_device(this, name, linux_escalade_device::AMCC_3WARE_9000_CHAR, disknum); else if (!strncmp(name, "/dev/twe", 8)) return new linux_escalade_device(this, name, linux_escalade_device::AMCC_3WARE_678K_CHAR, disknum); @@ -3104,16 +3119,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_ata_device(this, name, disknum, encnum); } // Highpoint ? @@ -3129,7 +3145,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 <= 128)) { set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied"); return 0; } @@ -3148,11 +3164,11 @@ smart_device * linux_smart_interface::get_custom_smart_device(const char * name, 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); + if (!(0 <= disknum && disknum <= 127)) { + set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 127", disknum); return 0; } - return new linux_cciss_device(this, name, disknum); + return get_sat_device("sat,auto", new linux_cciss_device(this, name, disknum)); } #endif // HAVE_LINUX_CCISS_IOCTL_H @@ -3160,12 +3176,22 @@ smart_device * linux_smart_interface::get_custom_smart_device(const char * name, if (sscanf(type, "megaraid,%d", &disknum) == 1) { return new linux_megaraid_device(this, name, 0, disknum); } + + //aacraid? + unsigned host, chan, device; + if (sscanf(type, "aacraid,%u,%u,%u", &host, &chan, &device) == 3) { + //return new linux_aacraid_device(this,name,channel,device); + return get_sat_device("sat,auto", + new linux_aacraid_device(this, name, host, chan, device)); + + } + 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, aacraid,H,L,ID" #ifdef HAVE_LINUX_CCISS_IOCTL_H ", cciss,N" #endif @@ -3174,7 +3200,6 @@ std::string linux_smart_interface::get_valid_custom_dev_types_str() } // namespace - ///////////////////////////////////////////////////////////////////////////// /// Initialize platform interface and register with smi()