Merge remote-tracking branch 'UFW/dev' into subghz/code_improvement

This commit is contained in:
gid9798 2023-06-14 09:23:36 +03:00
commit e2104bd6cf
23 changed files with 882 additions and 972 deletions

View File

@ -1,7 +1,7 @@
#include "bt_i.h" #include "bt_i.h"
#include "battery_service.h"
#include "bt_keys_storage.h" #include "bt_keys_storage.h"
#include <services/battery_service.h>
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
#include <gui/elements.h> #include <gui/elements.h>
#include <assets_icons.h> #include <assets_icons.h>

View File

@ -196,14 +196,14 @@ static void APPD_SetCPU2GpioConfig(void) {
gpio_config.Pin = gpiob_pin_list; gpio_config.Pin = gpiob_pin_list;
LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB); LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB);
LL_GPIO_Init(GPIOB, &gpio_config); LL_GPIO_Init(GPIOB, &gpio_config);
LL_GPIO_ResetOutputPin(GPIOB, gpiob_pin_list); LL_GPIO_ResetOutputPin(GPIOB, gpioa_pin_list);
} }
if(gpioc_pin_list != 0) { if(gpioc_pin_list != 0) {
gpio_config.Pin = gpioc_pin_list; gpio_config.Pin = gpioc_pin_list;
LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC); LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC);
LL_GPIO_Init(GPIOC, &gpio_config); LL_GPIO_Init(GPIOC, &gpio_config);
LL_GPIO_ResetOutputPin(GPIOC, gpioc_pin_list); LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list);
} }
} }

View File

@ -1,7 +1,5 @@
#include "battery_service.h" #include "battery_service.h"
#include "app_common.h" #include "app_common.h"
#include "gatt_char.h"
#include <ble/ble.h> #include <ble/ble.h>
#include <furi.h> #include <furi.h>
@ -9,6 +7,12 @@
#define TAG "BtBatterySvc" #define TAG "BtBatterySvc"
typedef struct {
uint16_t svc_handle;
uint16_t battery_level_char_handle;
uint16_t power_state_char_handle;
} BatterySvc;
enum { enum {
// Common states // Common states
BatterySvcPowerStateUnknown = 0b00, BatterySvcPowerStateUnknown = 0b00,
@ -36,44 +40,13 @@ typedef struct {
_Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); _Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size");
static BatterySvc* battery_svc = NULL;
#define BATTERY_POWER_STATE (0x2A1A) #define BATTERY_POWER_STATE (0x2A1A)
static const uint16_t service_uuid = BATTERY_SERVICE_UUID; static const uint16_t service_uuid = BATTERY_SERVICE_UUID;
static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID;
typedef enum { static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE;
BatterySvcGattCharacteristicBatteryLevel = 0,
BatterySvcGattCharacteristicPowerState,
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",
.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 {
uint16_t svc_handle;
FlipperGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount];
} BatterySvc;
static BatterySvc* battery_svc = NULL;
void battery_svc_start() { void battery_svc_start() {
battery_svc = malloc(sizeof(BatterySvc)); battery_svc = malloc(sizeof(BatterySvc));
@ -85,19 +58,53 @@ void battery_svc_start() {
if(status) { if(status) {
FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); FURI_LOG_E(TAG, "Failed to add Battery service: %d", status);
} }
for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { // Add Battery level characteristic
flipper_gatt_characteristic_init( status = aci_gatt_add_char(
battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]); battery_svc->svc_handle,
UUID_TYPE_16,
(Char_UUID_t*)&battery_level_char_uuid,
1,
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&battery_svc->battery_level_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status);
} }
// Add Power state characteristic
status = aci_gatt_add_char(
battery_svc->svc_handle,
UUID_TYPE_16,
(Char_UUID_t*)&power_state_char_uuid,
1,
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&battery_svc->power_state_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status);
}
// Update power state charachteristic
battery_svc_update_power_state(); battery_svc_update_power_state();
} }
void battery_svc_stop() { void battery_svc_stop() {
tBleStatus status; tBleStatus status;
if(battery_svc) { if(battery_svc) {
for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { // Delete Battery level characteristic
flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]); status =
aci_gatt_del_char(battery_svc->svc_handle, battery_svc->battery_level_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status);
}
// Delete Power state characteristic
status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->power_state_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status);
} }
// Delete Battery service // Delete Battery service
status = aci_gatt_del_service(battery_svc->svc_handle); status = aci_gatt_del_service(battery_svc->svc_handle);
@ -119,10 +126,13 @@ bool battery_svc_update_level(uint8_t battery_charge) {
return false; return false;
} }
// Update battery level characteristic // Update battery level characteristic
return flipper_gatt_characteristic_update( FURI_LOG_D(TAG, "Updating battery level characteristic");
battery_svc->svc_handle, tBleStatus result = aci_gatt_update_char_value(
&battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel], battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge);
&battery_charge); if(result) {
FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result);
}
return result != BLE_STATUS_SUCCESS;
} }
bool battery_svc_update_power_state() { bool battery_svc_update_power_state() {
@ -142,9 +152,15 @@ bool battery_svc_update_power_state() {
power_state.charging = BatterySvcPowerStateNotCharging; power_state.charging = BatterySvcPowerStateNotCharging;
power_state.discharging = BatterySvcPowerStateDischarging; power_state.discharging = BatterySvcPowerStateDischarging;
} }
FURI_LOG_D(TAG, "Updating power state characteristic");
return flipper_gatt_characteristic_update( tBleStatus result = aci_gatt_update_char_value(
battery_svc->svc_handle, battery_svc->svc_handle,
&battery_svc->chars[BatterySvcGattCharacteristicPowerState], battery_svc->power_state_char_handle,
&power_state); 0,
1,
(uint8_t*)&power_state);
if(result) {
FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result);
}
return result != BLE_STATUS_SUCCESS;
} }

