[bus] Let a threaded bus free itself
[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 "driver.h"
34 #include "device.h"
35 #include "bus.h"
36 #include "debug.h"
37
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;
42
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_;
50
51 /* Globals. */
52 struct device__irp_mj bus__irp_mj_ = {
53     bus__power_,
54     bus__sys_ctl_,
55     bus_dev_ctl__dispatch,
56     (device__scsi_func *) 0,
57     bus_pnp__dispatch,
58   };
59
60 /**
61  * Add a child node to the bus.
62  *
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.
66  */
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
70   ) {
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;
76
77     DBG("Entry\n");
78     if ((bus_ptr == NULL) || (dev_ptr == NULL)) {
79         DBG("No bus or no device!\n");
80         return FALSE;
81       }
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);
87         return FALSE;
88       }
89
90     dev_ptr->Parent = bus_ptr->device->Self;
91     /*
92      * Initialize the device.  For disks, this routine is responsible for
93      * determining the disk's geometry appropriately for AoE/RAM/file disks.
94      */
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. */
98     dev_num = 0;
99     if (bus_ptr->first_child == NULL) {
100         bus_ptr->first_child = dev_ptr;
101       } else {
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;
108           } else {
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;
116                     break;
117                   }
118                 walker = walker->next_sibling_ptr;
119                 dev_num = walker->dev_num + 1;
120               }
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;
125               }
126           }
127       }
128     dev_ptr->dev_num = dev_num;
129     bus_ptr->Children++;
130     if (bus_ptr->PhysicalDeviceObject != NULL) {
131         IoInvalidateDeviceRelations(
132             bus_ptr->PhysicalDeviceObject,
133             BusRelations
134           );
135       }
136     DBG("Exit\n");
137     return TRUE;
138   }
139
140 static NTSTATUS STDCALL bus__sys_ctl_(
141     IN struct device__type * dev,
142     IN PIRP irp
143   ) {
144     struct bus__type * bus = bus__get(dev);
145     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
146
147     if (lower) {
148         DBG("Passing IRP_MJ_SYSTEM_CONTROL down\n");
149         IoSkipCurrentIrpStackLocation(irp);
150         return IoCallDriver(lower, irp);
151       }
152     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
153   }
154
155 static NTSTATUS STDCALL bus__power_(
156     IN struct device__type * dev,
157     IN PIRP irp
158   ) {
159     struct bus__type * bus = bus__get(dev);
160     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
161
162     PoStartNextPowerIrp(irp);
163     if (lower) {
164         IoSkipCurrentIrpStackLocation(irp);
165         return PoCallDriver(lower, irp);
166       }
167     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
168   }
169
170 NTSTATUS STDCALL bus__get_dev_capabilities(
171     IN PDEVICE_OBJECT DeviceObject,
172     IN PDEVICE_CAPABILITIES DeviceCapabilities
173   ) {
174     IO_STATUS_BLOCK ioStatus;
175     KEVENT pnpEvent;
176     NTSTATUS status;
177     PDEVICE_OBJECT targetObject;
178     PIO_STACK_LOCATION irpStack;
179     PIRP pnpIrp;
180
181     RtlZeroMemory(DeviceCapabilities, sizeof (DEVICE_CAPABILITIES));
182     DeviceCapabilities->Size = sizeof (DEVICE_CAPABILITIES);
183     DeviceCapabilities->Version = 1;
184     DeviceCapabilities->Address = -1;
185     DeviceCapabilities->UINumber = -1;
186
187     KeInitializeEvent(&pnpEvent, NotificationEvent, FALSE);
188     targetObject = IoGetAttachedDeviceReference(DeviceObject);
189     pnpIrp = IoBuildSynchronousFsdRequest(
190         IRP_MJ_PNP,
191         targetObject,
192         NULL,
193         0,
194         NULL,
195         &pnpEvent,
196         &ioStatus
197       );
198     if (pnpIrp == NULL) {
199         status = STATUS_INSUFFICIENT_RESOURCES;
200       } else {
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 =
207           DeviceCapabilities;
208         status = IoCallDriver(targetObject, pnpIrp);
209         if (status == STATUS_PENDING) {
210             KeWaitForSingleObject(
211                 &pnpEvent,
212                 Executive,
213                 KernelMode,
214                 FALSE,
215                 NULL
216               );
217             status = ioStatus.Status;
218           }
219       }
220     ObDereferenceObject(targetObject);
221     return status;
222   }
223
224 /* Initialize a bus. */
225 static winvblock__bool STDCALL bus__init_(IN struct device__type * dev) {
226     return TRUE;
227   }
228
229 /**
230  * Create a new bus.
231  *
232  * @ret bus_ptr         The address of a new bus, or NULL for failure.
233  *
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.
238  */
239 winvblock__lib_func struct bus__type * bus__create(void) {
240     struct device__type * dev_ptr;
241     struct bus__type * bus_ptr;
242
243     /* Try to create a device. */
244     dev_ptr = device__create();
245     if (dev_ptr == NULL)
246       goto err_nodev;
247     /*
248      * Bus devices might be used for booting and should
249      * not be allocated from a paged memory pool.
250      */
251     bus_ptr = wv_mallocz(sizeof *bus_ptr);
252     if (bus_ptr == NULL)
253       goto err_nobus;
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;
268
269     return bus_ptr;
270
271     err_nobus:
272
273     device__free(dev_ptr);
274     err_nodev:
275
276     return NULL;
277   }
278
279 /**
280  * Create a bus PDO.
281  *
282  * @v dev               Populate PDO dev. ext. space from these details.
283  * @ret pdo             Points to the new PDO, or is NULL upon failure.
284  *
285  * Returns a Physical Device Object pointer on success, NULL for failure.
286  */
287 static PDEVICE_OBJECT STDCALL bus__create_pdo_(IN struct device__type * dev) {
288     PDEVICE_OBJECT pdo = NULL;
289     struct bus__type * bus;
290     NTSTATUS status;
291
292     /* Note the bus device needing a PDO. */
293     if (dev == NULL) {
294         DBG("No device passed\n");
295         return NULL;
296       }
297     bus = bus__get(dev);
298     /* Create the PDO. */
299     status = IoCreateDevice(
300         dev->DriverObject,
301         sizeof (driver__dev_ext),
302         &bus->dev_name,
303         FILE_DEVICE_CONTROLLER,
304         FILE_DEVICE_SECURE_OPEN,
305         FALSE,
306         &pdo
307       );
308     if (pdo == NULL) {
309         DBG("IoCreateDevice() failed!\n");
310         goto err_pdo;
311       }
312     /* DosDevice symlink. */
313     if (bus->named) {
314         status = IoCreateSymbolicLink(
315             &bus->dos_dev_name,
316             &bus->dev_name
317           );
318       }
319     if (!NT_SUCCESS(status)) {
320         DBG("IoCreateSymbolicLink");
321         goto err_name;
322       }
323
324     /* Set associations for the bus, device, PDO. */
325     device__set(pdo, dev);
326     dev->Self = bus->PhysicalDeviceObject = pdo;
327
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;
332     #ifdef RIS
333     dev->State = Started;
334     #endif
335
336     return pdo;
337
338     err_name:
339
340     IoDeleteDevice(pdo);
341     err_pdo:
342
343     return NULL;
344   }
345
346 /**
347  * Default bus deletion operation.
348  *
349  * @v dev_ptr           Points to the bus device to delete.
350  */
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);
355
356     wv_free(bus_ptr);
357   }
358
359 /**
360  * Get a bus from a device.
361  *
362  * @v dev       A pointer to a device.
363  * @ret         A pointer to the device's associated bus.
364  */
365 extern winvblock__lib_func struct bus__type * bus__get(
366     struct device__type * dev
367   ) {
368     return dev->ext;
369   }
370
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_
375   };
376
377 struct bus__work_item_ {
378     LIST_ENTRY list_entry;
379     enum bus__work_item_type_ type;
380     union {
381         PDEVICE_OBJECT dev_obj;
382       } context;
383   };
384
385 /**
386  * Add a work item for a bus to process.
387  *
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
391  */
392 static winvblock__bool bus__add_work_item_(
393     struct bus__type * bus,
394     struct bus__work_item_ * work_item
395   ) {
396     ExInterlockedInsertTailList(
397         &bus->work_items,
398         &work_item->list_entry,
399         &bus->work_items_lock
400       );
401
402     return TRUE;
403   }
404
405 /**
406  * Get (and dequeue) a work item from a bus' queue.
407  *
408  * @v bus                       The bus processing the work item.
409  * @ret bus__work_item_         The work item, or NULL for an empty queue.
410  */
411 static struct bus__work_item_ * bus__get_work_item_(
412     struct bus__type * bus
413   ) {
414     PLIST_ENTRY list_entry;
415
416     list_entry = ExInterlockedRemoveHeadList(
417         &bus->work_items,
418         &bus->work_items_lock
419       );
420     if (!list_entry)
421       return NULL;
422
423     return CONTAINING_RECORD(list_entry, struct bus__work_item_, list_entry);
424   }
425
426 /**
427  * Process work items for a bus.
428  *
429  * @v bus               The bus to process its work items.
430  */
431 winvblock__lib_func void bus__process_work_items(struct bus__type * bus) {
432     struct bus__work_item_ * work_item;
433
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");
438               break;
439
440             case bus__work_item_type_del_pdo_:
441               DBG("Deleting PDO...\n");
442               break;
443
444             default:
445               DBG("Unknown work item type!\n");
446           }
447       }
448     return;
449   }
450
451 /* The device__type::ops.free implementation for a threaded bus. */
452 static void STDCALL bus__thread_free_(IN struct device__type * dev) {
453     struct bus__type * bus = bus__get(dev);
454
455     bus->thread = (bus__thread_func *) 0;
456     KeSetEvent(&bus->work_signal, 0, FALSE);
457     return;
458   }
459
460 /**
461  * The bus thread wrapper.
462  *
463  * @v context           The thread context.  In our case, it points to
464  *                      the bus that the thread should use in processing.
465  */
466 static void STDCALL bus__thread_(IN void *context) {
467     struct bus__type * bus = context;
468
469     if (!bus || !bus->thread) {
470         DBG("No bus or no thread!\n");
471         return;
472       }
473
474     bus->thread(bus);
475     return;
476   }
477
478 /**
479  * The default bus thread routine.
480  *
481  * @v bus       Points to the bus device for the thread to work with.
482  *
483  * Note that if you implement your own bus type using this library,
484  * you can override the thread routine with your own.  If you do so,
485  * your thread routine should call bus__process_work_items() within
486  * its loop.  To start a bus thread, use bus__start_thread()
487  * If you implement your own thread routine, you are also responsible
488  * for freeing the bus.
489  */
490 static void STDCALL bus__default_thread_(IN struct bus__type * bus) {
491     LARGE_INTEGER timeout;
492
493     /* Wake up at least every second. */
494     timeout.QuadPart = -10000000LL;
495
496     /* Hook device__type::ops.free() */
497     bus->device->ops.free = bus__thread_free_;
498
499     /* When bus::thread is cleared, we shut down. */
500     while (bus->thread) {
501         DBG("Alive.\n");
502
503         /* Wait for the work signal or the timeout. */
504         KeWaitForSingleObject(
505             &bus->work_signal,
506             Executive,
507             KernelMode,
508             FALSE,
509             &timeout
510           );
511         /* Reset the work signal. */
512         KeResetEvent(&bus->work_signal);
513
514         bus__process_work_items(bus);
515       } /* while bus->alive */
516
517     bus__free_(bus->device);
518     return;
519   }
520
521 /**
522  * Start a bus thread.
523  *
524  * @v bus               The bus to start a thread for.
525  * @ret NTSTATUS        The status of the thread creation operation.
526  *
527  * Also see bus__thread_func in the header for details about the prototype
528  * for implementing your own bus thread routine.  You set bus::thread to
529  * specify your own thread routine, then call this function to start it.
530  */
531 winvblock__lib_func NTSTATUS bus__start_thread(
532     struct bus__type * bus
533   ) {
534     OBJECT_ATTRIBUTES obj_attrs;
535     HANDLE thread_handle;
536
537     if (!bus) {
538         DBG("No bus specified!\n");
539         return STATUS_INVALID_PARAMETER;
540       }
541
542     InitializeObjectAttributes(
543         &obj_attrs,
544         NULL,
545         OBJ_KERNEL_HANDLE,
546         NULL,
547         NULL
548       );
549     return PsCreateSystemThread(
550         &thread_handle,
551         THREAD_ALL_ACCESS,
552         &obj_attrs,
553         NULL,
554         NULL,
555         bus__thread_,
556         bus
557       );
558   }