1 /*++
2
3 Copyright (c) Microsoft Corporation
4
5 Module Name:
6
7 InterruptObject.cpp
8
9 Abstract:
10
11 This module implements a frameworks managed interrupt object
12
13 Author:
14
15
16
17
18 Environment:
19
20 Kernel mode only
21
22 Revision History:
23
24
25
26
27
28
29 --*/
30
31 #include "pnppriv.hpp"
32
33 // Tracing support
34 // #include "InterruptObjectKm.tmh"
35
36 _Must_inspect_result_
37 NTSTATUS
InitializeInternal(__in FxObject * Parent,__in PWDF_INTERRUPT_CONFIG Configuration)38 FxInterrupt::InitializeInternal(
39 __in FxObject* Parent,
40 __in PWDF_INTERRUPT_CONFIG Configuration
41 )
42 {
43 UNREFERENCED_PARAMETER(Parent);
44 UNREFERENCED_PARAMETER(Configuration);
45
46 DO_NOTHING();
47
48 return STATUS_SUCCESS;
49 }
50
51 VOID
DpcHandler(__in_opt PVOID SystemArgument1,__in_opt PVOID SystemArgument2)52 FxInterrupt::DpcHandler(
53 __in_opt PVOID SystemArgument1,
54 __in_opt PVOID SystemArgument2
55 )
56 {
57 UNREFERENCED_PARAMETER(SystemArgument1);
58 UNREFERENCED_PARAMETER(SystemArgument2);
59
60 ASSERT(m_EvtInterruptDpc != NULL);
61
62 FX_TRACK_DRIVER(GetDriverGlobals());
63
64 //
65 // Call the drivers registered DpcForIsr event callback
66 //
67 if (m_CallbackLock != NULL) {
68 KIRQL irql = 0;
69
70 m_CallbackLock->Lock(&irql);
71 m_EvtInterruptDpc(GetHandle(), m_Device->GetHandle());
72 m_CallbackLock->Unlock(irql);
73 }
74 else {
75 m_EvtInterruptDpc(GetHandle(), m_Device->GetHandle());
76 }
77
78 return;
79 }
80
81 BOOLEAN
QueueDpcForIsr(VOID)82 FxInterrupt::QueueDpcForIsr(
83 VOID
84 )
85 {
86 BOOLEAN queued;
87
88 //
89 // Using this function is optional,
90 // but the caller better have registered a handler
91 //
92 ASSERT(m_EvtInterruptDpc != NULL);
93
94 queued = KeInsertQueueDpc(&m_Dpc, this, NULL);
95
96 return queued;
97 }
98
99 VOID
WorkItemHandler(VOID)100 FxInterrupt::WorkItemHandler(
101 VOID
102 )
103 {
104 ASSERT(m_EvtInterruptWorkItem != NULL );
105
106 FX_TRACK_DRIVER(GetDriverGlobals());
107
108 //
109 // Call the drivers registered WorkItemForIsr event callback
110 //
111 if (m_CallbackLock != NULL) {
112 KIRQL irql = 0;
113
114 m_CallbackLock->Lock(&irql);
115
116 FxPerfTraceWorkItem(&m_EvtInterruptWorkItem);
117 m_EvtInterruptWorkItem(GetHandle(), m_Device->GetHandle());
118 m_CallbackLock->Unlock(irql);
119 }
120 else {
121 FxPerfTraceWorkItem(&m_EvtInterruptWorkItem);
122 m_EvtInterruptWorkItem(GetHandle(), m_Device->GetHandle());
123 }
124
125 return;
126 }
127
128 _Must_inspect_result_
129 NTSTATUS
ConnectInternal(VOID)130 FxInterrupt::ConnectInternal(
131 VOID
132 )
133 {
134 IO_CONNECT_INTERRUPT_PARAMETERS connectParams;
135 FxPkgPnp* fxPkgPnp;
136
137 fxPkgPnp = m_Device->m_PkgPnp;
138
139 //
140 // Tell the PnP Manager to connect the interrupt.
141 //
142 ASSERT(fxPkgPnp->m_IoConnectInterruptEx != NULL);
143
144 //
145 // We're running on Longhorn or later (or somebody backported the new
146 // interrupt code,) so tell the PnP manager everything we can about this
147 // device.
148 //
149 RtlZeroMemory(&connectParams, sizeof(connectParams));
150
151 if (FxIsProcessorGroupSupported()) {
152 connectParams.Version = CONNECT_FULLY_SPECIFIED_GROUP;
153 }
154 else {
155 connectParams.Version = CONNECT_FULLY_SPECIFIED;
156 }
157
158 connectParams.FullySpecified.PhysicalDeviceObject = m_Device->GetPhysicalDevice();
159 connectParams.FullySpecified.InterruptObject = &m_Interrupt;
160 connectParams.FullySpecified.ServiceRoutine = _InterruptThunk;
161 connectParams.FullySpecified.ServiceContext = this;
162 connectParams.FullySpecified.SpinLock = m_SpinLock;
163 connectParams.FullySpecified.FloatingSave = m_FloatingSave;
164 connectParams.FullySpecified.Vector = m_InterruptInfo.Vector;
165 connectParams.FullySpecified.Irql = m_InterruptInfo.Irql;
166 connectParams.FullySpecified.ProcessorEnableMask = m_InterruptInfo.TargetProcessorSet;
167 connectParams.FullySpecified.Group = m_InterruptInfo.Group;
168 connectParams.FullySpecified.InterruptMode = m_InterruptInfo.Mode;
169 connectParams.FullySpecified.ShareVector =
170 m_InterruptInfo.ShareDisposition == CmResourceShareShared ? TRUE : FALSE;
171 connectParams.FullySpecified.SynchronizeIrql = m_SynchronizeIrql;
172
173 return fxPkgPnp->m_IoConnectInterruptEx(&connectParams);
174 }
175
176 VOID
DisconnectInternal(VOID)177 FxInterrupt::DisconnectInternal(
178 VOID
179 )
180 {
181 IO_DISCONNECT_INTERRUPT_PARAMETERS params;
182 PKINTERRUPT interruptObject;
183 FxPkgPnp* fxPkgPnp;
184
185 fxPkgPnp = m_Device->m_PkgPnp;
186
187 //
188 // Now null these pointers so that we can catch anyone trying to use them
189 // erroneously.
190 //
191 interruptObject = m_Interrupt;
192 m_Interrupt = NULL;
193
194 //
195 // Disconnect the interrupt.
196 //
197 ASSERT(fxPkgPnp->m_IoDisconnectInterruptEx != NULL);
198
199 RtlZeroMemory(¶ms, sizeof(params));
200
201 if (FxIsProcessorGroupSupported()) {
202 params.Version = CONNECT_FULLY_SPECIFIED_GROUP;
203 }
204 else {
205 params.Version = CONNECT_FULLY_SPECIFIED;
206 }
207
208 params.ConnectionContext.InterruptObject = interruptObject;
209
210 fxPkgPnp->m_IoDisconnectInterruptEx(¶ms);
211
212 return;
213 }
214
215 VOID
FilterResourceRequirements(__inout PIO_RESOURCE_DESCRIPTOR IoResourceDescriptor)216 FxInterrupt::FilterResourceRequirements(
217 __inout PIO_RESOURCE_DESCRIPTOR IoResourceDescriptor
218 )
219 /*++
220
221 Routine Description:
222
223 This function allows an interrupt object to change the
224 IoResourceRequirementsList that the PnP Manager sends during
225 IRP_MN_FILTER_RESOURCE_REQUIREMENTS. This function takes a single
226 IoResourceDescriptor and applies default or user specified policy.
227
228 Arguments:
229
230 IoResourceDescriptor - Pointer to descriptor that matches this interrupt object
231
232 Return Value:
233
234 VOID
235
236 --*/
237 {
238 //
239 // Set sharing policy.
240 //
241 switch (m_ShareVector) {
242 case WdfTrue:
243 //
244 // Override the bus driver's value, explicitly sharing this interrupt.
245 //
246 IoResourceDescriptor->ShareDisposition = CmResourceShareShared;
247 break;
248
249 case WdfFalse:
250 //
251 // Override the bus driver's value, explicitly claiming this interrupt
252 // as non-shared.
253 //
254 IoResourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
255 break;
256
257 case WdfUseDefault:
258 default:
259 //
260 // Leave the bus driver's value alone.
261 //
262 break;
263 }
264
265 //
266 // Apply policy. Only do this if we are running on an OS which supports
267 // the notion of Interrupt Policy and if the policy is not already included
268 // by the bus driver based on registry settings.
269 //
270 if (FxLibraryGlobals.IoConnectInterruptEx != NULL &&
271 m_SetPolicy &&
272 (IoResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_POLICY_INCLUDED) == 0x0) {
273
274 IoResourceDescriptor->Flags |= CM_RESOURCE_INTERRUPT_POLICY_INCLUDED;
275 IoResourceDescriptor->u.Interrupt.AffinityPolicy = (IRQ_DEVICE_POLICY)m_Policy;
276 IoResourceDescriptor->u.Interrupt.PriorityPolicy = (IRQ_PRIORITY)m_Priority;
277 IoResourceDescriptor->u.Interrupt.TargetedProcessors = m_Processors.Mask;
278 IoResourceDescriptor->u.Interrupt.Group = m_Processors.Group;
279 }
280 }
281
282 VOID
FlushQueuedDpcs(VOID)283 FxInterrupt::FlushQueuedDpcs(
284 VOID
285 )
286 {
287 KeFlushQueuedDpcs();
288 }
289
290 BOOLEAN
_InterruptThunk(__in struct _KINTERRUPT * Interrupt,__in PVOID ServiceContext)291 FxInterrupt::_InterruptThunk(
292 __in struct _KINTERRUPT *Interrupt,
293 __in PVOID ServiceContext
294 )
295
296 /*++
297
298 Routine Description:
299
300 This is the C routine called by the kernels INTERRUPT handler
301
302 Arguments:
303
304 Return Value:
305
306 --*/
307
308 {
309 FxInterrupt* interrupt;
310 BOOLEAN result;
311
312 UNREFERENCED_PARAMETER(Interrupt);
313
314 interrupt = (FxInterrupt*)ServiceContext;
315 ASSERT( interrupt->m_EvtInterruptIsr != NULL );
316
317 if (interrupt->m_IsEdgeTriggeredNonMsiInterrupt == TRUE) {
318 //
319 // If KMDF is in the process of disconnecting this interrupt, discard it.
320 //
321 if (interrupt->m_Disconnecting == TRUE) {
322 return FALSE;
323 }
324
325 //
326 // Captures the interrupt object as a backup in case interrupts start
327 // to arrive before IoConnectInterruptEx sets FxInterrupt.m_Interrupt.
328 //
329 interrupt->m_InterruptCaptured = Interrupt;
330 }
331 //
332 // If the interrupt is not connected, treat this as spurious interrupt.
333 //
334 else if (NULL == interrupt->m_Interrupt) {
335 return FALSE;
336 }
337
338 if (interrupt->IsWakeCapable()) {
339 //
340 // if it is a wake capable interrupt, we will hand it off
341 // to the state machine so that it can power up the device
342 // if required and then invoke the ISR callback
343 //
344 ASSERT(interrupt->m_PassiveHandling);
345 FxPerfTracePassiveInterrupt(&interrupt->m_EvtInterruptIsr);
346 return interrupt->WakeInterruptIsr();
347 }
348
349 if (interrupt->m_PassiveHandling) {
350 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
351
352 //
353 // Acquire our internal passive-lock after the kernel acquired its own
354 // passive-lock and before invoking the callback.
355 //
356 interrupt->AcquireLock();
357
358 FxPerfTracePassiveInterrupt(&interrupt->m_EvtInterruptIsr);
359
360 result = interrupt->m_EvtInterruptIsr(
361 interrupt->GetHandle(),
362 interrupt->m_InterruptInfo.MessageNumber);
363 interrupt->ReleaseLock();
364 }
365 else {
366
367 FxPerfTraceInterrupt(&interrupt->m_EvtInterruptIsr);
368
369 result = interrupt->m_EvtInterruptIsr(
370 interrupt->GetHandle(),
371 interrupt->m_InterruptInfo.MessageNumber);
372 }
373
374 return result;
375 }
376
377 VOID
_InterruptDpcThunk(__in PKDPC Dpc,__in_opt PVOID DeferredContext,__in_opt PVOID SystemArgument1,__in_opt PVOID SystemArgument2)378 FxInterrupt::_InterruptDpcThunk(
379 __in PKDPC Dpc,
380 __in_opt PVOID DeferredContext,
381 __in_opt PVOID SystemArgument1,
382 __in_opt PVOID SystemArgument2
383 )
384 /*++
385
386 Routine Description:
387
388 Thunk used to invoke EvtInterruptDpc at DPC-level, or to queue a work-item
389 for invoking EvtInterruptWorkItem at passive-level.
390
391 --*/
392 {
393 FxInterrupt* interrupt;
394
395 UNREFERENCED_PARAMETER(Dpc);
396
397 ASSERT(DeferredContext != NULL);
398 interrupt = (FxInterrupt*) DeferredContext;
399
400 if (interrupt->m_SystemWorkItem == NULL) {
401
402 FxPerfTraceDpc(&interrupt->m_EvtInterruptDpc);
403
404 interrupt->DpcHandler(SystemArgument1, SystemArgument2);
405 }
406 else {
407 interrupt->m_SystemWorkItem->TryToEnqueue(_InterruptWorkItemCallback,
408 interrupt);
409 }
410 }
411
412 VOID
FlushAndRundownInternal(VOID)413 FxInterrupt::FlushAndRundownInternal(
414 VOID
415 )
416 {
417 //
418 // Rundown the workitem.
419 //
420 if (m_SystemWorkItem != NULL) {
421 m_SystemWorkItem->DeleteObject();
422 m_SystemWorkItem = NULL;
423 }
424
425 //
426 // If present, delete the default passive-lock.
427 //
428 if (m_DisposeWaitLock) {
429 ASSERT(m_WaitLock != NULL);
430 m_WaitLock->DeleteObject();
431 m_WaitLock = NULL;
432 m_DisposeWaitLock = FALSE;
433 }
434 }
435
436 BOOLEAN
_InterruptMarkDisconnecting(_In_opt_ PVOID SyncContext)437 FxInterrupt::_InterruptMarkDisconnecting(
438 _In_opt_ PVOID SyncContext
439 )
440 {
441 FxInterrupt* pFxInterrupt;
442
443 ASSERT(SyncContext != NULL);
444 pFxInterrupt = (FxInterrupt*)SyncContext;
445
446 //
447 // This callback is invoked only if m_IsEdgeTriggeredNonMsiInterrupt
448 // is TRUE. This will cause _InterruptThunk to discard subsequent
449 // interrupts until m_Disconnecting is reset to FALSE.
450 //
451 pFxInterrupt->m_Disconnecting = TRUE;
452
453 return TRUE;
454 }
455
456 VOID
ReportActive(_In_ BOOLEAN Internal)457 FxInterrupt::ReportActive(
458 _In_ BOOLEAN Internal
459 )
460 {
461 IO_REPORT_INTERRUPT_ACTIVE_STATE_PARAMETERS parameters;
462 FxPkgPnp* fxPkgPnp;
463
464 fxPkgPnp = m_Device->m_PkgPnp;
465
466 if (Internal == FALSE) {
467 //
468 // if the interrupt is not connected, you can't report active or inactive
469 //
470 if(m_Connected == FALSE || m_Interrupt == NULL) {
471 DoTraceLevelMessage(
472 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
473 "Driver is reporting WDFINTERRUPT %p as being active even though"
474 " it is not connected.", GetHandle());
475 FxVerifierDbgBreakPoint(GetDriverGlobals());
476 return;
477 }
478
479 if (fxPkgPnp->m_IoReportInterruptActive == NULL) {
480 DoTraceLevelMessage(
481 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
482 "Driver is calling DDI WdfInterruptReportActive() on an OS that "
483 "doesn't support the DDI.");
484 FxVerifierDbgBreakPoint(GetDriverGlobals());
485 return;
486 }
487 }
488
489 //
490 // No need to report active if interrupt is already active
491 //
492 if (m_Active) {
493 return;
494 }
495
496 RtlZeroMemory(¶meters, sizeof(parameters));
497
498 if (FxIsProcessorGroupSupported()) {
499 parameters.Version = CONNECT_FULLY_SPECIFIED_GROUP;
500 }
501 else {
502 parameters.Version = CONNECT_FULLY_SPECIFIED;
503 }
504
505 parameters.ConnectionContext.InterruptObject = m_Interrupt;
506
507 fxPkgPnp->m_IoReportInterruptActive(¶meters);
508 m_Active = TRUE;
509
510 return;
511 }
512
513 VOID
ReportInactive(_In_ BOOLEAN Internal)514 FxInterrupt::ReportInactive(
515 _In_ BOOLEAN Internal
516 )
517 {
518 IO_REPORT_INTERRUPT_ACTIVE_STATE_PARAMETERS parameters;
519 FxPkgPnp* fxPkgPnp;
520
521 fxPkgPnp = m_Device->m_PkgPnp;
522
523 if (Internal == FALSE) {
524 //
525 // if the interrupt is not connected, you can't report active or inactive
526 //
527 if(m_Connected == FALSE || m_Interrupt == NULL) {
528 DoTraceLevelMessage(
529 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
530 "Driver is reporting WDFINTERRUPT %p as being inactive even though"
531 " it is not connected.", GetHandle());
532 FxVerifierDbgBreakPoint(GetDriverGlobals());
533 return;
534 }
535
536 if (fxPkgPnp->m_IoReportInterruptInactive == NULL) {
537 DoTraceLevelMessage(
538 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
539 "Driver is calling DDI WdfInterruptReportInactive() on an OS that "
540 "doesn't support the DDI.");
541 FxVerifierDbgBreakPoint(GetDriverGlobals());
542 return;
543 }
544 }
545
546 //
547 // No need to report Inactive if interrupt is already Inactive
548 //
549 if (m_Active == FALSE) {
550 return;
551 }
552
553 RtlZeroMemory(¶meters, sizeof(parameters));
554
555 if (FxIsProcessorGroupSupported()) {
556 parameters.Version = CONNECT_FULLY_SPECIFIED_GROUP;
557 }
558 else {
559 parameters.Version = CONNECT_FULLY_SPECIFIED;
560 }
561
562 parameters.ConnectionContext.InterruptObject = m_Interrupt;
563
564 fxPkgPnp->m_IoReportInterruptInactive(¶meters);
565 m_Active = FALSE;
566
567 return;
568 }
569
570