View File

@ -33,51 +33,6 @@ static int32_t ble_app_hci_thread(void* context);
static void ble_app_hci_event_handler(void* pPayload); static void ble_app_hci_event_handler(void* pPayload);
static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status);
static const HCI_TL_HciInitConf_t hci_tl_config = {
.p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer,
.StatusNotCallBack = ble_app_hci_status_not_handler,
};
static const SHCI_C2_CONFIG_Cmd_Param_t config_param = {
.PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE,
.Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM,
.BleNvmRamAddress = (uint32_t)ble_app_nvm,
.EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE,
};
static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
.Header = {{0, 0, 0}}, // Header unused
.Param = {
.pBleBufferAddress = 0, // pBleBufferAddress not used
.BleBufferSize = 0, // BleBufferSize not used
.NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES,
.NumAttrServ = CFG_BLE_NUM_GATT_SERVICES,
.AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE,
.NumOfLinks = CFG_BLE_NUM_LINK,
.ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION,
.PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE,
.MblockCount = CFG_BLE_MBLOCK_COUNT,
.AttMtu = CFG_BLE_MAX_ATT_MTU,
.SlaveSca = CFG_BLE_SLAVE_SCA,
.MasterSca = CFG_BLE_MASTER_SCA,
.LsSource = CFG_BLE_LSE_SOURCE,
.MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH,
.HsStartupTime = CFG_BLE_HSE_STARTUP_TIME,
.ViterbiEnable = CFG_BLE_VITERBI_MODE,
.Options = CFG_BLE_OPTIONS,
.HwVersion = 0,
.max_coc_initiator_nbr = 32,
.min_tx_power = 0,
.max_tx_power = 0,
.rx_model_config = 1,
/* New stack (13.3->15.0) */
.max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set
.max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set
.tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB
.rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB
.ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3)
}};
bool ble_app_init() { bool ble_app_init() {
SHCI_CmdStatus_t status; SHCI_CmdStatus_t status;
ble_app = malloc(sizeof(BleApp)); ble_app = malloc(sizeof(BleApp));
@ -89,16 +44,58 @@ bool ble_app_init() {
furi_thread_start(ble_app->thread); furi_thread_start(ble_app->thread);
// Initialize Ble Transport Layer // Initialize Ble Transport Layer
HCI_TL_HciInitConf_t hci_tl_config = {
.p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer,
.StatusNotCallBack = ble_app_hci_status_not_handler,
};
hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config);
// Configure NVM store for pairing data // Configure NVM store for pairing data
status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param); SHCI_C2_CONFIG_Cmd_Param_t config_param = {
.PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE,
.Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM,
.BleNvmRamAddress = (uint32_t)ble_app_nvm,
.EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE,
};
status = SHCI_C2_Config(&config_param);
if(status) { if(status) {
FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status);
} }
// Start ble stack on 2nd core // Start ble stack on 2nd core
status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet); SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
.Header = {{0, 0, 0}}, // Header unused
.Param = {
.pBleBufferAddress = 0, // pBleBufferAddress not used
.BleBufferSize = 0, // BleBufferSize not used
.NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES,
.NumAttrServ = CFG_BLE_NUM_GATT_SERVICES,
.AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE,
.NumOfLinks = CFG_BLE_NUM_LINK,
.ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION,
.PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE,
.MblockCount = CFG_BLE_MBLOCK_COUNT,
.AttMtu = CFG_BLE_MAX_ATT_MTU,
.SlaveSca = CFG_BLE_SLAVE_SCA,
.MasterSca = CFG_BLE_MASTER_SCA,
.LsSource = CFG_BLE_LSE_SOURCE,
.MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH,
.HsStartupTime = CFG_BLE_HSE_STARTUP_TIME,
.ViterbiEnable = CFG_BLE_VITERBI_MODE,
.Options = CFG_BLE_OPTIONS,
.HwVersion = 0,
.max_coc_initiator_nbr = 32,
.min_tx_power = 0,
.max_tx_power = 0,
.rx_model_config = 1,
/* New stack (13.3->15.0) */
.max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set
.max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set
.tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB
.rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB
.ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3)
}};
status = SHCI_C2_BLE_Init(&ble_init_cmd_packet);
if(status) { if(status) {
FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); FURI_LOG_E(TAG, "Failed to start ble stack: %d", status);
} }

View File

