kvm-guest-drivers-windows
328 строк · 12.1 Кб
1/*
2* Joystick 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 "HidJoystick.tmh"39#endif40
41// initial length of the axis map (will grow as needed)
42#define AXIS_MAP_INITIAL_LENGTH 643
44typedef struct _tagInputClassJoystick45{
46INPUT_CLASS_COMMON Common;47
48// the joystick HID report is laid out as follows:49// offset 050// * report ID51// offset 152// * axes; four bytes per axis, mapping in pAxisMap53// offset 1 + cbAxisLen54// * buttons; one bit per button followed by padding to the nearest whole byte55
56// number of buttons supported by the HID report57ULONG uNumOfButtons;58// length of axis data59SIZE_T cbAxisLen;60// mapping from EVDEV axis codes to HID axis offsets61PULONG pAxisMap;62} INPUT_CLASS_JOYSTICK, *PINPUT_CLASS_JOYSTICK;63
64static NTSTATUS65HIDJoystickEventToReport(66PINPUT_CLASS_COMMON pClass,67PVIRTIO_INPUT_EVENT pEvent)68{
69PUCHAR pReport = pClass->pHidReport;70PINPUT_CLASS_JOYSTICK pJoystickDesc = (PINPUT_CLASS_JOYSTICK)pClass;71
72TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);73
74pReport[HID_REPORT_ID_OFFSET] = pClass->uReportID;75switch (pEvent->type)76{77case EV_ABS:78if (pJoystickDesc->pAxisMap)79{80PULONG pMap;81for (pMap = pJoystickDesc->pAxisMap; pMap[0] != (ULONG)-1; pMap += 2)82{83if (pMap[0] == ((pEvent->type << 16) | pEvent->code))84{85// 4 bytes per absolute axis86PULONG pAxisPtr = (PULONG)&pReport[HID_REPORT_DATA_OFFSET + pMap[1]];87*pAxisPtr = pEvent->value;88pClass->bDirty = TRUE;89}90}91}92break;93case EV_KEY:94if (pEvent->code >= BTN_JOYSTICK && pEvent->code < BTN_GAMEPAD)95{96USHORT uButton = pEvent->code - BTN_JOYSTICK;97if (uButton < pJoystickDesc->uNumOfButtons)98{99SIZE_T cbOffset = pJoystickDesc->cbAxisLen + (uButton / 8);100UCHAR uBit = 1 << (uButton % 8);101if (pEvent->value)102{103pReport[HID_REPORT_DATA_OFFSET + cbOffset] |= uBit;104}105else106{107pReport[HID_REPORT_DATA_OFFSET + cbOffset] &= ~uBit;108}109pClass->bDirty = TRUE;110}111}112break;113}114
115TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- %s\n", __FUNCTION__);116return STATUS_SUCCESS;117}
118
119static VOID120HIDJoystickCleanup(121PINPUT_CLASS_COMMON pClass)122{
123TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);124
125PINPUT_CLASS_JOYSTICK pJoystickDesc = (PINPUT_CLASS_JOYSTICK)pClass;126VIOInputFree(&pJoystickDesc->pAxisMap);127
128TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__);129}
130
131static VOID132HIDJoystickAxisMapAppend(133PDYNAMIC_ARRAY pAxisMap,134ULONG uCode,135SIZE_T uAxisIndex)136{
137DynamicArrayAppend(pAxisMap, &uCode, sizeof(ULONG));138DynamicArrayAppend(pAxisMap, &uAxisIndex, sizeof(ULONG));139}
140
141NTSTATUS
142HIDJoystickProbe(143PINPUT_DEVICE pContext,144PDYNAMIC_ARRAY pHidDesc,145PVIRTIO_INPUT_CFG_DATA pAxes,146PVIRTIO_INPUT_CFG_DATA pButtons)147{
148PINPUT_CLASS_JOYSTICK pJoystickDesc = NULL;149NTSTATUS status = STATUS_SUCCESS;150UCHAR i, uValue;151SIZE_T cbButtonBytes;152ULONG uNumOfAbsAxes = 0, uNumOfButtons = 0;153DYNAMIC_ARRAY AxisMap = { NULL };154
155TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);156
157for (i = 0; i < pButtons->size; i++)158{159UCHAR uNonButtons = 0;160while (DecodeNextBit(&pButtons->u.bitmap[i], &uValue))161{162ULONG uButtonCode = uValue + 8 * i;163if (uButtonCode >= BTN_JOYSTICK && uButtonCode < BTN_GAMEPAD)164{165// individual joystick button functions are not specified in the HID report,166// only their count is; the max button code we find will determine the count167TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got button %d\n", uButtonCode);168uNumOfButtons = max(uNumOfButtons, uButtonCode - BTN_JOYSTICK + 1);169}170else171{172// not a joystick button, put it back and let other devices claim it173uNonButtons |= (1 << uValue);174}175}176pButtons->u.bitmap[i] = uNonButtons;177}178
179if (uNumOfButtons == 0)180{181TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Joystick buttons not found\n");182goto Exit;183}184
185// allocate and initialize pJoystickDesc186pJoystickDesc = VIOInputAlloc(sizeof(INPUT_CLASS_JOYSTICK));187if (pJoystickDesc == NULL)188{189status = STATUS_INSUFFICIENT_RESOURCES;190goto Exit;191}192pJoystickDesc->Common.EventToReportFunc = HIDJoystickEventToReport;193pJoystickDesc->Common.CleanupFunc = HIDJoystickCleanup;194pJoystickDesc->Common.uReportID = (UCHAR)(pContext->uNumOfClasses + 1);195pJoystickDesc->uNumOfButtons = uNumOfButtons;196
197HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_GENERIC);198HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_GENERIC_JOYSTICK);199HIDAppend2(pHidDesc, HID_TAG_COLLECTION, HID_COLLECTION_APPLICATION);200
201HIDAppend2(pHidDesc, HID_TAG_REPORT_ID, pJoystickDesc->Common.uReportID);202
203// describe axes204HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_GENERIC);205DynamicArrayReserve(&AxisMap, AXIS_MAP_INITIAL_LENGTH * 2 * sizeof(ULONG));206
207for (i = 0; i < pAxes->size; i++)208{209UCHAR uNonAxes = 0;210while (DecodeNextBit(&pAxes->u.bitmap[i], &uValue))211{212USHORT uAbsCode = uValue + 8 * i;213BOOLEAN bSimulationPage = FALSE;214ULONG uAxisCode = 0;215
216switch (uAbsCode)217{218case ABS_X: uAxisCode = HID_USAGE_GENERIC_X; break;219case ABS_Y: uAxisCode = HID_USAGE_GENERIC_Y; break;220case ABS_Z: uAxisCode = HID_USAGE_GENERIC_Z; break;221case ABS_RX: uAxisCode = HID_USAGE_GENERIC_RX; break;222case ABS_RY: uAxisCode = HID_USAGE_GENERIC_RY; break;223case ABS_RZ: uAxisCode = HID_USAGE_GENERIC_RZ; break;224case ABS_TILT_X: uAxisCode = HID_USAGE_GENERIC_VX; break;225case ABS_TILT_Y: uAxisCode = HID_USAGE_GENERIC_VY; break;226case ABS_MISC: uAxisCode = HID_USAGE_GENERIC_SLIDER; break;227case ABS_THROTTLE: bSimulationPage = TRUE; uAxisCode = HID_USAGE_SIMULATION_THROTTLE; break;228case ABS_RUDDER: bSimulationPage = TRUE; uAxisCode = HID_USAGE_SIMULATION_RUDDER; break;229case ABS_BRAKE: bSimulationPage = TRUE; uAxisCode = HID_USAGE_SIMULATION_BRAKE; break;230case ABS_WHEEL: bSimulationPage = TRUE; uAxisCode = HID_USAGE_SIMULATION_STEERING; break;231case ABS_GAS: bSimulationPage = TRUE; uAxisCode = HID_USAGE_SIMULATION_ACCELERATOR; break;232}233
234if (uAxisCode != 0)235{236struct virtio_input_absinfo AbsInfo;237GetAbsAxisInfo(pContext, uAbsCode, &AbsInfo);238
239TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got joystick axis %d, min %d, max %d\n",240uAxisCode, AbsInfo.min, AbsInfo.max);241
242// some of the supported axes are on the generic page, some on the simulation page243if (bSimulationPage)244{245HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_SIMULATION);246}247else248{249HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_GENERIC);250}251HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, AbsInfo.min);252HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, AbsInfo.max);253
254// for simplicity always use 4-byte fields in the report255HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x20);256HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01);257
258HIDAppend2(pHidDesc, HID_TAG_USAGE, uAxisCode);259HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE);260
261// add mapping for this axis262HIDJoystickAxisMapAppend(&AxisMap, (EV_ABS << 16) | uAbsCode, pJoystickDesc->cbAxisLen);263pJoystickDesc->cbAxisLen += 4;264uNumOfAbsAxes++;265}266else267{268uNonAxes |= (1 << uValue);269}270}271pAxes->u.bitmap[i] = uNonAxes;272}273
274// finalize the axis map275HIDJoystickAxisMapAppend(&AxisMap, (ULONG)-1, (ULONG)-1);276pJoystickDesc->pAxisMap = DynamicArrayGet(&AxisMap, NULL);277if (pJoystickDesc->pAxisMap == NULL)278{279status = STATUS_INSUFFICIENT_RESOURCES;280goto Exit;281}282
283// one bit per button284cbButtonBytes = (uNumOfButtons + 7) / 8;285HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_BUTTON);286HIDAppend2(pHidDesc, HID_TAG_USAGE_MINIMUM, 0x01);287HIDAppend2(pHidDesc, HID_TAG_USAGE_MAXIMUM, uNumOfButtons);288HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, 0x00);289HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, 0x01);290HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, uNumOfButtons);291HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x01);292HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE);293
294// pad to the nearest whole byte295if (uNumOfButtons % 8)296{297HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01);298HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, (ULONG)(cbButtonBytes * 8) - uNumOfButtons);299HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE | HID_DATA_FLAG_CONSTANT);300}301
302HIDAppend1(pHidDesc, HID_TAG_END_COLLECTION);303
304TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT,305"Created HID joystick report descriptor with %d axes and %d buttons\n",306uNumOfAbsAxes,307uNumOfButtons);308
309// calculate the joystick HID report size310pJoystickDesc->Common.cbHidReportSize =3111ll + // report ID312uNumOfAbsAxes * 4ll + // axes313cbButtonBytes; // buttons314
315// register the joystick class316status = RegisterClass(pContext, &pJoystickDesc->Common);317
318Exit:319DynamicArrayDestroy(&AxisMap);320if (!NT_SUCCESS(status) && pJoystickDesc != NULL)321{322pJoystickDesc->Common.CleanupFunc(&pJoystickDesc->Common);323VIOInputFree(&pJoystickDesc);324}325
326TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s (%08x)\n", __FUNCTION__, status);327return status;328}
329