• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2019, 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"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file includes definitions for the SPI interface to radio (RCP).
32  */
33 
34 #ifndef POSIX_APP_SPI_INTERFACE_HPP_
35 #define POSIX_APP_SPI_INTERFACE_HPP_
36 
37 #include "openthread-posix-config.h"
38 
39 #include "platform-posix.h"
40 #include "lib/hdlc/hdlc.hpp"
41 #include "lib/spinel/spinel_interface.hpp"
42 
43 #include <openthread/openthread-system.h>
44 
45 #if OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_SPI
46 
47 #include "ncp/ncp_spi.hpp"
48 
49 namespace ot {
50 namespace Posix {
51 
52 /**
53  * This class defines an SPI interface to the Radio Co-processor (RCP).
54  *
55  */
56 class SpiInterface
57 {
58 public:
59     /**
60      * This constructor initializes the object.
61      *
62      * @param[in] aCallback         A reference to a `Callback` object.
63      * @param[in] aCallbackContext  The context pointer passed to the callback.
64      * @param[in] aFrameBuffer      A reference to a `RxFrameBuffer` object.
65      *
66      */
67     SpiInterface(Spinel::SpinelInterface::ReceiveFrameCallback aCallback,
68                  void *                                        aCallbackContext,
69                  Spinel::SpinelInterface::RxFrameBuffer &      aFrameBuffer);
70 
71     /**
72      * This destructor deinitializes the object.
73      *
74      */
75     ~SpiInterface(void);
76 
77     /**
78      * This method initializes the interface to the Radio Co-processor (RCP).
79      *
80      * @note This method should be called before reading and sending spinel frames to the interface.
81      *
82      * @param[in]  aRadioUrl          Arguments parsed from radio url.
83      *
84      * @retval OT_ERROR_NONE          The interface is initialized successfully.
85      * @retval OT_ERROR_ALREADY       The interface is already initialized.
86      * @retval OT_ERROR_INVALID_ARGS  The UART device or executable cannot be found or failed to open/run.
87      *
88      */
89     otError Init(const Url::Url &aRadioUrl);
90 
91     /**
92      * This method deinitializes the interface to the RCP.
93      *
94      */
95     void Deinit(void);
96 
97     /**
98      * This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket.
99      *
100      * @param[in] aFrame     A pointer to buffer containing the spinel frame to send.
101      * @param[in] aLength    The length (number of bytes) in the frame.
102      *
103      * @retval OT_ERROR_NONE     Successfully encoded and sent the spinel frame.
104      * @retval OT_ERROR_BUSY     Failed due to another operation is on going.
105      * @retval OT_ERROR_NO_BUFS  Insufficient buffer space available to encode the frame.
106      * @retval OT_ERROR_FAILED   Failed to call the SPI driver to send the frame.
107      *
108      */
109     otError SendFrame(const uint8_t *aFrame, uint16_t aLength);
110 
111     /**
112      * This method waits for receiving part or all of spinel frame within specified interval.
113      *
114      * @param[in]  aTimeout  The timeout value in microseconds.
115      *
116      * @retval OT_ERROR_NONE             Part or all of spinel frame is received.
117      * @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout.
118      *
119      */
120     otError WaitForFrame(uint64_t aTimeoutUs);
121 
122     /**
123      * This method updates the file descriptor sets with file descriptors used by the radio driver.
124      *
125      * @param[in,out]  aReadFdSet   A reference to the read file descriptors.
126      * @param[in,out]  aWriteFdSet  A reference to the write file descriptors.
127      * @param[in,out]  aMaxFd       A reference to the max file descriptor.
128      * @param[in,out]  aTimeout     A reference to the timeout.
129      *
130      */
131     void UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout);
132 
133     /**
134      * This method performs radio driver processing.
135      *
136      * @param[in]   aContext        The context containing fd_sets.
137      *
138      */
139     void Process(const RadioProcessContext &aContext);
140 
141     /**
142      * This method returns the bus speed between the host and the radio.
143      *
144      * @returns   Bus speed in bits/second.
145      *
146      */
GetBusSpeed(void) const147     uint32_t GetBusSpeed(void) const { return ((mSpiDevFd >= 0) ? mSpiSpeedHz : 0); }
148 
149     /**
150      * This method is called when RCP failure detected and resets internal states of the interface.
151      *
152      */
153     void OnRcpReset(void);
154 
155     /**
156      * This method is called when RCP is reset to recreate the connection with it.
157      * Intentionally empty.
158      *
159      */
ResetConnection(void)160     otError ResetConnection(void) { return OT_ERROR_NONE; }
161 
162     /**
163      * This method returns the RCP interface metrics.
164      *
165      * @returns The RCP interface metrics.
166      *
167      */
GetRcpInterfaceMetrics(void) const168     const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; }
169 
170 private:
171     int     SetupGpioHandle(int aFd, uint8_t aLine, uint32_t aHandleFlags, const char *aLabel);
172     int     SetupGpioEvent(int aFd, uint8_t aLine, uint32_t aHandleFlags, uint32_t aEventFlags, const char *aLabel);
173     void    SetGpioValue(int aFd, uint8_t aValue);
174     uint8_t GetGpioValue(int aFd);
175 
176     void InitResetPin(const char *aCharDev, uint8_t aLine);
177     void InitIntPin(const char *aCharDev, uint8_t aLine);
178     void InitSpiDev(const char *aPath, uint8_t aMode, uint32_t aSpeed);
179     void TriggerReset(void);
180 
181     uint8_t *GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength);
182     otError  DoSpiTransfer(uint8_t *aSpiRxFrameBuffer, uint32_t aTransferLength);
183     otError  PushPullSpi(void);
184     void     Process(const fd_set *aReadFdSet, const fd_set *aWriteFdSet);
185 
186     bool CheckInterrupt(void);
187     void LogStats(void);
188     void LogError(const char *aString);
189     void LogBuffer(const char *aDesc, const uint8_t *aBuffer, uint16_t aLength, bool aForce);
190 
191     enum
192     {
193         kSpiModeMax           = 3,
194         kSpiAlignAllowanceMax = 16,
195         kSpiFrameHeaderSize   = 5,
196         kSpiBitsPerWord       = 8,
197         kSpiTxRefuseWarnCount = 30,
198         kSpiTxRefuseExitCount = 100,
199         kImmediateRetryCount  = 5,
200         kFastRetryCount       = 15,
201         kDebugBytesPerLine    = 16,
202         kGpioIntAssertState   = 0,
203         kGpioResetAssertState = 0,
204     };
205 
206     enum
207     {
208         kMsecPerSec              = 1000,
209         kUsecPerMsec             = 1000,
210         kSpiPollPeriodUs         = kMsecPerSec * kUsecPerMsec / 30,
211         kSecPerDay               = 60 * 60 * 24,
212         kResetHoldOnUsec         = 10 * kUsecPerMsec,
213         kImmediateRetryTimeoutUs = 1 * kUsecPerMsec,
214         kFastRetryTimeoutUs      = 10 * kUsecPerMsec,
215         kSlowRetryTimeoutUs      = 33 * kUsecPerMsec,
216     };
217 
218     enum
219     {
220         kMaxFrameSize = Spinel::SpinelInterface::kMaxFrameSize,
221     };
222 
223     Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback;
224     void *                                        mReceiveFrameContext;
225     Spinel::SpinelInterface::RxFrameBuffer &      mRxFrameBuffer;
226 
227     int mSpiDevFd;
228     int mResetGpioValueFd;
229     int mIntGpioValueFd;
230 
231     uint8_t  mSpiMode;
232     uint8_t  mSpiAlignAllowance;
233     uint32_t mSpiResetDelay;
234     uint16_t mSpiCsDelayUs;
235     uint16_t mSpiSmallPacketSize;
236     uint32_t mSpiSpeedHz;
237 
238     uint64_t mSlaveResetCount;
239     uint64_t mSpiDuplexFrameCount;
240     uint64_t mSpiUnresponsiveFrameCount;
241 
242     bool     mSpiTxIsReady;
243     uint16_t mSpiTxRefusedCount;
244     uint16_t mSpiTxPayloadSize;
245     uint8_t  mSpiTxFrameBuffer[kMaxFrameSize + kSpiAlignAllowanceMax];
246 
247     bool     mDidPrintRateLimitLog;
248     uint16_t mSpiSlaveDataLen;
249 
250     bool mDidRxFrame;
251 
252     otRcpInterfaceMetrics mInterfaceMetrics;
253 
254     // Non-copyable, intentionally not implemented.
255     SpiInterface(const SpiInterface &);
256     SpiInterface &operator=(const SpiInterface &);
257 };
258 
259 } // namespace Posix
260 } // namespace ot
261 
262 #endif // OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_SPI
263 #endif // POSIX_APP_SPI_INTERFACE_HPP_
264