forked from zmkfirmware/zmk
-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(studio): Initial RPC infrastructure and subsystems.
* UART and BLE/GATT transports for a protobuf encoded RPC request/response protocol. * Custom framing protocol is used to frame a give message. * Requests/responses are divided into major "subsystems" which handle requests and create response messages. * Notification support, including mapping local events to RPC notifications by a given subsystem. * Meta responses for "no response" and "unlock needed". * Initial basic lock state support in a new core section, and allow specifying if a given RPC callback requires unlocked state or not. * Add behavior subsystem with full metadata support and examples of using callback to serialize a repeated field without extra stack space needed.
- Loading branch information
1 parent
82eed0f
commit 9d36cd5
Showing
27 changed files
with
3,744 additions
and
1,021 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/* | ||
* Copyright (c) 2024 The ZMK Contributors | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
#include <zephyr/linker/linker-defs.h> | ||
|
||
ITERABLE_SECTION_ROM(zmk_rpc_event_mapper, 4) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/* | ||
* Copyright (c) 2023 The ZMK Contributors | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
#include <zephyr/linker/linker-defs.h> | ||
|
||
ITERABLE_SECTION_ROM(zmk_rpc_subsystem_handler, 4) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/* | ||
* Copyright (c) 2023 The ZMK Contributors | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
#include <zephyr/linker/linker-defs.h> | ||
|
||
ITERABLE_SECTION_RAM(zmk_rpc_subsystem, 4) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/* | ||
* Copyright (c) 2024 The ZMK Contributors | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
#include <zephyr/linker/linker-defs.h> | ||
|
||
ITERABLE_SECTION_ROM(zmk_rpc_transport, 4) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
#pragma once | ||
|
||
#include <zmk/event_manager.h> | ||
|
||
enum zmk_studio_core_lock_state { | ||
ZMK_STUDIO_CORE_LOCK_STATE_LOCKED = 0, | ||
ZMK_STUDIO_CORE_LOCK_STATE_UNLOCKING = 1, | ||
ZMK_STUDIO_CORE_LOCK_STATE_UNLOCKED = 2, | ||
}; | ||
|
||
struct zmk_studio_core_lock_state_changed { | ||
enum zmk_studio_core_lock_state state; | ||
}; | ||
|
||
struct zmk_studio_core_unlock_requested {}; | ||
|
||
ZMK_EVENT_DECLARE(zmk_studio_core_lock_state_changed); | ||
ZMK_EVENT_DECLARE(zmk_studio_core_unlock_requested); | ||
|
||
enum zmk_studio_core_lock_state zmk_studio_core_get_lock_state(void); | ||
|
||
void zmk_studio_core_unlock(); | ||
void zmk_studio_core_lock(); | ||
void zmk_studio_core_initiate_unlock(); | ||
void zmk_studio_core_complete_unlock(); | ||
|
||
void zmk_studio_core_reschedule_lock_timeout(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
|
||
#pragma once | ||
|
||
#include <zephyr/sys/iterable_sections.h> | ||
#include <zephyr/sys/ring_buffer.h> | ||
|
||
#include <proto/zmk/studio.pb.h> | ||
|
||
#include <zmk/endpoints_types.h> | ||
#include <zmk/event_manager.h> | ||
#include <zmk/studio/core.h> | ||
|
||
struct zmk_studio_rpc_notification { | ||
zmk_Notification notification; | ||
}; | ||
|
||
ZMK_EVENT_DECLARE(zmk_studio_rpc_notification); | ||
|
||
struct zmk_rpc_subsystem; | ||
|
||
typedef zmk_Response(subsystem_func)(const struct zmk_rpc_subsystem *subsys, | ||
const zmk_Request *req); | ||
|
||
typedef zmk_Response(rpc_func)(const zmk_Request *neq); | ||
|
||
struct zmk_rpc_subsystem { | ||
subsystem_func *func; | ||
uint16_t handlers_start_index; | ||
uint16_t handlers_end_index; | ||
uint8_t subsystem_choice; | ||
}; | ||
|
||
struct zmk_rpc_subsystem_handler { | ||
rpc_func *func; | ||
uint8_t subsystem_choice; | ||
uint8_t request_choice; | ||
bool secured; | ||
}; | ||
|
||
#define ZMK_RPC_NO_RESPONSE() ZMK_RPC_RESPONSE(meta, no_response, true) | ||
#define ZMK_RPC_SIMPLE_ERR(type) \ | ||
ZMK_RPC_RESPONSE(meta, simple_error, zmk_meta_ErrorConditions_##type) | ||
|
||
#define ZMK_RPC_SUBSYSTEM(prefix) \ | ||
zmk_Response subsystem_func_##prefix(const struct zmk_rpc_subsystem *subsys, \ | ||
const zmk_Request *req) { \ | ||
uint8_t which_req = req->subsystem.prefix.which_request_type; \ | ||
LOG_DBG("Got subsystem func for %d", subsys->subsystem_choice); \ | ||
\ | ||
for (int i = subsys->handlers_start_index; i <= subsys->handlers_end_index; i++) { \ | ||
struct zmk_rpc_subsystem_handler *sub_handler; \ | ||
STRUCT_SECTION_GET(zmk_rpc_subsystem_handler, i, &sub_handler); \ | ||
if (sub_handler->request_choice == which_req) { \ | ||
if (sub_handler->secured && \ | ||
zmk_studio_core_get_lock_state() != ZMK_STUDIO_CORE_LOCK_STATE_UNLOCKED) { \ | ||
return ZMK_RPC_RESPONSE(meta, simple_error, \ | ||
zmk_meta_ErrorConditions_UNLOCK_REQUIRED); \ | ||
} \ | ||
return sub_handler->func(req); \ | ||
} \ | ||
} \ | ||
LOG_ERR("No handler func found for %d", which_req); \ | ||
return ZMK_RPC_RESPONSE(meta, simple_error, zmk_meta_ErrorConditions_RPC_NOT_FOUND); \ | ||
} \ | ||
STRUCT_SECTION_ITERABLE(zmk_rpc_subsystem, prefix##_subsystem) = { \ | ||
.func = subsystem_func_##prefix, \ | ||
.subsystem_choice = zmk_Request_##prefix##_tag, \ | ||
}; | ||
|
||
#define ZMK_RPC_SUBSYSTEM_HANDLER(prefix, request_id, _secured) \ | ||
STRUCT_SECTION_ITERABLE(zmk_rpc_subsystem_handler, \ | ||
prefix##_subsystem_handler_##request_id) = { \ | ||
.func = request_id, \ | ||
.subsystem_choice = zmk_Request_##prefix##_tag, \ | ||
.request_choice = zmk_##prefix##_Request_##request_id##_tag, \ | ||
.secured = _secured, \ | ||
}; | ||
|
||
#define ZMK_RPC_NOTIFICATION(subsys, _type, ...) \ | ||
((zmk_Notification){ \ | ||
.which_subsystem = zmk_Notification_##subsys##_tag, \ | ||
.subsystem = \ | ||
{ \ | ||
.subsys = \ | ||
{ \ | ||
.which_notification_type = zmk_##subsys##_Notification_##_type##_tag, \ | ||
.notification_type = {._type = __VA_ARGS__}, \ | ||
}, \ | ||
}, \ | ||
}) | ||
|
||
#define ZMK_RPC_RESPONSE(subsys, _type, ...) \ | ||
((zmk_Response){ \ | ||
.which_type = zmk_Response_request_response_tag, \ | ||
.type = \ | ||
{ \ | ||
.request_response = \ | ||
{ \ | ||
.which_subsystem = zmk_RequestResponse_##subsys##_tag, \ | ||
.subsystem = \ | ||
{ \ | ||
.subsys = \ | ||
{ \ | ||
.which_response_type = \ | ||
zmk_##subsys##_Response_##_type##_tag, \ | ||
.response_type = {._type = __VA_ARGS__}, \ | ||
}, \ | ||
}, \ | ||
}, \ | ||
}, \ | ||
}) | ||
|
||
typedef int(zmk_rpc_event_mapper_cb)(const zmk_event_t *ev, zmk_Notification *n); | ||
|
||
struct zmk_rpc_event_mapper { | ||
zmk_rpc_event_mapper_cb *func; | ||
}; | ||
|
||
#define ZMK_RPC_EVENT_MAPPER_ADD_LISTENER(_t) ZMK_SUBSCRIPTION(studio_rpc, _t) | ||
|
||
#define ZMK_RPC_EVENT_MAPPER(name, _func, ...) \ | ||
FOR_EACH_NONEMPTY_TERM(ZMK_RPC_EVENT_MAPPER_ADD_LISTENER, (;), __VA_ARGS__) \ | ||
STRUCT_SECTION_ITERABLE(zmk_rpc_event_mapper, name) = { \ | ||
.func = _func, \ | ||
}; | ||
|
||
typedef int (*zmk_rpc_rx_start_stop_func)(void); | ||
|
||
typedef void (*zmk_rpc_tx_buffer_notify_func)(struct ring_buf *buf, size_t added, bool message_done, | ||
void *user_data); | ||
typedef void *(*zmk_rpc_tx_user_data_func)(void); | ||
|
||
struct zmk_rpc_transport { | ||
enum zmk_transport transport; | ||
|
||
zmk_rpc_tx_user_data_func tx_user_data; | ||
zmk_rpc_tx_buffer_notify_func tx_notify; | ||
zmk_rpc_rx_start_stop_func rx_start; | ||
zmk_rpc_rx_start_stop_func rx_stop; | ||
}; | ||
|
||
struct ring_buf *zmk_rpc_get_tx_buf(void); | ||
struct ring_buf *zmk_rpc_get_rx_buf(void); | ||
void zmk_rpc_rx_notify(void); | ||
|
||
#define ZMK_RPC_TRANSPORT(name, _transport, _rx_start, _rx_stop, _tx_user_data, _tx_notify) \ | ||
STRUCT_SECTION_ITERABLE(zmk_rpc_transport, name) = { \ | ||
.transport = _transport, \ | ||
.rx_start = _rx_start, \ | ||
.rx_stop = _rx_stop, \ | ||
.tx_user_data = _tx_user_data, \ | ||
.tx_notify = _tx_notify, \ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Copyright (c) 2024 The ZMK Contributors | ||
# SPDX-License-Identifier: MIT | ||
|
||
zephyr_linker_sources(DATA_SECTIONS ../../include/linker/zmk-rpc-subsystems.ld) | ||
zephyr_linker_sources(SECTIONS ../../include/linker/zmk-rpc-subsystem-handlers.ld) | ||
zephyr_linker_sources(SECTIONS ../../include/linker/zmk-rpc-event-mappers.ld) | ||
zephyr_linker_sources(SECTIONS ../../include/linker/zmk-rpc-transport.ld) | ||
|
||
target_sources(app PRIVATE msg_framing.c) | ||
target_sources(app PRIVATE rpc.c) | ||
target_sources(app PRIVATE core.c) | ||
target_sources(app PRIVATE behavior_subsystem.c) | ||
target_sources(app PRIVATE core_subsystem.c) | ||
target_sources_ifdef(CONFIG_ZMK_STUDIO_TRANSPORT_UART app PRIVATE uart_rpc_transport.c) | ||
target_sources_ifdef(CONFIG_ZMK_STUDIO_TRANSPORT_BLE app PRIVATE gatt_rpc_transport.c) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# Copyright (c) 2024 The ZMK Contributors | ||
# SPDX-License-Identifier: MIT | ||
|
||
menuconfig ZMK_STUDIO | ||
bool "Studio Support" | ||
select NANOPB | ||
# These two save stack size | ||
imply NANOPB_NO_ERRMSG | ||
imply NANOPB_WITHOUT_64BIT | ||
select ZMK_BEHAVIOR_METADATA | ||
select ZMK_BEHAVIOR_LOCAL_IDS | ||
select RING_BUFFER | ||
help | ||
Add firmware support for realtime keymap updates (ZMK Studio) | ||
|
||
if ZMK_STUDIO | ||
|
||
module = ZMK_STUDIO | ||
module-str = zmk_studio | ||
source "subsys/logging/Kconfig.template.log_config" | ||
|
||
menuconfig ZMK_STUDIO_LOCKING | ||
bool "Lock Support" | ||
default y | ||
|
||
if ZMK_STUDIO_LOCKING | ||
|
||
config ZMK_STUDIO_LOCK_IDLE_TIMEOUT_SEC | ||
int "Idle Timeout" | ||
default 500 | ||
|
||
config ZMK_STUDIO_LOCK_ON_DISCONNECT | ||
bool "Lock On Disconnect" | ||
default y | ||
|
||
endif | ||
|
||
menu "Remote Procedure Calls (RPC)" | ||
|
||
menu "Transports" | ||
|
||
config ZMK_STUDIO_TRANSPORT_UART | ||
bool "Serial" | ||
select SERIAL | ||
select RING_BUFFER | ||
default y if ZMK_USB | ||
|
||
config ZMK_STUDIO_TRANSPORT_UART_RX_STACK_SIZE | ||
int "RX Stack Size" | ||
depends on !UART_INTERRUPT_DRIVEN | ||
default 512 | ||
|
||
config ZMK_STUDIO_TRANSPORT_BLE | ||
bool "BLE (GATT)" | ||
select RING_BUFFER | ||
select BT_USER_DATA_LEN_UPDATE | ||
depends on ZMK_BLE | ||
default y | ||
|
||
config BT_CONN_TX_MAX | ||
default 64 if ZMK_STUDIO_TRANSPORT_BLE | ||
|
||
config ZMK_STUDIO_TRANSPORT_BLE_PREF_LATENCY | ||
int "BLE Transport preferred latency" | ||
default 10 | ||
help | ||
When the studio UI is connected, a lower latency can be requested in order | ||
to make the interactions between keyboard and studio faster. | ||
|
||
endmenu | ||
|
||
config ZMK_STUDIO_RPC_THREAD_STACK_SIZE | ||
int "RPC Thread Stack Size" | ||
default 1800 | ||
|
||
config ZMK_STUDIO_RPC_RX_BUF_SIZE | ||
int "RX Buffer Size" | ||
default 30 | ||
|
||
config ZMK_STUDIO_RPC_TX_BUF_SIZE | ||
int "RX Buffer Size" | ||
default 64 | ||
|
||
endmenu | ||
|
||
endif |
Oops, something went wrong.