/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    detelk16.c

Abstract:

    This is the main file for the autodetection DLL for all the elnk16.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
//

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

ULONG
Elnk16NextIoBaseAddress(
    IN ULONG IoBaseAddress
    );


VOID
Elnk16GenerateIdPattern(
    IN INTERFACE_TYPE InterfaceType,
    IN ULONG BusNumber
    );

UCHAR Elnk16FirstTime = 1;

#ifdef WORKAROUND

//
// 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
// Elnk16QueryCfgHandler() and Elnk16VerifyCfgHandler() as well!
//

static ADAPTER_INFO Adapters[] = {

    {
        1000,
        L"ELNK16",
        L"IRQ 0 100 IRQTYPE 2 100 IOADDR 0 100 IOADDRLENGTH 2 100 MEMADDR 0 100 MEMADDRLENGTH 0 100 TRANSCEIVER 0 0 ZEROWAITSTATE 0 0 ",
        NULL,
        200

    }

};

#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
// Elnk16QueryCfgHandler() and Elnk16VerifyCfgHandler() as well!
//

static ADAPTER_INFO Adapters[] = {

    {
        1000,
        L"ELNK16",
        L"IRQ\0000\000100\000IRQTYPE\0002\000100\0IOADDR\0000\000100\000IOADDRLENGTH\0002\000100\0MEMADDR\0000\000100\0MEMADDRLENGTH\0000\000100\0TRANSCEIVER\0000\0000\0ZEROWAITSTATE\0000\0000\0",
        NULL,
        200

    }

};

#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 _ELNK16_ADAPTER {

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

} ELNK16_ADAPTER, *PELNK16_ADAPTER;






