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