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