/*++ Copyright (c) 1993 Microsoft Corporation Module Name: tdipnp.c Abstract: TDI routines for supporting PnP in transports and transport clients. Author: Henry Sanders (henrysa) Oct. 10, 1995 Revision History: Who When What -------- -------- ---------------------------------------------- henrysa 10-10-95 created shreem 01-23-97 bug #33975 adube 01-01-01 maintenance mode - windows xp Notes: Change from the previous approach: 1. Processing the TDI_REQUEST is done in a different function. 2. Requests can be queued while another thread is notifying its clients/providers 3. These are then dequeued by the and run on a different thread using CTE functions. --*/ #include #include #include #include #include #include #include "tdipnp.h" #include "tdidebug.h" #ifdef DBG CHAR DbgMsgs[LOG_MSG_CNT][MAX_MSG_LEN]; UINT First, Last; CTELock DbgLock; ULONG TdiDebugEx = TDI_DEBUG_ERROR; ULONG TdiMemLog = //LOG_NOTIFY | //LOG_REGISTER | //LOG_POWER | 0; ULONG TdiLogOutput = LOG_OUTPUT_BUFFER /*| LOG_OUTPUT_DEBUGGER*/; #endif KSPIN_LOCK TDIListLock; LIST_ENTRY PnpHandlerRequestList; LIST_ENTRY PnpHandlerProviderList; LIST_ENTRY PnpHandlerClientList; PTDI_OPEN_BLOCK OpenList = NULL; BOOLEAN PnpHandlerRequestInProgress; PETHREAD PnpHandlerRequestThread; UINT PrevRequestType = 0; ULONG ProvidersRegistered = 0; ULONG ProvidersReady = 0; ULONG EventScheduled = 0; // structure private to tdipnp.c. used to marshall parms to a CTE event typedef struct _TDI_EXEC_PARAMS { LIST_ENTRY Linkage; UINT Signature; PLIST_ENTRY ClientList; PLIST_ENTRY ProviderList; PLIST_ENTRY RequestList; TDI_SERIALIZED_REQUEST Request; PETHREAD *CurrentThread; CTEEvent *RequestCTEEvent; PBOOLEAN SerializeFlag; BOOLEAN ResetSerializeFlag; PVOID pCallersAddress; PVOID pCallersCaller; PETHREAD pCallerThread; } TDI_EXEC_PARAMS, *PTDI_EXEC_PARAMS; typedef struct { PVOID ExecParm; UINT Type; PVOID Element; PVOID Thread; } EXEC_PARM; // Keep a short list of current and last few requests that TDI has processed. // (Debug purposes only. Current request isn't store anyware during processing) #define EXEC_CNT 8 EXEC_PARM TrackExecs[EXEC_CNT]; int NextExec; EXEC_PARM TrackExecCompletes[EXEC_CNT]; int NextExecComplete; CTEEvent BindEvent; CTEEvent AddressEvent; CTEEvent PnpHandlerEvent; PWSTR StrRegTdiBindList = L"Bind"; PWSTR StrRegTdiLinkage = L"\\Linkage"; PWSTR StrRegTdiBindingsBasicPath = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"; #define MAX_UNICODE_BUFLEN 256 // private function prototypes NTSYSAPI NTSTATUS NTAPI NtClose( IN HANDLE Handle ); NTSTATUS TdiExecuteRequest( CTEEvent *Event, PVOID pTdiExecParams ); BOOLEAN TdipIsSzInMultiSzSafe ( IN PCWSTR pszSearchString, IN PCWSTR pmsz); VOID TdipRemoveMultiSzFromSzArray ( IN PWSTR pmszToRemove, IN OUT PWSTR* pszArray, IN ULONG ItemsInArray, OUT ULONG* pRemainingItems); VOID TdipRemoveMultiSzFromMultiSz ( IN PCWSTR pmszToRemove, IN OUT PWSTR pmszToModify); NTSTATUS TdipAddMultiSzToMultiSz( IN PUNICODE_STRING pmszAdd, IN PCWSTR pmszModify, OUT PWSTR* ppmszOut); VOID TdipGetMultiSZList( PWSTR **ListPointer, PWSTR BaseKeyName, PUNICODE_STRING DeviceName, PWSTR Linkage, PWSTR ParameterKeyName, PUINT NumEntries ); BOOLEAN TdipMultiSzStrStr( PWSTR *TdiClientBindingList, PUNICODE_STRING DeviceName ); BOOLEAN TdipBuildProviderList( PTDI_NOTIFY_PNP_ELEMENT NotifyElement ); PTDI_PROVIDER_RESOURCE LocateProviderContext( PUNICODE_STRING ProviderName ); // end private protos #if DBG VOID TdipPrintMultiSz ( IN PCWSTR pmsz); VOID TdiDumpAddress( IN PTA_ADDRESS Addr ) { int j; TDI_DEBUG(ADDRESS, ("len %d ", Addr->AddressLength)); if (Addr->AddressType == TDI_ADDRESS_TYPE_IP) { TDI_DEBUG(ADDRESS, ("IP %d.%d.%d.%d\n", Addr->Address[2], Addr->Address[3], Addr->Address[4], Addr->Address[5])); } else if (Addr->AddressType == TDI_ADDRESS_TYPE_NETBIOS) { if (Addr->Address[2] == '\0') { TDI_DEBUG(ADDRESS, ("NETBIOS reserved %2x %2x %2x %2x %2x %2x\n", (ULONG)(Addr->Address[12]), (ULONG)(Addr->Address[13]), (ULONG)(Addr->Address[14]), (ULONG)(Addr->Address[15]), (ULONG)(Addr->Address[16]), (ULONG)(Addr->Address[17]))); } else { TDI_DEBUG(ADDRESS, ("NETBIOS %.16s\n", Addr->Address+2)); } } else { TDI_DEBUG(ADDRESS, ("type %d ", Addr->AddressType)); for (j = 0; j < Addr->AddressLength; j++) { TDI_DEBUG(ADDRESS, ("%2x ", (ULONG)(Addr->Address[j]))); } TDI_DEBUG(ADDRESS, ("\n")); } } #else #define TdiDumpAddress(d) (0) #define TdipPrintMultiSz(p) #endif NTSTATUS TdiNotifyPnpClientList ( PLIST_ENTRY ListHead, PVOID Info, BOOLEAN Added ) /*++ Routine Description: Arguments: ListHead - Head of list to walk. Info - Information describing the provider that changed. Added - True if a provider was added, false otherwise Return Value: --*/ { PLIST_ENTRY Current; PTDI_PROVIDER_COMMON ProviderCommon; PTDI_NOTIFY_PNP_ELEMENT NotifyPnpElement; PTDI_PROVIDER_RESOURCE Provider; NTSTATUS Status, ReturnStatus = STATUS_SUCCESS; BOOLEAN bStatus = FALSE; TDI_DEBUG(FUNCTION, ("++ TdiNotifyPnpClientList\n")); Current = ListHead->Flink; // The Info parameter is actually a pointer to a PROVIDER_COMMON // structure, so get back to that so that we can find out what kind of // provider this is. ProviderCommon = (PTDI_PROVIDER_COMMON)Info; Provider = CONTAINING_RECORD( ProviderCommon, TDI_PROVIDER_RESOURCE, Common ); if (Provider->Common.Type == TDI_RESOURCE_DEVICE) { TDI_DEBUG(PROVIDERS, ("Got new (de)registration for device %wZ\n", &Provider->Specific.Device.DeviceName)); } else if (Provider->Common.Type == TDI_RESOURCE_NET_ADDRESS) { TDI_DEBUG(PROVIDERS, ("Got new (de)registration for address ")); TdiDumpAddress(&Provider->Specific.NetAddress.Address); } // Walk the input client list, and for every element in it // notify the client. while (Current != ListHead) { NotifyPnpElement = CONTAINING_RECORD( Current, TDI_NOTIFY_PNP_ELEMENT, Common.Linkage ); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); Provider->Common.pNotifyElement = NotifyPnpElement; //Debugging info if (Provider->Common.Type == TDI_RESOURCE_DEVICE) { if (TDI_VERSION_ONE == NotifyPnpElement->TdiVersion) { if (Added) { if (NotifyPnpElement->Bind.BindHandler) { TDI_LOG(LOG_NOTIFY, ("V1 bind %wZ to %wZ\n", &Provider->Specific.Device.DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->Bind.BindHandler))( &Provider->Specific.Device.DeviceName ); } } else { if (NotifyPnpElement->Bind.UnbindHandler) { TDI_LOG(LOG_NOTIFY, ("V1 unbind %wZ from %wZ\n", &Provider->Specific.Device.DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->Bind.UnbindHandler))( &Provider->Specific.Device.DeviceName ); } } } else { if (NULL != NotifyPnpElement->BindingHandler) { // Remove any providers from the list that we are supposed // to ignore. // TdipRemoveMultiSzFromSzArray ( NotifyPnpElement->ListofBindingsToIgnore, NotifyPnpElement->ListofProviders, NotifyPnpElement->NumberofEntries, &NotifyPnpElement->NumberofEntries); // This is a device object provider. // This must be a notify bind element. if (TdipMultiSzStrStr ( NotifyPnpElement->ListofProviders, &Provider->Specific.Device.DeviceName )) { if (Added) { TDI_LOG(LOG_NOTIFY, ("Bind %wZ to %wZ\n", &Provider->Specific.Device.DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->BindingHandler))( TDI_PNP_OP_ADD, &Provider->Specific.Device.DeviceName, (PWSTR) (NotifyPnpElement->ListofProviders + NotifyPnpElement->NumberofEntries) ); } else { TDI_LOG(LOG_NOTIFY, ("Unbind %wZ from %wZ\n", &Provider->Specific.Device.DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->BindingHandler))( TDI_PNP_OP_DEL, &Provider->Specific.Device.DeviceName, (PWSTR) (NotifyPnpElement->ListofProviders + NotifyPnpElement->NumberofEntries) ); } } else { TDI_DEBUG(BIND, ("The Client %wZ wasnt interested in this Provider %wZ!\r\n", &NotifyPnpElement->ElementName, &Provider->Specific.Device.DeviceName)); } } } } else if (Provider->Common.Type == TDI_RESOURCE_NET_ADDRESS) { // This is a notify net address element. If this is // an address coming in, call the add address handler, // otherwise call delete address handler. if (TDI_VERSION_ONE == NotifyPnpElement->TdiVersion) { if (Added && (NULL != NotifyPnpElement->AddressElement.AddHandler)) { TDI_LOG(LOG_NOTIFY, ("Add address v1 %wZ to %wZ\n", &Provider->DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->AddressElement.AddHandler))( &Provider->Specific.NetAddress.Address ); } else if (NULL != NotifyPnpElement->AddressElement.DeleteHandler) { TDI_LOG(LOG_NOTIFY, ("Del address v1 %wZ from %wZ\n", &Provider->DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->AddressElement.DeleteHandler))( &Provider->Specific.NetAddress.Address ); } } else { if (Added && (NULL != NotifyPnpElement->AddressElement.AddHandlerV2)) { TDI_LOG(LOG_NOTIFY, ("Add address %wZ to %wZ\n", &Provider->DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->AddressElement.AddHandlerV2))( &Provider->Specific.NetAddress.Address, &Provider->DeviceName, Provider->Context2 ); TDI_DEBUG(ADDRESS, ("Address Handler Called: ADD!\n")); } else if (NULL != NotifyPnpElement->AddressElement.DeleteHandlerV2) { TDI_LOG(LOG_NOTIFY, ("Del address %wZ from %wZ\n", &Provider->DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->AddressElement.DeleteHandlerV2))( &Provider->Specific.NetAddress.Address, &Provider->DeviceName, Provider->Context2 ); } } } else if (Provider->Common.Type == TDI_RESOURCE_POWER) { // RESOURCE_POWER if (NotifyPnpElement->PnpPowerHandler) { TDI_DEBUG(POWER, ("PnPPower Handler Called!\n")); TDI_LOG(LOG_NOTIFY | LOG_POWER, ("Power event %d to %wZ\n", Provider->PnpPowerEvent->NetEvent, &NotifyPnpElement->ElementName)); Status = (*(NotifyPnpElement->PnpPowerHandler)) ( &Provider->Specific.Device.DeviceName, Provider->PnpPowerEvent, Provider->Context1, Provider->Context2 ); if (STATUS_PENDING == Status) { TDI_DEBUG(POWER, ("Client returned PENDING (%d) ++\n", Provider->PowerHandlers)); ReturnStatus = STATUS_PENDING; } else { // // Record the return value only if it is not SUCCESS or PENDING. // if (STATUS_SUCCESS != Status) { Provider->Status = Status; TDI_DEBUG(POWER, ("Client: %wZ returned %x\n", &NotifyPnpElement->ElementName, Provider->Status)); // // For easier routing of failures. // DbgPrint("Client: %wZ returned %x\n", &NotifyPnpElement->ElementName, Provider->Status); } InterlockedDecrement(&Provider->PowerHandlers); TDI_DEBUG(POWER, ("Client returned Immediately (%d) : ++\n", Provider->PowerHandlers)); } } } else if (Provider->Common.Type == TDI_RESOURCE_PROVIDER && Provider->ProviderReady) { // // First inform the clients about this provider and then if // ProvidersRegistered == ProvidersReady call again with NULL. // if ((TDI_VERSION_ONE != NotifyPnpElement->TdiVersion) && (NULL != NotifyPnpElement->BindingHandler)) { TDI_LOG(LOG_NOTIFY, ("%wZ ready, notify %wZ\n", &Provider->Specific.Device.DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->BindingHandler))( TDI_PNP_OP_PROVIDERREADY, &Provider->Specific.Device.DeviceName, NULL ); if (ProvidersReady == ProvidersRegistered) { TDI_LOG(LOG_NOTIFY, ("NETREADY to %wZ\n", &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->BindingHandler))( TDI_PNP_OP_NETREADY, NULL, NULL ); } else { TDI_DEBUG(BIND, ("************** Registered:%d + Ready %d\n", ProvidersRegistered, ProvidersReady)); } } else { TDI_DEBUG(PROVIDERS, ("%wZ has a NULL BindHandler\n", &NotifyPnpElement->ElementName)); } } // Get the next one. Current = Current->Flink; Provider->Common.pNotifyElement = NULL; //Debugging info Provider->Common.ReturnStatus = ReturnStatus; // Debugging info } TDI_DEBUG(FUNCTION, ("-- TdiNotifyPnpClientList : %lx\n", ReturnStatus)); return ReturnStatus; } VOID TdiNotifyNewPnpClient( PLIST_ENTRY ListHead, PVOID Info ) /*++ Routine Description: Called when a new client is added and we want to notify it of existing providers. The client can be for either binds or net addresses. We walk the specified input list, and notify the client about each entry in it. Arguments: ListHead - Head of list to walk. Info - Information describing the new client to be notified. Return Value: --*/ { PLIST_ENTRY CurrentEntry; PTDI_NOTIFY_COMMON NotifyCommon; PTDI_PROVIDER_RESOURCE Provider; PTDI_NOTIFY_PNP_ELEMENT NotifyPnpElement; PWSTR MultiSZBindList = NULL; TDI_DEBUG(FUNCTION, ("++ TdiNotifyNewPnpClient\n")); CurrentEntry = ListHead->Flink; // The info is actually a pointer to a client notify element. Cast // it to the common type. NotifyCommon = (PTDI_NOTIFY_COMMON)Info; NotifyPnpElement = CONTAINING_RECORD( NotifyCommon, TDI_NOTIFY_PNP_ELEMENT, Common ); TDI_DEBUG(CLIENTS, ("New handler set registered by %wZ\n", &NotifyPnpElement->ElementName)); // Walk the input provider list, and for every element in it notify // the new client. while (CurrentEntry != ListHead) { // If the new client is for bind notifys, set up to call it's bind // handler. // Put the current provider element into the proper form. Provider = CONTAINING_RECORD( CurrentEntry, TDI_PROVIDER_RESOURCE, Common.Linkage ); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); if (Provider->Common.Type == TDI_RESOURCE_DEVICE) { if (TDI_VERSION_ONE == NotifyPnpElement->TdiVersion ) { if (NotifyPnpElement->Bind.BindHandler) { TDI_LOG(LOG_NOTIFY, ("V1 bind %wZ to %wZ\n", &Provider->Specific.Device.DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->Bind.BindHandler))( &Provider->Specific.Device.DeviceName ); } } else { if (NULL != NotifyPnpElement->BindingHandler) { // This is a bind notify client. if (TdipMultiSzStrStr( NotifyPnpElement->ListofProviders, &Provider->Specific.Device.DeviceName )) { TDI_DEBUG(BIND, ("Telling new handlers to bind to %wZ\n", &Provider->Specific.Device.DeviceName)); TDI_LOG(LOG_NOTIFY, ("bind(new) %wZ to %wZ\n", &Provider->Specific.Device.DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->BindingHandler))( TDI_PNP_OP_ADD, &Provider->Specific.Device.DeviceName, (PWSTR) (NotifyPnpElement->ListofProviders + NotifyPnpElement->NumberofEntries) ); } else { TDI_DEBUG(BIND, ("The Client %wZ wasnt interested in this Provider %wZ!\r\n", &NotifyPnpElement->ElementName, &Provider->Specific.Device.DeviceName)); } } else { TDI_DEBUG(BIND, ("The client %wZ has a NULL Binding Handler\n", &NotifyPnpElement->ElementName)); } } } else if (Provider->Common.Type == TDI_RESOURCE_NET_ADDRESS) { // This is an address notify client. // cant be TDI_RESOURCE_POWER coz we never put it on the list! - ShreeM if (TDI_VERSION_ONE == NotifyPnpElement->TdiVersion) { if (NULL != NotifyPnpElement->AddressElement.AddHandler) { TDI_LOG(LOG_NOTIFY, ("Add address v1 %wZ to %wZ\n", &Provider->DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->AddressElement.AddHandler))( &Provider->Specific.NetAddress.Address ); } } else { if (NotifyPnpElement->AddressElement.AddHandlerV2) { TdiDumpAddress(&Provider->Specific.NetAddress.Address); TDI_LOG(LOG_NOTIFY, ("Add address(2) %wZ to %wZ\n", &Provider->DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->AddressElement.AddHandlerV2))( &Provider->Specific.NetAddress.Address, &Provider->DeviceName, Provider->Context2 ); } } } // And do the next one. CurrentEntry = CurrentEntry->Flink; } // // Now the providers who are ready. // if (NULL == NotifyPnpElement->BindingHandler) { // // If the Bindhandler is NULL, further action is pointless. // TDI_DEBUG(PROVIDERS, ("%wZ has a NULL BindHandler!!\n", &NotifyPnpElement->ElementName)); TDI_DEBUG(FUNCTION, ("-- TdiNotifyNewPnpClient\n")); return; } if (TDI_VERSION_ONE == NotifyPnpElement->TdiVersion) { // // If the Bindhandler is NULL, further action is pointless. // TDI_DEBUG(PROVIDERS, ("This is a TDI v.1 client!\n")); TDI_DEBUG(FUNCTION, ("-- TdiNotifyNewPnpClient\n")); return; } // Otherwise, we can start the loop again. // Yes, maintaining different lists for addresses, providers, and devices // might be more efficient and I will do this later. CurrentEntry = ListHead->Flink; while (CurrentEntry != ListHead) { Provider = CONTAINING_RECORD( CurrentEntry, TDI_PROVIDER_RESOURCE, Common.Linkage ); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); if (Provider->Common.Type == TDI_RESOURCE_PROVIDER && Provider->ProviderReady) { // // First inform the clients about this provider and then if // ProvidersRegistered == ProvidersReady call again with NULL. // TDI_LOG(LOG_NOTIFY, ("%wZ ready2, notify %wZ\n", &Provider->Specific.Device.DeviceName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->BindingHandler))( TDI_PNP_OP_PROVIDERREADY, &Provider->Specific.Device.DeviceName, NULL ); } // And do the next one. CurrentEntry = CurrentEntry->Flink; } if (ProvidersReady == ProvidersRegistered) { TDI_LOG(LOG_NOTIFY, ("NETREADY2 to %wZ\n", &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->BindingHandler))( TDI_PNP_OP_NETREADY, NULL, NULL ); } else { TDI_DEBUG(PROVIDERS, ("Provider Ready Status: Registered:%d + Ready:%d\n", ProvidersRegistered, ProvidersReady)); } TDI_DEBUG(FUNCTION, ("-- TdiNotifyNewPnpClient\n")); } VOID TdiNotifyAddresses( PLIST_ENTRY ListHead, PVOID Info ) /*++ Routine Description: Called when a client wants to know about all the TDI Addresses Arguments: ListHead - Head of list to walk. Info - Information describing the new client to be notified. Return Value: --*/ { PLIST_ENTRY CurrentEntry; PTDI_NOTIFY_COMMON NotifyCommon; PTDI_PROVIDER_RESOURCE Provider; PTDI_NOTIFY_PNP_ELEMENT NotifyPnpElement; TDI_DEBUG(FUNCTION, ("++ TdiNotifyAddresses\n")); CurrentEntry = ListHead->Flink; // The info is actually a pointer to a client notify element. Cast // it to the common type. NotifyCommon = (PTDI_NOTIFY_COMMON)Info; NotifyPnpElement = CONTAINING_RECORD( NotifyCommon, TDI_NOTIFY_PNP_ELEMENT, Common ); TDI_DEBUG(CLIENTS, ("%wZ wants to know about all the addresses\n", &NotifyPnpElement->ElementName)); // Walk the input provider list, and for every element in it notify // the new client. while (CurrentEntry != ListHead) { // If the new client is for bind notifys, set up to call it's bind // handler. // Put the current provider element into the proper form. Provider = CONTAINING_RECORD( CurrentEntry, TDI_PROVIDER_RESOURCE, Common.Linkage ); if (Provider->Common.Type == TDI_RESOURCE_NET_ADDRESS) { if (NotifyPnpElement->AddressElement.AddHandlerV2) { TDI_DEBUG(ADDRESS, ("Add Address Handler\n")); TDI_DEBUG(CLIENTS, ("Telling new handlers about address: ")); TdiDumpAddress(&Provider->Specific.NetAddress.Address); (*(NotifyPnpElement->AddressElement.AddHandlerV2))( &Provider->Specific.NetAddress.Address, &Provider->DeviceName, Provider->Context2 ); } } // And do the next one. CurrentEntry = CurrentEntry->Flink; } TDI_DEBUG(FUNCTION, ("-- TdiNotifyAddresses\n")); } VOID TdiHandlePnpOperation( PLIST_ENTRY ListHead, PVOID Info ) { PLIST_ENTRY Current; PTDI_NOTIFY_PNP_ELEMENT NotifyPnpElement; PTDI_PROVIDER_RESOURCE ProviderElement; PTDI_NCPA_BINDING_INFO NCPABindingInfo; NTSTATUS Status = STATUS_SUCCESS; NET_PNP_EVENT NetEvent; ULONG Operation; BOOLEAN DeviceRegistered = FALSE; BOOLEAN ClientFound = FALSE; UINT NumberofEntries = 0; TDI_DEBUG(FUNCTION, ("---------------------------> ++ TdiHandlePnpOperation!!\n")); ASSERT(NULL != Info); ASSERT(NULL != ListHead); Current = ListHead->Flink; // The Info parameter is actually a pointer to TDI_NCPA_BINDING_INFO // structure. NCPABindingInfo = (PTDI_NCPA_BINDING_INFO) Info; Operation = (ULONG) NCPABindingInfo->PnpOpcode; // Walk the input client list, and see if that is the client we are looking for. while (Current != ListHead) { NotifyPnpElement = CONTAINING_RECORD( Current, TDI_NOTIFY_PNP_ELEMENT, Common.Linkage ); if (!RtlCompareUnicodeString( NCPABindingInfo->TdiClientName, &NotifyPnpElement->ElementName, TRUE) ) { TDI_DEBUG(NCPA, ("Found the TDI client for the message from NCPA\n")); ClientFound = TRUE; break; } Current = Current->Flink; } if (!ClientFound) { // // Cant do much if the client's handlers are not registered. // return; } else { // // Let's update the ListofProviders for this client. // Add a new provider to the Client's list of Providers... // if (NotifyPnpElement->ListofProviders) { TDI_DEBUG(NCPA, ("Before this BIND - Client %wZ was interested in %lx Providers\n", &NotifyPnpElement->ElementName, NotifyPnpElement->NumberofEntries)); ExFreePool(NotifyPnpElement->ListofProviders); TDI_DEBUG(NCPA, ("Freed the previous List of Providers\n")); } else { TDI_DEBUG(NCPA, ("List of providers was NULL for %wZ\n", &NotifyPnpElement->ElementName)); } TdipBuildProviderList( (PTDI_NOTIFY_PNP_ELEMENT) NotifyPnpElement ); TDI_DEBUG(NCPA, ("Built New BindList - %wZ is interested in %lx Providers after BIND\n", &NotifyPnpElement->ElementName, NotifyPnpElement->NumberofEntries)); } // // If it is a reconfigure, or an add or delete ignore binding, // don't see if device (provider) is registered. // if ((RECONFIGURE == Operation) || (ADD_IGNORE_BINDING == Operation) || (DEL_IGNORE_BINDING == Operation)) { goto DeviceNotRequired; } // // If we are here, the client exists. Check if the provider has registered the device // Current = PnpHandlerProviderList.Flink; while (Current != &PnpHandlerProviderList) { ProviderElement = CONTAINING_RECORD( Current, TDI_PROVIDER_RESOURCE, Common.Linkage ); if (ProviderElement->Common.Type != TDI_RESOURCE_DEVICE) { Current = Current->Flink; continue; } if (!RtlCompareUnicodeString(NCPABindingInfo->TdiProviderName, &ProviderElement->Specific.Device.DeviceName, TRUE)) { TDI_DEBUG(NCPA, ("Provider is registered with TDI\n")); DeviceRegistered = TRUE; break; } Current = Current->Flink; } if (!DeviceRegistered) { if (NULL != NotifyPnpElement->BindingHandler) { TDI_LOG(LOG_NOTIFY, ("Device is not registered, doing OP_UPDATE, %wZ to %wZ\n", NCPABindingInfo->TdiProviderName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->BindingHandler))( TDI_PNP_OP_UPDATE, NCPABindingInfo->TdiProviderName, (PWSTR) (NotifyPnpElement->ListofProviders + NotifyPnpElement->NumberofEntries) ); } else { TDI_DEBUG(NCPA, ("Device is not registered, the BindHandler was NULL\n")); } return; } DeviceNotRequired: // // We need to manufacture a NET_PNP_EVENT here. // RtlZeroMemory (NetEvent.TdiReserved, sizeof(NetEvent.TdiReserved)); // // Depending on the NetEvent, we call a different handler. // switch (Operation) { case BIND: // // First check if the TDI Client is interested in the Provider // if (TdipMultiSzStrStr( NotifyPnpElement->ListofProviders, &ProviderElement->Specific.Device.DeviceName )) { TDI_DEBUG(NCPA, ("The Client %wZ is interested in provider %wZ\n", &NotifyPnpElement->ElementName, &ProviderElement->Specific.Device.DeviceName )); } else { TDI_DEBUG(NCPA, ("RANDOM BIND CALL!!!\n")); TDI_DEBUG(NCPA, ("The Client %wZ is NOT interested in provider %wZ\n", &NotifyPnpElement->ElementName, &ProviderElement->Specific.Device.DeviceName )); } if (NULL != NotifyPnpElement->BindingHandler) { TDI_LOG(LOG_NOTIFY, ("Pnp Bind %wZ to %wZ\n", NCPABindingInfo->TdiProviderName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->BindingHandler))( TDI_PNP_OP_ADD, NCPABindingInfo->TdiProviderName, (PWSTR) (NotifyPnpElement->ListofProviders + NotifyPnpElement->NumberofEntries) ); // // Here we should also update the NotifyElement's buffer. // } else { TDI_DEBUG(NCPA, ("The BindHandler was NULL\n")); } break; case UNBIND: // // The plan is to do a QueryRemove first and then call UnBind. // if (NotifyPnpElement->PnpPowerHandler) { TDI_DEBUG(POWER, ("UNBind Handler Called!: First QueryRemoveDevice\n")); NetEvent.NetEvent = NetEventQueryRemoveDevice; NetEvent.Buffer = NULL; NetEvent.BufferLength = 0; // The TDI Client should look at the OpCode in NetEvent and decide how to use the buffer. Status = (*(NotifyPnpElement->PnpPowerHandler)) ( NCPABindingInfo->TdiProviderName, &NetEvent, NULL, NULL ); if (STATUS_PENDING == Status) { TDI_DEBUG(POWER, ("Client returned PENDING for QueryPower!\n")); //DbgBreakPoint(); } } else { TDI_DEBUG(NCPA, ("The PnpPowerHandler was NULL\n")); } // // OK, now call the UNBIND HANDLER anyway // case UNBIND_FORCE: // RDR returns PENDING all the time, we need a mechanism to fix this. // if (((STATUS_PENDING == Status) || (STATUS_SUCCESS == Status)) && (NULL != NotifyPnpElement->BindingHandler)) { if ((STATUS_SUCCESS == Status) && (NULL != NotifyPnpElement->BindingHandler)) { TDI_LOG(LOG_NOTIFY, ("Pnp Unbind %wZ from %wZ\n", NCPABindingInfo->TdiProviderName, &NotifyPnpElement->ElementName)); (*(NotifyPnpElement->BindingHandler))( TDI_PNP_OP_DEL, NCPABindingInfo->TdiProviderName, (PWSTR) (NotifyPnpElement->ListofProviders + NotifyPnpElement->NumberofEntries) ); } else { TDI_DEBUG(NCPA, ("The BindHandler was NULL\n")); } break; case RECONFIGURE: // // If the Reconfigure Buffer is NULL, we are notifying it of a NetEventBindList // Otherwise we are notifying it of a NetEventReconfig. Need to do the dirty work // of setting up the NET_PNP_EVENT accordingly. // TDI_DEBUG(POWER, ("Reconfigure Called.\n")); // // If the ReconfigBufferLength greater than 0, its Reconfig // if (NCPABindingInfo->ReconfigBufferSize) { NetEvent.BufferLength = NCPABindingInfo->ReconfigBufferSize; NetEvent.Buffer = NCPABindingInfo->ReconfigBuffer; NetEvent.NetEvent = NetEventReconfigure; } else { // // Else, its a BindOrder change // NetEvent.BufferLength = NCPABindingInfo->BindList->Length; NetEvent.Buffer = NCPABindingInfo->BindList->Buffer; NetEvent.NetEvent = NetEventBindList; } if (NotifyPnpElement->PnpPowerHandler) { // The TDI Client should look at the OpCode in NetEvent and decide how to use the buffer. TDI_LOG(LOG_NOTIFY, ("Pnp Reconfig %wZ to %wZ\n", NCPABindingInfo->TdiProviderName, &NotifyPnpElement->ElementName)); Status = (*(NotifyPnpElement->PnpPowerHandler)) ( NCPABindingInfo->TdiProviderName, &NetEvent, NULL, NULL ); if (STATUS_PENDING == Status) { TDI_DEBUG(POWER, ("Client returned PENDING for QueryPower!\n")); //DbgBreakPoint(); } } else { TDI_DEBUG(NCPA, ("The PnpPowerHandler was NULL\n")); } break; case ADD_IGNORE_BINDING: { // We are being told to add a binding to a list of bindings // to ignore for this client. These are bindings we will // not indicate to the client. // PWSTR pmszNewIgnoreList; ASSERT (NCPABindingInfo->BindList); // If a non-null bindlist was given... if (NCPABindingInfo->BindList) { TDI_DEBUG(BIND, ("Adding the following multi-sz to the ignore list\n")); //TdipPrintMultiSz (NCPABindingInfo->BindList->Buffer); // We need to add some bindings to our list of // bindings to ignore. // TdipAddMultiSzToMultiSz (NCPABindingInfo->BindList, NotifyPnpElement->ListofBindingsToIgnore, &pmszNewIgnoreList); if (pmszNewIgnoreList) { // If we have a new list, free the old one. // if (NotifyPnpElement->ListofBindingsToIgnore) { ExFreePool (NotifyPnpElement->ListofBindingsToIgnore); } NotifyPnpElement->ListofBindingsToIgnore = pmszNewIgnoreList; TDI_DEBUG(BIND, ("Printing new ignore list\n")); TdipPrintMultiSz (NotifyPnpElement->ListofBindingsToIgnore); } } break; } case DEL_IGNORE_BINDING: // We are being told to remove bindings from a list of bindings // to ignore for this client. These are bindings we will // now indicate to the client if we need to. // // If we don't have a current list of bindings to ignore // or the bindlist sent was NULL then there is no work to do. // We assert on a NULL BindList because it shouldn't happen. ASSERT(NCPABindingInfo->BindList); if (NotifyPnpElement->ListofBindingsToIgnore && NCPABindingInfo->BindList) { TDI_DEBUG(BIND, ("Removing the following multi-sz from the ignore list\n")); //TdipPrintMultiSz (NCPABindingInfo->BindList->Buffer); // We need to remove some bindings from our list of bindings // to ignore. // TdipRemoveMultiSzFromMultiSz (NCPABindingInfo->BindList->Buffer, NotifyPnpElement->ListofBindingsToIgnore); // If the list of bindings to ignore is now empty, // free the memory. // if (*NotifyPnpElement->ListofBindingsToIgnore) { ExFreePool (NotifyPnpElement->ListofBindingsToIgnore); NotifyPnpElement->ListofBindingsToIgnore = NULL; } TDI_DEBUG(BIND, ("Printing new ignore list\n")); TdipPrintMultiSz (NotifyPnpElement->ListofBindingsToIgnore); } break; } TDI_DEBUG(FUNCTION, ("---------------------------> -- TdiHandlePnpOperation!!\n")); } NTSTATUS TdiExecuteRequest( IN CTEEvent *Event, IN PVOID pParams ) /*++ Routine Description: Called by TdiHandleSerializedRequest to execute the request. It has been made another function, so that a worker thread can execute this function. Parameters: CTEEvent (Event) : If this is NULL, it means that we have been called directly from TdiHandleSerializedRequest. Otherwise, it is called from the WorkerThread PVOID (pParams) : This can NEVER be NULL. It tells this function of the work that needs to be done. Output: NT_STATUS. --*/ { PTDI_PROVIDER_RESOURCE ProviderElement, Context; PTDI_NOTIFY_COMMON NotifyElement; KIRQL OldIrql; PLIST_ENTRY List; PTDI_EXEC_PARAMS pTdiExecParams, pNextParams = NULL; NTSTATUS Status = STATUS_SUCCESS; ULONG ScheduledTest = 0; PTDI_NOTIFY_PNP_ELEMENT PnpNotifyElement = NULL; TDI_DEBUG(FUNCTION2, ("++ TdiExecuteRequest\n")); // we are in trouble if pParams is NULL ASSERT(NULL != pParams); pTdiExecParams = (PTDI_EXEC_PARAMS) pParams; if (NULL == pTdiExecParams) { TDI_DEBUG(PARAMETERS, ("TDIExecRequest: params NULL\n")); DbgBreakPoint(); } if(0x1234cdef != pTdiExecParams->Signature) { TDI_DEBUG(PARAMETERS, ("signature is BAD - %d not 0x1234cdef\r\n", pTdiExecParams->Signature)); DbgBreakPoint(); } ExAcquireSpinLock( &TDIListLock, &OldIrql ); if (pTdiExecParams->Request.Event != NULL) { *(pTdiExecParams->CurrentThread) = PsGetCurrentThread(); } // DEBUG TRACKING ++++++++++++++++++ TrackExecs[NextExec].ExecParm = pTdiExecParams; TrackExecs[NextExec].Type = pTdiExecParams->Request.Type; TrackExecs[NextExec].Element = pTdiExecParams->Request.Element; TrackExecs[NextExec].Thread = pTdiExecParams->CurrentThread; if (++NextExec == EXEC_CNT) NextExec = 0; // DEBUG TRACKING ++++++++++++++++++ PrevRequestType = pTdiExecParams->Request.Type; ExReleaseSpinLock( &TDIListLock, OldIrql ); switch (pTdiExecParams->Request.Type) { case TDI_REGISTER_HANDLERS_PNP: // This is a client register bind or address handler request. // Insert this one into the registered client list. NotifyElement = (PTDI_NOTIFY_COMMON)pTdiExecParams->Request.Element; InsertTailList( pTdiExecParams->ClientList, &NotifyElement->Linkage ); // // Generate the list of new TDI_OPEN_BLOCKS caused by the new // Client. If the provider isnt here, we set it to NULL for now. // TdipBuildProviderList( (PTDI_NOTIFY_PNP_ELEMENT) NotifyElement ); // Call TdiNotifyNewClient to notify this new client of all // all existing providers. TdiNotifyNewPnpClient( pTdiExecParams->ProviderList, pTdiExecParams->Request.Element ); break; case TDI_DEREGISTER_HANDLERS_PNP: // This is a client deregister request. Pull him from the // client list, free it, and we're done. NotifyElement = (PTDI_NOTIFY_COMMON)pTdiExecParams->Request.Element; CTEAssert(NotifyElement->Linkage.Flink != (PLIST_ENTRY)UlongToPtr(0xabababab)); CTEAssert(NotifyElement->Linkage.Blink != (PLIST_ENTRY)UlongToPtr(0xefefefef)); RemoveEntryList(&NotifyElement->Linkage); NotifyElement->Linkage.Flink = (PLIST_ENTRY)UlongToPtr(0xabababab); NotifyElement->Linkage.Blink = (PLIST_ENTRY)UlongToPtr(0xefefefef); // for the new handlers, we also have the name there. PnpNotifyElement = (PTDI_NOTIFY_PNP_ELEMENT)pTdiExecParams->Request.Element; // the name can be NULL, as in the case of TCP/IP. if (NULL != PnpNotifyElement->ElementName.Buffer) { ExFreePool(PnpNotifyElement->ElementName.Buffer); } if (NULL != PnpNotifyElement->ListofProviders) { ExFreePool(PnpNotifyElement->ListofProviders); } ExFreePool(NotifyElement); break; case TDI_REGISTER_PROVIDER_PNP: InterlockedIncrement(&ProvidersRegistered); case TDI_REGISTER_DEVICE_PNP: case TDI_REGISTER_ADDRESS_PNP: // A provider is registering a device or address. Add him to // the appropriate provider list, and then notify all // existing clients of the new device. ProviderElement = (PTDI_PROVIDER_RESOURCE) pTdiExecParams->Request.Element; InsertTailList( pTdiExecParams->ProviderList, &ProviderElement->Common.Linkage ); // Call TdiNotifyClientList to do the hard work. TdiNotifyPnpClientList( pTdiExecParams->ClientList, pTdiExecParams->Request.Element, TRUE ); break; case TDI_DEREGISTER_PROVIDER_PNP: InterlockedDecrement(&ProvidersRegistered); case TDI_DEREGISTER_DEVICE_PNP: case TDI_DEREGISTER_ADDRESS_PNP: // A provider device or address is deregistering. Pull the // resource from the provider list, and notify clients that // he's gone. ProviderElement = (PTDI_PROVIDER_RESOURCE)pTdiExecParams->Request.Element; CTEAssert(ProviderElement->Common.Linkage.Flink != (PLIST_ENTRY)UlongToPtr(0xabababab)); CTEAssert(ProviderElement->Common.Linkage.Blink != (PLIST_ENTRY)UlongToPtr(0xefefefef)); RemoveEntryList(&ProviderElement->Common.Linkage); ProviderElement->Common.Linkage.Flink = (PLIST_ENTRY) UlongToPtr(0xabababab); ProviderElement->Common.Linkage.Blink = (PLIST_ENTRY) UlongToPtr(0xefefefef); // // Dont have to tell the clients if this is a ProviderDeregister. // if (pTdiExecParams->Request.Type == TDI_DEREGISTER_PROVIDER_PNP) { if (ProviderElement->ProviderReady) { InterlockedDecrement(&ProvidersReady); } } else { TdiNotifyPnpClientList( pTdiExecParams->ClientList, pTdiExecParams->Request.Element, FALSE ); } // Free the tracking structure we had. if (pTdiExecParams->Request.Type == TDI_DEREGISTER_DEVICE_PNP) { ExFreePool(ProviderElement->Specific.Device.DeviceName.Buffer); } if (ProviderElement->DeviceName.Buffer) { ExFreePool(ProviderElement->DeviceName.Buffer); ProviderElement->DeviceName.Buffer = NULL; ProviderElement->DeviceName.Length = 0; ProviderElement->DeviceName.MaximumLength = 0; } if (ProviderElement->Context2) { ExFreePool(ProviderElement->Context2); ProviderElement->Context2 = NULL; } ExFreePool(ProviderElement); break; case TDI_REGISTER_PNP_POWER_EVENT: // Inform all the Clients of the Power Event, which has come from // a transport... ProviderElement = (PTDI_PROVIDER_RESOURCE)pTdiExecParams->Request.Element; /* KeInitializeEvent( &ProviderElement->PowerSyncEvent, SynchronizationEvent, FALSE ); */ // // Figure out how many clients we are going to inform. // { PLIST_ENTRY Current; PTDI_NOTIFY_PNP_ELEMENT NotifyPnpElement; ProviderElement->PowerHandlers = 1; Current = pTdiExecParams->ClientList->Flink; while (Current != pTdiExecParams->ClientList) { NotifyPnpElement = CONTAINING_RECORD( Current, TDI_NOTIFY_PNP_ELEMENT, Common.Linkage ); // RESOURCE_POWER if (NotifyPnpElement->PnpPowerHandler) { ProviderElement->PowerHandlers++; } // Get the next one. TDI_DEBUG(POWER, ("%d PowerCallBacks expected\n", ProviderElement->PowerHandlers)); Current = Current->Flink; } } TDI_LOG(LOG_POWER, ("%X, %d resources to notify\n", ProviderElement, ProviderElement->PowerHandlers)); Status = TdiNotifyPnpClientList( pTdiExecParams->ClientList, pTdiExecParams->Request.Element, FALSE // NOP: this param is ignored ); TDI_DEBUG(POWER, ("The client list returned %lx\n", Status)); TDI_LOG(LOG_POWER, ("%X, NotityClients returned %X\n", ProviderElement, Status)); if (!InterlockedDecrement(&ProviderElement->PowerHandlers)) { PTDI_PROVIDER_RESOURCE Temp; TDI_DEBUG(POWER, ("Power Handlers All done...\n", ProviderElement->PowerHandlers)); Temp = Context = *((PTDI_PROVIDER_RESOURCE *) ProviderElement->PnpPowerEvent->TdiReserved); // // Loop thru and see if there are any previous contexts associated // with this netpnp event, in which case, pop it. // Status = ProviderElement->Status; if (Temp->PreviousContext) { while (Temp->PreviousContext) { Context = Temp; Temp = Temp->PreviousContext; } Context->PreviousContext = NULL; //pop the last guy } else { // // This was the only pointer in the TdiReserved and we dont need it anymore // RtlZeroMemory(ProviderElement->PnpPowerEvent->TdiReserved, sizeof(ProviderElement->PnpPowerEvent->TdiReserved)); } TDI_LOG(LOG_POWER, ("%X, pnp power complete, Call completion at %X\n", ProviderElement, ProviderElement->PnPCompleteHandler)); if (pTdiExecParams->Request.Pending && (*(ProviderElement->PnPCompleteHandler))) { (*(ProviderElement->PnPCompleteHandler))( ProviderElement->PnpPowerEvent, ProviderElement->Status ); } } else { TDI_DEBUG(POWER, ("At least one of them is pending \n STATUS from ExecuteHAndler:%x\n", Status)); TDI_LOG(LOG_POWER, ("%X, a client didn't complete pnp power sync\n", ProviderElement)); } TDI_DEBUG(POWER, ("<<<>>>> : Returning %lx\n", Status)); break; case TDI_NDIS_IOCTL_HANDLER_PNP: TdiHandlePnpOperation( pTdiExecParams->ClientList, pTdiExecParams->Request.Element ); break; case TDI_ENUMERATE_ADDRESSES: // Insert this one into the registered client list. NotifyElement = (PTDI_NOTIFY_COMMON)pTdiExecParams->Request.Element; // Call TdiNotifyNewClient to notify this new client of all // all existing providers. TdiNotifyAddresses( pTdiExecParams->ProviderList, pTdiExecParams->Request.Element ); break; case TDI_PROVIDER_READY_PNP: // // Loop through and tell each client about it. // InterlockedIncrement(&ProvidersReady); ProviderElement = (PTDI_PROVIDER_RESOURCE)pTdiExecParams->Request.Element; ProviderElement->ProviderReady = TRUE; TdiNotifyPnpClientList( pTdiExecParams->ClientList, pTdiExecParams->Request.Element, TRUE ); break; default: TDI_DEBUG(ERROR, ("unknown switch statement\n")); CTEAssert(FALSE); break; } // If there was an event specified with this request, signal // it now. This should only be a client deregister request, which // needs to block until it's completed. if (pTdiExecParams->Request.Event != NULL) { // // If we had this thread marked to prevent re-entrant requests, then // clear that. Note that we do this BEFORE we set the event below to // let the thread go, since it may immediately resubmit another request. // *(pTdiExecParams->CurrentThread) = NULL; KeSetEvent(pTdiExecParams->Request.Event, 0, FALSE); } ExAcquireSpinLock( &TDIListLock, &OldIrql ); // DEBUG TRACKING ++++++++++++++++++ TrackExecCompletes[NextExecComplete].ExecParm = pTdiExecParams; TrackExecCompletes[NextExecComplete].Type = pTdiExecParams->Request.Type; TrackExecCompletes[NextExecComplete].Element = pTdiExecParams->Request.Element; TrackExecCompletes[NextExecComplete].Thread = pTdiExecParams->CurrentThread; if (++NextExecComplete == EXEC_CNT) NextExecComplete = 0; // DEBUG TRACKING ++++++++++++++++++ // // If this request occured on a worker thread // reset the EventScheduled to FALSE // if (Event != NULL) { EventScheduled = FALSE; } if (!IsListEmpty(pTdiExecParams->RequestList)) { if (EventScheduled == FALSE) { // // The following should indicate that no new events should be created. // EventScheduled = TRUE; // The request list isn't empty. Pull the next one from // the list and process it. List = RemoveHeadList(pTdiExecParams->RequestList); pNextParams = CONTAINING_RECORD(List, TDI_EXEC_PARAMS, Linkage); ExReleaseSpinLock( &TDIListLock, OldIrql ); // Schedule a thread to deal with this work // To fix bug# 33975 if(0x1234cdef != pNextParams->Signature) { TDI_DEBUG(PARAMETERS, ("2 Signature is BAD - %d not 0x1234cdef\r\n", pTdiExecParams->Signature)); DbgBreakPoint(); } ASSERT(pNextParams != NULL); ASSERT(0x1234cdef == pNextParams->Signature); PrevRequestType = pNextParams->Request.Type; CTEInitEvent(pNextParams->RequestCTEEvent, TdiExecuteRequest); CTEScheduleEvent(pNextParams->RequestCTEEvent, pNextParams); } else { ExReleaseSpinLock( &TDIListLock, OldIrql ); } ExFreePool(pTdiExecParams); } else { // The request list is empty. Clear the flag and we're done. // IMP: Since Serializataion can be bypassed // (the TdiSerializeRequest allows one type of request to bypass // serialization), // we need to make sure there are no other worker threads // currently processing TdiRequests if (pTdiExecParams->ResetSerializeFlag && EventScheduled == FALSE) { *(pTdiExecParams->SerializeFlag) = FALSE; } else { TDI_LOG(LOG_POWER, ("Not resetting serialized flag\n")); } PrevRequestType = 0; ExReleaseSpinLock( &TDIListLock, OldIrql ); ExFreePool(pTdiExecParams); } TDI_DEBUG(FUNCTION2, ("-- TdiExecuteRequest\n")); return Status; } NTSTATUS TdiHandleSerializedRequest ( PVOID RequestInfo, UINT RequestType ) /*++ Routine Description: Called when we want to process a request relating to one of the lists we manage. We look to see if we are currently processing such a request - if we are, we queue this for later. Otherwise we'll remember that we are doing this, and we'll process this request. When we're done we'll look to see if any more came in while we were busy. Arguments: RequestInfo - Reqeust specific information. RequestType - The type of the request. Return Value: Request completion status. --*/ { KIRQL OldIrql; PLIST_ENTRY List; PLIST_ENTRY ClientList; PLIST_ENTRY ProviderList; PLIST_ENTRY PnpHandlerList; PLIST_ENTRY RequestList; PBOOLEAN SerializeFlag; PETHREAD *RequestThread; PTDI_SERIALIZED_REQUEST Request; CTEEvent *pEvent; PTDI_EXEC_PARAMS pTdiExecParams; NTSTATUS Status = STATUS_SUCCESS; PVOID pCallersAddress; PVOID pCallersCallers; PETHREAD pCallerThread; TDI_DEBUG(FUNCTION2, ("++ TdiHandleSerializedRequest\n")); // Initialize tracking information RtlGetCallersAddress(&pCallersAddress, &pCallersCallers); pCallerThread = PsGetCurrentThread (); ExAcquireSpinLock( &TDIListLock, &OldIrql ); // means PnP handlers if (RequestType > TDI_MAX_ADDRESS_REQUEST) { ClientList = &PnpHandlerClientList; ProviderList = &PnpHandlerProviderList; RequestList = &PnpHandlerRequestList; SerializeFlag = &PnpHandlerRequestInProgress; RequestThread = &PnpHandlerRequestThread; pEvent = &PnpHandlerEvent; } else { TDI_DEBUG(FUNCTION2, ("-- TdiHandleSerializedRequest\n")); TDI_DEBUG(PARAMETERS, ("TDIHANDLESERIALIZEDREQUEST: BAD Request!!\r\n")); ExReleaseSpinLock( &TDIListLock, OldIrql ); return STATUS_UNSUCCESSFUL; } // We only need to allocate memory if this isn't a deregister call. if (RequestType != TDI_DEREGISTER_HANDLERS_PNP) { pTdiExecParams = (PTDI_EXEC_PARAMS)ExAllocatePoolWithTag( NonPagedPool, sizeof(TDI_EXEC_PARAMS), 'aIDT' ); if (NULL == pTdiExecParams) { ExReleaseSpinLock( &TDIListLock, OldIrql ); TDI_DEBUG(FUNCTION2, ("-- TdiHandleSerializedRequest : INSUFFICIENT RESOURCES\n")); return STATUS_INSUFFICIENT_RESOURCES; } } else { // We preallocated memory during register for this deregister call // so that it won't fail sue to low memory conditions. pTdiExecParams = ((PTDI_NOTIFY_PNP_ELEMENT)RequestInfo)->pTdiDeregisterExecParams; } RtlZeroMemory(&pTdiExecParams->Request, sizeof(TDI_SERIALIZED_REQUEST)); // Got the request. pTdiExecParams->Request.Element = RequestInfo; pTdiExecParams->Request.Type = RequestType; pTdiExecParams->Request.Event = NULL; // marshal params into a structure // Set the Request Structure, so that we can process it in the TdiExecute function. pTdiExecParams->ClientList = ClientList; pTdiExecParams->ProviderList = ProviderList; pTdiExecParams->RequestList = RequestList; pTdiExecParams->SerializeFlag = SerializeFlag; pTdiExecParams->RequestCTEEvent = pEvent; pTdiExecParams->CurrentThread = RequestThread; pTdiExecParams->Signature = 0x1234cdef; pTdiExecParams->ResetSerializeFlag = TRUE; pTdiExecParams->pCallersAddress = pCallersAddress; pTdiExecParams->pCallersCaller = pCallersCallers; pTdiExecParams->pCallerThread = pCallerThread; // If we're not already here, handle it right away. if ((!(*SerializeFlag)) || (((PrevRequestType == TDI_REGISTER_PNP_POWER_EVENT) || (PrevRequestType == TDI_NDIS_IOCTL_HANDLER_PNP)) && (RequestType == TDI_REGISTER_PNP_POWER_EVENT)) ) { if (*SerializeFlag == TRUE) { // A request is currently executing so don't // reset the serialize flag when this one // completes!! pTdiExecParams->ResetSerializeFlag = FALSE; } *SerializeFlag = TRUE; PrevRequestType = RequestType; // We're done with the lock for now, so free it. ExReleaseSpinLock( &TDIListLock, OldIrql ); // Figure out and execute the type of request we have here. Status = TdiExecuteRequest(NULL, pTdiExecParams); TDI_LOG(LOG_REGISTER, ("-TdiSerialized sync\n")); return Status; } else { // We're already running, so we'll have to queue. If this is a // deregister bind or address notify call, we'll see if the issueing // thread is the same one that is currently busy. If so, we'll fail // to avoid deadlock. Otherwise for deregister calls we'll block until // it's done. // // For Nt5, we have devicename and a context coming in along with net addresses/device objects. // It is the transport's responsibility to ensure that these are correct. // The Register_PNP_Handlers on the other hand need not be made synch. // if ( pTdiExecParams->Request.Type == TDI_DEREGISTER_HANDLERS_PNP || pTdiExecParams->Request.Type == TDI_NDIS_IOCTL_HANDLER_PNP ) { // This is a deregister request. See if it's the same thread // that's busy. If not, block for it to complete. if (*RequestThread == PsGetCurrentThread()) { // It's the same one, so give up now. ExReleaseSpinLock( &TDIListLock, OldIrql ); ExFreePool(pTdiExecParams); TDI_DEBUG(FUNCTION2, ("-- TdiHandleSerializedRequest: Network Busy\n")); TDI_LOG(LOG_ERROR, ("-TdiSerializedRequest rc=busy\n")); return STATUS_NETWORK_BUSY; } else { // He's not currently busy, go ahead and block. KEVENT Event; NTSTATUS Status; KeInitializeEvent( &Event, SynchronizationEvent, FALSE ); pTdiExecParams->Request.Event = &Event; // Put this guy on the end of the request list. InsertTailList(pTdiExecParams->RequestList, &pTdiExecParams->Linkage); ExReleaseSpinLock( &TDIListLock, OldIrql ); TDI_LOG(LOG_REGISTER, ("TdiSerializedRequest blocked\n")); Status = KeWaitForSingleObject( &Event, UserRequest, KernelMode, FALSE, NULL ); // I don't know what we'd do is the wait failed.... TDI_DEBUG(FUNCTION2, ("-- TdiHandleSerializedRequest\n")); TDI_LOG(LOG_REGISTER, ("-TdiSerializeRequest rc=0\n")); return STATUS_SUCCESS; } } else { // This isn't a deregister request, so there's no special handling // necessary. Just put the request on the end of the list. InsertTailList(pTdiExecParams->RequestList, &pTdiExecParams->Linkage); if (TDI_REGISTER_PNP_POWER_EVENT == pTdiExecParams->Request.Type) { // // For the PnP/PM event, there is now a completion handler, so // we can return pending here only for this case. // The other cases, we assume success. // pTdiExecParams->Request.Pending = TRUE; ExReleaseSpinLock( &TDIListLock, OldIrql ); TDI_DEBUG(FUNCTION2, ("-- TdiHandleSerializedRequest\n")); TDI_LOG(LOG_REGISTER, ("-TdiSerialzied Pending\n")); return STATUS_PENDING; } ExReleaseSpinLock( &TDIListLock, OldIrql ); TDI_LOG(LOG_REGISTER, ("-TdiSerialized sync~sync\n")); return STATUS_SUCCESS; } } } NTSTATUS TdiRegisterNotificationHandler( IN TDI_BIND_HANDLER BindHandler, IN TDI_UNBIND_HANDLER UnbindHandler, OUT HANDLE *BindingHandle ) /*++ Routine Description: This function is called when a TDI client wants to register for notification of the arrival of TDI providers. We allocate a TDI_NOTIFY_ELEMENT for the provider and then call the serialized worker routine to do the real work. Arguments: BindHandler - A pointer to the routine to be called when a new provider arrives. UnbindHandler - A pointer to the routine to be called when a provider leaves. BindingHandle - A handle we pass back that identifies this client to us. Return Value: The status of the attempt to register the client. --*/ { TDI_CLIENT_INTERFACE_INFO tdiInterface; // // Make sure Tdi is intialized. If there are no pnp transports, then this is // called by the tdi client and if tdi is not initialized, it is toast // Multiple calls to TdiIntialize are safe since only the first one does // the real work // TdiInitialize(); RtlZeroMemory(&tdiInterface, sizeof(tdiInterface)); tdiInterface.MajorTdiVersion = 1; tdiInterface.MinorTdiVersion = 0; tdiInterface.BindHandler = BindHandler; tdiInterface.UnBindHandler = UnbindHandler; return (TdiRegisterPnPHandlers( &tdiInterface, sizeof(tdiInterface), BindingHandle )); } NTSTATUS TdiDeregisterNotificationHandler( IN HANDLE BindingHandle ) /*++ Routine Description: This function is called when a TDI client wants to deregister a previously registered bind notification handler. All we really do is call TdiHandleSerializedRequest, which does the hard work. Arguments: BindingHandle - A handle we passed back to the client on the register call. This is really a pointer to the notify element. Return Value: The status of the attempt to deregister the client. --*/ { return (TdiDeregisterPnPHandlers( BindingHandle)); } NTSTATUS TdiRegisterDeviceObject( IN PUNICODE_STRING DeviceName, OUT HANDLE *RegistrationHandle ) /*++ Routine Description: Called when a TDI provider wants to register a device object. Arguments: DeviceName - Name of the device to be registered. RegistrationHandle - A handle we pass back to the provider, identifying this registration. Return Value: The status of the attempt to register the provider. --*/ { PTDI_PROVIDER_RESOURCE NewResource; NTSTATUS Status; PWCHAR Buffer; int i; TDI_DEBUG(FUNCTION, ("++ TdiRegisterDeviceObject\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); TdiInitialize(); // First, try and allocate the needed resource. NewResource = (PTDI_PROVIDER_RESOURCE)ExAllocatePoolWithTag( NonPagedPool, sizeof(TDI_PROVIDER_RESOURCE), 'cIDT' ); // If we couldn't get it, fail the request. if (NewResource == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(NewResource, sizeof(TDI_PROVIDER_RESOURCE)); // Try and get a buffer to hold the name. Buffer = (PWCHAR)ExAllocatePoolWithTag( NonPagedPool, DeviceName->MaximumLength, 'dIDT' ); if (Buffer == NULL) { ExFreePool(NewResource); return STATUS_INSUFFICIENT_RESOURCES; } // Fill in the basic stuff. NewResource->Common.Type = TDI_RESOURCE_DEVICE; NewResource->Specific.Device.DeviceName.MaximumLength = DeviceName->MaximumLength; NewResource->Specific.Device.DeviceName.Buffer = Buffer; RtlCopyUnicodeString( &NewResource->Specific.Device.DeviceName, DeviceName ); *RegistrationHandle = (HANDLE)NewResource; TDI_DEBUG(PROVIDERS, ("Registering Device Object\n")); Status = TdiHandleSerializedRequest( NewResource, TDI_REGISTER_DEVICE_PNP ); CTEAssert(STATUS_SUCCESS == Status); if (STATUS_SUCCESS != Status) { ExFreePool(Buffer); ExFreePool(NewResource); *RegistrationHandle = NULL; } TDI_DEBUG(FUNCTION, ("-- TdiRegisterDeviceObject\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); TDI_LOG(LOG_REGISTER, ("-RegisterDeviceObject rc=%X h=%X %wZ\n", Status, NewResource, DeviceName)); return Status; } NTSTATUS TdiDeregisterDeviceObject( IN HANDLE RegistrationHandle ) /*++ Routine Description: This function is called when a TDI provider want's to deregister a device object. Arguments: RegistrationHandle - A handle we passed back to the provider on the register call. This is really a pointer to the resource element. Return Value: The status of the attempt to deregister the provider. --*/ { NTSTATUS Status; TDI_DEBUG(FUNCTION, ("++ TdiDERegisterDeviceObject\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); CTEAssert(RegistrationHandle); Status = TdiHandleSerializedRequest( RegistrationHandle, TDI_DEREGISTER_DEVICE_PNP ); TDI_DEBUG(FUNCTION, ("-- TdiDERegisterDeviceObject\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); TDI_LOG(LOG_REGISTER, ("TdiDeregisterDeviceObject rc=%d\n", Status)); return Status; } NTSTATUS TdiRegisterAddressChangeHandler( IN TDI_ADD_ADDRESS_HANDLER AddHandler, IN TDI_DEL_ADDRESS_HANDLER DeleteHandler, OUT HANDLE *BindingHandle ) /*++ Routine Description: This function is called when a TDI client wants to register for notification of the arrival of network addresses. We allocate a TDI_NOTIFY_ELEMENT for the provider and then call the serialized worker routine to do the real work. Arguments: AddHandler - A pointer to the routine to be called when a new address arrives. DeleteHandler - A pointer to the routine to be called when an address leaves. BindingHandle - A handle we pass back that identifies this client to us. Return Value: The status of the attempt to register the client. --*/ { TDI_CLIENT_INTERFACE_INFO tdiInterface; // // Make sure Tdi is intialized. If there are no pnp transports, then this is // called by the tdi client and if tdi is not initialized, it is toast // Multiple calls to TdiIntialize are safe since only the first one does // the real work // TdiInitialize(); RtlZeroMemory(&tdiInterface, sizeof(tdiInterface)); tdiInterface.MajorTdiVersion = 1; tdiInterface.MinorTdiVersion = 0; tdiInterface.AddAddressHandler = AddHandler; tdiInterface.DelAddressHandler = DeleteHandler; return (TdiRegisterPnPHandlers( &tdiInterface, sizeof(tdiInterface), BindingHandle )); } NTSTATUS TdiDeregisterAddressChangeHandler( IN HANDLE BindingHandle ) /*++ Routine Description: This function is called when a TDI client wants to deregister a previously registered address change notification handler. All we really do is call TdiHandleSerializedRequest, which does the hard work. Arguments: BindingHandle - A handle we passed back to the client on the register call. This is really a pointer to the notify element. Return Value: The status of the attempt to deregister the client. --*/ { return (TdiDeregisterPnPHandlers( BindingHandle)); } NTSTATUS TdiRegisterNetAddress( IN PTA_ADDRESS Address, IN PUNICODE_STRING DeviceName, IN PTDI_PNP_CONTEXT Context2, OUT HANDLE *RegistrationHandle ) /*++ Routine Description: Called when a TDI provider wants to register a new net address. Arguments: Address - New net address to be registered. Context1 - Protocol defined context1. For example, TCPIP will pass the list of IP addresses associated with this device. Context2 - Protocol defined context2. For example, TCPIP may pass the PDO of the device on which this PnP event is being notified. RegistrationHandle - A handle we pass back to the provider, identifying this registration. Return Value: The status of the attempt to register the provider. --*/ { PTDI_PROVIDER_RESOURCE NewResource; NTSTATUS Status; TDI_DEBUG(FUNCTION, ("++ TdiRegisterNetAddress\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); // First, try and allocate the needed resource. NewResource = (PTDI_PROVIDER_RESOURCE)ExAllocatePoolWithTag( NonPagedPool, FIELD_OFFSET( TDI_PROVIDER_RESOURCE, Specific.NetAddress ) + FIELD_OFFSET(TA_ADDRESS, Address) + Address->AddressLength, 'eIDT' ); // If we couldn't get it, fail the request. if (NewResource == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory( NewResource, FIELD_OFFSET( TDI_PROVIDER_RESOURCE, Specific.NetAddress ) + FIELD_OFFSET( TA_ADDRESS, Address ) + Address->AddressLength ); // Fill in the basic stuff. NewResource->Common.Type = TDI_RESOURCE_NET_ADDRESS; NewResource->Specific.NetAddress.Address.AddressLength = Address->AddressLength; NewResource->Specific.NetAddress.Address.AddressType = Address->AddressType; RtlCopyMemory( NewResource->Specific.NetAddress.Address.Address, Address->Address, Address->AddressLength ); *RegistrationHandle = (HANDLE)NewResource; // Now call HandleBindRequest to handle this one. // we have to fill in the contexts here if (DeviceName) { NewResource->DeviceName.Buffer = ExAllocatePoolWithTag( NonPagedPool, DeviceName->MaximumLength, 'uIDT' ); if (NULL == NewResource->DeviceName.Buffer) { ExFreePool(NewResource); return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory( NewResource->DeviceName.Buffer, DeviceName->Buffer, DeviceName->MaximumLength ); NewResource->DeviceName.Length = DeviceName->Length; NewResource->DeviceName.MaximumLength = DeviceName->MaximumLength; } else { NewResource->DeviceName.Buffer = NULL; } if (Context2) { NewResource->Context2 = ExAllocatePoolWithTag( NonPagedPool, FIELD_OFFSET(TDI_PNP_CONTEXT, ContextData) + Context2->ContextSize, 'vIDT' ); if (NULL == NewResource->Context2) { if (NewResource->DeviceName.Buffer) { ExFreePool(NewResource->DeviceName.Buffer); } ExFreePool(NewResource); return STATUS_INSUFFICIENT_RESOURCES; } NewResource->Context2->ContextType = Context2->ContextType; NewResource->Context2->ContextSize = Context2->ContextSize; RtlCopyMemory( NewResource->Context2->ContextData, Context2->ContextData, Context2->ContextSize ); } else { NewResource->Context2 = NULL; } Status = TdiHandleSerializedRequest( NewResource, TDI_REGISTER_ADDRESS_PNP ); CTEAssert(STATUS_SUCCESS == Status); if (STATUS_SUCCESS != Status) { *RegistrationHandle = NULL; TDI_DEBUG(ERROR, ("Freeing Contexts due to failure!!\n")); if (NewResource->DeviceName.Buffer) { TDI_DEBUG(ERROR, ("Freeing context1: %x", NewResource->DeviceName)); ExFreePool(NewResource->DeviceName.Buffer); NewResource->DeviceName.Buffer = NULL; } if (NewResource->Context2) { TDI_DEBUG(ERROR, ("Freeing context2: %x", NewResource->Context2)); ExFreePool(NewResource->Context2); NewResource->Context2 = NULL; } TDI_DEBUG(ERROR, ("Freeing Provider: %x", NewResource)); ExFreePool(NewResource); } TDI_DEBUG(FUNCTION, ("-- TdiRegisterNetAddress\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); TDI_LOG(LOG_REGISTER, ("-RegisterNetAddress rc=%d h=%X %wZ\n", Status, *RegistrationHandle, DeviceName)); return Status; } NTSTATUS TdiDeregisterNetAddress( IN HANDLE RegistrationHandle ) /*++ Routine Description: This function is called when a TDI provider wants to deregister a net addres. Arguments: RegistrationHandle - A handle we passed back to the provider on the register call. This is really a pointer to the resource element. Return Value: The status of the attempt to deregister the provider. --*/ { NTSTATUS Status; TDI_DEBUG(FUNCTION, ("++ TdiDERegisterNetAddress\n")); CTEAssert(RegistrationHandle); if (NULL == RegistrationHandle) { TDI_DEBUG(ERROR, ("NULL Address Deregistration\n")); } CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); CTEAssert(((PTDI_PROVIDER_RESOURCE)RegistrationHandle)->Common.Linkage.Flink != (PLIST_ENTRY)UlongToPtr(0xabababab)); CTEAssert(((PTDI_PROVIDER_RESOURCE)RegistrationHandle)->Common.Linkage.Blink != (PLIST_ENTRY)UlongToPtr(0xefefefef)); Status = TdiHandleSerializedRequest( RegistrationHandle, TDI_DEREGISTER_ADDRESS_PNP ); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); TDI_DEBUG(FUNCTION, ("-- TdiDERegisterNetAddress\n")); return Status; } // The PnP/PM extension code NTSTATUS TdiRegisterPnPHandlers( IN PTDI_CLIENT_INTERFACE_INFO ClientInterfaceInfo, IN ULONG InterfaceInfoSize, OUT HANDLE *BindingHandle ) /*++ Routine Description: This function is called when a TDI client wants to register its set of PnP/PM handlers Arguments: ClientName BindingHandler AddAddressHandler DelAddressHandler PowerHandler BindingHandle Return Value: The status of the client's attempt to register the handlers. --*/ { PTDI_NOTIFY_PNP_ELEMENT NewElement; NTSTATUS Status; PWCHAR Buffer = NULL; TDI_DEBUG(FUNCTION, ("++ TdiRegisterPnPHandlers\n")); // // Check that this is a TDI 2.0 Client // CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); if (ClientInterfaceInfo->MajorTdiVersion > 2) { TDI_DEBUG(PROVIDERS, ("TDI Client: Bad Version!\n")); return TDI_STATUS_BAD_VERSION; } // // Check that ClientInfoLength is enough. // if (InterfaceInfoSize < sizeof(TDI_CLIENT_INTERFACE_INFO)) { TDI_DEBUG(PROVIDERS, ("TDI Client Info length was incorrect\n")); return TDI_STATUS_BAD_CHARACTERISTICS; } // This could be the first provider/client to call into TDI. TdiInitialize(); // First, try and allocate the needed resource. NewElement = (PTDI_NOTIFY_PNP_ELEMENT)ExAllocatePoolWithTag( NonPagedPool, sizeof(TDI_NOTIFY_PNP_ELEMENT), 'fIDT' ); // If we couldn't get it, fail the request. if (NewElement == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // Allocate space for the deregister exec request. NewElement->pTdiDeregisterExecParams = (PTDI_EXEC_PARAMS)ExAllocatePoolWithTag( NonPagedPool, sizeof(TDI_EXEC_PARAMS), 'aIDT' ); if (NULL == NewElement->pTdiDeregisterExecParams) { ExFreePool(NewElement); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(NewElement->pTdiDeregisterExecParams, sizeof (TDI_EXEC_PARAMS)); // Try and get a buffer to hold the name, if required. if (NULL != ClientInterfaceInfo->ClientName) { Buffer = (PWCHAR)ExAllocatePoolWithTag( NonPagedPool, ClientInterfaceInfo->ClientName->MaximumLength, 'gIDT' ); if (Buffer == NULL) { ExFreePool(NewElement->pTdiDeregisterExecParams); ExFreePool(NewElement); return STATUS_INSUFFICIENT_RESOURCES; } NewElement->ElementName.Length = ClientInterfaceInfo->ClientName->Length; NewElement->ElementName.MaximumLength = ClientInterfaceInfo->ClientName->MaximumLength; NewElement->ElementName.Buffer = Buffer; RtlCopyUnicodeString( &NewElement->ElementName, ClientInterfaceInfo->ClientName ); } else { NewElement->ElementName.Length = 0; NewElement->ElementName.MaximumLength = 0; NewElement->ElementName.Buffer = NULL; } // Fill in the basic stuff. NewElement->TdiVersion = ClientInterfaceInfo->TdiVersion; NewElement->Common.Type = TDI_NOTIFY_PNP_HANDLERS; if (TDI_VERSION_ONE == ClientInterfaceInfo->TdiVersion) { NewElement->Bind.BindHandler = ClientInterfaceInfo->BindHandler; NewElement->Bind.UnbindHandler = ClientInterfaceInfo->UnBindHandler; NewElement->AddressElement.AddHandler = ClientInterfaceInfo->AddAddressHandler; NewElement->AddressElement.DeleteHandler = ClientInterfaceInfo->DelAddressHandler; NewElement->PnpPowerHandler = NULL; } else { NewElement->BindingHandler = ClientInterfaceInfo->BindingHandler; NewElement->AddressElement.AddHandlerV2 = ClientInterfaceInfo->AddAddressHandlerV2; NewElement->AddressElement.DeleteHandlerV2 = ClientInterfaceInfo->DelAddressHandlerV2; NewElement->PnpPowerHandler = ClientInterfaceInfo->PnPPowerHandler; } NewElement->ListofBindingsToIgnore = NULL; // Now call HandleBindRequest to handle this one. *BindingHandle = (HANDLE)NewElement; TDI_DEBUG(PROVIDERS, ("TDI.SYS: Registering PnPHandlers ...")); Status = TdiHandleSerializedRequest( NewElement, TDI_REGISTER_HANDLERS_PNP ); CTEAssert(STATUS_SUCCESS == Status); if (Status != STATUS_SUCCESS) { if (Buffer) { ExFreePool(Buffer); } ExFreePool(NewElement->pTdiDeregisterExecParams); ExFreePool(NewElement); *BindingHandle = NULL; TDI_DEBUG(PROVIDERS, ("... NOT SUCCESS (%x)!\n", Status)); } else { TDI_DEBUG(PROVIDERS, ("... SUCCESS!\n")); } TDI_DEBUG(FUNCTION, ("-- TdiRegisterPnPHandlers\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); TDI_LOG(LOG_REGISTER, ("-RegisterPnpHandlers rc=%d h=%X %wZ\n", Status, *BindingHandle, ClientInterfaceInfo->ClientName)); return Status; } VOID TdiPnPPowerComplete( IN HANDLE BindingHandle, //IN PUNICODE_STRING DeviceName, IN PNET_PNP_EVENT PnpPowerEvent, IN NTSTATUS Status ) /*++ Routine Description: Arguments: Return Value: --*/ { PTDI_PROVIDER_RESOURCE Provider, Context, Temp; TDI_DEBUG(FUNCTION, ("++ TdiPnPPowerComplete\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); ASSERT (NULL != PnpPowerEvent); Context = *((PTDI_PROVIDER_RESOURCE *) PnpPowerEvent->TdiReserved); TDI_LOG(LOG_POWER, ("TdiPnpPowerComplete for %X\n", Context)); if (NULL != Context) { while(Context->PreviousContext) { Context = Context->PreviousContext; } Provider = Context; ASSERT(Provider->PowerHandlers != 0); // // Return Status only if Status was not SUCCESS. // if (Status != STATUS_SUCCESS) { Provider->Status = Status; } if (!InterlockedDecrement(&Provider->PowerHandlers)) { TDI_DEBUG(POWER, ("Calling ProtocolPnPCompletion handler\n")); if (Provider->PreviousContext) { while (Provider->PreviousContext) { Context = Provider; Provider = Provider->PreviousContext; } Context->PreviousContext = NULL; //pop the last guy Status = STATUS_SUCCESS; } else { // // This was the only pointer in the TdiReserved and we dont need it anymore // RtlZeroMemory(PnpPowerEvent->TdiReserved, sizeof(PnpPowerEvent->TdiReserved)); } if (Provider->PnPCompleteHandler != NULL) { TDI_LOG(LOG_POWER, ("%X, pnp power complete, Call completion at %X\n", Provider, Provider->PnPCompleteHandler)); (*(Provider->PnPCompleteHandler))( PnpPowerEvent, Status ); TDI_DEBUG(POWER, ("Done calling %wZ's ProtocolPnPCompletion handler\n", &Provider->Specific.Device.DeviceName)); TDI_DEBUG(POWER, ("The Previous Context at this point is %lx\n", Provider->PreviousContext)); //DbgBreakPoint(); } ExFreePool(Provider->Specific.Device.DeviceName.Buffer); if (Provider->Context1) { ExFreePool(Provider->Context1); Provider->Context1 = NULL; } if (Provider->Context2) { ExFreePool(Provider->Context2); Provider->Context2 = NULL; } ExFreePool(Provider); // free resources anyways } else { TDI_DEBUG(POWER, ("There are %d callbacks remaining for %wZ\n", Provider->PowerHandlers, &Provider->Specific.Device.DeviceName)); } } else { TDI_DEBUG(POWER, ("This was called separately, so we just return\n")); } TDI_DEBUG(FUNCTION, ("-- TdiPnPPowerComplete\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); return ; } NTSTATUS TdiDeregisterPnPHandlers( IN HANDLE BindingHandle ) /*++ Routine Description: Arguments: Return Value: The status of the attempt to deregister the provider. --*/ { NTSTATUS Status; TDI_DEBUG(FUNCTION, ("++ TdiDERegisterPnPHandlers\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); Status = TdiHandleSerializedRequest( BindingHandle, TDI_DEREGISTER_HANDLERS_PNP ); TDI_DEBUG(FUNCTION, ("-- TdiDERegisterPnPHandlers\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); return Status; } NTSTATUS TdiPnPPowerRequest( IN PUNICODE_STRING DeviceName, IN PNET_PNP_EVENT PnpPowerEvent, IN PTDI_PNP_CONTEXT Context1, IN PTDI_PNP_CONTEXT Context2, IN ProviderPnPPowerComplete ProtocolCompletionHandler ) /*++ Routine Description: Arguments: DeviceName PowerEvent: Choice of QUERYPOWER/SETPOWER Return Value: The status of the attempt to deregister the provider. --*/ { PTDI_PROVIDER_RESOURCE NewResource, Context; NTSTATUS Status; PWCHAR Buffer; TDI_DEBUG(FUNCTION, ("++ TdiPnPPowerRequest\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); CTEAssert(ProtocolCompletionHandler); // First, try and allocate the needed resource. NewResource = (PTDI_PROVIDER_RESOURCE)ExAllocatePoolWithTag( NonPagedPool, sizeof(TDI_PROVIDER_RESOURCE), 'hIDT' ); // If we couldn't get it, fail the request. if (NewResource == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // Try and get a buffer to hold the name. Buffer = (PWCHAR)ExAllocatePoolWithTag( NonPagedPool, DeviceName->MaximumLength, 'iIDT' ); if (Buffer == NULL) { ExFreePool(NewResource); return STATUS_INSUFFICIENT_RESOURCES; } // Fill in the basic stuff. NewResource->Common.Type = TDI_RESOURCE_POWER; NewResource->Specific.Device.DeviceName.MaximumLength = DeviceName->MaximumLength; NewResource->Specific.Device.DeviceName.Buffer = Buffer; NewResource->PnPCompleteHandler = ProtocolCompletionHandler; Context = *((PTDI_PROVIDER_RESOURCE *) PnpPowerEvent->TdiReserved); if (NULL == Context) { TDI_DEBUG(POWER, ("New NetPnP Event\n")); TDI_LOG(LOG_POWER, ("New pnp event %X, %wZ\n", NewResource, DeviceName)); *((PVOID *) PnpPowerEvent->TdiReserved) = (PVOID) NewResource; } else { // // This NetPnp structure has looped thru before // Loop thru and find out the last one. // while (Context->PreviousContext) { Context = Context->PreviousContext; } Context->PreviousContext = NewResource; TDI_LOG(LOG_POWER, ("pnp event linking %X to %X, %wZ\n", Context, NewResource, DeviceName)); } NewResource->PreviousContext = NULL; NewResource->PnpPowerEvent = PnpPowerEvent; NewResource->Status = STATUS_SUCCESS; // Note: These pointers must be good for the duration of this call. if (Context1) { NewResource->Context1 = ExAllocatePoolWithTag( NonPagedPool, FIELD_OFFSET(TDI_PNP_CONTEXT, ContextData) + Context1->ContextSize, 'xIDT' ); if (NULL == NewResource->Context1) { if (Context) { Context->PreviousContext = NULL; } ExFreePool(NewResource); ExFreePool(Buffer); return STATUS_INSUFFICIENT_RESOURCES; } NewResource->Context1->ContextSize = Context1->ContextSize; NewResource->Context1->ContextType = Context1->ContextType; RtlCopyMemory( NewResource->Context1->ContextData, Context1->ContextData, Context1->ContextSize ); } else { NewResource->Context1 = NULL; } if (Context2) { NewResource->Context2 = ExAllocatePoolWithTag( NonPagedPool, FIELD_OFFSET(TDI_PNP_CONTEXT, ContextData) + Context2->ContextSize, 'yIDT' ); if (NULL == NewResource->Context2) { ExFreePool(Buffer); if (NewResource->Context1) { ExFreePool(NewResource->Context1); } if (Context) { Context->PreviousContext = NULL; } ExFreePool(NewResource); return STATUS_INSUFFICIENT_RESOURCES; } NewResource->Context2->ContextSize = Context2->ContextSize; NewResource->Context2->ContextType = Context2->ContextType; RtlCopyMemory( NewResource->Context2->ContextData, Context2->ContextData, Context2->ContextSize ); } else { NewResource->Context2 = NULL; } RtlCopyUnicodeString( &NewResource->Specific.Device.DeviceName, DeviceName ); // Now call HandleBindRequest to handle this one. Status = TdiHandleSerializedRequest( NewResource, TDI_REGISTER_PNP_POWER_EVENT ); // // If TdiHandleSerialized returns PENDING, then the contexts and Resource // structures are freed up in the TdiPnPComplete call. // if (STATUS_PENDING != Status) { Status = NewResource->Status; // The status is stored in the newresource. ExFreePool(Buffer); if (NewResource->Context1) { ExFreePool(NewResource->Context1); NewResource->Context1 = NULL; } if (NewResource->Context2) { ExFreePool(NewResource->Context2); NewResource->Context2 = NULL; } if (Context) { Context->PreviousContext = NULL; } TDI_LOG(LOG_POWER, ("%X completed sync, Status %X\n", NewResource, Status)); ExFreePool(NewResource); // free resources anyways } TDI_DEBUG(FUNCTION, ("-- TdiPnPPowerRequest : %lx\n", Status)); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); return Status; } // This function is private between NDIS and TDI // In TdiInitialize we need to pass a pointer to this function. NTSTATUS TdiMakeNCPAChanges( IN TDI_NCPA_BINDING_INFO NcpaBindingInfo ) { return STATUS_NOT_IMPLEMENTED; } //+--------------------------------------------------------------------------- // Purpose: Count the number of bytes of a double NULL terminated // multi-sz, including all NULLs except for the final terminating // NULL. // // Arguments: // pmsz [in] The multi-sz to count bytes for. // // Returns: The count of bytes. // ULONG TdipCbOfMultiSzSafe ( IN PCWSTR pmsz) { ULONG cchTotal = 0; ULONG cch; // NULL strings have zero length by definition. if (!pmsz) { return 0; } while (*pmsz) { cch = wcslen (pmsz) + 1; cchTotal += cch; pmsz += cch; } // Return the count of bytes. return cchTotal * sizeof (WCHAR); } //+--------------------------------------------------------------------------- // Purpose: Search for a string in a multi-sz. // // Arguments: // psz [in] The string to search for. // pmsz [in] The multi-sz search in. // // Returns: TRUE if string was found in the multi-sz. // BOOLEAN TdipIsSzInMultiSzSafe ( IN PCWSTR pszSearchString, IN PCWSTR pmsz) { if (!pmsz || !pszSearchString) { return FALSE; } while (*pmsz) { if (0 == _wcsicmp (pmsz, pszSearchString)) { return TRUE; } pmsz += wcslen (pmsz) + 1; } return FALSE; } //+--------------------------------------------------------------------------- // Purpose: Remove strings in a multi-sz list from an array of strings. // // Arguments: // pmszToRemove [in] The strings we need to remove. // pszArray [inout] The array of strings to modify. // ItemsInArray [in] The number of items in the array. // pRemainingItems [out] The number of items remaining in the array // after we have removed all items that // match pmszToRemove. // // Returns: nothing // VOID TdipRemoveMultiSzFromSzArray ( IN PWSTR pmszToRemove, IN OUT PWSTR* pszArray, IN ULONG ItemsInArray, OUT ULONG* pRemainingItems) { PWSTR pszScan; ULONG i, j; ULONG ItemsRemoved; ASSERT(pRemainingItems); *pRemainingItems = ItemsInArray; if (!pszArray || !pszArray[0] || !pmszToRemove || !*pmszToRemove) { return; } // Go through the string array. // ItemsRemoved = 0; for (i = 0; pszArray[i]; i++) { // Check each string in the remove multi-sz against // the current array string. // pszScan = pmszToRemove; while (*pszScan) { if (0 == _wcsicmp (pszScan, pszArray[i])) { ItemsRemoved++; // The string needs to be removed. // Just move the indexes down one slot. // for (j = i; pszArray[j]; j++) { pszArray[j] = pszArray[j + 1]; } // If we removed the last item in the list, get out of the // loop. Note that the next entry is also NULL which // will cause us to get out of our paraent for loop as // well. // if (!pszArray[i]) { break; } // Reset the scan string since the current indexed // entry is now the next entry. This means we run the // scan again. pszScan = pmszToRemove; } else { pszScan += wcslen (pszScan) + 1; } } } // Update the count of items in the array. *pRemainingItems = ItemsInArray - ItemsRemoved; } //+--------------------------------------------------------------------------- // Purpose: Remove a multi-sz of strings from another multi-sz of strings. // // Arguments: // pmszToRemove [in] The strings to remove. // pmszToModify [in] The list to modify. // // Returns: nothing. // VOID TdipRemoveMultiSzFromMultiSz ( IN PCWSTR pmszToRemove, IN OUT PWSTR pmszToModify) { BOOLEAN fRemoved; PCWSTR pszScan; if (!pmszToModify || !pmszToRemove || !*pmszToRemove) { return; } // Look for each pmszToRemove string in pmsz. When it is found, move // the remaining part of the pmsz over it. // while (*pmszToModify) { fRemoved = FALSE; pszScan = pmszToRemove; while (*pszScan) { ULONG cchScan = wcslen (pszScan); if (0 == _wcsicmp (pmszToModify, pszScan)) { PWSTR pmszRemain = pmszToModify + cchScan + 1; // Count the remaining bytes including the final terminator; INT cbRemain = TdipCbOfMultiSzSafe (pmszRemain) + sizeof (WCHAR); RtlMoveMemory (pmszToModify, pmszRemain, cbRemain); fRemoved = TRUE; break; } pszScan += cchScan + 1; } // If we didn't remove the current modify string, advance our // pointer. // if (!fRemoved) { pmszToModify += wcslen (pmszToModify) + 1; } } } //+--------------------------------------------------------------------------- // Purpose: Adds a multi-sz of strings to another multi-sz. // // Arguments: // pUniStringToAdd - [in] The Unicode string that contains the multisz. // pmszModify [in] The multi-sz to add to. // // Returns: NT status code. Either STATUS_SUCCESS or // STATUS_INSUFFICIENT_RESOURCES // NTSTATUS TdipAddMultiSzToMultiSz ( IN PUNICODE_STRING pUniStringToAdd, IN PCWSTR pmszModify, OUT PWSTR* ppmszOut) { NTSTATUS status = STATUS_SUCCESS; PCWSTR pszScan; ULONG cbNeeded; PCWSTR pmszAdd = NULL; ASSERT(ppmszOut); // Initialize the output parameters. // *ppmszOut = NULL; pmszAdd = pUniStringToAdd->Buffer; ASSERT(pmszAdd); // Validate the input - all multisz have 2 End -Of -String // characters at the end of the unicode string // { ULONG LenWchar = pUniStringToAdd->Length/2; // Length is in bytes if(LenWchar <= 2) // is Multisz long enough for our checks { return (STATUS_INVALID_PARAMETER); } if (pmszAdd[LenWchar -1] != 0) // is Multisz null terminated { return (STATUS_INVALID_PARAMETER); } if (pmszAdd[LenWchar-2] != 0) // is the last string in multisz null terminated { return (STATUS_INVALID_PARAMETER); } } // Go through the multi-sz to add and compute how much space we need. // for (pszScan = pmszAdd, cbNeeded = 0; *pszScan; pszScan += wcslen (pszScan) + 1) { // Check if the string is already present in the pmszModify. // If it is not, add its size to our total. if (!TdipIsSzInMultiSzSafe (pszScan, pmszModify)) { cbNeeded += (wcslen (pszScan) + 1) * sizeof (WCHAR); } } // If we have something to add... // if (cbNeeded) { ULONG cbDataSize; ULONG cbAllocSize; PWSTR pmszNew; // Get size of current multi-sz. cbDataSize = TdipCbOfMultiSzSafe (pmszModify); // Enough space for the old data plus the new string and NULL, and for the // second trailing NULL (multi-szs are double-terminated) cbAllocSize = cbDataSize + cbNeeded + sizeof (WCHAR); pmszNew = (PWSTR)ExAllocatePoolWithTag ( NonPagedPool, cbAllocSize, 'jIDT'); if (pmszNew) { ULONG cchOffset; cchOffset = cbDataSize / sizeof (WCHAR); RtlZeroMemory (pmszNew, cbAllocSize); // Copy the current buffer into the new buffer. RtlCopyMemory (pmszNew, pmszModify, cbDataSize); pszScan = pmszAdd; while (*pszScan) { // Check if the string is already present in the new buffer. if (!TdipIsSzInMultiSzSafe (pszScan, pmszNew)) { wcscpy (pmszNew + cchOffset, pszScan); cchOffset += wcslen (pmszNew + cchOffset) + 1; } pszScan += wcslen (pszScan) + 1; } *ppmszOut = pmszNew; } else { status = STATUS_INSUFFICIENT_RESOURCES; TDI_DEBUG(ERROR, ("TdipAddMultiSzToMultiSz: Insufficient resources\n")); } } return status; } //+--------------------------------------------------------------------------- // Purpose: Prints the contents of a multi-sz list. // // Arguments: // pmsz [in] The multi-sz to print. // // Returns: nothing. // #if DBG VOID TdipPrintMultiSz ( IN PCWSTR pmsz) { if (pmsz && *pmsz) { while (*pmsz) { TDI_DEBUG(BIND, ("%S\n", pmsz)); pmsz += wcslen (pmsz) + 1; } } } #endif BOOLEAN TdipMultiSzStrStr( PWSTR *TdiClientBindingList, PUNICODE_STRING DeviceName ) { int i; TDI_DEBUG(FUNCTION2, ("++ TdipMultiSzStrStr\n")); // look for the string in the multiszstring if( TdiClientBindingList == NULL ) { return FALSE; } // // Check to see if this device is one of the devices // we're interested in. // for( i=0; TdiClientBindingList[i]; i++ ) { if( DeviceName->Length / sizeof( WCHAR ) != wcslen( TdiClientBindingList[i] ) ) { continue; } if( _wcsnicmp( DeviceName->Buffer, TdiClientBindingList[i], DeviceName->Length / sizeof( WCHAR ) ) == 0 ) { break; } } // // If we hit the end of the list, then DeviceName is not a device we're // interested in. // if( TdiClientBindingList[i] == NULL ) { TDI_DEBUG(FUNCTION2, ("-- TdipMultiSzStrStr: NULL\n")); return FALSE; } TDI_DEBUG(FUNCTION2, ("-- TdipMultiSzStrStr\n")); return TRUE; } VOID TdipGetMultiSZList( OUT PWSTR **ListPointer, IN PWSTR BaseKeyName, IN PUNICODE_STRING DeviceName, IN PWSTR Linkage, IN PWSTR ParameterKeyName, OUT PUINT NumEntries ) /*++ Routine Description: This routine queries a registry value key for its MULTI_SZ values. Arguments: ListPointer - Pointer to receive the pointer. ParameterKeyValue - Name of the value parameter to query. Return Value: none. --*/ { UNICODE_STRING unicodeKeyName; UNICODE_STRING unicodeParamPath; OBJECT_ATTRIBUTES objAttributes; HANDLE keyHandle; WCHAR ParamBuffer[MAX_UNICODE_BUFLEN]; ULONG lengthNeeded; ULONG i; ULONG numberOfEntries; ULONG numberOfDefaultEntries = 0; NTSTATUS status; INT copyflag = 0; PWCHAR regEntry; PWCHAR dataEntry; PWSTR *ptrEntry; PCHAR newBuffer; PKEY_VALUE_FULL_INFORMATION infoBuffer = NULL; TDI_DEBUG(FUNCTION2, ("++ TdipGetMultiSzList\n")); unicodeParamPath.Length = 0; unicodeParamPath.MaximumLength = MAX_UNICODE_BUFLEN; unicodeParamPath.Buffer = ParamBuffer; // BaseKeyName :\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"; RtlAppendUnicodeToString(&unicodeParamPath, BaseKeyName); // Add DeviceName to it. RtlAppendUnicodeStringToString(&unicodeParamPath, DeviceName); // Add Linkage to it. RtlAppendUnicodeToString(&unicodeParamPath, Linkage); RtlInitUnicodeString( &unicodeKeyName, ParameterKeyName ); InitializeObjectAttributes( &objAttributes, &unicodeParamPath, OBJ_CASE_INSENSITIVE, NULL, NULL ); status = ZwOpenKey( &keyHandle, KEY_QUERY_VALUE, &objAttributes ); if ( !NT_SUCCESS(status) ) { TDI_DEBUG(REGISTRY, ("tdi.sys Cannot open key: %x!!\n", status)); goto use_default; } status = ZwQueryValueKey( keyHandle, &unicodeKeyName, KeyValueFullInformation, NULL, 0, &lengthNeeded ); if ( status != STATUS_BUFFER_TOO_SMALL ) { NtClose( keyHandle ); TDI_DEBUG(REGISTRY, ("tdi.sys Cannot query buffer!!\n")); goto use_default; } infoBuffer = ExAllocatePoolWithTag( NonPagedPool, lengthNeeded, 'jIDT' ); if ( infoBuffer == NULL ) { NtClose( keyHandle ); TDI_DEBUG(REGISTRY, ("tdi.sys Cannot alloc buffer!!\n")); goto use_default; } status = ZwQueryValueKey( keyHandle, &unicodeKeyName, KeyValueFullInformation, infoBuffer, lengthNeeded, &lengthNeeded ); NtClose( keyHandle ); if ( !NT_SUCCESS(status) ) { TDI_DEBUG(REGISTRY, ("tdi.sys Cannot query buffer (2) !!\n")); goto freepool_and_use_default; } // // Figure out how many entries there are. // // numberOfEntries should be total number of entries + 1. The extra // one is for the NULL sentinel entry. // lengthNeeded = infoBuffer->DataLength; if ( lengthNeeded <= sizeof(WCHAR) ) { // // No entries on the list. Use default. // goto freepool_and_use_default; } dataEntry = (PWCHAR)((PCHAR)infoBuffer + infoBuffer->DataOffset); for ( i = 0, regEntry = dataEntry, numberOfEntries = 0; i < lengthNeeded; i += sizeof(WCHAR) ) { if ( *regEntry++ == L'\0' ) { numberOfEntries++; } } // // Allocate space needed for the array of pointers. This is in addition // to the ones in the default list. // newBuffer = ExAllocatePoolWithTag( NonPagedPool, lengthNeeded + (numberOfEntries) * sizeof( PWSTR ), 'kIDT' ); if ( newBuffer == NULL ) { goto freepool_and_use_default; } // // Copy the names // regEntry = (PWCHAR)(newBuffer + (numberOfEntries) * sizeof(PWSTR)); RtlCopyMemory( regEntry, dataEntry, lengthNeeded ); // // Free the info buffer // ExFreePool(infoBuffer); ptrEntry = (PWSTR *) newBuffer; // // Build the array of pointers. If numberOfEntries is 1, then // it means that the list is empty. // if ( numberOfEntries > 1 ) { *ptrEntry++ = regEntry++; // // Skip the first WCHAR and the last 2 NULL terminators. // for ( i = 3*sizeof(WCHAR) ; i < lengthNeeded ; i += sizeof(WCHAR) ) { if ( *regEntry++ == L'\0' ) { *ptrEntry++ = regEntry; } } } *ptrEntry = NULL; *ListPointer = (PWSTR *)newBuffer; TDI_DEBUG(FUNCTION2, ("-- TdipGetMultiSzList\n")); *NumEntries = numberOfEntries; return; freepool_and_use_default: ExFreePool(infoBuffer); // doesnt get freed otherwise use_default: *ListPointer = NULL; *NumEntries = 0; TDI_DEBUG(REGISTRY, ("GetRegStrings: There was an error : returning NULL\r\n")); TDI_DEBUG(FUNCTION2, ("-- TdipGetMultiSzList: error\n")); return; } // TdipGetMultiSZList NTSTATUS TdiPnPHandler( IN PUNICODE_STRING UpperComponent, IN PUNICODE_STRING LowerComponent, IN PUNICODE_STRING BindList, IN PVOID ReconfigBuffer, IN UINT ReconfigBufferSize, IN UINT Operation ) { PTDI_NCPA_BINDING_INFO NdisElement; NTSTATUS Status = STATUS_SUCCESS; TDI_DEBUG(FUNCTION, ("++ TdiPnPHandler\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); NdisElement = ExAllocatePoolWithTag( NonPagedPool, sizeof(TDI_NCPA_BINDING_INFO), 'kIDT' ); if (NdisElement == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } NdisElement->TdiClientName = UpperComponent; NdisElement->TdiProviderName = LowerComponent; NdisElement->BindList = BindList; NdisElement->ReconfigBuffer = ReconfigBuffer; NdisElement->ReconfigBufferSize = ReconfigBufferSize; NdisElement->PnpOpcode = Operation; Status = TdiHandleSerializedRequest( NdisElement, TDI_NDIS_IOCTL_HANDLER_PNP ); ExFreePool(NdisElement); TDI_DEBUG(FUNCTION, ("-- TdiPnPHandler\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); return Status; } /*++ Routine Description: Call the AddAddress handler of the client along with all the registered TDI addresses. Arguments: Input: Handle to the client context Output: NTSTATUS = Success/Failure Return Value: none. --*/ NTSTATUS TdiEnumerateAddresses( IN HANDLE BindingHandle ) { NTSTATUS Status = STATUS_SUCCESS; TDI_DEBUG(FUNCTION, ("++ TdiEnumerateAddresses\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); // Now call HandleBindRequest to handle this one. Status = TdiHandleSerializedRequest( BindingHandle, TDI_ENUMERATE_ADDRESSES ); TDI_DEBUG(FUNCTION, ("-- TdiEnumerateAddresses\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); TDI_LOG(LOG_REGISTER, ("-TdiEnumerateAddresses %d\n", Status)); return Status; } /*++ Routine Description: Register a generic provider with TDI. Each transport is a provider and teh devices that it registers are what constitute a transport. When a transport thinks it has all the devices ready, it calls TdiNetReady API. Arguments: Input: Device Name Output: Handle to be used in future references. Return Value: none. */ NTSTATUS TdiRegisterProvider( PUNICODE_STRING ProviderName, HANDLE *ProviderHandle ) { PTDI_PROVIDER_RESOURCE NewResource; NTSTATUS Status; PWCHAR Buffer; TDI_DEBUG(FUNCTION, ("++ TdiRegisterProvider\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); TdiInitialize(); // make sure that the transports arent screwing us. CTEAssert(ProviderName); CTEAssert(ProviderName->Buffer); CTEAssert(ProviderHandle); TDI_DEBUG(PROVIDERS, (" %wZ provider is being Registered\n", ProviderName)); // First, try and allocate the needed resource. NewResource = (PTDI_PROVIDER_RESOURCE)ExAllocatePoolWithTag( NonPagedPool, sizeof(TDI_PROVIDER_RESOURCE), 'cIDT' ); // If we couldn't get it, fail the request. if (NewResource == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // Try and get a buffer to hold the name. Buffer = (PWCHAR)ExAllocatePoolWithTag( NonPagedPool, ProviderName->MaximumLength, 'dIDT' ); if (Buffer == NULL) { ExFreePool(NewResource); return STATUS_INSUFFICIENT_RESOURCES; } // Fill in the basic stuff. RtlZeroMemory( NewResource, sizeof(TDI_PROVIDER_RESOURCE) ); NewResource->Common.Type = TDI_RESOURCE_PROVIDER; NewResource->Specific.Device.DeviceName.MaximumLength = ProviderName->MaximumLength; NewResource->Specific.Device.DeviceName.Buffer = Buffer; RtlCopyUnicodeString( &NewResource->Specific.Device.DeviceName, ProviderName ); *ProviderHandle = (HANDLE)NewResource; TDI_DEBUG(PROVIDERS, ("Registering Device Object\n")); NewResource->Context1 = NULL; NewResource->Context2 = NULL; Status = TdiHandleSerializedRequest( NewResource, TDI_REGISTER_PROVIDER_PNP ); CTEAssert(STATUS_SUCCESS == Status); if (STATUS_SUCCESS != Status) { ExFreePool(Buffer); ExFreePool(NewResource); *ProviderHandle = NULL; } TDI_DEBUG(FUNCTION, ("-- TdiRegisterProvider\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); TDI_LOG(LOG_REGISTER, ("-RegisterProvider rc=%d h=%X %wZ\n", Status, *ProviderHandle, ProviderName)); return Status; } /*++ Routine Description: Indicate that a registered provider is ready. This means that it thinks that all its devices are ready to be used. Arguments: Input: Handle to the client context Output: NTSTATUS = Success/Failure Return Value: none. */ NTSTATUS TdiProviderReady( HANDLE ProviderHandle ) { PTDI_PROVIDER_RESOURCE ProvResource = ProviderHandle; NTSTATUS Status; TDI_DEBUG(FUNCTION, ("++ TdiProviderReady\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); CTEAssert(ProviderHandle); TDI_DEBUG(PROVIDERS, (" %wZ provider is READY\n", &ProvResource->Specific.Device.DeviceName)); CTEAssert(!ProvResource->ProviderReady); // doing it twice? Status = TdiHandleSerializedRequest( ProvResource, TDI_PROVIDER_READY_PNP ); TDI_DEBUG(FUNCTION, ("-- TdiProviderReady\n")); CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); TDI_LOG(LOG_REGISTER, ("-TdiProviderReady rc=%d %wZ\n", Status, &ProvResource->Specific.Device.DeviceName)); return Status; } /*++ Routine Description: Deregister a generic provider with TDI. Arguments: Inpute: Handle to the provider structure. Return Value: none. */ NTSTATUS TdiDeregisterProvider( HANDLE ProviderHandle ) { PTDI_PROVIDER_RESOURCE ProvResource = ProviderHandle; NTSTATUS Status; TDI_DEBUG(FUNCTION, ("++ TdiDeregisterProvider\n")); CTEAssert(ProviderHandle); TDI_DEBUG(PROVIDERS, (" %wZ provider is being Deregistered\n", &ProvResource->Specific.Device.DeviceName)); Status = TdiHandleSerializedRequest( ProvResource, TDI_DEREGISTER_PROVIDER_PNP ); TDI_DEBUG(FUNCTION, ("-- TdiDeregisterProvider\n")); return Status; } // // Input: New Client // Pointer to the OpenList // Output: success/failure (boolean) // // This function takes in the new client and builds all the OPEN structures that // need to be built (all the providers that this client is bound to). If the // provider doesnt exist at this time, we just point it to NULL and change it // when the provider (deviceobject) registers itself. // // BOOLEAN TdipBuildProviderList( PTDI_NOTIFY_PNP_ELEMENT NotifyElement ) { ULONG i; TDI_DEBUG(FUNCTION2, ("++ TdipBuildOpenList\n")); TdipGetMultiSZList( &NotifyElement->ListofProviders, StrRegTdiBindingsBasicPath, &NotifyElement->ElementName, StrRegTdiLinkage, StrRegTdiBindList, &NotifyElement->NumberofEntries ); // look for the string in the multiszstring if (NotifyElement->ListofProviders == NULL) { return FALSE; } TDI_DEBUG(BIND, ("Added %d Entries\n", NotifyElement->NumberofEntries)); TDI_DEBUG(FUNCTION2, ("-- TdipBuildOpenList\n")); return TRUE; } // // Takes provider (devicename) and returns a pointer to the // internal provider structure if it exists. // PTDI_PROVIDER_RESOURCE LocateProviderContext( PUNICODE_STRING ProviderName ) { PLIST_ENTRY Current; PTDI_PROVIDER_RESOURCE ProviderElement = NULL; TDI_DEBUG(FUNCTION2, ("++ LocateProviderContext\n")); Current = PnpHandlerProviderList.Flink; while (Current != &PnpHandlerProviderList) { ProviderElement = CONTAINING_RECORD( Current, TDI_PROVIDER_RESOURCE, Common.Linkage ); if (ProviderElement->Common.Type != TDI_RESOURCE_DEVICE) { Current = Current->Flink; continue; } if (!RtlCompareUnicodeString( ProviderName, &ProviderElement->Specific.Device.DeviceName, TRUE)) { TDI_DEBUG(BIND, ("Provider is registered with TDI\n")); break; } Current = Current->Flink; } TDI_DEBUG(FUNCTION2, ("-- LocateProviderContext\n")); return ProviderElement; } #if DBG // // Cool new memory logging functions added to keep track of the store // and forward functionality in TDI (while debugging). // VOID DbgMsgInit() { First = 0; Last = 0; CTEInitLock(&DbgLock); } VOID DbgMsg(CHAR *Format, ...) { va_list Args; CTELockHandle LockHandle; CHAR Temp[MAX_MSG_LEN]; LONG numCharWritten; va_start(Args, Format); numCharWritten = _vsnprintf(Temp, MAX_MSG_LEN, Format, Args); if (numCharWritten < 0) { return; } // Zero Terminate the string // Temp[numCharWritten] = '\0'; if (TdiLogOutput & LOG_OUTPUT_DEBUGGER) { DbgPrint(Temp); } if (TdiLogOutput & LOG_OUTPUT_BUFFER) { CTEGetLock(&DbgLock, &LockHandle); RtlZeroMemory(DbgMsgs[Last], MAX_MSG_LEN); strcpy(DbgMsgs[Last], Temp); Last++; if (Last == LOG_MSG_CNT) Last = 0; if (First == Last) { First++; if (First == LOG_MSG_CNT) First = 0; } CTEFreeLock(&DbgLock, LockHandle); } va_end(Args); } #endif