Move Zephyr app into subdirectory.

This commit is contained in:
Pete Johanson
2020-06-08 21:07:16 -04:00
parent 92b41d28e5
commit 38f1dbd984
57 changed files with 1 additions and 9 deletions

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
build/

40
app/CMakeLists.txt Normal file
View File

@@ -0,0 +1,40 @@
# Find Zephyr. This also loads Zephyr's build system.
cmake_minimum_required(VERSION 3.13.1)
list(APPEND BOARD_ROOT ${CMAKE_SOURCE_DIR})
list(APPEND DTS_ROOT ${CMAKE_SOURCE_DIR})
# Add our custom Zephyr module for drivers w/ syscalls, etc.
list(APPEND DTS_ROOT ${CMAKE_SOURCE_DIR}/drivers/zephyr)
list(APPEND ZEPHYR_EXTRA_MODULES
${CMAKE_CURRENT_SOURCE_DIR}/drivers
)
list(APPEND SYSCALL_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/drivers/zephyr)
include(cmake/keymap.cmake)
find_package(Zephyr)
project(zmk)
if(EXISTS ${KEYMAP_DIR}/keymap.c)
target_sources(app PRIVATE ${KEYMAP_DIR}/keymap.c)
endif()
# Add your source file to the "app" target. This must come after
# find_package(Zephyr) which defines the target.
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_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)
target_sources_ifdef(CONFIG_ZMK_USB app PRIVATE src/usb_hid.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/hog.c)
target_sources(app PRIVATE src/endpoints.c)
target_sources(app PRIVATE src/main.c)
target_sources(app PRIVATE src/handlers.c)

84
app/Kconfig Normal file
View File

@@ -0,0 +1,84 @@
mainmenu "ZMK Firmware"
config ZMK_KEYBOARD_NAME
string "Keyboard Name"
config USB_DEVICE_PRODUCT
default ZMK_KEYBOARD_NAME
config BT_DEVICE_NAME
default ZMK_KEYBOARD_NAME
config ZMK_KSCAN_EVENT_QUEUE_SIZE
int "Size of the event queue for KSCAN events to buffer events"
default 4
menu "HID Output Types"
config ZMK_USB
bool "USB"
select USB
select USB_DEVICE_STACK
select USB_DEVICE_HID
menuconfig ZMK_BLE
bool "BLE (HID over GATT)"
select BT
select BT_SMP
select BT_SMP_SC_PAIR_ONLY
select BT_PERIPHERAL
select BT_GATT_DIS
select BT_GATT_BAS
if ZMK_BLE
# HID GATT notifications sent this way are *not* picked up by Linux, and possibly others.
config BT_GATT_NOTIFY_MULTIPLE
default n
config BT_DEVICE_APPEARANCE
default 961
config ZMK_BLE_PASSKEY_ENTRY
bool "Experimental: Requiring typing passkey from host to pair BLE connection"
default n
# Incresed stack due to settings API usage
# CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
#
# CONFIG_BT_SETTINGS=y
# CONFIG_FLASH=y
# CONFIG_FLASH_PAGE_LAYOUT=y
# CONFIG_FLASH_MAP=y
# CONFIG_NVS=y
# CONFIG_SETTINGS=y
endif
endmenu
config ZMK_KSCAN_MOCK_DRIVER
bool "Enable mock kscan driver to simulate key presses"
default n
config ZMK_KSCAN_COMPOSITE_DRIVER
bool "Enable composite kscan driver to combine kscan devices"
default n
menu "ZMK Actions"
config ZMK_ACTION_MOD_TAP
bool "Enable the Mod-Tap Action"
endmenu
module = ZMK
module-str = zmk
source "subsys/logging/Kconfig.template.log_config"
rsource "boards/Kconfig"
rsource "boards/shields/*/Kconfig.defconfig"
source "Kconfig.zephyr"

1
app/boards/Kconfig Normal file
View File

@@ -0,0 +1 @@
rsource "shields/*/Kconfig.shield"

View File

@@ -0,0 +1,9 @@
# SPDX-License-Identifier: Apache-2.0
list(APPEND EXTRA_DTC_FLAGS "-qq")
if(CONFIG_PINMUX)
zephyr_library()
zephyr_library_sources(pinmux.c)
zephyr_library_include_directories(${ZEPHYR_BASE}/drivers)
endif()

View File

@@ -0,0 +1,8 @@
# STM32F3DISCOVERY board configuration
# Copyright (c) 2017 I-SENSE group of ICCS
# SPDX-License-Identifier: Apache-2.0
config BOARD_STM32F3_DISCO
bool "STM32F3DISCOVERY Development Board"
depends on SOC_STM32F303XC

View File

@@ -0,0 +1,39 @@
# Planck keyboard configuration
# Copyright (c) 2017 I-SENSE group of ICCS
# SPDX-License-Identifier: Apache-2.0
if BOARD_PLANCK_REV6
config BOARD
default "planck_rev6"
config UART_1
default y
depends on UART_CONSOLE
if I2C
config I2C_1
default y
config I2C_2
default y
endif # I2C
if SPI
config SPI_1
default y
config SPI_2
default y
endif # SPI
config CAN_1
default y
depends on CAN
endif # BOARD_PLANCK_REV6

View File

