Merge branch 'family/ambz2'

This commit is contained in:
Kuba Szczodrzyński
2023-07-13 17:31:52 +02:00
207 changed files with 3559 additions and 2123 deletions

View File

@@ -25,7 +25,8 @@ jobs:
run: |
mkdir -p site/
boardgen ltci
python docs/scripts/update_docs.py
python docs/scripts/write_boards.py
python docs/scripts/write_apis.py
python docs/scripts/prepare_doxygen.py
python docs/scripts/build_json.py
cp *.json site/

2
.gitignore vendored
View File

@@ -264,3 +264,5 @@ docs/status/supported_*.md
docs/status/unsupported_boards_*.md
boards/**/*.svg
boards/**/*.md
# other generated files
docs/contrib/lt-api-functions.md

View File

@@ -47,10 +47,13 @@
* [Macros](ltapi/macros.md)
* [File list](ltapi/files.md)
* 👷 Contributor's manual (WIP)
* [📁 Project structure](docs/dev/project-structure.md)
* [✈️ OTA format](docs/dev/ota/README.md)
* [uf2ota.py tool](docs/dev/ota/uf2ota.md)
* [uf2ota.h library](docs/dev/ota/library.md)
* [Porting new families](docs/contrib/porting.md)
* [API functions guide](docs/contrib/lt-api.md)
* [C standard library](docs/contrib/stdlib.md)
* [📁 Project structure](docs/contrib/project-structure.md)
* [✈️ OTA format](docs/contrib/ota/README.md)
* [uf2ota.py tool](docs/contrib/ota/uf2ota.md)
* [uf2ota.h library](docs/contrib/ota/library.md)
* [📓 TODO](docs/TODO.md)
* [](SUMMARY.md)
* [🔗 Resources](docs/resources/)

View File

@@ -1,5 +1,8 @@
{
"build": {
"bkcrypt_coeffs": "510fb093a3cbeadc5993a17ec7adeb03"
},
"flash": {
"tuya": "0x1ED000+0x13000"
}
}

View File

@@ -2,14 +2,9 @@
"pcb": {
"templates": [
"esp12s",
"esp12s-shield",
"pcb-black",
"rf-type1"
],
"vars": {
"MASK_PRESET": "mask_black",
"TRACE_COLOR": "#FAFD9D",
"SILK_COLOR": "white"
},
"pinout_hidden": "I2S,TRIG,WAKE,CTS,RTS,SD",
"pinout": {
"1": {

View File

@@ -5,9 +5,8 @@
"flash": {
"ota1": "0x00B000+0x75000",
"ota2": "0x080000+0x75000",
"kvs": "0xF5000+0x6000",
"userdata": "0xFB000+0x104000",
"rdp": "0x1FF000+0x1000"
"kvs": "0x0F5000+0x8000",
"userdata": "0x0FD000+0x102000"
},
"upload": {
"flash_size": 2097152,

View File

@@ -5,9 +5,8 @@
"flash": {
"ota1": "0x00B000+0xC5000",
"ota2": "0x0D0000+0xC5000",
"kvs": "0x195000+0x6000",
"userdata": "0x19B000+0x64000",
"rdp": "0x1FF000+0x1000"
"kvs": "0x195000+0x8000",
"userdata": "0x19D000+0x62000"
},
"upload": {
"flash_size": 2097152,

View File

@@ -6,8 +6,7 @@
"ota1": "0x00B000+0xF5000",
"ota2": "0x100000+0xF5000",
"kvs": "0x1F5000+0x8000",
"userdata": "0x1FD000+0x202000",
"rdp": "0x3FF000+0x1000"
"userdata": "0x1FD000+0x202000"
},
"upload": {
"flash_size": 4194304,

View File

@@ -0,0 +1,5 @@
{
"flash": {
"tuya": "0x1EB000+0x15000"
}
}

View File

@@ -10,7 +10,8 @@
"boot_xip": "0x000000+0x4000",
"boot_ram": "0x004000+0x4000",
"system": "0x009000+0x1000",
"calibration": "0x00A000+0x1000"
"calibration": "0x00A000+0x1000",
"rdp": "0x1FF000+0x1000"
},
"connectivity": [
"wifi"
@@ -47,10 +48,6 @@
"General info": "../../docs/platform/realtek-amb/README.md",
"Flashing guide": "../../docs/platform/realtek-ambz/flashing.md",
"Debugging": "../../docs/platform/realtek-ambz/debugging.md"
},
"extra": [
"RDP is most likely not used in Tuya firmwares, as the System Data partition contains an incorrect offset 0xFF000 for RDP, which is in the middle of OTA2 image.",
"Additionally, Tuya firmware uses an encrypted KV or file storage, which resides at the end of flash memory. This seems to overlap system RDP area."
]
}
}
}

View File

@@ -0,0 +1,12 @@
{
"flash": {
"ota1": "0x010000+0xE0000",
"ota2": "0x0F0000+0xE0000",
"kvs": "0x1D0000+0x8000",
"userdata": "0x1D8000+0x28000"
},
"upload": {
"flash_size": 2097152,
"maximum_size": 917504
}
}

View File

@@ -2,8 +2,7 @@
"flash": {
"ota1": "0x00C000+0xF8000",
"ota2": "0x104000+0xF8000",
"kvs": "0x1FC000+0x2000",
"userdata": "0x1FE000+0x2000"
"kvs": "0x1FC000+0x4000"
},
"upload": {
"flash_size": 2097152,

View File

@@ -0,0 +1,96 @@
{
"image": {
"keys": {
"decryption": "a0d6dae7e062ca94cbb294bf896b9f68cf8438774256ac7403ca4fd9a1c9564f",
"keyblock": {
"part_table": "882aa16c8c44a7760aa8c9ab22e3568c6fa16c2afa4f0cea29a10abcdf60e44f",
"boot": "882aa16c8c44a7760aa8c9ab22e3568c6fa16c2afa4f0cea29a10abcdf60e44f"
},
"hash_keys": {
"part_table": "47e5661335a4c5e0a94d69f3c737d54f2383791332939753ef24279608f6d72b",
"boot": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ota1": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e5f",
"ota2": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e5f"
},
"user_keys": {
"boot": "aa0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
"ota1": "bb0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
"ota2": "bb0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
},
"xip_sce_key": "a0d6dae7e062ca94cbb294bf896b9f68",
"xip_sce_iv": "94879487948794879487948794879487"
},
"ptable": {
"boot": "BOOT",
"ota1": "FW1",
"ota2": "FW2"
},
"boot": {
"name": "boot.sram",
"type": "SRAM",
"entry": "__ram_start_table_start__",
"elf": [
".ram.func.table",
".data",
".ram.code_text",
".ram.code_rodata"
],
"is_boot": true
},
"fw": [
{
"type": "FWHS_S",
"sections": [
{
"name": "fwhs.sram",
"type": "SRAM",
"entry": "__ram_start_table_start__",
"elf": [
".ram.img.signature",
".ram.func.table",
".data",
".ram.code_text",
".ram.code_rodata"
]
},
{
"name": "fwhs.psram",
"type": "PSRAM",
"entry": "__psram_start__",
"elf": [
".psram.data",
".psram.code_text",
".psram.code_rodata"
]
}
]
},
{
"type": "XIP",
"sections": [
{
"name": "fwhs.xip_c",
"entry": "XIP_RamImgSignature_s",
"type": "XIP",
"elf": [
".xip.code_c"
]
}
]
},
{
"type": "XIP",
"sections": [
{
"name": "fwhs.xip_p",
"entry": "__xip_code_rodata_start__",
"type": "XIP",
"elf": [
".xip.code_p"
]
}
]
}
]
}
}

View File

@@ -0,0 +1,5 @@
{
"flash": {
"tuya": "0x1D5000+0x10000"
}
}

View File

@@ -13,10 +13,20 @@
},
"debug": {
"protocol": "openocd",
"protocols": []
"protocols": [
"openocd"
],
"openocd_config": "amebaz2.cfg",
"gdb_init": [
"mem 0x9b000000 0x9c000000 ro"
]
},
"upload": {
"maximum_ram_size": 262144
"maximum_ram_size": 262144,
"protocol": "uart",
"protocols": [
"uart"
]
},
"doc": {
"params": {

26
boards/bw15.json Normal file
View File

@@ -0,0 +1,26 @@
{
"_base": [
"realtek-ambz2",
"realtek-ambz2-8720",
"realtek-ambz2-image",
"realtek-ambz2-2mb-992k",
"ic/rtl8720cf",
"pcb/bw15"
],
"build": {
"mcu": "rtl8720cf",
"variant": "bw15"
},
"name": "BW15",
"url": "https://docs.ai-thinker.com/_media/rtl8710/docs/bw15_datasheet_en.pdf",
"vendor": "Ai-Thinker Co., Ltd.",
"pcb": {
"symbol": "BW15"
},
"doc": {
"fccid": "2AXVG-BW15",
"links": {
"Vendor datasheet": "https://docs.ai-thinker.com/_media/rtl8710/docs/bw15_datasheet_en.pdf"
}
}
}

View File

@@ -2,7 +2,7 @@
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/cb1s",
"pcb/cb1s-test"

View File

@@ -2,7 +2,7 @@
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/cb2l",
"pcb/cb2l-test"

View File

@@ -2,7 +2,7 @@
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/cb2s",
"pcb/cb2s-test"

View File

@@ -2,7 +2,7 @@
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/cb3l"
],

View File

@@ -2,7 +2,7 @@
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/cb3s"
],

View File

@@ -2,7 +2,7 @@
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/cb3se"
],

View File

@@ -2,7 +2,7 @@
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/cblc5",
"pcb/cblc5-test"

View File

@@ -2,7 +2,7 @@
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/cbu",
"pcb/cbu-test"

View File

@@ -3,7 +3,7 @@
"generic",
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32"
],
"build": {

View File

@@ -4,7 +4,7 @@
"beken-72xx",
"beken-7231",
"beken-7231t",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32"
],
"build": {

View File

@@ -0,0 +1,102 @@
{
"_base": [
"generic",
"realtek-ambz2",
"realtek-ambz2-8720",
"realtek-ambz2-image",
"realtek-ambz2-2mb-992k",
"ic/rtl8720cf"
],
"build": {
"mcu": "rtl8720cf",
"variant": "generic-rtl8720cf-2mb-992k"
},
"name": "Generic - RTL8720CF (2M/992k)",
"symbol": "RTL8720CF (2M/992k)",
"url": "https://docs.libretiny.eu/boards/generic-rtl8720cf-2mb-992k/",
"vendor": "Generic",
"pcb": {
"pinout": {
"1": {
"IC": 15,
"ARD": "D0"
},
"2": {
"IC": 16,
"ARD": "D1"
},
"3": {
"IC": 18,
"ARD": "D2"
},
"4": {
"IC": 19,
"ARD": "D3"
},
"5": {
"IC": 20,
"ARD": "D4"
},
"6": {
"IC": 21,
"ARD": "D5"
},
"7": {
"IC": 22,
"ARD": "D6"
},
"8": {
"IC": 23,
"ARD": "D7"
},
"9": {
"IC": 24,
"ARD": "D8"
},
"10": {
"IC": 25,
"ARD": "D9"
},
"11": {
"IC": 26,
"ARD": "D10"
},
"12": {
"IC": 33,
"ARD": "D11"
},
"13": {
"IC": 34,
"ARD": "D12"
},
"14": {
"IC": 36,
"ARD": "D13"
},
"15": {
"IC": 37,
"ARD": "D14"
},
"16": {
"IC": 38,
"ARD": "D15"
},
"17": {
"IC": 39,
"ARD": "D16"
},
"18": {
"IC": 40,
"ARD": "D17"
},
"19": {
"IC": 1,
"ARD": "D18"
},
"20": {
"IC": 3,
"ARD": "D19"
}
}
}
}

View File

@@ -3,7 +3,7 @@
"beken-72xx",
"beken-7231",
"beken-7231t",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/lsc-lma35"
],

View File

@@ -2,7 +2,7 @@
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/lsc-lma35"
],

View File

@@ -3,7 +3,7 @@
"beken-72xx",
"beken-7231",
"beken-7231t",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/wb1s"
],

View File

@@ -2,7 +2,7 @@
"_base": [
"beken-72xx",
"beken-7231n",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/wb2l",
"pcb/wb2l-test",

View File

@@ -3,7 +3,7 @@
"beken-72xx",
"beken-7231",
"beken-7231t",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/wb2l",
"pcb/wb2l-test"

View File

@@ -3,7 +3,7 @@
"beken-72xx",
"beken-7231",
"beken-7231t",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/wb2s",
"pcb/wb2s-test"

View File

@@ -3,7 +3,7 @@
"beken-72xx",
"beken-7231",
"beken-7231t",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/wb3l"
],

View File

@@ -3,7 +3,7 @@
"beken-72xx",
"beken-7231",
"beken-7231t",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/wb3s"
],

View File

@@ -3,7 +3,7 @@
"beken-72xx",
"beken-7231",
"beken-7231t",
"beken-7231-crypt-tuya",
"beken-7231-tuya",
"ic/bk7231-qfn32",
"pcb/wblc5",
"pcb/wblc5-test"

View File

@@ -1,6 +1,7 @@
{
"_base": [
"realtek-ambz",
"realtek-ambz-tuya",
"realtek-ambz-2mb-788k",
"ic/rtl8710bn",
"pcb/wr2-base",

View File

@@ -1,6 +1,7 @@
{
"_base": [
"realtek-ambz",
"realtek-ambz-tuya",
"realtek-ambz-2mb-788k",
"ic/rtl8710bn",
"pcb/wr2-base",

View File

@@ -1,6 +1,7 @@
{
"_base": [
"realtek-ambz",
"realtek-ambz-tuya",
"realtek-ambz-2mb-788k",
"realtek-ambz-bx",
"ic/rtl8710bn",

View File

@@ -1,6 +1,7 @@
{
"_base": [
"realtek-ambz",
"realtek-ambz-tuya",
"realtek-ambz-2mb-788k",
"realtek-ambz-bx",
"ic/rtl8710bn",

View File

@@ -1,6 +1,7 @@
{
"_base": [
"realtek-ambz",
"realtek-ambz-tuya",
"realtek-ambz-2mb-788k",
"ic/rtl8710bn",
"pcb/wr3-base",

View File

@@ -1,6 +1,7 @@
{
"_base": [
"realtek-ambz",
"realtek-ambz-tuya",
"realtek-ambz-2mb-788k",
"ic/rtl8710bn",
"pcb/wr3-base",

View File

@@ -1,6 +1,7 @@
{
"_base": [
"realtek-ambz",
"realtek-ambz-tuya",
"realtek-ambz-2mb-788k",
"realtek-ambz-bx",
"ic/rtl8710bn",

View File

@@ -1,6 +1,7 @@
{
"_base": [
"realtek-ambz",
"realtek-ambz-tuya",
"realtek-ambz-2mb-788k",
"realtek-ambz-bx",
"ic/rtl8710bn",

View File

@@ -1,6 +1,7 @@
{
"_base": [
"realtek-ambz",
"realtek-ambz-tuya",
"realtek-ambz-2mb-788k",
"ic/rtl8710bn",
"pcb/wr3-base",

View File

@@ -538,7 +538,7 @@ image_app_rblh = "${BUILD_DIR}/image_${MCULC}_app.${FLASH_RBL_OFFSET}.rblh"
image_ota_rbl = "${BUILD_DIR}/image_${MCULC}_app.ota.rbl"
env.Replace(
# linker command (encryption + packaging)
LINK="${LTCHIPTOOL} link2bin ${BOARD_JSON} '' ''",
LINK='${LTCHIPTOOL} link2bin ${BOARD_JSON} "" ""',
# UF2OTA input list
UF2OTA=[
# app binary image (enc+crc) for flasher

View File

@@ -1,6 +1,7 @@
# Copyright (c) Kuba Szczodrzyński 2022-07-20.
from os.path import join
from os.path import isfile, join
from shutil import copyfile
from platformio.platform.base import PlatformBase
from platformio.platform.board import PlatformBoardConfig
@@ -52,9 +53,10 @@ queue.AppendPublic(
"-mthumb",
"-mcmse",
"-mfloat-abi=soft",
"--specs=nosys.specs",
"--specs=nano.specs",
"-Wl,--use-blx",
"-Wl,--undefined=gRamStartFun",
"-Wl,--warn-section-align",
"-Wl,-wrap,aesccmp_construct_mic_iv",
"-Wl,-wrap,aesccmp_construct_mic_header1",
"-Wl,-wrap,aesccmp_construct_ctr_preload",
@@ -91,6 +93,7 @@ queue.AppendPublic(
"-Wl,-wrap,memset",
# TODO remove this if possible
"-Wl,-wrap,putc",
# rt_printf wrappers are not here, as they're just changing code using #defines
],
)
@@ -103,17 +106,7 @@ queue.AddLibrary(
# cmsis
"+<soc/realtek/8710c/cmsis/rtl8710c/source/ram/*.c>",
"+<soc/realtek/8710c/cmsis/rtl8710c/source/ram_s/app_start.c>",
# console
"+<common/api/at_cmd/atcmd_bt.c>",
"+<common/api/at_cmd/atcmd_lwip.c>",
"+<common/api/at_cmd/atcmd_mp_ext2.c>",
"+<common/api/at_cmd/atcmd_mp.c>",
"+<common/api/at_cmd/atcmd_sys.c>",
"+<common/api/at_cmd/atcmd_wifi.c>",
"+<common/api/at_cmd/log_service.c>",
"+<soc/realtek/8710c/app/shell/cmd_shell.c>",
"+<soc/realtek/8710c/app/shell/ram_s/consol_cmds.c>",
"+<soc/realtek/8710c/misc/driver/rtl_console.c>",
"+<soc/realtek/8710c/misc/driver/flash_api_ext.c>",
# utilities
"+<common/utilities/cJSON.c>",
"+<common/utilities/http_client.c>",
@@ -125,11 +118,6 @@ queue.AddLibrary(
"+<os/freertos/freertos_service.c>",
"+<os/os_dep/device_lock.c>",
"+<os/os_dep/osdep_service.c>",
# os - freertos
"+<os/freertos/freertos_v10.0.1/Source/*.c>",
# os - freertos - portable
"+<os/freertos/freertos_v10.0.1/Source/portable/MemMang/heap_5.c>",
"+<os/freertos/freertos_v10.0.1/Source/portable/GCC/ARM_RTL8710C/port.c>",
# peripheral - api
"+<common/mbed/targets/hal/rtl8710c/*.c>",
# peripheral - hal
@@ -146,13 +134,9 @@ queue.AddLibrary(
"+<common/file_system/fatfs/r0.10c/src/ff.c>",
"+<common/file_system/fatfs/r0.10c/src/option/ccsbcs.c>",
"+<common/file_system/ftl/ftl.c>",
# TODO remove this
"+<common/example/example_entry.c>",
"+<common/example/wlan_fast_connect/example_wlan_fast_connect.c>",
],
includes=[
"+<$SDK_DIR/project/realtek_amebaz2_v0_example/inc>",
"+<common/api/at_cmd>",
"+<common/api/platform>",
"+<common/api>",
"+<common/application>",
@@ -170,11 +154,8 @@ queue.AddLibrary(
"+<common/test>",
"+<common/utilities>",
"+<os/freertos>",
"+<os/freertos/freertos_v10.0.1/Source/include>",
"+<os/freertos/freertos_v10.0.1/Source/portable/GCC/ARM_RTL8710C>",
"+<os/os_dep/include>",
"+<soc/realtek/8710c/app/rtl_printf/include>",
"+<soc/realtek/8710c/app/shell>",
"+<soc/realtek/8710c/app/stdio_port>",
"+<soc/realtek/8710c/cmsis/cmsis-core/include>",
"+<soc/realtek/8710c/cmsis/rtl8710c/include>",
@@ -205,6 +186,14 @@ queue.AddLibrary(
),
)
# Sources - FreeRTOS
env.Replace(FREERTOS_PORT=env["FAMILY_NAME"], FREERTOS_PORT_DEFINE="REALTEK_AMBZ2")
queue.AddExternalLibrary("freertos")
queue.AddExternalLibrary("freertos-port")
# Sources - lwIP
queue.AddExternalLibrary("lwip", port="ambz2")
# Sources - network utilities
queue.AddLibrary(
name="ambz2_net",
@@ -214,6 +203,7 @@ queue.AddLibrary(
"+<common/api/lwip_netconf.c>",
# network - api - wifi
"+<common/api/wifi/*.c>",
"ARDUINO" in "ENV" and "-<common/api/wifi/wifi_ind.c>",
# network - api - wifi - rtw_wpa_supplicant
"+<common/api/wifi/rtw_wpa_supplicant/src/crypto/tls_polarssl.c>",
"+<common/api/wifi/rtw_wpa_supplicant/wpa_supplicant/*.c>",
@@ -231,7 +221,6 @@ queue.AddLibrary(
"+<common/network/httpd/httpd_tls.c>",
# network
"+<common/network/dhcp/dhcps.c>",
"+<common/network/sntp/sntp.c>",
# network - websocket
"+<common/network/websocket/*.c>",
# network - mdns
@@ -285,6 +274,7 @@ queue.AddLibrary(
# "+<src/ble/profile/server/hids_rmc.c>",
"+<src/ble/profile/server/simple_ble_service.c>",
"+<src/mcu/module/data_uart_cmd/user_cmd_parse.c>",
"-<board/common/src/bt_uart_bridge.c>",
],
includes=[
"+<.>",
@@ -315,34 +305,6 @@ queue.AddLibrary(
),
)
# Sources - lwIP 2.0.2
queue.AddLibrary(
name="ambz2_lwip",
base_dir=join(COMPONENT_DIR, "common", "network", "lwip", "lwip_v2.0.2"),
srcs=[
"+<port/realtek/freertos/*.c>",
"+<src/api/*.c>",
"+<src/apps/ping/*.c>",
"+<src/apps/mdns/*.c>",
"+<src/core/*.c>",
"+<src/core/ipv4/*.c>",
"+<src/core/ipv6/*.c>",
"+<src/netif/ethernet.c>",
],
includes=[
"+<port/realtek>",
"+<port/realtek/freertos>",
"+<src/include>",
"+<src/include/netif>",
],
options=dict(
CFLAGS=[
"-Wno-implicit-function-declaration",
],
),
)
# Sources - mbedTLS
queue.AddLibrary(
name="ambz2_mbedtls",
@@ -436,10 +398,38 @@ env.Replace(
SIZEPRINTCMD="$SIZETOOL -B -d $SOURCES",
)
# Bootloader - copy for linking
# fmt: off
bootloader_src = env.subst("${SDK_DIR}/component/soc/realtek/8710c/misc/bsp/image/bootloader.axf")
bootloader_dst = env.subst("${BUILD_DIR}/bootloader.axf")
# fmt: on
if not isfile(bootloader_dst):
copyfile(bootloader_src, bootloader_dst)
# OTA2 clearing - 4096 bytes of 0xFF
image_ota_clear = env.subst("${BUILD_DIR}/raw_ota_clear.bin")
if not isfile(image_ota_clear):
with open(image_ota_clear, "wb") as f:
f.write(b"\xFF" * 4096)
# Build all libraries
queue.BuildLibraries()
# Main firmware outputs and actions
image_part_table = "${BUILD_DIR}/image_part_table.${FLASH_PART_TABLE_OFFSET}.bin"
image_bootloader = "${BUILD_DIR}/image_bootloader.${FLASH_BOOT_OFFSET}.bin"
image_firmware_is = "${BUILD_DIR}/image_firmware_is.${FLASH_OTA1_OFFSET}.bin"
env.Replace(
# TODO
# linker command (dual .bin outputs)
LINK='${LTCHIPTOOL} link2bin ${BOARD_JSON} "" ""',
# UF2OTA input list
UF2OTA=[
# same OTA images for flasher and device
f"{image_firmware_is},{image_firmware_is}=device:ota1,ota2;flasher:ota1,ota2",
# having flashed an application image, update the bootloader and partition table (incl. keys)
f"{image_bootloader}=device:boot;flasher:boot",
f"{image_part_table}=device:part_table;flasher:part_table",
# clearing headers of the "other" OTA image (hence the indexes are swapped)
f"{image_ota_clear},{image_ota_clear}=device:ota2,ota1;flasher:ota2,ota1",
],
)

View File

@@ -34,7 +34,7 @@ for f in family.inheritance:
code = f"{f.code}_arduino"
path = join("$CORES_DIR", f.name, "arduino")
found = found or env.AddCoreSources(queue, name=code, path=join(path, "src"))
found = env.AddCoreSources(queue, name=code, path=join(path, "src")) or found
env.AddArduinoLibraries(queue, name=code, path=join(path, "libraries"))
if f.short_name:
@@ -73,13 +73,6 @@ queue.AppendPublic(
("ARDUINO_SDK", 1),
],
LINKFLAGS=[
"--specs=nosys.specs",
"-Wl,--as-needed",
"-Wl,--build-id=none",
"-Wl,--cref",
"-Wl,--no-enum-size-warning",
"-Wl,--no-undefined",
"-Wl,--warn-common",
# wrappers from posix/time.c
"-Wl,-wrap,gettimeofday",
"-Wl,-wrap,settimeofday",

View File

@@ -95,7 +95,7 @@ for f in family.inheritance:
env.Prepend(CPPDEFINES=[(f"LT_{f.code.upper()}", "1")])
# Sources - external libraries
queue.AddExternalLibrary("ltchiptool") # uf2ota source code
queue.AddExternalLibrary("uf2ota")
queue.AddExternalLibrary("flashdb")
queue.AddExternalLibrary("printf")
@@ -138,11 +138,14 @@ queue.AppendPublic(
LINKFLAGS=[
"-g2",
"-Os",
"-Wl,--as-needed",
"-Wl,--build-id=none",
"-Wl,--cref",
"-Wl,--gc-sections",
"-Wl,--no-enum-size-warning",
"-Wl,--no-wchar-size-warning",
"-Wl,--no-undefined",
"-Wl,--warn-common",
# malloc.c wrappers
"-Wl,-wrap,malloc",
"-Wl,-wrap,calloc",

View File

@@ -20,13 +20,21 @@ def env_load_defines(env: Environment, path: str):
line = line[7:].strip()
line = line.split(None, 2)
if len(line) == 1:
env.Append(CPPDEFINES=[(line[0], 1)])
config[line[0]] = 1
key, value = line[0], 1
elif len(line) == 2:
env.Append(CPPDEFINES=[(line[0], line[1])])
config[line[0]] = line[1]
key, value = line[0], line[1]
else:
raise ValueError(f"Unknown directive: {line}")
for tpl in env["CPPDEFINES"]:
if isinstance(tpl, tuple):
k = tpl[0]
else:
k = tpl
if k == key:
env["CPPDEFINES"].remove(tpl)
break
env.Append(CPPDEFINES=[(key, value)])
config[key] = value
env.Append(
CONFIG=config,
)

View File

@@ -46,10 +46,12 @@ def env_add_core_sources(env: Environment, queue, name: str, path: str) -> bool:
base_dir=path,
srcs=[
"+<*.c*>",
"+<api/*.c>",
"+<common/*.c*>",
"+<compat/*.c*>",
"+<port/*.c*>",
"+<posix/*.c>",
"+<wiring/*.c>",
"+<wraps/*.c>",
],
includes=[
@@ -58,6 +60,7 @@ def env_add_core_sources(env: Environment, queue, name: str, path: str) -> bool:
"!<compat>",
"!<config>",
"!<port>",
"!<wiring>",
],
)
queue.AddLibrary(

View File

@@ -59,14 +59,23 @@ def env_uf2ota(env: Environment, *args, **kwargs):
def env_flash_write(env: Environment):
# UPLOAD_PROTOCOL = upload_protocol or board->upload.protocol
# UPLOAD_PORT = upload_port (PIO can choose this automatically I guess)
# UPLOAD_SPEED = upload_speed or board->upload.speed (**can be empty**)
protocol = env.subst("${UPLOAD_PROTOCOL}")
speed = env.subst("${UPLOAD_SPEED}")
if protocol == "uart":
# upload via UART
if speed:
return [
"-d",
"${UPLOAD_PORT}",
"-b",
"${UPLOAD_SPEED}",
]
return [
"-d",
"${UPLOAD_PORT}",
"-b",
"${UPLOAD_SPEED}",
]
else:
# can't upload via ltchiptool

View File

@@ -0,0 +1,5 @@
#pragma once
#error "Don't include this file directly"
#define LT_HW_BLE 1

View File

@@ -0,0 +1,5 @@
#pragma once
#error "Don't include this file directly"
#define LT_HW_BLE 1

View File

@@ -0,0 +1,5 @@
#pragma once
#error "Don't include this file directly"
#define LT_HW_BLE 1

View File

@@ -1,59 +1,40 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-23. */
#include "Serial.h"
#include "SerialPrivate.h"
#include <Arduino.h>
extern "C" {
#include <uart_pub.h>
extern void bk_send_byte(uint8_t uport, uint8_t data);
extern void uart_hw_set_change(uint8_t uport, bk_uart_config_t *uart_config);
extern int uart_rx_callback_set(int uport, uart_callback callback, void *param);
} // extern "C"
#if HAS_SERIAL1
#if LT_HW_UART1
SerialClass Serial1(UART1_PORT);
#endif
#if HAS_SERIAL2
#if LT_HW_UART2
SerialClass Serial2(UART2_PORT);
#endif
SerialClass::SerialClass(uint8_t port) {
this->port = port;
this->buf = NULL;
}
#if LT_AUTO_DOWNLOAD_REBOOT
static uint8_t adrState = 0;
static const uint8_t adrCmd[] = {0x01, 0xE0, 0xFC, 0x01, 0x00};
static void adrParse(uint8_t c) {
// parse and respond to link check command (CMD_LinkCheck=0)
adrState = (adrState + 1) * (c == adrCmd[adrState]);
if (adrState == 5) {
LT_I("Auto download mode: rebooting");
LT.restart();
}
}
#endif
static void callback(int port, void *param) {
RingBuffer *buf = (RingBuffer *)param;
int ch;
while ((ch = uart_read_byte(port)) != -1) {
#if LT_AUTO_DOWNLOAD_REBOOT && defined(PIN_SERIAL1_RX)
#if LT_AUTO_DOWNLOAD_REBOOT && defined(LT_UART_ADR_PATTERN) && PIN_SERIAL1_RX != PIN_INVALID
// parse UART protocol commands on UART1
if (port == UART1_PORT)
adrParse(ch);
SerialClass::adrParse(ch);
#endif
buf->store_char(ch);
pBUF->store_char(ch);
}
}
void SerialClass::begin(unsigned long baudrate, uint16_t config) {
if (!this->data) {
this->data = new SerialData();
this->buf = &BUF;
}
if (this->baudrate != baudrate || this->config != config)
this->configure(baudrate, config);
}
void SerialClass::configure(unsigned long baudrate, uint16_t config) {
if (!this->data)
return;
uint8_t dataWidth = ((config & SERIAL_DATA_MASK) >> 8) - 1; // 0x100..0x400 -> 0..3
uint8_t parity = 3 - (config & SERIAL_PARITY_MASK); // 0x3..0x1 -> 0..2
uint8_t stopBits = (config & SERIAL_STOP_BIT_MASK) == SERIAL_STOP_BIT_2; // 0x10..0x30 -> 0..1
@@ -66,17 +47,17 @@ void SerialClass::begin(unsigned long baudrate, uint16_t config) {
.flow_control = FLOW_CTRL_DISABLED,
};
if (this->buf) {
this->buf->clear();
} else {
this->buf = new RingBuffer();
}
uart_hw_set_change(port, &cfg);
uart_rx_callback_set(port, callback, this->buf);
uart_rx_callback_set(port, callback, &BUF);
this->baudrate = baudrate;
this->config = config;
}
void SerialClass::end() {
if (!this->data)
return;
uart_rx_callback_set(port, NULL, NULL);
switch (port) {
case 1:
@@ -86,26 +67,22 @@ void SerialClass::end() {
uart2_exit();
break;
}
delete this->buf;
}
int SerialClass::available() {
return buf->available();
}
int SerialClass::peek() {
return buf->peek();
}
int SerialClass::read() {
return buf->read_char();
delete DATA;
this->data = NULL;
this->buf = NULL;
this->baudrate = 0;
}
void SerialClass::flush() {
if (!this->data)
return;
uart_wait_tx_over();
}
size_t SerialClass::write(uint8_t c) {
if (!this->data)
return 0;
bk_send_byte(port, c);
return 1;
}

View File

@@ -0,0 +1,14 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-24. */
#pragma once
#include <Arduino.h>
#include <sdk_private.h>
typedef struct {
RingBuffer buf;
} SerialData;
#define DATA ((SerialData *)data)
#define BUF (DATA->buf)
#define pBUF ((RingBuffer *)param)

View File

@@ -2,7 +2,6 @@
#error "Don't include this file directly"
#define LT_ARD_HAS_WIFI 1
#define LT_ARD_HAS_SERIAL 1
#define LT_ARD_HAS_WIFI 1
#define LT_ARD_HAS_SERIAL 1
#define LT_ARD_MD5_HOSTAPD 1

View File

@@ -1,13 +1,6 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
#include <Arduino.h>
#include <include.h>
#include <arm_arch.h>
#include <bk_timer.h>
#include <bk_timer_pub.h>
#include <rtos_pub.h>
#include <sys_rtos.h>
#include "wiring_private.h"
#if LT_BK7231Q
#undef LT_MICROS_HIGH_RES
@@ -18,10 +11,6 @@
#define US_PER_OVERFLOW (portTICK_PERIOD_MS * 1000)
#define TICKS_PER_OVERFLOW (TICKS_PER_US * US_PER_OVERFLOW)
void delayMilliseconds(unsigned long ms) {
rtos_delay_milliseconds(ms);
}
#if LT_MICROS_HIGH_RES
static uint32_t getTicksCount() {
// copied from bk_timer_ctrl(), for speeds
@@ -107,8 +96,22 @@ unsigned long micros() {
#endif
}
void yield() {
runPeriodicTasks();
vTaskDelay(1);
taskYIELD();
void pinRemoveMode(PinInfo *pin, uint32_t mask) {
PinData *data = pinData(pin);
if ((mask & PIN_GPIO) && (pin->enabled & PIN_GPIO)) {
gpio_config(pin->gpio, GMODE_INPUT_PULLDOWN);
pinDisable(pin, PIN_GPIO);
}
if ((mask & PIN_IRQ) && (pin->enabled & PIN_IRQ)) {
data->irqHandler = NULL;
gpio_int_disable(pin->gpio);
pinDisable(pin, PIN_IRQ);
}
if ((mask & PIN_PWM) && (pin->enabled & PIN_PWM)) {
data->pwm->cfg.bits.en = PWM_DISABLE;
__wrap_bk_printf_disable();
sddev_control(PWM_DEV_NAME, CMD_PWM_DEINIT_PARAM, data->pwm);
__wrap_bk_printf_enable();
pinDisable(pin, PIN_PWM);
}
}

View File

@@ -1,10 +1,6 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-20. */
#include <Arduino.h>
#include <gpio_pub.h>
#include <pwm_pub.h>
#include <saradc_pub.h>
#include "wiring_private.h"
static GPIO_INDEX pwmToGpio[] = {
GPIO6, // PWM0
@@ -59,11 +55,7 @@ static pwm_param_t pwm;
static uint16_t adcData[1];
uint16_t analogReadVoltage(pin_size_t pinNumber) {
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return 0;
if (!pinSupported(pin, PIN_ADC))
return 0;
pinCheckGetInfo(pinNumber, PIN_ADC, 0);
UINT32 status;
saradc_desc_t adc;
@@ -90,11 +82,10 @@ uint16_t analogReadMaxVoltage(pin_size_t pinNumber) {
}
void analogWrite(pin_size_t pinNumber, int value) {
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return;
if (!pinSupported(pin, PIN_PWM))
return;
pinCheckGetData(pinNumber, PIN_PWM, );
// GPIO can't be used together with PWM
pinRemoveMode(pin, PIN_GPIO | PIN_IRQ);
float percent = value * 1.0 / ((1 << _analogWriteResolution) - 1);
uint32_t frequency = 26 * _analogWritePeriod - 1;
@@ -122,8 +113,9 @@ void analogWrite(pin_size_t pinNumber, int value) {
sddev_control(PWM_DEV_NAME, CMD_PWM_INIT_LEVL_SET_HIGH, &pwm.channel);
sddev_control(PWM_DEV_NAME, CMD_PWM_UNIT_ENABLE, &pwm.channel);
__wrap_bk_printf_enable();
pin->enabled &= ~PIN_GPIO;
pin->enabled |= PIN_PWM;
// pass global PWM object pointer
data->pwm = &pwm;
pinEnable(pin, PIN_PWM);
} else {
// update duty cycle
sddev_control(PWM_DEV_NAME, CMD_PWM_SET_DUTY_CYCLE, &pwm);
@@ -131,11 +123,7 @@ void analogWrite(pin_size_t pinNumber, int value) {
} else {
if (pinEnabled(pin, PIN_PWM)) {
// disable PWM
pwm.cfg.bits.en = PWM_DISABLE;
__wrap_bk_printf_disable();
sddev_control(PWM_DEV_NAME, CMD_PWM_DEINIT_PARAM, &pwm);
__wrap_bk_printf_enable();
pin->enabled &= ~PIN_PWM;
pinRemoveMode(pin, PIN_PWM);
}
// force level as LOW
pinMode(pinNumber, OUTPUT);

View File

@@ -0,0 +1,23 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-24. */
#pragma once
#include <Arduino.h>
#include <sdk_private.h>
#ifdef __cplusplus
extern "C" {
#endif
struct PinData_s {
pwm_param_t *pwm;
PinMode gpioMode;
PinStatus irqMode;
void *irqHandler;
void *irqParam;
bool irqChange;
};
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -1,20 +1,16 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-20. */
#include <Arduino.h>
#include <gpio_pub.h>
#include "wiring_private.h"
void pinMode(pin_size_t pinNumber, PinMode pinMode) {
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return;
if (!pinSupported(pin, PIN_GPIO))
return;
if (pinEnabled(pin, PIN_PWM))
// disable PWM before using the pin
analogWrite(pinNumber, 0);
if (pinEnabled(pin, PIN_GPIO) && pin->mode == pinMode)
pinCheckGetData(pinNumber, PIN_GPIO, );
if (pinEnabled(pin, PIN_GPIO) && data->gpioMode == pinMode)
return;
// GPIO can't be used together with PWM
pinRemoveMode(pin, PIN_PWM);
switch (pinMode) {
case INPUT:
gpio_config(pin->gpio, GMODE_INPUT);
@@ -31,34 +27,21 @@ void pinMode(pin_size_t pinNumber, PinMode pinMode) {
case OUTPUT_OPENDRAIN:
gpio_config(pin->gpio, GMODE_SET_HIGH_IMPENDANCE);
break;
default:
return;
}
pin->enabled |= PIN_GPIO;
pin->mode = pinMode;
pinEnable(pin, PIN_GPIO);
data->gpioMode = pinMode;
}
void digitalWrite(pin_size_t pinNumber, PinStatus status) {
// verify level is 0 or 1
if (status > HIGH)
return;
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return;
// pin is not GPIO yet or not OUTPUT; enable or disable input pullup
if (!pinEnabled(pin, PIN_GPIO) || !pinIsOutput(pin)) {
pinMode(pinNumber, status * INPUT_PULLUP);
return;
}
// write the new state
gpio_output(pin->gpio, status);
pinCheckGetData(pinNumber, PIN_GPIO, );
pinSetOutputPull(pin, data, pinNumber, status);
gpio_output(pin->gpio, !!status);
}
PinStatus digitalRead(pin_size_t pinNumber) {
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return 0;
// pin is not GPIO yet or not INPUT; change the mode
if (!pinEnabled(pin, PIN_GPIO) || !pinIsInput(pin))
pinMode(pinNumber, INPUT);
// read the value
pinCheckGetData(pinNumber, PIN_GPIO, LOW);
pinSetInputMode(pin, data, pinNumber);
return gpio_input(pin->gpio);
}

View File

@@ -1,98 +1,70 @@
/* Copyright (c) Kuba Szczodrzyński 2022-07-31. */
#include <Arduino.h>
#include <gpio_pub.h>
static void *irqHandlerList[PINS_COUNT] = {NULL};
static void *irqHandlerArgs[PINS_COUNT] = {NULL};
static bool irqChangeList[PINS_COUNT];
#include "wiring_private.h"
static void irqHandler(unsigned char gpio) {
PinInfo *pin = pinByGpio(gpio);
if (pin == NULL)
return;
uint32_t index = pinIndex(pin);
if (!irqHandlerList[index])
PinData *data = pinData(pin);
if (!data->irqHandler)
return;
if (irqChangeList[index]) {
if (pin->mode == INPUT_PULLDOWN) {
pin->mode = INPUT_PULLUP;
if (data->irqChange) {
if (data->gpioMode == INPUT_PULLDOWN) {
data->gpioMode = INPUT_PULLUP;
gpio_int_enable(pin->gpio, GPIO_INT_LEVEL_FALLING, irqHandler);
} else if (pin->mode == INPUT_PULLUP) {
pin->mode = INPUT_PULLDOWN;
} else if (data->gpioMode == INPUT_PULLUP) {
data->gpioMode = INPUT_PULLDOWN;
gpio_int_enable(pin->gpio, GPIO_INT_LEVEL_RISING, irqHandler);
}
}
if (irqHandlerArgs[index] == NULL) {
((voidFuncPtr)irqHandlerList[index])();
} else {
((voidFuncPtrParam)irqHandlerList[index])(irqHandlerArgs[index]);
}
}
void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus mode) {
attachInterruptParam(interruptNumber, (voidFuncPtrParam)callback, mode, NULL);
if (!data->irqParam)
((voidFuncPtr)data->irqHandler)();
else
((voidFuncPtrParam)data->irqHandler)(data->irqParam);
}
void attachInterruptParam(pin_size_t interruptNumber, voidFuncPtrParam callback, PinStatus mode, void *param) {
PinInfo *pin = pinInfo(interruptNumber);
if (!pin)
return;
if (!pinSupported(pin, PIN_IRQ))
return;
uint32_t index = pinIndex(pin);
uint32_t event = 0;
PinMode modeNew = 0;
bool change = 0;
pinCheckGetData(interruptNumber, PIN_IRQ, );
data->irqHandler = callback;
data->irqParam = param;
if (pinEnabled(pin, PIN_IRQ) && data->irqMode == mode)
return;
// GPIO can't be used together with PWM
pinRemoveMode(pin, PIN_PWM);
uint32_t event = 0;
bool change = false;
switch (mode) {
case LOW:
event = GPIO_INT_LEVEL_LOW;
modeNew = INPUT_PULLUP;
change = false;
event = GPIO_INT_LEVEL_LOW;
break;
case HIGH:
event = GPIO_INT_LEVEL_HIGH;
modeNew = INPUT_PULLDOWN;
change = false;
event = GPIO_INT_LEVEL_HIGH;
break;
case FALLING:
event = GPIO_INT_LEVEL_FALLING;
modeNew = INPUT_PULLUP;
change = false;
event = GPIO_INT_LEVEL_FALLING;
break;
case RISING:
event = GPIO_INT_LEVEL_RISING;
modeNew = INPUT_PULLDOWN;
change = false;
event = GPIO_INT_LEVEL_RISING;
break;
case CHANGE:
event = GPIO_INT_LEVEL_FALLING;
modeNew = INPUT_PULLUP;
change = true;
event = GPIO_INT_LEVEL_FALLING;
change = true;
break;
default:
return;
}
irqHandlerList[index] = callback;
irqHandlerArgs[index] = param;
irqChangeList[index] = change;
pinEnable(pin, PIN_IRQ);
data->irqMode = mode;
data->irqChange = change;
gpio_int_enable(pin->gpio, event, irqHandler);
pin->enabled |= PIN_IRQ | PIN_GPIO;
pin->mode = modeNew;
}
void detachInterrupt(pin_size_t interruptNumber) {
PinInfo *pin = pinInfo(interruptNumber);
if (!pin)
return;
if (!pinSupported(pin, PIN_IRQ))
return;
uint32_t index = pinIndex(pin);
irqHandlerList[index] = NULL;
irqHandlerArgs[index] = NULL;
irqChangeList[index] = false;
gpio_int_disable(pin->gpio);
pin->enabled &= ~PIN_IRQ;
pinModeRemove(interruptNumber, PIN_IRQ);
}

View File

@@ -0,0 +1,13 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
lt_cpu_model_t lt_cpu_get_model() {
uint8_t chipId = *(uint8_t *)(SCTRL_CHIP_ID);
return CPU_MODEL_ENUM(FAMILY, chipId);
}
const char *lt_cpu_get_core_type() {
return "ARM968E-S (ARMv5TE)";
}

View File

@@ -0,0 +1,41 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
void lt_get_device_mac(uint8_t *mac) {
cfg_load_mac(mac);
}
void lt_reboot() {
bk_reboot();
}
bool lt_reboot_download_mode() {
bk_reboot();
return true;
}
lt_reboot_reason_t lt_get_reboot_reason() {
switch (bk_misc_get_start_type()) {
case RESET_SOURCE_POWERON:
return REBOOT_REASON_POWER;
case RESET_SOURCE_REBOOT:
return REBOOT_REASON_SOFTWARE;
case RESET_SOURCE_WATCHDOG:
return REBOOT_REASON_WATCHDOG;
case RESET_SOURCE_CRASH_XAT0:
case RESET_SOURCE_CRASH_UNDEFINED:
case RESET_SOURCE_CRASH_PREFETCH_ABORT:
case RESET_SOURCE_CRASH_DATA_ABORT:
case RESET_SOURCE_CRASH_UNUSED:
case RESET_SOURCE_CRASH_PER_XAT0:
return REBOOT_REASON_CRASH;
case RESET_SOURCE_DEEPPS_GPIO:
case RESET_SOURCE_DEEPPS_RTC:
case RESET_SOURCE_DEEPPS_USB:
return REBOOT_REASON_SLEEP;
default:
return REBOOT_REASON_UNKNOWN;
}
}

View File

@@ -0,0 +1,26 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
// can't include <flash.h> as it collides with <Flash.h> on Windows -_-
#define REG_FLASH_BASE 0x00803000
#define REG_FLASH_OPERATE_SW (REG_FLASH_BASE + 0 * 4)
#define REG_FLASH_RDID (REG_FLASH_BASE + 4 * 4)
#define FLASH_BUSY_SW (0x01UL << 31)
#define FLASH_WP_VALUE (0x01UL << 30)
#define FLASH_OP_SW (0x01UL << 29)
#define FLASH_OP_TYPE_POS 24
#define FLASH_OP_RDID 20
lt_flash_id_t lt_flash_get_id() {
uint32_t data = (FLASH_OP_RDID << FLASH_OP_TYPE_POS) | FLASH_OP_SW | FLASH_WP_VALUE;
REG_WRITE(REG_FLASH_OPERATE_SW, data);
while (REG_READ(REG_FLASH_OPERATE_SW) & FLASH_BUSY_SW) {}
lt_flash_id_t id = {
.manufacturer_id = REG_RD8(REG_FLASH_RDID, 2),
.chip_id = REG_RD8(REG_FLASH_RDID, 1),
.chip_size_id = REG_RD8(REG_FLASH_RDID, 0),
};
return id;
}

View File

@@ -0,0 +1,9 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
void lt_init_family() {
// set default UART output port
uart_print_port = LT_UART_DEFAULT_PORT - 1;
}

View File

@@ -0,0 +1,21 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
uint32_t lt_ram_get_size() {
return 256 * 1024;
}
uint32_t lt_heap_get_size() {
#if configDYNAMIC_HEAP_SIZE
extern unsigned char _empty_ram;
#if CFG_SOC_NAME == SOC_BK7231N
return (0x00400000 + 192 * 1024) - (uint32_t)(&_empty_ram);
#else
return (0x00400000 + 256 * 1024) - (uint32_t)(&_empty_ram);
#endif
#else
return configTOTAL_HEAP_SIZE;
#endif
}

View File

@@ -0,0 +1,39 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
lt_ota_type_t lt_ota_get_type() {
return OTA_TYPE_SINGLE;
}
bool lt_ota_is_valid(uint8_t index) {
if (index != 0)
return false;
// check download RBL
// TODO: maybe check header CRC or even binary hashes
uint32_t magic;
lt_flash_read(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4);
return magic == 0x004C4252; // "RBL\0", little-endian
}
uint8_t lt_ota_dual_get_current() {
return 0;
}
uint8_t lt_ota_dual_get_stored() {
return 0;
}
bool lt_ota_switch(bool revert) {
if (!lt_ota_is_valid(0))
// no valid "download" image
// - return false when trying to activate
// - return true when trying to revert
return revert;
if (revert) {
// there's a valid "download" image, which has to be removed
return lt_flash_erase_block(FLASH_DOWNLOAD_OFFSET);
}
return true;
}

View File

@@ -0,0 +1,18 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
bool lt_wdt_enable(uint32_t timeout) {
wdt_ctrl(WCMD_SET_PERIOD, &timeout);
wdt_ctrl(WCMD_POWER_UP, NULL);
return true;
}
void lt_wdt_disable() {
wdt_ctrl(WCMD_POWER_DOWN, NULL);
}
void lt_wdt_feed() {
wdt_ctrl(WCMD_RELOAD_PERIOD, NULL);
}

View File

@@ -1,190 +0,0 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
// can't include <flash.h> as it collides with <Flash.h> on Windows -_-
#define REG_FLASH_BASE 0x00803000
#define REG_FLASH_OPERATE_SW (REG_FLASH_BASE + 0 * 4)
#define REG_FLASH_RDID (REG_FLASH_BASE + 4 * 4)
#define FLASH_BUSY_SW (0x01UL << 31)
#define FLASH_WP_VALUE (0x01UL << 30)
#define FLASH_OP_SW (0x01UL << 29)
#define FLASH_OP_TYPE_POS 24
#define FLASH_OP_RDID 20
void lt_init_family() {
// set default UART output port
uart_print_port = LT_UART_DEFAULT_PORT - 1;
}
/* _____ _____ _ _
/ ____| __ \| | | |
| | | |__) | | | |
| | | ___/| | | |
| |____| | | |__| |
\_____|_| \____*/
lt_cpu_model_t lt_cpu_get_model() {
uint8_t chipId = *(uint8_t *)(SCTRL_CHIP_ID);
return CPU_MODEL_ENUM(FAMILY, chipId);
}
uint32_t lt_cpu_get_unique_id() {
return lt_cpu_get_mac_id();
}
uint32_t lt_cpu_get_mac_id() {
uint8_t mac[6];
cfg_load_mac(mac); // force loading MAC from TLV (ignore user-set WiFi MAC)
return (mac[3]) | (mac[4] << 8) | (mac[5] << 16);
}
const char *lt_cpu_get_core_type() {
return "ARM968E-S";
}
/*_____ _
| __ \ (_)
| | | | _____ ___ ___ ___
| | | |/ _ \ \ / / |/ __/ _ \
| |__| | __/\ V /| | (_| __/
|_____/ \___| \_/ |_|\___\__*/
void lt_reboot() {
bk_reboot();
}
bool lt_reboot_download_mode() {
bk_reboot();
return true;
}
lt_reboot_reason_t lt_get_reboot_reason() {
switch (bk_misc_get_start_type()) {
case RESET_SOURCE_POWERON:
return REBOOT_REASON_POWER;
case RESET_SOURCE_REBOOT:
return REBOOT_REASON_SOFTWARE;
case RESET_SOURCE_WATCHDOG:
return REBOOT_REASON_WATCHDOG;
case RESET_SOURCE_CRASH_XAT0:
case RESET_SOURCE_CRASH_UNDEFINED:
case RESET_SOURCE_CRASH_PREFETCH_ABORT:
case RESET_SOURCE_CRASH_DATA_ABORT:
case RESET_SOURCE_CRASH_UNUSED:
case RESET_SOURCE_CRASH_PER_XAT0:
return REBOOT_REASON_CRASH;
case RESET_SOURCE_DEEPPS_GPIO:
case RESET_SOURCE_DEEPPS_RTC:
case RESET_SOURCE_DEEPPS_USB:
return REBOOT_REASON_SLEEP;
default:
return REBOOT_REASON_UNKNOWN;
}
}
/*______ _ _
| ____| | | |
| |__ | | __ _ ___| |__
| __| | |/ _` / __| '_ \
| | | | (_| \__ \ | | |
|_| |_|\__,_|___/_| |*/
lt_flash_id_t lt_flash_get_id() {
uint32_t data = (FLASH_OP_RDID << FLASH_OP_TYPE_POS) | FLASH_OP_SW | FLASH_WP_VALUE;
REG_WRITE(REG_FLASH_OPERATE_SW, data);
while (REG_READ(REG_FLASH_OPERATE_SW) & FLASH_BUSY_SW) {}
lt_flash_id_t id = {
.manufacturer_id = REG_RD8(REG_FLASH_RDID, 2),
.chip_id = REG_RD8(REG_FLASH_RDID, 1),
.chip_size_id = REG_RD8(REG_FLASH_RDID, 0),
};
return id;
}
/*__ __
| \/ |
| \ / | ___ _ __ ___ ___ _ __ _ _
| |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | |
| | | | __/ | | | | | (_) | | | |_| |
|_| |_|\___|_| |_| |_|\___/|_| \__, |
__/ |
|__*/
uint32_t lt_ram_get_size() {
return 256 * 1024;
}
uint32_t lt_heap_get_size() {
#if configDYNAMIC_HEAP_SIZE
extern unsigned char _empty_ram;
#if CFG_SOC_NAME == SOC_BK7231N
return (0x00400000 + 192 * 1024) - (uint32_t)(&_empty_ram);
#else
return (0x00400000 + 256 * 1024) - (uint32_t)(&_empty_ram);
#endif
#else
return configTOTAL_HEAP_SIZE;
#endif
}
/* ____ _______
/ __ \__ __|/\
| | | | | | / \
| | | | | | / /\ \
| |__| | | |/ ____ \
\____/ |_/_/ \*/
lt_ota_type_t lt_ota_get_type() {
return OTA_TYPE_SINGLE;
}
bool lt_ota_is_valid(uint8_t index) {
if (index != 0)
return false;
// check download RBL
// TODO: maybe check header CRC or even binary hashes
uint32_t magic;
lt_flash_read(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4);
return magic == 0x004C4252; // "RBL\0", little-endian
}
uint8_t lt_ota_dual_get_current() {
return 0;
}
uint8_t lt_ota_dual_get_stored() {
return 0;
}
bool lt_ota_switch(bool revert) {
if (!lt_ota_is_valid(0))
// no valid "download" image
// - return false when trying to activate
// - return true when trying to revert
return revert;
if (revert) {
// there's a valid "download" image, which has to be removed
return lt_flash_erase_block(FLASH_DOWNLOAD_OFFSET);
}
return true;
}
/*_ __ _ _ _
\ \ / / | | | | | |
\ \ /\ / /_ _| |_ ___| |__ __| | ___ __ _
\ \/ \/ / _` | __/ __| '_ \ / _` |/ _ \ / _` |
\ /\ / (_| | || (__| | | | (_| | (_) | (_| |
\/ \/ \__,_|\__\___|_| |_|\__,_|\___/ \__, |
__/ |
|___*/
bool lt_wdt_enable(uint32_t timeout) {
wdt_ctrl(WCMD_SET_PERIOD, &timeout);
wdt_ctrl(WCMD_POWER_UP, NULL);
return true;
}
void lt_wdt_disable() {
wdt_ctrl(WCMD_POWER_DOWN, NULL);
}
void lt_wdt_feed() {
wdt_ctrl(WCMD_RELOAD_PERIOD, NULL);
}

View File

@@ -2,11 +2,16 @@
#error "Don't include this file directly"
#define LT_HAS_PRINTF 1
#define LT_HAS_LWIP 1
#define LT_HAS_LWIP2 1
#define LT_HAS_FREERTOS 1
#define LT_HAS_MBEDTLS 1
#define LT_HAS_FLASH 1
#define LT_HAS_FREERTOS 1
#define LT_HAS_LWIP 1
#define LT_HAS_LWIP2 1
#define LT_HAS_MBEDTLS 1
#define LT_HAS_OTA 1
#define LT_HAS_PRINTF 1
#define LT_HW_DEEP_SLEEP 1
#define LT_HW_WATCHDOG 1
#define LT_HW_WIFI 1
#define LT_HEAP_FUNC xPortGetFreeHeapSize
#define LT_REALLOC_FUNC pvPortRealloc

View File

@@ -2,15 +2,19 @@
#pragma once
#include LT_VARIANT_H
#include <lt_pins.h>
// Choose the main UART output port
#ifndef LT_UART_DEFAULT_PORT
#if HAS_SERIAL2
#if LT_HW_UART2
#define LT_UART_DEFAULT_PORT 2
#elif HAS_SERIAL1
#elif LT_HW_UART1
#define LT_UART_DEFAULT_PORT 1
#else
#error "No serial port is available"
#endif
#endif
// Auto-download-reboot detection pattern
// Link check command (CMD_LinkCheck=0)
#define LT_UART_ADR_PATTERN 0x01, 0xE0, 0xFC, 0x01, 0x00

View File

@@ -0,0 +1,24 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-24. */
#pragma once
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include <uart_pub.h>
// SDK
extern uint8_t system_mac[];
extern int uart_print_port;
uint32_t wdt_ctrl(uint32_t cmd, void *param);
void bk_send_byte(uint8_t uport, uint8_t data);
void uart_hw_set_change(uint8_t uport, bk_uart_config_t *uart_config);
int uart_rx_callback_set(int uport, uart_callback callback, void *param);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -6,12 +6,18 @@
extern "C" {
#endif // __cplusplus
// most stuff is here
// most stuff is here - this has to be before other includes!
#include <include.h>
// other includes
#include <arm_arch.h>
#include <bk_timer.h>
#include <bk_timer_pub.h>
#include <flash_pub.h>
#include <gpio_pub.h>
#include <param_config.h>
#include <pwm_pub.h>
#include <rtos_pub.h>
#include <saradc_pub.h>
#include <start_type_pub.h>
#include <sys_ctrl.h>
#include <sys_rtos.h>
@@ -19,9 +25,7 @@ extern "C" {
#include <wdt_pub.h>
#include <wlan_ui_pub.h>
extern uint8_t system_mac[];
extern uint32_t wdt_ctrl(uint32_t cmd, void *param);
extern int uart_print_port;
#include <sdk_extern.h>
// conflict with stl_algobase.h
#undef min

View File

@@ -0,0 +1,36 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-23. */
#include "Serial.h"
SerialClass::SerialClass(uint32_t port, pin_size_t rx, pin_size_t tx) {
this->port = port;
this->rx = rx;
this->tx = tx;
this->buf = NULL;
this->data = NULL;
}
#if LT_AUTO_DOWNLOAD_REBOOT && defined(LT_UART_ADR_PATTERN)
static uint8_t adrState = 0;
static uint8_t adrCmd[] = {LT_UART_ADR_PATTERN};
void SerialClass::adrParse(uint8_t c) {
adrState = (adrState + 1) * (c == adrCmd[adrState]);
if (adrState == sizeof(adrCmd) / sizeof(uint8_t)) {
LT_I("Auto download mode: rebooting");
LT.restartDownloadMode();
}
}
#endif
int SerialClass::available() {
return this->buf && this->buf->available();
}
int SerialClass::peek() {
return this->buf ? this->buf->peek() : -1;
}
int SerialClass::read() {
return this->buf ? this->buf->read_char() : -1;
}

View File

@@ -2,7 +2,7 @@
#pragma once
#include <api/ArduinoAPI.h>
#include <Arduino.h>
#include <api/HardwareSerial.h>
#include <api/RingBuffer.h>
@@ -10,17 +10,31 @@ using namespace arduino;
class SerialClass : public HardwareSerial {
private:
uint8_t port;
RingBuffer *buf;
uint32_t port;
pin_size_t rx;
pin_size_t tx;
public:
SerialClass(uint8_t port);
void *data;
private:
RingBuffer *buf;
uint32_t baudrate;
uint16_t config;
public:
SerialClass(uint32_t port, pin_size_t rx = PIN_INVALID, pin_size_t tx = PIN_INVALID);
inline void begin(unsigned long baudrate) {
begin(baudrate, SERIAL_8N1);
}
inline void configure(unsigned long baudrate) {
configure(baudrate, SERIAL_8N1);
}
void begin(unsigned long baudrate, uint16_t config);
void configure(unsigned long baudrate, uint16_t config);
void end();
int available();
int peek();
@@ -32,5 +46,12 @@ class SerialClass : public HardwareSerial {
return !!buf;
}
public:
#if LT_AUTO_DOWNLOAD_REBOOT
static void adrParse(uint8_t c);
#endif
using Print::write;
};
#define HAS_SERIAL_CLASS 1

View File

@@ -3,11 +3,11 @@
#include "WiFi.h"
void WiFiClass::printDiag(Print &dest) {
const char *modes[] = {"NULL", "STA", "AP", "STA+AP"};
const char *enc[] = {"Open", "WEP", "WPA PSK", "WPA2 PSK", "WPA/WPA2", "WPA", "WPA2"};
dest.print("Mode: ");
dest.println(modes[getMode()]);
dest.println(WiFiModeText[getMode()]);
dest.print("Status: ");
dest.println(WiFiStatusText[status()]);
if (getMode() & WIFI_MODE_STA) {
dest.println("-- Station --");
@@ -21,7 +21,7 @@ void WiFiClass::printDiag(Print &dest) {
dest.print("RSSI: ");
dest.println(RSSI());
dest.print("Encryption: ");
dest.println(enc[getEncryption()]);
dest.println(WiFiAuthModeText[getEncryption()]);
dest.print("IP: ");
dest.println(localIP());
dest.print("MAC: ");

View File

@@ -11,7 +11,7 @@ bool WiFiClass::mode(WiFiMode mode) {
pWiFi = this;
WiFiMode currentMode = getMode();
LT_DM(WIFI, "Mode changing %u -> %u", currentMode, mode);
LT_DM(WIFI, "Mode changing %s -> %s", WiFiModeText[currentMode], WiFiModeText[mode]);
if (mode == currentMode)
return true;

View File

@@ -162,3 +162,28 @@ typedef enum {
WLMODE_DISABLE = 1,
WLMODE_ENABLE = 2,
} WiFiModeAction;
static const char *WiFiModeText[] = {"NULL", "STA", "AP", "AP+STA"};
static const char *WiFiStatusText[] = {
"Idle",
"No SSID",
"Scan Completed",
"Connected",
"Connect failed",
"Connection lost",
"Disconnected",
};
static const char *WiFiAuthModeText[] = {
"Open",
"WEP",
"WPA PSK",
"WPA2 PSK",
"WPA/WPA2 PSK",
"WPA2 EAP",
"WPA3 PSK",
"WPA2/WPA3 PSK",
"WAPI PSK",
"WPA",
"WPA2",
"Auto",
};

View File

@@ -2,9 +2,25 @@
#include "Update.h"
UpdateClass::UpdateClass() : ctx(NULL), info(NULL), buf(NULL) {
cleanup();
}
static const UpdateError errorMap[] = {
UPDATE_ERROR_OK, /* UF2_ERR_OK - no error */
UPDATE_ERROR_OK, /* UF2_ERR_IGNORE - block should be ignored */
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_MAGIC - wrong magic numbers */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_FAMILY - family ID mismatched */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_NOT_HEADER - block is not a header */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_OTA_VER - unknown/invalid OTA format version */
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_OTA_WRONG - no data for current OTA scheme */
UPDATE_ERROR_NO_PARTITION, /* UF2_ERR_PART_404 - no partition with that name */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_INVALID - invalid partition info tag */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_UNSET - attempted to write without target partition */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_DATA_TOO_LONG - data too long - tags won't fit */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_SEQ_MISMATCH - sequence number mismatched */
UPDATE_ERROR_ERASE, /* UF2_ERR_ERASE_FAILED - erasing flash failed */
UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_FAILED - writing to flash failed */
UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_LENGTH - wrote fewer data than requested */
UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_PROTECT - target area is write-protected */
UPDATE_ERROR_WRITE, /* UF2_ERR_ALLOC_FAILED - dynamic memory allocation failed */
};
/**
* @brief Initialize the update process.
@@ -13,55 +29,93 @@ UpdateClass::UpdateClass() : ctx(NULL), info(NULL), buf(NULL) {
* @param command must be U_FLASH
* @return false if parameters are invalid or update is running, true otherwise
*/
bool UpdateClass::begin(size_t size, int command, int unused2, uint8_t unused3, const char *unused4) {
if (ctx)
return false;
cleanup();
LT_DM(OTA, "begin(%u, ...) / OTA curr: %u, scheme: %u", size, lt_ota_dual_get_current(), lt_ota_get_uf2_scheme());
ctx = uf2_ctx_init(lt_ota_get_uf2_scheme(), FAMILY);
info = uf2_info_init();
if (!size) {
cleanup(UPDATE_ERROR_SIZE);
bool UpdateClass::begin(
size_t size,
int command,
__attribute__((unused)) int ledPin,
__attribute__((unused)) uint8_t ledOn,
__attribute__((unused)) const char *label
) {
#if !LT_HAS_OTA
LT_E("OTA is not yet supported on this chip!");
this->errArd = UPDATE_ERROR_BAD_ARGUMENT;
return false;
#endif
if (this->ctx) {
return false;
}
this->clearError();
if (size == 0) {
this->errArd = UPDATE_ERROR_SIZE;
return false;
}
if (command != U_FLASH) {
cleanup(UPDATE_ERROR_BAD_ARGUMENT);
this->errArd = UPDATE_ERROR_BAD_ARGUMENT;
return false;
}
if (size == UPDATE_SIZE_UNKNOWN) {
size = 0;
}
bytesTotal = size;
this->ctx = static_cast<lt_ota_ctx_t *>(malloc(sizeof(lt_ota_ctx_t)));
lt_ota_begin(this->ctx, size);
this->ctx->callback = reinterpret_cast<void (*)(void *)>(progressHandler);
this->ctx->callback_param = this;
return true;
}
/**
* @brief Finalize the update process. Check for errors and update completion, then activate the new firmware image.
*
* @param evenIfRemaining no idea
* @return false in case of errors or no update running, true otherwise
* @param evenIfRemaining don't raise errors if still in progress
* @return false in case of errors or no update running; true otherwise
*/
bool UpdateClass::end(bool evenIfRemaining) {
if (hasError() || !ctx)
// false if not running
if (!this->ctx)
return false;
if (!isFinished() && !evenIfRemaining) {
// update is running or finished; cleanup and end it
if (!isFinished() && !evenIfRemaining)
// abort if not finished
cleanup(UPDATE_ERROR_ABORT);
return false;
}
// TODO what is evenIfRemaining for?
// try to activate the second OTA
if (!lt_ota_switch(/* revert= */ false)) {
cleanup(UPDATE_ERROR_ACTIVATE);
return false;
this->errArd = UPDATE_ERROR_ABORT;
this->cleanup(/* clearError= */ evenIfRemaining);
return !this->hasError();
}
/**
* @brief Cleanup (free) the update context.
* Try activating the firmware if possible, set local error codes.
*
* @param clearError assume successful finish after correct activation
*/
void UpdateClass::cleanup(bool clearError) {
if (!this->ctx)
return;
if (!lt_ota_end(this->ctx)) {
// activating firmware failed
this->errArd = UPDATE_ERROR_ACTIVATE;
this->errUf2 = UF2_ERR_OK;
} else if (clearError) {
// successful finish and activation, clear error codes
this->clearError();
} else if (this->ctx->error > UF2_ERR_IGNORE) {
// make error code based on UF2OTA code
this->errArd = errorMap[this->ctx->error];
this->errUf2 = this->ctx->error;
} else {
// only keep Arduino error code (set by the caller)
this->errUf2 = UF2_ERR_OK;
}
cleanup();
return true;
#if LT_DEBUG_OTA
if (this->hasError())
this->printErrorContext();
#endif
free(this->ctx);
this->ctx = nullptr;
}
/**
@@ -69,60 +123,44 @@ bool UpdateClass::end(bool evenIfRemaining) {
*
* It's advised to write in 512-byte chunks (or its multiples).
*
* @param data
* @param len
* @return size_t
* @param data chunk of data
* @param len length of the chunk
* @return size_t amount of bytes written
*/
size_t UpdateClass::write(uint8_t *data, size_t len) {
size_t written = 0;
if (hasError() || !ctx)
// 0 if not running
size_t UpdateClass::write(const uint8_t *data, size_t len) {
if (!this->ctx)
return 0;
LT_VM(OTA, "write(%u) / buf %u/512", len, bufSize());
/* while (buf == bufPos && len >= UF2_BLOCK_SIZE) {
// buffer empty and entire block is in data
if (!tryWriteData(data, UF2_BLOCK_SIZE)) {
// returns 0 if data contains an invalid block
return written;
}
data += UF2_BLOCK_SIZE;
len -= UF2_BLOCK_SIZE;
written += UF2_BLOCK_SIZE;
} */
// write until buffer space is available
uint16_t toWrite; // 1..512
while (len && (toWrite = min(len, bufLeft()))) {
tryWriteData(data, toWrite);
if (hasError()) {
// return on errors
printErrorContext2(data, toWrite);
return written;
}
data += toWrite;
len -= toWrite;
written += toWrite;
}
size_t written = lt_ota_write(ctx, data, len);
if (written != len)
this->cleanup(/* clearError= */ false);
return written;
}
/**
* @brief Write all data remaining in the given stream.
*
* If the stream doesn't produce any data within UPDATE_TIMEOUT_MS,
* the update process will be aborted.
*
* @param data stream to read from
* @return size_t amount of bytes written
*/
size_t UpdateClass::writeStream(Stream &data) {
size_t written = 0;
if (hasError() || !ctx)
// 0 if not running
if (!this->ctx)
return 0;
size_t written = 0;
uint32_t lastData = millis();
// loop until the update is complete
while (remaining()) {
// check stream availability
int available = data.available();
auto available = data.available();
if (available <= 0) {
if (millis() - lastData > UPDATE_TIMEOUT_MS) {
// waited for data too long; abort with error
cleanup(UPDATE_ERROR_STREAM);
this->errArd = UPDATE_ERROR_STREAM;
this->cleanup(/* clearError= */ false);
return written;
}
continue;
@@ -131,94 +169,21 @@ size_t UpdateClass::writeStream(Stream &data) {
lastData = millis();
// read data to fit in the remaining buffer space
bufAlloc();
uint16_t read = data.readBytes(bufPos, bufLeft());
bufPos += read;
written += read;
tryWriteData();
auto bufSize = this->ctx->buf_pos - this->ctx->buf;
auto read = data.readBytes(this->ctx->buf_pos, UF2_BLOCK_SIZE - bufSize);
// increment buffer writing head
this->ctx->buf_pos += read;
// process the block if complete
if (bufSize + read == UF2_BLOCK_SIZE)
lt_ota_write_block(this->ctx, reinterpret_cast<uf2_block_t *>(this->ctx->buf));
// abort on errors
if (hasError()) {
// return on errors
printErrorContext2(NULL, read); // buf is not valid anymore
this->cleanup(/* clearError= */ false);
return written;
}
written += read;
}
return written;
}
/**
* @brief Try to use the buffer as a block to write. In case of UF2 errors,
* error codes are set, the update is aborted and 0 is returned
*
* @param data received data to copy to buffer or NULL if already in buffer
* @param len received data length - must be at most bufLeft()
* @return size_t "used" data size - 0 or 512
*/
size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) {
uf2_block_t *block = NULL;
LT_VM(OTA, "Writing %u to buffer (%u/512)", len, bufSize());
if (len == UF2_BLOCK_SIZE) {
// data has a complete block
block = (uf2_block_t *)data;
} else if (data && len) {
// data has a part of a block, copy it to buffer
bufAlloc();
memcpy(bufPos, data, len);
bufPos += len;
}
if (!block && bufSize() == UF2_BLOCK_SIZE) {
// use buffer as block (only if not found above)
block = (uf2_block_t *)buf;
}
// a complete block has been found
if (block) {
if (checkUf2Error(uf2_check_block(ctx, block)))
// block is invalid
return 0;
if (errUf2 == UF2_ERR_IGNORE)
// treat ignored blocks as valid
return UF2_BLOCK_SIZE;
if (!bytesWritten) {
// parse header block to allow retrieving firmware info
if (checkUf2Error(uf2_parse_header(ctx, block, info)))
// header is invalid
return 0;
LT_IM(OTA, "%s v%s - LT v%s @ %s", info->fw_name, info->fw_version, info->lt_version, info->board);
if (bytesTotal == UPDATE_SIZE_UNKNOWN) {
// set total update size from block count info
bytesTotal = block->block_count * UF2_BLOCK_SIZE;
} else if (bytesTotal != block->block_count * UF2_BLOCK_SIZE) {
// given update size does not match the block count
LT_EM(OTA, "Image size wrong; got %u, calculated %u", bytesTotal, block->block_count * UF2_BLOCK_SIZE);
cleanup(UPDATE_ERROR_SIZE);
return 0;
}
} else {
// write data blocks normally
if (checkUf2Error(uf2_write(ctx, block)))
// block writing failed
return 0;
}
// increment total writing progress
bytesWritten += UF2_BLOCK_SIZE;
// call progress callback
if (callback)
callback(bytesWritten, bytesTotal);
// reset the buffer as it's used already
if (bufSize() == UF2_BLOCK_SIZE)
bufPos = buf;
return UF2_BLOCK_SIZE;
}
return 0;
}
UpdateClass Update;

View File

@@ -4,39 +4,30 @@
#include <functional>
#include <uf2ota/uf2ota.h>
// No Error
#define UPDATE_ERROR_OK (0)
// Flash Write Failed
#define UPDATE_ERROR_WRITE (1)
// Flash Erase Failed
#define UPDATE_ERROR_ERASE (2)
// Flash Read Failed
#define UPDATE_ERROR_READ (3)
// Not Enough Space
#define UPDATE_ERROR_SPACE (4)
// Bad Size Given
#define UPDATE_ERROR_SIZE (5)
// Stream Read Timeout
#define UPDATE_ERROR_STREAM (6)
// MD5 Check Failed
#define UPDATE_ERROR_MD5 (7)
// Wrong Magic Byte
#define UPDATE_ERROR_MAGIC_BYTE (8)
// Could Not Activate The Firmware
#define UPDATE_ERROR_ACTIVATE (9)
// Partition Could Not be Found
#define UPDATE_ERROR_NO_PARTITION (10)
// Bad Argument
#define UPDATE_ERROR_BAD_ARGUMENT (11)
// Aborted
#define UPDATE_ERROR_ABORT (12)
typedef enum {
UPDATE_ERROR_OK = 0, //!< No Error
UPDATE_ERROR_WRITE = 1, //!< Flash Write Failed
UPDATE_ERROR_ERASE = 2, //!< Flash Erase Failed
UPDATE_ERROR_READ = 3, //!< Flash Read Failed
UPDATE_ERROR_SPACE = 4, //!< Not Enough Space
UPDATE_ERROR_SIZE = 5, //!< Bad Size Given
UPDATE_ERROR_STREAM = 6, //!< Stream Read Timeout
UPDATE_ERROR_MD5 = 7, //!< MD5 Check Failed
UPDATE_ERROR_MAGIC_BYTE = 8, //!< Wrong Magic Byte
UPDATE_ERROR_ACTIVATE = 9, //!< Could Not Activate The Firmware
UPDATE_ERROR_NO_PARTITION = 10, //!< Partition Could Not be Found
UPDATE_ERROR_BAD_ARGUMENT = 11, //!< Bad Argument
UPDATE_ERROR_ABORT = 12, //!< Aborted
} UpdateError;
typedef enum {
U_FLASH = 0,
U_SPIFFS = 100,
U_AUTH = 200,
} UpdateCommand;
#define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF
#define U_FLASH 0
#define U_SPIFFS 100
#define U_AUTH 200
#define ENCRYPTED_BLOCK_SIZE 16
#define UPDATE_TIMEOUT_MS 30 * 1000
@@ -46,109 +37,132 @@ class UpdateClass {
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
public: /* Update.cpp */
UpdateClass();
bool begin(
size_t size = UPDATE_SIZE_UNKNOWN,
int command = U_FLASH,
int unused2 = -1,
uint8_t unused3 = LOW,
const char *unused4 = NULL // this is for SPIFFS
size_t size = UPDATE_SIZE_UNKNOWN,
int command = U_FLASH,
int ledPin = -1,
uint8_t ledOn = LOW,
const char *label = nullptr
);
bool end(bool evenIfRemaining = false);
size_t write(uint8_t *data, size_t len);
size_t write(const uint8_t *data, size_t len);
size_t writeStream(Stream &data);
bool canRollBack();
bool rollBack();
// bool setMD5(const char *expected_md5);
private: /* Update.cpp */
size_t tryWriteData(uint8_t *data = NULL, size_t len = 0);
void cleanup(bool clearError = false);
public: /* UpdateUtil.cpp */
UpdateClass &onProgress(THandlerFunction_Progress callback);
void abort();
void printError(Print &out);
const char *errorString();
const char *getFirmwareName();
const char *getFirmwareVersion();
const char *getLibreTinyVersion();
const char *getBoardName();
UpdateClass &onProgress(THandlerFunction_Progress handler);
static bool canRollBack();
static bool rollBack();
uint16_t getErrorCode() const;
bool hasError() const;
void clearError();
const char *errorString() const;
void printError(Print &out) const;
private: /* UpdateUtil.cpp */
void cleanup(uint8_t ardErr = UPDATE_ERROR_OK, uf2_err_t uf2Err = UF2_ERR_OK);
bool checkUf2Error(uf2_err_t err);
void bufAlloc();
void printErrorContext1();
void printErrorContext2(const uint8_t *data, size_t len);
uint16_t bufLeft();
uint16_t bufSize();
static void progressHandler(UpdateClass *self);
void printErrorContext();
private:
// uf2ota context
uf2_ota_t *ctx;
uf2_info_t *info;
// block buffer
uint8_t *buf;
uint8_t *bufPos;
// update progress - multiplies of 512 bytes
uint32_t bytesWritten;
uint32_t bytesTotal;
// errors
uf2_err_t errUf2;
uint8_t errArd;
// progress callback
THandlerFunction_Progress callback;
// String _target_md5;
// MD5Builder _md5;
lt_ota_ctx_t *ctx{nullptr};
uf2_err_t errUf2{UF2_ERR_OK};
UpdateError errArd{UPDATE_ERROR_OK};
THandlerFunction_Progress callback{nullptr};
public:
String md5String(void) {
// return _md5.toString();
return "";
/**
* @brief Get Arduino error code of the update.
*/
inline UpdateError getError() const {
return this->errArd;
}
void md5(uint8_t *result) {
// return _md5.getBytes(result);
/**
* @brief Get UF2OTA error code of the update.
*/
inline uf2_err_t getUF2Error() const {
return this->ctx ? this->ctx->error : this->errUf2;
}
uint8_t getError() {
return errArd;
/**
* @brief Same as end().
*/
inline void abort() {
this->end();
}
uf2_err_t getUF2Error() {
return errUf2;
/**
* @brief Check if the update process has been started.
*/
inline bool isRunning() {
return this->ctx;
}
uint16_t getErrorCode() {
return (errArd << 8) | errUf2;
/**
* @brief Check if the update process hasn't been started or has been completed.
*/
inline bool isFinished() {
return !(this->ctx && this->ctx->bytes_written != this->ctx->bytes_total);
}
void clearError() {
cleanup(UPDATE_ERROR_OK);
/**
* @brief Return complete update image size.
*/
inline size_t size() {
return this->ctx ? this->ctx->bytes_total : 0;
}
bool hasError() {
return errArd != UPDATE_ERROR_OK;
/**
* @brief Return amount of bytes already written.
*/
inline size_t progress() {
return this->ctx ? this->ctx->bytes_written : 0;
}
bool isRunning() {
return ctx != NULL;
/**
* @brief Return amount of bytes remaining to write.
*/
inline size_t remaining() {
return this->size() - this->progress();
}
bool isFinished() {
return bytesWritten == bytesTotal;
/**
* @brief Get firmware name from UF2 info.
*/
inline const char *getFirmwareName() {
if (this->ctx)
return this->ctx->info.fw_name;
return nullptr;
}
size_t size() {
return bytesTotal;
/**
* @brief Get firmware version from UF2 info.
*/
inline const char *getFirmwareVersion() {
if (this->ctx)
return this->ctx->info.fw_version;
return nullptr;
}
size_t progress() {
return bytesWritten;
/**
* @brief Get LibreTiny version from UF2 info.
*/
inline const char *getLibreTinyVersion() {
if (this->ctx)
return this->ctx->info.lt_version;
return nullptr;
}
size_t remaining() {
return bytesTotal - bytesWritten;
/**
* @brief Get target board name from UF2 info.
*/
inline const char *getBoardName() {
if (this->ctx)
return this->ctx->info.board;
return nullptr;
}
};

View File

@@ -2,26 +2,46 @@
#include "Update.h"
#include <utility>
extern "C" {
#include <fal.h>
}
static const uint8_t errorMap[] = {
UPDATE_ERROR_OK, /* UF2_ERR_OK - no error */
UPDATE_ERROR_OK, /* UF2_ERR_IGNORE - block should be ignored */
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_MAGIC - wrong magic numbers */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_FAMILY - family ID mismatched */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_NOT_HEADER - block is not a header */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_OTA_VER - unknown/invalid OTA format version */
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_OTA_WRONG - no data for current OTA scheme */
UPDATE_ERROR_NO_PARTITION, /* UF2_ERR_PART_404 - no partition with that name */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_INVALID - invalid partition info tag */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_UNSET - attempted to write without target partition */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_DATA_TOO_LONG - data too long - tags won't fit */
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_SEQ_MISMATCH - sequence number mismatched */
UPDATE_ERROR_ERASE, /* UF2_ERR_ERASE_FAILED - erasing flash failed */
UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_FAILED - writing to flash failed */
UPDATE_ERROR_WRITE /* UF2_ERR_WRITE_LENGTH - wrote fewer data than requested */
static const char *errUf2Text[] = {
nullptr, // UF2_ERR_OK (0)
nullptr, // UF2_ERR_IGNORE (1)
"Bad Magic Number", // UF2_ERR_MAGIC (2)
"Bad Family ID", // UF2_ERR_FAMILY (3)
"Missing Header", // UF2_ERR_NOT_HEADER (4)
"Old OTA Format Found", // UF2_ERR_OTA_VER (5)
"Image Not Applicable", // UF2_ERR_OTA_WRONG (6)
"Partition Not Found", // UF2_ERR_PART_404 (7)
"Partition Info Invalid", // UF2_ERR_PART_INVALID (8)
"Partition Info Missing", // UF2_ERR_PART_UNSET (9)
"Block Data Too Long", // UF2_ERR_DATA_TOO_LONG (10)
"Bad Block Sequence Number", // UF2_ERR_SEQ_MISMATCH (11)
"Flash Erase Failed", // UF2_ERR_ERASE_FAILED (12)
"Flash Write Failed", // UF2_ERR_WRITE_FAILED (13)
"Write Failed Length", // UF2_ERR_WRITE_LENGTH (14)
"Partition Write-Protected", // UF2_ERR_WRITE_PROTECT (15)
"Memory Alloc Failed", // UF2_ERR_ALLOC_FAILED (16)
};
static const char *errArdText[] = {
nullptr, // UPDATE_ERROR_OK (0)
nullptr, // UPDATE_ERROR_WRITE (1)
nullptr, // UPDATE_ERROR_ERASE (2)
nullptr, // UPDATE_ERROR_READ (3)
nullptr, // UPDATE_ERROR_SPACE (4)
"Bad Size Given", // UPDATE_ERROR_SIZE (5)
"Stream Read Timeout", // UPDATE_ERROR_STREAM (6)
"MD5 Check Failed", // UPDATE_ERROR_MD5 (7)
nullptr, // UPDATE_ERROR_MAGIC_BYTE (8)
"Could Not Activate The Firmware", // UPDATE_ERROR_ACTIVATE (9)
nullptr, // UPDATE_ERROR_NO_PARTITION (10)
"Bad Argument", // UPDATE_ERROR_BAD_ARGUMENT (11)
"Aborted", // UPDATE_ERROR_ABORT (12)
};
static char errorStr[14];
@@ -29,153 +49,14 @@ static char errorStr[14];
/**
* @brief Set the callback invoked after writing data to flash.
*/
UpdateClass &UpdateClass::onProgress(THandlerFunction_Progress callback) {
this->callback = callback;
UpdateClass &UpdateClass::onProgress(THandlerFunction_Progress handler) {
this->callback = std::move(handler);
return *this;
}
void UpdateClass::cleanup(uint8_t ardErr, uf2_err_t uf2Err) {
errUf2 = uf2Err;
errArd = ardErr;
#if LT_DEBUG_OTA
if (hasError())
printErrorContext1();
#endif
uf2_ctx_free(ctx); // NULL in constructor
ctx = NULL;
uf2_info_free(info); // NULL in constructor
info = NULL;
free(buf); // NULL in constructor
buf = bufPos = NULL;
bytesWritten = 0;
bytesTotal = 0;
}
/**
* @brief Check for UF2 errors. Set errArd and errUf2 in case of errors.
* Ignored blocks are not reported as errors.
* Abort the update.
* Use like: "if (errorUf2(...)) return false;"
* @return true if err is not OK, false otherwise
*/
bool UpdateClass::checkUf2Error(uf2_err_t err) {
if (err <= UF2_ERR_IGNORE)
return false;
cleanup(errorMap[err], err);
return true;
}
/**
* @brief Abort the update with UPDATE_ERROR_ABORT reason.
*/
void UpdateClass::abort() {
LT_DM(OTA, "Aborting update");
cleanup(UPDATE_ERROR_ABORT);
}
void UpdateClass::bufAlloc() {
if (!buf)
buf = bufPos = (uint8_t *)malloc(UF2_BLOCK_SIZE);
}
uint16_t UpdateClass::bufLeft() {
return buf + UF2_BLOCK_SIZE - bufPos;
}
uint16_t UpdateClass::bufSize() {
return bufPos - buf;
}
/**
* @brief Print string error info to the stream.
*/
void UpdateClass::printError(Print &out) {
out.println(errorString());
}
/**
* @brief Print details about the error and current OTA state.
*/
void UpdateClass::printErrorContext1() {
#if LT_DEBUG_OTA
LT_EM(OTA, "Error: %s", errorString());
if (errArd == UPDATE_ERROR_ABORT)
return;
LT_EM(OTA, "- written: %u of %u", bytesWritten, bytesTotal);
LT_EM(OTA, "- buf: size=%u, left=%u", bufSize(), bufLeft());
hexdump(buf, bufSize());
if (ctx)
LT_EM(
OTA,
"- ctx: seq=%u, part=%s",
ctx->seq - 1, // print last parsed block seq
ctx->part ? ctx->part->name : NULL
);
uf2_block_t *block = (uf2_block_t *)buf;
if (buf)
LT_EM(OTA, "- buf: seq=%u/%u, addr=%u, len=%u", block->block_seq, block->block_count, block->addr, block->len);
#endif
}
void UpdateClass::printErrorContext2(const uint8_t *data, size_t len) {
#if LT_DEBUG_OTA
LT_EM(OTA, "- while writing %u bytes", len);
if (data)
hexdump(data, len);
#endif
}
/**
* @brief Get string representation of the error in format
* "ard=..,uf2=..". Returns "" if no error.
*/
const char *UpdateClass::errorString() {
if (!errArd && !errUf2)
return "";
sprintf(errorStr, "ard=%u,uf2=%u", errArd, errUf2);
return errorStr;
}
/**
* @brief Get firmware name from UF2 info.
*/
const char *UpdateClass::getFirmwareName() {
if (info)
return info->fw_name;
return NULL;
}
/**
* @brief Get firmware version from UF2 info.
*/
const char *UpdateClass::getFirmwareVersion() {
if (info)
return info->fw_version;
return NULL;
}
/**
* @brief Get LibreTiny version from UF2 info.
*/
const char *UpdateClass::getLibreTinyVersion() {
if (info)
return info->lt_version;
return NULL;
}
/**
* @brief Get target board name from UF2 info.
*/
const char *UpdateClass::getBoardName() {
if (info)
return info->board;
return NULL;
void UpdateClass::progressHandler(UpdateClass *self) {
if (self->callback)
self->callback(self->ctx->bytes_written, self->ctx->bytes_total);
}
/** @copydoc lt_ota_can_rollback() */
@@ -187,5 +68,85 @@ bool UpdateClass::canRollBack() {
bool UpdateClass::rollBack() {
if (!lt_ota_can_rollback())
return false;
return lt_ota_switch(false);
return lt_ota_switch(/* revert= */ false);
}
/**
* @brief Get combined error code of the update.
*/
uint16_t UpdateClass::getErrorCode() const {
return (this->getError() << 8) | this->getUF2Error();
}
/**
* @brief Check if any error has occurred (incl. aborting the update).
*/
bool UpdateClass::hasError() const {
return this->getError() != UPDATE_ERROR_OK || this->getUF2Error() > UF2_ERR_IGNORE;
}
/**
* @brief Clear all errors. This is NOT recommended.
*/
void UpdateClass::clearError() {
this->errArd = UPDATE_ERROR_OK;
this->errUf2 = UF2_ERR_OK;
if (this->ctx)
this->ctx->error = UF2_ERR_OK;
}
/**
* @brief Get a textual description of the error.
*/
const char *UpdateClass::errorString() const {
uint8_t err;
if ((err = this->getUF2Error()) > UF2_ERR_IGNORE)
return errUf2Text[err];
if ((err = this->getError()) != UPDATE_ERROR_OK)
return errArdText[err];
if (!this->hasError())
return "";
sprintf(errorStr, "ard=%u,uf2=%u", this->getError(), this->getUF2Error());
return errorStr;
}
/**
* @brief Print string error info to the stream.
*/
void UpdateClass::printError(Print &out) const {
out.println(errorString());
}
/**
* @brief Print details about the error and current OTA state.
*/
void UpdateClass::printErrorContext() {
#if LT_DEBUG_OTA
if (!this->ctx)
return;
LT_EM(OTA, "Error: %s", errorString());
if (errArd == UPDATE_ERROR_ABORT)
return;
LT_EM(OTA, "- written: %u of %u", this->ctx->bytes_written, this->ctx->bytes_total);
LT_EM(
OTA,
"- buf: size=%lld, left=%lld",
this->ctx->buf_pos - this->ctx->buf,
this->ctx->buf + UF2_BLOCK_SIZE - this->ctx->buf_pos
);
hexdump(this->ctx->buf, this->ctx->buf_pos - this->ctx->buf);
if (ctx)
LT_EM(
OTA,
"- ctx: seq=%u, part=%s",
this->ctx->uf2.seq - 1, // print last parsed block seq
this->ctx->uf2.part ? this->ctx->uf2.part->name : nullptr
);
auto *block = (uf2_block_t *)this->ctx->buf;
LT_EM(OTA, "- buf: seq=%u/%u, addr=%u, len=%u", block->block_seq, block->block_count, block->addr, block->len);
#endif
}

View File

@@ -72,11 +72,11 @@ uint8_t LwIPUDP::begin(IPAddress address, uint16_t port) {
}
uint8_t LwIPUDP::begin(uint16_t p) {
return begin(IPAddress(INADDR_ANY), p);
return begin(IPAddress((uint32_t)INADDR_ANY), p);
}
uint8_t LwIPUDP::beginMulticast(IPAddress a, uint16_t p) {
if (begin(IPAddress(INADDR_ANY), p)) {
if (begin(IPAddress((uint32_t)INADDR_ANY), p)) {
if ((uint32_t)a != 0) {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = (in_addr_t)a;
@@ -111,14 +111,14 @@ void LwIPUDP::stop() {
mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip;
mreq.imr_interface.s_addr = (in_addr_t)0;
setsockopt(udp_server, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
multicast_ip = IPAddress(INADDR_ANY);
multicast_ip = IPAddress((uint32_t)INADDR_ANY);
}
close(udp_server);
udp_server = -1;
}
int LwIPUDP::beginMulticastPacket() {
if (!server_port || multicast_ip == IPAddress(INADDR_ANY))
if (!server_port || multicast_ip == IPAddress((uint32_t)INADDR_ANY))
return 0;
remote_ip = multicast_ip;
remote_port = server_port;

View File

@@ -13,6 +13,13 @@ extern "C" {
#include <lwip/netif.h>
}
#if LWIP_VERSION_SIMPLE < 20100 && defined(LWIP_NETIF_EXT_STATUS_CALLBACK)
#warning "LWIP_NETIF_EXT_STATUS_CALLBACK not available before lwIP 2.1.0"
#undef LWIP_NETIF_EXT_STATUS_CALLBACK
#endif
#if LWIP_MDNS_RESPONDER
static std::vector<char *> services_name;
static std::vector<char *> services;
static std::vector<uint8_t> protos;
@@ -81,7 +88,11 @@ static bool enableMDNS(struct netif *netif) {
err_t ret = mdns_resp_add_netif(netif, hostName, 255);
if (ret == ERR_OK) {
LT_DM(MDNS, "mDNS started on netif %u, announcing it to network", netif->num);
#if LWIP_VERSION_SIMPLE >= 20100
mdns_resp_announce(netif);
#else
#warning "lwIP version older than 2.1.0, mdns_resp_announce() unavailable"
#endif
return true;
} else {
LT_DM(MDNS, "Cannot start mDNS on netif %u; ret=%d, errno=%d", netif->num, ret, errno);
@@ -188,3 +199,5 @@ bool mDNS::addServiceTxtImpl(const char *service, uint8_t proto, const char *ite
MDNSResponder MDNS;
#endif
#endif

View File

@@ -25,6 +25,7 @@ using std::min;
#include <api/ArduinoAPI.h>
#ifdef __cplusplus
#include <LT.h>
using namespace arduino;
#endif
// Include family-specific code
@@ -42,15 +43,17 @@ using std::min;
#if defined(__cplusplus) && LT_ARD_HAS_SERIAL
#include <Serial.h>
#if HAS_SERIAL0
#if HAS_SERIAL_CLASS
#if LT_HW_UART0
extern SerialClass Serial0;
#endif
#if HAS_SERIAL1
#if LT_HW_UART1
extern SerialClass Serial1;
#endif
#if HAS_SERIAL2
#if LT_HW_UART2
extern SerialClass Serial2;
#endif
#endif
#define SerialN(x) Serial##x
#define SerialM(x) SerialN(x)

View File

@@ -1,61 +0,0 @@
/*
Copyright (c) 2015 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Empty yield() hook.
*
* This function is intended to be used by library writers to build
* libraries or sketches that supports cooperative threads.
*
* Its defined as a weak symbol and it can be redefined to implement a
* real cooperative scheduler.
*/
static void __empty() {
// Empty
}
void yield(void) __attribute__((weak, alias("__empty")));
/**
* SysTick hook
*
* This function is called from SysTick handler, before the default
* handler provided by Arduino.
*/
static int __false() {
// Return false
return 0;
}
int sysTickHook(void) __attribute__((weak, alias("__false")));
/**
* SVC hook
* PendSV hook
*
* These functions are called from SVC handler, and PensSV handler.
* Default action is halting.
*/
static void __halt() {
// Halts
while (1)
;
}
void svcHook(void) __attribute__((weak, alias("__halt")));
void pendSVHook(void) __attribute__((weak, alias("__halt")));

View File

@@ -0,0 +1,22 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-24. */
#include <Arduino.h>
#if LT_HAS_FREERTOS
__attribute__((weak)) void delay(uint32_t ms) {
vTaskDelay(pdMS_TO_TICKS(ms));
}
__attribute__((weak)) void yield() {
runPeriodicTasks();
vTaskDelay(1);
taskYIELD();
lt_wdt_feed();
}
#else
__attribute__((weak)) void yield() {}
#endif

View File

@@ -1,6 +1,6 @@
/* Copyright (c) Kuba Szczodrzyński 2022-06-20. */
#include "wiring_custom.h"
#include "wiring_private.h"
#if LT_HAS_FREERTOS
#include <FreeRTOS.h>
@@ -32,6 +32,18 @@ void runPeriodicTasks() {
#endif
}
/**
* @brief Disable modes specified by 'mask'.
*/
void pinModeRemove(pin_size_t pinNumber, uint32_t mask) {
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
return;
pinRemoveMode(pin, mask);
if (pin->enabled == PIN_NONE && mask == PIN_MODE_ALL)
pinRemoveData(pin);
}
/**
* @brief Get PinInfo struct for the specified number.
* Returns NULL if pin number is invalid.
@@ -85,20 +97,6 @@ bool pinEnabled(PinInfo *pin, uint32_t mask) {
return (pin->enabled & mask) == mask;
}
/**
* @brief Check if GPIO pin is configured as output.
*/
bool pinIsOutput(PinInfo *pin) {
return pin->mode == OUTPUT || pin->mode == OUTPUT_OPENDRAIN;
}
/**
* @brief Check if GPIO pin is configured as output.
*/
bool pinIsInput(PinInfo *pin) {
return pin->mode == INPUT || pin->mode == INPUT_PULLUP || pin->mode == INPUT_PULLDOWN;
}
/**
* @brief Read voltage from ADC and return a value between 0 and
* the current reading resolution.

View File

@@ -21,6 +21,10 @@ extern "C" {
#define PIN_SWD (1 << 10)
#define PIN_UART (1 << 11)
#define PIN_MODE_ALL 0xFFFFFFFF
typedef struct PinData_s PinData;
typedef struct {
/**
* @brief GPIO name in the family SDK.
@@ -35,9 +39,9 @@ typedef struct {
*/
uint32_t enabled;
/**
* @brief Pin mode (direction, IRQ level, etc.).
* @brief Pin data (direction, IRQ level, etc.). The structure is family-specific.
*/
uint32_t mode;
PinData *data;
} PinInfo;
extern PinInfo lt_arduino_pin_info_list[PINS_COUNT];
@@ -55,14 +59,21 @@ bool startMainTask(void);
void mainTask(const void *arg); // implemented in main.cpp
void runPeriodicTasks(); // implemented in wiring_custom.c
void pinModeRemove(pin_size_t pinNumber, uint32_t mask);
PinInfo *pinInfo(pin_size_t pinNumber);
PinInfo *pinByIndex(uint32_t index);
PinInfo *pinByGpio(uint32_t gpio);
uint32_t pinIndex(PinInfo *pin);
bool pinSupported(PinInfo *pin, uint32_t mask);
bool pinEnabled(PinInfo *pin, uint32_t mask);
bool pinIsOutput(PinInfo *pin);
bool pinIsInput(PinInfo *pin);
void pinRemoveMode(PinInfo *pin, uint32_t mask);
/**
* @brief Deinitialize the pin, by removing all enabled modes.
*/
inline void pinModeNone(pin_size_t pinNumber) {
pinModeRemove(pinNumber, PIN_MODE_ALL);
}
int analogRead(pin_size_t pinNumber);
void analogReadResolution(int res);

View File

@@ -0,0 +1,7 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-24. */
#include <Arduino.h>
void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus mode) {
attachInterruptParam(interruptNumber, (voidFuncPtrParam)callback, mode, NULL);
}

View File

@@ -0,0 +1,25 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-24. */
#include "wiring_private.h"
#if __has_include(<wiring_data.h>)
/**
* @brief Allocate and return a PinData structure (family-specific).
*/
PinData *pinData(PinInfo *pin) {
if (pin->data == NULL) {
pin->data = calloc(1, sizeof(PinData));
}
return (PinData *)pin->data;
}
/**
* @brief Deallocate the PinData structure.
*/
void pinRemoveData(PinInfo *pin) {
if (pin->data != NULL) {
free(pin->data);
}
pin->data = NULL;
}
#endif

View File

@@ -0,0 +1,64 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-24. */
#pragma once
#include <Arduino.h>
#if __has_include(<sdk_private.h>)
#include <sdk_private.h>
#endif
#if __has_include(<wiring_data.h>)
#include <wiring_data.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
PinData *pinData(PinInfo *pin);
void pinRemoveData(PinInfo *pin);
inline void pinEnable(PinInfo *pin, uint32_t mask) {
pin->enabled |= mask;
}
inline void pinDisable(PinInfo *pin, uint32_t mask) {
pin->enabled &= ~mask;
}
#define pinCheckGetInfo(pinNumber, mask, ret) \
PinInfo *pin = pinInfo(pinNumber); \
if (!pin) \
return ret; \
if (!pinSupported(pin, mask)) \
return ret;
#define pinCheckGetData(pinNumber, mask, ret) \
PinInfo *pin = pinInfo(pinNumber); \
if (!pin) \
return ret; \
if (!pinSupported(pin, mask)) \
return ret; \
PinData *data = pinData(pin);
#define pinIsOutput(pin, data) (pinEnabled(pin, PIN_GPIO) && (data->gpioMode ^ 0b101) < 5)
#define pinIsInput(pin, data) (pinEnabled(pin, PIN_GPIO) && (data->gpioMode ^ 0b101) > 4)
#define pinSetOutputPull(pin, data, pinNumber, status) \
do { \
if (!pinIsOutput(pin, data)) { \
pinMode(pinNumber, INPUT_PULLDOWN ^ !!status); \
return; \
} \
} while (0);
#define pinSetInputMode(pin, data, pinNumber) \
do { \
if (!pinIsInput(pin, data)) \
pinMode(pinNumber, INPUT); \
} while (0);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,58 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "lt_cpu.h"
#if LT_HAS_FREERTOS
#include <FreeRTOS.h>
#include <task.h>
#endif
lt_cpu_family_t lt_cpu_get_family() {
return FAMILY;
}
const char *lt_cpu_get_family_name() {
return STRINGIFY_MACRO(FAMILY) + 2;
}
__attribute__((weak)) lt_cpu_model_t lt_cpu_get_model() {
return MCU;
}
const char *lt_cpu_get_model_name() {
return STRINGIFY_MACRO(MCU);
}
const char *lt_cpu_get_model_code() {
return STRINGIFY_MACRO(MCULC);
}
__attribute__((weak)) uint32_t lt_cpu_get_unique_id() {
return lt_cpu_get_mac_id();
}
__attribute__((weak)) uint32_t lt_cpu_get_mac_id() {
uint8_t mac[6];
lt_get_device_mac(mac);
return (mac[3] << 0) | (mac[4] << 8) | (mac[5] << 16);
}
__attribute__((weak)) uint8_t lt_cpu_get_core_count() {
return 1;
}
#if LT_HAS_FREERTOS
__attribute__((weak)) uint32_t lt_cpu_get_freq() {
return configCPU_CLOCK_HZ;
}
#endif
uint32_t lt_cpu_get_freq_mhz() {
return lt_cpu_get_freq() / 1000000;
}
#if LT_HAS_FREERTOS
__attribute__((weak)) uint32_t lt_cpu_get_cycle_count() {
return xTaskGetTickCount() * (configCPU_CLOCK_HZ / configTICK_RATE_HZ);
}
#endif

View File

@@ -38,6 +38,7 @@ uint32_t lt_cpu_get_unique_id();
/**
* @brief Get CPU ID based on the last three octets of MAC address.
* Note: the number is 24-bit (with the MSB being zero).
* The 3rd-to-last octet is least-significant, the last octet is most-significant.
*/
uint32_t lt_cpu_get_mac_id();

View File

@@ -0,0 +1,80 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "lt_device.h"
static char *device_name = NULL;
const char *lt_get_version() {
return LT_VERSION_STR;
}
const char *lt_get_board_code() {
return LT_BOARD_STR;
}
const char *lt_get_device_name() {
if (device_name)
return device_name;
uint32_t chip_id = lt_cpu_get_mac_id();
uint8_t *id = (uint8_t *)&chip_id;
const char *model = lt_cpu_get_model_code();
uint8_t model_len = strlen(model);
device_name = (char *)malloc(3 + model_len + 1 + 6 + 1);
sprintf(device_name, "LT-%s-%02x%02x%02x", model, id[0], id[1], id[2]);
return device_name;
}
__attribute__((weak)) void lt_reboot() {
// The Watchdog Way
lt_wdt_enable(1L);
while (1) {}
}
__attribute__((weak)) bool lt_reboot_wdt() {
if (!lt_wdt_enable(1L))
return false;
while (1) {}
}
__attribute__((weak)) bool lt_reboot_download_mode() {
return false;
}
__attribute__((weak)) lt_reboot_reason_t lt_get_reboot_reason() {
return REBOOT_REASON_UNKNOWN;
}
const char *lt_get_reboot_reason_name(lt_reboot_reason_t reason) {
if (!reason)
reason = lt_get_reboot_reason();
switch (reason) {
case REBOOT_REASON_POWER:
return "Power-On";
case REBOOT_REASON_BROWNOUT:
return "Brownout";
case REBOOT_REASON_HARDWARE:
return "HW Reboot";
case REBOOT_REASON_SOFTWARE:
return "SW Reboot";
case REBOOT_REASON_WATCHDOG:
return "WDT Reset";
case REBOOT_REASON_CRASH:
return "Crash";
case REBOOT_REASON_SLEEP:
return "Sleep Wakeup";
case REBOOT_REASON_DEBUGGER:
return "Debugger";
default:
return "Unknown";
}
}
__attribute__((weak)) bool lt_set_debug_mode(lt_debug_mode_t mode) {
return false;
}
__attribute__((weak)) void lt_gpio_recover() {
lt_set_debug_mode(DEBUG_MODE_OFF);
}

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