android13/system/chre/chpp/clients/loopback.c

216 lines
8.3 KiB
C

/*
* 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 *context) {
CHPP_LOGD("Loopback client init");
context->loopbackClientContext =
chppMalloc(sizeof(struct ChppLoopbackClientState));
CHPP_NOT_NULL(context->loopbackClientContext);
memset(context->loopbackClientContext, 0,
sizeof(struct ChppLoopbackClientState));
context->loopbackClientContext->client.appContext = context;
chppClientInit(&context->loopbackClientContext->client, CHPP_HANDLE_LOOPBACK);
context->loopbackClientContext->testResult.error = CHPP_APP_ERROR_NONE;
context->loopbackClientContext->client.openState = CHPP_OPEN_STATE_OPENED;
}
void chppLoopbackClientDeinit(struct ChppAppState *context) {
CHPP_LOGD("Loopback client deinit");
CHPP_NOT_NULL(context);
CHPP_NOT_NULL(context->loopbackClientContext);
chppClientDeinit(&context->loopbackClientContext->client);
CHPP_FREE_AND_NULLIFY(context->loopbackClientContext);
}
bool chppDispatchLoopbackServiceResponse(struct ChppAppState *context,
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_NOT_NULL(context->loopbackClientContext);
CHPP_NOT_NULL(context->loopbackClientContext->loopbackData);
CHPP_ASSERT(chppClientTimestampResponse(
&context->loopbackClientContext->client,
&context->loopbackClientContext->runLoopbackTest,
(const struct ChppAppHeader *)response));
context->loopbackClientContext->testResult.error = CHPP_APP_ERROR_NONE;
context->loopbackClientContext->testResult.responseLen = len;
context->loopbackClientContext->testResult.firstError = len;
context->loopbackClientContext->testResult.byteErrors = 0;
context->loopbackClientContext->testResult.rttNs =
context->loopbackClientContext->runLoopbackTest.responseTimeNs -
context->loopbackClientContext->runLoopbackTest.requestTimeNs;
if (context->loopbackClientContext->testResult.requestLen !=
context->loopbackClientContext->testResult.responseLen) {
context->loopbackClientContext->testResult.error =
CHPP_APP_ERROR_INVALID_LENGTH;
context->loopbackClientContext->testResult.firstError =
MIN(context->loopbackClientContext->testResult.requestLen,
context->loopbackClientContext->testResult.responseLen);
}
for (size_t loc = CHPP_LOOPBACK_HEADER_LEN;
loc < MIN(context->loopbackClientContext->testResult.requestLen,
context->loopbackClientContext->testResult.responseLen);
loc++) {
if (context->loopbackClientContext
->loopbackData[loc - CHPP_LOOPBACK_HEADER_LEN] != response[loc]) {
context->loopbackClientContext->testResult.error =
CHPP_APP_ERROR_UNSPECIFIED;
context->loopbackClientContext->testResult.firstError =
MIN(context->loopbackClientContext->testResult.firstError,
loc - CHPP_LOOPBACK_HEADER_LEN);
context->loopbackClientContext->testResult.byteErrors++;
}
}
CHPP_LOGI("Loopback client RX err=0x%" PRIx16 " len=%" PRIuSIZE
" req len=%" PRIuSIZE " first err=%" PRIuSIZE
" total err=%" PRIuSIZE,
context->loopbackClientContext->testResult.error,
context->loopbackClientContext->testResult.responseLen,
context->loopbackClientContext->testResult.requestLen,
context->loopbackClientContext->testResult.firstError,
context->loopbackClientContext->testResult.byteErrors);
// Notify waiting (synchronous) client
chppMutexLock(&context->loopbackClientContext->client.responseMutex);
context->loopbackClientContext->client.responseReady = true;
chppConditionVariableSignal(
&context->loopbackClientContext->client.responseCondVar);
chppMutexUnlock(&context->loopbackClientContext->client.responseMutex);
return true;
}
struct ChppLoopbackTestResult chppRunLoopbackTest(struct ChppAppState *context,
const uint8_t *buf,
size_t len) {
CHPP_LOGI("Loopback client TX len=%" PRIuSIZE,
len + CHPP_LOOPBACK_HEADER_LEN);
struct ChppLoopbackTestResult result;
if (context == NULL) {
CHPP_LOGE("Cannot run loopback test with null app");
result.error = CHPP_APP_ERROR_UNSUPPORTED;
} else if (!chppWaitForDiscoveryComplete(context, 0 /* timeoutMs */)) {
result.error = CHPP_APP_ERROR_NOT_READY;
} else {
CHPP_NOT_NULL(context->loopbackClientContext);
if (context->loopbackClientContext->testResult.error ==
CHPP_APP_ERROR_BLOCKED) {
CHPP_DEBUG_ASSERT_LOG(false, "Another loopback in progress");
} else {
context->loopbackClientContext->testResult.error = CHPP_APP_ERROR_BLOCKED;
context->loopbackClientContext->testResult.requestLen =
len + CHPP_LOOPBACK_HEADER_LEN;
context->loopbackClientContext->testResult.responseLen = 0;
context->loopbackClientContext->testResult.firstError = 0;
context->loopbackClientContext->testResult.byteErrors = 0;
context->loopbackClientContext->testResult.rttNs = 0;
context->loopbackClientContext->runLoopbackTest.requestTimeNs =
CHPP_TIME_NONE;
context->loopbackClientContext->runLoopbackTest.responseTimeNs =
CHPP_TIME_NONE;
if (len == 0) {
CHPP_LOGE("Loopback payload=0!");
context->loopbackClientContext->testResult.error =
CHPP_APP_ERROR_INVALID_LENGTH;
} else {
uint8_t *loopbackRequest = (uint8_t *)chppAllocClientRequest(
&context->loopbackClientContext->client,
context->loopbackClientContext->testResult.requestLen);
if (loopbackRequest == NULL) {
// OOM
context->loopbackClientContext->testResult.requestLen = 0;
context->loopbackClientContext->testResult.error = CHPP_APP_ERROR_OOM;
CHPP_LOG_OOM();
} else {
context->loopbackClientContext->loopbackData = buf;
memcpy(&loopbackRequest[CHPP_LOOPBACK_HEADER_LEN], buf, len);
if (!chppSendTimestampedRequestAndWaitTimeout(
&context->loopbackClientContext->client,
&context->loopbackClientContext->runLoopbackTest,
loopbackRequest,
context->loopbackClientContext->testResult.requestLen,
CHPP_NSEC_PER_SEC /* 1s */)) {
context->loopbackClientContext->testResult.error =
CHPP_APP_ERROR_UNSPECIFIED;
} // else {context->loopbackClientContext->testResult is now
// populated}
}
}
}
result = context->loopbackClientContext->testResult;
}
return result;
}