Explorar el Código

pull in service files from firmware

Eric Betts hace 10 meses
padre
commit
2d3bd07c0a
Se han modificado 5 ficheros con 504 adiciones y 0 borrados
  1. 12 0
      app_common.h
  2. 200 0
      app_conf.h
  3. 230 0
      seos_service.c
  4. 55 0
      seos_service.h
  5. 7 0
      seos_service_uuid.inc

+ 12 - 0
app_common.h

@@ -0,0 +1,12 @@
+#pragma once
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <core/common_defines.h>
+#include <interface/patterns/ble_thread/tl/tl.h>
+
+#include "app_conf.h"

+ 200 - 0
app_conf.h

@@ -0,0 +1,200 @@
+#pragma once
+
+#include <ble/core/ble_defs.h>
+
+#define CFG_TX_POWER (0x19) /* +0dBm */
+
+#define CFG_IDENTITY_ADDRESS GAP_PUBLIC_ADDR
+
+/**
+ * Define IO Authentication
+ */
+#define CFG_ENCRYPTION_KEY_SIZE_MAX (16)
+#define CFG_ENCRYPTION_KEY_SIZE_MIN (8)
+
+/**
+ * Define IO capabilities
+ */
+#define CFG_IO_CAPABILITY IO_CAP_DISPLAY_YES_NO
+
+/**
+ * Define Secure Connections Support
+ */
+#define CFG_SC_SUPPORT SC_PAIRING_OPTIONAL
+
+/**
+ * Define PHY
+ */
+#define ALL_PHYS_PREFERENCE 0x00
+#define RX_2M_PREFERRED     0x02
+#define TX_2M_PREFERRED     0x02
+#define TX_1M               0x01
+#define TX_2M               0x02
+#define RX_1M               0x01
+#define RX_2M               0x02
+
+/******************************************************************************
+ * BLE Stack
+ ******************************************************************************/
+/**
+ * Maximum number of simultaneous connections that the device will support.
+ * Valid values are from 1 to 8
+ */
+#define CFG_BLE_NUM_LINK 2
+
+/**
+ * Maximum number of Services that can be stored in the GATT database.
+ * Note that the GAP and GATT services are automatically added so this parameter should be 2 plus the number of user services
+ */
+#define CFG_BLE_NUM_GATT_SERVICES 8
+
+/**
+ * Maximum number of Attributes
+ * (i.e. the number of characteristic + the number of characteristic values + the number of descriptors, excluding the services)
+ * that can be stored in the GATT database.
+ * Note that certain characteristics and relative descriptors are added automatically during device initialization
+ * so this parameters should be 9 plus the number of user Attributes
+ */
+#define CFG_BLE_NUM_GATT_ATTRIBUTES 68
+
+/**
+ * Maximum supported ATT_MTU size
+ */
+#define CFG_BLE_MAX_ATT_MTU (256 + 128 + 16 + 8 + 4 + 2)
+
+/**
+ * Size of the storage area for Attribute values
+ *  This value depends on the number of attributes used by application. In particular the sum of the following quantities (in octets) should be made for each attribute:
+ *  - attribute value length
+ *  - 5, if UUID is 16 bit; 19, if UUID is 128 bit
+ *  - 2, if server configuration descriptor is used
+ *  - 2*DTM_NUM_LINK, if client configuration descriptor is used
+ *  - 2, if extended properties is used
+ *  The total amount of memory needed is the sum of the above quantities for each attribute.
+ */
+#define CFG_BLE_ATT_VALUE_ARRAY_SIZE (1344)
+
+/**
+ * Prepare Write List size in terms of number of packet
+ */
+#define CFG_BLE_PREPARE_WRITE_LIST_SIZE BLE_PREP_WRITE_X_ATT(CFG_BLE_MAX_ATT_MTU)
+
+/**
+ * Number of allocated memory blocks
+ */
+#define CFG_BLE_MBLOCK_COUNT \
+    (BLE_MBLOCKS_CALC(CFG_BLE_PREPARE_WRITE_LIST_SIZE, CFG_BLE_MAX_ATT_MTU, CFG_BLE_NUM_LINK))
+
+/**
+ * Enable or disable the Extended Packet length feature. Valid values are 0 or 1.
+ */
+#define CFG_BLE_DATA_LENGTH_EXTENSION 1
+
+/**
+ * Sleep clock accuracy in Slave mode (ppm value)
+ */
+#define CFG_BLE_SLAVE_SCA 500
+
+/**
+ * Sleep clock accuracy in Master mode
+ * 0 : 251 ppm to 500 ppm
+ * 1 : 151 ppm to 250 ppm
+ * 2 : 101 ppm to 150 ppm
+ * 3 : 76 ppm to 100 ppm
+ * 4 : 51 ppm to 75 ppm
+ * 5 : 31 ppm to 50 ppm
+ * 6 : 21 ppm to 30 ppm
+ * 7 : 0 ppm to 20 ppm
+ */
+#define CFG_BLE_MASTER_SCA 0
+
+/**
+ *  Source for the low speed clock for RF wake-up
+ *  1 : external high speed crystal HSE/32/32
+ *  0 : external low speed crystal ( no calibration )
+ */
+#define CFG_BLE_LSE_SOURCE                                                        \
+    SHCI_C2_BLE_INIT_CFG_BLE_LS_CLK_LSE | SHCI_C2_BLE_INIT_CFG_BLE_LS_OTHER_DEV | \
+        SHCI_C2_BLE_INIT_CFG_BLE_LS_CALIB
+
+/**
+ * Start up time of the high speed (16 or 32 MHz) crystal oscillator in units of 625/256 us (~2.44 us)
+ */
+#define CFG_BLE_HSE_STARTUP_TIME 0x148
+
+/**
+ * Maximum duration of the connection event when the device is in Slave mode in units of 625/256 us (~2.44 us)
+ */
+#define CFG_BLE_MAX_CONN_EVENT_LENGTH (0xFFFFFFFF)
+
+/**
+ * Viterbi Mode
+ * 1 : enabled
+ * 0 : disabled
+ */
+#define CFG_BLE_VITERBI_MODE 1
+
+/**
+ * BLE stack Options flags to be configured with:
+ * - SHCI_C2_BLE_INIT_OPTIONS_LL_ONLY
+ * - SHCI_C2_BLE_INIT_OPTIONS_LL_HOST
+ * - SHCI_C2_BLE_INIT_OPTIONS_NO_SVC_CHANGE_DESC
+ * - SHCI_C2_BLE_INIT_OPTIONS_WITH_SVC_CHANGE_DESC
+ * - SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RO
+ * - SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RW
+ * - SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV
+ * - SHCI_C2_BLE_INIT_OPTIONS_NO_EXT_ADV
+ * - SHCI_C2_BLE_INIT_OPTIONS_CS_ALGO2
+ * - SHCI_C2_BLE_INIT_OPTIONS_NO_CS_ALGO2
+ * - SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_1
+ * - SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_2_3
+ * which are used to set following configuration bits:
+ * (bit 0): 1: LL only
+ *          0: LL + host
+ * (bit 1): 1: no service change desc.
+ *          0: with service change desc.
+ * (bit 2): 1: device name Read-Only
+ *          0: device name R/W
+ * (bit 3): 1: extended advertizing supported       [NOT SUPPORTED]
+ *          0: extended advertizing not supported   [NOT SUPPORTED]
+ * (bit 4): 1: CS Algo #2 supported
+ *          0: CS Algo #2 not supported
+ * (bit 7): 1: LE Power Class 1
+ *          0: LE Power Class 2-3
+ * other bits: reserved (shall be set to 0)
+ */
+#define CFG_BLE_OPTIONS                                                                 \
+    (SHCI_C2_BLE_INIT_OPTIONS_LL_HOST | SHCI_C2_BLE_INIT_OPTIONS_WITH_SVC_CHANGE_DESC | \
+     SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RO | SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV |       \
+     SHCI_C2_BLE_INIT_OPTIONS_CS_ALGO2 | SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_2_3)
+
+/**
+ * Queue length of BLE Event
+ * This parameter defines the number of asynchronous events that can be stored in the HCI layer before
+ * being reported to the application. When a command is sent to the BLE core coprocessor, the HCI layer
+ * is waiting for the event with the Num_HCI_Command_Packets set to 1. The receive queue shall be large
+ * enough to store all asynchronous events received in between.
+ * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events
+ * between the HCI command and its event.
+ * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is to small,
+ * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting
+ * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate
+ * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout).
+ */
+#define CFG_TLBLE_EVT_QUEUE_LENGTH 5
+/**
+ * This parameter should be set to fit most events received by the HCI layer. It defines the buffer size of each element
+ * allocated in the queue of received events and can be used to optimize the amount of RAM allocated by the Memory Manager.
+ * It should not exceed 255 which is the maximum HCI packet payload size (a greater value is a lost of memory as it will
+ * never be used)
+ * With the current wireless firmware implementation, this parameter shall be kept to 255
+ *
+ */
+#define CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE \
+    255 /**< Set to 255 with the memory manager and the mailbox */
+
+#define TL_BLE_EVENT_FRAME_SIZE (TL_EVT_HDR_SIZE + CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE)
+
+/* Various defines for compatibility with -Wundef - thanks, ST */
+#define CFG_DEBUG_TRACE_FULL  0
+#define CFG_DEBUG_TRACE_LIGHT 0

