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[inout]  aReadFdSet   A reference to the read file descriptors.
126      * @param[inout]  aWriteFdSet  A reference to the write file descriptors.
127      * @param[inout]  aMaxFd       A reference to the max file descriptor.
128      * @param[inout]  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 private:
156     int     SetupGpioHandle(int aFd, uint8_t aLine, uint32_t aHandleFlags, const char *aLabel);
157     int     SetupGpioEvent(int aFd, uint8_t aLine, uint32_t aHandleFlags, uint32_t aEventFlags, const char *aLabel);
158     void    SetGpioValue(int aFd, uint8_t aValue);
159     uint8_t GetGpioValue(int aFd);
160 
161     void InitResetPin(const char *aCharDev, uint8_t aLine);
162     void InitIntPin(const char *aCharDev, uint8_t aLine);
163     void InitSpiDev(const char *aPath, uint8_t aMode, uint32_t aSpeed);
164     void TriggerReset(void);
165 
166     uint8_t *GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength);
167     otError  DoSpiTransfer(uint8_t *aSpiRxFrameBuffer, uint32_t aTransferLength);
168     otError  PushPullSpi(void);
169 
170     bool CheckInterrupt(void);
171     void LogStats(void);
172     void LogError(const char *aString);
173     void LogBuffer(const char *aDesc, const uint8_t *aBuffer, uint16_t aLength, bool aForce);
174 
175     enum
176     {
177         kSpiModeMax           = 3,
178         kSpiAlignAllowanceMax = 16,
179         kSpiFrameHeaderSize   = 5,
180         kSpiBitsPerWord       = 8,
181         kSpiTxRefuseWarnCount = 30,
182         kSpiTxRefuseExitCount = 100,
183         kImmediateRetryCount  = 5,
184         kFastRetryCount       = 15,
185         kDebugBytesPerLine    = 16,
186         kGpioIntAssertState   = 0,
187         kGpioResetAssertState = 0,
188     };
189 
190     enum
191     {
192         kMsecPerSec              = 1000,
193         kUsecPerMsec             = 1000,
194         kSpiPollPeriodUs         = kMsecPerSec * kUsecPerMsec / 30,
195         kSecPerDay               = 60 * 60 * 24,
196         kResetHoldOnUsec         = 10 * kUsecPerMsec,
197         kImmediateRetryTimeoutUs = 1 * kUsecPerMsec,
198         kFastRetryTimeoutUs      = 10 * kUsecPerMsec,
199         kSlowRetryTimeoutUs      = 33 * kUsecPerMsec,
200     };
201 
202     enum
203     {
204         kMaxFrameSize = Spinel::SpinelInterface::kMaxFrameSize,
205     };
206 
207     Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback;
208     void *                                        mReceiveFrameContext;
209     Spinel::SpinelInterface::RxFrameBuffer &      mRxFrameBuffer;
210 
211     int mSpiDevFd;
212     int mResetGpioValueFd;
213     int mIntGpioValueFd;
214 
215     uint8_t  mSpiMode;
216     uint8_t  mSpiAlignAllowance;
217     uint32_t mSpiResetDelay;
218     uint16_t mSpiCsDelayUs;
219     uint16_t mSpiSmallPacketSize;
220     uint32_t mSpiSpeedHz;
221 
222     uint64_t mSlaveResetCount;
223     uint64_t mSpiFrameCount;
224     uint64_t mSpiValidFrameCount;
225     uint64_t mSpiGarbageFrameCount;
226     uint64_t mSpiDuplexFrameCount;
227     uint64_t mSpiUnresponsiveFrameCount;
228     uint64_t mSpiRxFrameCount;
229     uint64_t mSpiRxFrameByteCount;
230     uint64_t mSpiTxFrameCount;
231     uint64_t mSpiTxFrameByteCount;
232 
233     bool     mSpiTxIsReady;
234     uint16_t mSpiTxRefusedCount;
235     uint16_t mSpiTxPayloadSize;
236     uint8_t  mSpiTxFrameBuffer[kMaxFrameSize + kSpiAlignAllowanceMax];
237 
238     bool     mDidPrintRateLimitLog;
239     uint16_t mSpiSlaveDataLen;
240 
241     // Non-copyable, intentionally not implemented.
242     SpiInterface(const SpiInterface &);
243     SpiInterface &operator=(const SpiInterface &);
244 };
245 
246 } // namespace Posix
247 } // namespace ot
248 
249 #endif // OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_SPI
250 #endif // POSIX_APP_SPI_INTERFACE_HPP_
251