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