/* Bluetooth Mesh */
|
|
/*
|
* Copyright (c) 2017 Intel Corporation
|
* Copyright (c) 2021 Lingao Meng
|
*
|
* SPDX-License-Identifier: Apache-2.0
|
*/
|
|
#include "nimble_syscfg.h"
|
#define MESH_LOG_MODULE BLE_MESH_PROXY_LOG
|
|
#if MYNEWT_VAL(BLE_MESH_PROXY)
|
|
#include "mesh/mesh.h"
|
#include "host/ble_att.h"
|
#include "services/gatt/ble_svc_gatt.h"
|
#include "../../host/src/ble_hs_priv.h"
|
|
#include "mesh_priv.h"
|
#include "adv.h"
|
#include "net.h"
|
#include "rpl.h"
|
#include "prov.h"
|
#include "beacon.h"
|
#include "foundation.h"
|
#include "access.h"
|
#include "proxy.h"
|
#include "proxy_msg.h"
|
|
#define PDU_SAR(data) (data[0] >> 6)
|
|
#define BT_UUID_16_ENCODE(w16) \
|
(((w16) >> 0) & 0xFF), \
|
(((w16) >> 8) & 0xFF)
|
/* Mesh Profile 1.0 Section 6.6:
|
* "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 bufs[CONFIG_BT_MAX_CONN * CONFIG_BT_MESH_PROXY_MSG_LEN];
|
|
static struct bt_mesh_proxy_role roles[CONFIG_BT_MAX_CONN];
|
|
static void proxy_sar_timeout(struct ble_npl_event *work)
|
{
|
struct bt_mesh_proxy_role *role;
|
int rc;
|
role = ble_npl_event_get_arg(work);
|
|
|
BT_WARN("Proxy SAR timeout");
|
|
if (role->conn_handle) {
|
rc = ble_gap_terminate(role->conn_handle,
|
BLE_ERR_REM_USER_CONN_TERM);
|
assert(rc == 0);
|
}
|
}
|
|
int bt_mesh_proxy_msg_recv(struct bt_mesh_proxy_role *role,
|
const void *buf, uint16_t len)
|
{
|
const uint8_t *data = buf;
|
|
switch (PDU_SAR(data)) {
|
case SAR_COMPLETE:
|
if (role->buf->om_len) {
|
BT_WARN("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->om_len) {
|
BT_WARN("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->om_len) {
|
BT_WARN("Continuation with no prior data");
|
return -EINVAL;
|
}
|
|
if (role->msg_type != PDU_TYPE(data)) {
|
BT_WARN("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->om_len) {
|
BT_WARN("Last SAR PDU with no prior data");
|
return -EINVAL;
|
}
|
|
if (role->msg_type != PDU_TYPE(data)) {
|
BT_WARN("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_mesh_proxy_role *role, uint8_t type,
|
struct os_mbuf *msg)
|
{
|
int err;
|
uint16_t mtu;
|
uint16_t conn_handle = role->conn_handle;
|
|
BT_DBG("conn_handle %d type 0x%02x len %u: %s", conn_handle, type, msg->om_len,
|
bt_hex(msg->om_data, msg->om_len));
|
|
/* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */
|
mtu = ble_att_mtu(conn_handle) - 3;
|
if (mtu > msg->om_len) {
|
net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type));
|
return role->cb.send(conn_handle, msg->om_data, msg->om_len);
|
}
|
|
net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type));
|
err = role->cb.send(conn_handle, msg->om_data, mtu);
|
if (err) {
|
return err;
|
}
|
net_buf_simple_pull_mem(msg, mtu);
|
|
while (msg->om_len) {
|
if (msg->om_len + 1 < mtu) {
|
net_buf_simple_push_u8(msg, PDU_HDR(SAR_LAST, type));
|
err = role->cb.send(conn_handle, msg->om_data, msg->om_len);
|
if (err) {
|
return err;
|
}
|
break;
|
}
|
|
net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type));
|
err = role->cb.send(conn_handle, msg->om_data, mtu);
|
if (err) {
|
return err;
|
}
|
net_buf_simple_pull_mem(msg, mtu);
|
}
|
|
return 0;
|
}
|
|
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 != NULL) {
|
net_buf_simple_reset(role->buf);
|
return;
|
}
|
|
role->buf = NET_BUF_SIMPLE(CONFIG_BT_MESH_PROXY_MSG_LEN);
|
net_buf_simple_init_with_data(role->buf,
|
&bufs[role->conn_handle *
|
CONFIG_BT_MESH_PROXY_MSG_LEN],
|
CONFIG_BT_MESH_PROXY_MSG_LEN);
|
|
net_buf_simple_reset(role->buf);
|
|
k_work_init_delayable(&role->sar_timer, proxy_sar_timeout);
|
k_work_add_arg_delayable(&role->sar_timer, role);
|
}
|
|
struct bt_mesh_proxy_role *bt_mesh_proxy_role_setup(uint16_t conn_handle,
|
proxy_send_cb_t send,
|
proxy_recv_cb_t recv)
|
{
|
struct bt_mesh_proxy_role *role;
|
|
role = &roles[conn_handle];
|
|
role->conn_handle = conn_handle;
|
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);
|
|
role->conn_handle = BLE_HS_CONN_HANDLE_NONE;
|
|
bt_mesh_adv_update();
|
}
|
|
#endif /* MYNEWT_VAL(BLE_MESH_PROXY) */
|