cciss CSMI via sysfs for 2.6

From: mike . miller
Date: Wed Feb 16 2005 - 11:47:11 EST


I am sending the following patch for some constructive critisms :).
This is a small part of what the CSMI Ioctls were supposed to do. I am
looking for feedback on this implementation. It is not complete, and I
doubt anyone has hardware to test it with.
The firmware and bus_id files are only for my reference now. The part
I am interested is the get_phy_info.
For information on CSMI pls see: www.t11.org/ftp/t11/pub/sm/hba/04-468v0.pdf
Let the flames begin.

mikem

-------------------------------------------------------------------------------
drivers/block/cciss.c | 163 +++++++++++++++++++++++++++++++++++++
drivers/block/cciss_cmd.h | 3
drivers/block/cciss_scsi.c | 2
include/linux/cciss_ioctl.h | 189 +++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 352 insertions(+), 5 deletions(-)

diff -burNp lx2611.orig/drivers/block/cciss.c lx2611/drivers/block/cciss.c
--- lx2611.orig/drivers/block/cciss.c 2005-01-26 14:21:21.000000000 -0600
+++ lx2611/drivers/block/cciss.c 2005-02-15 12:31:27.000000000 -0600
@@ -46,12 +46,12 @@
#include <linux/completion.h>

#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
-#define DRIVER_NAME "HP CISS Driver (v 2.6.4)"
+#define DRIVER_NAME "HP CISS Driver (v 2.6.4-99)"
#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,4)

/* Embedded module documentation macros - see modules.h */
MODULE_AUTHOR("Hewlett-Packard Company");
-MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.4");
+MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.4-99");
MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"
" SA6i P600");
MODULE_LICENSE("GPL");
@@ -138,6 +138,8 @@ static int sendcmd( __u8 cmd, int ctlr,
unsigned int use_unit_num, unsigned int log_unit, __u8 page_code,
unsigned char *scsi3addr, int cmd_type);

+static int sas_error_check(__u8 phyId, __u8 portId, __u8 conn_rate);
+
#ifdef CONFIG_PROC_FS
static int cciss_proc_get_info(char *buffer, char **start, off_t offset,
int length, int *eof, void *data);
@@ -1076,6 +1078,158 @@ cleanup1:
}

