hyperv: Add support for mouse device

This change adds driver support for the Hyper-V HID mouse (fixes #16665 and #18882).

Change-Id: I45ee1f3c2fad10f07080d7966184856bd0a22f61
Reviewed-on: https://review.haiku-os.org/c/haiku/+/10334
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: waddlesplash <[email protected]>
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
John Davis
2026-03-06 15:53:52 +00:00
committed by Adrien Destugues
parent 767d7cf9fa
commit a3eda7cbda
8 changed files with 1007 additions and 0 deletions
+1
View File
@@ -144,6 +144,7 @@ AddNewDriversToPackage network :
virtio_net
;
AddNewDriversToPackage input :
hyperv_hid@x86,x86_64
i2c_elan
virtio_input
;
+1
View File
@@ -1,5 +1,6 @@
SubDir HAIKU_TOP src add-ons kernel drivers input ;
SubInclude HAIKU_TOP src add-ons kernel drivers input hyperv_hid ;
SubInclude HAIKU_TOP src add-ons kernel drivers input i2c_elan ;
SubInclude HAIKU_TOP src add-ons kernel drivers input i2c_hid ;
SubInclude HAIKU_TOP src add-ons kernel drivers input ps2_hid ;
@@ -0,0 +1,355 @@
/*
* Copyright 2026 John Davis. All rights reserved.
* Copyright 2020, Jérôme Duval, [email protected].
* Copyright 2008-2011 Michael Lotz <[email protected]>
* Distributed under the terms of the MIT License.
*/
#include "DeviceList.h"
#include "Driver.h"
#include "HIDDevice.h"
#include "ProtocolHandler.h"
#include <lock.h>
#include <util/AutoLock.h>
typedef struct {
device_node* node;
hyperv_device_interface* hyperv;
hyperv_device hyperv_cookie;
HIDDevice* hid_device;
} hid_driver_cookie;
typedef struct {
ProtocolHandler* handler;
uint32 cookie;
hid_driver_cookie* driver_cookie;
} device_cookie;
static device_manager_info* sDeviceManager;
DeviceList* gDeviceList = NULL;
static mutex sDriverLock;
static status_t
hyperv_hid_init_device(void* driverCookie, void** _deviceCookie)
{
CALLED();
*_deviceCookie = driverCookie;
return B_OK;
}
static void
hyperv_hid_uninit_device(void* deviceCookie)
{
CALLED();
}
static status_t
hyperv_hid_open(void* deviceCookie, const char* path, int openMode, void** _cookie)
{
CALLED();
device_cookie* cookie = new(std::nothrow) device_cookie();
if (cookie == NULL)
return B_NO_MEMORY;
cookie->driver_cookie = reinterpret_cast<hid_driver_cookie*>(deviceCookie);
MutexLocker locker(sDriverLock);
ProtocolHandler* handler = reinterpret_cast<ProtocolHandler*>(gDeviceList->FindDevice(path));
if (handler == NULL) {
delete cookie;
return B_ENTRY_NOT_FOUND;
}
cookie->handler = handler;
cookie->cookie = 0;
status_t status = handler->Open(openMode, &cookie->cookie);
if (status != B_OK) {
delete cookie;
return status;
}
*_cookie = cookie;
return B_OK;
}
static status_t
hyperv_hid_close(void* cookie)
{
CALLED();
device_cookie* deviceCookie = reinterpret_cast<device_cookie*>(cookie);
return deviceCookie->handler->Close(&deviceCookie->cookie);
}
static status_t
hyperv_hid_free(void* cookie)
{
CALLED();
device_cookie* deviceCookie = reinterpret_cast<device_cookie*>(cookie);
mutex_lock(&sDriverLock);
HIDDevice* device = deviceCookie->handler->Device();
if (device->IsOpen()) {
// Another handler of this device is still open so we can't free it
} else if (device->IsRemoved()) {
// The parent device is removed already and none of its handlers are
// open anymore so we can free it here
delete device;
}
mutex_unlock(&sDriverLock);
delete deviceCookie;
return B_OK;
}
static status_t
hyperv_hid_read(void* cookie, off_t pos, void* buffer, size_t* _length)
{
CALLED();
device_cookie* deviceCookie = reinterpret_cast<device_cookie*>(cookie);
return deviceCookie->handler->Read(&deviceCookie->cookie, pos, buffer, _length);
}
static status_t
hyperv_hid_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
{
CALLED();
device_cookie* deviceCookie = reinterpret_cast<device_cookie*>(cookie);
return deviceCookie->handler->Write(&deviceCookie->cookie, pos, buffer, _length);
}
static status_t
hyperv_hid_control(void* cookie, uint32 op, void* buffer, size_t length)
{
CALLED();
device_cookie* deviceCookie = reinterpret_cast<device_cookie*>(cookie);
return deviceCookie->handler->Control(&deviceCookie->cookie, op, buffer, length);
}
static float
hyperv_hid_supports_device(device_node* parent)
{
CALLED();
// Check if parent is the Hyper-V bus manager
const char* bus;
if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) != B_OK)
return -1;
if (strcmp(bus, HYPERV_BUS_NAME) != 0)
return 0.0f;
// Check if parent is a Hyper-V Input device
const char* type;
if (sDeviceManager->get_attr_string(parent, HYPERV_DEVICE_TYPE_ITEM, &type, false) != B_OK)
return 0.0f;
if (strcmp(type, VMBUS_TYPE_INPUT) != 0)
return 0.0f;
TRACE("Hyper-V HID device found!\n");
return 0.8f;
}
static status_t
hyperv_hid_register_device(device_node* parent)
{
CALLED();
device_attr attributes[] = {
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
{ .string = HYPERV_PRETTYNAME_INPUT }},
{ NULL }
};
return sDeviceManager->register_node(parent, HYPERV_HID_DRIVER_MODULE_NAME, attributes, NULL,
NULL);
}
static status_t
hyperv_hid_init_driver(device_node* node, void** _driverCookie)
{
CALLED();
hid_driver_cookie* hidCookie = new(std::nothrow) hid_driver_cookie;
if (hidCookie == NULL)
return B_NO_MEMORY;
memset(hidCookie, 0, sizeof(*hidCookie));
hidCookie->node = node;
device_node* parent = sDeviceManager->get_parent_node(node);
sDeviceManager->get_driver(parent, (driver_module_info**)&hidCookie->hyperv,
(void**)&hidCookie->hyperv_cookie);
sDeviceManager->put_node(parent);
mutex_lock(&sDriverLock);
HIDDevice* hidDevice = new(std::nothrow) HIDDevice(hidCookie->hyperv,
hidCookie->hyperv_cookie);
if (hidDevice == NULL) {
mutex_unlock(&sDriverLock);
delete hidCookie;
return B_NO_MEMORY;
}
status_t status = hidDevice->InitCheck();
if (status != B_OK) {
mutex_unlock(&sDriverLock);
delete hidDevice;
delete hidCookie;
return status;
}
hidCookie->hid_device = hidDevice;
mutex_unlock(&sDriverLock);
*_driverCookie = hidCookie;
return B_OK;
}
static void
hyperv_hid_uninit_driver(void* driverCookie)
{
CALLED();
hid_driver_cookie* hidCookie = reinterpret_cast<hid_driver_cookie*>(driverCookie);
delete hidCookie;
}
static status_t
hyperv_hid_register_child_devices(void* driverCookie)
{
CALLED();
hid_driver_cookie* hidCookie = reinterpret_cast<hid_driver_cookie*>(driverCookie);
HIDDevice* hidDevice = hidCookie->hid_device;
for (uint32 i = 0;; i++) {
ProtocolHandler* handler = hidDevice->ProtocolHandlerAt(i);
if (handler == NULL)
break;
// As devices can be un- and replugged at will, we cannot
// simply rely on a device count. If there is just one
// keyboard, this does not mean that it uses the 0 name.
// There might have been two keyboards and the one using 0
// might have been unplugged. So we just generate names
// until we find one that is not currently in use.
int32 index = 0;
char pathBuffer[B_DEV_NAME_LENGTH];
const char* basePath = handler->BasePath();
while (true) {
sprintf(pathBuffer, "%s%" B_PRId32, basePath, index++);
if (gDeviceList->FindDevice(pathBuffer) == NULL) {
// Name is still free
handler->SetPublishPath(strdup(pathBuffer));
break;
}
}
gDeviceList->AddDevice(handler->PublishPath(), handler);
sDeviceManager->publish_device(hidCookie->node, pathBuffer,
HYPERV_HID_DEVICE_MODULE_NAME);
}
return B_OK;
}
static status_t
std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
gDeviceList = new(std::nothrow) DeviceList();
if (gDeviceList == NULL)
return B_NO_MEMORY;
mutex_init(&sDriverLock, "hyper-v hid driver lock");
return B_OK;
case B_MODULE_UNINIT:
delete gDeviceList;
gDeviceList = NULL;
mutex_destroy(&sDriverLock);
return B_OK;
default:
break;
}
return B_ERROR;
}
static device_module_info sHyperVHIDDeviceModule = {
{
HYPERV_HID_DEVICE_MODULE_NAME,
0,
NULL
},
hyperv_hid_init_device,
hyperv_hid_uninit_device,
NULL, // remove
hyperv_hid_open,
hyperv_hid_close,
hyperv_hid_free,
hyperv_hid_read,
hyperv_hid_write,
NULL, // io
hyperv_hid_control,
NULL, // select
NULL // deselect
};
static driver_module_info sHyperVHIDDriverModule = {
{
HYPERV_HID_DRIVER_MODULE_NAME,
0,
&std_ops
},
hyperv_hid_supports_device,
hyperv_hid_register_device,
hyperv_hid_init_driver,
hyperv_hid_uninit_driver,
hyperv_hid_register_child_devices,
NULL, // rescan
NULL // removed
};
module_dependency module_dependencies[] = {
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager },
{ NULL }
};
module_info* modules[] = {
(module_info*)&sHyperVHIDDriverModule,
(module_info*)&sHyperVHIDDeviceModule,
NULL
};
@@ -0,0 +1,31 @@
/*
* Copyright 2026 John Davis. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _HYPERV_HID_DRIVER_H_
#define _HYPERV_HID_DRIVER_H_
#include <Drivers.h>
#include <KernelExport.h>
#include <OS.h>
#include <util/kernel_cpp.h>
#define DRIVER_NAME "hyperv_hid"
#define DEVICE_PATH_SUFFIX "hyperv"
#define DEVICE_NAME "Hyper-V HID"
//#define TRACE_HYPERV_HID
#ifdef TRACE_HYPERV_HID
# define TRACE(x...) dprintf("\33[94mhyperv_hid:\33[0m " x)
#else
# define TRACE(x...) ;
#endif
#define TRACE_ALWAYS(x...) dprintf("\33[94mhyperv_hid:\33[0m " x)
#define ERROR(x...) dprintf("\33[94mhyperv_hid:\33[0m " x)
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
#define HYPERV_HID_DRIVER_MODULE_NAME "drivers/input/hyperv_hid/driver_v1"
#define HYPERV_HID_DEVICE_MODULE_NAME "drivers/input/hyperv_hid/device_v1"
#endif // _HYPERV_HID_DRIVER_H_
@@ -0,0 +1,356 @@
/*
* Copyright 2026 John Davis. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "Driver.h"
#include "HIDCollection.h"
#include "HIDDevice.h"
#include "HIDReport.h"
#include "HIDReportItem.h"
#include "HIDWriter.h"
#include "ProtocolHandler.h"
#include <usb/USB_hid.h>
HIDDevice::HIDDevice(hyperv_device_interface* hyperv,
hyperv_device hyperv_cookie)
:
fStatus(B_NO_INIT),
fOpenCount(0),
fRemoved(false),
fParser(this),
fProtocolHandlerCount(0),
fProtocolHandlerList(NULL),
fHyperV(hyperv),
fHyperVCookie(hyperv_cookie),
fDeviceInfo(NULL),
fDeviceInfoLength(0),
fPacket(NULL),
fLastX(0),
fLastY(0)
{
CALLED();
fProtocolRespEvent.Init(this, "hyper-v hid protoresp");
fDeviceInfoEvent.Init(this, "hyper-v hid devinfo");
fPacket = malloc(HV_HID_RX_PKT_BUFFER_SIZE);
if (fPacket == NULL) {
fStatus = B_NO_MEMORY;
return;
}
fStatus = _Connect();
if (fStatus != B_OK) {
ERROR("Failed to connect to Hyper-V HID\n");
return;
}
ProtocolHandler::AddHandlers(*this, fProtocolHandlerList, fProtocolHandlerCount);
}
HIDDevice::~HIDDevice()
{
CALLED();
ProtocolHandler* handler = fProtocolHandlerList;
while (handler != NULL) {
ProtocolHandler* next = handler->NextHandler();
delete handler;
handler = next;
}
free(fPacket);
}
status_t
HIDDevice::Open(ProtocolHandler* handler, uint32 flags)
{
atomic_add(&fOpenCount, 1);
return B_OK;
}
status_t
HIDDevice::Close(ProtocolHandler* handler)
{
atomic_add(&fOpenCount, -1);
return B_OK;
}
void
HIDDevice::Removed()
{
fRemoved = true;
}
status_t
HIDDevice::MaybeScheduleTransfer(HIDReport* report)
{
if (fRemoved)
return ENODEV;
// Reports get sent as they become available, cannot query on-demand
return B_OK;
}
status_t
HIDDevice::SendReport(HIDReport* report)
{
// Hyper-V does not support reports to itself
return B_NOT_SUPPORTED;
}
ProtocolHandler*
HIDDevice::ProtocolHandlerAt(uint32 index) const
{
ProtocolHandler* handler = fProtocolHandlerList;
while (handler != NULL) {
if (index == 0)
return handler;
handler = handler->NextHandler();
index--;
}
return NULL;
}
/*static*/ void
HIDDevice::_CallbackHandler(void* data)
{
HIDDevice* hidDevice = reinterpret_cast<HIDDevice*>(data);
hidDevice->_Callback();
}
void
HIDDevice::_Callback()
{
while (true) {
vmbus_pkt_header header;
uint32 headerLength = sizeof(header);
uint32 packetLength = HV_HID_RX_PKT_BUFFER_SIZE;
status_t status = fHyperV->read_packet(fHyperVCookie, &header, &headerLength,
fPacket, &packetLength);
if (status == B_DEV_NOT_READY) {
break;
} else if (status != B_OK) {
ERROR("Failed to read packet (%s)\n", strerror(status));
break;
}
// Check if this is an HID pipe data message
hv_hid_pipe_in_msg* message = reinterpret_cast<hv_hid_pipe_in_msg*>(fPacket);
if (message->pipe_header.type != HV_HID_PIPE_MSGTYPE_DATA) {
ERROR("Non-data HID pipe message type %u received\n", message->pipe_header.type);
continue;
}
switch (message->header.type) {
case HV_HID_MSGTYPE_PROTOCOL_RESPONSE:
memcpy(&fProtocolResponse, &message->protocol_resp, sizeof(fProtocolResponse));
fProtocolRespEvent.NotifyAll();
break;
case HV_HID_MSGTYPE_INITIAL_DEV_INFO:
if (fDeviceInfo != NULL)
free(fDeviceInfo);
fDeviceInfo = reinterpret_cast<hv_hid_msg_initial_dev_info*>(
malloc(message->header.length));
if (fDeviceInfo != NULL) {
fDeviceInfoLength = message->header.length;
memcpy(fDeviceInfo, &message->dev_info, fDeviceInfoLength);
fDeviceInfoEvent.NotifyAll();
} else {
fDeviceInfoLength = 0;
ERROR("Failed to allocate device info\n");
fDeviceInfoEvent.NotifyAll(B_NO_MEMORY);
}
break;
case HV_HID_MSGTYPE_INPUT_REPORT:
_HandleInputReport(&message->input_report);
break;
default:
TRACE("Unexpected HID message type %u received\n", message->header.type);
break;
}
}
}
void
HIDDevice::_HandleInputReport(hv_hid_msg_input_report* reportMessage)
{
if (reportMessage->header.length < sizeof(hyperv_hid_input_report))
return;
hyperv_hid_input_report* report
= reinterpret_cast<hyperv_hid_input_report*>(reportMessage->data);
TRACE("New input report (btn %u x %u y %u wheel %d)\n", report->buttons, report->x, report->y,
report->wheel);
// Some versions of Hyper-V report an X/Y of zero when the wheel is scrolling,
// use the last reported X/Y
if (report->wheel != 0 && report->x == 0 && report->y == 0) {
report->x = fLastX;
report->y = fLastY;
TRACE("Fixed input report x %u y %u\n", report->x, report->y);
} else {
fLastX = report->x;
fLastY = report->y;
}
fParser.SetReport(B_OK, reportMessage->data, reportMessage->header.length);
}
status_t
HIDDevice::_Connect()
{
status_t status = fHyperV->open(fHyperVCookie, HV_HID_RING_SIZE, HV_HID_RING_SIZE,
_CallbackHandler, this);
if (status != B_OK) {
ERROR("Failed to open channel\n");
return status;
}
hv_hid_pipe_out_msg message;
message.pipe_header.type = HV_HID_PIPE_MSGTYPE_DATA;
message.pipe_header.length = sizeof(message.protocol_req);
message.header.type = HV_HID_MSGTYPE_PROTOCOL_REQUEST;
message.header.length = message.pipe_header.length - sizeof(message.header);
message.protocol_req.version = HV_HID_VERSION_V2_0;
ConditionVariableEntry protocolRespEntry;
ConditionVariableEntry deviceInfoEntry;
fProtocolRespEvent.Add(&protocolRespEntry);
fDeviceInfoEvent.Add(&deviceInfoEntry);
// Send the protocol request message and wait
// Initial device info is received immediately after protocol response
status = fHyperV->write_packet(fHyperVCookie, VMBUS_PKTTYPE_DATA_INBAND, &message,
sizeof(message.pipe_header) + message.pipe_header.length, TRUE, HV_HID_REQUEST_TRANS_ID);
if (status != B_OK) {
ERROR("Failed to send HID protocol request\n");
return status;
}
status = protocolRespEntry.Wait(B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, HV_HID_TIMEOUT_US);
if (status != B_OK)
return status;
TRACE("HID protocol version %u.%u status %u\n",
GET_HID_VERSION_MAJOR(fProtocolResponse.version),
GET_HID_VERSION_MINOR(fProtocolResponse.version), fProtocolResponse.result);
status = deviceInfoEntry.Wait(B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, HV_HID_TIMEOUT_US);
if (status != B_OK)
return status;
message.pipe_header.type = HV_HID_PIPE_MSGTYPE_DATA;
message.pipe_header.length = sizeof(message.dev_info_ack);
message.header.type = HV_HID_MSGTYPE_INITIAL_DEV_INFO_ACK;
message.header.length = message.pipe_header.length - sizeof(message.header);
message.dev_info_ack.reserved = 0;
// Send device info acknowledgement to Hyper-V
status = fHyperV->write_packet(fHyperVCookie, VMBUS_PKTTYPE_DATA_INBAND, &message,
sizeof(message.pipe_header) + message.pipe_header.length, FALSE, HV_HID_REQUEST_TRANS_ID);
if (status != B_OK) {
ERROR("Failed to send HID device info ack\n");
return status;
}
TRACE("Hyper-V HID vid 0x%04X pid 0x%04X version 0x%X\n", fDeviceInfo->info.vendor_id,
fDeviceInfo->info.product_id, fDeviceInfo->info.version);
status = fParser.ParseReportDescriptor(fDeviceInfo->descriptor_data,
fDeviceInfo->descriptor.hid_descriptor_length);
free(fDeviceInfo);
fDeviceInfo = NULL;
HIDReport* report = fParser.ReportAt(HID_REPORT_TYPE_INPUT, 0);
if (report == NULL)
return B_ERROR;
#ifdef TRACE_HYPERV_HID
report->PrintToStream();
#endif
// Check report for wheel item, if not present add a wheel item
HIDReportItem* wheelItem = report->FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP,
B_HID_UID_GD_WHEEL);
if (wheelItem == NULL) {
TRACE("No wheel found in HID report descriptor, adding\n");
status = _AddWheelHIDItem(report);
if (status != B_OK)
ERROR("Failed to add wheel device to HID descriptor (%s)\n", strerror(status));
}
return B_OK;
}
status_t
HIDDevice::_AddWheelHIDItem(HIDReport* report)
{
HIDCollection* rootCollection = fParser.RootCollection();
if (rootCollection == NULL)
return B_ERROR;
HIDCollection* mouseCollection = NULL;
for (uint32 i = 0; i < rootCollection->CountChildrenFlat(COLLECTION_APPLICATION); i++) {
HIDCollection* collection = rootCollection->ChildAtFlat(COLLECTION_APPLICATION, i);
if (collection != NULL && (collection->UsageID() == B_HID_UID_GD_MOUSE
|| collection->UsageID() == B_HID_UID_GD_POINTER)) {
mouseCollection = collection;
break;
}
}
if (mouseCollection == NULL)
return B_ERROR;
global_item_state globalState;
memset(&globalState, 0, sizeof(global_item_state));
local_item_state localState;
memset(&localState, 0, sizeof(local_item_state));
main_item_data_converter converter;
converter.flat_data = 0;
converter.main_data.array_variable = 1;
globalState.logical_minimum = -127;
globalState.logical_maximum = 127;
globalState.report_count = 1;
globalState.report_size = 8;
globalState.usage_page = B_HID_USAGE_PAGE_GENERIC_DESKTOP;
usage_value usageValue;
usageValue.u.s.usage_page = B_HID_USAGE_PAGE_GENERIC_DESKTOP;
usageValue.u.s.usage_id = B_HID_UID_GD_WHEEL;
localState.usage_stack_used = 1;
localState.usage_stack = &usageValue;
report->AddMainItem(globalState, localState, converter.main_data, mouseCollection);
#ifdef TRACE_HYPERV_HID
report->PrintToStream();
#endif
return B_OK;
}
@@ -0,0 +1,77 @@
/*
* Copyright 2026 John Davis. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _HYPERV_HID_DEVICE_H_
#define _HYPERV_HID_DEVICE_H_
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <condition_variable.h>
#include <device_manager.h>
#include <kernel.h>
#include <lock.h>
#include <fs/devfs.h>
#include <hyperv.h>
#include "HIDParser.h"
#include "HyperVHIDProtocol.h"
class ProtocolHandler;
class HIDDevice {
public:
HIDDevice(hyperv_device_interface* hyperv,
hyperv_device hyperv_cookie);
~HIDDevice();
status_t InitCheck() const { return fStatus; }
bool IsOpen() const { return fOpenCount > 0; }
status_t Open(ProtocolHandler* handler, uint32 flags);
status_t Close(ProtocolHandler* handler);
int32 OpenCount() const { return fOpenCount; }
void Removed();
bool IsRemoved() const { return fRemoved; }
status_t MaybeScheduleTransfer(HIDReport* report);
status_t SendReport(HIDReport* report);
HIDParser& Parser() { return fParser; }
ProtocolHandler* ProtocolHandlerAt(uint32 index) const;
private:
static void _CallbackHandler(void* data);
void _Callback();
void _HandleInputReport(hv_hid_msg_input_report* reportMessage);
status_t _Connect();
status_t _AddWheelHIDItem(HIDReport* report);
private:
status_t fStatus;
int32 fOpenCount;
bool fRemoved;
HIDParser fParser;
uint32 fProtocolHandlerCount;
ProtocolHandler* fProtocolHandlerList;
hyperv_device_interface* fHyperV;
hyperv_device fHyperVCookie;
hv_hid_msg_protocol_response fProtocolResponse;
hv_hid_msg_initial_dev_info* fDeviceInfo;
uint32 fDeviceInfoLength;
ConditionVariable fProtocolRespEvent;
ConditionVariable fDeviceInfoEvent;
void* fPacket;
uint16 fLastX;
uint16 fLastY;
};
#endif // _HYPERV_HID_DEVICE_H_
@@ -0,0 +1,159 @@
/*
* Copyright 2026 John Davis. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _HYPERV_HID_PROTOCOL_H_
#define _HYPERV_HID_PROTOCOL_H_
#include <hyperv_spec.h>
#define HV_HID_RING_SIZE 0x8000
#define HV_HID_RX_PKT_BUFFER_SIZE 256
#define HV_HID_REQUEST_TRANS_ID 0xCAFECAFE
#define HV_HID_TIMEOUT_US HV_MS_TO_US(5000)
typedef struct {
uint8 length;
uint8 type;
uint16 version;
uint8 country_code;
uint8 num_descriptors;
uint8 hid_descriptor_type;
uint16 hid_descriptor_length;
} _PACKED hv_hid_descriptor;
typedef struct {
uint32 length;
uint16 vendor_id;
uint16 product_id;
uint16 version;
uint16 reserved[11];
} _PACKED hv_hid_dev_info;
// Hyper-V HID version
#define MAKE_HID_VERSION(major, minor) ((((major) << 16) & 0xFFFF0000) | ((minor) & 0x0000FFFF))
#define GET_HID_VERSION_MAJOR(version) (((version) >> 16) & 0xFFFF)
#define GET_HID_VERSION_MINOR(version) ((version) & 0xFFFF)
// HID protocol version 2.0 used in Server 2008 and newer.
#define HV_HID_VERSION_V2_0 MAKE_HID_VERSION(2, 0)
// HID pipe message types
enum {
HV_HID_PIPE_MSGTYPE_INVALID = 0,
HV_HID_PIPE_MSGTYPE_DATA = 1
};
// HID message types
enum {
HV_HID_MSGTYPE_PROTOCOL_REQUEST = 0,
HV_HID_MSGTYPE_PROTOCOL_RESPONSE = 1,
HV_HID_MSGTYPE_INITIAL_DEV_INFO = 2,
HV_HID_MSGTYPE_INITIAL_DEV_INFO_ACK = 3,
HV_HID_MSGTYPE_INPUT_REPORT = 4,
HV_HID_MSGTYPE_INPUT_MAX
};
// HID message header
typedef struct {
uint32 type;
uint32 length;
} _PACKED hv_hid_msg_header;
// HID protocol request message sent to Hyper-V
typedef struct {
hv_hid_msg_header header;
uint32 version;
} _PACKED hv_hid_msg_protocol_request;
// HID protocol response message received from Hyper-V
typedef struct {
hv_hid_msg_header header;
uint32 version;
uint8 result;
uint8 reserved[3];
} _PACKED hv_hid_msg_protocol_response;
// HID initial device info message received from Hyper-V
typedef struct {
hv_hid_msg_header header;
hv_hid_dev_info info;
hv_hid_descriptor descriptor;
uint8 descriptor_data[];
} _PACKED hv_hid_msg_initial_dev_info;
// HID initial device info acknowledgement message sent to Hyper-V
typedef struct {
hv_hid_msg_header header;
uint8 reserved;
} _PACKED hv_hid_msg_initial_dev_info_ack;
// HID input report message received from Hyper-V
typedef struct {
hv_hid_msg_header header;
uint8 data[];
} _PACKED hv_hid_msg_input_report;
// HID pipe message header
typedef struct {
uint32 type;
uint32 length;
} _PACKED hv_hid_pipe_msg_header;
// HID pipe message sent to Hyper-V
typedef struct {
hv_hid_pipe_msg_header pipe_header;
union {
hv_hid_msg_header header;
hv_hid_msg_protocol_request protocol_req;
hv_hid_msg_protocol_response protocol_resp;
hv_hid_msg_initial_dev_info_ack dev_info_ack;
};
} _PACKED hv_hid_pipe_out_msg;
// HID pipe message received from Hyper-V
typedef struct {
hv_hid_pipe_msg_header pipe_header;
union {
hv_hid_msg_header header;
hv_hid_msg_protocol_request protocol_req;
hv_hid_msg_protocol_response protocol_resp;
hv_hid_msg_initial_dev_info dev_info;
hv_hid_msg_initial_dev_info_ack dev_info_ack;
hv_hid_msg_input_report input_report;
};
} _PACKED hv_hid_pipe_in_msg;
typedef struct {
uint8 buttons;
uint16 x;
uint16 y;
int8 wheel;
} _PACKED hyperv_hid_input_report;
#endif // _HYPERV_HID_PROTOCOL_H_
@@ -0,0 +1,27 @@
SubDir HAIKU_TOP src add-ons kernel drivers input hyperv_hid ;
SubDirSysHdrs $(HAIKU_TOP) headers os drivers ;
SubDirSysHdrs $(HAIKU_TOP) src add-ons kernel drivers input hid_shared ;
UsePrivateHeaders [ FDirName kernel util ] input drivers device hyperv ;
UsePrivateKernelHeaders ;
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons kernel drivers input hid_shared ] ;
KernelAddon hyperv_hid :
DeviceList.cpp
Driver.cpp
HIDDevice.cpp
HIDCollection.cpp
HIDParser.cpp
HIDReport.cpp
HIDReportItem.cpp
HIDWriter.cpp
ProtocolHandler.cpp
JoystickProtocolHandler.cpp
KeyboardProtocolHandler.cpp
MouseProtocolHandler.cpp
TabletProtocolHandler.cpp
;