+};
+
+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 : "<unknown opcode>");
+ 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
+
+class linux_megaraid_device
+: public /* implements */ scsi_device,
+ public /* extends */ linux_smart_device
+{
+public:
+ linux_megaraid_device(smart_interface *intf, const char *name,
+ unsigned int bus, unsigned int tgt);
+
+ virtual ~linux_megaraid_device() throw();
+
+ virtual smart_device * autodetect_open();
+
+ virtual bool open();
+ virtual bool close();
+
+ virtual bool scsi_pass_through(scsi_cmnd_io *iop);
+
+private:
+ unsigned int m_disknum;
+ unsigned int m_busnum;
+ unsigned int m_hba;
+ int m_fd;
+
+ bool (linux_megaraid_device::*pt_cmd)(int cdblen, void *cdb, int dataLen, void *data,
+ 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 direction);
+ bool megadev_cmd(int cdbLen, void *cdb, int dataLen, void *data,
+ int senseLen, void *sense, int report, int direction);
+};
+
+linux_megaraid_device::linux_megaraid_device(smart_interface *intf,
+ const char *dev_name, unsigned int bus, unsigned int tgt)
+ : smart_device(intf, dev_name, "megaraid", "megaraid"),
+ linux_smart_device(O_RDWR | O_NONBLOCK),
+ m_disknum(tgt), m_busnum(bus), m_hba(0),
+ 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()
+{
+ if (m_fd >= 0)
+ ::close(m_fd);
+}
+
+smart_device * linux_megaraid_device::autodetect_open()
+{
+ int report = scsi_debugmode;
+
+ // Open device
+ if (!open())
+ return this;
+
+ // The code below is based on smartd.cpp:SCSIFilterKnown()
+ if (strcmp(get_req_type(), "megaraid"))
+ return this;
+
+ // Get INQUIRY
+ unsigned char req_buff[64] = {0, };
+ int req_len = 36;
+ if (scsiStdInquiry(this, req_buff, req_len)) {
+ close();
+ set_err(EIO, "INQUIRY failed");
+ return this;
+ }
+
+ int avail_len = req_buff[4] + 5;
+ int len = (avail_len < req_len ? avail_len : req_len);
+ if (len < 36)
+ return this;
+
+ if (report)
+ pout("Got MegaRAID inquiry.. %s\n", req_buff+8);
+
+ // Use INQUIRY to detect type
+ {
+ // SAT?
+ ata_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
+ 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;
+ int report = scsi_debugmode;
+
+ 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();
+ }
+ /* Perform mknod of device ioctl node */
+ FILE * fp = fopen("/proc/devices", "r");
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ 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);
+
+ /* Open Device IOCTL node */
+ if ((m_fd = ::open("/dev/megaraid_sas_ioctl_node", O_RDWR)) >= 0) {
+ pt_cmd = &linux_megaraid_device::megasas_cmd;
+ }
+ else if ((m_fd = ::open("/dev/megadev0", O_RDWR)) >= 0) {
+ pt_cmd = &linux_megaraid_device::megadev_cmd;
+ }
+ else {
+ int err = errno;
+ linux_smart_device::close();
+ return set_err(err, "cannot open /dev/megaraid_sas_ioctl_node or /dev/megadev0");
+ }
+ set_fd(m_fd);
+ return true;
+}
+
+bool linux_megaraid_device::close()
+{
+ if (m_fd >= 0)
+ ::close(m_fd);
+ m_fd = -1; m_hba = 0; pt_cmd = 0;
+ set_fd(m_fd);
+ return true;
+}
+
+bool linux_megaraid_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 : "<unknown opcode>");
+ 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);
+ }
+
+ // Controller rejects Test Unit Ready
+ if (iop->cmnd[0] == 0x00)
+ 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,
+ iop->dxfer_len, iop->dxferp,
+ 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 dxfer_dir)
+{
+ struct megasas_pthru_frame *pthru;
+ struct megasas_iocpacket uio;
+
+ memset(&uio, 0, sizeof(uio));
+ pthru = &uio.frame.pthru;
+ pthru->cmd = MFI_CMD_PD_SCSI_IO;
+ pthru->cmd_status = 0xFF;
+ pthru->scsi_status = 0x0;
+ pthru->target_id = m_disknum;
+ pthru->lun = 0;
+ pthru->cdb_len = cdbLen;
+ pthru->timeout = 0;
+ 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;
+ 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;
+ }
+
+ errno = 0;
+ 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);
+ }
+ return set_err((errno ? errno : EIO), "megasas_cmd result: %d.%d = %d/%d",
+ m_hba, m_disknum, errno,
+ pthru->cmd_status);
+ }
+ return true;
+}
+
+/* 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 /* dir */)
+{
+ struct uioctl_t uio;
+ int rc;
+
+ /* Don't issue to the controller */
+ if (m_disknum == 7)
+ return false;
+
+ memset(&uio, 0, sizeof(uio));
+ uio.inlen = dataLen;
+ uio.outlen = dataLen;
+
+ memset(data, 0, dataLen);
+ uio.ui.fcs.opcode = 0x80; // M_RD_IOCTL_CMD
+ uio.ui.fcs.adapno = MKADAP(m_hba);
+
+ uio.data.pointer = (uint8_t *)data;
+
+ uio.mbox.cmd = MEGA_MBOXCMD_PASSTHRU;
+ uio.mbox.xferaddr = (intptr_t)&uio.pthru;
+
+ uio.pthru.ars = 1;
+ uio.pthru.timeout = 2;
+ uio.pthru.channel = 0;
+ uio.pthru.target = m_disknum;
+ uio.pthru.cdblen = cdbLen;
+ uio.pthru.reqsenselen = MAX_REQ_SENSE_LEN;
+ uio.pthru.dataxferaddr = (intptr_t)data;
+ uio.pthru.dataxferlen = dataLen;
+ memcpy(uio.pthru.cdb, cdb, cdbLen);
+
+ rc=ioctl(m_fd, MEGAIOCCMD, &uio);
+ if (uio.pthru.scsistatus || rc != 0) {
+ return set_err((errno ? errno : EIO), "megadev_cmd result: %d.%d = %d/%d",
+ m_hba, m_disknum, errno,
+ uio.pthru.scsistatus);
+ }
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/// CCISS RAID support
+
+#ifdef HAVE_LINUX_CCISS_IOCTL_H
+
+class linux_cciss_device
+: public /*implements*/ scsi_device,
+ public /*extends*/ linux_smart_device
+{
+public:
+ linux_cciss_device(smart_interface * intf, const char * name, unsigned char disknum);
+
+ virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+
+private:
+ unsigned char m_disknum; ///< Disk number.
+};
+
+linux_cciss_device::linux_cciss_device(smart_interface * intf,
+ const char * dev_name, unsigned char disknum)
+: smart_device(intf, dev_name, "cciss", "cciss"),
+ linux_smart_device(O_RDWR | O_NONBLOCK),
+ m_disknum(disknum)
+{
+ set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum);
+}
+
+bool linux_cciss_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+ int status = cciss_io_interface(get_fd(), m_disknum, iop, scsi_debugmode);
+ if (status < 0)
+ return set_err(-status);
+ return true;
+}
+
+#endif // HAVE_LINUX_CCISS_IOCTL_H
+
+/////////////////////////////////////////////////////////////////////////////
+/// AMCC/3ware RAID support
+
+class linux_escalade_device
+: public /*implements*/ ata_device,
+ public /*extends*/ linux_smart_device
+{
+public:
+ enum escalade_type_t {
+ AMCC_3WARE_678K,
+ AMCC_3WARE_678K_CHAR,
+ AMCC_3WARE_9000_CHAR,
+ AMCC_3WARE_9700_CHAR
+ };
+
+ linux_escalade_device(smart_interface * intf, const char * dev_name,
+ escalade_type_t escalade_type, int disknum);
+
+ virtual bool open();
+
+ virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+
+private:
+ escalade_type_t m_escalade_type; ///< Controller type
+ int m_disknum; ///< Disk number.
+};
+
+linux_escalade_device::linux_escalade_device(smart_interface * intf, const char * dev_name,
+ escalade_type_t escalade_type, int disknum)
+: smart_device(intf, dev_name, "3ware", "3ware"),
+ linux_smart_device(O_RDONLY | O_NONBLOCK),
+ m_escalade_type(escalade_type), m_disknum(disknum)
+{
+ set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum);
+}
+
+/* This function will setup and fix device nodes for a 3ware controller. */
+#define MAJOR_STRING_LENGTH 3
+#define DEVICE_STRING_LENGTH 32
+#define NODE_STRING_LENGTH 16
+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];
+ char device_name[DEVICE_STRING_LENGTH+1];
+ char nodestring[NODE_STRING_LENGTH];
+ struct stat stat_buf;
+ FILE *file;
+ int retval = 0;
+#ifdef WITH_SELINUX
+ security_context_t orig_context = NULL;
+ security_context_t node_context = NULL;
+ int selinux_enabled = is_selinux_enabled();
+ 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");
+ syserror("fopen");
+ return 0; // don't fail here: user might not have /proc !
+ }
+
+ /* Attempt to get device major number */
+ while (EOF != fscanf(file, "%3s %32s", majorstring, device_name)) {
+ majorstring[MAJOR_STRING_LENGTH]='\0';
+ device_name[DEVICE_STRING_LENGTH]='\0';
+ if (!strncmp(device_name, nodename, DEVICE_STRING_LENGTH)) {
+ tw_major = atoi(majorstring);
+ break;
+ }
+ }
+ fclose(file);
+
+ /* See if we found a major device number */
+ if (!tw_major) {
+ pout("No major number for /dev/%s listed in /proc/devices. Is the %s driver loaded?\n", nodename, driver_name);
+ return 2;
+ }
+#ifdef WITH_SELINUX
+ /* Prepare a database of contexts for files in /dev
+ * and save the current context */
+ if (selinux_enabled) {
+ if (matchpathcon_init_prefix(NULL, "/dev") < 0)
+ pout("Error initializing contexts database for /dev");
+ if (getfscreatecon(&orig_context) < 0) {
+ pout("Error retrieving original SELinux fscreate context");
+ if (selinux_enforced)
+ matchpathcon_fini();
+ return 6;
+ }
+ }
+#endif
+ /* Now check if nodes are correct */
+ for (index=0; index<16; 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) {
+ if (matchpathcon(nodestring, S_IRUSR | S_IWUSR, &node_context) < 0) {
+ pout("Could not retrieve context for %s", nodestring);
+ if (selinux_enforced) {
+ retval = 6;
+ break;
+ }
+ }
+ if (setfscreatecon(node_context) < 0) {
+ pout ("Error setting default fscreate context");
+ if (selinux_enforced) {
+ retval = 6;
+ break;
+ }
+ }
+ }
+#endif
+ /* Try to stat the node */
+ if ((stat(nodestring, &stat_buf))) {
+ pout("Node %s does not exist and must be created. Check the udev rules.\n", nodestring);
+ /* Create a new node if it doesn't exist */
+ if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) {
+ pout("problem creating 3ware device nodes %s", nodestring);
+ syserror("mknod");
+ retval = 3;
+ break;
+ } else {
+#ifdef WITH_SELINUX
+ if (selinux_enabled && node_context) {
+ freecon(node_context);
+ node_context = NULL;
+ }
+#endif
+ continue;
+ }
+ }
+
+ /* See if nodes major and minor numbers are correct */
+ if ((tw_major != (int)(major(stat_buf.st_rdev))) ||
+ (index != (int)(minor(stat_buf.st_rdev))) ||
+ (!S_ISCHR(stat_buf.st_mode))) {
+ pout("Node %s has wrong major/minor number and must be created anew."
+ " Check the udev rules.\n", nodestring);
+ /* Delete the old node */
+ if (unlink(nodestring)) {
+ pout("problem unlinking stale 3ware device node %s", nodestring);
+ syserror("unlink");
+ retval = 4;
+ break;
+ }
+
+ /* Make a new node */
+ if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) {
+ pout("problem creating 3ware device nodes %s", nodestring);
+ syserror("mknod");
+ retval = 5;
+ break;
+ }
+ }
+#ifdef WITH_SELINUX
+ if (selinux_enabled && node_context) {
+ freecon(node_context);
+ node_context = NULL;
+ }
+#endif
+ }
+
+#ifdef WITH_SELINUX
+ if (selinux_enabled) {
+ if(setfscreatecon(orig_context) < 0) {
+ pout("Error re-setting original fscreate context");
+ if (selinux_enforced)
+ retval = 6;
+ }
+ if(orig_context)
+ freecon(orig_context);
+ if(node_context)
+ freecon(node_context);
+ matchpathcon_fini();
+ }
+#endif
+ return retval;
+}
+
+bool linux_escalade_device::open()
+{
+ 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_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);
+ }
+ // Continue with default open
+ return linux_smart_device::open();
+}
+
+// TODO: Function no longer useful
+//void printwarning(smart_command_set command);
+
+// 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 3ware 3w-xxxx 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
+// escalade_type indicates the type of controller type, and if scsi or char interface is used
+// 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"
+
+/* 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
+#define BUFFER_LEN_9000 ( sizeof(TW_Ioctl_Buf_Apache)+512-1 ) // 2051 unpacked, 2048 packed
+#define TW_IOCTL_BUFFER_SIZE ( MAX(MAX(BUFFER_LEN_678K, BUFFER_LEN_9000), BUFFER_LEN_678K_CHAR) )
+
+bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+{
+ if (!ata_cmd_is_ok(in,
+ true, // data_out_support
+ false, // TODO: multi_sector_support
+ true) // ata_48bit_support
+ )
+ return false;
+
+ // Used by both the SCSI and char interfaces
+ TW_Passthru *passthru=NULL;
+ char ioctl_buffer[TW_IOCTL_BUFFER_SIZE];
+
+ // only used for SCSI device interface
+ TW_Ioctl *tw_ioctl=NULL;
+ TW_Output *tw_output=NULL;
+
+ // only used for 6000/7000/8000 char device interface
+ TW_New_Ioctl *tw_ioctl_char=NULL;
+
+ // only used for 9000 character device interface
+ TW_Ioctl_Buf_Apache *tw_ioctl_apache=NULL;
+
+ memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE);
+
+ // TODO: Handle controller differences by different classes
+ 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 */
+ passthru = (TW_Passthru *)&(tw_ioctl_apache->firmware_command.command.oldcommand);
+ }
+ else if (m_escalade_type==AMCC_3WARE_678K_CHAR) {
+ tw_ioctl_char = (TW_New_Ioctl *)ioctl_buffer;
+ tw_ioctl_char->data_buffer_length = 512;
+ passthru = (TW_Passthru *)&(tw_ioctl_char->firmware_command);
+ }
+ else if (m_escalade_type==AMCC_3WARE_678K) {
+ tw_ioctl = (TW_Ioctl *)ioctl_buffer;
+ tw_ioctl->cdb[0] = TW_IOCTL;
+ tw_ioctl->opcode = TW_ATA_PASSTHRU;
+ tw_ioctl->input_length = 512; // correct even for non-data commands
+ tw_ioctl->output_length = 512; // correct even for non-data commands
+ tw_output = (TW_Output *)tw_ioctl;
+ passthru = (TW_Passthru *)&(tw_ioctl->input_data);
+ }
+ else {
+ return set_err(ENOSYS,
+ "Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n"
+ "Please contact " PACKAGE_BUGREPORT "\n", (int)m_escalade_type, m_disknum);
+ }
+
+ // Same for (almost) all commands - but some reset below
+ passthru->byte0.opcode = TW_OP_ATA_PASSTHRU;
+ passthru->request_id = 0xFF;
+ passthru->unit = m_disknum;
+ passthru->status = 0;
+ passthru->flags = 0x1;
+
+ // Set registers
+ {
+ const ata_in_regs_48bit & r = in.in_regs;
+ passthru->features = r.features_16;
+ passthru->sector_count = r.sector_count_16;
+ passthru->sector_num = r.lba_low_16;
+ passthru->cylinder_lo = r.lba_mid_16;
+ passthru->cylinder_hi = r.lba_high_16;
+ passthru->drive_head = r.device;
+ passthru->command = r.command;
+ }
+
+ // Is this a command that reads or returns 512 bytes?
+ // passthru->param values are:
+ // 0x0 - non data command without TFR write check,
+ // 0x8 - non data command with TFR write check,
+ // 0xD - data command that returns data to host from device
+ // 0xF - data command that writes data from host to device
+ // passthru->size values are 0x5 for non-data and 0x07 for data
+ bool readdata = false;
+ if (in.direction == ata_cmd_in::data_in) {
+ readdata=true;
+ passthru->byte0.sgloff = 0x5;
+ passthru->size = 0x7; // TODO: Other value for multi-sector ?