• 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/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