@@ -0,0 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
board_runner_args(jlink "--device=STM32F303VC" "--speed=4000")
include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake)
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2017 I-SENSE group of ICCS
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <device.h>
#include <init.h>
#include <drivers/pinmux.h>
#include <sys/sys_io.h>
#include <pinmux/stm32/pinmux_stm32.h>
/* pin assignments for STM32F3DISCOVERY board */
static const struct pin_config pinconf[] = {
#ifdef CONFIG_UART_1
{STM32_PIN_PC4, STM32F3_PINMUX_FUNC_PC4_USART1_TX},
{STM32_PIN_PC5, STM32F3_PINMUX_FUNC_PC5_USART1_RX},
#endif /* CONFIG_UART_1 */
#ifdef CONFIG_UART_2
{STM32_PIN_PA2, STM32F3_PINMUX_FUNC_PA2_USART2_TX},
{STM32_PIN_PA3, STM32F3_PINMUX_FUNC_PA3_USART2_RX},
#endif /* CONFIG_UART_2 */
#ifdef CONFIG_I2C_1
{STM32_PIN_PB6, STM32F3_PINMUX_FUNC_PB6_I2C1_SCL},
{STM32_PIN_PB7, STM32F3_PINMUX_FUNC_PB7_I2C1_SDA},
#endif /* CONFIG_I2C_1 */
#ifdef CONFIG_I2C_2
{STM32_PIN_PA9, STM32F3_PINMUX_FUNC_PA9_I2C2_SCL},
{STM32_PIN_PA10, STM32F3_PINMUX_FUNC_PA10_I2C2_SDA},
#endif /* CONFIG_I2C_2 */
#ifdef CONFIG_SPI_1
#ifdef CONFIG_SPI_STM32_USE_HW_SS
{STM32_PIN_PA4, STM32F3_PINMUX_FUNC_PA4_SPI1_NSS},
#endif /* CONFIG_SPI_STM32_USE_HW_SS */
{STM32_PIN_PA5, STM32F3_PINMUX_FUNC_PA5_SPI1_SCK},
{STM32_PIN_PA6, STM32F3_PINMUX_FUNC_PA6_SPI1_MISO},
{STM32_PIN_PA7, STM32F3_PINMUX_FUNC_PA7_SPI1_MOSI},
#endif /* CONFIG_SPI_1 */
#ifdef CONFIG_SPI_2
#ifdef CONFIG_SPI_STM32_USE_HW_SS
{STM32_PIN_PB12, STM32F3_PINMUX_FUNC_PB12_SPI2_NSS},
#endif /* CONFIG_SPI_STM32_USE_HW_SS */
{STM32_PIN_PB13, STM32F3_PINMUX_FUNC_PB13_SPI2_SCK},
{STM32_PIN_PB14, STM32F3_PINMUX_FUNC_PB14_SPI2_MISO},
{STM32_PIN_PB15, STM32F3_PINMUX_FUNC_PB15_SPI2_MOSI},
#endif /* CONFIG_SPI_2 */
#ifdef CONFIG_USB_DC_STM32
{STM32_PIN_PA11, STM32F3_PINMUX_FUNC_PA11_USB_DM},
{STM32_PIN_PA12, STM32F3_PINMUX_FUNC_PA12_USB_DP},
#endif /* CONFIG_USB_DC_STM32 */
#ifdef CONFIG_CAN_1
{STM32_PIN_PD0, STM32F3_PINMUX_FUNC_PD0_CAN1_RX},
{STM32_PIN_PD1, STM32F3_PINMUX_FUNC_PD1_CAN1_TX},
#endif /* CONFIG_CAN_1 */
};
static int pinmux_stm32_init(struct device *port)
{
ARG_UNUSED(port);
stm32_setup_pins(pinconf, ARRAY_SIZE(pinconf));
return 0;
}
SYS_INIT(pinmux_stm32_init, PRE_KERNEL_1,
CONFIG_PINMUX_STM32_DEVICE_INITIALIZATION_PRIORITY);

View File

@@ -0,0 +1,162 @@
/*
* Copyright (c) 2017 I-SENSE group of ICCS
*
* SPDX-License-Identifier: Apache-2.0
*/
/dts-v1/;
#include <st/f3/stm32f303.dtsi>
/ {
model = "Plack PCD, rev6";
compatible = "planck,rev6", "st,stm32f303";
chosen {
zephyr,console = &usart1;
zephyr,shell-uart = &usart1;
zephyr,sram = &sram0;
zephyr,flash = &flash0;
};
leds {
compatible = "gpio-leds";
red_led_3: led_3 {
gpios = <&gpioe 9 GPIO_ACTIVE_HIGH>;
label = "User LD3";
};
blue_led_4: led_4 {
gpios = <&gpioe 8 GPIO_ACTIVE_HIGH>;
label = "User LD4";
};
orange_led_5: led_5 {
gpios = <&gpioe 10 GPIO_ACTIVE_HIGH>;
label = "User LD5";
};
green_led_6: led_6 {
gpios = <&gpioe 15 GPIO_ACTIVE_HIGH>;
label = "User LD6";
};
green_led_7: led_7 {
gpios = <&gpioe 11 GPIO_ACTIVE_HIGH>;
label = "User LD7";
};
orange_led_8: led_8 {
gpios = <&gpioe 14 GPIO_ACTIVE_HIGH>;
label = "User LD8";
};
blue_led_9: led_9 {
gpios = <&gpioe 12 GPIO_ACTIVE_HIGH>;
label = "User LD9";
};
red_led_10: led_10 {
gpios = <&gpioe 13 GPIO_ACTIVE_HIGH>;
label = "User LD10";
};
};
kscan {
compatible = "gpio-kscan";
label = "Keyscan Matrix";
row-gpios = <&gpioa 10 GPIO_ACTIVE_HIGH>,
<&gpioa 9 GPIO_ACTIVE_HIGH>,
<&gpioa 8 GPIO_ACTIVE_HIGH>,
<&gpiob 15 GPIO_ACTIVE_HIGH>,
<&gpioc 13 GPIO_ACTIVE_HIGH>,
<&gpioc 14 GPIO_ACTIVE_HIGH>,
<&gpioc 15 GPIO_ACTIVE_HIGH>,
<&gpioa 2 GPIO_ACTIVE_HIGH>;
col-gpios = <&gpiob 11 GPIO_ACTIVE_HIGH>,
<&gpiob 10 GPIO_ACTIVE_HIGH>,
<&gpiob 2 GPIO_ACTIVE_HIGH>,
<&gpiob 1 GPIO_ACTIVE_HIGH>,
<&gpioa 7 GPIO_ACTIVE_HIGH>,
<&gpiob 0 GPIO_ACTIVE_HIGH>;
};
gpio_keys {
compatible = "gpio-keys";
user_button: button {
label = "User";
gpios = <&gpioa 0 GPIO_ACTIVE_LOW>;
};
};
aliases {
led0 = &green_led_6;
led1 = &green_led_7;
sw0 = &user_button;
can-primary = &can1;
};
};
&usart1 {
current-speed = <115200>;
status = "okay";
};
&usart2 {
current-speed = <115200>;
status = "okay";
};
&i2c1 {
status = "okay";
clock-frequency = <I2C_BITRATE_FAST>;
lsm303dlhc-magn@1e {
compatible = "st,lsm303dlhc-magn";
reg = <0x1e>;
label = "LSM303DLHC-MAGN";
};
lsm303dlhc-accel@19 {
compatible = "st,lis2dh", "st,lsm303dlhc-accel";
reg = <0x19>;
irq-gpios = <&gpioe 4 GPIO_ACTIVE_HIGH>,
<&gpioe 5 GPIO_ACTIVE_HIGH>;
label = "LSM303DLHC-ACCEL";
};
};
&i2c2 {
status = "okay";
clock-frequency = <I2C_BITRATE_FAST>;
};
&spi1 {
status = "okay";
};
&spi2 {
status = "okay";
};
&usb {
status = "okay";
};
&rtc {
status = "okay";
};
&can1 {
status = "okay";
};
&flash0 {
/*
* For more information, see:
* http://docs.zephyrproject.org/latest/guides/dts/index.html#flash-partitions
*/
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* Set 6Kb of storage at the end of the 256Kb of flash */
storage_partition: partition@3e800 {
label = "storage";
reg = <0x0003e800 0x00001800>;
};
};
};

