[driver] Do not create the bus PDO again and again
[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 "bus.h"
38 #include "device.h"
39 #include "disk.h"
40 #include "registry.h"
41 #include "mount.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     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     WvDevInit(&WvDriverBusDev_);
170     /* Create the bus FDO. */
171     status = IoCreateDevice(
172         DriverObject,
173         sizeof (driver__dev_ext),
174         &WvDriverBusName_,
175         FILE_DEVICE_CONTROLLER,
176         FILE_DEVICE_SECURE_OPEN,
177         FALSE,
178         &fdo
179       );
180     if (!NT_SUCCESS(status)) {
181         DBG("IoCreateDevice() failed!\n");
182         goto err_fdo;
183       }
184     /* DosDevice symlink. */
185     status = IoCreateSymbolicLink(
186         &WvDriverBusDosname_,
187         &WvDriverBusName_
188       );
189     if (!NT_SUCCESS(status)) {
190         DBG("IoCreateSymbolicLink() failed!\n");
191         goto err_dos_symlink;
192       }
193     /* Set associations for the bus, device, FDO, PDO. */
194     WvDevForDevObj(fdo, &WvDriverBusDev_);
195     WvDriverBusDev_.Self = WvDriverBus_.Fdo = fdo;
196     WvDriverBusDev_.IsBus = TRUE;
197     WvDriverBusDev_.IrpMj = &irp_mj;
198     WvDriverBus_.QueryDevText = WvDriverBusPnpQueryDevText_;
199     WvDriverBus_.PhysicalDeviceObject = PhysicalDeviceObject;
200     fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
201     fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
202     /* Attach the FDO to the PDO. */
203     WvDriverBus_.LowerDeviceObject = IoAttachDeviceToDeviceStack(
204         fdo,
205         PhysicalDeviceObject
206       );
207     if (WvDriverBus_.LowerDeviceObject == NULL) {
208         status = STATUS_NO_SUCH_DEVICE;
209         DBG("IoAttachDeviceToDeviceStack() failed!\n");
210         goto err_attach;
211       }
212     status = WvBusStartThread(&WvDriverBus_);
213     if (!NT_SUCCESS(status)) {
214         DBG("Couldn't start bus thread!\n");
215         goto err_thread;
216       }
217     /* Ok! */
218     KeAcquireSpinLock(&WvDriverBusFdoLock_, &irql);
219     if (WvDriverBusFdo_) {
220         KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
221         DBG("Beaten to it!\n");
222         status = STATUS_NOT_SUPPORTED;
223         goto err_race_failed;
224       }
225     fdo->Flags &= ~DO_DEVICE_INITIALIZING;
226     #ifdef RIS
227     WvDriverBus_.Dev.State = Started;
228     #endif
229     WvDriverBusFdo_ = fdo;
230     KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
231     DBG("Exit\n");
232     return STATUS_SUCCESS;
233
234     err_race_failed:
235
236     err_thread:
237
238     err_attach:
239
240     IoDeleteSymbolicLink(&WvDriverBusDosname_);
241     err_dos_symlink:
242
243     IoDeleteDevice(fdo);
244     err_fdo:
245
246     err_already_established:
247
248     DBG("Exit with failure\n");
249     return status;
250   }
251
252 /* Establish the bus PDO. */
253 static NTSTATUS STDCALL WvDriverBusEstablish_(IN PUNICODE_STRING RegistryPath) {
254     NTSTATUS status;
255     HANDLE reg_key;
256     winvblock__uint32 pdo_done = 0;
257     PDEVICE_OBJECT bus_pdo = NULL;
258
259     /* Open our Registry path. */
260     status = WvlRegOpenKey(RegistryPath->Buffer, &reg_key);
261     if (!NT_SUCCESS(status))
262       return status;
263
264     /* Check the Registry to see if we've already got a PDO. */
265     status = WvlRegFetchDword(reg_key, L"PdoDone", &pdo_done);
266     if (NT_SUCCESS(status) && pdo_done) {
267         WvlRegCloseKey(reg_key);
268         return status;
269       }
270
271     /* Create a root-enumerated PDO for our bus. */
272     IoReportDetectedDevice(
273         WvDriverObj,
274         InterfaceTypeUndefined,
275         -1,
276         -1,
277         NULL,
278         NULL,
279         FALSE,
280         &bus_pdo
281       );
282     if (bus_pdo == NULL) {
283         DBG("IoReportDetectedDevice() went wrong!  Exiting.\n");
284         status = STATUS_UNSUCCESSFUL;
285         goto err_driver_bus;
286       }
287
288     /* Remember that we have a PDO for next time. */
289     status = WvlRegStoreDword(reg_key, L"PdoDone", 1);
290     if (!NT_SUCCESS(status)) {
291         DBG("Couldn't save PdoDone to Registry.  Oh well.\n");
292       }
293     /* Attach FDO to PDO. */
294     status = driver__attach_fdo_(WvDriverObj, bus_pdo);
295     if (!NT_SUCCESS(status)) {
296         DBG("driver__attach_fdo_() went wrong!\n");
297         goto err_add_dev;
298       }
299     /* PDO created, FDO attached.  All done. */
300     return STATUS_SUCCESS;
301
302     err_add_dev:
303
304     IoDeleteDevice(bus_pdo);
305     err_driver_bus:
306
307     return status;
308   }
309
310 /*
311  * Note the exception to the function naming convention.
312  * TODO: See if a Makefile change is good enough.
313  */
314 NTSTATUS STDCALL DriverEntry(
315     IN PDRIVER_OBJECT DriverObject,
316     IN PUNICODE_STRING RegistryPath
317   ) {
318     NTSTATUS status;
319     int i;
320
321     DBG("Entry\n");
322     if (WvDriverObj) {
323         DBG("Re-entry not allowed!\n");
324         return STATUS_NOT_SUPPORTED;
325       }
326     WvDriverObj = DriverObject;
327     if (WvDriverStarted_)
328       return STATUS_SUCCESS;
329     Debug_Initialize();
330     status = WvlRegNoteOsLoadOpts(&WvDriverOsLoadOpts_);
331     if (!NT_SUCCESS(status))
332       return Error("WvlRegNoteOsLoadOpts", status);
333
334     WvDriverStateHandle_ = NULL;
335     KeInitializeSpinLock(&WvDriverBusFdoLock_);
336
337     if ((WvDriverStateHandle_ = PoRegisterSystemState(
338         NULL,
339         ES_CONTINUOUS
340       )) == NULL) {
341         DBG("Could not set system state to ES_CONTINUOUS!!\n");
342       }
343     /*
344      * Set up IRP MajorFunction function table for devices
345      * this driver handles.
346      */
347     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
348       DriverObject->MajorFunction[i] = driver__dispatch_not_supported_;
349     DriverObject->MajorFunction[IRP_MJ_PNP] = driver__dispatch_pnp_;
350     DriverObject->MajorFunction[IRP_MJ_POWER] = driver__dispatch_power_;
351     DriverObject->MajorFunction[IRP_MJ_CREATE] = driver__dispatch_create_close_;
352     DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver__dispatch_create_close_;
353     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
354       driver__dispatch_sys_ctl_;
355     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
356       driver__dispatch_dev_ctl_;
357     DriverObject->MajorFunction[IRP_MJ_SCSI] = driver__dispatch_scsi_;
358     /* Set the driver Unload callback. */
359     DriverObject->DriverUnload = driver__unload_;
360     /* Set the driver AddDevice callback. */
361     DriverObject->DriverExtension->AddDevice = driver__attach_fdo_;
362     /* Initialize various modules. */
363     disk__module_init();        /* TODO: Check for error. */
364     filedisk__module_init();    /* TODO: Check for error. */
365     ramdisk__module_init();     /* TODO: Check for error. */
366
367     /* Establish the bus PDO. */
368     status = WvDriverBusEstablish_(RegistryPath);
369     if(!NT_SUCCESS(status))
370       goto err_bus;
371
372     WvDriverStarted_ = TRUE;
373     DBG("Exit\n");
374     return STATUS_SUCCESS;
375
376     err_bus:
377
378     driver__unload_(DriverObject);
379     DBG("Exit due to failure\n");
380     return status;
381   }
382
383 static NTSTATUS STDCALL driver__dispatch_not_supported_(
384     IN PDEVICE_OBJECT dev_obj,
385     IN PIRP irp
386   ) {
387     irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
388     IoCompleteRequest(irp, IO_NO_INCREMENT);
389     return irp->IoStatus.Status;
390   }
391
392 /**
393  * Common IRP completion routine.
394  *
395  * @v irp               Points to the IRP to complete.
396  * @v info              Number of bytes returned for the IRP, or 0.
397  * @v status            Status for the IRP to complete.
398  * @ret NTSTATUS        Returns the status value, as passed.
399  */
400 winvblock__lib_func NTSTATUS STDCALL driver__complete_irp(
401     IN PIRP irp,
402     IN ULONG_PTR info,
403     IN NTSTATUS status
404   ) {
405     irp->IoStatus.Information = info;
406     irp->IoStatus.Status = status;
407     IoCompleteRequest(irp, IO_NO_INCREMENT);
408     #ifdef DEBUGIRPS
409     Debug_IrpEnd(irp, status);
410     #endif
411     return status;
412   }
413
414 /* Handle a power IRP. */
415 static NTSTATUS driver__dispatch_power_(
416     IN PDEVICE_OBJECT dev_obj,
417     IN PIRP irp
418   ) {
419     /* WvDevFromDevObj() checks for a NULL dev_obj */
420     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
421
422     #ifdef DEBUGIRPS
423     Debug_IrpStart(dev_obj, irp);
424     #endif
425     /* Check that the device exists. */
426     if (!dev || dev->State == WvDevStateDeleted) {
427         /* Even if it doesn't, a power IRP is important! */
428         PoStartNextPowerIrp(irp);
429         return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
430       }
431     /* Call the particular device's power handler. */
432     if (dev->IrpMj && dev->IrpMj->Power)
433       return dev->IrpMj->Power(dev, irp);
434     /* Otherwise, we don't support the IRP. */
435     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
436   }
437
438 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
439 static NTSTATUS driver__dispatch_create_close_(
440     IN PDEVICE_OBJECT dev_obj,
441     IN PIRP irp
442   ) {
443     /* WvDevFromDevObj() checks for a NULL dev_obj */
444     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
445
446     #ifdef DEBUGIRPS
447     Debug_IrpStart(dev_obj, irp);
448     #endif
449     /* Check that the device exists. */
450     if (!dev || dev->State == WvDevStateDeleted)
451       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
452     /* Always succeed with nothing to do. */
453     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
454   }
455
456 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
457 static NTSTATUS driver__dispatch_sys_ctl_(
458     IN PDEVICE_OBJECT dev_obj,
459     IN PIRP irp
460   ) {
461     /* WvDevFromDevObj() checks for a NULL dev_obj */
462     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
463
464     #ifdef DEBUGIRPS
465     Debug_IrpStart(dev_obj, irp);
466     #endif
467     /* Check that the device exists. */
468     if (!dev || dev->State == WvDevStateDeleted)
469       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
470     /* Call the particular device's power handler. */
471     if (dev->IrpMj && dev->IrpMj->SysCtl)
472       return dev->IrpMj->SysCtl(dev, irp);
473     /* Otherwise, we don't support the IRP. */
474     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
475   }
476
477 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
478 static NTSTATUS driver__dispatch_dev_ctl_(
479     IN PDEVICE_OBJECT dev_obj,
480     IN PIRP irp
481   ) {
482     /* WvDevFromDevObj() checks for a NULL dev_obj */
483     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
484     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
485
486     #ifdef DEBUGIRPS
487     Debug_IrpStart(dev_obj, irp);
488     #endif
489     /* Check that the device exists. */
490     if (!dev || dev->State == WvDevStateDeleted)
491       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
492     /* Call the particular device's power handler. */
493     if (dev->IrpMj && dev->IrpMj->DevCtl) {
494         return dev->IrpMj->DevCtl(
495             dev,
496             irp,
497             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
498           );
499       }
500     /* Otherwise, we don't support the IRP. */
501     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
502   }
503
504 /* Handle an IRP_MJ_SCSI IRP. */
505 static NTSTATUS driver__dispatch_scsi_(
506     IN PDEVICE_OBJECT dev_obj,
507     IN PIRP irp
508   ) {
509     /* WvDevFromDevObj() checks for a NULL dev_obj */
510     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
511     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
512
513     #ifdef DEBUGIRPS
514     Debug_IrpStart(dev_obj, irp);
515     #endif
516     /* Check that the device exists. */
517     if (!dev || dev->State == WvDevStateDeleted)
518       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
519     /* Call the particular device's power handler. */
520     if (dev->IrpMj && dev->IrpMj->Scsi) {
521         return dev->IrpMj->Scsi(
522             dev,
523             irp,
524             io_stack_loc->Parameters.Scsi.Srb->Function
525           );
526       }
527     /* Otherwise, we don't support the IRP. */
528     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
529   }
530
531 /* Handle an IRP_MJ_PNP IRP. */
532 static NTSTATUS driver__dispatch_pnp_(
533     IN PDEVICE_OBJECT dev_obj,
534     IN PIRP irp
535   ) {
536     /* WvDevFromDevObj() checks for a NULL dev_obj */
537     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
538     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
539     NTSTATUS status;
540
541     #ifdef DEBUGIRPS
542     Debug_IrpStart(dev_obj, irp);
543     #endif
544     /* Check that the device exists. */
545     if (!dev || dev->State == WvDevStateDeleted)
546       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
547     /* Call the particular device's power handler. */
548     if (dev->IrpMj && dev->IrpMj->Pnp) {
549         status = dev->IrpMj->Pnp(
550             dev,
551             irp,
552             io_stack_loc->MinorFunction
553           );
554         if (dev->IsBus) {
555             dev->OldState = WvDriverBus_.OldState;
556             dev->State = WvDriverBus_.State;
557           }
558         return status;
559       }
560     /* Otherwise, we don't support the IRP. */
561     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
562   }
563
564 static void STDCALL driver__unload_(IN PDRIVER_OBJECT DriverObject) {
565     DBG("Unloading...\n");
566     if (WvDriverStateHandle_ != NULL)
567       PoUnregisterSystemState(WvDriverStateHandle_);
568     IoDeleteSymbolicLink(&WvDriverBusDosname_);
569     WvDriverBusFdo_ = NULL;
570     wv_free(WvDriverOsLoadOpts_);
571     WvDriverStarted_ = FALSE;
572     DBG("Done\n");
573   }
574
575 winvblock__lib_func void STDCALL WvDriverCompletePendingIrp(IN PIRP Irp) {
576     #ifdef DEBUGIRPS
577     Debug_IrpEnd(Irp, Irp->IoStatus.Status);
578     #endif
579     IoCompleteRequest(Irp, IO_NO_INCREMENT);
580   }
581
582 /* Note the exception to the function naming convention. */
583 winvblock__lib_func NTSTATUS STDCALL Error(
584     IN PCHAR Message,
585     IN NTSTATUS Status
586   ) {
587     DBG("%s: 0x%08x\n", Message, Status);
588     return Status;
589   }
590
591 /* Pass an IRP_MJ_SYSTEM_CONTROL IRP to the bus. */
592 static NTSTATUS STDCALL WvDriverBusSysCtl_(IN WV_SP_DEV_T dev, IN PIRP irp) {
593     return WvBusSysCtl(&WvDriverBus_, irp);
594   }
595
596 /* Pass a power IRP to the bus. */
597 static NTSTATUS STDCALL WvDriverBusPower_(IN WV_SP_DEV_T dev, IN PIRP irp) {
598     return WvBusPower(&WvDriverBus_, irp);
599   }
600
601 /* Pass an IRP_MJ_PNP to the bus. */
602 static NTSTATUS STDCALL WvDriverBusPnp_(
603     IN WV_SP_DEV_T dev,
604     IN PIRP irp,
605     IN UCHAR code
606   ) {
607     return WvBusPnp(&WvDriverBus_, irp, code);
608   }
609     
610 /**
611  * Add a child node to the bus.
612  *
613  * @v Dev               Points to the child device to add.
614  * @ret                 TRUE for success, FALSE for failure.
615  */
616 winvblock__lib_func winvblock__bool STDCALL WvDriverBusAddDev(
617     IN OUT WV_SP_DEV_T Dev
618   ) {
619     /* The new node's device object. */
620     PDEVICE_OBJECT dev_obj;
621
622     DBG("Entry\n");
623     if (!WvDriverBusFdo_ || !Dev) {
624         DBG("No bus or no device!\n");
625         return FALSE;
626       }
627     /* Create the child device. */
628     dev_obj = WvDevCreatePdo(Dev);
629     if (!dev_obj) {
630         DBG("PDO creation failed!\n");
631         return FALSE;
632       }
633     WvBusInitNode(&Dev->BusNode, dev_obj);
634     /* Associate the parent bus. */
635     Dev->Parent = WvDriverBus_.Fdo;
636     /*
637      * Initialize the device.  For disks, this routine is responsible for
638      * determining the disk's geometry appropriately for AoE/RAM/file disks.
639      */
640     Dev->Ops.Init(Dev);
641     dev_obj->Flags &= ~DO_DEVICE_INITIALIZING;
642     /* Add the new PDO device to the bus' list of children. */
643     WvBusAddNode(&WvDriverBus_, &Dev->BusNode);
644     Dev->DevNum = WvBusGetNodeNum(&Dev->BusNode);
645
646     DBG("Exit\n");
647     return TRUE;
648   }
649
650 static NTSTATUS STDCALL WvDriverBusDevCtlDetach_(
651     IN PIRP irp
652   ) {
653     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
654     winvblock__uint32 unit_num;
655     WV_SP_BUS_NODE walker;
656
657     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
658         NTSTATUS status;
659
660         /* Enqueue the IRP. */
661         status = WvBusEnqueueIrp(&WvDriverBus_, irp);
662         if (status != STATUS_PENDING)
663           /* Problem. */
664           return driver__complete_irp(irp, 0, status);
665         /* Ok. */
666         return status;
667       }
668     /* If we get here, we should be called by WvBusProcessWorkItems() */
669     unit_num = *((winvblock__uint32_ptr) irp->AssociatedIrp.SystemBuffer);
670     DBG("Request to detach unit: %d\n", unit_num);
671
672     walker = NULL;
673     /* For each node on the bus... */
674     while (walker = WvBusGetNextNode(&WvDriverBus_, walker)) {
675         WV_SP_DEV_T dev = WvDevFromDevObj(WvBusGetNodePdo(walker));
676
677         /* If the unit number matches... */
678         if (WvBusGetNodeNum(walker) == unit_num) {
679             /* If it's not a boot-time device... */
680             if (dev->Boot) {
681                 DBG("Cannot detach a boot-time device.\n");
682                 /* Signal error. */
683                 walker = NULL;
684                 break;
685               }
686             /* Detach the node and free it. */
687             DBG("Removing unit %d\n", unit_num);
688             WvBusRemoveNode(walker);
689             WvDevClose(dev);
690             IoDeleteDevice(dev->Self);
691             WvDevFree(dev);
692             break;
693           }
694       }
695     if (!walker)
696       return driver__complete_irp(irp, 0, STATUS_INVALID_PARAMETER);
697     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
698   }
699
700 NTSTATUS STDCALL WvDriverBusDevCtl_(
701     IN WV_SP_DEV_T dev,
702     IN PIRP irp,
703     IN ULONG POINTER_ALIGNMENT code
704   ) {
705     NTSTATUS status;
706
707     switch (code) {
708         case IOCTL_FILE_ATTACH:
709           status = filedisk__attach(dev, irp);
710           break;
711
712         case IOCTL_FILE_DETACH:
713           return WvDriverBusDevCtlDetach_(irp);
714
715         default:
716           irp->IoStatus.Information = 0;
717           status = STATUS_INVALID_DEVICE_REQUEST;
718       }
719
720     irp->IoStatus.Status = status;
721     IoCompleteRequest(irp, IO_NO_INCREMENT);
722     return status;
723   }
724
725 NTSTATUS STDCALL WvDriverGetDevCapabilities(
726     IN PDEVICE_OBJECT DevObj,
727     IN PDEVICE_CAPABILITIES DevCapabilities
728   ) {
729     IO_STATUS_BLOCK io_status;
730     KEVENT pnp_event;
731     NTSTATUS status;
732     PDEVICE_OBJECT target_obj;
733     PIO_STACK_LOCATION io_stack_loc;
734     PIRP pnp_irp;
735
736     RtlZeroMemory(DevCapabilities, sizeof *DevCapabilities);
737     DevCapabilities->Size = sizeof *DevCapabilities;
738     DevCapabilities->Version = 1;
739     DevCapabilities->Address = -1;
740     DevCapabilities->UINumber = -1;
741
742     KeInitializeEvent(&pnp_event, NotificationEvent, FALSE);
743     target_obj = IoGetAttachedDeviceReference(DevObj);
744     pnp_irp = IoBuildSynchronousFsdRequest(
745         IRP_MJ_PNP,
746         target_obj,
747         NULL,
748         0,
749         NULL,
750         &pnp_event,
751         &io_status
752       );
753     if (pnp_irp == NULL) {
754         status = STATUS_INSUFFICIENT_RESOURCES;
755       } else {
756         pnp_irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
757         io_stack_loc = IoGetNextIrpStackLocation(pnp_irp);
758         RtlZeroMemory(io_stack_loc, sizeof *io_stack_loc);
759         io_stack_loc->MajorFunction = IRP_MJ_PNP;
760         io_stack_loc->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
761         io_stack_loc->Parameters.DeviceCapabilities.Capabilities =
762           DevCapabilities;
763         status = IoCallDriver(target_obj, pnp_irp);
764         if (status == STATUS_PENDING) {
765             KeWaitForSingleObject(
766                 &pnp_event,
767                 Executive,
768                 KernelMode,
769                 FALSE,
770                 NULL
771               );
772             status = io_status.Status;
773           }
774       }
775     ObDereferenceObject(target_obj);
776     return status;
777   }
778
779 static NTSTATUS STDCALL WvDriverBusPnpQueryDevText_(
780     IN WV_SP_BUS_T bus,
781     IN PIRP irp
782   ) {
783     WCHAR (*str)[512];
784     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
785     NTSTATUS status;
786     winvblock__uint32 str_len;
787
788     /* Allocate a string buffer. */
789     str = wv_mallocz(sizeof *str);
790     if (str == NULL) {
791         DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n");
792         status = STATUS_INSUFFICIENT_RESOURCES;
793         goto alloc_str;
794       }
795     /* Determine the query type. */
796     switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) {
797         case DeviceTextDescription:
798           str_len = swprintf(*str, winvblock__literal_w L" Bus") + 1;
799           irp->IoStatus.Information =
800             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
801           if (irp->IoStatus.Information == 0) {
802               DBG("wv_palloc DeviceTextDescription\n");
803               status = STATUS_INSUFFICIENT_RESOURCES;
804               goto alloc_info;
805             }
806           RtlCopyMemory(
807               (PWCHAR) irp->IoStatus.Information,
808               str,
809               str_len * sizeof (WCHAR)
810             );
811           status = STATUS_SUCCESS;
812           goto alloc_info;
813
814         case DeviceTextLocationInformation:
815           str_len = WvDevPnpId(
816               &WvDriverBusDev_,
817               BusQueryInstanceID,
818               str
819             );
820           irp->IoStatus.Information =
821             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
822           if (irp->IoStatus.Information == 0) {
823               DBG("wv_palloc DeviceTextLocationInformation\n");
824               status = STATUS_INSUFFICIENT_RESOURCES;
825               goto alloc_info;
826             }
827           RtlCopyMemory(
828               (PWCHAR) irp->IoStatus.Information,
829               str,
830               str_len * sizeof (WCHAR)
831             );
832           status = STATUS_SUCCESS;
833           goto alloc_info;
834
835         default:
836           irp->IoStatus.Information = 0;
837           status = STATUS_NOT_SUPPORTED;
838       }
839     /* irp->IoStatus.Information not freed. */
840     alloc_info:
841
842     wv_free(str);
843     alloc_str:
844
845     return driver__complete_irp(irp, irp->IoStatus.Information, status);
846   }
847
848 /**
849  * Dummy device feature.
850  */
851
852 /* Prototype safety. */
853 WV_F_DEV_PNP WvDriverDummyPnp_;
854
855 /* Dummy PnP IRP handler. */
856 static NTSTATUS STDCALL WvDriverDummyPnp_(
857     IN WV_SP_DEV_T dev,
858     IN PIRP irp,
859     IN UCHAR code
860   ) {
861     if (code != IRP_MN_QUERY_ID)
862       return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
863
864     /* The WV_S_DEV_T extension points to the dummy IDs. */
865     return WvDriverDummyIds(irp, dev->ext);
866   }
867
868 typedef struct WV_DRIVER_ADD_DUMMY_ {
869     const WV_S_DRIVER_DUMMY_IDS * DummyIds;
870     DEVICE_TYPE DevType;
871     ULONG DevCharacteristics;
872     PKEVENT Event;
873     NTSTATUS Status;
874   } WV_S_DRIVER_ADD_DUMMY_, * WV_SP_DRIVER_ADD_DUMMY_;
875
876 /* Prototype safety. */
877 static WV_F_BUS_WORK_ITEM WvDriverAddDummy_;
878 /**
879  * Add a dummy PDO child node in the context of the bus' thread.
880  *
881  * @v context           Points to the WV_S_DRIVER_ADD_DUMMY_ to process.
882  */
883 static void STDCALL WvDriverAddDummy_(void * context) {
884     WV_SP_DRIVER_ADD_DUMMY_ dummy_context = context;
885     NTSTATUS status;
886     PDEVICE_OBJECT pdo = NULL;
887     WV_SP_DEV_T dev;
888     wv_size_t dummy_ids_size;
889     WV_SP_DRIVER_DUMMY_IDS dummy_ids;
890     static WV_S_DEV_IRP_MJ irp_mj = {
891         (WV_FP_DEV_DISPATCH) 0,
892         (WV_FP_DEV_DISPATCH) 0,
893         (WV_FP_DEV_CTL) 0,
894         (WV_FP_DEV_SCSI) 0,
895         WvDriverDummyPnp_,
896       };
897
898     status = IoCreateDevice(
899         WvDriverObj,
900         sizeof (driver__dev_ext),
901         NULL,
902         dummy_context->DevType,
903         dummy_context->DevCharacteristics,
904         FALSE,
905         &pdo
906       );
907     if (!NT_SUCCESS(status) || !pdo) {
908         DBG("Couldn't create dummy device.\n");
909         dummy_context->Status = STATUS_INSUFFICIENT_RESOURCES;
910         goto err_create_pdo;
911       }
912
913     dev = wv_malloc(sizeof *dev);
914     if (!dev) {
915         DBG("Couldn't allocate dummy device.\n");
916         dummy_context->Status = STATUS_INSUFFICIENT_RESOURCES;
917         goto err_dev;
918       }
919
920     dummy_ids_size =
921       sizeof *dummy_ids +
922       dummy_context->DummyIds->Len * sizeof dummy_ids->Text[0] -
923       sizeof dummy_ids->Text[0];        /* The struct hack uses a WCHAR[1]. */
924     dummy_ids = wv_malloc(dummy_ids_size);
925     if (!dummy_ids) {
926         DBG("Couldn't allocate dummy IDs.\n");
927         dummy_context->Status = STATUS_INSUFFICIENT_RESOURCES;
928         goto err_dummy_ids;
929       }
930     /* Copy the IDs offsets and lengths. */
931     RtlCopyMemory(dummy_ids, dummy_context->DummyIds, sizeof *dummy_ids);
932     /* Copy the text of the IDs. */
933     RtlCopyMemory(
934         &dummy_ids->Text,
935         dummy_context->DummyIds->Ids,
936         dummy_context->DummyIds->Len * sizeof dummy_ids->Text[0]
937       );
938     /* Point to the copy of the text. */
939     dummy_ids->Ids = dummy_ids->Text;
940
941     /* Ok! */
942     WvDevInit(dev);
943     dev->IrpMj = &irp_mj;
944     dev->ext = dummy_ids;       /* TODO: Implement a dummy free.  Leaking. */
945     dev->Self = pdo;
946     WvDevForDevObj(pdo, dev);
947     WvBusInitNode(&dev->BusNode, pdo);
948     /* Associate the parent bus. */
949     dev->Parent = WvDriverBus_.Fdo;
950     /* Add the new PDO device to the bus' list of children. */
951     WvBusAddNode(&WvDriverBus_, &dev->BusNode);
952     dev->DevNum = WvBusGetNodeNum(&dev->BusNode);
953     pdo->Flags &= ~DO_DEVICE_INITIALIZING;
954
955     dummy_context->Status = STATUS_SUCCESS;
956     KeSetEvent(dummy_context->Event, 0, FALSE);
957     return;
958
959     wv_free(dummy_ids);
960     err_dummy_ids:
961
962     wv_free(dev);
963     err_dev:
964
965     IoDeleteDevice(pdo);
966     err_create_pdo:
967
968     KeSetEvent(dummy_context->Event, 0, FALSE);
969     return;
970   }
971
972 /**
973  * Produce a dummy PDO node on the main bus.
974  *
975  * @v DummyIds                  The PnP IDs for the dummy.
976  * @v DevType                   The type for the dummy device.
977  * @v DevCharacteristics        The dummy device characteristics.
978  * @ret NTSTATUS                The status of the operation.
979  */
980 winvblock__lib_func NTSTATUS STDCALL WvDriverAddDummy(
981     IN const WV_S_DRIVER_DUMMY_IDS * DummyIds,
982     IN DEVICE_TYPE DevType,
983     IN ULONG DevCharacteristics
984   ) {
985     KEVENT event;
986     WV_S_DRIVER_ADD_DUMMY_ context = {
987         DummyIds,
988         DevType,
989         DevCharacteristics,
990         &event,
991         STATUS_UNSUCCESSFUL
992       };
993     WV_S_BUS_CUSTOM_WORK_ITEM work_item = {
994         WvDriverAddDummy_,
995         &context
996       };
997     NTSTATUS status;
998
999     if (!DummyIds)
1000       return STATUS_INVALID_PARAMETER;
1001
1002     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1003
1004     status = WvBusEnqueueCustomWorkItem(&WvDriverBus_, &work_item);
1005     if (!NT_SUCCESS(status))
1006       return status;
1007
1008     /* Wait for WvDriverAddDummy_() to complete. */
1009     KeWaitForSingleObject(
1010         &event,
1011         Executive,
1012         KernelMode,
1013         FALSE,
1014         NULL
1015       );
1016
1017     return context.Status;
1018   }
1019
1020 /**
1021  * Handle a PnP ID query with a WV_S_DRIVER_DUMMY_IDS object.
1022  *
1023  * @v Irp               The PnP ID query IRP to handle.
1024  * @v DummyIds          The object containing the IDs to respond with.
1025  * @ret NTSTATUS        The status of the operation.
1026  */
1027 winvblock__lib_func NTSTATUS STDCALL WvDriverDummyIds(
1028     IN PIRP Irp,
1029     IN WV_SP_DRIVER_DUMMY_IDS DummyIds
1030   ) {
1031     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(Irp);
1032     BUS_QUERY_ID_TYPE query_type = io_stack_loc->Parameters.QueryId.IdType;
1033     const WCHAR * ids;
1034     winvblock__uint32 len;
1035     NTSTATUS status;
1036
1037     switch (query_type) {
1038         case BusQueryDeviceID:
1039           ids = DummyIds->Ids + DummyIds->DevOffset;
1040           len = DummyIds->DevLen;
1041           break;
1042
1043         case BusQueryInstanceID:
1044           ids = DummyIds->Ids + DummyIds->InstanceOffset;
1045           len = DummyIds->InstanceLen;
1046           break;
1047
1048         case BusQueryHardwareIDs:
1049           ids = DummyIds->Ids + DummyIds->HardwareOffset;
1050           len = DummyIds->HardwareLen;
1051           break;
1052
1053         case BusQueryCompatibleIDs:
1054           ids = DummyIds->Ids + DummyIds->CompatOffset;
1055           len = DummyIds->CompatLen;
1056           break;
1057
1058         default:
1059           return driver__complete_irp(Irp, 0, STATUS_NOT_SUPPORTED);
1060       }
1061
1062     /* Allocate the return buffer. */
1063     Irp->IoStatus.Information = (ULONG_PTR) wv_palloc(len * sizeof *ids);
1064     if (Irp->IoStatus.Information == 0) {
1065         DBG("wv_palloc failed.\n");
1066         status = STATUS_INSUFFICIENT_RESOURCES;
1067         goto alloc_info;
1068       }
1069     /* Copy the working buffer to the return buffer. */
1070     RtlCopyMemory(
1071         (void *) Irp->IoStatus.Information,
1072         ids,
1073         len * sizeof *ids
1074       );
1075     status = STATUS_SUCCESS;
1076
1077     /* irp->IoStatus.Information not freed. */
1078     alloc_info:
1079
1080     return driver__complete_irp(Irp, Irp->IoStatus.Information, status);
1081   }