blob: e1c2ae3f4a109fed2ae806a49b8a409c4cd184d1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "syscfg/syscfg.h"
#define MESH_LOG_MODULE BLE_MESH_LOG
#include "mesh/glue.h"
#include "adv.h"
#ifndef MYNEWT
#include "nimble/nimble_port.h"
#endif
#if MYNEWT_VAL(BLE_MESH_SETTINGS)
#include "base64/base64.h"
#endif
extern uint8_t g_mesh_addr_type;
#if MYNEWT_VAL(BLE_EXT_ADV)
/* Store configuration for different bearers */
#define BT_MESH_ADV_IDX (0)
#define BT_MESH_GATT_IDX (1)
static struct ble_gap_adv_params ble_adv_cur_conf[2];
#endif
const char *
bt_hex(const void *buf, size_t len)
{
static const char hex[] = "0123456789abcdef";
static char hexbufs[4][137];
static uint8_t curbuf;
const uint8_t *b = buf;
char *str;
int i;
str = hexbufs[curbuf++];
curbuf %= ARRAY_SIZE(hexbufs);
len = min(len, (sizeof(hexbufs[0]) - 1) / 2);
for (i = 0; i < len; i++) {
str[i * 2] = hex[b[i] >> 4];
str[i * 2 + 1] = hex[b[i] & 0xf];
}
str[i * 2] = '\0';
return str;
}
void
net_buf_put(struct ble_npl_eventq *fifo, struct os_mbuf *om)
{
struct ble_npl_event *ev;
assert(OS_MBUF_IS_PKTHDR(om));
ev = &BT_MESH_ADV(om)->ev;
assert(ev);
assert(ble_npl_event_get_arg(ev));
ble_npl_eventq_put(fifo, ev);
}
void *
net_buf_ref(struct os_mbuf *om)
{
struct bt_mesh_adv *adv;
/* For bufs with header we count refs*/
if (OS_MBUF_USRHDR_LEN(om) == 0) {
return om;
}
adv = BT_MESH_ADV(om);
adv->ref_cnt++;
return om;
}
void
net_buf_unref(struct os_mbuf *om)
{
struct bt_mesh_adv *adv;
/* For bufs with header we count refs*/
if (OS_MBUF_USRHDR_LEN(om) == 0) {
goto free;
}
adv = BT_MESH_ADV(om);
if (--adv->ref_cnt > 0) {
return;
}
free:
os_mbuf_free_chain(om);
}
int
bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data)
{
struct tc_aes_key_sched_struct s;
if (tc_aes128_set_encrypt_key(&s, key) == TC_CRYPTO_FAIL) {
return BLE_HS_EUNKNOWN;
}
if (tc_aes_encrypt(enc_data, plaintext, &s) == TC_CRYPTO_FAIL) {
return BLE_HS_EUNKNOWN;
}
return 0;
}
uint16_t
net_buf_simple_pull_le16(struct os_mbuf *om)
{
uint16_t val;
struct os_mbuf *old = om;
om = os_mbuf_pullup(om, sizeof(val));
assert(om == old);
val = get_le16(om->om_data);
os_mbuf_adj(om, sizeof(val));
return val;
}
uint16_t
net_buf_simple_pull_be16(struct os_mbuf *om)
{
uint16_t val;
struct os_mbuf *old = om;
om = os_mbuf_pullup(om, sizeof(val));
assert(om == old);
val = get_be16(om->om_data);
os_mbuf_adj(om, sizeof(val));
return val;
}
uint32_t
net_buf_simple_pull_be32(struct os_mbuf *om)
{
uint32_t val;
struct os_mbuf *old = om;
om = os_mbuf_pullup(om, sizeof(val));
assert(om == old);
val = get_be32(om->om_data);
os_mbuf_adj(om, sizeof(val));
return val;
}
uint32_t
net_buf_simple_pull_le32(struct os_mbuf *om)
{
uint32_t val;
struct os_mbuf *old = om;
om = os_mbuf_pullup(om, sizeof(val));
assert(om == old);
val = get_le32(om->om_data);
os_mbuf_adj(om, sizeof(val));
return val;
}
uint8_t
net_buf_simple_pull_u8(struct os_mbuf *om)
{
uint8_t val;
struct os_mbuf *old = om;
om = os_mbuf_pullup(om, sizeof(val));
assert(om == old);
val = om->om_data[0];
os_mbuf_adj(om, 1);
return val;
}
void
net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val)
{
val = htole16(val);
os_mbuf_append(om, &val, sizeof(val));
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val)
{
val = htobe16(val);
os_mbuf_append(om, &val, sizeof(val));
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_le24(struct os_mbuf *om, uint32_t val)
{
val = htole32(val);
os_mbuf_append(om, &val, 3);
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val)
{
val = htobe32(val);
os_mbuf_append(om, &val, sizeof(val));
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_le32(struct os_mbuf *om, uint32_t val)
{
val = htole32(val);
os_mbuf_append(om, &val, sizeof(val));
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val)
{
os_mbuf_append(om, &val, 1);
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_push_le16(struct os_mbuf *om, uint16_t val)
{
uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
assert(headroom >= 2);
om->om_data -= 2;
put_le16(om->om_data, val);
om->om_len += 2;
if (om->om_pkthdr_len) {
OS_MBUF_PKTHDR(om)->omp_len += 2;
}
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_push_be16(struct os_mbuf *om, uint16_t val)
{
uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
assert(headroom >= 2);
om->om_data -= 2;
put_be16(om->om_data, val);
om->om_len += 2;
if (om->om_pkthdr_len) {
OS_MBUF_PKTHDR(om)->omp_len += 2;
}
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_push_be24(struct os_mbuf *om, uint32_t val)
{
uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
assert(headroom >= 3);
om->om_data -= 3;
put_be24(om->om_data, val);
om->om_len += 3;
if (om->om_pkthdr_len) {
OS_MBUF_PKTHDR(om)->omp_len += 3;
}
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_push_u8(struct os_mbuf *om, uint8_t val)
{
uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
assert(headroom >= 1);
om->om_data -= 1;
om->om_data[0] = val;
om->om_len += 1;
if (om->om_pkthdr_len) {
OS_MBUF_PKTHDR(om)->omp_len += 1;
}
ASSERT_NOT_CHAIN(om);
}
void
net_buf_add_zeros(struct os_mbuf *om, uint8_t len)
{
uint8_t z[len];
int rc;
memset(z, 0, len);
rc = os_mbuf_append(om, z, len);
if(rc) {
assert(0);
}
ASSERT_NOT_CHAIN(om);
}
void *
net_buf_simple_pull(struct os_mbuf *om, uint8_t len)
{
os_mbuf_adj(om, len);
return om->om_data;
}
void *
net_buf_simple_pull_mem(struct os_mbuf *om, uint8_t len)
{
void *data = om->om_data;
net_buf_simple_pull_mem(om, len);
return data;
}
void*
net_buf_simple_add(struct os_mbuf *om, uint8_t len)
{
void * tmp;
tmp = os_mbuf_extend(om, len);
ASSERT_NOT_CHAIN(om);
return tmp;
}
bool
k_fifo_is_empty(struct ble_npl_eventq *q)
{
return ble_npl_eventq_is_empty(q);
}
void * net_buf_get(struct ble_npl_eventq *fifo, int32_t t)
{
struct ble_npl_event *ev = ble_npl_eventq_get(fifo, 0);
if (ev) {
return ble_npl_event_get_arg(ev);
}
return NULL;
}
uint8_t *
net_buf_simple_push(struct os_mbuf *om, uint8_t len)
{
uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
assert(headroom >= len);
om->om_data -= len;
om->om_len += len;
return om->om_data;
}
void
net_buf_reserve(struct os_mbuf *om, size_t reserve)
{
/* We need reserve to be done on fresh buf */
assert(om->om_len == 0);
om->om_data += reserve;
}
void
k_work_init(struct ble_npl_callout *work, ble_npl_event_fn handler)
{
#ifndef MYNEWT
ble_npl_callout_init(work, nimble_port_get_dflt_eventq(), handler, NULL);
#else
ble_npl_callout_init(work, ble_npl_eventq_dflt_get(), handler, NULL);
#endif
}
void
k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f)
{
#ifndef MYNEWT
ble_npl_callout_init(&w->work, nimble_port_get_dflt_eventq(), f, NULL);
#else
ble_npl_callout_init(&w->work, ble_npl_eventq_dflt_get(), f, NULL);
#endif
}
void
k_delayed_work_cancel(struct k_delayed_work *w)
{
ble_npl_callout_stop(&w->work);
}
void
k_delayed_work_submit(struct k_delayed_work *w, uint32_t ms)
{
uint32_t ticks;
if (ble_npl_time_ms_to_ticks(ms, &ticks) != 0) {
assert(0);
}
ble_npl_callout_reset(&w->work, ticks);
}
void
k_work_submit(struct ble_npl_callout *w)
{
ble_npl_callout_reset(w, 0);
}
void
k_work_add_arg(struct ble_npl_callout *w, void *arg)
{
ble_npl_callout_set_arg(w, arg);
}
void
k_delayed_work_add_arg(struct k_delayed_work *w, void *arg)
{
k_work_add_arg(&w->work, arg);
}
uint32_t
k_delayed_work_remaining_get (struct k_delayed_work *w)
{
int sr;
ble_npl_time_t t;
OS_ENTER_CRITICAL(sr);
t = ble_npl_callout_remaining_ticks(&w->work, ble_npl_time_get());
OS_EXIT_CRITICAL(sr);
return ble_npl_time_ticks_to_ms32(t);
}
int64_t k_uptime_get(void)
{
/* We should return ms */
return ble_npl_time_ticks_to_ms32(ble_npl_time_get());
}
uint32_t k_uptime_get_32(void)
{
return k_uptime_get();
}
void k_sleep(int32_t duration)
{
uint32_t ticks;
ticks = ble_npl_time_ms_to_ticks32(duration);
ble_npl_time_delay(ticks);
}
static uint8_t pub[64];
static uint8_t priv[32];
static bool has_pub = false;
int
bt_dh_key_gen(const uint8_t remote_pk[64], bt_dh_key_cb_t cb)
{
uint8_t dh[32];
if (ble_sm_alg_gen_dhkey((uint8_t *)&remote_pk[0], (uint8_t *)&remote_pk[32],
priv, dh)) {
return -1;
}
cb(dh);
return 0;
}
int
bt_rand(void *buf, size_t len)
{
int rc;
rc = ble_hs_hci_util_rand(buf, len);
if (rc != 0) {
return -1;
}
return 0;
}
int
bt_pub_key_gen(struct bt_pub_key_cb *new_cb)
{
if (ble_sm_alg_gen_key_pair(pub, priv)) {
assert(0);
return -1;
}
new_cb->func(pub);
has_pub = true;
return 0;
}
uint8_t *
bt_pub_key_get(void)
{
if (!has_pub) {
return NULL;
}
return pub;
}
static int
set_ad(const struct bt_data *ad, size_t ad_len, uint8_t *buf, uint8_t *buf_len)
{
int i;
for (i = 0; i < ad_len; i++) {
buf[(*buf_len)++] = ad[i].data_len + 1;
buf[(*buf_len)++] = ad[i].type;
memcpy(&buf[*buf_len], ad[i].data,
ad[i].data_len);
*buf_len += ad[i].data_len;
}
return 0;
}
#if MYNEWT_VAL(BLE_EXT_ADV)
static void
ble_adv_copy_to_ext_param(struct ble_gap_ext_adv_params *ext_param,
const struct ble_gap_adv_params *param)
{
memset(ext_param, 0, sizeof(*ext_param));
ext_param->legacy_pdu = 1;
if (param->conn_mode != BLE_GAP_CONN_MODE_NON) {
ext_param->connectable = 1;
ext_param->scannable = 1;
}
ext_param->itvl_max = param->itvl_max;
ext_param->itvl_min = param->itvl_min;
ext_param->channel_map = param->channel_map;
ext_param->high_duty_directed = param->high_duty_cycle;
ext_param->own_addr_type = g_mesh_addr_type;
}
static int
ble_adv_conf_adv_instance(const struct ble_gap_adv_params *param, int *instance)
{
struct ble_gap_ext_adv_params ext_params;
struct ble_gap_adv_params *cur_conf;
int err = 0;
if (param->conn_mode == BLE_GAP_CONN_MODE_NON) {
*instance = BT_MESH_ADV_INST;
cur_conf = &ble_adv_cur_conf[BT_MESH_ADV_IDX];
} else {
#if MYNEWT_VAL(BLE_MESH_PROXY)
*instance = BT_MESH_ADV_GATT_INST;
cur_conf = &ble_adv_cur_conf[BT_MESH_GATT_IDX];
#else
assert(0);
#endif
}
/* Checking interval max as it has to be in place if instance was configured
* before.
*/
if (cur_conf->itvl_max == 0) {
goto configure;
}
if (memcmp(param, cur_conf, sizeof(*cur_conf)) == 0) {
/* Same parameters - skip reconfiguring */
goto done;
}
ble_gap_ext_adv_stop(*instance);
err = ble_gap_ext_adv_remove(*instance);
if (err) {
assert(0);
goto done;
}
configure:
ble_adv_copy_to_ext_param(&ext_params, param);
err = ble_gap_ext_adv_configure(*instance, &ext_params, 0,
ble_adv_gap_mesh_cb, NULL);
if (!err) {
memcpy(cur_conf, param, sizeof(*cur_conf));
}
done:
return err;
}
int
bt_le_adv_start(const struct ble_gap_adv_params *param,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len)
{
struct os_mbuf *data;
int instance;
int err;
uint8_t buf[BLE_HS_ADV_MAX_SZ];
uint8_t buf_len = 0;
err = ble_adv_conf_adv_instance(param, &instance);
if (err) {
return err;
}
if (ad_len > 0) {
err = set_ad(ad, ad_len, buf, &buf_len);
if (err) {
return err;
}
/* For now let's use msys pool. We are not putting more then legacy */
data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_SZ, 0);
if (!data) {
return OS_ENOMEM;
}
err = os_mbuf_append(data, buf, buf_len);
if (err) {
goto error;
}
err = ble_gap_ext_adv_set_data(instance, data);
if (err) {
return err;
}
data = NULL;
}
if (sd_len > 0) {
buf_len = 0;
err = set_ad(sd, sd_len, buf, &buf_len);
if (err) {
return err;
}
/* For now let's use msys pool. We are not putting more then legace*/
data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_SZ, 0);
if (!data) {
return OS_ENOMEM;
}
err = os_mbuf_append(data, buf, buf_len);
if (err) {
goto error;
}
err = ble_gap_ext_adv_rsp_set_data(instance, data);
if (err) {
goto error;
}
}
/*TODO: We could use duration and max events in the future */
err = ble_gap_ext_adv_start(instance, 0, 0);
return err;
error:
if (data) {
os_mbuf_free_chain(data);
}
return err;
}
int bt_le_adv_stop(bool proxy)
{
#if MYNEWT_VAL(BLE_MESH_PROXY)
int rc;
if (proxy) {
rc = ble_gap_ext_adv_stop(BT_MESH_ADV_GATT_INST);
} else {
rc = ble_gap_ext_adv_stop(BT_MESH_ADV_INST);
}
return rc;
#else
return ble_gap_ext_adv_stop(BT_MESH_ADV_INST);
#endif
}
#else
int
bt_le_adv_start(const struct ble_gap_adv_params *param,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len)
{
uint8_t buf[BLE_HS_ADV_MAX_SZ];
uint8_t buf_len = 0;
int err;
err = set_ad(ad, ad_len, buf, &buf_len);
if (err) {
return err;
}
err = ble_gap_adv_set_data(buf, buf_len);
if (err != 0) {
return err;
}
if (sd) {
buf_len = 0;
err = set_ad(sd, sd_len, buf, &buf_len);
if (err) {
BT_ERR("Advertising failed: err %d", err);
return err;
}
err = ble_gap_adv_rsp_set_data(buf, buf_len);
if (err != 0) {
BT_ERR("Advertising failed: err %d", err);
return err;
}
}
err = ble_gap_adv_start(g_mesh_addr_type, NULL, BLE_HS_FOREVER, param,
NULL, NULL);
if (err) {
BT_ERR("Advertising failed: err %d", err);
return err;
}
return 0;
}
int bt_le_adv_stop(bool proxy)
{
return ble_gap_adv_stop();
}
#endif
#if MYNEWT_VAL(BLE_MESH_PROXY)
int bt_mesh_proxy_svcs_register(void);
#endif
void
bt_mesh_register_gatt(void)
{
#if MYNEWT_VAL(BLE_MESH_PROXY)
bt_mesh_proxy_svcs_register();
#endif
}
void net_buf_slist_init(struct net_buf_slist_t *list)
{
STAILQ_INIT(list);
}
bool net_buf_slist_is_empty(struct net_buf_slist_t *list)
{
return STAILQ_EMPTY(list);
}
struct os_mbuf *net_buf_slist_peek_head(struct net_buf_slist_t *list)
{
struct os_mbuf_pkthdr *pkthdr;
/* Get mbuf pointer from packet header pointer */
pkthdr = STAILQ_FIRST(list);
if (!pkthdr) {
return NULL;
}
return OS_MBUF_PKTHDR_TO_MBUF(pkthdr);
}
struct os_mbuf *net_buf_slist_peek_next(struct os_mbuf *buf)
{
struct os_mbuf_pkthdr *pkthdr;
/* Get mbuf pointer from packet header pointer */
pkthdr = OS_MBUF_PKTHDR(buf);
pkthdr = STAILQ_NEXT(pkthdr, omp_next);
if (!pkthdr) {
return NULL;
}
return OS_MBUF_PKTHDR_TO_MBUF(pkthdr);
}
struct os_mbuf *net_buf_slist_get(struct net_buf_slist_t *list)
{
os_sr_t sr;
struct os_mbuf *m;
m = net_buf_slist_peek_head(list);
if (!m) {
return NULL;
}
/* Remove from queue */
OS_ENTER_CRITICAL(sr);
STAILQ_REMOVE_HEAD(list, omp_next);
OS_EXIT_CRITICAL(sr);
return m;
}
void net_buf_slist_put(struct net_buf_slist_t *list, struct os_mbuf *buf)
{
struct os_mbuf_pkthdr *pkthdr;
pkthdr = OS_MBUF_PKTHDR(buf);
STAILQ_INSERT_TAIL(list, pkthdr, omp_next);
}
void net_buf_slist_remove(struct net_buf_slist_t *list, struct os_mbuf *prev,
struct os_mbuf *cur)
{
struct os_mbuf_pkthdr *pkthdr, *cur_pkthdr;
cur_pkthdr = OS_MBUF_PKTHDR(cur);
STAILQ_FOREACH(pkthdr, list, omp_next) {
if (cur_pkthdr == pkthdr) {
STAILQ_REMOVE(list, cur_pkthdr, os_mbuf_pkthdr, omp_next);
break;
}
}
}
void net_buf_slist_merge_slist(struct net_buf_slist_t *list,
struct net_buf_slist_t *list_to_append)
{
if (!STAILQ_EMPTY(list_to_append)) {
*(list)->stqh_last = list_to_append->stqh_first;
(list)->stqh_last = list_to_append->stqh_last;
STAILQ_INIT(list_to_append);
}
}
/** Memory slab methods */
extern void k_mem_slab_free(struct k_mem_slab *slab, void **mem)
{
**(char ***)mem = slab->free_list;
slab->free_list = *(char **)mem;
slab->num_used--;
}
extern int k_mem_slab_alloc(struct k_mem_slab *slab, void **mem)
{
int result;
if (slab->free_list != NULL) {
/* take a free block */
*mem = slab->free_list;
slab->free_list = *(char **)(slab->free_list);
slab->num_used++;
result = 0;
} else {
*mem = NULL;
result = -ENOMEM;
}
return result;
}
#if MYNEWT_VAL(BLE_MESH_SETTINGS)
int settings_bytes_from_str(char *val_str, void *vp, int *len)
{
*len = base64_decode(val_str, vp);
return 0;
}
char *settings_str_from_bytes(const void *vp, int vp_len,
char *buf, int buf_len)
{
if (BASE64_ENCODE_SIZE(vp_len) > buf_len) {
return NULL;
}
base64_encode(vp, vp_len, buf, 1);
return buf;
}
#endif /* MYNEWT_VAL(BLE_MESH_SETTINGS) */