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