[蓝牙 Mesh & Zephyr]-[001]-Mesh beacon

1. Mesh beacon

AD-Type
这里关注 0x2B Mesh beacon

hci.h

#define BT_DATA_MESH_BEACON             0x2b /* Mesh Beacon */

2. 接收或者发送 一个 mesh beacon

2.1. 初始化

typedef enum {
	BT_MESH_PROV_ADV   = BIT(0),
	BT_MESH_PROV_GATT  = BIT(1),
} bt_mesh_prov_bearer_t;

/** @brief Enable specific provisioning bearers
 *
 *  Enable one or more provisioning bearers.
 *
 *  @param bearers Bit-wise or of provisioning bearers.
 *
 *  @return Zero on success or (negative) error code otherwise.
 */
int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers)
{
	if (bt_mesh_is_provisioned()) {
		return -EALREADY;
	}

	if (IS_ENABLED(CONFIG_BT_DEBUG)) {
		const struct bt_mesh_prov *prov = bt_mesh_prov_get();
		struct bt_uuid_128 uuid = { .uuid.type = BT_UUID_TYPE_128 };

		memcpy(uuid.val, prov->uuid, 16);
		BT_INFO("Device UUID: %s", bt_uuid_str(&uuid.uuid));
	}

	if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) &&
	    (bearers & BT_MESH_PROV_ADV)) {
		/* Make sure we're scanning for provisioning inviations */
		bt_mesh_scan_enable();
		/* Enable unprovisioned beacon sending */
		bt_mesh_beacon_enable();
	}

	if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) &&
	    (bearers & BT_MESH_PROV_GATT)) {
		bt_mesh_proxy_prov_enable();
		bt_mesh_adv_update();
	}

	return 0;
}

这里使能 ADV 和 GATT 两个 bearer。

bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);

2.2. 从 MESH ADV 接收一个 mesh beacon

2.2.1 MESH ADV

bt_mesh_scan_enable 使能广播扫描


int bt_mesh_scan_enable(void)
{
	struct bt_le_scan_param scan_param = {
			.type       = BT_HCI_LE_SCAN_PASSIVE,
			.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE,
			.interval   = MESH_SCAN_INTERVAL,
			.window     = MESH_SCAN_WINDOW };
	int err;

	BT_DBG("");

	err = bt_le_scan_start(&scan_param, bt_mesh_scan_cb);
	if (err && err != -EALREADY) {
		BT_ERR("starting scan failed (err %d)", err);
		return err;
	}

	return 0;
}

配置扫描参数和回调函数。

/** @brief Start (LE) scanning
 *
 *  Start LE scanning with given parameters and provide results through
 *  the specified callback.
 *
 *  @param param Scan parameters.
 *  @param cb Callback to notify scan results.
 *
 *  @return Zero on success or error code otherwise, positive in case
 *  of protocol error or negative (POSIX) in case of stack internal error
 */
int bt_le_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb)
{
	int err;

	if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
		return -EAGAIN;
	}

	/* Check that the parameters have valid values */
	if (!valid_le_scan_param(param)) {
		return -EINVAL;
	}

	/* Return if active scan is already enabled */
	if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) {
		return -EALREADY;
	}

	if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) {
		err = set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE);
		if (err) {
			atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN);
			return err;
		}
	}

	atomic_set_bit_to(bt_dev.flags, BT_DEV_SCAN_FILTER_DUP,
			  param->filter_dup);

	err = start_le_scan(param->type, param->interval, param->window);
	if (err) {
		atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN);
		return err;
	}

	scan_dev_found_cb = cb;

	return 0;
}

获取到广播,通过回调函数回调。


static void le_adv_report(struct net_buf *buf)
{
	u8_t num_reports = net_buf_pull_u8(buf);
	struct bt_hci_evt_le_advertising_info *info;

	BT_DBG("Adv number of reports %u",  num_reports);

	while (num_reports--) {
		bt_addr_le_t id_addr;
		s8_t rssi;

		if (buf->len < sizeof(*info)) {
			BT_ERR("Unexpected end of buffer");
			break;
		}

		info = net_buf_pull_mem(buf, sizeof(*info));
		rssi = info->data[info->length];

		BT_DBG("%s event %u, len %u, rssi %d dBm",
		       bt_addr_le_str(&info->addr),
		       info->evt_type, info->length, rssi);

		if (info->addr.type == BT_ADDR_LE_PUBLIC_ID ||
		    info->addr.type == BT_ADDR_LE_RANDOM_ID) {
			bt_addr_le_copy(&id_addr, &info->addr);
			id_addr.type -= BT_ADDR_LE_PUBLIC_ID;
		} else {
			bt_addr_le_copy(&id_addr,
					find_id_addr(bt_dev.adv_id,
						     &info->addr));
		}

		if (scan_dev_found_cb) {
			struct net_buf_simple_state state;

			net_buf_simple_save(&buf->b, &state);

			buf->len = info->length;
			scan_dev_found_cb(&id_addr, rssi, info->evt_type,
					  &buf->b);

			net_buf_simple_restore(&buf->b, &state);
		}

#if defined(CONFIG_BT_CENTRAL)
		check_pending_conn(&id_addr, &info->addr, info->evt_type);
#endif /* CONFIG_BT_CENTRAL */

		net_buf_pull(buf, info->length + sizeof(rssi));
	}
}

