1. BLE Mesh Core * Provisioning: Node Role * Advertising and GATT bearer * Authentication OOB * Provisioning: Provisioner Role * Advertising and GATT bearer * Authentication OOB * Networking * Relay * Segmentation and Reassembly * Key Refresh * IV Update * Proxy Support * Multiple Client Models Run Simultaneously * Support multiple client models send packets to different nodes simultaneously * No blocking between client model and server * NVS Storage * Store Provisioning Data of BLE Mesh Nodes in Flash 2. BLE Mesh Applications * BLE Mesh Node & Provisioner * Node Example * Provisioner Example * Node + Generic OnOff Client Example * Fast Provisioning * Vendor Fast Prov Server Model * Vendor Fast Prov Client Model * Examples * Wi-Fi & BLE Mesh Coexistence * Example * BLE Mesh Console Commands * Example 3. BLE Mesh Models * Foundation Models * Configuration Server Model * Configuration Client Model * Health Server Model * Health Client Model * Generic Client Models * Generic OnOff Client * Generic Level Client * Generic Location Client * Generic Default Transition Timer Client * Generic Power OnOff Client * Generic Power Level Client * Generic Battery Client * Generic Property Client * Generic Server Models * Generic OnOff Server (Example) * Lighting Client Models * Light Lightness Client * Light CTL Client * Light HSL Client * Light xyL Client * Light LC Client * Sensor Client Model * Sensor Client * Time and Scenes Client Models * Time Client * Scene Client * Scheduler Client
608 lines
16 KiB
C
608 lines
16 KiB
C
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "sdkconfig.h"
|
|
|
|
#include "mesh_bearer_adapt.h"
|
|
#include "mesh_trace.h"
|
|
|
|
#include "net.h"
|
|
#include "beacon.h"
|
|
#include "foundation.h"
|
|
#include "provisioner_prov.h"
|
|
#include "provisioner_proxy.h"
|
|
#include "provisioner_beacon.h"
|
|
|
|
#if CONFIG_BLE_MESH_PROVISIONER
|
|
|
|
#define PDU_TYPE(data) (data[0] & BIT_MASK(6))
|
|
#define PDU_SAR(data) (data[0] >> 6)
|
|
|
|
#define SAR_COMPLETE 0x00
|
|
#define SAR_FIRST 0x01
|
|
#define SAR_CONT 0x02
|
|
#define SAR_LAST 0x03
|
|
|
|
#define CFG_FILTER_SET 0x00
|
|
#define CFG_FILTER_ADD 0x01
|
|
#define CFG_FILTER_REMOVE 0x02
|
|
#define CFG_FILTER_STATUS 0x03
|
|
|
|
#define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6)))
|
|
|
|
#define SERVER_BUF_SIZE 68
|
|
|
|
#define ID_TYPE_NET 0x00
|
|
#define ID_TYPE_NODE 0x01
|
|
|
|
#define NODE_ID_LEN 19
|
|
#define NET_ID_LEN 11
|
|
|
|
#define CLOSE_REASON_PROXY 0xFF
|
|
|
|
static int conn_count;
|
|
|
|
static struct bt_mesh_proxy_server {
|
|
struct bt_mesh_conn *conn;
|
|
/* Provisioner can use filter to double check the dst within mesh messages */
|
|
u16_t filter[CONFIG_BLE_MESH_PROXY_FILTER_SIZE];
|
|
enum __packed {
|
|
NONE,
|
|
WHITELIST,
|
|
BLACKLIST,
|
|
PROV,
|
|
} filter_type;
|
|
u8_t msg_type;
|
|
struct net_buf_simple buf;
|
|
} servers[BLE_MESH_MAX_CONN];
|
|
|
|
static u8_t server_buf_data[SERVER_BUF_SIZE * BLE_MESH_MAX_CONN];
|
|
|
|
static struct bt_mesh_proxy_server *find_server(struct bt_mesh_conn *conn)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(servers); i++) {
|
|
if (servers[i].conn == conn) {
|
|
return &servers[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int filter_status(struct bt_mesh_proxy_server *server,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
/* TODO: Deal with received proxy configuration status messages */
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
static void send_filter_set(struct bt_mesh_proxy_server *server,
|
|
struct bt_mesh_net_rx *rx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
/* TODO: Act as proxy client, send proxy configuration set messages */
|
|
}
|
|
|
|
static void send_filter_add(struct bt_mesh_proxy_server *server,
|
|
struct bt_mesh_net_rx *rx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
/* TODO: Act as proxy client, send proxy configuration add messages */
|
|
}
|
|
|
|
static void send_filter_remove(struct bt_mesh_proxy_server *server,
|
|
struct bt_mesh_net_rx *rx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
/* TODO: Act as proxy client, send proxy configuration remove messages */
|
|
}
|
|
#endif
|
|
|
|
static void proxy_cfg(struct bt_mesh_proxy_server *server)
|
|
{
|
|
NET_BUF_SIMPLE_DEFINE(buf, 29);
|
|
struct bt_mesh_net_rx rx;
|
|
u8_t opcode;
|
|
int err;
|
|
|
|
/** In order to deal with proxy configuration messages, provisioner should
|
|
* do sth. like create mesh network after each device is provisioned.
|
|
*/
|
|
err = bt_mesh_net_decode(&server->buf, BLE_MESH_NET_IF_PROXY_CFG,
|
|
&rx, &buf);
|
|
if (err) {
|
|
BT_ERR("%s, Failed to decode Proxy Configuration (err %d)", __func__, err);
|
|
return;
|
|
}
|
|
|
|
/* Remove network headers */
|
|
net_buf_simple_pull(&buf, BLE_MESH_NET_HDR_LEN);
|
|
|
|
BT_DBG("%u bytes: %s", buf.len, bt_hex(buf.data, buf.len));
|
|
|
|
if (buf.len < 1) {
|
|
BT_WARN("Too short proxy configuration PDU");
|
|
return;
|
|
}
|
|
|
|
opcode = net_buf_simple_pull_u8(&buf);
|
|
switch (opcode) {
|
|
case CFG_FILTER_STATUS:
|
|
filter_status(server, &buf);
|
|
break;
|
|
default:
|
|
BT_WARN("Unhandled configuration OpCode 0x%02x", opcode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void proxy_complete_pdu(struct bt_mesh_proxy_server *server)
|
|
{
|
|
switch (server->msg_type) {
|
|
#if defined(CONFIG_BLE_MESH_GATT_PROXY)
|
|
case BLE_MESH_PROXY_NET_PDU:
|
|
BT_DBG("Mesh Network PDU");
|
|
bt_mesh_net_recv(&server->buf, 0, BLE_MESH_NET_IF_PROXY);
|
|
break;
|
|
case BLE_MESH_PROXY_BEACON:
|
|
BT_DBG("Mesh Beacon PDU");
|
|
provisioner_beacon_recv(&server->buf);
|
|
break;
|
|
case BLE_MESH_PROXY_CONFIG:
|
|
BT_DBG("Mesh Configuration PDU");
|
|
proxy_cfg(server);
|
|
break;
|
|
#endif
|
|
#if defined(CONFIG_BLE_MESH_PB_GATT)
|
|
case BLE_MESH_PROXY_PROV:
|
|
BT_DBG("Mesh Provisioning PDU");
|
|
provisioner_pb_gatt_recv(server->conn, &server->buf);
|
|
break;
|
|
#endif
|
|
default:
|
|
BT_WARN("Unhandled Message Type 0x%02x", server->msg_type);
|
|
break;
|
|
}
|
|
|
|
net_buf_simple_reset(&server->buf);
|
|
}
|
|
|
|
#define ATTR_IS_PROV(uuid) (uuid == BLE_MESH_UUID_MESH_PROV_VAL)
|
|
|
|
static ssize_t proxy_recv(struct bt_mesh_conn *conn,
|
|
const struct bt_mesh_gatt_attr *attr, const void *buf,
|
|
u16_t len, u16_t offset, u8_t flags)
|
|
{
|
|
struct bt_mesh_proxy_server *server = find_server(conn);
|
|
const u8_t *data = buf;
|
|
u16_t srvc_uuid = 0;
|
|
|
|
if (!server) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
if (len < 1) {
|
|
BT_WARN("Too small Proxy PDU");
|
|
return -EINVAL;
|
|
}
|
|
|
|
srvc_uuid = bt_mesh_gattc_get_service_uuid(conn);
|
|
if (!srvc_uuid) {
|
|
BT_ERR("%s, No service uuid found", __func__);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
if (ATTR_IS_PROV(srvc_uuid) != (PDU_TYPE(data) == BLE_MESH_PROXY_PROV)) {
|
|
BT_WARN("Proxy PDU type doesn't match GATT service uuid");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (len - 1 > net_buf_simple_tailroom(&server->buf)) {
|
|
BT_WARN("Too big proxy PDU");
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (PDU_SAR(data)) {
|
|
case SAR_COMPLETE:
|
|
if (server->buf.len) {
|
|
BT_WARN("Complete PDU while a pending incomplete one");
|
|
return -EINVAL;
|
|
}
|
|
|
|
server->msg_type = PDU_TYPE(data);
|
|
net_buf_simple_add_mem(&server->buf, data + 1, len - 1);
|
|
proxy_complete_pdu(server);
|
|
break;
|
|
|
|
case SAR_FIRST:
|
|
if (server->buf.len) {
|
|
BT_WARN("First PDU while a pending incomplete one");
|
|
return -EINVAL;
|
|
}
|
|
|
|
server->msg_type = PDU_TYPE(data);
|
|
net_buf_simple_add_mem(&server->buf, data + 1, len - 1);
|
|
break;
|
|
|
|
case SAR_CONT:
|
|
if (!server->buf.len) {
|
|
BT_WARN("Continuation with no prior data");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (server->msg_type != PDU_TYPE(data)) {
|
|
BT_WARN("Unexpected message type in continuation");
|
|
return -EINVAL;
|
|
}
|
|
|
|
net_buf_simple_add_mem(&server->buf, data + 1, len - 1);
|
|
break;
|
|
|
|
case SAR_LAST:
|
|
if (!server->buf.len) {
|
|
BT_WARN("Last SAR PDU with no prior data");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (server->msg_type != PDU_TYPE(data)) {
|
|
BT_WARN("Unexpected message type in last SAR PDU");
|
|
return -EINVAL;
|
|
}
|
|
|
|
net_buf_simple_add_mem(&server->buf, data + 1, len - 1);
|
|
proxy_complete_pdu(server);
|
|
break;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static void proxy_prov_connected(const u8_t addr[6], struct bt_mesh_conn *conn, int id)
|
|
{
|
|
struct bt_mesh_proxy_server *server = NULL;
|
|
|
|
conn_count++;
|
|
|
|
if (!servers[id].conn) {
|
|
server = &servers[id];
|
|
}
|
|
|
|
if (!server) {
|
|
BT_ERR("%s, No matching Proxy Client objects", __func__);
|
|
/** Disconnect current connection, clear part of prov_link
|
|
* information, like uuid, dev_addr, linking flag, etc.
|
|
*/
|
|
|
|
return;
|
|
}
|
|
|
|
server->conn = bt_mesh_conn_ref(conn);
|
|
server->filter_type = NONE;
|
|
memset(server->filter, 0, sizeof(server->filter));
|
|
net_buf_simple_reset(&server->buf);
|
|
|
|
#if defined(CONFIG_BLE_MESH_PB_GATT)
|
|
if (provisioner_set_prov_conn(addr, server->conn)) {
|
|
BT_ERR("%s, provisioner_set_prov_conn failed", __func__);
|
|
bt_mesh_gattc_disconnect(server->conn);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
bt_mesh_gattc_exchange_mtu(id);
|
|
}
|
|
|
|
static void proxy_prov_disconnected(struct bt_mesh_conn *conn, u8_t reason)
|
|
{
|
|
struct bt_mesh_proxy_server *server = NULL;
|
|
int i;
|
|
|
|
BT_DBG("conn %p, handle is %d, reason 0x%02x", conn, conn->handle, reason);
|
|
|
|
if (conn_count) {
|
|
conn_count--;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(servers); i++) {
|
|
server = &servers[i];
|
|
if (server->conn == conn) {
|
|
if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) &&
|
|
server->filter_type == PROV) {
|
|
provisioner_pb_gatt_close(conn, reason);
|
|
}
|
|
server->conn = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BLE_MESH_PB_GATT)
|
|
static ssize_t prov_write_ccc_descr(struct bt_mesh_conn *conn, u8_t *addr)
|
|
{
|
|
struct bt_mesh_proxy_server *server;
|
|
|
|
server = find_server(conn);
|
|
|
|
if (!server) {
|
|
BT_ERR("%s, No Proxy Server found", __func__);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
if (server->filter_type == NONE) {
|
|
server->filter_type = PROV;
|
|
return provisioner_pb_gatt_open(conn, addr);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static ssize_t prov_notification(struct bt_mesh_conn *conn, u8_t *data, u16_t len)
|
|
{
|
|
struct bt_mesh_proxy_server *server;
|
|
|
|
server = find_server(conn);
|
|
|
|
if (!server) {
|
|
BT_ERR("%s, No Proxy Server found", __func__);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
if (server->filter_type == PROV) {
|
|
return proxy_recv(conn, NULL, data, len, 0, 0);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
int provisioner_pb_gatt_enable(void)
|
|
{
|
|
int i;
|
|
|
|
BT_DBG("%s", __func__);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(servers); i++) {
|
|
if (servers[i].conn) {
|
|
servers[i].filter_type = PROV;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int provisioner_pb_gatt_disable(void)
|
|
{
|
|
int i;
|
|
|
|
BT_DBG("%s", __func__);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(servers); i++) {
|
|
struct bt_mesh_proxy_server *server = &servers[i];
|
|
|
|
if (server->conn && server->filter_type == PROV) {
|
|
bt_mesh_gattc_disconnect(server->conn);
|
|
server->filter_type = NONE;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CONFIG_BLE_MESH_PB_GATT */
|
|
|
|
#if defined(CONFIG_BLE_MESH_GATT_PROXY)
|
|
static ssize_t proxy_write_ccc_descr(struct bt_mesh_conn *conn)
|
|
{
|
|
struct bt_mesh_proxy_server *server;
|
|
|
|
server = find_server(conn);
|
|
|
|
if (!server) {
|
|
BT_ERR("%s, No Proxy Server found", __func__);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
if (server->filter_type == NONE) {
|
|
server->filter_type = WHITELIST;
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static ssize_t proxy_notification(struct bt_mesh_conn *conn, u8_t *data, u16_t len)
|
|
{
|
|
return proxy_recv(conn, NULL, data, len, 0, 0);
|
|
}
|
|
|
|
/** Currently provisioner does't need bt_mesh_provisioner_proxy_enable()
|
|
* and bt_mesh_provisioner_proxy_disable() functions, and once they are
|
|
* used, provisioner can be enabled to parse node_id_adv and net_id_adv
|
|
* in order to support proxy client role.
|
|
* And if gatt_proxy is disabled, provisioner can stop dealing with
|
|
* these two kinds of connectable advertising packets.
|
|
*/
|
|
int bt_mesh_provisioner_proxy_enable(void)
|
|
{
|
|
int i;
|
|
|
|
BT_DBG("%s", __func__);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(servers); i++) {
|
|
if (servers[i].conn) {
|
|
servers[i].filter_type = WHITELIST;
|
|
}
|
|
}
|
|
|
|
/** TODO: Once at leat one device has been provisioned, provisioner
|
|
* can be set to allow receiving and parsing node_id & net_id adv
|
|
* packets, and we may use a global flag to indicate this.
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void bt_mesh_proxy_gatt_proxy_disconnect(void)
|
|
{
|
|
int i;
|
|
|
|
BT_DBG("%s", __func__);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(servers); i++) {
|
|
struct bt_mesh_proxy_server *server = &servers[i];
|
|
|
|
if (server->conn && (server->filter_type == WHITELIST ||
|
|
server->filter_type == BLACKLIST)) {
|
|
server->filter_type = NONE;
|
|
bt_mesh_gattc_disconnect(server->conn);
|
|
}
|
|
}
|
|
}
|
|
|
|
int bt_mesh_provisioner_proxy_disable(void)
|
|
{
|
|
BT_DBG("%s", __func__);
|
|
|
|
/** TODO: Once this function is invoked, provisioner shall stop
|
|
* receiving and parsing node_id & net_id adv packets, and if
|
|
* proxy connection exists, we should disconnect it.
|
|
*/
|
|
|
|
bt_mesh_proxy_gatt_proxy_disconnect();
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CONFIG_BLE_MESH_GATT_PROXY */
|
|
|
|
static int proxy_send(struct bt_mesh_conn *conn, const void *data, u16_t len)
|
|
{
|
|
BT_DBG("%u bytes: %s", len, bt_hex(data, len));
|
|
|
|
#if defined(CONFIG_BLE_MESH_GATT_PROXY) || defined(CONFIG_BLE_MESH_PB_GATT)
|
|
return bt_mesh_gattc_write_no_rsp(conn, NULL, data, len);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int proxy_prov_segment_and_send(struct bt_mesh_conn *conn, u8_t type,
|
|
struct net_buf_simple *msg)
|
|
{
|
|
u16_t mtu;
|
|
|
|
if (conn == NULL) {
|
|
BT_ERR("%s, Invalid parameter", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
BT_DBG("conn %p type 0x%02x len %u: %s", conn, type, msg->len,
|
|
bt_hex(msg->data, msg->len));
|
|
|
|
mtu = bt_mesh_gattc_get_mtu_info(conn);
|
|
if (!mtu) {
|
|
BT_ERR("%s, Conn used to get mtu does not exist", __func__);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
/* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */
|
|
mtu -= 3;
|
|
if (mtu > msg->len) {
|
|
net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type));
|
|
return proxy_send(conn, msg->data, msg->len);
|
|
}
|
|
|
|
net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type));
|
|
proxy_send(conn, msg->data, mtu);
|
|
net_buf_simple_pull(msg, mtu);
|
|
|
|
while (msg->len) {
|
|
if (msg->len + 1 < mtu) {
|
|
net_buf_simple_push_u8(msg, PDU_HDR(SAR_LAST, type));
|
|
proxy_send(conn, msg->data, msg->len);
|
|
break;
|
|
}
|
|
|
|
net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type));
|
|
proxy_send(conn, msg->data, mtu);
|
|
net_buf_simple_pull(msg, mtu);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int provisioner_proxy_send(struct bt_mesh_conn *conn, u8_t type,
|
|
struct net_buf_simple *msg)
|
|
{
|
|
struct bt_mesh_proxy_server *server = find_server(conn);
|
|
|
|
if (!server) {
|
|
BT_ERR("$%s, No Proxy Server found", __func__);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
if ((server->filter_type == PROV) != (type == BLE_MESH_PROXY_PROV)) {
|
|
BT_ERR("%s, Invalid PDU type for Proxy Client", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return proxy_prov_segment_and_send(conn, type, msg);
|
|
}
|
|
|
|
static struct bt_mesh_prov_conn_cb conn_callbacks = {
|
|
.connected = proxy_prov_connected,
|
|
.disconnected = proxy_prov_disconnected,
|
|
#if defined(CONFIG_BLE_MESH_PB_GATT)
|
|
.prov_write_descr = prov_write_ccc_descr,
|
|
.prov_notify = prov_notification,
|
|
#endif /* CONFIG_BLE_MESH_PB_GATT */
|
|
#if defined(CONFIG_BLE_MESH_GATT_PROXY)
|
|
.proxy_write_descr = proxy_write_ccc_descr,
|
|
.proxy_notify = proxy_notification,
|
|
#endif /* CONFIG_BLE_MESH_GATT_PROXY */
|
|
};
|
|
|
|
void provisioner_proxy_srv_data_recv(struct net_buf_simple *buf)
|
|
{
|
|
/** TODO: Parse node_id_adv or net_id_adv pkts. Currently we
|
|
* don't support this function, and if realized later, proxy
|
|
* client need to check if there is server structure left
|
|
* before create connection with a server.
|
|
* check conn_count & CONFIG_BLE_MESH_PBG_SAME_TIME
|
|
*/
|
|
}
|
|
|
|
int provisioner_proxy_init(void)
|
|
{
|
|
int i;
|
|
|
|
/* Initialize the server receive buffers */
|
|
for (i = 0; i < ARRAY_SIZE(servers); i++) {
|
|
struct bt_mesh_proxy_server *server = &servers[i];
|
|
|
|
server->buf.size = SERVER_BUF_SIZE;
|
|
server->buf.__buf = server_buf_data + (i * SERVER_BUF_SIZE);
|
|
}
|
|
|
|
bt_mesh_gattc_conn_cb_register(&conn_callbacks);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CONFIG_BLE_MESH_PROVISIONER */
|