forked from kofal.net/zmk
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:
committed by
Pete Johanson
parent
ea64fcaf71
commit
feda96eb40
170
app/src/studio/uart_rpc_transport.c
Normal file
170
app/src/studio/uart_rpc_transport.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/sys/ring_buffer.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zmk/studio/rpc.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk_studio, CONFIG_ZMK_STUDIO_LOG_LEVEL);
|
||||
|
||||
/* change this to any other UART peripheral if desired */
|
||||
#define UART_DEVICE_NODE DT_CHOSEN(zmk_studio_rpc_uart)
|
||||
|
||||
static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE);
|
||||
|
||||
static void tx_notify(struct ring_buf *tx_ring_buf, size_t written, bool msg_done,
|
||||
void *user_data) {
|
||||
if (msg_done || (ring_buf_size_get(tx_ring_buf) > (ring_buf_capacity_get(tx_ring_buf) / 2))) {
|
||||
#if IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN)
|
||||
uart_irq_tx_enable(uart_dev);
|
||||
#else
|
||||
struct ring_buf *tx_buf = zmk_rpc_get_tx_buf();
|
||||
uint8_t *buf;
|
||||
uint32_t claim_len;
|
||||
while ((claim_len = ring_buf_get_claim(tx_buf, &buf, tx_buf->size)) > 0) {
|
||||
for (int i = 0; i < claim_len; i++) {
|
||||
uart_poll_out(uart_dev, buf[i]);
|
||||
}
|
||||
|
||||
ring_buf_get_finish(tx_buf, claim_len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if !IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN)
|
||||
|
||||
static void uart_rx_main(void) {
|
||||
for (;;) {
|
||||
uint8_t *buf;
|
||||
struct ring_buf *ring_buf = zmk_rpc_get_rx_buf();
|
||||
uint32_t claim_len = ring_buf_put_claim(ring_buf, &buf, 1);
|
||||
|
||||
if (claim_len < 1) {
|
||||
LOG_WRN("NO CLAIM ABLE TO BE HAD");
|
||||
k_sleep(K_MSEC(1));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uart_poll_in(uart_dev, buf) < 0) {
|
||||
ring_buf_put_finish(ring_buf, 0);
|
||||
k_sleep(K_MSEC(1));
|
||||
} else {
|
||||
ring_buf_put_finish(ring_buf, 1);
|
||||
zmk_rpc_rx_notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
K_THREAD_DEFINE(uart_transport_read_thread, CONFIG_ZMK_STUDIO_TRANSPORT_UART_RX_STACK_SIZE,
|
||||
uart_rx_main, NULL, NULL, NULL, K_LOWEST_APPLICATION_THREAD_PRIO, 0, 0);
|
||||
|
||||
#endif
|
||||
|
||||
static int start_rx() {
|
||||
#if IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN)
|
||||
uart_irq_rx_enable(uart_dev);
|
||||
#else
|
||||
k_thread_resume(uart_transport_read_thread);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stop_rx(void) {
|
||||
#if IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN)
|
||||
uart_irq_rx_disable(uart_dev);
|
||||
#else
|
||||
k_thread_suspend(uart_transport_read_thread);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
ZMK_RPC_TRANSPORT(uart, ZMK_TRANSPORT_USB, start_rx, stop_rx, NULL, tx_notify);
|
||||
|
||||
#if IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN)
|
||||
|
||||
/*
|
||||
* Read characters from UART until line end is detected. Afterwards push the
|
||||
* data to the message queue.
|
||||
*/
|
||||
static void serial_cb(const struct device *dev, void *user_data) {
|
||||
if (!uart_irq_update(uart_dev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (uart_irq_rx_ready(uart_dev)) {
|
||||
/* read until FIFO empty */
|
||||
uint32_t last_read = 0, len = 0;
|
||||
struct ring_buf *buf = zmk_rpc_get_rx_buf();
|
||||
do {
|
||||
uint8_t *buffer;
|
||||
len = ring_buf_put_claim(buf, &buffer, buf->size);
|
||||
if (len > 0) {
|
||||
last_read = uart_fifo_read(uart_dev, buffer, len);
|
||||
|
||||
ring_buf_put_finish(buf, last_read);
|
||||
} else {
|
||||
LOG_ERR("Dropping incoming RPC byte, insufficient room in the RX buffer. Bump "
|
||||
"CONFIG_ZMK_STUDIO_RPC_RX_BUF_SIZE.");
|
||||
uint8_t dummy;
|
||||
last_read = uart_fifo_read(uart_dev, &dummy, 1);
|
||||
}
|
||||
} while (last_read && last_read == len);
|
||||
|
||||
zmk_rpc_rx_notify();
|
||||
}
|
||||
|
||||
if (uart_irq_tx_ready(uart_dev)) {
|
||||
struct ring_buf *tx_buf = zmk_rpc_get_tx_buf();
|
||||
uint32_t len;
|
||||
while ((len = ring_buf_size_get(tx_buf)) > 0) {
|
||||
uint8_t *buf;
|
||||
uint32_t claim_len = ring_buf_get_claim(tx_buf, &buf, tx_buf->size);
|
||||
|
||||
if (claim_len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int sent = uart_fifo_fill(uart_dev, buf, claim_len);
|
||||
|
||||
ring_buf_get_finish(tx_buf, MAX(sent, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int uart_rpc_interface_init(void) {
|
||||
if (!device_is_ready(uart_dev)) {
|
||||
LOG_ERR("UART device not found!");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN)
|
||||
/* configure interrupt and callback to receive data */
|
||||
int ret = uart_irq_callback_user_data_set(uart_dev, serial_cb, NULL);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOTSUP) {
|
||||
printk("Interrupt-driven UART API support not enabled\n");
|
||||
} else if (ret == -ENOSYS) {
|
||||
printk("UART device does not support interrupt-driven API\n");
|
||||
} else {
|
||||
printk("Error setting UART callback: %d\n", ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(uart_rpc_interface_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
||||
Reference in New Issue
Block a user