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[inout] aReadFdSet A reference to the read file descriptors. 124 * @param[inout] aWriteFdSet A reference to the write file descriptors. 125 * @param[inout] aMaxFd A reference to the max file descriptor. 126 * @param[inout] 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 is called when RCP failure detected and resets internal states of the interface. 162 * 163 */ 164 void OnRcpReset(void); 165 166 #if OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION 167 /** 168 * This method is called when RCP is reset to recreate the connection with it. 169 * 170 */ 171 otError ResetConnection(void); 172 #endif 173 174 private: 175 /** 176 * This method instructs `HdlcInterface` to read and decode data from radio over the socket. 177 * 178 * If a full HDLC frame is decoded while reading data, this method invokes the `HandleReceivedFrame()` (on the 179 * `aCallback` object from constructor) to pass the received frame to be processed. 180 * 181 */ 182 void Read(void); 183 184 /** 185 * This method waits for the socket file descriptor associated with the HDLC interface to become writable within 186 * `kMaxWaitTime` interval. 187 * 188 * @retval OT_ERROR_NONE Socket is writable. 189 * @retval OT_ERROR_FAILED Socket did not become writable within `kMaxWaitTime`. 190 * 191 */ 192 otError WaitForWritable(void); 193 194 /** 195 * This method writes a given frame to the socket. 196 * 197 * This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable for 198 * up to `kMaxWaitTime` interval. 199 * 200 * @param[in] aFrame A pointer to buffer containing the frame to write. 201 * @param[in] aLength The length (number of bytes) in the frame. 202 * 203 * @retval OT_ERROR_NONE Frame was written successfully. 204 * @retval OT_ERROR_FAILED Failed to write due to socket not becoming writable within `kMaxWaitTime`. 205 * 206 */ 207 otError Write(const uint8_t *aFrame, uint16_t aLength); 208 209 /** 210 * This method performs HDLC decoding on received data. 211 * 212 * If a full HDLC frame is decoded while reading data, this method invokes the `HandleReceivedFrame()` (on the 213 * `aCallback` object from constructor) to pass the received frame to be processed. 214 * 215 * @param[in] aBuffer A pointer to buffer containing data. 216 * @param[in] aLength The length (number of bytes) in the buffer. 217 * 218 */ 219 void Decode(const uint8_t *aBuffer, uint16_t aLength); 220 221 static void HandleHdlcFrame(void *aContext, otError aError); 222 void HandleHdlcFrame(otError aError); 223 224 /** 225 * This method opens file specified by aRadioUrl. 226 * 227 * @param[in] aRadioUrl A reference to object containing path to file and data for configuring 228 * the connection with tty type file. 229 * 230 * @retval The file descriptor of newly opened file. 231 */ 232 int OpenFile(const Url::Url &aRadioUrl); 233 234 /** 235 * This method closes file associated with the file descriptor. 236 * 237 */ 238 void CloseFile(void); 239 240 #if OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION 241 /** 242 * This method waits until enumeration of RCP(USB CDC ACM) device ends. 243 * 244 * This is blocking call, this method waits for up to 10 seconds. 245 * 246 * @param[in] aRadioUrlPath A path to RCP device. 247 * 248 * @retval OT_ERROR_NONE The RCP device has been added to the host OS before timeout ends. 249 * @retval OT_ERROR_FAILED The RCP device has not been added to the host OS before timeout ends. 250 * 251 */ 252 otError WaitForUsbDevice(const char *aRadioUrlPath); 253 #endif 254 255 #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE 256 static int ForkPty(const Url::Url &aRadioUrl); 257 #endif 258 259 enum 260 { 261 kMaxFrameSize = Spinel::SpinelInterface::kMaxFrameSize, 262 kMaxWaitTime = 2000, ///< Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`). 263 }; 264 265 Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback; 266 void * mReceiveFrameContext; 267 Spinel::SpinelInterface::RxFrameBuffer & mReceiveFrameBuffer; 268 269 int mSockFd; 270 uint32_t mBaudRate; 271 Hdlc::Decoder mHdlcDecoder; 272 const Url::Url *mRadioUrl; 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