Merge branch 'zmkfirmware:main' into main

This commit is contained in:
shuiguohuooo 2022-06-14 01:13:36 +08:00 committed by GitHub
commit 54f7f39963
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
193 changed files with 20329 additions and 5533 deletions

131
.github/workflows/build-user-config.yml vendored Normal file
View file

@ -0,0 +1,131 @@
name: Reusable user config build
on:
workflow_call:
inputs:
build_matrix_path:
description: "Path to the build matrix file"
default: "build.yaml"
required: false
type: string
config_path:
description: "Path to the config directory"
default: "config"
required: false
type: string
fallback_binary:
description: "Fallback binary format, if no *.uf2 file was built"
default: "bin"
required: false
type: string
artifact_name:
description: 'Artifact output file name'
default: 'firmware'
required: false
type: string
jobs:
matrix:
runs-on: ubuntu-latest
name: Fetch Build Keyboards
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install yaml2json
run: python3 -m pip install remarshal
- name: Fetch Build Matrix
id: set-matrix
run: |
set -x
matrix=$(yaml2json ${{ inputs.build_matrix_path }} | jq -c .)
yaml2json ${{ inputs.build_matrix_path }}
echo "::set-output name=matrix::${matrix}"
build:
runs-on: ubuntu-latest
container:
image: zmkfirmware/zmk-build-arm:stable
needs: matrix
name: Build
strategy:
fail-fast: false
matrix: ${{fromJson(needs.matrix.outputs.matrix)}}
steps:
- name: Prepare variables
id: variables
run: |
set -x
if [ -n "${{ matrix.shield }}" ]
then
EXTRA_CMAKE_ARGS="-DSHIELD=${{ matrix.shield }}"
ARTIFACT_NAME="${{ matrix.shield }}-${{ matrix.board }}-zmk"
DISPLAY_NAME="${{ matrix.shield }} - ${{ matrix.board }}"
else
EXTRA_CMAKE_ARGS=
DISPLAY_NAME="${{ matrix.board }}"
ARTIFACT_NAME="${{ matrix.board }}-zmk"
fi
echo ::set-output name=extra-cmake-args::${EXTRA_CMAKE_ARGS}
echo ::set-output name=artifact-name::${ARTIFACT_NAME}
echo ::set-output name=display-name::${DISPLAY_NAME}
echo ::set-output name=zephyr-version::${ZEPHYR_VERSION}
- name: Checkout
uses: actions/checkout@v2
- name: Cache west modules
uses: actions/cache@v3.0.2
continue-on-error: true
env:
cache-name: cache-zephyr-${{ steps.variables.outputs.zephyr-version }}-modules
with:
path: |
modules/
tools/
zephyr/
bootloader/
zmk/
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/west.yml', '**/build.yaml') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: West Init
run: west init -l ${{ inputs.config_path }}
- name: West Update
run: west update
- name: West Zephyr export
run: west zephyr-export
- name: West Build (${{ steps.variables.outputs.display-name }})
run: |
set -x
west build -s zmk/app -b ${{ matrix.board }} -- -DZMK_CONFIG=${GITHUB_WORKSPACE}/${{ inputs.config_path }} ${{ steps.variables.outputs.extra-cmake-args }} ${{ matrix.cmake-args }}
- name: ${{ steps.variables.outputs.display-name }} Kconfig file
run: grep -v -e "^#" -e "^$" build/zephyr/.config | sort
- name: Rename artifacts
run: |
set -x
mkdir build/artifacts
if [ -f build/zephyr/zmk.uf2 ]
then
cp build/zephyr/zmk.uf2 "build/artifacts/${{ steps.variables.outputs.artifact-name }}.uf2"
elif [ -f build/zephyr/zmk.${{ inputs.fallback_binary }} ]
then
cp build/zephyr/zmk.${{ inputs.fallback_binary }} "build/artifacts/${{ steps.variables.outputs.artifact-name }}.${{ inputs.fallback_binary }}"
fi
- name: Archive (${{ steps.variables.outputs.display-name }})
uses: actions/upload-artifact@v2
with:
name: ${{ inputs.artifact_name }}
path: build/artifacts

View file

@ -26,7 +26,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Cache west modules
uses: actions/cache@v2
uses: actions/cache@v3.0.2
env:
cache-name: cache-zephyr-modules
with:

View file

@ -38,7 +38,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Cache west modules
uses: actions/cache@v2
uses: actions/cache@v3.0.2
env:
cache-name: cache-zephyr-modules
with:

View file

@ -1,18 +1,16 @@
cmake_minimum_required(VERSION 3.13.1)
set(CONFIG_APPLICATION_DEFINED_SYSCALL true)
list(APPEND BOARD_ROOT ${CMAKE_SOURCE_DIR})
list(APPEND DTS_ROOT ${CMAKE_SOURCE_DIR})
# Add our custom Zephyr module for drivers w/ syscalls, etc.
list(APPEND DTS_ROOT ${CMAKE_SOURCE_DIR}/drivers/zephyr)
set(ZephyrBuildConfiguration_ROOT ${CMAKE_SOURCE_DIR}/cmake)
list(APPEND ZEPHYR_EXTRA_MODULES
${CMAKE_CURRENT_SOURCE_DIR}/drivers
)
include(cmake/zmk_config.cmake)
# Find Zephyr. This also loads Zephyr's build system.
find_package(Zephyr REQUIRED HINTS ../zephyr)
project(zmk)
@ -26,26 +24,21 @@ target_sources(app PRIVATE src/stdlib.c)
target_sources(app PRIVATE src/activity.c)
target_sources(app PRIVATE src/kscan.c)
target_sources(app PRIVATE src/matrix_transform.c)
target_sources(app PRIVATE src/hid.c)
target_sources(app PRIVATE src/sensors.c)
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c)
target_sources(app PRIVATE src/event_manager.c)
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c)
target_sources(app PRIVATE src/events/activity_state_changed.c)
target_sources(app PRIVATE src/events/position_state_changed.c)
target_sources(app PRIVATE src/events/layer_state_changed.c)
target_sources(app PRIVATE src/events/keycode_state_changed.c)
target_sources(app PRIVATE src/events/modifiers_state_changed.c)
target_sources(app PRIVATE src/events/endpoint_selection_changed.c)
target_sources(app PRIVATE src/events/sensor_event.c)
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c)
target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c)
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/hid.c)
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c)
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c)
target_sources(app PRIVATE src/behaviors/behavior_caps_word.c)
@ -63,27 +56,35 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
target_sources(app PRIVATE src/combo.c)
target_sources(app PRIVATE src/behavior_queue.c)
target_sources(app PRIVATE src/conditional_layer.c)
target_sources(app PRIVATE src/endpoints.c)
target_sources(app PRIVATE src/events/endpoint_selection_changed.c)
target_sources(app PRIVATE src/hid_listener.c)
target_sources(app PRIVATE src/keymap.c)
target_sources(app PRIVATE src/events/layer_state_changed.c)
target_sources(app PRIVATE src/events/modifiers_state_changed.c)
target_sources(app PRIVATE src/events/keycode_state_changed.c)
if (CONFIG_ZMK_BLE)
target_sources(app PRIVATE src/events/ble_active_profile_changed.c)
target_sources(app PRIVATE src/behaviors/behavior_bt.c)
target_sources(app PRIVATE src/ble.c)
target_sources(app PRIVATE src/hog.c)
endif()
endif()
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)
target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/behaviors/behavior_backlight.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/behaviors/behavior_bt.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/battery.c)
if (CONFIG_ZMK_SPLIT_BLE AND (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
target_sources(app PRIVATE src/split_listener.c)
target_sources(app PRIVATE src/split/bluetooth/service.c)
endif()
if (CONFIG_ZMK_SPLIT_BLE AND CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
target_sources(app PRIVATE src/split/bluetooth/central.c)
endif()
target_sources_ifdef(CONFIG_ZMK_SPLIT app PRIVATE src/events/split_peripheral_status_changed.c)
add_subdirectory(src/split)
target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/usb.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_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.c)
target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/backlight.c)
target_sources(app PRIVATE src/endpoints.c)
target_sources(app PRIVATE src/hid_listener.c)
target_sources(app PRIVATE src/main.c)
add_subdirectory(src/display/)

View file

@ -88,6 +88,9 @@ if ZMK_USB
config USB_NUMOF_EP_WRITE_RETRIES
default 10
config USB_HID_POLL_INTERVAL_MS
default 1
#ZMK_USB
endif
@ -142,6 +145,18 @@ config ZMK_BLE_PASSKEY_ENTRY
bool "Experimental: Requiring typing passkey from host to pair BLE connection"
default n
config BT_PERIPHERAL_PREF_MIN_INT
default 6
config BT_PERIPHERAL_PREF_MAX_INT
default 12
config BT_PERIPHERAL_PREF_LATENCY
default 30
config BT_PERIPHERAL_PREF_TIMEOUT
default 400
#ZMK_BLE
endif
@ -151,107 +166,7 @@ endmenu
# HID
endmenu
menu "Split Support"
config ZMK_SPLIT
bool "Split keyboard support"
if ZMK_SPLIT
menuconfig ZMK_SPLIT_BLE
bool "Split keyboard support via BLE transport"
depends on ZMK_BLE
default y
select BT_USER_PHY_UPDATE
if ZMK_SPLIT_BLE
menuconfig ZMK_SPLIT_BLE_ROLE_CENTRAL
bool "Central"
select BT_CENTRAL
select BT_GATT_CLIENT
select BT_GATT_AUTO_DISCOVER_CCC
if ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE
int "Max number of key position state events to queue when received from peripherals"
default 5
config ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_STACK_SIZE
int "BLE split central write thread stack size"
default 512
config ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_QUEUE_SIZE
int "Max number of behavior run events to queue to send to the peripheral(s)"
default 5
endif
if !ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE
int "BLE split peripheral notify thread stack size"
default 512
config ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY
int "BLE split peripheral notify thread priority"
default 5
config ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE
int "Max number of key position state events to queue to send to the central"
default 10
config ZMK_USB
default n
config BT_MAX_PAIRED
default 1
config BT_MAX_CONN
default 1
config BT_GAP_AUTO_UPDATE_CONN_PARAMS
default n
#!ZMK_SPLIT_BLE_ROLE_CENTRAL
endif
#ZMK_SPLIT_BLE
endif
#ZMK_SPLIT
endif
if ZMK_BLE
if ZMK_SPLIT_BLE && ZMK_SPLIT_BLE_ROLE_CENTRAL
config BT_MAX_CONN
default 6
config BT_MAX_PAIRED
default 6
#ZMK_SPLIT_BLE && ZMK_SPLIT_BLE_ROLE_CENTRAL
endif
if !ZMK_SPLIT_BLE
config BT_MAX_CONN
default 5
config BT_MAX_PAIRED
default 5
#!ZMK_SPLIT_BLE
endif
#ZMK_BLE
endif
#Split Support
endmenu
rsource "src/split/Kconfig"
#Basic Keyboard Setup
endmenu
@ -415,6 +330,12 @@ config ZMK_BEHAVIORS_QUEUE_SIZE
int "Maximum number of behaviors to allow queueing from a macro or other complex behavior"
default 64
DT_COMPAT_ZMK_BEHAVIOR_KEY_TOGGLE := zmk,behavior-key-toggle
config ZMK_BEHAVIOR_KEY_TOGGLE
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_KEY_TOGGLE))
endmenu
menu "Advanced"
@ -448,12 +369,6 @@ config ZMK_KSCAN_EVENT_QUEUE_SIZE
int "Size of the event queue for KSCAN events to buffer events"
default 4
config ZMK_KSCAN_MOCK_DRIVER
bool "Enable mock kscan driver to simulate key presses"
config ZMK_KSCAN_COMPOSITE_DRIVER
bool "Enable composite kscan driver to combine kscan devices"
#KSCAN Settings
endmenu
@ -508,6 +423,11 @@ config ZMK_SETTINGS_SAVE_DEBOUNCE
#SETTINGS
endif
config ZMK_BATTERY_REPORT_INTERVAL
depends on ZMK_BLE
int "Battery level report interval in seconds"
default 60
#Advanced
endmenu

View file

