Add Silicon Labs EFM32GG11 chip family + WF200 Wi-Fi backend + dual-bank OTA (WGM160P)#387
Draft
RAR wants to merge 35 commits into
Draft
Add Silicon Labs EFM32GG11 chip family + WF200 Wi-Fi backend + dual-bank OTA (WGM160P)#387RAR wants to merge 35 commits into
RAR wants to merge 35 commits into
Conversation
New LibreTiny chip family for the Silicon Labs EFM32GG11B820 (Cortex-M4F), targeting the WGM160P SiP. Adds the family to families.json/platform.json (framework-silabs-gecko-sdk package), generic + WGM160P board definitions, the SCons family builder, EFM32GG11B820 linker script, LibreTiny base API (lt_cpu/device/flash/mem/ota/sleep/wdt/init/fault), FreeRTOS config, printf backend, FlashDB FAL port, syscalls, and the Arduino core (wiring, GPIO, EXTI, HardwareSerial). framework=base compiles and Arduino blink + Serial run on a WGM160P (bench-verified on a JuiceBox 40).
Wi-Fi support for the WGM160P's on-package WF200 over internal SDIO. Adds a from-scratch Arasan SDHCI PIO host driver (lt_sdio), the sl_wfx FMAC host port (reset/PDS/firmware feed, bus + event tasks, cmd52/53), the WF200<->lwIP netif glue, lwIP config, and the LibreTiny-style WiFi library (WiFiSTA/Scan/Generic/Events + HTTPClient). The board PDS is pulled from the GSDK package (brd4321a), not vendored into the tree. WiFi.begin() -> WL_CONNECTED -> DHCP and an HTTPClient GET work on a bench JuiceBox 40.
The WGM160P's WF200 is OTP-provisioned in enforced Secure Link mode, so plaintext host messages are rejected. Adds the host crypto hooks (sl_wfx_host_crypto.c) as an independent mbedtls implementation written from the public Apache-2.0 hook API (sl_wfx_host_api.h) and the SecureLink wire protocol — X25519 ECDH, HMAC-SHA512 public-key authentication, a SHA-256 session key, and AES-128-CCM records. A GG11 TRNG0 entropy source backs mbedtls, and a FreeRTOS task drives session-key renegotiation. The pub-key exchange indication is stashed when it arrives before the driver arms its wait, without which sl_wfx_init() intermittently times out. Bench: WiFi.begin() -> WL_CONNECTED -> DHCP and HTTPClient GET work deterministically over enforced Secure Link.
…CP server) Second lwIP netif (AP) beside STA on the single WF200 radio: interface-demuxed RX, per-interface TX, modePriv AP enable/disable, full WiFiAP API, AP client-event plumbing. DHCP for AP clients reuses the GSDK's Apache-2.0 dhcp_server.c (consumed unmodified from framework-silabs-gecko-sdk) via thin MIT glue + a compat shim. lwIP httpd enabled for the concurrent-server demo. Bench-confirmed on a JuiceBox 40: client on the AP gets a 192.168.4.x lease and loads an on-device page while STA is online (served on both netifs at once).
…lects working WiFi+softAP The generic SLWSTK6121A board mapped WF200 to SPI but the backend is SDIO-only and the board is untested (no hardware), so it's removed for now; wgm160p-juicebox-40 (bench-proven) is the sole board. README updated: WiFi STA + softAP are working; WF200 SDIO pin map corrected to PE8-PE13 LOC0 to match lt_sdio.c and the board JSON.
With LT_REMALLOC, __wrap_realloc passed new_size straight to pvPortMalloc(). realloc(ptr, 0) (e.g. Mongoose's mbuf_trim of an emptied recv buffer) then requests 0 bytes; heap_4 returns NULL, and with configUSE_MALLOC_FAILED_HOOK any NULL return fires the hook — on ports whose hook resets the SoC, a legal shrink-to-zero becomes a reboot loop. Factor the malloc+copy+free path into lt_remalloc(): free and return NULL when new_size==0, and skip the memcpy when the old pointer is NULL (realloc-as-malloc was reading from address 0). Affects every LT_REMALLOC family, not just silabs-efm32gg11.
AddCoreSources() only globs a fixed subdir set (api/, common/, port/, wiring/, ...) — base/ota/ is not among them, so lt_ota_meta.c and lt_crc32.c never reached the family core. lt_ota.c's references to lt_ota_meta_bank_valid only linked while every caller was gc-sections'd out; the first sketch to actually call lt_ota_switch() failed to link. Register base/ota/ explicitly (excluding the host-only lt_meta_hostshim.c).
…section) EFM32GG11 is Series 1, which emlib's em_msc.h does NOT include in its RAMFUNC allowlist (only Series 0/2 or an explicit EM_MSC_RUN_FROM_RAM). So MSC_LoadWriteData ran from flash, and a sustained flash write (e.g. staging a multi-KB OTA image into the inactive bank) hung mid-transfer inside the write loop — bench-confirmed the CPU halted in MSC_LoadWriteData at ~3s into an 85 KB FAL write. Define EM_MSC_RUN_FROM_RAM and add a .ram output section (VMA in RAM, LMA in flash) with a startup copy-table entry so the MSC routines are copied to and executed from RAM. _lt_image_size now spans .ram (the last flash-LMA section) so the OTA image-length symbol stays exact. Single 4-byte writes (Task 3 bench) worked from flash by luck of prefetch; this makes all flash writes robust.
The FSBL rewrites the OTA metadata page on every trial boot (attempt countdown / revert). Like the app, its emlib MSC write must execute from RAM on this Series-1 part or risk hanging mid-write. Add a .ram section + copy-table entry to bootloader.ld and define EM_MSC_RUN_FROM_RAM in the bootloader's standalone compile, so rollback can't stall on a flash-resident write loop.
…clock) lt_wdt_enable() armed the WDOG peripheral but never gave it a running clock: WDOG_INIT_DEFAULT selects LFRCO (32 kHz), which lt_init never enables, and the WDOG register clock (cmuClock_WDOG0) was never gated on — so WDOGn_Init writes were dropped and the counter never ticked. The watchdog therefore never reset the chip, which silently breaks OTA auto-rollback (an unhealthy trial image is supposed to hang and let the watchdog reset it so the bootloader can revert). Enable cmuClock_WDOG0 and select the always-on ULFRCO (~1 kHz), which also matches the existing ms->period mapping. Bench-found while testing OTA T8 rollback.
Add ltWifiStatusLedEnable(bool) so an application can take the RGB status LED away from the WiFi backend at runtime — needed by the openevse port, where LED_R/G/B are the charge-state indicator. When disabled, ltWifiStatusLed() touches no GPIO, leaving the pins for the app to drive; re-enabling re-inits them as outputs. No-op on boards without LED_R/LED_G/LED_B.
Add ltWifiStatusLedEnable(bool) so an application can take the RGB status LED away from the WiFi backend at runtime — needed by the openevse port, where LED_R/G/B are the charge-state indicator. When disabled, ltWifiStatusLed() touches no GPIO, leaving the pins for the app to drive; re-enabling re-inits them as outputs. No-op on boards without LED_R/LED_G/LED_B.
Shrink both OTA banks from 0x0F8000 to 0x0F0000 (960 KB) and add a 32 KB `kvs` partition at 0x1F0000+0x8000, between bank B and the OTA metadata. It sits outside both banks so it survives an OTA, and the matching FAL partition (FAL_PART_TABLE_ITEM(kvs, KVS) + FLASH_KVS_OFFSET/LENGTH) is generated from the board flash map by builder/utils/flash.py. Keeps the OTA bank constants, both linker defaults, and the bank-B re-link defsym in sync. Backs FlashDB (the LibreTiny-native KV store); LibreTiny ships only the IPreferences interface, no concrete Preferences. Flash map: boot 0x0/32K, ota1 0x008000/960K, ota2 0x100000/960K, kvs 0x1F0000/32K, meta 0x1F8000/32K (8K used). Bank A's freed tail 0x0F8000..0x100000 is left unused to keep both banks equal size. Build-verified: KVDB inits on the kvs partition; both banks link at the correct bases (entry delta 0xF8000); 91 KB app fits the 960 KB bank.
Bench-found: with the kvs partition in place, FlashDB formatted and wrote KVs but they never survived a reboot — the partition reformatted every boot. Root cause: FDB_WRITE_GRAN defaulted to 8 (STM32F2/F4 byte granularity), which packs KV status markers as sub-word bytes. EFM32GG11 internal flash is word-only (emlib MSC writes 4-byte words; the FAL port rejects sub-word offsets), so the markers corrupted. Make FDB_WRITE_GRAN overridable in the common fdb_cfg.h (#ifndef guard, default 8 unchanged for byte-writable SPI-flash families) and set 32 in the silabs builder — the stm32f1-class word-write value the comment names. Bench-verified on the JuiceBox WGM160P: a boot counter in the kvs partition now persists across power cycles (0->1->2->3 over 3 reboots).
WiFiGeneric::modePriv hardcoded WFM_MGMT_FRAME_PROTECTION_OPTIONAL in the START_AP command. PMF (802.11w) has no key hierarchy to protect on an OPEN BSS, so the WF200 rejects START_AP with 0x21 (INVALID_PARAMETER) and the AP never starts. Gate it on the security mode: OPTIONAL on a WPA2 AP, DISABLED on an open one. Bench-validated on the JuiceBox WGM160P: open softAP() now returns success (START_AP 0x00, IP 192.168.4.1) where it previously failed 0x21.
…e-free Bug-2 diagnostic, gated behind -DLT_WFX_RX_TRACE=1 (stock build byte-identical). The 'pbuf_free: p->ref > 0' double-free only reproduces with a client associated to the softAP generating RX traffic concurrent with scanNetworks() (bench-proven: scan + softAP with no client is crash-free, STA-only scan is crash-free). Two probes: - wf200_netif.c lt_wf200_netif_input(): bound frame_length to a max Ethernet frame; record + drop a bogus length instead of letting pbuf_take() write OOB and corrupt a neighbour pbuf's ref (hypothesis 2). Per-interface RX counters. - WiFiScan.cpp scanNetworks(): log the calling task + RX counters. task='tcpip' means the scan is driven from the lwIP thread (hypothesis 1). Probe validated on the JuiceBox bench (emits 'scan: ctx task=... rx_ap=...'). Hand to the openevse bench (which has a client) to settle the root cause.
The WF200 FMAC has no autonomous rejoin, so the backend now supervises it: a STA link that reached GOT_IP and then drops is rejoined automatically, instead of requiring the app to poll WiFi.status() and call reconnect() itself. setAutoReconnect()/getAutoReconnect() are now functional (was a no-op returning false); default ON to match the ESP32 Arduino core. Implementation (no new task, no new RAM): the existing WiFi event task gets a 1 s receive timeout and runs a supervisor tick on idle. When armed + enabled + the link is lost (CONNECTION_LOST / CONNECT_FAILED / DISCONNECTED), it fires a rate-limited (2 s -> 30 s backoff) NON-blocking join and lets the normal CONNECT -> GOT_IP ladder update status. Armed on GOT_IP (and static-IP link-up); disarmed on a fresh begin(), intentional disconnect(), or STA disable -- so a never-succeeded join (e.g. wrong password) is never looped; that stays the caller's to handle via begin()'s return. Fills a real gap for the openevse-lite consumer, which has no dropped-link watcher once STA-connected (its only retry path is softAP-mode-only).
…lags __wrap_malloc routes ALL allocations (Arduino String, ArduinoJson, mongoose mbufs, lwIP pbufs) to the single heap_4 ucHeap[], so a heap-hungry app (e.g. serving a ~21-26 KB gzipped web UI) exhausts the 64 KB default and crashes (malloc-failed hook / lwIP OOM -> corrupted callback in ucHeap -> HardFault) while ~350 KB of the part's 512 KB SRAM sits idle. Wrap the define in #ifndef so firmware can size it via -DconfigTOTAL_HEAP_SIZE=N. ucHeap is a static BSS array, so a larger value just grows BSS (no linker change). Default unchanged (64 KB). Verified: -DconfigTOTAL_HEAP_SIZE=262144 links at RAM 68.7% (360 KB), ~164 KB clearance below the top-of-RAM stack; no linker overflow.
Opt-in SNTP for firmware that drives it (sntp_init/setservername/ setoperatingmode) and provides the time-set hook. lwipopts.h: SNTP_SUPPORT + SNTP_SERVER_DNS (LWIP_DNS already on) + SNTP_SET_SYSTEM_TIME -> a firmware hook (lite_sntp_set_system_time, declared here). Builder: add lwip/src/apps/sntp/sntp.c to the lwIP source list (header was already on the include path). No behaviour change for apps that don't use it: sntp.c.o is built into liblwip.a but only linked when something references sntp_*. Verified: a sketch that defines the hook + calls sntp_init() links clean with sntp.c.o produced; a non-SNTP example links unchanged (no undefined lite_sntp_set_system_time).
…rollback) Brings the bench-verified OTA core onto the libretiny-eu#387 family branch: custom first-stage bootloader, ping-pong metadata, dual-bank app linker layout, real FAL/MSC flash driver, and lt_ota_confirm(). OTA core verified on the real JuiceBox WGM160P over SWD (FAL flash, bank-A boot, A/B switch, auto-rollback, healthy-confirm).
erase() returned (end - start) with start page-aligned DOWN, so a
page-aligned 256-byte first write reported only 256 B erased when the
whole 4 KB page was blank. uf2ota records {erased_offset=offset,
erased_length=ret} and skips re-erase only inside that window, so every
later 256 B block in the same page failed the check and re-erased the
page -- wiping the blocks already written. Net effect: a staged OTA bank
ended up mostly 0xFF (only the final page survived), the metadata CRC was
computed over that garbage, and the bootloader jumped into an empty bank
and hard-faulted.
Round the erased extent up to a full page and return (end_pages - offset),
matching the realtek/beken/ln882h ports (ceil(size/page)*page). Bench:
on-device Update OTA now writes bank B byte-exact (CRC matches the source
image) and boots it. Found via the first end-to-end on-device OTA test.
The dual-bank OTA engine (lt_ota + uf2ota + FAL + bootloader switch) was proven, but the standard Arduino Update path was never wired for this family: - LT_HAS_OTA was undefined, so Update::begin() short-circuited with 'OTA is not yet supported on this chip!'. Define it 1. - Update.cpp references MD5Init/Update/Final unconditionally, but neither LT_ARD_MD5_MBEDTLS nor _HOSTAPD was set, so they were undefined at link. Enable MD5 via the GSDK mbedtls already compiled for the WFX Secure Link: MBEDTLS_MD5_C + library/md5.c + LT_ARD_MD5_MBEDTLS=1. Independent of LT_HAS_MBEDTLS (no full TLS stack). - Replace the Phase-1 firmware.uf2 stub with a real dual-bank packer that drives 'ltchiptool uf2 write' (EFM32GG11 SocInterface, OTAType.DUAL): DUAL_1 -> ota1 (bank A), DUAL_2 -> ota2 (bank B, diff32 binpatch). Emits only firmware.uf2 so the raw firmware.bin stays the SWD/commander image. The SocInterface lives in ltchiptool; until a release carrying it is installed, 'uf2 write' raises NotImplementedError, so fall back to the raw-image stub (build stays green; OTA needs the SoC-enabled ltchiptool). Bench: full on-device OTA verified -- applier (bank A) applies an embedded .uf2 via Update.begin/write/end, bootloader switches to bank B, the new image runs and lt_ota_confirm()s; bank B persists across reset.
…F2 enablement) Brings the two follow-up commits from the OTA branch onto libretiny-eu#387: - FAL erase() return-value fix (uf2ota multi-block data loss) - Arduino Update/OTA UF2 path (LT_HAS_OTA, mbedtls MD5, dual-bank UF2 packer with stub fallback when ltchiptool lacks the EFM32GG11 SoC) Bench-verified end-to-end on the JuiceBox WGM160P.
lt_ota_confirm() unconditionally rewrote the metadata page (a 4 KB MSC erase+write) on every call, including boots where there is nothing to confirm -- e.g. just after an SWD flash that erased the meta region, where the metadata is blank and the bootloader already defaults to bank A. That stray erase+write can stall the core long enough to disrupt the live WF200 SDIO link (observed downstream: an OTA build with a self-confirm hook never came up on WiFi after a fresh flash; removing the confirm call fixed it). Bail out without touching flash unless the running bank is genuinely an unconfirmed TRIAL: skip when metadata is blank/invalid, and skip when the bank is already CONFIRMED. Confirm now writes flash at most once -- on the actual post-OTA trial boot -- so apps can call it unconditionally on every boot (after a health check) without perturbing WiFi on normal boots.
Carries the confirm fix from the OTA branch onto libretiny-eu#387: lt_ota_confirm() now no-ops without touching flash unless the running bank is an unconfirmed TRIAL, so calling it on every boot no longer does a stray metadata erase+write that can disrupt the WF200 link. Bench-verified: confirm on blank meta (post-SWD-flash) leaves the metadata untouched.
The WGM160P host drives the ATmega328P EVSE-controller RESET (active-low, continuity-confirmed 2026-06-13) on PF11 — nominally USB_DP (module pin 42), repurposed on the JuiceBox since USB is unused. Add it to the board JSON pinout and expose an EVSE_RESET macro (encoded 0x5B) in the variant; raise PINS_GPIO_MAX to 0x5B so it is addressable via pinMode/digitalWrite. Also drop the PB3 pinout entry: bench-verified to drive nothing visible, so it is not a meaningful board pin.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add Silicon Labs EFM32GG11 chip family + WF200 Wi-Fi backend + dual-bank OTA (WGM160P)
Summary
Adds a new chip family,
silabs-efm32gg11, for the Silicon LabsEFM32GG11B820 (Cortex-M4F) — together with a Wi-Fi backend for the on-package
WF200 radio (including softAP) and a dual-bank OTA scheme behind the
standard Arduino
UpdateAPI. The target is the WGM160P SiP (EFM32GG11host + WF200), e.g. as used in the Enel X JuiceBox 40.
What's in it
Grouped into six logical pieces (~34 commits):
families.json/platform.json(
framework-silabs-gecko-sdkpackage), WGM160P board def, SCons familybuilder, EFM32GG11B820 linker script, LibreTiny base API
(cpu/device/flash/mem/ota/sleep/wdt/init/fault), FreeRTOS, printf, FlashDB
FAL port, syscalls, and the Arduino core (wiring/GPIO/EXTI/HardwareSerial).
the
sl_wfxFMAC host port (reset/PDS/firmware feed, bus + event tasks,cmd52/53), WF200↔lwIP netif glue, lwIP config, and a LibreTiny-style WiFi
library (STA/Scan/Generic/Events + HTTPClient).
Secure Link mode, so plaintext is rejected. Adds the host crypto hooks
(curve25519 ECDH + AES-CCM via software mbedtls 3.5.0), a GG11 TRNG0 entropy
source, and the FreeRTOS renegotiation task.
lwIP netif beside the STA one, interface-demuxed RX / per-interface TX,
modePrivAP enable/disable, the fullWiFiAPAPI, and AP client-eventplumbing. AP clients get DHCP from the GSDK's own Apache-2.0
dhcp_server.c(consumed unmodified from the framework package via thin MIT glue).
UpdateAPI,with a custom MIT first-stage bootloader and auto-rollback:
bootloader/, original MIT) selects theboot bank from ping-pong metadata, validates the image CRC, and reverts to
the last-good bank if a trial image fails to confirm within N boots.
lt_ota_meta.{c,h}, common) with CRC32,plus a real FAL flash driver over emlib MSC (erase/write/read), run
from RAM (
EM_MSC_RUN_FROM_RAM) so the metadata write survives.0x008000, bank B @0x100000) anda dual-bank
.uf2artifact;Update.begin/write/endstages the inactivebank and arms it as a TRIAL.
lt_ota_confirm()(common weak no-op + silabs impl, idempotent) commits atrial image; the watchdog (now functional — ULFRCO + WDOG bus clock)
backstops a hung trial boot. Host unit tests cover CRC32 + metadata
ping-pong.
wgm160p-slwstk6121aboard(see below); README reflects the working Wi-Fi + softAP + OTA.
Boards
wgm160p-juicebox-40— Enel X JuiceBox 40 variant (RGB/pinmap). All benchtesting below was done on this board — the only WGM160P hardware I have.
(An earlier revision carried a generic
wgm160p-slwstk6121aStarter-Kit board,but it's removed: its WF200 host bus is SPI-strapped while this backend is
SDIO-only, and I can't verify it without the hardware. Happy to add a generic
board back once someone with a Starter Kit can confirm the strap + pinout.)
The OTA flash map (in
boards/_base/silabs-efm32gg11.json):Framework packages
Two SDK packages are referenced via
platform.json, following the existingframework-*pattern (separate repos, not vendored into this tree):framework-silabs-gecko-sdk(Gecko SDK 4.5.0 — emlib, lwIP, FreeRTOS port,mbedtls).
framework-wfx-fmac-driver(SiliconLabs wfx-fullMAC-driver 3.7.0, Apache-2.0,incl. the WF200 firmware blob — Silicon Labs limited-redistribution license).
Testing (bench, real WGM160P in a JuiceBox 40 — the only board I have)
framework=basecompiles; Arduino blink +Serialrun.attachInterrupt, multi-task FreeRTOS verified.WiFi.begin()→WL_CONNECTED→ DHCP lease;HTTPClientGETcompletes (full response body) over enforced Secure Link.
AP_CLIENT_CONNECTED_IND,station count) → client gets a DHCP lease (
192.168.4.x, gw192.168.4.1).home network as STA, a client on the device's AP loaded a page served by the
on-device lwIP
httpd— and the same page was reachable from the home LAN onthe STA address at the same time (httpd served on both netifs concurrently).
Updatestreamed a dual-bank.uf2→inactive bank written byte-exact (CRC matched the source image) → first-stage
bootloader switched banks → new image booted →
lt_ota_confirm()committedthe trial → choice persisted across reset. Auto-rollback verified by skipping
the confirm (bootloader reverts to the previous bank). Host CRC32 + metadata
ping-pong unit tests pass.
Notes / known limitations
(libretiny-eu/ltchiptool#93,
pending). Until it ships, the build emits a raw
firmware.uf2(notOTA-applicable) and prints a fallback notice; SWD/Commander flashing of
firmware.binis unaffected. With the patched ltchiptool the build emits areal dual-bank
.uf2.and auto-reverts unless
lt_ota_confirm()is called once the app is healthy.This is by design (auto-rollback), documented in the OTA API.
init); sufficient for the Secure Link key exchange and OTA image hashing.
AP follows its channel) is bench-proven; the late-connect edge (AP up before
STA joins on a different channel) is a documented limitation, with a
restart-AP-on-
CONNECT_INDfallback designed but not yet implemented.softAPdisconnect()first (documented inWiFiAP.cpp).Heads-up for reviewers (deliberate deviations)
can't model EFM32 port-aware pins (PA4/PB4/PD4 all collapse to "4") and drops
non-D0/A0 Arduino labels, so it emits a degenerate variant. The hand-written
variant header documents this; happy to file/track the upstream boardgen gap.
rather than the
library-freertos/library-freertos-portsplit — cleanerfor a SiP that already vendors the whole GSDK, but a deviation from the guide.
is no Silabs SoC module in ltchiptool yet, so initial provisioning goes
through SWD (OpenOCD/J-Link/Commander) and the dual-bank
.uf2packer is thenew ltchiptool SoC plugin (PR Unable to Flash to Certain Modules #93 above).
firmware.binstays the raw SWDimage;
firmware.uf2is the OTA artifact.Provenance / licensing
This PR contains no vendored SDK source — every file in the tree is original
work under LibreTiny's MIT license. The SDK proper (driver, mbedtls, lwIP,
FreeRTOS, emlib, the WF200 firmware blob, the board PDS, and the AP DHCP server)
is not in this PR — it is consumed from the
framework-*packages above. Inparticular:
sl_wfx_host.c,lt_sdio.c, lwIP glue, WiFi library) isoriginal work written against the Apache-2.0 driver API.
lt_sdio.cisfrom-scratch; only hardware register values (facts) were referenced from any
SiLabs material, no code.
sl_wfx_host_crypto.c(the Secure Link hooks) is an independent mbedtlsimplementation written from the public Apache-2.0 hook API and the wire
protocol — not derived from the Gecko SDK's MSLA-licensed example code.
dual-bank
lt_otaare all original MIT work; no Gecko Bootloader code isused or vendored.
dhcp_server.c, compiledunmodified from the
framework-silabs-gecko-sdkpackage; LibreTiny addsonly MIT glue (
lt_dhcpserver.c) and a small compat shim. No DHCP-serversource is vendored into this tree.
sl_wfx_pds.h, brd4321a) is pulled from theframework-silabs-gecko-sdkpackage via an include path, not copied here.Checklist
clang-format(v19) cleanautoflake/black/isort --profile blackclean