/* * 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 "precomp.h" #include #include #include UINT TransferDataCalled = 0; UINT TransferDataCompleteCalled = 0; #define CCS_ROOT L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet" #define TCPIP_GUID L"{4D36E972-E325-11CE-BFC1-08002BE10318}" typedef struct _LAN_WQ_ITEM { LIST_ENTRY ListEntry; PNDIS_PACKET Packet; PLAN_ADAPTER Adapter; UINT BytesTransferred; BOOLEAN LegacyReceive; } LAN_WQ_ITEM, *PLAN_WQ_ITEM; typedef struct _RECONFIGURE_CONTEXT { ULONG State; PLAN_ADAPTER Adapter; } RECONFIGURE_CONTEXT, *PRECONFIGURE_CONTEXT; NDIS_HANDLE NdisProtocolHandle = (NDIS_HANDLE)NULL; BOOLEAN ProtocolRegistered = FALSE; LIST_ENTRY AdapterListHead; KSPIN_LOCK AdapterListLock; 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; } /* Used by legacy ProtocolReceive for packet type */ NDIS_STATUS GetPacketTypeFromHeaderBuffer(PLAN_ADAPTER Adapter, PVOID HeaderBuffer, ULONG HeaderBufferSize, PULONG PacketType) { PETH_HEADER EthHeader = HeaderBuffer; if (HeaderBufferSize < Adapter->HeaderSize) { TI_DbgPrint(DEBUG_DATALINK, ("Runt frame (size %d).\n", HeaderBufferSize)); return NDIS_STATUS_NOT_ACCEPTED; } switch (Adapter->Media) { case NdisMedium802_3: /* Ethernet and IEEE 802.3 frames can be distinguished 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 EtherType value. See RFC 1122, section 2.3.3 for more information */ *PacketType = EthHeader->EType; break; default: TI_DbgPrint(MIN_TRACE, ("Unsupported media.\n")); /* FIXME: Support other medias */ return NDIS_STATUS_NOT_ACCEPTED; } TI_DbgPrint(DEBUG_DATALINK, ("EtherType (0x%X).\n", *PacketType)); return NDIS_STATUS_SUCCESS; } /* Used by ProtocolReceivePacket for packet type */ NDIS_STATUS GetPacketTypeFromNdisPacket(PLAN_ADAPTER Adapter, PNDIS_PACKET NdisPacket, PULONG PacketType) { PVOID HeaderBuffer; ULONG BytesCopied; NDIS_STATUS Status; HeaderBuffer = ExAllocatePoolWithTag(NonPagedPool, Adapter->HeaderSize, HEADER_TAG); if (!HeaderBuffer) return NDIS_STATUS_RESOURCES; /* Copy the media header */ BytesCopied = CopyPacketToBuffer(HeaderBuffer, NdisPacket, 0, Adapter->HeaderSize); if (BytesCopied != Adapter->HeaderSize) { /* Runt frame */ ExFreePoolWithTag(HeaderBuffer, HEADER_TAG); TI_DbgPrint(DEBUG_DATALINK, ("Runt frame (size %d).\n", BytesCopied)); return NDIS_STATUS_NOT_ACCEPTED; } Status = GetPacketTypeFromHeaderBuffer(Adapter, HeaderBuffer, BytesCopied, PacketType); ExFreePoolWithTag(HeaderBuffer, HEADER_TAG); return Status; } VOID FreeAdapter( PLAN_ADAPTER Adapter) /* * FUNCTION: Frees memory for a LAN_ADAPTER structure * ARGUMENTS: * Adapter = Pointer to LAN_ADAPTER structure to free */ { ExFreePoolWithTag(Adapter, LAN_ADAPTER_TAG); } NTSTATUS TcpipLanGetDwordOid ( PIP_INTERFACE Interface, NDIS_OID Oid, PULONG Result ) { /* Get maximum frame size */ if( Interface->Context ) { return NDISCall((PLAN_ADAPTER)Interface->Context, NdisRequestQueryInformation, Oid, Result, sizeof(ULONG)); } else switch( Oid ) { /* Loopback Case */ case OID_GEN_HARDWARE_STATUS: *Result = NdisHardwareStatusReady; return STATUS_SUCCESS; case OID_GEN_MEDIA_CONNECT_STATUS: *Result = NdisMediaStateConnected; return STATUS_SUCCESS; default: return STATUS_INVALID_PARAMETER; } } VOID NTAPI 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(DEBUG_DATALINK, ("Called.\n")); Adapter->NdisStatus = Status; KeSetEvent(&Adapter->Event, 0, FALSE); } VOID NTAPI 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(DEBUG_DATALINK, ("Called.\n")); Adapter->NdisStatus = Status; KeSetEvent(&Adapter->Event, 0, FALSE); } VOID NTAPI 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 */ { PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); Adapter->NdisStatus = Status; KeSetEvent(&Adapter->Event, 0, FALSE); } VOID NTAPI 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; TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); /* Save status of request and signal an event */ Adapter->NdisStatus = Status; KeSetEvent(&Adapter->Event, 0, FALSE); } VOID NTAPI 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 */ { FreeNdisPacket(Packet); } VOID LanReceiveWorker( PVOID Context ) { ULONG PacketType; PLAN_WQ_ITEM WorkItem = (PLAN_WQ_ITEM)Context; PNDIS_PACKET Packet; PLAN_ADAPTER Adapter; UINT BytesTransferred; IP_PACKET IPPacket; BOOLEAN LegacyReceive; PIP_INTERFACE Interface; TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); Packet = WorkItem->Packet; Adapter = WorkItem->Adapter; BytesTransferred = WorkItem->BytesTransferred; LegacyReceive = WorkItem->LegacyReceive; ExFreePoolWithTag(WorkItem, WQ_CONTEXT_TAG); Interface = Adapter->Context; IPInitializePacket(&IPPacket, 0); IPPacket.NdisPacket = Packet; IPPacket.ReturnPacket = !LegacyReceive; if (LegacyReceive) { /* Packet type is precomputed */ PacketType = PC(IPPacket.NdisPacket)->PacketType; /* Data is at position 0 */ IPPacket.Position = 0; /* Packet size is determined by bytes transferred */ IPPacket.TotalSize = BytesTransferred; } else { /* Determine packet type from media header */ if (GetPacketTypeFromNdisPacket(Adapter, IPPacket.NdisPacket, &PacketType) != NDIS_STATUS_SUCCESS) { /* Bad packet */ IPPacket.Free(&IPPacket); return; } /* Data is at the end of the media header */ IPPacket.Position = Adapter->HeaderSize; /* Calculate packet size (excluding media header) */ NdisQueryPacketLength(IPPacket.NdisPacket, &IPPacket.TotalSize); } TI_DbgPrint (DEBUG_DATALINK, ("Ether Type = %x Total = %d\n", PacketType, IPPacket.TotalSize)); /* Update interface stats */ Interface->Stats.InBytes += IPPacket.TotalSize + Adapter->HeaderSize; /* NDIS packet is freed in all of these cases */ switch (PacketType) { case ETYPE_IPv4: case ETYPE_IPv6: TI_DbgPrint(MID_TRACE,("Received IP Packet\n")); IPReceive(Adapter->Context, &IPPacket); break; case ETYPE_ARP: TI_DbgPrint(MID_TRACE,("Received ARP Packet\n")); ARPReceive(Adapter->Context, &IPPacket); break; default: IPPacket.Free(&IPPacket); break; } } VOID LanSubmitReceiveWork( NDIS_HANDLE BindingContext, PNDIS_PACKET Packet, UINT BytesTransferred, BOOLEAN LegacyReceive) { PLAN_WQ_ITEM WQItem = ExAllocatePoolWithTag(NonPagedPool, sizeof(LAN_WQ_ITEM), WQ_CONTEXT_TAG); PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; TI_DbgPrint(DEBUG_DATALINK,("called\n")); if (!WQItem) return; WQItem->Packet = Packet; WQItem->Adapter = Adapter; WQItem->BytesTransferred = BytesTransferred; WQItem->LegacyReceive = LegacyReceive; if (!ChewCreate( LanReceiveWorker, WQItem )) ExFreePoolWithTag(WQItem, WQ_CONTEXT_TAG); } VOID NTAPI 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 */ { ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); TI_DbgPrint(DEBUG_DATALINK,("called\n")); TransferDataCompleteCalled++; ASSERT(TransferDataCompleteCalled <= TransferDataCalled); if( Status != NDIS_STATUS_SUCCESS ) return; LanSubmitReceiveWork(BindingContext, Packet, BytesTransferred, TRUE); } INT NTAPI ProtocolReceivePacket( NDIS_HANDLE BindingContext, PNDIS_PACKET NdisPacket) { PLAN_ADAPTER Adapter = BindingContext; if (Adapter->State != LAN_STATE_STARTED) { TI_DbgPrint(DEBUG_DATALINK, ("Adapter is stopped.\n")); return 0; } LanSubmitReceiveWork(BindingContext, NdisPacket, 0, /* Unused */ FALSE); /* Hold 1 reference on this packet */ return 1; } NDIS_STATUS NTAPI 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 */ { ULONG PacketType; UINT BytesTransferred; PCHAR BufferData; NDIS_STATUS NdisStatus; PNDIS_PACKET NdisPacket; PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; TI_DbgPrint(DEBUG_DATALINK, ("Called. (packetsize %d)\n",PacketSize)); if (Adapter->State != LAN_STATE_STARTED) { TI_DbgPrint(DEBUG_DATALINK, ("Adapter is stopped.\n")); return NDIS_STATUS_NOT_ACCEPTED; } if (HeaderBufferSize < Adapter->HeaderSize) { TI_DbgPrint(DEBUG_DATALINK, ("Runt frame received.\n")); return NDIS_STATUS_NOT_ACCEPTED; } NdisStatus = GetPacketTypeFromHeaderBuffer(Adapter, HeaderBuffer, HeaderBufferSize, &PacketType); if (NdisStatus != NDIS_STATUS_SUCCESS) return NDIS_STATUS_NOT_ACCEPTED; TI_DbgPrint(DEBUG_DATALINK, ("Adapter: %x (MTU %d)\n", Adapter, Adapter->MTU)); /* Get a transfer data packet */ NdisStatus = AllocatePacketWithBuffer( &NdisPacket, NULL, PacketSize ); if( NdisStatus != NDIS_STATUS_SUCCESS ) { return NDIS_STATUS_NOT_ACCEPTED; } PC(NdisPacket)->PacketType = PacketType; TI_DbgPrint(DEBUG_DATALINK, ("pretransfer LookaheadBufferSize %d packsize %d\n",LookaheadBufferSize,PacketSize)); GetDataPtr( NdisPacket, 0, &BufferData, &PacketSize ); TransferDataCalled++; if (LookaheadBufferSize == PacketSize) { /* Optimized code path for packets that are fully contained in * the lookahead buffer. */ NdisCopyLookaheadData(BufferData, LookaheadBuffer, LookaheadBufferSize, Adapter->MacOptions); } else { NdisTransferData(&NdisStatus, Adapter->NdisHandle, MacReceiveContext, 0, PacketSize, NdisPacket, &BytesTransferred); } TI_DbgPrint(DEBUG_DATALINK, ("Calling complete\n")); if (NdisStatus != NDIS_STATUS_PENDING) ProtocolTransferDataComplete(BindingContext, NdisPacket, NdisStatus, PacketSize); TI_DbgPrint(DEBUG_DATALINK, ("leaving\n")); return NDIS_STATUS_SUCCESS; } VOID NTAPI 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(DEBUG_DATALINK, ("Called.\n")); } BOOLEAN ReadIpConfiguration(PIP_INTERFACE Interface) { OBJECT_ATTRIBUTES ObjectAttributes; HANDLE ParameterHandle; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; ULONG KeyValueInfoLength; WCHAR Buffer[150]; UNICODE_STRING IPAddress = RTL_CONSTANT_STRING(L"IPAddress"); UNICODE_STRING Netmask = RTL_CONSTANT_STRING(L"SubnetMask"); UNICODE_STRING Gateway = RTL_CONSTANT_STRING(L"DefaultGateway"); UNICODE_STRING EnableDhcp = RTL_CONSTANT_STRING(L"EnableDHCP"); UNICODE_STRING Prefix = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\"); UNICODE_STRING TcpipRegistryPath; UNICODE_STRING RegistryDataU; ANSI_STRING RegistryDataA; ULONG Unused; NTSTATUS Status; IP_ADDRESS DefaultMask, Router; AddrInitIPv4(&DefaultMask, 0); TcpipRegistryPath.MaximumLength = sizeof(WCHAR) * 150; TcpipRegistryPath.Length = 0; TcpipRegistryPath.Buffer = Buffer; /* Build the registry path */ RtlAppendUnicodeStringToString(&TcpipRegistryPath, &Prefix); RtlAppendUnicodeStringToString(&TcpipRegistryPath, &Interface->Name); InitializeObjectAttributes(&ObjectAttributes, &TcpipRegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); /* Open a handle to the adapter parameters */ Status = ZwOpenKey(&ParameterHandle, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { return FALSE; } else { KeyValueInfoLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + 16 * sizeof(WCHAR); KeyValueInfo = ExAllocatePoolWithTag(PagedPool, KeyValueInfoLength, KEY_VALUE_TAG); if (!KeyValueInfo) { ZwClose(ParameterHandle); return FALSE; } /* Read the EnableDHCP entry */ Status = ZwQueryValueKey(ParameterHandle, &EnableDhcp, KeyValuePartialInformation, KeyValueInfo, KeyValueInfoLength, &Unused); if (NT_SUCCESS(Status) && KeyValueInfo->DataLength == sizeof(ULONG) && (*(PULONG)KeyValueInfo->Data) == 0) { RegistryDataU.MaximumLength = KeyValueInfoLength - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); RegistryDataU.Buffer = (PWCHAR)KeyValueInfo->Data; /* Read the IP address */ Status = ZwQueryValueKey(ParameterHandle, &IPAddress, KeyValuePartialInformation, KeyValueInfo, KeyValueInfoLength, &Unused); if (NT_SUCCESS(Status)) { RegistryDataU.Length = KeyValueInfo->DataLength; Status = RtlUnicodeStringToAnsiString(&RegistryDataA, &RegistryDataU, TRUE); if (NT_SUCCESS(Status)) { AddrInitIPv4(&Interface->Unicast, inet_addr(RegistryDataA.Buffer)); RtlFreeAnsiString(&RegistryDataA); } } Status = ZwQueryValueKey(ParameterHandle, &Netmask, KeyValuePartialInformation, KeyValueInfo, KeyValueInfoLength, &Unused); if (NT_SUCCESS(Status)) { RegistryDataU.Length = KeyValueInfo->DataLength; Status = RtlUnicodeStringToAnsiString(&RegistryDataA, &RegistryDataU, TRUE); if (NT_SUCCESS(Status)) { AddrInitIPv4(&Interface->Netmask, inet_addr(RegistryDataA.Buffer)); RtlFreeAnsiString(&RegistryDataA); } } /* We have to wait until both IP address and subnet mask * are read to add the interface route, but we must do it * before we add the default gateway */ if (!AddrIsUnspecified(&Interface->Unicast) && !AddrIsUnspecified(&Interface->Netmask)) IPAddInterfaceRoute(Interface); /* Read default gateway info */ Status = ZwQueryValueKey(ParameterHandle, &Gateway, KeyValuePartialInformation, KeyValueInfo, KeyValueInfoLength, &Unused); if (NT_SUCCESS(Status)) { RegistryDataU.Length = KeyValueInfo->DataLength; Status = RtlUnicodeStringToAnsiString(&RegistryDataA, &RegistryDataU, TRUE); if (NT_SUCCESS(Status)) { AddrInitIPv4(&Router, inet_addr(RegistryDataA.Buffer)); if (!AddrIsUnspecified(&Router)) RouterCreateRoute(&DefaultMask, &DefaultMask, &Router, Interface, 1); RtlFreeAnsiString(&RegistryDataA); } } } ExFreePoolWithTag(KeyValueInfo, KEY_VALUE_TAG); ZwClose(ParameterHandle); } return TRUE; } BOOLEAN ReconfigureAdapter(PRECONFIGURE_CONTEXT Context) { PLAN_ADAPTER Adapter = Context->Adapter; PIP_INTERFACE Interface = Adapter->Context; //NDIS_STATUS NdisStatus; IP_ADDRESS DefaultMask; /* Initialize the default unspecified address (0.0.0.0) */ AddrInitIPv4(&DefaultMask, 0); if (Context->State == LAN_STATE_STARTED && !Context->Adapter->CompletingReset) { /* Read the IP configuration */ ReadIpConfiguration(Interface); /* Compute the broadcast address */ Interface->Broadcast.Type = IP_ADDRESS_V4; Interface->Broadcast.Address.IPv4Address = Interface->Unicast.Address.IPv4Address | ~Interface->Netmask.Address.IPv4Address; } else if (!Context->Adapter->CompletingReset) { /* Clear IP configuration */ Interface->Unicast = DefaultMask; Interface->Netmask = DefaultMask; Interface->Broadcast = DefaultMask; /* Remove all interface routes */ RouterRemoveRoutesForInterface(Interface); /* Destroy all cached neighbors */ NBDestroyNeighborsForInterface(Interface); } Context->Adapter->CompletingReset = FALSE; /* Update the IP and link status information cached in TCP */ TCPUpdateInterfaceIPInformation(Interface); TCPUpdateInterfaceLinkStatus(Interface); /* We're done here if the adapter isn't connected */ if (Context->State != LAN_STATE_STARTED) { Adapter->State = Context->State; return TRUE; } /* NDIS Bug! */ #if 0 /* Get maximum link speed */ NdisStatus = NDISCall(Adapter, NdisRequestQueryInformation, OID_GEN_LINK_SPEED, &Interface->Speed, sizeof(UINT)); if (!NT_SUCCESS(NdisStatus)) Interface->Speed = IP_DEFAULT_LINK_SPEED; Adapter->Speed = Interface->Speed * 100L; /* Get maximum frame size */ NdisStatus = NDISCall(Adapter, NdisRequestQueryInformation, OID_GEN_MAXIMUM_FRAME_SIZE, &Adapter->MTU, sizeof(UINT)); if (NdisStatus != NDIS_STATUS_SUCCESS) return FALSE; Interface->MTU = Adapter->MTU; /* Get maximum packet size */ NdisStatus = NDISCall(Adapter, NdisRequestQueryInformation, OID_GEN_MAXIMUM_TOTAL_SIZE, &Adapter->MaxPacketSize, sizeof(UINT)); if (NdisStatus != NDIS_STATUS_SUCCESS) return FALSE; #endif Adapter->State = Context->State; return TRUE; } VOID ReconfigureAdapterWorker(PVOID Context) { PRECONFIGURE_CONTEXT ReconfigureContext = Context; /* Complete the reconfiguration asynchronously */ ReconfigureAdapter(ReconfigureContext); /* Free the context */ ExFreePool(ReconfigureContext); } VOID NTAPI ProtocolStatus( NDIS_HANDLE BindingContext, NDIS_STATUS GeneralStatus, PVOID StatusBuffer, UINT StatusBufferSize) /* * FUNCTION: Called by NDIS when the underlying driver has changed state * ARGUMENTS: * BindingContext = Pointer to a device context (LAN_ADAPTER) * GeneralStatus = A general status code * StatusBuffer = Pointer to a buffer with medium-specific data * StatusBufferSize = Number of bytes in StatusBuffer */ { PLAN_ADAPTER Adapter = BindingContext; PRECONFIGURE_CONTEXT Context; TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); /* Ignore the status indication if we have no context yet. We'll get another later */ if (!Adapter->Context) return; Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(RECONFIGURE_CONTEXT), CONTEXT_TAG); if (!Context) return; Context->Adapter = Adapter; switch(GeneralStatus) { case NDIS_STATUS_MEDIA_CONNECT: DbgPrint("NDIS_STATUS_MEDIA_CONNECT\n"); if (Adapter->State == LAN_STATE_STARTED) { ExFreePoolWithTag(Context, CONTEXT_TAG); return; } Context->State = LAN_STATE_STARTED; break; case NDIS_STATUS_MEDIA_DISCONNECT: DbgPrint("NDIS_STATUS_MEDIA_DISCONNECT\n"); if (Adapter->State == LAN_STATE_STOPPED) { ExFreePoolWithTag(Context, CONTEXT_TAG); return; } Context->State = LAN_STATE_STOPPED; break; case NDIS_STATUS_RESET_START: Adapter->OldState = Adapter->State; Adapter->State = LAN_STATE_RESETTING; /* Nothing else to do here */ ExFreePoolWithTag(Context, CONTEXT_TAG); return; case NDIS_STATUS_RESET_END: Adapter->CompletingReset = TRUE; Context->State = Adapter->OldState; break; default: DbgPrint("Unhandled status: %x", GeneralStatus); ExFreePoolWithTag(Context, CONTEXT_TAG); return; } /* Queue the work item */ if (!ChewCreate(ReconfigureAdapterWorker, Context)) ExFreePoolWithTag(Context, CONTEXT_TAG); } VOID NTAPI 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(DEBUG_DATALINK, ("Called.\n")); } NDIS_STATUS NTAPI ProtocolPnPEvent( NDIS_HANDLE NdisBindingContext, PNET_PNP_EVENT PnPEvent) { switch(PnPEvent->NetEvent) { case NetEventSetPower: DbgPrint("Device transitioned to power state %ld\n", PnPEvent->Buffer); return NDIS_STATUS_SUCCESS; case NetEventQueryPower: DbgPrint("Device wants to go into power state %ld\n", PnPEvent->Buffer); return NDIS_STATUS_SUCCESS; case NetEventQueryRemoveDevice: DbgPrint("Device is about to be removed\n"); return NDIS_STATUS_SUCCESS; case NetEventCancelRemoveDevice: DbgPrint("Device removal cancelled\n"); return NDIS_STATUS_SUCCESS; default: DbgPrint("Unhandled event type: %ld\n", PnPEvent->NetEvent); return NDIS_STATUS_SUCCESS; } } VOID NTAPI ProtocolBindAdapter( OUT PNDIS_STATUS Status, IN NDIS_HANDLE BindContext, IN PNDIS_STRING DeviceName, IN PVOID SystemSpecific1, IN PVOID SystemSpecific2) /* * FUNCTION: Called by NDIS during NdisRegisterProtocol to set up initial * bindings, and periodically thereafter as new adapters come online * ARGUMENTS: * Status: Return value to NDIS * BindContext: Handle provided by NDIS to track pending binding operations * DeviceName: Name of the miniport device to bind to * SystemSpecific1: Pointer to a registry path with protocol-specific configuration information * SystemSpecific2: Unused & must not be touched */ { TI_DbgPrint(DEBUG_DATALINK, ("Called with registry path %wZ for %wZ\n", SystemSpecific1, DeviceName)); *Status = LANRegisterAdapter(DeviceName, SystemSpecific1); } 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; PCHAR Data, OldData; UINT Size, OldSize; PLAN_ADAPTER Adapter = (PLAN_ADAPTER)Context; KIRQL OldIrql; PNDIS_PACKET XmitPacket; PIP_INTERFACE Interface = Adapter->Context; TI_DbgPrint(DEBUG_DATALINK, ("Called( NdisPacket %x, Offset %d, Adapter %x )\n", NdisPacket, Offset, Adapter)); if (Adapter->State != LAN_STATE_STARTED) { (*PC(NdisPacket)->DLComplete)(PC(NdisPacket)->Context, NdisPacket, NDIS_STATUS_NOT_ACCEPTED); return; } TI_DbgPrint(DEBUG_DATALINK, ("Adapter Address [%02x %02x %02x %02x %02x %02x]\n", Adapter->HWAddress[0] & 0xff, Adapter->HWAddress[1] & 0xff, Adapter->HWAddress[2] & 0xff, Adapter->HWAddress[3] & 0xff, Adapter->HWAddress[4] & 0xff, Adapter->HWAddress[5] & 0xff)); GetDataPtr( NdisPacket, 0, &OldData, &OldSize ); NdisStatus = AllocatePacketWithBuffer(&XmitPacket, NULL, OldSize + Adapter->HeaderSize); if (NdisStatus != NDIS_STATUS_SUCCESS) { (*PC(NdisPacket)->DLComplete)(PC(NdisPacket)->Context, NdisPacket, NDIS_STATUS_RESOURCES); return; } GetDataPtr(XmitPacket, 0, &Data, &Size); RtlCopyMemory(Data + Adapter->HeaderSize, OldData, OldSize); (*PC(NdisPacket)->DLComplete)(PC(NdisPacket)->Context, NdisPacket, NDIS_STATUS_SUCCESS); 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: ASSERT(FALSE); return; } break; default: /* FIXME: Support other medias */ break; } TI_DbgPrint( MID_TRACE, ("LinkAddress: %x\n", LinkAddress)); if( LinkAddress ) { TI_DbgPrint ( MID_TRACE, ("Link Address [%02x %02x %02x %02x %02x %02x]\n", ((PCHAR)LinkAddress)[0] & 0xff, ((PCHAR)LinkAddress)[1] & 0xff, ((PCHAR)LinkAddress)[2] & 0xff, ((PCHAR)LinkAddress)[3] & 0xff, ((PCHAR)LinkAddress)[4] & 0xff, ((PCHAR)LinkAddress)[5] & 0xff)); } if (Adapter->MTU < Size) { /* This is NOT a pointer. MSDN explicitly says so. */ NDIS_PER_PACKET_INFO_FROM_PACKET(NdisPacket, TcpLargeSendPacketInfo) = (PVOID)((ULONG_PTR)Adapter->MTU); } /* Update interface stats */ Interface->Stats.OutBytes += Size; TcpipAcquireSpinLock( &Adapter->Lock, &OldIrql ); TI_DbgPrint(MID_TRACE, ("NdisSend\n")); NdisSend(&NdisStatus, Adapter->NdisHandle, XmitPacket); TI_DbgPrint(MID_TRACE, ("NdisSend %s\n", NdisStatus == NDIS_STATUS_PENDING ? "Pending" : "Complete")); TcpipReleaseSpinLock( &Adapter->Lock, OldIrql ); /* I had a talk with vizzini: these really ought to be here. * we're supposed to see these completed by ndis *only* when * status_pending is returned. Note that this is different from * the situation with IRPs. */ if (NdisStatus != NDIS_STATUS_PENDING) ProtocolSendComplete((NDIS_HANDLE)Context, XmitPacket, NdisStatus); } static NTSTATUS OpenRegistryKey( PNDIS_STRING RegistryPath, PHANDLE RegHandle ) { OBJECT_ATTRIBUTES Attributes; NTSTATUS Status; InitializeObjectAttributes(&Attributes, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0); Status = ZwOpenKey(RegHandle, KEY_ALL_ACCESS, &Attributes); return Status; } static NTSTATUS ReadStringFromRegistry( HANDLE RegHandle, PWCHAR RegistryValue, PUNICODE_STRING String ) { UNICODE_STRING ValueName; UNICODE_STRING UnicodeString; NTSTATUS Status; ULONG ResultLength; UCHAR buf[1024]; PKEY_VALUE_PARTIAL_INFORMATION Information = (PKEY_VALUE_PARTIAL_INFORMATION)buf; RtlInitUnicodeString(&ValueName, RegistryValue); Status = ZwQueryValueKey(RegHandle, &ValueName, KeyValuePartialInformation, Information, sizeof(buf), &ResultLength); if (!NT_SUCCESS(Status)) return Status; /* IP address is stored as a REG_MULTI_SZ - we only pay attention to the first one though */ TI_DbgPrint(MIN_TRACE, ("Information DataLength: 0x%x\n", Information->DataLength)); UnicodeString.Buffer = (PWCHAR)&Information->Data; UnicodeString.Length = Information->DataLength - sizeof(WCHAR); UnicodeString.MaximumLength = Information->DataLength; String->Buffer = (PWCHAR)ExAllocatePoolWithTag( NonPagedPool, UnicodeString.MaximumLength + sizeof(WCHAR), REG_STR_TAG ); if( !String->Buffer ) return STATUS_NO_MEMORY; String->MaximumLength = UnicodeString.MaximumLength; RtlCopyUnicodeString( String, &UnicodeString ); return STATUS_SUCCESS; } /* * Utility to copy and append two unicode strings. * * IN OUT PUNICODE_STRING ResultFirst -> First string and result * IN PUNICODE_STRING Second -> Second string to append * IN BOOL Deallocate -> TRUE: Deallocate First string before * overwriting. * * Returns NTSTATUS. */ NTSTATUS NTAPI AppendUnicodeString(PUNICODE_STRING ResultFirst, PUNICODE_STRING Second, BOOLEAN Deallocate) { NTSTATUS Status; UNICODE_STRING Ustr = *ResultFirst; PWSTR new_string = ExAllocatePoolWithTag (PagedPool, (ResultFirst->Length + Second->Length + sizeof(WCHAR)), TEMP_STRING_TAG); if( !new_string ) { return STATUS_NO_MEMORY; } memcpy( new_string, ResultFirst->Buffer, ResultFirst->Length ); memcpy( new_string + ResultFirst->Length / sizeof(WCHAR), Second->Buffer, Second->Length ); if( Deallocate ) RtlFreeUnicodeString(ResultFirst); ResultFirst->Length = Ustr.Length + Second->Length; ResultFirst->MaximumLength = ResultFirst->Length; new_string[ResultFirst->Length / sizeof(WCHAR)] = 0; Status = RtlCreateUnicodeString(ResultFirst,new_string) ? STATUS_SUCCESS : STATUS_NO_MEMORY; ExFreePoolWithTag(new_string, TEMP_STRING_TAG); return Status; } static NTSTATUS CheckForDeviceDesc( PUNICODE_STRING EnumKeyName, PUNICODE_STRING TargetKeyName, PUNICODE_STRING Name, PUNICODE_STRING DeviceDesc ) { UNICODE_STRING RootDevice = { 0, 0, NULL }, LinkageKeyName = { 0, 0, NULL }; UNICODE_STRING DescKeyName = { 0, 0, NULL }, Linkage = { 0, 0, NULL }; UNICODE_STRING BackSlash = { 0, 0, NULL }; HANDLE DescKey = NULL, LinkageKey = NULL; NTSTATUS Status; TI_DbgPrint(DEBUG_DATALINK,("EnumKeyName %wZ\n", EnumKeyName)); RtlInitUnicodeString(&BackSlash, L"\\"); RtlInitUnicodeString(&Linkage, L"\\Linkage"); RtlInitUnicodeString(&DescKeyName, L""); AppendUnicodeString( &DescKeyName, EnumKeyName, FALSE ); AppendUnicodeString( &DescKeyName, &BackSlash, TRUE ); AppendUnicodeString( &DescKeyName, TargetKeyName, TRUE ); RtlInitUnicodeString(&LinkageKeyName, L""); AppendUnicodeString( &LinkageKeyName, &DescKeyName, FALSE ); AppendUnicodeString( &LinkageKeyName, &Linkage, TRUE ); Status = OpenRegistryKey( &LinkageKeyName, &LinkageKey ); if( !NT_SUCCESS(Status) ) goto cleanup; Status = ReadStringFromRegistry( LinkageKey, L"RootDevice", &RootDevice ); if( !NT_SUCCESS(Status) ) goto cleanup; if( RtlCompareUnicodeString( &RootDevice, Name, TRUE ) == 0 ) { Status = OpenRegistryKey( &DescKeyName, &DescKey ); if( !NT_SUCCESS(Status) ) goto cleanup; Status = ReadStringFromRegistry( DescKey, L"DriverDesc", DeviceDesc ); if( !NT_SUCCESS(Status) ) goto cleanup; TI_DbgPrint(DEBUG_DATALINK,("ADAPTER DESC: %wZ\n", DeviceDesc)); } else Status = STATUS_UNSUCCESSFUL; cleanup: RtlFreeUnicodeString( &RootDevice ); RtlFreeUnicodeString( &LinkageKeyName ); RtlFreeUnicodeString( &DescKeyName ); if( LinkageKey ) ZwClose( LinkageKey ); if( DescKey ) ZwClose( DescKey ); TI_DbgPrint(DEBUG_DATALINK,("Returning %x\n", Status)); return Status; } static NTSTATUS FindDeviceDescForAdapter( PUNICODE_STRING Name, PUNICODE_STRING DeviceDesc ) { UNICODE_STRING EnumKeyName, TargetKeyName; HANDLE EnumKey; NTSTATUS Status; ULONG i; KEY_BASIC_INFORMATION *Kbio = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEY_BASIC_INFORMATION), KBIO_TAG); ULONG KbioLength = sizeof(KEY_BASIC_INFORMATION), ResultLength; RtlInitUnicodeString( DeviceDesc, NULL ); if( !Kbio ) return STATUS_INSUFFICIENT_RESOURCES; RtlInitUnicodeString (&EnumKeyName, CCS_ROOT L"\\Control\\Class\\" TCPIP_GUID); Status = OpenRegistryKey( &EnumKeyName, &EnumKey ); if( !NT_SUCCESS(Status) ) { TI_DbgPrint(DEBUG_DATALINK,("Couldn't open Enum key %wZ: %x\n", &EnumKeyName, Status)); ExFreePoolWithTag( Kbio, KBIO_TAG ); return Status; } for( i = 0; NT_SUCCESS(Status); i++ ) { Status = ZwEnumerateKey( EnumKey, i, KeyBasicInformation, Kbio, KbioLength, &ResultLength ); if( Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW ) { ExFreePoolWithTag( Kbio, KBIO_TAG ); KbioLength = ResultLength; Kbio = ExAllocatePoolWithTag( NonPagedPool, KbioLength, KBIO_TAG ); if( !Kbio ) { TI_DbgPrint(DEBUG_DATALINK,("Failed to allocate memory\n")); ZwClose( EnumKey ); return STATUS_NO_MEMORY; } Status = ZwEnumerateKey( EnumKey, i, KeyBasicInformation, Kbio, KbioLength, &ResultLength ); if( !NT_SUCCESS(Status) ) { TI_DbgPrint(DEBUG_DATALINK,("Couldn't enum key child %d\n", i)); ZwClose( EnumKey ); ExFreePoolWithTag( Kbio, KBIO_TAG ); return Status; } } if( NT_SUCCESS(Status) ) { TargetKeyName.Length = TargetKeyName.MaximumLength = Kbio->NameLength; TargetKeyName.Buffer = Kbio->Name; Status = CheckForDeviceDesc ( &EnumKeyName, &TargetKeyName, Name, DeviceDesc ); if( NT_SUCCESS(Status) ) { ZwClose( EnumKey ); ExFreePoolWithTag( Kbio, KBIO_TAG ); return Status; } else Status = STATUS_SUCCESS; } } ZwClose( EnumKey ); ExFreePoolWithTag( Kbio, KBIO_TAG ); return STATUS_UNSUCCESSFUL; } VOID GetName( PUNICODE_STRING RegistryKey, PUNICODE_STRING OutName ) { PWCHAR Ptr; UNICODE_STRING PartialRegistryKey; PartialRegistryKey.Buffer = RegistryKey->Buffer + wcslen(CCS_ROOT L"\\Services\\"); Ptr = PartialRegistryKey.Buffer; while( *Ptr != L'\\' && ((PCHAR)Ptr) < ((PCHAR)RegistryKey->Buffer) + RegistryKey->Length ) Ptr++; PartialRegistryKey.Length = PartialRegistryKey.MaximumLength = (Ptr - PartialRegistryKey.Buffer) * sizeof(WCHAR); RtlInitUnicodeString( OutName, L"" ); AppendUnicodeString( OutName, &PartialRegistryKey, FALSE ); } BOOLEAN BindAdapter( PLAN_ADAPTER Adapter, PNDIS_STRING RegistryPath) /* * 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 */ { PIP_INTERFACE IF; NDIS_STATUS NdisStatus; LLIP_BIND_INFO BindInfo; ULONG Lookahead = LOOKAHEAD_SIZE; NTSTATUS Status; NDIS_MEDIA_STATE MediaState; TI_DbgPrint(DEBUG_DATALINK, ("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(DEBUG_DATALINK, ("Could not set lookahead buffer size (0x%X).\n", NdisStatus)); return FALSE; } /* Bind the adapter to IP layer */ BindInfo.Context = Adapter; BindInfo.HeaderSize = Adapter->HeaderSize; BindInfo.MinFrameSize = Adapter->MinFrameSize; BindInfo.Address = (PUCHAR)&Adapter->HWAddress; BindInfo.AddressLength = Adapter->HWAddressLength; BindInfo.Transmit = LANTransmit; IF = IPCreateInterface(&BindInfo); if (!IF) { TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); return FALSE; } /* * Query per-adapter configuration from the registry * In case anyone is curious: there *is* an Ndis configuration api * for this sort of thing, but it doesn't really support things like * REG_MULTI_SZ very well, and there is a note in the DDK that says that * protocol drivers developed for win2k and above just use the native * services (ZwOpenKey, etc). */ GetName( RegistryPath, &IF->Name ); Status = FindDeviceDescForAdapter( &IF->Name, &IF->Description ); if (!NT_SUCCESS(Status)) { TI_DbgPrint(MIN_TRACE, ("Failed to get device description.\n")); IPDestroyInterface(IF); return FALSE; } TI_DbgPrint(DEBUG_DATALINK,("Adapter Description: %wZ\n", &IF->Description)); /* Get maximum link speed */ NdisStatus = NDISCall(Adapter, NdisRequestQueryInformation, OID_GEN_LINK_SPEED, &IF->Speed, sizeof(UINT)); if (!NT_SUCCESS(NdisStatus)) IF->Speed = IP_DEFAULT_LINK_SPEED; Adapter->Speed = IF->Speed * 100L; /* Get maximum frame size */ NdisStatus = NDISCall(Adapter, NdisRequestQueryInformation, OID_GEN_MAXIMUM_FRAME_SIZE, &Adapter->MTU, sizeof(UINT)); if (NdisStatus != NDIS_STATUS_SUCCESS) return FALSE; IF->MTU = Adapter->MTU; /* Get maximum packet size */ NdisStatus = NDISCall(Adapter, NdisRequestQueryInformation, OID_GEN_MAXIMUM_TOTAL_SIZE, &Adapter->MaxPacketSize, sizeof(UINT)); if (NdisStatus != NDIS_STATUS_SUCCESS) return FALSE; /* Register interface with IP layer */ IPRegisterInterface(IF); /* Store adapter context */ Adapter->Context = IF; /* Get the media state */ NdisStatus = NDISCall(Adapter, NdisRequestQueryInformation, OID_GEN_MEDIA_CONNECT_STATUS, &MediaState, sizeof(MediaState)); if (NdisStatus != NDIS_STATUS_SUCCESS) { TI_DbgPrint(DEBUG_DATALINK, ("Could not query media status (0x%X).\n", NdisStatus)); IPUnregisterInterface(IF); IPDestroyInterface(IF); return FALSE; } /* Indicate the current media state */ ProtocolStatus(Adapter, (MediaState == NdisMediaStateConnected) ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0); /* 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(DEBUG_DATALINK, ("Could not set packet filter (0x%X).\n", NdisStatus)); IPUnregisterInterface(IF); IPDestroyInterface(IF); return FALSE; } return TRUE; } VOID UnbindAdapter( PLAN_ADAPTER Adapter) /* * FUNCTION: Unbinds a LAN adapter from IP layer * ARGUMENTS: * Adapter = Pointer to LAN_ADAPTER structure */ { TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); if (Adapter->State == LAN_STATE_STARTED) { PIP_INTERFACE IF = Adapter->Context; IPUnregisterInterface(IF); IPDestroyInterface(IF); } } NDIS_STATUS LANRegisterAdapter( PNDIS_STRING AdapterName, PNDIS_STRING RegistryPath) /* * 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; TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); IF = ExAllocatePoolWithTag(NonPagedPool, sizeof(LAN_ADAPTER), LAN_ADAPTER_TAG); if (!IF) { TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); 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; TI_DbgPrint(DEBUG_DATALINK,("opening adapter %wZ\n", AdapterName)); /* 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) { TI_DbgPrint(DEBUG_DATALINK,("denying adapter %wZ\n", AdapterName)); ExFreePoolWithTag(IF, LAN_ADAPTER_TAG); 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")); ExFreePoolWithTag(IF, LAN_ADAPTER_TAG); return NDIS_STATUS_NOT_SUPPORTED; } /* 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")); ExFreePoolWithTag(IF, LAN_ADAPTER_TAG); return NdisStatus; } /* Bind adapter to IP layer */ if( !BindAdapter(IF, RegistryPath) ) { TI_DbgPrint(DEBUG_DATALINK,("denying adapter %wZ (BindAdapter)\n", AdapterName)); ExFreePoolWithTag(IF, LAN_ADAPTER_TAG); return NDIS_STATUS_NOT_ACCEPTED; } /* Add adapter to the adapter list */ ExInterlockedInsertTailList(&AdapterListHead, &IF->ListEntry, &AdapterListLock); TI_DbgPrint(DEBUG_DATALINK, ("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; NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS; TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); /* Unlink the adapter from the list */ RemoveEntryList(&Adapter->ListEntry); /* Unbind adapter from IP layer */ UnbindAdapter(Adapter); TcpipAcquireSpinLock(&Adapter->Lock, &OldIrql); NdisHandle = Adapter->NdisHandle; if (NdisHandle) { Adapter->NdisHandle = NULL; TcpipReleaseSpinLock(&Adapter->Lock, OldIrql); NdisCloseAdapter(&NdisStatus, NdisHandle); if (NdisStatus == NDIS_STATUS_PENDING) { TcpipWaitForSingleObject(&Adapter->Event, UserRequest, KernelMode, FALSE, NULL); NdisStatus = Adapter->NdisStatus; } } else TcpipReleaseSpinLock(&Adapter->Lock, OldIrql); FreeAdapter(Adapter); return NdisStatus; } VOID NTAPI LANUnregisterProtocol(VOID) /* * FUNCTION: Unregisters this protocol driver with NDIS * NOTES: Does not care wether we are already registered */ { TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); if (ProtocolRegistered) { NDIS_STATUS NdisStatus; PLIST_ENTRY CurrentEntry; PLIST_ENTRY NextEntry; PLAN_ADAPTER Current; KIRQL OldIrql; TcpipAcquireSpinLock(&AdapterListLock, &OldIrql); /* Search the list and remove every adapter we find */ CurrentEntry = AdapterListHead.Flink; while (CurrentEntry != &AdapterListHead) { NextEntry = CurrentEntry->Flink; Current = CONTAINING_RECORD(CurrentEntry, LAN_ADAPTER, ListEntry); /* Unregister it */ LANUnregisterAdapter(Current); CurrentEntry = NextEntry; } TcpipReleaseSpinLock(&AdapterListLock, OldIrql); NdisDeregisterProtocol(&NdisStatus, NdisProtocolHandle); ProtocolRegistered = FALSE; } } VOID NTAPI ProtocolUnbindAdapter( PNDIS_STATUS Status, NDIS_HANDLE ProtocolBindingContext, NDIS_HANDLE UnbindContext) { /* We don't pend any unbinding so we can just ignore UnbindContext */ *Status = LANUnregisterAdapter((PLAN_ADAPTER)ProtocolBindingContext); } NTSTATUS LANRegisterProtocol( PNDIS_STRING 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; TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); InitializeListHead(&AdapterListHead); KeInitializeSpinLock(&AdapterListLock); /* 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 = Name->Buffer; ProtChars.Name.MaximumLength = Name->MaximumLength; ProtChars.OpenAdapterCompleteHandler = ProtocolOpenAdapterComplete; ProtChars.CloseAdapterCompleteHandler = ProtocolCloseAdapterComplete; ProtChars.ResetCompleteHandler = ProtocolResetComplete; ProtChars.RequestCompleteHandler = ProtocolRequestComplete; ProtChars.SendCompleteHandler = ProtocolSendComplete; ProtChars.TransferDataCompleteHandler = ProtocolTransferDataComplete; ProtChars.ReceivePacketHandler = ProtocolReceivePacket; ProtChars.ReceiveHandler = ProtocolReceive; ProtChars.ReceiveCompleteHandler = ProtocolReceiveComplete; ProtChars.StatusHandler = ProtocolStatus; ProtChars.StatusCompleteHandler = ProtocolStatusComplete; ProtChars.BindAdapterHandler = ProtocolBindAdapter; ProtChars.PnPEventHandler = ProtocolPnPEvent; ProtChars.UnbindAdapterHandler = ProtocolUnbindAdapter; ProtChars.UnloadHandler = LANUnregisterProtocol; /* Try to register protocol */ NdisRegisterProtocol(&NdisStatus, &NdisProtocolHandle, &ProtChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); if (NdisStatus != NDIS_STATUS_SUCCESS) { TI_DbgPrint(DEBUG_DATALINK, ("NdisRegisterProtocol failed, status 0x%x\n", NdisStatus)); return (NTSTATUS)NdisStatus; } ProtocolRegistered = TRUE; return STATUS_SUCCESS; } /* EOF */