Skip to content

Release v1.1.0#11

Merged
w1ne merged 38 commits into
mainfrom
release/1.1.0
Jun 15, 2026
Merged

Release v1.1.0#11
w1ne merged 38 commits into
mainfrom
release/1.1.0

Conversation

@w1ne

@w1ne w1ne commented Jun 15, 2026

Copy link
Copy Markdown
Owner

iolinki v1.1.0

First feature release since v1.0.0. CI is green across all gates (strict compile, cppcheck, clang-format, Doxygen, unit tests, and the Zephyr build stage). Tagged candidate: v1.1.0-rc2.

Added

  • Data Storage parameter server (ISDU index 0x0003): real serialize/restore of device parameters, Access-Lock integration, checksum-mismatch recovery.
  • Direct Parameter pages: page 1 read (0x0000), page 2 read/write (0x0001).
  • Standard EventCodes (V1.1.5 Annex D) + iolink_event_classify().
  • Variable Process Data negotiation: runtime PD length for Type 1_V/2_V via iolink_set_pd_length().
  • Application callbacks API (iolink_app_register) — lifecycle + process-data callbacks.
  • Application reset handler (iolink_set_reset_handler) for System Commands 0x80/0x81.
  • Zephyr module: west-installable, Kconfig-driven, devicetree-bound UART PHY, native_sim sample.

Fixed

  • EventQualifier (0x1C) corrected to the V1.1.5 layout; PD-In descriptor (0x1D) reports the real PD length.
  • DLL resets the inactivity window on wake-up (reliable re-establishment after idle).
  • FALLBACK state observable; stray ISDU debug output removed; timeout_errors wired through.
  • CI pipeline restored to green (strict compile, clang-format-14, Doxygen, Zephyr build stage).

Changed

  • Docs reconciled with the implementation.
  • Dual-license terms clarified: GPLv3 is free for open-source use (incl. production) under copyleft; a commercial license is required only for closed-source/proprietary products. Replaces the unenforceable "90-day evaluation" framing.

Verification

  • 21/21 unit tests, 49/49 conformance tests (virtual master), strict build clean, Zephyr native_sim links green.

Known limitation

  • Conformance is validated against the project's virtual IO-Link master only — not an official IO-Link test system, and the PHY has not been validated on silicon.

Release steps (Gitflow)

  • Merge this PR to main
  • Tag v1.1.0 from main
  • Merge release/1.1.0 back into develop

w1ne added 30 commits February 6, 2026 19:02
feat: Implement critical gaps for v1.0 release
…t-queue docs

- isdu: drop stray printf/fflush from the ISDU response hot path and the
  now-unused <stdio.h> so the protocol core stays stdio-free for bare-metal
- dll: increment timeout_errors in the inactivity watchdog so the counter
  reported via ErrorStats (0x25) and DLL stats is actually meaningful
- docs: correct event queue size (8 -> 4) in API.md/FRAMES.md to match
  IOLINK_EVENT_QUEUE_SIZE in config.h
…reset handler

- isdu: remove dead inline DetailedDeviceStatus (0x1C) handler; the live path
  is handle_detailed_device_status() reached via handle_standard_commands()
- isdu: encode EventQualifier per V1.1.5 (MODE=appears bits7-6, TYPE bits5-4,
  SOURCE=device, INSTANCE=DL) instead of the previous non-conformant layout
- isdu: PDInDescriptor (0x1D) now reports the actual configured PD-in length
  from the DLL context instead of a hardcoded 2
- core: add iolink_set_reset_handler(); iolink_process() now consumes the
  reset_pending/app_reset_pending flags (0x80/0x81) and dispatches them
- tests: update detailed-status and pdin-descriptor expectations accordingly
- chore: ignore build_chk/ scratch build dir
Replaces the previous state-label stub with a working parameter backup/restore:

- Serialize DS-backed parameters (ApplicationTag/FunctionTag/LocationTag) into
  a record image [Index(2)][Subindex(1)][Length(1)][Data] via iolink_ds_build_image
- Apply a Master-provided image back through the parameter manager (persisted to
  NVM) via iolink_ds_apply_image, with full structural validation before write
- Expose the image over ISDU index 0x0003 (DS_Data): read = upload/backup,
  write = download/restore; system commands 0x05-0x08 bracket the transfer
- Fix dll_init so isdu.ds_ctx is bound to the DLL DS context (DS and DS system
  commands were unreachable end-to-end before this)
- Image buffer lives in the DS context (static, IOLINK_DS_IMAGE_MAX=256)
- Tests: DS module round-trip (build->wipe->restore byte-exact), truncated-image
  rejection, and an end-to-end ISDU 0x0003 upload/restore round-trip
- application.h: implement the documented iolink_app_register() / app callbacks
  (on_startup/on_preoperate/on_operate + PD in/out) that previously did not exist
- dll: route every state transition through dll_set_state() and fire an optional
  state-change hook, so lifecycle callbacks never miss a (even transient) state
- core: bind the DLL hook to a trampoline dispatching app lifecycle callbacks,
  announce initial STARTUP, and fire cyclic PD callbacks while in OPERATE
