| /* |
| * 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; |
| } |