/*
|
* 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 <string.h>
|
|
#include "host/ble_store.h"
|
#include "ble_hs_priv.h"
|
|
int
|
ble_store_read(int obj_type, const union ble_store_key *key,
|
union ble_store_value *val)
|
{
|
int rc;
|
|
ble_hs_lock();
|
|
if (ble_hs_cfg.store_read_cb == NULL) {
|
rc = BLE_HS_ENOTSUP;
|
} else {
|
rc = ble_hs_cfg.store_read_cb(obj_type, key, val);
|
}
|
|
ble_hs_unlock();
|
|
return rc;
|
}
|
|
int
|
ble_store_write(int obj_type, const union ble_store_value *val)
|
{
|
int rc;
|
|
if (ble_hs_cfg.store_write_cb == NULL) {
|
return BLE_HS_ENOTSUP;
|
}
|
|
while (1) {
|
ble_hs_lock();
|
rc = ble_hs_cfg.store_write_cb(obj_type, val);
|
ble_hs_unlock();
|
|
switch (rc) {
|
case 0:
|
return 0;
|
case BLE_HS_ESTORE_CAP:
|
/* Record didn't fit. Give the application the opportunity to free
|
* up some space.
|
*/
|
rc = ble_store_overflow_event(obj_type, val);
|
if (rc != 0) {
|
return rc;
|
}
|
|
/* Application made room for the record; try again. */
|
break;
|
|
default:
|
return rc;
|
}
|
}
|
}
|
|
int
|
ble_store_delete(int obj_type, const union ble_store_key *key)
|
{
|
int rc;
|
|
ble_hs_lock();
|
|
if (ble_hs_cfg.store_delete_cb == NULL) {
|
rc = BLE_HS_ENOTSUP;
|
} else {
|
rc = ble_hs_cfg.store_delete_cb(obj_type, key);
|
}
|
|
ble_hs_unlock();
|
|
return rc;
|
}
|
|
static int
|
ble_store_status(struct ble_store_status_event *event)
|
{
|
int rc;
|
|
BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
|
|
if (ble_hs_cfg.store_status_cb == NULL) {
|
rc = BLE_HS_ENOTSUP;
|
} else {
|
rc = ble_hs_cfg.store_status_cb(event, ble_hs_cfg.store_status_arg);
|
}
|
|
return rc;
|
}
|
|
int
|
ble_store_overflow_event(int obj_type, const union ble_store_value *value)
|
{
|
struct ble_store_status_event event;
|
|
event.event_code = BLE_STORE_EVENT_OVERFLOW;
|
event.overflow.obj_type = obj_type;
|
event.overflow.value = value;
|
|
return ble_store_status(&event);
|
}
|
|
int
|
ble_store_full_event(int obj_type, uint16_t conn_handle)
|
{
|
struct ble_store_status_event event;
|
|
event.event_code = BLE_STORE_EVENT_FULL;
|
event.full.obj_type = obj_type;
|
event.full.conn_handle = conn_handle;
|
|
return ble_store_status(&event);
|
}
|
|
int
|
ble_store_read_our_sec(const struct ble_store_key_sec *key_sec,
|
struct ble_store_value_sec *value_sec)
|
{
|
const union ble_store_key *store_key;
|
union ble_store_value *store_value;
|
int rc;
|
|
BLE_HS_DBG_ASSERT(key_sec->peer_addr.type == BLE_ADDR_PUBLIC ||
|
key_sec->peer_addr.type == BLE_ADDR_RANDOM ||
|
ble_addr_cmp(&key_sec->peer_addr, BLE_ADDR_ANY) == 0);
|
|
store_key = (void *)key_sec;
|
store_value = (void *)value_sec;
|
rc = ble_store_read(BLE_STORE_OBJ_TYPE_OUR_SEC, store_key, store_value);
|
return rc;
|
}
|
|
static int
|
ble_store_persist_sec(int obj_type,
|
const struct ble_store_value_sec *value_sec)
|
{
|
union ble_store_value *store_value;
|
int rc;
|
|
BLE_HS_DBG_ASSERT(value_sec->peer_addr.type == BLE_ADDR_PUBLIC ||
|
value_sec->peer_addr.type == BLE_ADDR_RANDOM);
|
BLE_HS_DBG_ASSERT(value_sec->ltk_present ||
|
value_sec->irk_present ||
|
value_sec->csrk_present);
|
|
store_value = (void *)value_sec;
|
rc = ble_store_write(obj_type, store_value);
|
return rc;
|
}
|
|
int
|
ble_store_write_our_sec(const struct ble_store_value_sec *value_sec)
|
{
|
int rc;
|
|
rc = ble_store_persist_sec(BLE_STORE_OBJ_TYPE_OUR_SEC, value_sec);
|
return rc;
|
}
|
|
int
|
ble_store_delete_our_sec(const struct ble_store_key_sec *key_sec)
|
{
|
union ble_store_key *store_key;
|
int rc;
|
|
store_key = (void *)key_sec;
|
rc = ble_store_delete(BLE_STORE_OBJ_TYPE_OUR_SEC, store_key);
|
return rc;
|
}
|
|
int
|
ble_store_delete_peer_sec(const struct ble_store_key_sec *key_sec)
|
{
|
union ble_store_key *store_key;
|
int rc;
|
|
store_key = (void *)key_sec;
|
rc = ble_store_delete(BLE_STORE_OBJ_TYPE_PEER_SEC, store_key);
|
return rc;
|
}
|
|
int
|
ble_store_read_peer_sec(const struct ble_store_key_sec *key_sec,
|
struct ble_store_value_sec *value_sec)
|
{
|
union ble_store_value *store_value;
|
union ble_store_key *store_key;
|
int rc;
|
|
BLE_HS_DBG_ASSERT(key_sec->peer_addr.type == BLE_ADDR_PUBLIC ||
|
key_sec->peer_addr.type == BLE_ADDR_RANDOM);
|
|
store_key = (void *)key_sec;
|
store_value = (void *)value_sec;
|
rc = ble_store_read(BLE_STORE_OBJ_TYPE_PEER_SEC, store_key, store_value);
|
|
if (rc != 0) {
|
return rc;
|
}
|
|
return 0;
|
}
|
|
int
|
ble_store_write_peer_sec(const struct ble_store_value_sec *value_sec)
|
{
|
int rc;
|
|
rc = ble_store_persist_sec(BLE_STORE_OBJ_TYPE_PEER_SEC, value_sec);
|
if (rc != 0) {
|
return rc;
|
}
|
|
if (ble_addr_cmp(&value_sec->peer_addr, BLE_ADDR_ANY) &&
|
value_sec->irk_present) {
|
|
/* Write the peer IRK to the controller keycache
|
* There is not much to do here if it fails */
|
rc = ble_hs_pvcy_add_entry(value_sec->peer_addr.val,
|
value_sec->peer_addr.type,
|
value_sec->irk);
|
if (rc != 0) {
|
return rc;
|
}
|
}
|
|
return 0;
|
}
|
|
int
|
ble_store_read_cccd(const struct ble_store_key_cccd *key,
|
struct ble_store_value_cccd *out_value)
|
{
|
union ble_store_value *store_value;
|
union ble_store_key *store_key;
|
int rc;
|
|
store_key = (void *)key;
|
store_value = (void *)out_value;
|
rc = ble_store_read(BLE_STORE_OBJ_TYPE_CCCD, store_key, store_value);
|
return rc;
|
}
|
|
int
|
ble_store_write_cccd(const struct ble_store_value_cccd *value)
|
{
|
union ble_store_value *store_value;
|
int rc;
|
|
store_value = (void *)value;
|
rc = ble_store_write(BLE_STORE_OBJ_TYPE_CCCD, store_value);
|
return rc;
|
}
|
|
int
|
ble_store_delete_cccd(const struct ble_store_key_cccd *key)
|
{
|
union ble_store_key *store_key;
|
int rc;
|
|
store_key = (void *)key;
|
rc = ble_store_delete(BLE_STORE_OBJ_TYPE_CCCD, store_key);
|
return rc;
|
}
|
|
void
|
ble_store_key_from_value_cccd(struct ble_store_key_cccd *out_key,
|
const struct ble_store_value_cccd *value)
|
{
|
out_key->peer_addr = value->peer_addr;
|
out_key->chr_val_handle = value->chr_val_handle;
|
out_key->idx = 0;
|
}
|
|
void
|
ble_store_key_from_value_sec(struct ble_store_key_sec *out_key,
|
const struct ble_store_value_sec *value)
|
{
|
out_key->peer_addr = value->peer_addr;
|
|
out_key->ediv = value->ediv;
|
out_key->rand_num = value->rand_num;
|
out_key->ediv_rand_present = 1;
|
out_key->idx = 0;
|
}
|
|
void
|
ble_store_key_from_value(int obj_type,
|
union ble_store_key *out_key,
|
const union ble_store_value *value)
|
{
|
switch (obj_type) {
|
case BLE_STORE_OBJ_TYPE_OUR_SEC:
|
case BLE_STORE_OBJ_TYPE_PEER_SEC:
|
ble_store_key_from_value_sec(&out_key->sec, &value->sec);
|
break;
|
|
case BLE_STORE_OBJ_TYPE_CCCD:
|
ble_store_key_from_value_cccd(&out_key->cccd, &value->cccd);
|
break;
|
|
default:
|
BLE_HS_DBG_ASSERT(0);
|
break;
|
}
|
}
|
|
int
|
ble_store_iterate(int obj_type,
|
ble_store_iterator_fn *callback,
|
void *cookie)
|
{
|
union ble_store_key key;
|
union ble_store_value value;
|
int idx = 0;
|
uint8_t *pidx;
|
int rc;
|
|
/* a magic value to retrieve anything */
|
memset(&key, 0, sizeof(key));
|
switch(obj_type) {
|
case BLE_STORE_OBJ_TYPE_PEER_SEC:
|
case BLE_STORE_OBJ_TYPE_OUR_SEC:
|
key.sec.peer_addr = *BLE_ADDR_ANY;
|
pidx = &key.sec.idx;
|
break;
|
case BLE_STORE_OBJ_TYPE_CCCD:
|
key.cccd.peer_addr = *BLE_ADDR_ANY;
|
pidx = &key.cccd.idx;
|
break;
|
default:
|
BLE_HS_DBG_ASSERT(0);
|
return BLE_HS_EINVAL;
|
}
|
|
while (1) {
|
*pidx = idx;
|
rc = ble_store_read(obj_type, &key, &value);
|
switch (rc) {
|
case 0:
|
if (callback != NULL) {
|
rc = callback(obj_type, &value, cookie);
|
if (rc != 0) {
|
/* User function indicates to stop iterating. */
|
return 0;
|
}
|
}
|
break;
|
|
case BLE_HS_ENOENT:
|
/* No more entries. */
|
return 0;
|
|
default:
|
/* Read error. */
|
return rc;
|
}
|
|
idx++;
|
}
|
}
|
|
/**
|
* Deletes all objects from the BLE host store.
|
*
|
* @return 0 on success; nonzero on failure.
|
*/
|
int
|
ble_store_clear(void)
|
{
|
const uint8_t obj_types[] = {
|
BLE_STORE_OBJ_TYPE_OUR_SEC,
|
BLE_STORE_OBJ_TYPE_PEER_SEC,
|
BLE_STORE_OBJ_TYPE_CCCD,
|
};
|
union ble_store_key key;
|
int obj_type;
|
int rc;
|
int i;
|
|
/* A zeroed key will always retrieve the first value. */
|
memset(&key, 0, sizeof key);
|
|
for (i = 0; i < sizeof obj_types / sizeof obj_types[0]; i++) {
|
obj_type = obj_types[i];
|
|
do {
|
rc = ble_store_delete(obj_type, &key);
|
} while (rc == 0);
|
|
/* BLE_HS_ENOENT means we deleted everything. */
|
if (rc != BLE_HS_ENOENT) {
|
return rc;
|
}
|
}
|
|
return 0;
|
}
|