1 /*
2  *    Copyright (c) 2016, 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" AND
17  *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20  *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /**
29  * @file
30  *   This file contains definitions for a HDLC based NCP interface to the OpenThread stack.
31  */
32 
33 #include "ncp_hdlc.hpp"
34 
35 #include <stdio.h>
36 
37 #include <openthread/ncp.h>
38 #include <openthread/platform/logging.h>
39 #include <openthread/platform/misc.h>
40 
41 #include "openthread-core-config.h"
42 #include "common/code_utils.hpp"
43 #include "common/debug.hpp"
44 #include "common/new.hpp"
45 #include "instance/instance.hpp"
46 #include "net/ip6.hpp"
47 
48 #if OPENTHREAD_CONFIG_NCP_HDLC_ENABLE
49 
50 #if OPENTHREAD_CONFIG_DIAG_ENABLE
51 static_assert(OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE <= OPENTHREAD_CONFIG_NCP_HDLC_RX_BUFFER_SIZE -
52                                                                ot::Ncp::NcpBase::kSpinelCmdHeaderSize -
53                                                                ot::Ncp::NcpBase::kSpinelPropIdSize,
54               "diag output should be smaller than NCP HDLC rx buffer");
55 
56 static_assert(OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE <= OPENTHREAD_CONFIG_NCP_HDLC_RX_BUFFER_SIZE,
57               "diag command line should be smaller than NCP HDLC rx buffer");
58 #endif
59 
60 namespace ot {
61 namespace Ncp {
62 
63 #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK == 0
64 
65 static OT_DEFINE_ALIGNED_VAR(sNcpRaw, sizeof(NcpHdlc), uint64_t);
66 
otNcpHdlcInit(otInstance * aInstance,otNcpHdlcSendCallback aSendCallback)67 extern "C" void otNcpHdlcInit(otInstance *aInstance, otNcpHdlcSendCallback aSendCallback)
68 {
69     NcpHdlc  *ncpHdlc  = nullptr;
70     Instance *instance = static_cast<Instance *>(aInstance);
71 
72     ncpHdlc = new (&sNcpRaw) NcpHdlc(instance, aSendCallback);
73 
74     if (ncpHdlc == nullptr || ncpHdlc != NcpBase::GetNcpInstance())
75     {
76         OT_ASSERT(false);
77     }
78 }
79 
80 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
81 
otNcpHdlcInitMulti(otInstance ** aInstances,uint8_t aCount,otNcpHdlcSendCallback aSendCallback)82 extern "C" void otNcpHdlcInitMulti(otInstance **aInstances, uint8_t aCount, otNcpHdlcSendCallback aSendCallback)
83 {
84     NcpHdlc      *ncpHdlc = nullptr;
85     ot::Instance *instances[SPINEL_HEADER_IID_MAX];
86 
87     OT_ASSERT(aCount < SPINEL_HEADER_IID_MAX + 1);
88     OT_ASSERT(aCount > 0);
89     OT_ASSERT(aInstances[0] != nullptr);
90 
91     for (int i = 0; i < aCount; i++)
92     {
93         instances[i] = static_cast<ot::Instance *>(aInstances[i]);
94     }
95 
96     ncpHdlc = new (&sNcpRaw) NcpHdlc(instances, aCount, aSendCallback);
97 
98     if (ncpHdlc == nullptr || ncpHdlc != NcpBase::GetNcpInstance())
99     {
100         OT_ASSERT(false);
101     }
102 }
103 #endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
104 
105 #endif // OPENTHREAD_ENABLE_NCP_VENDOR_HOOK == 0
106 
NcpHdlc(Instance * aInstance,otNcpHdlcSendCallback aSendCallback)107 NcpHdlc::NcpHdlc(Instance *aInstance, otNcpHdlcSendCallback aSendCallback)
108     : NcpBase(aInstance)
109     , mSendCallback(aSendCallback)
110     , mFrameEncoder(mHdlcBuffer)
111     , mState(kStartingFrame)
112     , mByte(0)
113     , mHdlcSendImmediate(false)
114     , mHdlcSendTask(*aInstance, EncodeAndSend)
115 #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
116     , mTxFrameBufferEncrypterReader(mTxFrameBuffer)
117 #endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
118 {
119     mFrameDecoder.Init(mRxBuffer, &NcpHdlc::HandleFrame, this);
120     mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this);
121 }
122 
123 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
124 
NcpHdlc(Instance ** aInstances,uint8_t aCount,otNcpHdlcSendCallback aSendCallback)125 NcpHdlc::NcpHdlc(Instance **aInstances, uint8_t aCount, otNcpHdlcSendCallback aSendCallback)
126     : NcpBase(aInstances, aCount)
127     , mSendCallback(aSendCallback)
128     , mFrameEncoder(mHdlcBuffer)
129     , mState(kStartingFrame)
130     , mByte(0)
131     , mHdlcSendImmediate(false)
132     , mHdlcSendTask(*aInstances[0], EncodeAndSend)
133 #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
134     , mTxFrameBufferEncrypterReader(mTxFrameBuffer)
135 #endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
136 {
137     mFrameDecoder.Init(mRxBuffer, &NcpHdlc::HandleFrame, this);
138     mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this);
139 }
140 
141 #endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
142 
HandleFrameAddedToNcpBuffer(void * aContext,Spinel::Buffer::FrameTag aTag,Spinel::Buffer::Priority aPriority,Spinel::Buffer * aBuffer)143 void NcpHdlc::HandleFrameAddedToNcpBuffer(void                    *aContext,
144                                           Spinel::Buffer::FrameTag aTag,
145                                           Spinel::Buffer::Priority aPriority,
146                                           Spinel::Buffer          *aBuffer)
147 {
148     OT_UNUSED_VARIABLE(aBuffer);
149     OT_UNUSED_VARIABLE(aTag);
150     OT_UNUSED_VARIABLE(aPriority);
151 
152     static_cast<NcpHdlc *>(aContext)->HandleFrameAddedToNcpBuffer();
153 }
154 
HandleFrameAddedToNcpBuffer(void)155 void NcpHdlc::HandleFrameAddedToNcpBuffer(void)
156 {
157     if (mHdlcBuffer.IsEmpty())
158     {
159         mHdlcSendTask.Post();
160     }
161 }
162 
EncodeAndSend(Tasklet & aTasklet)163 void NcpHdlc::EncodeAndSend(Tasklet &aTasklet)
164 {
165     OT_UNUSED_VARIABLE(aTasklet);
166     static_cast<NcpHdlc *>(GetNcpInstance())->EncodeAndSend();
167 }
168 
169 // This method encodes a frame from the tx frame buffer (mTxFrameBuffer) into the uart buffer and sends it over uart.
170 // If the uart buffer gets full, it sends the current encoded portion. This method remembers current state, so on
171 // sub-sequent calls, it restarts encoding the bytes from where it left of in the frame .
EncodeAndSend(void)172 void NcpHdlc::EncodeAndSend(void)
173 {
174     uint16_t len;
175     bool     prevHostPowerState;
176 #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
177     BufferEncrypterReader &txFrameBuffer = mTxFrameBufferEncrypterReader;
178 #else
179     Spinel::Buffer &txFrameBuffer = mTxFrameBuffer;
180 #endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
181 
182     while (!txFrameBuffer.IsEmpty() || (mState == kFinalizingFrame))
183     {
184         switch (mState)
185         {
186         case kStartingFrame:
187 
188             if (super_t::ShouldWakeHost())
189             {
190                 otPlatWakeHost();
191             }
192 
193             VerifyOrExit(!super_t::ShouldDeferHostSend());
194             SuccessOrExit(mFrameEncoder.BeginFrame());
195 
196             IgnoreError(txFrameBuffer.OutFrameBegin());
197 
198             mState = kEncodingFrame;
199 
200             while (!txFrameBuffer.OutFrameHasEnded())
201             {
202                 mByte = txFrameBuffer.OutFrameReadByte();
203 
204                 OT_FALL_THROUGH;
205 
206             case kEncodingFrame:
207 
208                 SuccessOrExit(mFrameEncoder.Encode(mByte));
209             }
210 
211             // track the change of mHostPowerStateInProgress by the
212             // call to OutFrameRemove.
213             prevHostPowerState = mHostPowerStateInProgress;
214 
215             IgnoreError(txFrameBuffer.OutFrameRemove());
216 
217             if (prevHostPowerState && !mHostPowerStateInProgress)
218             {
219                 // If mHostPowerStateInProgress transitioned from true -> false
220                 // in the call to OutFrameRemove, then the frame should be sent
221                 // out without attempting to push any new frames into the
222                 // mHdlcBuffer. This is necessary to avoid prematurely calling
223                 // otPlatWakeHost.
224                 mHdlcSendImmediate = true;
225             }
226 
227             mState = kFinalizingFrame;
228 
229             OT_FALL_THROUGH;
230 
231         case kFinalizingFrame:
232 
233             SuccessOrExit(mFrameEncoder.EndFrame());
234 
235             mState = kStartingFrame;
236 
237             if (mHdlcSendImmediate)
238             {
239                 // clear state and break;
240                 mHdlcSendImmediate = false;
241                 break;
242             }
243         }
244     }
245 
246 exit:
247     len = mHdlcBuffer.GetLength();
248 
249     if (len > 0)
250     {
251         int rval = mSendCallback(mHdlcBuffer.GetFrame(), len);
252         OT_UNUSED_VARIABLE(rval);
253         OT_ASSERT(rval == static_cast<int>(len));
254     }
255 }
256 
otNcpHdlcSendDone(void)257 extern "C" void otNcpHdlcSendDone(void)
258 {
259     NcpHdlc *ncpHdlc = static_cast<NcpHdlc *>(NcpBase::GetNcpInstance());
260 
261     if (ncpHdlc != nullptr)
262     {
263         ncpHdlc->HandleHdlcSendDone();
264     }
265 }
266 
HandleHdlcSendDone(void)267 void NcpHdlc::HandleHdlcSendDone(void)
268 {
269     mHdlcBuffer.Clear();
270     mHdlcSendTask.Post();
271 }
272 
otNcpHdlcReceive(const uint8_t * aBuf,uint16_t aBufLength)273 extern "C" void otNcpHdlcReceive(const uint8_t *aBuf, uint16_t aBufLength)
274 {
275     NcpHdlc *ncpHdlc = static_cast<NcpHdlc *>(NcpBase::GetNcpInstance());
276 
277     if (ncpHdlc != nullptr)
278     {
279         ncpHdlc->HandleHdlcReceiveDone(aBuf, aBufLength);
280     }
281 }
282 
HandleHdlcReceiveDone(const uint8_t * aBuf,uint16_t aBufLength)283 void NcpHdlc::HandleHdlcReceiveDone(const uint8_t *aBuf, uint16_t aBufLength)
284 {
285     mFrameDecoder.Decode(aBuf, aBufLength);
286 }
287 
HandleFrame(void * aContext,otError aError)288 void NcpHdlc::HandleFrame(void *aContext, otError aError) { static_cast<NcpHdlc *>(aContext)->HandleFrame(aError); }
289 
HandleFrame(otError aError)290 void NcpHdlc::HandleFrame(otError aError)
291 {
292     uint8_t *buf       = mRxBuffer.GetFrame();
293     uint16_t bufLength = mRxBuffer.GetLength();
294 
295     if (aError == OT_ERROR_NONE)
296     {
297 #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
298         size_t dataLen = bufLength;
299         if (SpinelEncrypter::DecryptInbound(buf, kRxBufferSize, &dataLen))
300         {
301             super_t::HandleReceive(buf, dataLen);
302         }
303 #else
304         super_t::HandleReceive(buf, bufLength);
305 #endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
306     }
307     else
308     {
309         HandleError(aError, buf, bufLength);
310     }
311 
312     mRxBuffer.Clear();
313 }
314 
HandleError(otError aError,uint8_t * aBuf,uint16_t aBufLength)315 void NcpHdlc::HandleError(otError aError, uint8_t *aBuf, uint16_t aBufLength)
316 {
317     char     hexbuf[128];
318     uint16_t i = 0;
319 
320     super_t::IncrementFrameErrorCounter();
321 
322     snprintf(hexbuf, sizeof(hexbuf), "Framing error %d: [", aError);
323 
324     // Write out the first part of our log message.
325     IgnoreError(otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(hexbuf), static_cast<int>(strlen(hexbuf))));
326 
327     // The first '3' comes from the trailing "]\n\000" at the end o the string.
328     // The second '3' comes from the length of two hex digits and a space.
329     for (i = 0; (i < aBufLength) && (i < (sizeof(hexbuf) - 3) / 3); i++)
330     {
331         snprintf(&hexbuf[i * 3], sizeof(hexbuf) - i * 3, " %02X", static_cast<uint8_t>(aBuf[i]));
332     }
333 
334     // Append a final closing bracket and newline character
335     // so our log line looks nice.
336     snprintf(&hexbuf[i * 3], sizeof(hexbuf) - i * 3, "]\n");
337 
338     // Write out the second part of our log message.
339     // We skip the first byte since it has a space in it.
340     IgnoreError(otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(hexbuf + 1), static_cast<int>(strlen(hexbuf) - 1)));
341 }
342 
343 #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
344 
BufferEncrypterReader(Spinel::Buffer & aTxFrameBuffer)345 NcpHdlc::BufferEncrypterReader::BufferEncrypterReader(Spinel::Buffer &aTxFrameBuffer)
346     : mTxFrameBuffer(aTxFrameBuffer)
347     , mDataBufferReadIndex(0)
348     , mOutputDataLength(0)
349 {
350 }
351 
IsEmpty(void) const352 bool NcpHdlc::BufferEncrypterReader::IsEmpty(void) const { return mTxFrameBuffer.IsEmpty() && !mOutputDataLength; }
353 
OutFrameBegin(void)354 otError NcpHdlc::BufferEncrypterReader::OutFrameBegin(void)
355 {
356     otError status = OT_ERROR_FAILED;
357 
358     Reset();
359 
360     if ((status = mTxFrameBuffer.OutFrameBegin()) == OT_ERROR_NONE)
361     {
362         mOutputDataLength = mTxFrameBuffer.OutFrameGetLength();
363 
364         if (mOutputDataLength > 0)
365         {
366             OT_ASSERT(mOutputDataLength <= sizeof(mDataBuffer));
367             mTxFrameBuffer.OutFrameRead(mOutputDataLength, mDataBuffer);
368 
369             if (!SpinelEncrypter::EncryptOutbound(mDataBuffer, sizeof(mDataBuffer), &mOutputDataLength))
370             {
371                 mOutputDataLength = 0;
372                 status            = OT_ERROR_FAILED;
373             }
374         }
375         else
376         {
377             status = OT_ERROR_FAILED;
378         }
379     }
380 
381     return status;
382 }
383 
OutFrameHasEnded(void)384 bool NcpHdlc::BufferEncrypterReader::OutFrameHasEnded(void) { return (mDataBufferReadIndex >= mOutputDataLength); }
385 
OutFrameReadByte(void)386 uint8_t NcpHdlc::BufferEncrypterReader::OutFrameReadByte(void) { return mDataBuffer[mDataBufferReadIndex++]; }
387 
OutFrameRemove(void)388 otError NcpHdlc::BufferEncrypterReader::OutFrameRemove(void) { return mTxFrameBuffer.OutFrameRemove(); }
389 
Reset(void)390 void NcpHdlc::BufferEncrypterReader::Reset(void)
391 {
392     mOutputDataLength    = 0;
393     mDataBufferReadIndex = 0;
394 }
395 
396 #endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
397 
398 } // namespace Ncp
399 } // namespace ot
400 
401 #endif // OPENTHREAD_CONFIG_NCP_HDLC_ENABLE
402