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