diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/isnp/ipx/driver.c | 4219 |
1 files changed, 4219 insertions, 0 deletions
diff --git a/private/ntos/tdi/isnp/ipx/driver.c b/private/ntos/tdi/isnp/ipx/driver.c new file mode 100644 index 000000000..7c32cd0e3 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/driver.c @@ -0,0 +1,4219 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + + Sanjay Anand (SanjayAn) 18-Sept-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + + +PDEVICE IpxDevice = NULL; +PIPX_PADDING_BUFFER IpxPaddingBuffer = NULL; + +#if DBG + +UCHAR IpxTempDebugBuffer[150]; +ULONG IpxDebug = 0x0; +ULONG IpxMemoryDebug = 0xffffffd3; +UCHAR IpxDebugMemory[IPX_MEMORY_LOG_SIZE][64]; +PUCHAR IpxDebugMemoryLoc = IpxDebugMemory[0]; +PUCHAR IpxDebugMemoryEnd = IpxDebugMemory[IPX_MEMORY_LOG_SIZE]; + +VOID +IpxDebugMemoryLog( + IN PUCHAR FormatString, + ... +) + +{ + INT ArgLen; + va_list ArgumentPointer; + + va_start(ArgumentPointer, FormatString); + + // + // To avoid any overflows, copy this in a temp buffer first. + RtlZeroMemory (IpxTempDebugBuffer, 150); + ArgLen = vsprintf(IpxTempDebugBuffer, FormatString, ArgumentPointer); + va_end(ArgumentPointer); + + if ( ArgLen > 64 ) { + CTEAssert( FALSE ); + } else { + RtlZeroMemory (IpxDebugMemoryLoc, 64); + RtlCopyMemory( IpxDebugMemoryLoc, IpxTempDebugBuffer, ArgLen ); + + IpxDebugMemoryLoc += 64; + if (IpxDebugMemoryLoc >= IpxDebugMemoryEnd) { + IpxDebugMemoryLoc = IpxDebugMemory[0]; + } + } +} + + +DEFINE_LOCK_STRUCTURE(IpxMemoryInterlock); +MEMORY_TAG IpxMemoryTag[MEMORY_MAX]; + +DEFINE_LOCK_STRUCTURE(IpxGlobalInterlock); + +#endif + +#if DBG + +// +// Use for debug printouts +// + +PUCHAR FrameTypeNames[5] = { "Ethernet II", "802.3", "802.2", "SNAP", "Arcnet" }; +#define OutputFrameType(_Binding) \ + (((_Binding)->Adapter->MacInfo.MediumType == NdisMediumArcnet878_2) ? \ + FrameTypeNames[4] : \ + FrameTypeNames[(_Binding)->FrameType]) +#endif + + +#ifdef IPX_PACKET_LOG + +ULONG IpxPacketLogDebug = IPX_PACKET_LOG_RCV_OTHER | IPX_PACKET_LOG_SEND_OTHER; +USHORT IpxPacketLogSocket = 0; +DEFINE_LOCK_STRUCTURE(IpxPacketLogLock); +IPX_PACKET_LOG_ENTRY IpxPacketLog[IPX_PACKET_LOG_LENGTH]; +PIPX_PACKET_LOG_ENTRY IpxPacketLogLoc = IpxPacketLog; +PIPX_PACKET_LOG_ENTRY IpxPacketLogEnd = &IpxPacketLog[IPX_PACKET_LOG_LENGTH]; + +VOID +IpxLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID IpxHeader, + IN PVOID Data + ) + +{ + + CTELockHandle LockHandle; + PIPX_PACKET_LOG_ENTRY PacketLog; + LARGE_INTEGER TickCount; + ULONG DataLength; + + CTEGetLock (&IpxPacketLogLock, &LockHandle); + + PacketLog = IpxPacketLogLoc; + + ++IpxPacketLogLoc; + if (IpxPacketLogLoc >= IpxPacketLogEnd) { + IpxPacketLogLoc = IpxPacketLog; + } + *(UNALIGNED ULONG *)IpxPacketLogLoc->TimeStamp = 0x3e3d3d3d; // "===>" + + CTEFreeLock (&IpxPacketLogLock, LockHandle); + + RtlZeroMemory (PacketLog, sizeof(IPX_PACKET_LOG_ENTRY)); + + PacketLog->SendReceive = Send ? '>' : '<'; + + KeQueryTickCount(&TickCount); + _itoa (TickCount.LowPart % 100000, PacketLog->TimeStamp, 10); + + RtlCopyMemory(PacketLog->DestMac, DestMac, 6); + RtlCopyMemory(PacketLog->SrcMac, SrcMac, 6); + PacketLog->Length[0] = Length / 256; + PacketLog->Length[1] = Length % 256; + + if (Length < sizeof(IPX_HEADER)) { + RtlCopyMemory(&PacketLog->IpxHeader, IpxHeader, Length); + } else { + RtlCopyMemory(&PacketLog->IpxHeader, IpxHeader, sizeof(IPX_HEADER)); + } + + DataLength = Length - sizeof(IPX_HEADER); + if (DataLength < 14) { + RtlCopyMemory(PacketLog->Data, Data, DataLength); + } else { + RtlCopyMemory(PacketLog->Data, Data, 14); + } + +} /* IpxLogPacket */ + +#endif // IPX_PACKET_LOG + + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +// +// This is now shared with other modules +// +#ifndef _PNP_POWER +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, + IN PUNICODE_STRING RegistryPath + ); + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ); + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigAdapter, + IN ULONG FrameTypeIndex + ); + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ); +#endif _PNP_POWER + +VOID +IpxUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +IpxDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) + +// +// These routines can be called at any time in case of PnP. +// +#ifndef _PNP_POWER +#pragma alloc_text(INIT,IpxResolveAutoDetect) +#pragma alloc_text(INIT,IpxResolveBindingSets) +#pragma alloc_text(INIT,IpxBindToAdapter) +#endif + +#endif + +UCHAR VirtualNode[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + +// +// This prevents us from having a bss section. +// + +ULONG _setjmpexused = 0; + +ULONG IpxFailLoad = FALSE; + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the IPX ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of IPX's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + UINT SuccessfulOpens, ValidBindings; +#ifdef _PNP_POWER + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("NWLNKIPX"); +#else + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("IPX Transport"); +#endif + PDEVICE Device; + PBINDING Binding; + PADAPTER Adapter; + ULONG BindingCount, BindingIndex; + PBINDING * BindingArray; + PLIST_ENTRY p; + ULONG AnnouncedMaxDatagram, RealMaxDatagram, MaxLookahead; + ULONG LinkSpeed, MacOptions; + ULONG Temp; + UINT i; + BOOLEAN CountedWan; + + PCONFIG Config = NULL; + PBINDING_CONFIG ConfigBinding; + +#if 0 + DbgPrint ("IPX: FailLoad at %lx\n", &IpxFailLoad); + DbgBreakPoint(); + + if (IpxFailLoad) { + return STATUS_UNSUCCESSFUL; + } +#endif + + + // + // This ordering matters because we use it to quickly + // determine if packets are internally generated or not. + // + + CTEAssert (IDENTIFIER_NB < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_SPX < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_RIP < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_RIP_INTERNAL > IDENTIFIER_IPX); + + // + // We assume that this structure is not packet in between + // the fields. + // + + CTEAssert (FIELD_OFFSET (TDI_ADDRESS_IPX, Socket) + sizeof(USHORT) == 12); + + + // + // Initialize the Common Transport Environment. + // + + if (CTEInitialize() == 0) { + + IPX_DEBUG (DEVICE, ("CTEInitialize() failed\n")); + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 101, + STATUS_UNSUCCESSFUL, + NULL, + 0, + NULL); + return STATUS_UNSUCCESSFUL; + } + +#if DBG + CTEInitLock (&IpxGlobalInterlock); + CTEInitLock (&IpxMemoryInterlock); + for (i = 0; i < MEMORY_MAX; i++) { + IpxMemoryTag[i].Tag = i; + IpxMemoryTag[i].BytesAllocated = 0; + } +#endif +#ifdef IPX_PACKET_LOG + CTEInitLock (&IpxPacketLogLock); +#endif + +#ifdef IPX_OWN_PACKETS + CTEAssert (NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); +#endif + + IPX_DEBUG (DEVICE, ("IPX loaded\n")); + + // + // This allocates the CONFIG structure and returns + // it in Config. + // + + status = IpxGetConfiguration(DriverObject, RegistryPath, &Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed, it logged an error. + // + + PANIC (" Failed to initialize transport, IPX initialization failed.\n"); + return status; + + } + +#ifdef _PNP_POWER + // + // Initialize the TDI layer. + // + TdiInitialize(); +#endif + + // + // make ourselves known to the NDIS wrapper. + // + + status = IpxRegisterProtocol ((PNDIS_STRING)&ProtocolName); + + if (!NT_SUCCESS (status)) { + + IpxFreeConfiguration(Config); + PANIC ("IpxInitialize: RegisterProtocol failed!\n"); + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 607, + status, + NULL, + 0, + NULL); + + return status; + + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = IpxDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = IpxDispatchDeviceControl; + + DriverObject->DriverUnload = IpxUnload; + + SuccessfulOpens = 0; + + status = IpxCreateDevice( + DriverObject, + &Config->DeviceName, + Config->Parameters[CONFIG_RIP_TABLE_SIZE], + &Device); + + if (!NT_SUCCESS (status)) { + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_CREATE_DEVICE, + 801, + status, + NULL, + 0, + NULL); + + IpxFreeConfiguration(Config); + IpxDeregisterProtocol(); + return status; + } + + IpxDevice = Device; + + + // + // Save the relevant configuration parameters. + // + + Device->DedicatedRouter = (BOOLEAN)(Config->Parameters[CONFIG_DEDICATED_ROUTER] != 0); + Device->InitDatagrams = Config->Parameters[CONFIG_INIT_DATAGRAMS]; + Device->MaxDatagrams = Config->Parameters[CONFIG_MAX_DATAGRAMS]; + Device->RipAgeTime = Config->Parameters[CONFIG_RIP_AGE_TIME]; + Device->RipCount = Config->Parameters[CONFIG_RIP_COUNT]; + Device->RipTimeout = + ((Config->Parameters[CONFIG_RIP_TIMEOUT] * 500) + (RIP_GRANULARITY/2)) / + RIP_GRANULARITY; + Device->RipUsageTime = Config->Parameters[CONFIG_RIP_USAGE_TIME]; + Device->SourceRouteUsageTime = Config->Parameters[CONFIG_ROUTE_USAGE_TIME]; + Device->SocketUniqueness = Config->Parameters[CONFIG_SOCKET_UNIQUENESS]; + Device->SocketStart = (USHORT)Config->Parameters[CONFIG_SOCKET_START]; + Device->SocketEnd = (USHORT)Config->Parameters[CONFIG_SOCKET_END]; + Device->MemoryLimit = Config->Parameters[CONFIG_MAX_MEMORY_USAGE]; + Device->VerifySourceAddress = (BOOLEAN)(Config->Parameters[CONFIG_VERIFY_SOURCE_ADDRESS] != 0); + + Device->InitReceivePackets = (Device->InitDatagrams + 1) / 2; + Device->InitReceiveBuffers = (Device->InitDatagrams + 1) / 2; + + Device->MaxReceivePackets = 10; // BUGBUG: config this? + Device->MaxReceiveBuffers = 10; + +#ifdef _PNP_POWER + Device->InitBindings = 5; // BUGBUG: config this? + + // + // RAS max is 240 (?) + 10 max LAN + // + Device->MaxPoolBindings = 250; // BUGBUG: config this? +#endif + + // + // Have to reverse this. + // +#ifndef _PNP_POWER +// +// Look at this only when the first adapter appears. +// + Temp = Config->Parameters[CONFIG_VIRTUAL_NETWORK]; + Device->VirtualNetworkNumber = REORDER_ULONG (Temp); +#endif + + Device->VirtualNetworkOptional = (BOOLEAN)(Config->Parameters[CONFIG_VIRTUAL_OPTIONAL] != 0); + + Device->CurrentSocket = Device->SocketStart; + + Device->EthernetPadToEven = (BOOLEAN)(Config->Parameters[CONFIG_ETHERNET_PAD] != 0); + Device->EthernetExtraPadding = (Config->Parameters[CONFIG_ETHERNET_LENGTH] & 0xfffffffe) + 1; + + Device->SingleNetworkActive = (BOOLEAN)(Config->Parameters[CONFIG_SINGLE_NETWORK] != 0); + Device->DisableDialoutSap = (BOOLEAN)(Config->Parameters[CONFIG_DISABLE_DIALOUT_SAP] != 0); + Device->DisableDialinNetbios = (UCHAR)(Config->Parameters[CONFIG_DISABLE_DIALIN_NB]); + +#ifdef _PNP_POWER +// +// Used later to access the registry. +// + Device->RegistryPathBuffer = Config->RegistryPathBuffer; + Device->RegistryPath.Length = RegistryPath->Length; + Device->RegistryPath.MaximumLength = RegistryPath->MaximumLength; + Device->RegistryPath.Buffer = Device->RegistryPathBuffer; +#endif _PNP_POWER + + // + // ActiveNetworkWan will start as FALSE, which is correct. + // + + // + // Allocate our initial packet pool. We do not allocate + // receive and receive buffer pools until we need them, + // because in many cases we never do. + // + +#if BACK_FILL + IpxAllocateBackFillPool (Device); +#endif + + IpxAllocateSendPool (Device); + +#ifdef _PNP_POWER + IpxAllocateBindingPool (Device); +#endif + + // + // Allocate one 1-byte buffer for odd length packets. + // + + IpxPaddingBuffer = IpxAllocatePaddingBuffer(Device); + + if ( IpxPaddingBuffer == (PIPX_PADDING_BUFFER)NULL ) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + 801, + STATUS_INSUFFICIENT_RESOURCES, + NULL, + 0, + NULL); + + IpxFreeConfiguration(Config); + IpxDeregisterProtocol(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the loopback structures + // + IpxInitLoopback(); + +// +// All this will be done on appearance of adapters. +// + +#ifndef _PNP_POWER + + // + // Bind to all the configured adapters. + // + + InitializeListHead (&Device->InitialBindingList); + + p = Config->BindingList.Flink; + + while (p != &Config->BindingList) { + + ConfigBinding = CONTAINING_RECORD (p, BINDING_CONFIG, Linkage); + p = p->Flink; + + for (i = 0; i < ConfigBinding->FrameTypeCount; i++) { + + // + // If successful, this queues them on Device->InitialBindingList. + // + + status = IpxBindToAdapter (Device, ConfigBinding, i); + + // + // If this failed because the adapter could not be bound + // to, then don't try any more frame types on this adapter. + // For other failures we do try the other frame types. + // + + if (status == STATUS_DEVICE_DOES_NOT_EXIST) { + break; + } + + if (status != STATUS_SUCCESS) { + continue; + } + + if (ConfigBinding->AutoDetect[i]) { + Device->AutoDetect = TRUE; + } + + ++SuccessfulOpens; + + } + + } + + + IpxFreeConfiguration(Config); + + if (SuccessfulOpens == 0) { + + IpxDereferenceDevice (Device, DREF_CREATE); + + } else { + + IPX_DEFINE_SYNC_CONTEXT (SyncContext); + + // + // Allocate the device binding array and transfer those + // on the list to it. First count up the bindings. + // + + BindingCount = 0; + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + p = p->Flink) { + + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + Adapter = Binding->Adapter; + + if (Adapter->MacInfo.MediumAsync) { + Adapter->FirstWanNicId = (USHORT)(BindingCount+1); + Adapter->LastWanNicId = (USHORT)(BindingCount + Adapter->WanNicIdCount); + BindingCount += Adapter->WanNicIdCount; + } else { + ++BindingCount; + } + } + + BindingArray = (PBINDING *)IpxAllocateMemory ((BindingCount+1) * sizeof(BINDING), MEMORY_BINDING, "Binding array"); + + if (BindingArray == NULL) { + + while (!IsListEmpty (&Device->InitialBindingList)) { + p = RemoveHeadList (&Device->InitialBindingList); + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + IpxDestroyBinding (Binding); + } + + IpxDereferenceDevice (Device, DREF_CREATE); + SuccessfulOpens = 0; + goto InitFailed; + } + + RtlZeroMemory (BindingArray, (BindingCount+1) * sizeof(BINDING)); + + // + // Now walk the list transferring bindings to the array. + // + + BindingIndex = 1; + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + ) { + + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + + p = p->Flink; // we overwrite the linkage in here, so save it. + + BindingArray[BindingIndex] = Binding; + Binding->NicId = (USHORT)BindingIndex; + + if (Binding->ConfiguredNetworkNumber != 0) { + + // + // If the configured network number is non-zero, then + // use it, unless we are unable to insert a rip table + // entry for it (duplicates are OK because they will + // become binding set members -- BUGBUG: What if the + // duplicate is a different media or frame type, then + // it won't get noted as a binding set). + // + + status = RipInsertLocalNetwork( + Binding->ConfiguredNetworkNumber, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->Adapter->MediumSpeed) / Binding->Adapter->MediumSpeed)); + + if ((status == STATUS_SUCCESS) || + (status == STATUS_DUPLICATE_NAME)) { + + Binding->LocalAddress.NetworkAddress = Binding->ConfiguredNetworkNumber; + } + } + + // + // These are a union with the InitialLinkage fields. + // + + Binding->NextBinding = NULL; + Binding->CurrentSendBinding = NULL; + + Adapter = Binding->Adapter; + + if (Adapter->MacInfo.MediumAsync) { + CTEAssert (Adapter->FirstWanNicId == BindingIndex); + BindingIndex += Adapter->WanNicIdCount; + } else { + ++BindingIndex; + } + } + + CTEAssert (BindingIndex == BindingCount+1); + + Device->Bindings = BindingArray; + Device->BindingCount = BindingCount; + + + // + // Queue a request to discover our locally attached + // adapter addresses. This must succeed because we + // just allocated our send packet pool. We need + // to wait for this, either because we are + // auto-detecting or because we need to determine + // if there are multiple cards on the same network. + // + + KeInitializeEvent( + &Device->AutoDetectEvent, + NotificationEvent, + FALSE + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_RUNNING; + + // + // Make this 0; after we are done waiting, which means + // the packet has been completed, we set it to the + // correct value. + // + + Device->IncludedHeaderOffset = 0; + + IPX_BEGIN_SYNC (&SyncContext); + status = RipQueueRequest (0xffffffff, RIP_REQUEST); + IPX_END_SYNC (&SyncContext); + + CTEAssert (status == STATUS_PENDING); + + // + // This is set when this rip send completes. + // + + IPX_DEBUG (AUTO_DETECT, ("Waiting for AutoDetectEvent\n")); + + KeWaitForSingleObject( + &Device->AutoDetectEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_PROCESSING; + + // + // Now that we are done receiving responses, insert the + // current network number for every auto-detect binding + // to the rip database. + // + + for (i = 1; i <= Device->BindingCount; i++) { + + Binding = Device->Bindings[i]; + + // + // Skip empty WAN slots or bindings that were configured + // for a certain network number, we inserted those above. + // If no network number was detected, also skip it. + // + + if ((!Binding) || + (Binding->ConfiguredNetworkNumber != 0) || + (Binding->TentativeNetworkAddress == 0)) { + + continue; + } + + IPX_DEBUG (AUTO_DETECT, ("Final score for %lx on %lx is %d - %d\n", + REORDER_ULONG(Binding->TentativeNetworkAddress), + Binding, + Binding->MatchingResponses, + Binding->NonMatchingResponses)); + + // + // We don't care about the status. + // + + status = RipInsertLocalNetwork( + Binding->TentativeNetworkAddress, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)); + + if ((status != STATUS_SUCCESS) && + (status != STATUS_DUPLICATE_NAME)) { + + // + // We failed to insert, keep it at zero, hopefully + // we will be able to update later. + // + +#if DBG + DbgPrint ("IPX: Could not insert net %lx for binding %lx\n", + REORDER_ULONG(Binding->LocalAddress.NetworkAddress), + Binding); +#endif + CTEAssert (Binding->LocalAddress.NetworkAddress == 0); + + } else { + + Binding->LocalAddress.NetworkAddress = Binding->TentativeNetworkAddress; + } + + } + + ValidBindings = Device->BindingCount; + + if (Device->AutoDetect) { + + ValidBindings = IpxResolveAutoDetect (Device, ValidBindings, RegistryPath); + + } + + Device->ValidBindings = ValidBindings; + + // + // Now see if any bindings are actually on the same + // network. This sets Device->HighestExternalNicId + // and Device->HighestType20NicId. + // + + IpxResolveBindingSets (Device, ValidBindings); + + + // + // For multiple adapters, use the offset of the first...why not. + // + +#if 0 + Device->IncludedHeaderOffset = Device->Bindings[1]->DefHeaderSize; +#endif + + Device->IncludedHeaderOffset = MAC_HEADER_SIZE; + + // + // Success; see if there is a virtual network configured. + // + + if (Device->VirtualNetworkNumber != 0) { + + status = RipInsertLocalNetwork( + Device->VirtualNetworkNumber, + 0, // NIC ID + Device->Bindings[1]->Adapter->NdisBindingHandle, + 1); + + if (status != STATUS_SUCCESS) { + + // + // Log the appropriate error, then ignore the + // virtual network. If the error was + // INSUFFICIENT_RESOURCES, the RIP module + // will have already logged an error. + // + + if (status == STATUS_DUPLICATE_NAME) { + + IPX_DEBUG (AUTO_DETECT, ("Ignoring virtual network %lx, conflict\n", REORDER_ULONG (Device->VirtualNetworkNumber))); + + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_IPX_INTERNAL_NET_INVALID, + 0, + REORDER_ULONG (Device->VirtualNetworkNumber)); + } + + Device->VirtualNetworkNumber = 0; + goto NoVirtualNetwork; + + } + + Device->VirtualNetwork = TRUE; + Device->MultiCardZeroVirtual = FALSE; + RtlCopyMemory(Device->SourceAddress.NodeAddress, VirtualNode, 6); + Device->SourceAddress.NetworkAddress = Device->VirtualNetworkNumber; + + // + // This will get set to FALSE if RIP binds. + // + + Device->RipResponder = TRUE; + + } else { + +NoVirtualNetwork: + + Device->VirtualNetwork = FALSE; + + // + // See if we need to be set up for the fake + // virtual network. + // + + if (ValidBindings > 1) { + + CTEAssert (Device->VirtualNetworkOptional); + + // + // In this case we return as our local node the + // address of the first card. We will also only + // direct SAP sends to that card. + // + + Device->MultiCardZeroVirtual = TRUE; + + } else { + + Device->MultiCardZeroVirtual = FALSE; + } + + RtlCopyMemory(&Device->SourceAddress, &Device->Bindings[1]->LocalAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + + } + + + // + // Now get SapNicCount -- regular adapters are counted + // as one, but all the WAN lines together only count for one. + // We also calculate FirstLanNicId and FirstWanNicId here. + // + + CountedWan = FALSE; + Device->SapNicCount = 0; + + Device->FirstLanNicId = (USHORT)-1; + Device->FirstWanNicId = (USHORT)-1; + + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + + if (Device->Bindings[i]) { + + if (Device->Bindings[i]->Adapter->MacInfo.MediumAsync) { + + if (Device->FirstWanNicId == (USHORT)-1) { + Device->FirstWanNicId = i; + } + + if (CountedWan) { + continue; + } else { + CountedWan = TRUE; + } + + } else { + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = i; + } + + } + + } else { + + // + // NULL bindings are WANs and are not the first one, + // so don't count them. + // + + CTEAssert (Device->FirstWanNicId != -1); + CTEAssert (CountedWan); + continue; + } + + ++Device->SapNicCount; + + } + } + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = 1; + } + if (Device->FirstWanNicId == (USHORT)-1) { + Device->FirstWanNicId = 1; + } + + + // + // Calculate some values based on all the bindings. + // + + MaxLookahead = Device->Bindings[1]->MaxLookaheadData; // largest binding value + AnnouncedMaxDatagram = Device->Bindings[1]->AnnouncedMaxDatagramSize; // smallest binding value + RealMaxDatagram = Device->Bindings[1]->RealMaxDatagramSize; // smallest binding value + + if (Device->Bindings[1]->LineUp) { + LinkSpeed = Device->Bindings[1]->MediumSpeed; // smallest binding value + } else { + LinkSpeed = 0xffffffff; + } + MacOptions = Device->Bindings[1]->Adapter->MacInfo.MacOptions; // AND of binding values + + for (i = 2; i <= ValidBindings; i++) { + + Binding = Device->Bindings[i]; + + if (!Binding) { + continue; + } + + if (Binding->MaxLookaheadData > MaxLookahead) { + MaxLookahead = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < AnnouncedMaxDatagram) { + AnnouncedMaxDatagram = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < RealMaxDatagram) { + RealMaxDatagram = Binding->RealMaxDatagramSize; + } + + if (Binding->LineUp && (Binding->MediumSpeed < LinkSpeed)) { + LinkSpeed = Binding->MediumSpeed; + } + MacOptions &= Binding->Adapter->MacInfo.MacOptions; + + } + + Device->Information.MaxDatagramSize = AnnouncedMaxDatagram; + Device->RealMaxDatagramSize = RealMaxDatagram; + Device->Information.MaximumLookaheadData = MaxLookahead; + + // + // If we couldn't find anything better, use the speed from + // the first binding. + // + + if (LinkSpeed == 0xffffffff) { + Device->LinkSpeed = Device->Bindings[1]->MediumSpeed; + } else { + Device->LinkSpeed = LinkSpeed; + } + Device->MacOptions = MacOptions; + + Device->State = DEVICE_STATE_OPEN; + Device->AutoDetectState = AUTO_DETECT_STATE_DONE; + + IPX_DEBUG (DEVICE, ("Node is %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x, ", + Device->SourceAddress.NodeAddress[0], Device->SourceAddress.NodeAddress[1], + Device->SourceAddress.NodeAddress[2], Device->SourceAddress.NodeAddress[3], + Device->SourceAddress.NodeAddress[4], Device->SourceAddress.NodeAddress[5])); + IPX_DEBUG (DEVICE, ("Network is %lx\n", + REORDER_ULONG (Device->SourceAddress.NetworkAddress))); + + + // + // Start the timer which updates the RIP database + // periodically. For the first one we do a ten + // second timeout (hopefully this is enough time + // for RIP to start if it is going to). + // + + IpxReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->RipLongTimer, + 10000, + RipLongTimeout, + (PVOID)Device); + + // + // We use this event when unloading to signal that we + // can proceed...initialize it here so we know it is + // ready to go when unload is called. + // + + KeInitializeEvent( + &IpxDevice->UnloadEvent, + NotificationEvent, + FALSE + ); + + } + +InitFailed: + + if (SuccessfulOpens == 0) { + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + return STATUS_DEVICE_DOES_NOT_EXIST; + + } else { + + return STATUS_SUCCESS; + } + +#else // _PNP_POWER +{ + PBIND_ARRAY_ELEM BindingArray; + PTA_ADDRESS TdiRegistrationAddress; + + // + // Pre-allocate the binding array + // Later, we will allocate the LAN/WAN and SLAVE bindings separately + // [BUGBUGZZ] Read the array size from registry? + // + BindingArray = (PBIND_ARRAY_ELEM)IpxAllocateMemory ( + MAX_BINDINGS * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + if (BindingArray == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxDereferenceDevice (Device, DREF_CREATE); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + Device->MaxBindings = MAX_BINDINGS; + + // + // Allocate the TA_ADDRESS structure - this will be used in all TdiRegisterNetAddress + // notifications. + // + TdiRegistrationAddress = (PTA_ADDRESS)IpxAllocateMemory ( + (2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)), + MEMORY_ADDRESS, + "Tdi Address"); + + if (TdiRegistrationAddress == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxFreeMemory(BindingArray, sizeof(BindingArray), MEMORY_BINDING, "Binding Array"); + IpxDereferenceDevice (Device, DREF_CREATE); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + RtlZeroMemory (BindingArray, MAX_BINDINGS * sizeof(BIND_ARRAY_ELEM)); + RtlZeroMemory (TdiRegistrationAddress, 2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)); + + Device->Bindings = BindingArray; + + TdiRegistrationAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + TdiRegistrationAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + + // + // Store the pointer in the Device. + // + Device->TdiRegistrationAddress = TdiRegistrationAddress; + + // + // Device state is loaded, but not opened. It is opened when at least + // one adapter has appeared. + // + Device->State = DEVICE_STATE_LOADED; + + Device->FirstLanNicId = Device->FirstWanNicId = (USHORT)1; // will be changed later + + IpxFreeConfiguration(Config); + + // + // We use this event when unloading to signal that we + // can proceed...initialize it here so we know it is + // ready to go when unload is called. + // + + KeInitializeEvent( + &IpxDevice->UnloadEvent, + NotificationEvent, + FALSE + ); + + return STATUS_SUCCESS; +} +#endif // _PNP_POWER +} /* DriverEntry */ + + +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, +#ifdef _PNP_POWER + IN CTELockHandle *LockHandle1, +#endif + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is called for auto-detect bindings to + remove any bindings that were not successfully found. + It also updates "DefaultAutoDetectType" in the registry + if needed. + +Arguments: + + Device - The IPX device object. + + ValidBindings - The total number of bindings present. + + RegistryPath - The path to the ipx registry, used if we have + to write a value back. + +Return Value: + + The updated number of bindings. + +--*/ + +{ + PBINDING Binding, TmpBinding; + UINT i, j; + + // + // Get rid of any auto-detect devices which we + // could not find nets for. We also remove any + // devices which are not the first ones + // auto-detected on a particular adapter. + // + + for (i = 1; i <= ValidBindings; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + + if (!Binding) { + continue; + } + + // + // If this was auto-detected and was not the default, + // or it was the default, but nothing was detected for + // it *and* something else *was* detected (which means + // we will use that frame type when we get to it), + // we may need to remove this binding. + // + + if (Binding->AutoDetect && + (!Binding->DefaultAutoDetect || + (Binding->DefaultAutoDetect && + (Binding->LocalAddress.NetworkAddress == 0) && + Binding->Adapter->AutoDetectResponse))) { + + if ((Binding->LocalAddress.NetworkAddress == 0) || + (Binding->Adapter->AutoDetectFound)) { + + // + // Remove this binding. + // + + if (Binding->LocalAddress.NetworkAddress == 0) { + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) no net found\n", + i, Binding->FrameType)); + } else { + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) adapter already auto-detected\n", + i, Binding->FrameType)); + } + + CTEAssert (Binding->NicId == i); + CTEAssert (!Binding->Adapter->MacInfo.MediumAsync); + + // + // Remove any routes through this NIC, and + // adjust any NIC ID's above this one in the + // database down by one. + // + + RipAdjustForBindingChange (Binding->NicId, 0, IpxBindingDeleted); + + Binding->Adapter->Bindings[Binding->FrameType] = NULL; + for (j = i+1; j <= ValidBindings; j++) { +#ifndef _PNP_POWER + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; +#else + TmpBinding = NIC_ID_TO_BINDING(Device, j); + INSERT_BINDING(Device, j-1, TmpBinding); +#endif _PNP_POWER + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } +#ifdef _PNP_POWER + INSERT_BINDING(Device, ValidBindings, NULL); +#else + Device->Bindings[ValidBindings] = NULL; +#endif + --Binding->Adapter->BindingCount; + --ValidBindings; + + --i; // so we check the binding that was just moved. + + // + // Wait 100 ms before freeing the binding, + // in case an indication is using it. + // + + KeStallExecutionProcessor(100000); + + IpxDestroyBinding (Binding); + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) auto-detected OK\n", + i, Binding->FrameType)); + +#if DBG + DbgPrint ("IPX: Auto-detected non-default frame type %s, net %lx\n", + OutputFrameType(Binding), + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + + // + // Save it in the registry for the next boot. + // +#ifdef _PNP_POWER +// +// This cannot be done at DPC, so, drop the IRQL +// + IPX_FREE_LOCK1(&Device->BindAccessLock, *LockHandle1); + IpxWriteDefaultAutoDetectType( + RegistryPath, + Binding->Adapter, + Binding->FrameType); + IPX_GET_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + IpxWriteDefaultAutoDetectType( + RegistryPath, + Binding->Adapter, + Binding->FrameType); +#endif + + Binding->Adapter->AutoDetectFound = TRUE; + } + + } else { + + if (Binding->AutoDetect) { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) auto-detect default\n", + i, Binding->FrameType)); + +#if DBG + if (Binding->LocalAddress.NetworkAddress != 0) { + DbgPrint ("IPX: Auto-detected default frame type %s, net %lx\n", + OutputFrameType(Binding), + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); + } else { + DbgPrint ("IPX: Using default auto-detect frame type %s\n", + OutputFrameType(Binding)); + } +#endif + + Binding->Adapter->AutoDetectFound = TRUE; + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) not auto-detected\n", + i, Binding->FrameType)); + } + + } + + } + + + for (i = 1; i <= ValidBindings; i++) { +#ifdef _PNP_POWER + if (Binding = NIC_ID_TO_BINDING(Device, i)) { +#else + if (Binding = Device->Bindings[i]) { +#endif + CTEAssert (Binding->NicId == i); + IPX_DEBUG (AUTO_DETECT, ("Binding %lx, type %d, auto %d\n", + Binding, Binding->FrameType, Binding->AutoDetect)); + } + + } + + return ValidBindings; + +} /* IpxResolveAutoDetect */ + + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ) + +/*++ + +Routine Description: + + This routine is called to determine if we have any + binding sets and rearrange the bindings the way we + like. The order is as follows: + + - First comes the first binding to each LAN network + - Following that are all WAN bindings + - Following that are any duplicate bindings to LAN networks + (the others in the "binding set"). + + If "global wan net" is true we will advertise up to + and including the first wan binding as the highest nic + id; otherwise we advertise up to and including the last + wan binding. In all cases the duplicate bindings are + hidden. + +Arguments: + + Device - The IPX device object. + + ValidBindings - The total number of bindings present. + +Return Value: + + None. + +--*/ + +{ + PBINDING Binding, MasterBinding, TmpBinding; + UINT i, j; + ULONG WanCount, DuplicateCount; + + // + // First loop through and push all the wan bindings + // to the end. + // +#ifdef _PNP_POWER + + WanCount = Device->HighestExternalNicId - Device->HighestLanNicId; + +#else + + WanCount = 0; + + // + // For PnP, we dont do this as the bindings are in order + // at the time of insertion + // + for (i = 1; i <= (ValidBindings-WanCount); ) { + + Binding = Device->Bindings[i]; + + if ((Binding == NULL) || Binding->Adapter->MacInfo.MediumAsync) { + + // + // Put this binding at the end, and slide all the + // others down. If it is a NULL WAN binding then we + // don't have to do some of this. + // + +#if DBG + // + // Any non-NULL bindings should be correct in this + // respect at any point. + // + + if (Binding != NULL) { + CTEAssert (Binding->NicId == i); + } +#endif + + // + // If the Binding is NULL we won't have anything in the + // database at this binding, but we still need to adjust + // any NIC ID's in the database which are above this. + // + + RipAdjustForBindingChange ((USHORT)i, (USHORT)ValidBindings, IpxBindingMoved); + + // + // Slide the bindings above this down. + // + + for (j = i+1; j <= ValidBindings; j++) { + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } + + // + // Put this binding at the end. + // + + Device->Bindings[ValidBindings] = Binding; + if (Binding != NULL) { + if ((Binding->Adapter->MacInfo.MediumAsync) && + (Binding->Adapter->FirstWanNicId == Binding->NicId)) { + Binding->Adapter->FirstWanNicId = (USHORT)ValidBindings; + Binding->Adapter->LastWanNicId += (USHORT)(ValidBindings - Binding->NicId); + } + Binding->NicId = (USHORT)ValidBindings; + } + ++WanCount; + + // + // Keep i the same, to check the new binding at + // this position. + // + + } else { + + i++; + + } + + } +#endif _PNP_POWER + // + // Now go through and find the LAN duplicates and + // create binding sets from them. + // + + DuplicateCount = 0; + + for (i = 1; i <= (ValidBindings-(WanCount+DuplicateCount)); ) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + CTEAssert (Binding != NULL); // because we are only looking at LAN bindings + + CTEAssert (!Binding->Adapter->MacInfo.MediumAsync); + + if (Binding->LocalAddress.NetworkAddress == 0) { + i++; + continue; + } + + // + // See if any previous bindings match the + // frame type, medium type, and number of + // this network (for the moment we match on + // frame type and medium type too so that we + // don't have to worry about different frame + // formats and header offsets within a set). + // + + for (j = 1; j < i; j++) { +#ifdef _PNP_POWER + MasterBinding = NIC_ID_TO_BINDING(Device, j); +#else + MasterBinding = Device->Bindings[j]; +#endif + if ((MasterBinding->LocalAddress.NetworkAddress == Binding->LocalAddress.NetworkAddress) && + (MasterBinding->FrameType == Binding->FrameType) && + (MasterBinding->Adapter->MacInfo.MediumType == Binding->Adapter->MacInfo.MediumType)) { + break; + } + + } + + if (j == i) { + i++; + continue; + } + + // + // We have a duplicate. First slide it down to the + // end. Note that we change any router entries that + // use our real NicId to use the real NicId of the + // master (there should be no entries in the rip + // database that have the NicId of a binding slave). + // + + RipAdjustForBindingChange (Binding->NicId, MasterBinding->NicId, IpxBindingMoved); + + for (j = i+1; j <= ValidBindings; j++) { +#ifdef _PNP_POWER + TmpBinding = NIC_ID_TO_BINDING(Device, j); + INSERT_BINDING(Device, j-1, TmpBinding); +#else + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; +#endif + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } +#ifdef _PNP_POWER + INSERT_BINDING(Device, ValidBindings, Binding); +#else + Device->Bindings[ValidBindings] = Binding; +#endif + + Binding->NicId = (USHORT)ValidBindings; + ++DuplicateCount; + + // + // Now make MasterBinding the head of a binding set. + // + + if (MasterBinding->BindingSetMember) { + + // + // Just insert ourselves in the chain. + // + +#if DBG + DbgPrint ("IPX: %lx is also on network %lx\n", + Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Add %lx to binding set of %lx\n", Binding, MasterBinding)); + + CTEAssert (MasterBinding->CurrentSendBinding); + Binding->NextBinding = MasterBinding->NextBinding; + + } else { + + // + // Start the chain with the two bindings in it. + // + +#if DBG + DbgPrint ("IPX: %lx and %lx are on the same network %lx, will load balance\n", + MasterBinding->Adapter->AdapterName, Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Create new %lx in binding set of %lx\n", Binding, MasterBinding)); + + MasterBinding->BindingSetMember = TRUE; + MasterBinding->CurrentSendBinding = MasterBinding; + MasterBinding->MasterBinding = MasterBinding; + Binding->NextBinding = MasterBinding; + + } + + MasterBinding->NextBinding = Binding; + Binding->BindingSetMember = TRUE; + Binding->ReceiveBroadcast = FALSE; + Binding->CurrentSendBinding = NULL; + Binding->MasterBinding = MasterBinding; + + // + // Since the master binding looks like all members of + // the binding set to people querying from above, we have + // to make it the worst-case of all the elements. Generally + // these will be equal since the frame type and media is + // the same. + // + + if (Binding->MaxLookaheadData > MasterBinding->MaxLookaheadData) { + MasterBinding->MaxLookaheadData = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < MasterBinding->AnnouncedMaxDatagramSize) { + MasterBinding->AnnouncedMaxDatagramSize = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < MasterBinding->RealMaxDatagramSize) { + MasterBinding->RealMaxDatagramSize = Binding->RealMaxDatagramSize; + } + if (Binding->MediumSpeed < MasterBinding->MediumSpeed) { + MasterBinding->MediumSpeed = Binding->MediumSpeed; + } + + // + // Keep i the same, to check the new binding at + // this position. + // + + } +#ifndef _PNP_POWER + Device->HighestExternalNicId = (USHORT)(ValidBindings - DuplicateCount); + Device->HighestType20NicId = (USHORT)(ValidBindings-(WanCount+DuplicateCount)); +#else + Device->HighestLanNicId -= (USHORT)DuplicateCount; + + if (Device->HighestLanNicId == 0) { + CTEAssert(FALSE); + } + + Device->HighestExternalNicId -= (USHORT)DuplicateCount; + Device->HighestType20NicId -= (USHORT)DuplicateCount; + Device->SapNicCount -= (USHORT)DuplicateCount; +#endif _PNP_POWER +} /* IpxResolveBindingSets */ + + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigBinding, +#ifdef _PNP_POWER + IN PADAPTER *AdapterPtr, +#endif + IN ULONG FrameTypeIndex + ) + +/*++ + +Routine Description: + + This routine handles binding the transport to a new + adapter. It can be called at any point during the life + of the transport. + +Arguments: + + Device - The IPX device object. + + ConfigBinding - The configuration info for this binding. + + AdapterPtr - pointer to the adapter to bind to in case of PnP. + + FrameTypeIndex - The index into ConfigBinding's array of frame + types for this adapter. The routine is called once for + every valid frame type. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + +#ifndef _PNP_POWER + // + // Adapter came in as a parameter + // + PADAPTER Adapter = NULL; +#else + PADAPTER Adapter = *AdapterPtr; +#endif + + PBINDING Binding, OldBinding; + ULONG FrameType, MappedFrameType; + PLIST_ENTRY p; + + // + // We can't bind more than one adapter unless we have a + // virtual network configured or we are allowed to run + // with a virtual network of 0. + // + + if (Device->BindingCount == 1) { + if ((Device->VirtualNetworkNumber == 0) && + (!Device->VirtualNetworkOptional)) { + + IPX_DEBUG (ADAPTER, ("Cannot bind to more than one adapter\n")); + DbgPrint ("IPX: Disallowing multiple bind ==> VirtualNetwork is 0\n"); + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_BINDING_FAILED, + 666, + STATUS_NOT_SUPPORTED, + ConfigBinding->AdapterName.Buffer, + 0, + NULL); + + return STATUS_NOT_SUPPORTED; + } + } + + + // + // First allocate the memory for the binding. + // + + status = IpxCreateBinding( + Device, + ConfigBinding, + FrameTypeIndex, + ConfigBinding->AdapterName.Buffer, + &Binding); + + if (status != STATUS_SUCCESS) { + return status; + } + + FrameType = ConfigBinding->FrameType[FrameTypeIndex]; + +// +// In PnP case, we dont need to check for existing adapters since +// we supply a NULL adapter in the parameters if it needs to be created +// +#ifndef _PNP_POWER + + // + // Check if there is already an NDIS binding to this adapter, + // and if so, that there is not already a binding with this + // frame type. + // + + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + p = p->Flink) { + + OldBinding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + + if (RtlEqualMemory( + OldBinding->Adapter->AdapterName, + ConfigBinding->AdapterName.Buffer, + OldBinding->Adapter->AdapterNameLength)) { + + Adapter = OldBinding->Adapter; + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + if (Adapter->Bindings[MappedFrameType] != NULL) { + + IPX_DEBUG (ADAPTER, ("Bind to adapter %ws, type %d exists\n", + Adapter->AdapterName, + MappedFrameType)); + + // + // If this was the auto-detect default for this + // adapter and it failed, we need to make the + // previous one the default, so that at least + // one binding will stick around. + // + + if (ConfigBinding->DefaultAutoDetect[FrameTypeIndex]) { + IPX_DEBUG (ADAPTER, ("Default auto-detect changed from %d to %d\n", + FrameType, MappedFrameType)); + Adapter->Bindings[MappedFrameType]->DefaultAutoDetect = TRUE; + } + + IpxDestroyBinding (Binding); + return STATUS_NOT_SUPPORTED; + } + + IPX_DEBUG (ADAPTER, ("Using existing bind to adapter %ws, type %d\n", + Adapter->AdapterName, + MappedFrameType)); + break; + + } + } +#endif _PNP_POWER + + if (Adapter == NULL) { + + // + // No binding to this adapter exists, so create a + // new one. + // + + status = IpxCreateAdapter( + Device, + &ConfigBinding->AdapterName, + &Adapter); + + if (status != STATUS_SUCCESS) { + IpxDestroyBinding(Binding); + return status; + } + + // + // Save these now (they will be the same for all bindings + // on this adapter). + // + + Adapter->ConfigMaxPacketSize = ConfigBinding->Parameters[BINDING_MAX_PKT_SIZE]; + Adapter->SourceRouting = (BOOLEAN)ConfigBinding->Parameters[BINDING_SOURCE_ROUTE]; + Adapter->EnableFunctionalAddress = (BOOLEAN)ConfigBinding->Parameters[BINDING_ENABLE_FUNC_ADDR]; + Adapter->EnableWanRouter = (BOOLEAN)ConfigBinding->Parameters[BINDING_ENABLE_WAN]; + + Adapter->BindSap = (USHORT)ConfigBinding->Parameters[BINDING_BIND_SAP]; + Adapter->BindSapNetworkOrder = REORDER_USHORT(Adapter->BindSap); + CTEAssert (Adapter->BindSap == 0x8137); + CTEAssert (Adapter->BindSapNetworkOrder == 0x3781); + + // + // Now fire up NDIS so this adapter talks + // + + status = IpxInitializeNdis( + Adapter, + ConfigBinding); + + if (!NT_SUCCESS (status)) { + + // + // Log an error. + // + + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_BINDING_FAILED, + 601, + status, + ConfigBinding->AdapterName.Buffer, + 0, + NULL); + + IpxDestroyAdapter (Adapter); + IpxDestroyBinding (Binding); + + // + // Returning this status informs the caller to not + // try any more frame types on this adapter. + // + + return STATUS_DEVICE_DOES_NOT_EXIST; + + } + + // + // For 802.5 bindings we need to start the source routing + // timer to time out old entries. + // + + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && + (Adapter->SourceRouting)) { + + if (!Device->SourceRoutingUsed) { + + Device->SourceRoutingUsed = TRUE; + IpxReferenceDevice (Device, DREF_SR_TIMER); + + CTEStartTimer( + &Device->SourceRoutingTimer, + 60000, // one minute timeout + MacSourceRoutingTimeout, + (PVOID)Device); + } + } + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + IPX_DEBUG (ADAPTER, ("Create new bind to adapter %ws, type %d\n", + ConfigBinding->AdapterName.Buffer, + MappedFrameType)); + + IpxAllocateReceiveBufferPool (Adapter); + +#ifdef _PNP_POWER + *AdapterPtr = Adapter; +#endif + } +#ifdef _PNP_POWER + else { + // + // get the mapped frame type + // + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + if (Adapter->Bindings[MappedFrameType] != NULL) { + + IPX_DEBUG (ADAPTER, ("Bind to adapter %ws, type %d exists\n", + Adapter->AdapterName, + MappedFrameType)); + + // + // If this was the auto-detect default for this + // adapter and it failed, we need to make the + // previous one the default, so that at least + // one binding will stick around. + // + + if (ConfigBinding->DefaultAutoDetect[FrameTypeIndex]) { + IPX_DEBUG (ADAPTER, ("Default auto-detect changed from %d to %d\n", + FrameType, MappedFrameType)); + Adapter->Bindings[MappedFrameType]->DefaultAutoDetect = TRUE; + } + + IpxDestroyBinding (Binding); + + return STATUS_NOT_SUPPORTED; + } + + IPX_DEBUG (ADAPTER, ("Using existing bind to adapter %ws, type %d\n", + Adapter->AdapterName, + MappedFrameType)); + } +#endif _PNP_POWER + + // + // The local node address starts out the same as the + // MAC address of the adapter (on WAN this will change). + // The local MAC address can also change for WAN. + // + + RtlCopyMemory (Binding->LocalAddress.NodeAddress, Adapter->LocalMacAddress.Address, 6); + RtlCopyMemory (Binding->LocalMacAddress.Address, Adapter->LocalMacAddress.Address, 6); + + + // + // Save the send handler. + // + + Binding->SendFrameHandler = NULL; + Binding->FrameType = MappedFrameType; + + // + // BUGBUG: Put this in InitializeBindingInfo. + // + + switch (Adapter->MacInfo.RealMediumType) { + case NdisMedium802_3: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrame802_3802_3; break; + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrame802_3802_2; break; + case ISN_FRAME_TYPE_ETHERNET_II: Binding->SendFrameHandler = IpxSendFrame802_3EthernetII; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrame802_3Snap; break; + } + break; + case NdisMedium802_5: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrame802_5802_2; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrame802_5Snap; break; + } + break; + case NdisMediumFddi: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrameFddi802_3; break; + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrameFddi802_2; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrameFddiSnap; break; + } + break; + case NdisMediumArcnet878_2: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrameArcnet878_2; break; + } + break; + case NdisMediumWan: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_ETHERNET_II: Binding->SendFrameHandler = IpxSendFrameWanEthernetII; break; + } + break; + } + + if (Binding->SendFrameHandler == NULL) { + DbgPrint ("BUGBUG!: SendFrameHandler is NULL\n"); + } + + Adapter->Bindings[MappedFrameType] = Binding; + ++Adapter->BindingCount; + + Binding->Adapter = Adapter; + +#ifndef _PNP_POWER + InsertTailList (&Device->InitialBindingList, &Binding->InitialLinkage); +#endif _PNP_POWER + + // + // NicId and ExternalNicId will be filled in later when the binding + // is assigned a spot in the Device->Bindings array. + // + + // + // Initialize the per-binding MAC information + // + + if ((Adapter->ConfigMaxPacketSize == 0) || + (Adapter->MaxSendPacketSize < Adapter->ConfigMaxPacketSize)) { + Binding->MaxSendPacketSize = Adapter->MaxSendPacketSize; + } else { + Binding->MaxSendPacketSize = Adapter->ConfigMaxPacketSize; + } + Binding->MediumSpeed = Adapter->MediumSpeed; + if (Adapter->MacInfo.MediumAsync) { + Binding->LineUp = FALSE; + } else { + Binding->LineUp = TRUE; + } + + MacInitializeBindingInfo( + Binding, + Adapter); + + return STATUS_SUCCESS; + +} /* IpxBindToAdapter */ + + +BOOLEAN +IpxIsAddressLocal( + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress + ) + +/*++ + +Routine Description: + + This routine returns TRUE if the specified SourceAddress indicates + the packet was sent by us, and FALSE otherwise. + +Arguments: + + SourceAddress - The source IPX address. + +Return Value: + + TRUE if the address is local. + +--*/ + +{ + PBINDING Binding; + UINT i; + + // + // First see if it is a virtual network address or not. + // + + if (RtlEqualMemory (VirtualNode, SourceAddress->NodeAddress, 6)) { + + // + // This is us if we have a virtual network configured. + // If we don't have a virtual node, we fall through to the + // other check -- an arcnet card configured as node 1 will + // have what we think of as the "virtual node" as its + // real node address. + // + + if ((IpxDevice->VirtualNetwork) && + (IpxDevice->VirtualNetworkNumber == SourceAddress->NetworkAddress)) { + return TRUE; + } + + } + + // + // Check through our list of adapters to see if one of + // them is the source node. + // + { + ULONG Index = MIN (IpxDevice->MaxBindings, IpxDevice->ValidBindings); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + if (((Binding = NIC_ID_TO_BINDING(IpxDevice, i)) != NULL) && +#else + if (((Binding = IpxDevice->Bindings[i]) != NULL) && +#endif _PNP_POWER + (RtlEqualMemory (Binding->LocalAddress.NodeAddress, SourceAddress->NodeAddress, 6))) { + return TRUE; + } + } + } + + return FALSE; + +} /* IpxIsAddressLocal */ + + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine handles unbinding the transport from an + adapter. It can be called at any point during the life + of the transport. + +Arguments: + + Binding - The adapter to unbind. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + PADAPTER Adapter = Binding->Adapter; + + Adapter->Bindings[Binding->FrameType] = NULL; + --Adapter->BindingCount; + + IpxDereferenceBinding (Binding, BREF_BOUND); + + if (Adapter->BindingCount == 0) { + + // + // DereferenceAdapter is a NULL macro for load-only. + // + // BUGBUG: Revisit Post 4.0 + // +#ifdef _PNP_LATER + // + // Take away the creation reference. When the in-use ref is taken off, + // we destroy this adapter. + // + IpxDereferenceAdapter(Adapter); +#else + // + // Free the packet pools, etc. and close the + // adapter. + // + + IpxCloseNdis (Adapter); + + IpxDestroyAdapter (Adapter); +#endif + } + + return STATUS_SUCCESS; + +} /* IpxUnBindFromAdapter */ + + +VOID +IpxUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has IPX open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + + PBINDING Binding; + PREQUEST Request; + PLIST_ENTRY p; + UINT i; + + + UNREFERENCED_PARAMETER (DriverObject); + + IpxDevice->State = DEVICE_STATE_STOPPING; + + + // + // Complete any pending address notify requests. + // + + while ((p = ExInterlockedRemoveHeadList( + &IpxDevice->AddressNotifyQueue, + &IpxDevice->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + REQUEST_STATUS(Request) = STATUS_DEVICE_NOT_READY; + IpxCompleteRequest (Request); + IpxFreeRequest (IpxDevice, Request); + + IpxDereferenceDevice (IpxDevice, DREF_ADDRESS_NOTIFY); + } + + + // + // Cancel the source routing timer if used. + // + + if (IpxDevice->SourceRoutingUsed) { + + IpxDevice->SourceRoutingUsed = FALSE; + if (CTEStopTimer (&IpxDevice->SourceRoutingTimer)) { + IpxDereferenceDevice (IpxDevice, DREF_SR_TIMER); + } + } + + + // + // Cancel the RIP long timer, and if we do that then + // send a RIP DOWN message if needed. + // + + if (CTEStopTimer (&IpxDevice->RipLongTimer)) { + + if (IpxDevice->RipResponder) { + + if (RipQueueRequest (IpxDevice->VirtualNetworkNumber, RIP_DOWN) == STATUS_PENDING) { + + // + // If we queue a request, it will stop the timer. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + IpxDereferenceDevice (IpxDevice, DREF_LONG_TIMER); + + } else { + + // + // We couldn't stop the timer, which means it is running, + // so we need to wait for the event that is kicked when + // the RIP DOWN messages are done. + // + + if (IpxDevice->RipResponder) { + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + + // + // Walk the list of device contexts. + // + + for (i = 1; i <= IpxDevice->BindingCount; i++) { +#ifdef _PNP_POWER + if ((Binding = NIC_ID_TO_BINDING(IpxDevice, i)) != NULL) { + INSERT_BINDING(IpxDevice, i, NULL); +#else + if (IpxDevice->Bindings[i] != NULL) { + Binding = IpxDevice->Bindings[i]; + IpxDevice->Bindings[i] = NULL; +#endif _PNP_POWER + + IpxUnBindFromAdapter (Binding); + + } + + } + +#ifdef _PNP_POWER + + IpxFreeMemory ( IpxDevice->Bindings, + IpxDevice->MaxBindings * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + // + // Deallocate the TdiRegistrationAddress and RegistryPathBuffer. + // + IpxFreeMemory ( IpxDevice->TdiRegistrationAddress, + (2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)), + MEMORY_ADDRESS, + "Tdi Address"); + + IpxFreeMemory ( IpxDevice->RegistryPathBuffer, + IpxDevice->RegistryPath.Length + sizeof(WCHAR), + MEMORY_CONFIG, + "RegistryPathBuffer"); + +#endif + + KeResetEvent( + &IpxDevice->UnloadEvent + ); + IpxDevice->UnloadWaiting = TRUE; + + // + // Remove the reference for us being loaded. + // + + IpxDereferenceDevice (IpxDevice, DREF_CREATE); + + // + // Wait for our count to drop to zero. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + // + // Now free the padding buffer. + // + + IpxFreePaddingBuffer (IpxDevice); + + // + // Now do the cleanup that has to happen at IRQL 0. + // + + ExDeleteResource (&IpxDevice->AddressResource); + IoDeleteDevice (IpxDevice->DeviceObject); + + // + // Finally, remove ourselves as an NDIS protocol. + // + + IpxDeregisterProtocol(); + +} /* IpxUnload */ + + +NTSTATUS +IpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the IPX device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = IpxDevice; + NTSTATUS Status; + PFILE_FULL_EA_INFORMATION openType; + BOOLEAN found; + PADDRESS_FILE AddressFile; + PREQUEST Request; + UINT i; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + +#ifdef _PNP_POWER + if ((Device->State == DEVICE_STATE_CLOSED) || + (Device->State == DEVICE_STATE_STOPPING)) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#else + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif + // + // Allocate a request to track this IRP. + // + + Request = IpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = IpxOpenAddress (Device, Request); + break; + } + + // + // Connection? + // + + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = STATUS_NOT_SUPPORTED; + break; + } + + } else { + + CTEGetLock (&Device->Lock, &LockHandle); + + // + // LowPart is in the OPEN_CONTEXT directly. + // HighPart goes into the upper 2 bytes of the OPEN_TYPE. + // + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->ControlChannelIdentifier.LowPart); + + (ULONG)(REQUEST_OPEN_TYPE(Request)) = (Device->ControlChannelIdentifier.HighPart << 16); + (ULONG)(REQUEST_OPEN_TYPE(Request)) |= IPX_FILE_TYPE_CONTROL; + + ++(Device->ControlChannelIdentifier.QuadPart); + + if (Device->ControlChannelIdentifier.QuadPart > MAX_CCID) { + Device->ControlChannelIdentifier.QuadPart = 1; + } + + CTEFreeLock (&Device->Lock, LockHandle); + + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + switch ((ULONG)(REQUEST_OPEN_TYPE(Request)) & IPX_CC_MASK) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + // + // This creates a reference to AddressFile->Address + // which is removed by IpxCloseAddressFile. + // + + Status = IpxVerifyAddressFile(AddressFile); + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = IpxCloseAddressFile (Device, Request); + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case IPX_FILE_TYPE_CONTROL: + { + LARGE_INTEGER ControlChannelId; + + CCID_FROM_REQUEST(ControlChannelId, Request); + + // + // See if it is one of the upper driver's control channels. + // + + Status = STATUS_SUCCESS; + + IPX_DEBUG (DEVICE, ("CCID: (%d, %d)\n", ControlChannelId.HighPart, ControlChannelId.LowPart)); + + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + if (Device->UpperDriverControlChannel[i].QuadPart == + ControlChannelId.QuadPart) { + Status = IpxInternalUnbind (Device, i); + break; + } + } + + break; + } + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + case IRP_MJ_CLEANUP: + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + switch ((ULONG)(REQUEST_OPEN_TYPE(Request)) & IPX_CC_MASK) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Status = IpxVerifyAddressFile(AddressFile); + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + IpxStopAddressFile (AddressFile); + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case IPX_FILE_TYPE_CONTROL: + { + LARGE_INTEGER ControlChannelId; + + CCID_FROM_REQUEST(ControlChannelId, Request); + + // + // Check for any line change IRPs submitted by this + // address. + // + + IpxAbortLineChanges ((PVOID)&ControlChannelId); + + Status = STATUS_SUCCESS; + break; + } + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* IpxDispatchOpenClose */ + +#define IOCTL_IPX_LOAD_SPX _IPX_CONTROL_CODE( 0x5678, METHOD_BUFFERED ) + +NTSYSAPI +NTSTATUS +NTAPI +ZwLoadDriver( + IN PUNICODE_STRING DriverServiceName + ); + + +NTSTATUS +IpxDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = IpxDevice; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + static NDIS_STRING SpxServiceName = NDIS_STRING_CONST ("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\NwlnkSpx"); + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_TDI_QUERY_DIRECT_SENDDG_HANDLER: { + + PULONG EntryPoint; + + // + // This is the LanmanServer trying to get the send + // entry point. + // + + IPX_DEBUG (BIND, ("Direct send entry point being returned\n")); + + EntryPoint = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + *EntryPoint = (ULONG)IpxTdiSendDatagram; + + Status = STATUS_SUCCESS; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + break; + } + + case IOCTL_IPX_INTERNAL_BIND: + + // + // This is a client trying to bind. + // + + CTEAssert ((IOCTL_IPX_INTERNAL_BIND & 0x3) == METHOD_BUFFERED); + CTEAssert (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL); + +#ifdef _PNP_POWER + + if ((Device->State == DEVICE_STATE_CLOSED) || + (Device->State == DEVICE_STATE_STOPPING)) { +#else + if (Device->State != DEVICE_STATE_OPEN) { +#endif + Status = STATUS_INVALID_DEVICE_STATE; + + } else { + + Status = IpxInternalBind (Device, Irp); + + } + + CTEAssert (Status != STATUS_PENDING); + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + break; + + case IOCTL_IPX_LOAD_SPX: + + // + // The SPX helper dll is asking us to load SPX. + // + + Status = ZwLoadDriver (&SpxServiceName); + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + break; + + default: + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + + Status = IpxDispatchInternal (DeviceObject, Irp); + + } else { + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + + break; + } + return Status; + +} /* IpxDispatchDeviceControl */ + + +NTSTATUS +IpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = IpxDevice; + PREQUEST Request; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + if (Device->State == DEVICE_STATE_OPEN) { + + // + // Allocate a request to track this IRP. + // + + Request = IpxAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); +#if DBG + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; +#endif + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (REQUEST_MINOR_FUNCTION(Request)) { + + case TDI_SEND_DATAGRAM: + Status = IpxTdiSendDatagram (DeviceObject, Request); + break; + + case TDI_ACTION: + Status = IpxTdiAction (Device, Request); + break; + + case TDI_QUERY_INFORMATION: + Status = IpxTdiQueryInformation (Device, Request); + break; + + case TDI_RECEIVE_DATAGRAM: + Status = IpxTdiReceiveDatagram (Request); + break; + + case TDI_SET_EVENT_HANDLER: + Status = IpxTdiSetEventHandler (Request); + break; + + case TDI_SET_INFORMATION: + Status = IpxTdiSetInformation (Device, Request); + break; + + + // + // Something we don't know about was submitted. + // + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Return the immediate status code to the caller. + // + + if (Status == STATUS_PENDING) { + + return STATUS_PENDING; + + } else { + + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + return Status; + } + + } else { + + // + // The device was not open. + // + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + +} /* IpxDispatchInternal */ + + +PVOID +IpxpAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine allocates memory, making sure it is within + the limit allowed by the device. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + PDEVICE Device = IpxDevice; + + if (ChargeDevice) { + if ((Device->MemoryLimit != 0) && + (((LONG)(Device->MemoryUsage + BytesNeeded) > + Device->MemoryLimit))) { + + IpxPrint1 ("IPX: Could not allocate %d: limit\n", BytesNeeded); + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + BytesNeeded, + Tag); + + return NULL; + } + } + +#if ISN_NT + Memory = ExAllocatePoolWithTag (NonPagedPool, BytesNeeded, ' XPI'); +#else + Memory = CTEAllocMem (BytesNeeded); +#endif + + if (Memory == NULL) { + + IpxPrint1("IPX: Could not allocate %d: no pool\n", BytesNeeded); + if (ChargeDevice) { + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + BytesNeeded, + Tag); + } + + return NULL; + } + + if (ChargeDevice) { + Device->MemoryUsage += BytesNeeded; + } + + return Memory; +} /* IpxpAllocateMemory */ + + +VOID +IpxpFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with IpxpAllocateMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + +#if ISN_NT + ExFreePool (Memory); +#else + CTEFreeMem (Memory); +#endif + if (ChargeDevice) { + Device->MemoryUsage -= BytesAllocated; + } + +} /* IpxpFreeMemory */ + +#if DBG + + +PVOID +IpxpAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine allocates memory, charging it to the device. + If it cannot allocate memory it uses the Tag and Descriptor + to log an error. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + + UNREFERENCED_PARAMETER(Description); + + Memory = IpxpAllocateMemory(BytesNeeded, Tag, (BOOLEAN)(Tag != MEMORY_CONFIG)); + + if (Memory) { + (VOID)IPX_ADD_ULONG( + &IpxMemoryTag[Tag].BytesAllocated, + BytesNeeded, + &IpxMemoryInterlock); + } + + return Memory; + +} /* IpxpAllocateTaggedMemory */ + + +VOID +IpxpFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with IpxpAllocateTaggedMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Description); + + (VOID)IPX_ADD_ULONG( + &IpxMemoryTag[Tag].BytesAllocated, + (ULONG)(-(LONG)BytesAllocated), + &IpxMemoryInterlock); + + IpxpFreeMemory (Memory, BytesAllocated, (BOOLEAN)(Tag != MEMORY_CONFIG)); + +} /* IpxpFreeTaggedMemory */ + +#endif + + +VOID +IpxWriteResourceErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry which has + a %3 value that needs to be converted to a string. It is currently + used for EVENT_TRANSPORT_RESOURCE_POOL and EVENT_IPX_INTERNAL_NET_ + INVALID. + +Arguments: + + DeviceObject - Pointer to the system device object. + + ErrorCode - The transport event code. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated -- will be put in the dump data. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet and converted for use as the %3 string. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + PDEVICE Device = IpxDevice; + static WCHAR UniqueErrorBuffer[9] = L"00000000"; + UINT CurrentDigit; + INT i; + + + // + // Convert the error value into a buffer. + // + + TempUniqueError = UniqueErrorValue; + i = 8; + do { + CurrentDigit = TempUniqueError & 0xf; + TempUniqueError >>= 4; + i--; + if (CurrentDigit >= 0xa) { + UniqueErrorBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + UniqueErrorBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } while (TempUniqueError); + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->DeviceNameLength + + sizeof(UniqueErrorBuffer) - (i * sizeof(WCHAR)); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + + StringLoc += Device->DeviceNameLength; + RtlCopyMemory (StringLoc, UniqueErrorBuffer + i, sizeof(UniqueErrorBuffer) - (i * sizeof(WCHAR))); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteResourceErrorLog */ + + +VOID +IpxWriteGeneralErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + DeviceObject - Pointer to the system device object, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + PDEVICE Device = IpxDevice; + static WCHAR DriverName[9] = L"NwlnkIpx"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (DeviceObject->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)Device->DeviceNameLength; + } else { + EntrySize += sizeof(DriverName); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (DeviceObject->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteGeneralErrorLog */ + + +VOID +IpxWriteOidErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + DeviceObject - Pointer to the system device object. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + PDEVICE Device = IpxDevice; + static WCHAR OidBuffer[9] = L"00000000"; + INT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + Device->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteOidErrorLog */ + + +#ifdef _PNP_POWER +VOID +IpxPnPUpdateDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + Updates datagram sizes, lookahead sizes, etc. in the Device as a result + of a new binding coming in. + +Arguments: + + Device - The IPX device object. + +Return Value: + + None. + +--*/ +{ + ULONG AnnouncedMaxDatagram, RealMaxDatagram, MaxLookahead; + ULONG LinkSpeed, MacOptions; + ULONG i; + PBINDING Binding; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + IPX_GET_LOCK(&Device->BindAccessLock, &LockHandle); + + // + // Calculate some values based on all the bindings. + // + + MaxLookahead = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MaxLookaheadData; // largest binding value + AnnouncedMaxDatagram = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->AnnouncedMaxDatagramSize; // smallest binding value + RealMaxDatagram = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->RealMaxDatagramSize; // smallest binding value + + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->LineUp) { + LinkSpeed = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MediumSpeed; // smallest binding value + } else { + LinkSpeed = 0xffffffff; + } + MacOptions = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->Adapter->MacInfo.MacOptions; // AND of binding values + + for (i = 2; i <= Device->ValidBindings; i++) { + + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, i); + + if (!Binding) { + continue; + } + + if (Binding->MaxLookaheadData > MaxLookahead) { + MaxLookahead = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < AnnouncedMaxDatagram) { + AnnouncedMaxDatagram = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < RealMaxDatagram) { + RealMaxDatagram = Binding->RealMaxDatagramSize; + } + + if (Binding->LineUp && (Binding->MediumSpeed < LinkSpeed)) { + LinkSpeed = Binding->MediumSpeed; + } + MacOptions &= Binding->Adapter->MacInfo.MacOptions; + + } + + Device->Information.MaxDatagramSize = AnnouncedMaxDatagram; + Device->RealMaxDatagramSize = RealMaxDatagram; + Device->Information.MaximumLookaheadData = MaxLookahead; + + // + // If we couldn't find anything better, use the speed from + // the first binding. + // + + if (LinkSpeed == 0xffffffff) { + Device->LinkSpeed = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MediumSpeed; + } else { + Device->LinkSpeed = LinkSpeed; + } + Device->MacOptions = MacOptions; + + IPX_FREE_LOCK(&Device->BindAccessLock, LockHandle); +} + +VOID +IpxPnPUpdateBindingArray( + IN PDEVICE Device, + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ) + +/*++ + +Routine Description: + + This routine is called to update the binding array to + add the new bindings that appeared in this PnP event. + The order of bindings in the array is as follows: + + - First comes the first binding to each LAN network + - Following that are all WAN bindings + - Following that are any duplicate bindings to LAN networks + (the others in the "binding set"). + + This routine inserts the bindings while maintaining this + order by resolving binding sets. + + The bindings are also inserted into the RIP database. + + If "global wan net" is true we will advertise up to + and including the first wan binding as the highest nic + id; otherwise we advertise up to and including the last + wan binding. In all cases the duplicate bindings are + hidden. + + Updates the SapNicCount, Device->FirstLanNicId and Device->FirstWanNicId + +Arguments: + + Device - The IPX device object. + + Adapter - The adapter added in this PnP event + + ValidBindings - the number of bindings valid for this adapter (if LAN) + +Return Value: + + None. + +--*/ +{ + ULONG i, j; + PBINDING Binding, MasterBinding; + NTSTATUS status; + + // + // Insert in proper place; if WAN, after all the WAN bindings + // If LAN, check for binding sets and insert in proper place + // Also, insert into the Rip Tables. + // + + // + // Go thru' the bindings for this adapter, inserting into the + // binding array in place + // + for (i = 0; i < ConfigBinding->FrameTypeCount; i++) { + ULONG MappedFrameType; + + // + // Store in the preference order. + // Map the frame types since we could have a case where the user selects a FrameType (say, EthernetII on FDDI) + // which maps to a different FrameType (802.2). Then we would fail to find the binding in the adapter array; + // we could potentialy add a binding twice (if two frame types map to the same Frame, then we would go to the + // mapped one twice). This is taken care of by purging dups from the ConfigBinding->FrameType array when we + // create the bindings off of the Adapter (see call to IpxBindToAdapter). + // + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + ConfigBinding->FrameType[i], + &MappedFrameType); + + Binding = Adapter->Bindings[MappedFrameType]; + + if (!Binding){ + continue; + } + + CTEAssert(Binding->FrameType == MappedFrameType); + + if (Adapter->MacInfo.MediumAsync) { + // + // WAN: Place after the HighestExternalNicId, with space for WanLine # of bindings. + // Update the First/LastWanNicId. + // + Adapter->FirstWanNicId = (USHORT)Device->HighestExternalNicId+1; + Adapter->LastWanNicId = (USHORT)(Device->HighestExternalNicId + Adapter->WanNicIdCount); + + // + // Make sure we dont overflow the array + // Re-alloc the array to fit the new bindings + // + if (Device->ValidBindings+Adapter->WanNicIdCount >= Device->MaxBindings) { + status = IpxPnPReallocateBindingArray(Device, Adapter->WanNicIdCount); + CTEAssert(status == STATUS_SUCCESS); + } + + // + // Move Slaves down by WanNicIdCount# of entries + // + for (j = Device->ValidBindings; j > Device->HighestExternalNicId; j--) { + INSERT_BINDING(Device, j+Adapter->WanNicIdCount, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, j+Adapter->WanNicIdCount)) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, j+Adapter->WanNicIdCount)->NicId += (USHORT)Adapter->WanNicIdCount; + } + } + + // + // Insert the WAN binding in the place just allocated + // + INSERT_BINDING(Device, Device->HighestExternalNicId+1, Binding); + SET_VERSION(Device, Device->HighestExternalNicId+1); + + Binding->NicId = (USHORT)Device->HighestExternalNicId+1; + + // + // Update the indices + // + Device->HighestExternalNicId += (USHORT)Adapter->WanNicIdCount; + Device->ValidBindings += (USHORT)Adapter->WanNicIdCount; + Device->BindingCount += (USHORT)Adapter->WanNicIdCount; + Device->SapNicCount++; + + // + // Since we initialize FirstWanNicId to 1, we need to compare against that. + // In case of no LAN bindings, we are fine since we have only one WAN binding initally + // (all the other WAN lines have place holders). + // + if (Device->FirstWanNicId == (USHORT)1) { + Device->FirstWanNicId = Binding->NicId; + } + + // + // BUGBUGZZ Make this inline later + // + // This should be done after all the auto-detect bindings have been thrown away. + // + // IpxPnPUpdateDevice(Device, Binding); + + // + // Since WAN can have only one frame type, break + // + break; + + } else { + + Device->BindingCount++; + + // + // Make sure we dont overflow the array + // Re-alloc the array to fit the new bindings + // + if (Device->ValidBindings+1 >= Device->MaxBindings) { + status = IpxPnPReallocateBindingArray(Device, 1); + CTEAssert(status == STATUS_SUCCESS); + } + + // + // LAN: Figure out if it is a slave binding only for non-auto-detect bindings. + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (j = 1; j < Index; j++) { + MasterBinding = NIC_ID_TO_BINDING_NO_ILOCK(Device, j); + if ((MasterBinding->ConfiguredNetworkNumber) && + (MasterBinding->ConfiguredNetworkNumber == Binding->ConfiguredNetworkNumber) && + (MasterBinding->FrameType == Binding->FrameType) && + (MasterBinding->Adapter->MacInfo.MediumType == Binding->Adapter->MacInfo.MediumType)) { + + CTEAssert(Binding->ConfiguredNetworkNumber); + break; + } + } + } + + if (j < Device->HighestExternalNicId) { + // + // Slave binding + // + + // + // Now make MasterBinding the head of a binding set. + // + + if (MasterBinding->BindingSetMember) { + + // + // Just insert ourselves in the chain. + // + +#if DBG + DbgPrint ("IPX: %ws is also on network %lx\n", + Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Add %lx to binding set of %lx\n", Binding, MasterBinding)); + + CTEAssert (MasterBinding->CurrentSendBinding); + Binding->NextBinding = MasterBinding->NextBinding; + + } else { + + // + // Start the chain with the two bindings in it. + // + +#if DBG + DbgPrint ("IPX: %lx and %lx are on the same network %lx, will load balance\n", + MasterBinding->Adapter->AdapterName, Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Create new %lx in binding set of %lx\n", Binding, MasterBinding)); + + MasterBinding->BindingSetMember = TRUE; + MasterBinding->CurrentSendBinding = MasterBinding; + MasterBinding->MasterBinding = MasterBinding; + Binding->NextBinding = MasterBinding; + + } + + MasterBinding->NextBinding = Binding; + Binding->BindingSetMember = TRUE; + Binding->ReceiveBroadcast = FALSE; + Binding->CurrentSendBinding = NULL; + Binding->MasterBinding = MasterBinding; + + // + // Since the master binding looks like all members of + // the binding set to people querying from above, we have + // to make it the worst-case of all the elements. Generally + // these will be equal since the frame type and media is + // the same. + // + + if (Binding->MaxLookaheadData > MasterBinding->MaxLookaheadData) { + MasterBinding->MaxLookaheadData = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < MasterBinding->AnnouncedMaxDatagramSize) { + MasterBinding->AnnouncedMaxDatagramSize = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < MasterBinding->RealMaxDatagramSize) { + MasterBinding->RealMaxDatagramSize = Binding->RealMaxDatagramSize; + } + if (Binding->MediumSpeed < MasterBinding->MediumSpeed) { + MasterBinding->MediumSpeed = Binding->MediumSpeed; + } + + // + // Place the binding after the last slave binding + // + INSERT_BINDING(Device, Device->ValidBindings+1, Binding); + SET_VERSION(Device, Device->ValidBindings+1); + + Binding->NicId = (USHORT)Device->ValidBindings+1; + + // + // Update the indices + // + Device->ValidBindings++; + + } else { + + // + // Not a binding set slave binding - just add it after the last LAN binding + // + + // + // Move WAN and Slaves down by 1 entry + // + for (j = Device->ValidBindings; j > Device->HighestLanNicId; j--) { + INSERT_BINDING(Device, j+1, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, j+1)) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, j+1)->NicId++; + } + } + + // + // Insert the LAN binding in the place just allocated + // + INSERT_BINDING(Device, Device->HighestLanNicId+1, Binding); + SET_VERSION(Device, Device->HighestLanNicId+1); + Binding->NicId = (USHORT)Device->HighestLanNicId+1; + + // + // Update the indices + // + Device->HighestLanNicId++; + Device->HighestExternalNicId++; + Device->ValidBindings++; + Device->HighestType20NicId++; + Device->SapNicCount++; + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = Binding->NicId; + } + + } + + } + + // + // Insert this binding in the RIP Tables + // + if (Binding->ConfiguredNetworkNumber != 0) { + status = RipInsertLocalNetwork( + Binding->ConfiguredNetworkNumber, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->Adapter->MediumSpeed) / Binding->Adapter->MediumSpeed)); + + if ((status == STATUS_SUCCESS) || + (status == STATUS_DUPLICATE_NAME)) { + + Binding->LocalAddress.NetworkAddress = Binding->ConfiguredNetworkNumber; + } + } + + // + // BUGBUGZZ Make this inline later + // + // This should be done after all the auto-detect bindings have been thrown away. + // + // IpxPnPUpdateDevice(Device, Binding); + } +} /* IpxPnPUpdateBindingArray */ + + +VOID +IpxPnPToLoad() +/*++ + +Routine Description: + + This routine takes the driver to LOADED state (from OPEN) when all + PnP adapters have been removed from the machine. + +Arguments: + + None. + +Return Value: + + None. When the function returns, the driver is in LOADED state. + +--*/ + +{ + PBINDING Binding; + PREQUEST Request; + PLIST_ENTRY p; + UINT i; + NTSTATUS ntStatus; + + IPX_DEBUG(PNP, ("Going back to loaded state\n")); + + // + // Inform TDI clients about the open of our device object. + // + if ((ntStatus = TdiDeregisterDeviceObject(IpxDevice->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterDeviceObject failed: %lx", ntStatus)); + } + + // + // Complete any pending address notify requests. + // + + while ((p = ExInterlockedRemoveHeadList( + &IpxDevice->AddressNotifyQueue, + &IpxDevice->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + REQUEST_STATUS(Request) = STATUS_DEVICE_NOT_READY; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IpxCompleteRequest (Request); + IpxFreeRequest (IpxDevice, Request); + + IpxDereferenceDevice (IpxDevice, DREF_ADDRESS_NOTIFY); + } + + // + // Cancel the source routing timer if used. + // + + if (IpxDevice->SourceRoutingUsed) { + + IpxDevice->SourceRoutingUsed = FALSE; + if (CTEStopTimer (&IpxDevice->SourceRoutingTimer)) { + IpxDereferenceDevice (IpxDevice, DREF_SR_TIMER); + } + } + + + // + // Cancel the RIP long timer, and if we do that then + // send a RIP DOWN message if needed. + // + + if (CTEStopTimer (&IpxDevice->RipLongTimer)) { + + if (IpxDevice->RipResponder) { + + if (RipQueueRequest (IpxDevice->VirtualNetworkNumber, RIP_DOWN) == STATUS_PENDING) { + + // + // If we queue a request, it will stop the timer. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + IpxDereferenceDevice (IpxDevice, DREF_LONG_TIMER); + + } else { + + // + // We couldn't stop the timer, which means it is running, + // so we need to wait for the event that is kicked when + // the RIP DOWN messages are done. + // + + if (IpxDevice->RipResponder) { + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } +} /* IpxPnPToLoad */ + + +NTSTATUS +IpxPnPReallocateBindingArray( + IN PDEVICE Device, + IN ULONG Size + ) +/*++ + +Routine Description: + + This routine reallocates the binding array when the number of bindings go above + Device->MaxBindings. + +Arguments: + + Device - pointer to the device. + Size - the number of new entries required. + +Return Value: + + None. + +--*/ +{ + PBIND_ARRAY_ELEM BindingArray; + ULONG Pad=2; // extra bindings we keep around + ULONG NewSize = Size + Pad + Device->MaxBindings; + + // + // The absolute max WAN bindings. + // + CTEAssert(Size < 2048); + + // + // Re-allocate the new array + // + BindingArray = (PBIND_ARRAY_ELEM)IpxAllocateMemory ( + NewSize * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + if (BindingArray == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)Device->DeviceObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxDereferenceDevice (Device, DREF_CREATE); + + DbgPrint ("Failed to allocate memory in binding array expansion\n"); + + // + // Unload the driver here? In case of WAN, we can tolerate this failure. What about LAN? [BUGBUGZZ] + // + + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory (BindingArray, NewSize * sizeof(BIND_ARRAY_ELEM)); + + // + // Copy the old array into the new one. + // + RtlCopyMemory (BindingArray, Device->Bindings, (Device->ValidBindings+1) * sizeof(BIND_ARRAY_ELEM)); + + // + // Free the old one. + // + IpxFreeMemory ( Device->Bindings, + Device->MaxBindings * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + IPX_DEBUG(PNP, ("Expand bindarr old: %lx, new: %lx, oldsize: %lx\n", + Device->Bindings, BindingArray, Device->MaxBindings)); + + // + // Use interlocked exchange to assign this since we dont take the BindAccessLock anymore. + // + // Device->Bindings = BindingArray; + SET_VALUE(Device->Bindings, BindingArray); + + Device->MaxBindings = (USHORT)NewSize; + + return STATUS_SUCCESS; +} +#endif _PNP_POWER + |