mirror of
https://github.com/zmkfirmware/zmk.git
synced 2026-03-19 20:45:18 -05:00
Move Zephyr app into subdirectory.
This commit is contained in:
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
build/
|
||||
40
app/CMakeLists.txt
Normal file
40
app/CMakeLists.txt
Normal 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
84
app/Kconfig
Normal 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
1
app/boards/Kconfig
Normal file
@@ -0,0 +1 @@
|
||||
rsource "shields/*/Kconfig.shield"
|
||||
9
app/boards/arm/planck/CMakeLists.txt
Normal file
9
app/boards/arm/planck/CMakeLists.txt
Normal 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()
|
||||
8
app/boards/arm/planck/Kconfig.board
Normal file
8
app/boards/arm/planck/Kconfig.board
Normal 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
|
||||
39
app/boards/arm/planck/Kconfig.defconfig
Normal file
39
app/boards/arm/planck/Kconfig.defconfig
Normal 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
|
||||
6
app/boards/arm/planck/board.cmake
Normal file
6
app/boards/arm/planck/board.cmake
Normal 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)
|
||||
69
app/boards/arm/planck/pinmux.c
Normal file
69
app/boards/arm/planck/pinmux.c
Normal 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);
|
||||
162
app/boards/arm/planck/planck_rev6.dts
Normal file
162
app/boards/arm/planck/planck_rev6.dts
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
19
app/boards/arm/planck/planck_rev6.yaml
Normal file
19
app/boards/arm/planck/planck_rev6.yaml
Normal 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
|
||||
50
app/boards/arm/planck/planck_rev6_defconfig
Normal file
50
app/boards/arm/planck/planck_rev6_defconfig
Normal 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
|
||||
9
app/boards/native_posix.conf
Normal file
9
app/boards/native_posix.conf
Normal 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
|
||||
79
app/boards/native_posix.overlay
Normal file
79
app/boards/native_posix.overlay
Normal 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
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
13
app/boards/shields/petejohanson_handwire/Kconfig.defconfig
Normal file
13
app/boards/shields/petejohanson_handwire/Kconfig.defconfig
Normal 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
|
||||
5
app/boards/shields/petejohanson_handwire/Kconfig.shield
Normal file
5
app/boards/shields/petejohanson_handwire/Kconfig.shield
Normal 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)
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
|
||||
#define CC_RAIS ZC_CSTM(1)
|
||||
#define CC_LOWR ZC_CSTM(2)
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_ZMK_KSCAN_COMPOSITE_DRIVER=y
|
||||
@@ -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
94
app/cmake/keymap.cmake
Normal 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()
|
||||
9
app/drivers/zephyr/CMakeLists.txt
Normal file
9
app/drivers/zephyr/CMakeLists.txt
Normal 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()
|
||||
11
app/drivers/zephyr/Kconfig
Normal file
11
app/drivers/zephyr/Kconfig
Normal 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.
|
||||
|
||||
25
app/drivers/zephyr/dts/bindings/gpio-kscan.yaml
Normal file
25
app/drivers/zephyr/dts/bindings/gpio-kscan.yaml
Normal 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
|
||||
256
app/drivers/zephyr/kscan_gpio.c
Normal file
256
app/drivers/zephyr/kscan_gpio.c
Normal 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)
|
||||
3
app/drivers/zephyr/module.yaml
Normal file
3
app/drivers/zephyr/module.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
build:
|
||||
cmake: zephyr
|
||||
kconfig: zephyr/Kconfig
|
||||
13
app/dts/bindings/zmk,keymap.yaml
Normal file
13
app/dts/bindings/zmk,keymap.yaml
Normal 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
|
||||
27
app/dts/bindings/zmk,kscan-composite.yaml
Normal file
27
app/dts/bindings/zmk,kscan-composite.yaml
Normal 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
|
||||
17
app/dts/bindings/zmk,kscan-mock.yaml
Normal file
17
app/dts/bindings/zmk,kscan-mock.yaml
Normal 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
|
||||
14
app/dts/bindings/zmk,layers.yaml
Normal file
14
app/dts/bindings/zmk,layers.yaml
Normal 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
|
||||
|
||||
142
app/include/dt-bindings/zmk/keys.h
Normal file
142
app/include/dt-bindings/zmk/keys.h
Normal 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
7
app/include/zmk/ble.h
Normal 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);
|
||||
8
app/include/zmk/endpoints.h
Normal file
8
app/include/zmk/endpoints.h
Normal 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);
|
||||
8
app/include/zmk/handlers.h
Normal file
8
app/include/zmk/handlers.h
Normal 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
194
app/include/zmk/hid.h
Normal 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
10
app/include/zmk/hog.h
Normal 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
28
app/include/zmk/keymap.h
Normal 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
17
app/include/zmk/keys.h
Normal 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;
|
||||
};
|
||||
8
app/include/zmk/kscan-mock.h
Normal file
8
app/include/zmk/kscan-mock.h
Normal 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
3
app/include/zmk/kscan.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int zmk_kscan_init(char *name);
|
||||
12
app/include/zmk/matrix.h
Normal file
12
app/include/zmk/matrix.h
Normal 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
14
app/include/zmk/usb_hid.h
Normal 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
2
app/prj.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
# CONFIG_LOG=y
|
||||
# CONFIG_ZMK_LOG_LEVEL_DBG=y
|
||||
193
app/src/ble.c
Normal file
193
app/src/ble.c
Normal 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
105
app/src/endpoints.c
Normal 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
88
app/src/handlers.c
Normal 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
130
app/src/hid.c
Normal 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
158
app/src/hog.c
Normal 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
74
app/src/keymap.c
Normal 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
75
app/src/kscan.c
Normal 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
130
app/src/kscan_composite.c
Normal 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
102
app/src/kscan_mock.c
Normal 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
39
app/src/main.c
Normal 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
58
app/src/usb_hid.c
Normal 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
13
app/west.yml
Normal 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
|
||||
Reference in New Issue
Block a user