diff options
Diffstat (limited to 'cciss.cpp')
-rw-r--r-- | cciss.cpp | 246 |
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 |