Some initial work on behavior bindings for keymaps

This commit is contained in:
Pete Johanson 2020-06-19 15:32:33 -04:00
parent 865f41a46d
commit c23d752917
19 changed files with 370 additions and 64 deletions

View File

@ -1,6 +1,7 @@
# Find Zephyr. This also loads Zephyr's build system.
cmake_minimum_required(VERSION 3.13.1)
set(CONFIG_APPLICATION_DEFINED_SYSCALL true)
list(APPEND BOARD_ROOT ${CMAKE_SOURCE_DIR})
list(APPEND DTS_ROOT ${CMAKE_SOURCE_DIR})
@ -29,6 +30,8 @@ target_include_directories(app PRIVATE include)
target_sources(app PRIVATE src/kscan.c)
target_sources(app PRIVATE src/keymap.c)
target_sources(app PRIVATE src/hid.c)
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble.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)

View File

@ -6,6 +6,8 @@
/dts-v1/;
#include <nordic/nrf52840_qiaa.dtsi>
#include <behaviors/key_press.dtsi>
#include <behaviors/reset.dtsi>
#include "arduino_pro_micro_pins.dtsi"
/ {

View File

@ -1,4 +1,6 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors/key_press.dtsi>
#include <behaviors/reset.dtsi>
#include <keymap.h>
/ {
@ -21,6 +23,9 @@
KC_A MT(MOD_LSFT, KC_B) ZC_NO ZC_NO
CC_RAIS CC_LOWR ZC_NO ZC_NO
>;
bindings = <
&reset &kp 0
&kp 1 &kp 2>;
};
lower: layer_1 {
@ -29,6 +34,10 @@
KC_MPLY KC_MNXT ZC_NO ZC_NO
ZC_TRNS ZC_TRNS ZC_NO ZC_NO
>;
bindings = <
&reset &kp 0
&kp 1 &kp 2>;
};
raise: layer_2 {
@ -36,6 +45,10 @@
keys = <
KC_C KC_D ZC_NO ZC_NO
ZC_TRNS ZC_TRNS ZC_NO ZC_NO>;
bindings = <
&reset &kp 0
&kp 1 &kp 2>;
};
};
};

View File

@ -21,6 +21,9 @@
KC_A MT(MOD_LSFT, KC_B)
CC_RAIS CC_LOWR
>;
bindings = <
&reset &kp 0
&kp 1 &kp 2>;
};
lower: layer_1 {
@ -29,6 +32,9 @@
KC_MPLY KC_MNXT
ZC_TRNS ZC_TRNS
>;
bindings = <
&reset &kp 0
&kp 1 &kp 2>;
};
raise: layer_2 {
@ -37,6 +43,9 @@
KC_C KC_D
ZC_TRNS ZC_TRNS
>;
bindings = <
&reset &kp 0
&kp 1 &kp 2>;
};
};
};

View File

@ -0,0 +1,9 @@
/ {
behaviors {
kp: behavior_key_press {
compatible = "zmk,behavior-key-press";
label = "KEY_PRESS";
#binding-cells = <1>;
};
};
};

View File

@ -0,0 +1,9 @@
/ {
behaviors {
reset: behavior_reset {
compatible = "zmk,behavior-reset";
label = "RESET";
#binding-cells = <0>;
};
};
};

View File

@ -0,0 +1,14 @@
# Copyright (c) 2020, Pete Johanson
# SPDX-License-Identifier: MIT
properties:
label:
type: string
required: true
"#binding-cells":
type: int
required: true
const: 1
binding-cells:
- param1

View File

@ -0,0 +1,15 @@
# Copyright (c) 2020, Pete Johanson
# SPDX-License-Identifier: MIT
properties:
label:
type: string
required: true
"#binding-cells":
type: int
required: true
const: 2
binding-cells:
- param1
- param2

View File

@ -0,0 +1,11 @@
# Copyright (c) 2020, Pete Johanson
# SPDX-License-Identifier: MIT
properties:
label:
type: string
required: true
"#binding-cells":
type: int
required: true
const: 0

View File

