/* * Copyright (C) 2020 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. */ #include "chpp/link.h" #include #include #include #include #include "chpp/log.h" #include "chpp/macros.h" #include "chpp/notifier.h" #include "chpp/platform/platform_link.h" #include "chpp/transport.h" // The set of signals to use for the linkSendThread. #define SIGNAL_EXIT UINT32_C(1 << 0) #define SIGNAL_DATA UINT32_C(1 << 1) #define SIGNAL_DATA_RX UINT32_C(1 << 2) struct ChppNotifier gCycleSendThreadNotifier; void cycleSendThread(void) { chppNotifierSignal(&gCycleSendThreadNotifier, 1); } /** * This thread is used to "send" TX data to the remote endpoint. The remote * endpoint is defined by the ChppTransportState pointer, so a loopback link * with a single CHPP instance can be supported. */ static void *linkSendThread(void *linkContext) { struct ChppLinuxLinkState *context = (struct ChppLinuxLinkState *)(linkContext); while (true) { if (context->manualSendCycle) { chppNotifierWait(&gCycleSendThreadNotifier); } uint32_t signal = chppNotifierTimedWait(&context->notifier, CHPP_TIME_MAX); if (signal & SIGNAL_EXIT) { break; } if (signal & SIGNAL_DATA) { enum ChppLinkErrorCode error; chppMutexLock(&context->mutex); if (context->remoteLinkState == NULL) { CHPP_LOGW("remoteLinkState is NULL"); error = CHPP_LINK_ERROR_NONE_SENT; } else if (!context->linkEstablished) { CHPP_LOGE("No (fake) link"); error = CHPP_LINK_ERROR_NO_LINK; } else { // Use notifiers only when there are 2 different link layers (i.e. no // loopback). Otherwise call chppRxDataCb directly. if (context->rxInRemoteEndpointWorker && context->remoteLinkState != context) { chppNotifierSignal(&context->remoteLinkState->notifier, SIGNAL_DATA_RX); // Wait for the RX thread to consume the buffer before we can modify // it. chppNotifierTimedWait(&context->rxNotifier, CHPP_TIME_MAX); } else if (!chppRxDataCb(context->remoteLinkState->transportContext, context->buf, context->bufLen)) { CHPP_LOGW("chppRxDataCb return state!=preamble (packet incomplete)"); } error = CHPP_LINK_ERROR_NONE_SENT; } context->bufLen = 0; chppLinkSendDoneCb(context->transportContext, error); chppMutexUnlock(&context->mutex); } if (signal & SIGNAL_DATA_RX) { CHPP_NOT_NULL(context->transportContext); CHPP_NOT_NULL(context->remoteLinkState); // Process RX data which are the TX data from the remote link. chppRxDataCb(context->transportContext, context->remoteLinkState->buf, context->remoteLinkState->bufLen); // Unblock the TX thread when the buffer has been consumed. chppNotifierSignal(&context->remoteLinkState->rxNotifier, 0x01); } } return NULL; } static void init(void *linkContext, struct ChppTransportState *transportContext) { struct ChppLinuxLinkState *context = (struct ChppLinuxLinkState *)(linkContext); context->bufLen = 0; context->transportContext = transportContext; chppMutexInit(&context->mutex); chppNotifierInit(&context->notifier); chppNotifierInit(&context->rxNotifier); chppNotifierInit(&gCycleSendThreadNotifier); pthread_create(&context->linkSendThread, NULL /* attr */, linkSendThread, context); if (context->linkThreadName != NULL) { pthread_setname_np(context->linkSendThread, context->linkThreadName); } } static void deinit(void *linkContext) { struct ChppLinuxLinkState *context = (struct ChppLinuxLinkState *)(linkContext); context->bufLen = 0; chppNotifierSignal(&context->notifier, SIGNAL_EXIT); if (context->manualSendCycle) { // Unblock the send thread so it exits. cycleSendThread(); } pthread_join(context->linkSendThread, NULL /* retval */); chppNotifierDeinit(&context->notifier); chppNotifierDeinit(&context->rxNotifier); chppNotifierDeinit(&gCycleSendThreadNotifier); chppMutexDeinit(&context->mutex); } static enum ChppLinkErrorCode send(void *linkContext, size_t len) { struct ChppLinuxLinkState *context = (struct ChppLinuxLinkState *)(linkContext); bool success = false; chppMutexLock(&context->mutex); if (context->bufLen != 0) { CHPP_LOGE("Failed to send data - link layer busy"); } else if (!context->isLinkActive) { success = false; } else { success = true; context->bufLen = len; } chppMutexUnlock(&context->mutex); if (success) { chppNotifierSignal(&context->notifier, SIGNAL_DATA); } return success ? CHPP_LINK_ERROR_NONE_QUEUED : CHPP_LINK_ERROR_BUSY; } static void doWork(void *linkContext, uint32_t signal) { UNUSED_VAR(linkContext); UNUSED_VAR(signal); } static void reset(void *linkContext) { struct ChppLinuxLinkState *context = (struct ChppLinuxLinkState *)(linkContext); deinit(context); init(context, context->transportContext); } static struct ChppLinkConfiguration getConfig(void *linkContext) { UNUSED_VAR(linkContext); const struct ChppLinkConfiguration config = { .txBufferLen = CHPP_LINUX_LINK_TX_MTU_BYTES, .rxBufferLen = CHPP_LINUX_LINK_RX_MTU_BYTES, }; return config; } static uint8_t *getTxBuffer(void *linkContext) { struct ChppLinuxLinkState *context = (struct ChppLinuxLinkState *)(linkContext); return &context->buf[0]; } const struct ChppLinkApi gLinuxLinkApi = { .init = &init, .deinit = &deinit, .send = &send, .doWork = &doWork, .reset = &reset, .getConfig = &getConfig, .getTxBuffer = &getTxBuffer, }; const struct ChppLinkApi *getLinuxLinkApi(void) { return &gLinuxLinkApi; }