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