blob: c73473a691c3ae5af783310af608a02299c65f4e [file] [log] [blame]
/*
* 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/memory.h"
#include "chpp/platform/log.h"
#include "chpp/transport.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
uint8_t *loopbackRequest; // Pointer to saved loopback data
};
// Note: This global definition of gLoopbackClientContext supports only one
// instance of the CHPP loopback client at a time.
struct ChppLoopbackClientState gLoopbackClientContext = {};
/************************************************
* Public Functions
***********************************************/
void chppLoopbackClientInit(struct ChppAppState *context) {
gLoopbackClientContext.client.appContext = context;
chppClientInit(&gLoopbackClientContext.client, CHPP_HANDLE_LOOPBACK);
gLoopbackClientContext.testResult.error = CHPP_APP_ERROR_NONE;
}
void chppLoopbackClientDeinit() {
chppClientDeinit(&gLoopbackClientContext.client);
}
bool chppDispatchLoopbackServiceResponse(struct ChppAppState *context,
const uint8_t *buf, size_t len) {
UNUSED_VAR(context);
CHPP_ASSERT(len < CHPP_LOOPBACK_HEADER_LEN);
chppClientTimestampResponse(&gLoopbackClientContext.runLoopbackTest,
(struct ChppAppHeader *)buf);
gLoopbackClientContext.testResult.error = CHPP_APP_ERROR_NONE;
gLoopbackClientContext.testResult.responseLen = len;
gLoopbackClientContext.testResult.firstError = len;
gLoopbackClientContext.testResult.byteErrors = 0;
gLoopbackClientContext.testResult.rtt =
gLoopbackClientContext.runLoopbackTest.responseTime -
gLoopbackClientContext.runLoopbackTest.requestTime;
if (gLoopbackClientContext.testResult.requestLen !=
gLoopbackClientContext.testResult.responseLen) {
gLoopbackClientContext.testResult.error = CHPP_APP_ERROR_INVALID_LENGTH;
gLoopbackClientContext.testResult.firstError =
MIN(gLoopbackClientContext.testResult.requestLen,
gLoopbackClientContext.testResult.responseLen);
}
for (size_t loc = CHPP_LOOPBACK_HEADER_LEN;
loc < MIN(gLoopbackClientContext.testResult.requestLen,
gLoopbackClientContext.testResult.responseLen);
loc++) {
if (gLoopbackClientContext.loopbackRequest[loc] != buf[loc]) {
gLoopbackClientContext.testResult.error = CHPP_APP_ERROR_UNSPECIFIED;
gLoopbackClientContext.testResult.firstError =
MIN(gLoopbackClientContext.testResult.firstError, loc);
gLoopbackClientContext.testResult.byteErrors++;
}
}
return true;
}
struct ChppLoopbackTestResult chppRunLoopbackTest(struct ChppAppState *context,
const uint8_t *buf,
size_t len) {
UNUSED_VAR(context);
if (gLoopbackClientContext.testResult.error == CHPP_APP_ERROR_BLOCKED) {
CHPP_LOGE("Loopback test cannot be run while another is in progress");
CHPP_DEBUG_ASSERT(false);
} else {
gLoopbackClientContext.testResult.error = CHPP_APP_ERROR_BLOCKED;
gLoopbackClientContext.testResult.requestLen =
len + CHPP_LOOPBACK_HEADER_LEN;
gLoopbackClientContext.testResult.responseLen = 0;
gLoopbackClientContext.testResult.firstError = 0;
gLoopbackClientContext.testResult.byteErrors = 0;
gLoopbackClientContext.testResult.rtt = 0;
if (len == 0) { // Length too short for a loopback test
gLoopbackClientContext.testResult.error = CHPP_APP_ERROR_INVALID_LENGTH;
} else {
gLoopbackClientContext.loopbackRequest =
(uint8_t *)chppAllocClientRequest(
&gLoopbackClientContext.client,
gLoopbackClientContext.testResult.requestLen);
if (gLoopbackClientContext.loopbackRequest == NULL) {
// OOM
gLoopbackClientContext.testResult.requestLen = 0;
gLoopbackClientContext.testResult.error = CHPP_APP_ERROR_OOM;
CHPP_LOG_OOM();
} else {
memcpy(
&gLoopbackClientContext.loopbackRequest[CHPP_LOOPBACK_HEADER_LEN],
buf, len);
if (!chppSendTimestampedRequestAndWait(
&gLoopbackClientContext.client,
&gLoopbackClientContext.runLoopbackTest,
gLoopbackClientContext.loopbackRequest,
gLoopbackClientContext.testResult.requestLen)) {
gLoopbackClientContext.testResult.error = CHPP_APP_ERROR_UNSPECIFIED;
} // else {gLoopbackClientContext.testResult is now populated}
}
}
}
return gLoopbackClientContext.testResult;
}