forked from zmkfirmware/zmk
-
Notifications
You must be signed in to change notification settings - Fork 53
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
08369f5
commit a237af9
Showing
27 changed files
with
1,182 additions
and
0 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
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,23 @@ | ||
|
||
#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; | ||
}; | ||
|
||
ZMK_EVENT_DECLARE(zmk_studio_core_lock_state_changed); | ||
|
||
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(); |
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,138 @@ | ||
|
||
#pragma once | ||
|
||
#include <zephyr/sys/iterable_sections.h> | ||
|
||
#include <proto/zmk/studio.pb.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_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__}, \ | ||
}, \ | ||
}, \ | ||
}, \ | ||
}, \ | ||
}) | ||
|
||
#define ZMK_RPC_NOTIFICATION_PUBLISHER(name, _cb) \ | ||
static int name##_listener_cb(const zmk_event_t *eh) { \ | ||
struct zmk_studio_rpc_notification *notif = as_zmk_studio_rpc_notification(eh); \ | ||
__ASSERT(notif, "Only notification events should be received"); \ | ||
\ | ||
zmk_Response resp = zmk_Response_init_zero; \ | ||
resp.which_type = zmk_Response_notification_tag; \ | ||
resp.type.notification = notif->notification; \ | ||
_cb(&resp); \ | ||
\ | ||
return ZMK_EV_EVENT_BUBBLE; \ | ||
}; \ | ||
ZMK_LISTENER(name##_listener, name##_listener_cb); \ | ||
ZMK_SUBSCRIPTION(name##_listener, zmk_studio_rpc_notification); | ||
|
||
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, \ | ||
}; | ||
|
||
zmk_Response zmk_rpc_handle_request(const zmk_Request *req); |
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,2 @@ | ||
include/ | ||
src/ |
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,3 @@ | ||
zmk.behaviors.GetBehaviorDetailsResponse.friendly_name max_size:20 | ||
zmk.behaviors.BehaviorBindingParameterStandard.name max_size:20 | ||
zmk.behaviors.BehaviorParameterValueDescription.name max_size:32 |
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,80 @@ | ||
syntax = "proto3"; | ||
|
||
package zmk.behaviors; | ||
|
||
message Request { | ||
oneof request_type { | ||
bool list_all_behaviors = 1; | ||
GetBehaviorDetailsRequest get_behavior_details = 2; | ||
} | ||
} | ||
|
||
message GetBehaviorDetailsRequest { | ||
uint32 behavior_id = 1; | ||
} | ||
|
||
message Response { | ||
oneof response_type { | ||
ListAllBehaviorsResponse list_all_behaviors = 1; | ||
GetBehaviorDetailsResponse get_behavior_details = 2; | ||
} | ||
} | ||
|
||
message ListAllBehaviorsResponse { | ||
repeated uint32 behaviors = 1; | ||
} | ||
|
||
message GetBehaviorDetailsResponse { | ||
uint32 id = 1; | ||
string friendly_name = 2; | ||
BehaviorBindingParameters parameters = 3; | ||
} | ||
|
||
message BehaviorBindingParameters { | ||
oneof parameter_types { | ||
BehaviorBindingParametersStandard standard = 1; | ||
BehaviorBindingParametersCustom custom = 2; | ||
} | ||
} | ||
|
||
message BehaviorBindingParametersStandard { | ||
BehaviorBindingParameterStandard param1 = 1; | ||
BehaviorBindingParameterStandard param2 = 2; | ||
|
||
} | ||
|
||
message BehaviorBindingParameterStandard { | ||
BehaviorBindingParameterStandardDomain domain = 1; | ||
string name = 2; | ||
} | ||
|
||
enum BehaviorBindingParameterStandardDomain { | ||
NIL = 0; | ||
HID_USAGE = 1; | ||
LAYER_INDEX = 2; | ||
HSV_VALUE = 3; | ||
} | ||
|
||
message BehaviorBindingParametersCustom { | ||
repeated BehaviorBindingParametersCustomSet param_sets = 1; | ||
} | ||
|
||
message BehaviorBindingParametersCustomSet { | ||
repeated BehaviorParameterValueDescription param1 = 1; | ||
repeated BehaviorParameterValueDescription param2 = 2; | ||
} | ||
|
||
message BehaviorParameterValueDescriptionRange { | ||
uint32 min = 1; | ||
uint32 max = 2; | ||
} | ||
|
||
message BehaviorParameterValueDescription { | ||
string name = 1; | ||
|
||
oneof value_type { | ||
uint32 constant = 2; | ||
BehaviorParameterValueDescriptionRange range = 3; | ||
BehaviorBindingParameterStandardDomain standard = 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,29 @@ | ||
syntax = "proto3"; | ||
|
||
package zmk.core; | ||
|
||
enum LockState { | ||
ZMK_STUDIO_CORE_LOCK_STATE_LOCKED = 0; | ||
ZMK_STUDIO_CORE_LOCK_STATE_UNLOCKING = 1; | ||
ZMK_STUDIO_CORE_LOCK_STATE_UNLOCKED = 2; | ||
} | ||
|
||
message Request { | ||
oneof request_type { | ||
bool get_lock_state = 1; | ||
bool request_unlock = 2; | ||
bool lock = 3; | ||
} | ||
} | ||
|
||
message Response { | ||
oneof response_type { | ||
LockState get_lock_state = 1; | ||
} | ||
} | ||
|
||
message Notification { | ||
oneof notification_type { | ||
LockState lock_state_changed = 1; | ||
} | ||
} |
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 @@ | ||
syntax = "proto3"; | ||
|
||
package zmk.keymap; | ||
|
||
message Request { | ||
oneof request_type { | ||
bool get_layer_summaries = 1; | ||
} | ||
} |
Oops, something went wrong.