[hdlc-interface] wait for socket to be writable when sending a frame (#3309)

This commit changes `HdlcInterface` method `SendFrame()` to handle
the case where the socket associated with the HDLC interface is
not immediately ready to accept the frame. With the change in this
commit `SendFrame()` will block and wait for socket to become
writable within a timeout interval.
This commit is contained in:
Abtin Keshavarzian
2018-11-19 09:46:06 -08:00
committed by Jonathan Hui
parent 1801b0713f
commit 3f3970ccaa
2 changed files with 113 additions and 12 deletions
+70 -7
View File
@@ -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<uint16_t>(rval);
aFrame += static_cast<uint16_t>(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;
}
+43 -5
View File
@@ -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);