mirror of
https://github.com/arehnman/yttrium-virtio-gpu.git
synced 2026-11-07 02:06:32 +00:00
The project is switching from GPL to BSD. Signed-off-by: Ladi Prosek <[email protected]>
281 lines
9.0 KiB
C
281 lines
9.0 KiB
C
/*
|
|
* Tablet specific HID functionality
|
|
*
|
|
* Copyright (c) 2016-2017 Red Hat, Inc.
|
|
*
|
|
* Author(s):
|
|
* Ladi Prosek <[email protected]>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met :
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and / or other materials provided with the distribution.
|
|
* 3. Neither the names of the copyright holders nor the names of their contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#include "vioinput.h"
|
|
#include "Hid.h"
|
|
|
|
#if defined(EVENT_TRACING)
|
|
#include "HidTablet.tmh"
|
|
#endif
|
|
|
|
typedef struct _tagInputClassTablet
|
|
{
|
|
INPUT_CLASS_COMMON Common;
|
|
|
|
// the tablet HID report is laid out as follows:
|
|
// offset 0
|
|
// * report ID
|
|
// offset 1
|
|
// * switches and flags
|
|
// ** bit 0 tip switch
|
|
// ** bit 1 in range
|
|
// ** bit 2 barrel switch
|
|
// ** bits 3-7 padding
|
|
// offset 2
|
|
// * X axis, 2 bytes, absolute
|
|
// offset 4
|
|
// * Y axis, 2 bytes, absolute
|
|
|
|
} INPUT_CLASS_TABLET, *PINPUT_CLASS_TABLET;
|
|
|
|
static NTSTATUS
|
|
HIDTabletEventToReport(
|
|
PINPUT_CLASS_COMMON pClass,
|
|
PVIRTIO_INPUT_EVENT pEvent)
|
|
{
|
|
PUCHAR pReport = pClass->pHidReport;
|
|
PUSHORT pAxisReport;
|
|
UCHAR uBits;
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);
|
|
|
|
pReport[HID_REPORT_ID_OFFSET] = pClass->uReportID;
|
|
switch (pEvent->type)
|
|
{
|
|
case EV_ABS:
|
|
switch (pEvent->code)
|
|
{
|
|
case ABS_X:
|
|
pAxisReport = (USHORT *)&pReport[HID_REPORT_DATA_OFFSET + 1];
|
|
break;
|
|
case ABS_Y:
|
|
pAxisReport = (USHORT *)&pReport[HID_REPORT_DATA_OFFSET + 3];
|
|
break;
|
|
default:
|
|
pAxisReport = NULL;
|
|
break;
|
|
}
|
|
if (pAxisReport != NULL)
|
|
{
|
|
*pAxisReport = (USHORT)pEvent->value;
|
|
pClass->bDirty = TRUE;
|
|
}
|
|
break;
|
|
case EV_KEY:
|
|
switch (pEvent->code)
|
|
{
|
|
case BTN_LEFT:
|
|
case BTN_TOUCH:
|
|
// tip switch + in range
|
|
uBits = 0x03;
|
|
break;
|
|
case BTN_RIGHT:
|
|
case BTN_STYLUS:
|
|
// barrel switch
|
|
uBits = 0x04;
|
|
break;
|
|
default:
|
|
uBits = 0x00;
|
|
break;
|
|
}
|
|
if (uBits)
|
|
{
|
|
if (pEvent->value)
|
|
{
|
|
pReport[HID_REPORT_DATA_OFFSET] |= uBits;
|
|
}
|
|
else
|
|
{
|
|
pReport[HID_REPORT_DATA_OFFSET] &= ~uBits;
|
|
}
|
|
pClass->bDirty = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- %s\n", __FUNCTION__);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
HIDTabletProbe(
|
|
PINPUT_DEVICE pContext,
|
|
PDYNAMIC_ARRAY pHidDesc,
|
|
PVIRTIO_INPUT_CFG_DATA pAxes,
|
|
PVIRTIO_INPUT_CFG_DATA pButtons)
|
|
{
|
|
PINPUT_CLASS_TABLET pTabletDesc = NULL;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
UCHAR i, uValue;
|
|
ULONG uAxisCode, uNumOfAbsAxes = 0;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
|
|
|
|
// we expect to see two absolute axes, X and Y
|
|
for (i = 0; i < pAxes->size; i++)
|
|
{
|
|
UCHAR uNonAxes = 0;
|
|
while (DecodeNextBit(&pAxes->u.bitmap[i], &uValue))
|
|
{
|
|
USHORT uAxisCode = uValue + 8 * i;
|
|
if (uAxisCode == ABS_X || uAxisCode == ABS_Y)
|
|
{
|
|
uNumOfAbsAxes++;
|
|
}
|
|
else
|
|
{
|
|
uNonAxes |= (1 << uValue);
|
|
}
|
|
}
|
|
pAxes->u.bitmap[i] = uNonAxes;
|
|
}
|
|
|
|
if (uNumOfAbsAxes != 2)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Tablet axes not found\n");
|
|
goto Exit;
|
|
}
|
|
|
|
// claim our buttons from the pAxes bitmap
|
|
for (i = 0; i < pButtons->size; i++)
|
|
{
|
|
UCHAR uNonButtons = 0;
|
|
while (DecodeNextBit(&pAxes->u.bitmap[i], &uValue))
|
|
{
|
|
USHORT uAxisCode = uValue + 8 * i;
|
|
// a few hard-coded buttons we understand
|
|
switch (uAxisCode)
|
|
{
|
|
case BTN_LEFT:
|
|
case BTN_RIGHT:
|
|
case BTN_TOUCH:
|
|
case BTN_STYLUS:
|
|
break;
|
|
default:
|
|
uNonButtons |= (1 << uValue);
|
|
}
|
|
}
|
|
pAxes->u.bitmap[i] = uNonButtons;
|
|
}
|
|
|
|
// allocate and initialize pTabletDesc
|
|
pTabletDesc = VIOInputAlloc(sizeof(INPUT_CLASS_TABLET));
|
|
if (pTabletDesc == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
pTabletDesc->Common.EventToReportFunc = HIDTabletEventToReport;
|
|
pTabletDesc->Common.uReportID = (UCHAR)(pContext->uNumOfClasses + 1);
|
|
|
|
HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_DIGITIZER);
|
|
HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_DIGITIZER);
|
|
HIDAppend2(pHidDesc, HID_TAG_COLLECTION, HID_COLLECTION_APPLICATION);
|
|
HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_DIGITIZER_STYLUS);
|
|
HIDAppend2(pHidDesc, HID_TAG_COLLECTION, HID_COLLECTION_PHYSICAL);
|
|
|
|
HIDAppend2(pHidDesc, HID_TAG_REPORT_ID, pTabletDesc->Common.uReportID);
|
|
|
|
// tip switch, one bit
|
|
HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_DIGITIZER_TIP_SWITCH);
|
|
HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, 0x00);
|
|
HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, 0x01);
|
|
HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x01);
|
|
HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01);
|
|
HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE);
|
|
|
|
// in range flag, one bit
|
|
HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_DIGITIZER_IN_RANGE);
|
|
HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE);
|
|
|
|
// barrel switch, one bit
|
|
HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_DIGITIZER_BARREL_SWITCH);
|
|
HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE);
|
|
|
|
// padding
|
|
HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x05);
|
|
HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE | HID_DATA_FLAG_CONSTANT);
|
|
|
|
HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_GENERIC);
|
|
HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_GENERIC_POINTER);
|
|
|
|
HIDAppend2(pHidDesc, HID_TAG_COLLECTION, HID_COLLECTION_PHYSICAL);
|
|
for (uAxisCode = ABS_X; uAxisCode <= ABS_Y; uAxisCode++)
|
|
{
|
|
struct virtio_input_absinfo AbsInfo;
|
|
GetAbsAxisInfo(pContext, uAxisCode, &AbsInfo);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got abs axis %d, min %d, max %d\n",
|
|
uAxisCode, AbsInfo.min, AbsInfo.max);
|
|
|
|
HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, AbsInfo.min);
|
|
HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, AbsInfo.max);
|
|
|
|
HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x10);
|
|
HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01);
|
|
HIDAppend2(pHidDesc, HID_TAG_UNIT_EXPONENT, 0x0D); // -3
|
|
HIDAppend2(pHidDesc, HID_TAG_UNIT, 0x00); // none
|
|
|
|
HIDAppend2(pHidDesc, HID_TAG_USAGE,
|
|
(uAxisCode == ABS_X ? HID_USAGE_GENERIC_X : HID_USAGE_GENERIC_Y));
|
|
|
|
HIDAppend2(pHidDesc, HID_TAG_PHYSICAL_MINIMUM, AbsInfo.min);
|
|
HIDAppend2(pHidDesc, HID_TAG_PHYSICAL_MAXIMUM, AbsInfo.max);
|
|
HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE);
|
|
}
|
|
HIDAppend1(pHidDesc, HID_TAG_END_COLLECTION);
|
|
|
|
HIDAppend1(pHidDesc, HID_TAG_END_COLLECTION);
|
|
HIDAppend1(pHidDesc, HID_TAG_END_COLLECTION);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Created HID tablet report descriptor\n");
|
|
|
|
// calculate the tablet HID report size
|
|
pTabletDesc->Common.cbHidReportSize =
|
|
1 + // report ID
|
|
1 + // flags
|
|
uNumOfAbsAxes * 2; // axes
|
|
|
|
// register the tablet class
|
|
status = RegisterClass(pContext, &pTabletDesc->Common);
|
|
|
|
Exit:
|
|
if (!NT_SUCCESS(status) && pTabletDesc != NULL)
|
|
{
|
|
VIOInputFree(&pTabletDesc);
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s (%08x)\n", __FUNCTION__, status);
|
|
return status;
|
|
}
|