| /* |
| * Copyright (c) 2017 Intel Corporation |
| * Copyright (c) 2020 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include <zephyr/bluetooth/mesh.h> |
| #include <zephyr/bluetooth/conn.h> |
| #include "net.h" |
| #include "proxy.h" |
| #include "prov.h" |
| #include "pb_gatt.h" |
| #include "proxy_msg.h" |
| #include "pb_gatt_srv.h" |
| #include "pb_gatt_cli.h" |
| |
| #include <zephyr/bluetooth/hci.h> |
| |
| #include "common/bt_str.h" |
| |
| #define LOG_LEVEL CONFIG_BT_MESH_PROV_LOG_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(bt_mesh_pb_gatt); |
| |
| struct prov_bearer_send_cb { |
| prov_bearer_send_complete_t cb; |
| void *cb_data; |
| }; |
| |
| struct prov_link { |
| struct bt_conn *conn; |
| const struct prov_bearer_cb *cb; |
| void *cb_data; |
| struct prov_bearer_send_cb comp; |
| struct k_work_delayable prot_timer; |
| }; |
| |
| static struct prov_link link; |
| |
| static void reset_state(void) |
| { |
| if (link.conn) { |
| bt_conn_unref(link.conn); |
| link.conn = NULL; |
| } |
| |
| /* If this fails, the protocol timeout handler will exit early. */ |
| (void)k_work_cancel_delayable(&link.prot_timer); |
| } |
| |
| static void link_closed(enum prov_bearer_link_status status) |
| { |
| const struct prov_bearer_cb *cb = link.cb; |
| void *cb_data = link.cb_data; |
| |
| reset_state(); |
| |
| cb->link_closed(&bt_mesh_pb_gatt, cb_data, status); |
| } |
| |
| static void protocol_timeout(struct k_work *work) |
| { |
| if (!atomic_test_bit(bt_mesh_prov_link.flags, LINK_ACTIVE)) { |
| return; |
| } |
| |
| /* If connection failed or timeout, not allow establish connection */ |
| if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT_CLIENT) && |
| atomic_test_bit(bt_mesh_prov_link.flags, PROVISIONER)) { |
| if (link.conn) { |
| (void)bt_conn_disconnect(link.conn, |
| BT_HCI_ERR_REMOTE_USER_TERM_CONN); |
| } else { |
| (void)bt_mesh_pb_gatt_cli_setup(NULL); |
| } |
| } |
| |
| LOG_DBG("Protocol timeout"); |
| |
| link_closed(PROV_BEARER_LINK_STATUS_TIMEOUT); |
| } |
| |
| int bt_mesh_pb_gatt_recv(struct bt_conn *conn, struct net_buf_simple *buf) |
| { |
| LOG_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); |
| |
| if (link.conn != conn || !link.cb) { |
| LOG_WRN("Data for unexpected connection"); |
| return -ENOTCONN; |
| } |
| |
| if (buf->len < 1) { |
| LOG_WRN("Too short provisioning packet (len %u)", buf->len); |
| return -EINVAL; |
| } |
| |
| k_work_reschedule(&link.prot_timer, bt_mesh_prov_protocol_timeout_get()); |
| |
| link.cb->recv(&bt_mesh_pb_gatt, link.cb_data, buf); |
| |
| return 0; |
| } |
| |
| int bt_mesh_pb_gatt_start(struct bt_conn *conn) |
| { |
| LOG_DBG("conn %p", (void *)conn); |
| |
| if (link.conn) { |
| return -EBUSY; |
| } |
| |
| link.conn = bt_conn_ref(conn); |
| k_work_reschedule(&link.prot_timer, bt_mesh_prov_protocol_timeout_get()); |
| |
| link.cb->link_opened(&bt_mesh_pb_gatt, link.cb_data); |
| |
| return 0; |
| } |
| |
| int bt_mesh_pb_gatt_close(struct bt_conn *conn) |
| { |
| LOG_DBG("conn %p", (void *)conn); |
| |
| if (link.conn != conn) { |
| LOG_DBG("Not connected"); |
| return -ENOTCONN; |
| } |
| |
| link_closed(PROV_BEARER_LINK_STATUS_SUCCESS); |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_BT_MESH_PB_GATT_CLIENT) |
| int bt_mesh_pb_gatt_cli_start(struct bt_conn *conn) |
| { |
| LOG_DBG("conn %p", (void *)conn); |
| |
| if (link.conn) { |
| return -EBUSY; |
| } |
| |
| link.conn = bt_conn_ref(conn); |
| k_work_reschedule(&link.prot_timer, bt_mesh_prov_protocol_timeout_get()); |
| |
| return 0; |
| } |
| |
| int bt_mesh_pb_gatt_cli_open(struct bt_conn *conn) |
| { |
| LOG_DBG("conn %p", (void *)conn); |
| |
| if (link.conn != conn) { |
| LOG_DBG("Not connected"); |
| return -ENOTCONN; |
| } |
| |
| link.cb->link_opened(&bt_mesh_pb_gatt, link.cb_data); |
| |
| return 0; |
| } |
| |
| static int prov_link_open(const uint8_t uuid[16], uint8_t timeout, |
| const struct prov_bearer_cb *cb, void *cb_data) |
| { |
| LOG_DBG("uuid %s", bt_hex(uuid, 16)); |
| |
| link.cb = cb; |
| link.cb_data = cb_data; |
| |
| k_work_reschedule(&link.prot_timer, K_SECONDS(timeout)); |
| |
| return bt_mesh_pb_gatt_cli_setup(uuid); |
| } |
| |
| static void prov_link_close(enum prov_bearer_link_status status) |
| { |
| (void)bt_conn_disconnect(link.conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); |
| } |
| #endif |
| |
| #if defined(CONFIG_BT_MESH_PB_GATT) |
| static int link_accept(const struct prov_bearer_cb *cb, void *cb_data) |
| { |
| int err; |
| |
| err = bt_mesh_adv_enable(); |
| if (err) { |
| LOG_ERR("Failed enabling advertiser"); |
| return err; |
| } |
| |
| (void)bt_mesh_pb_gatt_srv_enable(); |
| bt_mesh_adv_gatt_update(); |
| |
| link.cb = cb; |
| link.cb_data = cb_data; |
| |
| return 0; |
| } |
| #endif |
| |
| static void buf_send_end(struct bt_conn *conn, void *user_data) |
| { |
| if (link.comp.cb) { |
| link.comp.cb(0, link.comp.cb_data); |
| } |
| } |
| |
| static int buf_send(struct net_buf_simple *buf, prov_bearer_send_complete_t cb, |
| void *cb_data) |
| { |
| if (!link.conn) { |
| return -ENOTCONN; |
| } |
| |
| link.comp.cb = cb; |
| link.comp.cb_data = cb_data; |
| |
| k_work_reschedule(&link.prot_timer, bt_mesh_prov_protocol_timeout_get()); |
| |
| return bt_mesh_proxy_msg_send(link.conn, BT_MESH_PROXY_PROV, |
| buf, buf_send_end, NULL); |
| } |
| |
| static void clear_tx(void) |
| { |
| /* No action */ |
| } |
| |
| void bt_mesh_pb_gatt_init(void) |
| { |
| k_work_init_delayable(&link.prot_timer, protocol_timeout); |
| } |
| |
| void bt_mesh_pb_gatt_reset(void) |
| { |
| reset_state(); |
| } |
| |
| const struct prov_bearer bt_mesh_pb_gatt = { |
| .type = BT_MESH_PROV_GATT, |
| #if defined(CONFIG_BT_MESH_PB_GATT_CLIENT) |
| .link_open = prov_link_open, |
| .link_close = prov_link_close, |
| #endif |
| #if defined(CONFIG_BT_MESH_PB_GATT) |
| .link_accept = link_accept, |
| #endif |
| .send = buf_send, |
| .clear_tx = clear_tx, |
| }; |