6ffa06f0d4aa19f3f4c8fd705600b76fb8aec30f
[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 static winvblock__uint32 STDCALL AoeBusPnpId_(
168     IN WV_SP_DEV_T dev,
169     IN BUS_QUERY_ID_TYPE query_type,
170     IN OUT WCHAR (*buf)[512]
171   ) {
172     switch (query_type) {
173         case BusQueryDeviceID:
174           return swprintf(*buf, winvblock__literal_w L"\\AoE") + 1;
175
176         case BusQueryInstanceID:
177           return swprintf(*buf, L"0") + 1;
178
179         case BusQueryHardwareIDs:
180           return swprintf(*buf, winvblock__literal_w L"\\AoE") + 2;
181
182         case BusQueryCompatibleIDs:
183           return swprintf(*buf, winvblock__literal_w L"\\AoE") + 4;
184
185         default:
186           return 0;
187       }
188   }
189
190 static NTSTATUS STDCALL AoeBusPnpQueryDevText_(
191     IN WV_SP_BUS_T bus,
192     IN PIRP irp
193   ) {
194     WCHAR (*str)[512];
195     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
196     NTSTATUS status;
197     winvblock__uint32 str_len;
198
199     /* Allocate a string buffer. */
200     str = wv_mallocz(sizeof *str);
201     if (str == NULL) {
202         DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n");
203         status = STATUS_INSUFFICIENT_RESOURCES;
204         goto alloc_str;
205       }
206     /* Determine the query type. */
207     switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) {
208         case DeviceTextDescription:
209           str_len = swprintf(*str, L"AoE Bus") + 1;
210           irp->IoStatus.Information =
211             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
212           if (irp->IoStatus.Information == 0) {
213               DBG("wv_palloc DeviceTextDescription\n");
214               status = STATUS_INSUFFICIENT_RESOURCES;
215               goto alloc_info;
216             }
217           RtlCopyMemory(
218               (PWCHAR) irp->IoStatus.Information,
219               str,
220               str_len * sizeof (WCHAR)
221             );
222           status = STATUS_SUCCESS;
223           goto alloc_info;
224
225         case DeviceTextLocationInformation:
226           str_len = AoeBusPnpId_(
227               NULL,
228               BusQueryInstanceID,
229               str
230             );
231           irp->IoStatus.Information =
232             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
233           if (irp->IoStatus.Information == 0) {
234               DBG("wv_palloc DeviceTextLocationInformation\n");
235               status = STATUS_INSUFFICIENT_RESOURCES;
236               goto alloc_info;
237             }
238           RtlCopyMemory(
239               (PWCHAR) irp->IoStatus.Information,
240               str,
241               str_len * sizeof (WCHAR)
242             );
243           status = STATUS_SUCCESS;
244           goto alloc_info;
245
246         default:
247           irp->IoStatus.Information = 0;
248           status = STATUS_NOT_SUPPORTED;
249       }
250     /* irp->IoStatus.Information not freed. */
251     alloc_info:
252
253     wv_free(str);
254     alloc_str:
255
256     return driver__complete_irp(irp, irp->IoStatus.Information, status);
257   }
258
259 NTSTATUS STDCALL AoeBusAttachFdo(
260     IN PDRIVER_OBJECT driver_obj,
261     IN PDEVICE_OBJECT pdo
262   ) {
263     KIRQL irql;
264     NTSTATUS status;
265     PLIST_ENTRY walker;
266     PDEVICE_OBJECT fdo = NULL;
267
268     DBG("Entry\n");
269     /* Do we already have our main bus? */
270     if (AoeBusMain.Fdo) {
271         DBG("Already have the main bus.  Refusing.\n");
272         status = STATUS_NOT_SUPPORTED;
273         goto err_already_established;
274       }
275     /* Initialize the bus. */
276     WvBusInit(&AoeBusMain);
277     /* Create the bus FDO. */
278     status = IoCreateDevice(
279         driver_obj,
280         0,
281         &AoeBusName_,
282         FILE_DEVICE_CONTROLLER,
283         FILE_DEVICE_SECURE_OPEN,
284         FALSE,
285         &fdo
286       );
287     if (!NT_SUCCESS(status)) {
288         DBG("IoCreateDevice() failed!\n");
289         goto err_fdo;
290       }
291     /* DosDevice symlink. */
292     status = IoCreateSymbolicLink(
293         &AoeBusDosname_,
294         &AoeBusName_
295       );
296     if (!NT_SUCCESS(status)) {
297         DBG("IoCreateSymbolicLink() failed!\n");
298         goto err_dos_symlink;
299       }
300     /* Set associations for the bus, FDO, PDO. */
301     AoeBusMain.Fdo = fdo;
302     AoeBusMain.QueryDevText = AoeBusPnpQueryDevText_;
303     AoeBusMain.PhysicalDeviceObject = pdo;
304     fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
305     fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
306     /* Attach the FDO to the PDO. */
307     AoeBusMain.LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);
308     if (AoeBusMain.LowerDeviceObject == NULL) {
309         status = STATUS_NO_SUCH_DEVICE;
310         DBG("IoAttachDeviceToDeviceStack() failed!\n");
311         goto err_attach;
312       }
313     /* Ok! */
314     fdo->Flags &= ~DO_DEVICE_INITIALIZING;
315     #ifdef RIS
316     AoeBusMain.State = WvBusStateStarted;
317     #endif
318     DBG("Exit\n");
319     return STATUS_SUCCESS;
320
321     err_attach:
322
323     IoDeleteSymbolicLink(&AoeBusDosname_);
324     err_dos_symlink:
325
326     IoDeleteDevice(fdo);
327     err_fdo:
328
329     err_already_established:
330
331     DBG("Exit with failure\n");
332     return status;
333   }