/* * 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/clients/loopback.h" #include #include #include #include #include "chpp/app.h" #include "chpp/clients.h" #include "chpp/log.h" #include "chpp/memory.h" #include "chpp/transport.h" #include "chpp/clients/discovery.h" /************************************************ * Prototypes ***********************************************/ /************************************************ * Private Definitions ***********************************************/ /** * Structure to maintain state for the loopback client and its Request/Response * (RR) functionality. */ struct ChppLoopbackClientState { struct ChppClientState client; // Loopback client state struct ChppRequestResponseState runLoopbackTest; // Loopback test state struct ChppLoopbackTestResult testResult; // Last test result const uint8_t *loopbackData; // Pointer to loopback data }; /************************************************ * Public Functions ***********************************************/ void chppLoopbackClientInit(struct ChppAppState *appState) { CHPP_LOGD("Loopback client init"); CHPP_DEBUG_NOT_NULL(appState); appState->loopbackClientContext = chppMalloc(sizeof(struct ChppLoopbackClientState)); CHPP_NOT_NULL(appState->loopbackClientContext); struct ChppLoopbackClientState *state = appState->loopbackClientContext; memset(state, 0, sizeof(struct ChppLoopbackClientState)); state->client.appContext = appState; chppClientInit(&state->client, CHPP_HANDLE_LOOPBACK); state->testResult.error = CHPP_APP_ERROR_NONE; state->client.openState = CHPP_OPEN_STATE_OPENED; } void chppLoopbackClientDeinit(struct ChppAppState *appState) { CHPP_LOGD("Loopback client deinit"); CHPP_NOT_NULL(appState); CHPP_NOT_NULL(appState->loopbackClientContext); chppClientDeinit(&appState->loopbackClientContext->client); CHPP_FREE_AND_NULLIFY(appState->loopbackClientContext); } bool chppDispatchLoopbackServiceResponse(struct ChppAppState *appState, const uint8_t *response, size_t len) { CHPP_LOGD("Loopback client dispatch service response"); CHPP_ASSERT(len >= CHPP_LOOPBACK_HEADER_LEN); CHPP_NOT_NULL(response); CHPP_DEBUG_NOT_NULL(appState); struct ChppLoopbackClientState *state = appState->loopbackClientContext; CHPP_NOT_NULL(state); CHPP_NOT_NULL(state->loopbackData); CHPP_ASSERT( chppClientTimestampResponse(&state->client, &state->runLoopbackTest, (const struct ChppAppHeader *)response)); struct ChppLoopbackTestResult *result = &state->testResult; result->error = CHPP_APP_ERROR_NONE; result->responseLen = len; result->firstError = len; result->byteErrors = 0; result->rttNs = state->runLoopbackTest.responseTimeNs - state->runLoopbackTest.requestTimeNs; if (result->requestLen != result->responseLen) { result->error = CHPP_APP_ERROR_INVALID_LENGTH; result->firstError = MIN(result->requestLen, result->responseLen); } for (size_t loc = CHPP_LOOPBACK_HEADER_LEN; loc < MIN(result->requestLen, result->responseLen); loc++) { if (state->loopbackData[loc - CHPP_LOOPBACK_HEADER_LEN] != response[loc]) { result->error = CHPP_APP_ERROR_UNSPECIFIED; result->firstError = MIN(result->firstError, loc - CHPP_LOOPBACK_HEADER_LEN); result->byteErrors++; } } CHPP_LOGD("Loopback client RX err=0x%" PRIx16 " len=%" PRIuSIZE " req len=%" PRIuSIZE " first err=%" PRIuSIZE " total err=%" PRIuSIZE, result->error, result->responseLen, result->requestLen, result->firstError, result->byteErrors); // Notify waiting (synchronous) client chppMutexLock(&state->client.responseMutex); state->client.responseReady = true; chppConditionVariableSignal(&state->client.responseCondVar); chppMutexUnlock(&state->client.responseMutex); return true; } struct ChppLoopbackTestResult chppRunLoopbackTest(struct ChppAppState *appState, const uint8_t *buf, size_t len) { CHPP_LOGD("Loopback client TX len=%" PRIuSIZE, len + CHPP_LOOPBACK_HEADER_LEN); if (appState == NULL) { CHPP_LOGE("Cannot run loopback test with null app"); struct ChppLoopbackTestResult result; result.error = CHPP_APP_ERROR_UNSUPPORTED; return result; } if (!chppWaitForDiscoveryComplete(appState, 0 /* timeoutMs */)) { struct ChppLoopbackTestResult result; result.error = CHPP_APP_ERROR_NOT_READY; return result; } struct ChppLoopbackClientState *state = appState->loopbackClientContext; CHPP_NOT_NULL(state); struct ChppLoopbackTestResult *result = &state->testResult; if (result->error == CHPP_APP_ERROR_BLOCKED) { CHPP_DEBUG_ASSERT_LOG(false, "Another loopback in progress"); return *result; } result->error = CHPP_APP_ERROR_BLOCKED; result->requestLen = len + CHPP_LOOPBACK_HEADER_LEN; result->responseLen = 0; result->firstError = 0; result->byteErrors = 0; result->rttNs = 0; state->runLoopbackTest.requestTimeNs = CHPP_TIME_NONE; state->runLoopbackTest.responseTimeNs = CHPP_TIME_NONE; if (len == 0) { CHPP_LOGE("Loopback payload=0!"); result->error = CHPP_APP_ERROR_INVALID_LENGTH; return *result; } uint8_t *loopbackRequest = (uint8_t *)chppAllocClientRequest(&state->client, result->requestLen); if (loopbackRequest == NULL) { result->requestLen = 0; result->error = CHPP_APP_ERROR_OOM; CHPP_LOG_OOM(); return *result; } state->loopbackData = buf; memcpy(&loopbackRequest[CHPP_LOOPBACK_HEADER_LEN], buf, len); if (!chppSendTimestampedRequestAndWaitTimeout( &state->client, &state->runLoopbackTest, loopbackRequest, result->requestLen, 5 * CHPP_NSEC_PER_SEC)) { result->error = CHPP_APP_ERROR_UNSPECIFIED; } return *result; }