1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxDeviceBase.cpp
8 
9 Abstract:
10 
11     This is the class implementation for the base device class.
12 
13 Author:
14 
15 
16 
17 Environment:
18 
19     Both kernel and user mode
20 
21 Revision History:
22 
23 --*/
24 
25 #include "coreprivshared.hpp"
26 
27 extern "C" {
28 // #include "FxDeviceBase.tmh"
29 }
30 
FxDeviceBase(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in FxDriver * Driver,__in WDFTYPE Type,__in USHORT Size)31 FxDeviceBase::FxDeviceBase(
32     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
33     __in FxDriver* Driver,
34     __in WDFTYPE Type,
35     __in USHORT Size
36     ) :
37     FxNonPagedObject(Type, Size, FxDriverGlobals)
38 {
39     m_Driver = Driver;
40 
41     m_CallbackLockPtr = NULL;
42     m_CallbackLockObjectPtr = NULL;
43 
44     m_DisposeList = NULL;
45 
46     m_DmaPacketTransactionStatus = FxDmaPacketTransactionCompleted;
47 
48     m_ExecutionLevel = WdfExecutionLevelInheritFromParent;
49     m_SynchronizationScope = WdfSynchronizationScopeInheritFromParent;
50 
51     MarkPassiveDispose(ObjectDoNotLock);
52     SetDeviceBase(this);
53 }
54 
~FxDeviceBase(VOID)55 FxDeviceBase::~FxDeviceBase(
56     VOID
57     )
58 {
59     if (m_DisposeList != NULL) {
60         m_DisposeList->DeleteObject();
61         m_DisposeList = NULL;
62     }
63 
64     if (m_CallbackLockPtr != NULL) {
65         delete m_CallbackLockPtr;
66         m_CallbackLockPtr = NULL;
67     }
68 }
69 
70 _Must_inspect_result_
71 NTSTATUS
QueryInterface(__inout FxQueryInterfaceParams * Params)72 FxDeviceBase::QueryInterface(
73     __inout FxQueryInterfaceParams* Params
74     )
75 {
76     switch (Params->Type) {
77     case FX_TYPE_DEVICE_BASE:
78         *Params->Object = this;
79         break;
80 
81     case FX_TYPE_IHASCALLBACKS:
82         *Params->Object = (IFxHasCallbacks*) this;
83         break;
84 
85     default:
86         return FxNonPagedObject::QueryInterface(Params); // __super call
87     }
88 
89     return STATUS_SUCCESS;
90 }
91 
92 NTSTATUS
ConfigureConstraints(__in_opt PWDF_OBJECT_ATTRIBUTES ObjectAttributes)93 FxDeviceBase::ConfigureConstraints(
94     __in_opt PWDF_OBJECT_ATTRIBUTES ObjectAttributes
95     )
96 {
97     NTSTATUS            status;
98     WDF_EXECUTION_LEVEL driverLevel;
99     WDF_SYNCHRONIZATION_SCOPE driverScope;
100 
101     ASSERT(m_Driver != NULL);
102 
103     //
104     // If WDF_OBJECT_ATTRIBUTES is specified, these override any
105     // default settings.
106     //
107     if (ObjectAttributes != NULL) {
108         m_ExecutionLevel = ObjectAttributes->ExecutionLevel;
109         m_SynchronizationScope = ObjectAttributes->SynchronizationScope;
110     }
111 
112     //
113     // If no WDFDEVICE specific attributes are specified, we
114     // get them from WDFDRIVER, which allows WDFDRIVER to
115     // provide a default for all WDFDEVICE's created.
116     //
117     m_Driver->GetConstraints(&driverLevel, &driverScope);
118 
119     if (m_ExecutionLevel == WdfExecutionLevelInheritFromParent) {
120         m_ExecutionLevel = driverLevel;
121     }
122 
123     if (m_SynchronizationScope == WdfSynchronizationScopeInheritFromParent) {
124         m_SynchronizationScope = driverScope;
125     }
126 
127     //
128     // Configure The Execution Level Constraint
129     //
130     if (m_ExecutionLevel == WdfExecutionLevelPassive) {
131         m_CallbackLockPtr = new(GetDriverGlobals())
132                                 FxCallbackMutexLock(GetDriverGlobals());
133         //
134         // Currently, all event callbacks from FxDevice into the driver
135         // are from PASSIVE_LEVEL threads, and there is no need to defer
136         // to a system workitem like IoQueue. So we don't allocate and
137         // setup an FxSystemWorkItem here.
138         //
139         // If the FxDevice starts raising events to the device driver
140         // whose thread starts out above PASSIVE_LEVEL, then an FxSystemWorkItem
141         // would need to be allocated when WdfExecutionLevelPassive is specified.
142         //
143         // (FDO and PDO variants of FxDevice may own their own event dispatch
144         //  and deferral logic separate from FxDevice.)
145         //
146     }
147     else {
148         m_CallbackLockPtr  = new(GetDriverGlobals())
149                                 FxCallbackSpinLock(GetDriverGlobals());
150     }
151 
152     //
153     // Finish initializing the spin/mutex lock.
154     //
155     if (NULL == m_CallbackLockPtr) {
156         status = STATUS_INSUFFICIENT_RESOURCES;
157         DoTraceLevelMessage(
158             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGOBJECT,
159             "WDFDEVICE %p, could not allocate callback lock, %!STATUS!",
160             GetHandle(), status);
161         goto Done;
162     }
163 
164     m_CallbackLockPtr->Initialize(this);
165     m_CallbackLockObjectPtr = this;
166 
167     status = STATUS_SUCCESS;
168 
169 Done:
170     return status;
171 }
172 
173 VOID
GetConstraints(__out_opt WDF_EXECUTION_LEVEL * ExecutionLevel,__out_opt WDF_SYNCHRONIZATION_SCOPE * SynchronizationScope)174 FxDeviceBase::GetConstraints(
175     __out_opt WDF_EXECUTION_LEVEL*       ExecutionLevel,
176     __out_opt WDF_SYNCHRONIZATION_SCOPE* SynchronizationScope
177     )
178 {
179     if (ExecutionLevel != NULL) {
180         *ExecutionLevel = m_ExecutionLevel;
181     }
182 
183     if (SynchronizationScope != NULL) {
184         *SynchronizationScope = m_SynchronizationScope;
185     }
186 }
187 
188 FxCallbackLock*
GetCallbackLockPtr(__out_opt FxObject ** LockObject)189 FxDeviceBase::GetCallbackLockPtr(
190     __out_opt FxObject** LockObject
191     )
192 {
193     if (LockObject != NULL) {
194         *LockObject = m_CallbackLockObjectPtr;
195     }
196 
197     return m_CallbackLockPtr;
198 }
199 
200 
201 VOID
Init(__in MdDeviceObject DeviceObject,__in MdDeviceObject AttachedDevice,__in MdDeviceObject PhysicalDevice)202 FxDeviceBase::Init(
203     __in MdDeviceObject DeviceObject,
204     __in MdDeviceObject AttachedDevice,
205     __in MdDeviceObject PhysicalDevice
206     )
207 {
208     m_DeviceObject.SetObject(DeviceObject);
209     m_AttachedDevice.SetObject(AttachedDevice);
210     m_PhysicalDevice.SetObject(PhysicalDevice);
211 }
212 
213 FxDeviceBase*
_SearchForDevice(__in FxObject * Object,__out_opt IFxHasCallbacks ** Callbacks)214 FxDeviceBase::_SearchForDevice(
215     __in FxObject* Object,
216     __out_opt IFxHasCallbacks** Callbacks
217     )
218 {
219     FxObject* pParent, *pOrigParent;
220     FxDeviceBase* pDeviceBase;
221     FxQueryInterfaceParams cbParams = { (PVOID*) Callbacks, FX_TYPE_IHASCALLBACKS, 0 };
222     PVOID pTag;
223 
224     pDeviceBase = Object->GetDeviceBase();
225     if (pDeviceBase == NULL) {
226         DoTraceLevelMessage(
227             Object->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGOBJECT,
228             "WDFHANDLE %p does not have a WDFDEVICE as an ancestor",
229             Object->GetObjectHandle());
230         return NULL;
231     }
232 
233     if (Callbacks == NULL) {
234         //
235         // Caller is not interested in the callbacks interface, just return
236         // now without searching for it.
237         //
238         return pDeviceBase;
239     }
240 
241     //
242     // Init out parameter.
243     //
244     *Callbacks = NULL;
245 
246     pOrigParent = Object;
247     pTag = pOrigParent;
248 
249     //
250     // By adding a reference now, we simulate what GetParentObjectReferenced
251     // does later, thus allowing simple logic on when/how to release the
252     // reference on exit.
253     //
254     Object->ADDREF(pTag);
255 
256     do {
257         //
258         // If successful, Callbacks will be != NULL
259         //
260         if (NT_SUCCESS(Object->QueryInterface(&cbParams))) {
261             ASSERT(*Callbacks != NULL);
262             //
263             // Release the reference previously taken by the top of the function
264             // or GetParentObjectReferenced in a previous pass in the loop.
265             //
266             Object->RELEASE(pTag);
267             return pDeviceBase;
268         }
269 
270         pParent = Object->GetParentObjectReferenced(pTag);
271 
272         //
273         // Release the reference previously taken by the top of the function
274         // or GetParentObjectReferenced in a previous pass in the loop.
275         //
276         Object->RELEASE(pTag);
277 
278         Object = pParent;
279     } while (Object != NULL);
280 
281     ASSERT(Object == NULL);
282 
283     //
284     // Queue presented requests do not have parents (to increase performance).
285     // Try to find the callback interface on this object's device base.
286     //
287     if (NT_SUCCESS(pDeviceBase->QueryInterface(&cbParams))) {
288         ASSERT(*Callbacks != NULL);
289         //
290         // Success, we got a callback interface.
291         //
292         return pDeviceBase;
293     }
294 
295     DoTraceLevelMessage(
296         pOrigParent->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGOBJECT,
297         "WDFHANDLE %p does not have a callbacks interface in its object tree"
298         "(WDFDEVICE %p)", pOrigParent->GetObjectHandle(),
299         pDeviceBase->GetHandle());
300 
301     return pDeviceBase;
302 }
303 
304 FxDeviceBase*
_SearchForDevice(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in_opt PWDF_OBJECT_ATTRIBUTES Attributes)305 FxDeviceBase::_SearchForDevice(
306     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
307     __in_opt PWDF_OBJECT_ATTRIBUTES Attributes
308     )
309 {
310     FxObject* pParentObject;
311     FxDeviceBase* pDeviceBase;
312 
313     if (Attributes == NULL || Attributes->ParentObject == NULL) {
314         return NULL;
315     }
316 
317     FxObjectHandleGetPtr(FxDriverGlobals,
318                          Attributes->ParentObject,
319                          FX_TYPE_OBJECT,
320                          (PVOID*) &pParentObject);
321 
322     pDeviceBase = _SearchForDevice(pParentObject, NULL);
323 
324     return pDeviceBase;
325 }
326 
327 _Must_inspect_result_
328 NTSTATUS
AllocateTarget(_Out_ FxIoTarget ** Target,_In_ BOOLEAN SelfTarget)329 FxDeviceBase::AllocateTarget(
330     _Out_ FxIoTarget** Target,
331     _In_  BOOLEAN SelfTarget
332     )
333 /*++
334 
335 Routine Description:
336 
337     Allocates an IO Target or an Self IO target for the FxDevice.
338 
339 Arguments:
340 
341     Target - Out - returns the pointer to the allocated IO target.
342 
343     SelfTarget - If TRUE allocates an Self IO Target, if FALSE allocates
344         a regular IO target.
345 
346 Returns:
347 
348     NTSTATUS
349 
350 --*/
351 {
352     FxIoTarget* pTarget;
353     NTSTATUS status;
354 
355     if (SelfTarget) {
356         pTarget = (FxIoTarget*) new(GetDriverGlobals(), WDF_NO_OBJECT_ATTRIBUTES)
357             FxIoTargetSelf(GetDriverGlobals(), sizeof(FxIoTargetSelf));
358     } else {
359         pTarget = new(GetDriverGlobals(), WDF_NO_OBJECT_ATTRIBUTES)
360             FxIoTarget(GetDriverGlobals(), sizeof(FxIoTarget));
361     }
362 
363     if (pTarget == NULL) {
364         status = STATUS_INSUFFICIENT_RESOURCES;
365 
366         DoTraceLevelMessage(
367             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
368             "WDFDEVICE %p could not allocate a WDFIOTARGET, %!STATUS!",
369             GetHandle(), status);
370 
371         goto Done;
372     }
373 
374     status = AddIoTarget(pTarget);
375     if (!NT_SUCCESS(status)) {
376         DoTraceLevelMessage(
377             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
378             "WDFDEVICE %p failed to initialize (add) a WDFIOTARGET, %!STATUS!",
379             GetHandle(), status);
380 
381         goto Done;
382     }
383 
384     status = pTarget->Init(this);
385     if (!NT_SUCCESS(status)) {
386         DoTraceLevelMessage(
387             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
388             "WDFDEVICE %p failed to initialize a WDFIOTARGET, %!STATUS!",
389             GetHandle(), status);
390 
391         goto Done;
392     }
393 
394     status = pTarget->Commit(WDF_NO_OBJECT_ATTRIBUTES, NULL, this);
395     if (!NT_SUCCESS(status)) {
396         DoTraceLevelMessage(
397             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
398             "WDFDEVICE %p failed to initialize (commit) a WDFIOTARGET, %!STATUS!",
399             GetHandle(), status);
400 
401         goto Done;
402     }
403 
404     status = STATUS_SUCCESS;
405 
406 Done:
407     if (!NT_SUCCESS(status)) {
408         if (pTarget != NULL) {
409             pTarget->DeleteFromFailedCreate();
410             pTarget = NULL;
411         }
412     }
413 
414     *Target = pTarget;
415 
416     return status;
417 }
418