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