diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 5be628b7..3f2065f6 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -42,6 +42,8 @@ target_sources(app PRIVATE src/behaviors/behavior_mod_tap.c) target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c) target_sources(app PRIVATE src/behaviors/behavior_transparent.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble.c) +target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE app PRIVATE src/split_listener.c) +target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE app PRIVATE src/split/bluetooth/service.c) target_sources_ifdef(CONFIG_ZMK_KSCAN_MOCK_DRIVER app PRIVATE src/kscan_mock.c) target_sources_ifdef(CONFIG_ZMK_KSCAN_COMPOSITE_DRIVER app PRIVATE src/kscan_composite.c) target_sources_ifdef(CONFIG_ZMK_USB app PRIVATE src/usb_hid.c) diff --git a/app/Kconfig b/app/Kconfig index e6dc3bd5..10d98e6c 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -72,6 +72,25 @@ endif endmenu + +menu "Split Support" + +config ZMK_SPLIT + bool "Split keyboard support" + default n + +if ZMK_SPLIT + +config ZMK_SPLIT_BLE + bool "Split keyboard support via BLE transport" + depends on ZMK_BLE + default y + +endif + +endmenu + + config ZMK_KSCAN_MOCK_DRIVER bool "Enable mock kscan driver to simulate key presses" default n diff --git a/app/boards/shields/kyria/Kconfig.defconfig b/app/boards/shields/kyria/Kconfig.defconfig index 25af5376..9ce12d96 100644 --- a/app/boards/shields/kyria/Kconfig.defconfig +++ b/app/boards/shields/kyria/Kconfig.defconfig @@ -4,4 +4,7 @@ if SHIELD_KYRIA_LEFT || SHIELD_KYRIA_RIGHT config ZMK_KEYBOARD_NAME default "Kyria" +config ZMK_SPLIT + default y + endif diff --git a/app/boards/shields/kyria/kyria.dtsi b/app/boards/shields/kyria/kyria.dtsi index f96adf0f..f2f2d750 100644 --- a/app/boards/shields/kyria/kyria.dtsi +++ b/app/boards/shields/kyria/kyria.dtsi @@ -4,6 +4,7 @@ * SPDX-License-Identifier: MIT */ +#include #include / { diff --git a/app/dts/behaviors/split_bt.dtsi b/app/dts/behaviors/split_bt.dtsi new file mode 100644 index 00000000..c31d9579 --- /dev/null +++ b/app/dts/behaviors/split_bt.dtsi @@ -0,0 +1,9 @@ +/ { + behaviors { + split_behavior: behavior_split_bt { + compatible = "zmk,behavior-split-bt", "zmk,behavior-global"; + label = "SPLIT_BT"; + #binding-cells = <0>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-split-bt.yaml b/app/dts/bindings/behaviors/zmk,behavior-split-bt.yaml new file mode 100644 index 00000000..66d9630f --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-split-bt.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020, Pete Johanson +# SPDX-License-Identifier: MIT + +description: Split Bluetooth Behavior + +compatible: "zmk,behavior-split-bt" + +include: zero_param.yaml diff --git a/app/include/zmk/split/bluetooth/service.h b/app/include/zmk/split/bluetooth/service.h new file mode 100644 index 00000000..694afc16 --- /dev/null +++ b/app/include/zmk/split/bluetooth/service.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifndef BT_UUID_NUM_OF_DIGITALS +#define BT_UUID_NUM_OF_DIGITALS BT_UUID_DECLARE_16(0x2909) +#endif + +#define ZMK_SPLIT_BT_BASE_UUID 0x2a, 0x48, 0xc2, 0xb1, 0xcf, 0xc5, 0x67, 0xc9, 0x07, 0x71, 0x96, 0x00 +#define ZMK_SPLIT_BT_SERVICE_UUID ZMK_SPLIT_BT_BASE_UUID, 0x00, 0x00, 0x00, 0x00 +#define ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID ZMK_SPLIT_BT_BASE_UUID, 0x01, 0x00, 0x00, 0x00 + +#define ZMK_BT_UUID_SPLIT BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SERVICE_UUID) +#define ZMK_BT_UUID_SPLIT_POS_STATE BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID) + +int zmk_split_bt_position_pressed(u8_t position); +int zmk_split_bt_position_released(u8_t position); \ No newline at end of file diff --git a/app/src/ble.c b/app/src/ble.c index c0e81a96..94a23da9 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -12,6 +12,7 @@ #include #include +#include static struct bt_conn *auth_passkey_entry_conn; static u8_t passkey_entries[6] = {0, 0, 0, 0, 0, 0}; @@ -121,9 +122,13 @@ static struct bt_conn_auth_cb zmk_ble_auth_cb_display = { 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, + BT_DATA_BYTES(BT_DATA_UUID16_SOME, 0x12, 0x18, /* HID Service */ 0x0f, 0x18), /* Battery Service */ +#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE) + BT_DATA_BYTES(BT_DATA_UUID128_SOME, + ZMK_SPLIT_BT_SERVICE_UUID) +#endif }; static void zmk_ble_ready(int err) diff --git a/app/src/split/bluetooth/service.c b/app/src/split/bluetooth/service.c new file mode 100644 index 00000000..a669723d --- /dev/null +++ b/app/src/split/bluetooth/service.c @@ -0,0 +1,49 @@ + +#include +#include +#include + +#include +#include + +static u8_t num_of_positions = ZMK_KEYMAP_LEN; +static u8_t position_state[16]; + +static ssize_t split_svc_pos_state(struct bt_conn *conn, const struct bt_gatt_attr *attrs, void *buf, u16_t len, u16_t offset) +{ + return bt_gatt_attr_read(conn, attrs, buf, len, offset, &position_state, sizeof(position_state)); +} + +static ssize_t split_svc_num_of_positions(struct bt_conn *conn, const struct bt_gatt_attr *attrs, void *buf, u16_t len, u16_t offset) +{ + return bt_gatt_attr_read(conn, attrs, buf, len, offset, attrs->user_data, sizeof(u8_t)); +} + +static void split_svc_pos_state_ccc(const struct bt_gatt_attr *attr, u16_t value) +{ +} + + +BT_GATT_SERVICE_DEFINE(split_svc, + BT_GATT_PRIMARY_SERVICE(ZMK_BT_UUID_SPLIT), + BT_GATT_CHARACTERISTIC(ZMK_BT_UUID_SPLIT_POS_STATE, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ_ENCRYPT, + split_svc_pos_state, NULL, &position_state), + BT_GATT_CCC(split_svc_pos_state_ccc, + BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), + BT_GATT_DESCRIPTOR(BT_UUID_NUM_OF_DIGITALS, BT_GATT_PERM_READ, + split_svc_num_of_positions, NULL, &num_of_positions), +); + +int zmk_split_bt_position_pressed(u8_t position) +{ + WRITE_BIT(position_state[position / 8], position % 8, true); + return bt_gatt_notify(NULL, &split_svc.attrs[1], &position_state, sizeof(position_state)); +} + +int zmk_split_bt_position_released(u8_t position) +{ + WRITE_BIT(position_state[position / 8], position % 8, false); + // WRITE_BIT(position_state, position, false); + return bt_gatt_notify(NULL, &split_svc.attrs[1], &position_state, sizeof(position_state)); +} \ No newline at end of file diff --git a/app/src/split_listener.c b/app/src/split_listener.c new file mode 100644 index 00000000..65f835a2 --- /dev/null +++ b/app/src/split_listener.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Peter Johanson + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_split_listener + +#include +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include + +int split_listener(const struct zmk_event_header *eh) +{ + if (is_position_state_changed(eh)) { + const struct position_state_changed *ev = cast_position_state_changed(eh); + if (ev->state) { + zmk_split_bt_position_pressed(ev->position); + } else { + zmk_split_bt_position_released(ev->position); + } + } + return 0; +} + +ZMK_LISTENER(split_listener, split_listener); +ZMK_SUBSCRIPTION(split_listener, position_state_changed); \ No newline at end of file