3659cb7860845a46509faab93423b89fc3bc34d0
[people/sha0/winvblock.git] / src / winvblock / irp.c
1 /**
2  * Copyright (C) 2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  *
4  * This file is part of WinVBlock, derived from WinAoE.
5  * For WinAoE contact information, see http://winaoe.org/
6  *
7  * WinVBlock is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * WinVBlock is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 /**
22  * @file
23  *
24  * Handling IRPs.
25  */
26
27 #include <ntddk.h>
28 #include <stdlib.h>
29
30 #include "winvblock.h"
31 #include "wv_stdlib.h"
32 #include "portable.h"
33 #include "irp.h"
34 #include "driver.h"
35 #include "device.h"
36 #include "debug.h"
37
38 /* Forward declarations. */
39 static device__thread_func (irp_thread);
40
41 /*
42  * An internal type.  A device extension will have a
43  * pointer to this structure, but its type will be a void pointer
44  */
45 winvblock__def_struct ( handler_chain )
46 {
47   /*
48    * Points to an array of irp__handlings 
49    */
50   irp__handling *table;
51   /*
52    * Total table size, in bytes 
53    */
54   size_t size;
55   /*
56    * Points to the next table in the chain or NULL
57    */
58   handler_chain_ptr next;
59 };
60
61 /**
62  * Register an IRP handling table with a chain (with table size)
63  *
64  * @v chain_ptr Pointer to IRP handler chain to attach a table to
65  * @v table     Table to add
66  * @v size      Size of the table to add, in bytes
67  * @ret         FALSE for failure, TRUE for success
68  */
69 winvblock__lib_func winvblock__bool
70 irp__reg_table_s (
71   IN OUT irp__handler_chain_ptr chain_ptr,
72   IN irp__handling_ptr table,
73   IN size_t size
74  )
75 {
76   /*
77    * The type used by a device extension is opaque
78    */
79   handler_chain_ptr *link = ( handler_chain_ptr * ) chain_ptr,
80     new_link;
81
82   if ( link == NULL )
83     /*
84      * Nothing to attach to
85      */
86     return FALSE;
87   /*
88    * Allocate and attach a new link in the chain.
89    * Maybe we should use a spin-lock for this
90    */
91   new_link = wv_malloc(sizeof *new_link);
92   if ( new_link == NULL )
93     {
94       /*
95        * Really too bad
96        */
97       DBG ( "Could not allocate IRP handler chain!\n" );
98
99       return FALSE;
100     }
101   new_link->table = table;
102   /*
103    * Could sanity-check the size to be a multiple of sizeof(irp__handling)
104    */
105   new_link->size = size;
106   new_link->next = *link;
107   *link = new_link;
108   return TRUE;
109 }
110
111 /**
112  * Un-register an IRP handling table from a chain
113  *
114  * @v chain_ptr Pointer to IRP handler chain to remove table from
115  * @v table     Table to remove
116  * @ret         FALSE for failure, TRUE for success
117  */
118 winvblock__lib_func winvblock__bool
119 irp__unreg_table (
120   IN OUT irp__handler_chain_ptr chain_ptr,
121   IN irp__handling_ptr table
122  )
123 {
124   winvblock__bool done = FALSE;
125   /*
126    * The type used by a device extension is opaque
127    */
128   handler_chain_ptr *link = ( handler_chain_ptr * ) chain_ptr;
129
130   if ( link == NULL )
131     /*
132      * No chain given
133      */
134     return FALSE;
135   /*
136    * Walk the chain, looking for the given table
137    */
138   while ( *link != NULL )
139     {
140       if ( link[0]->table == table )
141         {
142           /*
143            * Remove this link in the chain
144            */
145           handler_chain_ptr next = link[0]->next;
146     wv_free(*link);
147           *link = next;
148           return TRUE;
149         }
150       link = &link[0]->next;
151     }
152
153   DBG ( "Table not found\n" );
154   return FALSE;
155 }
156
157 /**
158  * Walk a mini IRP handling table and process matches.
159  *
160  * @v dev               The device to process.
161  * @v irp               The IRP to process.
162  * @v table             The mini IRP handling table to use to process the IRP.
163  * @v table_size        The size of the mini IRP handling table, in bytes.
164  * @v completion        Points to a boolean which is TRUE at IRP completion.
165  * @ret                 STATUS_NOT_SUPPORTED for an unhandled IRP.
166  */
167 winvblock__lib_func NTSTATUS STDCALL (irp__process_with_table)(
168     IN PDEVICE_OBJECT (dev),
169     IN PIRP (irp),
170     const irp__handling * (table),
171     size_t (table_size),
172     winvblock__bool * (completion)
173   ) {
174     int (index);
175     PIO_STACK_LOCATION (io_stack_loc);
176     winvblock__bool (handles_major), (handles_minor);
177     NTSTATUS (status) = STATUS_NOT_SUPPORTED;
178
179     /* We could assert this division. */
180     index = table_size / sizeof *table;
181
182     io_stack_loc = IoGetCurrentIrpStackLocation(irp);
183     /* For each entry in the stack, in last-is-first order. */
184     while (index--) {
185         handles_major = (
186             (table[index].irp_major_func == io_stack_loc->MajorFunction) ||
187             table[index].any_major
188           );
189         handles_minor = (
190             (table[index].irp_minor_func == io_stack_loc->MinorFunction) ||
191             table[index].any_minor
192           );
193         if (handles_major && handles_minor) {
194             status = table[index].handler(
195                 dev,
196                 irp,
197                 io_stack_loc,
198                 ((driver__dev_ext_ptr) dev->DeviceExtension)->device,
199                                     completion
200               );
201             /* Do not process the IRP any further down the stack. */
202                   if (*completion) break;
203           } /* if handles */
204       } /* while index */
205
206     return status;
207   }
208
209 /**
210  * Mini IRP handling strategy.
211  *
212  */
213 NTSTATUS STDCALL irp__process(
214     IN PDEVICE_OBJECT DeviceObject,
215     IN PIRP Irp,
216     IN PIO_STACK_LOCATION Stack,
217     IN struct _device__type * dev_ptr,
218     OUT winvblock__bool_ptr completion_ptr
219   )
220   {
221     NTSTATUS status = STATUS_NOT_SUPPORTED;
222     handler_chain_ptr link;
223   
224     link = ( handler_chain_ptr ) dev_ptr->irp_handler_chain;
225   
226     while ( link != NULL )
227       {
228         status = irp__process_with_table(
229             DeviceObject,
230             Irp,
231             link->table,
232             link->size,
233             completion_ptr
234           );
235         if ( *completion_ptr )
236           break;
237         link = link->next;
238       }
239     return status;
240   }
241
242 static void STDCALL (irp_thread)(IN void * (context)) {
243     device__type * (dev) = context;
244     LARGE_INTEGER (timeout);
245     PLIST_ENTRY (walker);
246
247     /* Wake up at least every second. */
248     timeout.QuadPart = -10000000LL;
249
250     /* While the device is active... */
251     while (dev->thread) {
252         /* Wait for the signal or the timeout. */
253         KeWaitForSingleObject(
254             &dev->thread_wakeup,
255             Executive,
256             KernelMode,
257             FALSE,
258             &timeout
259           );
260         KeResetEvent(&dev->thread_wakeup);
261         /* Process each IRP in the queue. */
262         while (walker = ExInterlockedRemoveHeadList(
263             &dev->irp_list,
264                                           &dev->irp_list_lock
265           )) {
266             NTSTATUS (status);
267             PIRP (irp) = CONTAINING_RECORD(walker, IRP, Tail.Overlay.ListEntry);
268             winvblock__bool (completion) = FALSE;
269
270             /* Process the IRP. */
271             status = irp__process(
272                 dev->Self,
273                 irp,
274                 IoGetCurrentIrpStackLocation(irp),
275                 dev,
276                 &completion
277               );
278             #ifdef DEBUGIRPS
279             if (status != STATUS_PENDING) Debug_IrpEnd(Irp, status);
280             #endif
281           } /* while walker */
282       } /* while active */
283     /* The device has finished. */
284     dev->ops.free(dev);
285
286     return;
287   }