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

  1. Call start_ptp at most once per instance; store and reuse the returned PtpClock.
  2. Drop the previous PtpClock before starting again, if the driver releases the resource on drop.
  3. 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

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)