[driver,bus] Remove WvBusPower
[people/sha0/winvblock.git] / src / winvblock / driver.c
1 /**
2  * Copyright (C) 2009-2011, 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 "portable.h"
33 #include "winvblock.h"
34 #include "wv_stdlib.h"
35 #include "wv_string.h"
36 #include "irp.h"
37 #include "driver.h"
38 #include "bus.h"
39 #include "device.h"
40 #include "disk.h"
41 #include "registry.h"
42 #include "mount.h"
43 #include "filedisk.h"
44 #include "ramdisk.h"
45 #include "debug.h"
46
47 /* From bus.c */
48 extern WVL_S_BUS_T WvBus;
49 extern WV_S_DEV_T WvBusDev;
50 extern UNICODE_STRING WvBusName;
51 extern UNICODE_STRING WvBusDosname;
52 extern PETHREAD WvBusThread;
53 extern WV_F_DEV_DISPATCH WvBusSysCtl;
54 extern WV_F_DEV_CTL WvBusDevCtl;
55 extern WV_F_DEV_PNP WvBusPnp;
56 extern WVL_F_BUS_PNP WvBusPnpQueryDevText;
57 extern NTSTATUS STDCALL WvBusEstablish(IN PUNICODE_STRING);
58
59 /* Exported. */
60 PDRIVER_OBJECT WvDriverObj = NULL;
61
62 /* Globals. */
63 static PVOID WvDriverStateHandle;
64 static BOOLEAN WvDriverStarted = FALSE;
65 /* Contains TXTSETUP.SIF/BOOT.INI-style OsLoadOptions parameters. */
66 static LPWSTR WvOsLoadOpts = NULL;
67
68 /* Forward declarations. */
69 static DRIVER_DISPATCH WvIrpNotSupported;
70 static __drv_dispatchType(IRP_MJ_POWER) DRIVER_DISPATCH WvIrpPower;
71 static
72   __drv_dispatchType(IRP_MJ_CREATE)
73   __drv_dispatchType(IRP_MJ_CLOSE)
74   DRIVER_DISPATCH WvIrpCreateClose;
75 static __drv_dispatchType(IRP_MJ_SYSTEM_CONTROL)
76   DRIVER_DISPATCH WvIrpSysCtl;
77 static __drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
78   DRIVER_DISPATCH WvIrpDevCtl;
79 static __drv_dispatchType(IRP_MJ_SCSI) DRIVER_DISPATCH WvIrpScsi;
80 static __drv_dispatchType(IRP_MJ_PNP) DRIVER_DISPATCH WvIrpPnp;
81 static DRIVER_UNLOAD WvUnload;
82
83 static LPWSTR STDCALL WvGetOpt(IN LPWSTR opt_name) {
84     LPWSTR our_opts, the_opt;
85     WCHAR our_sig[] = L"WINVBLOCK=";
86     /* To produce constant integer expressions. */
87     enum {
88         our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
89         our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
90       };
91     size_t opt_name_len, opt_name_len_bytes;
92
93     if (!WvOsLoadOpts || !opt_name)
94       return NULL;
95
96     /* Find /WINVBLOCK= options. */
97     our_opts = WvOsLoadOpts;
98     while (*our_opts != L'\0') {
99         if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
100             our_opts++;
101             continue;
102           }
103         our_opts += our_sig_len;
104         break;
105       }
106
107     /* Search for the specific option. */
108     the_opt = our_opts;
109     opt_name_len = wcslen(opt_name);
110     opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
111     while (*the_opt != L'\0' && *the_opt != L' ') {
112         if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
113             while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
114               the_opt++;
115             continue;
116           }
117         the_opt += opt_name_len;
118         break;
119       }
120
121     if (*the_opt == L'\0' || *the_opt == L' ')
122       return NULL;
123
124     /* Next should come "=". */
125     if (*the_opt != L'=')
126       return NULL;
127
128     /*
129      * And finally our option's value.  The caller needs
130      * to worry about looking past the end of the option.
131      */
132     the_opt++;
133     if (*the_opt == L'\0' || *the_opt == L' ')
134       return NULL;
135     return the_opt;
136   }
137
138 static NTSTATUS STDCALL WvAttachFdo(
139     IN PDRIVER_OBJECT DriverObject,
140     IN PDEVICE_OBJECT Pdo
141   ) {
142     NTSTATUS status;
143     PLIST_ENTRY walker;
144     PDEVICE_OBJECT fdo = NULL;
145     static WV_S_DEV_IRP_MJ irp_mj = {
146         (WV_FP_DEV_DISPATCH) 0,
147         WvBusSysCtl,
148         WvBusDevCtl,
149         (WV_FP_DEV_SCSI) 0,
150         WvBusPnp,
151       };
152
153     DBG("Entry\n");
154     /* Do we alreay have our main bus? */
155     if (WvBus.Fdo) {
156         DBG("Already have the main bus.  Refusing.\n");
157         status = STATUS_NOT_SUPPORTED;
158         goto err_already_established;
159       }
160     /* Initialize the bus. */
161     WvlBusInit(&WvBus);
162     WvDevInit(&WvBusDev);
163     /* Create the bus FDO. */
164     status = IoCreateDevice(
165         DriverObject,
166         sizeof (WV_S_DEV_EXT),
167         &WvBusName,
168         FILE_DEVICE_CONTROLLER,
169         FILE_DEVICE_SECURE_OPEN,
170         FALSE,
171         &fdo
172       );
173     if (!NT_SUCCESS(status)) {
174         DBG("IoCreateDevice() failed!\n");
175         goto err_fdo;
176       }
177     /* DosDevice symlink. */
178     status = IoCreateSymbolicLink(
179         &WvBusDosname,
180         &WvBusName
181       );
182     if (!NT_SUCCESS(status)) {
183         DBG("IoCreateSymbolicLink() failed!\n");
184         goto err_dos_symlink;
185       }
186     /* Set associations for the bus, device, FDO, PDO. */
187     WvDevForDevObj(fdo, &WvBusDev);
188     WvBusDev.Self = WvBus.Fdo = fdo;
189     WvBusDev.IsBus = TRUE;
190     WvBusDev.IrpMj = &irp_mj;
191     WvBus.QueryDevText = WvBusPnpQueryDevText;
192     WvBus.Pdo = Pdo;
193     fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
194     fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
195     /* Attach the FDO to the PDO. */
196     WvBus.LowerDeviceObject = IoAttachDeviceToDeviceStack(
197         fdo,
198         Pdo
199       );
200     if (WvBus.LowerDeviceObject == NULL) {
201         status = STATUS_NO_SUCH_DEVICE;
202         DBG("IoAttachDeviceToDeviceStack() failed!\n");
203         goto err_attach;
204       }
205     status = WvlBusStartThread(&WvBus, &WvBusThread);
206     if (!NT_SUCCESS(status)) {
207         DBG("Couldn't start bus thread!\n");
208         goto err_thread;
209       }
210     /* Ok! */
211     fdo->Flags &= ~DO_DEVICE_INITIALIZING;
212     #ifdef RIS
213     WvBus.Dev.State = Started;
214     #endif
215     DBG("Exit\n");
216     return STATUS_SUCCESS;
217
218     err_thread:
219
220     err_attach:
221
222     IoDeleteSymbolicLink(&WvBusDosname);
223     err_dos_symlink:
224
225     IoDeleteDevice(fdo);
226     err_fdo:
227
228     err_already_established:
229
230     DBG("Exit with failure\n");
231     return status;
232   }
233
234 /*
235  * Note the exception to the function naming convention.
236  * TODO: See if a Makefile change is good enough.
237  */
238 NTSTATUS STDCALL DriverEntry(
239     IN PDRIVER_OBJECT DriverObject,
240     IN PUNICODE_STRING RegistryPath
241   ) {
242     NTSTATUS status;
243     int i;
244
245     DBG("Entry\n");
246     if (WvDriverObj) {
247         DBG("Re-entry not allowed!\n");
248         return STATUS_NOT_SUPPORTED;
249       }
250     WvDriverObj = DriverObject;
251     if (WvDriverStarted)
252       return STATUS_SUCCESS;
253     Debug_Initialize();
254     status = WvlRegNoteOsLoadOpts(&WvOsLoadOpts);
255     if (!NT_SUCCESS(status))
256       return WvlError("WvlRegNoteOsLoadOpts", status);
257
258     WvDriverStateHandle = NULL;
259
260     if ((WvDriverStateHandle = PoRegisterSystemState(
261         NULL,
262         ES_CONTINUOUS
263       )) == NULL) {
264         DBG("Could not set system state to ES_CONTINUOUS!!\n");
265       }
266     /*
267      * Set up IRP MajorFunction function table for devices
268      * this driver handles.
269      */
270     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
271       DriverObject->MajorFunction[i] = WvIrpNotSupported;
272     DriverObject->MajorFunction[IRP_MJ_PNP] = WvIrpPnp;
273     DriverObject->MajorFunction[IRP_MJ_POWER] = WvIrpPower;
274     DriverObject->MajorFunction[IRP_MJ_CREATE] = WvIrpCreateClose;
275     DriverObject->MajorFunction[IRP_MJ_CLOSE] = WvIrpCreateClose;
276     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
277       WvIrpSysCtl;
278     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
279       WvIrpDevCtl;
280     DriverObject->MajorFunction[IRP_MJ_SCSI] = WvIrpScsi;
281     /* Set the driver Unload callback. */
282     DriverObject->DriverUnload = WvUnload;
283     /* Set the driver AddDevice callback. */
284     DriverObject->DriverExtension->AddDevice = WvAttachFdo;
285     /* Initialize various modules. */
286     disk__module_init();        /* TODO: Check for error. */
287     filedisk__module_init();    /* TODO: Check for error. */
288     ramdisk__module_init();     /* TODO: Check for error. */
289
290     /* Establish the bus PDO. */
291     status = WvBusEstablish(RegistryPath);
292     if(!NT_SUCCESS(status))
293       goto err_bus;
294
295     WvDriverStarted = TRUE;
296     DBG("Exit\n");
297     return STATUS_SUCCESS;
298
299     err_bus:
300
301     WvUnload(DriverObject);
302     DBG("Exit due to failure\n");
303     return status;
304   }
305
306 static NTSTATUS STDCALL WvIrpNotSupported(
307     IN PDEVICE_OBJECT dev_obj,
308     IN PIRP irp
309   ) {
310     irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
311     IoCompleteRequest(irp, IO_NO_INCREMENT);
312     return irp->IoStatus.Status;
313   }
314
315 /* Handle a power IRP. */
316 static NTSTATUS WvIrpPower(
317     IN PDEVICE_OBJECT dev_obj,
318     IN PIRP irp
319   ) {
320     WV_SP_DEV_T dev;
321
322     #ifdef DEBUGIRPS
323     Debug_IrpStart(dev_obj, irp);
324     #endif
325     /* Check for a bus IRP. */
326     if (dev_obj == WvBus.Fdo)
327       return WvlBusPower(&WvBus, irp);
328     /* WvDevFromDevObj() checks for a NULL dev_obj */
329     dev = WvDevFromDevObj(dev_obj);
330     /* Check that the device exists. */
331     if (!dev || dev->State == WvDevStateDeleted) {
332         /* Even if it doesn't, a power IRP is important! */
333         PoStartNextPowerIrp(irp);
334         return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
335       }
336     /* Call the particular device's power handler. */
337     if (dev->IrpMj && dev->IrpMj->Power)
338       return dev->IrpMj->Power(dev, irp);
339     /* Otherwise, we don't support the IRP. */
340     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
341   }
342
343 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
344 static NTSTATUS WvIrpCreateClose(
345     IN PDEVICE_OBJECT dev_obj,
346     IN PIRP irp
347   ) {
348     /* WvDevFromDevObj() checks for a NULL dev_obj */
349     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
350
351     #ifdef DEBUGIRPS
352     Debug_IrpStart(dev_obj, irp);
353     #endif
354     /* Check that the device exists. */
355     if (!dev || dev->State == WvDevStateDeleted)
356       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
357     /* Always succeed with nothing to do. */
358     return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
359   }
360
361 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
362 static NTSTATUS WvIrpSysCtl(
363     IN PDEVICE_OBJECT dev_obj,
364     IN PIRP irp
365   ) {
366     /* WvDevFromDevObj() checks for a NULL dev_obj */
367     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
368
369     #ifdef DEBUGIRPS
370     Debug_IrpStart(dev_obj, irp);
371     #endif
372     /* Check that the device exists. */
373     if (!dev || dev->State == WvDevStateDeleted)
374       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
375     /* Call the particular device's power handler. */
376     if (dev->IrpMj && dev->IrpMj->SysCtl)
377       return dev->IrpMj->SysCtl(dev, irp);
378     /* Otherwise, we don't support the IRP. */
379     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
380   }
381
382 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
383 static NTSTATUS WvIrpDevCtl(
384     IN PDEVICE_OBJECT dev_obj,
385     IN PIRP irp
386   ) {
387     /* WvDevFromDevObj() checks for a NULL dev_obj */
388     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
389     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
390
391     #ifdef DEBUGIRPS
392     Debug_IrpStart(dev_obj, irp);
393     #endif
394     /* Check that the device exists. */
395     if (!dev || dev->State == WvDevStateDeleted)
396       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
397     /* Call the particular device's power handler. */
398     if (dev->IrpMj && dev->IrpMj->DevCtl) {
399         return dev->IrpMj->DevCtl(
400             dev,
401             irp,
402             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
403           );
404       }
405     /* Otherwise, we don't support the IRP. */
406     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
407   }
408
409 /* Handle an IRP_MJ_SCSI IRP. */
410 static NTSTATUS WvIrpScsi(
411     IN PDEVICE_OBJECT dev_obj,
412     IN PIRP irp
413   ) {
414     /* WvDevFromDevObj() checks for a NULL dev_obj */
415     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
416     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
417
418     #ifdef DEBUGIRPS
419     Debug_IrpStart(dev_obj, irp);
420     #endif
421     /* Check that the device exists. */
422     if (!dev || dev->State == WvDevStateDeleted)
423       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
424     /* Call the particular device's power handler. */
425     if (dev->IrpMj && dev->IrpMj->Scsi) {
426         return dev->IrpMj->Scsi(
427             dev,
428             irp,
429             io_stack_loc->Parameters.Scsi.Srb->Function
430           );
431       }
432     /* Otherwise, we don't support the IRP. */
433     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
434   }
435
436 /* Handle an IRP_MJ_PNP IRP. */
437 static NTSTATUS WvIrpPnp(
438     IN PDEVICE_OBJECT dev_obj,
439     IN PIRP irp
440   ) {
441     /* WvDevFromDevObj() checks for a NULL dev_obj */
442     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
443     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
444     NTSTATUS status;
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 WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
452     /* Call the particular device's power handler. */
453     if (dev->IrpMj && dev->IrpMj->Pnp) {
454         status = dev->IrpMj->Pnp(
455             dev,
456             irp,
457             io_stack_loc->MinorFunction
458           );
459         if (dev->IsBus) {
460             dev->OldState = WvBus.OldState;
461             dev->State = WvBus.State;
462           }
463         return status;
464       }
465     /* Otherwise, we don't support the IRP. */
466     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
467   }
468
469 static VOID STDCALL WvUnload(IN PDRIVER_OBJECT DriverObject) {
470     DBG("Unloading...\n");
471     WvBus.Stop = TRUE;
472     KeSetEvent(&WvBus.ThreadSignal, 0, FALSE);
473     if (WvBusThread) {
474         KeWaitForSingleObject(
475             WvBusThread,
476             Executive,
477             KernelMode,
478             FALSE,
479             NULL
480           );
481         ObDereferenceObject(WvBusThread);
482       }
483     if (WvDriverStateHandle != NULL)
484       PoUnregisterSystemState(WvDriverStateHandle);
485     IoDeleteSymbolicLink(&WvBusDosname);
486     WvBus.Fdo = NULL;
487     wv_free(WvOsLoadOpts);
488     WvDriverStarted = FALSE;
489     DBG("Done\n");
490   }
491
492 NTSTATUS STDCALL WvDriverGetDevCapabilities(
493     IN PDEVICE_OBJECT DevObj,
494     IN PDEVICE_CAPABILITIES DevCapabilities
495   ) {
496     IO_STATUS_BLOCK io_status;
497     KEVENT pnp_event;
498     NTSTATUS status;
499     PDEVICE_OBJECT target_obj;
500     PIO_STACK_LOCATION io_stack_loc;
501     PIRP pnp_irp;
502
503     RtlZeroMemory(DevCapabilities, sizeof *DevCapabilities);
504     DevCapabilities->Size = sizeof *DevCapabilities;
505     DevCapabilities->Version = 1;
506     DevCapabilities->Address = -1;
507     DevCapabilities->UINumber = -1;
508
509     KeInitializeEvent(&pnp_event, NotificationEvent, FALSE);
510     target_obj = IoGetAttachedDeviceReference(DevObj);
511     pnp_irp = IoBuildSynchronousFsdRequest(
512         IRP_MJ_PNP,
513         target_obj,
514         NULL,
515         0,
516         NULL,
517         &pnp_event,
518         &io_status
519       );
520     if (pnp_irp == NULL) {
521         status = STATUS_INSUFFICIENT_RESOURCES;
522       } else {
523         pnp_irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
524         io_stack_loc = IoGetNextIrpStackLocation(pnp_irp);
525         RtlZeroMemory(io_stack_loc, sizeof *io_stack_loc);
526         io_stack_loc->MajorFunction = IRP_MJ_PNP;
527         io_stack_loc->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
528         io_stack_loc->Parameters.DeviceCapabilities.Capabilities =
529           DevCapabilities;
530         status = IoCallDriver(target_obj, pnp_irp);
531         if (status == STATUS_PENDING) {
532             KeWaitForSingleObject(
533                 &pnp_event,
534                 Executive,
535                 KernelMode,
536                 FALSE,
537                 NULL
538               );
539             status = io_status.Status;
540           }
541       }
542     ObDereferenceObject(target_obj);
543     return status;
544   }