/*! \brief Write some bytes to the IEC bus \param Pdx Pointer to the device extension. \param Buffer Pointer to a buffer where the read bytes are written to. \param Size Maximum number of characters to read from the bus. \param Written Pointer to the variable which will hold the number of written bytes. \return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it returns one of the error status values. ATN is released on return of this routine */ NTSTATUS cbmiec_raw_write(IN PDEVICE_EXTENSION Pdx, IN const PUCHAR Buffer, IN ULONG Size, OUT ULONG *Written) { NTSTATUS ntStatus; #if DBG unsigned i; #endif FUNC_ENTER(); PERF_EVENT_VERBOSE(0x1000, 0); FUNC_PARAM((DBG_PREFIX "Buffer = 0x%p, Size = 0x%04x", Buffer, Size)); #if DBG for (i=0;i<Size;i++) { FUNC_PARAM((DBG_PREFIX " output %2u: 0x%02x '%c'", i, (unsigned int) Buffer[i], (UCHAR) Buffer[i])); } #endif PERF_EVENT_VERBOSE(0x1001, 0); ntStatus = cbmiec_i_raw_write(Pdx, Buffer, Size, Written, 0, 0); PERF_EVENT_VERBOSE(0x1002, 0); FUNC_LEAVE_NTSTATUS(ntStatus); }
/*! \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")); }