[aoe/bus] Generate a WV_S_DRIVER_DUMMY_IDS instance
[people/sha0/winvblock.git] / src / aoe / bus.c
1 /**
2  * Copyright (C) 2010, 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(void);
51 void AoeBusFree(void);
52
53 /* Globals. */
54 WV_S_BUS_T AoeBusMain = {0};
55 static UNICODE_STRING AoeBusName_ = {
56     sizeof AOE_M_BUS_NAME_,
57     sizeof AOE_M_BUS_NAME_,
58     AOE_M_BUS_NAME_
59   };
60 static UNICODE_STRING AoeBusDosname_ = {
61     sizeof AOE_M_BUS_DOSNAME_,
62     sizeof AOE_M_BUS_DOSNAME_,
63     AOE_M_BUS_DOSNAME_
64   };
65
66 static NTSTATUS STDCALL AoeBusDevCtlDetach_(IN PIRP irp) {
67     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
68     winvblock__uint32 unit_num;
69     WV_SP_BUS_NODE walker;
70
71     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
72         NTSTATUS status;
73
74         /* Enqueue the IRP. */
75         status = WvBusEnqueueIrp(&AoeBusMain, irp);
76         if (status != STATUS_PENDING)
77           /* Problem. */
78           return driver__complete_irp(irp, 0, status);
79         /* Ok. */
80         return status;
81       }
82     /* If we get here, we should be called by WvBusProcessWorkItems() */
83     unit_num = *((winvblock__uint32_ptr) irp->AssociatedIrp.SystemBuffer);
84     DBG("Request to detach unit: %d\n", unit_num);
85
86     walker = NULL;
87     /* For each node on the bus... */
88     while (walker = WvBusGetNextNode(&AoeBusMain, walker)) {
89         WV_SP_DEV_T dev = WvDevFromDevObj(WvBusGetNodePdo(walker));
90
91         /* If the unit number matches... */
92         if (WvBusGetNodeNum(walker) == unit_num) {
93             /* If it's not a boot-time device... */
94             if (dev->Boot) {
95                 DBG("Cannot detach a boot-time device.\n");
96                 /* Signal error. */
97                 walker = NULL;
98                 break;
99               }
100             /* Detach the node and free it. */
101             DBG("Removing unit %d\n", unit_num);
102             WvBusRemoveNode(walker);
103             WvDevClose(dev);
104             IoDeleteDevice(dev->Self);
105             WvDevFree(dev);
106             break;
107           }
108       }
109     if (!walker)
110       return driver__complete_irp(irp, 0, STATUS_INVALID_PARAMETER);
111     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
112   }
113
114 NTSTATUS STDCALL AoeBusDevCtl(
115     IN PIRP irp,
116     IN ULONG POINTER_ALIGNMENT code
117   ) {
118     switch(code) {
119         case IOCTL_AOE_SCAN:
120           return AoeBusDevCtlScan(irp);
121
122         case IOCTL_AOE_SHOW:
123           return AoeBusDevCtlShow(irp);
124
125         case IOCTL_AOE_MOUNT:
126           return AoeBusDevCtlMount(irp);
127
128         case IOCTL_AOE_UMOUNT:
129           return AoeBusDevCtlDetach_(irp);
130
131         default:
132           DBG("Unsupported IOCTL\n");
133           return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
134       }
135   }
136
137 /**
138  * Create the AoE bus.
139  *
140  * @ret         TRUE for success, else FALSE.
141  */
142 winvblock__bool AoeBusCreate(void) {
143     NTSTATUS status;
144
145     /* Create the PDO for the sub-bus on the WinVBlock bus. */
146     status = WvDriverAddDummy(
147         AoeBusPnpId_,
148         FILE_DEVICE_CONTROLLER,
149         FILE_DEVICE_SECURE_OPEN
150       );
151     if (!NT_SUCCESS(status)) {
152         DBG("Couldn't add AoE bus to WinVBlock bus!\n");
153         return FALSE;
154       }
155     /* All done. */
156     return TRUE;
157   }
158
159 /* Destroy the AoE bus. */
160 void AoeBusFree(void) {
161     IoDeleteSymbolicLink(&AoeBusDosname_);
162     if (AoeBusMain.Fdo)
163       IoDeleteDevice(AoeBusMain.Fdo);
164     return;
165   }
166
167 /* Generate dummy IDs for the AoE bus PDO. */
168 #define AOE_M_BUS_IDS(X_, Y_)                       \
169   X_(Y_, Dev,      winvblock__literal_w L"\\AoE"  ) \
170   X_(Y_, Instance, L"0"                           ) \
171   X_(Y_, Hardware, winvblock__literal_w L"\\AoE\0") \
172   X_(Y_, Compat,   winvblock__literal_w L"\\AoE\0")
173 WV_M_DRIVER_DUMMY_ID_GEN(AoeBusDummyIds_, AOE_M_BUS_IDS);
174
175 static winvblock__uint32 STDCALL AoeBusPnpId_(
176     IN WV_SP_DEV_T dev,
177     IN BUS_QUERY_ID_TYPE query_type,
178     IN OUT WCHAR (*buf)[512]
179   ) {
180     switch (query_type) {
181         case BusQueryDeviceID:
182           return swprintf(*buf, winvblock__literal_w L"\\AoE") + 1;
183
184         case BusQueryInstanceID:
185           return swprintf(*buf, L"0") + 1;
186
187         case BusQueryHardwareIDs:
188           return swprintf(*buf, winvblock__literal_w L"\\AoE") + 2;
189
190         case BusQueryCompatibleIDs:
191           return swprintf(*buf, winvblock__literal_w L"\\AoE") + 4;
192
193         default:
194           return 0;
195       }
196   }
197
198 static NTSTATUS STDCALL AoeBusPnpQueryDevText_(
199     IN WV_SP_BUS_T bus,
200     IN PIRP irp
201   ) {
202     WCHAR (*str)[512];
203     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
204     NTSTATUS status;
205     winvblock__uint32 str_len;
206
207     /* Allocate a string buffer. */
208     str = wv_mallocz(sizeof *str);
209     if (str == NULL) {
210         DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n");
211         status = STATUS_INSUFFICIENT_RESOURCES;
212         goto alloc_str;
213       }
214     /* Determine the query type. */
215     switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) {
216         case DeviceTextDescription:
217           str_len = swprintf(*str, L"AoE Bus") + 1;
218           irp->IoStatus.Information =
219             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
220           if (irp->IoStatus.Information == 0) {
221               DBG("wv_palloc DeviceTextDescription\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         case DeviceTextLocationInformation:
234           str_len = AoeBusPnpId_(
235               NULL,
236               BusQueryInstanceID,
237               str
238             );
239           irp->IoStatus.Information =
240             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
241           if (irp->IoStatus.Information == 0) {
242               DBG("wv_palloc DeviceTextLocationInformation\n");
243               status = STATUS_INSUFFICIENT_RESOURCES;
244               goto alloc_info;
245             }
246           RtlCopyMemory(
247               (PWCHAR) irp->IoStatus.Information,
248               str,
249               str_len * sizeof (WCHAR)
250             );
251           status = STATUS_SUCCESS;
252           goto alloc_info;
253
254         default:
255           irp->IoStatus.Information = 0;
256           status = STATUS_NOT_SUPPORTED;
257       }
258     /* irp->IoStatus.Information not freed. */
259     alloc_info:
260
261     wv_free(str);
262     alloc_str:
263
264     return driver__complete_irp(irp, irp->IoStatus.Information, status);
265   }
266
267 NTSTATUS STDCALL AoeBusAttachFdo(
268     IN PDRIVER_OBJECT driver_obj,
269     IN PDEVICE_OBJECT pdo
270   ) {
271     KIRQL irql;
272     NTSTATUS status;
273     PLIST_ENTRY walker;
274     PDEVICE_OBJECT fdo = NULL;
275
276     DBG("Entry\n");
277     /* Do we already have our main bus? */
278     if (AoeBusMain.Fdo) {
279         DBG("Already have the main bus.  Refusing.\n");
280         status = STATUS_NOT_SUPPORTED;
281         goto err_already_established;
282       }
283     /* Initialize the bus. */
284     WvBusInit(&AoeBusMain);
285     /* Create the bus FDO. */
286     status = IoCreateDevice(
287         driver_obj,
288         0,
289         &AoeBusName_,
290         FILE_DEVICE_CONTROLLER,
291         FILE_DEVICE_SECURE_OPEN,
292         FALSE,
293         &fdo
294       );
295     if (!NT_SUCCESS(status)) {
296         DBG("IoCreateDevice() failed!\n");
297         goto err_fdo;
298       }
299     /* DosDevice symlink. */
300     status = IoCreateSymbolicLink(
301         &AoeBusDosname_,
302         &AoeBusName_
303       );
304     if (!NT_SUCCESS(status)) {
305         DBG("IoCreateSymbolicLink() failed!\n");
306         goto err_dos_symlink;
307       }
308     /* Set associations for the bus, FDO, PDO. */
309     AoeBusMain.Fdo = fdo;
310     AoeBusMain.QueryDevText = AoeBusPnpQueryDevText_;
311     AoeBusMain.PhysicalDeviceObject = pdo;
312     fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
313     fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
314     /* Attach the FDO to the PDO. */
315     AoeBusMain.LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);
316     if (AoeBusMain.LowerDeviceObject == NULL) {
317         status = STATUS_NO_SUCH_DEVICE;
318         DBG("IoAttachDeviceToDeviceStack() failed!\n");
319         goto err_attach;
320       }
321     /* Ok! */
322     fdo->Flags &= ~DO_DEVICE_INITIALIZING;
323     #ifdef RIS
324     AoeBusMain.State = WvBusStateStarted;
325     #endif
326     DBG("Exit\n");
327     return STATUS_SUCCESS;
328
329     err_attach:
330
331     IoDeleteSymbolicLink(&AoeBusDosname_);
332     err_dos_symlink:
333
334     IoDeleteDevice(fdo);
335     err_fdo:
336
337     err_already_established:
338
339     DBG("Exit with failure\n");
340     return status;
341   }