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
chppAppProcessTimeout(struct ChppAppState * context,uint64_t currentTimeNs)725 void chppAppProcessTimeout(struct ChppAppState *context,
726 uint64_t currentTimeNs) {
727 CHPP_DEBUG_NOT_NULL(context);
728 for (uint8_t i = 0; i < context->registeredClientCount; i++) {
729 const struct ChppClient *client = context->registeredClients[i];
730 struct ChppEndpointState *endpointState =
731 context->registeredClientStates[i];
732 if ((currentTimeNs >= endpointState->nextTimerTimeoutNs) &&
733 client->timeoutFunctionPtr != NULL) {
734 client->timeoutFunctionPtr(endpointState->context);
735 endpointState->nextTimerTimeoutNs = CHPP_TIME_MAX;
736 }
737 }
738 for (uint8_t i = 0; i < context->registeredServiceCount; i++) {
739 const struct ChppService *service = context->registeredServices[i];
740 struct ChppEndpointState *endpointState =
741 context->registeredServiceStates[i];
742 if ((currentTimeNs >= endpointState->nextTimerTimeoutNs) &&
743 service->timeoutFunctionPtr != NULL) {
744 service->timeoutFunctionPtr(endpointState->context);
745 endpointState->nextTimerTimeoutNs = CHPP_TIME_MAX;
746 }
747 }
748 }
749
chppAppProcessReset(struct ChppAppState * context)750 void chppAppProcessReset(struct ChppAppState *context) {
751 CHPP_DEBUG_NOT_NULL(context);
752
753 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
754 if (!context->isDiscoveryComplete) {
755 chppInitiateDiscovery(context);
756
757 } else {
758 // Notify matched clients that a reset happened
759 for (uint8_t i = 0; i < context->discoveredServiceCount; i++) {
760 uint8_t clientIndex = context->clientIndexOfServiceIndex[i];
761 if (clientIndex != CHPP_CLIENT_INDEX_NONE) {
762 // Discovered service has a matched client
763 ChppNotifierFunction *ResetNotifierFunction =
764 chppGetClientResetNotifierFunction(context, clientIndex);
765
766 CHPP_LOGD("Client #%" PRIu8 " (H#%d) reset notifier found=%d",
767 clientIndex, CHPP_SERVICE_HANDLE_OF_INDEX(i),
768 (ResetNotifierFunction != NULL));
769
770 if (ResetNotifierFunction != NULL) {
771 ResetNotifierFunction(
772 context->registeredClientStates[clientIndex]->context);
773 }
774 }
775 }
776 }
777 #endif // CHPP_CLIENT_ENABLED_DISCOVERY
778
779 // Notify registered services that a reset happened
780 for (uint8_t i = 0; i < context->registeredServiceCount; i++) {
781 ChppNotifierFunction *ResetNotifierFunction =
782 chppGetServiceResetNotifierFunction(context, i);
783
784 CHPP_LOGD("Service #%" PRIu8 " (H#%d) reset notifier found=%d", i,
785 CHPP_SERVICE_HANDLE_OF_INDEX(i), (ResetNotifierFunction != NULL));
786
787 if (ResetNotifierFunction != NULL) {
788 ResetNotifierFunction(context->registeredServiceStates[i]->context);
789 }
790 }
791
792 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
793 // Reinitialize time offset
794 chppTimesyncClientReset(context);
795 #endif
796 }
797
chppUuidToStr(const uint8_t uuid[CHPP_SERVICE_UUID_LEN],char strOut[CHPP_SERVICE_UUID_STRING_LEN])798 void chppUuidToStr(const uint8_t uuid[CHPP_SERVICE_UUID_LEN],
799 char strOut[CHPP_SERVICE_UUID_STRING_LEN]) {
800 snprintf(
801 strOut, CHPP_SERVICE_UUID_STRING_LEN,
802 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
803 uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
804 uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
805 uuid[15]);
806 }
807
chppAppErrorToChreError(uint8_t chppError)808 uint8_t chppAppErrorToChreError(uint8_t chppError) {
809 switch (chppError) {
810 case CHPP_APP_ERROR_NONE:
811 case CHPP_APP_ERROR_INVALID_ARG:
812 case CHPP_APP_ERROR_BUSY:
813 case CHPP_APP_ERROR_OOM:
814 case CHPP_APP_ERROR_UNSUPPORTED:
815 case CHPP_APP_ERROR_TIMEOUT:
816 case CHPP_APP_ERROR_DISABLED:
817 case CHPP_APP_ERROR_RATELIMITED: {
818 // CHRE and CHPP error values are identical in these cases
819 return chppError;
820 }
821 default: {
822 return CHRE_ERROR;
823 }
824 }
825 }
826
chppAppShortResponseErrorHandler(uint8_t * buf,size_t len,const char * responseName)827 uint8_t chppAppShortResponseErrorHandler(uint8_t *buf, size_t len,
828 const char *responseName) {
829 CHPP_DEBUG_NOT_NULL(buf);
830 CHPP_DEBUG_NOT_NULL(responseName);
831
832 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
833 const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
834
835 if (rxHeader->error == CHPP_APP_ERROR_NONE) {
836 CHPP_LOGE("%s resp short len=%" PRIuSIZE, responseName, len);
837 return CHRE_ERROR;
838 }
839
840 CHPP_LOGD("%s resp short len=%" PRIuSIZE, responseName, len);
841 return chppAppErrorToChreError(rxHeader->error);
842 }
843
chppAllocNotification(uint8_t type,size_t len)844 struct ChppAppHeader *chppAllocNotification(uint8_t type, size_t len) {
845 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
846 CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION ||
847 type == CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION);
848
849 struct ChppAppHeader *notification = chppMalloc(len);
850 if (notification != NULL) {
851 notification->type = type;
852 notification->handle = CHPP_HANDLE_NONE;
853 notification->transaction = 0;
854 notification->error = CHPP_APP_ERROR_NONE;
855 notification->command = CHPP_APP_COMMAND_NONE;
856 } else {
857 CHPP_LOG_OOM();
858 }
859 return notification;
860 }
861
chppAllocRequest(uint8_t type,struct ChppEndpointState * endpointState,size_t len)862 struct ChppAppHeader *chppAllocRequest(uint8_t type,
863 struct ChppEndpointState *endpointState,
864 size_t len) {
865 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
866 CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
867 type == CHPP_MESSAGE_TYPE_SERVICE_REQUEST);
868 CHPP_DEBUG_NOT_NULL(endpointState);
869
870 struct ChppAppHeader *request = chppMalloc(len);
871 if (request != NULL) {
872 request->handle = endpointState->handle;
873 request->type = type;
874 request->transaction = endpointState->transaction;
875 request->error = CHPP_APP_ERROR_NONE;
876 request->command = CHPP_APP_COMMAND_NONE;
877
878 endpointState->transaction++;
879 } else {
880 CHPP_LOG_OOM();
881 }
882 return request;
883 }
884
chppAllocResponse(const struct ChppAppHeader * requestHeader,size_t len)885 struct ChppAppHeader *chppAllocResponse(
886 const struct ChppAppHeader *requestHeader, size_t len) {
887 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
888 CHPP_DEBUG_NOT_NULL(requestHeader);
889 uint8_t type = requestHeader->type;
890 CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
891 type == CHPP_MESSAGE_TYPE_SERVICE_REQUEST);
892
893 struct ChppAppHeader *response = chppMalloc(len);
894 if (response != NULL) {
895 *response = *requestHeader;
896 response->type = type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST
897 ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE
898 : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE;
899 response->error = CHPP_APP_ERROR_NONE;
900 } else {
901 CHPP_LOG_OOM();
902 }
903 return response;
904 }
905
chppTimestampIncomingRequest(struct ChppIncomingRequestState * inReqState,const struct ChppAppHeader * requestHeader)906 void chppTimestampIncomingRequest(struct ChppIncomingRequestState *inReqState,
907 const struct ChppAppHeader *requestHeader) {
908 CHPP_DEBUG_NOT_NULL(inReqState);
909 CHPP_DEBUG_NOT_NULL(requestHeader);
910 if (inReqState->responseTimeNs == CHPP_TIME_NONE &&
911 inReqState->requestTimeNs != CHPP_TIME_NONE) {
912 CHPP_LOGE("RX dupe req t=%" PRIu64,
913 inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
914 }
915 inReqState->requestTimeNs = chppGetCurrentTimeNs();
916 inReqState->responseTimeNs = CHPP_TIME_NONE;
917 inReqState->transaction = requestHeader->transaction;
918 }
919
chppTimestampOutgoingRequest(struct ChppAppState * appState,struct ChppOutgoingRequestState * outReqState,const struct ChppAppHeader * requestHeader,uint64_t timeoutNs)920 void chppTimestampOutgoingRequest(struct ChppAppState *appState,
921 struct ChppOutgoingRequestState *outReqState,
922 const struct ChppAppHeader *requestHeader,
923 uint64_t timeoutNs) {
924 CHPP_DEBUG_NOT_NULL(appState);
925 CHPP_DEBUG_NOT_NULL(outReqState);
926 CHPP_DEBUG_NOT_NULL(requestHeader);
927 enum ChppMessageType msgType = requestHeader->type;
928 enum ChppEndpointType endpointType =
929 msgType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ? CHPP_ENDPOINT_CLIENT
930 : CHPP_ENDPOINT_SERVICE;
931
932 CHPP_ASSERT(msgType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
933 msgType == CHPP_MESSAGE_TYPE_SERVICE_REQUEST);
934
935 // Hold the mutex to avoid concurrent read of a partially modified outReqState
936 // structure by the RX thread
937 chppMutexLock(&appState->transportContext->mutex);
938
939 if (outReqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
940 CHPP_LOGE("Dupe req ID=%" PRIu8 " existing ID=%" PRIu8 " from t=%" PRIu64,
941 requestHeader->transaction, outReqState->transaction,
942 outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
943
944 // Clear a possible pending timeout from the previous request
945 outReqState->responseTimeNs = CHPP_TIME_MAX;
946 chppRecalculateNextTimeout(appState, endpointType);
947 }
948
949 outReqState->requestTimeNs = chppGetCurrentTimeNs();
950 outReqState->requestState = CHPP_REQUEST_STATE_REQUEST_SENT;
951 outReqState->transaction = requestHeader->transaction;
952
953 uint64_t *nextRequestTimeoutNs =
954 getNextRequestTimeoutNs(appState, endpointType);
955
956 if (timeoutNs == CHPP_REQUEST_TIMEOUT_INFINITE) {
957 outReqState->responseTimeNs = CHPP_TIME_MAX;
958
959 } else {
960 outReqState->responseTimeNs = timeoutNs + outReqState->requestTimeNs;
961
962 *nextRequestTimeoutNs =
963 MIN(*nextRequestTimeoutNs, outReqState->responseTimeNs);
964 }
965
966 chppMutexUnlock(&appState->transportContext->mutex);
967
968 CHPP_LOGD("Timestamp req ID=%" PRIu8 " at t=%" PRIu64 " timeout=%" PRIu64
969 " (requested=%" PRIu64 "), next timeout=%" PRIu64,
970 outReqState->transaction,
971 outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC,
972 outReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
973 timeoutNs / CHPP_NSEC_PER_MSEC,
974 *nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
975 }
976
chppTimestampIncomingResponse(struct ChppAppState * appState,struct ChppOutgoingRequestState * outReqState,const struct ChppAppHeader * responseHeader)977 bool chppTimestampIncomingResponse(struct ChppAppState *appState,
978 struct ChppOutgoingRequestState *outReqState,
979 const struct ChppAppHeader *responseHeader) {
980 CHPP_DEBUG_NOT_NULL(appState);
981 CHPP_DEBUG_NOT_NULL(outReqState);
982 CHPP_DEBUG_NOT_NULL(responseHeader);
983
984 uint8_t type = responseHeader->type;
985
986 CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_RESPONSE ||
987 type == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE);
988
989 bool success = false;
990 uint64_t responseTime = chppGetCurrentTimeNs();
991
992 switch (outReqState->requestState) {
993 case CHPP_REQUEST_STATE_NONE: {
994 CHPP_LOGE("Resp with no req t=%" PRIu64,
995 responseTime / CHPP_NSEC_PER_MSEC);
996 break;
997 }
998
999 case CHPP_REQUEST_STATE_RESPONSE_RCV: {
1000 CHPP_LOGE("Extra resp at t=%" PRIu64 " for req t=%" PRIu64,
1001 responseTime / CHPP_NSEC_PER_MSEC,
1002 outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
1003 break;
1004 }
1005
1006 case CHPP_REQUEST_STATE_RESPONSE_TIMEOUT: {
1007 CHPP_LOGE("Late resp at t=%" PRIu64 " for req t=%" PRIu64,
1008 responseTime / CHPP_NSEC_PER_MSEC,
1009 outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
1010 break;
1011 }
1012
1013 case CHPP_REQUEST_STATE_REQUEST_SENT: {
1014 if (responseHeader->transaction != outReqState->transaction) {
1015 CHPP_LOGE("Invalid resp ID=%" PRIu8 " at t=%" PRIu64
1016 " expected=%" PRIu8,
1017 responseHeader->transaction,
1018 responseTime / CHPP_NSEC_PER_MSEC, outReqState->transaction);
1019 } else {
1020 outReqState->requestState = (responseTime > outReqState->responseTimeNs)
1021 ? CHPP_REQUEST_STATE_RESPONSE_TIMEOUT
1022 : CHPP_REQUEST_STATE_RESPONSE_RCV;
1023 success = true;
1024
1025 CHPP_LOGD(
1026 "Timestamp resp ID=%" PRIu8 " req t=%" PRIu64 " resp t=%" PRIu64
1027 " timeout t=%" PRIu64 " (RTT=%" PRIu64 ", timeout = %s)",
1028 outReqState->transaction,
1029 outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC,
1030 responseTime / CHPP_NSEC_PER_MSEC,
1031 outReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
1032 (responseTime - outReqState->requestTimeNs) / CHPP_NSEC_PER_MSEC,
1033 (responseTime > outReqState->responseTimeNs) ? "yes" : "no");
1034 }
1035 break;
1036 }
1037
1038 default: {
1039 CHPP_DEBUG_ASSERT_LOG(false, "Invalid req state");
1040 }
1041 }
1042
1043 if (success) {
1044 // When the received request is the next one that was expected
1045 // to timeout we need to recompute the timeout considering the
1046 // other pending requests.
1047 enum ChppEndpointType endpointType =
1048 type == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE ? CHPP_ENDPOINT_CLIENT
1049 : CHPP_ENDPOINT_SERVICE;
1050 if (outReqState->responseTimeNs ==
1051 *getNextRequestTimeoutNs(appState, endpointType)) {
1052 chppRecalculateNextTimeout(appState, endpointType);
1053 }
1054 outReqState->responseTimeNs = responseTime;
1055 }
1056 return success;
1057 }
1058
chppTimestampOutgoingResponse(struct ChppIncomingRequestState * inReqState)1059 uint64_t chppTimestampOutgoingResponse(
1060 struct ChppIncomingRequestState *inReqState) {
1061 CHPP_DEBUG_NOT_NULL(inReqState);
1062
1063 uint64_t previousResponseTime = inReqState->responseTimeNs;
1064 inReqState->responseTimeNs = chppGetCurrentTimeNs();
1065 return previousResponseTime;
1066 }
1067
chppSendTimestampedResponseOrFail(struct ChppAppState * appState,struct ChppIncomingRequestState * inReqState,void * buf,size_t len)1068 bool chppSendTimestampedResponseOrFail(
1069 struct ChppAppState *appState, struct ChppIncomingRequestState *inReqState,
1070 void *buf, size_t len) {
1071 CHPP_DEBUG_NOT_NULL(appState);
1072 CHPP_DEBUG_NOT_NULL(inReqState);
1073 CHPP_DEBUG_NOT_NULL(buf);
1074 uint64_t previousResponseTime = chppTimestampOutgoingResponse(inReqState);
1075
1076 if (inReqState->requestTimeNs == CHPP_TIME_NONE) {
1077 CHPP_LOGE("TX response w/ no req t=%" PRIu64,
1078 inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC);
1079
1080 } else if (previousResponseTime != CHPP_TIME_NONE) {
1081 CHPP_LOGW("TX additional response t=%" PRIu64 " for req t=%" PRIu64,
1082 inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
1083 inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
1084
1085 } else {
1086 CHPP_LOGD("Sending initial response at t=%" PRIu64
1087 " for request at t=%" PRIu64 " (RTT=%" PRIu64 ")",
1088 inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
1089 inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC,
1090 (inReqState->responseTimeNs - inReqState->requestTimeNs) /
1091 CHPP_NSEC_PER_MSEC);
1092 }
1093
1094 return chppEnqueueTxDatagramOrFail(appState->transportContext, buf, len);
1095 }
1096
chppSendTimestampedRequestOrFail(struct ChppEndpointState * endpointState,struct ChppOutgoingRequestState * outReqState,void * buf,size_t len,uint64_t timeoutNs)1097 bool chppSendTimestampedRequestOrFail(
1098 struct ChppEndpointState *endpointState,
1099 struct ChppOutgoingRequestState *outReqState, void *buf, size_t len,
1100 uint64_t timeoutNs) {
1101 CHPP_DEBUG_NOT_NULL(outReqState);
1102 CHPP_DEBUG_NOT_NULL(buf);
1103 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
1104
1105 if (timeoutNs < CHPP_TRANSPORT_TX_TIMEOUT_NS) {
1106 // The app layer sits above the transport layer.
1107 // Request timeout (app layer) should be longer than the transport timeout.
1108 CHPP_LOGW("Request timeout (%" PRIu64
1109 "ns) should be longer than the transport timeout (%" PRIu64 "ns)",
1110 timeoutNs, (uint64_t)CHPP_TRANSPORT_TX_TIMEOUT_NS);
1111 }
1112
1113 chppTimestampOutgoingRequest(endpointState->appContext, outReqState, buf,
1114 timeoutNs);
1115 endpointState->syncResponse.ready = false;
1116
1117 bool success = chppEnqueueTxDatagramOrFail(
1118 endpointState->appContext->transportContext, buf, len);
1119
1120 // Failure to enqueue a TX datagram means that a request was known to be not
1121 // transmitted. We explicitly set requestState to be in the NONE state, so
1122 // that unintended app layer timeouts do not occur.
1123 if (!success) {
1124 outReqState->requestState = CHPP_REQUEST_STATE_NONE;
1125 }
1126
1127 return success;
1128 }
1129
chppWaitForResponseWithTimeout(struct ChppSyncResponse * syncResponse,struct ChppOutgoingRequestState * outReqState,uint64_t timeoutNs)1130 bool chppWaitForResponseWithTimeout(
1131 struct ChppSyncResponse *syncResponse,
1132 struct ChppOutgoingRequestState *outReqState, uint64_t timeoutNs) {
1133 CHPP_DEBUG_NOT_NULL(syncResponse);
1134 CHPP_DEBUG_NOT_NULL(outReqState);
1135
1136 bool result = true;
1137
1138 chppMutexLock(&syncResponse->mutex);
1139
1140 while (result && !syncResponse->ready) {
1141 result = chppConditionVariableTimedWait(&syncResponse->condVar,
1142 &syncResponse->mutex, timeoutNs);
1143 }
1144 if (!syncResponse->ready) {
1145 outReqState->requestState = CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
1146 CHPP_LOGE("Response timeout after %" PRIu64 " ms",
1147 timeoutNs / CHPP_NSEC_PER_MSEC);
1148 result = false;
1149 }
1150
1151 chppMutexUnlock(&syncResponse->mutex);
1152
1153 return result;
1154 }
1155
getRegisteredEndpointState(struct ChppAppState * appState,uint8_t index,enum ChppEndpointType type)1156 struct ChppEndpointState *getRegisteredEndpointState(
1157 struct ChppAppState *appState, uint8_t index, enum ChppEndpointType type) {
1158 CHPP_DEBUG_NOT_NULL(appState);
1159 CHPP_DEBUG_ASSERT(index < getRegisteredEndpointCount(appState, type));
1160
1161 return type == CHPP_ENDPOINT_CLIENT
1162 ? appState->registeredClientStates[index]
1163 : appState->registeredServiceStates[index];
1164 }
1165
getRegisteredEndpointOutReqCount(struct ChppAppState * appState,uint8_t index,enum ChppEndpointType type)1166 uint16_t getRegisteredEndpointOutReqCount(struct ChppAppState *appState,
1167 uint8_t index,
1168 enum ChppEndpointType type) {
1169 CHPP_DEBUG_NOT_NULL(appState);
1170 CHPP_DEBUG_ASSERT(index < getRegisteredEndpointCount(appState, type));
1171
1172 return type == CHPP_ENDPOINT_CLIENT
1173 ? appState->registeredClients[index]->outReqCount
1174 : appState->registeredServices[index]->outReqCount;
1175 }
1176
getRegisteredEndpointCount(struct ChppAppState * appState,enum ChppEndpointType type)1177 uint8_t getRegisteredEndpointCount(struct ChppAppState *appState,
1178 enum ChppEndpointType type) {
1179 return type == CHPP_ENDPOINT_CLIENT ? appState->registeredClientCount
1180 : appState->registeredServiceCount;
1181 }
1182
chppRecalculateNextTimeout(struct ChppAppState * appState,enum ChppEndpointType type)1183 void chppRecalculateNextTimeout(struct ChppAppState *appState,
1184 enum ChppEndpointType type) {
1185 CHPP_DEBUG_NOT_NULL(appState);
1186
1187 uint64_t timeoutNs = CHPP_TIME_MAX;
1188
1189 const uint8_t endpointCount = getRegisteredEndpointCount(appState, type);
1190
1191 for (uint8_t endpointIdx = 0; endpointIdx < endpointCount; endpointIdx++) {
1192 uint16_t reqCount =
1193 getRegisteredEndpointOutReqCount(appState, endpointIdx, type);
1194 struct ChppEndpointState *endpointState =
1195 getRegisteredEndpointState(appState, endpointIdx, type);
1196 struct ChppOutgoingRequestState *reqStates = endpointState->outReqStates;
1197 for (uint16_t cmdIdx = 0; cmdIdx < reqCount; cmdIdx++) {
1198 struct ChppOutgoingRequestState *reqState = &reqStates[cmdIdx];
1199
1200 if (reqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
1201 timeoutNs = MIN(timeoutNs, reqState->responseTimeNs);
1202 }
1203 }
1204 }
1205
1206 CHPP_LOGD("nextReqTimeout=%" PRIu64, timeoutNs / CHPP_NSEC_PER_MSEC);
1207
1208 if (type == CHPP_ENDPOINT_CLIENT) {
1209 appState->nextClientRequestTimeoutNs = timeoutNs;
1210 } else {
1211 appState->nextServiceRequestTimeoutNs = timeoutNs;
1212 }
1213 }
1214
getNextRequestTimeoutNs(struct ChppAppState * appState,enum ChppEndpointType type)1215 uint64_t *getNextRequestTimeoutNs(struct ChppAppState *appState,
1216 enum ChppEndpointType type) {
1217 return type == CHPP_ENDPOINT_CLIENT ? &appState->nextClientRequestTimeoutNs
1218 : &appState->nextServiceRequestTimeoutNs;
1219 }
1220
chppCloseOpenRequests(struct ChppEndpointState * endpointState,enum ChppEndpointType type,bool clearOnly)1221 void chppCloseOpenRequests(struct ChppEndpointState *endpointState,
1222 enum ChppEndpointType type, bool clearOnly) {
1223 CHPP_DEBUG_NOT_NULL(endpointState);
1224
1225 bool recalcNeeded = false;
1226
1227 struct ChppAppState *appState = endpointState->appContext;
1228 const uint8_t enpointIdx = endpointState->index;
1229 const uint16_t cmdCount =
1230 getRegisteredEndpointOutReqCount(appState, enpointIdx, type);
1231
1232 for (uint16_t cmdIdx = 0; cmdIdx < cmdCount; cmdIdx++) {
1233 if (endpointState->outReqStates[cmdIdx].requestState ==
1234 CHPP_REQUEST_STATE_REQUEST_SENT) {
1235 recalcNeeded = true;
1236
1237 CHPP_LOGE("Closing open req #%" PRIu16 " clear %d", cmdIdx, clearOnly);
1238
1239 if (clearOnly) {
1240 endpointState->outReqStates[cmdIdx].requestState =
1241 CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
1242 } else {
1243 struct ChppAppHeader *response =
1244 chppMalloc(sizeof(struct ChppAppHeader));
1245 if (response == NULL) {
1246 CHPP_LOG_OOM();
1247 } else {
1248 // Simulate receiving a timeout response.
1249 response->handle = endpointState->handle;
1250 response->type = type == CHPP_ENDPOINT_CLIENT
1251 ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE
1252 : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE;
1253 response->transaction =
1254 endpointState->outReqStates[cmdIdx].transaction;
1255 response->error = CHPP_APP_ERROR_TIMEOUT;
1256 response->command = cmdIdx;
1257
1258 chppAppProcessRxDatagram(appState, (uint8_t *)response,
1259 sizeof(struct ChppAppHeader));
1260 }
1261 }
1262 }
1263 }
1264 if (recalcNeeded) {
1265 chppRecalculateNextTimeout(appState, type);
1266 }
1267 }
1268
chppAppRequestTimerTimeout(struct ChppEndpointState * endpointState,uint64_t timeoutNs)1269 bool chppAppRequestTimerTimeout(struct ChppEndpointState *endpointState,
1270 uint64_t timeoutNs) {
1271 if (endpointState->nextTimerTimeoutNs != CHPP_TIME_MAX) {
1272 CHPP_LOGE("Timer already scheduled for %" PRIu64 "ns",
1273 endpointState->nextTimerTimeoutNs);
1274 return false;
1275 }
1276
1277 endpointState->nextTimerTimeoutNs = chppGetCurrentTimeNs() + timeoutNs;
1278 return true;
1279 }
1280
chppAppCancelTimerTimeout(struct ChppEndpointState * endpointState)1281 void chppAppCancelTimerTimeout(struct ChppEndpointState *endpointState) {
1282 endpointState->nextTimerTimeoutNs = CHPP_TIME_MAX;
1283 }
1284
chppAppGetNextTimerTimeoutNs(struct ChppAppState * context)1285 uint64_t chppAppGetNextTimerTimeoutNs(struct ChppAppState *context) {
1286 CHPP_DEBUG_NOT_NULL(context);
1287 uint64_t timeoutNs = CHPP_TIME_MAX;
1288 for (uint8_t i = 0; i < context->registeredClientCount; i++) {
1289 timeoutNs =
1290 MIN(timeoutNs, context->registeredClientStates[i]->nextTimerTimeoutNs);
1291 }
1292 for (uint8_t i = 0; i < context->registeredServiceCount; i++) {
1293 timeoutNs =
1294 MIN(timeoutNs, context->registeredServiceStates[i]->nextTimerTimeoutNs);
1295 }
1296
1297 return timeoutNs;
1298 }