kvm-guest-drivers-windows
546 строк · 18.1 Кб
1/*
2* Keyboard specific HID functionality
3*
4* Copyright (c) 2016-2017 Red Hat, Inc.
5*
6* Author(s):
7* Ladi Prosek <lprosek@redhat.com>
8*
9* Redistribution and use in source and binary forms, with or without
10* modification, are permitted provided that the following conditions
11* are met :
12* 1. Redistributions of source code must retain the above copyright
13* notice, this list of conditions and the following disclaimer.
14* 2. Redistributions in binary form must reproduce the above copyright
15* notice, this list of conditions and the following disclaimer in the
16* documentation and / or other materials provided with the distribution.
17* 3. Neither the names of the copyright holders nor the names of their contributors
18* may be used to endorse or promote products derived from this software
19* without specific prior written permission.
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30* SUCH DAMAGE.
31*/
32
33#include "precomp.h"
34#include "vioinput.h"
35#include "Hid.h"
36
37#if defined(EVENT_TRACING)
38#include "HidKeyboard.tmh"
39#endif
40
41typedef struct _tagInputClassKeyboard
42{
43INPUT_CLASS_COMMON Common;
44
45#define HID_KEYBOARD_KEY_SLOTS 6
46// the keyboard HID report is laid out as follows:
47// offset 0
48// * report ID
49// offset 1
50// * 8 modifiers; one bit per modifier
51// offset 2
52// * padding
53// offset 3
54// * key array of length HID_KEYBOARD_KEY_SLOTS; one byte per key
55
56// bitmap of currently pressed keys
57PUCHAR pKeysPressed;
58// length of the pKeysPressed bitmap in bytes
59SIZE_T cbKeysPressedLen;
60// number of keys currently pressed
61SIZE_T nKeysPressed;
62// size of the output (host -> device) report
63SIZE_T cbOutputReport;
64// last seen output (host -> device) report
65PUCHAR pLastOutputReport;
66} INPUT_CLASS_KEYBOARD, *PINPUT_CLASS_KEYBOARD;
67
68static UCHAR
69HIDLEDEventCodeToUsageCode(
70USHORT uEventCode)
71{
72switch (uEventCode)
73{
74case LED_NUML: return HID_USAGE_LED_NUM_LOCK;
75case LED_CAPSL: return HID_USAGE_LED_CAPS_LOCK;
76case LED_SCROLLL: return HID_USAGE_LED_SCROLL_LOCK;
77case LED_COMPOSE: return HID_USAGE_LED_COMPOSE;
78case LED_KANA: return HID_USAGE_LED_KANA;
79case LED_SLEEP: return HID_USAGE_LED_STAND_BY;
80case LED_SUSPEND: return HID_USAGE_LED_SYSTEM_SUSPEND;
81case LED_MUTE: return HID_USAGE_LED_MUTE;
82case LED_MISC: return HID_USAGE_LED_GENERIC_INDICATOR;
83case LED_MAIL: return HID_USAGE_LED_MESSAGE_WAITING;
84case LED_CHARGING: return HID_USAGE_LED_EXTERNAL_POWER_CONNECTED;
85}
86return 0;
87}
88
89static USHORT
90HIDLEDUsageCodeToEventCode(
91ULONG uCode)
92{
93switch (uCode)
94{
95case HID_USAGE_LED_NUM_LOCK: return LED_NUML;
96case HID_USAGE_LED_CAPS_LOCK: return LED_CAPSL;
97case HID_USAGE_LED_SCROLL_LOCK: return LED_SCROLLL;
98case HID_USAGE_LED_COMPOSE: return LED_COMPOSE;
99case HID_USAGE_LED_KANA: return LED_KANA;
100case HID_USAGE_LED_STAND_BY: return LED_SLEEP;
101case HID_USAGE_LED_SYSTEM_SUSPEND: return LED_SUSPEND;
102case HID_USAGE_LED_MUTE: return LED_MUTE;
103case HID_USAGE_LED_GENERIC_INDICATOR: return LED_MISC;
104case HID_USAGE_LED_MESSAGE_WAITING: return LED_MAIL;
105case HID_USAGE_LED_EXTERNAL_POWER_CONNECTED: return LED_CHARGING;
106}
107return 0xFF;
108}
109
110static VOID
111HIDKeyboardEventKeyToReportKey(
112PINPUT_CLASS_KEYBOARD pKeyboardDesc,
113UCHAR uCode,
114ULONG uValue)
115{
116PUCHAR pKeyArray = pKeyboardDesc->Common.pHidReport + HID_REPORT_DATA_OFFSET + 2;
117BOOLEAN bPressed = FALSE, bReleased = FALSE;
118UCHAR uMask, uByte, uBit;
119SIZE_T i, iIndex;
120
121TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);
122
123// figure out the bitmap index and mask
124iIndex = uCode / 8;
125uMask = 1 << (uCode % 8);
126
127// set or clear the corresponding bitmap bit
128if (uValue)
129{
130if (!(pKeyboardDesc->pKeysPressed[iIndex] & uMask))
131{
132pKeyboardDesc->nKeysPressed++;
133pKeyboardDesc->pKeysPressed[iIndex] |= uMask;
134bPressed = TRUE;
135}
136}
137else
138{
139if (pKeyboardDesc->pKeysPressed[iIndex] & uMask)
140{
141pKeyboardDesc->nKeysPressed--;
142pKeyboardDesc->pKeysPressed[iIndex] &= ~uMask;
143bReleased = TRUE;
144}
145}
146
147// update the HID report key array
148if (bPressed)
149{
150if (pKeyboardDesc->nKeysPressed <= HID_KEYBOARD_KEY_SLOTS)
151{
152PUCHAR pSlot = memchr(pKeyArray, 0, HID_KEYBOARD_KEY_SLOTS);
153ASSERT(pSlot);
154if (pSlot)
155{
156*pSlot = uCode;
157}
158}
159else if (pKeyboardDesc->nKeysPressed == HID_KEYBOARD_KEY_SLOTS + 1)
160{
161// we just got into the "rolled over" state
162#pragma warning (push)
163#pragma warning (disable:28625) // C28625 heuristic triggered by "key" in the variable name
164RtlFillMemory(pKeyArray, HID_KEYBOARD_KEY_SLOTS, HID_USAGE_KEYBOARD_ROLLOVER);
165#pragma warning (pop)
166}
167}
168else if (bReleased)
169{
170if (pKeyboardDesc->nKeysPressed < HID_KEYBOARD_KEY_SLOTS)
171{
172PUCHAR pSlot = memchr(pKeyArray, uCode, HID_KEYBOARD_KEY_SLOTS);
173ASSERT(pSlot);
174if (pSlot)
175{
176*pSlot = 0;
177}
178}
179else if (pKeyboardDesc->nKeysPressed == HID_KEYBOARD_KEY_SLOTS)
180{
181// we just got out of the "rolled over" state
182i = 0;
183for (iIndex = 0; iIndex < pKeyboardDesc->cbKeysPressedLen; iIndex++)
184{
185uByte = pKeyboardDesc->pKeysPressed[iIndex];
186while (DecodeNextBit(&uByte, &uBit))
187{
188ASSERT(i < HID_KEYBOARD_KEY_SLOTS);
189if (i < HID_KEYBOARD_KEY_SLOTS)
190{
191pKeyArray[i++] = (UCHAR)(8 * iIndex + uBit);
192}
193}
194}
195}
196}
197
198TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- %s\n", __FUNCTION__);
199}
200
201static NTSTATUS
202HIDKeyboardEventToReport(
203PINPUT_CLASS_COMMON pClass,
204PVIRTIO_INPUT_EVENT pEvent)
205{
206PUCHAR pReport = pClass->pHidReport;
207PINPUT_CLASS_KEYBOARD pKeyboardDesc = (PINPUT_CLASS_KEYBOARD)pClass;
208
209TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);
210
211pReport[HID_REPORT_ID_OFFSET] = pClass->uReportID;
212if (pEvent->type == EV_KEY)
213{
214ULONG uCode = HIDKeyboardEventCodeToUsageCode(pEvent->code);
215if (uCode >= 0xE0 && uCode <= 0xE7)
216{
217// one bit per modifier, all in one byte
218unsigned char uMask = 1 << (uCode - 0xE0);
219if (pEvent->value)
220{
221pReport[HID_REPORT_DATA_OFFSET] |= uMask;
222}
223else
224{
225pReport[HID_REPORT_DATA_OFFSET] &= ~uMask;
226}
227pClass->bDirty = TRUE;
228}
229else if (uCode != 0)
230{
231HIDKeyboardEventKeyToReportKey(pKeyboardDesc, (UCHAR)uCode, pEvent->value);
232pClass->bDirty = TRUE;
233}
234}
235
236TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- %s\n", __FUNCTION__);
237return STATUS_SUCCESS;
238}
239
240static NTSTATUS
241HIDKeyboardSendStatus(
242PINPUT_DEVICE pContext,
243USHORT uType,
244USHORT uCode,
245ULONG uValue,
246WDFREQUEST Request)
247{
248PVIRTIO_INPUT_EVENT_WITH_REQUEST pEvent;
249NTSTATUS status;
250PHYSICAL_ADDRESS pa;
251
252TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "--> %s\n", __FUNCTION__);
253
254pEvent = pContext->StatusQMemBlock->get_slice(pContext->StatusQMemBlock, &pa);
255if (pEvent == NULL)
256{
257status = STATUS_INSUFFICIENT_RESOURCES;
258}
259else
260{
261pEvent->Event.type = uType;
262pEvent->Event.code = uCode;
263pEvent->Event.value = uValue;
264pEvent->Request = Request;
265
266WdfSpinLockAcquire(pContext->StatusQLock);
267status = VIOInputAddOutBuf(pContext->StatusQ, &pEvent->Event, pa);
268WdfSpinLockRelease(pContext->StatusQLock);
269if (!NT_SUCCESS(status))
270{
271pContext->StatusQMemBlock->return_slice(pContext->StatusQMemBlock, pEvent);
272}
273}
274
275TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "<-- %s\n", __FUNCTION__);
276return status;
277}
278
279static NTSTATUS
280HIDKeyboardReportToEvent(
281PINPUT_CLASS_COMMON pClass,
282PINPUT_DEVICE pContext,
283WDFREQUEST Request,
284PUCHAR pReport,
285ULONG cbReport)
286{
287PINPUT_CLASS_KEYBOARD pKeyboardDesc = (PINPUT_CLASS_KEYBOARD)pClass;
288USHORT uPendingLedCode = 0xFF;
289USHORT uShortPendingLedValue = 0;
290NTSTATUS status = STATUS_SUCCESS;
291SIZE_T i;
292
293TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "--> %s\n", __FUNCTION__);
294
295if (cbReport < pKeyboardDesc->cbOutputReport)
296{
297// unexpected output report size
298return STATUS_BUFFER_TOO_SMALL;
299}
300if (pReport[HID_REPORT_ID_OFFSET] != pClass->uReportID)
301{
302// unexpected output report ID
303return STATUS_INVALID_PARAMETER;
304}
305
306// diff this report with the last one we've seen
307for (i = HID_REPORT_DATA_OFFSET; i < pKeyboardDesc->cbOutputReport; i++)
308{
309UCHAR uDiff = pKeyboardDesc->pLastOutputReport[i] ^ pReport[i];
310UCHAR uValue;
311while (DecodeNextBit(&uDiff, &uValue))
312{
313ULONG uCode = uValue + 8 * (ULONG)(i - HID_REPORT_DATA_OFFSET);
314
315// LED codes are 1-based
316USHORT uLedCode = HIDLEDUsageCodeToEventCode(uCode + 1);
317if (uLedCode != 0xFF)
318{
319if (uPendingLedCode != 0xFF)
320{
321// send the pending LED change to the host
322status = HIDKeyboardSendStatus(pContext, EV_LED, uPendingLedCode,
323uShortPendingLedValue, NULL);
324if (!NT_SUCCESS(status))
325{
326return status;
327}
328}
329uPendingLedCode = uLedCode;
330uShortPendingLedValue = !!(pReport[i] & (1 << uValue));
331}
332}
333}
334
335// send the last pending LED change; this one will complete the request
336if (uPendingLedCode != 0xFF)
337{
338status = HIDKeyboardSendStatus(pContext, EV_LED, uPendingLedCode,
339uShortPendingLedValue, Request);
340}
341
342if (NT_SUCCESS(status))
343{
344// save this report
345RtlCopyMemory(pKeyboardDesc->pLastOutputReport, pReport,
346pKeyboardDesc->cbOutputReport);
347}
348if (uPendingLedCode == 0xFF)
349{
350// nothing was sent up, complete the request now
351WdfRequestComplete(Request, status);
352}
353
354TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "<-- %s\n", __FUNCTION__);
355return status;
356}
357
358static VOID
359HIDKeyboardCleanup(
360PINPUT_CLASS_COMMON pClass)
361{
362TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
363
364PINPUT_CLASS_KEYBOARD pKeyboardDesc = (PINPUT_CLASS_KEYBOARD)pClass;
365VIOInputFree(&pKeyboardDesc->pLastOutputReport);
366VIOInputFree(&pKeyboardDesc->pKeysPressed);
367
368TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__);
369}
370
371NTSTATUS
372HIDKeyboardProbe(
373PINPUT_DEVICE pContext,
374PDYNAMIC_ARRAY pHidDesc,
375PVIRTIO_INPUT_CFG_DATA pKeys,
376PVIRTIO_INPUT_CFG_DATA pLeds)
377{
378PINPUT_CLASS_KEYBOARD pKeyboardDesc = NULL;
379NTSTATUS status = STATUS_SUCCESS;
380UCHAR i, uValue, uMaxKey, uMaxLed;
381BOOLEAN bGotKey, bGotLed;
382
383TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
384
385uMaxKey = 0;
386bGotKey = FALSE;
387for (i = 0; i < pKeys->size; i++)
388{
389UCHAR uNonKeys = 0;
390while (DecodeNextBit(&pKeys->u.bitmap[i], &uValue))
391{
392USHORT uKeyCode = uValue + 8 * i;
393ULONG uCode = HIDKeyboardEventCodeToUsageCode(uKeyCode);
394if (uCode != 0 && (uCode & KEY_TYPE_MASK) == KEY_TYPE_KEYBOARD)
395{
396bGotKey = TRUE;
397if (uCode < 0xE0 || uCode > 0xE7)
398{
399uMaxKey = max(uMaxKey, (UCHAR)uCode);
400}
401}
402else
403{
404// we have not recognized this EV_KEY code as a keyboard key
405uNonKeys |= (1 << uValue);
406}
407}
408pKeys->u.bitmap[i] = uNonKeys;
409}
410
411if (!bGotKey)
412{
413// no keys in the array means that we're done
414TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "No keyboard key found\n");
415goto Exit;
416}
417
418// allocate and initialize pKeyboardDesc
419pKeyboardDesc = VIOInputAlloc(sizeof(INPUT_CLASS_KEYBOARD));
420if (pKeyboardDesc == NULL)
421{
422status = STATUS_INSUFFICIENT_RESOURCES;
423goto Exit;
424}
425pKeyboardDesc->Common.EventToReportFunc = HIDKeyboardEventToReport;
426pKeyboardDesc->Common.ReportToEventFunc = HIDKeyboardReportToEvent;
427pKeyboardDesc->Common.CleanupFunc = HIDKeyboardCleanup;
428pKeyboardDesc->Common.uReportID = (UCHAR)(pContext->uNumOfClasses + 1);
429
430HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_GENERIC);
431HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_GENERIC_KEYBOARD);
432HIDAppend2(pHidDesc, HID_TAG_COLLECTION, HID_COLLECTION_APPLICATION);
433
434HIDAppend2(pHidDesc, HID_TAG_REPORT_ID, pKeyboardDesc->Common.uReportID);
435
436// one bit per modifier
437HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_KEYBOARD);
438HIDAppend2(pHidDesc, HID_TAG_USAGE_MINIMUM, 0xE0);
439HIDAppend2(pHidDesc, HID_TAG_USAGE_MAXIMUM, 0xE7);
440HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, 0x00);
441HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, 0x01);
442HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x01);
443HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x08);
444HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE);
445
446// reserved byte
447HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x01);
448HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x08);
449HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_CONSTANT);
450
451// LEDs
452uMaxLed = 0;
453bGotLed = FALSE;
454for (i = 0; i < pLeds->size; i++)
455{
456while (DecodeNextBit(&pLeds->u.bitmap[i], &uValue))
457{
458USHORT uLedCode = uValue + 8 * i;
459UCHAR uCode = HIDLEDEventCodeToUsageCode(uLedCode);
460if (uCode != 0)
461{
462TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got LED %d\n", uLedCode);
463uMaxLed = max(uMaxLed, uCode);
464bGotLed = TRUE;
465}
466}
467}
468if (bGotLed)
469{
470ULONG uNumOfLEDs = uMaxLed + 1;
471pKeyboardDesc->cbOutputReport = (SIZE_T)HID_REPORT_DATA_OFFSET + (uNumOfLEDs + 7) / 8;
472
473// one bit per LED as usual in a keyboard device
474HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x01);
475HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, uMaxLed + 1);
476HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_LED);
477HIDAppend2(pHidDesc, HID_TAG_USAGE_MINIMUM, 1);
478HIDAppend2(pHidDesc, HID_TAG_USAGE_MAXIMUM, uMaxLed + 1);
479HIDAppend2(pHidDesc, HID_TAG_OUTPUT, HID_DATA_FLAG_VARIABLE);
480
481// pad to the nearest whole byte
482if (uNumOfLEDs % 8)
483{
484HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01);
485HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE,
486(ULONG)(pKeyboardDesc->cbOutputReport * 8) - uNumOfLEDs);
487HIDAppend2(pHidDesc, HID_TAG_OUTPUT, HID_DATA_FLAG_CONSTANT);
488}
489
490// allocate and initialize a buffer holding the last seen output report
491pKeyboardDesc->pLastOutputReport = ExAllocatePoolUninitialized(
492NonPagedPool,
493pKeyboardDesc->cbOutputReport,
494VIOINPUT_DRIVER_MEMORY_TAG);
495if (pKeyboardDesc->pLastOutputReport == NULL)
496{
497return STATUS_INSUFFICIENT_RESOURCES;
498}
499RtlZeroMemory(pKeyboardDesc->pLastOutputReport, pKeyboardDesc->cbOutputReport);
500}
501
502// keys
503HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x08);
504HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, HID_KEYBOARD_KEY_SLOTS);
505HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, 0x00);
506HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, uMaxKey);
507HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_KEYBOARD);
508HIDAppend2(pHidDesc, HID_TAG_USAGE_MINIMUM, 0x00);
509HIDAppend2(pHidDesc, HID_TAG_USAGE_MAXIMUM, uMaxKey);
510HIDAppend2(pHidDesc, HID_TAG_INPUT, 0);
511
512HIDAppend1(pHidDesc, HID_TAG_END_COLLECTION);
513
514// allocate and initialize the bitmap of currently pressed keys
515pKeyboardDesc->cbKeysPressedLen = ((uMaxKey + 1) + 7) / 8;
516pKeyboardDesc->pKeysPressed = VIOInputAlloc(pKeyboardDesc->cbKeysPressedLen);
517if (pKeyboardDesc->pKeysPressed == NULL)
518{
519status = STATUS_INSUFFICIENT_RESOURCES;
520goto Exit;
521}
522
523TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT,
524"Created HID keyboard report descriptor with %d keys and %d LEDs\n",
525uMaxKey + 1,
526bGotLed ? (uMaxLed + 1) : 0);
527
528// calculate the keyboard HID report size
529pKeyboardDesc->Common.cbHidReportSize =
530HID_REPORT_DATA_OFFSET +
5312 + // modifiers and padding
532HID_KEYBOARD_KEY_SLOTS;
533
534// register the keyboard class
535status = RegisterClass(pContext, &pKeyboardDesc->Common);
536
537Exit:
538if (!NT_SUCCESS(status) && pKeyboardDesc != NULL)
539{
540pKeyboardDesc->Common.CleanupFunc(&pKeyboardDesc->Common);
541VIOInputFree(&pKeyboardDesc);
542}
543
544TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s (%08x)\n", __FUNCTION__, status);
545return status;
546}
547