@ -0,0 +1,8 @@
# Copyright (c) 2020, Pete Johanson
# SPDX-License-Identifier: MIT
description: Key press/release behavior
compatible: "zmk,behavior-key-press"
include: one_param.yaml

View File

@ -0,0 +1,8 @@
# Copyright (c) 2020, Pete Johanson
# SPDX-License-Identifier: MIT
description: Keyboard Reset Behavior
compatible: "zmk,behavior-reset"
include: zero_param.yaml

View File

@ -11,4 +11,5 @@ child-binding:
type: string
keys:
type: array
bindings:
type: phandle-array

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2020 Peter Johanson
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zephyr/types.h>
#include <stddef.h>
#include <device.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @cond INTERNAL_HIDDEN
*
* Behavior driver API definition and system call entry points.
*
* (Internal use only.)
*/
typedef int (*behavior_position_pressed_t)(struct device *dev, u32_t param1, u32_t param2);
typedef int (*behavior_position_released_t)(struct device *dev, u32_t param1, u32_t param2);
__subsystem struct behavior_driver_api {
behavior_position_pressed_t position_pressed;
behavior_position_released_t position_released;
};
/**
* @endcond
*/
/**
* @brief Handle the assigned position being pressed
* @param dev Pointer to the device structure for the driver instance.
* @param param1 User parameter specified at time of behavior assignment.
*
* @retval 0 If successful.
* @retval Negative errno code if failure.
*/
__syscall int behavior_position_pressed(struct device *dev, u32_t param1, u32_t param2);
static inline int z_impl_behavior_position_pressed(struct device *dev, u32_t param1, u32_t param2)
{
const struct behavior_driver_api *api =
(const struct behavior_driver_api *)dev->driver_api;
if (api->position_pressed == NULL) {
return -ENOTSUP;
}
return api->position_pressed(dev, param1, param2);
}
/**
* @brief Handle the assigned position being pressed
* @param dev Pointer to the device structure for the driver instance.
* @param param1 User parameter specified at time of behavior assignment.
*
* @retval 0 If successful.
* @retval Negative errno code if failure.
*/
__syscall int behavior_position_released(struct device *dev, u32_t param1, u32_t param2);
static inline int z_impl_behavior_position_released(struct device *dev, u32_t param1, u32_t param2)
{
const struct behavior_driver_api *api =
(const struct behavior_driver_api *)dev->driver_api;
if (api->position_released == NULL) {
return -ENOTSUP;
}
return api->position_released(dev, param1, param2);
}
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#include <syscalls/behavior.h>

View File

@ -6,23 +6,9 @@
#include <dt-bindings/zmk/keys.h>
#include <zmk/matrix.h>
#include <zmk/keys.h>
#define ZMK_KEYMAP_NODE DT_CHOSEN(zmk_keymap)
#define ZMK_KEYMAP_LAYERS_LEN DT_PROP_LEN(ZMK_KEYMAP_NODE, layers)
/* TODO: Need to actually be able to get a NODELABEL from a node id
#define _ZMK_KEYMAP_GENERATE_LAYER_CONST(node_id) \
DT_NODELABEL_FOR_NODE(node_id)_layer,
enum zmk_keymap_layer
{
DT_FOREACH_CHILD(DT_INST(0, zmk_layers), _ZMK_KEYMAP_GENERATE_LAYER_CONST)
};
*/
// #include <zmk/keys.h>
bool zmk_keymap_layer_activate(u8_t layer);
bool zmk_keymap_layer_deactivate(u8_t layer);
zmk_key
zmk_keymap_keycode_from_position(u32_t row, u32_t column);
int zmk_keymap_position_state_changed(u32_t row, u32_t column, bool pressed);

View File

