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