blob: a09fab423b2e3f18533da7f8597d8362ce4cdeba [file] [log] [blame]
/*
* 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.
*/
#include "link_metrics.h"
#include <openthread/link_metrics.h>
#include "common/clearable.hpp"
#include "common/linked_list.hpp"
#include "common/pool.hpp"
#include "thread/link_quality.hpp"
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
using namespace ot;
static int8_t sNoiseFloor; ///< The noise floor used by Link Metrics. It should be set to the platform's
///< noise floor (measured noise floor, receiver sensitivity or a constant).
class LinkMetricsDataInfo : public LinkedListEntry<LinkMetricsDataInfo>, public Clearable<LinkMetricsDataInfo>
{
friend class LinkedList<LinkMetricsDataInfo>;
friend class LinkedListEntry<LinkMetricsDataInfo>;
public:
/**
* Construtor.
*
*/
LinkMetricsDataInfo(void) { Clear(); };
/**
* Set the information for this object.
*
* @param[in] aLinkMetrics Flags specifying what metrics to query.
* @param[in] aShortAddress Short Address of the Probing Initiator tracked by this object.
* @param[in] aExtAddress A reference to the Extended Address of the Probing Initiator tracked by this
* object.
*
*/
void Set(otLinkMetrics aLinkMetrics, otShortAddress aShortAddress, const otExtAddress &aExtAddress)
{
mLinkMetrics = aLinkMetrics;
mShortAddress = aShortAddress;
memcpy(mExtAddress.m8, aExtAddress.m8, sizeof(aExtAddress));
}
/**
* This method gets Link Metrics data stored in this object.
*
* TODO: Currently the order of Link Metircs data is fixed. Will update it to follow the order specified in TLV.
*
* @param[in] aLqi LQI value of the acknowledeged frame.
* @param[in] aRssi RSSI value of the acknowledged frame.
* @param[out] aData A pointer to the output buffer. @p aData MUST NOT be `nullptr`. The buffer must have
* at least 2 bytes (per spec 4.11.3.4.4.6). Otherwise the behavior would be undefined.
*
* @returns The number of bytes written. `0` on failure.
*
*/
uint8_t GetEnhAckData(uint8_t aLqi, int8_t aRssi, uint8_t *aData) const
{
enum
{
kEnhAckProbingDataMaxLen = 2,
};
uint8_t bytes = 0;
VerifyOrExit(aData != nullptr);
if (mLinkMetrics.mLqi)
{
aData[bytes++] = aLqi;
}
if (mLinkMetrics.mLinkMargin)
{
aData[bytes++] = static_cast<uint8_t>(GetLinkMargin(aRssi) * 255 /
130); // Linear scale Link Margin from [0, 130] to [0, 255]
}
if (bytes < kEnhAckProbingDataMaxLen && mLinkMetrics.mRssi)
{
aData[bytes++] =
static_cast<uint8_t>((aRssi + 130) * 255 / 130); // Linear scale RSSI from [-130, 0] to [0, 255]
}
exit:
return bytes;
}
/**
* This method gets the length of Link Metrics Data.
*
* @returns The number of bytes for the data.
*
*/
uint8_t GetEnhAckDataLen() const
{
return static_cast<uint8_t>(mLinkMetrics.mLqi) + static_cast<uint8_t>(mLinkMetrics.mLinkMargin) +
static_cast<uint8_t>(mLinkMetrics.mRssi);
}
/**
* This method gets the metrics configured for the Enhanced-ACK Based Probing.
*
* @returns The metrics configured.
*
*/
otLinkMetrics GetLinkMetrics(void) const { return mLinkMetrics; }
private:
uint8_t GetLinkMargin(int8_t aRssi) const { return LinkQualityInfo::ConvertRssToLinkMargin(sNoiseFloor, aRssi); }
bool Matches(const otShortAddress &aShortAddress) const { return mShortAddress == aShortAddress; };
bool Matches(const otExtAddress &aExtAddress) const
{
return memcmp(&mExtAddress, &aExtAddress, sizeof(otExtAddress)) == 0;
};
LinkMetricsDataInfo *mNext;
otLinkMetrics mLinkMetrics;
otShortAddress mShortAddress;
otExtAddress mExtAddress;
};
enum
{
kMaxEnhAckProbingInitiator = OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED,
};
typedef Pool<LinkMetricsDataInfo, kMaxEnhAckProbingInitiator> LinkMetricsDataInfoPool;
typedef LinkedList<LinkMetricsDataInfo> LinkMetricsDataInfoList;
static LinkMetricsDataInfoPool &GetLinkMetricsDataInfoPool(void)
{
static LinkMetricsDataInfoPool sDataInfoPool;
return sDataInfoPool;
}
static LinkMetricsDataInfoList &GetLinkMetricsDataInfoActiveList(void)
{
static LinkMetricsDataInfoList sDataInfoActiveList;
return sDataInfoActiveList;
}
static inline bool IsLinkMetricsClear(otLinkMetrics aLinkMetrics)
{
return !aLinkMetrics.mPduCount && !aLinkMetrics.mLqi && !aLinkMetrics.mLinkMargin && !aLinkMetrics.mRssi;
}
void otLinkMetricsInit(int8_t aNoiseFloor)
{
sNoiseFloor = aNoiseFloor;
}
otError otLinkMetricsConfigureEnhAckProbing(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
otLinkMetrics aLinkMetrics)
{
otError error = OT_ERROR_NONE;
LinkMetricsDataInfo *dataInfo = nullptr;
VerifyOrExit(aExtAddress != nullptr, error = OT_ERROR_INVALID_ARGS);
if (IsLinkMetricsClear(aLinkMetrics)) ///< Remove the entry
{
dataInfo = GetLinkMetricsDataInfoActiveList().RemoveMatching(aShortAddress);
VerifyOrExit(dataInfo != nullptr, error = OT_ERROR_NOT_FOUND);
GetLinkMetricsDataInfoPool().Free(*dataInfo);
}
else
{
dataInfo = GetLinkMetricsDataInfoActiveList().FindMatching(aShortAddress);
if (dataInfo == nullptr)
{
dataInfo = GetLinkMetricsDataInfoPool().Allocate();
VerifyOrExit(dataInfo != nullptr, error = OT_ERROR_NO_BUFS);
dataInfo->Clear();
GetLinkMetricsDataInfoActiveList().Push(*dataInfo);
}
// Overwrite the previous configuration if it already existed.
dataInfo->Set(aLinkMetrics, aShortAddress, *aExtAddress);
}
exit:
return error;
}
LinkMetricsDataInfo *GetLinkMetricsInfoByMacAddress(const otMacAddress *aMacAddress)
{
LinkMetricsDataInfo *dataInfo = nullptr;
VerifyOrExit(aMacAddress != nullptr);
if (aMacAddress->mType == OT_MAC_ADDRESS_TYPE_SHORT)
{
dataInfo = GetLinkMetricsDataInfoActiveList().FindMatching(aMacAddress->mAddress.mShortAddress);
}
else if (aMacAddress->mType == OT_MAC_ADDRESS_TYPE_EXTENDED)
{
dataInfo = GetLinkMetricsDataInfoActiveList().FindMatching(aMacAddress->mAddress.mExtAddress);
}
exit:
return dataInfo;
}
uint8_t otLinkMetricsEnhAckGenData(const otMacAddress *aMacAddress, uint8_t aLqi, int8_t aRssi, uint8_t *aData)
{
uint8_t bytes = 0;
LinkMetricsDataInfo *dataInfo = GetLinkMetricsInfoByMacAddress(aMacAddress);
VerifyOrExit(dataInfo != nullptr);
bytes = dataInfo->GetEnhAckData(aLqi, aRssi, aData);
exit:
return bytes;
}
uint8_t otLinkMetricsEnhAckGetDataLen(const otMacAddress *aMacAddress)
{
uint8_t len = 0;
LinkMetricsDataInfo *dataInfo = GetLinkMetricsInfoByMacAddress(aMacAddress);
VerifyOrExit(dataInfo != nullptr);
len = dataInfo->GetEnhAckDataLen();
exit:
return len;
}
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE