/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    det1000.c

Abstract:

    This is the main file for the autodetection DLL for all the ne1000.sys
    which MS is shipping with Windows NT.

Author:

    Sean Selitrennikoff (SeanSe) October 1992.

Environment:


Revision History:


--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <windows.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ntddnetd.h"
#include "detect.h"


//
// Individual card detection routines
//


//
// Helper functions
//

BOOLEAN
Ne1000CardAt(
    IN INTERFACE_TYPE InterfaceType,
    IN ULONG BusNumber,
    IN ULONG IoBaseAddress
    );

VOID
Ne1000CardCopyDownBuffer(
    IN INTERFACE_TYPE InterfaceType,
    IN ULONG BusNumber,
    IN ULONG IoBaseAddress,
    IN ULONG MemoryBaseAddress,
    IN PUCHAR Buffer,
    IN ULONG Length
    );

ULONG
Ne1000NextIoAddr(
    IN ULONG IoBaseAddress
    );

VOID
Ne1000CardSetup(
    IN  INTERFACE_TYPE InterfaceType,
    IN  ULONG BusNumber,
    IN  ULONG IoBaseAddress,
    OUT PULONG MemoryBaseAddress,
    IN  BOOLEAN EightBitSlot
    );

BOOLEAN
Ne1000CardSlotTest(
    IN  INTERFACE_TYPE InterfaceType,
    IN  ULONG BusNumber,
    IN  ULONG IoBaseAddress,
    OUT PBOOLEAN EightBitSlot
    );

BOOLEAN
Ne1000CheckForNovellAddress(
    IN INTERFACE_TYPE InterfaceType,
    IN ULONG BusNumber,
    IN ULONG IoBaseAddress
    );

#ifdef WORKAROUND

UCHAR Ne1000FirstTime = 1;

//
// List of all the adapters supported in this file, this cannot be > 256
// because of the way tokens are generated.
//
//
// NOTE : If you change the index of an adapter, be sure the change it in
// Ne1000QueryCfgHandler() and Ne1000VerifyCfgHandler() as well!
//

static ADAPTER_INFO Adapters[] = {

    {
        1000,
        L"NE1000",
        L"IRQ 1 80 IRQTYPE 2 100 IOADDR 1 100 IOADDRLENGTH 2 100 ",
        NULL,
        500

    }

};

#else

//
// List of all the adapters supported in this file, this cannot be > 256
// because of the way tokens are generated.
//
//
// NOTE : If you change the index of an adapter, be sure the change it in
// Ne1000QueryCfgHandler() and Ne1000VerifyCfgHandler() as well!
//

static ADAPTER_INFO Adapters[] = {

    {
        1000,
        L"NE1000",
        L"IRQ\0001\00080\0IRQTYPE\0002\000100\0IOADDR\x001\x00100\0IOADDRLENGTH\x002\x00100\0",
        NULL,
        500

    }

};

#endif

//
// Structure for holding state of a search
//

typedef struct _SEARCH_STATE {

    ULONG IoBaseAddress;

} SEARCH_STATE, *PSEARCH_STATE;


//
// This is an array of search states.  We need one state for each type
// of adapter supported.
//

static SEARCH_STATE SearchStates[sizeof(Adapters) / sizeof(ADAPTER_INFO)] = {0};


//
// Structure for holding a particular adapter's complete information
//
typedef struct _NE1000_ADAPTER {

    LONG CardType;
    INTERFACE_TYPE InterfaceType;
    ULONG BusNumber;
    ULONG IoBaseAddress;

} NE1000_ADAPTER, *PNE1000_ADAPTER;


extern
LONG
Ne1000IdentifyHandler(
    IN LONG lIndex,
    IN WCHAR * pwchBuffer,
    IN LONG cwchBuffSize
    )

/*++

Routine Description:

    This routine returns information about the netcards supported by
    this file.

Arguments:

    lIndex -  The index of the netcard being address.  The first
    cards information is at index 1000, the second at 1100, etc.

    pwchBuffer - Buffer to store the result into.

    cwchBuffSize - Number of bytes in pwchBuffer

Return Value:

    0 if nothing went wrong, else the appropriate WINERROR.H value.

--*/


{
    LONG NumberOfAdapters;
    LONG Code = lIndex % 100;
    LONG Length;
    LONG i;

    NumberOfAdapters = sizeof(Adapters) / sizeof(ADAPTER_INFO);

#ifdef WORKAROUND

    if (Ne1000FirstTime) {

        Ne1000FirstTime = 0;

        for (i = 0; i < NumberOfAdapters; i++) {

            Length = UnicodeStrLen(Adapters[i].Parameters);

            for (; Length > 0; Length--) {

                if (Adapters[i].Parameters[Length] == L' ') {

                    Adapters[i].Parameters[Length] = UNICODE_NULL;

                }

            }

        }

    }
#endif

    lIndex = lIndex - Code;

    if (((lIndex / 100) - 10) < NumberOfAdapters) {

        for (i=0; i < NumberOfAdapters; i++) {

            if (Adapters[i].Index == lIndex) {

                switch (Code) {

                    case 0:

                        //
                        // Find the string length
                        //

                        Length = UnicodeStrLen(Adapters[i].InfId);

                        Length ++;

                        if (cwchBuffSize < Length) {

                            return(ERROR_INSUFFICIENT_BUFFER);

                        }

                        memcpy((PVOID)pwchBuffer, Adapters[i].InfId, Length * sizeof(WCHAR));
                        break;

                    case 3:

                        //
                        // Maximum value is 1000
                        //

                        if (cwchBuffSize < 5) {

                            return(ERROR_INSUFFICIENT_BUFFER);

                        }

                        wsprintf((PVOID)pwchBuffer, L"%d", Adapters[i].SearchOrder);

                        break;

                    default:

                        return(ERROR_INVALID_PARAMETER);

                }

                return(0);

            }

        }

        return(ERROR_INVALID_PARAMETER);

    }

    return(ERROR_NO_MORE_ITEMS);

}


