summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
authorPete Johanson <peter@peterjohanson.com>2020-06-08 21:07:16 -0400
committerPete Johanson <peter@peterjohanson.com>2020-06-08 21:07:16 -0400
commit38f1dbd9842eaf53db359ad46b070bfff8db1359 (patch)
tree6a24845fab2043cd0cec8b2086971ff13edeed70 /app/src
parent92b41d28e5668fab4fe423ccce5f3f85b4537002 (diff)
Move Zephyr app into subdirectory.
Diffstat (limited to 'app/src')
-rw-r--r--app/src/ble.c193
-rw-r--r--app/src/endpoints.c105
-rw-r--r--app/src/handlers.c88
-rw-r--r--app/src/hid.c130
-rw-r--r--app/src/hog.c158
-rw-r--r--app/src/keymap.c74
-rw-r--r--app/src/kscan.c75
-rw-r--r--app/src/kscan_composite.c130
-rw-r--r--app/src/kscan_mock.c102
-rw-r--r--app/src/main.c39
-rw-r--r--app/src/usb_hid.c58
11 files changed, 1152 insertions, 0 deletions
diff --git a/app/src/ble.c b/app/src/ble.c
new file mode 100644
index 0000000..51607c6
--- /dev/null
+++ b/app/src/ble.c
@@ -0,0 +1,193 @@
+
+#include <math.h>
+
+#include <settings/settings.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/conn.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/gatt.h>
+
+#include <zmk/keys.h>
+
+static struct bt_conn *auth_passkey_entry_conn;
+static u8_t passkey_entries[6] = {0, 0, 0, 0, 0, 0};
+static u8_t passkey_digit = 0;
+
+static void connected(struct bt_conn *conn, u8_t err)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ if (err)
+ {
+ printk("Failed to connect to %s (%u)\n", addr, err);
+ return;
+ }
+
+ printk("Connected %s\n", addr);
+
+ if (bt_conn_set_security(conn, BT_SECURITY_L2))
+ {
+ printk("Failed to set security\n");
+ }
+}
+
+static void disconnected(struct bt_conn *conn, u8_t reason)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ printk("Disconnected from %s (reason 0x%02x)\n", addr, reason);
+}
+
+static void security_changed(struct bt_conn *conn, bt_security_t level,
+ enum bt_security_err err)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ if (!err)
+ {
+ printk("Security changed: %s level %u\n", addr, level);
+ }
+ else
+ {
+ printk("Security failed: %s level %u err %d\n", addr, level,
+ err);
+ }
+}
+
+static struct bt_conn_cb conn_callbacks = {
+ .connected = connected,
+ .disconnected = disconnected,
+ .security_changed = security_changed,
+};
+
+static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ printk("Passkey for %s: %06u\n", addr, passkey);
+}
+
+#ifdef CONFIG_ZMK_BLE_PASSKEY_ENTRY
+
+static void auth_passkey_entry(struct bt_conn *conn)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ printk("Passkey entry requested for %s\n", addr);
+ auth_passkey_entry_conn = bt_conn_ref(conn);
+}
+
+#endif
+
+static void auth_cancel(struct bt_conn *conn)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ if (auth_passkey_entry_conn)
+ {
+ bt_conn_unref(auth_passkey_entry_conn);
+ auth_passkey_entry_conn = NULL;
+ }
+
+ passkey_digit = 0;
+
+ printk("Pairing cancelled: %s\n", addr);
+}
+
+static struct bt_conn_auth_cb zmk_ble_auth_cb_display = {
+// .passkey_display = auth_passkey_display,
+
+#ifdef CONFIG_ZMK_BLE_PASSKEY_ENTRY
+ .passkey_entry = auth_passkey_entry,
+#endif
+ .cancel = auth_cancel,
+};
+
+static const struct bt_data zmk_ble_ad[] = {
+ BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
+ BT_DATA_BYTES(BT_DATA_UUID16_ALL,
+ 0x12, 0x18, /* HID Service */
+ 0x0f, 0x18), /* Battery Service */
+};
+
+static void zmk_ble_ready(int err)
+{
+ if (err)
+ {
+ printk("Bluetooth init failed (err %d)\n", err);
+ return;
+ }
+
+ err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad), NULL, 0);
+ if (err)
+ {
+ printk("Advertising failed to start (err %d)\n", err);
+ return;
+ }
+}
+
+int zmk_ble_init()
+{
+ if (IS_ENABLED(CONFIG_SETTINGS))
+ {
+ settings_load();
+ }
+ int err = bt_enable(zmk_ble_ready);
+
+ if (err)
+ {
+ printk("BLUETOOTH FAILED");
+ return err;
+ }
+
+ bt_conn_cb_register(&conn_callbacks);
+ bt_conn_auth_cb_register(&zmk_ble_auth_cb_display);
+
+ return 0;
+}
+
+bool zmk_ble_handle_key_user(struct zmk_key_event *key_event)
+{
+ zmk_key key = key_event->key;
+
+ if (!auth_passkey_entry_conn)
+ {
+ return true;
+ }
+
+ if (key < KC_1 || key > KC_0)
+ {
+ return true;
+ }
+
+ u32_t val = (key == KC_0) ? 0 : (key - KC_1 + 1);
+
+ passkey_entries[passkey_digit++] = val;
+
+ if (passkey_digit == 6)
+ {
+ u32_t passkey = 0;
+ for (int i = 5; i >= 0; i--)
+ {
+ passkey = (passkey * 10) + val;
+ }
+ bt_conn_auth_passkey_entry(auth_passkey_entry_conn, passkey);
+ bt_conn_unref(auth_passkey_entry_conn);
+ auth_passkey_entry_conn = NULL;
+ }
+
+ return false;
+}
diff --git a/app/src/endpoints.c b/app/src/endpoints.c
new file mode 100644
index 0000000..f46d42d
--- /dev/null
+++ b/app/src/endpoints.c
@@ -0,0 +1,105 @@
+
+#include <zmk/endpoints.h>
+#include <zmk/hid.h>
+#include <zmk/usb_hid.h>
+#include <zmk/hog.h>
+
+#include <logging/log.h>
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+int zmk_endpoints_init()
+{
+ int err;
+
+ LOG_DBG("");
+
+#ifdef CONFIG_ZMK_USB
+ err = zmk_usb_hid_init();
+ if (err)
+ {
+ LOG_ERR("USB HID Init Failed\n");
+ return err;
+ }
+#endif /* CONFIG_ZMK_USB */
+
+#ifdef CONFIG_ZMK_BLE
+ err = zmk_hog_init();
+ if (err)
+ {
+ LOG_ERR("HOG Init Failed\n");
+ return err;
+ }
+
+#endif /* CONFIG_ZMK_BLE */
+
+ return 0;
+}
+
+int zmk_endpoints_send_report(enum zmk_hid_report_changes report_type)
+{
+ int err;
+ struct zmk_hid_keypad_report *keypad_report;
+ struct zmk_hid_consumer_report *consumer_report;
+ switch (report_type)
+ {
+ case Keypad:
+ keypad_report = zmk_hid_get_keypad_report();
+#ifdef CONFIG_ZMK_USB
+ if (zmk_usb_hid_send_report((u8_t *)keypad_report, sizeof(struct zmk_hid_keypad_report)) != 0)
+ {
+ LOG_DBG("USB Send Failed");
+ }
+#endif /* CONFIG_ZMK_USB */
+
+#ifdef CONFIG_ZMK_BLE
+ err = zmk_hog_send_keypad_report(&keypad_report->body);
+ if (err)
+ {
+ LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
+ }
+#endif /* CONFIG_ZMK_BLE */
+
+ break;
+ case Consumer:
+ consumer_report = zmk_hid_get_consumer_report();
+#ifdef CONFIG_ZMK_USB
+ if (zmk_usb_hid_send_report((u8_t *)consumer_report, sizeof(struct zmk_hid_consumer_report)) != 0)
+ {
+ LOG_DBG("USB Send Failed");
+ }
+#endif /* CONFIG_ZMK_USB */
+
+#ifdef CONFIG_ZMK_BLE
+ err = zmk_hog_send_consumer_report(&consumer_report->body);
+ if (err)
+ {
+ LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
+ }
+#endif /* CONFIG_ZMK_BLE */
+
+ break;
+ default:
+ LOG_ERR("Unknown report change type %d", report_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int zmk_endpoints_send_key_event(struct zmk_key_event key_event)
+{
+ enum zmk_hid_report_changes changes;
+
+ LOG_DBG("key %d, state %d\n", key_event.key, key_event.pressed);
+
+ if (key_event.pressed)
+ {
+ changes = zmk_hid_press_key(key_event.key);
+ }
+ else
+ {
+ changes = zmk_hid_release_key(key_event.key);
+ }
+
+ return zmk_endpoints_send_report(changes);
+}
diff --git a/app/src/handlers.c b/app/src/handlers.c
new file mode 100644
index 0000000..464b1ab
--- /dev/null
+++ b/app/src/handlers.c
@@ -0,0 +1,88 @@
+
+#include <zmk/ble.h>
+#include <zmk/handlers.h>
+#include <zmk/endpoints.h>
+#include <zmk/hid.h>
+#include <zmk/matrix.h>
+
+#ifdef CONFIG_ZMK_ACTION_MOD_TAP
+u16_t action_effect_pending = 0;
+#endif
+
+__attribute__((weak)) bool zmk_handle_key_user(struct zmk_key_event *key_event)
+{
+ return true;
+};
+
+bool zmk_handle_action(zmk_action action, struct zmk_key_event *key_event)
+{
+ zmk_mod mods = ZK_MODS(key_event->key);
+ u8_t flattened_index = (key_event->row * ZMK_MATRIX_COLS) + key_event->column;
+ switch (action)
+ {
+#ifdef CONFIG_ZMK_ACTION_MOD_TAP
+ case ZMK_ACTION_MOD_TAP:
+ if (key_event->pressed)
+ {
+ WRITE_BIT(action_effect_pending, flattened_index, true);
+ zmk_hid_register_mods(mods);
+ }
+ else
+ {
+ zmk_hid_unregister_mods(mods);
+ if (action_effect_pending & BIT(flattened_index))
+ {
+ struct zmk_key_event non_mod_event =
+ {
+ .row = key_event->row,
+ .column = key_event->column,
+ .key = ZK_KEY(key_event->key),
+ .pressed = true};
+
+ zmk_handle_key(non_mod_event);
+ // A small sleep is needed to ensure device layer sends initial
+ // key, before we send the release.
+ k_msleep(10);
+ non_mod_event.pressed = false;
+ zmk_handle_key(non_mod_event);
+ }
+ else
+ {
+ // Since not sending a keycode, at least send the report w/ the mod removed
+ zmk_endpoints_send_report(Keypad);
+ }
+ }
+ break;
+#endif
+ }
+ return false;
+};
+
+void zmk_handle_key(struct zmk_key_event key_event)
+{
+ zmk_action action = ZK_ACTION(key_event.key);
+
+ if (!zmk_handle_key_user(&key_event))
+ {
+ return;
+ }
+
+ if (action && !zmk_handle_action(action, &key_event))
+ {
+ return;
+ }
+
+#ifdef CONFIG_ZMK_ACTION_MOD_TAP
+ action_effect_pending = 0;
+#endif
+
+#ifdef CONFIG_ZMK_BLE
+ /* Used for intercepting key presses when doing passkey verification */
+ if (!zmk_ble_handle_key_user(&key_event))
+ {
+ return;
+ }
+#endif /* CONFIG_ZMK_BLE */
+
+ zmk_endpoints_send_key_event(key_event);
+};
diff --git a/app/src/hid.c b/app/src/hid.c
new file mode 100644
index 0000000..b3d47cf
--- /dev/null
+++ b/app/src/hid.c
@@ -0,0 +1,130 @@
+#include <logging/log.h>
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#include <zmk/hid.h>
+
+static struct zmk_hid_keypad_report kp_report = {
+ .report_id = 1,
+ .body = {
+ .modifiers = 0,
+ .keys = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}};
+
+static struct zmk_hid_consumer_report consumer_report = {
+ .report_id = 2,
+ .body = {
+ .keys = 0x00}};
+
+#define _TOGGLE_MOD(mod, state) \
+ if (modifier > MOD_RGUI) \
+ { \
+ return -EINVAL; \
+ } \
+ WRITE_BIT(kp_report.body.modifiers, mod, state); \
+ return 0;
+
+int zmk_hid_register_mod(zmk_mod modifier)
+{
+ _TOGGLE_MOD(modifier, true);
+}
+int zmk_hid_unregister_mod(zmk_mod modifier)
+{
+ _TOGGLE_MOD(modifier, false);
+}
+
+int zmk_hid_register_mods(zmk_mod_flags modifiers)
+{
+ kp_report.body.modifiers |= modifiers;
+ return 0;
+}
+
+int zmk_hid_unregister_mods(zmk_mod_flags modifiers)
+{
+ kp_report.body.modifiers &= ~modifiers;
+ return 0;
+}
+
+#define KEY_OFFSET 0x02
+#define MAX_KEYS 6
+
+/*
+#define TOGGLE_BOOT_KEY(match, val) \
+ for (int idx = 0; idx < MAX_KEYS; idx++) \
+ { \
+ if (kp_report.boot.keys[idx + KEY_OFFSET] != match) \
+ { \
+ continue; \
+ } \
+ kp_report.boot.keys[idx + KEY_OFFSET] = val; \
+ break; \
+ }
+*/
+
+#define TOGGLE_KEY(code, val) WRITE_BIT(kp_report.body.keys[code / 8], code % 8, val)
+
+#define TOGGLE_CONSUMER(key, state) \
+ WRITE_BIT(consumer_report.body.keys, (key - 0x100), state);
+
+enum zmk_hid_report_changes zmk_hid_press_key(zmk_key code)
+{
+ if (code >= KC_LCTL && code <= KC_RGUI)
+ {
+ return zmk_hid_register_mod(code - KC_LCTL);
+ }
+
+ if (ZK_IS_CONSUMER(code))
+ {
+ LOG_DBG("Toggling a consumer key!");
+ TOGGLE_CONSUMER(code, true);
+ return Consumer;
+ }
+ else
+ {
+ if (code > ZMK_HID_MAX_KEYCODE)
+ {
+ return -EINVAL;
+ }
+
+ // TOGGLE_BOOT_KEY(0U, code);
+
+ TOGGLE_KEY(code, true);
+
+ return Keypad;
+ }
+};
+
+enum zmk_hid_report_changes zmk_hid_release_key(zmk_key code)
+{
+ if (code >= KC_LCTL && code <= KC_RGUI)
+ {
+ return zmk_hid_unregister_mod(code - KC_LCTL);
+ }
+
+ if (ZK_IS_CONSUMER(code))
+ {
+ TOGGLE_CONSUMER(code, false);
+ return Consumer;
+ }
+ else
+ {
+ if (code > ZMK_HID_MAX_KEYCODE)
+ {
+ return -EINVAL;
+ }
+
+ // TOGGLE_BOOT_KEY(0U, code);
+
+ TOGGLE_KEY(code, false);
+
+ return Keypad;
+ }
+};
+
+struct zmk_hid_keypad_report *zmk_hid_get_keypad_report()
+{
+ return &kp_report;
+}
+
+struct zmk_hid_consumer_report *zmk_hid_get_consumer_report()
+{
+ return &consumer_report;
+}
diff --git a/app/src/hog.c b/app/src/hog.c
new file mode 100644
index 0000000..087af42
--- /dev/null
+++ b/app/src/hog.c
@@ -0,0 +1,158 @@
+#include <settings/settings.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/gatt.h>
+
+#include <zmk/ble.h>
+#include <zmk/hog.h>
+#include <zmk/hid.h>
+
+int zmk_hog_init()
+{
+ return zmk_ble_init();
+}
+
+enum
+{
+ HIDS_REMOTE_WAKE = BIT(0),
+ HIDS_NORMALLY_CONNECTABLE = BIT(1),
+};
+
+struct hids_info
+{
+ u16_t version; /* version number of base USB HID Specification */
+ u8_t code; /* country HID Device hardware is localized for. */
+ u8_t flags;
+} __packed;
+
+struct hids_report
+{
+ u8_t id; /* report id */
+ u8_t type; /* report type */
+} __packed;
+
+static struct hids_info info = {
+ .version = 0x0000,
+ .code = 0x00,
+ .flags = HIDS_NORMALLY_CONNECTABLE & HIDS_REMOTE_WAKE,
+};
+
+enum
+{
+ HIDS_INPUT = 0x01,
+ HIDS_OUTPUT = 0x02,
+ HIDS_FEATURE = 0x03,
+};
+
+static struct hids_report input = {
+ .id = 0x01,
+ .type = HIDS_INPUT,
+};
+
+static struct hids_report consumer_input = {
+ .id = 0x02,
+ .type = HIDS_INPUT,
+};
+
+static bool host_requests_notification = false;
+static u8_t ctrl_point;
+// static u8_t proto_mode;
+
+static ssize_t read_hids_info(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset)
+{
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data, sizeof(struct hids_info));
+}
+
+static ssize_t read_hids_report_ref(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset)
+{
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data, sizeof(struct hids_report));
+}
+
+static ssize_t read_hids_report_map(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset)
+{
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, zmk_hid_report_desc, sizeof(zmk_hid_report_desc));
+}
+
+static ssize_t read_hids_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset)
+{
+ struct zmk_hid_keypad_report_body *report_body = &zmk_hid_get_keypad_report()->body;
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body, sizeof(struct zmk_hid_keypad_report_body));
+}
+
+static ssize_t read_hids_consumer_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset)
+{
+ struct zmk_hid_consumer_report_body *report_body = &zmk_hid_get_consumer_report()->body;
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body, sizeof(struct zmk_hid_consumer_report_body));
+}
+
+// static ssize_t write_proto_mode(struct bt_conn *conn,
+// const struct bt_gatt_attr *attr,
+// const void *buf, u16_t len, u16_t offset,
+// u8_t flags)
+// {
+// printk("PROTO CHANGED\n");
+// return 0;
+// }
+
+static void input_ccc_changed(const struct bt_gatt_attr *attr, u16_t value)
+{
+ host_requests_notification = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0;
+}
+
+static ssize_t write_ctrl_point(struct bt_conn *conn,
+ const struct bt_gatt_attr *attr,
+ const void *buf, u16_t len, u16_t offset,
+ u8_t flags)
+{
+ u8_t *value = attr->user_data;
+
+ if (offset + len > sizeof(ctrl_point))
+ {
+ return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
+ }
+
+ memcpy(value + offset, buf, len);
+
+ return len;
+}
+
+/* HID Service Declaration */
+BT_GATT_SERVICE_DEFINE(hog_svc,
+ BT_GATT_PRIMARY_SERVICE(BT_UUID_HIDS),
+ // BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_PROTOCOL_MODE, BT_GATT_CHRC_WRITE_WITHOUT_RESP,
+ // BT_GATT_PERM_WRITE, NULL, write_proto_mode, &proto_mode),
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_INFO, BT_GATT_CHRC_READ,
+ BT_GATT_PERM_READ, read_hids_info, NULL, &info),
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT_MAP, BT_GATT_CHRC_READ,
+ BT_GATT_PERM_READ, read_hids_report_map, NULL, NULL),
+
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
+ BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
+ BT_GATT_PERM_READ_ENCRYPT,
+ read_hids_input_report, NULL, NULL),
+ BT_GATT_CCC(input_ccc_changed,
+ BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
+ BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ,
+ read_hids_report_ref, NULL, &input),
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
+ BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
+ BT_GATT_PERM_READ_ENCRYPT,
+ read_hids_consumer_input_report, NULL, NULL),
+ BT_GATT_CCC(input_ccc_changed,
+ BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
+ BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ,
+ read_hids_report_ref, NULL, &consumer_input),
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT,
+ BT_GATT_CHRC_WRITE_WITHOUT_RESP,
+ BT_GATT_PERM_WRITE,
+ NULL, write_ctrl_point, &ctrl_point));
+
+int zmk_hog_send_keypad_report(struct zmk_hid_keypad_report_body *report)
+{
+ return bt_gatt_notify(NULL, &hog_svc.attrs[5], report, sizeof(struct zmk_hid_keypad_report_body));
+};
+
+int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report)
+{
+ return bt_gatt_notify(NULL, &hog_svc.attrs[10], report, sizeof(struct zmk_hid_consumer_report_body));
+};
diff --git a/app/src/keymap.c b/app/src/keymap.c
new file mode 100644
index 0000000..569a2cc
--- /dev/null
+++ b/app/src/keymap.c
@@ -0,0 +1,74 @@
+
+#include <logging/log.h>
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+#include <zmk/keymap.h>
+
+static u32_t zmk_keymap_layer_state = 0;
+static u8_t zmk_keymap_layer_default = 0;
+
+static zmk_key zmk_keymap[ZMK_KEYMAP_LAYERS_LEN][ZMK_MATRIX_ROWS * ZMK_MATRIX_COLS] = {
+#if DT_PROP_HAS_IDX(ZMK_KEYMAP_NODE, layers, 0)
+ DT_PROP_BY_PHANDLE_IDX(ZMK_KEYMAP_NODE, layers, 0, keys),
+#endif
+#if DT_PROP_HAS_IDX(ZMK_KEYMAP_NODE, layers, 1)
+ DT_PROP_BY_PHANDLE_IDX(ZMK_KEYMAP_NODE, layers, 1, keys),
+#endif
+#if DT_PROP_HAS_IDX(ZMK_KEYMAP_NODE, layers, 2)
+ DT_PROP_BY_PHANDLE_IDX(ZMK_KEYMAP_NODE, layers, 2, keys),
+#endif
+#if DT_PROP_HAS_IDX(ZMK_KEYMAP_NODE, layers, 3)
+ DT_PROP_BY_PHANDLE_IDX(ZMK_KEYMAP_NODE, layers, 3, keys),
+#endif
+#if DT_PROP_HAS_IDX(ZMK_KEYMAP_NODE, layers, 4)
+ DT_PROP_BY_PHANDLE_IDX(ZMK_KEYMAP_NODE, layers, 4, keys),
+#endif
+#if DT_PROP_HAS_IDX(ZMK_KEYMAP_NODE, layers, 5)
+ DT_PROP_BY_PHANDLE_IDX(ZMK_KEYMAP_NODE, layers, 5, keys),
+#endif
+#if DT_PROP_HAS_IDX(ZMK_KEYMAP_NODE, layers, 6)
+ DT_PROP_BY_PHANDLE_IDX(ZMK_KEYMAP_NODE, layers, 6, keys),
+#endif
+#if DT_PROP_HAS_IDX(ZMK_KEYMAP_NODE, layers, 7)
+ DT_PROP_BY_PHANDLE_IDX(ZMK_KEYMAP_NODE, layers, 7, keys),
+#endif
+};
+
+#define SET_LAYER_STATE(layer, state) \
+ if (layer >= 32) \
+ { \
+ return false; \
+ } \
+ WRITE_BIT(zmk_keymap_layer_state, layer, state); \
+ return true;
+
+bool zmk_keymap_layer_activate(u8_t layer)
+{
+ SET_LAYER_STATE(layer, true);
+};
+
+bool zmk_keymap_layer_deactivate(u8_t layer)
+{
+ SET_LAYER_STATE(layer, false);
+};
+
+zmk_key zmk_keymap_keycode_from_position(u32_t row, u32_t column)
+{
+ for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= zmk_keymap_layer_default; layer--)
+ {
+ if ((zmk_keymap_layer_state & BIT(layer)) == BIT(layer) || layer == zmk_keymap_layer_default)
+ {
+ u8_t key_index = (row * ZMK_MATRIX_COLS) + column;
+ LOG_DBG("Getting key at index %d", key_index);
+
+ zmk_key key = zmk_keymap[layer][key_index];
+ if (key == ZC_TRNS)
+ {
+ continue;
+ }
+
+ return key;
+ }
+ }
+
+ return ZC_NO;
+}
diff --git a/app/src/kscan.c b/app/src/kscan.c
new file mode 100644
index 0000000..1f54a14
--- /dev/null
+++ b/app/src/kscan.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Peter Johanson
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <zephyr.h>
+#include <device.h>
+#include <drivers/kscan.h>
+#include <logging/log.h>
+
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#include <zmk/keymap.h>
+#include <zmk/handlers.h>
+
+#define ZMK_KSCAN_EVENT_STATE_PRESSED 0
+#define ZMK_KSCAN_EVENT_STATE_RELEASED 1
+
+struct zmk_kscan_event
+{
+ u32_t row;
+ u32_t column;
+ u32_t state;
+};
+
+struct zmk_kscan_msg_processor
+{
+ struct k_work work;
+} msg_processor;
+
+K_MSGQ_DEFINE(zmk_kscan_msgq, sizeof(struct zmk_kscan_event), CONFIG_ZMK_KSCAN_EVENT_QUEUE_SIZE, 4);
+
+static void zmk_kscan_callback(struct device *dev, u32_t row, u32_t column, bool pressed)
+{
+ struct zmk_kscan_event ev = {
+ .row = row,
+ .column = column,
+ .state = (pressed ? ZMK_KSCAN_EVENT_STATE_PRESSED : ZMK_KSCAN_EVENT_STATE_RELEASED)};
+
+ k_msgq_put(&zmk_kscan_msgq, &ev, K_NO_WAIT);
+ k_work_submit(&msg_processor.work);
+}
+
+void zmk_kscan_process_msgq(struct k_work *item)
+{
+ struct zmk_kscan_event ev;
+
+ while (k_msgq_get(&zmk_kscan_msgq, &ev, K_NO_WAIT) == 0)
+ {
+ bool pressed = (ev.state == ZMK_KSCAN_EVENT_STATE_PRESSED);
+ zmk_key key = zmk_keymap_keycode_from_position(ev.row, ev.column);
+ struct zmk_key_event kev = (struct zmk_key_event){.row = ev.row, .column = ev.column, .key = key, .pressed = pressed};
+
+ LOG_DBG("Row: %d, col: %d, key: %d, pressed: %s\n", ev.row, ev.column, key, (pressed ? "true" : "false"));
+ zmk_handle_key(kev);
+ }
+}
+
+int zmk_kscan_init(char *name)
+{
+ struct device *dev = device_get_binding(name);
+ if (dev == NULL)
+ {
+ LOG_ERR("Failed to get the KSCAN device");
+ return -EINVAL;
+ }
+
+ k_work_init(&msg_processor.work, zmk_kscan_process_msgq);
+
+ kscan_config(dev, zmk_kscan_callback);
+ kscan_enable_callback(dev);
+
+ return 0;
+}
diff --git a/app/src/kscan_composite.c b/app/src/kscan_composite.c
new file mode 100644
index 0000000..d46484b
--- /dev/null
+++ b/app/src/kscan_composite.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define DT_DRV_COMPAT zmk_kscan_composite
+
+#include <device.h>
+#include <drivers/kscan.h>
+#include <logging/log.h>
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#define MATRIX_NODE_ID DT_DRV_INST(0)
+#define MATRIX_ROWS DT_PROP(MATRIX_NODE_ID, rows)
+#define MATRIX_COLS DT_PROP(MATRIX_NODE_ID, columns)
+
+struct kscan_composite_child_config
+{
+ char *label;
+ u8_t row_offset;
+ u8_t column_offset;
+};
+
+#define CHILD_CONFIG(inst) \
+ { \
+ .label = DT_LABEL(DT_PHANDLE(inst, kscan)), \
+ .row_offset = DT_PROP(inst, row_offset), \
+ .column_offset = DT_PROP(inst, column_offset)},
+
+const struct kscan_composite_child_config kscan_composite_children[] = {
+ DT_FOREACH_CHILD(MATRIX_NODE_ID, CHILD_CONFIG)};
+
+struct kscan_composite_config
+{
+};
+
+struct kscan_composite_data
+{
+ kscan_callback_t callback;
+
+ struct device *dev;
+};
+
+static int kscan_composite_enable_callback(struct device *dev)
+{
+ for (int i = 0; i < sizeof(kscan_composite_children) / sizeof(kscan_composite_children[0]); i++)
+ {
+ const struct kscan_composite_child_config *cfg = &kscan_composite_children[i];
+
+ kscan_enable_callback(device_get_binding(cfg->label));
+ }
+ return 0;
+}
+
+static int kscan_composite_disable_callback(struct device *dev)
+{
+ for (int i = 0; i < sizeof(kscan_composite_children) / sizeof(kscan_composite_children[0]); i++)
+ {
+ const struct kscan_composite_child_config *cfg = &kscan_composite_children[i];
+
+ kscan_disable_callback(device_get_binding(cfg->label));
+ }
+ return 0;
+}
+
+static void kscan_composite_child_callback(struct device *child_dev, u32_t row, u32_t column, bool pressed)
+{
+ // TODO: Ideally we can get this passed into our callback!
+ struct device *dev = device_get_binding(DT_INST_LABEL(0));
+ struct kscan_composite_data *data = dev->driver_data;
+
+ for (int i = 0; i < sizeof(kscan_composite_children) / sizeof(kscan_composite_children[0]); i++)
+ {
+ const struct kscan_composite_child_config *cfg = &kscan_composite_children[i];
+
+ if (device_get_binding(cfg->label) != child_dev)
+ {
+ continue;
+ }
+
+ data->callback(dev, row + cfg->row_offset, column + cfg->column_offset, pressed);
+ }
+}
+
+static int kscan_composite_configure(struct device *dev, kscan_callback_t callback)
+{
+ struct kscan_composite_data *data = dev->driver_data;
+
+ if (!callback)
+ {
+ return -EINVAL;
+ }
+
+ for (int i = 0; i < sizeof(kscan_composite_children) / sizeof(kscan_composite_children[0]); i++)
+ {
+ const struct kscan_composite_child_config *cfg = &kscan_composite_children[i];
+
+ kscan_config(device_get_binding(cfg->label), &kscan_composite_child_callback);
+ }
+
+ data->callback = callback;
+
+ return 0;
+}
+
+static int kscan_composite_init(struct device *dev)
+{
+ struct kscan_composite_data *data = dev->driver_data;
+
+ data->dev = dev;
+
+ return 0;
+}
+
+static const struct kscan_driver_api mock_driver_api = {
+ .config = kscan_composite_configure,
+ .enable_callback = kscan_composite_enable_callback,
+ .disable_callback = kscan_composite_disable_callback,
+};
+
+static const struct kscan_composite_config kscan_composite_config = {};
+
+static struct kscan_composite_data kscan_composite_data;
+
+DEVICE_AND_API_INIT(kscan_composite, DT_INST_LABEL(0), kscan_composite_init,
+ &kscan_composite_data,
+ &kscan_composite_config,
+ APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
+ &mock_driver_api);
diff --git a/app/src/kscan_mock.c b/app/src/kscan_mock.c
new file mode 100644
index 0000000..7d2d24d
--- /dev/null
+++ b/app/src/kscan_mock.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define DT_DRV_COMPAT zmk_kscan_mock
+
+#include <device.h>
+#include <drivers/kscan.h>
+#include <logging/log.h>
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#include <zmk/kscan-mock.h>
+
+struct kscan_mock_data
+{
+ kscan_callback_t callback;
+
+ u8_t event_index;
+ struct k_delayed_work work;
+ struct device *dev;
+};
+
+static int kscan_mock_disable_callback(struct device *dev)
+{
+ struct kscan_mock_data *data = dev->driver_data;
+
+ k_delayed_work_cancel(&data->work);
+ return 0;
+}
+
+static int kscan_mock_configure(struct device *dev, kscan_callback_t callback)
+{
+ struct kscan_mock_data *data = dev->driver_data;
+
+ if (!callback)
+ {
+ return -EINVAL;
+ }
+
+ data->event_index = 0;
+ data->callback = callback;
+
+ return 0;
+}
+
+#define MOCK_INST_INIT(n) \
+ struct kscan_mock_config_##n \
+ { \
+ u32_t events[DT_INST_PROP_LEN(n, events)]; \
+ }; \
+ static void kscan_mock_schedule_next_event_##n(struct device *dev) \
+ { \
+ struct kscan_mock_data *data = dev->driver_data; \
+ const struct kscan_mock_config_##n *cfg = dev->config_info; \
+ if (data->event_index < DT_INST_PROP_LEN(n, events)) \
+ { \
+ u32_t ev = cfg->events[data->event_index]; \
+ LOG_DBG("delaying next keypress: %d", ZMK_MOCK_MSEC(ev)); \
+ k_delayed_work_submit(&data->work, K_MSEC(ZMK_MOCK_MSEC(ev))); \
+ } \
+ } \
+ static void kscan_mock_work_handler_##n(struct k_work *work) \
+ { \
+ struct kscan_mock_data *data = \
+ CONTAINER_OF(work, struct kscan_mock_data, work); \
+ const struct kscan_mock_config_##n *cfg = data->dev->config_info; \
+ u32_t ev = cfg->events[data->event_index++]; \
+ LOG_DBG("ev %u row %d column %d state %d\n", ev, \
+ ZMK_MOCK_ROW(ev), ZMK_MOCK_COL(ev), ZMK_MOCK_IS_PRESS(ev)); \
+ data->callback(data->dev, \
+ ZMK_MOCK_ROW(ev), ZMK_MOCK_COL(ev), ZMK_MOCK_IS_PRESS(ev)); \
+ kscan_mock_schedule_next_event_##n(data->dev); \
+ } \
+ static int kscan_mock_init_##n(struct device *dev) \
+ { \
+ struct kscan_mock_data *data = dev->driver_data; \
+ data->dev = dev; \
+ k_delayed_work_init(&data->work, kscan_mock_work_handler_##n); \
+ return 0; \
+ } \
+ static int kscan_mock_enable_callback_##n(struct device *dev) \
+ { \
+ kscan_mock_schedule_next_event_##n(dev); \
+ return 0; \
+ } \
+ static const struct kscan_driver_api mock_driver_api_##n = { \
+ .config = kscan_mock_configure, \
+ .enable_callback = kscan_mock_enable_callback_##n, \
+ .disable_callback = kscan_mock_disable_callback, \
+ }; \
+ static struct kscan_mock_data kscan_mock_data_##n; \
+ static const struct kscan_mock_config_##n kscan_mock_config_##n = { \
+ .events = DT_INST_PROP(n, events)}; \
+ DEVICE_AND_API_INIT(kscan_mock_##n, DT_INST_LABEL(n), kscan_mock_init_##n, \
+ &kscan_mock_data_##n, \
+ &kscan_mock_config_##n, \
+ APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
+ &mock_driver_api_##n);
+
+DT_INST_FOREACH_STATUS_OKAY(MOCK_INST_INIT) \ No newline at end of file
diff --git a/app/src/main.c b/app/src/main.c
new file mode 100644
index 0000000..1ced310
--- /dev/null
+++ b/app/src/main.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Peter Johanson
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <zephyr.h>
+#include <device.h>
+#include <devicetree.h>
+#include <settings/settings.h>
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#include <zmk/matrix.h>
+#include <zmk/kscan.h>
+#include <zmk/endpoints.h>
+
+#define ZMK_KSCAN_DEV DT_LABEL(ZMK_MATRIX_NODE_ID)
+
+void main(void)
+{
+ printk("Welcome to ZMK!\n");
+
+ if (zmk_kscan_init(ZMK_KSCAN_DEV) != 0)
+ {
+ return;
+ }
+
+ if (zmk_endpoints_init())
+ {
+ printk("ENDPOINT INIT FAILED\n");
+ return;
+ }
+
+#ifdef CONFIG_SETTINGS
+ settings_load();
+#endif
+}
diff --git a/app/src/usb_hid.c b/app/src/usb_hid.c
new file mode 100644
index 0000000..2862d56
--- /dev/null
+++ b/app/src/usb_hid.c
@@ -0,0 +1,58 @@
+
+#include <device.h>
+
+#include <usb/usb_device.h>
+#include <usb/class/usb_hid.h>
+#include <dt-bindings/zmk/keys.h>
+
+#include <zmk/hid.h>
+#include <zmk/keymap.h>
+
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+static enum usb_dc_status_code usb_status;
+
+static struct device *hid_dev;
+
+int zmk_usb_hid_send_report(const u8_t *report, size_t len)
+{
+ if (usb_status == USB_DC_SUSPEND)
+ {
+ return usb_wakeup_request();
+ }
+
+ return hid_int_ep_write(hid_dev, report, len, NULL);
+}
+
+void usb_hid_status_cb(enum usb_dc_status_code status, const u8_t *params)
+{
+ usb_status = status;
+};
+
+int zmk_usb_hid_init()
+{
+ int usb_enable_ret;
+
+ hid_dev = device_get_binding("HID_0");
+ if (hid_dev == NULL)
+ {
+ LOG_ERR("Unable to locate HID device");
+ return -EINVAL;
+ }
+
+ usb_hid_register_device(hid_dev,
+ zmk_hid_report_desc, sizeof(zmk_hid_report_desc),
+ NULL);
+
+ usb_hid_init(hid_dev);
+
+ usb_enable_ret = usb_enable(usb_hid_status_cb);
+
+ if (usb_enable_ret != 0)
+ {
+ LOG_ERR("Unable to enable USB");
+ return -EINVAL;
+ }
+
+ return 0;
+}