361 lines
8.1 KiB
C
361 lines
8.1 KiB
C
/*
|
|
* Copyright (c) 2021 Xiaomi Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/byteorder.h>
|
|
|
|
#include <zephyr/net/buf.h>
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
#include <zephyr/bluetooth/hci.h>
|
|
#include <zephyr/bluetooth/uuid.h>
|
|
#include <zephyr/bluetooth/conn.h>
|
|
#include <zephyr/bluetooth/gatt.h>
|
|
#include <zephyr/bluetooth/mesh.h>
|
|
|
|
#include "common/bt_str.h"
|
|
|
|
#include "mesh.h"
|
|
#include "adv.h"
|
|
#include "net.h"
|
|
#include "rpl.h"
|
|
#include "transport.h"
|
|
#include "prov.h"
|
|
#include "beacon.h"
|
|
#include "foundation.h"
|
|
#include "access.h"
|
|
#include "proxy.h"
|
|
#include "proxy_msg.h"
|
|
#include "proxy_cli.h"
|
|
#include "gatt_cli.h"
|
|
#include "pb_gatt_cli.h"
|
|
|
|
#define LOG_LEVEL CONFIG_BT_MESH_PROXY_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(bt_mesh_gatt_client);
|
|
|
|
static struct bt_mesh_gatt_server {
|
|
struct bt_conn *conn;
|
|
uint16_t svc_start_handle;
|
|
uint16_t data_in_handle;
|
|
const struct bt_mesh_gatt_cli *gatt;
|
|
|
|
union {
|
|
void *user_data;
|
|
struct bt_gatt_discover_params discover;
|
|
struct bt_gatt_subscribe_params subscribe;
|
|
};
|
|
} servers[CONFIG_BT_MAX_CONN];
|
|
|
|
static struct bt_mesh_gatt_server *get_server(struct bt_conn *conn)
|
|
{
|
|
return &servers[bt_conn_index(conn)];
|
|
}
|
|
|
|
static uint8_t notify_func(struct bt_conn *conn,
|
|
struct bt_gatt_subscribe_params *params,
|
|
const void *data, uint16_t length)
|
|
{
|
|
const uint8_t *val = data;
|
|
|
|
if (!data) {
|
|
LOG_WRN("[UNSUBSCRIBED]");
|
|
params->value_handle = 0U;
|
|
return BT_GATT_ITER_STOP;
|
|
}
|
|
|
|
if (length < 1) {
|
|
LOG_WRN("Too small Proxy PDU");
|
|
return BT_GATT_ITER_STOP;
|
|
}
|
|
|
|
(void)bt_mesh_proxy_msg_recv(conn, val, length);
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
}
|
|
|
|
static void notify_enabled(struct bt_conn *conn, uint8_t err,
|
|
struct bt_gatt_subscribe_params *params)
|
|
{
|
|
struct bt_mesh_gatt_server *server = get_server(conn);
|
|
|
|
if (err != 0) {
|
|
LOG_WRN("Enable notify failed(err:%d)", err);
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("[SUBSCRIBED]");
|
|
|
|
server->gatt->link_open(conn);
|
|
}
|
|
|
|
static uint8_t discover_func(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr,
|
|
struct bt_gatt_discover_params *params)
|
|
{
|
|
int err;
|
|
struct bt_mesh_gatt_server *server = get_server(conn);
|
|
|
|
if (!attr) {
|
|
LOG_DBG("GATT Services Discover complete");
|
|
(void)memset(params, 0, sizeof(*params));
|
|
return BT_GATT_ITER_STOP;
|
|
}
|
|
|
|
LOG_DBG("[ATTRIBUTE UUID 0x%04x] handle %u", BT_UUID_16(server->discover.uuid)->val,
|
|
attr->handle);
|
|
|
|
if (!bt_uuid_cmp(server->discover.uuid, &server->gatt->srv_uuid.uuid)) {
|
|
server->svc_start_handle = attr->handle;
|
|
|
|
server->discover.uuid = &server->gatt->data_in_uuid.uuid;
|
|
server->discover.start_handle = attr->handle + 1;
|
|
server->discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
|
|
|
err = bt_gatt_discover(conn, &server->discover);
|
|
if (err) {
|
|
LOG_DBG("Discover GATT data in char failed (err %d)", err);
|
|
}
|
|
} else if (!bt_uuid_cmp(server->discover.uuid,
|
|
&server->gatt->data_in_uuid.uuid)) {
|
|
server->data_in_handle = attr->handle + 1;
|
|
|
|
server->discover.uuid = &server->gatt->data_out_uuid.uuid;
|
|
server->discover.start_handle = server->svc_start_handle + 1;
|
|
server->discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
|
|
|
err = bt_gatt_discover(conn, &server->discover);
|
|
if (err) {
|
|
LOG_DBG("Discover GATT data out char failed (err %d)", err);
|
|
}
|
|
} else if (!bt_uuid_cmp(server->discover.uuid,
|
|
&server->gatt->data_out_uuid.uuid)) {
|
|
server->discover.uuid = &server->gatt->data_out_cccd_uuid.uuid;
|
|
server->discover.start_handle = attr->handle + 2;
|
|
server->discover.type = BT_GATT_DISCOVER_DESCRIPTOR;
|
|
|
|
err = bt_gatt_discover(conn, &server->discover);
|
|
if (err) {
|
|
LOG_DBG("Discover GATT CCCD failed (err %d)", err);
|
|
}
|
|
} else {
|
|
(void)memset(&server->subscribe, 0, sizeof(server->subscribe));
|
|
|
|
server->subscribe.notify = notify_func;
|
|
server->subscribe.subscribe = notify_enabled;
|
|
server->subscribe.value = BT_GATT_CCC_NOTIFY;
|
|
server->subscribe.ccc_handle = attr->handle;
|
|
server->subscribe.value_handle = attr->handle - 1;
|
|
|
|
err = bt_gatt_subscribe(conn, &server->subscribe);
|
|
if (err && err != -EALREADY) {
|
|
LOG_DBG("Subscribe failed (err %d)", err);
|
|
}
|
|
}
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
}
|
|
|
|
int bt_mesh_gatt_send(struct bt_conn *conn,
|
|
const void *data, uint16_t len,
|
|
bt_gatt_complete_func_t end, void *user_data)
|
|
{
|
|
struct bt_mesh_gatt_server *server = get_server(conn);
|
|
|
|
LOG_DBG("%u bytes: %s", len, bt_hex(data, len));
|
|
|
|
return bt_gatt_write_without_response_cb(conn, server->data_in_handle,
|
|
data, len, false, end, user_data);
|
|
}
|
|
|
|
static void gatt_connected(struct bt_conn *conn, uint8_t conn_err)
|
|
{
|
|
struct bt_mesh_gatt_server *server = get_server(conn);
|
|
struct bt_conn_info info;
|
|
int err;
|
|
|
|
bt_conn_get_info(conn, &info);
|
|
if (info.role != BT_CONN_ROLE_CENTRAL ||
|
|
!server->gatt) {
|
|
return;
|
|
}
|
|
|
|
if (conn_err) {
|
|
LOG_ERR("Failed to connect GATT Services(%u)", conn_err);
|
|
|
|
bt_conn_unref(server->conn);
|
|
server->conn = NULL;
|
|
|
|
(void)bt_mesh_scan_enable();
|
|
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("conn %p err 0x%02x", (void *)conn, conn_err);
|
|
|
|
server->gatt->connected(conn, server->user_data);
|
|
|
|
(void)bt_mesh_scan_enable();
|
|
|
|
server->discover.uuid = &server->gatt->srv_uuid.uuid;
|
|
server->discover.func = discover_func;
|
|
server->discover.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
|
|
server->discover.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
|
|
server->discover.type = BT_GATT_DISCOVER_PRIMARY;
|
|
err = bt_gatt_discover(conn, &server->discover);
|
|
if (err) {
|
|
LOG_ERR("Unable discover GATT Services (err %d)", err);
|
|
}
|
|
}
|
|
|
|
static void gatt_disconnected(struct bt_conn *conn, uint8_t reason)
|
|
{
|
|
struct bt_conn_info info;
|
|
struct bt_mesh_gatt_server *server = get_server(conn);
|
|
|
|
bt_conn_get_info(conn, &info);
|
|
if (info.role != BT_CONN_ROLE_CENTRAL ||
|
|
!server->gatt) {
|
|
return;
|
|
}
|
|
|
|
server->gatt->disconnected(conn);
|
|
|
|
bt_conn_unref(server->conn);
|
|
|
|
(void)memset(server, 0, sizeof(struct bt_mesh_gatt_server));
|
|
}
|
|
|
|
int bt_mesh_gatt_cli_connect(const bt_addr_le_t *addr,
|
|
const struct bt_mesh_gatt_cli *gatt,
|
|
void *user_data)
|
|
{
|
|
int err;
|
|
struct bt_conn *conn;
|
|
struct bt_mesh_gatt_server *server;
|
|
|
|
/* Avoid interconnection between proxy client and server */
|
|
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr);
|
|
if (conn) {
|
|
bt_conn_unref(conn);
|
|
return -EALREADY;
|
|
}
|
|
|
|
err = bt_mesh_scan_disable();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
LOG_DBG("Try to connect services");
|
|
|
|
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
|
|
BT_LE_CONN_PARAM_DEFAULT, &conn);
|
|
if (err) {
|
|
LOG_ERR("Connection failed (err:%d)", err);
|
|
|
|
(void)bt_mesh_scan_enable();
|
|
|
|
return err;
|
|
}
|
|
|
|
server = get_server(conn);
|
|
server->conn = conn;
|
|
server->gatt = gatt;
|
|
server->user_data = user_data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gatt_advertising_recv(const struct bt_le_scan_recv_info *info,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
uint16_t uuid;
|
|
|
|
if (buf->len < 3) {
|
|
return;
|
|
}
|
|
|
|
uuid = net_buf_simple_pull_le16(buf);
|
|
switch (uuid) {
|
|
#if defined(CONFIG_BT_MESH_PROXY_CLIENT)
|
|
case BT_UUID_MESH_PROXY_VAL:
|
|
bt_mesh_proxy_cli_adv_recv(info, buf);
|
|
break;
|
|
#endif
|
|
#if defined(CONFIG_BT_MESH_PB_GATT_CLIENT)
|
|
case BT_UUID_MESH_PROV_VAL:
|
|
bt_mesh_pb_gatt_cli_adv_recv(info, buf);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void scan_recv(const struct bt_le_scan_recv_info *info,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
if (info->adv_type != BT_GAP_ADV_TYPE_ADV_IND) {
|
|
return;
|
|
}
|
|
|
|
if (!bt_mesh_proxy_has_avail_conn()) {
|
|
return;
|
|
}
|
|
|
|
while (buf->len > 1) {
|
|
struct net_buf_simple_state state;
|
|
uint8_t len, type;
|
|
|
|
len = net_buf_simple_pull_u8(buf);
|
|
/* Check for early termination */
|
|
if (len == 0U) {
|
|
return;
|
|
}
|
|
|
|
if (len > buf->len) {
|
|
LOG_WRN("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_SVC_DATA16:
|
|
gatt_advertising_recv(info, buf);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
net_buf_simple_restore(buf, &state);
|
|
net_buf_simple_pull(buf, len);
|
|
}
|
|
}
|
|
|
|
static struct bt_le_scan_cb scan_cb = {
|
|
.recv = scan_recv,
|
|
};
|
|
|
|
void bt_mesh_gatt_client_init(void)
|
|
{
|
|
bt_le_scan_cb_register(&scan_cb);
|
|
}
|
|
|
|
void bt_mesh_gatt_client_deinit(void)
|
|
{
|
|
bt_le_scan_cb_unregister(&scan_cb);
|
|
}
|
|
|
|
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
|
.connected = gatt_connected,
|
|
.disconnected = gatt_disconnected,
|
|
};
|