extern
LONG Ne1000FirstNextHandler(
    IN  LONG lNetcardId,
    IN  INTERFACE_TYPE InterfaceType,
    IN  ULONG BusNumber,
    IN  BOOL fFirst,
    OUT PVOID *ppvToken,
    OUT LONG *lConfidence
    )

/*++

Routine Description:

    This routine finds the instances of a physical adapter identified
    by the NetcardId.

Arguments:

    lNetcardId -  The index of the netcard being address.  The first
    cards information is id 1000, the second id 1100, etc.

    InterfaceType - Either Isa, or Eisa.

    BusNumber - The bus number of the bus to search.

    fFirst - TRUE is we are to search for the first instance of an
    adapter, FALSE if we are to continue search from a previous stopping
    point.

    ppvToken - A pointer to a handle to return to identify the found
    instance

    lConfidence - A pointer to a long for storing the confidence factor
    that the card exists.

Return Value:

    0 if nothing went wrong, else the appropriate WINERROR.H value.

--*/

{

    if ((InterfaceType != Isa) &&
        (InterfaceType != Eisa)) {

        *lConfidence = 0;

        return(0);

    }

    if (lNetcardId != 1000) {

        *lConfidence = 0;
        return(ERROR_INVALID_PARAMETER);

    }

    //
    // If fFirst, reset search state
    //

    if (fFirst) {

        SearchStates[0].IoBaseAddress = 0x300;

    } else if (SearchStates[0].IoBaseAddress < 0x400) {

        SearchStates[0].IoBaseAddress = Ne1000NextIoAddr(SearchStates[0].IoBaseAddress);
    }

    while (SearchStates[0].IoBaseAddress < 0x400) {

        if (Ne1000CardAt(InterfaceType, BusNumber, SearchStates[0].IoBaseAddress)) {

            break;

        }

        SearchStates[0].IoBaseAddress = Ne1000NextIoAddr(SearchStates[0].IoBaseAddress);

    }

    if (SearchStates[0].IoBaseAddress == 0x400) {

        *lConfidence = 0;
        return(0);

    }

    //
    // In this module I use the token as follows: Remember that
    // the token can only be 2 bytes long (the low 2) because of
    // the interface to the upper part of this DLL.
    //
    //  The high bit of the short is boolean for ISA (else, EISA).
    //  The rest of the high byte is the the bus number.
    //  The low byte is the driver index number into Adapters.
    //
    // NOTE: This presumes that there are < 129 buses in the
    // system. Is this reasonable?
    //

    if (InterfaceType == Isa) {

        *ppvToken = (PVOID)0x8000;

    } else {

        *ppvToken = (PVOID)0x0;
    }

    *ppvToken = (PVOID)(((ULONG)*ppvToken) | ((BusNumber & 0x7F) << 8));

    *ppvToken = (PVOID)(((ULONG)*ppvToken) | 0);  // index

    *lConfidence = 100;
    return(0);
}

extern
LONG
Ne1000OpenHandleHandler(
    IN  PVOID pvToken,
    OUT PVOID *ppvHandle
    )

/*++

Routine Description:

    This routine takes a token returned by FirstNext and converts it
    into a permanent handle.

Arguments:

    Token - The token.

    ppvHandle - A pointer to the handle, so we can store the resulting
    handle.

Return Value:

    0 if nothing went wrong, else the appropriate WINERROR.H value.

--*/

{
    PNE1000_ADAPTER Handle;
    LONG AdapterNumber;
    ULONG BusNumber;
    INTERFACE_TYPE InterfaceType;

    //
    // Get info from the token
    //

    if (((ULONG)pvToken) & 0x8000) {

        InterfaceType = Isa;

    } else {

        InterfaceType = Eisa;

    }

    BusNumber = (ULONG)(((ULONG)pvToken >> 8) & 0x7F);

    AdapterNumber = ((ULONG)pvToken) & 0xFF;

    //
    // Store information
    //

    Handle = (PNE1000_ADAPTER)DetectAllocateHeap(
                                 sizeof(NE1000_ADAPTER)
                                 );

    if (Handle == NULL) {

        return(ERROR_NOT_ENOUGH_MEMORY);

    }

    //
    // Copy across address
    //

    Handle->IoBaseAddress = SearchStates[(ULONG)AdapterNumber].IoBaseAddress;
    Handle->CardType = Adapters[AdapterNumber].Index;
    Handle->InterfaceType = InterfaceType;
    Handle->BusNumber = BusNumber;

    *ppvHandle = (PVOID)Handle;

    return(0);
}

LONG
Ne1000CreateHandleHandler(
    IN  LONG lNetcardId,
    IN  INTERFACE_TYPE InterfaceType,
    IN  ULONG BusNumber,
    OUT PVOID *ppvHandle
    )

/*++

Routine Description:

    This routine is used to force the creation of a handle for cases
    where a card is not found via FirstNext, but the user says it does
    exist.

Arguments:

    lNetcardId - The id of the card to create the handle for.

    InterfaceType - Isa or Eisa.

    BusNumber - The bus number of the bus in the system.

    ppvHandle - A pointer to the handle, for storing the resulting handle.

Return Value:

    0 if nothing went wrong, else the appropriate WINERROR.H value.

--*/

{
    PNE1000_ADAPTER Handle;
    LONG NumberOfAdapters;
    LONG i;

    if ((InterfaceType != Isa) &&
        (InterfaceType != Eisa)) {

        return(ERROR_INVALID_PARAMETER);

    }

    NumberOfAdapters = sizeof(Adapters) / sizeof(ADAPTER_INFO);

    for (i=0; i < NumberOfAdapters; i++) {

        if (Adapters[i].Index == lNetcardId) {

            //
            // Store information
            //

            Handle = (PNE1000_ADAPTER)DetectAllocateHeap(
                                         sizeof(NE1000_ADAPTER)
                                         );

            if (Handle == NULL) {

                return(ERROR_NOT_ENOUGH_MEMORY);

            }

            //
            // Copy across memory address
            //

            Handle->IoBaseAddress = 0x300;
            Handle->CardType = lNetcardId;
            Handle->InterfaceType = InterfaceType;
            Handle->BusNumber = BusNumber;

            *ppvHandle = (PVOID)Handle;

            return(0);

        }

    }

    return(ERROR_INVALID_PARAMETER);
}

extern
LONG
Ne1000CloseHandleHandler(
    IN PVOID pvHandle
    )

/*++

Routine Description:

    This frees any resources associated with a handle.

Arguments:

   pvHandle - The handle.

Return Value:

    0 if nothing went wrong, else the appropriate WINERROR.H value.

--*/

{
    DetectFreeHeap(pvHandle);

    return(0);
}

LONG
Ne1000QueryCfgHandler(
    IN  PVOID pvHandle,
    OUT WCHAR *pwchBuffer,
    IN  LONG cwchBuffSize
    )

/*++

Routine Description:

    This routine calls the appropriate driver's query config handler to
    get the parameters for the adapter associated with the handle.

Arguments:

    pvHandle - The handle.

    pwchBuffer - The resulting parameter list.

    cwchBuffSize - Length of the given buffer in WCHARs.

Return Value:

    0 if nothing went wrong, else the appropriate WINERROR.H value.

--*/

{
    PNE1000_ADAPTER Adapter = (PNE1000_ADAPTER)(pvHandle);
    NTSTATUS NtStatus;
    UCHAR Interrupt = 0;
    ULONG RamAddr = 0;
    HANDLE TrapHandle;
    UCHAR InterruptList[13];
    UCHAR ResultList[13] = {0};
    LONG OutputLengthLeft = cwchBuffSize;
    LONG CopyLength;
    UCHAR Value;
    UCHAR i;

    ULONG StartPointer = (ULONG)pwchBuffer;

    if ((Adapter->InterfaceType != Isa) &&
        (Adapter->InterfaceType != Eisa)) {

        return(ERROR_INVALID_PARAMETER);

    }

    //
    // Verify the IoBaseAddress
    //

    if (!Ne1000CardAt(Adapter->InterfaceType,
                  Adapter->BusNumber,
                  Adapter->IoBaseAddress
                 )) {

        if (Adapter->IoBaseAddress < 0x400) {

            Adapter->IoBaseAddress = Ne1000NextIoAddr(Adapter->IoBaseAddress);

        }

        while (Adapter->IoBaseAddress < 0x400) {

            if (Ne1000CardAt(Adapter->InterfaceType,
                         Adapter->BusNumber,
                         Adapter->IoBaseAddress
                        )) {

                //
                // Found one!
                //

                break;

            }

            Adapter->IoBaseAddress = Ne1000NextIoAddr(Adapter->IoBaseAddress);

        }

        if (Adapter->IoBaseAddress == 0x400) {

            //
            // No card
            //

            if (cwchBuffSize < 2) {

                return(ERROR_INSUFFICIENT_BUFFER);

            }

            wsprintf(pwchBuffer, L"\0");

            return(0);

        }

    }


    //
    // Find the interrupt
    //


    InterruptList[0] = 2;
    InterruptList[1] = 3;
    InterruptList[2] = 4;
    InterruptList[3] = 5;
    InterruptList[4] = 6;
    InterruptList[5] = 7;
    InterruptList[6] = 8;
    InterruptList[7] = 10;
    InterruptList[8] = 11;
    InterruptList[9] = 12;
    InterruptList[10] = 13;
    InterruptList[11] = 14;
    InterruptList[12] = 15;

    //
    // Set the interrupt trap -- we are checking the interrupt number now
    //

    NtStatus = DetectSetInterruptTrap(
                       Adapter->InterfaceType,
                       Adapter->BusNumber,
                       &TrapHandle,
                       InterruptList,
                       13
                       );

    if (NtStatus == STATUS_SUCCESS) {

        Ne1000CardSlotTest(
               Adapter->InterfaceType,
               Adapter->BusNumber,
               Adapter->IoBaseAddress,
               &Value
               );

        //
        // CardSetup
        //

        Ne1000CardSetup(
                Adapter->InterfaceType,
                Adapter->BusNumber,
                Adapter->IoBaseAddress,
                &RamAddr,
                Value
                );

        //
        // Check for interrupt
        //

        DetectQueryInterruptTrap(
                           TrapHandle,
                           ResultList,
                           13
                           );

        //
        // Stop the chip
        //

        DetectReadPortUchar(
                      Adapter->InterfaceType,
                      Adapter->BusNumber,
                      Adapter->IoBaseAddress + 0x1F, // RESET
                      &Value
                      );

        DetectWritePortUchar(
                      Adapter->InterfaceType,
                      Adapter->BusNumber,
                      Adapter->IoBaseAddress + 0x1F, // RESET
                      0xFF
                      );


        DetectWritePortUchar(
                      Adapter->InterfaceType,
                      Adapter->BusNumber,
                      Adapter->IoBaseAddress, // COMMAND
                      0x21
                      );


        //
        // Remove interrupt trap
        //

        DetectRemoveInterruptTrap(
                           TrapHandle
                           );

        //
        // Search resulting buffer to find the right interrupt
        //

        for (i = 0; i < 13; i++) {

            if ((ResultList[i] == 1) || (ResultList[i] == 2)) {

                if (Interrupt != 0) {

                    //
                    // Uh-oh, looks like interrupts on two different IRQs.
                    //

                    Interrupt = 0;
                    goto BuildBuffer;

                }

                Interrupt = InterruptList[i];

            }

        }

    }

BuildBuffer:

    //
    // Build resulting buffer
    //

    //
    // Copy in the title string (IRQ)
    //

    if (Interrupt != 0) {

        CopyLength = UnicodeStrLen(IrqString) + 1;

        if (OutputLengthLeft < CopyLength) {

            return(ERROR_INSUFFICIENT_BUFFER);

        }

        RtlMoveMemory((PVOID)pwchBuffer,
                      (PVOID)IrqString,
                      (CopyLength * sizeof(WCHAR))
                     );

        pwchBuffer = &(pwchBuffer[CopyLength]);
        OutputLengthLeft -= CopyLength;

        //
        // Copy in the value
        //

        if (OutputLengthLeft < 3) {

            return(ERROR_INSUFFICIENT_BUFFER);

        }

        CopyLength = wsprintf(pwchBuffer,L"%d",Interrupt);

        if (CopyLength < 0) {

            return(ERROR_INSUFFICIENT_BUFFER);

        }

        CopyLength++;  // Add in the \0

        pwchBuffer = &(pwchBuffer[CopyLength]);
        OutputLengthLeft -= CopyLength;

        //
        // Copy in IRQTYPE
        //

        CopyLength = UnicodeStrLen(IrqTypeString) + 1;

        if (OutputLengthLeft < CopyLength) {

            return(ERROR_INSUFFICIENT_BUFFER);

        }

        RtlMoveMemory((PVOID)pwchBuffer,
                      (PVOID)IrqTypeString,
                      (CopyLength * sizeof(WCHAR))
                     );

        pwchBuffer = &(pwchBuffer[CopyLength]);
        OutputLengthLeft -= CopyLength;

        //
        // Copy in the value
        //

        if (OutputLengthLeft < 2) {

            return(ERROR_INSUFFICIENT_BUFFER);

        }

        //
        // LATCHED (0 == latched)
        //
        CopyLength = wsprintf(pwchBuffer,L"0");

        if (CopyLength < 0) {

            return(ERROR_INSUFFICIENT_BUFFER);

        }

        CopyLength++;  // Add in the \0

        pwchBuffer = &(pwchBuffer[CopyLength]);
        OutputLengthLeft -= CopyLength;

    }

    //
    // Now the IoBaseAddress
    //

    //
    // Copy in the title string
    //

    CopyLength = UnicodeStrLen(IoAddrString) + 1;

    if (OutputLengthLeft < CopyLength) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    RtlMoveMemory((PVOID)pwchBuffer,
                  (PVOID)IoAddrString,
                  (CopyLength * sizeof(WCHAR))
                 );

    pwchBuffer = &(pwchBuffer[CopyLength]);
    OutputLengthLeft -= CopyLength;

    //
    // Copy in the value
    //

    if (OutputLengthLeft < 6) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength = wsprintf(pwchBuffer,L"0x%x",Adapter->IoBaseAddress);

    if (CopyLength < 0) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength++;  // Add in the \0

    pwchBuffer = &(pwchBuffer[CopyLength]);
    OutputLengthLeft -= CopyLength;

    //
    // Copy in the title string
    //

    CopyLength = UnicodeStrLen(IoLengthString) + 1;

    if (OutputLengthLeft < CopyLength) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    RtlMoveMemory((PVOID)pwchBuffer,
                  (PVOID)IoLengthString,
                  (CopyLength * sizeof(WCHAR))
                 );

    pwchBuffer = &(pwchBuffer[CopyLength]);
    OutputLengthLeft -= CopyLength;

    //
    // Copy in the value
    //

    if (OutputLengthLeft < 5) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength = wsprintf(pwchBuffer,L"0x20");

    if (CopyLength < 0) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength++;  // Add in the \0

    pwchBuffer = &(pwchBuffer[CopyLength]);
    OutputLengthLeft -= CopyLength;


    //
    // Copy in final \0
    //

    if (OutputLengthLeft < 1) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength = (ULONG)pwchBuffer - StartPointer;
    ((PUCHAR)StartPointer)[CopyLength] = L'\0';

    return(0);
}

