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

Merge remote-tracking branch 'origin/master' into aku_freertos_debug

aanper 5 лет назад
Родитель
Сommit
85dd8629c8

+ 1 - 1
.github/workflows/ci.yml

@@ -59,7 +59,7 @@ jobs:
       - name: Build F2 firmware in docker
         uses: ./.github/actions/docker
         with:
-          run: make -C firmware TARGET=f2
+          run: make -C firmware TARGET=f2 APP_RELEASE=1
 
       - name: Publish F2 firmware artifacts
         uses: actions/upload-artifact@v2

+ 124 - 0
applications/app-loader/app-loader.c

@@ -0,0 +1,124 @@
+#include "flipper_v2.h"
+#include <gui/gui.h>
+#include "menu/menu.h"
+
+typedef struct {
+    FuriApp* handler;
+    Widget* widget;
+    FlipperStartupApp* current_app;
+} AppLoaderState;
+
+typedef struct {
+    AppLoaderState* state;
+    FlipperStartupApp* app;
+} AppLoaderContext;
+
+// TODO add mutex for contex
+
+static void render_callback(CanvasApi* canvas, void* _ctx) {
+    AppLoaderState* ctx = (AppLoaderState*)_ctx;
+
+    canvas->clear(canvas);
+    canvas->set_color(canvas, ColorBlack);
+    canvas->set_font(canvas, FontPrimary);
+    canvas->draw_str(canvas, 2, 32, ctx->current_app->name);
+
+    canvas->set_font(canvas, FontSecondary);
+    canvas->draw_str(canvas, 2, 44, "press back to exit");
+}
+
+static void input_callback(InputEvent* input_event, void* _ctx) {
+    AppLoaderState* ctx = (AppLoaderState*)_ctx;
+
+    if(input_event->state && input_event->input == InputBack) {
+        furiac_kill(ctx->handler);
+        widget_enabled_set(ctx->widget, false);
+    }
+}
+
+static void handle_menu(void* _ctx) {
+    AppLoaderContext* ctx = (AppLoaderContext*)_ctx;
+
+    widget_enabled_set(ctx->state->widget, true);
+
+    // TODO how to call this?
+    // furiac_wait_libs(&FLIPPER_STARTUP[i].libs);
+
+    ctx->state->current_app = ctx->app;
+    ctx->state->handler = furiac_start(ctx->app->app, ctx->app->name, NULL);
+}
+
+void application_blink(void* p);
+void application_uart_write(void* p);
+void application_input_dump(void* p);
+void cc1101_workaround(void* p);
+
+const FlipperStartupApp FLIPPER_APPS[] = {
+    {.app = application_blink, .name = "blink", .libs = {0}},
+    {.app = application_uart_write, .name = "uart write", .libs = {0}},
+    {.app = application_input_dump, .name = "input dump", .libs = {1, FURI_LIB{"input_task"}}},
+    {.app = cc1101_workaround, .name = "cc1101 workaround", .libs = {1, FURI_LIB{"gui_task"}}},
+};
+
+void app_loader(void* p) {
+    osThreadId_t self_id = osThreadGetId();
+    assert(self_id);
+
+    AppLoaderState state;
+    state.handler = NULL;
+
+    state.widget = widget_alloc();
+    assert(state.widget);
+    widget_enabled_set(state.widget, false);
+    widget_draw_callback_set(state.widget, render_callback, &state);
+    widget_input_callback_set(state.widget, input_callback, &state);
+
+    ValueMutex* menu_mutex = furi_open("menu");
+    if(menu_mutex == NULL) {
+        printf("menu is not available\n");
+        furiac_exit(NULL);
+    }
+
+    // Open GUI and register widget
+    GuiApi* gui = furi_open("gui");
+    if(gui == NULL) {
+        printf("gui is not available\n");
+        furiac_exit(NULL);
+    }
+    gui->add_widget(gui, state.widget, WidgetLayerFullscreen);
+
+    {
+        Menu* menu = acquire_mutex_block(menu_mutex);
+
+        // FURI startup
+        const size_t flipper_app_count = sizeof(FLIPPER_APPS) / sizeof(FLIPPER_APPS[0]);
+
+        for(size_t i = 0; i < flipper_app_count; i++) {
+            AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext));
+            ctx->state = &state;
+            ctx->app = &FLIPPER_APPS[i];
+
+            menu_item_add(
+                menu, menu_item_alloc_function(FLIPPER_APPS[i].name, NULL, handle_menu, ctx));
+        }
+
+        /*
+        menu_item_add(menu, menu_item_alloc_function("Sub 1 gHz", NULL, NULL, NULL));
+        menu_item_add(menu, menu_item_alloc_function("125 kHz RFID", NULL, NULL, NULL));
+        menu_item_add(menu, menu_item_alloc_function("Infrared", NULL, NULL, NULL));
+        menu_item_add(menu, menu_item_alloc_function("I-Button", NULL, NULL, NULL));
+        menu_item_add(menu, menu_item_alloc_function("USB", NULL, NULL, NULL));
+        menu_item_add(menu, menu_item_alloc_function("Bluetooth", NULL, NULL, NULL));
+        menu_item_add(menu, menu_item_alloc_function("GPIO / HW", NULL, NULL, NULL));
+        menu_item_add(menu, menu_item_alloc_function("U2F", NULL, NULL, NULL));
+        menu_item_add(menu, menu_item_alloc_function("Tamagotchi", NULL, NULL, NULL));
+        menu_item_add(menu, menu_item_alloc_function("Plugins", NULL, NULL, NULL));
+        */
+
+        release_mutex(menu_mutex, menu);
+    }
+
+    printf("[app loader] start\n");
+
+    osThreadSuspend(self_id);
+}

+ 21 - 0
applications/applications.mk

