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 // len remains SIZE_MAX
272 CHPP_LOGE("Invalid H#%" PRIu8, handle);
273 }
274
275 } else { // Negotiated
276 enum ChppMessageType messageType =
277 CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type);
278
279 switch (messageType) {
280 case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
281 case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
282 const struct ChppService *service =
283 chppServiceOfHandle(context, handle);
284 if (service != NULL) {
285 minLen = service->minLength;
286 }
287 break;
288 }
289 case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
290 case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
291 const struct ChppClient *client = chppClientOfHandle(context, handle);
292 if (client != NULL) {
293 minLen = client->minLength;
294 }
295 break;
296 }
297 default: {
298 break;
299 }
300 }
301
302 if (minLen == SIZE_MAX) {
303 CHPP_LOGE("Invalid type=%d or H#%" PRIu8, messageType, handle);
304 }
305 }
306
307 if ((len < minLen) && (minLen != SIZE_MAX)) {
308 CHPP_LOGE("Datagram len=%" PRIuSIZE " < %" PRIuSIZE " for H#%" PRIu8, len,
309 minLen, handle);
310 }
311 return (len >= minLen) && (minLen != SIZE_MAX);
312 }
313
314 /**
315 * Returns the dispatch function of a particular negotiated client/service
316 * handle and message type. This shall be null if it is unsupported by the
317 * service.
318 *
319 * @param context Maintains status for each app layer instance.
320 * @param handle Handle number for the client/service.
321 * @param type Message type.
322 *
323 * @return Pointer to a function that dispatches incoming datagrams for any
324 * particular client/service.
325 */
chppGetDispatchFunction(struct ChppAppState * context,uint8_t handle,enum ChppMessageType type)326 ChppDispatchFunction *chppGetDispatchFunction(struct ChppAppState *context,
327 uint8_t handle,
328 enum ChppMessageType type) {
329 // chppDatagramLenIsOk() has already confirmed that the handle # is valid.
330 // Therefore, no additional checks are necessary for chppClientOfHandle(),
331 // chppServiceOfHandle(), or chppClientServiceContextOfHandle().
332
333 switch (CHPP_APP_GET_MESSAGE_TYPE(type)) {
334 case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: {
335 return chppServiceOfHandle(context, handle)->requestDispatchFunctionPtr;
336 break;
337 }
338 case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: {
339 struct ChppClientState *clientState =
340 (struct ChppClientState *)chppClientServiceContextOfHandle(
341 context, handle, type);
342 if (clientState->openState == CHPP_OPEN_STATE_CLOSED) {
343 CHPP_LOGE("RX service response but client closed");
344 } else {
345 return chppClientOfHandle(context, handle)->responseDispatchFunctionPtr;
346 }
347 break;
348 }
349 case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
350 return chppServiceOfHandle(context, handle)
351 ->notificationDispatchFunctionPtr;
352 break;
353 }
354 case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
355 struct ChppClientState *clientState =
356 (struct ChppClientState *)chppClientServiceContextOfHandle(
357 context, handle, type);
358 if (clientState->openState == CHPP_OPEN_STATE_CLOSED) {
359 CHPP_LOGE("RX service notification but client closed");
360 } else {
361 return chppClientOfHandle(context, handle)
362 ->notificationDispatchFunctionPtr;
363 }
364 break;
365 }
366 }
367
368 return NULL;
369 }
370
371 /**
372 * Returns the reset notification function pointer of a particular negotiated
373 * client. The function pointer will be set to null by clients that do not need
374 * or support a reset notification.
375 *
376 * @param context Maintains status for each app layer instance.
377 * @param index Index of the registered client.
378 *
379 * @return Pointer to the reset notification function.
380 */
chppGetClientResetNotifierFunction(struct ChppAppState * context,uint8_t index)381 ChppNotifierFunction *chppGetClientResetNotifierFunction(
382 struct ChppAppState *context, uint8_t index) {
383 return context->registeredClients[index]->resetNotifierFunctionPtr;
384 }
385
386 /**
387 * Returns the reset function pointer of a particular registered service. The
388 * function pointer will be set to null by services that do not need or support
389 * a reset notification.
390 *
391 * @param context Maintains status for each app layer instance.
392 * @param index Index of the registered service.
393 *
394 * @return Pointer to the reset function.
395 */
chppGetServiceResetNotifierFunction(struct ChppAppState * context,uint8_t index)396 ChppNotifierFunction *chppGetServiceResetNotifierFunction(
397 struct ChppAppState *context, uint8_t index) {
398 return context->registeredServices[index]->resetNotifierFunctionPtr;
399 }
400
401 /**
402 * Returns a pointer to the ChppService struct of the service matched to a
403 * negotiated handle. Returns null if a service doesn't exist for the handle.
404 *
405 * @param context Maintains status for each app layer instance.
406 * @param handle Handle number.
407 *
408 * @return Pointer to the ChppService struct of a particular service handle.
409 */
chppServiceOfHandle(struct ChppAppState * context,uint8_t handle)410 static inline const struct ChppService *chppServiceOfHandle(
411 struct ChppAppState *context, uint8_t handle) {
412 uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
413 if (serviceIndex < context->registeredServiceCount) {
414 return context->registeredServices[serviceIndex];
415 }
416
417 return NULL;
418 }
419
420 /**
421 * Returns a pointer to the ChppClient struct of the client matched to a
422 * negotiated handle. Returns null if a client doesn't exist for the handle.
423 *
424 * @param context Maintains status for each app layer instance.
425 * @param handle Handle number.
426 *
427 * @return Pointer to the ChppClient struct matched to a particular handle.
428 */
chppClientOfHandle(struct ChppAppState * context,uint8_t handle)429 static inline const struct ChppClient *chppClientOfHandle(
430 struct ChppAppState *context, uint8_t handle) {
431 uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
432 if (serviceIndex < context->discoveredServiceCount) {
433 uint8_t clientIndex = context->clientIndexOfServiceIndex[serviceIndex];
434 if (clientIndex < context->registeredClientCount) {
435 return context->registeredClients[clientIndex];
436 }
437 }
438
439 return NULL;
440 }
441
442 /**
443 * Returns a pointer to the service struct of a particular negotiated service
444 * handle.
445 * It is up to the caller to ensure the handle number is valid.
446 *
447 * @param context Maintains status for each app layer instance.
448 * @param handle Handle number for the service.
449 *
450 * @return Pointer to the context struct of the service.
451 */
chppServiceContextOfHandle(struct ChppAppState * context,uint8_t handle)452 static inline void *chppServiceContextOfHandle(struct ChppAppState *context,
453 uint8_t handle) {
454 CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) <
455 context->registeredServiceCount);
456 return context
457 ->registeredServiceContexts[CHPP_SERVICE_INDEX_OF_HANDLE(handle)];
458 }
459
460 /**
461 * Returns a pointer to the client struct of a particular negotiated client
462 * handle.
463 * It is up to the caller to ensure the handle number is valid.
464 *
465 * @param context Maintains status for each app layer instance.
466 * @param handle Handle number for the service.
467 *
468 * @return Pointer to the ChppService struct of the client.
469 */
chppClientContextOfHandle(struct ChppAppState * context,uint8_t handle)470 static inline void *chppClientContextOfHandle(struct ChppAppState *context,
471 uint8_t handle) {
472 CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) <
473 context->registeredClientCount);
474 return context
475 ->registeredClientContexts[context->clientIndexOfServiceIndex
476 [CHPP_SERVICE_INDEX_OF_HANDLE(handle)]];
477 }
478
479 /**
480 * Returns a pointer to the client/service struct of a particular negotiated
481 * client/service handle.
482 * It is up to the caller to ensure the handle number is valid.
483 *
484 * @param appContext Maintains status for each app layer instance.
485 * @param handle Handle number for the service.
486 * @param type Message type (indicates if this is for a client or service).
487 *
488 * @return Pointer to the client/service struct of the service handle.
489 */
chppClientServiceContextOfHandle(struct ChppAppState * appContext,uint8_t handle,enum ChppMessageType type)490 static void *chppClientServiceContextOfHandle(struct ChppAppState *appContext,
491 uint8_t handle,
492 enum ChppMessageType type) {
493 switch (CHPP_APP_GET_MESSAGE_TYPE(type)) {
494 case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
495 case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
496 return chppServiceContextOfHandle(appContext, handle);
497 break;
498 }
499 case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
500 case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
501 return chppClientContextOfHandle(appContext, handle);
502 break;
503 }
504 default: {
505 CHPP_LOGE("Unknown type=0x%" PRIx8 " (H#%" PRIu8 ")", type, handle);
506 return NULL;
507 }
508 }
509 }
510
511 /**
512 * Processes a received datagram that is determined to be for a predefined CHPP
513 * service. Responds with an error if unsuccessful.
514 *
515 * @param context Maintains status for each app layer instance.
516 * @param buf Input data. Cannot be null.
517 * @param len Length of input data in bytes.
518 */
chppProcessPredefinedHandleDatagram(struct ChppAppState * context,uint8_t * buf,size_t len)519 static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context,
520 uint8_t *buf, size_t len) {
521 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
522 bool success = true;
523
524 switch (CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type)) {
525 case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: {
526 success = chppProcessPredefinedClientRequest(context, buf, len);
527 break;
528 }
529 case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
530 success = chppProcessPredefinedClientNotification(context, buf, len);
531 break;
532 }
533 case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: {
534 success = chppProcessPredefinedServiceResponse(context, buf, len);
535 break;
536 }
537 case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
538 success = chppProcessPredefinedServiceNotification(context, buf, len);
539 break;
540 }
541 default: {
542 success = false;
543 }
544 }
545
546 if (success == false) {
547 CHPP_LOGE("H#%" PRIu8 " undefined msg type=0x%" PRIx8 " (len=%" PRIuSIZE
548 ", ID=%" PRIu8 ")",
549 rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
550 chppEnqueueTxErrorDatagram(context->transportContext,
551 CHPP_TRANSPORT_ERROR_APPLAYER);
552 }
553 }
554
555 /**
556 * Processes a received datagram that is determined to be for a negotiated CHPP
557 * client or service. Responds with an error if unsuccessful.
558 *
559 * @param context Maintains status for each app layer instance.
560 * @param buf Input data. Cannot be null.
561 * @param len Length of input data in bytes.
562 */
chppProcessNegotiatedHandleDatagram(struct ChppAppState * context,uint8_t * buf,size_t len)563 static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context,
564 uint8_t *buf, size_t len) {
565 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
566 enum ChppMessageType messageType = CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type);
567
568 void *clientServiceContext =
569 chppClientServiceContextOfHandle(context, rxHeader->handle, messageType);
570 if (clientServiceContext == NULL) {
571 CHPP_LOGE("H#%" PRIu8 " missing ctx (msg=0x%" PRIx8 " len=%" PRIuSIZE
572 ", ID=%" PRIu8 ")",
573 rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
574 chppEnqueueTxErrorDatagram(context->transportContext,
575 CHPP_TRANSPORT_ERROR_APPLAYER);
576 CHPP_DEBUG_ASSERT(false);
577
578 } else {
579 ChppDispatchFunction *dispatchFunc =
580 chppGetDispatchFunction(context, rxHeader->handle, messageType);
581 if (dispatchFunc == NULL) {
582 CHPP_LOGE("H#%" PRIu8 " unsupported msg=0x%" PRIx8 " (len=%" PRIuSIZE
583 ", ID=%" PRIu8 ")",
584 rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
585 chppEnqueueTxErrorDatagram(context->transportContext,
586 CHPP_TRANSPORT_ERROR_APPLAYER);
587
588 } else {
589 // All good. Dispatch datagram and possibly notify a waiting client
590
591 enum ChppAppErrorCode error =
592 dispatchFunc(clientServiceContext, buf, len);
593 if (error != CHPP_APP_ERROR_NONE) {
594 CHPP_LOGE("RX dispatch err=0x%" PRIx16 " H#%" PRIu8 " type=0x%" PRIx8
595 " ID=%" PRIu8 " cmd=0x%" PRIx16 " len=%" PRIuSIZE,
596 error, rxHeader->handle, rxHeader->type,
597 rxHeader->transaction, rxHeader->command, len);
598
599 // Only client requests require a dispatch failure response.
600 if (messageType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) {
601 struct ChppAppHeader *response =
602 chppAllocServiceResponseFixed(rxHeader, struct ChppAppHeader);
603 if (response == NULL) {
604 CHPP_LOG_OOM();
605 } else {
606 response->error = (uint8_t)error;
607 chppEnqueueTxDatagramOrFail(context->transportContext, response,
608 sizeof(*response));
609 }
610 }
611 } else if (messageType == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE) {
612 // Datagram is a service response. Check for synchronous operation and
613 // notify waiting client if needed.
614
615 struct ChppClientState *clientState =
616 (struct ChppClientState *)clientServiceContext;
617 chppMutexLock(&clientState->responseMutex);
618 clientState->responseReady = true;
619 CHPP_LOGD(
620 "Finished dispatching a service response. Notifying a potential "
621 "synchronous client");
622 chppConditionVariableSignal(&clientState->responseCondVar);
623 chppMutexUnlock(&clientState->responseMutex);
624 }
625 }
626 }
627 }
628
629 /************************************************
630 * Public Functions
631 ***********************************************/
632
chppAppInit(struct ChppAppState * appContext,struct ChppTransportState * transportContext)633 void chppAppInit(struct ChppAppState *appContext,
634 struct ChppTransportState *transportContext) {
635 // Default initialize all service/clients
636 struct ChppClientServiceSet set;
637 memset(&set, 0xff, sizeof(set)); // set all bits to 1
638
639 chppAppInitWithClientServiceSet(appContext, transportContext, set);
640 }
641
chppAppInitWithClientServiceSet(struct ChppAppState * appContext,struct ChppTransportState * transportContext,struct ChppClientServiceSet clientServiceSet)642 void chppAppInitWithClientServiceSet(
643 struct ChppAppState *appContext,
644 struct ChppTransportState *transportContext,
645 struct ChppClientServiceSet clientServiceSet) {
646 CHPP_NOT_NULL(appContext);
647
648 CHPP_LOGD("App Init");
649
650 memset(appContext, 0, sizeof(*appContext));
651
652 appContext->clientServiceSet = clientServiceSet;
653 appContext->transportContext = transportContext;
654 appContext->nextRequestTimeoutNs = CHPP_TIME_MAX;
655
656 chppPalSystemApiInit(appContext);
657
658 #ifdef CHPP_SERVICE_ENABLED
659 chppRegisterCommonServices(appContext);
660 #endif
661
662 #ifdef CHPP_CLIENT_ENABLED
663 chppRegisterCommonClients(appContext);
664 chppInitBasicClients(appContext);
665 #endif
666 }
667
chppAppDeinit(struct ChppAppState * appContext)668 void chppAppDeinit(struct ChppAppState *appContext) {
669 CHPP_LOGD("App deinit");
670
671 #ifdef CHPP_CLIENT_ENABLED
672 chppDeinitMatchedClients(appContext);
673 chppDeinitBasicClients(appContext);
674 chppDeregisterCommonClients(appContext);
675 #endif
676
677 #ifdef CHPP_SERVICE_ENABLED
678 chppDeregisterCommonServices(appContext);
679 #endif
680
681 chppPalSystemApiDeinit(appContext);
682 }
683
chppAppProcessRxDatagram(struct ChppAppState * context,uint8_t * buf,size_t len)684 void chppAppProcessRxDatagram(struct ChppAppState *context, uint8_t *buf,
685 size_t len) {
686 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
687
688 if (len == 0) {
689 CHPP_DEBUG_ASSERT_LOG(false, "App rx w/ len 0");
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
chppAppShortResponseErrorHandler(uint8_t * buf,size_t len,const char * responseName)800 uint8_t chppAppShortResponseErrorHandler(uint8_t *buf, size_t len,
801 const char *responseName) {
802 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
803 uint8_t result = CHRE_ERROR;
804 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
805
806 if (rxHeader->error == CHPP_APP_ERROR_NONE) {
807 CHPP_LOGE("%s resp short len=%" PRIuSIZE, responseName, len);
808 } else {
809 CHPP_LOGI("%s resp short len=%" PRIuSIZE, responseName, len);
810 result = chppAppErrorToChreError(rxHeader->error);
811 }
812
813 return result;
814 }
815