blob: 3b90edff2addf621307c99b9986980c40ec14bec [file] [log] [blame]
/*
* 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;
}
}
}