/* * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLUETOOTH_DEBUG_HCI_CORE) #include #include #include #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 = snprintk(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; } } } snprintk(path + len, sizeof(path) - len, "/%04x", key); #else return -ENAMETOOLONG; #endif } else { snprintk(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; } snprintk(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 snprintk(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);