summaryrefslogtreecommitdiffstats
path: root/cciss.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cciss.cpp')
-rw-r--r--cciss.cpp246
1 files changed, 246 insertions, 0 deletions
diff --git a/cciss.cpp b/cciss.cpp
new file mode 100644
index 0000000..9c5f629
--- /dev/null
+++ b/cciss.cpp
@@ -0,0 +1,246 @@
+/*
+ * cciss.cpp
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2007 Sergey Svishchev
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "config.h"
+
+#if defined(linux) || defined(__linux__)
+# include <sys/ioctl.h>
+# ifdef HAVE_LINUX_COMPILER_H
+# include <linux/compiler.h>
+# endif
+# if defined(HAVE_LINUX_CCISS_IOCTL_H)
+# include <linux/cciss_ioctl.h>
+# define _HAVE_CCISS
+# endif
+# include <asm/byteorder.h>
+# ifndef be32toh
+# define be32toh __be32_to_cpu
+# endif
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+# include <sys/endian.h>
+# include CISS_LOCATION
+# define _HAVE_CCISS
+#endif
+
+#ifdef _HAVE_CCISS
+#include "cciss.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+const char * cciss_cpp_cvsid = "$Id: cciss.cpp 4977 2019-11-22 19:57:04Z chrfranke $"
+ CCISS_H_CVSID;
+
+typedef struct _ReportLUNdata_struct
+{
+ uint32_t LUNListLength; /* always big-endian */
+ uint32_t reserved;
+ uint8_t LUN[CISS_MAX_LUN][8];
+} ReportLunData_struct;
+
+/* Structure/defines of Report Physical LUNS of drive */
+#ifndef CISS_MAX_LUN
+#define CISS_MAX_LUN 16
+#endif
+#define CISS_MAX_PHYS_LUN 1024
+#define CISS_REPORT_PHYS 0xc3
+
+#define LSCSI_DRIVER_SENSE 0x8 /* alternate CHECK CONDITION indication */
+#define SEND_IOCTL_RESP_SENSE_LEN 16 /* ioctl limitation */
+
+static int cciss_getlun(int device, int target, unsigned char *physlun, int report);
+static int cciss_sendpassthru(unsigned int cmdtype, unsigned char *CDB,
+ unsigned int CDBlen, char *buff,
+ unsigned int size, unsigned int LunID,
+ unsigned char *scsi3addr, int fd);
+
+/*
+ This is an interface that uses the cciss passthrough to talk to the SMART controller on
+ the HP system. The cciss driver provides a way to send SCSI cmds through the CCISS passthrough.
+*/
+int cciss_io_interface(int device, int target, struct scsi_cmnd_io * iop, int report)
+{
+ switch (iop->dxfer_dir) {
+ case DXFER_NONE: case DXFER_FROM_DEVICE: break;
+ default: return -ENOTSUP; // TODO: Support DXFER_TO_DEVICE
+ }
+
+ unsigned char phylun[8] = {0};
+
+ int status = cciss_getlun(device, target, phylun, report);
+ if (report > 0)
+ pout(" cciss_getlun(%d, %d) = 0x%x; scsi3addr: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ device, target, status,
+ phylun[0], phylun[1], phylun[2], phylun[3], phylun[4], phylun[5], phylun[6], phylun[7]);
+ if (status) {
+ return -ENXIO; /* give up, assume no device there */
+ }
+
+ unsigned char sensebuf[SEND_IOCTL_RESP_SENSE_LEN];
+ unsigned char * pBuf = (iop->dxferp ? iop->dxferp : sensebuf);
+ unsigned iBufLen = (iop->dxferp ? iop->dxfer_len : sizeof(sensebuf));
+
+ status = cciss_sendpassthru( 2, iop->cmnd, iop->cmnd_len, (char*) pBuf, iBufLen, 1, phylun, device);
+
+ if (0 == status)
+ {
+ if (report > 0)
+ pout(" status=0\n");
+ if (DXFER_FROM_DEVICE == iop->dxfer_dir)
+ {
+ if (report > 1)
+ {
+ int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+ pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+ (trunc ? " [only first 256 bytes shown]" : ""));
+ dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+ }
+ }
+ return 0;
+ }
+ iop->scsi_status = status & 0x7e; /* bits 0 and 7 used to be for vendors */
+ if (LSCSI_DRIVER_SENSE == ((status >> 24) & 0xf))
+ iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
+ unsigned len = (SEND_IOCTL_RESP_SENSE_LEN < iop->max_sense_len) ?
+ SEND_IOCTL_RESP_SENSE_LEN : iop->max_sense_len;
+ if (len > iBufLen)
+ len = iBufLen;
+ if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) &&
+ iop->sensep && (len > 0))
+ {
+ memcpy(iop->sensep, pBuf, len);
+ iop->resp_sense_len = len;
+ if (report > 1)
+ {
+ pout(" >>> Sense buffer, len=%d:\n", (int)len);
+ dStrHex((const uint8_t *)pBuf, len , 1);
+ }
+ }
+ if (report)
+ {
+ if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) {
+ pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", status & 0xff,
+ pBuf[2] & 0xf, pBuf[12], pBuf[13]);
+ }
+ else
+ pout(" status=0x%x\n", status);
+ }
+ if (iop->scsi_status > 0)
+ return 0;
+ else
+ {
+ if (report > 0)
+ pout(" ioctl status=0x%x but scsi status=0, fail with ENXIO\n", status);
+ return -ENXIO; /* give up, assume no device there */
+ }
+}
+
+static int cciss_sendpassthru(unsigned int cmdtype, unsigned char *CDB,
+ unsigned int CDBlen, char *buff,
+ unsigned int size, unsigned int LunID,
+ unsigned char *scsi3addr, int fd)
+{
+ int err ;
+ IOCTL_Command_struct iocommand;
+
+ memset(&iocommand, 0, sizeof(iocommand));
+
+ if (cmdtype == 0)
+ {
+ // To controller; nothing to do
+ }
+ else if (cmdtype == 1)
+ {
+ iocommand.LUN_info.LogDev.VolId = LunID;
+ iocommand.LUN_info.LogDev.Mode = 1;
+ }
+ else if (cmdtype == 2)
+ {
+ memcpy(&iocommand.LUN_info.LunAddrBytes,scsi3addr,8);
+ iocommand.LUN_info.LogDev.Mode = 0;
+ }
+ else
+ {
+ pout("cciss_sendpassthru: bad cmdtype\n");
+ return 1;
+ }
+
+ memcpy(&iocommand.Request.CDB[0], CDB, CDBlen);
+ iocommand.Request.CDBLen = CDBlen;
+ iocommand.Request.Type.Type = TYPE_CMD;
+ iocommand.Request.Type.Attribute = ATTR_SIMPLE;
+ iocommand.Request.Type.Direction = XFER_READ; // TODO: OK for DXFER_NONE ?
+ iocommand.Request.Timeout = 0;
+
+ iocommand.buf_size = size;
+ iocommand.buf = (unsigned char *)buff;
+
+ if ((err = ioctl(fd, CCISS_PASSTHRU, &iocommand)))
+ {
+ pout("CCISS ioctl error %d (fd %d CDBLen %u buf_size %u)\n",
+ fd, err, CDBlen, size);
+ }
+ return err;
+}
+
+static int cciss_getlun(int device, int target, unsigned char *physlun, int report)
+{
+ unsigned char CDB[16]= {0};
+ ReportLunData_struct *luns;
+ int reportlunsize = sizeof(*luns) + CISS_MAX_PHYS_LUN * 8;
+ int ret;
+
+ luns = (ReportLunData_struct *)malloc(reportlunsize);
+
+ memset(luns, 0, reportlunsize);
+
+ /* Get Physical LUN Info (for physical device) */
+ CDB[0] = CISS_REPORT_PHYS;
+ CDB[6] = (reportlunsize >> 24) & 0xFF; /* MSB */
+ CDB[7] = (reportlunsize >> 16) & 0xFF;
+ CDB[8] = (reportlunsize >> 8) & 0xFF;
+ CDB[9] = reportlunsize & 0xFF;
+
+ if ((ret = cciss_sendpassthru(0, CDB, 12, (char *)luns, reportlunsize, 0, NULL, device)))
+ {
+ free(luns);
+ return ret;
+ }
+
+ if (report > 1)
+ {
+ unsigned int i,j;
+ unsigned char *stuff = (unsigned char *)luns;
+
+ pout("\n===== [%s] DATA START (BASE-16) =====\n", "LUN DATA");
+ for (i=0; i<(sizeof(_ReportLUNdata_struct)+15)/16; i++){
+ pout("%03d-%03d: ", 16*i, 16*(i+1)-1);
+ for (j=0; j<15; j++)
+ pout("%02x ",*stuff++);
+ pout("%02x\n",*stuff++);
+ }
+ pout("===== [%s] DATA END (%u Bytes) =====\n\n", "LUN DATA", (unsigned)sizeof(_ReportLUNdata_struct));
+ }
+
+ if (target >= 0 && target < (int) be32toh(luns->LUNListLength) / 8)
+ {
+ memcpy(physlun, luns->LUN[target], 8);
+ free(luns);
+ return 0;
+ }
+
+ free(luns);
+ return 1;
+}
+#endif