summaryrefslogtreecommitdiffstats
path: root/private/ntos/miniport/wd7000ex
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/miniport/wd7000ex
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/miniport/wd7000ex')
-rw-r--r--private/ntos/miniport/wd7000ex/makefile6
-rw-r--r--private/ntos/miniport/wd7000ex/sources35
-rw-r--r--private/ntos/miniport/wd7000ex/wd7000ex.c1684
-rw-r--r--private/ntos/miniport/wd7000ex/wd7000ex.h218
-rw-r--r--private/ntos/miniport/wd7000ex/wd7000ex.rc12
5 files changed, 1955 insertions, 0 deletions
diff --git a/private/ntos/miniport/wd7000ex/makefile b/private/ntos/miniport/wd7000ex/makefile
new file mode 100644
index 000000000..b4338519e
--- /dev/null
+++ b/private/ntos/miniport/wd7000ex/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 WINDOWS
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/miniport/wd7000ex/sources b/private/ntos/miniport/wd7000ex/sources
new file mode 100644
index 000000000..c43f0916e
--- /dev/null
+++ b/private/ntos/miniport/wd7000ex/sources
@@ -0,0 +1,35 @@
+!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=fd7000ex
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=MINIPORT
+
+INCLUDES=..\..\inc
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\scsiport.lib
+
+SOURCES=wd7000ex.c wd7000ex.rc
diff --git a/private/ntos/miniport/wd7000ex/wd7000ex.c b/private/ntos/miniport/wd7000ex/wd7000ex.c
new file mode 100644
index 000000000..6dfb8af32
--- /dev/null
+++ b/private/ntos/miniport/wd7000ex/wd7000ex.c
@@ -0,0 +1,1684 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wd7000ex.c
+
+Abstract:
+
+ This is the port driver for the WD7000EX SCSI adapter.
+
+Authors:
+
+ Mike Glass
+ Shashir Shah (Western Digital)
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "miniport.h"
+#include "wd7000ex.h" // includes scsi.h
+
+//
+// The following structure is allocated
+// from noncached memory as data will be DMA'd to
+// and from it.
+//
+
+typedef struct _NONCACHED_EXTENSION {
+
+ //
+ // Internal Control Block
+ //
+
+ ICB Icb;
+
+ //
+ // Adapter inquiry buffer
+ //
+
+ ADAPTER_INQUIRY AdapterInquiry;
+
+} NONCACHED_EXTENSION, *PNONCACHED_EXTENSION;
+
+//
+// Device extension
+//
+
+typedef struct _HW_DEVICE_EXTENSION {
+
+ PEISA_CONTROLLER EisaController;
+
+ //
+ // Adapter parameters
+ //
+
+ CCHAR IdtVector;
+
+ //
+ // Adapter host adapter Id.
+ //
+
+ UCHAR HostTargetId;
+
+ //
+ // Real-Mode interrupt state.
+ //
+
+ UCHAR InterruptState;
+ UCHAR NumberOfBuses;
+
+} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION;
+
+
+//
+// Function declarations
+//
+// Functions that start with 'Wd7000Ex' are entry points
+// for the OS port driver.
+//
+
+ULONG
+Wd7000ExFindAdapter(
+ IN PVOID DeviceExtension,
+ IN PVOID Context,
+ IN PVOID BusInformation,
+ IN PCHAR ArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ OUT PBOOLEAN Again
+ );
+
+BOOLEAN
+Wd7000ExInitialize(
+ IN PVOID DeviceExtension
+ );
+
+BOOLEAN
+Wd7000ExStartIo(
+ IN PVOID DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+BOOLEAN
+Wd7000ExInterrupt(
+ IN PVOID DeviceExtension
+ );
+
+BOOLEAN
+Wd7000ExResetBus(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+ );
+
+//
+// This function is called from Wd7000ExStartIo.
+//
+
+VOID
+BuildCcb(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+//
+// This function is called from BuildCcb.
+//
+
+VOID
+BuildSdl(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+//
+// This function is called from Wd7000Initialize.
+//
+
+BOOLEAN
+AdapterPresent(
+ IN PVOID HwDeviceExtension,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ IN OUT PULONG AdapterCount
+ );
+
+BOOLEAN
+SendCommand(
+ IN UCHAR Opcode,
+ IN ULONG Address,
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ );
+
+
+BOOLEAN
+Wd7000ExAdapterState(
+ IN PVOID DeviceExtension,
+ IN PVOID AdaptersFound,
+ IN BOOLEAN SaveState
+ )
+
+/*++
+
+Routine Description:
+
+ Saves/restores adapter's real-mode configuration state.
+
+Arguments:
+
+ DeviceExtension - Adapter object device extension.
+ AdaptersFound - Passed through from DriverEntry as additional
+ context for the call.
+ SaveState - TRUE = Save adapter state, FALSE = restore state.
+
+Return Value:
+
+ The spec did not intend for this routine to have a return value.
+ Whoever did the header file just forgot to change the BOOLEAN to
+ a VOID. We will just return FALSE to shot the compiler up.
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = DeviceExtension;
+
+ if (SaveState) {
+
+ //
+ // Remember system interrupt state.
+ //
+ deviceExtension->InterruptState = ScsiPortReadPortUchar(
+ &deviceExtension->EisaController->SystemInterruptEnable);
+
+ } else {
+
+ //
+ // Restore system interrupt state.
+ //
+ ScsiPortWritePortUchar(&deviceExtension->EisaController->SystemInterruptEnable,
+ deviceExtension->InterruptState);
+
+ }
+
+ return FALSE;
+}
+
+
+ULONG
+DriverEntry (
+ IN PVOID DriverObject,
+ IN PVOID Argument2
+ )
+
+/*++
+
+Routine Description:
+
+ Installable driver initialization entry point for system.
+
+Arguments:
+
+ Driver Object
+ Argument2 - Not used.
+
+Return Value:
+
+ Status from ScsiPortInitialize()
+
+--*/
+
+{
+ HW_INITIALIZATION_DATA hwInitializationData;
+ ULONG AdapterCount = 0;
+ ULONG i;
+
+ DebugPrint((1,"\n\nSCSI WD7000EX MiniPort Driver\n"));
+
+ //
+ // Zero out structure.
+ //
+
+ for (i=0; i<sizeof(HW_INITIALIZATION_DATA); i++) {
+ ((PUCHAR)&hwInitializationData)[i] = 0;
+ }
+
+ //
+ // Set size of hwInitializationData.
+ //
+
+ hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
+
+ //
+ // Set entry points.
+ //
+
+ hwInitializationData.HwInitialize = Wd7000ExInitialize;
+ hwInitializationData.HwFindAdapter = Wd7000ExFindAdapter;
+ hwInitializationData.HwStartIo = Wd7000ExStartIo;
+ hwInitializationData.HwInterrupt = Wd7000ExInterrupt;
+ hwInitializationData.HwResetBus = Wd7000ExResetBus;
+ hwInitializationData.HwAdapterState = Wd7000ExAdapterState;
+
+ //
+ // Set number of access ranges and bus type.
+ //
+
+ hwInitializationData.NumberOfAccessRanges = 1;
+ hwInitializationData.AdapterInterfaceType = Eisa;
+
+ //
+ // Indicate no buffer mapping but will need physical addresses.
+ //
+
+ hwInitializationData.NeedPhysicalAddresses = TRUE;
+
+ //
+ // Specify size of extensions.
+ //
+
+ hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION);
+
+ //
+ // Ask for SRB extensions for CCBs.
+ //
+
+ hwInitializationData.SrbExtensionSize = sizeof(CCB);
+
+ return ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &AdapterCount);
+
+} // end DriverEntry()
+
+
+BOOLEAN
+AdapterPresent(
+ IN PVOID HwDeviceExtension,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ IN OUT PULONG AdapterCount
+ )
+
+/*++
+
+Routine Description:
+
+ Determine if WD7000EX SCSI adapter is installed in system
+ by reading the EISA board configuration registers for each
+ EISA slot looking for the correct signature.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+ ConfigInfo - Supplies the configuration information stucture. If an
+ adapter is found the access range is update.
+
+ AdapterCount - Supplies the count of slots which have already been checked.
+
+Return Value:
+
+ TRUE if adapter present.
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ ULONG eisaSlotNumber;
+ PEISA_CONTROLLER eisaController;
+
+ //
+ // Check to see if adapter present in system.
+ //
+
+
+ for (eisaSlotNumber=*AdapterCount + 1; eisaSlotNumber<MAXIMUM_EISA_SLOTS; eisaSlotNumber++) {
+
+ //
+ // Update the adapter count.
+ //
+
+ (*AdapterCount)++;
+
+ //
+ // Get the system address for this card. The card uses I/O space.
+ // If ConfigInfo already has default information about this
+ // controller, use it. If not, then we derive our own. This
+ // is for Chicago compatibility.
+ //
+ if (ScsiPortConvertPhysicalAddressToUlong(
+ (*ConfigInfo->AccessRanges)[0].RangeStart) != 0) {
+
+ eisaController = ScsiPortGetDeviceBase(
+ deviceExtension,
+ ConfigInfo->AdapterInterfaceType,
+ ConfigInfo->SystemIoBusNumber,
+ (*ConfigInfo->AccessRanges)[0].RangeStart,
+ (*ConfigInfo->AccessRanges)[0].RangeLength,
+ (BOOLEAN) !((*ConfigInfo->AccessRanges)[0].RangeInMemory));
+ } else {
+
+ eisaController = ScsiPortGetDeviceBase(
+ deviceExtension,
+ ConfigInfo->AdapterInterfaceType,
+ ConfigInfo->SystemIoBusNumber,
+ ScsiPortConvertUlongToPhysicalAddress(0x1000 * eisaSlotNumber),
+ 0x1000,
+ TRUE);
+ }
+
+ eisaController =
+ (PEISA_CONTROLLER)((PUCHAR)eisaController + EISA_ADDRESS_BASE);
+
+ if ((ScsiPortReadPortUchar(&eisaController->BoardId[0]) == 0x5C) &&
+ (ScsiPortReadPortUchar(&eisaController->BoardId[1]) == 0x83) &&
+ (ScsiPortReadPortUchar(&eisaController->BoardId[2]) == 0x20)) {
+
+ deviceExtension->EisaController = eisaController;
+
+ return TRUE;
+ }
+
+ //
+ // The card is not here so clean up.
+ //
+
+ ScsiPortFreeDeviceBase(deviceExtension,
+ (PUCHAR)eisaController - EISA_ADDRESS_BASE);
+
+ } // end for (eisaSlotNumber ...
+
+ //
+ // Clear the adapter count for the next bus.
+ //
+
+ *AdapterCount = 0;
+
+ return FALSE;
+
+} // end AdapterPresent()
+
+
+ULONG
+Wd7000ExFindAdapter(
+ IN PVOID HwDeviceExtension,
+ IN PVOID Context,
+ IN PVOID BusInformation,
+ IN PCHAR ArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ OUT PBOOLEAN Again
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by the OS-specific port driver after
+ the necessary storage has been allocated, to gather information
+ about the adapter's configuration.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+ ConfigInfo - Configuration information structure describing HBA
+
+Return Value:
+
+ TRUE if adapter present in system
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PEISA_CONTROLLER eisaController;
+ PULONG adapterCount = Context;
+ PNONCACHED_EXTENSION ncExtension;
+ ULONG eisaSlotNumber;
+ PICB icb;
+ PADAPTER_INQUIRY adapterInquiry;
+ ULONG physicalIcb;
+ ULONG i;
+ ULONG length;
+ UCHAR status;
+
+ //
+ // Check to see if adapter present in system.
+ //
+ if (!AdapterPresent(deviceExtension, ConfigInfo, Context)) {
+ DebugPrint((1,"Wd7000EX: SCSI adapter not present\n"));
+ *Again = FALSE;
+ return SP_RETURN_NOT_FOUND;
+ }
+
+ //
+ // There is still more to look at.
+ //
+
+ *Again = FALSE;
+
+ //
+ // Fill in the access array information only if there are no
+ // default parameters already there.
+ //
+ if (ScsiPortConvertPhysicalAddressToUlong(
+ (*ConfigInfo->AccessRanges)[0].RangeStart) == 0) {
+
+ *Again = TRUE;
+ (*ConfigInfo->AccessRanges)[0].RangeStart =
+ ScsiPortConvertUlongToPhysicalAddress(0x1000 * (*((PULONG) Context)) + EISA_ADDRESS_BASE);
+ (*ConfigInfo->AccessRanges)[0].RangeLength = sizeof(EISA_CONTROLLER);
+ (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
+
+ //
+ // Indicate maximum transfer length in bytes.
+ //
+ ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_SIZE;
+
+ //
+ // Maximum number of physical segments is 32.
+ //
+ ConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SDL_SIZE;
+
+ //
+ // Set the configuration parameters for this card.
+ //
+
+ ConfigInfo->NumberOfBuses = 1;
+ deviceExtension->NumberOfBuses = 1;
+ ConfigInfo->ScatterGather = TRUE;
+ ConfigInfo->Master = TRUE;
+
+ //
+ // Get a noncached extension for an adapter inquiry command.
+ //
+
+ ncExtension = ScsiPortGetUncachedExtension(
+ deviceExtension,
+ ConfigInfo,
+ sizeof(NONCACHED_EXTENSION));
+
+ if (ncExtension == NULL) {
+
+ //
+ // Log error.
+ //
+
+ ScsiPortLogError(
+ deviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ 6 << 16
+ );
+
+ return SP_RETURN_ERROR;
+ }
+
+ length = sizeof(NONCACHED_EXTENSION);
+
+ //
+ // Convert virtual to physical address.
+ //
+
+ physicalIcb = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension,
+ NULL,
+ ncExtension,
+ &length));
+
+ //
+ // Initialize the pointers.
+ //
+
+ icb = &ncExtension->Icb;
+ adapterInquiry = &ncExtension->AdapterInquiry;
+
+ //
+ // Create ICB for Adapter Inquiry Command.
+ //
+
+ icb->IcbFlags = 0;
+ icb->CompletionStatus = 0;
+ icb->Reserved = 0;
+ icb->DataBufferAddress = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(
+ deviceExtension,
+ NULL,
+ adapterInquiry,
+ &length));
+
+ icb->TransferCount = sizeof(ADAPTER_INQUIRY);
+
+ icb->OpCode = ADAPTER_INQUIRY_COMMAND;
+
+ //
+ // Get ICB physical address.
+ //
+
+ physicalIcb = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(
+ deviceExtension,
+ NULL,
+ icb,
+ &length));
+
+ //
+ // Disable system interrupts.
+ //
+
+ ScsiPortWritePortUchar(&deviceExtension->EisaController->SystemInterruptEnable,
+ SYSTEM_INTERRUPTS_DISABLE);
+
+ //
+ // Write ICB physical address and command to mailbox.
+ //
+
+ SendCommand(PROCESS_ICB,
+ physicalIcb,
+ deviceExtension);
+
+ //
+ // Poll for ICB completion.
+ //
+
+ i = 0;
+ while ((status =
+ ScsiPortReadPortUchar(
+ &deviceExtension->EisaController->ResponseRegister)) == 0) {
+
+ i++;
+
+ if (i > 100000) {
+
+ break;
+ }
+
+ ScsiPortStallExecution(10);
+ }
+
+ if (status == 0) {
+
+ //
+ // The request timed out. Log an error and return.
+ //
+
+ ScsiPortLogError(
+ deviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ 7 << 16
+ );
+ return SP_RETURN_ERROR;
+ }
+
+ DebugPrint((1, "Wd7000ExFindAdapter: Get configuration request time = %d.\n", i * 10));
+
+ //
+ // Acknowledge interrupt.
+ //
+
+ ScsiPortWritePortUchar(&deviceExtension->EisaController->ResponseRegister, 0xFF);
+
+ //
+ // Enable system interrupts.
+ //
+
+ ScsiPortWritePortUchar(&deviceExtension->EisaController->SystemInterruptEnable,
+ SYSTEM_INTERRUPTS_ENABLE);
+
+ //
+ // Check returned status for success.
+ //
+
+ if (status != COMPLETE_SUCCESS) {
+
+ //
+ // Give up.
+ //
+
+ DebugPrint((1,"Wd7000Ex: Response register %x\n", status));
+ DebugPrint((1,"Wd7000Ex: Adapter inquiry failed\n"));
+
+ //
+ // Log error.
+ //
+
+ ScsiPortLogError(
+ deviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ 8 << 16
+ );
+
+ return SP_RETURN_ERROR;
+ }
+
+ //
+ // NOTE: Delay here. I don't understand this latency between
+ // when the device interrupts and the status of the ICB
+ // is success and when the data is actually available in
+ // the buffer.
+ //
+
+ ScsiPortStallExecution(300);
+
+ if (adapterInquiry->AdapterInformation & DUAL_CHANNEL) {
+
+ //
+ // There are two buses on the adapter.
+ //
+
+ ConfigInfo->InitiatorBusId[1] =
+ (adapterInquiry->ChannelInformation >> 4) & BUS_ID_MASK;
+ ConfigInfo->NumberOfBuses = 2;
+ deviceExtension->NumberOfBuses = 2;
+ }
+
+ ConfigInfo->InitiatorBusId[0] =
+ adapterInquiry->ChannelInformation & BUS_ID_MASK;
+ ConfigInfo->BusInterruptLevel = adapterInquiry->Irq;
+ }
+ deviceExtension->HostTargetId = ConfigInfo->InitiatorBusId[0];
+ return SP_RETURN_FOUND;
+} // end Wd7000ExFindAdapter()
+
+
+BOOLEAN
+Wd7000ExInitialize(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Inititialize adapter and mailbox.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ TRUE - if initialization successful.
+ FALSE - if initialization unsuccessful.
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ BOOLEAN returnValue;
+
+ //
+ // Reset Wd7000Ex and SCSI bus.
+ //
+
+ returnValue = Wd7000ExResetBus(deviceExtension, 0);
+ ScsiPortNotification(
+ ResetDetected,
+ deviceExtension,
+ 0
+ );
+
+ if (deviceExtension->NumberOfBuses == 2) {
+
+ Wd7000ExResetBus(deviceExtension, 1);
+ ScsiPortNotification(
+ ResetDetected,
+ deviceExtension,
+ 1
+ );
+
+ }
+
+ if (!returnValue) {
+
+ return FALSE;
+
+ } else {
+
+ return TRUE;
+ }
+
+} // end Wd7000ExInitialize()
+
+
+BOOLEAN
+Wd7000ExStartIo(
+ IN PVOID HwDeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the SCSI port driver synchronized
+ with the kernel. The mailboxes are scanned for an empty one and
+ the CCB is written to it. Then the doorbell is rung and the
+ OS port driver is notified that the adapter can take
+ another request, if any are available.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+ Srb - IO request packet
+
+Return Value:
+
+ Nothing
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PEISA_CONTROLLER eisaController = deviceExtension->EisaController;
+ PCCB ccb;
+ UCHAR opCode;
+ ULONG physicalCcb;
+ ULONG length;
+ ULONG i = 0;
+
+ DebugPrint((3,"Wd7000ExStartIo: Enter routine\n"));
+
+ //
+ // Get CCB from SRB.
+ //
+
+ if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND) {
+
+ //
+ // Get CCB to abort.
+ //
+
+ ccb = Srb->NextSrb->SrbExtension;
+
+ //
+ // Set abort SRB for completion.
+ //
+
+ ccb->AbortSrb = Srb;
+
+ } else {
+
+ ccb = Srb->SrbExtension;
+
+ //
+ // Save SRB back pointer in CCB.
+ //
+
+ ccb->SrbAddress = Srb;
+ }
+
+ //
+ // Get CCB physical address.
+ //
+
+ physicalCcb =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension, NULL, ccb, &length));
+
+ //
+ // Assume physical address is contiguous for size of CCB.
+ //
+
+ ASSERT(length >= sizeof(CCB));
+
+ switch (Srb->Function) {
+
+ case SRB_FUNCTION_EXECUTE_SCSI:
+
+ //
+ // Build CCB.
+ //
+
+ BuildCcb(deviceExtension, Srb);
+
+ //
+ // Or in PathId.
+ //
+
+ opCode = PROCESS_CCB | Srb->PathId << 6;
+
+ break;
+
+ case SRB_FUNCTION_ABORT_COMMAND:
+
+ DebugPrint((1, "Wd7000ExStartIo: Abort request received\n"));
+
+ //
+ // NOTE: Race condition if aborts occur
+ // (what if CCB to be aborted
+ // completes after setting new SrbAddress?)
+ //
+
+ opCode = ABORT_CCB;
+
+ break;
+
+ case SRB_FUNCTION_RESET_BUS:
+
+ //
+ // Reset Wd7000Ex and SCSI bus.
+ //
+
+ DebugPrint((1, "Wd7000ExStartIo: Reset bus request received\n"));
+
+ Wd7000ExResetBus(deviceExtension,
+ Srb->PathId);
+
+ return TRUE;
+
+ case SRB_FUNCTION_RESET_DEVICE:
+
+ break;
+
+ default:
+
+ //
+ // Set error, complete request
+ // and signal ready for next request.
+ //
+
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+
+ return TRUE;
+
+ } // end switch
+
+ SendCommand(opCode,
+ physicalCcb,
+ deviceExtension);
+
+ //
+ // Adapter ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+
+ return TRUE;
+
+} // end Wd7000ExStartIo()
+
+
+BOOLEAN
+Wd7000ExInterrupt(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This is the interrupt service routine for the WD7000EX SCSI adapter.
+ It reads the interrupt register to determine if the adapter is indeed
+ the source of the interrupt and clears the interrupt at the device.
+ If the adapter is interrupting because a mailbox is full, the CCB is
+ retrieved to complete the request.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ TRUE if MailboxIn full
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PCCB ccb;
+ PSCSI_REQUEST_BLOCK srb;
+ PSCSI_REQUEST_BLOCK abortedSrb;
+ PEISA_CONTROLLER eisaController = deviceExtension->EisaController;
+ ULONG logError = 0;
+ ULONG physicalCcb;
+ UCHAR srbStatus;
+ UCHAR response;
+
+ if (!(ScsiPortReadPortUchar(
+ &eisaController->SystemInterruptEnable) & SYSTEM_INTERRUPT_PENDING)) {
+
+ DebugPrint((2, "WD7000EX: Spurious interrupt\n"));
+ return FALSE;
+ }
+
+ //
+ // Get physical address of CCB.
+ //
+
+ physicalCcb = ScsiPortReadPortUlong(&eisaController->ResponseMailbox);
+
+ //
+ // Read status from response register.
+ //
+
+ response = ScsiPortReadPortUchar(&eisaController->ResponseRegister);
+
+ //
+ // Acknowledge interrupt.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->ResponseRegister, 0xFF);
+
+ //
+ // Get virtual CCB address.
+ //
+
+ ccb = ScsiPortGetVirtualAddress(deviceExtension, ScsiPortConvertUlongToPhysicalAddress(physicalCcb));
+
+ //
+ // Make sure the ccb is valid, if it is supposed to be.
+ //
+
+ if (ccb == NULL &&
+ response != ABORT_COMMAND_COMPLETE &&
+ response != BUS_RESET &&
+ response != RESET_DEVICE_COMPLETE &&
+ response != ABORT_CCB_NOT_FOUND) {
+
+ ScsiPortLogError(
+ HwDeviceExtension,
+ NULL,
+ 0,
+ deviceExtension->HostTargetId,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ (5 << 16) | response
+ );
+
+ return TRUE;
+ }
+
+ switch (response & STATUS_MASK) {
+
+ case COMPLETE_SUCCESS:
+ case REQUEST_SENSE_COMPLETE:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Command complete\n"));
+ srbStatus = SRB_STATUS_SUCCESS;
+
+ break;
+
+ case COMPLETE_ERROR:
+
+ DebugPrint((2, "Wd7000ExInterrupt: Command complete with error\n"));
+ srbStatus = SRB_STATUS_ERROR;
+
+ break;
+
+ case BUS_RESET:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Bus reset\n"));
+ srbStatus = SRB_STATUS_BUS_RESET;
+
+ return TRUE;
+
+ case ABORT_COMMAND_COMPLETE:
+
+ //
+ // This interrupt is not associated with a CCB.
+ //
+
+ DebugPrint((1, "Wd7000ExInterrupt: Command aborted\n"));
+
+ return TRUE;
+
+ case RESET_DEVICE_COMPLETE:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Device reset complete\n"));
+ srbStatus = SRB_STATUS_SUCCESS;
+
+ break;
+
+ case COMMAND_ABORTED:
+
+ DebugPrint((1, "Wd7000ExInterrupt: Abort command complete\n"));
+
+ //
+ // Get SRB from CCB.
+ //
+
+ srb = ccb->SrbAddress;
+
+ //
+ // Get aborted SRB.
+ //
+
+ abortedSrb = ccb->AbortSrb;
+
+ //
+ // Update the abort SRB status.
+ //
+
+ abortedSrb->SrbStatus = SRB_STATUS_SUCCESS;
+
+ //
+ // Call notification routine for the aborted SRB.
+ //
+
+ ScsiPortNotification(RequestComplete,
+ (PVOID)deviceExtension,
+ abortedSrb);
+
+ //
+ // Update status of the aborted SRB.
+ //
+
+ srbStatus = SRB_STATUS_TIMEOUT;
+
+ break;
+
+ case ABORT_CCB_NOT_FOUND:
+
+ //
+ // This normally occurs when the adapter has started processing
+ // the request. Drop it on the floor and let the abort time out.
+ //
+
+ DebugPrint((1, "Wd7000ExInterrupt: Abort command failed\n"));
+ return TRUE;
+
+ case ILLEGAL_STATUS:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Illegal status\n"));
+ logError = SP_INTERNAL_ADAPTER_ERROR;
+ srbStatus = SRB_STATUS_ERROR;
+
+ break;
+
+ case DEVICE_TIMEOUT:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Device timeout\n"));
+ srbStatus = SRB_STATUS_SELECTION_TIMEOUT;
+
+ break;
+
+ case SHORT_RECORD_EXCEPTION:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Data record short\n"));
+
+ srb = ccb->SrbAddress;
+ if (ccb->ScsiDeviceStatus == SCSISTAT_CHECK_CONDITION) {
+ srbStatus = SRB_STATUS_ERROR;
+ } else {
+ srbStatus = SRB_STATUS_DATA_OVERRUN;
+
+ //
+ // Update SRB with actual number of bytes transferred.
+ //
+
+ srb->DataTransferLength -= ccb->TransferCount;
+ }
+
+ break;
+
+ case LONG_RECORD_EXCEPTION:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Data overrun\n"));
+ logError = SP_INTERNAL_ADAPTER_ERROR;
+
+ srbStatus = SRB_STATUS_DATA_OVERRUN;
+
+ break;
+
+ case PARITY_ERROR:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Parity error\n"));
+ logError = SP_BUS_PARITY_ERROR;
+ srbStatus = SRB_STATUS_PARITY_ERROR;
+
+ break;
+
+ case UNEXPECTED_BUS_FREE:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Unexpected bus free\n"));
+ logError = SP_UNEXPECTED_DISCONNECT;
+ srbStatus = SRB_STATUS_UNEXPECTED_BUS_FREE;
+
+ break;
+
+ case INVALID_STATE:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Phase sequence error\n"));
+ logError = SP_INTERNAL_ADAPTER_ERROR;
+ srbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
+
+ break;
+
+ case HOST_DMA_ERROR:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Host DMA error\n"));
+ logError = SP_INTERNAL_ADAPTER_ERROR;
+ srbStatus = SRB_STATUS_ERROR;
+
+ break;
+
+ case INVALID_COMMAND:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Invalid command\n"));
+ logError = SP_INTERNAL_ADAPTER_ERROR;
+ srbStatus = SRB_STATUS_ERROR;
+
+ break;
+
+ case INCORRECT_COMMAND_DIRECTION:
+ default:
+
+ DebugPrint((3, "Wd7000ExInterrupt: Incorrect command direction\n"));
+ logError = SP_INTERNAL_ADAPTER_ERROR;
+ srbStatus = SRB_STATUS_ERROR;
+
+ break;
+
+ } // end switch
+
+ //
+ // Get SRB from CCB.
+ //
+
+ srb = ccb->SrbAddress;
+
+ //
+ // Update SRB status.
+ //
+
+ srb->SrbStatus = srbStatus;
+
+ //
+ // Copy SCSI status from CCB to SRB.
+ //
+
+ srb->ScsiStatus = ccb->ScsiDeviceStatus;
+
+ if (logError != 0) {
+
+ //
+ // Log error.
+ //
+
+ ScsiPortLogError(
+ deviceExtension,
+ srb,
+ srb->PathId,
+ srb->TargetId,
+ srb->Lun,
+ logError,
+ 2 << 16 | response
+ );
+
+ }
+
+ //
+ // Call notification routine for the SRB.
+ //
+
+ ScsiPortNotification(RequestComplete,
+ (PVOID)deviceExtension,
+ srb);
+
+ return TRUE;
+
+} // end Wd7000ExInterrupt()
+
+
+VOID
+BuildCcb(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ Build CCB for WD7000EX.
+
+Arguments:
+
+ DeviceExtenson
+ SRB
+
+Return Value:
+
+ Nothing.
+
+--*/
+
+{
+ PCCB ccb = Srb->SrbExtension;
+
+ DebugPrint((3,"BuildCcb: Enter routine\n"));
+
+ //
+ // Zero out CCB statuses.
+ //
+
+ ccb->CompletionStatus = 0;
+ ccb->ScsiDeviceStatus = 0;
+
+ //
+ // Set transfer direction bit.
+ //
+
+ if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
+ ccb->CommandFlags = DIRECTION_WRITE | SCATTER_GATHER;
+ } else if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
+ ccb->CommandFlags = DIRECTION_READ | SCATTER_GATHER;
+ } else {
+ ccb->CommandFlags = 0;
+ }
+
+ //
+ // Check if disconnect explicity forbidden.
+ //
+
+ if (!(Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)) {
+
+ ccb->CommandFlags |= DISCONNECTION;
+ }
+
+ //
+ // Check if synchronous data transfers are allowed.
+ //
+
+ if (!(Srb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
+
+ ccb->CommandFlags |= SYNCHRONOUS_NEGOCIATION;
+
+ }
+
+ //
+ // Set target id and LUN.
+ // PathId is indicated in upper bits of command byte.
+ //
+
+ ccb->TargetId = Srb->TargetId;
+ ccb->Lun = Srb->Lun;
+
+ //
+ // Set CDB length and copy to CCB.
+ //
+
+ ScsiPortMoveMemory(ccb->Cdb, Srb->Cdb, Srb->CdbLength);
+
+ //
+ // Build SDL in CCB if data transfer.
+ //
+
+ if (Srb->DataTransferLength > 0) {
+ BuildSdl(DeviceExtension, Srb);
+ } else {
+ ccb->DataBufferAddress = 0;
+ ccb->TransferCount = 0;
+ }
+
+ return;
+
+} // end BuildCcb()
+
+
+VOID
+BuildSdl(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds a scatter/gather descriptor list for the CCB.
+
+Arguments:
+
+ DeviceExtension
+ Srb
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PVOID dataPointer = Srb->DataBuffer;
+ ULONG bytesLeft = Srb->DataTransferLength;
+ PCCB ccb = Srb->SrbExtension;
+ PSDL sdl = &ccb->Sdl;
+ ULONG physicalSdl;
+ ULONG physicalAddress;
+ ULONG length;
+ ULONG descriptorCount = 0;
+
+ DebugPrint((3,"BuildSdl: Enter routine\n"));
+
+ //
+ // Get physical SDL address.
+ //
+
+ physicalSdl = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension, NULL,
+ sdl, &length));
+
+ //
+ // Assume physical memory contiguous for sizeof(SDL) bytes.
+ //
+
+ ASSERT(length >= sizeof(SDL));
+
+ //
+ // Create SDL segment descriptors.
+ //
+
+ do {
+
+ DebugPrint((3, "BuildSdl: Data buffer %lx\n", dataPointer));
+
+ //
+ // Get physical address and length of contiguous
+ // physical buffer.
+ //
+
+ physicalAddress =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ Srb,
+ dataPointer,
+ &length));
+
+ DebugPrint((3, "BuildSdl: Physical address %lx\n", physicalAddress));
+ DebugPrint((3, "BuildSdl: Data length %lx\n", length));
+ DebugPrint((3, "BuildSdl: Bytes left %lx\n", bytesLeft));
+
+ //
+ // If length of physical memory is more
+ // than bytes left in transfer, use bytes
+ // left as final length.
+ //
+
+ if (length > bytesLeft) {
+ length = bytesLeft;
+ }
+
+ sdl->Descriptor[descriptorCount].Address = physicalAddress;
+ sdl->Descriptor[descriptorCount].Length = length;
+
+ //
+ // Adjust counts.
+ //
+
+ dataPointer = (PUCHAR)dataPointer + length;
+ bytesLeft -= length;
+ descriptorCount++;
+
+ } while (bytesLeft);
+
+ //
+ // Write SDL length to CCB.
+ //
+
+ ccb->TransferCount = descriptorCount * sizeof(SG_DESCRIPTOR);
+
+ DebugPrint((3,"BuildSdl: SDL length is %d\n", descriptorCount));
+
+ //
+ // Write SDL address to CCB.
+ //
+
+ ccb->DataBufferAddress = physicalSdl;
+
+ DebugPrint((3,"BuildSdl: SDL address is %lx\n", sdl));
+
+ DebugPrint((3,"BuildSdl: CCB address is %lx\n", ccb));
+
+ return;
+
+} // end BuildSdl()
+
+
+BOOLEAN
+Wd7000ExResetBus(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+)
+
+/*++
+
+Routine Description:
+
+ Reset Wd7000Ex SCSI adapter and SCSI bus.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ Nothing.
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PEISA_CONTROLLER eisaController = deviceExtension->EisaController;
+ UCHAR control;
+ ULONG i;
+
+ DebugPrint((2,"ResetBus: Reset Wd7000Ex and SCSI bus\n"));
+
+ //
+ // Disable system interrupts.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemInterruptEnable,
+ SYSTEM_INTERRUPTS_DISABLE);
+
+ //
+ // Set interrupt enable, SCSI bus reset bits in control register.
+ //
+
+ control = ADAPTER_INTERRUPT_ENABLE;
+ control += PathId == 0 ? SCSI_BUS_RESET_CHANNEL_0 : SCSI_BUS_RESET_CHANNEL_1;
+
+ ScsiPortWritePortUchar(&eisaController->ControlRegister, control);
+
+ //
+ // Wait 30 microseconds.
+ //
+
+ ScsiPortStallExecution(30);
+
+ //
+ // Reset adapter and clear SCSI bus reset.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->ControlRegister,
+ ADAPTER_RESET + ADAPTER_INTERRUPT_ENABLE);
+
+ //
+ // Wait for up to 2 seconds.
+ //
+
+ for (i=0; i<20; i++) {
+
+ //
+ // Stall 100 milliseconds.
+ //
+
+ ScsiPortStallExecution(100 * 1000);
+
+ //
+ // Check status.
+ //
+
+ if (ScsiPortReadPortUchar(&eisaController->ResponseRegister) ==
+ COMPLETE_SUCCESS) {
+
+ break;
+ }
+ }
+
+ if (i==20) {
+
+ DebugPrint((1, "ResetBus: Reset failed\n"));
+
+ ScsiPortLogError(
+ deviceExtension,
+ NULL,
+ 0,
+ deviceExtension->HostTargetId,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ 3 << 16
+ );
+
+ return FALSE;
+ }
+
+ //
+ // Acknowledge interrupt.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->ResponseRegister, 0xFF);
+
+ //
+ // Enable system interrupts.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemInterruptEnable,
+ SYSTEM_INTERRUPTS_ENABLE);
+
+ //
+ // Enable response interrupt mask.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->ResponseInterruptMask, 0xFF);
+
+ //
+ // Complete all outstanding requests with SRB_STATUS_BUS_RESET.
+ //
+
+ ScsiPortCompleteRequest((PVOID) deviceExtension,
+ (UCHAR) PathId,
+ (UCHAR) 0xFF,
+ (UCHAR) 0xFF,
+ (UCHAR) SRB_STATUS_BUS_RESET);
+
+ return TRUE;
+
+} // end Wd7000ExResetBus()
+
+
+BOOLEAN
+SendCommand(
+ IN UCHAR Opcode,
+ IN ULONG Address,
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Send a command the the WD7000ex.
+
+Arguments:
+
+ Opcode - Operation code to send.
+
+ Address - Physical address for the operation.
+
+ DeviceExtension - Pointer to device extension.
+
+Return Value:
+
+ True if command sent.
+ False if adapter never reached 'ready for next command' state.
+
+--*/
+
+{
+
+ PEISA_CONTROLLER Registers = DeviceExtension->EisaController;
+ ULONG i;
+
+ for (i=0; i<1000; i++) {
+
+ UCHAR status;
+
+ status = ScsiPortReadPortUchar(&Registers->CommandRegister);
+
+ if (status == 0) {
+
+ //
+ // Adapter ready for next command.
+ //
+
+ break;
+
+ } else {
+
+ //
+ // Stall 1 microsecond before trying again.
+ //
+
+ ScsiPortStallExecution(1);
+ }
+ }
+
+ if (i == 100) {
+
+ //
+ // Timed out waiting for adapter to become ready.
+ // Do not complete this command. Instead, let it
+ // timeout so normal recovery will take place.
+ //
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ 0,
+ DeviceExtension->HostTargetId,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ 4 << 16
+ );
+
+ return FALSE;
+ }
+
+ //
+ // Write the address and opcode to adapter.
+ //
+
+ ScsiPortWritePortUlong(&Registers->CommandMailbox, Address);
+ ScsiPortWritePortUchar(&Registers->CommandRegister, Opcode);
+
+ return TRUE;
+
+} // end SendCommand()
+
diff --git a/private/ntos/miniport/wd7000ex/wd7000ex.h b/private/ntos/miniport/wd7000ex/wd7000ex.h
new file mode 100644
index 000000000..4969f6cfe
--- /dev/null
+++ b/private/ntos/miniport/wd7000ex/wd7000ex.h
@@ -0,0 +1,218 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ wd7000.h
+
+Abstract:
+
+ This is the header file in support of the Western Digital
+ WD7000EX EISA SCSI adapter driver.
+
+Author:
+
+ mglass
+
+Notes:
+
+ The adapter supports up to 32 scatter gather descriptors.
+ But the system is tuned for up to 17 pages and allocating 32
+ descriptors out of noncached pool is expensive so the maximum
+ is set to 17.
+
+Revision History:
+
+--*/
+
+#include <scsi.h>
+
+#define MAXIMUM_EISA_SLOTS 0x08
+#define MAXIMUM_TRANSFER_SIZE 0xFFFFFFFF
+#define MAXIMUM_SDL_SIZE 0x11
+#define REQUEST_SENSE_BUFFER_SIZE 0x18
+#define EISA_SLOT_SHIFT 0x0C
+#define EISA_ADDRESS_BASE 0x0C80
+
+//
+// Scatter Gather descriptor list (SDL)
+//
+
+typedef struct _SG_DESCRIPTOR {
+ ULONG Address;
+ ULONG Length;
+} SG_DESCRIPTOR, *PSG_DESCRIPTOR;
+
+typedef struct _SDL {
+ SG_DESCRIPTOR Descriptor[MAXIMUM_SDL_SIZE];
+} SDL, *PSDL;
+
+//
+// Command Control Block (CCB)
+//
+
+typedef struct _CCB {
+
+ USHORT CommandFlags;
+ UCHAR CompletionStatus;
+ UCHAR ScsiDeviceStatus;
+ ULONG DataBufferAddress;
+ ULONG TransferCount;
+ ULONG LinkCommand;
+ ULONG RequestSenseAddress;
+ USHORT RequestSenseLength;
+ UCHAR Cdb[12];
+ UCHAR TargetId;
+ UCHAR Lun;
+ UCHAR Reserved[4];
+ PVOID SrbAddress;
+ PVOID AbortSrb;
+ SDL Sdl;
+ UCHAR RequestSenseBuffer[REQUEST_SENSE_BUFFER_SIZE];
+
+} CCB, *PCCB;
+
+//
+// Command Flag bit definitions
+//
+
+#define RETURN_CCB_STATUS 0x0080
+#define DIRECTION_WRITE 0x0000 + 0x0040
+#define DIRECTION_READ 0x0020 + 0x0040
+#define SCATTER_GATHER 0x0010
+#define AUTO_REQUEST_SENSE 0x0008
+#define DISCONNECTION 0x0004
+#define SYNCHRONOUS_NEGOCIATION 0x0002
+#define SUPPRESS_SHORT_RECORD_EXCEPTION 0x0001
+#define CHAINED_COMMAND 0x8000
+
+//
+// Internal Control Block (ICB)
+//
+
+typedef struct _ICB {
+ USHORT IcbFlags;
+ UCHAR CompletionStatus;
+ UCHAR Reserved;
+ ULONG DataBufferAddress;
+ ULONG TransferCount;
+ UCHAR OpCode;
+ UCHAR IcbCommand[15];
+} ICB, *PICB;
+
+//
+// ICB Operation code definitions
+//
+
+#define ADAPTER_INQUIRY_COMMAND 0x00
+
+//
+// Adapter Inquiry buffer
+//
+
+typedef struct _ADAPTER_INQUIRY {
+ USHORT HardwareRevisionLevel;
+ USHORT FirmwareRevisionLevel;
+ ULONG BiosBaseAddress;
+ UCHAR BusPreemptTime;
+ UCHAR Irq;
+ UCHAR AdapterInformation;
+ UCHAR ChannelInformation;
+ UCHAR ReservedBytes[8];
+ UCHAR VendorId[8];
+ UCHAR ProductId[8];
+} ADAPTER_INQUIRY, *PADAPTER_INQUIRY;
+
+//
+// Bus ID masks
+//
+
+#define BUS_ID_MASK 0x07
+
+#define DUAL_CHANNEL 0x02
+
+//
+// EISA Controller registers
+//
+
+typedef struct _EISA_CONTROLLER {
+
+ UCHAR BoardId[4]; // zC80
+ UCHAR Undefined1; // zC84
+ UCHAR AutoConfiguration[3]; // zC85
+ UCHAR Undefined2; // zC88
+ UCHAR SystemInterruptEnable; // zC89
+ UCHAR Undefined3[3]; // zC8A
+ UCHAR CommandRegister; // zC8D
+ UCHAR ResponseInterruptMask; // zC8E
+ UCHAR ResponseRegister; // zC8F
+ ULONG CommandMailbox; // zC90
+ ULONG ResponseMailbox; // zC94
+ UCHAR Undefined4[26]; // zC98
+ UCHAR ControlRegister; // zCB2
+
+} EISA_CONTROLLER, *PEISA_CONTROLLER;
+
+//
+// Command definitions
+//
+
+#define ILLEGAL_OPCODE 0x00
+#define PROCESS_CCB 0x01
+#define PROCESS_ICB 0x02
+#define RESET_DEVICE 0x03
+#define ABORT_CCB 0x04
+#define RESET_ACKNOWLEDGE 0x05
+
+//
+// Response Status
+//
+
+#define ILLEGAL_STATUS 0x00
+#define COMPLETE_SUCCESS 0x01
+#define COMPLETE_ERROR 0x02
+#define DEVICE_TIMEOUT 0x03
+#define BUS_RESET 0x04
+#define SHORT_RECORD_EXCEPTION 0x05
+#define LONG_RECORD_EXCEPTION 0x06
+#define PARITY_ERROR 0x07
+#define UNEXPECTED_BUS_FREE 0x08
+#define INVALID_STATE 0x09
+#define REQUEST_SENSE_COMPLETE 0x0A
+#define HOST_DMA_ERROR 0x0B
+#define INVALID_COMMAND 0x0C
+#define COMMAND_ABORTED 0x0D
+#define RESET_DEVICE_COMPLETE 0x0E
+#define ABORT_COMMAND_COMPLETE 0x0F
+#define ABORT_CCB_NOT_FOUND 0x10
+#define INCORRECT_COMMAND_DIRECTION 0x11
+
+//
+// System Interrupt Enable bits
+//
+
+#define SYSTEM_INTERRUPTS_DISABLE 0x00
+#define SYSTEM_INTERRUPTS_ENABLE 0x01
+#define SYSTEM_INTERRUPT_PENDING 0x02
+
+//
+// Control Register bit definitions
+//
+
+#define ADAPTER_RESET 0x01
+#define SCSI_BUS_RESET_CHANNEL_0 0x02
+#define ADAPTER_INTERRUPT_ENABLE 0x08
+#define SCSI_BUS_RESET_CHANNEL_1 0x10
+
+#define STATUS_MASK 0x3F
+
+//
+// Send ICB or CCB macro
+//
+
+#define SEND_COMMAND(Opcode, Address, Registers) { \
+ while (ScsiPortReadPortUchar(Registers->CommandRegister)); \
+ ScsiPortWritePortUlong(Registers->CommandMailbox, Address); \
+ ScsiPortWritePortUchar(Registers->CommandRegister, Opcode); \
+}
diff --git a/private/ntos/miniport/wd7000ex/wd7000ex.rc b/private/ntos/miniport/wd7000ex/wd7000ex.rc
new file mode 100644
index 000000000..217ff3689
--- /dev/null
+++ b/private/ntos/miniport/wd7000ex/wd7000ex.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 "Future Domain 7000ex SCSI Controller Driver"
+#define VER_INTERNALNAME_STR "fd7000ex.sys"
+#define VER_ORIGINALFILENAME_STR "fd7000ex.sys"
+
+#include "common.ver"
+