@ -72,7 +72,7 @@
&spi2 {
status = "okay";
pinctrl-0 = <&spi2_sck_pb13 &spi2_miso_pb14 &spi2_mosi_pb15>;
pinctrl-0 = <&spi2_sck_pb13 &spi2_mosi_pb15>;
pinctrl-names = "default";
led_strip: ws2812@0 {

View file

@ -24,7 +24,4 @@ config ZMK_BLE
config ZMK_USB
default y
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_BLUEMICRO840_V1

View file

@ -25,9 +25,6 @@ config ZMK_BLE
config ZMK_USB
default y
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
config ZMK_KEYBOARD_NAME
default "BT60"

View file

@ -17,7 +17,4 @@ config ZMK_USB
config ZMK_KSCAN_MATRIX_POLLING
default y
config ZMK_KSCAN_COMPOSITE_DRIVER
default y
endif # BOARD_FERRIS

View file

@ -17,8 +17,6 @@ CONFIG_I2C=y
# ZMK Settings
CONFIG_ZMK_USB=y
CONFIG_ZMK_KSCAN_GPIO_DRIVER=y
CONFIG_ZMK_KSCAN_COMPOSITE_DRIVER=y
CONFIG_ZMK_KSCAN_MATRIX_POLLING=y
CONFIG_USB_SELF_POWERED=n

View file

@ -34,7 +34,4 @@ choice BOARD_MIKOTO_CHARGER_CURRENT
default BOARD_MIKOTO_CHARGER_CURRENT_100MA
endchoice
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_MIKOTO_520

View file

@ -38,7 +38,7 @@
vbatt: vbatt {
compatible = "zmk,battery-voltage-divider";
label = "BATTERY";
io-channels = <&adc 2>;
io-channels = <&adc 1>;
output-ohms = <10000000>;
full-ohms = <(10000000 + 4000000)>;
};

View file

@ -22,7 +22,4 @@ config ZMK_BLE
config ZMK_USB
default y
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_NICE60

View file

@ -23,17 +23,3 @@ config ZMK_USB
default y
endif # BOARD_NICE_NANO || BOARD_NICE_NANO_V2
if BOARD_NICE_NANO
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_NICE_NANO
if BOARD_NICE_NANO_V2
config ZMK_BATTERY_NRF_VDDH
default y
endif # BOARD_NICE_NANO_V2

View file

@ -32,9 +32,6 @@ if BOARD_NRFMICRO_13
config BOARD_NRFMICRO_CHARGER
default y
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_NRFMICRO_13
endif # BOARD_NRFMICRO_11 || BOARD_NRFMICRO_11_FLIPPED || BOARD_NRFMICRO_13

View file

@ -25,7 +25,4 @@ config ZMK_BLE
config ZMK_USB
default y
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_S40NC

View file

@ -1,6 +1,3 @@
CONFIG_KSCAN=n
CONFIG_ZMK_KSCAN_MOCK_DRIVER=y
CONFIG_ZMK_KSCAN_GPIO_DRIVER=n
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y

View file

@ -1,6 +1,3 @@
CONFIG_KSCAN=n
CONFIG_ZMK_KSCAN_MOCK_DRIVER=y
CONFIG_ZMK_KSCAN_GPIO_DRIVER=n
CONFIG_GPIO=n
# Enable to have the native posix build expose USBIP device(s)
# CONFIG_ZMK_USB=y

View file

@ -6,3 +6,10 @@ CONFIG_UART_INTERRUPT_DRIVEN=n
CONFIG_ZMK_USB=y
CONFIG_ZMK_BLE=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y
CONFIG_NVS=y
CONFIG_SETTINGS_NVS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y

View file

@ -6,7 +6,7 @@ if SHIELD_A_DUX_LEFT
config ZMK_KEYBOARD_NAME
default "A. Dux"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_BFO9000_LEFT
config ZMK_KEYBOARD_NAME
default "BFO-9000"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_CLOG_LEFT
config ZMK_KEYBOARD_NAME
default "Clog"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -3,7 +3,7 @@ if SHIELD_CORNE_LEFT
config ZMK_KEYBOARD_NAME
default "Corne"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif
@ -12,7 +12,7 @@ if SHIELD_CORNE_LEFT || SHIELD_CORNE_RIGHT
config ZMK_SPLIT
default y
if ZMK_DISPLAY
config I2C

View file

@ -6,7 +6,7 @@ if SHIELD_CRADIO_LEFT
config ZMK_KEYBOARD_NAME
default "Cradio"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_ELEPHANT42_LEFT
config ZMK_KEYBOARD_NAME
default "Elephant42"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_ERGODASH_LEFT
config ZMK_KEYBOARD_NAME
default "Ergodash"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -7,7 +7,7 @@ if SHIELD_FOURIER_LEFT
config ZMK_KEYBOARD_NAME
default "Fourier"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_HELIX_LEFT
config ZMK_KEYBOARD_NAME
default "Helix"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_IRIS_LEFT
config ZMK_KEYBOARD_NAME
default "Iris"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -4,7 +4,7 @@ if SHIELD_JIAN_LEFT
config ZMK_KEYBOARD_NAME
default "Jian"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_JIRAN_LEFT
config ZMK_KEYBOARD_NAME
default "Jiran"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -4,7 +4,7 @@ if SHIELD_JORNE_LEFT
config ZMK_KEYBOARD_NAME
default "Jorne"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -4,7 +4,7 @@ if SHIELD_KYRIA_LEFT || SHIELD_KYRIA_REV2_LEFT
config ZMK_KEYBOARD_NAME
default "Kyria"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_LEELOO_LEFT
config ZMK_KEYBOARD_NAME
default "Leeloo"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -32,8 +32,8 @@ Build command for the default keymap of Leeloo:
Build command for your custom keymap of Leeloo:
west build -d build/right -p -b nice_nano_v2 -- -DSHIELD=leeloo_right -DZMK_CONFIG="C:\dev\zmk\[yourNmae]\leeloo\config"
west build -d build/left -p -b nice_nano_v2 -- -DSHIELD=leeloo_left -DZMK_CONFIG="C:\dev\zmk\[yourName]\leeloo\config"
west build -d build/right -p -b nice_nano_v2 -- -DSHIELD=leeloo_right -DZMK_CONFIG="C:/dev/zmk/[yourNmae]/leeloo/config"
west build -d build/left -p -b nice_nano_v2 -- -DSHIELD=leeloo_left -DZMK_CONFIG="C:/dev/zmk/[yourName]/leeloo/config"
# Support
If you have any questions with regards to Leeloo, please [Contact Us](https://clicketysplit.ca/pages/contact-us).

View file

@ -7,6 +7,7 @@
/ {
chosen {
zephyr,display = &oled;
zmk,kscan = &kscan0;
zmk,matrix_transform = &default_transform;
};

View file

@ -4,7 +4,7 @@ if SHIELD_LILY58_LEFT
config ZMK_KEYBOARD_NAME
default "Lily58"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_LOTUS58_LEFT
config ZMK_KEYBOARD_NAME
default "Lotus58"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_MICRODOX_LEFT
config ZMK_KEYBOARD_NAME
default "Microdox"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -7,7 +7,7 @@ if SHIELD_QUEFRENCY_LEFT
config ZMK_KEYBOARD_NAME
default "Quefrency"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -5,7 +5,7 @@ if SHIELD_REDOX_LEFT
config ZMK_KEYBOARD_NAME
default "Redox"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_SOFLE_LEFT
config ZMK_KEYBOARD_NAME
default "Sofle"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -8,7 +8,7 @@ if SHIELD_SPLITREUS62_LEFT
config ZMK_KEYBOARD_NAME
default "Splitreus62"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -6,7 +6,7 @@ if SHIELD_ZODIARK_LEFT
config ZMK_KEYBOARD_NAME
default "Zodiark"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y
endif

View file

@ -7,6 +7,9 @@
cmake_minimum_required(VERSION 3.15)
list(APPEND BOARD_ROOT ${APPLICATION_SOURCE_DIR})
list(APPEND DTS_ROOT ${APPLICATION_SOURCE_DIR})
get_property(cached_user_config_value CACHE ZMK_CONFIG PROPERTY VALUE)
set(user_config_cli_argument ${cached_user_config_value}) # Either new or old
@ -57,9 +60,29 @@ if (ZMK_CONFIG)
endif()
endif()
if(DEFINED SHIELD)
string(REPLACE " " ";" SHIELD_AS_LIST "${SHIELD}")
endif()
foreach(root ${BOARD_ROOT})
set(shield_dir ${root}/boards/shields)
# Match the Kconfig.shield files in the shield directories to make sure we are
# finding shields, e.g. x_nucleo_iks01a1/Kconfig.shield
file(GLOB_RECURSE shields_refs_list ${shield_dir}/*/Kconfig.shield)
unset(SHIELD_LIST)
foreach(shields_refs ${shields_refs_list})
get_filename_component(shield_path ${shields_refs} DIRECTORY)
file(GLOB shield_overlays RELATIVE ${shield_path} ${shield_path}/*.overlay)
foreach(overlay ${shield_overlays})
get_filename_component(shield ${overlay} NAME_WE)
list(APPEND SHIELD_LIST ${shield})
set(SHIELD_DIR_${shield} ${shield_path})
endforeach()
endforeach()
if (EXISTS "${root}/boards/${BOARD}.overlay")
list(APPEND ZMK_DTC_FILES "${root}/boards/${BOARD}.overlay")
list(APPEND shield_dts_files "${root}/boards/${BOARD}.overlay")
endif()
if (NOT DEFINED BOARD_DIR_NAME)
find_path(BOARD_DIR
@ -74,34 +97,58 @@ foreach(root ${BOARD_ROOT})
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})
get_filename_component(SHIELD_DIR ${shield_path} NAME)
list(APPEND KEYMAP_DIRS ${shield_path})
foreach(s ${SHIELD_AS_LIST})
if(NOT ${s} IN_LIST SHIELD_LIST)
message(WARNING "Didn't find ${s}")
continue()
endif()
message(STATUS "Adding ${SHIELD_DIR_${s}}")
list(APPEND KEYMAP_DIRS ${SHIELD_DIR_${s}})
get_filename_component(shield_dir_name ${SHIELD_DIR_${s}} NAME)
list(APPEND SHIELD_DIR ${shield_dir_name})
endforeach()
endif()
endforeach()
# Give a shield like `kyria_rev2_left` we want to use `kyria_rev2` and `kyria` as candidate names for
# overlay/conf/keymap files.
if(DEFINED SHIELD)
foreach(s ${SHIELD_AS_LIST})
if (DEFINED $SHIELD_DIR_${s})
get_filename_component(shield_dir_name ${SHIELD_DIR_${s}} NAME)
endif()
string(REPLACE "_" ";" S_PIECES ${s})
list(LENGTH S_PIECES S_PIECES_LEN)
while(NOT S_PIECES STREQUAL "")
list(POP_BACK S_PIECES)
list(JOIN S_PIECES "_" s_substr)
if ("${s_substr}" STREQUAL "" OR "${s_substr}" STREQUAL "${shield_dir_name}")
break()
endif()
list(APPEND shield_candidate_names ${s_substr})
endwhile()
endforeach()
endif()
if (ZMK_CONFIG)
if (EXISTS ${ZMK_CONFIG})
message(STATUS "ZMK Config directory: ${ZMK_CONFIG}")
list(APPEND DTS_ROOT ${ZMK_CONFIG})
list(PREPEND KEYMAP_DIRS "${ZMK_CONFIG}")
if (SHIELD)
message(STATUS "Board: ${BOARD}, ${BOARD_DIR}, ${SHIELD}, ${SHIELD_DIR}")
list(APPEND overlay_candidates "${ZMK_CONFIG}/${SHIELD_DIR}.overlay")
list(APPEND overlay_candidates "${ZMK_CONFIG}/${SHIELD_DIR}_${BOARD}.overlay")
list(APPEND overlay_candidates "${ZMK_CONFIG}/${SHIELD}_${BOARD}.overlay")
list(APPEND overlay_candidates "${ZMK_CONFIG}/${SHIELD}.overlay")
list(APPEND config_candidates "${ZMK_CONFIG}/${SHIELD_DIR}.conf")
list(APPEND config_candidates "${ZMK_CONFIG}/${SHIELD_DIR}_${BOARD}.conf")
list(APPEND config_candidates "${ZMK_CONFIG}/${SHIELD}_${BOARD}.conf")
list(APPEND config_candidates "${ZMK_CONFIG}/${SHIELD}.conf")
if (DEFINED SHIELD)
foreach (s ${shield_candidate_names} ${SHIELD_AS_LIST})
if (DEFINED ${SHIELD_DIR_${s}})
get_filename_component(shield_dir_name ${SHIELD_DIR_${s}} NAME)
endif()
list(APPEND overlay_candidates "${ZMK_CONFIG}/${s}_${BOARD}.overlay")
list(APPEND overlay_candidates "${ZMK_CONFIG}/${s}.overlay")
if (NOT "${shield_dir_name}" STREQUAL "${s}")
list(APPEND config_candidates "${ZMK_CONFIG}/${shield_dir_name}_${BOARD}.conf")
list(APPEND config_candidates "${ZMK_CONFIG}/${shield_dir_name}.conf")
endif()
list(APPEND config_candidates "${ZMK_CONFIG}/${s}_${BOARD}.conf")
list(APPEND config_candidates "${ZMK_CONFIG}/${s}.conf")
endforeach()
endif()
# TODO: Board revisions?
@ -115,7 +162,7 @@ if (ZMK_CONFIG)
foreach(overlay ${overlay_candidates})
if (EXISTS "${overlay}")
message(STATUS "ZMK Config devicetree overlay: ${overlay}")
list(APPEND ZMK_DTC_FILES "${overlay}")
list(APPEND shield_dts_files "${overlay}")
break()
endif()
endforeach()
@ -123,8 +170,7 @@ if (ZMK_CONFIG)
foreach(conf ${config_candidates})
if (EXISTS "${conf}")
message(STATUS "ZMK Config Kconfig: ${conf}")
set(CONF_FILE "${conf}")
break()
list(APPEND shield_conf_files "${conf}")
endif()
endforeach()
else()
@ -135,22 +181,20 @@ endif()
if(NOT KEYMAP_FILE)
foreach(keymap_dir ${KEYMAP_DIRS})
foreach(keymap_prefix ${SHIELD} ${SHIELD_DIR} ${BOARD} ${BOARD_DIR_NAME})
foreach(keymap_prefix ${shield_candidate_names} ${SHIELD_AS_LIST} ${SHIELD_DIR} ${BOARD} ${BOARD_DIR_NAME})
if (EXISTS ${keymap_dir}/${keymap_prefix}.keymap)
set(KEYMAP_FILE "${keymap_dir}/${keymap_prefix}.keymap" CACHE STRING "Selected keymap file")
message(STATUS "Using keymap file: ${KEYMAP_FILE}")
set(DTC_OVERLAY_FILE ${KEYMAP_FILE})
break()
endif()
endforeach()
endforeach()
else()
message(STATUS "Using keymap file: ${KEYMAP_FILE}")
set(DTC_OVERLAY_FILE ${KEYMAP_FILE})
endif()
if (NOT KEYMAP_FILE)
message(FATAL_ERROR "Failed to locate keymap file!")
endif()
list(APPEND ZMK_DTC_FILES ${KEYMAP_FILE})
if (ZMK_DTC_FILES)
string(REPLACE ";" " " DTC_OVERLAY_FILE "${ZMK_DTC_FILES}")
message(WARNING "Failed to locate keymap file!")
endif()

View file

@ -11,6 +11,8 @@ shield:
include:
- board: bdn9_rev2
- board: nice60
- board: seeeduino_xiao_ble
shield: hummingbird
- board: nrf52840_m2
shield: m60
- board: planck_rev6

View file

@ -1,7 +1,7 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
add_subdirectory(gpio)
add_subdirectory_ifdef(CONFIG_ZMK_DRIVERS_GPIO gpio)
add_subdirectory(kscan)
add_subdirectory(sensor)
add_subdirectory(display)

View file

@ -4,5 +4,5 @@
zephyr_library_named(zmk__drivers__gpio)
zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include)
zephyr_library_sources_ifdef(CONFIG_GPIO_595 gpio_595.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_MCP23017 gpio_mcp23017.c)
zephyr_library_sources_ifndef(CONFIG_GPIO_MCP23017 ${ZEPHYR_BASE}/misc/empty_file.c)

View file

@ -1 +1,5 @@
menuconfig ZMK_DRIVERS_GPIO
bool "GPIO"
rsource "Kconfig.mcp23017"
rsource "Kconfig.595"

View file

@ -0,0 +1,25 @@
# 595 GPIO configuration options
# Copyright (c) 2022 The ZMK Contributors
# SPDX-License-Identifier: MIT
DT_COMPAT_ZMK_GPIO_595 := zmk,gpio-595
menuconfig GPIO_595
bool "595 Shift Register SPI driver"
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_GPIO_595))
depends on SPI
select HAS_DTS_GPIO
select ZMK_DRIVERS_GPIO
help
Enable driver for 595 shift register chip using SPI.
if GPIO_595
config GPIO_595_INIT_PRIORITY
int "Init priority"
default 75
help
Device driver initialization priority.
endif #GPIO_595

View file

@ -7,6 +7,7 @@ menuconfig GPIO_MCP23017
bool "MCP23017 I2C-based GPIO chip"
depends on I2C
select HAS_DTS_GPIO
select ZMK_DRIVERS_GPIO
help
Enable driver for MCP23017 I2C-based GPIO chip.

215
app/drivers/gpio/gpio_595.c Normal file
View file

@ -0,0 +1,215 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_gpio_595
/**
* @file Driver for 595 SPI-based GPIO driver.
*/
#include <errno.h>
#include <kernel.h>
#include <device.h>
#include <init.h>
#include <sys/byteorder.h>
#include <drivers/gpio.h>
#include <drivers/spi.h>
#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(gpio_595);
/** Configuration data */
struct reg_595_config {
/* gpio_driver_data needs to be first */
struct gpio_driver_config common;
struct spi_dt_spec bus;
uint8_t ngpios;
};
/** Runtime driver data */
struct reg_595_drv_data {
/* gpio_driver_data needs to be first */
struct gpio_driver_config data;
struct k_sem lock;
uint32_t gpio_cache;
};
static int reg_595_write_registers(const struct device *dev, uint32_t value) {
const struct reg_595_config *config = dev->config;
struct reg_595_drv_data *const drv_data = (struct reg_595_drv_data *const)dev->data;
int ret = 0;
uint8_t nwrite = config->ngpios / 8;
uint32_t reg_data = sys_cpu_to_be32(value);
/* Allow a sequence of 1-4 registers in sequence, lowest byte is for the first in the chain */
const struct spi_buf tx_buf[1] = {{
.buf = ((uint8_t *)&reg_data) + (4 - nwrite),
.len = nwrite,
}};
const struct spi_buf_set tx = {
.buffers = tx_buf,
.count = ARRAY_SIZE(tx_buf),
};
ret = spi_write_dt(&config->bus, &tx);
if (ret < 0) {
LOG_ERR("spi_write FAIL %d\n", ret);
return ret;
}
drv_data->gpio_cache = value;
return 0;
}
/**
* @brief Setup the pin direction (input or output)
*
* @param dev Device struct of the 595
* @param pin The pin number
* @param flags Flags of pin or port
*
* @return 0 if successful, failed otherwise
*/
static int setup_pin_dir(const struct device *dev, uint32_t pin, int flags) {
if ((flags & GPIO_OUTPUT) == 0U) {
return -ENOTSUP;
}
return 0;
}
static int reg_595_pin_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) {
int ret;
/* Can't do SPI bus operations from an ISR */
if (k_is_in_isr()) {
return -EWOULDBLOCK;
}
if ((flags & GPIO_OPEN_DRAIN) != 0U) {
return -ENOTSUP;
};
ret = setup_pin_dir(dev, pin, flags);
if (ret) {
LOG_ERR("595: error setting pin direction (%d)", ret);
}
return ret;
}
static int reg_595_port_get_raw(const struct device *dev, uint32_t *value) { return -ENOTSUP; }
static int reg_595_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value) {
struct reg_595_drv_data *const drv_data = (struct reg_595_drv_data *const)dev->data;
uint32_t buf;
int ret;
/* Can't do SPI bus operations from an ISR */
if (k_is_in_isr()) {
return -EWOULDBLOCK;
}
k_sem_take(&drv_data->lock, K_FOREVER);
buf = drv_data->gpio_cache;
buf = (buf & ~mask) | (mask & value);
ret = reg_595_write_registers(dev, buf);
k_sem_give(&drv_data->lock);
return ret;
}
static int reg_595_port_set_bits_raw(const struct device *dev, uint32_t mask) {
return reg_595_port_set_masked_raw(dev, mask, mask);
}
static int reg_595_port_clear_bits_raw(const struct device *dev, uint32_t mask) {
return reg_595_port_set_masked_raw(dev, mask, 0);
}
static int reg_595_port_toggle_bits(const struct device *dev, uint32_t mask) {
struct reg_595_drv_data *const drv_data = (struct reg_595_drv_data *const)dev->data;
uint32_t buf;
int ret;
/* Can't do SPI bus operations from an ISR */
if (k_is_in_isr()) {
return -EWOULDBLOCK;
}
k_sem_take(&drv_data->lock, K_FOREVER);
buf = drv_data->gpio_cache;
buf ^= mask;
ret = reg_595_write_registers(dev, buf);
k_sem_give(&drv_data->lock);
return ret;
}
static const struct gpio_driver_api api_table = {
.pin_configure = reg_595_pin_config,
.port_get_raw = reg_595_port_get_raw,
.port_set_masked_raw = reg_595_port_set_masked_raw,
.port_set_bits_raw = reg_595_port_set_bits_raw,
.port_clear_bits_raw = reg_595_port_clear_bits_raw,
.port_toggle_bits = reg_595_port_toggle_bits,
};
/**
* @brief Initialization function of 595
*
* @param dev Device struct
* @return 0 if successful, failed otherwise.
*/
static int reg_595_init(const struct device *dev) {
const struct reg_595_config *const config = dev->config;
struct reg_595_drv_data *const drv_data = (struct reg_595_drv_data *const)dev->data;
if (!device_is_ready(config->bus.bus)) {
LOG_ERR("Unable to get SPI bus device");
return -ENODEV;
}
k_sem_init(&drv_data->lock, 1, 1);
return 0;
}
#define GPIO_PORT_PIN_MASK_FROM_NGPIOS(ngpios) ((gpio_port_pins_t)(((uint64_t)1 << (ngpios)) - 1U))
#define GPIO_PORT_PIN_MASK_FROM_DT_INST(inst) \
GPIO_PORT_PIN_MASK_FROM_NGPIOS(DT_INST_PROP(inst, ngpios))
#define REG_595_INIT(n) \
static struct reg_595_config reg_595_##n##_config = { \
.common = \
{ \
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \
}, \
.bus = \
SPI_DT_SPEC_INST_GET(n, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0), \
.ngpios = DT_INST_PROP(n, ngpios), \
}; \
\
static struct reg_595_drv_data reg_595_##n##_drvdata = {}; \
\
/* This has to init after SPI master */ \
DEVICE_DT_INST_DEFINE(n, reg_595_init, NULL, &reg_595_##n##_drvdata, &reg_595_##n##_config, \
POST_KERNEL, CONFIG_GPIO_595_INIT_PRIORITY, &api_table);
DT_INST_FOREACH_STATUS_OKAY(REG_595_INIT)

View file

@ -5,8 +5,8 @@ zephyr_library_named(zmk__drivers__kscan)
zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER debounce.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio_matrix.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio_direct.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio_demux.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_MATRIX kscan_gpio_matrix.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DIRECT kscan_gpio_direct.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DEMUX kscan_gpio_demux.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_MOCK_DRIVER kscan_mock.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_COMPOSITE_DRIVER kscan_composite.c)

View file

@ -1,11 +1,39 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
DT_COMPAT_ZMK_KSCAN_COMPOSITE := zmk,kscan-composite
DT_COMPAT_ZMK_KSCAN_GPIO_DEMUX := zmk,kscan-gpio-demux
DT_COMPAT_ZMK_KSCAN_GPIO_DIRECT := zmk,kscan-gpio-direct
DT_COMPAT_ZMK_KSCAN_GPIO_MATRIX := zmk,kscan-gpio-matrix
DT_COMPAT_ZMK_KSCAN_MOCK := zmk,kscan-mock
config ZMK_KSCAN_COMPOSITE_DRIVER
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_COMPOSITE))
config ZMK_KSCAN_GPIO_DRIVER
bool "Enable GPIO kscan driver to detect key presses"
default y
bool
select GPIO
config ZMK_KSCAN_GPIO_DEMUX
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_GPIO_DEMUX))
select ZMK_KSCAN_GPIO_DRIVER
config ZMK_KSCAN_GPIO_DIRECT
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_GPIO_DIRECT))
select ZMK_KSCAN_GPIO_DRIVER
config ZMK_KSCAN_GPIO_MATRIX
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_GPIO_MATRIX))
select ZMK_KSCAN_GPIO_DRIVER
config ZMK_KSCAN_MOCK_DRIVER
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_MOCK))
if ZMK_KSCAN_GPIO_DRIVER
config ZMK_KSCAN_MATRIX_POLLING

View file

@ -13,8 +13,6 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
struct kscan_gpio_item_config {
char *label;
gpio_pin_t pin;
@ -251,5 +249,3 @@ struct kscan_gpio_item_config {
&gpio_driver_api_##n);
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -4,85 +4,297 @@
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_kscan_gpio_direct
#include "debounce.h"
#include <device.h>
#include <drivers/kscan.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include <drivers/kscan.h>
#include <kernel.h>
#include <logging/log.h>
#include <sys/util.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#define DT_DRV_COMPAT zmk_kscan_gpio_direct
struct kscan_gpio_item_config {
char *label;
gpio_pin_t pin;
gpio_flags_t flags;
};
#if CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS >= 0
#define INST_DEBOUNCE_PRESS_MS(n) CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS
#else
#define INST_DEBOUNCE_PRESS_MS(n) \
DT_INST_PROP_OR(n, debounce_period, DT_INST_PROP(n, debounce_press_ms))
#endif
union work_reference {
struct k_work_delayable delayed;
struct k_work direct;
};
#if CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS >= 0
#define INST_DEBOUNCE_RELEASE_MS(n) CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS
#else
#define INST_DEBOUNCE_RELEASE_MS(n) \
DT_INST_PROP_OR(n, debounce_period, DT_INST_PROP(n, debounce_release_ms))
#endif
struct kscan_gpio_config {
uint8_t num_of_inputs;
uint8_t debounce_period;
struct kscan_gpio_item_config inputs[];
};
#define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
#define USE_INTERRUPTS (!USE_POLLING)
struct kscan_gpio_data {
#if defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
struct k_timer poll_timer;
#endif /* defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING) */
kscan_callback_t callback;
union work_reference work;
#define COND_INTERRUPTS(code) COND_CODE_1(CONFIG_ZMK_KSCAN_DIRECT_POLLING, (), code)
#define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
COND_CODE_1(CONFIG_ZMK_KSCAN_DIRECT_POLLING, pollcode, intcode)
#define INST_INPUTS_LEN(n) DT_INST_PROP_LEN(n, input_gpios)
#define KSCAN_DIRECT_INPUT_CFG_INIT(idx, inst_idx) \
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), input_gpios, idx),
struct kscan_direct_irq_callback {
const struct device *dev;
uint32_t pin_state;
const struct device *inputs[];
};
static const struct device **kscan_gpio_input_devices(const struct device *dev) {
struct kscan_gpio_data *data = dev->data;
return data->inputs;
}
static const struct kscan_gpio_item_config *kscan_gpio_input_configs(const struct device *dev) {
const struct kscan_gpio_config *cfg = dev->config;
return cfg->inputs;
}
static void kscan_gpio_direct_queue_read(union work_reference *work, uint8_t debounce_period) {
if (debounce_period > 0) {
k_work_reschedule(&work->delayed, K_MSEC(debounce_period));
} else {
k_work_submit(&work->direct);
}
}
#if !defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
struct kscan_gpio_irq_callback {
const struct device *dev;
union work_reference *work;
uint8_t debounce_period;
struct gpio_callback callback;
};
static int kscan_gpio_config_interrupts(const struct device *dev, gpio_flags_t flags) {
const struct kscan_gpio_config *cfg = dev->config;
const struct device **devices = kscan_gpio_input_devices(dev);
const struct kscan_gpio_item_config *configs = kscan_gpio_input_configs(dev);
struct kscan_direct_data {
const struct device *dev;
kscan_callback_t callback;
struct k_work_delayable work;
#if USE_INTERRUPTS
/** Array of length config->inputs.len */
struct kscan_direct_irq_callback *irqs;
#endif
/** Timestamp of the current or scheduled scan. */
int64_t scan_time;
/** Current state of the inputs as an array of length config->inputs.len */
struct debounce_state *pin_state;
};
for (int i = 0; i < cfg->num_of_inputs; i++) {
const struct device *dev = devices[i];
const struct kscan_gpio_item_config *cfg = &configs[i];
struct kscan_gpio_list {
const struct gpio_dt_spec *gpios;
size_t len;
};
int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags);
/** Define a kscan_gpio_list from a compile-time GPIO array. */
#define KSCAN_GPIO_LIST(gpio_array) \
((struct kscan_gpio_list){.gpios = gpio_array, .len = ARRAY_SIZE(gpio_array)})
struct kscan_direct_config {
struct kscan_gpio_list inputs;
struct debounce_config debounce_config;
int32_t debounce_scan_period_ms;
int32_t poll_period_ms;
bool toggle_mode;
};
#if USE_INTERRUPTS
static int kscan_direct_interrupt_configure(const struct device *dev, const gpio_flags_t flags) {
const struct kscan_direct_config *config = dev->config;
for (int i = 0; i < config->inputs.len; i++) {
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
int err = gpio_pin_interrupt_configure_dt(gpio, flags);
if (err) {
LOG_ERR("Unable to configure interrupt for pin %u on %s", gpio->pin, gpio->port->name);
return err;
}
}
return 0;
}
#endif
#if USE_INTERRUPTS
static int kscan_direct_interrupt_enable(const struct device *dev) {
return kscan_direct_interrupt_configure(dev, GPIO_INT_LEVEL_ACTIVE);
}
#endif
#if USE_INTERRUPTS
static int kscan_direct_interrupt_disable(const struct device *dev) {
return kscan_direct_interrupt_configure(dev, GPIO_INT_DISABLE);
}
#endif
#if USE_INTERRUPTS
static void kscan_direct_irq_callback_handler(const struct device *port, struct gpio_callback *cb,
const gpio_port_pins_t pin) {
struct kscan_direct_irq_callback *irq_data =
CONTAINER_OF(cb, struct kscan_direct_irq_callback, callback);
struct kscan_direct_data *data = irq_data->dev->data;
// Disable our interrupts temporarily to avoid re-entry while we scan.
kscan_direct_interrupt_disable(data->dev);
data->scan_time = k_uptime_get();
k_work_reschedule(&data->work, K_NO_WAIT);
}
#endif
static gpio_flags_t kscan_gpio_get_extra_flags(const struct gpio_dt_spec *gpio, bool active) {
if (!active) {
return ((BIT(0) & gpio->dt_flags) ? GPIO_PULL_UP : GPIO_PULL_DOWN);
}
return 0;
}
static int kscan_inputs_set_flags(const struct kscan_gpio_list *inputs,
const struct gpio_dt_spec *active_gpio) {
gpio_flags_t extra_flags;
for (int i = 0; i < inputs->len; i++) {
extra_flags = GPIO_INPUT | kscan_gpio_get_extra_flags(&inputs->gpios[i],
&inputs->gpios[i] == active_gpio);
LOG_DBG("Extra flags equal to: %d", extra_flags);
int err = gpio_pin_configure_dt(&inputs->gpios[i], extra_flags);
if (err) {
LOG_ERR("Unable to configure flags on pin %d on %s", inputs->gpios[i].pin,
inputs->gpios[i].port->name);
return err;
}
}
return 0;
}
static void kscan_direct_read_continue(const struct device *dev) {
const struct kscan_direct_config *config = dev->config;
struct kscan_direct_data *data = dev->data;
data->scan_time += config->debounce_scan_period_ms;
k_work_reschedule(&data->work, K_TIMEOUT_ABS_MS(data->scan_time));
}
static void kscan_direct_read_end(const struct device *dev) {
#if USE_INTERRUPTS
// Return to waiting for an interrupt.
kscan_direct_interrupt_enable(dev);
#else
struct kscan_direct_data *data = dev->data;
const struct kscan_direct_config *config = dev->config;
data->scan_time += config->poll_period_ms;
// Return to polling slowly.
k_work_reschedule(&data->work, K_TIMEOUT_ABS_MS(data->scan_time));
#endif
}
static int kscan_direct_read(const struct device *dev) {
struct kscan_direct_data *data = dev->data;
const struct kscan_direct_config *config = dev->config;
// Read the inputs.
for (int i = 0; i < config->inputs.len; i++) {
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
const bool active = gpio_pin_get_dt(gpio);
debounce_update(&data->pin_state[i], active, config->debounce_scan_period_ms,
&config->debounce_config);
}
// Process the new state.
bool continue_scan = false;
for (int i = 0; i < config->inputs.len; i++) {
struct debounce_state *state = &data->pin_state[i];
if (debounce_get_changed(state)) {
const bool pressed = debounce_is_pressed(state);
LOG_DBG("Sending event at 0,%i state %s", i, pressed ? "on" : "off");
data->callback(dev, 0, i, pressed);
if (config->toggle_mode && pressed) {
kscan_inputs_set_flags(&config->inputs, &config->inputs.gpios[i]);
}
}
continue_scan = continue_scan || debounce_is_active(state);
}
if (continue_scan) {
// At least one key is pressed or the debouncer has not yet decided if
// it is pressed. Poll quickly until everything is released.
kscan_direct_read_continue(dev);
} else {
// All keys are released. Return to normal.
kscan_direct_read_end(dev);
}
return 0;
}
static void kscan_direct_work_handler(struct k_work *work) {
struct k_work_delayable *dwork = CONTAINER_OF(work, struct k_work_delayable, work);
struct kscan_direct_data *data = CONTAINER_OF(dwork, struct kscan_direct_data, work);
kscan_direct_read(data->dev);
}
static int kscan_direct_configure(const struct device *dev, kscan_callback_t callback) {
struct kscan_direct_data *data = dev->data;
if (!callback) {
return -EINVAL;
}
data->callback = callback;
return 0;
}
static int kscan_direct_enable(const struct device *dev) {
struct kscan_direct_data *data = dev->data;
data->scan_time = k_uptime_get();
// Read will automatically start interrupts/polling once done.
return kscan_direct_read(dev);
}
static int kscan_direct_disable(const struct device *dev) {
struct kscan_direct_data *data = dev->data;
k_work_cancel_delayable(&data->work);
#if USE_INTERRUPTS
return kscan_direct_interrupt_disable(dev);
#else
return 0;
#endif
}
static int kscan_direct_init_input_inst(const struct device *dev, const struct gpio_dt_spec *gpio,
const int index, bool toggle_mode) {
if (!device_is_ready(gpio->port)) {
LOG_ERR("GPIO is not ready: %s", gpio->port->name);
return -ENODEV;
}
int err = gpio_pin_configure_dt(
gpio, GPIO_INPUT | (toggle_mode ? kscan_gpio_get_extra_flags(gpio, false) : 0));
if (err) {
LOG_ERR("Unable to configure pin %u on %s for input", gpio->pin, gpio->port->name);
return err;
}
LOG_DBG("Configured pin %u on %s for input", gpio->pin, gpio->port->name);
#if USE_INTERRUPTS
struct kscan_direct_data *data = dev->data;
struct kscan_direct_irq_callback *irq = &data->irqs[index];
irq->dev = dev;
gpio_init_callback(&irq->callback, kscan_direct_irq_callback_handler, BIT(gpio->pin));
err = gpio_add_callback(gpio->port, &irq->callback);
if (err) {
LOG_ERR("Error adding the callback to the input device: %i", err);
return err;
}
#endif
return 0;
}
static int kscan_direct_init_inputs(const struct device *dev) {
const struct kscan_direct_config *config = dev->config;
for (int i = 0; i < config->inputs.len; i++) {
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
int err = kscan_direct_init_input_inst(dev, gpio, i, config->toggle_mode);
if (err) {
LOG_ERR("Unable to enable direct GPIO interrupt");
return err;
}
}
@ -90,157 +302,55 @@ static int kscan_gpio_config_interrupts(const struct device *dev, gpio_flags_t f
return 0;
}
static int kscan_gpio_direct_enable(const struct device *dev) {
return kscan_gpio_config_interrupts(dev, GPIO_INT_LEVEL_ACTIVE);
}
static int kscan_gpio_direct_disable(const struct device *dev) {
return kscan_gpio_config_interrupts(dev, GPIO_INT_DISABLE);
}
static int kscan_direct_init(const struct device *dev) {
struct kscan_direct_data *data = dev->data;
static void kscan_gpio_irq_callback_handler(const struct device *dev, struct gpio_callback *cb,
gpio_port_pins_t pin) {
struct kscan_gpio_irq_callback *data =
CONTAINER_OF(cb, struct kscan_gpio_irq_callback, callback);
data->dev = dev;
kscan_gpio_direct_disable(data->dev);
kscan_gpio_direct_queue_read(data->work, data->debounce_period);
}
kscan_direct_init_inputs(dev);
#else /* !defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING) */
static void kscan_gpio_timer_handler(struct k_timer *timer) {
struct kscan_gpio_data *data = CONTAINER_OF(timer, struct kscan_gpio_data, poll_timer);
kscan_gpio_direct_queue_read(&data->work, 0);
}
static int kscan_gpio_direct_enable(const struct device *dev) {
struct kscan_gpio_data *data = dev->data;
k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10));
return 0;
}
static int kscan_gpio_direct_disable(const struct device *dev) {
struct kscan_gpio_data *data = dev->data;
k_timer_stop(&data->poll_timer);
return 0;
}
#endif /* defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING) */
static int kscan_gpio_direct_configure(const struct device *dev, kscan_callback_t callback) {
struct kscan_gpio_data *data = dev->data;
if (!callback) {
return -EINVAL;
}
data->callback = callback;
return 0;
}
static int kscan_gpio_read(const struct device *dev) {
struct kscan_gpio_data *data = dev->data;
const struct kscan_gpio_config *cfg = dev->config;
uint32_t read_state = data->pin_state;
bool submit_follow_up_read = false;
for (int i = 0; i < cfg->num_of_inputs; i++) {
const struct device *in_dev = kscan_gpio_input_devices(dev)[i];
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs(dev)[i];
WRITE_BIT(read_state, i, gpio_pin_get(in_dev, in_cfg->pin) > 0);
}
for (int i = 0; i < cfg->num_of_inputs; i++) {
bool prev_pressed = BIT(i) & data->pin_state;
bool pressed = (BIT(i) & read_state) != 0;
submit_follow_up_read = (submit_follow_up_read || pressed);
if (pressed != prev_pressed) {
LOG_DBG("Sending event at %d,%d state %s", 0, i, (pressed ? "on" : "off"));
WRITE_BIT(data->pin_state, i, pressed);
data->callback(dev, 0, i, pressed);
}
}
#if !defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
if (submit_follow_up_read) {
kscan_gpio_direct_queue_read(&data->work, cfg->debounce_period);
} else {
kscan_gpio_direct_enable(dev);
}
#endif
k_work_init_delayable(&data->work, kscan_direct_work_handler);
return 0;
}
static void kscan_gpio_work_handler(struct k_work *work) {
struct kscan_gpio_data *data = CONTAINER_OF(work, struct kscan_gpio_data, work);
kscan_gpio_read(data->dev);
}
static const struct kscan_driver_api gpio_driver_api = {
.config = kscan_gpio_direct_configure,
.enable_callback = kscan_gpio_direct_enable,
.disable_callback = kscan_gpio_direct_disable,
static const struct kscan_driver_api kscan_direct_api = {
.config = kscan_direct_configure,
.enable_callback = kscan_direct_enable,
.disable_callback = kscan_direct_disable,
};
#define KSCAN_DIRECT_INPUT_ITEM(i, n) \
{ \
.label = DT_INST_GPIO_LABEL_BY_IDX(n, input_gpios, i), \
.pin = DT_INST_GPIO_PIN_BY_IDX(n, input_gpios, i), \
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, input_gpios, i), \
},
#define KSCAN_DIRECT_INIT(n) \
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(n) <= DEBOUNCE_COUNTER_MAX, \
"ZMK_KSCAN_DEBOUNCE_PRESS_MS or debounce-press-ms is too large"); \
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
\
static const struct gpio_dt_spec kscan_direct_inputs_##n[] = { \
UTIL_LISTIFY(INST_INPUTS_LEN(n), KSCAN_DIRECT_INPUT_CFG_INIT, n)}; \
\
static struct debounce_state kscan_direct_state_##n[INST_INPUTS_LEN(n)]; \
\
COND_INTERRUPTS( \
(static struct kscan_direct_irq_callback kscan_direct_irqs_##n[INST_INPUTS_LEN(n)];)) \
\
static struct kscan_direct_data kscan_direct_data_##n = { \
.pin_state = kscan_direct_state_##n, COND_INTERRUPTS((.irqs = kscan_direct_irqs_##n, ))}; \
\
static struct kscan_direct_config kscan_direct_config_##n = { \
.inputs = KSCAN_GPIO_LIST(kscan_direct_inputs_##n), \
.debounce_config = \
{ \
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \
.debounce_release_ms = INST_DEBOUNCE_RELEASE_MS(n), \
}, \
.debounce_scan_period_ms = DT_INST_PROP(n, debounce_scan_period_ms), \
.poll_period_ms = DT_INST_PROP(n, poll_period_ms), \
.toggle_mode = DT_INST_PROP(n, toggle_mode), \
}; \
\
DEVICE_DT_INST_DEFINE(n, &kscan_direct_init, NULL, &kscan_direct_data_##n, \
&kscan_direct_config_##n, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, \
&kscan_direct_api);
#define INST_INPUT_LEN(n) DT_INST_PROP_LEN(n, input_gpios)
#define GPIO_INST_INIT(n) \
COND_CODE_0(IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING), \
(static struct kscan_gpio_irq_callback irq_callbacks_##n[INST_INPUT_LEN(n)];), ()) \
static struct kscan_gpio_data kscan_gpio_data_##n = { \
.inputs = {[INST_INPUT_LEN(n) - 1] = NULL}}; \
static int kscan_gpio_init_##n(const struct device *dev) { \
struct kscan_gpio_data *data = dev->data; \
const struct kscan_gpio_config *cfg = dev->config; \
int err; \
const struct device **input_devices = kscan_gpio_input_devices(dev); \
for (int i = 0; i < cfg->num_of_inputs; i++) { \
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs(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; \
} \
COND_CODE_0( \
IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING), \
(irq_callbacks_##n[i].work = &data->work; irq_callbacks_##n[i].dev = dev; \
irq_callbacks_##n[i].debounce_period = cfg->debounce_period; \
gpio_init_callback(&irq_callbacks_##n[i].callback, \
kscan_gpio_irq_callback_handler, 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; \
}), \
()) \
} \
data->dev = dev; \
COND_CODE_1(IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING), \
(k_timer_init(&data->poll_timer, kscan_gpio_timer_handler, NULL);), ()) \
if (cfg->debounce_period > 0) { \
k_work_init_delayable(&data->work.delayed, kscan_gpio_work_handler); \
} else { \
k_work_init(&data->work.direct, kscan_gpio_work_handler); \
} \
return 0; \
} \
static const struct kscan_gpio_config kscan_gpio_config_##n = { \
.inputs = {UTIL_LISTIFY(INST_INPUT_LEN(n), KSCAN_DIRECT_INPUT_ITEM, n)}, \
.num_of_inputs = INST_INPUT_LEN(n), \
.debounce_period = DT_INST_PROP(n, debounce_period)}; \
DEVICE_DT_INST_DEFINE(n, kscan_gpio_init_##n, NULL, &kscan_gpio_data_##n, \
&kscan_gpio_config_##n, POST_KERNEL, CONFIG_ZMK_KSCAN_INIT_PRIORITY, \
&gpio_driver_api);
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
DT_INST_FOREACH_STATUS_OKAY(KSCAN_DIRECT_INIT);

