1 /*
2  *  Copyright (c) 2020, 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 #include "radio.hpp"
30 
31 #include "instance/instance.hpp"
32 
33 namespace ot {
34 
35 const uint8_t Radio::kSupportedChannelPages[kNumChannelPages] = {
36 #if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
37     kChannelPage0,
38 #endif
39 #if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
40     kChannelPage2,
41 #endif
42 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
43     OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE,
44 #endif
45 };
46 
47 #if OPENTHREAD_RADIO
Init(void)48 void Radio::Init(void)
49 {
50 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
51 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
52     SuccessOrAssert(ResetCsl());
53 #endif
54 
55     EnableSrcMatch(false);
56     ClearSrcMatchShortEntries();
57     ClearSrcMatchExtEntries();
58 
59     if (IsEnabled())
60     {
61         SuccessOrAssert(Sleep());
62         SuccessOrAssert(Disable());
63     }
64 
65     SetPanId(Mac::kPanIdBroadcast);
66     SetExtendedAddress(Mac::ExtAddress{});
67     SetShortAddress(Mac::kShortAddrInvalid);
68     SetMacKey(0, 0, Mac::KeyMaterial{}, Mac::KeyMaterial{}, Mac::KeyMaterial{});
69     SetMacFrameCounter(0);
70 
71     SetPromiscuous(false);
72     SetRxOnWhenIdle(true);
73 #endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
74 }
75 #endif // OPENTHREAD_RADIO
76 
77 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
78 
SetExtendedAddress(const Mac::ExtAddress & aExtAddress)79 void Radio::SetExtendedAddress(const Mac::ExtAddress &aExtAddress)
80 {
81     Mac::ExtAddress address;
82 
83     address.Set(aExtAddress.m8, Mac::ExtAddress::kReverseByteOrder);
84     otPlatRadioSetExtendedAddress(GetInstancePtr(), &address);
85 
86 #if (OPENTHREAD_MTD || OPENTHREAD_FTD) && OPENTHREAD_CONFIG_OTNS_ENABLE
87     Get<Utils::Otns>().EmitExtendedAddress(address);
88 #endif
89 }
90 
SetShortAddress(Mac::ShortAddress aShortAddress)91 void Radio::SetShortAddress(Mac::ShortAddress aShortAddress)
92 {
93     otPlatRadioSetShortAddress(GetInstancePtr(), aShortAddress);
94 
95 #if (OPENTHREAD_MTD || OPENTHREAD_FTD) && OPENTHREAD_CONFIG_OTNS_ENABLE
96     Get<Utils::Otns>().EmitShortAddress(aShortAddress);
97 #endif
98 }
99 
AddSrcMatchExtEntry(const Mac::ExtAddress & aExtAddress)100 Error Radio::AddSrcMatchExtEntry(const Mac::ExtAddress &aExtAddress)
101 {
102     Mac::ExtAddress address;
103 
104     address.Set(aExtAddress.m8, Mac::ExtAddress::kReverseByteOrder);
105     return otPlatRadioAddSrcMatchExtEntry(GetInstancePtr(), &address);
106 }
Transmit(Mac::TxFrame & aFrame)107 Error Radio::Transmit(Mac::TxFrame &aFrame)
108 {
109 #if (OPENTHREAD_MTD || OPENTHREAD_FTD) && OPENTHREAD_CONFIG_OTNS_ENABLE
110     Get<Utils::Otns>().EmitTransmit(aFrame);
111 #endif
112 
113     return otPlatRadioTransmit(GetInstancePtr(), &aFrame);
114 }
115 #endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
116 
117 #if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
UintSafeMinus(uint64_t aLhs,uint64_t aRhs)118 inline uint64_t UintSafeMinus(uint64_t aLhs, uint64_t aRhs) { return aLhs > aRhs ? (aLhs - aRhs) : 0; }
119 
Statistics(void)120 Radio::Statistics::Statistics(void)
121     : mStatus(kDisabled)
122 {
123     ResetTime();
124 }
125 
RecordStateChange(Status aStatus)126 void Radio::Statistics::RecordStateChange(Status aStatus)
127 {
128     UpdateTime();
129     mStatus = aStatus;
130 }
131 
HandleReceiveAt(uint32_t aDurationUs)132 void Radio::Statistics::HandleReceiveAt(uint32_t aDurationUs)
133 {
134     // The actual rx time of ReceiveAt cannot be obtained from software level. This is a workaround.
135     if (mStatus == kSleep)
136     {
137         mTimeStats.mRxTime += aDurationUs;
138     }
139 }
140 
RecordTxDone(otError aError,uint16_t aPsduLength)141 void Radio::Statistics::RecordTxDone(otError aError, uint16_t aPsduLength)
142 {
143     if (aError == kErrorNone || aError == kErrorNoAck)
144     {
145         uint32_t txTimeUs = (aPsduLength + Mac::Frame::kPhyHeaderSize) * Radio::kSymbolsPerOctet * Radio::kSymbolTime;
146         uint32_t rxAckTimeUs = (Mac::Frame::kImmAckLength + Mac::Frame::kPhyHeaderSize) * Radio::kPhyUsPerByte;
147 
148         UpdateTime();
149         mTimeStats.mTxTime += txTimeUs;
150 
151         if (mStatus == kReceive)
152         {
153             mTimeStats.mRxTime = UintSafeMinus(mTimeStats.mRxTime, txTimeUs);
154         }
155         else if (mStatus == kSleep)
156         {
157             mTimeStats.mSleepTime = UintSafeMinus(mTimeStats.mSleepTime, txTimeUs);
158             if (aError == kErrorNone)
159             {
160                 mTimeStats.mRxTime += rxAckTimeUs;
161                 mTimeStats.mSleepTime = UintSafeMinus(mTimeStats.mSleepTime, rxAckTimeUs);
162             }
163         }
164     }
165 }
166 
RecordRxDone(otError aError)167 void Radio::Statistics::RecordRxDone(otError aError)
168 {
169     uint32_t ackTimeUs;
170 
171     VerifyOrExit(aError == kErrorNone);
172 
173     UpdateTime();
174     // Currently we cannot know the actual length of ACK. So assume the ACK is an immediate ACK.
175     ackTimeUs = (Mac::Frame::kImmAckLength + Mac::Frame::kPhyHeaderSize) * Radio::kPhyUsPerByte;
176     mTimeStats.mTxTime += ackTimeUs;
177     if (mStatus == kReceive)
178     {
179         mTimeStats.mRxTime = UintSafeMinus(mTimeStats.mRxTime, ackTimeUs);
180     }
181 
182 exit:
183     return;
184 }
185 
GetStats(void)186 const Radio::Statistics::TimeStats &Radio::Statistics::GetStats(void)
187 {
188     UpdateTime();
189 
190     return mTimeStats;
191 }
192 
ResetTime(void)193 void Radio::Statistics::ResetTime(void)
194 {
195     ClearAllBytes(mTimeStats);
196     mLastUpdateTime = TimerMicro::GetNow();
197 }
198 
UpdateTime(void)199 void Radio::Statistics::UpdateTime(void)
200 {
201     TimeMicro nowTime     = TimerMicro::GetNow();
202     uint32_t  timeElapsed = nowTime - mLastUpdateTime;
203 
204     switch (mStatus)
205     {
206     case kSleep:
207         mTimeStats.mSleepTime += timeElapsed;
208         break;
209     case kReceive:
210         mTimeStats.mRxTime += timeElapsed;
211         break;
212     case kDisabled:
213         mTimeStats.mDisabledTime += timeElapsed;
214         break;
215     }
216     mLastUpdateTime = nowTime;
217 }
218 
219 #endif // OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
220 
221 } // namespace ot
222