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