Add firmware nanopb protocol core
This commit is contained in:
@@ -17,6 +17,9 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
|||||||
|
|
||||||
project(new_kbd)
|
project(new_kbd)
|
||||||
|
|
||||||
|
list(APPEND CMAKE_MODULE_PATH ${ZEPHYR_BASE}/modules/nanopb)
|
||||||
|
include(nanopb)
|
||||||
|
|
||||||
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)
|
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)
|
||||||
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/events)
|
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/events)
|
||||||
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/ui)
|
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/ui)
|
||||||
@@ -33,11 +36,17 @@ target_compile_definitions(app PRIVATE
|
|||||||
APP_HID_KEYMAP_DEF_PATH=\"hid_keymap_def.h\"
|
APP_HID_KEYMAP_DEF_PATH=\"hid_keymap_def.h\"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
zephyr_nanopb_sources(app
|
||||||
|
KeyBorad/proto/keyboard.proto
|
||||||
|
)
|
||||||
|
|
||||||
target_sources(app PRIVATE
|
target_sources(app PRIVATE
|
||||||
src/main.c
|
src/main.c
|
||||||
src/events/battery_status_event.c
|
src/events/battery_status_event.c
|
||||||
src/events/config_event.c
|
src/events/config_event.c
|
||||||
src/events/display_theme_event.c
|
src/events/display_theme_event.c
|
||||||
|
src/events/function_bitmap_event.c
|
||||||
|
src/events/function_key_event.c
|
||||||
src/events/hid_boot_event.c
|
src/events/hid_boot_event.c
|
||||||
src/events/hid_host_ack_event.c
|
src/events/hid_host_ack_event.c
|
||||||
src/events/hid_host_command_error_event.c
|
src/events/hid_host_command_error_event.c
|
||||||
@@ -59,13 +68,16 @@ target_sources(app PRIVATE
|
|||||||
src/modules/display_module.c
|
src/modules/display_module.c
|
||||||
src/modules/hid_host_command_module.c
|
src/modules/hid_host_command_module.c
|
||||||
src/modules/hid_tx_manager_module.c
|
src/modules/hid_tx_manager_module.c
|
||||||
|
src/modules/keyboard_proto.c
|
||||||
src/modules/keyboard_module.c
|
src/modules/keyboard_module.c
|
||||||
src/modules/led_state_module.c
|
src/modules/led_state_module.c
|
||||||
src/modules/mode_switch_module.c
|
src/modules/mode_switch_module.c
|
||||||
src/modules/qdec_module.c
|
src/modules/qdec_module.c
|
||||||
src/modules/time_manager_module.c
|
src/modules/time_manager_module.c
|
||||||
|
src/modules/usb_cdc_proto_module.c
|
||||||
src/modules/usb_hid_module.c
|
src/modules/usb_hid_module.c
|
||||||
src/modules/ble_hid_module.c
|
src/modules/ble_hid_module.c
|
||||||
|
src/modules/ble_gatt_proto_module.c
|
||||||
src/ui/display_ui.c
|
src/ui/display_ui.c
|
||||||
src/ui/fonts/ui_font_keyboard_small_18.c
|
src/ui/fonts/ui_font_keyboard_small_18.c
|
||||||
src/ui/fonts/ui_font_keyboard_time_48.c
|
src/ui/fonts/ui_font_keyboard_time_48.c
|
||||||
|
|||||||
@@ -36,6 +36,13 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
&zephyr_udc0 {
|
||||||
|
cdc_acm_uart0: cdc_acm_uart0 {
|
||||||
|
compatible = "zephyr,cdc-acm-uart";
|
||||||
|
label = "new_kbd CDC ACM";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
&gpregret1 {
|
&gpregret1 {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
|
||||||
|
|||||||
@@ -32,11 +32,6 @@ Design notes:
|
|||||||
|
|
||||||
## Planned next nodes
|
## Planned next nodes
|
||||||
|
|
||||||
- keyboard split logic uses `function_bitmap_event`
|
|
||||||
- CDC transport module
|
|
||||||
- GATT transport module
|
|
||||||
- nanopb integration and generated protocol code
|
|
||||||
|
|
||||||
### Node 2: keyboard split point
|
### Node 2: keyboard split point
|
||||||
|
|
||||||
Files updated in this step:
|
Files updated in this step:
|
||||||
@@ -59,3 +54,43 @@ Implemented behavior:
|
|||||||
- for consumer usages marked as function keys:
|
- for consumer usages marked as function keys:
|
||||||
- stop normal HID reporting
|
- stop normal HID reporting
|
||||||
- emit `function_key_event`
|
- emit `function_key_event`
|
||||||
|
|
||||||
|
### Node 3: nanopb build integration and protocol core
|
||||||
|
|
||||||
|
Files updated in this step:
|
||||||
|
|
||||||
|
- `CMakeLists.txt`
|
||||||
|
- `prj.conf`
|
||||||
|
- `app.overlay`
|
||||||
|
- `inc/keyboard_proto.h`
|
||||||
|
- `src/modules/keyboard_proto.c`
|
||||||
|
|
||||||
|
Design notes:
|
||||||
|
|
||||||
|
- use NCS built-in nanopb instead of a hand-written protobuf runtime
|
||||||
|
- generate protocol code from the shared `.proto`
|
||||||
|
- keep one firmware-side protocol helper that:
|
||||||
|
- encodes and decodes `CdcPacketBody`
|
||||||
|
- encodes and decodes `CdcFrame`
|
||||||
|
- validates checksum
|
||||||
|
- routes host commands back into existing modules
|
||||||
|
|
||||||
|
Implemented behavior:
|
||||||
|
|
||||||
|
- enable CDC ACM class in Zephyr USB device-next stack
|
||||||
|
- add a CDC ACM UART node in devicetree
|
||||||
|
- wire `zephyr_nanopb_sources()` into the build
|
||||||
|
- add protocol helper functions for:
|
||||||
|
- body encode/decode
|
||||||
|
- CDC frame encode
|
||||||
|
- CDC frame stream extraction
|
||||||
|
- hello response build
|
||||||
|
- ack/error build
|
||||||
|
- function key event build
|
||||||
|
- LED state build
|
||||||
|
- host command dispatch
|
||||||
|
|
||||||
|
## Planned next nodes
|
||||||
|
|
||||||
|
- CDC transport module
|
||||||
|
- GATT transport module
|
||||||
|
|||||||
64
inc/keyboard_proto.h
Normal file
64
inc/keyboard_proto.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#ifndef KEYBOARD_PROTO_H__
|
||||||
|
#define KEYBOARD_PROTO_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <pb_decode.h>
|
||||||
|
#include <pb_encode.h>
|
||||||
|
|
||||||
|
#include "KeyBorad/proto/keyboard.pb.h"
|
||||||
|
|
||||||
|
#define KEYBOARD_PROTO_MAX_BODY_SIZE 64U
|
||||||
|
#define KEYBOARD_PROTO_MAX_FRAME_SIZE 128U
|
||||||
|
#define KEYBOARD_PROTO_FUNCTION_BITMAP_SIZE 29U
|
||||||
|
|
||||||
|
enum keyboard_proto_transport {
|
||||||
|
KEYBOARD_PROTO_TRANSPORT_CDC = 0,
|
||||||
|
KEYBOARD_PROTO_TRANSPORT_GATT,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef bool (*keyboard_proto_send_body_fn)(
|
||||||
|
const struct keyboard_cdc_CdcPacketBody *body,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
bool keyboard_proto_encode_body(
|
||||||
|
const struct keyboard_cdc_CdcPacketBody *body,
|
||||||
|
uint8_t *buffer,
|
||||||
|
size_t buffer_size,
|
||||||
|
size_t *encoded_size);
|
||||||
|
|
||||||
|
bool keyboard_proto_decode_body(
|
||||||
|
const uint8_t *buffer,
|
||||||
|
size_t buffer_size,
|
||||||
|
struct keyboard_cdc_CdcPacketBody *body);
|
||||||
|
|
||||||
|
bool keyboard_proto_encode_cdc_frame(
|
||||||
|
uint32_t packet_type,
|
||||||
|
const struct keyboard_cdc_CdcPacketBody *body,
|
||||||
|
uint8_t *buffer,
|
||||||
|
size_t buffer_size,
|
||||||
|
size_t *encoded_size);
|
||||||
|
|
||||||
|
bool keyboard_proto_try_take_cdc_frame(
|
||||||
|
uint8_t *buffer,
|
||||||
|
size_t *buffer_size,
|
||||||
|
struct keyboard_cdc_CdcFrame *frame);
|
||||||
|
|
||||||
|
bool keyboard_proto_build_function_key_event_body(
|
||||||
|
uint16_t usage,
|
||||||
|
bool pressed,
|
||||||
|
struct keyboard_cdc_CdcPacketBody *body);
|
||||||
|
|
||||||
|
bool keyboard_proto_build_led_state_body(
|
||||||
|
uint8_t led_mask,
|
||||||
|
struct keyboard_cdc_CdcPacketBody *body);
|
||||||
|
|
||||||
|
bool keyboard_proto_handle_host_body(
|
||||||
|
const struct keyboard_cdc_CdcPacketBody *body,
|
||||||
|
enum keyboard_proto_transport transport,
|
||||||
|
keyboard_proto_send_body_fn send_fn,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
#endif /* KEYBOARD_PROTO_H__ */
|
||||||
5
prj.conf
5
prj.conf
@@ -68,6 +68,11 @@ CONFIG_BT_DIS_PNP_PID=0x0001
|
|||||||
|
|
||||||
CONFIG_USB_DEVICE_STACK_NEXT=y
|
CONFIG_USB_DEVICE_STACK_NEXT=y
|
||||||
CONFIG_USBD_HID_SUPPORT=y
|
CONFIG_USBD_HID_SUPPORT=y
|
||||||
|
CONFIG_SERIAL=y
|
||||||
|
CONFIG_UART_INTERRUPT_DRIVEN=y
|
||||||
|
CONFIG_UART_LINE_CTRL=y
|
||||||
|
CONFIG_USBD_CDC_ACM_CLASS=y
|
||||||
|
CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n
|
||||||
CONFIG_UDC_BUF_POOL_SIZE=8192
|
CONFIG_UDC_BUF_POOL_SIZE=8192
|
||||||
CONFIG_UDC_BUF_COUNT=32
|
CONFIG_UDC_BUF_COUNT=32
|
||||||
CONFIG_USBD_MAX_UDC_MSG=20
|
CONFIG_USBD_MAX_UDC_MSG=20
|
||||||
|
|||||||
375
src/modules/keyboard_proto.c
Normal file
375
src/modules/keyboard_proto.c
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
#include "keyboard_proto.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <zephyr/sys/byteorder.h>
|
||||||
|
|
||||||
|
#include "display_theme_event.h"
|
||||||
|
#include "function_bitmap_event.h"
|
||||||
|
#include "time_manager.h"
|
||||||
|
#include "time_sync_event.h"
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(keyboard_proto, LOG_LEVEL_INF);
|
||||||
|
|
||||||
|
#define KB_PROTO_BODY_TAG_HELLO_REQ 1U
|
||||||
|
#define KB_PROTO_BODY_TAG_HELLO_RSP 2U
|
||||||
|
#define KB_PROTO_BODY_TAG_BITMAP 3U
|
||||||
|
#define KB_PROTO_BODY_TAG_FUNCTION_KEY_EVENT 4U
|
||||||
|
#define KB_PROTO_BODY_TAG_LED_STATE 5U
|
||||||
|
#define KB_PROTO_BODY_TAG_TIME_SYNC 6U
|
||||||
|
#define KB_PROTO_BODY_TAG_THEME_RGB 7U
|
||||||
|
#define KB_PROTO_BODY_TAG_ACK 8U
|
||||||
|
#define KB_PROTO_BODY_TAG_ERROR 9U
|
||||||
|
|
||||||
|
#define KB_PROTO_PACKET_HELLO_REQ 0x01U
|
||||||
|
#define KB_PROTO_PACKET_HELLO_RSP 0x02U
|
||||||
|
#define KB_PROTO_PACKET_BITMAP 0x10U
|
||||||
|
#define KB_PROTO_PACKET_FUNCTION_KEY_EVENT 0x20U
|
||||||
|
#define KB_PROTO_PACKET_LED_STATE 0x21U
|
||||||
|
#define KB_PROTO_PACKET_TIME_SYNC 0x30U
|
||||||
|
#define KB_PROTO_PACKET_THEME_RGB 0x31U
|
||||||
|
#define KB_PROTO_PACKET_ACK 0x7EU
|
||||||
|
#define KB_PROTO_PACKET_ERROR 0x7FU
|
||||||
|
|
||||||
|
#define KB_PROTO_ERROR_UNKNOWN_TYPE 0x01U
|
||||||
|
#define KB_PROTO_ERROR_INVALID_LENGTH 0x02U
|
||||||
|
#define KB_PROTO_ERROR_INVALID_PARAM 0x03U
|
||||||
|
#define KB_PROTO_ERROR_NOT_READY 0x04U
|
||||||
|
|
||||||
|
static uint8_t keyboard_proto_calc_checksum(const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t checksum = 0U;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
checksum ^= data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyboard_proto_fill_payload(
|
||||||
|
pb_bytes_array_t *dest,
|
||||||
|
size_t max_size,
|
||||||
|
const uint8_t *src,
|
||||||
|
size_t src_len)
|
||||||
|
{
|
||||||
|
if ((dest == NULL) || (src == NULL) || (src_len > max_size)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest->size = src_len;
|
||||||
|
memcpy(dest->bytes, src, src_len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_proto_encode_body(
|
||||||
|
const struct keyboard_cdc_CdcPacketBody *body,
|
||||||
|
uint8_t *buffer,
|
||||||
|
size_t buffer_size,
|
||||||
|
size_t *encoded_size)
|
||||||
|
{
|
||||||
|
pb_ostream_t stream;
|
||||||
|
|
||||||
|
if ((body == NULL) || (buffer == NULL) || (encoded_size == NULL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = pb_ostream_from_buffer(buffer, buffer_size);
|
||||||
|
if (!pb_encode(&stream, keyboard_cdc_CdcPacketBody_fields, body)) {
|
||||||
|
LOG_WRN("pb_encode body failed: %s", PB_GET_ERROR(&stream));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*encoded_size = stream.bytes_written;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_proto_decode_body(
|
||||||
|
const uint8_t *buffer,
|
||||||
|
size_t buffer_size,
|
||||||
|
struct keyboard_cdc_CdcPacketBody *body)
|
||||||
|
{
|
||||||
|
pb_istream_t stream;
|
||||||
|
|
||||||
|
if ((buffer == NULL) || (body == NULL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||||
|
stream = pb_istream_from_buffer(buffer, buffer_size);
|
||||||
|
|
||||||
|
if (!pb_decode(&stream, keyboard_cdc_CdcPacketBody_fields, body)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream.bytes_left == 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_proto_encode_cdc_frame(
|
||||||
|
uint32_t packet_type,
|
||||||
|
const struct keyboard_cdc_CdcPacketBody *body,
|
||||||
|
uint8_t *buffer,
|
||||||
|
size_t buffer_size,
|
||||||
|
size_t *encoded_size)
|
||||||
|
{
|
||||||
|
struct keyboard_cdc_CdcFrame frame = keyboard_cdc_CdcFrame_init_zero;
|
||||||
|
uint8_t payload[KEYBOARD_PROTO_MAX_BODY_SIZE];
|
||||||
|
size_t payload_size = 0U;
|
||||||
|
pb_ostream_t stream;
|
||||||
|
uint8_t checksum_bytes[4U + KEYBOARD_PROTO_MAX_BODY_SIZE];
|
||||||
|
|
||||||
|
if ((buffer == NULL) || (encoded_size == NULL) || (body == NULL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keyboard_proto_encode_body(body, payload, sizeof(payload), &payload_size)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.head1 = 0xAAU;
|
||||||
|
frame.head2 = 0x55U;
|
||||||
|
frame.payload_length = payload_size;
|
||||||
|
frame.type = (keyboard_cdc_CdcPacketType)packet_type;
|
||||||
|
if (!keyboard_proto_fill_payload(&frame.payload, KEYBOARD_PROTO_MAX_BODY_SIZE,
|
||||||
|
payload, payload_size)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum_bytes[0] = (uint8_t)frame.head1;
|
||||||
|
checksum_bytes[1] = (uint8_t)frame.head2;
|
||||||
|
checksum_bytes[2] = (uint8_t)frame.payload_length;
|
||||||
|
checksum_bytes[3] = (uint8_t)packet_type;
|
||||||
|
memcpy(&checksum_bytes[4], payload, payload_size);
|
||||||
|
frame.checksum = keyboard_proto_calc_checksum(checksum_bytes,
|
||||||
|
4U + payload_size);
|
||||||
|
|
||||||
|
stream = pb_ostream_from_buffer(buffer, buffer_size);
|
||||||
|
if (!pb_encode(&stream, keyboard_cdc_CdcFrame_fields, &frame)) {
|
||||||
|
LOG_WRN("pb_encode frame failed: %s", PB_GET_ERROR(&stream));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*encoded_size = stream.bytes_written;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyboard_proto_validate_frame(const struct keyboard_cdc_CdcFrame *frame)
|
||||||
|
{
|
||||||
|
uint8_t checksum_bytes[4U + KEYBOARD_PROTO_MAX_BODY_SIZE];
|
||||||
|
uint8_t checksum;
|
||||||
|
|
||||||
|
if ((frame == NULL) ||
|
||||||
|
(frame->head1 != 0xAAU) ||
|
||||||
|
(frame->head2 != 0x55U) ||
|
||||||
|
(frame->payload.size != frame->payload_length) ||
|
||||||
|
(frame->payload.size > KEYBOARD_PROTO_MAX_BODY_SIZE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum_bytes[0] = (uint8_t)frame->head1;
|
||||||
|
checksum_bytes[1] = (uint8_t)frame->head2;
|
||||||
|
checksum_bytes[2] = (uint8_t)frame->payload_length;
|
||||||
|
checksum_bytes[3] = (uint8_t)frame->type;
|
||||||
|
memcpy(&checksum_bytes[4], frame->payload.bytes, frame->payload.size);
|
||||||
|
checksum = keyboard_proto_calc_checksum(checksum_bytes,
|
||||||
|
4U + frame->payload.size);
|
||||||
|
|
||||||
|
return checksum == (uint8_t)frame->checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_proto_try_take_cdc_frame(
|
||||||
|
uint8_t *buffer,
|
||||||
|
size_t *buffer_size,
|
||||||
|
struct keyboard_cdc_CdcFrame *frame)
|
||||||
|
{
|
||||||
|
for (size_t candidate_len = 1U; candidate_len <= *buffer_size; ++candidate_len) {
|
||||||
|
struct keyboard_cdc_CdcFrame candidate = keyboard_cdc_CdcFrame_init_zero;
|
||||||
|
pb_istream_t stream = pb_istream_from_buffer(buffer, candidate_len);
|
||||||
|
|
||||||
|
if (!pb_decode(&stream, keyboard_cdc_CdcFrame_fields, &candidate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((stream.bytes_left != 0U) ||
|
||||||
|
!keyboard_proto_validate_frame(&candidate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*frame = candidate;
|
||||||
|
memmove(buffer, buffer + candidate_len, *buffer_size - candidate_len);
|
||||||
|
*buffer_size -= candidate_len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyboard_proto_build_hello_rsp_body(
|
||||||
|
struct keyboard_cdc_CdcPacketBody *body)
|
||||||
|
{
|
||||||
|
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||||
|
body->which_body = KB_PROTO_BODY_TAG_HELLO_RSP;
|
||||||
|
body->body.hello_rsp.protocol_version = 1U;
|
||||||
|
body->body.hello_rsp.vendor_id = 0x1209U;
|
||||||
|
body->body.hello_rsp.product_id = 0x0001U;
|
||||||
|
body->body.hello_rsp.firmware_major = 1U;
|
||||||
|
body->body.hello_rsp.firmware_minor = 0U;
|
||||||
|
body->body.hello_rsp.capability_flags = BIT(0) | BIT(1) | BIT(2) | BIT(3);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyboard_proto_build_ack_body(
|
||||||
|
uint32_t acked_type,
|
||||||
|
struct keyboard_cdc_CdcPacketBody *body)
|
||||||
|
{
|
||||||
|
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||||
|
body->which_body = KB_PROTO_BODY_TAG_ACK;
|
||||||
|
body->body.ack.acked_type = acked_type;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyboard_proto_build_error_body(
|
||||||
|
uint32_t error_type,
|
||||||
|
uint32_t error_code,
|
||||||
|
struct keyboard_cdc_CdcPacketBody *body)
|
||||||
|
{
|
||||||
|
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||||
|
body->which_body = KB_PROTO_BODY_TAG_ERROR;
|
||||||
|
body->body.error.error_type = error_type;
|
||||||
|
body->body.error.error_code = (keyboard_cdc_ErrorCode)error_code;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_proto_build_function_key_event_body(
|
||||||
|
uint16_t usage,
|
||||||
|
bool pressed,
|
||||||
|
struct keyboard_cdc_CdcPacketBody *body)
|
||||||
|
{
|
||||||
|
if (body == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||||
|
body->which_body = KB_PROTO_BODY_TAG_FUNCTION_KEY_EVENT;
|
||||||
|
body->body.function_key_event.usage = usage;
|
||||||
|
body->body.function_key_event.action = pressed ? 1U : 0U;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_proto_build_led_state_body(
|
||||||
|
uint8_t led_mask,
|
||||||
|
struct keyboard_cdc_CdcPacketBody *body)
|
||||||
|
{
|
||||||
|
if (body == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||||
|
body->which_body = KB_PROTO_BODY_TAG_LED_STATE;
|
||||||
|
body->body.led_state.led_mask = led_mask;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyboard_proto_handle_bitmap(
|
||||||
|
const struct keyboard_cdc_CdcPacketBody *body,
|
||||||
|
keyboard_proto_send_body_fn send_fn,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct keyboard_cdc_CdcPacketBody response;
|
||||||
|
|
||||||
|
if (body->body.bitmap.usage_bitmap.size != KEYBOARD_PROTO_FUNCTION_BITMAP_SIZE) {
|
||||||
|
keyboard_proto_build_error_body(KB_PROTO_PACKET_BITMAP,
|
||||||
|
KB_PROTO_ERROR_INVALID_LENGTH,
|
||||||
|
&response);
|
||||||
|
return send_fn(&response, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function_bitmap_event_submit(body->body.bitmap.usage_bitmap.bytes,
|
||||||
|
body->body.bitmap.usage_bitmap.size);
|
||||||
|
keyboard_proto_build_ack_body(KB_PROTO_PACKET_BITMAP, &response);
|
||||||
|
return send_fn(&response, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyboard_proto_handle_time_sync(
|
||||||
|
const struct keyboard_cdc_CdcPacketBody *body,
|
||||||
|
enum keyboard_proto_transport transport,
|
||||||
|
keyboard_proto_send_body_fn send_fn,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct time_sync_update update;
|
||||||
|
struct keyboard_cdc_CdcPacketBody response;
|
||||||
|
|
||||||
|
if (!time_manager_is_ready()) {
|
||||||
|
keyboard_proto_build_error_body(KB_PROTO_PACKET_TIME_SYNC,
|
||||||
|
KB_PROTO_ERROR_NOT_READY,
|
||||||
|
&response);
|
||||||
|
return send_fn(&response, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body->body.time_sync.utc_ms == 0U) {
|
||||||
|
keyboard_proto_build_error_body(KB_PROTO_PACKET_TIME_SYNC,
|
||||||
|
KB_PROTO_ERROR_INVALID_PARAM,
|
||||||
|
&response);
|
||||||
|
return send_fn(&response, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
update.utc_ms = body->body.time_sync.utc_ms;
|
||||||
|
update.timezone_min = (int16_t)body->body.time_sync.timezone_min;
|
||||||
|
update.accuracy_ms = body->body.time_sync.accuracy_ms;
|
||||||
|
update.source = (transport == KEYBOARD_PROTO_TRANSPORT_CDC) ?
|
||||||
|
TIME_SYNC_SOURCE_USB : TIME_SYNC_SOURCE_BLE;
|
||||||
|
time_sync_event_submit(&update);
|
||||||
|
|
||||||
|
keyboard_proto_build_ack_body(KB_PROTO_PACKET_TIME_SYNC, &response);
|
||||||
|
return send_fn(&response, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyboard_proto_handle_theme_rgb(
|
||||||
|
const struct keyboard_cdc_CdcPacketBody *body,
|
||||||
|
keyboard_proto_send_body_fn send_fn,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct keyboard_cdc_CdcPacketBody response;
|
||||||
|
|
||||||
|
display_theme_event_submit((uint8_t)body->body.theme_rgb.red,
|
||||||
|
(uint8_t)body->body.theme_rgb.green,
|
||||||
|
(uint8_t)body->body.theme_rgb.blue);
|
||||||
|
|
||||||
|
keyboard_proto_build_ack_body(KB_PROTO_PACKET_THEME_RGB, &response);
|
||||||
|
return send_fn(&response, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_proto_handle_host_body(
|
||||||
|
const struct keyboard_cdc_CdcPacketBody *body,
|
||||||
|
enum keyboard_proto_transport transport,
|
||||||
|
keyboard_proto_send_body_fn send_fn,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct keyboard_cdc_CdcPacketBody response;
|
||||||
|
|
||||||
|
if ((body == NULL) || (send_fn == NULL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (body->which_body) {
|
||||||
|
case KB_PROTO_BODY_TAG_HELLO_REQ:
|
||||||
|
keyboard_proto_build_hello_rsp_body(&response);
|
||||||
|
return send_fn(&response, user_data);
|
||||||
|
|
||||||
|
case KB_PROTO_BODY_TAG_BITMAP:
|
||||||
|
return keyboard_proto_handle_bitmap(body, send_fn, user_data);
|
||||||
|
|
||||||
|
case KB_PROTO_BODY_TAG_TIME_SYNC:
|
||||||
|
return keyboard_proto_handle_time_sync(body, transport, send_fn, user_data);
|
||||||
|
|
||||||
|
case KB_PROTO_BODY_TAG_THEME_RGB:
|
||||||
|
return keyboard_proto_handle_theme_rgb(body, send_fn, user_data);
|
||||||
|
|
||||||
|
default:
|
||||||
|
keyboard_proto_build_error_body(KB_PROTO_PACKET_ERROR,
|
||||||
|
KB_PROTO_ERROR_UNKNOWN_TYPE,
|
||||||
|
&response);
|
||||||
|
return send_fn(&response, user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user