[dummy] Pass back the created PDO
[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 driver.c */
39 extern WVL_S_BUS_T WvBus;
40
41 /* Forward declarations. */
42 static NTSTATUS STDCALL WvDummyIds(IN PIRP, IN WV_SP_DUMMY_IDS);
43 static WV_F_DEV_PNP WvDummyPnp;
44 static WVL_F_BUS_WORK_ITEM WvDummyAdd_;
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     if (code != IRP_MN_QUERY_ID)
53       return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
54
55     /* The WV_S_DEV_T extension points to the dummy IDs. */
56     return WvDummyIds(irp, dev->ext);
57   }
58
59 typedef struct WV_ADD_DUMMY {
60     const WV_S_DUMMY_IDS * DummyIds;
61     DEVICE_TYPE DevType;
62     ULONG DevCharacteristics;
63     PKEVENT Event;
64     NTSTATUS Status;
65     PDEVICE_OBJECT Pdo;
66   } WV_S_ADD_DUMMY, * WV_SP_ADD_DUMMY;
67
68 /**
69  * Add a dummy PDO child node in the context of the bus' thread.  Internal.
70  *
71  * @v context           Points to the WV_S_ADD_DUMMY to process.
72  */
73 static VOID STDCALL WvDummyAdd_(PVOID context) {
74     WV_SP_ADD_DUMMY dummy_context = context;
75     NTSTATUS status;
76     PDEVICE_OBJECT pdo = NULL;
77     WV_SP_DEV_T dev;
78     SIZE_T dummy_ids_size;
79     WV_SP_DUMMY_IDS dummy_ids;
80     static WV_S_DEV_IRP_MJ irp_mj = {
81         (WV_FP_DEV_DISPATCH) 0,
82         (WV_FP_DEV_DISPATCH) 0,
83         (WV_FP_DEV_CTL) 0,
84         (WV_FP_DEV_SCSI) 0,
85         WvDummyPnp,
86       };
87
88     status = IoCreateDevice(
89         WvDriverObj,
90         sizeof (WV_S_DEV_EXT),
91         NULL,
92         dummy_context->DevType,
93         dummy_context->DevCharacteristics,
94         FALSE,
95         &pdo
96       );
97     if (!NT_SUCCESS(status) || !pdo) {
98         DBG("Couldn't create dummy device.\n");
99         dummy_context->Status = STATUS_INSUFFICIENT_RESOURCES;
100         goto err_create_pdo;
101       }
102
103     dev = wv_malloc(sizeof *dev);
104     if (!dev) {
105         DBG("Couldn't allocate dummy device.\n");
106         dummy_context->Status = STATUS_INSUFFICIENT_RESOURCES;
107         goto err_dev;
108       }
109
110     dummy_ids_size =
111       sizeof *dummy_ids +
112       dummy_context->DummyIds->Len * sizeof dummy_ids->Text[0] -
113       sizeof dummy_ids->Text[0];        /* The struct hack uses a WCHAR[1]. */
114     dummy_ids = wv_malloc(dummy_ids_size);
115     if (!dummy_ids) {
116         DBG("Couldn't allocate dummy IDs.\n");
117         dummy_context->Status = STATUS_INSUFFICIENT_RESOURCES;
118         goto err_dummy_ids;
119       }
120     /* Copy the IDs offsets and lengths. */
121     RtlCopyMemory(dummy_ids, dummy_context->DummyIds, sizeof *dummy_ids);
122     /* Copy the text of the IDs. */
123     RtlCopyMemory(
124         &dummy_ids->Text,
125         dummy_context->DummyIds->Ids,
126         dummy_context->DummyIds->Len * sizeof dummy_ids->Text[0]
127       );
128     /* Point to the copy of the text. */
129     dummy_ids->Ids = dummy_ids->Text;
130
131     /* Ok! */
132     WvDevInit(dev);
133     dev->IrpMj = &irp_mj;
134     dev->ext = dummy_ids;       /* TODO: Implement a dummy free.  Leaking. */
135     dev->Self = pdo;
136     WvDevForDevObj(pdo, dev);
137     WvlBusInitNode(&dev->BusNode, pdo);
138     /* Associate the parent bus. */
139     dev->Parent = WvBus.Fdo;
140     /* Add the new PDO device to the bus' list of children. */
141     WvlBusAddNode(&WvBus, &dev->BusNode);
142     dev->DevNum = WvlBusGetNodeNum(&dev->BusNode);
143     pdo->Flags &= ~DO_DEVICE_INITIALIZING;
144
145     dummy_context->Status = STATUS_SUCCESS;
146     dummy_context->Pdo = pdo;
147     KeSetEvent(dummy_context->Event, 0, FALSE);
148     return;
149
150     wv_free(dummy_ids);
151     err_dummy_ids:
152
153     wv_free(dev);
154     err_dev:
155
156     IoDeleteDevice(pdo);
157     err_create_pdo:
158
159     KeSetEvent(dummy_context->Event, 0, FALSE);
160     return;
161   }
162
163 /**
164  * Produce a dummy PDO node on the main bus.
165  *
166  * @v DummyIds                  The PnP IDs for the dummy.
167  * @v DevType                   The type for the dummy device.
168  * @v DevCharacteristics        The dummy device characteristics.
169  * @v Pdo                       Filled with a pointer to the created PDO.
170  * @ret NTSTATUS                The status of the operation.
171  */
172 WVL_M_LIB NTSTATUS STDCALL WvDummyAdd(
173     IN const WV_S_DUMMY_IDS * DummyIds,
174     IN DEVICE_TYPE DevType,
175     IN ULONG DevCharacteristics,
176     OUT PDEVICE_OBJECT * Pdo
177   ) {
178     KEVENT event;
179     WV_S_ADD_DUMMY context = {
180         DummyIds,
181         DevType,
182         DevCharacteristics,
183         &event,
184         STATUS_UNSUCCESSFUL,
185         NULL
186       };
187     WVL_S_BUS_CUSTOM_WORK_ITEM work_item = {
188         WvDummyAdd_,
189         &context
190       };
191     NTSTATUS status;
192
193     if (!DummyIds)
194       return STATUS_INVALID_PARAMETER;
195
196     if (!WvBus.Fdo)
197       return STATUS_NO_SUCH_DEVICE;
198
199     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
200
201     status = WvlBusEnqueueCustomWorkItem(&WvBus, &work_item);
202     if (!NT_SUCCESS(status))
203       return status;
204
205     /* Wait for WvDummyAdd_() to complete. */
206     KeWaitForSingleObject(
207         &event,
208         Executive,
209         KernelMode,
210         FALSE,
211         NULL
212       );
213
214     if (context.Pdo)
215       *Pdo = context.Pdo;
216     return context.Status;
217   }
218
219 /**
220  * Handle a PnP ID query with a WV_S_DUMMY_IDS object.
221  *
222  * @v Irp               The PnP ID query IRP to handle.
223  * @v DummyIds          The object containing the IDs to respond with.
224  * @ret NTSTATUS        The status of the operation.
225  */
226 static NTSTATUS STDCALL WvDummyIds(
227     IN PIRP Irp,
228     IN WV_SP_DUMMY_IDS DummyIds
229   ) {
230     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(Irp);
231     BUS_QUERY_ID_TYPE query_type = io_stack_loc->Parameters.QueryId.IdType;
232     const WCHAR * ids;
233     UINT32 len;
234     NTSTATUS status;
235
236     switch (query_type) {
237         case BusQueryDeviceID:
238           ids = DummyIds->Ids + DummyIds->DevOffset;
239           len = DummyIds->DevLen;
240           break;
241
242         case BusQueryInstanceID:
243           ids = DummyIds->Ids + DummyIds->InstanceOffset;
244           len = DummyIds->InstanceLen;
245           break;
246
247         case BusQueryHardwareIDs:
248           ids = DummyIds->Ids + DummyIds->HardwareOffset;
249           len = DummyIds->HardwareLen;
250           break;
251
252         case BusQueryCompatibleIDs:
253           ids = DummyIds->Ids + DummyIds->CompatOffset;
254           len = DummyIds->CompatLen;
255           break;
256
257         default:
258           return WvlIrpComplete(Irp, 0, STATUS_NOT_SUPPORTED);
259       }
260
261     /* Allocate the return buffer. */
262     Irp->IoStatus.Information = (ULONG_PTR) wv_palloc(len * sizeof *ids);
263     if (Irp->IoStatus.Information == 0) {
264         DBG("wv_palloc failed.\n");
265         status = STATUS_INSUFFICIENT_RESOURCES;
266         goto alloc_info;
267       }
268     /* Copy the working buffer to the return buffer. */
269     RtlCopyMemory(
270         (PVOID) Irp->IoStatus.Information,
271         ids,
272         len * sizeof *ids
273       );
274     status = STATUS_SUCCESS;
275
276     /* irp->IoStatus.Information not freed. */
277     alloc_info:
278
279     return WvlIrpComplete(Irp, Irp->IoStatus.Information, status);
280   }