extern
LONG
Ne1000VerifyCfgHandler(
    IN PVOID pvHandle,
    IN WCHAR *pwchBuffer
    )

/*++

Routine Description:

    This routine verifys that a given parameter list is complete and
    correct for the adapter associated with the handle.

Arguments:

    pvHandle - The handle.

    pwchBuffer - The parameter list.

Return Value:

    0 if nothing went wrong, else the appropriate WINERROR.H value.

--*/

{
    PNE1000_ADAPTER Adapter = (PNE1000_ADAPTER)(pvHandle);
    ULONG IoBaseAddress;
    ULONG InterruptLong;
    ULONG RamAddr;
    WCHAR *Place;
    UCHAR Result;
    UCHAR PortValue;
    UCHAR Interrupt;
    HANDLE TrapHandle;
    NTSTATUS NtStatus;
    BOOLEAN Found;

    if ((Adapter->InterfaceType != Isa) &&
        (Adapter->InterfaceType != Eisa)) {

        return(ERROR_INVALID_DATA);

    }

    if (Adapter->CardType == 1000) {

        //
        // Parse out the parameter.
        //

        //
        // Get the IoBaseAddress
        //

        Place = FindParameterString(pwchBuffer, IoAddrString);

        if (Place == NULL) {

            return(ERROR_INVALID_DATA);

        }

        Place += UnicodeStrLen(IoAddrString) + 1;

        //
        // Now parse the thing.
        //

        ScanForNumber(Place, &IoBaseAddress, &Found);

        if (Found == FALSE) {

            return(ERROR_INVALID_DATA);

        }


        //
        // Get Interrupt number
        //

        Place = FindParameterString(pwchBuffer, IrqString);

        if (Place == NULL) {

            return(ERROR_INVALID_DATA);

        }

        Place += UnicodeStrLen(IrqString) + 1;

        //
        // Now parse the thing.
        //

        ScanForNumber(Place, &InterruptLong, &Found);

        Interrupt = (UCHAR)InterruptLong;

        if (Found == FALSE) {

            return(ERROR_INVALID_DATA);

        }

    } else {

        //
        // Error!
        //

        return(ERROR_INVALID_DATA);

    }

    //
    // Verify IoAddress
    //

    if (!Ne1000CardAt(Adapter->InterfaceType,
                  Adapter->BusNumber,
                  IoBaseAddress)) {

        return(ERROR_INVALID_DATA);

    }

    //
    // Set the interrupt trap -- we are checking the interrupt number now
    //

    NtStatus = DetectSetInterruptTrap(
                       Adapter->InterfaceType,
                       Adapter->BusNumber,
                       &TrapHandle,
                       &Interrupt,
                       1
                       );

    if (NtStatus == STATUS_SUCCESS) {

        //
        // Check that it was available
        //

        DetectQueryInterruptTrap(
                           TrapHandle,
                           &Result,
                           1
                           );

        if (Result == 3) {

            //
            // Remove interrupt trap
            //

            DetectRemoveInterruptTrap(
                           TrapHandle
                           );

            return(ERROR_INVALID_DATA);

        }


        Ne1000CardSlotTest(
               Adapter->InterfaceType,
               Adapter->BusNumber,
               IoBaseAddress,
               &Result
               );

        //
        // CardSetup
        //

        Ne1000CardSetup(
                Adapter->InterfaceType,
                Adapter->BusNumber,
                IoBaseAddress,
                &RamAddr,
                Result
                );

        //
        // Check for interrupt
        //

        DetectQueryInterruptTrap(
                           TrapHandle,
                           &Result,
                           1
                           );

        //
        // Stop the chip
        //

        DetectReadPortUchar(
                      Adapter->InterfaceType,
                      Adapter->BusNumber,
                      Adapter->IoBaseAddress + 0x1F, // RESET
                      &PortValue
                      );

        DetectWritePortUchar(
                      Adapter->InterfaceType,
                      Adapter->BusNumber,
                      Adapter->IoBaseAddress + 0x1F, // RESET
                      0xFF
                      );

        DetectWritePortUchar(
                      Adapter->InterfaceType,
                      Adapter->BusNumber,
                      IoBaseAddress, // COMMAND
                      0x21
                      );

        //
        // Remove interrupt trap
        //

        DetectRemoveInterruptTrap(
                           TrapHandle
                           );

        if ((Result != 1) && (Result != 2)) {

            return(ERROR_INVALID_DATA);

        }

    } else {

        return(ERROR_INVALID_DATA);

    }

    return(0);

}