/*
+ * sysfs stuff
+ * this should be moved to it's own file, maybe cciss_sysfs.h
+ */
+
+static ssize_t cciss_firmver_show(struct device *dev, char *buf)
+{
+ ctlr_info_t *h = dev->driver_data;
+ return sprintf(buf,"%c%c%c%c\n", h->firm_ver[0], h->firm_ver[1],
+ h->firm_ver[2], h->firm_ver[3]);
+}
+
+static ssize_t cciss_bus_id_show(struct device *dev, char *buf)
+{
+ return sprintf(buf,"%s\n", dev->bus_id);
+}
+
+static ssize_t cciss_phyinfo_show(struct device *dev, char *buf)
+{
+ ctlr_info_t *h = dev->driver_data;
+ unsigned long flags;
+ CommandList_struct *c;
+ CSMI_SAS_PHY_INFO_BUFFER iocommand;
+ CSMI_SAS_IDENTIFY p;
+ u64bit temp64;
+ DECLARE_COMPLETION(wait);
+
+ printk(KERN_WARNING "cciss: into cciss_phyinfo_show\n");
+ memset(&iocommand, 0, sizeof(CSMI_SAS_PHY_INFO_BUFFER));
+ memset(&p, 0, sizeof(CSMI_SAS_IDENTIFY));
+
+ /* allocate and fill in the command */
+ if ((c = cmd_alloc(h, 0)) == NULL)
+ return -ENOMEM;
+
+ iocommand.IoctlHeader.Length = sizeof(CSMI_SAS_PHY_INFO_BUFFER);
+ c->cmd_type = CMD_IOCTL_PEND;
+ c->Header.ReplyQueue = 0;
+
+ //Do we send the whole buffer?
+ if (iocommand.IoctlHeader.Length > 0){
+ c->Header.SGList = 1;
+ c->Header.SGTotal = 1;
+ } else {
+ c->Header.SGList = 0;
+ c->Header.SGTotal = 0;
+ }
+
+ //send the command to the controller
+ c->Header.LUN.LogDev.VolId = 0;
+ c->Header.LUN.LogDev.Mode = 0;
+
+ c->Header.Tag.lower = c->busaddr;
+
+ c->Request.Type.Type = TYPE_CMD;
+ c->Request.Type.Attribute = ATTR_SIMPLE;
+
+ c->Request.Type.Direction = XFER_BIDIRECTIONAL;
+ c->Request.Timeout = iocommand.IoctlHeader.Timeout; // Looks like CSMI uses 60 secs by default
+ c->Request.CDB[0] = 0x27; // CSMI Pass-Thru Bidirectional opcode
+ c->Request.CDB[1] = 0x00;
+ c->Request.CDB[2] = 0xcc; // Bytes 2-5 are 0xCC770014 to match with
+ c->Request.CDB[3] = 0x77; // the CC_CSMI_SAS_GET_PHY_INFO value
+ c->Request.CDB[4] = 0x00;
+ c->Request.CDB[5] = 0x14;
+ c->Request.CDB[6] = BMIC_CSMI_PASSTHRU;
+ c->Request.CDB[7] = (iocommand.IoctlHeader.Length >> 8) & 0xff;
+ c->Request.CDB[8] = iocommand.IoctlHeader.Length & 0xff;
+ c->Request.CDBLen = 16;
+
+ temp64.val = pci_map_single( h->pdev, &p,
+ iocommand.IoctlHeader.Length, PCI_DMA_BIDIRECTIONAL);
+ c->SG[0].Addr.lower = temp64.val32.lower;
+ c->SG[0].Addr.upper = temp64.val32.upper;
+ c->SG[0].Len = iocommand.IoctlHeader.Length;
+ c->SG[0].Ext = 0;
+ c->waiting = &wait;
+
+ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ addQ(&h->reqQ, c);
+ h->Qdepth++;
+ start_io(h);
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+ printk(KERN_WARNING "cciss: waiting for sysfs command to complete\n");
+ wait_for_completion(&wait);
+ printk(KERN_WARNING "cciss: sysfs command completed\n");
+
+ cmd_free(h, c, 0);
+
+ pci_unmap_single(h->pdev, (dma_addr_t)temp64.val,
+ iocommand.IoctlHeader.Length, PCI_DMA_BIDIRECTIONAL);
+ printk(KERN_WARNING "cciss: IOCTLHeader rturn = %d\n",
+ iocommand.IoctlHeader.ReturnCode);
+ return sprintf(buf, "%x %x %x %x%x%x%x%x%x%x%x %x%x%x%x%x%x%x%x "
+ "%x %x %x%x%x%x%x%x\n",
+ p.bDeviceType, p.bRestricted, p.bInitiatorPortProtocol,
+ p.bRestricted2[0],
+ p.bRestricted2[1],
+ p.bRestricted2[2],
+ p.bRestricted2[3],
+ p.bRestricted2[4],
+ p.bRestricted2[5],
+ p.bRestricted2[6],
+ p.bRestricted2[7],
+ p.bSASAddress[0],
+ p.bSASAddress[1],
+ p.bSASAddress[2],
+ p.bSASAddress[3],
+ p.bSASAddress[4],
+ p.bSASAddress[5],
+ p.bSASAddress[6],
+ p.bSASAddress[7],
+ p.bPhyIdentifier, p.bSignalClass,
+ p.bReserved[0],
+ p.bReserved[1],
+ p.bReserved[2],
+ p.bReserved[3],
+ p.bReserved[4],
+ p.bReserved[5]);
+}
+
+static DEVICE_ATTR(cciss_firmver, 0644, cciss_firmver_show, NULL);
+static DEVICE_ATTR(cciss_bus_id, 0644, cciss_bus_id_show, NULL);
+static DEVICE_ATTR(cciss_phyinfo, 0644, cciss_phyinfo_show, NULL);
+
+static int sas_error_check(__u8 phyId, __u8 portId, __u8 conn_rate)
+{
+ if( phyId == CSMI_SAS_USE_PORT_IDENTIFIER
+ && portId == CSMI_SAS_IGNORE_PORT) {
+ return CSMI_SAS_SELECT_PHY_OR_PORT;
+ }
+ if(phyId < CSMI_SAS_USE_PORT_IDENTIFIER) {
+ if(portId != CSMI_SAS_IGNORE_PORT)
+ return CSMI_SAS_PHY_DOES_NOT_MATCH_PORT;
+ }
+ if(portId < CSMI_SAS_IGNORE_PORT) {
+ if(phyId != CSMI_SAS_USE_PORT_IDENTIFIER)
+ return CSMI_SAS_PHY_CANNOT_BE_SELECTED;
+ }
+ if( conn_rate != CSMI_SAS_LINK_RATE_NEGOTIATE ||
+ conn_rate != CSMI_SAS_LINK_RATE_1_5_GBPS ||
+ conn_rate != CSMI_SAS_LINK_RATE_3_0_GBPS) {
+ return CSMI_SAS_LINK_RATE_OUT_OF_RANGE;
+ }
+ return 0;
+}
+
+/*
+ * end of sysfs stuff
+ */
+
+/*
* revalidate_allvol is for online array config utilities. After a
* utility reconfigures the drives in the array, it can use this function
* (through an ioctl) to make the driver zap any previous disk structs for
@@ -2383,6 +2537,11 @@ static int cciss_pci_init(ctlr_info_t *c

c->intr = pdev->irq;

+ pci_set_drvdata(pdev, c);
+ device_create_file(&(pdev->dev), &dev_attr_cciss_firmver);
+ device_create_file(&(pdev->dev), &dev_attr_cciss_bus_id);
+ device_create_file(&(pdev->dev), &dev_attr_cciss_phyinfo);
+
/*
* Memory base addr is first addr , the second points to the config
* table
diff -burNp lx2611.orig/drivers/block/cciss_cmd.h lx2611/drivers/block/cciss_cmd.h
--- lx2611.orig/drivers/block/cciss_cmd.h 2004-12-24 15:34:02.000000000 -0600
+++ lx2611/drivers/block/cciss_cmd.h 2005-02-08 14:13:11.000000000 -0600
@@ -29,7 +29,7 @@
#define XFER_NONE 0x00
#define XFER_WRITE 0x01
#define XFER_READ 0x02
-#define XFER_RSVD 0x03
+#define XFER_BIDIRECTIONAL 0x03

//task attribute
#define ATTR_UNTAGGED 0x00
@@ -129,6 +129,7 @@ typedef struct _ReadCapdata_struct
#define BMIC_WRITE 0x27
#define BMIC_CACHE_FLUSH 0xc2
#define CCISS_CACHE_FLUSH 0x01 //C2 was already being used by CCISS
+#define BMIC_CSMI_PASSTHRU 0x68;

//Command List Structure
typedef union _SCSI3Addr_struct {
diff -burNp lx2611.orig/drivers/block/cciss_scsi.c lx2611/drivers/block/cciss_scsi.c
--- lx2611.orig/drivers/block/cciss_scsi.c 2005-01-26 14:15:36.000000000 -0600
+++ lx2611/drivers/block/cciss_scsi.c 2005-02-09 16:53:52.000000000 -0600
@@ -1302,7 +1302,7 @@ cciss_scsi_queue_command (struct scsi_cm
// and sets both inlen and outlen to non-zero. ( see
// ../scsi/scsi_ioctl.c:scsi_ioctl_send_command() )

- cp->Request.Type.Direction = XFER_RSVD;
+ cp->Request.Type.Direction = XFER_BIDIRECTIONAL;
// This is technically wrong, and cciss controllers should
// reject it with CMD_INVALID, which is the most correct
// response, but non-fibre backends appear to let it
diff -burNp lx2611.orig/include/linux/cciss_ioctl.h lx2611/include/linux/cciss_ioctl.h
--- lx2611.orig/include/linux/cciss_ioctl.h 2004-12-24 15:34:32.000000000 -0600
+++ lx2611/include/linux/cciss_ioctl.h 2005-02-10 10:31:31.000000000 -0600
@@ -60,7 +60,7 @@ typedef __u32 DriverVer_type;
#define XFER_NONE 0x00
#define XFER_WRITE 0x01
#define XFER_READ 0x02
-#define XFER_RSVD 0x03
+#define XFER_BIDIRECTIONAL 0x03

//task attribute
#define ATTR_UNTAGGED 0x00
@@ -162,6 +162,193 @@ typedef struct _ErrorInfo_struct {
#pragma pack()
#endif /* CCISS_CMD_H */

