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