[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:
Li Cao
2020-10-11 10:46:20 -07:00
committed by GitHub
parent a06bba9d1e
commit a4368909ff
12 changed files with 218 additions and 36 deletions
+1 -1
View File
@@ -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
+15
View File
@@ -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);
/**
* @}
*
+14
View File
@@ -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.
+27
View File
@@ -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[])
{
+3
View File
@@ -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,
+9
View File
@@ -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
+6 -5
View File
@@ -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.
};
/**
+2 -30
View File
@@ -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,
+79
View File
@@ -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)
+13
View File
@@ -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);
+41
View File
@@ -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
+8
View File
@@ -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)