[bus,driver] Put bus IRP_MJ_PNP handler in driver
[people/sha0/winvblock.git] / src / winvblock / bus / pnp.c
1 /**
2  * Copyright (C) 2009-2010, 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 "winvblock.h"
33 #include "wv_stdlib.h"
34 #include "portable.h"
35 #include "driver.h"
36 #include "device.h"
37 #include "disk.h"
38 #include "bus.h"
39 #include "debug.h"
40 #include "probe.h"
41
42 /* Forward declarations. */
43 static WV_F_DEV_DISPATCH WvBusPnpStartDev_;
44 static WV_F_DEV_DISPATCH WvBusPnpRemoveDev_;
45 static WV_F_DEV_DISPATCH WvBusPnpQueryDevRelations_;
46 static WV_F_DEV_DISPATCH WvBusPnpQueryCapabilities_;
47 static WV_F_DEV_DISPATCH WvBusPnpQueryDevText_;
48 static WV_F_DEV_DISPATCH WvBusPnpQueryBusInfo_;
49 static WV_F_DEV_PNP WvBusPnpSimple_;
50
51 static NTSTATUS STDCALL WvBusPnpIoCompletion_(
52     IN PDEVICE_OBJECT dev_obj,
53     IN PIRP irp,
54     IN PKEVENT event
55   ) {
56     KeSetEvent(event, 0, FALSE);
57     return STATUS_MORE_PROCESSING_REQUIRED;
58   }
59
60 static NTSTATUS STDCALL WvBusPnpStartDev_(IN WV_SP_DEV_T dev, IN PIRP irp) {
61     NTSTATUS status;
62     KEVENT event;
63     WV_SP_BUS_T bus = WvBusFromDev(dev);
64     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
65
66     if (!lower)
67       return driver__complete_irp(irp, 0, STATUS_SUCCESS);
68     KeInitializeEvent(&event, NotificationEvent, FALSE);
69     IoCopyCurrentIrpStackLocationToNext(irp);
70     IoSetCompletionRoutine(
71         irp,
72         (PIO_COMPLETION_ROUTINE) WvBusPnpIoCompletion_,
73         (void *) &event,
74         TRUE,
75         TRUE,
76         TRUE
77       );
78     status = IoCallDriver(lower, irp);
79     if (status == STATUS_PENDING) {
80         DBG("Locked\n");
81         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
82       }
83     if (NT_SUCCESS(status = irp->IoStatus.Status)) {
84         dev->OldState = dev->State;
85         dev->State = WvDevStateStarted;
86       }
87     return driver__complete_irp(
88         irp,
89         irp->IoStatus.Information,
90         STATUS_SUCCESS
91       );
92   }
93
94 static NTSTATUS STDCALL WvBusPnpRemoveDev_(IN WV_SP_DEV_T dev, IN PIRP irp) {
95     NTSTATUS status = STATUS_SUCCESS;
96     WV_SP_BUS_T bus = WvBusFromDev(dev);
97     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
98     WV_SP_DEV_T walker, next;
99
100     dev->OldState = dev->State;
101     dev->State = WvDevStateDeleted;
102     /* Pass the IRP on to any lower DEVICE_OBJECT */
103     if (lower) {
104         irp->IoStatus.Information = 0;
105         irp->IoStatus.Status = STATUS_SUCCESS;
106         IoSkipCurrentIrpStackLocation(irp);
107         status = IoCallDriver(lower, irp);
108       }
109     /* Remove all children. */
110     walker = bus->first_child;
111     while (walker != NULL) {
112         next = walker->next_sibling_ptr;
113         WvDevClose(walker);
114         IoDeleteDevice(walker->Self);
115         WvDevFree(walker);
116         walker = next;
117       }
118     /* Somewhat redundant, since the bus will be freed shortly. */
119     bus->Children = 0;
120     bus->first_child = NULL;
121     /* Detach from any lower DEVICE_OBJECT */
122     if (lower)
123       IoDetachDevice(lower);
124     /* Delete and free. */
125     IoDeleteDevice(dev->Self);
126     WvDevFree(dev);
127     return status;
128   }
129
130 static NTSTATUS STDCALL WvBusPnpQueryDevRelations_(
131     IN WV_SP_DEV_T dev,
132     IN PIRP irp
133   ) {
134     NTSTATUS status;
135     WV_SP_BUS_T bus = WvBusFromDev(dev);
136     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
137     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
138     winvblock__uint32 count;
139     WV_SP_DEV_T walker;
140     PDEVICE_RELATIONS dev_relations;
141
142     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
143         /* Enqueue the IRP. */
144         status = WvBusEnqueueIrp(bus, irp);
145         if (status != STATUS_PENDING)
146           /* Problem. */
147           return driver__complete_irp(irp, 0, status);
148         /* Ok. */
149         return status;
150       }
151     /* If we get here, we should be called by WvBusProcessWorkItems() */
152     if (
153         io_stack_loc->Parameters.QueryDeviceRelations.Type != BusRelations ||
154         irp->IoStatus.Information
155       ) {
156         if (lower) {
157             IoSkipCurrentIrpStackLocation(irp);
158             return IoCallDriver(lower, irp);
159           }
160         return driver__complete_irp(
161             irp,
162             irp->IoStatus.Information,
163             irp->IoStatus.Status
164           );
165       }
166     WvProbeDisks();
167     count = 0;
168     walker = bus->first_child;
169     while (walker != NULL) {
170         count++;
171         walker = walker->next_sibling_ptr;
172       }
173     dev_relations = wv_malloc(
174         sizeof *dev_relations + (sizeof (PDEVICE_OBJECT) * count)
175       );
176     if (dev_relations == NULL) {
177         /* Couldn't allocate dev_relations, but silently succeed. */
178         if (lower) {
179             IoSkipCurrentIrpStackLocation(irp);
180             return IoCallDriver(lower, irp);
181           }
182         return driver__complete_irp(irp, 0, STATUS_SUCCESS);
183       }
184     dev_relations->Count = count;
185
186     count = 0;
187     walker = bus->first_child;
188     while (walker != NULL) {
189         ObReferenceObject(walker->Self);
190         dev_relations->Objects[count] = walker->Self;
191         count++;
192         walker = walker->next_sibling_ptr;
193       }
194     irp->IoStatus.Information = (ULONG_PTR) dev_relations;
195     irp->IoStatus.Status = status = STATUS_SUCCESS;
196     if (lower) {
197         IoSkipCurrentIrpStackLocation(irp);
198         return IoCallDriver(lower, irp);
199       }
200     return driver__complete_irp(irp, irp->IoStatus.Information, status);
201   }
202
203 static NTSTATUS STDCALL WvBusPnpQueryCapabilities_(
204     IN WV_SP_DEV_T dev,
205     IN PIRP irp
206   ) {
207     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
208     PDEVICE_CAPABILITIES DeviceCapabilities =
209       io_stack_loc->Parameters.DeviceCapabilities.Capabilities;
210     NTSTATUS status;
211     WV_SP_BUS_T bus = WvBusFromDev(dev);
212     DEVICE_CAPABILITIES ParentDeviceCapabilities;
213     PDEVICE_OBJECT lower;
214
215     if (DeviceCapabilities->Version != 1 ||
216         DeviceCapabilities->Size < sizeof (DEVICE_CAPABILITIES)
217       )
218       return driver__complete_irp(irp, 0, STATUS_UNSUCCESSFUL);
219     /* If there's a lower DEVICE_OBJECT, let it handle the IRP. */
220     lower = bus->LowerDeviceObject;
221     if (lower) {
222         IoSkipCurrentIrpStackLocation(irp);
223         return IoCallDriver(lower, irp);
224       }
225     /* Otherwise, return our parent's capabilities. */
226     status = WvBusGetDevCapabilities(
227         dev->Parent,
228         DeviceCapabilities
229       );
230     if (!NT_SUCCESS(status))
231       return driver__complete_irp(irp, 0, status);
232     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
233   }
234
235 static NTSTATUS STDCALL WvBusPnpQueryDevText_(IN WV_SP_DEV_T dev, IN PIRP irp) {
236     WV_SP_BUS_T bus = WvBusFromDev(dev);
237     WCHAR (*str)[512];
238     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
239     NTSTATUS status;
240     winvblock__uint32 str_len;
241
242     /* Allocate a string buffer. */
243     str = wv_mallocz(sizeof *str);
244     if (str == NULL) {
245         DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n");
246         status = STATUS_INSUFFICIENT_RESOURCES;
247         goto alloc_str;
248       }
249     /* Determine the query type. */
250     switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) {
251         case DeviceTextDescription:
252           str_len = swprintf(*str, winvblock__literal_w L" Bus") + 1;
253           irp->IoStatus.Information =
254             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
255           if (irp->IoStatus.Information == 0) {
256               DBG("wv_palloc DeviceTextDescription\n");
257               status = STATUS_INSUFFICIENT_RESOURCES;
258               goto alloc_info;
259             }
260           RtlCopyMemory(
261               (PWCHAR) irp->IoStatus.Information,
262               str,
263               str_len * sizeof (WCHAR)
264             );
265           status = STATUS_SUCCESS;
266           goto alloc_info;
267
268         case DeviceTextLocationInformation:
269           str_len = WvDevPnpId(
270               dev,
271               BusQueryInstanceID,
272               str
273             );
274           irp->IoStatus.Information =
275             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
276           if (irp->IoStatus.Information == 0) {
277               DBG("wv_palloc DeviceTextLocationInformation\n");
278               status = STATUS_INSUFFICIENT_RESOURCES;
279               goto alloc_info;
280             }
281           RtlCopyMemory(
282               (PWCHAR) irp->IoStatus.Information,
283               str,
284               str_len * sizeof (WCHAR)
285             );
286           status = STATUS_SUCCESS;
287           goto alloc_info;
288
289         default:
290           irp->IoStatus.Information = 0;
291           status = STATUS_NOT_SUPPORTED;
292       }
293     /* irp->IoStatus.Information not freed. */
294     alloc_info:
295
296     wv_free(str);
297     alloc_str:
298
299     return driver__complete_irp(irp, irp->IoStatus.Information, status);
300   }
301
302 DEFINE_GUID(
303     GUID_BUS_TYPE_INTERNAL,
304     0x2530ea73L,
305     0x086b,
306     0x11d1,
307     0xa0,
308     0x9f,
309     0x00,
310     0xc0,
311     0x4f,
312     0xc3,
313     0x40,
314     0xb1
315   );
316
317 static NTSTATUS STDCALL WvBusPnpQueryBusInfo_(IN WV_SP_DEV_T dev, IN PIRP irp) {
318     PPNP_BUS_INFORMATION pnp_bus_info;
319     NTSTATUS status;
320
321     pnp_bus_info = wv_palloc(sizeof *pnp_bus_info);
322     if (pnp_bus_info == NULL) {
323         DBG("wv_palloc IRP_MN_QUERY_BUS_INFORMATION\n");
324         status = STATUS_INSUFFICIENT_RESOURCES;
325         goto alloc_pnp_bus_info;
326       }
327     pnp_bus_info->BusTypeGuid = GUID_BUS_TYPE_INTERNAL;
328     pnp_bus_info->LegacyBusType = PNPBus;
329     pnp_bus_info->BusNumber = 0;
330     irp->IoStatus.Information = (ULONG_PTR) pnp_bus_info;
331     status = STATUS_SUCCESS;
332
333     /* irp-IoStatus.Information (pnp_bus_info) not freed. */
334     alloc_pnp_bus_info:
335
336     irp->IoStatus.Status = status;
337     IoCompleteRequest(irp, IO_NO_INCREMENT);
338     return status;
339   }
340
341 static NTSTATUS STDCALL WvBusPnpSimple_(
342     IN WV_SP_DEV_T dev,
343     IN PIRP irp,
344     IN UCHAR code
345   ) {
346     NTSTATUS status;
347     WV_SP_BUS_T bus = WvBusFromDev(dev);
348     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
349
350     switch (code) {
351         case IRP_MN_QUERY_PNP_DEVICE_STATE:
352           DBG("bus_pnp: IRP_MN_QUERY_PNP_DEVICE_STATE\n");
353           irp->IoStatus.Information = 0;
354           status = STATUS_SUCCESS;
355           break;
356
357         case IRP_MN_QUERY_STOP_DEVICE:
358           DBG("bus_pnp: IRP_MN_QUERY_STOP_DEVICE\n");
359           dev->OldState = dev->State;
360           dev->State = WvDevStateStopPending;
361           status = STATUS_SUCCESS;
362           break;
363
364         case IRP_MN_CANCEL_STOP_DEVICE:
365           DBG("bus_pnp: IRP_MN_CANCEL_STOP_DEVICE\n");
366           dev->State = dev->OldState;
367           status = STATUS_SUCCESS;
368           break;
369
370         case IRP_MN_STOP_DEVICE:
371           DBG("bus_pnp: IRP_MN_STOP_DEVICE\n");
372           dev->OldState = dev->State;
373           dev->State = WvDevStateStopped;
374           status = STATUS_SUCCESS;
375           break;
376
377         case IRP_MN_QUERY_REMOVE_DEVICE:
378           DBG("bus_pnp: IRP_MN_QUERY_REMOVE_DEVICE\n");
379           dev->OldState = dev->State;
380           dev->State = WvDevStateRemovePending;
381           status = STATUS_SUCCESS;
382           break;
383
384         case IRP_MN_CANCEL_REMOVE_DEVICE:
385           DBG("bus_pnp: IRP_MN_CANCEL_REMOVE_DEVICE\n");
386           dev->State = dev->OldState;
387           status = STATUS_SUCCESS;
388           break;
389
390         case IRP_MN_SURPRISE_REMOVAL:
391           DBG("bus_pnp: IRP_MN_SURPRISE_REMOVAL\n");
392           dev->OldState = dev->State;
393           dev->State = WvDevStateSurpriseRemovePending;
394           status = STATUS_SUCCESS;
395           break;
396
397         case IRP_MN_QUERY_RESOURCES:
398         case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
399           DBG("bus_pnp: IRP_MN_QUERY_RESOURCE*\n");
400           IoCompleteRequest(irp, IO_NO_INCREMENT);
401           return STATUS_SUCCESS;
402           
403         default:
404           DBG("bus_pnp: Unhandled IRP_MN_*: %d\n", code);
405           status = irp->IoStatus.Status;
406       }
407
408     irp->IoStatus.Status = status;
409     if (lower) {
410         IoSkipCurrentIrpStackLocation(irp);
411         return IoCallDriver(lower, irp);
412       }
413     return driver__complete_irp(irp, irp->IoStatus.Information, status);
414   }
415
416 /* Bus PnP dispatch routine. */
417 winvblock__lib_func NTSTATUS STDCALL WvBusPnp(
418     IN WV_SP_BUS_T bus,
419     IN PIRP irp,
420     IN UCHAR code
421   ) {
422     WV_SP_DEV_T dev = &bus->Dev;
423
424     switch (code) {
425         case IRP_MN_QUERY_ID:
426           DBG("bus_pnp: IRP_MN_QUERY_ID\n");
427           return WvDevPnpQueryId(dev, irp);
428
429         case IRP_MN_QUERY_DEVICE_TEXT:
430           DBG("bus_pnp: IRP_MN_QUERY_DEVICE_TEXT\n");
431           return WvBusPnpQueryDevText_(dev, irp);
432
433         case IRP_MN_QUERY_BUS_INFORMATION:
434           DBG("bus_pnp: IRP_MN_QUERY_BUS_INFORMATION\n");
435           return WvBusPnpQueryBusInfo_(dev, irp);
436
437         case IRP_MN_QUERY_DEVICE_RELATIONS:
438           DBG("bus_pnp: IRP_MJ_QUERY_DEVICE_RELATIONS\n");
439           return WvBusPnpQueryDevRelations_(dev, irp);
440
441         case IRP_MN_QUERY_CAPABILITIES:
442           DBG("bus_pnp: IRP_MN_QUERY_CAPABILITIES\n");
443           return WvBusPnpQueryCapabilities_(dev, irp);
444
445         case IRP_MN_REMOVE_DEVICE:
446           DBG("bus_pnp: IRP_MN_REMOVE_DEVICE\n");
447           return WvBusPnpRemoveDev_(dev, irp);
448
449         case IRP_MN_START_DEVICE:
450           DBG("bus_pnp: IRP_MN_START_DEVICE\n");
451           return WvBusPnpStartDev_(dev, irp);
452
453         default:
454           return WvBusPnpSimple_(dev, irp, code);
455       }
456   }