1 //
2 //    Copyright (C) Microsoft.  All rights reserved.
3 //
4 #ifndef _FXEVENTQUEUE_H_
5 #define _FXEVENTQUEUE_H_
6 
7 struct FxPostProcessInfo {
8     FxPostProcessInfo(
9         VOID
10         )
11     {
12         m_Event = NULL;
13         m_DeleteObject = FALSE;
14         m_SetRemovedEvent = FALSE;
15         m_FireAndForgetIrp = NULL;
16     }
17 
18     BOOLEAN
19     SomethingToDo(
20         VOID
21         )
22     {
23         return ((m_Event != NULL) || m_DeleteObject) ? TRUE : FALSE;
24     }
25 
26     VOID
27     Evaluate(
28         __inout FxPkgPnp* PkgPnp
29         );
30 
31     FxCREvent* m_Event;
32     BOOLEAN m_DeleteObject;
33     BOOLEAN m_SetRemovedEvent;
34     MdIrp m_FireAndForgetIrp;
35 };
36 
37 typedef
38 VOID
39 (*PFN_PNP_EVENT_WORKER)(
40     __in FxPkgPnp* PkgPnp,
41     __in FxPostProcessInfo* Info,
42     __in PVOID Context
43     );
44 
45 enum FxEventQueueFlags {
46     FxEventQueueFlagWorkItemQueued = 0x01,
47     FxEventQueueFlagClosed = 0x02,
48     FxEventQueueFlagDelayDeletion = 0x04,
49 };
50 
51 struct FxEventQueue : public FxStump {
52     FxEventQueue(
53         __in UCHAR QueueDepth
54         );
55 
56     _Must_inspect_result_
57     NTSTATUS
58     Initialize(
59         __in PFX_DRIVER_GLOBALS DriverGlobals
60         );
61 
62     _Acquires_lock_(this->m_QueueLock)
63     __drv_maxIRQL(DISPATCH_LEVEL)
64     __drv_setsIRQL(DISPATCH_LEVEL)
65     VOID
66     Lock(
67         __out __drv_deref(__drv_savesIRQL)
68         PKIRQL Irql
69         )
70     {
71         m_QueueLock.Acquire(Irql);
72     }
73 
74      _Releases_lock_(this->m_QueueLock)
75     __drv_requiresIRQL(DISPATCH_LEVEL)
76     VOID
77     Unlock(
78         __in __drv_restoresIRQL
79         KIRQL Irql
80         )
81     {
82         m_QueueLock.Release(Irql);
83     }
84 
85     BOOLEAN
86     IsFull(
87         VOID
88         )
89     {
90         return ((m_QueueHead + m_QueueDepth - 1) % m_QueueDepth) == (m_QueueTail % m_QueueDepth);
91     }
92 
93     BOOLEAN
94     IsEmpty(
95         VOID
96         )
97     {
98         return m_QueueHead == m_QueueTail;
99     }
100 
101     VOID
102     IncrementHead(
103         VOID
104         )
105     {
106         m_QueueHead = (m_QueueHead + 1) % m_QueueDepth;
107     }
108 
109     UCHAR
110     GetHead(
111         VOID
112         )
113     {
114         return m_QueueHead;
115     }
116 
117     UCHAR
118     InsertAtHead(
119         VOID
120         )
121     {
122         m_QueueHead = (m_QueueHead + m_QueueDepth - 1) % m_QueueDepth;
123         return m_QueueHead;
124     }
125 
126     UCHAR
127     InsertAtTail(
128         VOID
129         )
130     {
131         UCHAR index;
132 
133         // Save the index which is the current tail
134         index = m_QueueTail;
135 
136         // goto next slot
137         m_QueueTail = (m_QueueTail + 1) % m_QueueDepth;
138 
139         // return the old tail as the slot to insert at
140         return index;
141     }
142 
143     UCHAR
144     IncrementHistoryIndex(
145         VOID
146         )
147     {
148         UCHAR cur;
149 
150         cur = m_HistoryIndex;
151         m_HistoryIndex = (m_HistoryIndex + 1) % m_QueueDepth;
152 
153         return cur;
154     }
155 
156     BOOLEAN
157     IsClosedLocked(
158         VOID
159         )
160     {
161         return (m_QueueFlags & FxEventQueueFlagClosed) ? TRUE : FALSE;
162     }
163 
164     VOID
165     GetFinishedState(
166         __inout FxPostProcessInfo* Info
167         )
168     {
169         if (IsIdleLocked()) {
170             if (m_QueueFlags & FxEventQueueFlagDelayDeletion) {
171                 m_QueueFlags &= ~FxEventQueueFlagDelayDeletion;
172                 Info->m_DeleteObject = TRUE;
173             }
174 
175             if (IsClosedLocked()) {
176                 Info->m_Event = m_WorkItemFinished;
177                 m_WorkItemFinished = NULL;
178             }
179         }
180     }
181 
182     BOOLEAN
183     SetFinished(
184         __in FxCREvent* Event
185         );
186 
187     VOID
188     SetDelayedDeletion(
189         VOID
190         );
191 
192 protected:
193     VOID
194     Configure(
195         __in FxPkgPnp* Pnp,
196         __in PFN_PNP_EVENT_WORKER WorkerRoutine,
197         __in PVOID Context
198         );
199 
200     BOOLEAN
201     QueueToThreadWorker(
202         VOID
203         );
204 
205     VOID
206     EventQueueWorker(
207         VOID
208         );
209 
210     BOOLEAN
211     IsIdleLocked(
212         VOID
213         )
214     {
215         //
216         // We are idle if there is no work item queued, no work item running,
217         // and there are no events in the queue.  Since m_WorkItemQueued is
218         // cleared before we enter the state machine, we must also track the
219         // number of work items running.
220         //
221         if ((m_QueueFlags & FxEventQueueFlagWorkItemQueued) == 0x00 &&
222             m_WorkItemRunningCount == 0x0 &&
223             IsEmpty()) {
224             return TRUE;
225         }
226         else {
227             return FALSE;
228         }
229     }
230 
231 protected:
232     // index into the beginning of the circular event ring buffer
233     UCHAR m_QueueHead;
234 
235     // index into the end of the circular event ring buffer
236     UCHAR m_QueueTail;
237 
238     // circular event ring buffer size
239     UCHAR m_QueueDepth;
240 
241     UCHAR m_HistoryIndex;
242 
243     FxPkgPnp* m_PkgPnp;
244 
245     //
246     // Context that is passed back to the the state machine as
247     // part of the event worker
248     //
249     PVOID m_EventWorkerContext;
250 
251     //
252     // Lock for the queue
253     //
254     MxLock m_QueueLock;
255 
256 public:
257     FxWaitLockInternal m_StateMachineLock;
258 
259 protected:
260     PFN_PNP_EVENT_WORKER m_EventWorker;
261 
262     //
263     // Guarded by m_QueueLock.  Will be set to a valid pointer value when pnp
264     // wants to remove the device and we want to synchronize against the work
265     // item running.
266     //
267     FxCREvent* m_WorkItemFinished;
268 
269     //
270     //
271     union {
272         //
273         // See FxEventQueueFlags for values
274         //
275         UCHAR m_QueueFlags;
276 
277         struct {
278             UCHAR WorkItemQueued : 1;
279             UCHAR Closed : 1;
280             UCHAR DelayDeletion : 1;
281         } m_QueueFlagsByName;
282     };
283 
284     //
285     // Count of times the work item is running.  Since m_WorkItemQueued is
286     // cleared before we enter the state machine, we must also track the
287     // number of instances to make sure we know when we are idle or not.
288     //
289     UCHAR m_WorkItemRunningCount;
290 };
291 
292 struct FxWorkItemEventQueue : public FxEventQueue {
293     FxWorkItemEventQueue(
294         __in UCHAR QueueDepth
295         );
296 
297     ~FxWorkItemEventQueue();
298 
299     _Must_inspect_result_
300     NTSTATUS
301     Init(
302         __inout FxPkgPnp* Pnp,
303         __in PFN_PNP_EVENT_WORKER WorkerRoutine,
304         __in PVOID WorkerContext = NULL
305         );
306 
307     VOID
308     QueueToThread(
309         VOID
310         )
311     {
312         if (QueueToThreadWorker()) {
313             QueueWorkItem();
314         }
315     }
316 
317 protected:
318     VOID
319     QueueWorkItem(
320         VOID
321         );
322 
323     static
324     MX_WORKITEM_ROUTINE
325     _WorkItemCallback;
326 
327     MxWorkItem m_WorkItem;
328 };
329 
330 //
331 // struct that encapsulates posting a work item to the dedicated power thread
332 // or work item depending on the power pagable status of the stack.
333 //
334 struct FxThreadedEventQueue : public FxEventQueue {
335     FxThreadedEventQueue(
336         __in UCHAR QueueDepth
337         );
338 
339     ~FxThreadedEventQueue(
340         VOID
341         );
342 
343     _Must_inspect_result_
344     NTSTATUS
345     Init(
346         __inout FxPkgPnp* Pnp,
347         __in PFN_PNP_EVENT_WORKER WorkerRoutine,
348         __in PVOID WorkerContext = NULL
349         );
350 
351     VOID
352     QueueToThread(
353         VOID
354         )
355     {
356         if (QueueToThreadWorker()) {
357             QueueWorkItem();
358         }
359     }
360 
361 protected:
362     static
363     WORKER_THREAD_ROUTINE
364     _WorkerThreadRoutine;
365 
366     static
367     MX_WORKITEM_ROUTINE
368     _WorkItemCallback;
369 
370     VOID
371     QueueWorkItem(
372         VOID
373         );
374 
375     MxWorkItem m_WorkItem;
376 
377     // work item used to queue to the thread
378     WORK_QUEUE_ITEM     m_EventWorkQueueItem;
379 };
380 
381 #endif // _FXEVENTQUEUE_H_
382