198 lines
6.5 KiB
C
198 lines
6.5 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 *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;
|
|
}
|