Skip to content

Commit 84295be

Browse files
authored
[message] add TxCallback to track transmission outcome (openthread#11614)
This commit introduces new public APIs to register a `TxCallback` on a message to be notified of its transmission outcome. The callback is invoked with an error code indicating the transmission status of the IPv6 message to an immediate neighbor (a one-hop transmission). It does not indicate that the message was received by its final, multi-hop destination. For a unicast IPv6 message, a success (`OT_ERROR_NONE`) indicates that the message, including all its corresponding fragments if applicable, was successfully delivered to the immediate neighbor and a MAC layer acknowledgment was received for all fragments. This is reported regardless of whether the message is sent using direct or indirect transmission (e.g., to a sleepy child via CSL or a data poll). For a multicast message, an `OT_ERROR_NONE` status indicates that the message and all its fragments were successfully broadcast. Note that no MAC-level acknowledgment is required for a broadcast frame transmission.
1 parent 093d2a8 commit 84295be

7 files changed

Lines changed: 118 additions & 2 deletions

File tree

include/openthread/instance.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ extern "C" {
5252
*
5353
* @note This number versions both OpenThread platform and user APIs.
5454
*/
55-
#define OPENTHREAD_API_VERSION (513)
55+
#define OPENTHREAD_API_VERSION (514)
5656

5757
/**
5858
* @addtogroup api-instance

include/openthread/message.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ typedef struct otThreadLinkInfo
108108
uint8_t mRadioType; ///< Radio link type.
109109
} otThreadLinkInfo;
110110

111+
/**
112+
* Gets the `otInstance` associated with a given message.
113+
*
114+
* @param[in] aMessage A message.
115+
*
116+
* @returns The `otInstance` associated with @p aMessage.
117+
*/
118+
otInstance *otMessageGetInstance(const otMessage *aMessage);
119+
111120
/**
112121
* Free an allocated message buffer.
113122
*
@@ -288,6 +297,44 @@ int8_t otMessageGetRss(const otMessage *aMessage);
288297
*/
289298
otError otMessageGetThreadLinkInfo(const otMessage *aMessage, otThreadLinkInfo *aLinkInfo);
290299

300+
/**
301+
* Represents the callback function pointer to notify the transmission outcome (success or failure) of a message.
302+
*
303+
* The error indicates the transmission status of the IPv6 message from this device to an immediate neighbor (one-hop
304+
* transmission). It doesn't indicate that the message is received by its final intended destination (multi-hop away).
305+
*
306+
* For a unicast IPv6 message, an `OT_ERROR_NONE` error indicates that the message (all its corresponding fragment
307+
* frames if the message is larger and requires fragmentation) was successfully delivered to the immediate neighbor,
308+
* and a MAC layer acknowledgment was received for all fragments. This is reported regardless of whether the message
309+
* is sent using direct TX or indirect TX (to a sleepy child using CSL or data poll triggered TX).
310+
*
311+
* For a multicast message, an `OT_ERROR_NONE` status indicates that the message (all its fragment frames) was
312+
* successfully broadcast. Note that no MAC-level acknowledgment is required for broadcast frame TX.
313+
*
314+
* The OpenThread stack may alter the content of the message as it is prepared for transmission (e.g., IPv6 headers
315+
* may be prepended, or additional metadata appended at the end). So, the content of @p aMessage when this callback
316+
* is invoked may differ from its original content (e.g., when it was given as input in `otIp6Send()` for transmission).
317+
*
318+
* @param[in] aMessage A pointer to the message.
319+
* @param[in] aError The TX error when sending the message.
320+
* @param[in] aContext A pointer to the user-provided context when the callback was registered.
321+
*/
322+
typedef void (*otMessageTxCallback)(const otMessage *aMessage, otError aError, void *aContext);
323+
324+
/**
325+
* Registers a callback to be notified of a message's transmission outcome.
326+
*
327+
* Calling this function again for the same message will replace any previously registered callback.
328+
*
329+
* If the message is never actually sent (e.g., it's not passed to `otIp6Send()` or other send APIs), the callback
330+
* will still be invoked when the message is freed. In this case, `OT_ERROR_DROP` will be passed as the error.
331+
*
332+
* @param[in] aMessage The message to register the callback with.
333+
* @param[in] aCallback The TX callback.
334+
* @param[in] aContext A pointer to a user-provided arbitrary context for the callback.
335+
*/
336+
void otMessageRegisterTxCallback(otMessage *aMessage, otMessageTxCallback aCallback, void *aContext);
337+
291338
/**
292339
* Append bytes to a message.
293340
*

src/core/api/message_api.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737

3838
using namespace ot;
3939

40+
otInstance *otMessageGetInstance(const otMessage *aMessage) { return &AsCoreType(aMessage).GetInstance(); }
41+
4042
void otMessageFree(otMessage *aMessage) { AsCoreType(aMessage).Free(); }
4143

4244
uint16_t otMessageGetLength(const otMessage *aMessage) { return AsCoreType(aMessage).GetLength(); }
@@ -92,6 +94,11 @@ otError otMessageGetThreadLinkInfo(const otMessage *aMessage, otThreadLinkInfo *
9294
return AsCoreType(aMessage).GetLinkInfo(AsCoreType(aLinkInfo));
9395
}
9496

97+
void otMessageRegisterTxCallback(otMessage *aMessage, otMessageTxCallback aCallback, void *aContext)
98+
{
99+
AsCoreType(aMessage).RegisterTxCallback(aCallback, aContext);
100+
}
101+
95102
otError otMessageAppend(otMessage *aMessage, const void *aBuf, uint16_t aLength)
96103
{
97104
AssertPointerIsNotNull(aBuf);

src/core/common/message.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,15 @@ Error Message::ResizeMessage(uint16_t aLength)
250250
return error;
251251
}
252252

253-
void Message::Free(void) { Get<MessagePool>().Free(this); }
253+
void Message::Free(void)
254+
{
255+
// `TxCallback` is cleared once it is invoked. If the message is
256+
// freed before we know the TX outcome, it's treated as a dropped
257+
// message, signaling `kErrorDrop`.
258+
259+
InvokeTxCallback(kErrorDrop);
260+
Get<MessagePool>().Free(this);
261+
}
254262

255263
Message *Message::GetNext(void) const
256264
{
@@ -364,6 +372,23 @@ const char *Message::PriorityToString(Priority aPriority)
364372
return kPriorityStrings[aPriority];
365373
}
366374

375+
void Message::RegisterTxCallback(TxCallback aCallback, void *aContext)
376+
{
377+
GetMetadata().mTxCallback = aCallback;
378+
GetMetadata().mTxContext = aContext;
379+
}
380+
381+
void Message::InvokeTxCallback(Error aError)
382+
{
383+
TxCallback callback = GetMetadata().mTxCallback;
384+
385+
if (callback != nullptr)
386+
{
387+
GetMetadata().mTxCallback = nullptr;
388+
callback(this, aError, GetMetadata().mTxContext);
389+
}
390+
}
391+
367392
Error Message::AppendBytes(const void *aBuf, uint16_t aLength)
368393
{
369394
Error error = kErrorNone;

src/core/common/message.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ class Buffer : public otMessageBuffer, public LinkedListEntry<Buffer>
166166
friend class Message;
167167

168168
public:
169+
typedef otMessageTxCallback TxCallback; ///< Message TX callback.
170+
169171
/**
170172
* Returns a pointer to the next message buffer.
171173
*
@@ -234,6 +236,8 @@ class Buffer : public otMessageBuffer, public LinkedListEntry<Buffer>
234236
Message *mNext; // Next message in a doubly linked list.
235237
Message *mPrev; // Previous message in a doubly linked list.
236238
void *mQueue; // The queue where message is queued (if any). Queue type from `mInPriorityQ`.
239+
TxCallback mTxCallback; // The callback to inform message TX success or failure.
240+
void *mTxContext; // The arbitrary context associated with `mTxCallback`.
237241
RssAverager mRssAverager; // The averager maintaining the received signal strength (RSS) average.
238242
LqiAverager mLqiAverager; // The averager maintaining the Link quality indicator (LQI) average.
239243
#if OPENTHREAD_FTD
@@ -654,6 +658,35 @@ class Message : public otMessage, public Buffer, public GetProvider<Message>
654658
*/
655659
static const char *PriorityToString(Priority aPriority);
656660

661+
/**
662+
* Registers a callback to be notified of a message's transmission outcome.
663+
*
664+
* The registered `TxCallback` provides notification of the transmission status of the message from this device to
665+
* an immediate neighbor (one hop). It doesn't indicate delivery to the final multi-hop destination.
666+
*
667+
* For unicast messages, `kErrorNone` callback error signifies successful delivery and MAC acknowledgment for all
668+
* fragments of the message to an immediate neighbor, irrespective of whether direct or indirect TX is used. For
669+
* multicast messages, `kErrorNone` indicates successful broadcast of all fragments. Note that no MAC-level ack
670+
* is expected for broadcast frame transmissions.
671+
*
672+
* Only one callback can be registered per `Message`. Subsequent calls replace any existing callback. If the
673+
* message is never actually sent, the callback will still be invoked when the message is freed, with `kErrorDrop`
674+
* as the error.
675+
*
676+
* @param[in] aCallback The `TxCallback` function to register with the message.
677+
* @param[in] aContext An arbitrary context that will be passed when @p aCallback is invoked.
678+
*/
679+
void RegisterTxCallback(TxCallback aCallback, void *aContext);
680+
681+
/**
682+
* Invokes the registered `TxCallback` on the `Message` with the given error status.
683+
*
684+
* The `TxCallback` is a one-time callback, meaning it's automatically cleared once it's invoked.
685+
*
686+
* @param[in] aError The error to report.
687+
*/
688+
void InvokeTxCallback(Error aError);
689+
657690
/**
658691
* Prepends bytes to the front of the message.
659692
*

src/core/thread/indirect_sender.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,8 @@ void IndirectSender::HandleSentFrameToChild(const Mac::TxFrame &aFrame,
524524
mSourceMatchController.DecrementMessageCount(aChild);
525525
}
526526

527+
message->InvokeTxCallback(txError);
528+
527529
Get<MeshForwarder>().RemoveMessageIfNoPendingTx(*message);
528530
}
529531

src/core/thread/mesh_forwarder.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,8 @@ void MeshForwarder::FinalizeMessageDirectTx(Message &aMessage, Error aError)
12881288
Get<Mle::Mle>().HandleChildIdRequestTxDone(aMessage);
12891289
}
12901290

1291+
aMessage.InvokeTxCallback(aError);
1292+
12911293
exit:
12921294
return;
12931295
}

0 commit comments

Comments
 (0)