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