扫描结果,根据 AD Type 进行处理,这里关注:BT_DATA_MESH_BEACON 0x2b


static void bt_mesh_scan_cb(const bt_addr_le_t *addr, s8_t rssi,
			    u8_t adv_type, struct net_buf_simple *buf)
{
	if (adv_type != BT_LE_ADV_NONCONN_IND) {
		return;
	}

	BT_DBG("len %u: %s", buf->len, bt_hex(buf->data, buf->len));

	while (buf->len > 1) {
		struct net_buf_simple_state state;
		u8_t len, type;

		len = net_buf_simple_pull_u8(buf);
		/* Check for early termination */
		if (len == 0U) {
			return;
		}

		if (len > buf->len) {
			BT_WARN("AD malformed");
			return;
		}

		net_buf_simple_save(buf, &state);

		type = net_buf_simple_pull_u8(buf);

		buf->len = len - 1;

		switch (type) {
		case BT_DATA_MESH_MESSAGE:
			bt_mesh_net_recv(buf, rssi, BT_MESH_NET_IF_ADV);
			break;
#if defined(CONFIG_BT_MESH_PB_ADV)
		case BT_DATA_MESH_PROV:
			bt_mesh_pb_adv_recv(buf);
			break;
#endif
		case BT_DATA_MESH_BEACON:
			bt_mesh_beacon_recv(buf);
			break;
		default:
			break;
		}

		net_buf_simple_restore(buf, &state);
		net_buf_simple_pull(buf, len);
	}
}

处理:BEACON_TYPE_SECURE


#define BEACON_TYPE_UNPROVISIONED  0x00
#define BEACON_TYPE_SECURE         0x01

void bt_mesh_beacon_recv(struct net_buf_simple *buf)
{
	u8_t type;

	BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len));

	if (buf->len < 1) {
		BT_ERR("Too short beacon");
		return;
	}

	type = net_buf_simple_pull_u8(buf);
	switch (type) {
	//这里不处理 Unprovidioned beacon 证明没有 Provisionner 角色 
	case BEACON_TYPE_UNPROVISIONED:
		BT_DBG("Ignoring unprovisioned device beacon");
		break;
	case BEACON_TYPE_SECURE:
		secure_beacon_recv(buf);
		break;
	default:
		BT_WARN("Unknown beacon type 0x%02x", type);
		break;
	}
}

下面的函数是根据这个图片描述的格式来处理的。
Table 3.55: Secure Network beacon format
Figure 3.49: Key Refresh procedure overview


