blob: b8f19e8f92b4cabfc4cdfef6f78d8d05386d7bc5 [file] [log] [blame] [edit]
/*
* 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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#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;
}