extern
LONG Ne1000QueryMaskHandler(
    IN  LONG lNetcardId,
    OUT WCHAR *pwchBuffer,
    IN  LONG cwchBuffSize
    )

/*++

Routine Description:

    This routine returns the parameter list information for a specific
    network card.

Arguments:

    lNetcardId - The id of the desired netcard.

    pwchBuffer - The buffer for storing the parameter information.

    cwchBuffSize - Length of pwchBuffer in WCHARs.

Return Value:

    0 if nothing went wrong, else the appropriate WINERROR.H value.

--*/

{
    WCHAR *Result;
    LONG Length;
    LONG NumberOfAdapters;
    LONG i;

    //
    // Find the adapter
    //

    NumberOfAdapters = sizeof(Adapters) / sizeof(ADAPTER_INFO);

    for (i=0; i < NumberOfAdapters; i++) {

        if (Adapters[i].Index == lNetcardId) {

            Result = Adapters[i].Parameters;

            //
            // Find the string length (Ends with 2 NULLs)
            //

            for (Length=0; ; Length++) {

                if (Result[Length] == L'\0') {

                    ++Length;

                    if (Result[Length] == L'\0') {

                        break;

                    }

                }

            }

            Length++;

            if (cwchBuffSize < Length) {

                return(ERROR_NOT_ENOUGH_MEMORY);

            }

            memcpy((PVOID)pwchBuffer, Result, Length * sizeof(WCHAR));

            return(0);

        }

    }

    return(ERROR_INVALID_PARAMETER);

}

extern
LONG
Ne1000ParamRangeHandler(
    IN  LONG lNetcardId,
    IN  WCHAR *pwchParam,
    OUT LONG *plValues,
    OUT LONG *plBuffSize
    )

/*++

Routine Description:

    This routine returns a list of valid values for a given parameter name
    for a given card.

Arguments:

    lNetcardId - The Id of the card desired.

    pwchParam - A WCHAR string of the parameter name to query the values of.

    plValues - A pointer to a list of LONGs into which we store valid values
    for the parameter.

    plBuffSize - At entry, the length of plValues in LONGs.  At exit, the
    number of LONGs stored in plValues.

Return Value:

    0 if nothing went wrong, else the appropriate WINERROR.H value.

--*/

{

    //
    // Do we want the IoBaseAddress
    //

    if (memcmp(pwchParam, IoAddrString, (UnicodeStrLen(IoAddrString) + 1) * sizeof(WCHAR)) == 0) {

        //
        // Is there enough space
        //

        if (*plBuffSize < 4) {

            *plBuffSize = 0;
            return(ERROR_INSUFFICIENT_BUFFER);

        }

        plValues[0] = 0x300;
        plValues[1] = 0x320;
        plValues[2] = 0x340;
        plValues[3] = 0x360;
        *plBuffSize = 4;
        return(0);

    } else if (memcmp(pwchParam, IrqString, (UnicodeStrLen(IrqString) + 1) * sizeof(WCHAR)) == 0) {

        //
        // Is there enough space
        //

        if (*plBuffSize < 13) {

            *plBuffSize = 0;
            return(ERROR_INSUFFICIENT_BUFFER);

        }

        plValues[0] = 3;
        plValues[1] = 2;
        plValues[2] = 4;
        plValues[3] = 5;
        plValues[4] = 6;
        plValues[5] = 7;
        plValues[6] = 8;
        plValues[7] = 10;
        plValues[8] = 11;
        plValues[9] = 12;
        plValues[10] = 13;
        plValues[11] = 14;
        plValues[12] = 15;
        *plBuffSize = 13;
        return(0);

    }

    return(ERROR_INVALID_PARAMETER);

}