View file

@ -19,8 +19,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define DT_DRV_COMPAT zmk_kscan_gpio_matrix
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#define INST_DIODE_DIR(n) DT_ENUM_IDX(DT_DRV_INST(n), diode_direction)
#define COND_DIODE_DIR(n, row2col_code, col2row_code) \
COND_CODE_0(INST_DIODE_DIR(n), row2col_code, col2row_code)
@ -51,29 +49,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, pollcode, intcode)
// TODO (Zephr 2.6): replace the following
// kscan_gpio_dt_spec -> gpio_dt_spec
// KSCAN_GPIO_DT_SPEC_GET_BY_IDX -> GPIO_DT_SPEC_GET_BY_IDX
// gpio_pin_get -> gpio_pin_get_dt
// gpio_pin_set -> gpio_pin_set_dt
// gpio_pin_interrupt_configure -> gpio_pin_interrupt_configure_dt
struct kscan_gpio_dt_spec {
const struct device *port;
gpio_pin_t pin;
gpio_dt_flags_t dt_flags;
};
#define KSCAN_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \
{ \
.port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \
.pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \
.dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \
}
#define KSCAN_GPIO_ROW_CFG_INIT(idx, inst_idx) \
KSCAN_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), row_gpios, idx),
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), row_gpios, idx),
#define KSCAN_GPIO_COL_CFG_INIT(idx, inst_idx) \
KSCAN_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), col_gpios, idx),
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), col_gpios, idx),
enum kscan_diode_direction {
KSCAN_ROW2COL,
@ -103,7 +82,7 @@ struct kscan_matrix_data {
};
struct kscan_gpio_list {
const struct kscan_gpio_dt_spec *gpios;
const struct gpio_dt_spec *gpios;
size_t len;
};
@ -146,9 +125,9 @@ static int kscan_matrix_set_all_outputs(const struct device *dev, const int valu
const struct kscan_matrix_config *config = dev->config;
for (int i = 0; i < config->outputs.len; i++) {
const struct kscan_gpio_dt_spec *gpio = &config->outputs.gpios[i];
const struct gpio_dt_spec *gpio = &config->outputs.gpios[i];
int err = gpio_pin_set(gpio->port, gpio->pin, value);
int err = gpio_pin_set_dt(gpio, value);
if (err) {
LOG_ERR("Failed to set output %i to %i: %i", i, value, err);
return err;
@ -163,9 +142,9 @@ static int kscan_matrix_interrupt_configure(const struct device *dev, const gpio
const struct kscan_matrix_config *config = dev->config;
for (int i = 0; i < config->inputs.len; i++) {
const struct kscan_gpio_dt_spec *gpio = &config->inputs.gpios[i];
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
int err = gpio_pin_interrupt_configure(gpio->port, gpio->pin, flags);
int err = gpio_pin_interrupt_configure_dt(gpio, flags);
if (err) {
LOG_ERR("Unable to configure interrupt for pin %u on %s", gpio->pin, gpio->port->name);
return err;
@ -248,25 +227,25 @@ static int kscan_matrix_read(const struct device *dev) {
// Scan the matrix.
for (int o = 0; o < config->outputs.len; o++) {
const struct kscan_gpio_dt_spec *out_gpio = &config->outputs.gpios[o];
const struct gpio_dt_spec *out_gpio = &config->outputs.gpios[o];
int err = gpio_pin_set(out_gpio->port, out_gpio->pin, 1);
int err = gpio_pin_set_dt(out_gpio, 1);
if (err) {
LOG_ERR("Failed to set output %i active: %i", o, err);
return err;
}
for (int i = 0; i < config->inputs.len; i++) {
const struct kscan_gpio_dt_spec *in_gpio = &config->inputs.gpios[i];
const struct gpio_dt_spec *in_gpio = &config->inputs.gpios[i];
const int index = state_index_io(config, i, o);
const bool active = gpio_pin_get(in_gpio->port, in_gpio->pin);
const bool active = gpio_pin_get_dt(in_gpio);
debounce_update(&data->matrix_state[index], active, config->debounce_scan_period_ms,
&config->debounce_config);
}
err = gpio_pin_set(out_gpio->port, out_gpio->pin, 0);
err = gpio_pin_set_dt(out_gpio, 0);
if (err) {
LOG_ERR("Failed to set output %i inactive: %i", o, err);
return err;
@ -342,14 +321,14 @@ static int kscan_matrix_disable(const struct device *dev) {
#endif
}
static int kscan_matrix_init_input_inst(const struct device *dev,
const struct kscan_gpio_dt_spec *gpio, const int index) {
static int kscan_matrix_init_input_inst(const struct device *dev, const struct gpio_dt_spec *gpio,
const int index) {
if (!device_is_ready(gpio->port)) {
LOG_ERR("GPIO is not ready: %s", gpio->port->name);
return -ENODEV;
}
int err = gpio_pin_configure(gpio->port, gpio->pin, GPIO_INPUT | gpio->dt_flags);
int err = gpio_pin_configure_dt(gpio, GPIO_INPUT);
if (err) {
LOG_ERR("Unable to configure pin %u on %s for input", gpio->pin, gpio->port->name);
return err;
@ -377,7 +356,7 @@ static int kscan_matrix_init_inputs(const struct device *dev) {
const struct kscan_matrix_config *config = dev->config;
for (int i = 0; i < config->inputs.len; i++) {
const struct kscan_gpio_dt_spec *gpio = &config->inputs.gpios[i];
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
int err = kscan_matrix_init_input_inst(dev, gpio, i);
if (err) {
return err;
@ -388,13 +367,13 @@ static int kscan_matrix_init_inputs(const struct device *dev) {
}
static int kscan_matrix_init_output_inst(const struct device *dev,
const struct kscan_gpio_dt_spec *gpio) {
const struct gpio_dt_spec *gpio) {
if (!device_is_ready(gpio->port)) {
LOG_ERR("GPIO is not ready: %s", gpio->port->name);
return -ENODEV;
}
int err = gpio_pin_configure(gpio->port, gpio->pin, GPIO_OUTPUT | gpio->dt_flags);
int err = gpio_pin_configure_dt(gpio, GPIO_OUTPUT);
if (err) {
LOG_ERR("Unable to configure pin %u on %s for output", gpio->pin, gpio->port->name);
return err;
@ -409,7 +388,7 @@ static int kscan_matrix_init_outputs(const struct device *dev) {
const struct kscan_matrix_config *config = dev->config;
for (int i = 0; i < config->outputs.len; i++) {
const struct kscan_gpio_dt_spec *gpio = &config->outputs.gpios[i];
const struct gpio_dt_spec *gpio = &config->outputs.gpios[i];
int err = kscan_matrix_init_output_inst(dev, gpio);
if (err) {
return err;
@ -439,48 +418,46 @@ static const struct kscan_driver_api kscan_matrix_api = {
.disable_callback = kscan_matrix_disable,
};
#define KSCAN_MATRIX_INIT(index) \
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(index) <= DEBOUNCE_COUNTER_MAX, \
#define KSCAN_MATRIX_INIT(n) \
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(n) <= DEBOUNCE_COUNTER_MAX, \
"ZMK_KSCAN_DEBOUNCE_PRESS_MS or debounce-press-ms is too large"); \
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(index) <= DEBOUNCE_COUNTER_MAX, \
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
\
static const struct kscan_gpio_dt_spec kscan_matrix_rows_##index[] = { \
UTIL_LISTIFY(INST_ROWS_LEN(index), KSCAN_GPIO_ROW_CFG_INIT, index)}; \
static const struct gpio_dt_spec kscan_matrix_rows_##n[] = { \
UTIL_LISTIFY(INST_ROWS_LEN(n), KSCAN_GPIO_ROW_CFG_INIT, n)}; \
\
static const struct kscan_gpio_dt_spec kscan_matrix_cols_##index[] = { \
UTIL_LISTIFY(INST_COLS_LEN(index), KSCAN_GPIO_COL_CFG_INIT, index)}; \
static const struct gpio_dt_spec kscan_matrix_cols_##n[] = { \
UTIL_LISTIFY(INST_COLS_LEN(n), KSCAN_GPIO_COL_CFG_INIT, n)}; \
\
static struct debounce_state kscan_matrix_state_##index[INST_MATRIX_LEN(index)]; \
static struct debounce_state kscan_matrix_state_##n[INST_MATRIX_LEN(n)]; \
\
COND_INTERRUPTS((static struct kscan_matrix_irq_callback \
kscan_matrix_irqs_##index[INST_INPUTS_LEN(index)];)) \
COND_INTERRUPTS( \
(static struct kscan_matrix_irq_callback kscan_matrix_irqs_##n[INST_INPUTS_LEN(n)];)) \
\
static struct kscan_matrix_data kscan_matrix_data_##index = { \
.matrix_state = kscan_matrix_state_##index, \
COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##index, ))}; \
static struct kscan_matrix_data kscan_matrix_data_##n = { \
.matrix_state = kscan_matrix_state_##n, \
COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##n, ))}; \
\
static struct kscan_matrix_config kscan_matrix_config_##index = { \
.rows = KSCAN_GPIO_LIST(kscan_matrix_rows_##index), \
.cols = KSCAN_GPIO_LIST(kscan_matrix_cols_##index), \
.inputs = KSCAN_GPIO_LIST( \
COND_DIODE_DIR(index, (kscan_matrix_cols_##index), (kscan_matrix_rows_##index))), \
.outputs = KSCAN_GPIO_LIST( \
COND_DIODE_DIR(index, (kscan_matrix_rows_##index), (kscan_matrix_cols_##index))), \
static struct kscan_matrix_config kscan_matrix_config_##n = { \
.rows = KSCAN_GPIO_LIST(kscan_matrix_rows_##n), \
.cols = KSCAN_GPIO_LIST(kscan_matrix_cols_##n), \
.inputs = \
KSCAN_GPIO_LIST(COND_DIODE_DIR(n, (kscan_matrix_cols_##n), (kscan_matrix_rows_##n))), \
.outputs = \
KSCAN_GPIO_LIST(COND_DIODE_DIR(n, (kscan_matrix_rows_##n), (kscan_matrix_cols_##n))), \
.debounce_config = \
{ \
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(index), \
.debounce_release_ms = INST_DEBOUNCE_RELEASE_MS(index), \
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \
.debounce_release_ms = INST_DEBOUNCE_RELEASE_MS(n), \
}, \
.debounce_scan_period_ms = DT_INST_PROP(index, debounce_scan_period_ms), \
.poll_period_ms = DT_INST_PROP(index, poll_period_ms), \
.diode_direction = INST_DIODE_DIR(index), \
.debounce_scan_period_ms = DT_INST_PROP(n, debounce_scan_period_ms), \
.poll_period_ms = DT_INST_PROP(n, poll_period_ms), \
.diode_direction = INST_DIODE_DIR(n), \
}; \
\
DEVICE_DT_INST_DEFINE(index, &kscan_matrix_init, NULL, &kscan_matrix_data_##index, \
&kscan_matrix_config_##index, APPLICATION, \
CONFIG_APPLICATION_INIT_PRIORITY, &kscan_matrix_api);
DEVICE_DT_INST_DEFINE(n, &kscan_matrix_init, NULL, &kscan_matrix_data_##n, \
&kscan_matrix_config_##n, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, \
&kscan_matrix_api);
DT_INST_FOREACH_STATUS_OKAY(KSCAN_MATRIX_INIT);
#endif // DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)

View file

@ -1,20 +1,25 @@
# Copyright (c) 2020-2021 The ZMK Contributors
# SPDX-License-Identifier: MIT
DT_COMPAT_ZMK_BATTERY_NRF_VDDH := zmk,battery-nrf-vddh
DT_COMPAT_ZMK_BATTERY_VOLTAGE_DIVIDER := zmk,battery-voltage-divider
config ZMK_BATTERY
bool "ZMK battery monitoring"
help
Enable battery monitoring
config ZMK_BATTERY_NRF_VDDH
bool "ZMK nRF VDDH battery monitoring"
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BATTERY_NRF_VDDH))
select ADC
select ZMK_BATTERY
help
Enable ZMK nRF VDDH voltage driver for battery monitoring.
config ZMK_BATTERY_VOLTAGE_DIVIDER
bool "ZMK battery voltage divider"
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BATTERY_VOLTAGE_DIVIDER))
select ADC
select ZMK_BATTERY
help

View file

@ -0,0 +1,33 @@
#
# Copyright (c) 2022 The ZMK Contributors
#
# SPDX-License-Identifier: MIT
#
description: >
This is a representation of the 595 Shift Register.
compatible: "zmk,gpio-595"
include: [gpio-controller.yaml, spi-device.yaml]
properties:
label:
required: true
"#gpio-cells":
const: 2
ngpios:
type: int
required: true
enum:
- 8
- 16
- 24
- 32
description: Number of gpios supported
gpio-cells:
- pin
- flags

View file

@ -12,5 +12,26 @@ properties:
type: phandle-array
required: true
debounce-period:
type: int
required: false
deprecated: true
description: Deprecated. Use debounce-press-ms and debounce-release-ms instead.
debounce-press-ms:
type: int
default: 5
description: Debounce time for key press in milliseconds. Use 0 for eager debouncing.
debounce-release-ms:
type: int
default: 5
description: Debounce time for key release in milliseconds.
debounce-scan-period-ms:
type: int
default: 1
description: Time between reads in milliseconds when any key is pressed.
poll-period-ms:
type: int
default: 10
description: Time between reads in milliseconds when no key is pressed and ZMK_KSCAN_DIRECT_POLLING is enabled.
toggle-mode:
type: boolean
description: Enable toggle-switch mode.

View file

@ -1,4 +1,5 @@
#include <behaviors/key_press.dtsi>
#include <behaviors/key_toggle.dtsi>
#include <behaviors/transparent.dtsi>
#include <behaviors/none.dtsi>
#include <behaviors/mod_tap.dtsi>

View file

@ -12,7 +12,7 @@
compatible = "zmk,behavior-caps-word";
label = "CAPS_WORD";
#binding-cells = <0>;
continue-list = <UNDERSCORE>;
continue-list = <UNDERSCORE BACKSPACE DELETE>;
};
};
};

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
/ {
behaviors {
/omit-if-no-ref/ kt: behavior_key_toggle {
compatible = "zmk,behavior-key-toggle";
label = "KEY_TOGGLE";
#binding-cells = <1>;
};
};
};

View file

@ -20,6 +20,8 @@ properties:
default: -1
quick_tap_ms: # deprecated
type: int
global-quick-tap:
type: boolean
flavor:
type: string
required: false

View file

@ -0,0 +1,8 @@
# Copyright (c) 2022 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Key toggle behavior
compatible: "zmk,behavior-key-toggle"
include: one_param.yaml

View file

@ -12,7 +12,7 @@
#define ZMK_HID_USAGE(page, id) ((page << 16) | id)
#define ZMK_HID_USAGE_ID(usage) (usage & 0xFFFF)
#define ZMK_HID_USAGE_PAGE(usage) (usage >> 16)
#define ZMK_HID_USAGE_PAGE(usage) ((usage >> 16) & 0xFF)
/* WARNING: DEPRECATED from dt-bindings/zmk/keys.h */
#define USAGE_KEYPAD (0x07) // WARNING: DEPRECATED (DO NOT USE)

View file

@ -930,7 +930,7 @@
/* Consumer Closed Caption */
#define C_CAPTIONS (ZMK_HID_USAGE(HID_USAGE_CONSUMER, HID_USAGE_CONSUMER_CLOSED_CAPTION))
#define C_SUBTITILES (C_CAPTIONS)
#define C_SUBTITLES (C_CAPTIONS)
/* Consumer Snapshot */
#define C_SNAPSHOT (ZMK_HID_USAGE(HID_USAGE_CONSUMER, HID_USAGE_CONSUMER_SNAPSHOT))

View file

@ -11,7 +11,7 @@
#define ZMK_BLE_IS_CENTRAL \
(IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_BLE) && \
IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL))
#if ZMK_BLE_IS_CENTRAL
#define ZMK_BLE_PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - 1)
@ -33,6 +33,6 @@ char *zmk_ble_active_profile_name();
int zmk_ble_unpair_all();
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
void zmk_ble_set_peripheral_addr(bt_addr_le_t *addr);
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */

View file

@ -0,0 +1,19 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <lvgl.h>
#include <kernel.h>
struct zmk_widget_peripheral_status {
sys_snode_t node;
lv_obj_t *obj;
};
int zmk_widget_peripheral_status_init(struct zmk_widget_peripheral_status *widget,
lv_obj_t *parent);
lv_obj_t *zmk_widget_peripheral_status_obj(struct zmk_widget_peripheral_status *widget);

View file

@ -23,7 +23,7 @@ ZMK_EVENT_DECLARE(zmk_keycode_state_changed);
static inline struct zmk_keycode_state_changed_event *
zmk_keycode_state_changed_from_encoded(uint32_t encoded, bool pressed, int64_t timestamp) {
uint16_t page = ZMK_HID_USAGE_PAGE(encoded) & 0xFF;
uint16_t page = ZMK_HID_USAGE_PAGE(encoded);
uint16_t id = ZMK_HID_USAGE_ID(encoded);
uint8_t implicit_modifiers = 0x00;
uint8_t explicit_modifiers = 0x00;

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zephyr.h>
#include <zmk/event_manager.h>
struct zmk_split_peripheral_status_changed {
bool connected;
};
ZMK_EVENT_DECLARE(zmk_split_peripheral_status_changed);

View file

@ -129,17 +129,26 @@ struct zmk_hid_consumer_report {
zmk_mod_flags_t zmk_hid_get_explicit_mods();
int zmk_hid_register_mod(zmk_mod_t modifier);
int zmk_hid_unregister_mod(zmk_mod_t modifier);
bool zmk_hid_mod_is_pressed(zmk_mod_t modifier);
int zmk_hid_register_mods(zmk_mod_flags_t explicit_modifiers);
int zmk_hid_unregister_mods(zmk_mod_flags_t explicit_modifiers);
int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers);
int zmk_hid_implicit_modifiers_release();
int zmk_hid_keyboard_press(zmk_key_t key);
int zmk_hid_keyboard_release(zmk_key_t key);
void zmk_hid_keyboard_clear();
bool zmk_hid_keyboard_is_pressed(zmk_key_t key);
int zmk_hid_consumer_press(zmk_key_t key);
int zmk_hid_consumer_release(zmk_key_t key);
void zmk_hid_consumer_clear();
bool zmk_hid_consumer_is_pressed(zmk_key_t key);
int zmk_hid_press(uint32_t usage);
int zmk_hid_release(uint32_t usage);
bool zmk_hid_is_pressed(uint32_t usage);
struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report();
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report();

View file

@ -0,0 +1,9 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
bool zmk_split_bt_peripheral_is_connected(void);

View file

@ -68,9 +68,9 @@ void activity_work_handler(struct k_work *work) {
int32_t inactive_time = current - activity_last_uptime;
#if IS_ENABLED(CONFIG_ZMK_SLEEP)
if (inactive_time > MAX_SLEEP_MS && !is_usb_power_present()) {
// Put devices in low power mode before sleeping
// Put devices in suspend power mode before sleeping
set_state(ZMK_ACTIVITY_SLEEP);
pm_power_state_set((struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
pm_power_state_force(0U, (struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
} else
#endif /* IS_ENABLED(CONFIG_ZMK_SLEEP) */
if (inactive_time > MAX_IDLE_MS) {

View file

@ -104,7 +104,7 @@ static int zmk_battery_init(const struct device *_arg) {
return rc;
}
k_timer_start(&battery_timer, K_MINUTES(1), K_MINUTES(1));
k_timer_start(&battery_timer, K_MINUTES(1), K_SECONDS(CONFIG_ZMK_BATTERY_REPORT_INTERVAL));
return 0;
}

View file

@ -16,6 +16,7 @@
#include <zmk/events/position_state_changed.h>
#include <zmk/events/keycode_state_changed.h>
#include <zmk/events/modifiers_state_changed.h>
#include <zmk/keys.h>
#include <zmk/hid.h>
#include <zmk/keymap.h>
@ -92,7 +93,9 @@ static bool caps_word_is_caps_includelist(const struct behavior_caps_word_config
continuation->id, continuation->implicit_modifiers);
if (continuation->page == usage_page && continuation->id == usage_id &&
continuation->implicit_modifiers == implicit_modifiers) {
(continuation->implicit_modifiers &
(implicit_modifiers | zmk_hid_get_explicit_mods())) ==
continuation->implicit_modifiers) {
LOG_DBG("Continuing capsword, found included usage: 0x%02X - 0x%02X", usage_page,
usage_id);
return true;
@ -143,6 +146,7 @@ static int caps_word_keycode_state_changed_listener(const zmk_event_t *eh) {
caps_word_enhance_usage(config, ev);
if (!caps_word_is_alpha(ev->keycode) && !caps_word_is_numeric(ev->keycode) &&
!is_mod(ev->usage_page, ev->keycode) &&
!caps_word_is_caps_includelist(config, ev->usage_page, ev->keycode,
ev->implicit_modifiers)) {
LOG_DBG("Deactivating caps_word for 0x%02X - 0x%02X", ev->usage_page, ev->keycode);
@ -162,7 +166,7 @@ static int behavior_caps_word_init(const struct device *dev) {
#define CAPS_WORD_LABEL(i, _n) DT_INST_LABEL(i)
#define PARSE_BREAK(i) \
{.page = (ZMK_HID_USAGE_PAGE(i) & 0xFF), \
{.page = ZMK_HID_USAGE_PAGE(i), \
.id = ZMK_HID_USAGE_ID(i), \
.implicit_modifiers = SELECT_MODS(i)},

View file

@ -57,6 +57,7 @@ struct behavior_hold_tap_config {
char *hold_behavior_dev;
char *tap_behavior_dev;
int quick_tap_ms;
bool global_quick_tap;
enum flavor flavor;
bool retro_tap;
int32_t hold_trigger_key_positions_len;
@ -88,22 +89,33 @@ struct active_hold_tap active_hold_taps[ZMK_BHV_HOLD_TAP_MAX_HELD] = {};
// We capture most position_state_changed events and some modifiers_state_changed events.
const zmk_event_t *captured_events[ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS] = {};
// Keep track of which key was tapped most recently for 'quick_tap_ms'
// Keep track of which key was tapped most recently for the standard, if it is a hold-tap
// a position, will be given, if not it will just be INT32_MIN
struct last_tapped {
int32_t position;
int64_t tap_deadline;
int64_t timestamp;
};
struct last_tapped last_tapped;
struct last_tapped last_tapped = {INT32_MIN, INT64_MIN};
static void store_last_tapped(struct active_hold_tap *hold_tap) {
static void store_last_tapped(int64_t timestamp) {
if (timestamp > last_tapped.timestamp) {
last_tapped.position = INT32_MIN;
last_tapped.timestamp = timestamp;
}
}
static void store_last_hold_tapped(struct active_hold_tap *hold_tap) {
last_tapped.position = hold_tap->position;
last_tapped.tap_deadline = hold_tap->timestamp + hold_tap->config->quick_tap_ms;
last_tapped.timestamp = hold_tap->timestamp;
}
static bool is_quick_tap(struct active_hold_tap *hold_tap) {
return last_tapped.position == hold_tap->position &&
last_tapped.tap_deadline > hold_tap->timestamp;
if (hold_tap->config->global_quick_tap || last_tapped.position == hold_tap->position) {
return (last_tapped.timestamp + hold_tap->config->quick_tap_ms) > hold_tap->timestamp;
} else {
return false;
}
}
static int capture_event(const zmk_event_t *event) {
@ -362,7 +374,7 @@ static int press_binding(struct active_hold_tap *hold_tap) {
} else {
binding.behavior_dev = hold_tap->config->tap_behavior_dev;
binding.param1 = hold_tap->param_tap;
store_last_tapped(hold_tap);
store_last_hold_tapped(hold_tap);
}
return behavior_keymap_binding_pressed(&binding, event);
}
@ -619,6 +631,10 @@ static int keycode_state_changed_listener(const zmk_event_t *eh) {
// we want to catch layer-up events too... how?
struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh);
if (ev->state && !is_mod(ev->usage_page, ev->keycode)) {
store_last_tapped(ev->timestamp);
}
if (undecided_hold_tap == NULL) {
// LOG_DBG("0x%02X bubble (no undecided hold_tap active)", ev->keycode);
return ZMK_EV_EVENT_BUBBLE;
@ -680,6 +696,7 @@ static int behavior_hold_tap_init(const struct device *dev) {
.hold_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 0)), \
.tap_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 1)), \
.quick_tap_ms = DT_INST_PROP(n, quick_tap_ms), \
.global_quick_tap = DT_INST_PROP(n, global_quick_tap), \
.flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \
.retro_tap = DT_INST_PROP(n, retro_tap), \
.hold_trigger_key_positions = DT_INST_PROP(n, hold_trigger_key_positions), \

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_key_toggle
#include <device.h>
#include <drivers/behavior.h>
#include <logging/log.h>
#include <zmk/hid.h>
#include <zmk/event_manager.h>
#include <zmk/events/keycode_state_changed.h>
#include <zmk/behavior.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
static int behavior_key_toggle_init(const struct device *dev) { return 0; }
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
bool pressed = zmk_hid_is_pressed(binding->param1);
return ZMK_EVENT_RAISE(
zmk_keycode_state_changed_from_encoded(binding->param1, !pressed, event.timestamp));
}
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
return 0;
}
static const struct behavior_driver_api behavior_key_toggle_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
};
#define KT_INST(n) \
DEVICE_DT_INST_DEFINE(n, behavior_key_toggle_init, NULL, NULL, NULL, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_toggle_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KT_INST)

View file

@ -199,7 +199,7 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) {
if (strcmp(sticky_key->config->behavior.behavior_dev, "KEY_PRESS") == 0 &&
ZMK_HID_USAGE_ID(sticky_key->param1) == ev->keycode &&
(ZMK_HID_USAGE_PAGE(sticky_key->param1) & 0xFF) == ev->usage_page &&
ZMK_HID_USAGE_PAGE(sticky_key->param1) == ev->usage_page &&
SELECT_MODS(sticky_key->param1) == ev->implicit_modifiers) {
// don't catch key down events generated by the sticky key behavior itself
continue;

View file

@ -36,14 +36,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/event_manager.h>
#include <zmk/events/ble_active_profile_changed.h>
#define IS_HOST_PERIPHERAL \
(!IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
#define IS_SPLIT_PERIPHERAL \
(IS_ENABLED(CONFIG_ZMK_SPLIT) && !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
#define DO_PASSKEY_ENTRY (IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY) && !IS_SPLIT_PERIPHERAL)
#if DO_PASSKEY_ENTRY
#if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY)
#include <zmk/events/keycode_state_changed.h>
#define PASSKEY_DIGITS 6
@ -52,9 +45,9 @@ static struct bt_conn *auth_passkey_entry_conn;
static uint8_t passkey_entries[PASSKEY_DIGITS] = {};
static uint8_t passkey_digit = 0;
#endif
#endif /* IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY) */
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
#define PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - 1)
#else
#define PROFILE_COUNT CONFIG_BT_MAX_PAIRED
@ -81,27 +74,19 @@ static uint8_t active_profile;
BUILD_ASSERT(DEVICE_NAME_LEN <= 16, "ERROR: BLE device name is too long. Max length: 16");
static const struct bt_data zmk_ble_ad[] = {
#if IS_HOST_PERIPHERAL
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE, 0xC1, 0x03),
#endif
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_SOME,
#if IS_HOST_PERIPHERAL
0x12, 0x18, /* HID Service */
#endif
0x0f, 0x18 /* Battery Service */
BT_DATA_BYTES(BT_DATA_UUID16_SOME, 0x12, 0x18, /* HID Service */
0x0f, 0x18 /* Battery Service */
),
#if IS_SPLIT_PERIPHERAL
BT_DATA_BYTES(BT_DATA_UUID128_ALL, ZMK_SPLIT_BT_SERVICE_UUID)
#endif
};
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
static bt_addr_le_t peripheral_addr;
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */
static void raise_profile_changed_event() {
ZMK_EVENT_RAISE(new_zmk_ble_active_profile_changed((struct zmk_ble_active_profile_changed){
@ -293,14 +278,14 @@ bt_addr_le_t *zmk_ble_active_profile_addr() { return &profiles[active_profile].p
char *zmk_ble_active_profile_name() { return profiles[active_profile].name; }
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
void zmk_ble_set_peripheral_addr(bt_addr_le_t *addr) {
memcpy(&peripheral_addr, addr, sizeof(bt_addr_le_t));
settings_save_one("ble/peripheral_address", addr, sizeof(bt_addr_le_t));
}
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */
#if IS_ENABLED(CONFIG_SETTINGS)
@ -351,7 +336,7 @@ static int ble_profiles_handle_set(const char *name, size_t len, settings_read_c
return err;
}
}
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
else if (settings_name_steq(name, "peripheral_address", &next) && !next) {
if (len != sizeof(bt_addr_le_t)) {
return -EINVAL;
@ -398,15 +383,6 @@ static void connected(struct bt_conn *conn, uint8_t err) {
LOG_DBG("Connected %s", log_strdup(addr));
err = bt_conn_le_param_update(conn, BT_LE_CONN_PARAM(0x0006, 0x000c, 30, 400));
if (err) {
LOG_WRN("Failed to update LE parameters (err %d)", err);
}
#if IS_SPLIT_PERIPHERAL
bt_conn_le_phy_update(conn, BT_CONN_LE_PHY_PARAM_2M);
#endif
if (bt_conn_set_security(conn, BT_SECURITY_L2)) {
LOG_ERR("Failed to set security");
}
@ -482,7 +458,7 @@ static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey) {
}
*/
#if DO_PASSKEY_ENTRY
#if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY)
static void auth_passkey_entry(struct bt_conn *conn) {
char addr[BT_ADDR_LE_STR_LEN];
@ -501,7 +477,7 @@ static void auth_cancel(struct bt_conn *conn) {
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
#if DO_PASSKEY_ENTRY
#if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY)
if (auth_passkey_entry_conn) {
bt_conn_unref(auth_passkey_entry_conn);
auth_passkey_entry_conn = NULL;
@ -513,7 +489,6 @@ static void auth_cancel(struct bt_conn *conn) {
LOG_DBG("Pairing cancelled: %s", log_strdup(addr));
}
#if IS_HOST_PERIPHERAL
static enum bt_security_err auth_pairing_accept(struct bt_conn *conn,
const struct bt_conn_pairing_feat *const feat) {
struct bt_conn_info info;
@ -527,7 +502,6 @@ static enum bt_security_err auth_pairing_accept(struct bt_conn *conn,
return BT_SECURITY_ERR_SUCCESS;
};
#endif /* IS_HOST_PERIPHERAL */
static void auth_pairing_complete(struct bt_conn *conn, bool bonded) {
struct bt_conn_info info;
@ -542,26 +516,22 @@ static void auth_pairing_complete(struct bt_conn *conn, bool bonded) {
return;
}
#if IS_HOST_PERIPHERAL
if (!zmk_ble_active_profile_is_open()) {
LOG_ERR("Pairing completed but current profile is not open: %s", log_strdup(addr));
bt_unpair(BT_ID_DEFAULT, dst);
return;
}
#endif /* IS_HOST_PERIPHERAL */
set_profile_address(active_profile, dst);
update_advertising();
};
static struct bt_conn_auth_cb zmk_ble_auth_cb_display = {
#if IS_HOST_PERIPHERAL
.pairing_accept = auth_pairing_accept,
#endif /* IS_HOST_PERIPHERAL */
.pairing_complete = auth_pairing_complete,
// .passkey_display = auth_passkey_display,
#if DO_PASSKEY_ENTRY
#if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY)
.passkey_entry = auth_passkey_entry,
#endif
.cancel = auth_cancel,
@ -604,9 +574,7 @@ static int zmk_ble_init(const struct device *_arg) {
#if IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START)
LOG_WRN("Clearing all existing BLE bond information from the keyboard");
for (int i = 0; i < 10; i++) {
bt_unpair(i, NULL);
}
bt_unpair(BT_ID_DEFAULT, NULL);
for (int i = 0; i < ZMK_BLE_PROFILE_COUNT; i++) {
char setting_name[15];
@ -627,21 +595,7 @@ static int zmk_ble_init(const struct device *_arg) {
return 0;
}
int zmk_ble_unpair_all() {
int resp = 0;
for (int i = BT_ID_DEFAULT; i < CONFIG_BT_ID_MAX; i++) {
int err = bt_unpair(BT_ID_DEFAULT, NULL);
if (err) {
resp = err;
LOG_ERR("Failed to unpair devices (err %d)", err);
}
}
return resp;
};
#if DO_PASSKEY_ENTRY
#if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY)
static bool zmk_ble_numeric_usage_to_value(const zmk_key_t key, const zmk_key_t one,
const zmk_key_t zero, uint32_t *value) {
@ -714,6 +668,6 @@ static int zmk_ble_listener(const zmk_event_t *eh) {
ZMK_LISTENER(zmk_ble, zmk_ble_listener);
ZMK_SUBSCRIPTION(zmk_ble, zmk_keycode_state_changed);
#endif /* DO_PASSKEY_ENTRY */
#endif /* IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY) */
SYS_INIT(zmk_ble_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY);

View file

@ -7,6 +7,7 @@
#define DT_DRV_COMPAT zmk_conditional_layers
#include <stdint.h>
#include <kernel.h>
#include <devicetree.h>
#include <logging/log.h>
@ -19,6 +20,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static K_SEM_DEFINE(conditional_layer_sem, 1, 1);
// Conditional layer configuration that activates the specified then-layer when all if-layers are
// active. With two if-layers, this is referred to as "tri-layer", and is commonly used to activate
// a third "adjust" layer if and only if the "lower" and "raise" layers are both active.
@ -66,22 +69,53 @@ static void conditional_layer_deactivate(int8_t layer) {
}
}
// On layer state changes, examines each conditional layer config to determine if then-layer in the
// config should activate based on the currently active set of if-layers.
static int layer_state_changed_listener(const zmk_event_t *ev) {
for (int i = 0; i < NUM_CONDITIONAL_LAYER_CFGS; i++) {
const struct conditional_layer_cfg *cfg = CONDITIONAL_LAYER_CFGS + i;
zmk_keymap_layers_state_t mask = cfg->if_layers_state_mask;
static bool conditional_layer_updates_needed;
// Activate then-layer if and only if all if-layers are already active. Note that we
// reevaluate the current layer state for each config since activation of one layer can also
// trigger activation of another.
if ((zmk_keymap_layer_state() & mask) == mask) {
conditional_layer_activate(cfg->then_layer);
} else {
conditional_layer_deactivate(cfg->then_layer);
conditional_layer_updates_needed = true;
// Semaphore ensures we don't re-enter the loop in the middle of doing update, and
// ensures that "waterfalling layer updates" are all processed to trigger subsequent
// nested conditional layers properly.
if (k_sem_take(&conditional_layer_sem, K_NO_WAIT) < 0) {
return 0;
}
while (conditional_layer_updates_needed) {
int8_t max_then_layer = -1;
uint32_t then_layers = 0;
uint32_t then_layer_state = 0;
conditional_layer_updates_needed = false;
// On layer state changes, examines each conditional layer config to determine if then-layer
// in the config should activate based on the currently active set of if-layers.
for (int i = 0; i < NUM_CONDITIONAL_LAYER_CFGS; i++) {
const struct conditional_layer_cfg *cfg = CONDITIONAL_LAYER_CFGS + i;
zmk_keymap_layers_state_t mask = cfg->if_layers_state_mask;
then_layers |= BIT(cfg->then_layer);
max_then_layer = MAX(max_then_layer, cfg->then_layer);
// Activate then-layer if and only if all if-layers are already active. Note that we
// reevaluate the current layer state for each config since activation of one layer can
// also trigger activation of another.
if ((zmk_keymap_layer_state() & mask) == mask) {
then_layer_state |= BIT(cfg->then_layer);
}
}
for (uint8_t layer = 0; layer <= max_then_layer; layer++) {
if ((BIT(layer) & then_layers) != 0U) {
if ((BIT(layer) & then_layer_state) != 0U) {
conditional_layer_activate(layer);
} else {
conditional_layer_deactivate(layer);
}
}
}
}
k_sem_give(&conditional_layer_sem);
return 0;
}

View file

@ -5,6 +5,7 @@
*/
#include <zmk/display/widgets/output_status.h>
#include <zmk/display/widgets/peripheral_status.h>
#include <zmk/display/widgets/battery_status.h>
#include <zmk/display/widgets/layer_status.h>
#include <zmk/display/widgets/wpm_status.h>
@ -21,6 +22,10 @@ static struct zmk_widget_battery_status battery_status_widget;
static struct zmk_widget_output_status output_status_widget;
#endif
#if IS_ENABLED(CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS)
static struct zmk_widget_peripheral_status peripheral_status_widget;
#endif
#if IS_ENABLED(CONFIG_ZMK_WIDGET_LAYER_STATUS)
static struct zmk_widget_layer_status layer_status_widget;
#endif
@ -46,6 +51,12 @@ lv_obj_t *zmk_display_status_screen() {
0);
#endif
#if IS_ENABLED(CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS)
zmk_widget_peripheral_status_init(&peripheral_status_widget, screen);
lv_obj_align(zmk_widget_peripheral_status_obj(&peripheral_status_widget), NULL,
LV_ALIGN_IN_TOP_LEFT, 0, 0);
#endif
#if IS_ENABLED(CONFIG_ZMK_WIDGET_LAYER_STATUS)
zmk_widget_layer_status_init(&layer_status_widget, screen);
lv_obj_set_style_local_text_font(zmk_widget_layer_status_obj(&layer_status_widget),

View file

@ -3,5 +3,6 @@
target_sources_ifdef(CONFIG_ZMK_WIDGET_BATTERY_STATUS app PRIVATE battery_status.c)
target_sources_ifdef(CONFIG_ZMK_WIDGET_OUTPUT_STATUS app PRIVATE output_status.c)
target_sources_ifdef(CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS app PRIVATE peripheral_status.c)
target_sources_ifdef(CONFIG_ZMK_WIDGET_LAYER_STATUS app PRIVATE layer_status.c)
target_sources_ifdef(CONFIG_ZMK_WIDGET_WPM_STATUS app PRIVATE wpm_status.c)

View file

@ -6,7 +6,7 @@ menu "ZMK Display Widgets"
config ZMK_WIDGET_LAYER_STATUS
bool "Widget for highest, active layer using small icons"
default y
depends on !ZMK_SPLIT || ZMK_SPLIT_BLE_ROLE_CENTRAL
depends on !ZMK_SPLIT || ZMK_SPLIT_ROLE_CENTRAL
select LVGL_USE_LABEL
config ZMK_WIDGET_BATTERY_STATUS
@ -17,13 +17,19 @@ config ZMK_WIDGET_BATTERY_STATUS
config ZMK_WIDGET_OUTPUT_STATUS
bool "Widget for keyboard output status icons"
depends on BT
default y if BT
depends on BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_ROLE_CENTRAL)
default y if BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_ROLE_CENTRAL)
select LVGL_USE_LABEL
config ZMK_WIDGET_PERIPHERAL_STATUS
bool "Widget for split peripheral status icons"
depends on BT && ZMK_SPLIT_BLE && !ZMK_SPLIT_ROLE_CENTRAL
default y if BT && ZMK_SPLIT_BLE && !ZMK_SPLIT_ROLE_CENTRAL
select LVGL_USE_LABEL
config ZMK_WIDGET_WPM_STATUS
bool "Widget for displaying typed words per minute"
depends on !ZMK_SPLIT || ZMK_SPLIT_BLE_ROLE_CENTRAL
depends on !ZMK_SPLIT || ZMK_SPLIT_ROLE_CENTRAL
select LVGL_USE_LABEL
select ZMK_WPM

View file

@ -76,7 +76,7 @@ ZMK_SUBSCRIPTION(widget_battery_status, zmk_usb_conn_state_changed);
int zmk_widget_battery_status_init(struct zmk_widget_battery_status *widget, lv_obj_t *parent) {
widget->obj = lv_label_create(parent, NULL);
lv_obj_set_size(widget->obj, 40, 15);
lv_obj_set_size(widget->obj, 43, 15);
sys_slist_append(&widgets, &widget->node);

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <kernel.h>
#include <bluetooth/services/bas.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/display.h>
#include <zmk/display/widgets/peripheral_status.h>
#include <zmk/event_manager.h>
#include <zmk/split/bluetooth/peripheral.h>
#include <zmk/events/split_peripheral_status_changed.h>
static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets);
struct peripheral_status_state {
bool connected;
};
static struct peripheral_status_state get_state(const zmk_event_t *_eh) {
return (struct peripheral_status_state){.connected = zmk_split_bt_peripheral_is_connected()};
}
static void set_status_symbol(lv_obj_t *label, struct peripheral_status_state state) {
const char *text =
state.connected ? (LV_SYMBOL_WIFI " " LV_SYMBOL_OK) : (LV_SYMBOL_WIFI " " LV_SYMBOL_CLOSE);
LOG_DBG("connected? %s", state.connected ? "true" : "false");
lv_label_set_text(label, text);
}
static void output_status_update_cb(struct peripheral_status_state state) {
struct zmk_widget_peripheral_status *widget;
SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_status_symbol(widget->obj, state); }
}
ZMK_DISPLAY_WIDGET_LISTENER(widget_peripheral_status, struct peripheral_status_state,
output_status_update_cb, get_state)
ZMK_SUBSCRIPTION(widget_peripheral_status, zmk_split_peripheral_status_changed);
int zmk_widget_peripheral_status_init(struct zmk_widget_peripheral_status *widget,
lv_obj_t *parent) {
widget->obj = lv_label_create(parent, NULL);
lv_obj_set_size(widget->obj, 40, 15);
sys_slist_append(&widgets, &widget->node);
widget_peripheral_status_init();
return 0;
}
lv_obj_t *zmk_widget_peripheral_status_obj(struct zmk_widget_peripheral_status *widget) {
return widget->obj;
}

View file

@ -0,0 +1,10 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <kernel.h>
#include <zmk/events/split_peripheral_status_changed.h>
ZMK_EVENT_IMPL(zmk_split_peripheral_status_changed);

View file

@ -176,10 +176,10 @@ static int ext_power_generic_init(const struct device *dev) {
#ifdef CONFIG_PM_DEVICE
static int ext_power_generic_pm_action(const struct device *dev, enum pm_device_action action) {
switch (action) {
case PM_DEVICE_ACTION_TURN_ON:
case PM_DEVICE_ACTION_RESUME:
ext_power_generic_enable(dev);
return 0;
case PM_DEVICE_ACTION_TURN_OFF:
case PM_DEVICE_ACTION_SUSPEND:
ext_power_generic_disable(dev);
return 0;
default:

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: MIT
*/
#include "zmk/keys.h"
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@ -55,6 +56,11 @@ int zmk_hid_unregister_mod(zmk_mod_t modifier) {
return current == GET_MODIFIERS ? 0 : 1;
}
bool zmk_hid_mod_is_pressed(zmk_mod_t modifier) {
zmk_mod_flags_t mod_flag = 1 << modifier;
return (zmk_hid_get_explicit_mods() & mod_flag) == mod_flag;
}
int zmk_hid_register_mods(zmk_mod_flags_t modifiers) {
int ret = 0;
for (zmk_mod_t i = 0; i < 8; i++) {
@ -96,6 +102,13 @@ static inline int deselect_keyboard_usage(zmk_key_t usage) {
return 0;
}
static inline bool check_keyboard_usage(zmk_key_t usage) {
if (usage > ZMK_HID_KEYBOARD_NKRO_MAX_USAGE) {
return false;
}
return keyboard_report.body.keys[usage / 8] & (1 << (usage % 8));
}
#elif IS_ENABLED(CONFIG_ZMK_HID_REPORT_TYPE_HKRO)
#define TOGGLE_KEYBOARD(match, val) \
@ -119,6 +132,15 @@ static inline int deselect_keyboard_usage(zmk_key_t usage) {
return 0;
}
static inline int check_keyboard_usage(zmk_key_t usage) {
for (int idx = 0; idx < CONFIG_ZMK_HID_KEYBOARD_REPORT_SIZE; idx++) {
if (keyboard_report.body.keys[idx] == usage) {
return true;
}
}
return false;
}
#else
#error "A proper HID report type must be selected"
#endif
@ -164,6 +186,13 @@ int zmk_hid_keyboard_release(zmk_key_t code) {
return 0;
};
bool zmk_hid_keyboard_is_pressed(zmk_key_t code) {
if (code >= HID_USAGE_KEY_KEYBOARD_LEFTCONTROL && code <= HID_USAGE_KEY_KEYBOARD_RIGHT_GUI) {
return zmk_hid_mod_is_pressed(code - HID_USAGE_KEY_KEYBOARD_LEFTCONTROL);
}
return check_keyboard_usage(code);
}
void zmk_hid_keyboard_clear() { memset(&keyboard_report.body, 0, sizeof(keyboard_report.body)); }
int zmk_hid_consumer_press(zmk_key_t code) {
@ -178,6 +207,45 @@ int zmk_hid_consumer_release(zmk_key_t code) {
void zmk_hid_consumer_clear() { memset(&consumer_report.body, 0, sizeof(consumer_report.body)); }
bool zmk_hid_consumer_is_pressed(zmk_key_t key) {
for (int idx = 0; idx < CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE; idx++) {
if (consumer_report.body.keys[idx] == key) {
return true;
}
}
return false;
}
int zmk_hid_press(uint32_t usage) {
switch (ZMK_HID_USAGE_PAGE(usage)) {
case HID_USAGE_KEY:
return zmk_hid_keyboard_press(ZMK_HID_USAGE_ID(usage));
case HID_USAGE_CONSUMER:
return zmk_hid_consumer_press(ZMK_HID_USAGE_ID(usage));
}
return -EINVAL;
}
int zmk_hid_release(uint32_t usage) {
switch (ZMK_HID_USAGE_PAGE(usage)) {
case HID_USAGE_KEY:
return zmk_hid_keyboard_release(ZMK_HID_USAGE_ID(usage));
case HID_USAGE_CONSUMER:
return zmk_hid_consumer_release(ZMK_HID_USAGE_ID(usage));
}
return -EINVAL;
}
bool zmk_hid_is_pressed(uint32_t usage) {
switch (ZMK_HID_USAGE_PAGE(usage)) {
case HID_USAGE_KEY:
return zmk_hid_keyboard_is_pressed(ZMK_HID_USAGE_ID(usage));
case HID_USAGE_CONSUMER:
return zmk_hid_consumer_is_pressed(ZMK_HID_USAGE_ID(usage));
}
return false;
}
struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() {
return &keyboard_report;
}

View file

@ -21,21 +21,10 @@ static int hid_listener_keycode_pressed(const struct zmk_keycode_state_changed *
LOG_DBG("usage_page 0x%02X keycode 0x%02X implicit_mods 0x%02X explicit_mods 0x%02X",
ev->usage_page, ev->keycode, ev->implicit_modifiers, ev->explicit_modifiers);
switch (ev->usage_page) {
case HID_USAGE_KEY:
err = zmk_hid_keyboard_press(ev->keycode);
if (err < 0) {
LOG_ERR("Unable to press keycode");
return err;
}
break;
case HID_USAGE_CONSUMER:
err = zmk_hid_consumer_press(ev->keycode);
if (err < 0) {
LOG_ERR("Unable to press keycode");
return err;
}
break;
err = zmk_hid_press(ZMK_HID_USAGE(ev->usage_page, ev->keycode));
if (err < 0) {
LOG_DBG("Unable to press keycode");
return err;
}
explicit_mods_changed = zmk_hid_register_mods(ev->explicit_modifiers);
implicit_mods_changed = zmk_hid_implicit_modifiers_press(ev->implicit_modifiers);
@ -56,20 +45,10 @@ static int hid_listener_keycode_released(const struct zmk_keycode_state_changed
LOG_DBG("usage_page 0x%02X keycode 0x%02X implicit_mods 0x%02X explicit_mods 0x%02X",
ev->usage_page, ev->keycode, ev->implicit_modifiers, ev->explicit_modifiers);
switch (ev->usage_page) {
case HID_USAGE_KEY:
err = zmk_hid_keyboard_release(ev->keycode);
if (err < 0) {
LOG_ERR("Unable to release keycode");
return err;
}
break;
case HID_USAGE_CONSUMER:
err = zmk_hid_consumer_release(ev->keycode);
if (err < 0) {
LOG_ERR("Unable to release keycode");
return err;
}
err = zmk_hid_release(ZMK_HID_USAGE(ev->usage_page, ev->keycode));
if (err < 0) {
LOG_DBG("Unable to release keycode");
return err;
}
explicit_mods_changed = zmk_hid_unregister_mods(ev->explicit_modifiers);
@ -104,4 +83,4 @@ int hid_listener(const zmk_event_t *eh) {
}
ZMK_LISTENER(hid_listener, hid_listener);
ZMK_SUBSCRIPTION(hid_listener, zmk_keycode_state_changed);
ZMK_SUBSCRIPTION(hid_listener, zmk_keycode_state_changed);

View file

@ -0,0 +1,6 @@
# Copyright (c) 2022 The ZMK Contributors
# SPDX-License-Identifier: MIT
if (CONFIG_ZMK_SPLIT_BLE)
add_subdirectory(bluetooth)
endif()

26
app/src/split/Kconfig Normal file
View file

@ -0,0 +1,26 @@
# Copyright (c) 2022 The ZMK Contributors
# SPDX-License-Identifier: MIT
menuconfig ZMK_SPLIT
bool "Split keyboard support"
if ZMK_SPLIT
config ZMK_SPLIT_ROLE_CENTRAL
bool "Split central device"
choice ZMK_SPLIT_TRANSPORT
prompt "Split transport"
config ZMK_SPLIT_BLE
bool "BLE"
depends on ZMK_BLE
select BT_USER_PHY_UPDATE
select BT_AUTO_PHY_UPDATE
endchoice
#ZMK_SPLIT
endif
rsource "bluetooth/Kconfig"

View file

@ -0,0 +1,11 @@
# Copyright (c) 2022 The ZMK Contributors
# SPDX-License-Identifier: MIT
if (NOT CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE split_listener.c)
target_sources(app PRIVATE service.c)
target_sources(app PRIVATE peripheral.c)
endif()
if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE central.c)
endif()

View file

@ -0,0 +1,95 @@
# Copyright (c) 2022 The ZMK Contributors
# SPDX-License-Identifier: MIT
if ZMK_SPLIT && ZMK_SPLIT_BLE
menu "BLE Transport"
# Added for backwards compatibility. New shields/board should set `ZMK_SPLIT_ROLE_CENTRAL` only.
config ZMK_SPLIT_BLE_ROLE_CENTRAL
bool
select ZMK_SPLIT_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
select BT_CENTRAL
select BT_GATT_CLIENT
select BT_GATT_AUTO_DISCOVER_CCC
if ZMK_SPLIT_ROLE_CENTRAL
config ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE
int "Max number of key position state events to queue when received from peripherals"
default 5
config ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_STACK_SIZE
int "BLE split central write thread stack size"
default 512
config ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_QUEUE_SIZE
int "Max number of behavior run events to queue to send to the peripheral(s)"
default 5
endif # ZMK_SPLIT_ROLE_CENTRAL
if !ZMK_SPLIT_ROLE_CENTRAL
config ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE
int "BLE split peripheral notify thread stack size"
default 650
config ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY
int "BLE split peripheral notify thread priority"
default 5
config ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE
int "Max number of key position state events to queue to send to the central"
default 10
config ZMK_USB
default n
config BT_MAX_PAIRED
default 1
config BT_MAX_CONN
default 1
config BT_PERIPHERAL_PREF_MAX_INT
default 6
#!ZMK_SPLIT_ROLE_CENTRAL
endif
endmenu
#ZMK_SPLIT_BLE
endif
if ZMK_BLE
if ZMK_SPLIT_BLE && ZMK_SPLIT_ROLE_CENTRAL
config BT_MAX_CONN
default 6
config BT_MAX_PAIRED
default 6
#ZMK_SPLIT_BLE && ZMK_SPLIT_ROLE_CENTRAL
endif
if !ZMK_SPLIT_BLE
config BT_MAX_CONN
default 5
config BT_MAX_PAIRED
default 5
#!ZMK_SPLIT_BLE
endif
#ZMK_BLE
endif

View file

@ -54,6 +54,16 @@ static const struct bt_uuid_128 split_service_uuid = BT_UUID_INIT_128(ZMK_SPLIT_
K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed),
CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4);
void peripheral_event_work_callback(struct k_work *work) {
struct zmk_position_state_changed ev;
while (k_msgq_get(&peripheral_event_msgq, &ev, K_NO_WAIT) == 0) {
LOG_DBG("Trigger key position state change for %d", ev.position);
ZMK_EVENT_RAISE(new_zmk_position_state_changed(ev));
}
}
K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback);
int peripheral_slot_index_for_conn(struct bt_conn *conn) {
for (int i = 0; i < ZMK_BLE_SPLIT_PERIPHERAL_COUNT; i++) {
if (peripherals[i].conn == conn) {
@ -92,6 +102,22 @@ int release_peripheral_slot(int index) {
}
slot->state = PERIPHERAL_SLOT_STATE_OPEN;
// Raise events releasing any active positions from this peripheral
for (int i = 0; i < POSITION_STATE_DATA_LEN; i++) {
for (int j = 0; j < 8; j++) {
if (slot->position_state[i] & BIT(j)) {
uint32_t position = (i * 8) + j;
struct zmk_position_state_changed ev = {.source = index,
.position = position,
.state = false,
.timestamp = k_uptime_get()};
k_msgq_put(&peripheral_event_msgq, &ev, K_NO_WAIT);
k_work_submit(&peripheral_event_work);
}
}
}
for (int i = 0; i < POSITION_STATE_DATA_LEN; i++) {
slot->position_state[i] = 0U;
slot->changed_positions[i] = 0U;
@ -136,16 +162,6 @@ int confirm_peripheral_slot_conn(struct bt_conn *conn) {
return 0;
}
void peripheral_event_work_callback(struct k_work *work) {
struct zmk_position_state_changed ev;
while (k_msgq_get(&peripheral_event_msgq, &ev, K_NO_WAIT) == 0) {
LOG_DBG("Trigger key position state change for %d", ev.position);
ZMK_EVENT_RAISE(new_zmk_position_state_changed(ev));
}
}
K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback);
static uint8_t split_central_notify_func(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params, const void *data,
uint16_t length) {
@ -403,12 +419,6 @@ static bool split_central_eir_found(struct bt_data *data, void *user_data) {
BT_HCI_OP_LE_CREATE_CONN);
start_scan();
}
err = bt_conn_le_phy_update(slot->conn, BT_CONN_LE_PHY_PARAM_2M);
if (err) {
LOG_ERR("Update phy conn failed (err %d)", err);
start_scan();
}
}
return false;

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <device.h>
#include <init.h>
#include <errno.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.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 <bluetooth/hci_err.h>
#if IS_ENABLED(CONFIG_SETTINGS)
#include <settings/settings.h>
#endif
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/event_manager.h>
#include <zmk/events/split_peripheral_status_changed.h>
#include <zmk/ble.h>
#include <zmk/split/bluetooth/uuid.h>
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_SOME, 0x0f, 0x18 /* Battery Service */
),
BT_DATA_BYTES(BT_DATA_UUID128_ALL, ZMK_SPLIT_BT_SERVICE_UUID)};
static bool is_connected = false;
static int start_advertising() {
return bt_le_adv_start(BT_LE_ADV_CONN, zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad), NULL, 0);
};
static void connected(struct bt_conn *conn, uint8_t err) {
is_connected = (err == 0);
ZMK_EVENT_RAISE(new_zmk_split_peripheral_status_changed(
(struct zmk_split_peripheral_status_changed){.connected = is_connected}));
}
static void disconnected(struct bt_conn *conn, uint8_t reason) {
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_DBG("Disconnected from %s (reason 0x%02x)", log_strdup(addr), reason);
is_connected = false;
ZMK_EVENT_RAISE(new_zmk_split_peripheral_status_changed(
(struct zmk_split_peripheral_status_changed){.connected = is_connected}));
}
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) {
LOG_DBG("Security changed: %s level %u", log_strdup(addr), level);
} else {
LOG_ERR("Security failed: %s level %u err %d", log_strdup(addr), level, err);
}
}
static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency,
uint16_t timeout) {
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_DBG("%s: interval %d latency %d timeout %d", log_strdup(addr), interval, latency, timeout);
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
.security_changed = security_changed,
.le_param_updated = le_param_updated,
};
bool zmk_split_bt_peripheral_is_connected() { return is_connected; }
static int zmk_peripheral_ble_init(const struct device *_arg) {
int err = bt_enable(NULL);
if (err) {
LOG_ERR("BLUETOOTH FAILED (%d)", err);
return err;
}
#if IS_ENABLED(CONFIG_SETTINGS)
settings_subsys_init();
settings_load_subtree("ble");
settings_load_subtree("bt");
#endif
#if IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START)
LOG_WRN("Clearing all existing BLE bond information from the keyboard");
bt_unpair(BT_ID_DEFAULT, NULL);
#endif
bt_conn_cb_register(&conn_callbacks);
start_advertising();
return 0;
}
SYS_INIT(zmk_peripheral_ble_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY);

View file

@ -1,6 +1,3 @@
CONFIG_KSCAN=n
CONFIG_ZMK_KSCAN_MOCK_DRIVER=y
CONFIG_ZMK_KSCAN_GPIO_DRIVER=n
CONFIG_GPIO=y
CONFIG_GPIO_EMUL=y
CONFIG_ZMK_BLE=n

Some files were not shown because too many files have changed in this diff Show more