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