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