diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp index 10c12743d..92ece5758 100644 --- a/src/posix/platform/hdlc_interface.cpp +++ b/src/posix/platform/hdlc_interface.cpp @@ -174,7 +174,7 @@ otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) SuccessOrExit(error = hdlcEncoder.Encode(aFrame, aLength, encoderBuffer)); SuccessOrExit(error = hdlcEncoder.Finalize(encoderBuffer)); - SuccessOrExit(error = Write(encoderBuffer.GetBuffer(), encoderBuffer.GetLength())); + error = Write(encoderBuffer.GetBuffer(), encoderBuffer.GetLength()); exit: return error; @@ -183,7 +183,6 @@ exit: otError HdlcInterface::Write(const uint8_t *aFrame, uint16_t aLength) { otError error = OT_ERROR_NONE; - #if OPENTHREAD_POSIX_VIRTUAL_TIME otSimSendRadioSpinelWriteEvent(aFrame, aLength); #else @@ -195,19 +194,83 @@ otError HdlcInterface::Write(const uint8_t *aFrame, uint16_t aLength) { aLength -= static_cast(rval); aFrame += static_cast(rval); + continue; } - else if (rval < 0) + + if ((rval < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR)) { - perror("HdlcInterface::Write"); - ExitNow(error = OT_ERROR_FAILED); + perror("HdlcInterface::Write()"); + exit(OT_EXIT_FAILURE); + } + + SuccessOrExit(error = WaitForWritable()); + } + +exit: +#endif // OPENTHREAD_POSIX_VIRTUAL_TIME + return error; +} + +otError HdlcInterface::WaitForWritable(void) +{ + otError error = OT_ERROR_NONE; + struct timeval timeout = {kMaxWaitTime / 1000, (kMaxWaitTime % 1000) * 1000}; + struct timeval end; + struct timeval now; + fd_set writeFds; + fd_set errorFds; + int rval; + + otSysGetTime(&now); + timeradd(&now, &timeout, &end); + + while (true) + { + FD_ZERO(&writeFds); + FD_ZERO(&errorFds); + FD_SET(mSockFd, &writeFds); + FD_SET(mSockFd, &errorFds); + + rval = select(mSockFd + 1, NULL, &writeFds, &errorFds, &timeout); + + if (rval > 0) + { + if (FD_ISSET(mSockFd, &writeFds)) + { + ExitNow(); + } + else if (FD_ISSET(mSockFd, &errorFds)) + { + fprintf(stderr, "HdlcInterface::WaitForWritable(): socket error\n\r"); + exit(OT_EXIT_FAILURE); + } + else + { + fprintf(stderr, "HdlcInterface::WaitForWritable(): select error\n\r"); + exit(OT_EXIT_FAILURE); + } + } + else if ((rval < 0) && (errno != EINTR)) + { + perror("HdlcInterface::WaitForWritable()"); + exit(OT_EXIT_FAILURE); + } + + otSysGetTime(&now); + + if (timercmp(&end, &now, >)) + { + timersub(&end, &now, &timeout); } else { - ExitNow(error = OT_ERROR_FAILED); + break; } } + + error = OT_ERROR_FAILED; + exit: -#endif return error; } diff --git a/src/posix/platform/hdlc_interface.hpp b/src/posix/platform/hdlc_interface.hpp index 85a084e13..735241960 100644 --- a/src/posix/platform/hdlc_interface.hpp +++ b/src/posix/platform/hdlc_interface.hpp @@ -51,6 +51,7 @@ public: enum { kMaxFrameSize = 2048, ///< Maximum frame size (number of bytes). + kMaxWaitTime = 2000, ///< Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`). }; /** @@ -102,7 +103,7 @@ public: /** * - * This method returns the socket file descriptor associate with the interface + * This method returns the socket file descriptor associated with the interface. * * @returns The associated socket file descriptor, or -1 if interface is not initializes. * @@ -129,12 +130,15 @@ public: /** * This method encodes and sends a frame to Radio Co-processor (RCP) over the socket. * - * @param[in] aFrame A pointer to buffer containing the frame to send. - * @param[in] aLength The length (number of bytes) in the frame + * This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable for + * up to `kMaxWaitTime` interval. + * + * @param[in] aFrame A pointer to buffer containing the frame to send. + * @param[in] aLength The length (number of bytes) in the frame. * * @retval OT_ERROR_NONE Successfully encoded and sent the frame. * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to encode the frame. - * @retval OT_ERROR_FAILED Failed to send frame due to socket write failure. + * @retval OT_ERROR_FAILED Failed to send due to socket not becoming writable within `kMaxWaitTime`. * */ otError SendFrame(const uint8_t *aFrame, uint16_t aLength); @@ -154,8 +158,42 @@ public: #endif private: + /** + * This method waits for the socket file descriptor associated with the HDLC interface to become writable within + * `kMaxWaitTime` interval. + * + * @retval OT_ERROR_NONE Socket is writable. + * @retval OT_ERROR_FAILED Socket did not become writable within `kMaxWaitTime`. + * + */ + otError WaitForWritable(void); + + /** + * This method writes a given frame to the socket. + * + * This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable for + * up to `kMaxWaitTime` interval. + * + * @param[in] aFrame A pointer to buffer containing the frame to write. + * @param[in] aLength The length (number of bytes) in the frame. + * + * @retval OT_ERROR_NONE Frame was written successfully. + * @retval OT_ERROR_FAILED Failed to write due to socket not becoming writable within `kMaxWaitTime`. + * + */ otError Write(const uint8_t *aFrame, uint16_t aLength); - void Decode(const uint8_t *aBuffer, uint16_t aLength); + + /** + * This method performs HDLC decoding on received data. + * + * If a full HDLC frame is decoded while reading data, this method invokes the `HandleReceivedFrame()` (on the + * `aCallback` object from constructor) to pass the received frame to be processed. + * + * @param[in] aBuffer A pointer to buffer containing data. + * @param[in] aLength The length (number of bytes) in the buffer. + * + */ + void Decode(const uint8_t *aBuffer, uint16_t aLength); static void HandleHdlcFrame(void *aContext, uint8_t *aFrame, uint16_t aFrameLength); static void HandleHdlcError(void *aContext, otError aError, uint8_t *aFrame, uint16_t aFrameLength);