Bluetooth: Mesh: Send provisioning PDUs with randomized delay
This implements the following statement from the section 5.3.3: Each Generic Provisioning PDU shall be sent after a random delay between 20 and 50 milliseconds. Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
parent
fcfc99a21d
commit
54c048989d
1 changed files with 175 additions and 51 deletions
|
@ -67,6 +67,8 @@ enum {
|
|||
ADV_LINK_INVALID, /* Error occurred during provisioning */
|
||||
ADV_ACK_PENDING, /* An acknowledgment is being sent */
|
||||
ADV_PROVISIONER, /* The link was opened as provisioner */
|
||||
ADV_LINK_ACK_SENDING, /* Link Ack tx was scheduled but not finished. */
|
||||
ADV_SENDING, /* Sending any PDU */
|
||||
|
||||
ADV_NUM_FLAGS,
|
||||
};
|
||||
|
@ -100,15 +102,30 @@ struct pb_adv {
|
|||
/* Transaction timeout in seconds */
|
||||
uint8_t timeout;
|
||||
|
||||
/* Pending outgoing adv(s) */
|
||||
/* Pending outgoing adv(s) (Link Open, Gen Trans Start and Gen Trans Cont) */
|
||||
struct bt_mesh_adv *adv[3];
|
||||
|
||||
/* Index of the next adv to be sent */
|
||||
int next;
|
||||
|
||||
prov_bearer_send_complete_t cb;
|
||||
|
||||
void *cb_data;
|
||||
|
||||
/* Retransmit timer */
|
||||
struct k_work_delayable retransmit;
|
||||
|
||||
/* Unacked adv buffers (Link Ack, Link Close and Gen Trans Ack). Array size is
|
||||
* hardcoded to 2 allowing to send Gen Trans Ack and Link Close at the same time.
|
||||
*/
|
||||
struct unacked_adv_ctx {
|
||||
struct bt_mesh_adv *adv;
|
||||
prov_bearer_send_complete_t cb;
|
||||
void *cb_data;
|
||||
} unacked[2];
|
||||
|
||||
/* Last sent unacked[] buffer */
|
||||
int last_unacked;
|
||||
} tx;
|
||||
|
||||
/* Protocol timeout */
|
||||
|
@ -132,28 +149,124 @@ static void link_close(struct prov_rx *rx, struct net_buf_simple *buf);
|
|||
static void prov_link_close(enum prov_bearer_link_status status);
|
||||
static void close_link(enum prov_bearer_link_status status);
|
||||
|
||||
static void buf_sent(int err, void *user_data)
|
||||
{
|
||||
enum prov_bearer_link_status reason = (enum prov_bearer_link_status)(int)user_data;
|
||||
static void tx_work_handler(struct k_work *work);
|
||||
static K_WORK_DELAYABLE_DEFINE(tx_work, tx_work_handler);
|
||||
|
||||
if (atomic_test_and_clear_bit(link.flags, ADV_LINK_CLOSING)) {
|
||||
close_link(reason);
|
||||
static void tx_schedule(void)
|
||||
{
|
||||
uint16_t random_delay;
|
||||
|
||||
if (atomic_test_bit(link.flags, ADV_SENDING)) {
|
||||
LOG_DBG("Another tx is in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
(void)bt_rand(&random_delay, sizeof(random_delay));
|
||||
random_delay = 20 + (random_delay % 30);
|
||||
|
||||
LOG_DBG("Next PDU delayed by %ums", random_delay);
|
||||
|
||||
(void)k_work_schedule(&tx_work, K_MSEC(random_delay));
|
||||
}
|
||||
|
||||
static void buf_start(uint16_t duration, int err, void *user_data)
|
||||
static int send_unacked(struct bt_mesh_adv *adv, prov_bearer_send_complete_t cb,
|
||||
void *cb_data)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(link.tx.unacked); i++) {
|
||||
if (link.tx.unacked[i].adv != NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
link.tx.unacked[i].adv = adv;
|
||||
link.tx.unacked[i].cb = cb;
|
||||
link.tx.unacked[i].cb_data = cb_data;
|
||||
|
||||
tx_schedule();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_WRN("No memory to send unacked PDU: %s", bt_hex(adv->b.data, adv->b.len));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void send_reliable(void)
|
||||
{
|
||||
/* Dropping next tx adv index to start transmission from the first adv buffer. */
|
||||
link.tx.next = 0;
|
||||
|
||||
tx_schedule();
|
||||
}
|
||||
|
||||
static void delayed_adv_send_end(int err, void *user_data)
|
||||
{
|
||||
bool unacked = (bool)user_data;
|
||||
struct unacked_adv_ctx *unacked_adv = &link.tx.unacked[link.tx.last_unacked];
|
||||
|
||||
if (unacked && unacked_adv->adv != NULL) {
|
||||
if (unacked_adv->cb) {
|
||||
unacked_adv->cb(err, unacked_adv->cb_data);
|
||||
}
|
||||
|
||||
bt_mesh_adv_unref(unacked_adv->adv);
|
||||
unacked_adv->adv = NULL;
|
||||
}
|
||||
|
||||
atomic_clear_bit(link.flags, ADV_SENDING);
|
||||
tx_schedule();
|
||||
}
|
||||
|
||||
static void delayed_adv_send_start(uint16_t duration, int err, void *user_data)
|
||||
{
|
||||
if (err) {
|
||||
buf_sent(err, user_data);
|
||||
delayed_adv_send_end(err, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_mesh_send_cb buf_sent_cb = {
|
||||
.start = buf_start,
|
||||
.end = buf_sent,
|
||||
static const struct bt_mesh_send_cb delayed_adv_send_cb = {
|
||||
.start = delayed_adv_send_start,
|
||||
.end = delayed_adv_send_end,
|
||||
};
|
||||
|
||||
static void tx_work_handler(struct k_work *work)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Send Link Ack, Link Close and Gen Trans Ack first. */
|
||||
for (i = 0; i < ARRAY_SIZE(link.tx.unacked); i++) {
|
||||
int idx = (i + link.tx.last_unacked) % ARRAY_SIZE(link.tx.unacked);
|
||||
struct unacked_adv_ctx *unacked = &link.tx.unacked[idx];
|
||||
|
||||
if (!unacked->adv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
atomic_set_bit(link.flags, ADV_SENDING);
|
||||
bt_mesh_adv_send(unacked->adv, &delayed_adv_send_cb, (void *)true);
|
||||
|
||||
link.tx.last_unacked = idx;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send Trans Start, Trans Cont and Link Open */
|
||||
if (link.tx.next >= ARRAY_SIZE(link.tx.adv) || link.tx.adv[link.tx.next] == NULL) {
|
||||
LOG_DBG("All PDUs were sent");
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_set_bit(link.flags, ADV_SENDING);
|
||||
bt_mesh_adv_send(link.tx.adv[link.tx.next], &delayed_adv_send_cb, (void *)false);
|
||||
|
||||
link.tx.next++;
|
||||
|
||||
if (link.tx.next == ARRAY_SIZE(link.tx.adv) || link.tx.adv[link.tx.next] == NULL) {
|
||||
/* All ack-able PDUs are sent. Now we can run the retransmit timer. */
|
||||
LOG_DBG("Starting retransmit timer");
|
||||
k_work_reschedule(&link.tx.retransmit, RETRANSMIT_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t last_seg(uint16_t len)
|
||||
{
|
||||
if (len <= START_PAYLOAD_MAX) {
|
||||
|
@ -168,9 +281,11 @@ static uint8_t last_seg(uint16_t len)
|
|||
static void free_segments(void)
|
||||
{
|
||||
int i;
|
||||
bool canceled = false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(link.tx.adv); i++) {
|
||||
struct bt_mesh_adv *adv = link.tx.adv[i];
|
||||
int err;
|
||||
|
||||
if (!adv) {
|
||||
break;
|
||||
|
@ -180,14 +295,23 @@ static void free_segments(void)
|
|||
|
||||
/* Terminate active adv */
|
||||
if (adv->ctx.busy == 0U) {
|
||||
bt_mesh_adv_terminate(adv);
|
||||
err = bt_mesh_adv_terminate(adv);
|
||||
if (err == 0) {
|
||||
canceled = true;
|
||||
}
|
||||
} else {
|
||||
/* Mark as canceled */
|
||||
adv->ctx.busy = 0U;
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
bt_mesh_adv_unref(adv);
|
||||
}
|
||||
|
||||
if (canceled) {
|
||||
atomic_clear_bit(link.flags, ADV_SENDING);
|
||||
tx_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t next_transaction_id(uint8_t id)
|
||||
|
@ -268,7 +392,7 @@ static struct bt_mesh_adv *adv_create(uint8_t retransmits)
|
|||
return adv;
|
||||
}
|
||||
|
||||
static void ack_complete(uint16_t duration, int err, void *user_data)
|
||||
static void ack_complete(int err, void *user_data)
|
||||
{
|
||||
LOG_DBG("xact 0x%x complete", (uint8_t)link.tx.pending_ack);
|
||||
atomic_clear_bit(link.flags, ADV_ACK_PENDING);
|
||||
|
@ -323,12 +447,9 @@ static void protocol_timeout(struct k_work *work)
|
|||
|
||||
static void gen_prov_ack_send(uint8_t xact_id)
|
||||
{
|
||||
static const struct bt_mesh_send_cb cb = {
|
||||
.start = ack_complete,
|
||||
};
|
||||
const struct bt_mesh_send_cb *complete;
|
||||
struct bt_mesh_adv *adv;
|
||||
bool pending = atomic_test_and_set_bit(link.flags, ADV_ACK_PENDING);
|
||||
int err;
|
||||
|
||||
LOG_DBG("xact_id 0x%x", xact_id);
|
||||
|
||||
|
@ -343,19 +464,18 @@ static void gen_prov_ack_send(uint8_t xact_id)
|
|||
return;
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
complete = NULL;
|
||||
} else {
|
||||
if (!pending) {
|
||||
link.tx.pending_ack = xact_id;
|
||||
complete = &cb;
|
||||
}
|
||||
|
||||
net_buf_simple_add_be32(&adv->b, link.id);
|
||||
net_buf_simple_add_u8(&adv->b, xact_id);
|
||||
net_buf_simple_add_u8(&adv->b, GPC_ACK);
|
||||
|
||||
bt_mesh_adv_send(adv, complete, NULL);
|
||||
bt_mesh_adv_unref(adv);
|
||||
err = send_unacked(adv, pending ? NULL : ack_complete, NULL);
|
||||
if (err) {
|
||||
atomic_clear_bit(link.flags, ADV_ACK_PENDING);
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_prov_cont(struct prov_rx *rx, struct net_buf_simple *buf)
|
||||
|
@ -591,29 +711,6 @@ static void gen_prov_recv(struct prov_rx *rx, struct net_buf_simple *buf)
|
|||
* TX
|
||||
******************************************************************************/
|
||||
|
||||
static void send_reliable(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(link.tx.adv); i++) {
|
||||
struct bt_mesh_adv *adv = link.tx.adv[i];
|
||||
|
||||
if (!adv) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (adv->ctx.busy) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_DBG("%u bytes: %s", adv->b.len, bt_hex(adv->b.data, adv->b.len));
|
||||
|
||||
bt_mesh_adv_send(adv, NULL, NULL);
|
||||
}
|
||||
|
||||
k_work_reschedule(&link.tx.retransmit, RETRANSMIT_TIMEOUT);
|
||||
}
|
||||
|
||||
static void prov_retransmit(struct k_work *work)
|
||||
{
|
||||
LOG_DBG("");
|
||||
|
@ -669,8 +766,22 @@ static int bearer_ctl_send(struct bt_mesh_adv *adv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void buf_sent(int err, void *user_data)
|
||||
{
|
||||
enum prov_bearer_link_status reason = (enum prov_bearer_link_status)(int)user_data;
|
||||
|
||||
atomic_clear_bit(link.flags, ADV_LINK_ACK_SENDING);
|
||||
|
||||
if (atomic_test_and_clear_bit(link.flags, ADV_LINK_CLOSING)) {
|
||||
close_link(reason);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int bearer_ctl_send_unacked(struct bt_mesh_adv *adv, void *user_data)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!adv) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -678,10 +789,12 @@ static int bearer_ctl_send_unacked(struct bt_mesh_adv *adv, void *user_data)
|
|||
prov_clear_tx();
|
||||
k_work_reschedule(&link.prot_timer, bt_mesh_prov_protocol_timeout_get());
|
||||
|
||||
bt_mesh_adv_send(adv, &buf_sent_cb, user_data);
|
||||
bt_mesh_adv_unref(adv);
|
||||
err = send_unacked(adv, &buf_sent, user_data);
|
||||
if (err) {
|
||||
bt_mesh_adv_unref(adv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int prov_send_adv(struct net_buf_simple *msg,
|
||||
|
@ -772,8 +885,13 @@ static void link_open(struct prov_rx *rx, struct net_buf_simple *buf)
|
|||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Resending link ack");
|
||||
if (atomic_test_bit(link.flags, ADV_LINK_ACK_SENDING)) {
|
||||
LOG_DBG("Still sending Link Ack");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore errors, message will be attempted again if we keep receiving link open: */
|
||||
atomic_set_bit(link.flags, ADV_LINK_ACK_SENDING);
|
||||
(void)bearer_ctl_send_unacked(
|
||||
ctl_adv_create(LINK_ACK, NULL, 0, RETRANSMITS_ACK),
|
||||
(void *)PROV_BEARER_LINK_STATUS_SUCCESS);
|
||||
|
@ -789,6 +907,7 @@ static void link_open(struct prov_rx *rx, struct net_buf_simple *buf)
|
|||
atomic_set_bit(link.flags, ADV_LINK_ACTIVE);
|
||||
net_buf_simple_reset(link.rx.buf);
|
||||
|
||||
atomic_set_bit(link.flags, ADV_LINK_ACK_SENDING);
|
||||
err = bearer_ctl_send_unacked(
|
||||
ctl_adv_create(LINK_ACK, NULL, 0, RETRANSMITS_ACK),
|
||||
(void *)PROV_BEARER_LINK_STATUS_SUCCESS);
|
||||
|
@ -923,6 +1042,8 @@ static int prov_link_accept(const struct prov_bearer_cb *cb, void *cb_data)
|
|||
|
||||
static void prov_link_close(enum prov_bearer_link_status status)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (atomic_test_and_set_bit(link.flags, ADV_LINK_CLOSING)) {
|
||||
return;
|
||||
}
|
||||
|
@ -934,9 +1055,12 @@ static void prov_link_close(enum prov_bearer_link_status status)
|
|||
*/
|
||||
link.tx.timeout = CLOSING_TIMEOUT;
|
||||
/* Ignore errors, the link will time out eventually if this doesn't get sent */
|
||||
bearer_ctl_send_unacked(
|
||||
err = bearer_ctl_send_unacked(
|
||||
ctl_adv_create(LINK_CLOSE, &status, 1, RETRANSMITS_LINK_CLOSE),
|
||||
(void *)status);
|
||||
if (err) {
|
||||
close_link(status);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_mesh_pb_adv_init(void)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue