• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }