1. Mesh beacon
这里关注 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;
}
}
下面的函数是根据这个图片描述的格式来处理的。
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中定义。