f113cf811d083192f16e8577a800e7ea1214f19d
[people/sha0/winvblock.git] / src / winvblock / bus / bus.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 specifics.
26  */
27
28 #include <ntddk.h>
29
30 #include "winvblock.h"
31 #include "wv_stdlib.h"
32 #include "portable.h"
33 #include "irp.h"
34 #include "driver.h"
35 #include "device.h"
36 #include "bus.h"
37 #include "bus_pnp.h"
38 #include "bus_dev_ctl.h"
39 #include "debug.h"
40
41 /* Forward declarations. */
42 static device__free_func bus__free_;
43 static device__create_pdo_func bus__create_pdo_;
44
45 /**
46  * Add a child node to the bus.
47  *
48  * @v bus_ptr           Points to the bus receiving the child.
49  * @v dev_ptr           Points to the child device to add.
50  * @ret                 TRUE for success, FALSE for failure.
51  */
52 winvblock__lib_func winvblock__bool STDCALL bus__add_child(
53     IN OUT struct bus__type * bus_ptr,
54     IN OUT device__type_ptr dev_ptr
55   ) {
56     /* The new node's device object. */
57     PDEVICE_OBJECT dev_obj_ptr;
58     /* Walks the child nodes. */
59     device__type_ptr walker;
60
61     DBG("Entry\n");
62     if ((bus_ptr == NULL) || (dev_ptr == NULL)) {
63         DBG("No bus or no device!\n");
64         return FALSE;
65       }
66     /* Create the child device. */
67     dev_obj_ptr = device__create_pdo(dev_ptr);
68     if (dev_obj_ptr == NULL) {
69         DBG("PDO creation failed!\n");
70         device__free(dev_ptr);
71         return FALSE;
72       }
73
74     dev_ptr->Parent = bus_ptr->device->Self;
75     /*
76      * Initialize the device.  For disks, this routine is responsible for
77      * determining the disk's geometry appropriately for AoE/RAM/file disks.
78      */
79     dev_ptr->ops.init(dev_ptr);
80     dev_obj_ptr->Flags &= ~DO_DEVICE_INITIALIZING;
81     /* Add the new device's extension to the bus' list of children. */
82     if (bus_ptr->first_child_ptr == NULL) {
83         bus_ptr->first_child_ptr = dev_ptr;
84       } else {
85         walker = bus_ptr->first_child_ptr;
86         while (walker->next_sibling_ptr != NULL)
87           walker = walker->next_sibling_ptr;
88         walker->next_sibling_ptr = dev_ptr;
89       }
90     bus_ptr->Children++;
91     if (bus_ptr->PhysicalDeviceObject != NULL) {
92         IoInvalidateDeviceRelations(
93             bus_ptr->PhysicalDeviceObject,
94             BusRelations
95           );
96       }
97     DBG("Exit\n");
98     return TRUE;
99   }
100
101 static NTSTATUS STDCALL bus__sys_ctl_(
102     IN PDEVICE_OBJECT DeviceObject,
103     IN PIRP Irp,
104     IN PIO_STACK_LOCATION Stack,
105     IN struct _device__type * dev_ptr,
106     OUT winvblock__bool_ptr completion_ptr
107   ) {
108     struct bus__type * bus_ptr = bus__get(dev_ptr);
109     PDEVICE_OBJECT lower = bus_ptr->LowerDeviceObject;
110
111     DBG("...\n");
112     IoSkipCurrentIrpStackLocation(Irp);
113     *completion_ptr = TRUE;
114     return lower ? IoCallDriver(lower, Irp) : STATUS_SUCCESS;
115   }
116
117 static NTSTATUS STDCALL power(
118     IN PDEVICE_OBJECT DeviceObject,
119     IN PIRP Irp,
120     IN PIO_STACK_LOCATION Stack,
121     IN struct _device__type * dev_ptr,
122     OUT winvblock__bool_ptr completion_ptr
123   ) {
124     struct bus__type * bus_ptr = bus__get(dev_ptr);
125     PDEVICE_OBJECT lower = bus_ptr->LowerDeviceObject;
126
127     PoStartNextPowerIrp(Irp);
128     IoSkipCurrentIrpStackLocation(Irp);
129     *completion_ptr = TRUE;
130     return lower ? PoCallDriver(lower, Irp) : STATUS_SUCCESS;
131   }
132
133 NTSTATUS STDCALL bus__get_dev_capabilities(
134     IN PDEVICE_OBJECT DeviceObject,
135     IN PDEVICE_CAPABILITIES DeviceCapabilities
136   ) {
137     IO_STATUS_BLOCK ioStatus;
138     KEVENT pnpEvent;
139     NTSTATUS status;
140     PDEVICE_OBJECT targetObject;
141     PIO_STACK_LOCATION irpStack;
142     PIRP pnpIrp;
143
144     RtlZeroMemory(DeviceCapabilities, sizeof (DEVICE_CAPABILITIES));
145     DeviceCapabilities->Size = sizeof (DEVICE_CAPABILITIES);
146     DeviceCapabilities->Version = 1;
147     DeviceCapabilities->Address = -1;
148     DeviceCapabilities->UINumber = -1;
149
150     KeInitializeEvent(&pnpEvent, NotificationEvent, FALSE);
151     targetObject = IoGetAttachedDeviceReference(DeviceObject);
152     pnpIrp = IoBuildSynchronousFsdRequest(
153         IRP_MJ_PNP,
154         targetObject,
155         NULL,
156         0,
157         NULL,
158         &pnpEvent,
159         &ioStatus
160       );
161     if (pnpIrp == NULL) {
162         status = STATUS_INSUFFICIENT_RESOURCES;
163       } else {
164         pnpIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
165         irpStack = IoGetNextIrpStackLocation(pnpIrp);
166         RtlZeroMemory(irpStack, sizeof (IO_STACK_LOCATION));
167         irpStack->MajorFunction = IRP_MJ_PNP;
168         irpStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
169         irpStack->Parameters.DeviceCapabilities.Capabilities =
170           DeviceCapabilities;
171         status = IoCallDriver(targetObject, pnpIrp);
172         if (status == STATUS_PENDING) {
173             KeWaitForSingleObject(
174                 &pnpEvent,
175                 Executive,
176                 KernelMode,
177                 FALSE,
178                 NULL
179               );
180             status = ioStatus.Status;
181           }
182       }
183     ObDereferenceObject(targetObject);
184     return status;
185   }
186
187 /* Bus dispatch routine. */
188 static NTSTATUS STDCALL bus_dispatch(
189     IN PDEVICE_OBJECT dev,
190     IN PIRP irp
191   ) {
192     NTSTATUS status;
193     winvblock__bool completion = FALSE;
194     static const irp__handling handling_table[] = {
195         /*
196          * Major, minor, any major?, any minor?, handler
197          * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
198          * Note that the fall-through case must come FIRST!
199          * Why? It sets completion to true, so others won't be called.
200          */
201         {                     0, 0,  TRUE, TRUE, driver__not_supported },
202         {          IRP_MJ_CLOSE, 0, FALSE, TRUE,  driver__create_close },
203         {         IRP_MJ_CREATE, 0, FALSE, TRUE,  driver__create_close },
204         { IRP_MJ_SYSTEM_CONTROL, 0, FALSE, TRUE,         bus__sys_ctl_ },
205         {          IRP_MJ_POWER, 0, FALSE, TRUE,                 power },
206         { IRP_MJ_DEVICE_CONTROL, 0, FALSE, TRUE, bus_dev_ctl__dispatch },
207         {            IRP_MJ_PNP, 0, FALSE, TRUE,       bus_pnp__simple },
208         {            IRP_MJ_PNP,
209                IRP_MN_START_DEVICE, FALSE, FALSE,   bus_pnp__start_dev },
210         {            IRP_MJ_PNP,
211               IRP_MN_REMOVE_DEVICE, FALSE, FALSE,  bus_pnp__remove_dev },
212         {            IRP_MJ_PNP,
213      IRP_MN_QUERY_DEVICE_RELATIONS, FALSE, FALSE,
214                                           bus_pnp__query_dev_relations },
215       };
216
217     /* Try registered mini IRP handling tables first.  Deprecated. */
218     status = irp__process(
219         dev,
220         irp,
221         IoGetCurrentIrpStackLocation(irp),
222         device__get(dev),
223         &completion
224       );
225     /* Fall through to the bus defaults, if needed. */
226     if (status == STATUS_NOT_SUPPORTED && !completion)
227       status = irp__process_with_table(
228           dev,
229           irp,
230           handling_table,
231           sizeof handling_table,
232           &completion
233         );
234     #ifdef DEBUGIRPS
235     if (status != STATUS_PENDING)
236       Debug_IrpEnd(irp, status);
237     #endif
238
239     return status;
240   }
241
242 /* Initialize a bus. */
243 static winvblock__bool STDCALL bus__init_(IN device__type_ptr dev) {
244     return TRUE;
245   }
246
247 /**
248  * Create a new bus.
249  *
250  * @ret bus_ptr         The address of a new bus, or NULL for failure.
251  *
252  * This function should not be confused with a PDO creation routine, which is
253  * actually implemented for each device type.  This routine will allocate a
254  * bus__type, track it in a global list, as well as populate the bus
255  * with default values.
256  */
257 winvblock__lib_func struct bus__type * bus__create(void) {
258     device__type_ptr dev_ptr;
259     struct bus__type * bus_ptr;
260
261     /* Try to create a device. */
262     dev_ptr = device__create();
263     if (dev_ptr == NULL)
264       goto err_nodev;
265     /*
266      * Bus devices might be used for booting and should
267      * not be allocated from a paged memory pool.
268      */
269     bus_ptr = wv_mallocz(sizeof *bus_ptr);
270     if (bus_ptr == NULL)
271       goto err_nobus;
272     /* Populate non-zero device defaults. */
273     bus_ptr->device = dev_ptr;
274     bus_ptr->prev_free = dev_ptr->ops.free;
275     dev_ptr->dispatch = bus_dispatch;
276     dev_ptr->ops.create_pdo = bus__create_pdo_;
277     dev_ptr->ops.init = bus__init_;
278     dev_ptr->ops.free = bus__free_;
279     dev_ptr->ext = bus_ptr;
280     dev_ptr->IsBus = TRUE;
281     KeInitializeSpinLock(&bus_ptr->SpinLock);
282
283     return bus_ptr;
284
285     err_nobus:
286
287     device__free(dev_ptr);
288     err_nodev:
289
290     return NULL;
291   }
292
293 /**
294  * Create a bus PDO.
295  *
296  * @v dev               Populate PDO dev. ext. space from these details.
297  * @ret pdo             Points to the new PDO, or is NULL upon failure.
298  *
299  * Returns a Physical Device Object pointer on success, NULL for failure.
300  */
301 static PDEVICE_OBJECT STDCALL bus__create_pdo_(IN device__type_ptr dev) {
302     PDEVICE_OBJECT pdo = NULL;
303     struct bus__type * bus;
304     NTSTATUS status;
305
306     /* Note the bus device needing a PDO. */
307     if (dev == NULL) {
308         DBG("No device passed\n");
309         return NULL;
310       }
311     bus = bus__get(dev);
312     /* Create the PDO. */
313     status = IoCreateDevice(
314         dev->DriverObject,
315         sizeof (driver__dev_ext),
316         &bus->dev_name,
317         FILE_DEVICE_CONTROLLER,
318         FILE_DEVICE_SECURE_OPEN,
319         FALSE,
320         &pdo
321       );
322     if (pdo == NULL) {
323         DBG("IoCreateDevice() failed!\n");
324         goto err_pdo;
325       }
326     /* DosDevice symlink. */
327     if (bus->named) {
328         status = IoCreateSymbolicLink(
329             &bus->dos_dev_name,
330             &bus->dev_name
331           );
332       }
333     if (!NT_SUCCESS(status)) {
334         DBG("IoCreateSymbolicLink");
335         goto err_name;
336       }
337
338     /* Set associations for the bus, device, PDO. */
339     device__set(pdo, dev);
340     dev->Self = bus->PhysicalDeviceObject = pdo;
341
342     /* Set some DEVICE_OBJECT status. */
343     pdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
344     pdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
345     pdo->Flags &= ~DO_DEVICE_INITIALIZING;
346     #ifdef RIS
347     dev->State = Started;
348     #endif
349
350     return pdo;
351
352     err_name:
353
354     IoDeleteDevice(pdo);
355     err_pdo:
356
357     return NULL;
358   }
359
360 /**
361  * Default bus deletion operation.
362  *
363  * @v dev_ptr           Points to the bus device to delete.
364  */
365 static void STDCALL bus__free_(IN device__type_ptr dev_ptr) {
366     struct bus__type * bus_ptr = bus__get(dev_ptr);
367     /* Free the "inherited class". */
368     bus_ptr->prev_free(dev_ptr);
369
370     wv_free(bus_ptr);
371   }
372
373 /**
374  * Get a bus from a device.
375  *
376  * @v dev       A pointer to a device.
377  * @ret         A pointer to the device's associated bus.
378  */
379 extern winvblock__lib_func struct bus__type * bus__get(device__type_ptr dev) {
380     return dev->ext;
381   }