diff --git a/hal/inc/ble_hal.h b/hal/inc/ble_hal.h index 08a4d5d57f..2643f23146 100644 --- a/hal/inc/ble_hal.h +++ b/hal/inc/ble_hal.h @@ -29,6 +29,20 @@ // API version #define BLE_API_VERSION 1 +// Default maximum size of an ATT packet in bytes (ATT_MTU) +#define BLE_MIN_ATT_MTU_SIZE 23 + +// Size of the ATT opcode field in bytes +#define BLE_ATT_OPCODE_SIZE 1 + +// Size of the ATT handle field in bytes +#define BLE_ATT_HANDLE_SIZE 2 + +// Minimum and maximum number of bytes that can be sent in a single write command, read response, +// notification or indication packet +#define BLE_MIN_ATTR_VALUE_PACKET_SIZE (BLE_MIN_ATT_MTU_SIZE - BLE_ATT_OPCODE_SIZE - BLE_ATT_HANDLE_SIZE) +#define BLE_MAX_ATTR_VALUE_PACKET_SIZE (BLE_MAX_ATT_MTU_SIZE - BLE_ATT_OPCODE_SIZE - BLE_ATT_HANDLE_SIZE) + #ifdef __cplusplus extern "C" { #endif @@ -101,6 +115,13 @@ typedef struct ble_service { uint16_t char_count; } ble_service; +// Manufacturer-specific data +typedef struct ble_manuf_data { + uint16_t company_id; + uint16_t size; + const char* data; +} ble_manuf_data; + // BLE_EVENT_CONNECTED event data typedef struct ble_connected_event_data { uint16_t conn_handle; @@ -146,6 +167,7 @@ typedef struct ble_profile { uint16_t service_count; ble_service* services; const char* device_name; + const ble_manuf_data* manuf_data; ble_event_callback callback; void* user_data; } ble_profile; @@ -153,7 +175,7 @@ typedef struct ble_profile { // Connection parameters typedef struct ble_conn_param { uint16_t version; // API version - uint16_t max_char_value_size; + uint16_t att_mtu_size; // Maximum size of an ATT packet (ATT_MTU) } ble_conn_param; // Characteristic parameters diff --git a/hal/inc/deviceid_hal.h b/hal/inc/deviceid_hal.h index b6af71e113..2a2d325fe9 100644 --- a/hal/inc/deviceid_hal.h +++ b/hal/inc/deviceid_hal.h @@ -35,6 +35,9 @@ // Size of the device's serial number #define HAL_DEVICE_SERIAL_NUMBER_SIZE 15 +// Size of the device secret data +#define HAL_DEVICE_SECRET_SIZE 32 + #ifdef __cplusplus extern "C" { #endif @@ -74,6 +77,12 @@ void HAL_save_device_id(uint32_t offset); */ int hal_get_device_serial_number(char* str, size_t size, void* reserved); +/** + * Get the device secret data. + */ +// TODO: Move this function to an appropriate module +int hal_get_device_secret(char* data, size_t size, void* reserved); + #ifdef __cplusplus } #endif diff --git a/hal/src/nRF52840/ble_hal_impl.h b/hal/src/nRF52840/ble_hal_impl.h index be1cd32bc7..d61e14ac79 100644 --- a/hal/src/nRF52840/ble_hal_impl.h +++ b/hal/src/nRF52840/ble_hal_impl.h @@ -26,11 +26,6 @@ #error "NRF_SDH_BLE_GATT_MAX_MTU_SIZE is not defined" #endif -// TODO: Move to an appropriate platform header -#ifndef BLE_ENABLED -#define BLE_ENABLED 1 -#endif - // Invalid connection handle // TODO: Prefix all BLE HAL definitions with "hal_" #define BLE_INVALID_CONN_HANDLE BLE_CONN_HANDLE_INVALID @@ -47,11 +42,5 @@ // Maximum number of characteristics per service #define BLE_MAX_CHAR_COUNT 4 -// Size of the ATT opcode field in bytes -#define BLE_ATT_OPCODE_SIZE 1 - -// Size of the ATT handle field in bytes -#define BLE_ATT_HANDLE_SIZE 2 - -// Maximum size of an attribute's value in bytes -#define BLE_MAX_ATTR_VALUE_SIZE (NRF_SDH_BLE_GATT_MAX_MTU_SIZE - BLE_ATT_OPCODE_SIZE - BLE_ATT_HANDLE_SIZE) +// Maximum supported size of an ATT packet in bytes (ATT_MTU) +#define BLE_MAX_ATT_MTU_SIZE NRF_SDH_BLE_GATT_MAX_MTU_SIZE diff --git a/hal/src/nRF52840/deviceid_hal.cpp b/hal/src/nRF52840/deviceid_hal.cpp index 43f0945f7f..42e45b48f6 100644 --- a/hal/src/nRF52840/deviceid_hal.cpp +++ b/hal/src/nRF52840/deviceid_hal.cpp @@ -18,6 +18,9 @@ #include "deviceid_hal.h" #include "exflash_hal.h" #include "str_util.h" +#include "random.h" +#include "dct.h" + #include "nrf52840.h" #include @@ -28,6 +31,9 @@ using namespace particle; const uint32_t DEVICE_ID_PREFIX = 0x68ce0fe0; +const uintptr_t SERIAL_NUMBER_OTP_ADDRESS = 0x00000000; +const uintptr_t DEVICE_SECRET_OTP_ADDRESS = 0x00000010; + } // namespace unsigned HAL_device_ID(uint8_t* dest, unsigned destLen) @@ -54,7 +60,7 @@ int hal_get_device_serial_number(char* str, size_t size, void* reserved) { char serial[HAL_DEVICE_SERIAL_NUMBER_SIZE] = {}; - int r = hal_exflash_read_special(HAL_EXFLASH_SPECIAL_SECTOR_OTP, 0, + int r = hal_exflash_read_special(HAL_EXFLASH_SPECIAL_SECTOR_OTP, SERIAL_NUMBER_OTP_ADDRESS, (uint8_t*)serial, HAL_DEVICE_SERIAL_NUMBER_SIZE); if (r != 0 || !isPrintable(serial, sizeof(serial))) { @@ -69,3 +75,32 @@ int hal_get_device_serial_number(char* str, size_t size, void* reserved) } return HAL_DEVICE_SERIAL_NUMBER_SIZE; } + +int hal_get_device_secret(char* data, size_t size, void* reserved) +{ + // Check if the device secret data is initialized in the DCT + char secret[HAL_DEVICE_SECRET_SIZE] = {}; + static_assert(sizeof(secret) == DCT_DEVICE_SECRET_SIZE, ""); + int ret = dct_read_app_data_copy(DCT_DEVICE_SECRET_OFFSET, secret, sizeof(secret)); + if (ret < 0) { + return ret; + } + if (!isPrintable(secret, sizeof(secret))) { + // Check the OTP memory + ret = hal_exflash_read_special(HAL_EXFLASH_SPECIAL_SECTOR_OTP, DEVICE_SECRET_OTP_ADDRESS, (uint8_t*)secret, sizeof(secret)); + if (ret < 0) { + return ret; + } + if (!isPrintable(secret, sizeof(secret))) { + // Generate random data + Random().genBase32(secret, sizeof(secret)); + } + // Update the DCT + ret = dct_write_app_data(secret, DCT_DEVICE_SECRET_OFFSET, sizeof(secret)); + if (ret < 0) { + return ret; + } + } + memcpy(data, secret, std::min(size, sizeof(secret))); + return HAL_DEVICE_SECRET_SIZE; +} diff --git a/hal/src/xenon/ble_hal.cpp b/hal/src/xenon/ble_hal.cpp index 16e9244201..1335b0245e 100644 --- a/hal/src/xenon/ble_hal.cpp +++ b/hal/src/xenon/ble_hal.cpp @@ -271,6 +271,8 @@ void processGattEvent(nrf_ble_gatt_t* gatt, const nrf_ble_gatt_evt_t* event) { int halError(uint32_t error) { switch (error) { + case NRF_ERROR_INVALID_STATE: + return BLE_ERROR_INVALID_STATE; case NRF_ERROR_NO_MEM: return BLE_ERROR_NO_MEMORY; default: @@ -278,7 +280,7 @@ int halError(uint32_t error) { } } -int initAdvert() { +int initAdvert(const ble_manuf_data* halManufData) { // Service UUIDs ble_uuid_t svcUuids[BLE_MAX_SERVICE_COUNT] = {}; for (size_t i = 0; i < g_profile.serviceCount; ++i) { @@ -286,6 +288,13 @@ int initAdvert() { } // Initialize the advertising module ble_advertising_init_t init = {}; + ble_advdata_manuf_data_t manufData = {}; + if (halManufData) { + manufData.company_identifier = halManufData->company_id; + manufData.data.p_data = (uint8_t*)halManufData->data; + manufData.data.size = halManufData->size; + init.advdata.p_manuf_specific_data = &manufData; + } init.advdata.name_type = BLE_ADVDATA_FULL_NAME; init.advdata.include_appearance = false; init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; @@ -340,7 +349,7 @@ int initTxChar(uint16_t serviceHandle, ble_char* halChar, Char* chr) { ble_gatts_attr_t attr = {}; attr.p_uuid = &chr->uuid; attr.p_attr_md = &attrMd; - attr.max_len = BLE_MAX_ATTR_VALUE_SIZE; + attr.max_len = BLE_MAX_ATTR_VALUE_PACKET_SIZE; const uint32_t ret = sd_ble_gatts_characteristic_add(serviceHandle, &charMd, &attr, &chr->handles); if (ret != NRF_SUCCESS) { LOG(ERROR, "sd_ble_gatts_characteristic_add() failed: %u", (unsigned)ret); @@ -371,7 +380,7 @@ int initRxChar(uint16_t serviceHandle, ble_char* halChar, Char* chr) { ble_gatts_attr_t attr = {}; attr.p_uuid = &chr->uuid; attr.p_attr_md = &attrMd; - attr.max_len = BLE_MAX_ATTR_VALUE_SIZE; + attr.max_len = BLE_MAX_ATTR_VALUE_PACKET_SIZE; const uint32_t ret = sd_ble_gatts_characteristic_add(serviceHandle, &charMd, &attr, &chr->handles); if (ret != NRF_SUCCESS) { LOG(ERROR, "sd_ble_gatts_characteristic_add() failed: %u", (unsigned)ret); @@ -581,7 +590,7 @@ int ble_init_profile(ble_profile* profile, void* reserved) { LOG(ERROR, "Unable to initialize profile"); return ret; } - ret = initAdvert(); + ret = initAdvert(profile->manuf_data); if (ret != 0) { LOG(ERROR, "Unable to initialize advertising module"); return ret; @@ -673,11 +682,11 @@ int ble_get_conn_param(uint16_t conn_handle, ble_conn_param* param, void* reserv return BLE_ERROR_INVALID_PARAM; } const uint16_t mtu = nrf_ble_gatt_eff_mtu_get(&g_gatt, conn_handle); - if (mtu == 0 || mtu <= BLE_ATT_OPCODE_SIZE + BLE_ATT_HANDLE_SIZE) { + if (mtu < BLE_MIN_ATT_MTU_SIZE) { LOG(ERROR, "nrf_ble_gatt_eff_mtu_get() failed"); return BLE_ERROR_UNKNOWN; } - param->max_char_value_size = mtu - BLE_ATT_OPCODE_SIZE - BLE_ATT_HANDLE_SIZE; + param->att_mtu_size = mtu; return 0; } diff --git a/hal/src/xenon/hal_platform_config.h b/hal/src/xenon/hal_platform_config.h index fbe35e71d9..ab6d33592a 100644 --- a/hal/src/xenon/hal_platform_config.h +++ b/hal/src/xenon/hal_platform_config.h @@ -38,7 +38,7 @@ #define HAL_IPv6 (1) -#define HAL_PLATFORM_BLE (1) +#define HAL_PLATFORM_BLE (1) /* XXX: */ #define HAL_PLATFORM_DEFAULT_CLOUD_KEEPALIVE_INTERVAL (20000) diff --git a/platform/MCU/nRF52840/inc/dct.h b/platform/MCU/nRF52840/inc/dct.h index 1bd08d8788..55ba6f7dfe 100644 --- a/platform/MCU/nRF52840/inc/dct.h +++ b/platform/MCU/nRF52840/inc/dct.h @@ -103,7 +103,8 @@ typedef struct __attribute__((packed)) application_dct { led_config_t led_mirror[4]; // LED mirroring configuration, to be used by bootloader uint8_t led_theme[64]; // LED signaling theme eap_config_t eap_config; // WLAN EAP settings - uint8_t reserved2[272]; + uint8_t device_secret[32]; // Device secret data (aka "mobile secret") + uint8_t reserved2[240]; // safe to add more data here or use up some of the reserved space to keep the end where it is uint8_t end[0]; } application_dct_t; @@ -135,6 +136,7 @@ typedef struct __attribute__((packed)) application_dct { #define DCT_LED_MIRROR_OFFSET (offsetof(application_dct_t, led_mirror)) #define DCT_LED_THEME_OFFSET (offsetof(application_dct_t, led_theme)) #define DCT_EAP_CONFIG_OFFSET (offsetof(application_dct_t, eap_config)) +#define DCT_DEVICE_SECRET_OFFSET (offsetof(application_dct_t, device_secret)) #define DCT_SYSTEM_FLAGS_SIZE (sizeof(application_dct_t::system_flags)) #define DCT_DEVICE_PRIVATE_KEY_SIZE (sizeof(application_dct_t::device_private_key)) @@ -162,6 +164,7 @@ typedef struct __attribute__((packed)) application_dct { #define DCT_LED_MIRROR_SIZE (sizeof(application_dct_t::led_mirror)) #define DCT_LED_THEME_SIZE (sizeof(application_dct_t::led_theme)) #define DCT_EAP_CONFIG_SIZE (sizeof(application_dct_t::eap_config)) +#define DCT_DEVICE_SECRET_SIZE (sizeof(application_dct_t::device_secret)) #define STATIC_ASSERT_DCT_OFFSET(field, expected) PARTICLE_STATIC_ASSERT( dct_##field, offsetof(application_dct_t, field)==expected) #define STATIC_ASSERT_FLAGS_OFFSET(field, expected) PARTICLE_STATIC_ASSERT( dct_sysflag_##field, offsetof(platform_system_flags_t, field)==expected) @@ -199,8 +202,9 @@ STATIC_ASSERT_DCT_OFFSET(mode_button_mirror, 3631 /* 3630 + 1 */); STATIC_ASSERT_DCT_OFFSET(led_mirror, 3663 /* 3631 + 32 */); STATIC_ASSERT_DCT_OFFSET(led_theme, 3759 /* 3663 + 24 * 4 */); STATIC_ASSERT_DCT_OFFSET(eap_config, 3823 /* 3759 + 64 */); -STATIC_ASSERT_DCT_OFFSET(reserved2, 8119 /* 3823 + (196 + 4*1024 + 4) */); -STATIC_ASSERT_DCT_OFFSET(end, 8391 /* 8119 + 272 */); +STATIC_ASSERT_DCT_OFFSET(device_secret, 8119 /* 3823 + (196 + 4*1024 + 4) */); +STATIC_ASSERT_DCT_OFFSET(reserved2, 8151 /* 8119 + 32 */); +STATIC_ASSERT_DCT_OFFSET(end, 8391 /* 8151 + 240 */); STATIC_ASSERT_FLAGS_OFFSET(Bootloader_Version_SysFlag, 4); STATIC_ASSERT_FLAGS_OFFSET(NVMEM_SPARK_Reset_SysFlag, 6); diff --git a/proto b/proto index 7598b3eea2..f58553e85f 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 7598b3eea210a2b7291b9ea2832cc5fc89f7ac09 +Subproject commit f58553e85f8f974fd8d4acc164daa45ac0585c29 diff --git a/services/inc/allocator.h b/services/inc/allocator.h new file mode 100644 index 0000000000..28824163e5 --- /dev/null +++ b/services/inc/allocator.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#pragma once + +#include + +namespace particle { + +// Abstract allocator +class SimpleAllocator { +public: + virtual ~SimpleAllocator() = default; + + virtual void* alloc(size_t size) = 0; + virtual void free(void* ptr) = 0; +}; + +class Allocator: public SimpleAllocator { +public: + virtual void* realloc(void* ptr, size_t size) = 0; +}; + +// Allocator interface for malloc() +class HeapAllocator: public Allocator { +public: + virtual void* alloc(size_t size) override { + return ::malloc(size); + } + + virtual void* realloc(void* ptr, size_t size) override { + return ::realloc(ptr, size); + } + + virtual void free(void* ptr) override { + ::free(ptr); + } + + static HeapAllocator* instance() { + static HeapAllocator alloc; + return &alloc; + } +}; + +} // particle diff --git a/services/inc/endian.h b/services/inc/endian.h new file mode 100644 index 0000000000..dc675b055b --- /dev/null +++ b/services/inc/endian.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#pragma once + +#include + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define PARTICLE_BIG_ENDIAN 1 +#define PARTICLE_LITTLE_ENDIAN 0 +#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define PARTICLE_BIG_ENDIAN 0 +#define PARTICLE_LITTLE_ENDIAN 1 +#else +#error "Unsupported platform" // TODO: Add support for the PDP-11 architecture +#endif + +namespace particle { + +inline int8_t reverseByteOrder(int8_t val) { + return val; +} + +inline uint8_t reverseByteOrder(uint8_t val) { + return val; +} + +inline int16_t reverseByteOrder(int16_t val) { + return __builtin_bswap16(val); +} + +inline uint16_t reverseByteOrder(uint16_t val) { + return __builtin_bswap16(val); +} + +inline int32_t reverseByteOrder(int32_t val) { + return __builtin_bswap32(val); +} + +inline uint32_t reverseByteOrder(uint32_t val) { + return __builtin_bswap32(val); +} + +inline int64_t reverseByteOrder(int64_t val) { + return __builtin_bswap64(val); +} + +inline uint64_t reverseByteOrder(uint64_t val) { + return __builtin_bswap64(val); +} + +template +inline T nativeToBigEndian(T val) { +#if PARTICLE_BIG_ENDIAN + return val; +#else + return reverseByteOrder(val); +#endif +} + +template +inline T bigEndianToNative(T val) { + return nativeToBigEndian(val); +} + +template +inline T nativeToLittleEndian(T val) { +#if PARTICLE_LITTLE_ENDIAN + return val; +#else + return reverseByteOrder(val); +#endif +} + +template +inline T littleEndianToNative(T val) { + return nativeToLittleEndian(val); +} + +} // particle diff --git a/services/inc/intrusive_queue.h b/services/inc/intrusive_queue.h new file mode 100644 index 0000000000..79db7f467a --- /dev/null +++ b/services/inc/intrusive_queue.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#pragma once + +namespace particle { + +// Template class implementing an intrusive queue container +template +class IntrusiveQueue { +public: + typedef ItemT ItemType; + + IntrusiveQueue() : + front_(nullptr), + back_(nullptr) { + } + + void pushBack(ItemT* item) { + if (back_) { + back_->next = item; + } else { // The queue is empty + front_ = item; + } + item->next = nullptr; + back_ = item; + } + + ItemT* popFront() { + if (!front_) { + return nullptr; + } + const auto item = front_; + front_ = static_cast(front_->next); + if (!front_) { + back_ = nullptr; + } + return item; + } + + ItemT* front() const { + return front_; + } + + ItemT* back() const { + return back_; + } + +private: + ItemT* front_; + ItemT* back_; +}; + +} // particle diff --git a/services/inc/linked_buffer.h b/services/inc/linked_buffer.h new file mode 100644 index 0000000000..300ade9597 --- /dev/null +++ b/services/inc/linked_buffer.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#pragma once + +#include "allocator.h" + +#include + +namespace particle { + +namespace detail { + +constexpr size_t alignedSize(size_t size) { + return ((size + sizeof(uintptr_t) - 1) / sizeof(uintptr_t)) * sizeof(uintptr_t); +} + +} // particle::detail + +// Mixin class implementing a linked buffer +template +struct LinkedBuffer: BaseT... { + LinkedBuffer* next; +}; + +template +inline BufferT* allocLinkedBuffer(size_t size, SimpleAllocator* alloc) { + const auto buf = (BufferT*)alloc->alloc(size + detail::alignedSize(sizeof(BufferT))); + if (buf) { + new(buf) BufferT(); + } + return buf; +} + +template::value>::type> +inline BufferT* reallocLinkedBuffer(BufferT* buf, size_t size, Allocator* alloc) { + const auto b = (BufferT*)alloc->realloc(buf, size + detail::alignedSize(sizeof(BufferT))); + if (!buf && b) { + new(b) BufferT(); + } + return b; +} + +template +inline void freeLinkedBuffer(BufferT* buf, SimpleAllocator* alloc) { + if (buf) { + buf->~BufferT(); + alloc->free(buf); + } +} + +template +inline BufferT* allocLinkedBuffer(size_t size) { + return allocLinkedBuffer(size, HeapAllocator::instance()); +} + +template +inline BufferT* reallocLinkedBuffer(BufferT* buf, size_t size) { + return reallocLinkedBuffer(buf, size, HeapAllocator::instance()); +} + +template +inline void freeLinkedBuffer(BufferT* buf) { + freeLinkedBuffer(buf, HeapAllocator::instance()); +} + +template +inline char* linkedBufferData(BufferT* buf) { + return (char*)buf + detail::alignedSize(sizeof(BufferT)); +} + +template +inline const char* linkedBufferData(const BufferT* buf) { + return (const char*)buf + detail::alignedSize(sizeof(BufferT)); +} + +} // particle diff --git a/system/inc/simple_pool_allocator.h b/system/inc/simple_pool_allocator.h index 6de04b2a06..8382cbfe38 100644 --- a/system/inc/simple_pool_allocator.h +++ b/system/inc/simple_pool_allocator.h @@ -1,13 +1,40 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#pragma once + #include -#include +#include + +#include "spark_wiring_interrupts.h" + +#include "allocator.h" +#include "system_error.h" #ifndef SIMPLE_POOL_PREFER_SMALLER_BLOCK #define SIMPLE_POOL_PREFER_SMALLER_BLOCK (1) #endif -class SimpleBasePool { +class SimpleBasePool: public particle::SimpleAllocator { public: - void* allocate(size_t size) { + virtual void* alloc(size_t size) override { + if (!begin_) { + return nullptr; + } void* p = nullptr; const size_t alignedSize = aligned(sizeof(BlockHeader) + size); if ((size_ - (ptr_ - begin_)) >= alignedSize) { @@ -51,7 +78,7 @@ class SimpleBasePool { return p; } - void deallocate(void* p) { + virtual void free(void* p) override { if (p == nullptr) { return; } @@ -82,13 +109,30 @@ class SimpleBasePool { shrink(); } - virtual ~SimpleBasePool() = default; + // FIXME: This API is here for compatibility with the existing system code and unit tests + void* allocate(size_t size) { + return this->alloc(size); + } + + void deallocate(void* p) { + this->free(p); + } protected: - SimpleBasePool(void* location, size_t size) : - begin_(reinterpret_cast(location)), - size_(size), - ptr_(begin_) { + SimpleBasePool() { + reset(); + } + + SimpleBasePool(void* location, size_t size) { + reset(static_cast(location), size); + } + + void reset(uint8_t* data = nullptr, size_t size = 0) { + begin_ = data; + ptr_ = data; + size_ = size; + freeList_ = nullptr; + freeListEnd_ = nullptr; } uint8_t* begin_; @@ -157,3 +201,33 @@ class SimpleStaticPool : public SimpleBasePool { SimpleBasePool(ptr, size) { } }; + +class AtomicAllocedPool: public SimpleBasePool { +public: + virtual ~AtomicAllocedPool() { + delete[] SimpleBasePool::begin_; + } + + int init(size_t size) { + const auto p = new(std::nothrow) uint8_t[size]; + if (!p) { + return SYSTEM_ERROR_NO_MEMORY; + } + SimpleBasePool::reset(p, size); + return 0; + } + + virtual void* alloc(size_t size) override { + void* p = nullptr; + ATOMIC_BLOCK() { + p = SimpleBasePool::alloc(size); + } + return p; + } + + virtual void free(void* ptr) override { + ATOMIC_BLOCK() { + SimpleBasePool::free(ptr); + } + } +}; diff --git a/system/inc/system_control.h b/system/inc/system_control.h index f597b22c7b..37d18da249 100644 --- a/system/inc/system_control.h +++ b/system/inc/system_control.h @@ -18,16 +18,15 @@ #pragma once #include "usb_hal.h" -#include "ble_hal.h" +#include "hal_platform.h" #include "system_error.h" #include #include -// TODO: Move this feature macro to a platform-specific header #ifndef SYSTEM_CONTROL_ENABLED -#if defined(USB_VENDOR_REQUEST_ENABLE) || BLE_ENABLED +#if defined(USB_VENDOR_REQUEST_ENABLE) || HAL_PLATFORM_BLE #define SYSTEM_CONTROL_ENABLED 1 #else #define SYSTEM_CONTROL_ENABLED 0 diff --git a/system/src/atomic_intrusive_queue.h b/system/src/atomic_intrusive_queue.h new file mode 100644 index 0000000000..e48adcb91f --- /dev/null +++ b/system/src/atomic_intrusive_queue.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#pragma once + +#include "spark_wiring_interrupts.h" + +#include "intrusive_queue.h" + +namespace particle { + +namespace system { + +// TODO: Use a known good lockless queue implementation +template +class AtomicIntrusiveQueue: private IntrusiveQueue { +public: + using IntrusiveQueue::ItemType; + + void pushBack(ItemT* item) { + ATOMIC_BLOCK() { + IntrusiveQueue::pushBack(item); + } + } + + ItemT* popFront() { + ItemT* item = nullptr; + ATOMIC_BLOCK() { + item = IntrusiveQueue::popFront(); + } + return item; + } + + ItemT* front() const { + ItemT* item = nullptr; + ATOMIC_BLOCK() { + item = IntrusiveQueue::front(); + } + return item; + } + + ItemT* back() const { + ItemT* item = nullptr; + ATOMIC_BLOCK() { + item = IntrusiveQueue::back(); + } + return item; + } +}; + +} // particle::system + +} // particle diff --git a/system/src/ble_control_request_channel.cpp b/system/src/ble_control_request_channel.cpp index b2aee0e7d6..056a3842cd 100644 --- a/system/src/ble_control_request_channel.cpp +++ b/system/src/ble_control_request_channel.cpp @@ -21,14 +21,56 @@ LOG_SOURCE_CATEGORY("system.ctrl.ble") #include "ble_control_request_channel.h" -#if SYSTEM_CONTROL_ENABLED && BLE_ENABLED +#if SYSTEM_CONTROL_ENABLED && HAL_PLATFORM_BLE #include "device_code.h" -#include "preprocessor.h" +#include "timer_hal.h" +#include "deviceid_hal.h" -#ifndef PAIRING_ENABLED -#define PAIRING_ENABLED 0 +#include "endian.h" +#include "debug.h" + +#include "mbedtls/ecjpake.h" +#include "mbedtls/ccm.h" +#include "mbedtls/md.h" + +#include "mbedtls_util.h" + +#define CHECK(_expr) \ + do { \ + const int _ret = _expr; \ + if (_ret < 0) { \ + return _ret; \ + } \ + } while (false) + +#define CHECK_MBEDTLS(_expr) \ + do { \ + const int _ret = _expr; \ + if (_ret != 0) { \ + LOG_DEBUG(ERROR, #_expr " failed: %d", _ret); \ + return mbedtlsError(_ret); \ + } \ + } while (false) + +#undef DEBUG // Legacy logging macro + +#if BLE_CHANNEL_DEBUG_ENABLED +#define DEBUG(_fmt, ...) \ + do { \ + LOG_PRINTF(TRACE, _fmt "\r\n", ##__VA_ARGS__); \ + } while (false) + +#define DEBUG_DUMP(_data, _size) \ + do { \ + LOG_PRINT(TRACE, "> "); \ + LOG_DUMP(TRACE, _data, _size); \ + LOG_PRINTF(TRACE, " (%u bytes)\r\n", (unsigned)(_size)); \ + } while (false) +#else +#define DEBUG(...) +#define DEBUG_DUMP(...) #endif namespace particle { @@ -37,8 +79,35 @@ namespace system { namespace { +// Header containing some of the fields common for request and reply messages. These fields are +// authenticated but not encrypted +struct __attribute__((packed)) MessageHeader { + uint16_t size; // Payload size +}; + +// Request message header +struct __attribute__((packed)) RequestHeader { + uint16_t id; // Request ID + uint16_t type; // Request type + uint16_t reserved; +}; + +// Reply message header +struct __attribute__((packed)) ReplyHeader { + uint16_t id; // Request ID + int32_t result; // Result code +}; + +// Handshake packet header +struct __attribute__((packed)) HandshakeHeader { + uint16_t size; // Payload size +}; + +// Particle's company ID +const unsigned COMPANY_ID = 0x0662; + // Device setup protocol version -const unsigned PROTOCOL_VERSION = 0x01; +const unsigned PROTOCOL_VERSION = 0x02; // Vendor-specific base UUID: 6FA9xxxx-5C4E-48A8-94F4-8030546F36FC const uint8_t BASE_UUID[16] = { 0xfc, 0x36, 0x6f, 0x54, 0x30, 0x80, 0xf4, 0x94, 0xa8, 0x48, 0x4e, 0x5c, 0x00, 0x00, 0xa9, 0x6f }; @@ -55,46 +124,576 @@ const unsigned SEND_CHAR_UUID = 0x0003; // UUID of the characteristic used to receive request data const unsigned RECV_CHAR_UUID = 0x0004; -// Size of the buffer for reply data -const unsigned SEND_BUF_SIZE = BLE_MAX_ATTR_VALUE_SIZE; +// Size of the buffer pool +const size_t BUFFER_POOL_SIZE = 1024; -// Size of the buffer for request data -const unsigned RECV_BUF_SIZE = 1024; // Should be a power of two +// Size of the message header +const size_t MESSAGE_HEADER_SIZE = sizeof(MessageHeader); -static_assert(SEND_BUF_SIZE >= BLE_MAX_ATTR_VALUE_SIZE && RECV_BUF_SIZE >= BLE_MAX_ATTR_VALUE_SIZE, - "Invalid buffer size"); +// Size of the request header +const size_t REQUEST_HEADER_SIZE = sizeof(RequestHeader); -struct __attribute__((packed)) RequestHeader { - uint16_t id; - uint16_t type; - uint32_t size; +// Size of the reply header +const size_t REPLY_HEADER_SIZE = sizeof(ReplyHeader); + +// Size of the handshake packet header +const size_t HANDSHAKE_HEADER_SIZE = sizeof(HandshakeHeader); + +// Maximum size of the payload data in a handshake packet +const size_t MAX_HANDSHAKE_PAYLOAD_SIZE = 512; + +// Handshake timeout in milliseconds +const unsigned HANDSHAKE_TIMEOUT = 10000; + +// Size of the J-PAKE passphrase in bytes +const size_t JPAKE_PASSPHRASE_SIZE = 15; + +// Size of the J-PAKE's shared secret in bytes +const size_t JPAKE_SHARED_SECRET_SIZE = 32; + +// Client identity string +const char* const JPAKE_CLIENT_ID = "client"; + +// Server identity string +const char* const JPAKE_SERVER_ID = "server"; + +// Size of the cipher's key in bytes +const size_t AES_CCM_KEY_SIZE = 16; + +// Size of the authentication field in bytes +const size_t AES_CCM_TAG_SIZE = 8; + +// Total size of the nonce in bytes +const size_t AES_CCM_NONCE_SIZE = 12; + +// Size of the fixed part of the nonce in bytes +const size_t AES_CCM_FIXED_NONCE_SIZE = 8; + +// Sanity checks +static_assert(15 - AES_CCM_NONCE_SIZE >= 3, // At least 3 bytes should be available to store the size of encrypted data + "Invalid size of the CCM length field"); // See RFC 3610 + +static_assert(AES_CCM_NONCE_SIZE >= AES_CCM_FIXED_NONCE_SIZE + 4, // 4 bytes of the nonce data are reserved for the counter + "Invalid size of the nonce"); + +static_assert(JPAKE_SHARED_SECRET_SIZE >= AES_CCM_KEY_SIZE + AES_CCM_FIXED_NONCE_SIZE * 2, // See BleControlRequestChannel::initAesCcm() + "Invalid size of the shared secret"); + +#if BLE_CHANNEL_SECURITY_ENABLED +const size_t MESSAGE_FOOTER_SIZE = AES_CCM_TAG_SIZE; +#else +const size_t MESSAGE_FOOTER_SIZE = 0; +#endif + +int mbedtlsError(int ret) { + switch (ret) { + case 0: + return SYSTEM_ERROR_NONE; + // TODO + default: + return SYSTEM_ERROR_UNKNOWN; + } +} + +class Sha256 { +public: + static const size_t SIZE = 32; + + Sha256() : + ctx_() { + } + + ~Sha256() { + destroy(); + } + + int init() { + const auto mdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + if (!mdInfo) { + LOG_DEBUG(ERROR, "mbedtls_md_info_from_type() failed"); + return SYSTEM_ERROR_NOT_FOUND; + } + mbedtls_md_init(&ctx_); + CHECK_MBEDTLS(mbedtls_md_setup(&ctx_, mdInfo, 0 /* hmac */)); + CHECK_MBEDTLS(mbedtls_md_starts(&ctx_)); + return 0; + } + + int init(const Sha256& src) { + CHECK(init()); + CHECK_MBEDTLS(mbedtls_md_clone(&ctx_, &src.ctx_)); + return 0; + } + + void destroy() { + mbedtls_md_free(&ctx_); + } + + int start() { + CHECK_MBEDTLS(mbedtls_md_starts(&ctx_)); + return 0; + } + + int update(const char* data, size_t size) { + CHECK_MBEDTLS(mbedtls_md_update(&ctx_, (const uint8_t*)data, size)); + return 0; + } + + int update(const char* str) { + return update(str, strlen(str)); + } + + int finish(char* buf) { + CHECK_MBEDTLS(mbedtls_md_finish(&ctx_, (uint8_t*)buf)); + return 0; + } + +private: + mbedtls_md_context_t ctx_; }; -struct __attribute__((packed)) ReplyHeader { - uint16_t id; - int16_t result; - uint32_t size; +class HmacSha256 { +public: + static const size_t SIZE = 32; + + HmacSha256() : + ctx_() { + } + + ~HmacSha256() { + destroy(); + } + + int init(const char* key, size_t keySize) { + const auto mdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + if (!mdInfo) { + LOG_DEBUG(ERROR, "mbedtls_md_info_from_type() failed"); + return SYSTEM_ERROR_NOT_FOUND; + } + mbedtls_md_init(&ctx_); + CHECK_MBEDTLS(mbedtls_md_setup(&ctx_, mdInfo, 1 /* hmac */)); + CHECK_MBEDTLS(mbedtls_md_hmac_starts(&ctx_, (const uint8_t*)key, keySize)); + return 0; + } + + void destroy() { + mbedtls_md_free(&ctx_); + } + + int start() { + CHECK_MBEDTLS(mbedtls_md_hmac_reset(&ctx_)); + return 0; + } + + int update(const char* data, size_t size) { + CHECK_MBEDTLS(mbedtls_md_hmac_update(&ctx_, (const uint8_t*)data, size)); + return 0; + } + + int update(const char* str) { + return update(str, strlen(str)); + } + + int finish(char* buf) { + CHECK_MBEDTLS(mbedtls_md_hmac_finish(&ctx_, (uint8_t*)buf)); + return 0; + } + +private: + mbedtls_md_context_t ctx_; }; } // particle::system:: +class BleControlRequestChannel::HandshakeHandler { +public: + enum Result { + DONE = 0, + RUNNING + }; + + virtual ~HandshakeHandler() { + destroy(); + } + + virtual int run() = 0; + +protected: + explicit HandshakeHandler(BleControlRequestChannel* channel) : + channel_(channel), + buf_(nullptr), + timeStart_(0), + size_(0), + offs_(0) { + } + + int init() { + data_.reset(new(std::nothrow) char[MAX_HANDSHAKE_PAYLOAD_SIZE]); + if (!data_) { + return SYSTEM_ERROR_NO_MEMORY; + } + timeStart_ = HAL_Timer_Get_Milli_Seconds(); + return 0; + } + + void destroy() { + data_.reset(); + channel_->freeBuffer(buf_); + buf_ = nullptr; + size_ = 0; + offs_ = 0; + } + + int readPacket(const char** data, size_t* size) { + if (HAL_Timer_Get_Milli_Seconds() - timeStart_ >= HANDSHAKE_TIMEOUT) { + return SYSTEM_ERROR_TIMEOUT; + } + if (offs_ == size_) { + // Read packet header + HandshakeHeader h = {}; + if (!channel_->readAll((char*)&h, HANDSHAKE_HEADER_SIZE)) { + return Result::RUNNING; + } + size_ = littleEndianToNative(h.size); + if (size_ == 0 || size_ > MAX_HANDSHAKE_PAYLOAD_SIZE) { + LOG_DEBUG(WARN, "Too large handshake packet"); + return SYSTEM_ERROR_TOO_LARGE; + } + offs_ = 0; + } + // Read remaining packet data + offs_ += channel_->readSome(data_.get() + offs_, size_ - offs_); + if (offs_ < size_) { + return Result::RUNNING; + } + *data = data_.get(); + *size = size_; + return 0; + } + + void writePacket() { + SPARK_ASSERT(buf_); + // Serialize packet header + HandshakeHeader h = {}; + h.size = nativeToLittleEndian(buf_->size); + buf_->data -= HANDSHAKE_HEADER_SIZE; + buf_->size += HANDSHAKE_HEADER_SIZE; + memcpy(buf_->data, &h, HANDSHAKE_HEADER_SIZE); + // Enqueue the buffer for sending + channel_->sendBuffer(buf_); + buf_ = nullptr; + } + + int initPacket(Buffer** buf, size_t size) { + SPARK_ASSERT(!buf_); + CHECK(channel_->reallocBuffer(HANDSHAKE_HEADER_SIZE + size, &buf_)); + buf_->data += HANDSHAKE_HEADER_SIZE; + buf_->size -= HANDSHAKE_HEADER_SIZE; + *buf = buf_; + return 0; + } + + int initPacket(Buffer** buf) { + return initPacket(buf, MAX_HANDSHAKE_PAYLOAD_SIZE); + } + +private: + std::unique_ptr data_; + BleControlRequestChannel* channel_; + BleControlRequestChannel::Buffer* buf_; + system_tick_t timeStart_; + size_t size_; + size_t offs_; +}; + +class BleControlRequestChannel::JpakeHandler: public HandshakeHandler { +public: + explicit JpakeHandler(BleControlRequestChannel* channel) : + HandshakeHandler(channel), + ctx_(), + state_(State::NEW) { + } + + ~JpakeHandler() { + mbedtls_ecjpake_free(&ctx_); + memset(secret_, 0, sizeof(secret_)); + memset(confirmKey_, 0, sizeof(confirmKey_)); + } + + int init(const char* key, size_t keySize) { + CHECK(HandshakeHandler::init()); + CHECK(hash_.init()); + mbedtls_ecjpake_init(&ctx_); + CHECK_MBEDTLS(mbedtls_ecjpake_setup(&ctx_, MBEDTLS_ECJPAKE_SERVER, MBEDTLS_MD_SHA256, MBEDTLS_ECP_DP_SECP256R1, + (const uint8_t*)key, keySize)); + state_ = State::READ_ROUND1; + return 0; + } + + virtual int run() override { + int ret = 0; + switch (state_) { + case State::READ_ROUND1: + ret = readRound1(); + break; + case State::WRITE_ROUND1: + ret = writeRound1(); + break; + case State::READ_ROUND2: + ret = readRound2(); + break; + case State::WRITE_ROUND2: + ret = writeRound2(); + break; + case State::READ_CONFIRM: + ret = readConfirm(); + break; + case State::WRITE_CONFIRM: + ret = writeConfirm(); + break; + case State::DONE: + return Result::DONE; + default: + return SYSTEM_ERROR_INVALID_STATE; + } + if (ret < 0) { + state_ = State::FAILED; + } + return ret; + } + + const char* secret() const { + if (state_ != State::DONE) { + return nullptr; + } + return secret_; + } + +private: + enum class State { + NEW, + READ_ROUND1, + WRITE_ROUND1, + READ_ROUND2, + WRITE_ROUND2, + READ_CONFIRM, + WRITE_CONFIRM, + DONE, + FAILED + }; + + Sha256 hash_; + mbedtls_ecjpake_context ctx_; + char secret_[JPAKE_SHARED_SECRET_SIZE]; + char confirmKey_[Sha256::SIZE]; + State state_; + + int readRound1() { + const char* data = nullptr; + size_t size = 0; + const int ret = readPacket(&data, &size); + if (ret != Result::DONE) { + return ret; + } + CHECK_MBEDTLS(mbedtls_ecjpake_read_round_one(&ctx_, (const uint8_t*)data, size)); + CHECK(hash_.update(data, size)); + state_ = State::WRITE_ROUND1; + return Result::RUNNING; + } + + int writeRound1() { + Buffer* buf = nullptr; + CHECK(initPacket(&buf)); + size_t n = 0; + CHECK_MBEDTLS(mbedtls_ecjpake_write_round_one(&ctx_, (uint8_t*)buf->data, buf->size, &n, mbedtls_default_rng, nullptr)); + buf->size = n; + CHECK(hash_.update(buf->data, buf->size)); + writePacket(); + state_ = State::WRITE_ROUND2; // Send the round 2 message immediately + return Result::RUNNING; + } + + int readRound2() { + const char* data = nullptr; + size_t size = 0; + const int ret = readPacket(&data, &size); + if (ret != Result::DONE) { + return ret; + } + CHECK_MBEDTLS(mbedtls_ecjpake_read_round_two(&ctx_, (const uint8_t*)data, size)); + CHECK(hash_.update(data, size)); + state_ = State::READ_CONFIRM; + return Result::RUNNING; + } + + int writeRound2() { + Buffer* buf = nullptr; + CHECK(initPacket(&buf)); + size_t n = 0; + CHECK_MBEDTLS(mbedtls_ecjpake_write_round_two(&ctx_, (uint8_t*)buf->data, buf->size, &n, mbedtls_default_rng, nullptr)); + buf->size = n; + CHECK(hash_.update(buf->data, buf->size)); + writePacket(); + state_ = State::READ_ROUND2; + return Result::RUNNING; + } + + int readConfirm() { + const char* data = nullptr; + size_t size = 0; + const int ret = readPacket(&data, &size); + if (ret != Result::DONE) { + return ret; + } + if (size != Sha256::SIZE) { + LOG_DEBUG(ERROR, "Invalid size of the confirmation message"); + return SYSTEM_ERROR_BAD_DATA; + } + // Derive the shared secret + size_t n = 0; + CHECK_MBEDTLS(mbedtls_ecjpake_derive_secret(&ctx_, (uint8_t*)secret_, sizeof(secret_), &n, + mbedtls_default_rng, nullptr)); + if (n != JPAKE_SHARED_SECRET_SIZE) { // Sanity check + LOG_DEBUG(ERROR, "Invalid size of the shared secret"); + return SYSTEM_ERROR_INTERNAL; + } + mbedtls_ecjpake_free(&ctx_); + // Generate the confirmation key + Sha256 hash; + CHECK(hash.init()); + CHECK(hash.update(secret_, sizeof(secret_))); + CHECK(hash.update("JPAKE_KC")); + CHECK(hash.finish(confirmKey_)); + hash.destroy(); + // Validate the confirmation message + CHECK(hash.init(hash_)); + char hashVal[Sha256::SIZE] = {}; + CHECK(hash.finish(hashVal)); + hash.destroy(); + HmacSha256 hmac; + CHECK(hmac.init(confirmKey_, sizeof(confirmKey_))); + CHECK(hmac.update("KC_1_U")); + CHECK(hmac.update(JPAKE_CLIENT_ID)); + CHECK(hmac.update(JPAKE_SERVER_ID)); + CHECK(hmac.update(hashVal, sizeof(hashVal))); + CHECK(hmac.finish(hashVal)); + if (memcmp(data, hashVal, Sha256::SIZE) != 0) { + LOG_DEBUG(ERROR, "Invalid confirmation message"); + return SYSTEM_ERROR_BAD_DATA; + } + CHECK(hash_.update(data, size)); + HandshakeHandler::destroy(); // Free the buffer for received data + state_ = State::WRITE_CONFIRM; + return Result::RUNNING; + } + + int writeConfirm() { + Buffer* buf = nullptr; + CHECK(initPacket(&buf, Sha256::SIZE)); + CHECK(hash_.finish(buf->data)); + hash_.destroy(); + HmacSha256 hmac; + CHECK(hmac.init(confirmKey_, sizeof(confirmKey_))); + CHECK(hmac.update("KC_1_U")); + CHECK(hmac.update(JPAKE_SERVER_ID)); + CHECK(hmac.update(JPAKE_CLIENT_ID)); + CHECK(hmac.update(buf->data, buf->size)); + CHECK(hmac.finish(buf->data)); + writePacket(); + state_ = State::DONE; + return Result::DONE; + } +}; + +class BleControlRequestChannel::AesCcmCipher { +public: + AesCcmCipher() : + ctx_(), + reqCount_(0), + repCount_(0) { + } + + ~AesCcmCipher() { + mbedtls_ccm_free(&ctx_); + memset(reqNonce_, 0, AES_CCM_FIXED_NONCE_SIZE); + memset(repNonce_, 0, AES_CCM_FIXED_NONCE_SIZE); + } + + int init(const char* key, const char* clientNonce, const char* serverNonce) { + mbedtls_ccm_init(&ctx_); + CHECK_MBEDTLS(mbedtls_ccm_setkey(&ctx_, MBEDTLS_CIPHER_ID_AES, (const uint8_t*)key, AES_CCM_KEY_SIZE * 8)); + memcpy(reqNonce_, clientNonce, AES_CCM_FIXED_NONCE_SIZE); + memcpy(repNonce_, serverNonce, AES_CCM_FIXED_NONCE_SIZE); + return 0; + } + + int decryptRequestData(char* buf, size_t payloadSize) { + char nonce[AES_CCM_NONCE_SIZE] = {}; + genRequestNonce(nonce); + CHECK_MBEDTLS(mbedtls_ccm_auth_decrypt(&ctx_, + payloadSize + REQUEST_HEADER_SIZE, // Size of the input data + (const uint8_t*)nonce, AES_CCM_NONCE_SIZE, // Nonce + (const uint8_t*)buf, MESSAGE_HEADER_SIZE, // Additional data + (const uint8_t*)buf + MESSAGE_HEADER_SIZE, // Input buffer + (uint8_t*)buf + MESSAGE_HEADER_SIZE, // Output buffer + (const uint8_t*)buf + payloadSize + MESSAGE_HEADER_SIZE + REQUEST_HEADER_SIZE, AES_CCM_TAG_SIZE)); // Authentication tag + return 0; + } + + int encryptReplyData(char* buf, size_t payloadSize) { + char nonce[AES_CCM_NONCE_SIZE] = {}; + genReplyNonce(nonce); + CHECK_MBEDTLS(mbedtls_ccm_encrypt_and_tag(&ctx_, + payloadSize + REPLY_HEADER_SIZE, // Size of the input data + (const uint8_t*)nonce, AES_CCM_NONCE_SIZE, // Nonce + (const uint8_t*)buf, MESSAGE_HEADER_SIZE, // Additional data + (const uint8_t*)buf + MESSAGE_HEADER_SIZE, // Input buffer + (uint8_t*)buf + MESSAGE_HEADER_SIZE, // Output buffer + (uint8_t*)buf + payloadSize + MESSAGE_HEADER_SIZE + REPLY_HEADER_SIZE, AES_CCM_TAG_SIZE)); // Authentication tag + return 0; + } + +private: + mbedtls_ccm_context ctx_; + char reqNonce_[AES_CCM_FIXED_NONCE_SIZE]; + char repNonce_[AES_CCM_FIXED_NONCE_SIZE]; + uint32_t reqCount_; + uint32_t repCount_; + + void genRequestNonce(char* dest) { + const uint32_t count = nativeToLittleEndian(++reqCount_); + memcpy(dest, &count, 4); + memcpy(dest + 4, reqNonce_, AES_CCM_FIXED_NONCE_SIZE); + } + + void genReplyNonce(char* dest) { + const uint32_t count = nativeToLittleEndian(++repCount_ | 0x80000000u); + memcpy(dest, &count, 4); + memcpy(dest + 4, repNonce_, AES_CCM_FIXED_NONCE_SIZE); + } +}; + BleControlRequestChannel::BleControlRequestChannel(ControlRequestHandler* handler) : ControlRequestChannel(handler), - pendingReqs_(nullptr), - readyReqs_(nullptr), - sendReq_(nullptr), - sendPos_(0), - headerSent_(false), - recvReq_(nullptr), - recvFifo_(), - recvPos_(0), - headerRecvd_(false), - sendCharHandle_(BLE_INVALID_ATTR_HANDLE), - recvCharHandle_(BLE_INVALID_ATTR_HANDLE), +#if BLE_CHANNEL_DEBUG_ENABLED + allocReqCount_(0), + heapBufCount_(0), + poolBufCount_(0), +#endif + inBufSize_(0), + curReq_(nullptr), + reqBufSize_(0), + reqBufOffs_(0), + packetSize_(0), connHandle_(BLE_INVALID_CONN_HANDLE), - maxCharValSize_(0), + curConnHandle_(BLE_INVALID_CONN_HANDLE), + connId_(0), + curConnId_(0), + maxPacketSize_(0), notifEnabled_(false), - writable_(false) { + writable_(false), + sendCharHandle_(BLE_INVALID_ATTR_HANDLE), + recvCharHandle_(BLE_INVALID_ATTR_HANDLE) { } BleControlRequestChannel::~BleControlRequestChannel() { @@ -102,26 +701,17 @@ BleControlRequestChannel::~BleControlRequestChannel() { } int BleControlRequestChannel::init() { - uint32_t nrfRet = 0; + // Make sure we have a copy of the device secret in the DCT, so that it can be easily extracted + // via dfu-util for testing purposes + hal_get_device_secret(nullptr, 0, nullptr); + // Initialize the BLE profile int ret = initProfile(); if (ret != 0) { goto error; } - // TODO: Allocate necessary buffers when a BLE connection is accepted - sendBuf_.reset(new(std::nothrow) uint8_t[SEND_BUF_SIZE]); - if (!sendBuf_) { - ret = SYSTEM_ERROR_NO_MEMORY; - goto error; - } - recvBuf_.reset(new(std::nothrow) uint8_t[RECV_BUF_SIZE]); - if (!recvBuf_) { - ret = SYSTEM_ERROR_NO_MEMORY; - goto error; - } - nrfRet = app_fifo_init(&recvFifo_, recvBuf_.get(), RECV_BUF_SIZE); - if (nrfRet != NRF_SUCCESS) { - LOG(ERROR, "app_fifo_init() failed: %u", (unsigned)nrfRet); - ret = SYSTEM_ERROR_UNKNOWN; + // TODO: Initialize this allocator when a BLE connection is accepted + ret = pool_.init(BUFFER_POOL_SIZE); + if (ret != 0) { goto error; } return 0; @@ -132,231 +722,393 @@ int BleControlRequestChannel::init() { void BleControlRequestChannel::destroy() { // TODO: There doesn't seem to be a straightforward way to uninitialize the profile - sendBuf_.reset(); - recvBuf_.reset(); } -int BleControlRequestChannel::run() { - if (connHandle_ == BLE_INVALID_CONN_HANDLE) { - // Free completed requests - while (readyReqs_) { - readyReqs_ = freeRequest(readyReqs_); +void BleControlRequestChannel::run() { + int ret = 0; + const auto connId = curConnId_.load(std::memory_order_acquire); + if (connId_ != connId) { + const auto prevConnHandle = connHandle_; + connHandle_ = curConnHandle_; + connId_ = connId; + // Reset channel state + resetChannel(); + if (connHandle_ != BLE_INVALID_CONN_HANDLE) { + LOG(TRACE, "Connected"); + ret = initChannel(); + if (ret != 0) { + goto error; + } + } else if (prevConnHandle != BLE_INVALID_CONN_HANDLE) { + LOG(TRACE, "Disconnected"); } - freeRequest(sendReq_); - sendReq_ = nullptr; - freeRequest(recvReq_); - recvReq_ = nullptr; - // Clear buffers and reset state - app_fifo_flush(&recvFifo_); - sendPos_ = 0; - recvPos_ = 0; - headerSent_ = false; - headerRecvd_ = false; - } else { - // Proceed sending and receiving data - int ret = receiveNext(); - if (ret == 0) { - ret = sendNext(); + } + if (connHandle_ != BLE_INVALID_CONN_HANDLE) { +#if BLE_CHANNEL_SECURITY_ENABLED + if (jpake_) { + // Process handshake packets + ret = jpake_->run(); + if (ret < 0) { + LOG(ERROR, "Handshake failed"); + goto error; + } + if (ret == JpakeHandler::DONE) { + ret = initAesCcm(); + if (ret != 0) { + goto error; + } + jpake_.reset(); + LOG(TRACE, "Handshake done"); + } + } else { +#endif + // Serialize next reply and enqueue it for sending + ret = sendReply(); + if (ret != 0) { + goto error; + } + // Receive next request + ret = receiveRequest(); + if (ret != 0) { + goto error; + } +#if BLE_CHANNEL_SECURITY_ENABLED } +#endif + // Send BLE notification packet + ret = sendPacket(); if (ret != 0) { - LOG(ERROR, "Connection error"); - ble_disconnect(connHandle_, nullptr); - connHandle_ = BLE_INVALID_CONN_HANDLE; - return ret; + goto error; } } + return; +error: + LOG(ERROR, "Channel error: %d", ret); + if (connHandle_ != BLE_INVALID_CONN_HANDLE) { + ble_disconnect(connHandle_, nullptr); + connHandle_ = BLE_INVALID_CONN_HANDLE; + } + resetChannel(); +} + +int BleControlRequestChannel::allocReplyData(ctrl_request* ctrlReq, size_t size) { + const auto req = static_cast(ctrlReq); + CHECK(reallocBuffer(size + MESSAGE_HEADER_SIZE + REPLY_HEADER_SIZE + MESSAGE_FOOTER_SIZE, &req->repBuf)); + if (size > 0) { + req->reply_data = req->repBuf->data + MESSAGE_HEADER_SIZE + REPLY_HEADER_SIZE; + } else { + req->reply_data = nullptr; + } + req->reply_size = size; return 0; } +void BleControlRequestChannel::freeRequestData(ctrl_request* ctrlReq) { + const auto req = static_cast(ctrlReq); + delete[] req->reqBuf; + req->reqBuf = nullptr; + req->request_data = nullptr; + req->request_size = 0; +} + void BleControlRequestChannel::setResult(ctrl_request* ctrlReq, int result, ctrl_completion_handler_fn handler, void* data) { - // FIXME: Synchronization // TODO: Completion handling const auto req = static_cast(ctrlReq); + freeRequestData(req); req->result = result; - req->next = readyReqs_; - readyReqs_ = req; + const std::lock_guard lock(readyReqsLock_); + readyReqs_.pushBack(req); } -int BleControlRequestChannel::sendNext() { - if (!writable_) { - return 0; // Can't send now +int BleControlRequestChannel::initChannel() { + packetBuf_.reset(new(std::nothrow) char[BLE_MAX_ATTR_VALUE_PACKET_SIZE]); + if (!packetBuf_) { + return SYSTEM_ERROR_NO_MEMORY; + } +#if BLE_CHANNEL_SECURITY_ENABLED + CHECK(initJpake()); +#endif + return 0; +} + +void BleControlRequestChannel::resetChannel() { +#if BLE_CHANNEL_SECURITY_ENABLED + jpake_.reset(); + aesCcm_.reset(); +#endif + packetBuf_.reset(); + while (Request* req = readyReqs_.popFront()) { + freeRequest(req); + } + while (Buffer* buf = outBufs_.popFront()) { + freeBuffer(buf); + } + while (Buffer* buf = readInBufs_.popFront()) { + freePooledBuffer(buf); + } + freeRequest(curReq_); + curReq_ = nullptr; + reqBufSize_ = 0; + reqBufOffs_ = 0; + inBufSize_ = 0; + packetSize_ = 0; +} + +int BleControlRequestChannel::receiveRequest() { + if (!curReq_) { + // Read message header + MessageHeader mh = {}; + if (!readAll((char*)&mh, MESSAGE_HEADER_SIZE)) { + return 0; // Wait for more data + } + // Allocate a request object + const size_t payloadSize = littleEndianToNative(mh.size); + CHECK(allocRequest(payloadSize, &curReq_)); + memcpy(curReq_->reqBuf, &mh, MESSAGE_HEADER_SIZE); + reqBufSize_ = payloadSize + MESSAGE_HEADER_SIZE + REQUEST_HEADER_SIZE + MESSAGE_FOOTER_SIZE; // Total size of the request data + reqBufOffs_ = MESSAGE_HEADER_SIZE; + } + // Read remaining request data + const auto p = curReq_->reqBuf; + const size_t n = readSome(p + reqBufOffs_, reqBufSize_ - reqBufOffs_); + reqBufOffs_ += n; + if (reqBufOffs_ < reqBufSize_) { + return 0; // Wait for more data + } +#if BLE_CHANNEL_SECURITY_ENABLED + // Decrypt request data + SPARK_ASSERT(aesCcm_); + CHECK(aesCcm_->decryptRequestData(p, curReq_->request_size)); +#endif + // Parse request header + RequestHeader rh = {}; + memcpy(&rh, p + MESSAGE_HEADER_SIZE, REQUEST_HEADER_SIZE); + curReq_->id = littleEndianToNative(rh.id); // Request ID + curReq_->type = littleEndianToNative(rh.type); // Request type + LOG(TRACE, "Received a request message, ID: %u", (unsigned)curReq_->id); + // Process request + handler()->processRequest(curReq_, this); + curReq_ = nullptr; + reqBufSize_ = 0; + reqBufOffs_ = 0; + return 0; +} + +int BleControlRequestChannel::sendReply() { + std::unique_lock lock(readyReqsLock_); + Request* req = nullptr; + while ((req = readyReqs_.popFront())) { + if (req->connId == connId_) { + break; + } + freeRequest(req); + } + lock.unlock(); + if (!req) { + return 0; // Nothing to send } - if (!sendReq_) { - if (!readyReqs_) { - return 0; // Nothing to send + // Make sure we have a buffer to serialize the reply + if (!req->repBuf) { + const int ret = reallocBuffer(MESSAGE_HEADER_SIZE + REPLY_HEADER_SIZE + MESSAGE_FOOTER_SIZE, &req->repBuf); + if (ret != 0) { + freeRequest(req); + return ret; } - sendReq_ = readyReqs_; - readyReqs_ = readyReqs_->next; } + const auto p = req->repBuf->data; + // Serialize message header + MessageHeader mh = {}; + mh.size = nativeToLittleEndian(req->reply_size); + memcpy(p, &mh, MESSAGE_HEADER_SIZE); // Serialize reply header - uint16_t size = 0; - if (!headerSent_) { - ReplyHeader h = {}; - h.id = sendReq_->id; - h.result = sendReq_->result; - h.size = sendReq_->reply_size; - memcpy(sendBuf_.get(), &h, sizeof(ReplyHeader)); - size = sizeof(ReplyHeader); - } - // Copy reply data - size_t dataSize = 0; - if (sendReq_->reply_size > 0) { - dataSize = sendReq_->reply_size - sendPos_; - if (size + dataSize > maxCharValSize_) { - dataSize = maxCharValSize_ - size; + ReplyHeader rh = {}; + rh.id = nativeToLittleEndian(req->id); + rh.result = nativeToLittleEndian(req->result); + memcpy(p + MESSAGE_HEADER_SIZE, &rh, REPLY_HEADER_SIZE); + int ret = 0; +#if BLE_CHANNEL_SECURITY_ENABLED + // Encrypt reply data + SPARK_ASSERT(aesCcm_); + ret = aesCcm_->encryptReplyData(p, req->reply_size); + if (ret == 0) { +#endif + // Enqueue the reply buffer for sending + outBufs_.pushBack(req->repBuf); + req->repBuf = nullptr; + LOG(TRACE, "Enqueued a reply message for sending, ID: %u", (unsigned)req->id); +#if BLE_CHANNEL_SECURITY_ENABLED + } +#endif + freeRequest(req); + return ret; +} + +int BleControlRequestChannel::sendPacket() { + if (!writable_) { + return 0; // Can't send now + } + // Prepare a BLE packet + SPARK_ASSERT(packetBuf_); + const size_t maxSize = maxPacketSize_; + Buffer* buf = nullptr; + while (packetSize_ < maxSize && (buf = outBufs_.front())) { + const size_t n = std::min(maxSize - packetSize_, buf->size); + memcpy(packetBuf_.get() + packetSize_, buf->data, n); + buf->data += n; + buf->size -= n; + if (buf->size == 0) { + outBufs_.popFront(); + freeBuffer(buf); } - memcpy(sendBuf_.get() + size, sendReq_->reply_data + sendPos_, dataSize); - size += dataSize; + packetSize_ += n; + } + if (packetSize_ == 0) { + return 0; // Nothing to send } // Send packet - const int ret = ble_set_char_value(connHandle_, sendCharHandle_, (const char*)sendBuf_.get(), size, + const int ret = ble_set_char_value(connHandle_, sendCharHandle_, packetBuf_.get(), packetSize_, BLE_SET_CHAR_VALUE_FLAG_NOTIFY, nullptr); if (ret == BLE_ERROR_BUSY) { writable_ = false; // Retry later return 0; } - if (ret != size) { + if (ret != (int)packetSize_) { LOG(ERROR, "ble_set_char_value() failed: %d", ret); return ret; } - LOG_DEBUG(TRACE, "%u bytes sent", (unsigned)size); - sendPos_ += dataSize; - if (sendPos_ == sendReq_->reply_size) { - freeRequest(sendReq_); - sendReq_ = nullptr; - sendPos_ = 0; - headerSent_ = false; - } else { - headerSent_ = true; - } + DEBUG("Sent BLE packet"); + DEBUG_DUMP(packetBuf_.get(), packetSize_); + packetSize_ = 0; return 0; } -int BleControlRequestChannel::receiveNext() { - // Read request header - if (!headerRecvd_) { - uint32_t n = 0; - const auto ret = app_fifo_read(&recvFifo_, nullptr, &n); - if (ret != NRF_SUCCESS && ret != NRF_ERROR_NOT_FOUND) { - LOG(ERROR, "app_fifo_read() failed: %u", (unsigned)ret); - return SYSTEM_ERROR_UNKNOWN; - } - if (n < sizeof(RequestHeader)) { - return 0; // Keep reading - } - RequestHeader h = {}; - n = sizeof(RequestHeader); - app_fifo_read(&recvFifo_, (uint8_t*)&h, &n); - recvReq_ = allocRequest(h.size); - if (!recvReq_) { - LOG(ERROR, "Unable to allocate request object"); - return SYSTEM_ERROR_NO_MEMORY; - } - recvReq_->id = h.id; - recvReq_->type = h.type; - recvReq_->channel = this; - headerRecvd_ = true; - } - // Read request data - if (recvReq_->request_size > 0) { - uint32_t n = recvReq_->request_size - recvPos_; - const auto ret = app_fifo_read(&recvFifo_, (uint8_t*)recvReq_->request_data + recvPos_, &n); - if (ret != NRF_SUCCESS && ret != NRF_ERROR_NOT_FOUND) { - LOG(ERROR, "app_fifo_read() failed: %u", (unsigned)ret); - return SYSTEM_ERROR_UNKNOWN; - } - recvPos_ += n; - if (recvPos_ < recvReq_->request_size) { - return 0; // Keep reading +bool BleControlRequestChannel::readAll(char* data, size_t size) { + // Keep taking input buffers from the queue until there's enough data + Buffer* buf = nullptr; + while (inBufSize_ < size && (buf = inBufs_.popFront())) { + readInBufs_.pushBack(buf); + inBufSize_ += buf->size; + } + if (inBufSize_ < size) { + return false; // Wait for more data + } + // Copy data to the destination buffer + DEBUG("Reading %u bytes", (unsigned)size); + size_t offs = 0; + while (offs < size) { + buf = readInBufs_.front(); + SPARK_ASSERT(buf); + const size_t n = std::min(size - offs, buf->size); + memcpy(data + offs, buf->data, n); + buf->size -= n; + if (buf->size == 0) { + // Free the drained buffer + readInBufs_.popFront(); + freePooledBuffer(buf); + } else { + buf->data += n; } + offs += n; } - // Process request - recvReq_->next = pendingReqs_; - pendingReqs_ = recvReq_; - recvReq_ = nullptr; - recvPos_ = 0; - headerRecvd_ = false; - handler()->processRequest(pendingReqs_, this); - return 0; + inBufSize_ -= size; + DEBUG_DUMP(data, size); + return true; +} + +size_t BleControlRequestChannel::readSome(char* data, size_t size) { + Buffer* buf = nullptr; + while (inBufSize_ < size && (buf = inBufs_.popFront())) { + readInBufs_.pushBack(buf); + inBufSize_ += buf->size; + } + if (inBufSize_ < size) { + size = inBufSize_; + } + if (size > 0) { + const bool ok = readAll(data, size); + SPARK_ASSERT(ok); + } + return size; } -void BleControlRequestChannel::connected(const ble_connected_event_data& event) { - connHandle_ = event.conn_handle; +int BleControlRequestChannel::connected(const ble_connected_event_data& event) { + curConnHandle_ = event.conn_handle; // Get initial connection parameters ble_conn_param connParam = { .version = BLE_API_VERSION }; - int ret = ble_get_conn_param(connHandle_, &connParam, nullptr); - maxCharValSize_ = (ret == 0) ? connParam.max_char_value_size : BLE_MAX_ATTR_VALUE_SIZE; + int ret = ble_get_conn_param(curConnHandle_, &connParam, nullptr); + if (ret == 0) { + maxPacketSize_ = connParam.att_mtu_size - BLE_ATT_OPCODE_SIZE - BLE_ATT_HANDLE_SIZE; + } else { + maxPacketSize_ = BLE_MIN_ATTR_VALUE_PACKET_SIZE; + } ble_char_param charParam = { .version = BLE_API_VERSION }; - ret = ble_get_char_param(connHandle_, sendCharHandle_, &charParam, nullptr); - notifEnabled_ = (ret == 0) ? charParam.notif_enabled : false; + ret = ble_get_char_param(curConnHandle_, sendCharHandle_, &charParam, nullptr); + if (ret == 0) { + notifEnabled_ = charParam.notif_enabled; + } else { + notifEnabled_ = false; + } writable_ = notifEnabled_; + // Update connection state counter + curConnId_.fetch_add(1, std::memory_order_release); + return 0; } -void BleControlRequestChannel::disconnected(const ble_disconnected_event_data& event) { - connHandle_ = BLE_INVALID_CONN_HANDLE; - maxCharValSize_ = 0; - notifEnabled_ = false; +int BleControlRequestChannel::disconnected(const ble_disconnected_event_data& event) { + // Free queued buffers + while (Buffer* buf = inBufs_.popFront()) { + freePooledBuffer(buf); + } + // Reset connection parameters writable_ = false; + notifEnabled_ = false; + maxPacketSize_ = 0; + curConnHandle_ = BLE_INVALID_CONN_HANDLE; + // Update connection state counter + curConnId_.fetch_add(1, std::memory_order_release); + return 0; } -void BleControlRequestChannel::connParamChanged(const ble_conn_param_changed_event_data& event) { +int BleControlRequestChannel::connParamChanged(const ble_conn_param_changed_event_data& event) { ble_conn_param param = { .version = BLE_API_VERSION }; - const int ret = ble_get_conn_param(connHandle_, ¶m, nullptr); - if (ret == 0) { - maxCharValSize_ = param.max_char_value_size; - } else { - LOG(ERROR, "Unable to get connection parameters"); - ble_disconnect(connHandle_, nullptr); - connHandle_ = BLE_INVALID_CONN_HANDLE; - } + CHECK(ble_get_conn_param(curConnHandle_, ¶m, nullptr)); + maxPacketSize_ = param.att_mtu_size - BLE_ATT_OPCODE_SIZE - BLE_ATT_HANDLE_SIZE; + return 0; } -void BleControlRequestChannel::charParamChanged(const ble_char_param_changed_event_data& event) { +int BleControlRequestChannel::charParamChanged(const ble_char_param_changed_event_data& event) { if (event.char_handle == sendCharHandle_) { ble_char_param param = { .version = BLE_API_VERSION }; - const int ret = ble_get_char_param(connHandle_, sendCharHandle_, ¶m, nullptr); - if (ret == 0) { - notifEnabled_ = param.notif_enabled; - writable_ = notifEnabled_; - } else { - LOG(ERROR, "Unable to get characteristic parameters"); - ble_disconnect(connHandle_, nullptr); - connHandle_ = BLE_INVALID_CONN_HANDLE; - } + CHECK(ble_get_char_param(curConnHandle_, sendCharHandle_, ¶m, nullptr)); + notifEnabled_ = param.notif_enabled; + writable_ = notifEnabled_; } + return 0; } -void BleControlRequestChannel::dataSent(const ble_data_sent_event_data& event) { +int BleControlRequestChannel::dataSent(const ble_data_sent_event_data& event) { if (notifEnabled_) { writable_ = true; } + return 0; } -void BleControlRequestChannel::dataReceived(const ble_data_received_event_data& event) { +int BleControlRequestChannel::dataReceived(const ble_data_received_event_data& event) { if (event.char_handle == recvCharHandle_) { - uint32_t size = event.size; - const auto ret = app_fifo_write(&recvFifo_, (const uint8_t*)event.data, &size); - if (ret != NRF_SUCCESS) { - LOG(ERROR, "app_fifo_write() failed: %u", (unsigned)ret); - ble_disconnect(connHandle_, nullptr); - connHandle_ = BLE_INVALID_CONN_HANDLE; - } - if (size != event.size) { - LOG(ERROR, "Incomplete write, increase buffer size"); - ble_disconnect(connHandle_, nullptr); - connHandle_ = BLE_INVALID_CONN_HANDLE; - } - LOG_DEBUG(TRACE, "%u bytes received", (unsigned)event.size); + DEBUG("Received BLE packet"); + DEBUG_DUMP(event.data, event.size); + Buffer* buf = nullptr; + CHECK(allocPooledBuffer(event.size, &buf)); + memcpy(buf->data, event.data, event.size); + inBufs_.pushBack(buf); } + return 0; } int BleControlRequestChannel::initProfile() { // Register base UUID uint8_t uuidType = 0; - int ret = ble_add_base_uuid((const char*)BASE_UUID, &uuidType, nullptr); - if (ret != 0) { - return ret; - } + CHECK(ble_add_base_uuid((const char*)BASE_UUID, &uuidType, nullptr)); // Characteristics ble_char chars[3] = {}; // Protocol version @@ -372,117 +1124,209 @@ int BleControlRequestChannel::initProfile() { sendChar.uuid.type = uuidType; sendChar.uuid.uuid = SEND_CHAR_UUID; sendChar.type = BLE_CHAR_TYPE_TX; -#if PAIRING_ENABLED - sendChar.flags = BLE_CHAR_FLAG_REQUIRE_PAIRING; -#endif // RX ble_char& recvChar = chars[2]; recvChar.uuid.type = uuidType; recvChar.uuid.uuid = RECV_CHAR_UUID; recvChar.type = BLE_CHAR_TYPE_RX; -#if PAIRING_ENABLED - recvChar.flags = BLE_CHAR_FLAG_REQUIRE_PAIRING; -#endif // Services ble_service ctrlService = {}; ctrlService.uuid.type = uuidType; ctrlService.uuid.uuid = CTRL_SERVICE_UUID; ctrlService.chars = chars; ctrlService.char_count = sizeof(chars) / sizeof(chars[0]); + // Manufacturer-specific data + const uint16_t platformId = PLATFORM_ID; + ble_manuf_data manufData = {}; + manufData.company_id = COMPANY_ID; + manufData.data = (const char*)&platformId; + manufData.size = sizeof(platformId); // Initialize profile ble_profile profile = {}; profile.version = BLE_API_VERSION; char devName[32] = {}; - ret = get_device_name(devName, sizeof(devName)); - if (ret < 0) { - LOG(ERROR, "Unable to get device name"); - return ret; - } + CHECK(get_device_name(devName, sizeof(devName))); profile.device_name = devName; profile.services = &ctrlService; profile.service_count = 1; -#if PAIRING_ENABLED - profile.flags = BLE_PROFILE_FLAG_ENABLE_PAIRING; -#endif + profile.manuf_data = &manufData; profile.callback = processBleEvent; profile.user_data = this; - ret = ble_init_profile(&profile, nullptr); - if (ret != 0) { - return ret; - } + CHECK(ble_init_profile(&profile, nullptr)); sendCharHandle_ = sendChar.handle; recvCharHandle_ = recvChar.handle; return 0; } +#if BLE_CHANNEL_SECURITY_ENABLED +int BleControlRequestChannel::initAesCcm() { + SPARK_ASSERT(jpake_); + const auto secret = jpake_->secret(); + SPARK_ASSERT(secret); + aesCcm_.reset(new(std::nothrow) AesCcmCipher); + if (!aesCcm_) { + return SYSTEM_ERROR_NO_MEMORY; + } + // First AES_CCM_KEY_SIZE bytes of the shared secret are used as the session key for AES-CCM + // encryption, next two blocks of AES_CCM_FIXED_NONCE_SIZE bytes each are used as fixed parts + // of client and server nonces respectively + CHECK(aesCcm_->init(secret, secret + AES_CCM_KEY_SIZE, secret + AES_CCM_KEY_SIZE + AES_CCM_FIXED_NONCE_SIZE)); + return 0; +} + +int BleControlRequestChannel::initJpake() { + char secret[JPAKE_PASSPHRASE_SIZE] = {}; + static_assert(sizeof(secret) <= HAL_DEVICE_SECRET_SIZE, ""); + const int ret = hal_get_device_secret(secret, sizeof(secret), nullptr); + CHECK(ret); + if (ret < (int)sizeof(secret)) { // Sanity check + LOG_DEBUG(ERROR, "Invalid size of the device secret data"); + return SYSTEM_ERROR_INTERNAL; + } + jpake_.reset(new(std::nothrow) JpakeHandler(this)); + if (!jpake_) { + return SYSTEM_ERROR_NO_MEMORY; + } + CHECK(jpake_->init(secret, sizeof(secret))); + return 0; +} +#endif // BLE_CHANNEL_SECURITY_ENABLED + +int BleControlRequestChannel::allocRequest(size_t size, Request** req) { + std::unique_ptr r(new(std::nothrow) Request()); + if (!r) { + return SYSTEM_ERROR_NO_MEMORY; + } + r->reqBuf = new(std::nothrow) char[size + MESSAGE_HEADER_SIZE + REQUEST_HEADER_SIZE + MESSAGE_FOOTER_SIZE]; + if (!r->reqBuf) { + return SYSTEM_ERROR_NO_MEMORY; + } + if (size > 0) { + r->request_data = r->reqBuf + MESSAGE_HEADER_SIZE + REQUEST_HEADER_SIZE; + } + r->request_size = size; + r->connId = connId_; + r->channel = this; + *req = r.release(); // Transfer ownership +#if BLE_CHANNEL_DEBUG_ENABLED + const auto count = ++allocReqCount_; + DEBUG("Allocated a request object (count: %u)", (unsigned)count); +#endif + return 0; +} + +void BleControlRequestChannel::freeRequest(Request* req) { + if (req) { + freeBuffer(req->repBuf); + delete[] req->reqBuf; + delete req; +#if BLE_CHANNEL_DEBUG_ENABLED + const auto count = --allocReqCount_; + DEBUG("Freed a request object (count: %u)", (unsigned)count); +#endif + } +} + +int BleControlRequestChannel::reallocBuffer(size_t size, Buffer** buf) { + const auto b = reallocLinkedBuffer(*buf, size); + if (!b) { + return SYSTEM_ERROR_NO_MEMORY; + } + b->data = linkedBufferData(b); + b->size = size; +#if BLE_CHANNEL_DEBUG_ENABLED + if (!*buf) { + const auto count = ++heapBufCount_; + DEBUG("Allocated a buffer, size: %u (count: %u)", (unsigned)size, (unsigned)count); + } +#endif + *buf = b; + return 0; +} + +void BleControlRequestChannel::freeBuffer(Buffer* buf) { + if (buf) { + freeLinkedBuffer(buf); +#if BLE_CHANNEL_DEBUG_ENABLED + const auto count = --heapBufCount_; + DEBUG("Freed a buffer (count: %u)", (unsigned)count); +#endif + } +} + +int BleControlRequestChannel::allocPooledBuffer(size_t size, Buffer** buf) { + const auto b = allocLinkedBuffer(size, &pool_); + if (!b) { + return SYSTEM_ERROR_NO_MEMORY; + } + b->data = linkedBufferData(b); + b->size = size; + *buf = b; +#if BLE_CHANNEL_DEBUG_ENABLED + const auto count = ++poolBufCount_; + DEBUG("Allocated a pooled buffer, size: %u (count: %u)", (unsigned)size, (unsigned)count); +#endif + return 0; +} + +void BleControlRequestChannel::freePooledBuffer(Buffer* buf) { + if (buf) { + freeLinkedBuffer(buf, &pool_); +#if BLE_CHANNEL_DEBUG_ENABLED + const auto count = --poolBufCount_; + DEBUG("Freed a pooled buffer (count: %u)", (unsigned)count); +#endif + } +} + +// Note: This method is called from an ISR void BleControlRequestChannel::processBleEvent(int event, const void* eventData, void* userData) { - const auto that = (BleControlRequestChannel*)userData; + const auto ch = (BleControlRequestChannel*)userData; + int ret = 0; switch (event) { case BLE_EVENT_CONNECTED: { const auto d = (const ble_connected_event_data*)eventData; - that->connected(*d); + ret = ch->connected(*d); break; } case BLE_EVENT_DISCONNECTED: { const auto d = (const ble_disconnected_event_data*)eventData; - that->disconnected(*d); + ret = ch->disconnected(*d); break; } case BLE_EVENT_CONN_PARAM_CHANGED: { const auto d = (const ble_conn_param_changed_event_data*)eventData; - that->connParamChanged(*d); + ret = ch->connParamChanged(*d); break; } case BLE_EVENT_CHAR_PARAM_CHANGED: { const auto d = (const ble_char_param_changed_event_data*)eventData; - that->charParamChanged(*d); + ret = ch->charParamChanged(*d); break; } case BLE_EVENT_DATA_SENT: { const auto d = (const ble_data_sent_event_data*)eventData; - that->dataSent(*d); + ret = ch->dataSent(*d); break; } case BLE_EVENT_DATA_RECEIVED: { const auto d = (const ble_data_received_event_data*)eventData; - that->dataReceived(*d); + ret = ch->dataReceived(*d); break; } default: break; } -} - -BleControlRequestChannel::Request* BleControlRequestChannel::allocRequest(size_t size) { - const auto req = (Request*)malloc(sizeof(Request)); - if (!req) { - return nullptr; - } - memset(req, 0, sizeof(Request)); - if (size > 0) { - req->request_data = (char*)malloc(size); - if (!req->request_data) { - free(req); - return nullptr; + if (ret != 0) { + LOG(ERROR, "Failed to process BLE event: %d (error: %d)", event, ret); + if (ch->curConnHandle_ != BLE_INVALID_CONN_HANDLE) { + ble_disconnect(ch->curConnHandle_, nullptr); } - req->request_size = size; - } - return req; -} - -BleControlRequestChannel::Request* BleControlRequestChannel::freeRequest(Request* req) { - if (!req) { - return nullptr; } - const auto next = req->next; - free(req->request_data); - free(req->reply_data); - free(req); - return next; } } // particle::system } // particle -#endif // SYSTEM_CONTROL_ENABLED && BLE_ENABLED +#endif // SYSTEM_CONTROL_ENABLED && HAL_PLATFORM_BLE diff --git a/system/src/ble_control_request_channel.h b/system/src/ble_control_request_channel.h index 8de3c303e2..1189cda077 100644 --- a/system/src/ble_control_request_channel.h +++ b/system/src/ble_control_request_channel.h @@ -19,13 +19,30 @@ #include "system_control.h" -#if SYSTEM_CONTROL_ENABLED && BLE_ENABLED +#if SYSTEM_CONTROL_ENABLED && HAL_PLATFORM_BLE #include "control_request_handler.h" +#include "simple_pool_allocator.h" +#include "atomic_intrusive_queue.h" -#include "app_fifo.h" +#include "linked_buffer.h" + +#include "ble_hal.h" + +#include "spark_wiring_thread.h" #include +#include + +// Set this macro to 0 to disable the channel security +#ifndef BLE_CHANNEL_SECURITY_ENABLED +#define BLE_CHANNEL_SECURITY_ENABLED 1 +#endif + +// Set this macro to 1 to enable additional logging +#ifndef BLE_CHANNEL_DEBUG_ENABLED +#define BLE_CHANNEL_DEBUG_ENABLED 0 +#endif static_assert(BLE_MAX_PERIPH_CONN_COUNT == 1, "Concurrent peripheral connections are not supported"); @@ -42,62 +59,112 @@ class BleControlRequestChannel: public ControlRequestChannel { int init(); void destroy(); - // TODO: Use a separate thread for the BLE channel loop - int run(); + void run(); - // ControlRequestChannel + // Reimplemented from `ControlRequestChannel` + virtual int allocReplyData(ctrl_request* ctrlReq, size_t size) override; + virtual void freeRequestData(ctrl_request* ctrlReq) override; virtual void setResult(ctrl_request* ctrlReq, int result, ctrl_completion_handler_fn handler, void* data) override; private: + class HandshakeHandler; + class JpakeHandler; + class AesCcmCipher; + + struct Buffer: LinkedBuffer<> { + char* data; + size_t size; + }; + // Request data struct Request: ctrl_request { Request* next; // Next request + char* reqBuf; // Request buffer (assembled from multiple input buffers) + Buffer* repBuf; // Reply buffer (allocated as a single output buffer) int result; // Result code + unsigned connId; // Connection ID uint16_t id; // Request ID }; - Request* pendingReqs_; // Pending requests - Request* readyReqs_; // Completed requests - - Request* sendReq_; - std::unique_ptr sendBuf_; - size_t sendPos_; - bool headerSent_; +#if BLE_CHANNEL_DEBUG_ENABLED + std::atomic allocReqCount_; + std::atomic heapBufCount_; + std::atomic poolBufCount_; +#endif + IntrusiveQueue readyReqs_; // Completed requests + Mutex readyReqsLock_; + + AtomicIntrusiveQueue inBufs_; // Packets received from the client (input buffers) + IntrusiveQueue outBufs_; // Packets to be sent to the client (output buffers) + IntrusiveQueue readInBufs_; // "Consumed" input buffers (see read() function) + size_t inBufSize_; // Total size of all consumed input buffers + + Request* curReq_; // A request being received + size_t reqBufSize_; // Size of the request buffer + size_t reqBufOffs_; // Offset in the request buffer + + std::unique_ptr packetBuf_; // Intermediate buffer for BLE packet data + size_t packetSize_; // Size of the pending BLE packet +#if BLE_CHANNEL_SECURITY_ENABLED + std::unique_ptr aesCcm_; // AES cipher + std::unique_ptr jpake_; // J-PAKE handshake handler +#endif + AtomicAllocedPool pool_; // Pool allocator + + uint16_t connHandle_; // Connection handle used by the processing thread + volatile uint16_t curConnHandle_; // Current connection handle + + unsigned connId_; // Last connection ID known to the processing thread + std::atomic curConnId_; // Current connection ID + + volatile uint16_t maxPacketSize_; // Maximum number of bytes that can be written to the TX characteristic + volatile bool notifEnabled_; // Set to `true` if the client is subscribed to the notifications + volatile bool writable_; // Set to `true` if the TX characteristic is writable + + uint16_t sendCharHandle_; // TX characteristic handle + uint16_t recvCharHandle_; // RX characteristic handle + + int initChannel(); + void resetChannel(); + + int receiveRequest(); + int sendReply(); + int sendPacket(); + + bool readAll(char* data, size_t size); + size_t readSome(char* data, size_t size); + void sendBuffer(Buffer* buf); + + int connected(const ble_connected_event_data& event); + int disconnected(const ble_disconnected_event_data& event); + int connParamChanged(const ble_conn_param_changed_event_data& event); + int charParamChanged(const ble_char_param_changed_event_data& event); + int dataSent(const ble_data_sent_event_data& event); + int dataReceived(const ble_data_received_event_data& event); - Request* recvReq_; - std::unique_ptr recvBuf_; - app_fifo_t recvFifo_; - size_t recvPos_; - bool headerRecvd_; - - uint16_t sendCharHandle_; - uint16_t recvCharHandle_; - - volatile uint16_t connHandle_; - volatile uint16_t maxCharValSize_; - volatile bool notifEnabled_; - volatile bool writable_; + int initProfile(); +#if BLE_CHANNEL_SECURITY_ENABLED + int initAesCcm(); + int initJpake(); +#endif + int allocRequest(size_t size, Request** req); + void freeRequest(Request* req); - int sendNext(); - int receiveNext(); + int reallocBuffer(size_t size, Buffer** buf); + void freeBuffer(Buffer* buf); - void connected(const ble_connected_event_data& event); - void disconnected(const ble_disconnected_event_data& event); - void connParamChanged(const ble_conn_param_changed_event_data& event); - void charParamChanged(const ble_char_param_changed_event_data& event); - void dataSent(const ble_data_sent_event_data& event); - void dataReceived(const ble_data_received_event_data& event); - - int initProfile(); + int allocPooledBuffer(size_t size, Buffer** buf); + void freePooledBuffer(Buffer* buf); static void processBleEvent(int event, const void* eventData, void* userData); - - static Request* allocRequest(size_t size = 0); - static Request* freeRequest(Request* req); }; +inline void BleControlRequestChannel::sendBuffer(Buffer* buf) { + outBufs_.pushBack(buf); +} + } // particle::system } // particle -#endif // SYSTEM_CONTROL_ENABLED && BLE_ENABLED +#endif // SYSTEM_CONTROL_ENABLED && HAL_PLATFORM_BLE diff --git a/system/src/control/config.cpp b/system/src/control/config.cpp index fed28f0450..93f3ca3b22 100644 --- a/system/src/control/config.cpp +++ b/system/src/control/config.cpp @@ -78,6 +78,17 @@ int getSerialNumber(ctrl_request* req) { return 0; } +int getSystemVersion(ctrl_request* req) { + const auto verStr = PP_STR(SYSTEM_VERSION_STRING); + PB(GetSystemVersionReply) pbRep = {}; + EncodedString eVerStr(&pbRep.version, verStr, strlen(verStr)); + const int ret = encodeReplyMessage(req, PB_FIELDS(GetSystemVersionReply), &pbRep); + if (ret != 0) { + return ret; + } + return 0; +} + int handleSetClaimCodeRequest(ctrl_request* req) { particle_ctrl_SetClaimCodeRequest pbReq = {}; int ret = decodeRequestMessage(req, particle_ctrl_SetClaimCodeRequest_fields, &pbReq); diff --git a/system/src/control/config.h b/system/src/control/config.h index e7fc793da7..734862117e 100644 --- a/system/src/control/config.h +++ b/system/src/control/config.h @@ -25,6 +25,7 @@ namespace config { int getDeviceId(ctrl_request* req); int getSerialNumber(ctrl_request* req); +int getSystemVersion(ctrl_request* req); int handleSetClaimCodeRequest(ctrl_request* req); int handleIsClaimedRequest(ctrl_request* req); diff --git a/system/src/control/proto/config.pb.c b/system/src/control/proto/config.pb.c index 164d9f8e5c..56efdff914 100644 --- a/system/src/control/proto/config.pb.c +++ b/system/src/control/proto/config.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.9 at Thu Jun 21 22:40:14 2018. */ +/* Generated by nanopb-0.3.9 at Thu Jul 12 21:50:57 2018. */ #include "config.pb.h" @@ -28,6 +28,15 @@ const pb_field_t particle_ctrl_GetSerialNumberReply_fields[2] = { PB_LAST_FIELD }; +const pb_field_t particle_ctrl_GetSystemVersionRequest_fields[1] = { + PB_LAST_FIELD +}; + +const pb_field_t particle_ctrl_GetSystemVersionReply_fields[2] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, particle_ctrl_GetSystemVersionReply, version, version, 0), + PB_LAST_FIELD +}; + const pb_field_t particle_ctrl_SetClaimCodeRequest_fields[2] = { PB_FIELD( 1, STRING , SINGULAR, STATIC , FIRST, particle_ctrl_SetClaimCodeRequest, code, code, 0), PB_LAST_FIELD diff --git a/system/src/control/proto/config.pb.h b/system/src/control/proto/config.pb.h index 3717b84f24..450e2cb0ff 100644 --- a/system/src/control/proto/config.pb.h +++ b/system/src/control/proto/config.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.9 at Thu Jun 21 22:40:14 2018. */ +/* Generated by nanopb-0.3.9 at Thu Jul 12 21:50:57 2018. */ #ifndef PB_PARTICLE_CTRL_CONFIG_PB_H_INCLUDED #define PB_PARTICLE_CTRL_CONFIG_PB_H_INCLUDED @@ -62,6 +62,16 @@ typedef struct _particle_ctrl_GetServerProtocolRequest { /* @@protoc_insertion_point(struct:particle_ctrl_GetServerProtocolRequest) */ } particle_ctrl_GetServerProtocolRequest; +typedef struct _particle_ctrl_GetSystemVersionReply { + pb_callback_t version; +/* @@protoc_insertion_point(struct:particle_ctrl_GetSystemVersionReply) */ +} particle_ctrl_GetSystemVersionReply; + +typedef struct _particle_ctrl_GetSystemVersionRequest { + char dummy_field; +/* @@protoc_insertion_point(struct:particle_ctrl_GetSystemVersionRequest) */ +} particle_ctrl_GetSystemVersionRequest; + typedef struct _particle_ctrl_IsClaimedRequest { char dummy_field; /* @@protoc_insertion_point(struct:particle_ctrl_IsClaimedRequest) */ @@ -164,6 +174,8 @@ typedef struct _particle_ctrl_SetSoftApSsidRequest { #define particle_ctrl_GetDeviceIdReply_init_default {""} #define particle_ctrl_GetSerialNumberRequest_init_default {0} #define particle_ctrl_GetSerialNumberReply_init_default {""} +#define particle_ctrl_GetSystemVersionRequest_init_default {0} +#define particle_ctrl_GetSystemVersionReply_init_default {{{NULL}, NULL}} #define particle_ctrl_SetClaimCodeRequest_init_default {""} #define particle_ctrl_SetClaimCodeReply_init_default {0} #define particle_ctrl_IsClaimedRequest_init_default {0} @@ -186,6 +198,8 @@ typedef struct _particle_ctrl_SetSoftApSsidRequest { #define particle_ctrl_GetDeviceIdReply_init_zero {""} #define particle_ctrl_GetSerialNumberRequest_init_zero {0} #define particle_ctrl_GetSerialNumberReply_init_zero {""} +#define particle_ctrl_GetSystemVersionRequest_init_zero {0} +#define particle_ctrl_GetSystemVersionReply_init_zero {{{NULL}, NULL}} #define particle_ctrl_SetClaimCodeRequest_init_zero {""} #define particle_ctrl_SetClaimCodeReply_init_zero {0} #define particle_ctrl_IsClaimedRequest_init_zero {0} @@ -207,6 +221,7 @@ typedef struct _particle_ctrl_SetSoftApSsidRequest { /* Field tags (for use in manual encoding/decoding) */ #define particle_ctrl_GetSecurityKeyReply_data_tag 1 +#define particle_ctrl_GetSystemVersionReply_version_tag 1 #define particle_ctrl_GetDeviceIdReply_id_tag 1 #define particle_ctrl_GetSecurityKeyRequest_type_tag 1 #define particle_ctrl_GetSerialNumberReply_serial_tag 1 @@ -230,6 +245,8 @@ extern const pb_field_t particle_ctrl_GetDeviceIdRequest_fields[1]; extern const pb_field_t particle_ctrl_GetDeviceIdReply_fields[2]; extern const pb_field_t particle_ctrl_GetSerialNumberRequest_fields[1]; extern const pb_field_t particle_ctrl_GetSerialNumberReply_fields[2]; +extern const pb_field_t particle_ctrl_GetSystemVersionRequest_fields[1]; +extern const pb_field_t particle_ctrl_GetSystemVersionReply_fields[2]; extern const pb_field_t particle_ctrl_SetClaimCodeRequest_fields[2]; extern const pb_field_t particle_ctrl_SetClaimCodeReply_fields[1]; extern const pb_field_t particle_ctrl_IsClaimedRequest_fields[1]; @@ -254,6 +271,8 @@ extern const pb_field_t particle_ctrl_SetSoftApSsidReply_fields[1]; #define particle_ctrl_GetDeviceIdReply_size 26 #define particle_ctrl_GetSerialNumberRequest_size 0 #define particle_ctrl_GetSerialNumberReply_size 18 +#define particle_ctrl_GetSystemVersionRequest_size 0 +/* particle_ctrl_GetSystemVersionReply_size depends on runtime parameters */ #define particle_ctrl_SetClaimCodeRequest_size 66 #define particle_ctrl_SetClaimCodeReply_size 0 #define particle_ctrl_IsClaimedRequest_size 0 diff --git a/system/src/control/proto/network.pb.c b/system/src/control/proto/network.pb.c index dd20e2251e..e16264142f 100644 --- a/system/src/control/proto/network.pb.c +++ b/system/src/control/proto/network.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.9 at Tue Dec 5 16:20:09 2017. */ +/* Generated by nanopb-0.3.9 at Thu Jul 12 21:50:57 2018. */ #include "network.pb.h" diff --git a/system/src/control/proto/network.pb.h b/system/src/control/proto/network.pb.h index 515d41ef5c..3c176d8914 100644 --- a/system/src/control/proto/network.pb.h +++ b/system/src/control/proto/network.pb.h @@ -1,10 +1,12 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.9 at Tue Dec 5 16:20:09 2017. */ +/* Generated by nanopb-0.3.9 at Thu Jul 12 21:50:57 2018. */ #ifndef PB_PARTICLE_CTRL_NETWORK_PB_H_INCLUDED #define PB_PARTICLE_CTRL_NETWORK_PB_H_INCLUDED #include +#include "extensions.pb.h" + #include "common.pb.h" /* @@protoc_insertion_point(includes) */ diff --git a/system/src/control/proto/storage.pb.c b/system/src/control/proto/storage.pb.c index d02e3f77c6..963e9b21d0 100644 --- a/system/src/control/proto/storage.pb.c +++ b/system/src/control/proto/storage.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.9 at Fri Jan 12 21:30:44 2018. */ +/* Generated by nanopb-0.3.9 at Thu Jul 12 21:50:58 2018. */ #include "storage.pb.h" diff --git a/system/src/control/proto/storage.pb.h b/system/src/control/proto/storage.pb.h index 577679a4e7..c3aaf026ef 100644 --- a/system/src/control/proto/storage.pb.h +++ b/system/src/control/proto/storage.pb.h @@ -1,10 +1,12 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.9 at Fri Jan 12 21:30:44 2018. */ +/* Generated by nanopb-0.3.9 at Thu Jul 12 21:50:58 2018. */ #ifndef PB_PARTICLE_CTRL_STORAGE_PB_H_INCLUDED #define PB_PARTICLE_CTRL_STORAGE_PB_H_INCLUDED #include +#include "extensions.pb.h" + #include "common.pb.h" /* @@protoc_insertion_point(includes) */ diff --git a/system/src/control/proto/wifi.pb.c b/system/src/control/proto/wifi.pb.c index 2eb1af38a3..1e275b1ac1 100644 --- a/system/src/control/proto/wifi.pb.c +++ b/system/src/control/proto/wifi.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.9 at Tue Dec 5 16:20:09 2017. */ +/* Generated by nanopb-0.3.9 at Thu Jul 12 21:50:57 2018. */ #include "wifi.pb.h" diff --git a/system/src/control/proto/wifi.pb.h b/system/src/control/proto/wifi.pb.h index b5b8ef7161..96bf7ba2fa 100644 --- a/system/src/control/proto/wifi.pb.h +++ b/system/src/control/proto/wifi.pb.h @@ -1,10 +1,12 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.9 at Tue Dec 5 16:20:09 2017. */ +/* Generated by nanopb-0.3.9 at Thu Jul 12 21:50:57 2018. */ #ifndef PB_PARTICLE_CTRL_WIFI_PB_H_INCLUDED #define PB_PARTICLE_CTRL_WIFI_PB_H_INCLUDED #include +#include "extensions.pb.h" + #include "common.pb.h" /* @@protoc_insertion_point(includes) */ diff --git a/system/src/system_control_internal.cpp b/system/src/system_control_internal.cpp index c0a6bda388..267be0e4be 100644 --- a/system/src/system_control_internal.cpp +++ b/system/src/system_control_internal.cpp @@ -78,14 +78,14 @@ SystemControl::SystemControl() : #ifdef USB_VENDOR_REQUEST_ENABLE usbChannel_(this), #endif -#if BLE_ENABLED +#if HAL_PLATFORM_BLE bleChannel_(this), #endif appReqHandler_(nullptr) { } int SystemControl::init() { -#if BLE_ENABLED +#if HAL_PLATFORM_BLE const int ret = bleChannel_.init(); if (ret != 0) { return ret; @@ -94,11 +94,10 @@ int SystemControl::init() { return 0; } -int SystemControl::run() { -#if BLE_ENABLED - return bleChannel_.run(); +void SystemControl::run() { +#if HAL_PLATFORM_BLE + bleChannel_.run(); #endif - return 0; } void SystemControl::processRequest(ctrl_request* req, ControlRequestChannel* /* channel */) { @@ -111,6 +110,10 @@ void SystemControl::processRequest(ctrl_request* req, ControlRequestChannel* /* setResult(req, control::config::getSerialNumber(req)); break; } + case CTRL_REQUEST_SYSTEM_VERSION: { + setResult(req, control::config::getSystemVersion(req)); + break; + } case CTRL_REQUEST_RESET: { setResult(req, SYSTEM_ERROR_NONE, [](int result, void* data) { System.reset(); diff --git a/system/src/system_control_internal.h b/system/src/system_control_internal.h index b7db1df501..c0240d7f56 100644 --- a/system/src/system_control_internal.h +++ b/system/src/system_control_internal.h @@ -44,7 +44,7 @@ class SystemControl: public ControlRequestHandler { // TODO: Use a separate thread for the BLE channel loop int init(); - int run(); + void run(); // ControlRequestHandler virtual void processRequest(ctrl_request* req, ControlRequestChannel* channel) override; @@ -55,7 +55,7 @@ class SystemControl: public ControlRequestHandler { #ifdef USB_VENDOR_REQUEST_ENABLE UsbControlRequestChannel usbChannel_; #endif -#if BLE_ENABLED +#if HAL_PLATFORM_BLE BleControlRequestChannel bleChannel_; #endif ctrl_request_handler_fn appReqHandler_;