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