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