@ -0,0 +1,220 @@
#include "dev_info_service.h"
#include "app_common.h"
#include <ble/ble.h>
#include <furi.h>
#include <protobuf_version.h>
#include <lib/toolbox/version.h>
#define TAG "BtDevInfoSvc"
typedef struct {
uint16_t service_handle;
uint16_t man_name_char_handle;
uint16_t serial_num_char_handle;
uint16_t firmware_rev_char_handle;
uint16_t software_rev_char_handle;
uint16_t rpc_version_char_handle;
FuriString* version_string;
char hardware_revision[4];
} DevInfoSvc;
static DevInfoSvc* dev_info_svc = NULL;
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 const uint8_t dev_info_rpc_version_uuid[] =
{0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03};
void dev_info_svc_start() {
dev_info_svc = malloc(sizeof(DevInfoSvc));
dev_info_svc->version_string = furi_string_alloc_printf(
"%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;
// 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, 11, &dev_info_svc->service_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status);
}
// Add characteristics
uuid = MANUFACTURER_NAME_UUID;
status = aci_gatt_add_char(
dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_man_name),
CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->man_name_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add manufacturer name char: %d", status);
}
uuid = SERIAL_NUMBER_UUID;
status = aci_gatt_add_char(
dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_serial_num),
CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->serial_num_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add serial number char: %d", status);
}
uuid = FIRMWARE_REVISION_UUID;
status = aci_gatt_add_char(
dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_svc->hardware_revision),
CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->firmware_rev_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add firmware revision char: %d", status);
}
uuid = SOFTWARE_REVISION_UUID;
status = aci_gatt_add_char(
dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
furi_string_size(dev_info_svc->version_string),
CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->software_rev_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add software revision char: %d", status);
}
status = aci_gatt_add_char(
dev_info_svc->service_handle,
UUID_TYPE_128,
(const Char_UUID_t*)dev_info_rpc_version_uuid,
strlen(dev_info_rpc_version),
CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->rpc_version_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add rpc version characteristic: %d", status);
}
// Update characteristics
status = aci_gatt_update_char_value(
dev_info_svc->service_handle,
dev_info_svc->man_name_char_handle,
0,
strlen(dev_info_man_name),
(uint8_t*)dev_info_man_name);
if(status) {
FURI_LOG_E(TAG, "Failed to update manufacturer name char: %d", status);
}
status = aci_gatt_update_char_value(
dev_info_svc->service_handle,
dev_info_svc->serial_num_char_handle,
0,
strlen(dev_info_serial_num),
(uint8_t*)dev_info_serial_num);
if(status) {
FURI_LOG_E(TAG, "Failed to update serial number char: %d", status);
}
status = aci_gatt_update_char_value(
dev_info_svc->service_handle,
dev_info_svc->firmware_rev_char_handle,
0,
strlen(dev_info_svc->hardware_revision),
(uint8_t*)dev_info_svc->hardware_revision);
if(status) {
FURI_LOG_E(TAG, "Failed to update firmware revision char: %d", status);
}
status = aci_gatt_update_char_value(
dev_info_svc->service_handle,
dev_info_svc->software_rev_char_handle,
0,
furi_string_size(dev_info_svc->version_string),
(uint8_t*)furi_string_get_cstr(dev_info_svc->version_string));
if(status) {
FURI_LOG_E(TAG, "Failed to update software revision char: %d", status);
}
status = aci_gatt_update_char_value(
dev_info_svc->service_handle,
dev_info_svc->rpc_version_char_handle,
0,
strlen(dev_info_rpc_version),
(uint8_t*)dev_info_rpc_version);
if(status) {
FURI_LOG_E(TAG, "Failed to update rpc version char: %d", status);
}
}
void dev_info_svc_stop() {
tBleStatus status;
if(dev_info_svc) {
furi_string_free(dev_info_svc->version_string);
// Delete service characteristics
status =
aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete manufacturer name char: %d", status);
}
status =
aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete serial number char: %d", status);
}
status = aci_gatt_del_char(
dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete firmware revision char: %d", status);
}
status = aci_gatt_del_char(
dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete software revision char: %d", status);
}
status =
aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->rpc_version_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete rpc version char: %d", status);
}
// 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);
}
free(dev_info_svc);
dev_info_svc = NULL;
}
}
bool dev_info_svc_is_started() {
return dev_info_svc != NULL;
}

View File

