1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
|
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.
Module Name:
Toaster.c
Abstract:
This is a simple form of function driver for toaster device. The driver
doesn't handle any PnP and Power events because the framework provides
default behavior for those events. This driver has enough support to
allow an user application (toast/notify.exe) to open the device
interface registered by the driver and send read, write or ioctl requests.
Environment:
Kernel mode
--*/
#include "toaster.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, ToasterEvtDeviceAdd)
#pragma alloc_text (PAGE, ToasterEvtIoRead)
#pragma alloc_text (PAGE, ToasterEvtIoWrite)
#pragma alloc_text (PAGE, ToasterEvtIoDeviceControl)
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
DriverEntry initializes the driver and is the first routine called by the
system after the driver is loaded. DriverEntry configures and creates a WDF driver
object.
.
Parameters Description:
DriverObject - represents the instance of the function driver that is loaded
into memory. DriverObject is allocated by the system before the
driver is loaded, and it is released by the system after the system unloads
the function driver from memory.
RegistryPath - represents the driver specific path in the Registry.
The function driver can use the path to store driver related data between
reboots. The path does not store hardware instance specific data.
Return Value:
STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
WDF_DRIVER_CONFIG config;
KdPrint(("Toaster Function Driver Sample - Driver Framework Edition.\n"));
//
// Initialize driver config to control the attributes that
// are global to the driver. Note that framework by default
// provides a driver unload routine. If DriverEntry creates any resources
// that require clean-up in driver unload,
// you can manually override the default by supplying a pointer to the EvtDriverUnload
// callback in the config structure. In general xxx_CONFIG_INIT macros are provided to
// initialize most commonly used members.
//
WDF_DRIVER_CONFIG_INIT(
&config,
ToasterEvtDeviceAdd
);
//
// Create a framework driver object to represent our driver.
//
status = WdfDriverCreate(
DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES, // Driver Attributes
&config, // Driver Config Info
WDF_NO_HANDLE
);
if (!NT_SUCCESS(status)) {
KdPrint( ("WdfDriverCreate failed with status 0x%x\n", status));
}
return status;
}
NTSTATUS
ToasterEvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
/*++
Routine Description:
ToasterEvtDeviceAdd is called by the framework in response to AddDevice
call from the PnP manager. We create and initialize a WDF device object to
represent a new instance of toaster device.
Arguments:
Driver - Handle to a framework driver object created in DriverEntry
DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PFDO_DATA fdoData;
WDF_IO_QUEUE_CONFIG queueConfig;
WDF_OBJECT_ATTRIBUTES fdoAttributes;
WDFDEVICE hDevice;
WDFQUEUE queue;
UNREFERENCED_PARAMETER(Driver);
PAGED_CODE();
KdPrint(("ToasterEvtDeviceAdd called\n"));
//
// Initialize attributes and a context area for the device object.
//
//
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FDO_DATA);
//
// Create a framework device object.This call will in turn create
// a WDM device object, attach to the lower stack, and set the
// appropriate flags and attributes.
//
status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &hDevice);
if (!NT_SUCCESS(status)) {
KdPrint( ("WdfDeviceCreate failed with status code 0x%x\n", status));
return status;
}
//
// Get the device context by using the accessor function specified in
// the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for FDO_DATA.
//
fdoData = ToasterFdoGetData(hDevice);
//
// Tell the Framework that this device will need an interface
//
status = WdfDeviceCreateDeviceInterface(
hDevice,
(LPGUID) &GUID_DEVINTERFACE_TOASTER,
NULL // ReferenceString
);
if (!NT_SUCCESS (status)) {
KdPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status));
return status;
}
//
// Register I/O callbacks to tell the framework that you are interested
// in handling IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL requests.
// If a specific callback function is not specified for one ofthese,
// the request will be dispatched to the EvtIoDefault handler, if any.
// If there is no EvtIoDefault handler, the request will be failed with
// STATUS_INVALID_DEVICE_REQUEST.
// WdfIoQueueDispatchParallel means that we are capable of handling
// all the I/O requests simultaneously and we are responsible for protecting
// data that could be accessed by these callbacks simultaneously.
// A default queue gets all the requests that are not
// configured for forwarding using WdfDeviceConfigureRequestDispatching.
//
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel);
queueConfig.EvtIoRead = ToasterEvtIoRead;
queueConfig.EvtIoWrite = ToasterEvtIoWrite;
queueConfig.EvtIoDeviceControl = ToasterEvtIoDeviceControl;
//
// By default, Static Driver Verifier (SDV) displays a warning if it
// doesn't find the EvtIoStop callback on a power-managed queue.
// The 'assume' below causes SDV to suppress this warning. If the driver
// has not explicitly set PowerManaged to WdfFalse, the framework creates
// power-managed queues when the device is not a filter driver. Normally
// the EvtIoStop is required for power-managed queues, but for this driver
// it is not needed b/c the driver doesn't hold on to the requests or
// forward them to other drivers. This driver completes the requests
// directly in the queue's handlers. If the EvtIoStop callback is not
// implemented, the framework waits for all driver-owned requests to be
// done before moving in the Dx/sleep states or before removing the
// device, which is the correct behavior for this type of driver.
// If the requests were taking an indeterminate amount of time to complete,
// or if the driver forwarded the requests to a lower driver/another stack,
// the queue should have an EvtIoStop/EvtIoResume.
//
__analysis_assume(queueConfig.EvtIoStop != 0);
status = WdfIoQueueCreate(
hDevice,
&queueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&queue
);
__analysis_assume(queueConfig.EvtIoStop == 0);
if (!NT_SUCCESS (status)) {
KdPrint( ("WdfIoQueueCreate failed 0x%x\n", status));
return status;
}
return status;
}
VOID
ToasterEvtIoRead (
WDFQUEUE Queue,
WDFREQUEST Request,
size_t Length
)
/*++
Routine Description:
Performs read from the toaster device. This event is called when the
framework receives IRP_MJ_READ requests.
Arguments:
Queue - Handle to the framework queue object that is associated with the
I/O request.
Request - Handle to a framework request object.
Lenght - Length of the data buffer associated with the request.
By default, the queue does not dispatch
zero length read & write requests to the driver and instead to
complete such requests with status success. So we will never get
a zero length request.
Return Value:
None.
--*/
{
NTSTATUS status;
ULONG_PTR bytesCopied =0;
WDFMEMORY memory;
UNREFERENCED_PARAMETER(Queue);
UNREFERENCED_PARAMETER(Length);
PAGED_CODE();
KdPrint(( "ToasterEvtIoRead: Request: 0x%p, Queue: 0x%p\n",
Request, Queue));
//
// Get the request memory and perform read operation here
//
status = WdfRequestRetrieveOutputMemory(Request, &memory);
if(NT_SUCCESS(status) ) {
//
// Copy data into the memory buffer using WdfMemoryCopyFromBuffer
//
}
WdfRequestCompleteWithInformation(Request, status, bytesCopied);
}
VOID
ToasterEvtIoWrite (
WDFQUEUE Queue,
WDFREQUEST Request,
size_t Length
)
/*++
Routine Description:
Performs write to the toaster device. This event is called when the
framework receives IRP_MJ_WRITE requests.
Arguments:
Queue - Handle to the framework queue object that is associated with the
I/O request.
Request - Handle to a framework request object.
Lenght - Length of the data buffer associated with the request.
The default property of the queue is to not dispatch
zero lenght read & write requests to the driver and
complete is with status success. So we will never get
a zero length request.
Return Value:
None
--*/
{
NTSTATUS status;
ULONG_PTR bytesWritten =0;
WDFMEMORY memory;
UNREFERENCED_PARAMETER(Queue);
UNREFERENCED_PARAMETER(Length);
KdPrint(("ToasterEvtIoWrite. Request: 0x%p, Queue: 0x%p\n",
Request, Queue));
PAGED_CODE();
//
// Get the request buffer and perform write operation here
//
status = WdfRequestRetrieveInputMemory(Request, &memory);
if(NT_SUCCESS(status) ) {
//
// 1) Use WdfMemoryCopyToBuffer to copy data from the request
// to driver buffer.
// 2) Or get the buffer pointer from the request by calling
// WdfRequestRetrieveInputBuffer
// 3) Or you can get the buffer pointer from the memory handle
// by calling WdfMemoryGetBuffer.
//
bytesWritten = Length;
}
WdfRequestCompleteWithInformation(Request, status, bytesWritten);
}
VOID
ToasterEvtIoDeviceControl(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength,
IN ULONG IoControlCode
)
/*++
Routine Description:
This event is called when the framework receives IRP_MJ_DEVICE_CONTROL
requests from the system.
Arguments:
Queue - Handle to the framework queue object that is associated
with the I/O request.
Request - Handle to a framework request object.
OutputBufferLength - length of the request's output buffer,
if an output buffer is available.
InputBufferLength - length of the request's input buffer,
if an input buffer is available.
IoControlCode - the driver-defined or system-defined I/O control code
(IOCTL) that is associated with the request.
Return Value:
VOID
--*/
{
NTSTATUS status= STATUS_SUCCESS;
UNREFERENCED_PARAMETER(Queue);
UNREFERENCED_PARAMETER(OutputBufferLength);
UNREFERENCED_PARAMETER(InputBufferLength);
KdPrint(("ToasterEvtIoDeviceControl called\n"));
PAGED_CODE();
//
// Use WdfRequestRetrieveInputBuffer and WdfRequestRetrieveOutputBuffer
// to get the request buffers.
//
switch (IoControlCode) {
default:
status = STATUS_INVALID_DEVICE_REQUEST;
}
//
// Complete the Request.
//
WdfRequestCompleteWithInformation(Request, status, (ULONG_PTR) 0);
}
|