From 1623351bc0af51511b762dfad4cf2ab1d7d4bbf3 Mon Sep 17 00:00:00 2001 From: shihchienc Date: Mon, 29 Jan 2024 10:07:47 +0000 Subject: [PATCH] [Thread] Implement read on socket interface Bug: 313425570 Test: build pass & manual test Change-Id: I4e6dfdfa73f7145e8f36d05abf1531d7796b4b9e --- .../aidl/default/socket_interface.cpp | 92 +++++++++++++++++++ .../aidl/default/socket_interface.hpp | 51 ++++++++++ 2 files changed, 143 insertions(+) diff --git a/threadnetwork/aidl/default/socket_interface.cpp b/threadnetwork/aidl/default/socket_interface.cpp index 6b9b80eb07..f874209aaf 100644 --- a/threadnetwork/aidl/default/socket_interface.cpp +++ b/threadnetwork/aidl/default/socket_interface.cpp @@ -89,6 +89,41 @@ otError SocketInterface::SendFrame(const uint8_t* aFrame, uint16_t aLength) { return OT_ERROR_NONE; } +otError SocketInterface::WaitForFrame(uint64_t aTimeoutUs) { + otError error = OT_ERROR_NONE; + struct timeval timeout; + timeout.tv_sec = static_cast(aTimeoutUs / US_PER_S); + timeout.tv_usec = static_cast(aTimeoutUs % US_PER_S); + + fd_set readFds; + fd_set errorFds; + int rval; + + FD_ZERO(&readFds); + FD_ZERO(&errorFds); + FD_SET(mSockFd, &readFds); + FD_SET(mSockFd, &errorFds); + + rval = TEMP_FAILURE_RETRY(select(mSockFd + 1, &readFds, nullptr, &errorFds, &timeout)); + + if (rval > 0) { + if (FD_ISSET(mSockFd, &readFds)) { + Read(); + } else if (FD_ISSET(mSockFd, &errorFds)) { + DieNowWithMessage("RCP error", OT_EXIT_FAILURE); + } else { + DieNow(OT_EXIT_FAILURE); + } + } else if (rval == 0) { + ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT); + } else { + DieNowWithMessage("wait response", OT_EXIT_FAILURE); + } + +exit: + return error; +} + void SocketInterface::UpdateFdSet(void* aMainloopContext) { otSysMainloopContext* context = reinterpret_cast(aMainloopContext); @@ -101,12 +136,69 @@ void SocketInterface::UpdateFdSet(void* aMainloopContext) { } } +void SocketInterface::Process(const void* aMainloopContext) { + const otSysMainloopContext* context = + reinterpret_cast(aMainloopContext); + + assert(context != nullptr); + + if (FD_ISSET(mSockFd, &context->mReadFdSet)) { + Read(); + } +} + +void SocketInterface::Read(void) { + uint8_t buffer[kMaxFrameSize]; + + ssize_t rval = TEMP_FAILURE_RETRY(read(mSockFd, buffer, sizeof(buffer))); + + if (rval > 0) { + ProcessReceivedData(buffer, static_cast(rval)); + } else if (rval < 0) { + DieNow(OT_EXIT_ERROR_ERRNO); + } else { + otLogCritPlat("Socket connection is closed by remote."); + exit(OT_EXIT_FAILURE); + } +} + void SocketInterface::Write(const uint8_t* aFrame, uint16_t aLength) { ssize_t rval = TEMP_FAILURE_RETRY(write(mSockFd, aFrame, aLength)); VerifyOrDie(rval >= 0, OT_EXIT_ERROR_ERRNO); VerifyOrDie(rval > 0, OT_EXIT_FAILURE); } +void SocketInterface::ProcessReceivedData(const uint8_t* aBuffer, uint16_t aLength) { + while (aLength--) { + uint8_t byte = *aBuffer++; + if (mReceiveFrameBuffer->CanWrite(sizeof(uint8_t))) { + IgnoreError(mReceiveFrameBuffer->WriteByte(byte)); + } else { + HandleSocketFrame(this, OT_ERROR_NO_BUFS); + return; + } + } + HandleSocketFrame(this, OT_ERROR_NONE); +} + +void SocketInterface::HandleSocketFrame(void* aContext, otError aError) { + static_cast(aContext)->HandleSocketFrame(aError); +} + +void SocketInterface::HandleSocketFrame(otError aError) { + VerifyOrExit((mReceiveFrameCallback != nullptr) && (mReceiveFrameBuffer != nullptr)); + + if (aError == OT_ERROR_NONE) { + mReceiveFrameCallback(mReceiveFrameContext); + } else { + mReceiveFrameBuffer->DiscardFrame(); + otLogWarnPlat("Process socket frame failed: %s", otThreadErrorToString(aError)); + } + +exit: + return; +} + int SocketInterface::OpenFile(const ot::Url::Url& aRadioUrl) { int fd = -1; sockaddr_un serverAddress; diff --git a/threadnetwork/aidl/default/socket_interface.hpp b/threadnetwork/aidl/default/socket_interface.hpp index 2dd315d125..f88e92670c 100644 --- a/threadnetwork/aidl/default/socket_interface.hpp +++ b/threadnetwork/aidl/default/socket_interface.hpp @@ -86,6 +86,21 @@ class SocketInterface : public ot::Spinel::SpinelInterface { */ otError SendFrame(const uint8_t* aFrame, uint16_t aLength); + /** + * Waits for receiving part or all of Spinel frame within specified + * interval. + * + * @param[in] aTimeout The timeout value in microseconds. + * + * @retval OT_ERROR_NONE Part or all of Spinel frame is + * received. + * @retval OT_ERROR_RESPONSE_TIMEOUT No Spinel frame is received within @p + * aTimeout. + * @retval OT_EXIT_FAILURE RCP error + * + */ + otError WaitForFrame(uint64_t aTimeoutUs); + /** * Updates the file descriptor sets with file descriptors used by the radio * driver. @@ -96,6 +111,15 @@ class SocketInterface : public ot::Spinel::SpinelInterface { */ void UpdateFdSet(void* aMainloopContext); + /** + * Performs radio driver processing. + * + * @param[in] aMainloopContext A pointer to the mainloop context + * containing fd_sets. + * + */ + void Process(const void* aMainloopContext); + /** * Returns the bus speed between the host and the radio. * @@ -136,6 +160,17 @@ class SocketInterface : public ot::Spinel::SpinelInterface { } private: + /** + * Instructs `SocketInterface` to read data from radio over the + * socket. + * + * If a full Spinel frame is received, this method invokes the + * `HandleSocketFrame()` (on the `aCallback` object from constructor) to + * pass the received frame to be processed. + * + */ + void Read(void); + /** * Writes a given frame to the socket. * @@ -145,6 +180,22 @@ class SocketInterface : public ot::Spinel::SpinelInterface { */ void Write(const uint8_t* aFrame, uint16_t aLength); + /** + * Process received data. + * + * If a full frame is finished processing and we obtain the raw Spinel + * frame, this method invokes the `HandleSocketFrame()` (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 ProcessReceivedData(const uint8_t* aBuffer, uint16_t aLength); + + static void HandleSocketFrame(void* aContext, otError aError); + void HandleSocketFrame(otError aError); + /** * Opens file specified by aRadioUrl. *