View File

@@ -0,0 +1,19 @@
identifier: planck_rev6
name: PLANKREV6
type: keyboard
arch: arm
toolchain:
- zephyr
- gnuarmemb
- xtools
ram: 40
supported:
- gpio
- i2c
- counter
- spi
- usb_device
- lsm303dlhc
- nvs
- can
- kscan

View File

@@ -0,0 +1,50 @@
# SPDX-License-Identifier: Apache-2.0
CONFIG_SOC_SERIES_STM32F3X=y
CONFIG_SOC_STM32F303XC=y
# 72MHz system clock
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=72000000
# Floating Point Options
CONFIG_FLOAT=y
# enable uart driver
CONFIG_SERIAL=y
# enable console
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
#enable I2C
CONFIG_I2C=y
#enable SPI
CONFIG_SPI=y
# enable pinmux
CONFIG_PINMUX=y
# enable GPIO
CONFIG_GPIO=y
# clock configuration
CONFIG_CLOCK_CONTROL=y
# kscan matrix
CONFIG_KSCAN=y
CONFIG_KSCAN_GPIO=y
# Clock configuration for Cube Clock control driver
CONFIG_CLOCK_STM32_HSE_CLOCK=8000000
CONFIG_CLOCK_STM32_SYSCLK_SRC_PLL=y
# use HSE as PLL input
CONFIG_CLOCK_STM32_PLL_SRC_HSE=y
# however, the board does not have an external oscillator, so just use
# the 8MHz clock signal coming from integrated STLink
CONFIG_CLOCK_STM32_HSE_BYPASS=y
# produce 72MHz clock at PLL output
CONFIG_CLOCK_STM32_PLL_PREDIV=1
CONFIG_CLOCK_STM32_PLL_MULTIPLIER=9
CONFIG_CLOCK_STM32_AHB_PRESCALER=1
CONFIG_CLOCK_STM32_APB1_PRESCALER=2
CONFIG_CLOCK_STM32_APB2_PRESCALER=1

View File

@@ -0,0 +1,9 @@
CONFIG_KSCAN=n
CONFIG_ZMK_KSCAN_MOCK_DRIVER=y
CONFIG_ZMK_KSCAN_COMPOSITE_DRIVER=y
CONFIG_ZMK_KSCAN_GPIO_DRIVER=n
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y
CONFIG_KSCAN_LOG_LEVEL_DBG=y
CONFIG_ZMK_LOG_LEVEL_DBG=y

View File

@@ -0,0 +1,79 @@
#include <dt-bindings/zmk/keys.h>
#include <zmk/kscan-mock.h>
/ {
chosen {
zmk,kscan = &kscan0;
zmk,keymap = &keymap0;
};
kscan0: kscan_0 {
compatible = "zmk,kscan-composite";
label = "KSCAN_COMP";
rows = <2>;
columns = <4>;
left: left {
kscan = <&left_hand>;
};
right: right {
kscan = <&right_hand>;
column-offset = <2>;
};
};
left_hand: kscan_1 {
compatible = "zmk,kscan-mock";
label = "KSCAN_LEFT";
rows = <2>;
columns = <2>;
events = <ZMK_MOCK_PRESS(0,1,300) ZMK_MOCK_PRESS(0,0,300) ZMK_MOCK_RELEASE(0,0,300) ZMK_MOCK_RELEASE(0,1,300)>;
// events = <ZMK_MOCK_PRESS(0,0,800) ZMK_MOCK_RELEASE(0,0,800) ZMK_MOCK_PRESS(0,1,800) ZMK_MOCK_RELEASE(0,1,800)>;
};
right_hand: kscan_2 {
compatible = "zmk,kscan-mock";
label = "KSCAN_RIGHT";
rows = <2>;
columns = <2>;
events = <ZMK_MOCK_PRESS(1,1,800) ZMK_MOCK_RELEASE(1,1,100) ZMK_MOCK_PRESS(0,1,800) ZMK_MOCK_RELEASE(0,1,100)>;
};
keymap0: keymap {
compatible = "zmk,keymap";
label ="Default keymap";
layers = <&default &lower &raise>;
};
layers {
compatible = "zmk,layers";
default: layer_0 {
label = "DEFAULT";
keys =
<
KC_A MT(MOD_LSFT, KC_B) KC_C KC_D
KC_E KC_F KC_G KC_H
>;
};
lower: layer_1 {
label = "LOWER";
keys = <
KC_A KC_B KC_C KC_D
KC_E KC_F KC_G KC_H
>;
};
raise: layer_2 {
label = "RAISE";
keys = <
KC_E KC_F KC_G KC_H
KC_A KC_B KC_C KC_D
>;
};
};
};

View File

@@ -0,0 +1,13 @@
if SHIELD_PETEJOHANSON_HANDWIRE
config ZMK_KEYBOARD_NAME
default "Pete's Handwire Breadboard"
config ZMK_BLE
default y
config ZMK_ACTION_MOD_TAP
default y
endif

View File

@@ -0,0 +1,5 @@
# Copyright (c) 2019 Linaro Limited
# SPDX-License-Identifier: Apache-2.0
config SHIELD_PETEJOHANSON_HANDWIRE
def_bool $(shields_list_contains,petejohanson_handwire)

View File

@@ -0,0 +1,5 @@
#include <dt-bindings/zmk/keys.h>
#define CC_RAIS ZC_CSTM(1)
#define CC_LOWR ZC_CSTM(2)

View File

@@ -0,0 +1,34 @@
#include <zmk/keys.h>
#include <zmk/keymap.h>
#include <keymap.h>
bool zmk_handle_key_user(struct zmk_key_event *key_event)
{
switch (key_event->key)
{
case CC_LOWR:
if (key_event->pressed)
{
zmk_keymap_layer_activate(1);
}
else
{
zmk_keymap_layer_deactivate(1);
}
return false;
case CC_RAIS:
if (key_event->pressed)
{
zmk_keymap_layer_activate(2);
}
else
{
zmk_keymap_layer_deactivate(2);
}
return false;
}
return true;
};

View File