@ -1,3 +1,4 @@
# CONFIG_LOG=y
# CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_LOG=y
CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_KERNEL_BIN_NAME="zmk"
CONFIG_REBOOT=y

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_key_press
#include <device.h>
#include <drivers/behavior.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct behavior_key_press_config { };
struct behavior_key_press_data { };
static int behavior_key_press_init(struct device *dev)
{
return 0;
};
// They keycode is passed by the "keymap" based on the parameter created as part of the assignment.
// Other drivers instead might activate a layer, update the consumer page state, or update the RGB state, etc.
// Returns:
// * > 0 - indicate successful processing, and halt further handling,
// * 0 - Indicate successful processing, and continue propagation.
// * < 0 - Indicate error processing, report and halt further propagation.
static int on_position_pressed(struct device *dev, u32_t keycode, u32_t _)
{
// Invoking this triggers a *new* event, that can be linked to other behaviours.
//return zmk_key_state_press(u32_t keycode);
return 0;
}
// They keycode is passed by the "keymap" based on the parameter created as part of the assignment.
static int on_position_released(struct device *dev, u32_t keycode, u32_t _)
{
// Invoking this triggers a *new* event, that can will be handled by other behaviors
// This is the "command" piece. Which could be better/richer, but captures essence here.
// return zmk_key_state_release(u32_t keycode);
return 0;
}
static const struct behavior_driver_api behavior_key_press_driver_api = {
// These callbacks are all optional, and define which kinds of events the behavior can handle.
// They can reference local functions defined here, or shared event handlers.
.position_pressed = on_position_pressed,
.position_released = on_position_released
// Other optional callbacks a behavior can implement
// .on_mouse_moved
// .on_sensor_data - Any behaviour that wants to be linked to a censor can implement this behavior
};
static const struct behavior_key_press_config behavior_key_press_config = {};
static struct behavior_key_press_data behavior_key_press_data;
DEVICE_AND_API_INIT(behavior_key_press, DT_INST_LABEL(0), behavior_key_press_init,
&behavior_key_press_data,
&behavior_key_press_config,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&behavior_key_press_driver_api);

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_reset
#include <device.h>
#include <power/reboot.h>
#include <drivers/behavior.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct behavior_reset_config { };
struct behavior_reset_data { };
static int behavior_reset_init(struct device *dev)
{
return 0;
};
static int on_position_pressed(struct device *dev, u32_t _param1, u32_t _param2)
{
// TODO: Correct magic code for going into DFU?
// See https://github.com/adafruit/Adafruit_nRF52_Bootloader/blob/d6b28e66053eea467166f44875e3c7ec741cb471/src/main.c#L107
sys_reboot(0);
return 0;
}
static int on_position_released(struct device *dev, u32_t _param1, u32_t _param2)
{
return 0;
}
static const struct behavior_driver_api behavior_reset_driver_api = {
.position_pressed = on_position_pressed,
.position_released = on_position_released
};
static const struct behavior_reset_config behavior_reset_config = {};
static struct behavior_reset_data behavior_reset_data;
DEVICE_AND_API_INIT(behavior_reset, DT_INST_LABEL(0), behavior_reset_init,
&behavior_reset_data,
&behavior_reset_config,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&behavior_reset_driver_api);

View File

