-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDevice.c
456 lines (371 loc) · 14.5 KB
/
Device.c
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
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
/*++
Module Name:
device.c - Device handling events for example driver.
Abstract:
This file contains the device entry points and callbacks.
Environment:
Kernel-mode Driver Framework
--*/
#include "driver.h"
#include "wdftimer.h"
#include "device.tmh"
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, WacomPracticeCreateDevice)
#pragma alloc_text (PAGE, WacomPracticeEvtDevicePrepareHardware)
#endif
const __declspec(selectany) LONGLONG DEFAULT_CONTROL_TRANSFER_TIMEOUT = 5 * -1 * WDF_TIMEOUT_TO_SEC;
//
// vendor commands
//
#define UWIRE_GET_FIRMWARE 0x22 // bRequest used to retrieve firmware version
#define UWIRE_LED 0x36 // bRequest used to write/flush/preload the RGB LED
#define UWIRE_CHANGE_SERIAL 0x37 // bRequest used to change the device serial number
// misc defines required to talk to the firmware properly
#define UWIRE_FIRMWARE_VERSION 0b00010010 // expected: ver 1.2
#define PIN 0x1 // LED is connected the the ATtiny10's PIN1 -- this has to be specified to the firmware
// HSV-space colors for demo (v is irrelevant as it will be cycled)
#define green_h 113
#define green_s 38
#define blue_h 233
#define blue_s 67
#define orange_h 33
#define orange_s 59
// state variable for the blink routine
typedef enum { ON, OFF } LedBlink;
LedBlink ledBlinkState = OFF;
int periodsRemaining = 0;
NTSTATUS
WacomPracticeCreateDevice(
_Inout_ PWDFDEVICE_INIT DeviceInit
)
/*++
Routine Description:
Worker routine called to create a device and its software resources.
Arguments:
DeviceInit - Pointer to an opaque init structure. Memory for this
structure will be freed by the framework when the WdfDeviceCreate
succeeds. So don't access the structure after that point.
Return Value:
NTSTATUS
--*/
{
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDF_OBJECT_ATTRIBUTES deviceAttributes;
PDEVICE_CONTEXT deviceContext;
WDFDEVICE device;
NTSTATUS status;
PAGED_CODE();
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
pnpPowerCallbacks.EvtDevicePrepareHardware = WacomPracticeEvtDevicePrepareHardware;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (NT_SUCCESS(status)) {
//
// Get a pointer to the device context structure that we just associated
// with the device object. We define this structure in the device.h
// header file. DeviceGetContext is an inline function generated by
// using the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro in device.h.
// This function will do the type checking and return the device context.
// If you pass a wrong object handle it will return NULL and assert if
// run under framework verifier mode.
//
deviceContext = DeviceGetContext(device);
//
// Initialize the context.
//
deviceContext->PrivateDeviceData = 0;
//
// Create a device interface so that applications can find and talk
// to us.
//
status = WdfDeviceCreateDeviceInterface(
device,
&GUID_DEVINTERFACE_WacomPractice,
NULL // ReferenceString
);
if (NT_SUCCESS(status)) {
//
// Initialize the I/O Package and any Queues
//
status = WacomPracticeQueueInitialize(device);
}
}
return status;
}
VOID GetFirmwareVersion(
__in PDEVICE_CONTEXT DeviceContext
)
/*
* A function for retrieving the USB device's firmware version.
* Should return firmware version
*/
{
NTSTATUS status;
WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket;
WDF_MEMORY_DESCRIPTOR memoryDescriptor;
WDF_REQUEST_SEND_OPTIONS sendOptions;
USHORT firmwareVersion = 0; // expressed as ver X.Y where X is the 4 MSB and Y is the 4 LSB
PAGED_CODE();
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, (PVOID)&firmwareVersion, sizeof(firmwareVersion));
WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, WDF_REQUEST_SEND_OPTION_TIMEOUT);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&sendOptions, DEFAULT_CONTROL_TRANSFER_TIMEOUT);
// create packet configured to be from host to device using the right vendor command
WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, BmRequestDeviceToHost, BmRequestToDevice, UWIRE_GET_FIRMWARE, 0, 0);
// and send it
status = WdfUsbTargetDeviceSendControlTransferSynchronously(DeviceContext->UsbDevice,
WDF_NO_HANDLE,
&sendOptions,
&controlSetupPacket,
&memoryDescriptor,
NULL);
if (!NT_SUCCESS(status)) {
KdPrint(("Device %d: Failed to get device firmware version. status: 0x%x\n", DeviceContext->DeviceNumber, status));
/*TraceEvents(
TRACE_LEVEL_ERROR,
DBG_RUN,
"Device %d: Failed to get device firmware version 0x%x\n",
DeviceContext->DeviceNumber,
status);*/
}
else {
// print firmware version obtained
KdPrint(("Device %d: Got device firmware version: %d.%d\n", DeviceContext->DeviceNumber, firmwareVersion & 0xF0 >> 4, firmwareVersion & 0x0F));
/*TraceEvents(TRACE_LEVEL_INFORMATION,DBG_RUN,"Device %d: Got device firmware version: %d.%d\n",
DeviceContext->DeviceNumber,
((firmwareVersion & 0xF0) >> 4), // upper 4 bits
(firmwareVersion & 0x0F) // lower 4 bits
); */
}
return;
}
VOID SetLEDColor(
__in PDEVICE_CONTEXT DeviceContext,
__in int r,
__in int g,
__in int b
)
/*
* A function which sends a packet to the device to tell it which color to turn the RGB LED onto.
* Uses SETUP requests.
*/
{
NTSTATUS status;
WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket;
WDF_MEMORY_DESCRIPTOR memoryDescriptor;
WDF_REQUEST_SEND_OPTIONS sendOptions;
ULONG buffer = 0;
//PAGED_CODE();
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, (PVOID)&buffer, sizeof(buffer));
WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, WDF_REQUEST_SEND_OPTION_TIMEOUT);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&sendOptions, DEFAULT_CONTROL_TRANSFER_TIMEOUT);
//
// Commands are sent via SETUP requests. While this is unorthodox, it keeps the firmware simple.
// Setup packets that aren't system requests are automatically forwarded to the LED.
//
USHORT wValue = (USHORT)((g << 8) | PIN | 0x30); // The wValue and wIndex fields are used to transfer the
USHORT wIndex = (USHORT)((b << 8) | r); // data telling the MCU which color to send to the LED's internal controller
WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, BmRequestDeviceToHost, BmRequestToDevice, UWIRE_LED, wValue, wIndex);
status = WdfUsbTargetDeviceSendControlTransferSynchronously(DeviceContext->UsbDevice,
WDF_NO_HANDLE,
&sendOptions,
&controlSetupPacket,
&memoryDescriptor,
NULL);
if (!NT_SUCCESS(status)) {
KdPrint(("Device %d: Failed to set LED. status: 0x%x\n", DeviceContext->DeviceNumber, status));
//TraceEvents(TRACE_LEVEL_ERROR,DBG_RUN,"Device %d: Failed to set LED. status: 0x%x\n",DeviceContext->DeviceNumber,status);
}
else {
KdPrint(("Device %d: Successfully set LED to RGB = (%d,%d,%d)\n", DeviceContext->DeviceNumber, r, g, b));
}
//KdPrint(("DEBUG: setup packet return buffer val = 0x%x\n", buffer));
return;
}
VOID CycleLEDColor(
__in PDEVICE_CONTEXT DeviceContext,
__in int num_cycles,
__in int h,
__in int s
)
/* Function to start a fading color cycle on the LED by sending successive SETUP requests
* The fading lasts for three full cycles then stops.
*/
{
int v = 0;
int r;
int g;
int b;
// cycling through v values from 0 to 100% then back to 0
for (int i = 0; i < num_cycles; i++) {
LARGE_INTEGER delay;
delay.QuadPart = 60000; // 60,000 * 100 ns = 6,000,000 ns = 6 ms
for (int j = 0; j <= 100; j++) {
v = j;
// convert to RGB color space
HSVtoRGB(&r, &g, &b, h, s, v);
// send through USB packet
SetLEDColor(DeviceContext, r, g, b);
// delay for 3 ms
KeDelayExecutionThread(KernelMode, FALSE, &delay);
}
for (int j = 100; j >= 0; j--) {
v = j;
// convert to RGB color space
HSVtoRGB(&r, &g, &b, h, s, v);
// send through USB packet
SetLEDColor(DeviceContext, r, g, b);
// delay for 3 ms
KeDelayExecutionThread(KernelMode, FALSE, &delay);
}
}
return;
}
_Use_decl_annotations_
VOID BlinkLEDColorCallback(
__in WDFTIMER timer
)
/* The callback to the timer used to blink the LED */
{
UNREFERENCED_PARAMETER(timer);
switch (ledBlinkState) {
case(ON):
ledBlinkState = OFF;
break;
case(OFF):
ledBlinkState = ON;
break;
}
if (periodsRemaining > 0) periodsRemaining -= 1;
}
VOID BlinkLEDColor(
__in PDEVICE_CONTEXT DeviceContext,
__in int duration,
__in int r,
__in int g,
__in int b
)
/* Blinks the color selected on the LED
* - Set duration to -1 to blink indefinitely; otherwise duration is the number of periods to blink for
*/
{
NTSTATUS status;
WDFTIMER timer;
WDF_TIMER_CONFIG timerConfig;
WDF_OBJECT_ATTRIBUTES timerAttrib;
LARGE_INTEGER delay1ms;
delay1ms.QuadPart = 10000; // 10,000 * 100 ns = 1,000,000 ns = 1 ms
//
// Set up a WDF timer for color change every second
//
WDF_TIMER_CONFIG_INIT(&timerConfig, BlinkLEDColorCallback);
timerConfig.Period = 1000; // 1s
timerConfig.TolerableDelay = 50; // +/- 5% error range
timerConfig.AutomaticSerialization = TRUE;
WDF_OBJECT_ATTRIBUTES_INIT(&timerAttrib);
timerAttrib.ParentObject = WdfObjectContextGetObject(DeviceContext);
status = WdfTimerCreate(&timerConfig, &timerAttrib, &timer);
if (!NT_SUCCESS(status)) {
KdPrint(("Failed to setup timer for LED blink routine. status = 0x%x", status));
return;
}
else KdPrint(("Successfully set up timer for LED blink routine."));
//
// Start the timer
//
periodsRemaining = duration;
WdfTimerStart(timer, 0); // due time of 0 --> start immediately
//
// Determine when or whether to stop the timer -- poll every 1 ms
//
while (TRUE) {
KeDelayExecutionThread(KernelMode, FALSE, &delay1ms); // delay by 1 ms
if (ledBlinkState == ON) SetLEDColor(DeviceContext, r, g, b);
else SetLEDColor(DeviceContext, 0, 0, 0);
if (periodsRemaining == 0) {
WdfTimerStop(timer, TRUE); // stop and wait for queued DCPs to execute
break; // exit while loop and return from function
}
}
}
NTSTATUS
WacomPracticeEvtDevicePrepareHardware(
_In_ WDFDEVICE Device,
_In_ WDFCMRESLIST ResourceList,
_In_ WDFCMRESLIST ResourceListTranslated
)
/*++
Routine Description:
In this callback, the driver does whatever is necessary to make the
hardware ready to use. In the case of a USB device, this involves
reading and selecting descriptors.
Arguments:
Device - handle to a device
Return Value:
NT status value
--*/
{
NTSTATUS status;
PDEVICE_CONTEXT pDeviceContext;
WDF_USB_DEVICE_CREATE_CONFIG createParams;
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS configParams;
UNREFERENCED_PARAMETER(ResourceList);
UNREFERENCED_PARAMETER(ResourceListTranslated);
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
status = STATUS_SUCCESS;
pDeviceContext = DeviceGetContext(Device);
//
// Create a USB device handle so that we can communicate with the
// underlying USB stack. The WDFUSBDEVICE handle is used to query,
// configure, and manage all aspects of the USB device.
// These aspects include device properties, bus properties,
// and I/O creation and synchronization. We only create the device the first time
// PrepareHardware is called. If the device is restarted by pnp manager
// for resource rebalance, we will use the same device handle but then select
// the interfaces again because the USB stack could reconfigure the device on
// restart.
//
if (pDeviceContext->UsbDevice == NULL) {
//
// Specifying a client contract version of 602 enables us to query for
// and use the new capabilities of the USB driver stack for Windows 8.
// It also implies that we conform to rules mentioned in MSDN
// documentation for WdfUsbTargetDeviceCreateWithParameters.
//
WDF_USB_DEVICE_CREATE_CONFIG_INIT(&createParams,
USBD_CLIENT_CONTRACT_VERSION_602
);
status = WdfUsbTargetDeviceCreateWithParameters(Device,
&createParams,
WDF_NO_OBJECT_ATTRIBUTES,
&pDeviceContext->UsbDevice
);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"WdfUsbTargetDeviceCreateWithParameters failed 0x%x", status);
return status;
}
}
//
// Select the first configuration of the device, using the first alternate
// setting of each interface
//
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_MULTIPLE_INTERFACES(&configParams,
0,
NULL
);
status = WdfUsbTargetDeviceSelectConfig(pDeviceContext->UsbDevice,
WDF_NO_OBJECT_ATTRIBUTES,
&configParams
);
// on startup, retrieve the firmware version and turn on the LED
GetFirmwareVersion(pDeviceContext);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"WdfUsbTargetDeviceSelectConfig failed 0x%x", status);
return status;
}
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
return status;
}