Add firmware CDC protocol module
This commit is contained in:
220
src/modules/usb_cdc_proto_module.c
Normal file
220
src/modules/usb_cdc_proto_module.c
Normal file
@@ -0,0 +1,220 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <app_event_manager.h>
|
||||
|
||||
#define MODULE usb_cdc_proto
|
||||
#include <caf/events/module_state_event.h>
|
||||
|
||||
#include "function_key_event.h"
|
||||
#include "keyboard_led_event.h"
|
||||
#include "keyboard_proto.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||
|
||||
#define CDC_RX_BUFFER_SIZE 256
|
||||
#define CDC_TX_BUFFER_SIZE KEYBOARD_PROTO_MAX_FRAME_SIZE
|
||||
|
||||
struct usb_cdc_proto_ctx {
|
||||
const struct device *dev;
|
||||
struct k_work rx_work;
|
||||
uint8_t rx_buffer[CDC_RX_BUFFER_SIZE];
|
||||
size_t rx_len;
|
||||
bool ready;
|
||||
};
|
||||
|
||||
static struct usb_cdc_proto_ctx g_usb_cdc = {
|
||||
.dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart),
|
||||
};
|
||||
|
||||
static uint32_t keyboard_proto_packet_type_from_body(
|
||||
const struct keyboard_cdc_CdcPacketBody *body)
|
||||
{
|
||||
switch (body->which_body) {
|
||||
case 1U:
|
||||
return 0x01U;
|
||||
case 2U:
|
||||
return 0x02U;
|
||||
case 3U:
|
||||
return 0x10U;
|
||||
case 4U:
|
||||
return 0x20U;
|
||||
case 5U:
|
||||
return 0x21U;
|
||||
case 6U:
|
||||
return 0x30U;
|
||||
case 7U:
|
||||
return 0x31U;
|
||||
case 8U:
|
||||
return 0x7EU;
|
||||
case 9U:
|
||||
return 0x7FU;
|
||||
default:
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
|
||||
static bool usb_cdc_proto_send_body(
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
void *user_data)
|
||||
{
|
||||
struct usb_cdc_proto_ctx *ctx = user_data;
|
||||
uint8_t tx_buffer[CDC_TX_BUFFER_SIZE];
|
||||
size_t tx_size = 0U;
|
||||
|
||||
if ((ctx == NULL) || !ctx->ready || (ctx->dev == NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!keyboard_proto_encode_cdc_frame(
|
||||
keyboard_proto_packet_type_from_body(body),
|
||||
body,
|
||||
tx_buffer,
|
||||
sizeof(tx_buffer),
|
||||
&tx_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tx_size; ++i) {
|
||||
uart_poll_out(ctx->dev, tx_buffer[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void usb_cdc_proto_process_rx_work(struct k_work *work)
|
||||
{
|
||||
struct keyboard_cdc_CdcFrame frame;
|
||||
struct keyboard_cdc_CdcPacketBody body;
|
||||
|
||||
ARG_UNUSED(work);
|
||||
|
||||
while (keyboard_proto_try_take_cdc_frame(
|
||||
g_usb_cdc.rx_buffer,
|
||||
&g_usb_cdc.rx_len,
|
||||
&frame)) {
|
||||
if (!keyboard_proto_decode_body(frame.payload.bytes,
|
||||
frame.payload.size,
|
||||
&body)) {
|
||||
LOG_WRN("Drop invalid CDC body");
|
||||
continue;
|
||||
}
|
||||
|
||||
(void)keyboard_proto_handle_host_body(
|
||||
&body,
|
||||
KEYBOARD_PROTO_TRANSPORT_CDC,
|
||||
usb_cdc_proto_send_body,
|
||||
&g_usb_cdc);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_cdc_proto_irq_handler(const struct device *dev, void *user_data)
|
||||
{
|
||||
uint8_t buffer[64];
|
||||
|
||||
ARG_UNUSED(user_data);
|
||||
|
||||
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
|
||||
if (!uart_irq_rx_ready(dev)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int recv_len = uart_fifo_read(dev, buffer, sizeof(buffer));
|
||||
if (recv_len <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((g_usb_cdc.rx_len + (size_t)recv_len) > sizeof(g_usb_cdc.rx_buffer)) {
|
||||
LOG_WRN("CDC RX overflow, drop buffer");
|
||||
g_usb_cdc.rx_len = 0U;
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(&g_usb_cdc.rx_buffer[g_usb_cdc.rx_len], buffer, recv_len);
|
||||
g_usb_cdc.rx_len += (size_t)recv_len;
|
||||
k_work_submit(&g_usb_cdc.rx_work);
|
||||
}
|
||||
}
|
||||
|
||||
static bool handle_module_state_event(const struct module_state_event *event)
|
||||
{
|
||||
if (!check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!device_is_ready(g_usb_cdc.dev)) {
|
||||
LOG_ERR("CDC ACM device not ready");
|
||||
module_set_state(MODULE_STATE_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
k_work_init(&g_usb_cdc.rx_work, usb_cdc_proto_process_rx_work);
|
||||
uart_irq_callback_set(g_usb_cdc.dev, usb_cdc_proto_irq_handler);
|
||||
uart_irq_rx_enable(g_usb_cdc.dev);
|
||||
g_usb_cdc.ready = true;
|
||||
module_set_state(MODULE_STATE_READY);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handle_function_key_event(const struct function_key_event *event)
|
||||
{
|
||||
struct keyboard_cdc_CdcPacketBody body;
|
||||
|
||||
if (!g_usb_cdc.ready) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!keyboard_proto_build_function_key_event_body(event->usage,
|
||||
event->pressed,
|
||||
&body)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(void)usb_cdc_proto_send_body(&body, &g_usb_cdc);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handle_keyboard_led_event(const struct keyboard_led_event *event)
|
||||
{
|
||||
struct keyboard_cdc_CdcPacketBody body;
|
||||
|
||||
if (!g_usb_cdc.ready) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!keyboard_proto_build_led_state_body(
|
||||
keyboard_led_event_get_mask(event),
|
||||
&body)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(void)usb_cdc_proto_send_body(&body, &g_usb_cdc);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool app_event_handler(const struct app_event_header *aeh)
|
||||
{
|
||||
if (is_module_state_event(aeh)) {
|
||||
return handle_module_state_event(cast_module_state_event(aeh));
|
||||
}
|
||||
|
||||
if (is_function_key_event(aeh)) {
|
||||
return handle_function_key_event(cast_function_key_event(aeh));
|
||||
}
|
||||
|
||||
if (is_keyboard_led_event(aeh)) {
|
||||
return handle_keyboard_led_event(cast_keyboard_led_event(aeh));
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, function_key_event);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, keyboard_led_event);
|
||||
Reference in New Issue
Block a user