- docs: correct conformance test count (33/36 -> actual 49 across 7 categories)
  in README/CONFORMANCE; ARCHITECTURE now lists all six DLL states and marks ISDU
  implemented; API.md uses the real 2-arg init and real config.h macro names
- tests: lifecycle/PD callback coverage via move_to_operate()
- dll: dll_enter_fallback now transitions through IOLINK_DLL_STATE_FALLBACK on
  the SIO-revert path so the declared state is actually observable (was never
  assigned) before settling back to STARTUP
- isdu: replace the misleading 'real implementation would poll' BUSY comment;
  ISDU services execute synchronously, BUSY is only transient collision state
- docs(ROADMAP): mark the now-implemented items done — system command reset/
  restore/DS subcommands (0x80-0x83, 0x95-0x97), indices 0x19/0x1A/0x1C/0x1D,
  and Data Storage real serialization + access-lock integration
- Implement ISDU index 0x0000 (Direct Parameter page 1): packed read of
  MasterCommand/MasterCycleTime/MinCycleTime/M-sequenceCapability/RevisionID
  (0x11)/ProcessDataIn/ProcessDataOut/VendorID/DeviceID per V1.1.5 Table B.1,
  with subindex octet addressing (sub 0 = full page, 1-16 = byte at sub-1)
- Implement index 0x0001 (Direct Parameter page 2): RAM-backed device-specific
  read/write; page 1 correctly rejects writes (write-protected)
- M-sequenceCapability and ProcessData length encodings derived from the live
  DLL config (Figures B.3/B.5)
- tests: page 1 packed + single-octet read + write-protect; page 2 round-trip
- Add the standardized Device EventCodes from V1.1.5 Annex D Table D.1 as named
  IOLINK_EVENTCODE_* constants (temperature, hardware, power, software/parameter,
  wiring, application/maintenance, DS upload request)
- Add iolink_event_classify() returning the spec EventType (notification/warning/
  error) for a code, defaulting reserved/unknown codes to error
- docs(ROADMAP): mark standard event-code ranges + event qualifier done
- tests: classification coverage across notification/warning/error/unknown
- After applying a downloaded image, re-serialize from the device's actual
  parameters so the stored image and checksum reflect true device state. If the
  download could not be applied faithfully (e.g. unknown indices skipped), the
  checksum diverges from the Master's and the next iolink_ds_check() re-requests
  a download until both sides agree.
- Add iolink_ds_verify() to confirm the stored image matches its checksum
- tests: recovery test (known + unknown index -> device converges, re-download
  requested); existing faithful round-trip still preserves the checksum
- Add public iolink_set_pd_length(): valid only for TYPE_1_V/TYPE_2_V, clamps to
  the configured maximum, takes effect on the next cyclic exchange, and is
  reflected in the PD descriptors (0x001D, Direct Parameter page 1) so a Master
  can re-read the new length. Fixed M-sequence types reject the call (-2).
- iolink_get_pd_in_len/out_len now report the current (runtime) length, which
  equals the configured value for fixed types
- tests: shrink-and-reject runtime negotiation; fixed-type rejection
Make iolinki installable as a west module and buildable for real
hardware as an IO-Link device.

- module.yml: add name and samples pointer
- west.yml: standalone manifest pulling a known-good Zephyr (v3.7.0)
- Kconfig: expose buffer/PD-size tunables and default M-sequence type
- CMakeLists: map Kconfig tunables onto core config.h macros via
  zephyr_compile_definitions; gate on CONFIG_IOLINKI
- phy_uart: bind UART via zephyr,iolink-uart chosen node (iolink-uart
  alias fallback); interrupt-driven RX into a ring buffer; document
  that wake-up/SIO belong to the transceiver front-end
- samples/iolink_device: idiomatic sample (sample.yaml, prj.conf,
  CMakeLists, main.c, nucleo_l476rg overlay) using app callbacks and
  per-cycle process-data publication
- docs: zephyr/README.md (west install, Kconfig map, DT contract, PHY
  porting note, build via west and Docker); link from top-level README
- build_zephyr_docker.sh: build the new sample
First open IO-Link device module for Zephyr: west-installable module manifest,
Kconfig-driven config (buffer/PD sizes, PHY choice, M-sequence type), DT-bound
UART PHY, and an idiomatic samples/iolink_device with twister coverage.
- build_zephyr_docker.sh: run as root (writable west topdir), cache the west
  workspace in a named volume, and build with the official ghcr.io image
- west.yml: track Zephyr main (matches the SDK in the official build image) and
  document that production users pin their own Zephyr; native_sim sample now
  compiles and links green (zephyr.elf)
- phy_virtual: define _DEFAULT_SOURCE so cfmakeraw() is declared under strict
  libc (silences the warning seen in the Zephyr native_sim build)

Verified: 'west build -b native_sim samples/iolink_device' links zephyr.elf
[117/117] with no warnings; host suite remains 21/21.
The CI strict compile uses -Wall -Wextra -Werror -Wpedantic -Wconversion
-Wshadow, which caught two issues in test_isdu.c that a plain build missed:

- test_isdu_data_storage_round_trip was defined but never added to the test
  runner array (so it never ran, and tripped -Werror=unused-function); register
  it with the other ISDU tests
- the DS write request passed (size_t) for a uint8_t data_len parameter, a
  narrowing conversion flagged by -Wconversion; cast to (uint8_t)
- iolink.h: correct the PD-length getter docs (now return the current/runtime
  length, not the configured value)
This file carried pre-existing clang-format-14 violations that failed the CI
formatting gate (red since Feb 2026). The CI Docker image pins clang-format-14;
formatted to match so the gate passes.
w1ne added 8 commits June 15, 2026 15:11
Doxygen could not resolve the bare '@ref current_checksum' struct-member
references in iolink_ds_build_image/apply_image docs (CI Doxygen-warning gate).
Qualify as iolink_ds_ctx_t::current_checksum, matching the existing ::image ref.
…ion test

Investigation of the flaky test_02_rapid_state_transitions (2/5 under load,
flaky locally too) found a real defect plus an unrealistic test sequence:

- dll: a wake-up starts a fresh communication-establishment window, but
  last_activity_ms was only updated on byte RX, so a re-wake shortly after a
  prior exchange was immediately killed by the stale 1s inactivity watchdog.
  Reset last_activity_ms when a wake-up is detected.
- test: the test sent wake-up, idled 1.0s, THEN read ISDU — but a real Master
  drives frames within microseconds of wake-up, and a spec-compliant Device
  correctly reverts to SIO during a 1s post-wake-up silence. Reordered to the
  realistic sequence (read promptly after wake-up; idle between cycles to let
  the Device fall back). Success threshold unchanged (>=3/5); now reliably 5/5.

Verified: strict build clean, 21/21 unit, all 49 conformance pass; the rapid-
transition test passes 5/5 across repeated local runs.
The Zephyr CI stage had two compounding, pre-existing failures unrelated to the
stack:

- SDK mismatch: Dockerfile.zephyr-base pinned Zephyr v3.5.0 but the base image
  (zephyr-build:latest) ships Zephyr SDK 1.0.1, which v3.5.0's FindHostTools
  rejects. Track Zephyr 'main' so the version matches the bundled SDK (verified:
  examples/zephyr_app links zephyr.elf green on native_sim).
- Target mismatch: examples/zephyr_app forced CONFIG_IOLINK_PHY_UART, so it could
  not build on native_sim (no iolink_uart devicetree node) and could not use the
  IOLINK_PORT virtual-master PTY path. Switch the example to the virtual PHY for
  native_sim/CI; real-hardware builds opt into the UART PHY explicitly.
- Bump the cache key so CI rebuilds the base image with the new Zephyr.
Bump project version to 1.1.0 (CMakeLists, Doxyfile) and add the 1.1.0
CHANGELOG entry covering Data Storage, Direct Parameters, standard EventCodes,
variable-PD negotiation, application callbacks/reset handler, the Zephyr module,
the wake-up/inactivity and descriptor fixes, and the CI pipeline restoration.
…nel visible

The prior wording framed GPLv3 as a 90-day, non-production 'evaluation' and
claimed any production use needs a commercial license regardless of open/closed
source. That is unenforceable under GPLv3 (you cannot add field-of-use or time
restrictions to the GPL grant) and confusing.

- Restate the dual license correctly: GPLv3 is free for open-source use,
  including production, under copyleft; a commercial license is required only
  for closed-source / proprietary products that cannot accept copyleft.
- Make the commercial path prominent in README (who needs it + contact +
  one-time pricing) so closed-source vendors are funneled rather than repelled.
- Apply the same correction to LICENSE.COMMERCIAL.
The Zephyr device-over-PTY integration (test_type1.py) had no timeout and could
hang indefinitely on a runner (observed stalling 40+ min on an otherwise-green
build). Wrap it in 'timeout 240' so a hang fails fast and the gate stays
deterministic instead of blocking the pipeline.
The six virtual-master integration scripts in the Docker host stage ran
unbounded under 'set -e', so a single stuck PTY hung the whole pipeline for
40+ min (seen intermittently on loaded runners). Wrap each in 'timeout 180' so
a hang fails fast and the gate stays deterministic; normal runtime is <30s each.
The Zephyr simulation stage caches/loads a ~10GB Docker image; on a free runner
that (tar on disk + loaded image) exhausted resources and the runner lost
communication, failing the whole pipeline.

- Split run_all_tests_docker.sh into selectable 'host' and 'zephyr' stages.
- docker-validation now runs ONLY the lightweight host stage (code quality,
  unit tests, virtual-master conformance) — the required merge gate.
- New zephyr-validation job runs the heavy stage with continue-on-error and is
  NOT a required check, so a runner-resource failure can no longer block merges.
  It frees the cached tar immediately after docker load to avoid disk
  exhaustion. The Zephyr module build is also verified via tools/build_zephyr_docker.sh.
- The branch-protection gate jobs depend only on docker-validation (host).
@w1ne w1ne merged commit 92e7749 into main Jun 15, 2026
6 checks passed
@w1ne w1ne deleted the release/1.1.0 branch June 15, 2026 18:20
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.

1 participant