/*++ BUILD Version: 0000 // Increment this if a change has global effects Copyright (c) 1994 Microsoft Corporation Module Name: ndistapi.c Abstract: This module contains the NdisTapi.sys implementation Author: Dan Knudson (DanKn) 20-Feb-1994 Notes: (Future/outstanding issues) - stuff marked with "PnP" needs to be rev'd for plug 'n play support Revision History: --*/ #include "ndis.h" #include "stdarg.h" #include "stdio.h" #include "ntddndis.h" #include "ndistapi.h" #include "private.h" #include "intrface.h" NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); VOID NdisTapiCancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS NdisTapiCleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS NdisTapiDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID NdisTapiUnload( IN PDRIVER_OBJECT DriverObject ); #if DBG VOID DbgPrt( IN LONG DbgLevel, IN PUCHAR DbgMessage, IN ... ); #endif VOID DoProviderInitComplete( PPROVIDER_REQUEST ProviderRequest, NDIS_STATUS Status ); ULONG GetLineEvents( PVOID EventBuffer, ULONG BufferSize ); BOOLEAN SyncInitAllProviders( void ); VOID DoIrpMjCloseWork( PIRP Irp ); NDIS_STATUS SendProviderInitRequest( PPROVIDER_INFO Provider ); NDIS_STATUS SendProviderShutdown( PPROVIDER_INFO Provider, PKIRQL oldIrql ); VOID NdisTapiIndicateStatus( IN ULONG_PTR DriverHandle, IN PVOID StatusBuffer, IN UINT StatusBufferSize ); VOID DoLineOpenCompleteWork( PNDISTAPI_REQUEST ndisTapiRequest, PPROVIDER_INFO provider ); VOID DoLineOpenWork( PNDISTAPI_REQUEST ndisTapiRequest, PPROVIDER_INFO provider ); NDIS_STATUS VerifyProvider( PNDISTAPI_REQUEST ndisTapiRequest, PPROVIDER_INFO *provider ); NDIS_STATUS VerifyLineClose( PNDISTAPI_REQUEST ndisTapiRequest, PPROVIDER_INFO provider ); NTSTATUS DoIoctlConnectWork( PIRP Irp, PVOID ioBuffer, ULONG inputBufferLength, ULONG outputBufferLength ); NTSTATUS DoIoctlQuerySetWork( PIRP Irp, PVOID ioBuffer, ULONG inputBufferLength, ULONG outputBufferLength ); NTSTATUS DoGetProviderEventsWork( PIRP Irp, PVOID ioBuffer, ULONG inputBufferLength, ULONG outputBufferLength ); NTSTATUS DoLineCreateWork( PIRP Irp, PVOID ioBuffer, ULONG inputBufferLength, ULONG outputBufferLength ); // // Use the alloc_text pragma to specify the driver initialization routines // (they can be paged out). // #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,DriverEntry) #endif NPAGED_LOOKASIDE_LIST ProviderEventLookaside; NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Installable driver initialization entry point. This entry point is called directly by the I/O system. Arguments: DriverObject - pointer to the driver object RegistryPath - pointer to a unicode string representing the path to driver-specific key in the registry Return Value: STATUS_SUCCESS if successful, STATUS_UNSUCCESSFUL otherwise --*/ { PDEVICE_OBJECT deviceObject = NULL; NTSTATUS ntStatus; WCHAR deviceNameBuffer[] = L"\\Device\\NdisTapi"; UNICODE_STRING deviceNameUnicodeString; UNICODE_STRING registryPath; DBGOUT ((2, "DriverEntry: enter")); // // Create a NON-EXCLUSIVE device, i.e. multiple threads at a time // can send i/o requests. // RtlInitUnicodeString (&deviceNameUnicodeString, deviceNameBuffer); ntStatus = IoCreateDevice( DriverObject, sizeof (KMDD_DEVICE_EXTENSION), &deviceNameUnicodeString, FILE_DEVICE_NDISTAPI, 0, FALSE, &deviceObject ); if (NT_SUCCESS(ntStatus)) { // // Init the global & sero the extension // DeviceExtension = (PKMDD_DEVICE_EXTENSION) deviceObject->DeviceExtension; RtlZeroMemory( DeviceExtension, sizeof (KMDD_DEVICE_EXTENSION) ); // // Create a NULL-terminated registry path & retrieve the registry // params (EventDataQueueLength) // registryPath.Buffer = ExAllocatePoolWithTag( PagedPool, RegistryPath->Length + sizeof(UNICODE_NULL), 'IPAT' ); if (!registryPath.Buffer) { DBGOUT((1, "DriverEntry: ExAllocPool for szRegistryPath failed")); ntStatus = STATUS_UNSUCCESSFUL; goto DriverEntry_err; } else { registryPath.Length = RegistryPath->Length; registryPath.MaximumLength = registryPath.Length + sizeof(UNICODE_NULL); RtlZeroMemory( registryPath.Buffer, registryPath.MaximumLength ); RtlMoveMemory( registryPath.Buffer, RegistryPath->Buffer, RegistryPath->Length ); } ExFreePool (registryPath.Buffer); InitializeListHead(&DeviceExtension->ProviderEventList); ExInitializeNPagedLookasideList(&ProviderEventLookaside, NULL, NULL, 0, sizeof(PROVIDER_EVENT), 'IPAT', 0); DeviceExtension->DeviceObject = deviceObject; DeviceExtension->Status = NDISTAPI_STATUS_DISCONNECTED; DeviceExtension->NdisTapiNumDevices = 0; DeviceExtension->htCall = 0x80000001; KeInitializeSpinLock (&DeviceExtension->SpinLock); InitializeListHead(&DeviceExtension->ProviderRequestList); // // Create dispatch points for device control, create, close. // DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = NdisTapiDispatch; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = NdisTapiCleanup; DriverObject->DriverUnload = NdisTapiUnload; } if (!NT_SUCCESS(ntStatus)) { DriverEntry_err: // // Something went wrong, so clean up // DBGOUT((0, "init failed")); if (deviceObject) { while (!(IsListEmpty(&DeviceExtension->ProviderEventList))) { PPROVIDER_EVENT ProviderEvent; ProviderEvent = (PPROVIDER_EVENT) RemoveHeadList(&DeviceExtension->ProviderEventList); ExFreeToNPagedLookasideList(&ProviderEventLookaside, ProviderEvent); } IoDeleteDevice (deviceObject); } } DBGOUT ((2, "DriverEntry: exit")); return ntStatus; } VOID NdisTapiCancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { KIRQL oldIrql; DBGOUT((2,"NdisTapiCancel: enter")); // // Release the cancel spinlock // IoReleaseCancelSpinLock (Irp->CancelIrql); // // Acquire the SpinLock & check to see if we're canceling a // pending get-events Irp // KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); do { DeviceExtension->IrpsCanceledCount++; if (Irp == DeviceExtension->EventsRequestIrp) { DeviceExtension->EventsRequestIrp = NULL; DeviceExtension->Flags |= EVENTIRP_CANCELED; break; } // // Try to remove request from our special // user-mode requests dev queue // if (!IsListEmpty(&DeviceExtension->ProviderRequestList)) { PLIST_ENTRY Entry; Entry = DeviceExtension->ProviderRequestList.Flink; while (Entry != &DeviceExtension->ProviderRequestList) { PPROVIDER_REQUEST pReq; pReq = (PPROVIDER_REQUEST)Entry; if (pReq->Irp == Irp) { RemoveEntryList(&pReq->Linkage); DeviceExtension->RequestCount--; DeviceExtension->Flags |= REQUESTIRP_CANCELED; break; } Entry = Entry->Flink; } if (Entry == &DeviceExtension->ProviderRequestList) { DBGOUT((1,"NdisTapiCancel: Irp %p not in device queue?!?", Irp)); DeviceExtension->Flags |= CANCELIRP_NOTFOUND; } } } while (FALSE); KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); // // Complete the request with STATUS_CANCELLED. // Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT); DBGOUT((2,"NdisTapiCancel: completing irp=%p", Irp)); } NTSTATUS NdisTapiCleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch routine for cleanup requests. All requests queued are completed with STATUS_CANCELLED. Arguments: DeviceObject - Pointer to device object. Irp - Pointer to the request packet. Return Value: Status is returned. --*/ { KIRQL oldIrql; PNDISTAPI_REQUEST ndisTapiRequest; PKDEVICE_QUEUE_ENTRY packet; DBGOUT((2,"NdisTapiCleanup: enter")); // // Sync access to EventsRequestIrp by acquiring SpinLock // KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); DeviceExtension->Flags |= CLEANUP_INITIATED; // // Check to see if there's a get-events request pending that needs // completing // if ((DeviceExtension->EventsRequestIrp != NULL) && (DeviceExtension->EventsRequestIrp->Tail.Overlay.OriginalFileObject == Irp->Tail.Overlay.OriginalFileObject)) { PIRP LocalIrp; // // Acquire the cancel spinlock, remove the request from the // cancellable state, and free the cancel spinlock. // LocalIrp = DeviceExtension->EventsRequestIrp; if (IoSetCancelRoutine (LocalIrp, NULL) != NULL) { DeviceExtension->EventsRequestIrp = NULL; LocalIrp->IoStatus.Status = STATUS_CANCELLED; LocalIrp->IoStatus.Information = 0; DeviceExtension->IrpsCanceledCount++; KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); DBGOUT((2,"NdisTapiCleanup: Completing EventRequestIrp %p", LocalIrp)); IoCompleteRequest (LocalIrp, IO_NO_INCREMENT); KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); } } // // Cancel all outstanding QUERY/SET_INFO requests // if (!IsListEmpty(&DeviceExtension->ProviderRequestList)) { PPROVIDER_REQUEST pReq; pReq = (PPROVIDER_REQUEST) DeviceExtension->ProviderRequestList.Flink; // // Until we have walked the entire list // while ((PVOID)pReq != (PVOID)&DeviceExtension->ProviderRequestList) { PIRP LocalIrp; LocalIrp = pReq->Irp; // // If the current entry's irp has a fileobject that is // the same as the cleanup irp's fileobject then remove it // from the list and cancel it // if (LocalIrp->Tail.Overlay.OriginalFileObject == Irp->Tail.Overlay.OriginalFileObject) { // // Remove the IRP from the cancelable state // if (IoSetCancelRoutine (LocalIrp, NULL) == NULL) { // // The irp has been canceled. Let // cancel routine cleanup. // pReq = (PPROVIDER_REQUEST)pReq->Linkage.Flink; continue; } RemoveEntryList(&pReq->Linkage); DeviceExtension->RequestCount--; // // Set the status & info size values appropriately, & complete // the request // ndisTapiRequest = LocalIrp->AssociatedIrp.SystemBuffer; ndisTapiRequest->ulReturnValue = (ULONG) NDIS_STATUS_FAILURE; LocalIrp->IoStatus.Status = STATUS_CANCELLED; LocalIrp->IoStatus.Information = 0; DeviceExtension->IrpsCanceledCount++; KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); DBGOUT((2,"NdisTapiCleanup: Completing ProviderRequestIrp %p", LocalIrp)); IoCompleteRequest (LocalIrp, IO_NO_INCREMENT); KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); pReq = (PPROVIDER_REQUEST) DeviceExtension->ProviderRequestList.Flink; } else { pReq = (PPROVIDER_REQUEST) pReq->Linkage.Flink; } } } KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); // // Complete the cleanup request with STATUS_SUCCESS. // Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT); DBGOUT((2,"NdisTapiCleanup: exit")); return(STATUS_SUCCESS); } NTSTATUS NdisTapiDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Process the IRPs sent to this device. Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet Return Value: --*/ { NTSTATUS NtStatus; PVOID ioBuffer; ULONG inputBufferLength; ULONG outputBufferLength; PIO_STACK_LOCATION irpStack; // // Get a pointer to the current location in the Irp. This is where // the function codes and parameters are located. // irpStack = IoGetCurrentIrpStackLocation (Irp); // // Get the pointer to the input/output buffer and it's length // ioBuffer = Irp->AssociatedIrp.SystemBuffer; inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; switch (irpStack->MajorFunction) { case IRP_MJ_CREATE: DBGOUT ((2, "IRP_MJ_CREATE, Irp=%p", Irp)); InterlockedIncrement(&DeviceExtension->RefCount); NtStatus = Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; break; case IRP_MJ_CLOSE: DBGOUT ((2, "IRP_MJ_CLOSE, Irp=%p", Irp)); DoIrpMjCloseWork(Irp); NtStatus = STATUS_SUCCESS; break; case IRP_MJ_DEVICE_CONTROL: switch (irpStack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_NDISTAPI_CONNECT: DBGOUT ((2, "IOCTL_NDISTAPI_CONNECT, Irp=%p", Irp)); NtStatus = DoIoctlConnectWork(Irp, ioBuffer, inputBufferLength, outputBufferLength); break; case IOCTL_NDISTAPI_QUERY_INFO: case IOCTL_NDISTAPI_SET_INFO: DBGOUT ((2, "IOCTL_NDISTAPI_QUERY/SET_INFO, Irp=%p", Irp)); NtStatus = DoIoctlQuerySetWork(Irp, ioBuffer, inputBufferLength, outputBufferLength); break; case IOCTL_NDISTAPI_GET_LINE_EVENTS: DBGOUT ((2, "IOCTL_NDISTAPI_GET_LINE_EVENTS, Irp=%p", Irp)); NtStatus = DoGetProviderEventsWork(Irp, ioBuffer, inputBufferLength, outputBufferLength); break; case IOCTL_NDISTAPI_CREATE: DBGOUT ((2, "IOCTL_NDISTAPI_CREATE, Irp=%p", Irp)); NtStatus = DoLineCreateWork(Irp, ioBuffer, inputBufferLength, outputBufferLength); break; default: DBGOUT ((2, "Unknown IRP_MJ_DEVICE_CONTROL, Irp=%p", Irp)); NtStatus = Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; break; } break; } if (NtStatus == STATUS_PENDING) { return (STATUS_PENDING); } ASSERT(NtStatus == Irp->IoStatus.Status); IoCompleteRequest (Irp, IO_NO_INCREMENT); DBGOUT((3, "NdisTapiDispatch: completed Irp=%p", Irp)); return NtStatus; } VOID NdisTapiUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Free all the allocated resources, etc. Arguments: DriverObject - pointer to a driver object Return Value: --*/ { KIRQL oldIrql; PPROVIDER_INFO provider, nextProvider; DBGOUT ((2, "NdisTapiUnload: enter")); // // Delete the device object & sundry resources // while (!(IsListEmpty(&DeviceExtension->ProviderEventList))) { PPROVIDER_EVENT ProviderEvent; ProviderEvent = (PPROVIDER_EVENT) RemoveHeadList(&DeviceExtension->ProviderEventList); ExFreeToNPagedLookasideList(&ProviderEventLookaside, ProviderEvent); } ExDeleteNPagedLookasideList(&ProviderEventLookaside); KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); provider = DeviceExtension->Providers; while (provider != NULL) { nextProvider = provider->Next; ExFreePool (provider); provider = nextProvider; } KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); IoDeleteDevice (DriverObject->DeviceObject); DBGOUT ((2, "NdisTapiUnload: exit")); return; } VOID NdisTapiRegisterProvider( IN NDIS_HANDLE ProviderHandle, IN PNDISTAPI_CHARACTERISTICS Chars ) /*++ Routine Description: This func gets called by Ndis as a result of a Mac driver registering for Connection Wrapper services. Arguments: Return Value: --*/ { KIRQL oldIrql; BOOLEAN sendRequest = FALSE; NDIS_STATUS ndisStatus; PPROVIDER_INFO provider, newProvider; DBGOUT ((2, "NdisTapiRegisterProvider: enter")); // // Grab the spin lock & add the new provider, and see whether to // send the provider an init request // KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); // // See if this provider has already registered once. // provider = DeviceExtension->Providers; while (provider != NULL) { if (provider->Status == PROVIDER_STATUS_OFFLINE && RtlCompareMemory( &provider->Guid, &Chars->Guid, sizeof(provider->Guid)) == sizeof(provider->Guid)) { DBGOUT(( 1, "Found a provider %p for Guid %4.4x-%2.2x-%2.2x-%1.1x%1.1x%1.1x%1.1x%1.1x%1.1x%1.1x%1.1x", provider, provider->Guid.Data1, provider->Guid.Data2, provider->Guid.Data3, provider->Guid.Data4[0], provider->Guid.Data4[1], provider->Guid.Data4[2], provider->Guid.Data4[3], provider->Guid.Data4[4], provider->Guid.Data4[5], provider->Guid.Data4[6], provider->Guid.Data4[7] )); DBGOUT(( 1, "numDevices %d BaseID %d", provider->NumDevices, provider->DeviceIDBase )); provider->Status = PROVIDER_STATUS_PENDING_REINIT; provider->ProviderHandle = ProviderHandle; provider->RequestProc = Chars->RequestProc; provider->MediaType = Chars->MediaType; break; } provider = provider->Next; } if (provider == NULL) { // // Create a new provider instance // newProvider = ExAllocatePoolWithTag( NonPagedPoolCacheAligned, sizeof(PROVIDER_INFO), 'IPAT' ); if (!newProvider) { KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); return; } RtlZeroMemory(newProvider, sizeof(PROVIDER_INFO)); newProvider->Status = PROVIDER_STATUS_PENDING_INIT; newProvider->ProviderHandle = ProviderHandle; newProvider->RequestProc = Chars->RequestProc; RtlMoveMemory( &newProvider->Guid, &Chars->Guid, sizeof(newProvider->Guid) ); newProvider->MediaType = Chars->MediaType; newProvider->Next = NULL; DBGOUT(( 1, "New provider for Guid %4.4x-%2.2x-%2.2x-%1.1x%1.1x%1.1x%1.1x%1.1x%1.1x%1.1x%1.1x", newProvider->Guid.Data1, newProvider->Guid.Data2, newProvider->Guid.Data3, newProvider->Guid.Data4[0], newProvider->Guid.Data4[1], newProvider->Guid.Data4[2], newProvider->Guid.Data4[3], newProvider->Guid.Data4[4], newProvider->Guid.Data4[5], newProvider->Guid.Data4[6], newProvider->Guid.Data4[7] )); // // Add the new provider, and see whether to send the // provider an init request // if ((provider = DeviceExtension->Providers) == NULL) { DeviceExtension->Providers = newProvider; } else { while (provider->Next != NULL) { provider = provider->Next; } provider->Next = newProvider; } provider = newProvider; } // // The only case where we want to send off an init request to the // provider directly is when we are currently connected to TAPI, // and even then only when there are no other inits pending (since // we must synchronize inits due to calculation of DeviceIDBase) // if (DeviceExtension->Status == NDISTAPI_STATUS_CONNECTED) { // // TAPI is up. // // If TAPI already knows about this provider // go ahead and init the provider with it's current // DeviceIDBase. // // If TAPI does not know about this provider we // need to give TAPI an indication of a new device // coming on line. // if (provider->Status == PROVIDER_STATUS_PENDING_REINIT) { KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); ndisStatus = SendProviderInitRequest (provider); if (ndisStatus == NDIS_STATUS_PENDING) { // // Wait for completion routine to get called // KeWaitForSingleObject (&provider->SyncEvent, Executive, KernelMode, FALSE, (PTIME) NULL); } KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); // // Get tapi to reset the state of these lines by // forcing a line_close... // if (provider->DeviceInfo != NULL) { PDEVICE_INFO DeviceInfo; ULONG i; for(i = 0, DeviceInfo = provider->DeviceInfo; i < provider->NumDevices; i++, DeviceInfo++) { NDIS_TAPI_EVENT NdisTapiEvent; RtlZeroMemory (&NdisTapiEvent, sizeof(NDIS_TAPI_EVENT)); if (DeviceInfo->htLine != (HTAPI_LINE)NULL) { NdisTapiEvent.htLine = DeviceInfo->htLine; NdisTapiEvent.ulMsg = LINE_CLOSE; KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); NdisTapiIndicateStatus((ULONG_PTR) provider, &NdisTapiEvent, sizeof (NDIS_TAPI_EVENT)); KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); DeviceInfo->htLine = (HTAPI_LINE)NULL; DeviceInfo->hdLine = (HDRV_LINE)NULL; } } } KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); } else { NDIS_TAPI_EVENT NdisTapiEvent; ASSERT(provider->Status == PROVIDER_STATUS_PENDING_INIT); provider->Status = PROVIDER_STATUS_PENDING_LINE_CREATE; // // If there are no providers in the middle of doing // line_create's then we will kick off creates for this // provider. // // If we already have a line create pending on a provider // then we will wait until all of its line creates have // finished before we start sending them from // this one. // if (!(DeviceExtension->Flags & PENDING_LINECREATE)) { // // Do a LINE_CREATE so that we can get the starting // BaseID for this provider. When TAPI calls us back // with ProviderCreateLineDevice we will have the // BaseDeviceID to use for this provider and we will // then init the provider. Once we find out how many // devices the provider has we will alert TAPI of the // additional devices. // RtlZeroMemory(&NdisTapiEvent, sizeof(NDIS_TAPI_EVENT)); provider->TempID = (ULONG_PTR)provider; DBGOUT((-1, "LINE_CREATE %d for provider %p", provider->CreateCount, provider->TempID )); NdisTapiEvent.ulMsg = LINE_CREATE; NdisTapiEvent.ulParam1 = 0; NdisTapiEvent.ulParam2 = provider->TempID; NdisTapiEvent.ulParam3 = 0; DeviceExtension->Flags |= PENDING_LINECREATE; KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); NdisTapiIndicateStatus((ULONG_PTR)provider, &NdisTapiEvent, sizeof(NDIS_TAPI_EVENT)); } else { KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); } } KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); } KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); ObReferenceObject(DeviceExtension->DeviceObject); } VOID NdisTapiDeregisterProvider( IN NDIS_HANDLE ProviderHandle ) /*++ Routine Description: This func... Note that this func does not send the provider a shutdown message, as an implicit shutdown is assumed when the provider deegisters. Arguments: Return Value: --*/ { KIRQL oldIrql; BOOLEAN sendShutdownMsg = FALSE; PPROVIDER_INFO provider, previousProvider; DBGOUT ((2, "NdisTapiDeregisterProvider: enter")); // // Grab the spin lock protecting the device extension // KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); // // Find the provider instance corresponding to ProviderHandle // previousProvider = NULL; provider = DeviceExtension->Providers; while (provider != NULL && provider->ProviderHandle != ProviderHandle) { previousProvider = provider; provider = provider->Next; } if (provider == NULL) { KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); return; } if (provider->Status == PROVIDER_STATUS_ONLINE) { DeviceExtension->NdisTapiNumDevices -= provider->NumDevices; } // // Send the ProviderShutdown only if the provider // is not in PROVIDER_STATUS_OFFLINE. Otherwise // DoIrpMjCloseWork can end up sending // Providershutdown on a removed adapter. // if(provider->Status != PROVIDER_STATUS_OFFLINE) { SendProviderShutdown (provider, &oldIrql); provider->Status = PROVIDER_STATUS_OFFLINE; } // // Do the right thing according to the current NdisTapi state // switch (DeviceExtension->Status) { case NDISTAPI_STATUS_CONNECTED: { UINT i; // // Mark provider as offline // provider->Status = PROVIDER_STATUS_OFFLINE; provider->ProviderHandle = NULL; #if 0 if (provider->DeviceInfo != NULL) { PDEVICE_INFO DeviceInfo; for( i = 0, DeviceInfo = provider->DeviceInfo; i < provider->NumDevices; i++, DeviceInfo++ ) { NDIS_TAPI_EVENT NdisTapiEvent; RtlZeroMemory (&NdisTapiEvent, sizeof(NDIS_TAPI_EVENT)); if (DeviceInfo->htLine != (HTAPI_LINE)NULL) { NdisTapiEvent.htLine = DeviceInfo->htLine; NdisTapiEvent.ulMsg = LINE_CLOSE; KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); NdisTapiIndicateStatus((ULONG_PTR) provider, &NdisTapiEvent, sizeof (NDIS_TAPI_EVENT)); KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); DeviceInfo->htLine = (HTAPI_LINE)NULL; } } } #endif // PnP: what if providerInfo->State == PROVIDER_INIT_PENDING // PnP: what if providerInfo->State == PROVIDER_OFFLINE break; } case NDISTAPI_STATUS_DISCONNECTING: case NDISTAPI_STATUS_DISCONNECTED: // // Fix up pointers, remove provider from list // if (previousProvider == NULL) { DeviceExtension->Providers = provider->Next; } else { previousProvider->Next = provider->Next; } ExFreePool (provider); break; case NDISTAPI_STATUS_CONNECTING: // PnP: implement break; } // switch KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); ObDereferenceObject(DeviceExtension->DeviceObject); DBGOUT((2, "NdisTapiDeregisterProvider: exit")); } VOID NdisTapiIndicateStatus( IN ULONG_PTR DriverHandle, IN PVOID StatusBuffer, IN UINT StatusBufferSize ) /*++ Routine Description: This func gets called by Ndis when a miniport driver calls NdisIndicateStatus to notify us of an async event (i.e. new call, call state chg, dev state chg, etc.) Arguments: Return Value: --*/ { PIRP irp; KIRQL oldIrql; ULONG bytesInQueue; ULONG bytesToMove; ULONG moveSize; BOOLEAN satisfiedPendingEventsRequest = FALSE; PNDIS_TAPI_EVENT ndisTapiEvent; PNDISTAPI_EVENT_DATA ndisTapiEventData; DBGOUT((2,"NdisTapiIndicateStatus: enter")); bytesInQueue = StatusBufferSize; moveSize = 0; // // Sync event buf access by acquiring SpinLock // KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); // // The very first thing to do is check if this is a LINE_NEWCALL // indication. If so, we need to generate a unique tapi call // handle, which will be both returned to the calling miniport // (for use in subsequent status indications) and passed up to // the tapi server. // // The algorithim for computing a unique "htCall" is to start // at the value 0x80000001, and perpetually increment by 2. // Keeping the low bit set will allow the user-mode TAPI component // we talk to to distinguish between these incoming call handles // and outgoing call handles, the latter of which will always // have the low bit zero'd (since they're really pointers to heap). // We are again going to use the space between 0x80000001 and 0xFFFFFFFF // to identify our call handle. This allows for a maximum of 1GB of // calls to be active at a time. This is done to avoid a conflict // with ndiswan's connection table index. A bug in the ddk doc's // had users providing the connectionid instead of ndiswan's context // in the line get id oid. Ndiswan has to check both of these and // now that they overlap it can cause problems. NdisWan will use // 0x00000000 - 0x80000000 for it's context values. // // In <= NT 4.0, valid values used to range between 0x80000000 // and 0xffffffff, as we relied on the fact that user-mode // addresses always had the low bit zero'd. (Not a valid // assumption anymore!) // ndisTapiEvent = StatusBuffer; if (ndisTapiEvent->ulMsg == LINE_NEWCALL) { ndisTapiEvent->ulParam2 = DeviceExtension->htCall; DeviceExtension->htCall++; DeviceExtension->htCall++; if (DeviceExtension->htCall < 0x80000000) { DeviceExtension->htCall = 0x80000001; } } // // Check of there is an outstanding request to satisfy // if (DeviceExtension->EventsRequestIrp) { ASSERT(IsListEmpty(&DeviceExtension->ProviderEventList)); // // Acquire the cancel spinlock, remove the request from the // cancellable state, and free the cancel spinlock. // irp = DeviceExtension->EventsRequestIrp; if (IoSetCancelRoutine(irp, NULL) != NULL) { DeviceExtension->EventsRequestIrp = NULL; // // Copy as much of the input data possible from the input data // queue to the SystemBuffer to satisfy the read. // ndisTapiEventData = irp->AssociatedIrp.SystemBuffer; bytesToMove = ndisTapiEventData->ulTotalSize; moveSize = (bytesInQueue < bytesToMove) ? bytesInQueue : bytesToMove; RtlMoveMemory ( ndisTapiEventData->Data, (PCHAR) StatusBuffer, moveSize ); // // Set the flag so that we start the next packet and complete // this read request (with STATUS_SUCCESS) prior to return. // ndisTapiEventData->ulUsedSize = moveSize; irp->IoStatus.Status = STATUS_SUCCESS; irp->IoStatus.Information = sizeof(NDISTAPI_EVENT_DATA) + moveSize - 1; satisfiedPendingEventsRequest = TRUE; } } else { do { PPROVIDER_EVENT ProviderEvent; ProviderEvent = ExAllocateFromNPagedLookasideList(&ProviderEventLookaside); if (ProviderEvent == NULL) { break; } RtlMoveMemory(&ProviderEvent->Event, StatusBuffer, sizeof(NDIS_TAPI_EVENT)); InsertTailList(&DeviceExtension->ProviderEventList, &ProviderEvent->Linkage); DeviceExtension->EventCount++; } while ( FALSE ); } // // Release the spinlock // KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); // // If we satisfied an outstanding get events request then complete it // if (satisfiedPendingEventsRequest) { IoCompleteRequest (irp, IO_NO_INCREMENT); DBGOUT((2, "NdisTapiIndicateStatus: completion req %p", irp)); } DBGOUT((2,"NdisTapiIndicateStatus: exit")); return; } VOID NdisTapiCompleteRequest( IN NDIS_HANDLE NdisHandle, IN PNDIS_REQUEST NdisRequest, IN NDIS_STATUS NdisStatus ) /*++ Routine Description: This func gets called by Ndis as a result of a Mac driver calling NdisCompleteRequest of one of our requests. Arguments: Return Value: --*/ { PIRP Irp; KIRQL oldIrql; ULONG requestID; PNDISTAPI_REQUEST ndisTapiRequest; PPROVIDER_REQUEST providerRequest; PPROVIDER_REQUEST tempReq; PIO_STACK_LOCATION irpStack; DBGOUT ((2, "NdisTapiCompleteRequest: enter")); providerRequest = CONTAINING_RECORD(NdisRequest, PROVIDER_REQUEST, NdisRequest); do { if (providerRequest->Flags & INTERNAL_REQUEST) { // // This request originated from NdisTapi.sys // switch (NdisRequest->DATA.SET_INFORMATION.Oid) { case OID_TAPI_PROVIDER_INITIALIZE: DBGOUT((3, "NdisTapiCompleteRequest: ProviderInit - Provider=%p, reqID=%x, Status=%x", providerRequest->Provider, providerRequest->RequestID, NdisStatus)); switch (DeviceExtension->Status) { case NDISTAPI_STATUS_CONNECTED: case NDISTAPI_STATUS_CONNECTING: DoProviderInitComplete (providerRequest, NdisStatus); break; case NDISTAPI_STATUS_DISCONNECTED: case NDISTAPI_STATUS_DISCONNECTING: default: break; } break; case OID_TAPI_PROVIDER_SHUTDOWN: DBGOUT((3, "NdisTapiCompleteRequest: ProviderShutdown - Provider=%p, reqID=%x, Status=%x", providerRequest->Provider, providerRequest->RequestID, NdisStatus)); break; default: DBGOUT((1, "NdisTapiCompleteRequest: unrecognized Oid")); break; } break; } // // This is a request originating from TAPI // // // Acquire the SpinLock since we're going to be removing a // TAPI request from the queue, and it might not be the request // we're looking for. The primary concern is that we could (if // the request we're really looking for has been removed) remove // a synchrously-completed request that is about to be removed & // completed in NdisTapiDispatch, in which case we want to stick // the request back in the queue before NdisTapiDispatch tries // to remove it. // KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); tempReq = (PPROVIDER_REQUEST)DeviceExtension->ProviderRequestList.Flink; while ((PVOID)tempReq != (PVOID)&DeviceExtension->ProviderRequestList) { if (tempReq == providerRequest) { break; } tempReq = (PPROVIDER_REQUEST)tempReq->Linkage.Flink; } if (tempReq != providerRequest) { #if DBG DbgPrint("NDISTAPI: NdisTapiCompleteRequest: Request %p not found!\n", providerRequest); #endif DeviceExtension->MissingRequests++; KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); break; } Irp = providerRequest->Irp; ndisTapiRequest = Irp->AssociatedIrp.SystemBuffer; ASSERT(providerRequest->RequestID == *((ULONG *)ndisTapiRequest->Data)); // // Remove the IRP from the cancelable state // if (IoSetCancelRoutine(Irp, NULL) == NULL) { KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); break; } RemoveEntryList(&providerRequest->Linkage); DeviceExtension->RequestCount--; KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); DBGOUT((3, "NdisTapiCompleteRequest: Irp=%p, Oid=%x, devID=%d, reqID=%x, Status=%x", Irp, ndisTapiRequest->Oid, ndisTapiRequest->ulDeviceID, *((ULONG *)ndisTapiRequest->Data), NdisStatus)); // // Copy the relevant info back to the IRP // irpStack = IoGetCurrentIrpStackLocation (Irp); // // If this was a succesful QUERY_INFO request copy all the // data back to the tapi request buf & set // Irp->IoStatus.Information appropriately. Otherwise, we // just need to pass back the return value. Also mark irp // as successfully completed (regardless of actual op result) // if ((NdisRequest->RequestType == NdisRequestQueryInformation) && (NdisStatus == NDIS_STATUS_SUCCESS)) { RtlMoveMemory(ndisTapiRequest->Data, NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer, ndisTapiRequest->ulDataSize); Irp->IoStatus.Information = irpStack->Parameters.DeviceIoControl.OutputBufferLength; } else { Irp->IoStatus.Information = sizeof (ULONG); } if((NdisRequest->RequestType == NdisRequestQueryInformation) && (NdisRequest->DATA.QUERY_INFORMATION.Oid == OID_TAPI_OPEN)) { DoLineOpenCompleteWork(ndisTapiRequest, providerRequest->Provider); } Irp->IoStatus.Status = STATUS_SUCCESS; ndisTapiRequest->ulReturnValue = NdisStatus; IoCompleteRequest (Irp, IO_NO_INCREMENT); } while (FALSE); ExFreePool (providerRequest); DBGOUT ((2, "NdisTapiCompleteRequest: exit")); } #if DBG VOID DbgPrt( IN LONG DbgLevel, IN PUCHAR DbgMessage, IN ... ) /*++ Routine Description: Formats the incoming debug message & calls DbgPrint Arguments: DbgLevel - level of message verboseness DbgMessage - printf-style format string, followed by appropriate list of arguments Return Value: --*/ { if (DbgLevel <= NdisTapiDebugLevel) { char buf[256] = "NDISTAPI: "; va_list ap; va_start (ap, DbgMessage); vsprintf (&buf[10], DbgMessage, ap); strcat (buf, "\n"); DbgPrint (buf); va_end(ap); } return; } #endif // DBG VOID DoProviderInitComplete( PPROVIDER_REQUEST ProviderRequest, NDIS_STATUS Status ) /*++ Routine Description: Arguments: ProviderInitRequest - pointer successfully completed init request Return Value: Note: --*/ { PPROVIDER_INFO provider = ProviderRequest->Provider; PNDIS_TAPI_PROVIDER_INITIALIZE providerInitData = (PNDIS_TAPI_PROVIDER_INITIALIZE) ProviderRequest->Data; KIRQL OldIrql; DBGOUT ((2, "DoProviderInitComplete: enter")); // // Wrap this in an exception handler in case the provider was // removed during an async completion // try { if (Status == NDIS_STATUS_SUCCESS) { provider->ProviderID = (ULONG)providerInitData->ulProviderID; provider->NumDevices = providerInitData->ulNumLineDevs; KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql); DeviceExtension->NdisTapiNumDevices += provider->NumDevices; KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql); provider->Status = PROVIDER_STATUS_ONLINE; if (provider->DeviceInfo == NULL) { provider->DeviceInfo = (PDEVICE_INFO) ExAllocatePoolWithTag( NonPagedPool, sizeof(DEVICE_INFO) * provider->NumDevices, 'IPAT' ); if (provider->DeviceInfo != NULL) { PDEVICE_INFO DeviceInfo; UINT i; RtlZeroMemory( provider->DeviceInfo, sizeof(DEVICE_INFO) * provider->NumDevices ); for(i = 0, DeviceInfo = provider->DeviceInfo; i < provider->NumDevices; i++, DeviceInfo++) { DeviceInfo->DeviceID = provider->DeviceIDBase + i; } } } } // // Set the event which sync's miniport inits // KeSetEvent(&provider->SyncEvent, 0, FALSE); DBGOUT((3, "providerID = 0x%x, numDevices = %d, BaseID = %d", provider->ProviderID, provider->NumDevices, provider->DeviceIDBase)); } except (EXCEPTION_EXECUTE_HANDLER) { DBGOUT((1, "DoProviderInitComplete: provider invalid")); } DBGOUT ((2, "DoProviderInitComplete: exit")); } ULONG GetLineEvents( PCHAR EventBuffer, ULONG BufferSize ) /*++ Routine Description: Arguments: Return Value: Note: Assumes DeviceExtension->SpinLock held by caller. --*/ { ULONG BytesLeft; ULONG BytesMoved; ULONG EventCount; BytesLeft = BufferSize; BytesMoved = 0; EventCount = 0; while (!(IsListEmpty(&DeviceExtension->ProviderEventList))) { PPROVIDER_EVENT ProviderEvent; if (BytesLeft < sizeof(NDIS_TAPI_EVENT)) { break; } ProviderEvent = (PPROVIDER_EVENT) RemoveHeadList(&DeviceExtension->ProviderEventList); EventCount++; RtlMoveMemory(EventBuffer + BytesMoved, (PUCHAR)&ProviderEvent->Event, sizeof(NDIS_TAPI_EVENT)); BytesMoved += sizeof(NDIS_TAPI_EVENT); BytesLeft -= sizeof(NDIS_TAPI_EVENT); ExFreeToNPagedLookasideList(&ProviderEventLookaside, ProviderEvent); } DeviceExtension->EventCount -= EventCount; DBGOUT((3, "GetLineEvents: Returned %d Events", EventCount)); return (BytesMoved); } NDIS_STATUS SendProviderInitRequest( PPROVIDER_INFO Provider ) /*++ Routine Description: Arguments: Provider - pointer to a PROVIDER_INFO representing provider to initialize Return Value: Note: --*/ { KIRQL oldIrql; NDIS_STATUS ndisStatus; PNDIS_REQUEST NdisRequest; PPROVIDER_INFO tmpProvider; PPROVIDER_REQUEST providerRequest; PNDIS_TAPI_PROVIDER_INITIALIZE providerInitData; DBGOUT ((2, "SendProviderInitRequest: enter")); KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); // // Determine the DeviceIDBase to be used for this provider // if (Provider->Status == PROVIDER_STATUS_PENDING_INIT) { Provider->DeviceIDBase = DeviceExtension->ProviderBaseID; tmpProvider = DeviceExtension->Providers; while (tmpProvider != NULL) { if (tmpProvider->Status != PROVIDER_STATUS_PENDING_INIT) { Provider->DeviceIDBase += tmpProvider->NumDevices; } tmpProvider = tmpProvider->Next; } } // // Create a provider init request // providerRequest = ExAllocatePoolWithTag( NonPagedPoolCacheAligned, sizeof(PROVIDER_REQUEST) + sizeof(NDIS_TAPI_PROVIDER_INITIALIZE) - sizeof(ULONG), 'IPAT' ); if (!providerRequest) { KeReleaseSpinLock(&DeviceExtension->SpinLock, oldIrql); return NDIS_STATUS_RESOURCES; } providerRequest->Irp = NULL; providerRequest->Flags = INTERNAL_REQUEST; providerRequest->Provider = Provider; NdisRequest = &providerRequest->NdisRequest; NdisRequest->RequestType = NdisRequestQueryInformation; NdisRequest->DATA.SET_INFORMATION.Oid = OID_TAPI_PROVIDER_INITIALIZE; NdisRequest->DATA.SET_INFORMATION.InformationBuffer = providerRequest->Data; NdisRequest->DATA.SET_INFORMATION.InformationBufferLength = sizeof(NDIS_TAPI_PROVIDER_INITIALIZE); providerInitData = (PNDIS_TAPI_PROVIDER_INITIALIZE) providerRequest->Data; providerRequest->RequestID = providerInitData->ulRequestID = ++DeviceExtension->ulRequestID; providerInitData->ulDeviceIDBase = Provider->DeviceIDBase; KeInitializeEvent(&Provider->SyncEvent, SynchronizationEvent, FALSE); KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); // // Send the request // ndisStatus= (*Provider->RequestProc) (Provider->ProviderHandle,NdisRequest); if (ndisStatus != NDIS_STATUS_PENDING) { DoProviderInitComplete (providerRequest, ndisStatus); ExFreePool (providerRequest); } DBGOUT ((2, "SendProviderInitRequest: exit status %x", ndisStatus)); return ndisStatus; } NDIS_STATUS SendProviderShutdown( PPROVIDER_INFO Provider, PKIRQL oldIrql ) /*++ Routine Description: Arguments: Return Value: A pointer to the next provider in the global providers list Note: Assumes DeviceExtension->SpinLock held by caller. --*/ { NDIS_STATUS ndisStatus; PNDIS_REQUEST NdisRequest; PPROVIDER_REQUEST providerRequest; PNDIS_TAPI_PROVIDER_SHUTDOWN providerShutdownData; DBGOUT ((2, "SendProviderShutdown: Provider=%p", Provider)); // // Create a provider init request // providerRequest = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, sizeof(PROVIDER_REQUEST) + sizeof(NDIS_TAPI_PROVIDER_SHUTDOWN) - sizeof(ULONG), 'IPAT'); if (!providerRequest) { return NDIS_STATUS_RESOURCES; } providerRequest->Irp = NULL; providerRequest->Flags = INTERNAL_REQUEST; providerRequest->Provider = Provider; NdisRequest = &providerRequest->NdisRequest; NdisRequest->RequestType = NdisRequestSetInformation; NdisRequest->DATA.SET_INFORMATION.Oid = OID_TAPI_PROVIDER_SHUTDOWN; NdisRequest->DATA.SET_INFORMATION.InformationBuffer = providerRequest->Data; NdisRequest->DATA.SET_INFORMATION.InformationBufferLength = sizeof(NDIS_TAPI_PROVIDER_SHUTDOWN); providerShutdownData = (PNDIS_TAPI_PROVIDER_SHUTDOWN)providerRequest->Data; providerRequest->RequestID = providerShutdownData->ulRequestID = ++DeviceExtension->ulRequestID; KeReleaseSpinLock (&DeviceExtension->SpinLock, *oldIrql); // // Send the request // ndisStatus = (*Provider->RequestProc) (Provider->ProviderHandle, NdisRequest); // // If request was completed synchronously then free the request // (otherwise it will get freed when the completion proc is called) // if (ndisStatus != NDIS_STATUS_PENDING) { ExFreePool (providerRequest); } DBGOUT ((2, "SendProviderShutdown: Status=%x", ndisStatus)); KeAcquireSpinLock (&DeviceExtension->SpinLock, oldIrql); return ndisStatus; } BOOLEAN SyncInitAllProviders( void ) /*++ Routine Description: This functions walks the list of registered providers and sends init requests to the providers in the PENDING_INIT state Arguments: (none) Return Value: TRUE if all registered providers initialized, or FALSE if there are more providers to initialze Note: --*/ { ULONG numDevices = 0; NDIS_STATUS ndisStatus; PPROVIDER_INFO provider; KIRQL oldIrql; DBGOUT((2, "SyncInitAllProviders: enter")); KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); provider = DeviceExtension->Providers; while (provider != NULL) { if (provider->Status == PROVIDER_STATUS_PENDING_INIT || provider->Status == PROVIDER_STATUS_PENDING_REINIT || provider->Status == PROVIDER_STATUS_PENDING_LINE_CREATE) { KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); ndisStatus = SendProviderInitRequest (provider); if (ndisStatus == NDIS_STATUS_PENDING) { // // Wait for completion routine to get called // KeWaitForSingleObject (&provider->SyncEvent, Executive, KernelMode, FALSE, (PTIME) NULL ); } KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); } provider = provider->Next; } KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); DBGOUT((2, "SyncInitAllProviders: exit")); return TRUE; } VOID DoIrpMjCloseWork( PIRP Irp ) { KIRQL oldIrql; KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); if (InterlockedDecrement(&DeviceExtension->RefCount) == 0) { if (DeviceExtension->Status == NDISTAPI_STATUS_CONNECTED) { PPROVIDER_INFO provider; DeviceExtension->Status = NDISTAPI_STATUS_DISCONNECTING; // // Send the providers a shutdown request // provider = DeviceExtension->Providers; while (provider != NULL) { switch (provider->Status) { case PROVIDER_STATUS_ONLINE: DeviceExtension->NdisTapiNumDevices -= provider->NumDevices; SendProviderShutdown (provider, &oldIrql); // // fall thru... // case PROVIDER_STATUS_PENDING_INIT: case PROVIDER_STATUS_PENDING_REINIT: // // Reset provider status // provider->Status = PROVIDER_STATUS_PENDING_INIT; break; case PROVIDER_STATUS_OFFLINE: break; } provider = provider->Next; } DeviceExtension->Status = NDISTAPI_STATUS_DISCONNECTED; ASSERT(DeviceExtension->NdisTapiNumDevices == 0); } } KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; } NTSTATUS DoIoctlConnectWork( PIRP Irp, PVOID ioBuffer, ULONG inputBufferLength, ULONG outputBufferLength ) { KIRQL oldIrql; ULONG InfoSize; NTSTATUS NtStatus; // // Someone's connecting. Make sure they passed us a valid // info buffer // KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); do { if ((inputBufferLength < 2*sizeof(ULONG)) || (outputBufferLength < sizeof(ULONG))) { DBGOUT ((3, "IOCTL_NDISTAPI_CONNECT: buffer too small")); NtStatus = STATUS_BUFFER_TOO_SMALL; InfoSize = 0; break; } if (DeviceExtension->Status == NDISTAPI_STATUS_DISCONNECTED) { DeviceExtension->Status = NDISTAPI_STATUS_CONNECTING; DeviceExtension->ProviderBaseID = *((ULONG *) ioBuffer); DBGOUT ((1, "ProviderBaseID %d", DeviceExtension->ProviderBaseID)); // // Synchronously init all providers // KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); SyncInitAllProviders(); KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); } // // Return the number of line devs // { ULONG OfflineCount; PPROVIDER_INFO provider; // // Since some providers might be temporarily offline // we need to tell tapi about them even though they // are not currently useable. This keeps the tapi // deviceid space consistent. // OfflineCount = 0; provider = DeviceExtension->Providers; while (provider != NULL) { if (provider->Status == PROVIDER_STATUS_OFFLINE) { OfflineCount += provider->NumDevices; } provider = provider->Next; } *((ULONG *) ioBuffer)= DeviceExtension->NdisTapiNumDevices + OfflineCount; } DeviceExtension->Status = NDISTAPI_STATUS_CONNECTED; KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); InfoSize = sizeof(ULONG); NtStatus = STATUS_SUCCESS; } while (FALSE); Irp->IoStatus.Status = NtStatus; Irp->IoStatus.Information = InfoSize; return (NtStatus); } NTSTATUS DoIoctlQuerySetWork( PIRP Irp, PVOID ioBuffer, ULONG inputBufferLength, ULONG outputBufferLength ) { KIRQL oldIrql; ULONG InfoSize; NTSTATUS NtStatus; PPROVIDER_INFO provider; NDIS_STATUS ndisStatus; PNDIS_REQUEST NdisRequest; PNDISTAPI_REQUEST ndisTapiRequest; PPROVIDER_REQUEST providerRequest; PIO_STACK_LOCATION irpStack; do { ndisTapiRequest = ioBuffer; NtStatus = STATUS_SUCCESS; InfoSize = 0; // // Make sure input & output buffers are large enough // if ((inputBufferLength < sizeof (NDISTAPI_REQUEST)) || (ndisTapiRequest->ulDataSize > 0x10000000) || (inputBufferLength < (sizeof (NDISTAPI_REQUEST) + ndisTapiRequest->ulDataSize - sizeof (UCHAR)) || (outputBufferLength < (sizeof (NDISTAPI_REQUEST) + ndisTapiRequest->ulDataSize - sizeof (UCHAR))))) { DBGOUT((-1, "NdisTapiDispatch: buffer to small!")); NtStatus = STATUS_BUFFER_TOO_SMALL; break; } // // Verify we're connected, then check the device ID of the // incoming request against our list of online devices // ndisStatus = VerifyProvider(ndisTapiRequest, &provider); if (ndisStatus != NDIS_STATUS_SUCCESS) { ndisTapiRequest->ulReturnValue = ndisStatus; InfoSize = sizeof(ULONG); break; } // // If this is a line_close, check to see if the line has // been opened before sending a line close oid // if(ndisTapiRequest->Oid == OID_TAPI_CLOSE) { ndisStatus = VerifyLineClose(ndisTapiRequest, provider); if(ndisStatus != NDIS_STATUS_SUCCESS) { ndisTapiRequest->ulReturnValue = ndisStatus; InfoSize = sizeof(ULONG); break; } } // // Create the providerRequest & submit it // providerRequest = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, sizeof(PROVIDER_REQUEST) + ndisTapiRequest->ulDataSize - sizeof(ULONG), 'IPAT'); if (providerRequest == NULL) { DBGOUT((-1, "NdisTapiDispatch: unable to alloc request buf")); ndisTapiRequest->ulReturnValue = NDIS_STATUS_RESOURCES; InfoSize = sizeof (ULONG); break; } if (ndisTapiRequest->Oid == OID_TAPI_OPEN) { DoLineOpenWork(ndisTapiRequest, provider); } KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); providerRequest->Flags = 0; providerRequest->Irp = Irp; providerRequest->Provider = provider; providerRequest->RequestID = *((ULONG *)ndisTapiRequest->Data) = ++DeviceExtension->ulRequestID; RtlMoveMemory(providerRequest->Data, ndisTapiRequest->Data, ndisTapiRequest->ulDataSize); NdisRequest = &providerRequest->NdisRequest; irpStack = IoGetCurrentIrpStackLocation (Irp); NdisRequest->RequestType = (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_NDISTAPI_QUERY_INFO) ? NdisRequestQueryInformation : NdisRequestSetInformation; NdisRequest->DATA.SET_INFORMATION.Oid = ndisTapiRequest->Oid; NdisRequest->DATA.SET_INFORMATION.InformationBuffer = providerRequest->Data; NdisRequest->DATA.SET_INFORMATION.InformationBufferLength = ndisTapiRequest->ulDataSize; DBGOUT((3, "DoIoctlQuerySetWork: Oid=%x, devID=%d, reqID=%x", ndisTapiRequest->Oid, ndisTapiRequest->ulDeviceID, *((ULONG *)ndisTapiRequest->Data))); // // Queue up this TAPI request in our request list. // InsertTailList(&DeviceExtension->ProviderRequestList, &providerRequest->Linkage); DeviceExtension->RequestCount++; KeReleaseSpinLock(&DeviceExtension->SpinLock, oldIrql); // // Mark the TAPI request pending and set the cancel routine // IoMarkIrpPending(Irp); Irp->IoStatus.Status = STATUS_PENDING; IoSetCancelRoutine (Irp, NdisTapiCancel); // // Call the provider's request proc // ndisStatus = (*provider->RequestProc) (provider->ProviderHandle, NdisRequest); // // If PENDING was returned then just exit & let the completion // routine handle the request completion // // NOTE: If pending was returned then the request may have // already been completed, so DO NOT touch anything // in the Irp (don't reference the pointer, etc.) // if (ndisStatus == NDIS_STATUS_PENDING) { DBGOUT((1, "DoIoctlQuerySetWork: exit Irp=%p, Status=%x", Irp, STATUS_PENDING)); return (STATUS_PENDING); } // // The provider request completed synchronously, so remove // the TAPI request from the device queue. We need to // synchronize access to this queue with the // SpinLock. // KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); do { PPROVIDER_REQUEST pReq; pReq = (PPROVIDER_REQUEST) DeviceExtension->ProviderRequestList.Flink; while ((PVOID)pReq != (PVOID)&DeviceExtension->ProviderRequestList) { if (pReq == providerRequest) { break; } pReq = (PPROVIDER_REQUEST) pReq->Linkage.Flink; } if (pReq != providerRequest) { DBGOUT((0, "DoIoctlQuerySetWork - Request %p not found!", providerRequest)); KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); return (STATUS_PENDING); } Irp = providerRequest->Irp; ndisTapiRequest = Irp->AssociatedIrp.SystemBuffer; ASSERT(providerRequest->RequestID == *((ULONG *)ndisTapiRequest->Data)); // // Remove the IRP from the cancelable state // if (IoSetCancelRoutine(Irp, NULL) == NULL) { DBGOUT((0, "DoIoctlQuerySetWork - Irp %p has been canceled!", Irp)); KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); return (STATUS_PENDING); } RemoveEntryList(&providerRequest->Linkage); DeviceExtension->RequestCount--; } while (FALSE); KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); // // If this was a succesful QUERY_INFO request copy all the // data back to the tapi request buf & set // Irp->IoStatus.Information appropriately. Otherwise, we // just need to pass back the return value. // if ((irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_NDISTAPI_QUERY_INFO) && (ndisStatus == NDIS_STATUS_SUCCESS)) { RtlMoveMemory(ndisTapiRequest->Data, providerRequest->Data, ndisTapiRequest->ulDataSize); InfoSize = irpStack->Parameters.DeviceIoControl.OutputBufferLength; } else { InfoSize = sizeof (ULONG); } ndisTapiRequest->ulReturnValue = ndisStatus; // // Free the providerRequest // ExFreePool (providerRequest); } while (FALSE); Irp->IoStatus.Status = NtStatus; Irp->IoStatus.Information = InfoSize; DBGOUT((1, "DoIoctlQuerySetWork: exit Irp=%p, Status=%x", Irp, NtStatus)); return (NtStatus); } VOID DoLineOpenCompleteWork( PNDISTAPI_REQUEST ndisTapiRequest, PPROVIDER_INFO provider ) { DBGOUT((2, "DoLineOpenCompleteWork: Open Completed")); // // Now stash the hdLine for this deviceid // if (provider->DeviceInfo != NULL) { UINT i; PDEVICE_INFO DeviceInfo; PNDIS_TAPI_OPEN TapiOpen; TapiOpen = (PNDIS_TAPI_OPEN) ndisTapiRequest->Data; for(i = 0, DeviceInfo = provider->DeviceInfo; i < provider->NumDevices; i++, DeviceInfo++) { if (DeviceInfo->DeviceID == TapiOpen->ulDeviceID) { DeviceInfo->hdLine = TapiOpen->hdLine; DBGOUT((2, "Complete for open. stashing hdline=0x%x for device %d", DeviceInfo->hdLine, DeviceInfo->DeviceID)); break; } } } } VOID DoLineOpenWork( PNDISTAPI_REQUEST ndisTapiRequest, PPROVIDER_INFO provider ) { KIRQL oldIrql; PNDIS_TAPI_OPEN TapiOpen; PNDISTAPI_OPENDATA OpenData; TapiOpen = (PNDIS_TAPI_OPEN) ndisTapiRequest->Data; if (ndisTapiRequest->ulDataSize >= sizeof(NDIS_TAPI_OPEN) + sizeof(NDISTAPI_OPENDATA)) { OpenData = (PNDISTAPI_OPENDATA) ((PUCHAR)ndisTapiRequest->Data + sizeof(NDIS_TAPI_OPEN)); RtlMoveMemory(&OpenData->Guid, &provider->Guid, sizeof(OpenData->Guid)); OpenData->MediaType = provider->MediaType; } // // Now stash the htLine for this deviceid // if (provider->DeviceInfo != NULL) { UINT i; PDEVICE_INFO DeviceInfo; for(i = 0, DeviceInfo = provider->DeviceInfo; i < provider->NumDevices; i++, DeviceInfo++) { if (DeviceInfo->DeviceID == TapiOpen->ulDeviceID) { DeviceInfo->htLine = TapiOpen->htLine; DBGOUT(( 1, "Stash htLine - provider %p DeviceID %d htLine %x", provider, DeviceInfo->DeviceID, DeviceInfo->htLine)); } } } } NDIS_STATUS VerifyLineClose( PNDISTAPI_REQUEST ndisTapiRequest, PPROVIDER_INFO provider ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; if (provider->DeviceInfo != NULL) { UINT i; PDEVICE_INFO DeviceInfo; PNDIS_TAPI_CLOSE TapiClose; TapiClose = (PNDIS_TAPI_CLOSE) ndisTapiRequest->Data; for(i = 0, DeviceInfo = provider->DeviceInfo; i < provider->NumDevices; i++, DeviceInfo++) { if (DeviceInfo->hdLine == TapiClose->hdLine) { break; } } if(i == provider->NumDevices) { DBGOUT((2,"LINE_CLOSE: didn't find hdLine=0x%x", TapiClose->hdLine)); ndisStatus = NDISTAPIERR_DEVICEOFFLINE; } else { DBGOUT((2, "LINE_CLOSE: found hdLine=0x%x", TapiClose->hdLine)); } } return ndisStatus; } NDIS_STATUS VerifyProvider( PNDISTAPI_REQUEST ndisTapiRequest, PPROVIDER_INFO *provider ) { KIRQL oldIrql; PPROVIDER_INFO pp; NDIS_STATUS Status; ULONG targetDeviceID; Status = NDIS_STATUS_SUCCESS; *provider = NULL; targetDeviceID = ndisTapiRequest->ulDeviceID; KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); do { if (DeviceExtension->Status != NDISTAPI_STATUS_CONNECTED) { DBGOUT((3, "VerifyProvider: unconnected, returning err")); Status = NDISTAPIERR_UNINITIALIZED; break; } pp = DeviceExtension->Providers; while (pp != NULL) { if ((pp->Status == PROVIDER_STATUS_ONLINE) && (targetDeviceID >= pp->DeviceIDBase) && (targetDeviceID < pp->DeviceIDBase + pp->NumDevices) ) { break; } pp = pp->Next; } if (pp == NULL || pp->ProviderHandle == NULL) { // // Set Irp->IoStatus.Information large enough that err code // gets copied back to user buffer // DBGOUT((3, "VerifyProvider: dev offline, returning err")); Status = NDISTAPIERR_DEVICEOFFLINE; break; } *provider = pp; } while (FALSE); KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); return (Status); } NTSTATUS DoGetProviderEventsWork( PIRP Irp, PVOID ioBuffer, ULONG inputBufferLength, ULONG outputBufferLength ) { KIRQL oldIrql; ULONG InfoSize; NTSTATUS NtStatus; PNDISTAPI_EVENT_DATA ndisTapiEventData; ndisTapiEventData = ioBuffer; NtStatus = STATUS_SUCCESS; InfoSize = 0; // // Sync event buf access by acquiring SpinLock // KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); do { if ((inputBufferLength < sizeof (NDISTAPI_EVENT_DATA)) || (outputBufferLength < sizeof(NDISTAPI_EVENT_DATA)) || ((outputBufferLength - FIELD_OFFSET(NDISTAPI_EVENT_DATA, Data[0])) < ndisTapiEventData->ulTotalSize)) { NtStatus = STATUS_BUFFER_TOO_SMALL; break; } if (DeviceExtension->Status != NDISTAPI_STATUS_CONNECTED) { DBGOUT((3, "DoGetProviderEventsWork: Status!=NDIS_STATUS_CONNECTED!")); NtStatus = STATUS_UNSUCCESSFUL; break; } if (DeviceExtension->EventsRequestIrp != NULL) { #if DBG DbgPrint("NDISTAPI: Attempt to set duplicate EventIrp o:%p, d:%p\n", DeviceExtension->EventsRequestIrp, Irp); #endif NtStatus = STATUS_UNSUCCESSFUL; break; } // // Inspect DeviceExtension to see if there's any data available // if (DeviceExtension->EventCount == 0) { // // Hold the request pending. It remains in the cancelable // state. When new line event input is received // (NdisTapiIndicateStatus) or generated (i.e. // LINEDEVSTATE_REINIT) the data will get copied & the // request completed. // ASSERT(DeviceExtension->EventsRequestIrp == NULL); DeviceExtension->EventsRequestIrp = Irp; IoMarkIrpPending(Irp); Irp->IoStatus.Status = STATUS_PENDING; IoSetCancelRoutine (Irp, NdisTapiCancel); KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); DBGOUT((3, "DoGetProviderEventsWork: Pending Irp=%p", Irp)); return(STATUS_PENDING); } // // There's line event data queued in our ring buffer. Grab as // much as we can & complete the request. // ndisTapiEventData->ulUsedSize = GetLineEvents(ndisTapiEventData->Data, ndisTapiEventData->ulTotalSize); InfoSize = ndisTapiEventData->ulUsedSize + sizeof(NDISTAPI_EVENT_DATA) - 1; DBGOUT((3, "GetLineEvents: SyncComplete Irp=%p", Irp)); } while (FALSE); KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); Irp->IoStatus.Status = NtStatus; Irp->IoStatus.Information = InfoSize; return (NtStatus); } NTSTATUS DoLineCreateWork( PIRP Irp, PVOID ioBuffer, ULONG inputBufferLength, ULONG outputBufferLength ) { KIRQL oldIrql; ULONG InfoSize; NTSTATUS NtStatus; PPROVIDER_INFO provider; PNDISTAPI_CREATE_INFO CreateInfo; InfoSize = 0; NtStatus = STATUS_SUCCESS; KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); do { if (inputBufferLength < sizeof(CreateInfo)) { DBGOUT ((3, "IOCTL_NDISTAPI_CREATE: buffer too small")); NtStatus = STATUS_BUFFER_TOO_SMALL; break; } if (DeviceExtension->Status != NDISTAPI_STATUS_CONNECTED) { DBGOUT((3, "IOCTL_NDISTAPI_CREATE: while unconnected, returning err")); NtStatus = STATUS_UNSUCCESSFUL; break; } CreateInfo = (PNDISTAPI_CREATE_INFO)ioBuffer; provider = DeviceExtension->Providers; while (provider != NULL) { if (provider->TempID == CreateInfo->TempID) { break; } provider = provider->Next; } if (provider == NULL) { DBGOUT((0, "IOCTL_NDISTAPI_CREATE: Provider not found %x", CreateInfo->TempID)); NtStatus = STATUS_UNSUCCESSFUL; break; } if (provider->Status == PROVIDER_STATUS_OFFLINE) { DBGOUT((0, "IOCTL_CREATE - Provider %p invalid state %x", provider, provider->Status)); NtStatus = STATUS_UNSUCCESSFUL; break; } DBGOUT((1, "IOCTL_NDISTAPI_CREATE: provider %p ID %d", provider, CreateInfo->DeviceID)); if (provider->CreateCount == 0) { NDIS_STATUS ndisStatus; // // Set the base ID // provider->DeviceIDBase = CreateInfo->DeviceID; // // Init the provider // KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); ndisStatus = SendProviderInitRequest (provider); if (ndisStatus == NDIS_STATUS_PENDING) { // // Wait for completion routine to get called // KeWaitForSingleObject (&provider->SyncEvent, Executive, KernelMode, FALSE, (PTIME) NULL); } KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); } ASSERT(CreateInfo->DeviceID == (provider->DeviceIDBase + provider->CreateCount)); provider->CreateCount++; ASSERT(provider->CreateCount <= provider->NumDevices); if (provider->CreateCount == provider->NumDevices) { // // We have finished all of the line_creates for this // provider so find the next provider that needs to be // kick started. // provider = provider->Next; while (provider != NULL) { if (provider->Status == PROVIDER_STATUS_PENDING_LINE_CREATE) { break; } provider = provider->Next; } } if (provider != NULL) { NDIS_TAPI_EVENT NdisTapiEvent; // // Do a LINE_CREATE for all additional devices // on this provider // RtlZeroMemory(&NdisTapiEvent, sizeof(NDIS_TAPI_EVENT)); provider->TempID = (ULONG_PTR)provider; DBGOUT(( -1, "LINE_CREATE %d for provider %p", provider->CreateCount, provider->TempID )); NdisTapiEvent.ulMsg = LINE_CREATE; NdisTapiEvent.ulParam1 = 0; NdisTapiEvent.ulParam2 = provider->TempID; NdisTapiEvent.ulParam3 = 0; KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); NdisTapiIndicateStatus((ULONG_PTR) provider, &NdisTapiEvent, sizeof (NDIS_TAPI_EVENT)); KeAcquireSpinLock (&DeviceExtension->SpinLock, &oldIrql); } else { DeviceExtension->Flags &= ~PENDING_LINECREATE; } InfoSize = sizeof(NDISTAPI_CREATE_INFO); } while (FALSE); KeReleaseSpinLock (&DeviceExtension->SpinLock, oldIrql); Irp->IoStatus.Status = NtStatus; Irp->IoStatus.Information = InfoSize; return (NtStatus); }