1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxDmaEnablerAPI.cpp
8 
9 Abstract:
10 
11     Base for WDF DMA Enabler object APIs
12 
13 Environment:
14 
15     Kernel mode only.
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 #include "fxdmapch.hpp"
25 
26 extern "C" {
27 // #include "FxDmaEnablerAPI.tmh"
28 }
29 
30 //
31 // Extern "C" the entire file
32 //
33 extern "C" {
34 
35 _Must_inspect_result_
36 __drv_maxIRQL(PASSIVE_LEVEL)
37 NTSTATUS
38 WDFEXPORT(WdfDmaEnablerCreate)(
39     __in
40     PWDF_DRIVER_GLOBALS DriverGlobals,
41     __in
42     WDFDEVICE Device,
43     __in
44     WDF_DMA_ENABLER_CONFIG * Config,
45     __in_opt
46     WDF_OBJECT_ATTRIBUTES * Attributes,
47     __out
48     WDFDMAENABLER * DmaEnablerHandle
49     )
50 {
51     FxDmaEnabler *          pDmaEnabler;
52     FxDeviceBase *          pDevice;
53     NTSTATUS                status;
54     WDFDMAENABLER           handle;
55     PFX_DRIVER_GLOBALS      pFxDriverGlobals;
56     FxObject     *          pParent;
57     WDF_DMA_ENABLER_CONFIG  dmaConfig;
58 
59     //
60     // Validate the Device handle
61     //
62     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
63                                    Device,
64                                    FX_TYPE_DEVICE_BASE,
65                                    (PVOID *) &pDevice,
66                                    &pFxDriverGlobals);
67 
68     status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
69     if (!NT_SUCCESS(status)) {
70         return status;
71     }
72 
73     FxPointerNotNull(pFxDriverGlobals, DmaEnablerHandle);
74     FxPointerNotNull(pFxDriverGlobals, Config);
75 
76     *DmaEnablerHandle = NULL;
77 
78     status = FxValidateObjectAttributes(pFxDriverGlobals, Attributes);
79 
80     if (!NT_SUCCESS(status)) {
81         return status;
82     }
83 
84     if (Attributes != NULL && Attributes->ParentObject != NULL) {
85         FxObjectHandleGetPtr(pFxDriverGlobals,
86                              Attributes->ParentObject,
87                              FX_TYPE_OBJECT,
88                              (PVOID*)&pParent);
89 
90         if (pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,11)) {
91             FxDeviceBase * pSearchDevice;
92 
93             //
94             // If a parent object is passed-in it must be descendent of device.
95             // DmaEnabler stores device and uses it during dispose
96             // (to remove it from dmaenabler list maintained at device level),
97             // so DmaEnabler cannot outlive device.
98             //
99 
100             pSearchDevice = FxDeviceBase::_SearchForDevice(pParent, NULL);
101 
102             if (pSearchDevice == NULL) {
103                 status = STATUS_WDF_OBJECT_ATTRIBUTES_INVALID;
104 
105                 DoTraceLevelMessage(
106                     pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
107                     "Attributes->ParentObject 0x%p must have WDFDEVICE as an "
108                     "eventual ancestor, %!STATUS!",
109                     Attributes->ParentObject, status);
110 
111                 return status;
112             }
113             else if (pSearchDevice != pDevice) {
114                 status = STATUS_WDF_OBJECT_ATTRIBUTES_INVALID;
115 
116                 DoTraceLevelMessage(
117                     pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
118                     "Attributes->ParentObject 0x%p ancestor is WDFDEVICE %p, but "
119                     "not the same WDFDEVICE 0x%p passed to WdfDmaEnablerCreate, "
120                     "%!STATUS!",
121                     Attributes->ParentObject, pSearchDevice->GetHandle(),
122                     Device, status);
123 
124                 return status;
125             }
126         }
127         else {
128             //
129             // For < 1.11 drivers we only allow pDevice to be the parent
130             // since that is what we were blindly setting the parent to.
131             //
132             // Using the passed-in parent for such drivers could cause
133             // side-effects such as earlier deletion of DmaEnabler object. So
134             // we don't do that.
135             //
136             // We cause this verifier breakpoint to warn downlevel drivers
137             // that the parent they passed in gets ignored.
138             //
139             if (pParent != pDevice) {
140                 DoTraceLevelMessage(
141                     pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGDMA,
142                     "For drivers bound to version <= 1.9 "
143                     "WdfDmaEnablerCreate uses WDFDEVICE as the "
144                     "parent object for the dma enabler object. "
145                     "Attributes->ParentObject 0x%p, which is different from "
146                     "WDFDEVICE 0x%p, gets ignored. Please note that DmaEnabler "
147                     "would be disposed only when device is disposed.",
148                     Attributes->ParentObject, Device);
149 
150                 if (pFxDriverGlobals->IsDownlevelVerificationEnabled()) {
151                     FxVerifierDbgBreakPoint(pFxDriverGlobals);
152                 }
153             }
154 
155             pParent = pDevice;
156         }
157     }
158     else {
159         pParent = pDevice;
160     }
161 
162     {
163         ULONG expectedSize = pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,11) ?
164             sizeof(WDF_DMA_ENABLER_CONFIG) :
165             sizeof(WDF_DMA_ENABLER_CONFIG_V1_9);
166 
167         if (Config->Size != expectedSize) {
168             status = STATUS_INFO_LENGTH_MISMATCH;
169 
170             DoTraceLevelMessage(
171                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
172                 "WDF_DMA_ENABLER_CONFIG Size 0x%x, expected 0x%x, %!STATUS!",
173                 Config->Size, expectedSize, status);
174 
175             return status;
176         }
177 
178 
179         //
180         // Normalize DMA config structure if necessary.
181         //
182         if (Config->Size < sizeof(WDF_DMA_ENABLER_CONFIG)) {
183             //
184             // Init new fields to default values.
185             //
186             WDF_DMA_ENABLER_CONFIG_INIT(&dmaConfig,
187                                         Config->Profile,
188                                         Config->MaximumLength);
189             //
190             // Copy over existing fields and readjust the struct size.
191             //
192             RtlCopyMemory(&dmaConfig, Config, Config->Size);
193             dmaConfig.Size = sizeof(dmaConfig);
194 
195             //
196             // Use new config structure from now on.
197             //
198             Config = &dmaConfig;
199         }
200     }
201 
202     switch (Config->Profile) {
203         case WdfDmaProfilePacket:
204         case WdfDmaProfileScatterGather:
205         case WdfDmaProfilePacket64:
206         case WdfDmaProfileScatterGather64:
207         case WdfDmaProfileScatterGather64Duplex:
208         case WdfDmaProfileScatterGatherDuplex:
209         case WdfDmaProfileSystem:
210         case WdfDmaProfileSystemDuplex:
211             break;
212         default:
213             status = STATUS_INVALID_PARAMETER;
214             DoTraceLevelMessage(
215                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
216                 "DMA Profile value %d is unknown, %!STATUS!",
217                 Config->Profile, status);
218             return status;
219     }
220 
221     if (Config->MaximumLength == 0) {
222         status = STATUS_INVALID_PARAMETER;
223 
224         DoTraceLevelMessage(
225             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
226             "Config MaximumLength of zero is invalid, %!STATUS!",
227             status);
228 
229         return status;
230     }
231 
232     //
233     // Ok: create a new DmaEnabler
234     //
235     pDmaEnabler = new(pFxDriverGlobals, Attributes)
236         FxDmaEnabler( pFxDriverGlobals );
237 
238     if (pDmaEnabler == NULL) {
239         status = STATUS_INSUFFICIENT_RESOURCES;
240 
241         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
242                             "Could not allocate memory for a WDFDMAENABLER, "
243                             "%!STATUS!", status);
244 
245         return status;
246     }
247 
248     //
249     // Assign this FxDmaEnabler to its parent FxDevice object.
250     //
251     status = pDmaEnabler->Commit(Attributes, (WDFOBJECT*)&handle, pParent);
252 
253     if (NT_SUCCESS(status)) {
254         //
255         // Ok: start this DmaEnabler.
256         //
257         status = pDmaEnabler->Initialize( Config, pDevice );
258     }
259 
260     if (NT_SUCCESS(status)) {
261         //
262         // Only return a valid handle on success.
263         //
264         *DmaEnablerHandle = handle;
265     }
266     else {
267         pDmaEnabler->DeleteFromFailedCreate();
268     }
269 
270     return status;
271 }
272 
273 __drv_maxIRQL(DISPATCH_LEVEL)
274 size_t
275 WDFEXPORT(WdfDmaEnablerGetMaximumLength)(
276     __in
277     PWDF_DRIVER_GLOBALS DriverGlobals,
278     __in
279     WDFDMAENABLER DmaEnabler
280     )
281 {
282     FxDmaEnabler * pDmaEnabler;
283 
284     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
285                          DmaEnabler,
286                          FX_TYPE_DMA_ENABLER,
287                          (PVOID *) &pDmaEnabler);
288 
289     return pDmaEnabler->GetMaximumLength();
290 }
291 
292 __drv_maxIRQL(DISPATCH_LEVEL)
293 size_t
294 WDFEXPORT(WdfDmaEnablerGetMaximumScatterGatherElements)(
295     __in
296     PWDF_DRIVER_GLOBALS DriverGlobals,
297     __in
298     WDFDMAENABLER DmaEnabler
299     )
300 {
301     FxDmaEnabler * pDmaEnabler;
302 
303     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
304                          DmaEnabler,
305                          FX_TYPE_DMA_ENABLER,
306                          (PVOID *) &pDmaEnabler);
307 
308     return pDmaEnabler->GetMaxSGElements();
309 }
310 
311 
312 __drv_maxIRQL(PASSIVE_LEVEL)
313 VOID
314 WDFEXPORT(WdfDmaEnablerSetMaximumScatterGatherElements)(
315     __in
316     PWDF_DRIVER_GLOBALS DriverGlobals,
317     __in
318     WDFDMAENABLER DmaEnabler,
319     __in
320     __drv_when(MaximumElements == 0, __drv_reportError(MaximumElements cannot be zero))
321     size_t MaximumElements
322     )
323 {
324     PFX_DRIVER_GLOBALS pFxDriverGlobals;
325     FxDmaEnabler * pDmaEnabler;
326     NTSTATUS      status;
327 
328     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
329                                    DmaEnabler,
330                                    FX_TYPE_DMA_ENABLER,
331                                    (PVOID *) &pDmaEnabler,
332                                    &pFxDriverGlobals);
333 
334     status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
335     if (!NT_SUCCESS(status)) {
336         return;
337     }
338 
339     if (MaximumElements == 0) {
340         DoTraceLevelMessage(
341             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
342             "Cannot set MaximumElements of zero on WDFDMAENABLER %p",
343             DmaEnabler);
344         return;
345     }
346 
347     pDmaEnabler->SetMaxSGElements(MaximumElements);
348 }
349 
350 __drv_maxIRQL(DISPATCH_LEVEL)
351 size_t
352 WDFEXPORT(WdfDmaEnablerGetFragmentLength)(
353     __in
354     PWDF_DRIVER_GLOBALS DriverGlobals,
355     __in
356     WDFDMAENABLER DmaEnabler,
357     __in
358     WDF_DMA_DIRECTION   DmaDirection
359     )
360 {
361     FxDmaEnabler * pDmaEnabler;
362     size_t length;
363     PFX_DRIVER_GLOBALS pFxDriverGlobals;
364 
365     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
366                                    DmaEnabler,
367                                    FX_TYPE_DMA_ENABLER,
368                                    (PVOID *) &pDmaEnabler,
369                                    &pFxDriverGlobals);
370 
371     switch (DmaDirection) {
372 
373     case WdfDmaDirectionReadFromDevice:
374         length = pDmaEnabler->GetReadDmaDescription()->MaximumFragmentLength;
375         break;
376 
377     case WdfDmaDirectionWriteToDevice:
378         length = pDmaEnabler->GetWriteDmaDescription()->MaximumFragmentLength;
379         break;
380 
381     default:
382         length = 0;
383         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
384                             "Invalid value for Dma direction %d, %p",
385                             DmaDirection, DmaEnabler);
386         FxVerifierDbgBreakPoint(pFxDriverGlobals);
387         break;
388     }
389 
390     return length;
391 }
392 
393 __drv_maxIRQL(DISPATCH_LEVEL)
394 PDMA_ADAPTER
395 WDFEXPORT(WdfDmaEnablerWdmGetDmaAdapter)(
396     __in
397     PWDF_DRIVER_GLOBALS DriverGlobals,
398     __in
399     WDFDMAENABLER DmaEnabler,
400     __in
401     WDF_DMA_DIRECTION DmaDirection
402     )
403 {
404     PDMA_ADAPTER adapter;
405     PFX_DRIVER_GLOBALS pFxDriverGlobals;
406     FxDmaEnabler * pDmaEnabler;
407 
408     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
409                                    DmaEnabler,
410                                    FX_TYPE_DMA_ENABLER,
411                                    (PVOID *) &pDmaEnabler,
412                                    &pFxDriverGlobals);
413 
414     switch (DmaDirection) {
415 
416     case WdfDmaDirectionReadFromDevice:
417         adapter = pDmaEnabler->GetReadDmaDescription()->AdapterObject;
418         break;
419 
420     case WdfDmaDirectionWriteToDevice:
421         adapter = pDmaEnabler->GetWriteDmaDescription()->AdapterObject;
422         break;
423 
424     default:
425         adapter = NULL;
426         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
427                             "Invalid value for Dma direction %d, %p",
428                             DmaDirection, DmaEnabler);
429         FxVerifierDbgBreakPoint(pFxDriverGlobals);
430         break;
431     }
432 
433     return adapter;
434 }
435 
436 _Must_inspect_result_
437 __drv_maxIRQL(PASSIVE_LEVEL)
438 NTSTATUS
439 WDFEXPORT(WdfDmaEnablerConfigureSystemProfile)(
440     __in
441     PWDF_DRIVER_GLOBALS DriverGlobals,
442     __in
443     WDFDMAENABLER DmaEnabler,
444     __in
445     PWDF_DMA_SYSTEM_PROFILE_CONFIG ProfileConfig,
446     __in
447     WDF_DMA_DIRECTION ConfigDirection
448     )
449 {
450     PFX_DRIVER_GLOBALS pFxDriverGlobals;
451     FxDmaEnabler * pDmaEnabler;
452 
453     NTSTATUS status;
454 
455     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
456                                    DmaEnabler,
457                                    FX_TYPE_DMA_ENABLER,
458                                    (PVOID *) &pDmaEnabler,
459                                    &pFxDriverGlobals);
460 
461     //
462     // Verify the DMA config
463     //
464 
465     if (ProfileConfig == NULL)
466     {
467         status = STATUS_INVALID_PARAMETER;
468         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
469                             "ProfileConfig must be non-null, %!STATUS!",
470                             status);
471         FxVerifierDbgBreakPoint(pFxDriverGlobals);
472         return status;
473     }
474 
475     if (ProfileConfig->Size != sizeof(WDF_DMA_SYSTEM_PROFILE_CONFIG))
476     {
477         status = STATUS_INFO_LENGTH_MISMATCH;
478 
479         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
480                             "WDF_DMA_SYSTEM_PROFILE_CONFIG Size 0x%x, expected 0x%x, %!STATUS!",
481                             ProfileConfig->Size, sizeof(WDF_DMA_SYSTEM_PROFILE_CONFIG), status);
482 
483         FxVerifierDbgBreakPoint(pFxDriverGlobals);
484 
485         return status;
486     }
487 
488     if (ProfileConfig->DmaDescriptor == NULL)
489     {
490         status = STATUS_INVALID_PARAMETER;
491 
492         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
493                             "ProfileConfig (%p) may not have NULL DmaDescriptor, %!STATUS!",
494                             ProfileConfig, status);
495 
496         FxVerifierDbgBreakPoint(pFxDriverGlobals);
497 
498         return status;
499     }
500 
501     if (ConfigDirection != WdfDmaDirectionReadFromDevice &&
502         ConfigDirection != WdfDmaDirectionWriteToDevice) {
503         status = STATUS_INVALID_PARAMETER;
504         DoTraceLevelMessage(
505             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
506             "ConfigDirection 0x%x is an invalid value, %!STATUS!",
507             ConfigDirection, status);
508         return status;
509     }
510 
511     status = pDmaEnabler->ConfigureSystemAdapter(ProfileConfig,
512                                                  ConfigDirection);
513     return status;
514 }
515 
516 } // extern "C"
517