unleashed-firmware/lib/ble_profile/extra_services/hid_service.c
hedger 03196fa110
cleanup of various warnings from clangd (#3682)
* cleanup of various warnings from clangs
* lfrfid_debug: cast fixes
* subghz: binraw: round->roundf
* furi: thread: updated internal stack size variable to size_t
* github: fail faster on unsuccessful build
* unit_tests: double trouble
2024-06-03 14:43:23 +01:00

320 lines
12 KiB
C

#include "hid_service.h"
#include "app_common.h" // IWYU pragma: keep
#include <ble/ble.h>
#include <furi_ble/event_dispatcher.h>
#include <furi_ble/gatt.h>
#include <furi.h>
#include <stdint.h>
#define TAG "BleHid"
#define BLE_SVC_HID_REPORT_MAP_MAX_LEN (255)
#define BLE_SVC_HID_REPORT_MAX_LEN (255)
#define BLE_SVC_HID_REPORT_REF_LEN (2)
#define BLE_SVC_HID_INFO_LEN (4)
#define BLE_SVC_HID_CONTROL_POINT_LEN (1)
#define BLE_SVC_HID_INPUT_REPORT_COUNT (3)
#define BLE_SVC_HID_OUTPUT_REPORT_COUNT (0)
#define BLE_SVC_HID_FEATURE_REPORT_COUNT (0)
#define BLE_SVC_HID_REPORT_COUNT \
(BLE_SVC_HID_INPUT_REPORT_COUNT + BLE_SVC_HID_OUTPUT_REPORT_COUNT + \
BLE_SVC_HID_FEATURE_REPORT_COUNT)
typedef enum {
HidSvcGattCharacteristicProtocolMode = 0,
HidSvcGattCharacteristicReportMap,
HidSvcGattCharacteristicInfo,
HidSvcGattCharacteristicCtrlPoint,
HidSvcGattCharacteristicCount,
} HidSvcGattCharacteristicId;
typedef struct {
uint8_t report_idx;
uint8_t report_type;
} HidSvcReportId;
static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes");
static const Service_UUID_t ble_svc_hid_uuid = {
.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
};
static bool ble_svc_hid_char_desc_data_callback(
const void* context,
const uint8_t** data,
uint16_t* data_len) {
const HidSvcReportId* report_id = context;
*data_len = sizeof(HidSvcReportId);
if(data) {
*data = (const uint8_t*)report_id;
}
return false;
}
typedef struct {
const void* data_ptr;
uint16_t data_len;
} HidSvcDataWrapper;
static bool ble_svc_hid_report_data_callback(
const void* context,
const uint8_t** data,
uint16_t* data_len) {
const HidSvcDataWrapper* report_data = context;
if(data) {
*data = report_data->data_ptr;
*data_len = report_data->data_len;
} else {
*data_len = BLE_SVC_HID_REPORT_MAP_MAX_LEN;
}
return false;
}
static const BleGattCharacteristicParams ble_svc_hid_chars[HidSvcGattCharacteristicCount] = {
[HidSvcGattCharacteristicProtocolMode] =
{.name = "Protocol Mode",
.data_prop_type = FlipperGattCharacteristicDataFixed,
.data.fixed.length = 1,
.uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID,
.uuid_type = UUID_TYPE_16,
.char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP,
.security_permissions = ATTR_PERMISSION_NONE,
.gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE,
.is_variable = CHAR_VALUE_LEN_CONSTANT},
[HidSvcGattCharacteristicReportMap] =
{.name = "Report Map",
.data_prop_type = FlipperGattCharacteristicDataCallback,
.data.callback.fn = ble_svc_hid_report_data_callback,
.data.callback.context = NULL,
.uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID,
.uuid_type = UUID_TYPE_16,
.char_properties = CHAR_PROP_READ,
.security_permissions = ATTR_PERMISSION_NONE,
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
.is_variable = CHAR_VALUE_LEN_VARIABLE},
[HidSvcGattCharacteristicInfo] =
{.name = "HID Information",
.data_prop_type = FlipperGattCharacteristicDataFixed,
.data.fixed.length = BLE_SVC_HID_INFO_LEN,
.data.fixed.ptr = NULL,
.uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID,
.uuid_type = UUID_TYPE_16,
.char_properties = CHAR_PROP_READ,
.security_permissions = ATTR_PERMISSION_NONE,
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
.is_variable = CHAR_VALUE_LEN_CONSTANT},
[HidSvcGattCharacteristicCtrlPoint] =
{.name = "HID Control Point",
.data_prop_type = FlipperGattCharacteristicDataFixed,
.data.fixed.length = BLE_SVC_HID_CONTROL_POINT_LEN,
.uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID,
.uuid_type = UUID_TYPE_16,
.char_properties = CHAR_PROP_WRITE_WITHOUT_RESP,
.security_permissions = ATTR_PERMISSION_NONE,
.gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE,
.is_variable = CHAR_VALUE_LEN_CONSTANT},
};
static const BleGattCharacteristicDescriptorParams ble_svc_hid_char_descr_template = {
.uuid_type = UUID_TYPE_16,
.uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID,
.max_length = BLE_SVC_HID_REPORT_REF_LEN,
.data_callback.fn = ble_svc_hid_char_desc_data_callback,
.security_permissions = ATTR_PERMISSION_NONE,
.access_permissions = ATTR_ACCESS_READ_WRITE,
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
.is_variable = CHAR_VALUE_LEN_CONSTANT,
};
static const BleGattCharacteristicParams ble_svc_hid_report_template = {
.name = "Report",
.data_prop_type = FlipperGattCharacteristicDataCallback,
.data.callback.fn = ble_svc_hid_report_data_callback,
.data.callback.context = NULL,
.uuid.Char_UUID_16 = REPORT_CHAR_UUID,
.uuid_type = UUID_TYPE_16,
.char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY,
.security_permissions = ATTR_PERMISSION_NONE,
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
.is_variable = CHAR_VALUE_LEN_VARIABLE,
};
struct BleServiceHid {
uint16_t svc_handle;
BleGattCharacteristicInstance chars[HidSvcGattCharacteristicCount];
BleGattCharacteristicInstance input_report_chars[BLE_SVC_HID_INPUT_REPORT_COUNT];
BleGattCharacteristicInstance output_report_chars[BLE_SVC_HID_OUTPUT_REPORT_COUNT];
BleGattCharacteristicInstance feature_report_chars[BLE_SVC_HID_FEATURE_REPORT_COUNT];
GapSvcEventHandler* event_handler;
};
static BleEventAckStatus ble_svc_hid_event_handler(void* event, void* context) {
UNUSED(context);
BleEventAckStatus ret = BleEventNotAck;
hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data);
evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data;
// aci_gatt_attribute_modified_event_rp0* attribute_modified;
if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
// Process modification events
ret = BleEventAckFlowEnable;
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
// Process notification confirmation
ret = BleEventAckFlowEnable;
}
}
return ret;
}
BleServiceHid* ble_svc_hid_start(void) {
BleServiceHid* hid_svc = malloc(sizeof(BleServiceHid));
// Register event handler
hid_svc->event_handler =
ble_event_dispatcher_register_svc_handler(ble_svc_hid_event_handler, hid_svc);
/**
* Add Human Interface Device Service
*/
if(!ble_gatt_service_add(
UUID_TYPE_16,
&ble_svc_hid_uuid,
PRIMARY_SERVICE,
2 + /* protocol mode */
(4 * BLE_SVC_HID_INPUT_REPORT_COUNT) + (3 * BLE_SVC_HID_OUTPUT_REPORT_COUNT) +
(3 * BLE_SVC_HID_FEATURE_REPORT_COUNT) + 1 + 2 + 2 +
2, /* Service + Report Map + HID Information + HID Control Point */
&hid_svc->svc_handle)) {
free(hid_svc);
return NULL;
}
// Maintain previously defined characteristic order
ble_gatt_characteristic_init(
hid_svc->svc_handle,
&ble_svc_hid_chars[HidSvcGattCharacteristicProtocolMode],
&hid_svc->chars[HidSvcGattCharacteristicProtocolMode]);
uint8_t protocol_mode = 1;
ble_gatt_characteristic_update(
hid_svc->svc_handle,
&hid_svc->chars[HidSvcGattCharacteristicProtocolMode],
&protocol_mode);
// reports
BleGattCharacteristicDescriptorParams ble_svc_hid_char_descr;
BleGattCharacteristicParams report_char;
HidSvcReportId report_id;
memcpy(
&ble_svc_hid_char_descr, &ble_svc_hid_char_descr_template, sizeof(ble_svc_hid_char_descr));
memcpy(&report_char, &ble_svc_hid_report_template, sizeof(report_char));
ble_svc_hid_char_descr.data_callback.context = &report_id;
report_char.descriptor_params = &ble_svc_hid_char_descr;
typedef struct {
uint8_t report_type;
uint8_t report_count;
BleGattCharacteristicInstance* chars;
} HidSvcReportCharProps;
HidSvcReportCharProps hid_report_chars[] = {
{0x01, BLE_SVC_HID_INPUT_REPORT_COUNT, hid_svc->input_report_chars},
{0x02, BLE_SVC_HID_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars},
{0x03, BLE_SVC_HID_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars},
};
for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars);
report_type_idx++) {
report_id.report_type = hid_report_chars[report_type_idx].report_type;
for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count;
report_idx++) {
report_id.report_idx = report_idx + 1;
ble_gatt_characteristic_init(
hid_svc->svc_handle,
&report_char,
&hid_report_chars[report_type_idx].chars[report_idx]);
}
}
// Setup remaining characteristics
for(size_t i = HidSvcGattCharacteristicReportMap; i < HidSvcGattCharacteristicCount; i++) {
ble_gatt_characteristic_init(
hid_svc->svc_handle, &ble_svc_hid_chars[i], &hid_svc->chars[i]);
}
return hid_svc;
}
bool ble_svc_hid_update_report_map(BleServiceHid* hid_svc, const uint8_t* data, uint16_t len) {
furi_assert(data);
furi_assert(hid_svc);
HidSvcDataWrapper report_data = {
.data_ptr = data,
.data_len = len,
};
return ble_gatt_characteristic_update(
hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data);
}
bool ble_svc_hid_update_input_report(
BleServiceHid* hid_svc,
uint8_t input_report_num,
uint8_t* data,
uint16_t len) {
furi_assert(data);
furi_assert(hid_svc);
furi_assert(input_report_num < BLE_SVC_HID_INPUT_REPORT_COUNT);
HidSvcDataWrapper report_data = {
.data_ptr = data,
.data_len = len,
};
return ble_gatt_characteristic_update(
hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data);
}
bool ble_svc_hid_update_info(BleServiceHid* hid_svc, uint8_t* data) {
furi_assert(data);
furi_assert(hid_svc);
return ble_gatt_characteristic_update(
hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data);
}
void ble_svc_hid_stop(BleServiceHid* hid_svc) {
furi_assert(hid_svc);
ble_event_dispatcher_unregister_svc_handler(hid_svc->event_handler);
// Delete characteristics
for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) {
ble_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]);
}
typedef struct {
uint8_t report_count;
BleGattCharacteristicInstance* chars;
} HidSvcReportCharProps;
HidSvcReportCharProps hid_report_chars[] = {
{BLE_SVC_HID_INPUT_REPORT_COUNT, hid_svc->input_report_chars},
{BLE_SVC_HID_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars},
{BLE_SVC_HID_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars},
};
for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars);
report_type_idx++) {
for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count;
report_idx++) {
ble_gatt_characteristic_delete(
hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]);
}
}
// Delete service
ble_gatt_service_delete(hid_svc->svc_handle);
free(hid_svc);
}