Skip to content

subsys: modem: modem_chat: add optional per-line tap callback#108515

Open
matheuswhite wants to merge 2 commits intozephyrproject-rtos:mainfrom
edgebr:topic/modem-line-tap
Open

subsys: modem: modem_chat: add optional per-line tap callback#108515
matheuswhite wants to merge 2 commits intozephyrproject-rtos:mainfrom
edgebr:topic/modem-line-tap

Conversation

@matheuswhite
Copy link
Copy Markdown

@matheuswhite matheuswhite commented May 5, 2026

Adds an optional line_tap callback to struct modem_chat_config that
fires once per complete delimited line, before the typed matcher
dispatch.

This is useful for forensic logging or forwarding the raw stream to a
peer (e.g. USB CDC-ACM) without competing with the existing prefix
matchers — those are single-shot and would be blocked by a wildcard
"$" matcher placed alongside them.

A companion tap_buf field captures incoming bytes byte-faithfully into
a caller-provided buffer before the chat parser may replace separators
with \0 for argv parsing. Truncation is silent; the tap callback
receives whatever fit, with len computed against the captured prefix.
When tap_buf is omitted but line_tap is set, the tap fires with the
live receive_buf — separators of matched lines may already be mutated
by the typed-matcher path (documented).

Existing chat consumers leave both fields zero-initialized via designated
initializers and remain unaffected — line_tap = NULL keeps the
dispatch path byte-identical to pre-patch.

Test coverage

ztest cases in tests/subsys/modem/modem_chat/src/main.c:

  • Tap fires once per complete delimited line.
  • Without tap_buf: tap receives receive_buf after possible separator
    mutation by typed matchers; without typed matcher, raw bytes.
  • With tap_buf: tap receives byte-faithful bytes — separator commas
    intact even when a typed matcher engaged on the same line.
  • Empty line (delimiter-only) does NOT fire the tap.
  • Line longer than tap_buf_size is truncated; len arg equals
    tap_buf_size, no overrun.
  • parse_reset zeroes tap_buf_len so consecutive lines do not bleed
    into each other.
  • Backward compat: line_tap = NULL leaves existing dispatch behaviour
    byte-identical to pre-patch (regression guard for every other modem
    driver in tree).

Two extra chat instances (tap_cmd / tap_typed_cmd) live alongside
the existing fixtures so the tap tests do not perturb the script-driven
tests.

Adds a `line_tap` callback to struct modem_chat_config that fires once
per complete delimited line, before the typed matcher dispatch. Useful
for forensic logging or forwarding the raw stream to a peer (e.g. USB
CDC-ACM) without competing with the existing prefix matchers — those
are single-shot and would be blocked by a wildcard "$" matcher.

A companion `tap_buf` field captures incoming bytes byte-faithfully
into a caller-provided buffer before the chat parser may replace
separators with '\\0' for argv parsing. Truncation is silent; the tap
callback receives whatever fit, with line_len computed against the
captured prefix. When tap_buf is omitted but line_tap is set, the tap
fires with the live receive_buf (separators of matched lines may
already be mutated — documented).

Existing chat consumers leave both fields zero-initialized via
designated initializers and remain unaffected.

Signed-off-by: Matheus T. dos Santos <matheus.santos@vertex.org.br>
Add ztest coverage for the line_tap callback added in the previous
commit:

- Tap fires once per complete delimited line.
- Without tap_buf: tap receives receive_buf after possible separator
  mutation by typed matchers; without typed matcher, raw bytes.
- With tap_buf: tap receives byte-faithful bytes — separator commas
  intact even when a typed matcher engaged on the same line.
- Empty line (delimiter-only) does NOT fire the tap.
- Line longer than tap_buf_size is truncated; len arg equals
  tap_buf_size, no overrun.
- parse_reset zeroes tap_buf_len so consecutive lines do not bleed
  into each other.
- Backward compat: line_tap = NULL leaves existing dispatch behaviour
  byte-identical to pre-patch (regression guard for every other modem
  driver in tree).

Two extra chat instances (tap_cmd / tap_typed_cmd) live alongside the
existing fixtures so the tap tests do not perturb the script-driven
tests.

Signed-off-by: Matheus T. dos Santos <matheus.santos@vertex.org.br>
@matheuswhite matheuswhite changed the title Modem line tap subsys: modem: modem_chat: add optional per-line tap callback May 5, 2026
@matheuswhite matheuswhite marked this pull request as ready for review May 5, 2026 14:56
@zephyrbot zephyrbot added area: Modem area: Tests Issues related to a particular existing or missing test labels May 5, 2026
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 5, 2026

@bjarki-andreasen
Copy link
Copy Markdown
Contributor

bjarki-andreasen commented May 5, 2026

How about a simple pipe level splitter? if you want the data to be received both by a "tap" and by a chat instance, something like

UART -> UART backend -> modem pipe -> pipe splitter --+--> modem_chat
                                                      |
                                                      +--> tap

I don't see why modem_chat should be burdened with something as simple as "callback on newline". That could be implemented by another module.

So, create a pipe splitter, that reads from the uart backend, copies it into n amount of parralel buffers, and exposes one pipe for each of them. Kind of similar to how modem_cmux exposes multiple DLCI pipes (though that is a mux, not a "raw" splitter)

The splitter could also be used for detecting NO CARRIER on a PPP stream, or cloning raw NMEA messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: Modem area: Tests Issues related to a particular existing or missing test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants