Skip to main content

otPlatRadioScheduleTx

Function otPlatRadioScheduleTx 

Source
pub unsafe extern "C" fn otPlatRadioScheduleTx(
    aInstance: *mut otInstance,
    aTxTime: otRadioTime64,
) -> otError
Expand description

Schedule a radio frame transmission at a specific target time.

This API requires the radio platform to support the OT_RADIO_CAPS_SCHEDULED_TX capability. Its primary purpose is to support Thread Direct, though it may also be used for other time-sensitive transmission use cases in the future.

The caller forms the IEEE 802.15.4 frame in the buffer provided by otPlatRadioGetScheduledTxFrame() before calling this function to schedule the transmission at @p aTxTime.

The radio platform MUST execute the scheduled transmission according to the following rules:

  • Target Transmission Time and Strict On-Time Requirement:

    • The radio platform MUST transmit the requested frame at the exact specified time @p aTxTime.
    • @p aTxTime is an otRadioTime64 value in microseconds relative to the local radio clock (see otPlatRadioGetNow()).
    • @p aTxTime SHALL point to the exact point in time when the end of the Start of Frame Delimiter (SFD) will be present at the local antenna. The radio platform driver MUST account for any necessary radio ramp-up, channel switching, Clear Channel Assessment (CCA, if enabled), and SHR (preamble and SFD) transmission durations so that the end of the SFD aligns with @p aTxTime.
    • Strict on-time execution (no early or late transmission): It is critical that the frame is transmitted at @p aTxTime. If at the target transmission time the radio driver determines that it cannot transmit the frame at @p aTxTime (for example, due to Coex denial or hardware unavailability), it MUST NOT transmit the frame earlier or later. Instead, it MUST abort the transmission and invoke otPlatRadioScheduledTxDone() with OT_ERROR_ABORT to indicate that the frame was not sent.
    • Past or missed @p aTxTime at schedule time: The OpenThread stack calls otPlatRadioScheduleTx() as early as possible (as soon as it knows a scheduled transmission is needed), providing the radio platform with ample lead time ahead of @p aTxTime (for example, to account for radio ramp-up, Coex requests, etc.). However, @p aTxTime may still occasionally be in the past or too close to the current radio time otPlatRadioGetNow() (for example, if a frame is queued very close to the target slot, or if a blocking operation such as a flash re-write stalls execution). In this case, the radio platform MUST NOT transmit the frame. otPlatRadioScheduleTx() MUST still return OT_ERROR_NONE and asynchronously report the failure by invoking otPlatRadioScheduledTxDone() with OT_ERROR_ABORT. The radio platform MUST NOT invoke otPlatRadioScheduledTxDone() synchronously from within otPlatRadioScheduleTx(). If otPlatRadioCancelScheduledTx() is called before this deferred OT_ERROR_ABORT callback is invoked, the radio platform MAY either cancel the pending callback and return OT_ERROR_NONE, or return OT_ERROR_BUSY and proceed with invoking the otPlatRadioScheduledTxDone() callback.
  • Operational Precedence, Preemption, and State Restoration:

    • A scheduled transmission SHOULD be prioritized over other radio operations when @p aTxTime arrives:
      • Ongoing Receive or Acknowledgment TX: If the radio is in Receive mode and is in the middle of receiving a frame (e.g., after SHR detection) or transmitting an acknowledgment for a received frame when the radio needs to prepare/transmit for @p aTxTime, the scheduled transmission SHOULD take precedence—the active reception or acknowledgment transmission SHOULD be stopped/aborted so that the scheduled transmission goes out on time at @p aTxTime. When an ongoing reception or acknowledgment transmission is aborted, the radio platform MAY either invoke otPlatRadioReceiveDone() with OT_ERROR_ABORT or not invoke otPlatRadioReceiveDone() at all (if the radio platform still delivers a received frame whose acknowledgment transmission was aborted via otPlatRadioReceiveDone() with OT_ERROR_NONE, mAckedWithFramePending and mAckedWithSecEnhAck MUST be set to false).
      • Concurrent otPlatRadioTransmit(): If the radio is instructed to transmit a frame via otPlatRadioTransmit() close to or overlapping with a previously scheduled transmission, the scheduled transmission SHOULD take precedence and be performed at @p aTxTime. For the conflicting otPlatRadioTransmit() operation, the radio platform MAY either:
        • Abort the otPlatRadioTransmit() operation and invoke otPlatRadioTxDone() with OT_ERROR_ABORT (so the OpenThread stack does not remain blocked waiting for otPlatRadioTxDone() and can handle any retries), or
        • Defer the otPlatRadioTransmit() operation until after the scheduled transmission completes, provided that the otPlatRadioTransmit() has not yet started on the air (otPlatRadioTxStarted() has not been invoked) and is not a timed transmission (mTxDelay != 0) whose target time would be missed. If the otPlatRadioTransmit() has already started on the air or is a timed transmission whose target time would be missed, it MUST be aborted via otPlatRadioTxDone() with OT_ERROR_ABORT.
    • Radio State and Post-Transmission Restoration:
      • If this function is called while the radio is disabled (OT_RADIO_STATE_DISABLED), it MUST return OT_ERROR_INVALID_STATE. Calling otPlatRadioDisable() while a scheduled TX is pending or active MUST immediately abort and cancel it without invoking otPlatRadioScheduledTxDone() (and MUST NOT fail with OT_ERROR_INVALID_STATE due to the active scheduled TX when the underlying state is Sleep).
      • When the radio is enabled, otPlatRadioScheduleTx() can be called from any radio state (Sleep, Receive, or Transmit). Conversely, subsequent calls to otPlatRadioSleep(), otPlatRadioReceive(), otPlatRadioReceiveAt(), or otPlatRadioTransmit() while a scheduled TX is pending MUST NOT cancel or alter the pending scheduled transmission (only otPlatRadioCancelScheduledTx() or otPlatRadioDisable() cancels it).
      • If otPlatRadioSleep() or otPlatRadioReceive() is called while the scheduled transmission is actively in progress on the air (or waiting for its acknowledgment) at @p aTxTime, the radio platform MUST NOT abort the ongoing scheduled transmission and MUST NOT fail the call with OT_ERROR_BUSY or OT_ERROR_INVALID_STATE. Instead, it MUST return OT_ERROR_NONE, complete the scheduled transmission, and then transition to the newly requested state (Sleep or Receive on the requested channel).
      • Once the scheduled transmission completes (after transmitting the frame and receiving the acknowledgment or timing out when an acknowledgment is requested, or if CCA fails or the transmission is aborted), the radio platform MUST automatically return to its target state (the state prior to the scheduled transmission, or any updated state requested via otPlatRadioSleep() or otPlatRadioReceive() while the scheduled transmission was pending/active) and resume what it was doing:
        • If the target state is Receive on a channel (from otPlatRadioReceive()), it MUST return to Receive on that receive channel (mRxChannelAfterTxDone in otRadioFrame MUST be ignored).
        • If the target state is Sleep with an active timed reception window (from otPlatRadioReceiveAt() whose window has not yet expired), it MUST return to Receive on that window’s channel for the remaining duration of the window before transitioning back to Sleep.
        • If the target state is Sleep (and not in an active timed reception window), it MUST return to Sleep.
        • If a transmission requested via otPlatRadioTransmit() was deferred for the scheduled transmission, the radio platform SHOULD proceed with that transmission.
  • Single Active Schedule and Frame Buffer Ownership:

    • At most one scheduled transmission request can be active at any time, using the single otRadioFrame provided by otPlatRadioGetScheduledTxFrame().
    • After a successful otPlatRadioScheduleTx() call (and before it is canceled via otPlatRadioCancelScheduledTx() or otPlatRadioDisable(), or completed via otPlatRadioScheduledTxDone()), any new call to otPlatRadioScheduleTx() MUST be rejected by returning OT_ERROR_BUSY without affecting the active schedule. The OpenThread stack will generally not call otPlatRadioScheduleTx() again until the previous schedule is canceled or completed via otPlatRadioScheduledTxDone().
    • Upon a successful otPlatRadioScheduleTx() call, the radio platform takes ownership of the frame buffer from otPlatRadioGetScheduledTxFrame(). The OpenThread stack guarantees that it will not touch or modify the frame buffer or its contents while owned by the radio platform.
    • Invoking the otPlatRadioScheduledTxDone() callback, a successful otPlatRadioCancelScheduledTx() call (returning OT_ERROR_NONE), or otPlatRadioDisable() releases ownership of the frame buffer back to the OpenThread stack, allowing the stack to modify the frame again.
  • Handling of otRadioFrame (mTxInfo) fields:

    • mTxDelayBaseTime and mTxDelay: MUST be ignored. The target transmission time is specified solely by the @p aTxTime parameter.
    • mCsmaCaEnabled: Indicates whether CCA MUST be performed before transmitting the frame:
      • When set to false, the frame MUST be transmitted at @p aTxTime without performing CCA.
      • When set to true, CCA MUST be performed prior to transmission (timed so that the end of the SFD still occurs at @p aTxTime). If CCA fails, the radio platform MUST NOT transmit the frame and MUST report OT_ERROR_CHANNEL_ACCESS_FAILURE via otPlatRadioScheduledTxDone().
    • mMaxCsmaBackoffs: MUST always be ignored (treated as 0). The radio platform MUST NOT perform any CSMA backoff for a scheduled transmission.
    • mMaxFrameRetries: MUST always be ignored (treated as 0). A scheduled transmission is a single transmission attempt with no retries by the radio platform (the OpenThread stack manages any retransmissions in subsequent time slots).
    • mRxChannelAfterTxDone: MUST be ignored. Upon completing the scheduled transmission, the radio MUST return to its previous state and receive channel (if it was receiving).
    • mIsARetx and mCslPresent: MUST be ignored.
    • mTimestamp: When the frame is transmitted, the radio platform MUST update this field with the local radio clock timestamp (in microseconds) when the end of the SFD was present at the local antenna before invoking otPlatRadioScheduledTxDone().
    • mIsHeaderUpdated and mIsSecurityProcessed: Follow their existing behavior as defined for otPlatRadioTransmit() when OT_RADIO_CAPS_TRANSMIT_SEC is supported. When mIsHeaderUpdated is false, the radio platform MUST assign the MAC frame counter and perform security processing when preparing the frame for transmission at @p aTxTime (rather than when otPlatRadioScheduleTx() is called), ensuring that frame counters are used in order on the air if other transmissions or enhanced acknowledgments occur before @p aTxTime and that no frame counter is wasted if the scheduled transmission is canceled or aborted before the frame is prepared at @p aTxTime.
    • All other fields (mChannel, mAesKey, mIeInfo, mTxPower) MUST follow their existing behavior as defined for otPlatRadioTransmit().
  • Completion and Callbacks:

    • The radio platform MUST report the outcome of the scheduled transmission using the otPlatRadioScheduledTxDone() callback.
    • Aside from OT_ERROR_INVALID_STATE (when the radio is disabled) and OT_ERROR_BUSY (when a previously scheduled transmission is still active), otPlatRadioScheduleTx() MUST NOT return any other error synchronously. Any other runtime failure to schedule or transmit the frame (for example, if mChannel on the scheduled TX frame is invalid or unsupported) MUST still return OT_ERROR_NONE from otPlatRadioScheduleTx() and be reported asynchronously by invoking otPlatRadioScheduledTxDone() with OT_ERROR_ABORT.
    • A scheduled transmission MUST NOT invoke otPlatRadioTxStarted() or otPlatRadioTxDone(). Those callbacks are used exclusively for transmissions initiated via otPlatRadioTransmit().

Why a dedicated scheduled TX API is used (and comparison with OT_RADIO_CAPS_TRANSMIT_TIMING):

  • Coexistence (Coex) and Radio Arbitration: While the OpenThread stack can track time and schedule radio operations in software, it does not have visibility into platform-level radio coexistence (Coex) with other radios sharing the same device or antenna (such as Wi-Fi or Bluetooth/BLE). The radio platform driver is responsible for managing Coex arbitration and is therefore in a much better position to request appropriate Coex priority ahead of time and ensure the scheduled transmission goes out at the exact specified target time @p aTxTime. Similarly, the radio platform can directly preempt ongoing radio activity (such as an active frame reception, acknowledgment transmission, or a regular otPlatRadioTransmit() operation) when @p aTxTime arrives.

  • Differences from OT_RADIO_CAPS_TRANSMIT_TIMING (mTxDelayBaseTime and mTxDelay): OT_RADIO_CAPS_TRANSMIT_TIMING was originally introduced to support CSL in Thread 1.2 by extending the existing otPlatRadioTransmit() API using mTxDelayBaseTime and mTxDelay. That model has several limitations for more strict protocols like Thread Direct:

    • Existing vendor implementations of OT_RADIO_CAPS_TRANSMIT_TIMING vary in their behavior when the target transmission time is missed (for example, some implementations still transmit the frame after a delay even if the target time has passed). While a slightly delayed transmission may still fall within a peer’s CSL sample window, transmitting late or outside the designated time slot in Thread Direct could result in unpredictable failures. otPlatRadioScheduleTx() defines strict, explicit requirements on timing accuracy, operational precedence, and missed-target handling.
    • otPlatRadioTransmit() is a blocking state-transition operation: once called, the OpenThread stack must wait for otPlatRadioTxDone() before it can issue any other transmit, receive, or sleep commands to the radio (which also forces the stack to wait until just before the target time to issue a timed otPlatRadioTransmit() so the radio is not tied up prematurely). In contrast, otPlatRadioScheduleTx() schedules a future transmission using a dedicated frame buffer as soon as the stack knows a scheduled transmission is needed—allowing the radio to continue other operations in the meantime and supporting explicit cancellation via otPlatRadioCancelScheduledTx().

@param[in] aInstance The OpenThread instance structure. @param[in] aTxTime The target transmission time (end of SFD at the local antenna) in microseconds relative to the local radio clock (see otPlatRadioGetNow()).

@retval OT_ERROR_NONE Successfully scheduled the frame transmission. @retval OT_ERROR_BUSY A previously scheduled transmission is still active. @retval OT_ERROR_INVALID_STATE The radio is disabled.