[bus] Use Fdo member instead of Dev.Self
[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     PLIST_ENTRY node_link;
159
160     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
161         /* Enqueue the IRP. */
162         status = WvBusEnqueueIrp(bus, irp);
163         if (status != STATUS_PENDING)
164           /* Problem. */
165           return driver__complete_irp(irp, 0, status);
166         /* Ok. */
167         return status;
168       }
169     /* If we get here, we should be called by WvBusProcessWorkItems() */
170     if (
171         io_stack_loc->Parameters.QueryDeviceRelations.Type != BusRelations ||
172         irp->IoStatus.Information
173       ) {
174         if (lower) {
175             IoSkipCurrentIrpStackLocation(irp);
176             return IoCallDriver(lower, irp);
177           }
178         return driver__complete_irp(
179             irp,
180             irp->IoStatus.Information,
181             irp->IoStatus.Status
182           );
183       }
184     WvProbeDisks();
185     count = 0;
186     walker = bus->first_child;
187     while (walker != NULL) {
188         count++;
189         walker = walker->next_sibling_ptr;
190       }
191     count += bus->BusPrivate_.NodeCount;
192     dev_relations = wv_malloc(
193         sizeof *dev_relations + (sizeof (PDEVICE_OBJECT) * count)
194       );
195     if (dev_relations == NULL) {
196         /* Couldn't allocate dev_relations, but silently succeed. */
197         if (lower) {
198             IoSkipCurrentIrpStackLocation(irp);
199             return IoCallDriver(lower, irp);
200           }
201         return driver__complete_irp(irp, 0, STATUS_SUCCESS);
202       }
203     dev_relations->Count = count;
204
205     count = 0;
206     walker = bus->first_child;
207     while (walker != NULL) {
208         ObReferenceObject(walker->Self);
209         dev_relations->Objects[count] = walker->Self;
210         count++;
211         walker = walker->next_sibling_ptr;
212       }
213     node_link = &bus->BusPrivate_.Nodes;
214     while ((node_link = node_link->Flink) != &bus->BusPrivate_.Nodes) {
215         WV_SP_BUS_NODE node = CONTAINING_RECORD(
216             node_link,
217             WV_S_BUS_NODE,
218             BusPrivate_.Link
219           );
220
221         dev_relations->Objects[count] = node->BusPrivate_.Pdo;
222         count++;
223       }
224     irp->IoStatus.Information = (ULONG_PTR) dev_relations;
225     irp->IoStatus.Status = status = STATUS_SUCCESS;
226     if (lower) {
227         IoSkipCurrentIrpStackLocation(irp);
228         return IoCallDriver(lower, irp);
229       }
230     return driver__complete_irp(irp, irp->IoStatus.Information, status);
231   }
232
233 static NTSTATUS STDCALL WvBusPnpQueryCapabilities_(
234     IN WV_SP_BUS_T bus,
235     IN PIRP irp
236   ) {
237     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
238     PDEVICE_CAPABILITIES DeviceCapabilities =
239       io_stack_loc->Parameters.DeviceCapabilities.Capabilities;
240     NTSTATUS status;
241     WV_SP_DEV_T dev = &bus->Dev;
242     DEVICE_CAPABILITIES ParentDeviceCapabilities;
243     PDEVICE_OBJECT lower;
244
245     if (DeviceCapabilities->Version != 1 ||
246         DeviceCapabilities->Size < sizeof (DEVICE_CAPABILITIES)
247       )
248       return driver__complete_irp(irp, 0, STATUS_UNSUCCESSFUL);
249     /* If there's a lower DEVICE_OBJECT, let it handle the IRP. */
250     lower = bus->LowerDeviceObject;
251     if (lower) {
252         IoSkipCurrentIrpStackLocation(irp);
253         return IoCallDriver(lower, irp);
254       }
255     /* Otherwise, return our parent's capabilities. */
256     status = WvDriverGetDevCapabilities(
257         dev->Parent,
258         DeviceCapabilities
259       );
260     if (!NT_SUCCESS(status))
261       return driver__complete_irp(irp, 0, status);
262     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
263   }
264
265 static NTSTATUS STDCALL WvBusPnpQueryDevText_(IN WV_SP_BUS_T bus, IN PIRP irp) {
266     WV_SP_DEV_T dev = &bus->Dev;
267     WCHAR (*str)[512];
268     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
269     NTSTATUS status;
270     winvblock__uint32 str_len;
271
272     /* Allocate a string buffer. */
273     str = wv_mallocz(sizeof *str);
274     if (str == NULL) {
275         DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n");
276         status = STATUS_INSUFFICIENT_RESOURCES;
277         goto alloc_str;
278       }
279     /* Determine the query type. */
280     switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) {
281         case DeviceTextDescription:
282           str_len = swprintf(*str, winvblock__literal_w L" Bus") + 1;
283           irp->IoStatus.Information =
284             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
285           if (irp->IoStatus.Information == 0) {
286               DBG("wv_palloc DeviceTextDescription\n");
287               status = STATUS_INSUFFICIENT_RESOURCES;
288               goto alloc_info;
289             }
290           RtlCopyMemory(
291               (PWCHAR) irp->IoStatus.Information,
292               str,
293               str_len * sizeof (WCHAR)
294             );
295           status = STATUS_SUCCESS;
296           goto alloc_info;
297
298         case DeviceTextLocationInformation:
299           str_len = WvDevPnpId(
300               dev,
301               BusQueryInstanceID,
302               str
303             );
304           irp->IoStatus.Information =
305             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
306           if (irp->IoStatus.Information == 0) {
307               DBG("wv_palloc DeviceTextLocationInformation\n");
308               status = STATUS_INSUFFICIENT_RESOURCES;
309               goto alloc_info;
310             }
311           RtlCopyMemory(
312               (PWCHAR) irp->IoStatus.Information,
313               str,
314               str_len * sizeof (WCHAR)
315             );
316           status = STATUS_SUCCESS;
317           goto alloc_info;
318
319         default:
320           irp->IoStatus.Information = 0;
321           status = STATUS_NOT_SUPPORTED;
322       }
323     /* irp->IoStatus.Information not freed. */
324     alloc_info:
325
326     wv_free(str);
327     alloc_str:
328
329     return driver__complete_irp(irp, irp->IoStatus.Information, status);
330   }
331
332 DEFINE_GUID(
333     GUID_BUS_TYPE_INTERNAL,
334     0x2530ea73L,
335     0x086b,
336     0x11d1,
337     0xa0,
338     0x9f,
339     0x00,
340     0xc0,
341     0x4f,
342     0xc3,
343     0x40,
344     0xb1
345   );
346
347 static NTSTATUS STDCALL WvBusPnpQueryBusInfo_(IN WV_SP_BUS_T bus, IN PIRP irp) {
348     PPNP_BUS_INFORMATION pnp_bus_info;
349     NTSTATUS status;
350
351     pnp_bus_info = wv_palloc(sizeof *pnp_bus_info);
352     if (pnp_bus_info == NULL) {
353         DBG("wv_palloc IRP_MN_QUERY_BUS_INFORMATION\n");
354         status = STATUS_INSUFFICIENT_RESOURCES;
355         goto alloc_pnp_bus_info;
356       }
357     pnp_bus_info->BusTypeGuid = GUID_BUS_TYPE_INTERNAL;
358     pnp_bus_info->LegacyBusType = PNPBus;
359     pnp_bus_info->BusNumber = 0;
360     irp->IoStatus.Information = (ULONG_PTR) pnp_bus_info;
361     status = STATUS_SUCCESS;
362
363     /* irp-IoStatus.Information (pnp_bus_info) not freed. */
364     alloc_pnp_bus_info:
365
366     irp->IoStatus.Status = status;
367     IoCompleteRequest(irp, IO_NO_INCREMENT);
368     return status;
369   }
370
371 static NTSTATUS STDCALL WvBusPnpSimple_(
372     IN WV_SP_BUS_T bus,
373     IN PIRP irp,
374     IN UCHAR code
375   ) {
376     NTSTATUS status;
377     WV_SP_DEV_T dev = &bus->Dev;
378     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
379
380     switch (code) {
381         case IRP_MN_QUERY_PNP_DEVICE_STATE:
382           DBG("bus_pnp: IRP_MN_QUERY_PNP_DEVICE_STATE\n");
383           irp->IoStatus.Information = 0;
384           status = STATUS_SUCCESS;
385           break;
386
387         case IRP_MN_QUERY_STOP_DEVICE:
388           DBG("bus_pnp: IRP_MN_QUERY_STOP_DEVICE\n");
389           dev->OldState = dev->State;
390           dev->State = WvDevStateStopPending;
391           status = STATUS_SUCCESS;
392           break;
393
394         case IRP_MN_CANCEL_STOP_DEVICE:
395           DBG("bus_pnp: IRP_MN_CANCEL_STOP_DEVICE\n");
396           dev->State = dev->OldState;
397           status = STATUS_SUCCESS;
398           break;
399
400         case IRP_MN_STOP_DEVICE:
401           DBG("bus_pnp: IRP_MN_STOP_DEVICE\n");
402           dev->OldState = dev->State;
403           dev->State = WvDevStateStopped;
404           status = STATUS_SUCCESS;
405           break;
406
407         case IRP_MN_QUERY_REMOVE_DEVICE:
408           DBG("bus_pnp: IRP_MN_QUERY_REMOVE_DEVICE\n");
409           dev->OldState = dev->State;
410           dev->State = WvDevStateRemovePending;
411           status = STATUS_SUCCESS;
412           break;
413
414         case IRP_MN_CANCEL_REMOVE_DEVICE:
415           DBG("bus_pnp: IRP_MN_CANCEL_REMOVE_DEVICE\n");
416           dev->State = dev->OldState;
417           status = STATUS_SUCCESS;
418           break;
419
420         case IRP_MN_SURPRISE_REMOVAL:
421           DBG("bus_pnp: IRP_MN_SURPRISE_REMOVAL\n");
422           dev->OldState = dev->State;
423           dev->State = WvDevStateSurpriseRemovePending;
424           status = STATUS_SUCCESS;
425           break;
426
427         case IRP_MN_QUERY_RESOURCES:
428         case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
429           DBG("bus_pnp: IRP_MN_QUERY_RESOURCE*\n");
430           IoCompleteRequest(irp, IO_NO_INCREMENT);
431           return STATUS_SUCCESS;
432           
433         default:
434           DBG("bus_pnp: Unhandled IRP_MN_*: %d\n", code);
435           status = irp->IoStatus.Status;
436       }
437
438     irp->IoStatus.Status = status;
439     if (lower) {
440         IoSkipCurrentIrpStackLocation(irp);
441         return IoCallDriver(lower, irp);
442       }
443     return driver__complete_irp(irp, irp->IoStatus.Information, status);
444   }
445
446 /* Bus PnP dispatch routine. */
447 winvblock__lib_func NTSTATUS STDCALL WvBusPnp(
448     IN WV_SP_BUS_T bus,
449     IN PIRP irp,
450     IN UCHAR code
451   ) {
452     WV_SP_DEV_T dev = &bus->Dev;
453
454     switch (code) {
455         case IRP_MN_QUERY_ID:
456           DBG("bus_pnp: IRP_MN_QUERY_ID\n");
457           return WvDevPnpQueryId(dev, irp);
458
459         case IRP_MN_QUERY_DEVICE_TEXT:
460           DBG("bus_pnp: IRP_MN_QUERY_DEVICE_TEXT\n");
461           return WvBusPnpQueryDevText_(bus, irp);
462
463         case IRP_MN_QUERY_BUS_INFORMATION:
464           DBG("bus_pnp: IRP_MN_QUERY_BUS_INFORMATION\n");
465           return WvBusPnpQueryBusInfo_(bus, irp);
466
467         case IRP_MN_QUERY_DEVICE_RELATIONS:
468           DBG("bus_pnp: IRP_MN_QUERY_DEVICE_RELATIONS\n");
469           return WvBusPnpQueryDevRelations_(bus, irp);
470
471         case IRP_MN_QUERY_CAPABILITIES:
472           DBG("bus_pnp: IRP_MN_QUERY_CAPABILITIES\n");
473           return WvBusPnpQueryCapabilities_(bus, irp);
474
475         case IRP_MN_REMOVE_DEVICE:
476           DBG("bus_pnp: IRP_MN_REMOVE_DEVICE\n");
477           return WvBusPnpRemoveDev_(bus, irp);
478
479         case IRP_MN_START_DEVICE:
480           DBG("bus_pnp: IRP_MN_START_DEVICE\n");
481           return WvBusPnpStartDev_(bus, irp);
482
483         default:
484           return WvBusPnpSimple_(bus, irp, code);
485       }
486   }