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 * context)56 void chppLoopbackClientInit(struct ChppAppState *context) {
57 CHPP_LOGD("Loopback client init");
58
59 context->loopbackClientContext =
60 chppMalloc(sizeof(struct ChppLoopbackClientState));
61 CHPP_NOT_NULL(context->loopbackClientContext);
62 memset(context->loopbackClientContext, 0,
63 sizeof(struct ChppLoopbackClientState));
64
65 context->loopbackClientContext->client.appContext = context;
66 chppClientInit(&context->loopbackClientContext->client, CHPP_HANDLE_LOOPBACK);
67 context->loopbackClientContext->testResult.error = CHPP_APP_ERROR_NONE;
68 context->loopbackClientContext->client.openState = CHPP_OPEN_STATE_OPENED;
69 }
70
chppLoopbackClientDeinit(struct ChppAppState * context)71 void chppLoopbackClientDeinit(struct ChppAppState *context) {
72 CHPP_LOGD("Loopback client deinit");
73
74 CHPP_NOT_NULL(context);
75 CHPP_NOT_NULL(context->loopbackClientContext);
76
77 chppClientDeinit(&context->loopbackClientContext->client);
78 CHPP_FREE_AND_NULLIFY(context->loopbackClientContext);
79 }
80
chppDispatchLoopbackServiceResponse(struct ChppAppState * context,const uint8_t * response,size_t len)81 bool chppDispatchLoopbackServiceResponse(struct ChppAppState *context,
82 const uint8_t *response, size_t len) {
83 CHPP_LOGD("Loopback client dispatch service response");
84
85 CHPP_ASSERT(len >= CHPP_LOOPBACK_HEADER_LEN);
86 CHPP_NOT_NULL(response);
87 CHPP_NOT_NULL(context->loopbackClientContext);
88 CHPP_NOT_NULL(context->loopbackClientContext->loopbackData);
89
90 CHPP_ASSERT(chppClientTimestampResponse(
91 &context->loopbackClientContext->client,
92 &context->loopbackClientContext->runLoopbackTest,
93 (const struct ChppAppHeader *)response));
94
95 context->loopbackClientContext->testResult.error = CHPP_APP_ERROR_NONE;
96 context->loopbackClientContext->testResult.responseLen = len;
97 context->loopbackClientContext->testResult.firstError = len;
98 context->loopbackClientContext->testResult.byteErrors = 0;
99 context->loopbackClientContext->testResult.rttNs =
100 context->loopbackClientContext->runLoopbackTest.responseTimeNs -
101 context->loopbackClientContext->runLoopbackTest.requestTimeNs;
102
103 if (context->loopbackClientContext->testResult.requestLen !=
104 context->loopbackClientContext->testResult.responseLen) {
105 context->loopbackClientContext->testResult.error =
106 CHPP_APP_ERROR_INVALID_LENGTH;
107 context->loopbackClientContext->testResult.firstError =
108 MIN(context->loopbackClientContext->testResult.requestLen,
109 context->loopbackClientContext->testResult.responseLen);
110 }
111
112 for (size_t loc = CHPP_LOOPBACK_HEADER_LEN;
113 loc < MIN(context->loopbackClientContext->testResult.requestLen,
114 context->loopbackClientContext->testResult.responseLen);
115 loc++) {
116 if (context->loopbackClientContext
117 ->loopbackData[loc - CHPP_LOOPBACK_HEADER_LEN] != response[loc]) {
118 context->loopbackClientContext->testResult.error =
119 CHPP_APP_ERROR_UNSPECIFIED;
120 context->loopbackClientContext->testResult.firstError =
121 MIN(context->loopbackClientContext->testResult.firstError,
122 loc - CHPP_LOOPBACK_HEADER_LEN);
123 context->loopbackClientContext->testResult.byteErrors++;
124 }
125 }
126
127 CHPP_LOGI("Loopback client RX response. Error code=0x%" PRIx16
128 ", response len=%" PRIuSIZE ", request len=%" PRIuSIZE
129 ", first err=%" PRIuSIZE ", total err=%" PRIuSIZE,
130 context->loopbackClientContext->testResult.error,
131 context->loopbackClientContext->testResult.responseLen,
132 context->loopbackClientContext->testResult.requestLen,
133 context->loopbackClientContext->testResult.firstError,
134 context->loopbackClientContext->testResult.byteErrors);
135
136 // Notify waiting (synchronous) client
137 chppMutexLock(&context->loopbackClientContext->client.responseMutex);
138 context->loopbackClientContext->client.responseReady = true;
139 chppConditionVariableSignal(
140 &context->loopbackClientContext->client.responseCondVar);
141 chppMutexUnlock(&context->loopbackClientContext->client.responseMutex);
142
143 return true;
144 }
145
chppRunLoopbackTest(struct ChppAppState * context,const uint8_t * buf,size_t len)146 struct ChppLoopbackTestResult chppRunLoopbackTest(struct ChppAppState *context,
147 const uint8_t *buf,
148 size_t len) {
149 CHPP_LOGI("Loopback test. payload len=%" PRIuSIZE ", request len=%" PRIuSIZE,
150 len, len + CHPP_LOOPBACK_HEADER_LEN);
151
152 if (!chppWaitForDiscoveryComplete(context, 0 /* timeoutMs */)) {
153 static const struct ChppLoopbackTestResult result = {
154 .error = CHPP_APP_ERROR_NOT_READY,
155 };
156 return result;
157 }
158
159 CHPP_NOT_NULL(context->loopbackClientContext);
160 if (context->loopbackClientContext->testResult.error ==
161 CHPP_APP_ERROR_BLOCKED) {
162 CHPP_LOGE("Loopback test cannot be run while another is in progress");
163 CHPP_DEBUG_ASSERT(false);
164
165 } else {
166 context->loopbackClientContext->testResult.error = CHPP_APP_ERROR_BLOCKED;
167 context->loopbackClientContext->testResult.requestLen =
168 len + CHPP_LOOPBACK_HEADER_LEN;
169 context->loopbackClientContext->testResult.responseLen = 0;
170 context->loopbackClientContext->testResult.firstError = 0;
171 context->loopbackClientContext->testResult.byteErrors = 0;
172 context->loopbackClientContext->testResult.rttNs = 0;
173 context->loopbackClientContext->runLoopbackTest.requestTimeNs =
174 CHPP_TIME_NONE;
175 context->loopbackClientContext->runLoopbackTest.responseTimeNs =
176 CHPP_TIME_NONE;
177
178 if (len == 0) {
179 CHPP_LOGE("Loopback payload too short (0)");
180 context->loopbackClientContext->testResult.error =
181 CHPP_APP_ERROR_INVALID_LENGTH;
182
183 } else {
184 uint8_t *loopbackRequest = (uint8_t *)chppAllocClientRequest(
185 &context->loopbackClientContext->client,
186 context->loopbackClientContext->testResult.requestLen);
187
188 if (loopbackRequest == NULL) {
189 // OOM
190 context->loopbackClientContext->testResult.requestLen = 0;
191 context->loopbackClientContext->testResult.error = CHPP_APP_ERROR_OOM;
192 CHPP_LOG_OOM();
193
194 } else {
195 context->loopbackClientContext->loopbackData = buf;
196 memcpy(&loopbackRequest[CHPP_LOOPBACK_HEADER_LEN], buf, len);
197
198 if (!chppSendTimestampedRequestAndWaitTimeout(
199 &context->loopbackClientContext->client,
200 &context->loopbackClientContext->runLoopbackTest,
201 loopbackRequest,
202 context->loopbackClientContext->testResult.requestLen,
203 CHPP_NSEC_PER_SEC /* 1s */)) {
204 context->loopbackClientContext->testResult.error =
205 CHPP_APP_ERROR_UNSPECIFIED;
206 } // else {context->loopbackClientContext->testResult is now populated}
207 }
208 }
209 }
210
211 return context->loopbackClientContext->testResult;
212 }
213