Naming and coding style convention, new linter tool. (#945)

* Makefile, Scripts: new linter
* About: remove ID from IC
* Firmware: remove double define for DIVC/DIVR
* Scripts: check folder names too. Docker: replace syntax check with make lint.
* Reformat Sources and Migrate to new file naming convention
* Docker: symlink clang-format-12 to clang-format
* Add coding style guide
This commit is contained in:
あく
2022-01-05 19:10:18 +03:00
committed by GitHub
parent c98e54da10
commit 389ff92cc1
899 changed files with 379245 additions and 373421 deletions

View File

@@ -0,0 +1,39 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : app_common.h
* Description : App Common application configuration file for STM32WPAN Middleware.
*
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef APP_COMMON_H
#define APP_COMMON_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <furi/common_defines.h>
#include "app_conf.h"
#endif

View File

@@ -0,0 +1,465 @@
#pragma once
#include "hw.h"
#include "hw_conf.h"
#include "hw_if.h"
#include "ble_bufsize.h"
#define CFG_TX_POWER (0x19) /* +0dBm */
/**
* Define Advertising parameters
*/
#define CFG_ADV_BD_ADDRESS (0x7257acd87a6c)
#define CFG_FAST_CONN_ADV_INTERVAL_MIN (0x80) /**< 80ms */
#define CFG_FAST_CONN_ADV_INTERVAL_MAX (0xa0) /**< 100ms */
#define CFG_LP_CONN_ADV_INTERVAL_MIN (0x640) /**< 1s */
#define CFG_LP_CONN_ADV_INTERVAL_MAX (0xfa0) /**< 2.5s */
/**
* Define IO Authentication
*/
#define CFG_BONDING_MODE (1)
#define CFG_FIXED_PIN (111111)
#define CFG_USED_FIXED_PIN (1)
#define CFG_ENCRYPTION_KEY_SIZE_MAX (16)
#define CFG_ENCRYPTION_KEY_SIZE_MIN (8)
/**
* Define IO capabilities
*/
#define CFG_IO_CAPABILITY_DISPLAY_ONLY (0x00)
#define CFG_IO_CAPABILITY_DISPLAY_YES_NO (0x01)
#define CFG_IO_CAPABILITY_KEYBOARD_ONLY (0x02)
#define CFG_IO_CAPABILITY_NO_INPUT_NO_OUTPUT (0x03)
#define CFG_IO_CAPABILITY_KEYBOARD_DISPLAY (0x04)
#define CFG_IO_CAPABILITY CFG_IO_CAPABILITY_DISPLAY_YES_NO
/**
* Define MITM modes
*/
#define CFG_MITM_PROTECTION_NOT_REQUIRED (0x00)
#define CFG_MITM_PROTECTION_REQUIRED (0x01)
#define CFG_MITM_PROTECTION CFG_MITM_PROTECTION_REQUIRED
/**
* Define Secure Connections Support
*/
#define CFG_SECURE_NOT_SUPPORTED (0x00)
#define CFG_SECURE_OPTIONAL (0x01)
#define CFG_SECURE_MANDATORY (0x02)
#define CFG_SC_SUPPORT CFG_SECURE_OPTIONAL
/**
* Define Keypress Notification Support
*/
#define CFG_KEYPRESS_NOT_SUPPORTED (0x00)
#define CFG_KEYPRESS_SUPPORTED (0x01)
#define CFG_KEYPRESS_NOTIFICATION_SUPPORT CFG_KEYPRESS_NOT_SUPPORTED
/**
* Numeric Comparison Answers
*/
#define YES (0x01)
#define NO (0x00)
/**
* Device name configuration for Generic Access Service
*/
#define CFG_GAP_DEVICE_NAME "TEMPLATE"
#define CFG_GAP_DEVICE_NAME_LENGTH (8)
/**
* 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
/**
* Identity root key used to derive LTK and CSRK
*/
#define CFG_BLE_IRK \
{ \
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, \
0xf0 \
}
/**
* Encryption root key used to derive LTK and CSRK
*/
#define CFG_BLE_ERK \
{ \
0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, \
0x21 \
}
/* USER CODE BEGIN Generic_Parameters */
/**
* SMPS supply
* SMPS not used when Set to 0
* SMPS used when Set to 1
*/
#define CFG_USE_SMPS 1
/* USER CODE END Generic_Parameters */
/**< specific parameters */
/*****************************************************/
/**
* AD Element - Group B Feature
*/
/* LSB - Second Byte */
#define CFG_FEATURE_OTA_REBOOT (0x20)
/******************************************************************************
* 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 0
/**
* 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_RW | SHCI_C2_BLE_INIT_OPTIONS_NO_EXT_ADV | \
SHCI_C2_BLE_INIT_OPTIONS_NO_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)
/******************************************************************************
* UART interfaces
******************************************************************************/
/**
* Select UART interfaces
*/
#define CFG_DEBUG_TRACE_UART hw_uart1
#define CFG_CONSOLE_MENU 0
/******************************************************************************
* Low Power
******************************************************************************/
/**
* When set to 1, the low power mode is enable
* When set to 0, the device stays in RUN mode
*/
#define CFG_LPM_SUPPORTED 1
/******************************************************************************
* Timer Server
******************************************************************************/
/**
* CFG_RTC_WUCKSEL_DIVIDER: This sets the RTCCLK divider to the wakeup timer.
* The lower is the value, the better is the power consumption and the accuracy of the timerserver
* The higher is the value, the finest is the granularity
*
* CFG_RTC_ASYNCH_PRESCALER: This sets the asynchronous prescaler of the RTC. It should as high as possible ( to ouput
* clock as low as possible) but the output clock should be equal or higher frequency compare to the clock feeding
* the wakeup timer. A lower clock speed would impact the accuracy of the timer server.
*
* CFG_RTC_SYNCH_PRESCALER: This sets the synchronous prescaler of the RTC.
* When the 1Hz calendar clock is required, it shall be sets according to other settings
* When the 1Hz calendar clock is not needed, CFG_RTC_SYNCH_PRESCALER should be set to 0x7FFF (MAX VALUE)
*
* CFG_RTCCLK_DIVIDER_CONF:
* Shall be set to either 0,2,4,8,16
* When set to either 2,4,8,16, the 1Hhz calendar is supported
* When set to 0, the user sets its own configuration
*
* The following settings are computed with LSI as input to the RTC
*/
#define CFG_RTCCLK_DIVIDER_CONF 0
#if(CFG_RTCCLK_DIVIDER_CONF == 0)
/**
* Custom configuration
* It does not support 1Hz calendar
* It divides the RTC CLK by 16
*/
#define CFG_RTCCLK_DIV (16)
#define CFG_RTC_WUCKSEL_DIVIDER (0)
#define CFG_RTC_ASYNCH_PRESCALER (CFG_RTCCLK_DIV - 1)
#define CFG_RTC_SYNCH_PRESCALER (0x7FFF)
#else
#if(CFG_RTCCLK_DIVIDER_CONF == 2)
/**
* It divides the RTC CLK by 2
*/
#define CFG_RTC_WUCKSEL_DIVIDER (3)
#endif
#if(CFG_RTCCLK_DIVIDER_CONF == 4)
/**
* It divides the RTC CLK by 4
*/
#define CFG_RTC_WUCKSEL_DIVIDER (2)
#endif
#if(CFG_RTCCLK_DIVIDER_CONF == 8)
/**
* It divides the RTC CLK by 8
*/
#define CFG_RTC_WUCKSEL_DIVIDER (1)
#endif
#if(CFG_RTCCLK_DIVIDER_CONF == 16)
/**
* It divides the RTC CLK by 16
*/
#define CFG_RTC_WUCKSEL_DIVIDER (0)
#endif
#define CFG_RTCCLK_DIV CFG_RTCCLK_DIVIDER_CONF
#define CFG_RTC_ASYNCH_PRESCALER (CFG_RTCCLK_DIV - 1)
#define CFG_RTC_SYNCH_PRESCALER (DIVR(LSE_VALUE, (CFG_RTC_ASYNCH_PRESCALER + 1)) - 1)
#endif
/** tick timer value in us */
#define CFG_TS_TICK_VAL DIVR((CFG_RTCCLK_DIV * 1000000), LSE_VALUE)
typedef enum {
CFG_TIM_PROC_ID_ISR,
/* USER CODE BEGIN CFG_TimProcID_t */
/* USER CODE END CFG_TimProcID_t */
} CFG_TimProcID_t;
/******************************************************************************
* Debug
******************************************************************************/
/**
* When set, this resets some hw resources to set the device in the same state than the power up
* The FW resets only register that may prevent the FW to run properly
*
* This shall be set to 0 in a final product
*
*/
#define CFG_HW_RESET_BY_FW 0
/**
* keep debugger enabled while in any low power mode when set to 1
* should be set to 0 in production
*/
#define CFG_DEBUGGER_SUPPORTED 0
/**
* When set to 1, the traces are enabled in the BLE services
*/
#define CFG_DEBUG_BLE_TRACE 0
/**
* Enable or Disable traces in application
*/
#define CFG_DEBUG_APP_TRACE 0
#if(CFG_DEBUG_APP_TRACE != 0)
#define APP_DBG_MSG PRINT_MESG_DBG
#else
#define APP_DBG_MSG PRINT_NO_MESG
#endif
#if((CFG_DEBUG_BLE_TRACE != 0) || (CFG_DEBUG_APP_TRACE != 0))
#define CFG_DEBUG_TRACE 1
#endif
#if(CFG_DEBUG_TRACE != 0)
#undef CFG_LPM_SUPPORTED
#undef CFG_DEBUGGER_SUPPORTED
#define CFG_LPM_SUPPORTED 0
#define CFG_DEBUGGER_SUPPORTED 1
#endif
/**
* When CFG_DEBUG_TRACE_FULL is set to 1, the trace are output with the API name, the file name and the line number
* When CFG_DEBUG_TRACE_LIGHT is set to 1, only the debug message is output
*
* When both are set to 0, no trace are output
* When both are set to 1, CFG_DEBUG_TRACE_FULL is selected
*/
#define CFG_DEBUG_TRACE_LIGHT 0
#define CFG_DEBUG_TRACE_FULL 0
#if((CFG_DEBUG_TRACE != 0) && (CFG_DEBUG_TRACE_LIGHT == 0) && (CFG_DEBUG_TRACE_FULL == 0))
#undef CFG_DEBUG_TRACE_FULL
#undef CFG_DEBUG_TRACE_LIGHT
#define CFG_DEBUG_TRACE_FULL 0
#define CFG_DEBUG_TRACE_LIGHT 1
#endif
#if(CFG_DEBUG_TRACE == 0)
#undef CFG_DEBUG_TRACE_FULL
#undef CFG_DEBUG_TRACE_LIGHT
#define CFG_DEBUG_TRACE_FULL 0
#define CFG_DEBUG_TRACE_LIGHT 0
#endif
/**
* When not set, the traces is looping on sending the trace over UART
*/
#define DBG_TRACE_USE_CIRCULAR_QUEUE 0
/**
* max buffer Size to queue data traces and max data trace allowed.
* Only Used if DBG_TRACE_USE_CIRCULAR_QUEUE is defined
*/
#define DBG_TRACE_MSG_QUEUE_SIZE 4096
#define MAX_DBG_TRACE_MSG_SIZE 1024
#define CFG_OTP_BASE_ADDRESS OTP_AREA_BASE
#define CFG_OTP_END_ADRESS OTP_AREA_END_ADDR

View File

@@ -0,0 +1,357 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : app_debug.c
* Description : Debug capabilities source file for STM32WPAN Middleware
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "utilities_common.h"
#include "app_common.h"
#include "app_debug.h"
#include "shci.h"
#include "tl.h"
#include "dbg_trace.h"
#include <furi_hal.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef PACKED_STRUCT {
GPIO_TypeDef* port;
uint16_t pin;
uint8_t enable;
uint8_t reserved;
}
APPD_GpioConfig_t;
/* USER CODE END PTD */
/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define GPIO_NBR_OF_RF_SIGNALS 9
#define GPIO_CFG_NBR_OF_FEATURES 34
#define NBR_OF_TRACES_CONFIG_PARAMETERS 4
#define NBR_OF_GENERAL_CONFIG_PARAMETERS 4
/**
* THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT
*/
#define BLE_DTB_CFG 7
#define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN)
/* USER CODE END PD */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
PLACE_IN_SECTION("MB_MEM2")
ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0};
PLACE_IN_SECTION("MB_MEM2")
ALIGN(4)
static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}};
/**
* THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT
* It provides timing information on the CPU2 activity.
* All configuration of (port, pin) is supported for each features and can be selected by the user
* depending on the availability
*/
static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = {
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ISR - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_7, 1, 0}, /* BLE_STACK_TICK - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_CMD_PROCESS - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ACL_DATA_PROCESS - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* SYS_CMD_PROCESS - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* RNG_PROCESS - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVM_PROCESS - Set on Entry / Reset on Exit */
{GPIOB, LL_GPIO_PIN_3, 1, 0}, /* IPCC_GENERAL - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_CMD_RX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_EVT_TX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_ACL_DATA_RX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_SYS_CMD_RX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_SYS_EVT_TX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_CLI_CMD_RX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_OT_CMD_RX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_OT_ACK_TX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_CLI_ACK_TX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_MEM_MANAGER_RX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_6, 1, 0}, /* HARD_FAULT - Set on Entry / Reset on Exit */
/* From v1.1.1 */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */
/* From v1.2.0 */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */
{GPIOA, LL_GPIO_PIN_4, 1, 0}, /* PES_ACTIVITY - Set on Entry / Reset on Exit */
{GPIOB, LL_GPIO_PIN_2, 1, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */
/* From v1.3.0 */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_NO_DELAY - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_STACK_STORE_NVM_CB - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_WRITE_ONGOING - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_WRITE_COMPLETE - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_CLEANUP - Set on Entry / Reset on Exit */
/* From v1.4.0 */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_START - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_EOP - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_WRITE - Set on Entry / Reset on Exit */
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_ERASE - Set on Entry / Reset on Exit */
};
/**
* THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT
* This table is relevant only for BLE
* It provides timing information on BLE RF activity.
* New signals may be allocated at any location when requested by ST
* The GPIO allocated to each signal depend on the BLE_DTB_CFG value and cannot be changed
*/
#if(BLE_DTB_CFG == 7)
static const APPD_GpioConfig_t aRfConfigList[GPIO_NBR_OF_RF_SIGNALS] = {
{GPIOB, LL_GPIO_PIN_2, 0, 0}, /* DTB10 - Tx/Rx SPI */
{GPIOB, LL_GPIO_PIN_7, 0, 0}, /* DTB11 - Tx/Tx SPI Clk */
{GPIOA, LL_GPIO_PIN_8, 0, 0}, /* DTB12 - Tx/Rx Ready & SPI Select */
{GPIOA, LL_GPIO_PIN_9, 0, 0}, /* DTB13 - Tx/Rx Start */
{GPIOA, LL_GPIO_PIN_10, 0, 0}, /* DTB14 - FSM0 */
{GPIOA, LL_GPIO_PIN_11, 0, 0}, /* DTB15 - FSM1 */
{GPIOB, LL_GPIO_PIN_8, 0, 0}, /* DTB16 - FSM2 */
{GPIOB, LL_GPIO_PIN_11, 0, 0}, /* DTB17 - FSM3 */
{GPIOB, LL_GPIO_PIN_10, 0, 0}, /* DTB18 - FSM4 */
};
#endif
/* USER CODE END PV */
/* Global variables ----------------------------------------------------------*/
/* USER CODE BEGIN GV */
/* USER CODE END GV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
static void APPD_SetCPU2GpioConfig(void);
static void APPD_BleDtbCfg(void);
/* USER CODE END PFP */
/* Functions Definition ------------------------------------------------------*/
void APPD_Init(void) {
/* USER CODE BEGIN APPD_Init */
#if(CFG_DEBUGGER_SUPPORTED == 1)
/**
* Keep debugger enabled while in any low power mode
*/
HAL_DBGMCU_EnableDBGSleepMode();
HAL_DBGMCU_EnableDBGStopMode();
/***************** ENABLE DEBUGGER *************************************/
LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48);
#else
GPIO_InitTypeDef gpio_config = {0};
gpio_config.Pull = GPIO_NOPULL;
gpio_config.Mode = GPIO_MODE_ANALOG;
gpio_config.Pin = GPIO_PIN_15 | GPIO_PIN_14 | GPIO_PIN_13;
__HAL_RCC_GPIOA_CLK_ENABLE();
HAL_GPIO_Init(GPIOA, &gpio_config);
__HAL_RCC_GPIOA_CLK_DISABLE();
gpio_config.Pin = GPIO_PIN_4 | GPIO_PIN_3;
__HAL_RCC_GPIOB_CLK_ENABLE();
HAL_GPIO_Init(GPIOB, &gpio_config);
__HAL_RCC_GPIOB_CLK_DISABLE();
HAL_DBGMCU_DisableDBGSleepMode();
HAL_DBGMCU_DisableDBGStopMode();
HAL_DBGMCU_DisableDBGStandbyMode();
#endif /* (CFG_DEBUGGER_SUPPORTED == 1) */
#if(CFG_DEBUG_TRACE != 0)
DbgTraceInit();
#endif
APPD_SetCPU2GpioConfig();
APPD_BleDtbCfg();
/* USER CODE END APPD_Init */
return;
}
void APPD_EnableCPU2(void) {
/* USER CODE BEGIN APPD_EnableCPU2 */
SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = {
{{0, 0, 0}}, /**< Does not need to be initialized */
{(uint8_t*)aGpioConfigList,
(uint8_t*)&APPD_TracesConfig,
(uint8_t*)&APPD_GeneralConfig,
GPIO_CFG_NBR_OF_FEATURES,
NBR_OF_TRACES_CONFIG_PARAMETERS,
NBR_OF_GENERAL_CONFIG_PARAMETERS}};
/**< Traces channel initialization */
TL_TRACES_Init();
/** GPIO DEBUG Initialization */
SHCI_C2_DEBUG_Init(&DebugCmdPacket);
// GPIO_InitTypeDef GPIO_InitStruct;
// GPIO_InitStruct.Pull = GPIO_NOPULL;
// GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
// GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
// GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
// HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
// SHCI_C2_ExtpaConfig((uint32_t)GPIOC, LL_GPIO_PIN_3, EXT_PA_ENABLED_LOW, EXT_PA_ENABLED);
/* USER CODE END APPD_EnableCPU2 */
return;
}
/*************************************************************
*
* LOCAL FUNCTIONS
*
*************************************************************/
static void APPD_SetCPU2GpioConfig(void) {
/* USER CODE BEGIN APPD_SetCPU2GpioConfig */
GPIO_InitTypeDef gpio_config = {0};
uint8_t local_loop;
uint16_t gpioa_pin_list;
uint16_t gpiob_pin_list;
uint16_t gpioc_pin_list;
gpioa_pin_list = 0;
gpiob_pin_list = 0;
gpioc_pin_list = 0;
for(local_loop = 0; local_loop < GPIO_CFG_NBR_OF_FEATURES; local_loop++) {
if(aGpioConfigList[local_loop].enable != 0) {
switch((uint32_t)aGpioConfigList[local_loop].port) {
case(uint32_t)GPIOA:
gpioa_pin_list |= aGpioConfigList[local_loop].pin;
break;
case(uint32_t)GPIOB:
gpiob_pin_list |= aGpioConfigList[local_loop].pin;
break;
case(uint32_t)GPIOC:
gpioc_pin_list |= aGpioConfigList[local_loop].pin;
break;
default:
break;
}
}
}
gpio_config.Pull = GPIO_NOPULL;
gpio_config.Mode = GPIO_MODE_OUTPUT_PP;
gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
if(gpioa_pin_list != 0) {
gpio_config.Pin = gpioa_pin_list;
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_C2GPIOA_CLK_ENABLE();
HAL_GPIO_Init(GPIOA, &gpio_config);
HAL_GPIO_WritePin(GPIOA, gpioa_pin_list, GPIO_PIN_RESET);
}
if(gpiob_pin_list != 0) {
gpio_config.Pin = gpiob_pin_list;
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_C2GPIOB_CLK_ENABLE();
HAL_GPIO_Init(GPIOB, &gpio_config);
HAL_GPIO_WritePin(GPIOB, gpiob_pin_list, GPIO_PIN_RESET);
}
if(gpioc_pin_list != 0) {
gpio_config.Pin = gpioc_pin_list;
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_C2GPIOC_CLK_ENABLE();
HAL_GPIO_Init(GPIOC, &gpio_config);
HAL_GPIO_WritePin(GPIOC, gpioc_pin_list, GPIO_PIN_RESET);
}
/* USER CODE END APPD_SetCPU2GpioConfig */
return;
}
static void APPD_BleDtbCfg(void) {
/* USER CODE BEGIN APPD_BleDtbCfg */
#if(BLE_DTB_CFG != 0)
GPIO_InitTypeDef gpio_config = {0};
uint8_t local_loop;
uint16_t gpioa_pin_list;
uint16_t gpiob_pin_list;
gpioa_pin_list = 0;
gpiob_pin_list = 0;
for(local_loop = 0; local_loop < GPIO_NBR_OF_RF_SIGNALS; local_loop++) {
if(aRfConfigList[local_loop].enable != 0) {
switch((uint32_t)aRfConfigList[local_loop].port) {
case(uint32_t)GPIOA:
gpioa_pin_list |= aRfConfigList[local_loop].pin;
break;
case(uint32_t)GPIOB:
gpiob_pin_list |= aRfConfigList[local_loop].pin;
break;
default:
break;
}
}
}
gpio_config.Pull = GPIO_NOPULL;
gpio_config.Mode = GPIO_MODE_AF_PP;
gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio_config.Alternate = GPIO_AF6_RF_DTB7;
if(gpioa_pin_list != 0) {
gpio_config.Pin = gpioa_pin_list;
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_C2GPIOA_CLK_ENABLE();
HAL_GPIO_Init(GPIOA, &gpio_config);
}
if(gpiob_pin_list != 0) {
gpio_config.Pin = gpiob_pin_list;
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_C2GPIOB_CLK_ENABLE();
HAL_GPIO_Init(GPIOB, &gpio_config);
}
#endif
/* USER CODE END APPD_BleDtbCfg */
return;
}
/*************************************************************
*
* WRAP FUNCTIONS
*
*************************************************************/
#if(CFG_DEBUG_TRACE != 0)
void DbgOutputInit(void) {
}
void DbgOutputTraces(uint8_t* p_data, uint16_t size, void (*cb)(void)) {
furi_hal_console_tx(p_data, size);
cb();
}
#endif
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,38 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : app_debug.h
* Description : Header for app_debug.c module
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __APP_DEBUG_H
#define __APP_DEBUG_H
#ifdef __cplusplus
extern "C" {
#endif
void APPD_Init(void);
void APPD_EnableCPU2(void);
#ifdef __cplusplus
}
#endif
#endif /*__APP_DEBUG_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,81 @@
#include "battery_service.h"
#include "app_common.h"
#include "ble.h"
#include <furi.h>
#define TAG "BtBatterySvc"
typedef struct {
uint16_t svc_handle;
uint16_t char_level_handle;
} BatterySvc;
static BatterySvc* battery_svc = NULL;
static const uint16_t service_uuid = BATTERY_SERVICE_UUID;
static const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID;
void battery_svc_start() {
battery_svc = furi_alloc(sizeof(BatterySvc));
tBleStatus status;
// Add Battery service
status = aci_gatt_add_service(
UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc->svc_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add Battery service: %d", status);
}
// Add Battery level characteristic
status = aci_gatt_add_char(
battery_svc->svc_handle,
UUID_TYPE_16,
(Char_UUID_t*)&char_battery_level_uuid,
1,
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&battery_svc->char_level_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status);
}
}
void battery_svc_stop() {
tBleStatus status;
if(battery_svc) {
// Delete Battery level characteristic
status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->char_level_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status);
}
// Delete Battery service
status = aci_gatt_del_service(battery_svc->svc_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Battery service: %d", status);
}
free(battery_svc);
battery_svc = NULL;
}
}
bool battery_svc_is_started() {
return battery_svc != NULL;
}
bool battery_svc_update_level(uint8_t battery_charge) {
// Check if service was started
if(battery_svc == NULL) {
return false;
}
// Update battery level characteristic
FURI_LOG_I(TAG, "Updating battery level characteristic");
tBleStatus result = aci_gatt_update_char_value(
battery_svc->svc_handle, battery_svc->char_level_handle, 0, 1, &battery_charge);
if(result) {
FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result);
}
return result != BLE_STATUS_SUCCESS;
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
void battery_svc_start();
void battery_svc_stop();
bool battery_svc_is_started();
bool battery_svc_update_level(uint8_t battery_level);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,185 @@
#include "ble_app.h"
#include "hci_tl.h"
#include "ble.h"
#include "shci.h"
#include "gap.h"
#include <furi_hal.h>
#define TAG "Bt"
#define BLE_APP_FLAG_HCI_EVENT (1UL << 0)
#define BLE_APP_FLAG_KILL_THREAD (1UL << 1)
#define BLE_APP_FLAG_ALL (BLE_APP_FLAG_HCI_EVENT | BLE_APP_FLAG_KILL_THREAD)
PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer;
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE];
_Static_assert(
sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 49,
"Ble stack config structure size mismatch");
typedef struct {
osMutexId_t hci_mtx;
osSemaphoreId_t hci_sem;
FuriThread* thread;
osEventFlagsId_t event_flags;
} BleApp;
static BleApp* ble_app = NULL;
static int32_t ble_app_hci_thread(void* context);
static void ble_app_hci_event_handler(void* pPayload);
static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status);
bool ble_app_init() {
SHCI_CmdStatus_t status;
ble_app = furi_alloc(sizeof(BleApp));
// Allocate semafore and mutex for ble command buffer access
ble_app->hci_mtx = osMutexNew(NULL);
ble_app->hci_sem = osSemaphoreNew(1, 0, NULL);
ble_app->event_flags = osEventFlagsNew(NULL);
// HCI transport layer thread to handle user asynch events
ble_app->thread = furi_thread_alloc();
furi_thread_set_name(ble_app->thread, "BleHciWorker");
furi_thread_set_stack_size(ble_app->thread, 1024);
furi_thread_set_context(ble_app->thread, ble_app);
furi_thread_set_callback(ble_app->thread, ble_app_hci_thread);
furi_thread_start(ble_app->thread);
// Initialize Ble Transport Layer
HCI_TL_HciInitConf_t hci_tl_config = {
.p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer,
.StatusNotCallBack = ble_app_hci_status_not_handler,
};
hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config);
// Configure NVM store for pairing data
SHCI_C2_CONFIG_Cmd_Param_t config_param = {
.PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE,
.Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM,
.BleNvmRamAddress = (uint32_t)ble_app_nvm,
.EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE,
};
status = SHCI_C2_Config(&config_param);
if(status) {
FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status);
}
// Start ble stack on 2nd core
SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
.Header = {{0, 0, 0}}, // Header unused
.Param = {
.pBleBufferAddress = 0, // pBleBufferAddress not used
.BleBufferSize = 0, // BleBufferSize not used
.NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES,
.NumAttrServ = CFG_BLE_NUM_GATT_SERVICES,
.AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE,
.NumOfLinks = CFG_BLE_NUM_LINK,
.ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION,
.PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE,
.MblockCount = CFG_BLE_MBLOCK_COUNT,
.AttMtu = CFG_BLE_MAX_ATT_MTU,
.SlaveSca = CFG_BLE_SLAVE_SCA,
.MasterSca = CFG_BLE_MASTER_SCA,
.LsSource = CFG_BLE_LSE_SOURCE,
.MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH,
.HsStartupTime = CFG_BLE_HSE_STARTUP_TIME,
.ViterbiEnable = CFG_BLE_VITERBI_MODE,
.Options = CFG_BLE_OPTIONS,
.HwVersion = 0,
.max_coc_initiator_nbr = 32,
.min_tx_power = 0,
.max_tx_power = 0,
.rx_model_config = 1,
}};
status = SHCI_C2_BLE_Init(&ble_init_cmd_packet);
if(status) {
FURI_LOG_E(TAG, "Failed to start ble stack: %d", status);
}
return status == SHCI_Success;
}
void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size) {
*addr = (uint8_t*)ble_app_nvm;
*size = sizeof(ble_app_nvm);
}
void ble_app_thread_stop() {
if(ble_app) {
osEventFlagsSet(ble_app->event_flags, BLE_APP_FLAG_KILL_THREAD);
furi_thread_join(ble_app->thread);
furi_thread_free(ble_app->thread);
// Wait to make sure that EventFlags delivers pending events before memory free
osDelay(50);
// Free resources
osMutexDelete(ble_app->hci_mtx);
osSemaphoreDelete(ble_app->hci_sem);
osEventFlagsDelete(ble_app->event_flags);
free(ble_app);
ble_app = NULL;
memset(&ble_app_cmd_buffer, 0, sizeof(ble_app_cmd_buffer));
}
}
static int32_t ble_app_hci_thread(void* arg) {
uint32_t flags = 0;
while(1) {
flags = osEventFlagsWait(
ble_app->event_flags, BLE_APP_FLAG_ALL, osFlagsWaitAny, osWaitForever);
if(flags & BLE_APP_FLAG_KILL_THREAD) {
break;
}
if(flags & BLE_APP_FLAG_HCI_EVENT) {
hci_user_evt_proc();
}
}
return 0;
}
// Called by WPAN lib
void hci_notify_asynch_evt(void* pdata) {
if(ble_app) {
osEventFlagsSet(ble_app->event_flags, BLE_APP_FLAG_HCI_EVENT);
}
}
void hci_cmd_resp_release(uint32_t flag) {
if(ble_app) {
osSemaphoreRelease(ble_app->hci_sem);
}
}
void hci_cmd_resp_wait(uint32_t timeout) {
if(ble_app) {
osSemaphoreAcquire(ble_app->hci_sem, osWaitForever);
}
}
static void ble_app_hci_event_handler(void* pPayload) {
SVCCTL_UserEvtFlowStatus_t svctl_return_status;
tHCI_UserEvtRxParam* pParam = (tHCI_UserEvtRxParam*)pPayload;
if(ble_app) {
svctl_return_status = SVCCTL_UserEvtRx((void*)&(pParam->pckt->evtserial));
if(svctl_return_status != SVCCTL_UserEvtFlowDisable) {
pParam->status = HCI_TL_UserEventFlow_Enable;
} else {
pParam->status = HCI_TL_UserEventFlow_Disable;
}
}
}
static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status) {
if(status == HCI_TL_CmdBusy) {
osMutexAcquire(ble_app->hci_mtx, osWaitForever);
} else if(status == HCI_TL_CmdAvailable) {
osMutexRelease(ble_app->hci_mtx);
}
}
void SVCCTL_ResumeUserEventFlow(void) {
hci_resume_flow();
}

View File

@@ -0,0 +1,16 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
bool ble_app_init();
void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size);
void ble_app_thread_stop();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,69 @@
/**
******************************************************************************
* File Name : App/ble_conf.h
* Description : Configuration file for BLE Middleware.
*
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef BLE_CONF_H
#define BLE_CONF_H
#include "app_conf.h"
/******************************************************************************
*
* BLE SERVICES CONFIGURATION
* blesvc
*
******************************************************************************/
/**
* This setting shall be set to '1' if the device needs to support the Peripheral Role
* In the MS configuration, both BLE_CFG_PERIPHERAL and BLE_CFG_CENTRAL shall be set to '1'
*/
#define BLE_CFG_PERIPHERAL 1
/**
* This setting shall be set to '1' if the device needs to support the Central Role
* In the MS configuration, both BLE_CFG_PERIPHERAL and BLE_CFG_CENTRAL shall be set to '1'
*/
#define BLE_CFG_CENTRAL 0
/**
* There is one handler per service enabled
* Note: There is no handler for the Device Information Service
*
* This shall take into account all registered handlers
* (from either the provided services or the custom services)
*/
#define BLE_CFG_SVC_MAX_NBR_CB 7
#define BLE_CFG_CLT_MAX_NBR_CB 0
/******************************************************************************
* GAP Service - Apprearance
******************************************************************************/
#define BLE_CFG_UNKNOWN_APPEARANCE (0)
#define BLE_CFG_GAP_APPEARANCE (0x0086)
/******************************************************************************
* Over The Air Feature (OTA) - STM Proprietary
******************************************************************************/
#define BLE_CFG_OTA_REBOOT_CHAR 0 /**< REBOOT OTA MODE CHARACTERISTIC */
#endif /*BLE_CONF_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,199 @@
/**
******************************************************************************
* File Name : App/ble_dbg_conf.h
* Description : Debug configuration file for BLE Middleware.
*
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __BLE_DBG_CONF_H
#define __BLE_DBG_CONF_H
/**
* Enable or Disable traces from BLE
*/
#define BLE_DBG_APP_EN 1
#define BLE_DBG_DIS_EN 1
#define BLE_DBG_HRS_EN 1
#define BLE_DBG_SVCCTL_EN 1
#define BLE_DBG_BLS_EN 1
#define BLE_DBG_HTS_EN 1
#define BLE_DBG_P2P_STM_EN 1
/**
* Macro definition
*/
#if(BLE_DBG_APP_EN != 0)
#define BLE_DBG_APP_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_APP_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_DIS_EN != 0)
#define BLE_DBG_DIS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_DIS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_HRS_EN != 0)
#define BLE_DBG_HRS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_HRS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_P2P_STM_EN != 0)
#define BLE_DBG_P2P_STM_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_P2P_STM_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_TEMPLATE_STM_EN != 0)
#define BLE_DBG_TEMPLATE_STM_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_TEMPLATE_STM_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_EDS_STM_EN != 0)
#define BLE_DBG_EDS_STM_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_EDS_STM_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_LBS_STM_EN != 0)
#define BLE_DBG_LBS_STM_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_LBS_STM_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_SVCCTL_EN != 0)
#define BLE_DBG_SVCCTL_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_SVCCTL_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_CTS_EN != 0)
#define BLE_DBG_CTS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_CTS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_HIDS_EN != 0)
#define BLE_DBG_HIDS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_HIDS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_PASS_EN != 0)
#define BLE_DBG_PASS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_PASS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_BLS_EN != 0)
#define BLE_DBG_BLS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_BLS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_HTS_EN != 0)
#define BLE_DBG_HTS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_HTS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_ANS_EN != 0)
#define BLE_DBG_ANS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_ANS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_ESS_EN != 0)
#define BLE_DBG_ESS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_ESS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_GLS_EN != 0)
#define BLE_DBG_GLS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_GLS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_BAS_EN != 0)
#define BLE_DBG_BAS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_BAS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_RTUS_EN != 0)
#define BLE_DBG_RTUS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_RTUS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_HPS_EN != 0)
#define BLE_DBG_HPS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_HPS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_TPS_EN != 0)
#define BLE_DBG_TPS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_TPS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_LLS_EN != 0)
#define BLE_DBG_LLS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_LLS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_IAS_EN != 0)
#define BLE_DBG_IAS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_IAS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_WSS_EN != 0)
#define BLE_DBG_WSS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_WSS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_LNS_EN != 0)
#define BLE_DBG_LNS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_LNS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_SCPS_EN != 0)
#define BLE_DBG_SCPS_MSG PRINT_MESG_DBG
#else
#define BLE_DBG_SCPS_MSG PRINT_NO_MESG
#endif
#if(BLE_DBG_DTS_EN != 0)
#define BLE_DBG_DTS_MSG PRINT_MESG_DBG
#define BLE_DBG_DTS_BUF PRINT_LOG_BUFF_DBG
#else
#define BLE_DBG_DTS_MSG PRINT_NO_MESG
#define BLE_DBG_DTS_BUF PRINT_NO_MESG
#endif
#endif /*__BLE_DBG_CONF_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,291 @@
#include "ble_glue.h"
#include "app_common.h"
#include "main.h"
#include "ble_app.h"
#include "ble.h"
#include "tl.h"
#include "shci.h"
#include "shci_tl.h"
#include "app_debug.h"
#include <furi_hal.h>
#define TAG "Core2"
#define BLE_GLUE_FLAG_SHCI_EVENT (1UL << 0)
#define BLE_GLUE_FLAG_KILL_THREAD (1UL << 1)
#define BLE_GLUE_FLAG_ALL (BLE_GLUE_FLAG_SHCI_EVENT | BLE_GLUE_FLAG_KILL_THREAD)
#define POOL_SIZE \
(CFG_TLBLE_EVT_QUEUE_LENGTH * 4U * \
DIVC((sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE), 4U))
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ble_glue_event_pool[POOL_SIZE];
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t ble_glue_system_cmd_buff;
PLACE_IN_SECTION("MB_MEM2")
ALIGN(4)
static uint8_t ble_glue_system_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255U];
PLACE_IN_SECTION("MB_MEM2")
ALIGN(4)
static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255];
typedef enum {
// Stage 1: core2 startup and FUS
BleGlueStatusStartup,
BleGlueStatusBroken,
BleGlueStatusFusStarted,
// Stage 2: radio stack
BleGlueStatusRadioStackStarted,
BleGlueStatusRadioStackMissing
} BleGlueStatus;
typedef struct {
osMutexId_t shci_mtx;
osSemaphoreId_t shci_sem;
osEventFlagsId_t event_flags;
FuriThread* thread;
BleGlueStatus status;
BleGlueKeyStorageChangedCallback callback;
void* context;
} BleGlue;
static BleGlue* ble_glue = NULL;
static int32_t ble_glue_shci_thread(void* argument);
static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status);
static void ble_glue_sys_user_event_callback(void* pPayload);
void ble_glue_set_key_storage_changed_callback(
BleGlueKeyStorageChangedCallback callback,
void* context) {
furi_assert(ble_glue);
furi_assert(callback);
ble_glue->callback = callback;
ble_glue->context = context;
}
void ble_glue_init() {
ble_glue = furi_alloc(sizeof(BleGlue));
ble_glue->status = BleGlueStatusStartup;
// Configure the system Power Mode
// Select HSI as system clock source after Wake Up from Stop mode
LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI);
/* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */
LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
furi_hal_power_insomnia_enter();
// APPD_Init();
// Initialize all transport layers
TL_MM_Config_t tl_mm_config;
SHCI_TL_HciInitConf_t SHci_Tl_Init_Conf;
// Reference table initialization
TL_Init();
ble_glue->shci_mtx = osMutexNew(NULL);
ble_glue->shci_sem = osSemaphoreNew(1, 0, NULL);
ble_glue->event_flags = osEventFlagsNew(NULL);
// FreeRTOS system task creation
ble_glue->thread = furi_thread_alloc();
furi_thread_set_name(ble_glue->thread, "BleShciWorker");
furi_thread_set_stack_size(ble_glue->thread, 1024);
furi_thread_set_context(ble_glue->thread, ble_glue);
furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread);
furi_thread_start(ble_glue->thread);
// System channel initialization
SHci_Tl_Init_Conf.p_cmdbuffer = (uint8_t*)&ble_glue_system_cmd_buff;
SHci_Tl_Init_Conf.StatusNotCallBack = ble_glue_sys_status_not_callback;
shci_init(ble_glue_sys_user_event_callback, (void*)&SHci_Tl_Init_Conf);
/**< Memory Manager channel initialization */
tl_mm_config.p_BleSpareEvtBuffer = ble_glue_ble_spare_event_buff;
tl_mm_config.p_SystemSpareEvtBuffer = ble_glue_system_spare_event_buff;
tl_mm_config.p_AsynchEvtPool = ble_glue_event_pool;
tl_mm_config.AsynchEvtPoolSize = POOL_SIZE;
TL_MM_Init(&tl_mm_config);
TL_Enable();
/*
* From now, the application is waiting for the ready event ( VS_HCI_C2_Ready )
* received on the system channel before starting the Stack
* This system event is received with ble_glue_sys_user_event_callback()
*/
}
bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info) {
bool ret = false;
size_t countdown = 1000;
while(countdown > 0) {
if(ble_glue->status == BleGlueStatusFusStarted) {
ret = true;
break;
}
countdown--;
osDelay(1);
}
if(ble_glue->status == BleGlueStatusFusStarted) {
SHCI_GetWirelessFwInfo(info);
} else {
FURI_LOG_E(TAG, "Failed to start FUS");
ble_glue->status = BleGlueStatusBroken;
}
furi_hal_power_insomnia_exit();
return ret;
}
bool ble_glue_start() {
furi_assert(ble_glue);
if(ble_glue->status != BleGlueStatusFusStarted) {
return false;
}
bool ret = false;
furi_hal_power_insomnia_enter();
if(ble_app_init()) {
FURI_LOG_I(TAG, "Radio stack started");
ble_glue->status = BleGlueStatusRadioStackStarted;
ret = true;
if(SHCI_C2_SetFlashActivityControl(FLASH_ACTIVITY_CONTROL_SEM7) == SHCI_Success) {
FURI_LOG_I(TAG, "Flash activity control switched to SEM7");
} else {
FURI_LOG_E(TAG, "Failed to switch flash activity control to SEM7");
}
} else {
FURI_LOG_E(TAG, "Radio stack startup failed");
ble_glue->status = BleGlueStatusRadioStackMissing;
ble_app_thread_stop();
}
furi_hal_power_insomnia_exit();
return ret;
}
bool ble_glue_is_alive() {
if(!ble_glue) {
return false;
}
return ble_glue->status >= BleGlueStatusFusStarted;
}
bool ble_glue_is_radio_stack_ready() {
if(!ble_glue) {
return false;
}
return ble_glue->status == BleGlueStatusRadioStackStarted;
}
static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status) {
switch(status) {
case SHCI_TL_CmdBusy:
osMutexAcquire(ble_glue->shci_mtx, osWaitForever);
break;
case SHCI_TL_CmdAvailable:
osMutexRelease(ble_glue->shci_mtx);
break;
default:
break;
}
}
/*
* The type of the payload for a system user event is tSHCI_UserEvtRxParam
* When the system event is both :
* - a ready event (subevtcode = SHCI_SUB_EVT_CODE_READY)
* - reported by the FUS (sysevt_ready_rsp == FUS_FW_RUNNING)
* The buffer shall not be released
* ( eg ((tSHCI_UserEvtRxParam*)pPayload)->status shall be set to SHCI_TL_UserEventFlow_Disable )
* When the status is not filled, the buffer is released by default
*/
static void ble_glue_sys_user_event_callback(void* pPayload) {
UNUSED(pPayload);
/* Traces channel initialization */
// APPD_EnableCPU2( );
TL_AsynchEvt_t* p_sys_event =
(TL_AsynchEvt_t*)(((tSHCI_UserEvtRxParam*)pPayload)->pckt->evtserial.evt.payload);
if(p_sys_event->subevtcode == SHCI_SUB_EVT_CODE_READY) {
FURI_LOG_I(TAG, "Fus started");
ble_glue->status = BleGlueStatusFusStarted;
furi_hal_power_insomnia_exit();
} else if(p_sys_event->subevtcode == SHCI_SUB_EVT_ERROR_NOTIF) {
FURI_LOG_E(TAG, "Error during initialization");
furi_hal_power_insomnia_exit();
} else if(p_sys_event->subevtcode == SHCI_SUB_EVT_BLE_NVM_RAM_UPDATE) {
SHCI_C2_BleNvmRamUpdate_Evt_t* p_sys_ble_nvm_ram_update_event =
(SHCI_C2_BleNvmRamUpdate_Evt_t*)p_sys_event->payload;
if(ble_glue->callback) {
ble_glue->callback(
(uint8_t*)p_sys_ble_nvm_ram_update_event->StartAddress,
p_sys_ble_nvm_ram_update_event->Size,
ble_glue->context);
}
}
}
static void ble_glue_clear_shared_memory() {
memset(ble_glue_event_pool, 0, sizeof(ble_glue_event_pool));
memset(&ble_glue_system_cmd_buff, 0, sizeof(ble_glue_system_cmd_buff));
memset(ble_glue_system_spare_event_buff, 0, sizeof(ble_glue_system_spare_event_buff));
memset(ble_glue_ble_spare_event_buff, 0, sizeof(ble_glue_ble_spare_event_buff));
}
void ble_glue_thread_stop() {
if(ble_glue) {
osEventFlagsSet(ble_glue->event_flags, BLE_GLUE_FLAG_KILL_THREAD);
furi_thread_join(ble_glue->thread);
furi_thread_free(ble_glue->thread);
// Wait to make sure that EventFlags delivers pending events before memory free
osDelay(50);
// Free resources
osMutexDelete(ble_glue->shci_mtx);
osSemaphoreDelete(ble_glue->shci_sem);
osEventFlagsDelete(ble_glue->event_flags);
ble_glue_clear_shared_memory();
free(ble_glue);
ble_glue = NULL;
}
}
// Wrap functions
static int32_t ble_glue_shci_thread(void* context) {
uint32_t flags = 0;
while(true) {
flags = osEventFlagsWait(
ble_glue->event_flags, BLE_GLUE_FLAG_ALL, osFlagsWaitAny, osWaitForever);
if(flags & BLE_GLUE_FLAG_SHCI_EVENT) {
shci_user_evt_proc();
}
if(flags & BLE_GLUE_FLAG_KILL_THREAD) {
break;
}
}
return 0;
}
void shci_notify_asynch_evt(void* pdata) {
UNUSED(pdata);
if(ble_glue) {
osEventFlagsSet(ble_glue->event_flags, BLE_GLUE_FLAG_SHCI_EVENT);
}
}
void shci_cmd_resp_release(uint32_t flag) {
UNUSED(flag);
if(ble_glue) {
osSemaphoreRelease(ble_glue->shci_sem);
}
}
void shci_cmd_resp_wait(uint32_t timeout) {
UNUSED(timeout);
if(ble_glue) {
osSemaphoreAcquire(ble_glue->shci_sem, osWaitForever);
}
}

View File

@@ -0,0 +1,50 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <shci/shci.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (
*BleGlueKeyStorageChangedCallback)(uint8_t* change_addr_start, uint16_t size, void* context);
/** Initialize start core2 and initialize transport */
void ble_glue_init();
/** Start Core2 Radio stack
*
* @return true on success
*/
bool ble_glue_start();
/** Is core2 alive and at least FUS is running
*
* @return true if core2 is alive
*/
bool ble_glue_is_alive();
bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info);
/** Is core2 radio stack present and ready
*
* @return true if present and ready
*/
bool ble_glue_is_radio_stack_ready();
/** Set callback for NVM in RAM changes
*
* @param[in] callback The callback to call on NVM change
* @param context The context for callback
*/
void ble_glue_set_key_storage_changed_callback(
BleGlueKeyStorageChangedCallback callback,
void* context);
void ble_glue_thread_stop();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,174 @@
#include "dev_info_service.h"
#include "app_common.h"
#include "ble.h"
#include <furi.h>
#define TAG "BtDevInfoSvc"
typedef struct {
uint16_t service_handle;
uint16_t man_name_char_handle;
uint16_t serial_num_char_handle;
uint16_t firmware_rev_char_handle;
uint16_t software_rev_char_handle;
} DevInfoSvc;
static DevInfoSvc* dev_info_svc = NULL;
static const char dev_info_man_name[] = "Flipper Devices Inc.";
static const char dev_info_serial_num[] = "1.0";
static const char dev_info_firmware_rev_num[] = TOSTRING(TARGET);
static const char dev_info_software_rev_num[] = GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM
" " BUILD_DATE;
void dev_info_svc_start() {
dev_info_svc = furi_alloc(sizeof(DevInfoSvc));
tBleStatus status;
// Add Device Information Service
uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID;
status = aci_gatt_add_service(
UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc->service_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status);
}
// Add characteristics
uuid = MANUFACTURER_NAME_UUID;
status = aci_gatt_add_char(
dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_man_name),
CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->man_name_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add manufacturer name char: %d", status);
}
uuid = SERIAL_NUMBER_UUID;
status = aci_gatt_add_char(
dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_serial_num),
CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->serial_num_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add serial number char: %d", status);
}
uuid = FIRMWARE_REVISION_UUID;
status = aci_gatt_add_char(
dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_firmware_rev_num),
CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->firmware_rev_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add firmware revision char: %d", status);
}
uuid = SOFTWARE_REVISION_UUID;
status = aci_gatt_add_char(
dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_software_rev_num),
CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->software_rev_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add software revision char: %d", status);
}
// Update characteristics
status = aci_gatt_update_char_value(
dev_info_svc->service_handle,
dev_info_svc->man_name_char_handle,
0,
strlen(dev_info_man_name),
(uint8_t*)dev_info_man_name);
if(status) {
FURI_LOG_E(TAG, "Failed to update manufacturer name char: %d", status);
}
status = aci_gatt_update_char_value(
dev_info_svc->service_handle,
dev_info_svc->serial_num_char_handle,
0,
strlen(dev_info_serial_num),
(uint8_t*)dev_info_serial_num);
if(status) {
FURI_LOG_E(TAG, "Failed to update serial number char: %d", status);
}
status = aci_gatt_update_char_value(
dev_info_svc->service_handle,
dev_info_svc->firmware_rev_char_handle,
0,
strlen(dev_info_firmware_rev_num),
(uint8_t*)dev_info_firmware_rev_num);
if(status) {
FURI_LOG_E(TAG, "Failed to update firmware revision char: %d", status);
}
status = aci_gatt_update_char_value(
dev_info_svc->service_handle,
dev_info_svc->software_rev_char_handle,
0,
strlen(dev_info_software_rev_num),
(uint8_t*)dev_info_software_rev_num);
if(status) {
FURI_LOG_E(TAG, "Failed to update software revision char: %d", status);
}
}
void dev_info_svc_stop() {
tBleStatus status;
if(dev_info_svc) {
// Delete service characteristics
status =
aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete manufacturer name char: %d", status);
}
status =
aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete serial number char: %d", status);
}
status = aci_gatt_del_char(
dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete firmware revision char: %d", status);
}
status = aci_gatt_del_char(
dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete software revision char: %d", status);
}
// Delete service
status = aci_gatt_del_service(dev_info_svc->service_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete device info service: %d", status);
}
free(dev_info_svc);
dev_info_svc = NULL;
}
}
bool dev_info_svc_is_started() {
return dev_info_svc != NULL;
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define DEV_INFO_MANUFACTURER_NAME "Flipper Devices Inc."
#define DEV_INFO_SERIAL_NUMBER "1.0"
#define DEV_INFO_FIRMWARE_REVISION_NUMBER TARGET
#define DEV_INFO_SOFTWARE_REVISION_NUMBER \
GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE
void dev_info_svc_start();
void dev_info_svc_stop();
bool dev_info_svc_is_started();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,549 @@
#include "gap.h"
#include "ble.h"
#include <furi_hal.h>
#include <furi.h>
#define TAG "BtGap"
#define FAST_ADV_TIMEOUT 30000
#define INITIAL_ADV_TIMEOUT 60000
typedef struct {
uint16_t gap_svc_handle;
uint16_t dev_name_char_handle;
uint16_t appearance_char_handle;
uint16_t connection_handle;
uint8_t adv_svc_uuid_len;
uint8_t adv_svc_uuid[20];
char* adv_name;
} GapSvc;
typedef struct {
GapSvc service;
GapConfig* config;
GapState state;
osMutexId_t state_mutex;
GapEventCallback on_event_cb;
void* context;
osTimerId_t advertise_timer;
FuriThread* thread;
osMessageQueueId_t command_queue;
bool enable_adv;
} Gap;
typedef enum {
GapCommandAdvFast,
GapCommandAdvLowPower,
GapCommandAdvStop,
GapCommandKillThread,
} GapCommand;
typedef struct {
GapScanCallback callback;
void* context;
} GapScan;
// Identity root key
static const uint8_t gap_irk[16] =
{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0};
// Encryption root key
static const uint8_t gap_erk[16] =
{0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21};
static Gap* gap = NULL;
static GapScan* gap_scan = NULL;
static void gap_advertise_start(GapState new_state);
static int32_t gap_app(void* context);
SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
hci_event_pckt* event_pckt;
evt_le_meta_event* meta_evt;
evt_blue_aci* blue_evt;
hci_le_phy_update_complete_event_rp0* evt_le_phy_update_complete;
uint8_t tx_phy;
uint8_t rx_phy;
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
event_pckt = (hci_event_pckt*)((hci_uart_pckt*)pckt)->data;
if(gap) {
osMutexAcquire(gap->state_mutex, osWaitForever);
}
switch(event_pckt->evt) {
case EVT_DISCONN_COMPLETE: {
hci_disconnection_complete_event_rp0* disconnection_complete_event =
(hci_disconnection_complete_event_rp0*)event_pckt->data;
if(disconnection_complete_event->Connection_Handle == gap->service.connection_handle) {
gap->service.connection_handle = 0;
gap->state = GapStateIdle;
FURI_LOG_I(
TAG, "Disconnect from client. Reason: %02X", disconnection_complete_event->Reason);
}
if(gap->enable_adv) {
// Restart advertising
gap_advertise_start(GapStateAdvFast);
furi_hal_power_insomnia_exit();
}
GapEvent event = {.type = GapEventTypeDisconnected};
gap->on_event_cb(event, gap->context);
} break;
case EVT_LE_META_EVENT:
meta_evt = (evt_le_meta_event*)event_pckt->data;
switch(meta_evt->subevent) {
case EVT_LE_CONN_UPDATE_COMPLETE:
FURI_LOG_D(TAG, "Connection update event");
break;
case EVT_LE_PHY_UPDATE_COMPLETE:
evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
if(evt_le_phy_update_complete->Status) {
FURI_LOG_E(
TAG, "Update PHY failed, status %d", evt_le_phy_update_complete->Status);
} else {
FURI_LOG_I(TAG, "Update PHY succeed");
}
ret = hci_le_read_phy(gap->service.connection_handle, &tx_phy, &rx_phy);
if(ret) {
FURI_LOG_E(TAG, "Read PHY failed, status: %d", ret);
} else {
FURI_LOG_I(TAG, "PHY Params TX = %d, RX = %d ", tx_phy, rx_phy);
}
break;
case EVT_LE_CONN_COMPLETE:
furi_hal_power_insomnia_enter();
hci_le_connection_complete_event_rp0* connection_complete_event =
(hci_le_connection_complete_event_rp0*)meta_evt->data;
FURI_LOG_I(
TAG,
"Connection complete for connection handle 0x%x",
connection_complete_event->Connection_Handle);
// Stop advertising as connection completed
osTimerStop(gap->advertise_timer);
// Update connection status and handle
gap->state = GapStateConnected;
gap->service.connection_handle = connection_complete_event->Connection_Handle;
// Start pairing by sending security request
aci_gap_slave_security_req(connection_complete_event->Connection_Handle);
break;
case EVT_LE_ADVERTISING_REPORT: {
if(gap_scan) {
GapAddress address;
hci_le_advertising_report_event_rp0* evt =
(hci_le_advertising_report_event_rp0*)meta_evt->data;
for(uint8_t i = 0; i < evt->Num_Reports; i++) {
Advertising_Report_t* rep = &evt->Advertising_Report[i];
address.type = rep->Address_Type;
// Original MAC addres is in inverted order
for(uint8_t j = 0; j < sizeof(address.mac); j++) {
address.mac[j] = rep->Address[sizeof(address.mac) - j - 1];
}
gap_scan->callback(address, gap_scan->context);
}
}
} break;
default:
break;
}
break;
case EVT_VENDOR:
blue_evt = (evt_blue_aci*)event_pckt->data;
switch(blue_evt->ecode) {
aci_gap_pairing_complete_event_rp0* pairing_complete;
case EVT_BLUE_GAP_LIMITED_DISCOVERABLE:
FURI_LOG_I(TAG, "Limited discoverable event");
break;
case EVT_BLUE_GAP_PASS_KEY_REQUEST: {
// Generate random PIN code
uint32_t pin = rand() % 999999;
aci_gap_pass_key_resp(gap->service.connection_handle, pin);
FURI_LOG_I(TAG, "Pass key request event. Pin: %06d", pin);
GapEvent event = {.type = GapEventTypePinCodeShow, .data.pin_code = pin};
gap->on_event_cb(event, gap->context);
} break;
case EVT_BLUE_ATT_EXCHANGE_MTU_RESP: {
aci_att_exchange_mtu_resp_event_rp0* pr = (void*)blue_evt->data;
FURI_LOG_I(TAG, "Rx MTU size: %d", pr->Server_RX_MTU);
// Set maximum packet size given header size is 3 bytes
GapEvent event = {
.type = GapEventTypeUpdateMTU, .data.max_packet_size = pr->Server_RX_MTU - 3};
gap->on_event_cb(event, gap->context);
} break;
case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
FURI_LOG_I(TAG, "Authorization request event");
break;
case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
FURI_LOG_I(TAG, "Slave security initiated");
break;
case EVT_BLUE_GAP_BOND_LOST:
FURI_LOG_I(TAG, "Bond lost event. Start rebonding");
aci_gap_allow_rebond(gap->service.connection_handle);
break;
case EVT_BLUE_GAP_DEVICE_FOUND:
FURI_LOG_I(TAG, "Device found event");
break;
case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
FURI_LOG_I(TAG, "Address not resolved event");
break;
case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION:
FURI_LOG_I(TAG, "Key press notification event");
break;
case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: {
uint32_t pin =
((aci_gap_numeric_comparison_value_event_rp0*)(blue_evt->data))->Numeric_Value;
FURI_LOG_I(TAG, "Verify numeric comparison: %06d", pin);
GapEvent event = {.type = GapEventTypePinCodeVerify, .data.pin_code = pin};
bool result = gap->on_event_cb(event, gap->context);
aci_gap_numeric_comparison_value_confirm_yesno(gap->service.connection_handle, result);
break;
}
case EVT_BLUE_GAP_PAIRING_CMPLT:
pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data;
if(pairing_complete->Status) {
FURI_LOG_E(
TAG,
"Pairing failed with status: %d. Terminating connection",
pairing_complete->Status);
aci_gap_terminate(gap->service.connection_handle, 5);
} else {
FURI_LOG_I(TAG, "Pairing complete");
GapEvent event = {.type = GapEventTypeConnected};
gap->on_event_cb(event, gap->context);
}
break;
case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
FURI_LOG_I(TAG, "Procedure complete event");
break;
}
default:
break;
}
if(gap) {
osMutexRelease(gap->state_mutex);
}
return SVCCTL_UserEvtFlowEnable;
}
static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) {
if(uid_len == 2) {
gap->service.adv_svc_uuid[0] = AD_TYPE_16_BIT_SERV_UUID;
} else if(uid_len == 4) {
gap->service.adv_svc_uuid[0] = AD_TYPE_32_BIT_SERV_UUID;
} else if(uid_len == 16) {
gap->service.adv_svc_uuid[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST;
}
memcpy(&gap->service.adv_svc_uuid[gap->service.adv_svc_uuid_len], uid, uid_len);
gap->service.adv_svc_uuid_len += uid_len;
}
static void gap_init_svc(Gap* gap) {
tBleStatus status;
uint32_t srd_bd_addr[2];
// HCI Reset to synchronise BLE Stack
hci_reset();
// Configure mac address
aci_hal_write_config_data(
CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, gap->config->mac_address);
/* Static random Address
* The two upper bits shall be set to 1
* The lowest 32bits is read from the UDN to differentiate between devices
* The RNG may be used to provide a random number on each power on
*/
srd_bd_addr[1] = 0x0000ED6E;
srd_bd_addr[0] = LL_FLASH_GetUDN();
aci_hal_write_config_data(
CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr);
// Set Identity root key used to derive LTK and CSRK
aci_hal_write_config_data(CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)gap_irk);
// Set Encryption root key used to derive LTK and CSRK
aci_hal_write_config_data(CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)gap_erk);
// Set TX Power to 0 dBm
aci_hal_set_tx_power_level(1, 0x19);
// Initialize GATT interface
aci_gatt_init();
// Initialize GAP interface
// Skip fist symbol AD_TYPE_COMPLETE_LOCAL_NAME
char* name = gap->service.adv_name + 1;
aci_gap_init(
GAP_PERIPHERAL_ROLE,
0,
strlen(name),
&gap->service.gap_svc_handle,
&gap->service.dev_name_char_handle,
&gap->service.appearance_char_handle);
// Set GAP characteristics
status = aci_gatt_update_char_value(
gap->service.gap_svc_handle,
gap->service.dev_name_char_handle,
0,
strlen(name),
(uint8_t*)name);
if(status) {
FURI_LOG_E(TAG, "Failed updating name characteristic: %d", status);
}
uint8_t gap_appearence_char_uuid[2] = {
gap->config->appearance_char & 0xff, gap->config->appearance_char >> 8};
status = aci_gatt_update_char_value(
gap->service.gap_svc_handle,
gap->service.appearance_char_handle,
0,
2,
gap_appearence_char_uuid);
if(status) {
FURI_LOG_E(TAG, "Failed updating appearence characteristic: %d", status);
}
// Set default PHY
hci_le_set_default_phy(ALL_PHYS_PREFERENCE, TX_2M_PREFERRED, RX_2M_PREFERRED);
// Set I/O capability
bool keypress_supported = false;
if(gap->config->pairing_method == GapPairingPinCodeShow) {
aci_gap_set_io_capability(IO_CAP_DISPLAY_ONLY);
} else if(gap->config->pairing_method == GapPairingPinCodeVerifyYesNo) {
aci_gap_set_io_capability(IO_CAP_DISPLAY_YES_NO);
keypress_supported = true;
}
// Setup authentication
aci_gap_set_authentication_requirement(
gap->config->bonding_mode,
CFG_MITM_PROTECTION,
CFG_SC_SUPPORT,
keypress_supported,
CFG_ENCRYPTION_KEY_SIZE_MIN,
CFG_ENCRYPTION_KEY_SIZE_MAX,
CFG_USED_FIXED_PIN,
0,
PUBLIC_ADDR);
// Configure whitelist
aci_gap_configure_whitelist();
}
static void gap_advertise_start(GapState new_state) {
tBleStatus status;
uint16_t min_interval;
uint16_t max_interval;
if(new_state == GapStateAdvFast) {
min_interval = 0x80; // 80 ms
max_interval = 0xa0; // 100 ms
} else {
min_interval = 0x0640; // 1 s
max_interval = 0x0fa0; // 2.5 s
}
// Stop advertising timer
osTimerStop(gap->advertise_timer);
if((new_state == GapStateAdvLowPower) &&
((gap->state == GapStateAdvFast) || (gap->state == GapStateAdvLowPower))) {
// Stop advertising
status = aci_gap_set_non_discoverable();
if(status) {
FURI_LOG_E(TAG, "Stop Advertising Failed, result: %d", status);
}
}
// Configure advertising
status = aci_gap_set_discoverable(
ADV_IND,
min_interval,
max_interval,
PUBLIC_ADDR,
0,
strlen(gap->service.adv_name),
(uint8_t*)gap->service.adv_name,
gap->service.adv_svc_uuid_len,
gap->service.adv_svc_uuid,
0,
0);
if(status) {
FURI_LOG_E(TAG, "Set discoverable err: %d", status);
}
gap->state = new_state;
GapEvent event = {.type = GapEventTypeStartAdvertising};
gap->on_event_cb(event, gap->context);
osTimerStart(gap->advertise_timer, INITIAL_ADV_TIMEOUT);
}
static void gap_advertise_stop() {
if(gap->state > GapStateIdle) {
if(gap->state == GapStateConnected) {
// Terminate connection
aci_gap_terminate(gap->service.connection_handle, 0x13);
}
// Stop advertising
osTimerStop(gap->advertise_timer);
aci_gap_set_non_discoverable();
gap->state = GapStateIdle;
}
GapEvent event = {.type = GapEventTypeStopAdvertising};
gap->on_event_cb(event, gap->context);
}
void gap_start_advertising() {
osMutexAcquire(gap->state_mutex, osWaitForever);
if(gap->state == GapStateIdle) {
gap->state = GapStateStartingAdv;
FURI_LOG_I(TAG, "Start advertising");
gap->enable_adv = true;
GapCommand command = GapCommandAdvFast;
furi_check(osMessageQueuePut(gap->command_queue, &command, 0, 0) == osOK);
}
osMutexRelease(gap->state_mutex);
}
void gap_stop_advertising() {
osMutexAcquire(gap->state_mutex, osWaitForever);
if(gap->state > GapStateIdle) {
FURI_LOG_I(TAG, "Stop advertising");
gap->enable_adv = false;
GapCommand command = GapCommandAdvStop;
furi_check(osMessageQueuePut(gap->command_queue, &command, 0, 0) == osOK);
}
osMutexRelease(gap->state_mutex);
}
static void gap_advetise_timer_callback(void* context) {
GapCommand command = GapCommandAdvLowPower;
furi_check(osMessageQueuePut(gap->command_queue, &command, 0, 0) == osOK);
}
bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) {
if(!ble_glue_is_radio_stack_ready()) {
return false;
}
gap = furi_alloc(sizeof(Gap));
gap->config = config;
srand(DWT->CYCCNT);
// Create advertising timer
gap->advertise_timer = osTimerNew(gap_advetise_timer_callback, osTimerOnce, NULL, NULL);
// Initialization of GATT & GAP layer
gap->service.adv_name = config->adv_name;
gap_init_svc(gap);
// Initialization of the BLE Services
SVCCTL_Init();
// Initialization of the GAP state
gap->state_mutex = osMutexNew(NULL);
gap->state = GapStateIdle;
gap->service.connection_handle = 0xFFFF;
gap->enable_adv = true;
// Thread configuration
gap->thread = furi_thread_alloc();
furi_thread_set_name(gap->thread, "BleGapWorker");
furi_thread_set_stack_size(gap->thread, 1024);
furi_thread_set_context(gap->thread, gap);
furi_thread_set_callback(gap->thread, gap_app);
furi_thread_start(gap->thread);
// Command queue allocation
gap->command_queue = osMessageQueueNew(8, sizeof(GapCommand), NULL);
uint8_t adv_service_uid[2];
gap->service.adv_svc_uuid_len = 1;
adv_service_uid[0] = gap->config->adv_service_uuid & 0xff;
adv_service_uid[1] = gap->config->adv_service_uuid >> 8;
set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid));
// Set callback
gap->on_event_cb = on_event_cb;
gap->context = context;
return true;
}
GapState gap_get_state() {
GapState state;
if(gap) {
osMutexAcquire(gap->state_mutex, osWaitForever);
state = gap->state;
osMutexRelease(gap->state_mutex);
} else {
state = GapStateUninitialized;
}
return state;
}
void gap_start_scan(GapScanCallback callback, void* context) {
furi_assert(callback);
gap_scan = furi_alloc(sizeof(GapScan));
gap_scan->callback = callback;
gap_scan->context = context;
// Scan interval 250 ms
hci_le_set_scan_parameters(1, 4000, 200, 0, 0);
hci_le_set_scan_enable(1, 1);
}
void gap_stop_scan() {
furi_assert(gap_scan);
hci_le_set_scan_enable(0, 1);
free(gap_scan);
gap_scan = NULL;
}
void gap_thread_stop() {
if(gap) {
osMutexAcquire(gap->state_mutex, osWaitForever);
gap->enable_adv = false;
GapCommand command = GapCommandKillThread;
osMessageQueuePut(gap->command_queue, &command, 0, osWaitForever);
osMutexRelease(gap->state_mutex);
furi_thread_join(gap->thread);
furi_thread_free(gap->thread);
// Free resources
osMutexDelete(gap->state_mutex);
osMessageQueueDelete(gap->command_queue);
osTimerStop(gap->advertise_timer);
while(xTimerIsTimerActive(gap->advertise_timer) == pdTRUE) osDelay(1);
furi_check(osTimerDelete(gap->advertise_timer) == osOK);
free(gap);
gap = NULL;
}
}
static int32_t gap_app(void* context) {
GapCommand command;
while(1) {
osStatus_t status = osMessageQueueGet(gap->command_queue, &command, NULL, osWaitForever);
if(status != osOK) {
FURI_LOG_E(TAG, "Message queue get error: %d", status);
continue;
}
osMutexAcquire(gap->state_mutex, osWaitForever);
if(command == GapCommandKillThread) {
break;
}
if(command == GapCommandAdvFast) {
gap_advertise_start(GapStateAdvFast);
} else if(command == GapCommandAdvLowPower) {
gap_advertise_start(GapStateAdvLowPower);
} else if(command == GapCommandAdvStop) {
gap_advertise_stop();
}
osMutexRelease(gap->state_mutex);
}
return 0;
}

View File

@@ -0,0 +1,83 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <furi_hal_version.h>
#define GAP_MAC_ADDR_SIZE (6)
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
GapEventTypeConnected,
GapEventTypeDisconnected,
GapEventTypeStartAdvertising,
GapEventTypeStopAdvertising,
GapEventTypePinCodeShow,
GapEventTypePinCodeVerify,
GapEventTypeUpdateMTU,
} GapEventType;
typedef union {
uint32_t pin_code;
uint16_t max_packet_size;
} GapEventData;
typedef struct {
GapEventType type;
GapEventData data;
} GapEvent;
typedef bool (*GapEventCallback)(GapEvent event, void* context);
typedef struct {
uint8_t type;
uint8_t mac[6];
} GapAddress;
typedef void (*GapScanCallback)(GapAddress address, void* context);
typedef enum {
GapStateUninitialized,
GapStateIdle,
GapStateStartingAdv,
GapStateAdvFast,
GapStateAdvLowPower,
GapStateConnected,
} GapState;
typedef enum {
GapPairingNone,
GapPairingPinCodeShow,
GapPairingPinCodeVerifyYesNo,
} GapPairing;
typedef struct {
uint16_t adv_service_uuid;
uint16_t appearance_char;
bool bonding_mode;
GapPairing pairing_method;
uint8_t mac_address[GAP_MAC_ADDR_SIZE];
char adv_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH];
} GapConfig;
bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context);
void gap_start_advertising();
void gap_stop_advertising();
GapState gap_get_state();
void gap_thread_stop();
void gap_start_scan(GapScanCallback callback, void* context);
void gap_stop_scan();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,260 @@
#include "hid_service.h"
#include "app_common.h"
#include "ble.h"
#include <furi.h>
#define TAG "BtHid"
typedef struct {
uint16_t svc_handle;
uint16_t protocol_mode_char_handle;
uint16_t report_char_handle;
uint16_t report_ref_desc_handle;
uint16_t report_map_char_handle;
uint16_t keyboard_boot_char_handle;
uint16_t info_char_handle;
uint16_t ctrl_point_char_handle;
} HIDSvc;
static HIDSvc* hid_svc = NULL;
static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) {
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
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) {
// Process modification events
ret = SVCCTL_EvtAckFlowEnable;
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
// Process notification confirmation
ret = SVCCTL_EvtAckFlowEnable;
}
}
return ret;
}
void hid_svc_start() {
tBleStatus status;
hid_svc = furi_alloc(sizeof(HIDSvc));
Service_UUID_t svc_uuid = {};
Char_Desc_Uuid_t desc_uuid = {};
Char_UUID_t char_uuid = {};
// Register event handler
SVCCTL_RegisterSvcHandler(hid_svc_event_handler);
// Add service
svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID;
status =
aci_gatt_add_service(UUID_TYPE_16, &svc_uuid, PRIMARY_SERVICE, 30, &hid_svc->svc_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add HID service: %d", status);
}
// Add Protocol mode characterstics
char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
1,
CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP,
ATTR_PERMISSION_NONE,
GATT_NOTIFY_ATTRIBUTE_WRITE,
10,
CHAR_VALUE_LEN_CONSTANT,
&hid_svc->protocol_mode_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add protocol mode characteristic: %d", status);
}
// Update Protocol mode characteristic
uint8_t protocol_mode = 1;
status = aci_gatt_update_char_value(
hid_svc->svc_handle, hid_svc->protocol_mode_char_handle, 0, 1, &protocol_mode);
if(status) {
FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status);
}
// Add Report characterstics
char_uuid.Char_UUID_16 = REPORT_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_REPORT_MAX_LEN,
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_VARIABLE,
&hid_svc->report_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status);
}
// Add Report descriptor
uint8_t desc_val[] = {0x00, 0x01};
desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID;
status = aci_gatt_add_char_desc(
hid_svc->svc_handle,
hid_svc->report_char_handle,
UUID_TYPE_16,
&desc_uuid,
HID_SVC_REPORT_REF_LEN,
HID_SVC_REPORT_REF_LEN,
desc_val,
ATTR_PERMISSION_NONE,
ATTR_ACCESS_READ_ONLY,
GATT_DONT_NOTIFY_EVENTS,
MIN_ENCRY_KEY_SIZE,
CHAR_VALUE_LEN_CONSTANT,
&hid_svc->report_ref_desc_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status);
}
// Add Report Map characteristic
char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_REPORT_MAP_MAX_LEN,
CHAR_PROP_READ,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_VARIABLE,
&hid_svc->report_map_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status);
}
// Add Boot Keyboard characteristic
char_uuid.Char_UUID_16 = BOOT_KEYBOARD_INPUT_REPORT_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_BOOT_KEYBOARD_INPUT_REPORT_MAX_LEN,
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_NONE,
GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,
10,
CHAR_VALUE_LEN_VARIABLE,
&hid_svc->keyboard_boot_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status);
}
// Add Information characteristic
char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_INFO_LEN,
CHAR_PROP_READ,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&hid_svc->info_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add information characteristic: %d", status);
}
// Add Control Point characteristic
char_uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
HID_SVC_CONTROL_POINT_LEN,
CHAR_PROP_WRITE_WITHOUT_RESP,
ATTR_PERMISSION_NONE,
GATT_NOTIFY_ATTRIBUTE_WRITE,
10,
CHAR_VALUE_LEN_CONSTANT,
&hid_svc->ctrl_point_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status);
}
}
bool hid_svc_update_report_map(uint8_t* data, uint16_t len) {
furi_assert(data);
furi_assert(hid_svc);
tBleStatus status = aci_gatt_update_char_value(
hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data);
if(status) {
FURI_LOG_E(TAG, "Failed updating report map characteristic");
return false;
}
return true;
}
bool hid_svc_update_input_report(uint8_t* data, uint16_t len) {
furi_assert(data);
furi_assert(hid_svc);
tBleStatus status =
aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->report_char_handle, 0, len, data);
if(status) {
FURI_LOG_E(TAG, "Failed updating report characteristic");
return false;
}
return true;
}
bool hid_svc_update_info(uint8_t* data, uint16_t len) {
furi_assert(data);
furi_assert(hid_svc);
tBleStatus status =
aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data);
if(status) {
FURI_LOG_E(TAG, "Failed updating info characteristic");
return false;
}
return true;
}
bool hid_svc_is_started() {
return hid_svc != NULL;
}
void hid_svc_stop() {
tBleStatus status;
if(hid_svc) {
// Delete characteristics
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_map_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status);
}
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status);
}
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status);
}
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->keyboard_boot_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Keyboard Boot characteristic: %d", status);
}
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status);
}
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->ctrl_point_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status);
}
// Delete service
status = aci_gatt_del_service(hid_svc->svc_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete HID service: %d", status);
}
// Delete buffer size mutex
free(hid_svc);
hid_svc = NULL;
}
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define HID_SVC_REPORT_MAP_MAX_LEN (120)
#define HID_SVC_REPORT_MAX_LEN (9)
#define HID_SVC_BOOT_KEYBOARD_INPUT_REPORT_MAX_LEN (8)
#define HID_SVC_REPORT_REF_LEN (2)
#define HID_SVC_INFO_LEN (4)
#define HID_SVC_CONTROL_POINT_LEN (1)
void hid_svc_start();
void hid_svc_stop();
bool hid_svc_is_started();
bool hid_svc_update_report_map(uint8_t* data, uint16_t len);
bool hid_svc_update_input_report(uint8_t* data, uint16_t len);
bool hid_svc_update_info(uint8_t* data, uint16_t len);

View File

@@ -0,0 +1,231 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file hw_conf.h
* @author MCD Application Team
* @brief Configuration of hardware interface
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef HW_CONF_H
#define HW_CONF_H
#include "FreeRTOSConfig.h"
/******************************************************************************
* Semaphores
* THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+
*****************************************************************************/
/**
* Index of the semaphore used the prevent conflicts after standby sleep.
* Each CPUs takes this semaphore at standby wakeup until conclicting elements are restored.
*/
#define CFG_HW_PWR_STANDBY_SEMID 10
/**
* The CPU2 may be configured to store the Thread persistent data either in internal NVM storage on CPU2 or in
* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config()
* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed.
* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be:
* + CPU1 takes CFG_HW_THREAD_NVM_SRAM_SEMID semaphore
* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1)
* + CPU1 releases CFG_HW_THREAD_NVM_SRAM_SEMID semaphore
* CFG_HW_THREAD_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them.
* There is no timing constraint on how long this semaphore can be kept.
*/
#define CFG_HW_THREAD_NVM_SRAM_SEMID 9
/**
* The CPU2 may be configured to store the BLE persistent data either in internal NVM storage on CPU2 or in
* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config()
* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed.
* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be:
* + CPU1 takes CFG_HW_BLE_NVM_SRAM_SEMID semaphore
* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1)
* + CPU1 releases CFG_HW_BLE_NVM_SRAM_SEMID semaphore
* CFG_HW_BLE_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them.
* There is no timing constraint on how long this semaphore can be kept.
*/
#define CFG_HW_BLE_NVM_SRAM_SEMID 8
/**
* Index of the semaphore used by CPU2 to prevent the CPU1 to either write or erase data in flash
* The CPU1 shall not either write or erase in flash when this semaphore is taken by the CPU2
* When the CPU1 needs to either write or erase in flash, it shall first get the semaphore and release it just
* after writing a raw (64bits data) or erasing one sector.
* Once the Semaphore has been released, there shall be at least 1us before it can be taken again. This is required
* to give the opportunity to CPU2 to take it.
* On v1.4.0 and older CPU2 wireless firmware, this semaphore is unused and CPU2 is using PES bit.
* By default, CPU2 is using the PES bit to protect its timing. The CPU1 may request the CPU2 to use the semaphore
* instead of the PES bit by sending the system command SHCI_C2_SetFlashActivityControl()
*/
#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID 7
/**
* Index of the semaphore used by CPU1 to prevent the CPU2 to either write or erase data in flash
* In order to protect its timing, the CPU1 may get this semaphore to prevent the CPU2 to either
* write or erase in flash (as this will stall both CPUs)
* The PES bit shall not be used as this may stall the CPU2 in some cases.
*/
#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID 6
/**
* Index of the semaphore used to manage the CLK48 clock configuration
* When the USB is required, this semaphore shall be taken before configuring te CLK48 for USB
* and should be released after the application switch OFF the clock when the USB is not used anymore
* When using the RNG, it is good enough to use CFG_HW_RNG_SEMID to control CLK48.
* More details in AN5289
*/
#define CFG_HW_CLK48_CONFIG_SEMID 5
/* Index of the semaphore used to manage the entry Stop Mode procedure */
#define CFG_HW_ENTRY_STOP_MODE_SEMID 4
/* Index of the semaphore used to access the RCC */
#define CFG_HW_RCC_SEMID 3
/* Index of the semaphore used to access the FLASH */
#define CFG_HW_FLASH_SEMID 2
/* Index of the semaphore used to access the PKA */
#define CFG_HW_PKA_SEMID 1
/* Index of the semaphore used to access the RNG */
#define CFG_HW_RNG_SEMID 0
/******************************************************************************
* HW TIMER SERVER
*****************************************************************************/
/**
* The user may define the maximum number of virtual timers supported.
* It shall not exceed 255
*/
#define CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER 6
/**
* The user may define the priority in the NVIC of the RTC_WKUP interrupt handler that is used to manage the
* wakeup timer.
* This setting is the preemptpriority part of the NVIC.
*/
#define CFG_HW_TS_NVIC_RTC_WAKEUP_IT_PREEMPTPRIO \
(configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1) /* FreeRTOS requirement */
/**
* The user may define the priority in the NVIC of the RTC_WKUP interrupt handler that is used to manage the
* wakeup timer.
* This setting is the subpriority part of the NVIC. It does not exist on all processors. When it is not supported
* on the CPU, the setting is ignored
*/
#define CFG_HW_TS_NVIC_RTC_WAKEUP_IT_SUBPRIO 0
/**
* Define a critical section in the Timer server
* The Timer server does not support the API to be nested
* The Application shall either:
* a) Ensure this will never happen
* b) Define the critical section
* The default implementations is masking all interrupts using the PRIMASK bit
* The TimerServer driver uses critical sections to avoid context corruption. This is achieved with the macro
* TIMER_ENTER_CRITICAL_SECTION and TIMER_EXIT_CRITICAL_SECTION. When CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION is set
* to 1, all STM32 interrupts are masked with the PRIMASK bit of the CortexM CPU. It is possible to use the BASEPRI
* register of the CortexM CPU to keep allowed some interrupts with high priority. In that case, the user shall
* re-implement TIMER_ENTER_CRITICAL_SECTION and TIMER_EXIT_CRITICAL_SECTION and shall make sure that no TimerServer
* API are called when the TIMER critical section is entered
*/
#define CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION 1
/**
* This value shall reflect the maximum delay there could be in the application between the time the RTC interrupt
* is generated by the Hardware and the time when the RTC interrupt handler is called. This time is measured in
* number of RTCCLK ticks.
* A relaxed timing would be 10ms
* When the value is too short, the timerserver will not be able to count properly and all timeout may be random.
* When the value is too long, the device may wake up more often than the most optimal configuration. However, the
* impact on power consumption would be marginal (unless the value selected is extremely too long). It is strongly
* recommended to select a value large enough to make sure it is not too short to ensure reliability of the system
* as this will have marginal impact on low power mode
*/
#define CFG_HW_TS_RTC_HANDLER_MAX_DELAY (10 * (LSI_VALUE / 1000))
/**
* Interrupt ID in the NVIC of the RTC Wakeup interrupt handler
* It shall be type of IRQn_Type
*/
#define CFG_HW_TS_RTC_WAKEUP_HANDLER_ID RTC_WKUP_IRQn
/******************************************************************************
* HW UART
*****************************************************************************/
#define CFG_HW_LPUART1_ENABLED 0
#define CFG_HW_LPUART1_DMA_TX_SUPPORTED 0
#define CFG_HW_USART1_ENABLED 1
#define CFG_HW_USART1_DMA_TX_SUPPORTED 1
/**
* UART1
*/
#define CFG_HW_USART1_PREEMPTPRIORITY 0x0F
#define CFG_HW_USART1_SUBPRIORITY 0
/** < The application shall check the selected source clock is enable */
#define CFG_HW_USART1_SOURCE_CLOCK RCC_USART1CLKSOURCE_SYSCLK
#define CFG_HW_USART1_BAUDRATE 115200
#define CFG_HW_USART1_WORDLENGTH UART_WORDLENGTH_8B
#define CFG_HW_USART1_STOPBITS UART_STOPBITS_1
#define CFG_HW_USART1_PARITY UART_PARITY_NONE
#define CFG_HW_USART1_HWFLOWCTL UART_HWCONTROL_NONE
#define CFG_HW_USART1_MODE UART_MODE_TX_RX
#define CFG_HW_USART1_ADVFEATUREINIT UART_ADVFEATURE_NO_INIT
#define CFG_HW_USART1_OVERSAMPLING UART_OVERSAMPLING_8
#define CFG_HW_USART1_TX_PORT_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE
#define CFG_HW_USART1_TX_PORT GPIOB
#define CFG_HW_USART1_TX_PIN GPIO_PIN_6
#define CFG_HW_USART1_TX_MODE GPIO_MODE_AF_PP
#define CFG_HW_USART1_TX_PULL GPIO_NOPULL
#define CFG_HW_USART1_TX_SPEED GPIO_SPEED_FREQ_VERY_HIGH
#define CFG_HW_USART1_TX_ALTERNATE GPIO_AF7_USART1
#define CFG_HW_USART1_RX_PORT_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE
#define CFG_HW_USART1_RX_PORT GPIOB
#define CFG_HW_USART1_RX_PIN GPIO_PIN_7
#define CFG_HW_USART1_RX_MODE GPIO_MODE_AF_PP
#define CFG_HW_USART1_RX_PULL GPIO_NOPULL
#define CFG_HW_USART1_RX_SPEED GPIO_SPEED_FREQ_VERY_HIGH
#define CFG_HW_USART1_RX_ALTERNATE GPIO_AF7_USART1
#define CFG_HW_USART1_CTS_PORT_CLK_ENABLE __HAL_RCC_GPIOA_CLK_ENABLE
#define CFG_HW_USART1_CTS_PORT GPIOA
#define CFG_HW_USART1_CTS_PIN GPIO_PIN_11
#define CFG_HW_USART1_CTS_MODE GPIO_MODE_AF_PP
#define CFG_HW_USART1_CTS_PULL GPIO_PULLDOWN
#define CFG_HW_USART1_CTS_SPEED GPIO_SPEED_FREQ_VERY_HIGH
#define CFG_HW_USART1_CTS_ALTERNATE GPIO_AF7_USART1
#define CFG_HW_USART1_DMA_TX_PREEMPTPRIORITY 0x0F
#define CFG_HW_USART1_DMA_TX_SUBPRIORITY 0
#define CFG_HW_USART1_DMAMUX_CLK_ENABLE __HAL_RCC_DMAMUX1_CLK_ENABLE
#define CFG_HW_USART1_DMA_CLK_ENABLE __HAL_RCC_DMA2_CLK_ENABLE
#define CFG_HW_USART1_TX_DMA_REQ DMA_REQUEST_USART1_TX
#define CFG_HW_USART1_TX_DMA_CHANNEL DMA2_Channel4
#define CFG_HW_USART1_TX_DMA_IRQn DMA2_Channel4_IRQn
#define CFG_HW_USART1_DMA_TX_IRQHandler DMA2_Channel4_IRQHandler
#endif /*HW_CONF_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,102 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file hw_if.h
* @author MCD Application Team
* @brief Hardware Interface
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef HW_IF_H
#define HW_IF_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32wbxx.h"
#include "stm32wbxx_ll_exti.h"
#include "stm32wbxx_ll_system.h"
#include "stm32wbxx_ll_rcc.h"
#include "stm32wbxx_ll_ipcc.h"
#include "stm32wbxx_ll_bus.h"
#include "stm32wbxx_ll_pwr.h"
#include "stm32wbxx_ll_cortex.h"
#include "stm32wbxx_ll_utils.h"
#include "stm32wbxx_ll_hsem.h"
#include "stm32wbxx_ll_gpio.h"
#include "stm32wbxx_ll_rtc.h"
#ifdef USE_STM32WBXX_USB_DONGLE
#include "stm32wbxx_usb_dongle.h"
#endif
#ifdef USE_STM32WBXX_NUCLEO
#include "stm32wbxx_nucleo.h"
#endif
#ifdef USE_X_NUCLEO_EPD
#include "x_nucleo_epd.h"
#endif
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/******************************************************************************
* HW UART
******************************************************************************/
typedef enum {
hw_uart1,
hw_uart2,
hw_lpuart1,
} hw_uart_id_t;
typedef enum {
hw_uart_ok,
hw_uart_error,
hw_uart_busy,
hw_uart_to,
} hw_status_t;
void HW_UART_Init(hw_uart_id_t hw_uart_id);
void HW_UART_Receive_IT(
hw_uart_id_t hw_uart_id,
uint8_t* pData,
uint16_t Size,
void (*Callback)(void));
void HW_UART_Transmit_IT(
hw_uart_id_t hw_uart_id,
uint8_t* pData,
uint16_t Size,
void (*Callback)(void));
hw_status_t
HW_UART_Transmit(hw_uart_id_t hw_uart_id, uint8_t* p_data, uint16_t size, uint32_t timeout);
hw_status_t HW_UART_Transmit_DMA(
hw_uart_id_t hw_uart_id,
uint8_t* p_data,
uint16_t size,
void (*Callback)(void));
void HW_UART_Interrupt_Handler(hw_uart_id_t hw_uart_id);
void HW_UART_DMA_Interrupt_Handler(hw_uart_id_t hw_uart_id);
#ifdef __cplusplus
}
#endif
#endif /*HW_IF_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,597 @@
/**
******************************************************************************
* File Name : Target/hw_ipcc.c
* Description : Hardware IPCC source file for STM32WPAN Middleware.
*
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "app_common.h"
#include "mbox_def.h"
/* Global variables ---------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/
#define HW_IPCC_TX_PENDING(channel) \
(!(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, channel))) && (((~(IPCC->C1MR)) & (channel << 16U)))
#define HW_IPCC_RX_PENDING(channel) \
(LL_C2_IPCC_IsActiveFlag_CHx(IPCC, channel)) && (((~(IPCC->C1MR)) & (channel << 0U)))
/* Private macros ------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static void (*FreeBufCb)(void);
/* Private function prototypes -----------------------------------------------*/
static void HW_IPCC_BLE_EvtHandler(void);
static void HW_IPCC_BLE_AclDataEvtHandler(void);
static void HW_IPCC_MM_FreeBufHandler(void);
static void HW_IPCC_SYS_CmdEvtHandler(void);
static void HW_IPCC_SYS_EvtHandler(void);
static void HW_IPCC_TRACES_EvtHandler(void);
#ifdef THREAD_WB
static void HW_IPCC_OT_CmdEvtHandler(void);
static void HW_IPCC_THREAD_NotEvtHandler(void);
static void HW_IPCC_THREAD_CliNotEvtHandler(void);
#endif
#ifdef LLD_TESTS_WB
static void HW_IPCC_LLDTESTS_ReceiveCliRspHandler(void);
static void HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(void);
#endif
#ifdef LLD_BLE_WB
/*static void HW_IPCC_LLD_BLE_ReceiveCliRspHandler( void );*/
static void HW_IPCC_LLD_BLE_ReceiveRspHandler(void);
static void HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(void);
#endif
#ifdef MAC_802_15_4_WB
static void HW_IPCC_MAC_802_15_4_CmdEvtHandler(void);
static void HW_IPCC_MAC_802_15_4_NotEvtHandler(void);
#endif
#ifdef ZIGBEE_WB
static void HW_IPCC_ZIGBEE_CmdEvtHandler(void);
static void HW_IPCC_ZIGBEE_StackNotifEvtHandler(void);
static void HW_IPCC_ZIGBEE_StackM0RequestHandler(void);
#endif
/* Public function definition -----------------------------------------------*/
/******************************************************************************
* INTERRUPT HANDLER
******************************************************************************/
void HW_IPCC_Rx_Handler(void) {
if(HW_IPCC_RX_PENDING(HW_IPCC_SYSTEM_EVENT_CHANNEL)) {
HW_IPCC_SYS_EvtHandler();
}
#ifdef MAC_802_15_4_WB
else if(HW_IPCC_RX_PENDING(HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL)) {
HW_IPCC_MAC_802_15_4_NotEvtHandler();
}
#endif /* MAC_802_15_4_WB */
#ifdef THREAD_WB
else if(HW_IPCC_RX_PENDING(HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL)) {
HW_IPCC_THREAD_NotEvtHandler();
} else if(HW_IPCC_RX_PENDING(HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL)) {
HW_IPCC_THREAD_CliNotEvtHandler();
}
#endif /* THREAD_WB */
#ifdef LLD_TESTS_WB
else if(HW_IPCC_RX_PENDING(HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL)) {
HW_IPCC_LLDTESTS_ReceiveCliRspHandler();
} else if(HW_IPCC_RX_PENDING(HW_IPCC_LLDTESTS_M0_CMD_CHANNEL)) {
HW_IPCC_LLDTESTS_ReceiveM0CmdHandler();
}
#endif /* LLD_TESTS_WB */
#ifdef LLD_BLE_WB
else if(HW_IPCC_RX_PENDING(HW_IPCC_LLD_BLE_RSP_CHANNEL)) {
HW_IPCC_LLD_BLE_ReceiveRspHandler();
} else if(HW_IPCC_RX_PENDING(HW_IPCC_LLD_BLE_M0_CMD_CHANNEL)) {
HW_IPCC_LLD_BLE_ReceiveM0CmdHandler();
}
#endif /* LLD_TESTS_WB */
#ifdef ZIGBEE_WB
else if(HW_IPCC_RX_PENDING(HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL)) {
HW_IPCC_ZIGBEE_StackNotifEvtHandler();
} else if(HW_IPCC_RX_PENDING(HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL)) {
HW_IPCC_ZIGBEE_StackM0RequestHandler();
}
#endif /* ZIGBEE_WB */
else if(HW_IPCC_RX_PENDING(HW_IPCC_BLE_EVENT_CHANNEL)) {
HW_IPCC_BLE_EvtHandler();
} else if(HW_IPCC_RX_PENDING(HW_IPCC_TRACES_CHANNEL)) {
HW_IPCC_TRACES_EvtHandler();
}
return;
}
void HW_IPCC_Tx_Handler(void) {
if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) {
HW_IPCC_SYS_CmdEvtHandler();
}
#ifdef MAC_802_15_4_WB
else if(HW_IPCC_TX_PENDING(HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL)) {
HW_IPCC_MAC_802_15_4_CmdEvtHandler();
}
#endif /* MAC_802_15_4_WB */
#ifdef THREAD_WB
else if(HW_IPCC_TX_PENDING(HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL)) {
HW_IPCC_OT_CmdEvtHandler();
}
#endif /* THREAD_WB */
#ifdef LLD_TESTS_WB
// No TX handler for LLD tests
#endif /* LLD_TESTS_WB */
#ifdef ZIGBEE_WB
if(HW_IPCC_TX_PENDING(HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL)) {
HW_IPCC_ZIGBEE_CmdEvtHandler();
}
#endif /* ZIGBEE_WB */
else if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) {
HW_IPCC_SYS_CmdEvtHandler();
} else if(HW_IPCC_TX_PENDING(HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) {
HW_IPCC_MM_FreeBufHandler();
} else if(HW_IPCC_TX_PENDING(HW_IPCC_HCI_ACL_DATA_CHANNEL)) {
HW_IPCC_BLE_AclDataEvtHandler();
}
return;
}
/******************************************************************************
* GENERAL
******************************************************************************/
void HW_IPCC_Enable(void) {
/**
* Such as IPCC IP available to the CPU2, it is required to keep the IPCC clock running
when FUS is running on CPU2 and CPU1 enters deep sleep mode
*/
LL_C2_AHB3_GRP1_EnableClock(LL_C2_AHB3_GRP1_PERIPH_IPCC);
/**
* When the device is out of standby, it is required to use the EXTI mechanism to wakeup CPU2
*/
LL_C2_EXTI_EnableEvent_32_63(LL_EXTI_LINE_41);
LL_EXTI_EnableRisingTrig_32_63(LL_EXTI_LINE_41);
/**
* In case the SBSFU is implemented, it may have already set the C2BOOT bit to startup the CPU2.
* In that case, to keep the mechanism transparent to the user application, it shall call the system command
* SHCI_C2_Reinit( ) before jumping to the application.
* When the CPU2 receives that command, it waits for its event input to be set to restart the CPU2 firmware.
* This is required because once C2BOOT has been set once, a clear/set on C2BOOT has no effect.
* When SHCI_C2_Reinit( ) is not called, generating an event to the CPU2 does not have any effect
* So, by default, the application shall both set the event flag and set the C2BOOT bit.
*/
__SEV(); /* Set the internal event flag and send an event to the CPU2 */
__WFE(); /* Clear the internal event flag */
LL_PWR_EnableBootC2();
return;
}
void HW_IPCC_Init(void) {
LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_IPCC);
LL_C1_IPCC_EnableIT_RXO(IPCC);
LL_C1_IPCC_EnableIT_TXF(IPCC);
HAL_NVIC_SetPriority(IPCC_C1_RX_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(IPCC_C1_RX_IRQn);
HAL_NVIC_SetPriority(IPCC_C1_TX_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(IPCC_C1_TX_IRQn);
return;
}
/******************************************************************************
* BLE
******************************************************************************/
void HW_IPCC_BLE_Init(void) {
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_BLE_EVENT_CHANNEL);
return;
}
void HW_IPCC_BLE_SendCmd(void) {
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_BLE_CMD_CHANNEL);
return;
}
static void HW_IPCC_BLE_EvtHandler(void) {
HW_IPCC_BLE_RxEvtNot();
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_BLE_EVENT_CHANNEL);
return;
}
void HW_IPCC_BLE_SendAclData(void) {
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL);
LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL);
return;
}
static void HW_IPCC_BLE_AclDataEvtHandler(void) {
LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL);
HW_IPCC_BLE_AclDataAckNot();
return;
}
__weak void HW_IPCC_BLE_AclDataAckNot(void){};
__weak void HW_IPCC_BLE_RxEvtNot(void){};
/******************************************************************************
* SYSTEM
******************************************************************************/
void HW_IPCC_SYS_Init(void) {
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL);
return;
}
void HW_IPCC_SYS_SendCmd(void) {
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL);
LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL);
return;
}
static void HW_IPCC_SYS_CmdEvtHandler(void) {
LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL);
HW_IPCC_SYS_CmdEvtNot();
return;
}
static void HW_IPCC_SYS_EvtHandler(void) {
HW_IPCC_SYS_EvtNot();
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL);
return;
}
__weak void HW_IPCC_SYS_CmdEvtNot(void){};
__weak void HW_IPCC_SYS_EvtNot(void){};
/******************************************************************************
* MAC 802.15.4
******************************************************************************/
#ifdef MAC_802_15_4_WB
void HW_IPCC_MAC_802_15_4_Init(void) {
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL);
return;
}
void HW_IPCC_MAC_802_15_4_SendCmd(void) {
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL);
LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL);
return;
}
void HW_IPCC_MAC_802_15_4_SendAck(void) {
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL);
return;
}
static void HW_IPCC_MAC_802_15_4_CmdEvtHandler(void) {
LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL);
HW_IPCC_MAC_802_15_4_CmdEvtNot();
return;
}
static void HW_IPCC_MAC_802_15_4_NotEvtHandler(void) {
LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL);
HW_IPCC_MAC_802_15_4_EvtNot();
return;
}
__weak void HW_IPCC_MAC_802_15_4_CmdEvtNot(void){};
__weak void HW_IPCC_MAC_802_15_4_EvtNot(void){};
#endif
/******************************************************************************
* THREAD
******************************************************************************/
#ifdef THREAD_WB
void HW_IPCC_THREAD_Init(void) {
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL);
return;
}
void HW_IPCC_OT_SendCmd(void) {
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL);
LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL);
return;
}
void HW_IPCC_CLI_SendCmd(void) {
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_THREAD_CLI_CMD_CHANNEL);
return;
}
void HW_IPCC_THREAD_SendAck(void) {
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL);
return;
}
void HW_IPCC_THREAD_CliSendAck(void) {
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL);
return;
}
static void HW_IPCC_OT_CmdEvtHandler(void) {
LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL);
HW_IPCC_OT_CmdEvtNot();
return;
}
static void HW_IPCC_THREAD_NotEvtHandler(void) {
LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL);
HW_IPCC_THREAD_EvtNot();
return;
}
static void HW_IPCC_THREAD_CliNotEvtHandler(void) {
LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL);
HW_IPCC_THREAD_CliEvtNot();
return;
}
__weak void HW_IPCC_OT_CmdEvtNot(void){};
__weak void HW_IPCC_CLI_CmdEvtNot(void){};
__weak void HW_IPCC_THREAD_EvtNot(void){};
#endif /* THREAD_WB */
/******************************************************************************
* LLD TESTS
******************************************************************************/
#ifdef LLD_TESTS_WB
void HW_IPCC_LLDTESTS_Init(void) {
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL);
return;
}
void HW_IPCC_LLDTESTS_SendCliCmd(void) {
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLDTESTS_CLI_CMD_CHANNEL);
return;
}
static void HW_IPCC_LLDTESTS_ReceiveCliRspHandler(void) {
LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL);
HW_IPCC_LLDTESTS_ReceiveCliRsp();
return;
}
void HW_IPCC_LLDTESTS_SendCliRspAck(void) {
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL);
return;
}
static void HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(void) {
LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL);
HW_IPCC_LLDTESTS_ReceiveM0Cmd();
return;
}
void HW_IPCC_LLDTESTS_SendM0CmdAck(void) {
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL);
return;
}
__weak void HW_IPCC_LLDTESTS_ReceiveCliRsp(void){};
__weak void HW_IPCC_LLDTESTS_ReceiveM0Cmd(void){};
#endif /* LLD_TESTS_WB */
/******************************************************************************
* LLD BLE
******************************************************************************/
#ifdef LLD_BLE_WB
void HW_IPCC_LLD_BLE_Init(void) {
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL);
return;
}
void HW_IPCC_LLD_BLE_SendCliCmd(void) {
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CLI_CMD_CHANNEL);
return;
}
/*static void HW_IPCC_LLD_BLE_ReceiveCliRspHandler( void )
{
LL_C1_IPCC_DisableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL );
HW_IPCC_LLD_BLE_ReceiveCliRsp();
return;
}*/
void HW_IPCC_LLD_BLE_SendCliRspAck(void) {
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL);
return;
}
static void HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(void) {
//LL_C1_IPCC_DisableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL );
HW_IPCC_LLD_BLE_ReceiveM0Cmd();
return;
}
void HW_IPCC_LLD_BLE_SendM0CmdAck(void) {
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL);
//LL_C1_IPCC_EnableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL );
return;
}
__weak void HW_IPCC_LLD_BLE_ReceiveCliRsp(void){};
__weak void HW_IPCC_LLD_BLE_ReceiveM0Cmd(void){};
/* Transparent Mode */
void HW_IPCC_LLD_BLE_SendCmd(void) {
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CMD_CHANNEL);
return;
}
static void HW_IPCC_LLD_BLE_ReceiveRspHandler(void) {
LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL);
HW_IPCC_LLD_BLE_ReceiveRsp();
return;
}
void HW_IPCC_LLD_BLE_SendRspAck(void) {
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL);
return;
}
#endif /* LLD_BLE_WB */
/******************************************************************************
* ZIGBEE
******************************************************************************/
#ifdef ZIGBEE_WB
void HW_IPCC_ZIGBEE_Init(void) {
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL);
return;
}
void HW_IPCC_ZIGBEE_SendM4RequestToM0(void) {
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL);
LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL);
return;
}
void HW_IPCC_ZIGBEE_SendM4AckToM0Notify(void) {
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL);
return;
}
static void HW_IPCC_ZIGBEE_CmdEvtHandler(void) {
LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL);
HW_IPCC_ZIGBEE_RecvAppliAckFromM0();
return;
}
static void HW_IPCC_ZIGBEE_StackNotifEvtHandler(void) {
LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL);
HW_IPCC_ZIGBEE_RecvM0NotifyToM4();
return;
}
static void HW_IPCC_ZIGBEE_StackM0RequestHandler(void) {
LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL);
HW_IPCC_ZIGBEE_RecvM0RequestToM4();
return;
}
void HW_IPCC_ZIGBEE_SendM4AckToM0Request(void) {
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL);
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL);
return;
}
__weak void HW_IPCC_ZIGBEE_RecvAppliAckFromM0(void){};
__weak void HW_IPCC_ZIGBEE_RecvM0NotifyToM4(void){};
__weak void HW_IPCC_ZIGBEE_RecvM0RequestToM4(void){};
#endif /* ZIGBEE_WB */
/******************************************************************************
* MEMORY MANAGER
******************************************************************************/
void HW_IPCC_MM_SendFreeBuf(void (*cb)(void)) {
if(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) {
FreeBufCb = cb;
LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL);
} else {
cb();
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL);
}
return;
}
static void HW_IPCC_MM_FreeBufHandler(void) {
LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL);
FreeBufCb();
LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL);
return;
}
/******************************************************************************
* TRACES
******************************************************************************/
void HW_IPCC_TRACES_Init(void) {
LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_TRACES_CHANNEL);
return;
}
static void HW_IPCC_TRACES_EvtHandler(void) {
HW_IPCC_TRACES_EvtNot();
LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_TRACES_CHANNEL);
return;
}
__weak void HW_IPCC_TRACES_EvtNot(void){};
/******************* (C) COPYRIGHT 2019 STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,242 @@
#include "serial_service.h"
#include "app_common.h"
#include "ble.h"
#include <furi.h>
#define TAG "BtSerialSvc"
typedef struct {
uint16_t svc_handle;
uint16_t rx_char_handle;
uint16_t tx_char_handle;
uint16_t flow_ctrl_char_handle;
osMutexId_t buff_size_mtx;
uint32_t buff_size;
uint16_t bytes_ready_to_receive;
SerialServiceEventCallback callback;
void* context;
} SerialSvc;
static SerialSvc* serial_svc = NULL;
static const uint8_t service_uuid[] =
{0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f};
static const uint8_t char_tx_uuid[] =
{0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static const uint8_t char_rx_uuid[] =
{0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static const uint8_t flow_ctrl_uuid[] =
{0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
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 == serial_svc->rx_char_handle + 2) {
// Descriptor handle
ret = SVCCTL_EvtAckFlowEnable;
FURI_LOG_D(TAG, "RX descriptor event");
} else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) {
FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length);
if(serial_svc->callback) {
furi_check(osMutexAcquire(serial_svc->buff_size_mtx, osWaitForever) == osOK);
if(attribute_modified->Attr_Data_Length > serial_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,
serial_svc->bytes_ready_to_receive);
}
serial_svc->bytes_ready_to_receive -= MIN(
serial_svc->bytes_ready_to_receive, attribute_modified->Attr_Data_Length);
SerialServiceEvent event = {
.event = SerialServiceEventTypeDataReceived,
.data = {
.buffer = attribute_modified->Attr_Data,
.size = attribute_modified->Attr_Data_Length,
}};
uint32_t buff_free_size = serial_svc->callback(event, serial_svc->context);
FURI_LOG_D(TAG, "Available buff size: %d", buff_free_size);
furi_check(osMutexRelease(serial_svc->buff_size_mtx) == osOK);
}
ret = SVCCTL_EvtAckFlowEnable;
}
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
FURI_LOG_T(TAG, "Ack received", blecore_evt->ecode);
if(serial_svc->callback) {
SerialServiceEvent event = {
.event = SerialServiceEventTypeDataSent,
};
serial_svc->callback(event, serial_svc->context);
}
ret = SVCCTL_EvtAckFlowEnable;
}
}
return ret;
}
void serial_svc_start() {
tBleStatus status;
serial_svc = furi_alloc(sizeof(SerialSvc));
// Register event handler
SVCCTL_RegisterSvcHandler(serial_svc_event_handler);
// Add service
status = aci_gatt_add_service(
UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 10, &serial_svc->svc_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add Serial service: %d", status);
}
// Add RX characteristics
status = aci_gatt_add_char(
serial_svc->svc_handle,
UUID_TYPE_128,
(const Char_UUID_t*)char_rx_uuid,
SERIAL_SVC_DATA_LEN_MAX,
CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ,
ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE,
GATT_NOTIFY_ATTRIBUTE_WRITE,
10,
CHAR_VALUE_LEN_VARIABLE,
&serial_svc->rx_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add RX characteristic: %d", status);
}
// Add TX characteristic
status = aci_gatt_add_char(
serial_svc->svc_handle,
UUID_TYPE_128,
(const Char_UUID_t*)char_tx_uuid,
SERIAL_SVC_DATA_LEN_MAX,
CHAR_PROP_READ | CHAR_PROP_INDICATE,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_VARIABLE,
&serial_svc->tx_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add TX characteristic: %d", status);
}
// Add Flow Control characteristic
status = aci_gatt_add_char(
serial_svc->svc_handle,
UUID_TYPE_128,
(const Char_UUID_t*)flow_ctrl_uuid,
sizeof(uint32_t),
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_AUTHEN_READ,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&serial_svc->flow_ctrl_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status);
}
// Allocate buffer size mutex
serial_svc->buff_size_mtx = osMutexNew(NULL);
}
void serial_svc_set_callbacks(
uint16_t buff_size,
SerialServiceEventCallback callback,
void* context) {
furi_assert(serial_svc);
serial_svc->callback = callback;
serial_svc->context = context;
serial_svc->buff_size = buff_size;
serial_svc->bytes_ready_to_receive = buff_size;
uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size);
aci_gatt_update_char_value(
serial_svc->svc_handle,
serial_svc->flow_ctrl_char_handle,
0,
sizeof(uint32_t),
(uint8_t*)&buff_size_reversed);
}
void serial_svc_notify_buffer_is_empty() {
furi_assert(serial_svc);
furi_assert(serial_svc->buff_size_mtx);
furi_check(osMutexAcquire(serial_svc->buff_size_mtx, osWaitForever) == osOK);
if(serial_svc->bytes_ready_to_receive == 0) {
FURI_LOG_D(TAG, "Buffer is empty. Notifying client");
serial_svc->bytes_ready_to_receive = serial_svc->buff_size;
uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size);
aci_gatt_update_char_value(
serial_svc->svc_handle,
serial_svc->flow_ctrl_char_handle,
0,
sizeof(uint32_t),
(uint8_t*)&buff_size_reversed);
}
furi_check(osMutexRelease(serial_svc->buff_size_mtx) == osOK);
}
void serial_svc_stop() {
tBleStatus status;
if(serial_svc) {
// Delete characteristics
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete TX characteristic: %d", status);
}
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete RX characteristic: %d", status);
}
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status);
}
// Delete service
status = aci_gatt_del_service(serial_svc->svc_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete Serial service: %d", status);
}
// Delete buffer size mutex
osMutexDelete(serial_svc->buff_size_mtx);
free(serial_svc);
serial_svc = NULL;
}
}
bool serial_svc_is_started() {
return serial_svc != NULL;
}
bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) {
if(data_len > SERIAL_SVC_DATA_LEN_MAX) {
return false;
}
for(uint16_t remained = data_len; remained > 0;) {
uint8_t value_len = MIN(SERIAL_SVC_CHAR_VALUE_LEN_MAX, remained);
uint16_t value_offset = data_len - remained;
remained -= value_len;
tBleStatus result = aci_gatt_update_char_value_ext(
0,
serial_svc->svc_handle,
serial_svc->tx_char_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;
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define SERIAL_SVC_DATA_LEN_MAX (486)
#define SERIAL_SVC_CHAR_VALUE_LEN_MAX (243)
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
SerialServiceEventTypeDataReceived,
SerialServiceEventTypeDataSent,
} SerialServiceEventType;
typedef struct {
uint8_t* buffer;
uint16_t size;
} SerialServiceData;
typedef struct {
SerialServiceEventType event;
SerialServiceData data;
} SerialServiceEvent;
typedef uint16_t (*SerialServiceEventCallback)(SerialServiceEvent event, void* context);
void serial_svc_start();
void serial_svc_set_callbacks(
uint16_t buff_size,
SerialServiceEventCallback callback,
void* context);
void serial_svc_notify_buffer_is_empty();
void serial_svc_stop();
bool serial_svc_is_started();
bool serial_svc_update_tx(uint8_t* data, uint16_t data_len);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,136 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : App/tl_dbg_conf.h
* Description : Debug configuration file for stm32wpan transport layer interface.
*
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __TL_DBG_CONF_H
#define __TL_DBG_CONF_H
#ifdef __cplusplus
extern "C" {
#endif
/* USER CODE BEGIN Tl_Conf */
/* Includes ------------------------------------------------------------------*/
#include "app_conf.h" /* required as some configuration used in dbg_trace.h are set there */
#include "dbg_trace.h"
#include "hw_if.h"
#include <furi_hal.h>
/**
* Enable or Disable traces
* The raw data output is the hci binary packet format as specified by the BT specification *
*/
#define TL_SHCI_CMD_DBG_EN 1 /* Reports System commands sent to CPU2 and the command response */
#define TL_SHCI_CMD_DBG_RAW_EN \
0 /* Reports raw data System commands sent to CPU2 and the command response */
#define TL_SHCI_EVT_DBG_EN 1 /* Reports System Asynchronous Events received from CPU2 */
#define TL_SHCI_EVT_DBG_RAW_EN \
0 /* Reports raw data System Asynchronous Events received from CPU2 */
#define TL_HCI_CMD_DBG_EN 1 /* Reports BLE command sent to CPU2 and the command response */
#define TL_HCI_CMD_DBG_RAW_EN \
0 /* Reports raw data BLE command sent to CPU2 and the command response */
#define TL_HCI_EVT_DBG_EN 1 /* Reports BLE Asynchronous Events received from CPU2 */
#define TL_HCI_EVT_DBG_RAW_EN 0 /* Reports raw data BLE Asynchronous Events received from CPU2 */
#define TL_MM_DBG_EN 1 /* Reports the informations of the buffer released to CPU2 */
/**
* System Transport Layer
*/
#if(TL_SHCI_CMD_DBG_EN != 0)
#define TL_SHCI_CMD_DBG_MSG PRINT_MESG_DBG
#define TL_SHCI_CMD_DBG_BUF PRINT_LOG_BUFF_DBG
#else
#define TL_SHCI_CMD_DBG_MSG(...)
#define TL_SHCI_CMD_DBG_BUF(...)
#endif
#if(TL_SHCI_CMD_DBG_RAW_EN != 0)
#define TL_SHCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_)
#else
#define TL_SHCI_CMD_DBG_RAW(...)
#endif
#if(TL_SHCI_EVT_DBG_EN != 0)
#define TL_SHCI_EVT_DBG_MSG PRINT_MESG_DBG
#define TL_SHCI_EVT_DBG_BUF PRINT_LOG_BUFF_DBG
#else
#define TL_SHCI_EVT_DBG_MSG(...)
#define TL_SHCI_EVT_DBG_BUF(...)
#endif
#if(TL_SHCI_EVT_DBG_RAW_EN != 0)
#define TL_SHCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_)
#else
#define TL_SHCI_EVT_DBG_RAW(...)
#endif
/**
* BLE Transport Layer
*/
#if(TL_HCI_CMD_DBG_EN != 0)
#define TL_HCI_CMD_DBG_MSG PRINT_MESG_DBG
#define TL_HCI_CMD_DBG_BUF PRINT_LOG_BUFF_DBG
#else
#define TL_HCI_CMD_DBG_MSG(...)
#define TL_HCI_CMD_DBG_BUF(...)
#endif
#if(TL_HCI_CMD_DBG_RAW_EN != 0)
#define TL_HCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_)
#else
#define TL_HCI_CMD_DBG_RAW(...)
#endif
#if(TL_HCI_EVT_DBG_EN != 0)
#define TL_HCI_EVT_DBG_MSG PRINT_MESG_DBG
#define TL_HCI_EVT_DBG_BUF PRINT_LOG_BUFF_DBG
#else
#define TL_HCI_EVT_DBG_MSG(...)
#define TL_HCI_EVT_DBG_BUF(...)
#endif
#if(TL_HCI_EVT_DBG_RAW_EN != 0)
#define TL_HCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_)
#else
#define TL_HCI_EVT_DBG_RAW(...)
#endif
/**
* Memory Manager - Released buffer tracing
*/
#if(TL_MM_DBG_EN != 0)
#define TL_MM_DBG_MSG PRINT_MESG_DBG
#else
#define TL_MM_DBG_MSG(...)
#endif
/* USER CODE END Tl_Conf */
#ifdef __cplusplus
}
#endif
#endif /*__TL_DBG_CONF_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,69 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : utilities_conf.h
* Description : Configuration file for STM32 Utilities.
*
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef UTILITIES_CONF_H
#define UTILITIES_CONF_H
#ifdef __cplusplus
extern "C" {
#endif
#include "cmsis_compiler.h"
#include "string.h"
/******************************************************************************
* common
******************************************************************************/
#define UTILS_ENTER_CRITICAL_SECTION() \
uint32_t primask_bit = __get_PRIMASK(); \
__disable_irq()
#define UTILS_EXIT_CRITICAL_SECTION() __set_PRIMASK(primask_bit)
#define UTILS_MEMSET8(dest, value, size) memset(dest, value, size);
/******************************************************************************
* tiny low power manager
* (any macro that does not need to be modified can be removed)
******************************************************************************/
#define UTIL_LPM_INIT_CRITICAL_SECTION()
#define UTIL_LPM_ENTER_CRITICAL_SECTION() UTILS_ENTER_CRITICAL_SECTION()
#define UTIL_LPM_EXIT_CRITICAL_SECTION() UTILS_EXIT_CRITICAL_SECTION()
/******************************************************************************
* sequencer
* (any macro that does not need to be modified can be removed)
******************************************************************************/
#define UTIL_SEQ_INIT_CRITICAL_SECTION()
#define UTIL_SEQ_ENTER_CRITICAL_SECTION() UTILS_ENTER_CRITICAL_SECTION()
#define UTIL_SEQ_EXIT_CRITICAL_SECTION() UTILS_EXIT_CRITICAL_SECTION()
#define UTIL_SEQ_CONF_TASK_NBR (32)
#define UTIL_SEQ_CONF_PRIO_NBR (2)
#define UTIL_SEQ_MEMSET8(dest, value, size) UTILS_MEMSET8(dest, value, size)
#ifdef __cplusplus
}
#endif
#endif /*UTILITIES_CONF_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/