extern
LONG Ne1000QueryParameterNameHandler(
    IN  WCHAR *pwchParam,
    OUT WCHAR *pwchBuffer,
    IN  LONG cwchBufferSize
    )

/*++

Routine Description:

    Returns a localized, displayable name for a specific parameter.  All the
    parameters that this file uses are define by MS, so no strings are
    needed here.

Arguments:

    pwchParam - The parameter to be queried.

    pwchBuffer - The buffer to store the result into.

    cwchBufferSize - The length of pwchBuffer in WCHARs.

Return Value:

    ERROR_INVALID_PARAMETER -- To indicate that the MS supplied strings
    should be used.

--*/

{
    return(ERROR_INVALID_PARAMETER);
}

BOOLEAN
Ne1000CardAt(
    IN INTERFACE_TYPE InterfaceType,
    IN ULONG BusNumber,
    IN ULONG IoBaseAddress
    )

/*++

Routine Description:

    This routine checks for the instance of an Ne1000 card at the Io
    location given.

Arguments:

    InterfaceType - The type of bus, ISA or EISA.

    BusNumber - The bus number in the system.

    IoBaseAddress - The IO port address of the card.

Return Value:

    TRUE if a card is found, else FALSE.

--*/

{
    UCHAR Value;

    if (DetectCheckPortUsage(InterfaceType,
                             BusNumber,
                             IoBaseAddress,
                             0x20
                            ) != STATUS_SUCCESS) {

        return(FALSE);

    }

    if (!CheckFor8390(InterfaceType, BusNumber, IoBaseAddress)) {

        return(FALSE);

    }

    if (!Ne1000CheckForNovellAddress(InterfaceType, BusNumber, IoBaseAddress)) {

        //
        // Stop the 8390
        //

        DetectReadPortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0x1F, // RESET
                      &Value
                      );

        DetectWritePortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0x1F, // RESET
                      0xFF
                      );

        DetectWritePortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress, // COMMAND
                      0x21
                      );

        return(FALSE);

    }

    DetectReadPortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0x1F, // RESET
                      &Value
                      );

    DetectWritePortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0x1F, // RESET
                      0xFF
                      );

    DetectWritePortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress, // COMMAND
                      0x21
                      );

    return(TRUE);

}

BOOLEAN
Ne1000CheckForNovellAddress(
    IN INTERFACE_TYPE InterfaceType,
    IN ULONG BusNumber,
    IN ULONG IoBaseAddress
    )
/*++

Routine Description:

    This routine will determine if the network address of the card is
    (indeed) the Novell prefix (or compatible from another company).

Arguments:

    InterfaceType - The type of bus, ISA or EISA.

    BusNumber - The bus number in the system.

    IoBaseAddress - The IO port address of the card.

Return:

    TRUE if it is an SMC/WD identification, else FALSE.

--*/

{
    UCHAR Value;
    NTSTATUS NtStatus;
    ULONG RamAddr;
    BOOLEAN Result;

    if (!Ne1000CardSlotTest(
               InterfaceType,
               BusNumber,
               IoBaseAddress,
               &Result
               )) {

        return(FALSE);

    }

    Ne1000CardSetup(
                InterfaceType,
                BusNumber,
                IoBaseAddress,
                &RamAddr,
                Result
                );


    Result = TRUE;

    //
    // Read in the station address. (We have to read words -- 2 * 3 -- bytes)
    //

    NtStatus = DetectWritePortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0xA, // RBC0
                      6
                      );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0xB, // RBC1
                      0
                      );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0x8, // RADDR0
                      0
                      );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0x9, // RADDR1
                      0
                      );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress, // START | DMA_READ
                      0xA
                      );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }


    NtStatus = DetectReadPortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0x10, // RACK_NIC
                      &Value
                      );

    if (NtStatus != STATUS_SUCCESS) {

        Result = FALSE;

    }

    if (Value != 0x00) {

        Result = FALSE;

    }

    NtStatus = DetectReadPortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0x10, // RACK_NIC
                      &Value
                      );

    if (NtStatus != STATUS_SUCCESS) {

        Result = FALSE;

    }

    if (Value != 0x00) {

        Result = FALSE;

    }

    NtStatus = DetectReadPortUchar(
                      InterfaceType,
                      BusNumber,
                      IoBaseAddress + 0x10, // RACK_NIC
                      &Value
                      );

    if (NtStatus != STATUS_SUCCESS) {

        Result = FALSE;

    }

    if (Value == 0x1D) {

        //
        // Here we are checking for the IEEE address of Cabletron.  Turns out
        // that there are many many companies that make Ne1000 compatible
        // cards and checking for Novell only would be a small subset of
        // the market.   The only known card that detects just like an Ne1000
        // but is not an Ne1000 compatible card is the Cabletron card.
        //

        Result = FALSE;

    }

    return(Result);
}

VOID
Ne1000CardCopyDownBuffer(
    IN INTERFACE_TYPE InterfaceType,
    IN ULONG BusNumber,
    IN ULONG IoBaseAddress,
    IN ULONG MemoryBaseAddress,
    IN PUCHAR Buffer,
    IN ULONG Length
    )
