[project] Rename winvblock__uint32 back to UINT32
[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 "winvblock.h"
30 #include "wv_stdlib.h"
31 #include "portable.h"
32 #include "driver.h"
33 #include "bus.h"
34 #include "device.h"
35 #include "aoe.h"
36 #include "mount.h"
37 #include "debug.h"
38
39 /* Names for the AoE bus. */
40 #define AOE_M_BUS_NAME_ (L"\\Device\\AoE")
41 #define AOE_M_BUS_DOSNAME_ (L"\\DosDevices\\AoE")
42
43 /* TODO: Remove this pull from aoe/driver.c */
44 extern NTSTATUS STDCALL AoeBusDevCtlScan(IN PIRP);
45 extern NTSTATUS STDCALL AoeBusDevCtlShow(IN PIRP);
46 extern NTSTATUS STDCALL AoeBusDevCtlMount(IN PIRP);
47
48 /* Forward declarations. */
49 static WV_F_DEV_PNP_ID AoeBusPnpId_;
50 winvblock__bool AoeBusCreate(IN PDRIVER_OBJECT);
51 void AoeBusFree(void);
52
53 /* Globals. */
54 WVL_S_BUS_T AoeBusMain = {0};
55 static UNICODE_STRING AoeBusName_ = {
56     sizeof AOE_M_BUS_NAME_ - sizeof (WCHAR),
57     sizeof AOE_M_BUS_NAME_ - sizeof (WCHAR),
58     AOE_M_BUS_NAME_
59   };
60 static UNICODE_STRING AoeBusDosname_ = {
61     sizeof AOE_M_BUS_DOSNAME_ - sizeof (WCHAR),
62     sizeof AOE_M_BUS_DOSNAME_ - sizeof (WCHAR),
63     AOE_M_BUS_DOSNAME_
64   };
65 const WV_S_DRIVER_DUMMY_IDS * AoeBusDummyIds;
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 = WvBusEnqueueIrp(&AoeBusMain, irp);
77         if (status != STATUS_PENDING)
78           /* Problem. */
79           return driver__complete_irp(irp, 0, status);
80         /* Ok. */
81         return status;
82       }
83     /* If we get here, we should be called by WvBusProcessWorkItems() */
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 = WvBusGetNextNode(&AoeBusMain, walker)) {
90         WV_SP_DEV_T dev = WvDevFromDevObj(WvBusGetNodePdo(walker));
91
92         /* If the unit number matches... */
93         if (WvBusGetNodeNum(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             WvBusRemoveNode(walker);
104             WvDevClose(dev);
105             IoDeleteDevice(dev->Self);
106             WvDevFree(dev);
107             break;
108           }
109       }
110     if (!walker)
111       return driver__complete_irp(irp, 0, STATUS_INVALID_PARAMETER);
112     return driver__complete_irp(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 driver__complete_irp(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_DRIVER_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 driver__complete_irp(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.
289  *
290  * @ret         TRUE for success, else FALSE.
291  */
292 winvblock__bool AoeBusCreate(IN PDRIVER_OBJECT driver_obj) {
293     NTSTATUS status;
294
295     /* Do we already have our main bus? */
296     if (AoeBusMain.Fdo) {
297         DBG("AoeBusCreate called twice.\n");
298         return FALSE;
299       }
300     /* Initialize the bus. */
301     WvBusInit(&AoeBusMain);
302     /* Create the bus FDO. */
303     status = IoCreateDevice(
304         driver_obj,
305         0,
306         &AoeBusName_,
307         FILE_DEVICE_CONTROLLER,
308         FILE_DEVICE_SECURE_OPEN,
309         FALSE,
310         &AoeBusMain.Fdo
311       );
312     if (!NT_SUCCESS(status)) {
313         DBG("IoCreateDevice() failed!\n");
314         goto err_fdo;
315       }
316     /* DosDevice symlink. */
317     status = IoCreateSymbolicLink(
318         &AoeBusDosname_,
319         &AoeBusName_
320       );
321     if (!NT_SUCCESS(status)) {
322         DBG("IoCreateSymbolicLink() failed!\n");
323         goto err_dos_symlink;
324       }
325     /* Ok! */
326     AoeBusDummyIds = &AoeBusDummyIds_;
327     AoeBusMain.QueryDevText = AoeBusPnpQueryDevText_;
328     AoeBusMain.Fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
329     AoeBusMain.Fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
330     AoeBusMain.Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
331     #ifdef RIS
332     AoeBusMain.State = WvlBusStateStarted;
333     #endif
334     /* All done. */
335     DBG("Exit\n");
336     return TRUE;
337
338     IoDeleteSymbolicLink(&AoeBusDosname_);
339     err_dos_symlink:
340
341     IoDeleteDevice(AoeBusMain.Fdo);
342     err_fdo:
343
344     DBG("Exit with failure\n");
345     return FALSE;
346   }
347
348 /**
349  * Add a child node to the AoE bus.
350  *
351  * @v Dev               Points to the child device to add.
352  * @ret                 TRUE for success, FALSE for failure.
353  */
354 winvblock__bool STDCALL AoeBusAddDev(
355     IN OUT WV_SP_DEV_T Dev
356   ) {
357     /* The new node's device object. */
358     PDEVICE_OBJECT dev_obj;
359
360     DBG("Entry\n");
361     if (!AoeBusMain.Fdo || !Dev) {
362         DBG("No bus or no device!\n");
363         return FALSE;
364       }
365     /* Create the child device. */
366     dev_obj = WvDevCreatePdo(Dev);
367     if (!dev_obj) {
368         DBG("PDO creation failed!\n");
369         return FALSE;
370       }
371     WvBusInitNode(&Dev->BusNode, dev_obj);
372     /* Associate the parent bus. */
373     Dev->Parent = AoeBusMain.Fdo;
374     /*
375      * Initialize the device.  For disks, this routine is responsible for
376      * determining the disk's geometry appropriately for AoE disks.
377      */
378     Dev->Ops.Init(Dev);
379     dev_obj->Flags &= ~DO_DEVICE_INITIALIZING;
380     /* Add the new PDO device to the bus' list of children. */
381     WvBusAddNode(&AoeBusMain, &Dev->BusNode);
382     Dev->DevNum = WvBusGetNodeNum(&Dev->BusNode);
383
384     DBG("Exit\n");
385     return TRUE;
386   }