@ -0,0 +1,416 @@
#include "hid_service.h"
#include "app_common.h"
#include <ble/ble.h>
#include <furi.h>
#define TAG "BtHid"
typedef struct {
uint16_t svc_handle;
uint16_t protocol_mode_char_handle;
uint16_t report_char_handle[HID_SVC_REPORT_COUNT];
uint16_t report_ref_desc_handle[HID_SVC_REPORT_COUNT];
uint16_t report_map_char_handle;
uint16_t info_char_handle;
uint16_t ctrl_point_char_handle;
// led state
uint16_t led_state_char_handle;
uint16_t led_state_desc_handle;
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) {
// 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->led_state_char_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->led_state_char_handle,
req->Data_Length,
req->Data);
ret = SVCCTL_EvtAckFlowEnable;
}
}
}
return ret;
}
void hid_svc_start() {
tBleStatus status;
hid_svc = malloc(sizeof(HIDSvc));
Service_UUID_t svc_uuid = {};
Char_Desc_Uuid_t desc_uuid = {};
Char_UUID_t char_uuid = {};
// Register event handler
SVCCTL_RegisterSvcHandler(hid_svc_event_handler);
// Add service
svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID;
/**
* Add Human Interface Device Service
*/
status = aci_gatt_add_service(
UUID_TYPE_16,
&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);
}
// Add Protocol mode characteristics
char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
1,
CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP,
ATTR_PERMISSION_NONE,
GATT_NOTIFY_ATTRIBUTE_WRITE,
10,
CHAR_VALUE_LEN_CONSTANT,
&hid_svc->protocol_mode_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add protocol mode characteristic: %d", status);
}
// Update Protocol mode characteristic
uint8_t protocol_mode = 1;
status = aci_gatt_update_char_value(
hid_svc->svc_handle, hid_svc->protocol_mode_char_handle, 0, 1, &protocol_mode);
if(status) {
FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status);
}
#if(HID_SVC_REPORT_COUNT != 0)
for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) {
if(i < HID_SVC_INPUT_REPORT_COUNT) { //-V547
uint8_t buf[2] = {i + 1, 1}; // 1 input
char_uuid.Char_UUID_16 = REPORT_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_REPORT_MAX_LEN,
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_VARIABLE,
&(hid_svc->report_char_handle[i]));
if(status) {
FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status);
}
desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID;
status = aci_gatt_add_char_desc(
hid_svc->svc_handle,
hid_svc->report_char_handle[i],
UUID_TYPE_16,
&desc_uuid,
HID_SVC_REPORT_REF_LEN,
HID_SVC_REPORT_REF_LEN,
buf,
ATTR_PERMISSION_NONE,
ATTR_ACCESS_READ_WRITE,
GATT_DONT_NOTIFY_EVENTS,
MIN_ENCRY_KEY_SIZE,
CHAR_VALUE_LEN_CONSTANT,
&(hid_svc->report_ref_desc_handle[i]));
if(status) {
FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status);
}
} else if((i - HID_SVC_INPUT_REPORT_COUNT) < HID_SVC_OUTPUT_REPORT_COUNT) {
uint8_t buf[2] = {i + 1, 2}; // 2 output
char_uuid.Char_UUID_16 = REPORT_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_REPORT_MAX_LEN,
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_VARIABLE,
&(hid_svc->report_char_handle[i]));
if(status) {
FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status);
}
desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID;
status = aci_gatt_add_char_desc(
hid_svc->svc_handle,
hid_svc->report_char_handle[i],
UUID_TYPE_16,
&desc_uuid,
HID_SVC_REPORT_REF_LEN,
HID_SVC_REPORT_REF_LEN,
buf,
ATTR_PERMISSION_NONE,
ATTR_ACCESS_READ_WRITE,
GATT_DONT_NOTIFY_EVENTS,
MIN_ENCRY_KEY_SIZE,
CHAR_VALUE_LEN_CONSTANT,
&(hid_svc->report_ref_desc_handle[i]));
if(status) {
FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status);
}
} else {
uint8_t buf[2] = {i + 1, 3}; // 3 feature
char_uuid.Char_UUID_16 = REPORT_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_REPORT_MAX_LEN,
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_VARIABLE,
&(hid_svc->report_char_handle[i]));
if(status) {
FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status);
}
desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID;
status = aci_gatt_add_char_desc(
hid_svc->svc_handle,
hid_svc->report_char_handle[i],
UUID_TYPE_16,
&desc_uuid,
HID_SVC_REPORT_REF_LEN,
HID_SVC_REPORT_REF_LEN,
buf,
ATTR_PERMISSION_NONE,
ATTR_ACCESS_READ_WRITE,
GATT_DONT_NOTIFY_EVENTS,
MIN_ENCRY_KEY_SIZE,
CHAR_VALUE_LEN_CONSTANT,
&(hid_svc->report_ref_desc_handle[i]));
if(status) {
FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status);
}
}
}
#endif
// Add led state output report
char_uuid.Char_UUID_16 = REPORT_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
1,
CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE,
ATTR_PERMISSION_NONE,
GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,
10,
CHAR_VALUE_LEN_CONSTANT,
&(hid_svc->led_state_char_handle));
if(status) {
FURI_LOG_E(TAG, "Failed to add led state characteristic: %d", status);
}
// Add led state char descriptor specifying it is an output report
uint8_t buf[2] = {HID_SVC_REPORT_COUNT + 1, 2};
desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID;
status = aci_gatt_add_char_desc(
hid_svc->svc_handle,
hid_svc->led_state_char_handle,
UUID_TYPE_16,
&desc_uuid,
HID_SVC_REPORT_REF_LEN,
HID_SVC_REPORT_REF_LEN,
buf,
ATTR_PERMISSION_NONE,
ATTR_ACCESS_READ_WRITE,
GATT_DONT_NOTIFY_EVENTS,
MIN_ENCRY_KEY_SIZE,
CHAR_VALUE_LEN_CONSTANT,
&(hid_svc->led_state_desc_handle));
if(status) {
FURI_LOG_E(TAG, "Failed to add led state descriptor: %d", status);
}
// Add Report Map characteristic
char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_REPORT_MAP_MAX_LEN,
CHAR_PROP_READ,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_VARIABLE,
&hid_svc->report_map_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status);
}
// Add Information characteristic
char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_INFO_LEN,
CHAR_PROP_READ,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&hid_svc->info_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add information characteristic: %d", status);
}
// Add Control Point characteristic
char_uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_CONTROL_POINT_LEN,
CHAR_PROP_WRITE_WITHOUT_RESP,
ATTR_PERMISSION_NONE,
GATT_NOTIFY_ATTRIBUTE_WRITE,
10,
CHAR_VALUE_LEN_CONSTANT,
&hid_svc->ctrl_point_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status);
}
hid_svc->led_state_event_callback = NULL;
hid_svc->led_state_ctx = NULL;
}
bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) {
furi_assert(data);
furi_assert(hid_svc);
tBleStatus status = aci_gatt_update_char_value(
hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data);
if(status) {
FURI_LOG_E(TAG, "Failed updating report map characteristic: %d", status);
return false;
}
return true;
}
bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) {
furi_assert(data);
furi_assert(hid_svc);
tBleStatus status = aci_gatt_update_char_value(
hid_svc->svc_handle, hid_svc->report_char_handle[input_report_num], 0, len, data);
if(status) {
FURI_LOG_E(TAG, "Failed updating report characteristic: %d", status);
return false;
}
return true;
}
bool hid_svc_update_info(uint8_t* data, uint16_t len) {
furi_assert(data);
furi_assert(hid_svc);
tBleStatus status =
aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data);
if(status) {
FURI_LOG_E(TAG, "Failed updating info characteristic: %d", status);
return false;
}
return true;
}
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
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_map_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status);
}
#if(HID_SVC_INPUT_REPORT_COUNT != 0)
for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) {
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle[i]);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status);
}
}
#endif
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status);
}
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status);
}
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->ctrl_point_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status);
}
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->led_state_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete led state characteristic: %d", status);
}
// Delete service
status = aci_gatt_del_service(hid_svc->svc_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete HID service: %d", status);
}
// Delete buffer size mutex
free(hid_svc);
hid_svc = NULL;
}
}

