|  | /* | 
|  | * 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 "adv.h" | 
|  | #include "host/ecc.h" | 
|  | #include "prov.h" | 
|  | #include "pb_gatt.h" | 
|  | #include "proxy_msg.h" | 
|  | #include "pb_gatt_srv.h" | 
|  | #include "pb_gatt_cli.h" | 
|  |  | 
|  | #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_PROV) | 
|  | #define LOG_MODULE_NAME bt_mesh_pb_gatt | 
|  | #include "common/log.h" | 
|  |  | 
|  | 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(&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); | 
|  | } | 
|  | } | 
|  |  | 
|  | BT_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) | 
|  | { | 
|  | BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); | 
|  |  | 
|  | if (link.conn != conn || !link.cb) { | 
|  | BT_WARN("Data for unexpected connection"); | 
|  | return -ENOTCONN; | 
|  | } | 
|  |  | 
|  | if (buf->len < 1) { | 
|  | BT_WARN("Too short provisioning packet (len %u)", buf->len); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | k_work_reschedule(&link.prot_timer, PROTOCOL_TIMEOUT); | 
|  |  | 
|  | link.cb->recv(&pb_gatt, link.cb_data, buf); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int bt_mesh_pb_gatt_start(struct bt_conn *conn) | 
|  | { | 
|  | BT_DBG("conn %p", (void *)conn); | 
|  |  | 
|  | if (link.conn) { | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | link.conn = bt_conn_ref(conn); | 
|  | k_work_reschedule(&link.prot_timer, PROTOCOL_TIMEOUT); | 
|  |  | 
|  | link.cb->link_opened(&pb_gatt, link.cb_data); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int bt_mesh_pb_gatt_close(struct bt_conn *conn) | 
|  | { | 
|  | BT_DBG("conn %p", (void *)conn); | 
|  |  | 
|  | if (link.conn != conn) { | 
|  | BT_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) | 
|  | { | 
|  | BT_DBG("conn %p", (void *)conn); | 
|  |  | 
|  | if (link.conn) { | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | link.conn = bt_conn_ref(conn); | 
|  | k_work_reschedule(&link.prot_timer, PROTOCOL_TIMEOUT); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int bt_mesh_pb_gatt_cli_open(struct bt_conn *conn) | 
|  | { | 
|  | BT_DBG("conn %p", (void *)conn); | 
|  |  | 
|  | if (link.conn != conn) { | 
|  | BT_DBG("Not connected"); | 
|  | return -ENOTCONN; | 
|  | } | 
|  |  | 
|  | link.cb->link_opened(&pb_gatt, link.cb_data); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int prov_link_open(const uint8_t uuid[16], k_timeout_t timeout, | 
|  | const struct prov_bearer_cb *cb, void *cb_data) | 
|  | { | 
|  | BT_DBG("uuid %s", bt_hex(uuid, 16)); | 
|  |  | 
|  | link.cb = cb; | 
|  | link.cb_data = cb_data; | 
|  |  | 
|  | k_work_reschedule(&link.prot_timer, 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) { | 
|  | BT_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, PROTOCOL_TIMEOUT); | 
|  |  | 
|  | 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 pb_gatt_init(void) | 
|  | { | 
|  | k_work_init_delayable(&link.prot_timer, protocol_timeout); | 
|  | } | 
|  |  | 
|  | void pb_gatt_reset(void) | 
|  | { | 
|  | reset_state(); | 
|  | } | 
|  |  | 
|  | const struct prov_bearer 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, | 
|  | }; |