[driver,bus] Remove WvBusPower
[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_PNP WvBusPnp;
71 WVL_F_BUS_PNP WvBusPnpQueryDevText;
72
73 /* Establish the bus PDO. */
74 NTSTATUS STDCALL WvBusEstablish(IN PUNICODE_STRING RegistryPath) {
75     NTSTATUS status;
76     HANDLE reg_key;
77     UINT32 pdo_done = 0;
78     PDEVICE_OBJECT bus_pdo = NULL;
79
80     /* Open our Registry path. */
81     status = WvlRegOpenKey(RegistryPath->Buffer, &reg_key);
82     if (!NT_SUCCESS(status))
83       return status;
84
85     /* Check the Registry to see if we've already got a PDO. */
86     status = WvlRegFetchDword(reg_key, L"PdoDone", &pdo_done);
87     if (NT_SUCCESS(status) && pdo_done) {
88         WvlRegCloseKey(reg_key);
89         return status;
90       }
91
92     /* Create a root-enumerated PDO for our bus. */
93     IoReportDetectedDevice(
94         WvDriverObj,
95         InterfaceTypeUndefined,
96         -1,
97         -1,
98         NULL,
99         NULL,
100         FALSE,
101         &bus_pdo
102       );
103     if (bus_pdo == NULL) {
104         DBG("IoReportDetectedDevice() went wrong!  Exiting.\n");
105         status = STATUS_UNSUCCESSFUL;
106         goto err_driver_bus;
107       }
108
109     /* Remember that we have a PDO for next time. */
110     status = WvlRegStoreDword(reg_key, L"PdoDone", 1);
111     if (!NT_SUCCESS(status)) {
112         DBG("Couldn't save PdoDone to Registry.  Oh well.\n");
113       }
114     /* Attach FDO to PDO. */
115     status = WvDriverObj->DriverExtension->AddDevice(WvDriverObj, bus_pdo);
116     if (!NT_SUCCESS(status)) {
117         DBG("WvAttachFdo() went wrong!\n");
118         goto err_add_dev;
119       }
120     /* PDO created, FDO attached.  All done. */
121     return STATUS_SUCCESS;
122
123     err_add_dev:
124
125     IoDeleteDevice(bus_pdo);
126     err_driver_bus:
127
128     return status;
129   }
130
131 /* Pass an IRP_MJ_SYSTEM_CONTROL IRP to the bus. */
132 NTSTATUS STDCALL WvBusSysCtl(IN WV_SP_DEV_T dev, IN PIRP irp) {
133     return WvlBusSysCtl(&WvBus, irp);
134   }
135
136 /* Pass an IRP_MJ_PNP to the bus. */
137 NTSTATUS STDCALL WvBusPnp(
138     IN WV_SP_DEV_T dev,
139     IN PIRP irp,
140     IN UCHAR code
141   ) {
142     return WvlBusPnpIrp(&WvBus, irp, code);
143   }
144     
145 /**
146  * Add a child node to the bus.
147  *
148  * @v Dev               Points to the child device to add.
149  * @ret                 TRUE for success, FALSE for failure.
150  */
151 BOOLEAN STDCALL WvBusAddDev(
152     IN OUT WV_SP_DEV_T Dev
153   ) {
154     /* The new node's device object. */
155     PDEVICE_OBJECT dev_obj;
156
157     DBG("Entry\n");
158     if (!WvBus.Fdo || !Dev) {
159         DBG("No bus or no device!\n");
160         return FALSE;
161       }
162     /* Create the child device. */
163     dev_obj = WvDevCreatePdo(Dev);
164     if (!dev_obj) {
165         DBG("PDO creation failed!\n");
166         return FALSE;
167       }
168     WvlBusInitNode(&Dev->BusNode, dev_obj);
169     /* Associate the parent bus. */
170     Dev->Parent = WvBus.Fdo;
171     /*
172      * Initialize the device.  For disks, this routine is responsible for
173      * determining the disk's geometry appropriately for AoE/RAM/file disks.
174      */
175     Dev->Ops.Init(Dev);
176     dev_obj->Flags &= ~DO_DEVICE_INITIALIZING;
177     /* Add the new PDO device to the bus' list of children. */
178     WvlBusAddNode(&WvBus, &Dev->BusNode);
179     Dev->DevNum = WvlBusGetNodeNum(&Dev->BusNode);
180
181     DBG("Exit\n");
182     return TRUE;
183   }
184
185 static NTSTATUS STDCALL WvBusDevCtlDetach(
186     IN PIRP irp
187   ) {
188     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
189     UINT32 unit_num;
190     WVL_SP_BUS_NODE walker;
191
192     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
193         NTSTATUS status;
194
195         /* Enqueue the IRP. */
196         status = WvlBusEnqueueIrp(&WvBus, irp);
197         if (status != STATUS_PENDING)
198           /* Problem. */
199           return WvlIrpComplete(irp, 0, status);
200         /* Ok. */
201         return status;
202       }
203     /* If we get here, we should be called by WvlBusProcessWorkItems() */
204     unit_num = *((PUINT32) irp->AssociatedIrp.SystemBuffer);
205     DBG("Request to detach unit: %d\n", unit_num);
206
207     walker = NULL;
208     /* For each node on the bus... */
209     while (walker = WvlBusGetNextNode(&WvBus, walker)) {
210         WV_SP_DEV_T dev = WvDevFromDevObj(WvlBusGetNodePdo(walker));
211
212         /* If the unit number matches... */
213         if (WvlBusGetNodeNum(walker) == unit_num) {
214             /* If it's not a boot-time device... */
215             if (dev->Boot) {
216                 DBG("Cannot detach a boot-time device.\n");
217                 /* Signal error. */
218                 walker = NULL;
219                 break;
220               }
221             /* Detach the node and free it. */
222             DBG("Removing unit %d\n", unit_num);
223             WvlBusRemoveNode(walker);
224             WvDevClose(dev);
225             IoDeleteDevice(dev->Self);
226             WvDevFree(dev);
227             break;
228           }
229       }
230     if (!walker)
231       return WvlIrpComplete(irp, 0, STATUS_INVALID_PARAMETER);
232     return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
233   }
234
235 NTSTATUS STDCALL WvBusDevCtl(
236     IN WV_SP_DEV_T dev,
237     IN PIRP irp,
238     IN ULONG POINTER_ALIGNMENT code
239   ) {
240     NTSTATUS status;
241
242     switch (code) {
243         case IOCTL_FILE_ATTACH:
244           status = filedisk__attach(dev, irp);
245           break;
246
247         case IOCTL_FILE_DETACH:
248           return WvBusDevCtlDetach(irp);
249
250         default:
251           irp->IoStatus.Information = 0;
252           status = STATUS_INVALID_DEVICE_REQUEST;
253       }
254
255     irp->IoStatus.Status = status;
256     IoCompleteRequest(irp, IO_NO_INCREMENT);
257     return status;
258   }
259
260 NTSTATUS STDCALL WvBusPnpQueryDevText(
261     IN WVL_SP_BUS_T bus,
262     IN PIRP irp
263   ) {
264     WCHAR (*str)[512];
265     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
266     NTSTATUS status;
267     UINT32 str_len;
268
269     /* Allocate a string buffer. */
270     str = wv_mallocz(sizeof *str);
271     if (str == NULL) {
272         DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n");
273         status = STATUS_INSUFFICIENT_RESOURCES;
274         goto alloc_str;
275       }
276     /* Determine the query type. */
277     switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) {
278         case DeviceTextDescription:
279           str_len = swprintf(*str, WVL_M_WLIT L" Bus") + 1;
280           irp->IoStatus.Information =
281             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
282           if (irp->IoStatus.Information == 0) {
283               DBG("wv_palloc DeviceTextDescription\n");
284               status = STATUS_INSUFFICIENT_RESOURCES;
285               goto alloc_info;
286             }
287           RtlCopyMemory(
288               (PWCHAR) irp->IoStatus.Information,
289               str,
290               str_len * sizeof (WCHAR)
291             );
292           status = STATUS_SUCCESS;
293           goto alloc_info;
294
295         case DeviceTextLocationInformation:
296           str_len = WvDevPnpId(
297               &WvBusDev,
298               BusQueryInstanceID,
299               str
300             );
301           irp->IoStatus.Information =
302             (ULONG_PTR) wv_palloc(str_len * sizeof *str);
303           if (irp->IoStatus.Information == 0) {
304               DBG("wv_palloc DeviceTextLocationInformation\n");
305               status = STATUS_INSUFFICIENT_RESOURCES;
306               goto alloc_info;
307             }
308           RtlCopyMemory(
309               (PWCHAR) irp->IoStatus.Information,
310               str,
311               str_len * sizeof (WCHAR)
312             );
313           status = STATUS_SUCCESS;
314           goto alloc_info;
315
316         default:
317           irp->IoStatus.Information = 0;
318           status = STATUS_NOT_SUPPORTED;
319       }
320     /* irp->IoStatus.Information not freed. */
321     alloc_info:
322
323     wv_free(str);
324     alloc_str:
325
326     return WvlIrpComplete(irp, irp->IoStatus.Information, status);
327   }