Merged
Conversation
- policy engine states for enter/exit of EPR mode - consistent naming of modules - extend DPM
- add docstrings
Implements USB PD 3.x chunked extended message support per Section 6.13. Extended messages can exceed the standard packet size and are split into chunks of up to 26 bytes each. - Add ChunkedMessageAssembler for assembling multi-chunk messages - Add ExtendedHeader bitfield for extended message headers - Extend protocol layer to handle chunked message assembly and disassembly - Add parse_extended_chunk and parse_extended_payload helpers - Increase MAX_MESSAGE_SIZE to 272 bytes to support extended messages - Add extended message receive buffer and chunking state tracking - Refactor message ACK handling into separate handle_rx_ack method
Implements Extended Power Range (EPR) message types per USB PD 3.x spec. EPR mode enables power delivery beyond standard 100W limit. - Add EPR_Request data message type with RDO + PDO pair - Add EPR_Mode data message parsing and encoding - Add parse_raw_pdo helper to deduplicate PDO parsing logic - Extend SourceCapabilities to support up to 16 PDOs (EPR requires more) - Add message_type() and num_objects() helpers to PowerSource - Rename find_pps_voltage to find_augmented_pdo (handles both PPS and EPR AVS) - Fix message_type() to check extended bit before num_objects (EPR extended messages can have data objects) - Add ExtendedControl message parsing - Update PDO_SIZE constant for clarity
Implements the EPR mode state machine in the sink policy engine, completing the EPR support. This enables the sink to enter EPR mode, request EPR power levels, and maintain EPR mode with keep-alive messages. - Implement EPR entry states (_EprSendEntry, _EprEntryWaitForResponse) - Implement EPR keep-alive periodic timer and state - Implement EPR exit handling (_EprExitReceived) - Add EPR mode tracking and PS_TRANSITION timer selection based on mode - Handle EPR_Request power source selection in state machine - Add EPR source capabilities reception (extended message) - Add EPR_Mode data message handling (source exit notification) - Combine PPS and EPR keep-alive timers using select() - Reset mode to SPR on startup - Add transmit_epr_mode and transmit_extended_control_message methods - Update wait_for_source_capabilities to handle EPR extended messages
This commit completes the EPR (Extended Power Range) support for the USB PD sink implementation, enabling negotiation up to 240W power delivery. Key changes: - Renamed EPR state variants (removed underscore prefixes): EprKeepAlive, EprSendExit, EprExitReceived are now fully implemented - Added ExitEprMode DPM event to enable sink-initiated EPR exit - Implemented DummySinkEprDevice.request() to select highest EPR PDO (48V) - Created comprehensive integration test covering full EPR negotiation flow: * Phase 1: SPR negotiation (5V-20V) * Phase 2: EPR mode entry (Enter→EnterAck→EnterSucceeded) * Phase 3: Chunked EPR source capabilities assembly * Phase 4: EPR power negotiation (48V @ 5A = 240W) Technical details: - EPR request construction: Creates RDO + PDO copy per spec Section 6.4.9 - Test harness properly simulates GoodCRC and control message timing - All existing tests continue to pass Spec compliance (USB PD R3.2 Section 6.5.15.1): - Add SourceCapabilities helpers for spec-compliant PDO access: * is_zero_padding() - detect zero-filled entries * is_epr_capabilities() - check if EPR message (>7 PDOs) * spr_pdos() - positions 1-7 (SPR PDOs only) * epr_pdos() - positions 8+ (EPR PDOs only) - EPR PDOs always start at position 8 per spec Also includes clippy fixes and nightly rustfmt formatting.
Fix hard reset handling to comply with USB PD Spec R3.2: PE_SNK_Hard_Reset (Section 8.3.3.3.8): - Check HardResetCounter before transmitting (not after) - Transmit Hard Reset Signaling via protocol layer - Return PortPartnerUnresponsive if counter exceeded PE_SNK_Transition_to_default (Section 8.3.3.3.9): - Add DPM notification via new hard_reset() callback - Reset protocol layer (per spec 6.8.3) - Exit EPR mode (per spec 6.8.3.2) - Reset contract to Safe5V - Clear cached source capabilities DevicePolicyManager trait: - Add hard_reset() callback for DPM notification - Default implementation is no-op for backwards compatibility
13fa0b2 to
d84d3f7
Compare
- Add SinkCapabilities structure with Fixed, Variable, and Battery PDOs per USB PD Spec R3.2 Section 6.4.1.6 - Add sink_capabilities() method to DevicePolicyManager trait with default 5V @ 100mA implementation - Implement proper GiveSinkCap state to respond to Get_Sink_Cap messages (previously sent NotSupported which was not spec-compliant) - Add get_source_cap_pending flag to track pending source cap requests - Implement hard reset for unrequested Source_Capabilities in EPR mode per spec section 8.3.3.3.8
Reorder match arms in error handler so TransitionSink state is checked before the generic UnexpectedMessage handler. Per USB PD Spec R3.2 Table 6.72, unexpected messages during power transition (PE_SNK_Transition_Sink) shall trigger Hard Reset, not Soft Reset. Also improve comments with proper spec references throughout the error handling logic.
Per USB PD Spec R3.2: - EPR mode entry failure now triggers Soft Reset (not Hard Reset) per sections 8.3.3.26.2.1 and 8.3.3.26.2.2 - Add EPR_Get_Sink_Cap handling: respond with EPR_Sink_Capabilities per sections 8.3.3.3.7 and 8.3.3.3.10 - Fix GetSourceCap transition logic: go to EvaluateCapabilities when mode matches, Ready otherwise, per section 8.3.3.3.12 - Add EPR PDO position validation: Hard Reset if EPR PDO found in positions 1-7 of EPR_Source_Capabilities, per section 8.3.3.3.8
Two spec compliance fixes: 1. wait_for_source_capabilities helper now handles both Source_Capabilities and EPR_Source_Capabilities messages. EPR Mode persists through Soft Reset (per spec 6.8.3.2), so after a Soft Reset while in EPR Mode, the source sends EPR_Source_Capabilities (per spec 6.4.1.2.2). Previously this would panic on unreachable!(). 2. EprExitReceived state now checks if current contract is for SPR or EPR PDO per spec 8.3.3.26.4.2: - SPR PDO contract (positions 1-7): transition to WaitForCapabilities - EPR PDO contract (positions 8+): trigger Hard Reset This is a safety requirement - if at 28V/36V/48V when Exit is received, the sink cannot safely continue at that voltage in SPR mode.
Per spec sections 6.6.4.1 and 8.3.3.3.7, after receiving a Wait message in response to a Request, the sink must wait at least tSinkRequest (100ms) before re-requesting. This is a Shall requirement. Changes: - Add `after_wait` bool to Ready state to track if entered due to Wait - When entering Ready after Wait, run SinkRequestTimer before allowing re-request (transitions to SelectCapability on timeout) - Distinguish between Wait and Reject responses in SelectCapability
Two bugs fixed: 1. transmit_extended_control_message: Set num_objects to 1 instead of 0. Per USB PD spec 6.2.1.1.2, extended messages must have non-zero NumDataObjects. ExtendedControl messages are 4 bytes (2-byte extended header + 2-byte ECDB), which equals 1 data object. 2. transmit_chunk_request: Add 2 bytes of zero padding after the extended header. Per USB PD spec 6.2.1.2, chunked messages must be padded to the next 4-byte Data Object boundary. Without this padding, the UCPD hardware would read garbage bytes from the DMA buffer. These fixes ensure compatibility with strict USB PD implementations (e.g., Anker powerbanks) that validate the entire message format.
Per USB PD spec 6.4.10 Table 6.50, the Data field in EPR_Mode(Enter) "Shall be set to the EPR Sink Operational PDP". Previously this was hardcoded to 0. Changed Event::EnterEprMode to take a Power parameter so the caller specifies their operational PDP. For example, a 28V × 5A = 140W sink should pass Power::new::<watt>(140).
Per USB PD spec 6.2.1.2.1, chunked mode is required when either port partner only supports chunked messages. Most power supplies (and PHYs like FUSB302) don't support unchunked extended messages. Changes: - Set Chunked bit to 1 in extended message headers - Set unchunked_extended_messages_supported to false in RDOs - Add warning comments to unchunked_extended_messages_supported field
Demonstrates USB PD Extended Power Range (EPR) negotiation: - Initial SPR power negotiation with EPR capable flag - Automatic EPR mode entry when source is EPR capable - Requesting 28V @ 4A (112W) EPR power - Pretty-printing source capabilities with PDO details Based on the embassy-stm32-g431cb example.
- Refactor reset() to reuse new() constructor to prevent sync issues Using *self = Self::new() ensures the initialization logic is in one place - Add ParseError::ParserReuse variant to prevent silent data loss Instead of auto-resetting when chunk 0 arrives during assembly, return an error forcing explicit state management - Add new_from_chunk() constructor for convenient assembler initialization Allows creating and initializing an assembler from chunk 0 in one call - Add comprehensive tests for new error cases and API usage patterns This addresses elagil's concern about reset/new getting out of sync and improves safety by making parser state management explicit.
- Add ParseError::ChunkOverflow variant with actual and max size - Validate chunk size before copying (must be <= 26 bytes per spec) - Return error instead of silently truncating oversized chunks - Add test case for chunk overflow error Per USB PD spec, extended message chunks must not exceed 26 bytes. Previously we silently truncated, now we return an explicit error.
Replace manual byte-by-byte loop with idiomatic extend_from_slice(). More concise and clearer intent. Note: Chunk gapless validation is already in place at line 215-216, where we verify chunk_number == self.next_chunk, ensuring sequential ordering without gaps as required by USB PD spec.
Make ChunkedMessageSender more idiomatic by implementing the Iterator trait.
This allows users to use standard iterator methods like for loops, map, etc.
- Implement Iterator with Item = (ExtendedHeader, &'a [u8])
- Add size_hint() for optimization (exact size known upfront)
- Keep next_chunk() as a convenience wrapper
- Add tests demonstrating iterator usage patterns
Example usage:
for (ext_header, chunk_data) in sender {
// process chunk
}
Now that ChunkedMessageSender implements Iterator, the next_chunk() method is redundant - users can just call .next() directly. This eliminates code duplication and makes the API cleaner. - Removed next_chunk() method (logic is in Iterator::next()) - Updated tests to use .next() instead of .next_chunk() - Keeps get_chunk() for random access (responding to chunk requests)
Fix import ordering and code formatting to pass CI checks.
- Collapse nested match statements in validate_outgoing_message - Fix trailing whitespace in power.rs - Apply rustfmt to all code
Clippy detected that .get::<unit>() already returns u32, so the explicit casts were redundant.
elagil
reviewed
Jan 22, 2026
elagil
reviewed
Jan 22, 2026
elagil
reviewed
Jan 22, 2026
elagil
reviewed
Jan 22, 2026
elagil
reviewed
Jan 22, 2026
elagil
reviewed
Jan 22, 2026
elagil
reviewed
Jan 22, 2026
elagil
reviewed
Jan 25, 2026
elagil
reviewed
Jan 25, 2026
elagil
reviewed
Jan 25, 2026
elagil
reviewed
Jan 25, 2026
elagil
reviewed
Jan 25, 2026
elagil
reviewed
Jan 25, 2026
elagil
reviewed
Jan 29, 2026
elagil
reviewed
Jan 29, 2026
- Remove unused _transmit_data_message function (dead code with bug) - Add Default derive to ExtendedHeader, use default() instead of new(0) - Simplify match block in transmit_chunk_request - Clarify comment about num_objects for unchunked extended messages - Add MAX_DATA_MESSAGE_SIZE constant to replace magic number 30 - Simplify EPR PDO search using PowerSource::new_fixed()
This was referenced Feb 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Continuing the work in #37
Closes #36
Tested on STM32G431.