This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit b850fc02059c761d53f3b53ec4b70475256843c8 Author: Daniel P. Carvalho <[email protected]> AuthorDate: Fri Sep 18 12:39:13 2026 -0300 arch/arm/stm32: implement hardware TX timestamping via SO_TIMESTAMPING loopback Implement hardware TX timestamping support for STM32 Ethernet MAC (stm32_eth_m3m4_v1.c) following the upstream SO_TIMESTAMPING loopback architecture (PR #20161). When an outgoing packet is flagged with SO_TIMESTAMPING (dev->d_iob->io_conn != NULL): - Clone the IOB and hold a reference in priv->txmeta[txindex] - Set ETH_TDES0_TTSE on the transmit DMA descriptor - On transmission completion (stm32_freeframe), retrieve the hardware timestamp from TDES6/TDES7, convert to timespec via ptp_to_timespec(), and enqueue the clone onto priv->txtstampq - Deliver pending TX timestamp clones back to netdev RX path in stm32_receive using pkt_input(), where net/pkt intercepts the frame and delivers it to userspace via recvmsg(..., MSG_ERRQUEUE) - Properly drain pending queues and clones on interface down (stm32_ifdown) Assisted-by: Gemini:gemini-3.8-pro Signed-off-by: Daniel P. Carvalho <[email protected]> --- arch/arm/src/common/stm32/Kconfig.eth | 8 ++ arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c | 122 ++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/arch/arm/src/common/stm32/Kconfig.eth b/arch/arm/src/common/stm32/Kconfig.eth index 7a028c19fd8..dabdbc78458 100644 --- a/arch/arm/src/common/stm32/Kconfig.eth +++ b/arch/arm/src/common/stm32/Kconfig.eth @@ -193,6 +193,14 @@ config STM32_ETH_TIMESTAMP_RX Timestamp all received Ethernet packets. Timestamp is available to application through SO_TIMESTAMP socket option. +config STM32_ETH_TIMESTAMP_TX + bool "Hardware timestamping of transmitted packets" + depends on STM32_COMMON_LEGACY && STM32_ETH_PTP && NET_TIMESTAMP && STM32_ETH_ENHANCEDDESC + default n + ---help--- + Enables hardware timestamp capture on transmitted Ethernet frames. + Timestamp is looped back to userspace via MSG_ERRQUEUE with SO_TIMESTAMPING. + config STM32_RMII bool default !STM32_MII diff --git a/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c b/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c index d68038d85d1..c072fc0ff0c 100644 --- a/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c +++ b/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c @@ -635,6 +635,13 @@ struct stm32_ethmac_s uint32_t rxtimelow; /* Received packet timestamp subsecond */ uint32_t rxtimehigh; /* Received packet timestamp seconds */ #endif +#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX + struct iob_queue_s txtstampq; /* Pending TX timestamp loopback packets */ + + /* Per-descriptor TX clone */ + + struct iob_s *txmeta[CONFIG_STM32_ETH_NTXDESC]; +#endif #if defined(CONFIG_STM32_ETH_PTP) && defined(CONFIG_PTP_CLOCK) struct ptp_lowerhalf_s ptp_lower; /* PTP hardware clock lower half */ #endif @@ -709,6 +716,9 @@ static int stm32_recvframe(struct stm32_ethmac_s *priv); static void stm32_receive(struct stm32_ethmac_s *priv); static void stm32_freeframe(struct stm32_ethmac_s *priv); static void stm32_txdone(struct stm32_ethmac_s *priv); +#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX +static void stm32_txtstamp_flush(struct stm32_ethmac_s *priv); +#endif static void stm32_interrupt_work(void *arg); static int stm32_interrupt(int irq, void *context, void *arg); @@ -1162,6 +1172,25 @@ static int stm32_transmit(struct stm32_ethmac_s *priv) txdesc->tdes0 |= (ETH_TDES0_FS | ETH_TDES0_LS | ETH_TDES0_IC); +#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX + { + uint32_t txindex = txdesc - g_txtable; + + priv->txmeta[txindex] = NULL; + if (priv->dev.d_iob != NULL && priv->dev.d_iob->io_conn != NULL) + { + struct iob_s *clone = netdev_iob_clone(&priv->dev, false); + + if (clone != NULL) + { + clone->io_conn = priv->dev.d_iob->io_conn; + priv->txmeta[txindex] = clone; + txdesc->tdes0 |= ETH_TDES0_TTSE; + } + } + } +#endif + /* Set frame size */ DEBUGASSERT(priv->dev.d_len <= CONFIG_STM32_ETH_BUFSIZE); @@ -1700,6 +1729,47 @@ static int stm32_recvframe(struct stm32_ethmac_s *priv) return -EAGAIN; } +/**************************************************************************** + * Function: stm32_txtstamp_flush + * + * Description: + * Deliver pending TX hardware timestamp loopback packets to the network + * stack. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX +static void stm32_txtstamp_flush(struct stm32_ethmac_s *priv) +{ + struct net_driver_s *dev = &priv->dev; + + while (!IOB_QEMPTY(&priv->txtstampq)) + { + struct iob_s *iob = iob_remove_queue(&priv->txtstampq); + + if (iob != NULL) + { + dev->d_iob = iob; + dev->d_len = iob->io_pktlen; +#ifdef CONFIG_NET_PKT + pkt_input(dev); +#endif + dev->d_iob = NULL; + dev->d_len = 0; + } + } +} +#endif + /**************************************************************************** * Function: stm32_receive * @@ -1721,6 +1791,12 @@ static void stm32_receive(struct stm32_ethmac_s *priv) { struct net_driver_s *dev = &priv->dev; +#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX + /* Return any TX hardware timestamp loopback packets first */ + + stm32_txtstamp_flush(priv); +#endif + /* Loop while while stm32_recvframe() successfully retrieves valid * Ethernet frames. */ @@ -1912,6 +1988,29 @@ static void stm32_freeframe(struct stm32_ethmac_s *priv) if ((txdesc->tdes0 & ETH_TDES0_LS) != 0) { +#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX + uint32_t tail = txdesc - g_txtable; + struct iob_s *clone = priv->txmeta[tail]; + + priv->txmeta[tail] = NULL; + if (clone != NULL) + { + if ((txdesc->tdes0 & ETH_TDES0_TTSS) != 0) + { + uint64_t hwtime = ((uint64_t)txdesc->tdes7 << 32) + | ((txdesc->tdes6 & ETH_PTPTSLR_MASK) + << 1); + + ptp_to_timespec(hwtime, &clone->io_time); + iob_add_queue(clone, &priv->txtstampq); + } + else + { + iob_free_chain(clone); + } + } +#endif + /* Yes.. Decrement the number of frames "in-flight". */ priv->inflight--; @@ -1975,6 +2074,12 @@ static void stm32_txdone(struct stm32_ethmac_s *priv) stm32_freeframe(priv); +#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX + /* Deliver any pending TX hardware timestamp loopback packets */ + + stm32_txtstamp_flush(priv); +#endif + /* If no further xmits are pending, then cancel the TX timeout */ if (priv->inflight <= 0) @@ -2341,6 +2446,9 @@ static int stm32_ifdown(struct net_driver_s *dev) (struct stm32_ethmac_s *)dev->d_private; irqstate_t flags; int ret = OK; +#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX + int i; +#endif ninfo("Taking the network down\n"); @@ -2378,6 +2486,20 @@ static int stm32_ifdown(struct net_driver_s *dev) "still assuming it's going down.\n"); } +#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX + /* Drain any pending TX timestamp loopback packets and clones */ + + iob_free_queue(&priv->txtstampq); + for (i = 0; i < CONFIG_STM32_ETH_NTXDESC; i++) + { + if (priv->txmeta[i] != NULL) + { + iob_free_chain(priv->txmeta[i]); + priv->txmeta[i] = NULL; + } + } +#endif + /* Mark the device "down" */ priv->ifup = false;
