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]>
658 lines
19 KiB
C
658 lines
19 KiB
C
/*
|
|
* HID related 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 "Hid.tmh"
|
|
#endif
|
|
|
|
|
|
//
|
|
// HID descriptor based on this structure is returned by the mini driver in response
|
|
// to IOCTL_HID_GET_DEVICE_DESCRIPTOR.
|
|
//
|
|
static const HID_DESCRIPTOR G_DefaultHidDescriptor =
|
|
{
|
|
0x09, // length of HID descriptor
|
|
0x21, // descriptor type == HID 0x21
|
|
0x0100, // hid spec release
|
|
0x00, // country code == Not Specified
|
|
0x01, // number of HID class descriptors
|
|
{ // DescriptorList[0]
|
|
0x22, // report descriptor type 0x22
|
|
0x0000 // total length of report descriptor
|
|
}
|
|
};
|
|
|
|
VOID
|
|
EvtIoDeviceControl(
|
|
WDFQUEUE Queue,
|
|
WDFREQUEST Request,
|
|
size_t OutputBufferLength,
|
|
size_t InputBufferLength,
|
|
ULONG IoControlCode)
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN completeRequest = TRUE;
|
|
WDFDEVICE device = WdfIoQueueGetDevice(Queue);
|
|
PINPUT_DEVICE pContext = GetDeviceContext(device);
|
|
ULONG uReportSize;
|
|
HID_XFER_PACKET Packet;
|
|
WDF_REQUEST_PARAMETERS params;
|
|
UNREFERENCED_PARAMETER(OutputBufferLength);
|
|
UNREFERENCED_PARAMETER(InputBufferLength);
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS,
|
|
"--> %s, code = %d\n", __FUNCTION__, IoControlCode);
|
|
|
|
switch (IoControlCode)
|
|
{
|
|
case IOCTL_HID_GET_DEVICE_DESCRIPTOR:
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "IOCTL_HID_GET_DEVICE_DESCRIPTOR\n");
|
|
//
|
|
// Return the device's HID descriptor.
|
|
//
|
|
ASSERT(pContext->HidDescriptor.bLength != 0);
|
|
status = RequestCopyFromBuffer(Request,
|
|
&pContext->HidDescriptor,
|
|
pContext->HidDescriptor.bLength);
|
|
break;
|
|
|
|
case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
|
|
//
|
|
// Return the device's attributes in a HID_DEVICE_ATTRIBUTES structure.
|
|
//
|
|
status = RequestCopyFromBuffer(Request,
|
|
&pContext->HidDeviceAttributes,
|
|
sizeof(HID_DEVICE_ATTRIBUTES));
|
|
break;
|
|
|
|
case IOCTL_HID_GET_REPORT_DESCRIPTOR:
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "IOCTL_HID_GET_REPORT_DESCRIPTOR\n");
|
|
//
|
|
// Return the report descriptor for the HID device.
|
|
//
|
|
status = RequestCopyFromBuffer(Request,
|
|
pContext->HidReportDescriptor,
|
|
pContext->HidDescriptor.DescriptorList[0].wReportLength);
|
|
break;
|
|
|
|
case IOCTL_HID_READ_REPORT:
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "IOCTL_HID_READ_REPORT\n");
|
|
//
|
|
// Queue up a report request. We'll complete it when we actually
|
|
// receive data from the device.
|
|
//
|
|
|
|
status = WdfRequestForwardToIoQueue(
|
|
Request,
|
|
pContext->HidQueue);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
completeRequest = FALSE;
|
|
}
|
|
else
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTLS,
|
|
"WdfRequestForwardToIoQueue failed with 0x%x\n", status);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_HID_WRITE_REPORT:
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "IOCTL_HID_WRITE_REPORT\n");
|
|
//
|
|
// Write a report to the device, commonly used for controlling keyboard
|
|
// LEDs. We'll complete the request after the host processes all virtio
|
|
// buffers we add to the status queue.
|
|
//
|
|
|
|
WDF_REQUEST_PARAMETERS_INIT(¶ms);
|
|
WdfRequestGetParameters(Request, ¶ms);
|
|
|
|
if (params.Parameters.DeviceIoControl.InputBufferLength < sizeof(HID_XFER_PACKET))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(&Packet, WdfRequestWdmGetIrp(Request)->UserBuffer,
|
|
sizeof(HID_XFER_PACKET));
|
|
WdfRequestSetInformation(Request, Packet.reportBufferLen);
|
|
|
|
status = ProcessOutputReport(pContext, Request, &Packet);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
completeRequest = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
|
|
"Unrecognized IOCTL %d\n", IoControlCode);
|
|
status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
|
|
if (completeRequest)
|
|
{
|
|
WdfRequestComplete(Request, status);
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "<-- %s\n", __FUNCTION__);
|
|
}
|
|
|
|
static VOID
|
|
CompleteHIDQueueRequest(
|
|
PINPUT_DEVICE pContext,
|
|
PINPUT_CLASS_COMMON pClass)
|
|
{
|
|
WDFREQUEST request;
|
|
NTSTATUS status;
|
|
|
|
if (!pClass->bDirty)
|
|
{
|
|
// nothing to do
|
|
return;
|
|
}
|
|
|
|
status = WdfIoQueueRetrieveNextRequest(pContext->HidQueue, &request);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = RequestCopyFromBuffer(
|
|
request,
|
|
pClass->pHidReport,
|
|
pClass->cbHidReportSize);
|
|
WdfRequestComplete(request, status);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
pClass->bDirty = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ProcessInputEvent(
|
|
PINPUT_DEVICE pContext,
|
|
PVIRTIO_INPUT_EVENT pEvent)
|
|
{
|
|
ULONG i;
|
|
|
|
TraceEvents(
|
|
TRACE_LEVEL_VERBOSE,
|
|
DBG_READ,
|
|
"--> %s TYPE: %d, CODE: %d, VALUE: %d\n",
|
|
__FUNCTION__,
|
|
pEvent->type,
|
|
pEvent->code,
|
|
pEvent->value);
|
|
|
|
if (pEvent->type == EV_SYN)
|
|
{
|
|
// send report(s) up
|
|
for (i = 0; i < pContext->uNumOfClasses; i++)
|
|
{
|
|
CompleteHIDQueueRequest(pContext, pContext->InputClasses[i]);
|
|
}
|
|
}
|
|
|
|
// ask each class to translate the input event into a HID report
|
|
for (i = 0; i < pContext->uNumOfClasses; i++)
|
|
{
|
|
pContext->InputClasses[i]->EventToReportFunc(
|
|
pContext->InputClasses[i],
|
|
pEvent);
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- %s\n", __FUNCTION__);
|
|
}
|
|
|
|
NTSTATUS
|
|
ProcessOutputReport(
|
|
PINPUT_DEVICE pContext,
|
|
WDFREQUEST Request,
|
|
PHID_XFER_PACKET pPacket)
|
|
{
|
|
NTSTATUS status = STATUS_NONE_MAPPED;
|
|
ULONG i;
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "--> %s\n", __FUNCTION__);
|
|
|
|
for (i = 0; i < pContext->uNumOfClasses; i++)
|
|
{
|
|
PINPUT_CLASS_COMMON pClass = pContext->InputClasses[i];
|
|
if (pClass->uReportID == pPacket->reportId)
|
|
{
|
|
if (pClass->ReportToEventFunc)
|
|
{
|
|
status = pClass->ReportToEventFunc(
|
|
pClass,
|
|
pContext,
|
|
Request,
|
|
pPacket->reportBuffer,
|
|
pPacket->reportBufferLen);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
VOID HIDAppend1(PDYNAMIC_ARRAY pArray, UCHAR tag)
|
|
{
|
|
DynamicArrayAppend(pArray, &tag, sizeof(tag));
|
|
}
|
|
|
|
VOID HIDAppend2(PDYNAMIC_ARRAY pArray, UCHAR tag, LONG value)
|
|
{
|
|
if (value >= -MINCHAR && value <= MAXCHAR)
|
|
{
|
|
HIDAppend1(pArray, tag | 0x01);
|
|
DynamicArrayAppend(pArray, &value, 1);
|
|
}
|
|
else if (value >= -MINSHORT && value <= MAXSHORT)
|
|
{
|
|
HIDAppend1(pArray, tag | 0x02);
|
|
DynamicArrayAppend(pArray, &value, 2);
|
|
}
|
|
else
|
|
{
|
|
HIDAppend1(pArray, tag | 0x03);
|
|
DynamicArrayAppend(pArray, &value, 4);
|
|
}
|
|
}
|
|
|
|
BOOLEAN DecodeNextBit(PUCHAR pBitmap, PUCHAR pValue)
|
|
{
|
|
ULONG Index;
|
|
BOOLEAN bResult = BitScanForward(&Index, *pBitmap);
|
|
if (bResult)
|
|
{
|
|
*pValue = (unsigned char)Index;
|
|
*pBitmap &= ~(1 << Index);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
static void DumpReportDescriptor(PHID_REPORT_DESCRIPTOR pDescriptor, SIZE_T cbSize)
|
|
{
|
|
SIZE_T i = 0;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "HID report descriptor begin\n");
|
|
while (i < cbSize)
|
|
{
|
|
CHAR buffer[20];
|
|
switch (pDescriptor[i] & 0x03)
|
|
{
|
|
case 0x00:
|
|
{
|
|
sprintf_s(buffer, sizeof(buffer), "%02x", pDescriptor[i]);
|
|
i++;
|
|
break;
|
|
}
|
|
case 0x01:
|
|
{
|
|
sprintf_s(buffer, sizeof(buffer), "%02x %02x",
|
|
pDescriptor[i], pDescriptor[i + 1]);
|
|
i += 2;
|
|
break;
|
|
}
|
|
case 0x02:
|
|
{
|
|
sprintf_s(buffer, sizeof(buffer), "%02x %02x %02x",
|
|
pDescriptor[i], pDescriptor[i + 1], pDescriptor[i + 2]);
|
|
i += 3;
|
|
break;
|
|
}
|
|
case 0x03:
|
|
{
|
|
sprintf_s(buffer, sizeof(buffer), "%02x %02x %02x %02x %02x",
|
|
pDescriptor[i], pDescriptor[i + 1], pDescriptor[i + 2],
|
|
pDescriptor[i + 3], pDescriptor[i + 4]);
|
|
i += 5;
|
|
break;
|
|
}
|
|
}
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "%s\n", buffer);
|
|
}
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "HID report descriptor end\n");
|
|
}
|
|
|
|
static u8 SelectInputConfig(PINPUT_DEVICE pContext, u8 cfgSelect, u8 cfgSubSel)
|
|
{
|
|
u8 size = 0;
|
|
|
|
VirtIOWdfDeviceSet(
|
|
&pContext->VDevice, offsetof(struct virtio_input_config, select),
|
|
&cfgSelect, sizeof(cfgSelect));
|
|
VirtIOWdfDeviceSet(
|
|
&pContext->VDevice, offsetof(struct virtio_input_config, subsel),
|
|
&cfgSubSel, sizeof(cfgSubSel));
|
|
|
|
VirtIOWdfDeviceGet(
|
|
&pContext->VDevice, offsetof(struct virtio_input_config, size),
|
|
&size, sizeof(size));
|
|
return size;
|
|
}
|
|
|
|
BOOLEAN InputCfgDataHasBit(PVIRTIO_INPUT_CFG_DATA pData, ULONG bit)
|
|
{
|
|
if (pData->size <= (bit / 8))
|
|
{
|
|
return FALSE;
|
|
}
|
|
return (pData->u.bitmap[bit / 8] & (1 << (bit % 8)));
|
|
}
|
|
|
|
static BOOLEAN InputCfgDataEmpty(PVIRTIO_INPUT_CFG_DATA pCfgData)
|
|
{
|
|
UCHAR i;
|
|
for (i = 0; i < pCfgData->size; i++)
|
|
{
|
|
if (pCfgData->u.bitmap[i] != 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static UINT64 ComputeHash(PUCHAR pData, SIZE_T Length)
|
|
{
|
|
UINT64 hash = 0;
|
|
SIZE_T i;
|
|
|
|
for (i = 0; i < Length / sizeof(UINT64); i++)
|
|
{
|
|
hash ^= *(PUINT64)pData;
|
|
pData += sizeof(UINT64);
|
|
}
|
|
for (i = 0; i < (Length & 7); i++)
|
|
{
|
|
hash ^= (UINT64)*pData << (i * sizeof(UCHAR));
|
|
pData++;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
NTSTATUS
|
|
VIOInputBuildReportDescriptor(PINPUT_DEVICE pContext)
|
|
{
|
|
DYNAMIC_ARRAY ReportDescriptor = { NULL };
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
VIRTIO_INPUT_CFG_DATA KeyData, RelData, AbsData, LedData;
|
|
SIZE_T cbReportDescriptor;
|
|
UCHAR i, uReportID = 0;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
|
|
|
|
// key/button config
|
|
KeyData.size = SelectInputConfig(pContext, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY);
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got EV_KEY bits size %d\n", KeyData.size);
|
|
for (i = 0; i < KeyData.size; i++)
|
|
{
|
|
VirtIOWdfDeviceGet(
|
|
&pContext->VDevice, offsetof(struct virtio_input_config, u.bitmap[i]),
|
|
&KeyData.u.bitmap[i], 1);
|
|
}
|
|
|
|
// relative axis config
|
|
RelData.size = SelectInputConfig(pContext, VIRTIO_INPUT_CFG_EV_BITS, EV_REL);
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got EV_REL bits size %d\n", RelData.size);
|
|
for (i = 0; i < RelData.size; i++)
|
|
{
|
|
VirtIOWdfDeviceGet(
|
|
&pContext->VDevice, offsetof(struct virtio_input_config, u.bitmap[i]),
|
|
&RelData.u.bitmap[i], 1);
|
|
}
|
|
|
|
// absolute axis config
|
|
AbsData.size = SelectInputConfig(pContext, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS);
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got EV_ABS bits size %d\n", AbsData.size);
|
|
for (i = 0; i < AbsData.size; i++)
|
|
{
|
|
VirtIOWdfDeviceGet(
|
|
&pContext->VDevice, offsetof(struct virtio_input_config, u.bitmap[i]),
|
|
&AbsData.u.bitmap[i], 1);
|
|
}
|
|
|
|
// if we have any relative axes, we'll expose a mouse device
|
|
// if we have any absolute axes, we may expose a mouse as well
|
|
if (!InputCfgDataEmpty(&RelData) || !InputCfgDataEmpty(&AbsData))
|
|
{
|
|
status = HIDMouseProbe(
|
|
pContext,
|
|
&ReportDescriptor,
|
|
&RelData,
|
|
&AbsData,
|
|
&KeyData);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// if we have any absolute axes left, we may expose a joystick
|
|
if (!InputCfgDataEmpty(&AbsData))
|
|
{
|
|
status = HIDJoystickProbe(
|
|
pContext,
|
|
&ReportDescriptor,
|
|
&AbsData,
|
|
&KeyData);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// if we have any absolute axes left, we'll expose a tablet device
|
|
if (!InputCfgDataEmpty(&AbsData))
|
|
{
|
|
status = HIDTabletProbe(
|
|
pContext,
|
|
&ReportDescriptor,
|
|
&AbsData,
|
|
&KeyData);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// if we have any keys left, we'll expose a keyboard device
|
|
if (!InputCfgDataEmpty(&KeyData))
|
|
{
|
|
// LED config
|
|
LedData.size = SelectInputConfig(pContext, VIRTIO_INPUT_CFG_EV_BITS, EV_LED);
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got EV_LED bits size %d\n", LedData.size);
|
|
for (i = 0; i < LedData.size; i++)
|
|
{
|
|
VirtIOWdfDeviceGet(
|
|
&pContext->VDevice, offsetof(struct virtio_input_config, u.bitmap[i]),
|
|
&LedData.u.bitmap[i], 1);
|
|
}
|
|
|
|
status = HIDKeyboardProbe(
|
|
pContext,
|
|
&ReportDescriptor,
|
|
&KeyData,
|
|
&LedData);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// if we still have any keys left, we'll check for a consumer device
|
|
if (!InputCfgDataEmpty(&KeyData))
|
|
{
|
|
status = HIDConsumerProbe(
|
|
pContext,
|
|
&ReportDescriptor,
|
|
&KeyData);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (DynamicArrayIsEmpty(&ReportDescriptor))
|
|
{
|
|
// we are not exposing any device
|
|
pContext->HidReportDescriptor = NULL;
|
|
}
|
|
else
|
|
{
|
|
// initialize the HID descriptor
|
|
pContext->HidReportDescriptor = (PHID_REPORT_DESCRIPTOR)DynamicArrayGet(
|
|
&ReportDescriptor,
|
|
&cbReportDescriptor);
|
|
if (!pContext->HidReportDescriptor)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
pContext->HidDescriptor = G_DefaultHidDescriptor;
|
|
pContext->HidDescriptor.DescriptorList[0].wReportLength = (USHORT)cbReportDescriptor;
|
|
|
|
pContext->HidReportDescriptorHash = ComputeHash(pContext->HidReportDescriptor, cbReportDescriptor);
|
|
|
|
DumpReportDescriptor(pContext->HidReportDescriptor, cbReportDescriptor);
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s (%08x)\n", __FUNCTION__, status);
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
GetAbsAxisInfo(
|
|
PINPUT_DEVICE pContext,
|
|
ULONG uAbsAxis,
|
|
struct virtio_input_absinfo *pAbsInfo)
|
|
{
|
|
UCHAR uSize, i;
|
|
|
|
RtlZeroMemory(pAbsInfo, sizeof(struct virtio_input_absinfo));
|
|
if (uAbsAxis <= MAXUCHAR)
|
|
{
|
|
uSize = SelectInputConfig(pContext, VIRTIO_INPUT_CFG_ABS_INFO, (UCHAR)uAbsAxis);
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got abs axis %d info size %d\n", uAbsAxis, uSize);
|
|
for (i = 0; i < uSize && i < sizeof(struct virtio_input_absinfo); i++)
|
|
{
|
|
VirtIOWdfDeviceGet(
|
|
&pContext->VDevice, offsetof(struct virtio_input_config, u.bitmap[i]),
|
|
(PUCHAR)pAbsInfo + i, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
RegisterClass(
|
|
PINPUT_DEVICE pContext,
|
|
PINPUT_CLASS_COMMON pClass)
|
|
{
|
|
if (pContext->uNumOfClasses >= MAX_INPUT_CLASS_COUNT)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// allocate HID report buffer
|
|
if (pClass->pHidReport == NULL)
|
|
{
|
|
pClass->pHidReport = VIOInputAlloc(pClass->cbHidReportSize);
|
|
if (pClass->pHidReport == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
// insert the class into our array
|
|
pContext->InputClasses[pContext->uNumOfClasses++] = pClass;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
RequestCopyFromBuffer(
|
|
WDFREQUEST Request,
|
|
PVOID SourceBuffer,
|
|
size_t NumBytesToCopyFrom)
|
|
{
|
|
NTSTATUS status;
|
|
WDFMEMORY memory;
|
|
size_t outputBufferLength;
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);
|
|
|
|
status = WdfRequestRetrieveOutputMemory(Request, &memory);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
WdfMemoryGetBuffer(memory, &outputBufferLength);
|
|
if (outputBufferLength < NumBytesToCopyFrom)
|
|
{
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,
|
|
"RequestCopyFromBuffer: buffer too small. Size %d, expect %d\n",
|
|
(int)outputBufferLength, (int)NumBytesToCopyFrom);
|
|
return status;
|
|
}
|
|
|
|
status = WdfMemoryCopyFromBuffer(memory,
|
|
0,
|
|
SourceBuffer,
|
|
NumBytesToCopyFrom);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,
|
|
"WdfMemoryCopyFromBuffer failed 0x%x\n", status);
|
|
return status;
|
|
}
|
|
|
|
WdfRequestSetInformation(Request, NumBytesToCopyFrom);
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|