feat: input device subsystem to resolve race condition on polling input
[lunaix-os.git] / lunaix-os / kernel / device / input.c
diff --git a/lunaix-os/kernel/device/input.c b/lunaix-os/kernel/device/input.c
new file mode 100644 (file)
index 0000000..5ddf6a4
--- /dev/null
@@ -0,0 +1,85 @@
+#include <lunaix/clock.h>
+#include <lunaix/input.h>
+#include <lunaix/mm/valloc.h>
+#include <lunaix/spike.h>
+#include <lunaix/status.h>
+
+#include <klibc/string.h>
+
+static DEFINE_LLIST(listener_chain);
+
+static struct device* input_devcat = NULL;
+
+void
+input_init()
+{
+    input_devcat = device_addcat(NULL, "input");
+}
+
+void
+input_fire_event(struct input_device* idev, struct input_evt_pkt* pkt)
+{
+    pkt->timestamp = clock_systime();
+    idev->current_pkt = *pkt;
+
+    struct input_evt_chain *pos, *n;
+    llist_for_each(pos, n, &listener_chain, chain)
+    {
+        if (pos->evt_cb(idev) == INPUT_EVT_CATCH) {
+            break;
+        }
+    }
+
+    // wake up all pending readers
+    pwake_all(&idev->readers);
+}
+
+void
+input_add_listener(input_evt_cb listener)
+{
+    assert(listener);
+
+    struct input_evt_chain* chain = vzalloc(sizeof(*chain));
+    llist_append(&listener_chain, &chain->chain);
+
+    chain->evt_cb = listener;
+}
+
+int
+__input_dev_read(struct device* dev, void* buf, size_t offset, size_t len)
+{
+    struct input_device* idev = dev->underlay;
+
+    if (len < sizeof(struct input_evt_pkt)) {
+        return ERANGE;
+    }
+
+    // wait for new event
+    pwait(&idev->readers);
+
+    memcpy(buf, &idev->current_pkt, sizeof(struct input_evt_pkt));
+
+    return sizeof(struct input_evt_pkt);
+}
+
+struct input_device*
+input_add_device(char* name_fmt, ...)
+{
+    assert(input_devcat);
+
+    struct input_device* idev = vzalloc(sizeof(*idev));
+    waitq_init(&idev->readers);
+
+    va_list args;
+    va_start(args, name_fmt);
+
+    struct device* dev =
+      device_add(input_devcat, idev, name_fmt, DEV_IFSEQ, args);
+
+    idev->dev_if = dev;
+    dev->read = __input_dev_read;
+
+    va_end(args);
+
+    return idev;
+}