[aoe] Introduce AoE bus module
[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 "portable.h"
31 #include "irp.h"
32 #include "driver.h"
33 #include "device.h"
34 #include "bus.h"
35 #include "aoe.h"
36 #include "mount.h"
37 #include "debug.h"
38
39 /* TODO: Remove this pull from aoe/driver.c */
40 extern irp__handler scan;
41 extern irp__handler show;
42 extern irp__handler mount;
43
44 /* Forward declarations. */
45 static NTSTATUS STDCALL aoe_bus__dev_ctl_(
46     IN PDEVICE_OBJECT,
47     IN PIRP,
48     IN PIO_STACK_LOCATION,
49     IN struct device__type *,
50     OUT winvblock__bool_ptr
51   );
52 static device__pnp_id_func aoe_bus__pnp_id_;
53
54 /* Globals. */
55 struct bus__type * aoe_bus = NULL;
56 static irp__handling aoe_bus__handling_table_[] = {
57     /*
58      * Major, minor, any major?, any minor?, handler
59      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60      * Note that the fall-through case must come FIRST!
61      * Why? It sets completion to true, so others won't be called.
62      */
63     { IRP_MJ_DEVICE_CONTROL, 0, FALSE, TRUE, aoe_bus__dev_ctl_ },
64   };
65
66
67 static NTSTATUS STDCALL aoe_bus__dev_ctl_(
68     IN PDEVICE_OBJECT dev_obj,
69     IN PIRP irp,
70     IN PIO_STACK_LOCATION io_stack_loc,
71     IN struct device__type * dev,
72     OUT winvblock__bool_ptr completion
73   ) {
74     NTSTATUS status = STATUS_NOT_SUPPORTED;
75
76     switch(io_stack_loc->Parameters.DeviceIoControl.IoControlCode) {
77         case IOCTL_AOE_SCAN:
78           status = scan(dev_obj, irp, io_stack_loc, dev, completion);
79           break;
80
81         case IOCTL_AOE_SHOW:
82           status = show(dev_obj, irp, io_stack_loc, dev, completion);
83           break;
84
85         case IOCTL_AOE_MOUNT:
86           status = mount(dev_obj, irp, io_stack_loc, dev, completion);
87           break;
88
89         case IOCTL_AOE_UMOUNT:
90           io_stack_loc->Parameters.DeviceIoControl.IoControlCode =
91             IOCTL_FILE_DETACH;
92           break;
93       }
94     if (*completion)
95       IoCompleteRequest(irp, IO_NO_INCREMENT);
96     return status;
97   }
98
99 /**
100  * Create the AoE bus.
101  *
102  * @ret         TRUE for success, else FALSE.
103  */
104 winvblock__bool aoe_bus__create(void) {
105     struct bus__type * new_bus;
106
107     /* We should only be called once. */
108     if (aoe_bus) {
109         DBG("AoE bus already created\n");
110         return FALSE;
111       }
112     /* Try to create the AoE bus. */
113     new_bus = bus__create();
114     if (!new_bus) {
115         DBG("Failed to create AoE bus!\n");
116         goto err_new_bus;
117       }
118     /* When the PDO is created, we need to handle PnP ID queries. */
119     new_bus->device->ops.pnp_id = aoe_bus__pnp_id_;
120     /* Name the bus when the PDO is created. */
121     RtlInitUnicodeString(
122         &new_bus->dev_name,
123         L"\\Device\\AoE"
124       );
125     RtlInitUnicodeString(
126         &new_bus->dos_dev_name,
127         L"\\DosDevices\\AoE"
128       );
129     new_bus->named = TRUE;
130     /* Add it as a sub-bus to WinVBlock. */
131     if (!bus__add_child(driver__bus(), new_bus->device)) {
132         DBG("Couldn't add AoE bus to WinVBlock bus!\n");
133         goto err_add_child;
134       }
135     /* All done. */
136     aoe_bus = new_bus;
137     return TRUE;
138
139     err_add_child:
140
141     device__free(new_bus->device);
142     err_new_bus:
143
144     return FALSE;
145   }
146
147 /* Destroy the AoE bus. */
148 void aoe_bus__free(void) {
149     if (!aoe_bus)
150       /* Nothing to do. */
151       return;
152
153     IoDeleteSymbolicLink(&aoe_bus->dos_dev_name);
154     IoDeleteSymbolicLink(&aoe_bus->dev_name);
155     IoDeleteDevice(aoe_bus->device->Self);
156     irp__unreg_table(
157         &aoe_bus->device->irp_handler_chain,
158         aoe_bus__handling_table_
159       );
160     #if 0
161     bus__remove_child(driver__bus(), aoe_bus->device);
162     #endif
163     device__free(aoe_bus->device);
164     return;
165   }
166
167 static winvblock__uint32 STDCALL aoe_bus__pnp_id_(
168     IN struct device__type * 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, L"WinVBlock\\AoEDEVID") + 1;
175
176         case BusQueryInstanceID:
177           return swprintf(*buf, L"WinVBlock\\AoEINSTID") + 1;
178
179         case BusQueryHardwareIDs:
180           return swprintf(*buf, L"WinVBlock\\AoEHWID") + 1;
181
182         case BusQueryCompatibleIDs:
183           return swprintf(*buf, L"WinVBlock\\AoECOMPATID") + 4;
184
185         default:
186           return 0;
187       }
188   }