Commit 54c04898 authored by Pavel Vasilyev's avatar Pavel Vasilyev Committed by Henrik Brix Andersen
Browse files

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: default avatarPavel Vasilyev <pavel.vasilyev@nordicsemi.no>
parent fcfc99a2
Loading
Loading
Loading
Loading
+174 −50
Original line number Diff line number Diff line
@@ -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)
static void tx_work_handler(struct k_work *work);
static K_WORK_DELAYABLE_DEFINE(tx_work, tx_work_handler);

static void tx_schedule(void)
{
	enum prov_bearer_link_status reason = (enum prov_bearer_link_status)(int)user_data;
	uint16_t random_delay;

	if (atomic_test_and_clear_bit(link.flags, ADV_LINK_CLOSING)) {
		close_link(reason);
	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 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;
		}

static void buf_start(uint16_t duration, int err, void *user_data)
		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);
	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)