| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "chpp/app.h" |
| |
| #include <inttypes.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "chpp/clients.h" |
| #include "chpp/clients/discovery.h" |
| #ifdef CHPP_CLIENT_ENABLED_LOOPBACK |
| #include "chpp/clients/loopback.h" |
| #endif |
| #ifdef CHPP_CLIENT_ENABLED_TIMESYNC |
| #include "chpp/clients/timesync.h" |
| #endif |
| #include "chpp/log.h" |
| #include "chpp/macros.h" |
| #include "chpp/notifier.h" |
| #include "chpp/pal_api.h" |
| #include "chpp/services.h" |
| #include "chpp/services/discovery.h" |
| #include "chpp/services/loopback.h" |
| #include "chpp/services/nonhandle.h" |
| #include "chpp/services/timesync.h" |
| #include "chre_api/chre/common.h" |
| |
| /************************************************ |
| * Prototypes |
| ***********************************************/ |
| |
| static bool chppProcessPredefinedClientRequest(struct ChppAppState *context, |
| uint8_t *buf, size_t len); |
| static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context, |
| uint8_t *buf, size_t len); |
| static bool chppProcessPredefinedClientNotification( |
| struct ChppAppState *context, uint8_t *buf, size_t len); |
| static bool chppProcessPredefinedServiceNotification( |
| struct ChppAppState *context, uint8_t *buf, size_t len); |
| |
| static bool chppDatagramLenIsOk(struct ChppAppState *context, |
| struct ChppAppHeader *rxHeader, size_t len); |
| ChppDispatchFunction *chppGetDispatchFunction(struct ChppAppState *context, |
| uint8_t handle, |
| enum ChppMessageType type); |
| ChppNotifierFunction *chppGetClientResetNotifierFunction( |
| struct ChppAppState *context, uint8_t index); |
| ChppNotifierFunction *chppGetServiceResetNotifierFunction( |
| struct ChppAppState *context, uint8_t index); |
| static inline const struct ChppService *chppServiceOfHandle( |
| struct ChppAppState *appContext, uint8_t handle); |
| static inline const struct ChppClient *chppClientOfHandle( |
| struct ChppAppState *appContext, uint8_t handle); |
| static inline void *chppServiceContextOfHandle(struct ChppAppState *appContext, |
| uint8_t handle); |
| static inline void *chppClientContextOfHandle(struct ChppAppState *appContext, |
| uint8_t handle); |
| static void *chppClientServiceContextOfHandle(struct ChppAppState *appContext, |
| uint8_t handle, |
| enum ChppMessageType type); |
| |
| static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context, |
| uint8_t *buf, size_t len); |
| static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context, |
| uint8_t *buf, size_t len); |
| |
| /************************************************ |
| * Private Functions |
| ***********************************************/ |
| |
| /** |
| * Processes a client request that is determined to be for a predefined CHPP |
| * service. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param buf Input data. Cannot be null. |
| * @param len Length of input data in bytes. |
| * |
| * @return False if handle is invalid. True otherwise. |
| */ |
| static bool chppProcessPredefinedClientRequest(struct ChppAppState *context, |
| uint8_t *buf, size_t len) { |
| struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; |
| bool handleValid = true; |
| bool dispatchResult = true; |
| |
| switch (rxHeader->handle) { |
| case CHPP_HANDLE_LOOPBACK: { |
| dispatchResult = chppDispatchLoopbackClientRequest(context, buf, len); |
| break; |
| } |
| |
| case CHPP_HANDLE_TIMESYNC: { |
| dispatchResult = chppDispatchTimesyncClientRequest(context, buf, len); |
| break; |
| } |
| |
| case CHPP_HANDLE_DISCOVERY: { |
| dispatchResult = chppDispatchDiscoveryClientRequest(context, buf, len); |
| break; |
| } |
| |
| default: { |
| handleValid = false; |
| } |
| } |
| |
| if (dispatchResult == false) { |
| CHPP_LOGE("H#%" PRIu8 " unknown request. cmd=%#x, ID=%" PRIu8, |
| rxHeader->handle, rxHeader->command, rxHeader->transaction); |
| } |
| |
| return handleValid; |
| } |
| |
| /** |
| * Processes a service response that is determined to be for a predefined CHPP |
| * client. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param buf Input data. Cannot be null. |
| * @param len Length of input data in bytes. |
| * |
| * @return False if handle is invalid. True otherwise. |
| */ |
| static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context, |
| uint8_t *buf, size_t len) { |
| struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; |
| bool handleValid = true; |
| bool dispatchResult = true; |
| |
| switch (rxHeader->handle) { |
| #ifdef CHPP_CLIENT_ENABLED_LOOPBACK |
| case CHPP_HANDLE_LOOPBACK: { |
| dispatchResult = chppDispatchLoopbackServiceResponse(context, buf, len); |
| break; |
| } |
| #endif // CHPP_CLIENT_ENABLED_LOOPBACK |
| |
| #ifdef CHPP_CLIENT_ENABLED_TIMESYNC |
| case CHPP_HANDLE_TIMESYNC: { |
| dispatchResult = chppDispatchTimesyncServiceResponse(context, buf, len); |
| break; |
| } |
| #endif // CHPP_CLIENT_ENABLED_TIMESYNC |
| |
| #ifdef CHPP_CLIENT_ENABLED_DISCOVERY |
| case CHPP_HANDLE_DISCOVERY: { |
| dispatchResult = chppDispatchDiscoveryServiceResponse(context, buf, len); |
| break; |
| } |
| #endif // CHPP_CLIENT_ENABLED_DISCOVERY |
| |
| default: { |
| handleValid = false; |
| } |
| } |
| |
| if (dispatchResult == false) { |
| CHPP_LOGE("H#%" PRIu8 " unknown response. cmd=%#x, ID=%" PRIu8 |
| ", len=%" PRIuSIZE, |
| rxHeader->handle, rxHeader->command, rxHeader->transaction, len); |
| } |
| |
| return handleValid; |
| } |
| |
| /** |
| * Processes a client notification that is determined to be for a predefined |
| * CHPP service. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param buf Input data. Cannot be null. |
| * @param len Length of input data in bytes. |
| * |
| * @return False if handle is invalid. True otherwise. |
| */ |
| static bool chppProcessPredefinedClientNotification( |
| struct ChppAppState *context, uint8_t *buf, size_t len) { |
| struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; |
| bool handleValid = true; |
| bool dispatchResult = true; |
| |
| // No predefined services support these yet |
| handleValid = false; |
| |
| UNUSED_VAR(context); |
| UNUSED_VAR(len); |
| UNUSED_VAR(rxHeader); |
| UNUSED_VAR(dispatchResult); |
| |
| return handleValid; |
| } |
| |
| /** |
| * Processes a service notification that is determined to be for a predefined |
| * CHPP client. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param buf Input data. Cannot be null. |
| * @param len Length of input data in bytes. |
| * |
| * @return False if handle is invalid. True otherwise. |
| */ |
| static bool chppProcessPredefinedServiceNotification( |
| struct ChppAppState *context, uint8_t *buf, size_t len) { |
| struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; |
| bool handleValid = true; |
| bool dispatchResult = true; |
| |
| // No predefined clients support these yet |
| handleValid = false; |
| |
| UNUSED_VAR(context); |
| UNUSED_VAR(len); |
| UNUSED_VAR(rxHeader); |
| UNUSED_VAR(dispatchResult); |
| |
| return handleValid; |
| } |
| |
| /** |
| * Verifies if the length of a Rx Datagram from the transport layer is |
| * sufficient for the associated service. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param rxHeader The pointer to the datagram RX header. |
| * @param len Length of the datagram in bytes. |
| * |
| * @return true if length is ok. |
| */ |
| static bool chppDatagramLenIsOk(struct ChppAppState *context, |
| struct ChppAppHeader *rxHeader, size_t len) { |
| size_t minLen = SIZE_MAX; |
| uint8_t handle = rxHeader->handle; |
| |
| if (handle < CHPP_HANDLE_NEGOTIATED_RANGE_START) { // Predefined |
| switch (handle) { |
| case CHPP_HANDLE_NONE: |
| minLen = sizeof_member(struct ChppAppHeader, handle); |
| break; |
| |
| case CHPP_HANDLE_LOOPBACK: |
| minLen = sizeof_member(struct ChppAppHeader, handle) + |
| sizeof_member(struct ChppAppHeader, type); |
| break; |
| |
| case CHPP_HANDLE_TIMESYNC: |
| case CHPP_HANDLE_DISCOVERY: |
| minLen = sizeof(struct ChppAppHeader); |
| break; |
| |
| default: |
| CHPP_LOGE("Invalid H#%" PRIu8, handle); |
| } |
| |
| } else { // Negotiated |
| enum ChppMessageType messageType = |
| CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type); |
| |
| switch (messageType) { |
| case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: |
| case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: { |
| const struct ChppService *service = |
| chppServiceOfHandle(context, handle); |
| if (service != NULL) { |
| minLen = service->minLength; |
| } |
| break; |
| } |
| case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: |
| case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: { |
| const struct ChppClient *client = chppClientOfHandle(context, handle); |
| if (client != NULL) { |
| minLen = client->minLength; |
| } |
| break; |
| } |
| default: { |
| break; |
| } |
| } |
| |
| if (minLen == SIZE_MAX) { |
| CHPP_LOGE("Invalid type=%d or H#%" PRIu8, messageType, handle); |
| } |
| } |
| |
| if ((len < minLen) && (minLen != SIZE_MAX)) { |
| CHPP_LOGE("Datagram len=%" PRIuSIZE " < %" PRIuSIZE " for H#%" PRIu8, len, |
| minLen, handle); |
| } |
| return (len >= minLen) && (minLen != SIZE_MAX); |
| } |
| |
| /** |
| * Returns the dispatch function of a particular negotiated client/service |
| * handle and message type. This shall be null if it is unsupported by the |
| * service. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param handle Handle number for the client/service. |
| * @param type Message type. |
| * |
| * @return Pointer to a function that dispatches incoming datagrams for any |
| * particular client/service. |
| */ |
| ChppDispatchFunction *chppGetDispatchFunction(struct ChppAppState *context, |
| uint8_t handle, |
| enum ChppMessageType type) { |
| // chppDatagramLenIsOk() has already confirmed that the handle # is valid. |
| // Therefore, no additional checks are necessary for chppClientOfHandle(), |
| // chppServiceOfHandle(), or chppClientServiceContextOfHandle(). |
| |
| switch (CHPP_APP_GET_MESSAGE_TYPE(type)) { |
| case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: { |
| return chppServiceOfHandle(context, handle)->requestDispatchFunctionPtr; |
| break; |
| } |
| case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: { |
| struct ChppClientState *clientState = |
| (struct ChppClientState *)chppClientServiceContextOfHandle( |
| context, handle, type); |
| if (clientState->openState == CHPP_OPEN_STATE_CLOSED) { |
| CHPP_LOGE("Rx service response but client closed"); |
| } else { |
| return chppClientOfHandle(context, handle)->responseDispatchFunctionPtr; |
| } |
| break; |
| } |
| case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: { |
| return chppServiceOfHandle(context, handle) |
| ->notificationDispatchFunctionPtr; |
| break; |
| } |
| case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: { |
| struct ChppClientState *clientState = |
| (struct ChppClientState *)chppClientServiceContextOfHandle( |
| context, handle, type); |
| if (clientState->openState == CHPP_OPEN_STATE_CLOSED) { |
| CHPP_LOGE("Rx service notification but client closed"); |
| } else { |
| return chppClientOfHandle(context, handle) |
| ->notificationDispatchFunctionPtr; |
| } |
| break; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * Returns the reset notification function pointer of a particular negotiated |
| * client. The function pointer will be set to null by clients that do not need |
| * or support a reset notification. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param index Index of the registered client. |
| * |
| * @return Pointer to the reset notification function. |
| */ |
| ChppNotifierFunction *chppGetClientResetNotifierFunction( |
| struct ChppAppState *context, uint8_t index) { |
| return context->registeredClients[index]->resetNotifierFunctionPtr; |
| } |
| |
| /** |
| * Returns the reset function pointer of a particular registered service. The |
| * function pointer will be set to null by services that do not need or support |
| * a reset notification. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param index Index of the registered service. |
| * |
| * @return Pointer to the reset function. |
| */ |
| ChppNotifierFunction *chppGetServiceResetNotifierFunction( |
| struct ChppAppState *context, uint8_t index) { |
| return context->registeredServices[index]->resetNotifierFunctionPtr; |
| } |
| |
| /** |
| * Returns a pointer to the ChppService struct of the service matched to a |
| * negotiated handle. Returns null if a service doesn't exist for the handle. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param handle Handle number. |
| * |
| * @return Pointer to the ChppService struct of a particular service handle. |
| */ |
| static inline const struct ChppService *chppServiceOfHandle( |
| struct ChppAppState *context, uint8_t handle) { |
| uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle); |
| if (serviceIndex < context->registeredServiceCount) { |
| return context->registeredServices[serviceIndex]; |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * Returns a pointer to the ChppClient struct of the client matched to a |
| * negotiated handle. Returns null if a client doesn't exist for the handle. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param handle Handle number. |
| * |
| * @return Pointer to the ChppClient struct matched to a particular handle. |
| */ |
| static inline const struct ChppClient *chppClientOfHandle( |
| struct ChppAppState *context, uint8_t handle) { |
| uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle); |
| if (serviceIndex < context->discoveredServiceCount) { |
| uint8_t clientIndex = context->clientIndexOfServiceIndex[serviceIndex]; |
| if (clientIndex < context->registeredClientCount) { |
| return context->registeredClients[clientIndex]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * Returns a pointer to the service struct of a particular negotiated service |
| * handle. |
| * It is up to the caller to ensure the handle number is valid. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param handle Handle number for the service. |
| * |
| * @return Pointer to the context struct of the service. |
| */ |
| static inline void *chppServiceContextOfHandle(struct ChppAppState *context, |
| uint8_t handle) { |
| CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) < |
| context->registeredServiceCount); |
| return context |
| ->registeredServiceContexts[CHPP_SERVICE_INDEX_OF_HANDLE(handle)]; |
| } |
| |
| /** |
| * Returns a pointer to the client struct of a particular negotiated client |
| * handle. |
| * It is up to the caller to ensure the handle number is valid. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param handle Handle number for the service. |
| * |
| * @return Pointer to the ChppService struct of the client. |
| */ |
| static inline void *chppClientContextOfHandle(struct ChppAppState *context, |
| uint8_t handle) { |
| CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) < |
| context->registeredClientCount); |
| return context |
| ->registeredClientContexts[context->clientIndexOfServiceIndex |
| [CHPP_SERVICE_INDEX_OF_HANDLE(handle)]]; |
| } |
| |
| /** |
| * Returns a pointer to the client/service struct of a particular negotiated |
| * client/service handle. |
| * It is up to the caller to ensure the handle number is valid. |
| * |
| * @param appContext Maintains status for each app layer instance. |
| * @param handle Handle number for the service. |
| * @param type Message type (indicates if this is for a client or service). |
| * |
| * @return Pointer to the client/service struct of the service handle. |
| */ |
| static void *chppClientServiceContextOfHandle(struct ChppAppState *appContext, |
| uint8_t handle, |
| enum ChppMessageType type) { |
| switch (CHPP_APP_GET_MESSAGE_TYPE(type)) { |
| case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: |
| case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: { |
| return chppServiceContextOfHandle(appContext, handle); |
| break; |
| } |
| case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: |
| case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: { |
| return chppClientContextOfHandle(appContext, handle); |
| break; |
| } |
| default: { |
| CHPP_LOGE("Unknown type=0x%" PRIx8 " (H#%" PRIu8 ")", type, handle); |
| return NULL; |
| } |
| } |
| } |
| |
| /** |
| * Processes a received datagram that is determined to be for a predefined CHPP |
| * service. Responds with an error if unsuccessful. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param buf Input data. Cannot be null. |
| * @param len Length of input data in bytes. |
| */ |
| static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context, |
| uint8_t *buf, size_t len) { |
| struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; |
| bool success = true; |
| |
| switch (CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type)) { |
| case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: { |
| success = chppProcessPredefinedClientRequest(context, buf, len); |
| break; |
| } |
| case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: { |
| success = chppProcessPredefinedClientNotification(context, buf, len); |
| break; |
| } |
| case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: { |
| success = chppProcessPredefinedServiceResponse(context, buf, len); |
| break; |
| } |
| case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: { |
| success = chppProcessPredefinedServiceNotification(context, buf, len); |
| break; |
| } |
| default: { |
| success = false; |
| } |
| } |
| |
| if (success == false) { |
| CHPP_LOGE("H#%" PRIu8 " undefined msg type=0x%" PRIx8 " (len=%" PRIuSIZE |
| ", ID=%" PRIu8 ")", |
| rxHeader->handle, rxHeader->type, len, rxHeader->transaction); |
| chppEnqueueTxErrorDatagram(context->transportContext, |
| CHPP_TRANSPORT_ERROR_APPLAYER); |
| } |
| } |
| |
| /** |
| * Processes a received datagram that is determined to be for a negotiated CHPP |
| * client or service. Responds with an error if unsuccessful. |
| * |
| * @param context Maintains status for each app layer instance. |
| * @param buf Input data. Cannot be null. |
| * @param len Length of input data in bytes. |
| */ |
| static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context, |
| uint8_t *buf, size_t len) { |
| struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; |
| enum ChppMessageType messageType = CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type); |
| |
| void *clientServiceContext = |
| chppClientServiceContextOfHandle(context, rxHeader->handle, messageType); |
| if (clientServiceContext == NULL) { |
| CHPP_LOGE("H#%" PRIu8 " missing ctx (msg=0x%" PRIx8 " len=%" PRIuSIZE |
| ", ID=%" PRIu8 ")", |
| rxHeader->handle, rxHeader->type, len, rxHeader->transaction); |
| chppEnqueueTxErrorDatagram(context->transportContext, |
| CHPP_TRANSPORT_ERROR_APPLAYER); |
| CHPP_DEBUG_ASSERT(false); |
| |
| } else { |
| ChppDispatchFunction *dispatchFunc = |
| chppGetDispatchFunction(context, rxHeader->handle, messageType); |
| if (dispatchFunc == NULL) { |
| CHPP_LOGE("H#%" PRIu8 " unsupported msg=0x%" PRIx8 " (len=%" PRIuSIZE |
| ", ID=%" PRIu8 ")", |
| rxHeader->handle, rxHeader->type, len, rxHeader->transaction); |
| chppEnqueueTxErrorDatagram(context->transportContext, |
| CHPP_TRANSPORT_ERROR_APPLAYER); |
| |
| } else { |
| // All good. Dispatch datagram and possibly notify a waiting client |
| |
| enum ChppAppErrorCode error = |
| dispatchFunc(clientServiceContext, buf, len); |
| if (error != CHPP_APP_ERROR_NONE) { |
| CHPP_LOGE("RX dispatch err=0x%" PRIx16 " H#%" PRIu8 " type=0x%" PRIx8 |
| " ID=%" PRIu8 " cmd=0x%" PRIx16 " len=%" PRIuSIZE, |
| error, rxHeader->handle, rxHeader->type, |
| rxHeader->transaction, rxHeader->command, len); |
| |
| // Only client requests require a dispatch failure response. |
| if (messageType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) { |
| struct ChppAppHeader *response = |
| chppAllocServiceResponseFixed(rxHeader, struct ChppAppHeader); |
| if (response == NULL) { |
| CHPP_LOG_OOM(); |
| } else { |
| response->error = (uint8_t)error; |
| chppEnqueueTxDatagramOrFail(context->transportContext, response, |
| sizeof(*response)); |
| } |
| } |
| } else if (messageType == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE) { |
| // Datagram is a service response. Check for synchronous operation and |
| // notify waiting client if needed. |
| |
| struct ChppClientState *clientState = |
| (struct ChppClientState *)clientServiceContext; |
| chppMutexLock(&clientState->responseMutex); |
| clientState->responseReady = true; |
| CHPP_LOGD( |
| "Finished dispatching a service response. Notifying a potential " |
| "synchronous client"); |
| chppConditionVariableSignal(&clientState->responseCondVar); |
| chppMutexUnlock(&clientState->responseMutex); |
| } |
| } |
| } |
| } |
| |
| /************************************************ |
| * Public Functions |
| ***********************************************/ |
| |
| void chppAppInit(struct ChppAppState *appContext, |
| struct ChppTransportState *transportContext) { |
| // Default initialize all service/clients |
| struct ChppClientServiceSet set; |
| memset(&set, 0xff, sizeof(set)); // set all bits to 1 |
| |
| chppAppInitWithClientServiceSet(appContext, transportContext, set); |
| } |
| |
| void chppAppInitWithClientServiceSet( |
| struct ChppAppState *appContext, |
| struct ChppTransportState *transportContext, |
| struct ChppClientServiceSet clientServiceSet) { |
| CHPP_NOT_NULL(appContext); |
| |
| CHPP_LOGD("App Init"); |
| |
| memset(appContext, 0, sizeof(*appContext)); |
| |
| appContext->clientServiceSet = clientServiceSet; |
| appContext->transportContext = transportContext; |
| appContext->nextRequestTimeoutNs = CHPP_TIME_MAX; |
| |
| chppPalSystemApiInit(appContext); |
| |
| #ifdef CHPP_SERVICE_ENABLED |
| chppRegisterCommonServices(appContext); |
| #endif |
| |
| #ifdef CHPP_CLIENT_ENABLED |
| chppRegisterCommonClients(appContext); |
| chppInitBasicClients(appContext); |
| #endif |
| } |
| |
| void chppAppDeinit(struct ChppAppState *appContext) { |
| CHPP_LOGD("App deinit"); |
| |
| #ifdef CHPP_CLIENT_ENABLED |
| chppDeinitMatchedClients(appContext); |
| chppDeinitBasicClients(appContext); |
| chppDeregisterCommonClients(appContext); |
| #endif |
| |
| #ifdef CHPP_SERVICE_ENABLED |
| chppDeregisterCommonServices(appContext); |
| #endif |
| |
| chppPalSystemApiDeinit(appContext); |
| } |
| |
| void chppAppProcessRxDatagram(struct ChppAppState *context, uint8_t *buf, |
| size_t len) { |
| struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; |
| |
| if (len == 0) { |
| CHPP_LOGE("App rx w/ len 0"); |
| CHPP_DEBUG_ASSERT(false); |
| |
| } else if (len < sizeof(struct ChppAppHeader)) { |
| uint8_t *handle = (uint8_t *)buf; |
| CHPP_LOGD("RX datagram len=%" PRIuSIZE " H#%" PRIu8, len, *handle); |
| |
| } else if (rxHeader->error != CHPP_APP_ERROR_NONE) { |
| CHPP_LOGE("RX datagram len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8 |
| " ID=%" PRIu8 " ERR=%" PRIu8 " cmd=0x%" PRIx16, |
| len, rxHeader->handle, rxHeader->type, rxHeader->transaction, |
| rxHeader->error, rxHeader->command); |
| } else { |
| CHPP_LOGD("RX datagram len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8 |
| " ID=%" PRIu8 " err=%" PRIu8 " cmd=0x%" PRIx16, |
| len, rxHeader->handle, rxHeader->type, rxHeader->transaction, |
| rxHeader->error, rxHeader->command); |
| } |
| |
| if (!chppDatagramLenIsOk(context, rxHeader, len)) { |
| chppEnqueueTxErrorDatagram(context->transportContext, |
| CHPP_TRANSPORT_ERROR_APPLAYER); |
| |
| } else { |
| if (rxHeader->handle == CHPP_HANDLE_NONE) { |
| chppDispatchNonHandle(context, buf, len); |
| |
| } else if (rxHeader->handle < CHPP_HANDLE_NEGOTIATED_RANGE_START) { |
| chppProcessPredefinedHandleDatagram(context, buf, len); |
| |
| } else { |
| chppProcessNegotiatedHandleDatagram(context, buf, len); |
| } |
| } |
| |
| chppDatagramProcessDoneCb(context->transportContext, buf); |
| } |
| |
| void chppAppProcessReset(struct ChppAppState *context) { |
| #ifdef CHPP_CLIENT_ENABLED_DISCOVERY |
| if (!context->isDiscoveryComplete) { |
| chppInitiateDiscovery(context); |
| |
| } else { |
| // Notify matched clients that a reset happened |
| for (uint8_t i = 0; i < context->discoveredServiceCount; i++) { |
| uint8_t clientIndex = context->clientIndexOfServiceIndex[i]; |
| if (clientIndex != CHPP_CLIENT_INDEX_NONE) { |
| // Discovered service has a matched client |
| ChppNotifierFunction *ResetNotifierFunction = |
| chppGetClientResetNotifierFunction(context, clientIndex); |
| |
| CHPP_LOGD("Client #%" PRIu8 " (H#%d) reset notifier found=%d", |
| clientIndex, CHPP_SERVICE_HANDLE_OF_INDEX(i), |
| (ResetNotifierFunction != NULL)); |
| |
| if (ResetNotifierFunction != NULL) { |
| ResetNotifierFunction(context->registeredClientContexts[clientIndex]); |
| } |
| } |
| } |
| } |
| #endif // CHPP_CLIENT_ENABLED_DISCOVERY |
| |
| // Notify registered services that a reset happened |
| for (uint8_t i = 0; i < context->registeredServiceCount; i++) { |
| ChppNotifierFunction *ResetNotifierFunction = |
| chppGetServiceResetNotifierFunction(context, i); |
| |
| CHPP_LOGD("Service #%" PRIu8 " (H#%d) reset notifier found=%d", i, |
| CHPP_SERVICE_HANDLE_OF_INDEX(i), (ResetNotifierFunction != NULL)); |
| |
| if (ResetNotifierFunction != NULL) { |
| ResetNotifierFunction(context->registeredServiceContexts[i]); |
| } |
| } |
| |
| #ifdef CHPP_CLIENT_ENABLED_TIMESYNC |
| // Reinitialize time offset |
| chppTimesyncClientReset(context); |
| #endif |
| } |
| |
| void chppUuidToStr(const uint8_t uuid[CHPP_SERVICE_UUID_LEN], |
| char strOut[CHPP_SERVICE_UUID_STRING_LEN]) { |
| snprintf( |
| strOut, CHPP_SERVICE_UUID_STRING_LEN, |
| "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", |
| uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], |
| uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], |
| uuid[15]); |
| } |
| |
| uint8_t chppAppErrorToChreError(uint8_t chppError) { |
| switch (chppError) { |
| case CHPP_APP_ERROR_NONE: |
| case CHPP_APP_ERROR_INVALID_ARG: |
| case CHPP_APP_ERROR_BUSY: |
| case CHPP_APP_ERROR_OOM: |
| case CHPP_APP_ERROR_UNSUPPORTED: |
| case CHPP_APP_ERROR_TIMEOUT: |
| case CHPP_APP_ERROR_DISABLED: |
| case CHPP_APP_ERROR_RATELIMITED: { |
| // CHRE and CHPP error values are identical in these cases |
| return chppError; |
| } |
| default: { |
| return CHRE_ERROR; |
| } |
| } |
| } |