[dummy,aoe] Move dummy creation IOCTL setup from AoE
[people/sha0/winvblock.git] / src / winvblock / dummy.c
1 /**
2  * Copyright (C) 2010-2011, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  *
4  * This file is part of WinVBlock, originally derived from WinAoE.
5  *
6  * WinVBlock is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * WinVBlock is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /**
21  * @file
22  *
23  * Dummy device specifics.
24  */
25
26 #include <ntddk.h>
27
28 #include "portable.h"
29 #include "winvblock.h"
30 #include "wv_stdlib.h"
31 #include "irp.h"
32 #include "driver.h"
33 #include "bus.h"
34 #include "device.h"
35 #include "dummy.h"
36 #include "debug.h"
37
38 /* From bus.c */
39 extern WVL_S_BUS_T WvBus;
40 extern NTSTATUS STDCALL WvBusRemoveDev(IN WV_SP_DEV_T);
41
42 /* Forward declarations. */
43 static NTSTATUS STDCALL WvDummyIds(IN PIRP, IN WV_SP_DUMMY_IDS);
44 static WV_F_DEV_PNP WvDummyPnp;
45
46 /* Dummy PnP IRP handler. */
47 static NTSTATUS STDCALL WvDummyPnp(
48     IN WV_SP_DEV_T dev,
49     IN PIRP irp,
50     IN UCHAR code
51   ) {
52     switch (code) {
53         case IRP_MN_QUERY_ID:
54           /* The WV_S_DEV_T extension points to the dummy IDs. */
55           return WvDummyIds(irp, dev->ext);
56
57         case IRP_MN_QUERY_REMOVE_DEVICE:
58           return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
59
60         case IRP_MN_REMOVE_DEVICE:
61           /* Any error status for the removal slips away, here. */
62           WvBusRemoveDev(dev);
63           return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
64
65         default:
66           /* Return whatever upper drivers in the stack yielded. */
67           return WvlIrpComplete(
68               irp,
69               irp->IoStatus.Information,
70               irp->IoStatus.Status
71             );
72       }
73   }
74
75 typedef struct WV_ADD_DUMMY {
76     const WV_S_DUMMY_IDS * DummyIds;
77     DEVICE_TYPE DevType;
78     ULONG DevCharacteristics;
79     PKEVENT Event;
80     NTSTATUS Status;
81     PDEVICE_OBJECT Pdo;
82   } WV_S_ADD_DUMMY, * WV_SP_ADD_DUMMY;
83
84 /* Produce a dummy PDO node on the main bus.  Internal */
85 static NTSTATUS STDCALL WvDummyAdd_(
86     IN WV_SP_DUMMY_IDS DummyIds,
87     IN PDEVICE_OBJECT * Pdo
88   ) {
89     static WV_S_DEV_IRP_MJ irp_mj = {
90         (WV_FP_DEV_DISPATCH) 0,
91         (WV_FP_DEV_DISPATCH) 0,
92         (WV_FP_DEV_CTL) 0,
93         (WV_FP_DEV_SCSI) 0,
94         WvDummyPnp,
95       };
96     NTSTATUS status;
97     PDEVICE_OBJECT pdo = NULL;
98     WV_SP_DEV_T dev;
99     WV_SP_DUMMY_IDS new_dummy_ids;
100
101     status = IoCreateDevice(
102         WvDriverObj,
103         sizeof (WV_S_DEV_EXT),
104         NULL,
105         DummyIds->DevType,
106         DummyIds->DevCharacteristics,
107         FALSE,
108         &pdo
109       );
110     if (!NT_SUCCESS(status) || !pdo) {
111         DBG("Couldn't create dummy device.\n");
112         status = STATUS_INSUFFICIENT_RESOURCES;
113         goto err_create_pdo;
114       }
115
116     dev = wv_malloc(sizeof *dev);
117     if (!dev) {
118         DBG("Couldn't allocate dummy device.\n");
119         status = STATUS_INSUFFICIENT_RESOURCES;
120         goto err_dev;
121       }
122
123     new_dummy_ids = wv_malloc(DummyIds->Len);
124     if (!new_dummy_ids) {
125         DBG("Couldn't allocate dummy IDs.\n");
126         status = STATUS_INSUFFICIENT_RESOURCES;
127         goto err_dummy_ids;
128       }
129
130     /* Copy the IDs' offsets and lengths. */
131     RtlCopyMemory(new_dummy_ids, DummyIds, DummyIds->Len);
132
133     /* Ok! */
134     WvDevInit(dev);
135     dev->IrpMj = &irp_mj;
136     dev->ext = new_dummy_ids;   /* TODO: Implement a dummy free.  Leaking. */
137     dev->Self = pdo;
138     /* Optionally fill the caller's PDO pointer. */
139     if (Pdo)
140       *Pdo = pdo;
141     WvDevForDevObj(pdo, dev);
142     WvlBusInitNode(&dev->BusNode, pdo);
143     /* Associate the parent bus. */
144     dev->Parent = WvBus.Fdo;
145     /* Add the new PDO device to the bus' list of children. */
146     WvlBusAddNode(&WvBus, &dev->BusNode);
147     dev->DevNum = WvlBusGetNodeNum(&dev->BusNode);
148     pdo->Flags &= ~DO_DEVICE_INITIALIZING;
149
150     return STATUS_SUCCESS;
151
152     wv_free(new_dummy_ids);
153     err_dummy_ids:
154
155     wv_free(dev);
156     err_dev:
157
158     IoDeleteDevice(pdo);
159     err_create_pdo:
160
161     return status;
162   }
163
164 /**
165  * Produce a dummy PDO node on the main bus.
166  *
167  * @v DummyIds                  The PnP IDs for the dummy.  Also includes
168  *                              the device type and characteristics.
169  * @ret NTSTATUS                The status of the operation.
170  */
171 WVL_M_LIB NTSTATUS STDCALL WvDummyAdd(
172     IN const WV_S_DUMMY_IDS * DummyIds
173   ) {
174     KEVENT signal;
175     PIRP irp;
176     IO_STATUS_BLOCK io_status = {0};
177     NTSTATUS status;
178
179     /*
180      * In this function, we actually send the request through to the
181      * driver via an IRP.  This is a good exercise, since we expect a
182      * user-land utility to be capable of the same.
183      */
184     if (!WvBus.Fdo)
185       return STATUS_NO_SUCH_DEVICE;
186
187     /* Prepare the request. */
188     KeInitializeEvent(&signal, SynchronizationEvent, FALSE);
189     irp = IoBuildDeviceIoControlRequest(
190         IOCTL_WV_DUMMY,
191         WvBus.Fdo,
192         (PVOID) DummyIds,
193         DummyIds->Len,
194         NULL,
195         0,
196         FALSE,
197         &signal,
198         &io_status
199       );
200     if (!irp)
201       return STATUS_INSUFFICIENT_RESOURCES;
202
203     status = IoCallDriver(WvBus.Fdo, irp);
204     if (status == STATUS_PENDING) {
205         KeWaitForSingleObject(
206             &signal,
207             Executive,
208             KernelMode,
209             FALSE,
210             NULL
211           );
212         status = io_status.Status;
213       }
214     return status;
215   }
216
217 /**
218  * Remove a dummy PDO node on the WinVBlock bus.
219  *
220  * @v Pdo               The PDO to remove.
221  * @ret NTSTATUS        The status of the operation.
222  *
223  * It might actually be better to handle a PnP remove IOCTL.
224  */
225 WVL_M_LIB NTSTATUS STDCALL WvDummyRemove(IN PDEVICE_OBJECT Pdo) {
226     /* Sanity check. */
227     if (Pdo->DriverObject == WvDriverObj)
228       return WvBusRemoveDev(WvDevFromDevObj(Pdo));
229     return STATUS_INVALID_PARAMETER;
230   }
231
232 /**
233  * Handle a dummy IOCTL for creating a dummy PDO.
234  *
235  * @v Irp               An IRP with an associated buffer containing
236  *                      WV_S_DUMMY_IDS data.
237  * @ret NTSTATUS        The status of the operation.
238  */
239 NTSTATUS STDCALL WvDummyIoctl(IN PIRP Irp) {
240     WV_SP_DUMMY_IDS dummy_ids = Irp->AssociatedIrp.SystemBuffer;
241     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(Irp);
242
243     if (
244         io_stack_loc->Parameters.DeviceIoControl.InputBufferLength <
245         sizeof *dummy_ids
246       ) {
247         DBG("Dummy IDs too small in IRP %p.\n", Irp);
248         return WvlIrpComplete(Irp, 0, STATUS_INVALID_PARAMETER);
249       }
250
251     return WvlIrpComplete(Irp, 0, WvDummyAdd_(dummy_ids, NULL));
252   }
253
254 /**
255  * Handle a PnP ID query with a WV_S_DUMMY_IDS object.
256  *
257  * @v Irp               The PnP ID query IRP to handle.
258  * @v DummyIds          The object containing the IDs to respond with.
259  * @ret NTSTATUS        The status of the operation.
260  */
261 static NTSTATUS STDCALL WvDummyIds(
262     IN PIRP Irp,
263     IN WV_SP_DUMMY_IDS DummyIds
264   ) {
265     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(Irp);
266     BUS_QUERY_ID_TYPE query_type = io_stack_loc->Parameters.QueryId.IdType;
267     const CHAR * start;
268     const WCHAR * ids;
269     UINT32 len;
270     NTSTATUS status;
271
272     start = (const CHAR *) DummyIds + DummyIds->Offset;
273     ids = (const WCHAR *) start;
274     switch (query_type) {
275         case BusQueryDeviceID:
276           ids += DummyIds->DevOffset;
277           len = DummyIds->DevLen;
278           break;
279
280         case BusQueryInstanceID:
281           ids += DummyIds->InstanceOffset;
282           len = DummyIds->InstanceLen;
283           break;
284
285         case BusQueryHardwareIDs:
286           ids += DummyIds->HardwareOffset;
287           len = DummyIds->HardwareLen;
288           break;
289
290         case BusQueryCompatibleIDs:
291           ids += DummyIds->CompatOffset;
292           len = DummyIds->CompatLen;
293           break;
294
295         default:
296           return WvlIrpComplete(Irp, 0, STATUS_NOT_SUPPORTED);
297       }
298
299     /* Allocate the return buffer. */
300     Irp->IoStatus.Information = (ULONG_PTR) wv_palloc(len * sizeof *ids);
301     if (Irp->IoStatus.Information == 0) {
302         DBG("wv_palloc failed.\n");
303         status = STATUS_INSUFFICIENT_RESOURCES;
304         goto alloc_info;
305       }
306     /* Copy the working buffer to the return buffer. */
307     RtlCopyMemory(
308         (PVOID) Irp->IoStatus.Information,
309         ids,
310         len * sizeof *ids
311       );
312     status = STATUS_SUCCESS;
313
314     /* irp->IoStatus.Information not freed. */
315     alloc_info:
316
317     return WvlIrpComplete(Irp, Irp->IoStatus.Information, status);
318   }