static void secure_beacon_recv(struct net_buf_simple *buf)
{
	u8_t *data, *net_id, *auth;
	struct bt_mesh_subnet *sub;
	u32_t iv_index;
	bool new_key, kr_change, iv_change;
	u8_t flags;

	if (buf->len < 21) {
		BT_ERR("Too short secure beacon (len %u)", buf->len);
		return;
	}

	sub = cache_check(buf->data);
	if (sub) {
		/* We've seen this beacon before - just update the stats */
		goto update_stats;
	}

	/* So we can add to the cache if auth matches */
	data = buf->data;

	flags = net_buf_simple_pull_u8(buf);
	net_id = net_buf_simple_pull_mem(buf, 8);
	iv_index = net_buf_simple_pull_be32(buf);
	auth = buf->data;

	BT_DBG("flags 0x%02x id %s iv_index 0x%08x",
	       flags, bt_hex(net_id, 8), iv_index);

	sub = bt_mesh_subnet_find(net_id, flags, iv_index, auth, &new_key);
	if (!sub) {
		BT_DBG("No subnet that matched beacon");
		return;
	}
	//因为我们已收到新密钥,所以可以不处理旧密钥的 beacon
	if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !new_key) {
		BT_WARN("Ignoring Phase 2 KR Update secured using old key");
		return;
	}

	cache_add(data, sub);
    //1. 本设备在主网络中;
    //2. 与当前beacon 信息匹配的 sub(子网络)不是主网络,本设备也加入到了这个 subnet;
    //也就是收到了子网络的 beacon, 但为什么要忽略呢?
	/* If we have NetKey0 accept initiation only from it */
	if (bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY) &&
	    sub->net_idx != BT_MESH_KEY_PRIMARY) {
		BT_WARN("Ignoring secure beacon on non-primary subnet");
		goto update_stats;
	}

	BT_DBG("net_idx 0x%04x iv_index 0x%08x, current iv_index 0x%08x",
	       sub->net_idx, iv_index, bt_mesh.iv_index);

	if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) &&
	    (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ==
	     BT_MESH_IV_UPDATE(flags))) {
		bt_mesh_beacon_ivu_initiator(false);
	}

	iv_change = bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(flags));

	kr_change = bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(flags), new_key);
	if (kr_change) {
	//更新到对应的 beacon 配置,下次发送
		bt_mesh_net_beacon_update(sub);
	}

	if (iv_change) {
	//IV Index 更新,需要更新到所有subnet
		/* Update all subnets */
		bt_mesh_net_sec_update(NULL);
	} else if (kr_change) {
	//Key Refresh Flag = 1,只有对应的subnet 需要更新。
		/* Key Refresh without IV Update only impacts one subnet */
		bt_mesh_net_sec_update(sub);
	}

update_stats:
	if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED &&
	    sub->beacons_cur < 0xff) {
		sub->beacons_cur++;
	}
}

struct bt_mesh_subnet *bt_mesh_subnet_find() 函数是查找匹配的子网参数,其中sub->keys[] 大小为 2 是因为在更新密钥过程中需要存储2个密钥。

#define BT_MESH_KR_NORMAL         0x00
#define BT_MESH_KR_PHASE_1        0x01
#define BT_MESH_KR_PHASE_2        0x02
#define BT_MESH_KR_PHASE_3        0x03
//返回一个子网信息
struct bt_mesh_subnet *bt_mesh_subnet_find(const u8_t net_id[8], u8_t flags,
					   u32_t iv_index, const u8_t auth[8],
					   bool *new_key)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];

		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
			continue;
		}

		if (auth_match(&sub->keys[0], net_id, flags, iv_index, auth)) {
			*new_key = false;
			return sub;
		}

		if (sub->kr_phase == BT_MESH_KR_NORMAL) {
			continue;
		}

		if (auth_match(&sub->keys[1], net_id, flags, iv_index, auth)) {
			*new_key = true;
			return sub;
		}
	}

	return NULL;
}

2.2.2 GATT ADV, Proxy provisioning

int bt_mesh_proxy_prov_enable(void)
{
	int i;

	BT_DBG("");

	if (gatt_svc == MESH_GATT_PROV) {
		return -EALREADY;
	}

	if (gatt_svc != MESH_GATT_NONE) {
		return -EBUSY;
	}

	bt_gatt_service_register(&prov_svc);
	gatt_svc = MESH_GATT_PROV;
	prov_fast_adv = true;

	for (i = 0; i < ARRAY_SIZE(clients); i++) {
		if (clients[i].conn) {
			clients[i].filter_type = PROV;
		}
	}


	return 0;
}

3. Secure Network beacon

每个加入网络的 Relay and Friend nodes 都应该发送 Secure Network beacon,其他设备可以发。
为了防止网络拥堵,预期的行为是:每个节点大约10秒收到一个本网络的 Secure Network beacon,为了确定信标间隔,节点应连续观察信标,并在给定的观察周期内保持子网信标数量的滚动计数。
信标间隔应使用以下公式确定:
Beacon Interval = Observation Period * (Observed Number of Beacons + 1) / Expected Number of Beacons
信标间隔=观测周期*(观测信标数+1)/预期信标数
如果计算的信标间隔小于10秒,则应将其设置为10秒。如果计算的信标间隔大于600秒,则应将其设置为600秒。
以秒为单位的观察周期通常应为典型信标间隔的两倍(10 x 2 = 20秒)。每个子网都有一个单独的 Secure Network beacon ,因此,每个子网的预期信标数量、观察到的信标数量和观察周期可能不同。

4. Unprovisioned Device beacon

Unprovisioned Device beacon 允许未配置设备被 Provisioner 发现。
该信标的格式如图3.45所示,并在表3.53中定义。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值