|  | /* | 
|  | *  linux/drivers/message/fusion/mptscsih.c | 
|  | *      For use with LSI Logic PCI chip/adapter(s) | 
|  | *      running LSI Logic Fusion MPT (Message Passing Technology) firmware. | 
|  | * | 
|  | *  Copyright (c) 1999-2005 LSI Logic Corporation | 
|  | *  (mailto:mpt_linux_developer@lsil.com) | 
|  | * | 
|  | */ | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | 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. | 
|  |  | 
|  | NO WARRANTY | 
|  | THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR | 
|  | CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT | 
|  | LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, | 
|  | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is | 
|  | solely responsible for determining the appropriateness of using and | 
|  | distributing the Program and assumes all risks associated with its | 
|  | exercise of rights under this Agreement, including but not limited to | 
|  | the risks and costs of program errors, damage to or loss of data, | 
|  | programs or equipment, and unavailability or interruption of operations. | 
|  |  | 
|  | DISCLAIMER OF LIABILITY | 
|  | NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY | 
|  | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND | 
|  | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | 
|  | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | 
|  | USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED | 
|  | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES | 
|  |  | 
|  | 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 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  |  | 
|  | #include "linux_compat.h"	/* linux-2.6 tweaks */ | 
|  | #include <linux/module.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/errno.h> | 
|  | #include <linux/kdev_t.h> | 
|  | #include <linux/blkdev.h> | 
|  | #include <linux/delay.h>	/* for mdelay */ | 
|  | #include <linux/interrupt.h>	/* needed for in_interrupt() proto */ | 
|  | #include <linux/reboot.h>	/* notifier code */ | 
|  | #include <linux/sched.h> | 
|  | #include <linux/workqueue.h> | 
|  |  | 
|  | #include <scsi/scsi.h> | 
|  | #include <scsi/scsi_cmnd.h> | 
|  | #include <scsi/scsi_device.h> | 
|  | #include <scsi/scsi_host.h> | 
|  | #include <scsi/scsi_tcq.h> | 
|  | #include <scsi/scsi_dbg.h> | 
|  |  | 
|  | #include "mptbase.h" | 
|  | #include "mptscsih.h" | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | #define my_NAME		"Fusion MPT SCSI Host driver" | 
|  | #define my_VERSION	MPT_LINUX_VERSION_COMMON | 
|  | #define MYNAM		"mptscsih" | 
|  |  | 
|  | MODULE_AUTHOR(MODULEAUTHOR); | 
|  | MODULE_DESCRIPTION(my_NAME); | 
|  | MODULE_LICENSE("GPL"); | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  |  | 
|  | typedef struct _BIG_SENSE_BUF { | 
|  | u8		data[MPT_SENSE_BUFFER_ALLOC]; | 
|  | } BIG_SENSE_BUF; | 
|  |  | 
|  | #define MPT_SCANDV_GOOD			(0x00000000) /* must be 0 */ | 
|  | #define MPT_SCANDV_DID_RESET		(0x00000001) | 
|  | #define MPT_SCANDV_SENSE		(0x00000002) | 
|  | #define MPT_SCANDV_SOME_ERROR		(0x00000004) | 
|  | #define MPT_SCANDV_SELECTION_TIMEOUT	(0x00000008) | 
|  | #define MPT_SCANDV_ISSUE_SENSE		(0x00000010) | 
|  | #define MPT_SCANDV_FALLBACK		(0x00000020) | 
|  |  | 
|  | #define MPT_SCANDV_MAX_RETRIES		(10) | 
|  |  | 
|  | #define MPT_ICFLAG_BUF_CAP	0x01	/* ReadBuffer Read Capacity format */ | 
|  | #define MPT_ICFLAG_ECHO		0x02	/* ReadBuffer Echo buffer format */ | 
|  | #define MPT_ICFLAG_EBOS		0x04	/* ReadBuffer Echo buffer has EBOS */ | 
|  | #define MPT_ICFLAG_PHYS_DISK	0x08	/* Any SCSI IO but do Phys Disk Format */ | 
|  | #define MPT_ICFLAG_TAGGED_CMD	0x10	/* Do tagged IO */ | 
|  | #define MPT_ICFLAG_DID_RESET	0x20	/* Bus Reset occurred with this command */ | 
|  | #define MPT_ICFLAG_RESERVED	0x40	/* Reserved has been issued */ | 
|  |  | 
|  | typedef struct _internal_cmd { | 
|  | char		*data;		/* data pointer */ | 
|  | dma_addr_t	data_dma;	/* data dma address */ | 
|  | int		size;		/* transfer size */ | 
|  | u8		cmd;		/* SCSI Op Code */ | 
|  | u8		bus;		/* bus number */ | 
|  | u8		id;		/* SCSI ID (virtual) */ | 
|  | u8		lun; | 
|  | u8		flags;		/* Bit Field - See above */ | 
|  | u8		physDiskNum;	/* Phys disk number, -1 else */ | 
|  | u8		rsvd2; | 
|  | u8		rsvd; | 
|  | } INTERNAL_CMD; | 
|  |  | 
|  | typedef struct _negoparms { | 
|  | u8 width; | 
|  | u8 offset; | 
|  | u8 factor; | 
|  | u8 flags; | 
|  | } NEGOPARMS; | 
|  |  | 
|  | typedef struct _dv_parameters { | 
|  | NEGOPARMS	 max; | 
|  | NEGOPARMS	 now; | 
|  | u8		 cmd; | 
|  | u8		 id; | 
|  | u16		 pad1; | 
|  | } DVPARAMETERS; | 
|  |  | 
|  | /* | 
|  | *  Other private/forward protos... | 
|  | */ | 
|  | int		mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); | 
|  | static void	mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq); | 
|  | int		mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); | 
|  |  | 
|  | static int	mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt, | 
|  | SCSIIORequest_t *pReq, int req_idx); | 
|  | static void	mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx); | 
|  | static void	mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply); | 
|  | static int	mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd); | 
|  | static int	mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout ); | 
|  | static u32	SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc); | 
|  |  | 
|  | static int	mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout); | 
|  | static int	mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout); | 
|  |  | 
|  | int		mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset); | 
|  | int		mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply); | 
|  |  | 
|  | static void	mptscsih_initTarget(MPT_SCSI_HOST *hd, VirtTarget *vtarget, u8 lun, char *data, int dlen); | 
|  | static void	mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtTarget *vtarget, char byte56); | 
|  | static void	mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags); | 
|  | static void	mptscsih_no_negotiate(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc); | 
|  | static int	mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target, int flags); | 
|  | static int	mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus); | 
|  | int		mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); | 
|  | static int	mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd); | 
|  | static void	mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice); | 
|  | static void	mptscsih_negotiate_to_asyn_narrow(MPT_SCSI_HOST *hd, VirtTarget *vtarget); | 
|  | static int	mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id); | 
|  |  | 
|  | static struct work_struct   mptscsih_persistTask; | 
|  |  | 
|  | #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION | 
|  | static int	mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io); | 
|  | static void	mptscsih_domainValidation(void *hd); | 
|  | static void	mptscsih_qas_check(MPT_SCSI_HOST *hd, int id); | 
|  | static int	mptscsih_doDv(MPT_SCSI_HOST *hd, int channel, int target); | 
|  | static void	mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage); | 
|  | static void	mptscsih_fillbuf(char *buffer, int size, int index, int width); | 
|  | static void	mptscsih_set_dvflags_raid(MPT_SCSI_HOST *hd, int id); | 
|  | static void	mptscsih_set_dvflags(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc); | 
|  | #endif | 
|  |  | 
|  | void 		mptscsih_remove(struct pci_dev *); | 
|  | void 		mptscsih_shutdown(struct pci_dev *); | 
|  | #ifdef CONFIG_PM | 
|  | int 		mptscsih_suspend(struct pci_dev *pdev, pm_message_t state); | 
|  | int 		mptscsih_resume(struct pci_dev *pdev); | 
|  | #endif | 
|  |  | 
|  | #define SNS_LEN(scp)	sizeof((scp)->sense_buffer) | 
|  |  | 
|  | #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION | 
|  | /* | 
|  | * Domain Validation task structure | 
|  | */ | 
|  | static DEFINE_SPINLOCK(dvtaskQ_lock); | 
|  | static int dvtaskQ_active = 0; | 
|  | static int dvtaskQ_release = 0; | 
|  | static struct work_struct	dvTaskQ_task; | 
|  | #endif | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_add_sge - Place a simple SGE at address pAddr. | 
|  | *	@pAddr: virtual address for SGE | 
|  | *	@flagslength: SGE flags and data transfer length | 
|  | *	@dma_addr: Physical address | 
|  | * | 
|  | *	This routine places a MPT request frame back on the MPT adapter's | 
|  | *	FreeQ. | 
|  | */ | 
|  | static inline void | 
|  | mptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr) | 
|  | { | 
|  | if (sizeof(dma_addr_t) == sizeof(u64)) { | 
|  | SGESimple64_t *pSge = (SGESimple64_t *) pAddr; | 
|  | u32 tmp = dma_addr & 0xFFFFFFFF; | 
|  |  | 
|  | pSge->FlagsLength = cpu_to_le32(flagslength); | 
|  | pSge->Address.Low = cpu_to_le32(tmp); | 
|  | tmp = (u32) ((u64)dma_addr >> 32); | 
|  | pSge->Address.High = cpu_to_le32(tmp); | 
|  |  | 
|  | } else { | 
|  | SGESimple32_t *pSge = (SGESimple32_t *) pAddr; | 
|  | pSge->FlagsLength = cpu_to_le32(flagslength); | 
|  | pSge->Address = cpu_to_le32(dma_addr); | 
|  | } | 
|  | } /* mptscsih_add_sge() */ | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_add_chain - Place a chain SGE at address pAddr. | 
|  | *	@pAddr: virtual address for SGE | 
|  | *	@next: nextChainOffset value (u32's) | 
|  | *	@length: length of next SGL segment | 
|  | *	@dma_addr: Physical address | 
|  | * | 
|  | *	This routine places a MPT request frame back on the MPT adapter's | 
|  | *	FreeQ. | 
|  | */ | 
|  | static inline void | 
|  | mptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr) | 
|  | { | 
|  | if (sizeof(dma_addr_t) == sizeof(u64)) { | 
|  | SGEChain64_t *pChain = (SGEChain64_t *) pAddr; | 
|  | u32 tmp = dma_addr & 0xFFFFFFFF; | 
|  |  | 
|  | pChain->Length = cpu_to_le16(length); | 
|  | pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size(); | 
|  |  | 
|  | pChain->NextChainOffset = next; | 
|  |  | 
|  | pChain->Address.Low = cpu_to_le32(tmp); | 
|  | tmp = (u32) ((u64)dma_addr >> 32); | 
|  | pChain->Address.High = cpu_to_le32(tmp); | 
|  | } else { | 
|  | SGEChain32_t *pChain = (SGEChain32_t *) pAddr; | 
|  | pChain->Length = cpu_to_le16(length); | 
|  | pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size(); | 
|  | pChain->NextChainOffset = next; | 
|  | pChain->Address = cpu_to_le32(dma_addr); | 
|  | } | 
|  | } /* mptscsih_add_chain() */ | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_getFreeChainBuffer - Function to get a free chain | 
|  | *	from the MPT_SCSI_HOST FreeChainQ. | 
|  | *	@ioc: Pointer to MPT_ADAPTER structure | 
|  | *	@req_idx: Index of the SCSI IO request frame. (output) | 
|  | * | 
|  | *	return SUCCESS or FAILED | 
|  | */ | 
|  | static inline int | 
|  | mptscsih_getFreeChainBuffer(MPT_ADAPTER *ioc, int *retIndex) | 
|  | { | 
|  | MPT_FRAME_HDR *chainBuf; | 
|  | unsigned long flags; | 
|  | int rc; | 
|  | int chain_idx; | 
|  |  | 
|  | dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer called\n", | 
|  | ioc->name)); | 
|  | spin_lock_irqsave(&ioc->FreeQlock, flags); | 
|  | if (!list_empty(&ioc->FreeChainQ)) { | 
|  | int offset; | 
|  |  | 
|  | chainBuf = list_entry(ioc->FreeChainQ.next, MPT_FRAME_HDR, | 
|  | u.frame.linkage.list); | 
|  | list_del(&chainBuf->u.frame.linkage.list); | 
|  | offset = (u8 *)chainBuf - (u8 *)ioc->ChainBuffer; | 
|  | chain_idx = offset / ioc->req_sz; | 
|  | rc = SUCCESS; | 
|  | dsgprintk((MYIOC_s_ERR_FMT "getFreeChainBuffer chainBuf=%p ChainBuffer=%p offset=%d chain_idx=%d\n", | 
|  | ioc->name, chainBuf, ioc->ChainBuffer, offset, chain_idx)); | 
|  | } else { | 
|  | rc = FAILED; | 
|  | chain_idx = MPT_HOST_NO_CHAIN; | 
|  | dfailprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer failed\n", | 
|  | ioc->name)); | 
|  | } | 
|  | spin_unlock_irqrestore(&ioc->FreeQlock, flags); | 
|  |  | 
|  | *retIndex = chain_idx; | 
|  | return rc; | 
|  | } /* mptscsih_getFreeChainBuffer() */ | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_AddSGE - Add a SGE (plus chain buffers) to the | 
|  | *	SCSIIORequest_t Message Frame. | 
|  | *	@ioc: Pointer to MPT_ADAPTER structure | 
|  | *	@SCpnt: Pointer to scsi_cmnd structure | 
|  | *	@pReq: Pointer to SCSIIORequest_t structure | 
|  | * | 
|  | *	Returns ... | 
|  | */ | 
|  | static int | 
|  | mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt, | 
|  | SCSIIORequest_t *pReq, int req_idx) | 
|  | { | 
|  | char 	*psge; | 
|  | char	*chainSge; | 
|  | struct scatterlist *sg; | 
|  | int	 frm_sz; | 
|  | int	 sges_left, sg_done; | 
|  | int	 chain_idx = MPT_HOST_NO_CHAIN; | 
|  | int	 sgeOffset; | 
|  | int	 numSgeSlots, numSgeThisFrame; | 
|  | u32	 sgflags, sgdir, thisxfer = 0; | 
|  | int	 chain_dma_off = 0; | 
|  | int	 newIndex; | 
|  | int	 ii; | 
|  | dma_addr_t v2; | 
|  | u32	RequestNB; | 
|  |  | 
|  | sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK; | 
|  | if (sgdir == MPI_SCSIIO_CONTROL_WRITE)  { | 
|  | sgdir = MPT_TRANSFER_HOST_TO_IOC; | 
|  | } else { | 
|  | sgdir = MPT_TRANSFER_IOC_TO_HOST; | 
|  | } | 
|  |  | 
|  | psge = (char *) &pReq->SGL; | 
|  | frm_sz = ioc->req_sz; | 
|  |  | 
|  | /* Map the data portion, if any. | 
|  | * sges_left  = 0 if no data transfer. | 
|  | */ | 
|  | if ( (sges_left = SCpnt->use_sg) ) { | 
|  | sges_left = pci_map_sg(ioc->pcidev, | 
|  | (struct scatterlist *) SCpnt->request_buffer, | 
|  | SCpnt->use_sg, | 
|  | SCpnt->sc_data_direction); | 
|  | if (sges_left == 0) | 
|  | return FAILED; | 
|  | } else if (SCpnt->request_bufflen) { | 
|  | SCpnt->SCp.dma_handle = pci_map_single(ioc->pcidev, | 
|  | SCpnt->request_buffer, | 
|  | SCpnt->request_bufflen, | 
|  | SCpnt->sc_data_direction); | 
|  | dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n", | 
|  | ioc->name, SCpnt, SCpnt->request_bufflen)); | 
|  | mptscsih_add_sge((char *) &pReq->SGL, | 
|  | 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen, | 
|  | SCpnt->SCp.dma_handle); | 
|  |  | 
|  | return SUCCESS; | 
|  | } | 
|  |  | 
|  | /* Handle the SG case. | 
|  | */ | 
|  | sg = (struct scatterlist *) SCpnt->request_buffer; | 
|  | sg_done  = 0; | 
|  | sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION); | 
|  | chainSge = NULL; | 
|  |  | 
|  | /* Prior to entering this loop - the following must be set | 
|  | * current MF:  sgeOffset (bytes) | 
|  | *              chainSge (Null if original MF is not a chain buffer) | 
|  | *              sg_done (num SGE done for this MF) | 
|  | */ | 
|  |  | 
|  | nextSGEset: | 
|  | numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) ); | 
|  | numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots; | 
|  |  | 
|  | sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir; | 
|  |  | 
|  | /* Get first (num - 1) SG elements | 
|  | * Skip any SG entries with a length of 0 | 
|  | * NOTE: at finish, sg and psge pointed to NEXT data/location positions | 
|  | */ | 
|  | for (ii=0; ii < (numSgeThisFrame-1); ii++) { | 
|  | thisxfer = sg_dma_len(sg); | 
|  | if (thisxfer == 0) { | 
|  | sg ++; /* Get next SG element from the OS */ | 
|  | sg_done++; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | v2 = sg_dma_address(sg); | 
|  | mptscsih_add_sge(psge, sgflags | thisxfer, v2); | 
|  |  | 
|  | sg++;		/* Get next SG element from the OS */ | 
|  | psge += (sizeof(u32) + sizeof(dma_addr_t)); | 
|  | sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); | 
|  | sg_done++; | 
|  | } | 
|  |  | 
|  | if (numSgeThisFrame == sges_left) { | 
|  | /* Add last element, end of buffer and end of list flags. | 
|  | */ | 
|  | sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT | | 
|  | MPT_SGE_FLAGS_END_OF_BUFFER | | 
|  | MPT_SGE_FLAGS_END_OF_LIST; | 
|  |  | 
|  | /* Add last SGE and set termination flags. | 
|  | * Note: Last SGE may have a length of 0 - which should be ok. | 
|  | */ | 
|  | thisxfer = sg_dma_len(sg); | 
|  |  | 
|  | v2 = sg_dma_address(sg); | 
|  | mptscsih_add_sge(psge, sgflags | thisxfer, v2); | 
|  | /* | 
|  | sg++; | 
|  | psge += (sizeof(u32) + sizeof(dma_addr_t)); | 
|  | */ | 
|  | sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); | 
|  | sg_done++; | 
|  |  | 
|  | if (chainSge) { | 
|  | /* The current buffer is a chain buffer, | 
|  | * but there is not another one. | 
|  | * Update the chain element | 
|  | * Offset and Length fields. | 
|  | */ | 
|  | mptscsih_add_chain((char *)chainSge, 0, sgeOffset, ioc->ChainBufferDMA + chain_dma_off); | 
|  | } else { | 
|  | /* The current buffer is the original MF | 
|  | * and there is no Chain buffer. | 
|  | */ | 
|  | pReq->ChainOffset = 0; | 
|  | RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor)  + 1) & 0x03; | 
|  | dsgprintk((MYIOC_s_INFO_FMT | 
|  | "Single Buffer RequestNB=%x, sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset)); | 
|  | ioc->RequestNB[req_idx] = RequestNB; | 
|  | } | 
|  | } else { | 
|  | /* At least one chain buffer is needed. | 
|  | * Complete the first MF | 
|  | *  - last SGE element, set the LastElement bit | 
|  | *  - set ChainOffset (words) for orig MF | 
|  | *             (OR finish previous MF chain buffer) | 
|  | *  - update MFStructPtr ChainIndex | 
|  | *  - Populate chain element | 
|  | * Also | 
|  | * Loop until done. | 
|  | */ | 
|  |  | 
|  | dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n", | 
|  | ioc->name, sg_done)); | 
|  |  | 
|  | /* Set LAST_ELEMENT flag for last non-chain element | 
|  | * in the buffer. Since psge points at the NEXT | 
|  | * SGE element, go back one SGE element, update the flags | 
|  | * and reset the pointer. (Note: sgflags & thisxfer are already | 
|  | * set properly). | 
|  | */ | 
|  | if (sg_done) { | 
|  | u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t))); | 
|  | sgflags = le32_to_cpu(*ptmp); | 
|  | sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT; | 
|  | *ptmp = cpu_to_le32(sgflags); | 
|  | } | 
|  |  | 
|  | if (chainSge) { | 
|  | /* The current buffer is a chain buffer. | 
|  | * chainSge points to the previous Chain Element. | 
|  | * Update its chain element Offset and Length (must | 
|  | * include chain element size) fields. | 
|  | * Old chain element is now complete. | 
|  | */ | 
|  | u8 nextChain = (u8) (sgeOffset >> 2); | 
|  | sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); | 
|  | mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, ioc->ChainBufferDMA + chain_dma_off); | 
|  | } else { | 
|  | /* The original MF buffer requires a chain buffer - | 
|  | * set the offset. | 
|  | * Last element in this MF is a chain element. | 
|  | */ | 
|  | pReq->ChainOffset = (u8) (sgeOffset >> 2); | 
|  | RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor)  + 1) & 0x03; | 
|  | dsgprintk((MYIOC_s_ERR_FMT "Chain Buffer Needed, RequestNB=%x sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset)); | 
|  | ioc->RequestNB[req_idx] = RequestNB; | 
|  | } | 
|  |  | 
|  | sges_left -= sg_done; | 
|  |  | 
|  |  | 
|  | /* NOTE: psge points to the beginning of the chain element | 
|  | * in current buffer. Get a chain buffer. | 
|  | */ | 
|  | if ((mptscsih_getFreeChainBuffer(ioc, &newIndex)) == FAILED) { | 
|  | dfailprintk((MYIOC_s_INFO_FMT | 
|  | "getFreeChainBuffer FAILED SCSI cmd=%02x (%p)\n", | 
|  | ioc->name, pReq->CDB[0], SCpnt)); | 
|  | return FAILED; | 
|  | } | 
|  |  | 
|  | /* Update the tracking arrays. | 
|  | * If chainSge == NULL, update ReqToChain, else ChainToChain | 
|  | */ | 
|  | if (chainSge) { | 
|  | ioc->ChainToChain[chain_idx] = newIndex; | 
|  | } else { | 
|  | ioc->ReqToChain[req_idx] = newIndex; | 
|  | } | 
|  | chain_idx = newIndex; | 
|  | chain_dma_off = ioc->req_sz * chain_idx; | 
|  |  | 
|  | /* Populate the chainSGE for the current buffer. | 
|  | * - Set chain buffer pointer to psge and fill | 
|  | *   out the Address and Flags fields. | 
|  | */ | 
|  | chainSge = (char *) psge; | 
|  | dsgprintk((KERN_INFO "  Current buff @ %p (index 0x%x)", | 
|  | psge, req_idx)); | 
|  |  | 
|  | /* Start the SGE for the next buffer | 
|  | */ | 
|  | psge = (char *) (ioc->ChainBuffer + chain_dma_off); | 
|  | sgeOffset = 0; | 
|  | sg_done = 0; | 
|  |  | 
|  | dsgprintk((KERN_INFO "  Chain buff @ %p (index 0x%x)\n", | 
|  | psge, chain_idx)); | 
|  |  | 
|  | /* Start the SGE for the next buffer | 
|  | */ | 
|  |  | 
|  | goto nextSGEset; | 
|  | } | 
|  |  | 
|  | return SUCCESS; | 
|  | } /* mptscsih_AddSGE() */ | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_io_done - Main SCSI IO callback routine registered to | 
|  | *	Fusion MPT (base) driver | 
|  | *	@ioc: Pointer to MPT_ADAPTER structure | 
|  | *	@mf: Pointer to original MPT request frame | 
|  | *	@r: Pointer to MPT reply frame (NULL if TurboReply) | 
|  | * | 
|  | *	This routine is called from mpt.c::mpt_interrupt() at the completion | 
|  | *	of any SCSI IO request. | 
|  | *	This routine is registered with the Fusion MPT (base) driver at driver | 
|  | *	load/init time via the mpt_register() API call. | 
|  | * | 
|  | *	Returns 1 indicating alloc'd request frame ptr should be freed. | 
|  | */ | 
|  | int | 
|  | mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) | 
|  | { | 
|  | struct scsi_cmnd	*sc; | 
|  | MPT_SCSI_HOST	*hd; | 
|  | SCSIIORequest_t	*pScsiReq; | 
|  | SCSIIOReply_t	*pScsiReply; | 
|  | u16		 req_idx; | 
|  |  | 
|  | hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; | 
|  |  | 
|  | req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); | 
|  | sc = hd->ScsiLookup[req_idx]; | 
|  | if (sc == NULL) { | 
|  | MPIHeader_t *hdr = (MPIHeader_t *)mf; | 
|  |  | 
|  | /* Remark: writeSDP1 will use the ScsiDoneCtx | 
|  | * If a SCSI I/O cmd, device disabled by OS and | 
|  | * completion done. Cannot touch sc struct. Just free mem. | 
|  | */ | 
|  | if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST) | 
|  | printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n", | 
|  | ioc->name); | 
|  |  | 
|  | mptscsih_freeChainBuffers(ioc, req_idx); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | sc->result = DID_OK << 16;		/* Set default reply as OK */ | 
|  | pScsiReq = (SCSIIORequest_t *) mf; | 
|  | pScsiReply = (SCSIIOReply_t *) mr; | 
|  |  | 
|  | if((ioc->facts.MsgVersion >= MPI_VERSION_01_05) && pScsiReply){ | 
|  | dmfprintk((MYIOC_s_INFO_FMT | 
|  | "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d,task-tag=%d)\n", | 
|  | ioc->name, mf, mr, sc, req_idx, pScsiReply->TaskTag)); | 
|  | }else{ | 
|  | dmfprintk((MYIOC_s_INFO_FMT | 
|  | "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n", | 
|  | ioc->name, mf, mr, sc, req_idx)); | 
|  | } | 
|  |  | 
|  | if (pScsiReply == NULL) { | 
|  | /* special context reply handling */ | 
|  | ; | 
|  | } else { | 
|  | u32	 xfer_cnt; | 
|  | u16	 status; | 
|  | u8	 scsi_state, scsi_status; | 
|  |  | 
|  | status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; | 
|  | scsi_state = pScsiReply->SCSIState; | 
|  | scsi_status = pScsiReply->SCSIStatus; | 
|  | xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); | 
|  | sc->resid = sc->request_bufflen - xfer_cnt; | 
|  |  | 
|  | /* | 
|  | *  if we get a data underrun indication, yet no data was | 
|  | *  transferred and the SCSI status indicates that the | 
|  | *  command was never started, change the data underrun | 
|  | *  to success | 
|  | */ | 
|  | if (status == MPI_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 && | 
|  | (scsi_status == MPI_SCSI_STATUS_BUSY || | 
|  | scsi_status == MPI_SCSI_STATUS_RESERVATION_CONFLICT || | 
|  | scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)) { | 
|  | status = MPI_IOCSTATUS_SUCCESS; | 
|  | } | 
|  |  | 
|  | dreplyprintk((KERN_NOTICE "Reply ha=%d id=%d lun=%d:\n" | 
|  | "IOCStatus=%04xh SCSIState=%02xh SCSIStatus=%02xh\n" | 
|  | "resid=%d bufflen=%d xfer_cnt=%d\n", | 
|  | ioc->id, sc->device->id, sc->device->lun, | 
|  | status, scsi_state, scsi_status, sc->resid, | 
|  | sc->request_bufflen, xfer_cnt)); | 
|  |  | 
|  | if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) | 
|  | mptscsih_copy_sense_data(sc, hd, mf, pScsiReply); | 
|  |  | 
|  | /* | 
|  | *  Look for + dump FCP ResponseInfo[]! | 
|  | */ | 
|  | if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID && | 
|  | pScsiReply->ResponseInfo) { | 
|  | printk(KERN_NOTICE "ha=%d id=%d lun=%d: " | 
|  | "FCP_ResponseInfo=%08xh\n", | 
|  | ioc->id, sc->device->id, sc->device->lun, | 
|  | le32_to_cpu(pScsiReply->ResponseInfo)); | 
|  | } | 
|  |  | 
|  | switch(status) { | 
|  | case MPI_IOCSTATUS_BUSY:			/* 0x0002 */ | 
|  | /* CHECKME! | 
|  | * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry) | 
|  | * But not: DID_BUS_BUSY lest one risk | 
|  | * killing interrupt handler:-( | 
|  | */ | 
|  | sc->result = SAM_STAT_BUSY; | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_INVALID_BUS:		/* 0x0041 */ | 
|  | case MPI_IOCSTATUS_SCSI_INVALID_TARGETID:	/* 0x0042 */ | 
|  | sc->result = DID_BAD_TARGET << 16; | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE:	/* 0x0043 */ | 
|  | /* Spoof to SCSI Selection Timeout! */ | 
|  | sc->result = DID_NO_CONNECT << 16; | 
|  |  | 
|  | if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF) | 
|  | hd->sel_timeout[pScsiReq->TargetID]++; | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_TASK_TERMINATED:	/* 0x0048 */ | 
|  | case MPI_IOCSTATUS_SCSI_IOC_TERMINATED:		/* 0x004B */ | 
|  | case MPI_IOCSTATUS_SCSI_EXT_TERMINATED:		/* 0x004C */ | 
|  | /* Linux handles an unsolicited DID_RESET better | 
|  | * than an unsolicited DID_ABORT. | 
|  | */ | 
|  | sc->result = DID_RESET << 16; | 
|  |  | 
|  | /* GEM Workaround. */ | 
|  | if (ioc->bus_type == SPI) | 
|  | mptscsih_no_negotiate(hd, sc); | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:	/* 0x0049 */ | 
|  | sc->resid = sc->request_bufflen - xfer_cnt; | 
|  | if((xfer_cnt==0)||(sc->underflow > xfer_cnt)) | 
|  | sc->result=DID_SOFT_ERROR << 16; | 
|  | else /* Sufficient data transfer occurred */ | 
|  | sc->result = (DID_OK << 16) | scsi_status; | 
|  | dreplyprintk((KERN_NOTICE | 
|  | "RESIDUAL_MISMATCH: result=%x on id=%d\n", sc->result, sc->device->id)); | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN:		/* 0x0045 */ | 
|  | /* | 
|  | *  Do upfront check for valid SenseData and give it | 
|  | *  precedence! | 
|  | */ | 
|  | sc->result = (DID_OK << 16) | scsi_status; | 
|  | if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { | 
|  | /* Have already saved the status and sense data | 
|  | */ | 
|  | ; | 
|  | } else { | 
|  | if (xfer_cnt < sc->underflow) { | 
|  | if (scsi_status == SAM_STAT_BUSY) | 
|  | sc->result = SAM_STAT_BUSY; | 
|  | else | 
|  | sc->result = DID_SOFT_ERROR << 16; | 
|  | } | 
|  | if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) { | 
|  | /* What to do? | 
|  | */ | 
|  | sc->result = DID_SOFT_ERROR << 16; | 
|  | } | 
|  | else if (scsi_state & MPI_SCSI_STATE_TERMINATED) { | 
|  | /*  Not real sure here either...  */ | 
|  | sc->result = DID_RESET << 16; | 
|  | } | 
|  | } | 
|  |  | 
|  | dreplyprintk((KERN_NOTICE "  sc->underflow={report ERR if < %02xh bytes xfer'd}\n", | 
|  | sc->underflow)); | 
|  | dreplyprintk((KERN_NOTICE "  ActBytesXferd=%02xh\n", xfer_cnt)); | 
|  | /* Report Queue Full | 
|  | */ | 
|  | if (scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL) | 
|  | mptscsih_report_queue_full(sc, pScsiReply, pScsiReq); | 
|  |  | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR:	/* 0x0040 */ | 
|  | case MPI_IOCSTATUS_SUCCESS:			/* 0x0000 */ | 
|  | if (scsi_status == MPI_SCSI_STATUS_BUSY) | 
|  | sc->result = (DID_BUS_BUSY << 16) | scsi_status; | 
|  | else | 
|  | sc->result = (DID_OK << 16) | scsi_status; | 
|  | if (scsi_state == 0) { | 
|  | ; | 
|  | } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { | 
|  | /* | 
|  | * If running against circa 200003dd 909 MPT f/w, | 
|  | * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL | 
|  | * (QUEUE_FULL) returned from device! --> get 0x0000?128 | 
|  | * and with SenseBytes set to 0. | 
|  | */ | 
|  | if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL) | 
|  | mptscsih_report_queue_full(sc, pScsiReply, pScsiReq); | 
|  |  | 
|  | } | 
|  | else if (scsi_state & | 
|  | (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS) | 
|  | ) { | 
|  | /* | 
|  | * What to do? | 
|  | */ | 
|  | sc->result = DID_SOFT_ERROR << 16; | 
|  | } | 
|  | else if (scsi_state & MPI_SCSI_STATE_TERMINATED) { | 
|  | /*  Not real sure here either...  */ | 
|  | sc->result = DID_RESET << 16; | 
|  | } | 
|  | else if (scsi_state & MPI_SCSI_STATE_QUEUE_TAG_REJECTED) { | 
|  | /* Device Inq. data indicates that it supports | 
|  | * QTags, but rejects QTag messages. | 
|  | * This command completed OK. | 
|  | * | 
|  | * Not real sure here either so do nothing...  */ | 
|  | } | 
|  |  | 
|  | if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL) | 
|  | mptscsih_report_queue_full(sc, pScsiReply, pScsiReq); | 
|  |  | 
|  | /* Add handling of: | 
|  | * Reservation Conflict, Busy, | 
|  | * Command Terminated, CHECK | 
|  | */ | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR:		/* 0x0047 */ | 
|  | sc->result = DID_SOFT_ERROR << 16; | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_INVALID_FUNCTION:		/* 0x0001 */ | 
|  | case MPI_IOCSTATUS_INVALID_SGL:			/* 0x0003 */ | 
|  | case MPI_IOCSTATUS_INTERNAL_ERROR:		/* 0x0004 */ | 
|  | case MPI_IOCSTATUS_RESERVED:			/* 0x0005 */ | 
|  | case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES:	/* 0x0006 */ | 
|  | case MPI_IOCSTATUS_INVALID_FIELD:		/* 0x0007 */ | 
|  | case MPI_IOCSTATUS_INVALID_STATE:		/* 0x0008 */ | 
|  | case MPI_IOCSTATUS_SCSI_DATA_OVERRUN:		/* 0x0044 */ | 
|  | case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR:		/* 0x0046 */ | 
|  | case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED:	/* 0x004A */ | 
|  | default: | 
|  | /* | 
|  | * What to do? | 
|  | */ | 
|  | sc->result = DID_SOFT_ERROR << 16; | 
|  | break; | 
|  |  | 
|  | }	/* switch(status) */ | 
|  |  | 
|  | dreplyprintk((KERN_NOTICE "  sc->result is %08xh\n", sc->result)); | 
|  | } /* end of address reply case */ | 
|  |  | 
|  | /* Unmap the DMA buffers, if any. */ | 
|  | if (sc->use_sg) { | 
|  | pci_unmap_sg(ioc->pcidev, (struct scatterlist *) sc->request_buffer, | 
|  | sc->use_sg, sc->sc_data_direction); | 
|  | } else if (sc->request_bufflen) { | 
|  | pci_unmap_single(ioc->pcidev, sc->SCp.dma_handle, | 
|  | sc->request_bufflen, sc->sc_data_direction); | 
|  | } | 
|  |  | 
|  | hd->ScsiLookup[req_idx] = NULL; | 
|  |  | 
|  | sc->scsi_done(sc);		/* Issue the command callback */ | 
|  |  | 
|  | /* Free Chain buffers */ | 
|  | mptscsih_freeChainBuffers(ioc, req_idx); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	mptscsih_flush_running_cmds - For each command found, search | 
|  | *		Scsi_Host instance taskQ and reply to OS. | 
|  | *		Called only if recovering from a FW reload. | 
|  | *	@hd: Pointer to a SCSI HOST structure | 
|  | * | 
|  | *	Returns: None. | 
|  | * | 
|  | *	Must be called while new I/Os are being queued. | 
|  | */ | 
|  | static void | 
|  | mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd) | 
|  | { | 
|  | MPT_ADAPTER *ioc = hd->ioc; | 
|  | struct scsi_cmnd	*SCpnt; | 
|  | MPT_FRAME_HDR	*mf; | 
|  | int		 ii; | 
|  | int		 max = ioc->req_depth; | 
|  |  | 
|  | dprintk((KERN_INFO MYNAM ": flush_ScsiLookup called\n")); | 
|  | for (ii= 0; ii < max; ii++) { | 
|  | if ((SCpnt = hd->ScsiLookup[ii]) != NULL) { | 
|  |  | 
|  | /* Command found. | 
|  | */ | 
|  |  | 
|  | /* Null ScsiLookup index | 
|  | */ | 
|  | hd->ScsiLookup[ii] = NULL; | 
|  |  | 
|  | mf = MPT_INDEX_2_MFPTR(ioc, ii); | 
|  | dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)\n", | 
|  | mf, SCpnt)); | 
|  |  | 
|  | /* Set status, free OS resources (SG DMA buffers) | 
|  | * Do OS callback | 
|  | * Free driver resources (chain, msg buffers) | 
|  | */ | 
|  | if (SCpnt->use_sg) { | 
|  | pci_unmap_sg(ioc->pcidev, | 
|  | (struct scatterlist *) SCpnt->request_buffer, | 
|  | SCpnt->use_sg, | 
|  | SCpnt->sc_data_direction); | 
|  | } else if (SCpnt->request_bufflen) { | 
|  | pci_unmap_single(ioc->pcidev, | 
|  | SCpnt->SCp.dma_handle, | 
|  | SCpnt->request_bufflen, | 
|  | SCpnt->sc_data_direction); | 
|  | } | 
|  | SCpnt->result = DID_RESET << 16; | 
|  | SCpnt->host_scribble = NULL; | 
|  |  | 
|  | /* Free Chain buffers */ | 
|  | mptscsih_freeChainBuffers(ioc, ii); | 
|  |  | 
|  | /* Free Message frames */ | 
|  | mpt_free_msg_frame(ioc, mf); | 
|  |  | 
|  | SCpnt->scsi_done(SCpnt);	/* Issue the command callback */ | 
|  | } | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	mptscsih_search_running_cmds - Delete any commands associated | 
|  | *		with the specified target and lun. Function called only | 
|  | *		when a lun is disable by mid-layer. | 
|  | *		Do NOT access the referenced scsi_cmnd structure or | 
|  | *		members. Will cause either a paging or NULL ptr error. | 
|  | *      @hd: Pointer to a SCSI HOST structure | 
|  | *	@vdevice: per device private data | 
|  | * | 
|  | *	Returns: None. | 
|  | * | 
|  | *	Called from slave_destroy. | 
|  | */ | 
|  | static void | 
|  | mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, VirtDevice *vdevice) | 
|  | { | 
|  | SCSIIORequest_t	*mf = NULL; | 
|  | int		 ii; | 
|  | int		 max = hd->ioc->req_depth; | 
|  | struct scsi_cmnd *sc; | 
|  |  | 
|  | dsprintk((KERN_INFO MYNAM ": search_running target %d lun %d max %d\n", | 
|  | vdevice->target_id, vdevice->lun, max)); | 
|  |  | 
|  | for (ii=0; ii < max; ii++) { | 
|  | if ((sc = hd->ScsiLookup[ii]) != NULL) { | 
|  |  | 
|  | mf = (SCSIIORequest_t *)MPT_INDEX_2_MFPTR(hd->ioc, ii); | 
|  |  | 
|  | dsprintk(( "search_running: found (sc=%p, mf = %p) target %d, lun %d \n", | 
|  | hd->ScsiLookup[ii], mf, mf->TargetID, mf->LUN[1])); | 
|  |  | 
|  | if ((mf->TargetID != ((u8)vdevice->target_id)) || (mf->LUN[1] != ((u8) vdevice->lun))) | 
|  | continue; | 
|  |  | 
|  | /* Cleanup | 
|  | */ | 
|  | hd->ScsiLookup[ii] = NULL; | 
|  | mptscsih_freeChainBuffers(hd->ioc, ii); | 
|  | mpt_free_msg_frame(hd->ioc, (MPT_FRAME_HDR *)mf); | 
|  | if (sc->use_sg) { | 
|  | pci_unmap_sg(hd->ioc->pcidev, | 
|  | (struct scatterlist *) sc->request_buffer, | 
|  | sc->use_sg, | 
|  | sc->sc_data_direction); | 
|  | } else if (sc->request_bufflen) { | 
|  | pci_unmap_single(hd->ioc->pcidev, | 
|  | sc->SCp.dma_handle, | 
|  | sc->request_bufflen, | 
|  | sc->sc_data_direction); | 
|  | } | 
|  | sc->host_scribble = NULL; | 
|  | sc->result = DID_NO_CONNECT << 16; | 
|  | sc->scsi_done(sc); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_report_queue_full - Report QUEUE_FULL status returned | 
|  | *	from a SCSI target device. | 
|  | *	@sc: Pointer to scsi_cmnd structure | 
|  | *	@pScsiReply: Pointer to SCSIIOReply_t | 
|  | *	@pScsiReq: Pointer to original SCSI request | 
|  | * | 
|  | *	This routine periodically reports QUEUE_FULL status returned from a | 
|  | *	SCSI target device.  It reports this to the console via kernel | 
|  | *	printk() API call, not more than once every 10 seconds. | 
|  | */ | 
|  | static void | 
|  | mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq) | 
|  | { | 
|  | long time = jiffies; | 
|  | MPT_SCSI_HOST		*hd; | 
|  |  | 
|  | if (sc->device == NULL) | 
|  | return; | 
|  | if (sc->device->host == NULL) | 
|  | return; | 
|  | if ((hd = (MPT_SCSI_HOST *)sc->device->host->hostdata) == NULL) | 
|  | return; | 
|  |  | 
|  | if (time - hd->last_queue_full > 10 * HZ) { | 
|  | dprintk((MYIOC_s_WARN_FMT "Device (%d:%d:%d) reported QUEUE_FULL!\n", | 
|  | hd->ioc->name, 0, sc->device->id, sc->device->lun)); | 
|  | hd->last_queue_full = time; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_remove - Removed scsi devices | 
|  | *	@pdev: Pointer to pci_dev structure | 
|  | * | 
|  | * | 
|  | */ | 
|  | void | 
|  | mptscsih_remove(struct pci_dev *pdev) | 
|  | { | 
|  | MPT_ADAPTER 		*ioc = pci_get_drvdata(pdev); | 
|  | struct Scsi_Host 	*host = ioc->sh; | 
|  | MPT_SCSI_HOST		*hd; | 
|  | #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION | 
|  | int 		 	count; | 
|  | unsigned long	 	flags; | 
|  | #endif | 
|  | int sz1; | 
|  |  | 
|  | if(!host) { | 
|  | mpt_detach(pdev); | 
|  | return; | 
|  | } | 
|  |  | 
|  | scsi_remove_host(host); | 
|  |  | 
|  | if((hd = (MPT_SCSI_HOST *)host->hostdata) == NULL) | 
|  | return; | 
|  |  | 
|  | #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION | 
|  | /* Check DV thread active */ | 
|  | count = 10 * HZ; | 
|  | spin_lock_irqsave(&dvtaskQ_lock, flags); | 
|  | if (dvtaskQ_active) { | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, flags); | 
|  | while(dvtaskQ_active && --count) | 
|  | schedule_timeout_interruptible(1); | 
|  | } else { | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, flags); | 
|  | } | 
|  | if (!count) | 
|  | printk(KERN_ERR MYNAM ": ERROR - DV thread still active!\n"); | 
|  | #if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY) | 
|  | else | 
|  | printk(KERN_ERR MYNAM ": DV thread orig %d, count %d\n", 10 * HZ, count); | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | mptscsih_shutdown(pdev); | 
|  |  | 
|  | sz1=0; | 
|  |  | 
|  | if (hd->ScsiLookup != NULL) { | 
|  | sz1 = hd->ioc->req_depth * sizeof(void *); | 
|  | kfree(hd->ScsiLookup); | 
|  | hd->ScsiLookup = NULL; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Free pointer array. | 
|  | */ | 
|  | kfree(hd->Targets); | 
|  | hd->Targets = NULL; | 
|  |  | 
|  | dprintk((MYIOC_s_INFO_FMT | 
|  | "Free'd ScsiLookup (%d) memory\n", | 
|  | hd->ioc->name, sz1)); | 
|  |  | 
|  | kfree(hd->info_kbuf); | 
|  |  | 
|  | /* NULL the Scsi_Host pointer | 
|  | */ | 
|  | hd->ioc->sh = NULL; | 
|  |  | 
|  | scsi_host_put(host); | 
|  |  | 
|  | mpt_detach(pdev); | 
|  |  | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_shutdown - reboot notifier | 
|  | * | 
|  | */ | 
|  | void | 
|  | mptscsih_shutdown(struct pci_dev *pdev) | 
|  | { | 
|  | MPT_ADAPTER 		*ioc = pci_get_drvdata(pdev); | 
|  | struct Scsi_Host 	*host = ioc->sh; | 
|  | MPT_SCSI_HOST		*hd; | 
|  |  | 
|  | if(!host) | 
|  | return; | 
|  |  | 
|  | hd = (MPT_SCSI_HOST *)host->hostdata; | 
|  |  | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_PM | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_suspend - Fusion MPT scsi driver suspend routine. | 
|  | * | 
|  | * | 
|  | */ | 
|  | int | 
|  | mptscsih_suspend(struct pci_dev *pdev, pm_message_t state) | 
|  | { | 
|  | mptscsih_shutdown(pdev); | 
|  | return mpt_suspend(pdev,state); | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_resume - Fusion MPT scsi driver resume routine. | 
|  | * | 
|  | * | 
|  | */ | 
|  | int | 
|  | mptscsih_resume(struct pci_dev *pdev) | 
|  | { | 
|  | MPT_ADAPTER 		*ioc = pci_get_drvdata(pdev); | 
|  | struct Scsi_Host 	*host = ioc->sh; | 
|  | MPT_SCSI_HOST		*hd; | 
|  |  | 
|  | mpt_resume(pdev); | 
|  |  | 
|  | if(!host) | 
|  | return 0; | 
|  |  | 
|  | hd = (MPT_SCSI_HOST *)host->hostdata; | 
|  | if(!hd) | 
|  | return 0; | 
|  |  | 
|  | #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION | 
|  | { | 
|  | unsigned long lflags; | 
|  | spin_lock_irqsave(&dvtaskQ_lock, lflags); | 
|  | if (!dvtaskQ_active) { | 
|  | dvtaskQ_active = 1; | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, lflags); | 
|  | INIT_WORK(&dvTaskQ_task, | 
|  | mptscsih_domainValidation, (void *) hd); | 
|  | schedule_work(&dvTaskQ_task); | 
|  | } else { | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, lflags); | 
|  | } | 
|  | } | 
|  | #endif | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_info - Return information about MPT adapter | 
|  | *	@SChost: Pointer to Scsi_Host structure | 
|  | * | 
|  | *	(linux scsi_host_template.info routine) | 
|  | * | 
|  | *	Returns pointer to buffer where information was written. | 
|  | */ | 
|  | const char * | 
|  | mptscsih_info(struct Scsi_Host *SChost) | 
|  | { | 
|  | MPT_SCSI_HOST *h; | 
|  | int size = 0; | 
|  |  | 
|  | h = (MPT_SCSI_HOST *)SChost->hostdata; | 
|  |  | 
|  | if (h) { | 
|  | if (h->info_kbuf == NULL) | 
|  | if ((h->info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL) | 
|  | return h->info_kbuf; | 
|  | h->info_kbuf[0] = '\0'; | 
|  |  | 
|  | mpt_print_ioc_summary(h->ioc, h->info_kbuf, &size, 0, 0); | 
|  | h->info_kbuf[size-1] = '\0'; | 
|  | } | 
|  |  | 
|  | return h->info_kbuf; | 
|  | } | 
|  |  | 
|  | struct info_str { | 
|  | char *buffer; | 
|  | int   length; | 
|  | int   offset; | 
|  | int   pos; | 
|  | }; | 
|  |  | 
|  | static void | 
|  | mptscsih_copy_mem_info(struct info_str *info, char *data, int len) | 
|  | { | 
|  | if (info->pos + len > info->length) | 
|  | len = info->length - info->pos; | 
|  |  | 
|  | if (info->pos + len < info->offset) { | 
|  | info->pos += len; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (info->pos < info->offset) { | 
|  | data += (info->offset - info->pos); | 
|  | len  -= (info->offset - info->pos); | 
|  | } | 
|  |  | 
|  | if (len > 0) { | 
|  | memcpy(info->buffer + info->pos, data, len); | 
|  | info->pos += len; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | mptscsih_copy_info(struct info_str *info, char *fmt, ...) | 
|  | { | 
|  | va_list args; | 
|  | char buf[81]; | 
|  | int len; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | len = vsprintf(buf, fmt, args); | 
|  | va_end(args); | 
|  |  | 
|  | mptscsih_copy_mem_info(info, buf, len); | 
|  | return len; | 
|  | } | 
|  |  | 
|  | static int | 
|  | mptscsih_host_info(MPT_ADAPTER *ioc, char *pbuf, off_t offset, int len) | 
|  | { | 
|  | struct info_str info; | 
|  |  | 
|  | info.buffer	= pbuf; | 
|  | info.length	= len; | 
|  | info.offset	= offset; | 
|  | info.pos	= 0; | 
|  |  | 
|  | mptscsih_copy_info(&info, "%s: %s, ", ioc->name, ioc->prod_name); | 
|  | mptscsih_copy_info(&info, "%s%08xh, ", MPT_FW_REV_MAGIC_ID_STRING, ioc->facts.FWVersion.Word); | 
|  | mptscsih_copy_info(&info, "Ports=%d, ", ioc->facts.NumberOfPorts); | 
|  | mptscsih_copy_info(&info, "MaxQ=%d\n", ioc->req_depth); | 
|  |  | 
|  | return ((info.pos > info.offset) ? info.pos - info.offset : 0); | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_proc_info - Return information about MPT adapter | 
|  | * | 
|  | *	(linux scsi_host_template.info routine) | 
|  | * | 
|  | * 	buffer: if write, user data; if read, buffer for user | 
|  | * 	length: if write, return length; | 
|  | * 	offset: if write, 0; if read, the current offset into the buffer from | 
|  | * 		the previous read. | 
|  | * 	hostno: scsi host number | 
|  | *	func:   if write = 1; if read = 0 | 
|  | */ | 
|  | int | 
|  | mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, | 
|  | int length, int func) | 
|  | { | 
|  | MPT_SCSI_HOST	*hd = (MPT_SCSI_HOST *)host->hostdata; | 
|  | MPT_ADAPTER	*ioc = hd->ioc; | 
|  | int size = 0; | 
|  |  | 
|  | if (func) { | 
|  | /* | 
|  | * write is not supported | 
|  | */ | 
|  | } else { | 
|  | if (start) | 
|  | *start = buffer; | 
|  |  | 
|  | size = mptscsih_host_info(ioc, buffer, offset, length); | 
|  | } | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | #define ADD_INDEX_LOG(req_ent)	do { } while(0) | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine. | 
|  | *	@SCpnt: Pointer to scsi_cmnd structure | 
|  | *	@done: Pointer SCSI mid-layer IO completion function | 
|  | * | 
|  | *	(linux scsi_host_template.queuecommand routine) | 
|  | *	This is the primary SCSI IO start routine.  Create a MPI SCSIIORequest | 
|  | *	from a linux scsi_cmnd request and send it to the IOC. | 
|  | * | 
|  | *	Returns 0. (rtn value discarded by linux scsi mid-layer) | 
|  | */ | 
|  | int | 
|  | mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) | 
|  | { | 
|  | MPT_SCSI_HOST		*hd; | 
|  | MPT_FRAME_HDR		*mf; | 
|  | SCSIIORequest_t		*pScsiReq; | 
|  | VirtDevice		*vdev = SCpnt->device->hostdata; | 
|  | int	 lun; | 
|  | u32	 datalen; | 
|  | u32	 scsictl; | 
|  | u32	 scsidir; | 
|  | u32	 cmd_len; | 
|  | int	 my_idx; | 
|  | int	 ii; | 
|  |  | 
|  | hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata; | 
|  | lun = SCpnt->device->lun; | 
|  | SCpnt->scsi_done = done; | 
|  |  | 
|  | dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n", | 
|  | (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done)); | 
|  |  | 
|  | if (hd->resetPending) { | 
|  | dtmprintk((MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n", | 
|  | (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt)); | 
|  | return SCSI_MLQUEUE_HOST_BUSY; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  Put together a MPT SCSI request... | 
|  | */ | 
|  | if ((mf = mpt_get_msg_frame(hd->ioc->DoneCtx, hd->ioc)) == NULL) { | 
|  | dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n", | 
|  | hd->ioc->name)); | 
|  | return SCSI_MLQUEUE_HOST_BUSY; | 
|  | } | 
|  |  | 
|  | pScsiReq = (SCSIIORequest_t *) mf; | 
|  |  | 
|  | my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); | 
|  |  | 
|  | ADD_INDEX_LOG(my_idx); | 
|  |  | 
|  | /*    TUR's being issued with scsictl=0x02000000 (DATA_IN)! | 
|  | *    Seems we may receive a buffer (datalen>0) even when there | 
|  | *    will be no data transfer!  GRRRRR... | 
|  | */ | 
|  | if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) { | 
|  | datalen = SCpnt->request_bufflen; | 
|  | scsidir = MPI_SCSIIO_CONTROL_READ;	/* DATA IN  (host<--ioc<--dev) */ | 
|  | } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) { | 
|  | datalen = SCpnt->request_bufflen; | 
|  | scsidir = MPI_SCSIIO_CONTROL_WRITE;	/* DATA OUT (host-->ioc-->dev) */ | 
|  | } else { | 
|  | datalen = 0; | 
|  | scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER; | 
|  | } | 
|  |  | 
|  | /* Default to untagged. Once a target structure has been allocated, | 
|  | * use the Inquiry data to determine if device supports tagged. | 
|  | */ | 
|  | if (vdev | 
|  | && (vdev->vtarget->tflags & MPT_TARGET_FLAGS_Q_YES) | 
|  | && (SCpnt->device->tagged_supported)) { | 
|  | scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ; | 
|  | } else { | 
|  | scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED; | 
|  | } | 
|  |  | 
|  | /* Use the above information to set up the message frame | 
|  | */ | 
|  | pScsiReq->TargetID = (u8) vdev->target_id; | 
|  | pScsiReq->Bus = vdev->bus_id; | 
|  | pScsiReq->ChainOffset = 0; | 
|  | pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST; | 
|  | pScsiReq->CDBLength = SCpnt->cmd_len; | 
|  | pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE; | 
|  | pScsiReq->Reserved = 0; | 
|  | pScsiReq->MsgFlags = mpt_msg_flags(); | 
|  | pScsiReq->LUN[0] = 0; | 
|  | pScsiReq->LUN[1] = lun; | 
|  | pScsiReq->LUN[2] = 0; | 
|  | pScsiReq->LUN[3] = 0; | 
|  | pScsiReq->LUN[4] = 0; | 
|  | pScsiReq->LUN[5] = 0; | 
|  | pScsiReq->LUN[6] = 0; | 
|  | pScsiReq->LUN[7] = 0; | 
|  | pScsiReq->Control = cpu_to_le32(scsictl); | 
|  |  | 
|  | /* | 
|  | *  Write SCSI CDB into the message | 
|  | */ | 
|  | cmd_len = SCpnt->cmd_len; | 
|  | for (ii=0; ii < cmd_len; ii++) | 
|  | pScsiReq->CDB[ii] = SCpnt->cmnd[ii]; | 
|  |  | 
|  | for (ii=cmd_len; ii < 16; ii++) | 
|  | pScsiReq->CDB[ii] = 0; | 
|  |  | 
|  | /* DataLength */ | 
|  | pScsiReq->DataLength = cpu_to_le32(datalen); | 
|  |  | 
|  | /* SenseBuffer low address */ | 
|  | pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma | 
|  | + (my_idx * MPT_SENSE_BUFFER_ALLOC)); | 
|  |  | 
|  | /* Now add the SG list | 
|  | * Always have a SGE even if null length. | 
|  | */ | 
|  | if (datalen == 0) { | 
|  | /* Add a NULL SGE */ | 
|  | mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0, | 
|  | (dma_addr_t) -1); | 
|  | } else { | 
|  | /* Add a 32 or 64 bit SGE */ | 
|  | if (mptscsih_AddSGE(hd->ioc, SCpnt, pScsiReq, my_idx) != SUCCESS) | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | hd->ScsiLookup[my_idx] = SCpnt; | 
|  | SCpnt->host_scribble = NULL; | 
|  |  | 
|  | #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION | 
|  | if (hd->ioc->bus_type == SPI) { | 
|  | int dvStatus = hd->ioc->spi_data.dvStatus[vdev->target_id]; | 
|  | int issueCmd = 1; | 
|  |  | 
|  | if (dvStatus || hd->ioc->spi_data.forceDv) { | 
|  |  | 
|  | if ((dvStatus & MPT_SCSICFG_NEED_DV) || | 
|  | (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) { | 
|  | unsigned long lflags; | 
|  | /* Schedule DV if necessary */ | 
|  | spin_lock_irqsave(&dvtaskQ_lock, lflags); | 
|  | if (!dvtaskQ_active) { | 
|  | dvtaskQ_active = 1; | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, lflags); | 
|  | INIT_WORK(&dvTaskQ_task, mptscsih_domainValidation, (void *) hd); | 
|  |  | 
|  | schedule_work(&dvTaskQ_task); | 
|  | } else { | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, lflags); | 
|  | } | 
|  | hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV; | 
|  | } | 
|  |  | 
|  | /* Trying to do DV to this target, extend timeout. | 
|  | * Wait to issue until flag is clear | 
|  | */ | 
|  | if (dvStatus & MPT_SCSICFG_DV_PENDING) { | 
|  | mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ); | 
|  | issueCmd = 0; | 
|  | } | 
|  |  | 
|  | /* Set the DV flags. | 
|  | */ | 
|  | if (dvStatus & MPT_SCSICFG_DV_NOT_DONE) | 
|  | mptscsih_set_dvflags(hd, SCpnt); | 
|  |  | 
|  | if (!issueCmd) | 
|  | goto fail; | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | mpt_put_msg_frame(hd->ioc->DoneCtx, hd->ioc, mf); | 
|  | dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n", | 
|  | hd->ioc->name, SCpnt, mf, my_idx)); | 
|  | DBG_DUMP_REQUEST_FRAME(mf) | 
|  | return 0; | 
|  |  | 
|  | fail: | 
|  | hd->ScsiLookup[my_idx] = NULL; | 
|  | mptscsih_freeChainBuffers(hd->ioc, my_idx); | 
|  | mpt_free_msg_frame(hd->ioc, mf); | 
|  | return SCSI_MLQUEUE_HOST_BUSY; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_freeChainBuffers - Function to free chain buffers associated | 
|  | *	with a SCSI IO request | 
|  | *	@hd: Pointer to the MPT_SCSI_HOST instance | 
|  | *	@req_idx: Index of the SCSI IO request frame. | 
|  | * | 
|  | *	Called if SG chain buffer allocation fails and mptscsih callbacks. | 
|  | *	No return. | 
|  | */ | 
|  | static void | 
|  | mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx) | 
|  | { | 
|  | MPT_FRAME_HDR *chain; | 
|  | unsigned long flags; | 
|  | int chain_idx; | 
|  | int next; | 
|  |  | 
|  | /* Get the first chain index and reset | 
|  | * tracker state. | 
|  | */ | 
|  | chain_idx = ioc->ReqToChain[req_idx]; | 
|  | ioc->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN; | 
|  |  | 
|  | while (chain_idx != MPT_HOST_NO_CHAIN) { | 
|  |  | 
|  | /* Save the next chain buffer index */ | 
|  | next = ioc->ChainToChain[chain_idx]; | 
|  |  | 
|  | /* Free this chain buffer and reset | 
|  | * tracker | 
|  | */ | 
|  | ioc->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN; | 
|  |  | 
|  | chain = (MPT_FRAME_HDR *) (ioc->ChainBuffer | 
|  | + (chain_idx * ioc->req_sz)); | 
|  |  | 
|  | spin_lock_irqsave(&ioc->FreeQlock, flags); | 
|  | list_add_tail(&chain->u.frame.linkage.list, &ioc->FreeChainQ); | 
|  | spin_unlock_irqrestore(&ioc->FreeQlock, flags); | 
|  |  | 
|  | dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)\n", | 
|  | ioc->name, chain_idx)); | 
|  |  | 
|  | /* handle next */ | 
|  | chain_idx = next; | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	Reset Handling | 
|  | */ | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_TMHandler - Generic handler for SCSI Task Management. | 
|  | *	Fall through to mpt_HardResetHandler if: not operational, too many | 
|  | *	failed TM requests or handshake failure. | 
|  | * | 
|  | *	@ioc: Pointer to MPT_ADAPTER structure | 
|  | *	@type: Task Management type | 
|  | *	@target: Logical Target ID for reset (if appropriate) | 
|  | *	@lun: Logical Unit for reset (if appropriate) | 
|  | *	@ctx2abort: Context for the task to be aborted (if appropriate) | 
|  | * | 
|  | *	Remark: Currently invoked from a non-interrupt thread (_bh). | 
|  | * | 
|  | *	Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC | 
|  | *	will be active. | 
|  | * | 
|  | *	Returns 0 for SUCCESS or -1 if FAILED. | 
|  | */ | 
|  | static int | 
|  | mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout) | 
|  | { | 
|  | MPT_ADAPTER	*ioc; | 
|  | int		 rc = -1; | 
|  | int		 doTask = 1; | 
|  | u32		 ioc_raw_state; | 
|  | unsigned long	 flags; | 
|  |  | 
|  | /* If FW is being reloaded currently, return success to | 
|  | * the calling function. | 
|  | */ | 
|  | if (hd == NULL) | 
|  | return 0; | 
|  |  | 
|  | ioc = hd->ioc; | 
|  | if (ioc == NULL) { | 
|  | printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!\n"); | 
|  | return FAILED; | 
|  | } | 
|  | dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name)); | 
|  |  | 
|  | // SJR - CHECKME - Can we avoid this here? | 
|  | // (mpt_HardResetHandler has this check...) | 
|  | spin_lock_irqsave(&ioc->diagLock, flags); | 
|  | if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) { | 
|  | spin_unlock_irqrestore(&ioc->diagLock, flags); | 
|  | return FAILED; | 
|  | } | 
|  | spin_unlock_irqrestore(&ioc->diagLock, flags); | 
|  |  | 
|  | /*  Wait a fixed amount of time for the TM pending flag to be cleared. | 
|  | *  If we time out and not bus reset, then we return a FAILED status to the caller. | 
|  | *  The call to mptscsih_tm_pending_wait() will set the pending flag if we are | 
|  | *  successful. Otherwise, reload the FW. | 
|  | */ | 
|  | if (mptscsih_tm_pending_wait(hd) == FAILED) { | 
|  | if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) { | 
|  | dtmprintk((KERN_INFO MYNAM ": %s: TMHandler abort: " | 
|  | "Timed out waiting for last TM (%d) to complete! \n", | 
|  | hd->ioc->name, hd->tmPending)); | 
|  | return FAILED; | 
|  | } else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) { | 
|  | dtmprintk((KERN_INFO MYNAM ": %s: TMHandler target reset: " | 
|  | "Timed out waiting for last TM (%d) to complete! \n", | 
|  | hd->ioc->name, hd->tmPending)); | 
|  | return FAILED; | 
|  | } else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) { | 
|  | dtmprintk((KERN_INFO MYNAM ": %s: TMHandler bus reset: " | 
|  | "Timed out waiting for last TM (%d) to complete! \n", | 
|  | hd->ioc->name, hd->tmPending)); | 
|  | if (hd->tmPending & (1 << MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)) | 
|  | return FAILED; | 
|  |  | 
|  | doTask = 0; | 
|  | } | 
|  | } else { | 
|  | spin_lock_irqsave(&hd->ioc->FreeQlock, flags); | 
|  | hd->tmPending |=  (1 << type); | 
|  | spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); | 
|  | } | 
|  |  | 
|  | /* Is operational? | 
|  | */ | 
|  | ioc_raw_state = mpt_GetIocState(hd->ioc, 0); | 
|  |  | 
|  | #ifdef MPT_DEBUG_RESET | 
|  | if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) { | 
|  | printk(MYIOC_s_WARN_FMT | 
|  | "TM Handler: IOC Not operational(0x%x)!\n", | 
|  | hd->ioc->name, ioc_raw_state); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (doTask && ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL) | 
|  | && !(ioc_raw_state & MPI_DOORBELL_ACTIVE)) { | 
|  |  | 
|  | /* Isse the Task Mgmt request. | 
|  | */ | 
|  | if (hd->hard_resets < -1) | 
|  | hd->hard_resets++; | 
|  | rc = mptscsih_IssueTaskMgmt(hd, type, channel, target, lun, ctx2abort, timeout); | 
|  | if (rc) { | 
|  | printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name); | 
|  | } else { | 
|  | dtmprintk((MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Only fall through to the HRH if this is a bus reset | 
|  | */ | 
|  | if ((type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (rc || | 
|  | ioc->reload_fw || (ioc->alt_ioc && ioc->alt_ioc->reload_fw))) { | 
|  | dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n", | 
|  | hd->ioc->name)); | 
|  | rc = mpt_HardResetHandler(hd->ioc, CAN_SLEEP); | 
|  | } | 
|  |  | 
|  | dtmprintk((MYIOC_s_INFO_FMT "TMHandler rc = %d!\n", hd->ioc->name, rc)); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_IssueTaskMgmt - Generic send Task Management function. | 
|  | *	@hd: Pointer to MPT_SCSI_HOST structure | 
|  | *	@type: Task Management type | 
|  | *	@target: Logical Target ID for reset (if appropriate) | 
|  | *	@lun: Logical Unit for reset (if appropriate) | 
|  | *	@ctx2abort: Context for the task to be aborted (if appropriate) | 
|  | * | 
|  | *	Remark: _HardResetHandler can be invoked from an interrupt thread (timer) | 
|  | *	or a non-interrupt thread.  In the former, must not call schedule(). | 
|  | * | 
|  | *	Not all fields are meaningfull for all task types. | 
|  | * | 
|  | *	Returns 0 for SUCCESS, -999 for "no msg frames", | 
|  | *	else other non-zero value returned. | 
|  | */ | 
|  | static int | 
|  | mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout) | 
|  | { | 
|  | MPT_FRAME_HDR	*mf; | 
|  | SCSITaskMgmt_t	*pScsiTm; | 
|  | int		 ii; | 
|  | int		 retval; | 
|  |  | 
|  | /* Return Fail to calling function if no message frames available. | 
|  | */ | 
|  | if ((mf = mpt_get_msg_frame(hd->ioc->TaskCtx, hd->ioc)) == NULL) { | 
|  | dfailprintk((MYIOC_s_ERR_FMT "IssueTaskMgmt, no msg frames!!\n", | 
|  | hd->ioc->name)); | 
|  | return FAILED; | 
|  | } | 
|  | dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %p\n", | 
|  | hd->ioc->name, mf)); | 
|  |  | 
|  | /* Format the Request | 
|  | */ | 
|  | pScsiTm = (SCSITaskMgmt_t *) mf; | 
|  | pScsiTm->TargetID = target; | 
|  | pScsiTm->Bus = channel; | 
|  | pScsiTm->ChainOffset = 0; | 
|  | pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; | 
|  |  | 
|  | pScsiTm->Reserved = 0; | 
|  | pScsiTm->TaskType = type; | 
|  | pScsiTm->Reserved1 = 0; | 
|  | pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) | 
|  | ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0; | 
|  |  | 
|  | for (ii= 0; ii < 8; ii++) { | 
|  | pScsiTm->LUN[ii] = 0; | 
|  | } | 
|  | pScsiTm->LUN[1] = lun; | 
|  |  | 
|  | for (ii=0; ii < 7; ii++) | 
|  | pScsiTm->Reserved2[ii] = 0; | 
|  |  | 
|  | pScsiTm->TaskMsgContext = ctx2abort; | 
|  |  | 
|  | dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt: ctx2abort (0x%08x) type=%d\n", | 
|  | hd->ioc->name, ctx2abort, type)); | 
|  |  | 
|  | DBG_DUMP_TM_REQUEST_FRAME((u32 *)pScsiTm); | 
|  |  | 
|  | if ((retval = mpt_send_handshake_request(hd->ioc->TaskCtx, hd->ioc, | 
|  | sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, | 
|  | CAN_SLEEP)) != 0) { | 
|  | dfailprintk((MYIOC_s_ERR_FMT "_send_handshake FAILED!" | 
|  | " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd, | 
|  | hd->ioc, mf)); | 
|  | mpt_free_msg_frame(hd->ioc, mf); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | if(mptscsih_tm_wait_for_completion(hd, timeout) == FAILED) { | 
|  | dfailprintk((MYIOC_s_ERR_FMT "_wait_for_completion FAILED!" | 
|  | " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd, | 
|  | hd->ioc, mf)); | 
|  | mpt_free_msg_frame(hd->ioc, mf); | 
|  | dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n", | 
|  | hd->ioc->name)); | 
|  | retval = mpt_HardResetHandler(hd->ioc, CAN_SLEEP); | 
|  | } | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_abort - Abort linux scsi_cmnd routine, new_eh variant | 
|  | *	@SCpnt: Pointer to scsi_cmnd structure, IO to be aborted | 
|  | * | 
|  | *	(linux scsi_host_template.eh_abort_handler routine) | 
|  | * | 
|  | *	Returns SUCCESS or FAILED. | 
|  | */ | 
|  | int | 
|  | mptscsih_abort(struct scsi_cmnd * SCpnt) | 
|  | { | 
|  | MPT_SCSI_HOST	*hd; | 
|  | MPT_ADAPTER	*ioc; | 
|  | MPT_FRAME_HDR	*mf; | 
|  | u32		 ctx2abort; | 
|  | int		 scpnt_idx; | 
|  | int		 retval; | 
|  | VirtDevice	 *vdev; | 
|  |  | 
|  | /* If we can't locate our host adapter structure, return FAILED status. | 
|  | */ | 
|  | if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL) { | 
|  | SCpnt->result = DID_RESET << 16; | 
|  | SCpnt->scsi_done(SCpnt); | 
|  | dfailprintk((KERN_INFO MYNAM ": mptscsih_abort: " | 
|  | "Can't locate host! (sc=%p)\n", | 
|  | SCpnt)); | 
|  | return FAILED; | 
|  | } | 
|  |  | 
|  | ioc = hd->ioc; | 
|  | if (hd->resetPending) { | 
|  | return FAILED; | 
|  | } | 
|  |  | 
|  | if (hd->timeouts < -1) | 
|  | hd->timeouts++; | 
|  |  | 
|  | /* Find this command | 
|  | */ | 
|  | if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) { | 
|  | /* Cmd not found in ScsiLookup. | 
|  | * Do OS callback. | 
|  | */ | 
|  | SCpnt->result = DID_RESET << 16; | 
|  | dtmprintk((KERN_INFO MYNAM ": %s: mptscsih_abort: " | 
|  | "Command not in the active list! (sc=%p)\n", | 
|  | hd->ioc->name, SCpnt)); | 
|  | return SUCCESS; | 
|  | } | 
|  |  | 
|  | printk(KERN_WARNING MYNAM ": %s: attempting task abort! (sc=%p)\n", | 
|  | hd->ioc->name, SCpnt); | 
|  | scsi_print_command(SCpnt); | 
|  |  | 
|  | /* Most important!  Set TaskMsgContext to SCpnt's MsgContext! | 
|  | * (the IO to be ABORT'd) | 
|  | * | 
|  | * NOTE: Since we do not byteswap MsgContext, we do not | 
|  | *	 swap it here either.  It is an opaque cookie to | 
|  | *	 the controller, so it does not matter. -DaveM | 
|  | */ | 
|  | mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx); | 
|  | ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext; | 
|  |  | 
|  | hd->abortSCpnt = SCpnt; | 
|  |  | 
|  | vdev = SCpnt->device->hostdata; | 
|  | retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK, | 
|  | vdev->bus_id, vdev->target_id, vdev->lun, | 
|  | ctx2abort, 2 /* 2 second timeout */); | 
|  |  | 
|  | printk (KERN_WARNING MYNAM ": %s: task abort: %s (sc=%p)\n", | 
|  | hd->ioc->name, | 
|  | ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt); | 
|  |  | 
|  | if (retval == 0) | 
|  | return SUCCESS; | 
|  |  | 
|  | if(retval != FAILED ) { | 
|  | hd->tmPending = 0; | 
|  | hd->tmState = TM_STATE_NONE; | 
|  | } | 
|  | return FAILED; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_dev_reset - Perform a SCSI TARGET_RESET!  new_eh variant | 
|  | *	@SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to | 
|  | * | 
|  | *	(linux scsi_host_template.eh_dev_reset_handler routine) | 
|  | * | 
|  | *	Returns SUCCESS or FAILED. | 
|  | */ | 
|  | int | 
|  | mptscsih_dev_reset(struct scsi_cmnd * SCpnt) | 
|  | { | 
|  | MPT_SCSI_HOST	*hd; | 
|  | int		 retval; | 
|  | VirtDevice	 *vdev; | 
|  |  | 
|  | /* If we can't locate our host adapter structure, return FAILED status. | 
|  | */ | 
|  | if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){ | 
|  | dtmprintk((KERN_INFO MYNAM ": mptscsih_dev_reset: " | 
|  | "Can't locate host! (sc=%p)\n", | 
|  | SCpnt)); | 
|  | return FAILED; | 
|  | } | 
|  |  | 
|  | if (hd->resetPending) | 
|  | return FAILED; | 
|  |  | 
|  | printk(KERN_WARNING MYNAM ": %s: attempting target reset! (sc=%p)\n", | 
|  | hd->ioc->name, SCpnt); | 
|  | scsi_print_command(SCpnt); | 
|  |  | 
|  | vdev = SCpnt->device->hostdata; | 
|  | retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, | 
|  | vdev->bus_id, vdev->target_id, | 
|  | 0, 0, 5 /* 5 second timeout */); | 
|  |  | 
|  | printk (KERN_WARNING MYNAM ": %s: target reset: %s (sc=%p)\n", | 
|  | hd->ioc->name, | 
|  | ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt); | 
|  |  | 
|  | if (retval == 0) | 
|  | return SUCCESS; | 
|  |  | 
|  | if(retval != FAILED ) { | 
|  | hd->tmPending = 0; | 
|  | hd->tmState = TM_STATE_NONE; | 
|  | } | 
|  | return FAILED; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_bus_reset - Perform a SCSI BUS_RESET!	new_eh variant | 
|  | *	@SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to | 
|  | * | 
|  | *	(linux scsi_host_template.eh_bus_reset_handler routine) | 
|  | * | 
|  | *	Returns SUCCESS or FAILED. | 
|  | */ | 
|  | int | 
|  | mptscsih_bus_reset(struct scsi_cmnd * SCpnt) | 
|  | { | 
|  | MPT_SCSI_HOST	*hd; | 
|  | int		 retval; | 
|  | VirtDevice	 *vdev; | 
|  |  | 
|  | /* If we can't locate our host adapter structure, return FAILED status. | 
|  | */ | 
|  | if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){ | 
|  | dtmprintk((KERN_INFO MYNAM ": mptscsih_bus_reset: " | 
|  | "Can't locate host! (sc=%p)\n", | 
|  | SCpnt ) ); | 
|  | return FAILED; | 
|  | } | 
|  |  | 
|  | printk(KERN_WARNING MYNAM ": %s: attempting bus reset! (sc=%p)\n", | 
|  | hd->ioc->name, SCpnt); | 
|  | scsi_print_command(SCpnt); | 
|  |  | 
|  | if (hd->timeouts < -1) | 
|  | hd->timeouts++; | 
|  |  | 
|  | vdev = SCpnt->device->hostdata; | 
|  | retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, | 
|  | vdev->bus_id, 0, 0, 0, 5 /* 5 second timeout */); | 
|  |  | 
|  | printk (KERN_WARNING MYNAM ": %s: bus reset: %s (sc=%p)\n", | 
|  | hd->ioc->name, | 
|  | ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt); | 
|  |  | 
|  | if (retval == 0) | 
|  | return SUCCESS; | 
|  |  | 
|  | if(retval != FAILED ) { | 
|  | hd->tmPending = 0; | 
|  | hd->tmState = TM_STATE_NONE; | 
|  | } | 
|  | return FAILED; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_host_reset - Perform a SCSI host adapter RESET! | 
|  | *	new_eh variant | 
|  | *	@SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to | 
|  | * | 
|  | *	(linux scsi_host_template.eh_host_reset_handler routine) | 
|  | * | 
|  | *	Returns SUCCESS or FAILED. | 
|  | */ | 
|  | int | 
|  | mptscsih_host_reset(struct scsi_cmnd *SCpnt) | 
|  | { | 
|  | MPT_SCSI_HOST *  hd; | 
|  | int              status = SUCCESS; | 
|  |  | 
|  | /*  If we can't locate the host to reset, then we failed. */ | 
|  | if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){ | 
|  | dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: " | 
|  | "Can't locate host! (sc=%p)\n", | 
|  | SCpnt ) ); | 
|  | return FAILED; | 
|  | } | 
|  |  | 
|  | printk(KERN_WARNING MYNAM ": %s: Attempting host reset! (sc=%p)\n", | 
|  | hd->ioc->name, SCpnt); | 
|  |  | 
|  | /*  If our attempts to reset the host failed, then return a failed | 
|  | *  status.  The host will be taken off line by the SCSI mid-layer. | 
|  | */ | 
|  | if (mpt_HardResetHandler(hd->ioc, CAN_SLEEP) < 0){ | 
|  | status = FAILED; | 
|  | } else { | 
|  | /*  Make sure TM pending is cleared and TM state is set to | 
|  | *  NONE. | 
|  | */ | 
|  | hd->tmPending = 0; | 
|  | hd->tmState = TM_STATE_NONE; | 
|  | } | 
|  |  | 
|  | dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: " | 
|  | "Status = %s\n", | 
|  | (status == SUCCESS) ? "SUCCESS" : "FAILED" ) ); | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_tm_pending_wait - wait for pending task management request to | 
|  | *		complete. | 
|  | *	@hd: Pointer to MPT host structure. | 
|  | * | 
|  | *	Returns {SUCCESS,FAILED}. | 
|  | */ | 
|  | static int | 
|  | mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd) | 
|  | { | 
|  | unsigned long  flags; | 
|  | int            loop_count = 4 * 10;  /* Wait 10 seconds */ | 
|  | int            status = FAILED; | 
|  |  | 
|  | do { | 
|  | spin_lock_irqsave(&hd->ioc->FreeQlock, flags); | 
|  | if (hd->tmState == TM_STATE_NONE) { | 
|  | hd->tmState = TM_STATE_IN_PROGRESS; | 
|  | hd->tmPending = 1; | 
|  | spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); | 
|  | status = SUCCESS; | 
|  | break; | 
|  | } | 
|  | spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); | 
|  | msleep(250); | 
|  | } while (--loop_count); | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_tm_wait_for_completion - wait for completion of TM task | 
|  | *	@hd: Pointer to MPT host structure. | 
|  | * | 
|  | *	Returns {SUCCESS,FAILED}. | 
|  | */ | 
|  | static int | 
|  | mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout ) | 
|  | { | 
|  | unsigned long  flags; | 
|  | int            loop_count = 4 * timeout; | 
|  | int            status = FAILED; | 
|  |  | 
|  | do { | 
|  | spin_lock_irqsave(&hd->ioc->FreeQlock, flags); | 
|  | if(hd->tmPending == 0) { | 
|  | status = SUCCESS; | 
|  | spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); | 
|  | break; | 
|  | } | 
|  | spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); | 
|  | msleep_interruptible(250); | 
|  | } while (--loop_count); | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_taskmgmt_complete - Registered with Fusion MPT base driver | 
|  | *	@ioc: Pointer to MPT_ADAPTER structure | 
|  | *	@mf: Pointer to SCSI task mgmt request frame | 
|  | *	@mr: Pointer to SCSI task mgmt reply frame | 
|  | * | 
|  | *	This routine is called from mptbase.c::mpt_interrupt() at the completion | 
|  | *	of any SCSI task management request. | 
|  | *	This routine is registered with the MPT (base) driver at driver | 
|  | *	load/init time via the mpt_register() API call. | 
|  | * | 
|  | *	Returns 1 indicating alloc'd request frame ptr should be freed. | 
|  | */ | 
|  | int | 
|  | mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) | 
|  | { | 
|  | SCSITaskMgmtReply_t	*pScsiTmReply; | 
|  | SCSITaskMgmt_t		*pScsiTmReq; | 
|  | MPT_SCSI_HOST		*hd; | 
|  | unsigned long		 flags; | 
|  | u16			 iocstatus; | 
|  | u8			 tmType; | 
|  |  | 
|  | dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt completed (mf=%p,mr=%p)\n", | 
|  | ioc->name, mf, mr)); | 
|  | if (ioc->sh) { | 
|  | /* Depending on the thread, a timer is activated for | 
|  | * the TM request.  Delete this timer on completion of TM. | 
|  | * Decrement count of outstanding TM requests. | 
|  | */ | 
|  | hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; | 
|  | } else { | 
|  | dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptr\n", | 
|  | ioc->name)); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (mr == NULL) { | 
|  | dtmprintk((MYIOC_s_WARN_FMT "ERROR! TaskMgmt Reply: NULL Request %p\n", | 
|  | ioc->name, mf)); | 
|  | return 1; | 
|  | } else { | 
|  | pScsiTmReply = (SCSITaskMgmtReply_t*)mr; | 
|  | pScsiTmReq = (SCSITaskMgmt_t*)mf; | 
|  |  | 
|  | /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */ | 
|  | tmType = pScsiTmReq->TaskType; | 
|  |  | 
|  | dtmprintk((MYIOC_s_WARN_FMT "  TaskType = %d, TerminationCount=%d\n", | 
|  | ioc->name, tmType, le32_to_cpu(pScsiTmReply->TerminationCount))); | 
|  | DBG_DUMP_TM_REPLY_FRAME((u32 *)pScsiTmReply); | 
|  |  | 
|  | iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK; | 
|  | dtmprintk((MYIOC_s_WARN_FMT "  SCSI TaskMgmt (%d) IOCStatus=%04x IOCLogInfo=%08x\n", | 
|  | ioc->name, tmType, iocstatus, le32_to_cpu(pScsiTmReply->IOCLogInfo))); | 
|  | /* Error?  (anything non-zero?) */ | 
|  | if (iocstatus) { | 
|  |  | 
|  | /* clear flags and continue. | 
|  | */ | 
|  | if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) | 
|  | hd->abortSCpnt = NULL; | 
|  |  | 
|  | /* If an internal command is present | 
|  | * or the TM failed - reload the FW. | 
|  | * FC FW may respond FAILED to an ABORT | 
|  | */ | 
|  | if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) { | 
|  | if ((hd->cmdPtr) || | 
|  | (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED)) { | 
|  | if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) { | 
|  | printk((KERN_WARNING | 
|  | " Firmware Reload FAILED!!\n")); | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | dtmprintk((MYIOC_s_WARN_FMT " TaskMgmt SUCCESS\n", ioc->name)); | 
|  |  | 
|  | hd->abortSCpnt = NULL; | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | spin_lock_irqsave(&ioc->FreeQlock, flags); | 
|  | hd->tmPending = 0; | 
|  | spin_unlock_irqrestore(&ioc->FreeQlock, flags); | 
|  | hd->tmState = TM_STATE_NONE; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	This is anyones guess quite frankly. | 
|  | */ | 
|  | int | 
|  | mptscsih_bios_param(struct scsi_device * sdev, struct block_device *bdev, | 
|  | sector_t capacity, int geom[]) | 
|  | { | 
|  | int		heads; | 
|  | int		sectors; | 
|  | sector_t	cylinders; | 
|  | ulong 		dummy; | 
|  |  | 
|  | heads = 64; | 
|  | sectors = 32; | 
|  |  | 
|  | dummy = heads * sectors; | 
|  | cylinders = capacity; | 
|  | sector_div(cylinders,dummy); | 
|  |  | 
|  | /* | 
|  | * Handle extended translation size for logical drives | 
|  | * > 1Gb | 
|  | */ | 
|  | if ((ulong)capacity >= 0x200000) { | 
|  | heads = 255; | 
|  | sectors = 63; | 
|  | dummy = heads * sectors; | 
|  | cylinders = capacity; | 
|  | sector_div(cylinders,dummy); | 
|  | } | 
|  |  | 
|  | /* return result */ | 
|  | geom[0] = heads; | 
|  | geom[1] = sectors; | 
|  | geom[2] = cylinders; | 
|  |  | 
|  | dprintk((KERN_NOTICE | 
|  | ": bios_param: Id=%i Lun=%i Channel=%i CHS=%i/%i/%i\n", | 
|  | sdev->id, sdev->lun,sdev->channel,(int)cylinders,heads,sectors)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	OS entry point to allow host driver to alloc memory | 
|  | *	for each scsi target. Called once per device the bus scan. | 
|  | *	Return non-zero if allocation fails. | 
|  | */ | 
|  | int | 
|  | mptscsih_target_alloc(struct scsi_target *starget) | 
|  | { | 
|  | VirtTarget		*vtarget; | 
|  |  | 
|  | vtarget = kmalloc(sizeof(VirtTarget), GFP_KERNEL); | 
|  | if (!vtarget) | 
|  | return -ENOMEM; | 
|  | memset(vtarget, 0, sizeof(VirtTarget)); | 
|  | starget->hostdata = vtarget; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	OS entry point to allow host driver to alloc memory | 
|  | *	for each scsi device. Called once per device the bus scan. | 
|  | *	Return non-zero if allocation fails. | 
|  | */ | 
|  | int | 
|  | mptscsih_slave_alloc(struct scsi_device *sdev) | 
|  | { | 
|  | struct Scsi_Host	*host = sdev->host; | 
|  | MPT_SCSI_HOST		*hd = (MPT_SCSI_HOST *)host->hostdata; | 
|  | VirtTarget		*vtarget; | 
|  | VirtDevice		*vdev; | 
|  | struct scsi_target 	*starget; | 
|  |  | 
|  | vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL); | 
|  | if (!vdev) { | 
|  | printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n", | 
|  | hd->ioc->name, sizeof(VirtDevice)); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | memset(vdev, 0, sizeof(VirtDevice)); | 
|  | vdev->ioc_id = hd->ioc->id; | 
|  | vdev->target_id = sdev->id; | 
|  | vdev->bus_id = sdev->channel; | 
|  | vdev->lun = sdev->lun; | 
|  | sdev->hostdata = vdev; | 
|  |  | 
|  | starget = scsi_target(sdev); | 
|  | vtarget = starget->hostdata; | 
|  | vdev->vtarget = vtarget; | 
|  |  | 
|  | if (vtarget->num_luns == 0) { | 
|  | hd->Targets[sdev->id] = vtarget; | 
|  | vtarget->ioc_id = hd->ioc->id; | 
|  | vtarget->tflags = MPT_TARGET_FLAGS_Q_YES; | 
|  | vtarget->target_id = sdev->id; | 
|  | vtarget->bus_id = sdev->channel; | 
|  | if (hd->ioc->bus_type == SPI) { | 
|  | if (hd->ioc->raid_data.isRaid & (1 << sdev->id)) { | 
|  | vtarget->raidVolume = 1; | 
|  | ddvtprintk((KERN_INFO | 
|  | "RAID Volume @ id %d\n", sdev->id)); | 
|  | } | 
|  | } else { | 
|  | vtarget->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; | 
|  | } | 
|  | } | 
|  | vtarget->num_luns++; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	OS entry point to allow for host driver to free allocated memory | 
|  | *	Called if no device present or device being unloaded | 
|  | */ | 
|  | void | 
|  | mptscsih_target_destroy(struct scsi_target *starget) | 
|  | { | 
|  | if (starget->hostdata) | 
|  | kfree(starget->hostdata); | 
|  | starget->hostdata = NULL; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	OS entry point to allow for host driver to free allocated memory | 
|  | *	Called if no device present or device being unloaded | 
|  | */ | 
|  | void | 
|  | mptscsih_slave_destroy(struct scsi_device *sdev) | 
|  | { | 
|  | struct Scsi_Host	*host = sdev->host; | 
|  | MPT_SCSI_HOST		*hd = (MPT_SCSI_HOST *)host->hostdata; | 
|  | VirtTarget		*vtarget; | 
|  | VirtDevice		*vdevice; | 
|  | struct scsi_target 	*starget; | 
|  |  | 
|  | starget = scsi_target(sdev); | 
|  | vtarget = starget->hostdata; | 
|  | vdevice = sdev->hostdata; | 
|  |  | 
|  | mptscsih_search_running_cmds(hd, vdevice); | 
|  | vtarget->luns[0] &= ~(1 << vdevice->lun); | 
|  | vtarget->num_luns--; | 
|  | if (vtarget->num_luns == 0) { | 
|  | mptscsih_negotiate_to_asyn_narrow(hd, vtarget); | 
|  | if (hd->ioc->bus_type == SPI) { | 
|  | if (mptscsih_is_phys_disk(hd->ioc, vtarget->target_id)) { | 
|  | hd->ioc->spi_data.forceDv |= MPT_SCSICFG_RELOAD_IOC_PG3; | 
|  | } else { | 
|  | hd->ioc->spi_data.dvStatus[vtarget->target_id] = | 
|  | MPT_SCSICFG_NEGOTIATE; | 
|  | if (!hd->negoNvram) { | 
|  | hd->ioc->spi_data.dvStatus[vtarget->target_id] |= | 
|  | MPT_SCSICFG_DV_NOT_DONE; | 
|  | } | 
|  | } | 
|  | } | 
|  | hd->Targets[sdev->id] = NULL; | 
|  | } | 
|  | mptscsih_synchronize_cache(hd, vdevice); | 
|  | kfree(vdevice); | 
|  | sdev->hostdata = NULL; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_change_queue_depth - This function will set a devices queue depth | 
|  | *	@sdev: per scsi_device pointer | 
|  | *	@qdepth: requested queue depth | 
|  | * | 
|  | *	Adding support for new 'change_queue_depth' api. | 
|  | */ | 
|  | int | 
|  | mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth) | 
|  | { | 
|  | MPT_SCSI_HOST		*hd = (MPT_SCSI_HOST *)sdev->host->hostdata; | 
|  | VirtTarget 		*vtarget; | 
|  | struct scsi_target 	*starget; | 
|  | int			max_depth; | 
|  | int			tagged; | 
|  |  | 
|  | starget = scsi_target(sdev); | 
|  | vtarget = starget->hostdata; | 
|  |  | 
|  | if (hd->ioc->bus_type == SPI) { | 
|  | if (vtarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { | 
|  | if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)) | 
|  | max_depth = 1; | 
|  | else if (((vtarget->inq_data[0] & 0x1f) == 0x00) && | 
|  | (vtarget->minSyncFactor <= MPT_ULTRA160 )) | 
|  | max_depth = MPT_SCSI_CMD_PER_DEV_HIGH; | 
|  | else | 
|  | max_depth = MPT_SCSI_CMD_PER_DEV_LOW; | 
|  | } else { | 
|  | /* error case - No Inq. Data */ | 
|  | max_depth = 1; | 
|  | } | 
|  | } else | 
|  | max_depth = MPT_SCSI_CMD_PER_DEV_HIGH; | 
|  |  | 
|  | if (qdepth > max_depth) | 
|  | qdepth = max_depth; | 
|  | if (qdepth == 1) | 
|  | tagged = 0; | 
|  | else | 
|  | tagged = MSG_SIMPLE_TAG; | 
|  |  | 
|  | scsi_adjust_queue_depth(sdev, tagged, qdepth); | 
|  | return sdev->queue_depth; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	OS entry point to adjust the queue_depths on a per-device basis. | 
|  | *	Called once per device the bus scan. Use it to force the queue_depth | 
|  | *	member to 1 if a device does not support Q tags. | 
|  | *	Return non-zero if fails. | 
|  | */ | 
|  | int | 
|  | mptscsih_slave_configure(struct scsi_device *sdev) | 
|  | { | 
|  | struct Scsi_Host	*sh = sdev->host; | 
|  | VirtTarget		*vtarget; | 
|  | VirtDevice		*vdevice; | 
|  | struct scsi_target 	*starget; | 
|  | MPT_SCSI_HOST		*hd = (MPT_SCSI_HOST *)sh->hostdata; | 
|  | int			indexed_lun, lun_index; | 
|  |  | 
|  | starget = scsi_target(sdev); | 
|  | vtarget = starget->hostdata; | 
|  | vdevice = sdev->hostdata; | 
|  |  | 
|  | dsprintk((MYIOC_s_INFO_FMT | 
|  | "device @ %p, id=%d, LUN=%d, channel=%d\n", | 
|  | hd->ioc->name, sdev, sdev->id, sdev->lun, sdev->channel)); | 
|  | if (hd->ioc->bus_type == SPI) | 
|  | dsprintk((MYIOC_s_INFO_FMT | 
|  | "sdtr %d wdtr %d ppr %d inq length=%d\n", | 
|  | hd->ioc->name, sdev->sdtr, sdev->wdtr, | 
|  | sdev->ppr, sdev->inquiry_len)); | 
|  |  | 
|  | if (sdev->id > sh->max_id) { | 
|  | /* error case, should never happen */ | 
|  | scsi_adjust_queue_depth(sdev, 0, 1); | 
|  | goto slave_configure_exit; | 
|  | } | 
|  |  | 
|  | vdevice->configured_lun=1; | 
|  | lun_index = (vdevice->lun >> 5);  /* 32 luns per lun_index */ | 
|  | indexed_lun = (vdevice->lun % 32); | 
|  | vtarget->luns[lun_index] |= (1 << indexed_lun); | 
|  | mptscsih_initTarget(hd, vtarget, sdev->lun, sdev->inquiry, | 
|  | sdev->inquiry_len ); | 
|  | mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH); | 
|  |  | 
|  | dsprintk((MYIOC_s_INFO_FMT | 
|  | "Queue depth=%d, tflags=%x\n", | 
|  | hd->ioc->name, sdev->queue_depth, vtarget->tflags)); | 
|  |  | 
|  | if (hd->ioc->bus_type == SPI) | 
|  | dsprintk((MYIOC_s_INFO_FMT | 
|  | "negoFlags=%x, maxOffset=%x, SyncFactor=%x\n", | 
|  | hd->ioc->name, vtarget->negoFlags, vtarget->maxOffset, | 
|  | vtarget->minSyncFactor)); | 
|  |  | 
|  | slave_configure_exit: | 
|  |  | 
|  | dsprintk((MYIOC_s_INFO_FMT | 
|  | "tagged %d, simple %d, ordered %d\n", | 
|  | hd->ioc->name,sdev->tagged_supported, sdev->simple_tags, | 
|  | sdev->ordered_tags)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *  Private routines... | 
|  | */ | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* Utility function to copy sense data from the scsi_cmnd buffer | 
|  | * to the FC and SCSI target structures. | 
|  | * | 
|  | */ | 
|  | static void | 
|  | mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply) | 
|  | { | 
|  | VirtDevice	*vdev; | 
|  | SCSIIORequest_t	*pReq; | 
|  | u32		 sense_count = le32_to_cpu(pScsiReply->SenseCount); | 
|  |  | 
|  | /* Get target structure | 
|  | */ | 
|  | pReq = (SCSIIORequest_t *) mf; | 
|  | vdev = sc->device->hostdata; | 
|  |  | 
|  | if (sense_count) { | 
|  | u8 *sense_data; | 
|  | int req_index; | 
|  |  | 
|  | /* Copy the sense received into the scsi command block. */ | 
|  | req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); | 
|  | sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC)); | 
|  | memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc)); | 
|  |  | 
|  | /* Log SMART data (asc = 0x5D, non-IM case only) if required. | 
|  | */ | 
|  | if ((hd->ioc->events) && (hd->ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) { | 
|  | if ((sense_data[12] == 0x5D) && (vdev->vtarget->raidVolume == 0)) { | 
|  | int idx; | 
|  | MPT_ADAPTER *ioc = hd->ioc; | 
|  |  | 
|  | idx = ioc->eventContext % ioc->eventLogSize; | 
|  | ioc->events[idx].event = MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE; | 
|  | ioc->events[idx].eventContext = ioc->eventContext; | 
|  |  | 
|  | ioc->events[idx].data[0] = (pReq->LUN[1] << 24) || | 
|  | (MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA << 16) || | 
|  | (sc->device->channel << 8) || sc->device->id; | 
|  |  | 
|  | ioc->events[idx].data[1] = (sense_data[13] << 8) || sense_data[12]; | 
|  |  | 
|  | ioc->eventContext++; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | dprintk((MYIOC_s_INFO_FMT "Hmmm... SenseData len=0! (?)\n", | 
|  | hd->ioc->name)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static u32 | 
|  | SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc) | 
|  | { | 
|  | MPT_SCSI_HOST *hd; | 
|  | int i; | 
|  |  | 
|  | hd = (MPT_SCSI_HOST *) sc->device->host->hostdata; | 
|  |  | 
|  | for (i = 0; i < hd->ioc->req_depth; i++) { | 
|  | if (hd->ScsiLookup[i] == sc) { | 
|  | return i; | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | int | 
|  | mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) | 
|  | { | 
|  | MPT_SCSI_HOST	*hd; | 
|  | unsigned long	 flags; | 
|  | int 		ii; | 
|  |  | 
|  | dtmprintk((KERN_WARNING MYNAM | 
|  | ": IOC %s_reset routed to SCSI host driver!\n", | 
|  | reset_phase==MPT_IOC_SETUP_RESET ? "setup" : ( | 
|  | reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"))); | 
|  |  | 
|  | /* If a FW reload request arrives after base installed but | 
|  | * before all scsi hosts have been attached, then an alt_ioc | 
|  | * may have a NULL sh pointer. | 
|  | */ | 
|  | if ((ioc->sh == NULL) || (ioc->sh->hostdata == NULL)) | 
|  | return 0; | 
|  | else | 
|  | hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; | 
|  |  | 
|  | if (reset_phase == MPT_IOC_SETUP_RESET) { | 
|  | dtmprintk((MYIOC_s_WARN_FMT "Setup-Diag Reset\n", ioc->name)); | 
|  |  | 
|  | /* Clean Up: | 
|  | * 1. Set Hard Reset Pending Flag | 
|  | * All new commands go to doneQ | 
|  | */ | 
|  | hd->resetPending = 1; | 
|  |  | 
|  | } else if (reset_phase == MPT_IOC_PRE_RESET) { | 
|  | dtmprintk((MYIOC_s_WARN_FMT "Pre-Diag Reset\n", ioc->name)); | 
|  |  | 
|  | /* 2. Flush running commands | 
|  | *	Clean ScsiLookup (and associated memory) | 
|  | *	AND clean mytaskQ | 
|  | */ | 
|  |  | 
|  | /* 2b. Reply to OS all known outstanding I/O commands. | 
|  | */ | 
|  | mptscsih_flush_running_cmds(hd); | 
|  |  | 
|  | /* 2c. If there was an internal command that | 
|  | * has not completed, configuration or io request, | 
|  | * free these resources. | 
|  | */ | 
|  | if (hd->cmdPtr) { | 
|  | del_timer(&hd->timer); | 
|  | mpt_free_msg_frame(ioc, hd->cmdPtr); | 
|  | } | 
|  |  | 
|  | dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset complete.\n", ioc->name)); | 
|  |  | 
|  | } else { | 
|  | dtmprintk((MYIOC_s_WARN_FMT "Post-Diag Reset\n", ioc->name)); | 
|  |  | 
|  | /* Once a FW reload begins, all new OS commands are | 
|  | * redirected to the doneQ w/ a reset status. | 
|  | * Init all control structures. | 
|  | */ | 
|  |  | 
|  | /* ScsiLookup initialization | 
|  | */ | 
|  | for (ii=0; ii < hd->ioc->req_depth; ii++) | 
|  | hd->ScsiLookup[ii] = NULL; | 
|  |  | 
|  | /* 2. Chain Buffer initialization | 
|  | */ | 
|  |  | 
|  | /* 4. Renegotiate to all devices, if SPI | 
|  | */ | 
|  | if (ioc->bus_type == SPI) { | 
|  | dnegoprintk(("writeSDP1: ALL_IDS USE_NVRAM\n")); | 
|  | mptscsih_writeSDP1(hd, 0, 0, MPT_SCSICFG_ALL_IDS | MPT_SCSICFG_USE_NVRAM); | 
|  | } | 
|  |  | 
|  | /* 5. Enable new commands to be posted | 
|  | */ | 
|  | spin_lock_irqsave(&ioc->FreeQlock, flags); | 
|  | hd->tmPending = 0; | 
|  | spin_unlock_irqrestore(&ioc->FreeQlock, flags); | 
|  | hd->resetPending = 0; | 
|  | hd->tmState = TM_STATE_NONE; | 
|  |  | 
|  | /* 6. If there was an internal command, | 
|  | * wake this process up. | 
|  | */ | 
|  | if (hd->cmdPtr) { | 
|  | /* | 
|  | * Wake up the original calling thread | 
|  | */ | 
|  | hd->pLocal = &hd->localReply; | 
|  | hd->pLocal->completion = MPT_SCANDV_DID_RESET; | 
|  | hd->scandv_wait_done = 1; | 
|  | wake_up(&hd->scandv_waitq); | 
|  | hd->cmdPtr = NULL; | 
|  | } | 
|  |  | 
|  | /* 7. Set flag to force DV and re-read IOC Page 3 | 
|  | */ | 
|  | if (ioc->bus_type == SPI) { | 
|  | ioc->spi_data.forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3; | 
|  | ddvtprintk(("Set reload IOC Pg3 Flag\n")); | 
|  | } | 
|  |  | 
|  | dtmprintk((MYIOC_s_WARN_FMT "Post-Reset complete.\n", ioc->name)); | 
|  |  | 
|  | } | 
|  |  | 
|  | return 1;		/* currently means nothing really */ | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* work queue thread to clear the persitency table */ | 
|  | static void | 
|  | mptscsih_sas_persist_clear_table(void * arg) | 
|  | { | 
|  | MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg; | 
|  |  | 
|  | mptbase_sas_persist_operation(ioc, MPI_SAS_OP_CLEAR_NOT_PRESENT); | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | int | 
|  | mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) | 
|  | { | 
|  | MPT_SCSI_HOST *hd; | 
|  | u8 event = le32_to_cpu(pEvReply->Event) & 0xFF; | 
|  |  | 
|  | devtprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n", | 
|  | ioc->name, event)); | 
|  |  | 
|  | if (ioc->sh == NULL || | 
|  | ((hd = (MPT_SCSI_HOST *)ioc->sh->hostdata) == NULL)) | 
|  | return 1; | 
|  |  | 
|  | switch (event) { | 
|  | case MPI_EVENT_UNIT_ATTENTION:			/* 03 */ | 
|  | /* FIXME! */ | 
|  | break; | 
|  | case MPI_EVENT_IOC_BUS_RESET:			/* 04 */ | 
|  | case MPI_EVENT_EXT_BUS_RESET:			/* 05 */ | 
|  | if (hd && (ioc->bus_type == SPI) && (hd->soft_resets < -1)) | 
|  | hd->soft_resets++; | 
|  | break; | 
|  | case MPI_EVENT_LOGOUT:				/* 09 */ | 
|  | /* FIXME! */ | 
|  | break; | 
|  |  | 
|  | /* | 
|  | *  CHECKME! Don't think we need to do | 
|  | *  anything for these, but... | 
|  | */ | 
|  | case MPI_EVENT_RESCAN:				/* 06 */ | 
|  | case MPI_EVENT_LINK_STATUS_CHANGE:		/* 07 */ | 
|  | case MPI_EVENT_LOOP_STATE_CHANGE:		/* 08 */ | 
|  | /* | 
|  | *  CHECKME!  Falling thru... | 
|  | */ | 
|  | break; | 
|  |  | 
|  | case MPI_EVENT_INTEGRATED_RAID:			/* 0B */ | 
|  | { | 
|  | #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION | 
|  | pMpiEventDataRaid_t pRaidEventData = | 
|  | (pMpiEventDataRaid_t) pEvReply->Data; | 
|  | /* Domain Validation Needed */ | 
|  | if (ioc->bus_type == SPI && | 
|  | pRaidEventData->ReasonCode == | 
|  | MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED) | 
|  | mptscsih_set_dvflags_raid(hd, pRaidEventData->PhysDiskNum); | 
|  | #endif | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Persistent table is full. */ | 
|  | case MPI_EVENT_PERSISTENT_TABLE_FULL: | 
|  | INIT_WORK(&mptscsih_persistTask, | 
|  | mptscsih_sas_persist_clear_table,(void *)ioc); | 
|  | schedule_work(&mptscsih_persistTask); | 
|  | break; | 
|  |  | 
|  | case MPI_EVENT_NONE:				/* 00 */ | 
|  | case MPI_EVENT_LOG_DATA:			/* 01 */ | 
|  | case MPI_EVENT_STATE_CHANGE:			/* 02 */ | 
|  | case MPI_EVENT_EVENT_CHANGE:			/* 0A */ | 
|  | default: | 
|  | dprintk((KERN_INFO "  Ignoring event (=%02Xh)\n", event)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return 1;		/* currently means nothing really */ | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_initTarget - Target, LUN alloc/free functionality. | 
|  | *	@hd: Pointer to MPT_SCSI_HOST structure | 
|  | *	@vtarget: per target private data | 
|  | *	@lun: SCSI LUN id | 
|  | *	@data: Pointer to data | 
|  | *	@dlen: Number of INQUIRY bytes | 
|  | * | 
|  | *	NOTE: It's only SAFE to call this routine if data points to | 
|  | *	sane & valid STANDARD INQUIRY data! | 
|  | * | 
|  | *	Allocate and initialize memory for this target. | 
|  | *	Save inquiry data. | 
|  | * | 
|  | */ | 
|  | static void | 
|  | mptscsih_initTarget(MPT_SCSI_HOST *hd, VirtTarget *vtarget, u8 lun, char *data, int dlen) | 
|  | { | 
|  | SpiCfgData	*pSpi; | 
|  | char		data_56; | 
|  | int		inq_len; | 
|  |  | 
|  | dinitprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n", | 
|  | hd->ioc->name, vtarget->bus_id, vtarget->target_id, lun, hd)); | 
|  |  | 
|  | /* | 
|  | * If the peripheral qualifier filter is enabled then if the target reports a 0x1 | 
|  | * (i.e. The targer is capable of supporting the specified peripheral device type | 
|  | * on this logical unit; however, the physical device is not currently connected | 
|  | * to this logical unit) it will be converted to a 0x3 (i.e. The target is not | 
|  | * capable of supporting a physical device on this logical unit). This is to work | 
|  | * around a bug in th emid-layer in some distributions in which the mid-layer will | 
|  | * continue to try to communicate to the LUN and evntually create a dummy LUN. | 
|  | */ | 
|  | if (hd->mpt_pq_filter && dlen && (data[0] & 0xE0)) | 
|  | data[0] |= 0x40; | 
|  |  | 
|  | /* Is LUN supported? If so, upper 2 bits will be 0 | 
|  | * in first byte of inquiry data. | 
|  | */ | 
|  | if (data[0] & 0xe0) | 
|  | return; | 
|  |  | 
|  | if (vtarget == NULL) | 
|  | return; | 
|  |  | 
|  | if (data) | 
|  | vtarget->type = data[0]; | 
|  |  | 
|  | if (hd->ioc->bus_type != SPI) | 
|  | return; | 
|  |  | 
|  | if ((data[0] == TYPE_PROCESSOR) && (hd->ioc->spi_data.Saf_Te)) { | 
|  | /* Treat all Processors as SAF-TE if | 
|  | * command line option is set */ | 
|  | vtarget->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED; | 
|  | mptscsih_writeIOCPage4(hd, vtarget->target_id, vtarget->bus_id); | 
|  | }else if ((data[0] == TYPE_PROCESSOR) && | 
|  | !(vtarget->tflags & MPT_TARGET_FLAGS_SAF_TE_ISSUED )) { | 
|  | if ( dlen > 49 ) { | 
|  | vtarget->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; | 
|  | if ( data[44] == 'S' && | 
|  | data[45] == 'A' && | 
|  | data[46] == 'F' && | 
|  | data[47] == '-' && | 
|  | data[48] == 'T' && | 
|  | data[49] == 'E' ) { | 
|  | vtarget->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED; | 
|  | mptscsih_writeIOCPage4(hd, vtarget->target_id, vtarget->bus_id); | 
|  | } | 
|  | } | 
|  | } | 
|  | if (!(vtarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) { | 
|  | inq_len = dlen < 8 ? dlen : 8; | 
|  | memcpy (vtarget->inq_data, data, inq_len); | 
|  | /* If have not done DV, set the DV flag. | 
|  | */ | 
|  | pSpi = &hd->ioc->spi_data; | 
|  | if ((data[0] == TYPE_TAPE) || (data[0] == TYPE_PROCESSOR)) { | 
|  | if (pSpi->dvStatus[vtarget->target_id] & MPT_SCSICFG_DV_NOT_DONE) | 
|  | pSpi->dvStatus[vtarget->target_id] |= MPT_SCSICFG_NEED_DV; | 
|  | } | 
|  | vtarget->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; | 
|  |  | 
|  | data_56 = 0x0F;  /* Default to full capabilities if Inq data length is < 57 */ | 
|  | if (dlen > 56) { | 
|  | if ( (!(vtarget->tflags & MPT_TARGET_FLAGS_VALID_56))) { | 
|  | /* Update the target capabilities | 
|  | */ | 
|  | data_56 = data[56]; | 
|  | vtarget->tflags |= MPT_TARGET_FLAGS_VALID_56; | 
|  | } | 
|  | } | 
|  | mptscsih_setTargetNegoParms(hd, vtarget, data_56); | 
|  | } else { | 
|  | /* Initial Inquiry may not request enough data bytes to | 
|  | * obtain byte 57.  DV will; if target doesn't return | 
|  | * at least 57 bytes, data[56] will be zero. */ | 
|  | if (dlen > 56) { | 
|  | if ( (!(vtarget->tflags & MPT_TARGET_FLAGS_VALID_56))) { | 
|  | /* Update the target capabilities | 
|  | */ | 
|  | data_56 = data[56]; | 
|  | vtarget->tflags |= MPT_TARGET_FLAGS_VALID_56; | 
|  | mptscsih_setTargetNegoParms(hd, vtarget, data_56); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *  Update the target negotiation parameters based on the | 
|  | *  the Inquiry data, adapter capabilities, and NVRAM settings. | 
|  | * | 
|  | */ | 
|  | static void | 
|  | mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtTarget *target, char byte56) | 
|  | { | 
|  | SpiCfgData *pspi_data = &hd->ioc->spi_data; | 
|  | int  id = (int) target->target_id; | 
|  | int  nvram; | 
|  | VirtTarget	*vtarget; | 
|  | int ii; | 
|  | u8 width = MPT_NARROW; | 
|  | u8 factor = MPT_ASYNC; | 
|  | u8 offset = 0; | 
|  | u8 version, nfactor; | 
|  | u8 noQas = 1; | 
|  |  | 
|  | target->negoFlags = pspi_data->noQas; | 
|  |  | 
|  | /* noQas == 0 => device supports QAS. Need byte 56 of Inq to determine | 
|  | * support. If available, default QAS to off and allow enabling. | 
|  | * If not available, default QAS to on, turn off for non-disks. | 
|  | */ | 
|  |  | 
|  | /* Set flags based on Inquiry data | 
|  | */ | 
|  | version = target->inq_data[2] & 0x07; | 
|  | if (version < 2) { | 
|  | width = 0; | 
|  | factor = MPT_ULTRA2; | 
|  | offset = pspi_data->maxSyncOffset; | 
|  | target->tflags &= ~MPT_TARGET_FLAGS_Q_YES; | 
|  | } else { | 
|  | if (target->inq_data[7] & 0x20) { | 
|  | width = 1; | 
|  | } | 
|  |  | 
|  | if (target->inq_data[7] & 0x10) { | 
|  | factor = pspi_data->minSyncFactor; | 
|  | if (target->tflags & MPT_TARGET_FLAGS_VALID_56) { | 
|  | /* bits 2 & 3 show Clocking support */ | 
|  | if ((byte56 & 0x0C) == 0) | 
|  | factor = MPT_ULTRA2; | 
|  | else { | 
|  | if ((byte56 & 0x03) == 0) | 
|  | factor = MPT_ULTRA160; | 
|  | else { | 
|  | factor = MPT_ULTRA320; | 
|  | if (byte56 & 0x02) | 
|  | { | 
|  | ddvtprintk((KERN_INFO "Enabling QAS due to byte56=%02x on id=%d!\n", byte56, id)); | 
|  | noQas = 0; | 
|  | } | 
|  | if (target->inq_data[0] == TYPE_TAPE) { | 
|  | if (byte56 & 0x01) | 
|  | target->negoFlags |= MPT_TAPE_NEGO_IDP; | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | ddvtprintk((KERN_INFO "Enabling QAS on id=%d due to ~TARGET_FLAGS_VALID_56!\n", id)); | 
|  | noQas = 0; | 
|  | } | 
|  |  | 
|  | offset = pspi_data->maxSyncOffset; | 
|  |  | 
|  | /* If RAID, never disable QAS | 
|  | * else if non RAID, do not disable | 
|  | *   QAS if bit 1 is set | 
|  | * bit 1 QAS support, non-raid only | 
|  | * bit 0 IU support | 
|  | */ | 
|  | if (target->raidVolume == 1) { | 
|  | noQas = 0; | 
|  | } | 
|  | } else { | 
|  | factor = MPT_ASYNC; | 
|  | offset = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if ( (target->inq_data[7] & 0x02) == 0) { | 
|  | target->tflags &= ~MPT_TARGET_FLAGS_Q_YES; | 
|  | } | 
|  |  | 
|  | /* Update tflags based on NVRAM settings. (SCSI only) | 
|  | */ | 
|  | if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) { | 
|  | nvram = pspi_data->nvram[id]; | 
|  | nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8; | 
|  |  | 
|  | if (width) | 
|  | width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1; | 
|  |  | 
|  | if (offset > 0) { | 
|  | /* Ensure factor is set to the | 
|  | * maximum of: adapter, nvram, inquiry | 
|  | */ | 
|  | if (nfactor) { | 
|  | if (nfactor < pspi_data->minSyncFactor ) | 
|  | nfactor = pspi_data->minSyncFactor; | 
|  |  | 
|  | factor = max(factor, nfactor); | 
|  | if (factor == MPT_ASYNC) | 
|  | offset = 0; | 
|  | } else { | 
|  | offset = 0; | 
|  | factor = MPT_ASYNC; | 
|  | } | 
|  | } else { | 
|  | factor = MPT_ASYNC; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Make sure data is consistent | 
|  | */ | 
|  | if ((!width) && (factor < MPT_ULTRA2)) { | 
|  | factor = MPT_ULTRA2; | 
|  | } | 
|  |  | 
|  | /* Save the data to the target structure. | 
|  | */ | 
|  | target->minSyncFactor = factor; | 
|  | target->maxOffset = offset; | 
|  | target->maxWidth = width; | 
|  |  | 
|  | target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO; | 
|  |  | 
|  | /* Disable unused features. | 
|  | */ | 
|  | if (!width) | 
|  | target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE; | 
|  |  | 
|  | if (!offset) | 
|  | target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC; | 
|  |  | 
|  | if ( factor > MPT_ULTRA320 ) | 
|  | noQas = 0; | 
|  |  | 
|  | /* GEM, processor WORKAROUND | 
|  | */ | 
|  | if ((target->inq_data[0] == TYPE_PROCESSOR) || (target->inq_data[0] > 0x08)) { | 
|  | target->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC); | 
|  | pspi_data->dvStatus[id] |= MPT_SCSICFG_BLK_NEGO; | 
|  | } else { | 
|  | if (noQas && (pspi_data->noQas == 0)) { | 
|  | pspi_data->noQas |= MPT_TARGET_NO_NEGO_QAS; | 
|  | target->negoFlags |= MPT_TARGET_NO_NEGO_QAS; | 
|  |  | 
|  | /* Disable QAS in a mixed configuration case | 
|  | */ | 
|  |  | 
|  | ddvtprintk((KERN_INFO "Disabling QAS due to noQas=%02x on id=%d!\n", noQas, id)); | 
|  | for (ii = 0; ii < id; ii++) { | 
|  | if ( (vtarget = hd->Targets[ii]) ) { | 
|  | vtarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS; | 
|  | mptscsih_writeSDP1(hd, 0, ii, vtarget->negoFlags); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Write SDP1 on this I/O to this target */ | 
|  | if (pspi_data->dvStatus[id] & MPT_SCSICFG_NEGOTIATE) { | 
|  | ddvtprintk((KERN_INFO "MPT_SCSICFG_NEGOTIATE on id=%d!\n", id)); | 
|  | mptscsih_writeSDP1(hd, 0, id, hd->negoNvram); | 
|  | pspi_data->dvStatus[id] &= ~MPT_SCSICFG_NEGOTIATE; | 
|  | } else if (pspi_data->dvStatus[id] & MPT_SCSICFG_BLK_NEGO) { | 
|  | ddvtprintk((KERN_INFO "MPT_SCSICFG_BLK_NEGO on id=%d!\n", id)); | 
|  | mptscsih_writeSDP1(hd, 0, id, MPT_SCSICFG_BLK_NEGO); | 
|  | pspi_data->dvStatus[id] &= ~MPT_SCSICFG_BLK_NEGO; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | * If no Target, bus reset on 1st I/O. Set the flag to | 
|  | * prevent any future negotiations to this device. | 
|  | */ | 
|  | static void | 
|  | mptscsih_no_negotiate(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc) | 
|  | { | 
|  | VirtDevice	*vdev; | 
|  |  | 
|  | if ((vdev = sc->device->hostdata) != NULL) | 
|  | hd->ioc->spi_data.dvStatus[vdev->target_id] |= MPT_SCSICFG_BLK_NEGO; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *  SCSI Config Page functionality ... | 
|  | */ | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /*	mptscsih_setDevicePage1Flags  - add Requested and Configuration fields flags | 
|  | *	based on width, factor and offset parameters. | 
|  | *	@width: bus width | 
|  | *	@factor: sync factor | 
|  | *	@offset: sync offset | 
|  | *	@requestedPtr: pointer to requested values (updated) | 
|  | *	@configurationPtr: pointer to configuration values (updated) | 
|  | *	@flags: flags to block WDTR or SDTR negotiation | 
|  | * | 
|  | *	Return: None. | 
|  | * | 
|  | *	Remark: Called by writeSDP1 and _dv_params | 
|  | */ | 
|  | static void | 
|  | mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags) | 
|  | { | 
|  | u8 nowide = flags & MPT_TARGET_NO_NEGO_WIDE; | 
|  | u8 nosync = flags & MPT_TARGET_NO_NEGO_SYNC; | 
|  |  | 
|  | *configurationPtr = 0; | 
|  | *requestedPtr = width ? MPI_SCSIDEVPAGE1_RP_WIDE : 0; | 
|  | *requestedPtr |= (offset << 16) | (factor << 8); | 
|  |  | 
|  | if (width && offset && !nowide && !nosync) { | 
|  | if (factor < MPT_ULTRA160) { | 
|  | *requestedPtr |= (MPI_SCSIDEVPAGE1_RP_IU + MPI_SCSIDEVPAGE1_RP_DT); | 
|  | if ((flags & MPT_TARGET_NO_NEGO_QAS) == 0) | 
|  | *requestedPtr |= MPI_SCSIDEVPAGE1_RP_QAS; | 
|  | if (flags & MPT_TAPE_NEGO_IDP) | 
|  | *requestedPtr |= 0x08000000; | 
|  | } else if (factor < MPT_ULTRA2) { | 
|  | *requestedPtr |= MPI_SCSIDEVPAGE1_RP_DT; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (nowide) | 
|  | *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED; | 
|  |  | 
|  | if (nosync) | 
|  | *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED; | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /*	mptscsih_writeSDP1  - write SCSI Device Page 1 | 
|  | *	@hd: Pointer to a SCSI Host Strucutre | 
|  | *	@portnum: IOC port number | 
|  | *	@target_id: writeSDP1 for single ID | 
|  | *	@flags: MPT_SCSICFG_ALL_IDS, MPT_SCSICFG_USE_NVRAM, MPT_SCSICFG_BLK_NEGO | 
|  | * | 
|  | *	Return: -EFAULT if read of config page header fails | 
|  | *		or 0 if success. | 
|  | * | 
|  | *	Remark: If a target has been found, the settings from the | 
|  | *		target structure are used, else the device is set | 
|  | *		to async/narrow. | 
|  | * | 
|  | *	Remark: Called during init and after a FW reload. | 
|  | *	Remark: We do not wait for a return, write pages sequentially. | 
|  | */ | 
|  | static int | 
|  | mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target_id, int flags) | 
|  | { | 
|  | MPT_ADAPTER		*ioc = hd->ioc; | 
|  | Config_t		*pReq; | 
|  | SCSIDevicePage1_t	*pData; | 
|  | VirtTarget		*vtarget=NULL; | 
|  | MPT_FRAME_HDR		*mf; | 
|  | dma_addr_t		 dataDma; | 
|  | u16			 req_idx; | 
|  | u32			 frameOffset; | 
|  | u32			 requested, configuration, flagsLength; | 
|  | int			 ii, nvram; | 
|  | int			 id = 0, maxid = 0; | 
|  | u8			 width; | 
|  | u8			 factor; | 
|  | u8			 offset; | 
|  | u8			 bus = 0; | 
|  | u8			 negoFlags; | 
|  | u8			 maxwidth, maxoffset, maxfactor; | 
|  |  | 
|  | if (ioc->spi_data.sdp1length == 0) | 
|  | return 0; | 
|  |  | 
|  | if (flags & MPT_SCSICFG_ALL_IDS) { | 
|  | id = 0; | 
|  | maxid = ioc->sh->max_id - 1; | 
|  | } else if (ioc->sh) { | 
|  | id = target_id; | 
|  | maxid = min_t(int, id, ioc->sh->max_id - 1); | 
|  | } | 
|  |  | 
|  | for (; id <= maxid; id++) { | 
|  |  | 
|  | if (id == ioc->pfacts[portnum].PortSCSIID) | 
|  | continue; | 
|  |  | 
|  | /* Use NVRAM to get adapter and target maximums | 
|  | * Data over-riden by target structure information, if present | 
|  | */ | 
|  | maxwidth = ioc->spi_data.maxBusWidth; | 
|  | maxoffset = ioc->spi_data.maxSyncOffset; | 
|  | maxfactor = ioc->spi_data.minSyncFactor; | 
|  | if (ioc->spi_data.nvram && (ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) { | 
|  | nvram = ioc->spi_data.nvram[id]; | 
|  |  | 
|  | if (maxwidth) | 
|  | maxwidth = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1; | 
|  |  | 
|  | if (maxoffset > 0) { | 
|  | maxfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8; | 
|  | if (maxfactor == 0) { | 
|  | /* Key for async */ | 
|  | maxfactor = MPT_ASYNC; | 
|  | maxoffset = 0; | 
|  | } else if (maxfactor < ioc->spi_data.minSyncFactor) { | 
|  | maxfactor = ioc->spi_data.minSyncFactor; | 
|  | } | 
|  | } else | 
|  | maxfactor = MPT_ASYNC; | 
|  | } | 
|  |  | 
|  | /* Set the negotiation flags. | 
|  | */ | 
|  | negoFlags = ioc->spi_data.noQas; | 
|  | if (!maxwidth) | 
|  | negoFlags |= MPT_TARGET_NO_NEGO_WIDE; | 
|  |  | 
|  | if (!maxoffset) | 
|  | negoFlags |= MPT_TARGET_NO_NEGO_SYNC; | 
|  |  | 
|  | if (flags & MPT_SCSICFG_USE_NVRAM) { | 
|  | width = maxwidth; | 
|  | factor = maxfactor; | 
|  | offset = maxoffset; | 
|  | } else { | 
|  | width = 0; | 
|  | factor = MPT_ASYNC; | 
|  | offset = 0; | 
|  | //negoFlags = 0; | 
|  | //negoFlags = MPT_TARGET_NO_NEGO_SYNC; | 
|  | } | 
|  |  | 
|  | /* If id is not a raid volume, get the updated | 
|  | * transmission settings from the target structure. | 
|  | */ | 
|  | if (hd->Targets && (vtarget = hd->Targets[id]) && !vtarget->raidVolume) { | 
|  | width = vtarget->maxWidth; | 
|  | factor = vtarget->minSyncFactor; | 
|  | offset = vtarget->maxOffset; | 
|  | negoFlags = vtarget->negoFlags; | 
|  | } | 
|  |  | 
|  | #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION | 
|  | /* Force to async and narrow if DV has not been executed | 
|  | * for this ID | 
|  | */ | 
|  | if ((hd->ioc->spi_data.dvStatus[id] & MPT_SCSICFG_DV_NOT_DONE) != 0) { | 
|  | width = 0; | 
|  | factor = MPT_ASYNC; | 
|  | offset = 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (flags & MPT_SCSICFG_BLK_NEGO) | 
|  | negoFlags |= MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC; | 
|  |  | 
|  | mptscsih_setDevicePage1Flags(width, factor, offset, | 
|  | &requested, &configuration, negoFlags); | 
|  | dnegoprintk(("writeSDP1: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n", | 
|  | target_id, width, factor, offset, negoFlags, requested, configuration)); | 
|  |  | 
|  | /* Get a MF for this command. | 
|  | */ | 
|  | if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) { | 
|  | dfailprintk((MYIOC_s_WARN_FMT "write SDP1: no msg frames!\n", | 
|  | ioc->name)); | 
|  | return -EAGAIN; | 
|  | } | 
|  |  | 
|  | ddvprintk((MYIOC_s_INFO_FMT "WriteSDP1 (mf=%p, id=%d, req=0x%x, cfg=0x%x)\n", | 
|  | hd->ioc->name, mf, id, requested, configuration)); | 
|  |  | 
|  |  | 
|  | /* Set the request and the data pointers. | 
|  | * Request takes: 36 bytes (32 bit SGE) | 
|  | * SCSI Device Page 1 requires 16 bytes | 
|  | * 40 + 16 <= size of SCSI IO Request = 56 bytes | 
|  | * and MF size >= 64 bytes. | 
|  | * Place data at end of MF. | 
|  | */ | 
|  | pReq = (Config_t *)mf; | 
|  |  | 
|  | req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); | 
|  | frameOffset = ioc->req_sz - sizeof(SCSIDevicePage1_t); | 
|  |  | 
|  | pData = (SCSIDevicePage1_t *)((u8 *) mf + frameOffset); | 
|  | dataDma = ioc->req_frames_dma + (req_idx * ioc->req_sz) + frameOffset; | 
|  |  | 
|  | /* Complete the request frame (same for all requests). | 
|  | */ | 
|  | pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; | 
|  | pReq->Reserved = 0; | 
|  | pReq->ChainOffset = 0; | 
|  | pReq->Function = MPI_FUNCTION_CONFIG; | 
|  | pReq->ExtPageLength = 0; | 
|  | pReq->ExtPageType = 0; | 
|  | pReq->MsgFlags = 0; | 
|  | for (ii=0; ii < 8; ii++) { | 
|  | pReq->Reserved2[ii] = 0; | 
|  | } | 
|  | pReq->Header.PageVersion = ioc->spi_data.sdp1version; | 
|  | pReq->Header.PageLength = ioc->spi_data.sdp1length; | 
|  | pReq->Header.PageNumber = 1; | 
|  | pReq->Header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE; | 
|  | pReq->PageAddress = cpu_to_le32(id | (bus << 8 )); | 
|  |  | 
|  | /* Add a SGE to the config request. | 
|  | */ | 
|  | flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE | ioc->spi_data.sdp1length * 4; | 
|  |  | 
|  | mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma); | 
|  |  | 
|  | /* Set up the common data portion | 
|  | */ | 
|  | pData->Header.PageVersion = pReq->Header.PageVersion; | 
|  | pData->Header.PageLength = pReq->Header.PageLength; | 
|  | pData->Header.PageNumber = pReq->Header.PageNumber; | 
|  | pData->Header.PageType = pReq->Header.PageType; | 
|  | pData->RequestedParameters = cpu_to_le32(requested); | 
|  | pData->Reserved = 0; | 
|  | pData->Configuration = cpu_to_le32(configuration); | 
|  |  | 
|  | dprintk((MYIOC_s_INFO_FMT | 
|  | "write SDP1: id %d pgaddr 0x%x req 0x%x config 0x%x\n", | 
|  | ioc->name, id, (id | (bus<<8)), | 
|  | requested, configuration)); | 
|  |  | 
|  | mpt_put_msg_frame(ioc->DoneCtx, ioc, mf); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /*	mptscsih_writeIOCPage4  - write IOC Page 4 | 
|  | *	@hd: Pointer to a SCSI Host Structure | 
|  | *	@target_id: write IOC Page4 for this ID & Bus | 
|  | * | 
|  | *	Return: -EAGAIN if unable to obtain a Message Frame | 
|  | *		or 0 if success. | 
|  | * | 
|  | *	Remark: We do not wait for a return, write pages sequentially. | 
|  | */ | 
|  | static int | 
|  | mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus) | 
|  | { | 
|  | MPT_ADAPTER		*ioc = hd->ioc; | 
|  | Config_t		*pReq; | 
|  | IOCPage4_t		*IOCPage4Ptr; | 
|  | MPT_FRAME_HDR		*mf; | 
|  | dma_addr_t		 dataDma; | 
|  | u16			 req_idx; | 
|  | u32			 frameOffset; | 
|  | u32			 flagsLength; | 
|  | int			 ii; | 
|  |  | 
|  | /* Get a MF for this command. | 
|  | */ | 
|  | if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) { | 
|  | dfailprintk((MYIOC_s_WARN_FMT "writeIOCPage4 : no msg frames!\n", | 
|  | ioc->name)); | 
|  | return -EAGAIN; | 
|  | } | 
|  |  | 
|  | /* Set the request and the data pointers. | 
|  | * Place data at end of MF. | 
|  | */ | 
|  | pReq = (Config_t *)mf; | 
|  |  | 
|  | req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); | 
|  | frameOffset = ioc->req_sz - sizeof(IOCPage4_t); | 
|  |  | 
|  | /* Complete the request frame (same for all requests). | 
|  | */ | 
|  | pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; | 
|  | pReq->Reserved = 0; | 
|  | pReq->ChainOffset = 0; | 
|  | pReq->Function = MPI_FUNCTION_CONFIG; | 
|  | pReq->ExtPageLength = 0; | 
|  | pReq->ExtPageType = 0; | 
|  | pReq->MsgFlags = 0; | 
|  | for (ii=0; ii < 8; ii++) { | 
|  | pReq->Reserved2[ii] = 0; | 
|  | } | 
|  |  | 
|  | IOCPage4Ptr = ioc->spi_data.pIocPg4; | 
|  | dataDma = ioc->spi_data.IocPg4_dma; | 
|  | ii = IOCPage4Ptr->ActiveSEP++; | 
|  | IOCPage4Ptr->SEP[ii].SEPTargetID = target_id; | 
|  | IOCPage4Ptr->SEP[ii].SEPBus = bus; | 
|  | pReq->Header = IOCPage4Ptr->Header; | 
|  | pReq->PageAddress = cpu_to_le32(target_id | (bus << 8 )); | 
|  |  | 
|  | /* Add a SGE to the config request. | 
|  | */ | 
|  | flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE | | 
|  | (IOCPage4Ptr->Header.PageLength + ii) * 4; | 
|  |  | 
|  | mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma); | 
|  |  | 
|  | dinitprintk((MYIOC_s_INFO_FMT | 
|  | "writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n", | 
|  | ioc->name, IOCPage4Ptr->MaxSEP, IOCPage4Ptr->ActiveSEP, target_id, bus)); | 
|  |  | 
|  | mpt_put_msg_frame(ioc->DoneCtx, ioc, mf); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *  Bus Scan and Domain Validation functionality ... | 
|  | */ | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* | 
|  | *	mptscsih_scandv_complete - Scan and DV callback routine registered | 
|  | *	to Fustion MPT (base) driver. | 
|  | * | 
|  | *	@ioc: Pointer to MPT_ADAPTER structure | 
|  | *	@mf: Pointer to original MPT request frame | 
|  | *	@mr: Pointer to MPT reply frame (NULL if TurboReply) | 
|  | * | 
|  | *	This routine is called from mpt.c::mpt_interrupt() at the completion | 
|  | *	of any SCSI IO request. | 
|  | *	This routine is registered with the Fusion MPT (base) driver at driver | 
|  | *	load/init time via the mpt_register() API call. | 
|  | * | 
|  | *	Returns 1 indicating alloc'd request frame ptr should be freed. | 
|  | * | 
|  | *	Remark: Sets a completion code and (possibly) saves sense data | 
|  | *	in the IOC member localReply structure. | 
|  | *	Used ONLY for DV and other internal commands. | 
|  | */ | 
|  | int | 
|  | mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) | 
|  | { | 
|  | MPT_SCSI_HOST	*hd; | 
|  | SCSIIORequest_t *pReq; | 
|  | int		 completionCode; | 
|  | u16		 req_idx; | 
|  |  | 
|  | hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; | 
|  |  | 
|  | if ((mf == NULL) || | 
|  | (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) { | 
|  | printk(MYIOC_s_ERR_FMT | 
|  | "ScanDvComplete, %s req frame ptr! (=%p)\n", | 
|  | ioc->name, mf?"BAD":"NULL", (void *) mf); | 
|  | goto wakeup; | 
|  | } | 
|  |  | 
|  | del_timer(&hd->timer); | 
|  | req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); | 
|  | hd->ScsiLookup[req_idx] = NULL; | 
|  | pReq = (SCSIIORequest_t *) mf; | 
|  |  | 
|  | if (mf != hd->cmdPtr) { | 
|  | printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p, idx=%d)\n", | 
|  | hd->ioc->name, (void *)mf, (void *) hd->cmdPtr, req_idx); | 
|  | } | 
|  | hd->cmdPtr = NULL; | 
|  |  | 
|  | ddvprintk((MYIOC_s_INFO_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n", | 
|  | hd->ioc->name, mf, mr, req_idx)); | 
|  |  | 
|  | hd->pLocal = &hd->localReply; | 
|  | hd->pLocal->scsiStatus = 0; | 
|  |  | 
|  | /* If target struct exists, clear sense valid flag. | 
|  | */ | 
|  | if (mr == NULL) { | 
|  | completionCode = MPT_SCANDV_GOOD; | 
|  | } else { | 
|  | SCSIIOReply_t	*pReply; | 
|  | u16		 status; | 
|  | u8		 scsi_status; | 
|  |  | 
|  | pReply = (SCSIIOReply_t *) mr; | 
|  |  | 
|  | status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK; | 
|  | scsi_status = pReply->SCSIStatus; | 
|  |  | 
|  | ddvtprintk((KERN_NOTICE "  IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh, IOCLogInfo=%08xh\n", | 
|  | status, pReply->SCSIState, scsi_status, | 
|  | le32_to_cpu(pReply->IOCLogInfo))); | 
|  |  | 
|  | switch(status) { | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE:	/* 0x0043 */ | 
|  | completionCode = MPT_SCANDV_SELECTION_TIMEOUT; | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR:		/* 0x0046 */ | 
|  | case MPI_IOCSTATUS_SCSI_TASK_TERMINATED:	/* 0x0048 */ | 
|  | case MPI_IOCSTATUS_SCSI_IOC_TERMINATED:		/* 0x004B */ | 
|  | case MPI_IOCSTATUS_SCSI_EXT_TERMINATED:		/* 0x004C */ | 
|  | completionCode = MPT_SCANDV_DID_RESET; | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN:		/* 0x0045 */ | 
|  | case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR:	/* 0x0040 */ | 
|  | case MPI_IOCSTATUS_SUCCESS:			/* 0x0000 */ | 
|  | if (pReply->Function == MPI_FUNCTION_CONFIG) { | 
|  | ConfigReply_t *pr = (ConfigReply_t *)mr; | 
|  | completionCode = MPT_SCANDV_GOOD; | 
|  | hd->pLocal->header.PageVersion = pr->Header.PageVersion; | 
|  | hd->pLocal->header.PageLength = pr->Header.PageLength; | 
|  | hd->pLocal->header.PageNumber = pr->Header.PageNumber; | 
|  | hd->pLocal->header.PageType = pr->Header.PageType; | 
|  |  | 
|  | } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) { | 
|  | /* If the RAID Volume request is successful, | 
|  | * return GOOD, else indicate that | 
|  | * some type of error occurred. | 
|  | */ | 
|  | MpiRaidActionReply_t	*pr = (MpiRaidActionReply_t *)mr; | 
|  | if (le16_to_cpu(pr->ActionStatus) == MPI_RAID_ACTION_ASTATUS_SUCCESS) | 
|  | completionCode = MPT_SCANDV_GOOD; | 
|  | else | 
|  | completionCode = MPT_SCANDV_SOME_ERROR; | 
|  |  | 
|  | } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) { | 
|  | u8		*sense_data; | 
|  | int		 sz; | 
|  |  | 
|  | /* save sense data in global structure | 
|  | */ | 
|  | completionCode = MPT_SCANDV_SENSE; | 
|  | hd->pLocal->scsiStatus = scsi_status; | 
|  | sense_data = ((u8 *)hd->ioc->sense_buf_pool + | 
|  | (req_idx * MPT_SENSE_BUFFER_ALLOC)); | 
|  |  | 
|  | sz = min_t(int, pReq->SenseBufferLength, | 
|  | SCSI_STD_SENSE_BYTES); | 
|  | memcpy(hd->pLocal->sense, sense_data, sz); | 
|  |  | 
|  | ddvprintk((KERN_NOTICE "  Check Condition, sense ptr %p\n", | 
|  | sense_data)); | 
|  | } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) { | 
|  | if (pReq->CDB[0] == INQUIRY) | 
|  | completionCode = MPT_SCANDV_ISSUE_SENSE; | 
|  | else | 
|  | completionCode = MPT_SCANDV_DID_RESET; | 
|  | } | 
|  | else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS) | 
|  | completionCode = MPT_SCANDV_DID_RESET; | 
|  | else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED) | 
|  | completionCode = MPT_SCANDV_DID_RESET; | 
|  | else { | 
|  | completionCode = MPT_SCANDV_GOOD; | 
|  | hd->pLocal->scsiStatus = scsi_status; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR:		/* 0x0047 */ | 
|  | if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED) | 
|  | completionCode = MPT_SCANDV_DID_RESET; | 
|  | else | 
|  | completionCode = MPT_SCANDV_SOME_ERROR; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | completionCode = MPT_SCANDV_SOME_ERROR; | 
|  | break; | 
|  |  | 
|  | }	/* switch(status) */ | 
|  |  | 
|  | ddvtprintk((KERN_NOTICE "  completionCode set to %08xh\n", | 
|  | completionCode)); | 
|  | } /* end of address reply case */ | 
|  |  | 
|  | hd->pLocal->completion = completionCode; | 
|  |  | 
|  | /* MF and RF are freed in mpt_interrupt | 
|  | */ | 
|  | wakeup: | 
|  | /* Free Chain buffers (will never chain) in scan or dv */ | 
|  | //mptscsih_freeChainBuffers(ioc, req_idx); | 
|  |  | 
|  | /* | 
|  | * Wake up the original calling thread | 
|  | */ | 
|  | hd->scandv_wait_done = 1; | 
|  | wake_up(&hd->scandv_waitq); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /*	mptscsih_timer_expired - Call back for timer process. | 
|  | *	Used only for dv functionality. | 
|  | *	@data: Pointer to MPT_SCSI_HOST recast as an unsigned long | 
|  | * | 
|  | */ | 
|  | void | 
|  | mptscsih_timer_expired(unsigned long data) | 
|  | { | 
|  | MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data; | 
|  |  | 
|  | ddvprintk((MYIOC_s_WARN_FMT "Timer Expired! Cmd %p\n", hd->ioc->name, hd->cmdPtr)); | 
|  |  | 
|  | if (hd->cmdPtr) { | 
|  | MPIHeader_t *cmd = (MPIHeader_t *)hd->cmdPtr; | 
|  |  | 
|  | if (cmd->Function == MPI_FUNCTION_SCSI_IO_REQUEST) { | 
|  | /* Desire to issue a task management request here. | 
|  | * TM requests MUST be single threaded. | 
|  | * If old eh code and no TM current, issue request. | 
|  | * If new eh code, do nothing. Wait for OS cmd timeout | 
|  | *	for bus reset. | 
|  | */ | 
|  | ddvtprintk((MYIOC_s_NOTE_FMT "DV Cmd Timeout: NoOp\n", hd->ioc->name)); | 
|  | } else { | 
|  | /* Perform a FW reload */ | 
|  | if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) { | 
|  | printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", hd->ioc->name); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | /* This should NEVER happen */ | 
|  | printk(MYIOC_s_WARN_FMT "Null cmdPtr!!!!\n", hd->ioc->name); | 
|  | } | 
|  |  | 
|  | /* No more processing. | 
|  | * TM call will generate an interrupt for SCSI TM Management. | 
|  | * The FW will reply to all outstanding commands, callback will finish cleanup. | 
|  | * Hard reset clean-up will free all resources. | 
|  | */ | 
|  | ddvprintk((MYIOC_s_WARN_FMT "Timer Expired Complete!\n", hd->ioc->name)); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /*	mptscsih_do_raid - Format and Issue a RAID volume request message. | 
|  | *	@hd: Pointer to scsi host structure | 
|  | *	@action: What do be done. | 
|  | *	@id: Logical target id. | 
|  | *	@bus: Target locations bus. | 
|  | * | 
|  | *	Returns: < 0 on a fatal error | 
|  | *		0 on success | 
|  | * | 
|  | *	Remark: Wait to return until reply processed by the ISR. | 
|  | */ | 
|  | static int | 
|  | mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io) | 
|  | { | 
|  | MpiRaidActionRequest_t	*pReq; | 
|  | MPT_FRAME_HDR		*mf; | 
|  | int			in_isr; | 
|  |  | 
|  | in_isr = in_interrupt(); | 
|  | if (in_isr) { | 
|  | dprintk((MYIOC_s_WARN_FMT "Internal raid request not allowed in ISR context!\n", | 
|  | hd->ioc->name)); | 
|  | return -EPERM; | 
|  | } | 
|  |  | 
|  | /* Get and Populate a free Frame | 
|  | */ | 
|  | if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) { | 
|  | ddvprintk((MYIOC_s_WARN_FMT "_do_raid: no msg frames!\n", | 
|  | hd->ioc->name)); | 
|  | return -EAGAIN; | 
|  | } | 
|  | pReq = (MpiRaidActionRequest_t *)mf; | 
|  | pReq->Action = action; | 
|  | pReq->Reserved1 = 0; | 
|  | pReq->ChainOffset = 0; | 
|  | pReq->Function = MPI_FUNCTION_RAID_ACTION; | 
|  | pReq->VolumeID = io->id; | 
|  | pReq->VolumeBus = io->bus; | 
|  | pReq->PhysDiskNum = io->physDiskNum; | 
|  | pReq->MsgFlags = 0; | 
|  | pReq->Reserved2 = 0; | 
|  | pReq->ActionDataWord = 0; /* Reserved for this action */ | 
|  | //pReq->ActionDataSGE = 0; | 
|  |  | 
|  | mpt_add_sge((char *)&pReq->ActionDataSGE, | 
|  | MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1); | 
|  |  | 
|  | ddvprintk((MYIOC_s_INFO_FMT "RAID Volume action %x id %d\n", | 
|  | hd->ioc->name, action, io->id)); | 
|  |  | 
|  | hd->pLocal = NULL; | 
|  | hd->timer.expires = jiffies + HZ*10; /* 10 second timeout */ | 
|  | hd->scandv_wait_done = 0; | 
|  |  | 
|  | /* Save cmd pointer, for resource free if timeout or | 
|  | * FW reload occurs | 
|  | */ | 
|  | hd->cmdPtr = mf; | 
|  |  | 
|  | add_timer(&hd->timer); | 
|  | mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf); | 
|  | wait_event(hd->scandv_waitq, hd->scandv_wait_done); | 
|  |  | 
|  | if ((hd->pLocal == NULL) || (hd->pLocal->completion != MPT_SCANDV_GOOD)) | 
|  | return -1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */ | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_do_cmd - Do internal command. | 
|  | *	@hd: MPT_SCSI_HOST pointer | 
|  | *	@io: INTERNAL_CMD pointer. | 
|  | * | 
|  | *	Issue the specified internally generated command and do command | 
|  | *	specific cleanup. For bus scan / DV only. | 
|  | *	NOTES: If command is Inquiry and status is good, | 
|  | *	initialize a target structure, save the data | 
|  | * | 
|  | *	Remark: Single threaded access only. | 
|  | * | 
|  | *	Return: | 
|  | *		< 0 if an illegal command or no resources | 
|  | * | 
|  | *		   0 if good | 
|  | * | 
|  | *		 > 0 if command complete but some type of completion error. | 
|  | */ | 
|  | static int | 
|  | mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io) | 
|  | { | 
|  | MPT_FRAME_HDR	*mf; | 
|  | SCSIIORequest_t	*pScsiReq; | 
|  | SCSIIORequest_t	 ReqCopy; | 
|  | int		 my_idx, ii, dir; | 
|  | int		 rc, cmdTimeout; | 
|  | int		in_isr; | 
|  | char		 cmdLen; | 
|  | char		 CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; | 
|  | char		 cmd = io->cmd; | 
|  |  | 
|  | in_isr = in_interrupt(); | 
|  | if (in_isr) { | 
|  | dprintk((MYIOC_s_WARN_FMT "Internal SCSI IO request not allowed in ISR context!\n", | 
|  | hd->ioc->name)); | 
|  | return -EPERM; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Set command specific information | 
|  | */ | 
|  | switch (cmd) { | 
|  | case INQUIRY: | 
|  | cmdLen = 6; | 
|  | dir = MPI_SCSIIO_CONTROL_READ; | 
|  | CDB[0] = cmd; | 
|  | CDB[4] = io->size; | 
|  | cmdTimeout = 10; | 
|  | break; | 
|  |  | 
|  | case TEST_UNIT_READY: | 
|  | cmdLen = 6; | 
|  | dir = MPI_SCSIIO_CONTROL_READ; | 
|  | cmdTimeout = 10; | 
|  | break; | 
|  |  | 
|  | case START_STOP: | 
|  | cmdLen = 6; | 
|  | dir = MPI_SCSIIO_CONTROL_READ; | 
|  | CDB[0] = cmd; | 
|  | CDB[4] = 1;	/*Spin up the disk */ | 
|  | cmdTimeout = 15; | 
|  | break; | 
|  |  | 
|  | case REQUEST_SENSE: | 
|  | cmdLen = 6; | 
|  | CDB[0] = cmd; | 
|  | CDB[4] = io->size; | 
|  | dir = MPI_SCSIIO_CONTROL_READ; | 
|  | cmdTimeout = 10; | 
|  | break; | 
|  |  | 
|  | case READ_BUFFER: | 
|  | cmdLen = 10; | 
|  | dir = MPI_SCSIIO_CONTROL_READ; | 
|  | CDB[0] = cmd; | 
|  | if (io->flags & MPT_ICFLAG_ECHO) { | 
|  | CDB[1] = 0x0A; | 
|  | } else { | 
|  | CDB[1] = 0x02; | 
|  | } | 
|  |  | 
|  | if (io->flags & MPT_ICFLAG_BUF_CAP) { | 
|  | CDB[1] |= 0x01; | 
|  | } | 
|  | CDB[6] = (io->size >> 16) & 0xFF; | 
|  | CDB[7] = (io->size >>  8) & 0xFF; | 
|  | CDB[8] = io->size & 0xFF; | 
|  | cmdTimeout = 10; | 
|  | break; | 
|  |  | 
|  | case WRITE_BUFFER: | 
|  | cmdLen = 10; | 
|  | dir = MPI_SCSIIO_CONTROL_WRITE; | 
|  | CDB[0] = cmd; | 
|  | if (io->flags & MPT_ICFLAG_ECHO) { | 
|  | CDB[1] = 0x0A; | 
|  | } else { | 
|  | CDB[1] = 0x02; | 
|  | } | 
|  | CDB[6] = (io->size >> 16) & 0xFF; | 
|  | CDB[7] = (io->size >>  8) & 0xFF; | 
|  | CDB[8] = io->size & 0xFF; | 
|  | cmdTimeout = 10; | 
|  | break; | 
|  |  | 
|  | case RESERVE: | 
|  | cmdLen = 6; | 
|  | dir = MPI_SCSIIO_CONTROL_READ; | 
|  | CDB[0] = cmd; | 
|  | cmdTimeout = 10; | 
|  | break; | 
|  |  | 
|  | case RELEASE: | 
|  | cmdLen = 6; | 
|  | dir = MPI_SCSIIO_CONTROL_READ; | 
|  | CDB[0] = cmd; | 
|  | cmdTimeout = 10; | 
|  | break; | 
|  |  | 
|  | case SYNCHRONIZE_CACHE: | 
|  | cmdLen = 10; | 
|  | dir = MPI_SCSIIO_CONTROL_READ; | 
|  | CDB[0] = cmd; | 
|  | //		CDB[1] = 0x02;	/* set immediate bit */ | 
|  | cmdTimeout = 10; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | /* Error Case */ | 
|  | return -EFAULT; | 
|  | } | 
|  |  | 
|  | /* Get and Populate a free Frame | 
|  | */ | 
|  | if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) { | 
|  | ddvprintk((MYIOC_s_WARN_FMT "No msg frames!\n", | 
|  | hd->ioc->name)); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | pScsiReq = (SCSIIORequest_t *) mf; | 
|  |  | 
|  | /* Get the request index */ | 
|  | my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); | 
|  | ADD_INDEX_LOG(my_idx); /* for debug */ | 
|  |  | 
|  | if (io->flags & MPT_ICFLAG_PHYS_DISK) { | 
|  | pScsiReq->TargetID = io->physDiskNum; | 
|  | pScsiReq->Bus = 0; | 
|  | pScsiReq->ChainOffset = 0; | 
|  | pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH; | 
|  | } else { | 
|  | pScsiReq->TargetID = io->id; | 
|  | pScsiReq->Bus = io->bus; | 
|  | pScsiReq->ChainOffset = 0; | 
|  | pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST; | 
|  | } | 
|  |  | 
|  | pScsiReq->CDBLength = cmdLen; | 
|  | pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE; | 
|  |  | 
|  | pScsiReq->Reserved = 0; | 
|  |  | 
|  | pScsiReq->MsgFlags = mpt_msg_flags(); | 
|  | /* MsgContext set in mpt_get_msg_fram call  */ | 
|  |  | 
|  | for (ii=0; ii < 8; ii++) | 
|  | pScsiReq->LUN[ii] = 0; | 
|  | pScsiReq->LUN[1] = io->lun; | 
|  |  | 
|  | if (io->flags & MPT_ICFLAG_TAGGED_CMD) | 
|  | pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_SIMPLEQ); | 
|  | else | 
|  | pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED); | 
|  |  | 
|  | if (cmd == REQUEST_SENSE) { | 
|  | pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED); | 
|  | ddvprintk((MYIOC_s_INFO_FMT "Untagged! 0x%2x\n", | 
|  | hd->ioc->name, cmd)); | 
|  | } | 
|  |  | 
|  | for (ii=0; ii < 16; ii++) | 
|  | pScsiReq->CDB[ii] = CDB[ii]; | 
|  |  | 
|  | pScsiReq->DataLength = cpu_to_le32(io->size); | 
|  | pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma | 
|  | + (my_idx * MPT_SENSE_BUFFER_ALLOC)); | 
|  |  | 
|  | ddvprintk((MYIOC_s_INFO_FMT "Sending Command 0x%x for (%d:%d:%d)\n", | 
|  | hd->ioc->name, cmd, io->bus, io->id, io->lun)); | 
|  |  | 
|  | if (dir == MPI_SCSIIO_CONTROL_READ) { | 
|  | mpt_add_sge((char *) &pScsiReq->SGL, | 
|  | MPT_SGE_FLAGS_SSIMPLE_READ | io->size, | 
|  | io->data_dma); | 
|  | } else { | 
|  | mpt_add_sge((char *) &pScsiReq->SGL, | 
|  | MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size, | 
|  | io->data_dma); | 
|  | } | 
|  |  | 
|  | /* The ISR will free the request frame, but we need | 
|  | * the information to initialize the target. Duplicate. | 
|  | */ | 
|  | memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t)); | 
|  |  | 
|  | /* Issue this command after: | 
|  | *	finish init | 
|  | *	add timer | 
|  | * Wait until the reply has been received | 
|  | *  ScsiScanDvCtx callback function will | 
|  | *	set hd->pLocal; | 
|  | *	set scandv_wait_done and call wake_up | 
|  | */ | 
|  | hd->pLocal = NULL; | 
|  | hd->timer.expires = jiffies + HZ*cmdTimeout; | 
|  | hd->scandv_wait_done = 0; | 
|  |  | 
|  | /* Save cmd pointer, for resource free if timeout or | 
|  | * FW reload occurs | 
|  | */ | 
|  | hd->cmdPtr = mf; | 
|  |  | 
|  | add_timer(&hd->timer); | 
|  | mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf); | 
|  | wait_event(hd->scandv_waitq, hd->scandv_wait_done); | 
|  |  | 
|  | if (hd->pLocal) { | 
|  | rc = hd->pLocal->completion; | 
|  | hd->pLocal->skip = 0; | 
|  |  | 
|  | /* Always set fatal error codes in some cases. | 
|  | */ | 
|  | if (rc == MPT_SCANDV_SELECTION_TIMEOUT) | 
|  | rc = -ENXIO; | 
|  | else if (rc == MPT_SCANDV_SOME_ERROR) | 
|  | rc =  -rc; | 
|  | } else { | 
|  | rc = -EFAULT; | 
|  | /* This should never happen. */ | 
|  | ddvprintk((MYIOC_s_INFO_FMT "_do_cmd: Null pLocal!!!\n", | 
|  | hd->ioc->name)); | 
|  | } | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_negotiate_to_asyn_narrow - Restore devices to default state | 
|  | *	@hd: Pointer to a SCSI HOST structure | 
|  | *	@vtarget: per device private data | 
|  | * | 
|  | *	Uses the ISR, but with special processing. | 
|  | *	MUST be single-threaded. | 
|  | * | 
|  | */ | 
|  | static void | 
|  | mptscsih_negotiate_to_asyn_narrow(MPT_SCSI_HOST *hd, VirtTarget *vtarget) | 
|  | { | 
|  | MPT_ADAPTER		*ioc= hd->ioc; | 
|  | SCSIDevicePage1_t	*pcfg1Data; | 
|  | CONFIGPARMS		 cfg; | 
|  | dma_addr_t		 cfg1_dma_addr; | 
|  | ConfigPageHeader_t	 header; | 
|  | int			 id; | 
|  | int			 requested, configuration, data,i; | 
|  | u8			 flags, factor; | 
|  |  | 
|  | if (ioc->bus_type != SPI) | 
|  | return; | 
|  |  | 
|  | if (!ioc->spi_data.sdp1length) | 
|  | return; | 
|  |  | 
|  | pcfg1Data = (SCSIDevicePage1_t *)pci_alloc_consistent(ioc->pcidev, | 
|  | ioc->spi_data.sdp1length * 4, &cfg1_dma_addr); | 
|  |  | 
|  | if (pcfg1Data == NULL) | 
|  | return; | 
|  |  | 
|  | header.PageVersion = ioc->spi_data.sdp1version; | 
|  | header.PageLength = ioc->spi_data.sdp1length; | 
|  | header.PageNumber = 1; | 
|  | header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE; | 
|  | cfg.cfghdr.hdr = &header; | 
|  | cfg.physAddr = cfg1_dma_addr; | 
|  | cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; | 
|  | cfg.dir = 1; | 
|  | cfg.timeout = 0; | 
|  |  | 
|  | if (vtarget->raidVolume && ioc->raid_data.pIocPg3) { | 
|  | for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) { | 
|  | id = ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID; | 
|  | flags = hd->ioc->spi_data.noQas; | 
|  | if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) { | 
|  | data = hd->ioc->spi_data.nvram[id]; | 
|  | if (data & MPT_NVRAM_WIDE_DISABLE) | 
|  | flags |= MPT_TARGET_NO_NEGO_WIDE; | 
|  | factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT; | 
|  | if ((factor == 0) || (factor == MPT_ASYNC)) | 
|  | flags |= MPT_TARGET_NO_NEGO_SYNC; | 
|  | } | 
|  | mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested, | 
|  | &configuration, flags); | 
|  | dnegoprintk(("syncronize cache: id=%d width=0 factor=MPT_ASYNC " | 
|  | "offset=0 negoFlags=%x request=%x config=%x\n", | 
|  | id, flags, requested, configuration)); | 
|  | pcfg1Data->RequestedParameters = cpu_to_le32(requested); | 
|  | pcfg1Data->Reserved = 0; | 
|  | pcfg1Data->Configuration = cpu_to_le32(configuration); | 
|  | cfg.pageAddr = (vtarget->bus_id<<8) | id; | 
|  | mpt_config(hd->ioc, &cfg); | 
|  | } | 
|  | } else { | 
|  | flags = vtarget->negoFlags; | 
|  | mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested, | 
|  | &configuration, flags); | 
|  | dnegoprintk(("syncronize cache: id=%d width=0 factor=MPT_ASYNC " | 
|  | "offset=0 negoFlags=%x request=%x config=%x\n", | 
|  | vtarget->target_id, flags, requested, configuration)); | 
|  | pcfg1Data->RequestedParameters = cpu_to_le32(requested); | 
|  | pcfg1Data->Reserved = 0; | 
|  | pcfg1Data->Configuration = cpu_to_le32(configuration); | 
|  | cfg.pageAddr = (vtarget->bus_id<<8) | vtarget->target_id; | 
|  | mpt_config(hd->ioc, &cfg); | 
|  | } | 
|  |  | 
|  | if (pcfg1Data) | 
|  | pci_free_consistent(ioc->pcidev, header.PageLength * 4, pcfg1Data, cfg1_dma_addr); | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_synchronize_cache - Send SYNCHRONIZE_CACHE to all disks. | 
|  | *	@hd: Pointer to a SCSI HOST structure | 
|  | *	@vtarget: per device private data | 
|  | *	@lun: lun | 
|  | * | 
|  | *	Uses the ISR, but with special processing. | 
|  | *	MUST be single-threaded. | 
|  | * | 
|  | */ | 
|  | static void | 
|  | mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice) | 
|  | { | 
|  | INTERNAL_CMD		 iocmd; | 
|  |  | 
|  | /* Following parameters will not change | 
|  | * in this routine. | 
|  | */ | 
|  | iocmd.cmd = SYNCHRONIZE_CACHE; | 
|  | iocmd.flags = 0; | 
|  | iocmd.physDiskNum = -1; | 
|  | iocmd.data = NULL; | 
|  | iocmd.data_dma = -1; | 
|  | iocmd.size = 0; | 
|  | iocmd.rsvd = iocmd.rsvd2 = 0; | 
|  | iocmd.bus = vdevice->bus_id; | 
|  | iocmd.id = vdevice->target_id; | 
|  | iocmd.lun = (u8)vdevice->lun; | 
|  |  | 
|  | if ((vdevice->vtarget->type & TYPE_DISK) && | 
|  | (vdevice->configured_lun)) | 
|  | mptscsih_do_cmd(hd, &iocmd); | 
|  | } | 
|  |  | 
|  | /* Search IOC page 3 to determine if this is hidden physical disk | 
|  | */ | 
|  | /* Search IOC page 3 to determine if this is hidden physical disk | 
|  | */ | 
|  | static int | 
|  | mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (!ioc->raid_data.isRaid || !ioc->raid_data.pIocPg3) | 
|  | return 0; | 
|  |  | 
|  | for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) { | 
|  | if (id == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID) | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_domainValidation - Top level handler for domain validation. | 
|  | *	@hd: Pointer to MPT_SCSI_HOST structure. | 
|  | * | 
|  | *	Uses the ISR, but with special processing. | 
|  | *	Called from schedule, should not be in interrupt mode. | 
|  | *	While thread alive, do dv for all devices needing dv | 
|  | * | 
|  | *	Return: None. | 
|  | */ | 
|  | static void | 
|  | mptscsih_domainValidation(void *arg) | 
|  | { | 
|  | MPT_SCSI_HOST		*hd; | 
|  | MPT_ADAPTER		*ioc; | 
|  | unsigned long		 flags; | 
|  | int 			 id, maxid, dvStatus, did; | 
|  | int			 ii, isPhysDisk; | 
|  |  | 
|  | spin_lock_irqsave(&dvtaskQ_lock, flags); | 
|  | dvtaskQ_active = 1; | 
|  | if (dvtaskQ_release) { | 
|  | dvtaskQ_active = 0; | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, flags); | 
|  | return; | 
|  | } | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, flags); | 
|  |  | 
|  | /* For this ioc, loop through all devices and do dv to each device. | 
|  | * When complete with this ioc, search through the ioc list, and | 
|  | * for each scsi ioc found, do dv for all devices. Exit when no | 
|  | * device needs dv. | 
|  | */ | 
|  | did = 1; | 
|  | while (did) { | 
|  | did = 0; | 
|  | list_for_each_entry(ioc, &ioc_list, list) { | 
|  | spin_lock_irqsave(&dvtaskQ_lock, flags); | 
|  | if (dvtaskQ_release) { | 
|  | dvtaskQ_active = 0; | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, flags); | 
|  | return; | 
|  | } | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, flags); | 
|  |  | 
|  | msleep(250); | 
|  |  | 
|  | /* DV only to SPI adapters */ | 
|  | if (ioc->bus_type != SPI) | 
|  | continue; | 
|  |  | 
|  | /* Make sure everything looks ok */ | 
|  | if (ioc->sh == NULL) | 
|  | continue; | 
|  |  | 
|  | hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; | 
|  | if (hd == NULL) | 
|  | continue; | 
|  |  | 
|  | if ((ioc->spi_data.forceDv & MPT_SCSICFG_RELOAD_IOC_PG3) != 0) { | 
|  | mpt_read_ioc_pg_3(ioc); | 
|  | if (ioc->raid_data.pIocPg3) { | 
|  | Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk; | 
|  | int		numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks; | 
|  |  | 
|  | while (numPDisk) { | 
|  | if (ioc->spi_data.dvStatus[pPDisk->PhysDiskID] & MPT_SCSICFG_DV_NOT_DONE) | 
|  | ioc->spi_data.dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV; | 
|  |  | 
|  | pPDisk++; | 
|  | numPDisk--; | 
|  | } | 
|  | } | 
|  | ioc->spi_data.forceDv &= ~MPT_SCSICFG_RELOAD_IOC_PG3; | 
|  | } | 
|  |  | 
|  | maxid = min_t(int, ioc->sh->max_id, MPT_MAX_SCSI_DEVICES); | 
|  |  | 
|  | for (id = 0; id < maxid; id++) { | 
|  | spin_lock_irqsave(&dvtaskQ_lock, flags); | 
|  | if (dvtaskQ_release) { | 
|  | dvtaskQ_active = 0; | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, flags); | 
|  | return; | 
|  | } | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, flags); | 
|  | dvStatus = hd->ioc->spi_data.dvStatus[id]; | 
|  |  | 
|  | if (dvStatus & MPT_SCSICFG_NEED_DV) { | 
|  | did++; | 
|  | hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_DV_PENDING; | 
|  | hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_NEED_DV; | 
|  |  | 
|  | msleep(250); | 
|  |  | 
|  | /* If hidden phys disk, block IO's to all | 
|  | *	raid volumes | 
|  | * else, process normally | 
|  | */ | 
|  | isPhysDisk = mptscsih_is_phys_disk(ioc, id); | 
|  | if (isPhysDisk) { | 
|  | for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { | 
|  | if (hd->ioc->raid_data.isRaid & (1 << ii)) { | 
|  | hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_PENDING; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if(mpt_alt_ioc_wait(hd->ioc)!=0) { | 
|  | ddvprintk((MYIOC_s_WARN_FMT "alt_ioc busy!\n", | 
|  | hd->ioc->name)); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (mptscsih_doDv(hd, 0, id) == 1) { | 
|  | /* Untagged device was busy, try again | 
|  | */ | 
|  | hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_NEED_DV; | 
|  | hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_PENDING; | 
|  | } else { | 
|  | /* DV is complete. Clear flags. | 
|  | */ | 
|  | hd->ioc->spi_data.dvStatus[id] &= ~(MPT_SCSICFG_DV_NOT_DONE | MPT_SCSICFG_DV_PENDING); | 
|  | } | 
|  |  | 
|  | spin_lock(&hd->ioc->initializing_hba_lock); | 
|  | hd->ioc->initializing_hba_lock_flag=0; | 
|  | spin_unlock(&hd->ioc->initializing_hba_lock); | 
|  |  | 
|  | if (isPhysDisk) { | 
|  | for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { | 
|  | if (hd->ioc->raid_data.isRaid & (1 << ii)) { | 
|  | hd->ioc->spi_data.dvStatus[ii] &= ~MPT_SCSICFG_DV_PENDING; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (hd->ioc->spi_data.noQas) | 
|  | mptscsih_qas_check(hd, id); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | spin_lock_irqsave(&dvtaskQ_lock, flags); | 
|  | dvtaskQ_active = 0; | 
|  | spin_unlock_irqrestore(&dvtaskQ_lock, flags); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Write SDP1 if no QAS has been enabled | 
|  | */ | 
|  | static void | 
|  | mptscsih_qas_check(MPT_SCSI_HOST *hd, int id) | 
|  | { | 
|  | VirtTarget *vtarget; | 
|  | int ii; | 
|  |  | 
|  | if (hd->Targets == NULL) | 
|  | return; | 
|  |  | 
|  | for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { | 
|  | if (ii == id) | 
|  | continue; | 
|  |  | 
|  | if ((hd->ioc->spi_data.dvStatus[ii] & MPT_SCSICFG_DV_NOT_DONE) != 0) | 
|  | continue; | 
|  |  | 
|  | vtarget = hd->Targets[ii]; | 
|  |  | 
|  | if ((vtarget != NULL) && (!vtarget->raidVolume)) { | 
|  | if ((vtarget->negoFlags & hd->ioc->spi_data.noQas) == 0) { | 
|  | vtarget->negoFlags |= hd->ioc->spi_data.noQas; | 
|  | dnegoprintk(("writeSDP1: id=%d flags=0\n", id)); | 
|  | mptscsih_writeSDP1(hd, 0, ii, 0); | 
|  | } | 
|  | } else { | 
|  | if (mptscsih_is_phys_disk(hd->ioc, ii) == 1) { | 
|  | dnegoprintk(("writeSDP1: id=%d SCSICFG_USE_NVRAM\n", id)); | 
|  | mptscsih_writeSDP1(hd, 0, ii, MPT_SCSICFG_USE_NVRAM); | 
|  | } | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | #define MPT_GET_NVRAM_VALS	0x01 | 
|  | #define MPT_UPDATE_MAX		0x02 | 
|  | #define MPT_SET_MAX		0x04 | 
|  | #define MPT_SET_MIN		0x08 | 
|  | #define MPT_FALLBACK		0x10 | 
|  | #define MPT_SAVE		0x20 | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /** | 
|  | *	mptscsih_doDv - Perform domain validation to a target. | 
|  | *	@hd: Pointer to MPT_SCSI_HOST structure. | 
|  | *	@portnum: IOC port number. | 
|  | *	@target: Physical ID of this target | 
|  | * | 
|  | *	Uses the ISR, but with special processing. | 
|  | *	MUST be single-threaded. | 
|  | *	Test will exit if target is at async & narrow. | 
|  | * | 
|  | *	Return: None. | 
|  | */ | 
|  | static int | 
|  | mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id) | 
|  | { | 
|  | MPT_ADAPTER		*ioc = hd->ioc; | 
|  | VirtTarget		*vtarget; | 
|  | SCSIDevicePage1_t	*pcfg1Data; | 
|  | SCSIDevicePage0_t	*pcfg0Data; | 
|  | u8			*pbuf1; | 
|  | u8			*pbuf2; | 
|  | u8			*pDvBuf; | 
|  | dma_addr_t		 dvbuf_dma = -1; | 
|  | dma_addr_t		 buf1_dma = -1; | 
|  | dma_addr_t		 buf2_dma = -1; | 
|  | dma_addr_t		 cfg1_dma_addr = -1; | 
|  | dma_addr_t		 cfg0_dma_addr = -1; | 
|  | ConfigPageHeader_t	 header1; | 
|  | ConfigPageHeader_t	 header0; | 
|  | DVPARAMETERS		 dv; | 
|  | INTERNAL_CMD		 iocmd; | 
|  | CONFIGPARMS		 cfg; | 
|  | int			 dv_alloc = 0; | 
|  | int			 rc, sz = 0; | 
|  | int			 bufsize = 0; | 
|  | int			 dataBufSize = 0; | 
|  | int			 echoBufSize = 0; | 
|  | int			 notDone; | 
|  | int			 patt; | 
|  | int			 repeat; | 
|  | int			 retcode = 0; | 
|  | int			 nfactor =  MPT_ULTRA320; | 
|  | char			 firstPass = 1; | 
|  | char			 doFallback = 0; | 
|  | char			 readPage0; | 
|  | char			 bus, lun; | 
|  | char			 inq0 = 0; | 
|  |  | 
|  | if (ioc->spi_data.sdp1length == 0) | 
|  | return 0; | 
|  |  | 
|  | if (ioc->spi_data.sdp0length == 0) | 
|  | return 0; | 
|  |  | 
|  | /* If multiple buses are used, require that the initiator | 
|  | * id be the same on all buses. | 
|  | */ | 
|  | if (id == ioc->pfacts[0].PortSCSIID) | 
|  | return 0; | 
|  |  | 
|  | lun = 0; | 
|  | bus = (u8) bus_number; | 
|  | ddvtprintk((MYIOC_s_NOTE_FMT | 
|  | "DV started: bus=%d, id=%d dv @ %p\n", | 
|  | ioc->name, bus, id, &dv)); | 
|  |  | 
|  | /* Prep DV structure | 
|  | */ | 
|  | memset (&dv, 0, sizeof(DVPARAMETERS)); | 
|  | dv.id = id; | 
|  |  | 
|  | /* Populate tmax with the current maximum | 
|  | * transfer parameters for this target. | 
|  | * Exit if narrow and async. | 
|  | */ | 
|  | dv.cmd = MPT_GET_NVRAM_VALS; | 
|  | mptscsih_dv_parms(hd, &dv, NULL); | 
|  |  | 
|  | /* Prep SCSI IO structure | 
|  | */ | 
|  | iocmd.id = id; | 
|  | iocmd.bus = bus; | 
|  | iocmd.lun = lun; | 
|  | iocmd.flags = 0; | 
|  | iocmd.physDiskNum = -1; | 
|  | iocmd.rsvd = iocmd.rsvd2 = 0; | 
|  |  | 
|  | vtarget = hd->Targets[id]; | 
|  |  | 
|  | /* Use tagged commands if possible. | 
|  | */ | 
|  | if (vtarget) { | 
|  | if (vtarget->tflags & MPT_TARGET_FLAGS_Q_YES) | 
|  | iocmd.flags |= MPT_ICFLAG_TAGGED_CMD; | 
|  | else { | 
|  | if (hd->ioc->facts.FWVersion.Word < 0x01000600) | 
|  | return 0; | 
|  |  | 
|  | if ((hd->ioc->facts.FWVersion.Word >= 0x01010000) && | 
|  | (hd->ioc->facts.FWVersion.Word < 0x01010B00)) | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Prep cfg structure | 
|  | */ | 
|  | cfg.pageAddr = (bus<<8) | id; | 
|  | cfg.cfghdr.hdr = NULL; | 
|  |  | 
|  | /* Prep SDP0 header | 
|  | */ | 
|  | header0.PageVersion = ioc->spi_data.sdp0version; | 
|  | header0.PageLength = ioc->spi_data.sdp0length; | 
|  | header0.PageNumber = 0; | 
|  | header0.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE; | 
|  |  | 
|  | /* Prep SDP1 header | 
|  | */ | 
|  | header1.PageVersion = ioc->spi_data.sdp1version; | 
|  | header1.PageLength = ioc->spi_data.sdp1length; | 
|  | header1.PageNumber = 1; | 
|  | header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE; | 
|  |  | 
|  | if (header0.PageLength & 1) | 
|  | dv_alloc = (header0.PageLength * 4) + 4; | 
|  |  | 
|  | dv_alloc +=  (2048 + (header1.PageLength * 4)); | 
|  |  | 
|  | pDvBuf = pci_alloc_consistent(ioc->pcidev, dv_alloc, &dvbuf_dma); | 
|  | if (pDvBuf == NULL) | 
|  | return 0; | 
|  |  | 
|  | sz = 0; | 
|  | pbuf1 = (u8 *)pDvBuf; | 
|  | buf1_dma = dvbuf_dma; | 
|  | sz +=1024; | 
|  |  | 
|  | pbuf2 = (u8 *) (pDvBuf + sz); | 
|  | buf2_dma = dvbuf_dma + sz; | 
|  | sz +=1024; | 
|  |  | 
|  | pcfg0Data = (SCSIDevicePage0_t *) (pDvBuf + sz); | 
|  | cfg0_dma_addr = dvbuf_dma + sz; | 
|  | sz += header0.PageLength * 4; | 
|  |  | 
|  | /* 8-byte alignment | 
|  | */ | 
|  | if (header0.PageLength & 1) | 
|  | sz += 4; | 
|  |  | 
|  | pcfg1Data = (SCSIDevicePage1_t *) (pDvBuf + sz); | 
|  | cfg1_dma_addr = dvbuf_dma + sz; | 
|  |  | 
|  | /* Skip this ID? Set cfg.cfghdr.hdr to force config page write | 
|  | */ | 
|  | { | 
|  | SpiCfgData *pspi_data = &hd->ioc->spi_data; | 
|  | if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) { | 
|  | /* Set the factor from nvram */ | 
|  | nfactor = (pspi_data->nvram[id] & MPT_NVRAM_SYNC_MASK) >> 8; | 
|  | if (nfactor < pspi_data->minSyncFactor ) | 
|  | nfactor = pspi_data->minSyncFactor; | 
|  |  | 
|  | if (!(pspi_data->nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE) || | 
|  | (pspi_data->PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_OFF_DV) ) { | 
|  |  | 
|  | ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n", | 
|  | ioc->name, bus, id, lun)); | 
|  |  | 
|  | dv.cmd = MPT_SET_MAX; | 
|  | mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); | 
|  | cfg.cfghdr.hdr = &header1; | 
|  |  | 
|  | /* Save the final negotiated settings to | 
|  | * SCSI device page 1. | 
|  | */ | 
|  | cfg.physAddr = cfg1_dma_addr; | 
|  | cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; | 
|  | cfg.dir = 1; | 
|  | mpt_config(hd->ioc, &cfg); | 
|  | goto target_done; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Finish iocmd inititialization - hidden or visible disk? */ | 
|  | if (ioc->raid_data.pIocPg3) { | 
|  | /* Search IOC page 3 for matching id | 
|  | */ | 
|  | Ioc3PhysDisk_t *pPDisk =  ioc->raid_data.pIocPg3->PhysDisk; | 
|  | int		numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks; | 
|  |  | 
|  | while (numPDisk) { | 
|  | if (pPDisk->PhysDiskID == id) { | 
|  | /* match */ | 
|  | iocmd.flags |= MPT_ICFLAG_PHYS_DISK; | 
|  | iocmd.physDiskNum = pPDisk->PhysDiskNum; | 
|  |  | 
|  | /* Quiesce the IM | 
|  | */ | 
|  | if (mptscsih_do_raid(hd, MPI_RAID_ACTION_QUIESCE_PHYS_IO, &iocmd) < 0) { | 
|  | ddvprintk((MYIOC_s_ERR_FMT "RAID Queisce FAILED!\n", ioc->name)); | 
|  | goto target_done; | 
|  | } | 
|  | break; | 
|  | } | 
|  | pPDisk++; | 
|  | numPDisk--; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* RAID Volume ID's may double for a physical device. If RAID but | 
|  | * not a physical ID as well, skip DV. | 
|  | */ | 
|  | if ((hd->ioc->raid_data.isRaid & (1 << id)) && !(iocmd.flags & MPT_ICFLAG_PHYS_DISK)) | 
|  | goto target_done; | 
|  |  | 
|  |  | 
|  | /* Basic Test. | 
|  | * Async & Narrow - Inquiry | 
|  | * Async & Narrow - Inquiry | 
|  | * Maximum transfer rate - Inquiry | 
|  | * Compare buffers: | 
|  | *	If compare, test complete. | 
|  | *	If miscompare and first pass, repeat | 
|  | *	If miscompare and not first pass, fall back and repeat | 
|  | */ | 
|  | hd->pLocal = NULL; | 
|  | readPage0 = 0; | 
|  | sz = SCSI_MAX_INQUIRY_BYTES; | 
|  | rc = MPT_SCANDV_GOOD; | 
|  | while (1) { | 
|  | ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test on id=%d\n", ioc->name, id)); | 
|  | retcode = 0; | 
|  | dv.cmd = MPT_SET_MIN; | 
|  | mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); | 
|  |  | 
|  | cfg.cfghdr.hdr = &header1; | 
|  | cfg.physAddr = cfg1_dma_addr; | 
|  | cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; | 
|  | cfg.dir = 1; | 
|  | if (mpt_config(hd->ioc, &cfg) != 0) | 
|  | goto target_done; | 
|  |  | 
|  | /* Wide - narrow - wide workaround case | 
|  | */ | 
|  | if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) { | 
|  | /* Send an untagged command to reset disk Qs corrupted | 
|  | * when a parity error occurs on a Request Sense. | 
|  | */ | 
|  | if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) || | 
|  | ((hd->ioc->facts.FWVersion.Word >= 0x01010000) && | 
|  | (hd->ioc->facts.FWVersion.Word < 0x01010B00)) ) { | 
|  |  | 
|  | iocmd.cmd = REQUEST_SENSE; | 
|  | iocmd.data_dma = buf1_dma; | 
|  | iocmd.data = pbuf1; | 
|  | iocmd.size = 0x12; | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  | else { | 
|  | if (hd->pLocal == NULL) | 
|  | goto target_done; | 
|  | rc = hd->pLocal->completion; | 
|  | if ((rc == MPT_SCANDV_GOOD) || (rc == MPT_SCANDV_SENSE)) { | 
|  | dv.max.width = 0; | 
|  | doFallback = 0; | 
|  | } else | 
|  | goto target_done; | 
|  | } | 
|  | } else | 
|  | goto target_done; | 
|  | } | 
|  |  | 
|  | iocmd.cmd = INQUIRY; | 
|  | iocmd.data_dma = buf1_dma; | 
|  | iocmd.data = pbuf1; | 
|  | iocmd.size = sz; | 
|  | memset(pbuf1, 0x00, sz); | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  | else { | 
|  | if (hd->pLocal == NULL) | 
|  | goto target_done; | 
|  | rc = hd->pLocal->completion; | 
|  | if (rc == MPT_SCANDV_GOOD) { | 
|  | if (hd->pLocal->scsiStatus == SAM_STAT_BUSY) { | 
|  | if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0) | 
|  | retcode = 1; | 
|  | else | 
|  | retcode = 0; | 
|  |  | 
|  | goto target_done; | 
|  | } | 
|  | } else if  (rc == MPT_SCANDV_SENSE) { | 
|  | ; | 
|  | } else { | 
|  | /* If first command doesn't complete | 
|  | * with a good status or with a check condition, | 
|  | * exit. | 
|  | */ | 
|  | goto target_done; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Reset the size for disks | 
|  | */ | 
|  | inq0 = (*pbuf1) & 0x1F; | 
|  | if ((inq0 == 0) && vtarget && !vtarget->raidVolume) { | 
|  | sz = 0x40; | 
|  | iocmd.size = sz; | 
|  | } | 
|  |  | 
|  | /* Another GEM workaround. Check peripheral device type, | 
|  | * if PROCESSOR, quit DV. | 
|  | */ | 
|  | if (inq0 == TYPE_PROCESSOR) { | 
|  | mptscsih_initTarget(hd, | 
|  | vtarget, | 
|  | lun, | 
|  | pbuf1, | 
|  | sz); | 
|  | goto target_done; | 
|  | } | 
|  |  | 
|  | if (inq0 > 0x08) | 
|  | goto target_done; | 
|  |  | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  |  | 
|  | if (sz == 0x40) { | 
|  | if ((vtarget->maxWidth == 1) && (vtarget->maxOffset) && (nfactor < 0x0A) | 
|  | && (vtarget->minSyncFactor > 0x09)) { | 
|  | if ((pbuf1[56] & 0x04) == 0) | 
|  | ; | 
|  | else if ((pbuf1[56] & 0x01) == 1) { | 
|  | vtarget->minSyncFactor = | 
|  | nfactor > MPT_ULTRA320 ? nfactor : MPT_ULTRA320; | 
|  | } else { | 
|  | vtarget->minSyncFactor = | 
|  | nfactor > MPT_ULTRA160 ? nfactor : MPT_ULTRA160; | 
|  | } | 
|  |  | 
|  | dv.max.factor = vtarget->minSyncFactor; | 
|  |  | 
|  | if ((pbuf1[56] & 0x02) == 0) { | 
|  | vtarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS; | 
|  | hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS; | 
|  | ddvprintk((MYIOC_s_NOTE_FMT | 
|  | "DV: Start Basic noQas on id=%d due to pbuf1[56]=%x\n", | 
|  | ioc->name, id, pbuf1[56])); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (doFallback) | 
|  | dv.cmd = MPT_FALLBACK; | 
|  | else | 
|  | dv.cmd = MPT_SET_MAX; | 
|  |  | 
|  | mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); | 
|  | if (mpt_config(hd->ioc, &cfg) != 0) | 
|  | goto target_done; | 
|  |  | 
|  | if ((!dv.now.width) && (!dv.now.offset)) | 
|  | goto target_done; | 
|  |  | 
|  | iocmd.cmd = INQUIRY; | 
|  | iocmd.data_dma = buf2_dma; | 
|  | iocmd.data = pbuf2; | 
|  | iocmd.size = sz; | 
|  | memset(pbuf2, 0x00, sz); | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  | else if (hd->pLocal == NULL) | 
|  | goto target_done; | 
|  | else { | 
|  | /* Save the return code. | 
|  | * If this is the first pass, | 
|  | * read SCSI Device Page 0 | 
|  | * and update the target max parameters. | 
|  | */ | 
|  | rc = hd->pLocal->completion; | 
|  | doFallback = 0; | 
|  | if (rc == MPT_SCANDV_GOOD) { | 
|  | if (!readPage0) { | 
|  | u32 sdp0_info; | 
|  | u32 sdp0_nego; | 
|  |  | 
|  | cfg.cfghdr.hdr = &header0; | 
|  | cfg.physAddr = cfg0_dma_addr; | 
|  | cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; | 
|  | cfg.dir = 0; | 
|  |  | 
|  | if (mpt_config(hd->ioc, &cfg) != 0) | 
|  | goto target_done; | 
|  |  | 
|  | sdp0_info = le32_to_cpu(pcfg0Data->Information) & 0x0E; | 
|  | sdp0_nego = (le32_to_cpu(pcfg0Data->NegotiatedParameters) & 0xFF00 ) >> 8; | 
|  |  | 
|  | /* Quantum and Fujitsu workarounds. | 
|  | * Quantum: PPR U320 -> PPR reply with Ultra2 and wide | 
|  | * Fujitsu: PPR U320 -> Msg Reject and Ultra2 and wide | 
|  | * Resetart with a request for U160. | 
|  | */ | 
|  | if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) { | 
|  | doFallback = 1; | 
|  | } else { | 
|  | dv.cmd = MPT_UPDATE_MAX; | 
|  | mptscsih_dv_parms(hd, &dv, (void *)pcfg0Data); | 
|  | /* Update the SCSI device page 1 area | 
|  | */ | 
|  | pcfg1Data->RequestedParameters = pcfg0Data->NegotiatedParameters; | 
|  | readPage0 = 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Quantum workaround. Restart this test will the fallback | 
|  | * flag set. | 
|  | */ | 
|  | if (doFallback == 0) { | 
|  | if (memcmp(pbuf1, pbuf2, sz) != 0) { | 
|  | if (!firstPass) | 
|  | doFallback = 1; | 
|  | } else { | 
|  | ddvprintk((MYIOC_s_NOTE_FMT | 
|  | "DV:Inquiry compared id=%d, calling initTarget\n", ioc->name, id)); | 
|  | hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_NOT_DONE; | 
|  | mptscsih_initTarget(hd, | 
|  | vtarget, | 
|  | lun, | 
|  | pbuf1, | 
|  | sz); | 
|  | break;	/* test complete */ | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | } else if (rc == MPT_SCANDV_ISSUE_SENSE) | 
|  | doFallback = 1;	/* set fallback flag */ | 
|  | else if ((rc == MPT_SCANDV_DID_RESET) || | 
|  | (rc == MPT_SCANDV_SENSE) || | 
|  | (rc == MPT_SCANDV_FALLBACK)) | 
|  | doFallback = 1;	/* set fallback flag */ | 
|  | else | 
|  | goto target_done; | 
|  |  | 
|  | firstPass = 0; | 
|  | } | 
|  | } | 
|  | ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test on id=%d completed OK.\n", ioc->name, id)); | 
|  |  | 
|  | if (ioc->spi_data.mpt_dv == 0) | 
|  | goto target_done; | 
|  |  | 
|  | inq0 = (*pbuf1) & 0x1F; | 
|  |  | 
|  | /* Continue only for disks | 
|  | */ | 
|  | if (inq0 != 0) | 
|  | goto target_done; | 
|  |  | 
|  | if ( ioc->spi_data.PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_BASIC_DV_ONLY ) | 
|  | goto target_done; | 
|  |  | 
|  | /* Start the Enhanced Test. | 
|  | * 0) issue TUR to clear out check conditions | 
|  | * 1) read capacity of echo (regular) buffer | 
|  | * 2) reserve device | 
|  | * 3) do write-read-compare data pattern test | 
|  | * 4) release | 
|  | * 5) update nego parms to target struct | 
|  | */ | 
|  | cfg.cfghdr.hdr = &header1; | 
|  | cfg.physAddr = cfg1_dma_addr; | 
|  | cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; | 
|  | cfg.dir = 1; | 
|  |  | 
|  | iocmd.cmd = TEST_UNIT_READY; | 
|  | iocmd.data_dma = -1; | 
|  | iocmd.data = NULL; | 
|  | iocmd.size = 0; | 
|  | notDone = 1; | 
|  | while (notDone) { | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  |  | 
|  | if (hd->pLocal == NULL) | 
|  | goto target_done; | 
|  |  | 
|  | rc = hd->pLocal->completion; | 
|  | if (rc == MPT_SCANDV_GOOD) | 
|  | notDone = 0; | 
|  | else if (rc == MPT_SCANDV_SENSE) { | 
|  | u8 skey = hd->pLocal->sense[2] & 0x0F; | 
|  | u8 asc = hd->pLocal->sense[12]; | 
|  | u8 ascq = hd->pLocal->sense[13]; | 
|  | ddvprintk((MYIOC_s_INFO_FMT | 
|  | "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", | 
|  | ioc->name, skey, asc, ascq)); | 
|  |  | 
|  | if (skey == UNIT_ATTENTION) | 
|  | notDone++; /* repeat */ | 
|  | else if ((skey == NOT_READY) && | 
|  | (asc == 0x04)&&(ascq == 0x01)) { | 
|  | /* wait then repeat */ | 
|  | mdelay (2000); | 
|  | notDone++; | 
|  | } else if ((skey == NOT_READY) && (asc == 0x3A)) { | 
|  | /* no medium, try read test anyway */ | 
|  | notDone = 0; | 
|  | } else { | 
|  | /* All other errors are fatal. | 
|  | */ | 
|  | ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.", | 
|  | ioc->name)); | 
|  | goto target_done; | 
|  | } | 
|  | } else | 
|  | goto target_done; | 
|  | } | 
|  |  | 
|  | iocmd.cmd = READ_BUFFER; | 
|  | iocmd.data_dma = buf1_dma; | 
|  | iocmd.data = pbuf1; | 
|  | iocmd.size = 4; | 
|  | iocmd.flags |= MPT_ICFLAG_BUF_CAP; | 
|  |  | 
|  | dataBufSize = 0; | 
|  | echoBufSize = 0; | 
|  | for (patt = 0; patt < 2; patt++) { | 
|  | if (patt == 0) | 
|  | iocmd.flags |= MPT_ICFLAG_ECHO; | 
|  | else | 
|  | iocmd.flags &= ~MPT_ICFLAG_ECHO; | 
|  |  | 
|  | notDone = 1; | 
|  | while (notDone) { | 
|  | bufsize = 0; | 
|  |  | 
|  | /* If not ready after 8 trials, | 
|  | * give up on this device. | 
|  | */ | 
|  | if (notDone > 8) | 
|  | goto target_done; | 
|  |  | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  | else if (hd->pLocal == NULL) | 
|  | goto target_done; | 
|  | else { | 
|  | rc = hd->pLocal->completion; | 
|  | ddvprintk(("ReadBuffer Comp Code %d", rc)); | 
|  | ddvprintk(("  buff: %0x %0x %0x %0x\n", | 
|  | pbuf1[0], pbuf1[1], pbuf1[2], pbuf1[3])); | 
|  |  | 
|  | if (rc == MPT_SCANDV_GOOD) { | 
|  | notDone = 0; | 
|  | if (iocmd.flags & MPT_ICFLAG_ECHO) { | 
|  | bufsize =  ((pbuf1[2] & 0x1F) <<8) | pbuf1[3]; | 
|  | if (pbuf1[0] & 0x01) | 
|  | iocmd.flags |= MPT_ICFLAG_EBOS; | 
|  | } else { | 
|  | bufsize =  pbuf1[1]<<16 | pbuf1[2]<<8 | pbuf1[3]; | 
|  | } | 
|  | } else if (rc == MPT_SCANDV_SENSE) { | 
|  | u8 skey = hd->pLocal->sense[2] & 0x0F; | 
|  | u8 asc = hd->pLocal->sense[12]; | 
|  | u8 ascq = hd->pLocal->sense[13]; | 
|  | ddvprintk((MYIOC_s_INFO_FMT | 
|  | "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", | 
|  | ioc->name, skey, asc, ascq)); | 
|  | if (skey == ILLEGAL_REQUEST) { | 
|  | notDone = 0; | 
|  | } else if (skey == UNIT_ATTENTION) { | 
|  | notDone++; /* repeat */ | 
|  | } else if ((skey == NOT_READY) && | 
|  | (asc == 0x04)&&(ascq == 0x01)) { | 
|  | /* wait then repeat */ | 
|  | mdelay (2000); | 
|  | notDone++; | 
|  | } else { | 
|  | /* All other errors are fatal. | 
|  | */ | 
|  | ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.", | 
|  | ioc->name)); | 
|  | goto target_done; | 
|  | } | 
|  | } else { | 
|  | /* All other errors are fatal | 
|  | */ | 
|  | goto target_done; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (iocmd.flags & MPT_ICFLAG_ECHO) | 
|  | echoBufSize = bufsize; | 
|  | else | 
|  | dataBufSize = bufsize; | 
|  | } | 
|  | sz = 0; | 
|  | iocmd.flags &= ~MPT_ICFLAG_BUF_CAP; | 
|  |  | 
|  | /* Use echo buffers if possible, | 
|  | * Exit if both buffers are 0. | 
|  | */ | 
|  | if (echoBufSize > 0) { | 
|  | iocmd.flags |= MPT_ICFLAG_ECHO; | 
|  | if (dataBufSize > 0) | 
|  | bufsize = min(echoBufSize, dataBufSize); | 
|  | else | 
|  | bufsize = echoBufSize; | 
|  | } else if (dataBufSize == 0) | 
|  | goto target_done; | 
|  |  | 
|  | ddvprintk((MYIOC_s_INFO_FMT "%s Buffer Capacity %d\n", ioc->name, | 
|  | (iocmd.flags & MPT_ICFLAG_ECHO) ? "Echo" : " ", bufsize)); | 
|  |  | 
|  | /* Data buffers for write-read-compare test max 1K. | 
|  | */ | 
|  | sz = min(bufsize, 1024); | 
|  |  | 
|  | /* --- loop ---- | 
|  | * On first pass, always issue a reserve. | 
|  | * On additional loops, only if a reset has occurred. | 
|  | * iocmd.flags indicates if echo or regular buffer | 
|  | */ | 
|  | for (patt = 0; patt < 4; patt++) { | 
|  | ddvprintk(("Pattern %d\n", patt)); | 
|  | if ((iocmd.flags & MPT_ICFLAG_RESERVED) && (iocmd.flags & MPT_ICFLAG_DID_RESET)) { | 
|  | iocmd.cmd = TEST_UNIT_READY; | 
|  | iocmd.data_dma = -1; | 
|  | iocmd.data = NULL; | 
|  | iocmd.size = 0; | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  |  | 
|  | iocmd.cmd = RELEASE; | 
|  | iocmd.data_dma = -1; | 
|  | iocmd.data = NULL; | 
|  | iocmd.size = 0; | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  | else if (hd->pLocal == NULL) | 
|  | goto target_done; | 
|  | else { | 
|  | rc = hd->pLocal->completion; | 
|  | ddvprintk(("Release rc %d\n", rc)); | 
|  | if (rc == MPT_SCANDV_GOOD) | 
|  | iocmd.flags &= ~MPT_ICFLAG_RESERVED; | 
|  | else | 
|  | goto target_done; | 
|  | } | 
|  | iocmd.flags &= ~MPT_ICFLAG_RESERVED; | 
|  | } | 
|  | iocmd.flags &= ~MPT_ICFLAG_DID_RESET; | 
|  |  | 
|  | if (iocmd.flags & MPT_ICFLAG_EBOS) | 
|  | goto skip_Reserve; | 
|  |  | 
|  | repeat = 5; | 
|  | while (repeat && (!(iocmd.flags & MPT_ICFLAG_RESERVED))) { | 
|  | iocmd.cmd = RESERVE; | 
|  | iocmd.data_dma = -1; | 
|  | iocmd.data = NULL; | 
|  | iocmd.size = 0; | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  | else if (hd->pLocal == NULL) | 
|  | goto target_done; | 
|  | else { | 
|  | rc = hd->pLocal->completion; | 
|  | if (rc == MPT_SCANDV_GOOD) { | 
|  | iocmd.flags |= MPT_ICFLAG_RESERVED; | 
|  | } else if (rc == MPT_SCANDV_SENSE) { | 
|  | /* Wait if coming ready | 
|  | */ | 
|  | u8 skey = hd->pLocal->sense[2] & 0x0F; | 
|  | u8 asc = hd->pLocal->sense[12]; | 
|  | u8 ascq = hd->pLocal->sense[13]; | 
|  | ddvprintk((MYIOC_s_INFO_FMT | 
|  | "DV: Reserve Failed: ", ioc->name)); | 
|  | ddvprintk(("SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", | 
|  | skey, asc, ascq)); | 
|  |  | 
|  | if ((skey == NOT_READY) && (asc == 0x04)&& | 
|  | (ascq == 0x01)) { | 
|  | /* wait then repeat */ | 
|  | mdelay (2000); | 
|  | notDone++; | 
|  | } else { | 
|  | ddvprintk((MYIOC_s_INFO_FMT | 
|  | "DV: Reserved Failed.", ioc->name)); | 
|  | goto target_done; | 
|  | } | 
|  | } else { | 
|  | ddvprintk((MYIOC_s_INFO_FMT "DV: Reserved Failed.", | 
|  | ioc->name)); | 
|  | goto target_done; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | skip_Reserve: | 
|  | mptscsih_fillbuf(pbuf1, sz, patt, 1); | 
|  | iocmd.cmd = WRITE_BUFFER; | 
|  | iocmd.data_dma = buf1_dma; | 
|  | iocmd.data = pbuf1; | 
|  | iocmd.size = sz; | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  | else if (hd->pLocal == NULL) | 
|  | goto target_done; | 
|  | else { | 
|  | rc = hd->pLocal->completion; | 
|  | if (rc == MPT_SCANDV_GOOD) | 
|  | ;		/* Issue read buffer */ | 
|  | else if (rc == MPT_SCANDV_DID_RESET) { | 
|  | /* If using echo buffers, reset to data buffers. | 
|  | * Else do Fallback and restart | 
|  | * this test (re-issue reserve | 
|  | * because of bus reset). | 
|  | */ | 
|  | if ((iocmd.flags & MPT_ICFLAG_ECHO) && (dataBufSize >= bufsize)) { | 
|  | iocmd.flags &= ~MPT_ICFLAG_ECHO; | 
|  | } else { | 
|  | dv.cmd = MPT_FALLBACK; | 
|  | mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); | 
|  |  | 
|  | if (mpt_config(hd->ioc, &cfg) != 0) | 
|  | goto target_done; | 
|  |  | 
|  | if ((!dv.now.width) && (!dv.now.offset)) | 
|  | goto target_done; | 
|  | } | 
|  |  | 
|  | iocmd.flags |= MPT_ICFLAG_DID_RESET; | 
|  | patt = -1; | 
|  | continue; | 
|  | } else if (rc == MPT_SCANDV_SENSE) { | 
|  | /* Restart data test if UA, else quit. | 
|  | */ | 
|  | u8 skey = hd->pLocal->sense[2] & 0x0F; | 
|  | ddvprintk((MYIOC_s_INFO_FMT | 
|  | "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey, | 
|  | hd->pLocal->sense[12], hd->pLocal->sense[13])); | 
|  | if (skey == UNIT_ATTENTION) { | 
|  | patt = -1; | 
|  | continue; | 
|  | } else if (skey == ILLEGAL_REQUEST) { | 
|  | if (iocmd.flags & MPT_ICFLAG_ECHO) { | 
|  | if (dataBufSize >= bufsize) { | 
|  | iocmd.flags &= ~MPT_ICFLAG_ECHO; | 
|  | patt = -1; | 
|  | continue; | 
|  | } | 
|  | } | 
|  | goto target_done; | 
|  | } | 
|  | else | 
|  | goto target_done; | 
|  | } else { | 
|  | /* fatal error */ | 
|  | goto target_done; | 
|  | } | 
|  | } | 
|  |  | 
|  | iocmd.cmd = READ_BUFFER; | 
|  | iocmd.data_dma = buf2_dma; | 
|  | iocmd.data = pbuf2; | 
|  | iocmd.size = sz; | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | goto target_done; | 
|  | else if (hd->pLocal == NULL) | 
|  | goto target_done; | 
|  | else { | 
|  | rc = hd->pLocal->completion; | 
|  | if (rc == MPT_SCANDV_GOOD) { | 
|  | /* If buffers compare, | 
|  | * go to next pattern, | 
|  | * else, do a fallback and restart | 
|  | * data transfer test. | 
|  | */ | 
|  | if (memcmp (pbuf1, pbuf2, sz) == 0) { | 
|  | ; /* goto next pattern */ | 
|  | } else { | 
|  | /* Miscompare with Echo buffer, go to data buffer, | 
|  | * if that buffer exists. | 
|  | * Miscompare with Data buffer, check first 4 bytes, | 
|  | * some devices return capacity. Exit in this case. | 
|  | */ | 
|  | if (iocmd.flags & MPT_ICFLAG_ECHO) { | 
|  | if (dataBufSize >= bufsize) | 
|  | iocmd.flags &= ~MPT_ICFLAG_ECHO; | 
|  | else | 
|  | goto target_done; | 
|  | } else { | 
|  | if (dataBufSize == (pbuf2[1]<<16 | pbuf2[2]<<8 | pbuf2[3])) { | 
|  | /* Argh. Device returning wrong data. | 
|  | * Quit DV for this device. | 
|  | */ | 
|  | goto target_done; | 
|  | } | 
|  |  | 
|  | /* Had an actual miscompare. Slow down.*/ | 
|  | dv.cmd = MPT_FALLBACK; | 
|  | mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); | 
|  |  | 
|  | if (mpt_config(hd->ioc, &cfg) != 0) | 
|  | goto target_done; | 
|  |  | 
|  | if ((!dv.now.width) && (!dv.now.offset)) | 
|  | goto target_done; | 
|  | } | 
|  |  | 
|  | patt = -1; | 
|  | continue; | 
|  | } | 
|  | } else if (rc == MPT_SCANDV_DID_RESET) { | 
|  | /* Do Fallback and restart | 
|  | * this test (re-issue reserve | 
|  | * because of bus reset). | 
|  | */ | 
|  | dv.cmd = MPT_FALLBACK; | 
|  | mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); | 
|  |  | 
|  | if (mpt_config(hd->ioc, &cfg) != 0) | 
|  | goto target_done; | 
|  |  | 
|  | if ((!dv.now.width) && (!dv.now.offset)) | 
|  | goto target_done; | 
|  |  | 
|  | iocmd.flags |= MPT_ICFLAG_DID_RESET; | 
|  | patt = -1; | 
|  | continue; | 
|  | } else if (rc == MPT_SCANDV_SENSE) { | 
|  | /* Restart data test if UA, else quit. | 
|  | */ | 
|  | u8 skey = hd->pLocal->sense[2] & 0x0F; | 
|  | ddvprintk((MYIOC_s_INFO_FMT | 
|  | "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey, | 
|  | hd->pLocal->sense[12], hd->pLocal->sense[13])); | 
|  | if (skey == UNIT_ATTENTION) { | 
|  | patt = -1; | 
|  | continue; | 
|  | } | 
|  | else | 
|  | goto target_done; | 
|  | } else { | 
|  | /* fatal error */ | 
|  | goto target_done; | 
|  | } | 
|  | } | 
|  |  | 
|  | } /* --- end of patt loop ---- */ | 
|  |  | 
|  | target_done: | 
|  | if (iocmd.flags & MPT_ICFLAG_RESERVED) { | 
|  | iocmd.cmd = RELEASE; | 
|  | iocmd.data_dma = -1; | 
|  | iocmd.data = NULL; | 
|  | iocmd.size = 0; | 
|  | if (mptscsih_do_cmd(hd, &iocmd) < 0) | 
|  | printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d", | 
|  | ioc->name, id); | 
|  | else if (hd->pLocal) { | 
|  | if (hd->pLocal->completion == MPT_SCANDV_GOOD) | 
|  | iocmd.flags &= ~MPT_ICFLAG_RESERVED; | 
|  | } else { | 
|  | printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d", | 
|  | ioc->name, id); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Set if cfg1_dma_addr contents is valid | 
|  | */ | 
|  | if ((cfg.cfghdr.hdr != NULL) && (retcode == 0)){ | 
|  | /* If disk, not U320, disable QAS | 
|  | */ | 
|  | if ((inq0 == 0) && (dv.now.factor > MPT_ULTRA320)) { | 
|  | hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS; | 
|  | ddvprintk((MYIOC_s_NOTE_FMT | 
|  | "noQas set due to id=%d has factor=%x\n", ioc->name, id, dv.now.factor)); | 
|  | } | 
|  |  | 
|  | dv.cmd = MPT_SAVE; | 
|  | mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); | 
|  |  | 
|  | /* Double writes to SDP1 can cause problems, | 
|  | * skip save of the final negotiated settings to | 
|  | * SCSI device page 1. | 
|  | * | 
|  | cfg.cfghdr.hdr = &header1; | 
|  | cfg.physAddr = cfg1_dma_addr; | 
|  | cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; | 
|  | cfg.dir = 1; | 
|  | mpt_config(hd->ioc, &cfg); | 
|  | */ | 
|  | } | 
|  |  | 
|  | /* If this is a RAID Passthrough, enable internal IOs | 
|  | */ | 
|  | if (iocmd.flags & MPT_ICFLAG_PHYS_DISK) { | 
|  | if (mptscsih_do_raid(hd, MPI_RAID_ACTION_ENABLE_PHYS_IO, &iocmd) < 0) | 
|  | ddvprintk((MYIOC_s_ERR_FMT "RAID Enable FAILED!\n", ioc->name)); | 
|  | } | 
|  |  | 
|  | /* Done with the DV scan of the current target | 
|  | */ | 
|  | if (pDvBuf) | 
|  | pci_free_consistent(ioc->pcidev, dv_alloc, pDvBuf, dvbuf_dma); | 
|  |  | 
|  | ddvtprintk((MYIOC_s_INFO_FMT "DV Done id=%d\n", | 
|  | ioc->name, id)); | 
|  |  | 
|  | return retcode; | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /*	mptscsih_dv_parms - perform a variety of operations on the | 
|  | *	parameters used for negotiation. | 
|  | *	@hd: Pointer to a SCSI host. | 
|  | *	@dv: Pointer to a structure that contains the maximum and current | 
|  | *		negotiated parameters. | 
|  | */ | 
|  | static void | 
|  | mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage) | 
|  | { | 
|  | VirtTarget		*vtarget; | 
|  | SCSIDevicePage0_t	*pPage0; | 
|  | SCSIDevicePage1_t	*pPage1; | 
|  | int			val = 0, data, configuration; | 
|  | u8			width = 0; | 
|  | u8			offset = 0; | 
|  | u8			factor = 0; | 
|  | u8			negoFlags = 0; | 
|  | u8			cmd = dv->cmd; | 
|  | u8			id = dv->id; | 
|  |  | 
|  | switch (cmd) { | 
|  | case MPT_GET_NVRAM_VALS: | 
|  | ddvprintk((MYIOC_s_NOTE_FMT "Getting NVRAM: ", | 
|  | hd->ioc->name)); | 
|  | /* Get the NVRAM values and save in tmax | 
|  | * If not an LVD bus, the adapter minSyncFactor has been | 
|  | * already throttled back. | 
|  | */ | 
|  | negoFlags = hd->ioc->spi_data.noQas; | 
|  | if ((hd->Targets)&&((vtarget = hd->Targets[(int)id]) != NULL) && !vtarget->raidVolume) { | 
|  | width = vtarget->maxWidth; | 
|  | offset = vtarget->maxOffset; | 
|  | factor = vtarget->minSyncFactor; | 
|  | negoFlags |= vtarget->negoFlags; | 
|  | } else { | 
|  | if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) { | 
|  | data = hd->ioc->spi_data.nvram[id]; | 
|  | width = data & MPT_NVRAM_WIDE_DISABLE ? 0 : 1; | 
|  | if ((offset = hd->ioc->spi_data.maxSyncOffset) == 0) | 
|  | factor = MPT_ASYNC; | 
|  | else { | 
|  | factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT; | 
|  | if ((factor == 0) || (factor == MPT_ASYNC)){ | 
|  | factor = MPT_ASYNC; | 
|  | offset = 0; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | width = MPT_NARROW; | 
|  | offset = 0; | 
|  | factor = MPT_ASYNC; | 
|  | } | 
|  |  | 
|  | /* Set the negotiation flags */ | 
|  | if (!width) | 
|  | negoFlags |= MPT_TARGET_NO_NEGO_WIDE; | 
|  |  | 
|  | if (!offset) | 
|  | negoFlags |= MPT_TARGET_NO_NEGO_SYNC; | 
|  | } | 
|  |  | 
|  | /* limit by adapter capabilities */ | 
|  | width = min(width, hd->ioc->spi_data.maxBusWidth); | 
|  | offset = min(offset, hd->ioc->spi_data.maxSyncOffset); | 
|  | factor = max(factor, hd->ioc->spi_data.minSyncFactor); | 
|  |  | 
|  | /* Check Consistency */ | 
|  | if (offset && (factor < MPT_ULTRA2) && !width) | 
|  | factor = MPT_ULTRA2; | 
|  |  | 
|  | dv->max.width = width; | 
|  | dv->max.offset = offset; | 
|  | dv->max.factor = factor; | 
|  | dv->max.flags = negoFlags; | 
|  | ddvprintk((" id=%d width=%d factor=%x offset=%x flags=%x\n", | 
|  | id, width, factor, offset, negoFlags)); | 
|  | break; | 
|  |  | 
|  | case MPT_UPDATE_MAX: | 
|  | ddvprintk((MYIOC_s_NOTE_FMT | 
|  | "Updating with SDP0 Data: ", hd->ioc->name)); | 
|  | /* Update tmax values with those from Device Page 0.*/ | 
|  | pPage0 = (SCSIDevicePage0_t *) pPage; | 
|  | if (pPage0) { | 
|  | val = le32_to_cpu(pPage0->NegotiatedParameters); | 
|  | dv->max.width = val & MPI_SCSIDEVPAGE0_NP_WIDE ? 1 : 0; | 
|  | dv->max.offset = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK) >> 16; | 
|  | dv->max.factor = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK) >> 8; | 
|  | } | 
|  |  | 
|  | dv->now.width = dv->max.width; | 
|  | dv->now.offset = dv->max.offset; | 
|  | dv->now.factor = dv->max.factor; | 
|  | ddvprintk(("id=%d width=%d factor=%x offset=%x flags=%x\n", | 
|  | id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags)); | 
|  | break; | 
|  |  | 
|  | case MPT_SET_MAX: | 
|  | ddvprintk((MYIOC_s_NOTE_FMT "Setting Max: ", | 
|  | hd->ioc->name)); | 
|  | /* Set current to the max values. Update the config page.*/ | 
|  | dv->now.width = dv->max.width; | 
|  | dv->now.offset = dv->max.offset; | 
|  | dv->now.factor = dv->max.factor; | 
|  | dv->now.flags = dv->max.flags; | 
|  |  | 
|  | pPage1 = (SCSIDevicePage1_t *)pPage; | 
|  | if (pPage1) { | 
|  | mptscsih_setDevicePage1Flags (dv->now.width, dv->now.factor, | 
|  | dv->now.offset, &val, &configuration, dv->now.flags); | 
|  | dnegoprintk(("Setting Max: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n", | 
|  | id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration)); | 
|  | pPage1->RequestedParameters = cpu_to_le32(val); | 
|  | pPage1->Reserved = 0; | 
|  | pPage1->Configuration = cpu_to_le32(configuration); | 
|  | } | 
|  |  | 
|  | ddvprintk(("id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x configuration=%x\n", | 
|  | id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration)); | 
|  | break; | 
|  |  | 
|  | case MPT_SET_MIN: | 
|  | ddvprintk((MYIOC_s_NOTE_FMT "Setting Min: ", | 
|  | hd->ioc->name)); | 
|  | /* Set page to asynchronous and narrow | 
|  | * Do not update now, breaks fallback routine. */ | 
|  | width = MPT_NARROW; | 
|  | offset = 0; | 
|  | factor = MPT_ASYNC; | 
|  | negoFlags = dv->max.flags; | 
|  |  | 
|  | pPage1 = (SCSIDevicePage1_t *)pPage; | 
|  | if (pPage1) { | 
|  | mptscsih_setDevicePage1Flags (width, factor, | 
|  | offset, &val, &configuration, negoFlags); | 
|  | dnegoprintk(("Setting Min: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n", | 
|  | id, width, factor, offset, negoFlags, val, configuration)); | 
|  | pPage1->RequestedParameters = cpu_to_le32(val); | 
|  | pPage1->Reserved = 0; | 
|  | pPage1->Configuration = cpu_to_le32(configuration); | 
|  | } | 
|  | ddvprintk(("id=%d width=%d factor=%x offset=%x request=%x config=%x negoFlags=%x\n", | 
|  | id, width, factor, offset, val, configuration, negoFlags)); | 
|  | break; | 
|  |  | 
|  | case MPT_FALLBACK: | 
|  | ddvprintk((MYIOC_s_NOTE_FMT | 
|  | "Fallback: Start: offset %d, factor %x, width %d \n", | 
|  | hd->ioc->name, dv->now.offset, | 
|  | dv->now.factor, dv->now.width)); | 
|  | width = dv->now.width; | 
|  | offset = dv->now.offset; | 
|  | factor = dv->now.factor; | 
|  | if ((offset) && (dv->max.width)) { | 
|  | if (factor < MPT_ULTRA160) | 
|  | factor = MPT_ULTRA160; | 
|  | else if (factor < MPT_ULTRA2) { | 
|  | factor = MPT_ULTRA2; | 
|  | width = MPT_WIDE; | 
|  | } else if ((factor == MPT_ULTRA2) && width) { | 
|  | factor = MPT_ULTRA2; | 
|  | width = MPT_NARROW; | 
|  | } else if (factor < MPT_ULTRA) { | 
|  | factor = MPT_ULTRA; | 
|  | width = MPT_WIDE; | 
|  | } else if ((factor == MPT_ULTRA) && width) { | 
|  | width = MPT_NARROW; | 
|  | } else if (factor < MPT_FAST) { | 
|  | factor = MPT_FAST; | 
|  | width = MPT_WIDE; | 
|  | } else if ((factor == MPT_FAST) && width) { | 
|  | factor = MPT_FAST; | 
|  | width = MPT_NARROW; | 
|  | } else if (factor < MPT_SCSI) { | 
|  | factor = MPT_SCSI; | 
|  | width = MPT_WIDE; | 
|  | } else if ((factor == MPT_SCSI) && width) { | 
|  | factor = MPT_SCSI; | 
|  | width = MPT_NARROW; | 
|  | } else { | 
|  | factor = MPT_ASYNC; | 
|  | offset = 0; | 
|  | } | 
|  |  | 
|  | } else if (offset) { | 
|  | width = MPT_NARROW; | 
|  | if (factor < MPT_ULTRA) | 
|  | factor = MPT_ULTRA; | 
|  | else if (factor < MPT_FAST) | 
|  | factor = MPT_FAST; | 
|  | else if (factor < MPT_SCSI) | 
|  | factor = MPT_SCSI; | 
|  | else { | 
|  | factor = MPT_ASYNC; | 
|  | offset = 0; | 
|  | } | 
|  |  | 
|  | } else { | 
|  | width = MPT_NARROW; | 
|  | factor = MPT_ASYNC; | 
|  | } | 
|  | dv->max.flags |= MPT_TARGET_NO_NEGO_QAS; | 
|  | dv->max.flags &= ~MPT_TAPE_NEGO_IDP; | 
|  |  | 
|  | dv->now.width = width; | 
|  | dv->now.offset = offset; | 
|  | dv->now.factor = factor; | 
|  | dv->now.flags = dv->max.flags; | 
|  |  | 
|  | pPage1 = (SCSIDevicePage1_t *)pPage; | 
|  | if (pPage1) { | 
|  | mptscsih_setDevicePage1Flags (width, factor, offset, &val, | 
|  | &configuration, dv->now.flags); | 
|  | dnegoprintk(("Finish: id=%d width=%d offset=%d factor=%x negoFlags=%x request=%x config=%x\n", | 
|  | id, width, offset, factor, dv->now.flags, val, configuration)); | 
|  |  | 
|  | pPage1->RequestedParameters = cpu_to_le32(val); | 
|  | pPage1->Reserved = 0; | 
|  | pPage1->Configuration = cpu_to_le32(configuration); | 
|  | } | 
|  |  | 
|  | ddvprintk(("Finish: id=%d offset=%d factor=%x width=%d request=%x config=%x\n", | 
|  | id, dv->now.offset, dv->now.factor, dv->now.width, val, configuration)); | 
|  | break; | 
|  |  | 
|  | case MPT_SAVE: | 
|  | ddvprintk((MYIOC_s_NOTE_FMT | 
|  | "Saving to Target structure: ", hd->ioc->name)); | 
|  | ddvprintk(("id=%d width=%x factor=%x offset=%d flags=%x\n", | 
|  | id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags)); | 
|  |  | 
|  | /* Save these values to target structures | 
|  | * or overwrite nvram (phys disks only). | 
|  | */ | 
|  |  | 
|  | if ((hd->Targets)&&((vtarget = hd->Targets[(int)id]) != NULL) && !vtarget->raidVolume ) { | 
|  | vtarget->maxWidth = dv->now.width; | 
|  | vtarget->maxOffset = dv->now.offset; | 
|  | vtarget->minSyncFactor = dv->now.factor; | 
|  | vtarget->negoFlags = dv->now.flags; | 
|  | } else { | 
|  | /* Preserv all flags, use | 
|  | * read-modify-write algorithm | 
|  | */ | 
|  | if (hd->ioc->spi_data.nvram) { | 
|  | data = hd->ioc->spi_data.nvram[id]; | 
|  |  | 
|  | if (dv->now.width) | 
|  | data &= ~MPT_NVRAM_WIDE_DISABLE; | 
|  | else | 
|  | data |= MPT_NVRAM_WIDE_DISABLE; | 
|  |  | 
|  | if (!dv->now.offset) | 
|  | factor = MPT_ASYNC; | 
|  |  | 
|  | data &= ~MPT_NVRAM_SYNC_MASK; | 
|  | data |= (dv->now.factor << MPT_NVRAM_SYNC_SHIFT) & MPT_NVRAM_SYNC_MASK; | 
|  |  | 
|  | hd->ioc->spi_data.nvram[id] = data; | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /*	mptscsih_fillbuf - fill a buffer with a special data pattern | 
|  | *		cleanup. For bus scan only. | 
|  | * | 
|  | *	@buffer: Pointer to data buffer to be filled. | 
|  | *	@size: Number of bytes to fill | 
|  | *	@index: Pattern index | 
|  | *	@width: bus width, 0 (8 bits) or 1 (16 bits) | 
|  | */ | 
|  | static void | 
|  | mptscsih_fillbuf(char *buffer, int size, int index, int width) | 
|  | { | 
|  | char *ptr = buffer; | 
|  | int ii; | 
|  | char byte; | 
|  | short val; | 
|  |  | 
|  | switch (index) { | 
|  | case 0: | 
|  |  | 
|  | if (width) { | 
|  | /* Pattern:  0000 FFFF 0000 FFFF | 
|  | */ | 
|  | for (ii=0; ii < size; ii++, ptr++) { | 
|  | if (ii & 0x02) | 
|  | *ptr = 0xFF; | 
|  | else | 
|  | *ptr = 0x00; | 
|  | } | 
|  | } else { | 
|  | /* Pattern:  00 FF 00 FF | 
|  | */ | 
|  | for (ii=0; ii < size; ii++, ptr++) { | 
|  | if (ii & 0x01) | 
|  | *ptr = 0xFF; | 
|  | else | 
|  | *ptr = 0x00; | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 1: | 
|  | if (width) { | 
|  | /* Pattern:  5555 AAAA 5555 AAAA 5555 | 
|  | */ | 
|  | for (ii=0; ii < size; ii++, ptr++) { | 
|  | if (ii & 0x02) | 
|  | *ptr = 0xAA; | 
|  | else | 
|  | *ptr = 0x55; | 
|  | } | 
|  | } else { | 
|  | /* Pattern:  55 AA 55 AA 55 | 
|  | */ | 
|  | for (ii=0; ii < size; ii++, ptr++) { | 
|  | if (ii & 0x01) | 
|  | *ptr = 0xAA; | 
|  | else | 
|  | *ptr = 0x55; | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 2: | 
|  | /* Pattern:  00 01 02 03 04 05 | 
|  | * ... FE FF 00 01.. | 
|  | */ | 
|  | for (ii=0; ii < size; ii++, ptr++) | 
|  | *ptr = (char) ii; | 
|  | break; | 
|  |  | 
|  | case 3: | 
|  | if (width) { | 
|  | /* Wide Pattern:  FFFE 0001 FFFD 0002 | 
|  | * ...  4000 DFFF 8000 EFFF | 
|  | */ | 
|  | byte = 0; | 
|  | for (ii=0; ii < size/2; ii++) { | 
|  | /* Create the base pattern | 
|  | */ | 
|  | val = (1 << byte); | 
|  | /* every 64 (0x40) bytes flip the pattern | 
|  | * since we fill 2 bytes / iteration, | 
|  | * test for ii = 0x20 | 
|  | */ | 
|  | if (ii & 0x20) | 
|  | val = ~(val); | 
|  |  | 
|  | if (ii & 0x01) { | 
|  | *ptr = (char)( (val & 0xFF00) >> 8); | 
|  | ptr++; | 
|  | *ptr = (char)(val & 0xFF); | 
|  | byte++; | 
|  | byte &= 0x0F; | 
|  | } else { | 
|  | val = ~val; | 
|  | *ptr = (char)( (val & 0xFF00) >> 8); | 
|  | ptr++; | 
|  | *ptr = (char)(val & 0xFF); | 
|  | } | 
|  |  | 
|  | ptr++; | 
|  | } | 
|  | } else { | 
|  | /* Narrow Pattern:  FE 01 FD 02 FB 04 | 
|  | * .. 7F 80 01 FE 02 FD ...  80 7F | 
|  | */ | 
|  | byte = 0; | 
|  | for (ii=0; ii < size; ii++, ptr++) { | 
|  | /* Base pattern - first 32 bytes | 
|  | */ | 
|  | if (ii & 0x01) { | 
|  | *ptr = (1 << byte); | 
|  | byte++; | 
|  | byte &= 0x07; | 
|  | } else { | 
|  | *ptr = (char) (~(1 << byte)); | 
|  | } | 
|  |  | 
|  | /* Flip the pattern every 32 bytes | 
|  | */ | 
|  | if (ii & 0x20) | 
|  | *ptr = ~(*ptr); | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
|  | /* If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return. | 
|  | * Else set the NEED_DV flag after Read Capacity Issued (disks) | 
|  | * or Mode Sense (cdroms). | 
|  | * | 
|  | * Tapes, initTarget will set this flag on completion of Inquiry command. | 
|  | * Called only if DV_NOT_DONE flag is set | 
|  | */ | 
|  | static void | 
|  | mptscsih_set_dvflags(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc) | 
|  | { | 
|  | MPT_ADAPTER	*ioc = hd->ioc; | 
|  | u8 cmd; | 
|  | SpiCfgData	*pSpi; | 
|  |  | 
|  | ddvtprintk((MYIOC_s_NOTE_FMT | 
|  | " set_dvflags: id=%d lun=%d negoNvram=%x cmd=%x\n", | 
|  | hd->ioc->name, sc->device->id, sc->device->lun , hd->negoNvram, sc->cmnd[0])); | 
|  |  | 
|  | if ((sc->device->lun != 0) || (hd->negoNvram != 0)) | 
|  | return; | 
|  |  | 
|  | cmd = sc->cmnd[0]; | 
|  |  | 
|  | if ((cmd == READ_CAPACITY) || (cmd == MODE_SENSE)) { | 
|  | pSpi = &ioc->spi_data; | 
|  | if ((ioc->raid_data.isRaid & (1 << sc->device->id)) && ioc->raid_data.pIocPg3) { | 
|  | /* Set NEED_DV for all hidden disks | 
|  | */ | 
|  | Ioc3PhysDisk_t *pPDisk =  ioc->raid_data.pIocPg3->PhysDisk; | 
|  | int		numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks; | 
|  |  | 
|  | while (numPDisk) { | 
|  | pSpi->dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV; | 
|  | ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID)); | 
|  | pPDisk++; | 
|  | numPDisk--; | 
|  | } | 
|  | } | 
|  | pSpi->dvStatus[sc->device->id] |= MPT_SCSICFG_NEED_DV; | 
|  | ddvtprintk(("NEED_DV set for visible disk id %d\n", sc->device->id)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* mptscsih_raid_set_dv_flags() | 
|  | * | 
|  | * New or replaced disk. Set DV flag and schedule DV. | 
|  | */ | 
|  | static void | 
|  | mptscsih_set_dvflags_raid(MPT_SCSI_HOST *hd, int id) | 
|  | { | 
|  | MPT_ADAPTER	*ioc = hd->ioc; | 
|  | SpiCfgData	*pSpi = &ioc->spi_data; | 
|  | Ioc3PhysDisk_t	*pPDisk; | 
|  | int		 numPDisk; | 
|  |  | 
|  | if (hd->negoNvram != 0) | 
|  | return; | 
|  |  | 
|  | ddvtprintk(("DV requested for phys disk id %d\n", id)); | 
|  | if (ioc->raid_data.pIocPg3) { | 
|  | pPDisk =  ioc->raid_data.pIocPg3->PhysDisk; | 
|  | numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks; | 
|  | while (numPDisk) { | 
|  | if (id == pPDisk->PhysDiskNum) { | 
|  | pSpi->dvStatus[pPDisk->PhysDiskID] = | 
|  | (MPT_SCSICFG_NEED_DV | MPT_SCSICFG_DV_NOT_DONE); | 
|  | pSpi->forceDv = MPT_SCSICFG_NEED_DV; | 
|  | ddvtprintk(("NEED_DV set for phys disk id %d\n", | 
|  | pPDisk->PhysDiskID)); | 
|  | break; | 
|  | } | 
|  | pPDisk++; | 
|  | numPDisk--; | 
|  | } | 
|  |  | 
|  | if (numPDisk == 0) { | 
|  | /* The physical disk that needs DV was not found | 
|  | * in the stored IOC Page 3. The driver must reload | 
|  | * this page. DV routine will set the NEED_DV flag for | 
|  | * all phys disks that have DV_NOT_DONE set. | 
|  | */ | 
|  | pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3; | 
|  | ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n",id)); | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */ | 
|  |  | 
|  | EXPORT_SYMBOL(mptscsih_remove); | 
|  | EXPORT_SYMBOL(mptscsih_shutdown); | 
|  | #ifdef CONFIG_PM | 
|  | EXPORT_SYMBOL(mptscsih_suspend); | 
|  | EXPORT_SYMBOL(mptscsih_resume); | 
|  | #endif | 
|  | EXPORT_SYMBOL(mptscsih_proc_info); | 
|  | EXPORT_SYMBOL(mptscsih_info); | 
|  | EXPORT_SYMBOL(mptscsih_qcmd); | 
|  | EXPORT_SYMBOL(mptscsih_target_alloc); | 
|  | EXPORT_SYMBOL(mptscsih_slave_alloc); | 
|  | EXPORT_SYMBOL(mptscsih_target_destroy); | 
|  | EXPORT_SYMBOL(mptscsih_slave_destroy); | 
|  | EXPORT_SYMBOL(mptscsih_slave_configure); | 
|  | EXPORT_SYMBOL(mptscsih_abort); | 
|  | EXPORT_SYMBOL(mptscsih_dev_reset); | 
|  | EXPORT_SYMBOL(mptscsih_bus_reset); | 
|  | EXPORT_SYMBOL(mptscsih_host_reset); | 
|  | EXPORT_SYMBOL(mptscsih_bios_param); | 
|  | EXPORT_SYMBOL(mptscsih_io_done); | 
|  | EXPORT_SYMBOL(mptscsih_taskmgmt_complete); | 
|  | EXPORT_SYMBOL(mptscsih_scandv_complete); | 
|  | EXPORT_SYMBOL(mptscsih_event_process); | 
|  | EXPORT_SYMBOL(mptscsih_ioc_reset); | 
|  | EXPORT_SYMBOL(mptscsih_change_queue_depth); | 
|  | EXPORT_SYMBOL(mptscsih_timer_expired); | 
|  |  | 
|  | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ |