[dummy,aoe] Move dummy creation IOCTL setup from AoE
[people/sha0/winvblock.git] / src / aoe / bus.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  * AoE bus specifics.
24  */
25
26 #include <stdio.h>
27 #include <ntddk.h>
28
29 #include "portable.h"
30 #include "winvblock.h"
31 #include "wv_stdlib.h"
32 #include "irp.h"
33 #include "driver.h"
34 #include "bus.h"
35 #include "device.h"
36 #include "dummy.h"
37 #include "aoe.h"
38 #include "mount.h"
39 #include "debug.h"
40
41 /* Names for the AoE bus. */
42 #define AOE_M_BUS_NAME_ (L"\\Device\\AoE")
43 #define AOE_M_BUS_DOSNAME_ (L"\\DosDevices\\AoE")
44
45 /* TODO: Remove this pull from aoe/driver.c */
46 extern NTSTATUS STDCALL AoeBusDevCtlScan(IN PIRP);
47 extern NTSTATUS STDCALL AoeBusDevCtlShow(IN PIRP);
48 extern NTSTATUS STDCALL AoeBusDevCtlMount(IN PIRP);
49
50 /* Forward declarations. */
51 static WV_F_DEV_PNP_ID AoeBusPnpId_;
52 NTSTATUS AoeBusCreate(IN PDRIVER_OBJECT);
53 VOID AoeBusFree(void);
54
55 /* Globals. */
56 WVL_S_BUS_T AoeBusMain = {0};
57 static UNICODE_STRING AoeBusName_ = {
58     sizeof AOE_M_BUS_NAME_ - sizeof (WCHAR),
59     sizeof AOE_M_BUS_NAME_ - sizeof (WCHAR),
60     AOE_M_BUS_NAME_
61   };
62 static UNICODE_STRING AoeBusDosname_ = {
63     sizeof AOE_M_BUS_DOSNAME_ - sizeof (WCHAR),
64     sizeof AOE_M_BUS_DOSNAME_ - sizeof (WCHAR),
65     AOE_M_BUS_DOSNAME_
66   };
67
68 static NTSTATUS STDCALL AoeBusDevCtlDetach_(IN PIRP irp) {
69     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
70     UINT32 unit_num;
71     WVL_SP_BUS_NODE walker;
72     WV_SP_DEV_T dev = NULL;
73
74     unit_num = *((PUINT32) irp->AssociatedIrp.SystemBuffer);
75     DBG("Request to detach unit: %d\n", unit_num);
76
77     walker = NULL;
78     /* For each node on the bus... */
79     WvlBusLock(&AoeBusMain);
80     while (walker = WvlBusGetNextNode(&AoeBusMain, walker)) {
81         dev = WvDevFromDevObj(WvlBusGetNodePdo(walker));
82         /* If the unit number matches... */
83         if (WvlBusGetNodeNum(walker) == unit_num) {
84             /* If it's not a boot-time device... */
85             if (dev->Boot) {
86                 DBG("Cannot detach a boot-time device.\n");
87                 /* Signal error. */
88                 dev = NULL;
89                 break;
90               }
91           }
92       }
93     WvlBusUnlock(&AoeBusMain);
94     if (!dev) {
95         DBG("Unit %d not found.\n", unit_num);
96         return WvlIrpComplete(irp, 0, STATUS_INVALID_PARAMETER);
97       }
98     /* Detach the node. */
99     WvlBusRemoveNode(&dev->BusNode);
100     DBG("Removed unit %d.\n", unit_num);
101     return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
102   }
103
104 NTSTATUS STDCALL AoeBusDevCtl(
105     IN PIRP irp,
106     IN ULONG POINTER_ALIGNMENT code
107   ) {
108     switch(code) {
109         case IOCTL_AOE_SCAN:
110           return AoeBusDevCtlScan(irp);
111
112         case IOCTL_AOE_SHOW:
113           return AoeBusDevCtlShow(irp);
114
115         case IOCTL_AOE_MOUNT:
116           return AoeBusDevCtlMount(irp);
117
118         case IOCTL_AOE_UMOUNT:
119           return AoeBusDevCtlDetach_(irp);
120
121         default:
122           DBG("Unsupported IOCTL\n");
123           return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
124       }
125   }
126
127 /* Generate dummy IDs for the AoE bus PDO. */
128 WV_M_DUMMY_ID_GEN(
129     static const,
130     AoeBusDummyIds_,
131     WVL_M_WLIT L"\\AoE",
132     L"0",
133     WVL_M_WLIT L"\\AoE\0",
134     WVL_M_WLIT L"\\AoE\0",
135     FILE_DEVICE_CONTROLLER,
136     FILE_DEVICE_SECURE_OPEN
137   );
138
139 /* Destroy the AoE bus. */
140 VOID AoeBusFree(void) {
141     IoDeleteSymbolicLink(&AoeBusDosname_);
142     if (AoeBusMain.Fdo)
143       IoDeleteDevice(AoeBusMain.Fdo);
144     WvlBusClear(&AoeBusMain);
145     return;
146   }
147
148 static UINT32 STDCALL AoeBusPnpId_(
149     IN WV_SP_DEV_T dev,
150     IN BUS_QUERY_ID_TYPE query_type,
151     IN OUT WCHAR (*buf)[512]
152   ) {
153     switch (query_type) {
154         case BusQueryDeviceID:
155           return swprintf(*buf, WVL_M_WLIT L"\\AoE") + 1;
156
157         case BusQueryInstanceID:
158           return swprintf(*buf, L"0") + 1;
159
160         case BusQueryHardwareIDs:
161           return swprintf(*buf, WVL_M_WLIT L"\\AoE") + 2;
162
163         case BusQueryCompatibleIDs:
164           return swprintf(*buf, WVL_M_WLIT L"\\AoE") + 4;
165
166         default:
167           return 0;
168       }
169   }
170
171 static NTSTATUS STDCALL AoeBusPnpQueryDevText_(
172     IN WVL_SP_BUS_T bus,
173     IN PIRP irp
174   ) {
175     WCHAR (*str)[512];
176     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
177     NTSTATUS status;
178     UINT32 str_len;
179
180     /* Allocate a string buffer. */
181     str = wv_mallocz(sizeof *str);
182     if (str == NULL) {
183         DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n");
184         status = STATUS_INSUFFICIENT_RESOURCES;
185         goto alloc_str;
186       }
187     /* Determine the query type. */
188     switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) {
189         case DeviceTextDescription:
190           str_len = swprintf(*str, L"AoE Bus") + 1;
191           irp->IoStatus.Information =
192             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
193           if (irp->IoStatus.Information == 0) {
194               DBG("wv_palloc DeviceTextDescription\n");
195               status = STATUS_INSUFFICIENT_RESOURCES;
196               goto alloc_info;
197             }
198           RtlCopyMemory(
199               (PWCHAR) irp->IoStatus.Information,
200               str,
201               str_len * sizeof (WCHAR)
202             );
203           status = STATUS_SUCCESS;
204           goto alloc_info;
205
206         case DeviceTextLocationInformation:
207           str_len = AoeBusPnpId_(
208               NULL,
209               BusQueryInstanceID,
210               str
211             );
212           irp->IoStatus.Information =
213             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
214           if (irp->IoStatus.Information == 0) {
215               DBG("wv_palloc DeviceTextLocationInformation\n");
216               status = STATUS_INSUFFICIENT_RESOURCES;
217               goto alloc_info;
218             }
219           RtlCopyMemory(
220               (PWCHAR) irp->IoStatus.Information,
221               str,
222               str_len * sizeof (WCHAR)
223             );
224           status = STATUS_SUCCESS;
225           goto alloc_info;
226
227         default:
228           irp->IoStatus.Information = 0;
229           status = STATUS_NOT_SUPPORTED;
230       }
231     /* irp->IoStatus.Information not freed. */
232     alloc_info:
233
234     wv_free(str);
235     alloc_str:
236
237     return WvlIrpComplete(irp, irp->IoStatus.Information, status);
238   }
239
240 NTSTATUS STDCALL AoeBusAttachFdo(
241     IN PDRIVER_OBJECT driver_obj,
242     IN PDEVICE_OBJECT pdo
243   ) {
244     KIRQL irql;
245     NTSTATUS status;
246     PLIST_ENTRY walker;
247
248     DBG("Entry\n");
249     /* Do we already have our main bus? */
250     if (AoeBusMain.Pdo) {
251         DBG("Already have the main bus.  Refusing.\n");
252         status = STATUS_NOT_SUPPORTED;
253         goto err_already_established;
254       }
255     /* Set associations for the bus, FDO, PDO. */
256     AoeBusMain.Pdo = pdo;
257     /* Attach the FDO to the PDO. */
258     AoeBusMain.LowerDeviceObject = IoAttachDeviceToDeviceStack(
259         AoeBusMain.Fdo,
260         pdo
261       );
262     if (AoeBusMain.LowerDeviceObject == NULL) {
263         status = STATUS_NO_SUCH_DEVICE;
264         DBG("IoAttachDeviceToDeviceStack() failed!\n");
265         goto err_attach;
266       }
267
268     /* Ok! */
269     DBG("Exit\n");
270     return STATUS_SUCCESS;
271
272     err_attach:
273
274     err_already_established:
275
276     DBG("Exit with failure\n");
277     return status;
278   }
279
280 /**
281  * Request a PDO for the AoE bus from the WinVBlock bus.
282  *
283  * @ret NTSTATUS        The status of the operation.
284  */
285 static NTSTATUS AoeBusCreatePdo_(void) {
286     return WvDummyAdd(&AoeBusDummyIds_.DummyIds);
287   }
288
289 /**
290  * Create the AoE bus PDO and FDO.
291  *
292  * @ret NTSTATUS        The status of the operation.
293  */
294 NTSTATUS AoeBusCreate(IN PDRIVER_OBJECT driver_obj) {
295     NTSTATUS status;
296     KIRQL irql;
297
298     /* Do we already have our main bus? */
299     if (AoeBusMain.Fdo) {
300         DBG("AoeBusCreate called twice.\n");
301         return STATUS_UNSUCCESSFUL;
302       }
303     /* Create the PDO for the sub-bus on the WinVBlock bus. */
304     status = AoeBusCreatePdo_();
305     if (!NT_SUCCESS(status)) {
306         DBG("Couldn't create AoE bus PDO!\n");
307         goto err_pdo;
308       }
309     /* Initialize the bus. */
310     WvlBusInit(&AoeBusMain);
311     /* Create the bus FDO. */
312     status = IoCreateDevice(
313         driver_obj,
314         0,
315         &AoeBusName_,
316         FILE_DEVICE_CONTROLLER,
317         FILE_DEVICE_SECURE_OPEN,
318         FALSE,
319         &AoeBusMain.Fdo
320       );
321     if (!NT_SUCCESS(status)) {
322         DBG("IoCreateDevice() failed!\n");
323         goto err_fdo;
324       }
325     /* DosDevice symlink. */
326     status = IoCreateSymbolicLink(
327         &AoeBusDosname_,
328         &AoeBusName_
329       );
330     if (!NT_SUCCESS(status)) {
331         DBG("IoCreateSymbolicLink() failed!\n");
332         goto err_dos_symlink;
333       }
334     /* Ok! */
335     AoeBusMain.QueryDevText = AoeBusPnpQueryDevText_;
336     AoeBusMain.Fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
337     AoeBusMain.Fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
338     AoeBusMain.Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
339     AoeBusMain.State = WvlBusStateStarted;
340     /* All done. */
341     DBG("Exit\n");
342     return status;
343
344     IoDeleteSymbolicLink(&AoeBusDosname_);
345     err_dos_symlink:
346
347     IoDeleteDevice(AoeBusMain.Fdo);
348     err_fdo:
349
350     /* Difficult to remova the PDO if we don't know what it is... */
351     err_pdo:
352
353     DBG("Exit with failure\n");
354     return status;
355   }
356
357 /**
358  * Add a child node to the AoE bus.
359  *
360  * @v Dev               Points to the child device to add.
361  * @ret                 TRUE for success, FALSE for failure.
362  */
363 BOOLEAN STDCALL AoeBusAddDev(
364     IN OUT WV_SP_DEV_T Dev
365   ) {
366     /* The new node's device object. */
367     PDEVICE_OBJECT dev_obj;
368
369     DBG("Entry\n");
370     if (!AoeBusMain.Fdo || !Dev) {
371         DBG("No bus or no device!\n");
372         return FALSE;
373       }
374     /* Create the child device. */
375     dev_obj = WvDevCreatePdo(Dev);
376     if (!dev_obj) {
377         DBG("PDO creation failed!\n");
378         return FALSE;
379       }
380     WvlBusInitNode(&Dev->BusNode, dev_obj);
381     /* Associate the parent bus. */
382     Dev->Parent = AoeBusMain.Fdo;
383     /*
384      * Initialize the device.  For disks, this routine is responsible for
385      * determining the disk's geometry appropriately for AoE disks.
386      */
387     Dev->Ops.Init(Dev);
388     dev_obj->Flags &= ~DO_DEVICE_INITIALIZING;
389     /* Add the new PDO device to the bus' list of children. */
390     WvlBusAddNode(&AoeBusMain, &Dev->BusNode);
391     Dev->DevNum = WvlBusGetNodeNum(&Dev->BusNode);
392
393     DBG("Exit\n");
394     return TRUE;
395   }