[libbus/pnp] Add check for NULL QueryDevText member
[people/sha0/winvblock.git] / src / winvblock / libbus / pnp.c
1 /**
2  * Copyright (C) 2009-2011, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * Bus PnP IRP handling.
26  */
27
28 #include <stdio.h>
29 #include <ntddk.h>
30 #include <initguid.h>
31
32 #include "portable.h"
33 #include "winvblock.h"
34 #include "wv_stdlib.h"
35 #include "irp.h"
36 #include "bus.h"
37 #include "debug.h"
38
39 static NTSTATUS STDCALL WvlBusPnpIoCompletion(
40     IN PDEVICE_OBJECT dev_obj,
41     IN PIRP irp,
42     IN PKEVENT event
43   ) {
44     KeSetEvent(event, 0, FALSE);
45     return STATUS_MORE_PROCESSING_REQUIRED;
46   }
47
48 static NTSTATUS STDCALL WvlBusPnpStartDev(IN WVL_SP_BUS_T bus, IN PIRP irp) {
49     NTSTATUS status;
50     KEVENT event;
51     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
52
53     if (!lower)
54       return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
55     KeInitializeEvent(&event, NotificationEvent, FALSE);
56     IoCopyCurrentIrpStackLocationToNext(irp);
57     IoSetCompletionRoutine(
58         irp,
59         (PIO_COMPLETION_ROUTINE) WvlBusPnpIoCompletion,
60         (PVOID) &event,
61         TRUE,
62         TRUE,
63         TRUE
64       );
65     status = IoCallDriver(lower, irp);
66     if (status == STATUS_PENDING) {
67         DBG("Locked\n");
68         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
69       }
70     if (NT_SUCCESS(status = irp->IoStatus.Status)) {
71         bus->OldState = bus->State;
72         bus->State = WvlBusStateStarted;
73       }
74     return WvlIrpComplete(
75         irp,
76         irp->IoStatus.Information,
77         STATUS_SUCCESS
78       );
79   }
80
81 static NTSTATUS STDCALL WvlBusPnpRemoveDev(IN WVL_SP_BUS_T bus, IN PIRP irp) {
82     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
83     NTSTATUS status;
84
85     bus->OldState = bus->State;
86     bus->State = WvlBusStateDeleted;
87     /* Pass the IRP on to any lower DEVICE_OBJECT */
88     if (lower) {
89         irp->IoStatus.Information = 0;
90         irp->IoStatus.Status = STATUS_SUCCESS;
91         IoSkipCurrentIrpStackLocation(irp);
92         status = IoCallDriver(lower, irp);
93       }
94     WvlBusClear(bus);
95     /* Without a lower DEVICE_OBJECT, we have to complete the IRP. */
96     if (!lower)
97       return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
98     return status;
99   }
100
101 static NTSTATUS STDCALL WvlBusPnpQueryDevRelations(
102     IN WVL_SP_BUS_T bus,
103     IN PIRP irp
104   ) {
105     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
106     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
107     KIRQL irql;
108     PDEVICE_RELATIONS dev_relations;
109     UINT32 i;
110     PLIST_ENTRY node_link;
111
112     if (
113         io_stack_loc->Parameters.QueryDeviceRelations.Type != BusRelations ||
114         irp->IoStatus.Information
115       ) {
116         if (lower) {
117             IoSkipCurrentIrpStackLocation(irp);
118             return IoCallDriver(lower, irp);
119           }
120         return WvlIrpComplete(
121             irp,
122             irp->IoStatus.Information,
123             irp->IoStatus.Status
124           );
125       }
126     KeAcquireSpinLock(&bus->BusPrivate_.NodeLock, &irql);
127     dev_relations = wv_malloc(
128         sizeof *dev_relations +
129           (sizeof (PDEVICE_OBJECT) * bus->BusPrivate_.NodeCount)
130       );
131     if (dev_relations == NULL) {
132         /* Couldn't allocate dev_relations, but silently succeed. */
133         KeReleaseSpinLock(&bus->BusPrivate_.NodeLock, irql);
134         if (lower) {
135             IoSkipCurrentIrpStackLocation(irp);
136             return IoCallDriver(lower, irp);
137           }
138         return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
139       }
140     dev_relations->Count = bus->BusPrivate_.NodeCount;
141
142     i = 0;
143     node_link = &bus->BusPrivate_.Nodes;
144     while ((node_link = node_link->Flink) != &bus->BusPrivate_.Nodes) {
145         WVL_SP_BUS_NODE node = CONTAINING_RECORD(
146             node_link,
147             WVL_S_BUS_NODE,
148             BusPrivate_.Link
149           );
150
151         dev_relations->Objects[i] = node->BusPrivate_.Pdo;
152         ObReferenceObject(node->BusPrivate_.Pdo);
153         i++;
154       }
155     KeReleaseSpinLock(&bus->BusPrivate_.NodeLock, irql);
156     /* Assertion. */
157     if (i != dev_relations->Count) {
158         DBG("Inconsistent node list count!\n");
159         /* Pass it on or report failure. */
160         if (lower) {
161             IoSkipCurrentIrpStackLocation(irp);
162             return IoCallDriver(lower, irp);
163           }
164         return WvlIrpComplete(irp, 0, STATUS_UNSUCCESSFUL);
165       }
166     irp->IoStatus.Information = (ULONG_PTR) dev_relations;
167     irp->IoStatus.Status = STATUS_SUCCESS;
168     /* Should we pass it on? */
169     if (lower) {
170         IoSkipCurrentIrpStackLocation(irp);
171         return IoCallDriver(lower, irp);
172       }
173     /* Success. */
174     return WvlIrpComplete(irp, irp->IoStatus.Information, STATUS_SUCCESS);
175   }
176
177 static NTSTATUS STDCALL WvlBusPnpQueryCapabilities(
178     IN WVL_SP_BUS_T bus,
179     IN PIRP irp
180   ) {
181     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
182     PDEVICE_CAPABILITIES DeviceCapabilities =
183       io_stack_loc->Parameters.DeviceCapabilities.Capabilities;
184     NTSTATUS status;
185     DEVICE_CAPABILITIES ParentDeviceCapabilities;
186     PDEVICE_OBJECT lower = bus->LowerDeviceObject;;
187
188     if (DeviceCapabilities->Version != 1 ||
189         DeviceCapabilities->Size < sizeof (DEVICE_CAPABILITIES) ||
190         !lower
191       )
192       return WvlIrpComplete(irp, 0, STATUS_UNSUCCESSFUL);
193     /* Let the lower DEVICE_OBJECT handle the IRP. */
194     lower = bus->LowerDeviceObject;
195     IoSkipCurrentIrpStackLocation(irp);
196     return IoCallDriver(lower, irp);
197   }
198
199 DEFINE_GUID(
200     GUID_BUS_TYPE_INTERNAL,
201     0x2530ea73L,
202     0x086b,
203     0x11d1,
204     0xa0,
205     0x9f,
206     0x00,
207     0xc0,
208     0x4f,
209     0xc3,
210     0x40,
211     0xb1
212   );
213
214 static NTSTATUS STDCALL WvlBusPnpQueryBusInfo(
215     IN WVL_SP_BUS_T bus,
216     IN PIRP irp
217   ) {
218     PPNP_BUS_INFORMATION pnp_bus_info;
219     NTSTATUS status;
220
221     pnp_bus_info = wv_palloc(sizeof *pnp_bus_info);
222     if (pnp_bus_info == NULL) {
223         DBG("wv_palloc IRP_MN_QUERY_BUS_INFORMATION\n");
224         status = STATUS_INSUFFICIENT_RESOURCES;
225         goto alloc_pnp_bus_info;
226       }
227     pnp_bus_info->BusTypeGuid = GUID_BUS_TYPE_INTERNAL;
228     pnp_bus_info->LegacyBusType = PNPBus;
229     pnp_bus_info->BusNumber = 0;
230     irp->IoStatus.Information = (ULONG_PTR) pnp_bus_info;
231     status = STATUS_SUCCESS;
232
233     /* irp-IoStatus.Information (pnp_bus_info) not freed. */
234     alloc_pnp_bus_info:
235
236     irp->IoStatus.Status = status;
237     IoCompleteRequest(irp, IO_NO_INCREMENT);
238     return status;
239   }
240
241 static NTSTATUS STDCALL WvlBusPnpSimple(
242     IN WVL_SP_BUS_T bus,
243     IN PIRP irp,
244     IN UCHAR code
245   ) {
246     NTSTATUS status;
247     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
248
249     switch (code) {
250         case IRP_MN_QUERY_PNP_DEVICE_STATE:
251           DBG("IRP_MN_QUERY_PNP_DEVICE_STATE\n");
252           irp->IoStatus.Information = 0;
253           status = STATUS_SUCCESS;
254           break;
255
256         case IRP_MN_QUERY_STOP_DEVICE:
257           DBG("IRP_MN_QUERY_STOP_DEVICE\n");
258           bus->OldState = bus->State;
259           bus->State = WvlBusStateStopPending;
260           status = STATUS_SUCCESS;
261           break;
262
263         case IRP_MN_CANCEL_STOP_DEVICE:
264           DBG("IRP_MN_CANCEL_STOP_DEVICE\n");
265           bus->State = bus->OldState;
266           status = STATUS_SUCCESS;
267           break;
268
269         case IRP_MN_STOP_DEVICE:
270           DBG("IRP_MN_STOP_DEVICE\n");
271           bus->OldState = bus->State;
272           bus->State = WvlBusStateStopped;
273           status = STATUS_SUCCESS;
274           break;
275
276         case IRP_MN_QUERY_REMOVE_DEVICE:
277           DBG("IRP_MN_QUERY_REMOVE_DEVICE\n");
278           bus->OldState = bus->State;
279           bus->State = WvlBusStateRemovePending;
280           status = STATUS_SUCCESS;
281           break;
282
283         case IRP_MN_CANCEL_REMOVE_DEVICE:
284           DBG("IRP_MN_CANCEL_REMOVE_DEVICE\n");
285           bus->State = bus->OldState;
286           status = STATUS_SUCCESS;
287           break;
288
289         case IRP_MN_SURPRISE_REMOVAL:
290           DBG("IRP_MN_SURPRISE_REMOVAL\n");
291           bus->OldState = bus->State;
292           bus->State = WvlBusStateSurpriseRemovePending;
293           status = STATUS_SUCCESS;
294           break;
295
296         case IRP_MN_QUERY_RESOURCES:
297         case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
298           DBG("IRP_MN_QUERY_RESOURCE*\n");
299           IoCompleteRequest(irp, IO_NO_INCREMENT);
300           return STATUS_SUCCESS;
301           
302         default:
303           DBG("Unhandled IRP_MN_*: %d\n", code);
304           status = irp->IoStatus.Status;
305       }
306
307     irp->IoStatus.Status = status;
308     /* Should we pass it on?  We might be a floating FDO. */
309     if (lower) {
310         IoSkipCurrentIrpStackLocation(irp);
311         return IoCallDriver(lower, irp);
312       }
313     return WvlIrpComplete(irp, irp->IoStatus.Information, status);
314   }
315
316 /* Bus PnP dispatch routine. */
317 WVL_M_LIB NTSTATUS STDCALL WvlBusPnp(
318     IN WVL_SP_BUS_T Bus,
319     IN PIRP Irp
320   ) {
321     UCHAR code = IoGetCurrentIrpStackLocation(Irp)->MinorFunction;
322
323     switch (code) {
324         case IRP_MN_QUERY_DEVICE_TEXT:
325           DBG("IRP_MN_QUERY_DEVICE_TEXT\n");
326           if (Bus->QueryDevText)
327             return Bus->QueryDevText(Bus, Irp);
328           return WvlIrpComplete(Irp, 0, STATUS_NOT_SUPPORTED);
329
330         case IRP_MN_QUERY_BUS_INFORMATION:
331           DBG("IRP_MN_QUERY_BUS_INFORMATION\n");
332           return WvlBusPnpQueryBusInfo(Bus, Irp);
333
334         case IRP_MN_QUERY_DEVICE_RELATIONS:
335           DBG("IRP_MN_QUERY_DEVICE_RELATIONS\n");
336           return WvlBusPnpQueryDevRelations(Bus, Irp);
337
338         case IRP_MN_QUERY_CAPABILITIES:
339           DBG("IRP_MN_QUERY_CAPABILITIES\n");
340           return WvlBusPnpQueryCapabilities(Bus, Irp);
341
342         case IRP_MN_REMOVE_DEVICE:
343           DBG("IRP_MN_REMOVE_DEVICE\n");
344           return WvlBusPnpRemoveDev(Bus, Irp);
345
346         case IRP_MN_START_DEVICE:
347           DBG("IRP_MN_START_DEVICE\n");
348           return WvlBusPnpStartDev(Bus, Irp);
349
350         default:
351           return WvlBusPnpSimple(Bus, Irp, code);
352       }
353   }