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.

Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
This commit is contained in:
Peter Johanson
2024-02-19 08:48:20 +00:00
committed by Pete Johanson
parent ea64fcaf71
commit feda96eb40
28 changed files with 2840 additions and 9 deletions

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/drivers/hwinfo.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <pb_encode.h>
#include <zmk/studio/core.h>
#include <zmk/studio/rpc.h>
ZMK_RPC_SUBSYSTEM(core)
#define CORE_RESPONSE(type, ...) ZMK_RPC_RESPONSE(core, type, __VA_ARGS__)
static bool encode_device_info_name(pb_ostream_t *stream, const pb_field_t *field,
void *const *arg) {
if (!pb_encode_tag_for_field(stream, field)) {
return false;
}
return pb_encode_string(stream, CONFIG_ZMK_KEYBOARD_NAME, strlen(CONFIG_ZMK_KEYBOARD_NAME));
}
#if IS_ENABLED(CONFIG_HWINFO)
static bool encode_device_info_serial_number(pb_ostream_t *stream, const pb_field_t *field,
void *const *arg) {
uint8_t id_buffer[32];
const ssize_t id_size = hwinfo_get_device_id(id_buffer, ARRAY_SIZE(id_buffer));
if (id_size <= 0) {
return true;
}
if (!pb_encode_tag_for_field(stream, field)) {
return false;
}
return pb_encode_string(stream, id_buffer, id_size);
}
#endif // IS_ENABLED(CONFIG_HWINFO)
zmk_studio_Response get_device_info(const zmk_studio_Request *req) {
zmk_core_GetDeviceInfoResponse resp = zmk_core_GetDeviceInfoResponse_init_zero;
resp.name.funcs.encode = encode_device_info_name;
#if IS_ENABLED(CONFIG_HWINFO)
resp.serial_number.funcs.encode = encode_device_info_serial_number;
#endif // IS_ENABLED(CONFIG_HWINFO)
return CORE_RESPONSE(get_device_info, resp);
}
zmk_studio_Response get_lock_state(const zmk_studio_Request *req) {
zmk_core_LockState resp = zmk_studio_core_get_lock_state();
return CORE_RESPONSE(get_lock_state, resp);
}
ZMK_RPC_SUBSYSTEM_HANDLER(core, get_device_info, ZMK_STUDIO_RPC_HANDLER_UNSECURED);
ZMK_RPC_SUBSYSTEM_HANDLER(core, get_lock_state, ZMK_STUDIO_RPC_HANDLER_UNSECURED);
static int core_event_mapper(const zmk_event_t *eh, zmk_studio_Notification *n) {
struct zmk_studio_core_lock_state_changed *lock_ev = as_zmk_studio_core_lock_state_changed(eh);
if (!lock_ev) {
return -ENOTSUP;
}
LOG_DBG("Mapped a lock state event properly");
*n = ZMK_RPC_NOTIFICATION(core, lock_state_changed, lock_ev->state);
return 0;
}
ZMK_RPC_EVENT_MAPPER(core, core_event_mapper, zmk_studio_core_lock_state_changed);