mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-12 10:58:36 -07:00
Merge branch 'ofw_dev' into blerefactr
This commit is contained in:
@@ -1,28 +1,30 @@
|
||||
#include "battery_service.h"
|
||||
#include "app_common.h"
|
||||
#include "gatt_char.h"
|
||||
#include <core/check.h>
|
||||
#include <furi_ble/gatt.h>
|
||||
|
||||
#include <ble/ble.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal_power.h>
|
||||
|
||||
#include <m-list.h>
|
||||
|
||||
#define TAG "BtBatterySvc"
|
||||
|
||||
enum {
|
||||
// Common states
|
||||
/* Common states */
|
||||
BatterySvcPowerStateUnknown = 0b00,
|
||||
BatterySvcPowerStateUnsupported = 0b01,
|
||||
// Level states
|
||||
/* Level states */
|
||||
BatterySvcPowerStateGoodLevel = 0b10,
|
||||
BatterySvcPowerStateCriticallyLowLevel = 0b11,
|
||||
// Charging states
|
||||
/* Charging states */
|
||||
BatterySvcPowerStateNotCharging = 0b10,
|
||||
BatterySvcPowerStateCharging = 0b11,
|
||||
// Discharging states
|
||||
/* Discharging states */
|
||||
BatterySvcPowerStateNotDischarging = 0b10,
|
||||
BatterySvcPowerStateDischarging = 0b11,
|
||||
// Battery states
|
||||
/* Battery states */
|
||||
BatterySvcPowerStateBatteryNotPresent = 0b10,
|
||||
BatterySvcPowerStateBatteryPresent = 0b11,
|
||||
};
|
||||
@@ -46,96 +48,110 @@ typedef enum {
|
||||
BatterySvcGattCharacteristicCount,
|
||||
} BatterySvcGattCharacteristicId;
|
||||
|
||||
static const FlipperGattCharacteristicParams battery_svc_chars[BatterySvcGattCharacteristicCount] =
|
||||
{[BatterySvcGattCharacteristicBatteryLevel] =
|
||||
{.name = "Battery Level",
|
||||
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||
.data.fixed.length = 1,
|
||||
.uuid.Char_UUID_16 = BATTERY_LEVEL_CHAR_UUID,
|
||||
.uuid_type = UUID_TYPE_16,
|
||||
.char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY,
|
||||
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
|
||||
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
|
||||
.is_variable = CHAR_VALUE_LEN_CONSTANT},
|
||||
[BatterySvcGattCharacteristicPowerState] = {
|
||||
.name = "Power State",
|
||||
static const BleGattCharacteristicParams battery_svc_chars[BatterySvcGattCharacteristicCount] = {
|
||||
[BatterySvcGattCharacteristicBatteryLevel] =
|
||||
{.name = "Battery Level",
|
||||
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||
.data.fixed.length = 1,
|
||||
.uuid.Char_UUID_16 = BATTERY_POWER_STATE,
|
||||
.uuid.Char_UUID_16 = BATTERY_LEVEL_CHAR_UUID,
|
||||
.uuid_type = UUID_TYPE_16,
|
||||
.char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY,
|
||||
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
|
||||
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
|
||||
.is_variable = CHAR_VALUE_LEN_CONSTANT}};
|
||||
.is_variable = CHAR_VALUE_LEN_CONSTANT},
|
||||
[BatterySvcGattCharacteristicPowerState] = {
|
||||
.name = "Power State",
|
||||
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||
.data.fixed.length = 1,
|
||||
.uuid.Char_UUID_16 = BATTERY_POWER_STATE,
|
||||
.uuid_type = UUID_TYPE_16,
|
||||
.char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY,
|
||||
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
|
||||
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
|
||||
.is_variable = CHAR_VALUE_LEN_CONSTANT}};
|
||||
|
||||
typedef struct {
|
||||
struct BleServiceBattery {
|
||||
uint16_t svc_handle;
|
||||
FlipperGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount];
|
||||
} BatterySvc;
|
||||
BleGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount];
|
||||
bool auto_update;
|
||||
};
|
||||
|
||||
static BatterySvc* battery_svc = NULL;
|
||||
LIST_DEF(BatterySvcInstanceList, BleServiceBattery*, M_POD_OPLIST);
|
||||
|
||||
void battery_svc_start() {
|
||||
battery_svc = malloc(sizeof(BatterySvc));
|
||||
tBleStatus status;
|
||||
/* We need to keep track of all battery service instances so that we can update
|
||||
* them when the battery state changes. */
|
||||
static BatterySvcInstanceList_t instances;
|
||||
static bool instances_initialized = false;
|
||||
|
||||
// Add Battery service
|
||||
status = aci_gatt_add_service(
|
||||
UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 8, &battery_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add Battery service: %d", status);
|
||||
BleServiceBattery* ble_svc_battery_start(bool auto_update) {
|
||||
BleServiceBattery* battery_svc = malloc(sizeof(BleServiceBattery));
|
||||
|
||||
if(!ble_gatt_service_add(
|
||||
UUID_TYPE_16,
|
||||
(Service_UUID_t*)&service_uuid,
|
||||
PRIMARY_SERVICE,
|
||||
8,
|
||||
&battery_svc->svc_handle)) {
|
||||
free(battery_svc);
|
||||
return NULL;
|
||||
}
|
||||
for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) {
|
||||
flipper_gatt_characteristic_init(
|
||||
ble_gatt_characteristic_init(
|
||||
battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]);
|
||||
}
|
||||
|
||||
battery_svc_update_power_state();
|
||||
}
|
||||
|
||||
void battery_svc_stop() {
|
||||
tBleStatus status;
|
||||
if(battery_svc) {
|
||||
for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) {
|
||||
flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]);
|
||||
battery_svc->auto_update = auto_update;
|
||||
if(auto_update) {
|
||||
if(!instances_initialized) {
|
||||
BatterySvcInstanceList_init(instances);
|
||||
instances_initialized = true;
|
||||
}
|
||||
// Delete Battery service
|
||||
status = aci_gatt_del_service(battery_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to delete Battery service: %d", status);
|
||||
|
||||
BatterySvcInstanceList_push_back(instances, battery_svc);
|
||||
}
|
||||
|
||||
return battery_svc;
|
||||
}
|
||||
|
||||
void ble_svc_battery_stop(BleServiceBattery* battery_svc) {
|
||||
furi_assert(battery_svc);
|
||||
if(battery_svc->auto_update) {
|
||||
BatterySvcInstanceList_it_t it;
|
||||
for(BatterySvcInstanceList_it(it, instances); !BatterySvcInstanceList_end_p(it);
|
||||
BatterySvcInstanceList_next(it)) {
|
||||
if(*BatterySvcInstanceList_ref(it) == battery_svc) {
|
||||
BatterySvcInstanceList_remove(instances, it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(battery_svc);
|
||||
battery_svc = NULL;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) {
|
||||
ble_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]);
|
||||
}
|
||||
/* Delete Battery service */
|
||||
ble_gatt_service_delete(battery_svc->svc_handle);
|
||||
free(battery_svc);
|
||||
}
|
||||
|
||||
bool battery_svc_is_started() {
|
||||
return battery_svc != NULL;
|
||||
}
|
||||
|
||||
bool battery_svc_update_level(uint8_t battery_charge) {
|
||||
// Check if service was started
|
||||
if(battery_svc == NULL) {
|
||||
return false;
|
||||
}
|
||||
// Update battery level characteristic
|
||||
return flipper_gatt_characteristic_update(
|
||||
bool ble_svc_battery_update_level(BleServiceBattery* battery_svc, uint8_t battery_charge) {
|
||||
furi_check(battery_svc);
|
||||
/* Update battery level characteristic */
|
||||
return ble_gatt_characteristic_update(
|
||||
battery_svc->svc_handle,
|
||||
&battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel],
|
||||
&battery_charge);
|
||||
}
|
||||
|
||||
bool battery_svc_update_power_state() {
|
||||
// Check if service was started
|
||||
if(battery_svc == NULL) {
|
||||
return false;
|
||||
}
|
||||
// Update power state characteristic
|
||||
bool ble_svc_battery_update_power_state(BleServiceBattery* battery_svc, bool charging) {
|
||||
furi_check(battery_svc);
|
||||
|
||||
/* Update power state characteristic */
|
||||
BattrySvcPowerState power_state = {
|
||||
.level = BatterySvcPowerStateUnsupported,
|
||||
.present = BatterySvcPowerStateBatteryPresent,
|
||||
};
|
||||
if(furi_hal_power_is_charging()) {
|
||||
if(charging) {
|
||||
power_state.charging = BatterySvcPowerStateCharging;
|
||||
power_state.discharging = BatterySvcPowerStateNotDischarging;
|
||||
} else {
|
||||
@@ -143,8 +159,29 @@ bool battery_svc_update_power_state() {
|
||||
power_state.discharging = BatterySvcPowerStateDischarging;
|
||||
}
|
||||
|
||||
return flipper_gatt_characteristic_update(
|
||||
return ble_gatt_characteristic_update(
|
||||
battery_svc->svc_handle,
|
||||
&battery_svc->chars[BatterySvcGattCharacteristicPowerState],
|
||||
&power_state);
|
||||
}
|
||||
|
||||
void ble_svc_battery_state_update(uint8_t* battery_level, bool* charging) {
|
||||
if(!instances_initialized) {
|
||||
#ifdef FURI_BLE_EXTRA_LOG
|
||||
FURI_LOG_W(TAG, "Battery service not initialized");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
BatterySvcInstanceList_it_t it;
|
||||
for(BatterySvcInstanceList_it(it, instances); !BatterySvcInstanceList_end_p(it);
|
||||
BatterySvcInstanceList_next(it)) {
|
||||
BleServiceBattery* battery_svc = *BatterySvcInstanceList_ref(it);
|
||||
if(battery_level) {
|
||||
ble_svc_battery_update_level(battery_svc, *battery_level);
|
||||
}
|
||||
if(charging) {
|
||||
ble_svc_battery_update_power_state(battery_svc, *charging);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,27 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void battery_svc_start();
|
||||
/*
|
||||
* Battery service. Can be used in most profiles.
|
||||
* If auto_update is true, the service will automatically update the battery
|
||||
* level and charging state from power state updates.
|
||||
*/
|
||||
|
||||
void battery_svc_stop();
|
||||
typedef struct BleServiceBattery BleServiceBattery;
|
||||
|
||||
bool battery_svc_is_started();
|
||||
BleServiceBattery* ble_svc_battery_start(bool auto_update);
|
||||
|
||||
bool battery_svc_update_level(uint8_t battery_level);
|
||||
void ble_svc_battery_stop(BleServiceBattery* service);
|
||||
|
||||
bool battery_svc_update_power_state();
|
||||
bool ble_svc_battery_update_level(BleServiceBattery* service, uint8_t battery_level);
|
||||
|
||||
bool ble_svc_battery_update_power_state(BleServiceBattery* service, bool charging);
|
||||
|
||||
/* Global function, callable without a service instance
|
||||
* Will update all service instances created with auto_update==true
|
||||
* Both parameters are optional, pass NULL if no value is available
|
||||
*/
|
||||
void ble_svc_battery_state_update(uint8_t* battery_level, bool* charging);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "dev_info_service.h"
|
||||
#include "app_common.h"
|
||||
#include "gatt_char.h"
|
||||
#include <furi_ble/gatt.h>
|
||||
#include <ble/ble.h>
|
||||
|
||||
#include <furi.h>
|
||||
@@ -20,45 +20,30 @@ typedef enum {
|
||||
DevInfoSvcGattCharacteristicCount,
|
||||
} DevInfoSvcGattCharacteristicId;
|
||||
|
||||
#define DEVICE_INFO_HARDWARE_REV_SIZE 4
|
||||
typedef struct {
|
||||
uint16_t service_handle;
|
||||
FlipperGattCharacteristicInstance characteristics[DevInfoSvcGattCharacteristicCount];
|
||||
FuriString* version_string;
|
||||
char hardware_revision[DEVICE_INFO_HARDWARE_REV_SIZE];
|
||||
} DevInfoSvc;
|
||||
#define DEVICE_INFO_HARDWARE_REV_SIZE (4)
|
||||
#define DEVICE_INFO_SOFTWARE_REV_SIZE (40)
|
||||
|
||||
static DevInfoSvc* dev_info_svc = NULL;
|
||||
struct BleServiceDevInfo {
|
||||
uint16_t service_handle;
|
||||
BleGattCharacteristicInstance characteristics[DevInfoSvcGattCharacteristicCount];
|
||||
};
|
||||
|
||||
static const char dev_info_man_name[] = "Flipper Devices Inc.";
|
||||
static const char dev_info_serial_num[] = "1.0";
|
||||
static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION);
|
||||
static char hardware_revision[DEVICE_INFO_HARDWARE_REV_SIZE] = {0};
|
||||
static char software_revision[DEVICE_INFO_SOFTWARE_REV_SIZE] = {0};
|
||||
|
||||
static bool dev_info_char_firmware_rev_callback(
|
||||
const void* context,
|
||||
const uint8_t** data,
|
||||
uint16_t* data_len) {
|
||||
const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context;
|
||||
*data_len = strlen(dev_info_svc->hardware_revision);
|
||||
static bool
|
||||
dev_info_char_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) {
|
||||
*data_len = (uint16_t)strlen(context); //-V1029
|
||||
if(data) {
|
||||
*data = (const uint8_t*)&dev_info_svc->hardware_revision;
|
||||
*data = (const uint8_t*)context;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool dev_info_char_software_rev_callback(
|
||||
const void* context,
|
||||
const uint8_t** data,
|
||||
uint16_t* data_len) {
|
||||
const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context;
|
||||
*data_len = furi_string_size(dev_info_svc->version_string);
|
||||
if(data) {
|
||||
*data = (const uint8_t*)furi_string_get_cstr(dev_info_svc->version_string);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCharacteristicCount] =
|
||||
static const BleGattCharacteristicParams ble_svc_dev_info_chars[DevInfoSvcGattCharacteristicCount] =
|
||||
{[DevInfoSvcGattCharacteristicMfgName] =
|
||||
{.name = "Manufacturer Name",
|
||||
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||
@@ -84,8 +69,8 @@ static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCh
|
||||
[DevInfoSvcGattCharacteristicFirmwareRev] =
|
||||
{.name = "Firmware Revision",
|
||||
.data_prop_type = FlipperGattCharacteristicDataCallback,
|
||||
.data.callback.context = &dev_info_svc,
|
||||
.data.callback.fn = dev_info_char_firmware_rev_callback,
|
||||
.data.callback.context = hardware_revision,
|
||||
.data.callback.fn = dev_info_char_data_callback,
|
||||
.uuid.Char_UUID_16 = FIRMWARE_REVISION_UUID,
|
||||
.uuid_type = UUID_TYPE_16,
|
||||
.char_properties = CHAR_PROP_READ,
|
||||
@@ -95,8 +80,8 @@ static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCh
|
||||
[DevInfoSvcGattCharacteristicSoftwareRev] =
|
||||
{.name = "Software Revision",
|
||||
.data_prop_type = FlipperGattCharacteristicDataCallback,
|
||||
.data.callback.context = &dev_info_svc,
|
||||
.data.callback.fn = dev_info_char_software_rev_callback,
|
||||
.data.callback.context = software_revision,
|
||||
.data.callback.fn = dev_info_char_data_callback,
|
||||
.uuid.Char_UUID_16 = SOFTWARE_REVISION_UUID,
|
||||
.uuid_type = UUID_TYPE_16,
|
||||
.char_properties = CHAR_PROP_READ,
|
||||
@@ -115,64 +100,52 @@ static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCh
|
||||
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
|
||||
.is_variable = CHAR_VALUE_LEN_CONSTANT}};
|
||||
|
||||
void dev_info_svc_start() {
|
||||
dev_info_svc = malloc(sizeof(DevInfoSvc));
|
||||
dev_info_svc->version_string = furi_string_alloc_printf(
|
||||
BleServiceDevInfo* ble_svc_dev_info_start(void) {
|
||||
BleServiceDevInfo* dev_info_svc = malloc(sizeof(BleServiceDevInfo));
|
||||
snprintf(
|
||||
software_revision,
|
||||
sizeof(software_revision),
|
||||
"%s %s %s %s",
|
||||
version_get_githash(NULL),
|
||||
version_get_version(NULL),
|
||||
version_get_gitbranchnum(NULL),
|
||||
version_get_builddate(NULL));
|
||||
snprintf(
|
||||
dev_info_svc->hardware_revision,
|
||||
sizeof(dev_info_svc->hardware_revision),
|
||||
"%d",
|
||||
version_get_target(NULL));
|
||||
tBleStatus status;
|
||||
snprintf(hardware_revision, sizeof(hardware_revision), "%d", version_get_target(NULL));
|
||||
|
||||
// Add Device Information Service
|
||||
uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID;
|
||||
status = aci_gatt_add_service(
|
||||
UUID_TYPE_16,
|
||||
(Service_UUID_t*)&uuid,
|
||||
PRIMARY_SERVICE,
|
||||
1 + 2 * DevInfoSvcGattCharacteristicCount,
|
||||
&dev_info_svc->service_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status);
|
||||
if(!ble_gatt_service_add(
|
||||
UUID_TYPE_16,
|
||||
(Service_UUID_t*)&uuid,
|
||||
PRIMARY_SERVICE,
|
||||
1 + 2 * DevInfoSvcGattCharacteristicCount,
|
||||
&dev_info_svc->service_handle)) {
|
||||
free(dev_info_svc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) {
|
||||
flipper_gatt_characteristic_init(
|
||||
ble_gatt_characteristic_init(
|
||||
dev_info_svc->service_handle,
|
||||
&dev_info_svc_chars[i],
|
||||
&ble_svc_dev_info_chars[i],
|
||||
&dev_info_svc->characteristics[i]);
|
||||
flipper_gatt_characteristic_update(
|
||||
ble_gatt_characteristic_update(
|
||||
dev_info_svc->service_handle, &dev_info_svc->characteristics[i], NULL);
|
||||
}
|
||||
|
||||
return dev_info_svc;
|
||||
}
|
||||
|
||||
void dev_info_svc_stop() {
|
||||
tBleStatus status;
|
||||
if(dev_info_svc) {
|
||||
// Delete service characteristics
|
||||
for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) {
|
||||
flipper_gatt_characteristic_delete(
|
||||
dev_info_svc->service_handle, &dev_info_svc->characteristics[i]);
|
||||
}
|
||||
|
||||
// Delete service
|
||||
status = aci_gatt_del_service(dev_info_svc->service_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to delete device info service: %d", status);
|
||||
}
|
||||
|
||||
furi_string_free(dev_info_svc->version_string);
|
||||
free(dev_info_svc);
|
||||
dev_info_svc = NULL;
|
||||
void ble_svc_dev_info_stop(BleServiceDevInfo* dev_info_svc) {
|
||||
furi_assert(dev_info_svc);
|
||||
/* Delete service characteristics */
|
||||
for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) {
|
||||
ble_gatt_characteristic_delete(
|
||||
dev_info_svc->service_handle, &dev_info_svc->characteristics[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool dev_info_svc_is_started() {
|
||||
return dev_info_svc != NULL;
|
||||
/* Delete service */
|
||||
ble_gatt_service_delete(dev_info_svc->service_handle);
|
||||
|
||||
free(dev_info_svc);
|
||||
}
|
||||
|
||||
@@ -7,11 +7,16 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void dev_info_svc_start();
|
||||
/*
|
||||
* Device information service.
|
||||
* Holds Flipper name, version and other information.
|
||||
*/
|
||||
|
||||
void dev_info_svc_stop();
|
||||
typedef struct BleServiceDevInfo BleServiceDevInfo;
|
||||
|
||||
bool dev_info_svc_is_started();
|
||||
BleServiceDevInfo* ble_svc_dev_info_start(void);
|
||||
|
||||
void ble_svc_dev_info_stop(BleServiceDevInfo* service);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
#include "gatt_char.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "GattChar"
|
||||
|
||||
#define GATT_MIN_READ_KEY_SIZE (10)
|
||||
|
||||
void flipper_gatt_characteristic_init(
|
||||
uint16_t svc_handle,
|
||||
const FlipperGattCharacteristicParams* char_descriptor,
|
||||
FlipperGattCharacteristicInstance* char_instance) {
|
||||
furi_assert(char_descriptor);
|
||||
furi_assert(char_instance);
|
||||
|
||||
// Copy the descriptor to the instance, since it may point to stack memory
|
||||
char_instance->characteristic = malloc(sizeof(FlipperGattCharacteristicParams));
|
||||
memcpy(
|
||||
(void*)char_instance->characteristic,
|
||||
char_descriptor,
|
||||
sizeof(FlipperGattCharacteristicParams));
|
||||
|
||||
uint16_t char_data_size = 0;
|
||||
if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) {
|
||||
char_data_size = char_descriptor->data.fixed.length;
|
||||
} else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) {
|
||||
char_descriptor->data.callback.fn(
|
||||
char_descriptor->data.callback.context, NULL, &char_data_size);
|
||||
}
|
||||
|
||||
tBleStatus status = aci_gatt_add_char(
|
||||
svc_handle,
|
||||
char_descriptor->uuid_type,
|
||||
&char_descriptor->uuid,
|
||||
char_data_size,
|
||||
char_descriptor->char_properties,
|
||||
char_descriptor->security_permissions,
|
||||
char_descriptor->gatt_evt_mask,
|
||||
GATT_MIN_READ_KEY_SIZE,
|
||||
char_descriptor->is_variable,
|
||||
&char_instance->handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status);
|
||||
}
|
||||
|
||||
char_instance->descriptor_handle = 0;
|
||||
if((status == 0) && char_descriptor->descriptor_params) {
|
||||
uint8_t const* char_data = NULL;
|
||||
const FlipperGattCharacteristicDescriptorParams* char_data_descriptor =
|
||||
char_descriptor->descriptor_params;
|
||||
bool release_data = char_data_descriptor->data_callback.fn(
|
||||
char_data_descriptor->data_callback.context, &char_data, &char_data_size);
|
||||
|
||||
status = aci_gatt_add_char_desc(
|
||||
svc_handle,
|
||||
char_instance->handle,
|
||||
char_data_descriptor->uuid_type,
|
||||
&char_data_descriptor->uuid,
|
||||
char_data_descriptor->max_length,
|
||||
char_data_size,
|
||||
char_data,
|
||||
char_data_descriptor->security_permissions,
|
||||
char_data_descriptor->access_permissions,
|
||||
char_data_descriptor->gatt_evt_mask,
|
||||
MIN_ENCRY_KEY_SIZE,
|
||||
char_data_descriptor->is_variable,
|
||||
&char_instance->descriptor_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status);
|
||||
}
|
||||
if(release_data) {
|
||||
free((void*)char_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flipper_gatt_characteristic_delete(
|
||||
uint16_t svc_handle,
|
||||
FlipperGattCharacteristicInstance* char_instance) {
|
||||
tBleStatus status = aci_gatt_del_char(svc_handle, char_instance->handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(
|
||||
TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status);
|
||||
}
|
||||
free((void*)char_instance->characteristic);
|
||||
}
|
||||
|
||||
bool flipper_gatt_characteristic_update(
|
||||
uint16_t svc_handle,
|
||||
FlipperGattCharacteristicInstance* char_instance,
|
||||
const void* source) {
|
||||
furi_assert(char_instance);
|
||||
const FlipperGattCharacteristicParams* char_descriptor = char_instance->characteristic;
|
||||
FURI_LOG_D(TAG, "Updating %s char", char_descriptor->name);
|
||||
|
||||
const uint8_t* char_data = NULL;
|
||||
uint16_t char_data_size = 0;
|
||||
bool release_data = false;
|
||||
if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) {
|
||||
char_data = char_descriptor->data.fixed.ptr;
|
||||
if(source) {
|
||||
char_data = (uint8_t*)source;
|
||||
}
|
||||
char_data_size = char_descriptor->data.fixed.length;
|
||||
} else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) {
|
||||
const void* context = char_descriptor->data.callback.context;
|
||||
if(source) {
|
||||
context = source;
|
||||
}
|
||||
release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size);
|
||||
}
|
||||
|
||||
tBleStatus result = aci_gatt_update_char_value(
|
||||
svc_handle, char_instance->handle, 0, char_data_size, char_data);
|
||||
if(result) {
|
||||
FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result);
|
||||
}
|
||||
if(release_data) {
|
||||
free((void*)char_data);
|
||||
}
|
||||
return result != BLE_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <ble/ble.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Callback signature for getting characteristic data
|
||||
// Is called when characteristic is created to get max data length. Data ptr is NULL in this case
|
||||
// The result is passed to aci_gatt_add_char as "Char_Value_Length"
|
||||
// For updates, called with a context - see flipper_gatt_characteristic_update
|
||||
// Returns true if *data ownership is transferred to the caller and will be freed
|
||||
typedef bool (*cbFlipperGattCharacteristicData)(
|
||||
const void* context,
|
||||
const uint8_t** data,
|
||||
uint16_t* data_len);
|
||||
|
||||
typedef enum {
|
||||
FlipperGattCharacteristicDataFixed,
|
||||
FlipperGattCharacteristicDataCallback,
|
||||
} FlipperGattCharacteristicDataType;
|
||||
|
||||
typedef struct {
|
||||
Char_Desc_Uuid_t uuid;
|
||||
struct {
|
||||
cbFlipperGattCharacteristicData fn;
|
||||
const void* context;
|
||||
} data_callback;
|
||||
uint8_t uuid_type;
|
||||
uint8_t max_length;
|
||||
uint8_t security_permissions;
|
||||
uint8_t access_permissions;
|
||||
uint8_t gatt_evt_mask;
|
||||
uint8_t is_variable;
|
||||
} FlipperGattCharacteristicDescriptorParams;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
FlipperGattCharacteristicDescriptorParams* descriptor_params;
|
||||
union {
|
||||
struct {
|
||||
const uint8_t* ptr;
|
||||
uint16_t length;
|
||||
} fixed;
|
||||
struct {
|
||||
cbFlipperGattCharacteristicData fn;
|
||||
const void* context;
|
||||
} callback;
|
||||
} data;
|
||||
Char_UUID_t uuid;
|
||||
// Some packed bitfields to save space
|
||||
FlipperGattCharacteristicDataType data_prop_type : 2;
|
||||
uint8_t is_variable : 2;
|
||||
uint8_t uuid_type : 2;
|
||||
uint8_t char_properties;
|
||||
uint8_t security_permissions;
|
||||
uint8_t gatt_evt_mask;
|
||||
} FlipperGattCharacteristicParams;
|
||||
|
||||
_Static_assert(
|
||||
sizeof(FlipperGattCharacteristicParams) == 36,
|
||||
"FlipperGattCharacteristicParams size must be 36 bytes");
|
||||
|
||||
typedef struct {
|
||||
const FlipperGattCharacteristicParams* characteristic;
|
||||
uint16_t handle;
|
||||
uint16_t descriptor_handle;
|
||||
} FlipperGattCharacteristicInstance;
|
||||
|
||||
// Initialize a characteristic instance; copies the characteristic descriptor into the instance
|
||||
void flipper_gatt_characteristic_init(
|
||||
uint16_t svc_handle,
|
||||
const FlipperGattCharacteristicParams* char_descriptor,
|
||||
FlipperGattCharacteristicInstance* char_instance);
|
||||
|
||||
// Delete a characteristic instance; frees the copied characteristic descriptor from the instance
|
||||
void flipper_gatt_characteristic_delete(
|
||||
uint16_t svc_handle,
|
||||
FlipperGattCharacteristicInstance* char_instance);
|
||||
|
||||
// Update a characteristic instance; if source==NULL, uses the data from the characteristic
|
||||
// - For fixed data, fixed.ptr is used as the source if source==NULL
|
||||
// - For callback-based data, collback.context is passed as the context if source==NULL
|
||||
bool flipper_gatt_characteristic_update(
|
||||
uint16_t svc_handle,
|
||||
FlipperGattCharacteristicInstance* char_instance,
|
||||
const void* source);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,366 +0,0 @@
|
||||
#include "hid_service.h"
|
||||
#include "app_common.h"
|
||||
#include <ble/ble.h>
|
||||
#include "gatt_char.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "BtHid"
|
||||
|
||||
typedef enum {
|
||||
HidSvcGattCharacteristicProtocolMode = 0,
|
||||
HidSvcGattCharacteristicReportMap,
|
||||
HidSvcGattCharacteristicInfo,
|
||||
HidSvcGattCharacteristicCtrlPoint,
|
||||
HidSvcGattCharacteristicLed,
|
||||
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 hid_svc_uuid = {
|
||||
.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
|
||||
};
|
||||
|
||||
static bool
|
||||
hid_svc_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
|
||||
hid_svc_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 = HID_SVC_REPORT_MAP_MAX_LEN;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// LED Descriptor params for BadBT
|
||||
|
||||
static uint8_t led_desc_context_buf[2] = {HID_SVC_REPORT_COUNT + 1, 2};
|
||||
|
||||
static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = {
|
||||
.uuid_type = UUID_TYPE_16,
|
||||
.uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID,
|
||||
.max_length = HID_SVC_REPORT_REF_LEN,
|
||||
.data_callback.fn = hid_svc_char_desc_data_callback,
|
||||
.data_callback.context = led_desc_context_buf,
|
||||
.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 FlipperGattCharacteristicParams hid_svc_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 = hid_svc_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 = HID_SVC_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 = HID_SVC_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},
|
||||
[HidSvcGattCharacteristicLed] =
|
||||
{
|
||||
.name =
|
||||
"HID LED State", // LED Characteristic and descriptor for BadBT to get numlock state for altchars
|
||||
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||
.data.fixed.length = 1,
|
||||
.uuid.Char_UUID_16 = REPORT_CHAR_UUID,
|
||||
.uuid_type = UUID_TYPE_16,
|
||||
.char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE,
|
||||
.security_permissions = ATTR_PERMISSION_NONE,
|
||||
.gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE |
|
||||
GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,
|
||||
.is_variable = CHAR_VALUE_LEN_CONSTANT,
|
||||
.descriptor_params = &hid_svc_char_descr_led,
|
||||
},
|
||||
};
|
||||
|
||||
static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = {
|
||||
.uuid_type = UUID_TYPE_16,
|
||||
.uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID,
|
||||
.max_length = HID_SVC_REPORT_REF_LEN,
|
||||
.data_callback.fn = hid_svc_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 FlipperGattCharacteristicParams hid_svc_report_template = {
|
||||
.name = "Report",
|
||||
.data_prop_type = FlipperGattCharacteristicDataCallback,
|
||||
.data.callback.fn = hid_svc_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,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint16_t svc_handle;
|
||||
FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount];
|
||||
FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT];
|
||||
FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT];
|
||||
FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT];
|
||||
// led state
|
||||
HidLedStateEventCallback led_state_event_callback;
|
||||
void* led_state_ctx;
|
||||
} HIDSvc;
|
||||
|
||||
static HIDSvc* hid_svc = NULL;
|
||||
|
||||
static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) {
|
||||
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
|
||||
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 = SVCCTL_EvtAckFlowEnable;
|
||||
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
|
||||
// Process notification confirmation
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
} else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) {
|
||||
// LED Characteristic and descriptor for BadBT to get numlock state for altchars
|
||||
//
|
||||
// Process write request
|
||||
aci_gatt_write_permit_req_event_rp0* req =
|
||||
(aci_gatt_write_permit_req_event_rp0*)blecore_evt->data;
|
||||
|
||||
furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx);
|
||||
|
||||
// this check is likely to be incorrect, it will actually work in our case
|
||||
// but we need to investigate gatt api to see what is the rules
|
||||
// that specify attibute handle value from char handle (or the reverse)
|
||||
if(req->Attribute_Handle == (hid_svc->chars[HidSvcGattCharacteristicLed].handle + 1)) {
|
||||
hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx);
|
||||
aci_gatt_write_resp(
|
||||
req->Connection_Handle,
|
||||
req->Attribute_Handle,
|
||||
0x00, /* write_status = 0 (no error))*/
|
||||
0x00, /* err_code */
|
||||
req->Data_Length,
|
||||
req->Data);
|
||||
aci_gatt_write_char_value(
|
||||
req->Connection_Handle,
|
||||
hid_svc->chars[HidSvcGattCharacteristicLed].handle,
|
||||
req->Data_Length,
|
||||
req->Data);
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void hid_svc_start() {
|
||||
tBleStatus status;
|
||||
hid_svc = malloc(sizeof(HIDSvc));
|
||||
|
||||
// Register event handler
|
||||
SVCCTL_RegisterSvcHandler(hid_svc_event_handler);
|
||||
/**
|
||||
* Add Human Interface Device Service
|
||||
*/
|
||||
status = aci_gatt_add_service(
|
||||
UUID_TYPE_16,
|
||||
&hid_svc_uuid,
|
||||
PRIMARY_SERVICE,
|
||||
2 + /* protocol mode */
|
||||
(4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) +
|
||||
(3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 +
|
||||
4, /* Service + Report Map + HID Information + HID Control Point + LED state */
|
||||
&hid_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add HID service: %d", status);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) {
|
||||
flipper_gatt_characteristic_init(
|
||||
hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]);
|
||||
}
|
||||
uint8_t protocol_mode = 1;
|
||||
flipper_gatt_characteristic_update(
|
||||
hid_svc->svc_handle,
|
||||
&hid_svc->chars[HidSvcGattCharacteristicProtocolMode],
|
||||
&protocol_mode);
|
||||
|
||||
// reports
|
||||
FlipperGattCharacteristicDescriptorParams hid_svc_char_descr;
|
||||
FlipperGattCharacteristicParams report_char;
|
||||
HidSvcReportId report_id;
|
||||
|
||||
memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr));
|
||||
memcpy(&report_char, &hid_svc_report_template, sizeof(report_char));
|
||||
|
||||
hid_svc_char_descr.data_callback.context = &report_id;
|
||||
report_char.descriptor_params = &hid_svc_char_descr;
|
||||
|
||||
typedef struct {
|
||||
uint8_t report_type;
|
||||
uint8_t report_count;
|
||||
FlipperGattCharacteristicInstance* chars;
|
||||
} HidSvcReportCharProps;
|
||||
|
||||
HidSvcReportCharProps hid_report_chars[] = {
|
||||
{0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars},
|
||||
{0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars},
|
||||
{0x03, HID_SVC_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;
|
||||
flipper_gatt_characteristic_init(
|
||||
hid_svc->svc_handle,
|
||||
&report_char,
|
||||
&hid_report_chars[report_type_idx].chars[report_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) {
|
||||
furi_assert(data);
|
||||
furi_assert(hid_svc);
|
||||
|
||||
HidSvcDataWrapper report_data = {
|
||||
.data_ptr = data,
|
||||
.data_len = len,
|
||||
};
|
||||
return flipper_gatt_characteristic_update(
|
||||
hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data);
|
||||
}
|
||||
|
||||
bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) {
|
||||
furi_assert(data);
|
||||
furi_assert(hid_svc);
|
||||
furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT);
|
||||
|
||||
HidSvcDataWrapper report_data = {
|
||||
.data_ptr = data,
|
||||
.data_len = len,
|
||||
};
|
||||
return flipper_gatt_characteristic_update(
|
||||
hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data);
|
||||
}
|
||||
|
||||
bool hid_svc_update_info(uint8_t* data) {
|
||||
furi_assert(data);
|
||||
furi_assert(hid_svc);
|
||||
|
||||
return flipper_gatt_characteristic_update(
|
||||
hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data);
|
||||
}
|
||||
|
||||
void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) {
|
||||
furi_assert(hid_svc);
|
||||
furi_assert(callback);
|
||||
furi_assert(context);
|
||||
|
||||
hid_svc->led_state_event_callback = callback;
|
||||
hid_svc->led_state_ctx = context;
|
||||
}
|
||||
|
||||
bool hid_svc_is_started() {
|
||||
return hid_svc != NULL;
|
||||
}
|
||||
|
||||
void hid_svc_stop() {
|
||||
tBleStatus status;
|
||||
if(hid_svc) {
|
||||
// Delete characteristics
|
||||
for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) {
|
||||
flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t report_count;
|
||||
FlipperGattCharacteristicInstance* chars;
|
||||
} HidSvcReportCharProps;
|
||||
|
||||
HidSvcReportCharProps hid_report_chars[] = {
|
||||
{HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars},
|
||||
{HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars},
|
||||
{HID_SVC_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++) {
|
||||
flipper_gatt_characteristic_delete(
|
||||
hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete service
|
||||
status = aci_gatt_del_service(hid_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to delete HID service: %d", status);
|
||||
}
|
||||
free(hid_svc);
|
||||
hid_svc = NULL;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define HID_SVC_REPORT_MAP_MAX_LEN (255)
|
||||
#define HID_SVC_REPORT_MAX_LEN (255)
|
||||
#define HID_SVC_REPORT_REF_LEN (2)
|
||||
#define HID_SVC_INFO_LEN (4)
|
||||
#define HID_SVC_CONTROL_POINT_LEN (1)
|
||||
|
||||
#define HID_SVC_INPUT_REPORT_COUNT (3)
|
||||
#define HID_SVC_OUTPUT_REPORT_COUNT (0)
|
||||
#define HID_SVC_FEATURE_REPORT_COUNT (0)
|
||||
#define HID_SVC_REPORT_COUNT \
|
||||
(HID_SVC_INPUT_REPORT_COUNT + HID_SVC_OUTPUT_REPORT_COUNT + HID_SVC_FEATURE_REPORT_COUNT)
|
||||
|
||||
typedef uint16_t (*HidLedStateEventCallback)(uint8_t state, void* ctx);
|
||||
|
||||
void hid_svc_start();
|
||||
|
||||
void hid_svc_stop();
|
||||
|
||||
bool hid_svc_is_started();
|
||||
|
||||
bool hid_svc_update_report_map(const uint8_t* data, uint16_t len);
|
||||
|
||||
bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len);
|
||||
|
||||
// Expects data to be of length HID_SVC_INFO_LEN (4 bytes)
|
||||
bool hid_svc_update_info(uint8_t* data);
|
||||
|
||||
void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context);
|
||||
@@ -1,11 +1,13 @@
|
||||
#include "serial_service.h"
|
||||
#include "app_common.h"
|
||||
#include <ble/ble.h>
|
||||
#include "gatt_char.h"
|
||||
#include <furi_ble/event_dispatcher.h>
|
||||
#include <furi_ble/gatt.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include "serial_service_uuid.inc"
|
||||
#include <stdint.h>
|
||||
|
||||
#define TAG "BtSerialSvc"
|
||||
|
||||
@@ -17,12 +19,12 @@ typedef enum {
|
||||
SerialSvcGattCharacteristicCount,
|
||||
} SerialSvcGattCharacteristicId;
|
||||
|
||||
static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = {
|
||||
static const BleGattCharacteristicParams ble_svc_serial_chars[SerialSvcGattCharacteristicCount] = {
|
||||
[SerialSvcGattCharacteristicRx] =
|
||||
{.name = "RX",
|
||||
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||
.data.fixed.length = SERIAL_SVC_DATA_LEN_MAX,
|
||||
.uuid.Char_UUID_128 = SERIAL_SVC_RX_CHAR_UUID,
|
||||
.data.fixed.length = BLE_SVC_SERIAL_DATA_LEN_MAX,
|
||||
.uuid.Char_UUID_128 = BLE_SVC_SERIAL_RX_CHAR_UUID,
|
||||
.uuid_type = UUID_TYPE_128,
|
||||
.char_properties = CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ,
|
||||
.security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE,
|
||||
@@ -31,8 +33,8 @@ static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattChara
|
||||
[SerialSvcGattCharacteristicTx] =
|
||||
{.name = "TX",
|
||||
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||
.data.fixed.length = SERIAL_SVC_DATA_LEN_MAX,
|
||||
.uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID,
|
||||
.data.fixed.length = BLE_SVC_SERIAL_DATA_LEN_MAX,
|
||||
.uuid.Char_UUID_128 = BLE_SVC_SERIAL_TX_CHAR_UUID,
|
||||
.uuid_type = UUID_TYPE_128,
|
||||
.char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE,
|
||||
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
|
||||
@@ -42,7 +44,7 @@ static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattChara
|
||||
{.name = "Flow control",
|
||||
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||
.data.fixed.length = sizeof(uint32_t),
|
||||
.uuid.Char_UUID_128 = SERIAL_SVC_FLOW_CONTROL_UUID,
|
||||
.uuid.Char_UUID_128 = BLE_SVC_SERIAL_FLOW_CONTROL_UUID,
|
||||
.uuid_type = UUID_TYPE_128,
|
||||
.char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY,
|
||||
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
|
||||
@@ -51,28 +53,28 @@ static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattChara
|
||||
[SerialSvcGattCharacteristicStatus] = {
|
||||
.name = "RPC status",
|
||||
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||
.data.fixed.length = sizeof(SerialServiceRpcStatus),
|
||||
.uuid.Char_UUID_128 = SERIAL_SVC_RPC_STATUS_UUID,
|
||||
.data.fixed.length = sizeof(uint32_t),
|
||||
.uuid.Char_UUID_128 = BLE_SVC_SERIAL_RPC_STATUS_UUID,
|
||||
.uuid_type = UUID_TYPE_128,
|
||||
.char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY,
|
||||
.security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE,
|
||||
.gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE,
|
||||
.is_variable = CHAR_VALUE_LEN_CONSTANT}};
|
||||
|
||||
typedef struct {
|
||||
struct BleServiceSerial {
|
||||
uint16_t svc_handle;
|
||||
FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount];
|
||||
BleGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount];
|
||||
FuriMutex* buff_size_mtx;
|
||||
uint32_t buff_size;
|
||||
uint16_t bytes_ready_to_receive;
|
||||
SerialServiceEventCallback callback;
|
||||
void* context;
|
||||
} SerialSvc;
|
||||
GapSvcEventHandler* event_handler;
|
||||
};
|
||||
|
||||
static SerialSvc* serial_svc = NULL;
|
||||
|
||||
static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
||||
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
|
||||
static BleEventAckStatus ble_svc_serial_event_handler(void* event, void* context) {
|
||||
BleServiceSerial* serial_svc = (BleServiceSerial*)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;
|
||||
@@ -82,7 +84,7 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
||||
if(attribute_modified->Attr_Handle ==
|
||||
serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) {
|
||||
// Descriptor handle
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
ret = BleEventAckFlowEnable;
|
||||
FURI_LOG_D(TAG, "RX descriptor event");
|
||||
} else if(
|
||||
attribute_modified->Attr_Handle ==
|
||||
@@ -111,13 +113,12 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
||||
FURI_LOG_D(TAG, "Available buff size: %ld", buff_free_size);
|
||||
furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk);
|
||||
}
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
ret = BleEventAckFlowEnable;
|
||||
} else if(
|
||||
attribute_modified->Attr_Handle ==
|
||||
serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) {
|
||||
SerialServiceRpcStatus* rpc_status =
|
||||
(SerialServiceRpcStatus*)attribute_modified->Attr_Data;
|
||||
if(*rpc_status == SerialServiceRpcStatusNotActive) {
|
||||
bool* rpc_status = (bool*)attribute_modified->Attr_Data;
|
||||
if(!*rpc_status) {
|
||||
if(serial_svc->callback) {
|
||||
SerialServiceEvent event = {
|
||||
.event = SerialServiceEventTypesBleResetRequest,
|
||||
@@ -134,43 +135,47 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
||||
};
|
||||
serial_svc->callback(event, serial_svc->context);
|
||||
}
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
ret = BleEventAckFlowEnable;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) {
|
||||
flipper_gatt_characteristic_update(
|
||||
typedef enum {
|
||||
SerialServiceRpcStatusNotActive = 0UL,
|
||||
SerialServiceRpcStatusActive = 1UL,
|
||||
} SerialServiceRpcStatus;
|
||||
|
||||
static void
|
||||
ble_svc_serial_update_rpc_char(BleServiceSerial* serial_svc, SerialServiceRpcStatus status) {
|
||||
ble_gatt_characteristic_update(
|
||||
serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &status);
|
||||
}
|
||||
|
||||
void serial_svc_start() {
|
||||
UNUSED(serial_svc_chars);
|
||||
tBleStatus status;
|
||||
serial_svc = malloc(sizeof(SerialSvc));
|
||||
// Register event handler
|
||||
SVCCTL_RegisterSvcHandler(serial_svc_event_handler);
|
||||
BleServiceSerial* ble_svc_serial_start(void) {
|
||||
BleServiceSerial* serial_svc = malloc(sizeof(BleServiceSerial));
|
||||
|
||||
// Add service
|
||||
status = aci_gatt_add_service(
|
||||
UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add Serial service: %d", status);
|
||||
serial_svc->event_handler =
|
||||
ble_event_dispatcher_register_svc_handler(ble_svc_serial_event_handler, serial_svc);
|
||||
|
||||
if(!ble_gatt_service_add(
|
||||
UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle)) {
|
||||
free(serial_svc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Add characteristics
|
||||
for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) {
|
||||
flipper_gatt_characteristic_init(
|
||||
serial_svc->svc_handle, &serial_svc_chars[i], &serial_svc->chars[i]);
|
||||
ble_gatt_characteristic_init(
|
||||
serial_svc->svc_handle, &ble_svc_serial_chars[i], &serial_svc->chars[i]);
|
||||
}
|
||||
|
||||
serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive);
|
||||
// Allocate buffer size mutex
|
||||
ble_svc_serial_update_rpc_char(serial_svc, SerialServiceRpcStatusNotActive);
|
||||
serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
return serial_svc;
|
||||
}
|
||||
|
||||
void serial_svc_set_callbacks(
|
||||
void ble_svc_serial_set_callbacks(
|
||||
BleServiceSerial* serial_svc,
|
||||
uint16_t buff_size,
|
||||
SerialServiceEventCallback callback,
|
||||
void* context) {
|
||||
@@ -181,13 +186,13 @@ void serial_svc_set_callbacks(
|
||||
serial_svc->bytes_ready_to_receive = buff_size;
|
||||
|
||||
uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size);
|
||||
flipper_gatt_characteristic_update(
|
||||
ble_gatt_characteristic_update(
|
||||
serial_svc->svc_handle,
|
||||
&serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl],
|
||||
&buff_size_reversed);
|
||||
}
|
||||
|
||||
void serial_svc_notify_buffer_is_empty() {
|
||||
void ble_svc_serial_notify_buffer_is_empty(BleServiceSerial* serial_svc) {
|
||||
furi_assert(serial_svc);
|
||||
furi_assert(serial_svc->buff_size_mtx);
|
||||
|
||||
@@ -197,7 +202,7 @@ void serial_svc_notify_buffer_is_empty() {
|
||||
serial_svc->bytes_ready_to_receive = serial_svc->buff_size;
|
||||
|
||||
uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size);
|
||||
flipper_gatt_characteristic_update(
|
||||
ble_gatt_characteristic_update(
|
||||
serial_svc->svc_handle,
|
||||
&serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl],
|
||||
&buff_size_reversed);
|
||||
@@ -205,35 +210,26 @@ void serial_svc_notify_buffer_is_empty() {
|
||||
furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk);
|
||||
}
|
||||
|
||||
void serial_svc_stop() {
|
||||
tBleStatus status;
|
||||
if(serial_svc) {
|
||||
for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) {
|
||||
flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]);
|
||||
}
|
||||
// Delete service
|
||||
status = aci_gatt_del_service(serial_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to delete Serial service: %d", status);
|
||||
}
|
||||
// Delete buffer size mutex
|
||||
furi_mutex_free(serial_svc->buff_size_mtx);
|
||||
free(serial_svc);
|
||||
serial_svc = NULL;
|
||||
void ble_svc_serial_stop(BleServiceSerial* serial_svc) {
|
||||
furi_check(serial_svc);
|
||||
|
||||
ble_event_dispatcher_unregister_svc_handler(serial_svc->event_handler);
|
||||
|
||||
for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) {
|
||||
ble_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]);
|
||||
}
|
||||
ble_gatt_service_delete(serial_svc->svc_handle);
|
||||
furi_mutex_free(serial_svc->buff_size_mtx);
|
||||
free(serial_svc);
|
||||
}
|
||||
|
||||
bool serial_svc_is_started() {
|
||||
return serial_svc != NULL;
|
||||
}
|
||||
|
||||
bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) {
|
||||
if(data_len > SERIAL_SVC_DATA_LEN_MAX) {
|
||||
bool ble_svc_serial_update_tx(BleServiceSerial* serial_svc, uint8_t* data, uint16_t data_len) {
|
||||
if(data_len > BLE_SVC_SERIAL_DATA_LEN_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(uint16_t remained = data_len; remained > 0;) {
|
||||
uint8_t value_len = MIN(SERIAL_SVC_CHAR_VALUE_LEN_MAX, remained);
|
||||
uint8_t value_len = MIN(BLE_SVC_SERIAL_CHAR_VALUE_LEN_MAX, remained);
|
||||
uint16_t value_offset = data_len - remained;
|
||||
remained -= value_len;
|
||||
|
||||
@@ -256,7 +252,8 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void serial_svc_set_rpc_status(SerialServiceRpcStatus status) {
|
||||
void ble_svc_serial_set_rpc_active(BleServiceSerial* serial_svc, bool active) {
|
||||
furi_assert(serial_svc);
|
||||
serial_svc_update_rpc_char(status);
|
||||
ble_svc_serial_update_rpc_char(
|
||||
serial_svc, active ? SerialServiceRpcStatusActive : SerialServiceRpcStatusNotActive);
|
||||
}
|
||||
|
||||
@@ -3,17 +3,16 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SERIAL_SVC_DATA_LEN_MAX (486)
|
||||
#define SERIAL_SVC_CHAR_VALUE_LEN_MAX (243)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
SerialServiceRpcStatusNotActive = 0UL,
|
||||
SerialServiceRpcStatusActive = 1UL,
|
||||
} SerialServiceRpcStatus;
|
||||
/*
|
||||
* Serial service. Implements RPC over BLE, with flow control.
|
||||
*/
|
||||
|
||||
#define BLE_SVC_SERIAL_DATA_LEN_MAX (486)
|
||||
#define BLE_SVC_SERIAL_CHAR_VALUE_LEN_MAX (243)
|
||||
|
||||
typedef enum {
|
||||
SerialServiceEventTypeDataReceived,
|
||||
@@ -33,22 +32,23 @@ typedef struct {
|
||||
|
||||
typedef uint16_t (*SerialServiceEventCallback)(SerialServiceEvent event, void* context);
|
||||
|
||||
void serial_svc_start();
|
||||
typedef struct BleServiceSerial BleServiceSerial;
|
||||
|
||||
void serial_svc_set_callbacks(
|
||||
BleServiceSerial* ble_svc_serial_start(void);
|
||||
|
||||
void ble_svc_serial_stop(BleServiceSerial* service);
|
||||
|
||||
void ble_svc_serial_set_callbacks(
|
||||
BleServiceSerial* service,
|
||||
uint16_t buff_size,
|
||||
SerialServiceEventCallback callback,
|
||||
void* context);
|
||||
|
||||
void serial_svc_set_rpc_status(SerialServiceRpcStatus status);
|
||||
void ble_svc_serial_set_rpc_active(BleServiceSerial* service, bool active);
|
||||
|
||||
void serial_svc_notify_buffer_is_empty();
|
||||
void ble_svc_serial_notify_buffer_is_empty(BleServiceSerial* service);
|
||||
|
||||
void serial_svc_stop();
|
||||
|
||||
bool serial_svc_is_started();
|
||||
|
||||
bool serial_svc_update_tx(uint8_t* data, uint16_t data_len);
|
||||
bool ble_svc_serial_update_tx(BleServiceSerial* service, uint8_t* data, uint16_t data_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
static const Service_UUID_t service_uuid = { .Service_UUID_128 = \
|
||||
{ 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f }};
|
||||
|
||||
#define SERIAL_SVC_TX_CHAR_UUID \
|
||||
#define BLE_SVC_SERIAL_TX_CHAR_UUID \
|
||||
{ 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 }
|
||||
#define SERIAL_SVC_RX_CHAR_UUID \
|
||||
#define BLE_SVC_SERIAL_RX_CHAR_UUID \
|
||||
{ 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 }
|
||||
#define SERIAL_SVC_FLOW_CONTROL_UUID \
|
||||
#define BLE_SVC_SERIAL_FLOW_CONTROL_UUID \
|
||||
{ 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 }
|
||||
#define SERIAL_SVC_RPC_STATUS_UUID \
|
||||
#define BLE_SVC_SERIAL_RPC_STATUS_UUID \
|
||||
{ 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 }
|
||||
|
||||
Reference in New Issue
Block a user