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