kvm-guest-drivers-windows
563 строки · 21.6 Кб
1/*
2* Mouse 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 "HidMouse.tmh"39#endif40
41// initial length of the axis map (will grow as needed)
42#define AXIS_MAP_INITIAL_LENGTH 443
44typedef struct _tagInputClassMouse45{
46INPUT_CLASS_COMMON Common;47
48// the mouse HID report is laid out as follows:49// offset 050// * report ID51// offset 152// * buttons; one bit per button followed by padding to the nearest whole byte53// offset cbAxisOffset54// * axes; one byte per axis, mapping in pAxisMap55// offset cbAxisOffset + cbAxisLen56// * vertical wheel; one byte, if uFlags & CLASS_MOUSE_HAS_V_WHEEL57// * horizontal wheel; one byte, if uFlags & CLASS_MOUSE_HAS_H_WHEEL58
59// number of buttons supported by the HID report60ULONG uNumOfButtons;61// offset of axis data within the HID report62SIZE_T cbAxisOffset;63// length of axis data64SIZE_T cbAxisLen;65// mapping from EVDEV axis codes to HID axis offsets66PULONG pAxisMap;67// flags68#define CLASS_MOUSE_HAS_V_WHEEL 0x0169#define CLASS_MOUSE_HAS_H_WHEEL 0x0270#define CLASS_MOUSE_SUPPORTS_REL_WHEEL 0x0471#define CLASS_MOUSE_SUPPORTS_REL_HWHEEL 0x0872#define CLASS_MOUSE_SUPPORTS_REL_DIAL 0x1073ULONG uFlags;74} INPUT_CLASS_MOUSE, *PINPUT_CLASS_MOUSE;75
76static UCHAR FORCEINLINE TrimRelative(long val)77{
78if (val < -127) {79return (UCHAR)(-127);80} else if (val > 127) {81return 127;82}83return (UCHAR)val;84}
85
86static NTSTATUS87HIDMouseEventToReport(88PINPUT_CLASS_COMMON pClass,89PVIRTIO_INPUT_EVENT pEvent)90{
91PUCHAR pReport = pClass->pHidReport;92PINPUT_CLASS_MOUSE pMouseDesc = (PINPUT_CLASS_MOUSE)pClass;93PULONG pMap;94
95TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> %s\n", __FUNCTION__);96
97pReport[HID_REPORT_ID_OFFSET] = pClass->uReportID;98switch (pEvent->type)99{100#ifdef EXPOSE_ABS_AXES_WITH_BUTTONS_AS_MOUSE101case EV_ABS:102#endif // EXPOSE_ABS_AXES_WITH_BUTTONS_AS_MOUSE103case EV_REL:104if (pMouseDesc->pAxisMap)105{106for (pMap = pMouseDesc->pAxisMap; pMap[0] != (ULONG)-1; pMap += 2)107{108// axis map handles regular relative axes as well as wheels109if (pMap[0] == ((pEvent->type << 16) | pEvent->code))110{111#ifdef EXPOSE_ABS_AXES_WITH_BUTTONS_AS_MOUSE112if (pEvent->type == EV_ABS)113{114// 2 bytes per absolute axis115PUSHORT pAxisPtr = (PUSHORT)&pReport[pMouseDesc->cbAxisOffset + pMap[1]];116*pAxisPtr = (USHORT)pEvent->value;117}118else119#endif // EXPOSE_ABS_AXES_WITH_BUTTONS_AS_MOUSE120{121pReport[pMouseDesc->cbAxisOffset + pMap[1]] = TrimRelative((long)pEvent->value);122}123pClass->bDirty = TRUE;124break;125}126}127}128break;129
130case EV_KEY:131if (pEvent->code == BTN_GEAR_DOWN || pEvent->code == BTN_GEAR_UP)132{133if (pEvent->value && (pMouseDesc->uFlags & CLASS_MOUSE_HAS_V_WHEEL))134{135// increment/decrement the vertical wheel field136CHAR delta = (pEvent->code == BTN_GEAR_DOWN ? -1 : 1);137pReport[pMouseDesc->cbAxisOffset + pMouseDesc->cbAxisLen] += delta;138pClass->bDirty = TRUE;139}140}141else if (pEvent->code >= BTN_MOUSE && pEvent->code < BTN_JOYSTICK)142{143USHORT uButton = pEvent->code - BTN_MOUSE;144if (uButton < pMouseDesc->uNumOfButtons)145{146USHORT uOffset = uButton / 8;147UCHAR uBit = 1 << (uButton % 8);148if (pEvent->value)149{150pReport[HID_REPORT_DATA_OFFSET + uOffset] |= uBit;151}152else153{154pReport[HID_REPORT_DATA_OFFSET + uOffset] &= ~uBit;155}156pClass->bDirty = TRUE;157}158}159break;160
161case EV_SYN:162// reset all relative axes and wheel to 0, buttons stay unchanged163for (pMap = pMouseDesc->pAxisMap; pMap[0] != (ULONG)-1; pMap += 2)164{165if ((pMap[0] >> 16) == EV_REL)166{167pReport[pMouseDesc->cbAxisOffset + pMap[1]] = 0;168}169}170if ((pMouseDesc->uFlags & (CLASS_MOUSE_HAS_V_WHEEL | CLASS_MOUSE_SUPPORTS_REL_WHEEL))171== CLASS_MOUSE_HAS_V_WHEEL)172{173// button-based vertical wheel174pReport[pMouseDesc->cbAxisOffset + pMouseDesc->cbAxisLen] = 0;175}176break;177}178
179TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- %s\n", __FUNCTION__);180return STATUS_SUCCESS;181}
182
183static VOID184HIDMouseCleanup(185PINPUT_CLASS_COMMON pClass)186{
187TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);188
189PINPUT_CLASS_MOUSE pMouseDesc = (PINPUT_CLASS_MOUSE)pClass;190VIOInputFree(&pMouseDesc->pAxisMap);191
192TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__);193}
194
195static VOID196HIDMouseAxisMapAppend(197PDYNAMIC_ARRAY pAxisMap,198ULONG uCode,199SIZE_T uAxisIndex)200{
201DynamicArrayAppend(pAxisMap, &uCode, sizeof(ULONG));202DynamicArrayAppend(pAxisMap, &uAxisIndex, sizeof(ULONG));203}
204
205static VOID206HIDMouseDescribeWheel(207PDYNAMIC_ARRAY pHidDesc,208ULONG uUsagePage,209ULONG uWheelType)210{
211TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);212
213HIDAppend2(pHidDesc, HID_TAG_COLLECTION, HID_COLLECTION_LOGICAL);214
215// resolution multiplier feature (appears to be mandatory for wheel support)216HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_GENERIC_RESOLUTION_MULTIPLIER);217HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, 0x00);218HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, 0x01);219HIDAppend2(pHidDesc, HID_TAG_PHYSICAL_MINIMUM, 0x01);220HIDAppend2(pHidDesc, HID_TAG_PHYSICAL_MAXIMUM, 0x04);221HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x02);222HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01);223HIDAppend2(pHidDesc, HID_TAG_FEATURE, HID_DATA_FLAG_VARIABLE);224
225// the wheel itself226if (uUsagePage != 0)227{228HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, uUsagePage);229}230HIDAppend2(pHidDesc, HID_TAG_USAGE, uWheelType);231HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, -127);232HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, 127);233HIDAppend2(pHidDesc, HID_TAG_PHYSICAL_MINIMUM, 0x00);234HIDAppend2(pHidDesc, HID_TAG_PHYSICAL_MAXIMUM, 0x00);235HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x08);236HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01);237HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE | HID_DATA_FLAG_RELATIVE);238
239HIDAppend1(pHidDesc, HID_TAG_END_COLLECTION);240
241TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__);242}
243
244NTSTATUS
245HIDMouseProbe(246PINPUT_DEVICE pContext,247PDYNAMIC_ARRAY pHidDesc,248PVIRTIO_INPUT_CFG_DATA pRelAxes,249PVIRTIO_INPUT_CFG_DATA pAbsAxes,250PVIRTIO_INPUT_CFG_DATA pButtons)251{
252PINPUT_CLASS_MOUSE pMouseDesc = NULL;253NTSTATUS status = STATUS_SUCCESS;254UCHAR i, uValue;255ULONG uFeatureBitsUsed = 0, uNumOfRelAxes = 0, uNumOfAbsAxes = 0;256BOOLEAN bHasRelXY;257SIZE_T cbFeatureBytes = 0, cbButtonBytes;258SIZE_T cbInitialHidSize = pHidDesc->Size;259DYNAMIC_ARRAY AxisMap = { NULL };260
261TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);262
263// allocate and initialize pMouseDesc264pMouseDesc = VIOInputAlloc(sizeof(INPUT_CLASS_MOUSE));265if (pMouseDesc == NULL)266{267status = STATUS_INSUFFICIENT_RESOURCES;268goto Exit;269}270pMouseDesc->Common.EventToReportFunc = HIDMouseEventToReport;271pMouseDesc->Common.CleanupFunc = HIDMouseCleanup;272pMouseDesc->Common.uReportID = (UCHAR)(pContext->uNumOfClasses + 1);273
274HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_GENERIC);275HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_GENERIC_MOUSE);276HIDAppend2(pHidDesc, HID_TAG_COLLECTION, HID_COLLECTION_APPLICATION);277HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_GENERIC_POINTER);278HIDAppend2(pHidDesc, HID_TAG_COLLECTION, HID_COLLECTION_PHYSICAL);279
280HIDAppend2(pHidDesc, HID_TAG_REPORT_ID, pMouseDesc->Common.uReportID);281
282for (i = 0; i < pButtons->size; i++)283{284unsigned char non_buttons = 0;285while (DecodeNextBit(&pButtons->u.bitmap[i], &uValue))286{287ULONG uButtonCode = uValue + 8 * i;288if (uButtonCode >= BTN_MOUSE && uButtonCode < BTN_JOYSTICK)289{290// individual mouse button functions are not specified in the HID report,291// only their count is; the max button code we find will determine the count292TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got button %d\n", uButtonCode);293pMouseDesc->uNumOfButtons = max(pMouseDesc->uNumOfButtons, uButtonCode - BTN_MOUSE + 1);294}295else if (uButtonCode == BTN_GEAR_DOWN || uButtonCode == BTN_GEAR_UP)296{297// wheel is modeled as a pair of buttons in evdev but it's an axis in HID298TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got button-based vertical wheel\n");299pMouseDesc->uFlags |= CLASS_MOUSE_HAS_V_WHEEL;300}301else302{303// not a mouse button, put it back and let other devices claim it304TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got non-button key %d\n", uButtonCode);305non_buttons |= (1 << uValue);306}307}308pButtons->u.bitmap[i] = non_buttons;309}310
311cbButtonBytes = (pMouseDesc->uNumOfButtons + 7) / 8;312pMouseDesc->cbAxisOffset = HID_REPORT_DATA_OFFSET + cbButtonBytes;313if (pMouseDesc->uNumOfButtons > 0)314{315// one bit per button as usual in a mouse device316HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_BUTTON);317HIDAppend2(pHidDesc, HID_TAG_USAGE_MINIMUM, 0x01);318HIDAppend2(pHidDesc, HID_TAG_USAGE_MAXIMUM, pMouseDesc->uNumOfButtons);319HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, 0x00);320HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, 0x01);321HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, pMouseDesc->uNumOfButtons);322HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x01);323HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE);324
325// pad to the nearest whole byte326if (pMouseDesc->uNumOfButtons % 8)327{328HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01);329HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, (ULONG)(cbButtonBytes * 8) - pMouseDesc->uNumOfButtons);330HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE | HID_DATA_FLAG_CONSTANT);331}332}333
334// describe axes335HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_GENERIC);336DynamicArrayReserve(&AxisMap, AXIS_MAP_INITIAL_LENGTH * 2 * sizeof(ULONG));337
338bHasRelXY = InputCfgDataHasBit(pRelAxes, REL_X) && InputCfgDataHasBit(pRelAxes, REL_Y);339// Windows won't drive a mouse without at least the X and Y relative axes340if (bHasRelXY341#ifdef EXPOSE_ABS_AXES_WITH_BUTTONS_AS_MOUSE342|| (pMouseDesc->uNumOfButtons > 0 && InputCfgDataHasBit(pAbsAxes, ABS_X) && InputCfgDataHasBit(pAbsAxes, ABS_Y))343#endif // EXPOSE_ABS_AXES_WITH_BUTTONS_AS_MOUSE344)345{346for (i = 0; i < pRelAxes->size; i++)347{348while (DecodeNextBit(&pRelAxes->u.bitmap[i], &uValue))349{350ULONG uRelCode = uValue + 8 * i;351ULONG uAxisCode = 0;352switch (uRelCode)353{354case REL_X: uAxisCode = bHasRelXY ? HID_USAGE_GENERIC_X : 0; break;355case REL_Y: uAxisCode = bHasRelXY ? HID_USAGE_GENERIC_Y : 0; break;356case REL_Z: uAxisCode = HID_USAGE_GENERIC_Z; break;357case REL_RX: uAxisCode = HID_USAGE_GENERIC_RX; break;358case REL_RY: uAxisCode = HID_USAGE_GENERIC_RY; break;359case REL_RZ: uAxisCode = HID_USAGE_GENERIC_RZ; break;360case REL_WHEEL:361{362TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got axis-based vertical wheel\n");363pMouseDesc->uFlags |= CLASS_MOUSE_SUPPORTS_REL_WHEEL;364pMouseDesc->uFlags |= CLASS_MOUSE_HAS_V_WHEEL;365break;366}367case REL_DIAL:368{369TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got axis-based horizontal wheel (REL_DIAL)\n");370pMouseDesc->uFlags |= CLASS_MOUSE_SUPPORTS_REL_DIAL;371pMouseDesc->uFlags |= CLASS_MOUSE_HAS_H_WHEEL;372break;373}374case REL_HWHEEL:375{376TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got axis-based horizontal wheel (REL_HWHEEL)\n");377pMouseDesc->uFlags |= CLASS_MOUSE_SUPPORTS_REL_HWHEEL;378pMouseDesc->uFlags |= CLASS_MOUSE_HAS_H_WHEEL;379break;380}381default: uAxisCode = HID_USAGE_GENERIC_SLIDER; break;382}383
384if (uAxisCode != 0)385{386TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got rel axis %d\n", uAxisCode);387HIDAppend2(pHidDesc, HID_TAG_USAGE, uAxisCode);388
389// add mapping for this axis390HIDMouseAxisMapAppend(&AxisMap, (EV_REL << 16) | uRelCode, pMouseDesc->cbAxisLen);391pMouseDesc->cbAxisLen++;392uNumOfRelAxes++;393}394}395}396}397if (uNumOfRelAxes > 0)398{399// the range is -127 to 127 (one byte) on all axes400HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, -127);401HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, 127);402
403HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x08);404HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, uNumOfRelAxes);405
406HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE | HID_DATA_FLAG_RELATIVE);407}408
409#ifdef EXPOSE_ABS_AXES_WITH_BUTTONS_AS_MOUSE410if (!bHasRelXY &&411pMouseDesc->uNumOfButtons > 0 &&412InputCfgDataHasBit(pAbsAxes, ABS_X) && InputCfgDataHasBit(pAbsAxes, ABS_Y))413{414for (i = 0; i < pAbsAxes->size; i++)415{416while (DecodeNextBit(&pAbsAxes->u.bitmap[i], &uValue))417{418ULONG uAbsCode = uValue + 8 * i;419ULONG uAxisCode = 0;420switch (uAbsCode)421{422case ABS_X: uAxisCode = HID_USAGE_GENERIC_X; break;423case ABS_Y: uAxisCode = HID_USAGE_GENERIC_Y; break;424case ABS_Z: uAxisCode = HID_USAGE_GENERIC_Z; break;425case ABS_RX: uAxisCode = HID_USAGE_GENERIC_RX; break;426case ABS_RY: uAxisCode = HID_USAGE_GENERIC_RY; break;427case ABS_RZ: uAxisCode = HID_USAGE_GENERIC_RZ; break;428case ABS_WHEEL: uAxisCode = HID_USAGE_GENERIC_WHEEL; break;429}430
431if (uAxisCode != 0)432{433struct virtio_input_absinfo AbsInfo;434GetAbsAxisInfo(pContext, uAbsCode, &AbsInfo);435
436TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got abs axis %d, min %d, max %d\n",437uAxisCode, AbsInfo.min, AbsInfo.max);438HIDAppend2(pHidDesc, HID_TAG_USAGE, uAxisCode);439
440// we specify the minimum and maximum per-axis441HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, AbsInfo.min);442HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, AbsInfo.max);443if (AbsInfo.min != 0 || AbsInfo.max != MAXSHORT)444{445HIDAppend2(pHidDesc, HID_TAG_PHYSICAL_MINIMUM, 0);446HIDAppend2(pHidDesc, HID_TAG_PHYSICAL_MAXIMUM, 0x7fff);447}448
449HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x10); // 2 bytes450HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01);451
452HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE);453
454// add mapping for this axis455HIDMouseAxisMapAppend(&AxisMap, (EV_ABS << 16) | uAbsCode, pMouseDesc->cbAxisLen);456pMouseDesc->cbAxisLen += 2;457uNumOfAbsAxes++;458}459}460}461}462#endif // EXPOSE_ABS_AXES_WITH_BUTTONS_AS_MOUSE463
464if (uNumOfRelAxes == 0 && uNumOfAbsAxes == 0)465{466// this is not a mouse467TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "No mouse axis found\n");468VIOInputFree(&pMouseDesc);469pHidDesc->Size = cbInitialHidSize;470goto Exit;471}472
473// if we have detected axis-based wheel support, add mapping for those too474if (pMouseDesc->uFlags & CLASS_MOUSE_SUPPORTS_REL_WHEEL)475{476HIDMouseAxisMapAppend(&AxisMap, (EV_REL << 16) | REL_WHEEL, pMouseDesc->cbAxisLen);477}478if ((pMouseDesc->uFlags & CLASS_MOUSE_SUPPORTS_REL_HWHEEL) ||479(pMouseDesc->uFlags & CLASS_MOUSE_SUPPORTS_REL_DIAL))480{481SIZE_T cbOffset = pMouseDesc->cbAxisLen;482if (pMouseDesc->uFlags & CLASS_MOUSE_HAS_V_WHEEL)483{484// horizontal wheel field follows the vertical wheel field485cbOffset++;486}487if (pMouseDesc->uFlags & CLASS_MOUSE_SUPPORTS_REL_HWHEEL)488{489HIDMouseAxisMapAppend(&AxisMap, (EV_REL << 16) | REL_HWHEEL, cbOffset);490}491if (pMouseDesc->uFlags & CLASS_MOUSE_SUPPORTS_REL_DIAL)492{493HIDMouseAxisMapAppend(&AxisMap, (EV_REL << 16) | REL_DIAL, cbOffset);494}495}496
497// finalize the axis map498HIDMouseAxisMapAppend(&AxisMap, (ULONG)-1, (ULONG)-1);499pMouseDesc->pAxisMap = DynamicArrayGet(&AxisMap, NULL);500if (pMouseDesc->pAxisMap == NULL)501{502status = STATUS_INSUFFICIENT_RESOURCES;503goto Exit;504}505
506if (pMouseDesc->uFlags & CLASS_MOUSE_HAS_V_WHEEL)507{508// vertical mouse wheel509HIDMouseDescribeWheel(pHidDesc, 0, HID_USAGE_GENERIC_WHEEL);510uFeatureBitsUsed += 2;511}512
513if (pMouseDesc->uFlags & CLASS_MOUSE_HAS_H_WHEEL)514{515// horizontal mouse wheel516HIDMouseDescribeWheel(pHidDesc, HID_USAGE_PAGE_CONSUMER, HID_USAGE_CONSUMER_AC_PAN);517uFeatureBitsUsed += 2;518}519
520// feature padding521if (uFeatureBitsUsed % 8)522{523cbFeatureBytes = (uFeatureBitsUsed + 7) / 8;524HIDAppend2(pHidDesc, HID_TAG_PHYSICAL_MINIMUM, 0x00);525HIDAppend2(pHidDesc, HID_TAG_PHYSICAL_MAXIMUM, 0x00);526HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, (ULONG)(cbFeatureBytes * 8) - uFeatureBitsUsed);527HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01);528HIDAppend2(pHidDesc, HID_TAG_FEATURE, HID_DATA_FLAG_VARIABLE | HID_DATA_FLAG_CONSTANT);529}530
531// close all collections532HIDAppend1(pHidDesc, HID_TAG_END_COLLECTION);533HIDAppend1(pHidDesc, HID_TAG_END_COLLECTION);534
535TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT,536"Created HID mouse report descriptor with %d buttons, %d rel axes, %d abs axes, vwheel %s, hwheel %s\n",537pMouseDesc->uNumOfButtons,538uNumOfRelAxes,539uNumOfAbsAxes,540(pMouseDesc->uFlags & CLASS_MOUSE_HAS_V_WHEEL) ? "YES" : "NO",541(pMouseDesc->uFlags & CLASS_MOUSE_HAS_H_WHEEL) ? "YES" : "NO");542
543// calculate the mouse HID report size544pMouseDesc->Common.cbHidReportSize =545pMouseDesc->cbAxisOffset +546pMouseDesc->cbAxisLen +547((pMouseDesc->uFlags & CLASS_MOUSE_HAS_V_WHEEL) ? 1 : 0) +548((pMouseDesc->uFlags & CLASS_MOUSE_HAS_H_WHEEL) ? 1 : 0);549
550// register the joystick class551status = RegisterClass(pContext, &pMouseDesc->Common);552
553Exit:554DynamicArrayDestroy(&AxisMap);555if (!NT_SUCCESS(status) && pMouseDesc != NULL)556{557pMouseDesc->Common.CleanupFunc(&pMouseDesc->Common);558VIOInputFree(&pMouseDesc);559}560
561TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s (%08x)\n", __FUNCTION__, status);562return status;563}
564