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