extern
LONG
Elnk16IdentifyHandler(
    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 (Elnk16FirstTime) {

        Elnk16FirstTime = 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
Elnk16FirstNextHandler(
    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.

    NOTE: That for now there is only one card supported by this file,
    so we ignore the specific FirstNext handler in Adapters to save
    some processing time.

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.

--*/

{
    NTSTATUS NtStatus;

    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 = 0x200;

    } else {

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

    //
    // Find an adapter
    //

    //
    // Put all cards in RUN state.
    //


    NtStatus = DetectWritePortUchar(InterfaceType,
                                   BusNumber,
                                   0x100,
                                   0x00
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        *lConfidence = 0;

        return(0);

    }

    Elnk16GenerateIdPattern(InterfaceType, BusNumber);

    NtStatus = DetectWritePortUchar(InterfaceType,
                                   BusNumber,
                                   0x100,
                                   0x00
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        *lConfidence = 0;

        return(0);

    }

    while (SearchStates[0].IoBaseAddress != 0x3F0) {

        if (Elnk16CardAt(InterfaceType, BusNumber, SearchStates[0].IoBaseAddress)) {
            break;
        }

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

    }

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

        *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
Elnk16OpenHandleHandler(
    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.

--*/

{
    PELNK16_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 = (PELNK16_ADAPTER)DetectAllocateHeap(
                                 sizeof(ELNK16_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);
}

extern
LONG
Elnk16CreateHandleHandler(
    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.

--*/

{
    PELNK16_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 = (PELNK16_ADAPTER)DetectAllocateHeap(
                                         sizeof(ELNK16_ADAPTER)
                                         );

            if (Handle == NULL) {

                return(ERROR_NOT_ENOUGH_MEMORY);

            }

            //
            // Copy across memory address
            //

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

            *ppvHandle = (PVOID)Handle;

            return(0);

        }

    }

    return(ERROR_INVALID_PARAMETER);
}

extern
LONG
Elnk16CloseHandleHandler(
    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
Elnk16QueryCfgHandler(
    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.

--*/

{
    PELNK16_ADAPTER Adapter = (PELNK16_ADAPTER)(pvHandle);
    NTSTATUS NtStatus;
    ULONG MemAddress;
    ULONG MemLength;
    UCHAR InterruptNumber;
    UCHAR TmpValue;
    LONG OutputLengthLeft = cwchBuffSize;
    LONG CopyLength;
    UCHAR Transceiver;
    UCHAR ZeroWaitState;

    ULONG StartPointer = (ULONG)pwchBuffer;


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

        return(ERROR_INVALID_PARAMETER);

    }

    //
    // Verify the IoBaseAddress
    //

    //
    // Put all cards in RUN state.
    //


    NtStatus = DetectWritePortUchar(Adapter->InterfaceType,
                                   Adapter->BusNumber,
                                   0x100,
                                   0x00
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    Elnk16GenerateIdPattern(Adapter->InterfaceType, Adapter->BusNumber);

    NtStatus = DetectWritePortUchar(Adapter->InterfaceType,
                                   Adapter->BusNumber,
                                   0x100,
                                   0x00
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    while (Adapter->IoBaseAddress != 0x3F0) {

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

            break;
        }

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

    }

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

        if (cwchBuffSize < 2) {

            return(ERROR_INSUFFICIENT_BUFFER);

        }

        wsprintf(pwchBuffer, L"\0");

        return(FALSE);

    }

    //
    // Now get the Transceiver
    //

    //
    // Reach the ROM configuration register
    //

    NtStatus = DetectReadPortUchar(Adapter->InterfaceType,
                                   Adapter->BusNumber,
                                   Adapter->IoBaseAddress + 0xD,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        if (cwchBuffSize < 2) {

            return(ERROR_INSUFFICIENT_BUFFER);

        }

        wsprintf(pwchBuffer, L"\0");
        return(0);

    }

    //
    // Interpret
    //

    if (TmpValue & 0x80) {

        Transceiver = 2;

    } else {

        Transceiver = 1;

    }

    //
    // Now get the MemoryMappedBaseAddress
    //

    //
    // Read the RAM configuration register
    //

    NtStatus = DetectReadPortUchar(Adapter->InterfaceType,
                                   Adapter->BusNumber,
                                   Adapter->IoBaseAddress + 0xE,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        if (cwchBuffSize < 2) {

            return(ERROR_INSUFFICIENT_BUFFER);

        }

        wsprintf(pwchBuffer, L"\0");
        return(0);

    }

    //
    // Interpret
    //

    switch (TmpValue & 0x3C) {

        case 0x00:

            MemAddress = 0xC0000;
            break;

        case 0x08:

            MemAddress = 0xC8000;
            break;

        case 0x10:

            MemAddress = 0xD0000;
            break;

        case 0x18:

            MemAddress = 0xD8000;
            break;

        case 0x30:

            switch (TmpValue & 0x03) {

                case 0x00 :

                    MemAddress = 0xF0000;
                    break;

                case 0x01 :

                    MemAddress = 0xF2000;
                    break;

                case 0x02 :

                    MemAddress = 0xF4000;
                    break;

                case 0x03 :

                    MemAddress = 0xF6000;
                    break;

            }

            break;

        case 0x38:

            MemAddress = 0xF8000;
            break;

        default:

            MemAddress = 0;

    }

    switch (TmpValue & 0x3) {

        case 0x00 :

            MemLength = 0x4000;
            break;

        case 0x01 :

            MemLength = 0x8000;
            break;

        case 0x02 :

            MemLength = 0xC000;
            break;

        case 0x03 :

            MemLength = 0x10000;
            break;

    }

    //
    // Zero Wait State
    //

    if (TmpValue & 0x80) {

        ZeroWaitState = 1;

    } else {

        ZeroWaitState = 0;

    }

    //
    // Now get the IRQ
    //

    //
    // Read the Interrupt Config register
    //

    NtStatus = DetectReadPortUchar(Adapter->InterfaceType,
                                   Adapter->BusNumber,
                                   Adapter->IoBaseAddress + 0xF,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        if (cwchBuffSize < 2) {

            return(ERROR_INSUFFICIENT_BUFFER);

        }

        wsprintf(pwchBuffer, L"\0");
        return(0);

    }

    InterruptNumber = TmpValue & 0xF;

    //
    // Build resulting buffer
    //

    //
    // First put in memory addr
    //

    if (MemAddress == 0) {

        goto SkipMemory;

    }

    //
    // Copy in the title string
    //

    CopyLength = UnicodeStrLen(MemAddrString) + 1;

    if (OutputLengthLeft < CopyLength) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

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

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

    //
    // Copy in the value
    //

    if (OutputLengthLeft < 8) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength = wsprintf(pwchBuffer,L"0x%x",(ULONG)(MemAddress));

    if (CopyLength < 0) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength++;  // Add in the \0

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

    //
    // Now the amount of memory
    //

    //
    // Copy in the title string
    //

    CopyLength = UnicodeStrLen(MemLengthString) + 1;

    if (OutputLengthLeft < CopyLength) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

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

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

    //
    // Copy in the value
    //

    if (OutputLengthLeft < 8) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength = wsprintf(pwchBuffer,L"0x%x", MemLength);

    if (CopyLength < 0) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength++;  // Add in the \0

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

SkipMemory:

    //
    // 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;

    //
    // Now the IoAddressLength
    //

    //
    // 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"0x10");

    if (CopyLength < 0) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength++;  // Add in the \0

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

    //
    // Now the interrupt number
    //

    //
    // Copy in the title string
    //

    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",InterruptNumber);

    if (CopyLength < 0) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength++;  // Add in the \0

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


    //
    // Copy in the title string (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);

    }

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

    if (CopyLength < 0) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength++;  // Add in the \0

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

    //
    // Now the ZeroWaitState
    //

    //
    // Copy in the title string
    //

    CopyLength = UnicodeStrLen(ZeroWaitStateString) + 1;

    if (OutputLengthLeft < CopyLength) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

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

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

    //
    // Copy in the value
    //

    if (OutputLengthLeft < 2) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

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

    if (CopyLength < 0) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

    CopyLength++;  // Add in the \0

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

    //
    // Now the transceiver
    //

    //
    // Copy in the title string
    //

    CopyLength = UnicodeStrLen(TransceiverString) + 1;

    if (OutputLengthLeft < CopyLength) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

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

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

    //
    // Copy in the value
    //

    if (OutputLengthLeft < 2) {

        return(ERROR_INSUFFICIENT_BUFFER);

    }

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

    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
Elnk16VerifyCfgHandler(
    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.

--*/

{
    PELNK16_ADAPTER Adapter = (PELNK16_ADAPTER)(pvHandle);
    NTSTATUS NtStatus;
    ULONG IoBaseAddress;
    ULONG MemAddress;
    ULONG MemLength;
    ULONG Transceiver;
    ULONG Interrupt;
    ULONG ZeroWaitState;
    UCHAR TmpValue;
    WCHAR *Place;
    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 the Interrupt
        //

        Place = FindParameterString(pwchBuffer, IrqString);

        if (Place == NULL) {

            return(ERROR_INVALID_DATA);

        }

        Place += UnicodeStrLen(IrqString) + 1;

        //
        // Now parse the thing.
        //

        ScanForNumber(Place, &Interrupt, &Found);

        if (Found == FALSE) {

            return(ERROR_INVALID_DATA);

        }

        //
        // Get the MemAddress
        //

        Place = FindParameterString(pwchBuffer, MemAddrString);

        if (Place == NULL) {

            return(ERROR_INVALID_DATA);

        }

        Place += UnicodeStrLen(MemAddrString) + 1;

        //
        // Now parse the thing.
        //

        ScanForNumber(Place, &MemAddress, &Found);

        if (Found == FALSE) {

            return(ERROR_INVALID_DATA);

        }

        //
        // Get the MemLength
        //

        Place = FindParameterString(pwchBuffer, MemLengthString);

        if (Place == NULL) {

            return(ERROR_INVALID_DATA);

        }

        Place += UnicodeStrLen(MemLengthString) + 1;

        //
        // Now parse the thing.
        //

        ScanForNumber(Place, &MemLength, &Found);

        if (Found == FALSE) {

            return(ERROR_INVALID_DATA);

        }

        //
        // Get the Transceiver
        //

        Place = FindParameterString(pwchBuffer, TransceiverString);

        if (Place == NULL) {

            return(ERROR_INVALID_DATA);

        }

        Place += UnicodeStrLen(TransceiverString) + 1;

        //
        // Now parse the thing.
        //

        ScanForNumber(Place, &Transceiver, &Found);

        if (Found == FALSE) {

            return(ERROR_INVALID_DATA);

        }

        //
        // Get the ZeroWaitState
        //

        Place = FindParameterString(pwchBuffer, ZeroWaitStateString);

        if (Place == NULL) {

            return(ERROR_INVALID_DATA);

        }

        Place += UnicodeStrLen(ZeroWaitStateString) + 1;

        //
        // Now parse the thing.
        //

        ScanForNumber(Place, &ZeroWaitState, &Found);

        if (Found == FALSE) {

            return(ERROR_INVALID_DATA);

        }

    } else {

        //
        // Error!
        //

        return(ERROR_INVALID_DATA);

    }

    //
    // Put all cards in RUN state.
    //


    NtStatus = DetectWritePortUchar(Adapter->InterfaceType,
                                   Adapter->BusNumber,
                                   0x100,
                                   0x00
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    Elnk16GenerateIdPattern(Adapter->InterfaceType, Adapter->BusNumber);

    NtStatus = DetectWritePortUchar(Adapter->InterfaceType,
                                   Adapter->BusNumber,
                                   0x100,
                                   0x00
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    //
    // Verify IoAddress
    //

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

        return(ERROR_INVALID_DATA);

    }


    //
    // Verify rest
    //


    //
    // Reach the ROM configuration register
    //

    NtStatus = DetectReadPortUchar(Adapter->InterfaceType,
                                   Adapter->BusNumber,
                                   IoBaseAddress + 0xD,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(ERROR_INVALID_DATA);

    }

    //
    // Interpret
    //

    if (TmpValue & 0x80) {

        if (Transceiver != 2) {

            goto BadConfig;

        }

    } else {

        if (Transceiver != 1) {

            goto BadConfig;

        }

    }

    //
    // Now get the MemoryMappedBaseAddress
    //

    //
    // Read the RAM configuration register
    //

    NtStatus = DetectReadPortUchar(Adapter->InterfaceType,
                                   Adapter->BusNumber,
                                   Adapter->IoBaseAddress + 0xE,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        goto BadConfig;

    }

    //
    // Interpret
    //

    switch (TmpValue & 0x3C) {

        case 0x00:

            if (MemAddress != 0xC0000) {

                goto BadConfig;

            }

            break;

        case 0x08:

            if (MemAddress != 0xC8000) {

                goto BadConfig;

            }
            break;

        case 0x10:

            if (MemAddress != 0xD0000) {

                goto BadConfig;

            }
            break;

        case 0x18:

            if (MemAddress != 0xD8000) {

                goto BadConfig;

            }
            break;

        case 0x30:

            switch (TmpValue & 0x03) {

                case 0x00 :

                    if (MemAddress != 0xF0000) {

                        goto BadConfig;

                    }
                    break;

                case 0x01 :

                    if (MemAddress != 0xF2000) {

                        goto BadConfig;

                    }
                    break;

                case 0x02 :

                    if (MemAddress != 0xF4000) {

                        goto BadConfig;

                    }
                    break;

                case 0x03 :

                    if (MemAddress != 0xF6000) {

                        goto BadConfig;

                    }
                    break;

            }

            break;

        case 0x38:

            if (MemAddress != 0xF8000) {

                goto BadConfig;

            }

            break;

        default:

            goto BadConfig;

    }

    switch (TmpValue & 0x3) {

        case 0x00 :

            if (MemLength != 0x4000) {

                goto BadConfig;

            }
            break;

        case 0x01 :

            if (MemLength != 0x8000) {

                goto BadConfig;

            }
            break;

        case 0x02 :

            if (MemLength != 0xC000) {

                goto BadConfig;

            }
            break;

        case 0x03 :

            if (MemLength != 0x10000) {

                goto BadConfig;

            }
            break;

    }

    //
    // Zero Wait State
    //

    if (TmpValue & 0x80) {

        if (ZeroWaitState != 1) {

            goto BadConfig;

        }

    } else {

        if (ZeroWaitState != 0) {

            goto BadConfig;

        }

    }

    //
    // Now get the IRQ
    //

    //
    // Read the Interrupt Config register
    //

    NtStatus = DetectReadPortUchar(Adapter->InterfaceType,
                                   Adapter->BusNumber,
                                   Adapter->IoBaseAddress + 0xF,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        goto BadConfig;

    }

    if (Interrupt != (ULONG)(TmpValue & 0xF)) {

        goto BadConfig;

    }


    return(0);

BadConfig:

    return(ERROR_INVALID_DATA);

}

extern
LONG
Elnk16QueryMaskHandler(
    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
Elnk16ParamRangeHandler(
    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 < 25) {

            *plBuffSize = 0;
            return(ERROR_INSUFFICIENT_BUFFER);

        }

        plValues[0] = 0x300;
        plValues[1] = 0x210;
        plValues[2] = 0x220;
        plValues[3] = 0x230;
        plValues[4] = 0x240;
        plValues[5] = 0x250;
        plValues[6] = 0x260;
        plValues[7] = 0x280;
        plValues[8] = 0x2A0;
        plValues[9] = 0x2B0;
        plValues[10] = 0x2C0;
        plValues[11] = 0x2D0;
        plValues[12] = 0x2E0;
        plValues[13] = 0x200;
        plValues[14] = 0x310;
        plValues[15] = 0x320;
        plValues[16] = 0x330;
        plValues[17] = 0x340;
        plValues[18] = 0x350;
        plValues[19] = 0x360;
        plValues[20] = 0x370;
        plValues[21] = 0x380;
        plValues[22] = 0x390;
        plValues[23] = 0x3A0;
        plValues[24] = 0x3E0;
        *plBuffSize = 25;
        return(0);

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

        //
        // Is there enough space
        //

        if (*plBuffSize < 7) {

            *plBuffSize = 0;
            return(ERROR_INSUFFICIENT_BUFFER);

        }

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

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

        //
        // Is there enough space
        //

        if (*plBuffSize < 9) {

            *plBuffSize = 0;
            return(ERROR_INSUFFICIENT_BUFFER);

        }

        plValues[0] = 0xD0000;
        plValues[1] = 0xC8000;
        plValues[2] = 0xC0000;
        plValues[3] = 0xD8000;
        plValues[4] = 0xF0000;
        plValues[5] = 0xF2000;
        plValues[6] = 0xF4000;
        plValues[7] = 0xF6000;
        plValues[8] = 0xF8000;
        *plBuffSize = 9;
        return(0);

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

        //
        // Is there enough space
        //

        if (*plBuffSize < 4) {

            *plBuffSize = 0;
            return(ERROR_INSUFFICIENT_BUFFER);

        }

        plValues[0] = 0x4000;
        plValues[1] = 0x8000;
        plValues[2] = 0xC000;
        plValues[3] = 0x10000;
        *plBuffSize = 4;
        return(0);

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

        //
        // Is there enough space
        //

        if (*plBuffSize < 2) {

            *plBuffSize = 0;
            return(ERROR_INSUFFICIENT_BUFFER);

        }

        plValues[0] = 0x2;
        plValues[1] = 0x1;
        *plBuffSize = 2;
        return(0);

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

        //
        // Is there enough space
        //

        if (*plBuffSize < 2) {

            *plBuffSize = 0;
            return(ERROR_INSUFFICIENT_BUFFER);

        }

        plValues[0] = 0x0;
        plValues[1] = 0x1;
        *plBuffSize = 2;
        return(0);

    }

    return(ERROR_INVALID_PARAMETER);

}

extern
LONG
Elnk16QueryParameterNameHandler(
    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
Elnk16CardAt(
    IN INTERFACE_TYPE InterfaceType,
    IN ULONG BusNumber,
    IN ULONG IoBaseAddress
    )

/*++

Routine Description:

    This routine checks for the instance of a Elnk16 card at the Io
    location given.  This is done by checking for the *3COM* string
    in the ports

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.

--*/

{
    NTSTATUS NtStatus;

    UCHAR TmpValue;


    //
    // Check for resource conflict
    //
    NtStatus = DetectCheckPortUsage(InterfaceType,
                                    BusNumber,
                                    IoBaseAddress,
                                    0x10
                                   );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    //
    // Write CSR to be bank 0
    //

    NtStatus = DetectWritePortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 0x6,
                                   0x00
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    //
    // Read for the signature
    //

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    if (TmpValue != '*') {

        return(FALSE);

    }

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 1,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    if (TmpValue != '3') {

        return(FALSE);

    }

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 2,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    if (TmpValue != 'C') {

        return(FALSE);

    }

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 3,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    if (TmpValue != 'O') {

        return(FALSE);

    }

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 4,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    if (TmpValue != 'M') {

        return(FALSE);

    }

    NtStatus = DetectReadPortUchar(InterfaceType,
                                   BusNumber,
                                   IoBaseAddress + 5,
                                   &TmpValue
                                  );

    if (NtStatus != STATUS_SUCCESS) {

        return(FALSE);

    }

    if (TmpValue != '*') {

        return(FALSE);

    }

    return(TRUE);

}


ULONG
Elnk16NextIoBaseAddress(
    IN ULONG IoBaseAddress
    )

/*++

Routine Description:

    Returns the next in a sequence on good IoBaseAddresses for an Elnk16 card.

Arguments:

    IoBaseAddress - Current IO port address.

Return Value:

    Next IoBaseAddress

--*/

{

    IoBaseAddress += 0x10;

    if (IoBaseAddress > 0x3E0) {

        return(0x3F0);

    }

    if ((IoBaseAddress == 0x270) ||
        (IoBaseAddress == 0x290) ||
        (IoBaseAddress == 0x2F0) ||
        (IoBaseAddress == 0x3B0)) {

        if (IoBaseAddress == 0x3B0) {

            return(0x3E0);

        }

        IoBaseAddress += 0x10;

    }

    return(IoBaseAddress);

}

VOID
Elnk16GenerateIdPattern(
    IN INTERFACE_TYPE InterfaceType,
    IN ULONG BusNumber
    )

/*++

Routine Description:

    This routine will write the ID pattern to port 0x100h.

Arguments:

    InterfaceType - Bus type, either EISA or ISA.

    BusNumber - Number of the bus in the system.

Return Value:
    None.

--*/

{

    UCHAR Value;
    UINT i;

    Value = 0xff;

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

        DetectWritePortUchar(InterfaceType,
                                   BusNumber,
                                   0x100,
                                   Value
                                  );


        if (Value & 0x80) {

            Value = (UCHAR) (Value << 1);
            Value ^= 0xe7;

        } else {

            Value = (UCHAR) (Value << 1);

        }

    }

    return;
}


