You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
799 lines
27 KiB
799 lines
27 KiB
/*
|
|
* 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;
|
|
}
|
|
}
|
|
}
|