1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "chpp/clients/loopback.h"
18
19 #include <stdbool.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <string.h>
23
24 #include "chpp/app.h"
25 #include "chpp/clients.h"
26 #include "chpp/clients/discovery.h"
27 #include "chpp/log.h"
28 #include "chpp/memory.h"
29 #include "chpp/transport.h"
30
31 /************************************************
32 * Prototypes
33 ***********************************************/
34
35 /************************************************
36 * Private Definitions
37 ***********************************************/
38
39 /**
40 * Structure to maintain state for the loopback client and its Request/Response
41 * (RR) functionality.
42 */
43 struct ChppLoopbackClientState {
44 struct ChppEndpointState client; // CHPP client state
45 struct ChppOutgoingRequestState runLoopbackTest; // Loopback test state
46
47 struct ChppLoopbackTestResult testResult; // Last test result
48 const uint8_t *loopbackData; // Pointer to loopback data
49 };
50
51 // A loopback test buffer used for chppRunLoopbackTestAsync.
52 #define LOOPBACK_BUF_LEN 3
53 static const uint8_t gLoopbackBuf[LOOPBACK_BUF_LEN] = {1, 2, 3};
54
55 /************************************************
56 * Public Functions
57 ***********************************************/
58
chppLoopbackClientInit(struct ChppAppState * appState)59 void chppLoopbackClientInit(struct ChppAppState *appState) {
60 CHPP_LOGD("Loopback client init");
61 CHPP_DEBUG_NOT_NULL(appState);
62 if (appState->loopbackClientContext != NULL) {
63 CHPP_LOGE("Loopback client already initialized");
64 return;
65 }
66
67 appState->loopbackClientContext =
68 chppMalloc(sizeof(struct ChppLoopbackClientState));
69 CHPP_NOT_NULL(appState->loopbackClientContext);
70 struct ChppLoopbackClientState *state = appState->loopbackClientContext;
71 memset(state, 0, sizeof(struct ChppLoopbackClientState));
72
73 state->client.appContext = appState;
74 chppClientInit(&state->client, CHPP_HANDLE_LOOPBACK);
75 state->testResult.error = CHPP_APP_ERROR_NONE;
76 state->client.openState = CHPP_OPEN_STATE_OPENED;
77 }
78
chppLoopbackClientDeinit(struct ChppAppState * appState)79 void chppLoopbackClientDeinit(struct ChppAppState *appState) {
80 CHPP_LOGD("Loopback client deinit");
81 CHPP_NOT_NULL(appState);
82 CHPP_NOT_NULL(appState->loopbackClientContext);
83
84 chppClientDeinit(&appState->loopbackClientContext->client);
85 CHPP_FREE_AND_NULLIFY(appState->loopbackClientContext);
86 }
87
chppDispatchLoopbackServiceResponse(struct ChppAppState * appState,const uint8_t * response,size_t len)88 bool chppDispatchLoopbackServiceResponse(struct ChppAppState *appState,
89 const uint8_t *response, size_t len) {
90 CHPP_LOGD("Loopback client dispatch service response");
91 CHPP_ASSERT(len >= CHPP_LOOPBACK_HEADER_LEN);
92 CHPP_NOT_NULL(response);
93 CHPP_DEBUG_NOT_NULL(appState);
94 struct ChppLoopbackClientState *state = appState->loopbackClientContext;
95 CHPP_NOT_NULL(state);
96 CHPP_NOT_NULL(state->loopbackData);
97
98 CHPP_ASSERT(chppTimestampIncomingResponse(
99 state->client.appContext, &state->runLoopbackTest,
100 (const struct ChppAppHeader *)response));
101
102 struct ChppLoopbackTestResult *result = &state->testResult;
103
104 chppMutexLock(&state->client.syncResponse.mutex);
105 result->error = CHPP_APP_ERROR_NONE;
106 result->responseLen = len;
107 result->firstError = len;
108 result->byteErrors = 0;
109 result->rttNs = state->runLoopbackTest.responseTimeNs -
110 state->runLoopbackTest.requestTimeNs;
111
112 if (result->requestLen != result->responseLen) {
113 result->error = CHPP_APP_ERROR_INVALID_LENGTH;
114 result->firstError = MIN(result->requestLen, result->responseLen);
115 }
116
117 for (size_t loc = CHPP_LOOPBACK_HEADER_LEN;
118 loc < MIN(result->requestLen, result->responseLen); loc++) {
119 if (state->loopbackData[loc - CHPP_LOOPBACK_HEADER_LEN] != response[loc]) {
120 result->error = CHPP_APP_ERROR_UNSPECIFIED;
121 result->firstError =
122 MIN(result->firstError, loc - CHPP_LOOPBACK_HEADER_LEN);
123 result->byteErrors++;
124 }
125 }
126
127 CHPP_LOGI("Loopback client RX err=0x%" PRIx16 " len=%" PRIuSIZE
128 " req len=%" PRIuSIZE " first err=%" PRIuSIZE
129 " total err=%" PRIuSIZE,
130 result->error, result->responseLen, result->requestLen,
131 result->firstError, result->byteErrors);
132
133 // Notify waiting (synchronous) client
134 state->client.syncResponse.ready = true;
135 chppConditionVariableSignal(&state->client.syncResponse.condVar);
136 chppMutexUnlock(&state->client.syncResponse.mutex);
137
138 return true;
139 }
140
chppLoopbackCheckPreconditions(struct ChppAppState * appState)141 static enum ChppAppErrorCode chppLoopbackCheckPreconditions(
142 struct ChppAppState *appState) {
143 if (appState == NULL) {
144 CHPP_LOGE("Cannot run loopback test with null app");
145 return CHPP_APP_ERROR_UNSUPPORTED;
146 }
147
148 if (!chppWaitForDiscoveryComplete(appState, 0 /* timeoutMs */)) {
149 return CHPP_APP_ERROR_NOT_READY;
150 }
151
152 return CHPP_APP_ERROR_NONE;
153 }
154
155 /**
156 * Internal method for running the loopback test (sync or async).
157 *
158 * Note that the input buf must be valid for duration of the loopback test, so
159 * it is recommended to use a read-only constant.
160 *
161 * @param appState Application layer state.
162 * @param buf Input data. Cannot be null.
163 * @param len Length of input data in bytes.
164 * @param sync If true, runs the loopback test in synchronous mode.
165 * @param result A non-null pointer to store the detailed result of the test.
166 *
167 * @return true if the test was successfully started
168 */
chppRunLoopbackTestInternal(struct ChppAppState * appState,const uint8_t * buf,size_t len,bool sync,struct ChppLoopbackTestResult * out)169 static bool chppRunLoopbackTestInternal(struct ChppAppState *appState,
170 const uint8_t *buf, size_t len,
171 bool sync,
172 struct ChppLoopbackTestResult *out) {
173 CHPP_NOT_NULL(out);
174 bool success = false;
175 CHPP_LOGD("Loopback client TX len=%" PRIuSIZE,
176 len + CHPP_LOOPBACK_HEADER_LEN);
177
178 enum ChppAppErrorCode error = chppLoopbackCheckPreconditions(appState);
179 if (error != CHPP_APP_ERROR_NONE) {
180 out->error = error;
181 } else if (len == 0) {
182 CHPP_LOGE("Loopback payload=0!");
183 out->error = CHPP_APP_ERROR_INVALID_LENGTH;
184 } else {
185 struct ChppLoopbackClientState *state = appState->loopbackClientContext;
186 CHPP_NOT_NULL(state);
187 chppMutexLock(&state->client.syncResponse.mutex);
188 struct ChppLoopbackTestResult *result = &state->testResult;
189
190 if (result->error == CHPP_APP_ERROR_BLOCKED) {
191 CHPP_DEBUG_ASSERT_LOG(false, "Another loopback in progress");
192 out->error = CHPP_APP_ERROR_BLOCKED;
193 } else {
194 memset(result, 0, sizeof(struct ChppLoopbackTestResult));
195 result->error = CHPP_APP_ERROR_BLOCKED;
196 result->requestLen = len + CHPP_LOOPBACK_HEADER_LEN;
197
198 uint8_t *loopbackRequest =
199 (uint8_t *)chppAllocClientRequest(&state->client, result->requestLen);
200
201 if (loopbackRequest == NULL) {
202 CHPP_LOG_OOM();
203 result->error = CHPP_APP_ERROR_OOM;
204 } else {
205 state->loopbackData = buf;
206 memcpy(&loopbackRequest[CHPP_LOOPBACK_HEADER_LEN], buf, len);
207
208 chppMutexUnlock(&state->client.syncResponse.mutex);
209 if (sync) {
210 if (!chppClientSendTimestampedRequestAndWaitTimeout(
211 &state->client, &state->runLoopbackTest, loopbackRequest,
212 result->requestLen, 5 * CHPP_NSEC_PER_SEC)) {
213 result->error = CHPP_APP_ERROR_UNSPECIFIED;
214 } else {
215 success = true;
216 }
217 } else {
218 if (!chppClientSendTimestampedRequestOrFail(
219 &state->client, &state->runLoopbackTest, loopbackRequest,
220 result->requestLen, 5 * CHPP_NSEC_PER_SEC)) {
221 result->error = CHPP_APP_ERROR_UNSPECIFIED;
222 } else {
223 success = true;
224 }
225 }
226 chppMutexLock(&state->client.syncResponse.mutex);
227
228 *out = state->testResult;
229 }
230 }
231
232 chppMutexUnlock(&state->client.syncResponse.mutex);
233 }
234
235 return success;
236 }
237
chppRunLoopbackTest(struct ChppAppState * appState,const uint8_t * buf,size_t len)238 struct ChppLoopbackTestResult chppRunLoopbackTest(struct ChppAppState *appState,
239 const uint8_t *buf,
240 size_t len) {
241 struct ChppLoopbackTestResult result;
242 chppRunLoopbackTestInternal(appState, buf, len, /* sync= */ true, &result);
243 return result;
244 }
245
chppRunLoopbackTestAsync(struct ChppAppState * appState)246 enum ChppAppErrorCode chppRunLoopbackTestAsync(struct ChppAppState *appState) {
247 struct ChppLoopbackTestResult result;
248 bool success = chppRunLoopbackTestInternal(
249 appState, gLoopbackBuf, LOOPBACK_BUF_LEN, /* sync= */ false, &result);
250 // Override the result for the success case because for async, the stored
251 // error code would be CHPP_APP_ERROR_BLOCKED until the response arrives.
252 return success ? CHPP_APP_ERROR_NONE : result.error;
253 }