View File

@ -27,7 +27,6 @@ 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); 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, uint16_t len);
bool hid_svc_update_info(uint8_t* data);
void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context);

View File

@ -1,67 +1,17 @@
#include "serial_service.h" #include "serial_service.h"
#include "app_common.h" #include "app_common.h"
#include <ble/ble.h> #include <ble/ble.h>
#include "gatt_char.h"
#include <furi.h> #include <furi.h>
#include "serial_service_uuid.inc"
#define TAG "BtSerialSvc" #define TAG "BtSerialSvc"
typedef enum {
SerialSvcGattCharacteristicTx = 0,
SerialSvcGattCharacteristicRx,
SerialSvcGattCharacteristicFlowCtrl,
SerialSvcGattCharacteristicStatus,
SerialSvcGattCharacteristicCount,
} SerialSvcGattCharacteristicId;
static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = {
[SerialSvcGattCharacteristicTx] =
{.name = "TX",
.data_prop_type = FlipperGattCharacteristicDataFixed,
.data.fixed.length = SERIAL_SVC_DATA_LEN_MAX,
.uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID,
.uuid_type = UUID_TYPE_128,
.char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE,
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
.is_variable = CHAR_VALUE_LEN_VARIABLE},
[SerialSvcGattCharacteristicRx] =
{.name = "RX",
.data_prop_type = FlipperGattCharacteristicDataFixed,
.data.fixed.length = SERIAL_SVC_DATA_LEN_MAX,
.uuid.Char_UUID_128 = SERIAL_SVC_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,
.gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE,
.is_variable = CHAR_VALUE_LEN_VARIABLE},
[SerialSvcGattCharacteristicFlowCtrl] =
{.name = "Flow control",
.data_prop_type = FlipperGattCharacteristicDataFixed,
.data.fixed.length = sizeof(uint32_t),
.uuid.Char_UUID_128 = SERIAL_SVC_FLOW_CONTROL_UUID,
.uuid_type = UUID_TYPE_128,
.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},
[SerialSvcGattCharacteristicStatus] = {
.name = "RPC status",
.data_prop_type = FlipperGattCharacteristicDataFixed,
.data.fixed.length = sizeof(SerialServiceRpcStatus),
.uuid.Char_UUID_128 = SERIAL_SVC_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 { typedef struct {
uint16_t svc_handle; uint16_t svc_handle;
FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount]; uint16_t rx_char_handle;
uint16_t tx_char_handle;
uint16_t flow_ctrl_char_handle;
uint16_t rpc_status_char_handle;
FuriMutex* buff_size_mtx; FuriMutex* buff_size_mtx;
uint32_t buff_size; uint32_t buff_size;
uint16_t bytes_ready_to_receive; uint16_t bytes_ready_to_receive;
@ -71,6 +21,17 @@ typedef struct {
static SerialSvc* serial_svc = NULL; static SerialSvc* serial_svc = NULL;
static const uint8_t service_uuid[] =
{0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f};
static const uint8_t char_tx_uuid[] =
{0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static const uint8_t char_rx_uuid[] =
{0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static const uint8_t flow_ctrl_uuid[] =
{0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static const uint8_t rpc_status_uuid[] =
{0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data);
@ -79,14 +40,11 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data;
if(attribute_modified->Attr_Handle == if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) {
serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) {
// Descriptor handle // Descriptor handle
ret = SVCCTL_EvtAckFlowEnable; ret = SVCCTL_EvtAckFlowEnable;
FURI_LOG_D(TAG, "RX descriptor event"); FURI_LOG_D(TAG, "RX descriptor event");
} else if( } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) {
attribute_modified->Attr_Handle ==
serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) {
FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length);
if(serial_svc->callback) { if(serial_svc->callback) {
furi_check( furi_check(
@ -112,9 +70,7 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk);
} }
ret = SVCCTL_EvtAckFlowEnable; ret = SVCCTL_EvtAckFlowEnable;
} else if( } else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) {
attribute_modified->Attr_Handle ==
serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) {
SerialServiceRpcStatus* rpc_status = SerialServiceRpcStatus* rpc_status =
(SerialServiceRpcStatus*)attribute_modified->Attr_Data; (SerialServiceRpcStatus*)attribute_modified->Attr_Data;
if(*rpc_status == SerialServiceRpcStatusNotActive) { if(*rpc_status == SerialServiceRpcStatusNotActive) {
@ -141,12 +97,18 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
} }
static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) {
flipper_gatt_characteristic_update( tBleStatus ble_status = aci_gatt_update_char_value(
serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &status); serial_svc->svc_handle,
serial_svc->rpc_status_char_handle,
0,
sizeof(SerialServiceRpcStatus),
(uint8_t*)&status);
if(ble_status) {
FURI_LOG_E(TAG, "Failed to update RPC status char: %d", ble_status);
}
} }
void serial_svc_start() { void serial_svc_start() {
UNUSED(serial_svc_chars);
tBleStatus status; tBleStatus status;
serial_svc = malloc(sizeof(SerialSvc)); serial_svc = malloc(sizeof(SerialSvc));
// Register event handler // Register event handler
@ -154,17 +116,72 @@ void serial_svc_start() {
// Add service // Add service
status = aci_gatt_add_service( status = aci_gatt_add_service(
UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle);
if(status) { if(status) {
FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); FURI_LOG_E(TAG, "Failed to add Serial service: %d", status);
} }
// Add characteristics // Add RX characteristics
for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { status = aci_gatt_add_char(
flipper_gatt_characteristic_init( serial_svc->svc_handle,
serial_svc->svc_handle, &serial_svc_chars[i], &serial_svc->chars[i]); UUID_TYPE_128,
(const Char_UUID_t*)char_rx_uuid,
SERIAL_SVC_DATA_LEN_MAX,
CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE,
GATT_NOTIFY_ATTRIBUTE_WRITE,
10,
CHAR_VALUE_LEN_VARIABLE,
&serial_svc->rx_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add RX characteristic: %d", status);
} }
// Add TX characteristic
status = aci_gatt_add_char(
serial_svc->svc_handle,
UUID_TYPE_128,
(const Char_UUID_t*)char_tx_uuid,
SERIAL_SVC_DATA_LEN_MAX,
CHAR_PROP_READ | CHAR_PROP_INDICATE,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_VARIABLE,
&serial_svc->tx_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add TX characteristic: %d", status);
}
// Add Flow Control characteristic
status = aci_gatt_add_char(
serial_svc->svc_handle,
UUID_TYPE_128,
(const Char_UUID_t*)flow_ctrl_uuid,
sizeof(uint32_t),
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&serial_svc->flow_ctrl_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status);
}
// Add RPC status characteristic
status = aci_gatt_add_char(
serial_svc->svc_handle,
UUID_TYPE_128,
(const Char_UUID_t*)rpc_status_uuid,
sizeof(SerialServiceRpcStatus),
CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE,
GATT_NOTIFY_ATTRIBUTE_WRITE,
10,
CHAR_VALUE_LEN_CONSTANT,
&serial_svc->rpc_status_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add RPC status characteristic: %d", status);
}
serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive); serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive);
// Allocate buffer size mutex // Allocate buffer size mutex
serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal);
@ -179,12 +196,13 @@ void serial_svc_set_callbacks(
serial_svc->context = context; serial_svc->context = context;
serial_svc->buff_size = buff_size; serial_svc->buff_size = buff_size;
serial_svc->bytes_ready_to_receive = buff_size; serial_svc->bytes_ready_to_receive = buff_size;
uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size);
flipper_gatt_characteristic_update( aci_gatt_update_char_value(
serial_svc->svc_handle, serial_svc->svc_handle,
&serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], serial_svc->flow_ctrl_char_handle,
&buff_size_reversed); 0,
sizeof(uint32_t),
(uint8_t*)&buff_size_reversed);
} }
void serial_svc_notify_buffer_is_empty() { void serial_svc_notify_buffer_is_empty() {
@ -195,12 +213,13 @@ void serial_svc_notify_buffer_is_empty() {
if(serial_svc->bytes_ready_to_receive == 0) { if(serial_svc->bytes_ready_to_receive == 0) {
FURI_LOG_D(TAG, "Buffer is empty. Notifying client"); FURI_LOG_D(TAG, "Buffer is empty. Notifying client");
serial_svc->bytes_ready_to_receive = serial_svc->buff_size; serial_svc->bytes_ready_to_receive = serial_svc->buff_size;
uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size);
flipper_gatt_characteristic_update( aci_gatt_update_char_value(
serial_svc->svc_handle, serial_svc->svc_handle,
&serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], serial_svc->flow_ctrl_char_handle,
&buff_size_reversed); 0,
sizeof(uint32_t),
(uint8_t*)&buff_size_reversed);
} }
furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk);
} }
@ -208,8 +227,22 @@ void serial_svc_notify_buffer_is_empty() {
void serial_svc_stop() { void serial_svc_stop() {
tBleStatus status; tBleStatus status;
if(serial_svc) { if(serial_svc) {
for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { // Delete characteristics
flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]); status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete TX characteristic: %d", status);
}
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete RX characteristic: %d", status);
}
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status);
}
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rpc_status_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete RPC Status characteristic: %d", status);
} }
// Delete service // Delete service
status = aci_gatt_del_service(serial_svc->svc_handle); status = aci_gatt_del_service(serial_svc->svc_handle);
@ -240,7 +273,7 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) {
tBleStatus result = aci_gatt_update_char_value_ext( tBleStatus result = aci_gatt_update_char_value_ext(
0, 0,
serial_svc->svc_handle, serial_svc->svc_handle,
serial_svc->chars[SerialSvcGattCharacteristicTx].handle, serial_svc->tx_char_handle,
remained ? 0x00 : 0x02, remained ? 0x00 : 0x02,
data_len, data_len,
value_offset, value_offset,

View File

@ -1,176 +0,0 @@
#include "dev_info_service.h"
#include "app_common.h"
#include "gatt_char.h"
#include <ble/ble.h>
#include <furi.h>
#include <protobuf_version.h>
#include <lib/toolbox/version.h>
#include "dev_info_service_uuid.inc"
#define TAG "BtDevInfoSvc"
typedef enum {
DevInfoSvcGattCharacteristicMfgName = 0,
DevInfoSvcGattCharacteristicSerial,
DevInfoSvcGattCharacteristicFirmwareRev,
DevInfoSvcGattCharacteristicSoftwareRev,
DevInfoSvcGattCharacteristicRpcVersion,
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;
static DevInfoSvc* dev_info_svc = NULL;
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 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 = sizeof(dev_info_svc->hardware_revision);
if(data) {
*data = (const uint8_t*)&dev_info_svc->hardware_revision;
}
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] =
{[DevInfoSvcGattCharacteristicMfgName] =
{.name = "Manufacturer Name",
.data_prop_type = FlipperGattCharacteristicDataFixed,
.data.fixed.length = sizeof(dev_info_man_name) - 1,
.data.fixed.ptr = (const uint8_t*)&dev_info_man_name,
.uuid.Char_UUID_16 = MANUFACTURER_NAME_UUID,
.uuid_type = UUID_TYPE_16,
.char_properties = CHAR_PROP_READ,
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
.is_variable = CHAR_VALUE_LEN_CONSTANT},
[DevInfoSvcGattCharacteristicSerial] =
{.name = "Serial Number",
.data_prop_type = FlipperGattCharacteristicDataFixed,
.data.fixed.length = sizeof(dev_info_serial_num) - 1,
.data.fixed.ptr = (const uint8_t*)&dev_info_serial_num,
.uuid.Char_UUID_16 = SERIAL_NUMBER_UUID,
.uuid_type = UUID_TYPE_16,
.char_properties = CHAR_PROP_READ,
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
.is_variable = CHAR_VALUE_LEN_CONSTANT},
[DevInfoSvcGattCharacteristicFirmwareRev] =
{.name = "Firmware Revision",
.data_prop_type = FlipperGattCharacteristicDataCallback,
.data.callback.context = &dev_info_svc,
.data.callback.fn = dev_info_char_firmware_rev_callback,
.uuid.Char_UUID_16 = FIRMWARE_REVISION_UUID,
.uuid_type = UUID_TYPE_16,
.char_properties = CHAR_PROP_READ,
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
.is_variable = CHAR_VALUE_LEN_CONSTANT},
[DevInfoSvcGattCharacteristicSoftwareRev] =
{.name = "Software Revision",
.data_prop_type = FlipperGattCharacteristicDataCallback,
.data.callback.context = &dev_info_svc,
.data.callback.fn = dev_info_char_software_rev_callback,
.uuid.Char_UUID_16 = SOFTWARE_REVISION_UUID,
.uuid_type = UUID_TYPE_16,
.char_properties = CHAR_PROP_READ,
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
.is_variable = CHAR_VALUE_LEN_CONSTANT},
[DevInfoSvcGattCharacteristicRpcVersion] = {
.name = "RPC Version",
.data_prop_type = FlipperGattCharacteristicDataFixed,
.data.fixed.length = sizeof(dev_info_rpc_version) - 1,
.data.fixed.ptr = (const uint8_t*)&dev_info_rpc_version,
.uuid.Char_UUID_128 = DEV_INVO_RPC_VERSION_UID,
.uuid_type = UUID_TYPE_128,
.char_properties = CHAR_PROP_READ,
.security_permissions = ATTR_PERMISSION_AUTHEN_READ,
.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(
"%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;
// 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);
}
for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) {
flipper_gatt_characteristic_init(
dev_info_svc->service_handle,
&dev_info_svc_chars[i],
&dev_info_svc->characteristics[i]);
flipper_gatt_characteristic_update(
dev_info_svc->service_handle, &dev_info_svc->characteristics[i], NULL);
}
}
void dev_info_svc_stop() {
tBleStatus status;
if(dev_info_svc) {
furi_string_free(dev_info_svc->version_string);
// 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);
}
free(dev_info_svc);
dev_info_svc = NULL;
}
}
bool dev_info_svc_is_started() {
return dev_info_svc != NULL;
}

View File

@ -1,3 +0,0 @@
#define DEV_INVO_RPC_VERSION_UID \
{ 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 }

View File

@ -1,123 +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
// TODO: only copy if really comes from stack
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,
GATT_MIN_READ_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;
}

View File

@ -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

View File

@ -1,365 +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 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));
Service_UUID_t svc_uuid = {};
// Register event handler
SVCCTL_RegisterSvcHandler(hid_svc_event_handler);
// Add service
svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID;
/**
* Add Human Interface Device Service
*/
status = aci_gatt_add_service(
UUID_TYPE_16,
&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;
}
}

