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 POSIX_APP_HDLC_INTERFACE_HPP_ 35 #define POSIX_APP_HDLC_INTERFACE_HPP_ 36 37 #include "openthread-posix-config.h" 38 #include "platform-posix.h" 39 #include "lib/hdlc/hdlc.hpp" 40 #include "lib/spinel/openthread-spinel-config.h" 41 #include "lib/spinel/spinel_interface.hpp" 42 43 #if OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART 44 45 namespace ot { 46 namespace Posix { 47 48 /** 49 * This class defines an HDLC interface to the Radio Co-processor (RCP) 50 * 51 */ 52 class HdlcInterface 53 { 54 public: 55 /** 56 * This constructor initializes the object. 57 * 58 * @param[in] aCallback Callback on frame received 59 * @param[in] aCallbackContext Callback context 60 * @param[in] aFrameBuffer A reference to a `RxFrameBuffer` object. 61 * 62 */ 63 HdlcInterface(Spinel::SpinelInterface::ReceiveFrameCallback aCallback, 64 void *aCallbackContext, 65 Spinel::SpinelInterface::RxFrameBuffer &aFrameBuffer); 66 67 /** 68 * This destructor deinitializes the object. 69 * 70 */ 71 ~HdlcInterface(void); 72 73 /** 74 * This method initializes the interface to the Radio Co-processor (RCP) 75 * 76 * @note This method should be called before reading and sending spinel frames to the interface. 77 * 78 * @param[in] aRadioUrl RadioUrl parsed from radio url. 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_INVALID_ARGS The UART device or executable cannot be found or failed to open/run. 83 * 84 */ 85 otError Init(const Url::Url &aRadioUrl); 86 87 /** 88 * This method deinitializes the interface to the RCP. 89 * 90 */ 91 void Deinit(void); 92 93 /** 94 * This method 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 * This method 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 * This method updates the file descriptor sets with file descriptors used by the radio driver. 122 * 123 * @param[in,out] aReadFdSet A reference to the read file descriptors. 124 * @param[in,out] aWriteFdSet A reference to the write file descriptors. 125 * @param[in,out] aMaxFd A reference to the max file descriptor. 126 * @param[in,out] aTimeout A reference to the timeout. 127 * 128 */ 129 void UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout); 130 131 /** 132 * This method performs radio driver processing. 133 * 134 * @param[in] aContext The context containing fd_sets. 135 * 136 */ 137 void Process(const RadioProcessContext &aContext); 138 139 #if OPENTHREAD_POSIX_VIRTUAL_TIME 140 /** 141 * This method process read data (decode the data). 142 * 143 * This method is intended only for virtual time simulation. Its behavior is similar to `Read()` but instead of 144 * reading the data from the radio socket, it uses the given data in @p `aEvent`. 145 * 146 * @param[in] aEvent The data event. 147 * 148 */ Process(const VirtualTimeEvent & aEvent)149 void Process(const VirtualTimeEvent &aEvent) { Decode(aEvent.mData, aEvent.mDataLength); } 150 #endif 151 152 /** 153 * This method returns the bus speed between the host and the radio. 154 * 155 * @returns Bus speed in bits/second. 156 * 157 */ GetBusSpeed(void) const158 uint32_t GetBusSpeed(void) const { return mBaudRate; } 159 160 /** 161 * This method hardware resets the RCP. 162 * 163 * @retval OT_ERROR_NONE Successfully reset the RCP. 164 * @retval OT_ERROR_NOT_IMPLEMENT The hardware reset is not implemented. 165 * 166 */ HardwareReset(void)167 otError HardwareReset(void) { return OT_ERROR_NOT_IMPLEMENTED; } 168 169 /** 170 * This method returns the RCP interface metrics. 171 * 172 * @returns The RCP interface metrics. 173 * 174 */ GetRcpInterfaceMetrics(void) const175 const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; } 176 177 private: 178 /** 179 * This method is called when RCP is reset to recreate the connection with it. 180 * 181 */ 182 otError ResetConnection(void); 183 184 /** 185 * This method instructs `HdlcInterface` to read and decode data from radio over the socket. 186 * 187 * If a full HDLC frame is decoded while reading data, this method invokes the `HandleReceivedFrame()` (on the 188 * `aCallback` object from constructor) to pass the received frame to be processed. 189 * 190 */ 191 void Read(void); 192 193 /** 194 * This method waits for the socket file descriptor associated with the HDLC interface to become writable within 195 * `kMaxWaitTime` interval. 196 * 197 * @retval OT_ERROR_NONE Socket is writable. 198 * @retval OT_ERROR_FAILED Socket did not become writable within `kMaxWaitTime`. 199 * 200 */ 201 otError WaitForWritable(void); 202 203 /** 204 * This method writes a given frame to the socket. 205 * 206 * This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable for 207 * up to `kMaxWaitTime` interval. 208 * 209 * @param[in] aFrame A pointer to buffer containing the frame to write. 210 * @param[in] aLength The length (number of bytes) in the frame. 211 * 212 * @retval OT_ERROR_NONE Frame was written successfully. 213 * @retval OT_ERROR_FAILED Failed to write due to socket not becoming writable within `kMaxWaitTime`. 214 * 215 */ 216 otError Write(const uint8_t *aFrame, uint16_t aLength); 217 218 /** 219 * This method performs HDLC decoding on received data. 220 * 221 * If a full HDLC frame is decoded while reading data, this method invokes the `HandleReceivedFrame()` (on the 222 * `aCallback` object from constructor) to pass the received frame to be processed. 223 * 224 * @param[in] aBuffer A pointer to buffer containing data. 225 * @param[in] aLength The length (number of bytes) in the buffer. 226 * 227 */ 228 void Decode(const uint8_t *aBuffer, uint16_t aLength); 229 230 static void HandleHdlcFrame(void *aContext, otError aError); 231 void HandleHdlcFrame(otError aError); 232 233 /** 234 * This method opens file specified by aRadioUrl. 235 * 236 * @param[in] aRadioUrl A reference to object containing path to file and data for configuring 237 * the connection with tty type file. 238 * 239 * @retval The file descriptor of newly opened file. 240 */ 241 int OpenFile(const Url::Url &aRadioUrl); 242 243 /** 244 * This method closes file associated with the file descriptor. 245 * 246 */ 247 void CloseFile(void); 248 249 #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE 250 static int ForkPty(const Url::Url &aRadioUrl); 251 #endif 252 253 enum 254 { 255 kMaxFrameSize = Spinel::SpinelInterface::kMaxFrameSize, 256 kMaxWaitTime = 2000, ///< Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`). 257 kResetTimeout = 5000, ///< Maximum wait time in Milliseconds for file to become ready (see `ResetConnection`). 258 kOpenFileDelay = 500, ///< Delay between open file calls, in Milliseconds (see `ResetConnection`). 259 kRemoveRcpDelay = 260 2000, ///< Delay for removing RCP device from host OS after hard reset (see `ResetConnection`). 261 }; 262 263 Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback; 264 void *mReceiveFrameContext; 265 Spinel::SpinelInterface::RxFrameBuffer &mReceiveFrameBuffer; 266 267 int mSockFd; 268 uint32_t mBaudRate; 269 Hdlc::Decoder mHdlcDecoder; 270 const Url::Url *mRadioUrl; 271 272 otRcpInterfaceMetrics mInterfaceMetrics; 273 274 // Non-copyable, intentionally not implemented. 275 HdlcInterface(const HdlcInterface &); 276 HdlcInterface &operator=(const HdlcInterface &); 277 }; 278 279 } // namespace Posix 280 } // namespace ot 281 282 #endif // OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART 283 #endif // POSIX_APP_HDLC_INTERFACE_HPP_ 284