#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()) {
} else if (device->IsRemoved()) {
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();
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;
const char* type;
if (sDeviceManager->get_attr_string(parent, HYPERV_DEVICE_TYPE_STRING_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;
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) {
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,
hyperv_hid_open,
hyperv_hid_close,
hyperv_hid_free,
hyperv_hid_read,
hyperv_hid_write,
NULL,
hyperv_hid_control,
NULL,
NULL
};
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,
NULL
};
module_dependency module_dependencies[] = {
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager },
{ NULL }
};
module_info* modules[] = {
(module_info*)&sHyperVHIDDriverModule,
(module_info*)&sHyperVHIDDeviceModule,
NULL
};