Skip to content

feat: S28.09 FreeRtosLwip LAN9118 netif + NO_SYS=0 UDP on QEMU#476

Merged
DavidCozens merged 6 commits into
mainfrom
feat/s28-09-freertoslwip-netif-udp
May 29, 2026
Merged

feat: S28.09 FreeRtosLwip LAN9118 netif + NO_SYS=0 UDP on QEMU#476
DavidCozens merged 6 commits into
mainfrom
feat/s28-09-freertoslwip-netif-udp

Conversation

@DavidCozens

@DavidCozens DavidCozens commented May 29, 2026

Copy link
Copy Markdown
Owner

Purpose

S28.09 (#472, epic E28) gives the FreeRtosLwip BDD target a working
network stack: a hand-written lwIP netif over Arm's smsc9220 LAN9118
driver, lwIP in NO_SYS=0 (tcpip-thread) mode, and a UDP logging
pipeline proven end-to-end against the syslog-ng oracle on QEMU.

Closes #472.

Change Description

  • netif + driver. Vendored Arm smsc9220 LAN9118 driver under
    Bdd/Targets/FreeRtosLwip/netif/smsc9220/ (Apache-2.0 headers kept),
    isolated from analyze-format with a DisableFormat .clang-format.
    Hand-written netif glue netif/EthernetIf.c (RX task driven by IRQ-13
    EthernetISR, pbuf TX/RX, etharp_output).
  • NO_SYS=0. lwipopts.h runs the tcpip thread + contrib sys_arch.c;
    FreeRTOSConfig.h mirrors the requirements; new
    solidsyslog_user_tunables.h (MAX_MESSAGE_SIZE 512, FreeRtosMutex pool 2)
    flows in via the freertos-cross-lwip preset.
  • Pipeline. main.c calls tcpip_init pre-scheduler; netif bring-up
    runs from the interactive task via tcpip_callback (smsc9220_init calls
    vTaskDelay, so it must run post-scheduler). lwIP-touching adapter ops
    are marshalled through a tcpip_callback_with_block shim installed with
    S28.06's SolidSyslogLwipRaw_SetMarshal. UDP path = UdpSender +
    CircularBuffer + FreeRtosMutex + Service task + Meta/TimeQuality/Origin
    SD. SwitchingSender TCP/TLS slots are wired to NullSender (UDP only
    this story; TCP is S28.10).
  • First-packet ARP drop fixed. LwipRawDatagram sends zero-copy
    (PBUF_REF over a transient buffer), so an ARP-miss first send had its
    queued copy reference freed memory (seqId=1 lost, seqId=2 delivered).
    Fix is WarmUpGatewayArp: etharp_request(gw) at bring-up + poll
    etharp_find_addr (marshalled off the tcpip thread) until resolved
    before the pipeline goes live — keeps the zero-copy contract and pushes
    the one-time resolution cost to setup. pcap now shows gratuitous ARP →
    gw ARP request → reply → UDP seqId=1 → seqId=2 in order.
  • CI. Advisory bdd-freertos-qemu-lwip lane + syslog-ng/behave
    compose pair (UDP-only tag filter, @freertoslwipwip per-scenario
    escape hatch); build-freertos-target-lwip uploads the
    bdd-tunables-freertos-lwip artifact. NOT in summary.needs
    advisory until S28.11 promotes it.

Test Evidence

  • Oracle BDD pair run in WSL (no docker-in-docker in the freertos-target
    container):
    docker compose -f ci/docker-compose.bdd.yml up --abort-on-container-exit --exit-code-from behave-freertos-lwip behave-freertos-lwip syslog-ng-freertos-lwip
    8 features passed, 0 failed; 25 scenarios passed, 0 failed; 96 steps
    passed, 0 failed
    (24 scenarios skipped — the TCP/TLS/mTLS/store
    features excluded by the UDP-only tag filter). Delivery truth is the
    syslog-ng oracle's "receives a message with priority/hostname/PROCID"
    assertions, not an ad-hoc host listener (slirp does not reliably
    deliver the NATed datagram to host loopback even though it is correct
    on the wire).
  • Cross build clean under the strict bar; arm-none-eabi-nm confirms
    zero PlusTcp symbols linked into the lwIP ELF.
  • Tree-wide clang-format --dry-run --Werror (clang-format 19.1.7) clean.

Areas Affected

  • Bdd/Targets/FreeRtosLwip/ (Tier 3 best-effort BDD target) — new netif,
    driver, pipeline, tunables, presets.
  • .github/workflows/ci.yml, ci/docker-compose.bdd.yml — advisory lwIP
    lane + compose pair.
  • Top-level CMakeLists.txt / CMakePresets.jsonfreertos-cross-lwip
    wiring. No Tier-1/2 production source (Core/, Platform/) touched.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Full runtime support for FreeRTOS + lwIP on QEMU (Cortex‑M3) with Ethernet networking and UDP syslog over the SMSC/LAN9118 controller
    • Tunable defaults adjusted (max syslog message size reduced to 512, mutex pool sizing)
  • Bug Fixes

    • Resolved first‑packet UDP loss by adding ARP warm‑up before UDP logging start
  • CI/Testing Improvements

    • Added cross‑build artifacting and a Docker Compose BDD lane for lwIP with JUnit reporting
  • Documentation

    • Updated target README and developer log with run instructions and design notes

Review Change Stack

DavidCozens and others added 4 commits May 29, 2026 15:31
Grow the S28.07 link-probe into a runnable FreeRTOS+lwIP BDD target on QEMU
mps2-an385: a hand-written LAN9118 lwIP netif (netif/EthernetIf.c) over the
vendored Arm smsc9220 driver (Apache-2.0), tcpip_init + static IP bring-up on
the tcpip thread, and the SolidSyslog UDP pipeline wired through the
tcpip_callback marshal (S28.06). Verified on QEMU: boot, ARP, and a correct
RFC 5424 datagram to 10.0.2.2:5514, clean teardown, no PlusTcp symbols.

WIP: oracle BDD run + CI lanes still to come.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the syslog-ng-freertos-lwip / behave-freertos-lwip compose pair (mirrors
the +TCP pair, points at the lwIP ELF, UDP-only tag filter with a
@freertoslwipwip escape hatch) and the advisory bdd-freertos-qemu-lwip CI job
(not in summary.needs). Grow build-freertos-target-lwip to upload the BDD
tunables mirror; refresh the link-probe wording to BDD target across ci.yml,
the top-level CMake status, and the README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rmat

The LwipRaw datagram sends PBUF_REF packets pointing at a transient buffer, so
a first send that hit an ARP miss had its queued copy reference freed memory
and was dropped (confirmed on the wire: seqId=1 lost, seqId=2 delivered).
Warm up the gateway ARP at bring-up (etharp_request) and block the interactive
task — never the tcpip thread — until the cache populates before the pipeline
goes live. Verified: both seqId=1 and seqId=2 now reach the wire.

Also move the vendored Arm smsc9220 driver into netif/smsc9220/ behind a
DisableFormat .clang-format so the analyze-format lane does not reflow
third-party code, and clang-format the hand-written netif / main / config
files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eec8c7f1-7849-4b45-b4be-0dd37b19e3a1

📥 Commits

Reviewing files that changed from the base of the PR and between a456b2a and be7a7c3.

📒 Files selected for processing (4)
  • Bdd/Targets/FreeRtosLwip/README.md
  • Bdd/Targets/FreeRtosLwip/main.c
  • Bdd/Targets/FreeRtosLwip/netif/EthernetIf.c
  • DEVLOG.md
✅ Files skipped from review due to trivial changes (1)
  • DEVLOG.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • Bdd/Targets/FreeRtosLwip/README.md
  • Bdd/Targets/FreeRtosLwip/netif/EthernetIf.c
  • Bdd/Targets/FreeRtosLwip/main.c

📝 Walkthrough

Walkthrough

This PR converts the FreeRTOS+lwIP BDD target from a CI-only link‑probe into a runnable NO_SYS=0 QEMU target: adds a vendored LAN9118 driver, lwIP netif wrapper, expanded FreeRTOS/lwIP threading config, a SolidSyslog interactive BDD runtime with ARP warm‑up, CMake wiring, CI lane, and docs/DEVLOG entries.

Changes

FreeRTOS+lwIP BDD Target

Layer / File(s) Summary
FreeRTOS & lwIP configuration for threading and UDP
Bdd/Targets/FreeRtosLwip/FreeRTOSConfig.h, Bdd/Targets/FreeRtosLwip/lwipopts.h, Bdd/Targets/FreeRtosLwip/solidsyslog_user_tunables.h
FreeRTOS kernel expanded (mutexes, timers, static alloc); lwIP switched to NO_SYS=0 with tcpip thread sizing, mailbox sizing, LWIP_TCPIP_CORE_LOCKING, ARP_QUEUEING, and tcpip message pool sizing; SolidSyslog tunables set (512-byte max, 2 mutex pool).
Vendored LAN9118 SMSC9220 driver
Bdd/Targets/FreeRtosLwip/netif/smsc9220/smsc9220_eth_drv.h, .../smsc9220_eth_drv.c, smsc9220_emac_config.h, .clang-format
Adds complete SMSC9220 driver (register map, MAC/PHY access, FIFO helpers, IRQs, init/link, chunked send/receive) and a .clang-format to preserve upstream formatting.
lwIP network interface wrapper
Bdd/Targets/FreeRtosLwip/netif/EthernetIf.h, netif/EthernetIf.c
Netif adapter: MAC programming, FIFO interrupt threshold programming, dedicated RX task with ISR notification, pbuf allocation/copy path, oversized-frame discard, and low-level TX via chunked send API.
SolidSyslog BDD runtime and network lifecycle
Bdd/Targets/FreeRtosLwip/main.c
Runtime: UART init, LwipRaw marshal binding, tcpip_init with NetworkBringUp (static IP, ARP warm‑up), InteractiveTask (BDD loop, switching sender), ServiceTask, teardown orchestration, and semihosting exit.
Build system integration
Bdd/Targets/FreeRtosLwip/CMakeLists.txt, CMakeLists.txt, CMakePresets.json
CMake OBJECT library and executable expanded to include lwIP api/tcpip sources, sys_arch.c, driver, Platform/FreeRtos adapters, Ethernet startup and UART/syscalls; preset binds SOLIDSYSLOG_USER_TUNABLES_FILE.
Documentation and DEVLOG
Bdd/Targets/FreeRtosLwip/README.md, DEVLOG.md
README updated for runnable QEMU flow and docker compose invocation; DEVLOG documents PBUF_REF lifetime fix (ARP warm‑up), driver vendoring, tcpip marshal details, and CI advisory note.
CI/Behave lane for testing
.github/workflows/ci.yml, ci/docker-compose.bdd.yml
CI now cross-builds lwIP BDD ELF, probes ELF symbols for PlusTcp linkage, uploads ELF and lwIP tunables; adds bdd-freertos-qemu-lwip job that downloads artifacts and runs behave-freertos-lwip + syslog-ng-freertos-lwip via docker compose (UDP-only scenarios), publishing JUnit results.

Sequence Diagram

sequenceDiagram
  participant Developer
  participant CI
  participant QEMU as FreeRTOS/QEMU target
  participant lwIP as tcpip thread
  participant Driver as SMSC9220 driver
  participant Oracle as syslog-ng

  Developer->>CI: push PR (build & artifacts)
  CI->>QEMU: upload/download ELF & tunables
  CI->>Oracle: start syslog-ng-freertos-lwip
  CI->>QEMU: start behave-freertos-lwip (runs tests)
  QEMU->>lwIP: tcpip_init, netif_add, ARP warm-up
  lwIP->>Driver: linkoutput/send_by_chunks (TX)
  Driver->>Oracle: network packets delivered to syslog-ng
  Oracle->>CI: test results (JUnit)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related issues

  • #439: E28 epic for lwIP Raw API support — this PR implements core S28.09 scope from that epic.
  • #479: Adds/discusses the SMSC9220/LAN9118 driver and related behaviors referenced by this PR.
  • #474: Overlaps FreeRtosLwip target build and CI lane additions present in this PR.

Possibly related PRs

Poem

🐰 I tunneled through packets, soft and fleet,
A LAN9118 driver made the net complete.
lwIP woke its tcpip thread at dawn,
ARP warmed the gateway — no packets gone.
QEMU and syslog danced — the BDD passed on.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'S28.09 FreeRtosLwip LAN9118 netif + NO_SYS=0 UDP on QEMU' clearly and specifically summarizes the main change: adding a FreeRTOS+lwIP network stack to the BDD target.
Description check ✅ Passed The PR description follows the template with all required sections: Purpose (S28.09 with issue link), Change Description (detailed technical changes), Test Evidence (oracle BDD results, build validation, clang-format clean), and Areas Affected (Tier 3 BDD target, CI, CMake wiring).
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #472: LAN9118 netif driver vendored with Apache-2.0 preservation, NO_SYS=0 lwIP integration, UDP pipeline wired via tcpip-callback marshalling, first-packet ARP fix, zero PlusTcp symbol linking, and oracle BDD validation passed.
Out of Scope Changes check ✅ Passed All changes align with issue #472 scope: netif driver, NO_SYS=0 config, pipeline wiring, ARP fix, CI lanes, and CMake presets. TCP/TLS deferred as intended; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/s28-09-freertoslwip-netif-udp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

☀️   Quality Summary

   🚦   build-linux-gcc: 100% successful (✔️ 1344 passed)
   🚦   build-freertos-host-tdd-plustcp: 100% successful (✔️ 1652 passed)
   🚦   build-linux-clang: 100% successful (✔️ 1296 passed)
   🚦   sanitize-linux-gcc: 100% successful (✔️ 1296 passed)
   🚦   integration-linux-openssl: 100% successful (✔️ 9 passed)
   🚦   integration-linux-mbedtls: 100% successful (✔️ 7 passed)
   🚦   integration-windows-openssl: 100% successful (✔️ 9 passed)
   🚦   bdd-linux-syslog-ng: 94% successful (✔️ 46 passed, 🙈 3 skipped)
   🚦   bdd-windows-otel: 90% successful (✔️ 44 passed, 🙈 5 skipped)
   🚦   bdd-freertos-qemu-plustcp: 86% successful (✔️ 42 passed, 🙈 7 skipped)
   🚦   build-windows-msvc: 100% successful (✔️ 1149 passed)
   🚦   build-linux-tunable-override: 100% successful (✔️ 1296 passed)
   ⚠️   Clang-Tidy: No warnings
   ⚠️   CPPCheck: No warnings


Created by Quality Monitor v1.14.0 (#f3859fd). More details are shown in the GitHub Checks Result.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (1)
Bdd/Targets/FreeRtosLwip/netif/EthernetIf.c (1)

227-230: ⚡ Quick win

Prefer smsc9220_get_interrupt() over the local extern get_irq_status() in EthernetISR.
smsc9220_eth_drv.h exposes int smsc9220_get_interrupt(dev, source), which returns GET_BIT(register_map->irq_status, source); since the ISR already uses SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL, replace the get_irq_status(dev) & rxFifoStatusBit check with smsc9220_get_interrupt(dev, SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL) != 0 and drop the local extern to avoid coupling to the internal symbol.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Bdd/Targets/FreeRtosLwip/netif/EthernetIf.c` around lines 227 - 230, In
EthernetISR replace the use of the local extern get_irq_status(dev) check with
the public API: call smsc9220_get_interrupt(dev,
SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL) and test it != 0, and remove the local
extern uint32_t get_irq_status(...) declaration; this keeps the ISR using the
driver-exported smsc9220_get_interrupt function and avoids coupling to the
internal get_irq_status symbol.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 1157-1158: The workflow uses the mutable reference
dorny/test-reporter@v3 (e.g., the step named "BDD Test Report (FreeRTOS+lwIP)")
which should be pinned to a specific commit SHA; update every occurrence of
dorny/test-reporter@v3 in the CI workflow to the full commit SHA (the same
pattern used for other actions) so the action reference is immutable and
reproducible.
- Line 1133: The checkout step in the bdd-freertos-qemu-lwip job is leaving Git
credentials inside the workspace that get bind-mounted into containers; update
the actions/checkout@... step (the checkout action invocation) to include
persist-credentials: false so credentials are not written into the checked-out
.git directory before the docker compose step mounts the repo.

In `@Bdd/Targets/FreeRtosLwip/main.c`:
- Around line 567-587: TeardownAll uses a fixed 20ms vTaskDelay to hope
ServiceTask sees solidSyslogTeardown before lifecycleMutex is destroyed; instead
ensure the service has actually exited (or been signalled to exit) before
destroying lifecycleMutex. Modify TeardownAll to set solidSyslogTeardown, then
notify the service task (e.g., xTaskNotify or give a semaphore) and block
waiting for a confirmation from the service (e.g., service clears
serviceTaskHandle or gives a "terminated" semaphore/task notification) or poll
serviceTaskHandle in a loop with vTaskDelay and a sensible timeout; only after
receiving the explicit confirmation that ServiceTask has finished should you
call SolidSyslogFreeRtosMutex_Destroy(lifecycleMutex). Use the existing symbols
solidSyslogTeardown, serviceTaskHandle, lifecycleMutex and TeardownAll to
implement this handshake.
- Around line 368-370: When updating the "host" via TryUpdateString(host,
sizeof(host), value) in main.c, also increment the endpointVersion so
GetEndpointVersion() changes and the new host takes effect immediately; modify
the branch handling strcmp(name, "host") to detect a successful update
(TryUpdateString returned true) and bump endpointVersion (the variable used by
GetEndpointVersion()) after the update, similar to how port changes trigger the
version bump.
- Around line 204-206: Check the return values from both xTaskCreate calls (the
InteractiveTask and ServiceTask creations) instead of ignoring them; if either
returns an error (not pdPASS) log/report the failure and avoid calling
vTaskStartScheduler (e.g., return or loop/error-handle), and ensure
serviceTaskHandle is only used if ServiceTask creation succeeded. Update the
xTaskCreate calls in main (InteractiveTask, ServiceTask,
SERVICE_TASK_STACK_DEPTH, INTERACTIVE_TASK_STACK_DEPTH, and serviceTaskHandle)
to capture and check their return values and handle a failed creation path
before vTaskStartScheduler is invoked.
- Around line 256-282: The code incorrectly uses tcpip_callback which only
queues work; change both WarmUpGatewayArp and LwipTcpipMarshal to use
tcpip_callback_wait (or an equivalent completion API) so the caller blocks until
the posted callback finishes; in WarmUpGatewayArp ensure the boolean resolved is
safe to pass (stack-to-callback race) by either allocating a stable completion
object or using tcpip_callback_wait so the callback writes to resolved before
the function reads it; replace tcpip_callback(callback, context) calls in
LwipTcpipMarshal and the WarmUpGatewayArp loop with tcpip_callback_wait to
guarantee synchronous behavior.

In `@Bdd/Targets/FreeRtosLwip/netif/EthernetIf.c`:
- Around line 197-219: EthernetIf_LowLevelInput currently returns NULL for
overlength frames and leaves the RX FIFO intact; change it so that when
smsc9220_peek_next_packet_size() returns length > sizeof(EthernetIf_RxBuffer)
you explicitly drain that frame by calling smsc9220_receive_by_chunks() (into a
temporary discard buffer or in chunks) and update stats (e.g.,
LINK_STATS_INC(link.drop) and LINK_STATS_INC(link.memerr) as appropriate) before
returning NULL; keep the normal path for valid lengths that allocate a pbuf, and
ensure this drain behavior mirrors what
empty_rx_fifo()/smsc9220_receive_by_chunks() expect so the RX FIFO level
interrupt can deassert.

In `@Bdd/Targets/FreeRtosLwip/netif/smsc9220/smsc9220_eth_drv.c`:
- Around line 319-336: The code is prepending a pad DWORD even when the chunk is
already 4-byte aligned; change the logic so when remainder_bytes == 0 you do not
build or write a leading padded word. Specifically, in the block that computes
remainder_bytes and filler_bytes and fills tx_data_port_tmp_ptr (using
remainder_bytes, filler_bytes, tx_data_port_tmp_ptr, tx_data_port_tmp,
register_map->tx_data_port, size_bytes and data), skip the padding loop and the
write to register_map->tx_data_port for aligned chunks — only write actual data
words directly when remainder_bytes == 0; when remainder_bytes != 0 keep the
existing filler loop but only subtract remainder_bytes from size_bytes and
advance data when you actually consumed those start bytes. Also apply the same
fix at the other location that uses current_size % 4 (the 1150-1155 block) so
aligned segments are not prepended with a dummy DWORD.
- Around line 455-468: The busy-bit poll in smsc9220_mac_regwrite is incorrectly
masking register_map->mac_csr_cmd with the boolean result of GET_BIT, causing
the loop to test bit 0 instead of the BUSY bit; update the loop condition to
directly test the BUSY bit (either by using GET_BIT(register_map->mac_csr_cmd,
MAC_CSR_CMD_BUSY_INDEX) as the boolean expression or by masking
register_map->mac_csr_cmd with (1u << MAC_CSR_CMD_BUSY_INDEX)) so the loop only
exits when the BUSY bit is cleared or time_out reaches zero; change the
condition in the do/while to use one of these correct checks and keep the rest
of the smsc9220_mac_regwrite logic intact.
- Around line 855-859: smsc9220_check_id currently returns 0/1 causing callers
(like smsc9220_init) to interpret failure as SMSC9220_ERROR_TIMEOUT; change its
return to the proper smsc9220_error_t values: check the chip ID using
GET_BIT_FIELD(id, CHIP_ID_MASK, CHIP_ID_POS) == CHIP_ID and return the success
enum (e.g., SMSC9220_ERROR_NONE or SMSC9220_OK) on match and a specific chip-id
error enum (e.g., SMSC9220_ERROR_CHIP_ID or SMSC9220_ERROR_ID_MISMATCH) on
mismatch so callers receive the correct error code. Ensure the function
signature remains int -> smsc9220_error_t (or update signature to
smsc9220_error_t) and update any callers if you change the return type.

In `@Bdd/Targets/FreeRtosLwip/README.md`:
- Around line 27-29: The README references incorrect vendored driver
paths—update the mention of `netif/smsc9220_eth_drv.{c,h}` and
`netif/smsc9220_emac_config.h` to reflect the actual directory layout under
`netif/smsc9220/` (for example, `netif/smsc9220/smsc9220_eth_drv.c`,
`netif/smsc9220/smsc9220_eth_drv.h`, and
`netif/smsc9220/smsc9220_emac_config.h`) so the documented paths match the
repository structure in README.md.

In `@DEVLOG.md`:
- Around line 12986-12993: Update the DEVLOG wording to avoid implying an lwIP
API called tcpip_callback_with_block: explain that the adapters use the blocking
marshal wrapper implemented by LwipTcpipMarshal (installed via
SolidSyslogLwipRaw_SetMarshal), and that LwipTcpipMarshal internally calls the
lwIP API tcpip_callback(...) to achieve the synchronous/blocking behavior;
either rename the informal “tcpip_callback_with_block” to “LwipTcpipMarshal
(tcpip_callback-based blocking marshal)” or add an explicit mapping sentence
linking the informal term to the LwipTcpipMarshal implementation and its use of
tcpip_callback.

---

Nitpick comments:
In `@Bdd/Targets/FreeRtosLwip/netif/EthernetIf.c`:
- Around line 227-230: In EthernetISR replace the use of the local extern
get_irq_status(dev) check with the public API: call smsc9220_get_interrupt(dev,
SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL) and test it != 0, and remove the local
extern uint32_t get_irq_status(...) declaration; this keeps the ISR using the
driver-exported smsc9220_get_interrupt function and avoids coupling to the
internal get_irq_status symbol.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 40028f94-5c5b-479d-b55a-cbaaaddb1333

📥 Commits

Reviewing files that changed from the base of the PR and between 49f496b and a456b2a.

📒 Files selected for processing (17)
  • .github/workflows/ci.yml
  • Bdd/Targets/FreeRtosLwip/CMakeLists.txt
  • Bdd/Targets/FreeRtosLwip/FreeRTOSConfig.h
  • Bdd/Targets/FreeRtosLwip/README.md
  • Bdd/Targets/FreeRtosLwip/lwipopts.h
  • Bdd/Targets/FreeRtosLwip/main.c
  • Bdd/Targets/FreeRtosLwip/netif/EthernetIf.c
  • Bdd/Targets/FreeRtosLwip/netif/EthernetIf.h
  • Bdd/Targets/FreeRtosLwip/netif/smsc9220/.clang-format
  • Bdd/Targets/FreeRtosLwip/netif/smsc9220/smsc9220_emac_config.h
  • Bdd/Targets/FreeRtosLwip/netif/smsc9220/smsc9220_eth_drv.c
  • Bdd/Targets/FreeRtosLwip/netif/smsc9220/smsc9220_eth_drv.h
  • Bdd/Targets/FreeRtosLwip/solidsyslog_user_tunables.h
  • CMakeLists.txt
  • CMakePresets.json
  • DEVLOG.md
  • ci/docker-compose.bdd.yml

Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/ci.yml
Comment thread Bdd/Targets/FreeRtosLwip/main.c Outdated
Comment thread Bdd/Targets/FreeRtosLwip/main.c
Comment thread Bdd/Targets/FreeRtosLwip/main.c
Comment thread Bdd/Targets/FreeRtosLwip/netif/smsc9220/smsc9220_eth_drv.c
Comment thread Bdd/Targets/FreeRtosLwip/netif/smsc9220/smsc9220_eth_drv.c
Comment thread Bdd/Targets/FreeRtosLwip/netif/smsc9220/smsc9220_eth_drv.c
Comment thread Bdd/Targets/FreeRtosLwip/README.md Outdated
Comment thread DEVLOG.md Outdated
DavidCozens and others added 2 commits May 29, 2026 18:19
…sion, RX drain

Apply the actionable, in-scope findings from the CodeRabbit review of #476,
matching the established Bdd/Targets/FreeRtos (+TCP) sibling where it diverged:

- main.c: check both xTaskCreate results and SemihostingExit(1) on failure
  rather than entering vTaskStartScheduler with a missing task (sibling does
  the same guard).
- main.c: bump endpointVersion when `set host` changes the host, so a host
  change takes effect without also changing port (sibling already does this).
- main.c: correct the LwipTcpipMarshal comment — tcpip_callback only blocks
  until the work is queued; synchronicity here comes from TCPIP_THREAD_PRIO
  being above every marshalling task. Records the LOCK_TCPIP_CORE follow-up.
- EthernetIf.c: drain an oversize/corrupt RX frame from the FIFO in
  word-aligned chunks instead of returning NULL, which would re-peek the same
  frame forever and wedge receive.
- README.md / DEVLOG.md: fix the vendored driver paths (netif/smsc9220/) and
  the tcpip_callback wording.

Oracle re-run green (8 features / 25 scenarios / 96 steps, 0 failed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR #476 review #4. tcpip_callback only blocks until the work is queued, so
LwipTcpipMarshal previously relied on TCPIP_THREAD_PRIO preempting every
marshalling task to appear synchronous — a correct-but-implicit invariant.

LWIP_TCPIP_CORE_LOCKING is enabled, so run the callback in the caller's task
context under a LOCK_TCPIP_CORE/UNLOCK_TCPIP_CORE pair instead: unconditionally
synchronous regardless of priority, with no per-send mailbox message. The ARP
warm-up query reuses the same marshal, which also closes the stack-`resolved`
race in WarmUpGatewayArp. NetworkBringUp stays on tcpip_callback (its
smsc9220_init calls vTaskDelay and must not run under the held core lock).

Oracle re-run green (8 features / 25 scenarios / 96 steps, 0 failed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@DavidCozens

Copy link
Copy Markdown
Owner Author

CodeRabbit review triage

Thanks @coderabbitai. Verified all 12 findings against the code and the merged Bdd/Targets/FreeRtos (+TCP) sibling. Disposition:

Fixed in this PR

  • E01: Core Syslog Formatting #3 main.c — guard both xTaskCreate results, SemihostingExit(1) on failure (matches the +TCP sibling).
  • E02: UDP Sender #4 main.c — marshal lwIP calls under LOCK_TCPIP_CORE/UNLOCK_TCPIP_CORE instead of tcpip_callback. tcpip_callback only blocks until the work is queued; the previous code was synchronous only because TCPIP_THREAD_PRIO preempts every marshalling task — a correct-but-implicit invariant. Core-locking is enabled, so the callback now runs in the caller's context, unconditionally synchronous. The ARP warm-up reuses the same marshal, which also closes the stack-resolved race you flagged at 454–456. NetworkBringUp stays on tcpip_callback (its smsc9220_init calls vTaskDelay, which must not run under the held core lock).
  • E03: TLS Transport #5 main.c — bump endpointVersion when set host changes the host (matches the +TCP sibling).
  • E05: Store and Forward #7 EthernetIf.c — drain an oversize/corrupt RX frame from the FIFO in word-aligned chunks instead of returning NULL (which would re-peek it forever and wedge receive).
  • E09: C++ Wrapper #11 README.md / E10: Static Analysis and MISRA #12 DEVLOG.md — corrected the vendored driver paths (netif/smsc9220/) and the tcpip_callback wording.

Oracle re-run green after each change: 8 features / 25 scenarios / 96 steps, 0 failed.

Deferred (tracked, not dropped)

S28.09 stays UDP-scoped; the deferrals are real-hardware / project-wide hygiene that belong outside this story.

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

@DavidCozens — clean, well-structured triage; thank you for the detailed disposition on every finding.

A few notes on the fixes:

#4 is the most substantive improvement. The tcpip_callback path was correct by coincidence (tcpip thread priority dominates all marshalling tasks), but LOCK_TCPIP_CORE/UNLOCK_TCPIP_CORE makes the synchrony an explicit contract rather than a scheduler-ordering invariant that future priority changes could silently break. The carve-out for NetworkBringUp is exactly right — vTaskDelay inside a held core lock is a deadlock waiting to happen.

#7 (drain-on-oversize) was a genuine liveness bug — re-peeking a stuck oversized frame forever would have wedged the RX path on any real error injection, even if QEMU's model never produces one naturally.

The deferrals are sensibly scoped: #477 and #478 are project-wide patterns that should be fixed uniformly across siblings, not patched asymmetrically in a single target; #479 touches the vendored Apache-2.0 driver and is QEMU-benign per the oracle.

All findings are either fixed or tracked — happy to resolve all outstanding review comments on this PR if you'd like.

🐇 ✓


✏️ Learnings added
Learnt from: DavidCozens
URL: https://github.com/DavidCozens/solid-syslog/pull/476

Timestamp: 2026-05-29T17:38:24.647Z
Learning: In the `Bdd/Targets/FreeRtosLwip` FreeRTOS+lwIP target, `tcpip_callback` only blocks until the work item is *queued* on the tcpip thread mailbox, not until it is executed. When `LWIP_TCPIP_CORE_LOCKING=1`, using `LOCK_TCPIP_CORE`/`UNLOCK_TCPIP_CORE` is the preferred marshal pattern because it runs the protected code unconditionally synchronous in the caller's context. `NetworkBringUp` must stay on `tcpip_callback` (not under the core lock) because `smsc9220_init` calls `vTaskDelay`, which must not run under a held core lock (deadlock risk).

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Actions performed

Comments resolved. Approval is disabled; enable reviews.request_changes_workflow to allow explicit top-level @coderabbitai resolve or @coderabbitai approve commands.

@github-actions

Copy link
Copy Markdown
Contributor

☀️   Quality Summary

   🚦   build-linux-gcc: 100% successful (✔️ 1344 passed)
   🚦   build-freertos-host-tdd-plustcp: 100% successful (✔️ 1652 passed)
   🚦   build-linux-clang: 100% successful (✔️ 1296 passed)
   🚦   sanitize-linux-gcc: 100% successful (✔️ 1296 passed)
   🚦   integration-linux-openssl: 100% successful (✔️ 9 passed)
   🚦   integration-linux-mbedtls: 100% successful (✔️ 7 passed)
   🚦   integration-windows-openssl: 100% successful (✔️ 9 passed)
   🚦   bdd-linux-syslog-ng: 94% successful (✔️ 46 passed, 🙈 3 skipped)
   🚦   bdd-windows-otel: 90% successful (✔️ 44 passed, 🙈 5 skipped)
   🚦   bdd-freertos-qemu-plustcp: 86% successful (✔️ 42 passed, 🙈 7 skipped)
   🚦   build-windows-msvc: 100% successful (✔️ 1149 passed)
   🚦   build-linux-tunable-override: 100% successful (✔️ 1296 passed)
   ⚠️   Clang-Tidy: No warnings
   ⚠️   CPPCheck: No warnings


Created by Quality Monitor v1.14.0 (#f3859fd). More details are shown in the GitHub Checks Result.

@DavidCozens DavidCozens merged commit 37d3e04 into main May 29, 2026
25 checks passed
@DavidCozens DavidCozens deleted the feat/s28-09-freertoslwip-netif-udp branch May 29, 2026 17:45
DavidCozens added a commit that referenced this pull request May 30, 2026
Adds `persist-credentials: false` to all 26 `actions/checkout` steps (25 in
ci.yml, 1 in sbom.yml) so the checkout's GITHUB_TOKEN is not persisted in
.git/config for later steps — the BDD compose lanes bind-mount the repo root
into containers. Closes the zizmor `artipacked` finding deferred from #476.

SHA-pins the three remaining tag-pinned actions so the workflow tree is
zizmor `unpinned-uses`-clean:
  - dorny/test-reporter@v3      -> a43b3a5 # v3.0.0
  - actions/cache@v4            -> (then bumped to v5, see below)
  - uhafner/quality-monitor@v1  -> 778e3a6 # v1.14.0

Sweeps the entire workflow tree (ci.yml, sbom.yml, release-please.yml) off the
deprecated Node 20 runtime (forced off the runner from 2026-06-16) to the
node24 release of each action:
  - actions/upload-artifact            -> 043fb46 # v7.0.1
  - actions/download-artifact          -> 3e5f45b # v8.0.1
  - actions/cache                      -> 27d5ce7 # v5.0.5
  - actions/github-script              -> ed59741 # v8.0.0
  - actions/deploy-pages               -> cd2ce8f # v5.0.0
  - googleapis/release-please-action   -> 45996ed # v5.0.0

Verified node24-clean: every pinned SHA's action.yml resolves to using:node24
(or composite/docker); no node20 and no unpinned uses remain.

Behaviour notes (acceptable pre-release): download-artifact@v8 defaults a hash
mismatch to a hard error; release-please-action@v5 marks node24 a breaking
change but it is only the runtime bump. The deploy-pages and release-please
lanes run on main only, so this PR's CI does not exercise them.

Closes #477

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DavidCozens added a commit that referenced this pull request May 30, 2026
#485)

Adds `persist-credentials: false` to all 26 `actions/checkout` steps (25 in
ci.yml, 1 in sbom.yml) so the checkout's GITHUB_TOKEN is not persisted in
.git/config for later steps — the BDD compose lanes bind-mount the repo root
into containers. Closes the zizmor `artipacked` finding deferred from #476.

SHA-pins the three remaining tag-pinned actions so the workflow tree is
zizmor `unpinned-uses`-clean:
  - dorny/test-reporter@v3      -> a43b3a5 # v3.0.0
  - actions/cache@v4            -> (then bumped to v5, see below)
  - uhafner/quality-monitor@v1  -> 778e3a6 # v1.14.0

Sweeps the entire workflow tree (ci.yml, sbom.yml, release-please.yml) off the
deprecated Node 20 runtime (forced off the runner from 2026-06-16) to the
node24 release of each action:
  - actions/upload-artifact            -> 043fb46 # v7.0.1
  - actions/download-artifact          -> 3e5f45b # v8.0.1
  - actions/cache                      -> 27d5ce7 # v5.0.5
  - actions/github-script              -> ed59741 # v8.0.0
  - actions/deploy-pages               -> cd2ce8f # v5.0.0
  - googleapis/release-please-action   -> 45996ed # v5.0.0

Verified node24-clean: every pinned SHA's action.yml resolves to using:node24
(or composite/docker); no node20 and no unpinned uses remain.

Behaviour notes (acceptable pre-release): download-artifact@v8 defaults a hash
mismatch to a hard error; release-please-action@v5 marks node24 a breaking
change but it is only the runtime bump. The deploy-pages and release-please
lanes run on main only, so this PR's CI does not exercise them.

Closes #477

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

S28.09: Bdd/Targets/FreeRtosLwip — LAN9118 lwIP netif + UDP on QEMU

1 participant