2#include "VirtioInput.h"
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/Log.h"
5#include "pedigree/kernel/TargetInfo.h"
6#include "pedigree/kernel/machine/HidInputManager.h"
7#include "pedigree/kernel/machine/InputManager.h"
8#include "pedigree/kernel/machine/IrqManager.h"
9#include "pedigree/kernel/machine/Machine.h"
10#include "pedigree/kernel/panic.h"
11#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
12#include "pedigree/kernel/processor/VirtualAddressSpace.h"
13#include "pedigree/kernel/utilities/utility.h"
16constexpr uint8_t ConfigEventBits = 0x11;
17constexpr uint8_t ConfigAbsInfo = 0x12;
18constexpr uint8_t EvSyn = 0;
19constexpr uint8_t EvKey = 1;
20constexpr uint8_t EvRel = 2;
21constexpr uint8_t EvAbs = 3;
22constexpr uint8_t RelX = 0;
23constexpr uint8_t RelY = 1;
24constexpr uint8_t RelWheel = 8;
25constexpr uint8_t AbsX = 0;
26constexpr uint8_t AbsY = 1;
27constexpr uint16_t BtnLeft = 0x110;
28constexpr uint16_t BtnRight = 0x111;
29constexpr uint16_t BtnMiddle = 0x112;
30constexpr uint16_t BtnSide = 0x113;
31constexpr uint16_t BtnExtra = 0x114;
33uint8_t hidFromLinux(uint16_t code) {
34 static constexpr uint8_t letters[] = {
35 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50,
36 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,
38 for (
size_t i = 0; i <
sizeof(letters); ++i) {
39 if (letters[i] == code) {
43 if (code >= 2 && code <= 10) {
46 if (code >= 59 && code <= 68) {
142uint32_t buttonMask(uint16_t code) {
160VirtioInputDevice::VirtioInputDevice(
Device* pci)
165 m_Events(
"virtio-input-events"),
177 m_PointerChanged(false),
180 setSpecificType(
String(
"virtio-input-device"));
183VirtioInputDevice::~VirtioInputDevice() {
188 if (!m_Transport.reset()) {
189 panic(
"virtio-input could not stop DMA");
192 if (m_Irq && !Machine::instance().getIrqManager()->unregisterHandler(m_Irq,
this)) {
193 panic(
"virtio-input could not synchronously unregister its IRQ");
196 m_StatusQueue.stop();
199 for (
size_t key = 0; key <
sizeof(m_KeyDown); ++key) {
200 if (m_KeyDown[key]) {
204 }
else if (m_Buttons) {
205 if (m_Type == Mouse) {
213bool VirtioInputDevice::supportsEvent(uint8_t type, uint16_t code,
bool& supported) {
214 if (!m_Transport.writeDeviceConfig8(0, ConfigEventBits) ||
215 !m_Transport.writeDeviceConfig8(1, type)) {
219 if (!m_Transport.readDeviceConfig8(2, size) || size > 128) {
223 if (code / 8 < size) {
225 if (!m_Transport.readDeviceConfig8(8 + code / 8, bits)) {
228 supported = bits & (1U << (code % 8));
233bool VirtioInputDevice::readAxis(uint8_t axis, int32_t& minimum, int32_t& maximum) {
234 if (!m_Transport.writeDeviceConfig8(0, ConfigAbsInfo) ||
235 !m_Transport.writeDeviceConfig8(1, axis)) {
239 uint32_t minValue = 0;
240 uint32_t maxValue = 0;
241 if (!m_Transport.readDeviceConfig8(2, size) || size < 20 ||
242 !m_Transport.readDeviceConfig32(8, minValue) ||
243 !m_Transport.readDeviceConfig32(12, maxValue)) {
246 minimum =
static_cast<int32_t
>(LITTLE_TO_HOST32(minValue));
247 maximum =
static_cast<int32_t
>(LITTLE_TO_HOST32(maxValue));
248 return maximum > minimum;
251bool VirtioInputDevice::postReceive(Slot& slot) {
253 return m_EventQueue.submit(&buffer, 1, &slot);
256bool VirtioInputDevice::initialise() {
258 !m_Transport.initialise() || !m_Transport.negotiate(0) ||
259 !m_Transport.setupQueue(0, m_EventQueue) || !m_Transport.setupQueue(1, m_StatusQueue)) {
260 ERROR(
"virtio-input: PCI transport or queues unavailable");
264 bool absoluteX =
false, absoluteY =
false, relativeX =
false, relativeY =
false;
266 if (!supportsEvent(EvAbs, AbsX, absoluteX) || !supportsEvent(EvAbs, AbsY, absoluteY) ||
267 !supportsEvent(EvRel, RelX, relativeX) || !supportsEvent(EvRel, RelY, relativeY) ||
268 !supportsEvent(EvKey, 30, keyA)) {
269 ERROR(
"virtio-input: could not query supported input events");
272 if (absoluteX && absoluteY) {
274 if (!readAxis(AbsX, m_AbsMinimum[0], m_AbsMaximum[0]) ||
275 !readAxis(AbsY, m_AbsMinimum[1], m_AbsMaximum[1])) {
276 ERROR(
"virtio-input: invalid tablet axis ranges");
279 }
else if (relativeX && relativeY) {
284 ERROR(
"virtio-input: unsupported event set");
288 m_SlotCount = m_EventQueue.depth() < MaxSlots ? m_EventQueue.depth() : MaxSlots;
290 const size_t pages = (m_SlotCount *
sizeof(
Event) + page - 1) / page;
294 ERROR(
"virtio-input: could not allocate event buffers");
298 for (
size_t i = 0; i < m_SlotCount; ++i) {
301 if (!postReceive(m_Slots[i])) {
302 ERROR(
"virtio-input: could not populate event queue");
307 m_Irq = m_Transport.registerInterrupt(
this);
309 ERROR(
"virtio-input: could not register PCI interrupt");
313 if (!m_Transport.ready()) {
315 ERROR(
"virtio-input: device did not accept DRIVER_OK");
318 m_Transport.notify(0);
320 "virtio-input: " << (m_Type ==
Keyboard ?
"keyboard" : (m_Type == Mouse ?
"mouse" :
"tablet"))
325uint32_t VirtioInputDevice::scaleAxis(int32_t value,
unsigned axis)
const {
326 const int64_t minimum = m_AbsMinimum[axis];
327 const int64_t maximum = m_AbsMaximum[axis];
328 if (value <= minimum) {
331 if (value >= maximum) {
332 return AbsoluteMaximum;
334 return uint64_t(int64_t(value) - minimum) * AbsoluteMaximum / (maximum - minimum);
337void VirtioInputDevice::reportPointer() {
338 if (!m_PointerChanged) {
341 if (m_Type == Mouse) {
347 m_Relative[0] = m_Relative[1] = m_Relative[2] = 0;
348 m_PointerChanged =
false;
351void VirtioInputDevice::handle(
const Event& event) {
352 const uint16_t type = LITTLE_TO_HOST16(event.type);
353 const uint16_t code = LITTLE_TO_HOST16(event.code);
354 const int32_t value =
static_cast<int32_t
>(LITTLE_TO_HOST32(event.value));
357 if (type == EvKey && (value == 0 || value == 1)) {
358 const uint8_t hid = hidFromLinux(code);
359 if (hid && m_KeyDown[hid] != (value == 1)) {
360 m_KeyDown[hid] = value == 1;
371 if (type == EvSyn && code == 0) {
373 }
else if (type == EvKey && (value == 0 || value == 1)) {
374 const uint32_t mask = buttonMask(code);
376 const uint32_t before = m_Buttons;
382 m_PointerChanged |= before != m_Buttons;
384 }
else if (type == EvRel) {
385 if (code == RelWheel) {
386 m_Relative[2] += value;
387 m_PointerChanged =
true;
388 }
else if (m_Type == Mouse && code <= RelY) {
389 m_Relative[code] += value;
390 m_PointerChanged =
true;
392 }
else if (type == EvAbs && m_Type == Tablet && code <= AbsY) {
393 const uint32_t position = scaleAxis(value, code);
394 m_PointerChanged |= m_AbsPosition[code] != position;
395 m_AbsPosition[code] = position;
402 if (m_Stopping || !m_Active) {
405 if (!m_Transport.readIsr()) {
406 return IrqDisposition::NotHandled;
409 bool replenished =
false;
411 while (m_EventQueue.pop(completion)) {
412 auto* slot =
static_cast<Slot*
>(completion.cookie);
413 if (slot && completion.length ==
sizeof(
Event)) {
416 if (slot && postReceive(*slot)) {
419 ERROR(
"virtio-input: could not replenish event queue");
423 m_Transport.notify(0);
425 return IrqDisposition::Handled;
uint16_t getPciDeviceId()
uint16_t getPciVendorId()
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
static const size_t continuous
static PhysicalMemoryManager & instance()
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
static constexpr size_t getPageSize() noexcept
static const size_t KernelMode
static const size_t Write
void EXPORTED_PUBLIC panic(const char *msg) NORETURN