/*! \brief DPC function for releasing cbmiec_wait_for_listener() \param Dpc Pointer to the KDPC \param Fdo Pointer to the FDO \param Irp Pointer to the Irp. UNUSED. \param Context Caller-supplied Context. UNUSED. \param Result Pointer to the variable which will hold the return value. After return, it will contain 1 if there was an EOI, 0 otherwise. \return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it returns one of the error status values. */ VOID cbmiec_dpc(IN PKDPC Dpc, IN PDEVICE_OBJECT Fdo, IN PIRP Irp, IN PVOID Context) { PDEVICE_EXTENSION pdx; FUNC_ENTER(); UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(Irp); UNREFERENCED_PARAMETER(Context); DBG_DPC((DBG_PREFIX "DPC called")); pdx = Fdo->DeviceExtension; // Set the event that we are ready to quit cbmiec_wait_for_listener() DBG_IRQL( <= DISPATCH_LEVEL); KeSetEvent(&pdx->EventWaitForListener, IO_SOUND_INCREMENT, FALSE); FUNC_LEAVE(); }
/*! \brief Cancel routine if we are waiting This function is the cancel routine while a IRP is in the cbmiec_wait_for_listener() function. \param Fdo Pointer to the Fdo \param Irp Pointer to the IRP which is to be cancelled. This function does not cancel the IRP itself, but it only releases cbmiec_wait_for_listener(). */ static VOID WaitCancelRoutine(IN PDEVICE_OBJECT Fdo, IN PIRP Irp) { PDEVICE_EXTENSION pdx; FUNC_ENTER(); pdx = Fdo->DeviceExtension; // We do not need the cancel spin lock anymore DBG_DPC((DBG_PREFIX "Cancelling IRP 0x%p", Irp)); DBG_IRQL( == DISPATCH_LEVEL); IoReleaseCancelSpinLock(Irp->CancelIrql); // Just release cbmiec_wait_for_listener(), but do NOT cancel the IRP! // This is left for cbmiec_wait_for_listener() as an exercise... DBG_IRQL( <= DISPATCH_LEVEL); KeSetEvent(&pdx->EventWaitForListener, IO_NO_INCREMENT, FALSE); FUNC_LEAVE(); }
/*! \brief Wait until listener is ready to receive This function waits until a listener is ready. \param Pdx Pointer to the device extension. \param SendEoi TRUE if we want to signal an EOI. FALSE otherwise. */ VOID cbmiec_wait_for_listener(IN PDEVICE_EXTENSION Pdx, IN BOOLEAN SendEoi) { ULONG NumberOfAcks = SendEoi ? 2 : 1; FUNC_ENTER(); PERF_EVENT_VERBOSE(0x1100, NumberOfAcks); // This function has two incarnations. The first one // is used if we have successfully allocated the interrupt. // In this case, we just wait until the ISR has done the // essential work // When entering this function, DATA_IN should not be active DBG_ASSERT(CBMIEC_GET(PP_DATA_IN)); if (Pdx->ParallelPortAllocatedInterrupt) { LONG ret; // This is implementation 1. It needs a working // ISR. The main work is done there // Tell the ISR how many interrupts to wait for PERF_EVENT_VERBOSE(0x1101, NumberOfAcks); ret = InterlockedExchange(&Pdx->IrqCount, NumberOfAcks); DBG_ASSERT(ret==0); PERF_EVENT_VERBOSE(0x1102, ret); // in the sequel, allow interrupts to occur DBG_IRQ(("Allow Interrupts")); CBMIEC_SET(PP_LP_IRQ); /*! \todo Shouldn't we make sure that there * is no spurious interrupt until now? */ // Give the LISTENer the sign: We want to send something DBG_IRQ(("Release CLK_OUT")); CBMIEC_RELEASE(PP_CLK_OUT); #ifdef USE_DPC // set the cancel routine which will wake us up if we do not get // an IRQ, and a cancellation is requested PERF_EVENT_VERBOSE(0x1103, 0); DBG_VERIFY(IoSetCancelRoutine(Pdx->IrpQueue.CurrentIrp, WaitCancelRoutine) DBGDO(== NULL)); // Now, wait until we have been signalled PERF_EVENT_VERBOSE(0x1104, 0); DBG_DPC((DBG_PREFIX "CALL KeWaitForSingleObject()")); KeWaitForSingleObject(&Pdx->EventWaitForListener, Executive, KernelMode, FALSE, NULL); DBG_DPC((DBG_PREFIX "RETURN from KeWaitForSingleObject()")); PERF_EVENT_VERBOSE(0x1105, 0); // we do not need the cancel routine anymore: if (IoSetCancelRoutine(Pdx->IrpQueue.CurrentIrp, NULL) == NULL) { PERF_EVENT_VERBOSE(0x1106, -1); // the cancel routine was called! // Make sure the IrqCount is resetted to zero. InterlockedExchange(&Pdx->IrqCount, 0); } #else // Wait until the listener has told us that it is able to listen while (!QueueShouldCancelCurrentIrp(&Pdx->IrpQueue) && Pdx->IrqCount) { cbmiec_schedule_timeout(libiec_global_timeouts.T_WaitForListener_Granu_T_H); } #endif DBG_IRQ(("IrqCount = 0")); // from here on, no interrupts will be generated anymore CBMIEC_RELEASE(PP_LP_IRQ); DBG_IRQ(("No more Interrupts")); }