• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
228 {
229     reinterpret_cast<NcpSpi *>(aContext)->SpiTransactionProcess();
230 }
231 
SpiTransactionProcess(void)232 void NcpSpi::SpiTransactionProcess(void)
233 {
234     if (mTxState == kTxStateHandlingSendDone)
235     {
236         mPrepareTxFrameTask.Post();
237     }
238 
239     if (mHandlingRxFrame)
240     {
241         HandleRxFrame();
242     }
243 }
244 
HandleFrameAddedToTxBuffer(void * aContext,Spinel::Buffer::FrameTag aTag,Spinel::Buffer::Priority aPriority,Spinel::Buffer * aBuffer)245 void NcpSpi::HandleFrameAddedToTxBuffer(void *                   aContext,
246                                         Spinel::Buffer::FrameTag aTag,
247                                         Spinel::Buffer::Priority aPriority,
248                                         Spinel::Buffer *         aBuffer)
249 {
250     OT_UNUSED_VARIABLE(aBuffer);
251     OT_UNUSED_VARIABLE(aTag);
252     OT_UNUSED_VARIABLE(aPriority);
253 
254     static_cast<NcpSpi *>(aContext)->mPrepareTxFrameTask.Post();
255 }
256 
PrepareNextSpiSendFrame(void)257 void NcpSpi::PrepareNextSpiSendFrame(void)
258 {
259     otError  error = OT_ERROR_NONE;
260     uint16_t frameLength;
261     uint16_t readLength;
262     SpiFrame sendFrame(mSendFrame);
263 
264     VerifyOrExit(!mTxFrameBuffer.IsEmpty());
265 
266     if (ShouldWakeHost())
267     {
268         otPlatWakeHost();
269     }
270 
271     SuccessOrExit(error = mTxFrameBuffer.OutFrameBegin());
272 
273     frameLength = mTxFrameBuffer.OutFrameGetLength();
274     OT_ASSERT(frameLength <= kSpiBufferSize - kSpiHeaderSize);
275 
276     // The "accept length" in `mSendFrame` is already updated based
277     // on current state of receive. It is changed either from the
278     // `SpiTransactionComplete()` callback or from `HandleRxFrame()`.
279 
280     readLength = mTxFrameBuffer.OutFrameRead(frameLength, sendFrame.GetData());
281     OT_ASSERT(readLength == frameLength);
282 
283     // Suppress the warning when assertions are disabled
284     OT_UNUSED_VARIABLE(readLength);
285 
286     sendFrame.SetHeaderDataLen(frameLength);
287     mSendFrameLength = frameLength + kSpiHeaderSize;
288 
289     mTxState = kTxStateSending;
290 
291     // Prepare new transaction by using `mSendFrame` as the output
292     // frame while keeping the input frame unchanged.
293 
294     error = otPlatSpiSlavePrepareTransaction(mSendFrame, mSendFrameLength, nullptr, 0, /* aRequestTrans */ true);
295 
296     if (error == OT_ERROR_BUSY)
297     {
298         // Being busy is OK. We will get the transaction set up
299         // properly when the current transaction is completed.
300         error = OT_ERROR_NONE;
301     }
302 
303     if (error != OT_ERROR_NONE)
304     {
305         mTxState = kTxStateIdle;
306         mPrepareTxFrameTask.Post();
307         ExitNow();
308     }
309 
310     IgnoreError(mTxFrameBuffer.OutFrameRemove());
311 
312 exit:
313     return;
314 }
315 
PrepareTxFrame(Tasklet & aTasklet)316 void NcpSpi::PrepareTxFrame(Tasklet &aTasklet)
317 {
318     OT_UNUSED_VARIABLE(aTasklet);
319     static_cast<NcpSpi *>(GetNcpInstance())->PrepareTxFrame();
320 }
321 
PrepareTxFrame(void)322 void NcpSpi::PrepareTxFrame(void)
323 {
324     switch (mTxState)
325     {
326     case kTxStateHandlingSendDone:
327         mTxState = kTxStateIdle;
328 
329         OT_FALL_THROUGH;
330         // to next case to prepare the next frame (if any).
331 
332     case kTxStateIdle:
333         PrepareNextSpiSendFrame();
334         break;
335 
336     case kTxStateSending:
337         // The next frame in queue (if any) will be prepared when the
338         // current frame is successfully sent and this task is posted
339         // again from the `SpiTransactionComplete()` callback.
340         break;
341     }
342 }
343 
HandleRxFrame(void)344 void NcpSpi::HandleRxFrame(void)
345 {
346     SpiFrame recvFrame(mReceiveFrame);
347     SpiFrame sendFrame(mSendFrame);
348 
349     // Pass the received frame to base class to process.
350     HandleReceive(recvFrame.GetData(), recvFrame.GetHeaderDataLen());
351 
352     // The order of operations below is important. We should clear
353     // the `mHandlingRxFrame` before checking `mTxState` and possibly
354     // preparing the next transaction. Note that the callback
355     // `SpiTransactionComplete()` can be invoked from ISR at any point.
356     //
357     // If we switch the order, we have the following race situation:
358     // We check `mTxState` and it is in `kTxStateSending`, so we skip
359     // preparing the transaction here. But before we set the
360     // `mHandlingRxFrame` to `false`, the `SpiTransactionComplete()`
361     // happens and prepares the next transaction and sets the accept
362     // length to zero on `mSendFrame` (since it assumes we are still
363     // handling the previous received frame).
364 
365     mHandlingRxFrame = false;
366 
367     // If tx state is in `kTxStateSending`, we wait for the callback
368     // `SpiTransactionComplete()`  which will then set up everything
369     // and prepare the next transaction.
370 
371     if (mTxState != kTxStateSending)
372     {
373         sendFrame.SetHeaderAcceptLen(kSpiBufferSize - kSpiHeaderSize);
374 
375         IgnoreError(otPlatSpiSlavePrepareTransaction(mEmptySendFrameFullAccept, kSpiHeaderSize, mReceiveFrame,
376                                                      kSpiBufferSize,
377                                                      /* aRequestTrans */ false));
378 
379         // No need to check the error status. Getting `OT_ERROR_BUSY`
380         // is OK as everything will be set up properly from callback when
381         // the current transaction is completed.
382     }
383 }
384 
385 } // namespace Ncp
386 } // namespace ot
387 
388 #endif // OPENTHREAD_CONFIG_NCP_SPI_ENABLE
389