Finish firmware private transport stack
This commit is contained in:
@@ -17,6 +17,14 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
|
||||
project(new_kbd)
|
||||
|
||||
if(NOT c_std_11 IN_LIST CMAKE_C_COMPILE_FEATURES)
|
||||
list(APPEND CMAKE_C_COMPILE_FEATURES c_std_11)
|
||||
endif()
|
||||
|
||||
set(PROTOBUF_PROTOC_EXECUTABLE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/protoc_nanopb.bat"
|
||||
CACHE FILEPATH "Project-local protoc wrapper for nanopb" FORCE)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${ZEPHYR_BASE}/modules/nanopb)
|
||||
include(nanopb)
|
||||
|
||||
@@ -40,6 +48,8 @@ zephyr_nanopb_sources(app
|
||||
KeyBorad/proto/keyboard.proto
|
||||
)
|
||||
|
||||
target_link_libraries(app PRIVATE nanopb)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/events/battery_status_event.c
|
||||
|
||||
@@ -1,10 +1,210 @@
|
||||
#include "DRI/GATT/Dri_Gatt.h"
|
||||
|
||||
#include <QtBluetooth/QBluetoothAddress>
|
||||
#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
|
||||
#include <QtBluetooth/QBluetoothDeviceInfo>
|
||||
#include <QtBluetooth/QBluetoothUuid>
|
||||
#include <QtBluetooth/QLowEnergyCharacteristic>
|
||||
#include <QtBluetooth/QLowEnergyController>
|
||||
#include <QtBluetooth/QLowEnergyDescriptor>
|
||||
#include <QtBluetooth/QLowEnergyService>
|
||||
#include <QtCore/QEventLoop>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
struct Dri_Gatt_Struct_Context
|
||||
{
|
||||
QLowEnergyController* Controller = nullptr;
|
||||
QLowEnergyService* Service = nullptr;
|
||||
QLowEnergyCharacteristic RxCharacteristic;
|
||||
QLowEnergyCharacteristic TxCharacteristic;
|
||||
QList<QByteArray> ReadQueue;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const QBluetoothUuid kServiceUuid(
|
||||
QStringLiteral("0b7f6000-38d2-4f62-8f6f-36c4fd73a110"));
|
||||
const QBluetoothUuid kRxUuid(
|
||||
QStringLiteral("0b7f6001-38d2-4f62-8f6f-36c4fd73a110"));
|
||||
const QBluetoothUuid kTxUuid(
|
||||
QStringLiteral("0b7f6002-38d2-4f62-8f6f-36c4fd73a110"));
|
||||
|
||||
bool Dri_Gatt_Func_WaitForControllerConnected(QLowEnergyController* p_Controller)
|
||||
{
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
bool IsConnected = false;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(
|
||||
p_Controller,
|
||||
&QLowEnergyController::connected,
|
||||
&EventLoop,
|
||||
[&EventLoop, &IsConnected]()
|
||||
{
|
||||
IsConnected = true;
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
p_Controller,
|
||||
static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(
|
||||
&QLowEnergyController::error),
|
||||
&EventLoop,
|
||||
[&EventLoop]()
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
p_Controller->connectToDevice();
|
||||
TimeoutTimer.start(5000);
|
||||
EventLoop.exec();
|
||||
return IsConnected;
|
||||
}
|
||||
|
||||
bool Dri_Gatt_Func_WaitForServiceDiscovery(QLowEnergyController* p_Controller)
|
||||
{
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
bool IsFinished = false;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(
|
||||
p_Controller,
|
||||
&QLowEnergyController::discoveryFinished,
|
||||
&EventLoop,
|
||||
[&EventLoop, &IsFinished]()
|
||||
{
|
||||
IsFinished = true;
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
p_Controller,
|
||||
static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(
|
||||
&QLowEnergyController::error),
|
||||
&EventLoop,
|
||||
[&EventLoop]()
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
p_Controller->discoverServices();
|
||||
TimeoutTimer.start(5000);
|
||||
EventLoop.exec();
|
||||
return IsFinished;
|
||||
}
|
||||
|
||||
bool Dri_Gatt_Func_WaitForServiceDetails(QLowEnergyService* p_Service)
|
||||
{
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
bool IsDiscovered = false;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(
|
||||
p_Service,
|
||||
&QLowEnergyService::stateChanged,
|
||||
&EventLoop,
|
||||
[&EventLoop, &IsDiscovered](QLowEnergyService::ServiceState State)
|
||||
{
|
||||
if (State == QLowEnergyService::ServiceDiscovered)
|
||||
{
|
||||
IsDiscovered = true;
|
||||
EventLoop.quit();
|
||||
}
|
||||
});
|
||||
QObject::connect(
|
||||
p_Service,
|
||||
static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(
|
||||
&QLowEnergyService::error),
|
||||
&EventLoop,
|
||||
[&EventLoop]()
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
p_Service->discoverDetails();
|
||||
TimeoutTimer.start(5000);
|
||||
EventLoop.exec();
|
||||
return IsDiscovered;
|
||||
}
|
||||
|
||||
bool Dri_Gatt_Func_EnableNotify(
|
||||
Dri_Gatt_Struct_Context* p_Context,
|
||||
const QLowEnergyCharacteristic& Characteristic)
|
||||
{
|
||||
if (!Characteristic.isValid() || (p_Context == nullptr) || (p_Context->Service == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const QLowEnergyDescriptor Cccd =
|
||||
Characteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
|
||||
if (!Cccd.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
bool IsWritten = false;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(
|
||||
p_Context->Service,
|
||||
&QLowEnergyService::descriptorWritten,
|
||||
&EventLoop,
|
||||
[&EventLoop, &IsWritten, Cccd](const QLowEnergyDescriptor& Descriptor, const QByteArray&)
|
||||
{
|
||||
if (Descriptor == Cccd)
|
||||
{
|
||||
IsWritten = true;
|
||||
EventLoop.quit();
|
||||
}
|
||||
});
|
||||
QObject::connect(
|
||||
p_Context->Service,
|
||||
static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(
|
||||
&QLowEnergyService::error),
|
||||
&EventLoop,
|
||||
[&EventLoop]()
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
p_Context->Service->writeDescriptor(Cccd, QByteArray::fromHex("0100"));
|
||||
TimeoutTimer.start(3000);
|
||||
EventLoop.exec();
|
||||
return IsWritten;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QVector<Dri_Gatt_Struct_PortInfo> Dri_Gatt_Enum()
|
||||
{
|
||||
QVector<Dri_Gatt_Struct_PortInfo> PortList;
|
||||
@@ -79,6 +279,17 @@ void Dri_Gatt_Close(Dri_Gatt_Struct_Port* p_Port)
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_Port->p_Context != nullptr)
|
||||
{
|
||||
if (p_Port->p_Context->Controller != nullptr)
|
||||
{
|
||||
p_Port->p_Context->Controller->disconnectFromDevice();
|
||||
delete p_Port->p_Context->Controller;
|
||||
}
|
||||
|
||||
delete p_Port->p_Context;
|
||||
}
|
||||
|
||||
*p_Port = Dri_Gatt_Struct_Port();
|
||||
}
|
||||
|
||||
@@ -93,38 +304,183 @@ bool Dri_Gatt_Open(
|
||||
}
|
||||
|
||||
Dri_Gatt_Close(p_Port);
|
||||
|
||||
Dri_Gatt_Struct_Context* const p_Context = new Dri_Gatt_Struct_Context();
|
||||
p_Context->Controller = new QLowEnergyController(QBluetoothAddress(DeviceAddress.trimmed()));
|
||||
|
||||
if (!Dri_Gatt_Func_WaitForControllerConnected(p_Context->Controller))
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Dri_Gatt_Func_WaitForServiceDiscovery(p_Context->Controller) ||
|
||||
!p_Context->Controller->services().contains(kServiceUuid))
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
p_Context->Service = p_Context->Controller->createServiceObject(kServiceUuid, p_Context->Controller);
|
||||
if (p_Context->Service == nullptr)
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Dri_Gatt_Func_WaitForServiceDetails(p_Context->Service))
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
p_Context->RxCharacteristic = p_Context->Service->characteristic(kRxUuid);
|
||||
p_Context->TxCharacteristic = p_Context->Service->characteristic(kTxUuid);
|
||||
|
||||
if (!p_Context->RxCharacteristic.isValid() || !p_Context->TxCharacteristic.isValid())
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
QObject::connect(
|
||||
p_Context->Service,
|
||||
&QLowEnergyService::characteristicChanged,
|
||||
[p_Context](const QLowEnergyCharacteristic& Characteristic, const QByteArray& Value)
|
||||
{
|
||||
if (Characteristic == p_Context->TxCharacteristic)
|
||||
{
|
||||
p_Context->ReadQueue.append(Value);
|
||||
}
|
||||
});
|
||||
|
||||
if (!Dri_Gatt_Func_EnableNotify(p_Context, p_Context->TxCharacteristic))
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
p_Port->p_Context = p_Context;
|
||||
p_Port->IsOpened = true;
|
||||
p_Port->DeviceAddress = DeviceAddress.trimmed();
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dri_Gatt_ReadBytes(
|
||||
Dri_Gatt_Struct_Port* p_Port,
|
||||
QByteArray* p_ByteArray,
|
||||
int)
|
||||
int TimeoutMs)
|
||||
{
|
||||
if (p_ByteArray != nullptr)
|
||||
{
|
||||
p_ByteArray->clear();
|
||||
}
|
||||
|
||||
if ((p_Port == nullptr) || (p_ByteArray == nullptr) || !p_Port->IsOpened)
|
||||
if ((p_Port == nullptr) || (p_ByteArray == nullptr) ||
|
||||
!p_Port->IsOpened || (p_Port->p_Context == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_Port->p_Context->ReadQueue.isEmpty() && (TimeoutMs > 0))
|
||||
{
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
QObject::connect(
|
||||
p_Port->p_Context->Service,
|
||||
&QLowEnergyService::characteristicChanged,
|
||||
&EventLoop,
|
||||
[&EventLoop](const QLowEnergyCharacteristic&, const QByteArray&)
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
TimeoutTimer.start(TimeoutMs);
|
||||
EventLoop.exec();
|
||||
}
|
||||
|
||||
if (p_Port->p_Context->ReadQueue.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*p_ByteArray = p_Port->p_Context->ReadQueue.takeFirst();
|
||||
return !p_ByteArray->isEmpty();
|
||||
}
|
||||
|
||||
bool Dri_Gatt_WriteBytes(
|
||||
Dri_Gatt_Struct_Port* p_Port,
|
||||
const QByteArray& ByteArray,
|
||||
int)
|
||||
int TimeoutMs)
|
||||
{
|
||||
if ((p_Port == nullptr) || !p_Port->IsOpened)
|
||||
if ((p_Port == nullptr) || !p_Port->IsOpened || (p_Port->p_Context == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !ByteArray.isEmpty();
|
||||
if (ByteArray.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
bool IsWritten = false;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(
|
||||
p_Port->p_Context->Service,
|
||||
&QLowEnergyService::characteristicWritten,
|
||||
&EventLoop,
|
||||
[&EventLoop, &IsWritten, p_Port](const QLowEnergyCharacteristic& Characteristic, const QByteArray&)
|
||||
{
|
||||
if (Characteristic == p_Port->p_Context->RxCharacteristic)
|
||||
{
|
||||
IsWritten = true;
|
||||
EventLoop.quit();
|
||||
}
|
||||
});
|
||||
QObject::connect(
|
||||
p_Port->p_Context->Service,
|
||||
static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(
|
||||
&QLowEnergyService::error),
|
||||
&EventLoop,
|
||||
[&EventLoop]()
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
p_Port->p_Context->Service->writeCharacteristic(
|
||||
p_Port->p_Context->RxCharacteristic,
|
||||
ByteArray,
|
||||
QLowEnergyService::WriteWithResponse);
|
||||
TimeoutTimer.start(TimeoutMs > 0 ? TimeoutMs : 3000);
|
||||
EventLoop.exec();
|
||||
|
||||
return IsWritten;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVector>
|
||||
|
||||
struct Dri_Gatt_Struct_Context;
|
||||
|
||||
struct Dri_Gatt_Struct_PortInfo
|
||||
{
|
||||
QString DeviceName;
|
||||
@@ -17,7 +19,9 @@ struct Dri_Gatt_Struct_OpenConfig
|
||||
struct Dri_Gatt_Struct_Port
|
||||
{
|
||||
bool IsOpened = false;
|
||||
QString DeviceName;
|
||||
QString DeviceAddress;
|
||||
Dri_Gatt_Struct_Context* p_Context = nullptr;
|
||||
};
|
||||
|
||||
QVector<Dri_Gatt_Struct_PortInfo> Dri_Gatt_Enum();
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CdcFrame.payload max_size:64
|
||||
Bitmap.usage_bitmap max_size:29
|
||||
keyboard.cdc.CdcFrame.payload max_size:64
|
||||
keyboard.cdc.Bitmap.usage_bitmap max_size:29
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
};
|
||||
};
|
||||
|
||||
&zephyr_udc0 {
|
||||
&usbd {
|
||||
cdc_acm_uart0: cdc_acm_uart0 {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
label = "new_kbd CDC ACM";
|
||||
|
||||
@@ -80,6 +80,8 @@ 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
|
||||
- force nanopb generation to use the NCS toolchain Python instead of the
|
||||
broken Windows app alias
|
||||
- add protocol helper functions for:
|
||||
- body encode/decode
|
||||
- CDC frame encode
|
||||
|
||||
@@ -62,7 +62,30 @@ Implemented behavior:
|
||||
- collect device name and address
|
||||
- filter to `LowEnergyCoreConfiguration`
|
||||
- stop after a short timeout
|
||||
### Node 3: CDC transport cleanup
|
||||
|
||||
### Node 3: GATT open and raw byte transport
|
||||
|
||||
Files updated in this step:
|
||||
|
||||
- `KeyBorad/KeyBorad/DRI/GATT/Dri_Gatt.h`
|
||||
- `KeyBorad/KeyBorad/DRI/GATT/Dri_Gatt.cpp`
|
||||
|
||||
Design notes:
|
||||
|
||||
- keep transport-only responsibility
|
||||
- use Qt Bluetooth instead of custom Win32 BLE code
|
||||
- connect, discover service, subscribe, then pass raw bytes upward
|
||||
|
||||
Implemented behavior:
|
||||
|
||||
- connect to a BLE device by address
|
||||
- discover the target custom service
|
||||
- resolve RX/TX characteristics
|
||||
- enable notify on TX
|
||||
- queue raw bytes from TX notify
|
||||
- write raw bytes to RX characteristic
|
||||
|
||||
### Node 4: CDC transport cleanup
|
||||
|
||||
Files updated in this step:
|
||||
|
||||
|
||||
@@ -20,11 +20,11 @@ enum keyboard_proto_transport {
|
||||
};
|
||||
|
||||
typedef bool (*keyboard_proto_send_body_fn)(
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
void *user_data);
|
||||
|
||||
bool keyboard_proto_encode_body(
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
uint8_t *buffer,
|
||||
size_t buffer_size,
|
||||
size_t *encoded_size);
|
||||
@@ -32,11 +32,11 @@ bool keyboard_proto_encode_body(
|
||||
bool keyboard_proto_decode_body(
|
||||
const uint8_t *buffer,
|
||||
size_t buffer_size,
|
||||
struct keyboard_cdc_CdcPacketBody *body);
|
||||
keyboard_cdc_CdcPacketBody *body);
|
||||
|
||||
bool keyboard_proto_encode_cdc_frame(
|
||||
uint32_t packet_type,
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
uint8_t *buffer,
|
||||
size_t buffer_size,
|
||||
size_t *encoded_size);
|
||||
@@ -44,19 +44,19 @@ bool keyboard_proto_encode_cdc_frame(
|
||||
bool keyboard_proto_try_take_cdc_frame(
|
||||
uint8_t *buffer,
|
||||
size_t *buffer_size,
|
||||
struct keyboard_cdc_CdcFrame *frame);
|
||||
keyboard_cdc_CdcFrame *frame);
|
||||
|
||||
bool keyboard_proto_build_function_key_event_body(
|
||||
uint16_t usage,
|
||||
bool pressed,
|
||||
struct keyboard_cdc_CdcPacketBody *body);
|
||||
keyboard_cdc_CdcPacketBody *body);
|
||||
|
||||
bool keyboard_proto_build_led_state_body(
|
||||
uint8_t led_mask,
|
||||
struct keyboard_cdc_CdcPacketBody *body);
|
||||
keyboard_cdc_CdcPacketBody *body);
|
||||
|
||||
bool keyboard_proto_handle_host_body(
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
enum keyboard_proto_transport transport,
|
||||
keyboard_proto_send_body_fn send_fn,
|
||||
void *user_data);
|
||||
|
||||
5
scripts/protoc_nanopb.bat
Normal file
5
scripts/protoc_nanopb.bat
Normal file
@@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
setlocal
|
||||
set PATH=C:\ncs\toolchains\fd21892d0f\opt\bin;%PATH%
|
||||
"C:\ncs\toolchains\fd21892d0f\opt\bin\python.exe" "C:\ncs\v3.2.4\modules\lib\nanopb\generator\protoc" %*
|
||||
exit /b %ERRORLEVEL%
|
||||
@@ -32,6 +32,9 @@ struct ble_gatt_proto_ctx {
|
||||
};
|
||||
|
||||
static struct ble_gatt_proto_ctx g_ble_proto;
|
||||
static bool ble_gatt_proto_send_body(
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
void *user_data);
|
||||
|
||||
static void ble_gatt_proto_ccc_changed(
|
||||
const struct bt_gatt_attr *attr,
|
||||
@@ -50,7 +53,7 @@ static ssize_t ble_gatt_proto_rx_write(
|
||||
uint16_t offset,
|
||||
uint8_t flags)
|
||||
{
|
||||
struct keyboard_cdc_CdcPacketBody body;
|
||||
keyboard_cdc_CdcPacketBody body;
|
||||
|
||||
ARG_UNUSED(conn);
|
||||
ARG_UNUSED(attr);
|
||||
@@ -96,7 +99,7 @@ BT_GATT_SERVICE_DEFINE(
|
||||
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
|
||||
|
||||
static bool ble_gatt_proto_send_body(
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
void *user_data)
|
||||
{
|
||||
uint8_t buffer[KEYBOARD_PROTO_MAX_BODY_SIZE];
|
||||
@@ -128,7 +131,7 @@ static bool handle_module_state_event(const struct module_state_event *event)
|
||||
|
||||
static bool handle_function_key_event(const struct function_key_event *event)
|
||||
{
|
||||
struct keyboard_cdc_CdcPacketBody body;
|
||||
keyboard_cdc_CdcPacketBody body;
|
||||
|
||||
if (!keyboard_proto_build_function_key_event_body(event->usage,
|
||||
event->pressed,
|
||||
@@ -142,7 +145,7 @@ static bool handle_function_key_event(const struct function_key_event *event)
|
||||
|
||||
static bool handle_keyboard_led_event(const struct keyboard_led_event *event)
|
||||
{
|
||||
struct keyboard_cdc_CdcPacketBody body;
|
||||
keyboard_cdc_CdcPacketBody body;
|
||||
|
||||
if (!keyboard_proto_build_led_state_body(
|
||||
keyboard_led_event_get_mask(event),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#include "display_theme_event.h"
|
||||
#include "function_bitmap_event.h"
|
||||
@@ -48,23 +49,8 @@ static uint8_t keyboard_proto_calc_checksum(const uint8_t *data, size_t len)
|
||||
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,
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
uint8_t *buffer,
|
||||
size_t buffer_size,
|
||||
size_t *encoded_size)
|
||||
@@ -88,7 +74,7 @@ bool keyboard_proto_encode_body(
|
||||
bool keyboard_proto_decode_body(
|
||||
const uint8_t *buffer,
|
||||
size_t buffer_size,
|
||||
struct keyboard_cdc_CdcPacketBody *body)
|
||||
keyboard_cdc_CdcPacketBody *body)
|
||||
{
|
||||
pb_istream_t stream;
|
||||
|
||||
@@ -96,7 +82,10 @@ bool keyboard_proto_decode_body(
|
||||
return false;
|
||||
}
|
||||
|
||||
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
{
|
||||
keyboard_cdc_CdcPacketBody empty = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
*body = empty;
|
||||
}
|
||||
stream = pb_istream_from_buffer(buffer, buffer_size);
|
||||
|
||||
if (!pb_decode(&stream, keyboard_cdc_CdcPacketBody_fields, body)) {
|
||||
@@ -108,12 +97,12 @@ bool keyboard_proto_decode_body(
|
||||
|
||||
bool keyboard_proto_encode_cdc_frame(
|
||||
uint32_t packet_type,
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
const 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;
|
||||
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;
|
||||
@@ -131,10 +120,11 @@ bool keyboard_proto_encode_cdc_frame(
|
||||
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)) {
|
||||
if (payload_size > KEYBOARD_PROTO_MAX_BODY_SIZE) {
|
||||
return false;
|
||||
}
|
||||
frame.payload.size = payload_size;
|
||||
memcpy(frame.payload.bytes, payload, payload_size);
|
||||
|
||||
checksum_bytes[0] = (uint8_t)frame.head1;
|
||||
checksum_bytes[1] = (uint8_t)frame.head2;
|
||||
@@ -154,7 +144,7 @@ bool keyboard_proto_encode_cdc_frame(
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool keyboard_proto_validate_frame(const struct keyboard_cdc_CdcFrame *frame)
|
||||
static bool keyboard_proto_validate_frame(const keyboard_cdc_CdcFrame *frame)
|
||||
{
|
||||
uint8_t checksum_bytes[4U + KEYBOARD_PROTO_MAX_BODY_SIZE];
|
||||
uint8_t checksum;
|
||||
@@ -181,10 +171,10 @@ static bool keyboard_proto_validate_frame(const struct keyboard_cdc_CdcFrame *fr
|
||||
bool keyboard_proto_try_take_cdc_frame(
|
||||
uint8_t *buffer,
|
||||
size_t *buffer_size,
|
||||
struct keyboard_cdc_CdcFrame *frame)
|
||||
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;
|
||||
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)) {
|
||||
@@ -206,9 +196,12 @@ bool keyboard_proto_try_take_cdc_frame(
|
||||
}
|
||||
|
||||
static bool keyboard_proto_build_hello_rsp_body(
|
||||
struct keyboard_cdc_CdcPacketBody *body)
|
||||
keyboard_cdc_CdcPacketBody *body)
|
||||
{
|
||||
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
{
|
||||
keyboard_cdc_CdcPacketBody empty = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
*body = empty;
|
||||
}
|
||||
body->which_body = KB_PROTO_BODY_TAG_HELLO_RSP;
|
||||
body->body.hello_rsp.protocol_version = 1U;
|
||||
body->body.hello_rsp.vendor_id = 0x1209U;
|
||||
@@ -221,9 +214,12 @@ static bool keyboard_proto_build_hello_rsp_body(
|
||||
|
||||
static bool keyboard_proto_build_ack_body(
|
||||
uint32_t acked_type,
|
||||
struct keyboard_cdc_CdcPacketBody *body)
|
||||
keyboard_cdc_CdcPacketBody *body)
|
||||
{
|
||||
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
{
|
||||
keyboard_cdc_CdcPacketBody empty = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
*body = empty;
|
||||
}
|
||||
body->which_body = KB_PROTO_BODY_TAG_ACK;
|
||||
body->body.ack.acked_type = acked_type;
|
||||
return true;
|
||||
@@ -232,9 +228,12 @@ static bool keyboard_proto_build_ack_body(
|
||||
static bool keyboard_proto_build_error_body(
|
||||
uint32_t error_type,
|
||||
uint32_t error_code,
|
||||
struct keyboard_cdc_CdcPacketBody *body)
|
||||
keyboard_cdc_CdcPacketBody *body)
|
||||
{
|
||||
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
{
|
||||
keyboard_cdc_CdcPacketBody empty = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
*body = empty;
|
||||
}
|
||||
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;
|
||||
@@ -244,13 +243,16 @@ static bool keyboard_proto_build_error_body(
|
||||
bool keyboard_proto_build_function_key_event_body(
|
||||
uint16_t usage,
|
||||
bool pressed,
|
||||
struct keyboard_cdc_CdcPacketBody *body)
|
||||
keyboard_cdc_CdcPacketBody *body)
|
||||
{
|
||||
if (body == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
{
|
||||
keyboard_cdc_CdcPacketBody empty = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
*body = empty;
|
||||
}
|
||||
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;
|
||||
@@ -259,24 +261,27 @@ bool keyboard_proto_build_function_key_event_body(
|
||||
|
||||
bool keyboard_proto_build_led_state_body(
|
||||
uint8_t led_mask,
|
||||
struct keyboard_cdc_CdcPacketBody *body)
|
||||
keyboard_cdc_CdcPacketBody *body)
|
||||
{
|
||||
if (body == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*body = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
{
|
||||
keyboard_cdc_CdcPacketBody empty = keyboard_cdc_CdcPacketBody_init_zero;
|
||||
*body = empty;
|
||||
}
|
||||
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,
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
keyboard_proto_send_body_fn send_fn,
|
||||
void *user_data)
|
||||
{
|
||||
struct keyboard_cdc_CdcPacketBody response;
|
||||
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,
|
||||
@@ -292,13 +297,13 @@ static bool keyboard_proto_handle_bitmap(
|
||||
}
|
||||
|
||||
static bool keyboard_proto_handle_time_sync(
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
const 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;
|
||||
keyboard_cdc_CdcPacketBody response;
|
||||
|
||||
if (!time_manager_is_ready()) {
|
||||
keyboard_proto_build_error_body(KB_PROTO_PACKET_TIME_SYNC,
|
||||
@@ -326,11 +331,11 @@ static bool keyboard_proto_handle_time_sync(
|
||||
}
|
||||
|
||||
static bool keyboard_proto_handle_theme_rgb(
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
keyboard_proto_send_body_fn send_fn,
|
||||
void *user_data)
|
||||
{
|
||||
struct keyboard_cdc_CdcPacketBody response;
|
||||
keyboard_cdc_CdcPacketBody response;
|
||||
|
||||
display_theme_event_submit((uint8_t)body->body.theme_rgb.red,
|
||||
(uint8_t)body->body.theme_rgb.green,
|
||||
@@ -341,12 +346,12 @@ static bool keyboard_proto_handle_theme_rgb(
|
||||
}
|
||||
|
||||
bool keyboard_proto_handle_host_body(
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
enum keyboard_proto_transport transport,
|
||||
keyboard_proto_send_body_fn send_fn,
|
||||
void *user_data)
|
||||
{
|
||||
struct keyboard_cdc_CdcPacketBody response;
|
||||
keyboard_cdc_CdcPacketBody response;
|
||||
|
||||
if ((body == NULL) || (send_fn == NULL)) {
|
||||
return false;
|
||||
|
||||
@@ -32,7 +32,7 @@ static struct usb_cdc_proto_ctx g_usb_cdc = {
|
||||
};
|
||||
|
||||
static uint32_t keyboard_proto_packet_type_from_body(
|
||||
const struct keyboard_cdc_CdcPacketBody *body)
|
||||
const keyboard_cdc_CdcPacketBody *body)
|
||||
{
|
||||
switch (body->which_body) {
|
||||
case 1U:
|
||||
@@ -59,7 +59,7 @@ static uint32_t keyboard_proto_packet_type_from_body(
|
||||
}
|
||||
|
||||
static bool usb_cdc_proto_send_body(
|
||||
const struct keyboard_cdc_CdcPacketBody *body,
|
||||
const keyboard_cdc_CdcPacketBody *body,
|
||||
void *user_data)
|
||||
{
|
||||
struct usb_cdc_proto_ctx *ctx = user_data;
|
||||
@@ -88,8 +88,8 @@ static bool usb_cdc_proto_send_body(
|
||||
|
||||
static void usb_cdc_proto_process_rx_work(struct k_work *work)
|
||||
{
|
||||
struct keyboard_cdc_CdcFrame frame;
|
||||
struct keyboard_cdc_CdcPacketBody body;
|
||||
keyboard_cdc_CdcFrame frame;
|
||||
keyboard_cdc_CdcPacketBody body;
|
||||
|
||||
ARG_UNUSED(work);
|
||||
|
||||
@@ -162,7 +162,7 @@ static bool handle_module_state_event(const struct module_state_event *event)
|
||||
|
||||
static bool handle_function_key_event(const struct function_key_event *event)
|
||||
{
|
||||
struct keyboard_cdc_CdcPacketBody body;
|
||||
keyboard_cdc_CdcPacketBody body;
|
||||
|
||||
if (!g_usb_cdc.ready) {
|
||||
return false;
|
||||
@@ -180,7 +180,7 @@ static bool handle_function_key_event(const struct function_key_event *event)
|
||||
|
||||
static bool handle_keyboard_led_event(const struct keyboard_led_event *event)
|
||||
{
|
||||
struct keyboard_cdc_CdcPacketBody body;
|
||||
keyboard_cdc_CdcPacketBody body;
|
||||
|
||||
if (!g_usb_cdc.ready) {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user