[driver,bus] Move WvBusAddChild as WvDriverBusAddDev
[people/sha0/winvblock.git] / src / winvblock / driver.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  * Driver specifics.
26  */
27
28 #include <stdio.h>
29 #include <ntddk.h>
30 #include <scsi.h>
31
32 #include "winvblock.h"
33 #include "wv_stdlib.h"
34 #include "wv_string.h"
35 #include "portable.h"
36 #include "driver.h"
37 #include "device.h"
38 #include "disk.h"
39 #include "registry.h"
40 #include "mount.h"
41 #include "bus.h"
42 #include "filedisk.h"
43 #include "ramdisk.h"
44 #include "debug.h"
45
46 /* Names for the main bus. */
47 #define WV_M_BUS_NAME_ (L"\\Device\\" winvblock__literal_w)
48 #define WV_M_BUS_DOSNAME_ (L"\\DosDevices\\" winvblock__literal_w)
49
50 /* Exported. */
51 PDRIVER_OBJECT WvDriverObj = NULL;
52
53 /* Globals. */
54 static void * WvDriverStateHandle_;
55 static winvblock__bool WvDriverStarted_ = FALSE;
56 static PDEVICE_OBJECT WvDriverBusFdo_ = NULL;
57 static KSPIN_LOCK WvDriverBusFdoLock_;
58 static UNICODE_STRING WvDriverBusName_ = {
59     sizeof WV_M_BUS_NAME_,
60     sizeof WV_M_BUS_NAME_,
61     WV_M_BUS_NAME_
62   };
63 static UNICODE_STRING WvDriverBusDosname_ = {
64     sizeof WV_M_BUS_DOSNAME_,
65     sizeof WV_M_BUS_DOSNAME_,
66     WV_M_BUS_DOSNAME_
67   };
68 /* The main bus. */
69 static WV_S_BUS_T WvDriverBus_ = {0};
70 /* Contains TXTSETUP.SIF/BOOT.INI-style OsLoadOptions parameters. */
71 static LPWSTR WvDriverOsLoadOpts_ = NULL;
72
73 /* Forward declarations. */
74 static driver__dispatch_func driver__dispatch_not_supported_;
75 static driver__dispatch_func driver__dispatch_power_;
76 static driver__dispatch_func driver__dispatch_create_close_;
77 static driver__dispatch_func driver__dispatch_sys_ctl_;
78 static driver__dispatch_func driver__dispatch_dev_ctl_;
79 static driver__dispatch_func driver__dispatch_scsi_;
80 static driver__dispatch_func driver__dispatch_pnp_;
81 static void STDCALL driver__unload_(IN PDRIVER_OBJECT);
82 static WV_F_DEV_DISPATCH WvDriverBusSysCtl_;
83 static WV_F_DEV_DISPATCH WvDriverBusPower_;
84 static WV_F_DEV_PNP WvDriverBusPnp_;
85
86 static LPWSTR STDCALL get_opt(IN LPWSTR opt_name) {
87     LPWSTR our_opts, the_opt;
88     WCHAR our_sig[] = L"WINVBLOCK=";
89     /* To produce constant integer expressions. */
90     enum {
91         our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
92         our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
93       };
94     size_t opt_name_len, opt_name_len_bytes;
95
96     if (!WvDriverOsLoadOpts_ || !opt_name)
97       return NULL;
98
99     /* Find /WINVBLOCK= options. */
100     our_opts = WvDriverOsLoadOpts_;
101     while (*our_opts != L'\0') {
102         if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
103             our_opts++;
104             continue;
105           }
106         our_opts += our_sig_len;
107         break;
108       }
109
110     /* Search for the specific option. */
111     the_opt = our_opts;
112     opt_name_len = wcslen(opt_name);
113     opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
114     while (*the_opt != L'\0' && *the_opt != L' ') {
115         if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
116             while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
117               the_opt++;
118             continue;
119           }
120         the_opt += opt_name_len;
121         break;
122       }
123
124     if (*the_opt == L'\0' || *the_opt == L' ')
125       return NULL;
126
127     /* Next should come "=". */
128     if (*the_opt != L'=')
129       return NULL;
130
131     /*
132      * And finally our option's value.  The caller needs
133      * to worry about looking past the end of the option.
134      */
135     the_opt++;
136     if (*the_opt == L'\0' || *the_opt == L' ')
137       return NULL;
138     return the_opt;
139   }
140
141 static NTSTATUS STDCALL driver__attach_fdo_(
142     IN PDRIVER_OBJECT DriverObject,
143     IN PDEVICE_OBJECT PhysicalDeviceObject
144   ) {
145     KIRQL irql;
146     NTSTATUS status;
147     PLIST_ENTRY walker;
148     PUNICODE_STRING dev_name = NULL;
149     PDEVICE_OBJECT fdo = NULL;
150
151     DBG("Entry\n");
152     /* Do we alreay have our main bus? */
153     if (WvDriverBusFdo_) {
154         DBG("Already have the main bus.  Refusing.\n");
155         status = STATUS_NOT_SUPPORTED;
156         goto err_already_established;
157       }
158     /* Initialize the bus. */
159     WvBusInit(&WvDriverBus_);
160     /* Create the bus FDO. */
161     status = IoCreateDevice(
162         DriverObject,
163         sizeof (driver__dev_ext),
164         &WvDriverBusName_,
165         FILE_DEVICE_CONTROLLER,
166         FILE_DEVICE_SECURE_OPEN,
167         FALSE,
168         &fdo
169       );
170     if (!NT_SUCCESS(status)) {
171         DBG("IoCreateDevice() failed!\n");
172         goto err_fdo;
173       }
174     /* DosDevice symlink. */
175     status = IoCreateSymbolicLink(
176         &WvDriverBusDosname_,
177         &WvDriverBusName_
178       );
179     if (!NT_SUCCESS(status)) {
180         DBG("IoCreateSymbolicLink() failed!\n");
181         goto err_dos_symlink;
182       }
183     /* Set associations for the bus, device, FDO, PDO. */
184     WvDevForDevObj(fdo, &WvDriverBus_.Dev);
185     WvDriverBus_.Dev.Self = WvDriverBus_.Fdo = fdo;
186     WvDriverBus_.Dev.IsBus = TRUE;
187     WvDriverBus_.Dev.IrpMj->SysCtl = WvDriverBusSysCtl_;
188     WvDriverBus_.Dev.IrpMj->Power = WvDriverBusPower_;
189     WvDriverBus_.Dev.IrpMj->Pnp = WvDriverBusPnp_;
190     WvDriverBus_.PhysicalDeviceObject = PhysicalDeviceObject;
191     fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
192     fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
193     /* Attach the FDO to the PDO. */
194     WvDriverBus_.LowerDeviceObject = IoAttachDeviceToDeviceStack(
195         fdo,
196         PhysicalDeviceObject
197       );
198     if (WvDriverBus_.LowerDeviceObject == NULL) {
199         status = STATUS_NO_SUCH_DEVICE;
200         DBG("IoAttachDeviceToDeviceStack() failed!\n");
201         goto err_attach;
202       }
203     status = WvBusStartThread(&WvDriverBus_);
204     if (!NT_SUCCESS(status)) {
205         DBG("Couldn't start bus thread!\n");
206         goto err_thread;
207       }
208     /* Ok! */
209     KeAcquireSpinLock(&WvDriverBusFdoLock_, &irql);
210     if (WvDriverBusFdo_) {
211         KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
212         DBG("Beaten to it!\n");
213         status = STATUS_NOT_SUPPORTED;
214         goto err_race_failed;
215       }
216     fdo->Flags &= ~DO_DEVICE_INITIALIZING;
217     #ifdef RIS
218     WvDriverBus_.Dev.State = Started;
219     #endif
220     WvDriverBusFdo_ = fdo;
221     KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
222     DBG("Exit\n");
223     return STATUS_SUCCESS;
224
225     err_race_failed:
226
227     err_thread:
228
229     err_attach:
230
231     IoDeleteSymbolicLink(&WvDriverBusDosname_);
232     err_dos_symlink:
233
234     IoDeleteDevice(fdo);
235     err_fdo:
236
237     err_already_established:
238
239     DBG("Exit with failure\n");
240     return status;
241   }
242
243 /* Create the root-enumerated, main bus device. */
244 NTSTATUS STDCALL driver__create_bus_(void) {
245     WV_SP_BUS_T bus;
246     NTSTATUS status;
247     PDEVICE_OBJECT bus_pdo = NULL;
248
249     /* Create the PDO. */
250     IoReportDetectedDevice(
251         WvDriverObj,
252         InterfaceTypeUndefined,
253         -1,
254         -1,
255         NULL,
256         NULL,
257         FALSE,
258         &bus_pdo
259       );
260     if (bus_pdo == NULL) {
261         DBG("IoReportDetectedDevice() went wrong!  Exiting.\n");
262         status = STATUS_UNSUCCESSFUL;
263         goto err_driver_bus;
264       }
265     /* Attach FDO to PDO. */
266     status = driver__attach_fdo_(WvDriverObj, bus_pdo);
267     if (!NT_SUCCESS(status)) {
268         DBG("driver__attach_fdo_() went wrong!\n");
269         goto err_add_dev;
270       }
271     /* PDO created, FDO attached.  All done. */
272     return STATUS_SUCCESS;
273
274     err_add_dev:
275
276     IoDeleteDevice(bus_pdo);
277     err_driver_bus:
278
279     return status;
280   }
281
282 /*
283  * Note the exception to the function naming convention.
284  * TODO: See if a Makefile change is good enough.
285  */
286 NTSTATUS STDCALL DriverEntry(
287     IN PDRIVER_OBJECT DriverObject,
288     IN PUNICODE_STRING RegistryPath
289   ) {
290     NTSTATUS status;
291     int i;
292
293     DBG("Entry\n");
294     if (WvDriverObj) {
295         DBG("Re-entry not allowed!\n");
296         return STATUS_NOT_SUPPORTED;
297       }
298     WvDriverObj = DriverObject;
299     if (WvDriverStarted_)
300       return STATUS_SUCCESS;
301     Debug_Initialize();
302     status = registry__note_os_load_opts(&WvDriverOsLoadOpts_);
303     if (!NT_SUCCESS(status))
304       return Error("registry__note_driver__os_load_opts", status);
305
306     WvDriverStateHandle_ = NULL;
307     KeInitializeSpinLock(&WvDriverBusFdoLock_);
308
309     if ((WvDriverStateHandle_ = PoRegisterSystemState(
310         NULL,
311         ES_CONTINUOUS
312       )) == NULL) {
313         DBG("Could not set system state to ES_CONTINUOUS!!\n");
314       }
315     /*
316      * Set up IRP MajorFunction function table for devices
317      * this driver handles.
318      */
319     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
320       DriverObject->MajorFunction[i] = driver__dispatch_not_supported_;
321     DriverObject->MajorFunction[IRP_MJ_PNP] = driver__dispatch_pnp_;
322     DriverObject->MajorFunction[IRP_MJ_POWER] = driver__dispatch_power_;
323     DriverObject->MajorFunction[IRP_MJ_CREATE] = driver__dispatch_create_close_;
324     DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver__dispatch_create_close_;
325     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
326       driver__dispatch_sys_ctl_;
327     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
328       driver__dispatch_dev_ctl_;
329     DriverObject->MajorFunction[IRP_MJ_SCSI] = driver__dispatch_scsi_;
330     /* Set the driver Unload callback. */
331     DriverObject->DriverUnload = driver__unload_;
332     /* Set the driver AddDevice callback. */
333     DriverObject->DriverExtension->AddDevice = driver__attach_fdo_;
334     /* Initialize various modules. */
335     disk__module_init();        /* TODO: Check for error. */
336     filedisk__module_init();    /* TODO: Check for error. */
337     ramdisk__module_init();     /* TODO: Check for error. */
338
339     /*
340      * Always create the root-enumerated, main bus device.
341      * This is required in order to boot from a WinVBlock disk.
342      */
343     status = driver__create_bus_();
344     if(!NT_SUCCESS(status))
345       goto err_bus;
346
347     WvDriverStarted_ = TRUE;
348     DBG("Exit\n");
349     return STATUS_SUCCESS;
350
351     err_bus:
352
353     driver__unload_(DriverObject);
354     DBG("Exit due to failure\n");
355     return status;
356   }
357
358 static NTSTATUS STDCALL driver__dispatch_not_supported_(
359     IN PDEVICE_OBJECT dev_obj,
360     IN PIRP irp
361   ) {
362     irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
363     IoCompleteRequest(irp, IO_NO_INCREMENT);
364     return irp->IoStatus.Status;
365   }
366
367 /**
368  * Common IRP completion routine.
369  *
370  * @v irp               Points to the IRP to complete.
371  * @v info              Number of bytes returned for the IRP, or 0.
372  * @v status            Status for the IRP to complete.
373  * @ret NTSTATUS        Returns the status value, as passed.
374  */
375 winvblock__lib_func NTSTATUS STDCALL driver__complete_irp(
376     IN PIRP irp,
377     IN ULONG_PTR info,
378     IN NTSTATUS status
379   ) {
380     irp->IoStatus.Information = info;
381     irp->IoStatus.Status = status;
382     IoCompleteRequest(irp, IO_NO_INCREMENT);
383     #ifdef DEBUGIRPS
384     Debug_IrpEnd(irp, status);
385     #endif
386     return status;
387   }
388
389 /* Handle a power IRP. */
390 static NTSTATUS driver__dispatch_power_(
391     IN PDEVICE_OBJECT dev_obj,
392     IN PIRP irp
393   ) {
394     /* WvDevFromDevObj() checks for a NULL dev_obj */
395     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
396
397     #ifdef DEBUGIRPS
398     Debug_IrpStart(dev_obj, irp);
399     #endif
400     /* Check that the device exists. */
401     if (!dev || dev->State == WvDevStateDeleted) {
402         /* Even if it doesn't, a power IRP is important! */
403         PoStartNextPowerIrp(irp);
404         return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
405       }
406     /* Call the particular device's power handler. */
407     if (dev->IrpMj && dev->IrpMj->Power)
408       return dev->IrpMj->Power(dev, irp);
409     /* Otherwise, we don't support the IRP. */
410     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
411   }
412
413 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
414 static NTSTATUS driver__dispatch_create_close_(
415     IN PDEVICE_OBJECT dev_obj,
416     IN PIRP irp
417   ) {
418     /* WvDevFromDevObj() checks for a NULL dev_obj */
419     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
420
421     #ifdef DEBUGIRPS
422     Debug_IrpStart(dev_obj, irp);
423     #endif
424     /* Check that the device exists. */
425     if (!dev || dev->State == WvDevStateDeleted)
426       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
427     /* Always succeed with nothing to do. */
428     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
429   }
430
431 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
432 static NTSTATUS driver__dispatch_sys_ctl_(
433     IN PDEVICE_OBJECT dev_obj,
434     IN PIRP irp
435   ) {
436     /* WvDevFromDevObj() checks for a NULL dev_obj */
437     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
438
439     #ifdef DEBUGIRPS
440     Debug_IrpStart(dev_obj, irp);
441     #endif
442     /* Check that the device exists. */
443     if (!dev || dev->State == WvDevStateDeleted)
444       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
445     /* Call the particular device's power handler. */
446     if (dev->IrpMj && dev->IrpMj->SysCtl)
447       return dev->IrpMj->SysCtl(dev, irp);
448     /* Otherwise, we don't support the IRP. */
449     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
450   }
451
452 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
453 static NTSTATUS driver__dispatch_dev_ctl_(
454     IN PDEVICE_OBJECT dev_obj,
455     IN PIRP irp
456   ) {
457     /* WvDevFromDevObj() checks for a NULL dev_obj */
458     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
459     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
460
461     #ifdef DEBUGIRPS
462     Debug_IrpStart(dev_obj, irp);
463     #endif
464     /* Check that the device exists. */
465     if (!dev || dev->State == WvDevStateDeleted)
466       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
467     /* Call the particular device's power handler. */
468     if (dev->IrpMj && dev->IrpMj->DevCtl) {
469         return dev->IrpMj->DevCtl(
470             dev,
471             irp,
472             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
473           );
474       }
475     /* Otherwise, we don't support the IRP. */
476     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
477   }
478
479 /* Handle an IRP_MJ_SCSI IRP. */
480 static NTSTATUS driver__dispatch_scsi_(
481     IN PDEVICE_OBJECT dev_obj,
482     IN PIRP irp
483   ) {
484     /* WvDevFromDevObj() checks for a NULL dev_obj */
485     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
486     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
487
488     #ifdef DEBUGIRPS
489     Debug_IrpStart(dev_obj, irp);
490     #endif
491     /* Check that the device exists. */
492     if (!dev || dev->State == WvDevStateDeleted)
493       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
494     /* Call the particular device's power handler. */
495     if (dev->IrpMj && dev->IrpMj->Scsi) {
496         return dev->IrpMj->Scsi(
497             dev,
498             irp,
499             io_stack_loc->Parameters.Scsi.Srb->Function
500           );
501       }
502     /* Otherwise, we don't support the IRP. */
503     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
504   }
505
506 /* Handle an IRP_MJ_PNP IRP. */
507 static NTSTATUS driver__dispatch_pnp_(
508     IN PDEVICE_OBJECT dev_obj,
509     IN PIRP irp
510   ) {
511     /* WvDevFromDevObj() checks for a NULL dev_obj */
512     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
513     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
514
515     #ifdef DEBUGIRPS
516     Debug_IrpStart(dev_obj, irp);
517     #endif
518     /* Check that the device exists. */
519     if (!dev || dev->State == WvDevStateDeleted)
520       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
521     /* Call the particular device's power handler. */
522     if (dev->IrpMj && dev->IrpMj->Pnp) {
523         return dev->IrpMj->Pnp(
524             dev,
525             irp,
526             io_stack_loc->MinorFunction
527           );
528       }
529     /* Otherwise, we don't support the IRP. */
530     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
531   }
532
533 static void STDCALL driver__unload_(IN PDRIVER_OBJECT DriverObject) {
534     DBG("Unloading...\n");
535     if (WvDriverStateHandle_ != NULL)
536       PoUnregisterSystemState(WvDriverStateHandle_);
537     IoDeleteSymbolicLink(&WvDriverBusDosname_);
538     WvDriverBusFdo_ = NULL;
539     wv_free(WvDriverOsLoadOpts_);
540     WvDriverStarted_ = FALSE;
541     DBG("Done\n");
542   }
543
544 winvblock__lib_func void STDCALL WvDriverCompletePendingIrp(IN PIRP Irp) {
545     #ifdef DEBUGIRPS
546     Debug_IrpEnd(Irp, Irp->IoStatus.Status);
547     #endif
548     IoCompleteRequest(Irp, IO_NO_INCREMENT);
549   }
550
551 /* Note the exception to the function naming convention. */
552 winvblock__lib_func NTSTATUS STDCALL Error(
553     IN PCHAR Message,
554     IN NTSTATUS Status
555   ) {
556     DBG("%s: 0x%08x\n", Message, Status);
557     return Status;
558   }
559
560 /**
561  * Get a pointer to the driver bus device.
562  *
563  * @ret         A pointer to the driver bus, or NULL.
564  */
565 winvblock__lib_func WV_SP_BUS_T driver__bus(void) {
566     if (!WvDriverBusFdo_) {
567         DBG("No driver bus device!\n");
568         return NULL;
569       }
570     return WvBusFromDev(WvDevFromDevObj(WvDriverBusFdo_));
571   }
572
573 /* Pass an IRP_MJ_SYSTEM_CONTROL IRP to the bus. */
574 static NTSTATUS STDCALL WvDriverBusSysCtl_(IN WV_SP_DEV_T dev, IN PIRP irp) {
575     WV_SP_BUS_T bus = WvBusFromDev(dev);
576
577     return WvBusSysCtl(bus, irp);
578   }
579
580 /* Pass a power IRP to the bus. */
581 static NTSTATUS STDCALL WvDriverBusPower_(IN WV_SP_DEV_T dev, IN PIRP irp) {
582     WV_SP_BUS_T bus = WvBusFromDev(dev);
583
584     return WvBusPower(bus, irp);
585   }
586
587 /* Pass an IRP_MJ_PNP to the bus. */
588 static NTSTATUS STDCALL WvDriverBusPnp_(
589     IN WV_SP_DEV_T dev,
590     IN PIRP irp,
591     IN UCHAR code
592   ) {
593     WV_SP_BUS_T bus = WvBusFromDev(dev);
594
595     return WvBusPnp(bus, irp, code);
596   }
597     
598 /**
599  * Add a child node to the bus.
600  *
601  * @v Dev               Points to the child device to add.
602  * @ret                 TRUE for success, FALSE for failure.
603  */
604 winvblock__lib_func winvblock__bool STDCALL WvDriverBusAddDev(
605     IN OUT WV_SP_DEV_T Dev
606   ) {
607     /* The new node's device object. */
608     PDEVICE_OBJECT dev_obj;
609
610     DBG("Entry\n");
611     if (!WvDriverBusFdo_ || !Dev) {
612         DBG("No bus or no device!\n");
613         return FALSE;
614       }
615     /* Create the child device. */
616     dev_obj = WvDevCreatePdo(Dev);
617     if (!dev_obj) {
618         DBG("PDO creation failed!\n");
619         return FALSE;
620       }
621     /* Create a node.  TODO: Put the node somewhere better. */
622     Dev->BusNode = wv_malloc(sizeof *(Dev->BusNode));
623     if (!Dev->BusNode) {
624         DBG("Couldn't allocate node storage!\n");
625         IoDeleteDevice(dev_obj);
626         return FALSE;
627       }
628     WvBusInitNode(Dev->BusNode, dev_obj);
629     /* Associate the parent bus. */
630     Dev->Parent = WvDriverBus_.Dev.Self;
631     /*
632      * Initialize the device.  For disks, this routine is responsible for
633      * determining the disk's geometry appropriately for AoE/RAM/file disks.
634      */
635     Dev->Ops.Init(Dev);
636     dev_obj->Flags &= ~DO_DEVICE_INITIALIZING;
637     /* Add the new PDO device to the bus' list of children. */
638     WvBusAddNode(&WvDriverBus_, Dev->BusNode);
639
640     DBG("Exit\n");
641     return TRUE;
642   }