[bus/pnp] Remove Dev usage from WvBusPnpRemoveDev_
[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->OldState = bus->State;
75         bus->State = WvBusStateStarted;
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 walker, next;
89     PLIST_ENTRY node_link;
90
91     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
92         /* Enqueue the IRP. */
93         status = WvBusEnqueueIrp(bus, irp);
94         if (status != STATUS_PENDING)
95           /* Problem. */
96           return driver__complete_irp(irp, 0, status);
97         /* Ok. */
98         return status;
99       }
100     /* If we get here, we should be called by WvBusProcessWorkItems() */
101     status = STATUS_SUCCESS;
102     lower = bus->LowerDeviceObject;
103     bus->OldState = bus->State;
104     bus->State = WvBusStateDeleted;
105     /* Pass the IRP on to any lower DEVICE_OBJECT */
106     if (lower) {
107         irp->IoStatus.Information = 0;
108         irp->IoStatus.Status = STATUS_SUCCESS;
109         IoSkipCurrentIrpStackLocation(irp);
110         status = IoCallDriver(lower, irp);
111       }
112     /* Remove all children. */
113     walker = bus->first_child;
114     while (walker != NULL) {
115         next = walker->next_sibling_ptr;
116         WvDevClose(walker);
117         IoDeleteDevice(walker->Self);
118         WvDevFree(walker);
119         walker = next;
120       }
121     node_link = &bus->BusPrivate_.Nodes;
122     while ((node_link = node_link->Flink) != &bus->BusPrivate_.Nodes) {
123         WV_SP_BUS_NODE node = CONTAINING_RECORD(
124             node_link,
125             WV_S_BUS_NODE,
126             BusPrivate_.Link
127           );
128
129         DBG("Removing PDO from bus...\n");
130         RemoveEntryList(&node->BusPrivate_.Link);
131         ObDereferenceObject(node->BusPrivate_.Pdo);
132         bus->BusPrivate_.NodeCount--;
133       }
134     /* Somewhat redundant, since the bus will be freed shortly. */
135     bus->Children = 0;
136     bus->first_child = NULL;
137     /* Detach from any lower DEVICE_OBJECT */
138     if (lower)
139       IoDetachDevice(lower);
140     /* Delete. */
141     IoDeleteDevice(bus->Fdo);
142     return status;
143   }
144
145 static NTSTATUS STDCALL WvBusPnpQueryDevRelations_(
146     IN WV_SP_BUS_T bus,
147     IN PIRP irp
148   ) {
149     NTSTATUS status;
150     WV_SP_DEV_T dev = &bus->Dev;
151     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
152     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
153     winvblock__uint32 count;
154     WV_SP_DEV_T walker;
155     PDEVICE_RELATIONS dev_relations;
156     PLIST_ENTRY node_link;
157
158     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
159         /* Enqueue the IRP. */
160         status = WvBusEnqueueIrp(bus, irp);
161         if (status != STATUS_PENDING)
162           /* Problem. */
163           return driver__complete_irp(irp, 0, status);
164         /* Ok. */
165         return status;
166       }
167     /* If we get here, we should be called by WvBusProcessWorkItems() */
168     if (
169         io_stack_loc->Parameters.QueryDeviceRelations.Type != BusRelations ||
170         irp->IoStatus.Information
171       ) {
172         if (lower) {
173             IoSkipCurrentIrpStackLocation(irp);
174             return IoCallDriver(lower, irp);
175           }
176         return driver__complete_irp(
177             irp,
178             irp->IoStatus.Information,
179             irp->IoStatus.Status
180           );
181       }
182     WvProbeDisks();
183     count = 0;
184     walker = bus->first_child;
185     while (walker != NULL) {
186         count++;
187         walker = walker->next_sibling_ptr;
188       }
189     count += bus->BusPrivate_.NodeCount;
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     node_link = &bus->BusPrivate_.Nodes;
212     while ((node_link = node_link->Flink) != &bus->BusPrivate_.Nodes) {
213         WV_SP_BUS_NODE node = CONTAINING_RECORD(
214             node_link,
215             WV_S_BUS_NODE,
216             BusPrivate_.Link
217           );
218
219         dev_relations->Objects[count] = node->BusPrivate_.Pdo;
220         count++;
221       }
222     irp->IoStatus.Information = (ULONG_PTR) dev_relations;
223     irp->IoStatus.Status = status = STATUS_SUCCESS;
224     if (lower) {
225         IoSkipCurrentIrpStackLocation(irp);
226         return IoCallDriver(lower, irp);
227       }
228     return driver__complete_irp(irp, irp->IoStatus.Information, status);
229   }
230
231 static NTSTATUS STDCALL WvBusPnpQueryCapabilities_(
232     IN WV_SP_BUS_T bus,
233     IN PIRP irp
234   ) {
235     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
236     PDEVICE_CAPABILITIES DeviceCapabilities =
237       io_stack_loc->Parameters.DeviceCapabilities.Capabilities;
238     NTSTATUS status;
239     DEVICE_CAPABILITIES ParentDeviceCapabilities;
240     PDEVICE_OBJECT lower;
241
242     if (DeviceCapabilities->Version != 1 ||
243         DeviceCapabilities->Size < sizeof (DEVICE_CAPABILITIES)
244       )
245       return driver__complete_irp(irp, 0, STATUS_UNSUCCESSFUL);
246     /* Let the lower DEVICE_OBJECT handle the IRP. */
247     lower = bus->LowerDeviceObject;
248     IoSkipCurrentIrpStackLocation(irp);
249     return IoCallDriver(lower, irp);
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     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
365
366     switch (code) {
367         case IRP_MN_QUERY_PNP_DEVICE_STATE:
368           DBG("bus_pnp: IRP_MN_QUERY_PNP_DEVICE_STATE\n");
369           irp->IoStatus.Information = 0;
370           status = STATUS_SUCCESS;
371           break;
372
373         case IRP_MN_QUERY_STOP_DEVICE:
374           DBG("bus_pnp: IRP_MN_QUERY_STOP_DEVICE\n");
375           bus->OldState = bus->State;
376           bus->State = WvBusStateStopPending;
377           status = STATUS_SUCCESS;
378           break;
379
380         case IRP_MN_CANCEL_STOP_DEVICE:
381           DBG("bus_pnp: IRP_MN_CANCEL_STOP_DEVICE\n");
382           bus->State = bus->OldState;
383           status = STATUS_SUCCESS;
384           break;
385
386         case IRP_MN_STOP_DEVICE:
387           DBG("bus_pnp: IRP_MN_STOP_DEVICE\n");
388           bus->OldState = bus->State;
389           bus->State = WvBusStateStopped;
390           status = STATUS_SUCCESS;
391           break;
392
393         case IRP_MN_QUERY_REMOVE_DEVICE:
394           DBG("bus_pnp: IRP_MN_QUERY_REMOVE_DEVICE\n");
395           bus->OldState = bus->State;
396           bus->State = WvBusStateRemovePending;
397           status = STATUS_SUCCESS;
398           break;
399
400         case IRP_MN_CANCEL_REMOVE_DEVICE:
401           DBG("bus_pnp: IRP_MN_CANCEL_REMOVE_DEVICE\n");
402           bus->State = bus->OldState;
403           status = STATUS_SUCCESS;
404           break;
405
406         case IRP_MN_SURPRISE_REMOVAL:
407           DBG("bus_pnp: IRP_MN_SURPRISE_REMOVAL\n");
408           bus->OldState = bus->State;
409           bus->State = WvBusStateSurpriseRemovePending;
410           status = STATUS_SUCCESS;
411           break;
412
413         case IRP_MN_QUERY_RESOURCES:
414         case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
415           DBG("bus_pnp: IRP_MN_QUERY_RESOURCE*\n");
416           IoCompleteRequest(irp, IO_NO_INCREMENT);
417           return STATUS_SUCCESS;
418           
419         default:
420           DBG("bus_pnp: Unhandled IRP_MN_*: %d\n", code);
421           status = irp->IoStatus.Status;
422       }
423
424     irp->IoStatus.Status = status;
425     if (lower) {
426         IoSkipCurrentIrpStackLocation(irp);
427         return IoCallDriver(lower, irp);
428       }
429     return driver__complete_irp(irp, irp->IoStatus.Information, status);
430   }
431
432 /* Bus PnP dispatch routine. */
433 winvblock__lib_func NTSTATUS STDCALL WvBusPnp(
434     IN WV_SP_BUS_T bus,
435     IN PIRP irp,
436     IN UCHAR code
437   ) {
438     WV_SP_DEV_T dev = &bus->Dev;
439
440     switch (code) {
441         case IRP_MN_QUERY_ID:
442           DBG("bus_pnp: IRP_MN_QUERY_ID\n");
443           return WvDevPnpQueryId(dev, irp);
444
445         case IRP_MN_QUERY_DEVICE_TEXT:
446           DBG("bus_pnp: IRP_MN_QUERY_DEVICE_TEXT\n");
447           return WvBusPnpQueryDevText_(bus, irp);
448
449         case IRP_MN_QUERY_BUS_INFORMATION:
450           DBG("bus_pnp: IRP_MN_QUERY_BUS_INFORMATION\n");
451           return WvBusPnpQueryBusInfo_(bus, irp);
452
453         case IRP_MN_QUERY_DEVICE_RELATIONS:
454           DBG("bus_pnp: IRP_MN_QUERY_DEVICE_RELATIONS\n");
455           return WvBusPnpQueryDevRelations_(bus, irp);
456
457         case IRP_MN_QUERY_CAPABILITIES:
458           DBG("bus_pnp: IRP_MN_QUERY_CAPABILITIES\n");
459           return WvBusPnpQueryCapabilities_(bus, irp);
460
461         case IRP_MN_REMOVE_DEVICE:
462           DBG("bus_pnp: IRP_MN_REMOVE_DEVICE\n");
463           return WvBusPnpRemoveDev_(bus, irp);
464
465         case IRP_MN_START_DEVICE:
466           DBG("bus_pnp: IRP_MN_START_DEVICE\n");
467           return WvBusPnpStartDev_(bus, irp);
468
469         default:
470           return WvBusPnpSimple_(bus, irp, code);
471       }
472   }