From 30f1af1a8cb464c92ab47ad07e4eb7598972fe45 Mon Sep 17 00:00:00 2001 From: stli Date: Sat, 11 Apr 2026 13:00:04 +0800 Subject: [PATCH] Add firmware GATT protocol module --- docs/firmware_proto_transport.md | 26 ++++ src/modules/ble_gatt_proto_module.c | 178 ++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 src/modules/ble_gatt_proto_module.c diff --git a/docs/firmware_proto_transport.md b/docs/firmware_proto_transport.md index b45ff96..e38af58 100644 --- a/docs/firmware_proto_transport.md +++ b/docs/firmware_proto_transport.md @@ -119,3 +119,29 @@ Implemented behavior: - `Error` - `FunctionKeyEvent` - `LedState` + +### Node 5: GATT transport module + +Files added in this step: + +- `src/modules/ble_gatt_proto_module.c` + +Design notes: + +- define one custom service with one RX write characteristic and one TX notify + characteristic +- keep GATT transport byte-oriented by sending raw `CdcPacketBody` bytes +- reuse the same host command dispatcher as CDC + +Implemented behavior: + +- register the custom GATT service +- accept host writes on RX +- decode `CdcPacketBody` +- dispatch host messages +- notify: + - `HelloRsp` + - `Ack` + - `Error` + - `FunctionKeyEvent` + - `LedState` diff --git a/src/modules/ble_gatt_proto_module.c b/src/modules/ble_gatt_proto_module.c new file mode 100644 index 0000000..1a6d557 --- /dev/null +++ b/src/modules/ble_gatt_proto_module.c @@ -0,0 +1,178 @@ +#include + +#include + +#define MODULE ble_gatt_proto +#include + +#include "function_key_event.h" +#include "keyboard_led_event.h" +#include "keyboard_proto.h" + +#include +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +#define BT_UUID_KEYBOARD_PROTO_SERVICE_VAL \ + BT_UUID_128_ENCODE(0x0b7f6000, 0x38d2, 0x4f62, 0x8f6f, 0x36c4fd73a110) +#define BT_UUID_KEYBOARD_PROTO_RX_VAL \ + BT_UUID_128_ENCODE(0x0b7f6001, 0x38d2, 0x4f62, 0x8f6f, 0x36c4fd73a110) +#define BT_UUID_KEYBOARD_PROTO_TX_VAL \ + BT_UUID_128_ENCODE(0x0b7f6002, 0x38d2, 0x4f62, 0x8f6f, 0x36c4fd73a110) + +#define BT_UUID_KEYBOARD_PROTO_SERVICE \ + BT_UUID_DECLARE_128(BT_UUID_KEYBOARD_PROTO_SERVICE_VAL) +#define BT_UUID_KEYBOARD_PROTO_RX \ + BT_UUID_DECLARE_128(BT_UUID_KEYBOARD_PROTO_RX_VAL) +#define BT_UUID_KEYBOARD_PROTO_TX \ + BT_UUID_DECLARE_128(BT_UUID_KEYBOARD_PROTO_TX_VAL) + +struct ble_gatt_proto_ctx { + bool ready; + bool notify_enabled; +}; + +static struct ble_gatt_proto_ctx g_ble_proto; + +static void ble_gatt_proto_ccc_changed( + const struct bt_gatt_attr *attr, + uint16_t value) +{ + ARG_UNUSED(attr); + + g_ble_proto.notify_enabled = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t ble_gatt_proto_rx_write( + struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, + uint16_t len, + uint16_t offset, + uint8_t flags) +{ + struct keyboard_cdc_CdcPacketBody body; + + ARG_UNUSED(conn); + ARG_UNUSED(attr); + ARG_UNUSED(flags); + + if (!g_ble_proto.ready) { + return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); + } + + if (offset != 0U) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + if (!keyboard_proto_decode_body(buf, len, &body)) { + return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); + } + + (void)keyboard_proto_handle_host_body( + &body, + KEYBOARD_PROTO_TRANSPORT_GATT, + ble_gatt_proto_send_body, + NULL); + + return len; +} + +BT_GATT_SERVICE_DEFINE( + keyboard_proto_svc, + BT_GATT_PRIMARY_SERVICE(BT_UUID_KEYBOARD_PROTO_SERVICE), + BT_GATT_CHARACTERISTIC(BT_UUID_KEYBOARD_PROTO_RX, + BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP, + BT_GATT_PERM_WRITE, + NULL, + ble_gatt_proto_rx_write, + NULL), + BT_GATT_CHARACTERISTIC(BT_UUID_KEYBOARD_PROTO_TX, + BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_NONE, + NULL, + NULL, + NULL), + BT_GATT_CCC(ble_gatt_proto_ccc_changed, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)); + +static bool ble_gatt_proto_send_body( + const struct keyboard_cdc_CdcPacketBody *body, + void *user_data) +{ + uint8_t buffer[KEYBOARD_PROTO_MAX_BODY_SIZE]; + size_t size = 0U; + + ARG_UNUSED(user_data); + + if (!g_ble_proto.ready || !g_ble_proto.notify_enabled) { + return false; + } + + if (!keyboard_proto_encode_body(body, buffer, sizeof(buffer), &size)) { + return false; + } + + return bt_gatt_notify(NULL, &keyboard_proto_svc.attrs[4], buffer, size) == 0; +} + +static bool handle_module_state_event(const struct module_state_event *event) +{ + if (!check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { + return false; + } + + g_ble_proto.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 (!keyboard_proto_build_function_key_event_body(event->usage, + event->pressed, + &body)) { + return false; + } + + (void)ble_gatt_proto_send_body(&body, NULL); + return false; +} + +static bool handle_keyboard_led_event(const struct keyboard_led_event *event) +{ + struct keyboard_cdc_CdcPacketBody body; + + if (!keyboard_proto_build_led_state_body( + keyboard_led_event_get_mask(event), + &body)) { + return false; + } + + (void)ble_gatt_proto_send_body(&body, NULL); + 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);