embassy-rs/embassy · error
Ethernet PTP clock already started
Error message
Ethernet PTP clock already started
What it means
The Ethernet MAC's PTP clock is a singleton per instance, guarded by a ptp_clock_taken flag. Calling start_ptp() a second time without releasing the returned PtpClock panics, since exclusive ownership would be violated.
Solutions
- Call start_ptp at most once per instance; store and reuse the returned PtpClock.
- Drop the previous PtpClock before starting again, if the driver releases the resource on drop.
- Restructure so PTP start happens once in a single init path.
Example fix
// before let ptp = eth.start_ptp(cfg); let ptp2 = eth.start_ptp(cfg); // panics // after let ptp = eth.start_ptp(cfg); drop(ptp); let ptp = eth.start_ptp(cfg);
Defensive patterns
Strategy: validation
Validate before calling
static PTP_STARTED: AtomicBool = AtomicBool::new(false);
fn start_ptp_once(eth: &mut Ethernet) -> PtpClock<...> {
assert!(!PTP_STARTED.swap(true, Ordering::SeqCst), "PTP clock already started");
eth.start_ptp(ptp_cfg)
} Prevention
- Start PTP exactly once, from a single init path.
- Store the PtpClock in a OnceCell/static and reuse it.
- Drop the PtpClock before any restart attempt.
When it happens
Trigger: Calling eth.start_ptp(config) twice on the same instance; restarting PTP without dropping the previous PtpClock; re-running init code that starts PTP again.
Common situations: Retry/reconnect logic reinitializing the network stack; calling start_ptp from two tasks; forgetting the singleton nature.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/dae3d180cf27b147.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/eth/v1/mod.rs:430
Flex::new(rx_d0),
Flex::new(rx_d1),
Flex::new(rx_d2),
Flex::new(rx_d3),
Flex::new(tx_d0),
Flex::new(tx_d1),
Flex::new(tx_d2),
Flex::new(tx_d3),
Flex::new(tx_en),
]);
Self::new_inner(queue, peri, irq, pins, phy, mac_addr, false)
}
/// Start the Ethernet MAC PTP clock.
#[cfg(feature = "ptp")]
pub fn start_ptp(&mut self, config: PtpClockConfig) -> PtpClock<T> {
if self.ptp_clock_taken {
panic!("Ethernet PTP clock already started");
}
let clock = PtpClock::start(config);
self.ptp_clock_taken = true;
clock
}
}
impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> {
fn drop(&mut self) {
let dma = T::regs().ethernet_dma();
let mac = T::regs().ethernet_mac();
// Disable the TX DMA and wait for any previous transmissions to be completed
dma.dmaomr().modify(|w| w.set_st(St::Stopped));
// Disable MAC transmitter and receiver
mac.maccr().modify(|w| {View on GitHub (pinned to 463a07b963)