Added VirtIO Input Device base class and keyboard device implementation.
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
package li.cil.circuity.vm.device.virtio;
|
||||
|
||||
import li.cil.circuity.api.vm.MemoryMap;
|
||||
import li.cil.circuity.api.vm.device.memory.MemoryAccessException;
|
||||
import li.cil.circuity.api.vm.device.memory.Sizes;
|
||||
import li.cil.circuity.vm.evdev.EvdevEvents;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public abstract class AbstractVirtIOInputDevice extends AbstractVirtIODevice {
|
||||
protected static final int VIRTIO_INPUT_CFG_SELECT_UNSET = 0x00;
|
||||
protected static final int VIRTIO_INPUT_CFG_SELECT_ID_NAME = 0x01;
|
||||
protected static final int VIRTIO_INPUT_CFG_SELECT_ID_SERIAL = 0x02;
|
||||
protected static final int VIRTIO_INPUT_CFG_SELECT_ID_DEVIDS = 0x03;
|
||||
protected static final int VIRTIO_INPUT_CFG_SELECT_PROP_BITS = 0x10;
|
||||
protected static final int VIRTIO_INPUT_CFG_SELECT_EV_BITS = 0x11;
|
||||
protected static final int VIRTIO_INPUT_CFG_SELECT_ABS_INFO = 0x12;
|
||||
|
||||
// Config looks like this:
|
||||
// struct virtio_input_config {
|
||||
// u8 select;
|
||||
// u8 subsel;
|
||||
// u8 size;
|
||||
// u8 reserved[5];
|
||||
// union {
|
||||
// char string[128];
|
||||
// u8 bitmap[128];
|
||||
// struct virtio_input_absinfo abs;
|
||||
// struct virtio_input_devids ids;
|
||||
// } u;
|
||||
// };
|
||||
private static final int VIRTIO_INPUT_CFG_SELECT_OFFSET = 0x0;
|
||||
private static final int VIRTIO_INPUT_CFG_SUBSEL_OFFSET = 0x1;
|
||||
private static final int VIRTIO_INPUT_CFG_SIZE_OFFSET = 0x2;
|
||||
private static final int VIRTIO_INPUT_CFG_UNION_OFFSET = 0x8;
|
||||
private static final int VIRTIO_INPUT_CFG_UNION_SIZE = 128;
|
||||
|
||||
private static final int VIRTQ_EVENT = 0;
|
||||
private static final int VIRTQ_STATUS = 1;
|
||||
|
||||
private static final ThreadLocal<ByteBuffer> eventBuffer = new ThreadLocal<>();
|
||||
|
||||
// This holds the union with info in the config struct. We generate this whenever the state
|
||||
// changes and keep it for more efficient access (and fewer headaches).
|
||||
private final ByteBuffer config = ByteBuffer.allocate(VIRTIO_INPUT_CFG_UNION_SIZE);
|
||||
private boolean configDirty = true;
|
||||
|
||||
private DescriptorChain event;
|
||||
|
||||
protected AbstractVirtIOInputDevice(final MemoryMap memoryMap) {
|
||||
super(memoryMap, VirtIODeviceSpec.builder(VirtIODeviceType.VIRTIO_DEVICE_ID_INPUT_DEVICE)
|
||||
// We only physically use 2 bytes of config space, but this is used for getLength(), too.
|
||||
.configSpaceSize(256)
|
||||
.queueCount(2)
|
||||
.build());
|
||||
}
|
||||
|
||||
protected abstract void generateConfigUnion(final int select, final int subsel, final ByteBuffer config);
|
||||
|
||||
protected void handleStatus(final int type, final int code, final int value) {
|
||||
}
|
||||
|
||||
protected final void putEvent(final int type, final int code, final int value) {
|
||||
if ((getStatus() & VIRTIO_STATUS_FAILED) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 5.8.6.1: These buffers [in the eventq] MUST be device-writable [...]
|
||||
event = validateWriteOnlyDescriptorChain(VIRTQ_EVENT, event);
|
||||
if (event != null) {
|
||||
// 5.8.6.1: [eventq buffers] MUST be at least the size of struct virtio_input_event.
|
||||
if (event.writableBytes() < 8) {
|
||||
error();
|
||||
return;
|
||||
}
|
||||
|
||||
// VirtIO Input Events look like this:
|
||||
// struct virtio_input_event {
|
||||
// le16 type;
|
||||
// le16 code;
|
||||
// le32 value;
|
||||
// };
|
||||
final ByteBuffer buffer = getTempBuffer();
|
||||
buffer.putShort((short) type);
|
||||
buffer.putShort((short) code);
|
||||
buffer.putInt(value);
|
||||
|
||||
buffer.flip();
|
||||
event.put(buffer);
|
||||
event.use();
|
||||
}
|
||||
} catch (final VirtIODeviceException | MemoryAccessException e) {
|
||||
error();
|
||||
}
|
||||
}
|
||||
|
||||
protected final void putSyn() {
|
||||
putEvent(EvdevEvents.EV_SYN, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final int loadConfig(final int offset, final int sizeLog2) {
|
||||
if (sizeLog2 != Sizes.SIZE_8_LOG2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (offset == VIRTIO_INPUT_CFG_SIZE_OFFSET) {
|
||||
validateConfig();
|
||||
return config.limit();
|
||||
} else if (offset >= VIRTIO_INPUT_CFG_UNION_OFFSET && offset < VIRTIO_INPUT_CFG_UNION_OFFSET + VIRTIO_INPUT_CFG_UNION_SIZE) {
|
||||
validateConfig();
|
||||
return config.get(offset - VIRTIO_INPUT_CFG_UNION_OFFSET);
|
||||
}
|
||||
|
||||
return super.loadConfig(offset, sizeLog2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void storeConfig(final int offset, final int value, final int sizeLog2) {
|
||||
if (offset > VIRTIO_INPUT_CFG_SUBSEL_OFFSET) {
|
||||
return; // Only select and subsel are writable by the driver.
|
||||
}
|
||||
|
||||
super.storeConfig(offset, value, sizeLog2);
|
||||
configDirty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void handleFeaturesNegotiated() {
|
||||
setQueueNotifications(VIRTQ_EVENT, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void handleQueueNotification(final int queueIndex) throws VirtIODeviceException, MemoryAccessException {
|
||||
if (queueIndex == VIRTQ_STATUS) {
|
||||
final VirtqueueIterator queue = getQueueIterator(queueIndex);
|
||||
if (queue != null && queue.hasNext()) {
|
||||
while (queue.hasNext()) {
|
||||
final DescriptorChain chain = queue.next();
|
||||
while (chain.readableBytes() >= 8) {
|
||||
final ByteBuffer buffer = getTempBuffer();
|
||||
chain.get(buffer);
|
||||
buffer.flip();
|
||||
|
||||
final short type = buffer.getShort();
|
||||
final short code = buffer.getShort();
|
||||
final int value = buffer.getInt();
|
||||
handleStatus(type, code, value);
|
||||
}
|
||||
chain.use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateConfig() {
|
||||
if (!configDirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ByteBuffer configSpace = getConfiguration();
|
||||
final int select = configSpace.get(VIRTIO_INPUT_CFG_SELECT_OFFSET) & 0xFF;
|
||||
final int subsel = configSpace.get(VIRTIO_INPUT_CFG_SUBSEL_OFFSET) & 0xFF;
|
||||
|
||||
if (select == VIRTIO_INPUT_CFG_SELECT_UNSET) {
|
||||
config.limit(0);
|
||||
} else {
|
||||
config.clear();
|
||||
generateConfigUnion(select, subsel, config);
|
||||
}
|
||||
|
||||
config.position(0);
|
||||
|
||||
configDirty = false;
|
||||
}
|
||||
|
||||
private static ByteBuffer getTempBuffer() {
|
||||
ByteBuffer buffer = eventBuffer.get();
|
||||
if (buffer == null) {
|
||||
buffer = ByteBuffer.allocate(8);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
eventBuffer.set(buffer);
|
||||
}
|
||||
buffer.clear();
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package li.cil.circuity.vm.device.virtio;
|
||||
|
||||
import li.cil.circuity.api.vm.MemoryMap;
|
||||
import li.cil.circuity.vm.evdev.EvdevEvents;
|
||||
import li.cil.circuity.vm.evdev.EvdevKeys;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.BitSet;
|
||||
|
||||
public final class VirtIOKeyboardDevice extends AbstractVirtIOInputDevice {
|
||||
private static final String NAME = "virtio_keyboard";
|
||||
|
||||
public VirtIOKeyboardDevice(final MemoryMap memoryMap) {
|
||||
super(memoryMap);
|
||||
}
|
||||
|
||||
public void putKey(final int keycode, final boolean isDown) {
|
||||
putEvent(EvdevEvents.EV_KEY, keycode, isDown ? 1 : 0);
|
||||
putSyn();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateConfigUnion(final int select, final int subsel, final ByteBuffer config) {
|
||||
switch (select) {
|
||||
case VIRTIO_INPUT_CFG_SELECT_ID_NAME: {
|
||||
final char[] chars = NAME.toCharArray();
|
||||
for (final char ch : chars) {
|
||||
config.put((byte) ch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VIRTIO_INPUT_CFG_SELECT_EV_BITS: {
|
||||
switch (subsel) {
|
||||
case EvdevEvents.EV_KEY: {
|
||||
final BitSet bitmap = new BitSet();
|
||||
for (final int keycode : EvdevKeys.ALL_KEYS) {
|
||||
bitmap.set(keycode);
|
||||
}
|
||||
final byte[] maskBytes = bitmap.toByteArray();
|
||||
config.put(maskBytes);
|
||||
break;
|
||||
}
|
||||
case EvdevEvents.EV_REP: {
|
||||
config.put((byte) 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
config.limit(config.position());
|
||||
}
|
||||
}
|
||||
58
src/main/java/li/cil/circuity/vm/evdev/EvdevEvents.java
Normal file
58
src/main/java/li/cil/circuity/vm/evdev/EvdevEvents.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package li.cil.circuity.vm.evdev;
|
||||
|
||||
/**
|
||||
* Linux evdev <a href="https://www.kernel.org/doc/html/latest/input/event-codes.html#input-event-codes">input event types</a>.
|
||||
* <p>
|
||||
* Numeric values from Linux kernel: include/uapi/linux/input-event-codes.h
|
||||
*/
|
||||
public final class EvdevEvents {
|
||||
/**
|
||||
* Used as markers to separate events. Events may be separated in time or in space,
|
||||
* such as with the multitouch protocol.
|
||||
*/
|
||||
public static final int EV_SYN = 0x00;
|
||||
/**
|
||||
* Used to describe state changes of keyboards, buttons, or other key-like devices.
|
||||
*/
|
||||
public static final int EV_KEY = 0x01;
|
||||
/**
|
||||
* Used to describe relative axis value changes, e.g. moving the mouse 5 units to the left.
|
||||
*/
|
||||
public static final int EV_REL = 0x02;
|
||||
/**
|
||||
* Used to describe absolute axis value changes, e.g. describing the coordinates of a touch on a touchscreen.
|
||||
*/
|
||||
public static final int EV_ABS = 0x03;
|
||||
/**
|
||||
* Used to describe miscellaneous input data that do not fit into other types.
|
||||
*/
|
||||
public static final int EV_MSC = 0x04;
|
||||
/**
|
||||
* Used to describe binary state input switches.
|
||||
*/
|
||||
public static final int EV_SW = 0x05;
|
||||
/**
|
||||
* Used to turn LEDs on devices on and off.
|
||||
*/
|
||||
public static final int EV_LED = 0x11;
|
||||
/**
|
||||
* Used to output sound to devices.
|
||||
*/
|
||||
public static final int EV_SND = 0x12;
|
||||
/**
|
||||
* Used for autorepeating devices.
|
||||
*/
|
||||
public static final int EV_REP = 0x14;
|
||||
/**
|
||||
* Used to send force feedback commands to an input device.
|
||||
*/
|
||||
public static final int EV_FF = 0x15;
|
||||
/**
|
||||
* A special type for power button and switch input.
|
||||
*/
|
||||
public static final int EV_PWR = 0x16;
|
||||
/**
|
||||
* Used to receive force feedback device status.
|
||||
*/
|
||||
public static final int EV_FF_STATUS = 0x17;
|
||||
}
|
||||
238
src/main/java/li/cil/circuity/vm/evdev/EvdevKeys.java
Normal file
238
src/main/java/li/cil/circuity/vm/evdev/EvdevKeys.java
Normal file
@@ -0,0 +1,238 @@
|
||||
package li.cil.circuity.vm.evdev;
|
||||
|
||||
/**
|
||||
* Linux evdev input keycodes.
|
||||
* <p>
|
||||
* Numeric values from Linux kernel: include/uapi/linux/input-event-codes.h
|
||||
*/
|
||||
public final class EvdevKeys {
|
||||
public static final int KEY_ESC = 1;
|
||||
public static final int KEY_1 = 2;
|
||||
public static final int KEY_2 = 3;
|
||||
public static final int KEY_3 = 4;
|
||||
public static final int KEY_4 = 5;
|
||||
public static final int KEY_5 = 6;
|
||||
public static final int KEY_6 = 7;
|
||||
public static final int KEY_7 = 8;
|
||||
public static final int KEY_8 = 9;
|
||||
public static final int KEY_9 = 10;
|
||||
public static final int KEY_0 = 11;
|
||||
public static final int KEY_MINUS = 12;
|
||||
public static final int KEY_EQUAL = 13;
|
||||
public static final int KEY_BACKSPACE = 14;
|
||||
public static final int KEY_TAB = 15;
|
||||
public static final int KEY_Q = 16;
|
||||
public static final int KEY_W = 17;
|
||||
public static final int KEY_E = 18;
|
||||
public static final int KEY_R = 19;
|
||||
public static final int KEY_T = 20;
|
||||
public static final int KEY_Y = 21;
|
||||
public static final int KEY_U = 22;
|
||||
public static final int KEY_I = 23;
|
||||
public static final int KEY_O = 24;
|
||||
public static final int KEY_P = 25;
|
||||
public static final int KEY_LEFTBRACE = 26;
|
||||
public static final int KEY_RIGHTBRACE = 27;
|
||||
public static final int KEY_ENTER = 28;
|
||||
public static final int KEY_LEFTCTRL = 29;
|
||||
public static final int KEY_A = 30;
|
||||
public static final int KEY_S = 31;
|
||||
public static final int KEY_D = 32;
|
||||
public static final int KEY_F = 33;
|
||||
public static final int KEY_G = 34;
|
||||
public static final int KEY_H = 35;
|
||||
public static final int KEY_J = 36;
|
||||
public static final int KEY_K = 37;
|
||||
public static final int KEY_L = 38;
|
||||
public static final int KEY_SEMICOLON = 39;
|
||||
public static final int KEY_APOSTROPHE = 40;
|
||||
public static final int KEY_GRAVE = 41;
|
||||
public static final int KEY_LEFTSHIFT = 42;
|
||||
public static final int KEY_BACKSLASH = 43;
|
||||
public static final int KEY_Z = 44;
|
||||
public static final int KEY_X = 45;
|
||||
public static final int KEY_C = 46;
|
||||
public static final int KEY_V = 47;
|
||||
public static final int KEY_B = 48;
|
||||
public static final int KEY_N = 49;
|
||||
public static final int KEY_M = 50;
|
||||
public static final int KEY_COMMA = 51;
|
||||
public static final int KEY_DOT = 52;
|
||||
public static final int KEY_SLASH = 53;
|
||||
public static final int KEY_RIGHTSHIFT = 54;
|
||||
public static final int KEY_KPASTERISK = 55;
|
||||
public static final int KEY_LEFTALT = 56;
|
||||
public static final int KEY_SPACE = 57;
|
||||
public static final int KEY_CAPSLOCK = 58;
|
||||
public static final int KEY_F1 = 59;
|
||||
public static final int KEY_F2 = 60;
|
||||
public static final int KEY_F3 = 61;
|
||||
public static final int KEY_F4 = 62;
|
||||
public static final int KEY_F5 = 63;
|
||||
public static final int KEY_F6 = 64;
|
||||
public static final int KEY_F7 = 65;
|
||||
public static final int KEY_F8 = 66;
|
||||
public static final int KEY_F9 = 67;
|
||||
public static final int KEY_F10 = 68;
|
||||
public static final int KEY_NUMLOCK = 69;
|
||||
public static final int KEY_SCROLLLOCK = 70;
|
||||
public static final int KEY_KP7 = 71;
|
||||
public static final int KEY_KP8 = 72;
|
||||
public static final int KEY_KP9 = 73;
|
||||
public static final int KEY_KPMINUS = 74;
|
||||
public static final int KEY_KP4 = 75;
|
||||
public static final int KEY_KP5 = 76;
|
||||
public static final int KEY_KP6 = 77;
|
||||
public static final int KEY_KPPLUS = 78;
|
||||
public static final int KEY_KP1 = 79;
|
||||
public static final int KEY_KP2 = 80;
|
||||
public static final int KEY_KP3 = 81;
|
||||
public static final int KEY_KP0 = 82;
|
||||
public static final int KEY_KPDOT = 83;
|
||||
public static final int KEY_F11 = 87;
|
||||
public static final int KEY_F12 = 88;
|
||||
public static final int KEY_KPENTER = 96;
|
||||
public static final int KEY_RIGHTCTRL = 97;
|
||||
public static final int KEY_KPSLASH = 98;
|
||||
public static final int KEY_RIGHTALT = 100;
|
||||
public static final int KEY_HOME = 102;
|
||||
public static final int KEY_UP = 103;
|
||||
public static final int KEY_PAGEUP = 104;
|
||||
public static final int KEY_LEFT = 105;
|
||||
public static final int KEY_RIGHT = 106;
|
||||
public static final int KEY_END = 107;
|
||||
public static final int KEY_DOWN = 108;
|
||||
public static final int KEY_PAGEDOWN = 109;
|
||||
public static final int KEY_INSERT = 110;
|
||||
public static final int KEY_DELETE = 111;
|
||||
public static final int KEY_KPEQUAL = 117;
|
||||
public static final int KEY_PAUSE = 119;
|
||||
public static final int KEY_F13 = 183;
|
||||
public static final int KEY_F14 = 184;
|
||||
public static final int KEY_F15 = 185;
|
||||
public static final int KEY_F16 = 186;
|
||||
public static final int KEY_F17 = 187;
|
||||
public static final int KEY_F18 = 188;
|
||||
public static final int KEY_F19 = 189;
|
||||
public static final int KEY_F20 = 190;
|
||||
public static final int KEY_F21 = 191;
|
||||
public static final int KEY_F22 = 192;
|
||||
public static final int KEY_F23 = 193;
|
||||
public static final int KEY_F24 = 194;
|
||||
|
||||
public static final int[] ALL_KEYS = {
|
||||
KEY_ESC,
|
||||
KEY_1,
|
||||
KEY_2,
|
||||
KEY_3,
|
||||
KEY_4,
|
||||
KEY_5,
|
||||
KEY_6,
|
||||
KEY_7,
|
||||
KEY_8,
|
||||
KEY_9,
|
||||
KEY_0,
|
||||
KEY_MINUS,
|
||||
KEY_EQUAL,
|
||||
KEY_BACKSPACE,
|
||||
KEY_TAB,
|
||||
KEY_Q,
|
||||
KEY_W,
|
||||
KEY_E,
|
||||
KEY_R,
|
||||
KEY_T,
|
||||
KEY_Y,
|
||||
KEY_U,
|
||||
KEY_I,
|
||||
KEY_O,
|
||||
KEY_P,
|
||||
KEY_LEFTBRACE,
|
||||
KEY_RIGHTBRACE,
|
||||
KEY_ENTER,
|
||||
KEY_LEFTCTRL,
|
||||
KEY_A,
|
||||
KEY_S,
|
||||
KEY_D,
|
||||
KEY_F,
|
||||
KEY_G,
|
||||
KEY_H,
|
||||
KEY_J,
|
||||
KEY_K,
|
||||
KEY_L,
|
||||
KEY_SEMICOLON,
|
||||
KEY_APOSTROPHE,
|
||||
KEY_GRAVE,
|
||||
KEY_LEFTSHIFT,
|
||||
KEY_BACKSLASH,
|
||||
KEY_Z,
|
||||
KEY_X,
|
||||
KEY_C,
|
||||
KEY_V,
|
||||
KEY_B,
|
||||
KEY_N,
|
||||
KEY_M,
|
||||
KEY_COMMA,
|
||||
KEY_DOT,
|
||||
KEY_SLASH,
|
||||
KEY_RIGHTSHIFT,
|
||||
KEY_KPASTERISK,
|
||||
KEY_LEFTALT,
|
||||
KEY_SPACE,
|
||||
KEY_CAPSLOCK,
|
||||
KEY_F1,
|
||||
KEY_F2,
|
||||
KEY_F3,
|
||||
KEY_F4,
|
||||
KEY_F5,
|
||||
KEY_F6,
|
||||
KEY_F7,
|
||||
KEY_F8,
|
||||
KEY_F9,
|
||||
KEY_F10,
|
||||
KEY_NUMLOCK,
|
||||
KEY_SCROLLLOCK,
|
||||
KEY_KP7,
|
||||
KEY_KP8,
|
||||
KEY_KP9,
|
||||
KEY_KPMINUS,
|
||||
KEY_KP4,
|
||||
KEY_KP5,
|
||||
KEY_KP6,
|
||||
KEY_KPPLUS,
|
||||
KEY_KP1,
|
||||
KEY_KP2,
|
||||
KEY_KP3,
|
||||
KEY_KP0,
|
||||
KEY_KPDOT,
|
||||
KEY_F11,
|
||||
KEY_F12,
|
||||
KEY_KPENTER,
|
||||
KEY_RIGHTCTRL,
|
||||
KEY_KPSLASH,
|
||||
KEY_RIGHTALT,
|
||||
KEY_HOME,
|
||||
KEY_UP,
|
||||
KEY_PAGEUP,
|
||||
KEY_LEFT,
|
||||
KEY_RIGHT,
|
||||
KEY_END,
|
||||
KEY_DOWN,
|
||||
KEY_PAGEDOWN,
|
||||
KEY_INSERT,
|
||||
KEY_DELETE,
|
||||
KEY_KPEQUAL,
|
||||
KEY_PAUSE,
|
||||
KEY_F13,
|
||||
KEY_F14,
|
||||
KEY_F15,
|
||||
KEY_F16,
|
||||
KEY_F17,
|
||||
KEY_F18,
|
||||
KEY_F19,
|
||||
KEY_F20,
|
||||
KEY_F21,
|
||||
KEY_F22,
|
||||
KEY_F23,
|
||||
KEY_F24,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user