mirror of
				https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
				synced 2025-11-04 08:34:47 +10:00 
			
		
		
		
	Delete code which sets SCSI status incorrectly as it's already been set
correctly above this incorrect code.  The bug was introduced in 2009 by
commit b0e15f6db1 ("cciss: fix typo that causes scsi status to be
lost.")
Signed-off-by: Stephen M. Cameron <scameron@beardog.cce.hp.com>
Reported-by: Roel van Meer <roel.vanmeer@bokxing.nl>
Tested-by: Roel van Meer <roel.vanmeer@bokxing.nl>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
		
	
			
		
			
				
	
	
		
			1718 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1718 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 *    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 <linux/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) {
 | 
						|
					
 | 
						|
	/* 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;
 | 
						|
	sh->max_sectors = h->cciss_max_sectors;
 | 
						|
 | 
						|
	((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 = (u16) 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)
 | 
						|
#define cciss_engage_scsi(h)
 | 
						|
 | 
						|
#endif /* CONFIG_CISS_SCSI_TAPE */
 |