/*++ Copyright (c) 1995 Microsoft Corporation Module Name: request.c Abstract: Worker thread for the automatic connection driver. Author: Anthony Discolo (adiscolo) 17-Apr-1995 Environment: Kernel Mode Revision History: --*/ #include #include #include #include #include #include #include #include "acdapi.h" #include "acddefs.h" #include "mem.h" #include "debug.h" // // External declarations // VOID AcdPrintAddress( IN PACD_ADDR pAddr ); VOID ProcessCompletion( IN PACD_COMPLETION pCompletion, IN KIRQL irqlCancel, IN KIRQL irqlLock ) { PLIST_ENTRY pHead; KIRQL irql; PIRP pIrp; PIO_STACK_LOCATION pIrpSp; PACD_NOTIFICATION pNotification; ASSERT(!IsListEmpty(&AcdNotificationQueueG)); // // Complete the next irp in the // AcdNotificationQueueG queue. These // represent the ioctl completions the // system service has posted. Completing // this request will start the system service // to create a new RAS connection. // Logically, there is always just one. // pHead = RemoveHeadList(&AcdNotificationQueueG); pIrp = CONTAINING_RECORD(pHead, IRP, Tail.Overlay.ListEntry); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); // // Disable the irp's cancel routine. // IoSetCancelRoutine(pIrp, NULL); // // Copy the success flag and the address into the // system buffer. This will get copied into the // user's buffer on return. // pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer; RtlCopyMemory( pNotification, &pCompletion->notif, sizeof (ACD_NOTIFICATION)); IF_ACDDBG(ACD_DEBUG_WORKER) { AcdPrint(("AcdNotificationRequestThread: ")); AcdPrintAddress(&pCompletion->notif.addr); AcdPrint((", ulFlags=0x%x\n", pCompletion->notif.ulFlags)); } // // We can release both the cancel lock // and our lock now. // KeReleaseSpinLock(&AcdSpinLockG, irqlLock); IoReleaseCancelSpinLock(irqlCancel); // // Set the status code and the number // of bytes to be copied back to the user // buffer. // pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = sizeof (ACD_NOTIFICATION); // // Complete the irp. // IoCompleteRequest(pIrp, IO_NO_INCREMENT); } // ProcessCompletion VOID AcdNotificationRequestThread( PVOID context ) /*++ DESCRIPTION This thread handles the notification that an automatic connection may need to be initiated. This needs to happen in a separate thread, because the notification may occur at DPC irql. ARGUMENTS None. RETURN VALUE None. --*/ { KIRQL irql, irql2; PLIST_ENTRY pEntry, pEntry2; PACD_CONNECTION pConnection; PACD_COMPLETION pCompletion; BOOLEAN bStartTimer, bStopTimer; UNREFERENCED_PARAMETER(context); IoStartTimer(pAcdDeviceObjectG); for (;;) { bStartTimer = bStopTimer = FALSE; // // Acquire our lock. // IoAcquireCancelSpinLock(&irql); KeAcquireSpinLock(&AcdSpinLockG, &irql2); // // If there are no irps to complete, // then go back to sleep. // if (IsListEmpty(&AcdNotificationQueueG)) { IF_ACDDBG(ACD_DEBUG_WORKER) { AcdPrint(("AcdNotificationRequestThread: no ioctl to complete\n")); } KeReleaseSpinLock(&AcdSpinLockG, irql2); IoReleaseCancelSpinLock(irql); goto again; } // // Search for connections that haven't // been processed yet. // for (pEntry = AcdConnectionQueueG.Flink; pEntry != &AcdConnectionQueueG; pEntry = pEntry->Flink) { pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); // // Don't issue a request to the service // for more than one simultaneous connection. // IF_ACDDBG(ACD_DEBUG_WORKER) { AcdPrint(( "AcdNotificationRequestThread: pConnection=0x%x, fNotif=%d, fCompleting=%d\n", pConnection, pConnection->fNotif, pConnection->fCompleting)); } if (pConnection->fNotif) break; // // Skip all connections that are in // the process of being completed. // if (pConnection->fCompleting) continue; // // Make sure there is at least one // request in this connection that // hasn't been canceled. // for (pEntry2 = pConnection->CompletionList.Flink; pEntry2 != &pConnection->CompletionList; pEntry2 = pEntry2->Flink) { pCompletion = CONTAINING_RECORD(pEntry2, ACD_COMPLETION, ListEntry); if (!pCompletion->fCanceled) { IF_ACDDBG(ACD_DEBUG_WORKER) { AcdPrint(( "AcdNotificationRequestThread: starting pConnection=0x%x, pCompletion=0x%x\n", pConnection, pCompletion)); } pConnection->fNotif = TRUE; // // This call releases both the cancel lock // and our lock. // ProcessCompletion(pCompletion, irql, irql2); // // Start the connection timer. // bStartTimer = TRUE; // // We can only process one completion // at a time. // goto again; } } } // // Complete other requests. // if (!IsListEmpty(&AcdCompletionQueueG)) { pEntry = RemoveHeadList(&AcdCompletionQueueG); pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry); IF_ACDDBG(ACD_DEBUG_WORKER) { AcdPrint(( "AcdNotificationRequestThread: starting pCompletion=0x%x\n", pCompletion)); } // // This call releases both the cancel lock // and our lock. // ProcessCompletion(pCompletion, irql, irql2); // // We are done with the completion, // so we can free the memory now. // FREE_MEMORY(pCompletion); // // We can only process one completion // at a time. // goto again; } // // If there are no connections pending, // then stop the connection timer. // if (IsListEmpty(&AcdConnectionQueueG)) bStopTimer = TRUE; // // Release our lock. // KeReleaseSpinLock(&AcdSpinLockG, irql2); IoReleaseCancelSpinLock(irql); again: // // Start or stop the timer, depending // on what we found while we had the // spinlock. We can't hold our spin // lock when we call the Io*Timer // routines. // #ifdef notdef if (bStopTimer) IoStopTimer(pAcdDeviceObjectG); else if (bStartTimer) IoStartTimer(pAcdDeviceObjectG); #endif // // Wait for something to do. This event // will be signaled by AcdSignalNotification(). // IF_ACDDBG(ACD_DEBUG_WORKER) { AcdPrint(("AcdNotificationRequestThread: waiting on AcdPendingCompletionEventG\n")); } KeWaitForSingleObject( &AcdRequestThreadEventG, Executive, KernelMode, FALSE, NULL); KeClearEvent(&AcdRequestThreadEventG); IF_ACDDBG(ACD_DEBUG_WORKER) { AcdPrint(("AcdNotificationRequestThread: AcdPendingCompletionEventG signalled\n")); } } } // AcdNotificationRequestThread