/*++

Routine Description:

    This routine will copy down a buffer to the address given on the card.

Arguments:

    InterfaceType - The type of bus, ISA or EISA.

    BusNumber - The bus number in the system.

    IoBaseAddress - The IO port address of the card.

    MemoryBaseAddress - The destination of the buffer, if in memory mapped mode,
       else it is the value to be stored in the Gate Array Control Register
       when doing Programmed I/O.

    Buffer - The buffer to copy

    Length - Number of bytes to copy to the card.

Return:

    none.

--*/
{
    NTSTATUS NtStatus;
    UCHAR Tmp;
    USHORT OldAddr, NewAddr, Count;
    PUCHAR ReadBuffer;

    //
    // Do Write errata as described on pages 1-143 and 1-144 of the 1992
    // LAN databook
    //

    //
    // Set Count and destination address
    //

    ReadBuffer = ((PUCHAR)(MemoryBaseAddress + (MemoryBaseAddress & 1)));

    OldAddr = NewAddr = (USHORT)(ReadBuffer);

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x00 // PAGE0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x8, // NIC_RMT_ADDR_LSB
                                    (UCHAR)ReadBuffer
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x9, // NIC_RMT_ADDR_MSB
                                    (UCHAR)((ULONG)ReadBuffer >> 8)
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xA, //NIC_RMT_COUNT_LSB
                                    0x2
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xB, // NIC_RMT_COUNT_MSB
                                    0x0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    //
    // Set direction (Read)
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x2A // CR_START | CR_PAGE0 | CR_DMA_READ
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    //
    // Read from port
    //

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 0x10, // RACK_NIC
                                   &Tmp
                                  );


    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 0x10, // NIC_RACK_NIC
                                   &Tmp
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    //
    // Wait for addr to change
    //

    Count = 0xFF;

    while (Count != 0) {

        NtStatus = DetectReadPortUchar(InterfaceType,
                                       BusNumber,
                                       IoBaseAddress + 0x1, // NIC_CRDA_LSB
                                       &Tmp
                                      );

        if (NtStatus != STATUS_SUCCESS) {

            return;

        }

        NewAddr = Tmp;

        NtStatus = DetectReadPortUchar(InterfaceType,
                                       BusNumber,
                                       IoBaseAddress +  0x2, // NIC_CRDA_MSB
                                       &Tmp
                                      );

        if (NtStatus != STATUS_SUCCESS) {

            return;

        }

        NewAddr |= (Tmp << 8);

        if (NewAddr != OldAddr) {

            break;
        }

        Count--;

    }

    if (NewAddr == OldAddr) {

        return;

    }


    //
    // Now we can do write
    //

    //
    // Set Count and destination address
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x0 // PAGE0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x8, // NIC_RMT_ADDR_LSB
                                    (UCHAR)MemoryBaseAddress
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x9, // NIC_RMT_ADDR_MSB
                                    (UCHAR)(MemoryBaseAddress >> 8)
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xA, // NIC_RMT_COUNT_LSB
                                    (UCHAR)Length
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xB, // NIC_RMT_COUNT_MSB
                                    (UCHAR)(Length >> 8)
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    //
    // Set direction (Write)
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x32 // CR_START | CR_PAGE0 | CR_DMA_WRITE
                                   );
    if (NtStatus != STATUS_SUCCESS) {

        return;

    }

    //
    // Repeatedly write to out port
    //

    for (; Length > 0; Length--) {

        NtStatus = DetectWritePortUchar(InterfaceType,
                                        BusNumber,
                                        IoBaseAddress + 0x10, // NIC_RACK_NIC
                                        *Buffer
                                       );

        if (NtStatus != STATUS_SUCCESS) {

            return;

        }

        Buffer++;

    }

    //
    // Wait for DMA to complete
    //

    Count = 10;

    while (Count) {

        NtStatus = DetectReadPortUchar(InterfaceType,
                                       BusNumber,
                                       IoBaseAddress + 0x7, // NIC_INTR_STATUS
                                       &Tmp
                                      );

        if (NtStatus != STATUS_SUCCESS) {

            return;

        }

        if (Tmp & 0x40) { // ISR_DMA_DONE

            break;

        } else {

            Count--;

        }

    }

    return;

}

VOID
Ne1000CardSetup(
    IN  INTERFACE_TYPE InterfaceType,
    IN  ULONG BusNumber,
    IN  ULONG IoBaseAddress,
    OUT PULONG MemoryBaseAddress,
    IN  BOOLEAN EightBitSlot
    )

/*++

Routine Description:

    Sets up the card, using the sequence given in the Etherlink II
    technical reference.

Arguments:

    InterfaceType - The type of bus, ISA or EISA.

    BusNumber - The bus number in the system.

    IoBaseAddress - The IO port address of the card.

    MemoryBaseAddress - Pointer to store the base address of card memory.

Return Value:

    None.

--*/

