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:
committed by
Adrien Destugues
parent
767d7cf9fa
commit
a3eda7cbda
@@ -144,6 +144,7 @@ AddNewDriversToPackage network :
|
|||||||
virtio_net
|
virtio_net
|
||||||
;
|
;
|
||||||
AddNewDriversToPackage input :
|
AddNewDriversToPackage input :
|
||||||
|
hyperv_hid@x86,x86_64
|
||||||
i2c_elan
|
i2c_elan
|
||||||
virtio_input
|
virtio_input
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
SubDir HAIKU_TOP src add-ons kernel drivers input ;
|
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_elan ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel drivers input i2c_hid ;
|
SubInclude HAIKU_TOP src add-ons kernel drivers input i2c_hid ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel drivers input ps2_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
|
||||||
|
;
|
||||||
Reference in New Issue
Block a user