[driver] Use static bus in bus IRP handlers
[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_CTL WvDriverBusDevCtl_;
84 static WV_F_DEV_DISPATCH WvDriverBusPower_;
85 static WV_F_DEV_PNP WvDriverBusPnp_;
86
87 static LPWSTR STDCALL get_opt(IN LPWSTR opt_name) {
88     LPWSTR our_opts, the_opt;
89     WCHAR our_sig[] = L"WINVBLOCK=";
90     /* To produce constant integer expressions. */
91     enum {
92         our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
93         our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
94       };
95     size_t opt_name_len, opt_name_len_bytes;
96
97     if (!WvDriverOsLoadOpts_ || !opt_name)
98       return NULL;
99
100     /* Find /WINVBLOCK= options. */
101     our_opts = WvDriverOsLoadOpts_;
102     while (*our_opts != L'\0') {
103         if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
104             our_opts++;
105             continue;
106           }
107         our_opts += our_sig_len;
108         break;
109       }
110
111     /* Search for the specific option. */
112     the_opt = our_opts;
113     opt_name_len = wcslen(opt_name);
114     opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
115     while (*the_opt != L'\0' && *the_opt != L' ') {
116         if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
117             while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
118               the_opt++;
119             continue;
120           }
121         the_opt += opt_name_len;
122         break;
123       }
124
125     if (*the_opt == L'\0' || *the_opt == L' ')
126       return NULL;
127
128     /* Next should come "=". */
129     if (*the_opt != L'=')
130       return NULL;
131
132     /*
133      * And finally our option's value.  The caller needs
134      * to worry about looking past the end of the option.
135      */
136     the_opt++;
137     if (*the_opt == L'\0' || *the_opt == L' ')
138       return NULL;
139     return the_opt;
140   }
141
142 static NTSTATUS STDCALL driver__attach_fdo_(
143     IN PDRIVER_OBJECT DriverObject,
144     IN PDEVICE_OBJECT PhysicalDeviceObject
145   ) {
146     KIRQL irql;
147     NTSTATUS status;
148     PLIST_ENTRY walker;
149     PUNICODE_STRING dev_name = NULL;
150     PDEVICE_OBJECT fdo = NULL;
151     static WV_S_DEV_IRP_MJ irp_mj = {
152         WvDriverBusPower_,
153         WvDriverBusSysCtl_,
154         WvDriverBusDevCtl_,
155         (WV_FP_DEV_SCSI) 0,
156         WvDriverBusPnp_,
157       };
158
159     DBG("Entry\n");
160     /* Do we alreay have our main bus? */
161     if (WvDriverBusFdo_) {
162         DBG("Already have the main bus.  Refusing.\n");
163         status = STATUS_NOT_SUPPORTED;
164         goto err_already_established;
165       }
166     /* Initialize the bus. */
167     WvBusInit(&WvDriverBus_);
168     /* Create the bus FDO. */
169     status = IoCreateDevice(
170         DriverObject,
171         sizeof (driver__dev_ext),
172         &WvDriverBusName_,
173         FILE_DEVICE_CONTROLLER,
174         FILE_DEVICE_SECURE_OPEN,
175         FALSE,
176         &fdo
177       );
178     if (!NT_SUCCESS(status)) {
179         DBG("IoCreateDevice() failed!\n");
180         goto err_fdo;
181       }
182     /* DosDevice symlink. */
183     status = IoCreateSymbolicLink(
184         &WvDriverBusDosname_,
185         &WvDriverBusName_
186       );
187     if (!NT_SUCCESS(status)) {
188         DBG("IoCreateSymbolicLink() failed!\n");
189         goto err_dos_symlink;
190       }
191     /* Set associations for the bus, device, FDO, PDO. */
192     WvDevForDevObj(fdo, &WvDriverBus_.Dev);
193     WvDriverBus_.Dev.Self = WvDriverBus_.Fdo = fdo;
194     WvDriverBus_.Dev.IsBus = TRUE;
195     WvDriverBus_.Dev.IrpMj = &irp_mj;
196     WvDriverBus_.PhysicalDeviceObject = PhysicalDeviceObject;
197     fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
198     fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
199     /* Attach the FDO to the PDO. */
200     WvDriverBus_.LowerDeviceObject = IoAttachDeviceToDeviceStack(
201         fdo,
202         PhysicalDeviceObject
203       );
204     if (WvDriverBus_.LowerDeviceObject == NULL) {
205         status = STATUS_NO_SUCH_DEVICE;
206         DBG("IoAttachDeviceToDeviceStack() failed!\n");
207         goto err_attach;
208       }
209     status = WvBusStartThread(&WvDriverBus_);
210     if (!NT_SUCCESS(status)) {
211         DBG("Couldn't start bus thread!\n");
212         goto err_thread;
213       }
214     /* Ok! */
215     KeAcquireSpinLock(&WvDriverBusFdoLock_, &irql);
216     if (WvDriverBusFdo_) {
217         KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
218         DBG("Beaten to it!\n");
219         status = STATUS_NOT_SUPPORTED;
220         goto err_race_failed;
221       }
222     fdo->Flags &= ~DO_DEVICE_INITIALIZING;
223     #ifdef RIS
224     WvDriverBus_.Dev.State = Started;
225     #endif
226     WvDriverBusFdo_ = fdo;
227     KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
228     DBG("Exit\n");
229     return STATUS_SUCCESS;
230
231     err_race_failed:
232
233     err_thread:
234
235     err_attach:
236
237     IoDeleteSymbolicLink(&WvDriverBusDosname_);
238     err_dos_symlink:
239
240     IoDeleteDevice(fdo);
241     err_fdo:
242
243     err_already_established:
244
245     DBG("Exit with failure\n");
246     return status;
247   }
248
249 /* Create the root-enumerated, main bus device. */
250 NTSTATUS STDCALL driver__create_bus_(void) {
251     WV_SP_BUS_T bus;
252     NTSTATUS status;
253     PDEVICE_OBJECT bus_pdo = NULL;
254
255     /* Create the PDO. */
256     IoReportDetectedDevice(
257         WvDriverObj,
258         InterfaceTypeUndefined,
259         -1,
260         -1,
261         NULL,
262         NULL,
263         FALSE,
264         &bus_pdo
265       );
266     if (bus_pdo == NULL) {
267         DBG("IoReportDetectedDevice() went wrong!  Exiting.\n");
268         status = STATUS_UNSUCCESSFUL;
269         goto err_driver_bus;
270       }
271     /* Attach FDO to PDO. */
272     status = driver__attach_fdo_(WvDriverObj, bus_pdo);
273     if (!NT_SUCCESS(status)) {
274         DBG("driver__attach_fdo_() went wrong!\n");
275         goto err_add_dev;
276       }
277     /* PDO created, FDO attached.  All done. */
278     return STATUS_SUCCESS;
279
280     err_add_dev:
281
282     IoDeleteDevice(bus_pdo);
283     err_driver_bus:
284
285     return status;
286   }
287
288 /*
289  * Note the exception to the function naming convention.
290  * TODO: See if a Makefile change is good enough.
291  */
292 NTSTATUS STDCALL DriverEntry(
293     IN PDRIVER_OBJECT DriverObject,
294     IN PUNICODE_STRING RegistryPath
295   ) {
296     NTSTATUS status;
297     int i;
298
299     DBG("Entry\n");
300     if (WvDriverObj) {
301         DBG("Re-entry not allowed!\n");
302         return STATUS_NOT_SUPPORTED;
303       }
304     WvDriverObj = DriverObject;
305     if (WvDriverStarted_)
306       return STATUS_SUCCESS;
307     Debug_Initialize();
308     status = registry__note_os_load_opts(&WvDriverOsLoadOpts_);
309     if (!NT_SUCCESS(status))
310       return Error("registry__note_driver__os_load_opts", status);
311
312     WvDriverStateHandle_ = NULL;
313     KeInitializeSpinLock(&WvDriverBusFdoLock_);
314
315     if ((WvDriverStateHandle_ = PoRegisterSystemState(
316         NULL,
317         ES_CONTINUOUS
318       )) == NULL) {
319         DBG("Could not set system state to ES_CONTINUOUS!!\n");
320       }
321     /*
322      * Set up IRP MajorFunction function table for devices
323      * this driver handles.
324      */
325     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
326       DriverObject->MajorFunction[i] = driver__dispatch_not_supported_;
327     DriverObject->MajorFunction[IRP_MJ_PNP] = driver__dispatch_pnp_;
328     DriverObject->MajorFunction[IRP_MJ_POWER] = driver__dispatch_power_;
329     DriverObject->MajorFunction[IRP_MJ_CREATE] = driver__dispatch_create_close_;
330     DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver__dispatch_create_close_;
331     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
332       driver__dispatch_sys_ctl_;
333     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
334       driver__dispatch_dev_ctl_;
335     DriverObject->MajorFunction[IRP_MJ_SCSI] = driver__dispatch_scsi_;
336     /* Set the driver Unload callback. */
337     DriverObject->DriverUnload = driver__unload_;
338     /* Set the driver AddDevice callback. */
339     DriverObject->DriverExtension->AddDevice = driver__attach_fdo_;
340     /* Initialize various modules. */
341     disk__module_init();        /* TODO: Check for error. */
342     filedisk__module_init();    /* TODO: Check for error. */
343     ramdisk__module_init();     /* TODO: Check for error. */
344
345     /*
346      * Always create the root-enumerated, main bus device.
347      * This is required in order to boot from a WinVBlock disk.
348      */
349     status = driver__create_bus_();
350     if(!NT_SUCCESS(status))
351       goto err_bus;
352
353     WvDriverStarted_ = TRUE;
354     DBG("Exit\n");
355     return STATUS_SUCCESS;
356
357     err_bus:
358
359     driver__unload_(DriverObject);
360     DBG("Exit due to failure\n");
361     return status;
362   }
363
364 static NTSTATUS STDCALL driver__dispatch_not_supported_(
365     IN PDEVICE_OBJECT dev_obj,
366     IN PIRP irp
367   ) {
368     irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
369     IoCompleteRequest(irp, IO_NO_INCREMENT);
370     return irp->IoStatus.Status;
371   }
372
373 /**
374  * Common IRP completion routine.
375  *
376  * @v irp               Points to the IRP to complete.
377  * @v info              Number of bytes returned for the IRP, or 0.
378  * @v status            Status for the IRP to complete.
379  * @ret NTSTATUS        Returns the status value, as passed.
380  */
381 winvblock__lib_func NTSTATUS STDCALL driver__complete_irp(
382     IN PIRP irp,
383     IN ULONG_PTR info,
384     IN NTSTATUS status
385   ) {
386     irp->IoStatus.Information = info;
387     irp->IoStatus.Status = status;
388     IoCompleteRequest(irp, IO_NO_INCREMENT);
389     #ifdef DEBUGIRPS
390     Debug_IrpEnd(irp, status);
391     #endif
392     return status;
393   }
394
395 /* Handle a power IRP. */
396 static NTSTATUS driver__dispatch_power_(
397     IN PDEVICE_OBJECT dev_obj,
398     IN PIRP irp
399   ) {
400     /* WvDevFromDevObj() checks for a NULL dev_obj */
401     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
402
403     #ifdef DEBUGIRPS
404     Debug_IrpStart(dev_obj, irp);
405     #endif
406     /* Check that the device exists. */
407     if (!dev || dev->State == WvDevStateDeleted) {
408         /* Even if it doesn't, a power IRP is important! */
409         PoStartNextPowerIrp(irp);
410         return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
411       }
412     /* Call the particular device's power handler. */
413     if (dev->IrpMj && dev->IrpMj->Power)
414       return dev->IrpMj->Power(dev, irp);
415     /* Otherwise, we don't support the IRP. */
416     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
417   }
418
419 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
420 static NTSTATUS driver__dispatch_create_close_(
421     IN PDEVICE_OBJECT dev_obj,
422     IN PIRP irp
423   ) {
424     /* WvDevFromDevObj() checks for a NULL dev_obj */
425     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
426
427     #ifdef DEBUGIRPS
428     Debug_IrpStart(dev_obj, irp);
429     #endif
430     /* Check that the device exists. */
431     if (!dev || dev->State == WvDevStateDeleted)
432       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
433     /* Always succeed with nothing to do. */
434     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
435   }
436
437 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
438 static NTSTATUS driver__dispatch_sys_ctl_(
439     IN PDEVICE_OBJECT dev_obj,
440     IN PIRP irp
441   ) {
442     /* WvDevFromDevObj() checks for a NULL dev_obj */
443     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
444
445     #ifdef DEBUGIRPS
446     Debug_IrpStart(dev_obj, irp);
447     #endif
448     /* Check that the device exists. */
449     if (!dev || dev->State == WvDevStateDeleted)
450       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
451     /* Call the particular device's power handler. */
452     if (dev->IrpMj && dev->IrpMj->SysCtl)
453       return dev->IrpMj->SysCtl(dev, irp);
454     /* Otherwise, we don't support the IRP. */
455     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
456   }
457
458 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
459 static NTSTATUS driver__dispatch_dev_ctl_(
460     IN PDEVICE_OBJECT dev_obj,
461     IN PIRP irp
462   ) {
463     /* WvDevFromDevObj() checks for a NULL dev_obj */
464     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
465     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
466
467     #ifdef DEBUGIRPS
468     Debug_IrpStart(dev_obj, irp);
469     #endif
470     /* Check that the device exists. */
471     if (!dev || dev->State == WvDevStateDeleted)
472       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
473     /* Call the particular device's power handler. */
474     if (dev->IrpMj && dev->IrpMj->DevCtl) {
475         return dev->IrpMj->DevCtl(
476             dev,
477             irp,
478             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
479           );
480       }
481     /* Otherwise, we don't support the IRP. */
482     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
483   }
484
485 /* Handle an IRP_MJ_SCSI IRP. */
486 static NTSTATUS driver__dispatch_scsi_(
487     IN PDEVICE_OBJECT dev_obj,
488     IN PIRP irp
489   ) {
490     /* WvDevFromDevObj() checks for a NULL dev_obj */
491     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
492     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
493
494     #ifdef DEBUGIRPS
495     Debug_IrpStart(dev_obj, irp);
496     #endif
497     /* Check that the device exists. */
498     if (!dev || dev->State == WvDevStateDeleted)
499       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
500     /* Call the particular device's power handler. */
501     if (dev->IrpMj && dev->IrpMj->Scsi) {
502         return dev->IrpMj->Scsi(
503             dev,
504             irp,
505             io_stack_loc->Parameters.Scsi.Srb->Function
506           );
507       }
508     /* Otherwise, we don't support the IRP. */
509     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
510   }
511
512 /* Handle an IRP_MJ_PNP IRP. */
513 static NTSTATUS driver__dispatch_pnp_(
514     IN PDEVICE_OBJECT dev_obj,
515     IN PIRP irp
516   ) {
517     /* WvDevFromDevObj() checks for a NULL dev_obj */
518     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
519     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
520
521     #ifdef DEBUGIRPS
522     Debug_IrpStart(dev_obj, irp);
523     #endif
524     /* Check that the device exists. */
525     if (!dev || dev->State == WvDevStateDeleted)
526       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
527     /* Call the particular device's power handler. */
528     if (dev->IrpMj && dev->IrpMj->Pnp) {
529         return dev->IrpMj->Pnp(
530             dev,
531             irp,
532             io_stack_loc->MinorFunction
533           );
534       }
535     /* Otherwise, we don't support the IRP. */
536     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
537   }
538
539 static void STDCALL driver__unload_(IN PDRIVER_OBJECT DriverObject) {
540     DBG("Unloading...\n");
541     if (WvDriverStateHandle_ != NULL)
542       PoUnregisterSystemState(WvDriverStateHandle_);
543     IoDeleteSymbolicLink(&WvDriverBusDosname_);
544     WvDriverBusFdo_ = NULL;
545     wv_free(WvDriverOsLoadOpts_);
546     WvDriverStarted_ = FALSE;
547     DBG("Done\n");
548   }
549
550 winvblock__lib_func void STDCALL WvDriverCompletePendingIrp(IN PIRP Irp) {
551     #ifdef DEBUGIRPS
552     Debug_IrpEnd(Irp, Irp->IoStatus.Status);
553     #endif
554     IoCompleteRequest(Irp, IO_NO_INCREMENT);
555   }
556
557 /* Note the exception to the function naming convention. */
558 winvblock__lib_func NTSTATUS STDCALL Error(
559     IN PCHAR Message,
560     IN NTSTATUS Status
561   ) {
562     DBG("%s: 0x%08x\n", Message, Status);
563     return Status;
564   }
565
566 /* Pass an IRP_MJ_SYSTEM_CONTROL IRP to the bus. */
567 static NTSTATUS STDCALL WvDriverBusSysCtl_(IN WV_SP_DEV_T dev, IN PIRP irp) {
568     return WvBusSysCtl(&WvDriverBus_, irp);
569   }
570
571 /* Pass a power IRP to the bus. */
572 static NTSTATUS STDCALL WvDriverBusPower_(IN WV_SP_DEV_T dev, IN PIRP irp) {
573     return WvBusPower(&WvDriverBus_, irp);
574   }
575
576 /* Pass an IRP_MJ_PNP to the bus. */
577 static NTSTATUS STDCALL WvDriverBusPnp_(
578     IN WV_SP_DEV_T dev,
579     IN PIRP irp,
580     IN UCHAR code
581   ) {
582     return WvBusPnp(&WvDriverBus_, irp, code);
583   }
584     
585 /**
586  * Add a child node to the bus.
587  *
588  * @v Dev               Points to the child device to add.
589  * @ret                 TRUE for success, FALSE for failure.
590  */
591 winvblock__lib_func winvblock__bool STDCALL WvDriverBusAddDev(
592     IN OUT WV_SP_DEV_T Dev
593   ) {
594     /* The new node's device object. */
595     PDEVICE_OBJECT dev_obj;
596
597     DBG("Entry\n");
598     if (!WvDriverBusFdo_ || !Dev) {
599         DBG("No bus or no device!\n");
600         return FALSE;
601       }
602     /* Create the child device. */
603     dev_obj = WvDevCreatePdo(Dev);
604     if (!dev_obj) {
605         DBG("PDO creation failed!\n");
606         return FALSE;
607       }
608     /* Create a node.  TODO: Put the node somewhere better. */
609     Dev->BusNode = wv_malloc(sizeof *(Dev->BusNode));
610     if (!Dev->BusNode) {
611         DBG("Couldn't allocate node storage!\n");
612         IoDeleteDevice(dev_obj);
613         return FALSE;
614       }
615     WvBusInitNode(Dev->BusNode, dev_obj);
616     /* Associate the parent bus. */
617     Dev->Parent = WvDriverBus_.Dev.Self;
618     /*
619      * Initialize the device.  For disks, this routine is responsible for
620      * determining the disk's geometry appropriately for AoE/RAM/file disks.
621      */
622     Dev->Ops.Init(Dev);
623     dev_obj->Flags &= ~DO_DEVICE_INITIALIZING;
624     /* Add the new PDO device to the bus' list of children. */
625     WvBusAddNode(&WvDriverBus_, Dev->BusNode);
626
627     DBG("Exit\n");
628     return TRUE;
629   }
630
631 static NTSTATUS STDCALL WvDriverBusDevCtlDetach_(
632     IN PIRP irp
633   ) {
634     winvblock__uint32 disk_num =
635       *((winvblock__uint32_ptr) irp->AssociatedIrp.SystemBuffer);
636     WV_SP_DEV_T dev_walker;
637     WV_SP_DISK_T disk_walker = NULL, prev_disk_walker;
638     WV_SP_BUS_T bus = &WvDriverBus_;
639
640     DBG("Request to detach disk: %d\n", disk_num);
641     dev_walker = bus->first_child;
642     if (dev_walker != NULL)
643       disk_walker = disk__get_ptr(dev_walker);
644     prev_disk_walker = disk_walker;
645     while ((disk_walker != NULL) && (dev_walker->DevNum != disk_num)) {
646         prev_disk_walker = disk_walker;
647         dev_walker = dev_walker->next_sibling_ptr;
648         if (dev_walker != NULL)
649           disk_walker = disk__get_ptr(dev_walker);
650       }
651     if (disk_walker != NULL) {
652         if (disk_walker->Dev->Boot) {
653             DBG("Cannot unmount a boot drive.\n");
654             irp->IoStatus.Information = 0;
655             return STATUS_INVALID_DEVICE_REQUEST;
656           }
657         DBG("Deleting disk %d\n", dev_walker->DevNum);
658         if (disk_walker == disk__get_ptr(bus->first_child))
659           bus->first_child = dev_walker->next_sibling_ptr;
660           else {
661             prev_disk_walker->Dev->next_sibling_ptr =
662               dev_walker->next_sibling_ptr;
663           }
664         disk_walker->Unmount = TRUE;
665         dev_walker->next_sibling_ptr = NULL;
666         if (bus->PhysicalDeviceObject != NULL)
667           IoInvalidateDeviceRelations(bus->PhysicalDeviceObject, BusRelations);
668       }
669     bus->Children--;
670     irp->IoStatus.Information = 0;
671     return STATUS_SUCCESS;
672   }
673
674 NTSTATUS STDCALL WvDriverBusDevCtl_(
675     IN WV_SP_DEV_T dev,
676     IN PIRP irp,
677     IN ULONG POINTER_ALIGNMENT code
678   ) {
679     NTSTATUS status;
680
681     switch (code) {
682         case IOCTL_FILE_ATTACH:
683           status = filedisk__attach(dev, irp);
684           break;
685
686         case IOCTL_FILE_DETACH:
687           status = WvDriverBusDevCtlDetach_(irp);
688           break;
689
690         default:
691           irp->IoStatus.Information = 0;
692           status = STATUS_INVALID_DEVICE_REQUEST;
693       }
694
695     irp->IoStatus.Status = status;
696     IoCompleteRequest(irp, IO_NO_INCREMENT);
697     return status;
698   }
699
700 NTSTATUS STDCALL WvDriverGetDevCapabilities(
701     IN PDEVICE_OBJECT DevObj,
702     IN PDEVICE_CAPABILITIES DevCapabilities
703   ) {
704     IO_STATUS_BLOCK io_status;
705     KEVENT pnp_event;
706     NTSTATUS status;
707     PDEVICE_OBJECT target_obj;
708     PIO_STACK_LOCATION io_stack_loc;
709     PIRP pnp_irp;
710
711     RtlZeroMemory(DevCapabilities, sizeof *DevCapabilities);
712     DevCapabilities->Size = sizeof *DevCapabilities;
713     DevCapabilities->Version = 1;
714     DevCapabilities->Address = -1;
715     DevCapabilities->UINumber = -1;
716
717     KeInitializeEvent(&pnp_event, NotificationEvent, FALSE);
718     target_obj = IoGetAttachedDeviceReference(DevObj);
719     pnp_irp = IoBuildSynchronousFsdRequest(
720         IRP_MJ_PNP,
721         target_obj,
722         NULL,
723         0,
724         NULL,
725         &pnp_event,
726         &io_status
727       );
728     if (pnp_irp == NULL) {
729         status = STATUS_INSUFFICIENT_RESOURCES;
730       } else {
731         pnp_irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
732         io_stack_loc = IoGetNextIrpStackLocation(pnp_irp);
733         RtlZeroMemory(io_stack_loc, sizeof *io_stack_loc);
734         io_stack_loc->MajorFunction = IRP_MJ_PNP;
735         io_stack_loc->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
736         io_stack_loc->Parameters.DeviceCapabilities.Capabilities =
737           DevCapabilities;
738         status = IoCallDriver(target_obj, pnp_irp);
739         if (status == STATUS_PENDING) {
740             KeWaitForSingleObject(
741                 &pnp_event,
742                 Executive,
743                 KernelMode,
744                 FALSE,
745                 NULL
746               );
747             status = io_status.Status;
748           }
749       }
750     ObDereferenceObject(target_obj);
751     return status;
752   }