--- embedaddon/smartmontools/os_linux.cpp 2012/10/09 09:36:45 1.1.1.2 +++ embedaddon/smartmontools/os_linux.cpp 2013/07/22 01:17:35 1.1.1.3 @@ -65,6 +65,7 @@ #include // for offsetof() #include #include +#include #ifndef makedev // old versions of types.h do not include sysmacros.h #include #endif @@ -82,6 +83,7 @@ #include "dev_interface.h" #include "dev_ata_cmd_set.h" +#include "dev_areca.h" #ifndef ENOTSUP #define ENOTSUP ENOSYS @@ -89,10 +91,10 @@ #define ARGUSED(x) ((void)(x)) -const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp,v 1.1.1.2 2012/10/09 09:36:45 misho Exp $" +const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp,v 1.1.1.3 2013/07/22 01:17:35 misho Exp $" OS_LINUX_H_CVSID; +extern unsigned char failuretest_permissive; - namespace os_linux { // No need to publish anything, name provided for Doxygen ///////////////////////////////////////////////////////////////////////////// @@ -121,13 +123,15 @@ class linux_smart_device (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) @@ -201,7 +205,6 @@ static const char smartctl_examples[] = " on Areca RAID controller)\n" ; - ///////////////////////////////////////////////////////////////////////////// /// Linux ATA support @@ -242,7 +245,6 @@ linux_ata_device::linux_ata_device(smart_interface * i // 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) @@ -849,7 +851,6 @@ linux_scsi_device::linux_scsi_device(smart_interface * { } - bool linux_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) { int status = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); @@ -875,7 +876,7 @@ class linux_megaraid_device (public) virtual bool open(); virtual bool close(); - + virtual bool scsi_pass_through(scsi_cmnd_io *iop); private: @@ -885,11 +886,11 @@ class linux_megaraid_device (public) 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, @@ -900,6 +901,7 @@ linux_megaraid_device::linux_megaraid_device(smart_int 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() @@ -939,61 +941,55 @@ smart_device * linux_megaraid_device::autodetect_open( // Use INQUIRY to detect type { - // SAT or USB ? + // SAT? ata_device * newdev = smi()->autodetect_sat_device(this, req_buff, len); - 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"); + if (newdev) // NOTE: 'this' is now owned by '*newdev' return newdev; - } } // Nothing special found return this; } - bool linux_megaraid_device::open() { char line[128]; - int mjr, n1; - FILE *fp; + int mjr; int report = scsi_debugmode; - 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; + if(sscanf(get_dev_name(),"/dev/bus/%d", &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) - 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; - } + 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); @@ -1009,7 +1005,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; } @@ -1018,7 +1014,8 @@ 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) @@ -1061,23 +1058,25 @@ bool linux_megaraid_device::scsi_pass_through(scsi_cmn 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 ((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; @@ -1092,7 +1091,21 @@ bool linux_megaraid_device::megasas_cmd(int cdbLen, vo pthru->lun = 0; pthru->cdb_len = cdbLen; pthru->timeout = 0; - pthru->flags = MFI_FRAME_DIR_READ; + 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; @@ -1126,7 +1139,7 @@ bool linux_megaraid_device::megasas_cmd(int cdbLen, vo /* 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; @@ -1261,7 +1274,6 @@ static int setup_3ware_nodes(const char *nodename, con 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"); @@ -1301,7 +1313,7 @@ static int setup_3ware_nodes(const char *nodename, con #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) { @@ -1434,7 +1446,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 @@ -1644,84 +1655,39 @@ bool linux_escalade_device::ata_pass_through(const ata return true; } - ///////////////////////////////////////////////////////////////////////////// /// Areca RAID support -class linux_areca_device -: public /*implements*/ ata_device, +/////////////////////////////////////////////////////////////////// +// 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, int encnum = 1); - -protected: - 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_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 +/////////////////////////////////////////////////////////////////// +// SAS(SCSI) device behind Areca RAID Controller +class linux_areca_scsi_device +: public /*implements*/ areca_scsi_device, + public /*extends*/ linux_smart_device { - unsigned int HeaderLength; - unsigned char Signature[8]; - unsigned int Timeout; - unsigned int ControlCode; - unsigned int ReturnCode; - unsigned int Length; -} sSRB_IO_CONTROL; +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); +}; -typedef struct _SRB_BUFFER -{ - sSRB_IO_CONTROL srbioctl; - unsigned char ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware -} sSRB_BUFFER; - // Looks in /proc/scsi to suggest correct areca devices -// If hint not NULL, return device path guess -static 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"; @@ -1760,9 +1726,6 @@ static 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++; } @@ -1771,405 +1734,118 @@ static int find_areca_in_proc(char *hint) return 0; } +// 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); +} -#if 0 // For debugging areca code - -static void dumpdata(unsigned char *block, int len) +smart_device * linux_areca_ata_device::autodetect_open() { - int ln = (len / 16) + 1; // total line# - unsigned char c; - int pos = 0; + int is_ata = 1; - 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"); + // autodetect device type + is_ata = arcmsr_get_dev_type(); + if(is_ata < 0) + { + set_err(EIO); + return this; + } - 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(is_ata == 1) + { + // SATA device + return this; + } - if ( pos < 16 ) - { - for ( int loop = pos; loop < 16; loop++ ) - { - printf(" "); - } - } + // 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 - // 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"); + return newdev.release(); } -#endif - -static int arcmsr_command_handler(int fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len, void *ext_data /* reserved for further use */) +int linux_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) { - ARGUSED(ext_data); + int ioctlreturn = 0; - int ioctlreturn = 0; - sSRB_BUFFER sBuf; - struct scsi_cmnd_io io_hdr; - int dir = DXFER_TO_DEVICE; + if(!is_open()) { + if(!open()){ + find_areca_in_proc(); + } + } - UINT8 cdb[10]; - UINT8 sense[32]; + ioctlreturn = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); + if ( ioctlreturn || iop->scsi_status ) + { + // errors found + return -1; + } - 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)); + return ioctlreturn; +} - memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); - memset(&io_hdr, 0, sizeof(io_hdr)); - memset(cdb, 0, sizeof(cdb)); - memset(sense, 0, sizeof(sense)); +bool linux_areca_ata_device::arcmsr_lock() +{ + return true; +} - - 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; - } - } - } - - // Deal with the different error cases - if ( ioctlreturn ) - { - pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn); - return -2; - } - - - if ( io_hdr.scsi_status ) - { - pout("io_hdr.scsi_status with write buffer failed code = %x\n", io_hdr.scsi_status); - return -3; - } - - - if ( data ) - { - memcpy(data, return_buff, total); - } - - return total; +bool linux_areca_ata_device::arcmsr_unlock() +{ + return true; } - -linux_areca_device::linux_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +// 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), - m_encnum(encnum) + 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); } -// Areca RAID Controller -// 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) +smart_device * linux_areca_scsi_device::autodetect_open() { -if (!ata_cmd_is_ok(in, - true, // data_out_support - false, // TODO: multi_sector_support - true) // ata_48bit_support - ) - return false; + return this; +} - // 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; +int linux_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + int ioctlreturn = 0; - // 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; + if(!is_open()) { + if(!open()){ + find_areca_in_proc(); + } + } - // 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 | - // +--------------------------------------------------------------------------------+ - // + ioctlreturn = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); + if ( ioctlreturn || iop->scsi_status ) + { + // errors found + return -1; + } - //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; + return ioctlreturn; +} - sATA_INPUT_REGISTERS *ata_cmd; +bool linux_areca_scsi_device::arcmsr_lock() +{ + return true; +} - // 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]; - - // 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; - } - bool readdata = false; - if (in.direction == ata_cmd_in::data_in) { - readdata = true; - // the command will read data - areca_packet[6] = 0x13; - } - else if ( in.direction == ata_cmd_in::no_data ) - { - // the commands will return no data - areca_packet[6] = 0x15; - } - else if (in.direction == ata_cmd_in::data_out) - { - // 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; // disk# - areca_packet[19] = m_encnum - 1; // enc# - - // ----- 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 set_err(EIO); - } - - 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 ) - { - return set_err(EIO); - } - - sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; - if ( ata_out->status ) - { - 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 (readdata) - { - memcpy(in.buffer, &return_buff[7], in.size); - } - - // Return register values - { - 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 true; +bool linux_areca_scsi_device::arcmsr_unlock() +{ + return true; } - ///////////////////////////////////////////////////////////////////////////// /// Marvell support @@ -2317,7 +1993,6 @@ int linux_marvell_device::ata_command_interface(smart_ return 0; } - ///////////////////////////////////////////////////////////////////////////// /// Highpoint RAID support @@ -2550,7 +2225,6 @@ int linux_highpoint_device::ata_command_interface(smar 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){ @@ -2582,7 +2256,6 @@ void printwarning(smart_command_set command){ } #endif - ///////////////////////////////////////////////////////////////////////////// /// SCSI open with autodetection support @@ -2640,7 +2313,9 @@ smart_device * linux_scsi_device::autodetect_open() } // 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; @@ -2676,7 +2351,6 @@ smart_device * linux_scsi_device::autodetect_open() return this; } - ////////////////////////////////////////////////////////////////////// // USB bridge ID detection @@ -2730,7 +2404,6 @@ static bool get_usb_id(const char * name, unsigned sho return true; } - ////////////////////////////////////////////////////////////////////// /// Linux interface @@ -2759,8 +2432,11 @@ class linux_smart_interface (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() @@ -2779,7 +2455,6 @@ std::string linux_smart_interface::get_app_examples(co 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 @@ -2876,7 +2551,61 @@ bool linux_smart_interface::get_dev_list(smart_device_ // 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 + struct dirent *ep; + unsigned int host_no = 0; + char sysfsdir[256]; + + /* we are using sysfs to get list of all scsi hosts */ + DIR * dp = opendir ("/sys/class/scsi_host/"); + if (dp != NULL) + { + while ((ep = readdir (dp)) != NULL) { + if (!sscanf(ep->d_name, "host%d", &host_no)) + continue; + /* proc_name should be megaraid_sas */ + snprintf(sysfsdir, sizeof(sysfsdir) - 1, + "/sys/class/scsi_host/host%d/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; } @@ -2904,6 +2633,8 @@ bool linux_smart_interface::scan_smart_devices(smart_d 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 @@ -2931,6 +2662,99 @@ smart_device * linux_smart_interface::missing_option(c return 0; } +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); + 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 = (megasas_pd_list *)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() { @@ -3068,7 +2892,7 @@ smart_device * linux_smart_interface::get_custom_smart 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, encnum); + return new linux_areca_ata_device(this, name, disknum, encnum); } // Highpoint ? @@ -3128,7 +2952,6 @@ std::string linux_smart_interface::get_valid_custom_de } } // namespace - ///////////////////////////////////////////////////////////////////////////// /// Initialize platform interface and register with smi()