diff --git a/platform/windows/btstack_tlv_windows.c b/platform/windows/btstack_tlv_windows.c new file mode 100644 index 000000000..0fa2adeb6 --- /dev/null +++ b/platform/windows/btstack_tlv_windows.c @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2017 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define BTSTACK_FILE__ "btstack_tlv_windows.c" + +#include "btstack_tlv.h" +#include "btstack_tlv_windows.h" +#include "btstack_debug.h" +#include "btstack_util.h" +#include "btstack_debug.h" +#include "string.h" + +#include +#include + + +// Header: +// - Magic: 'BTstack' +// - Status: +// - bits 765432: reserved +// - bits 10: epoch + +// Entries +// - Tag: 32 bit +// - Len: 32 bit +// - Value: Len in bytes + +#define BTSTACK_TLV_HEADER_LEN 8 + +#define MAX_TLV_VALUE_SIZE 2048 + +static const char btstack_tlv_header_magic[] = "BTstack"; + +#define DUMMY_SIZE 4 +typedef struct tlv_entry { + void * next; + uint32_t tag; + uint32_t len; + uint8_t value[DUMMY_SIZE]; // dummy size +} tlv_entry_t; + +static int btstack_tlv_windows_append_tag(btstack_tlv_windows_t * self, uint32_t tag, const uint8_t * data, uint32_t data_size){ + + if (self->file == INVALID_HANDLE_VALUE) return 1; + + log_info("append tag %04x, len %u", tag, data_size); + + uint8_t header[8]; + big_endian_store_32(header, 0, tag); + big_endian_store_32(header, 4, data_size); + DWORD written_header = 0; + (void)WriteFile( + self->file, // file handle + &header, // start of data to write + sizeof(header), // number of bytes to write + &written_header, // number of bytes that were written + NULL); // no overlapped structure + + if (written_header != sizeof(header)) return 1; + if (data_size > 0) { + DWORD written_value = 0; + (void)WriteFile( + self->file, // file handle + &header, // start of data to write + sizeof(header), // number of bytes to write + &written_value, // number of bytes that were written + NULL); // no overlapped structure + if (written_value != data_size) return 1; + } + return 1; +} + +static tlv_entry_t * btstack_tlv_windows_find_entry(btstack_tlv_windows_t * self, uint32_t tag){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &self->entry_list); + while (btstack_linked_list_iterator_has_next(&it)){ + tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); + if (entry->tag != tag) continue; + return entry; + } + return NULL; +} + +/** + * Delete Tag + * @param tag + */ +static void btstack_tlv_windows_delete_tag(void * context, uint32_t tag){ + btstack_tlv_windows_t * self = (btstack_tlv_windows_t *) context; + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &self->entry_list); + while (btstack_linked_list_iterator_has_next(&it)){ + tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); + if (entry->tag != tag) continue; + btstack_linked_list_iterator_remove(&it); + free(entry); + btstack_tlv_windows_append_tag(self, tag, NULL, 0); + return; + } +} + +/** + * Get Value for Tag + * @param tag + * @param buffer + * @param buffer_size + * @returns size of value + */ +static int btstack_tlv_windows_get_tag(void * context, uint32_t tag, uint8_t * buffer, uint32_t buffer_size){ + btstack_tlv_windows_t * self = (btstack_tlv_windows_t *) context; + tlv_entry_t * entry = btstack_tlv_windows_find_entry(self, tag); + // not found + if (!entry) return 0; + // return len if buffer = NULL + if (!buffer) return entry->len; + // otherwise copy data into buffer + uint16_t bytes_to_copy = btstack_min(buffer_size, entry->len); + memcpy(buffer, &entry->value[0], bytes_to_copy); + return bytes_to_copy; +} + +/** + * Store Tag + * @param tag + * @param data + * @param data_size + */ +static int btstack_tlv_windows_store_tag(void * context, uint32_t tag, const uint8_t * data, uint32_t data_size){ + btstack_tlv_windows_t * self = (btstack_tlv_windows_t *) context; + + // enforce arbitrary max value size + btstack_assert(data_size <= MAX_TLV_VALUE_SIZE); + + // remove old entry + tlv_entry_t * old_entry = btstack_tlv_windows_find_entry(self, tag); + if (old_entry){ + btstack_linked_list_remove(&self->entry_list, (btstack_linked_item_t *) old_entry); + free(old_entry); + } + + // create new entry + uint32_t entry_size = sizeof(tlv_entry_t) - DUMMY_SIZE + data_size; + tlv_entry_t * new_entry = (tlv_entry_t *) malloc(entry_size); + if (!new_entry) return 0; + memset(new_entry, 0, entry_size); + new_entry->tag = tag; + new_entry->len = data_size; + memcpy(&new_entry->value[0], data, data_size); + + // append new entry + btstack_linked_list_add(&self->entry_list, (btstack_linked_item_t *) new_entry); + + // write new tag + btstack_tlv_windows_append_tag(self, tag, data, data_size); + + return 0; +} + +// returns 0 on success +static int btstack_tlv_windows_read_db(btstack_tlv_windows_t * self){ + // open file + log_info("open db %s", self->db_path); + self->file = CreateFile( + self->db_path, + GENERIC_READ | GENERIC_WRITE, + 0, // do not share + NULL, // default security + OPEN_EXISTING, // only open if it exists + FILE_ATTRIBUTE_NORMAL, + NULL); + + uint8_t header[BTSTACK_TLV_HEADER_LEN]; + if (self->file != INVALID_HANDLE_VALUE){ + // check header + bool ok = ReadFile(self->file, header, BTSTACK_TLV_HEADER_LEN, NULL, NULL); + int file_valid = 0; + if (ok){ + if (memcmp(header, btstack_tlv_header_magic, strlen(btstack_tlv_header_magic)) == 0){ + log_info("BTstack Magic Header found"); + // read entries + while (true){ + uint8_t entry[8]; + DWORD bytes_read; + ok = ReadFile(self->file, entry, sizeof(entry), &bytes_read, NULL); + if ( ok && (bytes_read == 0)) { + // EOF, we're good + file_valid = 1; + break; + } + + if ( (ok == false) || (bytes_read != sizeof(entry))) { + break; + } + + uint32_t tag = big_endian_read_32(entry, 0); + uint32_t len = big_endian_read_32(entry, 4); + + // arbitrary safety check: values <= MAX_TLV_VALUE_SIZE + if (len > MAX_TLV_VALUE_SIZE) { + break; + } + + // create new entry for regular tag + tlv_entry_t * new_entry = NULL; + if (len > 0) { + new_entry = (tlv_entry_t *) malloc(sizeof(tlv_entry_t) - DUMMY_SIZE + len); + if (!new_entry) return 0; + new_entry->tag = tag; + new_entry->len = len; + + // read + DWORD value_read; + ok = ReadFile(self->file, &new_entry->value[0],len, &value_read, NULL); + if ((ok == false) || (value_read != len)) { + break; + } + } + + // remove old entry + tlv_entry_t * old_entry = btstack_tlv_windows_find_entry(self, tag); + if (old_entry){ + btstack_linked_list_remove(&self->entry_list, (btstack_linked_item_t *) old_entry); + free(old_entry); + } + + // append new entry + if (new_entry){ + btstack_linked_list_add(&self->entry_list, (btstack_linked_item_t *) new_entry); + } + } + } + } + if (!file_valid) { + log_info("file invalid, re-create"); + CloseHandle(self->file); + self->file = INVALID_HANDLE_VALUE; + } + } + if (self->file == INVALID_HANDLE_VALUE){ + // create new file + self->file = CreateFile( + self->db_path, + GENERIC_WRITE, + 0, // do not share + NULL, // default security + CREATE_ALWAYS, // create new file / replace old one + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (self->file == INVALID_HANDLE_VALUE) { + log_error("failed to create file"); + return -1; + } + memset(header, 0, sizeof(header)); + memcpy(header, btstack_tlv_header_magic, sizeof(btstack_tlv_header_magic)); + + DWORD dwBytesWritten = 0; + (void)WriteFile( + self->file, // file handle + &header, // start of data to write + sizeof(header), // number of bytes to write + &dwBytesWritten, // number of bytes that were written + NULL); // no overlapped structure + UNUSED(dwBytesWritten); + + // write out all valid entries (if any) + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &self->entry_list); + while (btstack_linked_list_iterator_has_next(&it)){ + tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); + btstack_tlv_windows_append_tag(self, entry->tag, &entry->value[0], entry->len); + } + } + return 0; +} + +static const btstack_tlv_t btstack_tlv_windows = { + /* int (*get_tag)(..); */ &btstack_tlv_windows_get_tag, + /* int (*store_tag)(..); */ &btstack_tlv_windows_store_tag, + /* void (*delete_tag)(v..); */ &btstack_tlv_windows_delete_tag, +}; + +/** + * Init Tag Length Value Store + */ +const btstack_tlv_t * btstack_tlv_windows_init_instance(btstack_tlv_windows_t * self, const char * db_path){ + memset(self, 0, sizeof(btstack_tlv_windows_t)); + self->db_path = db_path; + self->file = INVALID_HANDLE_VALUE; + + // read DB + btstack_tlv_windows_read_db(self); + return &btstack_tlv_windows; +} + +/** + * Free TLV entries + * @param self + */ +void btstack_tlv_windows_deinit(btstack_tlv_windows_t * self){ + // free all entries + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &self->entry_list); + while (btstack_linked_list_iterator_has_next(&it)){ + tlv_entry_t * entry = (tlv_entry_t*) btstack_linked_list_iterator_next(&it); + btstack_linked_list_iterator_remove(&it); + free(entry); + } +} diff --git a/platform/windows/btstack_tlv_windows.h b/platform/windows/btstack_tlv_windows.h new file mode 100644 index 000000000..7f605fb6e --- /dev/null +++ b/platform/windows/btstack_tlv_windows.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * btstack_tlv_posix.h + * + * Implementation for BTstack's Tag Value Length Persistent Storage implementations + * using in-memory storage (RAM & malloc) and append-only log files on disc + */ + +#ifndef BTSTACK_TLV_WINDOWS_H +#define BTSTACK_TLV_WINDOWS_H + +#include + +#include "btstack_tlv.h" +#include "btstack_linked_list.h" + +#if defined __cplusplus +extern "C" { +#endif + +typedef struct { + btstack_linked_list_t entry_list; + const char * db_path; + HANDLE file; +} btstack_tlv_windows_t; + +/** + * Init Tag Length Value Store + * @param context btstack_tlv_windows_t + * @param db_path on disc + */ +const btstack_tlv_t * btstack_tlv_windows_init_instance(btstack_tlv_windows_t * context, const char * db_path); + +/** + * Free TLV entries + * @param self + */ +void btstack_tlv_windows_deinit(btstack_tlv_windows_t * self); + +#if defined __cplusplus +} +#endif +#endif // BTSTACK_TLV_WINDOWS_H