/* Bluetooth Mesh */ /* * Copyright (c) 2017 Intel Corporation * Copyright (c) 2021 Lingao Meng * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include "common/bt_str.h" #include "mesh.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" #define LOG_LEVEL CONFIG_BT_MESH_PROXY_LOG_LEVEL #include LOG_MODULE_REGISTER(bt_mesh_proxy); #define PDU_SAR(data) (data[0] >> 6) /* MshPRTv1.1: 6.3.2.2: * "The timeout for the SAR transfer is 20 seconds. When the timeout * expires, the Proxy Server shall disconnect." */ #define PROXY_SAR_TIMEOUT K_SECONDS(20) #define SAR_COMPLETE 0x00 #define SAR_FIRST 0x01 #define SAR_CONT 0x02 #define SAR_LAST 0x03 #define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6))) static uint8_t __noinit bufs[CONFIG_BT_MAX_CONN * CONFIG_BT_MESH_PROXY_MSG_LEN]; static struct bt_mesh_proxy_role roles[CONFIG_BT_MAX_CONN]; static int conn_count; static void proxy_queue_put(struct bt_mesh_proxy_role *role, struct bt_mesh_adv *adv) { k_fifo_put(&role->pending, &(adv->gatt_bearer[bt_conn_index(role->conn)])); } static struct bt_mesh_adv *proxy_queue_get(struct bt_mesh_proxy_role *role) { void *gatt_bearer; gatt_bearer = k_fifo_get(&role->pending, K_NO_WAIT); if (!gatt_bearer) { return NULL; } return CONTAINER_OF(gatt_bearer, struct bt_mesh_adv, gatt_bearer[bt_conn_index(role->conn)]); } static void proxy_sar_timeout(struct k_work *work) { struct bt_mesh_proxy_role *role; struct k_work_delayable *dwork = k_work_delayable_from_work(work); LOG_WRN("Proxy SAR timeout"); role = CONTAINER_OF(dwork, struct bt_mesh_proxy_role, sar_timer); while (!k_fifo_is_empty(&role->pending)) { struct bt_mesh_adv *adv = proxy_queue_get(role); __ASSERT_NO_MSG(adv); bt_mesh_adv_unref(adv); } if (role->conn) { bt_conn_disconnect(role->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); } } ssize_t bt_mesh_proxy_msg_recv(struct bt_conn *conn, const void *buf, uint16_t len) { const uint8_t *data = buf; struct bt_mesh_proxy_role *role = &roles[bt_conn_index(conn)]; if (net_buf_simple_tailroom(&role->buf) < len - 1) { LOG_WRN("Proxy role buffer overflow"); return -EINVAL; } switch (PDU_SAR(data)) { case SAR_COMPLETE: if (role->buf.len) { LOG_WRN("Complete PDU while a pending incomplete one"); return -EINVAL; } role->msg_type = PDU_TYPE(data); net_buf_simple_add_mem(&role->buf, data + 1, len - 1); role->cb.recv(role); net_buf_simple_reset(&role->buf); break; case SAR_FIRST: if (role->buf.len) { LOG_WRN("First PDU while a pending incomplete one"); return -EINVAL; } k_work_reschedule(&role->sar_timer, PROXY_SAR_TIMEOUT); role->msg_type = PDU_TYPE(data); net_buf_simple_add_mem(&role->buf, data + 1, len - 1); break; case SAR_CONT: if (!role->buf.len) { LOG_WRN("Continuation with no prior data"); return -EINVAL; } if (role->msg_type != PDU_TYPE(data)) { LOG_WRN("Unexpected message type in continuation"); return -EINVAL; } k_work_reschedule(&role->sar_timer, PROXY_SAR_TIMEOUT); net_buf_simple_add_mem(&role->buf, data + 1, len - 1); break; case SAR_LAST: if (!role->buf.len) { LOG_WRN("Last SAR PDU with no prior data"); return -EINVAL; } if (role->msg_type != PDU_TYPE(data)) { LOG_WRN("Unexpected message type in last SAR PDU"); return -EINVAL; } /* If this fails, the work handler exits early, as there's no * active SAR buffer. */ (void)k_work_cancel_delayable(&role->sar_timer); net_buf_simple_add_mem(&role->buf, data + 1, len - 1); role->cb.recv(role); net_buf_simple_reset(&role->buf); break; } return len; } int bt_mesh_proxy_msg_send(struct bt_conn *conn, uint8_t type, struct net_buf_simple *msg, bt_gatt_complete_func_t end, void *user_data) { int err; uint16_t att_mtu = bt_gatt_get_mtu(conn); uint16_t mtu; struct bt_mesh_proxy_role *role = &roles[bt_conn_index(conn)]; LOG_DBG("conn %p type 0x%02x len %u: %s", (void *)conn, type, msg->len, bt_hex(msg->data, msg->len)); /* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */ if (att_mtu < 3) { LOG_WRN("Invalid ATT MTU: %d", att_mtu); return -EINVAL; } mtu = att_mtu - 3; if (mtu > msg->len) { net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type)); return role->cb.send(conn, msg->data, msg->len, end, user_data); } net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type)); err = role->cb.send(conn, msg->data, mtu, NULL, NULL); if (err) { return err; } 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)); err = role->cb.send(conn, msg->data, msg->len, end, user_data); if (err) { return err; } break; } net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type)); err = role->cb.send(conn, msg->data, mtu, NULL, NULL); if (err) { return err; } net_buf_simple_pull(msg, mtu); } return 0; } static void buf_send_end(struct bt_conn *conn, void *user_data) { struct bt_mesh_adv *adv = user_data; bt_mesh_adv_unref(adv); } static int proxy_relay_send(struct bt_conn *conn, struct bt_mesh_adv *adv) { int err; NET_BUF_SIMPLE_DEFINE(msg, 1 + BT_MESH_NET_MAX_PDU_LEN); /* Proxy PDU sending modifies the original buffer, * so we need to make a copy. */ net_buf_simple_reserve(&msg, 1); net_buf_simple_add_mem(&msg, adv->b.data, adv->b.len); err = bt_mesh_proxy_msg_send(conn, BT_MESH_PROXY_NET_PDU, &msg, buf_send_end, bt_mesh_adv_ref(adv)); bt_mesh_adv_send_start(0, err, &adv->ctx); if (err) { LOG_ERR("Failed to send proxy message (err %d)", err); /* If segment_and_send() fails the buf_send_end() callback will * not be called, so we need to clear the user data (net_buf, * which is just opaque data to segment_and send) reference given * to segment_and_send() here. */ bt_mesh_adv_unref(adv); } return err; } int bt_mesh_proxy_relay_send(struct bt_conn *conn, struct bt_mesh_adv *adv) { struct bt_mesh_proxy_role *role = &roles[bt_conn_index(conn)]; proxy_queue_put(role, bt_mesh_adv_ref(adv)); bt_mesh_wq_submit(&role->work); return 0; } static void proxy_msg_send_pending(struct k_work *work) { struct bt_mesh_proxy_role *role = CONTAINER_OF(work, struct bt_mesh_proxy_role, work); struct bt_mesh_adv *adv; if (!role->conn) { return; } adv = proxy_queue_get(role); if (!adv) { return; } (void)proxy_relay_send(role->conn, adv); bt_mesh_adv_unref(adv); if (!k_fifo_is_empty(&role->pending)) { bt_mesh_wq_submit(&role->work); } } static void proxy_msg_init(struct bt_mesh_proxy_role *role) { /* Check if buf has been allocated, in this way, we no longer need * to repeat the operation. */ if (role->buf.__buf) { net_buf_simple_reset(&role->buf); return; } net_buf_simple_init_with_data(&role->buf, &bufs[bt_conn_index(role->conn) * CONFIG_BT_MESH_PROXY_MSG_LEN], CONFIG_BT_MESH_PROXY_MSG_LEN); net_buf_simple_reset(&role->buf); k_fifo_init(&role->pending); k_work_init(&role->work, proxy_msg_send_pending); k_work_init_delayable(&role->sar_timer, proxy_sar_timeout); } struct bt_mesh_proxy_role *bt_mesh_proxy_role_setup(struct bt_conn *conn, proxy_send_cb_t send, proxy_recv_cb_t recv) { struct bt_mesh_proxy_role *role; conn_count++; role = &roles[bt_conn_index(conn)]; role->conn = bt_conn_ref(conn); proxy_msg_init(role); role->cb.recv = recv; role->cb.send = send; return role; } void bt_mesh_proxy_role_cleanup(struct bt_mesh_proxy_role *role) { /* If this fails, the work handler exits early, as * there's no active connection. */ (void)k_work_cancel_delayable(&role->sar_timer); bt_conn_unref(role->conn); role->conn = NULL; conn_count--; bt_mesh_adv_gatt_update(); } bool bt_mesh_proxy_has_avail_conn(void) { return conn_count < CONFIG_BT_MESH_MAX_CONN; }