Просмотр исходного кода

Fix GUI freeze after NFC classic read (#1074)

* nfc worker: add delay for task switching
* device info service: fix rpc version characteristic
* firmware: move FreeRTOSConfig.h to target include
* dev_info service: fix typo
* ble glue: use osThreadFlags instead of osEventFlags
* Infrared: fix issue with timer lockup
* FuriHal: yeld in flash lock routine
* ble: change connection parameters, display actual params
* ble: verify connection parameters
* ble: fix typo

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
gornekich 3 лет назад
Родитель
Сommit
0bf2702210

+ 1 - 0
applications/nfc/nfc_worker.c

@@ -780,6 +780,7 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
                     }
                 }
                 if(nfc_worker->state != NfcWorkerStateReadMifareClassic) break;
+                osDelay(1);
             }
             if(nfc_worker->state != NfcWorkerStateReadMifareClassic) break;
             if(sector_key_found) {

+ 3 - 1
lib/FreeRTOS-glue/FreeRTOSConfig.h → firmware/targets/f7/Inc/FreeRTOSConfig.h

@@ -77,11 +77,13 @@ to exclude the API function. */
 /* CMSIS-RTOS V2 flags */
 #define configUSE_OS2_THREAD_SUSPEND_RESUME 1
 #define configUSE_OS2_THREAD_ENUMERATE 1
-#define configUSE_OS2_EVENTFLAGS_FROM_ISR 1
 #define configUSE_OS2_THREAD_FLAGS 1
 #define configUSE_OS2_TIMER 1
 #define configUSE_OS2_MUTEX 1
 
+// NEVER TO BE USED, because of their hard realtime nature
+// #define configUSE_OS2_EVENTFLAGS_FROM_ISR 1
+
 /* CMSIS-RTOS */
 #define configTASK_NOTIFICATION_ARRAY_ENTRIES 2
 #define CMSIS_TASK_NOTIFY_INDEX 1

+ 8 - 9
firmware/targets/f7/ble_glue/ble_app.c

@@ -24,7 +24,6 @@ typedef struct {
     osMutexId_t hci_mtx;
     osSemaphoreId_t hci_sem;
     FuriThread* thread;
-    osEventFlagsId_t event_flags;
 } BleApp;
 
 static BleApp* ble_app = NULL;
@@ -39,7 +38,6 @@ bool ble_app_init() {
     // Allocate semafore and mutex for ble command buffer access
     ble_app->hci_mtx = osMutexNew(NULL);
     ble_app->hci_sem = osSemaphoreNew(1, 0, NULL);
-    ble_app->event_flags = osEventFlagsNew(NULL);
     // HCI transport layer thread to handle user asynch events
     ble_app->thread = furi_thread_alloc();
     furi_thread_set_name(ble_app->thread, "BleHciDriver");
@@ -108,15 +106,14 @@ void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size) {
 
 void ble_app_thread_stop() {
     if(ble_app) {
-        osEventFlagsSet(ble_app->event_flags, BLE_APP_FLAG_KILL_THREAD);
+        osThreadId_t thread_id = furi_thread_get_thread_id(ble_app->thread);
+        furi_assert(thread_id);
+        osThreadFlagsSet(thread_id, BLE_APP_FLAG_KILL_THREAD);
         furi_thread_join(ble_app->thread);
         furi_thread_free(ble_app->thread);
-        // Wait to make sure that EventFlags delivers pending events before memory free
-        osDelay(50);
         // Free resources
         osMutexDelete(ble_app->hci_mtx);
         osSemaphoreDelete(ble_app->hci_sem);
-        osEventFlagsDelete(ble_app->event_flags);
         free(ble_app);
         ble_app = NULL;
         memset(&ble_app_cmd_buffer, 0, sizeof(ble_app_cmd_buffer));
@@ -125,9 +122,9 @@ void ble_app_thread_stop() {
 
 static int32_t ble_app_hci_thread(void* arg) {
     uint32_t flags = 0;
+
     while(1) {
-        flags = osEventFlagsWait(
-            ble_app->event_flags, BLE_APP_FLAG_ALL, osFlagsWaitAny, osWaitForever);
+        flags = osThreadFlagsWait(BLE_APP_FLAG_ALL, osFlagsWaitAny, osWaitForever);
         if(flags & BLE_APP_FLAG_KILL_THREAD) {
             break;
         }
@@ -142,7 +139,9 @@ static int32_t ble_app_hci_thread(void* arg) {
 // Called by WPAN lib
 void hci_notify_asynch_evt(void* pdata) {
     if(ble_app) {
-        osEventFlagsSet(ble_app->event_flags, BLE_APP_FLAG_HCI_EVENT);
+        osThreadId_t thread_id = furi_thread_get_thread_id(ble_app->thread);
+        furi_assert(thread_id);
+        osThreadFlagsSet(thread_id, BLE_APP_FLAG_HCI_EVENT);
     }
 }
 

+ 8 - 9
firmware/targets/f7/ble_glue/ble_glue.c

@@ -40,7 +40,6 @@ typedef enum {
 typedef struct {
     osMutexId_t shci_mtx;
     osSemaphoreId_t shci_sem;
-    osEventFlagsId_t event_flags;
     FuriThread* thread;
     BleGlueStatus status;
     BleGlueKeyStorageChangedCallback callback;
@@ -83,7 +82,6 @@ void ble_glue_init() {
 
     ble_glue->shci_mtx = osMutexNew(NULL);
     ble_glue->shci_sem = osSemaphoreNew(1, 0, NULL);
-    ble_glue->event_flags = osEventFlagsNew(NULL);
 
     // FreeRTOS system task creation
     ble_glue->thread = furi_thread_alloc();
@@ -257,15 +255,14 @@ static void ble_glue_clear_shared_memory() {
 
 void ble_glue_thread_stop() {
     if(ble_glue) {
-        osEventFlagsSet(ble_glue->event_flags, BLE_GLUE_FLAG_KILL_THREAD);
+        osThreadId_t thread_id = furi_thread_get_thread_id(ble_glue->thread);
+        furi_assert(thread_id);
+        osThreadFlagsSet(thread_id, BLE_GLUE_FLAG_KILL_THREAD);
         furi_thread_join(ble_glue->thread);
         furi_thread_free(ble_glue->thread);
-        // Wait to make sure that EventFlags delivers pending events before memory free
-        osDelay(50);
         // Free resources
         osMutexDelete(ble_glue->shci_mtx);
         osSemaphoreDelete(ble_glue->shci_sem);
-        osEventFlagsDelete(ble_glue->event_flags);
         ble_glue_clear_shared_memory();
         free(ble_glue);
         ble_glue = NULL;
@@ -275,9 +272,9 @@ void ble_glue_thread_stop() {
 // Wrap functions
 static int32_t ble_glue_shci_thread(void* context) {
     uint32_t flags = 0;
+
     while(true) {
-        flags = osEventFlagsWait(
-            ble_glue->event_flags, BLE_GLUE_FLAG_ALL, osFlagsWaitAny, osWaitForever);
+        flags = osThreadFlagsWait(BLE_GLUE_FLAG_ALL, osFlagsWaitAny, osWaitForever);
         if(flags & BLE_GLUE_FLAG_SHCI_EVENT) {
             shci_user_evt_proc();
         }
@@ -292,7 +289,9 @@ static int32_t ble_glue_shci_thread(void* context) {
 void shci_notify_asynch_evt(void* pdata) {
     UNUSED(pdata);
     if(ble_glue) {
-        osEventFlagsSet(ble_glue->event_flags, BLE_GLUE_FLAG_SHCI_EVENT);
+        osThreadId_t thread_id = furi_thread_get_thread_id(ble_glue->thread);
+        furi_assert(thread_id);
+        osThreadFlagsSet(thread_id, BLE_GLUE_FLAG_SHCI_EVENT);
     }
 }
 

+ 4 - 8
firmware/targets/f7/ble_glue/dev_info_service.c

@@ -8,8 +8,6 @@
 
 #define TAG "BtDevInfoSvc"
 
-#define DEV_INFO_RPC_VERSION_CHAR_MAX_SIZE (10)
-
 typedef struct {
     uint16_t service_handle;
     uint16_t man_name_char_handle;
@@ -26,6 +24,7 @@ static const char dev_info_serial_num[] = "1.0";
 static const char dev_info_firmware_rev_num[] = TOSTRING(TARGET);
 static const char dev_info_software_rev_num[] = GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM
                                                            " " BUILD_DATE;
+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};
@@ -107,7 +106,7 @@ void dev_info_svc_start() {
         dev_info_svc->service_handle,
         UUID_TYPE_128,
         (const Char_UUID_t*)dev_info_rpc_version_uuid,
-        DEV_INFO_RPC_VERSION_CHAR_MAX_SIZE,
+        strlen(dev_info_rpc_version),
         CHAR_PROP_READ,
         ATTR_PERMISSION_AUTHEN_READ,
         GATT_DONT_NOTIFY_EVENTS,
@@ -155,18 +154,15 @@ void dev_info_svc_start() {
     if(status) {
         FURI_LOG_E(TAG, "Failed to update software revision char: %d", status);
     }
-    string_t rpc_version;
-    string_init_printf(rpc_version, "%d.%d", PROTOBUF_MAJOR_VERSION, PROTOBUF_MINOR_VERSION);
     status = aci_gatt_update_char_value(
         dev_info_svc->service_handle,
         dev_info_svc->rpc_version_char_handle,
         0,
-        strlen(string_get_cstr(rpc_version)),
-        (uint8_t*)string_get_cstr(rpc_version));
+        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);
     }
-    string_clear(rpc_version);
 }
 
 void dev_info_svc_stop() {

+ 42 - 22
firmware/targets/f7/ble_glue/gap.c

@@ -10,6 +10,8 @@
 #define FAST_ADV_TIMEOUT 30000
 #define INITIAL_ADV_TIMEOUT 60000
 
+#define GAP_INTERVAL_TO_MS(x) (uint16_t)((x)*1.25)
+
 typedef struct {
     uint16_t gap_svc_handle;
     uint16_t dev_name_char_handle;
@@ -23,6 +25,7 @@ typedef struct {
 typedef struct {
     GapSvc service;
     GapConfig* config;
+    GapConnectionParams connection_params;
     GapState state;
     osMutexId_t state_mutex;
     GapEventCallback on_event_cb;
@@ -58,6 +61,33 @@ static GapScan* gap_scan = NULL;
 static void gap_advertise_start(GapState new_state);
 static int32_t gap_app(void* context);
 
+static void gap_verify_connection_parameters(Gap* gap) {
+    furi_assert(gap);
+
+    FURI_LOG_I(
+        TAG,
+        "Connection parameters: Connection Interval: %d (%d ms), Slave Latency: %d, Supervision Timeout: %d",
+        gap->connection_params.conn_interval,
+        GAP_INTERVAL_TO_MS(gap->connection_params.conn_interval),
+        gap->connection_params.slave_latency,
+        gap->connection_params.supervisor_timeout);
+
+    // Send connection parameters request update if necessary
+    GapConnectionParamsRequest* params = &gap->config->conn_param;
+    if(params->conn_int_min > gap->connection_params.conn_interval ||
+       params->conn_int_max < gap->connection_params.conn_interval) {
+        FURI_LOG_W(TAG, "Unsupported connection interval. Request connection parameters update");
+        if(aci_l2cap_connection_parameter_update_req(
+               gap->service.connection_handle,
+               params->conn_int_min,
+               params->conn_int_max,
+               gap->connection_params.slave_latency,
+               gap->connection_params.supervisor_timeout)) {
+            FURI_LOG_E(TAG, "Failed to request connection parameters update");
+        }
+    }
+}
+
 SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
     hci_event_pckt* event_pckt;
     evt_le_meta_event* meta_evt;
@@ -97,12 +127,11 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
         case EVT_LE_CONN_UPDATE_COMPLETE: {
             hci_le_connection_update_complete_event_rp0* event =
                 (hci_le_connection_update_complete_event_rp0*)meta_evt->data;
-            FURI_LOG_I(
-                TAG,
-                "Connection interval: %d, latency: %d, supervision timeout: %d",
-                event->Conn_Interval,
-                event->Conn_Latency,
-                event->Supervision_Timeout);
+            gap->connection_params.conn_interval = event->Conn_Interval;
+            gap->connection_params.slave_latency = event->Conn_Latency;
+            gap->connection_params.supervisor_timeout = event->Supervision_Timeout;
+            FURI_LOG_I(TAG, "Connection parameters event complete");
+            gap_verify_connection_parameters(gap);
             break;
         }
 
@@ -124,31 +153,22 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
 
         case EVT_LE_CONN_COMPLETE:
             furi_hal_power_insomnia_enter();
-            hci_le_connection_complete_event_rp0* connection_complete_event =
+            hci_le_connection_complete_event_rp0* event =
                 (hci_le_connection_complete_event_rp0*)meta_evt->data;
-            FURI_LOG_I(
-                TAG,
-                "Connection complete for connection handle 0x%x",
-                connection_complete_event->Connection_Handle);
+            gap->connection_params.conn_interval = event->Conn_Interval;
+            gap->connection_params.slave_latency = event->Conn_Latency;
+            gap->connection_params.supervisor_timeout = event->Supervision_Timeout;
 
             // Stop advertising as connection completed
             osTimerStop(gap->advertise_timer);
 
             // Update connection status and handle
             gap->state = GapStateConnected;
-            gap->service.connection_handle = connection_complete_event->Connection_Handle;
-            GapConnectionParams* params = &gap->config->conn_param;
-            if(aci_l2cap_connection_parameter_update_req(
-                   gap->service.connection_handle,
-                   params->conn_int_min,
-                   params->conn_int_max,
-                   params->slave_latency,
-                   params->supervisor_timeout)) {
-                FURI_LOG_W(TAG, "Failed to request connection parameters update");
-            }
+            gap->service.connection_handle = event->Connection_Handle;
 
+            gap_verify_connection_parameters(gap);
             // Start pairing by sending security request
-            aci_gap_slave_security_req(connection_complete_event->Connection_Handle);
+            aci_gap_slave_security_req(event->Connection_Handle);
             break;
 
         case EVT_LE_ADVERTISING_REPORT: {

+ 8 - 2
firmware/targets/f7/ble_glue/gap.h

@@ -55,12 +55,18 @@ typedef enum {
     GapPairingPinCodeVerifyYesNo,
 } GapPairing;
 
+typedef struct {
+    uint16_t conn_interval;
+    uint16_t slave_latency;
+    uint16_t supervisor_timeout;
+} GapConnectionParams;
+
 typedef struct {
     uint16_t conn_int_min;
     uint16_t conn_int_max;
     uint16_t slave_latency;
     uint16_t supervisor_timeout;
-} GapConnectionParams;
+} GapConnectionParamsRequest;
 
 typedef struct {
     uint16_t adv_service_uuid;
@@ -69,7 +75,7 @@ typedef struct {
     GapPairing pairing_method;
     uint8_t mac_address[GAP_MAC_ADDR_SIZE];
     char adv_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH];
-    GapConnectionParams conn_param;
+    GapConnectionParamsRequest conn_param;
 } GapConfig;
 
 bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context);

+ 6 - 7
firmware/targets/f7/furi_hal/furi_hal_bt.c

@@ -44,8 +44,8 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = {
                     .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
                     .conn_param =
                         {
-                            .conn_int_min = 0x08,
-                            .conn_int_max = 0x18,
+                            .conn_int_min = 0x18, // 30 ms
+                            .conn_int_max = 0x24, // 45 ms
                             .slave_latency = 0,
                             .supervisor_timeout = 0,
                         },
@@ -62,13 +62,12 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = {
                     .bonding_mode = true,
                     .pairing_method = GapPairingPinCodeVerifyYesNo,
                     .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
-                    // TODO optimize
                     .conn_param =
                         {
-                            .conn_int_min = 0x12,
-                            .conn_int_max = 0x1e,
-                            .slave_latency = 6,
-                            .supervisor_timeout = 700,
+                            .conn_int_min = 0x18, // 30 ms
+                            .conn_int_max = 0x24, // 45 ms
+                            .slave_latency = 0,
+                            .supervisor_timeout = 0,
                         },
                 },
         },

+ 2 - 6
firmware/targets/f7/furi_hal/furi_hal_flash.c

@@ -124,18 +124,14 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) {
         // Actually we already have mutex for it, but specification is specification
         if(LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) {
             taskEXIT_CRITICAL();
-            continue;
-        }
-
-        //
-        if(LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID)) {
-            taskEXIT_CRITICAL();
+            osThreadYield();
             continue;
         }
 
         // Take sempahopre and prevent core2 from anything funky
         if(LL_HSEM_1StepLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != 0) {
             taskEXIT_CRITICAL();
+            osThreadYield();
             continue;
         }
 

+ 14 - 17
lib/infrared/worker/infrared_worker.c

@@ -51,7 +51,6 @@ struct InfraredWorkerSignal {
 struct InfraredWorker {
     FuriThread* thread;
     StreamBufferHandle_t stream;
-    osEventFlagsId_t events;
 
     InfraredWorkerSignal signal;
     InfraredWorkerState state;
@@ -93,7 +92,8 @@ static void infrared_worker_furi_hal_message_sent_isr_callback(void* context);
 
 static void infrared_worker_rx_timeout_callback(void* context) {
     InfraredWorker* instance = context;
-    uint32_t flags_set = osEventFlagsSet(instance->events, INFRARED_WORKER_RX_TIMEOUT_RECEIVED);
+    uint32_t flags_set = osThreadFlagsSet(
+        furi_thread_get_thread_id(instance->thread), INFRARED_WORKER_RX_TIMEOUT_RECEIVED);
     furi_check(flags_set & INFRARED_WORKER_RX_TIMEOUT_RECEIVED);
 }
 
@@ -110,7 +110,7 @@ static void infrared_worker_rx_callback(void* context, bool level, uint32_t dura
                                                        INFRARED_WORKER_OVERRUN;
     portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
 
-    uint32_t flags_set = osEventFlagsSet(instance->events, events);
+    uint32_t flags_set = osThreadFlagsSet(furi_thread_get_thread_id(instance->thread), events);
     furi_check(flags_set & events);
 }
 
@@ -152,7 +152,8 @@ static void
             instance->signal.timings[instance->signal.timings_cnt] = duration;
             ++instance->signal.timings_cnt;
         } else {
-            uint32_t flags_set = osEventFlagsSet(instance->events, INFRARED_WORKER_OVERRUN);
+            uint32_t flags_set = osThreadFlagsSet(
+                furi_thread_get_thread_id(instance->thread), INFRARED_WORKER_OVERRUN);
             furi_check(flags_set & INFRARED_WORKER_OVERRUN);
             instance->rx.overrun = true;
         }
@@ -166,8 +167,7 @@ static int32_t infrared_worker_rx_thread(void* thread_context) {
     TickType_t last_blink_time = 0;
 
     while(1) {
-        events =
-            osEventFlagsWait(instance->events, INFRARED_WORKER_ALL_RX_EVENTS, 0, osWaitForever);
+        events = osThreadFlagsWait(INFRARED_WORKER_ALL_RX_EVENTS, 0, osWaitForever);
         furi_check(events & INFRARED_WORKER_ALL_RX_EVENTS); /* at least one caught */
 
         if(events & INFRARED_WORKER_RX_RECEIVED) {
@@ -238,7 +238,6 @@ InfraredWorker* infrared_worker_alloc() {
     instance->blink_enable = false;
     instance->notification = furi_record_open("notification");
     instance->state = InfraredWorkerStateIdle;
-    instance->events = osEventFlagsNew(NULL);
 
     return instance;
 }
@@ -252,7 +251,6 @@ void infrared_worker_free(InfraredWorker* instance) {
     infrared_free_encoder(instance->infrared_encoder);
     vStreamBufferDelete(instance->stream);
     furi_thread_free(instance->thread);
-    osEventFlagsDelete(instance->events);
 
     free(instance);
 }
@@ -263,7 +261,6 @@ void infrared_worker_rx_start(InfraredWorker* instance) {
 
     xStreamBufferSetTriggerLevel(instance->stream, sizeof(LevelDuration));
 
-    osEventFlagsClear(instance->events, INFRARED_WORKER_ALL_EVENTS);
     furi_thread_set_callback(instance->thread, infrared_worker_rx_thread);
     furi_thread_start(instance->thread);
 
@@ -285,7 +282,7 @@ void infrared_worker_rx_stop(InfraredWorker* instance) {
     furi_hal_infrared_async_rx_set_capture_isr_callback(NULL, NULL);
     furi_hal_infrared_async_rx_stop();
 
-    osEventFlagsSet(instance->events, INFRARED_WORKER_EXIT);
+    osThreadFlagsSet(furi_thread_get_thread_id(instance->thread), INFRARED_WORKER_EXIT);
     furi_thread_join(instance->thread);
 
     BaseType_t xReturn = xStreamBufferReset(instance->stream);
@@ -330,7 +327,6 @@ void infrared_worker_tx_start(InfraredWorker* instance) {
     // size have to be greater than api hal infrared async tx buffer size
     xStreamBufferSetTriggerLevel(instance->stream, sizeof(InfraredWorkerTiming));
 
-    osEventFlagsClear(instance->events, INFRARED_WORKER_ALL_EVENTS);
     furi_thread_set_callback(instance->thread, infrared_worker_tx_thread);
 
     instance->tx.steady_signal_sent = false;
@@ -346,7 +342,8 @@ void infrared_worker_tx_start(InfraredWorker* instance) {
 
 static void infrared_worker_furi_hal_message_sent_isr_callback(void* context) {
     InfraredWorker* instance = context;
-    uint32_t flags_set = osEventFlagsSet(instance->events, INFRARED_WORKER_TX_MESSAGE_SENT);
+    uint32_t flags_set = osThreadFlagsSet(
+        furi_thread_get_thread_id(instance->thread), INFRARED_WORKER_TX_MESSAGE_SENT);
     furi_check(flags_set & INFRARED_WORKER_TX_MESSAGE_SENT);
 }
 
@@ -372,7 +369,8 @@ static FuriHalInfraredTxGetDataState
         state = FuriHalInfraredTxGetDataStateDone;
     }
 
-    uint32_t flags_set = osEventFlagsSet(instance->events, INFRARED_WORKER_TX_FILL_BUFFER);
+    uint32_t flags_set = osThreadFlagsSet(
+        furi_thread_get_thread_id(instance->thread), INFRARED_WORKER_TX_FILL_BUFFER);
     furi_check(flags_set & INFRARED_WORKER_TX_FILL_BUFFER);
 
     return state;
@@ -500,7 +498,7 @@ static int32_t infrared_worker_tx_thread(void* thread_context) {
             furi_hal_infrared_async_tx_wait_termination();
             instance->state = InfraredWorkerStateStartTx;
 
-            events = osEventFlagsGet(instance->events);
+            events = osThreadFlagsGet();
             if(events & INFRARED_WORKER_EXIT) {
                 exit = true;
                 break;
@@ -508,8 +506,7 @@ static int32_t infrared_worker_tx_thread(void* thread_context) {
 
             break;
         case InfraredWorkerStateRunTx:
-            events = osEventFlagsWait(
-                instance->events, INFRARED_WORKER_ALL_TX_EVENTS, 0, osWaitForever);
+            events = osThreadFlagsWait(INFRARED_WORKER_ALL_TX_EVENTS, 0, osWaitForever);
             furi_check(events & INFRARED_WORKER_ALL_TX_EVENTS); /* at least one caught */
 
             if(events & INFRARED_WORKER_EXIT) {
@@ -561,7 +558,7 @@ void infrared_worker_tx_stop(InfraredWorker* instance) {
     furi_assert(instance);
     furi_assert(instance->state != InfraredWorkerStateRunRx);
 
-    osEventFlagsSet(instance->events, INFRARED_WORKER_EXIT);
+    osThreadFlagsSet(furi_thread_get_thread_id(instance->thread), INFRARED_WORKER_EXIT);
     furi_thread_join(instance->thread);
     furi_hal_infrared_async_tx_set_data_isr_callback(NULL, NULL);
     furi_hal_infrared_async_tx_set_signal_sent_isr_callback(NULL, NULL);