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 return;
177 }
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] = newClient;
187
188 char uuidText[CHPP_SERVICE_UUID_STRING_LEN];
189 chppUuidToStr(newClient->descriptor.uuid, uuidText);
190 CHPP_LOGD("Client # %" PRIu8 " UUID=%s, version=%" PRIu8 ".%" PRIu8
191 ".%" PRIu16 ", min_len=%" PRIuSIZE,
192 appContext->registeredClientCount, uuidText,
193 newClient->descriptor.version.major,
194 newClient->descriptor.version.minor,
195 newClient->descriptor.version.patch, newClient->minLength);
196
197 appContext->registeredClientCount++;
198 }
199
chppInitBasicClients(struct ChppAppState * context)200 void chppInitBasicClients(struct ChppAppState *context) {
201 UNUSED_VAR(context);
202 CHPP_LOGD("Initializing basic clients");
203
204 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
205 if (context->clientServiceSet.loopbackClient) {
206 chppLoopbackClientInit(context);
207 }
208 #endif
209
210 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
211 chppTimesyncClientInit(context);
212 #endif
213
214 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
215 chppDiscoveryInit(context);
216 #endif
217 }
218
chppClientInit(struct ChppClientState * clientState,uint8_t handle)219 void chppClientInit(struct ChppClientState *clientState, uint8_t handle) {
220 CHPP_ASSERT_LOG(!clientState->initialized,
221 "Client H#%" PRIu8 " already initialized", handle);
222
223 if (!clientState->everInitialized) {
224 clientState->handle = handle;
225 chppMutexInit(&clientState->responseMutex);
226 chppConditionVariableInit(&clientState->responseCondVar);
227 clientState->everInitialized = true;
228 }
229
230 clientState->initialized = true;
231 }
232
chppClientDeinit(struct ChppClientState * clientState)233 void chppClientDeinit(struct ChppClientState *clientState) {
234 CHPP_ASSERT_LOG(clientState->initialized,
235 "Client H#%" PRIu8 " already deinitialized",
236 clientState->handle);
237
238 clientState->initialized = false;
239 }
240
chppDeinitBasicClients(struct ChppAppState * context)241 void chppDeinitBasicClients(struct ChppAppState *context) {
242 UNUSED_VAR(context);
243 CHPP_LOGD("Deinitializing basic clients");
244
245 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
246 if (context->clientServiceSet.loopbackClient) {
247 chppLoopbackClientDeinit(context);
248 }
249 #endif
250
251 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
252 chppTimesyncClientDeinit(context);
253 #endif
254
255 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
256 chppDiscoveryDeinit(context);
257 #endif
258 }
259
chppDeinitMatchedClients(struct ChppAppState * context)260 void chppDeinitMatchedClients(struct ChppAppState *context) {
261 CHPP_LOGD("Deinitializing matched clients");
262
263 for (uint8_t i = 0; i < context->discoveredServiceCount; i++) {
264 uint8_t clientIndex = context->clientIndexOfServiceIndex[i];
265 if (clientIndex != CHPP_CLIENT_INDEX_NONE) {
266 // Discovered service has a matched client
267 ChppClientDeinitFunction *clientDeinitFunction =
268 chppGetClientDeinitFunction(context, clientIndex);
269
270 CHPP_LOGD("Client #%" PRIu8 " (H#%d) deinit fp found=%d", clientIndex,
271 CHPP_SERVICE_HANDLE_OF_INDEX(i),
272 (clientDeinitFunction != NULL));
273
274 if (clientDeinitFunction != NULL) {
275 clientDeinitFunction(context->registeredClientContexts[clientIndex]);
276 }
277 }
278 }
279 }
280
chppAllocClientRequest(struct ChppClientState * clientState,size_t len)281 struct ChppAppHeader *chppAllocClientRequest(
282 struct ChppClientState *clientState, size_t len) {
283 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
284
285 struct ChppAppHeader *result = chppMalloc(len);
286 if (result != NULL) {
287 result->handle = clientState->handle;
288 result->type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST;
289 result->transaction = clientState->transaction;
290 result->error = CHPP_APP_ERROR_NONE;
291 result->command = CHPP_APP_COMMAND_NONE;
292
293 clientState->transaction++;
294 }
295 return result;
296 }
297
chppAllocClientRequestCommand(struct ChppClientState * clientState,uint16_t command)298 struct ChppAppHeader *chppAllocClientRequestCommand(
299 struct ChppClientState *clientState, uint16_t command) {
300 struct ChppAppHeader *result =
301 chppAllocClientRequest(clientState, sizeof(struct ChppAppHeader));
302
303 if (result != NULL) {
304 result->command = command;
305 }
306 return result;
307 }
308
chppClientTimestampRequest(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,struct ChppAppHeader * requestHeader,uint64_t timeoutNs)309 void chppClientTimestampRequest(struct ChppClientState *clientState,
310 struct ChppRequestResponseState *rRState,
311 struct ChppAppHeader *requestHeader,
312 uint64_t timeoutNs) {
313 if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
314 CHPP_LOGE("Dupe req ID=%" PRIu8 " existing ID=%" PRIu8 " from t=%" PRIu64,
315 requestHeader->transaction, rRState->transaction,
316 rRState->requestTimeNs / CHPP_NSEC_PER_MSEC);
317
318 // Clear a possible pending timeout from the previous request
319 rRState->responseTimeNs = CHPP_TIME_MAX;
320 chppClientRecalculateNextTimeout(clientState->appContext);
321 }
322
323 rRState->requestTimeNs = chppGetCurrentTimeNs();
324 rRState->requestState = CHPP_REQUEST_STATE_REQUEST_SENT;
325 rRState->transaction = requestHeader->transaction;
326
327 if (timeoutNs == CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE) {
328 rRState->responseTimeNs = CHPP_TIME_MAX;
329
330 } else {
331 rRState->responseTimeNs = timeoutNs + rRState->requestTimeNs;
332
333 clientState->appContext->nextRequestTimeoutNs = MIN(
334 clientState->appContext->nextRequestTimeoutNs, rRState->responseTimeNs);
335 }
336
337 CHPP_LOGD("Timestamp req ID=%" PRIu8 " at t=%" PRIu64 " timeout=%" PRIu64
338 " (requested=%" PRIu64 "), next timeout=%" PRIu64,
339 rRState->transaction, rRState->requestTimeNs / CHPP_NSEC_PER_MSEC,
340 rRState->responseTimeNs / CHPP_NSEC_PER_MSEC,
341 timeoutNs / CHPP_NSEC_PER_MSEC,
342 clientState->appContext->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
343 }
344
chppClientTimestampResponse(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,const struct ChppAppHeader * responseHeader)345 bool chppClientTimestampResponse(struct ChppClientState *clientState,
346 struct ChppRequestResponseState *rRState,
347 const struct ChppAppHeader *responseHeader) {
348 bool success = false;
349 uint64_t responseTime = chppGetCurrentTimeNs();
350
351 switch (rRState->requestState) {
352 case CHPP_REQUEST_STATE_NONE: {
353 CHPP_LOGE("Resp with no req t=%" PRIu64,
354 responseTime / CHPP_NSEC_PER_MSEC);
355 break;
356 }
357
358 case CHPP_REQUEST_STATE_RESPONSE_RCV: {
359 CHPP_LOGE("Extra resp at t=%" PRIu64 " for req t=%" PRIu64,
360 responseTime / CHPP_NSEC_PER_MSEC,
361 rRState->requestTimeNs / CHPP_NSEC_PER_MSEC);
362 break;
363 }
364
365 case CHPP_REQUEST_STATE_RESPONSE_TIMEOUT: {
366 CHPP_LOGE("Late resp at t=%" PRIu64 " for req t=%" PRIu64,
367 responseTime / CHPP_NSEC_PER_MSEC,
368 rRState->requestTimeNs / CHPP_NSEC_PER_MSEC);
369 break;
370 }
371
372 case CHPP_REQUEST_STATE_REQUEST_SENT: {
373 if (responseHeader->transaction != rRState->transaction) {
374 CHPP_LOGE("Invalid resp ID=%" PRIu8 " at t=%" PRIu64
375 " expected=%" PRIu8,
376 responseHeader->transaction,
377 responseTime / CHPP_NSEC_PER_MSEC, rRState->transaction);
378 } else {
379 rRState->requestState = (responseTime > rRState->responseTimeNs)
380 ? CHPP_REQUEST_STATE_RESPONSE_TIMEOUT
381 : CHPP_REQUEST_STATE_RESPONSE_RCV;
382 success = true;
383
384 CHPP_LOGD(
385 "Timestamp resp ID=%" PRIu8 " req t=%" PRIu64 " resp t=%" PRIu64
386 " timeout t=%" PRIu64 " (RTT=%" PRIu64 ", timeout = %s)",
387 rRState->transaction, rRState->requestTimeNs / CHPP_NSEC_PER_MSEC,
388 responseTime / CHPP_NSEC_PER_MSEC,
389 rRState->responseTimeNs / CHPP_NSEC_PER_MSEC,
390 (responseTime - rRState->requestTimeNs) / CHPP_NSEC_PER_MSEC,
391 (responseTime > rRState->responseTimeNs) ? "yes" : "no");
392 }
393 break;
394 }
395
396 default: {
397 CHPP_DEBUG_ASSERT_LOG(false, "Invalid req state");
398 }
399 }
400
401 if (success) {
402 if (rRState->responseTimeNs ==
403 clientState->appContext->nextRequestTimeoutNs) {
404 // This was the next upcoming timeout
405 chppClientRecalculateNextTimeout(clientState->appContext);
406 }
407 rRState->responseTimeNs = responseTime;
408 }
409 return success;
410 }
411
chppSendTimestampedRequestOrFail(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,void * buf,size_t len,uint64_t timeoutNs)412 bool chppSendTimestampedRequestOrFail(struct ChppClientState *clientState,
413 struct ChppRequestResponseState *rRState,
414 void *buf, size_t len,
415 uint64_t timeoutNs) {
416 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
417 if (!chppIsClientApiReady(clientState)) {
418 CHPP_FREE_AND_NULLIFY(buf);
419 return false;
420 }
421
422 chppClientTimestampRequest(clientState, rRState, buf, timeoutNs);
423 clientState->responseReady = false;
424
425 bool success = chppEnqueueTxDatagramOrFail(
426 clientState->appContext->transportContext, buf, len);
427
428 // Failure to enqueue a TX datagram means that a request was known to be not
429 // transmitted. We explicitly set requestState to be in the NONE state, so
430 // that unintended app layer timeouts do not occur.
431 if (!success) {
432 rRState->requestState = CHPP_REQUEST_STATE_NONE;
433 }
434
435 return success;
436 }
437
chppSendTimestampedRequestAndWait(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,void * buf,size_t len)438 bool chppSendTimestampedRequestAndWait(struct ChppClientState *clientState,
439 struct ChppRequestResponseState *rRState,
440 void *buf, size_t len) {
441 return chppSendTimestampedRequestAndWaitTimeout(
442 clientState, rRState, buf, len, CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT);
443 }
444
chppSendTimestampedRequestAndWaitTimeout(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,void * buf,size_t len,uint64_t timeoutNs)445 bool chppSendTimestampedRequestAndWaitTimeout(
446 struct ChppClientState *clientState,
447 struct ChppRequestResponseState *rRState, void *buf, size_t len,
448 uint64_t timeoutNs) {
449 bool result = chppSendTimestampedRequestOrFail(
450 clientState, rRState, buf, len, CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE);
451
452 if (result) {
453 chppMutexLock(&clientState->responseMutex);
454
455 while (result && !clientState->responseReady) {
456 result = chppConditionVariableTimedWait(&clientState->responseCondVar,
457 &clientState->responseMutex,
458 timeoutNs);
459 }
460 if (!clientState->responseReady) {
461 rRState->requestState = CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
462 CHPP_LOGE("Response timeout after %" PRIu64 " ms",
463 timeoutNs / CHPP_NSEC_PER_MSEC);
464 result = false;
465 }
466
467 chppMutexUnlock(&clientState->responseMutex);
468 }
469
470 return result;
471 }
472
chppClientPseudoOpen(struct ChppClientState * clientState)473 void chppClientPseudoOpen(struct ChppClientState *clientState) {
474 clientState->pseudoOpen = true;
475 }
476
chppClientSendOpenRequest(struct ChppClientState * clientState,struct ChppRequestResponseState * openRRState,uint16_t openCommand,bool blocking)477 bool chppClientSendOpenRequest(struct ChppClientState *clientState,
478 struct ChppRequestResponseState *openRRState,
479 uint16_t openCommand, bool blocking) {
480 bool result = false;
481 uint8_t priorState = clientState->openState;
482
483 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
484 chppTimesyncMeasureOffset(clientState->appContext);
485 #endif
486
487 struct ChppAppHeader *request =
488 chppAllocClientRequestCommand(clientState, openCommand);
489
490 if (request == NULL) {
491 CHPP_LOG_OOM();
492 return false;
493 }
494
495 clientState->openState = CHPP_OPEN_STATE_OPENING;
496
497 if (blocking) {
498 CHPP_LOGD("Opening service - blocking");
499 result = chppSendTimestampedRequestAndWait(clientState, openRRState,
500 request, sizeof(*request));
501 } else {
502 CHPP_LOGD("Opening service - non-blocking");
503 result = chppSendTimestampedRequestOrFail(
504 clientState, openRRState, request, sizeof(*request),
505 CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE);
506 }
507
508 if (!result) {
509 CHPP_LOGE("Service open fail from state=%" PRIu8 " psudo=%d blocking=%d",
510 priorState, clientState->pseudoOpen, blocking);
511 clientState->openState = CHPP_OPEN_STATE_CLOSED;
512
513 } else if (blocking) {
514 result = (clientState->openState == CHPP_OPEN_STATE_OPENED);
515 }
516
517 return result;
518 }
519
chppClientProcessOpenResponse(struct ChppClientState * clientState,uint8_t * buf,size_t len)520 void chppClientProcessOpenResponse(struct ChppClientState *clientState,
521 uint8_t *buf, size_t len) {
522 UNUSED_VAR(len); // Necessary depending on assert macro below
523 // Assert condition already guaranteed by chppAppProcessRxDatagram() but
524 // checking again since this is a public function
525 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
526
527 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
528 if (rxHeader->error != CHPP_APP_ERROR_NONE) {
529 CHPP_LOGE("Service open failed at service");
530 clientState->openState = CHPP_OPEN_STATE_CLOSED;
531 } else {
532 CHPP_LOGD("Service open succeeded at service");
533 clientState->openState = CHPP_OPEN_STATE_OPENED;
534 }
535 }
536
chppClientRecalculateNextTimeout(struct ChppAppState * context)537 void chppClientRecalculateNextTimeout(struct ChppAppState *context) {
538 context->nextRequestTimeoutNs = CHPP_TIME_MAX;
539
540 for (uint8_t clientIdx = 0; clientIdx < context->registeredClientCount;
541 clientIdx++) {
542 const struct ChppClient *client = context->registeredClients[clientIdx];
543 for (uint16_t cmdIdx = 0; cmdIdx < client->rRStateCount; cmdIdx++) {
544 const struct ChppClientState *state =
545 context->registeredClientStates[clientIdx];
546 struct ChppRequestResponseState *rRState = &state->rRStates[cmdIdx];
547
548 if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
549 context->nextRequestTimeoutNs =
550 MIN(context->nextRequestTimeoutNs, rRState->responseTimeNs);
551 }
552 }
553 }
554
555 CHPP_LOGD("nextReqTimeout=%" PRIu64,
556 context->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
557 }
558
chppClientCloseOpenRequests(struct ChppClientState * clientState,const struct ChppClient * client,bool clearOnly)559 void chppClientCloseOpenRequests(struct ChppClientState *clientState,
560 const struct ChppClient *client,
561 bool clearOnly) {
562 bool recalcNeeded = false;
563
564 for (uint16_t cmdIdx = 0; cmdIdx < client->rRStateCount; cmdIdx++) {
565 if (clientState->rRStates[cmdIdx].requestState ==
566 CHPP_REQUEST_STATE_REQUEST_SENT) {
567 recalcNeeded = true;
568
569 CHPP_LOGE("Closing open req #%" PRIu16 " clear %d", cmdIdx, clearOnly);
570
571 if (clearOnly) {
572 clientState->rRStates[cmdIdx].requestState =
573 CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
574 } else {
575 struct ChppAppHeader *response =
576 chppMalloc(sizeof(struct ChppAppHeader));
577 if (response == NULL) {
578 CHPP_LOG_OOM();
579 } else {
580 response->handle = clientState->handle;
581 response->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE;
582 response->transaction = clientState->rRStates[cmdIdx].transaction;
583 response->error = CHPP_APP_ERROR_TIMEOUT;
584 response->command = cmdIdx;
585
586 chppAppProcessRxDatagram(clientState->appContext, (uint8_t *)response,
587 sizeof(struct ChppAppHeader));
588 }
589 }
590 }
591 }
592
593 if (recalcNeeded) {
594 chppClientRecalculateNextTimeout(clientState->appContext);
595 }
596 }
597