@@ -0,0 +1,41 @@
#include <dt-bindings/zmk/keys.h>
#include <keymap.h>
/ {
chosen {
zmk,keymap = &keymap0;
};
keymap0: keymap {
compatible = "zmk,keymap";
label ="Default keymap";
layers = <&default &lower &raise>;
};
layers {
compatible = "zmk,layers";
default: layer_0 {
label = "DEFAULT";
keys =
<
KC_A MT(MOD_LSFT, KC_B) ZC_NO ZC_NO
CC_RAIS CC_LOWR ZC_NO ZC_NO
>;
};
lower: layer_1 {
label = "LOWER";
keys = <
KC_MPLY KC_MNXT ZC_NO ZC_NO
ZC_TRNS ZC_TRNS ZC_NO ZC_NO
>;
};
raise: layer_2 {
label = "RAISE";
keys = <
KC_C KC_D ZC_NO ZC_NO
ZC_TRNS ZC_TRNS ZC_NO ZC_NO>;
};
};
};

View File

@@ -0,0 +1 @@
CONFIG_ZMK_KSCAN_COMPOSITE_DRIVER=y

View File

@@ -0,0 +1,44 @@
/ {
chosen {
zmk,kscan = &kscan0;
};
kscan0: kscan_0 {
compatible = "zmk,kscan-composite";
label = "KSCAN_COMP";
rows = <2>;
columns = <4>;
left {
kscan = <&left_hand>;
};
right {
kscan = <&right_hand>;
column-offset = <2>;
};
};
left_hand: kscan_1 {
compatible = "gpio-kscan";
label = "KSCAN_LEFT";
diode-direction = "row2col";
row-gpios = <&arduino_header 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
<&arduino_header 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
col-gpios = <&arduino_header 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
<&arduino_header 11 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
};
right_hand: kscan_2 {
compatible = "gpio-kscan";
label = "KSCAN_RIGHT";
diode-direction = "row2col";
row-gpios = <&arduino_header 12 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
<&arduino_header 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
col-gpios = <&arduino_header 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
<&arduino_header 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
};
};

94
app/cmake/keymap.cmake Normal file
View File

