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 }