64a7cc81d055a88343c4918a9954739b16fe62e7
[people/sha0/winvblock.git] / src / winvblock / device.c
1 /**
2  * Copyright (C) 2009-2010, 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  * Device specifics.
26  */
27
28 #include <ntddk.h>
29
30 #include "winvblock.h"
31 #include "wv_stdlib.h"
32 #include "portable.h"
33 #include "driver.h"
34 #include "device.h"
35 #include "debug.h"
36
37 /* Forward declarations. */
38 static device__free_func device__free_dev_;
39 static device__create_pdo_func device__make_pdo_;
40
41 /**
42  * Create a new device.
43  *
44  * @ret dev             The address of a new device, or NULL for failure.
45  *
46  * This function should not be confused with a PDO creation routine, which is
47  * actually implemented for each device type.  This routine will allocate a
48  * device__type, track it in a global list, as well as populate the device
49  * with default values.
50  */
51 winvblock__lib_func struct device__type * device__create(void) {
52     struct device__type * dev;
53
54     /*
55      * Devices might be used for booting and should
56      * not be allocated from a paged memory pool.
57      */
58     dev = wv_mallocz(sizeof *dev);
59     if (dev == NULL)
60       return NULL;
61     /* Populate non-zero device defaults. */
62     dev->DriverObject = driver__obj_ptr;
63     dev->ops.create_pdo = device__make_pdo_;
64     dev->ops.free = device__free_dev_;
65
66     return dev;
67   }
68
69 /**
70  * Create a device PDO.
71  *
72  * @v dev               Points to the device that needs a PDO.
73  */
74 winvblock__lib_func PDEVICE_OBJECT STDCALL device__create_pdo(
75     IN struct device__type * dev
76   ) {
77     return dev->ops.create_pdo(dev);
78   }
79
80 /**
81  * Default PDO creation operation.
82  *
83  * @v dev               Points to the device that needs a PDO.
84  * @ret NULL            Reports failure, no matter what.
85  *
86  * This function does nothing, since it doesn't make sense to create a PDO
87  * for an unknown type of device.
88  */
89 static PDEVICE_OBJECT STDCALL device__make_pdo_(IN struct device__type * dev) {
90     DBG("No specific PDO creation operation for this device!\n");
91     return NULL;
92   }
93
94 /**
95  * Respond to a device PnP ID query.
96  *
97  * @v dev                       The device being queried for PnP IDs.
98  * @v query_type                The query type.
99  * @v buf                       Wide character, 512-element buffer for the
100  *                              ID response.
101  * @ret winvblock__uint32       The number of wide characters in the response,
102  *                              or 0 upon a failure.
103  */
104 winvblock__uint32 STDCALL device__pnp_id(
105     IN struct device__type * dev,
106     IN BUS_QUERY_ID_TYPE query_type,
107     IN OUT WCHAR (*buf)[512]
108   ) {
109     return dev->ops.pnp_id ? dev->ops.pnp_id(dev, query_type, buf) : 0;
110   }
111
112 /* An IRP handler for a PnP ID query. */
113 NTSTATUS STDCALL device__pnp_query_id(
114     IN struct device__type * dev,
115     IN PIRP irp
116   ) {
117     NTSTATUS status;
118     WCHAR (*str)[512];
119     winvblock__uint32 str_len;
120     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
121
122     /* Allocate the working buffer. */
123     str = wv_mallocz(sizeof *str);
124     if (str == NULL) {
125         DBG("wv_malloc IRP_MN_QUERY_ID\n");
126         status = STATUS_INSUFFICIENT_RESOURCES;
127         goto alloc_str;
128       }
129     /* Invoke the specific device's ID query. */
130     str_len = device__pnp_id(
131         dev,
132         io_stack_loc->Parameters.QueryId.IdType,
133         str
134       );
135     if (str_len == 0) {
136         irp->IoStatus.Information = 0;
137         status = STATUS_NOT_SUPPORTED;
138         goto alloc_info;
139       }
140     /* Allocate the return buffer. */
141     irp->IoStatus.Information = (ULONG_PTR) wv_palloc(str_len * sizeof **str);
142     if (irp->IoStatus.Information == 0) {
143         DBG("wv_palloc failed.\n");
144         status = STATUS_INSUFFICIENT_RESOURCES;
145         goto alloc_info;
146       }
147     /* Copy the working buffer to the return buffer. */
148     RtlCopyMemory(
149         (void *) irp->IoStatus.Information,
150         str,
151         str_len * sizeof **str
152       );
153     status = STATUS_SUCCESS;
154
155     /* irp->IoStatus.Information not freed. */
156     alloc_info:
157
158     wv_free(str);
159     alloc_str:
160
161     return driver__complete_irp(irp, irp->IoStatus.Information, status);
162   }
163
164 /**
165  * Close a device.
166  *
167  * @v dev               Points to the device to close.
168  */
169 winvblock__lib_func void STDCALL device__close(
170     IN struct device__type * dev
171   ) {
172     /* Call the device's close routine. */
173     dev->ops.close(dev);
174     return;
175   }
176
177 /**
178  * Delete a device.
179  *
180  * @v dev               Points to the device to delete.
181  */
182 winvblock__lib_func void STDCALL device__free(IN struct device__type * dev) {
183     /* Call the device's free routine. */
184     dev->ops.free(dev);
185   }
186
187 /**
188  * Default device deletion operation.
189  *
190  * @v dev               Points to the device to delete.
191  */
192 static void STDCALL device__free_dev_(IN struct device__type * dev) {
193     wv_free(dev);
194   }
195
196 /**
197  * Get a device from a DEVICE_OBJECT.
198  *
199  * @v dev_obj           Points to the DEVICE_OBJECT to get the device from.
200  * @ret                 Returns a pointer to the device on success, else NULL.
201  */
202 winvblock__lib_func struct device__type * device__get(PDEVICE_OBJECT dev_obj) {
203     driver__dev_ext_ptr dev_ext;
204
205     if (!dev_obj)
206       return NULL;
207     dev_ext = dev_obj->DeviceExtension;
208     return dev_ext->device;
209   }
210
211 /**
212  * Set the device for a DEVICE_OBJECT.
213  *
214  * @v dev_obj           Points to the DEVICE_OBJECT to set the device for.
215  * @v dev               Points to the device to associate with.
216  */
217 winvblock__lib_func void device__set(
218     PDEVICE_OBJECT dev_obj,
219     struct device__type * dev
220   ) {
221     driver__dev_ext_ptr dev_ext = dev_obj->DeviceExtension;
222     dev_ext->device = dev;
223     return;
224   }