1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxQueryInterfaceAPI.cpp
8 
9 Abstract:
10 
11     This module implements the device interface object.
12 
13 Author:
14 
15 
16 
17 
18 Environment:
19 
20     Kernel mode only
21 
22 Revision History:
23 
24 --*/
25 #include "fxsupportpch.hpp"
26 
27 extern "C" {
28 // #include "FxQueryInterfaceAPI.tmh"
29 }
30 
31 //
32 // Extern "C" the entire file
33 //
34 extern "C" {
35 _Must_inspect_result_
36 __drv_maxIRQL(PASSIVE_LEVEL)
37 NTSTATUS
38 STDCALL
39 WDFEXPORT(WdfDeviceAddQueryInterface)(
40     __in
41     PWDF_DRIVER_GLOBALS DriverGlobals,
42     __in
43     WDFDEVICE Device,
44     __in
45     PWDF_QUERY_INTERFACE_CONFIG InterfaceConfig
46     )
47 {
48     PFX_DRIVER_GLOBALS pFxDriverGlobals;
49     FxQueryInterface *pQueryInterface;
50     FxDevice *pDevice;
51     PINTERFACE pInterface;
52     NTSTATUS status;
53 
54     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
55                                    Device,
56                                    FX_TYPE_DEVICE,
57                                    (PVOID*) &pDevice,
58                                    &pFxDriverGlobals );
59 
60     pQueryInterface = NULL;
61 
62     FxPointerNotNull(pFxDriverGlobals, Device);
63     FxPointerNotNull(pFxDriverGlobals, InterfaceConfig);
64     FxPointerNotNull(pFxDriverGlobals, InterfaceConfig->InterfaceType);
65 
66     status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
67     if (!NT_SUCCESS(status)) {
68         return status;
69     }
70 
71     pInterface = InterfaceConfig->Interface;
72 
73     if (InterfaceConfig->Size != sizeof(WDF_QUERY_INTERFACE_CONFIG)) {
74         status = STATUS_INFO_LENGTH_MISMATCH;
75 
76         DoTraceLevelMessage(
77             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
78             "WDFDEVICE %p, WDF_QUERY_INTERFACE_CONFIG Size %d, expected %d, "
79             "%!STATUS!", Device, InterfaceConfig->Size,
80             sizeof(WDF_QUERY_INTERFACE_CONFIG), status);
81 
82         goto Done;
83     }
84 
85     //
86     // A pass through interface is only associated with PDOs
87     //
88     if (InterfaceConfig->SendQueryToParentStack && pDevice->IsPdo() == FALSE) {
89         status = STATUS_INVALID_PARAMETER;
90 
91         DoTraceLevelMessage(
92             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
93             "SendQueryToParentStack TRUE, but WDFDEVICE %p not a PDO, %!STATUS!",
94             Device, status);
95 
96         goto Done;
97     }
98 
99     //
100     // The only time we can have a NULL Interface is if the interface is
101     // passthrough or it is an import interface (since on import interfaces
102     // the callback must do the copying).
103     //
104     if (pInterface == NULL) {
105         if (InterfaceConfig->SendQueryToParentStack || InterfaceConfig->ImportInterface) {
106             //
107             // A NULL interface is valid for this config
108             //
109             DO_NOTHING();
110         }
111         else {
112             status = STATUS_INVALID_PARAMETER;
113 
114             DoTraceLevelMessage(
115                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
116                 "WDFDEVICE %p,  SendQueryToParentStack is FALSE and "
117                 "InterfaceConfig->ImportInterface is FALSE, %!STATUS!",
118                 Device, status);
119 
120             goto Done;
121         }
122     }
123 
124     //
125     // If it is an import interface, we need a callback so that the driver can
126     // modify the interface being returned.
127     //
128     if (InterfaceConfig->ImportInterface &&
129         InterfaceConfig->EvtDeviceProcessQueryInterfaceRequest == NULL) {
130         status = STATUS_INVALID_PARAMETER;
131 
132         DoTraceLevelMessage(
133             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
134             "WDFDEVICE %p, ImportInterface is TRUE and "
135             "EvtDeviceProcessQueryInterfaceRequest is NULL, %!STATUS!",
136             Device, status);
137 
138         goto Done;
139     }
140 
141     if (pInterface != NULL) {
142         //
143         // Make sure we are exposing the minimum size
144         //
145         if (pInterface->Size < sizeof(INTERFACE)) {
146             status = STATUS_INVALID_PARAMETER;
147 
148             DoTraceLevelMessage(
149                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
150                 "WDFDEVICE %p, Interface size %d < sizeof(INTERFACE) (%d), "
151                 "%!STATUS!", Device, pInterface->Size, sizeof(INTERFACE), status);
152 
153             goto Done;
154         }
155     }
156 
157     //
158     // Since the QI irp is only allowed to be sent at passive level and
159     // the list of FxQueryInterface's is locked by a lock which does not
160     // raise IRQL, we can allocate the structure out paged pool.
161     //
162     pQueryInterface = new (pFxDriverGlobals, PagedPool)
163         FxQueryInterface(pDevice, InterfaceConfig);
164 
165     if (pQueryInterface == NULL) {
166         status = STATUS_INSUFFICIENT_RESOURCES;
167 
168         DoTraceLevelMessage(
169             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
170             "WDFDEVICE %p, object creation failed, %!STATUS!", Device, status);
171 
172         goto Done;
173     }
174 
175     if (pInterface != NULL) {
176         //
177         // Try to allocate memory for the interface.
178         //
179         pQueryInterface->m_Interface = (PINTERFACE)
180             FxPoolAllocate(pFxDriverGlobals, PagedPool, pInterface->Size);
181 
182         if (pQueryInterface->m_Interface == NULL) {
183             status = STATUS_INSUFFICIENT_RESOURCES;
184 
185             DoTraceLevelMessage(
186                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
187                 "WDFDEVICE %p, interface allocation failed, %!STATUS!",
188                 Device, status);
189 
190             goto Done;
191         }
192 
193         RtlCopyMemory(pQueryInterface->m_Interface,
194                       pInterface,
195                       pInterface->Size);
196 
197         if (pInterface->InterfaceReference == NULL) {
198             pQueryInterface->m_Interface->InterfaceReference =
199                 FxDevice::_InterfaceReferenceNoOp;
200         }
201         if (pInterface->InterfaceDereference == NULL) {
202             pQueryInterface->m_Interface->InterfaceDereference =
203                 FxDevice::_InterfaceDereferenceNoOp;
204         }
205     }
206 
207     status = STATUS_SUCCESS;
208 
209     pDevice->m_PkgPnp->AddQueryInterface(pQueryInterface, TRUE);
210 
211 Done:
212     //
213     // Delete the query interface structure if there is an error.
214     //
215     if (!NT_SUCCESS(status) && pQueryInterface != NULL) {
216         delete pQueryInterface;
217         pQueryInterface = NULL;
218     }
219 
220     return status;
221 }
222 
223 } // extern "C"
224