@ -3,22 +3,48 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/keymap.h>
#include <dt-bindings/zmk/matrix-transform.h>
#include <drivers/behavior.h>
#include <sys/util.h>
static u32_t zmk_keymap_layer_state = 0;
static u8_t zmk_keymap_layer_default = 0;
struct zmk_behavior_binding {
char *behavior_dev;
u32_t param1;
u32_t param2;
};
#define ZMK_KEYMAP_NODE DT_CHOSEN(zmk_keymap)
#define ZMK_KEYMAP_LAYERS_LEN DT_PROP_LEN(ZMK_KEYMAP_NODE, layers)
#define LAYER_NODE(l) DT_PHANDLE_BY_IDX(ZMK_KEYMAP_NODE, layers, l)
#define BINDING_FOR_IDX(layer,idx) \
{ .behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(DT_PHANDLE_BY_IDX(ZMK_KEYMAP_NODE, layers, layer), bindings, idx)), \
.param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(LAYER_NODE(layer), bindings, idx, param1), (0), (DT_PHA_BY_IDX(LAYER_NODE(layer), bindings, idx, param1))), \
.param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(LAYER_NODE(layer), bindings, idx, param2), (0), (DT_PHA_BY_IDX(LAYER_NODE(layer), bindings, idx, param2))), \
}
#if DT_NODE_HAS_PROP(ZMK_KEYMAP_NODE, transform)
#define ZMK_KEYMAP_TRANSFORM_NODE DT_PHANDLE(ZMK_KEYMAP_NODE, transform)
#define ZMK_KEYMAP_LEN DT_PROP_LEN(ZMK_KEYMAP_TRANSFORM_NODE, map)
#define _TRANSFORM_ENTRY(i, l) \
[(KT_ROW(DT_PROP_BY_IDX(ZMK_KEYMAP_TRANSFORM_NODE, map, i)) * ZMK_MATRIX_COLS) + KT_COL(DT_PROP_BY_IDX(ZMK_KEYMAP_TRANSFORM_NODE, map, i))] = DT_PROP_BY_IDX(DT_PHANDLE_BY_IDX(ZMK_KEYMAP_NODE, layers, l), keys, i),
[(KT_ROW(DT_PROP_BY_IDX(ZMK_KEYMAP_TRANSFORM_NODE, map, i)) * ZMK_MATRIX_COLS) + KT_COL(DT_PROP_BY_IDX(ZMK_KEYMAP_TRANSFORM_NODE, map, i))] = BINDING_FOR_IDX(l,i),
#else
#define ZMK_KEYMAP_LEN DT_PROP_LEN(ZMK_KEYMAP_NODE, bindings)
#define _TRANSFORM_ENTRY(i, l) \
BINDING_FOR_IDX(l,i),
#endif
#define TRANSFORMED_LAYER(idx) \
{ UTIL_LISTIFY(ZMK_KEYMAP_LEN, _TRANSFORM_ENTRY, idx) }
{ UTIL_LISTIFY(DT_PROP_LEN(DT_PHANDLE_BY_IDX(ZMK_KEYMAP_NODE, layers, idx), bindings), _TRANSFORM_ENTRY, idx) }
static zmk_key zmk_keymap[ZMK_KEYMAP_LAYERS_LEN][ZMK_MATRIX_ROWS * ZMK_MATRIX_COLS] = {
static struct zmk_behavior_binding zmk_keymap[ZMK_KEYMAP_LAYERS_LEN][ZMK_MATRIX_ROWS * ZMK_MATRIX_COLS] = {
#if DT_PROP_HAS_IDX(ZMK_KEYMAP_NODE, layers, 0)
TRANSFORMED_LAYER(0),
#endif
@ -51,36 +77,7 @@ static zmk_key zmk_keymap[ZMK_KEYMAP_LAYERS_LEN][ZMK_MATRIX_ROWS * ZMK_MATRIX_CO
#endif
};
#else
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
};
#endif
// #else
#define SET_LAYER_STATE(layer, state) \
if (layer >= 32) \
@ -100,24 +97,38 @@ 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)
int zmk_keymap_position_state_changed(u32_t row, u32_t column, bool pressed)
{
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);
struct zmk_behavior_binding *binding = &zmk_keymap[layer][key_index];
struct device *behavior;
int ret;
zmk_key key = zmk_keymap[layer][key_index];
if (key == ZC_TRNS)
{
continue;
LOG_DBG("key index: %d, binding name: %s", key_index, binding->behavior_dev);
behavior = device_get_binding(binding->behavior_dev);
if (pressed) {
ret = behavior_position_pressed(behavior, binding->param1, binding->param2);
} else {
ret = behavior_position_released(behavior, binding->param1, binding->param2);
}
return key;
if (ret > 0) {
LOG_DBG("behavior processing to continue to next layer");
continue;
} else if (ret < 0) {
LOG_DBG("Behavior returned error: %d", ret);
return ret;
} else {
return ret;
}
}
}
return ZC_NO;
return -ENOTSUP;
}

View File

@ -49,11 +49,12 @@ void zmk_kscan_process_msgq(struct k_work *item)
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};
zmk_keymap_position_state_changed(ev.row, ev.column, 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);
// LOG_DBG("Row: %d, col: %d, key: %d, pressed: %s\n", ev.row, ev.column, key, (pressed ? "true" : "false"));
// zmk_handle_key(kev);
}
}