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