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.h"
18
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23
24 #include "chpp/app.h"
25 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
26 #include "chpp/clients/discovery.h"
27 #endif
28 #ifdef CHPP_CLIENT_ENABLED_GNSS
29 #include "chpp/clients/gnss.h"
30 #endif
31 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
32 #include "chpp/clients/loopback.h"
33 #endif
34 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
35 #include "chpp/clients/timesync.h"
36 #endif
37 #ifdef CHPP_CLIENT_ENABLED_WIFI
38 #include "chpp/clients/wifi.h"
39 #endif
40 #ifdef CHPP_CLIENT_ENABLED_WWAN
41 #include "chpp/clients/wwan.h"
42 #endif
43 #include "chpp/log.h"
44 #include "chpp/macros.h"
45 #include "chpp/memory.h"
46 #include "chpp/time.h"
47 #include "chpp/transport.h"
48
49 /************************************************
50 * Prototypes
51 ***********************************************/
52
53 static bool chppIsClientApiReady(struct ChppClientState *clientState);
54 ChppClientDeinitFunction *chppGetClientDeinitFunction(
55 struct ChppAppState *context, uint8_t index);
56
57 /************************************************
58 * Private Functions
59 ***********************************************/
60
61 /**
62 * Determines whether a client is ready to accept commands via its API (i.e. is
63 * initialized and opened). If the client is in the process of reopening, it
64 * will wait for the client to reopen.
65 *
66 * @param clientState State of the client sending the client request.
67 *
68 * @return Indicates whetherthe client is ready.
69 */
chppIsClientApiReady(struct ChppClientState * clientState)70 static bool chppIsClientApiReady(struct ChppClientState *clientState) {
71 bool result = false;
72
73 if (clientState->initialized) {
74 switch (clientState->openState) {
75 case (CHPP_OPEN_STATE_CLOSED):
76 case (CHPP_OPEN_STATE_WAITING_TO_OPEN): {
77 // result remains false
78 break;
79 }
80
81 case (CHPP_OPEN_STATE_OPENED): {
82 result = true;
83 break;
84 }
85
86 case (CHPP_OPEN_STATE_OPENING): {
87 // Allow the open request to go through
88 clientState->openState = CHPP_OPEN_STATE_WAITING_TO_OPEN;
89 result = true;
90 break;
91 }
92 }
93 }
94
95 if (!result) {
96 CHPP_LOGE("Client not ready (everInit=%d, init=%d, open=%" PRIu8 ")",
97 clientState->everInitialized, clientState->initialized,
98 clientState->openState);
99 }
100 return result;
101 }
102
103 /**
104 * Returns the deinitialization function pointer of a particular negotiated
105 * client.
106 *
107 * @param context Maintains status for each app layer instance.
108 * @param index Index of the registered client.
109 *
110 * @return Pointer to the match notification function.
111 */
chppGetClientDeinitFunction(struct ChppAppState * context,uint8_t index)112 ChppClientDeinitFunction *chppGetClientDeinitFunction(
113 struct ChppAppState *context, uint8_t index) {
114 return context->registeredClients[index]->deinitFunctionPtr;
115 }
116
117 /************************************************
118 * Public Functions
119 ***********************************************/
120
chppRegisterCommonClients(struct ChppAppState * context)121 void chppRegisterCommonClients(struct ChppAppState *context) {
122 UNUSED_VAR(context);
123 CHPP_LOGD("Registering Clients");
124
125 #ifdef CHPP_CLIENT_ENABLED_WWAN
126 if (context->clientServiceSet.wwanClient) {
127 chppRegisterWwanClient(context);
128 }
129 #endif
130
131 #ifdef CHPP_CLIENT_ENABLED_WIFI
132 if (context->clientServiceSet.wifiClient) {
133 chppRegisterWifiClient(context);
134 }
135 #endif
136
137 #ifdef CHPP_CLIENT_ENABLED_GNSS
138 if (context->clientServiceSet.gnssClient) {
139 chppRegisterGnssClient(context);
140 }
141 #endif
142 }
143
chppDeregisterCommonClients(struct ChppAppState * context)144 void chppDeregisterCommonClients(struct ChppAppState *context) {
145 UNUSED_VAR(context);
146 CHPP_LOGD("Deregistering Clients");
147
148 #ifdef CHPP_CLIENT_ENABLED_WWAN
149 if (context->clientServiceSet.wwanClient) {
150 chppDeregisterWwanClient(context);
151 }
152 #endif
153
154 #ifdef CHPP_CLIENT_ENABLED_WIFI
155 if (context->clientServiceSet.wifiClient) {
156 chppDeregisterWifiClient(context);
157 }
158 #endif
159
160 #ifdef CHPP_CLIENT_ENABLED_GNSS
161 if (context->clientServiceSet.gnssClient) {
162 chppDeregisterGnssClient(context);
163 }
164 #endif
165 }
166
chppRegisterClient(struct ChppAppState * appContext,void * clientContext,struct ChppClientState * clientState,struct ChppRequestResponseState * rRStates,const struct ChppClient * newClient)167 void chppRegisterClient(struct ChppAppState *appContext, void *clientContext,
168 struct ChppClientState *clientState,
169 struct ChppRequestResponseState *rRStates,
170 const struct ChppClient *newClient) {
171 CHPP_NOT_NULL(newClient);
172
173 if (appContext->registeredClientCount >= CHPP_MAX_REGISTERED_CLIENTS) {
174 CHPP_LOGE("Max clients registered: %" PRIu8,
175 appContext->registeredClientCount);
176
177 } else {
178 clientState->appContext = appContext;
179 clientState->rRStates = rRStates;
180 clientState->index = appContext->registeredClientCount;
181
182 appContext->registeredClientContexts[appContext->registeredClientCount] =
183 clientContext;
184 appContext->registeredClientStates[appContext->registeredClientCount] =
185 clientState;
186 appContext->registeredClients[appContext->registeredClientCount] =
187 newClient;
188
189 char uuidText[CHPP_SERVICE_UUID_STRING_LEN];
190 chppUuidToStr(newClient->descriptor.uuid, uuidText);
191 CHPP_LOGD("Client # %" PRIu8 " UUID=%s, version=%" PRIu8 ".%" PRIu8
192 ".%" PRIu16 ", min_len=%" PRIuSIZE,
193 appContext->registeredClientCount, uuidText,
194 newClient->descriptor.version.major,
195 newClient->descriptor.version.minor,
196 newClient->descriptor.version.patch, newClient->minLength);
197
198 appContext->registeredClientCount++;
199 }
200 }
201
chppInitBasicClients(struct ChppAppState * context)202 void chppInitBasicClients(struct ChppAppState *context) {
203 UNUSED_VAR(context);
204 CHPP_LOGD("Initializing basic clients");
205
206 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
207 if (context->clientServiceSet.loopbackClient) {
208 chppLoopbackClientInit(context);
209 }
210 #endif
211
212 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
213 chppTimesyncClientInit(context);
214 #endif
215
216 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
217 chppDiscoveryInit(context);
218 #endif
219 }
220
chppClientInit(struct ChppClientState * clientState,uint8_t handle)221 void chppClientInit(struct ChppClientState *clientState, uint8_t handle) {
222 CHPP_ASSERT_LOG(!clientState->initialized,
223 "Client H#%" PRIu8 " already initialized", handle);
224
225 if (!clientState->everInitialized) {
226 clientState->handle = handle;
227 chppMutexInit(&clientState->responseMutex);
228 chppConditionVariableInit(&clientState->responseCondVar);
229 clientState->everInitialized = true;
230 }
231
232 clientState->initialized = true;
233 }
234
chppClientDeinit(struct ChppClientState * clientState)235 void chppClientDeinit(struct ChppClientState *clientState) {
236 CHPP_ASSERT_LOG(clientState->initialized,
237 "Client H#%" PRIu8 " already deinitialized",
238 clientState->handle);
239
240 clientState->initialized = false;
241 }
242
chppDeinitBasicClients(struct ChppAppState * context)243 void chppDeinitBasicClients(struct ChppAppState *context) {
244 UNUSED_VAR(context);
245 CHPP_LOGD("Deinitializing basic clients");
246
247 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
248 if (context->clientServiceSet.loopbackClient) {
249 chppLoopbackClientDeinit(context);
250 }
251 #endif
252
253 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
254 chppTimesyncClientDeinit(context);
255 #endif
256
257 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
258 chppDiscoveryDeinit(context);
259 #endif
260 }
261
chppDeinitMatchedClients(struct ChppAppState * context)262 void chppDeinitMatchedClients(struct ChppAppState *context) {
263 CHPP_LOGD("Deinitializing matched clients");
264
265 for (uint8_t i = 0; i < context->discoveredServiceCount; i++) {
266 uint8_t clientIndex = context->clientIndexOfServiceIndex[i];
267 if (clientIndex != CHPP_CLIENT_INDEX_NONE) {
268 // Discovered service has a matched client
269 ChppClientDeinitFunction *clientDeinitFunction =
270 chppGetClientDeinitFunction(context, clientIndex);
271
272 CHPP_LOGD("Client #%" PRIu8 " (H#%d) deinit fp found=%d", clientIndex,
273 CHPP_SERVICE_HANDLE_OF_INDEX(i),
274 (clientDeinitFunction != NULL));
275
276 if (clientDeinitFunction != NULL) {
277 clientDeinitFunction(context->registeredClientContexts[clientIndex]);
278 }
279 }
280 }
281 }
282
chppAllocClientRequest(struct ChppClientState * clientState,size_t len)283 struct ChppAppHeader *chppAllocClientRequest(
284 struct ChppClientState *clientState, size_t len) {
285 CHPP_ASSERT(len >= CHPP_APP_MIN_LEN_HEADER_WITH_TRANSACTION);
286
287 struct ChppAppHeader *result = chppMalloc(len);
288 if (result != NULL) {
289 result->handle = clientState->handle;
290 result->type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST;
291 result->transaction = clientState->transaction;
292 result->error = CHPP_APP_ERROR_NONE;
293 result->command = CHPP_APP_COMMAND_NONE;
294
295 clientState->transaction++;
296 }
297 return result;
298 }
299
chppAllocClientRequestCommand(struct ChppClientState * clientState,uint16_t command)300 struct ChppAppHeader *chppAllocClientRequestCommand(
301 struct ChppClientState *clientState, uint16_t command) {
302 struct ChppAppHeader *result =
303 chppAllocClientRequest(clientState, sizeof(struct ChppAppHeader));
304
305 if (result != NULL) {
306 result->command = command;
307 }
308 return result;
309 }
310
chppClientTimestampRequest(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,struct ChppAppHeader * requestHeader,uint64_t timeoutNs)311 void chppClientTimestampRequest(struct ChppClientState *clientState,
312 struct ChppRequestResponseState *rRState,
313 struct ChppAppHeader *requestHeader,
314 uint64_t timeoutNs) {
315 if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
316 CHPP_LOGE("Dupe req ID=%" PRIu8 " existing ID=%" PRIu8 " from t=%" PRIu64,
317 requestHeader->transaction, rRState->transaction,
318 rRState->requestTimeNs / CHPP_NSEC_PER_MSEC);
319
320 // Clear a possible pending timeout from the previous request
321 rRState->responseTimeNs = CHPP_TIME_MAX;
322 chppClientRecalculateNextTimeout(clientState->appContext);
323 }
324
325 rRState->requestTimeNs = chppGetCurrentTimeNs();
326 rRState->requestState = CHPP_REQUEST_STATE_REQUEST_SENT;
327 rRState->transaction = requestHeader->transaction;
328
329 if (timeoutNs == CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE) {
330 rRState->responseTimeNs = CHPP_TIME_MAX;
331
332 } else {
333 rRState->responseTimeNs = timeoutNs + rRState->requestTimeNs;
334
335 clientState->appContext->nextRequestTimeoutNs = MIN(
336 clientState->appContext->nextRequestTimeoutNs, rRState->responseTimeNs);
337 }
338
339 CHPP_LOGD("Timestamp req ID=%" PRIu8 " at t=%" PRIu64 " timeout=%" PRIu64
340 " (requested=%" PRIu64 "), next timeout=%" PRIu64,
341 rRState->transaction, rRState->requestTimeNs / CHPP_NSEC_PER_MSEC,
342 rRState->responseTimeNs / CHPP_NSEC_PER_MSEC,
343 timeoutNs / CHPP_NSEC_PER_MSEC,
344 clientState->appContext->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
345 }
346
chppClientTimestampResponse(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,const struct ChppAppHeader * responseHeader)347 bool chppClientTimestampResponse(struct ChppClientState *clientState,
348 struct ChppRequestResponseState *rRState,
349 const struct ChppAppHeader *responseHeader) {
350 bool success = false;
351 uint64_t responseTime = chppGetCurrentTimeNs();
352
353 switch (rRState->requestState) {
354 case CHPP_REQUEST_STATE_NONE: {
355 CHPP_LOGE("Resp with no req t=%" PRIu64,
356 responseTime / CHPP_NSEC_PER_MSEC);
357 break;
358 }
359
360 case CHPP_REQUEST_STATE_RESPONSE_RCV: {
361 CHPP_LOGE("Extra resp at t=%" PRIu64 " for req t=%" PRIu64,
362 responseTime / CHPP_NSEC_PER_MSEC,
363 rRState->requestTimeNs / CHPP_NSEC_PER_MSEC);
364 break;
365 }
366
367 case CHPP_REQUEST_STATE_RESPONSE_TIMEOUT: {
368 CHPP_LOGE("Late resp at t=%" PRIu64 " for req t=%" PRIu64,
369 responseTime / CHPP_NSEC_PER_MSEC,
370 rRState->requestTimeNs / CHPP_NSEC_PER_MSEC);
371 break;
372 }
373
374 case CHPP_REQUEST_STATE_REQUEST_SENT: {
375 if (responseHeader->transaction != rRState->transaction) {
376 CHPP_LOGE("Invalid resp ID=%" PRIu8 " at t=%" PRIu64
377 " expected=%" PRIu8,
378 responseHeader->transaction,
379 responseTime / CHPP_NSEC_PER_MSEC, rRState->transaction);
380 } else {
381 rRState->requestState = (responseTime > rRState->responseTimeNs)
382 ? CHPP_REQUEST_STATE_RESPONSE_TIMEOUT
383 : CHPP_REQUEST_STATE_RESPONSE_RCV;
384 success = true;
385
386 CHPP_LOGD(
387 "Timestamp resp ID=%" PRIu8 " req t=%" PRIu64 " resp t=%" PRIu64
388 " timeout t=%" PRIu64 " (RTT=%" PRIu64 ", timeout = %s)",
389 rRState->transaction, rRState->requestTimeNs / CHPP_NSEC_PER_MSEC,
390 responseTime / CHPP_NSEC_PER_MSEC,
391 rRState->responseTimeNs / CHPP_NSEC_PER_MSEC,
392 (responseTime - rRState->requestTimeNs) / CHPP_NSEC_PER_MSEC,
393 (responseTime > rRState->responseTimeNs) ? "yes" : "no");
394 }
395 break;
396 }
397
398 default: {
399 CHPP_DEBUG_ASSERT(false);
400 }
401 }
402
403 if (success) {
404 if (rRState->responseTimeNs ==
405 clientState->appContext->nextRequestTimeoutNs) {
406 // This was the next upcoming timeout
407 chppClientRecalculateNextTimeout(clientState->appContext);
408 }
409 rRState->responseTimeNs = responseTime;
410 }
411 return success;
412 }
413
chppSendTimestampedRequestOrFail(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,void * buf,size_t len,uint64_t timeoutNs)414 bool chppSendTimestampedRequestOrFail(struct ChppClientState *clientState,
415 struct ChppRequestResponseState *rRState,
416 void *buf, size_t len,
417 uint64_t timeoutNs) {
418 CHPP_ASSERT(len >= CHPP_APP_MIN_LEN_HEADER_WITH_TRANSACTION);
419 if (!chppIsClientApiReady(clientState)) {
420 CHPP_FREE_AND_NULLIFY(buf);
421 return false;
422 }
423
424 chppClientTimestampRequest(clientState, rRState, buf, timeoutNs);
425 clientState->responseReady = false;
426
427 bool success = chppEnqueueTxDatagramOrFail(
428 clientState->appContext->transportContext, buf, len);
429
430 // Failure to enqueue a TX datagram means that a request was known to be not
431 // transmitted. We explicitly set requestState to be in the NONE state, so
432 // that unintended app layer timeouts do not occur.
433 if (!success) {
434 rRState->requestState = CHPP_REQUEST_STATE_NONE;
435 }
436
437 return success;
438 }
439
chppSendTimestampedRequestAndWait(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,void * buf,size_t len)440 bool chppSendTimestampedRequestAndWait(struct ChppClientState *clientState,
441 struct ChppRequestResponseState *rRState,
442 void *buf, size_t len) {
443 return chppSendTimestampedRequestAndWaitTimeout(
444 clientState, rRState, buf, len, CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT);
445 }
446
chppSendTimestampedRequestAndWaitTimeout(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,void * buf,size_t len,uint64_t timeoutNs)447 bool chppSendTimestampedRequestAndWaitTimeout(
448 struct ChppClientState *clientState,
449 struct ChppRequestResponseState *rRState, void *buf, size_t len,
450 uint64_t timeoutNs) {
451 bool result = chppSendTimestampedRequestOrFail(
452 clientState, rRState, buf, len, CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE);
453
454 if (result) {
455 chppMutexLock(&clientState->responseMutex);
456
457 while (result && !clientState->responseReady) {
458 result = chppConditionVariableTimedWait(&clientState->responseCondVar,
459 &clientState->responseMutex,
460 timeoutNs);
461 }
462 if (!clientState->responseReady) {
463 rRState->requestState = CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
464 CHPP_LOGE("Response timeout after %" PRIu64 " ms",
465 timeoutNs / CHPP_NSEC_PER_MSEC);
466 result = false;
467 }
468
469 chppMutexUnlock(&clientState->responseMutex);
470 }
471
472 return result;
473 }
474
chppClientPseudoOpen(struct ChppClientState * clientState)475 void chppClientPseudoOpen(struct ChppClientState *clientState) {
476 clientState->pseudoOpen = true;
477 }
478
chppClientSendOpenRequest(struct ChppClientState * clientState,struct ChppRequestResponseState * openRRState,uint16_t openCommand,bool blocking)479 bool chppClientSendOpenRequest(struct ChppClientState *clientState,
480 struct ChppRequestResponseState *openRRState,
481 uint16_t openCommand, bool blocking) {
482 bool result = false;
483 uint8_t priorState = clientState->openState;
484
485 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
486 chppTimesyncMeasureOffset(clientState->appContext);
487 #endif
488
489 struct ChppAppHeader *request =
490 chppAllocClientRequestCommand(clientState, openCommand);
491
492 if (request == NULL) {
493 CHPP_LOG_OOM();
494
495 } else {
496 clientState->openState = CHPP_OPEN_STATE_OPENING;
497
498 if (blocking) {
499 CHPP_LOGD("Opening service - blocking");
500 result = chppSendTimestampedRequestAndWait(clientState, openRRState,
501 request, sizeof(*request));
502 } else {
503 CHPP_LOGD("Opening service - non-blocking");
504 result = chppSendTimestampedRequestOrFail(
505 clientState, openRRState, request, sizeof(*request),
506 CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE);
507 }
508
509 if (!result) {
510 CHPP_LOGE("Service open fail from state=%" PRIu8 " psudo=%d blocking=%d",
511 priorState, clientState->pseudoOpen, blocking);
512 clientState->openState = CHPP_OPEN_STATE_CLOSED;
513
514 } else if (blocking) {
515 result = (clientState->openState == CHPP_OPEN_STATE_OPENED);
516 }
517 }
518
519 return result;
520 }
521
chppClientProcessOpenResponse(struct ChppClientState * clientState,uint8_t * buf,size_t len)522 void chppClientProcessOpenResponse(struct ChppClientState *clientState,
523 uint8_t *buf, size_t len) {
524 UNUSED_VAR(len); // Necessary depending on assert macro below
525 // Assert condition already guaranteed by chppAppProcessRxDatagram() but
526 // checking again since this is a public function
527 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
528
529 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
530 if (rxHeader->error != CHPP_APP_ERROR_NONE) {
531 CHPP_LOGE("Service open failed at service");
532 clientState->openState = CHPP_OPEN_STATE_CLOSED;
533 } else {
534 CHPP_LOGI("Service open succeeded at service");
535 clientState->openState = CHPP_OPEN_STATE_OPENED;
536 }
537 }
538
chppClientRecalculateNextTimeout(struct ChppAppState * context)539 void chppClientRecalculateNextTimeout(struct ChppAppState *context) {
540 context->nextRequestTimeoutNs = CHPP_TIME_MAX;
541
542 for (uint8_t clientIdx = 0; clientIdx < context->registeredClientCount;
543 clientIdx++) {
544 for (uint16_t cmdIdx = 0;
545 cmdIdx < context->registeredClients[clientIdx]->rRStateCount;
546 cmdIdx++) {
547 struct ChppRequestResponseState *rRState =
548 &context->registeredClientStates[clientIdx]->rRStates[cmdIdx];
549
550 if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
551 context->nextRequestTimeoutNs =
552 MIN(context->nextRequestTimeoutNs, rRState->responseTimeNs);
553 }
554 }
555 }
556
557 CHPP_LOGD("nextReqTimeout=%" PRIu64,
558 context->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
559 }
560
chppClientCloseOpenRequests(struct ChppClientState * clientState,const struct ChppClient * client,bool clearOnly)561 void chppClientCloseOpenRequests(struct ChppClientState *clientState,
562 const struct ChppClient *client,
563 bool clearOnly) {
564 bool recalcNeeded = false;
565
566 for (uint16_t cmdIdx = 0; cmdIdx < client->rRStateCount; cmdIdx++) {
567 if (clientState->rRStates[cmdIdx].requestState ==
568 CHPP_REQUEST_STATE_REQUEST_SENT) {
569 recalcNeeded = true;
570
571 CHPP_LOGE("Closing open req #%" PRIu16 " clear %d", cmdIdx, clearOnly);
572
573 if (clearOnly) {
574 clientState->rRStates[cmdIdx].requestState =
575 CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
576 } else {
577 struct ChppAppHeader *response =
578 chppMalloc(sizeof(struct ChppAppHeader));
579 if (response == NULL) {
580 CHPP_LOG_OOM();
581 } else {
582 response->handle = clientState->handle;
583 response->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE;
584 response->transaction = clientState->rRStates[cmdIdx].transaction;
585 response->error = CHPP_APP_ERROR_TIMEOUT;
586 response->command = cmdIdx;
587
588 chppAppProcessRxDatagram(clientState->appContext, (uint8_t *)response,
589 sizeof(struct ChppAppHeader));
590 }
591 }
592 }
593 }
594
595 if (recalcNeeded) {
596 chppClientRecalculateNextTimeout(clientState->appContext);
597 }
598 }
599