Added networking code from Casper Hornstrup
[reactos.git] / reactos / drivers / net / tcpip / datalink / lan.c
index 4476922..df15ca9 100644 (file)
-/*\r
- * COPYRIGHT:   See COPYING in the top level directory\r
- * PROJECT:     ReactOS TCP/IP protocol driver\r
- * FILE:        datalink/lan.c\r
- * PURPOSE:     Local Area Network media routines\r
- * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)\r
- * REVISIONS:\r
- *   CSH 01/08-2000 Created\r
- */\r
-#include <tcpip.h>\r
-#include <lan.h>\r
-#include <address.h>\r
-#include <routines.h>\r
-#include <transmit.h>\r
-#include <receive.h>\r
-#include <arp.h>\r
-\r
-\r
-NDIS_HANDLE NdisProtocolHandle = (NDIS_HANDLE)NULL;\r
-BOOLEAN ProtocolRegistered     = FALSE;\r
-PLAN_ADAPTER Adapters          = NULL;\r
-\r
-\r
-NDIS_STATUS NDISCall(\r
-    PLAN_ADAPTER Adapter,\r
-    NDIS_REQUEST_TYPE Type,\r
-    NDIS_OID OID,\r
-    PVOID Buffer,\r
-    UINT Length)\r
-/*\r
- * FUNCTION: Send a request to NDIS\r
- * ARGUMENTS:\r
- *     Adapter     = Pointer to a LAN_ADAPTER structure\r
- *     Type        = Type of request (Set or Query)\r
- *     OID         = Value to be set/queried for\r
- *     Buffer      = Pointer to a buffer to use\r
- *     Length      = Number of bytes in Buffer\r
- * RETURNS:\r
- *     Status of operation\r
- */\r
-{\r
-    NDIS_REQUEST Request;\r
-    NDIS_STATUS NdisStatus;\r
-\r
-    Request.RequestType = Type;\r
-    if (Type == NdisRequestSetInformation) {\r
-        Request.DATA.SET_INFORMATION.Oid                     = OID;\r
-        Request.DATA.SET_INFORMATION.InformationBuffer       = Buffer;\r
-        Request.DATA.SET_INFORMATION.InformationBufferLength = Length;\r
-    } else {\r
-        Request.DATA.QUERY_INFORMATION.Oid                     = OID;\r
-        Request.DATA.QUERY_INFORMATION.InformationBuffer       = Buffer;\r
-        Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length;\r
-    }\r
-\r
-    if (Adapter->State != LAN_STATE_RESETTING) {\r
-        NdisRequest(&NdisStatus, Adapter->NdisHandle, &Request);\r
-    } else\r
-        NdisStatus = NDIS_STATUS_NOT_ACCEPTED;\r
-\r
-    /* Wait for NDIS to complete the request */\r
-    if (NdisStatus == NDIS_STATUS_PENDING) {\r
-        KeWaitForSingleObject(&Adapter->Event, UserRequest, KernelMode, FALSE, NULL);\r
-        NdisStatus = Adapter->NdisStatus;\r
-    }\r
-\r
-    return NdisStatus;\r
-}\r
-\r
-\r
-PNDIS_PACKET AllocateTDPacket(\r
-    PLAN_ADAPTER Adapter)\r
-/*\r
- * FUNCTION: Allocates an NDIS packet for NdisTransferData\r
- * ARGUMENTS:\r
- *     Adapter = Pointer to LAN_ADAPTER structure\r
- * RETURNS:\r
- *     Pointer to NDIS packet or NULL if there was not enough free\r
- *     non-paged memory\r
- */\r
-{\r
-    NDIS_STATUS NdisStatus;\r
-    PNDIS_PACKET NdisPacket;\r
-    PNDIS_BUFFER Buffer;\r
-    PVOID Data;\r
-\r
-    NdisAllocatePacket(&NdisStatus, &NdisPacket, GlobalPacketPool);\r
-    if (NdisStatus != NDIS_STATUS_SUCCESS)\r
-        return NULL;\r
-\r
-    Data = ExAllocatePool(NonPagedPool, Adapter->MTU);\r
-    if (!Data) {\r
-        NdisFreePacket(NdisPacket);\r
-        return NULL;\r
-    }\r
-        \r
-    NdisAllocateBuffer(&NdisStatus, &Buffer, GlobalBufferPool, Data, Adapter->MTU);\r
-    if (NdisStatus != NDIS_STATUS_SUCCESS) {\r
-        NdisFreePacket(NdisPacket);\r
-        ExFreePool(Data);\r
-        return NULL;\r
-    }\r
-\r
-    NdisChainBufferAtFront(NdisPacket, Buffer);\r
-\r
-    PC(NdisPacket)->Context = NULL; /* End of list */\r
-\r
-    return NdisPacket;\r
-}\r
-\r
-\r
-VOID FreeTDPackets(\r
-    PLAN_ADAPTER Adapter)\r
-/*\r
- * FUNCTION: Frees transfer data packets\r
- * ARGUMENTS:\r
- *     Adapter = Pointer to LAN_ADAPTER structure\r
- */\r
-{\r
-    PNDIS_PACKET NdisPacket, Next;\r
-\r
-    /* Release transfer data packets */\r
-    NdisPacket = Adapter->TDPackets;\r
-    while (NdisPacket) {\r
-        Next = PC(NdisPacket)->Context;\r
-        FreeNdisPacket(NdisPacket);\r
-        NdisPacket = Next;\r
-    }\r
-    Adapter->TDPackets = NULL;\r
-}\r
-\r
-\r
-VOID FreeAdapter(\r
-    PLAN_ADAPTER Adapter)\r
-/*\r
- * FUNCTION: Frees memory for a LAN_ADAPTER structure\r
- * ARGUMENTS:\r
- *     Adapter = Pointer to LAN_ADAPTER structure to free\r
- */\r
-{\r
-    FreeTDPackets(Adapter);\r
-    ExFreePool(Adapter);\r
-}\r
-\r
-\r
-VOID ProtocolOpenAdapterComplete(\r
-    NDIS_HANDLE BindingContext,\r
-    NDIS_STATUS Status,\r
-    NDIS_STATUS OpenErrorStatus)\r
-/*\r
- * FUNCTION: Called by NDIS to complete opening of an adapter\r
- * ARGUMENTS:\r
- *     BindingContext  = Pointer to a device context (LAN_ADAPTER)\r
- *     Status          = Status of the operation\r
- *     OpenErrorStatus = Additional status information\r
- */\r
-{\r
-    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;\r
-\r
-    TI_DbgPrint(MID_TRACE, ("Called.\n"));\r
-\r
-    KeSetEvent(&Adapter->Event, 0, FALSE);\r
-}\r
-\r
-\r
-VOID ProtocolCloseAdapterComplete(\r
-    NDIS_HANDLE BindingContext,\r
-    NDIS_STATUS Status)\r
-/*\r
- * FUNCTION: Called by NDIS to complete closing an adapter\r
- * ARGUMENTS:\r
- *     BindingContext = Pointer to a device context (LAN_ADAPTER)\r
- *     Status         = Status of the operation\r
- */\r
-{\r
-    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;\r
-\r
-    TI_DbgPrint(MID_TRACE, ("Called.\n"));\r
-\r
-    Adapter->NdisStatus = Status;\r
-\r
-    KeSetEvent(&Adapter->Event, 0, FALSE);\r
-}\r
-\r
-\r
-VOID ProtocolResetComplete(\r
-    NDIS_HANDLE BindingContext,\r
-    NDIS_STATUS Status)\r
-/*\r
- * FUNCTION: Called by NDIS to complete resetting an adapter\r
- * ARGUMENTS:\r
- *     BindingContext = Pointer to a device context (LAN_ADAPTER)\r
- *     Status         = Status of the operation\r
- */\r
-{\r
-    TI_DbgPrint(MID_TRACE, ("Called.\n"));\r
-}\r
-\r
-\r
-VOID ProtocolRequestComplete(\r
-    NDIS_HANDLE BindingContext,\r
-    PNDIS_REQUEST NdisRequest,\r
-    NDIS_STATUS Status)\r
-/*\r
- * FUNCTION: Called by NDIS to complete a request\r
- * ARGUMENTS:\r
- *     BindingContext = Pointer to a device context (LAN_ADAPTER)\r
- *     NdisRequest    = Pointer to an object describing the request\r
- *     Status         = Status of the operation\r
- */\r
-{\r
-    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;\r
-\r
-    /* Save status of request and signal an event */\r
-    Adapter->NdisStatus = Status;\r
-\r
-    KeSetEvent(&Adapter->Event, 0, FALSE);\r
-}\r
-\r
-\r
-VOID ProtocolSendComplete(\r
-    NDIS_HANDLE BindingContext,\r
-    PNDIS_PACKET Packet,\r
-    NDIS_STATUS Status)\r
-/*\r
- * FUNCTION: Called by NDIS to complete sending process\r
- * ARGUMENTS:\r
- *     BindingContext = Pointer to a device context (LAN_ADAPTER)\r
- *     Packet         = Pointer to a packet descriptor\r
- *     Status         = Status of the operation\r
- */\r
-{\r
-       PLAN_ADAPTER Adapter = BindingContext;\r
-\r
-    TI_DbgPrint(MAX_TRACE, ("Called.\n"));\r
-\r
-    AdjustPacket(Packet, Adapter->HeaderSize, PC(Packet)->DLOffset);\r
-\r
-    (*PC(Packet)->DLComplete)(Adapter->Context, Packet, Status);\r
-}\r
-\r
-\r
-VOID ProtocolTransferDataComplete(\r
-    NDIS_HANDLE BindingContext,\r
-    PNDIS_PACKET Packet,\r
-    NDIS_STATUS Status,\r
-    UINT BytesTransferred)\r
-/*\r
- * FUNCTION: Called by NDIS to complete reception of data\r
- * ARGUMENTS:\r
- *     BindingContext   = Pointer to a device context (LAN_ADAPTER)\r
- *     Packet           = Pointer to a packet descriptor\r
- *     Status           = Status of the operation\r
- *     BytesTransferred = Number of bytes transferred\r
- * NOTES:\r
- *     If the packet was successfully received, determine the protocol\r
- *     type and pass it to the correct receive handler\r
- */\r
-{\r
-    UINT PacketType;\r
-    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;\r
-\r
-    if (Status == NDIS_STATUS_SUCCESS) {\r
-        PNDIS_BUFFER NdisBuffer;\r
-        IP_PACKET IPPacket;\r
-\r
-        NdisGetFirstBufferFromPacket(\r
-            Packet, &NdisBuffer, &IPPacket.Header,\r
-            &IPPacket.ContigSize, &IPPacket.TotalSize);\r
-\r
-        /* Determine which upper layer protocol that should receive\r
-           this packet and pass it to the correct receive handler */\r
-        PacketType = ((PETH_HEADER)IPPacket.Header)->EType;\r
-        switch (PacketType) {\r
-            case ETYPE_IPv4:\r
-            case ETYPE_IPv6:\r
-                IPReceive(Adapter->Context, &IPPacket);\r
-                break;\r
-            case ETYPE_ARP:\r
-                ARPReceive(Adapter->Context, &IPPacket);\r
-            default:\r
-                break;\r
-        }\r
-    }\r
-\r
-    /* Release the packet descriptor */\r
-    KeAcquireSpinLockAtDpcLevel(&Adapter->Lock);\r
-\r
-    PC(Packet)->Context = Adapter->TDPackets;\r
-    Adapter->TDPackets  = Packet;\r
-\r
-    KeReleaseSpinLockFromDpcLevel(&Adapter->Lock);\r
-}\r
-\r
-\r
-NDIS_STATUS ProtocolReceive(\r
-    NDIS_HANDLE BindingContext,\r
-    NDIS_HANDLE MacReceiveContext,\r
-    PVOID HeaderBuffer,\r
-    UINT HeaderBufferSize,\r
-    PVOID LookaheadBuffer,\r
-    UINT LookaheadBufferSize,\r
-    UINT PacketSize)\r
-/*\r
- * FUNCTION: Called by NDIS when a packet has been received on the physical link\r
- * ARGUMENTS:\r
- *     BindingContext      = Pointer to a device context (LAN_ADAPTER)\r
- *     MacReceiveContext   = Handle used by underlying NIC driver\r
- *     HeaderBuffer        = Pointer to a buffer containing the packet header\r
- *     HeaderBufferSize    = Number of bytes in HeaderBuffer\r
- *     LookaheadBuffer     = Pointer to a buffer containing buffered packet data\r
- *     LookaheadBufferSize = Size of LookaheadBuffer. May be less than asked for\r
- *     PacketSize          = Overall size of the packet (not including header)\r
- * RETURNS:\r
- *     Status of operation\r
- */\r
-{\r
-    USHORT EType;\r
-    UINT PacketType;\r
-    IP_PACKET IPPacket;\r
-    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;\r
-    PETH_HEADER EHeader  = (PETH_HEADER)HeaderBuffer;\r
-\r
-    TI_DbgPrint(MAX_TRACE, ("Called.\n"));\r
-\r
-    if ((Adapter->State != LAN_STATE_STARTED) ||\r
-        HeaderBufferSize < Adapter->HeaderSize)\r
-        /* Adapter is not started or the header was too small */\r
-        return NDIS_STATUS_NOT_ACCEPTED;\r
-\r
-    if (Adapter->Media == NdisMedium802_3) {\r
-        /* Ethernet and IEEE 802.3 frames can be destinguished by\r
-           looking at the IEEE 802.3 length field. This field is\r
-           less than or equal to 1500 for a valid IEEE 802.3 frame\r
-           and larger than 1500 is it's a valid Ether-Type value.\r
-           See RFC 1122, section 2.3.3 for more information */\r
-        if (((EType = EHeader->EType) != ETYPE_IPv4) && (EType != ETYPE_ARP))\r
-            return NDIS_STATUS_NOT_ACCEPTED;\r
-        /* We use Ether-Type constants to destinguish packets */\r
-        PacketType = EType;\r
-    } else\r
-        /* FIXME: Support other medias */\r
-        return NDIS_STATUS_NOT_ACCEPTED;\r
-\r
-    if (LookaheadBufferSize < PacketSize) {\r
-        NDIS_STATUS NdisStatus;\r
-        PNDIS_PACKET NdisPacket;\r
-        UINT BytesTransferred;\r
-        \r
-        /* Get transfer data packet */\r
-\r
-        KeAcquireSpinLockAtDpcLevel(&Adapter->Lock);\r
-\r
-        NdisPacket = Adapter->TDPackets;\r
-        if (NdisPacket == (PNDIS_PACKET)NULL) {\r
-            /* We don't have a free packet descriptor. Drop the packet */\r
-            KeReleaseSpinLockFromDpcLevel(&Adapter->Lock);\r
-            return NDIS_STATUS_SUCCESS;\r
-        }\r
-        Adapter->TDPackets = PC(NdisPacket)->Context;\r
-\r
-        KeReleaseSpinLockFromDpcLevel(&Adapter->Lock);\r
-\r
-        /* Get the data */\r
-        NdisTransferData(&NdisStatus, Adapter->NdisHandle,\r
-            MacReceiveContext, 0, PacketSize,\r
-            NdisPacket, &BytesTransferred);\r
-        if (NdisStatus != NDIS_STATUS_PENDING)\r
-            ProtocolTransferDataComplete(BindingContext,\r
-                NdisPacket, NdisStatus, BytesTransferred);\r
-\r
-        return NDIS_STATUS_SUCCESS;\r
-    }\r
-\r
-    /* We got all the data in the lookahead buffer */\r
-    RtlZeroMemory(&IPPacket, sizeof(IPPacket));\r
-    IPPacket.Header    = LookaheadBuffer;\r
-    IPPacket.TotalSize = PacketSize;\r
-\r
-    switch (PacketType) {\r
-        case ETYPE_IPv4:\r
-        case ETYPE_IPv6:\r
-            IPReceive(Adapter->Context, &IPPacket);\r
-            break;\r
-        case ETYPE_ARP:\r
-            ARPReceive(Adapter->Context, &IPPacket);\r
-            break;\r
-        default:\r
-            break;\r
-    }\r
-\r
-    return NDIS_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-VOID ProtocolReceiveComplete(\r
-    NDIS_HANDLE BindingContext)\r
-/*\r
- * FUNCTION: Called by NDIS when we're done receiving data\r
- * ARGUMENTS:\r
- *     BindingContext = Pointer to a device context (LAN_ADAPTER)\r
- */\r
-{\r
-    TI_DbgPrint(MID_TRACE, ("Called.\n"));\r
-}\r
-\r
-\r
-VOID ProtocolStatus(\r
-    NDIS_HANDLE BindingContext,\r
-    NDIS_STATUS GenerelStatus,\r
-    PVOID StatusBuffer,\r
-    UINT StatusBufferSize)\r
-/*\r
- * FUNCTION: Called by NDIS when the underlying driver has changed state\r
- * ARGUMENTS:\r
- *     BindingContext   = Pointer to a device context (LAN_ADAPTER)\r
- *     GenerelStatus    = A generel status code\r
- *     StatusBuffer     = Pointer to a buffer with medium-specific data\r
- *     StatusBufferSize = Number of bytes in StatusBuffer\r
- */\r
-{\r
-    TI_DbgPrint(MID_TRACE, ("Called.\n"));\r
-}\r
-\r
-\r
-VOID ProtocolStatusComplete(\r
-    NDIS_HANDLE NdisBindingContext)\r
-/*\r
- * FUNCTION: Called by NDIS when a status-change has occurred\r
- * ARGUMENTS:\r
- *     BindingContext = Pointer to a device context (LAN_ADAPTER)\r
- */\r
-{\r
-    TI_DbgPrint(MID_TRACE, ("Called.\n"));\r
-}\r
-\r
-\r
-VOID LANTransmit(\r
-    PVOID Context,\r
-    PNDIS_PACKET NdisPacket,\r
-    UINT Offset,\r
-    PVOID LinkAddress,\r
-    USHORT Type)\r
-/*\r
- * FUNCTION: Transmits a packet\r
- * ARGUMENTS:\r
- *     Context     = Pointer to context information (LAN_ADAPTER)\r
- *     NdisPacket  = Pointer to NDIS packet to send\r
- *     Offset      = Offset in packet where data starts\r
- *     LinkAddress = Pointer to link address of destination (NULL = broadcast)\r
- *     Type        = LAN protocol type (LAN_PROTO_*)\r
- */\r
-{\r
-    NDIS_STATUS NdisStatus;\r
-    PETH_HEADER EHeader;\r
-    PVOID Data;\r
-    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)Context;\r
-\r
-    TI_DbgPrint(MAX_TRACE, ("Called.\n"));\r
-\r
-    /* NDIS send routines don't have an offset argument so we\r
-       must offset the data in upper layers and adjust the\r
-       packet here. We save the offset in the packet context\r
-       area so it can be undone before we release the packet */\r
-    Data = AdjustPacket(NdisPacket, Offset, Adapter->HeaderSize);\r
-    PC(NdisPacket)->DLOffset = Offset;\r
-\r
-    if (Adapter->State == LAN_STATE_STARTED) {\r
-        switch (Adapter->Media) {\r
-        case NdisMedium802_3:\r
-            EHeader = (PETH_HEADER)Data;\r
-    \r
-            if (LinkAddress)\r
-                /* Unicast address */\r
-                RtlCopyMemory(EHeader->DstAddr, LinkAddress, IEEE_802_ADDR_LENGTH);\r
-             else\r
-                /* Broadcast address */\r
-                RtlFillMemory(EHeader->DstAddr, IEEE_802_ADDR_LENGTH, 0xFF);\r
-\r
-            RtlCopyMemory(EHeader->SrcAddr, Adapter->HWAddress, IEEE_802_ADDR_LENGTH);\r
-\r
-            switch (Type) {\r
-                case LAN_PROTO_IPv4:\r
-                    EHeader->EType = ETYPE_IPv4;\r
-                    break;\r
-                case LAN_PROTO_ARP:\r
-                    EHeader->EType = ETYPE_ARP;\r
-                    break;\r
-                case LAN_PROTO_IPv6:\r
-                    EHeader->EType = ETYPE_IPv6;\r
-                    break;\r
-                default:\r
-#if DBG\r
-                    /* Should not happen */\r
-                    TI_DbgPrint(MIN_TRACE, ("Unknown LAN protocol.\n"));\r
-\r
-                    ProtocolSendComplete((NDIS_HANDLE)Context, NdisPacket, NDIS_STATUS_FAILURE);\r
-#endif\r
-                    return;\r
-            }\r
-            break;\r
-\r
-        default:\r
-            /* FIXME: Support other medias */\r
-            break;\r
-        }\r
-       \r
-        NdisSend(&NdisStatus, Adapter->NdisHandle, NdisPacket);\r
-        if (NdisStatus != NDIS_STATUS_PENDING)\r
-            ProtocolSendComplete((NDIS_HANDLE)Context, NdisPacket, NdisStatus);\r
-    } else\r
-        ProtocolSendComplete((NDIS_HANDLE)Context, NdisPacket, NDIS_STATUS_CLOSED);\r
-}\r
-\r
-\r
-VOID BindAdapter(\r
-    PLAN_ADAPTER Adapter)\r
-/*\r
- * FUNCTION: Binds a LAN adapter to IP layer\r
- * ARGUMENTS:\r
- *     Adapter = Pointer to LAN_ADAPTER structure\r
- * NOTES:\r
- *    We set the lookahead buffer size, set the packet filter and\r
- *    bind the adapter to IP layer\r
- */\r
-{\r
-    INT i;\r
-    PIP_INTERFACE IF;\r
-    PIP_ADDRESS Address;\r
-    PNDIS_PACKET Packet;\r
-    NDIS_STATUS NdisStatus;\r
-    LLIP_BIND_INFO BindInfo;\r
-    ULONG Lookahead = LOOKAHEAD_SIZE;\r
-\r
-    TI_DbgPrint(MID_TRACE, ("Called.\n"));\r
-\r
-    Adapter->State = LAN_STATE_OPENING;\r
-\r
-    NdisStatus = NDISCall(Adapter, NdisRequestSetInformation,\r
-        OID_GEN_CURRENT_LOOKAHEAD, &Lookahead, sizeof(ULONG));\r
-    if (NdisStatus != NDIS_STATUS_SUCCESS) {\r
-        TI_DbgPrint(MID_TRACE, ("Could not set lookahead buffer size (0x%X).\n", NdisStatus));\r
-        return;\r
-    }\r
-\r
-    /* Allocate packets for NdisTransferData */\r
-    /* FIXME: How many should we allocate? */\r
-    Adapter->TDPackets = NULL;\r
-    for (i = 0; i < 2; i++) {\r
-        Packet              = AllocateTDPacket(Adapter);\r
-        PC(Packet)->Context = Adapter->TDPackets;\r
-        Adapter->TDPackets  = Packet;\r
-        if (!Packet) {\r
-            TI_DbgPrint(MID_TRACE, ("Could not allocate transfer data packet (out of resources).\n"));\r
-            FreeTDPackets(Adapter);\r
-            return;\r
-        }\r
-    }\r
-\r
-    /* Bind the adapter to IP layer */\r
-    BindInfo.Context       = Adapter;\r
-    BindInfo.HeaderSize    = Adapter->HeaderSize;\r
-    BindInfo.MinFrameSize  = Adapter->MinFrameSize;\r
-    BindInfo.MTU           = Adapter->MTU;\r
-    BindInfo.Address       = (PUCHAR)&Adapter->HWAddress;\r
-    BindInfo.AddressLength = Adapter->HWAddressLength;\r
-    BindInfo.Transmit      = LANTransmit;\r
-\r
-    IF = IPCreateInterface(&BindInfo);\r
-    if (!IF) {\r
-        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));\r
-        FreeTDPackets(Adapter);\r
-        return;\r
-    }\r
-\r
-    /* FIXME: Get address from registry.\r
-       For now just use a private address, eg. 10.0.0.10 */\r
-    Address = AddrBuildIPv4(0x0A00000A);\r
-    if (!Address) {\r
-        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));\r
-        FreeTDPackets(Adapter);\r
-        IPDestroyInterface(Adapter->Context);\r
-        return;\r
-    }\r
-    /* Create a net table entry for this interface */\r
-    if (!IPCreateNTE(IF, Address, 8)) {\r
-        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));\r
-        FreeTDPackets(Adapter);\r
-        IPDestroyInterface(IF);\r
-        return;\r
-    }\r
-\r
-    /* Reference the interface for the NTE. The reference for\r
-       the address is just passed on to the NTE */\r
-    ReferenceObject(IF);\r
-\r
-    /* Register interface with IP layer */\r
-    IPRegisterInterface(IF);\r
-\r
-    /* Set packet filter so we can send and receive packets */\r
-    NdisStatus = NDISCall(Adapter, NdisRequestSetInformation,\r
-        OID_GEN_CURRENT_PACKET_FILTER, &Adapter->PacketFilter, sizeof(UINT));\r
-    if (NdisStatus != NDIS_STATUS_SUCCESS) {\r
-        TI_DbgPrint(MID_TRACE, ("Could not set packet filter (0x%X).\n", NdisStatus));\r
-        FreeTDPackets(Adapter);\r
-        IPDestroyInterface(IF);\r
-        return;\r
-    }\r
-\r
-    Adapter->Context = IF;\r
-\r
-    Adapter->State = LAN_STATE_STARTED;\r
-}\r
-\r
-\r
-VOID UnbindAdapter(\r
-    PLAN_ADAPTER Adapter)\r
-/*\r
- * FUNCTION: Unbinds a LAN adapter from IP layer\r
- * ARGUMENTS:\r
- *     Adapter = Pointer to LAN_ADAPTER structure\r
- */\r
-{\r
-    TI_DbgPrint(MID_TRACE, ("Called.\n"));\r
-\r
-    if (Adapter->State == LAN_STATE_STARTED) {\r
-        PIP_INTERFACE IF = Adapter->Context;\r
-\r
-        IPUnregisterInterface(IF);\r
-\r
-        IPDestroyInterface(IF);\r
-\r
-        /* Free transfer data packets */\r
-        FreeTDPackets(Adapter);\r
-    }\r
-}\r
-\r
-\r
-NDIS_STATUS LANRegisterAdapter(\r
-    PNDIS_STRING AdapterName,\r
-    PLAN_ADAPTER *Adapter)\r
-/*\r
- * FUNCTION: Registers protocol with an NDIS adapter\r
- * ARGUMENTS:\r
- *     AdapterName = Pointer to string with name of adapter to register\r
- *     Adapter     = Address of pointer to a LAN_ADAPTER structure\r
- * RETURNS:\r
- *     Status of operation\r
- */\r
-{\r
-    PLAN_ADAPTER IF;\r
-    NDIS_STATUS NdisStatus;\r
-    NDIS_STATUS OpenStatus;\r
-    UINT MediaIndex;\r
-    NDIS_MEDIUM MediaArray[MAX_MEDIA];\r
-    UINT AddressOID;\r
-    UINT Speed;\r
-\r
-    TI_DbgPrint(MAX_TRACE, ("Called.\n"));\r
-\r
-    IF = ExAllocatePool(NonPagedPool, sizeof(LAN_ADAPTER));\r
-    if (!IF)\r
-        return NDIS_STATUS_RESOURCES;\r
-\r
-    RtlZeroMemory(IF, sizeof(LAN_ADAPTER));\r
-\r
-    /* Put adapter in stopped state */\r
-    IF->State = LAN_STATE_STOPPED;\r
-\r
-    /* Initialize protecting spin lock */\r
-    KeInitializeSpinLock(&IF->Lock);\r
-\r
-    KeInitializeEvent(&IF->Event, SynchronizationEvent, FALSE);\r
-\r
-    /* Initialize array with media IDs we support */\r
-    MediaArray[MEDIA_ETH] = NdisMedium802_3;\r
-\r
-    /* Open the adapter. */\r
-    NdisOpenAdapter(&NdisStatus, &OpenStatus, &IF->NdisHandle, &MediaIndex,\r
-        MediaArray, MAX_MEDIA, NdisProtocolHandle, IF, AdapterName, 0, NULL);\r
-\r
-    /* Wait until the adapter is opened */\r
-    if (NdisStatus == NDIS_STATUS_PENDING)\r
-        KeWaitForSingleObject(&IF->Event, UserRequest, KernelMode, FALSE, NULL);\r
-    else if (NdisStatus != NDIS_STATUS_SUCCESS) {\r
-        ExFreePool(IF);\r
-        return NdisStatus;\r
-    }\r
-\r
-    IF->Media = MediaArray[MediaIndex];\r
-\r
-    /* Fill LAN_ADAPTER structure with some adapter specific information */\r
-    switch (IF->Media) {\r
-    case NdisMedium802_3:\r
-        IF->HWAddressLength = IEEE_802_ADDR_LENGTH;\r
-        IF->BCastMask       = BCAST_ETH_MASK;\r
-        IF->BCastCheck      = BCAST_ETH_CHECK;\r
-        IF->BCastOffset     = BCAST_ETH_OFFSET;\r
-        IF->HeaderSize      = sizeof(ETH_HEADER);\r
-        IF->MinFrameSize    = 60;\r
-        AddressOID          = OID_802_3_CURRENT_ADDRESS;\r
-        IF->PacketFilter    = \r
-            NDIS_PACKET_TYPE_BROADCAST |\r
-            NDIS_PACKET_TYPE_DIRECTED  |\r
-            NDIS_PACKET_TYPE_MULTICAST;\r
-        break;\r
-\r
-    default:\r
-        /* Unsupported media */\r
-        TI_DbgPrint(MIN_TRACE, ("Unsupported media.\n"));\r
-        ExFreePool(IF);\r
-        return NDIS_STATUS_NOT_SUPPORTED;\r
-    }\r
-\r
-    /* Get maximum frame size */\r
-    NdisStatus = NDISCall(IF, NdisRequestQueryInformation,\r
-        OID_GEN_MAXIMUM_FRAME_SIZE, &IF->MTU, sizeof(UINT));\r
-    if (NdisStatus != NDIS_STATUS_SUCCESS) {\r
-        ExFreePool(IF);\r
-        return NdisStatus;\r
-    }\r
-\r
-    /* Get maximum packet size */\r
-    NdisStatus = NDISCall(IF, NdisRequestQueryInformation,\r
-        OID_GEN_MAXIMUM_TOTAL_SIZE, &IF->MaxPacketSize, sizeof(UINT));\r
-    if (NdisStatus != NDIS_STATUS_SUCCESS) {\r
-        TI_DbgPrint(MIN_TRACE, ("Query for maximum packet size failed.\n"));\r
-        ExFreePool(IF);\r
-        return NdisStatus;\r
-    }\r
-\r
-    /* Get maximum number of packets we can pass to NdisSend(Packets) at one time */\r
-    NdisStatus = NDISCall(IF, NdisRequestQueryInformation,\r
-        OID_GEN_MAXIMUM_SEND_PACKETS, &IF->MaxSendPackets, sizeof(UINT));\r
-    if (NdisStatus != NDIS_STATUS_SUCCESS)\r
-        /* Legacy NIC drivers may not support this query, if it fails we\r
-           assume it can send at least one packet per call to NdisSend(Packets) */\r
-        IF->MaxSendPackets = 1;\r
-\r
-    /* Get current hardware address */\r
-    NdisStatus = NDISCall(IF, NdisRequestQueryInformation, AddressOID,\r
-        IF->HWAddress, IF->HWAddressLength);\r
-    if (NdisStatus != NDIS_STATUS_SUCCESS) {\r
-        TI_DbgPrint(MIN_TRACE, ("Query for current hardware address failed.\n"));\r
-        ExFreePool(IF);\r
-        return NdisStatus;\r
-    }\r
-\r
-    /* Get maximum link speed */\r
-    NdisStatus = NDISCall(IF, NdisRequestQueryInformation,\r
-        OID_GEN_LINK_SPEED, &Speed, sizeof(UINT));\r
-    if (NdisStatus != NDIS_STATUS_SUCCESS) {\r
-        TI_DbgPrint(MIN_TRACE, ("Query for maximum link speed failed.\n"));\r
-        ExFreePool(IF);\r
-        return NdisStatus;\r
-    }\r
-\r
-    /* Convert returned link speed to bps (it is in 100bps increments) */\r
-    IF->Speed = Speed * 100L;\r
-\r
-    *Adapter = IF;\r
-\r
-    /* Add adapter to the adapter list */\r
-    IF->Next = Adapters;\r
-    Adapters = IF;\r
-\r
-    /* Bind adapter to IP layer */\r
-    BindAdapter(IF);\r
-\r
-    TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));\r
-\r
-    return NDIS_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-NDIS_STATUS LANUnregisterAdapter(\r
-    PLAN_ADAPTER Adapter)\r
-/*\r
- * FUNCTION: Unregisters protocol with NDIS adapter\r
- * ARGUMENTS:\r
- *     Adapter = Pointer to a LAN_ADAPTER structure\r
- * RETURNS:\r
- *     Status of operation\r
- */\r
-{\r
-    KIRQL OldIrql;\r
-    NDIS_HANDLE NdisHandle;\r
-    PLAN_ADAPTER IF, PrevIF;\r
-    BOOLEAN Found = FALSE;\r
-    NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;\r
-\r
-    TI_DbgPrint(MAX_TRACE, ("Called.\n"));\r
-\r
-    /* Search the adapter list for the specified adapter and remove it */\r
-    IF = Adapters;\r
-    if (IF) {\r
-        if (Adapter != Adapters) {\r
-            PrevIF = IF;\r
-            while ((IF) && (!Found)) {\r
-                if (IF == Adapter) {\r
-                    /* We've found the adapter, now remove it from the list */\r
-                    PrevIF->Next = IF->Next;\r
-                    Found = TRUE;\r
-                }\r
-                PrevIF = IF;\r
-                IF = IF->Next;\r
-            }\r
-        } else {\r
-            Adapters = NULL;\r
-            Found    = TRUE;\r
-        }\r
-    }\r
-    if (!Found) {\r
-        TI_DbgPrint(MIN_TRACE, ("Leaving (adapter was not in list).\n"));\r
-        return NDIS_STATUS_ADAPTER_NOT_FOUND;\r
-    }\r
-\r
-    /* Unbind adapter from IP layer */\r
-    UnbindAdapter(Adapter);\r
-\r
-    KeAcquireSpinLock(&Adapter->Lock, &OldIrql);\r
-    NdisHandle = Adapter->NdisHandle;\r
-    if (NdisHandle) {\r
-        Adapter->NdisHandle = NULL;\r
-        KeReleaseSpinLock(&Adapter->Lock, OldIrql);\r
-\r
-        NdisCloseAdapter(&NdisStatus, NdisHandle);\r
-        if (NdisStatus == NDIS_STATUS_PENDING) {\r
-            KeWaitForSingleObject(&Adapter->Event,\r
-                UserRequest, KernelMode, FALSE, NULL);\r
-            NdisStatus = Adapter->NdisStatus;\r
-        }\r
-    } else\r
-        KeReleaseSpinLock(&Adapter->Lock, OldIrql);\r
-\r
-    FreeAdapter(Adapter);\r
-\r
-    return NDIS_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-NTSTATUS LANRegisterProtocol(\r
-    PSTRING Name)\r
-/*\r
- * FUNCTION: Registers this protocol driver with NDIS\r
- * ARGUMENTS:\r
- *     Name = Name of this protocol driver\r
- * RETURNS:\r
- *     Status of operation\r
- */\r
-{\r
-    NDIS_STATUS NdisStatus;\r
-    NDIS_PROTOCOL_CHARACTERISTICS ProtChars;\r
-\r
-    /* Set up protocol characteristics */\r
-    RtlZeroMemory(&ProtChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));\r
-    ProtChars.MajorNdisVersion            = NDIS_VERSION_MAJOR;\r
-    ProtChars.MinorNdisVersion            = NDIS_VERSION_MINOR;\r
-    ProtChars.Name.Length                 = Name->Length;\r
-    ProtChars.Name.Buffer                 = (PVOID)Name->Buffer;\r
-    ProtChars.OpenAdapterCompleteHandler  = ProtocolOpenAdapterComplete;\r
-    ProtChars.CloseAdapterCompleteHandler = ProtocolCloseAdapterComplete;\r
-    ProtChars.ResetCompleteHandler        = ProtocolResetComplete;\r
-    ProtChars.RequestCompleteHandler      = ProtocolRequestComplete;\r
-    ProtChars.SendCompleteHandler         = ProtocolSendComplete;\r
-    ProtChars.TransferDataCompleteHandler = ProtocolTransferDataComplete;\r
-    ProtChars.ReceiveHandler              = ProtocolReceive;\r
-    ProtChars.ReceiveCompleteHandler      = ProtocolReceiveComplete;\r
-    ProtChars.StatusHandler               = ProtocolStatus;\r
-    ProtChars.StatusCompleteHandler       = ProtocolStatusComplete;\r
-\r
-       /* Try to register protocol */\r
-    NdisRegisterProtocol(\r
-        &NdisStatus, &NdisProtocolHandle, &ProtChars,\r
-        sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + Name->Length);\r
-    if (NdisStatus != NDIS_STATUS_SUCCESS)\r
-        return (NTSTATUS)NdisStatus;\r
-\r
-    ProtocolRegistered = TRUE;\r
-\r
-    return STATUS_SUCCESS;\r
-}\r
-\r
-\r
-VOID LANUnregisterProtocol(\r
-    VOID)\r
-/*\r
- * FUNCTION: Unregisters this protocol driver with NDIS\r
- * NOTES: Does not care wether we are already registered\r
- */\r
-{\r
-    if (ProtocolRegistered) {\r
-        NDIS_STATUS NdisStatus;\r
-\r
-        while (Adapters)\r
-            NdisStatus = LANUnregisterAdapter(Adapters);\r
-\r
-        NdisDeregisterProtocol(&NdisStatus, NdisProtocolHandle);\r
-        ProtocolRegistered = FALSE;\r
-    }\r
-}\r
-\r
-/* EOF */\r
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        datalink/lan.c
+ * PURPOSE:     Local Area Network media routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+#include <tcpip.h>
+#include <lan.h>
+#include <address.h>
+#include <routines.h>
+#include <transmit.h>
+#include <receive.h>
+#include <arp.h>
+
+
+NDIS_HANDLE NdisProtocolHandle = (NDIS_HANDLE)NULL;
+BOOLEAN ProtocolRegistered     = FALSE;
+PLAN_ADAPTER Adapters          = NULL;
+
+
+NDIS_STATUS NDISCall(
+    PLAN_ADAPTER Adapter,
+    NDIS_REQUEST_TYPE Type,
+    NDIS_OID OID,
+    PVOID Buffer,
+    UINT Length)
+/*
+ * FUNCTION: Send a request to NDIS
+ * ARGUMENTS:
+ *     Adapter     = Pointer to a LAN_ADAPTER structure
+ *     Type        = Type of request (Set or Query)
+ *     OID         = Value to be set/queried for
+ *     Buffer      = Pointer to a buffer to use
+ *     Length      = Number of bytes in Buffer
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    NDIS_REQUEST Request;
+    NDIS_STATUS NdisStatus;
+
+    Request.RequestType = Type;
+    if (Type == NdisRequestSetInformation) {
+        Request.DATA.SET_INFORMATION.Oid                     = OID;
+        Request.DATA.SET_INFORMATION.InformationBuffer       = Buffer;
+        Request.DATA.SET_INFORMATION.InformationBufferLength = Length;
+    } else {
+        Request.DATA.QUERY_INFORMATION.Oid                     = OID;
+        Request.DATA.QUERY_INFORMATION.InformationBuffer       = Buffer;
+        Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length;
+    }
+
+    if (Adapter->State != LAN_STATE_RESETTING) {
+        NdisRequest(&NdisStatus, Adapter->NdisHandle, &Request);
+    } else
+        NdisStatus = NDIS_STATUS_NOT_ACCEPTED;
+
+    /* Wait for NDIS to complete the request */
+    if (NdisStatus == NDIS_STATUS_PENDING) {
+        KeWaitForSingleObject(&Adapter->Event, UserRequest, KernelMode, FALSE, NULL);
+        NdisStatus = Adapter->NdisStatus;
+    }
+
+    return NdisStatus;
+}
+
+
+PNDIS_PACKET AllocateTDPacket(
+    PLAN_ADAPTER Adapter)
+/*
+ * FUNCTION: Allocates an NDIS packet for NdisTransferData
+ * ARGUMENTS:
+ *     Adapter = Pointer to LAN_ADAPTER structure
+ * RETURNS:
+ *     Pointer to NDIS packet or NULL if there was not enough free
+ *     non-paged memory
+ */
+{
+    NDIS_STATUS NdisStatus;
+    PNDIS_PACKET NdisPacket;
+    PNDIS_BUFFER Buffer;
+    PVOID Data;
+
+    NdisAllocatePacket(&NdisStatus, &NdisPacket, GlobalPacketPool);
+    if (NdisStatus != NDIS_STATUS_SUCCESS)
+        return NULL;
+
+    Data = ExAllocatePool(NonPagedPool, Adapter->MTU);
+    if (!Data) {
+        NdisFreePacket(NdisPacket);
+        return NULL;
+    }
+        
+    NdisAllocateBuffer(&NdisStatus, &Buffer, GlobalBufferPool, Data, Adapter->MTU);
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        NdisFreePacket(NdisPacket);
+        ExFreePool(Data);
+        return NULL;
+    }
+
+    NdisChainBufferAtFront(NdisPacket, Buffer);
+
+    PC(NdisPacket)->Context = NULL; /* End of list */
+
+    return NdisPacket;
+}
+
+
+VOID FreeTDPackets(
+    PLAN_ADAPTER Adapter)
+/*
+ * FUNCTION: Frees transfer data packets
+ * ARGUMENTS:
+ *     Adapter = Pointer to LAN_ADAPTER structure
+ */
+{
+    PNDIS_PACKET NdisPacket, Next;
+
+    /* Release transfer data packets */
+    NdisPacket = Adapter->TDPackets;
+    while (NdisPacket) {
+        Next = PC(NdisPacket)->Context;
+        FreeNdisPacket(NdisPacket);
+        NdisPacket = Next;
+    }
+    Adapter->TDPackets = NULL;
+}
+
+
+VOID FreeAdapter(
+    PLAN_ADAPTER Adapter)
+/*
+ * FUNCTION: Frees memory for a LAN_ADAPTER structure
+ * ARGUMENTS:
+ *     Adapter = Pointer to LAN_ADAPTER structure to free
+ */
+{
+    FreeTDPackets(Adapter);
+    ExFreePool(Adapter);
+}
+
+
+VOID ProtocolOpenAdapterComplete(
+    NDIS_HANDLE BindingContext,
+    NDIS_STATUS Status,
+    NDIS_STATUS OpenErrorStatus)
+/*
+ * FUNCTION: Called by NDIS to complete opening of an adapter
+ * ARGUMENTS:
+ *     BindingContext  = Pointer to a device context (LAN_ADAPTER)
+ *     Status          = Status of the operation
+ *     OpenErrorStatus = Additional status information
+ */
+{
+    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
+
+    TI_DbgPrint(MID_TRACE, ("Called.\n"));
+
+    KeSetEvent(&Adapter->Event, 0, FALSE);
+}
+
+
+VOID ProtocolCloseAdapterComplete(
+    NDIS_HANDLE BindingContext,
+    NDIS_STATUS Status)
+/*
+ * FUNCTION: Called by NDIS to complete closing an adapter
+ * ARGUMENTS:
+ *     BindingContext = Pointer to a device context (LAN_ADAPTER)
+ *     Status         = Status of the operation
+ */
+{
+    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
+
+    TI_DbgPrint(MID_TRACE, ("Called.\n"));
+
+    Adapter->NdisStatus = Status;
+
+    KeSetEvent(&Adapter->Event, 0, FALSE);
+}
+
+
+VOID ProtocolResetComplete(
+    NDIS_HANDLE BindingContext,
+    NDIS_STATUS Status)
+/*
+ * FUNCTION: Called by NDIS to complete resetting an adapter
+ * ARGUMENTS:
+ *     BindingContext = Pointer to a device context (LAN_ADAPTER)
+ *     Status         = Status of the operation
+ */
+{
+    TI_DbgPrint(MID_TRACE, ("Called.\n"));
+}
+
+
+VOID ProtocolRequestComplete(
+    NDIS_HANDLE BindingContext,
+    PNDIS_REQUEST NdisRequest,
+    NDIS_STATUS Status)
+/*
+ * FUNCTION: Called by NDIS to complete a request
+ * ARGUMENTS:
+ *     BindingContext = Pointer to a device context (LAN_ADAPTER)
+ *     NdisRequest    = Pointer to an object describing the request
+ *     Status         = Status of the operation
+ */
+{
+    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
+
+    /* Save status of request and signal an event */
+    Adapter->NdisStatus = Status;
+
+    KeSetEvent(&Adapter->Event, 0, FALSE);
+}
+
+
+VOID ProtocolSendComplete(
+    NDIS_HANDLE BindingContext,
+    PNDIS_PACKET Packet,
+    NDIS_STATUS Status)
+/*
+ * FUNCTION: Called by NDIS to complete sending process
+ * ARGUMENTS:
+ *     BindingContext = Pointer to a device context (LAN_ADAPTER)
+ *     Packet         = Pointer to a packet descriptor
+ *     Status         = Status of the operation
+ */
+{
+       PLAN_ADAPTER Adapter = BindingContext;
+
+    TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+    AdjustPacket(Packet, Adapter->HeaderSize, PC(Packet)->DLOffset);
+
+    (*PC(Packet)->DLComplete)(Adapter->Context, Packet, Status);
+}
+
+
+VOID ProtocolTransferDataComplete(
+    NDIS_HANDLE BindingContext,
+    PNDIS_PACKET Packet,
+    NDIS_STATUS Status,
+    UINT BytesTransferred)
+/*
+ * FUNCTION: Called by NDIS to complete reception of data
+ * ARGUMENTS:
+ *     BindingContext   = Pointer to a device context (LAN_ADAPTER)
+ *     Packet           = Pointer to a packet descriptor
+ *     Status           = Status of the operation
+ *     BytesTransferred = Number of bytes transferred
+ * NOTES:
+ *     If the packet was successfully received, determine the protocol
+ *     type and pass it to the correct receive handler
+ */
+{
+    UINT PacketType;
+    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
+
+    if (Status == NDIS_STATUS_SUCCESS) {
+        PNDIS_BUFFER NdisBuffer;
+        IP_PACKET IPPacket;
+
+        NdisGetFirstBufferFromPacket(
+            Packet, &NdisBuffer, &IPPacket.Header,
+            &IPPacket.ContigSize, &IPPacket.TotalSize);
+
+        /* Determine which upper layer protocol that should receive
+           this packet and pass it to the correct receive handler */
+        PacketType = ((PETH_HEADER)IPPacket.Header)->EType;
+        switch (PacketType) {
+            case ETYPE_IPv4:
+            case ETYPE_IPv6:
+                IPReceive(Adapter->Context, &IPPacket);
+                break;
+            case ETYPE_ARP:
+                ARPReceive(Adapter->Context, &IPPacket);
+            default:
+                break;
+        }
+    }
+
+    /* Release the packet descriptor */
+    KeAcquireSpinLockAtDpcLevel(&Adapter->Lock);
+
+    PC(Packet)->Context = Adapter->TDPackets;
+    Adapter->TDPackets  = Packet;
+
+    KeReleaseSpinLockFromDpcLevel(&Adapter->Lock);
+}
+
+
+NDIS_STATUS ProtocolReceive(
+    NDIS_HANDLE BindingContext,
+    NDIS_HANDLE MacReceiveContext,
+    PVOID HeaderBuffer,
+    UINT HeaderBufferSize,
+    PVOID LookaheadBuffer,
+    UINT LookaheadBufferSize,
+    UINT PacketSize)
+/*
+ * FUNCTION: Called by NDIS when a packet has been received on the physical link
+ * ARGUMENTS:
+ *     BindingContext      = Pointer to a device context (LAN_ADAPTER)
+ *     MacReceiveContext   = Handle used by underlying NIC driver
+ *     HeaderBuffer        = Pointer to a buffer containing the packet header
+ *     HeaderBufferSize    = Number of bytes in HeaderBuffer
+ *     LookaheadBuffer     = Pointer to a buffer containing buffered packet data
+ *     LookaheadBufferSize = Size of LookaheadBuffer. May be less than asked for
+ *     PacketSize          = Overall size of the packet (not including header)
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    USHORT EType;
+    UINT PacketType;
+    IP_PACKET IPPacket;
+    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
+    PETH_HEADER EHeader  = (PETH_HEADER)HeaderBuffer;
+
+    TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+    if ((Adapter->State != LAN_STATE_STARTED) ||
+        HeaderBufferSize < Adapter->HeaderSize)
+        /* Adapter is not started or the header was too small */
+        return NDIS_STATUS_NOT_ACCEPTED;
+
+    if (Adapter->Media == NdisMedium802_3) {
+        /* Ethernet and IEEE 802.3 frames can be destinguished by
+           looking at the IEEE 802.3 length field. This field is
+           less than or equal to 1500 for a valid IEEE 802.3 frame
+           and larger than 1500 is it's a valid Ether-Type value.
+           See RFC 1122, section 2.3.3 for more information */
+        if (((EType = EHeader->EType) != ETYPE_IPv4) && (EType != ETYPE_ARP))
+            return NDIS_STATUS_NOT_ACCEPTED;
+        /* We use Ether-Type constants to destinguish packets */
+        PacketType = EType;
+    } else
+        /* FIXME: Support other medias */
+        return NDIS_STATUS_NOT_ACCEPTED;
+
+    if (LookaheadBufferSize < PacketSize) {
+        NDIS_STATUS NdisStatus;
+        PNDIS_PACKET NdisPacket;
+        UINT BytesTransferred;
+        
+        /* Get transfer data packet */
+
+        KeAcquireSpinLockAtDpcLevel(&Adapter->Lock);
+
+        NdisPacket = Adapter->TDPackets;
+        if (NdisPacket == (PNDIS_PACKET)NULL) {
+            /* We don't have a free packet descriptor. Drop the packet */
+            KeReleaseSpinLockFromDpcLevel(&Adapter->Lock);
+            return NDIS_STATUS_SUCCESS;
+        }
+        Adapter->TDPackets = PC(NdisPacket)->Context;
+
+        KeReleaseSpinLockFromDpcLevel(&Adapter->Lock);
+
+        /* Get the data */
+        NdisTransferData(&NdisStatus, Adapter->NdisHandle,
+            MacReceiveContext, 0, PacketSize,
+            NdisPacket, &BytesTransferred);
+        if (NdisStatus != NDIS_STATUS_PENDING)
+            ProtocolTransferDataComplete(BindingContext,
+                NdisPacket, NdisStatus, BytesTransferred);
+
+        return NDIS_STATUS_SUCCESS;
+    }
+
+    /* We got all the data in the lookahead buffer */
+    RtlZeroMemory(&IPPacket, sizeof(IPPacket));
+    IPPacket.Header    = LookaheadBuffer;
+    IPPacket.TotalSize = PacketSize;
+
+    switch (PacketType) {
+        case ETYPE_IPv4:
+        case ETYPE_IPv6:
+            IPReceive(Adapter->Context, &IPPacket);
+            break;
+        case ETYPE_ARP:
+            ARPReceive(Adapter->Context, &IPPacket);
+            break;
+        default:
+            break;
+    }
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+
+VOID ProtocolReceiveComplete(
+    NDIS_HANDLE BindingContext)
+/*
+ * FUNCTION: Called by NDIS when we're done receiving data
+ * ARGUMENTS:
+ *     BindingContext = Pointer to a device context (LAN_ADAPTER)
+ */
+{
+    TI_DbgPrint(MID_TRACE, ("Called.\n"));
+}
+
+
+VOID ProtocolStatus(
+    NDIS_HANDLE BindingContext,
+    NDIS_STATUS GenerelStatus,
+    PVOID StatusBuffer,
+    UINT StatusBufferSize)
+/*
+ * FUNCTION: Called by NDIS when the underlying driver has changed state
+ * ARGUMENTS:
+ *     BindingContext   = Pointer to a device context (LAN_ADAPTER)
+ *     GenerelStatus    = A generel status code
+ *     StatusBuffer     = Pointer to a buffer with medium-specific data
+ *     StatusBufferSize = Number of bytes in StatusBuffer
+ */
+{
+    TI_DbgPrint(MID_TRACE, ("Called.\n"));
+}
+
+
+VOID ProtocolStatusComplete(
+    NDIS_HANDLE NdisBindingContext)
+/*
+ * FUNCTION: Called by NDIS when a status-change has occurred
+ * ARGUMENTS:
+ *     BindingContext = Pointer to a device context (LAN_ADAPTER)
+ */
+{
+    TI_DbgPrint(MID_TRACE, ("Called.\n"));
+}
+
+
+VOID LANTransmit(
+    PVOID Context,
+    PNDIS_PACKET NdisPacket,
+    UINT Offset,
+    PVOID LinkAddress,
+    USHORT Type)
+/*
+ * FUNCTION: Transmits a packet
+ * ARGUMENTS:
+ *     Context     = Pointer to context information (LAN_ADAPTER)
+ *     NdisPacket  = Pointer to NDIS packet to send
+ *     Offset      = Offset in packet where data starts
+ *     LinkAddress = Pointer to link address of destination (NULL = broadcast)
+ *     Type        = LAN protocol type (LAN_PROTO_*)
+ */
+{
+    NDIS_STATUS NdisStatus;
+    PETH_HEADER EHeader;
+    PVOID Data;
+    PLAN_ADAPTER Adapter = (PLAN_ADAPTER)Context;
+
+    TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+    /* NDIS send routines don't have an offset argument so we
+       must offset the data in upper layers and adjust the
+       packet here. We save the offset in the packet context
+       area so it can be undone before we release the packet */
+    Data = AdjustPacket(NdisPacket, Offset, Adapter->HeaderSize);
+    PC(NdisPacket)->DLOffset = Offset;
+
+    if (Adapter->State == LAN_STATE_STARTED) {
+        switch (Adapter->Media) {
+        case NdisMedium802_3:
+            EHeader = (PETH_HEADER)Data;
+    
+            if (LinkAddress)
+                /* Unicast address */
+                RtlCopyMemory(EHeader->DstAddr, LinkAddress, IEEE_802_ADDR_LENGTH);
+             else
+                /* Broadcast address */
+                RtlFillMemory(EHeader->DstAddr, IEEE_802_ADDR_LENGTH, 0xFF);
+
+            RtlCopyMemory(EHeader->SrcAddr, Adapter->HWAddress, IEEE_802_ADDR_LENGTH);
+
+            switch (Type) {
+                case LAN_PROTO_IPv4:
+                    EHeader->EType = ETYPE_IPv4;
+                    break;
+                case LAN_PROTO_ARP:
+                    EHeader->EType = ETYPE_ARP;
+                    break;
+                case LAN_PROTO_IPv6:
+                    EHeader->EType = ETYPE_IPv6;
+                    break;
+                default:
+#if DBG
+                    /* Should not happen */
+                    TI_DbgPrint(MIN_TRACE, ("Unknown LAN protocol.\n"));
+
+                    ProtocolSendComplete((NDIS_HANDLE)Context, NdisPacket, NDIS_STATUS_FAILURE);
+#endif
+                    return;
+            }
+            break;
+
+        default:
+            /* FIXME: Support other medias */
+            break;
+        }
+       
+        NdisSend(&NdisStatus, Adapter->NdisHandle, NdisPacket);
+        if (NdisStatus != NDIS_STATUS_PENDING)
+            ProtocolSendComplete((NDIS_HANDLE)Context, NdisPacket, NdisStatus);
+    } else
+        ProtocolSendComplete((NDIS_HANDLE)Context, NdisPacket, NDIS_STATUS_CLOSED);
+}
+
+
+VOID BindAdapter(
+    PLAN_ADAPTER Adapter)
+/*
+ * FUNCTION: Binds a LAN adapter to IP layer
+ * ARGUMENTS:
+ *     Adapter = Pointer to LAN_ADAPTER structure
+ * NOTES:
+ *    We set the lookahead buffer size, set the packet filter and
+ *    bind the adapter to IP layer
+ */
+{
+    INT i;
+    PIP_INTERFACE IF;
+    PIP_ADDRESS Address;
+    PNDIS_PACKET Packet;
+    NDIS_STATUS NdisStatus;
+    LLIP_BIND_INFO BindInfo;
+    ULONG Lookahead = LOOKAHEAD_SIZE;
+
+    TI_DbgPrint(MID_TRACE, ("Called.\n"));
+
+    Adapter->State = LAN_STATE_OPENING;
+
+    NdisStatus = NDISCall(Adapter, NdisRequestSetInformation,
+        OID_GEN_CURRENT_LOOKAHEAD, &Lookahead, sizeof(ULONG));
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        TI_DbgPrint(MID_TRACE, ("Could not set lookahead buffer size (0x%X).\n", NdisStatus));
+        return;
+    }
+
+    /* Allocate packets for NdisTransferData */
+    /* FIXME: How many should we allocate? */
+    Adapter->TDPackets = NULL;
+    for (i = 0; i < 2; i++) {
+        Packet              = AllocateTDPacket(Adapter);
+        PC(Packet)->Context = Adapter->TDPackets;
+        Adapter->TDPackets  = Packet;
+        if (!Packet) {
+            TI_DbgPrint(MID_TRACE, ("Could not allocate transfer data packet (out of resources).\n"));
+            FreeTDPackets(Adapter);
+            return;
+        }
+    }
+
+    /* Bind the adapter to IP layer */
+    BindInfo.Context       = Adapter;
+    BindInfo.HeaderSize    = Adapter->HeaderSize;
+    BindInfo.MinFrameSize  = Adapter->MinFrameSize;
+    BindInfo.MTU           = Adapter->MTU;
+    BindInfo.Address       = (PUCHAR)&Adapter->HWAddress;
+    BindInfo.AddressLength = Adapter->HWAddressLength;
+    BindInfo.Transmit      = LANTransmit;
+
+    IF = IPCreateInterface(&BindInfo);
+    if (!IF) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        FreeTDPackets(Adapter);
+        return;
+    }
+
+    /* FIXME: Get address from registry.
+       For now just use a private address, eg. 10.0.0.10 */
+    Address = AddrBuildIPv4(0x0A00000A);
+    if (!Address) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        FreeTDPackets(Adapter);
+        IPDestroyInterface(Adapter->Context);
+        return;
+    }
+    /* Create a net table entry for this interface */
+    if (!IPCreateNTE(IF, Address, 8)) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        FreeTDPackets(Adapter);
+        IPDestroyInterface(IF);
+        return;
+    }
+
+    /* Reference the interface for the NTE. The reference for
+       the address is just passed on to the NTE */
+    ReferenceObject(IF);
+
+    /* Register interface with IP layer */
+    IPRegisterInterface(IF);
+
+    /* Set packet filter so we can send and receive packets */
+    NdisStatus = NDISCall(Adapter, NdisRequestSetInformation,
+        OID_GEN_CURRENT_PACKET_FILTER, &Adapter->PacketFilter, sizeof(UINT));
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        TI_DbgPrint(MID_TRACE, ("Could not set packet filter (0x%X).\n", NdisStatus));
+        FreeTDPackets(Adapter);
+        IPDestroyInterface(IF);
+        return;
+    }
+
+    Adapter->Context = IF;
+
+    Adapter->State = LAN_STATE_STARTED;
+}
+
+
+VOID UnbindAdapter(
+    PLAN_ADAPTER Adapter)
+/*
+ * FUNCTION: Unbinds a LAN adapter from IP layer
+ * ARGUMENTS:
+ *     Adapter = Pointer to LAN_ADAPTER structure
+ */
+{
+    TI_DbgPrint(MID_TRACE, ("Called.\n"));
+
+    if (Adapter->State == LAN_STATE_STARTED) {
+        PIP_INTERFACE IF = Adapter->Context;
+
+        IPUnregisterInterface(IF);
+
+        IPDestroyInterface(IF);
+
+        /* Free transfer data packets */
+        FreeTDPackets(Adapter);
+    }
+}
+
+
+NDIS_STATUS LANRegisterAdapter(
+    PNDIS_STRING AdapterName,
+    PLAN_ADAPTER *Adapter)
+/*
+ * FUNCTION: Registers protocol with an NDIS adapter
+ * ARGUMENTS:
+ *     AdapterName = Pointer to string with name of adapter to register
+ *     Adapter     = Address of pointer to a LAN_ADAPTER structure
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    PLAN_ADAPTER IF;
+    NDIS_STATUS NdisStatus;
+    NDIS_STATUS OpenStatus;
+    UINT MediaIndex;
+    NDIS_MEDIUM MediaArray[MAX_MEDIA];
+    UINT AddressOID;
+    UINT Speed;
+
+    TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+    IF = ExAllocatePool(NonPagedPool, sizeof(LAN_ADAPTER));
+    if (!IF)
+        return NDIS_STATUS_RESOURCES;
+
+    RtlZeroMemory(IF, sizeof(LAN_ADAPTER));
+
+    /* Put adapter in stopped state */
+    IF->State = LAN_STATE_STOPPED;
+
+    /* Initialize protecting spin lock */
+    KeInitializeSpinLock(&IF->Lock);
+
+    KeInitializeEvent(&IF->Event, SynchronizationEvent, FALSE);
+
+    /* Initialize array with media IDs we support */
+    MediaArray[MEDIA_ETH] = NdisMedium802_3;
+
+    /* Open the adapter. */
+    NdisOpenAdapter(&NdisStatus, &OpenStatus, &IF->NdisHandle, &MediaIndex,
+        MediaArray, MAX_MEDIA, NdisProtocolHandle, IF, AdapterName, 0, NULL);
+
+    /* Wait until the adapter is opened */
+    if (NdisStatus == NDIS_STATUS_PENDING)
+        KeWaitForSingleObject(&IF->Event, UserRequest, KernelMode, FALSE, NULL);
+    else if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        ExFreePool(IF);
+        return NdisStatus;
+    }
+
+    IF->Media = MediaArray[MediaIndex];
+
+    /* Fill LAN_ADAPTER structure with some adapter specific information */
+    switch (IF->Media) {
+    case NdisMedium802_3:
+        IF->HWAddressLength = IEEE_802_ADDR_LENGTH;
+        IF->BCastMask       = BCAST_ETH_MASK;
+        IF->BCastCheck      = BCAST_ETH_CHECK;
+        IF->BCastOffset     = BCAST_ETH_OFFSET;
+        IF->HeaderSize      = sizeof(ETH_HEADER);
+        IF->MinFrameSize    = 60;
+        AddressOID          = OID_802_3_CURRENT_ADDRESS;
+        IF->PacketFilter    = 
+            NDIS_PACKET_TYPE_BROADCAST |
+            NDIS_PACKET_TYPE_DIRECTED  |
+            NDIS_PACKET_TYPE_MULTICAST;
+        break;
+
+    default:
+        /* Unsupported media */
+        TI_DbgPrint(MIN_TRACE, ("Unsupported media.\n"));
+        ExFreePool(IF);
+        return NDIS_STATUS_NOT_SUPPORTED;
+    }
+
+    /* Get maximum frame size */
+    NdisStatus = NDISCall(IF, NdisRequestQueryInformation,
+        OID_GEN_MAXIMUM_FRAME_SIZE, &IF->MTU, sizeof(UINT));
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        ExFreePool(IF);
+        return NdisStatus;
+    }
+
+    /* Get maximum packet size */
+    NdisStatus = NDISCall(IF, NdisRequestQueryInformation,
+        OID_GEN_MAXIMUM_TOTAL_SIZE, &IF->MaxPacketSize, sizeof(UINT));
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        TI_DbgPrint(MIN_TRACE, ("Query for maximum packet size failed.\n"));
+        ExFreePool(IF);
+        return NdisStatus;
+    }
+
+    /* Get maximum number of packets we can pass to NdisSend(Packets) at one time */
+    NdisStatus = NDISCall(IF, NdisRequestQueryInformation,
+        OID_GEN_MAXIMUM_SEND_PACKETS, &IF->MaxSendPackets, sizeof(UINT));
+    if (NdisStatus != NDIS_STATUS_SUCCESS)
+        /* Legacy NIC drivers may not support this query, if it fails we
+           assume it can send at least one packet per call to NdisSend(Packets) */
+        IF->MaxSendPackets = 1;
+
+    /* Get current hardware address */
+    NdisStatus = NDISCall(IF, NdisRequestQueryInformation, AddressOID,
+        IF->HWAddress, IF->HWAddressLength);
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        TI_DbgPrint(MIN_TRACE, ("Query for current hardware address failed.\n"));
+        ExFreePool(IF);
+        return NdisStatus;
+    }
+
+    /* Get maximum link speed */
+    NdisStatus = NDISCall(IF, NdisRequestQueryInformation,
+        OID_GEN_LINK_SPEED, &Speed, sizeof(UINT));
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        TI_DbgPrint(MIN_TRACE, ("Query for maximum link speed failed.\n"));
+        ExFreePool(IF);
+        return NdisStatus;
+    }
+
+    /* Convert returned link speed to bps (it is in 100bps increments) */
+    IF->Speed = Speed * 100L;
+
+    *Adapter = IF;
+
+    /* Add adapter to the adapter list */
+    IF->Next = Adapters;
+    Adapters = IF;
+
+    /* Bind adapter to IP layer */
+    BindAdapter(IF);
+
+    TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+
+NDIS_STATUS LANUnregisterAdapter(
+    PLAN_ADAPTER Adapter)
+/*
+ * FUNCTION: Unregisters protocol with NDIS adapter
+ * ARGUMENTS:
+ *     Adapter = Pointer to a LAN_ADAPTER structure
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    KIRQL OldIrql;
+    NDIS_HANDLE NdisHandle;
+    PLAN_ADAPTER IF, PrevIF;
+    BOOLEAN Found = FALSE;
+    NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;
+
+    TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+    /* Search the adapter list for the specified adapter and remove it */
+    IF = Adapters;
+    if (IF) {
+        if (Adapter != Adapters) {
+            PrevIF = IF;
+            while ((IF) && (!Found)) {
+                if (IF == Adapter) {
+                    /* We've found the adapter, now remove it from the list */
+                    PrevIF->Next = IF->Next;
+                    Found = TRUE;
+                }
+                PrevIF = IF;
+                IF = IF->Next;
+            }
+        } else {
+            Adapters = NULL;
+            Found    = TRUE;
+        }
+    }
+    if (!Found) {
+        TI_DbgPrint(MIN_TRACE, ("Leaving (adapter was not in list).\n"));
+        return NDIS_STATUS_ADAPTER_NOT_FOUND;
+    }
+
+    /* Unbind adapter from IP layer */
+    UnbindAdapter(Adapter);
+
+    KeAcquireSpinLock(&Adapter->Lock, &OldIrql);
+    NdisHandle = Adapter->NdisHandle;
+    if (NdisHandle) {
+        Adapter->NdisHandle = NULL;
+        KeReleaseSpinLock(&Adapter->Lock, OldIrql);
+
+        NdisCloseAdapter(&NdisStatus, NdisHandle);
+        if (NdisStatus == NDIS_STATUS_PENDING) {
+            KeWaitForSingleObject(&Adapter->Event,
+                UserRequest, KernelMode, FALSE, NULL);
+            NdisStatus = Adapter->NdisStatus;
+        }
+    } else
+        KeReleaseSpinLock(&Adapter->Lock, OldIrql);
+
+    FreeAdapter(Adapter);
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+
+NTSTATUS LANRegisterProtocol(
+    PSTRING Name)
+/*
+ * FUNCTION: Registers this protocol driver with NDIS
+ * ARGUMENTS:
+ *     Name = Name of this protocol driver
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    NDIS_STATUS NdisStatus;
+    NDIS_PROTOCOL_CHARACTERISTICS ProtChars;
+
+    /* Set up protocol characteristics */
+    RtlZeroMemory(&ProtChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
+    ProtChars.MajorNdisVersion            = NDIS_VERSION_MAJOR;
+    ProtChars.MinorNdisVersion            = NDIS_VERSION_MINOR;
+    ProtChars.Name.Length                 = Name->Length;
+    ProtChars.Name.Buffer                 = (PVOID)Name->Buffer;
+    ProtChars.OpenAdapterCompleteHandler  = ProtocolOpenAdapterComplete;
+    ProtChars.CloseAdapterCompleteHandler = ProtocolCloseAdapterComplete;
+    ProtChars.ResetCompleteHandler        = ProtocolResetComplete;
+    ProtChars.RequestCompleteHandler      = ProtocolRequestComplete;
+    ProtChars.SendCompleteHandler         = ProtocolSendComplete;
+    ProtChars.TransferDataCompleteHandler = ProtocolTransferDataComplete;
+    ProtChars.ReceiveHandler              = ProtocolReceive;
+    ProtChars.ReceiveCompleteHandler      = ProtocolReceiveComplete;
+    ProtChars.StatusHandler               = ProtocolStatus;
+    ProtChars.StatusCompleteHandler       = ProtocolStatusComplete;
+
+       /* Try to register protocol */
+    NdisRegisterProtocol(
+        &NdisStatus, &NdisProtocolHandle, &ProtChars,
+        sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + Name->Length);
+    if (NdisStatus != NDIS_STATUS_SUCCESS)
+        return (NTSTATUS)NdisStatus;
+
+    ProtocolRegistered = TRUE;
+
+    return STATUS_SUCCESS;
+}
+
+
+VOID LANUnregisterProtocol(
+    VOID)
+/*
+ * FUNCTION: Unregisters this protocol driver with NDIS
+ * NOTES: Does not care wether we are already registered
+ */
+{
+    if (ProtocolRegistered) {
+        NDIS_STATUS NdisStatus;
+
+        while (Adapters)
+            NdisStatus = LANUnregisterAdapter(Adapters);
+
+        NdisDeregisterProtocol(&NdisStatus, NdisProtocolHandle);
+        ProtocolRegistered = FALSE;
+    }
+}
+
+/* EOF */