diff --git a/nimble/host/mesh/include/mesh/glue.h b/nimble/host/mesh/include/mesh/glue.h index a84c53e2c..643a36b60 100644 --- a/nimble/host/mesh/include/mesh/glue.h +++ b/nimble/host/mesh/include/mesh/glue.h @@ -242,6 +242,10 @@ static inline void net_buf_simple_init(struct os_mbuf *buf, buf->om_len = 0; } +#define net_buf_simple_init_with_data(buf, data, size) \ + buf = NET_BUF_SIMPLE(size); \ + os_mbuf_copyinto(buf, 0, data, size); + static inline void net_buf_simple_reset(struct os_mbuf *om) { om->om_len = 0; @@ -413,6 +417,7 @@ static inline unsigned int find_msb_set(u32_t op) #define CONFIG_BT_MESH_LPN_GROUPS MYNEWT_VAL(BLE_MESH_LPN_GROUPS) #define CONFIG_BT_MESH_ADV_BUF_COUNT MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT) +#define CONFIG_BT_MESH_SEG_BUFS MYNEWT_VAL(BLE_MESH_SEG_BUFS ) #define CONFIG_BT_MESH_FRIEND_QUEUE_SIZE MYNEWT_VAL(BLE_MESH_FRIEND_QUEUE_SIZE) #define CONFIG_BT_MESH_FRIEND_RECV_WIN MYNEWT_VAL(BLE_MESH_FRIEND_RECV_WIN) #define CONFIG_BT_MESH_LPN_POLL_TIMEOUT MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT) @@ -427,7 +432,10 @@ static inline unsigned int find_msb_set(u32_t op) #define CONFIG_BT_MESH_STORE_TIMEOUT MYNEWT_VAL(BLE_MESH_STORE_TIMEOUT) #define CONFIG_BT_MESH_IVU_DIVIDER MYNEWT_VAL(BLE_MESH_IVU_DIVIDER) #define CONFIG_BT_DEVICE_NAME MYNEWT_VAL(BLE_MESH_DEVICE_NAME) +#define CONFIG_BT_RX_SEG_MAX MYNEWT_VAL(BLE_MESH_RX_SEG_MAX) #define CONFIG_BT_MESH_TX_SEG_MAX MYNEWT_VAL(BLE_MESH_TX_SEG_MAX) +#define CONFIG_BT_MESH_RX_SEG_MAX MYNEWT_VAL(BLE_MESH_RX_SEG_MAX) +#define CONFIG_BT_MESH_RX_SEG_MSG_COUNT MYNEWT_VAL(BLE_MESH_RX_SEG_MSG_COUNT) #define CONFIG_BT_MESH_LABEL_COUNT MYNEWT_VAL(BLE_MESH_LABEL_COUNT) #define CONFIG_BT_MESH_NODE_COUNT MYNEWT_VAL(BLE_MESH_CDB_NODE_COUNT) #define CONFIG_BT_MESH_CDB BLE_MESH_CDB @@ -515,6 +523,56 @@ settings_load(void) #define BUILD_ASSERT(cond) _Static_assert(cond, "") + +/* Memory slabs/blocks */ + +/** Memory slab structure */ +struct k_mem_slab { + /** + * _wait_q_t is not required now, as we don't implement zephyr timeouts - + * if slab couldn't be allocated, we simply return error + */ + uint32_t num_blocks; /** number of memory blocks available for allocation */ + size_t block_size; /** size of single block */ + /** + * buffer for blocks - must be alligned to N-byte, where N is a power of 2. + * Minimal size of buffer is num_blocks * block_size + */ + char *buffer; + char *free_list; /** list of free memory blocks */ + uint32_t num_used; /** count of used memory blocks */ +}; + +struct k_mem_block_id { + uint32_t pool : 8; + uint32_t level : 4; + uint32_t block : 20; +}; + +struct k_mem_block { + void *data; + struct k_mem_block_id id; +}; + +extern void k_mem_slab_free(struct k_mem_slab *slab, void **mem); +extern int k_mem_slab_alloc(struct k_mem_slab *slab, void **mem); +static inline uint32_t k_mem_slab_num_free_get(struct k_mem_slab *slab) +{ + return slab->num_blocks - slab->num_used; +} + +/** slab_align must be power of 2 */ +#define K_MEM_SLAB_DEFINE(name, slab_block_size, slab_num_blocks, slab_align) \ + char _k_mem_slab_buf_##name[slab_num_blocks * slab_block_size]; \ + struct k_mem_slab name = { \ + slab_num_blocks, \ + slab_block_size, \ + _k_mem_slab_buf_##name, \ + NULL, \ + 0 \ + }; + + #ifdef __cplusplus } #endif diff --git a/nimble/host/mesh/src/friend.c b/nimble/host/mesh/src/friend.c index 75ae0b20e..8ab4f5895 100644 --- a/nimble/host/mesh/src/friend.c +++ b/nimble/host/mesh/src/friend.c @@ -259,7 +259,7 @@ int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct os_mbuf *buf) cfm.lpn_counter = msg->lpn_counter; bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR_CFM, &cfm, - sizeof(cfm), NULL, NULL, NULL); + sizeof(cfm), NULL, NULL); friend_clear(frnd); @@ -784,7 +784,7 @@ static void send_friend_clear(struct bt_mesh_friend *frnd) BT_DBG(""); bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req, - sizeof(req), NULL, &clear_sent_cb, frnd); + sizeof(req), &clear_sent_cb, frnd); } static void clear_timeout(struct ble_npl_event *work) diff --git a/nimble/host/mesh/src/glue.c b/nimble/host/mesh/src/glue.c index a4e9ca85f..82025bd55 100644 --- a/nimble/host/mesh/src/glue.c +++ b/nimble/host/mesh/src/glue.c @@ -870,6 +870,31 @@ void net_buf_slist_merge_slist(struct net_buf_slist_t *list, } } +/** Memory slab methods */ +extern void k_mem_slab_free(struct k_mem_slab *slab, void **mem) +{ + **(char ***)mem = slab->free_list; + slab->free_list = *(char **)mem; + slab->num_used--; +} + +extern int k_mem_slab_alloc(struct k_mem_slab *slab, void **mem) +{ + int result; + + if (slab->free_list != NULL) { + /* take a free block */ + *mem = slab->free_list; + slab->free_list = *(char **)(slab->free_list); + slab->num_used++; + result = 0; + } else { + *mem = NULL; + result = -ENOMEM; + } + return result; +} + #if MYNEWT_VAL(BLE_MESH_SETTINGS) int settings_bytes_from_str(char *val_str, void *vp, int *len) diff --git a/nimble/host/mesh/src/lpn.c b/nimble/host/mesh/src/lpn.c index 39e54331f..49c366e8d 100644 --- a/nimble/host/mesh/src/lpn.c +++ b/nimble/host/mesh/src/lpn.c @@ -190,7 +190,7 @@ static int send_friend_clear(void) BT_DBG(""); return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req, - sizeof(req), NULL, &clear_sent_cb, NULL); + sizeof(req), &clear_sent_cb, NULL); } static void clear_friendship(bool force, bool disable) @@ -308,7 +308,7 @@ static int send_friend_req(struct bt_mesh_lpn *lpn) BT_DBG(""); return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_REQ, &req, - sizeof(req), NULL, &friend_req_sent_cb, NULL); + sizeof(req), &friend_req_sent_cb, NULL); } static void req_sent(u16_t duration, int err, void *user_data) @@ -378,7 +378,7 @@ static int send_friend_poll(void) } err = bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_POLL, &fsn, 1, - NULL, &req_sent_cb, NULL); + &req_sent_cb, NULL); if (err == 0) { lpn->pending_poll = 0; lpn->sent_req = TRANS_CTL_OP_FRIEND_POLL; @@ -689,8 +689,8 @@ static bool sub_update(u8_t op) req.xact = lpn->xact_next++; - if (bt_mesh_ctl_send(&tx, op, &req, 1 + g * 2, NULL, - &req_sent_cb, NULL) < 0) { + if (bt_mesh_ctl_send(&tx, op, &req, 1 + g * 2, + &req_sent_cb, NULL) < 0) { group_zero(lpn->pending); return false; } diff --git a/nimble/host/mesh/src/net.c b/nimble/host/mesh/src/net.c index 276082741..ca561a17e 100644 --- a/nimble/host/mesh/src/net.c +++ b/nimble/host/mesh/src/net.c @@ -724,62 +724,6 @@ u32_t bt_mesh_next_seq(void) return seq; } -int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct os_mbuf *buf, - bool new_key, const struct bt_mesh_send_cb *cb, - void *cb_data) -{ - const u8_t *enc, *priv; - u32_t seq; - u16_t dst; - int err; - - BT_DBG("net_idx 0x%04x new_key %u len %u", sub->net_idx, new_key, - buf->om_len); - - enc = sub->keys[new_key].enc; - priv = sub->keys[new_key].privacy; - - err = bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv); - if (err) { - BT_ERR("deobfuscate failed (err %d)", err); - return err; - } - - err = bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_TX, false); - if (err) { - BT_ERR("decrypt failed (err %d)", err); - return err; - } - - seq = bt_mesh_next_seq(); - sys_put_be24(seq, &buf->om_data[2]); - - /* Get destination, in case it's a proxy client */ - dst = DST(buf->om_data); - - err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, false); - if (err) { - BT_ERR("encrypt failed (err %d)", err); - return err; - } - - err = bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv); - if (err) { - BT_ERR("obfuscate failed (err %d)", err); - return err; - } - - if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && - bt_mesh_proxy_relay(buf, dst) && - BT_MESH_ADDR_IS_UNICAST(dst)) { - send_cb_finalize(cb, cb_data); - } else { - bt_mesh_adv_send(buf, cb, cb_data); - } - - return 0; -} - static void bt_mesh_net_local(struct ble_npl_event *work) { struct os_mbuf *buf; diff --git a/nimble/host/mesh/src/net.h b/nimble/host/mesh/src/net.h index cef09bb2c..529314ce6 100644 --- a/nimble/host/mesh/src/net.h +++ b/nimble/host/mesh/src/net.h @@ -344,10 +344,6 @@ int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct os_mbuf *buf, int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf, const struct bt_mesh_send_cb *cb, void *cb_data); -int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct os_mbuf *buf, - bool new_key, const struct bt_mesh_send_cb *cb, - void *cb_data); - int bt_mesh_net_decode(struct os_mbuf *data, enum bt_mesh_net_if net_if, struct bt_mesh_net_rx *rx, struct os_mbuf *buf); diff --git a/nimble/host/mesh/src/transport.c b/nimble/host/mesh/src/transport.c index 8d68dfe1c..36fd513c0 100644 --- a/nimble/host/mesh/src/transport.c +++ b/nimble/host/mesh/src/transport.c @@ -13,6 +13,7 @@ #include #include "mesh/mesh.h" +#include "mesh/glue.h" #include "mesh_priv.h" #include "crypto.h" @@ -26,13 +27,6 @@ #include "transport.h" #include "testing.h" -/* The transport layer needs at least three buffers for itself to avoid - * deadlocks. Ensure that there are a sufficient number of advertising - * buffers available compared to the maximum supported outgoing segment - * count. - */ -BUILD_ASSERT(CONFIG_BT_MESH_ADV_BUF_COUNT >= (CONFIG_BT_MESH_TX_SEG_MAX + 3)); - #define AID_MASK ((u8_t)(BIT_MASK(6))) #define SEG(data) ((data)[0] >> 7) @@ -40,7 +34,7 @@ BUILD_ASSERT(CONFIG_BT_MESH_ADV_BUF_COUNT >= (CONFIG_BT_MESH_TX_SEG_MAX + 3)); #define AID(data) ((data)[0] & AID_MASK) #define ASZMIC(data) (((data)[1] >> 7) & 1) -#define APP_MIC_LEN(aszmic) ((aszmic) ? 8 : 4) +#define APP_MIC_LEN(aszmic) ((aszmic) ? BT_MESH_MIC_LONG : BT_MESH_MIC_SHORT) #define UNSEG_HDR(akf, aid) ((akf << 6) | (aid & AID_MASK)) #define SEG_HDR(akf, aid) (UNSEG_HDR(akf, aid) | 0x80) @@ -72,11 +66,21 @@ BUILD_ASSERT(CONFIG_BT_MESH_ADV_BUF_COUNT >= (CONFIG_BT_MESH_TX_SEG_MAX + 3)); static struct seg_tx { struct bt_mesh_subnet *sub; - struct os_mbuf *seg[CONFIG_BT_MESH_TX_SEG_MAX]; + void *seg[CONFIG_BT_MESH_TX_SEG_MAX]; u64_t seq_auth; + u16_t src; u16_t dst; + u16_t len; + u8_t hdr; + u8_t xmit; u8_t seg_n:5, /* Last segment index */ - new_key:1; /* New/old key */ + ctl:1, + aszmic:1, + friend_cred:1; + u8_t seg_o:5, + started:1, /* Start cb called */ + sending:1, /* Sending is in progress */ + blocked:1; /* Blocked by ongoing tx */ u8_t nack_count; /* Number of unacked segs */ u8_t ttl; u8_t seg_pending:5, /* Number of segments pending */ @@ -88,22 +92,23 @@ static struct seg_tx { static struct seg_rx { struct bt_mesh_subnet *sub; + void *seg[CONFIG_BT_MESH_TX_SEG_MAX]; u64_t seq_auth; - u8_t seg_n:5, - ctl:1, - in_use:1, - obo:1; - u8_t hdr; - u8_t ttl; u16_t src; u16_t dst; + u16_t len; + u8_t hdr; + u8_t seg_n:5, + ctl:1, + in_use:1, + obo:1; + u8_t ttl; u32_t block; u32_t last; struct k_delayed_work ack; - struct os_mbuf *buf; -} seg_rx[MYNEWT_VAL(BLE_MESH_RX_SEG_MSG_COUNT)] = { - [0 ... (MYNEWT_VAL(BLE_MESH_RX_SEG_MSG_COUNT) - 1)] = { 0 }, -}; +} seg_rx[CONFIG_BT_MESH_RX_SEG_MSG_COUNT]; + +K_MEM_SLAB_DEFINE(segs, BT_MESH_APP_SEG_SDU_MAX, CONFIG_BT_MESH_SEG_BUFS, 4); static u16_t hb_sub_dst = BT_MESH_ADDR_UNASSIGNED; @@ -113,13 +118,11 @@ void bt_mesh_set_hb_sub_dst(u16_t addr) } static int send_unseg(struct bt_mesh_net_tx *tx, struct os_mbuf *sdu, - const struct bt_mesh_send_cb *cb, void *cb_data) + const struct bt_mesh_send_cb *cb, void *cb_data, + const u8_t *ctl_op) { struct os_mbuf *buf; - BT_DBG("src 0x%04x dst 0x%04x app_idx 0x%04x sdu_len %u", - tx->src, tx->ctx->addr, tx->ctx->app_idx, sdu->om_len); - buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, tx->xmit, BUF_TIMEOUT); if (!buf) { BT_ERR("Out of network buffers"); @@ -128,7 +131,9 @@ static int send_unseg(struct bt_mesh_net_tx *tx, struct os_mbuf *sdu, net_buf_reserve(buf, BT_MESH_NET_HDR_LEN); - if (BT_MESH_IS_DEV_KEY(tx->ctx->app_idx)) { + if (ctl_op) { + net_buf_add_u8(buf, TRANS_CTL_HDR(*ctl_op, 0)); + } else if (BT_MESH_IS_DEV_KEY(tx->ctx->app_idx)) { net_buf_add_u8(buf, UNSEG_HDR(0, 0)); } else { net_buf_add_u8(buf, UNSEG_HDR(1, tx->aid)); @@ -166,6 +171,15 @@ send: return bt_mesh_net_send(tx, buf, cb, cb_data); } +static inline u8_t seg_len(bool ctl) +{ + if (ctl) { + return BT_MESH_CTL_SEG_SDU_MAX; + } else { + return BT_MESH_APP_SEG_SDU_MAX; + } +} + bool bt_mesh_tx_in_progress(void) { int i; @@ -181,12 +195,39 @@ bool bt_mesh_tx_in_progress(void) static void seg_tx_done(struct seg_tx *tx, u8_t seg_idx) { - BT_MESH_ADV(tx->seg[seg_idx])->busy = 0U; - net_buf_unref(tx->seg[seg_idx]); + k_mem_slab_free(&segs, (void **)&tx->seg[seg_idx]); tx->seg[seg_idx] = NULL; tx->nack_count--; } +static bool seg_tx_blocks(struct seg_tx *tx, u16_t src, u16_t dst) +{ + return (tx->src == src) && (tx->dst == dst); +} + +static void seg_tx_unblock_check(struct seg_tx *tx) +{ + struct seg_tx *blocked = NULL; + int i; + + /* Unblock the first blocked tx with the same params. */ + for (i = 0; i < ARRAY_SIZE(seg_tx); ++i) { + if (&seg_tx[i] != tx && + seg_tx[i].blocked && + seg_tx_blocks(tx, seg_tx[i].src, seg_tx[i].dst) && + (!blocked || seg_tx[i].seq_auth < blocked->seq_auth)) { + blocked = &seg_tx[i]; + } + } + + if (blocked) { + BT_DBG("Unblocked 0x%04x", + (u16_t)(blocked->seq_auth & TRANS_SEQ_ZERO_MASK)); + blocked->blocked = false; + k_delayed_work_submit(&blocked->retransmit, 0); + } +} + static void seg_tx_reset(struct seg_tx *tx) { int i; @@ -197,7 +238,9 @@ static void seg_tx_reset(struct seg_tx *tx) tx->cb_data = NULL; tx->seq_auth = 0; tx->sub = NULL; + tx->src = BT_MESH_ADDR_UNASSIGNED; tx->dst = BT_MESH_ADDR_UNASSIGNED; + tx->blocked = false; if (!tx->nack_count) { return; @@ -211,6 +254,8 @@ static void seg_tx_reset(struct seg_tx *tx) seg_tx_done(tx, i); } + tx->nack_count = 0; + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_IVU_PENDING)) { BT_DBG("Proceding with pending IV Update"); @@ -228,6 +273,8 @@ static inline void seg_tx_complete(struct seg_tx *tx, int err) const struct bt_mesh_send_cb *cb = tx->cb; void *cb_data = tx->cb_data; + seg_tx_unblock_check(tx); + seg_tx_reset(tx); if (cb && cb->end) { @@ -237,32 +284,49 @@ static inline void seg_tx_complete(struct seg_tx *tx, int err) static void schedule_retransmit(struct seg_tx *tx) { + if (!tx->nack_count) { + return; + } + if (--tx->seg_pending) { return; } - if (!BT_MESH_ADDR_IS_UNICAST(tx->dst) && !tx->attempts) { + /* Group address sending should stop when the last segment goes out for + * the last time. Normally, this happens after the attempts counter has + * been set to 0. For self-rx, this callback is called inline, which + * means that the attempt counter is still 1. Since this is group based + * sar, we know that segments aren't removed by acks, so seg_o will be + * the last segment that goes out. + */ + if (!BT_MESH_ADDR_IS_UNICAST(tx->dst) && + (!tx->attempts || + (tx->sending && tx->attempts == 1 && tx->seg_n == tx->seg_o))) { BT_DBG("SDU TX complete"); seg_tx_complete(tx, 0); return; } - k_delayed_work_submit(&tx->retransmit, SEG_RETRANSMIT_TIMEOUT(tx)); -} - -static void seg_first_send_start(u16_t duration, int err, void *user_data) -{ - struct seg_tx *tx = user_data; - - if (tx->cb && tx->cb->start) { - tx->cb->start(duration, err, tx->cb_data); - } + /* If we haven't gone through all the segments for this attempt yet, + * (likely because of a buffer allocation failure or because we + * called this from inside bt_mesh_net_send), we should continue the + * retransmit immediately, as we just freed up a tx buffer. + */ + k_delayed_work_submit(&tx->retransmit, + (tx->sending || !tx->seg_o) ? + SEG_RETRANSMIT_TIMEOUT(tx) : + 0); } static void seg_send_start(u16_t duration, int err, void *user_data) { struct seg_tx *tx = user_data; + if (!tx->started && tx->cb && tx->cb->start) { + tx->cb->start(duration, err, tx->cb_data); + tx->started = 1U; + } + /* If there's an error in transmitting the 'sent' callback will never * be called. Make sure that we kick the retransmit timer also in this * case since otherwise we risk the transmission of becoming stale. @@ -279,52 +343,90 @@ static void seg_sent(int err, void *user_data) schedule_retransmit(tx); } -static const struct bt_mesh_send_cb first_sent_cb = { - .start = seg_first_send_start, - .end = seg_sent, -}; - static const struct bt_mesh_send_cb seg_sent_cb = { .start = seg_send_start, .end = seg_sent, }; +static void seg_tx_buf_build(struct seg_tx *tx, u8_t seg_o, + struct os_mbuf *buf) +{ + u16_t seq_zero = tx->seq_auth & TRANS_SEQ_ZERO_MASK; + u8_t len = MIN(seg_len(tx->ctl), tx->len - (seg_len(tx->ctl) * seg_o)); + + net_buf_simple_add_u8(buf, tx->hdr); + net_buf_simple_add_u8(buf, (tx->aszmic << 7) | seq_zero >> 6); + net_buf_simple_add_u8(buf, (((seq_zero & 0x3f) << 2) | (seg_o >> 3))); + net_buf_simple_add_u8(buf, ((seg_o & 0x07) << 5) | tx->seg_n); + net_buf_simple_add_mem(buf, tx->seg[seg_o], len); +} + static void seg_tx_send_unacked(struct seg_tx *tx) { - int i, err; + struct bt_mesh_msg_ctx ctx = { + .net_idx = tx->sub->net_idx, + /* App idx only used by network to detect control messages: */ + .app_idx = (tx->ctl ? BT_MESH_KEY_UNUSED : 0), + .addr = tx->dst, + .send_rel = true, + .send_ttl = tx->ttl, + }; + struct bt_mesh_net_tx net_tx = { + .sub = tx->sub, + .ctx = &ctx, + .src = tx->src, + .xmit = tx->xmit, + .friend_cred = tx->friend_cred, + .aid = tx->hdr & AID_MASK, + }; - if (!(tx->attempts--)) { + if (!tx->attempts) { BT_ERR("Ran out of retransmit attempts"); seg_tx_complete(tx, -ETIMEDOUT); return; } - BT_DBG("Attempts: %u", tx->attempts); + BT_DBG("SeqZero: 0x%04x Attempts: %u", + (u16_t)(tx->seq_auth & TRANS_SEQ_ZERO_MASK), tx->attempts); - for (i = 0; i <= tx->seg_n; i++) { - struct os_mbuf *seg = tx->seg[i]; + tx->sending = 1U; + for (; tx->seg_o <= tx->seg_n; tx->seg_o++) { + struct os_mbuf *seg; + int err; + + if (!tx->seg[tx->seg_o]) { + continue; + } + + seg = bt_mesh_adv_create(BT_MESH_ADV_DATA, tx->xmit, + BUF_TIMEOUT); if (!seg) { - continue; + BT_DBG("Allocating segment failed"); + tx->sending = 0U; + return; } - if (BT_MESH_ADV(seg)->busy) { - BT_DBG("Skipping segment that's still advertising"); - continue; - } + net_buf_reserve(seg, BT_MESH_NET_HDR_LEN); + seg_tx_buf_build(tx, tx->seg_o, seg); tx->seg_pending++; - BT_DBG("resending %u/%u", i, tx->seg_n); + BT_DBG("Sending %u/%u", tx->seg_o, tx->seg_n); - err = bt_mesh_net_resend(tx->sub, seg, tx->new_key, - &seg_sent_cb, tx); + err = bt_mesh_net_send(&net_tx, seg, &seg_sent_cb, tx); if (err) { BT_ERR("Sending segment failed"); seg_tx_complete(tx, -EIO); + BT_DBG("Sending segment failed"); + tx->seg_pending--; + tx->sending = 0U; return; } } + tx->sending = 0U; + tx->seg_o = 0U; + tx->attempts--; } static void seg_retransmit(struct ble_npl_event *work) @@ -334,31 +436,25 @@ static void seg_retransmit(struct ble_npl_event *work) } static int send_seg(struct bt_mesh_net_tx *net_tx, struct os_mbuf *sdu, - const struct bt_mesh_send_cb *cb, void *cb_data) + const struct bt_mesh_send_cb *cb, void *cb_data, + u8_t *ctl_op) { - u8_t seg_hdr, seg_o; - u16_t seq_zero; +{ + bool blocked = false; struct seg_tx *tx; + u8_t seg_o; int i; BT_DBG("src 0x%04x dst 0x%04x app_idx 0x%04x aszmic %u sdu_len %u", net_tx->src, net_tx->ctx->addr, net_tx->ctx->app_idx, net_tx->aszmic, sdu->om_len); - if (sdu->om_len < 1) { - BT_ERR("Zero-length SDU not allowed"); - return -EINVAL; - } - - if (sdu->om_len > BT_MESH_TX_SDU_MAX) { - BT_ERR("Not enough segment buffers for length %u", sdu->om_len); - return -EMSGSIZE; - } - for (tx = NULL, i = 0; i < ARRAY_SIZE(seg_tx); i++) { - if (!seg_tx[i].nack_count) { + if (seg_tx[i].nack_count) { + blocked |= seg_tx_blocks(&seg_tx[i], net_tx->src, + net_tx->ctx->addr); + } else if (!tx) { tx = &seg_tx[i]; - break; } } @@ -367,23 +463,32 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, struct os_mbuf *sdu, return -EBUSY; } - if (BT_MESH_IS_DEV_KEY(net_tx->ctx->app_idx)) { - seg_hdr = SEG_HDR(0, 0); + if (ctl_op) { + tx->hdr = TRANS_CTL_HDR(*ctl_op, 1); + } else if (BT_MESH_IS_DEV_KEY(net_tx->ctx->app_idx)) { + tx->hdr = SEG_HDR(0, 0); } else { - seg_hdr = SEG_HDR(1, net_tx->aid); + tx->hdr = SEG_HDR(1, net_tx->aid); } - seg_o = 0; + tx->src = net_tx->src; tx->dst = net_tx->ctx->addr; - tx->seg_n = (sdu->om_len - 1) / 12; + tx->seg_n = (sdu->om_len - 1) / seg_len(!!ctl_op); + tx->seg_o = 0; + tx->len = sdu->om_len; tx->nack_count = tx->seg_n + 1; tx->seq_auth = SEQ_AUTH(BT_MESH_NET_IVI_TX, bt_mesh.seq); tx->sub = net_tx->sub; - tx->new_key = net_tx->sub->kr_flag; tx->cb = cb; tx->cb_data = cb_data; tx->attempts = SEG_RETRANSMIT_ATTEMPTS; tx->seg_pending = 0; + tx->xmit = net_tx->xmit; + tx->aszmic = net_tx->aszmic; + tx->friend_cred = net_tx->friend_cred; + tx->blocked = blocked; + tx->started = 0; + tx->ctl = !!ctl_op; if (net_tx->ctx->send_ttl == BT_MESH_TTL_DEFAULT) { tx->ttl = bt_mesh_default_ttl_get(); @@ -391,9 +496,8 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, struct os_mbuf *sdu, tx->ttl = net_tx->ctx->send_ttl; } - seq_zero = tx->seq_auth & TRANS_SEQ_ZERO_MASK; - - BT_DBG("SeqZero 0x%04x", seq_zero); + BT_DBG("SeqZero 0x%04x (segs: %u)", + (u16_t)(tx->seq_auth & TRANS_SEQ_ZERO_MASK), tx->nack_count); if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !bt_mesh_friend_queue_has_space(tx->sub->net_idx, net_tx->src, @@ -407,65 +511,46 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, struct os_mbuf *sdu, } for (seg_o = 0; sdu->om_len; seg_o++) { - struct os_mbuf *seg; + void *buf; u16_t len; int err; - seg = bt_mesh_adv_create(BT_MESH_ADV_DATA, net_tx->xmit, - BUF_TIMEOUT); - if (!seg) { + err = k_mem_slab_alloc(&segs, &buf); + if (err) { BT_ERR("Out of segment buffers"); seg_tx_reset(tx); return -ENOBUFS; } - net_buf_reserve(seg, BT_MESH_NET_HDR_LEN); - - net_buf_add_u8(seg, seg_hdr); - net_buf_add_u8(seg, (net_tx->aszmic << 7) | seq_zero >> 6); - net_buf_add_u8(seg, (((seq_zero & 0x3f) << 2) | - (seg_o >> 3))); - net_buf_add_u8(seg, ((seg_o & 0x07) << 5) | tx->seg_n); - - len = min(sdu->om_len, 12); - net_buf_add_mem(seg, sdu->om_data, len); - net_buf_simple_pull(sdu, len); + len = MIN(sdu->om_len, seg_len(!!ctl_op)); + memcpy(buf, net_buf_simple_pull_mem(sdu, len), len); + BT_DBG("seg %u: %s", seg_o, bt_hex(buf, len)); if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { enum bt_mesh_friend_pdu_type type; + struct os_mbuf *seg = NET_BUF_SIMPLE(16); + seg_tx_buf_build(tx, seg_o, seg); + if (seg_o == tx->seg_n) { type = BT_MESH_FRIEND_PDU_COMPLETE; } else { type = BT_MESH_FRIEND_PDU_PARTIAL; } - if (bt_mesh_friend_enqueue_tx(net_tx, type, - &tx->seq_auth, - tx->seg_n + 1, - seg) && + if (bt_mesh_friend_enqueue_tx( + net_tx, type, ctl_op ? NULL : &tx->seq_auth, + tx->seg_n + 1, seg) && BT_MESH_ADDR_IS_UNICAST(net_tx->ctx->addr)) { /* PDUs for a specific Friend should only go * out through the Friend Queue. */ - net_buf_unref(seg); + k_mem_slab_free(&segs, &buf); continue; } } - tx->seg[seg_o] = net_buf_ref(seg); - - BT_DBG("Sending %u/%u", seg_o, tx->seg_n); - tx->seg_pending++; - - err = bt_mesh_net_send(net_tx, seg, - seg_o ? &seg_sent_cb : &first_sent_cb, - tx); - if (err) { - BT_ERR("Sending segment failed"); - seg_tx_reset(tx); - return err; - } + tx->seg[seg_o] = buf; } /* This can happen if segments only went into the Friend Queue */ @@ -477,6 +562,21 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, struct os_mbuf *sdu, * with the Friend Queue. */ send_cb_finalize(cb, cb_data); + return 0; + } + + if (blocked) { + /* Move the sequence number, so we don't end up creating + * another segmented transmission with the same SeqZero while + * this one is blocked. + */ + bt_mesh_next_seq(); + BT_DBG("Blocked."); + return 0; + } + + seg_tx_send_unacked(tx); + } if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && @@ -511,12 +611,22 @@ int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct os_mbuf *msg, u8_t aid; int err; + if (msg->om_len < 1) { + BT_ERR("Zero-length SDU not allowed"); + return -EINVAL; + } + + if (msg->om_len > BT_MESH_TX_SDU_MAX) { + BT_ERR("Not enough segment buffers for length %u", msg->om_len); + return -EMSGSIZE; + } + if (net_buf_simple_tailroom(msg) < 4) { BT_ERR("Insufficient tailroom for Transport MIC"); return -EINVAL; } - if (msg->om_len > 11) { + if (msg->om_len > BT_MESH_SDU_UNSEG_MAX) { tx->ctx->send_rel = 1; tx->ctx->send_rel = true; } @@ -553,9 +663,9 @@ int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct os_mbuf *msg, } if (tx->ctx->send_rel) { - err = send_seg(tx, msg, cb, cb_data); + err = send_seg(tx, msg, cb, cb_data, NULL); } else { - err = send_unseg(tx, msg, cb, cb_data); + err = send_unseg(tx, msg, cb, cb_data, NULL); } return err; @@ -630,28 +740,118 @@ static bool is_replay(struct bt_mesh_net_rx *rx, struct bt_mesh_rpl **match) return true; } -static int sdu_recv(struct bt_mesh_net_rx *rx, u32_t seq, u8_t hdr, - u8_t aszmic, struct os_mbuf *buf) +static void seg_rx_assemble(struct seg_rx *rx, struct os_mbuf *buf, + u8_t aszmic) +{ + int i; + + net_buf_simple_reset(buf); + + for (i = 0; i <= rx->seg_n; i++) { + net_buf_simple_add_mem(buf, rx->seg[i], + MIN(seg_len(rx->ctl), + rx->len - (i * seg_len(rx->ctl)))); + } + + /* Adjust the length to not contain the MIC at the end */ + if (!rx->ctl) { + buf->om_len -= APP_MIC_LEN(aszmic); + } +} + +static int remote_devkey_decrypt(struct bt_mesh_net_rx *rx, u32_t seq, u8_t *ad, + u8_t aszmic, struct os_mbuf *buf, + struct os_mbuf *sdu) +{ + struct bt_mesh_cdb_node *node; + int err; + + if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { + return -ENOENT; + } + + /* We will try our local devkey separately. */ + if (bt_mesh_elem_find(rx->ctx.addr)) { + return -ENOENT; + } + + /* + * There is no way of knowing if we should use our + * local DevKey or the remote DevKey to decrypt the + * message so we must try both. + */ + node = bt_mesh_cdb_node_get(rx->ctx.addr); + if (node == NULL) { + BT_ERR("No node found for addr 0x%04x", rx->ctx.addr); + return -EINVAL; + } + + err = bt_mesh_app_decrypt(node->dev_key, true, aszmic, buf, sdu, ad, + rx->ctx.addr, rx->ctx.recv_dst, seq, + BT_MESH_NET_IVI_RX(rx)); + if (err) { + BT_DBG("Unable to decrypt with node DevKey"); + return -EINVAL; + } + + return 0; +} + +static int app_key_decrypt(struct bt_mesh_net_rx *rx, + struct bt_mesh_app_key *key, u32_t seq, u8_t *ad, + u8_t hdr, u8_t aszmic, struct os_mbuf *buf, + struct os_mbuf *sdu) +{ + struct bt_mesh_app_keys *keys; + int err; + + /* Check that this AppKey matches received net_idx */ + if (key->net_idx != rx->sub->net_idx) { + return -EINVAL; + } + + if (rx->new_key && key->updated) { + keys = &key->keys[1]; + } else { + keys = &key->keys[0]; + } + + /* Check that the AppKey ID matches */ + if (AID(&hdr) != keys->id) { + return -EINVAL; + } + + err = bt_mesh_app_decrypt(keys->val, false, aszmic, buf, sdu, ad, + rx->ctx.addr, rx->ctx.recv_dst, seq, + BT_MESH_NET_IVI_RX(rx)); + if (err) { + BT_WARN("Unable to decrypt with AppKey 0x%03x", key->app_idx); + } + + return err; +} + +static int sdu_recv_unseg(struct bt_mesh_net_rx *rx, u8_t hdr, + struct os_mbuf *buf) { struct os_mbuf *sdu = - NET_BUF_SIMPLE(MYNEWT_VAL(BLE_MESH_RX_SDU_MAX) - 4); + NET_BUF_SIMPLE(BT_MESH_SDU_UNSEG_MAX); u8_t *ad; u16_t i; int err = 0; - BT_DBG("ASZMIC %u AKF %u AID 0x%02x", aszmic, AKF(&hdr), AID(&hdr)); + BT_DBG("AKF %u AID 0x%02x", AKF(&hdr), AID(&hdr)); BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); - if (buf->om_len < 1 + APP_MIC_LEN(aszmic)) { + if (buf->om_len < 1 + APP_MIC_LEN(0)) { BT_ERR("Too short SDU + MIC"); - err = -EINVAL; - goto done; + return -EINVAL; } if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !rx->local_match) { BT_DBG("Ignoring PDU for LPN 0x%04x of this Friend", rx->ctx.recv_dst); - goto done; + return 0; } if (BT_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) { @@ -661,86 +861,140 @@ static int sdu_recv(struct bt_mesh_net_rx *rx, u32_t seq, u8_t hdr, } /* Adjust the length to not contain the MIC at the end */ - buf->om_len -= APP_MIC_LEN(aszmic); + buf->om_len -= APP_MIC_LEN(0); if (!AKF(&hdr)) { - net_buf_simple_init(sdu, 0); - err = bt_mesh_app_decrypt(bt_mesh.dev_key, true, aszmic, buf, - sdu, ad, rx->ctx.addr, - rx->ctx.recv_dst, seq, - BT_MESH_NET_IVI_RX(rx)); - if (err) { - BT_WARN("Unable to decrypt with local DevKey"); - } else { - rx->ctx.app_idx = BT_MESH_KEY_DEV_LOCAL; - bt_mesh_model_recv(rx, sdu); - goto done; - } - - if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { - struct bt_mesh_cdb_node *node; - - /* - * There is no way of knowing if we should use our - * local DevKey or the remote DevKey to decrypt the - * message so we must try both. - */ - - node = bt_mesh_cdb_node_get(rx->ctx.addr); - if (node == NULL) { - BT_ERR("No node found for addr 0x%04x", - rx->ctx.addr); - return -EINVAL; - } - - net_buf_simple_init(sdu, 0); - err = bt_mesh_app_decrypt(node->dev_key, true, aszmic, - buf, sdu, ad, rx->ctx.addr, - rx->ctx.recv_dst, seq, - BT_MESH_NET_IVI_RX(rx)); - if (err) { - BT_ERR("Unable to decrypt with node DevKey"); - return -EINVAL; - } - + /* Attempt remote dev key first, as that is only available for + * provisioner devices, which normally don't interact with nodes + * that know their local dev key. + */ + net_buf_simple_reset(sdu); + err = remote_devkey_decrypt(rx, rx->seq, ad, 0, buf, sdu); + if (!err) { rx->ctx.app_idx = BT_MESH_KEY_DEV_REMOTE; bt_mesh_model_recv(rx, sdu); return 0; } + net_buf_simple_reset(sdu); + err = bt_mesh_app_decrypt(bt_mesh.dev_key, true, 0, buf, sdu, + ad, rx->ctx.addr, rx->ctx.recv_dst, + rx->seq, BT_MESH_NET_IVI_RX(rx)); + if (err) { + BT_ERR("Unable to decrypt with local DevKey"); + return err; + } + + rx->ctx.app_idx = BT_MESH_KEY_DEV_LOCAL; + bt_mesh_model_recv(rx, sdu); + return 0; + } + + for (i = 0U; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + net_buf_simple_reset(sdu); + err = app_key_decrypt(rx, &bt_mesh.app_keys[i], rx->seq, ad, + hdr, 0, buf, sdu); + + if (err) { + continue; + } + + rx->ctx.app_idx = key->app_idx; + + bt_mesh_model_recv(rx, sdu); + return 0; + } + + BT_WARN("No matching AppKey"); + + return -EINVAL; +} + +static int sdu_recv_seg(struct seg_rx *seg, u8_t hdr, u8_t aszmic, + struct bt_mesh_net_rx *rx) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(BT_MESH_RX_SDU_MAX); + struct os_mbuf *sdu; + u32_t seq = (seg->seq_auth & 0xffffff); + u8_t *ad; + u16_t i; + int err; + + BT_DBG("ASZMIC %u AKF %u AID 0x%02x", aszmic, AKF(&hdr), AID(&hdr)); + + if (seg->len < 1 + APP_MIC_LEN(aszmic)) { + BT_ERR("Too short SDU + MIC"); return -EINVAL; } - for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { - struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; - struct bt_mesh_app_keys *keys; + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !rx->local_match) { + BT_DBG("Ignoring PDU for LPN 0x%04x of this Friend", + rx->ctx.recv_dst); + return 0; + } - /* Check that this AppKey matches received net_idx */ - if (key->net_idx != rx->sub->net_idx) { - continue; - } + if (BT_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) { + ad = bt_mesh_label_uuid_get(rx->ctx.recv_dst); + } else { + ad = NULL; + } - if (rx->new_key && key->updated) { - keys = &key->keys[1]; - } else { - keys = &key->keys[0]; - } + /* Decrypting in place to avoid creating two assembly buffers. + * We'll reassemble the buffer from the segments before each decryption + * attempt. + */ + if (!AKF(&hdr)) { + /* Attempt remote dev key first, as that is only available for + * provisioner devices, which normally don't interact with nodes + * that know their local dev key. + */ + seg_rx_assemble(seg, buf, aszmic); + + net_buf_simple_init_with_data(sdu, buf->om_data, + seg->len - APP_MIC_LEN(aszmic)); + sdu->om_len = 0; /* Check that the AppKey ID matches */ - if (AID(&hdr) != keys->id) { - continue; + err = remote_devkey_decrypt(rx, seq, ad, aszmic, buf, sdu); + if (!err) { + rx->ctx.app_idx = BT_MESH_KEY_DEV_REMOTE; + bt_mesh_model_recv(rx, sdu); + return 0; } - net_buf_simple_init(sdu, 0); - err = bt_mesh_app_decrypt(keys->val, false, aszmic, buf, + seg_rx_assemble(seg, buf, aszmic); + net_buf_simple_init_with_data(sdu, buf->om_data, + seg->len - APP_MIC_LEN(aszmic)); + sdu->om_len = 0; + + err = bt_mesh_app_decrypt(bt_mesh.dev_key, true, aszmic, buf, sdu, ad, rx->ctx.addr, rx->ctx.recv_dst, seq, BT_MESH_NET_IVI_RX(rx)); if (err) { - BT_WARN("Unable to decrypt with AppKey 0x%03x", - key->app_idx); - continue; + BT_ERR("Unable to decrypt with local DevKey"); + return err; + } + rx->ctx.app_idx = BT_MESH_KEY_DEV_LOCAL; + bt_mesh_model_recv(rx, sdu); + return 0; + } + + for (i = 0U; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + seg_rx_assemble(seg, buf, aszmic); + net_buf_simple_init_with_data(sdu, buf->om_data, + seg->len - APP_MIC_LEN(aszmic)); + sdu->om_len = 0; + + err = app_key_decrypt(rx, &bt_mesh.app_keys[i], seq, ad, hdr, + aszmic, buf, sdu); + if (err) { + continue; } rx->ctx.app_idx = key->app_idx; @@ -985,7 +1239,7 @@ static int trans_unseg(struct os_mbuf *buf, struct bt_mesh_net_rx *rx, return 0; } - return sdu_recv(rx, rx->seq, hdr, 0, buf); + return sdu_recv_unseg(rx, hdr, buf); } } @@ -1014,138 +1268,27 @@ static inline s32_t ack_timeout(struct seg_rx *rx) return max(to, K_MSEC(400)); } -static int ctl_send_unseg(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data, - size_t data_len, u64_t *seq_auth, - const struct bt_mesh_send_cb *cb, void *cb_data) +int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data, + size_t data_len, const struct bt_mesh_send_cb *cb, void *cb_data) { struct os_mbuf *buf; - buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, tx->xmit, BUF_TIMEOUT); - if (!buf) { - BT_ERR("Out of transport buffers"); - return -ENOBUFS; + net_buf_simple_init_with_data(buf, data, data_len); + + if (data_len > BT_MESH_SDU_UNSEG_MAX) { + tx->ctx->send_rel = true; } - net_buf_reserve(buf, BT_MESH_NET_HDR_LEN); + tx->ctx->app_idx = BT_MESH_KEY_UNUSED; - net_buf_add_u8(buf, TRANS_CTL_HDR(ctl_op, 0)); - - net_buf_add_mem(buf, data, data_len); - - if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { - if (bt_mesh_friend_enqueue_tx(tx, BT_MESH_FRIEND_PDU_SINGLE, - seq_auth, 1, buf) && - BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) { - /* PDUs for a specific Friend should only go - * out through the Friend Queue. - */ - net_buf_unref(buf); - return 0; - } - } - - return bt_mesh_net_send(tx, buf, cb, cb_data); -} - -static int ctl_send_seg(struct bt_mesh_net_tx *tx, u8_t ctl_op, - void *data, size_t data_len, u64_t *seq_auth, - const struct bt_mesh_send_cb *cb, void *cb_data) -{ - u8_t seg_o = 0; - u16_t seq_zero; - struct seg_tx *tx_seg; - int i; - u16_t unsent = data_len; - - for (tx_seg = NULL, i = 0; i < ARRAY_SIZE(seg_tx); i++) { - if (!seg_tx[i].nack_count) { - tx_seg = &seg_tx[i]; - break; - } - } - - if (!tx_seg) { - BT_ERR("No multi-segment message contexts available"); - return -EBUSY; - } - - tx_seg->dst = tx->ctx->addr; - tx_seg->seg_n = (data_len - 1) / 8; - tx_seg->nack_count = tx_seg->seg_n + 1; - tx_seg->seq_auth = SEQ_AUTH(BT_MESH_NET_IVI_TX, bt_mesh.seq); - tx_seg->sub = tx->sub; - tx_seg->new_key = tx->sub->kr_flag; - tx_seg->cb = cb; - tx_seg->cb_data = cb_data; - tx_seg->attempts = SEG_RETRANSMIT_ATTEMPTS; - tx_seg->seg_pending = 0; - - if (tx->ctx->send_ttl == BT_MESH_TTL_DEFAULT) { - tx_seg->ttl = bt_mesh_default_ttl_get(); - } else { - tx_seg->ttl = tx->ctx->send_ttl; - } - - seq_zero = tx_seg->seq_auth & TRANS_SEQ_ZERO_MASK; - - for (seg_o = 0; seg_o <= tx_seg->seg_n; seg_o++) { - struct os_mbuf *seg; - u16_t len; - int err; - - seg = bt_mesh_adv_create(BT_MESH_ADV_DATA, tx->xmit, - BUF_TIMEOUT); - if (!seg) { - BT_ERR("Out of segment buffers"); - seg_tx_reset(tx_seg); - return -ENOBUFS; - } - - net_buf_reserve(seg, BT_MESH_NET_HDR_LEN); - net_buf_add_u8(seg, TRANS_CTL_HDR(ctl_op, 1)); - net_buf_add_u8(seg, (tx->aszmic << 7) | seq_zero >> 6); - net_buf_add_u8(seg, (((seq_zero & 0x3f) << 2) | (seg_o >> 3))); - net_buf_add_u8(seg, ((seg_o & 0x07) << 5) | tx_seg->seg_n); - if (unsent < 8) { - len = unsent; - } else { - len = 8; - } - - net_buf_add_mem(seg, (char *)data + (data_len - unsent), len); - unsent -= len; - tx_seg->seg[seg_o] = net_buf_ref(seg); - - BT_DBG("Sending %u/%u", seg_o, tx_seg->seg_n); - tx_seg->seg_pending++; - - err = bt_mesh_net_send(tx, seg, - seg_o ? &seg_sent_cb : &first_sent_cb, - tx_seg); - if (err) { - BT_ERR("Sending segment failed"); - seg_tx_reset(tx_seg); - return err; - } - } - - return 0; -} - -int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data, - size_t data_len, u64_t *seq_auth, - const struct bt_mesh_send_cb *cb, void *cb_data) -{ BT_DBG("src 0x%04x dst 0x%04x ttl 0x%02x ctl 0x%02x", tx->src, tx->ctx->addr, tx->ctx->send_ttl, ctl_op); BT_DBG("len %zu: %s", data_len, bt_hex(data, data_len)); - if (data_len <= 11) { - return ctl_send_unseg(tx, ctl_op, data, data_len, seq_auth, - cb, cb_data); + if (tx->ctx->send_rel) { + return send_seg(tx, buf, cb, cb_data, &ctl_op); } else { - return ctl_send_seg(tx, ctl_op, data, data_len, seq_auth, - cb, cb_data); + return send_unseg(tx, buf, cb, cb_data, &ctl_op); } } @@ -1187,11 +1330,13 @@ static int send_ack(struct bt_mesh_subnet *sub, u16_t src, u16_t dst, sys_put_be32(block, &buf[2]); return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_ACK, buf, sizeof(buf), - NULL, NULL, NULL); + NULL, NULL); } static void seg_rx_reset(struct seg_rx *rx, bool full_reset) { + int i; + BT_DBG("rx %p", rx); k_delayed_work_cancel(&rx->ack); @@ -1203,6 +1348,11 @@ static void seg_rx_reset(struct seg_rx *rx, bool full_reset) &rx->seq_auth); } + for (i = 0; i <= rx->seg_n; i++) { + k_mem_slab_free(&segs, &rx->seg[i]); + rx->seg[i] = NULL; + } + rx->in_use = 0; /* We don't always reset these values since we need to be able to @@ -1240,18 +1390,9 @@ static void seg_ack(struct ble_npl_event *work) k_delayed_work_submit(&rx->ack, ack_timeout(rx)); } -static inline u8_t seg_len(bool ctl) -{ - if (ctl) { - return 8; - } else { - return 12; - } -} - static inline bool sdu_len_is_ok(bool ctl, u8_t seg_n) { - return ((seg_n * seg_len(ctl) + 1) <= MYNEWT_VAL(BLE_MESH_RX_SDU_MAX)); + return (seg_n < CONFIG_BT_MESH_RX_SEG_MAX); } static struct seg_rx *seg_rx_find(struct bt_mesh_net_rx *net_rx, @@ -1316,7 +1457,15 @@ static struct seg_rx *seg_rx_alloc(struct bt_mesh_net_rx *net_rx, const u8_t *hdr, const u64_t *seq_auth, u8_t seg_n) { - int i; + int i, j; + + /* No race condition on this check, as this function only executes in + * the collaborative Bluetooth rx thread: + */ + if (k_mem_slab_num_free_get(&segs) < seg_n) { + BT_WARN("Not enough segments for incoming message"); + return NULL; + } for (i = 0; i < ARRAY_SIZE(seg_rx); i++) { struct seg_rx *rx = &seg_rx[i]; @@ -1326,7 +1475,6 @@ static struct seg_rx *seg_rx_alloc(struct bt_mesh_net_rx *net_rx, } rx->in_use = 1; - net_buf_simple_init(rx->buf, 0); rx->sub = net_rx->sub; rx->ctl = net_rx->ctl; rx->seq_auth = *seq_auth; @@ -1337,6 +1485,14 @@ static struct seg_rx *seg_rx_alloc(struct bt_mesh_net_rx *net_rx, rx->dst = net_rx->ctx.recv_dst; rx->block = 0; + /* Allocating everything at the beginning to avoid any failures + * after initial rx. Could reduce overall buffer usage by + * allocating as we go? + */ + for (j = 0; j <= seg_n; j++) { + k_mem_slab_alloc(&segs, &rx->seg[j]); + } + BT_DBG("New RX context. Block Complete 0x%08x", (unsigned) BLOCK_COMPLETE(seg_n)); @@ -1498,11 +1654,11 @@ found_rx: */ if (seg_o == seg_n) { /* Set the expected final buffer length */ - rx->buf->om_len = seg_n * seg_len(rx->ctl) + buf->om_len; + rx->len = seg_n * seg_len(rx->ctl) + buf->om_len; BT_DBG("Target len %u * %u + %u = %u", seg_n, seg_len(rx->ctl), - buf->om_len, rx->buf->om_len); + buf->om_len, rx->len); - if (rx->buf->om_len > MYNEWT_VAL(BLE_MESH_RX_SDU_MAX)) { + if (rx->len > BT_MESH_RX_SDU_MAX) { BT_ERR("Too large SDU len"); send_ack(net_rx->sub, net_rx->ctx.recv_dst, net_rx->ctx.addr, net_rx->ctx.send_ttl, @@ -1525,8 +1681,7 @@ found_rx: k_delayed_work_submit(&rx->ack, ack_timeout(rx)); } - /* Location in buffer can be calculated based on seg_o & rx->ctl */ - memcpy(rx->buf->om_data + (seg_o * seg_len(rx->ctl)), buf->om_data, buf->om_len); + memcpy(rx->seg[seg_o], buf->om_data, buf->om_len); BT_DBG("Received %u/%u", seg_o, seg_n); @@ -1551,10 +1706,11 @@ found_rx: net_rx->ctx.send_ttl, seq_auth, rx->block, rx->obo); if (net_rx->ctl) { - err = ctl_recv(net_rx, *hdr, rx->buf, seq_auth); + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_RX_CTL_MAX); + seg_rx_assemble(rx, sdu, 0U); + err = ctl_recv(net_rx, *hdr, sdu, seq_auth); } else { - err = sdu_recv(net_rx, (rx->seq_auth & 0xffffff), *hdr, - ASZMIC(hdr), rx->buf); + err = sdu_recv_seg(rx, *hdr, ASZMIC(hdr), net_rx); } seg_rx_reset(rx, false); @@ -1694,7 +1850,6 @@ void bt_mesh_trans_init(void) * For now we increase MSYS_1_BLOCK_COUNT */ for (i = 0; i < ARRAY_SIZE(seg_rx); i++) { - seg_rx[i].buf = NET_BUF_SIMPLE(MYNEWT_VAL(BLE_MESH_RX_SDU_MAX)); k_delayed_work_init(&seg_rx[i].ack, seg_ack); k_delayed_work_add_arg(&seg_rx[i].ack, &seg_rx[i]); } @@ -1755,7 +1910,7 @@ int bt_mesh_heartbeat_send(const struct bt_mesh_send_cb *cb, void *cb_data) BT_DBG("InitTTL %u feat 0x%04x", cfg->hb_pub.ttl, feat); return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb), - NULL, cb, cb_data); + cb, cb_data); } int bt_mesh_app_key_get(const struct bt_mesh_subnet *subnet, u16_t app_idx, diff --git a/nimble/host/mesh/src/transport.h b/nimble/host/mesh/src/transport.h index 8bcbff071..d9b4781a6 100644 --- a/nimble/host/mesh/src/transport.h +++ b/nimble/host/mesh/src/transport.h @@ -11,7 +11,12 @@ #define TRANS_SEQ_AUTH_NVAL 0xffffffffffffffff -#define BT_MESH_TX_SDU_MAX (CONFIG_BT_MESH_TX_SEG_MAX * 12) +#define BT_MESH_SDU_UNSEG_MAX 11 +#define BT_MESH_CTL_SEG_SDU_MAX 8 +#define BT_MESH_APP_SEG_SDU_MAX 12 +#define BT_MESH_TX_SDU_MAX (CONFIG_BT_MESH_TX_SEG_MAX * BT_MESH_APP_SEG_SDU_MAX) +#define BT_MESH_RX_SDU_MAX (CONFIG_BT_MESH_RX_SEG_MAX * BT_MESH_APP_SEG_SDU_MAX) +#define BT_MESH_RX_CTL_MAX (CONFIG_BT_MESH_RX_SEG_MAX * BT_MESH_CTL_SEG_SDU_MAX) #define TRANS_SEQ_ZERO_MASK ((u16_t)BIT_MASK(13)) #define TRANS_CTL_OP_MASK ((u8_t)BIT_MASK(7)) @@ -87,8 +92,7 @@ void bt_mesh_rx_reset(void); void bt_mesh_tx_reset(void); int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data, - size_t data_len, u64_t *seq_auth, - const struct bt_mesh_send_cb *cb, void *cb_data); + size_t data_len, const struct bt_mesh_send_cb *cb, void *cb_data); int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct os_mbuf *msg, const struct bt_mesh_send_cb *cb, void *cb_data); diff --git a/nimble/host/mesh/syscfg.yml b/nimble/host/mesh/syscfg.yml index a61cbc54f..6339f305f 100644 --- a/nimble/host/mesh/syscfg.yml +++ b/nimble/host/mesh/syscfg.yml @@ -186,7 +186,7 @@ syscfg.defs: description: > Maximum number of simultaneous outgoing multi-segment and/or reliable messages. - value: 4 + value: 1 BLE_MESH_RX_SEG_MSG_COUNT: description: > @@ -194,15 +194,39 @@ syscfg.defs: reliable messages. value: 2 - BLE_MESH_RX_SDU_MAX: + BLE_MESH_SEG_BUFS: description: > - Maximum incoming Upper Transport Access PDU length. This - determines also how many segments incoming segmented messages - can have. Each segment can contain 12 bytes, so this value should - be set to a multiple of 12 to avoid wasted memory. The minimum - requirement is 2 segments (24 bytes) whereas the maximum supported - by the Mesh specification is 32 segments (384 bytes). - value: 72 + The incoming and outgoing segmented messages allocate their + segments from the same pool. Each segment is a 12 byte block, + and may only be used by one message at the time. + + Outgoing messages will allocate their segments at the start of the + transmission, and release them one by one as soon as they have been + acknowledged by the receiver. Incoming messages allocate all their + segments at the start of the transaction, and won't release them until + the message is fully received. + value: + 64 + + BLE_MESH_RX_SEG_MAX: + description: > + Maximum number of segments supported for incoming messages. + This value should typically be fine-tuned based on what + models the local node supports, i.e. what's the largest + message payload that the node needs to be able to receive. + This value affects memory and call stack consumption, which + is why the default is lower than the maximum that the + specification would allow (32 segments). + + The maximum incoming SDU size is 12 times this number (out of + which 4 or 8 bytes is used for the Transport Layer MIC). For + example, 5 segments means the maximum SDU size is 60 bytes, + which leaves 56 bytes for application layer data using a + 4-byte MIC and 52 bytes using an 8-byte MIC. + value: + 3 + + BLE_MESH_TX_SEG_MAX: description: > @@ -210,20 +234,15 @@ syscfg.defs: This value should typically be fine-tuned based on what models the local node supports, i.e. what's the largest message payload that the node needs to be able to send. - This value affects memory and call stack consumption, which - is why the default is lower than the maximum that the - specification would allow (32 segments). + This value affects memory consumption, which is why the + default is lower than the maximum that the specification + would allow (32 segments). The maximum outgoing SDU size is 12 times this number (out of which 4 or 8 bytes is used for the Transport Layer MIC). For example, 5 segments means the maximum SDU size is 60 bytes, which leaves 56 bytes for application layer data using a 4-byte MIC and 52 bytes using an 8-byte MIC. - - Be sure to specify a sufficient number of advertising buffers - when setting this option to a higher value. There must be at - least three more advertising buffers (BT_MESH_ADV_BUF_COUNT) - as there are outgoing segments. value: 3 BLE_MESH_SEG_RETRANSMIT_ATTEMPTS: diff --git a/porting/examples/linux_blemesh/include/syscfg/syscfg.h b/porting/examples/linux_blemesh/include/syscfg/syscfg.h index 7880d3de6..341a1dcf7 100644 --- a/porting/examples/linux_blemesh/include/syscfg/syscfg.h +++ b/porting/examples/linux_blemesh/include/syscfg/syscfg.h @@ -1070,8 +1070,12 @@ #define MYNEWT_VAL_BLE_MESH_RPL_STORE_TIMEOUT (5) #endif -#ifndef MYNEWT_VAL_BLE_MESH_RX_SDU_MAX -#define MYNEWT_VAL_BLE_MESH_RX_SDU_MAX (72) +#ifndef MYNEWT_VAL_BLE_MESH_SEG_BUFS +#define MYNEWT_VAL_BLE_MESH_SEG_BUFS (72) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_RX_SEG_MAX +#define MYNEWT_VAL_BLE_MESH_RX_SEG_MAX (3) #endif #ifndef MYNEWT_VAL_BLE_MESH_RX_SEG_MSG_COUNT @@ -1143,6 +1147,10 @@ #define MYNEWT_VAL_BLE_MESH_TX_SEG_MAX (6) #endif +#ifndef MYNEWT_VAL_BLE_MESH_RX_SEG_MSG_COUNT +#define MYNEWT_VAL_BLE_MESH_RX_SEG_MSG_COUNT (3) +#endif + #ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_MSG_COUNT #define MYNEWT_VAL_BLE_MESH_TX_SEG_MSG_COUNT (4) #endif