mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-15 16:24:28 +08:00
723 lines
16 KiB
C
723 lines
16 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
|
*
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <sys/time.h>
|
|
#include <ell/ell.h>
|
|
|
|
#include "mesh/mesh-defs.h"
|
|
|
|
#include "mesh/mesh-io.h"
|
|
#include "mesh/display.h"
|
|
#include "mesh/crypto.h"
|
|
#include "mesh/net.h"
|
|
#include "mesh/prov.h"
|
|
|
|
#define PB_ADV_MTU 24
|
|
|
|
#define DEFAULT_CONN_ID 0x00000000
|
|
#define DEFAULT_PROV_MSG_NUM 0x00
|
|
#define DEFAULT_DEV_MSG_NUM 0x80
|
|
|
|
#define TX_TIMEOUT 30
|
|
|
|
struct mesh_prov *mesh_prov_new(struct mesh_net *net, uint16_t remote)
|
|
{
|
|
struct mesh_prov *prov;
|
|
|
|
prov = l_new(struct mesh_prov, 1);
|
|
|
|
prov->remote = remote;
|
|
prov->net = net;
|
|
|
|
return mesh_prov_ref(prov);
|
|
}
|
|
|
|
struct mesh_prov *mesh_prov_ref(struct mesh_prov *prov)
|
|
{
|
|
if (!prov)
|
|
return NULL;
|
|
|
|
__sync_fetch_and_add(&prov->ref_count, 1);
|
|
|
|
return prov;
|
|
}
|
|
|
|
void mesh_prov_unref(struct mesh_prov *prov)
|
|
{
|
|
struct mesh_io *io;
|
|
uint8_t type;
|
|
|
|
if (!prov)
|
|
return;
|
|
|
|
if (__sync_sub_and_fetch(&prov->ref_count, 1))
|
|
return;
|
|
|
|
io = mesh_net_get_io(prov->net);
|
|
type = MESH_AD_TYPE_BEACON;
|
|
mesh_io_send_cancel(io, &type, 1);
|
|
type = MESH_AD_TYPE_PROVISION;
|
|
mesh_io_send_cancel(io, &type, 1);
|
|
mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_PROV);
|
|
mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_BEACON);
|
|
l_timeout_remove(prov->tx_timeout);
|
|
|
|
|
|
l_info("Freed Prov Data");
|
|
l_free(prov);
|
|
}
|
|
|
|
static void packet_received(struct mesh_prov *prov, const void *data,
|
|
uint16_t size, uint8_t fcs)
|
|
{
|
|
if (prov->receive_callback)
|
|
prov->receive_callback(data, size, prov);
|
|
}
|
|
|
|
static void send_open_req(struct mesh_prov *prov)
|
|
{
|
|
struct mesh_io *io;
|
|
uint8_t open_req[23] = { MESH_AD_TYPE_PROVISION };
|
|
struct mesh_io_send_info info = {
|
|
.type = MESH_IO_TIMING_TYPE_GENERAL,
|
|
.u.gen.interval = 50,
|
|
.u.gen.cnt = 1,
|
|
.u.gen.min_delay = 0,
|
|
.u.gen.max_delay = 0,
|
|
};
|
|
|
|
if (!prov)
|
|
return;
|
|
|
|
io = mesh_net_get_io(prov->net);
|
|
if (!io)
|
|
return;
|
|
|
|
l_put_be32(prov->conn_id, open_req + 1);
|
|
open_req[1 + 4] = prov->local_msg_num = 0;
|
|
open_req[1 + 4 + 1] = 0x03; /* OPEN_REQ */
|
|
memcpy(open_req + 1 + 4 + 1 + 1, prov->uuid, 16);
|
|
|
|
/* print_packet("PB-TX", open_req + 1, sizeof(open_req) - 1); */
|
|
mesh_io_send_cancel(io, open_req, 1);
|
|
mesh_io_send(io, &info, open_req, sizeof(open_req));
|
|
}
|
|
|
|
static void send_open_cfm(struct mesh_prov *prov)
|
|
{
|
|
struct mesh_io *io;
|
|
uint8_t open_cfm[7] = { MESH_AD_TYPE_PROVISION };
|
|
struct mesh_io_send_info info = {
|
|
.type = MESH_IO_TIMING_TYPE_GENERAL,
|
|
.u.gen.interval = 50,
|
|
.u.gen.cnt = 1,
|
|
.u.gen.min_delay = 0,
|
|
.u.gen.max_delay = 0,
|
|
};
|
|
|
|
if (!prov)
|
|
return;
|
|
|
|
io = mesh_net_get_io(prov->net);
|
|
if (!io)
|
|
return;
|
|
|
|
l_put_be32(prov->conn_id, open_cfm + 1);
|
|
open_cfm[1 + 4] = 0;
|
|
open_cfm[1 + 4 + 1] = 0x07; /* OPEN_CFM */
|
|
|
|
/* print_packet("PB-TX", open_cfm + 1, sizeof(open_cfm) - 1); */
|
|
|
|
mesh_io_send_cancel(io, open_cfm, 1);
|
|
mesh_io_send(io, &info, open_cfm, sizeof(open_cfm));
|
|
}
|
|
|
|
static void send_close_ind(struct mesh_prov *prov, uint8_t reason)
|
|
{
|
|
uint8_t buf[8] = { MESH_AD_TYPE_PROVISION };
|
|
struct mesh_io *io;
|
|
struct mesh_io_send_info info = {
|
|
.type = MESH_IO_TIMING_TYPE_GENERAL,
|
|
.u.gen.interval = 50,
|
|
.u.gen.cnt = 3,
|
|
.u.gen.min_delay = 0,
|
|
.u.gen.max_delay = 0,
|
|
};
|
|
|
|
if (!prov)
|
|
return;
|
|
|
|
if (prov->bearer == MESH_BEARER_ADV) {
|
|
io = mesh_net_get_io(prov->net);
|
|
if (!io)
|
|
return;
|
|
|
|
l_put_be32(prov->conn_id, buf + 1);
|
|
buf[5] = 0;
|
|
buf[6] = 0x0B; /* CLOSE_IND */
|
|
buf[7] = reason;
|
|
|
|
/* print_packet("PB-TX", buf + 1, sizeof(buf) - 1); */
|
|
|
|
mesh_io_send_cancel(io, buf, 1);
|
|
mesh_io_send(io, &info, buf, sizeof(buf));
|
|
}
|
|
|
|
prov->bearer = MESH_BEARER_IDLE;
|
|
}
|
|
|
|
static void tx_timeout(struct l_timeout *timeout, void *user_data)
|
|
{
|
|
struct mesh_prov *prov = user_data;
|
|
uint8_t cancel[] = { MESH_AD_TYPE_PROVISION };
|
|
struct mesh_io *io;
|
|
|
|
if (!prov)
|
|
return;
|
|
|
|
l_timeout_remove(prov->tx_timeout);
|
|
prov->tx_timeout = NULL;
|
|
|
|
io = mesh_net_get_io(prov->net);
|
|
if (!io)
|
|
return;
|
|
|
|
mesh_io_send_cancel(io, cancel, sizeof(cancel));
|
|
|
|
l_info("TX timeout");
|
|
mesh_prov_close(prov, 1);
|
|
}
|
|
|
|
static void send_adv_segs(struct mesh_prov *prov)
|
|
{
|
|
struct mesh_io *io = mesh_net_get_io(prov->net);
|
|
struct mesh_io_send_info info = {
|
|
.type = MESH_IO_TIMING_TYPE_GENERAL,
|
|
.u.gen.interval = 50,
|
|
.u.gen.cnt = MESH_IO_TX_COUNT_UNLIMITED,
|
|
.u.gen.min_delay = 0,
|
|
.u.gen.max_delay = 0,
|
|
};
|
|
const void *data = prov->packet_buf;
|
|
uint16_t size = prov->packet_len;
|
|
uint16_t init_size;
|
|
uint8_t buf[1 + PB_ADV_MTU + 5] = { MESH_AD_TYPE_PROVISION };
|
|
uint8_t max_seg;
|
|
uint8_t consumed;
|
|
int i;
|
|
|
|
if (!size)
|
|
return;
|
|
|
|
mesh_io_send_cancel(io, buf, 1);
|
|
|
|
l_put_be32(prov->conn_id, buf + 1);
|
|
buf[1 + 4] = prov->local_msg_num;
|
|
|
|
if (size > PB_ADV_MTU - 4) {
|
|
max_seg = 1 +
|
|
(((size - (PB_ADV_MTU - 4)) - 1) / (PB_ADV_MTU - 1));
|
|
init_size = PB_ADV_MTU - 4;
|
|
} else {
|
|
max_seg = 0;
|
|
init_size = size;
|
|
}
|
|
|
|
/* print_packet("FULL-TX", data, size); */
|
|
|
|
l_debug("Sending %u fragments for %u octets", max_seg + 1, size);
|
|
|
|
buf[1 + 4 + 1] = max_seg << 2;
|
|
l_put_be16(size, buf + 1 + 4 + 1 + 1);
|
|
buf[9] = mesh_crypto_compute_fcs(data, size);
|
|
memcpy(buf + 1 + 4 + 1 + 1 + 2 + 1, data, init_size);
|
|
|
|
l_debug("max_seg: %2.2x", max_seg);
|
|
l_debug("size: %2.2x, CRC: %2.2x", size, buf[9]);
|
|
|
|
/* print_packet("PB-TX", buf + 1, init_size + 9); */
|
|
mesh_io_send(io, &info, buf, init_size + 10);
|
|
|
|
consumed = init_size;
|
|
|
|
for (i = 1; i <= max_seg; i++) {
|
|
uint8_t seg_size; /* Amount of payload data being sent */
|
|
|
|
if (size - consumed > PB_ADV_MTU - 1)
|
|
seg_size = PB_ADV_MTU - 1;
|
|
else
|
|
seg_size = size - consumed;
|
|
|
|
buf[6] = (i << 2) | 0x02;
|
|
memcpy(buf + 7, data + consumed, seg_size);
|
|
|
|
/* print_packet("PB-TX", buf + 1, seg_size + 6); */
|
|
|
|
mesh_io_send(io, &info, buf, seg_size + 7);
|
|
|
|
consumed += seg_size;
|
|
}
|
|
}
|
|
|
|
static void send_adv_msg(struct mesh_prov *prov, const void *data,
|
|
uint16_t size)
|
|
{
|
|
l_timeout_remove(prov->tx_timeout);
|
|
prov->tx_timeout = l_timeout_create(TX_TIMEOUT, tx_timeout, prov, NULL);
|
|
|
|
memcpy(prov->packet_buf, data, size);
|
|
prov->packet_len = size;
|
|
|
|
send_adv_segs(prov);
|
|
}
|
|
|
|
static void send_ack(struct mesh_prov *prov)
|
|
{
|
|
struct mesh_io *io = mesh_net_get_io(prov->net);
|
|
struct mesh_io_send_info info = {
|
|
.type = MESH_IO_TIMING_TYPE_GENERAL,
|
|
.u.gen.interval = 50,
|
|
.u.gen.cnt = 1,
|
|
.u.gen.min_delay = 0,
|
|
.u.gen.max_delay = 0,
|
|
};
|
|
uint8_t ack[7] = { MESH_AD_TYPE_PROVISION };
|
|
|
|
l_put_be32(prov->conn_id, ack + 1);
|
|
ack[1 + 4] = prov->last_peer_msg_num;
|
|
ack[1 + 4 + 1] = 0x01; /* ACK */
|
|
|
|
/* print_packet("ADV-ACK", ack + 1, sizeof(ack) - 1); */
|
|
mesh_io_send(io, &info, ack, sizeof(ack));
|
|
}
|
|
|
|
static void adv_data_pkt(uint8_t type, const void *pkt, uint8_t size,
|
|
void *user_data)
|
|
{
|
|
const uint8_t *data = pkt;
|
|
struct mesh_prov *prov = user_data;
|
|
uint16_t offset = 0;
|
|
|
|
if ((type & 0x03) == 0x00) {
|
|
uint8_t last_seg = type >> 2;
|
|
|
|
prov->expected_len = l_get_be16(data);
|
|
prov->expected_fcs = l_get_u8(data + 2);
|
|
|
|
/* print_packet("Pkt", pkt, size); */
|
|
data += 3;
|
|
size -= 3;
|
|
|
|
prov->trans = MESH_TRANS_RX;
|
|
|
|
if (prov->expected_len > sizeof(prov->peer_buf)) {
|
|
l_info("Incoming pkt exceeds storage %d > %ld",
|
|
prov->expected_len, sizeof(prov->peer_buf));
|
|
return;
|
|
} else if (last_seg == 0)
|
|
prov->trans = MESH_TRANS_IDLE;
|
|
|
|
prov->expected_segs = 0xff >> (7 - last_seg);
|
|
prov->got_segs |= 1;
|
|
memcpy(prov->peer_buf, data, size);
|
|
|
|
} else if ((type & 0x03) == 0x02) {
|
|
offset = (PB_ADV_MTU - 4) + ((type >> 2) - 1) *
|
|
(PB_ADV_MTU - 1);
|
|
|
|
if (offset + size > prov->expected_len) {
|
|
l_info("Incoming pkt exceeds agreed len %d + %d > %d",
|
|
offset, size, prov->expected_len);
|
|
return;
|
|
}
|
|
|
|
prov->trans = MESH_TRANS_RX;
|
|
|
|
l_debug("Processing fragment %u", type & 0x3f);
|
|
|
|
prov->got_segs |= 1 << (type >> 2);
|
|
memcpy(prov->peer_buf + offset, data, size);
|
|
|
|
} else if (type == 0x01) {
|
|
if (prov->send_callback) {
|
|
void *data = prov->send_data;
|
|
mesh_prov_send_func_t cb = prov->send_callback;
|
|
|
|
prov->trans = MESH_TRANS_IDLE;
|
|
prov->send_callback = NULL;
|
|
prov->send_data = NULL;
|
|
|
|
cb(true, data);
|
|
}
|
|
return;
|
|
} else
|
|
return;
|
|
|
|
if (prov->got_segs != prov->expected_segs)
|
|
return;
|
|
|
|
/* Validate RXed packet and pass up to Provisioning */
|
|
if (!mesh_crypto_check_fcs(prov->peer_buf,
|
|
prov->expected_len,
|
|
prov->expected_fcs)) {
|
|
l_debug("Invalid FCS");
|
|
return;
|
|
}
|
|
|
|
prov->last_peer_msg_num = prov->peer_msg_num;
|
|
send_ack(prov);
|
|
|
|
prov->trans = MESH_TRANS_IDLE;
|
|
|
|
packet_received(prov, prov->peer_buf,
|
|
prov->expected_len, prov->expected_fcs);
|
|
|
|
/* Reset Re-Assembly for next packet */
|
|
prov->expected_len = sizeof(prov->peer_buf);
|
|
prov->expected_fcs = 0;
|
|
prov->expected_segs = 0;
|
|
prov->got_segs = 0;
|
|
|
|
}
|
|
|
|
static void adv_bearer_packet(void *user_data, struct mesh_io_recv_info *info,
|
|
const uint8_t *pkt, uint16_t len)
|
|
{
|
|
struct mesh_prov *prov = user_data;
|
|
uint32_t conn_id;
|
|
uint8_t msg_num;
|
|
uint8_t type;
|
|
|
|
if (len < 6) {
|
|
l_info(" Too short packet");
|
|
return;
|
|
}
|
|
|
|
conn_id = l_get_be32(pkt + 1);
|
|
msg_num = l_get_u8(pkt + 1 + 4);
|
|
type = l_get_u8(pkt + 1 + 4 + 1);
|
|
|
|
/*if (prov->conn_id == conn_id) print_packet("ADV-RX", pkt, len); */
|
|
|
|
if (prov->conn_id != DEFAULT_CONN_ID) {
|
|
if (prov->conn_id != conn_id) {
|
|
l_debug("rxed unknown conn_id: %8.8x != %8.8x",
|
|
conn_id, prov->conn_id);
|
|
return;
|
|
}
|
|
} else if (type != 0x03)
|
|
return;
|
|
|
|
/* print_packet("PB-ADV-RX", pkt, len); */
|
|
|
|
/* Normalize pkt to start of PROV pkt payload */
|
|
pkt += 7;
|
|
len -= 7;
|
|
|
|
if (type == 0x07) { /* OPEN_CFM */
|
|
if (conn_id != prov->conn_id)
|
|
return;
|
|
|
|
if (msg_num != prov->local_msg_num)
|
|
return;
|
|
|
|
l_info("Link open confirmed");
|
|
|
|
prov->bearer = MESH_BEARER_ADV;
|
|
if (prov->open_callback)
|
|
prov->open_callback(prov->receive_data);
|
|
} else if (type == 0x01) {
|
|
if (conn_id != prov->conn_id)
|
|
return;
|
|
|
|
if (msg_num != prov->local_msg_num)
|
|
return;
|
|
|
|
l_debug("Got ACK %d", msg_num);
|
|
adv_data_pkt(type, pkt, len, user_data);
|
|
} else if (type == 0x03) {
|
|
/*
|
|
* Ignore if:
|
|
* 1. We are already provisioning
|
|
* 2. We are not advertising that we are unprovisioned
|
|
* 3. Open request not addressed to us
|
|
*/
|
|
if (prov->conn_id != DEFAULT_CONN_ID &&
|
|
prov->conn_id != conn_id)
|
|
return;
|
|
|
|
if (prov->local_msg_num != (DEFAULT_DEV_MSG_NUM - 1))
|
|
return;
|
|
|
|
if (memcmp(pkt, prov->uuid, 16))
|
|
return;
|
|
|
|
l_info("Link open request");
|
|
|
|
prov->last_peer_msg_num = 0xFF;
|
|
prov->bearer = MESH_BEARER_ADV;
|
|
if (prov->open_callback && prov->conn_id == DEFAULT_CONN_ID)
|
|
prov->open_callback(prov->receive_data);
|
|
|
|
prov->conn_id = conn_id;
|
|
prov->peer_msg_num = msg_num;
|
|
send_open_cfm(prov);
|
|
} else if (type == 0x0B) {
|
|
if (prov->conn_id != conn_id)
|
|
return;
|
|
|
|
prov->conn_id = DEFAULT_CONN_ID;
|
|
prov->local_msg_num = 0xFF;
|
|
prov->peer_msg_num = 0xFF;
|
|
prov->last_peer_msg_num = 0xFF;
|
|
|
|
l_timeout_remove(prov->tx_timeout);
|
|
prov->tx_timeout = NULL;
|
|
|
|
l_info("Link closed notification: %2.2x", pkt[0]);
|
|
|
|
if (prov->close_callback)
|
|
prov->close_callback(prov->receive_data, pkt[0]);
|
|
} else if ((type & 0x03) == 0x00) {
|
|
if (prov->conn_id != conn_id)
|
|
return;
|
|
|
|
if (msg_num == prov->last_peer_msg_num) {
|
|
send_ack(prov);
|
|
return;
|
|
}
|
|
|
|
prov->peer_msg_num = msg_num;
|
|
|
|
l_debug("Processing Data with %u fragments,%d octets",
|
|
type >> 2, l_get_be16(pkt));
|
|
adv_data_pkt(type, pkt, len, user_data);
|
|
|
|
} else if ((type & 0x03) == 0x02) {
|
|
if (prov->conn_id != conn_id)
|
|
return;
|
|
|
|
if (msg_num == prov->last_peer_msg_num) {
|
|
send_ack(prov);
|
|
return;
|
|
}
|
|
|
|
prov->peer_msg_num = msg_num;
|
|
|
|
l_debug("Processing fragment %u", type >> 2);
|
|
adv_data_pkt(type, pkt, len, user_data);
|
|
}
|
|
}
|
|
|
|
static void beacon_packet(void *user_data, struct mesh_io_recv_info *info,
|
|
const uint8_t *pkt, uint16_t len)
|
|
{
|
|
struct mesh_prov *prov = user_data;
|
|
struct mesh_io *io;
|
|
|
|
pkt++;
|
|
len--;
|
|
|
|
if (len < 19)
|
|
return;
|
|
|
|
if (!pkt[0])
|
|
print_packet("UnProv-BEACON-RX", pkt, len);
|
|
|
|
/* Ignore devices not matching UUID */
|
|
if (pkt[0] || memcmp(pkt + 1, prov->uuid, 16))
|
|
return;
|
|
|
|
io = mesh_net_get_io(prov->net);
|
|
mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_BEACON);
|
|
|
|
if ((prov->conn_id != DEFAULT_CONN_ID) ||
|
|
(prov->bearer != MESH_BEARER_IDLE)) {
|
|
l_info("PB-ADV: Already Provisioning");
|
|
return;
|
|
}
|
|
|
|
l_getrandom(&prov->conn_id, sizeof(prov->conn_id));
|
|
prov->bearer = MESH_BEARER_ADV;
|
|
send_open_req(prov);
|
|
}
|
|
|
|
static bool mesh_prov_enable(struct mesh_prov *prov, enum mesh_prov_mode mode,
|
|
uint8_t uuid[16])
|
|
{
|
|
const uint8_t pb_adv_data[] = { MESH_AD_TYPE_BEACON, 0 };
|
|
uint8_t adv_data[62];
|
|
uint8_t adv_len, type;
|
|
struct mesh_io *io;
|
|
struct mesh_io_send_info tx_info = {
|
|
.type = MESH_IO_TIMING_TYPE_GENERAL,
|
|
.u.gen.interval = 1000, /* ms */
|
|
.u.gen.cnt = 0, /* 0 == Infinite */
|
|
.u.gen.min_delay = 0, /* no delay */
|
|
.u.gen.max_delay = 0, /* no delay */
|
|
};
|
|
|
|
if (!prov || !prov->net)
|
|
return false;
|
|
|
|
|
|
prov->mode = mode;
|
|
memcpy(prov->uuid, uuid, 16);
|
|
prov->conn_id = DEFAULT_CONN_ID;
|
|
io = mesh_net_get_io(prov->net);
|
|
|
|
switch (mode) {
|
|
case MESH_PROV_MODE_NONE:
|
|
break;
|
|
case MESH_PROV_MODE_INITIATOR:
|
|
print_packet("Searching for uuid", uuid, 16);
|
|
prov->local_msg_num = DEFAULT_PROV_MSG_NUM;
|
|
prov->peer_msg_num = DEFAULT_DEV_MSG_NUM;
|
|
mesh_io_register_recv_cb(io, MESH_IO_FILTER_PROV,
|
|
adv_bearer_packet, prov);
|
|
mesh_io_register_recv_cb(io, MESH_IO_FILTER_BEACON,
|
|
beacon_packet, prov);
|
|
break;
|
|
|
|
case MESH_PROV_MODE_ADV_ACCEPTOR:
|
|
prov->local_msg_num = DEFAULT_DEV_MSG_NUM - 1;
|
|
prov->peer_msg_num = DEFAULT_PROV_MSG_NUM;
|
|
|
|
print_packet("Beaconing as unProvisioned uuid", uuid, 16);
|
|
adv_len = sizeof(pb_adv_data);
|
|
memcpy(adv_data, pb_adv_data, adv_len);
|
|
memcpy(adv_data + adv_len, uuid, 16);
|
|
adv_len += 16;
|
|
adv_len += 2;
|
|
mesh_io_register_recv_cb(io, MESH_IO_FILTER_PROV,
|
|
adv_bearer_packet, prov);
|
|
type = MESH_AD_TYPE_BEACON;
|
|
mesh_io_send_cancel(io, &type, 1);
|
|
mesh_io_send(io, &tx_info, adv_data, adv_len);
|
|
break;
|
|
|
|
case MESH_PROV_MODE_GATT_CLIENT:
|
|
case MESH_PROV_MODE_MESH_GATT_CLIENT:
|
|
case MESH_PROV_MODE_GATT_ACCEPTOR:
|
|
case MESH_PROV_MODE_MESH_SERVER:
|
|
case MESH_PROV_MODE_MESH_CLIENT:
|
|
default:
|
|
l_error("Unimplemented Prov Mode: %d", mode);
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_prov_listen(struct mesh_net *net, uint8_t uuid[16], uint8_t caps[12],
|
|
mesh_prov_open_func_t open_callback,
|
|
mesh_prov_close_func_t close_callback,
|
|
mesh_prov_receive_func_t recv_callback,
|
|
void *user_data)
|
|
{
|
|
struct mesh_prov *prov = mesh_net_get_prov(net);
|
|
|
|
if (!prov) {
|
|
prov = mesh_prov_new(net, 0);
|
|
if (!prov)
|
|
return false;
|
|
|
|
mesh_net_set_prov(net, prov);
|
|
}
|
|
|
|
prov->open_callback = open_callback;
|
|
prov->close_callback = close_callback;
|
|
prov->receive_callback = recv_callback;
|
|
prov->receive_data = prov; /* TODO: retink the callback placement */
|
|
memcpy(prov->caps, caps, sizeof(prov->caps));
|
|
|
|
prov->trans = MESH_TRANS_IDLE;
|
|
|
|
|
|
return mesh_prov_enable(prov, MESH_PROV_MODE_ADV_ACCEPTOR, uuid);
|
|
}
|
|
|
|
unsigned int mesh_prov_send(struct mesh_prov *prov,
|
|
const void *ptr, uint16_t size,
|
|
mesh_prov_send_func_t send_callback,
|
|
void *user_data)
|
|
{
|
|
const uint8_t *data = ptr;
|
|
|
|
if (!prov)
|
|
return 0;
|
|
|
|
if (prov->trans != MESH_TRANS_IDLE)
|
|
return 0;
|
|
|
|
if (prov->remote) {
|
|
/* TODO -- PB-Remote */
|
|
} else {
|
|
prov->send_callback = send_callback;
|
|
prov->send_data = user_data;
|
|
prov->trans = MESH_TRANS_TX;
|
|
prov->local_msg_num++;
|
|
send_adv_msg(prov, data, size);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool mesh_prov_close(struct mesh_prov *prov, uint8_t reason)
|
|
{
|
|
if (!prov)
|
|
return false;
|
|
|
|
prov->local_msg_num = 0;
|
|
send_close_ind(prov, reason);
|
|
|
|
prov->conn_id = DEFAULT_CONN_ID;
|
|
prov->local_msg_num = 0xFF;
|
|
prov->peer_msg_num = 0xFF;
|
|
prov->last_peer_msg_num = 0xFF;
|
|
|
|
if (prov->tx_timeout) {
|
|
l_timeout_remove(prov->tx_timeout);
|
|
|
|
/* If timing out, give Close indication 1 second of
|
|
* provisioning timing to get final Close indication out
|
|
*/
|
|
prov->tx_timeout = l_timeout_create(1, tx_timeout, prov, NULL);
|
|
}
|
|
|
|
if (prov->close_callback)
|
|
prov->close_callback(prov->receive_data, reason);
|
|
|
|
return false;
|
|
}
|
|
|
|
void mesh_prov_set_addr(struct mesh_prov *prov, uint16_t addr)
|
|
{
|
|
prov->addr = addr;
|
|
}
|
|
|
|
uint16_t mesh_prov_get_idx(struct mesh_prov *prov)
|
|
{
|
|
return prov->net_idx;
|
|
}
|