1 /*
2  *  Copyright (c) 2018, 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 HDLC interface to radio (RCP).
32  */
33 
34 #ifndef OT_POSIX_PLATFORM_HDLC_INTERFACE_HPP_
35 #define OT_POSIX_PLATFORM_HDLC_INTERFACE_HPP_
36 
37 #include "logger.hpp"
38 #include "openthread-posix-config.h"
39 #include "platform-posix.h"
40 #include "lib/hdlc/hdlc.hpp"
41 #include "lib/spinel/multi_frame_buffer.hpp"
42 #include "lib/spinel/openthread-spinel-config.h"
43 #include "lib/spinel/spinel_interface.hpp"
44 
45 namespace ot {
46 namespace Posix {
47 
48 /**
49  * Defines an HDLC interface to the Radio Co-processor (RCP)
50  *
51  */
52 class HdlcInterface : public ot::Spinel::SpinelInterface, public Logger<HdlcInterface>
53 {
54 public:
55     static const char kLogModuleName[]; ///< Module name used for logging.
56 
57     /**
58      * Initializes the object.
59      *
60      * @param[in] aRadioUrl  RadioUrl parsed from radio url.
61      *
62      */
63     HdlcInterface(const Url::Url &aRadioUrl);
64 
65     /**
66      * This destructor deinitializes the object.
67      *
68      */
69     ~HdlcInterface(void);
70 
71     /**
72      * Initializes the interface to the Radio Co-processor (RCP)
73      *
74      * @note This method should be called before reading and sending spinel frames to the interface.
75      *
76      * @param[in] aCallback         Callback on frame received
77      * @param[in] aCallbackContext  Callback context
78      * @param[in] aFrameBuffer      A reference to a `RxFrameBuffer` object.
79      *
80      * @retval OT_ERROR_NONE       The interface is initialized successfully
81      * @retval OT_ERROR_ALREADY    The interface is already initialized.
82      * @retval OT_ERROR_FAILED     Failed to initialize the interface.
83      *
84      */
85     otError Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer);
86 
87     /**
88      * Deinitializes the interface to the RCP.
89      *
90      */
91     void Deinit(void);
92 
93     /**
94      * Encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket.
95      *
96      * This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable for
97      * up to `kMaxWaitTime` interval.
98      *
99      * @param[in] aFrame     A pointer to buffer containing the spinel frame to send.
100      * @param[in] aLength    The length (number of bytes) in the frame.
101      *
102      * @retval OT_ERROR_NONE     Successfully encoded and sent the spinel frame.
103      * @retval OT_ERROR_NO_BUFS  Insufficient buffer space available to encode the frame.
104      * @retval OT_ERROR_FAILED   Failed to send due to socket not becoming writable within `kMaxWaitTime`.
105      *
106      */
107     otError SendFrame(const uint8_t *aFrame, uint16_t aLength);
108 
109     /**
110      * Waits for receiving part or all of spinel frame within specified interval.
111      *
112      * @param[in]  aTimeout  The timeout value in microseconds.
113      *
114      * @retval OT_ERROR_NONE             Part or all of spinel frame is received.
115      * @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout.
116      *
117      */
118     otError WaitForFrame(uint64_t aTimeoutUs);
119 
120     /**
121      * Updates the file descriptor sets with file descriptors used by the radio driver.
122      *
123      * @param[in,out]   aMainloopContext  A pointer to the mainloop context containing fd_sets.
124      *
125      */
126     void UpdateFdSet(void *aMainloopContext);
127 
128     /**
129      * Performs radio driver processing.
130      *
131      * @param[in]   aMainloopContext  A pointer to the mainloop context containing fd_sets.
132      *
133      */
134     void Process(const void *aMainloopContext);
135 
136     /**
137      * Returns the bus speed between the host and the radio.
138      *
139      * @returns   Bus speed in bits/second.
140      *
141      */
GetBusSpeed(void) const142     uint32_t GetBusSpeed(void) const { return mBaudRate; }
143 
144     /**
145      * Hardware resets the RCP.
146      *
147      * @retval OT_ERROR_NONE            Successfully reset the RCP.
148      * @retval OT_ERROR_NOT_IMPLEMENT   The hardware reset is not implemented.
149      *
150      */
HardwareReset(void)151     otError HardwareReset(void) { return OT_ERROR_NOT_IMPLEMENTED; }
152 
153     /**
154      * Returns the RCP interface metrics.
155      *
156      * @returns The RCP interface metrics.
157      *
158      */
GetRcpInterfaceMetrics(void) const159     const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; }
160 
161     /**
162      * Indicates whether or not the given interface matches this interface name.
163      *
164      * @param[in] aInterfaceName A pointer to the interface name.
165      *
166      * @retval TRUE   The given interface name matches this interface name.
167      * @retval FALSE  The given interface name doesn't match this interface name.
168      */
IsInterfaceNameMatch(const char * aInterfaceName)169     static bool IsInterfaceNameMatch(const char *aInterfaceName)
170     {
171         static const char kInterfaceName[] = "spinel+hdlc";
172         return (strncmp(aInterfaceName, kInterfaceName, strlen(kInterfaceName)) == 0);
173     }
174 
175 private:
176     /**
177      * Is called when RCP is reset to recreate the connection with it.
178      *
179      */
180     otError ResetConnection(void);
181 
182     /**
183      * Instructs `HdlcInterface` to read and decode data from radio over the socket.
184      *
185      * If a full HDLC frame is decoded while reading data, this method invokes the `HandleReceivedFrame()` (on the
186      * `aCallback` object from constructor) to pass the received frame to be processed.
187      *
188      */
189     void Read(void);
190 
191     /**
192      * Waits for the socket file descriptor associated with the HDLC interface to become writable within
193      * `kMaxWaitTime` interval.
194      *
195      * @retval OT_ERROR_NONE   Socket is writable.
196      * @retval OT_ERROR_FAILED Socket did not become writable within `kMaxWaitTime`.
197      *
198      */
199     otError WaitForWritable(void);
200 
201     /**
202      * Writes a given frame to the socket.
203      *
204      * This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable for
205      * up to `kMaxWaitTime` interval.
206      *
207      * @param[in] aFrame  A pointer to buffer containing the frame to write.
208      * @param[in] aLength The length (number of bytes) in the frame.
209      *
210      * @retval OT_ERROR_NONE    Frame was written successfully.
211      * @retval OT_ERROR_FAILED  Failed to write due to socket not becoming writable within `kMaxWaitTime`.
212      *
213      */
214     otError Write(const uint8_t *aFrame, uint16_t aLength);
215 
216     /**
217      * Performs HDLC decoding on received data.
218      *
219      * If a full HDLC frame is decoded while reading data, this method invokes the `HandleReceivedFrame()` (on the
220      * `aCallback` object from constructor) to pass the received frame to be processed.
221      *
222      * @param[in] aBuffer  A pointer to buffer containing data.
223      * @param[in] aLength  The length (number of bytes) in the buffer.
224      *
225      */
226     void Decode(const uint8_t *aBuffer, uint16_t aLength);
227 
228     static void HandleHdlcFrame(void *aContext, otError aError);
229     void        HandleHdlcFrame(otError aError);
230 
231     /**
232      * Opens file specified by aRadioUrl.
233      *
234      * @param[in] aRadioUrl  A reference to object containing path to file and data for configuring
235      *                       the connection with tty type file.
236      *
237      * @retval The file descriptor of newly opened file.
238      */
239     int OpenFile(const Url::Url &aRadioUrl);
240 
241     /**
242      * Closes file associated with the file descriptor.
243      *
244      */
245     void CloseFile(void);
246 
247 #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
248     static int ForkPty(const Url::Url &aRadioUrl);
249 #endif
250 
251     enum
252     {
253         kMaxWaitTime   = 2000, ///< Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`).
254         kResetTimeout  = 5000, ///< Maximum wait time in Milliseconds for file to become ready (see `ResetConnection`).
255         kOpenFileDelay = 50,   ///< Delay between open file calls, in Milliseconds (see `ResetConnection`).
256         kRemoveRcpDelay =
257             2000, ///< Delay for removing RCP device from host OS after hard reset (see `ResetConnection`).
258     };
259 
260     ReceiveFrameCallback mReceiveFrameCallback;
261     void                *mReceiveFrameContext;
262     RxFrameBuffer       *mReceiveFrameBuffer;
263 
264     int             mSockFd;
265     uint32_t        mBaudRate;
266     Hdlc::Decoder   mHdlcDecoder;
267     const Url::Url &mRadioUrl;
268 
269     otRcpInterfaceMetrics mInterfaceMetrics;
270 
271     // Non-copyable, intentionally not implemented.
272     HdlcInterface(const HdlcInterface &);
273     HdlcInterface &operator=(const HdlcInterface &);
274 };
275 
276 } // namespace Posix
277 } // namespace ot
278 
279 #endif // OT_POSIX_PLATFORM_HDLC_INTERFACE_HPP_
280