@@ -16,6 +16,11 @@ APP_INPUT	= 1
 APP_GUI		= 1
 CFLAGS		+= -DAPP_MENU
 C_SOURCES	+= $(wildcard $(APP_DIR)/menu/*.c)
+C_SOURCES	+= $(wildcard $(APP_DIR)/app-loader/*.c)
+
+APP_EXAMPLE_BLINK = 1
+APP_EXAMPLE_UART_WRITE = 1
+APP_EXAMPLE_INPUT_DUMP = 1
 endif
 
 APP_TEST	?= 0
@@ -78,6 +83,22 @@ APP_INPUT = 1
 APP_DISPLAY = 1
 endif
 
+APP_CC1101 ?= 0
+ifeq ($(APP_CC1101), 1)
+CFLAGS		+= -DAPP_CC1101
+C_SOURCES	+= $(wildcard $(APP_DIR)/cc1101-workaround/*.c)
+CPP_SOURCES	+= $(wildcard $(APP_DIR)/cc1101-workaround/*.cpp)
+APP_INPUT = 1
+APP_GUI = 1
+endif
+
+ifeq ($(APP_RELEASE), 1)
+C_SOURCES	+= $(wildcard $(APP_DIR)/cc1101-workaround/*.c)
+CPP_SOURCES	+= $(wildcard $(APP_DIR)/cc1101-workaround/*.cpp)
+APP_INPUT = 1
+APP_GUI = 1
+endif
+
 # device drivers
 APP_GUI	?= 0
 ifeq ($(APP_GUI), 1)

+ 380 - 0
applications/cc1101-workaround/cc1101-workaround.cpp

@@ -0,0 +1,380 @@
+#include "flipper.h"
+
+#include "cc1101-workaround/cc1101.h"
+
+#define RSSI_DELAY 5000 //rssi delay in micro second
+#define NUM_OF_SUB_BANDS 7
+#define CHAN_SPA 0.05 // channel spacing
+
+int16_t rssi_to_dbm(uint8_t rssi_dec, uint8_t rssiOffset) {
+    int16_t rssi;
+
+    if(rssi_dec >= 128) {
+        rssi = (int16_t)((int16_t)(rssi_dec - 256) / 2) - rssiOffset;
+    } else {
+        rssi = (rssi_dec / 2) - rssiOffset;
+    }
+
+    return rssi;
+}
+
+typedef struct {
+    float base_freq;
+    uint8_t reg[3]; // FREQ2, FREQ1, FREQ0
+    uint8_t first_channel;
+    uint8_t last_channel;
+    uint8_t rssi_offset;
+} Band;
+
+typedef struct {
+    const Band* band;
+    uint16_t channel;
+} FreqConfig;
+
+void setup_freq(CC1101* cc1101, const FreqConfig* config) {
+    // cc1101->SpiWriteReg(CC1101_MCSM0, 0x08); // disalbe FS_AUTOCAL
+    cc1101->SpiWriteReg(CC1101_AGCCTRL2, 0x43 | 0x0C); // MAX_DVGA_GAIN to 11 for fast rssi
+    cc1101->SpiWriteReg(CC1101_AGCCTRL0, 0xB0); // max AGC WAIT_TIME; 0 filter_length
+    cc1101->SetMod(GFSK); // set to GFSK for fast rssi measurement | +8 is dcfilter off
+
+    cc1101->SetFreq(config->band->reg[0], config->band->reg[1], config->band->reg[2]);
+    cc1101->SetChannel(config->channel);
+
+    /*
+    //set test0 to 0x09
+    cc1101->SpiWriteReg(CC1101_TEST0, 0x09);
+    //set FSCAL2 to 0x2A to force VCO HIGH
+    cc1101->SpiWriteReg(CC1101_FSCAL2, 0x2A);
+
+    // perform a manual calibration by issuing SCAL command
+    cc1101->SpiStrobe(CC1101_SCAL);
+    */
+}
+
+int16_t rx_rssi(CC1101* cc1101, const FreqConfig* config) {
+    cc1101->SetReceive();
+
+    delayMicroseconds(RSSI_DELAY);
+
+    // 1.4.8) read PKTSTATUS register while the radio is in RX state
+    /*uint8_t _pkt_status = */ cc1101->SpiReadStatus(CC1101_PKTSTATUS);
+
+    // 1.4.9) enter IDLE state by issuing a SIDLE command
+    cc1101->SpiStrobe(CC1101_SIDLE);
+
+    // //read rssi value and converto to dBm form
+    uint8_t rssi_dec = (uint8_t)cc1101->SpiReadStatus(CC1101_RSSI);
+    int16_t rssi_dBm = rssi_to_dbm(rssi_dec, config->band->rssi_offset);
+
+    return rssi_dBm;
+}
+
+void tx(CC1101* cc1101, const FreqConfig* config) {
+    /*
+    cc1101->SpiWriteReg(CC1101_MCSM0, 0x18); //enable FS_AUTOCAL
+    cc1101->SpiWriteReg(CC1101_AGCCTRL2, 0x43); //back to recommended config
+    cc1101->SpiWriteReg(CC1101_AGCCTRL0, 0x91); //back to recommended config
+    */
+
+    cc1101->SetFreq(config->band->reg[0], config->band->reg[1], config->band->reg[2]);
+    cc1101->SetChannel(config->channel);
+
+    cc1101->SetTransmit();
+}
+
+void idle(CC1101* cc1101) {
+    cc1101->SpiStrobe(CC1101_SIDLE);
+}
+
+const Band bands[NUM_OF_SUB_BANDS] = {
+    {387, {0x0E, 0xE2, 0x76}, 0, 255, 74},
+    {399.8, {0x0F, 0x60, 0x76}, 0, 255, 74},
+    {412.6, {0x0F, 0xDE, 0x76}, 0, 255, 74},
+    {425.4, {0x10, 0x5C, 0x76}, 160, 180, 74},
+    {438.2, {0x10, 0xDA, 0x76}, 0, 255, 74},
+    {451, {0x11, 0x58, 0x8F}, 0, 255, 74},
+    {463.8, {0x11, 0xD6, 0x8F}, 0, 4, 74},
+};
+
+const FreqConfig FREQ_LIST[] = {
+    {&bands[0], 0},   {&bands[0], 50},  {&bands[0], 100}, {&bands[0], 150}, {&bands[0], 200},
+    {&bands[1], 0},   {&bands[1], 50},  {&bands[1], 100}, {&bands[1], 150}, {&bands[1], 200},
+    {&bands[2], 0},   {&bands[2], 50},  {&bands[2], 100}, {&bands[2], 150}, {&bands[2], 200},
+    {&bands[3], 160}, {&bands[3], 170}, {&bands[4], 0},   {&bands[4], 50},  {&bands[4], 100},
+    {&bands[4], 150}, {&bands[4], 200}, {&bands[5], 0},   {&bands[5], 50},  {&bands[5], 100},
+    {&bands[5], 150}, {&bands[5], 200}, {&bands[6], 0},
+};
+
+typedef enum {
+    EventTypeTick,
+    EventTypeKey,
+} EventType;
+
+typedef struct {
+    union {
+        InputEvent input;
+    } value;
+    EventType type;
+} Event;
+
+typedef enum { ModeRx, ModeTx } Mode;
+
+typedef struct {
+    int16_t dbm;
+    uint8_t reg;
+} TxLevel;
+
+const TxLevel TX_LEVELS[] = {
+    {-10, 0},
+    {-5, 0},
+    {0, 0},
+    {5, 0},
+};
+
+typedef struct {
+    Mode mode;
+    size_t active_freq;
+    int16_t last_rssi;
+    size_t tx_level;
+    bool need_cc1101_conf;
+} State;
+
+static void render_callback(CanvasApi* canvas, void* ctx) {
+    State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25);
+
+    canvas->clear(canvas);
+    canvas->set_color(canvas, ColorBlack);
+    canvas->set_font(canvas, FontPrimary);
+    canvas->draw_str(canvas, 2, 12, "cc1101 workaround");
+
+    {
+        char buf[24];
+        FreqConfig conf = FREQ_LIST[state->active_freq];
+        float freq = conf.band->base_freq + CHAN_SPA * conf.channel;
+        sprintf(buf, "freq: %ld.%02ld MHz", (uint32_t)freq, (uint32_t)(freq * 100.) % 100);
+
+        canvas->set_font(canvas, FontSecondary);
+        canvas->draw_str(canvas, 2, 25, buf);
+    }
+
+    {
+        canvas->set_font(canvas, FontSecondary);
+
+        if(state->need_cc1101_conf) {
+            canvas->draw_str(canvas, 2, 36, "mode: configuring...");
+        } else if(state->mode == ModeRx) {
+            canvas->draw_str(canvas, 2, 36, "mode: RX");
+        } else if(state->mode == ModeTx) {
+            canvas->draw_str(canvas, 2, 36, "mode: TX");
+        } else {
+            canvas->draw_str(canvas, 2, 36, "mode: unknown");
+        }
+    }
+
+    {
+        if(!state->need_cc1101_conf && state->mode == ModeRx) {
+            char buf[24];
+            sprintf(buf, "RSSI: %d dBm", state->last_rssi);
+
+            canvas->set_font(canvas, FontSecondary);
+            canvas->draw_str(canvas, 2, 48, buf);
+        }
+    }
+
+    {
+        char buf[24];
+        sprintf(buf, "tx level: %d dBm", TX_LEVELS[state->tx_level].dbm);
+
+        canvas->set_font(canvas, FontSecondary);
+        canvas->draw_str(canvas, 2, 63, buf);
+    }
+
+    release_mutex((ValueMutex*)ctx, state);
+}
+
+static void input_callback(InputEvent* input_event, void* ctx) {
+    osMessageQueueId_t event_queue = (QueueHandle_t)ctx;
+
+    Event event;
+    event.type = EventTypeKey;
+    event.value.input = *input_event;
+    osMessageQueuePut(event_queue, &event, 0, 0);
+}
+
+extern "C" void cc1101_workaround(void* p) {
+    osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(Event), NULL);
+    assert(event_queue);
+
+    State _state;
+    _state.mode = ModeRx;
+    _state.active_freq = 0;
+    _state.need_cc1101_conf = true;
+    _state.last_rssi = 0;
+    _state.tx_level = 0;
+
+    ValueMutex state_mutex;
+    if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
+        printf("[cc1101] cannot create mutex\n");
+        furiac_exit(NULL);
+    }
+
+    Widget* widget = widget_alloc();
+
+    widget_draw_callback_set(widget, render_callback, &state_mutex);
+    widget_input_callback_set(widget, input_callback, event_queue);
+
+    // Open GUI and register widget
+    GuiApi* gui = (GuiApi*)furi_open("gui");
+    if(gui == NULL) {
+        printf("[cc1101] gui is not available\n");
+        furiac_exit(NULL);
+    }
+    gui->add_widget(gui, widget, WidgetLayerFullscreen);
+
+    printf("[cc1101] creating device\n");
+    CC1101 cc1101(GpioPin{CC1101_CS_GPIO_Port, CC1101_CS_Pin});
+    printf("[cc1101] init device\n");
+
+    uint8_t address = cc1101.Init();
+    if(address > 0) {
+        printf("[cc1101] init done: %d\n", address);
+    } else {
+        printf("[cc1101] init fail\n");
+        furiac_exit(NULL);
+    }
+
+    // RX filter bandwidth 58.035714(0xFD) 100k(0xCD) 200k(0x8D)
+    cc1101.SpiWriteReg(CC1101_MDMCFG4, 0xCD);
+    // datarate config 250kBaud  for the purpose of fast rssi measurement
+    cc1101.SpiWriteReg(CC1101_MDMCFG3, 0x3B);
+    // FEC preamble etc. last 2 bits for channel spacing
+    cc1101.SpiWriteReg(CC1101_MDMCFG1, 0x20);
+    // 50khz channel spacing
+    cc1101.SpiWriteReg(CC1101_MDMCFG0, 0xF8);
+
+    // create pin
+    GpioPin led = {GPIOA, GPIO_PIN_8};
+
+    // configure pin
+    pinMode(led, GpioModeOpenDrain);
+
+    const int16_t RSSI_THRESHOLD = -89;
+
+    Event event;
+    while(1) {
+        osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 150);
+        State* state = (State*)acquire_mutex_block(&state_mutex);
+
+        if(event_status == osOK) {
+            if(event.type == EventTypeKey) {
+                if(event.value.input.state && event.value.input.input == InputBack) {
+                    printf("[cc1101] bye!\n");
+                    // TODO remove all widgets create by app
+                    widget_enabled_set(widget, false);
+                    furiac_exit(NULL);
+                }
+
+                if(event.value.input.state && event.value.input.input == InputUp) {
+                    if(state->active_freq > 0) {
+                        state->active_freq--;
+                        state->need_cc1101_conf = true;
+                    }
+                }
+
+                if(event.value.input.state && event.value.input.input == InputDown) {
+                    if(state->active_freq < (sizeof(FREQ_LIST) / sizeof(FREQ_LIST[0]) - 1)) {
+                        state->active_freq++;
+                        state->need_cc1101_conf = true;
+                    }
+                }
+
+                if(event.value.input.state && event.value.input.input == InputLeft) {
+                    if(state->tx_level < (sizeof(TX_LEVELS) / sizeof(TX_LEVELS[0]) - 1)) {
+                        state->tx_level++;
+                    } else {
+                        state->tx_level = 0;
+                    }
+
+                    state->need_cc1101_conf = true;
+                }
+
+                if(event.value.input.input == InputOk) {
+                    state->mode = event.value.input.state ? ModeTx : ModeRx;
+                    state->need_cc1101_conf = true;
+                }
+            }
+        } else {
+            if(!state->need_cc1101_conf && state->mode == ModeRx) {
+                state->last_rssi = rx_rssi(&cc1101, &FREQ_LIST[state->active_freq]);
+            }
+        }
+
+        if(state->need_cc1101_conf) {
+            if(state->mode == ModeRx) {
+                setup_freq(&cc1101, &FREQ_LIST[state->active_freq]);
+                state->last_rssi = rx_rssi(&cc1101, &FREQ_LIST[state->active_freq]);
+                // idle(&cc1101);
+            } else if(state->mode == ModeTx) {
+                tx(&cc1101, &FREQ_LIST[state->active_freq]);
+            }
+
+            state->need_cc1101_conf = false;
+        }
+
+        digitalWrite(
+            led, (state->last_rssi > RSSI_THRESHOLD && !state->need_cc1101_conf) ? LOW : HIGH);
+
+        release_mutex(&state_mutex, state);
+        widget_update(widget);
+    }
+
+    /*
+    while(1) {
+        for(uint8_t i = 0; i <= NUM_OF_SUB_BANDS; i++) {
+            highRSSI[i] = MIN_DBM;
+        }
+
+        activeChannel = 300;
+
+        tx(&cc1101, activeBand, activeChannel, 500);
+
+        scanFreq(&cc1101);
+
+        if(activeChannel < 256 && highRSSI[activeBand] > RSSI_THRESHOLD) {
+            float freq = base_freq[activeBand] + CHAN_SPA * activeChannel;
+
+            printf(
+                "channel: %d, freq: %d, RSSI: %d\n",
+                activeChannel,
+                (uint32_t)(freq * 1000),
+                highRSSI[activeBand]
+            );
+
+            *
+            if(tx_on) {
+                tx(&cc1101, activeBand, activeChannel, 500);
+            } else {
+                osDelay(1000);
+            }
+            *
+        } else {
+            // printf("0 carrier sensed\n");
+        }
+        *
+
+        uint8_t band = 4; // 438.2 MHz
+
+        *
+        cc1101.SetFreq(freqSettings[band][0], freqSettings[band][1], freqSettings[band][2]);
+        cc1101.SetChannel(0);
+        cc1101.SetTransmit();
+
+        delay(5000);
+
+        cc1101.SpiStrobe(CC1101_SIDLE);
+        *
+
+        delay(1000);
+    }
+    */
+}

+ 396 - 0
applications/cc1101-workaround/cc1101.cpp

@@ -0,0 +1,396 @@
+#include "flipper_v2.h"
+#include "cc1101-workaround/cc1101.h"
+
+// ******************************************************************************
+#define WRITE_BURST 0x40
+#define READ_SINGLE 0x80
+#define READ_BURST 0xC0
+#define BYTES_IN_FIFO 0x7F //used to detect FIFO underflow or overflow
+
+/*********************ss_pin as global variable****************************** */
+/*                         cc1101                                       */
+/******************************************************************************/
+GpioPin ss_pin;
+
+CC1101::CC1101(GpioPin ss_pin) {
+    /*
+    pinMode(gdo0_pin, OUTPUT); //GDO0 as asynchronous serial mode input
+    pinMode(gdo2_pin, INPUT); //GDO2 as asynchronous serial mode output
+    */
+    pinMode(ss_pin, OUTPUT);
+    this->ss_pin = ss_pin;
+}
+//******************************************************************************
+//SpiInit
+/******************************************************************************/
+void CC1101::SpiInit(void) {
+    //initialize spi pins
+
+    //Enable spi master, MSB, SPI mode 0, FOSC/4
+    SpiMode(0);
+}
+
+void CC1101::SpiEnd(void) {
+    /*
+    SPCR = ((0<<SPE) |                  // SPI Enable
+        (0<<SPIE)|                      // SPI Interupt Enable
+        (0<<DORD)|                      // Data Order (0:MSB first / 1:LSB first)
+        (1<<MSTR)|                      // Master/Slave select
+        (0<<SPR1)|(0<<SPR0)|        // SPI Clock Rate ( 0 0 = osc/4; 0 1 = osc/16; 1 0 = osc/64; 1 1= 0sc/128)
+        (0<<CPOL)|                  // Clock Polarity (0:SCK low / 1:SCK hi when idle)
+        (0<<CPHA));                     // Clock Phase (0:leading / 1:trailing edge sampling)
+
+    //SPSR =  (0<<SPI2X);                   // Double Clock Rate
+    */
+}
+/******************************************************************************
+Function: SpiMode
+ *INPUT        :        config               mode
+               (0<<CPOL) | (0 << CPHA)       0
+               (0<<CPOL) | (1 << CPHA)       1
+               (1<<CPOL) | (0 << CPHA)       2
+               (1<<CPOL) | (1 << CPHA)       3
+*OUTPUT       :none
+******************************************************************************/
+void CC1101::SpiMode(byte config) {
+    /*
+  byte tmp;
+  // enable SPI master with configuration byte specified
+  SPCR = 0;
+  SPCR = (config & 0x7F) | (1<<SPE) | (1<<MSTR);
+  tmp = SPSR;
+  tmp = SPDR;
+  */
+}
+/****************************************************************
+*FUNCTION NAME:SpiTransfer
+*FUNCTION     :spi transfer
+*INPUT        :value: data to send
+*OUTPUT       :data to receive
+****************************************************************/
+extern SPI_HandleTypeDef hspi3;
+
+byte CC1101::SpiTransfer(byte value) {
+    uint8_t buf[1] = {value};
+    uint8_t rxbuf[1] = {0};
+
+    HAL_SPI_TransmitReceive(&hspi3, buf, rxbuf, 1, HAL_MAX_DELAY);
+
+    return rxbuf[0];
+}
+
+/****************************************************************
+*FUNCTION NAME:SpiWriteReg
+*FUNCTION     :CC1101 write data to register
+*INPUT        :addr: register address; value: register value
+*OUTPUT       :none
+****************************************************************/
+void CC1101::SpiWriteReg(byte addr, byte value) {
+    digitalWrite(ss_pin, LOW);
+    while(digitalRead(MISO_PIN))
+        ;
+    SpiTransfer(addr);
+    SpiTransfer(value);
+    digitalWrite(ss_pin, HIGH);
+}
+
+/****************************************************************
+*FUNCTION NAME:SpiWriteBurstReg
+*FUNCTION     :CC1101 write burst data to register
+*INPUT        :addr: register address; buffer:register value array; num:number to write
+*OUTPUT       :none
+****************************************************************/
+void CC1101::SpiWriteBurstReg(byte addr, byte* buffer, byte num) {
+    byte i, temp;
+
+    temp = addr | WRITE_BURST;
+    digitalWrite(ss_pin, LOW);
+    while(digitalRead(MISO_PIN))
+        ;
+    SpiTransfer(temp);
+    for(i = 0; i < num; i++) {
+        SpiTransfer(buffer[i]);
+    }
+    digitalWrite(ss_pin, HIGH);
+}
+
+/****************************************************************
+*FUNCTION NAME:SpiStrobe
+*FUNCTION     :CC1101 Strobe
+*INPUT        :strobe: command; //refer define in CC1101.h//
+*OUTPUT       :none
+****************************************************************/
+void CC1101::SpiStrobe(byte strobe) {
+    digitalWrite(ss_pin, LOW);
+    while(digitalRead(MISO_PIN))
+        ;
+    SpiTransfer(strobe);
+    digitalWrite(ss_pin, HIGH);
+}
+
+/****************************************************************
+*FUNCTION NAME:SpiReadReg
+*FUNCTION     :CC1101 read data from register
+*INPUT        :addr: register address
+*OUTPUT       :register value
+****************************************************************/
+byte CC1101::SpiReadReg(byte addr) {
+    byte temp, value;
+
+    temp = addr | READ_SINGLE;
+    digitalWrite(ss_pin, LOW);
+    while(digitalRead(MISO_PIN))
+        ;
+    SpiTransfer(temp);
+    value = SpiTransfer(0);
+    digitalWrite(ss_pin, HIGH);
+
+    return value;
+}
+
+/****************************************************************
+*FUNCTION NAME:SpiReadBurstReg
+*FUNCTION     :CC1101 read burst data from register
+*INPUT        :addr: register address; buffer:array to store register value; num: number to read
+*OUTPUT       :none
+****************************************************************/
+void CC1101::SpiReadBurstReg(byte addr, byte* buffer, byte num) {
+    byte i, temp;
+
+    temp = addr | READ_BURST;
+    digitalWrite(ss_pin, LOW);
+    while(digitalRead(MISO_PIN))
+        ;
+    SpiTransfer(temp);
+    for(i = 0; i < num; i++) {
+        buffer[i] = SpiTransfer(0);
+    }
+    digitalWrite(ss_pin, HIGH);
+}
+
+/****************************************************************
+*FUNCTION NAME:SpiReadStatus
+*FUNCTION     :CC1101 read status register
+*INPUT        :addr: register address
+*OUTPUT       :status value
+****************************************************************/
+byte CC1101::SpiReadStatus(byte addr) {
+    byte value, temp;
+
+    temp = addr | READ_BURST;
+    digitalWrite(ss_pin, LOW);
+    while(digitalRead(MISO_PIN))
+        ;
+    SpiTransfer(temp);
+    value = SpiTransfer(0);
+    digitalWrite(ss_pin, HIGH);
+
+    return value;
+}
+
+/****************************************************************
+*FUNCTION NAME:Reset
+*FUNCTION     :CC1101 reset //details refer datasheet of CC1101/CC1100//
+*INPUT        :none
+*OUTPUT       :none
+****************************************************************/
+void CC1101::Reset(void) {
+    digitalWrite(ss_pin, LOW);
+    delay(1);
+    digitalWrite(ss_pin, HIGH);
+    delay(1);
+    digitalWrite(ss_pin, LOW);
+    while(digitalRead(MISO_PIN))
+        ;
+    SpiTransfer(CC1101_SRES);
+    while(digitalRead(MISO_PIN))
+        ;
+    digitalWrite(ss_pin, HIGH);
+}
+/****************************************************************
+*FUNCTION NAME:Init
+*FUNCTION     :CC1101 initialization
+*INPUT        :none
+*OUTPUT       :none
+****************************************************************/
+byte CC1101::Init(void) {
+#ifdef CC1101_DEBUG
+    printf("Init SPI...\n");
+#endif
+    SpiInit(); //spi initialization
+    digitalWrite(ss_pin, HIGH);
+// digitalWrite(SCK_PIN, HIGH);
+// digitalWrite(MOSI_PIN, LOW);
+#ifdef CC1101_DEBUG
+    printf("Reset CC1101...\n");
+#endif
+    Reset(); //CC1101 reset
+
+    byte partnum, version;
+    partnum = SpiReadStatus(CC1101_PARTNUM);
+    version = SpiReadStatus(CC1101_VERSION);
+
+#ifdef CC1101_DEBUG
+
+    printf("Partnum:0x%02X, Version:0x%02X\n", partnum, version);
+#endif
+
+#ifdef CC1101_DEBUG
+    printf("Init CC1101...");
+#endif
+    RegConfigSettings(); //CC1101 register config
+
+#ifdef CC1101_DEBUG
+    printf("Done!\n");
+#endif
+
+    return version;
+}
+/****************************************************************
+*FUNCTION NAME:SetMod
+*FUNCTION     :CC1101 modulation type
+*INPUT        :byte mode
+*OUTPUT       :none
+****************************************************************/
+void CC1101::SetMod(byte mode) {
+    SpiWriteReg(CC1101_MDMCFG2, mode); //no sync/preamble; ASK/OOK only support up to -1dbm
+    if((mode | 0x30) == ASK) {
+        SpiWriteReg(CC1101_FREND0, 0x11); //use first up to PATABLE(0)
+        byte PaTabel[8] = {0x00, POWER, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+        SpiWriteBurstReg(CC1101_PATABLE, PaTabel, 8); //CC1101 PATABLE config
+    } else {
+        SpiWriteReg(CC1101_FREND0, 0x10); //use first up to PATABLE(0)
+        byte PaTabel[8] = {POWER, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+        SpiWriteBurstReg(CC1101_PATABLE, PaTabel, 8); //CC1101 PATABLE config
+    }
+
+#ifdef CC1101_DEBUG
+    switch(mode | 0x30) {
+    case GFSK: {
+        printf("CC1101 Modulation: GFSK");
+        break;
+    }
+    case MSK: {
+        printf("CC1101 Modulation: MSK");
+        break;
+    }
+    case ASK: {
+        printf("CC1101 Modulation: ASK/OOK");
+        break;
+    }
+    case FSK2: {
+        printf("CC1101 Modulation: 2-FSK");
+        break;
+    }
+    case FSK4: {
+        printf("CC1101 Modulation: 4-FSK");
+        break;
+    }
+    default: //default to GFSK
+    {
+        printf("Modulation mode not supported");
+        break;
+    }
+    }
+
+    printf("\n");
+#endif
+}
+/****************************************************************
+*FUNCTION NAME:RegConfigSettings
+*FUNCTION     :CC1101 register config //details refer datasheet of CC1101/CC1100//
+*INPUT        :none
+*OUTPUT       :none
+****************************************************************/
+void CC1101::RegConfigSettings(void) {
+    SpiWriteReg(CC1101_FSCTRL1, 0x06); //IF frequency
+    SpiWriteReg(CC1101_FSCTRL0, 0x00); //frequency offset before synthesizer
+
+    SpiWriteReg(CC1101_MDMCFG4, 0xCC); // RX filter bandwidth 100k(0xcc)
+    SpiWriteReg(
+        CC1101_MDMCFG3, 0x43); //datarate config 512kBaud  for the purpose of fast rssi measurement
+    SpiWriteReg(CC1101_MDMCFG1, 0x21); //FEC preamble etc. last 2 bits for channel spacing
+    SpiWriteReg(CC1101_MDMCFG0, 0xF8); //100khz channel spacing
+    //CC1101_CHANNR moved to SetChannel func
+
+    //SpiWriteReg(CC1101_DEVIATN,  0x47);
+    SpiWriteReg(
+        CC1101_MCSM0, 0x18); // calibrate when going from IDLE to RX or TX ; 149 - 155 μs timeout
+    SpiWriteReg(CC1101_FOCCFG, 0x16); //frequency compensation
+    //SpiWriteReg(CC1101_BSCFG,    0x1C);   //bit synchronization config
+    SpiWriteReg(CC1101_AGCCTRL2, 0x43);
+    SpiWriteReg(CC1101_AGCCTRL1, 0x49);
+    SpiWriteReg(CC1101_AGCCTRL0, 0x91);
+    //freq synthesizer calibration
+    SpiWriteReg(CC1101_FSCAL3, 0xEA);
+    SpiWriteReg(CC1101_FSCAL2, 0x2A);
+    SpiWriteReg(CC1101_FSCAL1, 0x00);
+    SpiWriteReg(CC1101_FSCAL0, 0x1F);
+    SpiWriteReg(CC1101_TEST2, 0x81);
+    SpiWriteReg(CC1101_TEST1, 0x35);
+    SpiWriteReg(CC1101_TEST0, 0x0B); //should be 0x0B for lower than 430.6MHz and 0x09 for higher
+
+    //SpiWriteReg(CC1101_FREND1,   0x56);
+
+    //SpiWriteReg(CC1101_IOCFG2,   0x0B);   //serial clock.synchronous to the data in synchronous serial mode
+    //SpiWriteReg(CC1101_IOCFG0,   0x06);   //asserts when sync word has been sent/received, and de-asserts at the end of the packet
+    SpiWriteReg(CC1101_IOCFG2, 0x0D); //data output pin for asynchronous mode
+    SpiWriteReg(
+        CC1101_IOCFG0,
+        0x2E); //High impedance (3-state), GDO0 configed as data input for asynchronous mode
+    //SpiWriteReg(CC1101_PKTCTRL0, 0x05);       //whitening off;CRC Enable;variable length packets, packet length configured by the first byte after sync word
+    SpiWriteReg(
+        CC1101_PKTCTRL0, 0x33); //whitening off; asynchronous serial mode; CRC diable;reserved
+    //SpiWriteReg(CC1101_PKTLEN,   0x3D);   //61 bytes max length
+    SpiWriteReg(
+        CC1101_FIFOTHR,
+        0x47); //Adc_retention enabled for RX filter bandwidth less than 325KHz; defalut fifo threthold.
+}
+/****************************************************************
+ *FUNCTION NAME:SetFreq
+ *FUNCTION     :SetFreq
+ *INPUT        :Freq2, Freq1, Freq0
+ *OUTPUT       :none
+ ****************************************************************/
+void CC1101::SetFreq(byte freq2, byte freq1, byte freq0) {
+    SpiWriteReg(CC1101_FREQ2, freq2);
+    SpiWriteReg(CC1101_FREQ1, freq1);
+    SpiWriteReg(CC1101_FREQ0, freq0);
+}
+/****************************************************************
+ *FUNCTION NAME:SetChannel
+ *FUNCTION     :SetChannel
+ *INPUT        :int channel
+ *OUTPUT       :none
+ ****************************************************************/
+void CC1101::SetChannel(int channel) {
+#ifdef CC1101_DEBUG
+    printf("Set CC1101 channel to: %d \n", channel);
+#endif
+    SpiWriteReg(CC1101_CHANNR, (byte)channel); //related to channel numbers
+}
+/****************************************************************
+ *FUNCTION NAME:SetReceive
+ *FUNCTION     :SetReceive
+ *INPUT        :none
+ *OUTPUT       :none
+ ****************************************************************/
+void CC1101::SetReceive(void) {
+    SpiStrobe(CC1101_SRX);
+    while(SpiReadStatus(CC1101_MARCSTATE) ^ CC1101_STATUS_RX) {
+        // delay(1);
+        // printf("wait status\n");
+    }
+}
+/****************************************************************
+ *FUNCTION NAME:SetTransmit
+ *FUNCTION     :
+ *INPUT        :none
+ *OUTPUT       :none
+ ****************************************************************/
+void CC1101::SetTransmit(void) {
+    SpiStrobe(CC1101_STX);
+    while(SpiReadStatus(CC1101_MARCSTATE) ^ CC1101_STATUS_TX)
+        ;
+}
+//cc1101 cc1101;

+ 166 - 0
applications/cc1101-workaround/cc1101.h

@@ -0,0 +1,166 @@
+#pragma once
+
+#include "flipper_v2.h"
+
+/*******************************debug mode*************************************/
+// #define     CC1101_DEBUG 1
+//******************************CC1101 defines ********************************
+//******************************config registers  *****************************
+#define CC1101_IOCFG2 0x00 //GDO2 output pin configration
+#define CC1101_IOCFG1 0x01 // GDO1 output pin configuration
+#define CC1101_IOCFG0 0x02 // GDO0 output pin configuration
+#define CC1101_FIFOTHR 0x03 // RX FIFO and TX FIFO thresholds
+#define CC1101_SYNC1 0x04 // Sync word, high INT8U
+#define CC1101_SYNC0 0x05 // Sync word, low INT8U
+#define CC1101_PKTLEN 0x06 // Packet length
+#define CC1101_PKTCTRL1 0x07 // Packet automation control
+#define CC1101_PKTCTRL0 0x08 // Packet automation control
+#define CC1101_ADDR 0x09 // Device address
+#define CC1101_CHANNR 0x0A // Channel number
+#define CC1101_FSCTRL1 0x0B // Frequency synthesizer control
+#define CC1101_FSCTRL0 0x0C // Frequency synthesizer control
+#define CC1101_FREQ2 0x0D // Frequency control word, high INT8U
+#define CC1101_FREQ1 0x0E // Frequency control word, middle INT8U
+#define CC1101_FREQ0 0x0F // Frequency control word, low INT8U
+#define CC1101_MDMCFG4 0x10 // Modem configuration
+#define CC1101_MDMCFG3 0x11 // Modem configuration
+#define CC1101_MDMCFG2 0x12 // Modem configuration
+#define CC1101_MDMCFG1 0x13 // Modem configuration
+#define CC1101_MDMCFG0 0x14 // Modem configuration
+#define CC1101_DEVIATN 0x15 // Modem deviation setting
+#define CC1101_MCSM2 0x16 // Main Radio Control State Machine configuration
+#define CC1101_MCSM1 0x17 // Main Radio Control State Machine configuration
+#define CC1101_MCSM0 0x18 // Main Radio Control State Machine configuration
+#define CC1101_FOCCFG 0x19 // Frequency Offset Compensation configuration
+#define CC1101_BSCFG 0x1A // Bit Synchronization configuration
+#define CC1101_AGCCTRL2 0x1B // AGC control
+#define CC1101_AGCCTRL1 0x1C // AGC control
+#define CC1101_AGCCTRL0 0x1D // AGC control
+#define CC1101_WOREVT1 0x1E // High INT8U Event 0 timeout
+#define CC1101_WOREVT0 0x1F // Low INT8U Event 0 timeout
+#define CC1101_WORCTRL 0x20 // Wake On Radio control
+#define CC1101_FREND1 0x21 // Front end RX configuration
+#define CC1101_FREND0 0x22 // Front end TX configuration
+#define CC1101_FSCAL3 0x23 // Frequency synthesizer calibration
+#define CC1101_FSCAL2 0x24 // Frequency synthesizer calibration
+#define CC1101_FSCAL1 0x25 // Frequency synthesizer calibration
+#define CC1101_FSCAL0 0x26 // Frequency synthesizer calibration
+#define CC1101_RCCTRL1 0x27 // RC oscillator configuration
+#define CC1101_RCCTRL0 0x28 // RC oscillator configuration
+#define CC1101_FSTEST 0x29 // Frequency synthesizer calibration control
+#define CC1101_PTEST 0x2A // Production test
+#define CC1101_AGCTEST 0x2B // AGC test
+#define CC1101_TEST2 0x2C // Various test settings
+#define CC1101_TEST1 0x2D // Various test settings
+#define CC1101_TEST0 0x2E // Various test settings
+
+//*********************CC1101 Strobe commands  *********************************
+#define CC1101_SRES 0x30 // Reset chip.
+
+// Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1).
+// If in RX/TX: Go to a wait state where only the synthesizer is
+// running (for quick RX / TX turnaround).
+#define CC1101_SFSTXON 0x31
+
+#define CC1101_SXOFF 0x32 // Turn off crystal oscillator.
+
+// Calibrate frequency synthesizer and turn it off
+// (enables quick start).
+#define CC1101_SCAL 0x33
+
+// Enable RX. Perform calibration first if coming from IDLE and
+// MCSM0.FS_AUTOCAL=1.
+#define CC1101_SRX 0x34
+
+// In IDLE state: Enable TX. Perform calibration first if
+// MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled:
+// Only go to TX if channel is clear.
+#define CC1101_STX 0x35
+
+// Exit RX / TX, turn off frequency synthesizer and exit
+// Wake-On-Radio mode if applicable.
+#define CC1101_SIDLE 0x36
+
+#define CC1101_SAFC 0x37 // Perform AFC adjustment of the frequency synthesizer
+#define CC1101_SWOR 0x38 // Start automatic RX polling sequence (Wake-on-Radio)
+#define CC1101_SPWD 0x39 // Enter power down mode when CSn goes high.
+#define CC1101_SFRX 0x3A // Flush the RX FIFO buffer.
+#define CC1101_SFTX 0x3B // Flush the TX FIFO buffer.
+#define CC1101_SWORRST 0x3C // Reset real time clock.
+
+// No operation. May be used to pad strobe commands to two
+// INT8Us for simpler software.
+#define CC1101_SNOP 0x3D
+
+//**************************CC1101 STATUS REGSITER ****************************
+//use burst read to access
+#define CC1101_PARTNUM 0x30
+#define CC1101_VERSION 0x31
+#define CC1101_FREQEST 0x32
+#define CC1101_LQI 0x33
+#define CC1101_RSSI 0x34
+#define CC1101_MARCSTATE 0x35
+#define CC1101_WORTIME1 0x36
+#define CC1101_WORTIME0 0x37
+#define CC1101_PKTSTATUS 0x38
+#define CC1101_VCO_VC_DAC 0x39
+#define CC1101_TXBYTES 0x3A
+#define CC1101_RXBYTES 0x3B
+#define CC1101_RCCTRL1_STATUS 0x3C
+#define CC1101_RCCTRL_STATUS 0x3D
+/****************************cc1101 status ***********************************/
+#define CC1101_STATUS_RX 0x0D
+#define CC1101_STATUS_TX 0x13
+
+//***********************CC1101 PATABLE,TXFIFO,RXFIFO**************************
+#define CC1101_PATABLE 0x3E
+#define CC1101_TXFIFO 0x3F
+#define CC1101_RXFIFO 0x3F
+
+//******************************* pins ****************************************
+// #define SCK_PIN   13
+// #define MISO_PIN  12
+// #define MOSI_PIN  11
+// #define SS_PIN    10
+// #define GDO0	8	//pin assignment
+// #define GDO2	9
+//*****************************CC1101 Config**********************************
+//no  pa ramping, output power to 10dBm
+#define POWER 0xC0 //output power to maximum
+//modulation
+#define FSK2 0x00
+#define GFSK 0x10
+#define ASK 0x30
+#define FSK4 0x40
+#define MSK 0x70
+//******************************** class **************************************//
+class CC1101 {
+private:
+    GpioPin ss_pin;
+    GpioPin gdo0_pin;
+    GpioPin gdo2_pin;
+
+private:
+    void SpiMode(byte config);
+    byte SpiTransfer(byte value);
+    void Reset(void);
+    void SpiWriteBurstReg(byte addr, byte* buffer, byte num);
+    byte SpiReadReg(byte addr);
+    void SpiReadBurstReg(byte addr, byte* buffer, byte num);
+    void RegConfigSettings(void);
+
+public:
+    CC1101(GpioPin ss_pin);
+
+    void SpiWriteReg(byte addr, byte value);
+    void SpiInit(void);
+    void SpiEnd(void);
+    void SetMod(byte mode);
+    void SetFreq(byte Freq2, byte Freq1, byte Freq0);
+    byte Init(void);
+    void SpiStrobe(byte strobe);
+    byte SpiReadStatus(byte addr);
+    void SetReceive(void);
+    void SetTransmit(void);
+    void SetChannel(int channel);
+};

+ 1 - 1
applications/display-u8g2/display-u8g2.c

@@ -124,7 +124,7 @@ typedef struct {
 static void handle_fb_change(const void* fb, size_t fb_size, void* raw_ctx) {
     DisplayCtx* ctx = (DisplayCtx*)raw_ctx; // make right type
 
-    fuprintf(ctx->log, "[display_u8g2] change fb\n");
+    // fuprintf(ctx->log, "[display_u8g2] change fb\n");
 
     // send update to app thread
     xSemaphoreGive(ctx->update);

+ 34 - 25
applications/menu/menu.c

@@ -3,7 +3,6 @@
 #include <stdio.h>
 #include <stdbool.h>
 
-#include <flipper.h>
 #include <flipper_v2.h>
 #include <gui/gui.h>
 
@@ -25,23 +24,30 @@ struct Menu {
 
 void menu_widget_callback(CanvasApi* canvas, void* context);
 
-Menu* menu_init() {
+ValueMutex* menu_init() {
     Menu* menu = furi_alloc(sizeof(Menu));
 
     // Event dispatcher
     menu->event = menu_event_alloc();
 
+    ValueMutex* menu_mutex = furi_alloc(sizeof(ValueMutex));
+    if(menu_mutex == NULL || !init_mutex(menu_mutex, menu, sizeof(Menu))) {
+        printf("[menu_task] cannot create menu mutex\n");
+        furiac_exit(NULL);
+    }
+
     // Allocate and configure widget
     menu->widget = widget_alloc();
-    widget_draw_callback_set(menu->widget, menu_widget_callback, menu);
-    widget_input_callback_set(menu->widget, menu_event_input_callback, menu->event);
 
     // Open GUI and register fullscreen widget
     GuiApi* gui = furi_open("gui");
     assert(gui);
     gui->add_widget(gui, menu->widget, WidgetLayerFullscreen);
 
-    return menu;
+    widget_draw_callback_set(menu->widget, menu_widget_callback, menu_mutex);
+    widget_input_callback_set(menu->widget, menu_event_input_callback, menu->event);
+
+    return menu_mutex;
 }
 
 void menu_build_main(Menu* menu) {
@@ -49,17 +55,6 @@ void menu_build_main(Menu* menu) {
     // Root point
     menu->root = menu_item_alloc_menu(NULL, NULL);
 
-    menu_item_add(menu, menu_item_alloc_function("Sub 1 gHz", NULL, NULL, NULL));
-    menu_item_add(menu, menu_item_alloc_function("125 kHz RFID", NULL, NULL, NULL));
-    menu_item_add(menu, menu_item_alloc_function("Infrared", NULL, NULL, NULL));
-    menu_item_add(menu, menu_item_alloc_function("I-Button", NULL, NULL, NULL));
-    menu_item_add(menu, menu_item_alloc_function("USB", NULL, NULL, NULL));
-    menu_item_add(menu, menu_item_alloc_function("Bluetooth", NULL, NULL, NULL));
-    menu_item_add(menu, menu_item_alloc_function("GPIO / HW", NULL, NULL, NULL));
-    menu_item_add(menu, menu_item_alloc_function("U2F", NULL, NULL, NULL));
-    menu_item_add(menu, menu_item_alloc_function("Tamagotchi", NULL, NULL, NULL));
-    menu_item_add(menu, menu_item_alloc_function("Plugins", NULL, NULL, NULL));
-
     menu->settings = menu_item_alloc_menu("Setting", NULL);
     menu_item_subitem_add(menu->settings, menu_item_alloc_function("one", NULL, NULL, NULL));
     menu_item_subitem_add(menu->settings, menu_item_alloc_function("two", NULL, NULL, NULL));
@@ -80,9 +75,8 @@ void menu_widget_callback(CanvasApi* canvas, void* context) {
     assert(canvas);
     assert(context);
 
-    Menu* menu = context;
-
-    menu_event_lock(menu->event);
+    Menu* menu = acquire_mutex((ValueMutex*)context, 100); // wait 10 ms to get mutex
+    if(menu == NULL) return; // redraw fail
 
     if(!menu->current) {
         canvas->clear(canvas);
@@ -102,7 +96,7 @@ void menu_widget_callback(CanvasApi* canvas, void* context) {
         }
     }
 
-    menu_event_unlock(menu->event);
+    release_mutex((ValueMutex*)context, menu);
 }
 
 void menu_update(Menu* menu) {
@@ -172,10 +166,22 @@ void menu_exit(Menu* menu) {
 }
 
 void menu_task(void* p) {
-    Menu* menu = menu_init();
-    menu_build_main(menu);
+    ValueMutex* menu_mutex = menu_init();
 
-    if(!furi_create_deprecated("menu", menu, sizeof(menu))) {
+    MenuEvent* menu_event = NULL;
+    {
+        Menu* menu = acquire_mutex_block(menu_mutex);
+        assert(menu);
+
+        menu_build_main(menu);
+
+        // immutable thread-safe object
+        menu_event = menu->event;
+
+        release_mutex(menu_mutex, menu);
+    }
+
+    if(!furi_create("menu", menu_mutex)) {
         printf("[menu_task] cannot create the menu record\n");
         furiac_exit(NULL);
     }
@@ -183,10 +189,11 @@ void menu_task(void* p) {
     furiac_ready();
 
     while(1) {
-        MenuMessage m = menu_event_next(menu->event);
+        MenuMessage m = menu_event_next(menu_event);
+
+        Menu* menu = acquire_mutex_block(menu_mutex);
 
         if(!menu->current && m.type != MenuMessageTypeOk) {
-            continue;
         } else if(m.type == MenuMessageTypeUp) {
             menu_up(menu);
         } else if(m.type == MenuMessageTypeDown) {
@@ -204,5 +211,7 @@ void menu_task(void* p) {
         } else {
             // TODO: fail somehow?
         }
+
+        release_mutex(menu_mutex, menu);
     }
 }

+ 2 - 0
applications/menu/menu.h

@@ -1,5 +1,7 @@
 #pragma once
 
+#include "menu/menu_item.h"
+
 typedef struct Menu Menu;
 typedef struct MenuItem MenuItem;
 

+ 0 - 17
applications/menu/menu_event.c

@@ -12,7 +12,6 @@
 struct MenuEvent {
     osMessageQueueId_t mqueue;
     osTimerId_t timeout_timer;
-    osMutexId_t lock_mutex;
 };
 
 void MenuEventimeout_callback(void* arg) {
@@ -32,29 +31,15 @@ MenuEvent* menu_event_alloc() {
         osTimerNew(MenuEventimeout_callback, osTimerOnce, menu_event, NULL);
     assert(menu_event->timeout_timer);
 
-    menu_event->lock_mutex = osMutexNew(NULL);
-    assert(menu_event->lock_mutex);
-
-    menu_event_lock(menu_event);
-
     return menu_event;
 }
 
 void menu_event_free(MenuEvent* menu_event) {
     assert(menu_event);
-    menu_event_unlock(menu_event);
     assert(osMessageQueueDelete(menu_event->mqueue) == osOK);
     free(menu_event);
 }
 
-void menu_event_lock(MenuEvent* menu_event) {
-    assert(osMutexAcquire(menu_event->lock_mutex, osWaitForever) == osOK);
-}
-
-void menu_event_unlock(MenuEvent* menu_event) {
-    assert(osMutexRelease(menu_event->lock_mutex) == osOK);
-}
-
 void menu_event_activity_notify(MenuEvent* menu_event) {
     assert(menu_event);
     osTimerStart(menu_event->timeout_timer, 60000U); // 1m timeout, return to main
@@ -63,10 +48,8 @@ void menu_event_activity_notify(MenuEvent* menu_event) {
 MenuMessage menu_event_next(MenuEvent* menu_event) {
     assert(menu_event);
     MenuMessage message;
-    menu_event_unlock(menu_event);
     while(osMessageQueueGet(menu_event->mqueue, &message, NULL, osWaitForever) != osOK) {
     };
-    menu_event_lock(menu_event);
     return message;
 }
 

+ 0 - 4
applications/menu/menu_event.h

@@ -25,10 +25,6 @@ MenuEvent* menu_event_alloc();
 
 void menu_event_free(MenuEvent* menu_event);
 
-void menu_event_lock(MenuEvent* menu_event);
-
-void menu_event_unlock(MenuEvent* menu_event);
-
 void menu_event_activity_notify(MenuEvent* menu_event);
 
 MenuMessage menu_event_next(MenuEvent* menu_event);

+ 7 - 15
applications/startup.h

@@ -2,8 +2,6 @@
 
 #include "flipper.h"
 
-#define FURI_LIB (const char*[])
-
 #ifdef APP_TEST
 void flipper_test_app(void* p);
 #endif
@@ -27,6 +25,8 @@ void u8g2_qrcode(void* p);
 void fatfs_list(void* p);
 void gui_task(void* p);
 void backlight_control(void* p);
+void app_loader(void* p);
+void cc1101_workaround(void* p);
 
 const FlipperStartupApp FLIPPER_STARTUP[] = {
 #ifdef APP_DISPLAY
@@ -44,6 +44,11 @@ const FlipperStartupApp FLIPPER_STARTUP[] = {
 
 #ifdef APP_MENU
     {.app = menu_task, .name = "menu_task", .libs = {1, FURI_LIB{"gui_task"}}},
+    {.app = app_loader, .name = "app_loader", .libs = {1, FURI_LIB{"menu_task"}}},
+#endif
+
+#ifdef APP_CC1101
+    {.app = cc1101_workaround, .name = "cc1101 workaround", .libs = {1, FURI_LIB{"gui_task"}}},
 #endif
 
 // {.app = coreglitch_demo_0, .name = "coreglitch_demo_0", .libs = ""},
@@ -52,23 +57,11 @@ const FlipperStartupApp FLIPPER_STARTUP[] = {
     {.app = flipper_test_app, .name = "test app", .libs = {0}},
 #endif
 
-#ifdef APP_EXAMPLE_BLINK
-    {.app = application_blink, .name = "blink", .libs = {0}},
-#endif
-
-#ifdef APP_EXAMPLE_UART_WRITE
-    {.app = application_uart_write, .name = "uart write", .libs = {0}},
-#endif
-
 #ifdef APP_EXAMPLE_IPC
     {.app = application_ipc_display, .name = "ipc display", .libs = {0}},
     {.app = application_ipc_widget, .name = "ipc widget", .libs = {0}},
 #endif
 
-#ifdef APP_EXAMPLE_INPUT_DUMP
-    {.app = application_input_dump, .name = "input dump", .libs = {1, FURI_LIB{"input_task"}}},
-#endif
-
 #ifdef APP_EXAMPLE_QRCODE
     {.app = u8g2_qrcode, .name = "u8g2_qrcode", .libs = {1, FURI_LIB{"display_u8g2"}}},
 #endif
@@ -80,5 +73,4 @@ const FlipperStartupApp FLIPPER_STARTUP[] = {
 #ifdef APP_EXAMPLE_DISPLAY
     {.app = u8g2_example, .name = "u8g2_example", .libs = {1, FURI_LIB{"display_u8g2"}}},
 #endif
-
 };

+ 4 - 0
core/flipper.h

@@ -16,6 +16,8 @@ extern "C" {
 }
 #endif
 
+#include <stdio.h>
+
 // Arduino defines
 
 #define pinMode app_gpio_init
@@ -32,3 +34,5 @@ extern "C" {
 #define HIGH true
 
 void set_exitcode(uint32_t _exitcode);
+
+#define FURI_LIB (const char*[])

+ 12 - 0
core/flipper_v2.h

@@ -1,5 +1,11 @@
 #pragma once
 
+#include "flipper.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include "api-basic/furi.h"
 //#include "api-basic/flapp.h"
 #include "cmsis_os2.h"
@@ -7,3 +13,9 @@
 #include "api-basic/pubsub.h"
 
 #include "api-basic/memmgr.h"
+
+#include "gui/gui.h"
+
+#ifdef __cplusplus
+}
+#endif

+ 2 - 0
firmware/targets/f2/Inc/main.h

@@ -125,6 +125,8 @@ void Error_Handler(void);
 #define BUTTON_OK_EXTI_IRQn EXTI9_5_IRQn
 /* USER CODE BEGIN Private defines */
 
+#define MISO_PIN GpioPin{.port = GPIOC, .pin = GPIO_PIN_11}
+
 /* USER CODE END Private defines */
 
 #ifdef __cplusplus