summaryrefslogtreecommitdiffstats
path: root/private/ntos/miniport/wd33c93
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/miniport/wd33c93')
-rw-r--r--private/ntos/miniport/wd33c93/makefile6
-rw-r--r--private/ntos/miniport/wd33c93/maynard.h173
-rw-r--r--private/ntos/miniport/wd33c93/s3scsi.h145
-rw-r--r--private/ntos/miniport/wd33c93/sources37
-rw-r--r--private/ntos/miniport/wd33c93/vendor.h26
-rw-r--r--private/ntos/miniport/wd33c93/wd33c93.c5946
-rw-r--r--private/ntos/miniport/wd33c93/wd33c93.h305
-rw-r--r--private/ntos/miniport/wd33c93/wd33c93.rc12
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"
+