Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/embassy-nucleo-h563zi/src/power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl<'d> UcpdSinkDriver<'d> {
}

impl SinkDriver for UcpdSinkDriver<'_> {
async fn wait_for_vbus(&self) {
async fn wait_for_vbus(&mut self) {
// The sink policy engine is only running when attached. Therefore VBus is present.
}

Expand Down
2 changes: 1 addition & 1 deletion examples/embassy-stm32-g431cb-epr/src/power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ impl<'d> UcpdSinkDriver<'d> {
}

impl SinkDriver for UcpdSinkDriver<'_> {
async fn wait_for_vbus(&self) {
async fn wait_for_vbus(&mut self) {
// The sink policy engine is only running when attached. Therefore VBus is present.
}

Expand Down
2 changes: 1 addition & 1 deletion examples/embassy-stm32-g431cb/src/power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl<'d> UcpdSinkDriver<'d> {
}

impl SinkDriver for UcpdSinkDriver<'_> {
async fn wait_for_vbus(&self) {
async fn wait_for_vbus(&mut self) {
// The sink policy engine is only running when attached. Therefore VBus is present.
}

Expand Down
9 changes: 8 additions & 1 deletion usbpd-traits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,15 @@ pub trait Driver {
/// GoodCRC messages and will instead rely on the hardware.
const HAS_AUTO_GOOD_CRC: bool = false;

/// If this is `true`, the hardware automatically retries transmission
/// when no GoodCRC is received. The protocol layer will skip its own
/// retry loop and call driver.transmit() directly. On success, it
/// maintains protocol state (TX message counter) without calling
/// wait_for_good_crc(), since the hardware already verified GoodCRC.
const HAS_AUTO_RETRY: bool = false;

/// Wait for availability of VBus voltage.
fn wait_for_vbus(&self) -> impl Future<Output = ()>;
fn wait_for_vbus(&mut self) -> impl Future<Output = ()>;

/// Receive a packet.
fn receive(&mut self, buffer: &mut [u8]) -> impl Future<Output = Result<usize, DriverRxError>>;
Expand Down
2 changes: 1 addition & 1 deletion usbpd/src/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ impl<const N: usize> Driver for DummyDriver<N> {
Ok(())
}

async fn wait_for_vbus(&self) {
async fn wait_for_vbus(&mut self) {
// Do nothing.
}
}
Expand Down
75 changes: 54 additions & 21 deletions usbpd/src/protocol_layer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,29 +296,50 @@ impl<DRIVER: Driver, TIMER: Timer> ProtocolLayer<DRIVER, TIMER> {
Self::validate_outgoing_message(&message)?;

trace!("Transmit message: {:?}", message);
self.counters.retry.reset();

let mut buffer = Self::get_message_buffer();
let size = message.to_bytes(&mut buffer);

loop {
match self.transmit_inner(&buffer[..size]).await {
Ok(_) => match self.wait_for_good_crc().await {
Ok(()) => {
trace!("Transmit success");
return Ok(());
}
Err(RxError::ReceiveTimeout) => match self.counters.retry.increment() {
Ok(_) => {
// Retry transmission, until the retry counter is exceeded.
}
Err(CounterError::Exceeded) => {
return Err(ProtocolError::TransmitRetriesExceeded(self.counters.retry.max_value()));
if DRIVER::HAS_AUTO_RETRY {
// Hardware handles retries and verifies GoodCRC reception.
// Call driver.transmit() directly (not transmit_inner()) because
// Discarded here means all hardware retries exhausted — no point
// retrying in software.
match self.driver.transmit(&buffer[..size]).await {
Ok(()) => {
self.counters.retry.reset();
_ = self.counters.tx_message.increment();
trace!("Transmit success (hardware retry)");
Ok(())
}
Err(DriverTxError::HardReset) => Err(TxError::HardReset.into()),
Err(DriverTxError::Discarded) => {
Err(ProtocolError::TransmitRetriesExceeded(self.counters.retry.max_value()))
}
}
} else {
// Software retry loop
self.counters.retry.reset();

loop {
match self.transmit_inner(&buffer[..size]).await {
Ok(_) => match self.wait_for_good_crc().await {
Ok(()) => {
trace!("Transmit success");
return Ok(());
}
Err(RxError::ReceiveTimeout) => match self.counters.retry.increment() {
Ok(_) => {
// Retry transmission, until the retry counter is exceeded.
}
Err(CounterError::Exceeded) => {
return Err(ProtocolError::TransmitRetriesExceeded(self.counters.retry.max_value()));
}
},
Err(other) => return Err(other.into()),
},
Err(other) => return Err(other.into()),
},
Err(other) => return Err(other.into()),
}
}
}
}
Expand Down Expand Up @@ -721,11 +742,23 @@ impl<DRIVER: Driver, TIMER: Timer> ProtocolLayer<DRIVER, TIMER> {
offset += 2;

// Transmit and wait for GoodCRC
match self.transmit_inner(&buffer[..offset]).await {
Ok(_) => self.wait_for_good_crc().await,
Err(TxError::HardReset) => Err(RxError::HardReset),
Err(TxError::UnchunkedExtendedMessagesNotSupported | TxError::AvsVoltageAlignmentInvalid) => {
unreachable!("validation should happen before transmit_inner")
if DRIVER::HAS_AUTO_RETRY {
match self.driver.transmit(&buffer[..offset]).await {
Ok(()) => {
self.counters.retry.reset();
_ = self.counters.tx_message.increment();
Ok(())
}
Err(DriverTxError::HardReset) => Err(RxError::HardReset),
Err(DriverTxError::Discarded) => Err(RxError::ReceiveTimeout),
}
} else {
match self.transmit_inner(&buffer[..offset]).await {
Ok(_) => self.wait_for_good_crc().await,
Err(TxError::HardReset) => Err(RxError::HardReset),
Err(TxError::UnchunkedExtendedMessagesNotSupported | TxError::AvsVoltageAlignmentInvalid) => {
unreachable!("validation should happen before transmit_inner")
}
}
}
}
Expand Down