@@ -0,0 +1,94 @@
get_property(cached_keymap_value CACHE KEYMAP PROPERTY VALUE)
# There are actually 4 sources, the three user input sources, and the
# previously used value (CACHED_KEYMAP). The previously used value has
# precedence, and if we detect that the user is trying to change the
# value we give him a warning about needing to clean the build
# directory to be able to change keymaps.
set(keymap_cli_argument ${cached_keymap_value}) # Either new or old
if(keymap_cli_argument STREQUAL CACHED_KEYMAP)
# We already have a CACHED_KEYMAP so there is no new input on the CLI
unset(keymap_cli_argument)
endif()
set(keymap_app_cmake_lists ${KEYMAP})
if(cached_keymap_value STREQUAL KEYMAP)
# The app build scripts did not set a default, The KEYMAP we are
# reading is the cached value from the CLI
unset(keymap_app_cmake_lists)
endif()
if(CACHED_KEYMAP)
# Warn the user if it looks like he is trying to change the keymap
# without cleaning first
if(keymap_cli_argument)
if(NOT (CACHED_KEYMAP STREQUAL keymap_cli_argument))
message(WARNING "The build directory must be cleaned pristinely when changing keymaps")
# TODO: Support changing keymaps without requiring a clean build
endif()
endif()
set(KEYMAP ${CACHED_KEYMAP})
elseif(keymap_cli_argument)
set(KEYMAP ${keymap_cli_argument})
elseif(DEFINED ENV{KEYMAP})
set(KEYMAP $ENV{KEYMAP})
elseif(keymap_app_cmake_lists)
set(KEYMAP ${keymap_app_cmake_lists})
else()
set(KEYMAP default)
message(STATUS "KEYMAP defaulted to 'default'")
endif()
message(STATUS "Keymap: ${KEYMAP}")
# Store the selected keymap in the cache
set(CACHED_KEYMAP ${KEYMAP} CACHE STRING "Selected keymap")
set(ZMK_APP_DIR ${CMAKE_CURRENT_SOURCE_DIR})
list(APPEND KEYMAP_DIRS ${ZMK_APP_DIR}/keymaps)
foreach(root ${BOARD_ROOT})
find_path(BOARD_DIR
NAMES ${BOARD}_defconfig
PATHS ${root}/boards/*/*
NO_DEFAULT_PATH
)
if(BOARD_DIR)
list(APPEND KEYMAP_DIRS ${BOARD_DIR}/keymaps)
endif()
if(DEFINED SHIELD)
find_path(shields_refs_list
NAMES ${SHIELD}.overlay
PATHS ${root}/boards/shields/*
NO_DEFAULT_PATH
)
foreach(shield_path ${shields_refs_list})
list(APPEND KEYMAP_DIRS ${shield_path}/keymaps)
endforeach()
endif()
endforeach()
find_path(BASE_KEYMAPS_DIR
NAMES ${KEYMAP}/keymap.overlay
PATHS ${KEYMAP_DIRS}
NO_DEFAULT_PATH
)
if (BASE_KEYMAPS_DIR)
set(KEYMAP_DIR "${BASE_KEYMAPS_DIR}/${KEYMAP}" CACHE STRING "Selected keymap directory")
message(STATUS "Using keymap directory: ${KEYMAP_DIR}/")
# Used to let local imports of custom keycodes work as expected
list(APPEND DTS_ROOT ${KEYMAP_DIR})
if (EXISTS "${KEYMAP_DIR}/include")
include_directories("${KEYMAP_DIR}/include")
endif()
set(DTC_OVERLAY_FILE ${KEYMAP_DIR}/keymap.overlay)
endif()

View File

@@ -0,0 +1,9 @@
if(CONFIG_ZMK_KSCAN_GPIO_DRIVER)
# Add hello_world_driver.h to the set of global include paths.
zephyr_include_directories(.)
zephyr_library()
zephyr_library_sources(
kscan_gpio.c
)
endif()

View File

@@ -0,0 +1,11 @@
config ZMK_KSCAN_GPIO_DRIVER
bool "Enable GPIO kscan driver to simulate key presses"
default y
select GPIO
config ZMK_KSCAN_INIT_PRIORITY
int "Keyboard scan driver init priority"
default 40
help
Keyboard scan device driver initialization priority.

View File

@@ -0,0 +1,25 @@
# Copyright (c) 2020, Pete Johanson
# SPDX-License-Identifier: Apache-2.0
description: GPIO keyboard matrix controller
compatible: "gpio-kscan"
include: kscan.yaml
properties:
row-gpios:
type: phandle-array
required: true
col-gpios:
type: phandle-array
required: true
debounce-period:
type: int
default: 5
diode-direction:
type: string
default: row2col
enum:
- row2col
- col2row

View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT gpio_kscan
#include <device.h>
#include <drivers/kscan.h>
#include <drivers/gpio.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct kscan_gpio_item_config
{
char *label;
gpio_pin_t pin;
gpio_flags_t flags;
};
#define _KSCAN_GPIO_ITEM_CFG_INIT(n, prop, idx) \
{ \
.label = DT_INST_GPIO_LABEL_BY_IDX(n, prop, idx), \
.pin = DT_INST_GPIO_PIN_BY_IDX(n, prop, idx), \
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, prop, idx), \
},
static int kscan_gpio_config_interrupts(struct device **devices,
const struct kscan_gpio_item_config *configs,
size_t len, gpio_flags_t flags)
{
for (int i = 0; i < len; i++)
{
struct device *dev = devices[i];
const struct kscan_gpio_item_config *cfg = &configs[i];
int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags);
if (err)
{
LOG_ERR("Unable to enable matrix GPIO interrupt");
return err;
}
}
return 0;
}
#define INST_MATRIX_ROWS(n) DT_INST_PROP_LEN(n, row_gpios)
#define INST_MATRIX_COLS(n) DT_INST_PROP_LEN(n, col_gpios)
#define INST_OUTPUT_LEN(n) COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_COLS(n)), (INST_MATRIX_ROWS(n)))
#define INST_INPUT_LEN(n) COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_ROWS(n)), (INST_MATRIX_COLS(n)))
#define GPIO_INST_INIT(n) \
struct kscan_gpio_irq_callback_##n \
{ \
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * work; \
struct gpio_callback callback; \
}; \
static struct kscan_gpio_irq_callback_##n \
irq_callbacks_##n[INST_INPUT_LEN(n)]; \
struct kscan_gpio_config_##n \
{ \
struct kscan_gpio_item_config rows[INST_MATRIX_ROWS(n)]; \
struct kscan_gpio_item_config cols[INST_MATRIX_COLS(n)]; \
}; \
struct kscan_gpio_data_##n \
{ \
kscan_callback_t callback; \
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) work; \
bool matrix_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
struct device *rows[INST_MATRIX_ROWS(n)]; \
struct device *cols[INST_MATRIX_COLS(n)]; \
struct device *dev; \
}; \
static struct device **kscan_gpio_input_devices_##n(struct device *dev) \
{ \
struct kscan_gpio_data_##n *data = dev->driver_data; \
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->cols), (data->rows))); \
} \
static const struct kscan_gpio_item_config *kscan_gpio_input_configs_##n(struct device *dev) \
{ \
const struct kscan_gpio_config_##n *cfg = dev->config_info; \
return ((COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->cols), (cfg->rows)))); \
} \
static struct device **kscan_gpio_output_devices_##n(struct device *dev) \
{ \
struct kscan_gpio_data_##n *data = dev->driver_data; \
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->rows), (data->cols))); \
} \
static const struct kscan_gpio_item_config *kscan_gpio_output_configs_##n(struct device *dev) \
{ \
const struct kscan_gpio_config_##n *cfg = dev->config_info; \
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->rows), (cfg->cols))); \
} \
static int kscan_gpio_enable_interrupts_##n(struct device *dev) \
{ \
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), kscan_gpio_input_configs_##n(dev), INST_INPUT_LEN(n), \
GPIO_INT_DEBOUNCE | GPIO_INT_EDGE_BOTH); \
} \
static int kscan_gpio_disable_interrupts_##n(struct device *dev) \
{ \
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), kscan_gpio_input_configs_##n(dev), INST_INPUT_LEN(n), \
GPIO_INT_DISABLE); \
} \
static void kscan_gpio_set_output_state_##n(struct device *dev, int value) \
{ \
for (int i = 0; i < INST_OUTPUT_LEN(n); i++) \
{ \
struct device *in_dev = kscan_gpio_output_devices_##n(dev)[i]; \
const struct kscan_gpio_item_config *cfg = &kscan_gpio_output_configs_##n(dev)[i]; \
gpio_pin_set(in_dev, cfg->pin, value); \
} \
} \
static void kscan_gpio_set_matrix_state_##n(bool state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)], u32_t input_index, u32_t output_index, bool value) \
{ \
state[COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (output_index), (input_index))][COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (input_index), (output_index))] = value; \
} \
static int kscan_gpio_read_##n(struct device *dev) \
{ \
struct kscan_gpio_data_##n *data = dev->driver_data; \
static bool read_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
LOG_DBG("Scanning the matrix for updated state"); \
/* Disable our interrupts temporarily while we scan, to avoid */ \
/* re-entry while we iterate columns and set them active one by one */ \
/* to get pressed state for each matrix cell. */ \
kscan_gpio_disable_interrupts_##n(dev); \
kscan_gpio_set_output_state_##n(dev, 0); \
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) \
{ \
struct device *out_dev = kscan_gpio_output_devices_##n(dev)[o]; \
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
gpio_pin_set(out_dev, out_cfg->pin, 1); \
for (int i = 0; i < INST_INPUT_LEN(n); i++) \
{ \
struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(dev)[i]; \
kscan_gpio_set_matrix_state_##n(read_state, i, o, gpio_pin_get(in_dev, in_cfg->pin) > 0); \
} \
gpio_pin_set(out_dev, out_cfg->pin, 0); \
} \
/* Set all our outputs as active again, then re-enable interrupts, */ \
/* so we can trigger interrupts again for future press/release */ \
kscan_gpio_set_output_state_##n(dev, 1); \
kscan_gpio_enable_interrupts_##n(dev); \
for (int r = 0; r < INST_MATRIX_ROWS(n); r++) \
{ \
for (int c = 0; c < INST_MATRIX_COLS(n); c++) \
{ \
bool pressed = read_state[r][c]; \
if (pressed != data->matrix_state[r][c]) \
{ \
LOG_DBG("Sending event at %d,%d state %s", \
r, c, (pressed ? "on" : "off")); \
data->matrix_state[r][c] = pressed; \
data->callback(dev, r, c, pressed); \
} \
} \
} \
return 0; \
} \
static void kscan_gpio_work_handler_##n(struct k_work *work) \
{ \
struct kscan_gpio_data_##n *data = \
CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \
kscan_gpio_read_##n(data->dev); \
} \
static void kscan_gpio_irq_callback_handler_##n(struct device *dev, \
struct gpio_callback *cb, gpio_port_pins_t pin) \
{ \
struct kscan_gpio_irq_callback_##n *data = \
CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \
COND_CODE_0(DT_INST_PROP(n, debounce_period), \
({ k_work_submit(data->work); }), \
({ \
k_delayed_work_cancel(data->work); \
k_delayed_work_submit(data->work, K_MSEC(DT_INST_PROP(n, debounce_period))); })) \
} \
static struct kscan_gpio_data_##n kscan_gpio_data_##n; \
static int kscan_gpio_configure_##n(struct device *dev, kscan_callback_t callback) \
{ \
struct kscan_gpio_data_##n *data = dev->driver_data; \
if (!callback) \
{ \
return -EINVAL; \
} \
data->callback = callback; \
return 0; \
} \
static int kscan_gpio_init_##n(struct device *dev) \
{ \
struct kscan_gpio_data_##n *data = dev->driver_data; \
int err; \
struct device **input_devices = kscan_gpio_input_devices_##n(dev); \
for (int i = 0; i < INST_INPUT_LEN(n); i++) \
{ \
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(dev)[i]; \
input_devices[i] = device_get_binding(in_cfg->label); \
if (!input_devices[i]) \
{ \
LOG_ERR("Unable to find input GPIO device"); \
return -EINVAL; \
} \
err = gpio_pin_configure(input_devices[i], in_cfg->pin, GPIO_INPUT | in_cfg->flags); \
if (err) \
{ \
LOG_ERR("Unable to configure pin %d on %s for input", in_cfg->pin, in_cfg->label); \
return err; \
} \
irq_callbacks_##n[i].work = &data->work; \
gpio_init_callback(&irq_callbacks_##n[i].callback, kscan_gpio_irq_callback_handler_##n, BIT(in_cfg->pin)); \
err = gpio_add_callback(input_devices[i], &irq_callbacks_##n[i].callback); \
if (err) \
{ \
LOG_ERR("Error adding the callback to the column device"); \
return err; \
} \
} \
struct device **output_devices = kscan_gpio_output_devices_##n(dev); \
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) \
{ \
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
output_devices[o] = device_get_binding(out_cfg->label); \
if (!output_devices[o]) \
{ \
LOG_ERR("Unable to find output GPIO device"); \
return -EINVAL; \
} \
err = gpio_pin_configure(output_devices[o], out_cfg->pin, GPIO_OUTPUT_ACTIVE | out_cfg->flags); \
if (err) \
{ \
LOG_ERR("Unable to configure pin %d on %s for output", out_cfg->pin, out_cfg->label); \
return err; \
} \
} \
data->dev = dev; \
(COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work_init), (k_delayed_work_init)))(&data->work, kscan_gpio_work_handler_##n); \
return 0; \
} \
static const struct kscan_driver_api gpio_driver_api_##n = { \
.config = kscan_gpio_configure_##n, \
.enable_callback = kscan_gpio_enable_interrupts_##n, \
.disable_callback = kscan_gpio_disable_interrupts_##n, \
}; \
static const struct kscan_gpio_config_##n kscan_gpio_config_##n = { \
.rows = { \
IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, row_gpios, 0, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, row_gpios, 0))) \
IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, row_gpios, 1, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, row_gpios, 1)))}, \
.cols = {IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, col_gpios, 0, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, col_gpios, 0))) IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, col_gpios, 1, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, col_gpios, 1)))}}; \
DEVICE_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \
&kscan_gpio_data_##n, &kscan_gpio_config_##n, \
POST_KERNEL, CONFIG_ZMK_KSCAN_INIT_PRIORITY, \
&gpio_driver_api_##n);
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)

View File

@@ -0,0 +1,3 @@
build:
cmake: zephyr
kconfig: zephyr/Kconfig

View File

@@ -0,0 +1,13 @@
description: |
Allows defining a keymap composed of multiple layers
compatible: "zmk,keymap"
properties:
label:
type: string
required: true
layers:
type: phandles
required: true

View File

@@ -0,0 +1,27 @@
description: |
Allows composing multiple KSCAN devices into one virtual device
compatible: "zmk,kscan-composite"
properties:
label:
type: string
rows:
type: int
columns:
type: int
child-binding:
description: "Details of an included KSCAN devices"
properties:
label:
type: string
kscan:
type: phandle
row-offset:
type: int
default: 0
column-offset:
type: int
default: 0

View File

@@ -0,0 +1,17 @@
description: |
Allows defining a mock keyboard scan driver that simulates periodic events.
compatible: "zmk,kscan-mock"
properties:
label:
type: string
event-period:
type: int
description: Milliseconds between each generated event
events:
type: array
rows:
type: int
columns:
type: int

View File

@@ -0,0 +1,14 @@
description: |
Allows defining the various keymap layers for use.
compatible: "zmk,layers"
child-binding:
description: "A layer to be used in a keymap"
properties:
label:
type: string
keys:
type: array

View File

