[driver] Minor style/cosmetic changes
[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
31 #include "winvblock.h"
32 #include "wv_stdlib.h"
33 #include "wv_string.h"
34 #include "portable.h"
35 #include "irp.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 /* Globals. */
47 static void * Driver_Globals_StateHandle;
48 static winvblock__bool Driver_Globals_Started = FALSE;
49 PDRIVER_OBJECT driver__obj_ptr = NULL;
50
51 /* Contains TXTSETUP.SIF/BOOT.INI-style OsLoadOptions parameters */
52 LPWSTR os_load_opts = NULL;
53
54 /* Forward declarations. */
55 static driver__dispatch_func driver_dispatch_not_supported;
56 static driver__dispatch_func driver_dispatch;
57 static void STDCALL Driver_Unload(
58     IN PDRIVER_OBJECT DriverObject
59   );
60
61 static LPWSTR STDCALL get_opt(IN LPWSTR opt_name) {
62     LPWSTR our_opts, the_opt;
63     WCHAR our_sig[] = L"WINVBLOCK=";
64     /* To produce constant integer expressions. */
65     enum {
66         our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
67         our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
68       };
69     size_t opt_name_len, opt_name_len_bytes;
70
71     if (!os_load_opts || !opt_name)
72       return NULL;
73
74     /* Find /WINVBLOCK= options. */
75     our_opts = os_load_opts;
76     while (*our_opts != L'\0') {
77         if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
78             our_opts++;
79             continue;
80           }
81         our_opts += our_sig_len;
82         break;
83       }
84
85     /* Search for the specific option. */
86     the_opt = our_opts;
87     opt_name_len = wcslen(opt_name);
88     opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
89     while (*the_opt != L'\0' && *the_opt != L' ') {
90         if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
91             while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
92               the_opt++;
93             continue;
94           }
95         the_opt += opt_name_len;
96         break;
97       }
98
99     if (*the_opt == L'\0' || *the_opt == L' ')
100       return NULL;
101
102     /* Next should come "=". */
103     if (*the_opt != L'=')
104       return NULL;
105
106     /*
107      * And finally our option's value.  The caller needs
108      * to worry about looking past the end of the option.
109      */
110     the_opt++;
111     if (*the_opt == L'\0' || *the_opt == L' ')
112       return NULL;
113     return the_opt;
114   }
115
116 /*
117  * Note the exception to the function naming convention.
118  * TODO: See if a Makefile change is good enough.
119  */
120 NTSTATUS STDCALL DriverEntry(
121     IN PDRIVER_OBJECT DriverObject,
122     IN PUNICODE_STRING RegistryPath
123   ) {
124     NTSTATUS status;
125     int i;
126
127     DBG("Entry\n");
128     if (driver__obj_ptr) {
129         DBG("Re-entry not allowed!\n");
130         return STATUS_NOT_SUPPORTED;
131       }
132     driver__obj_ptr = DriverObject;
133     if (Driver_Globals_Started)
134       return STATUS_SUCCESS;
135     Debug_Initialize();
136     status = registry__note_os_load_opts(&os_load_opts);
137     if (!NT_SUCCESS(status))
138       return Error("registry__note_os_load_opts", status);
139
140     Driver_Globals_StateHandle = NULL;
141
142     if ((Driver_Globals_StateHandle = PoRegisterSystemState(
143         NULL,
144         ES_CONTINUOUS
145       )) == NULL) {
146         DBG("Could not set system state to ES_CONTINUOUS!!\n");
147       }
148     /*
149      * Set up IRP MajorFunction function table for devices
150      * this driver handles.
151      */
152     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
153       DriverObject->MajorFunction[i] = driver_dispatch_not_supported;
154     DriverObject->MajorFunction[IRP_MJ_PNP] = driver_dispatch;
155     DriverObject->MajorFunction[IRP_MJ_POWER] = driver_dispatch;
156     DriverObject->MajorFunction[IRP_MJ_CREATE] = driver_dispatch;
157     DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver_dispatch;
158     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = driver_dispatch;
159     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver_dispatch;
160     DriverObject->MajorFunction[IRP_MJ_SCSI] = driver_dispatch;
161     /* Set the driver Unload callback. */
162     DriverObject->DriverUnload = Driver_Unload;
163     /* Initialize various modules. */
164     device__init();             /* TODO: Check for error. */
165     bus__module_init();         /* TODO: Check for error. */
166     disk__init();               /* TODO: Check for error. */
167     filedisk__init();           /* TODO: Check for error. */
168     ramdisk__init();            /* TODO: Check for error. */
169
170     Driver_Globals_Started = TRUE;
171     DBG("Exit\n");
172     return STATUS_SUCCESS;
173   }
174
175 static NTSTATUS STDCALL driver_dispatch_not_supported(
176     IN PDEVICE_OBJECT dev_obj,
177     IN PIRP irp
178   ) {
179     irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
180     IoCompleteRequest(irp, IO_NO_INCREMENT);
181     return irp->IoStatus.Status;
182   }
183
184 /* Handle IRP_MJ_CREATE and IRP_MJ_CLOSE */
185 extern winvblock__lib_func NTSTATUS STDCALL driver__create_close(
186     IN PDEVICE_OBJECT dev_obj,
187     IN PIRP irp,
188     IN PIO_STACK_LOCATION stack,
189     IN struct _device__type * dev_ptr,
190     OUT winvblock__bool_ptr completion_ptr
191   ) {
192     NTSTATUS status = STATUS_SUCCESS;
193
194     irp->IoStatus.Status = status;
195     IoCompleteRequest(irp, IO_NO_INCREMENT);
196     *completion_ptr = TRUE;
197     return status;
198   }
199
200 /* IRP is not understood. */
201 extern winvblock__lib_func NTSTATUS STDCALL driver__not_supported(
202     IN PDEVICE_OBJECT dev_obj,
203     IN PIRP irp,
204     IN PIO_STACK_LOCATION stack,
205     IN struct _device__type * dev_ptr,
206     OUT winvblock__bool_ptr completion_ptr
207   ) {
208     NTSTATUS status = STATUS_NOT_SUPPORTED;
209
210     irp->IoStatus.Status = status;
211     IoCompleteRequest(irp, IO_NO_INCREMENT);
212     *completion_ptr = TRUE;
213     return status;
214   }
215
216 static NTSTATUS STDCALL driver_dispatch(
217     IN PDEVICE_OBJECT DeviceObject,
218     IN PIRP Irp
219   ) {
220     NTSTATUS status;
221     device__type_ptr dev_ptr;
222
223     #ifdef DEBUGIRPS
224     Debug_IrpStart(DeviceObject, Irp);
225     #endif
226     dev_ptr = device__get(DeviceObject);
227
228     /* We handle IRP_MJ_POWER as an exception. */
229     if (dev_ptr->State == Deleted) {
230         if (IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_POWER)
231           PoStartNextPowerIrp(Irp);
232         Irp->IoStatus.Information = 0;
233         Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
234         IoCompleteRequest(Irp, IO_NO_INCREMENT);
235         #ifdef DEBUGIRPS
236         Debug_IrpEnd ( Irp, STATUS_NO_SUCH_DEVICE );
237         #endif
238         return STATUS_NO_SUCH_DEVICE;
239       }
240
241     /* Enqueue the IRP for threaded devices, or process immediately. */
242     if (dev_ptr->thread) {
243         IoMarkIrpPending(Irp);
244         ExInterlockedInsertTailList(
245             &dev_ptr->irp_list,
246             /* Where IRPs can be linked. */
247             &Irp->Tail.Overlay.ListEntry,
248             &dev_ptr->irp_list_lock
249           );
250         KeSetEvent(&dev_ptr->thread_wakeup, 0, FALSE);
251         status = STATUS_PENDING;
252       } else
253       status = dev_ptr->dispatch(DeviceObject, Irp);
254
255     return status;
256   }
257
258 /* Place-holder while implementing a dispatch routine per device class. */
259 winvblock__lib_func NTSTATUS STDCALL driver__default_dispatch(
260     IN PDEVICE_OBJECT dev,
261     IN PIRP irp
262   ) {
263     NTSTATUS status;
264     winvblock__bool completion = FALSE;
265     static const irp__handling handling_table[] = {
266         /*
267          * Major, minor, any major?, any minor?, handler
268          * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
269          * Note that the fall-through case must come FIRST!
270          * Why? It sets completion to true, so others won't be called.
271          */
272         {             0, 0,  TRUE, TRUE, driver__not_supported },
273         {  IRP_MJ_CLOSE, 0, FALSE, TRUE,  driver__create_close },
274         { IRP_MJ_CREATE, 0, FALSE, TRUE,  driver__create_close },
275       };
276
277     status = irp__process(
278         dev,
279         irp,
280         IoGetCurrentIrpStackLocation(irp),
281         device__get(dev),
282         &completion
283       );
284     /* Fall through to some driver defaults, if needed. */
285     if (status == STATUS_NOT_SUPPORTED && !completion) {
286         status = irp__process_with_table(
287             dev,
288             irp,
289             handling_table,
290             sizeof handling_table,
291             &completion
292           );
293       }
294     #ifdef DEBUGIRPS
295     if (status != STATUS_PENDING)
296       Debug_IrpEnd(irp, status);
297     #endif
298
299     return status;
300   }
301
302 static void STDCALL Driver_Unload(IN PDRIVER_OBJECT DriverObject) {
303     if (Driver_Globals_StateHandle != NULL)
304       PoUnregisterSystemState(Driver_Globals_StateHandle);
305     bus__module_shutdown();
306     wv_free(os_load_opts);
307     Driver_Globals_Started = FALSE;
308     DBG("Done\n");
309   }
310
311 winvblock__lib_func void STDCALL Driver_CompletePendingIrp(IN PIRP Irp) {
312     #ifdef DEBUGIRPS
313     Debug_IrpEnd(Irp, Irp->IoStatus.Status);
314     #endif
315     IoCompleteRequest(Irp, IO_NO_INCREMENT);
316   }
317
318 /* Note the exception to the function naming convention. */
319 winvblock__lib_func NTSTATUS STDCALL Error(
320     IN PCHAR Message,
321     IN NTSTATUS Status
322   ) {
323     DBG("%s: 0x%08x\n", Message, Status);
324     return Status;
325   }