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:
Pavel Vasilyev 2024-01-22 14:50:19 +01:00 committed by Henrik Brix Andersen
commit 54c048989d

View file

@ -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)