diff options
Diffstat (limited to 'app/src')
-rw-r--r-- | app/src/ble.c | 193 | ||||
-rw-r--r-- | app/src/endpoints.c | 105 | ||||
-rw-r--r-- | app/src/handlers.c | 88 | ||||
-rw-r--r-- | app/src/hid.c | 130 | ||||
-rw-r--r-- | app/src/hog.c | 158 | ||||
-rw-r--r-- | app/src/keymap.c | 74 | ||||
-rw-r--r-- | app/src/kscan.c | 75 | ||||
-rw-r--r-- | app/src/kscan_composite.c | 130 | ||||
-rw-r--r-- | app/src/kscan_mock.c | 102 | ||||
-rw-r--r-- | app/src/main.c | 39 | ||||
-rw-r--r-- | app/src/usb_hid.c | 58 |
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; +} |