+// (IoctlHeader.Direction, Linux only)
+#define CSMI_SAS_DATA_READ 0
+#define CSMI_SAS_DATA_WRITE 1
+#define CSMI_SAS_USE_PORT_IDENTIFIER 0xFF
+#define CSMI_SAS_IGNORE_PORT 0xFF
+#define CSMI_SAS_LINK_RATE_OUT_OF_RANGE 2001
+#define CSMI_SAS_PHY_DOES_NOT_EXIST 2002
+#define CSMI_SAS_PHY_DOES_NOT_MATCH_PORT 2003
+#define CSMI_SAS_PHY_CANNOT_BE_SELECTED 2004
+#define CSMI_SAS_SELECT_PHY_OR_PORT 2005
+#define CSMI_SAS_LINK_RATE_NEGOTIATE 0x00
+#define CSMI_SAS_LINK_RATE_1_5_GBPS 0x08
+#define CSMI_SAS_LINK_RATE_3_0_GBPS 0x09
+
+
+/* SAS command structures */
+
+typedef struct _IOCTL_HEADER {
+ __u32 IOControllerNumber;
+ __u32 Length;
+ __u32 ReturnCode;
+ __u32 Timeout;
+ __u16 Direction;
+} IOCTL_HEADER;
+
+// CC_CSMI_SAS_GET_PHY_INFO
+typedef struct _CSMI_SAS_IDENTIFY {
+ __u8 bDeviceType;
+ __u8 bRestricted;
+ __u8 bInitiatorPortProtocol;
+ __u8 bTargetPortProtocol;
+ __u8 bRestricted2[8];
+ __u8 bSASAddress[8];
+ __u8 bPhyIdentifier;
+ __u8 bSignalClass;
+ __u8 bReserved[6];
+} CSMI_SAS_IDENTIFY;
+
+typedef struct _CSMI_SAS_PHY_ENTITY {
+ CSMI_SAS_IDENTIFY Identify;
+ __u8 bPortIdentifier;
+ __u8 bNegotiatedLinkRate;
+ __u8 bMinimumLinkRate;
+ __u8 bMaximumLinkRate;
+ __u8 bPhyChangeCount;
+ __u8 bAutoDiscover;
+ __u8 bReserved[2];
+ CSMI_SAS_IDENTIFY Attached;
+} CSMI_SAS_PHY_ENTITY;
+
+typedef struct _CSMI_SAS_PHY_INFO {
+ __u8 bNumberOfPhys;
+ __u8 bReserved[3];
+ CSMI_SAS_PHY_ENTITY Phy[32];
+} CSMI_SAS_PHY_INFO;
+
+typedef struct _CSMI_SAS_PHY_INFO_BUFFER {
+ IOCTL_HEADER IoctlHeader;
+ CSMI_SAS_PHY_INFO Information;
+} CSMI_SAS_PHY_INFO_BUFFER;
+
+// CC_CSMI_SAS_SET_PHY_INFO
+typedef struct _CSMI_SAS_SET_PHY_INFO {
+ __u8 bPhyIdentifier;
+ __u8 bNegotiatedLinkRate;
+ __u8 bProgrammedMinimumLinkRate;
+ __u8 bProgrammedMaximumLinkRate;
+ __u8 bSignalClass;
+ __u8 bReserved[3];
+} CSMI_SAS_SET_PHY_INFO;
+
+typedef struct _CSMI_SAS_SET_PHY_INFO_BUFFER {
+ IOCTL_HEADER IoctlHeader;
+ CSMI_SAS_SET_PHY_INFO Information;
+} CSMI_SAS_SET_PHY_INFO_BUFFER;
+
+// CC_CSMI_SAS_GET_LINK_ERRORS
+typedef struct _CSMI_SAS_LINK_ERRORS {
+ __u8 bPhyIdentifier;
+ __u8 bResetCounts;
+ __u8 bReserved[2];
+ __u32 uInvalidDwordCount;
+ __u32 uRunningDisparityErrorCount;
+ __u32 uLossOfDwordSyncCount;
+ __u32 uPhyResetProblemCount;
+} CSMI_SAS_LINK_ERRORS;
+
+typedef struct _CSMI_SAS_LINK_ERRORS_BUFFER {
+ IOCTL_HEADER IoctlHeader;
+ CSMI_SAS_LINK_ERRORS Information;
+} CSMI_SAS_LINK_ERRORS_BUFFER;
+
+typedef struct _CSMI_SAS_SMP_REQUEST {
+ __u8 bFrameType;
+ __u8 bFunction;
+ __u8 bReserved[2];
+ __u8 bAdditionalRequestBytes[1016];
+} CSMI_SAS_SMP_REQUEST;
+
+typedef struct _CSMI_SAS_SMP_RESPONSE {
+ __u8 bFrameType;
+ __u8 bFunction;
+ __u8 bFunctionResult;
+ __u8 bReserved;
+ __u8 bAdditionalResponseBytes[1016];
+} CSMI_SAS_SMP_RESPONSE;
+
+typedef struct _CSMI_SAS_SMP_PASSTHRU {
+ __u8 bPhyIdentifier;
+ __u8 bPortIdentifier;
+ __u8 bConnectionRate;
+ __u8 bReserved;
+ __u8 bDestinationSASAddress[8];
+ __u32 uRequestLength;
+ CSMI_SAS_SMP_REQUEST Request;
+ __u8 bConnectionStatus;
+ __u8 bReserved2[3];
+ __u32 uResponseBytes;
+ CSMI_SAS_SMP_RESPONSE Response;
+} CSMI_SAS_SMP_PASSTHRU;
+
+typedef struct _CSMI_SAS_SMP_PASSTHRU_BUFFER {
+ IOCTL_HEADER IoctlHeader;
+ CSMI_SAS_SMP_PASSTHRU Parameters;
+} CSMI_SAS_SMP_PASSTHRU_BUFFER;
+
+typedef struct _CSMI_SAS_SSP_PASSTHRU_STATUS {
+ __u8 bConnectionStatus;
+ __u8 bReserved[3];
+ __u8 bDataPresent;
+ __u8 bStatus;
+ __u8 bResponseLength[2];
+ __u8 bResponse[256];
+ __u32 uDataBytes;
+} CSMI_SAS_SSP_PASSTHRU_STATUS;
+
+typedef struct _CSMI_SAS_SSP_PASSTHRU {
+ __u8 bPhyIdentifier;
+ __u8 bPortIdentifier;
+ __u8 bConnectionRate;
+ __u8 bReserved;
+ __u8 bDestinationSASAddress[8];
+ __u8 bLun[8];
+ __u8 bCDBLength;
+ __u8 bAdditionalCDBLength;
+ __u8 bReserved2[2];
+ __u8 bCDB[16];
+ __u32 uFlags;
+ __u8 bAdditionalCDB[24];
+ __u32 uDataLength;
+} CSMI_SAS_SSP_PASSTHRU;
+
+typedef struct _CSMI_SAS_SSP_PASSTHRU_BUFFER {
+ IOCTL_HEADER IoctlHeader;
+ CSMI_SAS_SSP_PASSTHRU Parameters;
+ CSMI_SAS_SSP_PASSTHRU_STATUS Status;
+ __u8 bDataBuffer[1];
+} CSMI_SAS_SSP_PASSTHRU_BUFFER;
+
+typedef struct _CSMI_SAS_STP_PASSTHRU {
+ __u8 bPhyIdentifier;
+ __u8 bPortIdentifier;
+ __u8 bConnectionRate;
+ __u8 bReserved;
+ __u8 bDestinationSASAddress[8];
+ __u8 bReserved2[4];
+ __u8 bCommandFIS[20];
+ __u32 uFlags;
+ __u32 uDataLength;
+} CSMI_SAS_STP_PASSTHRU;
+
+typedef struct _CSMI_SAS_STP_PASSTHRU_STATUS {
+ __u8 bConnectionStatus;
+ __u8 bReserved[3];
+ __u8 bStatusFIS[20];
+ __u32 uSCR[16];
+ __u32 uDataBytes;
+} CSMI_SAS_STP_PASSTHRU_STATUS;
+
+typedef struct _CSMI_SAS_STP_PASSTHRU_BUFFER {
+ IOCTL_HEADER IoctlHeader;
+ CSMI_SAS_STP_PASSTHRU Parameters;
+ CSMI_SAS_STP_PASSTHRU_STATUS Status;
+ __u8 bDataBuffer[1];
+} CSMI_SAS_STP_PASSTHRU_BUFFER;
+
+
typedef struct _IOCTL_Command_struct {
LUNAddr_struct LUN_info;
RequestBlock_struct Request;
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/