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/instance.hpp"
45 #include "common/new.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 #endif // OPENTHREAD_ENABLE_NCP_VENDOR_HOOK == 0
81 
NcpHdlc(Instance * aInstance,otNcpHdlcSendCallback aSendCallback)82 NcpHdlc::NcpHdlc(Instance *aInstance, otNcpHdlcSendCallback aSendCallback)
83     : NcpBase(aInstance)
84     , mSendCallback(aSendCallback)
85     , mFrameEncoder(mHdlcBuffer)
86     , mFrameDecoder(mRxBuffer, &NcpHdlc::HandleFrame, this)
87     , mState(kStartingFrame)
88     , mByte(0)
89     , mHdlcSendImmediate(false)
90     , mHdlcSendTask(*aInstance, EncodeAndSend)
91 #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
92     , mTxFrameBufferEncrypterReader(mTxFrameBuffer)
93 #endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
94 {
95     mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this);
96 }
97 
HandleFrameAddedToNcpBuffer(void * aContext,Spinel::Buffer::FrameTag aTag,Spinel::Buffer::Priority aPriority,Spinel::Buffer * aBuffer)98 void NcpHdlc::HandleFrameAddedToNcpBuffer(void                    *aContext,
99                                           Spinel::Buffer::FrameTag aTag,
100                                           Spinel::Buffer::Priority aPriority,
101                                           Spinel::Buffer          *aBuffer)
102 {
103     OT_UNUSED_VARIABLE(aBuffer);
104     OT_UNUSED_VARIABLE(aTag);
105     OT_UNUSED_VARIABLE(aPriority);
106 
107     static_cast<NcpHdlc *>(aContext)->HandleFrameAddedToNcpBuffer();
108 }
109 
HandleFrameAddedToNcpBuffer(void)110 void NcpHdlc::HandleFrameAddedToNcpBuffer(void)
111 {
112     if (mHdlcBuffer.IsEmpty())
113     {
114         mHdlcSendTask.Post();
115     }
116 }
117 
EncodeAndSend(Tasklet & aTasklet)118 void NcpHdlc::EncodeAndSend(Tasklet &aTasklet)
119 {
120     OT_UNUSED_VARIABLE(aTasklet);
121     static_cast<NcpHdlc *>(GetNcpInstance())->EncodeAndSend();
122 }
123 
124 // This method encodes a frame from the tx frame buffer (mTxFrameBuffer) into the uart buffer and sends it over uart.
125 // If the uart buffer gets full, it sends the current encoded portion. This method remembers current state, so on
126 // sub-sequent calls, it restarts encoding the bytes from where it left of in the frame .
EncodeAndSend(void)127 void NcpHdlc::EncodeAndSend(void)
128 {
129     uint16_t len;
130     bool     prevHostPowerState;
131 #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
132     BufferEncrypterReader &txFrameBuffer = mTxFrameBufferEncrypterReader;
133 #else
134     Spinel::Buffer &txFrameBuffer = mTxFrameBuffer;
135 #endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
136 
137     while (!txFrameBuffer.IsEmpty() || (mState == kFinalizingFrame))
138     {
139         switch (mState)
140         {
141         case kStartingFrame:
142 
143             if (super_t::ShouldWakeHost())
144             {
145                 otPlatWakeHost();
146             }
147 
148             VerifyOrExit(!super_t::ShouldDeferHostSend());
149             SuccessOrExit(mFrameEncoder.BeginFrame());
150 
151             IgnoreError(txFrameBuffer.OutFrameBegin());
152 
153             mState = kEncodingFrame;
154 
155             while (!txFrameBuffer.OutFrameHasEnded())
156             {
157                 mByte = txFrameBuffer.OutFrameReadByte();
158 
159                 OT_FALL_THROUGH;
160 
161             case kEncodingFrame:
162 
163                 SuccessOrExit(mFrameEncoder.Encode(mByte));
164             }
165 
166             // track the change of mHostPowerStateInProgress by the
167             // call to OutFrameRemove.
168             prevHostPowerState = mHostPowerStateInProgress;
169 
170             IgnoreError(txFrameBuffer.OutFrameRemove());
171 
172             if (prevHostPowerState && !mHostPowerStateInProgress)
173             {
174                 // If mHostPowerStateInProgress transitioned from true -> false
175                 // in the call to OutFrameRemove, then the frame should be sent
176                 // out without attempting to push any new frames into the
177                 // mHdlcBuffer. This is necessary to avoid prematurely calling
178                 // otPlatWakeHost.
179                 mHdlcSendImmediate = true;
180             }
181 
182             mState = kFinalizingFrame;
183 
184             OT_FALL_THROUGH;
185 
186         case kFinalizingFrame:
187 
188             SuccessOrExit(mFrameEncoder.EndFrame());
189 
190             mState = kStartingFrame;
191 
192             if (mHdlcSendImmediate)
193             {
194                 // clear state and break;
195                 mHdlcSendImmediate = false;
196                 break;
197             }
198         }
199     }
200 
201 exit:
202     len = mHdlcBuffer.GetLength();
203 
204     if (len > 0)
205     {
206         int rval = mSendCallback(mHdlcBuffer.GetFrame(), len);
207         OT_UNUSED_VARIABLE(rval);
208         OT_ASSERT(rval == static_cast<int>(len));
209     }
210 }
211 
otNcpHdlcSendDone(void)212 extern "C" void otNcpHdlcSendDone(void)
213 {
214     NcpHdlc *ncpHdlc = static_cast<NcpHdlc *>(NcpBase::GetNcpInstance());
215 
216     if (ncpHdlc != nullptr)
217     {
218         ncpHdlc->HandleHdlcSendDone();
219     }
220 }
221 
HandleHdlcSendDone(void)222 void NcpHdlc::HandleHdlcSendDone(void)
223 {
224     mHdlcBuffer.Clear();
225     mHdlcSendTask.Post();
226 }
227 
otNcpHdlcReceive(const uint8_t * aBuf,uint16_t aBufLength)228 extern "C" void otNcpHdlcReceive(const uint8_t *aBuf, uint16_t aBufLength)
229 {
230     NcpHdlc *ncpHdlc = static_cast<NcpHdlc *>(NcpBase::GetNcpInstance());
231 
232     if (ncpHdlc != nullptr)
233     {
234         ncpHdlc->HandleHdlcReceiveDone(aBuf, aBufLength);
235     }
236 }
237 
HandleHdlcReceiveDone(const uint8_t * aBuf,uint16_t aBufLength)238 void NcpHdlc::HandleHdlcReceiveDone(const uint8_t *aBuf, uint16_t aBufLength)
239 {
240     mFrameDecoder.Decode(aBuf, aBufLength);
241 }
242 
HandleFrame(void * aContext,otError aError)243 void NcpHdlc::HandleFrame(void *aContext, otError aError) { static_cast<NcpHdlc *>(aContext)->HandleFrame(aError); }
244 
HandleFrame(otError aError)245 void NcpHdlc::HandleFrame(otError aError)
246 {
247     uint8_t *buf       = mRxBuffer.GetFrame();
248     uint16_t bufLength = mRxBuffer.GetLength();
249 
250     if (aError == OT_ERROR_NONE)
251     {
252 #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
253         size_t dataLen = bufLength;
254         if (SpinelEncrypter::DecryptInbound(buf, kRxBufferSize, &dataLen))
255         {
256             super_t::HandleReceive(buf, dataLen);
257         }
258 #else
259         super_t::HandleReceive(buf, bufLength);
260 #endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
261     }
262     else
263     {
264         HandleError(aError, buf, bufLength);
265     }
266 
267     mRxBuffer.Clear();
268 }
269 
HandleError(otError aError,uint8_t * aBuf,uint16_t aBufLength)270 void NcpHdlc::HandleError(otError aError, uint8_t *aBuf, uint16_t aBufLength)
271 {
272     char     hexbuf[128];
273     uint16_t i = 0;
274 
275     super_t::IncrementFrameErrorCounter();
276 
277     snprintf(hexbuf, sizeof(hexbuf), "Framing error %d: [", aError);
278 
279     // Write out the first part of our log message.
280     IgnoreError(otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(hexbuf), static_cast<int>(strlen(hexbuf))));
281 
282     // The first '3' comes from the trailing "]\n\000" at the end o the string.
283     // The second '3' comes from the length of two hex digits and a space.
284     for (i = 0; (i < aBufLength) && (i < (sizeof(hexbuf) - 3) / 3); i++)
285     {
286         snprintf(&hexbuf[i * 3], sizeof(hexbuf) - i * 3, " %02X", static_cast<uint8_t>(aBuf[i]));
287     }
288 
289     // Append a final closing bracket and newline character
290     // so our log line looks nice.
291     snprintf(&hexbuf[i * 3], sizeof(hexbuf) - i * 3, "]\n");
292 
293     // Write out the second part of our log message.
294     // We skip the first byte since it has a space in it.
295     IgnoreError(otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(hexbuf + 1), static_cast<int>(strlen(hexbuf) - 1)));
296 }
297 
298 #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
299 
BufferEncrypterReader(Spinel::Buffer & aTxFrameBuffer)300 NcpHdlc::BufferEncrypterReader::BufferEncrypterReader(Spinel::Buffer &aTxFrameBuffer)
301     : mTxFrameBuffer(aTxFrameBuffer)
302     , mDataBufferReadIndex(0)
303     , mOutputDataLength(0)
304 {
305 }
306 
IsEmpty(void) const307 bool NcpHdlc::BufferEncrypterReader::IsEmpty(void) const { return mTxFrameBuffer.IsEmpty() && !mOutputDataLength; }
308 
OutFrameBegin(void)309 otError NcpHdlc::BufferEncrypterReader::OutFrameBegin(void)
310 {
311     otError status = OT_ERROR_FAILED;
312 
313     Reset();
314 
315     if ((status = mTxFrameBuffer.OutFrameBegin()) == OT_ERROR_NONE)
316     {
317         mOutputDataLength = mTxFrameBuffer.OutFrameGetLength();
318 
319         if (mOutputDataLength > 0)
320         {
321             OT_ASSERT(mOutputDataLength <= sizeof(mDataBuffer));
322             mTxFrameBuffer.OutFrameRead(mOutputDataLength, mDataBuffer);
323 
324             if (!SpinelEncrypter::EncryptOutbound(mDataBuffer, sizeof(mDataBuffer), &mOutputDataLength))
325             {
326                 mOutputDataLength = 0;
327                 status            = OT_ERROR_FAILED;
328             }
329         }
330         else
331         {
332             status = OT_ERROR_FAILED;
333         }
334     }
335 
336     return status;
337 }
338 
OutFrameHasEnded(void)339 bool NcpHdlc::BufferEncrypterReader::OutFrameHasEnded(void) { return (mDataBufferReadIndex >= mOutputDataLength); }
340 
OutFrameReadByte(void)341 uint8_t NcpHdlc::BufferEncrypterReader::OutFrameReadByte(void) { return mDataBuffer[mDataBufferReadIndex++]; }
342 
OutFrameRemove(void)343 otError NcpHdlc::BufferEncrypterReader::OutFrameRemove(void) { return mTxFrameBuffer.OutFrameRemove(); }
344 
Reset(void)345 void NcpHdlc::BufferEncrypterReader::Reset(void)
346 {
347     mOutputDataLength    = 0;
348     mDataBufferReadIndex = 0;
349 }
350 
351 #endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
352 
353 } // namespace Ncp
354 } // namespace ot
355 
356 #endif // OPENTHREAD_CONFIG_NCP_HDLC_ENABLE
357