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/
6 * This file is part of WinVBlock, derived from WinAoE.
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.
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.
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/>.
30 #include "winvblock.h"
31 #include "wv_stdlib.h"
38 #include "bus_dev_ctl.h"
42 static PDEVICE_OBJECT bus__boot_fdo_ = NULL;
43 static LIST_ENTRY bus__list_;
44 static KSPIN_LOCK bus__list_lock_;
46 /* Forward declarations. */
47 static device__free_func bus__free_;
48 static device__create_pdo_decl(bus__create_pdo_);
51 * Tear down the global, bus-common environment.
53 void bus__finalize(void) {
54 UNICODE_STRING DosDeviceName;
59 L"\\DosDevices\\" winvblock__literal_w
61 IoDeleteSymbolicLink(&DosDeviceName);
62 bus__boot_fdo_ = NULL;
67 * Add a child node to the bus.
69 * @v bus_ptr Points to the bus receiving the child.
70 * @v dev_ptr Points to the child device to add.
71 * @ret TRUE for success, FALSE for failure.
73 winvblock__lib_func winvblock__bool STDCALL bus__add_child(
74 IN OUT struct bus__type * bus_ptr,
75 IN OUT device__type_ptr dev_ptr
78 * @v dev_obj_ptr The new node's device object.
79 * @v walker Walks the child nodes.
81 PDEVICE_OBJECT dev_obj_ptr;
82 device__type_ptr walker;
85 if ((bus_ptr == NULL) || (dev_ptr == NULL)) {
86 DBG("No bus or no device!\n");
89 /* Create the child device. */
90 dev_obj_ptr = device__create_pdo(dev_ptr);
91 if (dev_obj_ptr == NULL) {
92 DBG("PDO creation failed!\n");
93 device__free(dev_ptr);
97 dev_ptr->Parent = bus_ptr->device->Self;
99 * Initialize the device. For disks, this routine is responsible for
100 * determining the disk's geometry appropriately for AoE/RAM/file disks.
102 dev_ptr->ops.init(dev_ptr);
103 dev_obj_ptr->Flags &= ~DO_DEVICE_INITIALIZING;
104 /* Add the new device's extension to the bus' list of children. */
105 if (bus_ptr->first_child_ptr == NULL) {
106 bus_ptr->first_child_ptr = dev_ptr;
108 walker = bus_ptr->first_child_ptr;
109 while (walker->next_sibling_ptr != NULL)
110 walker = walker->next_sibling_ptr;
111 walker->next_sibling_ptr = dev_ptr;
114 if (bus_ptr->PhysicalDeviceObject != NULL) {
115 IoInvalidateDeviceRelations(
116 bus_ptr->PhysicalDeviceObject,
124 static NTSTATUS STDCALL sys_ctl(
125 IN PDEVICE_OBJECT DeviceObject,
127 IN PIO_STACK_LOCATION Stack,
128 IN struct _device__type * dev_ptr,
129 OUT winvblock__bool_ptr completion_ptr
131 struct bus__type * bus_ptr = bus__get(dev_ptr);
133 IoSkipCurrentIrpStackLocation(Irp);
134 *completion_ptr = TRUE;
135 return IoCallDriver(bus_ptr->LowerDeviceObject, Irp);
138 static NTSTATUS STDCALL power(
139 IN PDEVICE_OBJECT DeviceObject,
141 IN PIO_STACK_LOCATION Stack,
142 IN struct _device__type * dev_ptr,
143 OUT winvblock__bool_ptr completion_ptr
145 struct bus__type * bus_ptr = bus__get(dev_ptr);
146 PoStartNextPowerIrp(Irp);
147 IoSkipCurrentIrpStackLocation(Irp);
148 *completion_ptr = TRUE;
149 return PoCallDriver(bus_ptr->LowerDeviceObject, Irp);
152 NTSTATUS STDCALL bus__get_dev_capabilities(
153 IN PDEVICE_OBJECT DeviceObject,
154 IN PDEVICE_CAPABILITIES DeviceCapabilities
156 IO_STATUS_BLOCK ioStatus;
159 PDEVICE_OBJECT targetObject;
160 PIO_STACK_LOCATION irpStack;
163 RtlZeroMemory(DeviceCapabilities, sizeof (DEVICE_CAPABILITIES));
164 DeviceCapabilities->Size = sizeof (DEVICE_CAPABILITIES);
165 DeviceCapabilities->Version = 1;
166 DeviceCapabilities->Address = -1;
167 DeviceCapabilities->UINumber = -1;
169 KeInitializeEvent(&pnpEvent, NotificationEvent, FALSE);
170 targetObject = IoGetAttachedDeviceReference(DeviceObject);
171 pnpIrp = IoBuildSynchronousFsdRequest(
180 if (pnpIrp == NULL) {
181 status = STATUS_INSUFFICIENT_RESOURCES;
183 pnpIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
184 irpStack = IoGetNextIrpStackLocation(pnpIrp);
185 RtlZeroMemory(irpStack, sizeof (IO_STACK_LOCATION));
186 irpStack->MajorFunction = IRP_MJ_PNP;
187 irpStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
188 irpStack->Parameters.DeviceCapabilities.Capabilities =
190 status = IoCallDriver(targetObject, pnpIrp);
191 if (status == STATUS_PENDING) {
192 KeWaitForSingleObject(
199 status = ioStatus.Status;
202 ObDereferenceObject(targetObject);
206 static NTSTATUS STDCALL attach_fdo(
207 IN PDRIVER_OBJECT DriverObject,
208 IN PDEVICE_OBJECT PhysicalDeviceObject
211 struct bus__type * bus_ptr;
213 PUNICODE_STRING dev_name = NULL;
214 PDEVICE_OBJECT fdo = NULL;
215 device__type_ptr dev_ptr;
218 /* Search for the associated bus. */
219 walker = bus__list_.Flink;
220 while (walker != &bus__list_) {
221 bus_ptr = CONTAINING_RECORD(walker, struct bus__type, tracking);
222 if (bus_ptr->PhysicalDeviceObject == PhysicalDeviceObject)
224 walker = walker->Flink;
226 /* If we get back to the list head, we need to create a bus. */
227 if (walker == &bus__list_) {
228 DBG("No bus->PDO association. Creating a new bus\n");
229 bus_ptr = bus__create();
230 if (bus_ptr == NULL) {
232 "Could not create a bus!\n",
233 STATUS_INSUFFICIENT_RESOURCES
237 /* This bus might have an associated name. */
239 dev_name = &bus_ptr->dev_name;
240 /* Create the bus FDO. */
241 status = IoCreateDevice(
243 sizeof (driver__dev_ext),
245 FILE_DEVICE_CONTROLLER,
246 FILE_DEVICE_SECURE_OPEN,
250 if (!NT_SUCCESS(status)) {
251 device__free(bus_ptr->device);
252 return Error("IoCreateDevice", status);
254 /* DosDevice symlink. */
255 if (bus_ptr->named) {
256 status = IoCreateSymbolicLink(
257 &bus_ptr->dos_dev_name,
261 if (!NT_SUCCESS(status)) {
263 device__free(bus_ptr->device);
264 return Error("IoCreateSymbolicLink", status);
267 /* Set associations for the bus, device, FDO, PDO. */
268 dev_ptr = bus_ptr->device;
269 device__set(fdo, dev_ptr);
272 bus_ptr->PhysicalDeviceObject = PhysicalDeviceObject;
273 fdo->Flags |= DO_DIRECT_IO; /* FIXME? */
274 fdo->Flags |= DO_POWER_INRUSH; /* FIXME? */
275 /* Add the bus to the device tree. */
276 if (PhysicalDeviceObject != NULL) {
277 bus_ptr->LowerDeviceObject = IoAttachDeviceToDeviceStack(
281 if (bus_ptr->LowerDeviceObject == NULL) {
283 device__free(bus_ptr->device);
284 return Error("IoAttachDeviceToDeviceStack", STATUS_NO_SUCH_DEVICE);
287 fdo->Flags &= ~DO_DEVICE_INITIALIZING;
289 dev_ptr->State = Started;
292 return STATUS_SUCCESS;
295 /* Bus dispatch routine. */
296 static NTSTATUS STDCALL bus_dispatch(
297 IN PDEVICE_OBJECT dev,
301 winvblock__bool completion = FALSE;
302 static const irp__handling handling_table[] = {
304 * Major, minor, any major?, any minor?, handler
305 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
306 * Note that the fall-through case must come FIRST!
307 * Why? It sets completion to true, so others won't be called.
309 { 0, 0, TRUE, TRUE, driver__not_supported },
310 { IRP_MJ_CLOSE, 0, FALSE, TRUE, driver__create_close },
311 { IRP_MJ_CREATE, 0, FALSE, TRUE, driver__create_close },
312 { IRP_MJ_SYSTEM_CONTROL, 0, FALSE, TRUE, sys_ctl },
313 { IRP_MJ_POWER, 0, FALSE, TRUE, power },
314 { IRP_MJ_DEVICE_CONTROL, 0, FALSE, TRUE, bus_dev_ctl__dispatch },
315 { IRP_MJ_PNP, 0, FALSE, TRUE, bus_pnp__simple },
317 IRP_MN_START_DEVICE, FALSE, FALSE, bus_pnp__start_dev },
319 IRP_MN_REMOVE_DEVICE, FALSE, FALSE, bus_pnp__remove_dev },
321 IRP_MN_QUERY_DEVICE_RELATIONS, FALSE, FALSE,
322 bus_pnp__query_dev_relations },
325 /* Try registered mini IRP handling tables first. Deprecated. */
326 status = irp__process(
329 IoGetCurrentIrpStackLocation(irp),
333 /* Fall through to the bus defaults, if needed. */
334 if (status == STATUS_NOT_SUPPORTED && !completion)
335 status = irp__process_with_table(
339 sizeof handling_table,
343 if (status != STATUS_PENDING)
344 Debug_IrpEnd(irp, status);
353 * @ret bus_ptr The address of a new bus, or NULL for failure.
355 * See the header file for additional details.
357 winvblock__lib_func struct bus__type * bus__create(void) {
358 device__type_ptr dev_ptr;
359 struct bus__type * bus_ptr;
361 /* Try to create a device. */
362 dev_ptr = device__create();
366 * Bus devices might be used for booting and should
367 * not be allocated from a paged memory pool.
369 bus_ptr = wv_mallocz(sizeof *bus_ptr);
372 /* Track the new bus in our global list. */
373 ExInterlockedInsertTailList(&bus__list_, &bus_ptr->tracking, &bus__list_lock_);
374 /* Populate non-zero device defaults. */
375 bus_ptr->device = dev_ptr;
376 bus_ptr->prev_free = dev_ptr->ops.free;
377 dev_ptr->dispatch = bus_dispatch;
378 dev_ptr->ops.create_pdo = bus__create_pdo_;
379 dev_ptr->ops.free = bus__free_;
380 dev_ptr->ext = bus_ptr;
381 dev_ptr->IsBus = TRUE;
382 KeInitializeSpinLock(&bus_ptr->SpinLock);
388 device__free(dev_ptr);
397 * @v dev_ptr Populate PDO dev. ext. space from these details.
398 * @ret pdo_ptr Points to the new PDO, or is NULL upon failure.
400 * Returns a Physical Device Object pointer on success, NULL for failure.
402 static device__create_pdo_decl(bus__create_pdo_) {
403 PDEVICE_OBJECT pdo_ptr = NULL;
404 struct bus__type * bus_ptr;
407 /* Note the bus device needing a PDO. */
408 if (dev_ptr == NULL) {
409 DBG("No device passed\n");
412 bus_ptr = bus__get(dev_ptr);
413 /* Always create the root-enumerated bus device. */
414 IoReportDetectedDevice(
416 InterfaceTypeUndefined,
424 if (pdo_ptr == NULL) {
425 DBG("IoReportDetectedDevice() went wrong!\n");
428 /* We have a PDO. Note it in the bus. */
429 bus_ptr->PhysicalDeviceObject = pdo_ptr;
431 * Attach FDO to PDO. *sigh* Note that we do not own the PDO,
432 * so we must associate the bus structure with the FDO, instead.
433 * Consider that the AddDevice()/attach_fdo() routine takes two parameters,
434 * neither of which are guaranteed to be owned by a caller in this driver.
435 * Since attach_fdo() associates a bus device, it is forced to walk our
436 * global list of bus devices. Otherwise, it would be easy to pass it here
438 status = attach_fdo(driver__obj_ptr, pdo_ptr);
439 if (!NT_SUCCESS(status)) {
440 DBG("attach_fdo() went wrong!\n");
447 IoDeleteDevice(pdo_ptr);
452 * Initialize the global, bus-common environment.
454 * @ret ntstatus STATUS_SUCCESS or the NTSTATUS for a failure.
456 NTSTATUS bus__module_init(void) {
457 struct bus__type * boot_bus_ptr;
459 /* Initialize the global list of devices. */
460 InitializeListHead(&bus__list_);
461 KeInitializeSpinLock(&bus__list_lock_);
462 /* We handle AddDevice call-backs for the driver. */
463 driver__obj_ptr->DriverExtension->AddDevice = attach_fdo;
464 /* Establish the boot bus (required for booting from a WinVBlock disk). */
465 boot_bus_ptr = bus__create();
466 if (boot_bus_ptr == NULL)
467 return STATUS_UNSUCCESSFUL;
468 /* In booting, he has a name. His name is WinVBlock. */
469 RtlInitUnicodeString(
470 &boot_bus_ptr->dev_name,
471 L"\\Device\\" winvblock__literal_w
473 RtlInitUnicodeString(
474 &boot_bus_ptr->dos_dev_name,
475 L"\\DosDevices\\" winvblock__literal_w
477 boot_bus_ptr->named = TRUE;
478 /* Create the PDO, which also attaches the FDO *sigh*. */
479 if (bus__create_pdo_(boot_bus_ptr->device) == NULL) {
480 return STATUS_UNSUCCESSFUL;
482 bus__boot_fdo_ = boot_bus_ptr->device->Self;
484 return STATUS_SUCCESS;
488 * Default bus deletion operation.
490 * @v dev_ptr Points to the bus device to delete.
492 static void STDCALL bus__free_(IN device__type_ptr dev_ptr) {
493 struct bus__type * bus_ptr = bus__get(dev_ptr);
494 /* Free the "inherited class". */
495 bus_ptr->prev_free(dev_ptr);
497 * Track the bus deletion in our global list. Unfortunately,
498 * for now we have faith that a bus won't be deleted twice and
499 * result in a race condition. Something to keep in mind...
501 ExInterlockedRemoveHeadList(bus_ptr->tracking.Blink, &bus__list_lock_);
507 * Get a pointer to the boot bus device.
509 * @ret A pointer to the boot bus, or NULL.
511 winvblock__lib_func struct bus__type * bus__boot(void) {
512 if (!bus__boot_fdo_) {
513 DBG("No boot bus device!\n");
516 return bus__get(device__get(bus__boot_fdo_));
520 * Get a bus from a device.
522 * @v dev A pointer to a device.
524 extern winvblock__lib_func struct bus__type * bus__get(device__type_ptr dev) {