Example #1
0
bool btif_config_set_bin(const char *section, const char *key, const uint8_t *value, size_t length) {
  const char *lookup = "0123456789abcdef";

  assert(config != NULL);
  assert(section != NULL);
  assert(key != NULL);

  if (length > 0)
      assert(value != NULL);

  char *str = (char *)osi_calloc(length * 2 + 1);
  if (!str)
    return false;

  for (size_t i = 0; i < length; ++i) {
    str[(i * 2) + 0] = lookup[(value[i] >> 4) & 0x0F];
    str[(i * 2) + 1] = lookup[value[i] & 0x0F];
  }

  pthread_mutex_lock(&lock);
  config_set_string(config, section, key, str);
  pthread_mutex_unlock(&lock);

  osi_free(str);
  return true;
}
Example #2
0
socket_t *socket_new(void) {
  socket_t *ret = (socket_t *)osi_calloc(sizeof(socket_t));
  if (!ret) {
    LOG_ERROR("%s unable to allocate memory for socket.", __func__);
    goto error;
  }

  ret->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (ret->fd == INVALID_FD) {
    LOG_ERROR("%s unable to create socket: %s", __func__, strerror(errno));
    goto error;
  }

  int enable = 1;
  if (setsockopt(ret->fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == -1) {
    LOG_ERROR("%s unable to set SO_REUSEADDR: %s", __func__, strerror(errno));
    goto error;
  }

  return ret;

error:;
  if (ret)
    close(ret->fd);
  osi_free(ret);
  return NULL;
}
Example #3
0
classic_peer_t *classic_peer_by_address(bt_bdaddr_t *address) {
  assert(initialized);
  assert(address != NULL);

  classic_peer_t *peer = hash_map_get(peers_by_address, address);

  if (!peer) {
    pthread_mutex_lock(&bag_of_peers_lock);

    // Make sure it didn't get added in the meantime
    peer = hash_map_get(peers_by_address, address);
    if (peer)
      goto done;

    // Splice in a new peer struct on behalf of the caller.
    peer = osi_calloc(sizeof(classic_peer_t));
    peer->address = *address;
    hash_map_set(peers_by_address, &peer->address, peer);

    pthread_mutex_unlock(&bag_of_peers_lock);
  }

done:
  return peer;
}
data_dispatcher_t *data_dispatcher_new(const char *name) {
  assert(name != NULL);

  data_dispatcher_t *ret = osi_calloc(sizeof(data_dispatcher_t));
  if (!ret) {
    LOG_ERROR(LOG_TAG, "%s unable to allocate memory for new data dispatcher.", __func__);
    goto error;
  }

  ret->dispatch_table = hash_map_new(DEFAULT_TABLE_BUCKETS, hash_function_naive, NULL, NULL, NULL);
  if (!ret->dispatch_table) {
    LOG_ERROR(LOG_TAG, "%s unable to create dispatch table.", __func__);
    goto error;
  }

  ret->name = osi_strdup(name);
  if (!ret->name) {
    LOG_ERROR(LOG_TAG, "%s unable to duplicate provided name.", __func__);
    goto error;
  }

  return ret;

error:;
  data_dispatcher_free(ret);
  return NULL;
}
Example #5
0
static void accept_ready(socket_t *socket, UNUSED_ATTR void *context) {
  assert(socket != NULL);
  assert(socket == listen_socket);

  socket = socket_accept(socket);
  if (!socket)
    return;

  client_t *client = (client_t *)osi_calloc(sizeof(client_t));
  if (!client) {
    LOG_ERROR(LOG_TAG, "%s unable to allocate memory for client.", __func__);
    socket_free(socket);
    return;
  }

  client->socket = socket;

  if (!list_append(clients, client)) {
    LOG_ERROR(LOG_TAG, "%s unable to add client to list.", __func__);
    client_free(client);
    return;
  }

  socket_register(socket, thread_get_reactor(thread), client, read_ready, NULL);
}
Example #6
0
static void transmit_command(
    BT_HDR *command,
    command_complete_cb complete_callback,
    command_status_cb status_callback,
    void *context)
{
    uint8_t *stream;
    waiting_command_t *wait_entry = osi_calloc(sizeof(waiting_command_t));
    if (!wait_entry) {
        LOG_ERROR("%s couldn't allocate space for wait entry.", __func__);
        return;
    }

    stream = command->data + command->offset;
    STREAM_TO_UINT16(wait_entry->opcode, stream);
    wait_entry->complete_callback = complete_callback;
    wait_entry->status_callback = status_callback;
    wait_entry->command = command;
    wait_entry->context = context;

    // Store the command message type in the event field
    // in case the upper layer didn't already
    command->event = MSG_STACK_TO_HC_HCI_CMD;
    LOG_DEBUG("HCI Enqueue Comamnd opcode=0x%x\n", wait_entry->opcode);
    BTTRC_DUMP_BUFFER(NULL, command->data + command->offset, command->len);

    fixed_queue_enqueue(hci_host_env.command_queue, wait_entry);
    hci_host_task_post();
}
Example #7
0
eager_reader_t *eager_reader_new(
    int fd_to_read,
    const allocator_t *allocator,
    size_t buffer_size,
    size_t max_buffer_count,
    const char *thread_name) {

  assert(fd_to_read != INVALID_FD);
  assert(allocator != NULL);
  assert(buffer_size > 0);
  assert(max_buffer_count > 0);
  assert(thread_name != NULL && *thread_name != '\0');

  eager_reader_t *ret = osi_calloc(sizeof(eager_reader_t));
  if (!ret) {
    LOG_ERROR(LOG_TAG, "%s unable to allocate memory for new eager_reader.", __func__);
    goto error;
  }

  ret->allocator = allocator;
  ret->inbound_fd = fd_to_read;

  ret->bytes_available_fd = eventfd(0, 0);
  if (ret->bytes_available_fd == INVALID_FD) {
    LOG_ERROR(LOG_TAG, "%s unable to create output reading semaphore.", __func__);
    goto error;
  }

  ret->buffer_size = buffer_size;

  ret->buffers = fixed_queue_new(max_buffer_count);
  if (!ret->buffers) {
    LOG_ERROR(LOG_TAG, "%s unable to create buffers queue.", __func__);
    goto error;
  }

  ret->inbound_read_thread = thread_new(thread_name);
  if (!ret->inbound_read_thread) {
    LOG_ERROR(LOG_TAG, "%s unable to make reading thread.", __func__);
    goto error;
  }

  ret->inbound_read_object = reactor_register(
    thread_get_reactor(ret->inbound_read_thread),
    fd_to_read,
    ret,
    inbound_data_waiting,
    NULL
  );

  return ret;

error:;
  eager_reader_free(ret);
  return NULL;
}
Example #8
0
static void background_connection_add(bt_bdaddr_t *address) {
  assert(address);
  background_connections_lazy_init();
  background_connection_t *connection = hash_map_get(background_connections, address);
  if (!connection) {
    connection = osi_calloc(sizeof(background_connection_t));
    connection->address = *address;
    hash_map_set(background_connections, address, connection);
  }
}
Example #9
0
static entry_t *entry_new(const char *key, const char *value)
{
    entry_t *entry = osi_calloc(sizeof(entry_t));
    if (!entry) {
        return NULL;
    }

    entry->key = osi_strdup(key);
    entry->value = osi_strdup(value);
    return entry;
}
Example #10
0
static section_t *section_new(const char *name)
{
    section_t *section = osi_calloc(sizeof(section_t));
    if (!section) {
        return NULL;
    }

    section->name = osi_strdup(name);
    section->entries = list_new(entry_free);
    return section;
}
Example #11
0
File: list.c Project: tve/esp-idf
// Hidden constructor, only to be used by the hash map for the allocation tracker.
// Behaves the same as |list_new|, except you get to specify the allocator.
list_t *list_new_internal(list_free_cb callback)
{
    list_t *list = (list_t *) osi_calloc(sizeof(list_t));
    if (!list) {
        return NULL;
    }

    list->head = list->tail = NULL;
    list->length = 0;
    list->free_cb = callback;
    return list;
}
Example #12
0
socket_t *socket_new_from_fd(int fd) {
  assert(fd != INVALID_FD);

  socket_t *ret = (socket_t *)osi_calloc(sizeof(socket_t));
  if (!ret) {
    LOG_ERROR("%s unable to allocate memory for socket.", __func__);
    return NULL;
  }

  ret->fd = fd;
  return ret;
}
Example #13
0
void module_start_up_callbacked_wrapper(
    const module_t *module,
    thread_t *callback_thread,
    thread_fn callback) {
  callbacked_wrapper_t *wrapper = osi_calloc(sizeof(callbacked_wrapper_t));

  wrapper->module = module;
  wrapper->lifecycle_thread = thread_new("module_wrapper");
  wrapper->callback_thread = callback_thread;
  wrapper->callback = callback;

  // Run the actual module start up
  thread_post(wrapper->lifecycle_thread, run_wrapped_start_up, wrapper);
}
Example #14
0
buffer_t *buffer_new(size_t size) {
  assert(size > 0);

  buffer_t *buffer = osi_calloc(sizeof(buffer_t) + size);
  if (!buffer) {
    LOG_ERROR(LOG_TAG, "%s unable to allocate buffer of %zu bytes.", __func__, size);
    return NULL;
  }

  buffer->root = buffer;
  buffer->refcount = 1;
  buffer->length = size;

  return buffer;
}
Example #15
0
File: list.c Project: tve/esp-idf
bool list_insert_after(list_t *list, list_node_t *prev_node, void *data) {
    assert(list != NULL);
    assert(prev_node != NULL);
    assert(data != NULL);
    list_node_t *node = (list_node_t *)osi_calloc(sizeof(list_node_t));
    if (!node) {
        return false;
    }
    node->next = prev_node->next;
    node->data = data;
    prev_node->next = node;
    if (list->tail == prev_node) {
        list->tail = node;
    }
    ++list->length;
    return true;
}
Example #16
0
File: list.c Project: tve/esp-idf
bool list_prepend(list_t *list, void *data)
{
    assert(list != NULL);
    assert(data != NULL);
    list_node_t *node = (list_node_t *)osi_calloc(sizeof(list_node_t));
    if (!node) {
        return false;
    }
    node->next = list->head;
    node->data = data;
    list->head = node;
    if (list->tail == NULL) {
        list->tail = list->head;
    }
    ++list->length;
    return true;
}
Example #17
0
static future_t *transmit_command_futured(BT_HDR *command) {
  waiting_command_t *wait_entry = osi_calloc(sizeof(waiting_command_t));
  assert(wait_entry != NULL);

  future_t *future = future_new();

  uint8_t *stream = command->data + command->offset;
  STREAM_TO_UINT16(wait_entry->opcode, stream);
  wait_entry->complete_future = future;
  wait_entry->command = command;

  // Store the command message type in the event field
  // in case the upper layer didn't already
  command->event = MSG_STACK_TO_HC_HCI_CMD;

  fixed_queue_enqueue(command_queue, wait_entry);
  return future;
}
non_repeating_timer_t *non_repeating_timer_new(period_ms_t duration, alarm_callback_t cb, void *data) {
  assert(cb != NULL);

  non_repeating_timer_t *ret = osi_calloc(sizeof(non_repeating_timer_t));

  ret->alarm = alarm_new();
  if (!ret->alarm)
    goto error;

  ret->duration = duration;
  ret->callback = cb;
  ret->data = data;

  return ret;
error:;
  non_repeating_timer_free(ret);
  return NULL;
}
Example #19
0
socket_t *socket_accept(const socket_t *socket) {
  assert(socket != NULL);

  int fd = accept(socket->fd, NULL, NULL);
  if (fd == INVALID_FD) {
    LOG_ERROR("%s unable to accept socket: %s", __func__, strerror(errno));
    return NULL;
  }

  socket_t *ret = (socket_t *)osi_calloc(sizeof(socket_t));
  if (!ret) {
    close(fd);
    LOG_ERROR("%s unable to allocate memory for socket.", __func__);
    return NULL;
  }

  ret->fd = fd;
  return ret;
}
Example #20
0
static int get_config_size_from_flash(nvs_handle fp)
{
    assert(fp != 0);

    esp_err_t err;
    char *keyname = osi_calloc(sizeof(CONFIG_KEY) + 1);
    if (!keyname){
        LOG_ERROR("%s, malloc error\n", __func__);
        return 0;
    }
    size_t length = CONFIG_FILE_DEFAULE_LENGTH;
    size_t total_length = 0;
    uint16_t i = 0;
    snprintf(keyname, sizeof(CONFIG_KEY) + 1, "%s%d", CONFIG_KEY, 0);
    err = nvs_get_blob(fp, keyname, NULL, &length);
    if (err == ESP_ERR_NVS_NOT_FOUND) {
        osi_free(keyname);
        return 0;
    }
    if (err != ESP_OK) {
        LOG_ERROR("%s, error %d\n", __func__, err);
        osi_free(keyname);
        return 0;
    }
    total_length += length;
    while (length == CONFIG_FILE_MAX_SIZE) {
        length = CONFIG_FILE_DEFAULE_LENGTH;
        snprintf(keyname, sizeof(CONFIG_KEY) + 1, "%s%d", CONFIG_KEY, ++i);
        err = nvs_get_blob(fp, keyname, NULL, &length);

        if (err == ESP_ERR_NVS_NOT_FOUND) {
            break;
        }
        if (err != ESP_OK) {
            LOG_ERROR("%s, error %d\n", __func__, err);
            osi_free(keyname);
            return 0;
        }
        total_length += length;
    }
    osi_free(keyname);
    return total_length;
}
Example #21
0
buffer_t *buffer_new_slice(const buffer_t *buf, size_t slice_size) {
  assert(buf != NULL);
  assert(slice_size > 0);
  assert(slice_size <= buf->length);

  buffer_t *ret = osi_calloc(sizeof(buffer_t));
  if (!ret) {
    LOG_ERROR(LOG_TAG, "%s unable to allocate new buffer for slice of length %zu.", __func__, slice_size);
    return NULL;
  }

  ret->root = buf->root;
  ret->refcount = SIZE_MAX;
  ret->length = slice_size;

  ++buf->root->refcount;

  return ret;
}
Example #22
0
reactor_t *reactor_new(void) {
  reactor_t *ret = (reactor_t *)osi_calloc(sizeof(reactor_t));
  if (!ret)
    return NULL;

  ret->epoll_fd = INVALID_FD;
  ret->event_fd = INVALID_FD;

  ret->epoll_fd = epoll_create(MAX_EVENTS);
  if (ret->epoll_fd == INVALID_FD) {
    LOG_ERROR("%s unable to create epoll instance: %s", __func__, strerror(errno));
    goto error;
  }

  ret->event_fd = eventfd(0, 0);
  if (ret->event_fd == INVALID_FD) {
    LOG_ERROR("%s unable to create eventfd: %s", __func__, strerror(errno));
    goto error;
  }

  pthread_mutex_init(&ret->list_lock, NULL);
  ret->invalidation_list = list_new(NULL);
  if (!ret->invalidation_list) {
    LOG_ERROR("%s unable to allocate object invalidation list.", __func__);
    goto error;
  }

  struct epoll_event event;
  memset(&event, 0, sizeof(event));
  event.events = EPOLLIN;
  event.data.ptr = NULL;
  if (epoll_ctl(ret->epoll_fd, EPOLL_CTL_ADD, ret->event_fd, &event) == -1) {
    LOG_ERROR("%s unable to register eventfd with epoll set: %s", __func__, strerror(errno));
    goto error;
  }

  return ret;

error:;
  reactor_free(ret);
  return NULL;
}
Example #23
0
thread_t *thread_new_sized(const char *name, size_t work_queue_capacity) {
  assert(name != NULL);
  assert(work_queue_capacity != 0);

  thread_t *ret = osi_calloc(sizeof(thread_t));
  if (!ret)
    goto error;

  ret->reactor = reactor_new();
  if (!ret->reactor)
    goto error;

  ret->work_queue = fixed_queue_new(work_queue_capacity);
  if (!ret->work_queue)
    goto error;

  // Start is on the stack, but we use a semaphore, so it's safe
  struct start_arg start;
  start.start_sem = semaphore_new(0);
  if (!start.start_sem)
    goto error;

  strncpy(ret->name, name, THREAD_NAME_MAX);
  start.thread = ret;
  start.error = 0;
  pthread_create(&ret->pthread, NULL, run_thread, &start);
  semaphore_wait(start.start_sem);
  semaphore_free(start.start_sem);

  if (start.error)
    goto error;

  return ret;

error:;
  if (ret) {
    fixed_queue_free(ret->work_queue, osi_free);
    reactor_free(ret->reactor);
  }
  osi_free(ret);
  return NULL;
}
Example #24
0
config_t *config_new_empty(void)
{
    config_t *config = osi_calloc(sizeof(config_t));
    if (!config) {
        LOG_ERROR("%s unable to allocate memory for config_t.\n", __func__);
        goto error;
    }

    config->sections = list_new(section_free);
    if (!config->sections) {
        LOG_ERROR("%s unable to allocate list for sections.\n", __func__);
        goto error;
    }

    return config;

error:;
    config_free(config);
    return NULL;
}
Example #25
0
reactor_object_t *reactor_register(reactor_t *reactor,
    int fd, void *context,
    void (*read_ready)(void *context),
    void (*write_ready)(void *context)) {
  assert(reactor != NULL);
  assert(fd != INVALID_FD);

  reactor_object_t *object = (reactor_object_t *)osi_calloc(sizeof(reactor_object_t));
  if (!object) {
    LOG_ERROR("%s unable to allocate reactor object: %s", __func__, strerror(errno));
    return NULL;
  }

  object->reactor = reactor;
  object->fd = fd;
  object->context = context;
  object->read_ready = read_ready;
  object->write_ready = write_ready;
  pthread_mutex_init(&object->lock, NULL);

  struct epoll_event event;
  memset(&event, 0, sizeof(event));
  if (read_ready)
    event.events |= (EPOLLIN | EPOLLRDHUP);
  if (write_ready)
    event.events |= EPOLLOUT;
  event.data.ptr = object;

  if (epoll_ctl(reactor->epoll_fd, EPOLL_CTL_ADD, fd, &event) == -1) {
    LOG_ERROR("%s unable to register fd %d to epoll set: %s", __func__, fd, strerror(errno));
    pthread_mutex_destroy(&object->lock);
    osi_free(object);
    return NULL;
  }

  return object;
}
Example #26
0
static void transmit_command(
    BT_HDR *command,
    command_complete_cb complete_callback,
    command_status_cb status_callback,
    void *context) {
  waiting_command_t *wait_entry = osi_calloc(sizeof(waiting_command_t));
  if (!wait_entry) {
    LOG_ERROR(LOG_TAG, "%s couldn't allocate space for wait entry.", __func__);
    return;
  }

  uint8_t *stream = command->data + command->offset;
  STREAM_TO_UINT16(wait_entry->opcode, stream);
  wait_entry->complete_callback = complete_callback;
  wait_entry->status_callback = status_callback;
  wait_entry->command = command;
  wait_entry->context = context;

  // Store the command message type in the event field
  // in case the upper layer didn't already
  command->event = MSG_STACK_TO_HC_HCI_CMD;

  fixed_queue_enqueue(command_queue, wait_entry);
}
Example #27
0
alarm_t *alarm_new(void) {
  // Make sure we have a list we can insert alarms into.
  if (!alarms && !lazy_initialize())
    return NULL;

  pthread_mutexattr_t attr;
  pthread_mutexattr_init(&attr);

  alarm_t *ret = osi_calloc(sizeof(alarm_t));
  if (!ret) {
    LOG_ERROR("%s unable to allocate memory for alarm.", __func__);
    goto error;
  }

  // Make this a recursive mutex to make it safe to call |alarm_cancel| from
  // within the callback function of the alarm.
  int error = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  if (error) {
    LOG_ERROR("%s unable to create a recursive mutex: %s", __func__, strerror(error));
    goto error;
  }

  error = pthread_mutex_init(&ret->callback_lock, &attr);
  if (error) {
    LOG_ERROR("%s unable to initialize mutex: %s", __func__, strerror(error));
    goto error;
  }

  pthread_mutexattr_destroy(&attr);
  return ret;

error:;
  pthread_mutexattr_destroy(&attr);
  osi_free(ret);
  return NULL;
}
Example #28
0
static void config_parse(nvs_handle fp, config_t *config)
{
    assert(fp != 0);
    assert(config != NULL);

    esp_err_t err;
    int line_num = 0;
    int err_code = 0;
    uint16_t i = 0;
    size_t length = CONFIG_FILE_DEFAULE_LENGTH;
    size_t total_length = 0;
    char *line = osi_calloc(1024);
    char *section = osi_calloc(1024);
    char *keyname = osi_calloc(sizeof(CONFIG_KEY) + 1);
    int buf_size = get_config_size_from_flash(fp);
    char *buf = osi_calloc(buf_size + 100);
    if (!line || !section || !buf || !keyname) {
        err_code |= 0x01;
        goto error;
    }
    snprintf(keyname, sizeof(CONFIG_KEY)+1, "%s%d", CONFIG_KEY, 0);
    err = nvs_get_blob(fp, keyname, buf, &length);
    if (err == ESP_ERR_NVS_NOT_FOUND) {
        goto error;
    }
    if (err != ESP_OK) {
        err_code |= 0x02;
        goto error;
    }
    total_length += length;
    while (length == CONFIG_FILE_MAX_SIZE) {
        length = CONFIG_FILE_DEFAULE_LENGTH;
        snprintf(keyname, sizeof(CONFIG_KEY) + 1, "%s%d", CONFIG_KEY, ++i);
        err = nvs_get_blob(fp, keyname, buf + CONFIG_FILE_MAX_SIZE * i, &length);

        if (err == ESP_ERR_NVS_NOT_FOUND) {
            break;
        }
        if (err != ESP_OK) {
            err_code |= 0x02;
            goto error;
        }
        total_length += length;
    }
    char *p_line_end;
    char *p_line_bgn = buf;
    strcpy(section, CONFIG_DEFAULT_SECTION);

    while ( (p_line_bgn < buf + total_length - 1) && (p_line_end = strchr(p_line_bgn, '\n'))) {

        // get one line
        int line_len = p_line_end - p_line_bgn;
        if (line_len > 1023) {
            LOG_WARN("%s exceed max line length on line %d.\n", __func__, line_num);
            break;
        }
        memcpy(line, p_line_bgn, line_len);
        line[line_len] = '\0';
        p_line_bgn = p_line_end + 1;
        char *line_ptr = trim(line);
        ++line_num;

        // Skip blank and comment lines.
        if (*line_ptr == '\0' || *line_ptr == '#') {
            continue;
        }

        if (*line_ptr == '[') {
            size_t len = strlen(line_ptr);
            if (line_ptr[len - 1] != ']') {
                LOG_WARN("%s unterminated section name on line %d.\n", __func__, line_num);
                continue;
            }
            strncpy(section, line_ptr + 1, len - 2);
            section[len - 2] = '\0';
        } else {
            char *split = strchr(line_ptr, '=');
            if (!split) {
                LOG_DEBUG("%s no key/value separator found on line %d.\n", __func__, line_num);
                continue;
            }
            *split = '\0';
            config_set_string(config, section, trim(line_ptr), trim(split + 1), true);
        }
    }

error:
    if (buf) {
        osi_free(buf);
    }
    if (line) {
        osi_free(line);
    }
    if (section) {
        osi_free(section);
    }
    if (keyname) {
        osi_free(keyname);
    }
    if (err_code) {
        LOG_ERROR("%s returned with err code: %d\n", __func__, err_code);
    }
}
Example #29
0
static sco_socket_t *sco_socket_new(void) {
  sco_socket_t *sco_socket = (sco_socket_t *)osi_calloc(sizeof(sco_socket_t));
  if (sco_socket)
    sco_socket->sco_handle = BTM_INVALID_SCO_INDEX;
  return sco_socket;
}
Example #30
0
bool config_save(const config_t *config, const char *filename)
{
    assert(config != NULL);
    assert(filename != NULL);
    assert(*filename != '\0');

    esp_err_t err;
    int err_code = 0;
    nvs_handle fp;
    char *line = osi_calloc(1024);
    char *keyname = osi_calloc(sizeof(CONFIG_KEY) + 1);
    int config_size = get_config_size(config);
    char *buf = osi_calloc(config_size + 100);
    if (!line || !buf || !keyname) {
        err_code |= 0x01;
        goto error;
    }

    err = nvs_open(filename, NVS_READWRITE, &fp);
    if (err != ESP_OK) {
        if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
            LOG_ERROR("%s: NVS not initialized. "
                      "Call nvs_flash_init before initializing bluetooth.", __func__);
        }
        err_code |= 0x02;
        goto error;
    }

    int w_cnt, w_cnt_total = 0;
    for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
        const section_t *section = (const section_t *)list_node(node);
        w_cnt = snprintf(line, 1024, "[%s]\n", section->name);
        LOG_DEBUG("section name: %s, w_cnt + w_cnt_total = %d\n", section->name, w_cnt + w_cnt_total);
        memcpy(buf + w_cnt_total, line, w_cnt);
        w_cnt_total += w_cnt;

        for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) {
            const entry_t *entry = (const entry_t *)list_node(enode);
            LOG_DEBUG("(key, val): (%s, %s)\n", entry->key, entry->value);
            w_cnt = snprintf(line, 1024, "%s = %s\n", entry->key, entry->value);
            LOG_DEBUG("%s, w_cnt + w_cnt_total = %d", __func__, w_cnt + w_cnt_total);
            memcpy(buf + w_cnt_total, line, w_cnt);
            w_cnt_total += w_cnt;
        }

        // Only add a separating newline if there are more sections.
        if (list_next(node) != list_end(config->sections)) {
            buf[w_cnt_total] = '\n';
            w_cnt_total += 1;
        } else {
            break;
        }
    }
    buf[w_cnt_total] = '\0';
    if (w_cnt_total < CONFIG_FILE_MAX_SIZE) {
        snprintf(keyname, sizeof(CONFIG_KEY)+1, "%s%d", CONFIG_KEY, 0);
        err = nvs_set_blob(fp, keyname, buf, w_cnt_total);
        if (err != ESP_OK) {
            nvs_close(fp);
            err_code |= 0x04;
            goto error;
        }
    }else {
        uint count = (w_cnt_total / CONFIG_FILE_MAX_SIZE);
        for (int i = 0; i <= count; i++)
        {
            snprintf(keyname, sizeof(CONFIG_KEY)+1, "%s%d", CONFIG_KEY, i);
            if (i == count) {
                err = nvs_set_blob(fp, keyname, buf + i*CONFIG_FILE_MAX_SIZE, w_cnt_total - i*CONFIG_FILE_MAX_SIZE);
                LOG_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, w_cnt_total - i*CONFIG_FILE_MAX_SIZE);
            }else {
                err = nvs_set_blob(fp, keyname, buf + i*CONFIG_FILE_MAX_SIZE, CONFIG_FILE_MAX_SIZE);
                LOG_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, CONFIG_FILE_MAX_SIZE);
            }
            if (err != ESP_OK) {
                nvs_close(fp);
                err_code |= 0x04;
                goto error;
            }
        }
    }

    err = nvs_commit(fp);
    if (err != ESP_OK) {
        nvs_close(fp);
        err_code |= 0x08;
        goto error;
    }

    nvs_close(fp);
    osi_free(line);
    osi_free(buf);
    osi_free(keyname);
    return true;

error:
    if (buf) {
        osi_free(buf);
    }
    if (line) {
        osi_free(line);
    }
    if (keyname) {
        osi_free(keyname);
    }
    if (err_code) {
        LOG_ERROR("%s, err_code: 0x%x\n", __func__, err_code);
    }
    return false;
}