@@ -0,0 +1,142 @@
#pragma once
#define KC_A 0x04
#define KC_B 0x05
#define KC_C 0x06
#define KC_D 0x07
#define KC_E 0x08
#define KC_F 0x09
#define KC_G 0x0A
#define KC_H 0x0B
#define KC_I 0x0C
#define KC_J 0x0D
#define KC_K 0x0E
#define KC_L 0x0F
#define KC_M 0x10
#define KC_N 0x11
#define KC_O 0x12
#define KC_P 0x13
#define KC_Q 0x14
#define KC_R 0x15
#define KC_S 0x16
#define KC_T 0x17
#define KC_U 0x18
#define KC_V 0x19
#define KC_W 0x1A
#define KC_X 0x1B
#define KC_Y 0x1C
#define KC_Z 0x1D
#define KC_1 0x1E
#define KC_2 0x1F
#define KC_3 0x20
#define KC_4 0x21
#define KC_5 0x22
#define KC_6 0x23
#define KC_7 0x24
#define KC_8 0x25
#define KC_9 0x26
#define KC_0 0x27
#define KC_RET 0x28
#define KC_ESC 0x29
#define KC_DEL 0x2A
#define KC_BKSP KC_DEL
#define KC_TAB 0x2B
#define KC_SPC 0x2C
#define KC_MIN 0x2D
#define KC_EQL 0x2E
#define KC_LBKT 0x2F
#define KC_RBKT 0x30
#define KC_FSLH 0x31
#define KC_SCLN 0x33
#define KC_QUOT 0x34
#define KC_GRAV 0x35
#define KC_CMMA 0x36
#define KC_DOT 0x37
#define KC_BSLH 0x38
#define KC_CLCK 0x39
#define KC_F1 0x3A
#define KC_F2 0x3B
#define KC_RARW 0x4F
#define KC_LARW 0x50
#define KC_DARW 0x51
#define KC_UARW 0x52
#define KC_KDIV 0x54
#define KC_KMLT 0x55
#define KC_KMIN 0x56
#define KC_KPLS 0x57
#define KC_APP 0x65
#define KC_CURU 0xB4
#define KC_LPRN 0xB6
#define KC_RPRN 0xB7
#define KC_LCUR 0xB8
#define KC_RCUR 0xB9
#define KC_CRRT 0xC3
#define KC_PRCT 0xC4
#define KC_LABT 0xC5
#define KC_RABT 0xC6
#define KC_AMPS 0xC7
#define KC_PIPE 0xC9
#define KC_COLN 0xCB
#define KC_HASH 0xCC
#define KC_KSPC 0xCD
#define KC_ATSN 0xCE
#define KC_BANG 0xCF
#define KC_LCTL 0xE0
#define KC_LSFT 0xE1
#define KC_LALT 0xE2
#define KC_LGUI 0xE3
#define KC_RCTL 0xE4
#define KC_RSFT 0xE5
#define KC_RALT 0xE6
#define KC_RGUI 0xE7
#define KC_VOLU 0x80
#define KC_VOLD 0x81
/* The following are select consumer page usages */
#define KC_MNXT 0x100
#define KC_MPRV 0x101
#define KC_MSTP 0x102
#define KC_MJCT 0x103
#define KC_MPLY 0x104
#define KC_MMUT 0x105
#define KC_MVLU 0x106
#define KC_MVLD 0x107
#define ZC_TRNS (0xFFFF)
#define ZC_NO (0xFFFF - 1)
#define ZC_CSTM(n) (0xFFF + n)
#define MOD_LCTL (1 << 0x00)
#define MOD_LSFT (1 << 0x01)
#define MOD_LALT (1 << 0x02)
#define MOD_LGUI (1 << 0x03)
#define MOD_RCTL (1 << 0x04)
#define MOD_RSFT (1 << 0x05)
#define MOD_RALT (1 << 0x06)
#define MOD_RGUI (1 << 0x07)
#define ZK_ACTION(k) (k >> 24)
#define _ACTION(a) (a << 24)
#define _ACTION_MODS(m) (m << 16)
#define ZK_KEY(a) (a & 0xFFFF)
#define ZK_MODS(a) ((a >> 16) & 0xFF)
#define ZK_IS_CONSUMER(k) (ZK_KEY(k) >= 0x100)
#define ZMK_ACTION_KEY 0x01
#define ZMK_ACTION_MOD_TAP 0x01
#define ZMK_ACTION_ONE_SHOT 0x02
#define MT(mods, kc) (_ACTION(ZMK_ACTION_MOD_TAP) + _ACTION_MODS(mods) + kc)

7
app/include/zmk/ble.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include <zmk/keys.h>
int zmk_ble_init();
bool zmk_ble_handle_key_user(struct zmk_key_event *key_event);

View File

@@ -0,0 +1,8 @@
#pragma once
#include <zmk/keys.h>
#include <zmk/hid.h>
int zmk_endpoints_init();
int zmk_endpoints_send_report(enum zmk_hid_report_changes changes);
int zmk_endpoints_send_key_event(struct zmk_key_event key_event);

View File

@@ -0,0 +1,8 @@
#pragma once
#include <dt-bindings/zmk/keys.h>
#include <zmk/keymap.h>
#include <zmk/keys.h>
void zmk_handle_key(struct zmk_key_event key_event);

194
app/include/zmk/hid.h Normal file
View File

@@ -0,0 +1,194 @@
#pragma once
#include <usb/usb_device.h>
#include <usb/class/usb_hid.h>
#include <dt-bindings/zmk/keys.h>
#include <zmk/keys.h>
#define COLLECTION_REPORT 0x03
#define ZMK_HID_MAX_KEYCODE KC_APP
static const u8_t zmk_hid_report_desc[] = {
/* USAGE_PAGE (Generic Desktop) */
HID_GI_USAGE_PAGE,
USAGE_GEN_DESKTOP,
/* USAGE (Keyboard) */
HID_LI_USAGE,
USAGE_GEN_DESKTOP_KEYBOARD,
/* COLLECTION (Application) */
HID_MI_COLLECTION,
COLLECTION_APPLICATION,
/* REPORT ID (1) */
HID_GI_REPORT_ID,
0x01,
/* USAGE_PAGE (Keypad) */
HID_GI_USAGE_PAGE,
USAGE_GEN_DESKTOP_KEYPAD,
/* USAGE_MINIMUM (Keyboard LeftControl) */
HID_LI_USAGE_MIN(1),
0xE0,
/* USAGE_MAXIMUM (Keyboard Right GUI) */
HID_LI_USAGE_MAX(1),
0xE7,
/* LOGICAL_MINIMUM (0) */
HID_GI_LOGICAL_MIN(1),
0x00,
/* LOGICAL_MAXIMUM (1) */
HID_GI_LOGICAL_MAX(1),
0x01,
/* REPORT_SIZE (1) */
HID_GI_REPORT_SIZE,
0x01,
/* REPORT_COUNT (8) */
HID_GI_REPORT_COUNT,
0x08,
/* INPUT (Data,Var,Abs) */
HID_MI_INPUT,
0x02,
/* USAGE_PAGE (Keypad) */
HID_GI_USAGE_PAGE,
USAGE_GEN_DESKTOP_KEYPAD,
/* LOGICAL_MINIMUM (0) */
HID_GI_LOGICAL_MIN(1),
0x00,
/* LOGICAL_MAXIMUM (1) */
HID_GI_LOGICAL_MAX(1),
0x01,
/* USAGE_MINIMUM (Reserved) */
HID_LI_USAGE_MIN(1),
0x00,
/* USAGE_MAXIMUM (Keyboard Application) */
HID_LI_USAGE_MAX(1),
ZMK_HID_MAX_KEYCODE,
/* REPORT_SIZE (8) */
HID_GI_REPORT_SIZE,
0x01,
/* REPORT_COUNT (6) */
HID_GI_REPORT_COUNT,
ZMK_HID_MAX_KEYCODE + 1,
/* INPUT (Data,Ary,Abs) */
HID_MI_INPUT,
0x02,
/* USAGE_PAGE (Keypad) */
HID_GI_USAGE_PAGE,
USAGE_GEN_DESKTOP_KEYPAD,
/* REPORT_SIZE (8) */
HID_GI_REPORT_SIZE,
0x02,
/* REPORT_COUNT (6) */
HID_GI_REPORT_COUNT,
0x01,
/* INPUT (Cnst,Var,Abs) */
HID_MI_INPUT,
0x03,
/* END_COLLECTION */
HID_MI_COLLECTION_END,
/* USAGE_PAGE (Consumer) */
HID_GI_USAGE_PAGE,
0x0C,
/* USAGE (Consumer Control) */
HID_LI_USAGE,
0x01,
/* Consumer Page */
HID_MI_COLLECTION,
COLLECTION_APPLICATION,
/* REPORT ID (1) */
HID_GI_REPORT_ID,
0x02,
/* USAGE_PAGE (Consumer) */
HID_GI_USAGE_PAGE,
0x0C,
/* LOGICAL_MINIMUM (0) */
HID_GI_LOGICAL_MIN(1),
0x00,
/* LOGICAL_MAXIMUM (1) */
HID_GI_LOGICAL_MAX(1),
0x01,
/* USAGE (Scan Next Track) */
HID_LI_USAGE,
0xB5,
/* USAGE (Scan Previous Track) */
HID_LI_USAGE,
0xB6,
/* USAGE (Stop) */
HID_LI_USAGE,
0xB7,
/* USAGE (Eject) */
HID_LI_USAGE,
0xB8,
/* USAGE (Media Play/Pause) */
HID_LI_USAGE,
0xCD,
/* USAGE (Mute) */
HID_LI_USAGE,
0xE2,
/* USAGE (Volume Increment) */
HID_LI_USAGE,
0xE9,
/* USAGE (Volume Decrement) */
HID_LI_USAGE,
0xEA,
/* INPUT (Data,Ary,Abs) */
/* REPORT_SIZE (1) */
HID_GI_REPORT_SIZE,
0x01,
/* REPORT_COUNT (8) */
HID_GI_REPORT_COUNT,
0x08,
HID_MI_INPUT,
0x02,
/* END COLLECTION */
HID_MI_COLLECTION_END,
};
// struct zmk_hid_boot_report
// {
// u8_t modifiers;
// u8_t _unused;
// u8_t keys[6];
// } __packed;
struct zmk_hid_keypad_report_body
{
zmk_mod_flags modifiers;
u8_t keys[13];
} __packed;
struct zmk_hid_keypad_report
{
u8_t report_id;
struct zmk_hid_keypad_report_body body;
} __packed;
struct zmk_hid_consumer_report_body
{
u8_t keys;
} __packed;
struct zmk_hid_consumer_report
{
u8_t report_id;
struct zmk_hid_consumer_report_body body;
} __packed;
enum zmk_hid_report_changes
{
None = 0x00,
Keypad = (0x01 << 0x00),
Consumer = (0x01 << 0x01)
};
int zmk_hid_register_mod(zmk_mod modifier);
int zmk_hid_unregister_mod(zmk_mod modifier);
int zmk_hid_register_mods(zmk_mod_flags modifiers);
int zmk_hid_unregister_mods(zmk_mod_flags modifiers);
enum zmk_hid_report_changes zmk_hid_press_key(zmk_key key);
enum zmk_hid_report_changes zmk_hid_release_key(zmk_key key);
struct zmk_hid_keypad_report *zmk_hid_get_keypad_report();
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report();

