[bus/pnp,disk/pnp,aoe] Get AoE sub-bus to work
[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 device__dispatch_func bus_pnp__start_dev_;
44 static device__dispatch_func bus_pnp__remove_dev_;
45 static device__dispatch_func bus_pnp__query_dev_relations_;
46 static device__dispatch_func bus_pnp__query_capabilities_;
47 static device__dispatch_func bus_pnp__query_dev_text_;
48 static device__dispatch_func bus_pnp__query_bus_info_;
49 static device__pnp_func bus_pnp__simple_;
50 device__pnp_func bus_pnp__dispatch;
51
52 static NTSTATUS STDCALL bus_pnp__io_completion_(
53     IN PDEVICE_OBJECT dev_obj,
54     IN PIRP irp,
55     IN PKEVENT event
56   ) {
57     KeSetEvent(event, 0, FALSE);
58     return STATUS_MORE_PROCESSING_REQUIRED;
59   }
60
61 static NTSTATUS STDCALL bus_pnp__start_dev_(
62     IN struct device__type * dev,
63     IN PIRP irp
64   ) {
65     NTSTATUS status;
66     KEVENT event;
67     struct bus__type * bus = bus__get(dev);
68     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
69
70     if (!lower)
71       return driver__complete_irp(irp, 0, STATUS_SUCCESS);
72     KeInitializeEvent(&event, NotificationEvent, FALSE);
73     IoCopyCurrentIrpStackLocationToNext(irp);
74     IoSetCompletionRoutine(
75         irp,
76         (PIO_COMPLETION_ROUTINE) bus_pnp__io_completion_,
77         (void *) &event,
78         TRUE,
79         TRUE,
80         TRUE
81       );
82     status = IoCallDriver(lower, irp);
83     if (status == STATUS_PENDING) {
84         DBG("Locked\n");
85         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
86       }
87     if (NT_SUCCESS(status = irp->IoStatus.Status)) {
88         dev->old_state = dev->state;
89         dev->state = device__state_started;
90       }
91     return driver__complete_irp(
92         irp,
93         irp->IoStatus.Information,
94         STATUS_SUCCESS
95       );
96   }
97
98 static NTSTATUS STDCALL bus_pnp__remove_dev_(
99     IN struct device__type * dev,
100     IN PIRP irp
101   ) {
102     NTSTATUS status = STATUS_SUCCESS;
103     struct bus__type * bus = bus__get(dev);
104     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
105     struct device__type * walker, * next;
106
107     dev->old_state = dev->state;
108     dev->state = device__state_deleted;
109     /* Pass the IRP on to any lower DEVICE_OBJECT */
110     if (lower) {
111         irp->IoStatus.Information = 0;
112         irp->IoStatus.Status = STATUS_SUCCESS;
113         IoSkipCurrentIrpStackLocation(irp);
114         status = IoCallDriver(lower, irp);
115       }
116     /* Remove all children. */
117     walker = bus->first_child;
118     while (walker != NULL) {
119         next = walker->next_sibling_ptr;
120         device__close(walker);
121         IoDeleteDevice(walker->Self);
122         device__free(walker);
123         walker = next;
124       }
125     /* Somewhat redundant, since the bus will be freed shortly. */
126     bus->Children = 0;
127     bus->first_child = NULL;
128     /* Detach from any lower DEVICE_OBJECT */
129     if (lower)
130       IoDetachDevice(lower);
131     /* Delete and free. */
132     IoDeleteDevice(dev->Self);
133     device__free(dev);
134     return status;
135   }
136
137 static NTSTATUS STDCALL bus_pnp__query_dev_relations_(
138     IN struct device__type * dev,
139     IN PIRP irp
140   ) {
141     NTSTATUS status;
142     struct bus__type * bus = bus__get(dev);
143     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
144     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
145     winvblock__uint32 count;
146     struct device__type * walker;
147     PDEVICE_RELATIONS dev_relations;
148
149     if (
150         io_stack_loc->Parameters.QueryDeviceRelations.Type != BusRelations ||
151         irp->IoStatus.Information
152       ) {
153         if (lower) {
154             IoSkipCurrentIrpStackLocation(irp);
155             return IoCallDriver(lower, irp);
156           }
157         return driver__complete_irp(
158             irp,
159             irp->IoStatus.Information,
160             irp->IoStatus.Status
161           );
162       }
163     probe__disks();
164     count = 0;
165     walker = bus->first_child;
166     while (walker != NULL) {
167         count++;
168         walker = walker->next_sibling_ptr;
169       }
170     dev_relations = wv_malloc(
171         sizeof *dev_relations + (sizeof (PDEVICE_OBJECT) * count)
172       );
173     if (dev_relations == NULL) {
174         /* Couldn't allocate dev_relations, but silently succeed. */
175         if (lower) {
176             IoSkipCurrentIrpStackLocation(irp);
177             return IoCallDriver(lower, irp);
178           }
179         return driver__complete_irp(irp, 0, STATUS_SUCCESS);
180       }
181     dev_relations->Count = count;
182
183     count = 0;
184     walker = bus->first_child;
185     while (walker != NULL) {
186         ObReferenceObject(walker->Self);
187         dev_relations->Objects[count] = walker->Self;
188         count++;
189         walker = walker->next_sibling_ptr;
190       }
191     irp->IoStatus.Information = (ULONG_PTR) dev_relations;
192     irp->IoStatus.Status = status = STATUS_SUCCESS;
193     if (lower) {
194         IoSkipCurrentIrpStackLocation(irp);
195         return IoCallDriver(lower, irp);
196       }
197     return driver__complete_irp(irp, irp->IoStatus.Information, status);
198   }
199
200 static NTSTATUS STDCALL bus_pnp__query_capabilities_(
201     IN struct device__type * dev,
202     IN PIRP irp
203   ) {
204     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
205     PDEVICE_CAPABILITIES DeviceCapabilities =
206       io_stack_loc->Parameters.DeviceCapabilities.Capabilities;
207     NTSTATUS status;
208     struct bus__type * bus = bus__get(dev);
209     DEVICE_CAPABILITIES ParentDeviceCapabilities;
210     PDEVICE_OBJECT lower;
211
212     if (DeviceCapabilities->Version != 1 ||
213         DeviceCapabilities->Size < sizeof (DEVICE_CAPABILITIES)
214       )
215       return driver__complete_irp(irp, 0, STATUS_UNSUCCESSFUL);
216     /* If there's a lower DEVICE_OBJECT, let it handle the IRP. */
217     lower = bus->LowerDeviceObject;
218     if (lower) {
219         IoSkipCurrentIrpStackLocation(irp);
220         return IoCallDriver(lower, irp);
221       }
222     /* Otherwise, return our parent's capabilities. */
223     status = bus__get_dev_capabilities(
224         dev->Parent,
225         DeviceCapabilities
226       );
227     if (!NT_SUCCESS(status))
228       return driver__complete_irp(irp, 0, status);
229     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
230   }
231
232 static NTSTATUS STDCALL bus_pnp__query_dev_text_(
233     IN struct device__type * dev,
234     IN PIRP irp
235   ) {
236     struct bus__type * bus = bus__get(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 = device__pnp_id(
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 bus_pnp__query_bus_info_(
318     IN struct device__type * dev,
319     IN PIRP irp
320   ) {
321     PPNP_BUS_INFORMATION pnp_bus_info;
322     NTSTATUS status;
323
324     pnp_bus_info = wv_palloc(sizeof *pnp_bus_info);
325     if (pnp_bus_info == NULL) {
326         DBG("wv_palloc IRP_MN_QUERY_BUS_INFORMATION\n");
327         status = STATUS_INSUFFICIENT_RESOURCES;
328         goto alloc_pnp_bus_info;
329       }
330     pnp_bus_info->BusTypeGuid = GUID_BUS_TYPE_INTERNAL;
331     pnp_bus_info->LegacyBusType = PNPBus;
332     pnp_bus_info->BusNumber = 0;
333     irp->IoStatus.Information = (ULONG_PTR) pnp_bus_info;
334     status = STATUS_SUCCESS;
335
336     /* irp-IoStatus.Information (pnp_bus_info) not freed. */
337     alloc_pnp_bus_info:
338
339     irp->IoStatus.Status = status;
340     IoCompleteRequest(irp, IO_NO_INCREMENT);
341     return status;
342   }
343
344 static NTSTATUS STDCALL bus_pnp__simple_(
345     IN struct device__type * dev,
346     IN PIRP irp,
347     IN UCHAR code
348   ) {
349     NTSTATUS status;
350     struct bus__type * bus = bus__get(dev);
351     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
352
353     switch (code) {
354         case IRP_MN_QUERY_PNP_DEVICE_STATE:
355           DBG("bus_pnp: IRP_MN_QUERY_PNP_DEVICE_STATE\n");
356           irp->IoStatus.Information = 0;
357           status = STATUS_SUCCESS;
358           break;
359
360         case IRP_MN_QUERY_STOP_DEVICE:
361           DBG("bus_pnp: IRP_MN_QUERY_STOP_DEVICE\n");
362           dev->old_state = dev->state;
363           dev->state = device__state_stop_pending;
364           status = STATUS_SUCCESS;
365           break;
366
367         case IRP_MN_CANCEL_STOP_DEVICE:
368           DBG("bus_pnp: IRP_MN_CANCEL_STOP_DEVICE\n");
369           dev->state = dev->old_state;
370           status = STATUS_SUCCESS;
371           break;
372
373         case IRP_MN_STOP_DEVICE:
374           DBG("bus_pnp: IRP_MN_STOP_DEVICE\n");
375           dev->old_state = dev->state;
376           dev->state = device__state_stopped;
377           status = STATUS_SUCCESS;
378           break;
379
380         case IRP_MN_QUERY_REMOVE_DEVICE:
381           DBG("bus_pnp: IRP_MN_QUERY_REMOVE_DEVICE\n");
382           dev->old_state = dev->state;
383           dev->state = device__state_remove_pending;
384           status = STATUS_SUCCESS;
385           break;
386
387         case IRP_MN_CANCEL_REMOVE_DEVICE:
388           DBG("bus_pnp: IRP_MN_CANCEL_REMOVE_DEVICE\n");
389           dev->state = dev->old_state;
390           status = STATUS_SUCCESS;
391           break;
392
393         case IRP_MN_SURPRISE_REMOVAL:
394           DBG("bus_pnp: IRP_MN_SURPRISE_REMOVAL\n");
395           dev->old_state = dev->state;
396           dev->state = device__state_surprise_remove_pending;
397           status = STATUS_SUCCESS;
398           break;
399
400         case IRP_MN_QUERY_RESOURCES:
401         case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
402           DBG("bus_pnp: IRP_MN_QUERY_RESOURCE*\n");
403           IoCompleteRequest(irp, IO_NO_INCREMENT);
404           return STATUS_SUCCESS;
405           
406         default:
407           DBG("bus_pnp: Unhandled IRP_MN_*: %d\n", code);
408           status = irp->IoStatus.Status;
409       }
410
411     irp->IoStatus.Status = status;
412     if (lower) {
413         IoSkipCurrentIrpStackLocation(irp);
414         return IoCallDriver(lower, irp);
415       }
416     return driver__complete_irp(irp, irp->IoStatus.Information, status);
417   }
418
419 /* Bus PnP dispatch routine. */
420 NTSTATUS STDCALL bus_pnp__dispatch(
421     IN struct device__type * dev,
422     IN PIRP irp,
423     IN UCHAR code
424   ) {
425     switch (code) {
426         case IRP_MN_QUERY_ID:
427           DBG("bus_pnp: IRP_MN_QUERY_ID\n");
428           return device__pnp_query_id(dev, irp);
429
430         case IRP_MN_QUERY_DEVICE_TEXT:
431           DBG("bus_pnp: IRP_MN_QUERY_DEVICE_TEXT\n");
432           return bus_pnp__query_dev_text_(dev, irp);
433
434         case IRP_MN_QUERY_BUS_INFORMATION:
435           DBG("bus_pnp: IRP_MN_QUERY_BUS_INFORMATION\n");
436           return bus_pnp__query_bus_info_(dev, irp);
437
438         case IRP_MN_QUERY_DEVICE_RELATIONS:
439           DBG("bus_pnp: IRP_MJ_QUERY_DEVICE_RELATIONS\n");
440           return bus_pnp__query_dev_relations_(dev, irp);
441
442         case IRP_MN_QUERY_CAPABILITIES:
443           DBG("bus_pnp: IRP_MN_QUERY_CAPABILITIES\n");
444           return bus_pnp__query_capabilities_(dev, irp);
445
446         case IRP_MN_REMOVE_DEVICE:
447           DBG("bus_pnp: IRP_MN_REMOVE_DEVICE\n");
448           return bus_pnp__remove_dev_(dev, irp);
449
450         case IRP_MN_START_DEVICE:
451           DBG("bus_pnp: IRP_MN_START_DEVICE\n");
452           return bus_pnp__start_dev_(dev, irp);
453
454         default:
455           return bus_pnp__simple_(dev, irp, code);
456       }
457   }