OpenCBM
waitlistener.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version
5  * 2 of the License, or (at your option) any later version.
6  *
7  * Copyright 1999-2004 Michael Klein <michael(dot)klein(at)puffin(dot)lb(dot)shuttle(dot)de>
8  * Copyright 2001-2004 Spiro Trikaliotis
9  *
10  */
11 
22 #include <wdm.h>
23 #include "cbm_driver.h"
24 #include "i_iec.h"
25 
26 #ifdef USE_DPC
27 
42 static VOID
43 WaitCancelRoutine(IN PDEVICE_OBJECT Fdo, IN PIRP Irp)
44 {
45  PDEVICE_EXTENSION pdx;
46 
47  FUNC_ENTER();
48 
49  pdx = Fdo->DeviceExtension;
50 
51  // We do not need the cancel spin lock anymore
52 
53  DBG_DPC((DBG_PREFIX "Cancelling IRP 0x%p", Irp));
54  DBG_IRQL( == DISPATCH_LEVEL);
55  IoReleaseCancelSpinLock(Irp->CancelIrql);
56 
57  // Just release cbmiec_wait_for_listener(), but do NOT cancel the IRP!
58  // This is left for cbmiec_wait_for_listener() as an exercise...
59 
60  DBG_IRQL( <= DISPATCH_LEVEL);
61  KeSetEvent(&pdx->EventWaitForListener, IO_NO_INCREMENT, FALSE);
62 
63  FUNC_LEAVE();
64 }
65 
66 #endif // #ifdef USE_DPC
67 
79 VOID
80 cbmiec_wait_for_listener(IN PDEVICE_EXTENSION Pdx, IN BOOLEAN SendEoi)
81 {
82  ULONG NumberOfAcks = SendEoi ? 2 : 1;
83 
84  FUNC_ENTER();
85 
86  PERF_EVENT_VERBOSE(0x1100, NumberOfAcks);
87 
88  // This function has two incarnations. The first one
89  // is used if we have successfully allocated the interrupt.
90  // In this case, we just wait until the ISR has done the
91  // essential work
92 
93  // When entering this function, DATA_IN should not be active
94 
96 
97  if (Pdx->ParallelPortAllocatedInterrupt)
98  {
99  LONG ret;
100 
101  // This is implementation 1. It needs a working
102  // ISR. The main work is done there
103 
104  // Tell the ISR how many interrupts to wait for
105 
106  PERF_EVENT_VERBOSE(0x1101, NumberOfAcks);
107  ret = InterlockedExchange(&Pdx->IrqCount, NumberOfAcks);
108  DBG_ASSERT(ret==0);
109  PERF_EVENT_VERBOSE(0x1102, ret);
110 
111  // in the sequel, allow interrupts to occur
112 
113  DBG_IRQ(("Allow Interrupts"));
115 
120  // Give the LISTENer the sign: We want to send something
121 
122  DBG_IRQ(("Release CLK_OUT"));
124 
125 #ifdef USE_DPC
126 
127  // set the cancel routine which will wake us up if we do not get
128  // an IRQ, and a cancellation is requested
129 
130  PERF_EVENT_VERBOSE(0x1103, 0);
131  DBG_VERIFY(IoSetCancelRoutine(Pdx->IrpQueue.CurrentIrp, WaitCancelRoutine) DBGDO(== NULL));
132 
133  // Now, wait until we have been signalled
134 
135  PERF_EVENT_VERBOSE(0x1104, 0);
136  DBG_DPC((DBG_PREFIX "CALL KeWaitForSingleObject()"));
137  KeWaitForSingleObject(&Pdx->EventWaitForListener, Executive, KernelMode, FALSE, NULL);
138  DBG_DPC((DBG_PREFIX "RETURN from KeWaitForSingleObject()"));
139 
140  PERF_EVENT_VERBOSE(0x1105, 0);
141 
142  // we do not need the cancel routine anymore:
143 
144  if (IoSetCancelRoutine(Pdx->IrpQueue.CurrentIrp, NULL) == NULL)
145  {
146  PERF_EVENT_VERBOSE(0x1106, -1);
147  // the cancel routine was called!
148 
149  // Make sure the IrqCount is resetted to zero.
150 
151  InterlockedExchange(&Pdx->IrqCount, 0);
152  }
153 
154 #else
155 
156  // Wait until the listener has told us that it is able to listen
157 
158  while (!QueueShouldCancelCurrentIrp(&Pdx->IrpQueue) && Pdx->IrqCount)
159  {
161  }
162 #endif
163 
164  DBG_IRQ(("IrqCount = 0"));
165 
166  // from here on, no interrupts will be generated anymore
167 
169  DBG_IRQ(("No more Interrupts"));
170  }
171  else
172  {
173  // This is implementation 2. We do not have a working
174  // ISR. Due to this, we have to busy wait until the LISTENer
175  // has told us that it will accept our data.
176 
177  // This solution isn't good, as we have to busy wait
178  // and the acknowledgement can take very long.
179 
180  // As we need very exact timing, don't allow anyone to
181  // disturb us
182 
187  cbmiec_block_irq(Pdx);
188 
189  // Give the LISTENer the sign: We want to send something
190 
192 
193  // Wait until the listener has told us that it is able to listen
194 
195  while (!QueueShouldCancelCurrentIrp(&Pdx->IrpQueue) && NumberOfAcks)
196  {
197  while (!QueueShouldCancelCurrentIrp(&Pdx->IrpQueue) && CBMIEC_GET(PP_DATA_IN))
198  {
199  // Wait for 1 us:
200  KeStallExecutionProcessor(1);
201  }
202 
203  if (!CBMIEC_GET(PP_DATA_IN))
204  {
205  if (--NumberOfAcks == 0)
206  {
208  DBG_SUCCESS((DBG_PREFIX "continue to send%s EOI", SendEoi ? "" : " no"));
209  }
210  else
211  {
212  while (!QueueShouldCancelCurrentIrp(&Pdx->IrpQueue) && !CBMIEC_GET(PP_DATA_IN))
213  {
214  // Wait for 1 us:
215  KeStallExecutionProcessor(1);
216  }
217  }
218  }
219  }
220 
221  // Our timing is not critical anymore, go back to the old IRQL
222 
223  cbmiec_release_irq(Pdx);
224  }
225 
226  PERF_EVENT_VERBOSE(0x1107, 0);
227  FUNC_LEAVE();
228 }
#define DBGDO(_xxx)
Definition: debug.h:409
IEC_TIMEOUTS libiec_global_timeouts
Definition: libiec/init.c:28
#define CBMIEC_GET(_line)
Definition: i_iec.h:75
#define DBG_VERIFY(_xxx)
Definition: debug.h:406
#define CBMIEC_SET(_set)
Definition: i_iec.h:64
ULONG T_WaitForListener_Granu_T_H
= 10 us: Graunularity of wait_for_listener() polls
Definition: i_iec.h:88
#define FUNC_LEAVE()
Definition: debug.h:349
BOOLEAN QueueShouldCancelCurrentIrp(PQUEUE Queue)
Should the current IRP be cancelled?
Definition: queue.c:1041
VOID cbmiec_wait_for_listener(IN PDEVICE_EXTENSION Pdx, IN BOOLEAN SendEoi)
Wait until listener is ready to receive.
Definition: waitlistener.c:80
Internal functions and definitions of the libiec library.
#define PP_LP_IRQ
Bit for allowing interrupts of the LPT.
Definition: i_iec.h:45
VOID cbmiec_schedule_timeout(IN ULONG howlong)
Schedule a timeout.
Definition: libiec/util.c:34
#define DBG_ASSERT(_xxx)
Definition: debug.h:401
#define PP_DATA_IN
The DATA IN bit.
Definition: i_iec.h:51
#define DBG_SUCCESS(_xxx)
Definition: debug.h:393
#define CBMIEC_RELEASE(_rel)
Definition: i_iec.h:66
#define PP_CLK_OUT
The CLOCK OUT bit.
Definition: i_iec.h:40
VOID cbmiec_block_irq(PDEVICE_EXTENSION Pdx)
Block all interrupts.
Definition: libiec/util.c:94
#define FUNC_ENTER()
Definition: debug.h:347
Definitions for the opencbm driver.
#define DBG_PREFIX
Definition: debug.h:320
VOID cbmiec_release_irq(PDEVICE_EXTENSION Pdx)
Release the interrupts.
Definition: libiec/util.c:118