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 /* IRP_MJ_DEVICE_CONTROL dispatcher from bus/dev_ctl.c */
39 extern device__dev_ctl_func bus_dev_ctl__dispatch;
40 /* IRP_MJ_PNP dispatcher from bus/pnp.c */
41 extern device__pnp_func bus_pnp__dispatch;
43 /* Forward declarations. */
44 static device__free_func bus__free_;
45 static device__create_pdo_func bus__create_pdo_;
46 static device__dispatch_func bus__power_;
47 static device__dispatch_func bus__sys_ctl_;
48 static device__pnp_func bus__pnp_dispatch_;
49 static bus__thread_func bus__default_thread_;
52 struct device__irp_mj bus__irp_mj_ = {
55 bus_dev_ctl__dispatch,
56 (device__scsi_func *) 0,
61 * Add a child node to the bus.
63 * @v bus_ptr Points to the bus receiving the child.
64 * @v dev_ptr Points to the child device to add.
65 * @ret TRUE for success, FALSE for failure.
67 winvblock__lib_func winvblock__bool STDCALL bus__add_child(
68 IN OUT struct bus__type * bus_ptr,
69 IN OUT struct device__type * dev_ptr
71 /* The new node's device object. */
72 PDEVICE_OBJECT dev_obj_ptr;
73 /* Walks the child nodes. */
74 struct device__type * walker;
75 winvblock__uint32 dev_num;
78 if ((bus_ptr == NULL) || (dev_ptr == NULL)) {
79 DBG("No bus or no device!\n");
82 /* Create the child device. */
83 dev_obj_ptr = device__create_pdo(dev_ptr);
84 if (dev_obj_ptr == NULL) {
85 DBG("PDO creation failed!\n");
86 device__free(dev_ptr);
90 dev_ptr->Parent = bus_ptr->device->Self;
92 * Initialize the device. For disks, this routine is responsible for
93 * determining the disk's geometry appropriately for AoE/RAM/file disks.
95 dev_ptr->ops.init(dev_ptr);
96 dev_obj_ptr->Flags &= ~DO_DEVICE_INITIALIZING;
97 /* Add the new device's extension to the bus' list of children. */
99 if (bus_ptr->first_child == NULL) {
100 bus_ptr->first_child = dev_ptr;
102 walker = bus_ptr->first_child;
103 /* If the first child device number isn't 0... */
104 if (walker->dev_num) {
105 /* We insert before. */
106 dev_ptr->next_sibling_ptr = walker;
107 bus_ptr->first_child = dev_ptr;
109 while (walker->next_sibling_ptr != NULL) {
110 /* If there's a gap in the device numbers for the bus... */
111 if (walker->dev_num < walker->next_sibling_ptr->dev_num - 1) {
112 /* Insert here, instead of at the end. */
113 dev_num = walker->dev_num + 1;
114 dev_ptr->next_sibling_ptr = walker->next_sibling_ptr;
115 walker->next_sibling_ptr = dev_ptr;
118 walker = walker->next_sibling_ptr;
119 dev_num = walker->dev_num + 1;
121 /* If we haven't already inserted the device... */
122 if (!dev_ptr->next_sibling_ptr) {
123 walker->next_sibling_ptr = dev_ptr;
124 dev_num = walker->dev_num + 1;
128 dev_ptr->dev_num = dev_num;
130 if (bus_ptr->PhysicalDeviceObject != NULL) {
131 IoInvalidateDeviceRelations(
132 bus_ptr->PhysicalDeviceObject,
140 static NTSTATUS STDCALL bus__sys_ctl_(
141 IN struct device__type * dev,
144 struct bus__type * bus = bus__get(dev);
145 PDEVICE_OBJECT lower = bus->LowerDeviceObject;
148 DBG("Passing IRP_MJ_SYSTEM_CONTROL down\n");
149 IoSkipCurrentIrpStackLocation(irp);
150 return IoCallDriver(lower, irp);
152 return driver__complete_irp(irp, 0, STATUS_SUCCESS);
155 static NTSTATUS STDCALL bus__power_(
156 IN struct device__type * dev,
159 struct bus__type * bus = bus__get(dev);
160 PDEVICE_OBJECT lower = bus->LowerDeviceObject;
162 PoStartNextPowerIrp(irp);
164 IoSkipCurrentIrpStackLocation(irp);
165 return PoCallDriver(lower, irp);
167 return driver__complete_irp(irp, 0, STATUS_SUCCESS);
170 NTSTATUS STDCALL bus__get_dev_capabilities(
171 IN PDEVICE_OBJECT DeviceObject,
172 IN PDEVICE_CAPABILITIES DeviceCapabilities
174 IO_STATUS_BLOCK ioStatus;
177 PDEVICE_OBJECT targetObject;
178 PIO_STACK_LOCATION irpStack;
181 RtlZeroMemory(DeviceCapabilities, sizeof (DEVICE_CAPABILITIES));
182 DeviceCapabilities->Size = sizeof (DEVICE_CAPABILITIES);
183 DeviceCapabilities->Version = 1;
184 DeviceCapabilities->Address = -1;
185 DeviceCapabilities->UINumber = -1;
187 KeInitializeEvent(&pnpEvent, NotificationEvent, FALSE);
188 targetObject = IoGetAttachedDeviceReference(DeviceObject);
189 pnpIrp = IoBuildSynchronousFsdRequest(
198 if (pnpIrp == NULL) {
199 status = STATUS_INSUFFICIENT_RESOURCES;
201 pnpIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
202 irpStack = IoGetNextIrpStackLocation(pnpIrp);
203 RtlZeroMemory(irpStack, sizeof (IO_STACK_LOCATION));
204 irpStack->MajorFunction = IRP_MJ_PNP;
205 irpStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
206 irpStack->Parameters.DeviceCapabilities.Capabilities =
208 status = IoCallDriver(targetObject, pnpIrp);
209 if (status == STATUS_PENDING) {
210 KeWaitForSingleObject(
217 status = ioStatus.Status;
220 ObDereferenceObject(targetObject);
224 /* Initialize a bus. */
225 static winvblock__bool STDCALL bus__init_(IN struct device__type * dev) {
232 * @ret bus_ptr The address of a new bus, or NULL for failure.
234 * This function should not be confused with a PDO creation routine, which is
235 * actually implemented for each device type. This routine will allocate a
236 * bus__type, track it in a global list, as well as populate the bus
237 * with default values.
239 winvblock__lib_func struct bus__type * bus__create(void) {
240 struct device__type * dev_ptr;
241 struct bus__type * bus_ptr;
243 /* Try to create a device. */
244 dev_ptr = device__create();
248 * Bus devices might be used for booting and should
249 * not be allocated from a paged memory pool.
251 bus_ptr = wv_mallocz(sizeof *bus_ptr);
254 /* Populate non-zero device defaults. */
255 bus_ptr->device = dev_ptr;
256 bus_ptr->prev_free = dev_ptr->ops.free;
257 bus_ptr->thread = bus__default_thread_;
258 KeInitializeSpinLock(&bus_ptr->SpinLock);
259 KeInitializeSpinLock(&bus_ptr->work_items_lock);
260 InitializeListHead(&bus_ptr->work_items);
261 KeInitializeEvent(&bus_ptr->work_signal, SynchronizationEvent, FALSE);
262 dev_ptr->ops.create_pdo = bus__create_pdo_;
263 dev_ptr->ops.init = bus__init_;
264 dev_ptr->ops.free = bus__free_;
265 dev_ptr->ext = bus_ptr;
266 dev_ptr->irp_mj = &bus__irp_mj_;
267 dev_ptr->IsBus = TRUE;
273 device__free(dev_ptr);
282 * @v dev Populate PDO dev. ext. space from these details.
283 * @ret pdo Points to the new PDO, or is NULL upon failure.
285 * Returns a Physical Device Object pointer on success, NULL for failure.
287 static PDEVICE_OBJECT STDCALL bus__create_pdo_(IN struct device__type * dev) {
288 PDEVICE_OBJECT pdo = NULL;
289 struct bus__type * bus;
292 /* Note the bus device needing a PDO. */
294 DBG("No device passed\n");
298 /* Create the PDO. */
299 status = IoCreateDevice(
301 sizeof (driver__dev_ext),
303 FILE_DEVICE_CONTROLLER,
304 FILE_DEVICE_SECURE_OPEN,
309 DBG("IoCreateDevice() failed!\n");
312 /* DosDevice symlink. */
314 status = IoCreateSymbolicLink(
319 if (!NT_SUCCESS(status)) {
320 DBG("IoCreateSymbolicLink");
324 /* Set associations for the bus, device, PDO. */
325 device__set(pdo, dev);
326 dev->Self = bus->PhysicalDeviceObject = pdo;
328 /* Set some DEVICE_OBJECT status. */
329 pdo->Flags |= DO_DIRECT_IO; /* FIXME? */
330 pdo->Flags |= DO_POWER_INRUSH; /* FIXME? */
331 pdo->Flags &= ~DO_DEVICE_INITIALIZING;
333 dev->State = Started;
347 * Default bus deletion operation.
349 * @v dev_ptr Points to the bus device to delete.
351 static void STDCALL bus__free_(IN struct device__type * dev_ptr) {
352 struct bus__type * bus_ptr = bus__get(dev_ptr);
353 /* Free the "inherited class". */
354 bus_ptr->prev_free(dev_ptr);
360 * Get a bus from a device.
362 * @v dev A pointer to a device.
363 * @ret A pointer to the device's associated bus.
365 extern winvblock__lib_func struct bus__type * bus__get(
366 struct device__type * dev
371 enum bus__work_item_type_ {
372 bus__work_item_type_add_pdo_,
373 bus__work_item_type_del_pdo_,
374 bus__work_item_types_
377 struct bus__work_item_ {
378 LIST_ENTRY list_entry;
379 enum bus__work_item_type_ type;
381 PDEVICE_OBJECT dev_obj;
386 * Add a work item for a bus to process.
388 * @v bus The bus to process the work item.
389 * @v work_item The work item to add.
390 * @ret winvblock__bool TRUE if added, else FALSE
392 static winvblock__bool bus__add_work_item_(
393 struct bus__type * bus,
394 struct bus__work_item_ * work_item
396 ExInterlockedInsertTailList(
398 &work_item->list_entry,
399 &bus->work_items_lock
406 * Get (and dequeue) a work item from a bus' queue.
408 * @v bus The bus processing the work item.
409 * @ret bus__work_item_ The work item, or NULL for an empty queue.
411 static struct bus__work_item_ * bus__get_work_item_(
412 struct bus__type * bus
414 PLIST_ENTRY list_entry;
416 list_entry = ExInterlockedRemoveHeadList(
418 &bus->work_items_lock
423 return CONTAINING_RECORD(list_entry, struct bus__work_item_, list_entry);
427 * Process work items for a bus.
429 * @v bus The bus to process its work items.
431 winvblock__lib_func void bus__process_work_items(struct bus__type * bus) {
432 struct bus__work_item_ * work_item;
434 while (work_item = bus__get_work_item_(bus)) {
435 switch (work_item->type) {
436 case bus__work_item_type_add_pdo_:
437 DBG("Adding PDO...\n");
440 case bus__work_item_type_del_pdo_:
441 DBG("Deleting PDO...\n");
445 DBG("Unknown work item type!\n");
452 * The bus thread wrapper.
454 * @v context The thread context. In our case, it points to
455 * the bus that the thread should use in processing.
457 static void STDCALL bus__thread_(IN void *context) {
458 struct bus__type * bus = context;
460 if (!bus || !bus->thread) {
461 DBG("No bus or no thread!\n");
470 * The default bus thread routine.
472 * @v bus Points to the bus device for the thread to work with.
474 * Note that if you implement your own bus type using this library,
475 * you can override the thread routine with your own. If you do so,
476 * your thread routine should call bus__process_work_items() within
477 * its loop. To start a bus thread, use bus__start_thread()
478 * If you implement your own thread routine, you are also responsible
479 * for freeing the bus.
481 static void STDCALL bus__default_thread_(IN struct bus__type * bus) {
482 LARGE_INTEGER timeout;
484 /* Wake up at least every second. */
485 timeout.QuadPart = -10000000LL;
487 /* When bus::thread is cleared, we shut down. */
488 while (bus->thread) {
491 /* Wait for the work signal or the timeout. */
492 KeWaitForSingleObject(
499 /* Reset the work signal. */
500 KeResetEvent(&bus->work_signal);
502 bus__process_work_items(bus);
503 } /* while bus->alive */
505 bus__free_(bus->device);
510 * Start a bus thread.
512 * @v bus The bus to start a thread for.
513 * @ret NTSTATUS The status of the thread creation operation.
515 * Also see bus__thread_func in the header for details about the prototype
516 * for implementing your own bus thread routine. You set bus::thread to
517 * specify your own thread routine, then call this function to start it.
519 winvblock__lib_func NTSTATUS bus__start_thread(
520 struct bus__type * bus
522 OBJECT_ATTRIBUTES obj_attrs;
523 HANDLE thread_handle;
526 DBG("No bus specified!\n");
527 return STATUS_INVALID_PARAMETER;
530 InitializeObjectAttributes(
537 return PsCreateSystemThread(