• 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 * 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 err=0x%" PRIx16 " len=%" PRIuSIZE
128             " req len=%" PRIuSIZE " first err=%" PRIuSIZE
129             " 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 client TX len=%" PRIuSIZE,
150             len + CHPP_LOOPBACK_HEADER_LEN);
151 
152   struct ChppLoopbackTestResult result;
153   if (context == NULL) {
154     CHPP_LOGE("Cannot run loopback test with null app");
155     result.error = CHPP_APP_ERROR_UNSUPPORTED;
156   } else if (!chppWaitForDiscoveryComplete(context, 0 /* timeoutMs */)) {
157     result.error = CHPP_APP_ERROR_NOT_READY;
158   } else {
159     CHPP_NOT_NULL(context->loopbackClientContext);
160     if (context->loopbackClientContext->testResult.error ==
161         CHPP_APP_ERROR_BLOCKED) {
162       CHPP_DEBUG_ASSERT_LOG(false, "Another loopback in progress");
163 
164     } else {
165       context->loopbackClientContext->testResult.error = CHPP_APP_ERROR_BLOCKED;
166       context->loopbackClientContext->testResult.requestLen =
167           len + CHPP_LOOPBACK_HEADER_LEN;
168       context->loopbackClientContext->testResult.responseLen = 0;
169       context->loopbackClientContext->testResult.firstError = 0;
170       context->loopbackClientContext->testResult.byteErrors = 0;
171       context->loopbackClientContext->testResult.rttNs = 0;
172       context->loopbackClientContext->runLoopbackTest.requestTimeNs =
173           CHPP_TIME_NONE;
174       context->loopbackClientContext->runLoopbackTest.responseTimeNs =
175           CHPP_TIME_NONE;
176 
177       if (len == 0) {
178         CHPP_LOGE("Loopback payload=0!");
179         context->loopbackClientContext->testResult.error =
180             CHPP_APP_ERROR_INVALID_LENGTH;
181 
182       } else {
183         uint8_t *loopbackRequest = (uint8_t *)chppAllocClientRequest(
184             &context->loopbackClientContext->client,
185             context->loopbackClientContext->testResult.requestLen);
186 
187         if (loopbackRequest == NULL) {
188           // OOM
189           context->loopbackClientContext->testResult.requestLen = 0;
190           context->loopbackClientContext->testResult.error = CHPP_APP_ERROR_OOM;
191           CHPP_LOG_OOM();
192 
193         } else {
194           context->loopbackClientContext->loopbackData = buf;
195           memcpy(&loopbackRequest[CHPP_LOOPBACK_HEADER_LEN], buf, len);
196 
197           if (!chppSendTimestampedRequestAndWaitTimeout(
198                   &context->loopbackClientContext->client,
199                   &context->loopbackClientContext->runLoopbackTest,
200                   loopbackRequest,
201                   context->loopbackClientContext->testResult.requestLen,
202                   CHPP_NSEC_PER_SEC /* 1s */)) {
203             context->loopbackClientContext->testResult.error =
204                 CHPP_APP_ERROR_UNSPECIFIED;
205           }  // else {context->loopbackClientContext->testResult is now
206              // populated}
207         }
208       }
209     }
210 
211     result = context->loopbackClientContext->testResult;
212   }
213 
214   return result;
215 }
216