Bluetooth: storage: Add basic support for internal storage
Add basic implementation of an internal storage handler that uses the local file system. The root directory for all Bluetooth related data is /bt. Each remote device has its own subdirectory and each key its own file. This helps keep the implementation very simple, but does come with the meta-data overhead for each file. As an example, the value of a key 0x0001 for a device with a static random address cc:11:22:33:44:55 would be stored in the following file: /bt/cc11223344551/0001. Local values such as the identity address are stored directly under /bt with a file name that matches the key the same way as remote-device files. For full functionality the implementation requires a file system that can support file/directory names of up to 13 characters in length. If the file system supports less than that (as is the case with FAT12) then only local values can be stored (in /bt/abcd). Local values include the identity address as well as the local IRK. Jira: ZEP-181 Change-Id: I7dc696af6353a154cb00dcd01a5f4ac3d7127e6b Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
parent
e18e3ed60a
commit
77a8516dd2
3 changed files with 247 additions and 0 deletions
|
@ -137,6 +137,14 @@ config BLUETOOTH_L2CAP_IN_MTU
|
|||
endif # BLUETOOTH_LE && BLUETOOTH_CONN || BLUETOOTH_STACK_HCI_RAW
|
||||
|
||||
if BLUETOOTH_LE
|
||||
config BLUETOOTH_INTERNAL_STORAGE
|
||||
bool "Use an internal persistent storage handler"
|
||||
depends on FILE_SYSTEM
|
||||
help
|
||||
When selected the application doesn't need to register its own
|
||||
persistent storage handlers through the bt_storage API, rather
|
||||
an internal default handler is used for this.
|
||||
|
||||
config BLUETOOTH_RX_STACK_SIZE
|
||||
int "Size of the receiving fiber stack"
|
||||
default 1024
|
||||
|
|
|
@ -13,6 +13,8 @@ obj-$(CONFIG_BLUETOOTH_DEBUG_MONITOR) += monitor.o
|
|||
|
||||
obj-$(CONFIG_BLUETOOTH_TINYCRYPT_ECC) += hci_ecc.o
|
||||
|
||||
obj-$(CONFIG_BLUETOOTH_INTERNAL_STORAGE) += storage.o
|
||||
|
||||
ifeq ($(CONFIG_BLUETOOTH_CONN),y)
|
||||
obj-y += conn.o l2cap.o att.o gatt.o
|
||||
|
||||
|
|
237
subsys/bluetooth/host/storage.c
Normal file
237
subsys/bluetooth/host/storage.c
Normal file
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed 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 <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <init.h>
|
||||
#include <fs.h>
|
||||
|
||||
#include <bluetooth/log.h>
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/storage.h>
|
||||
|
||||
#define STORAGE_ROOT "/bt"
|
||||
|
||||
/* Required file name length for full storage support. If the maximum
|
||||
* file name length supported by the chosen file system is less than
|
||||
* this value, then only local keys are supported (/bt/abcd).
|
||||
*/
|
||||
#define STORAGE_FILE_NAME_LEN 13
|
||||
|
||||
#if MAX_FILE_NAME >= STORAGE_FILE_NAME_LEN
|
||||
/* /bt/aabbccddeeff0/abcd */
|
||||
#define STORAGE_PATH_MAX 23
|
||||
#else
|
||||
/* /bt/abcd */
|
||||
#define STORAGE_PATH_MAX 9
|
||||
#endif
|
||||
|
||||
enum storage_access {
|
||||
STORAGE_READ,
|
||||
STORAGE_WRITE
|
||||
};
|
||||
|
||||
static int storage_open(const bt_addr_le_t *addr, uint16_t key,
|
||||
enum storage_access access, fs_file_t *file)
|
||||
{
|
||||
char path[STORAGE_PATH_MAX];
|
||||
|
||||
if (addr) {
|
||||
#if MAX_FILE_NAME >= STORAGE_FILE_NAME_LEN
|
||||
int len;
|
||||
|
||||
len = snprintf(path, sizeof(path),
|
||||
STORAGE_ROOT "/%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%u",
|
||||
addr->a.val[5], addr->a.val[4], addr->a.val[3],
|
||||
addr->a.val[2], addr->a.val[1], addr->a.val[0],
|
||||
addr->type);
|
||||
|
||||
/* Create the subdirectory if necessary */
|
||||
if (access == STORAGE_WRITE) {
|
||||
struct fs_dirent entry;
|
||||
int err;
|
||||
|
||||
err = fs_stat(path, &entry);
|
||||
if (err) {
|
||||
err = fs_mkdir(path);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(path + len, sizeof(path) - len, "/%04x", key);
|
||||
#else
|
||||
return -ENAMETOOLONG;
|
||||
#endif
|
||||
} else {
|
||||
snprintf(path, sizeof(path), STORAGE_ROOT "/%04x", key);
|
||||
}
|
||||
|
||||
return fs_open(file, path);
|
||||
}
|
||||
|
||||
static ssize_t storage_read(const bt_addr_le_t *addr, uint16_t key, void *data,
|
||||
size_t length)
|
||||
{
|
||||
fs_file_t file;
|
||||
ssize_t ret;
|
||||
|
||||
ret = storage_open(addr, key, STORAGE_READ, &file);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = fs_read(&file, data, length);
|
||||
fs_close(&file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t storage_write(const bt_addr_le_t *addr, uint16_t key,
|
||||
const void *data, size_t length)
|
||||
{
|
||||
fs_file_t file;
|
||||
ssize_t ret;
|
||||
|
||||
ret = storage_open(addr, key, STORAGE_WRITE, &file);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = fs_write(&file, data, length);
|
||||
fs_close(&file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int unlink_recursive(char path[STORAGE_PATH_MAX])
|
||||
{
|
||||
size_t path_len;
|
||||
fs_dir_t dir;
|
||||
int err;
|
||||
|
||||
err = fs_opendir(&dir, path);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* We calculate this up-front so we can keep reusing the same
|
||||
* buffer for the path when recursing.
|
||||
*/
|
||||
path_len = strlen(path);
|
||||
|
||||
while (1) {
|
||||
struct fs_dirent entry;
|
||||
|
||||
err = fs_readdir(&dir, &entry);
|
||||
if (err) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (entry.name[0] == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(path + path_len, STORAGE_PATH_MAX - path_len, "/%s",
|
||||
entry.name);
|
||||
|
||||
if (entry.type == FS_DIR_ENTRY_DIR) {
|
||||
err = unlink_recursive(path);
|
||||
} else {
|
||||
err = fs_unlink(path);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fs_closedir(&dir);
|
||||
|
||||
/* Return to the original value */
|
||||
path[path_len] = '\0';
|
||||
|
||||
fs_unlink(path);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int storage_clear(const bt_addr_le_t *addr)
|
||||
{
|
||||
char path[STORAGE_PATH_MAX];
|
||||
int err;
|
||||
|
||||
if (addr) {
|
||||
#if MAX_FILE_NAME >= STORAGE_FILE_NAME_LEN
|
||||
snprintf(path, STORAGE_PATH_MAX,
|
||||
STORAGE_ROOT "/%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%u",
|
||||
addr->a.val[5], addr->a.val[4], addr->a.val[3],
|
||||
addr->a.val[2], addr->a.val[1], addr->a.val[0],
|
||||
addr->type);
|
||||
|
||||
return unlink_recursive(path);
|
||||
#else
|
||||
return -ENAMETOOLONG;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* unlink_recursive() uses the given path as a buffer for
|
||||
* constructing sub-paths, so we can't give it a string literal
|
||||
* such as STORAGE_ROOT directly.
|
||||
*/
|
||||
strcpy(path, STORAGE_ROOT);
|
||||
|
||||
err = unlink_recursive(path);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return fs_mkdir(STORAGE_ROOT);
|
||||
}
|
||||
|
||||
static int storage_init(struct device *unused)
|
||||
{
|
||||
static const struct bt_storage storage = {
|
||||
.read = storage_read,
|
||||
.write = storage_write,
|
||||
.clear = storage_clear
|
||||
};
|
||||
struct fs_dirent entry;
|
||||
int err;
|
||||
|
||||
err = fs_stat(STORAGE_ROOT, &entry);
|
||||
if (err) {
|
||||
BT_WARN("%s doesn't seem to exist (err %d). Creating it.",
|
||||
STORAGE_ROOT, err);
|
||||
|
||||
err = fs_mkdir(STORAGE_ROOT);
|
||||
if (err) {
|
||||
BT_ERR("Unable to create %s (err %d)",
|
||||
STORAGE_ROOT, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
bt_storage_register(&storage);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(storage_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
Loading…
Add table
Add a link
Reference in a new issue