Skip to content

Commit 6161ce9

Browse files
committed
[thread-direct] API surface for TD link establishment
1 parent e753891 commit 6161ce9

19 files changed

Lines changed: 1210 additions & 105 deletions

include/openthread/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ source_set("openthread") {
113113
"platform/settings.h",
114114
"platform/spi-slave.h",
115115
"platform/tcp.h",
116+
"platform/thread_direct.h",
116117
"platform/time.h",
117118
"platform/toolchain.h",
118119
"platform/trel.h",
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Copyright (c) 2025, The OpenThread Authors.
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
* 3. Neither the name of the copyright holder nor the
13+
* names of its contributors may be used to endorse or promote products
14+
* derived from this software without specific prior written permission.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
*/
28+
29+
/**
30+
* @file
31+
* This file includes the platform abstraction for Thread Direct link operations.
32+
*/
33+
34+
#ifndef OPENTHREAD_PLATFORM_THREAD_DIRECT_H_
35+
#define OPENTHREAD_PLATFORM_THREAD_DIRECT_H_
36+
37+
#include <stdint.h>
38+
39+
#include <openthread/error.h>
40+
#include <openthread/instance.h>
41+
#include <openthread/thread_direct.h>
42+
#include <openthread/platform/radio.h>
43+
44+
#ifdef __cplusplus
45+
extern "C" {
46+
#endif
47+
48+
/**
49+
* @addtogroup plat-thread-direct
50+
*
51+
* @brief
52+
* This module includes the platform abstraction for Thread Direct.
53+
*
54+
* @{
55+
*
56+
*/
57+
58+
/**
59+
* Pre-configures the IE payload to inject into the Enh-ACK sent in response
60+
* to a TD Link Command frame received from @p aWiExtAddress.
61+
*
62+
* The stack calls this during the connection window, before the TD Link Command
63+
* is expected, because the Enh-ACK must be transmitted within the 192 us
64+
* IEEE 802.15.4 turnaround window.
65+
*
66+
* Pass @p aIeData = NULL / @p aIeLength = 0 to remove a previously registered
67+
* entry for @p aWiExtAddress.
68+
*
69+
* @param[in] aInstance The OpenThread instance.
70+
* @param[in] aWiExtAddress Extended address of the WI peer.
71+
* @param[in] aIeData IE payload bytes to inject, or NULL to remove.
72+
* @param[in] aIeLength Length of @p aIeData in bytes (0 to remove).
73+
*
74+
* @retval OT_ERROR_NONE Entry registered or removed successfully.
75+
* @retval OT_ERROR_NO_BUFS Platform Enh-ACK IE table is full.
76+
* @retval OT_ERROR_NOT_FOUND @p aIeData is NULL and no entry exists for @p aWiExtAddress.
77+
* @retval OT_ERROR_NOT_IMPLEMENTED Feature is not implemented.
78+
*/
79+
otError otPlatRadioConfigureThreadDirectEnhAckIe(otInstance *aInstance,
80+
const otExtAddress *aWiExtAddress,
81+
const uint8_t *aIeData,
82+
uint16_t aIeLength);
83+
84+
/**
85+
* Enables or disables the Scheduled Listen Window (SLW) receive schedule for
86+
* the Thread Direct link with @p aWiExtAddress.
87+
*
88+
* Called by the WL after a TD link is established to program the platform with
89+
* its SLW period. Pass @p aSlwPeriod = 0 to disable SLW scheduling for this
90+
* peer.
91+
*
92+
* @param[in] aInstance The OpenThread instance.
93+
* @param[in] aSlwPeriod SLW period in 160 us slots (0 = disable).
94+
* @param[in] aWiExtAddress WI peer extended address.
95+
*
96+
* @retval OT_ERROR_NONE SLW schedule updated.
97+
* @retval OT_ERROR_NOT_IMPLEMENTED Feature is not implemented.
98+
*/
99+
otError otPlatRadioEnableThreadDirectSlw(otInstance *aInstance, uint16_t aSlwPeriod, const otExtAddress *aWiExtAddress);
100+
101+
/**
102+
* Updates the next SLW sample time for the Thread Direct link with @p aWiExtAddress.
103+
*
104+
* Called after each received or missed frame to advance the window to the next
105+
* expected arrival time. @p aSlwSampleTime is in microseconds on the local
106+
* radio clock (see `otPlatRadioGetNow()`).
107+
*
108+
* @param[in] aInstance The OpenThread instance.
109+
* @param[in] aWiExtAddress WI peer extended address.
110+
* @param[in] aSlwSampleTime Next expected frame arrival time in us.
111+
*/
112+
void otPlatRadioUpdateThreadDirectSlwSampleTime(otInstance *aInstance,
113+
const otExtAddress *aWiExtAddress,
114+
uint32_t aSlwSampleTime);
115+
116+
/**
117+
* Returns the worst-case clock accuracy of the local radio in PPM for Thread
118+
* Direct SLW transmit scheduling.
119+
*
120+
* Used by the WI to calculate the guard time when scheduling a unicast TX to
121+
* arrive within the WL's SLW window.
122+
*
123+
* @param[in] aInstance The OpenThread instance.
124+
*
125+
* @returns Worst-case clock accuracy in PPM.
126+
*/
127+
uint8_t otPlatRadioGetThreadDirectSlwAccuracy(otInstance *aInstance);
128+
129+
/**
130+
* Returns the fixed arrival-time uncertainty for Thread Direct SLW frames in
131+
* units of 10 us.
132+
*
133+
* Used by the WI alongside `otPlatRadioGetThreadDirectSlwAccuracy()` to compute
134+
* the total guard window.
135+
*
136+
* @param[in] aInstance The OpenThread instance.
137+
*
138+
* @returns Fixed SLW arrival-time uncertainty in units of 10 us.
139+
*/
140+
uint8_t otPlatRadioGetThreadDirectSlwUncertainty(otInstance *aInstance);
141+
142+
/**
143+
* Retrieves the current Radio Availability Mask (RAM) parameters from the
144+
* platform radio driver.
145+
*
146+
* Called when `OPENTHREAD_CONFIG_THREAD_DIRECT_COEX_ENABLE` is set. The
147+
* platform fills @p aParams with the current CoEx constraints (bitmap of
148+
* unavailable slots within the SLW period, signed offset, and RAM Duration
149+
* code). Platforms without CoEx support may return `OT_ERROR_NOT_IMPLEMENTED`.
150+
*
151+
* @param[in] aInstance The OpenThread instance.
152+
* @param[out] aParams Filled with current CoEx constraints.
153+
*
154+
* @retval OT_ERROR_NONE @p aParams populated.
155+
* @retval OT_ERROR_NOT_IMPLEMENTED Feature is not implemented.
156+
*/
157+
otError otPlatRadioGetThreadDirectRamParams(otInstance *aInstance, otThreadDirectRamParams *aParams);
158+
159+
/**
160+
* @}
161+
*
162+
*/
163+
164+
#ifdef __cplusplus
165+
} // extern "C"
166+
#endif
167+
168+
#endif // OPENTHREAD_PLATFORM_THREAD_DIRECT_H_

include/openthread/thread_direct.h

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,10 @@ typedef struct otThreadDirectPeerInfo
9191
*/
9292
typedef enum otThreadDirectEvent
9393
{
94-
OT_THREAD_DIRECT_EVENT_LINK_FAILED = 0, ///< Wake attempt failed (timeout or authentication failure).
95-
OT_THREAD_DIRECT_EVENT_WAKE_RECEIVED = 1, ///< WL received a TD Wake Command from a WI peer.
94+
OT_THREAD_DIRECT_EVENT_LINKED = 0, ///< Thread Direct link successfully established.
95+
OT_THREAD_DIRECT_EVENT_LINK_FAILED = 1, ///< Wake attempt failed (timeout or authentication failure).
96+
OT_THREAD_DIRECT_EVENT_UNLINKED = 2, ///< Thread Direct link torn down (supervision timeout or teardown frame).
97+
OT_THREAD_DIRECT_EVENT_WAKE_RECEIVED = 3, ///< WL received a TD Wake Command from a WI peer.
9698
} otThreadDirectEvent;
9799

98100
/**
@@ -141,7 +143,8 @@ typedef enum
141143
* Transmits Wake Frames of type @p aWakeType at @p aIntervalUs for @p aDurationMs.
142144
* When @p aWakeType is OT_THREAD_DIRECT_WAKE_TYPE_LINK, a connection window is opened
143145
* after the burst; the registered event callback (see otThreadDirectSetEventCallback())
144-
* fires OT_THREAD_DIRECT_EVENT_LINK_FAILED if the window expires without a response.
146+
* fires OT_THREAD_DIRECT_EVENT_LINKED on receipt of a TD Link Command, or
147+
* OT_THREAD_DIRECT_EVENT_LINK_FAILED if the window expires without a response.
145148
*
146149
* Pass @p aIntervalUs = 0 to use OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INTERVAL_US.
147150
* Pass @p aDurationMs = 0 to use OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_DURATION_MS.
@@ -228,6 +231,127 @@ bool otThreadDirectIsWakeListenerEnabled(otInstance *aInstance);
228231
*/
229232
bool otThreadDirectIsWakeBurstActive(otInstance *aInstance);
230233

234+
/**
235+
* Configures the Scheduled Listen Window (SLW) period this device advertises to
236+
* its peer in the SCA LTV. Both WI and WL may call this function.
237+
*
238+
* Requires `OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE` or
239+
* `OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_LISTENER_ENABLE`.
240+
*
241+
* Phase is a dynamic, stack-computed value (time until the next SLW window at
242+
* frame-build time) and is not configurable by the application.
243+
*
244+
* Passing @p aSlwPeriodSlots = 0 clears the schedule and causes a teardown SCA LTV
245+
* (empty payload) to be sent in the next frame to each peer.
246+
*
247+
* @param[in] aInstance The OpenThread instance.
248+
* @param[in] aSlwPeriodSlots SLW period in 160 us slots (0 = clear schedule).
249+
*
250+
* @retval OT_ERROR_NONE Schedule updated.
251+
* @retval OT_ERROR_INVALID_ARGS @p aSlwPeriodSlots is non-zero and below the minimum.
252+
*/
253+
otError otThreadDirectSetSlwSchedule(otInstance *aInstance, uint16_t aSlwPeriodSlots);
254+
255+
/**
256+
* Represents the Radio Availability Mask (RAM) parameters this device will
257+
* advertise in outgoing SCA LTVs.
258+
*
259+
* RAM Duration semantics (valid stored values):
260+
* 1 - device has no CoEx constraints (no bitmap transmitted); this is the default.
261+
* 2-31 - CoEx bitmap present; (mDuration + 1) bits are valid in mBits.
262+
*
263+
* Note: mDuration = 0 ("no change to prior RAM") is a wire-encoding sentinel and
264+
* is NOT a valid value for otThreadDirectSetRamOverride().
265+
*/
266+
typedef struct otThreadDirectRamParams
267+
{
268+
int16_t mOffsetUs; ///< RAM Offset in us, signed [-1024, 1023].
269+
uint8_t mDuration; ///< RAM Duration code (1 = no constraints, 2-31 = CoEx bitmap).
270+
uint8_t mBits[4]; ///< RAM bitmap bytes (used when mDuration >= 2).
271+
} otThreadDirectRamParams;
272+
273+
/**
274+
* Represents the full local SCA state as advertised in outgoing SCA LTVs.
275+
*/
276+
typedef struct otThreadDirectLocalSca
277+
{
278+
uint16_t mSlwPeriodSlots; ///< SLW period in 160 us slots (0 = not configured).
279+
otThreadDirectRamParams mRam; ///< RAM parameters.
280+
} otThreadDirectLocalSca;
281+
282+
/**
283+
* Overrides the RAM parameters this device advertises in outgoing SCA LTVs.
284+
*
285+
* Intended for testing and for platforms where the CoEx schedule is managed at
286+
* the application layer. When `OPENTHREAD_CONFIG_THREAD_DIRECT_COEX_ENABLE`
287+
* is enabled, the stack obtains RAM parameters from the platform radio driver
288+
* at SCA LTV build time; this override does not apply.
289+
*
290+
* Pass @p aParams->mDuration = 1 to explicitly clear CoEx constraints.
291+
* @p aParams->mDuration = 0 is invalid (wire-encoding sentinel; not storable).
292+
*
293+
* @param[in] aInstance The OpenThread instance.
294+
* @param[in] aParams RAM parameters to store.
295+
*
296+
* @retval OT_ERROR_NONE Parameters stored.
297+
* @retval OT_ERROR_INVALID_ARGS @p aParams is NULL, @p aParams->mDuration is 0,
298+
* @p aParams->mDuration is outside [1, 31], or
299+
* @p aParams->mOffsetUs is outside [-1024, 1023].
300+
*/
301+
otError otThreadDirectSetRamOverride(otInstance *aInstance, const otThreadDirectRamParams *aParams);
302+
303+
/**
304+
* Gets the local SCA state (SLW schedule and RAM parameters) this device advertises.
305+
*
306+
* @param[in] aInstance The OpenThread instance.
307+
* @param[out] aLocalSca Populated with the local SCA state.
308+
*
309+
* @retval OT_ERROR_NONE @p aLocalSca populated.
310+
*/
311+
otError otThreadDirectGetLocalSca(otInstance *aInstance, otThreadDirectLocalSca *aLocalSca);
312+
313+
/**
314+
* Returns the SLW link inactivity timeout in seconds.
315+
*
316+
* The timeout is the number of seconds without receiving a unicast frame from
317+
* a WI peer before the stack tears down the TD link and stops the SLW schedule.
318+
* 0 means the timeout has not been explicitly set and the default from
319+
* OPENTHREAD_CONFIG_THREAD_DIRECT_SLW_TIMEOUT applies.
320+
*
321+
* @param[in] aInstance The OpenThread instance.
322+
*
323+
* @returns The current SLW timeout in seconds.
324+
*/
325+
uint32_t otThreadDirectGetSlwTimeout(otInstance *aInstance);
326+
327+
/**
328+
* Sets the SLW link inactivity timeout in seconds.
329+
*
330+
* @param[in] aInstance The OpenThread instance.
331+
* @param[in] aTimeout Timeout in seconds. 0 restores the compile-time
332+
* default (OPENTHREAD_CONFIG_THREAD_DIRECT_SLW_TIMEOUT).
333+
*
334+
* @retval OT_ERROR_NONE Timeout updated.
335+
* @retval OT_ERROR_INVALID_ARGS @p aTimeout exceeds
336+
* OPENTHREAD_CONFIG_THREAD_DIRECT_SLW_MAX_TIMEOUT.
337+
*/
338+
otError otThreadDirectSetSlwTimeout(otInstance *aInstance, uint32_t aTimeout);
339+
340+
/**
341+
* Gets the current SLW schedule and link state of an established Thread Direct peer.
342+
*
343+
* @param[in] aInstance The OpenThread instance.
344+
* @param[in] aExtAddress Extended address of the peer.
345+
* @param[out] aPeerInfo Output structure populated with peer state.
346+
*
347+
* @retval OT_ERROR_NONE @p aPeerInfo populated.
348+
* @retval OT_ERROR_NOT_FOUND No established link to @p aExtAddress.
349+
* @retval OT_ERROR_NOT_IMPLEMENTED Feature is not implemented.
350+
*/
351+
otError otThreadDirectGetPeerInfo(otInstance *aInstance,
352+
const otExtAddress *aExtAddress,
353+
otThreadDirectPeerInfo *aPeerInfo);
354+
231355
/**
232356
* Adds or replaces a guest Wake Key at the given key index.
233357
*
@@ -243,6 +367,7 @@ bool otThreadDirectIsWakeBurstActive(otInstance *aInstance);
243367
* @retval OT_ERROR_INVALID_ARGS @p aKeyIndex is outside [130, 192].
244368
* @retval OT_ERROR_NO_BUFS Guest key table is full.
245369
* @retval OT_ERROR_DISABLED_FEATURE OPENTHREAD_CONFIG_THREAD_DIRECT_GUEST_WAKE_KEY_ENABLE = 0.
370+
* @retval OT_ERROR_NOT_IMPLEMENTED Feature is not implemented.
246371
*/
247372
otError otThreadDirectSetGuestWakeKey(otInstance *aInstance, uint8_t aKeyIndex, const otThreadDirectWakeKey *aKey);
248373

0 commit comments

Comments
 (0)