Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c239f1e3 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge changes Ie1b0c687,I4e6dfdfa,I2da14a56,I47041785,I34388f76, ... into main...

Merge changes Ie1b0c687,I4e6dfdfa,I2da14a56,I47041785,I34388f76, ... into main am: 57220562 am: aec03115

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2926261



Change-Id: I21d2ad7c522c2abb2cbec21b3525f6f8584c1347
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 7fea7cff aec03115
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ cc_binary {
    srcs: [
        "main.cpp",
        "service.cpp",
        "socket_interface.cpp",
        "thread_chip.cpp",
        "utils.cpp",
    ],
@@ -63,6 +64,7 @@ cc_fuzz {
    ],

    srcs: [
        "socket_interface.cpp",
        "thread_chip.cpp",
        "utils.cpp",
        "fuzzer.cpp",
+301 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file
 *   This file includes the implementation for the Socket interface to radio
 * (RCP).
 */

#include "socket_interface.hpp"

#include <errno.h>
#include <openthread/logging.h>
#include <sys/inotify.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>

#include <string>

#include "common/code_utils.hpp"
#include "openthread/openthread-system.h"
#include "platform-posix.h"

namespace aidl {
namespace android {
namespace hardware {
namespace threadnetwork {

SocketInterface::SocketInterface(const ot::Url::Url& aRadioUrl)
    : mReceiveFrameCallback(nullptr),
      mReceiveFrameContext(nullptr),
      mReceiveFrameBuffer(nullptr),
      mSockFd(-1),
      mRadioUrl(aRadioUrl) {
    memset(&mInterfaceMetrics, 0, sizeof(mInterfaceMetrics));
    mInterfaceMetrics.mRcpInterfaceType = kSpinelInterfaceTypeVendor;
}

otError SocketInterface::Init(ReceiveFrameCallback aCallback, void* aCallbackContext,
                              RxFrameBuffer& aFrameBuffer) {
    otError error = OT_ERROR_NONE;

    VerifyOrExit(mSockFd == -1, error = OT_ERROR_ALREADY);

    WaitForSocketFileCreated(mRadioUrl.GetPath());

    mSockFd = OpenFile(mRadioUrl);
    VerifyOrExit(mSockFd != -1, error = OT_ERROR_FAILED);

    mReceiveFrameCallback = aCallback;
    mReceiveFrameContext = aCallbackContext;
    mReceiveFrameBuffer = &aFrameBuffer;

exit:
    return error;
}

SocketInterface::~SocketInterface(void) {
    Deinit();
}

void SocketInterface::Deinit(void) {
    CloseFile();

    mReceiveFrameCallback = nullptr;
    mReceiveFrameContext = nullptr;
    mReceiveFrameBuffer = nullptr;
}

otError SocketInterface::SendFrame(const uint8_t* aFrame, uint16_t aLength) {
    Write(aFrame, aLength);

    return OT_ERROR_NONE;
}

otError SocketInterface::WaitForFrame(uint64_t aTimeoutUs) {
    otError error = OT_ERROR_NONE;
    struct timeval timeout;
    timeout.tv_sec = static_cast<time_t>(aTimeoutUs / US_PER_S);
    timeout.tv_usec = static_cast<suseconds_t>(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<otSysMainloopContext*>(aMainloopContext);

    assert(context != nullptr);

    FD_SET(mSockFd, &context->mReadFdSet);

    if (context->mMaxFd < mSockFd) {
        context->mMaxFd = mSockFd;
    }
}

void SocketInterface::Process(const void* aMainloopContext) {
    const otSysMainloopContext* context =
            reinterpret_cast<const otSysMainloopContext*>(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<uint16_t>(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<SocketInterface*>(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;

    VerifyOrExit(sizeof(serverAddress.sun_path) > strlen(aRadioUrl.GetPath()),
                 otLogCritPlat("Invalid file path length"));
    strncpy(serverAddress.sun_path, aRadioUrl.GetPath(), sizeof(serverAddress.sun_path));
    serverAddress.sun_family = AF_UNIX;

    fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
    VerifyOrExit(fd != -1, otLogCritPlat("open(): errno=%s", strerror(errno)));

    if (connect(fd, reinterpret_cast<struct sockaddr*>(&serverAddress), sizeof(serverAddress)) ==
        -1) {
        otLogCritPlat("connect(): errno=%s", strerror(errno));
        close(fd);
        fd = -1;
    }

exit:
    return fd;
}

void SocketInterface::CloseFile(void) {
    VerifyOrExit(mSockFd != -1);

    VerifyOrExit(0 == close(mSockFd), otLogCritPlat("close(): errno=%s", strerror(errno)));
    VerifyOrExit(wait(nullptr) != -1 || errno == ECHILD,
                 otLogCritPlat("wait(): errno=%s", strerror(errno)));

    mSockFd = -1;

exit:
    return;
}

void SocketInterface::WaitForSocketFileCreated(const char* aPath) {
    int inotifyFd;
    int wd;
    int lastSlashIdx;
    std::string folderPath;
    std::string socketPath(aPath);

    VerifyOrExit(!IsSocketFileExisted(aPath));

    inotifyFd = inotify_init();
    VerifyOrDie(inotifyFd != -1, OT_EXIT_ERROR_ERRNO);

    lastSlashIdx = socketPath.find_last_of('/');
    VerifyOrDie(lastSlashIdx != std::string::npos, OT_EXIT_ERROR_ERRNO);

    folderPath = socketPath.substr(0, lastSlashIdx);
    wd = inotify_add_watch(inotifyFd, folderPath.c_str(), IN_CREATE);
    VerifyOrDie(wd != -1, OT_EXIT_ERROR_ERRNO);

    otLogInfoPlat("Waiting for socket file %s be created...", aPath);

    while (true) {
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(inotifyFd, &fds);
        struct timeval timeout = {kMaxSelectTimeMs / MS_PER_S,
                                  (kMaxSelectTimeMs % MS_PER_S) * MS_PER_S};

        int rval = select(inotifyFd + 1, &fds, nullptr, nullptr, &timeout);
        VerifyOrDie(rval >= 0, OT_EXIT_ERROR_ERRNO);

        if (rval == 0 && IsSocketFileExisted(aPath)) {
            break;
        }

        if (FD_ISSET(inotifyFd, &fds)) {
            char buffer[sizeof(struct inotify_event)];
            ssize_t bytesRead = read(inotifyFd, buffer, sizeof(buffer));

            VerifyOrDie(bytesRead >= 0, OT_EXIT_ERROR_ERRNO);

            struct inotify_event* event = reinterpret_cast<struct inotify_event*>(buffer);
            if ((event->mask & IN_CREATE) && IsSocketFileExisted(aPath)) {
                break;
            }
        }
    }

    close(inotifyFd);

exit:
    otLogInfoPlat("Socket file: %s is created", aPath);
    return;
}

bool SocketInterface::IsSocketFileExisted(const char* aPath) {
    struct stat st;
    return stat(aPath, &st) == 0 && S_ISSOCK(st.st_mode);
}

}  // namespace threadnetwork
}  // namespace hardware
}  // namespace android
}  // namespace aidl
+258 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file
 *   This file includes definitions for the Socket interface interface to radio
 * (RCP).
 */

#include "lib/spinel/spinel_interface.hpp"
#include "lib/url/url.hpp"

namespace aidl {
namespace android {
namespace hardware {
namespace threadnetwork {

/**
 * Defines a Socket interface to the Radio Co-processor (RCP)
 *
 */
class SocketInterface : public ot::Spinel::SpinelInterface {
  public:
    /**
     * Initializes the object.
     *
     * @param[in] aRadioUrl  RadioUrl parsed from radio url.
     *
     */
    explicit SocketInterface(const ot::Url::Url& aRadioUrl);

    /**
     * This destructor deinitializes the object.
     *
     */
    ~SocketInterface();

    /**
     * Initializes the interface to the Radio Co-processor (RCP)
     *
     * @note This method should be called before reading and sending Spinel
     * frames to the interface.
     *
     * @param[in] aCallback         Callback on frame received
     * @param[in] aCallbackContext  Callback context
     * @param[in] aFrameBuffer      A reference to a `RxFrameBuffer` object.
     *
     * @retval OT_ERROR_NONE       The interface is initialized successfully
     * @retval OT_ERROR_ALREADY    The interface is already initialized.
     * @retval OT_ERROR_FAILED     Failed to initialize the interface.
     *
     */
    otError Init(ReceiveFrameCallback aCallback, void* aCallbackContext,
                 RxFrameBuffer& aFrameBuffer);

    /**
     * Deinitializes the interface to the RCP.
     *
     */
    void Deinit(void);

    /**
     * Sends a Spinel frame to Radio Co-processor (RCP) over the
     * socket.
     *
     * @param[in] aFrame     A pointer to buffer containing the Spinel frame to
     * send.
     * @param[in] aLength    The length (number of bytes) in the frame.
     *
     * @retval OT_ERROR_NONE     Successfully sent the Spinel frame.
     * @retval OT_ERROR_FAILED   Failed to send a frame.
     *
     */
    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.
     *
     * @param[in,out]   aMainloopContext  A pointer to the mainloop context
     * containing fd_sets.
     *
     */
    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.
     *
     * @return   Bus speed in bits/second.
     *
     */
    uint32_t GetBusSpeed(void) const { return 1000000; }

    /**
     * Hardware resets the RCP.
     *
     * @retval OT_ERROR_NONE            Successfully reset the RCP.
     * @retval OT_ERROR_NOT_IMPLEMENT   The hardware reset is not implemented.
     *
     */
    otError HardwareReset(void) { return OT_ERROR_NOT_IMPLEMENTED; }

    /**
     * Returns the RCP interface metrics.
     *
     * @return The RCP interface metrics.
     *
     */
    const otRcpInterfaceMetrics* GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; }

    /**
     * Indicates whether or not the given interface matches this interface name.
     *
     * @param[in] aInterfaceName A pointer to the interface name.
     *
     * @retval TRUE   The given interface name matches this interface name.
     * @retval FALSE  The given interface name doesn't match this interface
     * name.
     */
    static bool IsInterfaceNameMatch(const char* aInterfaceName) {
        static const char kInterfaceName[] = "spinel+socket";
        return (strncmp(aInterfaceName, kInterfaceName, strlen(kInterfaceName)) == 0);
    }

  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.
     *
     * @param[in] aFrame  A pointer to buffer containing the frame to write.
     * @param[in] aLength The length (number of bytes) in the frame.
     *
     */
    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.
     *
     * @param[in] aRadioUrl  A reference to object containing path to file and
     * data for configuring the connection with tty type file.
     *
     * @retval The file descriptor of newly opened file.
     * @retval -1 Fail to open file.
     *
     */
    int OpenFile(const ot::Url::Url& aRadioUrl);

    /**
     * Closes file associated with the file descriptor.
     *
     */
    void CloseFile(void);

    /**
     * Check if socket file is created.
     *
     * @param[in] aPath  Socket file path name.
     *
     * @retval TRUE The required socket file is created.
     * @retval FALSE The required socket file is not created.
     *
     */
    bool IsSocketFileExisted(const char* aPath);

    /**
     * Wait until the socket file is created.
     *
     * @param[in] aPath  Socket file path name.
     *
     */
    void WaitForSocketFileCreated(const char* aPath);

    enum {
        kMaxSelectTimeMs = 2000,  ///< Maximum wait time in Milliseconds for file
                                  ///< descriptor to become available.
    };

    ReceiveFrameCallback mReceiveFrameCallback;
    void* mReceiveFrameContext;
    RxFrameBuffer* mReceiveFrameBuffer;

    int mSockFd;
    const ot::Url::Url& mRadioUrl;

    otRcpInterfaceMetrics mInterfaceMetrics;

    // Non-copyable, intentionally not implemented.
    SocketInterface(const SocketInterface&);
    SocketInterface& operator=(const SocketInterface&);
};

}  // namespace threadnetwork
}  // namespace hardware
}  // namespace android
}  // namespace aidl
+3 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <utils/Log.h>

#include "hdlc_interface.hpp"
#include "socket_interface.hpp"
#include "spi_interface.hpp"

namespace aidl {
@@ -43,6 +44,8 @@ ThreadChip::ThreadChip(char* url) : mUrl(), mRxFrameBuffer(), mCallback(nullptr)
        mSpinelInterface = std::make_shared<ot::Posix::SpiInterface>(mUrl);
    } else if (ot::Posix::HdlcInterface::IsInterfaceNameMatch(interfaceName)) {
        mSpinelInterface = std::make_shared<ot::Posix::HdlcInterface>(mUrl);
    } else if (SocketInterface::IsInterfaceNameMatch(interfaceName)) {
        mSpinelInterface = std::make_shared<SocketInterface>(mUrl);
    } else {
        ALOGE("The interface \"%s\" is not supported", interfaceName);
        exit(EXIT_FAILURE);