From 7d339f89ba7480c46fae26078e20199287f07331 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Wed, 29 Jan 2020 10:26:39 +0100 Subject: [PATCH] mesh: implement config client heartbeat publication and subscription msgs [a2580, a2581] --- src/btstack_defines.h | 25 +++++ src/btstack_event.h | 128 +++++++++++++++++++++++++ src/mesh/mesh_configuration_client.c | 136 +++++++++++++++++++++++++++ src/mesh/mesh_configuration_client.h | 48 ++++++++++ src/mesh/mesh_node.h | 13 +++ 5 files changed, 350 insertions(+) diff --git a/src/btstack_defines.h b/src/btstack_defines.h index b95e0d79d..9b7c58178 100644 --- a/src/btstack_defines.h +++ b/src/btstack_defines.h @@ -3117,5 +3117,30 @@ typedef uint8_t sm_key_t[16]; */ #define MESH_SUBEVENT_CONFIGURATION_KEY_REFRESH_PHASE 0x53 +/** + * @format 12111122 + * @param subevent_code + * @param dest + * @param foundation_status + * @param count_log + * @param period_log + * @param ttl + * @param features + * @param netkey_index + */ +#define MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION 0x54 + +/** + * @format 12121111 + * @param subevent_code + * @param dest + * @param foundation_status + * @param source + * @param count_log + * @param period_log + * @param min_hops + * @param max_hops + */ +#define MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_SUBSCRIPTION 0x55 #endif diff --git a/src/btstack_event.h b/src/btstack_event.h index df3344dc0..9e3ad204b 100644 --- a/src/btstack_event.h +++ b/src/btstack_event.h @@ -8701,6 +8701,134 @@ static inline uint8_t mesh_subevent_configuration_key_refresh_phase_get_phase(co return event[8]; } +/** + * @brief Get field dest from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION + * @param event packet + * @return dest + * @note: btstack_type 2 + */ +static inline uint16_t mesh_subevent_configuration_heartbeat_publication_get_dest(const uint8_t * event){ + return little_endian_read_16(event, 3); +} +/** + * @brief Get field foundation_status from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION + * @param event packet + * @return foundation_status + * @note: btstack_type 1 + */ +static inline uint8_t mesh_subevent_configuration_heartbeat_publication_get_foundation_status(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field period_log from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION + * @param event packet + * @return period_log + * @note: btstack_type 1 + */ +static inline uint8_t mesh_subevent_configuration_heartbeat_publication_get_period_log(const uint8_t * event){ + return event[6]; +} +/** + * @brief Get field count_log from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION + * @param event packet + * @return count_log + * @note: btstack_type 1 + */ +static inline uint8_t mesh_subevent_configuration_heartbeat_publication_get_count_log(const uint8_t * event){ + return event[7]; +} +/** + * @brief Get field ttl from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION + * @param event packet + * @return ttl + * @note: btstack_type 1 + */ +static inline uint8_t mesh_subevent_configuration_heartbeat_publication_get_ttl(const uint8_t * event){ + return event[8]; +} +/** + * @brief Get field features from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION + * @param event packet + * @return features + * @note: btstack_type 2 + */ +static inline uint16_t mesh_subevent_configuration_heartbeat_publication_get_features(const uint8_t * event){ + return little_endian_read_16(event, 9); +} +/** + * @brief Get field netkey_index from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION + * @param event packet + * @return netkey_index + * @note: btstack_type 2 + */ +static inline uint16_t mesh_subevent_configuration_heartbeat_publication_get_netkey_index(const uint8_t * event){ + return little_endian_read_16(event, 11); +} + +/** + * @brief Get field dest from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_SUBSCRIPTION + * @param event packet + * @return dest + * @note: btstack_type 2 + */ +static inline uint16_t mesh_subevent_configuration_heartbeat_subscription_get_dest(const uint8_t * event){ + return little_endian_read_16(event, 3); +} +/** + * @brief Get field foundation_status from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_SUBSCRIPTION + * @param event packet + * @return foundation_status + * @note: btstack_type 1 + */ +static inline uint8_t mesh_subevent_configuration_heartbeat_subscription_get_foundation_status(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field source from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_SUBSCRIPTION + * @param event packet + * @return source + * @note: btstack_type 2 + */ +static inline uint16_t mesh_subevent_configuration_heartbeat_subscription_get_source(const uint8_t * event){ + return little_endian_read_16(event, 6); +} +/** + * @brief Get field period_log from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_SUBSCRIPTION + * @param event packet + * @return period_log + * @note: btstack_type 1 + */ +static inline uint8_t mesh_subevent_configuration_heartbeat_subscription_get_period_log(const uint8_t * event){ + return event[8]; +} +/** + * @brief Get field count_log from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_SUBSCRIPTION + * @param event packet + * @return count_log + * @note: btstack_type 1 + */ +static inline uint8_t mesh_subevent_configuration_heartbeat_subscription_get_count_log(const uint8_t * event){ + return event[9]; +} +/** + * @brief Get field min_hops from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_SUBSCRIPTION + * @param event packet + * @return min_hops + * @note: btstack_type 1 + */ +static inline uint8_t mesh_subevent_configuration_heartbeat_subscription_get_min_hops(const uint8_t * event){ + return event[10]; +} +/** + * @brief Get field max_hops from event MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_SUBSCRIPTION + * @param event packet + * @return max_hops + * @note: btstack_type 1 + */ +static inline uint8_t mesh_subevent_configuration_heartbeat_subscription_get_max_hops(const uint8_t * event){ + return event[11]; +} + /* API_END */ diff --git a/src/mesh/mesh_configuration_client.c b/src/mesh/mesh_configuration_client.c index 38544c82b..8f535c4af 100644 --- a/src/mesh/mesh_configuration_client.c +++ b/src/mesh/mesh_configuration_client.c @@ -295,6 +295,20 @@ static const mesh_access_message_t mesh_configuration_client_key_refresh_phase_s MESH_FOUNDATION_OPERATION_KEY_REFRESH_PHASE_SET, "21" }; +static const mesh_access_message_t mesh_configuration_client_heartbeat_publication_get = { + MESH_FOUNDATION_OPERATION_HEARTBEAT_PUBLICATION_GET, "" +}; +static const mesh_access_message_t mesh_configuration_client_heartbeat_publication_set = { + MESH_FOUNDATION_OPERATION_HEARTBEAT_PUBLICATION_SET, "11122" +}; + +static const mesh_access_message_t mesh_configuration_client_heartbeat_subscription_get = { + MESH_FOUNDATION_OPERATION_HEARTBEAT_SUBSCRIPTION_GET, "" +}; +static const mesh_access_message_t mesh_configuration_client_heartbeat_subscription_set = { + MESH_FOUNDATION_OPERATION_HEARTBEAT_SUBSCRIPTION_SET, "21" +}; + static void mesh_configuration_client_send_acknowledged(uint16_t src, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, mesh_pdu_t *pdu, uint32_t ack_opcode){ uint8_t ttl = mesh_foundation_default_ttl_get(); mesh_upper_transport_setup_access_pdu_header(pdu, netkey_index, appkey_index, ttl, src, dest, 0); @@ -790,6 +804,57 @@ uint8_t mesh_configuration_client_send_key_refresh_phase_set(mesh_model_t * mesh return ERROR_CODE_SUCCESS; } +uint8_t mesh_configuration_client_send_heartbeat_publication_get(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index){ + uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index); + if (status != ERROR_CODE_SUCCESS) return status; + + mesh_network_pdu_t * transport_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_heartbeat_publication_get); + if (!transport_pdu) return BTSTACK_MEMORY_ALLOC_FAILED; + + mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) transport_pdu, MESH_FOUNDATION_OPERATION_HEARTBEAT_PUBLICATION_STATUS); + return ERROR_CODE_SUCCESS; +} + +uint8_t mesh_configuration_client_send_heartbeat_publication_set(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, mesh_heartbeat_publication_state_t publication_state){ + uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index); + if (status != ERROR_CODE_SUCCESS) return status; + + mesh_network_pdu_t * transport_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_heartbeat_publication_set, + publication_state.count_log, + publication_state.period_log, + publication_state.ttl, + publication_state.features, + publication_state.netkey_index); + + if (!transport_pdu) return BTSTACK_MEMORY_ALLOC_FAILED; + + mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) transport_pdu, MESH_FOUNDATION_OPERATION_HEARTBEAT_PUBLICATION_STATUS); + return ERROR_CODE_SUCCESS; +} + +uint8_t mesh_configuration_client_send_heartbeat_subscription_get(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index){ + uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index); + if (status != ERROR_CODE_SUCCESS) return status; + + mesh_network_pdu_t * transport_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_heartbeat_subscription_get); + if (!transport_pdu) return BTSTACK_MEMORY_ALLOC_FAILED; + + mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) transport_pdu, MESH_FOUNDATION_OPERATION_HEARTBEAT_SUBSCRIPTION_STATUS); + return ERROR_CODE_SUCCESS; +} + +uint8_t mesh_configuration_client_send_heartbeat_subscription_set(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint16_t source, uint8_t period_log){ + uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index); + if (status != ERROR_CODE_SUCCESS) return status; + + mesh_network_pdu_t * transport_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_heartbeat_subscription_set, source, period_log); + if (!transport_pdu) return BTSTACK_MEMORY_ALLOC_FAILED; + + mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) transport_pdu, MESH_FOUNDATION_OPERATION_HEARTBEAT_SUBSCRIPTION_STATUS); + return ERROR_CODE_SUCCESS; +} + + // Model Operations static void mesh_configuration_client_composition_data_status_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ // Composition Data has variable of element descriptions, with two lists of model lists @@ -1291,6 +1356,75 @@ static void mesh_configuration_client_key_refresh_phase_handler(mesh_model_t *me mesh_access_message_processed(pdu); } +static void mesh_configuration_client_heartbeat_publication_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ + mesh_access_parser_state_t parser; + mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); + uint8_t status = mesh_access_parser_get_u8(&parser); + uint16_t dest = mesh_access_parser_get_u16(&parser); + uint8_t count_log = mesh_access_parser_get_u8(&parser); + uint8_t period_log = mesh_access_parser_get_u8(&parser); + uint8_t ttl = mesh_access_parser_get_u8(&parser); + uint16_t features = mesh_access_parser_get_u16(&parser); + uint16_t netkey_index = mesh_access_parser_get_u16(&parser); + + if (dest != mesh_pdu_src(pdu)){ + log_info("MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION event, destination differs from mesh_pdu_src"); + } + uint8_t event[13]; + int pos = 0; + event[pos++] = HCI_EVENT_MESH_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION; + // dest + little_endian_store_16(event, pos, mesh_pdu_src(pdu)); + pos += 2; + event[pos++] = status; + event[pos++] = count_log; + event[pos++] = period_log; + event[pos++] = ttl; + little_endian_store_16(event, pos, features); + pos += 2; + little_endian_store_16(event, pos, netkey_index); + pos += 2; + (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos); + mesh_access_message_processed(pdu); +} + +static void mesh_configuration_client_heartbeat_subscription_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ + mesh_access_parser_state_t parser; + mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); + uint8_t status = mesh_access_parser_get_u8(&parser); + uint16_t source = mesh_access_parser_get_u16(&parser); + uint16_t dest = mesh_access_parser_get_u16(&parser); + uint8_t period_log = mesh_access_parser_get_u8(&parser); + uint8_t count_log = mesh_access_parser_get_u8(&parser); + uint8_t min_hops = mesh_access_parser_get_u8(&parser); + uint8_t max_hops = mesh_access_parser_get_u8(&parser); + + if (dest != mesh_pdu_src(pdu)){ + log_info("MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_PUBLICATION event, destination differs from mesh_pdu_src"); + } + + uint8_t event[12]; + int pos = 0; + event[pos++] = HCI_EVENT_MESH_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = MESH_SUBEVENT_CONFIGURATION_HEARTBEAT_SUBSCRIPTION; + // dest + little_endian_store_16(event, pos, mesh_pdu_src(pdu)); + pos += 2; + event[pos++] = status; + + little_endian_store_16(event, pos, source); + pos += 2; + event[pos++] = count_log; + event[pos++] = period_log; + event[pos++] = min_hops; + event[pos++] = max_hops; + + (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos); + mesh_access_message_processed(pdu); +} const static mesh_operation_t mesh_configuration_client_model_operations[] = { { MESH_FOUNDATION_OPERATION_BEACON_STATUS, 1, mesh_configuration_client_beacon_status_handler }, @@ -1313,6 +1447,8 @@ const static mesh_operation_t mesh_configuration_client_model_operations[] = { { MESH_FOUNDATION_OPERATION_NODE_RESET_STATUS, 0, mesh_configuration_client_node_reset_handler }, { MESH_FOUNDATION_OPERATION_FRIEND_STATUS, 1, mesh_configuration_client_friend_handler }, { MESH_FOUNDATION_OPERATION_KEY_REFRESH_PHASE_STATUS, 4, mesh_configuration_client_key_refresh_phase_handler }, + { MESH_FOUNDATION_OPERATION_HEARTBEAT_PUBLICATION_STATUS, 10, mesh_configuration_client_heartbeat_publication_handler }, + { MESH_FOUNDATION_OPERATION_HEARTBEAT_SUBSCRIPTION_STATUS, 9, mesh_configuration_client_heartbeat_subscription_handler }, { 0, 0, NULL } }; diff --git a/src/mesh/mesh_configuration_client.h b/src/mesh/mesh_configuration_client.h index cd6845c6d..5970832f5 100644 --- a/src/mesh/mesh_configuration_client.h +++ b/src/mesh/mesh_configuration_client.h @@ -612,6 +612,54 @@ uint8_t mesh_configuration_client_send_key_refresh_phase_get(mesh_model_t * mesh */ uint8_t mesh_configuration_client_send_key_refresh_phase_set(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint16_t netk_index, uint8_t transition); +/** + * @brief Get the current Heartbeat Publication state of an element. + * @param mesh_model + * @param dest element_address + * @param netkey_index + * @param appkey_index + * @param netk_index + * @return status ERROR_CODE_SUCCESS if successful, otherwise BTSTACK_MEMORY_ALLOC_FAILED or ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE + */ +uint8_t mesh_configuration_client_send_heartbeat_publication_get(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index); + +/** + * @brief Set the current Heartbeat Publication state of an element. + * @param mesh_model + * @param dest element_address + * @param netkey_index + * @param appkey_index + * @param netk_index + * @param publication_state + * @return status ERROR_CODE_SUCCESS if successful, otherwise BTSTACK_MEMORY_ALLOC_FAILED or ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE + */ +uint8_t mesh_configuration_client_send_heartbeat_publication_set(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, mesh_heartbeat_publication_state_t publication_state); + +/** + * @brief Get the current Heartbeat Subscription state of an element. + * @param mesh_model + * @param dest element_address + * @param netkey_index + * @param appkey_index + * @param netk_index + * @return status ERROR_CODE_SUCCESS if successful, otherwise BTSTACK_MEMORY_ALLOC_FAILED or ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE + */ +uint8_t mesh_configuration_client_send_heartbeat_subscription_get(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index); + +/** + * @brief Set the current Heartbeat Subscription state of an element. + * @param mesh_model + * @param dest element_address + * @param netkey_index + * @param appkey_index + * @param netk_index + * @param source + * @param period_log + * @return status ERROR_CODE_SUCCESS if successful, otherwise BTSTACK_MEMORY_ALLOC_FAILED or ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE + */ +uint8_t mesh_configuration_client_send_heartbeat_subscription_set(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint16_t source, uint8_t period_log); + + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/src/mesh/mesh_node.h b/src/mesh/mesh_node.h index ab15bbe2c..a83512b90 100644 --- a/src/mesh/mesh_node.h +++ b/src/mesh/mesh_node.h @@ -52,6 +52,11 @@ extern "C" { #define MAX_NR_MESH_APPKEYS_PER_MODEL 3u #define MAX_NR_MESH_SUBSCRIPTION_PER_MODEL 3u +#define MESH_HEARTBEAT_PUBLICATION_FEATURE_RELAY 1 +#define MESH_HEARTBEAT_PUBLICATION_FEATURE_PROXY 2 +#define MESH_HEARTBEAT_PUBLICATION_FEATURE_FRIEND 4 +#define MESH_HEARTBEAT_PUBLICATION_FEATURE_LOW_POWER 8 + struct mesh_model; struct mesh_element; @@ -99,6 +104,14 @@ typedef struct { uint8_t retransmit; } mesh_publication_model_t; +typedef struct { + uint8_t count_log; // Number of Heartbeat messages to be sent + uint8_t period_log; // Period for sending Heartbeat messages + uint8_t ttl; // TTL to be used when sending Heartbeat messages + uint16_t features; // Bit field indicating features that trigger Heartbeat messages when changed + uint16_t netkey_index; +} mesh_heartbeat_publication_state_t; + typedef struct { uint32_t opcode; uint16_t minimum_length;