From 03fd479633da3d090cfd2fefac40d3f1b7b95e1f Mon Sep 17 00:00:00 2001 From: X512 Date: Mon, 24 May 2021 06:41:04 +0900 Subject: [PATCH] virtio_input: add new virtio input driver Change-Id: I10af2b30c79d1e8ff890d45642988f362d3d0a1e Reviewed-on: https://review.haiku-os.org/c/haiku/+/4012 Reviewed-by: Alex von Gluck IV --- headers/private/input/virtio_input_driver.h | 19 + src/add-ons/kernel/drivers/input/Jamfile | 1 + .../kernel/drivers/input/virtio_input/Jamfile | 9 + .../input/virtio_input/virtio_input.cpp | 583 ++++++++++++++++++ 4 files changed, 612 insertions(+) create mode 100644 headers/private/input/virtio_input_driver.h create mode 100644 src/add-ons/kernel/drivers/input/virtio_input/Jamfile create mode 100644 src/add-ons/kernel/drivers/input/virtio_input/virtio_input.cpp diff --git a/headers/private/input/virtio_input_driver.h b/headers/private/input/virtio_input_driver.h new file mode 100644 index 0000000000..9980cca438 --- /dev/null +++ b/headers/private/input/virtio_input_driver.h @@ -0,0 +1,19 @@ +/* + * Copyright 2021, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _VIRTIO_INPUT_DRIVER_H_ +#define _VIRTIO_INPUT_DRIVER_H_ + + +#include +#include + + +enum { + virtioInputRead = B_DEVICE_OP_CODES_END + 1, + virtioInputCancelIO = B_DEVICE_OP_CODES_END + 2, +}; + + +#endif // _VIRTIO_INPUT_DRIVER_H_ diff --git a/src/add-ons/kernel/drivers/input/Jamfile b/src/add-ons/kernel/drivers/input/Jamfile index 73ba672d6a..44a2ab2a74 100644 --- a/src/add-ons/kernel/drivers/input/Jamfile +++ b/src/add-ons/kernel/drivers/input/Jamfile @@ -3,4 +3,5 @@ SubDir HAIKU_TOP src add-ons kernel drivers input ; 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 usb_hid ; +SubInclude HAIKU_TOP src add-ons kernel drivers input virtio_input ; SubInclude HAIKU_TOP src add-ons kernel drivers input wacom ; diff --git a/src/add-ons/kernel/drivers/input/virtio_input/Jamfile b/src/add-ons/kernel/drivers/input/virtio_input/Jamfile new file mode 100644 index 0000000000..2a93617803 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/virtio_input/Jamfile @@ -0,0 +1,9 @@ +SubDir HAIKU_TOP src add-ons kernel drivers input virtio_input ; + +UsePrivateKernelHeaders ; +UsePrivateHeaders drivers virtio input ; +SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ; + +KernelAddon virtio_input : + virtio_input.cpp +; diff --git a/src/add-ons/kernel/drivers/input/virtio_input/virtio_input.cpp b/src/add-ons/kernel/drivers/input/virtio_input/virtio_input.cpp new file mode 100644 index 0000000000..e554ca1f2c --- /dev/null +++ b/src/add-ons/kernel/drivers/input/virtio_input/virtio_input.cpp @@ -0,0 +1,583 @@ +/* + * Copyright 2013, Jérôme Duval, korli@users.berlios.de. + * Copyright 2021, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + + +//#define TRACE_VIRTIO_INPUT +#ifdef TRACE_VIRTIO_INPUT +# define TRACE(x...) dprintf("virtio_input: " x) +#else +# define TRACE(x...) ; +#endif +#define ERROR(x...) dprintf("virtio_input: " x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + +#define VIRTIO_INPUT_DRIVER_MODULE_NAME "drivers/input/virtio_input/driver_v1" +#define VIRTIO_INPUT_DEVICE_MODULE_NAME "drivers/input/virtio_input/device_v1" +#define VIRTIO_INPUT_DEVICE_ID_GENERATOR "virtio_input/device_id" + + +struct Packet { + VirtioInputPacket data; + int32 next; +}; + + +struct VirtioInputDevice { + device_node* node; + ::virtio_device virtio_device; + virtio_device_interface* virtio; + ::virtio_queue virtio_queue; + + uint32 features; + + uint32 packetCnt; + int32 freePackets; + int32 readyPackets, lastReadyPacket; + AreaDeleter packetArea; + phys_addr_t physAdr; + Packet* packets; + + SemDeleter sem_cb; +}; + + +struct VirtioInputHandle { + VirtioInputDevice* info; +}; + + +device_manager_info* gDeviceManager; + + +static void +WriteInputPacket(const VirtioInputPacket &pkt) +{ + switch (pkt.type) { + case kVirtioInputEvSyn: + TRACE("syn"); + break; + case kVirtioInputEvKey: + TRACE("key, "); + switch (pkt.code) { + case kVirtioInputBtnLeft: + TRACE("left"); + break; + case kVirtioInputBtnRight: + TRACE("middle"); + break; + case kVirtioInputBtnMiddle: + TRACE("right"); + break; + case kVirtioInputBtnGearDown: + TRACE("gearDown"); + break; + case kVirtioInputBtnGearUp: + TRACE("gearUp"); + break; + default: + TRACE("%d", pkt.code); + } + break; + case kVirtioInputEvRel: + TRACE("rel, "); + switch (pkt.code) { + case kVirtioInputRelX: + TRACE("relX"); + break; + case kVirtioInputRelY: + TRACE("relY"); + break; + case kVirtioInputRelZ: + TRACE("relZ"); + break; + case kVirtioInputRelWheel: + TRACE("relWheel"); + break; + default: + TRACE("%d", pkt.code); + } + break; + case kVirtioInputEvAbs: + TRACE("abs, "); + switch (pkt.code) { + case kVirtioInputAbsX: + TRACE("absX"); + break; + case kVirtioInputAbsY: + TRACE("absY"); + break; + case kVirtioInputAbsZ: + TRACE("absZ"); + break; + default: + TRACE("%d", pkt.code); + } + break; + case kVirtioInputEvRep: + TRACE("rep"); + break; + default: + TRACE("?(%d)", pkt.type); + } + switch (pkt.type) { + case kVirtioInputEvSyn: + break; + case kVirtioInputEvKey: + TRACE(", "); + if (pkt.value == 0) { + TRACE("up"); + } else if (pkt.value == 1) { + TRACE("down"); + } else { + TRACE("%" B_PRId32, pkt.value); + } + break; + default: + TRACE(", %" B_PRId32, pkt.value); + } +} + + +static void +InitPackets(VirtioInputDevice* dev, uint32 count) +{ + TRACE("InitPackets(%p, %" B_PRIu32 ")\n", dev, count); + size_t size = ROUNDUP(sizeof(Packet)*count, B_PAGE_SIZE); + + dev->packetArea.SetTo(create_area("VirtIO input packets", + (void**)&dev->packets, B_ANY_KERNEL_ADDRESS, size, + B_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA)); + if (!dev->packetArea.IsSet()) { + ERROR("Unable to set packet area!"); + return; + } + + physical_entry pe; + if (get_memory_map(dev->packets, size, &pe, 1) < B_OK) { + ERROR("Unable to get memory map for input packets!"); + return; + } + dev->physAdr = pe.address; + memset(dev->packets, 0, size); + dprintf(" size: 0x%" B_PRIxSIZE "\n", size); + dprintf(" virt: %p\n", dev->packets); + dprintf(" phys: %p\n", (void*)dev->physAdr); + + dev->packetCnt = count; + + dev->freePackets = 0; + for (uint32 i = 0; i < dev->packetCnt - 1; i++) + dev->packets[i].next = i + 1; + dev->packets[dev->packetCnt - 1].next = -1; + + dev->readyPackets = -1; + dev->lastReadyPacket = -1; +} + + +static const physical_entry +PacketPhysEntry(VirtioInputDevice* dev, Packet* pkt) +{ + physical_entry pe; + pe.address = dev->physAdr + (uint8*)pkt - (uint8*)dev->packets; + pe.size = sizeof(VirtioInputPacket); + return pe; +} + + +static Packet* +AllocPacket(VirtioInputDevice* dev) +{ + int32 idx = dev->freePackets; + if (idx < 0) + return NULL; + dev->freePackets = dev->packets[idx].next; + + return &dev->packets[idx]; +} + + +static void +FreePacket(VirtioInputDevice* dev, Packet* pkt) +{ + pkt->next = dev->freePackets; + dev->freePackets = pkt - dev->packets; +} + + +static void +ScheduleReadyPacket(VirtioInputDevice* dev, Packet* pkt) +{ + if (dev->readyPackets < 0) + dev->readyPackets = pkt - dev->packets; + else + dev->packets[dev->lastReadyPacket].next = pkt - dev->packets; + + dev->lastReadyPacket = pkt - dev->packets; +} + + +static Packet* +ConsumeReadyPacket(VirtioInputDevice* dev) +{ + if (dev->readyPackets < 0) + return NULL; + Packet* pkt = &dev->packets[dev->readyPackets]; + dev->readyPackets = pkt->next; + if (dev->readyPackets < 0) + dev->lastReadyPacket = -1; + return pkt; +} + + +static void +virtio_input_callback(void* driverCookie, void* cookie) +{ + CALLED(); + VirtioInputDevice* dev = (VirtioInputDevice*)cookie; + + Packet* pkt; + while (dev->virtio->queue_dequeue(dev->virtio_queue, (void**)&pkt, NULL)) { +#ifdef TRACE_VIRTIO_INPUT + TRACE("%" B_PRIdSSIZE ": ", pkt - dev->packets); + WriteInputPacket(pkt->data); + TRACE("\n"); +#endif + ScheduleReadyPacket(dev, pkt); + release_sem_etc(dev->sem_cb.Get(), 1, B_DO_NOT_RESCHEDULE); + } +} + + +// #pragma mark - device module API + + +static status_t +virtio_input_init_device(void* _info, void** _cookie) +{ + CALLED(); + VirtioInputDevice* info = (VirtioInputDevice*)_info; + + DeviceNodePutter<&gDeviceManager> parent( + gDeviceManager->get_parent_node(info->node)); + + gDeviceManager->get_driver(parent.Get(), + (driver_module_info **)&info->virtio, + (void **)&info->virtio_device); + + info->virtio->negotiate_features(info->virtio_device, 0, + &info->features, NULL); + + status_t status = B_OK; +/* + status = info->virtio->read_device_config( + info->virtio_device, 0, &info->config, + sizeof(struct virtio_blk_config)); + if (status != B_OK) + return status; +*/ + + InitPackets(info, 8); + + status = info->virtio->alloc_queues(info->virtio_device, 1, + &info->virtio_queue); + if (status != B_OK) { + ERROR("queue allocation failed (%s)\n", strerror(status)); + return status; + } + TRACE(" queue: %p\n", info->virtio_queue); + + status = info->virtio->queue_setup_interrupt(info->virtio_queue, + virtio_input_callback, info); + if (status < B_OK) + return status; + + for (uint32 i = 0; i < info->packetCnt; i++) { + Packet* pkt = &info->packets[i]; + physical_entry pe = PacketPhysEntry(info, pkt); + info->virtio->queue_request(info->virtio_queue, NULL, &pe, pkt); + } + + *_cookie = info; + return B_OK; +} + + +static void +virtio_input_uninit_device(void* _cookie) +{ + CALLED(); + VirtioInputDevice* info = (VirtioInputDevice*)_cookie; + (void)info; +} + + +static status_t +virtio_input_open(void* _info, const char* path, int openMode, void** _cookie) +{ + CALLED(); + VirtioInputDevice* info = (VirtioInputDevice*)_info; + + ObjectDeleter + handle(new(std::nothrow) (VirtioInputHandle)); + + if (!handle.IsSet()) + return B_NO_MEMORY; + + handle->info = info; + + *_cookie = handle.Detach(); + return B_OK; +} + + +static status_t +virtio_input_close(void* cookie) +{ + CALLED(); + return B_OK; +} + + +static status_t +virtio_input_free(void* cookie) +{ + CALLED(); + ObjectDeleter handle((VirtioInputHandle*)cookie); + return B_OK; +} + + +static status_t +virtio_input_read(void* cookie, off_t pos, void* buffer, size_t* _length) +{ + return B_ERROR; +} + + +static status_t +virtio_input_write(void* cookie, off_t pos, const void* buffer, + size_t* _length) +{ + *_length = 0; + return B_ERROR; +} + + +static status_t +virtio_input_ioctl(void* cookie, uint32 op, void* buffer, size_t length) +{ + CALLED(); + + VirtioInputHandle* handle = (VirtioInputHandle*)cookie; + VirtioInputDevice* info = handle->info; + (void)info; + + TRACE("ioctl(op = %" B_PRIu32 ")\n", op); + + switch (op) { + case virtioInputRead: { + TRACE("virtioInputRead\n"); + if (buffer == NULL || length < sizeof(VirtioInputPacket)) + return B_BAD_VALUE; + + status_t res = acquire_sem(info->sem_cb.Get()); + if (res < B_OK) + return res; + + Packet* pkt = ConsumeReadyPacket(info); + TRACE(" pkt: %" B_PRIdSSIZE "\n", pkt - info->packets); + + physical_entry pe = PacketPhysEntry(info, pkt); + info->virtio->queue_request(info->virtio_queue, NULL, &pe, pkt); + + res = user_memcpy(buffer, pkt, sizeof(Packet)); + if (res < B_OK) + return res; + + return B_OK; + } + } + + return B_DEV_INVALID_IOCTL; +} + + +// #pragma mark - driver module API + + +static float +virtio_input_supports_device(device_node *parent) +{ + CALLED(); + + const char *bus; + uint16 deviceType; + + // make sure parent is really the Virtio bus manager + if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) + return -1; + + if (strcmp(bus, "virtio")) + return 0.0; + + // check whether it's really a Direct Access Device + if (gDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM, + &deviceType, true) != B_OK || deviceType != kVirtioDevInput) + return 0.0; + + TRACE("Virtio input device found!\n"); + + return 0.6; +} + + +static status_t +virtio_input_register_device(device_node *node) +{ + CALLED(); + + device_attr attrs[] = { + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "VirtIO input" }}, + { NULL } + }; + + return gDeviceManager->register_node(node, VIRTIO_INPUT_DRIVER_MODULE_NAME, + attrs, NULL, NULL); +} + + +static status_t +virtio_input_init_driver(device_node *node, void **cookie) +{ + CALLED(); + + ObjectDeleter + info(new(std::nothrow) VirtioInputDevice()); + + if (!info.IsSet()) + return B_NO_MEMORY; + + memset(info.Get(), 0, sizeof(*info.Get())); + + info->sem_cb.SetTo(create_sem(0, "virtio_input_cb")); + if (!info->sem_cb.IsSet()) + return info->sem_cb.Get(); + + info->node = node; + + *cookie = info.Detach(); + return B_OK; +} + + +static void +virtio_input_uninit_driver(void *_cookie) +{ + CALLED(); + ObjectDeleter info((VirtioInputDevice*)_cookie); +} + + +static status_t +virtio_input_register_child_devices(void* _cookie) +{ + CALLED(); + VirtioInputDevice* info = (VirtioInputDevice*)_cookie; + status_t status; + + int32 id = gDeviceManager->create_id(VIRTIO_INPUT_DEVICE_ID_GENERATOR); + if (id < 0) + return id; + + char name[64]; + snprintf(name, sizeof(name), "input/virtio/%" B_PRId32 "/raw", id); + + status = gDeviceManager->publish_device(info->node, name, + VIRTIO_INPUT_DEVICE_MODULE_NAME); + + if (status < B_OK) { + ERROR("publish_device error: 0x%" B_PRIx32 "(%s) \n", status, + strerror(status)); + } + + return status; +} + + +// #pragma mark - + + +module_dependency module_dependencies[] = { + { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager }, + { NULL } +}; + + +struct device_module_info sVirtioInputDevice = { + { + VIRTIO_INPUT_DEVICE_MODULE_NAME, + 0, + NULL + }, + + virtio_input_init_device, + virtio_input_uninit_device, + NULL, // remove, + + virtio_input_open, + virtio_input_close, + virtio_input_free, + virtio_input_read, + virtio_input_write, + NULL, + virtio_input_ioctl, + + NULL, // select + NULL, // deselect +}; + +struct driver_module_info sVirtioInputDriver = { + { + VIRTIO_INPUT_DRIVER_MODULE_NAME, + 0, + NULL + }, + + virtio_input_supports_device, + virtio_input_register_device, + virtio_input_init_driver, + virtio_input_uninit_driver, + virtio_input_register_child_devices, + NULL, // rescan + NULL, // removed +}; + +module_info* modules[] = { + (module_info*)&sVirtioInputDriver, + (module_info*)&sVirtioInputDevice, + NULL +};