Skip to content

Restart MSP over telemetry reply framing when a reply is discarded - #11919

Open
Raffi1202 wants to merge 1 commit into
iNavFlight:maintenance-10.xfrom
Raffi1202:fix/msp-v1-reply-header
Open

Raffi1202 wants to merge 1 commit into
iNavFlight:maintenance-10.xfrom
Raffi1202:fix/msp-v1-reply-header

Conversation

@Raffi1202

@Raffi1202 Raffi1202 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Problem

MSPv1 replies sent over CRSF telemetry arrive without the function byte: after the length byte the payload follows directly (#10667, captured on INAV 8.0.0). The receiving side cannot tell which request a reply belongs to and parses the payload at the wrong offset.

Cause

On maintenance-10.x the v1 reply header already contains the function byte (src/main/telemetry/msp_shared.c:229), but the same symptom is still produced by stale reply state. headerSent is a function-local static in sendMspReply() (msp_shared.c:209) and is cleared only after the last chunk (:261). When a new request arrives while a reply is still being sent, handleMspFrame() discards that reply through initSharedMsp() (:119-125, :66-77), which does not touch headerSent. The first chunk of the next reply then takes the continuation branch (:237): status byte only, no size, no function byte. Both transports reach this: SmartPort passes every incoming MSP payload to handleMspFrame() while a reply is pending (smartport.c:450), and CRSF drains several queued requests in one loop (crsf.c:128-136).

Change

Moves the flag to file scope as replyHeaderSent and clears it in initSharedMsp(), so a reply that follows a discarded one starts with a full header again. Changes the chunk test at msp_shared.c:244 from >= to >, so a payload that exactly fills the frame is sent as the last chunk instead of being followed by a frame that holds only a continuation status byte.

Test

Not run on hardware or SITL. Cause verified by reading msp_shared.c:209-261, crsf.c:128-136 and smartport.c:450 on maintenance-10.x; src/test/ has no test that references msp_shared.c.

Flash / RAM

Not measured yet. The upstream firmware CI has not been released for this PR, so no size report exists.

Docs

No documentation change needed: nothing under docs/ describes the MSP-over-telemetry frame layout or chunking (checked docs/Telemetry.md, docs/Rx.md, docs/development/msp/).

sendMspReply() kept the "header already sent" flag in a function local
static that is cleared only after the last chunk of a reply. When a new
request arrives while a reply is still being sent, handleMspFrame()
discards that reply through initSharedMsp(), but the flag stayed set, so
the first chunk of the next reply went out as a continuation frame: a
status byte only, without the payload size and without the function
byte. A consumer cannot tell which request such a reply belongs to and
parses the payload at the wrong offset. Both transports reach this: the
SmartPort handler feeds every incoming MSP payload to handleMspFrame()
while a reply is pending, and the CRSF buffer loop processes several
queued requests in a row.

Move the flag to the module state and clear it in initSharedMsp(), so a
reply that starts after a discarded one carries a full header again:
status, size, function for MSPv1, status, flags, function, size for
MSPv2, matching the request layout described by MSP_INDEX_*.

Send a reply that exactly fills the last frame as the final chunk as
well, instead of announcing a further chunk that then carried nothing
but a continuation status byte.

Fixes iNavFlight#10667
@Raffi1202
Raffi1202 marked this pull request as ready for review September 11, 2026 15:40
@qodo-code-review

Copy link
Copy Markdown
Contributor

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Restart MSP telemetry reply framing after discarded replies

🐞 Bug fix 🕐 10-20 Minutes

Grey Divider

AI Description

• Resets reply-header state whenever a new request discards an in-flight telemetry reply.
• Restores complete MSPv1 and MSPv2 headers on the replacement reply's first chunk.
• Eliminates status-only trailing frames when payload data exactly fills the current chunk.
Diagram

graph TD
  A["Telemetry Request"] --> B["Handle Frame"] --> C["Reset Reply State"] --> D["Process Request"] --> E["Build Reply Chunk"] --> F{"More Than Capacity?"}
  F -->|Yes| G["Continuation Chunk"] --> E
  F -->|No| H["Final Chunk"]
Loading
High-Level Assessment

The minimal module-scoped state fix is appropriate because initialization is the lifecycle boundary that discards pending replies. Keeping the flag function-local could not support reset from initSharedMsp(), while introducing a new reply context object would add unnecessary structural churn for this isolated state.

Files changed (1) +7 / -5

Bug fix (1) +7 / -5
msp_shared.cReset reply framing state and remove empty trailing chunks +7/-5

Reset reply framing state and remove empty trailing chunks

• Moves the reply-header flag to module scope so initSharedMsp() can clear it whenever an in-flight response is discarded. It also treats an exact payload fit as the final chunk, preventing a subsequent status-only telemetry frame.

src/main/telemetry/msp_shared.c

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can switch off images and animations for a plain-text comment

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@sensei-hacker sensei-hacker added this to the 10.0 milestone Sep 20, 2026
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.

2 participants