da974a18a465fd3689062532944c241ee15943f7
[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 <ntddk.h>
29
30 #include "winvblock.h"
31 #include "wv_stdlib.h"
32 #include "portable.h"
33 #include "driver.h"
34 #include "device.h"
35 #include "disk.h"
36 #include "bus.h"
37 #include "debug.h"
38 #include "probe.h"
39
40 /* Forward declarations. */
41 static device__dispatch_func bus_pnp__start_dev_;
42 static device__dispatch_func bus_pnp__remove_dev_;
43 static device__dispatch_func bus_pnp__query_dev_relations_;
44 static device__pnp_func bus_pnp__simple_;
45 device__pnp_func bus_pnp__dispatch;
46
47 static NTSTATUS STDCALL bus_pnp__io_completion_(
48     IN PDEVICE_OBJECT dev_obj,
49     IN PIRP irp,
50     IN PKEVENT event
51   ) {
52     KeSetEvent(event, 0, FALSE);
53     return STATUS_MORE_PROCESSING_REQUIRED;
54   }
55
56 static NTSTATUS STDCALL bus_pnp__start_dev_(
57     IN struct device__type * dev,
58     IN PIRP irp
59   ) {
60     NTSTATUS status;
61     KEVENT event;
62     struct bus__type * bus = bus__get(dev);
63     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
64
65     if (!lower)
66       return driver__complete_irp(irp, 0, STATUS_SUCCESS);
67     KeInitializeEvent(&event, NotificationEvent, FALSE);
68     IoCopyCurrentIrpStackLocationToNext(irp);
69     IoSetCompletionRoutine(
70         irp,
71         (PIO_COMPLETION_ROUTINE) bus_pnp__io_completion_,
72         (void *) &event,
73         TRUE,
74         TRUE,
75         TRUE
76       );
77     status = IoCallDriver(lower, irp);
78     if (status == STATUS_PENDING) {
79         DBG("Locked\n");
80         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
81       }
82     if (NT_SUCCESS(status = irp->IoStatus.Status)) {
83         dev->old_state = dev->state;
84         dev->state = device__state_started;
85       }
86     return driver__complete_irp(
87         irp,
88         irp->IoStatus.Information,
89         STATUS_SUCCESS
90       );
91   }
92
93 static NTSTATUS STDCALL bus_pnp__remove_dev_(
94     IN struct device__type * dev,
95     IN PIRP irp
96   ) {
97     NTSTATUS status = STATUS_SUCCESS;
98     struct bus__type * bus = bus__get(dev);
99     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
100     struct device__type * walker, * next;
101
102     dev->old_state = dev->state;
103     dev->state = device__state_deleted;
104     /* Pass the IRP on to any lower DEVICE_OBJECT */
105     if (lower) {
106         irp->IoStatus.Information = 0;
107         irp->IoStatus.Status = STATUS_SUCCESS;
108         IoSkipCurrentIrpStackLocation(irp);
109         status = IoCallDriver(lower, irp);
110       }
111     /* Remove all children. */
112     walker = bus->first_child;
113     while (walker != NULL) {
114         next = walker->next_sibling_ptr;
115         device__close(walker);
116         IoDeleteDevice(walker->Self);
117         device__free(walker);
118         walker = next;
119       }
120     /* Somewhat redundant, since the bus will be freed shortly. */
121     bus->Children = 0;
122     bus->first_child = NULL;
123     /* Detach from any lower DEVICE_OBJECT */
124     if (lower)
125       IoDetachDevice(lower);
126     /* Delete and free. */
127     IoDeleteDevice(dev->Self);
128     device__free(dev);
129     return status;
130   }
131
132 static NTSTATUS STDCALL bus_pnp__query_dev_relations_(
133     IN struct device__type * dev,
134     IN PIRP irp
135   ) {
136     NTSTATUS status;
137     struct bus__type * bus = bus__get(dev);
138     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
139     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
140     winvblock__uint32 count;
141     struct device__type * walker;
142     PDEVICE_RELATIONS dev_relations;
143
144     if (
145         io_stack_loc->Parameters.QueryDeviceRelations.Type != BusRelations ||
146         irp->IoStatus.Information
147       ) {
148         if (lower) {
149             IoSkipCurrentIrpStackLocation(irp);
150             return IoCallDriver(lower, irp);
151           }
152         return driver__complete_irp(
153             irp,
154             irp->IoStatus.Information,
155             irp->IoStatus.Status
156           );
157       }
158     probe__disks();
159     count = 0;
160     walker = bus->first_child;
161     while (walker != NULL) {
162         count++;
163         walker = walker->next_sibling_ptr;
164       }
165     dev_relations = wv_malloc(
166         sizeof *dev_relations + (sizeof (PDEVICE_OBJECT) * count)
167       );
168     if (dev_relations == NULL) {
169         /* Couldn't allocate dev_relations, but silently succeed. */
170         if (lower) {
171             IoSkipCurrentIrpStackLocation(irp);
172             return IoCallDriver(lower, irp);
173           }
174         return driver__complete_irp(irp, 0, STATUS_SUCCESS);
175       }
176     dev_relations->Count = count;
177
178     count = 0;
179     walker = bus->first_child;
180     while (walker != NULL) {
181         ObReferenceObject(walker->Self);
182         dev_relations->Objects[count] = walker->Self;
183         count++;
184         walker = walker->next_sibling_ptr;
185       }
186     irp->IoStatus.Information = (ULONG_PTR) dev_relations;
187     irp->IoStatus.Status = status = STATUS_SUCCESS;
188     if (lower) {
189         IoSkipCurrentIrpStackLocation(irp);
190         return IoCallDriver(lower, irp);
191       }
192     return driver__complete_irp(irp, irp->IoStatus.Information, status);
193   }
194
195 static NTSTATUS STDCALL bus_pnp__simple_(
196     IN struct device__type * dev,
197     IN PIRP irp,
198     IN UCHAR code
199   ) {
200     NTSTATUS status;
201     struct bus__type * bus = bus__get(dev);
202     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
203
204     switch (code) {
205         case IRP_MN_QUERY_PNP_DEVICE_STATE:
206           irp->IoStatus.Information = 0;
207           status = STATUS_SUCCESS;
208           break;
209
210         case IRP_MN_QUERY_STOP_DEVICE:
211           dev->old_state = dev->state;
212           dev->state = device__state_stop_pending;
213           status = STATUS_SUCCESS;
214           break;
215
216         case IRP_MN_CANCEL_STOP_DEVICE:
217           dev->state = dev->old_state;
218           status = STATUS_SUCCESS;
219           break;
220
221         case IRP_MN_STOP_DEVICE:
222           dev->old_state = dev->state;
223           dev->state = device__state_stopped;
224           status = STATUS_SUCCESS;
225           break;
226
227         case IRP_MN_QUERY_REMOVE_DEVICE:
228           dev->old_state = dev->state;
229           dev->state = device__state_remove_pending;
230           status = STATUS_SUCCESS;
231           break;
232
233         case IRP_MN_CANCEL_REMOVE_DEVICE:
234           dev->state = dev->old_state;
235           status = STATUS_SUCCESS;
236           break;
237
238         case IRP_MN_SURPRISE_REMOVAL:
239           dev->old_state = dev->state;
240           dev->state = device__state_surprise_remove_pending;
241           status = STATUS_SUCCESS;
242           break;
243
244         default:
245           status = irp->IoStatus.Status;
246       }
247
248     irp->IoStatus.Status = status;
249     if (lower) {
250         IoSkipCurrentIrpStackLocation(irp);
251         return IoCallDriver(lower, irp);
252       }
253     return driver__complete_irp(irp, irp->IoStatus.Information, status);
254   }
255
256 /* Bus PnP dispatch routine. */
257 NTSTATUS STDCALL bus_pnp__dispatch(
258     IN struct device__type * dev,
259     IN PIRP irp,
260     IN UCHAR code
261   ) {
262     switch (code) {
263         case IRP_MN_QUERY_ID:
264           return device__pnp_query_id(dev, irp);
265
266         case IRP_MN_QUERY_DEVICE_RELATIONS:
267           return bus_pnp__query_dev_relations_(dev, irp);
268
269         case IRP_MN_REMOVE_DEVICE:
270           return bus_pnp__remove_dev_(dev, irp);
271
272         case IRP_MN_START_DEVICE:
273           return bus_pnp__start_dev_(dev, irp);
274
275         default:
276           return bus_pnp__simple_(dev, irp, code);
277       }
278   }