1 /*
2  *  Copyright (c) 2016-2018, 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 /**
30  * @file
31  *   This file implements the OpenThread Link Raw API.
32  */
33 
34 #include "openthread-core-config.h"
35 
36 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
37 
38 #include <string.h>
39 #include <openthread/diag.h>
40 #include <openthread/platform/diag.h>
41 
42 #include "common/debug.hpp"
43 #include "common/instance.hpp"
44 #include "common/locator_getters.hpp"
45 #include "common/log.hpp"
46 #include "common/random.hpp"
47 #include "mac/mac_frame.hpp"
48 
49 namespace ot {
50 namespace Mac {
51 
52 RegisterLogModule("LinkRaw");
53 
LinkRaw(Instance & aInstance)54 LinkRaw::LinkRaw(Instance &aInstance)
55     : InstanceLocator(aInstance)
56     , mReceiveChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL)
57     , mPanId(kPanIdBroadcast)
58     , mReceiveDoneCallback(nullptr)
59     , mTransmitDoneCallback(nullptr)
60     , mEnergyScanDoneCallback(nullptr)
61 #if OPENTHREAD_RADIO
62     , mSubMac(aInstance)
63 #elif OPENTHREAD_CONFIG_LINK_RAW_ENABLE
64     , mSubMac(aInstance.Get<SubMac>())
65 #endif
66 {
67     Init();
68 }
69 
Init(void)70 void LinkRaw::Init(void)
71 {
72     mEnergyScanDoneCallback = nullptr;
73     mTransmitDoneCallback   = nullptr;
74     mReceiveDoneCallback    = nullptr;
75 
76     mReceiveChannel      = OPENTHREAD_CONFIG_DEFAULT_CHANNEL;
77     mPanId               = kPanIdBroadcast;
78     mReceiveDoneCallback = nullptr;
79 #if OPENTHREAD_RADIO
80     mSubMac.Init();
81 #endif
82 }
83 
SetReceiveDone(otLinkRawReceiveDone aCallback)84 Error LinkRaw::SetReceiveDone(otLinkRawReceiveDone aCallback)
85 {
86     Error error  = kErrorNone;
87     bool  enable = aCallback != nullptr;
88 
89     LogDebg("Enabled(%s)", (enable ? "true" : "false"));
90 
91 #if OPENTHREAD_MTD || OPENTHREAD_FTD
92     VerifyOrExit(!Get<ThreadNetif>().IsUp(), error = kErrorInvalidState);
93 
94     // In MTD/FTD build, `Mac` has already enabled sub-mac. We ensure to
95     // disable/enable MAC layer when link-raw is being enabled/disabled to
96     // avoid any conflict in control of radio and sub-mac between `Mac` and
97     // `LinkRaw`. in RADIO build, we directly enable/disable sub-mac.
98 
99     if (!enable)
100     {
101         // When disabling link-raw, make sure there is no ongoing
102         // transmit or scan operation. Otherwise Mac will attempt to
103         // handle an unexpected "done" callback.
104         VerifyOrExit(!mSubMac.IsTransmittingOrScanning(), error = kErrorBusy);
105     }
106 
107     Get<Mac>().SetEnabled(!enable);
108 #else
109     if (enable)
110     {
111         SuccessOrExit(error = mSubMac.Enable());
112     }
113     else
114     {
115         IgnoreError(mSubMac.Disable());
116     }
117 #endif
118 
119     mReceiveDoneCallback = aCallback;
120 
121 exit:
122     return error;
123 }
124 
SetPanId(uint16_t aPanId)125 Error LinkRaw::SetPanId(uint16_t aPanId)
126 {
127     Error error = kErrorNone;
128 
129     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
130     mSubMac.SetPanId(aPanId);
131     mPanId = aPanId;
132 
133 exit:
134     return error;
135 }
136 
SetChannel(uint8_t aChannel)137 Error LinkRaw::SetChannel(uint8_t aChannel)
138 {
139     Error error = kErrorNone;
140 
141     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
142     mReceiveChannel = aChannel;
143 
144 exit:
145     return error;
146 }
147 
SetExtAddress(const ExtAddress & aExtAddress)148 Error LinkRaw::SetExtAddress(const ExtAddress &aExtAddress)
149 {
150     Error error = kErrorNone;
151 
152     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
153     mSubMac.SetExtAddress(aExtAddress);
154 
155 exit:
156     return error;
157 }
158 
SetShortAddress(ShortAddress aShortAddress)159 Error LinkRaw::SetShortAddress(ShortAddress aShortAddress)
160 {
161     Error error = kErrorNone;
162 
163     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
164     mSubMac.SetShortAddress(aShortAddress);
165 
166 exit:
167     return error;
168 }
169 
Receive(void)170 Error LinkRaw::Receive(void)
171 {
172     Error error = kErrorNone;
173 
174     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
175 
176     SuccessOrExit(error = mSubMac.Receive(mReceiveChannel));
177 
178 exit:
179     return error;
180 }
181 
InvokeReceiveDone(RxFrame * aFrame,Error aError)182 void LinkRaw::InvokeReceiveDone(RxFrame *aFrame, Error aError)
183 {
184     LogDebg("ReceiveDone(%d bytes), error:%s", (aFrame != nullptr) ? aFrame->mLength : 0, ErrorToString(aError));
185 
186     if (mReceiveDoneCallback && (aError == kErrorNone))
187     {
188         mReceiveDoneCallback(&GetInstance(), aFrame, aError);
189     }
190 }
191 
Transmit(otLinkRawTransmitDone aCallback)192 Error LinkRaw::Transmit(otLinkRawTransmitDone aCallback)
193 {
194     Error error = kErrorNone;
195 
196     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
197 
198     SuccessOrExit(error = mSubMac.Send());
199     mTransmitDoneCallback = aCallback;
200 
201 exit:
202     return error;
203 }
204 
InvokeTransmitDone(TxFrame & aFrame,RxFrame * aAckFrame,Error aError)205 void LinkRaw::InvokeTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError)
206 {
207     LogDebg("TransmitDone(%d bytes), error:%s", aFrame.mLength, ErrorToString(aError));
208 
209     if (mTransmitDoneCallback)
210     {
211         mTransmitDoneCallback(&GetInstance(), &aFrame, aAckFrame, aError);
212         mTransmitDoneCallback = nullptr;
213     }
214 }
215 
EnergyScan(uint8_t aScanChannel,uint16_t aScanDuration,otLinkRawEnergyScanDone aCallback)216 Error LinkRaw::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration, otLinkRawEnergyScanDone aCallback)
217 {
218     Error error = kErrorNone;
219 
220     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
221 
222     SuccessOrExit(error = mSubMac.EnergyScan(aScanChannel, aScanDuration));
223     mEnergyScanDoneCallback = aCallback;
224 
225 exit:
226     return error;
227 }
228 
InvokeEnergyScanDone(int8_t aEnergyScanMaxRssi)229 void LinkRaw::InvokeEnergyScanDone(int8_t aEnergyScanMaxRssi)
230 {
231     if (IsEnabled() && mEnergyScanDoneCallback != nullptr)
232     {
233         mEnergyScanDoneCallback(&GetInstance(), aEnergyScanMaxRssi);
234         mEnergyScanDoneCallback = nullptr;
235     }
236 }
237 
SetMacKey(uint8_t aKeyIdMode,uint8_t aKeyId,const Key & aPrevKey,const Key & aCurrKey,const Key & aNextKey)238 Error LinkRaw::SetMacKey(uint8_t    aKeyIdMode,
239                          uint8_t    aKeyId,
240                          const Key &aPrevKey,
241                          const Key &aCurrKey,
242                          const Key &aNextKey)
243 {
244     Error       error = kErrorNone;
245     KeyMaterial prevKey;
246     KeyMaterial currKey;
247     KeyMaterial nextKey;
248 
249     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
250 
251     prevKey.SetFrom(aPrevKey);
252     currKey.SetFrom(aCurrKey);
253     nextKey.SetFrom(aNextKey);
254 
255     mSubMac.SetMacKey(aKeyIdMode, aKeyId, prevKey, currKey, nextKey);
256 
257 exit:
258     return error;
259 }
260 
SetMacFrameCounter(uint32_t aFrameCounter,bool aSetIfLarger)261 Error LinkRaw::SetMacFrameCounter(uint32_t aFrameCounter, bool aSetIfLarger)
262 {
263     Error error = kErrorNone;
264 
265     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
266     mSubMac.SetFrameCounter(aFrameCounter, aSetIfLarger);
267 
268 exit:
269     return error;
270 }
271 
272 // LCOV_EXCL_START
273 
274 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
275 
RecordFrameTransmitStatus(const TxFrame & aFrame,RxFrame * aAckFrame,Error aError,uint8_t aRetryCount,bool aWillRetx)276 void LinkRaw::RecordFrameTransmitStatus(const TxFrame &aFrame,
277                                         RxFrame       *aAckFrame,
278                                         Error          aError,
279                                         uint8_t        aRetryCount,
280                                         bool           aWillRetx)
281 {
282     OT_UNUSED_VARIABLE(aAckFrame);
283     OT_UNUSED_VARIABLE(aWillRetx);
284 
285     if (aError != kErrorNone)
286     {
287         LogInfo("Frame tx failed, error:%s, retries:%d/%d, %s", ErrorToString(aError), aRetryCount,
288                 aFrame.GetMaxFrameRetries(), aFrame.ToInfoString().AsCString());
289     }
290 }
291 
292 #endif
293 
294 // LCOV_EXCL_STOP
295 
296 } // namespace Mac
297 } // namespace ot
298 
299 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
300