+ 230 - 0
seos_service.c

@@ -0,0 +1,230 @@
+#include "seos_service.h"
+#include "app_common.h"
+#include <ble/ble.h>
+#include <furi_ble/event_dispatcher.h>
+#include <furi_ble/gatt.h>
+
+#include <furi.h>
+
+#include "seos_service_uuid.inc"
+#include <stdint.h>
+
+#define TAG "BtSeosSvc"
+
+typedef enum {
+    SeosSvcGattCharacteristicRx = 0,
+    SeosSvcGattCharacteristicTx,
+    SeosSvcGattCharacteristicFlowCtrl,
+    SeosSvcGattCharacteristicStatus,
+    SeosSvcGattCharacteristicCount,
+} SeosSvcGattCharacteristicId;
+
+static const BleGattCharacteristicParams ble_svc_seos_chars[SeosSvcGattCharacteristicCount] = {
+    [SeosSvcGattCharacteristicRx] =
+        {.name = "SEOS",
+         .data_prop_type = FlipperGattCharacteristicDataFixed,
+         .data.fixed.length = BLE_SVC_SEOS_DATA_LEN_MAX,
+         .uuid.Char_UUID_128 = BLE_SVC_SEOS_CHAR_UUID,
+         .uuid_type = UUID_TYPE_128,
+         .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_NOTIFY,
+         .security_permissions = ATTR_PERMISSION_NONE,
+         .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE,
+         .is_variable = CHAR_VALUE_LEN_VARIABLE},
+};
+
+struct BleServiceSeos {
+    uint16_t svc_handle;
+    BleGattCharacteristicInstance chars[SeosSvcGattCharacteristicCount];
+    FuriMutex* buff_size_mtx;
+    uint32_t buff_size;
+    uint16_t bytes_ready_to_receive;
+    SeosServiceEventCallback callback;
+    void* context;
+    GapSvcEventHandler* event_handler;
+};
+
+static BleEventAckStatus ble_svc_seos_event_handler(void* event, void* context) {
+    BleServiceSeos* seos_svc = (BleServiceSeos*)context;
+    BleEventAckStatus ret = BleEventNotAck;
+    hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data);
+    evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data;
+    aci_gatt_attribute_modified_event_rp0* attribute_modified;
+    if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
+        if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
+            attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data;
+            if(attribute_modified->Attr_Handle ==
+               seos_svc->chars[SeosSvcGattCharacteristicRx].handle + 2) {
+                // Descriptor handle
+                ret = BleEventAckFlowEnable;
+                FURI_LOG_D(TAG, "RX descriptor event");
+            } else if(
+                attribute_modified->Attr_Handle ==
+                seos_svc->chars[SeosSvcGattCharacteristicRx].handle + 1) {
+                FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length);
+                if(seos_svc->callback) {
+                    furi_check(
+                        furi_mutex_acquire(seos_svc->buff_size_mtx, FuriWaitForever) ==
+                        FuriStatusOk);
+                    if(attribute_modified->Attr_Data_Length > seos_svc->bytes_ready_to_receive) {
+                        FURI_LOG_W(
+                            TAG,
+                            "Received %d, while was ready to receive %d bytes. Can lead to buffer overflow!",
+                            attribute_modified->Attr_Data_Length,
+                            seos_svc->bytes_ready_to_receive);
+                    }
+                    seos_svc->bytes_ready_to_receive -= MIN(
+                        seos_svc->bytes_ready_to_receive, attribute_modified->Attr_Data_Length);
+                    SeosServiceEvent event = {
+                        .event = SeosServiceEventTypeDataReceived,
+                        .data = {
+                            .buffer = attribute_modified->Attr_Data,
+                            .size = attribute_modified->Attr_Data_Length,
+                        }};
+                    uint32_t buff_free_size = seos_svc->callback(event, seos_svc->context);
+                    FURI_LOG_D(TAG, "Available buff size: %ld", buff_free_size);
+                    furi_check(furi_mutex_release(seos_svc->buff_size_mtx) == FuriStatusOk);
+                }
+                ret = BleEventAckFlowEnable;
+            } else if(
+                attribute_modified->Attr_Handle ==
+                seos_svc->chars[SeosSvcGattCharacteristicStatus].handle + 1) {
+                bool* rpc_status = (bool*)attribute_modified->Attr_Data;
+                if(!*rpc_status) {
+                    if(seos_svc->callback) {
+                        SeosServiceEvent event = {
+                            .event = SeosServiceEventTypesBleResetRequest,
+                        };
+                        seos_svc->callback(event, seos_svc->context);
+                    }
+                }
+            }
+        } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
+            FURI_LOG_T(TAG, "Ack received");
+            if(seos_svc->callback) {
+                SeosServiceEvent event = {
+                    .event = SeosServiceEventTypeDataSent,
+                };
+                seos_svc->callback(event, seos_svc->context);
+            }
+            ret = BleEventAckFlowEnable;
+        }
+    }
+    return ret;
+}
+
+typedef enum {
+    SeosServiceRpcStatusNotActive = 0UL,
+    SeosServiceRpcStatusActive = 1UL,
+} SeosServiceRpcStatus;
+
+static void
+    ble_svc_seos_update_rpc_char(BleServiceSeos* seos_svc, SeosServiceRpcStatus status) {
+    ble_gatt_characteristic_update(
+        seos_svc->svc_handle, &seos_svc->chars[SeosSvcGattCharacteristicStatus], &status);
+}
+
+BleServiceSeos* ble_svc_seos_start(void) {
+    BleServiceSeos* seos_svc = malloc(sizeof(BleServiceSeos));
+
+    seos_svc->event_handler =
+        ble_event_dispatcher_register_svc_handler(ble_svc_seos_event_handler, seos_svc);
+
+    if(!ble_gatt_service_add(
+           UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &seos_svc->svc_handle)) {
+        free(seos_svc);
+        return NULL;
+    }
+    for(uint8_t i = 0; i < SeosSvcGattCharacteristicCount; i++) {
+        ble_gatt_characteristic_init(
+            seos_svc->svc_handle, &ble_svc_seos_chars[i], &seos_svc->chars[i]);
+    }
+
+    ble_svc_seos_update_rpc_char(seos_svc, SeosServiceRpcStatusNotActive);
+    seos_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal);
+
+    return seos_svc;
+}
+
+void ble_svc_seos_set_callbacks(
+    BleServiceSeos* seos_svc,
+    uint16_t buff_size,
+    SeosServiceEventCallback callback,
+    void* context) {
+    furi_check(seos_svc);
+    seos_svc->callback = callback;
+    seos_svc->context = context;
+    seos_svc->buff_size = buff_size;
+    seos_svc->bytes_ready_to_receive = buff_size;
+
+    uint32_t buff_size_reversed = REVERSE_BYTES_U32(seos_svc->buff_size);
+    ble_gatt_characteristic_update(
+        seos_svc->svc_handle,
+        &seos_svc->chars[SeosSvcGattCharacteristicFlowCtrl],
+        &buff_size_reversed);
+}
+
+void ble_svc_seos_notify_buffer_is_empty(BleServiceSeos* seos_svc) {
+    furi_check(seos_svc);
+    furi_check(seos_svc->buff_size_mtx);
+
+    furi_check(furi_mutex_acquire(seos_svc->buff_size_mtx, FuriWaitForever) == FuriStatusOk);
+    if(seos_svc->bytes_ready_to_receive == 0) {
+        FURI_LOG_D(TAG, "Buffer is empty. Notifying client");
+        seos_svc->bytes_ready_to_receive = seos_svc->buff_size;
+
+        uint32_t buff_size_reversed = REVERSE_BYTES_U32(seos_svc->buff_size);
+        ble_gatt_characteristic_update(
+            seos_svc->svc_handle,
+            &seos_svc->chars[SeosSvcGattCharacteristicFlowCtrl],
+            &buff_size_reversed);
+    }
+    furi_check(furi_mutex_release(seos_svc->buff_size_mtx) == FuriStatusOk);
+}
+
+void ble_svc_seos_stop(BleServiceSeos* seos_svc) {
+    furi_check(seos_svc);
+
+    ble_event_dispatcher_unregister_svc_handler(seos_svc->event_handler);
+
+    for(uint8_t i = 0; i < SeosSvcGattCharacteristicCount; i++) {
+        ble_gatt_characteristic_delete(seos_svc->svc_handle, &seos_svc->chars[i]);
+    }
+    ble_gatt_service_delete(seos_svc->svc_handle);
+    furi_mutex_free(seos_svc->buff_size_mtx);
+    free(seos_svc);
+}
+
+bool ble_svc_seos_update_tx(BleServiceSeos* seos_svc, uint8_t* data, uint16_t data_len) {
+    if(data_len > BLE_SVC_SEOS_DATA_LEN_MAX) {
+        return false;
+    }
+
+    for(uint16_t remained = data_len; remained > 0;) {
+        uint8_t value_len = MIN(BLE_SVC_SEOS_CHAR_VALUE_LEN_MAX, remained);
+        uint16_t value_offset = data_len - remained;
+        remained -= value_len;
+
+        tBleStatus result = aci_gatt_update_char_value_ext(
+            0,
+            seos_svc->svc_handle,
+            seos_svc->chars[SeosSvcGattCharacteristicTx].handle,
+            remained ? 0x00 : 0x02,
+            data_len,
+            value_offset,
+            value_len,
+            data + value_offset);
+
+        if(result) {
+            FURI_LOG_E(TAG, "Failed updating TX characteristic: %d", result);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void ble_svc_seos_set_rpc_active(BleServiceSeos* seos_svc, bool active) {
+    furi_check(seos_svc);
+    ble_svc_seos_update_rpc_char(
+        seos_svc, active ? SeosServiceRpcStatusActive : SeosServiceRpcStatusNotActive);
+}

+ 55 - 0
seos_service.h

@@ -0,0 +1,55 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 
+ * Seos service. Implements RPC over BLE, with flow control.
+ */
+
+#define BLE_SVC_SEOS_DATA_LEN_MAX       (486)
+#define BLE_SVC_SEOS_CHAR_VALUE_LEN_MAX (243)
+
+typedef enum {
+    SeosServiceEventTypeDataReceived,
+    SeosServiceEventTypeDataSent,
+    SeosServiceEventTypesBleResetRequest,
+} SeosServiceEventType;
+
+typedef struct {
+    uint8_t* buffer;
+    uint16_t size;
+} SeosServiceData;
+
+typedef struct {
+    SeosServiceEventType event;
+    SeosServiceData data;
+} SeosServiceEvent;
+
+typedef uint16_t (*SeosServiceEventCallback)(SeosServiceEvent event, void* context);
+
+typedef struct BleServiceSeos BleServiceSeos;
+
+BleServiceSeos* ble_svc_seos_start(void);
+
+void ble_svc_seos_stop(BleServiceSeos* service);
+
+void ble_svc_seos_set_callbacks(
+    BleServiceSeos* service,
+    uint16_t buff_size,
+    SeosServiceEventCallback callback,
+    void* context);
+
+void ble_svc_seos_set_rpc_active(BleServiceSeos* service, bool active);
+
+void ble_svc_seos_notify_buffer_is_empty(BleServiceSeos* service);
+
+bool ble_svc_seos_update_tx(BleServiceSeos* service, uint8_t* data, uint16_t data_len);
+
+#ifdef __cplusplus
+}
+#endif

+ 7 - 0
seos_service_uuid.inc

@@ -0,0 +1,7 @@
+
+static const Service_UUID_t service_uuid = { .Service_UUID_128 = \
+    {0x02, 0x00, 0x00, 0x7a, 0x17, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00
+
+#define BLE_SVC_SEOS_CHAR_UUID      \
+    {0x02, 0x00, 0x00, 0x7a, 0x17, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00}
+