{
    UINT i;
    UCHAR Tmp;
    NTSTATUS NtStatus;
    LARGE_INTEGER Delay;

    //
    // Stop the card.
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x21 // STOP | ABORT_DMA
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Initialize the Data Configuration register.
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xE, // NIC_DATA_CONFIG
                                    0x50 // DCR_AUTO_INIT | DCR_FIFO_8_BYTE
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Set Xmit start location
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x4, // NIC_XMIT_START
                                    0xA0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Set Xmit configuration
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xD, // NIC_XMIT_CONFIG
                                    0x0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Set Receive configuration
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xC, // NIC_RCV_CONFIG
                                    0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Set Receive start
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x1, // NIC_PAGE_START
                                    0x4
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Set Receive end
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x2, // NIC_PAGE_STOP
                                    0xFF
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Set Receive boundary
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x3, // NIC_BOUNDARY
                                    0x4
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Set Xmit bytes
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x5, // NIC_XMIT_COUNT_LSB
                                    0x3C
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x6, //  NIC_XMIT_COUNT_MSB
                                    0x0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Pause
    //

    //
    // Wait for reset to complete. (100 ms)
    //

    Delay.LowPart = 100000;
    Delay.HighPart = 0;

    NtDelayExecution(
        FALSE,
        &Delay
        );

    //
    // Ack all interrupts that we might have produced
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x7, // NIC_INTR_STATUS
                                    0xFF
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Change to page 1
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x61 // CR_PAGE1 | CR_STOP
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Set current
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x7, // NIC_CURRENT
                                    0x4
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Back to page 0
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x21 // CR_PAGE0 | CR_STOP
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Pause
    //

    Delay.LowPart = 1000;
    Delay.HighPart = 0;

    NtDelayExecution(
        FALSE,
        &Delay
        );


    //
    // Do initialization errata
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xA, // NIC_RMT_COUNT_LSB
                                    55
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Reset the chip
    //

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 0x1F, // NIC_RESET
                                   &Tmp
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x1F, // NIC_RESET
                                    0xFF
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Start the chip
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x22
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Mask Interrupts
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xF, // NIC_INTR_MASK
                                    0xFF
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    if (EightBitSlot) {

        NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xE, // NIC_DATA_CONFIG
                                    0x48 // DCR_FIFO_8_BYTE | DCR_NORMAL | DCR_BYTE_WIDE
                                   );

    } else {

        NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xE, // NIC_DATA_CONFIG
                                    0x49 // DCR_FIFO_8_BYTE | DCR_NORMAL | DCR_WORD_WIDE
                                   );

    }

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xD, // NIC_XMIT_CONFIG
                                    0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xC, // NIC_RCV_CONFIG
                                    0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x7, // NIC_INTR_STATUS
                                    0xFF
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x21 // CR_NO_DMA | CR_STOP
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xA, // NIC_RMT_COUNT_LSB
                                    0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xB, // NIC_RMT_COUNT_MSB
                                    0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // Wait for STOP to complete
    //

    i = 0xFF;

    while (--i) {

        NtStatus = DetectReadPortUchar(InterfaceType,
                                       BusNumber,
                                       IoBaseAddress + 0x7, // NIC_INTR_STATUS
                                       &Tmp
                                      );

        if (NtStatus != STATUS_SUCCESS) {

            *MemoryBaseAddress = 0;
            return;

        }

        if (Tmp & 0x80) { // ISR_RESET

            break;

        }

    }

    //
    // Put card in loopback mode
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xD, // NIC_XMIT_CONFIG
                                    0x2 // TCR_LOOPBACK
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x22 // CR_NO_DMA | CR_START
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        *MemoryBaseAddress = 0;
        return;

    }

    //
    // ... but it is still in loopback mode.
    //

    return;
}

ULONG
Ne1000NextIoAddr(
    IN ULONG IoBaseAddress
    )

/*++

Routine Description:

    Gets the next IoBaseAddress.

Arguments:

    IoBaseAddress - The IO port address of the card.

Return Value:

    BaseAddress

--*/

{

    IoBaseAddress += 0x20;

    if (IoBaseAddress > 0x380) {

        IoBaseAddress = 0x400;

    }

    return(IoBaseAddress);

}

BOOLEAN
Ne1000CardSlotTest(
    IN  INTERFACE_TYPE InterfaceType,
    IN  ULONG BusNumber,
    IN  ULONG IoBaseAddress,
    OUT PBOOLEAN EightBitSlot
    )

/*++

Routine Description:

    Checks if the card is in an 8 or 16 bit slot and sets a flag in the
    adapter structure.

Arguments:


    InterfaceType - The type of bus, ISA or EISA.

    BusNumber - The bus number in the system.

    IoBaseAddress - The IO port address of the card.

    EightBitSlot - Result of test.

Return Value:

    TRUE, if all goes well, else FALSE.

--*/

{
    UCHAR Tmp;
    UCHAR RomCopy[32];
    UCHAR i;
    NTSTATUS NtStatus;
    LARGE_INTEGER Delay;

    //
    // Reset the chip
    //

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 0x1F, // NIC_RESET
                                   &Tmp
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x1F, // NIC_RESET
                                    0xFF
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    //
    // Go to page 0 and stop
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x21 // CR_STOP | CR_NO_DMA
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    //
    // Pause
    //

    Delay.LowPart = 1000;
    Delay.HighPart = 0;

    NtDelayExecution(
        FALSE,
        &Delay
        );

    //
    // Setup to read from ROM
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xE, // NIC_DATA_CONFIG
                                    0x48 // DCR_BYTE_WIDE | DCR_FIFO_8_BYTE | DCR_NORMAL
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xF, // NIC_INTR_MASK
                                    0x0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    //
    // Ack any interrupts that may be hanging around
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x7, // NIC_INTR_STATUS
                                    0xFF
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x8, // NIC_RMT_ADDR_LSB
                                    0x0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x9, // NIC_RMT_ADDR_MSB,
                                    0x0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xA, // NIC_RMT_COUNT_LSB
                                    32
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0xB, // NIC_RMT_COUNT_MSB
                                    0x0
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0xA // CR_DMA_READ | CR_START
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    //
    // Read first 32 bytes in 16 bit mode
    //

    for (i = 0; i < 32; i++) {

        NtStatus = DetectReadPortUchar(InterfaceType,
                                       BusNumber,
                                       IoBaseAddress + 0x10, // NIC_RACK_NIC
                                       RomCopy + i
                                      );

        if (NtStatus != STATUS_SUCCESS) {

            return(FALSE);

        }

    }

    //
    // Reset the chip
    //

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 0x1F, // NIC_RESET
                                   &Tmp
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    NtStatus = DetectWritePortUchar(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress + 0x1F, // NIC_RESET
                                    0xFF
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    //
    // Check address being singular
    //

    for (i = 6; i < 31; i++) {

        if ((RomCopy[i] != 'B') && (RomCopy[i+1] == 'B')) {

            //
            // Now check that the address is singular.  On an Ne1000 the
            // ethernet address is store in offsets 0 thru 5.  On the Ne2000
            // the address is stored in offsets 0 thru 11, where each byte
            // is duplicated.
            //
            // Here we only check the first two bytes because some of the
            // other bytes may be accidentally equal.
            //

            if ((RomCopy[0] != RomCopy[1]) ||
                (RomCopy[2] != RomCopy[3]) ||
                (RomCopy[4] != RomCopy[5])) {

                return(TRUE);

            }

            return(FALSE);

        }

    }

    //
    // If no 'B' found, then not an NE1000.
    //

    return(FALSE);
}