10
app/include/zmk/hog.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include <zmk/keys.h>
#include <zmk/hid.h>
int zmk_hog_init();
int zmk_hog_send_keypad_report(struct zmk_hid_keypad_report_body *body);
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body);

28
app/include/zmk/keymap.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include <devicetree.h>
#include <usb/usb_device.h>
#include <usb/class/usb_hid.h>
#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)
};
*/
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);

17
app/include/zmk/keys.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include <zephyr.h>
#include <dt-bindings/zmk/keys.h>
typedef u32_t zmk_key;
typedef u8_t zmk_action;
typedef u8_t zmk_mod;
typedef u8_t zmk_mod_flags;
struct zmk_key_event
{
u32_t column;
u32_t row;
zmk_key key;
bool pressed;
};

View File

@@ -0,0 +1,8 @@
#pragma once
#define ZMK_MOCK_IS_PRESS(v) ((v & (0x01 << 31)) != 0)
#define ZMK_MOCK_PRESS(row, col, msec) (row + (col << 8) + (msec << 16) + (0x01 << 31))
#define ZMK_MOCK_RELEASE(row, col, msec) (row + (col << 8) + (msec << 16))
#define ZMK_MOCK_ROW(v) (v & 0xFF)
#define ZMK_MOCK_COL(v) ((v >> 8) & 0xFF)
#define ZMK_MOCK_MSEC(v) ((v & ~(0x01 << 31)) >> 16)

3
app/include/zmk/kscan.h Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
int zmk_kscan_init(char *name);

12
app/include/zmk/matrix.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#define ZMK_MATRIX_NODE_ID DT_CHOSEN(zmk_kscan)
#if DT_NODE_HAS_PROP(ZMK_MATRIX_NODE_ID,row_gpios)
#define ZMK_MATRIX_ROWS DT_PROP_LEN(ZMK_MATRIX_NODE_ID,row_gpios)
#define ZMK_MATRIX_COLS DT_PROP_LEN(ZMK_MATRIX_NODE_ID,col_gpios)
#else
#define ZMK_MATRIX_ROWS DT_PROP(ZMK_MATRIX_NODE_ID,rows)
#define ZMK_MATRIX_COLS DT_PROP(ZMK_MATRIX_NODE_ID,columns)
#endif

14
app/include/zmk/usb_hid.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef ZMK_USB_HID
#define ZMK_USB_HID
#include <usb/usb_device.h>
#include <usb/class/usb_hid.h>
#include <zmk/keys.h>
#include <zmk/hid.h>
int zmk_usb_hid_init();
int zmk_usb_hid_send_report(u8_t *report, size_t len);
#endif

2
app/prj.conf Normal file
View File

@@ -0,0 +1,2 @@
# CONFIG_LOG=y
# CONFIG_ZMK_LOG_LEVEL_DBG=y

193
app/src/ble.c Normal file
View File

@@ -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;
}

105
app/src/endpoints.c Normal file
View File

@@ -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);
}

88
app/src/handlers.c Normal file
View File

@@ -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);
};

130
app/src/hid.c Normal file
View File

@@ -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;
}

158
app/src/hog.c Normal file
View File

@@ -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));
};

74
app/src/keymap.c Normal file
View File

@@ -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;
}

75
app/src/kscan.c Normal file
View File

@@ -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;
}

130
app/src/kscan_composite.c Normal file
View File

@@ -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);

102
app/src/kscan_mock.c Normal file
View File

@@ -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)

39
app/src/main.c Normal file
View File

@@ -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
}

58
app/src/usb_hid.c Normal file
View File

@@ -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;
}

13
app/west.yml Normal file
View File

@@ -0,0 +1,13 @@
manifest:
remotes:
- name: zephyrproject-rtos
url-base: https://github.com/zephyrproject-rtos
- name: petejohanson
url-base: https://github.com/petejohanson
projects:
- name: zephyr
remote: petejohanson
revision: kconfig/external-sheilds-shields-as-list-fix
import: true
self:
path: zmk