diff options
Diffstat (limited to 'private/ntos/miniport/wd33c93')
-rw-r--r-- | private/ntos/miniport/wd33c93/makefile | 6 | ||||
-rw-r--r-- | private/ntos/miniport/wd33c93/maynard.h | 173 | ||||
-rw-r--r-- | private/ntos/miniport/wd33c93/s3scsi.h | 145 | ||||
-rw-r--r-- | private/ntos/miniport/wd33c93/sources | 37 | ||||
-rw-r--r-- | private/ntos/miniport/wd33c93/vendor.h | 26 | ||||
-rw-r--r-- | private/ntos/miniport/wd33c93/wd33c93.c | 5946 | ||||
-rw-r--r-- | private/ntos/miniport/wd33c93/wd33c93.h | 305 | ||||
-rw-r--r-- | private/ntos/miniport/wd33c93/wd33c93.rc | 12 |
8 files changed, 6650 insertions, 0 deletions
diff --git a/private/ntos/miniport/wd33c93/makefile b/private/ntos/miniport/wd33c93/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/miniport/wd33c93/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/miniport/wd33c93/maynard.h b/private/ntos/miniport/wd33c93/maynard.h new file mode 100644 index 000000000..9e6576549 --- /dev/null +++ b/private/ntos/miniport/wd33c93/maynard.h @@ -0,0 +1,173 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + maynard.h + +Abstract: + + The module defines the structures, and defines for the Maynard 16-bit + host bus adapter card. + +Author: + + Jeff Havens (jhavens) 20-June-1991 + +Revision History: + + +--*/ + +#ifndef _MAYNARD_ +#define _MAYNARD_ + +// +// Define the SCSI host adapter card register structure. +// + +typedef struct _CARD_REGISTERS { + UCHAR InternalAddress; + UCHAR IoChannel; + UCHAR CardControl; + UCHAR IoChannel2; + UCHAR SetWriteLogic; + UCHAR SetReadLogic; + UCHAR Unused; +}CARD_REGISTERS, *PCARD_REGISTERS; + +// +// Define the SCSI host adapter card control register structure. +// + +typedef struct _CARD_CONTROL { + UCHAR ResetScsiBus : 1; + UCHAR EnableDmaWrite : 1; + UCHAR SetBufferedIo : 1; + UCHAR SetIrql : 3; + UCHAR SetDmaRequest : 2; +}CARD_CONTROL, *PCARD_CONTROL; + +// +// Specify board dependent parameters. +// + +#define CLOCK_CONVERSION_FACTOR 0x0 +#define SYNCHRONOUS_OFFSET 0x0c +#define SYNCHRONOUS_PERIOD 0x32 +#define SYNCHRONOUS_PERIOD_STEP 0x32 +#define ASYNCHRONOUS_OFFSET 0x00 +#define ASYNCHRONOUS_PERIOD 0X02 +#define SCSI_CLOCK_SPEED 10 // Clock speed of the WD protocol chip in MHz. +#define SELECT_TIMEOUT_VALUE (SCSI_CLOCK_SPEED * 4) // = (Input clock * 250ms timeout) / 80 rounded up. +#define CARD_DMA_MODE DMA_NORMAL + +// +// SCSI Protocol Chip Control read and write macros. +// + +#define SCSI_READ(ChipAddr, Register) ( \ + ScsiPortWritePortUchar(&((ChipAddr)->InternalAddress), \ + (UCHAR) &((PSCSI_REGISTERS) 0)->Register), \ + ScsiPortReadPortUchar(&((ChipAddr)->IoChannel))) + +#define SCSI_READ_AUX(ChipAddr, Register) (ScsiPortReadPortUchar (&((ChipAddr)->InternalAddress))) + +#define SCSI_WRITE(ChipAddr, Register, Value) { \ + ScsiPortWritePortUchar(&((ChipAddr)->InternalAddress), \ + (UCHAR) &((PSCSI_REGISTERS) 0)->Register); \ + ScsiPortWritePortUchar(&((ChipAddr)->IoChannel), (Value)); } + +#define SCSI_READ_TRANSFER_COUNT(ChipAddr, Count) { \ + ScsiPortWritePortUchar(&((ChipAddr)->InternalAddress), \ + (UCHAR) &((PSCSI_REGISTERS) 0)->TransferCountMsb); \ + Count = ScsiPortReadPortUchar(&((ChipAddr)->IoChannel)) << 16; \ + Count |= ScsiPortReadPortUchar(&((ChipAddr)->IoChannel)) << 8 ; \ + Count |= ScsiPortReadPortUchar(&((ChipAddr)->IoChannel)); } + +#define SCSI_WRITE_TRANSFER_COUNT(ChipAddr, Count) { \ + SCSI_WRITE(ChipAddr, TransferCountMsb, (UCHAR)(Count >> 16)); \ + ScsiPortWritePortUchar(&((ChipAddr)->IoChannel), (UCHAR)(Count >> 8)); \ + ScsiPortWritePortUchar(&((ChipAddr)->IoChannel), (UCHAR)Count); } + +#define SCSI_RESET_BUS(DeviceExtension) { CARD_CONTROL CardControl; \ + *((PUCHAR) &CardControl) = 0; CardControl.ResetScsiBus = 1; \ + CardControl.SetIrql = DeviceExtension->IrqCode; \ + CardControl.SetDmaRequest = DeviceExtension->DmaCode; \ + ScsiPortWritePortUchar(&((DeviceExtension->Adapter)->CardControl), *((PUCHAR) & CardControl)); \ + ScsiPortStallExecution( 25 ); CardControl.ResetScsiBus = 1; \ + ScsiPortWritePortUchar(&((DeviceExtension->Adapter)->CardControl), *((PUCHAR) & CardControl)); \ + } + +#define CARD_INITIALIZE(DeviceExtension) { CARD_CONTROL CardControl; \ + *((PUCHAR) &CardControl) = 0; \ + CardControl.SetIrql = DeviceExtension->IrqCode; \ + CardControl.SetDmaRequest = DeviceExtension->DmaCode; \ + ScsiPortWritePortUchar(&((DeviceExtension->Adapter)->CardControl), \ + *((PUCHAR) & CardControl)); } + +#define CARD_DMA_INITIATE(DeviceExtension, ToDevice) {\ + SCSI_CONTROL ScsiControl;\ + CARD_CONTROL CardControl;\ + *((PUCHAR) &CardControl) = 0;\ + CardControl.EnableDmaWrite = ToDevice ? 0 : 1;\ + CardControl.SetIrql = DeviceExtension->IrqCode;\ + CardControl.SetDmaRequest = DeviceExtension->DmaCode;\ + ScsiPortWritePortUchar(&((DeviceExtension->Adapter)->CardControl), *((PUCHAR)&CardControl));\ + ScsiPortWritePortUchar(ToDevice ? &((DeviceExtension->Adapter)->SetReadLogic) : &((DeviceExtension->Adapter)->SetWriteLogic), TRUE);\ + ScsiPortWritePortUchar(&((DeviceExtension->Adapter)->InternalAddress), (UCHAR)(&(((PSCSI_REGISTERS)0)->Control)));\ + *((PUCHAR)&ScsiControl) = ScsiPortReadPortUchar(&((DeviceExtension->Adapter)->IoChannel));\ + ScsiControl.DmaModeSelect = DMA_NORMAL;\ + ScsiPortWritePortUchar(&((DeviceExtension->Adapter)->InternalAddress), (UCHAR)(&(((PSCSI_REGISTERS)0)->Control)));\ + ScsiPortWritePortUchar(&((DeviceExtension->Adapter)->IoChannel), *((PUCHAR)&ScsiControl));} + +#define CARD_DMA_TERMINATE(DeviceExtension) {\ + SCSI_CONTROL ScsiControl;\ + ScsiPortWritePortUchar(&((DeviceExtension->Adapter)->InternalAddress), (UCHAR)(&(((PSCSI_REGISTERS)0)->Control)));\ + *((PUCHAR)&ScsiControl) = ScsiPortReadPortUchar(&((DeviceExtension->Adapter)->IoChannel));\ + ScsiControl.DmaModeSelect = DMA_POLLED;\ + ScsiPortWritePortUchar(&((DeviceExtension->Adapter)->InternalAddress), (UCHAR)(&(((PSCSI_REGISTERS)0)->Control)));\ + ScsiPortWritePortUchar(&((DeviceExtension->Adapter)->IoChannel), *((PUCHAR)&ScsiControl));} + +// +// Define SCSI host adapter card configuration parameters. +// + +#ifdef SCSI_VECTOR +#undef SCSI_VECTOR +#endif + +#ifdef SCSI_LEVEL +#undef SCSI_LEVEL +#endif + +#ifdef SCSI_PHYSICAL_BASE +#undef SCSI_PHYSICAL_BASE +#endif + +// +// Define for EISA card using interrupt request level of 10. +// + +#define SCSI_LEVEL 10 +#define SCSI_VECTOR 0 +#define SCSI_BUS_INTERFACE Isa + +// +// Define EISA DMA channel request level of 6. +// + +#define CARD_DMA_REQUEST 6 +#define CARD_DMA_WIDTH Width16Bits; +#define CARD_DMA_SPEED TypeA; + +// +// Define the default physical base address for SCSI protocol card. +// + +#define SCSI_PHYSICAL_BASE 0x360 +#define SCSI_SECOND_PHYSICAL_BASE 0x370 + +#endif + diff --git a/private/ntos/miniport/wd33c93/s3scsi.h b/private/ntos/miniport/wd33c93/s3scsi.h new file mode 100644 index 000000000..d978b0217 --- /dev/null +++ b/private/ntos/miniport/wd33c93/s3scsi.h @@ -0,0 +1,145 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation +Copyright (c) 1991 Compaq Computer Corporation + +Module Name: + + s3scsi.h + +Abstract: + + The module defines the structures, and defines for the 32-bit + GIO bus HPC scsi interface. + +Author: + + Jeff Havens (jhavens) 20-June-1991 + Tom Bonola (o-tomb) 25-Aug-1991 + +Revision History: + + +--*/ + +#ifndef _S3SCSI_ +#define _S3SCSI_ + +#include "sgidef.h" + +// +// Define the SCSI host adapter card register structure. +// + +typedef struct _CARD_REGISTERS { + UCHAR fill1; + UCHAR InternalAddress; // SCSI.ADDR + UCHAR fill2[3]; + UCHAR IoChannel; // SCSI.DATA + UCHAR fill3[2]; +}CARD_REGISTERS, *PCARD_REGISTERS; + +// +// Define the SCSI host adapter card control register structure. +// + +typedef struct _CARD_CONTROL { + UCHAR ResetScsiBus : 1; + UCHAR EnableDmaWrite : 1; + UCHAR SetBufferedIo : 1; + UCHAR SetIrql : 3; + UCHAR SetDmaRequest : 2; +}CARD_CONTROL, *PCARD_CONTROL; + +// +// Specify board dependent parameters. +// + +#define CLOCK_CONVERSION_FACTOR CLOCK_20MHZ +#define SYNCHRONOUS_OFFSET 0x0c +#define SYNCHRONOUS_PERIOD 0x32 +#define SYNCHRONOUS_PERIOD_STEP 0x32 +#define ASYNCHRONOUS_OFFSET 0x00 +#define ASYNCHRONOUS_PERIOD 0X02 +#define SCSI_CLOCK_SPEED 20 // Clock speed of the WD protocol chip in MHz. +#define SELECT_TIMEOUT_VALUE (SCSI_CLOCK_SPEED * 4) // = (Input clock * 250ms timeout) / 80 rounded up. +#define CARD_DMA_MODE DMA_BURST + +// +// SCSI Protocol Chip Control read and write macros. +// + +#define SCSI_READ(ChipAddr, Register) ( \ + ScsiPortWritePortUchar(&((ChipAddr)->InternalAddress), \ + (UCHAR) &((PSCSI_REGISTERS) 0)->Register), \ + ScsiPortReadPortUchar(&((ChipAddr)->IoChannel))) + +#define SCSI_READ_AUX(ChipAddr, Register) \ + (ScsiPortReadPortUchar (&((ChipAddr)->InternalAddress))) + +#define SCSI_WRITE(ChipAddr, Register, Value) { \ + ScsiPortWritePortUchar(&((ChipAddr)->InternalAddress), \ + (UCHAR) &((PSCSI_REGISTERS) 0)->Register); \ + ScsiPortWritePortUchar(&((ChipAddr)->IoChannel), (Value)); } + +#define SCSI_READ_TRANSFER_COUNT(ChipAddr, Count) { \ + ScsiPortWritePortUchar(&((ChipAddr)->InternalAddress), \ + (UCHAR) &((PSCSI_REGISTERS) 0)->TransferCountMsb); \ + Count = ScsiPortReadPortUchar(&((ChipAddr)->IoChannel)) << 16; \ + Count |= ScsiPortReadPortUchar(&((ChipAddr)->IoChannel)) << 8 ; \ + Count |= ScsiPortReadPortUchar(&((ChipAddr)->IoChannel)); } + +#define SCSI_WRITE_TRANSFER_COUNT(ChipAddr, Count) { \ + SCSI_WRITE(ChipAddr, TransferCountMsb, Count >> 16); \ + ScsiPortWritePortUchar(&((ChipAddr)->IoChannel), Count >> 8); \ + ScsiPortWritePortUchar(&((ChipAddr)->IoChannel), Count); } + +#define SCSI_RESET_BUS(ChipAddr) \ + *(volatile ULONG *)&SCSI0_HPCREG->ScsiCNTL = SGI_CNTL_SCSIRESET; \ + KeStallExecutionProcessor( 25 ); \ + *(volatile ULONG *)&SCSI0_HPCREG->ScsiCNTL = 0L; \ + KeStallExecutionProcessor( 1000 ) + +#define CARD_INITIALIZE(ChipAddr) +#define CARD_DMA_INITIATE(ChipAddr, ToDevice) +#define CARD_DMA_TERMINATE(ChipAddr) + +// +// Define SCSI host adapter card configuration parameters. +// + +#ifdef SCSI_VECTOR +#undef SCSI_VECTOR +#endif + +#ifdef SCSI_LEVEL +#undef SCSI_LEVEL +#endif + +#ifdef SCSI_PHYSICAL_BASE +#undef SCSI_PHYSICAL_BASE +#endif + +// +// Define for LOCAL0 register +// + +#define SCSI_LEVEL LOCAL0_LEVEL +#define SCSI_VECTOR SGI_VECTOR_SCSI +#define SCSI_BUS_INTERFACE Internal + +// +// Define SCSI DMA channel. +// + +#define CARD_DMA_REQUEST SGI_SCSI_DMA_CHANNEL +#define CARD_DMA_WIDTH Width32Bits; +#define CARD_DMA_SPEED TypeA; + +// +// Define the default physical base address for SCSI controller. +// + +#define SCSI_PHYSICAL_BASE SGI_SCSI0_WD_BASE + +#endif diff --git a/private/ntos/miniport/wd33c93/sources b/private/ntos/miniport/wd33c93/sources new file mode 100644 index 000000000..48879bbf5 --- /dev/null +++ b/private/ntos/miniport/wd33c93/sources @@ -0,0 +1,37 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=miniport + +TARGETNAME=wd33c93 +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=MINIPORT + +INCLUDES=..\..\inc +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\scsiport.lib + +#MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=wd33c93.c wd33c93.rc diff --git a/private/ntos/miniport/wd33c93/vendor.h b/private/ntos/miniport/wd33c93/vendor.h new file mode 100644 index 000000000..69465aa54 --- /dev/null +++ b/private/ntos/miniport/wd33c93/vendor.h @@ -0,0 +1,26 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + vendor.h + +Abstract: + + The module includes the appropriate vendor header file. + +Author: + + Jeff Havens (jhavens) 20-June-1991 + +Revision History: + + +--*/ + +#if defined(SGI) +#include "s3scsi.h" +#else +#include "maynard.h" +#endif diff --git a/private/ntos/miniport/wd33c93/wd33c93.c b/private/ntos/miniport/wd33c93/wd33c93.c new file mode 100644 index 000000000..efc9df3f8 --- /dev/null +++ b/private/ntos/miniport/wd33c93/wd33c93.c @@ -0,0 +1,5946 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + wd33c93.c + +Abstract: + + This module contains the WD33C93-specific functions for the NT SCSI port + driver. + +Author: + + Jeff Havens (jhavens) 10-June-1991 + +Environment: + + Kernel Mode only + +Revision History: + +--*/ + +#include "miniport.h" +#include "scsi.h" +#include "wd33c93.h" +#include "vendor.h" + +#if DBG +int WdDebug; +#define WdPrint(arg) ScsiDebugPrint arg +#else +#define WdPrint(arg) +#endif + +#define ScsiPortGetNextLink(srb) srb->NextSrb + +// +// Define SCSI Protocol Chip configuration parameters. +// + +#define INITIATOR_BUS_ID 0x7 +#define RESET_STALL_TIME 25 // The minimum assertion time for a SCSI bus reset. +#define RESET_DELAY_TIME 3 // Time in 250ms increments to delay after reset. +#define INTERRUPT_STALL_TIME 50 // Time to wait for the next interrupt. +#define INTERRUPT_CLEAR_TIME 10 +#define DATA_BUS_READY_TIME 3000 + + +// +// WD33C93-specific port driver device extension flags. +// + +#define PD_SYNCHRONOUS_RESPONSE_SENT 0X0001 +#define PD_SYNCHRONOUS_TRANSFER_SENT 0X0002 +#define PD_PENDING_START_IO 0X0004 +#define PD_MESSAGE_OUT_VALID 0X0008 +#define PD_DISCONNECT_EXPECTED 0X0010 +#define PD_SEND_MESSAGE_REQUEST 0X0020 +#define PD_POSSIBLE_EXTRA_MESSAGE_OUT 0X0040 +#define PD_DISCONNECT_INTERRUPT_ENABLED 0X0080 +#define PD_DMA_ACTIVE 0X0100 +#define PD_PARITY_ERROR 0X0200 +#define PD_PENDING_DATA_TRANSFER 0X0400 + +// +// The following defines specify masks which are used to clear flags when +// specific events occur, such as reset or disconnect. +// + +#define PD_ADAPTER_RESET_MASK ( PD_SYNCHRONOUS_TRANSFER_SENT | \ + PD_PENDING_START_IO | \ + PD_MESSAGE_OUT_VALID | \ + PD_SEND_MESSAGE_REQUEST | \ + PD_POSSIBLE_EXTRA_MESSAGE_OUT | \ + PD_DMA_ACTIVE | \ + PD_PARITY_ERROR | \ + PD_PENDING_DATA_TRANSFER | \ + PD_DISCONNECT_EXPECTED \ + ) + +#define PD_ADAPTER_DISCONNECT_MASK ( PD_SYNCHRONOUS_TRANSFER_SENT | \ + PD_MESSAGE_OUT_VALID | \ + PD_SEND_MESSAGE_REQUEST | \ + PD_POSSIBLE_EXTRA_MESSAGE_OUT | \ + PD_PARITY_ERROR | \ + PD_PENDING_DATA_TRANSFER | \ + PD_DISCONNECT_EXPECTED \ + ) + +// +// The largest SCSI bus message expected. +// + +#define MESSAGE_BUFFER_SIZE 7 + +// +// Retry count limits. +// + +#define RETRY_SELECTION_LIMIT 1 +#define RETRY_ERROR_LIMIT 2 +#define MAX_INTERRUPT_COUNT 64 + +// +// Bus and chip states. +// + +typedef enum _ADAPTER_STATE { + BusFree, + Select, + SelectAndTransfer, + CommandOut, + DataTransfer, + DataTransferComplete, + DisconnectExpected, + MessageAccepted, + MessageIn, + MessageOut, + StatusIn +} ADAPTER_STATE, *PADAPTER_STATE; + +// +// WD33C93-specific port driver logical unit flags. +// + +#define PD_SYNCHRONOUS_NEGOTIATION_DONE 0X0001 +#define PD_DO_NOT_NEGOTIATE 0X0002 +#define PD_STATUS_VALID 0X0004 +#define PD_DO_NOT_CHECK_TRANSFER_LENGTH 0X0008 +#define PD_INITIATE_RECOVERY 0X0010 + +// +// The following defines specify masks which are used to clear flags when +// specific events occur, such as reset or command complete. +// + +#define PD_LU_COMPLETE_MASK (PD_STATUS_VALID | \ + PD_DO_NOT_CHECK_TRANSFER_LENGTH | \ + PD_INITIATE_RECOVERY \ + ) + +#define PD_LU_RESET_MASK (PD_SYNCHRONOUS_NEGOTIATION_DONE |\ + PD_STATUS_VALID | \ + PD_DO_NOT_CHECK_TRANSFER_LENGTH | \ + PD_INITIATE_RECOVERY \ + ) +// +// WD33C93-specific port driver logical unit extension. +// + +typedef struct _SPECIFIC_LOGICAL_UNIT_EXTENSION { + USHORT LuFlags; + UCHAR SynchronousPeriod; + UCHAR SynchronousOffset; + ULONG SavedDataPointer; + ULONG SavedDataLength; + ULONG MaximumTransferLength; + PSCSI_REQUEST_BLOCK ActiveLuRequest; + PSCSI_REQUEST_BLOCK ActiveSendRequest; + ULONG RetryCount; + UCHAR SavedCommandPhase; +}SPECIFIC_LOGICAL_UNIT_EXTENSION, *PSPECIFIC_LOGICAL_UNIT_EXTENSION; + +// +// WD33C93-specific port driver device object extension. +// + +typedef struct _SPECIFIC_DEVICE_EXTENSION { + ULONG AdapterFlags; + ADAPTER_STATE AdapterState; // Current state of the adapter + PCARD_REGISTERS Adapter; // Address of the WD33C93 card + AUXILIARY_STATUS AdapterStatus; // Saved status register value + SCSI_STATUS AdapterInterrupt; // Saved interrupt status register + UCHAR CommandPhase; // Saved command phase value + UCHAR InitiatorBusId; // This adapter's SCSI bus ID in bit mask form + UCHAR DmaCommand; + UCHAR DmaPhase; + UCHAR MessageCount; // Count of bytes in message buffer + UCHAR MessageSent; // Count of bytes sent to target + UCHAR RequestCount; + UCHAR DmaCode; + UCHAR IrqCode; + UCHAR MessageBuffer[MESSAGE_BUFFER_SIZE]; // SCSI bus message buffer + ULONG ActiveDataPointer; // SCSI bus active data pointer + ULONG ActiveDataLength; // The amount of data to be transferred. + LONG InterruptCount; // Count of interrupts since connection. + PSPECIFIC_LOGICAL_UNIT_EXTENSION ActiveLogicalUnit; + + // Pointer to the active request. + + PSCSI_REQUEST_BLOCK NextSrbRequest; + // Pointer to the next SRB to process. + +} SPECIFIC_DEVICE_EXTENSION, *PSPECIFIC_DEVICE_EXTENSION; + + +// +// Functions passed to the OS-specific port driver. +// + +ULONG +WdFindAdapter( + IN PVOID ServiceContext, + IN PVOID Context, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ); + +BOOLEAN +WdInitializeAdapter( + IN PVOID ServiceContext + ); + +BOOLEAN +WdInterruptServiceRoutine( + IN PVOID ServiceContext + ); + +BOOLEAN +WdResetScsiBus( + IN PVOID ServiceContext, + IN ULONG PathId + ); + +VOID +WdSetupDma( + PVOID ServiceContext + ); + +BOOLEAN +WdStartIo( + IN PVOID ServiceContext, + IN PSCSI_REQUEST_BLOCK Srb + ); + +// +// WD33C93-specific internal mini-port driver functions. +// + +VOID +WdAcceptMessage( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN BOOLEAN SetAttention, + IN BOOLEAN SetSynchronousParameters + ); + +VOID +WdCleanupAfterReset( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN BOOLEAN ExternalReset + ); + +VOID +WdCompleteSendMessage( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN ULONG SrbStatus + ); + +BOOLEAN +WdDecodeSynchronousRequest( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension, + IN BOOLEAN ResponseExpected + ); + +VOID +WdDumpState( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension + ); + +BOOLEAN +WdIssueCommand( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN UCHAR CommandByte, + IN LONG TransferCount, + IN UCHAR CommandPhase + ); + +VOID +WdLogError( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN ULONG ErrorCode, + IN ULONG UniqueId + ); + +BOOLEAN +WdMessageDecode( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension + ); + +VOID +WdProcessRequestCompletion( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension + ); + +BOOLEAN +WdProcessReselection( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN UCHAR TargetId, + IN UCHAR LogicalUnitNumber + ); + +VOID +WdResetScsiBusInternal( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN ULONG PathId + ); + +VOID +WdSelectTarget( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension + ); + +VOID +WdSendMessage( + IN PSCSI_REQUEST_BLOCK Srb, + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension + ); + +VOID +WdStartExecution( + PSCSI_REQUEST_BLOCK Srb, + PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension + ); + +BOOLEAN +WdTransferInformation( + PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + PUCHAR BufferPointer, + ULONG Count, + BOOLEAN TransferToChip + ); + +ULONG +WdParseArgumentString( + IN PCHAR String, + IN PCHAR KeyWord + ); + +VOID +WdAcceptMessage( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN BOOLEAN SetAttention, + IN BOOLEAN SetSynchronousParameters + ) +/*++ + +Routine Description: + + This procedure tells the adapter to accept a pending message on the SCSI + bus. Optionally, it will set the synchronous transfer parameters and the + attention signal. + +Arguments: + + DeviceExtension - Supplies a pointer to the device extension. + + SetAttention - Indicates the attention line on the SCSI bus should be set. + + SetSynchronousParameters - Indicates the synchronous data transfer + parameters should be set. + +Return Value: + + None. + +--*/ + +{ + + PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension; + SCSI_SYNCHRONOUS scsiSynchronous; + + /* Powerfail */ + + // + // Check to see if the synchonous data transfer parameters need to be set. + // + + if (SetSynchronousParameters) { + + // + // These must be set before a data transfer is started. + // + + luExtension = DeviceExtension->ActiveLogicalUnit; + *((PUCHAR) &scsiSynchronous) = 0; + scsiSynchronous.SynchronousOffset = luExtension->SynchronousOffset; + scsiSynchronous.SynchronousPeriod = luExtension->SynchronousPeriod; + + SCSI_WRITE( + DeviceExtension->Adapter, + Synchronous, + *((PUCHAR) &scsiSynchronous) + ); + } + + // + // Check to see if the attention signal needs to be set. + // + + if (SetAttention) { + + // + // This requests that the target enter the message-out phase. + // + + WdIssueCommand(DeviceExtension, ASSERT_ATN, -1, 0); + } + + // + // Indicate to the adapter that the message-in phase may now be completed. + // + + WdIssueCommand(DeviceExtension, NEGATE_ACK, -1, 0); +} + + +VOID +WdCleanupAfterReset( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN BOOLEAN ExternalReset + ) + +/*++ + +Routine Description: + + This routine cleans up the adapter-specific + and logical-unit-specific data structures. Any active requests are + completed and the synchronous negotiation flags are cleared. + +Arguments: + + DeviceExtension - Supplies a pointer to device extension for the bus that + was reset. + + ExternalReset - When set, indicates that the reset was generated by a + SCSI device other than this host adapter. + +Return Value: + + None. + +--*/ + +{ + LONG pathId = 0; + LONG targetId; + LONG luId; + PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension; + + // + // Check to see if a data transfer was in progress, if so, flush the DMA. + // + + if (DeviceExtension->AdapterFlags & PD_DMA_ACTIVE) { + CARD_DMA_TERMINATE( DeviceExtension ); + ScsiPortFlushDma(DeviceExtension); + } + + // + // if the current state is Select then SCSI port driver needs + // to be notified that new requests can be sent. + // + + if (DeviceExtension->AdapterFlags & PD_PENDING_START_IO) { + + // + // Ask for another request and clear the pending one. The pending + // request will be processed when the rest of the active requests + // are completed. + + DeviceExtension->NextSrbRequest = NULL; + DeviceExtension->AdapterFlags &= ~PD_PENDING_START_IO; + + ScsiPortNotification( NextRequest, DeviceExtension, NULL ); + + } + + // + // If there was an active request, then complete it with + // SRB_STATUS_PHASE_SEQUENCE_FAILURE so the class driver will know not + // to retry it too many times. + // + + if (DeviceExtension->ActiveLogicalUnit != NULL + && DeviceExtension->ActiveLogicalUnit->ActiveLuRequest != NULL) { + + // + // Set the SrbStatus in the SRB, complete the request and + // clear the active pointers + // + + luExtension = DeviceExtension->ActiveLogicalUnit; + + luExtension->ActiveLuRequest->SrbStatus = + SRB_STATUS_PHASE_SEQUENCE_FAILURE; + + ScsiPortNotification( + RequestComplete, + DeviceExtension, + luExtension->ActiveLuRequest + ); + + // + // Check to see if there was a synchronous negotiation in progress. If + // there was then do not try to negotiate with this target again. + // + + if (DeviceExtension->AdapterFlags & (PD_SYNCHRONOUS_RESPONSE_SENT | + PD_SYNCHRONOUS_TRANSFER_SENT | PD_POSSIBLE_EXTRA_MESSAGE_OUT)) { + + // + // This target cannot negotiate properly. Set a flag to prevent + // further attempts and set the synchronous parameters to use + // asynchronous data transfer. + // + + /* TODO: Consider propagating this flag to all the Lus on this target. */ + luExtension->LuFlags |= PD_DO_NOT_NEGOTIATE; + luExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET; + luExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD; + + } + + luExtension->ActiveLuRequest = NULL; + luExtension->RetryCount = 0; + DeviceExtension->ActiveLogicalUnit = NULL; + + } + + // + // Clear the appropriate state flags as well as the next request. + // The request will actually be cleared when the logical units are processed. + // Note that it is not necessary to fail the request waiting to be started + // since it will be processed properly by the target controller, but it + // is cleared anyway. + // + + for (targetId = 0; targetId < SCSI_MAXIMUM_TARGETS; targetId++) { + + // + // Loop through each of the possible logical units for this target. + // + + for (luId = 0; luId < SCSI_MAXIMUM_LOGICAL_UNITS; luId++) { + + luExtension = ScsiPortGetLogicalUnit( DeviceExtension, + (UCHAR)pathId, + (UCHAR)targetId, + (UCHAR)luId + ); + + if (luExtension == NULL) { + continue; + } + + if (luExtension->ActiveLuRequest != NULL) { + + // + // Set the SrbStatus in the SRB, complete the request and + // clear the active pointers + // + + luExtension->ActiveLuRequest->SrbStatus = + SRB_STATUS_BUS_RESET; + + // + // Complete the request. + // + + ScsiPortNotification( + RequestComplete, + DeviceExtension, + luExtension->ActiveLuRequest + ); + + luExtension->ActiveLuRequest = NULL; + + } + + if (luExtension->ActiveSendRequest != NULL) { + + // + // Set the SrbStatus in the SRB, complete the request and + // clear the active pointers + // + + luExtension->ActiveSendRequest->SrbStatus = + SRB_STATUS_BUS_RESET; + + // + // Complete the request. + // + + ScsiPortNotification( + RequestComplete, + DeviceExtension, + luExtension->ActiveSendRequest + ); + + luExtension->ActiveSendRequest = NULL; + + } + + // + // Clear the necessary logical unit flags. + // + + luExtension->LuFlags &= ~PD_LU_RESET_MASK; + luExtension->RetryCount = 0; + luExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET; + luExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD; + + } /* for luId */ + } /* for targetId */ + + // + // Set the bus state to free and clear the adapter flags. + // + + DeviceExtension->AdapterState = BusFree; + DeviceExtension->AdapterFlags &= ~PD_ADAPTER_RESET_MASK; + DeviceExtension->ActiveLogicalUnit = NULL; + +} + +VOID +WdCompleteSendMessage( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN ULONG SrbStatus + ) +/*++ + +Routine Description: + + This function does the cleanup necessary to complete a send-message request. + This includes completing any affected execute-I/O requests and cleaning + up the device extension state. + +Arguments: + + DeviceExtension - Supplies a pointer to the device extension of the SCSI bus + adapter. The active logical unit is stored in ActiveLogicalUnit. + + SrbStatus - Indicates the status that the request should be completed with + if the request did not complete normally, then any active execute + requests are not considered to have been affected. + +Return Value: + + None. + +--*/ + +{ + PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension; + PSCSI_REQUEST_BLOCK srb; + LONG targetId; + LONG luId; + + luExtension = DeviceExtension->ActiveLogicalUnit; + srb = luExtension->ActiveSendRequest; + + // + // Clean up any EXECUTE requests which may have been affected by this + // message. + // + + if (SrbStatus == SRB_STATUS_SUCCESS) { + switch (srb->Function) { + case SRB_FUNCTION_ABORT_COMMAND: + + // + // Make sure there is still a request to complete. If so complete + // it with an SRB_STATUS_ABORTED status. + // + + if (luExtension->ActiveLuRequest == NULL) { + + // + // If there is no request, then fail the abort. + // + + SrbStatus = SRB_STATUS_ABORT_FAILED; + break; + } + + luExtension->ActiveLuRequest->SrbStatus = + SRB_STATUS_ABORTED; + + ScsiPortNotification( + RequestComplete, + DeviceExtension, + luExtension->ActiveLuRequest + ); + + luExtension->ActiveLuRequest = NULL; + luExtension->RetryCount = 0; + luExtension->LuFlags &= ~PD_LU_COMPLETE_MASK; + + break; + + case SRB_FUNCTION_RESET_DEVICE: + + // + // Cycle through each of the possible logical units looking + // for requests which have been cleared by the target reset. + // + + targetId = srb->TargetId; + + for (luId = 0; luId < SCSI_MAXIMUM_LOGICAL_UNITS; luId) { + + luExtension = ScsiPortGetLogicalUnit( DeviceExtension, + srb->PathId, + (UCHAR)targetId, + (UCHAR)luId + ); + + if (luExtension == NULL) { + continue; + } + + if (luExtension->ActiveLuRequest != NULL) { + + // + // Set the SrbStatus in the SRB, complete the + // request and clear the active pointers + // + + luExtension->ActiveLuRequest->SrbStatus = + SRB_STATUS_BUS_RESET; + + // + // Complete the request. + // + + ScsiPortNotification( + RequestComplete, + DeviceExtension, + luExtension->ActiveLuRequest + ); + + luExtension->RetryCount = 0; + luExtension->ActiveLuRequest = NULL; + + // + // Clear the necessary logical unit flags. + // + + luExtension->LuFlags &= ~PD_LU_RESET_MASK; + } + } /* for luId */ + + /* TODO: Handle CLEAR QUEUE and ABORT WITH TAG */ + } + + } else { + + // + // If an abort request fails then complete target of the abort; + // otherwise the target of the ABORT may never be compileted. + // + + if (srb->Function == SRB_FUNCTION_ABORT_COMMAND) { + + // + // Make sure there is still a request to complete. If so + // it with an SRB_STATUS_ABORTED status. + // + + if (luExtension->ActiveLuRequest != NULL) { + + luExtension->ActiveLuRequest->SrbStatus = + SRB_STATUS_ABORTED; + + ScsiPortNotification( + RequestComplete, + DeviceExtension, + luExtension->ActiveLuRequest + ); + + luExtension->ActiveLuRequest = NULL; + luExtension->RetryCount = 0; + luExtension->LuFlags &= ~PD_LU_COMPLETE_MASK; + + } + } + } + + // + // Complete the actual send-message request. + // + + srb->SrbStatus = (UCHAR)SrbStatus; + ScsiPortNotification( + RequestComplete, + DeviceExtension, + srb + ); + + // + // Clear the active send request and PD_SEND_MESSAGE_REQUEST flag. + // + + luExtension->ActiveSendRequest = NULL; + luExtension->RetryCount = 0; + DeviceExtension->AdapterFlags &= ~PD_SEND_MESSAGE_REQUEST; +} + +BOOLEAN +WdIssueCommand( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN UCHAR CommandByte, + IN LONG TransferCount, + IN UCHAR CommandPhase + ) + +/*++ + +Routine Description: + + This function waits for the command buffer to become available and then + issues the requested command. The transfer count registers + and CommandPhase are optionally set. + +Arguments: + + DeviceExtension - Supplies a pointer to the specific device extension. + + CommandByte - Supplies the command byte to be written to the SCSI + protocol chip. + + TransferCount - Supplies the value to load in transfer count register. + If -1 is supplied, then the transfer counter is not loaded. + + CommandPhase - Supplies the value to load into the Command Phase register. + +Return Value: + + TRUE - If the command was written. + + FALSE - If the command could not be written. + +--*/ + +{ + ULONG i; + AUXILIARY_STATUS auxiliaryStatus; + + // + // First make sure the SCSI adapter chip is ready for a command. + // + + *((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX( + DeviceExtension->Adapter, + AuxiliaryStatus + ); + + for (i = 0; + i < INTERRUPT_STALL_TIME && auxiliaryStatus.CommandInProgress; + i++) { + + ScsiPortStallExecution(1); + + *((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX( + DeviceExtension->Adapter, + AuxiliaryStatus + ); + } + + if (auxiliaryStatus.CommandInProgress) { + + // + // The chip is messed up but there is nothing that can be done so + // just return false. + // + + WdPrint((0, "WdIssueCommand: A command in progress timeout occured! Aux Status 0x%.2X\n", auxiliaryStatus)); + WdDumpState(DeviceExtension); + return(FALSE); + } + + // + // Set the transfer count if necessary. + // + + if (TransferCount != -1) { + + // + // Set up the SCSI protocol chip for the data transfer with the + // transfer length, regardless of the length. + // + + SCSI_WRITE_TRANSFER_COUNT(DeviceExtension->Adapter, TransferCount); + + } + + if (CommandByte == SELECT_ATN_AND_TRANSFER || + CommandByte == SELECT_AND_TRANSFER) { + + // + // These commands use the command phase register, so set it to the + // requested value. + // + + SCSI_WRITE(DeviceExtension->Adapter, CommandPhase, CommandPhase); + } + + SCSI_WRITE(DeviceExtension->Adapter, Command, CommandByte); + + return(TRUE); +} + + +BOOLEAN +WdMessageDecode( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension + ) +/*++ + +Routine Description: + + This function decodes the SCSI bus message-in the device extension message + buffer. After the message is decoded it decides what action to take in + response to the message. If an outgoing message needs to be sent, then + it is placed in the message buffer and true is returned. If the message + is acceptable, then the device state is set either to DisconnectExpected or + MessageAccepted and the MessageCount is reset to 0. + + Some messages are made up of serveral bytes. This funtion will simply + return false when an incomplete message is detected, allowing the target + to send the rest of the message. The message count is left unchanged. + +Arguments: + + DeviceExtension - Supplies a pointer to the specific device extension. + +Return Value: + + TRUE - Returns true if there is a reponse message to be sent. + + FALSE - If there is no response message. + +--*/ + +{ + PSCSI_REQUEST_BLOCK srb; + PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension; + LONG offset; + LONG i; + ULONG savedAdapterFlags; + PSCSI_EXTENDED_MESSAGE extendedMessage; + + // + // Note: the ActivelogicalUnit field could be invalid if the + // PD_DISCONNECT_EXPECTED flag is set, so luExtension cannot be used until + // this flag has been checked. + // + + luExtension = DeviceExtension->ActiveLogicalUnit; + savedAdapterFlags = DeviceExtension->AdapterFlags; + + // + // A number of special cases must be handled if a special message has + // just been sent. These special messages are synchronous negotiations + // or a messages which imply a disconnect. The special cases are: + // + // If a disconnect is expected because of a send-message request, + // then the only valid message-in is a MESSAGE REJECT; other messages + // are a protocol error and are rejected. + // + // If a synchronous negotiation response was just sent and the message + // in was not a MESSAGE REJECT, then the negotiation has been accepted. + // + // If a synchronous negotiation request was just sent, then valid responses + // are a MESSAGE REJECT or an extended synchronous message back. + // + + if (DeviceExtension->AdapterFlags & (PD_SYNCHRONOUS_RESPONSE_SENT | + PD_DISCONNECT_EXPECTED | PD_SYNCHRONOUS_TRANSFER_SENT)) { + + if (DeviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED && + DeviceExtension->MessageBuffer[0] != SCSIMESS_MESSAGE_REJECT) { + + // + // The target is not responding correctly to the message. Send a + // message reject of this message. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + + return(TRUE); + } else { + srb = luExtension->ActiveLuRequest; + } + + if (DeviceExtension->AdapterFlags & PD_SYNCHRONOUS_RESPONSE_SENT && + DeviceExtension->MessageBuffer[0] != SCSIMESS_MESSAGE_REJECT) { + + // + // The target did not reject our response so the synchronous + // transfer negotiation is done. Clear the adapter flags and + // set the logical unit flags indicating this. Continue processing + // the message which is unrelated to negotiation. + // + + DeviceExtension->AdapterFlags &= ~PD_SYNCHRONOUS_RESPONSE_SENT; + luExtension->LuFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE; + } + + // + // Save the adapter flags for later use. + // + + savedAdapterFlags = DeviceExtension->AdapterFlags; + + if (DeviceExtension->AdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT ) { + + // + // The target is sending a message after a synchronous transfer + // request was sent. Valid responses are a MESSAGE REJECT or an + // extended synchronous message; any other message negates the + // fact that a negotiation was started. However, since extended + // messages are multi-byte, it is difficult to determine what the + // incoming message is. So at this point, the fact that a + // sychronous transfer was sent will be saved and cleared from the + // AdapterFlags. If the message looks like a synchronous transfer + // request, then restore this fact back into the AdapterFlags. If + // the complete message is not the one expected, then opening + // negotiation will be forgotten. This is an error by the target, + // but minor so nothing will be done about it. Finally, to prevent + // this cycle from reoccurring on the next request indicate that + // the negotiation is done. + // + + DeviceExtension->AdapterFlags &= ~PD_SYNCHRONOUS_TRANSFER_SENT; + luExtension->LuFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE; + } + + } else { + srb = luExtension->ActiveLuRequest; + } + + switch (DeviceExtension->MessageBuffer[0]) { + case SCSIMESS_COMMAND_COMPLETE: + + // + // For better or worse the command is complete. Process request which + // sets the SrbStatus and cleans up the device and logical unit states. + // + + WdProcessRequestCompletion(DeviceExtension); + + // + // Complete the request. + // + + ScsiPortNotification( + RequestComplete, + DeviceExtension, + srb + ); + + // + // Everything is ok with the message so do not send one and set the + // state to DisconnectExpected. + // + + DeviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED; + DeviceExtension->AdapterState = DisconnectExpected; + DeviceExtension->MessageCount = 0; + return(FALSE); + + case SCSIMESS_DISCONNECT: + + // + // The target wants to disconnect. Set the state to DisconnectExpected, + // and do not request a message-out. + // + + DeviceExtension->AdapterState = DisconnectExpected; + DeviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED; + DeviceExtension->MessageCount = 0; + return(FALSE); + + case SCSIMESS_EXTENDED_MESSAGE: + + // + // The format of an extended message is: + // Extended Message Code + // Length of Message + // Extended Message Type + // . + // . + // + // Until the entire message has been read in, just keep getting bytes + // from the target, making sure that the message buffer is not + // overrun. + // + + extendedMessage = (PSCSI_EXTENDED_MESSAGE) + DeviceExtension->MessageBuffer; + + if (DeviceExtension->MessageCount < 2 || + (DeviceExtension->MessageCount < (UCHAR) MESSAGE_BUFFER_SIZE && + DeviceExtension->MessageCount < (UCHAR) (extendedMessage->MessageLength + 2)) + ) { + + // + // Update the state and return; also restore the AdapterFlags. + // + + DeviceExtension->AdapterFlags = savedAdapterFlags; + DeviceExtension->AdapterState = MessageAccepted; + return(FALSE); + + } + + // + // Make sure the length includes an extended op-code. + // + + if (DeviceExtension->MessageCount < 3) { + + // + // This is an illegal extended message. Send a MESSAGE_REJECT. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + + return(TRUE); + } + + // + // Determine the extended message type. + // + + switch (extendedMessage->MessageType) { + case SCSIMESS_MODIFY_DATA_POINTER: + + // + // Verify the message length. + // + + if (extendedMessage->MessageLength != SCSIMESS_MODIFY_DATA_LENGTH) { + + // + // Reject the message. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + + return(TRUE); + } + + // + // Calculate the modification to be added to the data pointer. + // + + offset = 0; + for (i = 0; i < 4; i++) { + offset << 8; + offset += extendedMessage->ExtendedArguments.Modify.Modifier[i]; + } + + // + // Verify that the new data pointer is still within the range + // of the buffer. + // + + if (DeviceExtension->ActiveDataLength - offset > + srb->DataTransferLength || + ((LONG) DeviceExtension->ActiveDataLength - offset) < 0 ) { + + // + // The new pointer is not valid, so reject the message. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + + return(TRUE); + } + + // + // Everything has checked out, so update the pointer. + // + + DeviceExtension->ActiveDataPointer += offset; + DeviceExtension->ActiveDataLength -= offset; + + // + // Everything is ok, so accept the message as is. + // + + DeviceExtension->MessageCount = 0; + DeviceExtension->AdapterState = MessageAccepted; + return(FALSE); + + case SCSIMESS_SYNCHRONOUS_DATA_REQ: + + // + // A SYNCHRONOUS DATA TRANSFER REQUEST message was received. + // Make sure the length is correct. + // + + if ( extendedMessage->MessageLength != + SCSIMESS_SYNCH_DATA_LENGTH) { + + // + // The length is invalid reject the message. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + return(TRUE); + } + + // + // If synchrouns negotiation has been disabled for this request, + // then reject any synchronous messages; however, when synchronous + // transfers are allowed then a new attempt can be made. + // + + if (srb != NULL && + !(savedAdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT) && + srb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER) { + + // + // Reject the synchronous transfer message since synchonrous + // transfers are not desired at this time. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + return(TRUE); + + } + + // + // Call WdDecodeSynchronousMessage to decode the message and + // formulate a response if necessary. + // WdDecodeSynchronousRequest will return FALSE if the + // message is not accepable and should be rejected. + // + + if (!WdDecodeSynchronousRequest( + DeviceExtension, + luExtension, + (BOOLEAN) (!(savedAdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT)) + )) { + + // + // Indicate that a negotiation has been done in the logical + // unit and clear the negotiation flags. + // + + luExtension->LuFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE; + DeviceExtension->AdapterFlags &= + ~(PD_SYNCHRONOUS_RESPONSE_SENT| + PD_SYNCHRONOUS_TRANSFER_SENT); + + // + // The message was not acceptable so send a MESSAGE_REJECT. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + return(TRUE); + } + + // + // If a reponse was expected, then set the state for a message-out. + // Otherwise, WdDecodeSynchronousRequest has put a reponse + // in the message buffer to be returned to the target. + // + + if (savedAdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT){ + + // + // We initiated the negotiation, so no response is necessary. + // + + DeviceExtension->AdapterState = MessageAccepted; + DeviceExtension->AdapterFlags &= ~PD_SYNCHRONOUS_TRANSFER_SENT; + luExtension->LuFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE; + DeviceExtension->MessageCount = 0; + return(FALSE); + } + + // + // Set up the state to send the reponse. The message count is + // still correct. + // + + DeviceExtension->MessageSent = 0; + DeviceExtension->AdapterState = MessageOut; + DeviceExtension->AdapterFlags &= ~PD_SYNCHRONOUS_TRANSFER_SENT; + DeviceExtension->AdapterFlags |= PD_SYNCHRONOUS_RESPONSE_SENT; + return(TRUE); + + case SCSIMESS_WIDE_DATA_REQUEST: + + // + // A WIDE DATA TRANSFER REQUEST message was received. + // Make sure the length is correct. + // + + if ( extendedMessage->MessageLength != + SCSIMESS_WIDE_DATA_LENGTH) { + + // + // The length is invalid reject the message. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + return(TRUE); + } + + // + // Since this SCSI protocol chip only supports 8 bits, return + // a width of 0 which indicates an 8-bit-wide transfers. The + // MessageCount is still correct for the message. + // + + extendedMessage->ExtendedArguments.Wide.Width = 0; + DeviceExtension->MessageSent = 0; + DeviceExtension->AdapterState = MessageOut; + return(TRUE); + + default: + + // + // This is an unknown or illegal message, so send message REJECT. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + return(TRUE); + } + + case SCSIMESS_INITIATE_RECOVERY: + + // + // Save the fact that a INITIATE RECOVERY message was received. + // + + luExtension->LuFlags |= PD_INITIATE_RECOVERY; + DeviceExtension->MessageCount = 0; + return(FALSE); + + case SCSIMESS_LINK_CMD_COMP: + + // + // A link command completed. Process the completion. Since the link + // FLAG was not set, do not call ScsiPortNotification. Get the next + // segment of the request and accept the message. + // + + // + // Make sure that this is a linked command. + // Linked commands are not supported. + // + + if (TRUE) { + + // + // Something is messed up. Reject the message. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + return(TRUE); + } + + WdProcessRequestCompletion(DeviceExtension); + + luExtension->ActiveLuRequest = srb->NextSrb; + + // + // Everything is ok with the message, so do not send one and set the + // state to MessageAccepted. + // + + DeviceExtension->AdapterState = MessageAccepted; + DeviceExtension->MessageCount = 0; + return(FALSE); + + case SCSIMESS_LINK_CMD_COMP_W_FLAG: + + // + // A link command completed. Process the completion and get the next + // segment of the request. Since the link FLAG was set, call + // ScsiPortNotification to notify the class driver. + // + + // + // Make sure that this is a linked command. + // Linked commands are not supported. + // + + if (TRUE) { + + // + // Something is messed up. Reject the message. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + return(TRUE); + } + + WdProcessRequestCompletion(DeviceExtension); + + luExtension->ActiveLuRequest = srb->NextSrb; + + // + // Complete the request. + // + + ScsiPortNotification( + RequestComplete, + DeviceExtension, + srb + ); + + // + // Everything is ok with the message, so do not send one and set the + // state to MessageAccepted. + // + + DeviceExtension->AdapterState = MessageAccepted; + DeviceExtension->MessageCount = 0; + return(FALSE); + + case SCSIMESS_MESSAGE_REJECT: + + // + // The last message we sent was rejected. If this was a send + // message request, then set the proper status and complete the + // request. Set the state to message accepted. + // + + if (DeviceExtension->AdapterFlags & PD_SEND_MESSAGE_REQUEST) { + + // + // Complete the request with message rejected status. + // + + WdCompleteSendMessage( + DeviceExtension, + SRB_STATUS_MESSAGE_REJECTED + ); + } + + // + // Check to see if a synchronous negotiation is in progress. + // + + if (savedAdapterFlags & (PD_SYNCHRONOUS_RESPONSE_SENT| + PD_SYNCHRONOUS_TRANSFER_SENT)) { + + // + // The negotiation failed so use asynchronous data transfers. + // Indicate that the negotiation has been attempted and set + // the transfer for asynchronous. Clear the negotiation flags. + // + + luExtension->LuFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE; + luExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET; + luExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD; + DeviceExtension->AdapterFlags &= ~(PD_SYNCHRONOUS_RESPONSE_SENT| + PD_SYNCHRONOUS_TRANSFER_SENT); + + // + // Even though the negotiation appeared to go ok, there is no reason + // to try again, and some targets get messed up later, so do not try + // synchronous negotiation again. + // + + /* TODO: Reconsider doing this. */ + + // luExtension->LuFlags |= PD_DO_NOT_NEGOTIATE; + + } + + DeviceExtension->AdapterState = MessageAccepted; + DeviceExtension->MessageCount = 0; + return(FALSE); + + case SCSIMESS_RESTORE_POINTERS: + + // + // Restore data pointer message. Just copy the saved data pointer + // and the length to the active data pointers. + // + + DeviceExtension->ActiveDataPointer = luExtension->SavedDataPointer; + DeviceExtension->ActiveDataLength = luExtension->SavedDataLength; + DeviceExtension->AdapterState = MessageAccepted; + DeviceExtension->MessageCount = 0; + return(FALSE); + + case SCSIMESS_SAVE_DATA_POINTER: + + // + // SAVE DATA POINTER message request that the active data pointer and + // length be copied to the saved location. + // + + luExtension->SavedDataPointer = DeviceExtension->ActiveDataPointer; + luExtension->SavedDataLength = DeviceExtension->ActiveDataLength; + DeviceExtension->AdapterState = MessageAccepted; + DeviceExtension->MessageCount = 0; + return(FALSE); + + default: + + // + // An unrecognized or unsupported message: send message reject. + // + + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT; + DeviceExtension->AdapterState = MessageOut; + return(TRUE); + } +} + +BOOLEAN +WdDecodeSynchronousRequest( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + OUT PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension, + IN BOOLEAN ResponseExpected + ) +/*++ + +Routine Description: + + This function decodes the synchronous data transfer request message from + the target. It will update the synchronous message in the buffer and the + synchronous transfer parameters in the logical unit extension. These + parameters are specific for the WD 53C9X protocol chip. The updated + message in the device extension message buffer might be returned to the + target. + + This function should be called before the final byte of the message is + accepted from the SCSI bus. + +Arguments: + + DeviceExtension - Supplies a pointer to the adapter-specific device + extension. + + LuExtension - Supplies a pointer to the logical unit's device extension. + The synchronous transfer fields are updated in this structure to + reflect the new parameter in the message. + + ResponseExpected - When set, indicates that the target initiated the + negotiation and that it expects a response. + +Return Value: + + TRUE - Returned if the request is acceptable. + + FALSE - Returned if the request should be rejected and asynchronous + transfer should be used. + +--*/ + +{ + PSCSI_EXTENDED_MESSAGE extendedMessage; + SCSI_SYNCHRONOUS scsiSynchronous; + LONG period; + LONG i; + + + extendedMessage = (PSCSI_EXTENDED_MESSAGE) DeviceExtension->MessageBuffer; + + // + // Determine the transfer offset. It is the minimum of the SCSI protocol + // chip's maximum offset and the requested offset. + // + + if (extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset > + SYNCHRONOUS_OFFSET) { + + if (!ResponseExpected) { + + // + // The negotiation failed for some reason, fall back to + // asynchronous data transfer. + // + + LuExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET; + LuExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD; + return(FALSE); + } + + extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset = SYNCHRONOUS_OFFSET; + LuExtension->SynchronousOffset = SYNCHRONOUS_OFFSET; + + } else { + + LuExtension->SynchronousOffset = + extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset; + + } + + // + // If the offset requests asynchronous transfers then set the default + // period and return. + // + + if (extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset == + ASYNCHRONOUS_OFFSET) { + LuExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD; + return(TRUE); + } + + // + // Check to see if the period is less than the SCSI protocol chip can + // use. If it is then update the message with our minimum and return. + // + + if (extendedMessage->ExtendedArguments.Synchronous.TransferPeriod < SYNCHRONOUS_PERIOD) { + + if (!ResponseExpected) { + + // + // The negotiation failed for some reason, fall back to + // asynchronous data transfer. + // + + LuExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET; + LuExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD; + return(FALSE); + } + + extendedMessage->ExtendedArguments.Synchronous.TransferPeriod = SYNCHRONOUS_PERIOD; + } + + // + // The synchronous period uses the following formula to calculate the + // transfer period returned in the message: + // + // (SynchronousPeriod - 2) * 1000 * ClockDivide + // --------------------------------------------- + // Clock speed in Mhz * 2 * 4 + // + // The 4 is the divisor is because the message byte is in units of 4 ns. + // For the WD53c93 the Synchronous period will be calculated by: + // + // (MessagePeriod - SYNCHRONOUS_PERIOD) + // SynchrounousPeriod = ------------------------------------ + // SYNCHRONOUS_PERIOD_STEP + // + // Note that this must be rounded up. Since the range of SynchronousPeriod + // is only 3-6 a simple loop will handle this calculation. + // + + period = extendedMessage->ExtendedArguments.Synchronous.TransferPeriod - + SYNCHRONOUS_PERIOD; + + for (i = 3; i < 7; i++) { + if (period <= 0) { + break; + } + period -= SYNCHRONOUS_PERIOD_STEP; + } + + if (i >= 7) { + + // + // The requested transfer period is too long for the SCSI protocol + // chip. Fall back to synchronous and reject the request. + // + + LuExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET; + LuExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD; + return(FALSE); + } else { + LuExtension->SynchronousPeriod = (UCHAR)i; + } + + // + // Set the synchronous data transfer parameter registers + // to the new values. These must be set before a data transfer + // is started. If a response message is received then the parameters + // must be reset. + // + + /* Powerfail */ + + *((PCHAR) &scsiSynchronous) = 0; + scsiSynchronous.SynchronousPeriod = LuExtension->SynchronousPeriod; + scsiSynchronous.SynchronousOffset = LuExtension->SynchronousOffset; + + SCSI_WRITE( + DeviceExtension->Adapter, + Synchronous, + *((PUCHAR) &scsiSynchronous) + ); + + return(TRUE); + +} + +VOID +WdDumpState( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension + ) + +/*++ + +Routine Description: + + This function prints the interesting state information about the requested + SCSI bus adapter. + +Arguments: + + DeviceExtension - Supplies a pointer to device extension for the SCSI + bus adapter that should be displayed. + +Return Value: + + None. + +--*/ + +{ + WdPrint((0, "WdDumpState: Specific device extension: 0x%.8X; Active Logical Unit: 0x%.8X;\n", + DeviceExtension, + DeviceExtension->ActiveLogicalUnit + )); + WdPrint((0, "WdDumpState: Adapter Status: 0x%.2X; Adapter Interrupt: 0x%.2X; Command Phase: 0x%.2X;\n", + *((PUCHAR) &DeviceExtension->AdapterStatus), + *((PUCHAR) &DeviceExtension->AdapterInterrupt), + DeviceExtension->CommandPhase + )); + WdPrint((0, "WdDumpState: Adapter flags: 0x%.4X; Adapter state: %d;\n", + DeviceExtension->AdapterFlags, + DeviceExtension->AdapterState + )); + +} + + +BOOLEAN +WdInitializeAdapter( + IN PVOID ServiceContext + ) +/*++ + +Routine Description: + + This function initializes the WD SCSI adapter chip. This function must + be called before any other operations are performed on the chip. It should + also be called after a power failure. This function does not cause any + interrupts; however, after it completes interrupts can occur. + +Arguments: + + ServiceContext - Pointer to the specific device extension for this SCSI + bus. + +Return Value: + + TRUE - Returns TRUE indicating that the initialization of the chip is + complete. + + FALSE - Returns FALSE if the initialization failed. + +--*/ + +{ + PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext; + OWN_ID ownId; + SCSI_CONTROL control; + SCSI_STATUS interruptStatus; + AUXILIARY_STATUS auxiliaryStatus; + SOURCE_ID sourceId; + ULONG i; + + // + // Initialize the card. + // + + CARD_INITIALIZE(deviceExtension); + + // + // If the SCSI protocol chip is interrupting, then clear the interrupt so + // that the reset command can be written to the chip. + // + + *((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX( + deviceExtension->Adapter, + AuxiliaryStatus + ); + + if (auxiliaryStatus.Interrupt) { + + // + // Read the status register to clear the interrupt. + // + + *((PUCHAR) &interruptStatus) = SCSI_READ( + deviceExtension->Adapter, + Status + ); + + // + // Stall the required time to allow the interrupt to clear. + // + + ScsiPortStallExecution(INTERRUPT_CLEAR_TIME); + } + + // + // The OwnId register must be set when the SCSI protocol chip is reset. + // Initialize the ownId with the adapter's host ID, advanced features, + // and the correct clock frequency select. Note the CdbSize register is + // used as the OwnId register when a reset command is issued. + // + + *((PUCHAR) &ownId) = 0; + ownId.InitiatorId = deviceExtension->InitiatorBusId; + ownId.AdvancedFeatures = 1; + ownId.FrequencySelect = CLOCK_CONVERSION_FACTOR; + + SCSI_WRITE( deviceExtension->Adapter, CdbSize, *((PUCHAR) &ownId) ); + + // + // Issue a reset-chip command. + // + + WdIssueCommand(deviceExtension, RESET_SCSI_CHIP, -1, 0); + + // + // Wait for the reset to complete. A reset complete is indicated by an + // interrupt with none of the interrupt bits set. The PhaseState in the + // interruptStatus indicates whether the this chip supports advanced mode + // or not. + // + + i = 0; + *((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX( + deviceExtension->Adapter, + AuxiliaryStatus + ); + + while (!auxiliaryStatus.Interrupt && i < INTERRUPT_STALL_TIME) { + + ScsiPortStallExecution(1); + i++; + + *((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX( + deviceExtension->Adapter, + AuxiliaryStatus + ); + + } + +#if DBG + if (WdDebug) { + WdPrint((0, "WdInitializeAdapter: Interrupt stall time for reset = %d\n", i)); + } +#endif + + if (!auxiliaryStatus.Interrupt) { + + // + // The SCSI protocol chip did not reset properly. Notify the OS port + // driver of the error. + // + + WdPrint((0, "WdInitializeAdapter: SCSI chip reset failed. Aux Status: 0x%.2X.\n", + auxiliaryStatus + )); + + return(FALSE); + } + + + // + // Dismiss the reset interrupt, and + // verify that the SCSI protocol chip reset correctly and that the + // advanced features were enabled. + // + + *((PUCHAR) &interruptStatus) = SCSI_READ(deviceExtension->Adapter, Status); + + if (interruptStatus.PhaseState != RESET_STATUS && + interruptStatus.PhaseState != RESET_WITH_ADVANCED) { + + WdPrint((0, "WdInitializeAdapter: SCSI chip reset failed. Status: 0x%.2X.\n", + *((PUCHAR) &interruptStatus) + )); + + return(FALSE); + } + + // + // Stall the required time to allow the interrupt to clear. + // + + ScsiPortStallExecution(INTERRUPT_CLEAR_TIME); + + // + // Initialize the control register for halt on parity error, + // intermediate disconnect interrupt, ending disconnect interrupt, + // and polled I/O mode. + // + + *((PUCHAR) &control) = 0; + control.HaltOnParity = 1; + control.IntermediateDisconnectInt = 1; + control.EndingDisconnectInt = 1; + + SCSI_WRITE(deviceExtension->Adapter, Control, *((PUCHAR) &control)); + + // + // Set the SelectTimeOut Register to 250ms. This value does not need to + // be reinitialized for each selection. + // + + SCSI_WRITE(deviceExtension->Adapter, Timeout, SELECT_TIMEOUT_VALUE); + + // + // Initialize the source register, in particular, enable reselection. + // + + *((PUCHAR) &sourceId) = 0; + sourceId.EnableReselection = 1; + + SCSI_WRITE(deviceExtension->Adapter, SourceId, *((PUCHAR) &sourceId)); + + return( TRUE ); +} + +BOOLEAN +WdInterruptServiceRoutine( + PVOID ServiceContext + ) +/*++ + +Routine Description: + + The routine is the interrupt service routine for the WD 33C94 SCSI + protocol chip. It is the main SCSI protocol engine of the driver and + is driven by service requests from targets on the SCSI bus. This routine + also detects errors and performs error recovery. Generally, this routine + handles one interrupt per invokation. + + The general flow of this routine is as follows: + + Check for an interrupt. + + Determine if there are any pending errors. + + Determine what interrupt occurred. + + Update the adapter state based on what has occurred. + + Determine what the target wants to do next and program the chip + appropriately. + + Check for the next interrupt. + +Arguments: + + ServiceContext - Supplies a pointer to the device extension for the + interrupting adapter. + +Return Value: + + TRUE - Indicates that an interrupt was found. + + FALSE - Indicates the device was not interrupting. + +--*/ + +{ + PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext; + PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension; + PSCSI_REQUEST_BLOCK srb; + BOOLEAN setAttention; + BOOLEAN waitForInterrupt; + SCSI_STATUS interruptStatus; + SOURCE_ID sourceId; + TARGET_LUN targetLun; + ULONG waitCount; + + /* POWERFAIL */ + + // + // Get the current chip state which includes the auxiliary status + // register, the command phase register and the SCSI status register. + // These registers are frozen until the interrupt register is read. + // + + *((PUCHAR) &deviceExtension->AdapterStatus) = SCSI_READ_AUX( + deviceExtension->Adapter, + AuxiliaryStatus + ); + + // + // Make sure there is really an interrupt before reading the other + // registers, particularly, the interrupt register. + // + + if (!deviceExtension->AdapterStatus.Interrupt) { + return(FALSE); + } + +NextInterrupt: + + *((PUCHAR) &deviceExtension->CommandPhase) = SCSI_READ( + deviceExtension->Adapter, + CommandPhase + ); + + // + // This read will dismiss the interrupt. + // + + *((PUCHAR) &interruptStatus) = SCSI_READ( + deviceExtension->Adapter, + Status + ); + + deviceExtension->AdapterInterrupt = interruptStatus; + + // + // Intialize the logical unit extension pointer. + // Note that this may be NULL. + // + + luExtension = deviceExtension->ActiveLogicalUnit; + + if (luExtension != NULL) { + srb = luExtension->ActiveLuRequest; + } else { + srb = NULL; + } + + // + // If data transfer is active, + // then update the active pointers. + // If a DMA data transfer is complete, then flush the DMA buffer. + // + + if (deviceExtension->AdapterState == DataTransfer) { + ULONG transferCount; + + // + // Get the number of bytes that didn't get transferred, if any. + // + + SCSI_READ_TRANSFER_COUNT(deviceExtension->Adapter, transferCount); + + // + // transferCount now contains the number of bytes that did not + // get transferred. Change it to the number of bytes that did get + // transferred. + // + + transferCount = deviceExtension->ActiveDataLength - transferCount; + + // + // Now figure out if anything remains to be transferred. + // + + luExtension->MaximumTransferLength += transferCount; + deviceExtension->ActiveDataPointer += transferCount; + deviceExtension->ActiveDataLength = srb->DataTransferLength - + luExtension->MaximumTransferLength; + luExtension->SavedDataPointer = deviceExtension->ActiveDataPointer; + luExtension->SavedDataLength = deviceExtension->ActiveDataLength; + + if (deviceExtension->AdapterFlags & PD_DMA_ACTIVE) { + + // + // Flush the DMA buffer to ensure all the bytes are transferred. + // + + ScsiPortFlushDma(deviceExtension); + + // + // Shutdown DMA mode on the card. + // + + CARD_DMA_TERMINATE( deviceExtension ); + + // + // Clear the DMA active flag. + // + + deviceExtension->AdapterFlags &= ~(PD_DMA_ACTIVE | PD_PENDING_DATA_TRANSFER); + + } + } + + // + // Stall after the interrupt has been dismissed. + // This must be done before any commands issued. + // + + ScsiPortStallExecution(INTERRUPT_CLEAR_TIME); + +#if DBG + if (WdDebug) { + WdPrint((0, "WdInterrupt: Adapter Status: 0x%.2X; Adapter Interrupt: %2x; Command Phase: 0x%.2X;\n", + *((PUCHAR) &deviceExtension->AdapterStatus), + *((PUCHAR) &interruptStatus), + *((PUCHAR) &deviceExtension->CommandPhase) + )); + } +#endif + + deviceExtension->InterruptCount++; + waitForInterrupt = FALSE; + + // + // Check for major errors that should never occur. + // + + if (deviceExtension->InterruptCount > MAX_INTERRUPT_COUNT) { + + // + // Things are really messed up. Reset the bus, the chip and + // bail out. + // + + WdPrint((0, + "WdInterruptServiceRoutine: Unexpected error. Interrupt Count=%d\n", + deviceExtension->InterruptCount + )); + + WdDumpState(deviceExtension); + + WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 1); + + WdResetScsiBusInternal(deviceExtension, 0); + + return(TRUE); + } + + // + // Check to see if a subsequent request can be made pending. + // Logically this happens when a select occurs that causes an + // already "pending" request to become an "active" request. A + // select of this type has occured if and only if the adapter + // state is either Select or SelectAndTransfer (attempting to + // select) and the reason for the SCSI protocol chip interrupt + // is something other than one of the following interrupt and + // PhaseState values: + // + // AbortedPaused PAUSED_DURING_RESELECT + // AbortedPaused PAUSED_RESELECT_OR_SELECT + // ServiceRequired SERVICE_RESELECTED + // ServiceRequired SERVICE_RESELECTED_IDENTIFY + // Terminated TERMINATE_SELECT_TIMEOUT + // + + switch (deviceExtension->AdapterState) { + case Select: + case SelectAndTransfer: + if (!interruptStatus.PhaseStateValid) { + if (interruptStatus.AbortedPaused) { + if ((interruptStatus.PhaseState == PAUSED_DURING_RESELECT) || + (interruptStatus.PhaseState == PAUSED_RESELECT_OR_SELECT)) { + break; + } + } else if (interruptStatus.ServiceRequired) { + if ((interruptStatus.PhaseState == SERVICE_RESELECTED) || + (interruptStatus.PhaseState == SERVICE_RESELECTED_IDENTIFY)) { + break; + } + } else if (interruptStatus.Terminated) { + if (interruptStatus.PhaseState == TERMINATE_SELECT_TIMEOUT) { + break; + } + } + } + + // + // A Select has completed or a SelectAndTransfer has begun. + // Set the adapter state appropriately. + // + + if (deviceExtension->AdapterState == Select) { + deviceExtension->AdapterState = MessageOut; + } else { + deviceExtension->AdapterState = CommandOut; + } + + // + // The "pending" srb has now become the active srb. + // Clear the deviceExtension information associated with + // the no longer pending condition and ask for another srb + // (if any) to be made the pending srb. + // + + deviceExtension->AdapterFlags &= ~PD_PENDING_START_IO; + deviceExtension->NextSrbRequest = NULL; + + ScsiPortNotification( + NextRequest, + deviceExtension, + NULL + ); + + break; + } + + + // + // Check for a successful completion interrupt. + // + + if (interruptStatus.CommandComplete && + !interruptStatus.PhaseStateValid) { + + // + // Determine what completed based on the state in the interrupt status. + // + + switch (interruptStatus.PhaseState) { + case COMPLETE_SELECT: + + // + // This case was handled above. + // + + waitForInterrupt = 1; + break; + + case COMPLETE_SELECT_AND_TRANS: + + // + // A select-and-transfer command completed. This implies that + // everything went normally. In particular, all of the data was + // transferred, a SCSI bus status byte was received, the + // command complete message was accepted and the target has + // disconnected. Simulate the state change which would normally + // occur if these events were individual interrupts. + // + + srb = luExtension->ActiveLuRequest; + + // + // Get the status value and indicate it has been received. The + // SCSI status value is saved in the TargetLun register. + // + + srb->ScsiStatus = SCSI_READ(deviceExtension->Adapter, TargetLun); + luExtension->LuFlags |= PD_STATUS_VALID; + + // + // Simulate the COMMAND COMPLETE message. + // + + deviceExtension->MessageCount = 0; + deviceExtension->MessageBuffer[0] = SCSIMESS_COMMAND_COMPLETE; + WdMessageDecode(deviceExtension); + + // + // Indicate that the bus is free. + // Clean up the adapter state to indicate the bus is now free, + // stop the PhaseTimer, and start any pending request. + // + + deviceExtension->AdapterState = BusFree; + deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK; + deviceExtension->ActiveLogicalUnit = NULL; + + if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) { + + // + // Call WdStartIo to start the pending request. + // Note that WdStartIo is idempotent when called with + // the same arguments. + // + + WdStartIo( + deviceExtension, + deviceExtension->NextSrbRequest + ); + + } + + waitForInterrupt = TRUE; + break; + + default: + + // + // Things are really messed up. Reset the bus, the chip and + // bail out. + // + + WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 3); + + WdPrint((0, "WdInterruptServiceRoutine: Unexpected command complete state.\n")); + WdDumpState(deviceExtension); + + + WdResetScsiBusInternal(deviceExtension, 0); + + return(TRUE); + + } + } else if (interruptStatus.CommandComplete && + interruptStatus.PhaseStateValid) { + + // + // A transfer information command completed and the target is + // requesting another bus phase. Process the fact that the transfer + // completed based on the current state. The new request will be + // serviced later. Note that message-in transfers do not cause this + // type of interrupt, because the last byte of the transfer is not + // acknowleged by the SCSI protocol chip. + // + + srb = luExtension->ActiveLuRequest; + + // + // The following states are processed: + // + // CommandOut + // DataTransfer + // DataTransferComplete + // MessageOut + // StatusIn + // + // + + switch (deviceExtension->AdapterState) { + case CommandOut: + case DataTransferComplete: + break; + + case DataTransfer: + + // + // A data transfer completed or is being suspended. + // If no longer in data transfer state, + // then change the state to say so. + // + + switch (interruptStatus.PhaseState) { + case DATA_OUT: + case DATA_IN: + // + // The target device is still in data phase. + // This may be because an even length DMA transfer + // has just completed and an odd length byte still + // remains to be transferred. Check to see if this + // is the case: if so, stay in DataTransfer state + // to transfer the odd byte; else, we have a genuine + // data under run (our data transfer length is less + // than the target's expected data transfer length). + // + + if (deviceExtension->ActiveDataLength == 1) { + break; + } + + // + // else: fall through to next/default case + // + + default: + deviceExtension->AdapterState = DataTransferComplete; + break; + } + + break; + + case MessageOut: + + // + // The SCSI protocol chip indicates that the message has been sent; + // however, the target may need to reread the message or there + // may be more messages to send. This condition is indicated by a + // message-out bus phase; otherwise, the message has been accepted + // by the target. If message has been accepted then check to see + // if any special processing is necessary. Note that the driver + // state is set to MessageOut after the PD_DISCONNECT_EXPECTED is + // set, or after a selection. So it is only necessary to check for + // PD_DISCONNECT_EXPECTED when the driver state is currently in + // MessageOut. + // + + if (deviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED && + interruptStatus.PhaseState != MESSAGE_OUT && + interruptStatus.PhaseState != MESSAGE_IN) { + + // + // If a disconnect was expected and a bus service interrupt + // was detected, then a SCSI protocol error has been + // detected and the SCSI bus should be reset to clear the + // condition. + // + + WdLogError(deviceExtension, SP_PROTOCOL_ERROR, 4); + + WdPrint((0, "WdInterruptServiceRoutine: Bus request while disconnect expected after message-out.\n")); + WdDumpState(deviceExtension); + + WdResetScsiBusInternal(deviceExtension, 0); + + return(TRUE); + + } + + if (deviceExtension->AdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT && + interruptStatus.PhaseState != MESSAGE_OUT && + interruptStatus.PhaseState != MESSAGE_IN) { + + // + // The controller ignored the synchronous transfer message. + // Treat it as a rejection and clear the necessary state. + // + + deviceExtension->ActiveLogicalUnit->LuFlags |= + PD_SYNCHRONOUS_NEGOTIATION_DONE; + deviceExtension->AdapterFlags &= + ~(PD_SYNCHRONOUS_RESPONSE_SENT| + PD_SYNCHRONOUS_TRANSFER_SENT); + } + + if (deviceExtension->AdapterFlags & PD_SYNCHRONOUS_RESPONSE_SENT && + interruptStatus.PhaseState != MESSAGE_OUT && + interruptStatus.PhaseState != MESSAGE_IN) { + + // + // The target controller accepted the negotiation. Set + // the done flag in the logical unit and clear the + // negotiation flags in the adapter. + // + + deviceExtension->ActiveLogicalUnit->LuFlags |= + PD_SYNCHRONOUS_NEGOTIATION_DONE; + deviceExtension->AdapterFlags &= + ~(PD_SYNCHRONOUS_RESPONSE_SENT| + PD_SYNCHRONOUS_TRANSFER_SENT); + + } + + // + // Finally, update the message sent count to indicate that all of + // the current message bytes have been sent. + // + + deviceExtension->MessageSent = deviceExtension->MessageCount; + break; + + case StatusIn: + + // + // Get the status value and indicate it has been received. The + // SCSI status value is data register. + // + +#if DBG + if (!deviceExtension->AdapterStatus.DataBufferReady) { + WdPrint((0, "WdInterruptServiceRoutine: Status in complete and data buffer not ready\n")); + WdDumpState(deviceExtension); + } +#endif + + srb->ScsiStatus = SCSI_READ(deviceExtension->Adapter, Data); + luExtension->LuFlags |= PD_STATUS_VALID; + break; + + default: + + // + // A function complete should not occur while in any other states. + // + + WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 5); + + WdPrint((0, "WdInterruptServiceRoutine: Unexpected function complete interrupt.\n")); + WdDumpState(deviceExtension); + + WdResetScsiBusInternal(deviceExtension, 0); + + return(TRUE); + + } + + + // + // Check for an Aborted or Paused interrupt. + // + + } else if (interruptStatus.AbortedPaused && + !interruptStatus.PhaseStateValid) { + + // + // Determine the cause of this interrupt based on the state value. + // + + switch (interruptStatus.PhaseState) { + case PAUSED_MESSAGE_IN_DONE: + + // + // A message byte has been received. + // Call message decode to determine what to do. The message + // byte will either be accepted, or cause a message to be sent. + // A message-out is indicated to the target by setting the ATN + // line before sending the SCSI protocol chip the MESSAGE_ACCEPTED + // command. + // + // First determine if this is an IDENTIFY message for a reselect. + // If it is, then clear the disconnect expected flag and process + // the reselection. This block of code will respond the to message + // as necessary rather, than calling WdMessageDecode. + // + + if (deviceExtension->MessageBuffer[0] & SCSIMESS_IDENTIFY && + deviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED) { + + // + // Process this message as a reselect. + // + + deviceExtension->AdapterFlags &= ~PD_DISCONNECT_EXPECTED; + *((PUCHAR) &targetLun) = deviceExtension->MessageBuffer[0]; + + // + // Read in the target ID from the source register. + // + + *((PUCHAR) &sourceId) = SCSI_READ( + deviceExtension->Adapter, + SourceId + ); + +#if DBG + if (!sourceId.TargetIdValid || !targetLun.TargetLunValid) { + + WdPrint((0, "WdInterruptServiceRoutine: Reselection data not valid.\n")); + WdPrint((0, + "WdInterruptServiceRoutine: Source ID: 0x%.2X; Target LUN: 0x%.2X;\n", + sourceId, + targetLun + )); + } + +#endif + + WdProcessReselection( + deviceExtension, + (UCHAR)sourceId.TargetId, + (UCHAR)targetLun.LogicalUnitNumber + ); + + break; + + } + + if (WdMessageDecode( deviceExtension )) { + + // + // WdMessageDecode returns TRUE if there is a message to be + // sent out. This message will normally be a MESSAGE REJECT + // or a SYNCHRONOUS DATA TRANSFER REQUEST. In any case, the + // message has been set up by WdMessageDecode. All that needs + // to be done here is to set the ATN signal and set + // PD_MESSAGE_OUT_VALID in the adapter flags. + // + + deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID; + setAttention = TRUE; + + } else { + + setAttention = FALSE; + + } + + // + // In either case, tell the SCSI protocol chip to acknowlege or + // accept the message. The synchronous data transfer parameters + // do not need to be set. + // + + WdAcceptMessage( deviceExtension, setAttention, FALSE); + waitForInterrupt = TRUE; + break; + + case PAUSED_SAVE_POINTER_MESSAGE: + + // + // A SAVE DATA POINTERS message was received. Perform the + // requested operation. Since this is a select-and-transfer + // operation it can be restarted from here. + // The data pointers are saved by copying the active pointers to + // their saved location in the logical unit extension. + // + + luExtension->SavedDataPointer = deviceExtension->ActiveDataPointer; + luExtension->SavedDataLength = deviceExtension->ActiveDataLength; + + // + // Restart the select-and-transfer command where it left off. All + // of the registers are in the correct state, but + // clear the transfer count so no DMA attempts occur. Finally, + // return. + // + + WdIssueCommand( + deviceExtension, // Device Extension. + SELECT_ATN_AND_TRANSFER, // Command to issue. + 0, // New transfer count. + deviceExtension->CommandPhase // New CommandPhase. + ); + + return(TRUE); + + case PAUSED_RESELECT_OR_SELECT: + case PAUSED_DURING_RESELECT: + + // + // An abort during a selection or relection has occurred. The bus + // is free. Clean up the adapter state to indicate the bus + // is now free, and start any pending request. + // + + deviceExtension->AdapterState = BusFree; + deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK; + deviceExtension->ActiveLogicalUnit = NULL; + + if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) { + + // + // Call WdStartIo to start the pending request. + // Note that WdStartIo is idempotent when called with + // the same arguments. + // + + WdStartIo( + deviceExtension, + deviceExtension->NextSrbRequest + ); + + } + + break; + + case PAUSED_NEW_TARGET_RESELECT: + + // + // A new or different target has reselected the SCSI protocol chip. + // First processs the implied disconnect of the previous target by + // clearing the flags and saving the command phase. Then process + // the new reselecting target. + // + + deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK; + luExtension->SavedCommandPhase = deviceExtension->CommandPhase; + + // + // Figure which target reselected. The target ID is in the source + // ID register and the logical unit number is in the target lun + // register. + // + + *((PUCHAR) &sourceId) = SCSI_READ( + deviceExtension->Adapter, + SourceId + ); + + *((PUCHAR) &targetLun) = SCSI_READ( + deviceExtension->Adapter, + TargetLun + ); + +#if DBG + if (!sourceId.TargetIdValid || !targetLun.TargetLunValid) { + + WdPrint((0, "WdInterruptServiceRoutine: Reselection data not valid.\n")); + WdPrint((0, + "WdInterruptServiceRoutine: Source ID: 0x%.2X; Target LUN: 0x%.2X;\n", + sourceId, + targetLun + )); + } + +#endif + + // + // Check that the message which was read in is a valid IDENTIFY + // message. If it is not reject the message. + // + + if (!targetLun.TargetLunValid) { + + // + // This is a bogus message and should be aborted. + // Send an abort message. Put the message in the buffer, set + // the state, indicate that a disconnect is expected after + // this, and set the attention signal. + // + + deviceExtension->MessageBuffer[0] = SCSIMESS_ABORT; + deviceExtension->MessageCount = 1; + deviceExtension->MessageSent = 0; + deviceExtension->AdapterState = MessageOut; + deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK; + deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID | + PD_DISCONNECT_EXPECTED; + + // + // The bus is waiting for the message to be accepted. The + // attention signal will be set since this is not a valid + // reselection. Finally, the synchronous data tranfer + // parameters need to be set in case a data transfer is done. + // + + WdAcceptMessage(deviceExtension, TRUE, TRUE); + deviceExtension->InterruptCount = 0; + break; + + } + + WdProcessReselection( + deviceExtension, + (UCHAR)sourceId.TargetId, + (UCHAR)targetLun.LogicalUnitNumber + ); + + break; + + default: + + // + // A phased or abort interrupt should not occur with any other + // state values. + // + + WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 6); + + WdPrint((0, "WdInterruptServiceRoutine: Unexpected paused/aborted interrupt.\n")); + WdDumpState(deviceExtension); + WdResetScsiBusInternal(deviceExtension, 0); + + return(TRUE); + + } + + // + // Look for a service required interrupt. + // + + } else if (interruptStatus.ServiceRequired && + !interruptStatus.PhaseStateValid) { + + // + // A service-required interrupt has occurred. Determine what happened + // based on the PhaseState code. + // + + switch (interruptStatus.PhaseState) { + case SERVICE_RESELECTED: + + // + // A target reselected; however, the IDENTIFY message has not been + // received. Indicate that a disconnect is expected. This will + // only allow the target to perform a message-in or a message-out. + // When the IDENTIFY message is actually received then the disconnect + // expected flag will be cleared. + // + + deviceExtension->MessageCount = 0; + deviceExtension->AdapterFlags &= ~PD_MESSAGE_OUT_VALID; + deviceExtension->AdapterState = MessageOut; + deviceExtension->InterruptCount = 0; + deviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED; + waitForInterrupt = TRUE; + + break; + + case SERVICE_RESELECTED_IDENTIFY: + + // + // A target reselected, and an IDENTIFY message has been received. + // The target ID is in the SourceId register and the IDENTIFY + // message is in the Data register. Get these values and process + // the reselection. Note that the format of the identify message + // is identical to that of the TargetId register. + // + + *((PUCHAR) &sourceId) = SCSI_READ( + deviceExtension->Adapter, + SourceId + ); + + *((PUCHAR) &targetLun) = SCSI_READ( + deviceExtension->Adapter, + Data + ); + +#if DBG + if (!sourceId.TargetIdValid || !targetLun.TargetLunValid) { + + WdPrint((0, "WdInterruptServiceRoutine: Reselection data not valid.\n")); + WdPrint((0, + "WdInterruptServiceRoutine: Source ID: 0x%.2X; Target LUN: 0x%.2X;\n", + sourceId, + targetLun + )); + } + +#endif + // + // Check that the message which was read in is a valid IDENTIFY + // message. If it is not reject the message. + // + + if (!targetLun.TargetLunValid) { + + // + // This is a bogus message and should be aborted. + // Send an abort message. Put the message-in the buffer, set + // the state, indicate that a disconnect is expected after + // this, and set the attention signal. + // + + deviceExtension->MessageBuffer[0] = SCSIMESS_ABORT; + deviceExtension->MessageCount = 1; + deviceExtension->MessageSent = 0; + deviceExtension->AdapterState = MessageOut; + deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK; + deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID | + PD_DISCONNECT_EXPECTED; + + // + // The bus is waiting for the message to be accepted. The + // attention signal will be set since this is not a valid + // reselection. Finally, the synchronous data tranfer + // parameters need to be set in case a data transfer is done. + // + + WdAcceptMessage(deviceExtension, TRUE, TRUE); + deviceExtension->InterruptCount = 0; + break; + + } + + WdProcessReselection( + deviceExtension, + (UCHAR)sourceId.TargetId, + (UCHAR)targetLun.LogicalUnitNumber + ); + + break; + + case SERVICE_DISCONNECTED: + + // + // A disconnect has occurred. Clean up the state and check for + // pending requests. + // Check to see if this was a send-message request which is + // completed when the disconnect occurs. + // + + if (deviceExtension->AdapterFlags & PD_SEND_MESSAGE_REQUEST) { + + // + // Complete the request. + // + + WdCompleteSendMessage( deviceExtension, + SRB_STATUS_SUCCESS + ); + } + + // + // Save the command phase for the logical unit. + // + + luExtension->SavedCommandPhase = deviceExtension->CommandPhase; + + // + // If this disconnect was not expected or the command phase is not + // correct for a legal disconnect, then this is a unexpected + // disconnect. + // + + if (deviceExtension->CommandPhase != PHASE_LEGAL_DISCONNECT && + !(deviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED)) { + + // + // An unexpected disconnect occurred; make sure this is not + // related to a synchronous message. + // + + if (deviceExtension->AdapterFlags & + (PD_SYNCHRONOUS_RESPONSE_SENT | PD_SYNCHRONOUS_TRANSFER_SENT | + PD_POSSIBLE_EXTRA_MESSAGE_OUT)) { + + // + // This target cannot negotiate properly. Set a flag to + // prevent further attempts and set the synchronous + // parameters to use asynchronous data transfer. + // + + /* TODO: Consider propagating this flag to all the Lus on this target. */ + luExtension->LuFlags |= PD_DO_NOT_NEGOTIATE; + luExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET; + luExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD; + + } + + WdPrint((0, "WdInterruptServiceRoutine: Unexpected bus disconnect\n")); + WdLogError(deviceExtension, SP_UNEXPECTED_DISCONNECT, 7); + + } + + // + // Clean up the adapter state to indicate the bus is now free, enable + // reselection, and start any pending request. + // + + deviceExtension->AdapterState = BusFree; + deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK; + deviceExtension->ActiveLogicalUnit = NULL; + +#if DBG + if (WdDebug) { + WdPrint((0, "WdInterruptServiceRoutine: DisconnectComplete.\n")); + } +#endif + + if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) { + + // + // Call WdStartIo to start the pending request. + // Note that WdStartIo is idempotent when called with + // the same arguments. + // + + WdStartIo( + deviceExtension, + deviceExtension->NextSrbRequest + ); + + } + + break; + + default: + + // + // This interrupt should not occur with any other + // state values. + // + + WdPrint((0, "WdInterruptServiceRoutine: Unexpected service required interrupt.\n")); + WdDumpState(deviceExtension); + WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 8); + + WdResetScsiBusInternal(deviceExtension, 0); + + return(TRUE); + } + + + // + // Look for a terminated interrupt. + // + + } else if (interruptStatus.Terminated && + !interruptStatus.PhaseStateValid) { + + // + // A terminated interrupt occurred. Decode the state information to + // determine why this happened. + // + + switch (interruptStatus.PhaseState) { + case TERMINATE_INVALID_COMMAND: + + // + // The chip detected an invalid command. This may occur during + // normal operation if a select is attempted at the same time that + // a reselect occurred. + // + +#if DBG + WdPrint((0, "WdInterruptServiceRoutine: Invalid command interrupt occurred\n")); + WdDumpState(deviceExtension); +#endif + + WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 9); + + // + // Things appear to be messed up. Reset the bus and the chip. + // + + WdResetScsiBusInternal(deviceExtension, 0); + + return(TRUE); + break; + + case TERMINATE_UNEXPECTED_DISC: + + // + // An unexpected disconnect occurred; make sure this is not + // related to a synchronous message. + // + + if (deviceExtension->AdapterFlags & + (PD_SYNCHRONOUS_RESPONSE_SENT | PD_SYNCHRONOUS_TRANSFER_SENT | + PD_POSSIBLE_EXTRA_MESSAGE_OUT)) { + + // + // This target cannot negotiate properly. Set a flag to + // prevent further attempts and set the synchronous + // parameters to use asynchronous data transfer. + // + + /* TODO: Consider propagating this flag to all the Lus on this target. */ + luExtension->LuFlags |= PD_DO_NOT_NEGOTIATE; + luExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET; + luExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD; + + } + + // + // An unexpected disconnect has occurred. Log the error. It is + // not clear if the device will respond again, so let the time-out + // code clean up the request if necessary. + // + + WdPrint((0, "WdInterruptServiceRoutine: Unexpected bus disconnect\n")); + WdLogError(deviceExtension, SP_UNEXPECTED_DISCONNECT, 10); + + + + // + // Clean up the adapter state to indicate the bus is now free, + // and start any pending request. + // + + deviceExtension->AdapterState = BusFree; + deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK; + deviceExtension->ActiveLogicalUnit = NULL; + +#if DBG + if (WdDebug) { + WdPrint((0, "WdInterruptServiceRoutine: DisconnectComplete.\n")); + } +#endif + + if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) { + + // + // Call WdStartIo to start the pending request. + // Note that WdStartIo is idempotent when called with + // the same arguments. + // + + WdStartIo( + deviceExtension, + deviceExtension->NextSrbRequest + ); + + } + + break; + + case TERMINATE_SELECT_TIMEOUT: + + // + // The target selection failed. Log the error. If the retry + // count is not exceeded then retry the selection; otherwise + // fail the request. + // + + if (luExtension->RetryCount++ >= RETRY_SELECTION_LIMIT) { + + // + // Clear the Active request in the logical unit. + // + + if (deviceExtension->AdapterFlags & PD_SEND_MESSAGE_REQUEST) { + luExtension->ActiveSendRequest = NULL; + } else { + luExtension->ActiveLuRequest = NULL; + } + + luExtension->RetryCount = 0; + deviceExtension->NextSrbRequest->SrbStatus = + SRB_STATUS_SELECTION_TIMEOUT; + + ScsiPortNotification( + RequestComplete, + deviceExtension, + deviceExtension->NextSrbRequest + ); + + deviceExtension->NextSrbRequest = NULL; + deviceExtension->AdapterFlags &= ~PD_PENDING_START_IO; + + ScsiPortNotification( + NextRequest, + deviceExtension, + NULL + ); + } + + // + // Clean up the adapter state to indicate the bus is now free, + // and start any pending request. + // + + deviceExtension->AdapterState = BusFree; + deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK; + deviceExtension->ActiveLogicalUnit = NULL; + +#if DBG + if (WdDebug) { + WdPrint((0, "WdInterruptServiceRoutine: DisconnectComplete.\n")); + } +#endif + + if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) { + + // + // Call WdStartIo to start the pending request. + // Note that WdStartIo is idempotent when called with + // the same arguments. + // + + WdStartIo( + deviceExtension, + deviceExtension->NextSrbRequest + ); + + } + + break; + + case TERMINATE_PARITY_NO_ATN: + case TERMINATE_PARITY_STATUS_IN: + case TERMINATE_PARITY_WITH_ATN: + + // + // The SCSI protocol chip has set not ATN; we expect the target to + // go into message-out so that a error message can be sent and the + // operation retried. After the error has been noted, continue + // processing the interrupt. The message sent depends on whether a + // message was being received: if the adapter + // state is currently message-in then send-message PARITY ERROR; + // otherwise, send INITIATOR DETECTED ERROR. + // + + WdPrint((0, "WdInterruptServiceRoutine: Parity error detected.\n")); + WdDumpState(deviceExtension); + + if (!(deviceExtension->AdapterFlags & PD_PARITY_ERROR)) { + + // + // Only log one parity error per request. + // + + WdLogError(deviceExtension, SP_BUS_PARITY_ERROR, 11); + } + + // + // If the ATN single has not been set then set it and clear the ACK + // signal. + // + + if (!(interruptStatus.PhaseState == TERMINATE_PARITY_WITH_ATN)) { + + // + // The ATN signal must be set. + // + + WdAcceptMessage(deviceExtension, TRUE, FALSE); + + } else { + + // + // ATN is already set so just clear ACK. + // + + WdAcceptMessage(deviceExtension, FALSE, FALSE); + + } + + + deviceExtension->MessageBuffer[0] = + deviceExtension->AdapterState == MessageIn ? + SCSIMESS_MESS_PARITY_ERROR : SCSIMESS_INIT_DETECTED_ERROR; + deviceExtension->MessageCount = 1; + deviceExtension->MessageSent = 0; + deviceExtension->AdapterState = MessageOut; + deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID | PD_PARITY_ERROR; + + break; + + case TERMINATE_NEW_TRAGET_NO_ID: + + // + // First processs the implied disconnect of the previous target by + // clearing the flags and saving the command phase. Then process + // the new reselecting target. + // + + deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK; + luExtension->SavedCommandPhase = deviceExtension->CommandPhase; + + // + // A new target as reselected; however, the IDENTIFY message has + // not been received yet. The target Id is in the destination + // register. Indicate that a disconnect is expected. This will + // only allow the target to perform a message-in or the message-out. + // When the IDENTIFY message is actually received then the disconnect + // expected flag will be cleared. + // + + deviceExtension->MessageCount = 0; + deviceExtension->AdapterFlags &= ~PD_MESSAGE_OUT_VALID; + deviceExtension->AdapterState = MessageOut; + deviceExtension->InterruptCount = 0; + deviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED; + waitForInterrupt = TRUE; + + break; + + + default: + + // + // This interrupt should not occur with any other + // state values. + // + + WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 12); + + WdPrint((0, "WdInterruptServiceRoutine: Unexpected terminated interrupt.\n")); + WdDumpState(deviceExtension); + WdResetScsiBusInternal(deviceExtension, 0); + + return(TRUE); + + } + + } else if (interruptStatus.Terminated && + interruptStatus.PhaseStateValid) { + + // + // A select-and-transfer command was halted because of an unexpected + // phase or because a simple transfer command was not completed because + // the target switched to a new phase. + // If this was part of a synchronous negotiation and the phase is + // message-out, then the target do not read all of the message bytes + // may later request extra bytes. + // + + if (deviceExtension->AdapterState == MessageOut) { + + ULONG count; + + // + // Read the transfer counter to determine how many message bytes + // have been sent to the target and update the message sent count. + // This is necessary in case the message out needs to be restarted. + // + + SCSI_READ_TRANSFER_COUNT(deviceExtension->Adapter, count); + + deviceExtension->MessageSent = (UCHAR)(deviceExtension->MessageCount - + count); + + if (deviceExtension->AdapterFlags & (PD_SYNCHRONOUS_TRANSFER_SENT | + PD_SYNCHRONOUS_RESPONSE_SENT)) { + + // + // The target do not read all of the message bytes as it should + // have. This is not a problem except that some targets will + // come back later and try to read more bytes. Indicate that + // this is ok. + // + + deviceExtension->AdapterFlags |= PD_POSSIBLE_EXTRA_MESSAGE_OUT; + + } + + } + + // + // Based on the command phase determine if any data needs to be saved. + // The important cases are: + // + // Status byte read in. + // Command complete message read in. + // + + switch (deviceExtension->CommandPhase) { + case PHASE_STATUS_RECEIVED: + + // + // Get the status value and indicate it has been received. The + // SCSI status value is TargetLun. + // + + srb->ScsiStatus = SCSI_READ(deviceExtension->Adapter, TargetLun); + luExtension->LuFlags |= PD_STATUS_VALID; + break; + + case PHASE_COMPLETE_RECEIVED: + + // + // The request is almost complete. A status has been received and + // the COMMAND COMPLETE message has been received; however, the + // target has not disconnected yet. + // + + srb = luExtension->ActiveLuRequest; + + // + // Get the status value and indicate it has been received. The + // SCSI status value is saved the the TargetLun register. + // + + srb->ScsiStatus = SCSI_READ(deviceExtension->Adapter, TargetLun); + luExtension->LuFlags |= PD_STATUS_VALID; + + // + // Simulate the COMMAND COMPLETE message. + // + + deviceExtension->MessageCount = 1; + deviceExtension->MessageBuffer[0] = SCSIMESS_COMMAND_COMPLETE; + WdMessageDecode(deviceExtension); + + // + // The next thing which should occur is a disconnect; however, this + // point is reached when the target makes a new request after the + // COMMAND COMPLETE message. WdMessageDecode has set the + // AdapterState correct, so try and process the target's request. + // This usually occurs with screwy targets which are messed up by + // synchronous negotiation messages. + // + + break; + } + + // + // Check for a reset interrupt. This is indicated by an interrupt with + // no interrupt bits set. + // + + } else if (!interruptStatus.PhaseStateValid) { + + // + // The SCSI protocol chip was reset. + // + + WdPrint((0, "WdInterruptServiceRoutine: The SCSI protocol chip was reset.\n")); + WdDumpState(deviceExtension); + + if (interruptStatus.PhaseState != RESET_WITH_ADVANCED && + interruptStatus.PhaseState != RESET_STATUS) { + WdPrint((0, "WdInterruptServiceRoutine: SCSI chip reset failed. Status: 0x%.2X.\n", + *((PUCHAR) &interruptStatus) + )); + + WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 13); + + } + + if (interruptStatus.PhaseState == RESET_STATUS ) { + + OWN_ID ownId; + + // + // The OwnId register must be set when the SCSI protocol chip is reset. + // Initialize the ownId with the adapter's host ID, advanced features, + // and the correct clock frequency select. Note the CdbSize register is + // used as the OwnId register when a reset command is issued. + // + + *((PUCHAR) &ownId) = 0; + ownId.InitiatorId = deviceExtension->InitiatorBusId; + ownId.AdvancedFeatures = 1; + ownId.FrequencySelect = CLOCK_CONVERSION_FACTOR; + + SCSI_WRITE( deviceExtension->Adapter, CdbSize, *((PUCHAR) &ownId) ); + + // + // Issue a reset-chip command. + // + + WdIssueCommand(deviceExtension, RESET_SCSI_CHIP, -1, 0); + + return(TRUE); + + } else { + + SCSI_CONTROL control; + + // + // Clean up the logical units and notify the port driver, + // then return. + // + + WdCleanupAfterReset(deviceExtension, TRUE); + ScsiPortNotification( + ResetDetected, + deviceExtension, + NULL + ); + + // + // Set the control register for halt on parity error, halt on ATN, ending + // disconnect interrupt, and normal DMA mode. + // + + *((PUCHAR) &control) = 0; + control.HaltOnParity = 1; + control.HaltOnAtn = 1; + control.IntermediateDisconnectInt = 1; + control.EndingDisconnectInt = 1; + control.DmaModeSelect = CARD_DMA_MODE; + + SCSI_WRITE(deviceExtension->Adapter, Control, *((PUCHAR) &control)); + + // + // Set the SelectTimeOut Register to 250ms. This value does not need to + // be reinitialized for each selection. + // + + SCSI_WRITE(deviceExtension->Adapter, Timeout, SELECT_TIMEOUT_VALUE); + + // + // Initialize the source register, in particular, enable reselection. + // + + *((PUCHAR) &sourceId) = 0; + sourceId.EnableReselection = 1; + + SCSI_WRITE(deviceExtension->Adapter, SourceId, *((PUCHAR) &sourceId)); + + // + // DO NOT REMOVE THE FOLLOWING SCSI_WRITE( )! + // It may look superfluous, but it is not. + // It is here to keep another driver in the system that + // is erroneously outputting to I/O port address 0x361 + // during setup from triggering an unexpected interrupt + // from the Maynard card and hence crashing this driver. + // + + SCSI_WRITE(deviceExtension->Adapter, Data, 0); + + deviceExtension->AdapterState = BusFree; + deviceExtension->ActiveLogicalUnit = NULL; + + return(TRUE); + + } + + } + + // + // Check for to see if the traget is + // is requesting some form of bus-transfer. The bus transfer type is + // determined by the bus phase. This is indicated by the + // StatusPhaseValid + // + + if (interruptStatus.PhaseStateValid) { + + // + // The bus is changing phases or needs more data. + // + + if (deviceExtension->AdapterState == MessageOut) { + + // + // The adapter state indicates that a message has been + // sent. The target may need to reread it or there may + // be more messages to send: this condition is indicated + // by a message-out bus phase. Otherwise, the message has + // been accepted by the target. Note that the driver state + // is set to MessageOut after PD_DISCONNECT_EXPECTED is set, + // or after a selection. So it is only necessary to check + // for PD_DISCONNECT_EXPECTED when the driver state is + // MessageOut. + // + + if ((deviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED) && + (interruptStatus.PhaseState != MESSAGE_OUT) && + (interruptStatus.PhaseState != MESSAGE_IN)) { + + // + // If a disconnect was expected and a bus service + // interrupt was detected, then a SCSI protocol error + // has been detected and the SCSI bus should be reset + // to clear the condition. + // + + WdLogError(deviceExtension, SP_PROTOCOL_ERROR, 14); + WdPrint((0, "WdInterruptServiceRoutine: Bus request while disconnect expected after message-out.\n")); + WdDumpState(deviceExtension); + + WdResetScsiBusInternal(deviceExtension, 0); + + return(TRUE); + + } + } + + // + // Decode the current bus phase. + // + + switch (interruptStatus.PhaseState) { + + case COMMAND_OUT: + + // + // Transfer the SCSI command block to the chip. + // + + deviceExtension->AdapterState = CommandOut; + + WdTransferInformation( + deviceExtension, + srb->Cdb, + srb->CdbLength, + TRUE + ); + + break; + + case STATUS_IN: + + // + // Setup of the SCSI protocol chip to read in the status, read the + // following message byte, and wait for the final disconnect and + // set the adapter state. + // + + deviceExtension->AdapterState = MessageAccepted; + + + // + // Clear the transfer counter registers. This is necessary for the + // chip to really believe that the transfer has completed. + // Set the CommandPhase register to resume with a status-in phase, + // and command the SCSI protocol chip to resume a + // select-and-transfer command. + // + + WdIssueCommand( + deviceExtension, // Device Extension. + SELECT_ATN_AND_TRANSFER, // Command to issue. + 0, // New transfer count. + PHASE_DATA_TRANSFER_DONE // New CommandPhase. + ); + + break; + + case MESSAGE_OUT: + + // + // The target is requesting a message-out. There are four + // possible cases: + // + // 1. ATN has been asserted (because of a data under + // run condition) to force the target out of data + // transfer phase and into message out phase. + // + // 2. The target is improperly requesting a message. + // + // 3. A message has been sent, but the target could not + // read it properly. + // + // 4. It is a "normal" message out: all or the remainder + // of a message is ready and waiting to be sent. + // + + // + // The first case is indicated when the adapter state is + // DataTransferComplete. + // + + if (deviceExtension->AdapterState == DataTransferComplete) { + + // + // The target was trying to go into, or stay in, + // data transfer phase, but we did not expect it + // to do so. Complete the request here and now + // and send an abort message to tell the target + // to abort the transfer. + // + + srb->ScsiStatus = SCSISTAT_GOOD; + luExtension->LuFlags &= ~PD_STATUS_VALID; + WdProcessRequestCompletion(deviceExtension); + ScsiPortNotification( + RequestComplete, + deviceExtension, + srb + ); + + deviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED; + deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID; + deviceExtension->MessageBuffer[0] = SCSIMESS_ABORT; + deviceExtension->MessageCount = 1; + deviceExtension->MessageSent = 0; + } + + // + // The second case is indicated when the MessageCount is + // zero or the message-out flag is not set. + // + + if ( deviceExtension->MessageCount == 0 || + !(deviceExtension->AdapterFlags & PD_MESSAGE_OUT_VALID) + ) { + + // + // If extra message-outs are possible then just send a NOP + // message. + + if (deviceExtension->AdapterFlags & + PD_POSSIBLE_EXTRA_MESSAGE_OUT) { + + // + // Set the message to NOP and clear the extra message + // flag. This is a hack for controllers that do not + // properly read the entire message. + // + + deviceExtension->MessageBuffer[0] = SCSIMESS_NO_OPERATION; + deviceExtension->AdapterFlags &= + ~PD_POSSIBLE_EXTRA_MESSAGE_OUT; + } else { + + // + // Send an INITIATOR DETECTED ERROR message. + // + + deviceExtension->MessageBuffer[0] = + SCSIMESS_INIT_DETECTED_ERROR; + + WdLogError(deviceExtension, SP_PROTOCOL_ERROR, 15); + + WdPrint((0, "WdInterruptServiceRoutine: Unexpected message-out request\n")); + WdDumpState(deviceExtension); + + } + + deviceExtension->AdapterState = MessageOut; + deviceExtension->MessageCount = 1; + deviceExtension->MessageSent = 0; + + } + + // + // The third case is indicated when MessageCount and MessageSent + // are equal and nonzero (note: MessageCount can't be zero at + // this point because of the first or second case above). + // + + if (deviceExtension->MessageCount == deviceExtension->MessageSent){ + + // + // The message needs to be re-sent, so clear MessageSent + // and fall through to the next case. + // + + deviceExtension->MessageSent = 0; + } + + // + // The fourth case and/or fallout from the cases above is + // taken care of by default hereinafter. + // + + // + // Clear the parity error flag. + // + + deviceExtension->AdapterFlags &= ~PD_PARITY_ERROR; + + // + // Tell the SCSI protocol chip to "go" and + // transfer the message to the data register. + // + + deviceExtension->AdapterState = MessageOut; + SCSI_WRITE(deviceExtension->Adapter, Command, ASSERT_ATN); + WdTransferInformation( + deviceExtension, + &deviceExtension->MessageBuffer[deviceExtension->MessageSent], + deviceExtension->MessageCount - deviceExtension->MessageSent, + TRUE + ); + + break; + + case MESSAGE_IN: + + // + // If this is the first byte of the message then initialize + // MessageCount and the adapter state. The message buffer + // cannot overflow because the message decode function will + // take care of the message before the buffer is full. + // The SCSI protocol chip will interrupt for each message + // byte. + // + + if ( deviceExtension->AdapterState != MessageIn && + deviceExtension->AdapterState != MessageAccepted ) { + + deviceExtension->AdapterFlags &= ~PD_MESSAGE_OUT_VALID; + deviceExtension->MessageCount = 0; + } + + deviceExtension->AdapterState = MessageIn; + waitForInterrupt = TRUE; + + // + // Set the transfer counter registers to one byte and write + // the command register. + // + + WdTransferInformation( + deviceExtension, + &deviceExtension->MessageBuffer[deviceExtension->MessageCount++], + 1, + FALSE + ); + + break; + + case DATA_OUT: + case DATA_IN: + + if (deviceExtension->AdapterState == CommandOut) { + // + // Check that the transfer direction is ok. + // + + if ((!(srb->SrbFlags & SRB_FLAGS_DATA_IN) && + (interruptStatus.PhaseState == DATA_IN)) || + + (!(srb->SrbFlags & SRB_FLAGS_DATA_OUT) && + (interruptStatus.PhaseState == DATA_OUT))) { + + // + // The data direction is incorrect. + // Reset the bus to clear things up. + // + + WdPrint((0, "WdInterruptServiceRoutine: Illegal transfer direction.\n")); + WdDumpState(deviceExtension); + + WdLogError(deviceExtension, SP_PROTOCOL_ERROR, 16); + + WdResetScsiBusInternal(deviceExtension, 0); + return(TRUE); + } + } + + if (deviceExtension->ActiveDataLength == 0) { + WdPrint((0, "WdInterruptServiceRoutine: Data underrun!\n")); + + // + // We have a data under run condition! + // The target is trying to go into, or stay in, + // data transfer phase, but we did not expect it + // to do so. Set ATN to force the target out of + // data phase and into message out phase in order + // to tell the target to abort the data transfer. + // + + deviceExtension->InterruptCount--; + deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID; + SCSI_WRITE(deviceExtension->Adapter, Command, ASSERT_ATN); + WdTransferInformation( + deviceExtension, + &deviceExtension->MessageBuffer[MESSAGE_BUFFER_SIZE-1], + 1, + (BOOLEAN)((interruptStatus.PhaseState == DATA_OUT)? TRUE : FALSE) + ); + waitForInterrupt = TRUE; + + } else if (deviceExtension->ActiveDataLength == 1) { + + // + // We have a final (or only), odd length byte to transfer. + // + + deviceExtension->AdapterState = DataTransfer; + WdTransferInformation( + deviceExtension, + (PUCHAR)deviceExtension->ActiveDataPointer, + 1, + (BOOLEAN) ((interruptStatus.PhaseState == DATA_OUT)? TRUE : FALSE) + ); + + waitForInterrupt = TRUE; + + } else { + + // + // Setup a DMA data transfer. + // + + deviceExtension->DmaPhase = 0; + deviceExtension->DmaCommand = TRANSFER_INFORMATION; + deviceExtension->AdapterState = DataTransfer; + deviceExtension->AdapterFlags |= PD_DMA_ACTIVE | + PD_PENDING_DATA_TRANSFER; + deviceExtension->ActiveDataLength &= ~((ULONG)1); + + ScsiPortIoMapTransfer( + deviceExtension, + srb, + (PVOID) deviceExtension->ActiveDataPointer, + deviceExtension->ActiveDataLength + ); + } + return(TRUE); + break; + + default: + + // + // This phase is illegal and indicates a serious error. Reset the + // bus to clear the problem. + // + + WdLogError(deviceExtension, SP_PROTOCOL_ERROR, 17); + + WdPrint((0, "WdInterruptServiceRoutine: Illegal bus state detected.\n")); + WdDumpState(deviceExtension); + + WdResetScsiBusInternal(deviceExtension, 0); + + return(TRUE); + } + } + + // + // If an interrupt is expected, then wait a short time for it. + // + + if (waitForInterrupt) { + + for (waitCount = 0; waitCount < INTERRUPT_STALL_TIME; waitCount++) { + + ScsiPortStallExecution(1); + + // + // Read the auxilary status register to determine if the there is + // an interrupt. + // + + *((PUCHAR) &deviceExtension->AdapterStatus) = SCSI_READ_AUX( + deviceExtension->Adapter, + AuxiliaryStatus + ); + + // + // If there is an interrupt, then start to process it. + // + + if (deviceExtension->AdapterStatus.Interrupt) { + goto NextInterrupt; + } + } + } + + return(TRUE); +} + +VOID +WdLogError( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN ULONG ErrorCode, + IN ULONG UniqueId + ) +/*++ + +Routine Description: + + This routine logs an error. + +Arguments: + + DeviceExtension - Supplies a pointer to the device extension for the + port adapter to which the completing target controller is connected. + + ErrorCode - Supplies the error code to log with the error. + + UniqueId - Supplies the unique error identifier. + +Return Value: + + None. + +--*/ +{ + + PSCSI_REQUEST_BLOCK srb; + + // + // Look for a current request in the device extension. + // + + if (DeviceExtension->ActiveLogicalUnit != NULL) { + + if (DeviceExtension->ActiveLogicalUnit->ActiveLuRequest != NULL) { + + srb = DeviceExtension->ActiveLogicalUnit->ActiveLuRequest; + + } else { + + srb = DeviceExtension->ActiveLogicalUnit->ActiveSendRequest; + + } + } else { + + srb = DeviceExtension->NextSrbRequest; + + } + + // + // If the srb is NULL, then log the error against the host adapter address. + // + + if (srb == NULL) { + + ScsiPortLogError( + DeviceExtension, // HwDeviceExtension, + NULL, // Srb + 0, // PathId, + DeviceExtension->InitiatorBusId, // TargetId, + 0, // Lun, + ErrorCode, // ErrorCode, + UniqueId // UniqueId + ); + + } else { + + ScsiPortLogError( + DeviceExtension, // HwDeviceExtension, + srb, // Srb + srb->PathId, // PathId, + srb->TargetId, // TargetId, + srb->Lun, // Lun, + ErrorCode, // ErrorCode, + UniqueId // UniqueId + ); + + } + +} + +VOID +WdProcessRequestCompletion( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension + ) +/*++ + +Routine Description: + + This routine does all of checking and state updating necessary when a + request terminates normally. It determines what the SrbStatus + should be and updates the state in the DeviceExtension, the + logicalUnitExtension and the srb. + +Arguments: + + DeviceExtension - Supplies a pointer to the device extension for the + port adapter to which the completing target controller is connected. + +Return Value: + + None. + +--*/ + +{ + PSCSI_REQUEST_BLOCK srb; + PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension; + + luExtension = DeviceExtension->ActiveLogicalUnit; + srb = luExtension->ActiveLuRequest; + + if ( srb->ScsiStatus != SCSISTAT_GOOD && + srb->ScsiStatus != SCSISTAT_CONDITION_MET && + srb->ScsiStatus != SCSISTAT_INTERMEDIATE && + srb->ScsiStatus != SCSISTAT_INTERMEDIATE_COND_MET ) { + + // + // Indicate an abnormal status code. + // + + srb->SrbStatus = SRB_STATUS_ERROR; + + // + // Add in the INITIATE RECOVERY flag if it was received. This + // indicates to the class driver that it must send a TERMINATE + // RECOVERY message before the logical unit will resume normal + // operation. + // + +#ifdef SRB_INITIATE_RECOVERY + if (DeviceExtension->ActiveLogicalUnit->LuFlags & + PD_INITIATE_RECOVERY) { + + // + // Modify the SrbStatus. + // + + srb->SrbStatus |= SRB_INITIATE_RECOVERY; + } +#endif + + // + // If this is a check condition, then clear the synchronous negotiation + // done flag. This is done in case the controller was power cycled. + // + + if (srb->ScsiStatus == SCSISTAT_CHECK_CONDITION) { + + luExtension->LuFlags &= ~PD_SYNCHRONOUS_NEGOTIATION_DONE; + + } + + } else { + + // + // Everything looks correct so far. + // + + srb->SrbStatus = SRB_STATUS_SUCCESS; + + // + // Make sure that status is valid. + // + + if (!(luExtension->LuFlags & PD_STATUS_VALID)) { + + // + // The status byte is not valid. + // + + srb->SrbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE; + + // + // Log the error. + // + + WdLogError(DeviceExtension, SP_PROTOCOL_ERROR, 20); + + } + + } + + // + // Check that data was transferred to the end of the buffer. + // + + if ( luExtension->MaximumTransferLength != srb->DataTransferLength ){ + + // + // The entire buffer was not transferred. Update the length + // and update the status code. + // + +#if DBG + if (srb->SrbStatus == SRB_STATUS_SUCCESS) { + + WdPrint((1, "WdProcessRequestCompletion: Short transfer, Actual: %lu; Expected: %lu;\n", + luExtension->MaximumTransferLength, + srb->DataTransferLength + )); + + } +#endif + srb->DataTransferLength = luExtension->MaximumTransferLength; + + // + // If the request was ok upto this point then over write the status + // with data overrun. + // + + if (srb->SrbStatus == SRB_STATUS_SUCCESS) { + + srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; + + } + } + +#if DBG + if (srb->SrbStatus != SRB_STATUS_SUCCESS) { + WdPrint((1, "WdProcessRequestCompletion: Request failed. ScsiStatus: 0x%.2X, SrbStatus: 0x%.2X\n", + srb->ScsiStatus, + srb->SrbStatus + )); + } +#endif + + // + // Clear the request but not the ActiveLogicalUnit since the target has + // not disconnected from the SCSI bus yet. + // + + luExtension->ActiveLuRequest = NULL; + luExtension->RetryCount = 0; + luExtension->LuFlags &= ~PD_LU_COMPLETE_MASK; +} + +BOOLEAN +WdProcessReselection( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN UCHAR TargetId, + IN UCHAR LogicalUnitNumber + ) + +/*++ + +Routine Description: + + This function updates the device extension and the logical unit extension + when target reselects. If necessary, the DMA is set up. This routine + should be called before the identify message has been accepted. The SCSI + protocol chip will be restarted with either a select-and-transfer command + or a message accept. + +Arguments: + + DeviceExtension - Supplies a pointer to the specific device extension. + + TargetId - Supplies the target id of the reselecting target. + + LogicalUnitNumber - Supplies the logical unit number of the reselecting + target. + +Return Value: + + TRUE - Returned if the reselection is valid. + + FALSE - Returned if the reselection is invalid and ATN should be set. + +--*/ + +{ + LONG pathId = 0; + PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension; + PSCSI_REQUEST_BLOCK srb; + DESTINATION_ID destinationId; + SCSI_SYNCHRONOUS scsiSynchronous; + + + DeviceExtension->InterruptCount = 0; + + // + // Get the specific logical unit extension. + // + + luExtension = ScsiPortGetLogicalUnit( + DeviceExtension, + (UCHAR)pathId, + TargetId, + LogicalUnitNumber + ); + + DeviceExtension->ActiveLogicalUnit = luExtension; + + if (!luExtension || !luExtension->ActiveLuRequest) { + + ScsiPortLogError( + DeviceExtension, // HwDeviceExtension, + NULL, // Srb + (UCHAR)pathId, // PathId, + TargetId, // TargetId, + LogicalUnitNumber, // Lun, + SP_INVALID_RESELECTION, // ErrorCode, + 18 // UniqueId + ); + + + WdPrint((0, "WdProcessReselection: Reselection Failed.\n")); + WdDumpState(DeviceExtension); + + // + // Send an abort message. Put the message-in the buffer, set the + // state, indicate that a disconnect is expected after this, and + // set the attention signal. + // + + DeviceExtension->MessageBuffer[0] = SCSIMESS_ABORT; + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->AdapterState = MessageOut; + DeviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK; + DeviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID | + PD_DISCONNECT_EXPECTED; + + + // + // The target and logical unit specified are not valid. A + // MESSAGE REJECT message has been set up. Set ATN and accept the + // message. + // + + WdAcceptMessage(DeviceExtension, TRUE, FALSE); + + return(FALSE); + + } + + srb = luExtension->ActiveLuRequest; + + // + // A reselection has been completed. Set the active logical unit, + // restore the active data pointer, set the state. + // In addition, any adapter flags set by a pending select must be + // cleared using the disconnect mask. + // + + DeviceExtension->ActiveDataPointer = luExtension->SavedDataPointer; + DeviceExtension->ActiveDataLength = luExtension->SavedDataLength; + DeviceExtension->AdapterState = MessageAccepted; + DeviceExtension->MessageCount = 0; + + // + // Determine if the DMA needs to be done then set the synchronous transfer + // register. + // + + if (luExtension->SavedDataLength) { + + // + // Set the synchronous data transfer parameter registers in case a + // data transfer will be done. These must be set before a data transfer + // is started. + // + + *((PUCHAR) &scsiSynchronous) = 0; + scsiSynchronous.SynchronousOffset = luExtension->SynchronousOffset; + scsiSynchronous.SynchronousPeriod = luExtension->SynchronousPeriod; + + SCSI_WRITE( + DeviceExtension->Adapter, + Synchronous, + *((PUCHAR) &scsiSynchronous) + ); + + } + + // + // Set the destination id register; this register will allow the target to + // reselect later without an interrupt, and tell the SCSI protocol chip the + // direction of the data transfer. + // + + *((PUCHAR) &destinationId) = 0; + destinationId.TargetId = TargetId; + destinationId.DataDirection = srb->SrbFlags & SRB_FLAGS_DATA_OUT ? 0 : 1; + + SCSI_WRITE( DeviceExtension->Adapter, DestinationId, *((PUCHAR) &destinationId)); + + // + // Its not clear what state the target is in so just handle + // the rest of this request one phase at a time. + // Clear the saved comand phase. + // + + luExtension->SavedCommandPhase = 0; + + // + // Accept the IDENTIFY message and wait for the next interrupt. + // Note that WdProcessReselect set the synchronous transfer + // registers if the DMA was set up. + // + + WdAcceptMessage(DeviceExtension, FALSE, FALSE); + + + return(TRUE); + +} + +BOOLEAN +WdResetScsiBus( + IN PVOID ServiceContext, + IN ULONG PathId + ) +/*++ + +Routine Description: + + This function resets the SCSI bus and calls the reset cleanup function. + +Arguments: + + ServiceContext - Supplies a pointer to the specific device extension. + + PathId - Supplies the path id of the bus to be reset. + +Return Value: + + TRUE - Indicating that the reset has completed. + +--*/ + +{ + PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext; + + WdPrint((0, "WdResetScsiBus: Resetting the SCSI bus.\n")); + + // + // Tell the chip to disconnect from the bus. + // + + WdIssueCommand(deviceExtension, DISCONNECT_FROM_BUS, -1, 0); + + // + // Reset the SCSI bus. + // + + SCSI_RESET_BUS(deviceExtension); + + // + // Reset the adapter. + // + + WdInitializeAdapter(deviceExtension); + + WdCleanupAfterReset(deviceExtension, FALSE); + + return TRUE; +} + +VOID +WdResetScsiBusInternal( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN ULONG PathId + ) +/*++ + +Routine Description: + + This function resets the SCSI bus, notifies the port driver of the reset + and calls the reset cleanup function. + +Arguments: + + DeviceExtension - Supplies a pointer to the specific device extension. + + PathId - Supplies the path id of the bus to be reset. + +Return Value: + + None + +--*/ +{ + ScsiPortNotification( + ResetDetected, + DeviceExtension, + NULL + ); + + WdResetScsiBus(DeviceExtension, 0); +} + + +VOID +WdSelectTarget( + IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + IN PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension + ) +/*++ + +Routine Description: + + This routine sets up the hardware to select a target. If a valid message + is in the message buffer, it will be sent to the target. If the request + includes a SCSI command descriptor block, it will also be passed to the + target. + +Arguments: + + DeviceExtension - Supplies the device extension for this HBA adapter. + + LuExtension - Supplies the logical unit extension for the target being + selected. + +Return Value: + + None + +--*/ + +{ + AUXILIARY_STATUS auxiliaryStatus; + PSCSI_REQUEST_BLOCK srb; + TARGET_LUN targetLun; + DESTINATION_ID destinationId; + SCSI_SYNCHRONOUS scsiSynchronous; + ULONG i; + + srb = DeviceExtension->NextSrbRequest; + DeviceExtension->ActiveLogicalUnit = LuExtension; + +#if DBG + if (WdDebug) { + WdPrint((0, "WdSelectTarget: Attempting target select.\n")); + } +#endif + /* Powerfail Start */ + + // + // Set up the SCSI protocol chip to select the target, transfer the + // IDENTIFY message and the CDB. This can be done by following steps: + // + // setting the destination register, + // filling the registers with the IDENTIFY message and the CDB + // setting the command register + // + // Read the auxiliary status. + // + + *((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX(DeviceExtension->Adapter, + AuxiliaryStatus + ); + + // + // If the SCSI protocol chip is interrupting or busy, just return. + // + + if (auxiliaryStatus.Interrupt || auxiliaryStatus.ChipBusy) { + + return; + + } + + // + // Ensure that the data transfer count is zero if no transfer is expected. + // + + if (!(srb->SrbFlags & (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT))) { + + // + // Clear the srb DataTransferLength and the SavedDataLength so chip + // won't transfer data if requested. + // + + srb->DataTransferLength = 0; + LuExtension->SavedDataLength = 0; + } + + // + // Set the destination ID, data direction and the target logical unit + // number. Note that setting the data direction flag indicates the + // transfer is in. The data direction flags will only be set if the + // transfer is really in. + // + + *((PUCHAR) &destinationId) = 0; + destinationId.TargetId = srb->TargetId; + destinationId.DataDirection = srb->SrbFlags & SRB_FLAGS_DATA_OUT ? 0 : 1; + SCSI_WRITE( + DeviceExtension->Adapter, + DestinationId, + *((PUCHAR) &destinationId) + ); + + // + // Set the synchronous data transfer parameter registers in case a + // data transfer is done. These must be set before a data transfer + // is started. + // + + *((PUCHAR) &scsiSynchronous) = 0; + scsiSynchronous.SynchronousOffset = LuExtension->SynchronousOffset; + scsiSynchronous.SynchronousPeriod = LuExtension->SynchronousPeriod; + + SCSI_WRITE( + DeviceExtension->Adapter, + Synchronous, + *((PUCHAR) &scsiSynchronous) + ); + + // + // Determine if this srb has a Cdb with it and whether the message is + // such that the message and the Cdb can be loaded into the registers; + // otherwise, just select the target with ATN. + // + + if ((srb->Function == SRB_FUNCTION_EXECUTE_SCSI) && + (DeviceExtension->MessageCount == 1)) { + + // + // Update the message-sent count to indicate that the IDENTIFY + // message has been sent. + // + + DeviceExtension->MessageSent++; + + // + // Initialize the target logical unit register. + // + + *((PUCHAR) &targetLun) = 0; + targetLun.LogicalUnitNumber = srb->Lun; + + SCSI_WRITE( DeviceExtension->Adapter, + TargetLun, + *((PUCHAR) &targetLun) + ); + + // + // Copy the CDB into the registers. + // + + for (i = 0; i < srb->CdbLength; i++) { + SCSI_WRITE( DeviceExtension->Adapter, + Cdb[i], + srb->Cdb[i] + ); + } + + SCSI_WRITE( DeviceExtension->Adapter, + CdbSize, + srb->CdbLength + ); + + // + // Initialize the CommandPhase in the logical unit. + // + + LuExtension->SavedCommandPhase = 0; + + // + // Since the chip may automatically start a data transfer after the + // selection, restore the data pointers. + // + + DeviceExtension->ActiveDataPointer = LuExtension->SavedDataPointer; + DeviceExtension->ActiveDataLength = LuExtension->SavedDataLength; + + // + // Set the adapter state for subsequent processing. + // + + DeviceExtension->AdapterState = SelectAndTransfer; + + // + // Issue select-with-ATN-and-transfer command: the transfer + // count is set to zero in order to cause an interrupt to occur + // if/when the target requests a data transfer phase. + // + + WdIssueCommand( + DeviceExtension, // Device Extension. + SELECT_ATN_AND_TRANSFER, // Command to issue. + 0, // New transfer count. + 0 // New CommandPhase. + ); + + } else { + + // + // Set the adapter state for subsequent processing. + // + + DeviceExtension->AdapterState = Select; + + // + // Select the target with ATN + // + + WdIssueCommand(DeviceExtension, SELECT_WITH_ATN, -1, 0); + + } + + /* Powerfail release */ + + // + // Indicate that a message out can be or is being sent. + // Start the phase timer. + // + + DeviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID; + DeviceExtension->InterruptCount = 0; + +} + + +VOID +WdSendMessage( + PSCSI_REQUEST_BLOCK Srb, + PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension + ) + +/*++ + +Routine Description: + + This routine attempts to send the indicated message to the target + controller. There are three classes of messages: + Those which terminate a specific request and end in bus free. + Those which apply to a specific request and then proceed. + Those which end in bus free. + + For those messages that apply to a specific request, check to see that + the request is currently being processed and an IDENTIFY message prefixed + the message. + + It is possible that the destination logical unit is the active logical unit; + however, it would difficult to jump in and send the requested message, so + just wait for the bus to become free. + + In the case where the target is not currently active, then set up the SCSI + protocol chip to select the target controller and send the message. + +Arguments: + + Srb - Supplies the request to be started. + + DeviceExtension - Supplies the extended device extension for this SCSI bus. + + LuExtension - Supplies the logical unit extension for this request. + +Notes: + + This routine must be synchronized with the interrupt routine. + +Return Value: + + None + +--*/ +{ + PSCSI_REQUEST_BLOCK linkedSrb; + BOOLEAN impliesDisconnect; + UCHAR message; + + impliesDisconnect = FALSE; + + // + // Decode the type of message. + // + + switch (Srb->Function) { + + // case SCSIMESS_ABORT_WITH_TAG: + // case SCSIMESS_TERMINATE_IO_PROCESS: + + /* TODO: Handle the previous two cases. */ + case SRB_FUNCTION_ABORT_COMMAND: + + // + // Verify that the request is being processed by the logical unit. + // + + linkedSrb = ScsiPortGetNextLink( Srb ); + if (linkedSrb != LuExtension->ActiveLuRequest) { + + // + // The specified request is not here. Complete the request + // without error. + // + + Srb->SrbStatus = SRB_STATUS_ABORT_FAILED; + ScsiPortNotification( + RequestComplete, + DeviceExtension, + Srb + ); + + ScsiPortNotification( + NextRequest, + DeviceExtension, + NULL + ); + + return; + + } + + message = SCSIMESS_ABORT; + impliesDisconnect = TRUE; + break; + + case SRB_FUNCTION_RESET_DEVICE: + + // + // Because of the way the chip works it is easiest to send an IDENTIFY + // message along with the BUS DEVICE RESET message. That is because + // there is no way to select a target with ATN and send one message + // byte. This IDENTIFY message is not necessary for the SCSI protocol, + // but it is legal and should not cause any problem. + // + + message = SCSIMESS_BUS_DEVICE_RESET; + impliesDisconnect = TRUE; + break; + +/* case SCSIMESS_RELEASE_RECOVERY: + case SCSIMESS_CLEAR_QUEUE: + + // + // These messages require an IDENTIFY message and imply a disconnect. + // + + impliesDisconnect = TRUE; + break; + */ + default: + + // + // This is an unsupported message request. Fail the request. + // + + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + ScsiPortNotification( + RequestComplete, + DeviceExtension, + Srb + ); + + ScsiPortNotification( + NextRequest, + DeviceExtension, + NULL + ); + + return; + + } + + // + // Save away the parameters in case nothing can be done now. + // + + DeviceExtension->NextSrbRequest = Srb; + DeviceExtension->AdapterFlags |= PD_PENDING_START_IO; + LuExtension->ActiveSendRequest = Srb; + + // + // Check to see if the bus is free. If it is not, then return. Since + // the request parameters have been saved, indicate that the request has + // been accepted. The request will be processed when the bus becomes free. + // + + if (DeviceExtension->AdapterState != BusFree) { + return; + } + + // + // Create the identify command and copy the message to the buffer. + // + + DeviceExtension->MessageBuffer[0] = SCSIMESS_IDENTIFY_WITH_DISCON | + Srb->Lun; + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + + DeviceExtension->MessageBuffer[DeviceExtension->MessageCount++] = message; + + // + // Attempt to select the target and update the adapter flags. + // + + WdSelectTarget( DeviceExtension, LuExtension ); + + DeviceExtension->AdapterFlags |= impliesDisconnect ? + PD_DISCONNECT_EXPECTED | PD_SEND_MESSAGE_REQUEST + : PD_SEND_MESSAGE_REQUEST; + +} + +VOID +WdSetupDma( + PVOID ServiceContext + ) + +/*++ + +Routine Description: + + This function performs all of the operations necessary set up the SCSI + protocol card and chip for a data transfer. This function should be called + after the last access to chip. In particular, the transfer count is set and + the transfer is mapped. If the active transfer count is zero, then the + DMA is not started. + +Arguments: + + ServiceContext - Supplies the extended device extension for this SCSI bus. + +Return Value: + + None. + +--*/ + +{ + PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension; + PSCSI_REQUEST_BLOCK srb; + PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext; + + // + // If the data tranfer is no longer expected then ignore the notification. + // + + if (!(deviceExtension->AdapterFlags & PD_PENDING_DATA_TRANSFER)) { + + return; + + } + + luExtension = deviceExtension->ActiveLogicalUnit; + srb = luExtension->ActiveLuRequest; + + // + // Setup the SCSI host adapter card. + // + + CARD_DMA_INITIATE( deviceExtension, srb->SrbFlags & SRB_FLAGS_DATA_IN ); + + // + // Tell the chip to transfer the data. + // + + WdIssueCommand( + deviceExtension, // Device Extension. + deviceExtension->DmaCommand, // Command to issue. + deviceExtension->ActiveDataLength, // New transfer count. + deviceExtension->DmaPhase // New CommandPhase. + ); + + deviceExtension->AdapterFlags &= ~PD_PENDING_DATA_TRANSFER; + +} + + +VOID +WdStartExecution( + PSCSI_REQUEST_BLOCK Srb, + PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension + ) + +/*++ + +Routine Description: + + This procedure sets up the chip to select the target and notify it that + a request is available. For the WD chip, the chip is set up to select, + send the IDENTIFY message and send the command data block. A check is + made to determine if synchronous negotiation is necessary. + +Arguments: + + Srb - Supplies the request to be started. + + DeviceExtension - Supplies the extended device extension for this SCSI bus. + + LuExtension - Supplies the logical unit extension for this request. + +Notes: + + This routine must be synchronized with the interrupt routine. + +Return Value: + + None + +--*/ + +{ + + PSCSI_EXTENDED_MESSAGE extendedMessage; + + // + // Save away the parameters in case nothing can be done now. + // + + LuExtension->ActiveLuRequest = Srb; + LuExtension->SavedDataPointer = (ULONG) Srb->DataBuffer; + LuExtension->SavedDataLength = Srb->DataTransferLength; + LuExtension->MaximumTransferLength = 0; + DeviceExtension->NextSrbRequest = Srb; + DeviceExtension->AdapterFlags |= PD_PENDING_START_IO; + + // + // Check to see if the bus is free. If it is not, then return. Since + // the request parameters have been saved, indicate that the request has + // been accepted. The request will be processed when the bus becomes free. + // + + if (DeviceExtension->AdapterState != BusFree) { + return; + } + + // + // Create the identify command. + // + + DeviceExtension->MessageBuffer[0] = SCSIMESS_IDENTIFY_WITH_DISCON | Srb->Lun; + DeviceExtension->MessageCount = 1; + DeviceExtension->MessageSent = 0; + DeviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID; + + // + // Set the active data length and pointer in the specific device + // extension. These are used to program the DMA. + // + + DeviceExtension->ActiveDataPointer = LuExtension->SavedDataPointer; + DeviceExtension->ActiveDataLength = LuExtension->SavedDataLength; + + // + // Check to see if synchronous negotiation is necessary. + // + + if (!(LuExtension->LuFlags & + (PD_SYNCHRONOUS_NEGOTIATION_DONE | PD_DO_NOT_NEGOTIATE)) && + !(Srb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) { + + // + // Initialize the synchronous transfer register values to an + // asynchronous transfer, which is what will be used if anything + // goes wrong with the negotiation. + // + + LuExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET; + LuExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD; + + // + // Create the synchronous data transfer request message. + // The format of the message is: + // + // EXTENDED_MESSAGE op-code + // Length of message + // Synchronous transfer data request op-code + // Our Transfer period + // Our REQ/ACK offset + // + // The message is placed after the IDENTIFY message. + // + + extendedMessage = (PSCSI_EXTENDED_MESSAGE) + &DeviceExtension->MessageBuffer[DeviceExtension->MessageCount]; + DeviceExtension->MessageCount += 2 + SCSIMESS_SYNCH_DATA_LENGTH; + + extendedMessage->InitialMessageCode = SCSIMESS_EXTENDED_MESSAGE; + extendedMessage->MessageLength = SCSIMESS_SYNCH_DATA_LENGTH; + extendedMessage->MessageType = SCSIMESS_SYNCHRONOUS_DATA_REQ; + extendedMessage->ExtendedArguments.Synchronous.TransferPeriod = SYNCHRONOUS_PERIOD; + extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset = SYNCHRONOUS_OFFSET; + + // + // Attempt to select the target and update the adapter flags. + // + + WdSelectTarget( DeviceExtension, LuExtension ); + + // + // Many controllers reject the first byte of a synchronous + // negotiation message. Since this is a multibyte message the + // ATN signal remains set after the first byte is sent. Some + // controllers remember this attempt to do a message-out + // later. Setting the PD_POSSIBLE_EXTRA_MESSAGE_OUT flag allows + // this extra message transfer to occur without error. + // + + DeviceExtension->AdapterFlags |= PD_POSSIBLE_EXTRA_MESSAGE_OUT | + PD_SYNCHRONOUS_TRANSFER_SENT; + + return; + } + + WdSelectTarget( DeviceExtension, LuExtension ); + +} + +BOOLEAN +WdStartIo( + IN PVOID ServiceContext, + IN PSCSI_REQUEST_BLOCK Srb + ) +/*++ + +Routine Description: + + This function is used by the OS dependent port driver to pass requests to + the HBA-dependent driver. This function begins the execution of the request. + Requests to reset the SCSI bus are handled immediately. Requests to send + a message or start a SCSI command are handled when the bus is free. + +Arguments: + + ServiceContext - Supplies the device extension for the SCSI bus adapter. + + Srb - Supplies the SCSI request block to be started. + +Return Value: + + TRUE - If the request can be accepted at this time. + + FALSE - If the request must be submitted later. + +--*/ + +{ + PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext; + PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension; + + switch (Srb->Function) { + case SRB_FUNCTION_EXECUTE_SCSI: + + WdPrint((0, "WdStartIo: execute scsi\n" )); + + // + // Determine the logical unit that this request is for. + // + + luExtension = ScsiPortGetLogicalUnit( + deviceExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun + ); + WdStartExecution( + Srb, + deviceExtension, + luExtension + ); + + return(TRUE); + + case SRB_FUNCTION_ABORT_COMMAND: + case SRB_FUNCTION_RESET_DEVICE: + case SRB_FUNCTION_TERMINATE_IO: + + WdPrint((0, "WdStartIo: abort/reset/terminate\n" )); + + // + // + // Determine the logical unit that this request is for. + // + + luExtension = ScsiPortGetLogicalUnit( + deviceExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun + ); + WdSendMessage( + Srb, + deviceExtension, + luExtension + ); + + return(TRUE); + + case SRB_FUNCTION_RESET_BUS: + + WdPrint((0, "WdStartIo: reset bus\n" )); + + // + // There is no logical unit so just reset the bus. + // + + WdResetScsiBus( deviceExtension, 0 ); + return(TRUE); + + default: + + WdPrint((0, "WdStartIo: default case -- bad function\n" )); + + // + // Unknown function code in the request. + // Complete the request with an error and ask for the next request. + // + + Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; + ScsiPortNotification( + RequestComplete, + deviceExtension, + Srb + ); + + ScsiPortNotification( + NextRequest, + deviceExtension, + NULL + ); + + return(TRUE); + } +} + + + +BOOLEAN +WdTransferInformation( + PSPECIFIC_DEVICE_EXTENSION DeviceExtension, + PUCHAR BufferPointer, + ULONG Count, + BOOLEAN TransferToChip + ) +/*++ + +Routine Description: + + This fucntion transfers data between memory and the SCSI protocol chip's + data registers. It is only used for non-data transfers. This operation + is done synchronously; however, since the chip has a 12-byte FIFO and this + function is used for small transfers, it will execute quickly. + +Arguments: + + DeviceExtension - Supplies the device Extension for the SCSI bus adapter. + + BufferPointer - Supplies a pointer to the data to be copied. + + Count - Supplies the number of bytes to be transferred. + + TransferToChip - Indicates the direction of the transfer. + +Return Value: + + Returns the number of bytes actually transferred. + +--*/ + +{ + ULONG i; + ULONG j; + AUXILIARY_STATUS scsiStatus; + + // + // Issue the transfer information command to the SCSI protocol chip. + // + + WdIssueCommand(DeviceExtension, TRANSFER_INFORMATION, Count, 0); + + for (i = 0; i < Count; i++ ) { + + // + // Determine if the data buffer is ready. + // + + *((PUCHAR) &scsiStatus) = SCSI_READ_AUX( + DeviceExtension->Adapter, + AuxiliaryStatus + ); + + j = 0; + + while (!scsiStatus.DataBufferReady && j < DATA_BUS_READY_TIME) { + + if (scsiStatus.Interrupt) { + + // + // The SCSI protocol chip is interrupting so no more data + // will be transferred. + // + + return(FALSE); + } + + // + // Wait for the data buffer to become free. + // + + ScsiPortStallExecution( 1 ); + j++; + + *((PUCHAR) &scsiStatus) = SCSI_READ_AUX( + DeviceExtension->Adapter, + AuxiliaryStatus + ); + + } + + if (!scsiStatus.DataBufferReady) { + + // + // The data buffer did not become free in time. Something is hung. + // Reset the controller and the bus, finally return FALSE. + // + + WdLogError(DeviceExtension, SP_BUS_TIME_OUT, 21); + + WdPrint((0, + "WdTransferInformation: The data buffer ready flag timed out. Transfer count = %lu.\n", + Count + )); + + WdDumpState(DeviceExtension); + WdResetScsiBusInternal(DeviceExtension, 0); + + return(FALSE); + + + } + +#if DBG + if (WdDebug && j > 1) { + WdPrint((0, "WdTransferInformation Data wait time: %d, Status: 0x%.2X\n", + j, DeviceExtension->AdapterInterrupt)); + } +#endif + + // + // Move the data to the data register. + // + + if (TransferToChip) { + + SCSI_WRITE(DeviceExtension->Adapter, Data, *BufferPointer); + BufferPointer++; + + } else { + + *BufferPointer = SCSI_READ(DeviceExtension->Adapter, Data); + BufferPointer++; + + } + + } + + return(TRUE); +} + +ULONG +DriverEntry ( + IN PVOID DriverObject, + IN PVOID Argument2 + ) + +/*++ + +Routine Description: + +Arguments: + + Driver Object is passed to ScsiPortInitialize() + +Return Value: + + Status from ScsiPortInitialize() + +--*/ + +{ + HW_INITIALIZATION_DATA hwInitializationData; + PUCHAR BufferPointer; + ULONG i; + + WdPrint((1,"\nSCSI WD33C93A Miniport Driver\n")); + + // + // Initialize the buffer to zero. + // + + BufferPointer = (PUCHAR) &hwInitializationData; + for (i = 0; i < sizeof(hwInitializationData); i++) { + *BufferPointer++ = 0; + } + + hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA); + + hwInitializationData.HwInitialize = WdInitializeAdapter; + hwInitializationData.HwStartIo = WdStartIo; + hwInitializationData.HwInterrupt = WdInterruptServiceRoutine; + hwInitializationData.HwFindAdapter = WdFindAdapter; + hwInitializationData.HwResetBus = WdResetScsiBus; + hwInitializationData.HwDmaStarted = WdSetupDma; + hwInitializationData.AdapterInterfaceType = SCSI_BUS_INTERFACE; + hwInitializationData.NumberOfAccessRanges = 1; + hwInitializationData.DeviceExtensionSize = sizeof(SPECIFIC_DEVICE_EXTENSION); + hwInitializationData.MapBuffers = TRUE; + hwInitializationData.SpecificLuExtensionSize = + sizeof(SPECIFIC_LOGICAL_UNIT_EXTENSION); + + return ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, NULL); + +} // end DriverEntry() + +ULONG +WdFindAdapter( + IN PVOID ServiceContext, + IN PVOID Context, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ) +/*++ + +Routine Description: + + This function fills in the configuration information structure and maps + the SCSI protocol chip for access. This routine is temporary until + the configuration manager supplies similar information. + +Arguments: + + ServiceContext - Supplies a pointer to the device extension. + + Context - Unused. + + BusInformation - Unused. + + ArgumentString - Unused. + + ConfigInfo - Pointer to the configuration information structure to be + filled in. + + Again - Returns back a request to call this function again. + +Return Value: + + Returns a status value for the initialization. + +--*/ + +{ + PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext; + AUXILIARY_STATUS scsiStatus; + ULONG physicalBase; + SCSI_PHYSICAL_ADDRESS base; + UCHAR dataByte; + BOOLEAN found = FALSE; + BOOLEAN baseSupplied; + + *Again = FALSE; + + // + // Parse any supplied argument string. + // + + if (ArgumentString != NULL) { + + // + // Look for a base parameter. + // + + physicalBase = WdParseArgumentString(ArgumentString, "BASE"); + + if (physicalBase != 0) { + + base = ScsiPortConvertUlongToPhysicalAddress(physicalBase); + (*ConfigInfo->AccessRanges)[0].RangeStart = base; + } + + // + // Look for interrupt level. + // + + physicalBase = WdParseArgumentString(ArgumentString, "IRQ"); + + if (physicalBase != 0) { + + ConfigInfo->BusInterruptVector = SCSI_VECTOR; + ConfigInfo->BusInterruptLevel = physicalBase; + + } + + // + // Look for interrupt level. + // + + physicalBase = WdParseArgumentString(ArgumentString, "DMA"); + + if (physicalBase != 0) { + + ConfigInfo->DmaChannel = physicalBase; + + } + + + } + + physicalBase = ScsiPortConvertPhysicalAddressToUlong( + (*ConfigInfo->AccessRanges)[0].RangeStart); + if (physicalBase != 0) { + + baseSupplied = TRUE; + + } else { + + baseSupplied = FALSE; + physicalBase = SCSI_PHYSICAL_BASE; + } + + // + // Get the system physical address for this card. The card uses I/O space. + // + + base = ScsiPortConvertUlongToPhysicalAddress(physicalBase); + deviceExtension->Adapter = ScsiPortGetDeviceBase( + deviceExtension, // HwDeviceExtension + ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType + ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber + base, + sizeof(CARD_REGISTERS), // NumberOfBytes + TRUE // InIoSpace + ); + + if (deviceExtension->Adapter == NULL) { + WdPrint((0, "\nScsiPortInitialize: Failed to map SCSI device registers into system space.\n")); + return(SP_RETURN_ERROR); + } + + // + // Determine if the card is really installed in the system. First read the + // status register. + // + + *((PUCHAR) &scsiStatus) = SCSI_READ_AUX( + deviceExtension->Adapter, + AuxiliaryStatus + ); + + // + // Make sure the reserved bits are zero. + // + + if (scsiStatus.Reserved != 0) { + + // + // The card is not here so clean up. + // + + ScsiPortFreeDeviceBase( + deviceExtension, + deviceExtension->Adapter + ); + + } else { + + // + // Write and read the command phase register. This is not a good thing to + // do for card detection, but there does not appear to be any other way. + // + + dataByte = PHASE_STATUS_STARTED; + SCSI_WRITE(deviceExtension->Adapter, CommandPhase, dataByte); + dataByte = SCSI_READ(deviceExtension->Adapter, CommandPhase); + if (dataByte != PHASE_STATUS_STARTED) { + + // + // This is not our card so clean up. + // + + ScsiPortFreeDeviceBase( + deviceExtension, + deviceExtension->Adapter + ); + + } else { + + found = TRUE; + + } + } + + + if (!found && !baseSupplied) { + +#ifndef SCSI_SECOND_PHYSICAL_BASE + + return(SP_RETURN_NOT_FOUND); + +#else + // + // Try an alternet address. + // Get the system physical address for this card. The card uses I/O space. + // + + base = ScsiPortConvertUlongToPhysicalAddress(SCSI_SECOND_PHYSICAL_BASE); + deviceExtension->Adapter = ScsiPortGetDeviceBase( + deviceExtension, // HwDeviceExtension + ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType + ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber + base, + sizeof(CARD_REGISTERS), // NumberOfBytes + TRUE // InIoSpace + ); + + if (deviceExtension->Adapter == NULL) { + WdPrint((0, "\nScsiPortInitialize: Failed to map SCSI device registers into system space.\n")); + return(SP_RETURN_ERROR); + } + + // + // Determine if the card is really installed in the system. First read the + // status register. + // + + *((PUCHAR) &scsiStatus) = SCSI_READ_AUX( + deviceExtension->Adapter, + AuxiliaryStatus + ); + + // + // Make sure the reserved bits are zero. + // + + if (scsiStatus.Reserved != 0) { + + // + // The card is not here so clean up. + // + + ScsiPortFreeDeviceBase( + deviceExtension, + deviceExtension->Adapter + ); + + return(SP_RETURN_NOT_FOUND); + + } + + // + // Write and read the command phase register. This is not a good thing to + // do for card detection, but there does not appear to be any other way. + // + + dataByte = PHASE_STATUS_STARTED; + SCSI_WRITE(deviceExtension->Adapter, CommandPhase, dataByte); + dataByte = SCSI_READ(deviceExtension->Adapter, CommandPhase); + if (dataByte != PHASE_STATUS_STARTED) { + + // + // This is not our card so clean up. + // + + ScsiPortFreeDeviceBase( + deviceExtension, + deviceExtension->Adapter + ); + + return(SP_RETURN_NOT_FOUND); + + } + + found = TRUE; +#endif + } + + if (!found) { + return(SP_RETURN_NOT_FOUND); + } + + // + // Set the defaults for any uninitialized values. + // + + if (ConfigInfo->DmaChannel == SP_UNINITIALIZED_VALUE) { + + ConfigInfo->DmaChannel = CARD_DMA_REQUEST; + } + + if (ConfigInfo->BusInterruptLevel == 0 && + ConfigInfo->BusInterruptVector == 0) { + + ConfigInfo->BusInterruptLevel = SCSI_LEVEL; + ConfigInfo->BusInterruptVector = SCSI_VECTOR; + + } + + // + // Get the adapter object for this card. + // + + ConfigInfo->MaximumTransferLength = 0x1000000; + ConfigInfo->DmaWidth = CARD_DMA_WIDTH; + ConfigInfo->DmaSpeed = CARD_DMA_SPEED; + ConfigInfo->NumberOfBuses = 1; + + // + // Translate the DMA Channel values. + // + + switch (ConfigInfo->DmaChannel) { + case 5: + deviceExtension->DmaCode = 1; + break; + + case 6: + deviceExtension->DmaCode = 2; + break; + + case 7: + deviceExtension->DmaCode = 3; + break; + + default: + return(SP_RETURN_BAD_CONFIG); + } + + // + // Translate interrupt level values. + // + + switch (ConfigInfo->BusInterruptLevel) { + case 3: + deviceExtension->IrqCode = 1; + break; + + case 4: + deviceExtension->IrqCode = 2; + break; + + case 5: + deviceExtension->IrqCode = 3; + break; + + case 10: + deviceExtension->IrqCode = 4; + break; + + case 11: + deviceExtension->IrqCode = 5; + break; + + case 12: + deviceExtension->IrqCode = 6; + break; + + case 15: + deviceExtension->IrqCode = 7; + break; + + default: + return(SP_RETURN_BAD_CONFIG); + + } + + // + // Get the SCSI bus Id from the configuration information if there + // is any. + // + + if (ConfigInfo->InitiatorBusId[0] == (CCHAR) SP_UNINITIALIZED_VALUE) { + ConfigInfo->InitiatorBusId[0] = INITIATOR_BUS_ID; + deviceExtension->InitiatorBusId = INITIATOR_BUS_ID; + } else { + deviceExtension->InitiatorBusId = ConfigInfo->InitiatorBusId[0]; + } + + // + // Fill in the access array information. + // + + (*ConfigInfo->AccessRanges)[0].RangeStart = base; + (*ConfigInfo->AccessRanges)[0].RangeLength = sizeof(CARD_REGISTERS); + (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE; + + return(SP_RETURN_FOUND); +} + +ULONG +WdParseArgumentString( + IN PCHAR String, + IN PCHAR KeyWord + ) + +/*++ + +Routine Description: + + This routine will parse the string for a match on the keyword, then + calculate the value for the keyword and return it to the caller. + +Arguments: + + String - The ASCII string to parse. + KeyWord - The keyword for the value desired. + +Return Values: + + Zero if value not found + Value converted from ASCII to binary. + +--*/ + +{ + PCHAR cptr; + PCHAR kptr; + ULONG value; + ULONG stringLength = 0; + ULONG keyWordLength = 0; + ULONG index; + + // + // Calculate the string length and lower case all characters. + // + cptr = String; + while (*cptr) { + + if (*cptr >= 'A' && *cptr <= 'Z') { + *cptr = *cptr + ('a' - 'A'); + } + cptr++; + stringLength++; + } + + // + // Calculate the keyword length and lower case all characters. + // + cptr = KeyWord; + while (*cptr) { + + if (*cptr >= 'A' && *cptr <= 'Z') { + *cptr = *cptr + ('a' - 'A'); + } + cptr++; + keyWordLength++; + } + + if (keyWordLength > stringLength) { + + // + // Can't possibly have a match. + // + return 0; + } + + // + // Now setup and start the compare. + // + cptr = String; + +ContinueSearch: + // + // The input string may start with white space. Skip it. + // + while (*cptr == ' ' || *cptr == '\t') { + cptr++; + } + + if (*cptr == '\0') { + + // + // end of string. + // + return 0; + } + + kptr = KeyWord; + while (*cptr++ == *kptr++) { + + if (*(cptr - 1) == '\0') { + + // + // end of string + // + return 0; + } + } + + if (*(kptr - 1) == '\0') { + + // + // May have a match backup and check for blank or equals. + // + + cptr--; + while (*cptr == ' ' || *cptr == '\t') { + cptr++; + } + + // + // Found a match. Make sure there is an equals. + // + if (*cptr != '=') { + + // + // Not a match so move to the next semicolon. + // + while (*cptr) { + if (*cptr++ == ';') { + goto ContinueSearch; + } + } + return 0; + } + + // + // Skip the equals sign. + // + cptr++; + + // + // Skip white space. + // + while ((*cptr == ' ') || (*cptr == '\t')) { + cptr++; + } + + if (*cptr == '\0') { + + // + // Early end of string, return not found + // + return 0; + } + + if (*cptr == ';') { + + // + // This isn't it either. + // + cptr++; + goto ContinueSearch; + } + + value = 0; + if ((*cptr == '0') && (*(cptr + 1) == 'x')) { + + // + // Value is in Hex. Skip the "0x" + // + cptr += 2; + for (index = 0; *(cptr + index); index++) { + + if (*(cptr + index) == ' ' || + *(cptr + index) == '\t' || + *(cptr + index) == ';') { + break; + } + + if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) { + value = (16 * value) + (*(cptr + index) - '0'); + } else { + if ((*(cptr + index) >= 'a') && (*(cptr + index) <= 'f')) { + value = (16 * value) + (*(cptr + index) - 'a' + 10); + } else { + + // + // Syntax error, return not found. + // + return 0; + } + } + } + } else { + + // + // Value is in Decimal. + // + for (index = 0; *(cptr + index); index++) { + + if (*(cptr + index) == ' ' || + *(cptr + index) == '\t' || + *(cptr + index) == ';') { + break; + } + + if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) { + value = (10 * value) + (*(cptr + index) - '0'); + } else { + + // + // Syntax error return not found. + // + return 0; + } + } + } + + return value; + } else { + + // + // Not a match check for ';' to continue search. + // + while (*cptr) { + if (*cptr++ == ';') { + goto ContinueSearch; + } + } + + return 0; + } +} + diff --git a/private/ntos/miniport/wd33c93/wd33c93.h b/private/ntos/miniport/wd33c93/wd33c93.h new file mode 100644 index 000000000..0fb684bab --- /dev/null +++ b/private/ntos/miniport/wd33c93/wd33c93.h @@ -0,0 +1,305 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + wd33c93.h + +Abstract: + + The module defines the structures, and defines for the Western Digital + WD33C93 host bus adapter chip. + +Author: + + Jeff Havens (jhavens) 10-June-1991 + +Revision History: + + +--*/ + +#ifndef _WD33C93_ +#define _WD33C93_ + +// +// SCSI host bus adapter Western Digital WD33C93 chip definitions. +// + +// +// Define the register type +// +typedef struct _SCSI_REGISTER{ + UCHAR Value; +}SCSI_REGISTER, *PSCSI_REGISTER; + +// +// Define the chip registers. +// +typedef struct _SCSI_REGISTERS{ + SCSI_REGISTER CdbSize; + SCSI_REGISTER Control; + SCSI_REGISTER Timeout; + SCSI_REGISTER Cdb[12]; + SCSI_REGISTER TargetLun; + SCSI_REGISTER CommandPhase; + SCSI_REGISTER Synchronous; + SCSI_REGISTER TransferCountMsb; + SCSI_REGISTER TransferCount2; + SCSI_REGISTER TransferCountLsb; + SCSI_REGISTER DestinationId; + SCSI_REGISTER SourceId; + SCSI_REGISTER Status; + SCSI_REGISTER Command; + SCSI_REGISTER Data; + SCSI_REGISTER AuxiliaryStatus; +}SCSI_REGISTERS, *PSCSI_REGISTERS; + +// +// Define SCSI Auxiliary Status register structure. +// + +typedef struct _AUXILIARY_STATUS{ + ULONG DataBufferReady : 1; + ULONG ParityError : 1; + ULONG Reserved : 2; + ULONG CommandInProgress : 1; + ULONG ChipBusy : 1; + ULONG LastCommandIgnored : 1; + ULONG Interrupt : 1; +}AUXILIARY_STATUS, *PAUXILIARY_STATUS; + +// +// Define SCSI Own Id register structure. +// + +typedef struct _OWN_ID{ + ULONG InitiatorId : 3; + ULONG AdvancedFeatures : 1; + ULONG EnableHostParity : 1; + ULONG Reserved : 1; + ULONG FrequencySelect : 2; +}OWN_ID, *POWN_ID; + +// +// Define Frequency Select values. +// + +#define CLOCK_10MHZ 0 +#define CLOCK_15MHZ 1 +#define CLOCK_16MHZ 2 +#define CLOCK_20MHZ 2 + +// +// Define SCSI control register structure. +// +typedef struct _SCSI_CONTROL{ + ULONG HaltOnParity : 1; + ULONG HaltOnAtn : 1; + ULONG IntermediateDisconnectInt : 1; + ULONG EndingDisconnectInt : 1; + ULONG HaltOnHostParity : 1; + ULONG DmaModeSelect : 3; +}SCSI_CONTROL, *PSCSI_CONTROL; + +// +// Define SCSI DMA Mode Select values. +// + +#define DMA_POLLED 0 +#define DMA_BURST 1 +#define DMA_WD_BUS 2 +#define DMA_NORMAL 4 + +// +// Define the SCSI Target LUN register structure. +// + +typedef struct _TARGET_LUN{ + ULONG LogicalUnitNumber : 3; + ULONG Reserved : 3; + ULONG DisconnectOk : 1; + ULONG TargetLunValid : 1; +}TARGET_LUN, *PTARGET_LUN; + +// +// Define the SCSI Synchronous Transfer register structure. +// + +typedef struct _SCSI_SYNCHRONOUS{ + ULONG SynchronousOffset : 4; + ULONG SynchronousPeriod : 3; + ULONG Reserved : 1; +}SCSI_SYNCHRONOUS, *PSCSI_SYNCHRONOUS; + +// +// Define SCSI Destination Id register. +// + +typedef struct _DESTINATION_ID{ + ULONG TargetId : 3; + ULONG Reserved : 3; + ULONG DataDirection : 1; + ULONG SelectCommandChain : 1; +}DESTINATION_ID, *PDESTINATION_ID; + +// +// Define SCSI Source Id register. +// + +typedef struct _SOURCE_ID{ + ULONG TargetId : 3; + ULONG TargetIdValid : 1; + ULONG Reserved : 1; + ULONG DisableParitySelect : 1; + ULONG EnableSelection : 1; + ULONG EnableReselection : 1; +}SOURCE_ID, *PSOURCE_ID; + +// +// Define the SCSI Status register structure. +// + +typedef struct _SCSI_STATUS{ + ULONG PhaseState : 3; + ULONG PhaseStateValid : 1; + ULONG CommandComplete : 1; + ULONG AbortedPaused : 1; + ULONG Terminated : 1; + ULONG ServiceRequired : 1; +}SCSI_STATUS, *PSCSI_STATUS; + +// +// Define SCSI protocol chip SCSI status PhaseState values for command complete +// interrupts. +// + +#define COMPLETE_RESELECT 0x00 +#define COMPLETE_SELECT 0x01 +#define COMPLETE_TARGET_COMMAND 0x03 +#define COMPLETE_TARGET_WITH_ATN 0x04 +#define COMPLETE_TRANSLATE_ADDRESS 0x05 +#define COMPLETE_SELECT_AND_TRANS 0x06 + +// +// Define SCSI protocol chip SCSI status PhaseState values for aborted paused +// interrupts. +// + +#define PAUSED_MESSAGE_IN_DONE 0x00 +#define PAUSED_SAVE_POINTER_MESSAGE 0x01 +#define PAUSED_RESELECT_OR_SELECT 0x02 +#define PAUSED_TARGET_TRANS_ERROR 0x03 +#define PAUSED_TRANS_WITH_ATN 0x04 +#define PAUSED_DURING_RESELECT 0x05 +#define PAUSED_NEW_TARGET_RESELECT 0x07 + +// +// Define SCSI protocol chip SCSI status PhaseState values for service required +// interrupts. +// + +#define SERVICE_RESELECTED 0x00 +#define SERVICE_RESELECTED_IDENTIFY 0x01 +#define SERVICE_SELECTED_NO_ATN 0x02 +#define SERVICE_SELECTED_WITH_ATH 0x03 +#define SERVICE_ATN_ASSERTED 0x04 +#define SERVICE_DISCONNECTED 0x05 +#define SERVICE_UNKNWON_CDB_TYPE 0x07 + +// +// Define SCSI protocol chip SCSI status PhaseState values for terminated +// interrupts. +// + +#define TERMINATE_INVALID_COMMAND 0x00 +#define TERMINATE_UNEXPECTED_DISC 0x01 +#define TERMINATE_SELECT_TIMEOUT 0x02 +#define TERMINATE_PARITY_NO_ATN 0x03 +#define TERMINATE_PARITY_WITH_ATN 0x04 +#define TERMINATE_BAD_ADDRESS 0x05 +#define TERMINATE_NEW_TRAGET_NO_ID 0x06 +#define TERMINATE_PARITY_STATUS_IN 0x07 + + +// +// Define SCSI protocol chip SCSI status PhaseState values for reset +// interrupts. +// + +#define RESET_STATUS 0x00 +#define RESET_WITH_ADVANCED 0x01 + +// +// Define SCSI Phase Codes. +// + +#define DATA_OUT 0x0 +#define DATA_IN 0x1 +#define COMMAND_OUT 0x2 +#define STATUS_IN 0x3 +#define MESSAGE_OUT 0x6 +#define MESSAGE_IN 0x7 + + +// +// Define SCSI command register structure. +// + +typedef struct _SCSI_COMMAND{ + ULONG OpCode : 7; + ULONG SingleByte : 1; +}SCSI_COMMAND, *PSCSI_COMMAND; + +// +// Define SCSI OpCode command values. +// + +#define RESET_SCSI_CHIP 0x00 +#define ABORT_SCSI_CHIP 0x01 +#define ASSERT_ATN 0x02 +#define NEGATE_ACK 0x03 +#define DISCONNECT_FROM_BUS 0x04 +#define RESELECT 0x05 +#define SELECT_WITH_ATN 0x06 +#define SELECT_WITHOUT_ATN 0x07 +#define SELECT_ATN_AND_TRANSFER 0x08 +#define SELECT_AND_TRANSFER 0x09 +#define RESELECT_AND_RECEIVE_DATA 0x0a +#define RESELECT_AND_SEND_DATA 0x0b +#define WAIT_FOR_SELECT_RECEIVE 0x0c +#define SEND_STATUS_AND_COMPETE 0x0d +#define SEND_DISCONNECT_MESSAGE 0x0e +#define SET_DISCONNECT_INTERRUPT 0x0f +#define RECEIVE_COMMAND 0x10 +#define RECEIVE_DATA 0x11 +#define RECEIVE_MESSEAG_OUT 0x12 +#define RECEIVE_UNSPECIFIED_OUT 0x13 +#define SEND_STATUS 0x14 +#define SEND_DATA 0x15 +#define SEND_MESSAGE_IN 0x16 +#define SEND_UNSPECIFIED_DATA_IN 0x17 +#define TRANSLATE_ADDRESS 0x18 +#define TRANSFER_INFORMATION 0x20 + +// +// Define SCSI protocol chip command phase values. +// + +#define PHASE_NO_SELECT 0x00 +#define PHASE_SELECTED 0x10 +#define PHASE_IDENTIFY_SENT 0x20 +#define PHASE_COMMAND_OUT 0x30 +#define PHASE_SAVE_DATA_RECEIVED 0x41 +#define PHASE_DISCONNECT_RECEIVED 0x42 +#define PHASE_LEGAL_DISCONNECT 0x43 +#define PHASE_RESELECTED 0x44 +#define PHASE_IDENTIFY_RECEIVED 0x45 +#define PHASE_DATA_TRANSFER_DONE 0x46 +#define PHASE_STATUS_STARTED 0x47 +#define PHASE_STATUS_RECEIVED 0x50 +#define PHASE_COMPLETE_RECEIVED 0x60 + +#endif + diff --git a/private/ntos/miniport/wd33c93/wd33c93.rc b/private/ntos/miniport/wd33c93/wd33c93.rc new file mode 100644 index 000000000..63793a7ad --- /dev/null +++ b/private/ntos/miniport/wd33c93/wd33c93.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Western Digital SCSI Controller Driver" +#define VER_INTERNALNAME_STR "wd33c93.sys" +#define VER_ORIGINALFILENAME_STR "wd33c93.sys" + +#include "common.ver" + |