A peer that has not heard the UA repeats its SABM, and cannot have sent a
numbered frame meanwhile. Treating that as a restart threw away queued
frames and reported the session lost for a single dropped UA. A SABM is
now a restart only if the peer has sent a numbered frame since the link
came up.
Co-Authored-By: Claude Code <noreply@anthropic.com>
max_packet_size is derived from the layer below in the constructor
instead of being assigned in the body.
Co-Authored-By: Claude Code <noreply@anthropic.com>
- Keep App alive across the HDLC tests, as the test main requires
- Run a layer's loop in the tests only while it has asked for it, so
the loop's disable and request bookkeeping is tested, and wait for the
retry timer in the tests of refused sends
- Report no send capacity while the layer has no send queue
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Reset the link after max_retries even when it is 255, where the retry
counter used to wrap
- Require a reliability timeout of at least 50 ms
- Drop received frames larger than the layer's max_packet_size
- Fail setup, instead of aborting, when the send queue cannot be
allocated or max_packet_size leaves no room
- Test the ack delay timer and a refused REJ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Layers stack on a link and are configured on its packet_interface entry.
Each layer type registers itself with its kind, so a new type needs only
a new module. The first is a reliability layer in the style of HDLC
asynchronous balanced mode, with go-back-N retransmission.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The link derives max_packet_size from the span it is given, so a packet
larger than the copy buffer cannot be copied into it.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The copy buffer is sized by code generation to max_packet_size, but
set_max_packet_size() can raise the limit afterwards, and copying a split
packet would then overflow it. The link now takes the buffer as a span
and refuses a split packet larger than it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A read cuts a datagram longer than the buffer to fit, so a 600 byte
datagram arrived as a valid looking 508 byte packet. The buffer is now one
byte longer, and a read that fills it is dropped. The split packet test
also checks the bytes joined in the copy buffer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Code generation declares the UDP link's buffer for packets split across
buffers as a static array of max_packet_size bytes, so it is not
allocated at run time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Enforce max_packet_size on send and receive
- Copy split packets into a buffer allocated in setup, not on the stack
- Add unit tests for the link's send and receive paths
- Explain why the broadcast socket is always enabled
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each packet is one UDP datagram, using the settings of the udp component.
The udp component now reports whether a packet was sent, and an ENOMEM
refusal from lwIP is logged at verbose level as the packet can be sent
again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
max_packet_size was a default with a setter that code generation called
after construction, so it could be raised above the size of a link's
buffer. It is now a constructor parameter, and a link with a fixed buffer
takes the buffer as a std::span, so its size is the link's limit and the
two cannot differ.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The receive buffer is sized by code generation to max_packet_size, but
set_max_packet_size() can raise the limit afterwards, and the received
bytes would then overflow the buffer. The link now takes the buffer as a
span and stops at its size too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test main constructs App once, and later tests and its listener use
it, so the fixture now replaces App in SetUp and constructs it again in
TearDown, instead of constructing over a live App and leaving it
destroyed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fake link with a layer stacked on it checks that the bridge sends
through the top of the stack, passes a packet in one buffer to
packet_transport, and drops a packet split across buffers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It allocated a vector for each call, which invites heap churn. Only the
tests used it; they now have a local helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Code generation declares the UART link's receive buffer as a static
array of max_packet_size bytes, so it is not allocated at run time
- Move the increment out of the condition in note_received_()
- Initialise the UART transmit copy buffer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A packet interface moves packets over a channel, with a uart link and a
packet_transport platform that uses any packet interface. Later layers
stack on the same base class.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>