blob: 79b2f7b95e2f16d69aa2b1aa0ccae82997cc15a7 [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 <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->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;
unsigned 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;
}