Merge branch 'zmkfirmware:main' into main
This commit is contained in:
commit
54f7f39963
193 changed files with 20329 additions and 5533 deletions
131
.github/workflows/build-user-config.yml
vendored
Normal file
131
.github/workflows/build-user-config.yml
vendored
Normal 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
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -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:
|
||||
|
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -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:
|
||||
|
|
|
@ -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/)
|
||||
|
|
134
app/Kconfig
134
app/Kconfig
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -24,7 +24,4 @@ config ZMK_BLE
|
|||
config ZMK_USB
|
||||
default y
|
||||
|
||||
config ZMK_BATTERY_VOLTAGE_DIVIDER
|
||||
default y
|
||||
|
||||
endif # BOARD_BLUEMICRO840_V1
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -17,7 +17,4 @@ config ZMK_USB
|
|||
config ZMK_KSCAN_MATRIX_POLLING
|
||||
default y
|
||||
|
||||
config ZMK_KSCAN_COMPOSITE_DRIVER
|
||||
default y
|
||||
|
||||
endif # BOARD_FERRIS
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)>;
|
||||
};
|
||||
|
|
|
@ -22,7 +22,4 @@ config ZMK_BLE
|
|||
config ZMK_USB
|
||||
default y
|
||||
|
||||
config ZMK_BATTERY_VOLTAGE_DIVIDER
|
||||
default y
|
||||
|
||||
endif # BOARD_NICE60
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -25,7 +25,4 @@ config ZMK_BLE
|
|||
config ZMK_USB
|
||||
default y
|
||||
|
||||
config ZMK_BATTERY_VOLTAGE_DIVIDER
|
||||
default y
|
||||
|
||||
endif # BOARD_S40NC
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
/ {
|
||||
chosen {
|
||||
zephyr,display = &oled;
|
||||
zmk,kscan = &kscan0;
|
||||
zmk,matrix_transform = &default_transform;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1 +1,5 @@
|
|||
menuconfig ZMK_DRIVERS_GPIO
|
||||
bool "GPIO"
|
||||
|
||||
rsource "Kconfig.mcp23017"
|
||||
rsource "Kconfig.595"
|
25
app/drivers/gpio/Kconfig.595
Normal file
25
app/drivers/gpio/Kconfig.595
Normal 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
|
|
@ -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
215
app/drivers/gpio/gpio_595.c
Normal 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 *)®_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, ®_595_##n##_drvdata, ®_595_##n##_config, \
|
||||
POST_KERNEL, CONFIG_GPIO_595_INIT_PRIORITY, &api_table);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(REG_595_INIT)
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
33
app/drivers/zephyr/dts/bindings/gpio/zmk,gpio-595.yaml
Normal file
33
app/drivers/zephyr/dts/bindings/gpio/zmk,gpio-595.yaml
Normal 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
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
compatible = "zmk,behavior-caps-word";
|
||||
label = "CAPS_WORD";
|
||||
#binding-cells = <0>;
|
||||
continue-list = <UNDERSCORE>;
|
||||
continue-list = <UNDERSCORE BACKSPACE DELETE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
15
app/dts/behaviors/key_toggle.dtsi
Normal file
15
app/dts/behaviors/key_toggle.dtsi
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -20,6 +20,8 @@ properties:
|
|||
default: -1
|
||||
quick_tap_ms: # deprecated
|
||||
type: int
|
||||
global-quick-tap:
|
||||
type: boolean
|
||||
flavor:
|
||||
type: string
|
||||
required: false
|
||||
|
|
8
app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml
Normal file
8
app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml
Normal 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
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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) */
|
||||
|
|
19
app/include/zmk/display/widgets/peripheral_status.h
Normal file
19
app/include/zmk/display/widgets/peripheral_status.h
Normal 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);
|
|
@ -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;
|
||||
|
|
16
app/include/zmk/events/split_peripheral_status_changed.h
Normal file
16
app/include/zmk/events/split_peripheral_status_changed.h
Normal 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);
|
|
@ -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();
|
||||
|
|
9
app/include/zmk/split/bluetooth/peripheral.h
Normal file
9
app/include/zmk/split/bluetooth/peripheral.h
Normal 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);
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)},
|
||||
|
||||
|
|
|
@ -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), \
|
||||
|
|
44
app/src/behaviors/behavior_key_toggle.c
Normal file
44
app/src/behaviors/behavior_key_toggle.c
Normal 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)
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
60
app/src/display/widgets/peripheral_status.c
Normal file
60
app/src/display/widgets/peripheral_status.c
Normal 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;
|
||||
}
|
10
app/src/events/split_peripheral_status_changed.c
Normal file
10
app/src/events/split_peripheral_status_changed.c
Normal 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);
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
6
app/src/split/CMakeLists.txt
Normal file
6
app/src/split/CMakeLists.txt
Normal 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
26
app/src/split/Kconfig
Normal 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"
|
11
app/src/split/bluetooth/CMakeLists.txt
Normal file
11
app/src/split/bluetooth/CMakeLists.txt
Normal 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()
|
95
app/src/split/bluetooth/Kconfig
Normal file
95
app/src/split/bluetooth/Kconfig
Normal 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
|
||||
|
|
@ -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;
|
||||
|
|
128
app/src/split/bluetooth/peripheral.c
Normal file
128
app/src/split/bluetooth/peripheral.c
Normal 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);
|
|
@ -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
Loading…
Add table
Reference in a new issue