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 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
29 #include "chpp/clients/loopback.h"
30 #endif
31 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
32 #include "chpp/clients/timesync.h"
33 #endif
34 #include "chpp/log.h"
35 #include "chpp/macros.h"
36 #include "chpp/notifier.h"
37 #include "chpp/pal_api.h"
38 #include "chpp/services.h"
39 #include "chpp/services/discovery.h"
40 #include "chpp/services/loopback.h"
41 #include "chpp/services/nonhandle.h"
42 #include "chpp/services/timesync.h"
43 #include "chre_api/chre/common.h"
44
45 /************************************************
46 * Prototypes
47 ***********************************************/
48
49 static bool chppProcessPredefinedClientRequest(struct ChppAppState *context,
50 uint8_t *buf, size_t len);
51 static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context,
52 uint8_t *buf, size_t len);
53 static bool chppProcessPredefinedClientNotification(
54 struct ChppAppState *context, uint8_t *buf, size_t len);
55 static bool chppProcessPredefinedServiceNotification(
56 struct ChppAppState *context, uint8_t *buf, size_t len);
57
58 static bool chppDatagramLenIsOk(struct ChppAppState *context,
59 struct ChppAppHeader *rxHeader, size_t len);
60 ChppDispatchFunction *chppGetDispatchFunction(struct ChppAppState *context,
61 uint8_t handle,
62 enum ChppMessageType type);
63 ChppNotifierFunction *chppGetClientResetNotifierFunction(
64 struct ChppAppState *context, uint8_t index);
65 ChppNotifierFunction *chppGetServiceResetNotifierFunction(
66 struct ChppAppState *context, uint8_t index);
67 static inline const struct ChppService *chppServiceOfHandle(
68 struct ChppAppState *appContext, uint8_t handle);
69 static inline const struct ChppClient *chppClientOfHandle(
70 struct ChppAppState *appContext, uint8_t handle);
71 static inline void *chppServiceContextOfHandle(struct ChppAppState *appContext,
72 uint8_t handle);
73 static inline void *chppClientContextOfHandle(struct ChppAppState *appContext,
74 uint8_t handle);
75 static void *chppClientServiceContextOfHandle(struct ChppAppState *appContext,
76 uint8_t handle,
77 enum ChppMessageType type);
78
79 static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context,
80 uint8_t *buf, size_t len);
81 static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context,
82 uint8_t *buf, size_t len);
83
84 /************************************************
85 * Private Functions
86 ***********************************************/
87
88 /**
89 * Processes a client request that is determined to be for a predefined CHPP
90 * service.
91 *
92 * @param context Maintains status for each app layer instance.
93 * @param buf Input data. Cannot be null.
94 * @param len Length of input data in bytes.
95 *
96 * @return False if handle is invalid. True otherwise.
97 */
chppProcessPredefinedClientRequest(struct ChppAppState * context,uint8_t * buf,size_t len)98 static bool chppProcessPredefinedClientRequest(struct ChppAppState *context,
99 uint8_t *buf, size_t len) {
100 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
101 bool handleValid = true;
102 bool dispatchResult = true;
103
104 switch (rxHeader->handle) {
105 case CHPP_HANDLE_LOOPBACK: {
106 dispatchResult = chppDispatchLoopbackClientRequest(context, buf, len);
107 break;
108 }
109
110 case CHPP_HANDLE_TIMESYNC: {
111 dispatchResult = chppDispatchTimesyncClientRequest(context, buf, len);
112 break;
113 }
114
115 case CHPP_HANDLE_DISCOVERY: {
116 dispatchResult = chppDispatchDiscoveryClientRequest(context, buf, len);
117 break;
118 }
119
120 default: {
121 handleValid = false;
122 }
123 }
124
125 if (dispatchResult == false) {
126 CHPP_LOGE("H#%" PRIu8 " unknown request. cmd=%#x, ID=%" PRIu8,
127 rxHeader->handle, rxHeader->command, rxHeader->transaction);
128 }
129
130 return handleValid;
131 }
132
133 /**
134 * Processes a service response that is determined to be for a predefined CHPP
135 * client.
136 *
137 * @param context Maintains status for each app layer instance.
138 * @param buf Input data. Cannot be null.
139 * @param len Length of input data in bytes.
140 *
141 * @return False if handle is invalid. True otherwise.
142 */
chppProcessPredefinedServiceResponse(struct ChppAppState * context,uint8_t * buf,size_t len)143 static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context,
144 uint8_t *buf, size_t len) {
145 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
146 bool handleValid = true;
147 bool dispatchResult = true;
148
149 switch (rxHeader->handle) {
150 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
151 case CHPP_HANDLE_LOOPBACK: {
152 dispatchResult = chppDispatchLoopbackServiceResponse(context, buf, len);
153 break;
154 }
155 #endif // CHPP_CLIENT_ENABLED_LOOPBACK
156
157 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
158 case CHPP_HANDLE_TIMESYNC: {
159 dispatchResult = chppDispatchTimesyncServiceResponse(context, buf, len);
160 break;
161 }
162 #endif // CHPP_CLIENT_ENABLED_TIMESYNC
163
164 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
165 case CHPP_HANDLE_DISCOVERY: {
166 dispatchResult = chppDispatchDiscoveryServiceResponse(context, buf, len);
167 break;
168 }
169 #endif // CHPP_CLIENT_ENABLED_DISCOVERY
170
171 default: {
172 handleValid = false;
173 }
174 }
175
176 if (dispatchResult == false) {
177 CHPP_LOGE("H#%" PRIu8 " unknown response. cmd=%#x, ID=%" PRIu8
178 ", len=%" PRIuSIZE,
179 rxHeader->handle, rxHeader->command, rxHeader->transaction, len);
180 }
181
182 return handleValid;
183 }
184
185 /**
186 * Processes a client notification that is determined to be for a predefined
187 * CHPP service.
188 *
189 * @param context Maintains status for each app layer instance.
190 * @param buf Input data. Cannot be null.
191 * @param len Length of input data in bytes.
192 *
193 * @return False if handle is invalid. True otherwise.
194 */
chppProcessPredefinedClientNotification(struct ChppAppState * context,uint8_t * buf,size_t len)195 static bool chppProcessPredefinedClientNotification(
196 struct ChppAppState *context, uint8_t *buf, size_t len) {
197 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
198 bool handleValid = true;
199 bool dispatchResult = true;
200
201 // No predefined services support these yet
202 handleValid = false;
203
204 UNUSED_VAR(context);
205 UNUSED_VAR(len);
206 UNUSED_VAR(rxHeader);
207 UNUSED_VAR(dispatchResult);
208
209 return handleValid;
210 }
211
212 /**
213 * Processes a service notification that is determined to be for a predefined
214 * CHPP client.
215 *
216 * @param context Maintains status for each app layer instance.
217 * @param buf Input data. Cannot be null.
218 * @param len Length of input data in bytes.
219 *
220 * @return False if handle is invalid. True otherwise.
221 */
chppProcessPredefinedServiceNotification(struct ChppAppState * context,uint8_t * buf,size_t len)222 static bool chppProcessPredefinedServiceNotification(
223 struct ChppAppState *context, uint8_t *buf, size_t len) {
224 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
225 bool handleValid = true;
226 bool dispatchResult = true;
227
228 // No predefined clients support these yet
229 handleValid = false;
230
231 UNUSED_VAR(context);
232 UNUSED_VAR(len);
233 UNUSED_VAR(rxHeader);
234 UNUSED_VAR(dispatchResult);
235
236 return handleValid;
237 }
238
239 /**
240 * Verifies if the length of a Rx Datagram from the transport layer is
241 * sufficient for the associated service.
242 *
243 * @param context Maintains status for each app layer instance.
244 * @param rxHeader The pointer to the datagram RX header.
245 * @param len Length of the datagram in bytes.
246 *
247 * @return true if length is ok.
248 */
chppDatagramLenIsOk(struct ChppAppState * context,struct ChppAppHeader * rxHeader,size_t len)249 static bool chppDatagramLenIsOk(struct ChppAppState *context,
250 struct ChppAppHeader *rxHeader, size_t len) {
251 size_t minLen = SIZE_MAX;
252 uint8_t handle = rxHeader->handle;
253
254 if (handle < CHPP_HANDLE_NEGOTIATED_RANGE_START) { // Predefined
255 switch (handle) {
256 case CHPP_HANDLE_NONE:
257 minLen = sizeof_member(struct ChppAppHeader, handle);
258 break;
259
260 case CHPP_HANDLE_LOOPBACK:
261 minLen = sizeof_member(struct ChppAppHeader, handle) +
262 sizeof_member(struct ChppAppHeader, type);
263 break;
264
265 case CHPP_HANDLE_TIMESYNC:
266 case CHPP_HANDLE_DISCOVERY:
267 minLen = sizeof(struct ChppAppHeader);
268 break;
269
270 default:
271 CHPP_LOGE("Invalid H#%" PRIu8, handle);
272 }
273
274 } else { // Negotiated
275 enum ChppMessageType messageType =
276 CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type);
277
278 switch (messageType) {
279 case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
280 case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
281 const struct ChppService *service =
282 chppServiceOfHandle(context, handle);
283 if (service != NULL) {
284 minLen = service->minLength;
285 }
286 break;
287 }
288 case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
289 case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
290 const struct ChppClient *client = chppClientOfHandle(context, handle);
291 if (client != NULL) {
292 minLen = client->minLength;
293 }
294 break;
295 }
296 default: {
297 break;
298 }
299 }
300
301 if (minLen == SIZE_MAX) {
302 CHPP_LOGE("Invalid type=%d or H#%" PRIu8, messageType, handle);
303 }
304 }
305
306 if ((len < minLen) && (minLen != SIZE_MAX)) {
307 CHPP_LOGE("Datagram len=%" PRIuSIZE " < %" PRIuSIZE " for H#%" PRIu8, len,
308 minLen, handle);
309 }
310 return (len >= minLen) && (minLen != SIZE_MAX);
311 }
312
313 /**
314 * Returns the dispatch function of a particular negotiated client/service
315 * handle and message type. This shall be null if it is unsupported by the
316 * service.
317 *
318 * @param context Maintains status for each app layer instance.
319 * @param handle Handle number for the client/service.
320 * @param type Message type.
321 *
322 * @return Pointer to a function that dispatches incoming datagrams for any
323 * particular client/service.
324 */
chppGetDispatchFunction(struct ChppAppState * context,uint8_t handle,enum ChppMessageType type)325 ChppDispatchFunction *chppGetDispatchFunction(struct ChppAppState *context,
326 uint8_t handle,
327 enum ChppMessageType type) {
328 // chppDatagramLenIsOk() has already confirmed that the handle # is valid.
329 // Therefore, no additional checks are necessary for chppClientOfHandle(),
330 // chppServiceOfHandle(), or chppClientServiceContextOfHandle().
331
332 switch (CHPP_APP_GET_MESSAGE_TYPE(type)) {
333 case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: {
334 return chppServiceOfHandle(context, handle)->requestDispatchFunctionPtr;
335 break;
336 }
337 case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: {
338 struct ChppClientState *clientState =
339 (struct ChppClientState *)chppClientServiceContextOfHandle(
340 context, handle, type);
341 if (clientState->openState == CHPP_OPEN_STATE_CLOSED) {
342 CHPP_LOGE("Rx service response but client closed");
343 } else {
344 return chppClientOfHandle(context, handle)->responseDispatchFunctionPtr;
345 }
346 break;
347 }
348 case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
349 return chppServiceOfHandle(context, handle)
350 ->notificationDispatchFunctionPtr;
351 break;
352 }
353 case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
354 struct ChppClientState *clientState =
355 (struct ChppClientState *)chppClientServiceContextOfHandle(
356 context, handle, type);
357 if (clientState->openState == CHPP_OPEN_STATE_CLOSED) {
358 CHPP_LOGE("Rx service notification but client closed");
359 } else {
360 return chppClientOfHandle(context, handle)
361 ->notificationDispatchFunctionPtr;
362 }
363 break;
364 }
365 }
366
367 return NULL;
368 }
369
370 /**
371 * Returns the reset notification function pointer of a particular negotiated
372 * client. The function pointer will be set to null by clients that do not need
373 * or support a reset notification.
374 *
375 * @param context Maintains status for each app layer instance.
376 * @param index Index of the registered client.
377 *
378 * @return Pointer to the reset notification function.
379 */
chppGetClientResetNotifierFunction(struct ChppAppState * context,uint8_t index)380 ChppNotifierFunction *chppGetClientResetNotifierFunction(
381 struct ChppAppState *context, uint8_t index) {
382 return context->registeredClients[index]->resetNotifierFunctionPtr;
383 }
384
385 /**
386 * Returns the reset function pointer of a particular registered service. The
387 * function pointer will be set to null by services that do not need or support
388 * a reset notification.
389 *
390 * @param context Maintains status for each app layer instance.
391 * @param index Index of the registered service.
392 *
393 * @return Pointer to the reset function.
394 */
chppGetServiceResetNotifierFunction(struct ChppAppState * context,uint8_t index)395 ChppNotifierFunction *chppGetServiceResetNotifierFunction(
396 struct ChppAppState *context, uint8_t index) {
397 return context->registeredServices[index]->resetNotifierFunctionPtr;
398 }
399
400 /**
401 * Returns a pointer to the ChppService struct of the service matched to a
402 * negotiated handle. Returns null if a service doesn't exist for the handle.
403 *
404 * @param context Maintains status for each app layer instance.
405 * @param handle Handle number.
406 *
407 * @return Pointer to the ChppService struct of a particular service handle.
408 */
chppServiceOfHandle(struct ChppAppState * context,uint8_t handle)409 static inline const struct ChppService *chppServiceOfHandle(
410 struct ChppAppState *context, uint8_t handle) {
411 uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
412 if (serviceIndex < context->registeredServiceCount) {
413 return context->registeredServices[serviceIndex];
414 }
415
416 return NULL;
417 }
418
419 /**
420 * Returns a pointer to the ChppClient struct of the client matched to a
421 * negotiated handle. Returns null if a client doesn't exist for the handle.
422 *
423 * @param context Maintains status for each app layer instance.
424 * @param handle Handle number.
425 *
426 * @return Pointer to the ChppClient struct matched to a particular handle.
427 */
chppClientOfHandle(struct ChppAppState * context,uint8_t handle)428 static inline const struct ChppClient *chppClientOfHandle(
429 struct ChppAppState *context, uint8_t handle) {
430 uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
431 if (serviceIndex < context->discoveredServiceCount) {
432 uint8_t clientIndex = context->clientIndexOfServiceIndex[serviceIndex];
433 if (clientIndex < context->registeredClientCount) {
434 return context->registeredClients[clientIndex];
435 }
436 }
437
438 return NULL;
439 }
440
441 /**
442 * Returns a pointer to the service struct of a particular negotiated service
443 * handle.
444 * It is up to the caller to ensure the handle number is valid.
445 *
446 * @param context Maintains status for each app layer instance.
447 * @param handle Handle number for the service.
448 *
449 * @return Pointer to the context struct of the service.
450 */
chppServiceContextOfHandle(struct ChppAppState * context,uint8_t handle)451 static inline void *chppServiceContextOfHandle(struct ChppAppState *context,
452 uint8_t handle) {
453 CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) <
454 context->registeredServiceCount);
455 return context
456 ->registeredServiceContexts[CHPP_SERVICE_INDEX_OF_HANDLE(handle)];
457 }
458
459 /**
460 * Returns a pointer to the client struct of a particular negotiated client
461 * handle.
462 * It is up to the caller to ensure the handle number is valid.
463 *
464 * @param context Maintains status for each app layer instance.
465 * @param handle Handle number for the service.
466 *
467 * @return Pointer to the ChppService struct of the client.
468 */
chppClientContextOfHandle(struct ChppAppState * context,uint8_t handle)469 static inline void *chppClientContextOfHandle(struct ChppAppState *context,
470 uint8_t handle) {
471 CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) <
472 context->registeredClientCount);
473 return context
474 ->registeredClientContexts[context->clientIndexOfServiceIndex
475 [CHPP_SERVICE_INDEX_OF_HANDLE(handle)]];
476 }
477
478 /**
479 * Returns a pointer to the client/service struct of a particular negotiated
480 * client/service handle.
481 * It is up to the caller to ensure the handle number is valid.
482 *
483 * @param appContext Maintains status for each app layer instance.
484 * @param handle Handle number for the service.
485 * @param type Message type (indicates if this is for a client or service).
486 *
487 * @return Pointer to the client/service struct of the service handle.
488 */
chppClientServiceContextOfHandle(struct ChppAppState * appContext,uint8_t handle,enum ChppMessageType type)489 static void *chppClientServiceContextOfHandle(struct ChppAppState *appContext,
490 uint8_t handle,
491 enum ChppMessageType type) {
492 switch (CHPP_APP_GET_MESSAGE_TYPE(type)) {
493 case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
494 case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
495 return chppServiceContextOfHandle(appContext, handle);
496 break;
497 }
498 case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
499 case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
500 return chppClientContextOfHandle(appContext, handle);
501 break;
502 }
503 default: {
504 CHPP_LOGE("Unknown type=0x%" PRIx8 " (H#%" PRIu8 ")", type, handle);
505 return NULL;
506 }
507 }
508 }
509
510 /**
511 * Processes a received datagram that is determined to be for a predefined CHPP
512 * service. Responds with an error if unsuccessful.
513 *
514 * @param context Maintains status for each app layer instance.
515 * @param buf Input data. Cannot be null.
516 * @param len Length of input data in bytes.
517 */
chppProcessPredefinedHandleDatagram(struct ChppAppState * context,uint8_t * buf,size_t len)518 static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context,
519 uint8_t *buf, size_t len) {
520 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
521 bool success = true;
522
523 switch (CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type)) {
524 case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: {
525 success = chppProcessPredefinedClientRequest(context, buf, len);
526 break;
527 }
528 case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
529 success = chppProcessPredefinedClientNotification(context, buf, len);
530 break;
531 }
532 case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: {
533 success = chppProcessPredefinedServiceResponse(context, buf, len);
534 break;
535 }
536 case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
537 success = chppProcessPredefinedServiceNotification(context, buf, len);
538 break;
539 }
540 default: {
541 success = false;
542 }
543 }
544
545 if (success == false) {
546 CHPP_LOGE("H#%" PRIu8 " undefined msg type=0x%" PRIx8 " (len=%" PRIuSIZE
547 ", ID=%" PRIu8 ")",
548 rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
549 chppEnqueueTxErrorDatagram(context->transportContext,
550 CHPP_TRANSPORT_ERROR_APPLAYER);
551 }
552 }
553
554 /**
555 * Processes a received datagram that is determined to be for a negotiated CHPP
556 * client or service. Responds with an error if unsuccessful.
557 *
558 * @param context Maintains status for each app layer instance.
559 * @param buf Input data. Cannot be null.
560 * @param len Length of input data in bytes.
561 */
chppProcessNegotiatedHandleDatagram(struct ChppAppState * context,uint8_t * buf,size_t len)562 static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context,
563 uint8_t *buf, size_t len) {
564 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
565 enum ChppMessageType messageType = CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type);
566
567 void *clientServiceContext =
568 chppClientServiceContextOfHandle(context, rxHeader->handle, messageType);
569 if (clientServiceContext == NULL) {
570 CHPP_LOGE("H#%" PRIu8 " missing ctx (msg=0x%" PRIx8 " len=%" PRIuSIZE
571 ", ID=%" PRIu8 ")",
572 rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
573 chppEnqueueTxErrorDatagram(context->transportContext,
574 CHPP_TRANSPORT_ERROR_APPLAYER);
575 CHPP_DEBUG_ASSERT(false);
576
577 } else {
578 ChppDispatchFunction *dispatchFunc =
579 chppGetDispatchFunction(context, rxHeader->handle, messageType);
580 if (dispatchFunc == NULL) {
581 CHPP_LOGE("H#%" PRIu8 " unsupported msg=0x%" PRIx8 " (len=%" PRIuSIZE
582 ", ID=%" PRIu8 ")",
583 rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
584 chppEnqueueTxErrorDatagram(context->transportContext,
585 CHPP_TRANSPORT_ERROR_APPLAYER);
586
587 } else {
588 // All good. Dispatch datagram and possibly notify a waiting client
589
590 enum ChppAppErrorCode error =
591 dispatchFunc(clientServiceContext, buf, len);
592 if (error != CHPP_APP_ERROR_NONE) {
593 CHPP_LOGE("RX dispatch err=0x%" PRIx16 " H#%" PRIu8 " type=0x%" PRIx8
594 " ID=%" PRIu8 " cmd=0x%" PRIx16 " len=%" PRIuSIZE,
595 error, rxHeader->handle, rxHeader->type,
596 rxHeader->transaction, rxHeader->command, len);
597
598 // Only client requests require a dispatch failure response.
599 if (messageType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) {
600 struct ChppAppHeader *response =
601 chppAllocServiceResponseFixed(rxHeader, struct ChppAppHeader);
602 if (response == NULL) {
603 CHPP_LOG_OOM();
604 } else {
605 response->error = (uint8_t)error;
606 chppEnqueueTxDatagramOrFail(context->transportContext, response,
607 sizeof(*response));
608 }
609 }
610 } else if (messageType == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE) {
611 // Datagram is a service response. Check for synchronous operation and
612 // notify waiting client if needed.
613
614 struct ChppClientState *clientState =
615 (struct ChppClientState *)clientServiceContext;
616 chppMutexLock(&clientState->responseMutex);
617 clientState->responseReady = true;
618 CHPP_LOGD(
619 "Finished dispatching a service response. Notifying a potential "
620 "synchronous client");
621 chppConditionVariableSignal(&clientState->responseCondVar);
622 chppMutexUnlock(&clientState->responseMutex);
623 }
624 }
625 }
626 }
627
628 /************************************************
629 * Public Functions
630 ***********************************************/
631
chppAppInit(struct ChppAppState * appContext,struct ChppTransportState * transportContext)632 void chppAppInit(struct ChppAppState *appContext,
633 struct ChppTransportState *transportContext) {
634 // Default initialize all service/clients
635 struct ChppClientServiceSet set;
636 memset(&set, 0xff, sizeof(set)); // set all bits to 1
637
638 chppAppInitWithClientServiceSet(appContext, transportContext, set);
639 }
640
chppAppInitWithClientServiceSet(struct ChppAppState * appContext,struct ChppTransportState * transportContext,struct ChppClientServiceSet clientServiceSet)641 void chppAppInitWithClientServiceSet(
642 struct ChppAppState *appContext,
643 struct ChppTransportState *transportContext,
644 struct ChppClientServiceSet clientServiceSet) {
645 CHPP_NOT_NULL(appContext);
646
647 CHPP_LOGD("App Init");
648
649 memset(appContext, 0, sizeof(*appContext));
650
651 appContext->clientServiceSet = clientServiceSet;
652 appContext->transportContext = transportContext;
653 appContext->nextRequestTimeoutNs = CHPP_TIME_MAX;
654
655 chppPalSystemApiInit(appContext);
656
657 #ifdef CHPP_SERVICE_ENABLED
658 chppRegisterCommonServices(appContext);
659 #endif
660
661 #ifdef CHPP_CLIENT_ENABLED
662 chppRegisterCommonClients(appContext);
663 chppInitBasicClients(appContext);
664 #endif
665 }
666
chppAppDeinit(struct ChppAppState * appContext)667 void chppAppDeinit(struct ChppAppState *appContext) {
668 CHPP_LOGD("App deinit");
669
670 #ifdef CHPP_CLIENT_ENABLED
671 chppDeinitMatchedClients(appContext);
672 chppDeinitBasicClients(appContext);
673 chppDeregisterCommonClients(appContext);
674 #endif
675
676 #ifdef CHPP_SERVICE_ENABLED
677 chppDeregisterCommonServices(appContext);
678 #endif
679
680 chppPalSystemApiDeinit(appContext);
681 }
682
chppAppProcessRxDatagram(struct ChppAppState * context,uint8_t * buf,size_t len)683 void chppAppProcessRxDatagram(struct ChppAppState *context, uint8_t *buf,
684 size_t len) {
685 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
686
687 if (len == 0) {
688 CHPP_LOGE("App rx w/ len 0");
689 CHPP_DEBUG_ASSERT(false);
690
691 } else if (len < sizeof(struct ChppAppHeader)) {
692 uint8_t *handle = (uint8_t *)buf;
693 CHPP_LOGD("RX datagram len=%" PRIuSIZE " H#%" PRIu8, len, *handle);
694
695 } else if (rxHeader->error != CHPP_APP_ERROR_NONE) {
696 CHPP_LOGE("RX datagram len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8
697 " ID=%" PRIu8 " ERR=%" PRIu8 " cmd=0x%" PRIx16,
698 len, rxHeader->handle, rxHeader->type, rxHeader->transaction,
699 rxHeader->error, rxHeader->command);
700 } else {
701 CHPP_LOGD("RX datagram len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8
702 " ID=%" PRIu8 " err=%" PRIu8 " cmd=0x%" PRIx16,
703 len, rxHeader->handle, rxHeader->type, rxHeader->transaction,
704 rxHeader->error, rxHeader->command);
705 }
706
707 if (!chppDatagramLenIsOk(context, rxHeader, len)) {
708 chppEnqueueTxErrorDatagram(context->transportContext,
709 CHPP_TRANSPORT_ERROR_APPLAYER);
710
711 } else {
712 if (rxHeader->handle == CHPP_HANDLE_NONE) {
713 chppDispatchNonHandle(context, buf, len);
714
715 } else if (rxHeader->handle < CHPP_HANDLE_NEGOTIATED_RANGE_START) {
716 chppProcessPredefinedHandleDatagram(context, buf, len);
717
718 } else {
719 chppProcessNegotiatedHandleDatagram(context, buf, len);
720 }
721 }
722
723 chppDatagramProcessDoneCb(context->transportContext, buf);
724 }
725
chppAppProcessReset(struct ChppAppState * context)726 void chppAppProcessReset(struct ChppAppState *context) {
727 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
728 if (!context->isDiscoveryComplete) {
729 chppInitiateDiscovery(context);
730
731 } else {
732 // Notify matched clients that a reset happened
733 for (uint8_t i = 0; i < context->discoveredServiceCount; i++) {
734 uint8_t clientIndex = context->clientIndexOfServiceIndex[i];
735 if (clientIndex != CHPP_CLIENT_INDEX_NONE) {
736 // Discovered service has a matched client
737 ChppNotifierFunction *ResetNotifierFunction =
738 chppGetClientResetNotifierFunction(context, clientIndex);
739
740 CHPP_LOGD("Client #%" PRIu8 " (H#%d) reset notifier found=%d",
741 clientIndex, CHPP_SERVICE_HANDLE_OF_INDEX(i),
742 (ResetNotifierFunction != NULL));
743
744 if (ResetNotifierFunction != NULL) {
745 ResetNotifierFunction(context->registeredClientContexts[clientIndex]);
746 }
747 }
748 }
749 }
750 #endif // CHPP_CLIENT_ENABLED_DISCOVERY
751
752 // Notify registered services that a reset happened
753 for (uint8_t i = 0; i < context->registeredServiceCount; i++) {
754 ChppNotifierFunction *ResetNotifierFunction =
755 chppGetServiceResetNotifierFunction(context, i);
756
757 CHPP_LOGD("Service #%" PRIu8 " (H#%d) reset notifier found=%d", i,
758 CHPP_SERVICE_HANDLE_OF_INDEX(i), (ResetNotifierFunction != NULL));
759
760 if (ResetNotifierFunction != NULL) {
761 ResetNotifierFunction(context->registeredServiceContexts[i]);
762 }
763 }
764
765 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
766 // Reinitialize time offset
767 chppTimesyncClientReset(context);
768 #endif
769 }
770
chppUuidToStr(const uint8_t uuid[CHPP_SERVICE_UUID_LEN],char strOut[CHPP_SERVICE_UUID_STRING_LEN])771 void chppUuidToStr(const uint8_t uuid[CHPP_SERVICE_UUID_LEN],
772 char strOut[CHPP_SERVICE_UUID_STRING_LEN]) {
773 snprintf(
774 strOut, CHPP_SERVICE_UUID_STRING_LEN,
775 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
776 uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
777 uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
778 uuid[15]);
779 }
780
chppAppErrorToChreError(uint8_t chppError)781 uint8_t chppAppErrorToChreError(uint8_t chppError) {
782 switch (chppError) {
783 case CHPP_APP_ERROR_NONE:
784 case CHPP_APP_ERROR_INVALID_ARG:
785 case CHPP_APP_ERROR_BUSY:
786 case CHPP_APP_ERROR_OOM:
787 case CHPP_APP_ERROR_UNSUPPORTED:
788 case CHPP_APP_ERROR_TIMEOUT:
789 case CHPP_APP_ERROR_DISABLED:
790 case CHPP_APP_ERROR_RATELIMITED: {
791 // CHRE and CHPP error values are identical in these cases
792 return chppError;
793 }
794 default: {
795 return CHRE_ERROR;
796 }
797 }
798 }
799