//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1998 - 1999 // // File: thread.c // //-------------------------------------------------------------------------- // // This file contains functions associated with ParClass worker threads // #include "pch.h" VOID PptPdoThread( IN PVOID Context ) /*++ Routine Description: This is the parallel thread routine. Loops performing I/O operations. Arguments: Context -- Really the extension Return Value: None --*/ { PPDO_EXTENSION pdx = Context; KIRQL OldIrql; NTSTATUS Status; LARGE_INTEGER Timeout; PIRP CurrentIrp; DD((PCE)pdx,DDT,"PptPdoThread - %s - enter\n",pdx->Location); do { Timeout = pdx->IdleTimeout; Status = KeWaitForSingleObject( &pdx->RequestSemaphore, UserRequest, KernelMode, FALSE, &Timeout ); if( Status == STATUS_TIMEOUT ) { if( pdx->P12843DL.bEventActive ) { // Dot4.sys has a worker thread blocked on this event // waiting for us to signal if the peripheral has data // available for dot4 to read. When we signal this // event dot4.sys generates a read request to retrieve // the data from the peripheral. if( ParHaveReadData( pdx ) ) { // the peripheral has data - signal dot4.sys DD((PCE)pdx,DDT,"PptPdoThread: Signaling Event [%x]\n", pdx->P12843DL.Event); KeSetEvent(pdx->P12843DL.Event, 0, FALSE); } } if( pdx->QueryNumWaiters( pdx->PortContext ) != 0 ) { // someone else is waiting on the port - give up the // port - we'll attempt to reaquire the port later // when we have a request to process ParTerminate(pdx); ParFreePort(pdx); continue; } } // endif STATUS_TIMEOUT // wait here if PnP has paused us (e.g., QUERY_STOP, STOP, QUERY_REMOVE) KeWaitForSingleObject(&pdx->PauseEvent, Executive, KernelMode, FALSE, 0); if ( pdx->TimeToTerminateThread ) { // A dispatch thread has signalled us that we should clean // up any communication with our peripheral and then // terminate self. The dispatch thread is blocked waiting // for us to terminate self. if( pdx->Connected ) { // We currently have the port acquired and have the // peripheral negotiated into an IEEE mode. Terminate // the peripheral back to Compatibility mode forward // idle and release the port. ParTerminate( pdx ); ParFreePort( pdx ); } // terminate self PsTerminateSystemThread( STATUS_SUCCESS ); } // // process the next request from the work queue - use the // Cancel SpinLock to protect the queue // IoAcquireCancelSpinLock(&OldIrql); ASSERT(!pdx->CurrentOpIrp); while (!IsListEmpty(&pdx->WorkQueue)) { // get next IRP from our list of work items PLIST_ENTRY HeadOfList; HeadOfList = RemoveHeadList(&pdx->WorkQueue); CurrentIrp = CONTAINING_RECORD(HeadOfList, IRP, Tail.Overlay.ListEntry); // we have started processing, this IRP can no longer be cancelled #pragma warning( push ) #pragma warning( disable : 4054 4055 ) IoSetCancelRoutine(CurrentIrp, NULL); #pragma warning( pop ) ASSERT(NULL == CurrentIrp->CancelRoutine); ASSERT(!CurrentIrp->Cancel); pdx->CurrentOpIrp = CurrentIrp; IoReleaseCancelSpinLock(OldIrql); // // Do the Io - PptPdoStartIo will exectute and complete the IRP: pdx->CurrentIrp // PptPdoStartIo(pdx); if( pdx->P12843DL.bEventActive ) { // Dot4.sys has a worker thread blocked on this event // waiting for us to signal if the peripheral has data // available for dot4 to read. When we signal this // event dot4.sys generates a read request to retrieve // the data from the peripheral. if( ParHaveReadData( pdx ) ) { // the peripheral has data - signal dot4.sys DD((PCE)pdx,DDT,"PptPdoThread: Signaling Eventb [%x]\n", pdx->P12843DL.Event); KeSetEvent(pdx->P12843DL.Event, 0, FALSE); } } // wait here if PnP has paused us (e.g., QUERY_STOP, STOP, QUERY_REMOVE) KeWaitForSingleObject(&pdx->PauseEvent, Executive, KernelMode, FALSE, 0); IoAcquireCancelSpinLock(&OldIrql); } IoReleaseCancelSpinLock(OldIrql); } while (TRUE); } NTSTATUS ParCreateSystemThread( PPDO_EXTENSION Pdx ) { NTSTATUS Status; HANDLE ThreadHandle; OBJECT_ATTRIBUTES objAttrib; DD((PCE)Pdx,DDT,"ParCreateSystemThread - %s - enter\n",Pdx->Location); // Start the thread - save referenced pointer to thread in our extension InitializeObjectAttributes( &objAttrib, NULL, OBJ_KERNEL_HANDLE, NULL, NULL ); Status = PsCreateSystemThread( &ThreadHandle, THREAD_ALL_ACCESS, &objAttrib, NULL, NULL, PptPdoThread, Pdx ); if (!NT_ERROR(Status)) { // We've got the thread. Now get a pointer to it. Status = ObReferenceObjectByHandle( ThreadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, &Pdx->ThreadObjectPointer, NULL ); if (NT_ERROR(Status)) { Pdx->TimeToTerminateThread = TRUE; KeReleaseSemaphore( &Pdx->RequestSemaphore, 0, 1, FALSE ); } else { // Now that we have a reference to the thread we can simply close the handle. ZwClose(ThreadHandle); } DD((PCE)Pdx,DDT,"ParCreateSystemThread - %s - SUCCESS\n",Pdx->Location); } else { DD((PCE)Pdx,DDT,"ParCreateSystemThread - %s FAIL - status = %x\n",Pdx->Location, Status); } return Status; } VOID PptPdoStartIo( IN PPDO_EXTENSION Pdx ) /*++ Routine Description: This routine starts an I/O operation for the driver and then returns Arguments: Pdx - The parallel device extension Return Value: None --*/ { PIRP Irp; PIO_STACK_LOCATION IrpSp; KIRQL CancelIrql; Irp = Pdx->CurrentOpIrp; IrpSp = IoGetCurrentIrpStackLocation(Irp); Irp->IoStatus.Information = 0; if (!Pdx->Connected && !ParAllocPort(Pdx)) { // #pragma message( "dvrh Left bad stuff in thread.c") DD((PCE)Pdx,DDE,"PptPdoStartIo - %s - threads are hosed\n",Pdx->Location); // __asm int 3 // // If the allocation didn't succeed then fail this IRP. // goto CompleteIrp; } switch (IrpSp->MajorFunction) { case IRP_MJ_WRITE: ParWriteIrp(Pdx); break; case IRP_MJ_READ: ParReadIrp(Pdx); break; default: ParDeviceIo(Pdx); break; } if (!Pdx->Connected && !Pdx->AllocatedByLockPort) { // if we're not connected in a 1284 mode, then release host port // otherwise let the watchdog timer do it. ParFreePort(Pdx); } CompleteIrp: IoAcquireCancelSpinLock(&CancelIrql); Pdx->CurrentOpIrp = NULL; IoReleaseCancelSpinLock(CancelIrql); P4CompleteRequest( Irp, Irp->IoStatus.Status, Irp->IoStatus.Information ); return; }