mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 06:54:19 +00:00
Merge branch 'ofw-dev' into dev
This commit is contained in:
commit
18220515b5
38 changed files with 1206 additions and 929 deletions
|
@ -171,7 +171,7 @@ distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_
|
|||
|
||||
fap_deploy = distenv.PhonyTarget(
|
||||
"fap_deploy",
|
||||
"${PYTHON3} ${ROOT_DIR}/scripts/storage.py send ${SOURCE} /ext/apps",
|
||||
"${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send ${SOURCE} /ext/apps",
|
||||
source=Dir("#/assets/resources/apps"),
|
||||
)
|
||||
|
||||
|
@ -323,7 +323,9 @@ distenv.PhonyTarget(
|
|||
)
|
||||
|
||||
# Start Flipper CLI via PySerial's miniterm
|
||||
distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py")
|
||||
distenv.PhonyTarget(
|
||||
"cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}"
|
||||
)
|
||||
|
||||
|
||||
# Find blackmagic probe
|
||||
|
|
|
@ -50,7 +50,7 @@ static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) {
|
|||
widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61);
|
||||
widget_add_string_multiline_element(
|
||||
widget, 87, 13, AlignCenter, AlignTop, FontPrimary, "Emulating\nNFC V");
|
||||
if(strcmp(nfc->dev->dev_name, "")) {
|
||||
if(strcmp(nfc->dev->dev_name, "") != 0) {
|
||||
furi_string_printf(info_str, "%s", nfc->dev->dev_name);
|
||||
} else {
|
||||
for(uint8_t i = 0; i < data->uid_len; i++) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "bt_i.h"
|
||||
#include "battery_service.h"
|
||||
#include "bt_keys_storage.h"
|
||||
|
||||
#include <services/battery_service.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <gui/elements.h>
|
||||
#include <assets_icons.h>
|
||||
|
|
|
@ -196,14 +196,14 @@ static void APPD_SetCPU2GpioConfig(void) {
|
|||
gpio_config.Pin = gpiob_pin_list;
|
||||
LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB);
|
||||
LL_GPIO_Init(GPIOB, &gpio_config);
|
||||
LL_GPIO_ResetOutputPin(GPIOB, gpioa_pin_list);
|
||||
LL_GPIO_ResetOutputPin(GPIOB, gpiob_pin_list);
|
||||
}
|
||||
|
||||
if(gpioc_pin_list != 0) {
|
||||
gpio_config.Pin = gpioc_pin_list;
|
||||
LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC);
|
||||
LL_GPIO_Init(GPIOC, &gpio_config);
|
||||
LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list);
|
||||
LL_GPIO_ResetOutputPin(GPIOC, gpioc_pin_list);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,51 @@ static int32_t ble_app_hci_thread(void* context);
|
|||
static void ble_app_hci_event_handler(void* pPayload);
|
||||
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() {
|
||||
SHCI_CmdStatus_t status;
|
||||
ble_app = malloc(sizeof(BleApp));
|
||||
|
@ -44,58 +89,16 @@ bool ble_app_init() {
|
|||
furi_thread_start(ble_app->thread);
|
||||
|
||||
// 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);
|
||||
|
||||
// Configure NVM store for pairing data
|
||||
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);
|
||||
status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status);
|
||||
}
|
||||
|
||||
// Start ble stack on 2nd core
|
||||
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);
|
||||
status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to start ble stack: %d", status);
|
||||
}
|
||||
|
|
|
@ -1,220 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -1,416 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
#include "battery_service.h"
|
||||
#include "app_common.h"
|
||||
#include "gatt_char.h"
|
||||
|
||||
#include <ble/ble.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
@ -7,12 +9,6 @@
|
|||
|
||||
#define TAG "BtBatterySvc"
|
||||
|
||||
typedef struct {
|
||||
uint16_t svc_handle;
|
||||
uint16_t battery_level_char_handle;
|
||||
uint16_t power_state_char_handle;
|
||||
} BatterySvc;
|
||||
|
||||
enum {
|
||||
// Common states
|
||||
BatterySvcPowerStateUnknown = 0b00,
|
||||
|
@ -40,13 +36,44 @@ typedef struct {
|
|||
|
||||
_Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size");
|
||||
|
||||
static BatterySvc* battery_svc = NULL;
|
||||
|
||||
#define BATTERY_POWER_STATE (0x2A1A)
|
||||
|
||||
static const uint16_t service_uuid = BATTERY_SERVICE_UUID;
|
||||
static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID;
|
||||
static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE;
|
||||
|
||||
typedef enum {
|
||||
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() {
|
||||
battery_svc = malloc(sizeof(BatterySvc));
|
||||
|
@ -58,53 +85,19 @@ void battery_svc_start() {
|
|||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add Battery service: %d", status);
|
||||
}
|
||||
// Add Battery level characteristic
|
||||
status = aci_gatt_add_char(
|
||||
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);
|
||||
for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) {
|
||||
flipper_gatt_characteristic_init(
|
||||
battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]);
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
|
||||
void battery_svc_stop() {
|
||||
tBleStatus status;
|
||||
if(battery_svc) {
|
||||
// Delete Battery level characteristic
|
||||
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);
|
||||
for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) {
|
||||
flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]);
|
||||
}
|
||||
// Delete Battery service
|
||||
status = aci_gatt_del_service(battery_svc->svc_handle);
|
||||
|
@ -126,13 +119,10 @@ bool battery_svc_update_level(uint8_t battery_charge) {
|
|||
return false;
|
||||
}
|
||||
// Update battery level characteristic
|
||||
FURI_LOG_D(TAG, "Updating battery level characteristic");
|
||||
tBleStatus result = aci_gatt_update_char_value(
|
||||
battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge);
|
||||
if(result) {
|
||||
FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result);
|
||||
}
|
||||
return result != BLE_STATUS_SUCCESS;
|
||||
return flipper_gatt_characteristic_update(
|
||||
battery_svc->svc_handle,
|
||||
&battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel],
|
||||
&battery_charge);
|
||||
}
|
||||
|
||||
bool battery_svc_update_power_state() {
|
||||
|
@ -152,15 +142,9 @@ bool battery_svc_update_power_state() {
|
|||
power_state.charging = BatterySvcPowerStateNotCharging;
|
||||
power_state.discharging = BatterySvcPowerStateDischarging;
|
||||
}
|
||||
FURI_LOG_D(TAG, "Updating power state characteristic");
|
||||
tBleStatus result = aci_gatt_update_char_value(
|
||||
|
||||
return flipper_gatt_characteristic_update(
|
||||
battery_svc->svc_handle,
|
||||
battery_svc->power_state_char_handle,
|
||||
0,
|
||||
1,
|
||||
(uint8_t*)&power_state);
|
||||
if(result) {
|
||||
FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result);
|
||||
}
|
||||
return result != BLE_STATUS_SUCCESS;
|
||||
&battery_svc->chars[BatterySvcGattCharacteristicPowerState],
|
||||
&power_state);
|
||||
}
|
176
firmware/targets/f7/ble_glue/services/dev_info_service.c
Normal file
176
firmware/targets/f7/ble_glue/services/dev_info_service.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#define DEV_INVO_RPC_VERSION_UID \
|
||||
{ 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 }
|
||||
|
123
firmware/targets/f7/ble_glue/services/gatt_char.c
Normal file
123
firmware/targets/f7/ble_glue/services/gatt_char.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
#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;
|
||||
}
|
96
firmware/targets/f7/ble_glue/services/gatt_char.h
Normal file
96
firmware/targets/f7/ble_glue/services/gatt_char.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
#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
|
293
firmware/targets/f7/ble_glue/services/hid_service.c
Normal file
293
firmware/targets/f7/ble_glue/services/hid_service.c
Normal file
|
@ -0,0 +1,293 @@
|
|||
#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,
|
||||
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;
|
||||
}
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
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];
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
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, /* Service + Report Map + HID Information + HID Control Point */
|
||||
&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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -15,8 +15,6 @@
|
|||
#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();
|
||||
|
@ -27,6 +25,5 @@ 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_info(uint8_t* data, uint16_t len);
|
||||
|
||||
void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context);
|
||||
// Expects data to be of length HID_SVC_INFO_LEN (4 bytes)
|
||||
bool hid_svc_update_info(uint8_t* data);
|
|
@ -1,17 +1,67 @@
|
|||
#include "serial_service.h"
|
||||
#include "app_common.h"
|
||||
#include <ble/ble.h>
|
||||
#include "gatt_char.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include "serial_service_uuid.inc"
|
||||
|
||||
#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 {
|
||||
uint16_t svc_handle;
|
||||
uint16_t rx_char_handle;
|
||||
uint16_t tx_char_handle;
|
||||
uint16_t flow_ctrl_char_handle;
|
||||
uint16_t rpc_status_char_handle;
|
||||
FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount];
|
||||
FuriMutex* buff_size_mtx;
|
||||
uint32_t buff_size;
|
||||
uint16_t bytes_ready_to_receive;
|
||||
|
@ -21,17 +71,6 @@ typedef struct {
|
|||
|
||||
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) {
|
||||
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
|
||||
hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data);
|
||||
|
@ -40,11 +79,14 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
|||
if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
|
||||
if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
|
||||
attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data;
|
||||
if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) {
|
||||
if(attribute_modified->Attr_Handle ==
|
||||
serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) {
|
||||
// Descriptor handle
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
FURI_LOG_D(TAG, "RX descriptor event");
|
||||
} else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) {
|
||||
} else if(
|
||||
attribute_modified->Attr_Handle ==
|
||||
serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) {
|
||||
FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length);
|
||||
if(serial_svc->callback) {
|
||||
furi_check(
|
||||
|
@ -70,7 +112,9 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
|||
furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk);
|
||||
}
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
} else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) {
|
||||
} else if(
|
||||
attribute_modified->Attr_Handle ==
|
||||
serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) {
|
||||
SerialServiceRpcStatus* rpc_status =
|
||||
(SerialServiceRpcStatus*)attribute_modified->Attr_Data;
|
||||
if(*rpc_status == SerialServiceRpcStatusNotActive) {
|
||||
|
@ -97,18 +141,12 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
|||
}
|
||||
|
||||
static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) {
|
||||
tBleStatus ble_status = aci_gatt_update_char_value(
|
||||
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);
|
||||
}
|
||||
flipper_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
|
||||
|
@ -116,72 +154,17 @@ void serial_svc_start() {
|
|||
|
||||
// Add service
|
||||
status = aci_gatt_add_service(
|
||||
UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle);
|
||||
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);
|
||||
}
|
||||
|
||||
// Add RX characteristics
|
||||
status = aci_gatt_add_char(
|
||||
serial_svc->svc_handle,
|
||||
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 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]);
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Allocate buffer size mutex
|
||||
serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
@ -196,13 +179,12 @@ void serial_svc_set_callbacks(
|
|||
serial_svc->context = context;
|
||||
serial_svc->buff_size = buff_size;
|
||||
serial_svc->bytes_ready_to_receive = buff_size;
|
||||
|
||||
uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size);
|
||||
aci_gatt_update_char_value(
|
||||
flipper_gatt_characteristic_update(
|
||||
serial_svc->svc_handle,
|
||||
serial_svc->flow_ctrl_char_handle,
|
||||
0,
|
||||
sizeof(uint32_t),
|
||||
(uint8_t*)&buff_size_reversed);
|
||||
&serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl],
|
||||
&buff_size_reversed);
|
||||
}
|
||||
|
||||
void serial_svc_notify_buffer_is_empty() {
|
||||
|
@ -213,13 +195,12 @@ void serial_svc_notify_buffer_is_empty() {
|
|||
if(serial_svc->bytes_ready_to_receive == 0) {
|
||||
FURI_LOG_D(TAG, "Buffer is empty. Notifying client");
|
||||
serial_svc->bytes_ready_to_receive = serial_svc->buff_size;
|
||||
|
||||
uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size);
|
||||
aci_gatt_update_char_value(
|
||||
flipper_gatt_characteristic_update(
|
||||
serial_svc->svc_handle,
|
||||
serial_svc->flow_ctrl_char_handle,
|
||||
0,
|
||||
sizeof(uint32_t),
|
||||
(uint8_t*)&buff_size_reversed);
|
||||
&serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl],
|
||||
&buff_size_reversed);
|
||||
}
|
||||
furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk);
|
||||
}
|
||||
|
@ -227,22 +208,8 @@ void serial_svc_notify_buffer_is_empty() {
|
|||
void serial_svc_stop() {
|
||||
tBleStatus status;
|
||||
if(serial_svc) {
|
||||
// Delete characteristics
|
||||
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);
|
||||
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);
|
||||
|
@ -273,7 +240,7 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) {
|
|||
tBleStatus result = aci_gatt_update_char_value_ext(
|
||||
0,
|
||||
serial_svc->svc_handle,
|
||||
serial_svc->tx_char_handle,
|
||||
serial_svc->chars[SerialSvcGattCharacteristicTx].handle,
|
||||
remained ? 0x00 : 0x02,
|
||||
data_len,
|
||||
value_offset,
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
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 }
|
|
@ -8,8 +8,7 @@
|
|||
#include <furi_hal_bt_hid.h>
|
||||
#include <furi_hal_bt_serial.h>
|
||||
#include <furi_hal_bus.c>
|
||||
#include "battery_service.h"
|
||||
|
||||
#include <services/battery_service.h>
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "FuriHalBt"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include <furi_hal_bt_hid.h>
|
||||
#include <furi_hal_usb_hid.h>
|
||||
#include "usb_hid.h"
|
||||
#include "dev_info_service.h"
|
||||
#include "battery_service.h"
|
||||
#include "hid_service.h"
|
||||
#include <services/dev_info_service.h>
|
||||
#include <services/battery_service.h>
|
||||
#include <services/hid_service.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <usb_hid.h>
|
||||
|
||||
#define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101)
|
||||
#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_NORMALLY_CONNECTABLE_MSK,
|
||||
};
|
||||
hid_svc_update_info(hid_info_val, sizeof(hid_info_val));
|
||||
hid_svc_update_info(hid_info_val);
|
||||
}
|
||||
|
||||
void furi_hal_bt_hid_stop() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <furi_hal_bt_serial.h>
|
||||
#include "dev_info_service.h"
|
||||
#include "battery_service.h"
|
||||
#include "serial_service.h"
|
||||
#include <services/dev_info_service.h>
|
||||
#include <services/battery_service.h>
|
||||
#include <services/serial_service.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <furi.h>
|
||||
#include <stdbool.h>
|
||||
#include <gap.h>
|
||||
#include <serial_service.h>
|
||||
#include <services/serial_service.h>
|
||||
#include <ble_glue.h>
|
||||
#include <ble_app.h>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "serial_service.h"
|
||||
#include <services/serial_service.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <stm32wbxx_ll_tim.h>
|
||||
|
||||
/* must be on bank B */
|
||||
//#define DEBUG_OUTPUT gpio_ext_pb3
|
||||
// For debugging purposes use `--extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3` fbt option
|
||||
|
||||
struct ReloadBuffer {
|
||||
uint32_t* buffer; /* DMA ringbuffer */
|
||||
|
@ -194,9 +194,9 @@ void digital_signal_prepare_arr(DigitalSignal* signal) {
|
|||
uint32_t bit_set = internals->gpio->pin;
|
||||
uint32_t bit_reset = internals->gpio->pin << 16;
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
bit_set |= DEBUG_OUTPUT.pin;
|
||||
bit_reset |= DEBUG_OUTPUT.pin << 16;
|
||||
#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN
|
||||
bit_set |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin;
|
||||
bit_reset |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << 16;
|
||||
#endif
|
||||
|
||||
if(signal->start_level) {
|
||||
|
@ -544,8 +544,9 @@ bool digital_sequence_send(DigitalSequence* sequence) {
|
|||
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
|
||||
|
||||
furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
#ifdef DEBUG_OUTPUT
|
||||
furi_hal_gpio_init(&DEBUG_OUTPUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN
|
||||
furi_hal_gpio_init(
|
||||
&DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
#endif
|
||||
|
||||
if(sequence->bake) {
|
||||
|
|
|
@ -808,7 +808,7 @@ static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) {
|
|||
return saved;
|
||||
}
|
||||
|
||||
bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) {
|
||||
bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { // -V524
|
||||
bool parsed = false;
|
||||
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
|
||||
memset(data, 0, sizeof(NfcVSlixData));
|
||||
|
|
|
@ -301,10 +301,10 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) {
|
|||
ret = slix_unlock(nfcv_data, 4);
|
||||
} else {
|
||||
key = 0x7FFD6E5B;
|
||||
key_data[0] = key >> 24;
|
||||
key_data[1] = key >> 16;
|
||||
key_data[2] = key >> 8;
|
||||
key_data[3] = key >> 0;
|
||||
key_data[0] = (key >> 24) & 0xFF;
|
||||
key_data[1] = (key >> 16) & 0xFF;
|
||||
key_data[2] = (key >> 8) & 0xFF;
|
||||
key_data[3] = (key >> 0) & 0xFF;
|
||||
ret = slix_unlock(nfcv_data, 4);
|
||||
|
||||
if(ret != ERR_NONE) {
|
||||
|
@ -321,10 +321,10 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) {
|
|||
}
|
||||
|
||||
key = 0x0F0F0F0F;
|
||||
key_data[0] = key >> 24;
|
||||
key_data[1] = key >> 16;
|
||||
key_data[2] = key >> 8;
|
||||
key_data[3] = key >> 0;
|
||||
key_data[0] = (key >> 24) & 0xFF;
|
||||
key_data[1] = (key >> 16) & 0xFF;
|
||||
key_data[2] = (key >> 8) & 0xFF;
|
||||
key_data[3] = (key >> 0) & 0xFF;
|
||||
ret = slix_unlock(nfcv_data, 4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) {
|
|||
uint16_t received = 0;
|
||||
for(size_t block = 0; block < nfcv_data->block_num; block++) {
|
||||
uint8_t rxBuf[32];
|
||||
//FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1));
|
||||
FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1));
|
||||
|
||||
ReturnCode ret = ERR_NONE;
|
||||
for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) {
|
||||
|
@ -64,18 +64,18 @@ ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) {
|
|||
}
|
||||
}
|
||||
if(ret != ERR_NONE) {
|
||||
//FURI_LOG_D(TAG, "failed to read: %d", ret);
|
||||
FURI_LOG_D(TAG, "failed to read: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
memcpy(
|
||||
&(nfcv_data->data[block * nfcv_data->block_size]), &rxBuf[1], nfcv_data->block_size);
|
||||
/*FURI_LOG_D(
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
" %02X %02X %02X %02X",
|
||||
nfcv_data->data[block * nfcv_data->block_size + 0],
|
||||
nfcv_data->data[block * nfcv_data->block_size + 1],
|
||||
nfcv_data->data[block * nfcv_data->block_size + 2],
|
||||
nfcv_data->data[block * nfcv_data->block_size + 3]);*/
|
||||
nfcv_data->data[block * nfcv_data->block_size + 3]);
|
||||
}
|
||||
|
||||
return ERR_NONE;
|
||||
|
@ -86,7 +86,7 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
|
|||
uint16_t received = 0;
|
||||
ReturnCode ret = ERR_NONE;
|
||||
|
||||
//FURI_LOG_D(TAG, "Read SYSTEM INFORMATION...");
|
||||
FURI_LOG_D(TAG, "Read SYSTEM INFORMATION...");
|
||||
|
||||
for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) {
|
||||
/* TODO: needs proper abstraction via fury_hal(_ll)_* */
|
||||
|
@ -110,7 +110,7 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
|
|||
nfcv_data->block_num = rxBuf[NFCV_UID_LENGTH + 4] + 1;
|
||||
nfcv_data->block_size = rxBuf[NFCV_UID_LENGTH + 5] + 1;
|
||||
nfcv_data->ic_ref = rxBuf[NFCV_UID_LENGTH + 6];
|
||||
/*FURI_LOG_D(
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
" UID: %02X %02X %02X %02X %02X %02X %02X %02X",
|
||||
nfc_data->uid[0],
|
||||
|
@ -128,10 +128,10 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
|
|||
nfcv_data->afi,
|
||||
nfcv_data->block_num,
|
||||
nfcv_data->block_size,
|
||||
nfcv_data->ic_ref);*/
|
||||
nfcv_data->ic_ref);
|
||||
return ret;
|
||||
}
|
||||
//FURI_LOG_D(TAG, "Failed: %d", ret);
|
||||
FURI_LOG_D(TAG, "Failed: %d", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -438,7 +438,7 @@ void nfcv_emu_send(
|
|||
furi_assert(nfcv);
|
||||
|
||||
/* picked default value (0) to match the most common format */
|
||||
if(!flags) {
|
||||
if(flags == NfcVSendFlagsNormal) {
|
||||
flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof |
|
||||
NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate;
|
||||
}
|
||||
|
@ -1084,9 +1084,9 @@ void nfcv_emu_sniff_packet(
|
|||
break;
|
||||
}
|
||||
|
||||
/*if(strlen(nfcv_data->last_command) > 0) {
|
||||
if(strlen(nfcv_data->last_command) > 0) {
|
||||
FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
|
||||
|
@ -1140,7 +1140,7 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
|
|||
}
|
||||
}
|
||||
|
||||
/*FURI_LOG_D(TAG, "Starting NfcV emulation");
|
||||
FURI_LOG_D(TAG, "Starting NfcV emulation");
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
" UID: %02X %02X %02X %02X %02X %02X %02X %02X",
|
||||
|
@ -1151,7 +1151,7 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
|
|||
nfc_data->uid[4],
|
||||
nfc_data->uid[5],
|
||||
nfc_data->uid[6],
|
||||
nfc_data->uid[7]);*/
|
||||
nfc_data->uid[7]);
|
||||
|
||||
switch(nfcv_data->sub_type) {
|
||||
case NfcVTypeSlixL:
|
||||
|
@ -1326,7 +1326,7 @@ bool nfcv_emu_loop(
|
|||
bits_received += 2;
|
||||
|
||||
if(periods == 1) {
|
||||
byte_value |= 0x00 << 6;
|
||||
byte_value |= 0x00 << 6; // -V684
|
||||
periods_previous = 6;
|
||||
} else if(periods == 3) {
|
||||
byte_value |= 0x01 << 6;
|
||||
|
|
|
@ -381,7 +381,7 @@ void slix_prepare(NfcVData* nfcv_data) {
|
|||
ctx->emu_protocol_filter = &slix_protocol_filter;
|
||||
}
|
||||
|
||||
bool slix2_protocol_filter(
|
||||
bool slix2_protocol_filter( // -V524
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
FuriHalNfcDevData* nfc_data,
|
||||
void* nfcv_data_in) {
|
||||
|
|
|
@ -132,7 +132,7 @@ def generate(env):
|
|||
"UsbInstall": Builder(
|
||||
action=[
|
||||
Action(
|
||||
'${PYTHON3} "${SELFUPDATE_SCRIPT}" ${UPDATE_BUNDLE_DIR}/update.fuf'
|
||||
'${PYTHON3} "${SELFUPDATE_SCRIPT}" -p ${FLIP_PORT} ${UPDATE_BUNDLE_DIR}/update.fuf'
|
||||
),
|
||||
Touch("${TARGET}"),
|
||||
]
|
||||
|
|
|
@ -431,7 +431,7 @@ def AddAppLaunchTarget(env, appname, launch_target_name):
|
|||
# print(deploy_sources, flipp_dist_paths)
|
||||
env.PhonyTarget(
|
||||
launch_target_name,
|
||||
'${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}',
|
||||
'${PYTHON3} "${APP_RUN_SCRIPT}" -p ${FLIP_PORT} ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}',
|
||||
source=deploy_sources,
|
||||
FLIPPER_FILE_TARGETS=flipp_dist_paths,
|
||||
EXTRA_ARGS=run_script_extra_ars,
|
||||
|
@ -443,7 +443,6 @@ def generate(env, **kw):
|
|||
env.SetDefault(
|
||||
EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}",
|
||||
APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py",
|
||||
STORAGE_SCRIPT="${FBT_SCRIPT_DIR}/storage.py",
|
||||
)
|
||||
if not env["VERBOSE"]:
|
||||
env.SetDefault(
|
||||
|
|
|
@ -257,12 +257,12 @@ class FlipperStorage:
|
|||
self.read.until(self.CLI_PROMPT)
|
||||
|
||||
ftell = file.tell()
|
||||
percent = str(math.ceil(ftell / filesize * 100))
|
||||
total_chunks = str(math.ceil(filesize / buffer_size))
|
||||
current_chunk = str(math.ceil(ftell / buffer_size))
|
||||
percent = math.ceil(ftell / filesize * 100)
|
||||
total_chunks = math.ceil(filesize / buffer_size)
|
||||
current_chunk = math.ceil(ftell / buffer_size)
|
||||
approx_speed = ftell / (time.time() - start_time + 0.0001)
|
||||
sys.stdout.write(
|
||||
f"\r{percent}%, chunk {current_chunk} of {total_chunks} @ {approx_speed/1024:.2f} kb/s"
|
||||
f"\r<{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s"
|
||||
)
|
||||
sys.stdout.flush()
|
||||
print()
|
||||
|
@ -270,6 +270,7 @@ class FlipperStorage:
|
|||
def read_file(self, filename: str):
|
||||
"""Receive file from Flipper, and get filedata (bytes)"""
|
||||
buffer_size = self.chunk_size
|
||||
start_time = time.time()
|
||||
self.send_and_wait_eol(
|
||||
'storage read_chunks "' + filename + '" ' + str(buffer_size) + "\r"
|
||||
)
|
||||
|
@ -290,10 +291,13 @@ class FlipperStorage:
|
|||
filedata.extend(self.port.read(chunk_size))
|
||||
read_size = read_size + chunk_size
|
||||
|
||||
percent = str(math.ceil(read_size / size * 100))
|
||||
total_chunks = str(math.ceil(size / buffer_size))
|
||||
current_chunk = str(math.ceil(read_size / buffer_size))
|
||||
sys.stdout.write(f"\r{percent}%, chunk {current_chunk} of {total_chunks}")
|
||||
percent = math.ceil(read_size / size * 100)
|
||||
total_chunks = math.ceil(size / buffer_size)
|
||||
current_chunk = math.ceil(read_size / buffer_size)
|
||||
approx_speed = read_size / (time.time() - start_time + 0.0001)
|
||||
sys.stdout.write(
|
||||
f"\r>{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s"
|
||||
)
|
||||
sys.stdout.flush()
|
||||
print()
|
||||
self.read.until(self.CLI_PROMPT)
|
||||
|
|
|
@ -6,7 +6,7 @@ def resolve_port(logger, portname: str = "auto"):
|
|||
if portname != "auto":
|
||||
return portname
|
||||
# Try guessing
|
||||
flippers = list(list_ports.grep("flip"))
|
||||
flippers = list(list_ports.grep("flip_"))
|
||||
if len(flippers) == 1:
|
||||
flipper = flippers[0]
|
||||
logger.info(f"Using {flipper.serial_number} on {flipper.device}")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
@ -8,7 +9,10 @@ from flipper.utils.cdc import resolve_port
|
|||
|
||||
def main():
|
||||
logger = logging.getLogger()
|
||||
if not (port := resolve_port(logger, "auto")):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-p", "--port", help="CDC Port", default="auto")
|
||||
args = parser.parse_args()
|
||||
if not (port := resolve_port(logger, args.port)):
|
||||
logger.error("Is Flipper connected via USB and not in DFU mode?")
|
||||
return 1
|
||||
subprocess.call(
|
||||
|
|
|
@ -342,7 +342,7 @@ else:
|
|||
|
||||
appenv.PhonyTarget(
|
||||
"cli",
|
||||
'${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py"',
|
||||
'${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}',
|
||||
)
|
||||
|
||||
# Linter
|
||||
|
@ -469,7 +469,7 @@ if dolphin_src_dir.exists():
|
|||
)
|
||||
dist_env.PhonyTarget(
|
||||
"dolphin_ext",
|
||||
'${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py send "${SOURCE}" /ext/dolphin',
|
||||
'${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send "${SOURCE}" /ext/dolphin',
|
||||
source=ufbt_build_dir.Dir("dolphin"),
|
||||
)
|
||||
else:
|
||||
|
|
|
@ -71,6 +71,11 @@ vars.AddVariables(
|
|||
validator=PathVariable.PathIsDir,
|
||||
default="",
|
||||
),
|
||||
(
|
||||
"FLIP_PORT",
|
||||
"CDC Port of Flipper to use, if multiple are connected",
|
||||
"auto",
|
||||
),
|
||||
)
|
||||
|
||||
Return("vars")
|
||||
|
|
240
scripts/wifi_board.py
Executable file
240
scripts/wifi_board.py
Executable file
|
@ -0,0 +1,240 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from flipper.app import App
|
||||
from serial.tools.list_ports_common import ListPortInfo
|
||||
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import subprocess
|
||||
import serial.tools.list_ports as list_ports
|
||||
import json
|
||||
import requests
|
||||
import tarfile
|
||||
|
||||
|
||||
class UpdateDownloader:
|
||||
UPDATE_SERVER = "https://update.flipperzero.one"
|
||||
UPDATE_PROJECT = "/blackmagic-firmware"
|
||||
UPDATE_INDEX = UPDATE_SERVER + UPDATE_PROJECT + "/directory.json"
|
||||
UPDATE_TYPE = "full_tgz"
|
||||
|
||||
CHANNEL_ID_ALIAS = {
|
||||
"dev": "development",
|
||||
"rc": "release-candidate",
|
||||
"r": "release",
|
||||
"rel": "release",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger()
|
||||
|
||||
def download(self, channel_id: str, dir: str) -> bool:
|
||||
# Aliases
|
||||
if channel_id in self.CHANNEL_ID_ALIAS:
|
||||
channel_id = self.CHANNEL_ID_ALIAS[channel_id]
|
||||
|
||||
# Make directory
|
||||
if not os.path.exists(dir):
|
||||
self.logger.info(f"Creating directory {dir}")
|
||||
os.makedirs(dir)
|
||||
|
||||
# Download json index
|
||||
self.logger.info(f"Downloading {self.UPDATE_INDEX}")
|
||||
response = requests.get(self.UPDATE_INDEX)
|
||||
if response.status_code != 200:
|
||||
self.logger.error(f"Failed to download {self.UPDATE_INDEX}")
|
||||
return False
|
||||
|
||||
# Parse json index
|
||||
try:
|
||||
index = json.loads(response.content)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to parse json index: {e}")
|
||||
return False
|
||||
|
||||
# Find channel
|
||||
channel = None
|
||||
for channel_candidate in index["channels"]:
|
||||
if channel_candidate["id"] == channel_id:
|
||||
channel = channel_candidate
|
||||
break
|
||||
|
||||
# Check if channel found
|
||||
if channel is None:
|
||||
self.logger.error(
|
||||
f"Channel '{channel_id}' not found. Valid channels: {', '.join([c['id'] for c in index['channels']])}"
|
||||
)
|
||||
return False
|
||||
|
||||
self.logger.info(f"Using channel '{channel_id}'")
|
||||
|
||||
# Get latest version
|
||||
try:
|
||||
version = channel["versions"][0]
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to get version: {e}")
|
||||
return False
|
||||
|
||||
self.logger.info(f"Using version '{version['version']}'")
|
||||
|
||||
# Get changelog
|
||||
changelog = None
|
||||
try:
|
||||
changelog = version["changelog"]
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to get changelog: {e}")
|
||||
|
||||
# print changelog
|
||||
if changelog is not None:
|
||||
self.logger.info(f"Changelog:")
|
||||
for line in changelog.split("\n"):
|
||||
if line.strip() == "":
|
||||
continue
|
||||
self.logger.info(f" {line}")
|
||||
|
||||
# Find file
|
||||
file_url = None
|
||||
for file_candidate in version["files"]:
|
||||
if file_candidate["type"] == self.UPDATE_TYPE:
|
||||
file_url = file_candidate["url"]
|
||||
break
|
||||
|
||||
if file_url is None:
|
||||
self.logger.error(f"File not found")
|
||||
return False
|
||||
|
||||
# Make file path
|
||||
file_name = file_url.split("/")[-1]
|
||||
file_path = os.path.join(dir, file_name)
|
||||
|
||||
# Download file
|
||||
self.logger.info(f"Downloading {file_url} to {file_path}")
|
||||
with open(file_path, "wb") as f:
|
||||
response = requests.get(file_url)
|
||||
f.write(response.content)
|
||||
|
||||
# Unzip tgz
|
||||
self.logger.info(f"Unzipping {file_path}")
|
||||
with tarfile.open(file_path, "r") as tar:
|
||||
tar.extractall(dir)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class Main(App):
|
||||
def init(self):
|
||||
self.parser.add_argument("-p", "--port", help="CDC Port", default="auto")
|
||||
self.parser.add_argument(
|
||||
"-c", "--channel", help="Channel name", default="release"
|
||||
)
|
||||
self.parser.set_defaults(func=self.update)
|
||||
|
||||
# logging
|
||||
self.logger = logging.getLogger()
|
||||
|
||||
def find_wifi_board(self) -> bool:
|
||||
# idk why, but python thinks that list_ports.grep returns tuple[str, str, str]
|
||||
blackmagics: list[ListPortInfo] = list(list_ports.grep("blackmagic")) # type: ignore
|
||||
daps: list[ListPortInfo] = list(list_ports.grep("CMSIS-DAP")) # type: ignore
|
||||
|
||||
return len(blackmagics) > 0 or len(daps) > 0
|
||||
|
||||
def find_wifi_board_bootloader(self):
|
||||
# idk why, but python thinks that list_ports.grep returns tuple[str, str, str]
|
||||
ports: list[ListPortInfo] = list(list_ports.grep("ESP32-S2")) # type: ignore
|
||||
|
||||
if len(ports) == 0:
|
||||
# Blackmagic probe serial port not found, will be handled later
|
||||
pass
|
||||
elif len(ports) > 1:
|
||||
raise Exception("More than one WiFi board found")
|
||||
else:
|
||||
port = ports[0]
|
||||
if os.name == "nt":
|
||||
port.device = f"\\\\.\\{port.device}"
|
||||
return port.device
|
||||
|
||||
def update(self):
|
||||
try:
|
||||
port = self.find_wifi_board_bootloader()
|
||||
except Exception as e:
|
||||
self.logger.error(f"{e}")
|
||||
return 1
|
||||
|
||||
if self.args.port != "auto":
|
||||
port = self.args.port
|
||||
|
||||
available_ports = [p[0] for p in list(list_ports.comports())]
|
||||
if port not in available_ports:
|
||||
self.logger.error(f"Port {port} not found")
|
||||
return 1
|
||||
|
||||
if port is None:
|
||||
if self.find_wifi_board():
|
||||
self.logger.error("WiFi board found, but not in bootloader mode.")
|
||||
self.logger.info("Please hold down BOOT button and press RESET button")
|
||||
else:
|
||||
self.logger.error("WiFi board not found")
|
||||
self.logger.info(
|
||||
"Please connect WiFi board to your computer, hold down BOOT button and press RESET button"
|
||||
)
|
||||
return 1
|
||||
|
||||
# get temporary dir
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
downloader = UpdateDownloader()
|
||||
|
||||
# download latest channel update
|
||||
try:
|
||||
if not downloader.download(self.args.channel, temp_dir):
|
||||
self.logger.error(f"Cannot download update")
|
||||
return 1
|
||||
except Exception as e:
|
||||
self.logger.error(f"Cannot download update: {e}")
|
||||
return 1
|
||||
|
||||
with open(os.path.join(temp_dir, "flash.command"), "r") as f:
|
||||
flash_command = f.read()
|
||||
|
||||
flash_command = flash_command.replace("\n", "").replace("\r", "")
|
||||
flash_command = flash_command.replace("(PORT)", port)
|
||||
|
||||
# We can't reset the board after flashing via usb
|
||||
flash_command = flash_command.replace(
|
||||
"--after hard_reset", "--after no_reset_stub"
|
||||
)
|
||||
|
||||
args = flash_command.split(" ")[0:]
|
||||
args = list(filter(None, args))
|
||||
|
||||
esptool_params = []
|
||||
esptool_params.extend(args)
|
||||
|
||||
self.logger.info(f'Running command: "{" ".join(args)}" in "{temp_dir}"')
|
||||
|
||||
process = subprocess.Popen(
|
||||
esptool_params,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=temp_dir,
|
||||
bufsize=1,
|
||||
universal_newlines=True,
|
||||
)
|
||||
|
||||
while process.poll() is None:
|
||||
if process.stdout is not None:
|
||||
for line in process.stdout:
|
||||
self.logger.debug(f"{line.strip()}")
|
||||
|
||||
if process.returncode != 0:
|
||||
self.logger.error(f"Failed to flash WiFi board")
|
||||
else:
|
||||
self.logger.info("WiFi board flashed successfully")
|
||||
self.logger.info("Press RESET button on WiFi board to start it")
|
||||
|
||||
return process.returncode
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Main()()
|
|
@ -248,6 +248,11 @@ vars.AddVariables(
|
|||
" app can check what version it is being built for.",
|
||||
"Official",
|
||||
),
|
||||
(
|
||||
"FLIP_PORT",
|
||||
"Full port name of Flipper to use, if multiple Flippers are connected",
|
||||
"auto",
|
||||
),
|
||||
)
|
||||
|
||||
Return("vars")
|
||||
|
|
Loading…
Reference in a new issue