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 implements a SPI interface to the OpenThread stack.
31  */
32 
33 #include "ncp_spi.hpp"
34 
35 #include <openthread/ncp.h>
36 #include <openthread/platform/misc.h>
37 #include <openthread/platform/spi-slave.h>
38 #include <openthread/platform/toolchain.h>
39 
40 #include "openthread-core-config.h"
41 #include "common/code_utils.hpp"
42 #include "common/debug.hpp"
43 #include "common/new.hpp"
44 #include "instance/instance.hpp"
45 #include "net/ip6.hpp"
46 
47 #if OPENTHREAD_CONFIG_NCP_SPI_ENABLE
48 
49 #if OPENTHREAD_CONFIG_DIAG_ENABLE
50 static_assert(OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE <=
51                   OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE - ot::Ncp::NcpBase::kSpinelCmdHeaderSize -
52                       ot::Ncp::NcpBase::kSpinelPropIdSize - ot::Spinel::SpiFrame::kHeaderSize,
53               "diag output should be smaller than NCP SPI tx buffer");
54 static_assert(OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE <= OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE,
55               "diag command line should be smaller than NCP SPI rx buffer");
56 #endif
57 
58 namespace ot {
59 namespace Ncp {
60 
61 using Spinel::SpiFrame;
62 
63 #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK == 0
64 
65 static OT_DEFINE_ALIGNED_VAR(sNcpRaw, sizeof(NcpSpi), uint64_t);
66 
otNcpSpiInit(otInstance * aInstance)67 extern "C" void otNcpSpiInit(otInstance *aInstance)
68 {
69     NcpSpi   *ncpSpi   = nullptr;
70     Instance *instance = static_cast<Instance *>(aInstance);
71 
72     ncpSpi = new (&sNcpRaw) NcpSpi(instance);
73 
74     if (ncpSpi == nullptr || ncpSpi != NcpBase::GetNcpInstance())
75     {
76         OT_ASSERT(false);
77     }
78 }
79 
80 #endif // OPENTHREAD_ENABLE_NCP_VENDOR_HOOK == 0
81 
NcpSpi(Instance * aInstance)82 NcpSpi::NcpSpi(Instance *aInstance)
83     : NcpBase(aInstance)
84     , mTxState(kTxStateIdle)
85     , mHandlingRxFrame(false)
86     , mResetFlag(true)
87     , mPrepareTxFrameTask(*aInstance, NcpSpi::PrepareTxFrame)
88     , mSendFrameLength(0)
89 {
90     SpiFrame sendFrame(mSendFrame);
91     SpiFrame emptyFullAccept(mEmptySendFrameFullAccept);
92     SpiFrame emptyZeroAccept(mEmptySendFrameZeroAccept);
93 
94     sendFrame.SetHeaderFlagByte(/* aResetFlag */ true);
95     sendFrame.SetHeaderAcceptLen(0);
96     sendFrame.SetHeaderDataLen(0);
97 
98     emptyFullAccept.SetHeaderFlagByte(/* aResetFlag */ true);
99     emptyFullAccept.SetHeaderAcceptLen(kSpiBufferSize - kSpiHeaderSize);
100     emptyFullAccept.SetHeaderDataLen(0);
101 
102     emptyZeroAccept.SetHeaderFlagByte(/* aResetFlag */ true);
103     emptyZeroAccept.SetHeaderAcceptLen(0);
104     emptyZeroAccept.SetHeaderDataLen(0);
105 
106     mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToTxBuffer, this);
107 
108     IgnoreError(otPlatSpiSlaveEnable(&NcpSpi::SpiTransactionComplete, &NcpSpi::SpiTransactionProcess, this));
109 
110     // We signal an interrupt on this first transaction to
111     // make sure that the host processor knows that our
112     // reset flag was set.
113 
114     IgnoreError(otPlatSpiSlavePrepareTransaction(mEmptySendFrameZeroAccept, kSpiHeaderSize, mEmptyReceiveFrame,
115                                                  kSpiHeaderSize,
116                                                  /* aRequestTransactionFlag */ true));
117 }
118 
SpiTransactionComplete(void * aContext,uint8_t * aOutputBuf,uint16_t aOutputLen,uint8_t * aInputBuf,uint16_t aInputLen,uint16_t aTransLen)119 bool NcpSpi::SpiTransactionComplete(void    *aContext,
120                                     uint8_t *aOutputBuf,
121                                     uint16_t aOutputLen,
122                                     uint8_t *aInputBuf,
123                                     uint16_t aInputLen,
124                                     uint16_t aTransLen)
125 {
126     NcpSpi *ncp = reinterpret_cast<NcpSpi *>(aContext);
127 
128     return ncp->SpiTransactionComplete(aOutputBuf, aOutputLen, aInputBuf, aInputLen, aTransLen);
129 }
130 
SpiTransactionComplete(uint8_t * aOutputBuf,uint16_t aOutputLen,uint8_t * aInputBuf,uint16_t aInputLen,uint16_t aTransLen)131 bool NcpSpi::SpiTransactionComplete(uint8_t *aOutputBuf,
132                                     uint16_t aOutputLen,
133                                     uint8_t *aInputBuf,
134                                     uint16_t aInputLen,
135                                     uint16_t aTransLen)
136 {
137     // This can be executed from an interrupt context, therefore we cannot
138     // use any of OpenThread APIs here. If further processing is needed,
139     // returned value `shouldProcess` is set to `true` to indicate to
140     // platform SPI slave driver to invoke `SpiTransactionProcess()` callback
141     // which unlike this callback must be called from the same OS context
142     // that OpenThread APIs/callbacks are executed.
143 
144     uint16_t transDataLen;
145     bool     shouldProcess = false;
146     SpiFrame outputFrame(aOutputBuf);
147     SpiFrame inputFrame(aInputBuf);
148     SpiFrame sendFrame(mSendFrame);
149 
150     VerifyOrExit((aTransLen >= kSpiHeaderSize) && (aInputLen >= kSpiHeaderSize) && (aOutputLen >= kSpiHeaderSize));
151     VerifyOrExit(inputFrame.IsValid() && outputFrame.IsValid());
152 
153     transDataLen = aTransLen - kSpiHeaderSize;
154 
155     if (!mHandlingRxFrame)
156     {
157         uint16_t rxDataLen = inputFrame.GetHeaderDataLen();
158 
159         // A new frame is successfully received if input frame
160         // indicates that there is data and the "data len" is not
161         // larger than than the "accept len" we provided in the
162         // exchanged output frame.
163 
164         if ((rxDataLen > 0) && (rxDataLen <= transDataLen) && (rxDataLen <= outputFrame.GetHeaderAcceptLen()))
165         {
166             mHandlingRxFrame = true;
167             shouldProcess    = true;
168         }
169     }
170 
171     if (mTxState == kTxStateSending)
172     {
173         uint16_t txDataLen = outputFrame.GetHeaderDataLen();
174 
175         // Frame transmission is successful if master indicates
176         // in the input frame that it could accept the frame
177         // length that was exchanged, i.e., the "data len" in
178         // the output frame is smaller than or equal to "accept
179         // len" in the received input frame from master.
180 
181         if ((txDataLen > 0) && (txDataLen <= transDataLen) && (txDataLen <= inputFrame.GetHeaderAcceptLen()))
182         {
183             mTxState      = kTxStateHandlingSendDone;
184             shouldProcess = true;
185         }
186     }
187 
188 exit:
189     // Determine the input and output frames to prepare a new transaction.
190 
191     if (mResetFlag && (aTransLen > 0) && (aOutputLen > 0))
192     {
193         mResetFlag = false;
194         sendFrame.SetHeaderFlagByte(/*aResetFlag */ false);
195         SpiFrame(mEmptySendFrameFullAccept).SetHeaderFlagByte(/*aResetFlag */ false);
196         SpiFrame(mEmptySendFrameZeroAccept).SetHeaderFlagByte(/*aResetFlag */ false);
197     }
198 
199     if (mTxState == kTxStateSending)
200     {
201         aOutputBuf = mSendFrame;
202         aOutputLen = mSendFrameLength;
203     }
204     else
205     {
206         aOutputBuf = mHandlingRxFrame ? mEmptySendFrameZeroAccept : mEmptySendFrameFullAccept;
207         aOutputLen = kSpiHeaderSize;
208     }
209 
210     if (mHandlingRxFrame)
211     {
212         aInputBuf = mEmptyReceiveFrame;
213         aInputLen = kSpiHeaderSize;
214     }
215     else
216     {
217         aInputBuf = mReceiveFrame;
218         aInputLen = kSpiBufferSize;
219     }
220 
221     sendFrame.SetHeaderAcceptLen(aInputLen - kSpiHeaderSize);
222 
223     IgnoreError(
224         otPlatSpiSlavePrepareTransaction(aOutputBuf, aOutputLen, aInputBuf, aInputLen, (mTxState == kTxStateSending)));
225 
226     return shouldProcess;
227 }
228 
SpiTransactionProcess(void * aContext)229 void NcpSpi::SpiTransactionProcess(void *aContext) { reinterpret_cast<NcpSpi *>(aContext)->SpiTransactionProcess(); }
230 
SpiTransactionProcess(void)231 void NcpSpi::SpiTransactionProcess(void)
232 {
233     if (mTxState == kTxStateHandlingSendDone)
234     {
235         mPrepareTxFrameTask.Post();
236     }
237 
238     if (mHandlingRxFrame)
239     {
240         HandleRxFrame();
241     }
242 }
243 
HandleFrameAddedToTxBuffer(void * aContext,Spinel::Buffer::FrameTag aTag,Spinel::Buffer::Priority aPriority,Spinel::Buffer * aBuffer)244 void NcpSpi::HandleFrameAddedToTxBuffer(void                    *aContext,
245                                         Spinel::Buffer::FrameTag aTag,
246                                         Spinel::Buffer::Priority aPriority,
247                                         Spinel::Buffer          *aBuffer)
248 {
249     OT_UNUSED_VARIABLE(aBuffer);
250     OT_UNUSED_VARIABLE(aTag);
251     OT_UNUSED_VARIABLE(aPriority);
252 
253     static_cast<NcpSpi *>(aContext)->mPrepareTxFrameTask.Post();
254 }
255 
PrepareNextSpiSendFrame(void)256 void NcpSpi::PrepareNextSpiSendFrame(void)
257 {
258     otError  error = OT_ERROR_NONE;
259     uint16_t frameLength;
260     uint16_t readLength;
261     SpiFrame sendFrame(mSendFrame);
262 
263     VerifyOrExit(!mTxFrameBuffer.IsEmpty());
264 
265     if (ShouldWakeHost())
266     {
267         otPlatWakeHost();
268     }
269 
270     SuccessOrExit(error = mTxFrameBuffer.OutFrameBegin());
271 
272     frameLength = mTxFrameBuffer.OutFrameGetLength();
273     OT_ASSERT(frameLength <= kSpiBufferSize - kSpiHeaderSize);
274 
275     // The "accept length" in `mSendFrame` is already updated based
276     // on current state of receive. It is changed either from the
277     // `SpiTransactionComplete()` callback or from `HandleRxFrame()`.
278 
279     readLength = mTxFrameBuffer.OutFrameRead(frameLength, sendFrame.GetData());
280     OT_ASSERT(readLength == frameLength);
281 
282     // Suppress the warning when assertions are disabled
283     OT_UNUSED_VARIABLE(readLength);
284 
285     sendFrame.SetHeaderDataLen(frameLength);
286     mSendFrameLength = frameLength + kSpiHeaderSize;
287 
288     mTxState = kTxStateSending;
289 
290     // Prepare new transaction by using `mSendFrame` as the output
291     // frame while keeping the input frame unchanged.
292 
293     error = otPlatSpiSlavePrepareTransaction(mSendFrame, mSendFrameLength, nullptr, 0, /* aRequestTrans */ true);
294 
295     if (error == OT_ERROR_BUSY)
296     {
297         // Being busy is OK. We will get the transaction set up
298         // properly when the current transaction is completed.
299         error = OT_ERROR_NONE;
300     }
301 
302     if (error != OT_ERROR_NONE)
303     {
304         mTxState = kTxStateIdle;
305         mPrepareTxFrameTask.Post();
306         ExitNow();
307     }
308 
309     IgnoreError(mTxFrameBuffer.OutFrameRemove());
310 
311 exit:
312     return;
313 }
314 
PrepareTxFrame(Tasklet & aTasklet)315 void NcpSpi::PrepareTxFrame(Tasklet &aTasklet)
316 {
317     OT_UNUSED_VARIABLE(aTasklet);
318     static_cast<NcpSpi *>(GetNcpInstance())->PrepareTxFrame();
319 }
320 
PrepareTxFrame(void)321 void NcpSpi::PrepareTxFrame(void)
322 {
323     switch (mTxState)
324     {
325     case kTxStateHandlingSendDone:
326         mTxState = kTxStateIdle;
327 
328         OT_FALL_THROUGH;
329         // to next case to prepare the next frame (if any).
330 
331     case kTxStateIdle:
332         PrepareNextSpiSendFrame();
333         break;
334 
335     case kTxStateSending:
336         // The next frame in queue (if any) will be prepared when the
337         // current frame is successfully sent and this task is posted
338         // again from the `SpiTransactionComplete()` callback.
339         break;
340     }
341 }
342 
HandleRxFrame(void)343 void NcpSpi::HandleRxFrame(void)
344 {
345     SpiFrame recvFrame(mReceiveFrame);
346     SpiFrame sendFrame(mSendFrame);
347 
348     // Pass the received frame to base class to process.
349     HandleReceive(recvFrame.GetData(), recvFrame.GetHeaderDataLen());
350 
351     // The order of operations below is important. We should clear
352     // the `mHandlingRxFrame` before checking `mTxState` and possibly
353     // preparing the next transaction. Note that the callback
354     // `SpiTransactionComplete()` can be invoked from ISR at any point.
355     //
356     // If we switch the order, we have the following race situation:
357     // We check `mTxState` and it is in `kTxStateSending`, so we skip
358     // preparing the transaction here. But before we set the
359     // `mHandlingRxFrame` to `false`, the `SpiTransactionComplete()`
360     // happens and prepares the next transaction and sets the accept
361     // length to zero on `mSendFrame` (since it assumes we are still
362     // handling the previous received frame).
363 
364     mHandlingRxFrame = false;
365 
366     // If tx state is in `kTxStateSending`, we wait for the callback
367     // `SpiTransactionComplete()`  which will then set up everything
368     // and prepare the next transaction.
369 
370     if (mTxState != kTxStateSending)
371     {
372         sendFrame.SetHeaderAcceptLen(kSpiBufferSize - kSpiHeaderSize);
373 
374         IgnoreError(otPlatSpiSlavePrepareTransaction(mEmptySendFrameFullAccept, kSpiHeaderSize, mReceiveFrame,
375                                                      kSpiBufferSize,
376                                                      /* aRequestTrans */ false));
377 
378         // No need to check the error status. Getting `OT_ERROR_BUSY`
379         // is OK as everything will be set up properly from callback when
380         // the current transaction is completed.
381     }
382 }
383 
384 } // namespace Ncp
385 } // namespace ot
386 
387 #endif // OPENTHREAD_CONFIG_NCP_SPI_ENABLE
388