View File

@ -1,12 +0,0 @@
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 \
{ 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 }
#define SERIAL_SVC_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 \
{ 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 }
#define SERIAL_SVC_RPC_STATUS_UUID \
{ 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 }

View File

@ -8,7 +8,8 @@
#include <furi_hal_bt_hid.h> #include <furi_hal_bt_hid.h>
#include <furi_hal_bt_serial.h> #include <furi_hal_bt_serial.h>
#include <furi_hal_bus.c> #include <furi_hal_bus.c>
#include <services/battery_service.h> #include "battery_service.h"
#include <furi.h> #include <furi.h>
#define TAG "FuriHalBt" #define TAG "FuriHalBt"

View File

@ -1,11 +1,11 @@
#include <furi_hal_bt_hid.h> #include <furi_hal_bt_hid.h>
#include <furi_hal_usb_hid.h> #include <furi_hal_usb_hid.h>
#include <services/dev_info_service.h> #include "usb_hid.h"
#include <services/battery_service.h> #include "dev_info_service.h"
#include <services/hid_service.h> #include "battery_service.h"
#include "hid_service.h"
#include <furi.h> #include <furi.h>
#include <usb_hid.h>
#define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101) #define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101)
#define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00) #define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00)
@ -220,7 +220,7 @@ void furi_hal_bt_hid_start() {
FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK | FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK |
FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK, FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK,
}; };
hid_svc_update_info(hid_info_val); hid_svc_update_info(hid_info_val, sizeof(hid_info_val));
} }
void furi_hal_bt_hid_stop() { void furi_hal_bt_hid_stop() {

View File

@ -1,7 +1,7 @@
#include <furi_hal_bt_serial.h> #include <furi_hal_bt_serial.h>
#include <services/dev_info_service.h> #include "dev_info_service.h"
#include <services/battery_service.h> #include "battery_service.h"
#include <services/serial_service.h> #include "serial_service.h"
#include <furi.h> #include <furi.h>

View File

@ -1,6 +1,5 @@
#include <furi_hal_clock.h> #include <furi_hal_clock.h>
#include <furi_hal_resources.h> #include <furi_hal_resources.h>
#include <furi_hal_rtc.h>
#include <furi.h> #include <furi.h>
#include <stm32wbxx_ll_pwr.h> #include <stm32wbxx_ll_pwr.h>
@ -143,7 +142,10 @@ void furi_hal_clock_switch_to_hsi() {
} }
void furi_hal_clock_switch_to_pll() { void furi_hal_clock_switch_to_pll() {
#ifdef FURI_HAL_CLOCK_TRACK_STARTUP
uint32_t clock_start_time = DWT->CYCCNT; uint32_t clock_start_time = DWT->CYCCNT;
#endif
LL_RCC_HSE_Enable(); LL_RCC_HSE_Enable();
LL_RCC_PLL_Enable(); LL_RCC_PLL_Enable();
LL_RCC_PLLSAI1_Enable(); LL_RCC_PLLSAI1_Enable();
@ -166,11 +168,12 @@ void furi_hal_clock_switch_to_pll() {
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
; ;
#ifdef FURI_HAL_CLOCK_TRACK_STARTUP
uint32_t total = DWT->CYCCNT - clock_start_time; uint32_t total = DWT->CYCCNT - clock_start_time;
if(total > (20 * 0x148)) { if(total > (20 * 0x148)) {
furi_hal_rtc_set_flag(FuriHalRtcFlagLegacySleep);
furi_crash("Slow HSE/PLL startup"); furi_crash("Slow HSE/PLL startup");
} }
#endif
} }
void furi_hal_clock_suspend_tick() { void furi_hal_clock_suspend_tick() {

View File

@ -8,7 +8,7 @@
#include <furi.h> #include <furi.h>
#include <stdbool.h> #include <stdbool.h>
#include <gap.h> #include <gap.h>
#include <services/serial_service.h> #include <serial_service.h>
#include <ble_glue.h> #include <ble_glue.h>
#include <ble_app.h> #include <ble_app.h>

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <services/serial_service.h> #include "serial_service.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {