Merge branch 'dev' into release-candidate

This commit is contained in:
Aleksandr Kutuzov 2021-10-25 03:41:13 +03:00
commit 7b730dd08a
302 changed files with 47943 additions and 49987 deletions

1
.gitattributes vendored
View File

@ -1 +1,2 @@
* text=auto

View File

@ -57,13 +57,13 @@ jobs:
if [[ ${{ github.event_name }} == 'pull_request' ]]; then
REF=${{ github.head_ref }}
fi
BRANCH_OR_TAG=${REF##*/}
BRANCH_OR_TAG=${REF#refs/*/}
SHA=$(git rev-parse --short HEAD)
if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
SUFFIX=${BRANCH_OR_TAG}
SUFFIX=${BRANCH_OR_TAG//\//_}
else
SUFFIX=${BRANCH_OR_TAG}-$(date +'%d%m%Y')-${SHA}
SUFFIX=${BRANCH_OR_TAG//\//_}-$(date +'%d%m%Y')-${SHA}
fi
echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV
@ -186,9 +186,9 @@ jobs:
- name: 'Upload artifacts to update server'
if: ${{ !github.event.pull_request.head.repo.fork }}
uses: burnett01/rsync-deployments@4.1
uses: burnett01/rsync-deployments@5.1
with:
switches: -avzP --delete
switches: -avzP --delete --mkpath
path: artifacts/
remote_path: "${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${{steps.names.outputs.artifacts-path}}/"
remote_host: ${{ secrets.RSYNC_DEPLOY_HOST }}

5
.gitignore vendored
View File

@ -9,22 +9,18 @@ compile_commands.json
# JetBrains IDEs
.idea/
# Python VirtEnvironments
.env
.venv
env/
venv/
# Python Byte-compiled / optimized files
__pycache__/
*.py[cod]
*$py.class
.obj/
target_lo/build/
target_*/build/
bindings/
.DS_Store
.mxproject
@ -35,4 +31,3 @@ bindings/
# legendary cmake's
build
CMakeLists.txt
firmware/targets/f2/CMakeLists.txt

80
Makefile Normal file
View File

@ -0,0 +1,80 @@
PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST)))))
COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x
NPROCS := 1
OS := $(shell uname -s)
ifeq ($(OS), Linux)
NPROCS := $(shell grep -c ^processor /proc/cpuinfo)
else ifeq ($(OS), Darwin)
NPROCS := $(shell sysctl -n hw.ncpu)
endif
.PHONY: all
all: bootloader_all firmware_all
.PHONY: whole
whole: flash_radio bootloader_flash firmware_flash
.PHONY: clean
clean: bootloader_clean firmware_clean
.PHONY: flash
flash: bootloader_flash firmware_flash
.PHONY: debug
debug:
$(MAKE) -C firmware -j$(NPROCS) debug
.PHONY: wipe
wipe:
$(PROJECT_ROOT)/scripts/flash.py wipe
$(PROJECT_ROOT)/scripts/ob.py set
.PHONY: bootloader_all
bootloader_all:
$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) all
.PHONY: firmware_all
firmware_all:
$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) all
.PHONY: bootloader_clean
bootloader_clean:
$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) clean
.PHONY: firmware_clean
firmware_clean:
$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) clean
.PHONY: bootloader_flash
bootloader_flash:
rm $(PROJECT_ROOT)/bootloader/.obj/f*/flash || true
$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) flash
.PHONY: firmware_flash
firmware_flash:
rm $(PROJECT_ROOT)/firmware/.obj/f*/flash || true
$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash
.PHONY: flash_radio
flash_radio:
$(PROJECT_ROOT)/scripts/flash.py core2radio 0x080CA000 $(COPRO_DIR)/stm32wb5x_BLE_Stack_full_fw.bin
$(PROJECT_ROOT)/scripts/ob.py set
.PHONY: flash_radio_fus
flash_radio_fus:
@echo
@echo "================ DON'T DO IT ================"
@echo "= Flashing FUS is going to erase secure enclave ="
@echo "= You will loose ability to use encrypted assets ="
@echo "= type 'find / -exec rm -rf {} \;' ="
@echo "= In case if you still want to continue ="
@echo "================ JUST DON'T ================"
@echo
.PHONY:
flash_radio_fus_please_i_m_not_going_to_complain:
$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin
$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin
$(PROJECT_ROOT)/scripts/ob.py set

View File

@ -49,11 +49,11 @@ One liner: `./flash_core1_main.sh`
2. Reboot Flipper to Bootloader
- Press and hold `← Left` + `↩ Back` for reset
- Release `← Left` and keep holding `↩ Back` until blue LED lights up
- Release `↩ Back`
![Switch to DFU sequence](https://habrastorage.org/webt/uu/c3/g2/uuc3g2n36f2sju19rskcvjzjf6w.png)
- Release `↩ Back` and keep holding `← Left` until blue LED lights up
- Release `← Left`
<!-- ![Switch to DFU sequence](https://habrastorage.org/webt/uu/c3/g2/uuc3g2n36f2sju19rskcvjzjf6w.png) -->
1. Run `dfu-util -D full.dfu -a 0`
3. Run `dfu-util -D full.dfu -a 0`
# Build from source
@ -70,29 +70,41 @@ One liner: `./flash_core1_main.sh`
docker-compose up -d
```
## Compile everything
```sh
docker-compose exec dev make -j$(nproc)
```
## Flash everything
```sh
docker-compose exec dev make -j$(nproc) whole
```
## Compile bootloader
```sh
docker-compose exec dev make -j$(nproc) -C bootloader TARGET=f6
docker-compose exec dev make -j$(nproc) -C bootloader
```
Bootloader compilation results:
* `bootloader/.obj/f6/bootloader.elf`
* `bootloader/.obj/f6/bootloader.hex`
* `bootloader/.obj/f6/bootloader.bin`
* **`bootloader/.obj/f6/bootloader.dfu`** - should be used to flash
* `bootloader/.obj/f7/bootloader.elf`
* `bootloader/.obj/f7/bootloader.hex`
* `bootloader/.obj/f7/bootloader.bin`
* **`bootloader/.obj/f7/bootloader.dfu`** - should be used to flash
## Compile firmware
```sh
docker-compose exec dev make -j$(nproc) -C firmware TARGET=f6
docker-compose exec dev make -j$(nproc) -C firmware
```
Firmware compilation results:
* `firmware/.obj/f6/firmware.elf`
* `firmware/.obj/f6/firmware.hex`
* `firmware/.obj/f6/firmware.bin`
* **`firmware/.obj/f6/firmware.dfu`** - should be used to flash
* `firmware/.obj/f7/firmware.elf`
* `firmware/.obj/f7/firmware.hex`
* `firmware/.obj/f7/firmware.bin`
* **`firmware/.obj/f7/firmware.dfu`** - should be used to flash
## Concatenate bootloader and firmware
@ -103,19 +115,19 @@ That's exactly how we generate our `full` builds.
1. Concatenate HEX files:
```sh
docker-compose exec dev srec_cat \
bootloader/.obj/f6/bootloader.hex -Intel \
firmware/.obj/f6/firmware.hex -Intel \
-o firmware/.obj/f6/full.hex -Intel
bootloader/.obj/f7/bootloader.hex -Intel \
firmware/.obj/f7/firmware.hex -Intel \
-o firmware/.obj/f7/full.hex -Intel
```
2. Convert HEX to DFU:
```sh
docker-compose exec dev hex2dfu \
-i firmware/.obj/f6/full.hex \
-o firmware/.obj/f6/full.dfu \
-l "Flipper Zero F6"
-i firmware/.obj/f7/full.hex \
-o firmware/.obj/f7/full.dfu \
-l "Flipper Zero F7"
```
Finally, you will have **`firmware/.obj/f6/full.dfu`** file that can be distributed and flashed.
Finally, you will have **`firmware/.obj/f7/full.dfu`** file that can be distributed and flashed.
# Links
* Discord: [flipp.dev/discord](https://flipp.dev/discord)

View File

@ -110,7 +110,7 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage*
} else {
string_cat_printf(
buffer,
"%s [%s]\n%s [%s]\n[%s] %s",
"%s [%s]\n%s [%s]\n[%d] %s",
version_get_version(ver),
version_get_builddate(ver),
version_get_githash(ver),
@ -140,7 +140,7 @@ static DialogMessageButton boot_version_screen(DialogsApp* dialogs, DialogMessag
} else {
string_cat_printf(
buffer,
"%s [%s]\n%s [%s]\n[%s] %s",
"%s [%s]\n%s [%s]\n[%d] %s",
version_get_version(ver),
version_get_builddate(ver),
version_get_githash(ver),

View File

@ -19,8 +19,11 @@ extern int32_t desktop_srv(void* p);
// Apps
extern int32_t accessor_app(void* p);
extern int32_t archive_app(void* p);
extern int32_t bad_usb_app(void* p);
extern int32_t blink_test_app(void* p);
extern int32_t bt_debug_app(void* p);
extern int32_t delay_test_app(void* p);
extern int32_t display_test_app(void* p);
extern int32_t gpio_app(void* p);
extern int32_t ibutton_app(void* p);
extern int32_t irda_app(void* p);
@ -32,11 +35,9 @@ extern int32_t nfc_app(void* p);
extern int32_t scened_app(void* p);
extern int32_t storage_test_app(void* p);
extern int32_t subghz_app(void* p);
extern int32_t vibro_test_app(void* p);
extern int32_t bt_debug_app(void* p);
extern int32_t usb_test_app(void* p);
extern int32_t usb_mouse_app(void* p);
extern int32_t bad_usb_app(void* p);
extern int32_t usb_test_app(void* p);
extern int32_t vibro_test_app(void* p);
// Plugins
extern int32_t music_player_app(void* p);
@ -208,43 +209,43 @@ const size_t FLIPPER_PLUGINS_COUNT = sizeof(FLIPPER_PLUGINS) / sizeof(FlipperApp
// Plugin menu
const FlipperApplication FLIPPER_DEBUG_APPS[] = {
#ifdef APP_BLINK
{.app = blink_test_app, .name = "Blink Test", .stack_size = 1024, .icon = &A_Plugins_14},
{.app = blink_test_app, .name = "Blink Test", .stack_size = 1024, .icon = NULL},
#endif
#ifdef APP_VIBRO_DEMO
{.app = vibro_test_app, .name = "Vibro Test", .stack_size = 1024, .icon = &A_Plugins_14},
#ifdef APP_VIBRO_TEST
{.app = vibro_test_app, .name = "Vibro Test", .stack_size = 1024, .icon = NULL},
#endif
#ifdef APP_KEYPAD_TEST
{.app = keypad_test_app, .name = "Keypad Test", .stack_size = 1024, .icon = &A_Plugins_14},
{.app = keypad_test_app, .name = "Keypad Test", .stack_size = 1024, .icon = NULL},
#endif
#ifdef APP_ACCESSOR
{.app = accessor_app, .name = "Accessor", .stack_size = 4096, .icon = &A_Plugins_14},
{.app = accessor_app, .name = "Accessor", .stack_size = 4096, .icon = NULL},
#endif
#ifdef APP_USB_TEST
{.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = &A_Plugins_14},
{.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = NULL},
#endif
#ifdef APP_USB_MOUSE
{.app = usb_mouse_app, .name = "USB Mouse demo", .stack_size = 1024, .icon = &A_Plugins_14},
{.app = usb_mouse_app, .name = "USB Mouse demo", .stack_size = 1024, .icon = NULL},
#endif
#ifdef APP_BAD_USB
{.app = bad_usb_app, .name = "Bad USB test", .stack_size = 2048, .icon = &A_Plugins_14},
{.app = bad_usb_app, .name = "Bad USB test", .stack_size = 2048, .icon = NULL},
#endif
#ifdef APP_IRDA_MONITOR
{.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = &A_Plugins_14},
{.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = NULL},
#endif
#ifdef APP_SCENED
{.app = scened_app, .name = "Templated Scene", .stack_size = 1024, .icon = &A_Plugins_14},
{.app = scened_app, .name = "Templated Scene", .stack_size = 1024, .icon = NULL},
#endif
#ifdef APP_LF_RFID
{.app = lfrfid_debug_app, .name = "LF-RFID Debug", .stack_size = 1024, .icon = &A_125khz_14},
{.app = lfrfid_debug_app, .name = "LF-RFID Debug", .stack_size = 1024, .icon = NULL},
#endif
#ifdef SRV_BT
@ -252,7 +253,11 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
#endif
#ifdef APP_UNIT_TESTS
{.app = delay_test_app, .name = "Delay Test App", .stack_size = 1024, .icon = &A_Plugins_14},
{.app = delay_test_app, .name = "Delay Test", .stack_size = 1024, .icon = NULL},
#endif
#ifdef APP_DISPLAY_TEST
{.app = display_test_app, .name = "Display Test", .stack_size = 1024, .icon = NULL},
#endif
};

View File

@ -38,12 +38,14 @@ APP_MUSIC_PLAYER = 1
# Debug
APP_ACCESSOR = 1
APP_BLINK = 1
APP_BLINK = 1
APP_IRDA_MONITOR = 1
APP_KEYPAD_TEST = 1
APP_SD_TEST = 1
APP_VIBRO_DEMO = 1
APP_VIBRO_TEST = 1
APP_USB_TEST = 1
APP_DISPLAY_TEST = 1
APP_USB_MOUSE = 1
APP_BAD_USB = 1
endif
@ -117,9 +119,9 @@ SRV_GUI = 1
endif
APP_VIBRO_DEMO ?= 0
ifeq ($(APP_VIBRO_DEMO), 1)
CFLAGS += -DAPP_VIBRO_DEMO
APP_VIBRO_TEST ?= 0
ifeq ($(APP_VIBRO_TEST), 1)
CFLAGS += -DAPP_VIBRO_TEST
SRV_GUI = 1
endif
@ -127,21 +129,26 @@ endif
APP_USB_TEST ?= 0
ifeq ($(APP_USB_TEST), 1)
CFLAGS += -DAPP_USB_TEST
SRV_INPUT = 1
SRV_GUI = 1
endif
APP_DISPLAY_TEST ?= 0
ifeq ($(APP_DISPLAY_TEST), 1)
CFLAGS += -DAPP_DISPLAY_TEST
SRV_GUI = 1
endif
APP_USB_MOUSE ?= 0
ifeq ($(APP_USB_MOUSE), 1)
CFLAGS += -DAPP_USB_MOUSE
SRV_INPUT = 1
SRV_GUI = 1
endif
APP_BAD_USB ?= 0
ifeq ($(APP_BAD_USB), 1)
CFLAGS += -DAPP_BAD_USB
SRV_INPUT = 1
SRV_GUI = 1
endif

View File

@ -4,13 +4,20 @@
#define BT_SERVICE_TAG "BT"
static void bt_draw_statusbar_callback(Canvas* canvas, void* context) {
canvas_draw_icon(canvas, 0, 0, &I_Bluetooth_5x8);
furi_assert(context);
Bt* bt = context;
if(bt->status == BtStatusAdvertising) {
canvas_draw_icon(canvas, 0, 0, &I_Bluetooth_5x8);
} else if(bt->status == BtStatusConnected) {
canvas_draw_icon(canvas, 0, 0, &I_BT_Pair_9x8);
}
}
static ViewPort* bt_statusbar_view_port_alloc() {
static ViewPort* bt_statusbar_view_port_alloc(Bt* bt) {
ViewPort* statusbar_view_port = view_port_alloc();
view_port_set_width(statusbar_view_port, 5);
view_port_draw_callback_set(statusbar_view_port, bt_draw_statusbar_callback, NULL);
view_port_draw_callback_set(statusbar_view_port, bt_draw_statusbar_callback, bt);
view_port_enabled_set(statusbar_view_port, false);
return statusbar_view_port;
}
@ -18,10 +25,11 @@ static ViewPort* bt_statusbar_view_port_alloc() {
static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) {
furi_assert(bt);
string_t pin_str;
string_init_printf(pin_str, "%06d", pin);
dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0);
string_init_printf(pin_str, "Pairing code\n%06d", pin);
dialog_message_set_text(
bt->dialog_message, string_get_cstr(pin_str), 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(bt->dialog_message, "Back", NULL, NULL);
bt->dialog_message, string_get_cstr(pin_str), 64, 4, AlignCenter, AlignTop);
dialog_message_set_buttons(bt->dialog_message, "Quit", NULL, NULL);
dialog_message_show(bt->dialogs, bt->dialog_message);
string_clear(pin_str);
}
@ -50,7 +58,7 @@ Bt* bt_alloc() {
bt->message_queue = osMessageQueueNew(8, sizeof(BtMessage), NULL);
// Setup statusbar view port
bt->statusbar_view_port = bt_statusbar_view_port_alloc();
bt->statusbar_view_port = bt_statusbar_view_port_alloc(bt);
// Gui
bt->gui = furi_record_open("gui");
gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft);
@ -115,6 +123,11 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) {
Bt* bt = context;
if(event.type == BleEventTypeConnected) {
// Update status bar
bt->status = BtStatusConnected;
BtMessage message = {.type = BtMessageTypeUpdateStatusbar};
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
// Open RPC session
FURI_LOG_I(BT_SERVICE_TAG, "Open RPC connection");
bt->rpc_session = rpc_open_session(bt->rpc);
rpc_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback, bt);
@ -123,8 +136,8 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) {
// Update battery level
PowerInfo info;
power_get_info(bt->power, &info);
BtMessage message = {
.type = BtMessageTypeUpdateBatteryLevel, .data.battery_level = info.charge};
message.type = BtMessageTypeUpdateBatteryLevel;
message.data.battery_level = info.charge;
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
} else if(event.type == BleEventTypeDisconnected) {
FURI_LOG_I(BT_SERVICE_TAG, "Close RPC connection");
@ -132,7 +145,12 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) {
rpc_close_session(bt->rpc_session);
bt->rpc_session = NULL;
}
} else if(event.type == BleEventTypeStartAdvertising || event.type == BleEventTypeStopAdvertising) {
} else if(event.type == BleEventTypeStartAdvertising) {
bt->status = BtStatusAdvertising;
BtMessage message = {.type = BtMessageTypeUpdateStatusbar};
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
} else if(event.type == BleEventTypeStopAdvertising) {
bt->status = BtStatusOff;
BtMessage message = {.type = BtMessageTypeUpdateStatusbar};
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
} else if(event.type == BleEventTypePinCodeShow) {
@ -142,6 +160,18 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) {
}
}
static void bt_statusbar_update(Bt* bt) {
if(bt->status == BtStatusAdvertising) {
view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_5x8));
view_port_enabled_set(bt->statusbar_view_port, true);
} else if(bt->status == BtStatusConnected) {
view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_BT_Pair_9x8));
view_port_enabled_set(bt->statusbar_view_port, true);
} else {
view_port_enabled_set(bt->statusbar_view_port, false);
}
}
int32_t bt_srv() {
Bt* bt = bt_alloc();
furi_record_create("bt", bt);
@ -160,14 +190,14 @@ int32_t bt_srv() {
}
}
// Update statusbar
view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_active());
bt_statusbar_update(bt);
BtMessage message;
while(1) {
furi_check(osMessageQueueGet(bt->message_queue, &message, NULL, osWaitForever) == osOK);
if(message.type == BtMessageTypeUpdateStatusbar) {
// Update statusbar
view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_active());
bt_statusbar_update(bt);
} else if(message.type == BtMessageTypeUpdateBatteryLevel) {
// Update battery level
if(furi_hal_bt_is_active()) {

View File

@ -15,6 +15,12 @@
#include "../bt_settings.h"
typedef enum {
BtStatusOff,
BtStatusAdvertising,
BtStatusConnected,
} BtStatus;
typedef enum {
BtMessageTypeUpdateStatusbar,
BtMessageTypeUpdateBatteryLevel,
@ -33,6 +39,7 @@ typedef struct {
struct Bt {
BtSettings bt_settings;
BtStatus status;
osMessageQueueId_t message_queue;
Gui* gui;
ViewPort* statusbar_view_port;

View File

@ -55,12 +55,11 @@ static const uint8_t enclave_signature_expected[ENCLAVE_SIGNATURE_KEY_SLOTS][ENC
* Keys and values format MUST NOT BE changed
*/
void cli_command_device_info(Cli* cli, string_t args, void* context) {
// Device Info version
printf("device_info_major : %d\r\n", 1);
printf("device_info_minor : %d\r\n", 0);
// Model name
printf("hardware_model : %s\r\n", furi_hal_version_get_model_name());
const char* name = furi_hal_version_get_name_ptr();
if(name) {
printf("hardware_name : %s\r\n", name);
}
// Unique ID
printf("hardware_uid : ");
@ -70,35 +69,45 @@ void cli_command_device_info(Cli* cli, string_t args, void* context) {
}
printf("\r\n");
// OTP Revision
printf("hardware_otp_ver : %d\r\n", furi_hal_version_get_otp_version());
printf("hardware_timestamp : %lu\r\n", furi_hal_version_get_hw_timestamp());
// Board Revision
printf("hardware_ver : %d\r\n", furi_hal_version_get_hw_version());
printf("hardware_target : %d\r\n", furi_hal_version_get_hw_target());
printf("hardware_body : %d\r\n", furi_hal_version_get_hw_body());
printf("hardware_connect : %d\r\n", furi_hal_version_get_hw_connect());
printf("hardware_timestamp : %lu\r\n", furi_hal_version_get_hw_timestamp());
printf("hardware_display : %d\r\n", furi_hal_version_get_hw_display());
// Color and Region
// Board Personification
printf("hardware_color : %d\r\n", furi_hal_version_get_hw_color());
printf("hardware_region : %d\r\n", furi_hal_version_get_hw_region());
const char* name = furi_hal_version_get_name_ptr();
if(name) {
printf("hardware_name : %s\r\n", name);
}
// Bootloader Version
const Version* boot_version = furi_hal_version_get_boot_version();
if(boot_version) {
printf("boot_version : %s\r\n", version_get_version(boot_version));
printf("boot_target : %s\r\n", version_get_target(boot_version));
printf("boot_commit : %s\r\n", version_get_githash(boot_version));
printf("boot_branch : %s\r\n", version_get_gitbranch(boot_version));
printf("boot_branch_num : %s\r\n", version_get_gitbranchnum(boot_version));
printf("boot_version : %s\r\n", version_get_version(boot_version));
printf("boot_build_date : %s\r\n", version_get_builddate(boot_version));
printf("boot_target : %d\r\n", version_get_target(boot_version));
}
// Firmware version
const Version* firmware_version = furi_hal_version_get_firmware_version();
if(firmware_version) {
printf("firmware_version : %s\r\n", version_get_version(firmware_version));
printf("firmware_target : %s\r\n", version_get_target(firmware_version));
printf("firmware_commit : %s\r\n", version_get_githash(firmware_version));
printf("firmware_branch : %s\r\n", version_get_gitbranch(firmware_version));
printf("firmware_branch_num : %s\r\n", version_get_gitbranchnum(firmware_version));
printf("firmware_version : %s\r\n", version_get_version(firmware_version));
printf("firmware_build_date : %s\r\n", version_get_builddate(firmware_version));
printf("firmware_target : %d\r\n", version_get_target(firmware_version));
}
WirelessFwInfo_t pWirelessInfo;

View File

@ -248,6 +248,8 @@ static void badusb_worker(void* context) {
evt.worker.state = WorkerStateDone;
osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
furi_hal_hid_kb_release_all();
osThreadExit();
}

View File

@ -0,0 +1,225 @@
#include "display_test.h"
#include <furi-hal.h>
#include <furi.h>
// Need access to u8g2
#include <gui/gui_i.h>
#include <gui/canvas_i.h>
#include <u8g2_glue.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/submenu.h>
#include <gui/modules/variable-item-list.h>
#include "view_display_test.h"
typedef struct {
Gui* gui;
ViewDispatcher* view_dispatcher;
ViewDisplayTest* view_display_test;
VariableItemList* variable_item_list;
Submenu* submenu;
bool config_bias;
uint8_t config_contrast;
uint8_t config_regulation_ratio;
} DisplayTest;
typedef enum {
DisplayTestViewSubmenu,
DisplayTestViewConfigure,
DisplayTestViewDisplayTest,
} DisplayTestView;
const bool config_bias_value[] = {
true,
false,
};
const char* const config_bias_text[] = {
"1/7",
"1/9",
};
const uint8_t config_regulation_ratio_value[] = {
0b000,
0b001,
0b010,
0b011,
0b100,
0b101,
0b110,
0b111,
};
const char* const config_regulation_ratio_text[] = {
"3.0",
"3.5",
"4.0",
"4.5",
"5.0",
"5.5",
"6.0",
"6.5",
};
static void display_test_submenu_callback(void* context, uint32_t index) {
DisplayTest* instance = (DisplayTest*)context;
view_dispatcher_switch_to_view(instance->view_dispatcher, index);
}
static uint32_t display_test_previous_callback(void* context) {
return DisplayTestViewSubmenu;
}
static uint32_t display_test_exit_callback(void* context) {
return VIEW_NONE;
}
static void display_test_reload_config(DisplayTest* instance) {
FURI_LOG_I(
"DisplayTest",
"contrast: %d, regulation_ratio: %d, bias: %d",
instance->config_contrast,
instance->config_regulation_ratio,
instance->config_bias);
u8x8_d_st756x_init(
&instance->gui->canvas->fb.u8x8,
instance->config_contrast,
instance->config_regulation_ratio,
instance->config_bias);
gui_update(instance->gui);
}
static void display_config_set_bias(VariableItem* item) {
DisplayTest* instance = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, config_bias_text[index]);
instance->config_bias = config_bias_value[index];
display_test_reload_config(instance);
}
static void display_config_set_regulation_ratio(VariableItem* item) {
DisplayTest* instance = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, config_regulation_ratio_text[index]);
instance->config_regulation_ratio = config_regulation_ratio_value[index];
display_test_reload_config(instance);
}
static void display_config_set_contrast(VariableItem* item) {
DisplayTest* instance = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
string_t temp;
string_init(temp);
string_cat_printf(temp, "%d", index);
variable_item_set_current_value_text(item, string_get_cstr(temp));
string_clear(temp);
instance->config_contrast = index;
display_test_reload_config(instance);
}
DisplayTest* display_test_alloc() {
DisplayTest* instance = furi_alloc(sizeof(DisplayTest));
View* view = NULL;
instance->gui = furi_record_open("gui");
instance->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(instance->view_dispatcher);
view_dispatcher_attach_to_gui(
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
// Test
instance->view_display_test = view_display_test_alloc();
view = view_display_test_get_view(instance->view_display_test);
view_set_previous_callback(view, display_test_previous_callback);
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewDisplayTest, view);
// Configure
instance->variable_item_list = variable_item_list_alloc();
view = variable_item_list_get_view(instance->variable_item_list);
view_set_previous_callback(view, display_test_previous_callback);
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view);
// Configurtion items
VariableItem* item;
instance->config_bias = false;
instance->config_contrast = 32;
instance->config_regulation_ratio = 0b101;
// Bias
item = variable_item_list_add(
instance->variable_item_list,
"Bias:",
COUNT_OF(config_bias_value),
display_config_set_bias,
instance);
variable_item_set_current_value_index(item, 1);
variable_item_set_current_value_text(item, config_bias_text[1]);
// Regulation Ratio
item = variable_item_list_add(
instance->variable_item_list,
"Reg Ratio:",
COUNT_OF(config_regulation_ratio_value),
display_config_set_regulation_ratio,
instance);
variable_item_set_current_value_index(item, 5);
variable_item_set_current_value_text(item, config_regulation_ratio_text[5]);
// Contrast
item = variable_item_list_add(
instance->variable_item_list, "Contrast:", 64, display_config_set_contrast, instance);
variable_item_set_current_value_index(item, 32);
variable_item_set_current_value_text(item, "32");
// Menu
instance->submenu = submenu_alloc();
view = submenu_get_view(instance->submenu);
view_set_previous_callback(view, display_test_exit_callback);
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewSubmenu, view);
submenu_add_item(
instance->submenu,
"Test",
DisplayTestViewDisplayTest,
display_test_submenu_callback,
instance);
submenu_add_item(
instance->submenu,
"Configure",
DisplayTestViewConfigure,
display_test_submenu_callback,
instance);
return instance;
}
void display_test_free(DisplayTest* instance) {
view_dispatcher_remove_view(instance->view_dispatcher, DisplayTestViewSubmenu);
submenu_free(instance->submenu);
view_dispatcher_remove_view(instance->view_dispatcher, DisplayTestViewConfigure);
variable_item_list_free(instance->variable_item_list);
view_dispatcher_remove_view(instance->view_dispatcher, DisplayTestViewDisplayTest);
view_display_test_free(instance->view_display_test);
view_dispatcher_free(instance->view_dispatcher);
furi_record_close("gui");
free(instance);
}
int32_t display_test_run(DisplayTest* instance) {
view_dispatcher_switch_to_view(instance->view_dispatcher, DisplayTestViewSubmenu);
view_dispatcher_run(instance->view_dispatcher);
return 0;
}
int32_t display_test_app(void* p) {
DisplayTest* instance = display_test_alloc();
int32_t ret = display_test_run(instance);
display_test_free(instance);
return ret;
}

View File

@ -0,0 +1 @@
#pragma once

View File

@ -0,0 +1,185 @@
#include "view_display_test.h"
typedef struct {
uint32_t test;
uint32_t size;
uint32_t counter;
bool flip_flop;
} ViewDisplayTestModel;
struct ViewDisplayTest {
View* view;
osTimerId_t timer;
};
static void view_display_test_draw_callback_intro(Canvas* canvas, void* _model) {
canvas_draw_str(canvas, 12, 24, "Use < and > to switch tests");
canvas_draw_str(canvas, 12, 36, "Use ^ and v to switch size");
canvas_draw_str(canvas, 32, 48, "Use (o) to flip");
}
static void view_display_test_draw_callback_fill(Canvas* canvas, void* _model) {
ViewDisplayTestModel* model = _model;
if(model->flip_flop) {
uint8_t width = canvas_width(canvas);
uint8_t height = canvas_height(canvas);
canvas_draw_box(canvas, 0, 0, width, height);
}
}
static void view_display_test_draw_callback_hstripe(Canvas* canvas, void* _model) {
ViewDisplayTestModel* model = _model;
uint8_t block = 1 + model->size;
uint8_t width = canvas_width(canvas);
uint8_t height = canvas_height(canvas);
for(uint8_t y = model->flip_flop * block; y < height; y += 2 * block) {
canvas_draw_box(canvas, 0, y, width, block);
}
}
static void view_display_test_draw_callback_vstripe(Canvas* canvas, void* _model) {
ViewDisplayTestModel* model = _model;
uint8_t block = 1 + model->size;
uint8_t width = canvas_width(canvas);
uint8_t height = canvas_height(canvas);
for(uint8_t x = model->flip_flop * block; x < width; x += 2 * block) {
canvas_draw_box(canvas, x, 0, block, height);
}
}
static void view_display_test_draw_callback_check(Canvas* canvas, void* _model) {
ViewDisplayTestModel* model = _model;
uint8_t block = 1 + model->size;
uint8_t width = canvas_width(canvas);
uint8_t height = canvas_height(canvas);
bool flip_flop = model->flip_flop;
for(uint8_t x = 0; x < width; x += block) {
bool last_flip_flop = flip_flop;
for(uint8_t y = 0; y < height; y += block) {
if(flip_flop) {
canvas_draw_box(canvas, x, y, block, block);
}
flip_flop = !flip_flop;
}
if(last_flip_flop == flip_flop) {
flip_flop = !flip_flop;
}
}
}
static void view_display_test_draw_callback_move(Canvas* canvas, void* _model) {
ViewDisplayTestModel* model = _model;
uint8_t block = 1 + model->size;
uint8_t width = canvas_width(canvas) - block;
uint8_t height = canvas_height(canvas) - block;
uint8_t x = model->counter % width;
if((model->counter / width) % 2) {
x = width - x;
}
uint8_t y = model->counter % height;
if((model->counter / height) % 2) {
y = height - y;
}
canvas_draw_box(canvas, x, y, block, block);
}
ViewDrawCallback view_display_test_tests[] = {
view_display_test_draw_callback_intro,
view_display_test_draw_callback_fill,
view_display_test_draw_callback_hstripe,
view_display_test_draw_callback_vstripe,
view_display_test_draw_callback_check,
view_display_test_draw_callback_move,
};
static void view_display_test_draw_callback(Canvas* canvas, void* _model) {
ViewDisplayTestModel* model = _model;
view_display_test_tests[model->test](canvas, _model);
}
static bool view_display_test_input_callback(InputEvent* event, void* context) {
ViewDisplayTest* instance = context;
bool consumed = false;
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
with_view_model(
instance->view, (ViewDisplayTestModel * model) {
if(event->key == InputKeyLeft && model->test > 0) {
model->test--;
consumed = true;
} else if(
event->key == InputKeyRight &&
model->test < (COUNT_OF(view_display_test_tests) - 1)) {
model->test++;
consumed = true;
} else if(event->key == InputKeyDown && model->size > 0) {
model->size--;
consumed = true;
} else if(event->key == InputKeyUp && model->size < 24) {
model->size++;
consumed = true;
} else if(event->key == InputKeyOk) {
model->flip_flop = !model->flip_flop;
consumed = true;
}
return consumed;
});
}
return consumed;
}
static void view_display_test_enter(void* context) {
ViewDisplayTest* instance = context;
osTimerStart(instance->timer, osKernelGetTickFreq() / 32);
}
static void view_display_test_exit(void* context) {
ViewDisplayTest* instance = context;
osTimerStop(instance->timer);
}
static void view_display_test_timer_callback(void* context) {
ViewDisplayTest* instance = context;
with_view_model(
instance->view, (ViewDisplayTestModel * model) {
model->counter++;
return true;
});
}
ViewDisplayTest* view_display_test_alloc() {
ViewDisplayTest* instance = furi_alloc(sizeof(ViewDisplayTest));
instance->view = view_alloc();
view_set_context(instance->view, instance);
view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(ViewDisplayTestModel));
view_set_draw_callback(instance->view, view_display_test_draw_callback);
view_set_input_callback(instance->view, view_display_test_input_callback);
view_set_enter_callback(instance->view, view_display_test_enter);
view_set_exit_callback(instance->view, view_display_test_exit);
instance->timer =
osTimerNew(view_display_test_timer_callback, osTimerPeriodic, instance, NULL);
return instance;
}
void view_display_test_free(ViewDisplayTest* instance) {
furi_assert(instance);
osTimerDelete(instance->timer);
view_free(instance->view);
free(instance);
}
View* view_display_test_get_view(ViewDisplayTest* instance) {
furi_assert(instance);
return instance->view;
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
#include <gui/view.h>
typedef struct ViewDisplayTest ViewDisplayTest;
ViewDisplayTest* view_display_test_alloc();
void view_display_test_free(ViewDisplayTest* instance);
View* view_display_test_get_view(ViewDisplayTest* instance);

View File

@ -15,8 +15,9 @@ void desktop_scene_hw_mismatch_on_enter(void* context) {
snprintf(
buffer,
sizeof(buffer),
"HW target: F%d\nFW target: " TARGET,
furi_hal_version_get_hw_target());
"HW target: %d\nFW target: %d",
furi_hal_version_get_hw_target(),
version_get_target(NULL));
popup_set_context(popup, desktop);
popup_set_header(popup, "!!!! HW Mismatch !!!!", 60, 14, AlignCenter, AlignCenter);
popup_set_text(popup, buffer, 60, 37, AlignCenter, AlignCenter);

View File

@ -67,7 +67,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
canvas_draw_str(canvas, 5, 43, buffer);
snprintf(
buffer, sizeof(buffer), "[%s] %s", version_get_target(ver), version_get_gitbranch(ver));
buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver));
canvas_draw_str(canvas, 5, 54, buffer);
} else {

View File

@ -44,7 +44,7 @@ void desktop_first_start_render(Canvas* canvas, void* model) {
"%s %s%s",
"I am",
my_name ? my_name : "Unknown",
",\ncyberdesktop\nliving in your\npocket >");
",\ncyberdolphin\nliving in your\npocket >");
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_54x49);
elements_multiline_text_framed(canvas, 60, 17, buf);
} else if(m->page == 6) {

6
applications/gpio/gpio_app.c Executable file → Normal file
View File

@ -42,6 +42,9 @@ GpioApp* gpio_app_alloc() {
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewUsbUart, variable_item_list_get_view(app->var_item_list));
scene_manager_next_scene(app->scene_manager, GpioSceneStart);
return app;
@ -54,10 +57,13 @@ void gpio_app_free(GpioApp* app) {
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList);
variable_item_list_free(app->var_item_list);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart);
gpio_test_free(app->gpio_test);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Close records
furi_record_close("gui");
furi_record_close("notification");

View File

@ -7,8 +7,8 @@
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <notification/notification-messages.h>
#include <gui/modules/variable-item-list.h>
#include "views/gpio_test.h"
@ -25,4 +25,5 @@ struct GpioApp {
typedef enum {
GpioAppViewVarItemList,
GpioAppViewGpioTest,
GpioAppViewUsbUart,
} GpioAppView;

View File

@ -1,2 +1,3 @@
ADD_SCENE(gpio, start, Start)
ADD_SCENE(gpio, test, Test)
ADD_SCENE(gpio, usb_uart, UsbUart)

8
applications/gpio/scenes/gpio_scene_start.c Executable file → Normal file
View File

@ -4,10 +4,12 @@
#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL)
#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL)
#define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL)
#define GPIO_SCENE_START_CUSTOM_EVENT_USB_UART (3UL)
enum GpioItem {
GpioItemOtg,
GpioItemTest,
GpioItemUsbUart,
};
enum GpioOtg {
@ -27,6 +29,9 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind
if(index == GpioItemTest) {
view_dispatcher_send_custom_event(
app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_TEST);
} else if(index == GpioItemUsbUart) {
view_dispatcher_send_custom_event(
app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_USB_UART);
}
}
@ -65,6 +70,7 @@ void gpio_scene_start_on_enter(void* context) {
variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]);
}
variable_item_list_add(var_item_list, "GPIO tester", 0, NULL, NULL);
variable_item_list_add(var_item_list, "USB-UART bridge", 0, NULL, NULL);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewVarItemList);
}
@ -80,6 +86,8 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
furi_hal_power_disable_otg();
} else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_TEST) {
scene_manager_next_scene(app->scene_manager, GpioSceneTest);
} else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_USB_UART) {
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
}
consumed = true;
}

View File

@ -0,0 +1,352 @@
#include "../gpio_app_i.h"
#include "furi-hal.h"
#include <stream_buffer.h>
#include <furi-hal-usb-cdc_i.h>
#include "usb_cdc.h"
#define USB_PKT_LEN CDC_DATA_SZ
#define USB_UART_RX_BUF_SIZE (USB_PKT_LEN * 3)
#define USB_UART_TX_BUF_SIZE (USB_PKT_LEN * 3)
typedef enum {
WorkerCmdStop = (1 << 0),
} WorkerCommandFlags;
typedef enum {
UsbUartLineIndexVcp,
UsbUartLineIndexUart,
UsbUartLineIndexBaudrate,
UsbUartLineIndexEnable,
UsbUartLineIndexDisable,
} LineIndex;
typedef enum {
UsbUartPortUSART1 = 0,
UsbUartPortLPUART1 = 1,
} PortIdx;
typedef struct {
uint8_t vcp_ch;
PortIdx uart_ch;
uint32_t baudrate;
} UsbUartConfig;
typedef struct {
UsbUartConfig cfg_cur;
UsbUartConfig cfg_set;
char br_text[8];
bool running;
osThreadId_t parent_thread;
osThreadAttr_t thread_attr;
osThreadId_t thread;
osThreadAttr_t tx_thread_attr;
osThreadId_t tx_thread;
StreamBufferHandle_t rx_stream;
osSemaphoreId_t rx_done_sem;
osSemaphoreId_t usb_sof_sem;
StreamBufferHandle_t tx_stream;
uint8_t rx_buf[USB_PKT_LEN];
uint8_t tx_buf[USB_PKT_LEN];
} UsbUartParams;
static UsbUartParams* usb_uart;
static const char* vcp_ch[] = {"0 (CLI)", "1"};
static const char* uart_ch[] = {"USART1", "LPUART1"};
static const char* baudrate_mode[] = {"Host"};
static const uint32_t baudrate_list[] = {
2400,
9600,
19200,
38400,
57600,
115200,
230400,
460800,
921600,
};
static void vcp_on_cdc_tx_complete();
static void vcp_on_cdc_rx();
static void vcp_state_callback(uint8_t state);
static void vcp_on_cdc_control_line(uint8_t state);
static void vcp_on_line_config(struct usb_cdc_line_coding* config);
static CdcCallbacks cdc_cb = {
vcp_on_cdc_tx_complete,
vcp_on_cdc_rx,
vcp_state_callback,
vcp_on_cdc_control_line,
vcp_on_line_config,
};
/* USB UART worker */
static void usb_uart_tx_thread(void* context);
static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if(ev == UartIrqEventRXNE) {
size_t ret =
xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
furi_check(ret == 1);
ret = xStreamBufferBytesAvailable(usb_uart->rx_stream);
if(ret > USB_PKT_LEN) osSemaphoreRelease(usb_uart->rx_done_sem);
} else if(ev == UartIrqEventIDLE) {
osSemaphoreRelease(usb_uart->rx_done_sem);
}
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
static void usb_uart_worker(void* context) {
memcpy(&usb_uart->cfg_cur, &usb_uart->cfg_set, sizeof(UsbUartConfig));
usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1);
usb_uart->rx_done_sem = osSemaphoreNew(1, 1, NULL);
usb_uart->usb_sof_sem = osSemaphoreNew(1, 1, NULL);
usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1);
usb_uart->tx_thread = NULL;
usb_uart->tx_thread_attr.name = "usb_uart_tx";
usb_uart->tx_thread_attr.stack_size = 512;
UsbMode usb_mode_prev = furi_hal_usb_get_config();
if(usb_uart->cfg_cur.vcp_ch == 0) {
furi_hal_usb_set_config(UsbModeVcpSingle);
furi_hal_vcp_disable();
} else {
furi_hal_usb_set_config(UsbModeVcpDual);
}
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) {
furi_hal_usart_init();
furi_hal_usart_set_irq_cb(usb_uart_on_irq_cb);
if(usb_uart->cfg_cur.baudrate != 0)
furi_hal_usart_set_br(usb_uart->cfg_cur.baudrate);
else
vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch));
} else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) {
furi_hal_lpuart_init();
furi_hal_lpuart_set_irq_cb(usb_uart_on_irq_cb);
if(usb_uart->cfg_cur.baudrate != 0)
furi_hal_lpuart_set_br(usb_uart->cfg_cur.baudrate);
else
vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch));
}
furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, &cdc_cb);
usb_uart->tx_thread = osThreadNew(usb_uart_tx_thread, NULL, &usb_uart->tx_thread_attr);
while(1) {
furi_check(osSemaphoreAcquire(usb_uart->rx_done_sem, osWaitForever) == osOK);
if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, 0) == WorkerCmdStop) break;
size_t len = 0;
do {
len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0);
if(len > 0) {
if(osSemaphoreAcquire(usb_uart->usb_sof_sem, 100) == osOK)
furi_hal_cdc_send(usb_uart->cfg_cur.vcp_ch, usb_uart->rx_buf, len);
else
xStreamBufferReset(usb_uart->rx_stream);
}
} while(len > 0);
}
osThreadTerminate(usb_uart->tx_thread);
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
furi_hal_usart_deinit();
else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
furi_hal_lpuart_deinit();
furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, NULL);
furi_hal_usb_set_config(usb_mode_prev);
if(usb_uart->cfg_cur.vcp_ch == 0) furi_hal_vcp_enable();
vStreamBufferDelete(usb_uart->rx_stream);
osSemaphoreDelete(usb_uart->rx_done_sem);
osSemaphoreDelete(usb_uart->usb_sof_sem);
vStreamBufferDelete(usb_uart->tx_stream);
osThreadFlagsSet(usb_uart->parent_thread, WorkerCmdStop);
osThreadExit();
}
static void usb_uart_tx_thread(void* context) {
uint8_t data = 0;
while(1) {
size_t len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, osWaitForever);
if(len > 0) {
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
furi_hal_usart_tx(&data, len);
else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
furi_hal_lpuart_tx(&data, len);
}
}
osThreadExit();
}
/* VCP callbacks */
static void vcp_on_cdc_tx_complete() {
osSemaphoreRelease(usb_uart->usb_sof_sem);
}
static void vcp_on_cdc_rx() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream);
if(max_len > 0) {
if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN;
int32_t size = furi_hal_cdc_receive(usb_uart->cfg_cur.vcp_ch, usb_uart->tx_buf, max_len);
if(size > 0) {
size_t ret = xStreamBufferSendFromISR(
usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken);
furi_check(ret == size);
}
}
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
static void vcp_state_callback(uint8_t state) {
}
static void vcp_on_cdc_control_line(uint8_t state) {
}
static void vcp_on_line_config(struct usb_cdc_line_coding* config) {
if((usb_uart->cfg_cur.baudrate == 0) && (config->dwDTERate != 0)) {
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
furi_hal_usart_set_br(config->dwDTERate);
else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
furi_hal_lpuart_set_br(config->dwDTERate);
}
}
/* USB UART app */
static void usb_uart_enable() {
if(usb_uart->running == false) {
usb_uart->thread = NULL;
usb_uart->thread_attr.name = "usb_uart";
usb_uart->thread_attr.stack_size = 1024;
usb_uart->parent_thread = osThreadGetId();
usb_uart->running = true;
usb_uart->thread = osThreadNew(usb_uart_worker, NULL, &usb_uart->thread_attr);
}
}
static void usb_uart_disable() {
if(usb_uart->running == true) {
osThreadFlagsSet(usb_uart->thread, WorkerCmdStop);
osSemaphoreRelease(usb_uart->rx_done_sem);
osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, osWaitForever);
usb_uart->running = false;
}
}
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
//GpioApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == UsbUartLineIndexEnable) {
usb_uart_enable();
} else if(event.event == UsbUartLineIndexDisable) {
usb_uart_disable();
}
consumed = true;
}
return consumed;
}
/* Scene callbacks */
static void line_vcp_cb(VariableItem* item) {
//GpioApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, vcp_ch[index]);
usb_uart->cfg_set.vcp_ch = index;
}
static void line_port_cb(VariableItem* item) {
//GpioApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, uart_ch[index]);
usb_uart->cfg_set.uart_ch = index;
}
static void line_baudrate_cb(VariableItem* item) {
//GpioApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
if(index > 0) {
snprintf(usb_uart->br_text, 7, "%lu", baudrate_list[index - 1]);
variable_item_set_current_value_text(item, usb_uart->br_text);
usb_uart->cfg_set.baudrate = baudrate_list[index - 1];
} else {
variable_item_set_current_value_text(item, baudrate_mode[index]);
usb_uart->cfg_set.baudrate = 0;
}
}
static void gpio_scene_usb_uart_enter_callback(void* context, uint32_t index) {
furi_assert(context);
GpioApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void gpio_scene_usb_uart_on_enter(void* context) {
GpioApp* app = context;
VariableItemList* var_item_list = app->var_item_list;
usb_uart = furi_alloc(sizeof(UsbUartParams));
VariableItem* item;
variable_item_list_set_enter_callback(var_item_list, gpio_scene_usb_uart_enter_callback, app);
item = variable_item_list_add(var_item_list, "VCP Channel", 2, line_vcp_cb, app);
variable_item_set_current_value_index(item, 0);
variable_item_set_current_value_text(item, vcp_ch[0]);
item = variable_item_list_add(var_item_list, "UART Port", 2, line_port_cb, app);
variable_item_set_current_value_index(item, 0);
variable_item_set_current_value_text(item, uart_ch[0]);
item = variable_item_list_add(
var_item_list,
"Baudrate",
sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
line_baudrate_cb,
app);
variable_item_set_current_value_index(item, 0);
variable_item_set_current_value_text(item, baudrate_mode[0]);
item = variable_item_list_add(var_item_list, "Enable", 0, NULL, NULL);
item = variable_item_list_add(var_item_list, "Disable", 0, NULL, NULL);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
}
void gpio_scene_usb_uart_on_exit(void* context) {
GpioApp* app = context;
usb_uart_disable();
variable_item_list_clean(app->var_item_list);
free(usb_uart);
}

19
applications/gui/canvas.c Normal file → Executable file
View File

@ -4,9 +4,7 @@
#include <furi.h>
#include <furi-hal.h>
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
#include <u8g2_glue.h>
Canvas* canvas_init() {
Canvas* canvas = furi_alloc(sizeof(Canvas));
@ -14,19 +12,16 @@ Canvas* canvas_init() {
furi_hal_power_insomnia_enter();
canvas->orientation = CanvasOrientationHorizontal;
u8g2_Setup_st7565_erc12864_alt_f(
&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
// send init sequence to the display, display is in sleep mode after this
u8g2_InitDisplay(&canvas->fb);
u8g2_SetContrast(&canvas->fb, 36);
// wake up display
u8g2_ClearBuffer(&canvas->fb);
u8g2_SetPowerSave(&canvas->fb, 0);
u8g2_SendBuffer(&canvas->fb);
furi_hal_power_insomnia_exit();
return canvas;
}
@ -44,7 +39,6 @@ void canvas_reset(Canvas* canvas) {
void canvas_commit(Canvas* canvas) {
furi_assert(canvas);
u8g2_SetPowerSave(&canvas->fb, 0); // wake up display
u8g2_SendBuffer(&canvas->fb);
}
@ -194,13 +188,15 @@ void canvas_draw_icon_animation(
x += canvas->offset_x;
y += canvas->offset_y;
uint8_t* icon_data = NULL;
furi_hal_compress_icon_decode(icon_animation_get_data(icon_animation), &icon_data);
u8g2_DrawXBM(
&canvas->fb,
x,
y,
icon_animation_get_width(icon_animation),
icon_animation_get_height(icon_animation),
icon_animation_get_data(icon_animation));
icon_data);
}
void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
@ -209,8 +205,9 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
x += canvas->offset_x;
y += canvas->offset_y;
u8g2_DrawXBM(
&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_get_data(icon));
uint8_t* icon_data = NULL;
furi_hal_compress_icon_decode(icon_get_data(icon), &icon_data);
u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data);
}
void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) {

View File

@ -259,7 +259,7 @@ void variable_item_list_clean(VariableItemList* variable_item_list) {
VariableItemArray_it_t it;
for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
VariableItemArray_next(it)) {
string_clean(VariableItemArray_ref(it)->current_value_text);
string_clear(VariableItemArray_ref(it)->current_value_text);
}
VariableItemArray_clean(model->items);
return false;

View File

@ -1,68 +0,0 @@
#include "u8g2/u8g2.h"
#include <furi-hal.h>
#include <furi.h>
static FuriHalSpiDevice* u8g2_periphery_display = NULL;
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
switch(msg) {
case U8X8_MSG_GPIO_AND_DELAY_INIT:
/* HAL initialization contains all what we need so we can skip this part. */
break;
case U8X8_MSG_DELAY_MILLI:
osDelay(arg_int);
break;
case U8X8_MSG_DELAY_10MICRO:
delay_us(10);
break;
case U8X8_MSG_DELAY_100NANO:
asm("nop");
break;
case U8X8_MSG_GPIO_RESET:
hal_gpio_write(&gpio_display_rst, arg_int);
break;
default:
return 0;
}
return 1;
}
uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
switch(msg) {
case U8X8_MSG_BYTE_SEND:
furi_hal_spi_bus_tx(u8g2_periphery_display->bus, (uint8_t*)arg_ptr, arg_int, 10000);
break;
case U8X8_MSG_BYTE_SET_DC:
hal_gpio_write(&gpio_display_di, arg_int);
break;
case U8X8_MSG_BYTE_INIT:
break;
case U8X8_MSG_BYTE_START_TRANSFER:
furi_assert(u8g2_periphery_display == NULL);
u8g2_periphery_display =
(FuriHalSpiDevice*)furi_hal_spi_device_get(FuriHalSpiDeviceIdDisplay);
hal_gpio_write(u8g2_periphery_display->chip_select, false);
break;
case U8X8_MSG_BYTE_END_TRANSFER:
furi_assert(u8g2_periphery_display);
hal_gpio_write(u8g2_periphery_display->chip_select, true);
furi_hal_spi_device_return(u8g2_periphery_display);
u8g2_periphery_display = NULL;
break;
default:
return 0;
}
return 1;
}

View File

@ -509,6 +509,14 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) {
"Mifare Ultralight Type: %d, Pages: %d",
mf_ul_read.type,
mf_ul_read.pages_to_read);
FURI_LOG_I(NFC_WORKER_TAG, "Reading signature ...");
tx_len = mf_ul_prepare_read_signature(tx_buff);
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
FURI_LOG_W(NFC_WORKER_TAG, "Failed reading signature");
memset(mf_ul_read.data.signature, 0, sizeof(mf_ul_read.data.signature));
} else {
mf_ul_parse_read_signature_response(rx_buff, &mf_ul_read);
}
} else if(err == ERR_TIMEOUT) {
FURI_LOG_W(
NFC_WORKER_TAG,
@ -540,15 +548,6 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) {
rx_buff, 0x00, mf_ul_read.pages_to_read - 1, &mf_ul_read);
}
FURI_LOG_I(NFC_WORKER_TAG, "Reading signature ...");
tx_len = mf_ul_prepare_read_signature(tx_buff);
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
FURI_LOG_W(NFC_WORKER_TAG, "Failed reading signature");
memset(mf_ul_read.data.signature, 0, sizeof(mf_ul_read.data.signature));
} else {
mf_ul_parse_read_signature_response(rx_buff, &mf_ul_read);
}
FURI_LOG_I(NFC_WORKER_TAG, "Reading 3 counters ...");
for(uint8_t i = 0; i < 3; i++) {
tx_len = mf_ul_prepare_read_cnt(tx_buff, i);

View File

@ -11,4 +11,7 @@ void test_furi_create_open() {
// 2. Open it
void* record = furi_record_open("test/holding");
mu_assert_pointers_eq(record, &test_data);
furi_record_close("test/holding");
furi_record_destroy("test/holding");
}

View File

@ -247,6 +247,11 @@ MU_TEST(test_decoder_necext1) {
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
}
MU_TEST(test_decoder_long_packets_with_nec_start) {
RUN_DECODER(test_decoder_nec42ext_input1, test_decoder_nec42ext_expected1);
RUN_DECODER(test_decoder_nec42ext_input2, test_decoder_nec42ext_expected2);
}
MU_TEST(test_encoder_sirc) {
RUN_ENCODER(test_encoder_sirc_input1, test_encoder_sirc_expected1);
RUN_ENCODER(test_encoder_sirc_input2, test_encoder_sirc_expected2);
@ -310,6 +315,7 @@ MU_TEST_SUITE(test_irda_decoder_encoder) {
MU_RUN_TEST(test_decoder_rc6);
MU_RUN_TEST(test_encoder_rc6);
MU_RUN_TEST(test_decoder_unexpected_end_in_sequence);
MU_RUN_TEST(test_decoder_long_packets_with_nec_start);
MU_RUN_TEST(test_decoder_nec);
MU_RUN_TEST(test_decoder_samsung32);
MU_RUN_TEST(test_decoder_necext1);

View File

@ -306,3 +306,36 @@ const IrdaMessage test_nec42ext[] = {
{IrdaProtocolNEC42ext, 0x1555555, 0x5555, true},
};
const uint32_t test_decoder_nec42ext_input1[] = {
2000000, 9000, 4500, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 8
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 16
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 24
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 32
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 40
560, 560, 560, 560, 560, // 42
};
const uint32_t test_decoder_nec42ext_input2[] = {
2000000, 9000, 4500, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 8
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 16
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 24
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 32
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 40
560, 560, 560, 560, 560, 560, 560, // 43 - failed
2000000, 9000, 4500, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 8
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 16
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 24
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 32
560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, // 40
560, 560, 560, 560, 560, 10000, 560, // 42 OK + 1 failed
};
const IrdaMessage test_decoder_nec42ext_expected1[] = {
{IrdaProtocolNEC42ext, 0x00, 0, false},
};
const IrdaMessage test_decoder_nec42ext_expected2[] = {
{IrdaProtocolNEC42ext, 0x00, 0, false},
};

View File

@ -1,45 +1,95 @@
/*
_____---------______--____--__--__--____------____--__----____--__----__--__--____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
_____---------______--____--__--__------____--____--__----____--__----__--__--____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
_____---------______--____--__--__--____------____--__----____--__----__--__--____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
_____---------______--____--__--__--____------____--__----____--__----__--__--____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
_____---------______--____--__--__--____------____--__----____--__----__--__--____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
_____---------______--____--__--__------____--____--__----____--__----__--__--____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 93 A0 0
s m2 m1 m0 T | address | command |
// 93 A0 0
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
//27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888,
// --__----__--__--
// 0 | 0 | 1 | 1 | 1
//444, 444, 888, 444, 444, 444, 444,
//888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
_____---------______--____--__--__------____--____--__----____--__----__--__--____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 93 A0 1
// 93 A0 1
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
//27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888,
//444, 444, 888, 444, 444, 444, 444,
//888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
_____---------______--____--__--__--____------____--__----____----____--__----____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 94 A0 0
// 94 A0 0
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 888, 888, 444, 444, 888, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
//27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888,
//----____--__----
//0 | 1 | 0 | 0 | 1
//888, 888, 444, 444, 888,
//888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
_____---------______--____--__--__------____--____--__----____----____--__----____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 94 A0 1
// 94 A0 1
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 888, 888, 444, 444, 888, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
//27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888,
//888, 888, 444, 444, 888,
//888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
_____---------______--____--__--__--____------____--__----____----____----__--____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 95 A0 0
// 95 A0 0
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 888, 888, 888, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
//27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888,
//----____----__--
//0 | 1 | 0 | 1 | 1
//888, 888, 888, 444, 444,
//888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
_____---------______--____--__--__------____--____--__----____----____----__--____----____--__--__--__--__--___________
| 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 95 A0 1
// 95 A0 1
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 888, 888, 888, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
//27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888,
//888, 888, 888, 444, 444,
//888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
*/
const uint32_t test_decoder_rc6_input1[] = {
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
// 94 A0 0
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 888, 888, 444, 444, 888, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
// 93 A0 1
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888, // failed
// failed 95
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 888, 888, 888, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888,
// 93 A0 0
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888, // failed
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888,
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888, // failed
// 94 A0 1
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 888, 888, 444, 444, 888, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
// 95 A0 0
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 888, 888, 888, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
// failed 93 + 1 sample
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, 444,
// failed 93
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888, 444, 444,
// failed 93
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888,
// 95 A0 1
27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 888, 888, 888, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444,
};
const IrdaMessage test_decoder_rc6_expected1[] = {
{IrdaProtocolRC6, 0x93, 0xA0, false}, // toggle 0
{IrdaProtocolRC6, 0x94, 0xA0, false}, // toggle 0
{IrdaProtocolRC6, 0x93, 0xA0, false}, // toggle 1
// {IrdaProtocolRC6, 0x93, 0xA0, false},
// {IrdaProtocolRC6, 0x95, 0xA0, false}, failed
{IrdaProtocolRC6, 0x93, 0xA0, false}, // toggle 0
{IrdaProtocolRC6, 0x93, 0xA0, true}, // toggle 0
{IrdaProtocolRC6, 0x93, 0xA0, true}, // toggle 0
{IrdaProtocolRC6, 0x93, 0xA0, false}, // toggle 1
// {IrdaProtocolRC6, 0x93, 0xA0, false},
{IrdaProtocolRC6, 0x93, 0xA0, false}, // toggle 0
{IrdaProtocolRC6, 0x93, 0xA1, false}, // toggle 1
// {IrdaProtocolRC6, 0x93, 0xA0, false},
{IrdaProtocolRC6, 0x94, 0xA0, false}, // toggle 1
{IrdaProtocolRC6, 0x95, 0xA0, false}, // toggle 0
// {IrdaProtocolRC6, 0x93, 0xA0, false}, failed
// {IrdaProtocolRC6, 0x93, 0xA0, false}, failed
// {IrdaProtocolRC6, 0x93, 0xA0, false}, failed
{IrdaProtocolRC6, 0x95, 0xA0, false}, // toggle 1
};
const IrdaMessage test_encoder_rc6_input1[] = {

View File

@ -5,6 +5,7 @@
#include "pb_decode.h"
#include "rpc/rpc_i.h"
#include "storage.pb.h"
#include "storage/filesystem-api-defines.h"
#include "storage/storage.h"
#include <furi.h>
#include "../minunit.h"
@ -104,7 +105,8 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) {
if(fileinfo.flags & FSF_DIRECTORY) {
clean_directory(fs_api, fullname);
}
storage_common_remove(fs_api, fullname);
FS_Error error = storage_common_remove(fs_api, fullname);
furi_assert(error == FSE_OK);
free(fullname);
}
free(name);

File diff suppressed because one or more lines are too long

View File

@ -13,6 +13,7 @@ extern const Icon I_ibutt_10px;
extern const Icon I_unknown_10px;
extern const Icon I_ble_10px;
extern const Icon I_125_10px;
extern const Icon I_BLE_Pairing_128x64;
extern const Icon I_ButtonRightSmall_3x5;
extern const Icon I_ButtonLeft_4x7;
extern const Icon I_ButtonLeftSmall_3x5;
@ -108,6 +109,7 @@ extern const Icon I_SDcardMounted_11x8;
extern const Icon I_SDcardFail_11x8;
extern const Icon I_USBConnected_15x8;
extern const Icon I_Bluetooth_5x8;
extern const Icon I_BT_Pair_9x8;
extern const Icon I_Background_128x11;
extern const Icon I_Scanning_123x52;
extern const Icon I_Quest_7x8;

View File

@ -30,8 +30,8 @@ typedef enum _PB_CommandStatus {
PB_CommandStatus_ERROR_STORAGE_INTERNAL = 11, /* *< Internal error */
PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED = 12, /* *< Functon not implemented */
PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN = 13, /* *< File/Dir already opened */
PB_CommandStatus_ERROR_APP_CANT_START = 16, /* *< Can't start app - either wrong name, or internal error */
PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED = 17 /* *< Another app is running */
PB_CommandStatus_ERROR_APP_CANT_START = 16, /* *< Can't start app - or internal error */
PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED = 17 /* *< Another app is running */
} PB_CommandStatus;
/* Struct definitions */

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -17,8 +17,7 @@ LIB_DIR = $(PROJECT_ROOT)/lib
# U8G2 display library
U8G2_DIR = $(LIB_DIR)/u8g2
CFLAGS += -I$(U8G2_DIR)
C_SOURCES += $(U8G2_DIR)/u8x8_d_st7565.c
C_SOURCES += $(U8G2_DIR)/u8g2_d_setup.c
C_SOURCES += $(U8G2_DIR)/u8g2_glue.c
C_SOURCES += $(U8G2_DIR)/u8g2_intersection.c
C_SOURCES += $(U8G2_DIR)/u8g2_setup.c
C_SOURCES += $(U8G2_DIR)/u8g2_d_memory.c

View File

@ -5,21 +5,18 @@ What it does?
- [x] Hardware initialization
- [x] Boot process LED indicators
- [x] Firmware update
- [ ] Firmware CRC check
- [ ] Interactive UI
- [ ] FS check
- [x] Errata crutches
- [ ] Recovery mode
- [ ] Errata crutches
# Targets
| Name | Bootloader | Firmware | Reset | DFU |
| | Address | Address | Combo | Combo |
-----------------------------------------------------------------------------
| f4 | 0x08000000 | 0x00008000 | L+Back | L+Back, hold L |
| f7 | 0x08000000 | 0x00008000 | L+Back | L+Back, hold L |
Also there is a ST bootloader combo available on empty device: L+Ok+Back, release Back,Left.
Target independend code and headers in `src`and `target/include` folders.
Target independend code and headers in `src` and `target/include` folders.
# Building
@ -38,16 +35,16 @@ Target independend code and headers in `src`and `target/include` folders.
# Flashing
Using stlink(st-flash):
Using SWD (STLink):
`make -C bootloader flash`
Or use ST bootloader:
Or use DFU (USB):
`make -C bootloader upload`
# Debug
Using stlink (st-util + gdb):
Using SWD (STLink):
`make -C bootloader debug`
`make -C bootloader debug`

View File

@ -0,0 +1,290 @@
#include <furi-hal-version.h>
#include <stm32wbxx.h>
#include <stm32wbxx_ll_rtc.h>
#include <stm32wbxx_ll_system.h>
#include <stdio.h>
#define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
#define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
/** OTP V0 Structure: prototypes and early EVT */
typedef struct {
uint8_t board_version;
uint8_t board_target;
uint8_t board_body;
uint8_t board_connect;
uint32_t header_timestamp;
char name[FURI_HAL_VERSION_NAME_LENGTH];
} FuriHalVersionOTPv0;
/** OTP V1 Structure: late EVT, DVT */
typedef struct {
/* First 64 bits: header */
uint16_t header_magic;
uint8_t header_version;
uint8_t header_reserved;
uint32_t header_timestamp;
/* Second 64 bits: board info */
uint8_t board_version; /** Board version */
uint8_t board_target; /** Board target firmware */
uint8_t board_body; /** Board body */
uint8_t board_connect; /** Board interconnect */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint16_t board_reserved; /** Reserved for future use, 0x0000 */
/* Third 64 bits: Unique Device Name */
char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
} FuriHalVersionOTPv1;
/** OTP V2 Structure: DVT2, PVT, Production */
typedef struct {
/* Early First 64 bits: header */
uint16_t header_magic;
uint8_t header_version;
uint8_t header_reserved;
uint32_t header_timestamp;
/* Early Second 64 bits: board info */
uint8_t board_version; /** Board version */
uint8_t board_target; /** Board target firmware */
uint8_t board_body; /** Board body */
uint8_t board_connect; /** Board interconnect */
uint8_t board_display; /** Board display */
uint8_t board_reserved2_0; /** Reserved for future use, 0x00 */
uint16_t board_reserved2_1; /** Reserved for future use, 0x0000 */
/* Late Third 64 bits: device info */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint16_t board_reserved3_0; /** Reserved for future use, 0x0000 */
uint32_t board_reserved3_1; /** Reserved for future use, 0x00000000 */
/* Late Fourth 64 bits: Unique Device Name */
char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
} FuriHalVersionOTPv2;
/** Represenation Model: */
typedef struct {
uint32_t timestamp;
uint8_t board_version; /** Board version */
uint8_t board_target; /** Board target firmware */
uint8_t board_body; /** Board body */
uint8_t board_connect; /** Board interconnect */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint8_t board_display; /** Board display */
char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */
uint8_t ble_mac[6];
} FuriHalVersion;
static FuriHalVersion furi_hal_version = {0};
static void furi_hal_version_set_name(const char* name) {
if(name != NULL) {
strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
snprintf(
furi_hal_version.device_name,
FURI_HAL_VERSION_DEVICE_NAME_LENGTH,
"xFlipper %s",
furi_hal_version.name);
} else {
snprintf(furi_hal_version.device_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH, "xFlipper");
}
furi_hal_version.device_name[0] = 0;
// BLE Mac address
uint32_t udn = LL_FLASH_GetUDN();
uint32_t company_id = LL_FLASH_GetSTCompanyID();
uint32_t device_id = LL_FLASH_GetDeviceID();
furi_hal_version.ble_mac[0] = (uint8_t)(udn & 0x000000FF);
furi_hal_version.ble_mac[1] = (uint8_t)((udn & 0x0000FF00) >> 8);
furi_hal_version.ble_mac[2] = (uint8_t)((udn & 0x00FF0000) >> 16);
furi_hal_version.ble_mac[3] = (uint8_t)device_id;
furi_hal_version.ble_mac[4] = (uint8_t)(company_id & 0x000000FF);
furi_hal_version.ble_mac[5] = (uint8_t)((company_id & 0x0000FF00) >> 8);
}
static void furi_hal_version_load_otp_default() {
furi_hal_version_set_name(NULL);
}
static void furi_hal_version_load_otp_v0() {
const FuriHalVersionOTPv0* otp = (FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS;
furi_hal_version.timestamp = otp->header_timestamp;
furi_hal_version.board_version = otp->board_version;
furi_hal_version.board_target = otp->board_target;
furi_hal_version.board_body = otp->board_body;
furi_hal_version.board_connect = otp->board_connect;
furi_hal_version_set_name(otp->name);
}
static void furi_hal_version_load_otp_v1() {
const FuriHalVersionOTPv1* otp = (FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS;
furi_hal_version.timestamp = otp->header_timestamp;
furi_hal_version.board_version = otp->board_version;
furi_hal_version.board_target = otp->board_target;
furi_hal_version.board_body = otp->board_body;
furi_hal_version.board_connect = otp->board_connect;
furi_hal_version.board_color = otp->board_color;
furi_hal_version.board_region = otp->board_region;
furi_hal_version_set_name(otp->name);
}
static void furi_hal_version_load_otp_v2() {
const FuriHalVersionOTPv2* otp = (FuriHalVersionOTPv2*)FURI_HAL_VERSION_OTP_ADDRESS;
// 1st block, programmed afer baking
furi_hal_version.timestamp = otp->header_timestamp;
// 2nd block, programmed afer baking
furi_hal_version.board_version = otp->board_version;
furi_hal_version.board_target = otp->board_target;
furi_hal_version.board_body = otp->board_body;
furi_hal_version.board_connect = otp->board_connect;
furi_hal_version.board_display = otp->board_display;
// 3rd and 4th blocks, programmed on FATP stage
if(otp->board_color != 0xFF) {
furi_hal_version.board_color = otp->board_color;
furi_hal_version.board_region = otp->board_region;
furi_hal_version_set_name(otp->name);
} else {
furi_hal_version.board_color = 0;
furi_hal_version.board_region = 0;
furi_hal_version_set_name(NULL);
}
}
void furi_hal_version_init() {
switch(furi_hal_version_get_otp_version()) {
case FuriHalVersionOtpVersionUnknown:
furi_hal_version_load_otp_default();
break;
case FuriHalVersionOtpVersionEmpty:
furi_hal_version_load_otp_default();
break;
case FuriHalVersionOtpVersion0:
furi_hal_version_load_otp_v0();
break;
case FuriHalVersionOtpVersion1:
furi_hal_version_load_otp_v1();
break;
case FuriHalVersionOtpVersion2:
furi_hal_version_load_otp_v2();
break;
default:
furi_hal_version_load_otp_default();
}
}
bool furi_hal_version_do_i_belong_here() {
return furi_hal_version_get_hw_target() == 7;
}
const char* furi_hal_version_get_model_name() {
return "Flipper Zero";
}
const FuriHalVersionOtpVersion furi_hal_version_get_otp_version() {
if(*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
return FuriHalVersionOtpVersionEmpty;
} else {
if(((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic ==
FURI_HAL_VERSION_OTP_HEADER_MAGIC) {
// Version 1+
uint8_t version = ((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_version;
if(version >= FuriHalVersionOtpVersion1 && version <= FuriHalVersionOtpVersion2) {
return version;
} else {
return FuriHalVersionOtpVersionUnknown;
}
} else if(((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
// Version 0
return FuriHalVersionOtpVersion0;
} else {
// Version Unknown
return FuriHalVersionOtpVersionUnknown;
}
}
}
const uint8_t furi_hal_version_get_hw_version() {
return furi_hal_version.board_version;
}
const uint8_t furi_hal_version_get_hw_target() {
return furi_hal_version.board_target;
}
const uint8_t furi_hal_version_get_hw_body() {
return furi_hal_version.board_body;
}
const FuriHalVersionColor furi_hal_version_get_hw_color() {
return furi_hal_version.board_color;
}
const uint8_t furi_hal_version_get_hw_connect() {
return furi_hal_version.board_connect;
}
const FuriHalVersionRegion furi_hal_version_get_hw_region() {
return furi_hal_version.board_region;
}
const FuriHalVersionDisplay furi_hal_version_get_hw_display() {
return furi_hal_version.board_display;
}
const uint32_t furi_hal_version_get_hw_timestamp() {
return furi_hal_version.timestamp;
}
const char* furi_hal_version_get_name_ptr() {
return *furi_hal_version.name == 0x00 ? NULL : furi_hal_version.name;
}
const char* furi_hal_version_get_device_name_ptr() {
return furi_hal_version.device_name + 1;
}
const char* furi_hal_version_get_ble_local_device_name_ptr() {
return furi_hal_version.device_name;
}
const uint8_t* furi_hal_version_get_ble_mac() {
return furi_hal_version.ble_mac;
}
const struct Version* furi_hal_version_get_firmware_version(void) {
return version_get();
}
const struct Version* furi_hal_version_get_boot_version(void) {
#ifdef NO_BOOTLOADER
return 0;
#else
/* Backup register which points to structure in flash memory */
return (const struct Version*)LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR1);
#endif
}
size_t furi_hal_version_uid_size() {
return 64 / 8;
}
const uint8_t* furi_hal_version_uid() {
return (const uint8_t*)UID64_BASE;
}

View File

@ -1,7 +1,21 @@
#include <furi-hal.h>
#include <stm32wbxx_ll_utils.h>
void furi_hal_init() {
furi_hal_i2c_init();
furi_hal_light_init();
furi_hal_spi_init();
furi_hal_version_init();
}
void delay(float milliseconds) {
LL_mDelay((uint32_t)milliseconds);
}
void delay_us(float microseconds) {
microseconds = microseconds / 1000;
if(microseconds < 1) {
microseconds = 1;
}
LL_mDelay((uint32_t)microseconds);
}

View File

@ -1,8 +0,0 @@
#pragma once
#include <furi-hal-i2c.h>
#include <furi-hal-light.h>
#include <furi-hal-resources.h>
#include <furi-hal-spi.h>
void furi_hal_init();

View File

@ -1,69 +0,0 @@
#include <u8g2.h>
#include <assert.h>
#include <furi-hal.h>
#include <stm32wbxx_ll_utils.h>
static FuriHalSpiDevice* u8g2_periphery_display = NULL;
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
switch(msg) {
case U8X8_MSG_GPIO_AND_DELAY_INIT:
/* HAL initialization contains all what we need so we can skip this part. */
break;
case U8X8_MSG_DELAY_MILLI:
LL_mDelay(arg_int);
break;
case U8X8_MSG_DELAY_10MICRO:
LL_mDelay(1);
break;
case U8X8_MSG_DELAY_100NANO:
asm("nop");
break;
case U8X8_MSG_GPIO_RESET:
hal_gpio_write(&gpio_display_rst, arg_int);
break;
default:
return 0;
}
return 1;
}
uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
switch(msg) {
case U8X8_MSG_BYTE_SEND:
furi_hal_spi_bus_tx(u8g2_periphery_display->bus, (uint8_t*)arg_ptr, arg_int, 10000);
break;
case U8X8_MSG_BYTE_SET_DC:
hal_gpio_write(&gpio_display_di, arg_int);
break;
case U8X8_MSG_BYTE_INIT:
break;
case U8X8_MSG_BYTE_START_TRANSFER:
assert(u8g2_periphery_display == NULL);
u8g2_periphery_display =
(FuriHalSpiDevice*)furi_hal_spi_device_get(FuriHalSpiDeviceIdDisplay);
hal_gpio_write(u8g2_periphery_display->chip_select, false);
break;
case U8X8_MSG_BYTE_END_TRANSFER:
assert(u8g2_periphery_display);
hal_gpio_write(u8g2_periphery_display->chip_select, true);
furi_hal_spi_device_return(u8g2_periphery_display);
u8g2_periphery_display = NULL;
break;
default:
return 0;
}
return 1;
}

View File

@ -11,7 +11,9 @@
#include <lib/toolbox/version.h>
#include <furi-hal.h>
#include <u8g2.h>
#include <u8g2_glue.h>
const uint8_t I_DFU_128x50[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0x00,
@ -81,9 +83,6 @@ const uint8_t I_DFU_128x50[] = {
#define RTC_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady())
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
void target_led_control(char* c) {
furi_hal_light_set(LightRed, 0x00);
furi_hal_light_set(LightGreen, 0x00);
@ -190,9 +189,8 @@ void target_display_init() {
hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
// Initialize
u8g2_t fb;
u8g2_Setup_st7565_erc12864_alt_f(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
u8g2_Setup_st756x_flipper(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
u8g2_InitDisplay(&fb);
u8g2_SetContrast(&fb, 36);
// Create payload
u8g2_ClearBuffer(&fb);
u8g2_SetDrawColor(&fb, 0x01);

View File

@ -12,6 +12,8 @@ MCU_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
CFLAGS += $(MCU_FLAGS) $(BOOT_CFLAGS) -DSTM32WB55xx -Wall -fdata-sections -ffunction-sections
LDFLAGS += $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs
HARDWARE_TARGET = 6
CUBE_DIR = $(PROJECT_ROOT)/lib/STM32CubeWB
# ST HAL

View File

@ -0,0 +1,290 @@
#include <furi-hal-version.h>
#include <stm32wbxx.h>
#include <stm32wbxx_ll_rtc.h>
#include <stm32wbxx_ll_system.h>
#include <stdio.h>
#define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
#define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
/** OTP V0 Structure: prototypes and early EVT */
typedef struct {
uint8_t board_version;
uint8_t board_target;
uint8_t board_body;
uint8_t board_connect;
uint32_t header_timestamp;
char name[FURI_HAL_VERSION_NAME_LENGTH];
} FuriHalVersionOTPv0;
/** OTP V1 Structure: late EVT, DVT */
typedef struct {
/* First 64 bits: header */
uint16_t header_magic;
uint8_t header_version;
uint8_t header_reserved;
uint32_t header_timestamp;
/* Second 64 bits: board info */
uint8_t board_version; /** Board version */
uint8_t board_target; /** Board target firmware */
uint8_t board_body; /** Board body */
uint8_t board_connect; /** Board interconnect */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint16_t board_reserved; /** Reserved for future use, 0x0000 */
/* Third 64 bits: Unique Device Name */
char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
} FuriHalVersionOTPv1;
/** OTP V2 Structure: DVT2, PVT, Production */
typedef struct {
/* Early First 64 bits: header */
uint16_t header_magic;
uint8_t header_version;
uint8_t header_reserved;
uint32_t header_timestamp;
/* Early Second 64 bits: board info */
uint8_t board_version; /** Board version */
uint8_t board_target; /** Board target firmware */
uint8_t board_body; /** Board body */
uint8_t board_connect; /** Board interconnect */
uint8_t board_display; /** Board display */
uint8_t board_reserved2_0; /** Reserved for future use, 0x00 */
uint16_t board_reserved2_1; /** Reserved for future use, 0x0000 */
/* Late Third 64 bits: device info */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint16_t board_reserved3_0; /** Reserved for future use, 0x0000 */
uint32_t board_reserved3_1; /** Reserved for future use, 0x00000000 */
/* Late Fourth 64 bits: Unique Device Name */
char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
} FuriHalVersionOTPv2;
/** Represenation Model: */
typedef struct {
uint32_t timestamp;
uint8_t board_version; /** Board version */
uint8_t board_target; /** Board target firmware */
uint8_t board_body; /** Board body */
uint8_t board_connect; /** Board interconnect */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint8_t board_display; /** Board display */
char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */
uint8_t ble_mac[6];
} FuriHalVersion;
static FuriHalVersion furi_hal_version = {0};
static void furi_hal_version_set_name(const char* name) {
if(name != NULL) {
strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
snprintf(
furi_hal_version.device_name,
FURI_HAL_VERSION_DEVICE_NAME_LENGTH,
"xFlipper %s",
furi_hal_version.name);
} else {
snprintf(furi_hal_version.device_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH, "xFlipper");
}
furi_hal_version.device_name[0] = 0;
// BLE Mac address
uint32_t udn = LL_FLASH_GetUDN();
uint32_t company_id = LL_FLASH_GetSTCompanyID();
uint32_t device_id = LL_FLASH_GetDeviceID();
furi_hal_version.ble_mac[0] = (uint8_t)(udn & 0x000000FF);
furi_hal_version.ble_mac[1] = (uint8_t)((udn & 0x0000FF00) >> 8);
furi_hal_version.ble_mac[2] = (uint8_t)((udn & 0x00FF0000) >> 16);
furi_hal_version.ble_mac[3] = (uint8_t)device_id;
furi_hal_version.ble_mac[4] = (uint8_t)(company_id & 0x000000FF);
furi_hal_version.ble_mac[5] = (uint8_t)((company_id & 0x0000FF00) >> 8);
}
static void furi_hal_version_load_otp_default() {
furi_hal_version_set_name(NULL);
}
static void furi_hal_version_load_otp_v0() {
const FuriHalVersionOTPv0* otp = (FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS;
furi_hal_version.timestamp = otp->header_timestamp;
furi_hal_version.board_version = otp->board_version;
furi_hal_version.board_target = otp->board_target;
furi_hal_version.board_body = otp->board_body;
furi_hal_version.board_connect = otp->board_connect;
furi_hal_version_set_name(otp->name);
}
static void furi_hal_version_load_otp_v1() {
const FuriHalVersionOTPv1* otp = (FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS;
furi_hal_version.timestamp = otp->header_timestamp;
furi_hal_version.board_version = otp->board_version;
furi_hal_version.board_target = otp->board_target;
furi_hal_version.board_body = otp->board_body;
furi_hal_version.board_connect = otp->board_connect;
furi_hal_version.board_color = otp->board_color;
furi_hal_version.board_region = otp->board_region;
furi_hal_version_set_name(otp->name);
}
static void furi_hal_version_load_otp_v2() {
const FuriHalVersionOTPv2* otp = (FuriHalVersionOTPv2*)FURI_HAL_VERSION_OTP_ADDRESS;
// 1st block, programmed afer baking
furi_hal_version.timestamp = otp->header_timestamp;
// 2nd block, programmed afer baking
furi_hal_version.board_version = otp->board_version;
furi_hal_version.board_target = otp->board_target;
furi_hal_version.board_body = otp->board_body;
furi_hal_version.board_connect = otp->board_connect;
furi_hal_version.board_display = otp->board_display;
// 3rd and 4th blocks, programmed on FATP stage
if(otp->board_color != 0xFF) {
furi_hal_version.board_color = otp->board_color;
furi_hal_version.board_region = otp->board_region;
furi_hal_version_set_name(otp->name);
} else {
furi_hal_version.board_color = 0;
furi_hal_version.board_region = 0;
furi_hal_version_set_name(NULL);
}
}
void furi_hal_version_init() {
switch(furi_hal_version_get_otp_version()) {
case FuriHalVersionOtpVersionUnknown:
furi_hal_version_load_otp_default();
break;
case FuriHalVersionOtpVersionEmpty:
furi_hal_version_load_otp_default();
break;
case FuriHalVersionOtpVersion0:
furi_hal_version_load_otp_v0();
break;
case FuriHalVersionOtpVersion1:
furi_hal_version_load_otp_v1();
break;
case FuriHalVersionOtpVersion2:
furi_hal_version_load_otp_v2();
break;
default:
furi_hal_version_load_otp_default();
}
}
bool furi_hal_version_do_i_belong_here() {
return furi_hal_version_get_hw_target() == 7;
}
const char* furi_hal_version_get_model_name() {
return "Flipper Zero";
}
const FuriHalVersionOtpVersion furi_hal_version_get_otp_version() {
if(*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
return FuriHalVersionOtpVersionEmpty;
} else {
if(((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic ==
FURI_HAL_VERSION_OTP_HEADER_MAGIC) {
// Version 1+
uint8_t version = ((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_version;
if(version >= FuriHalVersionOtpVersion1 && version <= FuriHalVersionOtpVersion2) {
return version;
} else {
return FuriHalVersionOtpVersionUnknown;
}
} else if(((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
// Version 0
return FuriHalVersionOtpVersion0;
} else {
// Version Unknown
return FuriHalVersionOtpVersionUnknown;
}
}
}
const uint8_t furi_hal_version_get_hw_version() {
return furi_hal_version.board_version;
}
const uint8_t furi_hal_version_get_hw_target() {
return furi_hal_version.board_target;
}
const uint8_t furi_hal_version_get_hw_body() {
return furi_hal_version.board_body;
}
const FuriHalVersionColor furi_hal_version_get_hw_color() {
return furi_hal_version.board_color;
}
const uint8_t furi_hal_version_get_hw_connect() {
return furi_hal_version.board_connect;
}
const FuriHalVersionRegion furi_hal_version_get_hw_region() {
return furi_hal_version.board_region;
}
const FuriHalVersionDisplay furi_hal_version_get_hw_display() {
return furi_hal_version.board_display;
}
const uint32_t furi_hal_version_get_hw_timestamp() {
return furi_hal_version.timestamp;
}
const char* furi_hal_version_get_name_ptr() {
return *furi_hal_version.name == 0x00 ? NULL : furi_hal_version.name;
}
const char* furi_hal_version_get_device_name_ptr() {
return furi_hal_version.device_name + 1;
}
const char* furi_hal_version_get_ble_local_device_name_ptr() {
return furi_hal_version.device_name;
}
const uint8_t* furi_hal_version_get_ble_mac() {
return furi_hal_version.ble_mac;
}
const struct Version* furi_hal_version_get_firmware_version(void) {
return version_get();
}
const struct Version* furi_hal_version_get_boot_version(void) {
#ifdef NO_BOOTLOADER
return 0;
#else
/* Backup register which points to structure in flash memory */
return (const struct Version*)LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR1);
#endif
}
size_t furi_hal_version_uid_size() {
return 64 / 8;
}
const uint8_t* furi_hal_version_uid() {
return (const uint8_t*)UID64_BASE;
}

View File

@ -1,7 +1,21 @@
#include <furi-hal.h>
#include <stm32wbxx_ll_utils.h>
void furi_hal_init() {
furi_hal_i2c_init();
furi_hal_light_init();
furi_hal_spi_init();
furi_hal_version_init();
}
void delay(float milliseconds) {
LL_mDelay((uint32_t)milliseconds);
}
void delay_us(float microseconds) {
microseconds = microseconds / 1000;
if(microseconds < 1) {
microseconds = 1;
}
LL_mDelay((uint32_t)microseconds);
}

View File

@ -1,8 +0,0 @@
#pragma once
#include <furi-hal-i2c.h>
#include <furi-hal-light.h>
#include <furi-hal-resources.h>
#include <furi-hal-spi.h>
void furi_hal_init();

View File

@ -1,69 +0,0 @@
#include <u8g2.h>
#include <assert.h>
#include <furi-hal.h>
#include <stm32wbxx_ll_utils.h>
static FuriHalSpiDevice* u8g2_periphery_display = NULL;
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
switch(msg) {
case U8X8_MSG_GPIO_AND_DELAY_INIT:
/* HAL initialization contains all what we need so we can skip this part. */
break;
case U8X8_MSG_DELAY_MILLI:
LL_mDelay(arg_int);
break;
case U8X8_MSG_DELAY_10MICRO:
LL_mDelay(1);
break;
case U8X8_MSG_DELAY_100NANO:
asm("nop");
break;
case U8X8_MSG_GPIO_RESET:
hal_gpio_write(&gpio_display_rst, arg_int);
break;
default:
return 0;
}
return 1;
}
uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
switch(msg) {
case U8X8_MSG_BYTE_SEND:
furi_hal_spi_bus_tx(u8g2_periphery_display->bus, (uint8_t*)arg_ptr, arg_int, 10000);
break;
case U8X8_MSG_BYTE_SET_DC:
hal_gpio_write(&gpio_display_di, arg_int);
break;
case U8X8_MSG_BYTE_INIT:
break;
case U8X8_MSG_BYTE_START_TRANSFER:
assert(u8g2_periphery_display == NULL);
u8g2_periphery_display =
(FuriHalSpiDevice*)furi_hal_spi_device_get(FuriHalSpiDeviceIdDisplay);
hal_gpio_write(u8g2_periphery_display->chip_select, false);
break;
case U8X8_MSG_BYTE_END_TRANSFER:
assert(u8g2_periphery_display);
hal_gpio_write(u8g2_periphery_display->chip_select, true);
furi_hal_spi_device_return(u8g2_periphery_display);
u8g2_periphery_display = NULL;
break;
default:
return 0;
}
return 1;
}

View File

@ -11,7 +11,9 @@
#include <lib/toolbox/version.h>
#include <furi-hal.h>
#include <u8g2.h>
#include <u8g2_glue.h>
const uint8_t I_DFU_128x50[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0x00,
@ -81,9 +83,6 @@ const uint8_t I_DFU_128x50[] = {
#define RTC_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady())
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
void target_led_control(char* c) {
furi_hal_light_set(LightRed, 0x00);
furi_hal_light_set(LightGreen, 0x00);
@ -190,9 +189,8 @@ void target_display_init() {
hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
// Initialize
u8g2_t fb;
u8g2_Setup_st7565_erc12864_alt_f(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
u8g2_Setup_st756x_flipper(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
u8g2_InitDisplay(&fb);
u8g2_SetContrast(&fb, 36);
// Create payload
u8g2_ClearBuffer(&fb);
u8g2_SetDrawColor(&fb, 0x01);

View File

@ -12,6 +12,8 @@ MCU_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
CFLAGS += $(MCU_FLAGS) $(BOOT_CFLAGS) -DSTM32WB55xx -Wall -fdata-sections -ffunction-sections
LDFLAGS += $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs
HARDWARE_TARGET = 7
CUBE_DIR = $(PROJECT_ROOT)/lib/STM32CubeWB
# ST HAL

View File

@ -0,0 +1,173 @@
/**
* @file furi-hal-version.h
* Version HAL API
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <lib/toolbox/version.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FURI_HAL_VERSION_NAME_LENGTH 8
#define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1)
/** BLE symbol + "Flipper " + name */
#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + 8 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH)
/** OTP Versions enum */
typedef enum {
FuriHalVersionOtpVersion0 = 0x00,
FuriHalVersionOtpVersion1 = 0x01,
FuriHalVersionOtpVersion2 = 0x02,
FuriHalVersionOtpVersionEmpty = 0xFFFFFFFE,
FuriHalVersionOtpVersionUnknown = 0xFFFFFFFF,
} FuriHalVersionOtpVersion;
/** Device Colors */
typedef enum {
FuriHalVersionColorUnknown = 0x00,
FuriHalVersionColorBlack = 0x01,
FuriHalVersionColorWhite = 0x02,
} FuriHalVersionColor;
/** Device Regions */
typedef enum {
FuriHalVersionRegionUnknown = 0x00,
FuriHalVersionRegionEuRu = 0x01,
FuriHalVersionRegionUsCaAu = 0x02,
FuriHalVersionRegionJp = 0x03,
} FuriHalVersionRegion;
/** Device Display */
typedef enum {
FuriHalVersionDisplayUnknown = 0x00,
FuriHalVersionDisplayErc = 0x01,
FuriHalVersionDisplayMgg = 0x02,
} FuriHalVersionDisplay;
/** Init flipper version
*/
void furi_hal_version_init();
/** Check target firmware version
*
* @return true if target and real matches
*/
bool furi_hal_version_do_i_belong_here();
/** Get model name
*
* @return model name C-string
*/
const char* furi_hal_version_get_model_name();
/** Get OTP version
*
* @return OTP Version
*/
const FuriHalVersionOtpVersion furi_hal_version_get_otp_version();
/** Get hardware version
*
* @return Hardware Version
*/
const uint8_t furi_hal_version_get_hw_version();
/** Get hardware target
*
* @return Hardware Target
*/
const uint8_t furi_hal_version_get_hw_target();
/** Get hardware body
*
* @return Hardware Body
*/
const uint8_t furi_hal_version_get_hw_body();
/** Get hardware body color
*
* @return Hardware Color
*/
const FuriHalVersionColor furi_hal_version_get_hw_color();
/** Get hardware connect
*
* @return Hardware Interconnect
*/
const uint8_t furi_hal_version_get_hw_connect();
/** Get hardware region
*
* @return Hardware Region
*/
const FuriHalVersionRegion furi_hal_version_get_hw_region();
/** Get hardware display id
*
* @return Display id
*/
const FuriHalVersionDisplay furi_hal_version_get_hw_display();
/** Get hardware timestamp
*
* @return Hardware Manufacture timestamp
*/
const uint32_t furi_hal_version_get_hw_timestamp();
/** Get pointer to target name
*
* @return Hardware Name C-string
*/
const char* furi_hal_version_get_name_ptr();
/** Get pointer to target device name
*
* @return Hardware Device Name C-string
*/
const char* furi_hal_version_get_device_name_ptr();
/** Get pointer to target ble local device name
*
* @return Ble Device Name C-string
*/
const char* furi_hal_version_get_ble_local_device_name_ptr();
/** Get BLE MAC address
*
* @return pointer to BLE MAC address
*/
const uint8_t* furi_hal_version_get_ble_mac();
/** Get address of version structure of bootloader, stored in chip flash.
*
* @return Address of boot version structure.
*/
const struct Version* furi_hal_version_get_boot_version();
/** Get address of version structure of firmware.
*
* @return Address of firmware version structure.
*/
const struct Version* furi_hal_version_get_firmware_version();
/** Get platform UID size in bytes
*
* @return UID size in bytes
*/
size_t furi_hal_version_uid_size();
/** Get const pointer to UID
*
* @return pointer to UID
*/
const uint8_t* furi_hal_version_uid();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,15 @@
#pragma once
#include <furi-hal-i2c.h>
#include <furi-hal-light.h>
#include <furi-hal-resources.h>
#include <furi-hal-spi.h>
#include <furi-hal-version.h>
#define furi_assert(value) (void)(value)
void furi_hal_init();
void delay(float milliseconds);
void delay_us(float microseconds);

View File

@ -57,3 +57,11 @@
#ifndef ALIGN
#define ALIGN(n) __attribute__((aligned(n)))
#endif
#ifndef STRINGIFY
#define STRINGIFY(x) #x
#endif
#ifndef TOSTRING
#define TOSTRING(x) STRINGIFY(x)
#endif

View File

@ -33,6 +33,9 @@ RUN git clone https://github.com/rusdacent/hex2dfu.git && \
cd hex2dfu && gcc hex2dfu.c ED25519/*.c -o hex2dfu && mv ./hex2dfu /usr/local/bin/hex2dfu && \
hex2dfu -h
RUN git clone --depth 1 --branch v0.4.1 https://github.com/atomicobject/heatshrink.git && \
cd heatshrink && make && mv ./heatshrink /usr/local/bin/heatshrink
COPY entrypoint.sh syntax_check.sh /
RUN chmod +x /syntax_check.sh

View File

@ -1,79 +1,50 @@
# Project structure
# Flipper firmware
```
.
├── applications # Flipper applications
├── assets # Assets: icons, animation
├── bootloader # Bootloader make project
├── core # Main feature like OS, HAL (target-independed)
├── core-rs # Rust code
├── debug # Debug helpers, configs and plugins
├── docker # Docker toolchain container
├── firmware # Firmware make project
├── lib # Libs and 3rd parties
├── make # Makefile scripts
```
What it does?
# Bootloader
- [x] RTOS
- [x] FuriHAL
- [x] FuriCore
- [x] Services
- [x] Applications
Bootloader must be flashed first.
Detailed instruction on how to compile and flash it you can find in `bootloader` folder.
# Targets
# OS
| Name | Bootloader | Firmware | Reset | DFU |
| | Address | Address | Combo | Combo |
-----------------------------------------------------------------------------
| f7 | 0x08000000 | 0x00008000 | L+Back | L+Back, hold L |
CMSIS-RTOS2 over FreeRTOS
Also there is a ST bootloader combo available on empty device: L+Ok+Back, release Back,Left.
Target independend code and headers in `target/include` folders.
## Platform code
# Building
CMSIS, Freertos and HAL files are generated by CubeMX.
You can find platform code for STM32WB55 version in `f4` folder:
## With dev docker image:
```
├── Inc # CubeMX generated headers
├── Src # CubeMX generated code
├── furi-hal # Our HAL wrappers and platform specifics
├── ble-glue # BLE specific code(Glue for STMWPAN)
├── f4.ioc # CubeMX project file
├── startup_stm32wb55xx_cm4.s # Board startup/initialization assembler code
├── stm32wb55xx_flash_cm4*.ld # Linker scripts
├── target.mk # Makefile include
`docker-compose exec dev make -C firmware`
```
## With toolchain installed in path:
Working with CubeMX:
1. Download CubeMX from [st.com](https://www.st.com/en/development-tools/stm32cubemx.html)
2. Open `*.ioc` file
3. Do whatever you want to
3. Click `generate code`
4. After regenerating, look at git status, regeneration may brake some files.
5. Check one more time that things that you've changes are not covered in platform furi-hal. Because you know...
`make -C firmware`
# Flipper Universal Registry Implementation (FURI)
## Build Options
FURI is used to:
- `DEBUG` - 0/1 - enable or disable debug build. Default is 1.
- `TARGET` - string - target to build. Default is `f7`.
* application control (start, exit, switch between active)
* data exchange between application (create/open channel, subscribe and push messages or read/write values)
* non-volatile data storage for application (create/open value and read/write)
# Flashing
Read more at [FURI page](FURI)
Using SWD (STLink):
# FS (not implemented)
`make -C firmware flash`
File system is used to volaile storage some files (config, application data, etc.). There are some folders mounted to different volumes:
Or use DFU (USB):
* `/usr` for store static data like assets, menu items. Build system add files to usr while building. It can be useful for exchange some static data between application. For example, your app can add link to itself to Plugins menu items file, user will see your app and can call it from this menu.
* Specially `/usr/etc-default` folder contains default configs for apps. Bootloader has `factory default` options to reset applications config. Also when new app is bootstapping, system copy files from default config folder to `/etc`.
* `/etc` for store configs of application. This volume not overwrite during flashing.
* `/var` for store some application data (saved keys, application database, logs). This volume also not overwrite during flashing.
* `/media/*` mounted if SD card is inserted.
`make -C firmware upload`
# Flipper applications
# Debug
Each flipper functionality except OS/HAL/FURI doing by Flipper application. Some application are called at startup, the rest are called by the user (for example, from menu).
Using SWD (STLink):
(you can see some [examples](Application-examples))
For exchange data between application each app expose own record in FURI. You can subscribe on/read record to get data from application and write to record to send data to application.
**[List of FURI records](FURI-records-list)**
`make -C firmware debug`

View File

@ -18,7 +18,7 @@ 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_firmware_rev_num[] = TARGET;
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;
void dev_info_svc_start() {

View File

@ -0,0 +1,221 @@
#include <furi-hal-compress.h>
#include <furi.h>
#include <lib/heatshrink/heatshrink_encoder.h>
#include <lib/heatshrink/heatshrink_decoder.h>
#define FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE (512)
#define FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE (1024)
#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE (1 << FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG)
typedef struct {
uint8_t is_compressed;
uint8_t reserved;
uint16_t compressed_buff_size;
} FuriHalCompressHeader;
typedef struct {
heatshrink_decoder* decoder;
uint8_t compress_buff[FURI_HAL_COMPRESS_EXP_BUFF_SIZE + FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE];
uint8_t decoded_buff[FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE];
} FuriHalCompressIcon;
struct FuriHalCompress {
heatshrink_encoder* encoder;
heatshrink_decoder* decoder;
uint8_t *compress_buff;
uint16_t compress_buff_size;
};
static FuriHalCompressIcon* icon_decoder;
static void furi_hal_compress_reset(FuriHalCompress* compress) {
furi_assert(compress);
heatshrink_encoder_reset(compress->encoder);
heatshrink_decoder_reset(compress->decoder);
memset(compress->compress_buff, 0, compress->compress_buff_size);
}
void furi_hal_compress_icon_init() {
icon_decoder = furi_alloc(sizeof(FuriHalCompressIcon));
icon_decoder->decoder = heatshrink_decoder_alloc(
icon_decoder->compress_buff,
FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE,
FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG,
FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
heatshrink_decoder_reset(icon_decoder->decoder);
memset(icon_decoder->decoded_buff, 0, sizeof(icon_decoder->decoded_buff));
FURI_LOG_I("FuriHalCompress", "Init OK");
}
void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff) {
furi_assert(icon_data);
furi_assert(decoded_buff);
FuriHalCompressHeader* header = (FuriHalCompressHeader*) icon_data;
if(header->is_compressed) {
size_t data_processed = 0;
heatshrink_decoder_sink(icon_decoder->decoder, (uint8_t*)&icon_data[4], header->compressed_buff_size, &data_processed);
while(
heatshrink_decoder_poll(
icon_decoder->decoder,
icon_decoder->decoded_buff,
sizeof(icon_decoder->decoded_buff),
&data_processed) == HSDR_POLL_MORE
) {};
heatshrink_decoder_reset(icon_decoder->decoder);
memset(icon_decoder->compress_buff, 0, sizeof(icon_decoder->compress_buff));
*decoded_buff = icon_decoder->decoded_buff;
} else {
*decoded_buff = (uint8_t*)&icon_data[1];
}
}
FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size) {
FuriHalCompress* compress = furi_alloc(sizeof(FuriHalCompress));
compress->compress_buff = furi_alloc(compress_buff_size + FURI_HAL_COMPRESS_EXP_BUFF_SIZE);
compress->encoder = heatshrink_encoder_alloc(compress->compress_buff, FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
compress->decoder = heatshrink_decoder_alloc(compress->compress_buff, compress_buff_size, FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
return compress;
}
void furi_hal_compress_free(FuriHalCompress* compress) {
furi_assert(compress);
heatshrink_encoder_free(compress->encoder);
heatshrink_decoder_free(compress->decoder);
free(compress->compress_buff);
free(compress);
}
bool furi_hal_compress_encode(FuriHalCompress* compress, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, size_t data_out_size, size_t* data_res_size) {
furi_assert(compress);
furi_assert(data_in);
furi_assert(data_in_size);
size_t sink_size = 0;
size_t poll_size = 0;
HSE_sink_res sink_res;
HSE_poll_res poll_res;
HSE_finish_res finish_res;
bool encode_failed = false;
size_t sunk = 0;
size_t res_buff_size = sizeof(FuriHalCompressHeader);
// Sink data to encoding buffer
while((sunk < data_in_size) && !encode_failed) {
sink_res = heatshrink_encoder_sink(compress->encoder, &data_in[sunk], data_in_size - sunk, &sink_size);
if(sink_res != HSER_SINK_OK) {
encode_failed = true;
break;
}
sunk += sink_size;
do {
poll_res = heatshrink_encoder_poll(compress->encoder, &data_out[res_buff_size], data_out_size - res_buff_size, &poll_size);
if(poll_res < 0) {
encode_failed = true;
break;
}
res_buff_size += poll_size;
} while(poll_res == HSER_POLL_MORE);
}
// Notify sinking complete and poll encoded data
finish_res = heatshrink_encoder_finish(compress->encoder);
if(finish_res < 0) {
encode_failed = true;
} else {
do {
poll_res = heatshrink_encoder_poll(compress->encoder, &data_out[res_buff_size], data_out_size - 4 - res_buff_size, &poll_size);
if(poll_res < 0) {
encode_failed = true;
break;
}
res_buff_size += poll_size;
finish_res = heatshrink_encoder_finish(compress->encoder);
} while(finish_res != HSER_FINISH_DONE);
}
bool result = true;
// Write encoded data to output buffer if compression is efficient. Else - write header and original data
if(!encode_failed && (res_buff_size < data_in_size + 1)) {
FuriHalCompressHeader header = {.is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size};
memcpy(data_out, &header, sizeof(header));
*data_res_size = res_buff_size;
} else if (data_out_size > data_in_size) {
data_out[0] = 0x00;
memcpy(&data_out[1], data_in, data_in_size);
*data_res_size = data_in_size + 1;
} else {
*data_res_size = 0;
result = false;
}
furi_hal_compress_reset(compress);
return result;
}
bool furi_hal_compress_decode(FuriHalCompress* compress, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, size_t data_out_size, size_t* data_res_size) {
furi_assert(compress);
furi_assert(data_in);
furi_assert(data_out);
furi_assert(data_res_size);
bool result = false;
bool decode_failed = false;
HSD_sink_res sink_res;
HSD_poll_res poll_res;
HSD_finish_res finish_res;
size_t sink_size = 0;
size_t res_buff_size = 0;
size_t poll_size = 0;
FuriHalCompressHeader* header = (FuriHalCompressHeader*) data_in;
if(header->is_compressed) {
// Sink data to decoding buffer
size_t compressed_size = header->compressed_buff_size;
size_t sunk = sizeof(FuriHalCompressHeader);
while(sunk < compressed_size && !decode_failed) {
sink_res = heatshrink_decoder_sink(compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size);
if(sink_res < 0) {
decode_failed = true;
break;
}
sunk += sink_size;
do {
poll_res = heatshrink_decoder_poll(compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size);
if(poll_res < 0) {
decode_failed = true;
break;
}
res_buff_size += poll_size;
} while(poll_res == HSDR_POLL_MORE);
}
// Notify sinking complete and poll decoded data
if(!decode_failed) {
finish_res = heatshrink_decoder_finish(compress->decoder);
if(finish_res < 0) {
decode_failed = true;
} else {
do {
poll_res = heatshrink_decoder_poll(compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size);
res_buff_size += poll_size;
finish_res = heatshrink_decoder_finish(compress->decoder);
} while(finish_res != HSDR_FINISH_DONE);
}
}
*data_res_size = res_buff_size;
result = !decode_failed;
} else if(data_out_size >= data_in_size - 1) {
memcpy(data_out, &data_in[1], data_in_size);
*data_res_size = data_in_size - 1;
result = true;
} else {
result = false;
}
furi_hal_compress_reset(compress);
return result;
}

View File

@ -1,4 +1,5 @@
#include <furi-hal-console.h>
#include <furi-hal-lpuart.h>
#include <stdbool.h>
#include <stm32wbxx_ll_gpio.h>
@ -7,8 +8,12 @@
#include <furi.h>
#define CONSOLE_BAUDRATE 230400
volatile bool furi_hal_console_alive = false;
static void (*irq_cb)(uint8_t ev, uint8_t data);
void furi_hal_console_init() {
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7;
@ -21,11 +26,11 @@ void furi_hal_console_init() {
LL_USART_InitTypeDef USART_InitStruct = {0};
USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
USART_InitStruct.BaudRate = 230400;
USART_InitStruct.BaudRate = CONSOLE_BAUDRATE;
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(USART1, &USART_InitStruct);
@ -36,12 +41,41 @@ void furi_hal_console_init() {
LL_USART_Enable(USART1);
while(!LL_USART_IsActiveFlag_TEACK(USART1)) ;
LL_USART_EnableIT_RXNE_RXFNE(USART1);
LL_USART_EnableIT_IDLE(USART1);
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
furi_hal_console_alive = true;
FURI_LOG_I("FuriHalConsole", "Init OK");
}
static void furi_hal_console_uart_tx(const uint8_t* buffer, size_t buffer_size) {
void furi_hal_usart_init() {
furi_hal_console_alive = false;
}
void furi_hal_usart_set_br(uint32_t baud) {
if (LL_USART_IsEnabled(USART1)) {
// Wait for transfer complete flag
while (!LL_USART_IsActiveFlag_TC(USART1));
LL_USART_Disable(USART1);
uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud);
LL_USART_Enable(USART1);
}
}
void furi_hal_usart_deinit() {
while (!LL_USART_IsActiveFlag_TC(USART1));
furi_hal_usart_set_br(CONSOLE_BAUDRATE);
furi_hal_console_alive = true;
}
void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size) {
if (LL_USART_IsEnabled(USART1) == 0)
return;
while(buffer_size > 0) {
while (!LL_USART_IsActiveFlag_TXE(USART1));
@ -52,12 +86,32 @@ static void furi_hal_console_uart_tx(const uint8_t* buffer, size_t buffer_size)
}
}
void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) {
irq_cb = cb;
if (irq_cb == NULL)
NVIC_DisableIRQ(USART1_IRQn);
else
NVIC_EnableIRQ(USART1_IRQn);
}
void USART1_IRQHandler(void) {
if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) {
uint8_t data = LL_USART_ReceiveData8(USART1);
irq_cb(UartIrqEventRXNE, data);
} else if (LL_USART_IsActiveFlag_IDLE(USART1)) {
irq_cb(UartIrqEventIDLE, 0);
LL_USART_ClearFlag_IDLE(USART1);
}
//TODO: more events
}
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
if (!furi_hal_console_alive)
return;
// Transmit data
furi_hal_console_uart_tx(buffer, buffer_size);
furi_hal_usart_tx(buffer, buffer_size);
// Wait for TC flag to be raised for last char
while (!LL_USART_IsActiveFlag_TC(USART1));
}
@ -67,9 +121,9 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size
return;
// Transmit data
furi_hal_console_uart_tx(buffer, buffer_size);
furi_hal_usart_tx(buffer, buffer_size);
// Transmit new line symbols
furi_hal_console_uart_tx((const uint8_t*)"\r\n", 2);
furi_hal_usart_tx((const uint8_t*)"\r\n", 2);
// Wait for TC flag to be raised for last char
while (!LL_USART_IsActiveFlag_TC(USART1));
}

View File

@ -7,6 +7,12 @@
extern "C" {
#endif
typedef enum {
UartIrqEventRXNE,
UartIrqEventIDLE,
//TODO: more events
} UartIrqEvent;
void furi_hal_console_init();
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size);
@ -23,6 +29,18 @@ void furi_hal_console_printf(const char format[], ...);
void furi_hal_console_puts(const char* data);
void furi_hal_usart_init();
void furi_hal_usart_deinit();
void furi_hal_usart_set_br(uint32_t baud);
void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size);
void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data));
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,105 @@
#include <furi-hal-lpuart.h>
#include <stdbool.h>
#include <stm32wbxx_ll_gpio.h>
#include <stm32wbxx_ll_lpuart.h>
#include <furi.h>
static void (*irq_cb)(uint8_t ev, uint8_t data);
void furi_hal_lpuart_init() {
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = PC0_Pin|PC1_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1);
LL_LPUART_InitTypeDef LPUART_InitStruct = {0};
LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
LPUART_InitStruct.BaudRate = 115200;
LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
LL_LPUART_EnableFIFO(LPUART1);
LL_LPUART_Enable(LPUART1);
while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1))));
LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
LL_LPUART_EnableIT_IDLE(LPUART1);
HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0);
FURI_LOG_I("FuriHalLpUart", "Init OK");
}
void furi_hal_lpuart_set_br(uint32_t baud) {
if (LL_LPUART_IsEnabled(LPUART1)) {
// Wait for transfer complete flag
while (!LL_LPUART_IsActiveFlag_TC(LPUART1));
LL_LPUART_Disable(LPUART1);
uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1));
if (uartclk/baud > 4095) {
LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32);
LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud);
} else {
LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1);
LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud);
}
LL_LPUART_Enable(LPUART1);
}
}
void furi_hal_lpuart_deinit() {
furi_hal_lpuart_set_irq_cb(NULL);
LL_GPIO_SetPinMode(GPIOC, PC0_Pin, LL_GPIO_MODE_ANALOG);
LL_GPIO_SetPinMode(GPIOC, PC1_Pin, LL_GPIO_MODE_ANALOG);
LL_LPUART_Disable(LPUART1);
LL_APB1_GRP2_DisableClock(LL_APB1_GRP2_PERIPH_LPUART1);
}
void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size) {
if (LL_LPUART_IsEnabled(LPUART1) == 0)
return;
while(buffer_size > 0) {
while (!LL_LPUART_IsActiveFlag_TXE(LPUART1));
LL_LPUART_TransmitData8(LPUART1, *buffer);
buffer++;
buffer_size--;
}
}
void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) {
irq_cb = cb;
if (irq_cb == NULL)
NVIC_DisableIRQ(LPUART1_IRQn);
else
NVIC_EnableIRQ(LPUART1_IRQn);
}
void LPUART1_IRQHandler(void) {
if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) {
uint8_t data = LL_LPUART_ReceiveData8(LPUART1);
irq_cb(UartIrqEventRXNE, data);
} else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) {
irq_cb(UartIrqEventIDLE, 0);
LL_LPUART_ClearFlag_IDLE(LPUART1);
}
//TODO: more events
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "furi-hal-console.h"
#ifdef __cplusplus
extern "C" {
#endif
void furi_hal_lpuart_init();
void furi_hal_lpuart_deinit();
void furi_hal_lpuart_set_br(uint32_t baud);
void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size);
void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data));
#ifdef __cplusplus
}
#endif

View File

@ -1,6 +1,5 @@
#include "furi-hal-version.h"
#include "furi-hal-usb_i.h"
#include "furi-hal-vcp_i.h"
#include "furi-hal-usb-cdc_i.h"
#include <furi.h>
@ -17,6 +16,8 @@
#define CDC_NTF_SZ 0x08
#define IF_NUM_MAX 2
struct CdcIadDescriptor {
struct usb_iad_descriptor comm_iad;
struct usb_interface_descriptor comm;
@ -343,12 +344,8 @@ static const struct CdcConfigDescriptorDual cdc_cfg_desc_dual = {
},
};
static struct usb_cdc_line_coding cdc_line = {
.dwDTERate = 38400,
.bCharFormat = USB_CDC_1_STOP_BITS,
.bParityType = USB_CDC_NO_PARITY,
.bDataBits = 8,
};
static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {};
static void cdc_init(usbd_device* dev, struct UsbInterface* intf);
static void cdc_deinit(usbd_device *dev);
static void cdc_on_wakeup(usbd_device *dev);
@ -358,6 +355,7 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg);
static usbd_respond cdc_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
static usbd_device* usb_dev;
static struct UsbInterface* cdc_if_cur = NULL;
static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL};
struct UsbInterface usb_cdc_single = {
.init = cdc_init,
@ -429,6 +427,17 @@ static void cdc_deinit(usbd_device *dev) {
cdc_if_cur = NULL;
}
void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb) {
if (if_num < 2)
callbacks[if_num] = cb;
}
struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num) {
if (if_num < 2)
return &cdc_config[if_num];
return NULL;
}
void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len) {
if (if_num == 0)
usbd_ep_write(usb_dev, CDC0_TXD_EP, buf, len);
@ -444,25 +453,47 @@ int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len) {
}
static void cdc_on_wakeup(usbd_device *dev) {
furi_hal_vcp_on_usb_resume();
for (uint8_t i = 0; i < IF_NUM_MAX; i++) {
if (callbacks[i] != NULL) {
if (callbacks[i]->state_callback != NULL)
callbacks[i]->state_callback(1);
}
}
}
static void cdc_on_suspend(usbd_device *dev) {
furi_hal_vcp_on_usb_suspend();
for (uint8_t i = 0; i < IF_NUM_MAX; i++) {
if (callbacks[i] != NULL) {
if (callbacks[i]->state_callback != NULL)
callbacks[i]->state_callback(0);
}
}
}
static void cdc_rx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
uint8_t if_num = 0;
if (ep == CDC0_RXD_EP)
furi_hal_vcp_on_cdc_rx(0);
if_num = 0;
else
furi_hal_vcp_on_cdc_rx(1);
if_num = 1;
if (callbacks[if_num] != NULL) {
if (callbacks[if_num]->rx_ep_callback != NULL)
callbacks[if_num]->rx_ep_callback();
}
}
static void cdc_tx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
uint8_t if_num = 0;
if (ep == CDC0_TXD_EP)
furi_hal_vcp_on_cdc_tx_complete(0);
if_num = 0;
else
furi_hal_vcp_on_cdc_tx_complete(1);
if_num = 1;
if (callbacks[if_num] != NULL) {
if (callbacks[if_num]->tx_ep_callback != NULL)
callbacks[if_num]->tx_ep_callback();
}
}
static void cdc_txrx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
@ -495,12 +526,14 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg) {
case 1:
/* configuring device */
if ((CDC0_TXD_EP & 0x7F) != (CDC0_RXD_EP & 0x7F)) {
// 2x unidirectional endpoint mode with dualbuf
usbd_ep_config(dev, CDC0_RXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ);
usbd_ep_config(dev, CDC0_TXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ);
usbd_ep_config(dev, CDC0_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ);
usbd_reg_endpoint(dev, CDC0_RXD_EP, cdc_rx_ep_callback);
usbd_reg_endpoint(dev, CDC0_TXD_EP, cdc_tx_ep_callback);
} else {
// 1x bidirectional endpoint mode
usbd_ep_config(dev, CDC0_RXD_EP, USB_EPTYPE_BULK, CDC_DATA_SZ);
usbd_ep_config(dev, CDC0_TXD_EP, USB_EPTYPE_BULK, CDC_DATA_SZ);
usbd_ep_config(dev, CDC0_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ);
@ -532,20 +565,33 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg) {
}
/* Control requests handler */
static usbd_respond cdc_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback) {
static usbd_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
/* CDC control requests */
if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS)
&& req->wIndex == 0 ) {
switch (req->bRequest) {
uint8_t if_num = 0;
if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS)
&& (req->wIndex == 0 || req->wIndex == 2)) {
if (req->wIndex == 0)
if_num = 0;
else
if_num = 1;
switch(req->bRequest) {
case USB_CDC_SET_CONTROL_LINE_STATE:
furi_hal_vcp_on_cdc_control_line(req->wValue);
if (callbacks[if_num] != NULL) {
if (callbacks[if_num]->ctrl_line_callback != NULL)
callbacks[if_num]->ctrl_line_callback(req->wValue);
}
return usbd_ack;
case USB_CDC_SET_LINE_CODING:
memcpy(&cdc_line, req->data, sizeof(cdc_line));
memcpy(&cdc_config[if_num], req->data, sizeof(cdc_config[0]));
if (callbacks[if_num] != NULL) {
if (callbacks[if_num]->config_callback != NULL)
callbacks[if_num]->config_callback(&cdc_config[if_num]);
}
return usbd_ack;
case USB_CDC_GET_LINE_CODING:
dev->status.data_ptr = &cdc_line;
dev->status.data_count = sizeof(cdc_line);
dev->status.data_ptr = &cdc_config[if_num];
dev->status.data_count = sizeof(cdc_config[0]);
return usbd_ack;
default:
return usbd_fail;

View File

@ -1,6 +1,21 @@
#pragma once
#define CDC_DATA_SZ 0x40
#include <stdint.h>
#include "usb_cdc.h"
#define CDC_DATA_SZ 64
typedef struct {
void (*tx_ep_callback)(void);
void (*rx_ep_callback)(void);
void (*state_callback)(uint8_t state);
void (*ctrl_line_callback)(uint8_t state);
void (*config_callback)(struct usb_cdc_line_coding* config);
} CdcCallbacks;
void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb);
struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num);
void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len);

View File

@ -1,7 +1,6 @@
#include "furi-hal-version.h"
#include "furi-hal-usb_i.h"
#include "furi-hal-usb.h"
#include "furi-hal-vcp_i.h"
#include <furi.h>
#include "usb.h"
@ -34,6 +33,7 @@ struct UsbCfg{
UsbMode mode_cur;
UsbMode mode_next;
bool enabled;
bool connected;
} usb_config;
static void furi_hal_usb_tmr_cb(void* context);
@ -158,11 +158,15 @@ static usbd_respond usb_descriptor_get (usbd_ctlreq *req, void **address, uint16
}
static void susp_evt(usbd_device *dev, uint8_t event, uint8_t ep) {
if (usb_if_modes[usb_config.mode_cur] != NULL)
if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == true)) {
usb_config.connected = false;
usb_if_modes[usb_config.mode_cur]->suspend(&udev);
}
}
static void wkup_evt(usbd_device *dev, uint8_t event, uint8_t ep) {
if (usb_if_modes[usb_config.mode_cur] != NULL)
if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == false)) {
usb_config.connected = true;
usb_if_modes[usb_config.mode_cur]->wakeup(&udev);
}
}

View File

@ -1,4 +1,3 @@
#include <furi-hal-vcp_i.h>
#include <furi-hal-usb-cdc_i.h>
#include <furi.h>
@ -7,6 +6,7 @@
#define APP_RX_DATA_SIZE CDC_DATA_SZ
#define APP_TX_DATA_SIZE CDC_DATA_SZ
#define FURI_HAL_VCP_RX_BUFFER_SIZE (APP_RX_DATA_SIZE * 16)
#define VCP_IF_NUM 0
typedef struct {
volatile bool connected;
@ -17,6 +17,19 @@ typedef struct {
osSemaphoreId_t tx_semaphore;
} FuriHalVcp;
static void vcp_on_cdc_tx_complete();
static void vcp_on_cdc_rx();
static void vcp_state_callback(uint8_t state);
static void vcp_on_cdc_control_line(uint8_t state);
static CdcCallbacks cdc_cb = {
vcp_on_cdc_tx_complete,
vcp_on_cdc_rx,
vcp_state_callback,
vcp_on_cdc_control_line,
NULL,
};
static FuriHalVcp* furi_hal_vcp = NULL;
static const uint8_t ascii_soh = 0x01;
@ -34,9 +47,22 @@ void furi_hal_vcp_init() {
furi_hal_vcp->tx_semaphore = osSemaphoreNew(1, 1, NULL);
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb);
FURI_LOG_I("FuriHalVcp", "Init OK");
}
void furi_hal_vcp_enable() {
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb);
furi_hal_vcp->connected = true;
}
void furi_hal_vcp_disable() {
furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL);
furi_hal_vcp->connected = false;
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
}
size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size) {
furi_assert(furi_hal_vcp);
@ -68,24 +94,22 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) {
batch_size = APP_TX_DATA_SIZE;
}
furi_hal_cdc_send(0, (uint8_t*)buffer, batch_size);
furi_hal_cdc_send(VCP_IF_NUM, (uint8_t*)buffer, batch_size);
size -= batch_size;
buffer += batch_size;
}
}
void furi_hal_vcp_on_usb_resume() {
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
}
void furi_hal_vcp_on_usb_suspend() {
if (furi_hal_vcp->connected) {
static void vcp_state_callback(uint8_t state) {
if (state == 1)
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
else if (furi_hal_vcp->connected) {
furi_hal_vcp->connected = false;
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
}
}
void furi_hal_vcp_on_cdc_control_line(uint8_t state) {
static void vcp_on_cdc_control_line(uint8_t state) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// bit 0: DTR state, bit 1: RTS state
@ -110,30 +134,26 @@ void furi_hal_vcp_on_cdc_control_line(uint8_t state) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void furi_hal_vcp_on_cdc_rx(uint8_t if_num) {
static void vcp_on_cdc_rx() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (if_num == 0) {
uint16_t max_len = xStreamBufferSpacesAvailable(furi_hal_vcp->rx_stream);
if (max_len > 0) {
if (max_len > APP_RX_DATA_SIZE)
max_len = APP_RX_DATA_SIZE;
int32_t size = furi_hal_cdc_receive(0, vcp_rx_buf, max_len);
uint16_t max_len = xStreamBufferSpacesAvailable(furi_hal_vcp->rx_stream);
if (max_len > 0) {
if (max_len > APP_RX_DATA_SIZE)
max_len = APP_RX_DATA_SIZE;
int32_t size = furi_hal_cdc_receive(VCP_IF_NUM, vcp_rx_buf, max_len);
if (size > 0) {
size_t ret = xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, vcp_rx_buf, size, &xHigherPriorityTaskWoken);
furi_check(ret == size);
}
} else {
furi_hal_vcp->rx_stream_full = true;
};
}
if (size > 0) {
size_t ret = xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, vcp_rx_buf, size, &xHigherPriorityTaskWoken);
furi_check(ret == size);
}
} else {
furi_hal_vcp->rx_stream_full = true;
};
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void furi_hal_vcp_on_cdc_tx_complete(uint8_t if_num) {
if (if_num == 0)
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
static void vcp_on_cdc_tx_complete() {
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
}

View File

@ -1,13 +0,0 @@
#pragma once
#include <furi-hal-vcp.h>
void furi_hal_vcp_on_usb_resume();
void furi_hal_vcp_on_usb_suspend();
void furi_hal_vcp_on_cdc_control_line(uint8_t state);
void furi_hal_vcp_on_cdc_rx(uint8_t if_num);
void furi_hal_vcp_on_cdc_tx_complete(uint8_t if_num);

View File

@ -10,13 +10,6 @@
#define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
#define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
/** OTP Versions enum */
typedef enum {
FuriHalVersionOtpVersion0=0x00,
FuriHalVersionOtpVersion1=0x01,
FuriHalVersionOtpVersionEmpty=0xFFFFFFFE,
FuriHalVersionOtpVersionUnknown=0xFFFFFFFF,
} FuriHalVersionOtpVersion;
/** OTP V0 Structure: prototypes and early EVT */
typedef struct {
@ -28,7 +21,7 @@ typedef struct {
char name[FURI_HAL_VERSION_NAME_LENGTH];
} FuriHalVersionOTPv0;
/** OTP V1 Structure: late EVT, DVT, PVT, Production */
/** OTP V1 Structure: late EVT, DVT */
typedef struct {
/* First 64 bits: header */
uint16_t header_magic;
@ -49,10 +42,35 @@ typedef struct {
char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
} FuriHalVersionOTPv1;
/** OTP V2 Structure: DVT2, PVT, Production */
typedef struct {
/* Early First 64 bits: header */
uint16_t header_magic;
uint8_t header_version;
uint8_t header_reserved;
uint32_t header_timestamp;
/* Early Second 64 bits: board info */
uint8_t board_version; /** Board version */
uint8_t board_target; /** Board target firmware */
uint8_t board_body; /** Board body */
uint8_t board_connect; /** Board interconnect */
uint8_t board_display; /** Board display */
uint8_t board_reserved2_0; /** Reserved for future use, 0x00 */
uint16_t board_reserved2_1; /** Reserved for future use, 0x0000 */
/* Late Third 64 bits: device info */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint16_t board_reserved3_0; /** Reserved for future use, 0x0000 */
uint32_t board_reserved3_1; /** Reserved for future use, 0x00000000 */
/* Late Fourth 64 bits: Unique Device Name */
char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
} FuriHalVersionOTPv2;
/** Represenation Model: */
typedef struct {
FuriHalVersionOtpVersion otp_version;
uint32_t timestamp;
uint8_t board_version; /** Board version */
@ -61,6 +79,7 @@ typedef struct {
uint8_t board_connect; /** Board interconnect */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint8_t board_display; /** Board display */
char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */
@ -69,20 +88,6 @@ typedef struct {
static FuriHalVersion furi_hal_version = {0};
static FuriHalVersionOtpVersion furi_hal_version_get_otp_version() {
if (*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
return FuriHalVersionOtpVersionEmpty;
} else {
if (((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic == FURI_HAL_VERSION_OTP_HEADER_MAGIC) {
return FuriHalVersionOtpVersion1;
} else if (((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
return FuriHalVersionOtpVersion0;
} else {
return FuriHalVersionOtpVersionUnknown;
}
}
}
static void furi_hal_version_set_name(const char* name) {
if(name != NULL) {
strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
@ -124,8 +129,6 @@ static void furi_hal_version_load_otp_v0() {
furi_hal_version.board_target = otp->board_target;
furi_hal_version.board_body = otp->board_body;
furi_hal_version.board_connect = otp->board_connect;
furi_hal_version.board_color = 0;
furi_hal_version.board_region = 0;
furi_hal_version_set_name(otp->name);
}
@ -144,9 +147,33 @@ static void furi_hal_version_load_otp_v1() {
furi_hal_version_set_name(otp->name);
}
static void furi_hal_version_load_otp_v2() {
const FuriHalVersionOTPv2* otp = (FuriHalVersionOTPv2*)FURI_HAL_VERSION_OTP_ADDRESS;
// 1st block, programmed afer baking
furi_hal_version.timestamp = otp->header_timestamp;
// 2nd block, programmed afer baking
furi_hal_version.board_version = otp->board_version;
furi_hal_version.board_target = otp->board_target;
furi_hal_version.board_body = otp->board_body;
furi_hal_version.board_connect = otp->board_connect;
furi_hal_version.board_display = otp->board_display;
// 3rd and 4th blocks, programmed on FATP stage
if (otp->board_color != 0xFF) {
furi_hal_version.board_color = otp->board_color;
furi_hal_version.board_region = otp->board_region;
furi_hal_version_set_name(otp->name);
} else {
furi_hal_version.board_color = 0;
furi_hal_version.board_region = 0;
furi_hal_version_set_name(NULL);
}
}
void furi_hal_version_init() {
furi_hal_version.otp_version = furi_hal_version_get_otp_version();
switch(furi_hal_version.otp_version) {
switch(furi_hal_version_get_otp_version()) {
case FuriHalVersionOtpVersionUnknown:
furi_hal_version_load_otp_default();
break;
@ -159,6 +186,9 @@ void furi_hal_version_init() {
case FuriHalVersionOtpVersion1:
furi_hal_version_load_otp_v1();
break;
case FuriHalVersionOtpVersion2:
furi_hal_version_load_otp_v2();
break;
default: furi_crash(NULL);
}
FURI_LOG_I("FuriHalVersion", "Init OK");
@ -172,6 +202,28 @@ const char* furi_hal_version_get_model_name() {
return "Flipper Zero";
}
const FuriHalVersionOtpVersion furi_hal_version_get_otp_version() {
if (*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
return FuriHalVersionOtpVersionEmpty;
} else {
if (((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic == FURI_HAL_VERSION_OTP_HEADER_MAGIC) {
// Version 1+
uint8_t version = ((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_version;
if (version >= FuriHalVersionOtpVersion1 && version <= FuriHalVersionOtpVersion2) {
return version;
} else {
return FuriHalVersionOtpVersionUnknown;
}
} else if (((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
// Version 0
return FuriHalVersionOtpVersion0;
} else {
// Version Unknown
return FuriHalVersionOtpVersionUnknown;
}
}
}
const uint8_t furi_hal_version_get_hw_version() {
return furi_hal_version.board_version;
}
@ -196,6 +248,10 @@ const FuriHalVersionRegion furi_hal_version_get_hw_region() {
return furi_hal_version.board_region;
}
const FuriHalVersionDisplay furi_hal_version_get_hw_display() {
return furi_hal_version.board_display;
}
const uint32_t furi_hal_version_get_hw_timestamp() {
return furi_hal_version.timestamp;
}

View File

@ -33,9 +33,9 @@ void furi_hal_init() {
furi_hal_crypto_init();
// VCP + USB
furi_hal_vcp_init();
furi_hal_usb_init();
furi_hal_usb_set_config(UsbModeVcpSingle);
furi_hal_vcp_init();
FURI_LOG_I("HAL", "USB OK");
furi_hal_i2c_init();
@ -48,6 +48,7 @@ void furi_hal_init() {
furi_hal_nfc_init();
furi_hal_rfid_init();
furi_hal_bt_init();
furi_hal_compress_icon_init();
// FreeRTOS glue
furi_hal_os_init();

View File

@ -24,6 +24,8 @@ LDFLAGS += $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs
CPPFLAGS += -fno-rtti -fno-use-cxa-atexit -fno-exceptions
LDFLAGS += -Wl,--start-group -lstdc++ -lsupc++ -Wl,--end-group
HARDWARE_TARGET = 6
MXPROJECT_DIR = $(TARGET_DIR)
# Entry Point
@ -70,6 +72,7 @@ C_SOURCES += \
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_spi.c \
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_tim.c \
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_usart.c \
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_lpuart.c \
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_utils.c
# FreeRTOS

View File

@ -18,7 +18,7 @@ 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_firmware_rev_num[] = TARGET;
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;
void dev_info_svc_start() {

View File

@ -0,0 +1,221 @@
#include <furi-hal-compress.h>
#include <furi.h>
#include <lib/heatshrink/heatshrink_encoder.h>
#include <lib/heatshrink/heatshrink_decoder.h>
#define FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE (512)
#define FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE (1024)
#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE (1 << FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG)
typedef struct {
uint8_t is_compressed;
uint8_t reserved;
uint16_t compressed_buff_size;
} FuriHalCompressHeader;
typedef struct {
heatshrink_decoder* decoder;
uint8_t compress_buff[FURI_HAL_COMPRESS_EXP_BUFF_SIZE + FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE];
uint8_t decoded_buff[FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE];
} FuriHalCompressIcon;
struct FuriHalCompress {
heatshrink_encoder* encoder;
heatshrink_decoder* decoder;
uint8_t *compress_buff;
uint16_t compress_buff_size;
};
static FuriHalCompressIcon* icon_decoder;
static void furi_hal_compress_reset(FuriHalCompress* compress) {
furi_assert(compress);
heatshrink_encoder_reset(compress->encoder);
heatshrink_decoder_reset(compress->decoder);
memset(compress->compress_buff, 0, compress->compress_buff_size);
}
void furi_hal_compress_icon_init() {
icon_decoder = furi_alloc(sizeof(FuriHalCompressIcon));
icon_decoder->decoder = heatshrink_decoder_alloc(
icon_decoder->compress_buff,
FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE,
FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG,
FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
heatshrink_decoder_reset(icon_decoder->decoder);
memset(icon_decoder->decoded_buff, 0, sizeof(icon_decoder->decoded_buff));
FURI_LOG_I("FuriHalCompress", "Init OK");
}
void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff) {
furi_assert(icon_data);
furi_assert(decoded_buff);
FuriHalCompressHeader* header = (FuriHalCompressHeader*) icon_data;
if(header->is_compressed) {
size_t data_processed = 0;
heatshrink_decoder_sink(icon_decoder->decoder, (uint8_t*)&icon_data[4], header->compressed_buff_size, &data_processed);
while(
heatshrink_decoder_poll(
icon_decoder->decoder,
icon_decoder->decoded_buff,
sizeof(icon_decoder->decoded_buff),
&data_processed) == HSDR_POLL_MORE
) {};
heatshrink_decoder_reset(icon_decoder->decoder);
memset(icon_decoder->compress_buff, 0, sizeof(icon_decoder->compress_buff));
*decoded_buff = icon_decoder->decoded_buff;
} else {
*decoded_buff = (uint8_t*)&icon_data[1];
}
}
FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size) {
FuriHalCompress* compress = furi_alloc(sizeof(FuriHalCompress));
compress->compress_buff = furi_alloc(compress_buff_size + FURI_HAL_COMPRESS_EXP_BUFF_SIZE);
compress->encoder = heatshrink_encoder_alloc(compress->compress_buff, FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
compress->decoder = heatshrink_decoder_alloc(compress->compress_buff, compress_buff_size, FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
return compress;
}
void furi_hal_compress_free(FuriHalCompress* compress) {
furi_assert(compress);
heatshrink_encoder_free(compress->encoder);
heatshrink_decoder_free(compress->decoder);
free(compress->compress_buff);
free(compress);
}
bool furi_hal_compress_encode(FuriHalCompress* compress, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, size_t data_out_size, size_t* data_res_size) {
furi_assert(compress);
furi_assert(data_in);
furi_assert(data_in_size);
size_t sink_size = 0;
size_t poll_size = 0;
HSE_sink_res sink_res;
HSE_poll_res poll_res;
HSE_finish_res finish_res;
bool encode_failed = false;
size_t sunk = 0;
size_t res_buff_size = sizeof(FuriHalCompressHeader);
// Sink data to encoding buffer
while((sunk < data_in_size) && !encode_failed) {
sink_res = heatshrink_encoder_sink(compress->encoder, &data_in[sunk], data_in_size - sunk, &sink_size);
if(sink_res != HSER_SINK_OK) {
encode_failed = true;
break;
}
sunk += sink_size;
do {
poll_res = heatshrink_encoder_poll(compress->encoder, &data_out[res_buff_size], data_out_size - res_buff_size, &poll_size);
if(poll_res < 0) {
encode_failed = true;
break;
}
res_buff_size += poll_size;
} while(poll_res == HSER_POLL_MORE);
}
// Notify sinking complete and poll encoded data
finish_res = heatshrink_encoder_finish(compress->encoder);
if(finish_res < 0) {
encode_failed = true;
} else {
do {
poll_res = heatshrink_encoder_poll(compress->encoder, &data_out[res_buff_size], data_out_size - 4 - res_buff_size, &poll_size);
if(poll_res < 0) {
encode_failed = true;
break;
}
res_buff_size += poll_size;
finish_res = heatshrink_encoder_finish(compress->encoder);
} while(finish_res != HSER_FINISH_DONE);
}
bool result = true;
// Write encoded data to output buffer if compression is efficient. Else - write header and original data
if(!encode_failed && (res_buff_size < data_in_size + 1)) {
FuriHalCompressHeader header = {.is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size};
memcpy(data_out, &header, sizeof(header));
*data_res_size = res_buff_size;
} else if (data_out_size > data_in_size) {
data_out[0] = 0x00;
memcpy(&data_out[1], data_in, data_in_size);
*data_res_size = data_in_size + 1;
} else {
*data_res_size = 0;
result = false;
}
furi_hal_compress_reset(compress);
return result;
}
bool furi_hal_compress_decode(FuriHalCompress* compress, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, size_t data_out_size, size_t* data_res_size) {
furi_assert(compress);
furi_assert(data_in);
furi_assert(data_out);
furi_assert(data_res_size);
bool result = false;
bool decode_failed = false;
HSD_sink_res sink_res;
HSD_poll_res poll_res;
HSD_finish_res finish_res;
size_t sink_size = 0;
size_t res_buff_size = 0;
size_t poll_size = 0;
FuriHalCompressHeader* header = (FuriHalCompressHeader*) data_in;
if(header->is_compressed) {
// Sink data to decoding buffer
size_t compressed_size = header->compressed_buff_size;
size_t sunk = sizeof(FuriHalCompressHeader);
while(sunk < compressed_size && !decode_failed) {
sink_res = heatshrink_decoder_sink(compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size);
if(sink_res < 0) {
decode_failed = true;
break;
}
sunk += sink_size;
do {
poll_res = heatshrink_decoder_poll(compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size);
if(poll_res < 0) {
decode_failed = true;
break;
}
res_buff_size += poll_size;
} while(poll_res == HSDR_POLL_MORE);
}
// Notify sinking complete and poll decoded data
if(!decode_failed) {
finish_res = heatshrink_decoder_finish(compress->decoder);
if(finish_res < 0) {
decode_failed = true;
} else {
do {
poll_res = heatshrink_decoder_poll(compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size);
res_buff_size += poll_size;
finish_res = heatshrink_decoder_finish(compress->decoder);
} while(finish_res != HSDR_FINISH_DONE);
}
}
*data_res_size = res_buff_size;
result = !decode_failed;
} else if(data_out_size >= data_in_size - 1) {
memcpy(data_out, &data_in[1], data_in_size);
*data_res_size = data_in_size - 1;
result = true;
} else {
result = false;
}
furi_hal_compress_reset(compress);
return result;
}

View File

@ -1,4 +1,5 @@
#include <furi-hal-console.h>
#include <furi-hal-lpuart.h>
#include <stdbool.h>
#include <stm32wbxx_ll_gpio.h>
@ -7,8 +8,12 @@
#include <furi.h>
#define CONSOLE_BAUDRATE 230400
volatile bool furi_hal_console_alive = false;
static void (*irq_cb)(uint8_t ev, uint8_t data);
void furi_hal_console_init() {
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7;
@ -21,11 +26,11 @@ void furi_hal_console_init() {
LL_USART_InitTypeDef USART_InitStruct = {0};
USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
USART_InitStruct.BaudRate = 230400;
USART_InitStruct.BaudRate = CONSOLE_BAUDRATE;
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(USART1, &USART_InitStruct);
@ -36,12 +41,41 @@ void furi_hal_console_init() {
LL_USART_Enable(USART1);
while(!LL_USART_IsActiveFlag_TEACK(USART1)) ;
LL_USART_EnableIT_RXNE_RXFNE(USART1);
LL_USART_EnableIT_IDLE(USART1);
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
furi_hal_console_alive = true;
FURI_LOG_I("FuriHalConsole", "Init OK");
}
static void furi_hal_console_uart_tx(const uint8_t* buffer, size_t buffer_size) {
void furi_hal_usart_init() {
furi_hal_console_alive = false;
}
void furi_hal_usart_set_br(uint32_t baud) {
if (LL_USART_IsEnabled(USART1)) {
// Wait for transfer complete flag
while (!LL_USART_IsActiveFlag_TC(USART1));
LL_USART_Disable(USART1);
uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud);
LL_USART_Enable(USART1);
}
}
void furi_hal_usart_deinit() {
while (!LL_USART_IsActiveFlag_TC(USART1));
furi_hal_usart_set_br(CONSOLE_BAUDRATE);
furi_hal_console_alive = true;
}
void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size) {
if (LL_USART_IsEnabled(USART1) == 0)
return;
while(buffer_size > 0) {
while (!LL_USART_IsActiveFlag_TXE(USART1));
@ -52,12 +86,32 @@ static void furi_hal_console_uart_tx(const uint8_t* buffer, size_t buffer_size)
}
}
void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) {
irq_cb = cb;
if (irq_cb == NULL)
NVIC_DisableIRQ(USART1_IRQn);
else
NVIC_EnableIRQ(USART1_IRQn);
}
void USART1_IRQHandler(void) {
if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) {
uint8_t data = LL_USART_ReceiveData8(USART1);
irq_cb(UartIrqEventRXNE, data);
} else if (LL_USART_IsActiveFlag_IDLE(USART1)) {
irq_cb(UartIrqEventIDLE, 0);
LL_USART_ClearFlag_IDLE(USART1);
}
//TODO: more events
}
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
if (!furi_hal_console_alive)
return;
// Transmit data
furi_hal_console_uart_tx(buffer, buffer_size);
furi_hal_usart_tx(buffer, buffer_size);
// Wait for TC flag to be raised for last char
while (!LL_USART_IsActiveFlag_TC(USART1));
}
@ -67,9 +121,9 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size
return;
// Transmit data
furi_hal_console_uart_tx(buffer, buffer_size);
furi_hal_usart_tx(buffer, buffer_size);
// Transmit new line symbols
furi_hal_console_uart_tx((const uint8_t*)"\r\n", 2);
furi_hal_usart_tx((const uint8_t*)"\r\n", 2);
// Wait for TC flag to be raised for last char
while (!LL_USART_IsActiveFlag_TC(USART1));
}

View File

@ -7,6 +7,12 @@
extern "C" {
#endif
typedef enum {
UartIrqEventRXNE,
UartIrqEventIDLE,
//TODO: more events
} UartIrqEvent;
void furi_hal_console_init();
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size);
@ -23,6 +29,18 @@ void furi_hal_console_printf(const char format[], ...);
void furi_hal_console_puts(const char* data);
void furi_hal_usart_init();
void furi_hal_usart_deinit();
void furi_hal_usart_set_br(uint32_t baud);
void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size);
void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data));
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,105 @@
#include <furi-hal-lpuart.h>
#include <stdbool.h>
#include <stm32wbxx_ll_gpio.h>
#include <stm32wbxx_ll_lpuart.h>
#include <furi.h>
static void (*irq_cb)(uint8_t ev, uint8_t data);
void furi_hal_lpuart_init() {
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = PC0_Pin|PC1_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1);
LL_LPUART_InitTypeDef LPUART_InitStruct = {0};
LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
LPUART_InitStruct.BaudRate = 115200;
LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
LL_LPUART_EnableFIFO(LPUART1);
LL_LPUART_Enable(LPUART1);
while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1))));
LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
LL_LPUART_EnableIT_IDLE(LPUART1);
HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0);
FURI_LOG_I("FuriHalLpUart", "Init OK");
}
void furi_hal_lpuart_set_br(uint32_t baud) {
if (LL_LPUART_IsEnabled(LPUART1)) {
// Wait for transfer complete flag
while (!LL_LPUART_IsActiveFlag_TC(LPUART1));
LL_LPUART_Disable(LPUART1);
uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1));
if (uartclk/baud > 4095) {
LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32);
LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud);
} else {
LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1);
LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud);
}
LL_LPUART_Enable(LPUART1);
}
}
void furi_hal_lpuart_deinit() {
furi_hal_lpuart_set_irq_cb(NULL);
LL_GPIO_SetPinMode(GPIOC, PC0_Pin, LL_GPIO_MODE_ANALOG);
LL_GPIO_SetPinMode(GPIOC, PC1_Pin, LL_GPIO_MODE_ANALOG);
LL_LPUART_Disable(LPUART1);
LL_APB1_GRP2_DisableClock(LL_APB1_GRP2_PERIPH_LPUART1);
}
void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size) {
if (LL_LPUART_IsEnabled(LPUART1) == 0)
return;
while(buffer_size > 0) {
while (!LL_LPUART_IsActiveFlag_TXE(LPUART1));
LL_LPUART_TransmitData8(LPUART1, *buffer);
buffer++;
buffer_size--;
}
}
void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) {
irq_cb = cb;
if (irq_cb == NULL)
NVIC_DisableIRQ(LPUART1_IRQn);
else
NVIC_EnableIRQ(LPUART1_IRQn);
}
void LPUART1_IRQHandler(void) {
if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) {
uint8_t data = LL_LPUART_ReceiveData8(LPUART1);
irq_cb(UartIrqEventRXNE, data);
} else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) {
irq_cb(UartIrqEventIDLE, 0);
LL_LPUART_ClearFlag_IDLE(LPUART1);
}
//TODO: more events
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "furi-hal-console.h"
#ifdef __cplusplus
extern "C" {
#endif
void furi_hal_lpuart_init();
void furi_hal_lpuart_deinit();
void furi_hal_lpuart_set_br(uint32_t baud);
void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size);
void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data));
#ifdef __cplusplus
}
#endif

View File

@ -1,6 +1,5 @@
#include "furi-hal-version.h"
#include "furi-hal-usb_i.h"
#include "furi-hal-vcp_i.h"
#include "furi-hal-usb-cdc_i.h"
#include <furi.h>
@ -17,6 +16,8 @@
#define CDC_NTF_SZ 0x08
#define IF_NUM_MAX 2
struct CdcIadDescriptor {
struct usb_iad_descriptor comm_iad;
struct usb_interface_descriptor comm;
@ -343,12 +344,8 @@ static const struct CdcConfigDescriptorDual cdc_cfg_desc_dual = {
},
};
static struct usb_cdc_line_coding cdc_line = {
.dwDTERate = 38400,
.bCharFormat = USB_CDC_1_STOP_BITS,
.bParityType = USB_CDC_NO_PARITY,
.bDataBits = 8,
};
static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {};
static void cdc_init(usbd_device* dev, struct UsbInterface* intf);
static void cdc_deinit(usbd_device *dev);
static void cdc_on_wakeup(usbd_device *dev);
@ -358,6 +355,7 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg);
static usbd_respond cdc_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
static usbd_device* usb_dev;
static struct UsbInterface* cdc_if_cur = NULL;
static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL};
struct UsbInterface usb_cdc_single = {
.init = cdc_init,
@ -429,6 +427,17 @@ static void cdc_deinit(usbd_device *dev) {
cdc_if_cur = NULL;
}
void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb) {
if (if_num < 2)
callbacks[if_num] = cb;
}
struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num) {
if (if_num < 2)
return &cdc_config[if_num];
return NULL;
}
void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len) {
if (if_num == 0)
usbd_ep_write(usb_dev, CDC0_TXD_EP, buf, len);
@ -444,25 +453,47 @@ int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len) {
}
static void cdc_on_wakeup(usbd_device *dev) {
furi_hal_vcp_on_usb_resume();
for (uint8_t i = 0; i < IF_NUM_MAX; i++) {
if (callbacks[i] != NULL) {
if (callbacks[i]->state_callback != NULL)
callbacks[i]->state_callback(1);
}
}
}
static void cdc_on_suspend(usbd_device *dev) {
furi_hal_vcp_on_usb_suspend();
for (uint8_t i = 0; i < IF_NUM_MAX; i++) {
if (callbacks[i] != NULL) {
if (callbacks[i]->state_callback != NULL)
callbacks[i]->state_callback(0);
}
}
}
static void cdc_rx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
uint8_t if_num = 0;
if (ep == CDC0_RXD_EP)
furi_hal_vcp_on_cdc_rx(0);
if_num = 0;
else
furi_hal_vcp_on_cdc_rx(1);
if_num = 1;
if (callbacks[if_num] != NULL) {
if (callbacks[if_num]->rx_ep_callback != NULL)
callbacks[if_num]->rx_ep_callback();
}
}
static void cdc_tx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
uint8_t if_num = 0;
if (ep == CDC0_TXD_EP)
furi_hal_vcp_on_cdc_tx_complete(0);
if_num = 0;
else
furi_hal_vcp_on_cdc_tx_complete(1);
if_num = 1;
if (callbacks[if_num] != NULL) {
if (callbacks[if_num]->tx_ep_callback != NULL)
callbacks[if_num]->tx_ep_callback();
}
}
static void cdc_txrx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
@ -495,12 +526,14 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg) {
case 1:
/* configuring device */
if ((CDC0_TXD_EP & 0x7F) != (CDC0_RXD_EP & 0x7F)) {
// 2x unidirectional endpoint mode with dualbuf
usbd_ep_config(dev, CDC0_RXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ);
usbd_ep_config(dev, CDC0_TXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ);
usbd_ep_config(dev, CDC0_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ);
usbd_reg_endpoint(dev, CDC0_RXD_EP, cdc_rx_ep_callback);
usbd_reg_endpoint(dev, CDC0_TXD_EP, cdc_tx_ep_callback);
} else {
// 1x bidirectional endpoint mode
usbd_ep_config(dev, CDC0_RXD_EP, USB_EPTYPE_BULK, CDC_DATA_SZ);
usbd_ep_config(dev, CDC0_TXD_EP, USB_EPTYPE_BULK, CDC_DATA_SZ);
usbd_ep_config(dev, CDC0_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ);
@ -532,20 +565,33 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg) {
}
/* Control requests handler */
static usbd_respond cdc_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback) {
static usbd_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
/* CDC control requests */
if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS)
&& req->wIndex == 0 ) {
switch (req->bRequest) {
uint8_t if_num = 0;
if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS)
&& (req->wIndex == 0 || req->wIndex == 2)) {
if (req->wIndex == 0)
if_num = 0;
else
if_num = 1;
switch(req->bRequest) {
case USB_CDC_SET_CONTROL_LINE_STATE:
furi_hal_vcp_on_cdc_control_line(req->wValue);
if (callbacks[if_num] != NULL) {
if (callbacks[if_num]->ctrl_line_callback != NULL)
callbacks[if_num]->ctrl_line_callback(req->wValue);
}
return usbd_ack;
case USB_CDC_SET_LINE_CODING:
memcpy(&cdc_line, req->data, sizeof(cdc_line));
memcpy(&cdc_config[if_num], req->data, sizeof(cdc_config[0]));
if (callbacks[if_num] != NULL) {
if (callbacks[if_num]->config_callback != NULL)
callbacks[if_num]->config_callback(&cdc_config[if_num]);
}
return usbd_ack;
case USB_CDC_GET_LINE_CODING:
dev->status.data_ptr = &cdc_line;
dev->status.data_count = sizeof(cdc_line);
dev->status.data_ptr = &cdc_config[if_num];
dev->status.data_count = sizeof(cdc_config[0]);
return usbd_ack;
default:
return usbd_fail;

View File

@ -1,6 +1,21 @@
#pragma once
#define CDC_DATA_SZ 0x40
#include <stdint.h>
#include "usb_cdc.h"
#define CDC_DATA_SZ 64
typedef struct {
void (*tx_ep_callback)(void);
void (*rx_ep_callback)(void);
void (*state_callback)(uint8_t state);
void (*ctrl_line_callback)(uint8_t state);
void (*config_callback)(struct usb_cdc_line_coding* config);
} CdcCallbacks;
void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb);
struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num);
void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len);

View File

@ -1,7 +1,6 @@
#include "furi-hal-version.h"
#include "furi-hal-usb_i.h"
#include "furi-hal-usb.h"
#include "furi-hal-vcp_i.h"
#include <furi.h>
#include "usb.h"
@ -34,6 +33,7 @@ struct UsbCfg{
UsbMode mode_cur;
UsbMode mode_next;
bool enabled;
bool connected;
} usb_config;
static void furi_hal_usb_tmr_cb(void* context);
@ -158,11 +158,15 @@ static usbd_respond usb_descriptor_get (usbd_ctlreq *req, void **address, uint16
}
static void susp_evt(usbd_device *dev, uint8_t event, uint8_t ep) {
if (usb_if_modes[usb_config.mode_cur] != NULL)
if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == true)) {
usb_config.connected = false;
usb_if_modes[usb_config.mode_cur]->suspend(&udev);
}
}
static void wkup_evt(usbd_device *dev, uint8_t event, uint8_t ep) {
if (usb_if_modes[usb_config.mode_cur] != NULL)
if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == false)) {
usb_config.connected = true;
usb_if_modes[usb_config.mode_cur]->wakeup(&udev);
}
}

View File

@ -1,4 +1,3 @@
#include <furi-hal-vcp_i.h>
#include <furi-hal-usb-cdc_i.h>
#include <furi.h>
@ -7,6 +6,7 @@
#define APP_RX_DATA_SIZE CDC_DATA_SZ
#define APP_TX_DATA_SIZE CDC_DATA_SZ
#define FURI_HAL_VCP_RX_BUFFER_SIZE (APP_RX_DATA_SIZE * 16)
#define VCP_IF_NUM 0
typedef struct {
volatile bool connected;
@ -17,6 +17,19 @@ typedef struct {
osSemaphoreId_t tx_semaphore;
} FuriHalVcp;
static void vcp_on_cdc_tx_complete();
static void vcp_on_cdc_rx();
static void vcp_state_callback(uint8_t state);
static void vcp_on_cdc_control_line(uint8_t state);
static CdcCallbacks cdc_cb = {
vcp_on_cdc_tx_complete,
vcp_on_cdc_rx,
vcp_state_callback,
vcp_on_cdc_control_line,
NULL,
};
static FuriHalVcp* furi_hal_vcp = NULL;
static const uint8_t ascii_soh = 0x01;
@ -34,9 +47,22 @@ void furi_hal_vcp_init() {
furi_hal_vcp->tx_semaphore = osSemaphoreNew(1, 1, NULL);
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb);
FURI_LOG_I("FuriHalVcp", "Init OK");
}
void furi_hal_vcp_enable() {
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb);
furi_hal_vcp->connected = true;
}
void furi_hal_vcp_disable() {
furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL);
furi_hal_vcp->connected = false;
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
}
size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size) {
furi_assert(furi_hal_vcp);
@ -68,24 +94,22 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) {
batch_size = APP_TX_DATA_SIZE;
}
furi_hal_cdc_send(0, (uint8_t*)buffer, batch_size);
furi_hal_cdc_send(VCP_IF_NUM, (uint8_t*)buffer, batch_size);
size -= batch_size;
buffer += batch_size;
}
}
void furi_hal_vcp_on_usb_resume() {
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
}
void furi_hal_vcp_on_usb_suspend() {
if (furi_hal_vcp->connected) {
static void vcp_state_callback(uint8_t state) {
if (state == 1)
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
else if (furi_hal_vcp->connected) {
furi_hal_vcp->connected = false;
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
}
}
void furi_hal_vcp_on_cdc_control_line(uint8_t state) {
static void vcp_on_cdc_control_line(uint8_t state) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// bit 0: DTR state, bit 1: RTS state
@ -110,30 +134,26 @@ void furi_hal_vcp_on_cdc_control_line(uint8_t state) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void furi_hal_vcp_on_cdc_rx(uint8_t if_num) {
static void vcp_on_cdc_rx() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (if_num == 0) {
uint16_t max_len = xStreamBufferSpacesAvailable(furi_hal_vcp->rx_stream);
if (max_len > 0) {
if (max_len > APP_RX_DATA_SIZE)
max_len = APP_RX_DATA_SIZE;
int32_t size = furi_hal_cdc_receive(0, vcp_rx_buf, max_len);
uint16_t max_len = xStreamBufferSpacesAvailable(furi_hal_vcp->rx_stream);
if (max_len > 0) {
if (max_len > APP_RX_DATA_SIZE)
max_len = APP_RX_DATA_SIZE;
int32_t size = furi_hal_cdc_receive(VCP_IF_NUM, vcp_rx_buf, max_len);
if (size > 0) {
size_t ret = xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, vcp_rx_buf, size, &xHigherPriorityTaskWoken);
furi_check(ret == size);
}
} else {
furi_hal_vcp->rx_stream_full = true;
};
}
if (size > 0) {
size_t ret = xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, vcp_rx_buf, size, &xHigherPriorityTaskWoken);
furi_check(ret == size);
}
} else {
furi_hal_vcp->rx_stream_full = true;
};
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void furi_hal_vcp_on_cdc_tx_complete(uint8_t if_num) {
if (if_num == 0)
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
static void vcp_on_cdc_tx_complete() {
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
}

View File

@ -1,13 +0,0 @@
#pragma once
#include <furi-hal-vcp.h>
void furi_hal_vcp_on_usb_resume();
void furi_hal_vcp_on_usb_suspend();
void furi_hal_vcp_on_cdc_control_line(uint8_t state);
void furi_hal_vcp_on_cdc_rx(uint8_t if_num);
void furi_hal_vcp_on_cdc_tx_complete(uint8_t if_num);

View File

@ -10,13 +10,6 @@
#define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
#define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
/** OTP Versions enum */
typedef enum {
FuriHalVersionOtpVersion0=0x00,
FuriHalVersionOtpVersion1=0x01,
FuriHalVersionOtpVersionEmpty=0xFFFFFFFE,
FuriHalVersionOtpVersionUnknown=0xFFFFFFFF,
} FuriHalVersionOtpVersion;
/** OTP V0 Structure: prototypes and early EVT */
typedef struct {
@ -28,7 +21,7 @@ typedef struct {
char name[FURI_HAL_VERSION_NAME_LENGTH];
} FuriHalVersionOTPv0;
/** OTP V1 Structure: late EVT, DVT, PVT, Production */
/** OTP V1 Structure: late EVT, DVT */
typedef struct {
/* First 64 bits: header */
uint16_t header_magic;
@ -49,10 +42,35 @@ typedef struct {
char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
} FuriHalVersionOTPv1;
/** OTP V2 Structure: DVT2, PVT, Production */
typedef struct {
/* Early First 64 bits: header */
uint16_t header_magic;
uint8_t header_version;
uint8_t header_reserved;
uint32_t header_timestamp;
/* Early Second 64 bits: board info */
uint8_t board_version; /** Board version */
uint8_t board_target; /** Board target firmware */
uint8_t board_body; /** Board body */
uint8_t board_connect; /** Board interconnect */
uint8_t board_display; /** Board display */
uint8_t board_reserved2_0; /** Reserved for future use, 0x00 */
uint16_t board_reserved2_1; /** Reserved for future use, 0x0000 */
/* Late Third 64 bits: device info */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint16_t board_reserved3_0; /** Reserved for future use, 0x0000 */
uint32_t board_reserved3_1; /** Reserved for future use, 0x00000000 */
/* Late Fourth 64 bits: Unique Device Name */
char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
} FuriHalVersionOTPv2;
/** Represenation Model: */
typedef struct {
FuriHalVersionOtpVersion otp_version;
uint32_t timestamp;
uint8_t board_version; /** Board version */
@ -61,6 +79,7 @@ typedef struct {
uint8_t board_connect; /** Board interconnect */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint8_t board_display; /** Board display */
char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */
@ -69,20 +88,6 @@ typedef struct {
static FuriHalVersion furi_hal_version = {0};
static FuriHalVersionOtpVersion furi_hal_version_get_otp_version() {
if (*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
return FuriHalVersionOtpVersionEmpty;
} else {
if (((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic == FURI_HAL_VERSION_OTP_HEADER_MAGIC) {
return FuriHalVersionOtpVersion1;
} else if (((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
return FuriHalVersionOtpVersion0;
} else {
return FuriHalVersionOtpVersionUnknown;
}
}
}
static void furi_hal_version_set_name(const char* name) {
if(name != NULL) {
strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
@ -124,8 +129,6 @@ static void furi_hal_version_load_otp_v0() {
furi_hal_version.board_target = otp->board_target;
furi_hal_version.board_body = otp->board_body;
furi_hal_version.board_connect = otp->board_connect;
furi_hal_version.board_color = 0;
furi_hal_version.board_region = 0;
furi_hal_version_set_name(otp->name);
}
@ -144,9 +147,33 @@ static void furi_hal_version_load_otp_v1() {
furi_hal_version_set_name(otp->name);
}
static void furi_hal_version_load_otp_v2() {
const FuriHalVersionOTPv2* otp = (FuriHalVersionOTPv2*)FURI_HAL_VERSION_OTP_ADDRESS;
// 1st block, programmed afer baking
furi_hal_version.timestamp = otp->header_timestamp;
// 2nd block, programmed afer baking
furi_hal_version.board_version = otp->board_version;
furi_hal_version.board_target = otp->board_target;
furi_hal_version.board_body = otp->board_body;
furi_hal_version.board_connect = otp->board_connect;
furi_hal_version.board_display = otp->board_display;
// 3rd and 4th blocks, programmed on FATP stage
if (otp->board_color != 0xFF) {
furi_hal_version.board_color = otp->board_color;
furi_hal_version.board_region = otp->board_region;
furi_hal_version_set_name(otp->name);
} else {
furi_hal_version.board_color = 0;
furi_hal_version.board_region = 0;
furi_hal_version_set_name(NULL);
}
}
void furi_hal_version_init() {
furi_hal_version.otp_version = furi_hal_version_get_otp_version();
switch(furi_hal_version.otp_version) {
switch(furi_hal_version_get_otp_version()) {
case FuriHalVersionOtpVersionUnknown:
furi_hal_version_load_otp_default();
break;
@ -159,6 +186,9 @@ void furi_hal_version_init() {
case FuriHalVersionOtpVersion1:
furi_hal_version_load_otp_v1();
break;
case FuriHalVersionOtpVersion2:
furi_hal_version_load_otp_v2();
break;
default: furi_crash(NULL);
}
FURI_LOG_I("FuriHalVersion", "Init OK");
@ -172,6 +202,28 @@ const char* furi_hal_version_get_model_name() {
return "Flipper Zero";
}
const FuriHalVersionOtpVersion furi_hal_version_get_otp_version() {
if (*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
return FuriHalVersionOtpVersionEmpty;
} else {
if (((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic == FURI_HAL_VERSION_OTP_HEADER_MAGIC) {
// Version 1+
uint8_t version = ((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_version;
if (version >= FuriHalVersionOtpVersion1 && version <= FuriHalVersionOtpVersion2) {
return version;
} else {
return FuriHalVersionOtpVersionUnknown;
}
} else if (((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
// Version 0
return FuriHalVersionOtpVersion0;
} else {
// Version Unknown
return FuriHalVersionOtpVersionUnknown;
}
}
}
const uint8_t furi_hal_version_get_hw_version() {
return furi_hal_version.board_version;
}
@ -196,6 +248,10 @@ const FuriHalVersionRegion furi_hal_version_get_hw_region() {
return furi_hal_version.board_region;
}
const FuriHalVersionDisplay furi_hal_version_get_hw_display() {
return furi_hal_version.board_display;
}
const uint32_t furi_hal_version_get_hw_timestamp() {
return furi_hal_version.timestamp;
}

View File

@ -33,9 +33,9 @@ void furi_hal_init() {
furi_hal_crypto_init();
// VCP + USB
furi_hal_vcp_init();
furi_hal_usb_init();
furi_hal_usb_set_config(UsbModeVcpSingle);
furi_hal_vcp_init();
FURI_LOG_I("HAL", "USB OK");
furi_hal_i2c_init();
@ -48,6 +48,7 @@ void furi_hal_init() {
furi_hal_nfc_init();
furi_hal_rfid_init();
furi_hal_bt_init();
furi_hal_compress_icon_init();
// FreeRTOS glue
furi_hal_os_init();

View File

@ -24,6 +24,8 @@ LDFLAGS += $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs
CPPFLAGS += -fno-rtti -fno-use-cxa-atexit -fno-exceptions
LDFLAGS += -Wl,--start-group -lstdc++ -lsupc++ -Wl,--end-group
HARDWARE_TARGET = 7
MXPROJECT_DIR = $(TARGET_DIR)
# Entry Point
@ -70,6 +72,7 @@ C_SOURCES += \
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_spi.c \
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_tim.c \
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_usart.c \
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_lpuart.c \
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_utils.c
# FreeRTOS

View File

@ -0,0 +1,67 @@
/**
* @file furi-hal-compress.h
* LZSS based compression HAL API
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
/** Defines encoder and decoder window size */
#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG (8)
/** Defines encoder and decoder lookahead buffer size */
#define FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4)
/** FuriHalCompress control structure */
typedef struct FuriHalCompress FuriHalCompress;
/** Initialize icon decoder
*/
void furi_hal_compress_icon_init();
/** Icon decoder
*
* @param icon_data pointer to icon data
* @param decoded_buff pointer to decoded buffer
*/
void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff);
/** Allocate encoder and decoder
*
* @param compress_buff_size size of decoder and encoder buffer to allocate
*
* @return FuriHalCompress instance
*/
FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size);
/** Free encoder and decoder
*
* @param compress FuriHalCompress instance
*/
void furi_hal_compress_free(FuriHalCompress* compress);
/** Encode data
*
* @param compress FuriHalCompress instance
* @param data_in pointer to input data
* @param data_in_size size of input data
* @param data_out maximum size of output data
* @param data_res_size pointer to result output data size
*
* @return true on success
*/
bool furi_hal_compress_encode(FuriHalCompress* compress, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, size_t data_out_size, size_t* data_res_size);
/** Decode data
*
* @param compress FuriHalCompress instance
* @param data_in pointer to input data
* @param data_in_size size of input data
* @param data_out maximum size of output data
* @param data_res_size pointer to result output data size
*
* @return true on success
*/
bool furi_hal_compress_decode(FuriHalCompress* compress, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, size_t data_out_size, size_t* data_res_size);

View File

@ -17,6 +17,14 @@ extern "C" {
*/
void furi_hal_vcp_init();
/** Disable VCP to make CDC interface usable by other application
*/
void furi_hal_vcp_disable();
/** Enable VCP
*/
void furi_hal_vcp_enable();
/** Recieve data from VCP Waits till some data arrives, never returns 0
*
* @param buffer pointer to buffer

View File

@ -19,6 +19,15 @@ extern "C" {
/** BLE symbol + "Flipper " + name */
#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + 8 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH)
/** OTP Versions enum */
typedef enum {
FuriHalVersionOtpVersion0=0x00,
FuriHalVersionOtpVersion1=0x01,
FuriHalVersionOtpVersion2=0x02,
FuriHalVersionOtpVersionEmpty=0xFFFFFFFE,
FuriHalVersionOtpVersionUnknown=0xFFFFFFFF,
} FuriHalVersionOtpVersion;
/** Device Colors */
typedef enum {
FuriHalVersionColorUnknown=0x00,
@ -34,6 +43,13 @@ typedef enum {
FuriHalVersionRegionJp=0x03,
} FuriHalVersionRegion;
/** Device Display */
typedef enum {
FuriHalVersionDisplayUnknown=0x00,
FuriHalVersionDisplayErc=0x01,
FuriHalVersionDisplayMgg=0x02,
} FuriHalVersionDisplay;
/** Init flipper version
*/
void furi_hal_version_init();
@ -50,6 +66,12 @@ bool furi_hal_version_do_i_belong_here();
*/
const char* furi_hal_version_get_model_name();
/** Get OTP version
*
* @return OTP Version
*/
const FuriHalVersionOtpVersion furi_hal_version_get_otp_version();
/** Get hardware version
*
* @return Hardware Version
@ -86,6 +108,12 @@ const uint8_t furi_hal_version_get_hw_connect();
*/
const FuriHalVersionRegion furi_hal_version_get_hw_region();
/** Get hardware display id
*
* @return Display id
*/
const FuriHalVersionDisplay furi_hal_version_get_hw_display();
/** Get hardware timestamp
*
* @return Hardware Manufacture timestamp

View File

@ -35,6 +35,8 @@ template <unsigned int N> struct STOP_EXTERNING_ME {};
#include "furi-hal-nfc.h"
#include "furi-hal-usb.h"
#include "furi-hal-usb-hid.h"
#include "furi-hal-compress.h"
#include "furi-hal-lpuart.h"
/** Init furi-hal */
void furi_hal_init();

View File

@ -0,0 +1,20 @@
#ifndef HEATSHRINK_H
#define HEATSHRINK_H
#define HEATSHRINK_AUTHOR "Scott Vokes <vokes.s@gmail.com>"
#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink"
/* Version 0.4.1 */
#define HEATSHRINK_VERSION_MAJOR 0
#define HEATSHRINK_VERSION_MINOR 4
#define HEATSHRINK_VERSION_PATCH 1
#define HEATSHRINK_MIN_WINDOW_BITS 4
#define HEATSHRINK_MAX_WINDOW_BITS 15
#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3
#define HEATSHRINK_LITERAL_MARKER 0x01
#define HEATSHRINK_BACKREF_MARKER 0x00
#endif

View File

@ -0,0 +1,28 @@
#ifndef HEATSHRINK_CONFIG_H
#define HEATSHRINK_CONFIG_H
#include <furi.h>
/* Should functionality assuming dynamic allocation be used? */
#ifndef HEATSHRINK_DYNAMIC_ALLOC
#define HEATSHRINK_DYNAMIC_ALLOC 1
#endif
#if HEATSHRINK_DYNAMIC_ALLOC
/* Optional replacement of malloc/free */
#define HEATSHRINK_MALLOC(SZ) furi_alloc(SZ)
#define HEATSHRINK_FREE(P, SZ) free(P)
#else
/* Required parameters for static configuration */
#define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 1024
#define HEATSHRINK_STATIC_WINDOW_BITS 8
#define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4
#endif
/* Turn on logging for debugging. */
#define HEATSHRINK_DEBUGGING_LOGS 0
/* Use indexing for faster compression. (This requires additional space.) */
#define HEATSHRINK_USE_INDEX 1
#endif

View File

@ -0,0 +1,364 @@
#include <stdlib.h>
#include <string.h>
#include "heatshrink_decoder.h"
/* States for the polling state machine. */
typedef enum {
HSDS_TAG_BIT, /* tag bit */
HSDS_YIELD_LITERAL, /* ready to yield literal byte */
HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */
HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */
HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */
HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */
HSDS_YIELD_BACKREF, /* ready to yield back-reference */
} HSD_state;
#if HEATSHRINK_DEBUGGING_LOGS
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#define ASSERT(X) assert(X)
static const char *state_names[] = {
"tag_bit",
"yield_literal",
"backref_index_msb",
"backref_index_lsb",
"backref_count_msb",
"backref_count_lsb",
"yield_backref",
};
#else
#define LOG(...) /* no-op */
#define ASSERT(X) /* no-op */
#endif
typedef struct {
uint8_t *buf; /* output buffer */
size_t buf_size; /* buffer size */
size_t *output_size; /* bytes pushed to buffer, so far */
} output_info;
#define NO_BITS ((uint16_t)-1)
/* Forward references. */
static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count);
static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte);
#if HEATSHRINK_DYNAMIC_ALLOC
heatshrink_decoder *heatshrink_decoder_alloc(uint8_t* buffer,
uint16_t input_buffer_size,
uint8_t window_sz2,
uint8_t lookahead_sz2) {
if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) ||
(window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) ||
(input_buffer_size == 0) ||
(lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) ||
(lookahead_sz2 >= window_sz2)) {
return NULL;
}
size_t sz = sizeof(heatshrink_decoder);
heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz);
if (hsd == NULL) { return NULL; }
hsd->input_buffer_size = input_buffer_size;
hsd->window_sz2 = window_sz2;
hsd->lookahead_sz2 = lookahead_sz2;
hsd->buffers = buffer;
heatshrink_decoder_reset(hsd);
LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n",
sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size);
return hsd;
}
void heatshrink_decoder_free(heatshrink_decoder *hsd) {
size_t sz = sizeof(heatshrink_decoder);
HEATSHRINK_FREE(hsd, sz);
(void)sz; /* may not be used by free */
}
#endif
void heatshrink_decoder_reset(heatshrink_decoder *hsd) {
hsd->state = HSDS_TAG_BIT;
hsd->input_size = 0;
hsd->input_index = 0;
hsd->bit_index = 0x00;
hsd->current_byte = 0x00;
hsd->output_count = 0;
hsd->output_index = 0;
hsd->head_index = 0;
}
/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */
HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd,
uint8_t *in_buf, size_t size, size_t *input_size) {
if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) {
return HSDR_SINK_ERROR_NULL;
}
size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size;
if (rem == 0) {
*input_size = 0;
return HSDR_SINK_FULL;
}
size = rem < size ? rem : size;
LOG("-- sinking %zd bytes\n", size);
/* copy into input buffer (at head of buffers) */
memcpy(&hsd->buffers[hsd->input_size], in_buf, size);
hsd->input_size += size;
*input_size = size;
return HSDR_SINK_OK;
}
/*****************
* Decompression *
*****************/
#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD))
#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD))
// States
static HSD_state st_tag_bit(heatshrink_decoder *hsd);
static HSD_state st_yield_literal(heatshrink_decoder *hsd,
output_info *oi);
static HSD_state st_backref_index_msb(heatshrink_decoder *hsd);
static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd);
static HSD_state st_backref_count_msb(heatshrink_decoder *hsd);
static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd);
static HSD_state st_yield_backref(heatshrink_decoder *hsd,
output_info *oi);
HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd,
uint8_t *out_buf, size_t out_buf_size, size_t *output_size) {
if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) {
return HSDR_POLL_ERROR_NULL;
}
*output_size = 0;
output_info oi;
oi.buf = out_buf;
oi.buf_size = out_buf_size;
oi.output_size = output_size;
while (1) {
LOG("-- poll, state is %d (%s), input_size %d\n",
hsd->state, state_names[hsd->state], hsd->input_size);
uint8_t in_state = hsd->state;
switch (in_state) {
case HSDS_TAG_BIT:
hsd->state = st_tag_bit(hsd);
break;
case HSDS_YIELD_LITERAL:
hsd->state = st_yield_literal(hsd, &oi);
break;
case HSDS_BACKREF_INDEX_MSB:
hsd->state = st_backref_index_msb(hsd);
break;
case HSDS_BACKREF_INDEX_LSB:
hsd->state = st_backref_index_lsb(hsd);
break;
case HSDS_BACKREF_COUNT_MSB:
hsd->state = st_backref_count_msb(hsd);
break;
case HSDS_BACKREF_COUNT_LSB:
hsd->state = st_backref_count_lsb(hsd);
break;
case HSDS_YIELD_BACKREF:
hsd->state = st_yield_backref(hsd, &oi);
break;
default:
return HSDR_POLL_ERROR_UNKNOWN;
}
/* If the current state cannot advance, check if input or output
* buffer are exhausted. */
if (hsd->state == in_state) {
if (*output_size == out_buf_size) { return HSDR_POLL_MORE; }
return HSDR_POLL_EMPTY;
}
}
}
static HSD_state st_tag_bit(heatshrink_decoder *hsd) {
uint32_t bits = get_bits(hsd, 1); // get tag bit
if (bits == NO_BITS) {
return HSDS_TAG_BIT;
} else if (bits) {
return HSDS_YIELD_LITERAL;
} else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) {
return HSDS_BACKREF_INDEX_MSB;
} else {
hsd->output_index = 0;
return HSDS_BACKREF_INDEX_LSB;
}
}
static HSD_state st_yield_literal(heatshrink_decoder *hsd,
output_info *oi) {
/* Emit a repeated section from the window buffer, and add it (again)
* to the window buffer. (Note that the repetition can include
* itself.)*/
if (*oi->output_size < oi->buf_size) {
uint16_t byte = get_bits(hsd, 8);
if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */
uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)];
uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1;
uint8_t c = byte & 0xFF;
LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.');
buf[hsd->head_index++ & mask] = c;
push_byte(hsd, oi, c);
return HSDS_TAG_BIT;
} else {
return HSDS_YIELD_LITERAL;
}
}
static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) {
uint8_t bit_ct = BACKREF_INDEX_BITS(hsd);
ASSERT(bit_ct > 8);
uint16_t bits = get_bits(hsd, bit_ct - 8);
LOG("-- backref index (msb), got 0x%04x (+1)\n", bits);
if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; }
hsd->output_index = bits << 8;
return HSDS_BACKREF_INDEX_LSB;
}
static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) {
uint8_t bit_ct = BACKREF_INDEX_BITS(hsd);
uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8);
LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits);
if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; }
hsd->output_index |= bits;
hsd->output_index++;
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
hsd->output_count = 0;
return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB;
}
static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) {
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
ASSERT(br_bit_ct > 8);
uint16_t bits = get_bits(hsd, br_bit_ct - 8);
LOG("-- backref count (msb), got 0x%04x (+1)\n", bits);
if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; }
hsd->output_count = bits << 8;
return HSDS_BACKREF_COUNT_LSB;
}
static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) {
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8);
LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits);
if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; }
hsd->output_count |= bits;
hsd->output_count++;
return HSDS_YIELD_BACKREF;
}
static HSD_state st_yield_backref(heatshrink_decoder *hsd,
output_info *oi) {
size_t count = oi->buf_size - *oi->output_size;
if (count > 0) {
size_t i = 0;
if (hsd->output_count < count) count = hsd->output_count;
uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)];
uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1;
uint16_t neg_offset = hsd->output_index;
LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset);
ASSERT(neg_offset <= mask + 1);
ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd)));
for (i=0; i<count; i++) {
uint8_t c = buf[(hsd->head_index - neg_offset) & mask];
push_byte(hsd, oi, c);
buf[hsd->head_index & mask] = c;
hsd->head_index++;
LOG(" -- ++ 0x%02x\n", c);
}
hsd->output_count -= count;
if (hsd->output_count == 0) { return HSDS_TAG_BIT; }
}
return HSDS_YIELD_BACKREF;
}
/* Get the next COUNT bits from the input buffer, saving incremental progress.
* Returns NO_BITS on end of input, or if more than 15 bits are requested. */
static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) {
uint16_t accumulator = 0;
int i = 0;
if (count > 15) { return NO_BITS; }
LOG("-- popping %u bit(s)\n", count);
/* If we aren't able to get COUNT bits, suspend immediately, because we
* don't track how many bits of COUNT we've accumulated before suspend. */
if (hsd->input_size == 0) {
if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; }
}
for (i = 0; i < count; i++) {
if (hsd->bit_index == 0x00) {
if (hsd->input_size == 0) {
LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n",
accumulator, accumulator);
return NO_BITS;
}
hsd->current_byte = hsd->buffers[hsd->input_index++];
LOG(" -- pulled byte 0x%02x\n", hsd->current_byte);
if (hsd->input_index == hsd->input_size) {
hsd->input_index = 0; /* input is exhausted */
hsd->input_size = 0;
}
hsd->bit_index = 0x80;
}
accumulator <<= 1;
if (hsd->current_byte & hsd->bit_index) {
accumulator |= 0x01;
if (0) {
LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n",
accumulator, hsd->bit_index);
}
} else {
if (0) {
LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n",
accumulator, hsd->bit_index);
}
}
hsd->bit_index >>= 1;
}
if (count > 1) { LOG(" -- accumulated %08x\n", accumulator); }
return accumulator;
}
HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) {
if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; }
switch (hsd->state) {
case HSDS_TAG_BIT:
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
/* If we want to finish with no input, but are in these states, it's
* because the 0-bit padding to the last byte looks like a backref
* marker bit followed by all 0s for index and count bits. */
case HSDS_BACKREF_INDEX_LSB:
case HSDS_BACKREF_INDEX_MSB:
case HSDS_BACKREF_COUNT_LSB:
case HSDS_BACKREF_COUNT_MSB:
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
/* If the output stream is padded with 0xFFs (possibly due to being in
* flash memory), also explicitly check the input size rather than
* uselessly returning MORE but yielding 0 bytes when polling. */
case HSDS_YIELD_LITERAL:
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
default:
return HSDR_FINISH_MORE;
}
}
static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) {
LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.');
oi->buf[(*oi->output_size)++] = byte;
(void)hsd;
}

View File

@ -0,0 +1,100 @@
#ifndef HEATSHRINK_DECODER_H
#define HEATSHRINK_DECODER_H
#include <stdint.h>
#include <stddef.h>
#include "heatshrink_common.h"
#include "heatshrink_config.h"
typedef enum {
HSDR_SINK_OK, /* data sunk, ready to poll */
HSDR_SINK_FULL, /* out of space in internal buffer */
HSDR_SINK_ERROR_NULL=-1, /* NULL argument */
} HSD_sink_res;
typedef enum {
HSDR_POLL_EMPTY, /* input exhausted */
HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */
HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */
HSDR_POLL_ERROR_UNKNOWN=-2,
} HSD_poll_res;
typedef enum {
HSDR_FINISH_DONE, /* output is done */
HSDR_FINISH_MORE, /* more output remains */
HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */
} HSD_finish_res;
#if HEATSHRINK_DYNAMIC_ALLOC
#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \
((BUF)->input_buffer_size)
#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \
((BUF)->window_sz2)
#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \
((BUF)->lookahead_sz2)
#else
#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \
HEATSHRINK_STATIC_INPUT_BUFFER_SIZE
#define HEATSHRINK_DECODER_WINDOW_BITS(_) \
(HEATSHRINK_STATIC_WINDOW_BITS)
#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \
(HEATSHRINK_STATIC_LOOKAHEAD_BITS)
#endif
typedef struct {
uint16_t input_size; /* bytes in input buffer */
uint16_t input_index; /* offset to next unprocessed input byte */
uint16_t output_count; /* how many bytes to output */
uint16_t output_index; /* index for bytes to output */
uint16_t head_index; /* head of window buffer */
uint8_t state; /* current state machine node */
uint8_t current_byte; /* current byte of input */
uint8_t bit_index; /* current bit index */
#if HEATSHRINK_DYNAMIC_ALLOC
/* Fields that are only used if dynamically allocated. */
uint8_t window_sz2; /* window buffer bits */
uint8_t lookahead_sz2; /* lookahead bits */
uint16_t input_buffer_size; /* input buffer size */
/* Input buffer, then expansion window buffer */
uint8_t* buffers;
#else
/* Input buffer, then expansion window buffer */
uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_))
+ HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)];
#endif
} heatshrink_decoder;
#if HEATSHRINK_DYNAMIC_ALLOC
/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes,
* an expansion buffer size of 2^WINDOW_SZ2, and a lookahead
* size of 2^lookahead_sz2. (The window buffer and lookahead sizes
* must match the settings used when the data was compressed.)
* Returns NULL on error. */
heatshrink_decoder *heatshrink_decoder_alloc(uint8_t* buffer, uint16_t input_buffer_size,
uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2);
/* Free a decoder. */
void heatshrink_decoder_free(heatshrink_decoder *hsd);
#endif
/* Reset a decoder. */
void heatshrink_decoder_reset(heatshrink_decoder *hsd);
/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to
* indicate how many bytes were actually sunk (in case a buffer was filled). */
HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd,
uint8_t *in_buf, size_t size, size_t *input_size);
/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into
* OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */
HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd,
uint8_t *out_buf, size_t out_buf_size, size_t *output_size);
/* Notify the dencoder that the input stream is finished.
* If the return value is HSDR_FINISH_MORE, there is still more output, so
* call heatshrink_decoder_poll and repeat. */
HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd);
#endif

View File

@ -0,0 +1,601 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "heatshrink_encoder.h"
typedef enum {
HSES_NOT_FULL, /* input buffer not full enough */
HSES_FILLED, /* buffer is full */
HSES_SEARCH, /* searching for patterns */
HSES_YIELD_TAG_BIT, /* yield tag bit */
HSES_YIELD_LITERAL, /* emit literal byte */
HSES_YIELD_BR_INDEX, /* yielding backref index */
HSES_YIELD_BR_LENGTH, /* yielding backref length */
HSES_SAVE_BACKLOG, /* copying buffer to backlog */
HSES_FLUSH_BITS, /* flush bit buffer */
HSES_DONE, /* done */
} HSE_state;
#if HEATSHRINK_DEBUGGING_LOGS
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#define ASSERT(X) assert(X)
static const char *state_names[] = {
"not_full",
"filled",
"search",
"yield_tag_bit",
"yield_literal",
"yield_br_index",
"yield_br_length",
"save_backlog",
"flush_bits",
"done",
};
#else
#define LOG(...) /* no-op */
#define ASSERT(X) /* no-op */
#endif
// Encoder flags
enum {
FLAG_IS_FINISHING = 0x01,
};
typedef struct {
uint8_t *buf; /* output buffer */
size_t buf_size; /* buffer size */
size_t *output_size; /* bytes pushed to buffer, so far */
} output_info;
#define MATCH_NOT_FOUND ((uint16_t)-1)
static uint16_t get_input_offset(heatshrink_encoder *hse);
static uint16_t get_input_buffer_size(heatshrink_encoder *hse);
static uint16_t get_lookahead_size(heatshrink_encoder *hse);
static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag);
static int can_take_byte(output_info *oi);
static int is_finishing(heatshrink_encoder *hse);
static void save_backlog(heatshrink_encoder *hse);
/* Push COUNT (max 8) bits to the output buffer, which has room. */
static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits,
output_info *oi);
static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi);
static void push_literal_byte(heatshrink_encoder *hse, output_info *oi);
#if HEATSHRINK_DYNAMIC_ALLOC
heatshrink_encoder *heatshrink_encoder_alloc(uint8_t* buffer, uint8_t window_sz2,
uint8_t lookahead_sz2) {
if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) ||
(window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) ||
(lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) ||
(lookahead_sz2 >= window_sz2)) {
return NULL;
}
/* Note: 2 * the window size is used because the buffer needs to fit
* (1 << window_sz2) bytes for the current input, and an additional
* (1 << window_sz2) bytes for the previous buffer of input, which
* will be scanned for useful backreferences. */
size_t buf_sz = (2 << window_sz2);
heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse));
if (hse == NULL) { return NULL; }
hse->window_sz2 = window_sz2;
hse->lookahead_sz2 = lookahead_sz2;
hse->buffer = buffer;
heatshrink_encoder_reset(hse);
#if HEATSHRINK_USE_INDEX
size_t index_sz = buf_sz*sizeof(uint16_t);
hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index));
if (hse->search_index == NULL) {
HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz);
return NULL;
}
hse->search_index->size = index_sz;
#endif
LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n",
buf_sz, get_input_buffer_size(hse));
return hse;
}
void heatshrink_encoder_free(heatshrink_encoder *hse) {
#if HEATSHRINK_USE_INDEX
size_t index_sz = sizeof(struct hs_index) + hse->search_index->size;
HEATSHRINK_FREE(hse->search_index, index_sz);
(void)index_sz;
#endif
HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder));
}
#endif
void heatshrink_encoder_reset(heatshrink_encoder *hse) {
hse->input_size = 0;
hse->state = HSES_NOT_FULL;
hse->match_scan_index = 0;
hse->flags = 0;
hse->bit_index = 0x80;
hse->current_byte = 0x00;
hse->match_length = 0;
hse->outgoing_bits = 0x0000;
hse->outgoing_bits_count = 0;
#ifdef LOOP_DETECT
hse->loop_detect = (uint32_t)-1;
#endif
}
HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse,
uint8_t *in_buf, size_t size, size_t *input_size) {
if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) {
return HSER_SINK_ERROR_NULL;
}
/* Sinking more content after saying the content is done, tsk tsk */
if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; }
/* Sinking more content before processing is done */
if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; }
uint16_t write_offset = get_input_offset(hse) + hse->input_size;
uint16_t ibs = get_input_buffer_size(hse);
uint16_t rem = ibs - hse->input_size;
uint16_t cp_sz = rem < size ? rem : size;
memcpy(&hse->buffer[write_offset], in_buf, cp_sz);
*input_size = cp_sz;
hse->input_size += cp_sz;
LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n",
cp_sz, size, write_offset, hse->input_size);
if (cp_sz == rem) {
LOG("-- internal buffer is now full\n");
hse->state = HSES_FILLED;
}
return HSER_SINK_OK;
}
/***************
* Compression *
***************/
static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start,
uint16_t end, const uint16_t maxlen, uint16_t *match_length);
static void do_indexing(heatshrink_encoder *hse);
static HSE_state st_step_search(heatshrink_encoder *hse);
static HSE_state st_yield_tag_bit(heatshrink_encoder *hse,
output_info *oi);
static HSE_state st_yield_literal(heatshrink_encoder *hse,
output_info *oi);
static HSE_state st_yield_br_index(heatshrink_encoder *hse,
output_info *oi);
static HSE_state st_yield_br_length(heatshrink_encoder *hse,
output_info *oi);
static HSE_state st_save_backlog(heatshrink_encoder *hse);
static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse,
output_info *oi);
HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse,
uint8_t *out_buf, size_t out_buf_size, size_t *output_size) {
if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) {
return HSER_POLL_ERROR_NULL;
}
if (out_buf_size == 0) {
LOG("-- MISUSE: output buffer size is 0\n");
return HSER_POLL_ERROR_MISUSE;
}
*output_size = 0;
output_info oi;
oi.buf = out_buf;
oi.buf_size = out_buf_size;
oi.output_size = output_size;
while (1) {
LOG("-- polling, state %u (%s), flags 0x%02x\n",
hse->state, state_names[hse->state], hse->flags);
uint8_t in_state = hse->state;
switch (in_state) {
case HSES_NOT_FULL:
return HSER_POLL_EMPTY;
case HSES_FILLED:
do_indexing(hse);
hse->state = HSES_SEARCH;
break;
case HSES_SEARCH:
hse->state = st_step_search(hse);
break;
case HSES_YIELD_TAG_BIT:
hse->state = st_yield_tag_bit(hse, &oi);
break;
case HSES_YIELD_LITERAL:
hse->state = st_yield_literal(hse, &oi);
break;
case HSES_YIELD_BR_INDEX:
hse->state = st_yield_br_index(hse, &oi);
break;
case HSES_YIELD_BR_LENGTH:
hse->state = st_yield_br_length(hse, &oi);
break;
case HSES_SAVE_BACKLOG:
hse->state = st_save_backlog(hse);
break;
case HSES_FLUSH_BITS:
hse->state = st_flush_bit_buffer(hse, &oi);
case HSES_DONE:
return HSER_POLL_EMPTY;
default:
LOG("-- bad state %s\n", state_names[hse->state]);
return HSER_POLL_ERROR_MISUSE;
}
if (hse->state == in_state) {
/* Check if output buffer is exhausted. */
if (*output_size == out_buf_size) return HSER_POLL_MORE;
}
}
}
HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) {
if (hse == NULL) { return HSER_FINISH_ERROR_NULL; }
LOG("-- setting is_finishing flag\n");
hse->flags |= FLAG_IS_FINISHING;
if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; }
return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE;
}
static HSE_state st_step_search(heatshrink_encoder *hse) {
uint16_t window_length = get_input_buffer_size(hse);
uint16_t lookahead_sz = get_lookahead_size(hse);
uint16_t msi = hse->match_scan_index;
LOG("## step_search, scan @ +%d (%d/%d), input size %d\n",
msi, hse->input_size + msi, 2*window_length, hse->input_size);
bool fin = is_finishing(hse);
if (msi > hse->input_size - (fin ? 1 : lookahead_sz)) {
/* Current search buffer is exhausted, copy it into the
* backlog and await more input. */
LOG("-- end of search @ %d\n", msi);
return fin ? HSES_FLUSH_BITS : HSES_SAVE_BACKLOG;
}
uint16_t input_offset = get_input_offset(hse);
uint16_t end = input_offset + msi;
uint16_t start = end - window_length;
uint16_t max_possible = lookahead_sz;
if (hse->input_size - msi < lookahead_sz) {
max_possible = hse->input_size - msi;
}
uint16_t match_length = 0;
uint16_t match_pos = find_longest_match(hse,
start, end, max_possible, &match_length);
if (match_pos == MATCH_NOT_FOUND) {
LOG("ss Match not found\n");
hse->match_scan_index++;
hse->match_length = 0;
return HSES_YIELD_TAG_BIT;
} else {
LOG("ss Found match of %d bytes at %d\n", match_length, match_pos);
hse->match_pos = match_pos;
hse->match_length = match_length;
ASSERT(match_pos <= 1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse) /*window_length*/);
return HSES_YIELD_TAG_BIT;
}
}
static HSE_state st_yield_tag_bit(heatshrink_encoder *hse,
output_info *oi) {
if (can_take_byte(oi)) {
if (hse->match_length == 0) {
add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER);
return HSES_YIELD_LITERAL;
} else {
add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER);
hse->outgoing_bits = hse->match_pos - 1;
hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse);
return HSES_YIELD_BR_INDEX;
}
} else {
return HSES_YIELD_TAG_BIT; /* output is full, continue */
}
}
static HSE_state st_yield_literal(heatshrink_encoder *hse,
output_info *oi) {
if (can_take_byte(oi)) {
push_literal_byte(hse, oi);
return HSES_SEARCH;
} else {
return HSES_YIELD_LITERAL;
}
}
static HSE_state st_yield_br_index(heatshrink_encoder *hse,
output_info *oi) {
if (can_take_byte(oi)) {
LOG("-- yielding backref index %u\n", hse->match_pos);
if (push_outgoing_bits(hse, oi) > 0) {
return HSES_YIELD_BR_INDEX; /* continue */
} else {
hse->outgoing_bits = hse->match_length - 1;
hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse);
return HSES_YIELD_BR_LENGTH; /* done */
}
} else {
return HSES_YIELD_BR_INDEX; /* continue */
}
}
static HSE_state st_yield_br_length(heatshrink_encoder *hse,
output_info *oi) {
if (can_take_byte(oi)) {
LOG("-- yielding backref length %u\n", hse->match_length);
if (push_outgoing_bits(hse, oi) > 0) {
return HSES_YIELD_BR_LENGTH;
} else {
hse->match_scan_index += hse->match_length;
hse->match_length = 0;
return HSES_SEARCH;
}
} else {
return HSES_YIELD_BR_LENGTH;
}
}
static HSE_state st_save_backlog(heatshrink_encoder *hse) {
LOG("-- saving backlog\n");
save_backlog(hse);
return HSES_NOT_FULL;
}
static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse,
output_info *oi) {
if (hse->bit_index == 0x80) {
LOG("-- done!\n");
return HSES_DONE;
} else if (can_take_byte(oi)) {
LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index);
oi->buf[(*oi->output_size)++] = hse->current_byte;
LOG("-- done!\n");
return HSES_DONE;
} else {
return HSES_FLUSH_BITS;
}
}
static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) {
LOG("-- adding tag bit: %d\n", tag);
push_bits(hse, 1, tag, oi);
}
static uint16_t get_input_offset(heatshrink_encoder *hse) {
return get_input_buffer_size(hse);
}
static uint16_t get_input_buffer_size(heatshrink_encoder *hse) {
return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse));
(void)hse;
}
static uint16_t get_lookahead_size(heatshrink_encoder *hse) {
return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse));
(void)hse;
}
static void do_indexing(heatshrink_encoder *hse) {
#if HEATSHRINK_USE_INDEX
/* Build an index array I that contains flattened linked lists
* for the previous instances of every byte in the buffer.
*
* For example, if buf[200] == 'x', then index[200] will either
* be an offset i such that buf[i] == 'x', or a negative offset
* to indicate end-of-list. This significantly speeds up matching,
* while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM.
*
* Future optimization options:
* 1. Since any negative value represents end-of-list, the other
* 15 bits could be used to improve the index dynamically.
*
* 2. Likewise, the last lookahead_sz bytes of the index will
* not be usable, so temporary data could be stored there to
* dynamically improve the index.
* */
struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse);
int16_t last[256];
memset(last, 0xFF, sizeof(last));
uint8_t * const data = hse->buffer;
int16_t * const index = hsi->index;
const uint16_t input_offset = get_input_offset(hse);
const uint16_t end = input_offset + hse->input_size;
for (uint16_t i=0; i<end; i++) {
uint8_t v = data[i];
int16_t lv = last[v];
index[i] = lv;
last[v] = i;
}
#else
(void)hse;
#endif
}
static int is_finishing(heatshrink_encoder *hse) {
return hse->flags & FLAG_IS_FINISHING;
}
static int can_take_byte(output_info *oi) {
return *oi->output_size < oi->buf_size;
}
/* Return the longest match for the bytes at buf[end:end+maxlen] between
* buf[start] and buf[end-1]. If no match is found, return -1. */
static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start,
uint16_t end, const uint16_t maxlen, uint16_t *match_length) {
LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n",
end, end + maxlen, start, end + maxlen - 1, maxlen);
uint8_t *buf = hse->buffer;
uint16_t match_maxlen = 0;
uint16_t match_index = MATCH_NOT_FOUND;
uint16_t len = 0;
uint8_t * const needlepoint = &buf[end];
#if HEATSHRINK_USE_INDEX
struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse);
int16_t pos = hsi->index[end];
while (pos - (int16_t)start >= 0) {
uint8_t * const pospoint = &buf[pos];
len = 0;
/* Only check matches that will potentially beat the current maxlen.
* This is redundant with the index if match_maxlen is 0, but the
* added branch overhead to check if it == 0 seems to be worse. */
if (pospoint[match_maxlen] != needlepoint[match_maxlen]) {
pos = hsi->index[pos];
continue;
}
for (len = 1; len < maxlen; len++) {
if (pospoint[len] != needlepoint[len]) break;
}
if (len > match_maxlen) {
match_maxlen = len;
match_index = pos;
if (len == maxlen) { break; } /* won't find better */
}
pos = hsi->index[pos];
}
#else
for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) {
uint8_t * const pospoint = &buf[pos];
if ((pospoint[match_maxlen] == needlepoint[match_maxlen])
&& (*pospoint == *needlepoint)) {
for (len=1; len<maxlen; len++) {
if (0) {
LOG(" --> cmp buf[%d] == 0x%02x against %02x (start %u)\n",
pos + len, pospoint[len], needlepoint[len], start);
}
if (pospoint[len] != needlepoint[len]) { break; }
}
if (len > match_maxlen) {
match_maxlen = len;
match_index = pos;
if (len == maxlen) { break; } /* don't keep searching */
}
}
}
#endif
const size_t break_even_point =
(1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) +
HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse));
/* Instead of comparing break_even_point against 8*match_maxlen,
* compare match_maxlen against break_even_point/8 to avoid
* overflow. Since MIN_WINDOW_BITS and MIN_LOOKAHEAD_BITS are 4 and
* 3, respectively, break_even_point/8 will always be at least 1. */
if (match_maxlen > (break_even_point / 8)) {
LOG("-- best match: %u bytes at -%u\n",
match_maxlen, end - match_index);
*match_length = match_maxlen;
return end - match_index;
}
LOG("-- none found\n");
return MATCH_NOT_FOUND;
}
static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) {
uint8_t count = 0;
uint8_t bits = 0;
if (hse->outgoing_bits_count > 8) {
count = 8;
bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8);
} else {
count = hse->outgoing_bits_count;
bits = hse->outgoing_bits;
}
if (count > 0) {
LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits);
push_bits(hse, count, bits, oi);
hse->outgoing_bits_count -= count;
}
return count;
}
/* Push COUNT (max 8) bits to the output buffer, which has room.
* Bytes are set from the lowest bits, up. */
static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits,
output_info *oi) {
ASSERT(count <= 8);
LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits);
/* If adding a whole byte and at the start of a new output byte,
* just push it through whole and skip the bit IO loop. */
if (count == 8 && hse->bit_index == 0x80) {
oi->buf[(*oi->output_size)++] = bits;
} else {
for (int i=count - 1; i>=0; i--) {
bool bit = bits & (1 << i);
if (bit) { hse->current_byte |= hse->bit_index; }
if (0) {
LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n",
bit ? 1 : 0, hse->bit_index, hse->current_byte);
}
hse->bit_index >>= 1;
if (hse->bit_index == 0x00) {
hse->bit_index = 0x80;
LOG(" > pushing byte 0x%02x\n", hse->current_byte);
oi->buf[(*oi->output_size)++] = hse->current_byte;
hse->current_byte = 0x00;
}
}
}
}
static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) {
uint16_t processed_offset = hse->match_scan_index - 1;
uint16_t input_offset = get_input_offset(hse) + processed_offset;
uint8_t c = hse->buffer[input_offset];
LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n",
c, isprint(c) ? c : '.', input_offset);
push_bits(hse, 8, c, oi);
}
static void save_backlog(heatshrink_encoder *hse) {
size_t input_buf_sz = get_input_buffer_size(hse);
uint16_t msi = hse->match_scan_index;
/* Copy processed data to beginning of buffer, so it can be
* used for future matches. Don't bother checking whether the
* input is less than the maximum size, because if it isn't,
* we're done anyway. */
uint16_t rem = input_buf_sz - msi; // unprocessed bytes
uint16_t shift_sz = input_buf_sz + rem;
memmove(&hse->buffer[0],
&hse->buffer[input_buf_sz - rem],
shift_sz);
hse->match_scan_index = 0;
hse->input_size -= input_buf_sz - rem;
}

View File

@ -0,0 +1,109 @@
#ifndef HEATSHRINK_ENCODER_H
#define HEATSHRINK_ENCODER_H
#include <stdint.h>
#include <stddef.h>
#include "heatshrink_common.h"
#include "heatshrink_config.h"
typedef enum {
HSER_SINK_OK, /* data sunk into input buffer */
HSER_SINK_ERROR_NULL=-1, /* NULL argument */
HSER_SINK_ERROR_MISUSE=-2, /* API misuse */
} HSE_sink_res;
typedef enum {
HSER_POLL_EMPTY, /* input exhausted */
HSER_POLL_MORE, /* poll again for more output */
HSER_POLL_ERROR_NULL=-1, /* NULL argument */
HSER_POLL_ERROR_MISUSE=-2, /* API misuse */
} HSE_poll_res;
typedef enum {
HSER_FINISH_DONE, /* encoding is complete */
HSER_FINISH_MORE, /* more output remaining; use poll */
HSER_FINISH_ERROR_NULL=-1, /* NULL argument */
} HSE_finish_res;
#if HEATSHRINK_DYNAMIC_ALLOC
#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \
((HSE)->window_sz2)
#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \
((HSE)->lookahead_sz2)
#define HEATSHRINK_ENCODER_INDEX(HSE) \
((HSE)->search_index)
struct hs_index {
uint16_t size;
int16_t index[];
};
#else
#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \
(HEATSHRINK_STATIC_WINDOW_BITS)
#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \
(HEATSHRINK_STATIC_LOOKAHEAD_BITS)
#define HEATSHRINK_ENCODER_INDEX(HSE) \
(&(HSE)->search_index)
struct hs_index {
uint16_t size;
int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS];
};
#endif
typedef struct {
uint16_t input_size; /* bytes in input buffer */
uint16_t match_scan_index;
uint16_t match_length;
uint16_t match_pos;
uint16_t outgoing_bits; /* enqueued outgoing bits */
uint8_t outgoing_bits_count;
uint8_t flags;
uint8_t state; /* current state machine node */
uint8_t current_byte; /* current byte of output */
uint8_t bit_index; /* current bit index */
#if HEATSHRINK_DYNAMIC_ALLOC
uint8_t window_sz2; /* 2^n size of window */
uint8_t lookahead_sz2; /* 2^n size of lookahead */
#if HEATSHRINK_USE_INDEX
struct hs_index *search_index;
#endif
/* input buffer and / sliding window for expansion */
uint8_t* buffer;
#else
#if HEATSHRINK_USE_INDEX
struct hs_index search_index;
#endif
/* input buffer and / sliding window for expansion */
uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)];
#endif
} heatshrink_encoder;
#if HEATSHRINK_DYNAMIC_ALLOC
/* Allocate a new encoder struct and its buffers.
* Returns NULL on error. */
heatshrink_encoder *heatshrink_encoder_alloc(uint8_t* buffer, uint8_t window_sz2,
uint8_t lookahead_sz2);
/* Free an encoder. */
void heatshrink_encoder_free(heatshrink_encoder *hse);
#endif
/* Reset an encoder. */
void heatshrink_encoder_reset(heatshrink_encoder *hse);
/* Sink up to SIZE bytes from IN_BUF into the encoder.
* INPUT_SIZE is set to the number of bytes actually sunk (in case a
* buffer was filled.). */
HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse,
uint8_t *in_buf, size_t size, size_t *input_size);
/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into
* OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */
HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse,
uint8_t *out_buf, size_t out_buf_size, size_t *output_size);
/* Notify the encoder that the input stream is finished.
* If the return value is HSER_FINISH_MORE, there is still more output, so
* call heatshrink_encoder_poll and repeat. */
HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse);
#endif

View File

@ -84,16 +84,22 @@ static IrdaStatus irda_common_decode_bits(IrdaCommonDecoder* decoder) {
bool level = (decoder->level + decoder->timings_cnt + 1) % 2;
uint32_t timing = decoder->timings[0];
/* check if short protocol version can be decoded */
if (timings->min_split_time && !level && (timing > timings->min_split_time)) {
for (int i = 1; decoder->protocol->databit_len[i] && (i < COUNT_OF(decoder->protocol->databit_len)); ++i) {
if (decoder->protocol->databit_len[i] == decoder->databit_cnt) {
return IrdaStatusReady;
if (timings->min_split_time && !level) {
if (timing > timings->min_split_time) {
/* long low timing - check if we're ready for any of protocol modification */
for (int i = 0; decoder->protocol->databit_len[i] && (i < COUNT_OF(decoder->protocol->databit_len)); ++i) {
if (decoder->protocol->databit_len[i] == decoder->databit_cnt) {
return IrdaStatusReady;
}
}
} else if (decoder->protocol->databit_len[0] == decoder->databit_cnt) {
/* short low timing for longest protocol - this is signal is longer than we expected */
return IrdaStatusError;
}
}
status = decoder->protocol->decode(decoder, level, timing);
furi_check(decoder->databit_cnt <= decoder->protocol->databit_len[0]);
furi_assert(status == IrdaStatusError || status == IrdaStatusOk);
if (status == IrdaStatusError) {
break;
@ -101,7 +107,7 @@ static IrdaStatus irda_common_decode_bits(IrdaCommonDecoder* decoder) {
decoder->timings_cnt = consume_samples(decoder->timings, decoder->timings_cnt, 1);
/* check if largest protocol version can be decoded */
if (level && (decoder->protocol->databit_len[0] == decoder->databit_cnt)) {
if (level && (decoder->protocol->databit_len[0] == decoder->databit_cnt) && !timings->min_split_time) {
status = IrdaStatusReady;
break;
}
@ -177,6 +183,9 @@ IrdaStatus irda_common_decode_manchester(IrdaCommonDecoder* decoder, bool level,
}
if (*switch_detect) {
if (decoder->protocol->databit_len[0] == decoder->databit_cnt) {
return IrdaStatusError;
}
accumulate_lsb(decoder, level);
}
@ -185,8 +194,16 @@ IrdaStatus irda_common_decode_manchester(IrdaCommonDecoder* decoder, bool level,
IrdaMessage* irda_common_decoder_check_ready(IrdaCommonDecoder* decoder) {
IrdaMessage* message = NULL;
bool found_length = false;
if (decoder->protocol->interpret(decoder)) {
for (int i = 0; decoder->protocol->databit_len[i] && (i < COUNT_OF(decoder->protocol->databit_len)); ++i) {
if (decoder->protocol->databit_len[i] == decoder->databit_cnt) {
found_length = true;
break;
}
}
if (found_length && decoder->protocol->interpret(decoder)) {
decoder->databit_cnt = 0;
message = &decoder->message;
if (decoder->protocol->decode_repeat) {
@ -268,7 +285,6 @@ void* irda_common_decoder_alloc(const IrdaCommonProtocolSpec* protocol) {
+ protocol->databit_len[0] / 8
+ !!(protocol->databit_len[0] % 8);
IrdaCommonDecoder* decoder = furi_alloc(alloc_size);
memset(decoder, 0, alloc_size);
decoder->protocol = protocol;
decoder->level = true;
return decoder;

View File

@ -35,6 +35,7 @@ const IrdaCommonProtocolSpec protocol_samsung32 = {
.preamble_tolerance = IRDA_SAMSUNG_PREAMBLE_TOLERANCE,
.bit_tolerance = IRDA_SAMSUNG_BIT_TOLERANCE,
.silence_time = IRDA_SAMSUNG_SILENCE,
.min_split_time = IRDA_SAMSUNG_MIN_SPLIT_TIME,
},
.databit_len[0] = 32,
.no_stop_bit = false,
@ -53,6 +54,7 @@ const IrdaCommonProtocolSpec protocol_rc6 = {
.preamble_tolerance = IRDA_RC6_PREAMBLE_TOLERANCE,
.bit_tolerance = IRDA_RC6_BIT_TOLERANCE,
.silence_time = IRDA_RC6_SILENCE,
.min_split_time = IRDA_RC6_MIN_SPLIT_TIME,
},
.databit_len[0] = 1 + 3 + 1 + 8 + 8, // start_bit + 3 mode bits, + 1 toggle bit (x2 timing) + 8 address + 8 command
.manchester_start_from_space = false,
@ -71,6 +73,7 @@ const IrdaCommonProtocolSpec protocol_rc5 = {
.preamble_tolerance = 0,
.bit_tolerance = IRDA_RC5_BIT_TOLERANCE,
.silence_time = IRDA_RC5_SILENCE,
.min_split_time = IRDA_RC5_MIN_SPLIT_TIME,
},
.databit_len[0] = 1 + 1 + 1 + 5 + 6, // start_bit + start_bit/command_bit + toggle_bit + 5 address + 6 command
.manchester_start_from_space = true,

View File

@ -59,6 +59,7 @@ static const IrdaEncoderDecoder irda_encoder_decoder[] = {
.alloc = irda_decoder_samsung32_alloc,
.decode = irda_decoder_samsung32_decode,
.reset = irda_decoder_samsung32_reset,
.check_ready = irda_decoder_samsung32_check_ready,
.free = irda_decoder_samsung32_free},
.encoder = {
.alloc = irda_encoder_samsung32_alloc,
@ -72,6 +73,7 @@ static const IrdaEncoderDecoder irda_encoder_decoder[] = {
.alloc = irda_decoder_rc5_alloc,
.decode = irda_decoder_rc5_decode,
.reset = irda_decoder_rc5_reset,
.check_ready = irda_decoder_rc5_check_ready,
.free = irda_decoder_rc5_free},
.encoder = {
.alloc = irda_encoder_rc5_alloc,
@ -85,6 +87,7 @@ static const IrdaEncoderDecoder irda_encoder_decoder[] = {
.alloc = irda_decoder_rc6_alloc,
.decode = irda_decoder_rc6_decode,
.reset = irda_decoder_rc6_reset,
.check_ready = irda_decoder_rc6_check_ready,
.free = irda_decoder_rc6_free},
.encoder = {
.alloc = irda_encoder_rc6_alloc,

View File

@ -81,6 +81,7 @@ extern const IrdaCommonProtocolSpec protocol_nec;
* of some data. Real tolerances we don't know, but in real life
* silence time should be greater than max repeat time. This is
* because of similar preambule timings for repeat and first messages. */
#define IRDA_SAMSUNG_MIN_SPLIT_TIME 5000
#define IRDA_SAMSUNG_SILENCE 145000
#define IRDA_SAMSUNG_REPEAT_PAUSE_MAX 140000
#define IRDA_SAMSUNG_REPEAT_MARK 4500
@ -91,6 +92,7 @@ extern const IrdaCommonProtocolSpec protocol_nec;
void* irda_decoder_samsung32_alloc(void);
void irda_decoder_samsung32_reset(void* decoder);
void irda_decoder_samsung32_free(void* decoder);
IrdaMessage* irda_decoder_samsung32_check_ready(void* ctx);
IrdaMessage* irda_decoder_samsung32_decode(void* decoder, bool level, uint32_t duration);
IrdaStatus irda_encoder_samsung32_encode(void* encoder_ptr, uint32_t* duration, bool* level);
void irda_encoder_samsung32_reset(void* encoder_ptr, const IrdaMessage* message);
@ -135,10 +137,12 @@ extern const IrdaCommonProtocolSpec protocol_samsung32;
#define IRDA_RC6_BIT_TOLERANCE 120 // us
/* protocol allows 2700 silence, but it is hard to send 1 message without repeat */
#define IRDA_RC6_SILENCE (2700 * 10)
#define IRDA_RC6_MIN_SPLIT_TIME 2700
void* irda_decoder_rc6_alloc(void);
void irda_decoder_rc6_reset(void* decoder);
void irda_decoder_rc6_free(void* decoder);
IrdaMessage* irda_decoder_rc6_check_ready(void* ctx);
IrdaMessage* irda_decoder_rc6_decode(void* decoder, bool level, uint32_t duration);
void* irda_encoder_rc6_alloc(void);
void irda_encoder_rc6_reset(void* encoder_ptr, const IrdaMessage* message);
@ -184,10 +188,12 @@ extern const IrdaCommonProtocolSpec protocol_rc6;
#define IRDA_RC5_BIT_TOLERANCE 120 // us
/* protocol allows 2700 silence, but it is hard to send 1 message without repeat */
#define IRDA_RC5_SILENCE (2700 * 10)
#define IRDA_RC5_MIN_SPLIT_TIME 2700
void* irda_decoder_rc5_alloc(void);
void irda_decoder_rc5_reset(void* decoder);
void irda_decoder_rc5_free(void* decoder);
IrdaMessage* irda_decoder_rc5_check_ready(void* ctx);
IrdaMessage* irda_decoder_rc5_decode(void* decoder, bool level, uint32_t duration);
void* irda_encoder_rc5_alloc(void);
void irda_encoder_rc5_reset(void* encoder_ptr, const IrdaMessage* message);

View File

@ -11,6 +11,11 @@ typedef struct {
bool toggle;
} IrdaRc5Decoder;
IrdaMessage* irda_decoder_rc5_check_ready(void* ctx) {
IrdaRc5Decoder* decoder = ctx;
return irda_common_decoder_check_ready(decoder->common_decoder);
}
bool irda_decoder_rc5_interpret(IrdaCommonDecoder* decoder) {
furi_assert(decoder);

Some files were not shown because too many files have changed in this diff Show More