mirror of
https://github.com/espressif/openthread.git
synced 2026-08-26 04:09:52 +00:00
[low-power] add cli command macsend for certification (#5573)
This command allows an Rx-Off-When-Idle device to send an empty mac data frame or mac datarequest to its parent. This command is only used for certification.
This commit is contained in:
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (30)
|
||||
#define OPENTHREAD_API_VERSION (31)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -1109,6 +1109,21 @@ otError otLinkSetEnabled(otInstance *aInstance, bool aEnable);
|
||||
*/
|
||||
bool otLinkIsEnabled(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* This function instructs the device to send an empty IEEE 802.15.4 data frame.
|
||||
*
|
||||
* This function is only supported on an Rx-Off-When-Idle device to send an empty data frame to its parent.
|
||||
* Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully enqueued an empty message.
|
||||
* @retval OT_ERROR_INVALID_STATE Device is not in Rx-Off-When-Idle mode.
|
||||
* @retval OT_ERROR_NO_BUFS Insufficient message buffers available.
|
||||
*
|
||||
*/
|
||||
otError otLinkSendEmptyData(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -2047,6 +2047,20 @@ Set the number of indirect TX retries on the MAC layer.
|
||||
Done
|
||||
```
|
||||
|
||||
### mac send \<op\>
|
||||
|
||||
Instruct an Rx-Off-When-Idle device to send a mac frame to its parent. The mac frame could be either a mac data request or an empty mac data frame. Use `datarequest` to send a mac data request and `data` to send an empty mac data. This feature is for certification, it can only be used when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
|
||||
|
||||
```bash
|
||||
> mac send datarequest
|
||||
Done
|
||||
```
|
||||
|
||||
```bash
|
||||
> mac send emptydata
|
||||
Done
|
||||
```
|
||||
|
||||
### macfilter
|
||||
|
||||
List the macfilter status, including address and received signal strength filter settings.
|
||||
|
||||
@@ -4028,6 +4028,12 @@ otError Interpreter::ProcessMac(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
error = ProcessMacRetries(aArgsLength - 1, aArgs + 1);
|
||||
}
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
else if (strcmp(aArgs[0], "send") == 0)
|
||||
{
|
||||
error = ProcessMacSend(aArgsLength - 1, aArgs + 1);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
error = OT_ERROR_INVALID_COMMAND;
|
||||
@@ -4082,6 +4088,27 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
otError Interpreter::ProcessMacSend(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_INVALID_ARGS;
|
||||
|
||||
VerifyOrExit(aArgsLength == 1, OT_NOOP);
|
||||
|
||||
if (strcmp(aArgs[0], "datarequest") == 0)
|
||||
{
|
||||
error = otLinkSendDataRequest(mInstance);
|
||||
}
|
||||
else if (strcmp(aArgs[0], "emptydata") == 0)
|
||||
{
|
||||
error = otLinkSendEmptyData(mInstance);
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
otError Interpreter::ProcessDiag(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
|
||||
@@ -471,6 +471,9 @@ private:
|
||||
#endif
|
||||
otError ProcessMac(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError ProcessMacRetries(uint8_t aArgsLength, char *aArgs[]);
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
otError ProcessMacSend(uint8_t aArgsLength, char *aArgs[]);
|
||||
#endif
|
||||
|
||||
static void HandleIcmpReceive(void * aContext,
|
||||
otMessage * aMessage,
|
||||
|
||||
@@ -536,3 +536,12 @@ exit:
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
otError otLinkSendEmptyData(otInstance *aInstance)
|
||||
{
|
||||
Instance &instance = *static_cast<Instance *>(aInstance);
|
||||
|
||||
return instance.Get<MeshForwarder>().SendEmptyMessage();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -164,7 +164,7 @@ struct MessageMetadata
|
||||
uint8_t mChannel; ///< Used for MLE Announce.
|
||||
} mPanIdChannel; ///< Used for MLE Discover Request, Response, and Announce messages.
|
||||
|
||||
uint8_t mType : 2; ///< Identifies the type of message.
|
||||
uint8_t mType : 3; ///< Identifies the type of message.
|
||||
uint8_t mSubType : 4; ///< Identifies the message sub type.
|
||||
bool mDirectTx : 1; ///< Used to indicate whether a direct transmission is required.
|
||||
bool mLinkSecurity : 1; ///< Indicates whether or not link security is enabled.
|
||||
@@ -297,10 +297,11 @@ public:
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
kTypeIp6 = 0, ///< A full uncompressed IPv6 packet
|
||||
kType6lowpan = 1, ///< A 6lowpan frame
|
||||
kTypeSupervision = 2, ///< A child supervision frame.
|
||||
kTypeOther = 3, ///< Other (data) message.
|
||||
kTypeIp6 = 0, ///< A full uncompressed IPv6 packet
|
||||
kType6lowpan = 1, ///< A 6lowpan frame
|
||||
kTypeSupervision = 2, ///< A child supervision frame.
|
||||
kTypeMacEmptyData = 3, ///< An empty MAC data frame.
|
||||
kTypeOther = 4, ///< Other (data) message.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -418,37 +418,9 @@ uint16_t IndirectSender::PrepareDataFrame(Mac::TxFrame &aFrame, Child &aChild, M
|
||||
|
||||
void IndirectSender::PrepareEmptyFrame(Mac::TxFrame &aFrame, Child &aChild, bool aAckRequest)
|
||||
{
|
||||
uint16_t fcf;
|
||||
Mac::Address macSource, macDest;
|
||||
|
||||
Mac::Address macDest;
|
||||
aChild.GetMacAddress(macDest);
|
||||
|
||||
macSource.SetShort(Get<Mac::Mac>().GetShortAddress());
|
||||
|
||||
if (macSource.IsShortAddrInvalid() || macDest.IsExtended())
|
||||
{
|
||||
macSource.SetExtended(Get<Mac::Mac>().GetExtAddress());
|
||||
}
|
||||
|
||||
fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfPanidCompression |
|
||||
Mac::Frame::kFcfSecurityEnabled;
|
||||
|
||||
if (aAckRequest)
|
||||
{
|
||||
fcf |= Mac::Frame::kFcfAckRequest;
|
||||
}
|
||||
|
||||
fcf |= (macDest.IsShort()) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt;
|
||||
fcf |= (macSource.IsShort()) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt;
|
||||
|
||||
aFrame.InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
|
||||
|
||||
aFrame.SetDstPanId(Get<Mac::Mac>().GetPanId());
|
||||
IgnoreError(aFrame.SetSrcPanId(Get<Mac::Mac>().GetPanId()));
|
||||
aFrame.SetDstAddr(macDest);
|
||||
aFrame.SetSrcAddr(macSource);
|
||||
aFrame.SetPayloadLength(0);
|
||||
aFrame.SetFramePending(false);
|
||||
Get<MeshForwarder>().PrepareEmptyFrame(aFrame, macDest, aAckRequest);
|
||||
}
|
||||
|
||||
void IndirectSender::HandleSentFrameToChild(const Mac::TxFrame &aFrame,
|
||||
|
||||
@@ -151,6 +151,46 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void MeshForwarder::PrepareEmptyFrame(Mac::TxFrame &aFrame, const Mac::Address &aMacDest, bool aAckRequest)
|
||||
{
|
||||
uint16_t fcf = 0;
|
||||
|
||||
Mac::Address macSource;
|
||||
macSource.SetShort(Get<Mac::Mac>().GetShortAddress());
|
||||
|
||||
if (macSource.IsShortAddrInvalid() || aMacDest.IsExtended())
|
||||
{
|
||||
macSource.SetExtended(Get<Mac::Mac>().GetExtAddress());
|
||||
}
|
||||
|
||||
fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfSecurityEnabled;
|
||||
|
||||
if (aAckRequest)
|
||||
{
|
||||
fcf |= Mac::Frame::kFcfAckRequest;
|
||||
}
|
||||
|
||||
fcf |= (aMacDest.IsShort()) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt;
|
||||
fcf |= (macSource.IsShort()) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt;
|
||||
Get<Mac::Mac>().UpdateFrameControlField(nullptr, 0, fcf);
|
||||
|
||||
aFrame.InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
|
||||
|
||||
if (aFrame.IsDstPanIdPresent())
|
||||
{
|
||||
aFrame.SetDstPanId(Get<Mac::Mac>().GetPanId());
|
||||
}
|
||||
IgnoreError(aFrame.SetSrcPanId(Get<Mac::Mac>().GetPanId()));
|
||||
|
||||
aFrame.SetDstAddr(aMacDest);
|
||||
aFrame.SetSrcAddr(macSource);
|
||||
aFrame.SetFramePending(false);
|
||||
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
|
||||
IgnoreError(Get<Mac::Mac>().AppendHeaderIe(false, aFrame));
|
||||
#endif
|
||||
aFrame.SetPayloadLength(0);
|
||||
}
|
||||
|
||||
void MeshForwarder::RemoveMessage(Message &aMessage)
|
||||
{
|
||||
PriorityQueue *queue = aMessage.GetPriorityQueue();
|
||||
@@ -238,6 +278,12 @@ Message *MeshForwarder::GetDirectTransmission(void)
|
||||
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
case Message::kTypeMacEmptyData:
|
||||
error = OT_ERROR_NONE;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
error = OT_ERROR_DROP;
|
||||
break;
|
||||
@@ -454,6 +500,17 @@ otError MeshForwarder::HandleFrameRequest(Mac::TxFrame &aFrame)
|
||||
OT_ASSERT(aFrame.GetLength() != 7);
|
||||
break;
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
case Message::kTypeMacEmptyData:
|
||||
{
|
||||
Mac::Address macDestAddr;
|
||||
|
||||
macDestAddr.SetShort(Get<Mle::MleRouter>().GetParent().GetRloc16());
|
||||
PrepareEmptyFrame(aFrame, macDestAddr, /* aAckRequest */ true);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
|
||||
case Message::kType6lowpan:
|
||||
@@ -1281,6 +1338,28 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
otError MeshForwarder::SendEmptyMessage(void)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
Message *message = nullptr;
|
||||
|
||||
VerifyOrExit(mEnabled && !Get<Mac::Mac>().GetRxOnWhenIdle() &&
|
||||
Get<Mle::MleRouter>().GetParent().IsStateValidOrRestoring(),
|
||||
error = OT_ERROR_INVALID_STATE);
|
||||
|
||||
message = Get<MessagePool>().New(Message::kTypeMacEmptyData, 0);
|
||||
VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
|
||||
|
||||
SuccessOrExit(error = SendMessage(*message));
|
||||
|
||||
exit:
|
||||
FreeMessageOnError(message, error);
|
||||
otLogDebgMac("Send empty message, error:%s", otThreadErrorToString(error));
|
||||
return error;
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
|
||||
// LCOV_EXCL_START
|
||||
|
||||
#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
|
||||
|
||||
@@ -186,6 +186,18 @@ public:
|
||||
*/
|
||||
otError SendMessage(Message &aMessage);
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
/**
|
||||
* This method sends an empty data frame to the parent.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully enqueued an empty message.
|
||||
* @retval OT_ERROR_INVALID_STATE Device is not in Rx-Off-When-Idle mode or it has no parent.
|
||||
* @retval OT_ERROR_NO_BUFS Insufficient message buffers available.
|
||||
*
|
||||
*/
|
||||
otError SendEmptyMessage(void);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This method is called by the address resolver when an EID-to-RLOC mapping has been resolved.
|
||||
*
|
||||
@@ -403,6 +415,7 @@ private:
|
||||
bool aAddMeshHeader = false,
|
||||
uint16_t aMeshSource = 0xffff,
|
||||
uint16_t aMeshDest = 0xffff);
|
||||
void PrepareEmptyFrame(Mac::TxFrame &aFrame, const Mac::Address &aDstAddr, bool aAckRequest);
|
||||
|
||||
void SendMesh(Message &aMessage, Mac::TxFrame &aFrame);
|
||||
void SendDestinationUnreachable(uint16_t aMeshSource, const Message &aMessage);
|
||||
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/expect -f
|
||||
#
|
||||
# Copyright (c) 2020, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
source "tests/scripts/expect/_common.exp"
|
||||
source "tests/scripts/expect/_multinode.exp"
|
||||
|
||||
setup_nodes "-"
|
||||
|
||||
set spawn_id $spawn_2
|
||||
send "mac send emptydata\n"
|
||||
expect "Done"
|
||||
send "mac send datarequest\n"
|
||||
expect "Done"
|
||||
|
||||
dispose_nodes
|
||||
@@ -956,6 +956,14 @@ class NodeImpl:
|
||||
self.send_command('csl timeout %d' % csl_timeout)
|
||||
self._expect('Done')
|
||||
|
||||
def send_mac_emptydata(self):
|
||||
self.send_command('mac send emptydata')
|
||||
self._expect('Done')
|
||||
|
||||
def send_mac_datarequest(self):
|
||||
self.send_command('mac send datarequest')
|
||||
self._expect('Done')
|
||||
|
||||
def set_router_upgrade_threshold(self, threshold):
|
||||
cmd = 'routerupgradethreshold %d' % threshold
|
||||
self.send_command(cmd)
|
||||
|
||||
Reference in New Issue
Block a user