diff --git a/nimble/host/mesh/include/mesh/glue.h b/nimble/host/mesh/include/mesh/glue.h index d61a5169a..926cbfb93 100644 --- a/nimble/host/mesh/include/mesh/glue.h +++ b/nimble/host/mesh/include/mesh/glue.h @@ -28,6 +28,7 @@ #include "os/os_mbuf.h" #include "os/os_callout.h" #include "os/os_eventq.h" +#include "os/queue.h" #include "atomic.h" #include "nimble/ble.h" @@ -341,6 +342,7 @@ static inline unsigned int find_msb_set(u32_t op) #define CONFIG_BT_MESH_PB_ADV BLE_MESH_PB_ADV #define CONFIG_BT_MESH_PB_GATT BLE_MESH_PB_GATT #define CONFIG_BT_MESH_PROV BLE_MESH_PROV +#define CONFIG_BT_TESTING BLE_MESH_TESTING /* Above flags are used with IS_ENABLED macro */ #define IS_ENABLED(config) MYNEWT_VAL(config) @@ -394,4 +396,22 @@ static inline int net_buf_id(struct os_mbuf *buf) return (buf_ptr - pool_start) / BUF_SIZE(pool); } +/* XXX: We should not use os_mbuf_pkthdr chains to represent a list of + * packets, this is a hack. For now this is not an issue, because mesh + * does not use os_mbuf chains. We should change this in the future. + */ +STAILQ_HEAD(net_buf_slist_t, os_mbuf_pkthdr); + +void net_buf_slist_init(struct net_buf_slist_t *list); +bool net_buf_slist_is_empty(struct net_buf_slist_t *list); +struct os_mbuf *net_buf_slist_peek_head(struct net_buf_slist_t *list); +struct os_mbuf *net_buf_slist_peek_next(struct os_mbuf *buf); +struct os_mbuf *net_buf_slist_get(struct net_buf_slist_t *list); +void net_buf_slist_put(struct net_buf_slist_t *list, struct os_mbuf *buf); +void net_buf_slist_remove(struct net_buf_slist_t *list, struct os_mbuf *prev, + struct os_mbuf *cur); +void net_buf_slist_merge_slist(struct net_buf_slist_t *list, + struct net_buf_slist_t *list_to_append); +#define NET_BUF_SLIST_FOR_EACH_NODE(head, var) STAILQ_FOREACH(var, head, omp_next) + #endif diff --git a/nimble/host/mesh/include/mesh/main.h b/nimble/host/mesh/include/mesh/main.h index 4b7c5fd65..bc3c86f8f 100644 --- a/nimble/host/mesh/include/mesh/main.h +++ b/nimble/host/mesh/include/mesh/main.h @@ -218,6 +218,10 @@ int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers); #define BT_MESH_FEAT_PROXY BIT(1) #define BT_MESH_FEAT_FRIEND BIT(2) #define BT_MESH_FEAT_LOW_POWER BIT(3) +#define BT_MESH_FEAT_SUPPORTED (BT_MESH_FEAT_RELAY | \ + BT_MESH_FEAT_PROXY | \ + BT_MESH_FEAT_FRIEND | \ + BT_MESH_FEAT_LOW_POWER) /** @brief Initialize Mesh support * diff --git a/nimble/host/mesh/include/mesh/slist.h b/nimble/host/mesh/include/mesh/slist.h index 79f845755..8a858f83b 100644 --- a/nimble/host/mesh/include/mesh/slist.h +++ b/nimble/host/mesh/include/mesh/slist.h @@ -19,15 +19,22 @@ #include #include -#include "os/queue.h" #ifdef __cplusplus extern "C" { #endif -typedef struct os_mbuf sys_snode_t; -STAILQ_HEAD(_slist, os_mbuf_pkthdr); +struct _snode { + struct _snode *next; +}; + +typedef struct _snode sys_snode_t; + +struct _slist { + sys_snode_t *head; + sys_snode_t *tail; +}; typedef struct _slist sys_slist_t; @@ -46,9 +53,98 @@ typedef struct _slist sys_slist_t; * @param __sl A pointer on a sys_slist_t to iterate on * @param __sn A sys_snode_t pointer to peek each node of the list */ -#define SYS_SLIST_FOR_EACH_NODE(__sl, __sn) \ - for (__sn = sys_slist_peek_head(__sl); __sn; \ - __sn = sys_slist_peek_next(__sn)) +#define SYS_SLIST_FOR_EACH_NODE(__sl, __sn) \ + for (__sn = sys_slist_peek_head(__sl); __sn; \ + __sn = sys_slist_peek_next(__sn)) + +/** + * @brief Provide the primitive to iterate on a list, from a node in the list + * Note: the loop is unsafe and thus __sn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_ITERATE_FROM_NODE(l, n) { + * + * } + * + * Like SYS_SLIST_FOR_EACH_NODE(), but __dn already contains a node in the list + * where to start searching for the next entry from. If NULL, it starts from + * the head. + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + * it contains the starting node, or NULL to start from the head + */ +#define SYS_SLIST_ITERATE_FROM_NODE(__sl, __sn) \ + for (__sn = __sn ? sys_slist_peek_next_no_check(__sn) \ + : sys_slist_peek_head(__sl); \ + __sn; \ + __sn = sys_slist_peek_next(__sn)) + +/** + * @brief Provide the primitive to safely iterate on a list + * Note: __sn can be removed, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE_SAFE(l, n, s) { + * + * } + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + * @param __sns A sys_snode_t pointer for the loop to run safely + */ +#define SYS_SLIST_FOR_EACH_NODE_SAFE(__sl, __sn, __sns) \ + for (__sn = sys_slist_peek_head(__sl), \ + __sns = sys_slist_peek_next(__sn); \ + __sn; __sn = __sns, \ + __sns = sys_slist_peek_next(__sn)) + +/* + * @brief Provide the primitive to resolve the container of a list node + * Note: it is safe to use with NULL pointer nodes + * + * @param __ln A pointer on a sys_node_t to get its container + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_CONTAINER(__ln, __cn, __n) \ + ((__ln) ? CONTAINER_OF((__ln), __typeof__(*(__cn)), __n) : NULL) +/* + * @brief Provide the primitive to peek container of the list head + * + * @param __sl A pointer on a sys_slist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n) \ + SYS_SLIST_CONTAINER(sys_slist_peek_head(__sl), __cn, __n) + +/* + * @brief Provide the primitive to peek container of the list tail + * + * @param __sl A pointer on a sys_slist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_PEEK_TAIL_CONTAINER(__sl, __cn, __n) \ + SYS_SLIST_CONTAINER(sys_slist_peek_tail(__sl), __cn, __n) + +/* + * @brief Provide the primitive to peek the next container + * + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ + +#define SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n) \ + ((__cn) ? SYS_SLIST_CONTAINER(sys_slist_peek_next(&((__cn)->__n)), \ + __cn, __n) : NULL) /** * @brief Provide the primitive to iterate on a list under a container @@ -64,17 +160,42 @@ typedef struct _slist sys_slist_t; * @param __cn A pointer to peek each entry of the list * @param __n The field name of sys_node_t within the container struct */ -#define SYS_SLIST_FOR_EACH_CONTAINER(__sl, __cn, __n) \ - STAILQ_FOREACH(__cn, __sl, omp_next) +#define SYS_SLIST_FOR_EACH_CONTAINER(__sl, __cn, __n) \ + for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n); __cn; \ + __cn = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +/** + * @brief Provide the primitive to safely iterate on a list under a container + * Note: __cn can be detached, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE_SAFE(l, c, cn, n) { + * + * } + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __cns A pointer for the loop to run safely + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_FOR_EACH_CONTAINER_SAFE(__sl, __cn, __cns, __n) \ + for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n), \ + __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n); __cn; \ + __cn = __cns, __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) /** * @brief Initialize a list * * @param list A pointer on the list to initialize */ -void sys_slist_init(sys_slist_t *list); +static inline void sys_slist_init(sys_slist_t *list) +{ + list->head = NULL; + list->tail = NULL; +} -#define SYS_SLIST_STATIC_INIT(ptr_to_list) STAILQ_HEAD_INITIALIZER(ptr_to_list) +#define SYS_SLIST_STATIC_INIT(ptr_to_list) {NULL, NULL} /** * @brief Test if the given list is empty @@ -83,7 +204,10 @@ void sys_slist_init(sys_slist_t *list); * * @return a boolean, true if it's empty, false otherwise */ -bool sys_slist_is_empty(sys_slist_t *list); +static inline bool sys_slist_is_empty(sys_slist_t *list) +{ + return (!list->head); +} /** * @brief Peek the first node from the list @@ -92,7 +216,10 @@ bool sys_slist_is_empty(sys_slist_t *list); * * @return A pointer on the first node of the list (or NULL if none) */ -sys_snode_t *sys_slist_peek_head(sys_slist_t *list); +static inline sys_snode_t *sys_slist_peek_head(sys_slist_t *list) +{ + return list->head; +} /** * @brief Peek the last node from the list @@ -101,7 +228,10 @@ sys_snode_t *sys_slist_peek_head(sys_slist_t *list); * * @return A pointer on the last node of the list (or NULL if none) */ -sys_snode_t *sys_slist_peek_tail(sys_slist_t *list); +static inline sys_snode_t *sys_slist_peek_tail(sys_slist_t *list) +{ + return list->tail; +} /** * @brief Peek the next node from current node, node is not NULL @@ -112,7 +242,10 @@ sys_snode_t *sys_slist_peek_tail(sys_slist_t *list); * * @return a pointer on the next node (or NULL if none) */ -sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node); +static inline sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node) +{ + return node->next; +} /** * @brief Peek the next node from current node @@ -121,7 +254,10 @@ sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node); * * @return a pointer on the next node (or NULL if none) */ -sys_snode_t *sys_slist_peek_next(sys_snode_t *node); +static inline sys_snode_t *sys_slist_peek_next(sys_snode_t *node) +{ + return node ? sys_slist_peek_next_no_check(node) : NULL; +} /** * @brief Prepend a node to the given list @@ -131,7 +267,16 @@ sys_snode_t *sys_slist_peek_next(sys_snode_t *node); * @param list A pointer on the list to affect * @param node A pointer on the node to prepend */ -void sys_slist_prepend(sys_slist_t *list, sys_snode_t *node); +static inline void sys_slist_prepend(sys_slist_t *list, + sys_snode_t *node) +{ + node->next = list->head; + list->head = node; + + if (!list->tail) { + list->tail = list->head; + } +} /** * @brief Append a node to the given list @@ -141,7 +286,19 @@ void sys_slist_prepend(sys_slist_t *list, sys_snode_t *node); * @param list A pointer on the list to affect * @param node A pointer on the node to append */ -void sys_slist_append(sys_slist_t *list, sys_snode_t *node); +static inline void sys_slist_append(sys_slist_t *list, + sys_snode_t *node) +{ + node->next = NULL; + + if (!list->tail) { + list->tail = node; + list->head = node; + } else { + list->tail->next = node; + list->tail = node; + } +} /** * @brief Append a list to the given list @@ -154,7 +311,17 @@ void sys_slist_append(sys_slist_t *list, sys_snode_t *node); * @param head A pointer to the first element of the list to append * @param tail A pointer to the last element of the list to append */ -void sys_slist_append_list(sys_slist_t *list, sys_slist_t *list_append); +static inline void sys_slist_append_list(sys_slist_t *list, + void *head, void *tail) +{ + if (!list->tail) { + list->head = (sys_snode_t *)head; + list->tail = (sys_snode_t *)tail; + } else { + list->tail->next = (sys_snode_t *)head; + list->tail = (sys_snode_t *)tail; + } +} /** * @brief merge two slists, appending the second one to the first @@ -165,7 +332,13 @@ void sys_slist_append_list(sys_slist_t *list, sys_slist_t *list_append); * @param list A pointer on the list to affect * @param list_to_append A pointer to the list to append. */ -void sys_slist_merge_slist(sys_slist_t *list, sys_slist_t *list_to_append); +static inline void sys_slist_merge_slist(sys_slist_t *list, + sys_slist_t *list_to_append) +{ + sys_slist_append_list(list, list_to_append->head, + list_to_append->tail); + sys_slist_init(list_to_append); +} /** * @brief Insert a node to the given list @@ -176,9 +349,19 @@ void sys_slist_merge_slist(sys_slist_t *list, sys_slist_t *list_to_append); * @param prev A pointer on the previous node * @param node A pointer on the node to insert */ -void sys_slist_insert(sys_slist_t *list, - sys_snode_t *prev, - sys_snode_t *node); +static inline void sys_slist_insert(sys_slist_t *list, + sys_snode_t *prev, + sys_snode_t *node) +{ + if (!prev) { + sys_slist_prepend(list, node); + } else if (!prev->next) { + sys_slist_append(list, node); + } else { + node->next = prev->next; + prev->next = node; + } +} /** * @brief Fetch and remove the first node of the given list @@ -190,7 +373,18 @@ void sys_slist_insert(sys_slist_t *list, * * @return A pointer to the first node of the list */ -sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list); +static inline sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list) +{ + sys_snode_t *node = list->head; + + list->head = node->next; + if (list->tail == node) { + list->tail = list->head; + } + + return node; +} + /** * @brief Fetch and remove the first node of the given list * @@ -200,7 +394,10 @@ sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list); * * @return A pointer to the first node of the list (or NULL if empty) */ -sys_snode_t *sys_slist_get(sys_slist_t *list); +static inline sys_snode_t *sys_slist_get(sys_slist_t *list) +{ + return sys_slist_is_empty(list) ? NULL : sys_slist_get_not_empty(list); +} /** * @brief Remove a node @@ -212,9 +409,28 @@ sys_snode_t *sys_slist_get(sys_slist_t *list); * (can be NULL, which means the node is the list's head) * @param node A pointer on the node to remove */ -void sys_slist_remove(sys_slist_t *list, - sys_snode_t *prev_node, - sys_snode_t *node); +static inline void sys_slist_remove(sys_slist_t *list, + sys_snode_t *prev_node, + sys_snode_t *node) +{ + if (!prev_node) { + list->head = node->next; + + /* Was node also the tail? */ + if (list->tail == node) { + list->tail = list->head; + } + } else { + prev_node->next = node->next; + + /* Was node the tail? */ + if (list->tail == node) { + list->tail = prev_node; + } + } + + node->next = NULL; +} /** * @brief Find and remove a node from a list @@ -226,12 +442,24 @@ void sys_slist_remove(sys_slist_t *list, * * @return true if node was removed */ -bool sys_slist_find_and_remove(sys_slist_t *list, - sys_snode_t *node); +static inline bool sys_slist_find_and_remove(sys_slist_t *list, + sys_snode_t *node) +{ + sys_snode_t *prev = NULL; + sys_snode_t *test; -void net_buf_slist_put(sys_slist_t *list, struct os_mbuf *buf); + SYS_SLIST_FOR_EACH_NODE(list, test) { + if (test == node) { + sys_slist_remove(list, prev, node); + return true; + } + + prev = test; + } + + return false; +} -struct os_mbuf *net_buf_slist_get(sys_slist_t *list); #ifdef __cplusplus } diff --git a/nimble/host/mesh/include/mesh/testing.h b/nimble/host/mesh/include/mesh/testing.h new file mode 100644 index 000000000..f8b9ab224 --- /dev/null +++ b/nimble/host/mesh/include/mesh/testing.h @@ -0,0 +1,68 @@ +/** + * @file testing.h + * @brief Internal API for Bluetooth testing. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BT_TESTING_H +#define __BT_TESTING_H + +#include "slist.h" +#include "glue.h" +#include "access.h" + +/** + * @brief Bluetooth testing + * @defgroup bt_test_cb Bluetooth testing callbacks + * @ingroup bluetooth + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Bluetooth Testing callbacks structure. + * + * Callback structure to be used for Bluetooth testing purposes. + * Allows access to Bluetooth stack internals, not exposed by public API. + */ +struct bt_test_cb { + void (*mesh_net_recv)(u8_t ttl, u8_t ctl, u16_t src, u16_t dst, + const void *payload, size_t payload_len); + void (*mesh_model_bound)(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx); + void (*mesh_model_unbound)(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx); + void (*mesh_prov_invalid_bearer)(u8_t opcode); + void (*mesh_trans_incomp_timer_exp)(void); + + sys_snode_t node; +}; + +/** Register callbacks for Bluetooth testing purposes + * + * @param cb bt_test_cb callback structure + */ +void bt_test_cb_register(struct bt_test_cb *cb); + +/** Unregister callbacks for Bluetooth testing purposes + * + * @param cb bt_test_cb callback structure + */ +void bt_test_cb_unregister(struct bt_test_cb *cb); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_TESTING_H */ diff --git a/nimble/host/mesh/src/adv.c b/nimble/host/mesh/src/adv.c index 40259e6ad..c8aced18b 100644 --- a/nimble/host/mesh/src/adv.c +++ b/nimble/host/mesh/src/adv.c @@ -258,7 +258,7 @@ static void bt_mesh_scan_cb(const bt_addr_le_t *addr, s8_t rssi, return; } - if (len > buf->om_len || buf->om_len < 1) { + if (len > buf->om_len) { BT_WARN("AD malformed"); return; } diff --git a/nimble/host/mesh/src/cfg_srv.c b/nimble/host/mesh/src/cfg_srv.c index 2517ab809..bda46a729 100644 --- a/nimble/host/mesh/src/cfg_srv.c +++ b/nimble/host/mesh/src/cfg_srv.c @@ -27,12 +27,14 @@ #include "proxy.h" #include "foundation.h" #include "friend.h" +#include "testing.h" #define DEFAULT_TTL 7 static struct bt_mesh_cfg_srv *conf; static struct label { + u16_t ref; u16_t addr; u8_t uuid[16]; } labels[MYNEWT_VAL(BLE_MESH_LABEL_COUNT)]; @@ -344,11 +346,9 @@ static u8_t mod_unbind(struct bt_mesh_model *model, u16_t key_idx) _mod_pub_set(model, BT_MESH_ADDR_UNASSIGNED, 0, 0, 0, 0, 0); } - - return STATUS_SUCCESS; } - return STATUS_CANNOT_BIND; + return STATUS_SUCCESS; } static struct bt_mesh_app_key *app_key_alloc(u16_t app_idx) @@ -1115,39 +1115,24 @@ send_status: } #if MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0 -static u16_t va_find(u8_t *label_uuid, struct label **free_slot) +static u8_t va_add(u8_t *label_uuid, u16_t *addr) { + struct label *free_slot = NULL; int i; - if (free_slot) { - *free_slot = NULL; - } - for (i = 0; i < ARRAY_SIZE(labels); i++) { - if (!BT_MESH_ADDR_IS_VIRTUAL(labels[i].addr)) { - if (free_slot) { - *free_slot = &labels[i]; - } + if (!labels[i].ref) { + free_slot = &labels[i]; continue; } if (!memcmp(labels[i].uuid, label_uuid, 16)) { - return labels[i].addr; + *addr = labels[i].addr; + labels[i].ref++; + return STATUS_SUCCESS; } } - return BT_MESH_ADDR_UNASSIGNED; -} - -static u8_t va_add(u8_t *label_uuid, u16_t *addr) -{ - struct label *free_slot; - - *addr = va_find(label_uuid, &free_slot); - if (*addr != BT_MESH_ADDR_UNASSIGNED) { - return STATUS_SUCCESS; - } - if (!free_slot) { return STATUS_INSUFF_RESOURCES; } @@ -1156,12 +1141,59 @@ static u8_t va_add(u8_t *label_uuid, u16_t *addr) return STATUS_UNSPECIFIED; } + free_slot->ref = 1; free_slot->addr = *addr; memcpy(free_slot->uuid, label_uuid, 16); return STATUS_SUCCESS; } +static u8_t va_del(u8_t *label_uuid, u16_t *addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (!memcmp(labels[i].uuid, label_uuid, 16)) { + if (addr) { + *addr = labels[i].addr; + } + + labels[i].ref--; + return STATUS_SUCCESS; + } + } + + if (addr) { + *addr = BT_MESH_ADDR_UNASSIGNED; + } + + return STATUS_CANNOT_REMOVE; +} + +static void mod_sub_list_clear(struct bt_mesh_model *mod) +{ + u8_t *label_uuid; + int i; + + /* Unref stored labels related to this model */ + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (!BT_MESH_ADDR_IS_VIRTUAL(mod->groups[i])) { + continue; + } + + label_uuid = bt_mesh_label_uuid_get(mod->groups[i]); + if (!label_uuid) { + BT_ERR("Label UUID not found"); + continue; + } + + va_del(label_uuid, NULL); + } + + /* Clear all subscriptions (0x0000 is the unassigned address) */ + memset(mod->groups, 0, sizeof(mod->groups)); +} + static void mod_pub_va_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) @@ -1226,6 +1258,12 @@ send_status: status, mod_id); } #else +static void mod_sub_list_clear(struct bt_mesh_model *mod) +{ + /* Clear all subscriptions (0x0000 is the unassigned address) */ + memset(mod->groups, 0, sizeof(mod->groups)); +} + static void mod_pub_va_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) @@ -1464,8 +1502,7 @@ static void mod_sub_overwrite(struct bt_mesh_model *model, bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); } - /* Clear all subscriptions (0x0000 is the unassigned address) */ - memset(mod->groups, 0, sizeof(mod->groups)); + mod_sub_list_clear(mod); if (ARRAY_SIZE(mod->groups) > 0) { mod->groups[0] = sub_addr; @@ -1519,8 +1556,7 @@ static void mod_sub_del_all(struct bt_mesh_model *model, bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); } - /* Clear all subscriptions (0x0000 is the unassigned address) */ - memset(mod->groups, 0, sizeof(mod->groups)); + mod_sub_list_clear(mod); status = STATUS_SUCCESS; @@ -1750,12 +1786,15 @@ static void mod_sub_va_del(struct bt_mesh_model *model, goto send_status; } - sub_addr = va_find(label_uuid, NULL); + status = va_del(label_uuid, &sub_addr); if (sub_addr == BT_MESH_ADDR_UNASSIGNED) { - status = STATUS_CANNOT_REMOVE; goto send_status; } + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_del(&sub_addr, 1); + } + match = bt_mesh_model_find_group(mod, sub_addr); if (match) { *match = BT_MESH_ADDR_UNASSIGNED; @@ -1807,8 +1846,7 @@ static void mod_sub_va_overwrite(struct bt_mesh_model *model, bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); } - /* Clear all subscriptions (0x0000 is the unassigned address) */ - memset(mod->groups, 0, sizeof(mod->groups)); + mod_sub_list_clear(mod); if (ARRAY_SIZE(mod->groups) > 0) { status = va_add(label_uuid, &sub_addr); @@ -2359,6 +2397,10 @@ static void mod_app_bind(struct bt_mesh_model *model, status = mod_bind(mod, key_app_idx); + if (IS_ENABLED(CONFIG_BT_TESTING) && status == STATUS_SUCCESS) { + bt_test_mesh_model_bound(ctx->addr, mod, key_app_idx); + } + send_status: BT_DBG("status 0x%02x", status); create_mod_app_status(msg, mod, vnd, elem_addr, key_app_idx, status, @@ -2403,6 +2445,10 @@ static void mod_app_unbind(struct bt_mesh_model *model, status = mod_unbind(mod, key_app_idx); + if (IS_ENABLED(CONFIG_BT_TESTING) && status == STATUS_SUCCESS) { + bt_test_mesh_model_unbound(ctx->addr, mod, key_app_idx); + } + send_status: BT_DBG("status 0x%02x", status); create_mod_app_status(msg, mod, vnd, elem_addr, key_app_idx, status, @@ -2860,7 +2906,7 @@ static void heartbeat_pub_set(struct bt_mesh_model *model, cfg->hb_pub.dst = dst; cfg->hb_pub.period = param->period_log; - cfg->hb_pub.feat = feat; + cfg->hb_pub.feat = feat & BT_MESH_FEAT_SUPPORTED; cfg->hb_pub.net_idx = idx; if (dst == BT_MESH_ADDR_UNASSIGNED) { diff --git a/nimble/host/mesh/src/friend.c b/nimble/host/mesh/src/friend.c index 053f35ebb..50d65e6a5 100644 --- a/nimble/host/mesh/src/friend.c +++ b/nimble/host/mesh/src/friend.c @@ -175,14 +175,14 @@ static void friend_clear(struct bt_mesh_friend *frnd) frnd->last = NULL; } - while (!sys_slist_is_empty(&frnd->queue)) { + while (!net_buf_slist_is_empty(&frnd->queue)) { net_buf_unref(net_buf_slist_get(&frnd->queue)); } for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { struct bt_mesh_friend_seg *seg = &frnd->seg[i]; - while (!sys_slist_is_empty(&seg->queue)) { + while (!net_buf_slist_is_empty(&seg->queue)) { net_buf_unref(net_buf_slist_get(&seg->queue)); } } @@ -592,7 +592,7 @@ int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct os_mbuf *buf) frnd->fsn = msg->fsn; - if (sys_slist_is_empty(&frnd->queue)) { + if (net_buf_slist_is_empty(&frnd->queue)) { enqueue_update(frnd, 0); BT_DBG("Enqueued Friend Update to empty queue"); } @@ -894,7 +894,7 @@ static struct bt_mesh_friend_seg *get_seg(struct bt_mesh_friend *frnd, for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { struct bt_mesh_friend_seg *seg = &frnd->seg[i]; - struct os_mbuf *buf = (void *)sys_slist_peek_head(&seg->queue); + struct os_mbuf *buf = (void *)net_buf_slist_peek_head(&seg->queue); if (buf && BT_MESH_ADV(buf)->addr == src && FRIEND_ADV(buf)->seq_auth == *seq_auth) { @@ -948,14 +948,14 @@ static void enqueue_friend_pdu(struct bt_mesh_friend *frnd, * the SeqAuth information from the segments before merging. */ struct os_mbuf *m; - struct os_mbuf_pkthdr *mp; - SYS_SLIST_FOR_EACH_CONTAINER(&seg->queue, mp,) { - m = OS_MBUF_PKTHDR_TO_MBUF(mp); + struct os_mbuf_pkthdr *pkthdr; + NET_BUF_SLIST_FOR_EACH_NODE(&seg->queue, pkthdr) { + m = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); FRIEND_ADV(m)->seq_auth = TRANS_SEQ_AUTH_NVAL; frnd->queue_size++; } - sys_slist_merge_slist(&frnd->queue, &seg->queue); + net_buf_slist_merge_slist(&frnd->queue, &seg->queue); } } @@ -1058,7 +1058,7 @@ int bt_mesh_friend_init(void) frnd->net_idx = BT_MESH_KEY_UNUSED; - sys_slist_init(&frnd->queue); + net_buf_slist_init(&frnd->queue); k_delayed_work_init(&frnd->timer, friend_timeout); k_delayed_work_add_arg(&frnd->timer, frnd); @@ -1066,7 +1066,7 @@ int bt_mesh_friend_init(void) k_delayed_work_add_arg(&frnd->clear.timer, frnd); for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) { - sys_slist_init(&frnd->seg[j].queue); + net_buf_slist_init(&frnd->seg[j].queue); } } @@ -1076,19 +1076,19 @@ int bt_mesh_friend_init(void) static void friend_purge_old_ack(struct bt_mesh_friend *frnd, u64_t *seq_auth, u16_t src) { - sys_snode_t *cur, *prev = NULL; + struct os_mbuf *cur, *prev = NULL; BT_DBG("SeqAuth %llx src 0x%04x", *seq_auth, src); - for (cur = sys_slist_peek_head(&frnd->queue); - cur != NULL; prev = cur, cur = sys_slist_peek_next(cur)) { + for (cur = net_buf_slist_peek_head(&frnd->queue); + cur != NULL; prev = cur, cur = net_buf_slist_peek_next(cur)) { struct os_mbuf *buf = (void *)cur; if (BT_MESH_ADV(buf)->addr == src && FRIEND_ADV(buf)->seq_auth == *seq_auth) { BT_DBG("Removing old ack from Friend Queue"); - sys_slist_remove(&frnd->queue, prev, cur); + net_buf_slist_remove(&frnd->queue, prev, cur); frnd->queue_size--; net_buf_unref(buf); @@ -1304,7 +1304,7 @@ void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src, struct bt_mesh_friend_seg *seg = &frnd->seg[j]; struct os_mbuf *buf; - buf = (void *)sys_slist_peek_head(&seg->queue); + buf = (void *)net_buf_slist_peek_head(&seg->queue); if (!buf) { continue; } @@ -1319,7 +1319,7 @@ void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src, BT_WARN("Clearing incomplete segments for 0x%04x", src); - while (!sys_slist_is_empty(&seg->queue)) { + while (!net_buf_slist_is_empty(&seg->queue)) { net_buf_unref(net_buf_slist_get(&seg->queue)); } } diff --git a/nimble/host/mesh/src/glue.c b/nimble/host/mesh/src/glue.c index 9ce1319e9..a4ab5a85e 100644 --- a/nimble/host/mesh/src/glue.c +++ b/nimble/host/mesh/src/glue.c @@ -505,3 +505,92 @@ bt_mesh_register_gatt(void) bt_mesh_proxy_svcs_register(); #endif } + +void net_buf_slist_init(struct net_buf_slist_t *list) +{ + STAILQ_INIT(list); +} + +bool net_buf_slist_is_empty(struct net_buf_slist_t *list) +{ + return STAILQ_EMPTY(list); +} + +struct os_mbuf *net_buf_slist_peek_head(struct net_buf_slist_t *list) +{ + struct os_mbuf_pkthdr *pkthdr; + + /* Get mbuf pointer from packet header pointer */ + pkthdr = STAILQ_FIRST(list); + if (!pkthdr) { + return NULL; + } + + return OS_MBUF_PKTHDR_TO_MBUF(pkthdr); +} + +struct os_mbuf *net_buf_slist_peek_next(struct os_mbuf *buf) +{ + struct os_mbuf_pkthdr *pkthdr; + + /* Get mbuf pointer from packet header pointer */ + pkthdr = OS_MBUF_PKTHDR(buf); + pkthdr = STAILQ_NEXT(pkthdr, omp_next); + if (!pkthdr) { + return NULL; + } + + return OS_MBUF_PKTHDR_TO_MBUF(pkthdr); +} + +struct os_mbuf *net_buf_slist_get(struct net_buf_slist_t *list) +{ + os_sr_t sr; + struct os_mbuf *m; + + m = net_buf_slist_peek_head(list); + if (!m) { + return NULL; + } + + /* Remove from queue */ + OS_ENTER_CRITICAL(sr); + STAILQ_REMOVE_HEAD(list, omp_next); + OS_EXIT_CRITICAL(sr); + return m; +} + +void net_buf_slist_put(struct net_buf_slist_t *list, struct os_mbuf *buf) +{ + struct os_mbuf_pkthdr *pkthdr; + + pkthdr = OS_MBUF_PKTHDR(buf); + STAILQ_INSERT_TAIL(list, pkthdr, omp_next); +} + +void net_buf_slist_remove(struct net_buf_slist_t *list, struct os_mbuf *prev, + struct os_mbuf *cur) +{ + struct os_mbuf_pkthdr *pkthdr, *cur_pkthdr; + + cur_pkthdr = OS_MBUF_PKTHDR(cur); + + STAILQ_FOREACH(pkthdr, list, omp_next) { + if (cur_pkthdr == pkthdr) { + STAILQ_REMOVE(list, cur_pkthdr, os_mbuf_pkthdr, omp_next); + break; + } + } +} + +void net_buf_slist_merge_slist(struct net_buf_slist_t *list, + struct net_buf_slist_t *list_to_append) +{ + struct os_mbuf_pkthdr *pkthdr; + + STAILQ_FOREACH(pkthdr, list_to_append, omp_next) { + STAILQ_INSERT_TAIL(list, pkthdr, omp_next); + } + + STAILQ_INIT(list); +} diff --git a/nimble/host/mesh/src/net.c b/nimble/host/mesh/src/net.c index a1325a7cf..ffde0a6aa 100644 --- a/nimble/host/mesh/src/net.c +++ b/nimble/host/mesh/src/net.c @@ -75,7 +75,7 @@ static u16_t msg_cache_next; /* Singleton network context (the implementation only supports one) */ struct bt_mesh_net bt_mesh = { - .local_queue = SYS_SLIST_STATIC_INIT(bt_mesh.local_queue), + .local_queue = STAILQ_HEAD_INITIALIZER(bt_mesh.local_queue), .sub = { [0 ... (MYNEWT_VAL(BLE_MESH_SUBNET_COUNT) - 1)] = { .net_idx = BT_MESH_KEY_UNUSED, @@ -1351,4 +1351,5 @@ void bt_mesh_net_init(void) k_delayed_work_init(&bt_mesh.ivu_complete, ivu_complete); k_work_init(&bt_mesh.local_work, bt_mesh_net_local); + net_buf_slist_init(&bt_mesh.local_queue); } diff --git a/nimble/host/mesh/src/net.h b/nimble/host/mesh/src/net.h index a99b75ac2..24940a648 100644 --- a/nimble/host/mesh/src/net.h +++ b/nimble/host/mesh/src/net.h @@ -22,7 +22,7 @@ #include #include "mesh/mesh.h" -#include "mesh/slist.h" +#include "mesh/glue.h" struct bt_mesh_app_key { u16_t net_idx; @@ -103,12 +103,12 @@ struct bt_mesh_friend { struct k_delayed_work timer; struct bt_mesh_friend_seg { - sys_slist_t queue; + struct net_buf_slist_t queue; } seg[FRIEND_SEG_RX]; struct os_mbuf *last; - sys_slist_t queue; + struct net_buf_slist_t queue; u32_t queue_size; /* Friend Clear Procedure */ @@ -201,7 +201,7 @@ struct bt_mesh_net { /* Local network interface */ struct os_callout local_work; - sys_slist_t local_queue; + struct net_buf_slist_t local_queue; #if MYNEWT_VAL(BLE_MESH_FRIEND) /* Friend state, unique for each LPN that we're Friends for */ diff --git a/nimble/host/mesh/src/prov.c b/nimble/host/mesh/src/prov.c index 623464305..695c0fa34 100644 --- a/nimble/host/mesh/src/prov.c +++ b/nimble/host/mesh/src/prov.c @@ -26,6 +26,7 @@ #include "foundation.h" #include "proxy.h" #include "prov.h" +#include "testing.h" /* 3 transmissions, 20ms interval */ #define PROV_XMIT_COUNT 2 @@ -1236,6 +1237,11 @@ static void gen_prov_ctl(struct prov_rx *rx, struct os_mbuf *buf) break; default: BT_ERR("Unknown bearer opcode: 0x%02x", BEARER_CTL(rx->gpc)); + + if (IS_ENABLED(CONFIG_BT_TESTING)) { + bt_test_mesh_prov_invalid_bearer(BEARER_CTL(rx->gpc)); + } + return; } } diff --git a/nimble/host/mesh/src/shell.c b/nimble/host/mesh/src/shell.c index 3b668d299..303f13ccc 100644 --- a/nimble/host/mesh/src/shell.c +++ b/nimble/host/mesh/src/shell.c @@ -1627,7 +1627,7 @@ static int cmd_provision(int argc, char *argv[]) addr = strtoul(argv[2], NULL, 0); if (argc > 3) { - iv_index = strtoul(argv[1], NULL, 0); + iv_index = strtoul(argv[3], NULL, 0); } else { iv_index = 0; } diff --git a/nimble/host/mesh/src/slist.c b/nimble/host/mesh/src/slist.c deleted file mode 100644 index 719c0679e..000000000 --- a/nimble/host/mesh/src/slist.c +++ /dev/null @@ -1,323 +0,0 @@ - -/* - * Copyright (c) 2016 Intel Corporation - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include "mesh/slist.h" -#include "os/queue.h" -#include "os/os.h" -#include "os/os_mbuf.h" - - -/** - * @brief Initialize a list - * - * @param list A pointer on the list to initialize - */ -void sys_slist_init(sys_slist_t *list) -{ - STAILQ_INIT(list); -} - -/** - * @brief Test if the given list is empty - * - * @param list A pointer on the list to test - * - * @return a boolean, true if it's empty, false otherwise - */ -bool sys_slist_is_empty(sys_slist_t *list) -{ - return STAILQ_EMPTY(list); -} - -/** - * @brief Peek the first node from the list - * - * @param list A point on the list to peek the first node from - * - * @return A pointer on the first node of the list (or NULL if none) - */ -sys_snode_t *sys_slist_peek_head(sys_slist_t *list) -{ - struct os_mbuf_pkthdr *mp; - struct os_mbuf *m; - - mp = STAILQ_FIRST(list); - - if (mp) { - m = OS_MBUF_PKTHDR_TO_MBUF(mp); - } else { - m = NULL; - } - - return m; -} - -/** - * @brief Peek the last node from the list - * - * @param list A point on the list to peek the last node from - * - * @return A pointer on the last node of the list (or NULL if none) - */ -sys_snode_t *sys_slist_peek_tail(sys_slist_t *list) -{ - struct os_mbuf_pkthdr *mp; - struct os_mbuf *m; - - mp = STAILQ_LAST(list, os_mbuf_pkthdr, omp_next); - - if (mp) { - m = OS_MBUF_PKTHDR_TO_MBUF(mp); - } else { - m = NULL; - } - - return m; -} - -/** - * @brief Peek the next node from current node, node is not NULL - * - * Faster then sys_slist_peek_next() if node is known not to be NULL. - * - * @param node A pointer on the node where to peek the next node - * - * @return a pointer on the next node (or NULL if none) - */ -sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node) -{ - struct os_mbuf_pkthdr *mp; - struct os_mbuf *m; - - mp = OS_MBUF_PKTHDR(node); - if (!mp) { - return NULL; - } - - mp = STAILQ_NEXT(mp, omp_next); - - if (mp) { - m = OS_MBUF_PKTHDR_TO_MBUF(mp); - } else { - m = NULL; - } - - return m; -} - -/** - * @brief Peek the next node from current node - * - * @param node A pointer on the node where to peek the next node - * - * @return a pointer on the next node (or NULL if none) - */ -sys_snode_t *sys_slist_peek_next(sys_snode_t *node) -{ - return node ? sys_slist_peek_next_no_check(node) : NULL; -} - -/** - * @brief Prepend a node to the given list - * - * This and other sys_slist_*() functions are not thread safe. - * - * @param list A pointer on the list to affect - * @param node A pointer on the node to prepend - */ -void sys_slist_prepend(sys_slist_t *list, - sys_snode_t *node) -{ - struct os_mbuf_pkthdr *mp; - - mp = OS_MBUF_PKTHDR(node); - if (!mp) { - return; - } - - STAILQ_INSERT_HEAD(list, mp, omp_next); -} - -/** - * @brief Append a node to the given list - * - * This and other sys_slist_*() functions are not thread safe. - * - * @param list A pointer on the list to affect - * @param node A pointer on the node to append - */ -void sys_slist_append(sys_slist_t *list, - sys_snode_t *node) -{ - struct os_mbuf_pkthdr *mp; - - mp = OS_MBUF_PKTHDR(node); - if (!mp) { - return; - } - - STAILQ_INSERT_TAIL(list, mp, omp_next); -} - -/** - * @brief Append a list to the given list - * - * Append a singly-linked, NULL-terminated list consisting of nodes containing - * the pointer to the next node as the first element of a node, to @a list. - * This and other sys_slist_*() functions are not thread safe. - * - * @param list A pointer on the list to affect - * @param head A pointer to the first element of the list to append - * @param tail A pointer to the last element of the list to append - */ -void sys_slist_append_list(sys_slist_t *list, - sys_slist_t *list_append) -{ - struct os_mbuf_pkthdr *mp; - - STAILQ_FOREACH(mp, list_append, omp_next) { - STAILQ_INSERT_TAIL(list, mp, omp_next); - } -} - -/** - * @brief merge two slists, appending the second one to the first - * - * When the operation is completed, the appending list is empty. - * This and other sys_slist_*() functions are not thread safe. - * - * @param list A pointer on the list to affect - * @param list_to_append A pointer to the list to append. - */ -void sys_slist_merge_slist(sys_slist_t *list, - sys_slist_t *list_to_append) -{ - sys_slist_append_list(list, list_to_append); - sys_slist_init(list_to_append); -} - -/** - * @brief Insert a node to the given list - * - * This and other sys_slist_*() functions are not thread safe. - * - * @param list A pointer on the list to affect - * @param prev A pointer on the previous node - * @param node A pointer on the node to insert - */ -void sys_slist_insert(sys_slist_t *list, - sys_snode_t *prev, - sys_snode_t *node) -{ - struct os_mbuf_pkthdr *mp, *mp_prev; - - if (!prev) { - sys_slist_prepend(list, node); - } else { - mp_prev = OS_MBUF_PKTHDR(prev); - mp = OS_MBUF_PKTHDR(node); - STAILQ_INSERT_AFTER(list, mp_prev, mp, omp_next); - } -} - -/** - * @brief Fetch and remove the first node of the given list - * - * List must be known to be non-empty. - * This and other sys_slist_*() functions are not thread safe. - * - * @param list A pointer on the list to affect - * - * @return A pointer to the first node of the list - */ -sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list) -{ - struct os_mbuf_pkthdr *mp; - struct os_mbuf *m; - - mp = STAILQ_FIRST(list); - m = OS_MBUF_PKTHDR_TO_MBUF(mp); - - STAILQ_REMOVE_HEAD(list, omp_next); - - return m; -} - -/** - * @brief Fetch and remove the first node of the given list - * - * This and other sys_slist_*() functions are not thread safe. - * - * @param list A pointer on the list to affect - * - * @return A pointer to the first node of the list (or NULL if empty) - */ -sys_snode_t *sys_slist_get(sys_slist_t *list) -{ - return sys_slist_is_empty(list) ? NULL : sys_slist_get_not_empty(list); -} - -/** - * @brief Remove a node - * - * This and other sys_slist_*() functions are not thread safe. - * - * @param list A pointer on the list to affect - * @param prev_node A pointer on the previous node - * (can be NULL, which means the node is the list's head) - * @param node A pointer on the node to remove - */ -void sys_slist_remove(sys_slist_t *list, - sys_snode_t *prev_node, - sys_snode_t *node) -{ - struct os_mbuf_pkthdr *mp; - - mp = OS_MBUF_PKTHDR(node); - STAILQ_REMOVE(list, mp, os_mbuf_pkthdr, omp_next); -} - -/** - * @brief Find and remove a node from a list - * - * This and other sys_slist_*() functions are not thread safe. - * - * @param list A pointer on the list to affect - * @param node A pointer on the node to remove from the list - * - * @return true if node was removed - */ -bool sys_slist_find_and_remove(sys_slist_t *list, - sys_snode_t *node) -{ - sys_snode_t *prev = NULL; - sys_snode_t *test; - - SYS_SLIST_FOR_EACH_NODE(list, test) { - if (test == node) { - sys_slist_remove(list, prev, node); - return true; - } - - prev = test; - } - - return false; - -} - -void net_buf_slist_put(sys_slist_t *list, struct os_mbuf *buf) -{ - sys_slist_append(list, buf); -} - -struct os_mbuf *net_buf_slist_get(sys_slist_t *list) -{ - return sys_slist_get(list); -} diff --git a/nimble/host/mesh/src/testing.c b/nimble/host/mesh/src/testing.c new file mode 100644 index 000000000..4a41978cd --- /dev/null +++ b/nimble/host/mesh/src/testing.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "mesh/testing.h" +#include "mesh/slist.h" +#include "mesh/glue.h" +#include "mesh/access.h" + +#include "net.h" +#include "testing.h" + +static sys_slist_t cb_slist; + +void bt_test_cb_register(struct bt_test_cb *cb) +{ + sys_slist_append(&cb_slist, &cb->node); +} + +void bt_test_cb_unregister(struct bt_test_cb *cb) +{ + sys_slist_find_and_remove(&cb_slist, &cb->node); +} + +void bt_test_mesh_net_recv(u8_t ttl, u8_t ctl, u16_t src, u16_t dst, + const void *payload, size_t payload_len) +{ + struct bt_test_cb *cb; + + SYS_SLIST_FOR_EACH_CONTAINER(&cb_slist, cb, node) { + if (cb->mesh_net_recv) { + cb->mesh_net_recv(ttl, ctl, src, dst, payload, + payload_len); + } + } +} + +void bt_test_mesh_model_bound(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx) +{ + struct bt_test_cb *cb; + + SYS_SLIST_FOR_EACH_CONTAINER(&cb_slist, cb, node) { + if (cb->mesh_model_bound) { + cb->mesh_model_bound(addr, model, key_idx); + } + } +} + +void bt_test_mesh_model_unbound(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx) +{ + struct bt_test_cb *cb; + + SYS_SLIST_FOR_EACH_CONTAINER(&cb_slist, cb, node) { + if (cb->mesh_model_unbound) { + cb->mesh_model_unbound(addr, model, key_idx); + } + } +} + +void bt_test_mesh_prov_invalid_bearer(u8_t opcode) +{ + struct bt_test_cb *cb; + + SYS_SLIST_FOR_EACH_CONTAINER(&cb_slist, cb, node) { + if (cb->mesh_prov_invalid_bearer) { + cb->mesh_prov_invalid_bearer(opcode); + } + } +} + +void bt_test_mesh_trans_incomp_timer_exp(void) +{ + struct bt_test_cb *cb; + + SYS_SLIST_FOR_EACH_CONTAINER(&cb_slist, cb, node) { + if (cb->mesh_trans_incomp_timer_exp) { + cb->mesh_trans_incomp_timer_exp(); + } + } +} diff --git a/nimble/host/mesh/src/testing.h b/nimble/host/mesh/src/testing.h new file mode 100644 index 000000000..9a1df6f0e --- /dev/null +++ b/nimble/host/mesh/src/testing.h @@ -0,0 +1,22 @@ +/** + * @file testing.h + * @brief Internal API for Bluetooth testing. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mesh/glue.h" +#include "mesh/access.h" + +void bt_test_mesh_model_bound(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx); +void bt_test_mesh_model_unbound(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx); +void bt_test_mesh_prov_invalid_bearer(u8_t opcode); +void bt_test_mesh_net_recv(u8_t ttl, u8_t ctl, u16_t src, u16_t dst, + const void *payload, size_t payload_len); +void bt_test_mesh_trans_incomp_timer_exp(void); diff --git a/nimble/host/mesh/src/transport.c b/nimble/host/mesh/src/transport.c index 013946bba..9902a6d7c 100644 --- a/nimble/host/mesh/src/transport.c +++ b/nimble/host/mesh/src/transport.c @@ -25,6 +25,7 @@ #include "access.h" #include "foundation.h" #include "transport.h" +#include "testing.h" #define AID_MASK ((u8_t)(BIT_MASK(6))) @@ -42,9 +43,6 @@ #define SEQ_AUTH(iv_index, seq) (((u64_t)iv_index) << 24 | (u64_t)seq) -/* Retransmit timeout after which to retransmit unacked segments */ -#define SEG_RETRANSMIT_TIMEOUT K_MSEC(400) - /* Number of retransmit attempts (after the initial transmit) per segment */ #define SEG_RETRANSMIT_ATTEMPTS 4 @@ -59,6 +57,7 @@ static struct seg_tx { u8_t seg_n:5, /* Last segment index */ new_key:1; /* New/old key */ u8_t nack_count; /* Number of unacked segs */ + u8_t ttl; const struct bt_mesh_send_cb *cb; void *cb_data; struct k_delayed_work retransmit; /* Retransmit timer */ @@ -204,8 +203,16 @@ static void seg_send_start(u16_t duration, int err, void *user_data) static void seg_sent(int err, void *user_data) { struct seg_tx *tx = user_data; + s32_t timeout; - k_delayed_work_submit(&tx->retransmit, SEG_RETRANSMIT_TIMEOUT); + /* "This timer shall be set to a minimum of 200 + 50 * TTL + * milliseconds.". We use 400 since 300 is a common send + * duration for standard HCI, and we need to have a timeout + * that's bigger than that. + */ + timeout = K_MSEC(400) + 50 * tx->ttl; + + k_delayed_work_submit(&tx->retransmit, timeout); } static const struct bt_mesh_send_cb first_sent_cb = { @@ -307,6 +314,12 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, struct os_mbuf *sdu, tx->cb = cb; tx->cb_data = cb_data; + if (net_tx->ctx->send_ttl == BT_MESH_TTL_DEFAULT) { + tx->ttl = bt_mesh_default_ttl_get(); + } else { + tx->ttl = net_tx->ctx->send_ttl; + } + seq_zero = tx->seq_auth & 0x1fff; BT_DBG("SeqZero 0x%04x", seq_zero); @@ -846,11 +859,18 @@ static int trans_unseg(struct os_mbuf *buf, struct bt_mesh_net_rx *rx, static inline s32_t ack_timeout(struct seg_rx *rx) { s32_t to; + u8_t ttl; + + if (rx->ttl == BT_MESH_TTL_DEFAULT) { + ttl = bt_mesh_default_ttl_get(); + } else { + ttl = rx->ttl; + } /* The acknowledgment timer shall be set to a minimum of * 150 + 50 * TTL milliseconds. */ - to = K_MSEC(150 + (50 * rx->ttl)); + to = K_MSEC(150 + (50 * ttl)); /* 100 ms for every not yet received segment */ to += K_MSEC(((rx->seg_n + 1) - popcount(rx->block)) * 100); @@ -940,7 +960,7 @@ static int send_ack(struct bt_mesh_subnet *sub, u16_t src, u16_t dst, NULL, NULL, NULL); } -static void seg_rx_reset(struct seg_rx *rx) +static void seg_rx_reset(struct seg_rx *rx, bool full_reset) { BT_DBG("rx %p", rx); @@ -953,12 +973,18 @@ static void seg_rx_reset(struct seg_rx *rx) &rx->seq_auth); } - /* We don't reset rx->net and rx->seq_auth here since we need to - * be able to send an ack if we receive a segment after we've - * already received the full SDU. - */ - rx->in_use = 0; + + /* We don't always reset these values since we need to be able to + * send an ack if we receive a segment after we've already received + * the full SDU. + */ + if (full_reset) { + rx->seq_auth = 0; + rx->sub = NULL; + rx->src = BT_MESH_ADDR_UNASSIGNED; + rx->dst = BT_MESH_ADDR_UNASSIGNED; + } } static void seg_ack(struct os_event *work) @@ -967,11 +993,16 @@ static void seg_ack(struct os_event *work) BT_DBG("rx %p", rx); - if (k_uptime_get_32() - rx->last > (60 * MSEC_PER_SEC)) { + if (k_uptime_get_32() - rx->last > K_SECONDS(60)) { BT_WARN("Incomplete timer expired"); send_ack(rx->sub, rx->dst, rx->src, rx->ttl, &rx->seq_auth, 0, rx->obo); - seg_rx_reset(rx); + seg_rx_reset(rx, true); + + if (IS_ENABLED(CONFIG_BT_TESTING)) { + bt_test_mesh_trans_incomp_timer_exp(); + } + return; } @@ -1007,7 +1038,10 @@ static struct seg_rx *seg_rx_find(struct bt_mesh_net_rx *net_rx, continue; } - if (rx->seq_auth == *seq_auth) { + /* Return newer RX context in addition to an exact match, so + * the calling function can properly discard an old SeqAuth. + */ + if (rx->seq_auth >= *seq_auth) { return rx; } @@ -1018,7 +1052,7 @@ static struct seg_rx *seg_rx_find(struct bt_mesh_net_rx *net_rx, /* Clear out the old context since the sender * has apparently started sending a new SDU. */ - seg_rx_reset(rx); + seg_rx_reset(rx, true); /* Return non-match so caller can re-allocate */ return NULL; @@ -1122,6 +1156,12 @@ static int trans_seg(struct os_mbuf *buf, struct bt_mesh_net_rx *net_rx, /* Look for old RX sessions */ rx = seg_rx_find(net_rx, seq_auth); if (rx) { + /* Discard old SeqAuth packet */ + if (rx->seq_auth > *seq_auth) { + BT_WARN("Ignoring old SeqAuth"); + return -EINVAL; + } + if (!seg_rx_is_valid(rx, net_rx, hdr, seg_n)) { return -EINVAL; } @@ -1189,7 +1229,7 @@ found_rx: BT_ERR("Too large SDU len"); send_ack(net_rx->sub, net_rx->dst, net_rx->ctx.addr, net_rx->ctx.send_ttl, seq_auth, 0, rx->obo); - seg_rx_reset(rx); + seg_rx_reset(rx, true); return -EMSGSIZE; } } else { @@ -1245,7 +1285,7 @@ found_rx: err = sdu_recv(net_rx, *hdr, ASZMIC(hdr), rx->buf); } - seg_rx_reset(rx); + seg_rx_reset(rx, false); return err; } @@ -1272,6 +1312,11 @@ int bt_mesh_trans_recv(struct os_mbuf *buf, struct bt_mesh_net_rx *rx) BT_DBG("Payload %s", bt_hex(buf->om_data, buf->om_len)); + if (IS_ENABLED(CONFIG_BT_TESTING)) { + bt_test_mesh_net_recv(rx->ctx.recv_ttl, rx->ctl, rx->ctx.addr, + rx->dst, buf->om_data, buf->om_len); + } + /* If LPN mode is enabled messages are only accepted when we've * requested the Friend to send them. The messages must also * be encrypted using the Friend Credentials. @@ -1307,16 +1352,12 @@ int bt_mesh_trans_recv(struct os_mbuf *buf, struct bt_mesh_net_rx *rx) * bt_mesh_lpn_waiting_update() function will return false: * we still need to go through the actual sending to the bearer and * wait for ReceiveDelay before transitioning to WAIT_UPDATE state. - * * Another situation where we want to notify the LPN state machine * is if it's configured to use an automatic Friendship establishment * timer, in which case we want to reset the timer at this point. * - * ENOENT is a special condition that's only used to indicate that - * the Transport OpCode was invalid, in which case we should ignore - * the PDU completely, as per MESH/NODE/FRND/LPN/BI-02-C. */ - if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && err != -ENOENT && + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && (bt_mesh_lpn_timer() || (bt_mesh_lpn_established() && bt_mesh_lpn_waiting_update()))) { bt_mesh_lpn_msg_received(rx); @@ -1342,9 +1383,7 @@ void bt_mesh_rx_reset(void) BT_DBG(""); for (i = 0; i < ARRAY_SIZE(seg_rx); i++) { - seg_rx_reset(&seg_rx[i]); - seg_rx[i].src = BT_MESH_ADDR_UNASSIGNED; - seg_rx[i].dst = BT_MESH_ADDR_UNASSIGNED; + seg_rx_reset(&seg_rx[i], true); } } diff --git a/nimble/host/mesh/syscfg.yml b/nimble/host/mesh/syscfg.yml index 7ed342ca2..63622de3f 100644 --- a/nimble/host/mesh/syscfg.yml +++ b/nimble/host/mesh/syscfg.yml @@ -371,6 +371,11 @@ syscfg.defs: Procedure and lets the state be changed at any time. value: 0 + BLE_MESH_TESTING: + description: > + This option enables testing API. + value: 0 + BLE_MESH_DEV_UUID: description: > Device UUID