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