xref: /aosp_15_r20/system/chre/chpp/app.c (revision 84e339476a462649f82315436d70fd732297a399)
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/app.h"
18 
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include "chpp/clients.h"
27 #include "chpp/clients/discovery.h"
28 #include "chpp/services.h"
29 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
30 #include "chpp/clients/loopback.h"
31 #endif
32 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
33 #include "chpp/clients/timesync.h"
34 #endif
35 #include "chpp/log.h"
36 #include "chpp/macros.h"
37 #include "chpp/notifier.h"
38 #include "chpp/pal_api.h"
39 #ifdef CHPP_CLIENT_ENABLED_VENDOR
40 #include "chpp/platform/vendor_clients.h"
41 #endif
42 #ifdef CHPP_SERVICE_ENABLED_VENDOR
43 #include "chpp/platform/vendor_services.h"
44 #endif
45 #include "chpp/services.h"
46 #include "chpp/services/discovery.h"
47 #include "chpp/services/loopback.h"
48 #include "chpp/services/nonhandle.h"
49 #include "chpp/services/timesync.h"
50 #include "chpp/time.h"
51 
52 /************************************************
53  *  Prototypes
54  ***********************************************/
55 
56 static bool chppProcessPredefinedClientRequest(struct ChppAppState *context,
57                                                uint8_t *buf, size_t len);
58 static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context,
59                                                  uint8_t *buf, size_t len);
60 
61 static bool chppDatagramLenIsOk(struct ChppAppState *context,
62                                 const struct ChppAppHeader *rxHeader,
63                                 size_t len);
64 static ChppDispatchFunction *chppGetDispatchFunction(
65     struct ChppAppState *context, uint8_t handle, enum ChppMessageType type);
66 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
67 static ChppNotifierFunction *chppGetClientResetNotifierFunction(
68     struct ChppAppState *context, uint8_t index);
69 #endif  // CHPP_CLIENT_ENABLED_DISCOVERY
70 static ChppNotifierFunction *chppGetServiceResetNotifierFunction(
71     struct ChppAppState *context, uint8_t index);
72 static inline const struct ChppService *chppServiceOfHandle(
73     struct ChppAppState *appContext, uint8_t handle);
74 static inline const struct ChppClient *chppClientOfHandle(
75     struct ChppAppState *appContext, uint8_t handle);
76 static inline struct ChppEndpointState *chppServiceStateOfHandle(
77     struct ChppAppState *appContext, uint8_t handle);
78 static inline struct ChppEndpointState *chppClientStateOfHandle(
79     struct ChppAppState *appContext, uint8_t handle);
80 static struct ChppEndpointState *chppClientOrServiceStateOfHandle(
81     struct ChppAppState *appContext, uint8_t handle, enum ChppMessageType type);
82 
83 static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context,
84                                                 uint8_t *buf, size_t len);
85 static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context,
86                                                 uint8_t *buf, size_t len);
87 
88 /************************************************
89  *  Private Functions
90  ***********************************************/
91 
92 /**
93  * Processes a client request that is determined to be for a predefined CHPP
94  * service.
95  *
96  * @param context State of the app layer.
97  * @param buf Input data. Cannot be null.
98  * @param len Length of input data in bytes.
99  *
100  * @return False if handle is invalid. True otherwise.
101  */
chppProcessPredefinedClientRequest(struct ChppAppState * context,uint8_t * buf,size_t len)102 static bool chppProcessPredefinedClientRequest(struct ChppAppState *context,
103                                                uint8_t *buf, size_t len) {
104   const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
105   bool handleValid = true;
106   bool dispatchResult = true;
107 
108   switch (rxHeader->handle) {
109     case CHPP_HANDLE_LOOPBACK: {
110       dispatchResult = chppDispatchLoopbackClientRequest(context, buf, len);
111       break;
112     }
113 
114     case CHPP_HANDLE_TIMESYNC: {
115       dispatchResult = chppDispatchTimesyncClientRequest(context, buf, len);
116       break;
117     }
118 
119     case CHPP_HANDLE_DISCOVERY: {
120       dispatchResult = chppDispatchDiscoveryClientRequest(context, buf, len);
121       break;
122     }
123 
124     default: {
125       handleValid = false;
126     }
127   }
128 
129   if (dispatchResult == false) {
130     CHPP_LOGE("H#%" PRIu8 " unknown request. cmd=%#x, ID=%" PRIu8,
131               rxHeader->handle, rxHeader->command, rxHeader->transaction);
132   }
133 
134   return handleValid;
135 }
136 
137 /**
138  * Processes a service response that is determined to be for a predefined CHPP
139  * client.
140  *
141  * @param context State of the app layer.
142  * @param buf Input data. Cannot be null.
143  * @param len Length of input data in bytes.
144  *
145  * @return False if handle is invalid. True otherwise.
146  */
chppProcessPredefinedServiceResponse(struct ChppAppState * context,uint8_t * buf,size_t len)147 static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context,
148                                                  uint8_t *buf, size_t len) {
149   CHPP_DEBUG_NOT_NULL(buf);
150   // Possibly unused if compiling without the clients below enabled
151   UNUSED_VAR(context);
152   UNUSED_VAR(len);
153 
154   const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
155   bool handleValid = true;
156   bool dispatchResult = true;
157 
158   switch (rxHeader->handle) {
159 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
160     case CHPP_HANDLE_LOOPBACK: {
161       dispatchResult = chppDispatchLoopbackServiceResponse(context, buf, len);
162       break;
163     }
164 #endif  // CHPP_CLIENT_ENABLED_LOOPBACK
165 
166 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
167     case CHPP_HANDLE_TIMESYNC: {
168       dispatchResult = chppDispatchTimesyncServiceResponse(context, buf, len);
169       break;
170     }
171 #endif  // CHPP_CLIENT_ENABLED_TIMESYNC
172 
173 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
174     case CHPP_HANDLE_DISCOVERY: {
175       dispatchResult = chppDispatchDiscoveryServiceResponse(context, buf, len);
176       break;
177     }
178 #endif  // CHPP_CLIENT_ENABLED_DISCOVERY
179 
180     default: {
181       handleValid = false;
182     }
183   }
184 
185   if (dispatchResult == false) {
186     CHPP_LOGE("H#%" PRIu8 " unknown response. cmd=%#x, ID=%" PRIu8
187               ", len=%" PRIuSIZE,
188               rxHeader->handle, rxHeader->command, rxHeader->transaction, len);
189   }
190 
191   return handleValid;
192 }
193 
194 /**
195  * Verifies if the length of a Rx Datagram from the transport layer is
196  * sufficient for the associated service/client.
197  *
198  * @param context State of the app layer.
199  * @param rxHeader The pointer to the datagram RX header.
200  * @param len Length of the datagram in bytes.
201  *
202  * @return true if length is ok.
203  */
chppDatagramLenIsOk(struct ChppAppState * context,const struct ChppAppHeader * rxHeader,size_t len)204 static bool chppDatagramLenIsOk(struct ChppAppState *context,
205                                 const struct ChppAppHeader *rxHeader,
206                                 size_t len) {
207   CHPP_DEBUG_NOT_NULL(context);
208   CHPP_DEBUG_NOT_NULL(rxHeader);
209 
210   size_t minLen = SIZE_MAX;
211   uint8_t handle = rxHeader->handle;
212 
213   if (handle < CHPP_HANDLE_NEGOTIATED_RANGE_START) {  // Predefined
214     switch (handle) {
215       case CHPP_HANDLE_NONE:
216         minLen = sizeof_member(struct ChppAppHeader, handle);
217         break;
218 
219       case CHPP_HANDLE_LOOPBACK:
220         minLen = sizeof_member(struct ChppAppHeader, handle) +
221                  sizeof_member(struct ChppAppHeader, type);
222         break;
223 
224       case CHPP_HANDLE_TIMESYNC:
225       case CHPP_HANDLE_DISCOVERY:
226         minLen = sizeof(struct ChppAppHeader);
227         break;
228 
229       default:
230         // len remains SIZE_MAX
231         CHPP_LOGE("Invalid H#%" PRIu8, handle);
232         return false;
233     }
234 
235   } else {  // Negotiated
236     enum ChppMessageType messageType =
237         CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type);
238 
239     switch (messageType) {
240       case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
241       case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE:
242       case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
243         const struct ChppService *service =
244             chppServiceOfHandle(context, handle);
245         if (service != NULL) {
246           minLen = service->minLength;
247         }
248         break;
249       }
250       case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
251       case CHPP_MESSAGE_TYPE_SERVICE_REQUEST:
252       case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
253         const struct ChppClient *client = chppClientOfHandle(context, handle);
254         if (client != NULL) {
255           minLen = client->minLength;
256         }
257         break;
258       }
259       default:
260         CHPP_LOGE("Invalid type=%d or H#%" PRIu8, messageType, handle);
261         return false;
262     }
263   }
264 
265   if (len < minLen) {
266     CHPP_LOGE("Datagram len=%" PRIuSIZE " < %" PRIuSIZE " for H#%" PRIu8, len,
267               minLen, handle);
268     return false;
269   }
270 
271   return true;
272 }
273 
274 /**
275  * Returns the dispatch function of a particular negotiated client/service
276  * handle and message type.
277  *
278  * Returns null if it is unsupported by the service.
279  *
280  * @param context State of the app layer.
281  * @param handle Handle number for the client/service.
282  * @param type Message type.
283  *
284  * @return Pointer to a function that dispatches incoming datagrams for any
285  * particular client/service.
286  */
chppGetDispatchFunction(struct ChppAppState * context,uint8_t handle,enum ChppMessageType type)287 static ChppDispatchFunction *chppGetDispatchFunction(
288     struct ChppAppState *context, uint8_t handle, enum ChppMessageType type) {
289   CHPP_DEBUG_NOT_NULL(context);
290   // chppDatagramLenIsOk() has already confirmed that the handle # is valid.
291   // Therefore, no additional checks are necessary for chppClientOfHandle(),
292   // chppServiceOfHandle(), or chppClientOrServiceStateOfHandle().
293 
294   // Make sure the client is open before it can receive any message:
295   switch (CHPP_APP_GET_MESSAGE_TYPE(type)) {
296     case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
297     case CHPP_MESSAGE_TYPE_SERVICE_REQUEST:
298     case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
299       struct ChppEndpointState *clientState =
300           chppClientStateOfHandle(context, handle);
301       if (clientState->openState == CHPP_OPEN_STATE_CLOSED) {
302         CHPP_LOGE("RX service response but client closed");
303         return NULL;
304       }
305       break;
306     }
307     default:
308       // no check needed on the service side
309       break;
310   }
311 
312   switch (CHPP_APP_GET_MESSAGE_TYPE(type)) {
313     case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
314       return chppServiceOfHandle(context, handle)->requestDispatchFunctionPtr;
315     case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
316       return chppClientOfHandle(context, handle)->responseDispatchFunctionPtr;
317     case CHPP_MESSAGE_TYPE_SERVICE_REQUEST:
318       return chppClientOfHandle(context, handle)->requestDispatchFunctionPtr;
319     case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE:
320       return chppServiceOfHandle(context, handle)->responseDispatchFunctionPtr;
321     case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION:
322       return chppServiceOfHandle(context, handle)
323           ->notificationDispatchFunctionPtr;
324     case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION:
325       return chppClientOfHandle(context, handle)
326           ->notificationDispatchFunctionPtr;
327   }
328 
329   return NULL;
330 }
331 
332 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
333 /**
334  * Returns the reset notification function pointer of a particular negotiated
335  * client.
336  *
337  * Returns null for clients that do not need or support a reset notification.
338  *
339  * @param context State of the app layer.
340  * @param index Index of the registered client.
341  *
342  * @return Pointer to the reset notification function.
343  */
chppGetClientResetNotifierFunction(struct ChppAppState * context,uint8_t index)344 static ChppNotifierFunction *chppGetClientResetNotifierFunction(
345     struct ChppAppState *context, uint8_t index) {
346   CHPP_DEBUG_NOT_NULL(context);
347   return context->registeredClients[index]->resetNotifierFunctionPtr;
348 }
349 #endif  // CHPP_CLIENT_ENABLED_DISCOVERY
350 
351 /**
352  * Returns the reset function pointer of a particular registered service.
353  *
354  * Returns null for services that do not need or support a reset notification.
355  *
356  * @param context State of the app layer.
357  * @param index Index of the registered service.
358  *
359  * @return Pointer to the reset function.
360  */
chppGetServiceResetNotifierFunction(struct ChppAppState * context,uint8_t index)361 ChppNotifierFunction *chppGetServiceResetNotifierFunction(
362     struct ChppAppState *context, uint8_t index) {
363   CHPP_DEBUG_NOT_NULL(context);
364   return context->registeredServices[index]->resetNotifierFunctionPtr;
365 }
366 
367 /**
368  * Returns a pointer to the ChppService struct of the service matched to a
369  * negotiated handle.
370  *
371  * Returns null if a service doesn't exist for the handle.
372  *
373  * @param context State of the app layer.
374  * @param handle Handle number.
375  *
376  * @return Pointer to the ChppService struct of a particular service handle.
377  */
chppServiceOfHandle(struct ChppAppState * context,uint8_t handle)378 static inline const struct ChppService *chppServiceOfHandle(
379     struct ChppAppState *context, uint8_t handle) {
380   CHPP_DEBUG_NOT_NULL(context);
381   uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
382   if (serviceIndex < context->registeredServiceCount) {
383     return context->registeredServices[serviceIndex];
384   }
385 
386   return NULL;
387 }
388 
389 /**
390  * Returns a pointer to the ChppClient struct of the client matched to a
391  * negotiated handle.
392  *
393  * Returns null if a client doesn't exist for the handle.
394  *
395  * @param context State of the app layer.
396  * @param handle Handle number.
397  *
398  * @return Pointer to the ChppClient struct matched to a particular handle.
399  */
chppClientOfHandle(struct ChppAppState * context,uint8_t handle)400 static inline const struct ChppClient *chppClientOfHandle(
401     struct ChppAppState *context, uint8_t handle) {
402   CHPP_DEBUG_NOT_NULL(context);
403   uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
404   if (serviceIndex < context->discoveredServiceCount) {
405     uint8_t clientIndex = context->clientIndexOfServiceIndex[serviceIndex];
406     if (clientIndex < context->registeredClientCount) {
407       return context->registeredClients[clientIndex];
408     }
409   }
410 
411   return NULL;
412 }
413 
414 /**
415  * Returns the service state for a given handle.
416  *
417  * The caller must pass a valid handle.
418  *
419  * @param context State of the app layer.
420  * @param handle Handle number for the service.
421  *
422  * @return Pointer to a ChppEndpointState.
423  */
chppServiceStateOfHandle(struct ChppAppState * context,uint8_t handle)424 static inline struct ChppEndpointState *chppServiceStateOfHandle(
425     struct ChppAppState *context, uint8_t handle) {
426   CHPP_DEBUG_NOT_NULL(context);
427   CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) <
428                     context->registeredServiceCount);
429 
430   const uint8_t serviceIdx = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
431   return context->registeredServiceStates[serviceIdx];
432 }
433 
434 /**
435  * Returns a pointer to the client state for a given handle.
436  *
437  * The caller must pass a valid handle.
438  *
439  * @param context State of the app layer.
440  * @param handle Handle number for the service.
441  *
442  * @return Pointer to the endpoint state.
443  */
chppClientStateOfHandle(struct ChppAppState * context,uint8_t handle)444 static inline struct ChppEndpointState *chppClientStateOfHandle(
445     struct ChppAppState *context, uint8_t handle) {
446   CHPP_DEBUG_NOT_NULL(context);
447   CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) <
448                     context->registeredClientCount);
449   const uint8_t serviceIdx = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
450   const uint8_t clientIdx = context->clientIndexOfServiceIndex[serviceIdx];
451   return context->registeredClientStates[clientIdx]->context;
452 }
453 
454 /**
455  * Returns a pointer to the client or service state for a given handle.
456  *
457  * The caller must pass a valid handle.
458  *
459  * @param appContext State of the app layer.
460  * @param handle Handle number for the service.
461  * @param type Message type (indicates if this is for a client or service).
462  *
463  * @return Pointer to the endpoint state (NULL if wrong type).
464  */
chppClientOrServiceStateOfHandle(struct ChppAppState * appContext,uint8_t handle,enum ChppMessageType type)465 static struct ChppEndpointState *chppClientOrServiceStateOfHandle(
466     struct ChppAppState *appContext, uint8_t handle,
467     enum ChppMessageType type) {
468   switch (CHPP_APP_GET_MESSAGE_TYPE(type)) {
469     case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
470     case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE:
471     case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION:
472       return chppServiceStateOfHandle(appContext, handle);
473     case CHPP_MESSAGE_TYPE_SERVICE_REQUEST:
474     case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
475     case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION:
476       return chppClientStateOfHandle(appContext, handle);
477     default:
478       CHPP_LOGE("Unknown type=0x%" PRIx8 " (H#%" PRIu8 ")", type, handle);
479       return NULL;
480   }
481 }
482 
483 /**
484  * Processes a received datagram that is determined to be for a predefined CHPP
485  * service. Responds with an error if unsuccessful.
486  *
487  * Predefined requests are only sent by the client side.
488  * Predefined responses are only sent by the service side.
489  *
490  * @param context State of the app layer.
491  * @param buf Input data. Cannot be null.
492  * @param len Length of input data in bytes.
493  */
chppProcessPredefinedHandleDatagram(struct ChppAppState * context,uint8_t * buf,size_t len)494 static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context,
495                                                 uint8_t *buf, size_t len) {
496   CHPP_DEBUG_NOT_NULL(context);
497   CHPP_DEBUG_NOT_NULL(buf);
498 
499   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
500   bool success = false;
501 
502   switch (CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type)) {
503     case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: {
504       success = chppProcessPredefinedClientRequest(context, buf, len);
505       break;
506     }
507     case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: {
508       success = chppProcessPredefinedServiceResponse(context, buf, len);
509       break;
510     }
511     default:
512       // Predefined client/services do not use
513       // - notifications,
514       // - service requests / client responses
515       break;
516   }
517 
518   if (!success) {
519     CHPP_LOGE("H#%" PRIu8 " undefined msg type=0x%" PRIx8 " (len=%" PRIuSIZE
520               ", ID=%" PRIu8 ")",
521               rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
522     chppEnqueueTxErrorDatagram(context->transportContext,
523                                CHPP_TRANSPORT_ERROR_APPLAYER);
524   }
525 }
526 
527 /**
528  * Processes a received datagram that is determined to be for a negotiated CHPP
529  * client or service.
530  *
531  * The datagram is processed by the dispatch function matching the datagram
532  * type. @see ChppService and ChppClient.
533  *
534  * If a request dispatch function returns an error (anything different from
535  * CHPP_APP_ERROR_NONE) then an error response is automatically sent back to the
536  * remote endpoint.
537  *
538  * @param appContext State of the app layer.
539  * @param buf Input data. Cannot be null.
540  * @param len Length of input data in bytes.
541  */
chppProcessNegotiatedHandleDatagram(struct ChppAppState * appContext,uint8_t * buf,size_t len)542 static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *appContext,
543                                                 uint8_t *buf, size_t len) {
544   CHPP_DEBUG_NOT_NULL(appContext);
545   CHPP_DEBUG_NOT_NULL(buf);
546 
547   const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
548   enum ChppMessageType messageType = CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type);
549 
550   // Could be either the client or the service state depending on the message
551   // type.
552   struct ChppEndpointState *endpointState = chppClientOrServiceStateOfHandle(
553       appContext, rxHeader->handle, messageType);
554   if (endpointState == NULL) {
555     CHPP_LOGE("H#%" PRIu8 " missing ctx (msg=0x%" PRIx8 " len=%" PRIuSIZE
556               ", ID=%" PRIu8 ")",
557               rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
558     chppEnqueueTxErrorDatagram(appContext->transportContext,
559                                CHPP_TRANSPORT_ERROR_APPLAYER);
560     CHPP_DEBUG_ASSERT(false);
561     return;
562   }
563 
564   ChppDispatchFunction *dispatchFunc =
565       chppGetDispatchFunction(appContext, rxHeader->handle, messageType);
566   if (dispatchFunc == NULL) {
567     CHPP_LOGE("H#%" PRIu8 " unsupported msg=0x%" PRIx8 " (len=%" PRIuSIZE
568               ", ID=%" PRIu8 ")",
569               rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
570     chppEnqueueTxErrorDatagram(appContext->transportContext,
571                                CHPP_TRANSPORT_ERROR_APPLAYER);
572     return;
573   }
574 
575   // All good. Dispatch datagram and possibly notify a waiting client
576   enum ChppAppErrorCode error = dispatchFunc(endpointState->context, buf, len);
577 
578   if (error != CHPP_APP_ERROR_NONE) {
579     CHPP_LOGE("RX dispatch err=0x%" PRIx16 " H#%" PRIu8 " type=0x%" PRIx8
580               " ID=%" PRIu8 " cmd=0x%" PRIx16 " len=%" PRIuSIZE,
581               error, rxHeader->handle, rxHeader->type, rxHeader->transaction,
582               rxHeader->command, len);
583 
584     // Requests require a dispatch failure response.
585     if (messageType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
586         messageType == CHPP_MESSAGE_TYPE_SERVICE_REQUEST) {
587       struct ChppAppHeader *response =
588           chppAllocResponseFixed(rxHeader, struct ChppAppHeader);
589       if (response != NULL) {
590         response->error = (uint8_t)error;
591         chppEnqueueTxDatagramOrFail(appContext->transportContext, response,
592                                     sizeof(*response));
593       }
594     }
595     return;
596   }
597 
598   // Datagram is a response.
599   // Check for synchronous operation and notify waiting endpoint if needed.
600   if (messageType == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE ||
601       messageType == CHPP_MESSAGE_TYPE_CLIENT_RESPONSE) {
602     struct ChppSyncResponse *syncResponse = &endpointState->syncResponse;
603     chppMutexLock(&syncResponse->mutex);
604     syncResponse->ready = true;
605     CHPP_LOGD("Finished dispatching a response -> synchronous notification");
606     chppConditionVariableSignal(&syncResponse->condVar);
607     chppMutexUnlock(&syncResponse->mutex);
608   }
609 }
610 
611 /************************************************
612  *  Public Functions
613  ***********************************************/
614 
chppAppInit(struct ChppAppState * appContext,struct ChppTransportState * transportContext)615 void chppAppInit(struct ChppAppState *appContext,
616                  struct ChppTransportState *transportContext) {
617   // Default initialize all service/clients
618   struct ChppClientServiceSet set;
619   memset(&set, 0xff, sizeof(set));  // set all bits to 1
620 
621   chppAppInitWithClientServiceSet(appContext, transportContext, set);
622 }
623 
chppAppInitWithClientServiceSet(struct ChppAppState * appContext,struct ChppTransportState * transportContext,struct ChppClientServiceSet clientServiceSet)624 void chppAppInitWithClientServiceSet(
625     struct ChppAppState *appContext,
626     struct ChppTransportState *transportContext,
627     struct ChppClientServiceSet clientServiceSet) {
628   CHPP_NOT_NULL(appContext);
629   CHPP_DEBUG_NOT_NULL(transportContext);
630 
631   CHPP_LOGD("App Init");
632 
633   memset(appContext, 0, sizeof(*appContext));
634 
635   appContext->clientServiceSet = clientServiceSet;
636   appContext->transportContext = transportContext;
637   appContext->nextClientRequestTimeoutNs = CHPP_TIME_MAX;
638   appContext->nextServiceRequestTimeoutNs = CHPP_TIME_MAX;
639 
640   chppPalSystemApiInit(appContext);
641 
642 #ifdef CHPP_SERVICE_ENABLED
643   chppRegisterCommonServices(appContext);
644 #ifdef CHPP_SERVICE_ENABLED_VENDOR
645   chppRegisterVendorServices(appContext);
646 #endif
647 #endif
648 
649 #ifdef CHPP_CLIENT_ENABLED
650   chppRegisterCommonClients(appContext);
651 #ifdef CHPP_CLIENT_ENABLED_VENDOR
652   chppRegisterVendorClients(appContext);
653 #endif
654   chppInitBasicClients(appContext);
655 #endif
656 }
657 
chppAppDeinit(struct ChppAppState * appContext)658 void chppAppDeinit(struct ChppAppState *appContext) {
659   CHPP_LOGD("App deinit");
660 
661 #ifdef CHPP_CLIENT_ENABLED
662   chppDeinitMatchedClients(appContext);
663   chppDeinitBasicClients(appContext);
664   chppDeregisterCommonClients(appContext);
665 #ifdef CHPP_CLIENT_ENABLED_VENDOR
666   chppDeregisterVendorClients(appContext);
667 #endif
668 #endif
669 
670 #ifdef CHPP_SERVICE_ENABLED
671   chppDeregisterCommonServices(appContext);
672 #ifdef CHPP_SERVICE_ENABLED_VENDOR
673   chppDeregisterVendorServices(appContext);
674 #endif
675 #endif
676 
677   chppPalSystemApiDeinit(appContext);
678 }
679 
chppAppProcessRxDatagram(struct ChppAppState * context,uint8_t * buf,size_t len)680 void chppAppProcessRxDatagram(struct ChppAppState *context, uint8_t *buf,
681                               size_t len) {
682   CHPP_DEBUG_NOT_NULL(context);
683   CHPP_DEBUG_NOT_NULL(buf);
684 
685   const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
686 
687   if (len == 0) {
688     CHPP_DEBUG_ASSERT_LOG(false, "App rx w/ len 0");
689 
690   } else if (len < sizeof(struct ChppAppHeader)) {
691     uint8_t *handle = (uint8_t *)buf;
692     CHPP_LOGD("RX datagram len=%" PRIuSIZE " H#%" PRIu8, len, *handle);
693 
694   } else if (rxHeader->error != CHPP_APP_ERROR_NONE) {
695     CHPP_LOGE("RX datagram len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8
696               " ID=%" PRIu8 " ERR=%" PRIu8 " cmd=0x%" PRIx16,
697               len, rxHeader->handle, rxHeader->type, rxHeader->transaction,
698               rxHeader->error, rxHeader->command);
699   } else {
700     CHPP_LOGD("RX datagram len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8
701               " ID=%" PRIu8 " err=%" PRIu8 " cmd=0x%" PRIx16,
702               len, rxHeader->handle, rxHeader->type, rxHeader->transaction,
703               rxHeader->error, rxHeader->command);
704   }
705 
706   if (!chppDatagramLenIsOk(context, rxHeader, len)) {
707     chppEnqueueTxErrorDatagram(context->transportContext,
708                                CHPP_TRANSPORT_ERROR_APPLAYER);
709 
710   } else {
711     if (rxHeader->handle == CHPP_HANDLE_NONE) {
712       chppDispatchNonHandle(context, buf, len);
713 
714     } else if (rxHeader->handle < CHPP_HANDLE_NEGOTIATED_RANGE_START) {
715       chppProcessPredefinedHandleDatagram(context, buf, len);
716 
717     } else {
718       chppProcessNegotiatedHandleDatagram(context, buf, len);
719     }
720   }
721 
722   chppDatagramProcessDoneCb(context->transportContext, buf);
723 }
724 
chppAppProcessReset(struct ChppAppState * context)725 void chppAppProcessReset(struct ChppAppState *context) {
726   CHPP_DEBUG_NOT_NULL(context);
727 
728 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
729   if (!context->isDiscoveryComplete) {
730     chppInitiateDiscovery(context);
731 
732   } else {
733     // Notify matched clients that a reset happened
734     for (uint8_t i = 0; i < context->discoveredServiceCount; i++) {
735       uint8_t clientIndex = context->clientIndexOfServiceIndex[i];
736       if (clientIndex != CHPP_CLIENT_INDEX_NONE) {
737         // Discovered service has a matched client
738         ChppNotifierFunction *ResetNotifierFunction =
739             chppGetClientResetNotifierFunction(context, clientIndex);
740 
741         CHPP_LOGD("Client #%" PRIu8 " (H#%d) reset notifier found=%d",
742                   clientIndex, CHPP_SERVICE_HANDLE_OF_INDEX(i),
743                   (ResetNotifierFunction != NULL));
744 
745         if (ResetNotifierFunction != NULL) {
746           ResetNotifierFunction(
747               context->registeredClientStates[clientIndex]->context);
748         }
749       }
750     }
751   }
752 #endif  // CHPP_CLIENT_ENABLED_DISCOVERY
753 
754   // Notify registered services that a reset happened
755   for (uint8_t i = 0; i < context->registeredServiceCount; i++) {
756     ChppNotifierFunction *ResetNotifierFunction =
757         chppGetServiceResetNotifierFunction(context, i);
758 
759     CHPP_LOGD("Service #%" PRIu8 " (H#%d) reset notifier found=%d", i,
760               CHPP_SERVICE_HANDLE_OF_INDEX(i), (ResetNotifierFunction != NULL));
761 
762     if (ResetNotifierFunction != NULL) {
763       ResetNotifierFunction(context->registeredServiceStates[i]->context);
764     }
765   }
766 
767 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
768   // Reinitialize time offset
769   chppTimesyncClientReset(context);
770 #endif
771 }
772 
chppUuidToStr(const uint8_t uuid[CHPP_SERVICE_UUID_LEN],char strOut[CHPP_SERVICE_UUID_STRING_LEN])773 void chppUuidToStr(const uint8_t uuid[CHPP_SERVICE_UUID_LEN],
774                    char strOut[CHPP_SERVICE_UUID_STRING_LEN]) {
775   snprintf(
776       strOut, CHPP_SERVICE_UUID_STRING_LEN,
777       "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
778       uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
779       uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
780       uuid[15]);
781 }
782 
chppAppErrorToChreError(uint8_t chppError)783 uint8_t chppAppErrorToChreError(uint8_t chppError) {
784   switch (chppError) {
785     case CHPP_APP_ERROR_NONE:
786     case CHPP_APP_ERROR_INVALID_ARG:
787     case CHPP_APP_ERROR_BUSY:
788     case CHPP_APP_ERROR_OOM:
789     case CHPP_APP_ERROR_UNSUPPORTED:
790     case CHPP_APP_ERROR_TIMEOUT:
791     case CHPP_APP_ERROR_DISABLED:
792     case CHPP_APP_ERROR_RATELIMITED: {
793       // CHRE and CHPP error values are identical in these cases
794       return chppError;
795     }
796     default: {
797       return CHRE_ERROR;
798     }
799   }
800 }
801 
chppAppShortResponseErrorHandler(uint8_t * buf,size_t len,const char * responseName)802 uint8_t chppAppShortResponseErrorHandler(uint8_t *buf, size_t len,
803                                          const char *responseName) {
804   CHPP_DEBUG_NOT_NULL(buf);
805   CHPP_DEBUG_NOT_NULL(responseName);
806 
807   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
808   const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
809 
810   if (rxHeader->error == CHPP_APP_ERROR_NONE) {
811     CHPP_LOGE("%s resp short len=%" PRIuSIZE, responseName, len);
812     return CHRE_ERROR;
813   }
814 
815   CHPP_LOGD("%s resp short len=%" PRIuSIZE, responseName, len);
816   return chppAppErrorToChreError(rxHeader->error);
817 }
818 
chppAllocNotification(uint8_t type,size_t len)819 struct ChppAppHeader *chppAllocNotification(uint8_t type, size_t len) {
820   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
821   CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION ||
822               type == CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION);
823 
824   struct ChppAppHeader *notification = chppMalloc(len);
825   if (notification != NULL) {
826     notification->type = type;
827     notification->handle = CHPP_HANDLE_NONE;
828     notification->transaction = 0;
829     notification->error = CHPP_APP_ERROR_NONE;
830     notification->command = CHPP_APP_COMMAND_NONE;
831   } else {
832     CHPP_LOG_OOM();
833   }
834   return notification;
835 }
836 
chppAllocRequest(uint8_t type,struct ChppEndpointState * endpointState,size_t len)837 struct ChppAppHeader *chppAllocRequest(uint8_t type,
838                                        struct ChppEndpointState *endpointState,
839                                        size_t len) {
840   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
841   CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
842               type == CHPP_MESSAGE_TYPE_SERVICE_REQUEST);
843   CHPP_DEBUG_NOT_NULL(endpointState);
844 
845   struct ChppAppHeader *request = chppMalloc(len);
846   if (request != NULL) {
847     request->handle = endpointState->handle;
848     request->type = type;
849     request->transaction = endpointState->transaction;
850     request->error = CHPP_APP_ERROR_NONE;
851     request->command = CHPP_APP_COMMAND_NONE;
852 
853     endpointState->transaction++;
854   } else {
855     CHPP_LOG_OOM();
856   }
857   return request;
858 }
859 
chppAllocResponse(const struct ChppAppHeader * requestHeader,size_t len)860 struct ChppAppHeader *chppAllocResponse(
861     const struct ChppAppHeader *requestHeader, size_t len) {
862   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
863   CHPP_DEBUG_NOT_NULL(requestHeader);
864   uint8_t type = requestHeader->type;
865   CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
866               type == CHPP_MESSAGE_TYPE_SERVICE_REQUEST);
867 
868   struct ChppAppHeader *response = chppMalloc(len);
869   if (response != NULL) {
870     *response = *requestHeader;
871     response->type = type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST
872                          ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE
873                          : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE;
874     response->error = CHPP_APP_ERROR_NONE;
875   } else {
876     CHPP_LOG_OOM();
877   }
878   return response;
879 }
880 
chppTimestampIncomingRequest(struct ChppIncomingRequestState * inReqState,const struct ChppAppHeader * requestHeader)881 void chppTimestampIncomingRequest(struct ChppIncomingRequestState *inReqState,
882                                   const struct ChppAppHeader *requestHeader) {
883   CHPP_DEBUG_NOT_NULL(inReqState);
884   CHPP_DEBUG_NOT_NULL(requestHeader);
885   if (inReqState->responseTimeNs == CHPP_TIME_NONE &&
886       inReqState->requestTimeNs != CHPP_TIME_NONE) {
887     CHPP_LOGE("RX dupe req t=%" PRIu64,
888               inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
889   }
890   inReqState->requestTimeNs = chppGetCurrentTimeNs();
891   inReqState->responseTimeNs = CHPP_TIME_NONE;
892   inReqState->transaction = requestHeader->transaction;
893 }
894 
chppTimestampOutgoingRequest(struct ChppAppState * appState,struct ChppOutgoingRequestState * outReqState,const struct ChppAppHeader * requestHeader,uint64_t timeoutNs)895 void chppTimestampOutgoingRequest(struct ChppAppState *appState,
896                                   struct ChppOutgoingRequestState *outReqState,
897                                   const struct ChppAppHeader *requestHeader,
898                                   uint64_t timeoutNs) {
899   CHPP_DEBUG_NOT_NULL(appState);
900   CHPP_DEBUG_NOT_NULL(outReqState);
901   CHPP_DEBUG_NOT_NULL(requestHeader);
902   enum ChppMessageType msgType = requestHeader->type;
903   enum ChppEndpointType endpointType =
904       msgType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ? CHPP_ENDPOINT_CLIENT
905                                                   : CHPP_ENDPOINT_SERVICE;
906 
907   CHPP_ASSERT(msgType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
908               msgType == CHPP_MESSAGE_TYPE_SERVICE_REQUEST);
909 
910   // Hold the mutex to avoid concurrent read of a partially modified outReqState
911   // structure by the RX thread
912   chppMutexLock(&appState->transportContext->mutex);
913 
914   if (outReqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
915     CHPP_LOGE("Dupe req ID=%" PRIu8 " existing ID=%" PRIu8 " from t=%" PRIu64,
916               requestHeader->transaction, outReqState->transaction,
917               outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
918 
919     // Clear a possible pending timeout from the previous request
920     outReqState->responseTimeNs = CHPP_TIME_MAX;
921     chppRecalculateNextTimeout(appState, endpointType);
922   }
923 
924   outReqState->requestTimeNs = chppGetCurrentTimeNs();
925   outReqState->requestState = CHPP_REQUEST_STATE_REQUEST_SENT;
926   outReqState->transaction = requestHeader->transaction;
927 
928   uint64_t *nextRequestTimeoutNs =
929       getNextRequestTimeoutNs(appState, endpointType);
930 
931   if (timeoutNs == CHPP_REQUEST_TIMEOUT_INFINITE) {
932     outReqState->responseTimeNs = CHPP_TIME_MAX;
933 
934   } else {
935     outReqState->responseTimeNs = timeoutNs + outReqState->requestTimeNs;
936 
937     *nextRequestTimeoutNs =
938         MIN(*nextRequestTimeoutNs, outReqState->responseTimeNs);
939   }
940 
941   chppMutexUnlock(&appState->transportContext->mutex);
942 
943   CHPP_LOGD("Timestamp req ID=%" PRIu8 " at t=%" PRIu64 " timeout=%" PRIu64
944             " (requested=%" PRIu64 "), next timeout=%" PRIu64,
945             outReqState->transaction,
946             outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC,
947             outReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
948             timeoutNs / CHPP_NSEC_PER_MSEC,
949             *nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
950 }
951 
chppTimestampIncomingResponse(struct ChppAppState * appState,struct ChppOutgoingRequestState * outReqState,const struct ChppAppHeader * responseHeader)952 bool chppTimestampIncomingResponse(struct ChppAppState *appState,
953                                    struct ChppOutgoingRequestState *outReqState,
954                                    const struct ChppAppHeader *responseHeader) {
955   CHPP_DEBUG_NOT_NULL(appState);
956   CHPP_DEBUG_NOT_NULL(outReqState);
957   CHPP_DEBUG_NOT_NULL(responseHeader);
958 
959   uint8_t type = responseHeader->type;
960 
961   CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_RESPONSE ||
962               type == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE);
963 
964   bool success = false;
965   uint64_t responseTime = chppGetCurrentTimeNs();
966 
967   switch (outReqState->requestState) {
968     case CHPP_REQUEST_STATE_NONE: {
969       CHPP_LOGE("Resp with no req t=%" PRIu64,
970                 responseTime / CHPP_NSEC_PER_MSEC);
971       break;
972     }
973 
974     case CHPP_REQUEST_STATE_RESPONSE_RCV: {
975       CHPP_LOGE("Extra resp at t=%" PRIu64 " for req t=%" PRIu64,
976                 responseTime / CHPP_NSEC_PER_MSEC,
977                 outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
978       break;
979     }
980 
981     case CHPP_REQUEST_STATE_RESPONSE_TIMEOUT: {
982       CHPP_LOGE("Late resp at t=%" PRIu64 " for req t=%" PRIu64,
983                 responseTime / CHPP_NSEC_PER_MSEC,
984                 outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
985       break;
986     }
987 
988     case CHPP_REQUEST_STATE_REQUEST_SENT: {
989       if (responseHeader->transaction != outReqState->transaction) {
990         CHPP_LOGE("Invalid resp ID=%" PRIu8 " at t=%" PRIu64
991                   " expected=%" PRIu8,
992                   responseHeader->transaction,
993                   responseTime / CHPP_NSEC_PER_MSEC, outReqState->transaction);
994       } else {
995         outReqState->requestState = (responseTime > outReqState->responseTimeNs)
996                                         ? CHPP_REQUEST_STATE_RESPONSE_TIMEOUT
997                                         : CHPP_REQUEST_STATE_RESPONSE_RCV;
998         success = true;
999 
1000         CHPP_LOGD(
1001             "Timestamp resp ID=%" PRIu8 " req t=%" PRIu64 " resp t=%" PRIu64
1002             " timeout t=%" PRIu64 " (RTT=%" PRIu64 ", timeout = %s)",
1003             outReqState->transaction,
1004             outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC,
1005             responseTime / CHPP_NSEC_PER_MSEC,
1006             outReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
1007             (responseTime - outReqState->requestTimeNs) / CHPP_NSEC_PER_MSEC,
1008             (responseTime > outReqState->responseTimeNs) ? "yes" : "no");
1009       }
1010       break;
1011     }
1012 
1013     default: {
1014       CHPP_DEBUG_ASSERT_LOG(false, "Invalid req state");
1015     }
1016   }
1017 
1018   if (success) {
1019     // When the received request is the next one that was expected
1020     // to timeout we need to recompute the timeout considering the
1021     // other pending requests.
1022     enum ChppEndpointType endpointType =
1023         type == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE ? CHPP_ENDPOINT_CLIENT
1024                                                    : CHPP_ENDPOINT_SERVICE;
1025     if (outReqState->responseTimeNs ==
1026         *getNextRequestTimeoutNs(appState, endpointType)) {
1027       chppRecalculateNextTimeout(appState, endpointType);
1028     }
1029     outReqState->responseTimeNs = responseTime;
1030   }
1031   return success;
1032 }
1033 
chppTimestampOutgoingResponse(struct ChppIncomingRequestState * inReqState)1034 uint64_t chppTimestampOutgoingResponse(
1035     struct ChppIncomingRequestState *inReqState) {
1036   CHPP_DEBUG_NOT_NULL(inReqState);
1037 
1038   uint64_t previousResponseTime = inReqState->responseTimeNs;
1039   inReqState->responseTimeNs = chppGetCurrentTimeNs();
1040   return previousResponseTime;
1041 }
1042 
chppSendTimestampedResponseOrFail(struct ChppAppState * appState,struct ChppIncomingRequestState * inReqState,void * buf,size_t len)1043 bool chppSendTimestampedResponseOrFail(
1044     struct ChppAppState *appState, struct ChppIncomingRequestState *inReqState,
1045     void *buf, size_t len) {
1046   CHPP_DEBUG_NOT_NULL(appState);
1047   CHPP_DEBUG_NOT_NULL(inReqState);
1048   CHPP_DEBUG_NOT_NULL(buf);
1049   uint64_t previousResponseTime = chppTimestampOutgoingResponse(inReqState);
1050 
1051   if (inReqState->requestTimeNs == CHPP_TIME_NONE) {
1052     CHPP_LOGE("TX response w/ no req t=%" PRIu64,
1053               inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC);
1054 
1055   } else if (previousResponseTime != CHPP_TIME_NONE) {
1056     CHPP_LOGW("TX additional response t=%" PRIu64 " for req t=%" PRIu64,
1057               inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
1058               inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
1059 
1060   } else {
1061     CHPP_LOGD("Sending initial response at t=%" PRIu64
1062               " for request at t=%" PRIu64 " (RTT=%" PRIu64 ")",
1063               inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
1064               inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC,
1065               (inReqState->responseTimeNs - inReqState->requestTimeNs) /
1066                   CHPP_NSEC_PER_MSEC);
1067   }
1068 
1069   return chppEnqueueTxDatagramOrFail(appState->transportContext, buf, len);
1070 }
1071 
chppSendTimestampedRequestOrFail(struct ChppEndpointState * endpointState,struct ChppOutgoingRequestState * outReqState,void * buf,size_t len,uint64_t timeoutNs)1072 bool chppSendTimestampedRequestOrFail(
1073     struct ChppEndpointState *endpointState,
1074     struct ChppOutgoingRequestState *outReqState, void *buf, size_t len,
1075     uint64_t timeoutNs) {
1076   CHPP_DEBUG_NOT_NULL(outReqState);
1077   CHPP_DEBUG_NOT_NULL(buf);
1078   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
1079 
1080   if (timeoutNs < CHPP_TRANSPORT_TX_TIMEOUT_NS) {
1081     // The app layer sits above the transport layer.
1082     // Request timeout (app layer) should be longer than the transport timeout.
1083     CHPP_LOGW("Request timeout (%" PRIu64
1084               "ns) should be longer than the transport timeout (%" PRIu64 "ns)",
1085               timeoutNs, (uint64_t)CHPP_TRANSPORT_TX_TIMEOUT_NS);
1086   }
1087 
1088   chppTimestampOutgoingRequest(endpointState->appContext, outReqState, buf,
1089                                timeoutNs);
1090   endpointState->syncResponse.ready = false;
1091 
1092   bool success = chppEnqueueTxDatagramOrFail(
1093       endpointState->appContext->transportContext, buf, len);
1094 
1095   // Failure to enqueue a TX datagram means that a request was known to be not
1096   // transmitted. We explicitly set requestState to be in the NONE state, so
1097   // that unintended app layer timeouts do not occur.
1098   if (!success) {
1099     outReqState->requestState = CHPP_REQUEST_STATE_NONE;
1100   }
1101 
1102   return success;
1103 }
1104 
chppWaitForResponseWithTimeout(struct ChppSyncResponse * syncResponse,struct ChppOutgoingRequestState * outReqState,uint64_t timeoutNs)1105 bool chppWaitForResponseWithTimeout(
1106     struct ChppSyncResponse *syncResponse,
1107     struct ChppOutgoingRequestState *outReqState, uint64_t timeoutNs) {
1108   CHPP_DEBUG_NOT_NULL(syncResponse);
1109   CHPP_DEBUG_NOT_NULL(outReqState);
1110 
1111   bool result = true;
1112 
1113   chppMutexLock(&syncResponse->mutex);
1114 
1115   while (result && !syncResponse->ready) {
1116     result = chppConditionVariableTimedWait(&syncResponse->condVar,
1117                                             &syncResponse->mutex, timeoutNs);
1118   }
1119   if (!syncResponse->ready) {
1120     outReqState->requestState = CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
1121     CHPP_LOGE("Response timeout after %" PRIu64 " ms",
1122               timeoutNs / CHPP_NSEC_PER_MSEC);
1123     result = false;
1124   }
1125 
1126   chppMutexUnlock(&syncResponse->mutex);
1127 
1128   return result;
1129 }
1130 
getRegisteredEndpointState(struct ChppAppState * appState,uint8_t index,enum ChppEndpointType type)1131 struct ChppEndpointState *getRegisteredEndpointState(
1132     struct ChppAppState *appState, uint8_t index, enum ChppEndpointType type) {
1133   CHPP_DEBUG_NOT_NULL(appState);
1134   CHPP_DEBUG_ASSERT(index < getRegisteredEndpointCount(appState, type));
1135 
1136   return type == CHPP_ENDPOINT_CLIENT
1137              ? appState->registeredClientStates[index]
1138              : appState->registeredServiceStates[index];
1139 }
1140 
getRegisteredEndpointOutReqCount(struct ChppAppState * appState,uint8_t index,enum ChppEndpointType type)1141 uint16_t getRegisteredEndpointOutReqCount(struct ChppAppState *appState,
1142                                           uint8_t index,
1143                                           enum ChppEndpointType type) {
1144   CHPP_DEBUG_NOT_NULL(appState);
1145   CHPP_DEBUG_ASSERT(index < getRegisteredEndpointCount(appState, type));
1146 
1147   return type == CHPP_ENDPOINT_CLIENT
1148              ? appState->registeredClients[index]->outReqCount
1149              : appState->registeredServices[index]->outReqCount;
1150 }
1151 
getRegisteredEndpointCount(struct ChppAppState * appState,enum ChppEndpointType type)1152 uint8_t getRegisteredEndpointCount(struct ChppAppState *appState,
1153                                    enum ChppEndpointType type) {
1154   return type == CHPP_ENDPOINT_CLIENT ? appState->registeredClientCount
1155                                       : appState->registeredServiceCount;
1156 }
1157 
chppRecalculateNextTimeout(struct ChppAppState * appState,enum ChppEndpointType type)1158 void chppRecalculateNextTimeout(struct ChppAppState *appState,
1159                                 enum ChppEndpointType type) {
1160   CHPP_DEBUG_NOT_NULL(appState);
1161 
1162   uint64_t timeoutNs = CHPP_TIME_MAX;
1163 
1164   const uint8_t endpointCount = getRegisteredEndpointCount(appState, type);
1165 
1166   for (uint8_t endpointIdx = 0; endpointIdx < endpointCount; endpointIdx++) {
1167     uint16_t reqCount =
1168         getRegisteredEndpointOutReqCount(appState, endpointIdx, type);
1169     struct ChppEndpointState *endpointState =
1170         getRegisteredEndpointState(appState, endpointIdx, type);
1171     struct ChppOutgoingRequestState *reqStates = endpointState->outReqStates;
1172     for (uint16_t cmdIdx = 0; cmdIdx < reqCount; cmdIdx++) {
1173       struct ChppOutgoingRequestState *reqState = &reqStates[cmdIdx];
1174 
1175       if (reqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
1176         timeoutNs = MIN(timeoutNs, reqState->responseTimeNs);
1177       }
1178     }
1179   }
1180 
1181   CHPP_LOGD("nextReqTimeout=%" PRIu64, timeoutNs / CHPP_NSEC_PER_MSEC);
1182 
1183   if (type == CHPP_ENDPOINT_CLIENT) {
1184     appState->nextClientRequestTimeoutNs = timeoutNs;
1185   } else {
1186     appState->nextServiceRequestTimeoutNs = timeoutNs;
1187   }
1188 }
1189 
getNextRequestTimeoutNs(struct ChppAppState * appState,enum ChppEndpointType type)1190 uint64_t *getNextRequestTimeoutNs(struct ChppAppState *appState,
1191                                   enum ChppEndpointType type) {
1192   return type == CHPP_ENDPOINT_CLIENT ? &appState->nextClientRequestTimeoutNs
1193                                       : &appState->nextServiceRequestTimeoutNs;
1194 }
1195 
chppCloseOpenRequests(struct ChppEndpointState * endpointState,enum ChppEndpointType type,bool clearOnly)1196 void chppCloseOpenRequests(struct ChppEndpointState *endpointState,
1197                            enum ChppEndpointType type, bool clearOnly) {
1198   CHPP_DEBUG_NOT_NULL(endpointState);
1199 
1200   bool recalcNeeded = false;
1201 
1202   struct ChppAppState *appState = endpointState->appContext;
1203   const uint8_t enpointIdx = endpointState->index;
1204   const uint16_t cmdCount =
1205       getRegisteredEndpointOutReqCount(appState, enpointIdx, type);
1206 
1207   for (uint16_t cmdIdx = 0; cmdIdx < cmdCount; cmdIdx++) {
1208     if (endpointState->outReqStates[cmdIdx].requestState ==
1209         CHPP_REQUEST_STATE_REQUEST_SENT) {
1210       recalcNeeded = true;
1211 
1212       CHPP_LOGE("Closing open req #%" PRIu16 " clear %d", cmdIdx, clearOnly);
1213 
1214       if (clearOnly) {
1215         endpointState->outReqStates[cmdIdx].requestState =
1216             CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
1217       } else {
1218         struct ChppAppHeader *response =
1219             chppMalloc(sizeof(struct ChppAppHeader));
1220         if (response == NULL) {
1221           CHPP_LOG_OOM();
1222         } else {
1223           // Simulate receiving a timeout response.
1224           response->handle = endpointState->handle;
1225           response->type = type == CHPP_ENDPOINT_CLIENT
1226                                ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE
1227                                : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE;
1228           response->transaction =
1229               endpointState->outReqStates[cmdIdx].transaction;
1230           response->error = CHPP_APP_ERROR_TIMEOUT;
1231           response->command = cmdIdx;
1232 
1233           chppAppProcessRxDatagram(appState, (uint8_t *)response,
1234                                    sizeof(struct ChppAppHeader));
1235         }
1236       }
1237     }
1238   }
1239   if (recalcNeeded) {
1240     chppRecalculateNextTimeout(appState, type);
1241   }
1242 }