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/log.h"
27 #include "chpp/memory.h"
28 #include "chpp/transport.h"
29
30 #include "chpp/clients/discovery.h"
31
32 /************************************************
33 * Prototypes
34 ***********************************************/
35
36 /************************************************
37 * Private Definitions
38 ***********************************************/
39
40 /**
41 * Structure to maintain state for the loopback client and its Request/Response
42 * (RR) functionality.
43 */
44 struct ChppLoopbackClientState {
45 struct ChppClientState client; // Loopback client state
46 struct ChppRequestResponseState runLoopbackTest; // Loopback test state
47
48 struct ChppLoopbackTestResult testResult; // Last test result
49 const uint8_t *loopbackData; // Pointer to loopback data
50 };
51
52 /************************************************
53 * Public Functions
54 ***********************************************/
55
chppLoopbackClientInit(struct ChppAppState * appState)56 void chppLoopbackClientInit(struct ChppAppState *appState) {
57 CHPP_LOGD("Loopback client init");
58 CHPP_DEBUG_NOT_NULL(appState);
59
60 appState->loopbackClientContext =
61 chppMalloc(sizeof(struct ChppLoopbackClientState));
62 CHPP_NOT_NULL(appState->loopbackClientContext);
63 struct ChppLoopbackClientState *state = appState->loopbackClientContext;
64 memset(state, 0, sizeof(struct ChppLoopbackClientState));
65
66 state->client.appContext = appState;
67 chppClientInit(&state->client, CHPP_HANDLE_LOOPBACK);
68 state->testResult.error = CHPP_APP_ERROR_NONE;
69 state->client.openState = CHPP_OPEN_STATE_OPENED;
70 }
71
chppLoopbackClientDeinit(struct ChppAppState * appState)72 void chppLoopbackClientDeinit(struct ChppAppState *appState) {
73 CHPP_LOGD("Loopback client deinit");
74 CHPP_NOT_NULL(appState);
75 CHPP_NOT_NULL(appState->loopbackClientContext);
76
77 chppClientDeinit(&appState->loopbackClientContext->client);
78 CHPP_FREE_AND_NULLIFY(appState->loopbackClientContext);
79 }
80
chppDispatchLoopbackServiceResponse(struct ChppAppState * appState,const uint8_t * response,size_t len)81 bool chppDispatchLoopbackServiceResponse(struct ChppAppState *appState,
82 const uint8_t *response, size_t len) {
83 CHPP_LOGD("Loopback client dispatch service response");
84 CHPP_ASSERT(len >= CHPP_LOOPBACK_HEADER_LEN);
85 CHPP_NOT_NULL(response);
86 CHPP_DEBUG_NOT_NULL(appState);
87 struct ChppLoopbackClientState *state = appState->loopbackClientContext;
88 CHPP_NOT_NULL(state);
89 CHPP_NOT_NULL(state->loopbackData);
90
91 CHPP_ASSERT(
92 chppClientTimestampResponse(&state->client, &state->runLoopbackTest,
93 (const struct ChppAppHeader *)response));
94
95 struct ChppLoopbackTestResult *result = &state->testResult;
96
97 result->error = CHPP_APP_ERROR_NONE;
98 result->responseLen = len;
99 result->firstError = len;
100 result->byteErrors = 0;
101 result->rttNs = state->runLoopbackTest.responseTimeNs -
102 state->runLoopbackTest.requestTimeNs;
103
104 if (result->requestLen != result->responseLen) {
105 result->error = CHPP_APP_ERROR_INVALID_LENGTH;
106 result->firstError = MIN(result->requestLen, result->responseLen);
107 }
108
109 for (size_t loc = CHPP_LOOPBACK_HEADER_LEN;
110 loc < MIN(result->requestLen, result->responseLen); loc++) {
111 if (state->loopbackData[loc - CHPP_LOOPBACK_HEADER_LEN] != response[loc]) {
112 result->error = CHPP_APP_ERROR_UNSPECIFIED;
113 result->firstError =
114 MIN(result->firstError, loc - CHPP_LOOPBACK_HEADER_LEN);
115 result->byteErrors++;
116 }
117 }
118
119 CHPP_LOGD("Loopback client RX err=0x%" PRIx16 " len=%" PRIuSIZE
120 " req len=%" PRIuSIZE " first err=%" PRIuSIZE
121 " total err=%" PRIuSIZE,
122 result->error, result->responseLen, result->requestLen,
123 result->firstError, result->byteErrors);
124
125 // Notify waiting (synchronous) client
126 chppMutexLock(&state->client.responseMutex);
127 state->client.responseReady = true;
128 chppConditionVariableSignal(&state->client.responseCondVar);
129 chppMutexUnlock(&state->client.responseMutex);
130
131 return true;
132 }
133
chppRunLoopbackTest(struct ChppAppState * appState,const uint8_t * buf,size_t len)134 struct ChppLoopbackTestResult chppRunLoopbackTest(struct ChppAppState *appState,
135 const uint8_t *buf,
136 size_t len) {
137 CHPP_LOGD("Loopback client TX len=%" PRIuSIZE,
138 len + CHPP_LOOPBACK_HEADER_LEN);
139
140 if (appState == NULL) {
141 CHPP_LOGE("Cannot run loopback test with null app");
142 struct ChppLoopbackTestResult result;
143 result.error = CHPP_APP_ERROR_UNSUPPORTED;
144 return result;
145 }
146
147 if (!chppWaitForDiscoveryComplete(appState, 0 /* timeoutMs */)) {
148 struct ChppLoopbackTestResult result;
149 result.error = CHPP_APP_ERROR_NOT_READY;
150 return result;
151 }
152
153 struct ChppLoopbackClientState *state = appState->loopbackClientContext;
154 CHPP_NOT_NULL(state);
155 struct ChppLoopbackTestResult *result = &state->testResult;
156
157 if (result->error == CHPP_APP_ERROR_BLOCKED) {
158 CHPP_DEBUG_ASSERT_LOG(false, "Another loopback in progress");
159 return *result;
160 }
161
162 result->error = CHPP_APP_ERROR_BLOCKED;
163 result->requestLen = len + CHPP_LOOPBACK_HEADER_LEN;
164 result->responseLen = 0;
165 result->firstError = 0;
166 result->byteErrors = 0;
167 result->rttNs = 0;
168 state->runLoopbackTest.requestTimeNs = CHPP_TIME_NONE;
169 state->runLoopbackTest.responseTimeNs = CHPP_TIME_NONE;
170
171 if (len == 0) {
172 CHPP_LOGE("Loopback payload=0!");
173 result->error = CHPP_APP_ERROR_INVALID_LENGTH;
174 return *result;
175 }
176
177 uint8_t *loopbackRequest =
178 (uint8_t *)chppAllocClientRequest(&state->client, result->requestLen);
179
180 if (loopbackRequest == NULL) {
181 result->requestLen = 0;
182 result->error = CHPP_APP_ERROR_OOM;
183 CHPP_LOG_OOM();
184 return *result;
185 }
186
187 state->loopbackData = buf;
188 memcpy(&loopbackRequest[CHPP_LOOPBACK_HEADER_LEN], buf, len);
189
190 if (!chppSendTimestampedRequestAndWaitTimeout(
191 &state->client, &state->runLoopbackTest, loopbackRequest,
192 result->requestLen, 5 * CHPP_NSEC_PER_SEC)) {
193 result->error = CHPP_APP_ERROR_UNSPECIFIED;
194 }
195
196 return *result;
197 }
198