* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * Questions/Comments/Bugfixes to arrays@compaq.com
+ * Questions/Comments/Bugfixes to iss_storagedev@hp.com
*
* Author: Stephen M. Cameron
*/
through the array controller. Note in particular, neither
physical nor logical disks are presented through the scsi layer. */
-#include "../scsi/scsi.h"
-#include "../scsi/hosts.h"
-#include <asm/atomic.h>
#include <linux/timer.h>
#include <linux/completion.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <asm/atomic.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
#include "cciss_scsi.h"
+#define CCISS_ABORT_MSG 0x00
+#define CCISS_RESET_MSG 0x01
+
/* some prototypes... */
static int sendcmd(
__u8 cmd,
int cmd_type);
-const char *cciss_scsi_info(struct Scsi_Host *sa);
-
-int cciss_scsi_proc_info(
+static int cciss_scsi_proc_info(
struct Scsi_Host *sh,
char *buffer, /* data buffer */
char **start, /* where data in buffer starts */
int length, /* length of data in buffer */
int func); /* 0 == read, 1 == write */
-int cciss_scsi_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *));
-#if 0
-int cciss_scsi_abort(Scsi_Cmnd *cmd);
-#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
-int cciss_scsi_reset(Scsi_Cmnd *cmd, unsigned int reset_flags);
-#else
-int cciss_scsi_reset(Scsi_Cmnd *cmd);
-#endif
-#endif
+static int cciss_scsi_queue_command (struct scsi_cmnd *cmd,
+ void (* done)(struct scsi_cmnd *));
+static int cciss_eh_device_reset_handler(struct scsi_cmnd *);
+static int cciss_eh_abort_handler(struct scsi_cmnd *);
static struct cciss_scsi_hba_t ccissscsi[MAX_CTLR] = {
{ .name = "cciss0", .ndevices = 0 },
{ .name = "cciss7", .ndevices = 0 },
};
-static Scsi_Host_Template cciss_driver_template = {
+static struct scsi_host_template cciss_driver_template = {
.module = THIS_MODULE,
.name = "cciss",
.proc_name = "cciss",
.sg_tablesize = MAXSGENTRIES,
.cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
+ /* Can't have eh_bus_reset_handler or eh_host_reset_handler for cciss */
+ .eh_device_reset_handler= cciss_eh_device_reset_handler,
+ .eh_abort_handler = cciss_eh_abort_handler,
};
#pragma pack(1)
CommandList_struct cmd;
ErrorInfo_struct Err;
__u32 busaddr;
+ __u32 pad;
};
#pragma pack()
if (shba == NULL)
return;
shba->scsi_host = NULL;
- shba->lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_init(&shba->lock);
shba->registered = 0;
if (scsi_cmd_stack_setup(cntl_num, shba) != 0) {
kfree(shba);
static void
complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag)
{
- Scsi_Cmnd *cmd;
+ struct scsi_cmnd *cmd;
ctlr_info_t *ctlr;
u64bit addr64;
ErrorInfo_struct *ei;
return;
}
- cmd = (Scsi_Cmnd *) cp->scsi_cmd;
+ cmd = (struct scsi_cmnd *) cp->scsi_cmd;
ctlr = hba[cp->ctlr];
/* undo the DMA mappings */
if (cmd->use_sg) {
pci_unmap_sg(ctlr->pdev,
cmd->buffer, cmd->use_sg,
- scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ cmd->sc_data_direction);
}
else if (cmd->request_bufflen) {
addr64.val32.lower = cp->SG[0].Addr.lower;
addr64.val32.upper = cp->SG[0].Addr.upper;
pci_unmap_single(ctlr->pdev, (dma_addr_t) addr64.val,
cmd->request_bufflen,
- scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ cmd->sc_data_direction);
}
cmd->result = (DID_OK << 16); /* host byte */
cciss_scsi_detect(int ctlr)
{
struct Scsi_Host *sh;
+ int error;
sh = scsi_host_alloc(&cciss_driver_template, sizeof(struct ctlr_info *));
if (sh == NULL)
- return 0;
-
+ goto fail;
sh->io_port = 0; // good enough? FIXME,
sh->n_io_port = 0; // I don't think we use these two...
-
sh->this_id = SELF_SCSI_ID;
((struct cciss_scsi_adapter_data_t *)
hba[ctlr]->scsi_ctlr)->scsi_host = (void *) sh;
sh->hostdata[0] = (unsigned long) hba[ctlr];
- sh->irq = hba[ctlr]->intr;
+ sh->irq = hba[ctlr]->intr[SIMPLE_MODE_INT];
sh->unique_id = sh->irq;
- scsi_add_host(sh, &hba[ctlr]->pdev->dev); /* XXX handle failure */
+ error = scsi_add_host(sh, &hba[ctlr]->pdev->dev);
+ if (error)
+ goto fail_host_put;
scsi_scan_host(sh);
-
return 1;
-}
-static void __exit cleanup_cciss_module(void);
+ fail_host_put:
+ scsi_host_put(sh);
+ fail:
+ return 0;
+}
static void
cciss_unmap_one(struct pci_dev *pdev,
cp->Request.Type.Direction = direction;
/* Fill in the SG list and do dma mapping */
- cciss_map_one(c->pdev, cp,
- (unsigned char *) buf, bufsize,
- scsi_to_pci_dma_dir(SCSI_DATA_READ));
+ cciss_map_one(c->pdev, cp, (unsigned char *) buf,
+ bufsize, DMA_FROM_DEVICE);
cp->waiting = &wait;
wait_for_completion(&wait);
/* undo the dma mapping */
- cciss_unmap_one(c->pdev, cp, bufsize,
- scsi_to_pci_dma_dir(SCSI_DATA_READ));
-
+ cciss_unmap_one(c->pdev, cp, bufsize, DMA_FROM_DEVICE);
return(0);
}
static int
cciss_scsi_do_inquiry(ctlr_info_t *c, unsigned char *scsi3addr,
- InquiryData_struct *buf)
+ unsigned char *buf, unsigned char bufsize)
{
int rc;
CommandList_struct *cp;
cdb[1] = 0;
cdb[2] = 0;
cdb[3] = 0;
- cdb[4] = sizeof(*buf) & 0xff;
+ cdb[4] = bufsize;
cdb[5] = 0;
rc = cciss_scsi_do_simple_cmd(c, cp, scsi3addr, cdb,
- 6, (unsigned char *) buf,
- sizeof(*buf), XFER_READ);
+ 6, buf, bufsize, XFER_READ);
if (rc != 0) return rc; /* something went wrong */
that though.
*/
-
+#define OBDR_TAPE_INQ_SIZE 49
+#define OBDR_TAPE_SIG "$DR-10"
ReportLunData_struct *ld_buff;
- InquiryData_struct *inq_buff;
+ unsigned char *inq_buff;
unsigned char scsi3addr[8];
ctlr_info_t *c;
__u32 num_luns=0;
int i;
c = (ctlr_info_t *) hba[cntl_num];
- ld_buff = kmalloc(reportlunsize, GFP_KERNEL);
+ ld_buff = kzalloc(reportlunsize, GFP_KERNEL);
if (ld_buff == NULL) {
printk(KERN_ERR "cciss: out of memory\n");
return;
}
- memset(ld_buff, 0, reportlunsize);
- inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL);
+ inq_buff = kmalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL);
if (inq_buff == NULL) {
printk(KERN_ERR "cciss: out of memory\n");
kfree(ld_buff);
/* for each physical lun, do an inquiry */
if (ld_buff->LUN[i][3] & 0xC0) continue;
- memset(inq_buff, 0, sizeof(InquiryData_struct));
+ memset(inq_buff, 0, OBDR_TAPE_INQ_SIZE);
memcpy(&scsi3addr[0], &ld_buff->LUN[i][0], 8);
- if (cciss_scsi_do_inquiry(hba[cntl_num],
- scsi3addr, inq_buff) != 0)
- {
+ if (cciss_scsi_do_inquiry(hba[cntl_num], scsi3addr, inq_buff,
+ (unsigned char) OBDR_TAPE_INQ_SIZE) != 0) {
/* Inquiry failed (msg printed already) */
devtype = 0; /* so we will skip this device. */
} else /* what kind of device is this? */
- devtype = (inq_buff->data_byte[0] & 0x1f);
+ devtype = (inq_buff[0] & 0x1f);
switch (devtype)
{
+ case 0x05: /* CD-ROM */ {
+
+ /* We don't *really* support actual CD-ROM devices,
+ * just this "One Button Disaster Recovery" tape drive
+ * which temporarily pretends to be a CD-ROM drive.
+ * So we check that the device is really an OBDR tape
+ * device by checking for "$DR-10" in bytes 43-48 of
+ * the inquiry data.
+ */
+ char obdr_sig[7];
+
+ strncpy(obdr_sig, &inq_buff[43], 6);
+ obdr_sig[6] = '\0';
+ if (strncmp(obdr_sig, OBDR_TAPE_SIG, 6) != 0)
+ /* Not OBDR device, ignore it. */
+ break;
+ }
+ /* fall through . . . */
case 0x01: /* sequential access, (tape) */
case 0x08: /* medium changer */
if (ncurrent >= CCISS_MAX_SCSI_DEVS_PER_HBA) {
}
-int
+static int
cciss_scsi_proc_info(struct Scsi_Host *sh,
char *buffer, /* data buffer */
char **start, /* where data in buffer starts */
int buflen, datalen;
ctlr_info_t *ci;
+ int i;
int cntl_num;
cntl_num = ci->ctlr; /* Get our index into the hba[] array */
if (func == 0) { /* User is reading from /proc/scsi/ciss*?/?* */
- buflen = sprintf(buffer, "hostnum=%d\n", sh->host_no);
-
+ buflen = sprintf(buffer, "cciss%d: SCSI host: %d\n",
+ cntl_num, sh->host_no);
+
+ /* this information is needed by apps to know which cciss
+ device corresponds to which scsi host number without
+ having to open a scsi target device node. The device
+ information is not a duplicate of /proc/scsi/scsi because
+ the two may be out of sync due to scsi hotplug, rather
+ this info is for an app to be able to use to know how to
+ get them back in sync. */
+
+ for (i=0;i<ccissscsi[cntl_num].ndevices;i++) {
+ struct cciss_scsi_dev_t *sd = &ccissscsi[cntl_num].dev[i];
+ buflen += sprintf(&buffer[buflen], "c%db%dt%dl%d %02d "
+ "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ sh->host_no, sd->bus, sd->target, sd->lun,
+ sd->devtype,
+ sd->scsi3addr[0], sd->scsi3addr[1],
+ sd->scsi3addr[2], sd->scsi3addr[3],
+ sd->scsi3addr[4], sd->scsi3addr[5],
+ sd->scsi3addr[6], sd->scsi3addr[7]);
+ }
datalen = buflen - offset;
if (datalen < 0) { /* they're reading past EOF. */
datalen = 0;
buffer, length);
}
-/* this is via the generic proc support */
-const char *
-cciss_scsi_info(struct Scsi_Host *sa)
-{
- static char buf[300];
- ctlr_info_t *ci;
-
- /* probably need to work on putting a bit more info in here... */
- /* this is output via the /proc filesystem. */
-
- ci = (ctlr_info_t *) sa->hostdata[0];
-
- sprintf(buf, "%s %c%c%c%c\n",
- ci->product_name,
- ci->firm_ver[0],
- ci->firm_ver[1],
- ci->firm_ver[2],
- ci->firm_ver[3]);
-
- return buf;
-}
-
-
-/* cciss_scatter_gather takes a Scsi_Cmnd, (cmd), and does the pci
+/* cciss_scatter_gather takes a struct scsi_cmnd, (cmd), and does the pci
dma mapping and fills in the scatter gather entries of the
cciss command, cp. */
static void
cciss_scatter_gather(struct pci_dev *pdev,
CommandList_struct *cp,
- Scsi_Cmnd *cmd)
+ struct scsi_cmnd *cmd)
{
unsigned int use_sg, nsegs=0, len;
struct scatterlist *scatter = (struct scatterlist *) cmd->buffer;
addr64 = (__u64) pci_map_single(pdev,
cmd->request_buffer,
cmd->request_bufflen,
- scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ cmd->sc_data_direction);
cp->SG[0].Addr.lower =
(__u32) (addr64 & (__u64) 0x00000000FFFFFFFF);
else if (cmd->use_sg <= MAXSGENTRIES) { /* not too many addrs? */
use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg,
- scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ cmd->sc_data_direction);
for (nsegs=0; nsegs < use_sg; nsegs++) {
addr64 = (__u64) sg_dma_address(&scatter[nsegs]);
}
-int
-cciss_scsi_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
+static int
+cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
{
ctlr_info_t **c;
int ctlr, rc;
cp->Request.Timeout = 0;
memset(cp->Request.CDB, 0, sizeof(cp->Request.CDB));
- if (cmd->cmd_len > sizeof(cp->Request.CDB)) BUG();
+ BUG_ON(cmd->cmd_len > sizeof(cp->Request.CDB));
cp->Request.CDBLen = cmd->cmd_len;
memcpy(cp->Request.CDB, cmd->cmnd, cmd->cmd_len);
cp->Request.Type.Type = TYPE_CMD;
cp->Request.Type.Attribute = ATTR_SIMPLE;
switch(cmd->sc_data_direction)
{
- case SCSI_DATA_WRITE: cp->Request.Type.Direction = XFER_WRITE; break;
- case SCSI_DATA_READ: cp->Request.Type.Direction = XFER_READ; break;
- case SCSI_DATA_NONE: cp->Request.Type.Direction = XFER_NONE; break;
-
- case SCSI_DATA_UNKNOWN:
+ case DMA_TO_DEVICE: cp->Request.Type.Direction = XFER_WRITE; break;
+ case DMA_FROM_DEVICE: cp->Request.Type.Direction = XFER_READ; break;
+ case DMA_NONE: cp->Request.Type.Direction = XFER_NONE; break;
+ case DMA_BIDIRECTIONAL:
// This can happen if a buggy application does a scsi passthru
// and sets both inlen and outlen to non-zero. ( see
// ../scsi/scsi_ioctl.c:scsi_ioctl_send_command() )
CPQ_TAPE_LOCK(ctlr, flags);
size = sprintf(buffer + *len,
- " Sequential access devices: %d\n\n",
+ "Sequential access devices: %d\n\n",
ccissscsi[ctlr].ndevices);
CPQ_TAPE_UNLOCK(ctlr, flags);
*pos += size; *len += size;
}
+/* Need at least one of these error handlers to keep ../scsi/hosts.c from
+ * complaining. Doing a host- or bus-reset can't do anything good here.
+ * Despite what it might say in scsi_error.c, there may well be commands
+ * on the controller, as the cciss driver registers twice, once as a block
+ * device for the logical drives, and once as a scsi device, for any tape
+ * drives. So we know there are no commands out on the tape drives, but we
+ * don't know there are no commands on the controller, and it is likely
+ * that there probably are, as the cciss block device is most commonly used
+ * as a boot device (embedded controller on HP/Compaq systems.)
+*/
+
+static int cciss_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
+{
+ int rc;
+ CommandList_struct *cmd_in_trouble;
+ ctlr_info_t **c;
+ int ctlr;
+
+ /* find the controller to which the command to be aborted was sent */
+ c = (ctlr_info_t **) &scsicmd->device->host->hostdata[0];
+ if (c == NULL) /* paranoia */
+ return FAILED;
+ ctlr = (*c)->ctlr;
+ printk(KERN_WARNING "cciss%d: resetting tape drive or medium changer.\n", ctlr);
+
+ /* find the command that's giving us trouble */
+ cmd_in_trouble = (CommandList_struct *) scsicmd->host_scribble;
+ if (cmd_in_trouble == NULL) { /* paranoia */
+ return FAILED;
+ }
+ /* send a reset to the SCSI LUN which the command was sent to */
+ rc = sendcmd(CCISS_RESET_MSG, ctlr, NULL, 0, 2, 0, 0,
+ (unsigned char *) &cmd_in_trouble->Header.LUN.LunAddrBytes[0],
+ TYPE_MSG);
+ /* sendcmd turned off interrputs on the board, turn 'em back on. */
+ (*c)->access.set_intr_mask(*c, CCISS_INTR_ON);
+ if (rc == 0)
+ return SUCCESS;
+ printk(KERN_WARNING "cciss%d: resetting device failed.\n", ctlr);
+ return FAILED;
+}
+
+static int cciss_eh_abort_handler(struct scsi_cmnd *scsicmd)
+{
+ int rc;
+ CommandList_struct *cmd_to_abort;
+ ctlr_info_t **c;
+ int ctlr;
+
+ /* find the controller to which the command to be aborted was sent */
+ c = (ctlr_info_t **) &scsicmd->device->host->hostdata[0];
+ if (c == NULL) /* paranoia */
+ return FAILED;
+ ctlr = (*c)->ctlr;
+ printk(KERN_WARNING "cciss%d: aborting tardy SCSI cmd\n", ctlr);
+
+ /* find the command to be aborted */
+ cmd_to_abort = (CommandList_struct *) scsicmd->host_scribble;
+ if (cmd_to_abort == NULL) /* paranoia */
+ return FAILED;
+ rc = sendcmd(CCISS_ABORT_MSG, ctlr, &cmd_to_abort->Header.Tag,
+ 0, 2, 0, 0,
+ (unsigned char *) &cmd_to_abort->Header.LUN.LunAddrBytes[0],
+ TYPE_MSG);
+ /* sendcmd turned off interrputs on the board, turn 'em back on. */
+ (*c)->access.set_intr_mask(*c, CCISS_INTR_ON);
+ if (rc == 0)
+ return SUCCESS;
+ return FAILED;
+
+}
+
#else /* no CONFIG_CISS_SCSI_TAPE */
/* If no tape support, then these become defined out of existence */