e9f7abaa20dd9288d76dc5b7ce6af469d8fcc87a
[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
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     dev->OldState = dev->State;
104     dev->State = WvDevStateDeleted;
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     /* Somewhat redundant, since the bus will be freed shortly. */
122     bus->Children = 0;
123     bus->first_child = NULL;
124     /* Detach from any lower DEVICE_OBJECT */
125     if (lower)
126       IoDetachDevice(lower);
127     /* Delete and free. */
128     IoDeleteDevice(dev->Self);
129     WvDevFree(dev);
130     return status;
131   }
132
133 static NTSTATUS STDCALL WvBusPnpQueryDevRelations_(
134     IN WV_SP_BUS_T bus,
135     IN PIRP irp
136   ) {
137     NTSTATUS status;
138     WV_SP_DEV_T dev = &bus->Dev;
139     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
140     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
141     winvblock__uint32 count;
142     WV_SP_DEV_T walker;
143     PDEVICE_RELATIONS dev_relations;
144
145     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
146         /* Enqueue the IRP. */
147         status = WvBusEnqueueIrp(bus, irp);
148         if (status != STATUS_PENDING)
149           /* Problem. */
150           return driver__complete_irp(irp, 0, status);
151         /* Ok. */
152         return status;
153       }
154     /* If we get here, we should be called by WvBusProcessWorkItems() */
155     if (
156         io_stack_loc->Parameters.QueryDeviceRelations.Type != BusRelations ||
157         irp->IoStatus.Information
158       ) {
159         if (lower) {
160             IoSkipCurrentIrpStackLocation(irp);
161             return IoCallDriver(lower, irp);
162           }
163         return driver__complete_irp(
164             irp,
165             irp->IoStatus.Information,
166             irp->IoStatus.Status
167           );
168       }
169     WvProbeDisks();
170     count = 0;
171     walker = bus->first_child;
172     while (walker != NULL) {
173         count++;
174         walker = walker->next_sibling_ptr;
175       }
176     dev_relations = wv_malloc(
177         sizeof *dev_relations + (sizeof (PDEVICE_OBJECT) * count)
178       );
179     if (dev_relations == NULL) {
180         /* Couldn't allocate dev_relations, but silently succeed. */
181         if (lower) {
182             IoSkipCurrentIrpStackLocation(irp);
183             return IoCallDriver(lower, irp);
184           }
185         return driver__complete_irp(irp, 0, STATUS_SUCCESS);
186       }
187     dev_relations->Count = count;
188
189     count = 0;
190     walker = bus->first_child;
191     while (walker != NULL) {
192         ObReferenceObject(walker->Self);
193         dev_relations->Objects[count] = walker->Self;
194         count++;
195         walker = walker->next_sibling_ptr;
196       }
197     irp->IoStatus.Information = (ULONG_PTR) dev_relations;
198     irp->IoStatus.Status = status = STATUS_SUCCESS;
199     if (lower) {
200         IoSkipCurrentIrpStackLocation(irp);
201         return IoCallDriver(lower, irp);
202       }
203     return driver__complete_irp(irp, irp->IoStatus.Information, status);
204   }
205
206 static NTSTATUS STDCALL WvBusPnpQueryCapabilities_(
207     IN WV_SP_BUS_T bus,
208     IN PIRP irp
209   ) {
210     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
211     PDEVICE_CAPABILITIES DeviceCapabilities =
212       io_stack_loc->Parameters.DeviceCapabilities.Capabilities;
213     NTSTATUS status;
214     WV_SP_DEV_T dev = &bus->Dev;
215     DEVICE_CAPABILITIES ParentDeviceCapabilities;
216     PDEVICE_OBJECT lower;
217
218     if (DeviceCapabilities->Version != 1 ||
219         DeviceCapabilities->Size < sizeof (DEVICE_CAPABILITIES)
220       )
221       return driver__complete_irp(irp, 0, STATUS_UNSUCCESSFUL);
222     /* If there's a lower DEVICE_OBJECT, let it handle the IRP. */
223     lower = bus->LowerDeviceObject;
224     if (lower) {
225         IoSkipCurrentIrpStackLocation(irp);
226         return IoCallDriver(lower, irp);
227       }
228     /* Otherwise, return our parent's capabilities. */
229     status = WvBusGetDevCapabilities(
230         dev->Parent,
231         DeviceCapabilities
232       );
233     if (!NT_SUCCESS(status))
234       return driver__complete_irp(irp, 0, status);
235     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
236   }
237
238 static NTSTATUS STDCALL WvBusPnpQueryDevText_(IN WV_SP_BUS_T bus, IN PIRP irp) {
239     WV_SP_DEV_T dev = &bus->Dev;
240     WCHAR (*str)[512];
241     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
242     NTSTATUS status;
243     winvblock__uint32 str_len;
244
245     /* Allocate a string buffer. */
246     str = wv_mallocz(sizeof *str);
247     if (str == NULL) {
248         DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n");
249         status = STATUS_INSUFFICIENT_RESOURCES;
250         goto alloc_str;
251       }
252     /* Determine the query type. */
253     switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) {
254         case DeviceTextDescription:
255           str_len = swprintf(*str, winvblock__literal_w L" Bus") + 1;
256           irp->IoStatus.Information =
257             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
258           if (irp->IoStatus.Information == 0) {
259               DBG("wv_palloc DeviceTextDescription\n");
260               status = STATUS_INSUFFICIENT_RESOURCES;
261               goto alloc_info;
262             }
263           RtlCopyMemory(
264               (PWCHAR) irp->IoStatus.Information,
265               str,
266               str_len * sizeof (WCHAR)
267             );
268           status = STATUS_SUCCESS;
269           goto alloc_info;
270
271         case DeviceTextLocationInformation:
272           str_len = WvDevPnpId(
273               dev,
274               BusQueryInstanceID,
275               str
276             );
277           irp->IoStatus.Information =
278             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
279           if (irp->IoStatus.Information == 0) {
280               DBG("wv_palloc DeviceTextLocationInformation\n");
281               status = STATUS_INSUFFICIENT_RESOURCES;
282               goto alloc_info;
283             }
284           RtlCopyMemory(
285               (PWCHAR) irp->IoStatus.Information,
286               str,
287               str_len * sizeof (WCHAR)
288             );
289           status = STATUS_SUCCESS;
290           goto alloc_info;
291
292         default:
293           irp->IoStatus.Information = 0;
294           status = STATUS_NOT_SUPPORTED;
295       }
296     /* irp->IoStatus.Information not freed. */
297     alloc_info:
298
299     wv_free(str);
300     alloc_str:
301
302     return driver__complete_irp(irp, irp->IoStatus.Information, status);
303   }
304
305 DEFINE_GUID(
306     GUID_BUS_TYPE_INTERNAL,
307     0x2530ea73L,
308     0x086b,
309     0x11d1,
310     0xa0,
311     0x9f,
312     0x00,
313     0xc0,
314     0x4f,
315     0xc3,
316     0x40,
317     0xb1
318   );
319
320 static NTSTATUS STDCALL WvBusPnpQueryBusInfo_(IN WV_SP_BUS_T bus, IN PIRP irp) {
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 WvBusPnpSimple_(
345     IN WV_SP_BUS_T bus,
346     IN PIRP irp,
347     IN UCHAR code
348   ) {
349     NTSTATUS status;
350     WV_SP_DEV_T dev = &bus->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->OldState = dev->State;
363           dev->State = WvDevStateStopPending;
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->OldState;
370           status = STATUS_SUCCESS;
371           break;
372
373         case IRP_MN_STOP_DEVICE:
374           DBG("bus_pnp: IRP_MN_STOP_DEVICE\n");
375           dev->OldState = dev->State;
376           dev->State = WvDevStateStopped;
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->OldState = dev->State;
383           dev->State = WvDevStateRemovePending;
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->OldState;
390           status = STATUS_SUCCESS;
391           break;
392
393         case IRP_MN_SURPRISE_REMOVAL:
394           DBG("bus_pnp: IRP_MN_SURPRISE_REMOVAL\n");
395           dev->OldState = dev->State;
396           dev->State = WvDevStateSurpriseRemovePending;
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 winvblock__lib_func NTSTATUS STDCALL WvBusPnp(
421     IN WV_SP_BUS_T bus,
422     IN PIRP irp,
423     IN UCHAR code
424   ) {
425     WV_SP_DEV_T dev = &bus->Dev;
426
427     switch (code) {
428         case IRP_MN_QUERY_ID:
429           DBG("bus_pnp: IRP_MN_QUERY_ID\n");
430           return WvDevPnpQueryId(dev, irp);
431
432         case IRP_MN_QUERY_DEVICE_TEXT:
433           DBG("bus_pnp: IRP_MN_QUERY_DEVICE_TEXT\n");
434           return WvBusPnpQueryDevText_(bus, irp);
435
436         case IRP_MN_QUERY_BUS_INFORMATION:
437           DBG("bus_pnp: IRP_MN_QUERY_BUS_INFORMATION\n");
438           return WvBusPnpQueryBusInfo_(bus, irp);
439
440         case IRP_MN_QUERY_DEVICE_RELATIONS:
441           DBG("bus_pnp: IRP_MN_QUERY_DEVICE_RELATIONS\n");
442           return WvBusPnpQueryDevRelations_(bus, irp);
443
444         case IRP_MN_QUERY_CAPABILITIES:
445           DBG("bus_pnp: IRP_MN_QUERY_CAPABILITIES\n");
446           return WvBusPnpQueryCapabilities_(bus, irp);
447
448         case IRP_MN_REMOVE_DEVICE:
449           DBG("bus_pnp: IRP_MN_REMOVE_DEVICE\n");
450           return WvBusPnpRemoveDev_(bus, irp);
451
452         case IRP_MN_START_DEVICE:
453           DBG("bus_pnp: IRP_MN_START_DEVICE\n");
454           return WvBusPnpStartDev_(bus, irp);
455
456         default:
457           return WvBusPnpSimple_(bus, irp, code);
458       }
459   }