[driver,bus] Move WvBus* subjects into new bus.c
[people/sha0/winvblock.git] / src / winvblock / bus.c
1 /**
2  * Copyright (C) 2009-2011, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * WinVBlock driver bus specifics.
26  */
27
28 #include <stdio.h>
29 #include <ntddk.h>
30 #include <scsi.h>
31
32 #include "portable.h"
33 #include "winvblock.h"
34 #include "wv_stdlib.h"
35 #include "wv_string.h"
36 #include "irp.h"
37 #include "driver.h"
38 #include "bus.h"
39 #include "device.h"
40 #include "disk.h"
41 #include "registry.h"
42 #include "mount.h"
43 #include "filedisk.h"
44 #include "ramdisk.h"
45 #include "debug.h"
46
47 /* Names for the main bus. */
48 #define WV_M_BUS_NAME (L"\\Device\\" WVL_M_WLIT)
49 #define WV_M_BUS_DOSNAME (L"\\DosDevices\\" WVL_M_WLIT)
50
51 /* Globals. */
52 UNICODE_STRING WvBusName = {
53     sizeof WV_M_BUS_NAME - sizeof (WCHAR),
54     sizeof WV_M_BUS_NAME - sizeof (WCHAR),
55     WV_M_BUS_NAME
56   };
57 UNICODE_STRING WvBusDosname = {
58     sizeof WV_M_BUS_DOSNAME - sizeof (WCHAR),
59     sizeof WV_M_BUS_DOSNAME - sizeof (WCHAR),
60     WV_M_BUS_DOSNAME
61   };
62 /* The main bus. */
63 WVL_S_BUS_T WvBus = {0};
64 WV_S_DEV_T WvBusDev = {0};
65 PETHREAD WvBusThread = NULL;
66
67 /* Forward declarations. */
68 WV_F_DEV_DISPATCH WvBusSysCtl;
69 WV_F_DEV_CTL WvBusDevCtl;
70 WV_F_DEV_DISPATCH WvBusPower;
71 WV_F_DEV_PNP WvBusPnp;
72 WVL_F_BUS_PNP WvBusPnpQueryDevText;
73
74 /* Establish the bus PDO. */
75 NTSTATUS STDCALL WvBusEstablish(IN PUNICODE_STRING RegistryPath) {
76     NTSTATUS status;
77     HANDLE reg_key;
78     UINT32 pdo_done = 0;
79     PDEVICE_OBJECT bus_pdo = NULL;
80
81     /* Open our Registry path. */
82     status = WvlRegOpenKey(RegistryPath->Buffer, &reg_key);
83     if (!NT_SUCCESS(status))
84       return status;
85
86     /* Check the Registry to see if we've already got a PDO. */
87     status = WvlRegFetchDword(reg_key, L"PdoDone", &pdo_done);
88     if (NT_SUCCESS(status) && pdo_done) {
89         WvlRegCloseKey(reg_key);
90         return status;
91       }
92
93     /* Create a root-enumerated PDO for our bus. */
94     IoReportDetectedDevice(
95         WvDriverObj,
96         InterfaceTypeUndefined,
97         -1,
98         -1,
99         NULL,
100         NULL,
101         FALSE,
102         &bus_pdo
103       );
104     if (bus_pdo == NULL) {
105         DBG("IoReportDetectedDevice() went wrong!  Exiting.\n");
106         status = STATUS_UNSUCCESSFUL;
107         goto err_driver_bus;
108       }
109
110     /* Remember that we have a PDO for next time. */
111     status = WvlRegStoreDword(reg_key, L"PdoDone", 1);
112     if (!NT_SUCCESS(status)) {
113         DBG("Couldn't save PdoDone to Registry.  Oh well.\n");
114       }
115     /* Attach FDO to PDO. */
116     status = WvDriverObj->DriverExtension->AddDevice(WvDriverObj, bus_pdo);
117     if (!NT_SUCCESS(status)) {
118         DBG("WvAttachFdo() went wrong!\n");
119         goto err_add_dev;
120       }
121     /* PDO created, FDO attached.  All done. */
122     return STATUS_SUCCESS;
123
124     err_add_dev:
125
126     IoDeleteDevice(bus_pdo);
127     err_driver_bus:
128
129     return status;
130   }
131
132 /* Pass an IRP_MJ_SYSTEM_CONTROL IRP to the bus. */
133 NTSTATUS STDCALL WvBusSysCtl(IN WV_SP_DEV_T dev, IN PIRP irp) {
134     return WvlBusSysCtl(&WvBus, irp);
135   }
136
137 /* Pass a power IRP to the bus. */
138 NTSTATUS STDCALL WvBusPower(IN WV_SP_DEV_T dev, IN PIRP irp) {
139     return WvlBusPower(&WvBus, irp);
140   }
141
142 /* Pass an IRP_MJ_PNP to the bus. */
143 NTSTATUS STDCALL WvBusPnp(
144     IN WV_SP_DEV_T dev,
145     IN PIRP irp,
146     IN UCHAR code
147   ) {
148     return WvlBusPnpIrp(&WvBus, irp, code);
149   }
150     
151 /**
152  * Add a child node to the bus.
153  *
154  * @v Dev               Points to the child device to add.
155  * @ret                 TRUE for success, FALSE for failure.
156  */
157 BOOLEAN STDCALL WvBusAddDev(
158     IN OUT WV_SP_DEV_T Dev
159   ) {
160     /* The new node's device object. */
161     PDEVICE_OBJECT dev_obj;
162
163     DBG("Entry\n");
164     if (!WvBus.Fdo || !Dev) {
165         DBG("No bus or no device!\n");
166         return FALSE;
167       }
168     /* Create the child device. */
169     dev_obj = WvDevCreatePdo(Dev);
170     if (!dev_obj) {
171         DBG("PDO creation failed!\n");
172         return FALSE;
173       }
174     WvlBusInitNode(&Dev->BusNode, dev_obj);
175     /* Associate the parent bus. */
176     Dev->Parent = WvBus.Fdo;
177     /*
178      * Initialize the device.  For disks, this routine is responsible for
179      * determining the disk's geometry appropriately for AoE/RAM/file disks.
180      */
181     Dev->Ops.Init(Dev);
182     dev_obj->Flags &= ~DO_DEVICE_INITIALIZING;
183     /* Add the new PDO device to the bus' list of children. */
184     WvlBusAddNode(&WvBus, &Dev->BusNode);
185     Dev->DevNum = WvlBusGetNodeNum(&Dev->BusNode);
186
187     DBG("Exit\n");
188     return TRUE;
189   }
190
191 static NTSTATUS STDCALL WvBusDevCtlDetach(
192     IN PIRP irp
193   ) {
194     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
195     UINT32 unit_num;
196     WVL_SP_BUS_NODE walker;
197
198     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
199         NTSTATUS status;
200
201         /* Enqueue the IRP. */
202         status = WvlBusEnqueueIrp(&WvBus, irp);
203         if (status != STATUS_PENDING)
204           /* Problem. */
205           return WvlIrpComplete(irp, 0, status);
206         /* Ok. */
207         return status;
208       }
209     /* If we get here, we should be called by WvlBusProcessWorkItems() */
210     unit_num = *((PUINT32) irp->AssociatedIrp.SystemBuffer);
211     DBG("Request to detach unit: %d\n", unit_num);
212
213     walker = NULL;
214     /* For each node on the bus... */
215     while (walker = WvlBusGetNextNode(&WvBus, walker)) {
216         WV_SP_DEV_T dev = WvDevFromDevObj(WvlBusGetNodePdo(walker));
217
218         /* If the unit number matches... */
219         if (WvlBusGetNodeNum(walker) == unit_num) {
220             /* If it's not a boot-time device... */
221             if (dev->Boot) {
222                 DBG("Cannot detach a boot-time device.\n");
223                 /* Signal error. */
224                 walker = NULL;
225                 break;
226               }
227             /* Detach the node and free it. */
228             DBG("Removing unit %d\n", unit_num);
229             WvlBusRemoveNode(walker);
230             WvDevClose(dev);
231             IoDeleteDevice(dev->Self);
232             WvDevFree(dev);
233             break;
234           }
235       }
236     if (!walker)
237       return WvlIrpComplete(irp, 0, STATUS_INVALID_PARAMETER);
238     return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
239   }
240
241 NTSTATUS STDCALL WvBusDevCtl(
242     IN WV_SP_DEV_T dev,
243     IN PIRP irp,
244     IN ULONG POINTER_ALIGNMENT code
245   ) {
246     NTSTATUS status;
247
248     switch (code) {
249         case IOCTL_FILE_ATTACH:
250           status = filedisk__attach(dev, irp);
251           break;
252
253         case IOCTL_FILE_DETACH:
254           return WvBusDevCtlDetach(irp);
255
256         default:
257           irp->IoStatus.Information = 0;
258           status = STATUS_INVALID_DEVICE_REQUEST;
259       }
260
261     irp->IoStatus.Status = status;
262     IoCompleteRequest(irp, IO_NO_INCREMENT);
263     return status;
264   }
265
266 NTSTATUS STDCALL WvBusPnpQueryDevText(
267     IN WVL_SP_BUS_T bus,
268     IN PIRP irp
269   ) {
270     WCHAR (*str)[512];
271     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
272     NTSTATUS status;
273     UINT32 str_len;
274
275     /* Allocate a string buffer. */
276     str = wv_mallocz(sizeof *str);
277     if (str == NULL) {
278         DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n");
279         status = STATUS_INSUFFICIENT_RESOURCES;
280         goto alloc_str;
281       }
282     /* Determine the query type. */
283     switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) {
284         case DeviceTextDescription:
285           str_len = swprintf(*str, WVL_M_WLIT L" Bus") + 1;
286           irp->IoStatus.Information =
287             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
288           if (irp->IoStatus.Information == 0) {
289               DBG("wv_palloc DeviceTextDescription\n");
290               status = STATUS_INSUFFICIENT_RESOURCES;
291               goto alloc_info;
292             }
293           RtlCopyMemory(
294               (PWCHAR) irp->IoStatus.Information,
295               str,
296               str_len * sizeof (WCHAR)
297             );
298           status = STATUS_SUCCESS;
299           goto alloc_info;
300
301         case DeviceTextLocationInformation:
302           str_len = WvDevPnpId(
303               &WvBusDev,
304               BusQueryInstanceID,
305               str
306             );
307           irp->IoStatus.Information =
308             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
309           if (irp->IoStatus.Information == 0) {
310               DBG("wv_palloc DeviceTextLocationInformation\n");
311               status = STATUS_INSUFFICIENT_RESOURCES;
312               goto alloc_info;
313             }
314           RtlCopyMemory(
315               (PWCHAR) irp->IoStatus.Information,
316               str,
317               str_len * sizeof (WCHAR)
318             );
319           status = STATUS_SUCCESS;
320           goto alloc_info;
321
322         default:
323           irp->IoStatus.Information = 0;
324           status = STATUS_NOT_SUPPORTED;
325       }
326     /* irp->IoStatus.Information not freed. */
327     alloc_info:
328
329     wv_free(str);
330     alloc_str:
331
332     return WvlIrpComplete(irp, irp->IoStatus.Information, status);
333   }