| /* | 
 |  *    Disk Array driver for HP Smart Array controllers, SCSI Tape module. | 
 |  *    (C) Copyright 2001, 2007 Hewlett-Packard Development Company, L.P. | 
 |  * | 
 |  *    This program is free software; you can redistribute it and/or modify | 
 |  *    it under the terms of the GNU General Public License as published by | 
 |  *    the Free Software Foundation; version 2 of the License. | 
 |  * | 
 |  *    This program is distributed in the hope that it will be useful, | 
 |  *    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  *    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 
 |  *    General Public License for more details. | 
 |  * | 
 |  *    You should have received a copy of the GNU General Public License | 
 |  *    along with this program; if not, write to the Free Software | 
 |  *    Foundation, Inc., 59 Temple Place, Suite 300, Boston, MA | 
 |  *    02111-1307, USA. | 
 |  * | 
 |  *    Questions/Comments/Bugfixes to iss_storagedev@hp.com | 
 |  *     | 
 |  *    Author: Stephen M. Cameron | 
 |  */ | 
 | #ifdef CONFIG_CISS_SCSI_TAPE | 
 |  | 
 | /* Here we have code to present the driver as a scsi driver  | 
 |    as it is simultaneously presented as a block driver.  The  | 
 |    reason for doing this is to allow access to SCSI tape drives | 
 |    through the array controller.  Note in particular, neither  | 
 |    physical nor logical disks are presented through the scsi layer. */ | 
 |  | 
 | #include <linux/timer.h> | 
 | #include <linux/completion.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/string.h> | 
 |  | 
 | #include <asm/atomic.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 | 
 |  | 
 | static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff, | 
 | 	size_t size, | 
 | 	__u8 page_code, unsigned char *scsi3addr, | 
 | 	int cmd_type); | 
 |  | 
 | static CommandList_struct *cmd_alloc(ctlr_info_t *h); | 
 | static CommandList_struct *cmd_special_alloc(ctlr_info_t *h); | 
 | static void cmd_free(ctlr_info_t *h, CommandList_struct *c); | 
 | static void cmd_special_free(ctlr_info_t *h, CommandList_struct *c); | 
 |  | 
 | static int cciss_scsi_proc_info( | 
 | 		struct Scsi_Host *sh, | 
 | 		char *buffer, /* data buffer */ | 
 | 		char **start, 	   /* where data in buffer starts */ | 
 | 		off_t offset,	   /* offset from start of imaginary file */ | 
 | 		int length, 	   /* length of data in buffer */ | 
 | 		int func);	   /* 0 == read, 1 == write */ | 
 |  | 
 | static int cciss_scsi_queue_command (struct Scsi_Host *h, | 
 | 				     struct scsi_cmnd *cmd); | 
 | 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 = "cciss1", .ndevices = 0 }, | 
 | 	{ .name = "cciss2", .ndevices = 0 }, | 
 | 	{ .name = "cciss3", .ndevices = 0 }, | 
 | 	{ .name = "cciss4", .ndevices = 0 }, | 
 | 	{ .name = "cciss5", .ndevices = 0 }, | 
 | 	{ .name = "cciss6", .ndevices = 0 }, | 
 | 	{ .name = "cciss7", .ndevices = 0 }, | 
 | }; | 
 |  | 
 | static struct scsi_host_template cciss_driver_template = { | 
 | 	.module			= THIS_MODULE, | 
 | 	.name			= "cciss", | 
 | 	.proc_name		= "cciss", | 
 | 	.proc_info		= cciss_scsi_proc_info, | 
 | 	.queuecommand		= cciss_scsi_queue_command, | 
 | 	.this_id		= 7, | 
 | 	.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) | 
 |  | 
 | #define SCSI_PAD_32 8 | 
 | #define SCSI_PAD_64 8 | 
 |  | 
 | struct cciss_scsi_cmd_stack_elem_t { | 
 | 	CommandList_struct cmd; | 
 | 	ErrorInfo_struct Err; | 
 | 	__u32 busaddr; | 
 | 	int cmdindex; | 
 | 	u8 pad[IS_32_BIT * SCSI_PAD_32 + IS_64_BIT * SCSI_PAD_64]; | 
 | }; | 
 |  | 
 | #pragma pack() | 
 |  | 
 | #pragma pack(1) | 
 | struct cciss_scsi_cmd_stack_t { | 
 | 	struct cciss_scsi_cmd_stack_elem_t *pool; | 
 | 	struct cciss_scsi_cmd_stack_elem_t **elem; | 
 | 	dma_addr_t cmd_pool_handle; | 
 | 	int top; | 
 | 	int nelems; | 
 | }; | 
 | #pragma pack() | 
 |  | 
 | struct cciss_scsi_adapter_data_t { | 
 | 	struct Scsi_Host *scsi_host; | 
 | 	struct cciss_scsi_cmd_stack_t cmd_stack; | 
 | 	SGDescriptor_struct **cmd_sg_list; | 
 | 	int registered; | 
 | 	spinlock_t lock; // to protect ccissscsi[ctlr];  | 
 | }; | 
 |  | 
 | #define CPQ_TAPE_LOCK(h, flags) spin_lock_irqsave( \ | 
 | 	&h->scsi_ctlr->lock, flags); | 
 | #define CPQ_TAPE_UNLOCK(h, flags) spin_unlock_irqrestore( \ | 
 | 	&h->scsi_ctlr->lock, flags); | 
 |  | 
 | static CommandList_struct * | 
 | scsi_cmd_alloc(ctlr_info_t *h) | 
 | { | 
 | 	/* assume only one process in here at a time, locking done by caller. */ | 
 | 	/* use h->lock */ | 
 | 	/* might be better to rewrite how we allocate scsi commands in a way that */ | 
 | 	/* needs no locking at all. */ | 
 |  | 
 | 	/* take the top memory chunk off the stack and return it, if any. */ | 
 | 	struct cciss_scsi_cmd_stack_elem_t *c; | 
 | 	struct cciss_scsi_adapter_data_t *sa; | 
 | 	struct cciss_scsi_cmd_stack_t *stk; | 
 | 	u64bit temp64; | 
 |  | 
 | 	sa = h->scsi_ctlr; | 
 | 	stk = &sa->cmd_stack;  | 
 |  | 
 | 	if (stk->top < 0)  | 
 | 		return NULL; | 
 | 	c = stk->elem[stk->top]; 	 | 
 | 	/* memset(c, 0, sizeof(*c)); */ | 
 | 	memset(&c->cmd, 0, sizeof(c->cmd)); | 
 | 	memset(&c->Err, 0, sizeof(c->Err)); | 
 | 	/* set physical addr of cmd and addr of scsi parameters */ | 
 | 	c->cmd.busaddr = c->busaddr;  | 
 | 	c->cmd.cmdindex = c->cmdindex; | 
 | 	/* (__u32) (stk->cmd_pool_handle +  | 
 | 		(sizeof(struct cciss_scsi_cmd_stack_elem_t)*stk->top)); */ | 
 |  | 
 | 	temp64.val = (__u64) (c->busaddr + sizeof(CommandList_struct)); | 
 | 	/* (__u64) (stk->cmd_pool_handle +  | 
 | 		(sizeof(struct cciss_scsi_cmd_stack_elem_t)*stk->top) + | 
 | 		 sizeof(CommandList_struct)); */ | 
 | 	stk->top--; | 
 | 	c->cmd.ErrDesc.Addr.lower = temp64.val32.lower; | 
 | 	c->cmd.ErrDesc.Addr.upper = temp64.val32.upper; | 
 | 	c->cmd.ErrDesc.Len = sizeof(ErrorInfo_struct); | 
 | 	 | 
 | 	c->cmd.ctlr = h->ctlr; | 
 | 	c->cmd.err_info = &c->Err; | 
 |  | 
 | 	return (CommandList_struct *) c; | 
 | } | 
 |  | 
 | static void  | 
 | scsi_cmd_free(ctlr_info_t *h, CommandList_struct *c) | 
 | { | 
 | 	/* assume only one process in here at a time, locking done by caller. */ | 
 | 	/* use h->lock */ | 
 | 	/* drop the free memory chunk on top of the stack. */ | 
 |  | 
 | 	struct cciss_scsi_adapter_data_t *sa; | 
 | 	struct cciss_scsi_cmd_stack_t *stk; | 
 |  | 
 | 	sa = h->scsi_ctlr; | 
 | 	stk = &sa->cmd_stack;  | 
 | 	stk->top++; | 
 | 	if (stk->top >= stk->nelems) { | 
 | 		dev_err(&h->pdev->dev, | 
 | 			"scsi_cmd_free called too many times.\n"); | 
 | 		BUG(); | 
 | 	} | 
 | 	stk->elem[stk->top] = (struct cciss_scsi_cmd_stack_elem_t *) c; | 
 | } | 
 |  | 
 | static int | 
 | scsi_cmd_stack_setup(ctlr_info_t *h, struct cciss_scsi_adapter_data_t *sa) | 
 | { | 
 | 	int i; | 
 | 	struct cciss_scsi_cmd_stack_t *stk; | 
 | 	size_t size; | 
 |  | 
 | 	stk = &sa->cmd_stack; | 
 | 	stk->nelems = cciss_tape_cmds + 2; | 
 | 	sa->cmd_sg_list = cciss_allocate_sg_chain_blocks(h, | 
 | 		h->chainsize, stk->nelems); | 
 | 	if (!sa->cmd_sg_list && h->chainsize > 0) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	size = sizeof(struct cciss_scsi_cmd_stack_elem_t) * stk->nelems; | 
 |  | 
 | 	/* Check alignment, see cciss_cmd.h near CommandList_struct def. */ | 
 | 	BUILD_BUG_ON((sizeof(*stk->pool) % COMMANDLIST_ALIGNMENT) != 0); | 
 | 	/* pci_alloc_consistent guarantees 32-bit DMA address will be used */ | 
 | 	stk->pool = (struct cciss_scsi_cmd_stack_elem_t *) | 
 | 		pci_alloc_consistent(h->pdev, size, &stk->cmd_pool_handle); | 
 |  | 
 | 	if (stk->pool == NULL) { | 
 | 		cciss_free_sg_chain_blocks(sa->cmd_sg_list, stk->nelems); | 
 | 		sa->cmd_sg_list = NULL; | 
 | 		return -ENOMEM; | 
 | 	} | 
 | 	stk->elem = kmalloc(sizeof(stk->elem[0]) * stk->nelems, GFP_KERNEL); | 
 | 	if (!stk->elem) { | 
 | 		pci_free_consistent(h->pdev, size, stk->pool, | 
 | 		stk->cmd_pool_handle); | 
 | 		return -1; | 
 | 	} | 
 | 	for (i = 0; i < stk->nelems; i++) { | 
 | 		stk->elem[i] = &stk->pool[i]; | 
 | 		stk->elem[i]->busaddr = (__u32) (stk->cmd_pool_handle +  | 
 | 			(sizeof(struct cciss_scsi_cmd_stack_elem_t) * i)); | 
 | 		stk->elem[i]->cmdindex = i; | 
 | 	} | 
 | 	stk->top = stk->nelems-1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void | 
 | scsi_cmd_stack_free(ctlr_info_t *h) | 
 | { | 
 | 	struct cciss_scsi_adapter_data_t *sa; | 
 | 	struct cciss_scsi_cmd_stack_t *stk; | 
 | 	size_t size; | 
 |  | 
 | 	sa = h->scsi_ctlr; | 
 | 	stk = &sa->cmd_stack;  | 
 | 	if (stk->top != stk->nelems-1) { | 
 | 		dev_warn(&h->pdev->dev, | 
 | 			"bug: %d scsi commands are still outstanding.\n", | 
 | 			stk->nelems - stk->top); | 
 | 	} | 
 | 	size = sizeof(struct cciss_scsi_cmd_stack_elem_t) * stk->nelems; | 
 |  | 
 | 	pci_free_consistent(h->pdev, size, stk->pool, stk->cmd_pool_handle); | 
 | 	stk->pool = NULL; | 
 | 	cciss_free_sg_chain_blocks(sa->cmd_sg_list, stk->nelems); | 
 | 	kfree(stk->elem); | 
 | 	stk->elem = NULL; | 
 | } | 
 |  | 
 | #if 0 | 
 | static int xmargin=8; | 
 | static int amargin=60; | 
 |  | 
 | static void | 
 | print_bytes (unsigned char *c, int len, int hex, int ascii) | 
 | { | 
 |  | 
 | 	int i; | 
 | 	unsigned char *x; | 
 |  | 
 | 	if (hex) | 
 | 	{ | 
 | 		x = c; | 
 | 		for (i=0;i<len;i++) | 
 | 		{ | 
 | 			if ((i % xmargin) == 0 && i>0) printk("\n"); | 
 | 			if ((i % xmargin) == 0) printk("0x%04x:", i); | 
 | 			printk(" %02x", *x); | 
 | 			x++; | 
 | 		} | 
 | 		printk("\n"); | 
 | 	} | 
 | 	if (ascii) | 
 | 	{ | 
 | 		x = c; | 
 | 		for (i=0;i<len;i++) | 
 | 		{ | 
 | 			if ((i % amargin) == 0 && i>0) printk("\n"); | 
 | 			if ((i % amargin) == 0) printk("0x%04x:", i); | 
 | 			if (*x > 26 && *x < 128) printk("%c", *x); | 
 | 			else printk("."); | 
 | 			x++; | 
 | 		} | 
 | 		printk("\n"); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | print_cmd(CommandList_struct *cp) | 
 | { | 
 | 	printk("queue:%d\n", cp->Header.ReplyQueue); | 
 | 	printk("sglist:%d\n", cp->Header.SGList); | 
 | 	printk("sgtot:%d\n", cp->Header.SGTotal); | 
 | 	printk("Tag:0x%08x/0x%08x\n", cp->Header.Tag.upper,  | 
 | 			cp->Header.Tag.lower); | 
 | 	printk("LUN:0x%02x%02x%02x%02x%02x%02x%02x%02x\n", | 
 | 		cp->Header.LUN.LunAddrBytes[0], | 
 | 		cp->Header.LUN.LunAddrBytes[1], | 
 | 		cp->Header.LUN.LunAddrBytes[2], | 
 | 		cp->Header.LUN.LunAddrBytes[3], | 
 | 		cp->Header.LUN.LunAddrBytes[4], | 
 | 		cp->Header.LUN.LunAddrBytes[5], | 
 | 		cp->Header.LUN.LunAddrBytes[6], | 
 | 		cp->Header.LUN.LunAddrBytes[7]); | 
 | 	printk("CDBLen:%d\n", cp->Request.CDBLen); | 
 | 	printk("Type:%d\n",cp->Request.Type.Type); | 
 | 	printk("Attr:%d\n",cp->Request.Type.Attribute); | 
 | 	printk(" Dir:%d\n",cp->Request.Type.Direction); | 
 | 	printk("Timeout:%d\n",cp->Request.Timeout); | 
 | 	printk( "CDB: %02x %02x %02x %02x %02x %02x %02x %02x" | 
 | 		" %02x %02x %02x %02x %02x %02x %02x %02x\n", | 
 | 		cp->Request.CDB[0], cp->Request.CDB[1], | 
 | 		cp->Request.CDB[2], cp->Request.CDB[3], | 
 | 		cp->Request.CDB[4], cp->Request.CDB[5], | 
 | 		cp->Request.CDB[6], cp->Request.CDB[7], | 
 | 		cp->Request.CDB[8], cp->Request.CDB[9], | 
 | 		cp->Request.CDB[10], cp->Request.CDB[11], | 
 | 		cp->Request.CDB[12], cp->Request.CDB[13], | 
 | 		cp->Request.CDB[14], cp->Request.CDB[15]), | 
 | 	printk("edesc.Addr: 0x%08x/0%08x, Len  = %d\n",  | 
 | 		cp->ErrDesc.Addr.upper, cp->ErrDesc.Addr.lower,  | 
 | 			cp->ErrDesc.Len); | 
 | 	printk("sgs..........Errorinfo:\n"); | 
 | 	printk("scsistatus:%d\n", cp->err_info->ScsiStatus); | 
 | 	printk("senselen:%d\n", cp->err_info->SenseLen); | 
 | 	printk("cmd status:%d\n", cp->err_info->CommandStatus); | 
 | 	printk("resid cnt:%d\n", cp->err_info->ResidualCnt); | 
 | 	printk("offense size:%d\n", cp->err_info->MoreErrInfo.Invalid_Cmd.offense_size); | 
 | 	printk("offense byte:%d\n", cp->err_info->MoreErrInfo.Invalid_Cmd.offense_num); | 
 | 	printk("offense value:%d\n", cp->err_info->MoreErrInfo.Invalid_Cmd.offense_value); | 
 | 			 | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | static int  | 
 | find_bus_target_lun(ctlr_info_t *h, int *bus, int *target, int *lun) | 
 | { | 
 | 	/* finds an unused bus, target, lun for a new device */ | 
 | 	/* assumes h->scsi_ctlr->lock is held */ | 
 | 	int i, found=0; | 
 | 	unsigned char target_taken[CCISS_MAX_SCSI_DEVS_PER_HBA]; | 
 |  | 
 | 	memset(&target_taken[0], 0, CCISS_MAX_SCSI_DEVS_PER_HBA); | 
 |  | 
 | 	target_taken[SELF_SCSI_ID] = 1;	 | 
 | 	for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++) | 
 | 		target_taken[ccissscsi[h->ctlr].dev[i].target] = 1; | 
 | 	 | 
 | 	for (i = 0; i < CCISS_MAX_SCSI_DEVS_PER_HBA; i++) { | 
 | 		if (!target_taken[i]) { | 
 | 			*bus = 0; *target=i; *lun = 0; found=1; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	return (!found);	 | 
 | } | 
 | struct scsi2map { | 
 | 	char scsi3addr[8]; | 
 | 	int bus, target, lun; | 
 | }; | 
 |  | 
 | static int  | 
 | cciss_scsi_add_entry(ctlr_info_t *h, int hostno, | 
 | 		struct cciss_scsi_dev_t *device, | 
 | 		struct scsi2map *added, int *nadded) | 
 | { | 
 | 	/* assumes h->scsi_ctlr->lock is held */ | 
 | 	int n = ccissscsi[h->ctlr].ndevices; | 
 | 	struct cciss_scsi_dev_t *sd; | 
 | 	int i, bus, target, lun; | 
 | 	unsigned char addr1[8], addr2[8]; | 
 |  | 
 | 	if (n >= CCISS_MAX_SCSI_DEVS_PER_HBA) { | 
 | 		dev_warn(&h->pdev->dev, "Too many devices, " | 
 | 			"some will be inaccessible.\n"); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	bus = target = -1; | 
 | 	lun = 0; | 
 | 	/* Is this device a non-zero lun of a multi-lun device */ | 
 | 	/* byte 4 of the 8-byte LUN addr will contain the logical unit no. */ | 
 | 	if (device->scsi3addr[4] != 0) { | 
 | 		/* Search through our list and find the device which */ | 
 | 		/* has the same 8 byte LUN address, excepting byte 4. */ | 
 | 		/* Assign the same bus and target for this new LUN. */ | 
 | 		/* Use the logical unit number from the firmware. */ | 
 | 		memcpy(addr1, device->scsi3addr, 8); | 
 | 		addr1[4] = 0; | 
 | 		for (i = 0; i < n; i++) { | 
 | 			sd = &ccissscsi[h->ctlr].dev[i]; | 
 | 			memcpy(addr2, sd->scsi3addr, 8); | 
 | 			addr2[4] = 0; | 
 | 			/* differ only in byte 4? */ | 
 | 			if (memcmp(addr1, addr2, 8) == 0) { | 
 | 				bus = sd->bus; | 
 | 				target = sd->target; | 
 | 				lun = device->scsi3addr[4]; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	sd = &ccissscsi[h->ctlr].dev[n]; | 
 | 	if (lun == 0) { | 
 | 		if (find_bus_target_lun(h, | 
 | 			&sd->bus, &sd->target, &sd->lun) != 0) | 
 | 			return -1; | 
 | 	} else { | 
 | 		sd->bus = bus; | 
 | 		sd->target = target; | 
 | 		sd->lun = lun; | 
 | 	} | 
 | 	added[*nadded].bus = sd->bus; | 
 | 	added[*nadded].target = sd->target; | 
 | 	added[*nadded].lun = sd->lun; | 
 | 	(*nadded)++; | 
 |  | 
 | 	memcpy(sd->scsi3addr, device->scsi3addr, 8); | 
 | 	memcpy(sd->vendor, device->vendor, sizeof(sd->vendor)); | 
 | 	memcpy(sd->revision, device->revision, sizeof(sd->revision)); | 
 | 	memcpy(sd->device_id, device->device_id, sizeof(sd->device_id)); | 
 | 	sd->devtype = device->devtype; | 
 |  | 
 | 	ccissscsi[h->ctlr].ndevices++; | 
 |  | 
 | 	/* initially, (before registering with scsi layer) we don't  | 
 | 	   know our hostno and we don't want to print anything first  | 
 | 	   time anyway (the scsi layer's inquiries will show that info) */ | 
 | 	if (hostno != -1) | 
 | 		dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d added.\n", | 
 | 			scsi_device_type(sd->devtype), hostno, | 
 | 			sd->bus, sd->target, sd->lun); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void | 
 | cciss_scsi_remove_entry(ctlr_info_t *h, int hostno, int entry, | 
 | 	struct scsi2map *removed, int *nremoved) | 
 | { | 
 | 	/* assumes h->ctlr]->scsi_ctlr->lock is held */ | 
 | 	int i; | 
 | 	struct cciss_scsi_dev_t sd; | 
 |  | 
 | 	if (entry < 0 || entry >= CCISS_MAX_SCSI_DEVS_PER_HBA) return; | 
 | 	sd = ccissscsi[h->ctlr].dev[entry]; | 
 | 	removed[*nremoved].bus    = sd.bus; | 
 | 	removed[*nremoved].target = sd.target; | 
 | 	removed[*nremoved].lun    = sd.lun; | 
 | 	(*nremoved)++; | 
 | 	for (i = entry; i < ccissscsi[h->ctlr].ndevices-1; i++) | 
 | 		ccissscsi[h->ctlr].dev[i] = ccissscsi[h->ctlr].dev[i+1]; | 
 | 	ccissscsi[h->ctlr].ndevices--; | 
 | 	dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d removed.\n", | 
 | 		scsi_device_type(sd.devtype), hostno, | 
 | 			sd.bus, sd.target, sd.lun); | 
 | } | 
 |  | 
 |  | 
 | #define SCSI3ADDR_EQ(a,b) ( \ | 
 | 	(a)[7] == (b)[7] && \ | 
 | 	(a)[6] == (b)[6] && \ | 
 | 	(a)[5] == (b)[5] && \ | 
 | 	(a)[4] == (b)[4] && \ | 
 | 	(a)[3] == (b)[3] && \ | 
 | 	(a)[2] == (b)[2] && \ | 
 | 	(a)[1] == (b)[1] && \ | 
 | 	(a)[0] == (b)[0]) | 
 |  | 
 | static void fixup_botched_add(ctlr_info_t *h, char *scsi3addr) | 
 | { | 
 | 	/* called when scsi_add_device fails in order to re-adjust */ | 
 | 	/* ccissscsi[] to match the mid layer's view. */ | 
 | 	unsigned long flags; | 
 | 	int i, j; | 
 | 	CPQ_TAPE_LOCK(h, flags); | 
 | 	for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++) { | 
 | 		if (memcmp(scsi3addr, | 
 | 				ccissscsi[h->ctlr].dev[i].scsi3addr, 8) == 0) { | 
 | 			for (j = i; j < ccissscsi[h->ctlr].ndevices-1; j++) | 
 | 				ccissscsi[h->ctlr].dev[j] = | 
 | 					ccissscsi[h->ctlr].dev[j+1]; | 
 | 			ccissscsi[h->ctlr].ndevices--; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	CPQ_TAPE_UNLOCK(h, flags); | 
 | } | 
 |  | 
 | static int device_is_the_same(struct cciss_scsi_dev_t *dev1, | 
 | 	struct cciss_scsi_dev_t *dev2) | 
 | { | 
 | 	return dev1->devtype == dev2->devtype && | 
 | 		memcmp(dev1->scsi3addr, dev2->scsi3addr, | 
 | 			sizeof(dev1->scsi3addr)) == 0 && | 
 | 		memcmp(dev1->device_id, dev2->device_id, | 
 | 			sizeof(dev1->device_id)) == 0 && | 
 | 		memcmp(dev1->vendor, dev2->vendor, | 
 | 			sizeof(dev1->vendor)) == 0 && | 
 | 		memcmp(dev1->model, dev2->model, | 
 | 			sizeof(dev1->model)) == 0 && | 
 | 		memcmp(dev1->revision, dev2->revision, | 
 | 			sizeof(dev1->revision)) == 0; | 
 | } | 
 |  | 
 | static int | 
 | adjust_cciss_scsi_table(ctlr_info_t *h, int hostno, | 
 | 	struct cciss_scsi_dev_t sd[], int nsds) | 
 | { | 
 | 	/* sd contains scsi3 addresses and devtypes, but | 
 | 	   bus target and lun are not filled in.  This funciton | 
 | 	   takes what's in sd to be the current and adjusts | 
 | 	   ccissscsi[] to be in line with what's in sd. */  | 
 |  | 
 | 	int i,j, found, changes=0; | 
 | 	struct cciss_scsi_dev_t *csd; | 
 | 	unsigned long flags; | 
 | 	struct scsi2map *added, *removed; | 
 | 	int nadded, nremoved; | 
 | 	struct Scsi_Host *sh = NULL; | 
 |  | 
 | 	added = kzalloc(sizeof(*added) * CCISS_MAX_SCSI_DEVS_PER_HBA, | 
 | 			GFP_KERNEL); | 
 | 	removed = kzalloc(sizeof(*removed) * CCISS_MAX_SCSI_DEVS_PER_HBA, | 
 | 			GFP_KERNEL); | 
 |  | 
 | 	if (!added || !removed) { | 
 | 		dev_warn(&h->pdev->dev, | 
 | 			"Out of memory in adjust_cciss_scsi_table\n"); | 
 | 		goto free_and_out; | 
 | 	} | 
 |  | 
 | 	CPQ_TAPE_LOCK(h, flags); | 
 |  | 
 | 	if (hostno != -1)  /* if it's not the first time... */ | 
 | 		sh = h->scsi_ctlr->scsi_host; | 
 |  | 
 | 	/* find any devices in ccissscsi[] that are not in  | 
 | 	   sd[] and remove them from ccissscsi[] */ | 
 |  | 
 | 	i = 0; | 
 | 	nremoved = 0; | 
 | 	nadded = 0; | 
 | 	while (i < ccissscsi[h->ctlr].ndevices) { | 
 | 		csd = &ccissscsi[h->ctlr].dev[i]; | 
 | 		found=0; | 
 | 		for (j=0;j<nsds;j++) { | 
 | 			if (SCSI3ADDR_EQ(sd[j].scsi3addr, | 
 | 				csd->scsi3addr)) { | 
 | 				if (device_is_the_same(&sd[j], csd)) | 
 | 					found=2; | 
 | 				else | 
 | 					found=1; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if (found == 0) { /* device no longer present. */  | 
 | 			changes++; | 
 | 			cciss_scsi_remove_entry(h, hostno, i, | 
 | 				removed, &nremoved); | 
 | 			/* remove ^^^, hence i not incremented */ | 
 | 		} else if (found == 1) { /* device is different in some way */ | 
 | 			changes++; | 
 | 			dev_info(&h->pdev->dev, | 
 | 				"device c%db%dt%dl%d has changed.\n", | 
 | 				hostno, csd->bus, csd->target, csd->lun); | 
 | 			cciss_scsi_remove_entry(h, hostno, i, | 
 | 				removed, &nremoved); | 
 | 			/* remove ^^^, hence i not incremented */ | 
 | 			if (cciss_scsi_add_entry(h, hostno, &sd[j], | 
 | 				added, &nadded) != 0) | 
 | 				/* we just removed one, so add can't fail. */ | 
 | 					BUG(); | 
 | 			csd->devtype = sd[j].devtype; | 
 | 			memcpy(csd->device_id, sd[j].device_id, | 
 | 				sizeof(csd->device_id)); | 
 | 			memcpy(csd->vendor, sd[j].vendor, | 
 | 				sizeof(csd->vendor)); | 
 | 			memcpy(csd->model, sd[j].model, | 
 | 				sizeof(csd->model)); | 
 | 			memcpy(csd->revision, sd[j].revision, | 
 | 				sizeof(csd->revision)); | 
 | 		} else 		/* device is same as it ever was, */ | 
 | 			i++;	/* so just move along. */ | 
 | 	} | 
 |  | 
 | 	/* Now, make sure every device listed in sd[] is also | 
 |  	   listed in ccissscsi[], adding them if they aren't found */ | 
 |  | 
 | 	for (i=0;i<nsds;i++) { | 
 | 		found=0; | 
 | 		for (j = 0; j < ccissscsi[h->ctlr].ndevices; j++) { | 
 | 			csd = &ccissscsi[h->ctlr].dev[j]; | 
 | 			if (SCSI3ADDR_EQ(sd[i].scsi3addr, | 
 | 				csd->scsi3addr)) { | 
 | 				if (device_is_the_same(&sd[i], csd)) | 
 | 					found=2;	/* found device */ | 
 | 				else | 
 | 					found=1; 	/* found a bug. */ | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 		if (!found) { | 
 | 			changes++; | 
 | 			if (cciss_scsi_add_entry(h, hostno, &sd[i], | 
 | 				added, &nadded) != 0) | 
 | 				break; | 
 | 		} else if (found == 1) { | 
 | 			/* should never happen... */ | 
 | 			changes++; | 
 | 			dev_warn(&h->pdev->dev, | 
 | 				"device unexpectedly changed\n"); | 
 | 			/* but if it does happen, we just ignore that device */ | 
 | 		} | 
 | 	} | 
 | 	CPQ_TAPE_UNLOCK(h, flags); | 
 |  | 
 | 	/* Don't notify scsi mid layer of any changes the first time through */ | 
 | 	/* (or if there are no changes) scsi_scan_host will do it later the */ | 
 | 	/* first time through. */ | 
 | 	if (hostno == -1 || !changes) | 
 | 		goto free_and_out; | 
 |  | 
 | 	/* Notify scsi mid layer of any removed devices */ | 
 | 	for (i = 0; i < nremoved; i++) { | 
 | 		struct scsi_device *sdev = | 
 | 			scsi_device_lookup(sh, removed[i].bus, | 
 | 				removed[i].target, removed[i].lun); | 
 | 		if (sdev != NULL) { | 
 | 			scsi_remove_device(sdev); | 
 | 			scsi_device_put(sdev); | 
 | 		} else { | 
 | 			/* We don't expect to get here. */ | 
 | 			/* future cmds to this device will get selection */ | 
 | 			/* timeout as if the device was gone. */ | 
 | 			dev_warn(&h->pdev->dev, "didn't find " | 
 | 				"c%db%dt%dl%d\n for removal.", | 
 | 				hostno, removed[i].bus, | 
 | 				removed[i].target, removed[i].lun); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Notify scsi mid layer of any added devices */ | 
 | 	for (i = 0; i < nadded; i++) { | 
 | 		int rc; | 
 | 		rc = scsi_add_device(sh, added[i].bus, | 
 | 			added[i].target, added[i].lun); | 
 | 		if (rc == 0) | 
 | 			continue; | 
 | 		dev_warn(&h->pdev->dev, "scsi_add_device " | 
 | 			"c%db%dt%dl%d failed, device not added.\n", | 
 | 			hostno, added[i].bus, added[i].target, added[i].lun); | 
 | 		/* now we have to remove it from ccissscsi, */ | 
 | 		/* since it didn't get added to scsi mid layer */ | 
 | 		fixup_botched_add(h, added[i].scsi3addr); | 
 | 	} | 
 |  | 
 | free_and_out: | 
 | 	kfree(added); | 
 | 	kfree(removed); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | lookup_scsi3addr(ctlr_info_t *h, int bus, int target, int lun, char *scsi3addr) | 
 | { | 
 | 	int i; | 
 | 	struct cciss_scsi_dev_t *sd; | 
 | 	unsigned long flags; | 
 |  | 
 | 	CPQ_TAPE_LOCK(h, flags); | 
 | 	for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++) { | 
 | 		sd = &ccissscsi[h->ctlr].dev[i]; | 
 | 		if (sd->bus == bus && | 
 | 		    sd->target == target && | 
 | 		    sd->lun == lun) { | 
 | 			memcpy(scsi3addr, &sd->scsi3addr[0], 8); | 
 | 			CPQ_TAPE_UNLOCK(h, flags); | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 | 	CPQ_TAPE_UNLOCK(h, flags); | 
 | 	return -1; | 
 | } | 
 |  | 
 | static void  | 
 | cciss_scsi_setup(ctlr_info_t *h) | 
 | { | 
 | 	struct cciss_scsi_adapter_data_t * shba; | 
 |  | 
 | 	ccissscsi[h->ctlr].ndevices = 0; | 
 | 	shba = (struct cciss_scsi_adapter_data_t *) | 
 | 		kmalloc(sizeof(*shba), GFP_KERNEL);	 | 
 | 	if (shba == NULL) | 
 | 		return; | 
 | 	shba->scsi_host = NULL; | 
 | 	spin_lock_init(&shba->lock); | 
 | 	shba->registered = 0; | 
 | 	if (scsi_cmd_stack_setup(h, shba) != 0) { | 
 | 		kfree(shba); | 
 | 		shba = NULL; | 
 | 	} | 
 | 	h->scsi_ctlr = shba; | 
 | 	return; | 
 | } | 
 |  | 
 | static void complete_scsi_command(CommandList_struct *c, int timeout, | 
 | 	__u32 tag) | 
 | { | 
 | 	struct scsi_cmnd *cmd; | 
 | 	ctlr_info_t *h; | 
 | 	ErrorInfo_struct *ei; | 
 |  | 
 | 	ei = c->err_info; | 
 |  | 
 | 	/* First, see if it was a message rather than a command */ | 
 | 	if (c->Request.Type.Type == TYPE_MSG)  { | 
 | 		c->cmd_type = CMD_MSG_DONE; | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	cmd = (struct scsi_cmnd *) c->scsi_cmd; | 
 | 	h = hba[c->ctlr]; | 
 |  | 
 | 	scsi_dma_unmap(cmd); | 
 | 	if (c->Header.SGTotal > h->max_cmd_sgentries) | 
 | 		cciss_unmap_sg_chain_block(h, c); | 
 |  | 
 | 	cmd->result = (DID_OK << 16); 		/* host byte */ | 
 | 	cmd->result |= (COMMAND_COMPLETE << 8);	/* msg byte */ | 
 | 	/* cmd->result |= (GOOD < 1); */		/* status byte */ | 
 |  | 
 | 	cmd->result |= (ei->ScsiStatus); | 
 | 	/* printk("Scsistatus is 0x%02x\n", ei->ScsiStatus);  */ | 
 |  | 
 | 	/* copy the sense data whether we need to or not. */ | 
 |  | 
 | 	memcpy(cmd->sense_buffer, ei->SenseInfo,  | 
 | 		ei->SenseLen > SCSI_SENSE_BUFFERSIZE ? | 
 | 			SCSI_SENSE_BUFFERSIZE :  | 
 | 			ei->SenseLen); | 
 | 	scsi_set_resid(cmd, ei->ResidualCnt); | 
 |  | 
 | 	if(ei->CommandStatus != 0)  | 
 | 	{ /* an error has occurred */  | 
 | 		switch(ei->CommandStatus) | 
 | 		{ | 
 | 			case CMD_TARGET_STATUS: | 
 | 				/* Pass it up to the upper layers... */ | 
 | 				if( ei->ScsiStatus) | 
 |                 		{ | 
 | #if 0 | 
 |                     			printk(KERN_WARNING "cciss: cmd %p " | 
 | 						"has SCSI Status = %x\n", | 
 | 						c, ei->ScsiStatus); | 
 | #endif | 
 | 					cmd->result |= (ei->ScsiStatus << 1); | 
 |                 		} | 
 | 				else {  /* scsi status is zero??? How??? */ | 
 | 					 | 
 | 	/* Ordinarily, this case should never happen, but there is a bug | 
 | 	   in some released firmware revisions that allows it to happen | 
 | 	   if, for example, a 4100 backplane loses power and the tape | 
 | 	   drive is in it.  We assume that it's a fatal error of some | 
 | 	   kind because we can't show that it wasn't. We will make it | 
 | 	   look like selection timeout since that is the most common | 
 | 	   reason for this to occur, and it's severe enough. */ | 
 |  | 
 | 					cmd->result = DID_NO_CONNECT << 16; | 
 | 				} | 
 | 			break; | 
 | 			case CMD_DATA_UNDERRUN: /* let mid layer handle it. */ | 
 | 			break; | 
 | 			case CMD_DATA_OVERRUN: | 
 | 				dev_warn(&h->pdev->dev, "%p has" | 
 | 					" completed with data overrun " | 
 | 					"reported\n", c); | 
 | 			break; | 
 | 			case CMD_INVALID: { | 
 | 				/* print_bytes(c, sizeof(*c), 1, 0); | 
 | 				print_cmd(c); */ | 
 |      /* We get CMD_INVALID if you address a non-existent tape drive instead | 
 | 	of a selection timeout (no response).  You will see this if you yank  | 
 | 	out a tape drive, then try to access it. This is kind of a shame | 
 | 	because it means that any other CMD_INVALID (e.g. driver bug) will | 
 | 	get interpreted as a missing target. */ | 
 | 				cmd->result = DID_NO_CONNECT << 16; | 
 | 				} | 
 | 			break; | 
 | 			case CMD_PROTOCOL_ERR: | 
 | 				dev_warn(&h->pdev->dev, | 
 | 					"%p has protocol error\n", c); | 
 |                         break; | 
 | 			case CMD_HARDWARE_ERR: | 
 | 				cmd->result = DID_ERROR << 16; | 
 | 				dev_warn(&h->pdev->dev, | 
 | 					"%p had hardware error\n", c); | 
 |                         break; | 
 | 			case CMD_CONNECTION_LOST: | 
 | 				cmd->result = DID_ERROR << 16; | 
 | 				dev_warn(&h->pdev->dev, | 
 | 					"%p had connection lost\n", c); | 
 | 			break; | 
 | 			case CMD_ABORTED: | 
 | 				cmd->result = DID_ABORT << 16; | 
 | 				dev_warn(&h->pdev->dev, "%p was aborted\n", c); | 
 | 			break; | 
 | 			case CMD_ABORT_FAILED: | 
 | 				cmd->result = DID_ERROR << 16; | 
 | 				dev_warn(&h->pdev->dev, | 
 | 					"%p reports abort failed\n", c); | 
 | 			break; | 
 | 			case CMD_UNSOLICITED_ABORT: | 
 | 				cmd->result = DID_ABORT << 16; | 
 | 				dev_warn(&h->pdev->dev, "%p aborted due to an " | 
 | 					"unsolicited abort\n", c); | 
 | 			break; | 
 | 			case CMD_TIMEOUT: | 
 | 				cmd->result = DID_TIME_OUT << 16; | 
 | 				dev_warn(&h->pdev->dev, "%p timedout\n", c); | 
 | 			break; | 
 | 			case CMD_UNABORTABLE: | 
 | 				cmd->result = DID_ERROR << 16; | 
 | 				dev_warn(&h->pdev->dev, "c %p command " | 
 | 					"unabortable\n", c); | 
 | 			break; | 
 | 			default: | 
 | 				cmd->result = DID_ERROR << 16; | 
 | 				dev_warn(&h->pdev->dev, | 
 | 					"%p returned unknown status %x\n", c, | 
 | 						ei->CommandStatus);  | 
 | 		} | 
 | 	} | 
 | 	cmd->scsi_done(cmd); | 
 | 	scsi_cmd_free(h, c); | 
 | } | 
 |  | 
 | static int | 
 | cciss_scsi_detect(ctlr_info_t *h) | 
 | { | 
 | 	struct Scsi_Host *sh; | 
 | 	int error; | 
 |  | 
 | 	sh = scsi_host_alloc(&cciss_driver_template, sizeof(struct ctlr_info *)); | 
 | 	if (sh == NULL) | 
 | 		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;   | 
 | 	sh->can_queue = cciss_tape_cmds; | 
 | 	sh->sg_tablesize = h->maxsgentries; | 
 | 	sh->max_cmd_len = MAX_COMMAND_SIZE; | 
 |  | 
 | 	((struct cciss_scsi_adapter_data_t *)  | 
 | 		h->scsi_ctlr)->scsi_host = sh; | 
 | 	sh->hostdata[0] = (unsigned long) h; | 
 | 	sh->irq = h->intr[SIMPLE_MODE_INT]; | 
 | 	sh->unique_id = sh->irq; | 
 | 	error = scsi_add_host(sh, &h->pdev->dev); | 
 | 	if (error) | 
 | 		goto fail_host_put; | 
 | 	scsi_scan_host(sh); | 
 | 	return 1; | 
 |  | 
 |  fail_host_put: | 
 | 	scsi_host_put(sh); | 
 |  fail: | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void | 
 | cciss_unmap_one(struct pci_dev *pdev, | 
 | 		CommandList_struct *c, | 
 | 		size_t buflen, | 
 | 		int data_direction) | 
 | { | 
 | 	u64bit addr64; | 
 |  | 
 | 	addr64.val32.lower = c->SG[0].Addr.lower; | 
 | 	addr64.val32.upper = c->SG[0].Addr.upper; | 
 | 	pci_unmap_single(pdev, (dma_addr_t) addr64.val, buflen, data_direction); | 
 | } | 
 |  | 
 | static void | 
 | cciss_map_one(struct pci_dev *pdev, | 
 | 		CommandList_struct *c, | 
 | 		unsigned char *buf, | 
 | 		size_t buflen, | 
 | 		int data_direction) | 
 | { | 
 | 	__u64 addr64; | 
 |  | 
 | 	addr64 = (__u64) pci_map_single(pdev, buf, buflen, data_direction); | 
 | 	c->SG[0].Addr.lower = | 
 | 	  (__u32) (addr64 & (__u64) 0x00000000FFFFFFFF); | 
 | 	c->SG[0].Addr.upper = | 
 | 	  (__u32) ((addr64 >> 32) & (__u64) 0x00000000FFFFFFFF); | 
 | 	c->SG[0].Len = buflen; | 
 | 	c->Header.SGList = (__u8) 1;   /* no. SGs contig in this cmd */ | 
 | 	c->Header.SGTotal = (__u16) 1; /* total sgs in this cmd list */ | 
 | } | 
 |  | 
 | static int | 
 | cciss_scsi_do_simple_cmd(ctlr_info_t *h, | 
 | 			CommandList_struct *c, | 
 | 			unsigned char *scsi3addr,  | 
 | 			unsigned char *cdb, | 
 | 			unsigned char cdblen, | 
 | 			unsigned char *buf, int bufsize, | 
 | 			int direction) | 
 | { | 
 | 	DECLARE_COMPLETION_ONSTACK(wait); | 
 |  | 
 | 	c->cmd_type = CMD_IOCTL_PEND; /* treat this like an ioctl */ | 
 | 	c->scsi_cmd = NULL; | 
 | 	c->Header.ReplyQueue = 0;  /* unused in simple mode */ | 
 | 	memcpy(&c->Header.LUN, scsi3addr, sizeof(c->Header.LUN)); | 
 | 	c->Header.Tag.lower = c->busaddr;  /* Use k. address of cmd as tag */ | 
 | 	// Fill in the request block... | 
 |  | 
 | 	/* printk("Using scsi3addr 0x%02x%0x2%0x2%0x2%0x2%0x2%0x2%0x2\n",  | 
 | 		scsi3addr[0], scsi3addr[1], scsi3addr[2], scsi3addr[3], | 
 | 		scsi3addr[4], scsi3addr[5], scsi3addr[6], scsi3addr[7]); */ | 
 |  | 
 | 	memset(c->Request.CDB, 0, sizeof(c->Request.CDB)); | 
 | 	memcpy(c->Request.CDB, cdb, cdblen); | 
 | 	c->Request.Timeout = 0; | 
 | 	c->Request.CDBLen = cdblen; | 
 | 	c->Request.Type.Type = TYPE_CMD; | 
 | 	c->Request.Type.Attribute = ATTR_SIMPLE; | 
 | 	c->Request.Type.Direction = direction; | 
 |  | 
 | 	/* Fill in the SG list and do dma mapping */ | 
 | 	cciss_map_one(h->pdev, c, (unsigned char *) buf, | 
 | 			bufsize, DMA_FROM_DEVICE);  | 
 |  | 
 | 	c->waiting = &wait; | 
 | 	enqueue_cmd_and_start_io(h, c); | 
 | 	wait_for_completion(&wait); | 
 |  | 
 | 	/* undo the dma mapping */ | 
 | 	cciss_unmap_one(h->pdev, c, bufsize, DMA_FROM_DEVICE); | 
 | 	return(0); | 
 | } | 
 |  | 
 | static void  | 
 | cciss_scsi_interpret_error(ctlr_info_t *h, CommandList_struct *c) | 
 | { | 
 | 	ErrorInfo_struct *ei; | 
 |  | 
 | 	ei = c->err_info; | 
 | 	switch(ei->CommandStatus) | 
 | 	{ | 
 | 		case CMD_TARGET_STATUS: | 
 | 			dev_warn(&h->pdev->dev, | 
 | 				"cmd %p has completed with errors\n", c); | 
 | 			dev_warn(&h->pdev->dev, | 
 | 				"cmd %p has SCSI Status = %x\n", | 
 | 				c, ei->ScsiStatus); | 
 | 			if (ei->ScsiStatus == 0) | 
 | 				dev_warn(&h->pdev->dev, | 
 | 				"SCSI status is abnormally zero.  " | 
 | 				"(probably indicates selection timeout " | 
 | 				"reported incorrectly due to a known " | 
 | 				"firmware bug, circa July, 2001.)\n"); | 
 | 		break; | 
 | 		case CMD_DATA_UNDERRUN: /* let mid layer handle it. */ | 
 | 			dev_info(&h->pdev->dev, "UNDERRUN\n"); | 
 | 		break; | 
 | 		case CMD_DATA_OVERRUN: | 
 | 			dev_warn(&h->pdev->dev, "%p has" | 
 | 				" completed with data overrun " | 
 | 				"reported\n", c); | 
 | 		break; | 
 | 		case CMD_INVALID: { | 
 | 			/* controller unfortunately reports SCSI passthru's */ | 
 | 			/* to non-existent targets as invalid commands. */ | 
 | 			dev_warn(&h->pdev->dev, | 
 | 				"%p is reported invalid (probably means " | 
 | 				"target device no longer present)\n", c); | 
 | 			/* print_bytes((unsigned char *) c, sizeof(*c), 1, 0); | 
 | 			print_cmd(c);  */ | 
 | 			} | 
 | 		break; | 
 | 		case CMD_PROTOCOL_ERR: | 
 | 			dev_warn(&h->pdev->dev, "%p has protocol error\n", c); | 
 | 		break; | 
 | 		case CMD_HARDWARE_ERR: | 
 | 			/* cmd->result = DID_ERROR << 16; */ | 
 | 			dev_warn(&h->pdev->dev, "%p had hardware error\n", c); | 
 | 		break; | 
 | 		case CMD_CONNECTION_LOST: | 
 | 			dev_warn(&h->pdev->dev, "%p had connection lost\n", c); | 
 | 		break; | 
 | 		case CMD_ABORTED: | 
 | 			dev_warn(&h->pdev->dev, "%p was aborted\n", c); | 
 | 		break; | 
 | 		case CMD_ABORT_FAILED: | 
 | 			dev_warn(&h->pdev->dev, | 
 | 				"%p reports abort failed\n", c); | 
 | 		break; | 
 | 		case CMD_UNSOLICITED_ABORT: | 
 | 			dev_warn(&h->pdev->dev, | 
 | 				"%p aborted due to an unsolicited abort\n", c); | 
 | 		break; | 
 | 		case CMD_TIMEOUT: | 
 | 			dev_warn(&h->pdev->dev, "%p timedout\n", c); | 
 | 		break; | 
 | 		case CMD_UNABORTABLE: | 
 | 			dev_warn(&h->pdev->dev, | 
 | 				"%p unabortable\n", c); | 
 | 		break; | 
 | 		default: | 
 | 			dev_warn(&h->pdev->dev, | 
 | 				"%p returned unknown status %x\n", | 
 | 				c, ei->CommandStatus); | 
 | 	} | 
 | } | 
 |  | 
 | static int | 
 | cciss_scsi_do_inquiry(ctlr_info_t *h, unsigned char *scsi3addr, | 
 | 	unsigned char page, unsigned char *buf, | 
 | 	unsigned char bufsize) | 
 | { | 
 | 	int rc; | 
 | 	CommandList_struct *c; | 
 | 	char cdb[6]; | 
 | 	ErrorInfo_struct *ei; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 	c = scsi_cmd_alloc(h); | 
 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 |  | 
 | 	if (c == NULL) {			/* trouble... */ | 
 | 		printk("cmd_alloc returned NULL!\n"); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	ei = c->err_info; | 
 |  | 
 | 	cdb[0] = CISS_INQUIRY; | 
 | 	cdb[1] = (page != 0); | 
 | 	cdb[2] = page; | 
 | 	cdb[3] = 0; | 
 | 	cdb[4] = bufsize; | 
 | 	cdb[5] = 0; | 
 | 	rc = cciss_scsi_do_simple_cmd(h, c, scsi3addr, cdb, | 
 | 				6, buf, bufsize, XFER_READ); | 
 |  | 
 | 	if (rc != 0) return rc; /* something went wrong */ | 
 |  | 
 | 	if (ei->CommandStatus != 0 &&  | 
 | 	    ei->CommandStatus != CMD_DATA_UNDERRUN) { | 
 | 		cciss_scsi_interpret_error(h, c); | 
 | 		rc = -1; | 
 | 	} | 
 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 	scsi_cmd_free(h, c); | 
 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 	return rc;	 | 
 | } | 
 |  | 
 | /* Get the device id from inquiry page 0x83 */ | 
 | static int cciss_scsi_get_device_id(ctlr_info_t *h, unsigned char *scsi3addr, | 
 | 	unsigned char *device_id, int buflen) | 
 | { | 
 | 	int rc; | 
 | 	unsigned char *buf; | 
 |  | 
 | 	if (buflen > 16) | 
 | 		buflen = 16; | 
 | 	buf = kzalloc(64, GFP_KERNEL); | 
 | 	if (!buf) | 
 | 		return -1; | 
 | 	rc = cciss_scsi_do_inquiry(h, scsi3addr, 0x83, buf, 64); | 
 | 	if (rc == 0) | 
 | 		memcpy(device_id, &buf[8], buflen); | 
 | 	kfree(buf); | 
 | 	return rc != 0; | 
 | } | 
 |  | 
 | static int | 
 | cciss_scsi_do_report_phys_luns(ctlr_info_t *h, | 
 | 		ReportLunData_struct *buf, int bufsize) | 
 | { | 
 | 	int rc; | 
 | 	CommandList_struct *c; | 
 | 	unsigned char cdb[12]; | 
 | 	unsigned char scsi3addr[8];  | 
 | 	ErrorInfo_struct *ei; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 	c = scsi_cmd_alloc(h); | 
 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 	if (c == NULL) {			/* trouble... */ | 
 | 		printk("cmd_alloc returned NULL!\n"); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	memset(&scsi3addr[0], 0, 8); /* address the controller */ | 
 | 	cdb[0] = CISS_REPORT_PHYS; | 
 | 	cdb[1] = 0; | 
 | 	cdb[2] = 0; | 
 | 	cdb[3] = 0; | 
 | 	cdb[4] = 0; | 
 | 	cdb[5] = 0; | 
 | 	cdb[6] = (bufsize >> 24) & 0xFF;  //MSB | 
 | 	cdb[7] = (bufsize >> 16) & 0xFF; | 
 | 	cdb[8] = (bufsize >> 8) & 0xFF; | 
 | 	cdb[9] = bufsize & 0xFF; | 
 | 	cdb[10] = 0; | 
 | 	cdb[11] = 0; | 
 |  | 
 | 	rc = cciss_scsi_do_simple_cmd(h, c, scsi3addr, | 
 | 				cdb, 12,  | 
 | 				(unsigned char *) buf,  | 
 | 				bufsize, XFER_READ); | 
 |  | 
 | 	if (rc != 0) return rc; /* something went wrong */ | 
 |  | 
 | 	ei = c->err_info; | 
 | 	if (ei->CommandStatus != 0 &&  | 
 | 	    ei->CommandStatus != CMD_DATA_UNDERRUN) { | 
 | 		cciss_scsi_interpret_error(h, c); | 
 | 		rc = -1; | 
 | 	} | 
 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 	scsi_cmd_free(h, c); | 
 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 	return rc;	 | 
 | } | 
 |  | 
 | static void | 
 | cciss_update_non_disk_devices(ctlr_info_t *h, int hostno) | 
 | { | 
 | 	/* the idea here is we could get notified from /proc | 
 | 	   that some devices have changed, so we do a report  | 
 | 	   physical luns cmd, and adjust our list of devices  | 
 | 	   accordingly.  (We can't rely on the scsi-mid layer just | 
 | 	   doing inquiries, because the "busses" that the scsi  | 
 | 	   mid-layer probes are totally fabricated by this driver, | 
 | 	   so new devices wouldn't show up. | 
 |  | 
 | 	   the scsi3addr's of devices won't change so long as the  | 
 | 	   adapter is not reset.  That means we can rescan and  | 
 | 	   tell which devices we already know about, vs. new  | 
 | 	   devices, vs.  disappearing devices. | 
 |  | 
 | 	   Also, if you yank out a tape drive, then put in a disk | 
 | 	   in it's place, (say, a configured volume from another  | 
 | 	   array controller for instance)  _don't_ poke this driver  | 
 |            (so it thinks it's still a tape, but _do_ poke the scsi  | 
 |            mid layer, so it does an inquiry... the scsi mid layer  | 
 |            will see the physical disk.  This would be bad.  Need to | 
 | 	   think about how to prevent that.  One idea would be to  | 
 | 	   snoop all scsi responses and if an inquiry repsonse comes | 
 | 	   back that reports a disk, chuck it an return selection | 
 | 	   timeout instead and adjust our table...  Not sure i like | 
 | 	   that though.   | 
 |  | 
 | 	 */ | 
 | #define OBDR_TAPE_INQ_SIZE 49 | 
 | #define OBDR_TAPE_SIG "$DR-10" | 
 | 	ReportLunData_struct *ld_buff; | 
 | 	unsigned char *inq_buff; | 
 | 	unsigned char scsi3addr[8]; | 
 | 	__u32 num_luns=0; | 
 | 	unsigned char *ch; | 
 | 	struct cciss_scsi_dev_t *currentsd, *this_device; | 
 | 	int ncurrent=0; | 
 | 	int reportlunsize = sizeof(*ld_buff) + CISS_MAX_PHYS_LUN * 8; | 
 | 	int i; | 
 |  | 
 | 	ld_buff = kzalloc(reportlunsize, GFP_KERNEL); | 
 | 	inq_buff = kmalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL); | 
 | 	currentsd = kzalloc(sizeof(*currentsd) * | 
 | 			(CCISS_MAX_SCSI_DEVS_PER_HBA+1), GFP_KERNEL); | 
 | 	if (ld_buff == NULL || inq_buff == NULL || currentsd == NULL) { | 
 | 		printk(KERN_ERR "cciss: out of memory\n"); | 
 | 		goto out; | 
 | 	} | 
 | 	this_device = ¤tsd[CCISS_MAX_SCSI_DEVS_PER_HBA]; | 
 | 	if (cciss_scsi_do_report_phys_luns(h, ld_buff, reportlunsize) == 0) { | 
 | 		ch = &ld_buff->LUNListLength[0]; | 
 | 		num_luns = ((ch[0]<<24) | (ch[1]<<16) | (ch[2]<<8) | ch[3]) / 8; | 
 | 		if (num_luns > CISS_MAX_PHYS_LUN) { | 
 | 			printk(KERN_WARNING  | 
 | 				"cciss: Maximum physical LUNs (%d) exceeded.  " | 
 | 				"%d LUNs ignored.\n", CISS_MAX_PHYS_LUN,  | 
 | 				num_luns - CISS_MAX_PHYS_LUN); | 
 | 			num_luns = CISS_MAX_PHYS_LUN; | 
 | 		} | 
 | 	} | 
 | 	else { | 
 | 		printk(KERN_ERR  "cciss: Report physical LUNs failed.\n"); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 |  | 
 | 	/* adjust our table of devices */	 | 
 | 	for (i = 0; i < num_luns; i++) { | 
 | 		/* for each physical lun, do an inquiry */ | 
 | 		if (ld_buff->LUN[i][3] & 0xC0) continue; | 
 | 		memset(inq_buff, 0, OBDR_TAPE_INQ_SIZE); | 
 | 		memcpy(&scsi3addr[0], &ld_buff->LUN[i][0], 8); | 
 |  | 
 | 		if (cciss_scsi_do_inquiry(h, scsi3addr, 0, inq_buff, | 
 | 			(unsigned char) OBDR_TAPE_INQ_SIZE) != 0) | 
 | 			/* Inquiry failed (msg printed already) */ | 
 | 			continue; /* so we will skip this device. */ | 
 |  | 
 | 		this_device->devtype = (inq_buff[0] & 0x1f); | 
 | 		this_device->bus = -1; | 
 | 		this_device->target = -1; | 
 | 		this_device->lun = -1; | 
 | 		memcpy(this_device->scsi3addr, scsi3addr, 8); | 
 | 		memcpy(this_device->vendor, &inq_buff[8], | 
 | 			sizeof(this_device->vendor)); | 
 | 		memcpy(this_device->model, &inq_buff[16], | 
 | 			sizeof(this_device->model)); | 
 | 		memcpy(this_device->revision, &inq_buff[32], | 
 | 			sizeof(this_device->revision)); | 
 | 		memset(this_device->device_id, 0, | 
 | 			sizeof(this_device->device_id)); | 
 | 		cciss_scsi_get_device_id(h, scsi3addr, | 
 | 			this_device->device_id, sizeof(this_device->device_id)); | 
 |  | 
 | 		switch (this_device->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) { | 
 | 				printk(KERN_INFO "cciss%d: %s ignored, " | 
 | 					"too many devices.\n", h->ctlr, | 
 | 					scsi_device_type(this_device->devtype)); | 
 | 				break; | 
 | 			} | 
 | 			currentsd[ncurrent] = *this_device; | 
 | 			ncurrent++; | 
 | 			break; | 
 | 		  default:  | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	adjust_cciss_scsi_table(h, hostno, currentsd, ncurrent); | 
 | out: | 
 | 	kfree(inq_buff); | 
 | 	kfree(ld_buff); | 
 | 	kfree(currentsd); | 
 | 	return; | 
 | } | 
 |  | 
 | static int | 
 | is_keyword(char *ptr, int len, char *verb)  // Thanks to ncr53c8xx.c | 
 | { | 
 | 	int verb_len = strlen(verb); | 
 | 	if (len >= verb_len && !memcmp(verb,ptr,verb_len)) | 
 | 		return verb_len; | 
 | 	else | 
 | 		return 0; | 
 | } | 
 |  | 
 | static int | 
 | cciss_scsi_user_command(ctlr_info_t *h, int hostno, char *buffer, int length) | 
 | { | 
 | 	int arg_len; | 
 |  | 
 | 	if ((arg_len = is_keyword(buffer, length, "rescan")) != 0) | 
 | 		cciss_update_non_disk_devices(h, hostno); | 
 | 	else | 
 | 		return -EINVAL; | 
 | 	return length; | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | cciss_scsi_proc_info(struct Scsi_Host *sh, | 
 | 		char *buffer, /* data buffer */ | 
 | 		char **start, 	   /* where data in buffer starts */ | 
 | 		off_t offset,	   /* offset from start of imaginary file */ | 
 | 		int length, 	   /* length of data in buffer */ | 
 | 		int func)	   /* 0 == read, 1 == write */ | 
 | { | 
 |  | 
 | 	int buflen, datalen; | 
 | 	ctlr_info_t *h; | 
 | 	int i; | 
 |  | 
 | 	h = (ctlr_info_t *) sh->hostdata[0]; | 
 | 	if (h == NULL)  /* This really shouldn't ever happen. */ | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (func == 0) {	/* User is reading from /proc/scsi/ciss*?/?*  */ | 
 | 		buflen = sprintf(buffer, "cciss%d: SCSI host: %d\n", | 
 | 				h->ctlr, 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[h->ctlr].ndevices; i++) { | 
 | 			struct cciss_scsi_dev_t *sd = | 
 | 				&ccissscsi[h->ctlr].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; | 
 | 			*start = buffer+buflen;	 | 
 | 		} else | 
 | 			*start = buffer + offset; | 
 | 		return(datalen); | 
 | 	} else 	/* User is writing to /proc/scsi/cciss*?/?*  ... */ | 
 | 		return cciss_scsi_user_command(h, sh->host_no, | 
 | 			buffer, length);	 | 
 | }  | 
 |  | 
 | /* 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, c. */ | 
 |  | 
 | static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *c, | 
 | 	struct scsi_cmnd *cmd) | 
 | { | 
 | 	unsigned int len; | 
 | 	struct scatterlist *sg; | 
 | 	__u64 addr64; | 
 | 	int request_nsgs, i, chained, sg_index; | 
 | 	struct cciss_scsi_adapter_data_t *sa = h->scsi_ctlr; | 
 | 	SGDescriptor_struct *curr_sg; | 
 |  | 
 | 	BUG_ON(scsi_sg_count(cmd) > h->maxsgentries); | 
 |  | 
 | 	chained = 0; | 
 | 	sg_index = 0; | 
 | 	curr_sg = c->SG; | 
 | 	request_nsgs = scsi_dma_map(cmd); | 
 | 	if (request_nsgs) { | 
 | 		scsi_for_each_sg(cmd, sg, request_nsgs, i) { | 
 | 			if (sg_index + 1 == h->max_cmd_sgentries && | 
 | 				!chained && request_nsgs - i > 1) { | 
 | 				chained = 1; | 
 | 				sg_index = 0; | 
 | 				curr_sg = sa->cmd_sg_list[c->cmdindex]; | 
 | 			} | 
 | 			addr64 = (__u64) sg_dma_address(sg); | 
 | 			len  = sg_dma_len(sg); | 
 | 			curr_sg[sg_index].Addr.lower = | 
 | 				(__u32) (addr64 & 0x0FFFFFFFFULL); | 
 | 			curr_sg[sg_index].Addr.upper = | 
 | 				(__u32) ((addr64 >> 32) & 0x0FFFFFFFFULL); | 
 | 			curr_sg[sg_index].Len = len; | 
 | 			curr_sg[sg_index].Ext = 0; | 
 | 			++sg_index; | 
 | 		} | 
 | 		if (chained) | 
 | 			cciss_map_sg_chain_block(h, c, | 
 | 				sa->cmd_sg_list[c->cmdindex], | 
 | 				(request_nsgs - (h->max_cmd_sgentries - 1)) * | 
 | 					sizeof(SGDescriptor_struct)); | 
 | 	} | 
 | 	/* track how many SG entries we are using */ | 
 | 	if (request_nsgs > h->maxSG) | 
 | 		h->maxSG = request_nsgs; | 
 | 	c->Header.SGTotal = (__u8) request_nsgs + chained; | 
 | 	if (request_nsgs > h->max_cmd_sgentries) | 
 | 		c->Header.SGList = h->max_cmd_sgentries; | 
 | 	else | 
 | 		c->Header.SGList = c->Header.SGTotal; | 
 | 	return; | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | cciss_scsi_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) | 
 | { | 
 | 	ctlr_info_t *h; | 
 | 	int rc; | 
 | 	unsigned char scsi3addr[8]; | 
 | 	CommandList_struct *c; | 
 | 	unsigned long flags; | 
 |  | 
 | 	// Get the ptr to our adapter structure (hba[i]) out of cmd->host. | 
 | 	// We violate cmd->host privacy here.  (Is there another way?) | 
 | 	h = (ctlr_info_t *) cmd->device->host->hostdata[0]; | 
 |  | 
 | 	rc = lookup_scsi3addr(h, cmd->device->channel, cmd->device->id, | 
 | 			cmd->device->lun, scsi3addr); | 
 | 	if (rc != 0) { | 
 | 		/* the scsi nexus does not match any that we presented... */ | 
 | 		/* pretend to mid layer that we got selection timeout */ | 
 | 		cmd->result = DID_NO_CONNECT << 16; | 
 | 		done(cmd); | 
 | 		/* we might want to think about registering controller itself | 
 | 		   as a processor device on the bus so sg binds to it. */ | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	/* Ok, we have a reasonable scsi nexus, so send the cmd down, and | 
 |            see what the device thinks of it. */ | 
 |  | 
 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 	c = scsi_cmd_alloc(h); | 
 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 	if (c == NULL) {			/* trouble... */ | 
 | 		dev_warn(&h->pdev->dev, "scsi_cmd_alloc returned NULL!\n"); | 
 | 		/* FIXME: next 3 lines are -> BAD! <- */ | 
 | 		cmd->result = DID_NO_CONNECT << 16; | 
 | 		done(cmd); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	// Fill in the command list header | 
 |  | 
 | 	cmd->scsi_done = done;    // save this for use by completion code  | 
 |  | 
 | 	/* save c in case we have to abort it */ | 
 | 	cmd->host_scribble = (unsigned char *) c; | 
 |  | 
 | 	c->cmd_type = CMD_SCSI; | 
 | 	c->scsi_cmd = cmd; | 
 | 	c->Header.ReplyQueue = 0;  /* unused in simple mode */ | 
 | 	memcpy(&c->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8); | 
 | 	c->Header.Tag.lower = c->busaddr;  /* Use k. address of cmd as tag */ | 
 | 	 | 
 | 	// Fill in the request block... | 
 |  | 
 | 	c->Request.Timeout = 0; | 
 | 	memset(c->Request.CDB, 0, sizeof(c->Request.CDB)); | 
 | 	BUG_ON(cmd->cmd_len > sizeof(c->Request.CDB)); | 
 | 	c->Request.CDBLen = cmd->cmd_len; | 
 | 	memcpy(c->Request.CDB, cmd->cmnd, cmd->cmd_len); | 
 | 	c->Request.Type.Type = TYPE_CMD; | 
 | 	c->Request.Type.Attribute = ATTR_SIMPLE; | 
 | 	switch(cmd->sc_data_direction) | 
 | 	{ | 
 | 	  case DMA_TO_DEVICE: | 
 | 		c->Request.Type.Direction = XFER_WRITE; | 
 | 		break; | 
 | 	  case DMA_FROM_DEVICE: | 
 | 		c->Request.Type.Direction = XFER_READ; | 
 | 		break; | 
 | 	  case DMA_NONE: | 
 | 		c->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() ) | 
 |  | 
 | 		c->Request.Type.Direction = XFER_RSVD; | 
 | 		// 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  | 
 | 		// slide by, and give the same results as if this field | 
 | 		// were set correctly.  Either way is acceptable for | 
 | 		// our purposes here. | 
 |  | 
 | 		break; | 
 |  | 
 | 	  default:  | 
 | 		dev_warn(&h->pdev->dev, "unknown data direction: %d\n", | 
 | 			cmd->sc_data_direction); | 
 | 		BUG(); | 
 | 		break; | 
 | 	} | 
 | 	cciss_scatter_gather(h, c, cmd); | 
 | 	enqueue_cmd_and_start_io(h, c); | 
 | 	/* the cmd'll come back via intr handler in complete_scsi_command()  */ | 
 | 	return 0; | 
 | } | 
 |  | 
 | static DEF_SCSI_QCMD(cciss_scsi_queue_command) | 
 |  | 
 | static void cciss_unregister_scsi(ctlr_info_t *h) | 
 | { | 
 | 	struct cciss_scsi_adapter_data_t *sa; | 
 | 	struct cciss_scsi_cmd_stack_t *stk; | 
 | 	unsigned long flags; | 
 |  | 
 | 	/* we are being forcibly unloaded, and may not refuse. */ | 
 |  | 
 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 	sa = h->scsi_ctlr; | 
 | 	stk = &sa->cmd_stack;  | 
 |  | 
 | 	/* if we weren't ever actually registered, don't unregister */  | 
 | 	if (sa->registered) { | 
 | 		spin_unlock_irqrestore(&h->lock, flags); | 
 | 		scsi_remove_host(sa->scsi_host); | 
 | 		scsi_host_put(sa->scsi_host); | 
 | 		spin_lock_irqsave(&h->lock, flags); | 
 | 	} | 
 |  | 
 | 	/* set scsi_host to NULL so our detect routine will  | 
 | 	   find us on register */ | 
 | 	sa->scsi_host = NULL; | 
 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 	scsi_cmd_stack_free(h); | 
 | 	kfree(sa); | 
 | } | 
 |  | 
 | static int cciss_engage_scsi(ctlr_info_t *h) | 
 | { | 
 | 	struct cciss_scsi_adapter_data_t *sa; | 
 | 	struct cciss_scsi_cmd_stack_t *stk; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 	sa = h->scsi_ctlr; | 
 | 	stk = &sa->cmd_stack;  | 
 |  | 
 | 	if (sa->registered) { | 
 | 		dev_info(&h->pdev->dev, "SCSI subsystem already engaged.\n"); | 
 | 		spin_unlock_irqrestore(&h->lock, flags); | 
 | 		return -ENXIO; | 
 | 	} | 
 | 	sa->registered = 1; | 
 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 	cciss_update_non_disk_devices(h, -1); | 
 | 	cciss_scsi_detect(h); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void | 
 | cciss_seq_tape_report(struct seq_file *seq, ctlr_info_t *h) | 
 | { | 
 | 	unsigned long flags; | 
 |  | 
 | 	CPQ_TAPE_LOCK(h, flags); | 
 | 	seq_printf(seq, | 
 | 		"Sequential access devices: %d\n\n", | 
 | 			ccissscsi[h->ctlr].ndevices); | 
 | 	CPQ_TAPE_UNLOCK(h, flags); | 
 | } | 
 |  | 
 | static int wait_for_device_to_become_ready(ctlr_info_t *h, | 
 | 	unsigned char lunaddr[]) | 
 | { | 
 | 	int rc; | 
 | 	int count = 0; | 
 | 	int waittime = HZ; | 
 | 	CommandList_struct *c; | 
 |  | 
 | 	c = cmd_alloc(h); | 
 | 	if (!c) { | 
 | 		dev_warn(&h->pdev->dev, "out of memory in " | 
 | 			"wait_for_device_to_become_ready.\n"); | 
 | 		return IO_ERROR; | 
 | 	} | 
 |  | 
 | 	/* Send test unit ready until device ready, or give up. */ | 
 | 	while (count < 20) { | 
 |  | 
 | 		/* Wait for a bit.  do this first, because if we send | 
 | 		 * the TUR right away, the reset will just abort it. | 
 | 		 */ | 
 | 		schedule_timeout_uninterruptible(waittime); | 
 | 		count++; | 
 |  | 
 | 		/* Increase wait time with each try, up to a point. */ | 
 | 		if (waittime < (HZ * 30)) | 
 | 			waittime = waittime * 2; | 
 |  | 
 | 		/* Send the Test Unit Ready */ | 
 | 		rc = fill_cmd(h, c, TEST_UNIT_READY, NULL, 0, 0, | 
 | 			lunaddr, TYPE_CMD); | 
 | 		if (rc == 0) | 
 | 			rc = sendcmd_withirq_core(h, c, 0); | 
 |  | 
 | 		(void) process_sendcmd_error(h, c); | 
 |  | 
 | 		if (rc != 0) | 
 | 			goto retry_tur; | 
 |  | 
 | 		if (c->err_info->CommandStatus == CMD_SUCCESS) | 
 | 			break; | 
 |  | 
 | 		if (c->err_info->CommandStatus == CMD_TARGET_STATUS && | 
 | 			c->err_info->ScsiStatus == SAM_STAT_CHECK_CONDITION) { | 
 | 			if (c->err_info->SenseInfo[2] == NO_SENSE) | 
 | 				break; | 
 | 			if (c->err_info->SenseInfo[2] == UNIT_ATTENTION) { | 
 | 				unsigned char asc; | 
 | 				asc = c->err_info->SenseInfo[12]; | 
 | 				check_for_unit_attention(h, c); | 
 | 				if (asc == POWER_OR_RESET) | 
 | 					break; | 
 | 			} | 
 | 		} | 
 | retry_tur: | 
 | 		dev_warn(&h->pdev->dev, "Waiting %d secs " | 
 | 			"for device to become ready.\n", | 
 | 			waittime / HZ); | 
 | 		rc = 1; /* device not ready. */ | 
 | 	} | 
 |  | 
 | 	if (rc) | 
 | 		dev_warn(&h->pdev->dev, "giving up on device.\n"); | 
 | 	else | 
 | 		dev_warn(&h->pdev->dev, "device is ready.\n"); | 
 |  | 
 | 	cmd_free(h, c); | 
 | 	return rc; | 
 | } | 
 |  | 
 | /* 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; | 
 | 	unsigned char lunaddr[8]; | 
 | 	ctlr_info_t *h; | 
 |  | 
 | 	/* find the controller to which the command to be aborted was sent */ | 
 | 	h = (ctlr_info_t *) scsicmd->device->host->hostdata[0]; | 
 | 	if (h == NULL) /* paranoia */ | 
 | 		return FAILED; | 
 | 	dev_warn(&h->pdev->dev, "resetting tape drive or medium changer.\n"); | 
 | 	/* find the command that's giving us trouble */ | 
 | 	cmd_in_trouble = (CommandList_struct *) scsicmd->host_scribble; | 
 | 	if (cmd_in_trouble == NULL) /* paranoia */ | 
 | 		return FAILED; | 
 | 	memcpy(lunaddr, &cmd_in_trouble->Header.LUN.LunAddrBytes[0], 8); | 
 | 	/* send a reset to the SCSI LUN which the command was sent to */ | 
 | 	rc = sendcmd_withirq(h, CCISS_RESET_MSG, NULL, 0, 0, lunaddr, | 
 | 		TYPE_MSG); | 
 | 	if (rc == 0 && wait_for_device_to_become_ready(h, lunaddr) == 0) | 
 | 		return SUCCESS; | 
 | 	dev_warn(&h->pdev->dev, "resetting device failed.\n"); | 
 | 	return FAILED; | 
 | } | 
 |  | 
 | static int  cciss_eh_abort_handler(struct scsi_cmnd *scsicmd) | 
 | { | 
 | 	int rc; | 
 | 	CommandList_struct *cmd_to_abort; | 
 | 	unsigned char lunaddr[8]; | 
 | 	ctlr_info_t *h; | 
 |  | 
 | 	/* find the controller to which the command to be aborted was sent */ | 
 | 	h = (ctlr_info_t *) scsicmd->device->host->hostdata[0]; | 
 | 	if (h == NULL) /* paranoia */ | 
 | 		return FAILED; | 
 | 	dev_warn(&h->pdev->dev, "aborting tardy SCSI cmd\n"); | 
 |  | 
 | 	/* find the command to be aborted */ | 
 | 	cmd_to_abort = (CommandList_struct *) scsicmd->host_scribble; | 
 | 	if (cmd_to_abort == NULL) /* paranoia */ | 
 | 		return FAILED; | 
 | 	memcpy(lunaddr, &cmd_to_abort->Header.LUN.LunAddrBytes[0], 8); | 
 | 	rc = sendcmd_withirq(h, CCISS_ABORT_MSG, &cmd_to_abort->Header.Tag, | 
 | 		0, 0, lunaddr, TYPE_MSG); | 
 | 	if (rc == 0) | 
 | 		return SUCCESS; | 
 | 	return FAILED; | 
 |  | 
 | } | 
 |  | 
 | #else /* no CONFIG_CISS_SCSI_TAPE */ | 
 |  | 
 | /* If no tape support, then these become defined out of existence */ | 
 |  | 
 | #define cciss_scsi_setup(cntl_num) | 
 |  | 
 | #endif /* CONFIG_CISS_SCSI_TAPE */ |