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