initial commit
This commit is contained in:
+1354
File diff suppressed because it is too large
Load Diff
+88
@@ -0,0 +1,88 @@
|
||||
//*****************************************************************************
|
||||
//
|
||||
//! @file hci_drv_apollo3.h
|
||||
//!
|
||||
//! @brief Support functions for the Nationz BTLE radio in Apollo3.
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Copyright (c) 2020, Ambiq Micro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from this
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// Third party software included in this distribution is subject to the
|
||||
// additional license terms as defined in the /docs/licenses directory.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
|
||||
//
|
||||
//*****************************************************************************
|
||||
#ifndef HCI_DRV_APOLLO3_H
|
||||
#define HCI_DRV_APOLLO3_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// NATIONZ vendor specific events
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
|
||||
// Tx power level in dBm.
|
||||
typedef enum
|
||||
{
|
||||
TX_POWER_LEVEL_MINUS_10P0_dBm = 0x3,
|
||||
TX_POWER_LEVEL_0P0_dBm = 0x8,
|
||||
TX_POWER_LEVEL_PLUS_3P0_dBm = 0xF,
|
||||
TX_POWER_LEVEL_INVALID = 0x10,
|
||||
}txPowerLevel_t;
|
||||
|
||||
|
||||
bool_t HciVsA3_SetRfPowerLevelEx(txPowerLevel_t txPowerlevel);
|
||||
void HciVsA3_ConstantTransmission(uint8_t txchannel);
|
||||
void HciVsA3_CarrierWaveMode(uint8_t txchannel);
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Hci driver functions unique to Apollo3
|
||||
//
|
||||
//*****************************************************************************
|
||||
extern void HciDrvHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
extern void HciDrvHandlerInit(wsfHandlerId_t handlerId);
|
||||
extern void HciDrvIntService(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // HCI_DRV_APOLLO3_H
|
||||
+565
@@ -0,0 +1,565 @@
|
||||
//*****************************************************************************
|
||||
//
|
||||
//! @file hci_drv.c
|
||||
//!
|
||||
//! @brief HCI driver interface.
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Copyright (c) 2020, Ambiq Micro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from this
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// Third party software included in this distribution is subject to the
|
||||
// additional license terms as defined in the /docs/licenses directory.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
|
||||
//
|
||||
//*****************************************************************************
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_cs.h"
|
||||
#include "hci_defs.h"
|
||||
#include "hci_drv.h"
|
||||
#include "hci_drv_apollo.h"
|
||||
#include "hci_tr_apollo.h"
|
||||
#include "hci_core.h"
|
||||
|
||||
#include "am_mcu_apollo.h"
|
||||
#include "am_util.h"
|
||||
#include "am_devices_em9304.h"
|
||||
#include "hci_drv_em9304.h"
|
||||
#include "em9304_patches.h"
|
||||
#include "em9304_init.h"
|
||||
#include "hci_apollo_config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Unless the config file overwrites this option, the HCI driver will use a
|
||||
// direct call to the HAL when it needs to sleep.
|
||||
//
|
||||
//*****************************************************************************
|
||||
#ifndef HCI_DRV_SLEEP
|
||||
#define HCI_DRV_SLEEP am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP)
|
||||
#endif
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// If the config file doesn't say anything about MAC addresses, use a EM Microelectronic
|
||||
// assigned BD address by default .
|
||||
//
|
||||
//*****************************************************************************
|
||||
#ifndef HCI_APOLLO_MAC
|
||||
#define HCI_APOLLO_MAC {0x01, 0x00, 0x00, 0xEE, 0xF3, 0x0C}
|
||||
#endif
|
||||
|
||||
// HCI_APOLLO_USE_CUSTOMER_OWN_MAC should be defined to true if customer
|
||||
// supply their own BD address.
|
||||
|
||||
#ifndef HCI_APOLLO_USE_CUSTOMER_OWN_MAC
|
||||
#define HCI_APOLLO_USE_CUSTOMER_OWN_MAC false
|
||||
#endif
|
||||
|
||||
uint8_t radio_boot_complete = 0;
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Mac address for the EM.
|
||||
//
|
||||
//*****************************************************************************
|
||||
static uint8_t g_pui8BLEMacAddress[6] = HCI_APOLLO_MAC;
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// HCI RX packet buffer for EM9304 Driver.
|
||||
//
|
||||
//*****************************************************************************
|
||||
static uint32_t g_pui32HCIRXBuffer[64];
|
||||
static uint32_t g_ui32HCIPacketSize;
|
||||
static uint8_t g_consumed_bytes;
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Static record of the EM9304 vendor specific events
|
||||
//
|
||||
//*****************************************************************************
|
||||
g_EMVSEvent_t g_EMVendorSpecificEvents = {0,0,0,0,0,0,0};
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Workaround for Keil memcpy()
|
||||
//
|
||||
// Keil's version of memcpy() contains an optimization that allows it to copy
|
||||
// data more efficiently when both the source and destination pointers are well
|
||||
// aligned. Unforunately, some of exactLE's complex callback structures confuse
|
||||
// Keil's memcpy implementation. Left unchecked, this can lead to intermittent
|
||||
// hard-faults.
|
||||
//
|
||||
// This function definition will intercept calls to this optimized version of
|
||||
// memcpy and avoid the problem when the pointers are unexpectedly unaligned.
|
||||
//
|
||||
//*****************************************************************************
|
||||
#if defined(__ARMCC_VERSION)
|
||||
|
||||
void $Super$$__aeabi_memcpy4(void *dest, const void *src, size_t n);
|
||||
|
||||
void
|
||||
$Sub$$__aeabi_memcpy4(void *dest, const void *src, size_t n)
|
||||
{
|
||||
//
|
||||
// If the pointers are aligned, we can use Keil's normal memcpy.
|
||||
//
|
||||
if ((((uint32_t)dest % 4) == 0) && (((uint32_t)src % 4) == 0))
|
||||
{
|
||||
$Super$$__aeabi_memcpy4(dest, src, n);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Otherwise, make sure we use 8-bit pointers.
|
||||
//
|
||||
uint8_t *tempSrc = (uint8_t *)(src);
|
||||
uint8_t *tempDest = (uint8_t *)(dest);
|
||||
|
||||
//
|
||||
// Copy from src to dest, one byte at a time.
|
||||
//
|
||||
for (uint32_t i = 0; i < n; i++)
|
||||
{
|
||||
*tempDest++ = *tempSrc++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
//! @brief Get the EM9304 vendor specific event counters.
|
||||
//!
|
||||
//! @return Returns a pointer to the EM9304 vendor specific event counters.
|
||||
//
|
||||
//*****************************************************************************
|
||||
g_EMVSEvent_t *getEM9304VSEventCounters(void)
|
||||
{
|
||||
return &g_EMVendorSpecificEvents;
|
||||
}
|
||||
//*****************************************************************************
|
||||
//
|
||||
//! @brief Write data the driver.
|
||||
//!
|
||||
//! @param type HCI packet type
|
||||
//! @param len Number of bytes to write
|
||||
//! @param pData Byte array to write
|
||||
//!
|
||||
//! @return Returns the number of bytes written.
|
||||
//
|
||||
//*****************************************************************************
|
||||
uint16_t
|
||||
hciDrvWrite(uint8_t type, uint16_t len, uint8_t *pData)
|
||||
{
|
||||
//
|
||||
// Turn on the IOM for this operation.
|
||||
//
|
||||
am_devices_em9304_spi_awake(g_sEm9304.ui32IOMModule);
|
||||
|
||||
//
|
||||
// Write the HCI packet.
|
||||
//
|
||||
am_devices_em9304_block_write(&g_sEm9304, type, pData, len );
|
||||
|
||||
//
|
||||
// Disable IOM SPI pins and turn off the IOM after operation
|
||||
//
|
||||
am_devices_em9304_spi_sleep(g_sEm9304.ui32IOMModule);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// hciDrvReadyToSleep - Stub provided to allow other layers to run correctly.
|
||||
//
|
||||
//*****************************************************************************
|
||||
bool_t
|
||||
hciDrvReadyToSleep(void)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool_t
|
||||
HciDataReadyISR(void)
|
||||
{
|
||||
//
|
||||
// If the radio boot has not yet completed, then do not process HCI packets
|
||||
if (!radio_boot_complete)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// check if there's pending HCI data from last time
|
||||
if (g_ui32HCIPacketSize > g_consumed_bytes)
|
||||
{
|
||||
g_consumed_bytes += hciTrSerialRxIncoming(
|
||||
((uint8_t *)g_pui32HCIRXBuffer) + g_consumed_bytes,
|
||||
g_ui32HCIPacketSize - g_consumed_bytes);
|
||||
|
||||
if (g_consumed_bytes == g_ui32HCIPacketSize) {
|
||||
g_ui32HCIPacketSize = 0;
|
||||
g_consumed_bytes = 0;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Turn on the IOM for this operation.
|
||||
//
|
||||
am_devices_em9304_spi_awake(g_sEm9304.ui32IOMModule);
|
||||
|
||||
g_ui32HCIPacketSize = am_devices_em9304_block_read(&g_sEm9304, g_pui32HCIRXBuffer, 0);
|
||||
|
||||
// Check for EM9304 Vendor Specific events and record them.
|
||||
if ( (g_ui32HCIPacketSize > 3) && (0x0001FF04 == (g_pui32HCIRXBuffer[0] & 0x00FFFFFF)) )
|
||||
{
|
||||
switch((g_pui32HCIRXBuffer[0] & 0xFF000000) >> 24)
|
||||
{
|
||||
case 0x01:
|
||||
g_EMVendorSpecificEvents.EM_ActiveStateEntered++;
|
||||
am_util_debug_printf("Received EM_ActiveStateEntered Event\n");
|
||||
break;
|
||||
case 0x03:
|
||||
g_EMVendorSpecificEvents.EM_TestModeEntered++;
|
||||
am_util_debug_printf("Received EM_TestModeEntered Event\n");
|
||||
break;
|
||||
case 0x04:
|
||||
g_EMVendorSpecificEvents.EM_HalNotification++;
|
||||
am_util_debug_printf("Received EM_HalNotification Event\n");
|
||||
break;
|
||||
default:
|
||||
am_util_debug_printf("Received Unknown Vendor Specific Event from EM9304\n");
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Reset the packet size to 0 so that this packet will not be processed by the host stack.
|
||||
//
|
||||
g_ui32HCIPacketSize = 0;
|
||||
}
|
||||
|
||||
if (g_ui32HCIPacketSize > 0)
|
||||
{
|
||||
g_consumed_bytes += hciTrSerialRxIncoming((uint8_t *)g_pui32HCIRXBuffer, g_ui32HCIPacketSize);
|
||||
if (g_consumed_bytes == g_ui32HCIPacketSize) {
|
||||
g_ui32HCIPacketSize = 0;
|
||||
g_consumed_bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Disable IOM SPI pins and turn off the IOM after operation
|
||||
//
|
||||
am_devices_em9304_spi_sleep(g_sEm9304.ui32IOMModule);
|
||||
|
||||
return (g_ui32HCIPacketSize == 0);
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Configure the necessary pins and start the EM9304 radio.
|
||||
//
|
||||
//*****************************************************************************
|
||||
void
|
||||
HciDrvRadioBoot(uint32_t ui32UartModule)
|
||||
{
|
||||
uint32_t patch_dest_memory = DEST_MEMORY_IRAM;
|
||||
|
||||
uint32_t ui32PN;
|
||||
|
||||
// disable interrupt during EM9304 initialization.
|
||||
am_devices_em9304_disable_interrupt();
|
||||
|
||||
radio_boot_complete = 0;
|
||||
|
||||
//
|
||||
// Enable the radio pins.
|
||||
//
|
||||
#ifdef HCI_APOLLO_POWER_PIN
|
||||
|
||||
//
|
||||
// Insert a power on reset to TLSR8269
|
||||
// (with TLSR8269 EVK, this is only done via power pin)
|
||||
//
|
||||
am_hal_gpio_pin_config(HCI_APOLLO_POWER_PIN, HCI_APOLLO_POWER_CFG);
|
||||
am_hal_gpio_out_bit_clear(HCI_APOLLO_POWER_PIN);
|
||||
am_util_delay_ms(100);
|
||||
am_hal_gpio_out_bit_set(HCI_APOLLO_POWER_PIN);
|
||||
am_util_delay_ms(100);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Device identification
|
||||
//
|
||||
ui32PN = AM_REG(MCUCTRL, CHIP_INFO) &
|
||||
AM_UTIL_MCUCTRL_CHIP_INFO_PARTNUM_PN_M;
|
||||
|
||||
// Currently only enable this for Apollo2-Blue
|
||||
if (ui32PN == AM_UTIL_MCUCTRL_CHIP_INFO_PARTNUM_APOLLOBL)
|
||||
{
|
||||
am_hal_gpio_pin_config(30, AM_HAL_GPIO_OUTPUT);
|
||||
am_hal_gpio_out_bit_clear(30);
|
||||
am_hal_gpio_pin_config(35, AM_HAL_GPIO_OUTPUT);
|
||||
am_hal_gpio_out_bit_clear(35);
|
||||
am_hal_gpio_pin_config(36, AM_HAL_GPIO_OUTPUT);
|
||||
am_hal_gpio_out_bit_clear(36);
|
||||
|
||||
}
|
||||
//
|
||||
// Assert RESET to the EM9304 device.
|
||||
//
|
||||
am_hal_gpio_pin_config(HCI_APOLLO_RESET_PIN, AM_HAL_GPIO_OUTPUT);
|
||||
am_hal_gpio_out_bit_clear(HCI_APOLLO_RESET_PIN);
|
||||
|
||||
//
|
||||
// Setup SPI interface for EM9304
|
||||
//
|
||||
am_devices_em9304_config_pins();
|
||||
am_devices_em9304_spi_init(g_sEm9304.ui32IOMModule, &g_sEm9304IOMConfigSPI);
|
||||
|
||||
//
|
||||
// Enable the IOM and GPIO interrupt handlers.
|
||||
//
|
||||
am_hal_gpio_out_bit_set(HCI_APOLLO_RESET_PIN);
|
||||
|
||||
//
|
||||
// Delay for 20ms to make sure the em device gets ready for commands.
|
||||
//
|
||||
am_util_delay_ms(20);
|
||||
|
||||
//
|
||||
// Initialize the EM9304.
|
||||
//
|
||||
patch_dest_memory = initEM9304();
|
||||
|
||||
//
|
||||
// Delay for 20ms to make sure the em device completes initialization.
|
||||
//
|
||||
am_util_delay_ms(20);
|
||||
|
||||
if (patch_dest_memory == DEST_MEMORY_OTP)
|
||||
{
|
||||
|
||||
//
|
||||
// Completed the patching process by cycling RESET to the EM9304 device.
|
||||
//
|
||||
am_hal_gpio_pin_config(HCI_APOLLO_RESET_PIN, AM_HAL_GPIO_OUTPUT);
|
||||
am_hal_gpio_out_bit_clear(HCI_APOLLO_RESET_PIN);
|
||||
am_util_delay_ms(20);
|
||||
am_hal_gpio_out_bit_set(HCI_APOLLO_RESET_PIN);
|
||||
|
||||
// delay here to make sure EM9304 is ready for operation after
|
||||
// patch is loaded.
|
||||
am_util_delay_ms(20);
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Set the MAC address if customer provides their own BD address
|
||||
//
|
||||
if (HCI_APOLLO_USE_CUSTOMER_OWN_MAC)
|
||||
{
|
||||
//
|
||||
// currently just use the default one, customer either update g_pui8BLEMacAddress
|
||||
// directly or call HciDrvAssignBDAddress() with a pointer of assigned BD address.
|
||||
HciDrvAssignBDAddress(g_pui8BLEMacAddress);
|
||||
}
|
||||
|
||||
// Initialization of the EM9304 is complete.
|
||||
radio_boot_complete = 1;
|
||||
g_ui32HCIPacketSize = 0;
|
||||
g_consumed_bytes = 0;
|
||||
am_util_debug_printf("HciDrvRadioBoot complete\n");
|
||||
|
||||
// enable interrupt after EM9304 initialization is done.
|
||||
am_devices_em9304_enable_interrupt();
|
||||
}
|
||||
|
||||
void
|
||||
HciDrvRadioShutdown(void)
|
||||
{
|
||||
uint32_t ui32PN;
|
||||
|
||||
radio_boot_complete = 0;
|
||||
g_ui32HCIPacketSize = 0;
|
||||
g_consumed_bytes = 0;
|
||||
|
||||
am_devices_em9304_disable_interrupt();
|
||||
AM_HAL_GPIO_MASKCREATE(GpioIntMask);
|
||||
am_hal_gpio_int_clear(AM_HAL_GPIO_MASKBIT(pGpioIntMask, AM_BSP_GPIO_EM9304_INT));
|
||||
|
||||
am_hal_gpio_pin_config(AM_BSP_GPIO_EM9304_CS, AM_HAL_PIN_OUTPUT);
|
||||
am_hal_gpio_out_bit_clear(AM_BSP_GPIO_EM9304_CS);
|
||||
|
||||
am_hal_clkgen_clkout_disable();
|
||||
|
||||
//
|
||||
// Device identification
|
||||
//
|
||||
ui32PN = AM_REG(MCUCTRL, CHIP_INFO) &
|
||||
AM_UTIL_MCUCTRL_CHIP_INFO_PARTNUM_PN_M;
|
||||
|
||||
if (ui32PN == AM_UTIL_MCUCTRL_CHIP_INFO_PARTNUM_APOLLOBL)
|
||||
{
|
||||
// Currently clear pin 24 only for Apollo2-Blue
|
||||
am_hal_gpio_pin_config(24, AM_HAL_PIN_24_CLKOUT);
|
||||
am_hal_gpio_out_bit_clear(24);
|
||||
}
|
||||
|
||||
am_hal_gpio_pin_config(HCI_APOLLO_RESET_PIN, AM_HAL_PIN_OUTPUT);
|
||||
am_hal_gpio_out_bit_clear(HCI_APOLLO_RESET_PIN);
|
||||
|
||||
|
||||
am_hal_gpio_pin_config(AM_BSP_GPIO_EM9304_INT, AM_HAL_PIN_OUTPUT);
|
||||
am_hal_gpio_out_bit_clear(AM_BSP_GPIO_EM9304_INT);
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciVsSetRfPowerLevelEx
|
||||
*
|
||||
* \brief Vendor-specific command for settting Radio transmit power level
|
||||
* for EM9304.
|
||||
*
|
||||
* \param txPowerlevel valid range from 0 to 17 in decimal.
|
||||
*
|
||||
* \return true when success, otherwise false
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t HciVsEM_SetRfPowerLevelEx(txPowerLevel_t txPowerlevel)
|
||||
{
|
||||
// make sure it's 8 bit
|
||||
uint8_t tx_power_level = (uint8_t)txPowerlevel;
|
||||
|
||||
if(tx_power_level < TX_POWER_LEVEL_INVALID) {
|
||||
HciVendorSpecificCmd(0xFC26, sizeof(tx_power_level), &tx_power_level);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciVsEM_TransmitterTest
|
||||
*
|
||||
* \brief Vendor-specific command for start transmitter testing
|
||||
*
|
||||
* \param test_mode refer to em9304 datasheet
|
||||
* \param channel_number refer to em9304 datasheet
|
||||
* \param packet_len refer to em9304 datasheet
|
||||
* \param packet_payload_type refer to em9304 datasheet
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciVsEM_TransmitterTest(uint8_t test_mode, uint8_t channel_number, uint8_t packet_len, uint8_t packet_payload_type)
|
||||
{
|
||||
uint8_t params[4] = {
|
||||
test_mode,
|
||||
channel_number,
|
||||
packet_len,
|
||||
packet_payload_type
|
||||
};
|
||||
|
||||
HciVendorSpecificCmd(0xFC11, sizeof(params), ¶ms[0]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciVsEM_TransmitterTestEnd
|
||||
*
|
||||
* \brief Vendor-specific command for ending Radio transmitter testing.
|
||||
*
|
||||
* \param None
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciVsEM_TransmitterTestEnd(void)
|
||||
{
|
||||
HciVendorSpecificCmd(0xFC12, 0, NULL);
|
||||
}
|
||||
|
||||
void HciVsEM_ReadAtAddress(uint32_t addr)
|
||||
{
|
||||
uint8_t ReadAtAddress[5];
|
||||
|
||||
// only read 4 bytes
|
||||
memcpy(ReadAtAddress, &addr, 4);
|
||||
ReadAtAddress[4] = 4;
|
||||
HciVendorSpecificCmd(0xFC20, sizeof(ReadAtAddress), ReadAtAddress);
|
||||
}
|
||||
|
||||
void HciVsEM_WriteAtAddress(uint32_t addr, uint32_t value)
|
||||
{
|
||||
uint8_t WriteAtAddress[8];
|
||||
|
||||
// only write 4 bytes
|
||||
memcpy(WriteAtAddress, &addr, 4);
|
||||
memcpy(WriteAtAddress+4, &value, 4);
|
||||
HciVendorSpecificCmd(0xFC22, sizeof(WriteAtAddress), WriteAtAddress);
|
||||
}
|
||||
|
||||
void HciDrvAssignBDAddress(uint8_t * customer_unique_bd_address)
|
||||
{
|
||||
if (customer_unique_bd_address)
|
||||
{
|
||||
memcpy(g_pui8BLEMacAddress, customer_unique_bd_address, 6);
|
||||
}
|
||||
}
|
||||
|
||||
void HciVsEM_SetBDAddress(void)
|
||||
{
|
||||
if (HCI_APOLLO_USE_CUSTOMER_OWN_MAC)
|
||||
{
|
||||
HciVendorSpecificCmd(0xFC02, 6, g_pui8BLEMacAddress);
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
//*****************************************************************************
|
||||
//
|
||||
//! @file hci_drv_em9304.h
|
||||
//!
|
||||
//! @brief Support functions for the EM Micro EM9304 BTLE radio.
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Copyright (c) 2020, Ambiq Micro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from this
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// Third party software included in this distribution is subject to the
|
||||
// additional license terms as defined in the /docs/licenses directory.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
|
||||
//
|
||||
//*****************************************************************************
|
||||
#ifndef HCI_DRV_EM9304_H
|
||||
#define HCI_DRV_EM9304_H
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// EM9304 vendor specific events
|
||||
//
|
||||
//*****************************************************************************
|
||||
typedef struct
|
||||
{
|
||||
uint32_t EM_ActiveStateEntered;
|
||||
uint32_t EM_TestModeEntered;
|
||||
uint32_t EM_HalNotification;
|
||||
uint32_t EM_DebugPrint;
|
||||
uint32_t EM_DebugStackUsage;
|
||||
uint32_t EM_DebugBacktrace;
|
||||
uint32_t EM_DebugAssert;
|
||||
} g_EMVSEvent_t;
|
||||
|
||||
// Tx power level in dBm.
|
||||
typedef enum
|
||||
{
|
||||
TX_POWER_LEVEL_MINOR_33P5_dBm, // 0, not compliant with BT spec
|
||||
TX_POWER_LEVEL_MINOR_29P0_dBm, // 1, not compliant with BT spec
|
||||
TX_POWER_LEVEL_MINOR_17P9_dBm, // 2
|
||||
TX_POWER_LEVEL_MINOR_16P4_dBm, // 3
|
||||
TX_POWER_LEVEL_MINOR_14P6_dBm, // 4
|
||||
TX_POWER_LEVEL_MINOR_13P1_dBm, // 5
|
||||
TX_POWER_LEVEL_MINOR_11P4_dBm, // 6
|
||||
TX_POWER_LEVEL_MINOR_9P9_dBm, // 7
|
||||
TX_POWER_LEVEL_MINOR_8P4_dBm, // 8
|
||||
TX_POWER_LEVEL_MINOR_6P9_dBm, // 9
|
||||
TX_POWER_LEVEL_MINOR_5P5_dBm, // 10
|
||||
TX_POWER_LEVEL_MINOR_4P0_dBm, // 11
|
||||
TX_POWER_LEVEL_MINOR_2P6_dBm, // 12
|
||||
TX_POWER_LEVEL_MINOR_1P4_dBm, // 13
|
||||
TX_POWER_LEVEL_PLUS_0P4_dBm, // 14
|
||||
TX_POWER_LEVEL_PLUS_2P5_dBm, // 15
|
||||
TX_POWER_LEVEL_PLUS_4P6_dBm, // 16
|
||||
TX_POWER_LEVEL_PLUS_6P2_dBm, // 17
|
||||
TX_POWER_LEVEL_INVALID
|
||||
}txPowerLevel_t;
|
||||
|
||||
extern g_EMVSEvent_t *getEM9304VSEventCounters(void);
|
||||
extern uint32_t HciVsEM_SetRfPowerLevelEx(txPowerLevel_t txPowerlevel);
|
||||
extern void HciVsEM_TransmitterTest(uint8_t test_mode, uint8_t channel_number, uint8_t packet_len, uint8_t packet_payload_type);
|
||||
extern void HciVsEM_TransmitterTestEnd(void);
|
||||
extern void HciVsEM_ReadAtAddress(uint32_t addr);
|
||||
extern void HciVsEM_WriteAtAddress(uint32_t addr, uint32_t value);
|
||||
extern void HciDrvAssignBDAddress(uint8_t * customer_unique_bd_address);
|
||||
extern void HciVsEM_SetBDAddress(void);
|
||||
|
||||
|
||||
#endif // HCI_DRV_EM9304_H
|
||||
+1626
File diff suppressed because it is too large
Load Diff
Vendored
+703
@@ -0,0 +1,703 @@
|
||||
/* Copyright (c) 2009-2019 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI Advertising Extensions (AE) command module.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "hci_cmd.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set advertising set random device address command.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param pAddr Random device address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetAdvSetRandAddrCmd(uint8_t advHandle, const uint8_t *pAddr)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_ADV_SET_RAND_ADDR, HCI_LEN_LE_SET_ADV_SET_RAND_ADDR)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, advHandle);
|
||||
BDA_TO_BSTREAM(p, pAddr);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set extended advertising parameters command.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param pExtAdvParam Extended advertising parameters.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetExtAdvParamCmd(uint8_t advHandle, hciExtAdvParam_t *pExtAdvParam)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_EXT_ADV_PARAM, HCI_LEN_LE_SET_EXT_ADV_PARAM)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, advHandle);
|
||||
UINT16_TO_BSTREAM(p, pExtAdvParam->advEventProp);
|
||||
UINT24_TO_BSTREAM(p, pExtAdvParam->priAdvInterMin);
|
||||
UINT24_TO_BSTREAM(p, pExtAdvParam->priAdvInterMax);
|
||||
UINT8_TO_BSTREAM(p, pExtAdvParam->priAdvChanMap);
|
||||
UINT8_TO_BSTREAM(p, pExtAdvParam->ownAddrType);
|
||||
UINT8_TO_BSTREAM(p, pExtAdvParam->peerAddrType);
|
||||
BDA_TO_BSTREAM(p, pExtAdvParam->pPeerAddr);
|
||||
UINT8_TO_BSTREAM(p, pExtAdvParam->advFiltPolicy);
|
||||
UINT8_TO_BSTREAM(p, pExtAdvParam->advTxPwr);
|
||||
UINT8_TO_BSTREAM(p, pExtAdvParam->priAdvPhy);
|
||||
UINT8_TO_BSTREAM(p, pExtAdvParam->secAdvMaxSkip);
|
||||
UINT8_TO_BSTREAM(p, pExtAdvParam->secAdvPhy);
|
||||
UINT8_TO_BSTREAM(p, pExtAdvParam->advSetId);
|
||||
UINT8_TO_BSTREAM(p, pExtAdvParam->scanReqNotifEna);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set extended advertising data command.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param op Operation.
|
||||
* \param fragPref Fragment preference.
|
||||
* \param len Data buffer length.
|
||||
* \param pData Advertising data buffer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetExtAdvDataCmd(uint8_t advHandle, uint8_t op, uint8_t fragPref, uint8_t len,
|
||||
const uint8_t *pData)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if (len > HCI_EXT_ADV_DATA_LEN)
|
||||
{
|
||||
len = HCI_EXT_ADV_DATA_LEN;
|
||||
}
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_EXT_ADV_DATA, HCI_LEN_LE_SET_EXT_ADV_DATA(len))) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, advHandle);
|
||||
UINT8_TO_BSTREAM(p, op);
|
||||
UINT8_TO_BSTREAM(p, fragPref);
|
||||
UINT8_TO_BSTREAM(p, len);
|
||||
memcpy(p, pData, len);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set extended scan response data command.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param op Operation.
|
||||
* \param fragPref Fragment preference.
|
||||
* \param len Data buffer length.
|
||||
* \param pData Scan response data buffer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetExtScanRespDataCmd(uint8_t advHandle, uint8_t op, uint8_t fragPref, uint8_t len,
|
||||
const uint8_t *pData)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if (len > HCI_EXT_ADV_DATA_LEN)
|
||||
{
|
||||
len = HCI_EXT_ADV_DATA_LEN;
|
||||
}
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_EXT_SCAN_RESP_DATA,
|
||||
HCI_LEN_LE_SET_EXT_SCAN_RESP_DATA(len))) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, advHandle);
|
||||
UINT8_TO_BSTREAM(p, op);
|
||||
UINT8_TO_BSTREAM(p, fragPref);
|
||||
UINT8_TO_BSTREAM(p, len);
|
||||
memcpy(p, pData, len);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set extended advertising enable command.
|
||||
*
|
||||
* \param enable Set to TRUE to enable advertising, FALSE to disable advertising.
|
||||
* \param numSets Number of advertising sets.
|
||||
* \param pScanParam Advertising enable parameter array.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetExtAdvEnableCmd(uint8_t enable, uint8_t numSets, hciExtAdvEnableParam_t *pEnableParam)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
uint8_t i;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_EXT_ADV_ENABLE, HCI_LEN_LE_EXT_ADV_ENABLE(numSets))) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, enable);
|
||||
UINT8_TO_BSTREAM(p, numSets);
|
||||
|
||||
for (i = 0; i < numSets; i++)
|
||||
{
|
||||
UINT8_TO_BSTREAM(p, pEnableParam[i].advHandle);
|
||||
UINT16_TO_BSTREAM(p, pEnableParam[i].duration);
|
||||
UINT8_TO_BSTREAM(p, pEnableParam[i].maxEaEvents);
|
||||
}
|
||||
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE read maximum advertising data length command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeReadMaxAdvDataLen(void)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_READ_MAX_ADV_DATA_LEN, HCI_LEN_LE_READ_MAX_ADV_DATA_LEN)) != NULL)
|
||||
{
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE read number of supported advertising sets command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeReadNumSupAdvSets(void)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_READ_NUM_SUP_ADV_SETS, HCI_LEN_LE_READ_NUM_OF_SUP_ADV_SETS)) != NULL)
|
||||
{
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE remove advertising set command.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return Status error code.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeRemoveAdvSet(uint8_t advHandle)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_REMOVE_ADV_SET, HCI_LEN_LE_REMOVE_ADV_SET)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, advHandle);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE clear advertising sets command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeClearAdvSets(void)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_CLEAR_ADV_SETS, HCI_LEN_LE_CLEAR_ADV_SETS)) != NULL)
|
||||
{
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set periodic advertising parameters command.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param advIntervalMin Periodic advertising interval minimum.
|
||||
* \param advIntervalMax Periodic advertising interval maximum.
|
||||
* \param advProps Periodic advertising properties.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetPerAdvParamCmd(uint8_t advHandle, uint16_t advIntervalMin, uint16_t advIntervalMax,
|
||||
uint16_t advProps)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_PER_ADV_PARAM, HCI_LEN_LE_SET_PER_ADV_PARAM)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, advHandle);
|
||||
UINT16_TO_BSTREAM(p, advIntervalMin);
|
||||
UINT16_TO_BSTREAM(p, advIntervalMax);
|
||||
UINT16_TO_BSTREAM(p, advProps);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set periodic advertising data command.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param op Operation.
|
||||
* \param len Data buffer length.
|
||||
* \param pData Advertising data buffer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetPerAdvDataCmd(uint8_t advHandle, uint8_t op, uint8_t len, const uint8_t *pData)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if (len > HCI_PER_ADV_DATA_LEN)
|
||||
{
|
||||
len = HCI_PER_ADV_DATA_LEN;
|
||||
}
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_PER_ADV_DATA, HCI_LEN_LE_SET_PER_ADV_DATA(len))) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, advHandle);
|
||||
UINT8_TO_BSTREAM(p, op);
|
||||
UINT8_TO_BSTREAM(p, len);
|
||||
memcpy(p, pData, len);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set periodic advertising enable command.
|
||||
*
|
||||
* \param enable Set to TRUE to enable advertising, FALSE to disable advertising.
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetPerAdvEnableCmd(uint8_t enable, uint8_t advHandle)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_PER_ADV_ENABLE, HCI_LEN_LE_SET_PER_ADV_ENABLE)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, enable);
|
||||
UINT8_TO_BSTREAM(p, advHandle);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set extended scanning parameters command.
|
||||
*
|
||||
* \param ownAddrType Address type used by this device.
|
||||
* \param scanFiltPolicy Scan filter policy.
|
||||
* \param scanPhys Scanning PHYs.
|
||||
* \param pScanParam Scanning parameter array.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetExtScanParamCmd(uint8_t ownAddrType, uint8_t scanFiltPolicy, uint8_t scanPhys,
|
||||
hciExtScanParam_t *pScanParam)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
uint8_t i;
|
||||
uint8_t numPhys;
|
||||
|
||||
/* find out number of scanning PHYs */
|
||||
for (i = 0, numPhys = 0; (i < 8) && (numPhys <= HCI_MAX_NUM_PHYS); i++)
|
||||
{
|
||||
if (scanPhys & (1 << i))
|
||||
{
|
||||
numPhys++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_EXT_SCAN_PARAM, HCI_LEN_LE_SET_EXT_SCAN_PARAM(numPhys))) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, ownAddrType);
|
||||
UINT8_TO_BSTREAM(p, scanFiltPolicy);
|
||||
UINT8_TO_BSTREAM(p, scanPhys);
|
||||
|
||||
for (i = 0; i < numPhys; i++)
|
||||
{
|
||||
UINT8_TO_BSTREAM(p, pScanParam[i].scanType);
|
||||
UINT16_TO_BSTREAM(p, pScanParam[i].scanInterval);
|
||||
UINT16_TO_BSTREAM(p, pScanParam[i].scanWindow)
|
||||
}
|
||||
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE extended scan enable command.
|
||||
*
|
||||
* \param enable Set to TRUE to enable scanning, FALSE to disable scanning.
|
||||
* \param filterDup Set to TRUE to filter duplicates.
|
||||
* \param duration Duration.
|
||||
* \param period Period.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeExtScanEnableCmd(uint8_t enable, uint8_t filterDup, uint16_t duration, uint16_t period)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_EXT_SCAN_ENABLE, HCI_LEN_LE_SET_EXT_SCAN_ENABLE)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, enable);
|
||||
UINT8_TO_BSTREAM(p, filterDup);
|
||||
UINT16_TO_BSTREAM(p, duration);
|
||||
UINT16_TO_BSTREAM(p, period);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE extended create connection command.
|
||||
*
|
||||
* \param pInitParam Initiating parameters.
|
||||
* \param pScanParam Initiating scan parameters.
|
||||
* \param pConnSpec Connection specification.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeExtCreateConnCmd(hciExtInitParam_t *pInitParam, hciExtInitScanParam_t *pScanParam,
|
||||
hciConnSpec_t *pConnSpec)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
uint8_t i;
|
||||
uint8_t numPhys;
|
||||
|
||||
/* find out number of initiating PHYs */
|
||||
for (i = 0, numPhys = 0; (i < 8) && (numPhys <= HCI_MAX_NUM_PHYS); i++)
|
||||
{
|
||||
if (pInitParam->initPhys & (1 << i))
|
||||
{
|
||||
numPhys++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_EXT_CREATE_CONN, HCI_LEN_LE_EXT_CREATE_CONN(numPhys))) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, pInitParam->filterPolicy);
|
||||
UINT8_TO_BSTREAM(p, pInitParam->ownAddrType);
|
||||
UINT8_TO_BSTREAM(p, pInitParam->peerAddrType);
|
||||
BDA_TO_BSTREAM(p, pInitParam->pPeerAddr);
|
||||
UINT8_TO_BSTREAM(p, pInitParam->initPhys);
|
||||
|
||||
for (i = 0; i < numPhys; i++)
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, pScanParam[i].scanInterval);
|
||||
UINT16_TO_BSTREAM(p, pScanParam[i].scanWindow);
|
||||
UINT16_TO_BSTREAM(p, pConnSpec[i].connIntervalMin);
|
||||
UINT16_TO_BSTREAM(p, pConnSpec[i].connIntervalMax);
|
||||
UINT16_TO_BSTREAM(p, pConnSpec[i].connLatency);
|
||||
UINT16_TO_BSTREAM(p, pConnSpec[i].supTimeout);
|
||||
UINT16_TO_BSTREAM(p, pConnSpec[i].minCeLen);
|
||||
UINT16_TO_BSTREAM(p, pConnSpec[i].maxCeLen);
|
||||
}
|
||||
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE periodic advertising create sync command.
|
||||
*
|
||||
* \param options Options.
|
||||
* \param advSid Advertising SID.
|
||||
* \param advAddrType Advertiser address type.
|
||||
* \param pAdvAddr Advertiser address.
|
||||
* \param skip Number of periodic advertising packets that can be skipped after
|
||||
* successful receive.
|
||||
* \param syncTimeout Synchronization timeout.
|
||||
* \param unused Reserved for future use (must be zero).
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLePerAdvCreateSyncCmd(uint8_t options, uint8_t advSid, uint8_t advAddrType,
|
||||
uint8_t *pAdvAddr, uint16_t skip, uint16_t syncTimeout, uint8_t unused)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_PER_ADV_CREATE_SYNC, HCI_LEN_LE_PER_ADV_CREATE_SYNC)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, options);
|
||||
UINT8_TO_BSTREAM(p, advSid);
|
||||
UINT8_TO_BSTREAM(p, advAddrType);
|
||||
BDA_TO_BSTREAM(p, pAdvAddr);
|
||||
UINT16_TO_BSTREAM(p, skip);
|
||||
UINT16_TO_BSTREAM(p, syncTimeout);
|
||||
UINT8_TO_BSTREAM(p, unused);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE periodic advertising create sync cancel command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLePerAdvCreateSyncCancelCmd(void)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_PER_ADV_CREATE_SYNC_CANCEL, HCI_LEN_LE_PER_ADV_CREATE_SYNC_CANCEL)) != NULL)
|
||||
{
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE periodic advertising terminate sync command.
|
||||
*
|
||||
* \param syncHandle Sync handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLePerAdvTerminateSyncCmd(uint16_t syncHandle)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_PER_ADV_TERMINATE_SYNC, HCI_LEN_LE_PER_ADV_TERMINATE_SYNC)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, syncHandle);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE add device to periodic advertiser list command.
|
||||
*
|
||||
* \param advAddrType Advertiser address type.
|
||||
* \param pAdvAddr Advertiser address.
|
||||
* \param advSid Advertising SID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeAddDeviceToPerAdvListCmd(uint8_t advAddrType, uint8_t *pAdvAddr, uint8_t advSid)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_ADD_DEV_PER_ADV_LIST, HCI_LEN_LE_ADD_DEV_PER_ADV_LIST)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, advAddrType);
|
||||
BDA_TO_BSTREAM(p, pAdvAddr);
|
||||
UINT8_TO_BSTREAM(p, advSid);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE remove device from periodic advertiser list command.
|
||||
*
|
||||
* \param advAddrType Advertiser address type.
|
||||
* \param pAdvAddr Advertiser address.
|
||||
* \param advSid Advertising SID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeRemoveDeviceFromPerAdvListCmd(uint8_t advAddrType, uint8_t *pAdvAddr, uint8_t advSid)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_REMOVE_DEV_PER_ADV_LIST, HCI_LEN_LE_REMOVE_DEV_PER_ADV_LIST)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, advAddrType);
|
||||
BDA_TO_BSTREAM(p, pAdvAddr);
|
||||
UINT8_TO_BSTREAM(p, advSid);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE clear periodic advertiser list command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeClearPerAdvListCmd(void)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_CLEAR_PER_ADV_LIST, HCI_LEN_LE_CLEAR_PER_ADV_LIST)) != NULL)
|
||||
{
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE read periodic advertiser size command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeReadPerAdvListSizeCmd(void)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_READ_PER_ADV_LIST_SIZE, HCI_LEN_LE_READ_PER_ADV_LIST_SIZE)) != NULL)
|
||||
{
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE read transmit power command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeReadTxPower(void)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_READ_TX_POWER, HCI_LEN_LE_READ_TX_POWER)) != NULL)
|
||||
{
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE read RF path compensation command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeReadRfPathComp(void)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_READ_RF_PATH_COMP, HCI_LEN_LE_READ_RF_PATH_COMP)) != NULL)
|
||||
{
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE write RF path compensation command.
|
||||
*
|
||||
* \param txPathComp RF transmit path compensation value.
|
||||
* \param rxPathComp RF receive path compensation value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeWriteRfPathComp(int16_t txPathComp, int16_t rxPathComp)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_WRITE_RF_PATH_COMP, HCI_LEN_LE_WRITE_RF_PATH_COMP)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, txPathComp);
|
||||
UINT16_TO_BSTREAM(p, rxPathComp);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
Vendored
+165
@@ -0,0 +1,165 @@
|
||||
/* Copyright (c) 2009-2019 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI Constant Tone Extension (CTE) command module.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "hci_cmd.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set connection CTE receive parameters command.
|
||||
*
|
||||
* \param connHandle Connection handle.
|
||||
* \param samplingEnable TRUE to enable Connection IQ sampling, FALSE to disable it.
|
||||
* \param slotDurations Switching and sampling slot durations to be used while receiving CTE.
|
||||
* \param switchPatternLen Number of Antenna IDs in switching pattern.
|
||||
* \param pAntennaIDs List of Antenna IDs in switching pattern.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetConnCteRxParamsCmd(uint16_t connHandle, uint8_t samplingEnable, uint8_t slotDurations,
|
||||
uint8_t switchPatternLen, uint8_t *pAntennaIDs)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_CONN_CTE_RX_PARAMS,
|
||||
HCI_LEN_LE_SET_CONN_CTE_RX_PARAMS(switchPatternLen))) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, connHandle);
|
||||
UINT8_TO_BSTREAM(p, samplingEnable);
|
||||
UINT8_TO_BSTREAM(p, slotDurations);
|
||||
UINT8_TO_BSTREAM(p, switchPatternLen);
|
||||
memcpy(p, pAntennaIDs, switchPatternLen);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set connection CTE transmit parameters command.
|
||||
*
|
||||
* \param connHandle Connection handle.
|
||||
* \param cteTypeBits Permitted CTE type bits used for transmitting CTEs requested by peer.
|
||||
* \param switchPatternLen Number of Antenna IDs in switching pattern.
|
||||
* \param pAntennaIDs List of Antenna IDs in switching pattern.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetConnCteTxParamsCmd(uint16_t connHandle, uint8_t cteTypeBits, uint8_t switchPatternLen,
|
||||
uint8_t *pAntennaIDs)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_CONN_CTE_TX_PARAMS,
|
||||
HCI_LEN_LE_SET_CONN_CTE_TX_PARAMS(switchPatternLen))) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, connHandle);
|
||||
UINT8_TO_BSTREAM(p, cteTypeBits);
|
||||
UINT8_TO_BSTREAM(p, switchPatternLen);
|
||||
memcpy(p, pAntennaIDs, switchPatternLen);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE connection CTE request enable command.
|
||||
*
|
||||
* \param connHandle Connection handle.
|
||||
* \param enable TRUE to enable CTE request for connection, FALSE to disable it.
|
||||
* \param cteReqInt CTE request interval.
|
||||
* \param reqCteLen Minimum length of CTE being requested in 8 us units.
|
||||
* \param reqCteType Requested CTE type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeConnCteReqEnableCmd(uint16_t connHandle, uint8_t enable, uint16_t cteReqInt,
|
||||
uint8_t reqCteLen, uint8_t reqCteType)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_CONN_CTE_REQ_ENABLE,
|
||||
HCI_LEN_LE_CONN_CTE_REQ_ENABLE)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, connHandle);
|
||||
UINT8_TO_BSTREAM(p, enable);
|
||||
UINT16_TO_BSTREAM(p, cteReqInt);
|
||||
UINT8_TO_BSTREAM(p, reqCteLen);
|
||||
UINT8_TO_BSTREAM(p, reqCteType);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE connection CTE response enable command.
|
||||
*
|
||||
* \param connHandle Connection handle.
|
||||
* \param enable TRUE to enable CTE response for connection, FALSE to disable it.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeConnCteRspEnableCmd(uint16_t connHandle, uint8_t enable)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_CONN_CTE_RSP_ENABLE,
|
||||
HCI_LEN_LE_CONN_CTE_RSP_ENABLE)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, connHandle);
|
||||
UINT8_TO_BSTREAM(p, enable);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE read antenna information command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeReadAntennaInfoCmd(void)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_READ_ANTENNA_INFO, HCI_LEN_LE_READ_ANTENNA_INFO)) != NULL)
|
||||
{
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
Vendored
+171
@@ -0,0 +1,171 @@
|
||||
/* Copyright (c) 2009-2019 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI Periodic Advertising Sync Transfer (PAST) command module.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "hci_cmd.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set periodic advertising receive enable command.
|
||||
*
|
||||
* \param syncHandle Periodic sync handle.
|
||||
* \param enable TRUE to enable reports, FALSE to disable reports.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetPerAdvRcvEnableCmd(uint16_t syncHandle, uint8_t enable)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_PER_ADV_RCV_ENABLE,
|
||||
HCI_LEN_LE_SET_PER_ADV_RCV_ENABLE)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, syncHandle);
|
||||
UINT8_TO_BSTREAM(p, enable);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE periodic advertising sync transfer command.
|
||||
*
|
||||
* \param connHandle Connection handle.
|
||||
* \param serviceData Service data provided by the host.
|
||||
* \param syncHandle Periodic sync handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLePerAdvSyncTrsfCmd(uint16_t connHandle, uint16_t serviceData, uint16_t syncHandle)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_PER_ADV_SYNC_TRANSFER,
|
||||
HCI_LEN_LE_PER_ADV_SYNC_TRANSFER)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, connHandle);
|
||||
UINT16_TO_BSTREAM(p, serviceData);
|
||||
UINT16_TO_BSTREAM(p, syncHandle);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set periodic advertising set info transfer command.
|
||||
*
|
||||
* \param connHandle Connection handle.
|
||||
* \param serviceData Service data provided by the host.
|
||||
* \param advHandle Handle to identify an advertising set.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLePerAdvSetInfoTrsfCmd(uint16_t connHandle, uint16_t serviceData, uint8_t advHandle)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_PER_ADV_SET_INFO_TRANSFER,
|
||||
HCI_LEN_LE_PER_ADV_SET_INFO_TRANSFER)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, connHandle);
|
||||
UINT16_TO_BSTREAM(p, serviceData);
|
||||
UINT8_TO_BSTREAM(p, advHandle);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set periodic advertising sync transfer parameters command.
|
||||
*
|
||||
* \param connHandle Connection handle.
|
||||
* \param mode Periodic sync advertising sync transfer mode.
|
||||
* \param skip The number of periodic advertising packets that can be skipped after
|
||||
* a successful receive.
|
||||
* \param syncTimeout Synchronization timeout for the periodic advertising.
|
||||
* \param cteType Constant tone extension type(Used in AoD/AoA).
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetPerAdvSyncTrsfParamsCmd(uint16_t connHandle, uint8_t mode, uint16_t skip,
|
||||
uint16_t syncTimeout, uint8_t cteType)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_PAST_PARAM, HCI_LEN_LE_SET_PAST_PARAM)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, connHandle);
|
||||
UINT8_TO_BSTREAM(p, mode);
|
||||
UINT16_TO_BSTREAM(p, skip);
|
||||
UINT16_TO_BSTREAM(p, syncTimeout);
|
||||
UINT8_TO_BSTREAM(p, cteType);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI LE set default periodic advertising sync transfer parameters command.
|
||||
*
|
||||
* \param mode Periodic sync advertising sync transfer mode.
|
||||
* \param skip The number of periodic advertising packets that can be skipped after
|
||||
* a successful receive.
|
||||
* \param syncTimeout Synchronization timeout for the periodic advertising.
|
||||
* \param cteType Constant tone extension type(Used in AoD/AoA).
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetDefaultPerAdvSyncTrsfParamsCmd(uint8_t mode, uint16_t skip, uint16_t syncTimeout,
|
||||
uint8_t cteType)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_DEFAULT_PAST_PARAM,
|
||||
HCI_LEN_LE_SET_DEFAULT_PAST_PARAM)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, mode);
|
||||
UINT16_TO_BSTREAM(p, skip);
|
||||
UINT16_TO_BSTREAM(p, syncTimeout);
|
||||
UINT8_TO_BSTREAM(p, cteType);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
/* Copyright (c) 2009-2019 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI PHY command module.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "hci_cmd.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI read PHY command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeReadPhyCmd(uint16_t handle)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_READ_PHY, HCI_LEN_LE_READ_PHY)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI set default PHY command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetDefaultPhyCmd(uint8_t allPhys, uint8_t txPhys, uint8_t rxPhys)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_DEF_PHY, HCI_LEN_LE_SET_DEF_PHY)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT8_TO_BSTREAM(p, allPhys);
|
||||
UINT8_TO_BSTREAM(p, txPhys);
|
||||
UINT8_TO_BSTREAM(p, rxPhys);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI set PHY command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciLeSetPhyCmd(uint16_t handle, uint8_t allPhys, uint8_t txPhys, uint8_t rxPhys, uint16_t phyOptions)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_PHY, HCI_LEN_LE_SET_PHY)) != NULL)
|
||||
{
|
||||
p = pBuf + HCI_CMD_HDR_LEN;
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT8_TO_BSTREAM(p, allPhys);
|
||||
UINT8_TO_BSTREAM(p, txPhys);
|
||||
UINT8_TO_BSTREAM(p, rxPhys);
|
||||
UINT16_TO_BSTREAM(p, phyOptions);
|
||||
hciCmdSend(pBuf);
|
||||
}
|
||||
}
|
||||
+924
@@ -0,0 +1,924 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file hci_core.c
|
||||
*
|
||||
* \brief HCI core module, platform independent functions.
|
||||
*
|
||||
* $Date: 2017-03-10 14:08:37 -0600 (Fri, 10 Mar 2017) $
|
||||
* $Revision: 11501 $
|
||||
*
|
||||
* Copyright (c) 2009-2017 ARM Ltd., all rights reserved.
|
||||
* ARM Ltd. confidential and proprietary.
|
||||
*
|
||||
* IMPORTANT. Your use of this file is governed by a Software License Agreement
|
||||
* ("Agreement") that must be accepted in order to download or otherwise receive a
|
||||
* copy of this file. You may not use or copy this file for any purpose other than
|
||||
* as described in the Agreement. If you do not agree to all of the terms of the
|
||||
* Agreement do not use this file and delete all copies in your possession or control;
|
||||
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
|
||||
* to any use, copying or further distribution of this software.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "bda.h"
|
||||
#include "bstream.h"
|
||||
#include "hci_core.h"
|
||||
#include "hci_tr.h"
|
||||
#include "hci_cmd.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_main.h"
|
||||
#include "l2c_defs.h"
|
||||
#include "am_mcu_apollo.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Default ACL buffer flow control watermark levels */
|
||||
#ifndef HCI_ACL_QUEUE_HI
|
||||
#define HCI_ACL_QUEUE_HI 5 /* Disable flow when this many buffers queued */
|
||||
#endif
|
||||
#ifndef HCI_ACL_QUEUE_LO
|
||||
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
|
||||
#define HCI_ACL_QUEUE_LO 3 /* Enable flow when this many buffers queued */
|
||||
#else
|
||||
#define HCI_ACL_QUEUE_LO 1 /* Enable flow when this many buffers queued */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Default maximum ACL packet size for reassembly */
|
||||
#ifndef HCI_MAX_RX_ACL_LEN
|
||||
#define HCI_MAX_RX_ACL_LEN HCI_ACL_DEFAULT_LEN
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Event mask */
|
||||
const uint8_t hciEventMask[HCI_EVT_MASK_LEN] =
|
||||
{
|
||||
HCI_EVT_MASK_DISCONNECT_CMPL | /* Byte 0 */
|
||||
HCI_EVT_MASK_ENC_CHANGE, /* Byte 0 */
|
||||
HCI_EVT_MASK_READ_REMOTE_VER_INFO_CMPL | /* Byte 1 */
|
||||
HCI_EVT_MASK_HW_ERROR, /* Byte 1 */
|
||||
0, /* Byte 2 */
|
||||
HCI_EVT_MASK_DATA_BUF_OVERFLOW, /* Byte 3 */
|
||||
0, /* Byte 4 */
|
||||
HCI_EVT_MASK_ENC_KEY_REFRESH_CMPL, /* Byte 5 */
|
||||
0, /* Byte 6 */
|
||||
HCI_EVT_MASK_LE_META /* Byte 7 */
|
||||
};
|
||||
|
||||
/* LE event mask */
|
||||
const uint8_t hciLeEventMask[HCI_LE_EVT_MASK_LEN] =
|
||||
{
|
||||
HCI_EVT_MASK_LE_CONN_CMPL_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_ADV_REPORT_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_CONN_UPDATE_CMPL_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_READ_REMOTE_FEAT_CMPL_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_LTK_REQ_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_REMOTE_CONN_PARAM_REQ_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_DATA_LEN_CHANGE_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_READ_LOCAL_P256_PUB_KEY_CMPL, /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_GENERATE_DHKEY_CMPL | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_ENHANCED_CONN_CMPL_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_DIRECT_ADV_REPORT_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_PHY_UPDATE_CMPL_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_EXT_ADV_REPORT_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_PER_ADV_SYNC_EST_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_PER_ADV_REPORT_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_PER_ADV_SYNC_LOST_EVT, /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_SCAN_TIMEOUT_EVT | /* Byte 2 */
|
||||
HCI_EVT_MASK_LE_ADV_SET_TERM_EVT | /* Byte 2 */
|
||||
HCI_EVT_MASK_LE_SCAN_REQ_RCVD_EVT | /* Byte 2 */
|
||||
HCI_EVT_MASK_LE_CH_SEL_ALGO_EVT, /* Byte 2 */
|
||||
0, /* Byte 3 */
|
||||
0, /* Byte 4 */
|
||||
0, /* Byte 5 */
|
||||
0, /* Byte 6 */
|
||||
0 /* Byte 7 */
|
||||
};
|
||||
|
||||
/* event mask page 2 */
|
||||
const uint8_t hciEventMaskPage2[HCI_EVT_MASK_PAGE_2_LEN] =
|
||||
{
|
||||
0, /* Byte 0 */
|
||||
0, /* Byte 1 */
|
||||
HCI_EVT_MASK_AUTH_PAYLOAD_TIMEOUT, /* Byte 2 */
|
||||
0, /* Byte 3 */
|
||||
0, /* Byte 4 */
|
||||
0, /* Byte 5 */
|
||||
0, /* Byte 6 */
|
||||
0 /* Byte 7 */
|
||||
};
|
||||
|
||||
/* LE supported features configuration mask */
|
||||
uint32_t hciLeSupFeatCfg =
|
||||
HCI_LE_SUP_FEAT_ENCRYPTION | /* LE Encryption */
|
||||
HCI_LE_SUP_FEAT_CONN_PARAM_REQ_PROC | /* Connection Parameters Request Procedure */
|
||||
HCI_LE_SUP_FEAT_EXT_REJECT_IND | /* Extended Reject Indication */
|
||||
HCI_LE_SUP_FEAT_SLV_INIT_FEAT_EXCH | /* Slave-initiated Features Exchange */
|
||||
HCI_LE_SUP_FEAT_LE_PING | /* LE Ping */
|
||||
HCI_LE_SUP_FEAT_DATA_LEN_EXT | /* LE Data Packet Length Extension */
|
||||
HCI_LE_SUP_FEAT_PRIVACY | /* LL Privacy */
|
||||
HCI_LE_SUP_FEAT_EXT_SCAN_FILT_POLICY | /* Extended Scanner Filter Policies */
|
||||
HCI_LE_SUP_FEAT_LE_2M_PHY | /* LE 2M PHY supported */
|
||||
HCI_LE_SUP_FEAT_STABLE_MOD_IDX_TRANSMITTER | /* Stable Modulation Index - Transmitter supported */
|
||||
HCI_LE_SUP_FEAT_STABLE_MOD_IDX_RECEIVER | /* Stable Modulation Index - Receiver supported */
|
||||
HCI_LE_SUP_FEAT_LE_EXT_ADV | /* LE Extended Advertising */
|
||||
HCI_LE_SUP_FEAT_LE_PER_ADV; /* LE Periodic Advertising */
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
hciCoreCb_t hciCoreCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreConnAlloc
|
||||
*
|
||||
* \brief Allocate a connection structure.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void hciCoreConnAlloc(uint16_t handle)
|
||||
{
|
||||
uint8_t i;
|
||||
hciCoreConn_t *pConn = hciCoreCb.conn;
|
||||
|
||||
/* find available connection struct */
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
|
||||
{
|
||||
if (pConn->handle == HCI_HANDLE_NONE)
|
||||
{
|
||||
/* allocate and initialize */
|
||||
pConn->handle = handle;
|
||||
pConn->flowDisabled = FALSE;
|
||||
pConn->outBufs = 0;
|
||||
pConn->queuedBufs = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HCI_TRACE_WARN0("HCI conn struct alloc failure");
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreConnFree
|
||||
*
|
||||
* \brief Free a connection structure.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void hciCoreConnFree(uint16_t handle)
|
||||
{
|
||||
uint8_t i;
|
||||
hciCoreConn_t *pConn = hciCoreCb.conn;
|
||||
|
||||
/* find connection struct */
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
|
||||
{
|
||||
if (pConn->handle == handle)
|
||||
{
|
||||
/* free any fragmenting ACL packet */
|
||||
if (pConn->pTxAclPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pConn->pTxAclPkt);
|
||||
pConn->pTxAclPkt = NULL;
|
||||
}
|
||||
pConn->fragmenting = FALSE;
|
||||
|
||||
if (pConn->pRxAclPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pConn->pRxAclPkt);
|
||||
pConn->pRxAclPkt = NULL;
|
||||
}
|
||||
|
||||
/* free structure */
|
||||
pConn->handle = HCI_HANDLE_NONE;
|
||||
|
||||
/* optional: iterate through tx ACL queue and free any buffers with this handle */
|
||||
|
||||
/* outstanding buffers are now available; service TX data path */
|
||||
hciCoreTxReady(pConn->outBufs);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HCI_TRACE_WARN1("hciCoreConnFree handle not found:%u", handle);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreConnByHandle
|
||||
*
|
||||
* \brief Get a connection structure by handle
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
*
|
||||
* \return Pointer to connection structure or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
hciCoreConn_t *hciCoreConnByHandle(uint16_t handle)
|
||||
{
|
||||
uint8_t i;
|
||||
hciCoreConn_t *pConn = hciCoreCb.conn;
|
||||
|
||||
/* find available connection struct */
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
|
||||
{
|
||||
if (pConn->handle == handle)
|
||||
{
|
||||
return pConn;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreNextConnFragment
|
||||
*
|
||||
* \brief Get the next connection structure with a packet fragment to send.
|
||||
*
|
||||
* \return Pointer to connection structure or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static hciCoreConn_t *hciCoreNextConnFragment(void)
|
||||
{
|
||||
uint8_t i;
|
||||
hciCoreConn_t *pConn = hciCoreCb.conn;
|
||||
|
||||
/* find connection struct */
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
|
||||
{
|
||||
if (pConn->handle != HCI_HANDLE_NONE && pConn->fragmenting)
|
||||
{
|
||||
return pConn;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreConnOpen
|
||||
*
|
||||
* \brief Perform internal processing on HCI connection open.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreConnOpen(uint16_t handle)
|
||||
{
|
||||
/* allocate connection structure */
|
||||
hciCoreConnAlloc(handle);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreConnClose
|
||||
*
|
||||
* \brief Perform internal processing on HCI connection close.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreConnClose(uint16_t handle)
|
||||
{
|
||||
/* free connection structure */
|
||||
hciCoreConnFree(handle);
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreSendAclData
|
||||
*
|
||||
* \brief Send ACL data to transport.
|
||||
*
|
||||
* \param pConn Pointer to connection structure.
|
||||
* \param pData WSF buffer containing an ACL packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreSendAclData(hciCoreConn_t *pConn, uint8_t *pData)
|
||||
{
|
||||
/* increment outstanding buf count for handle */
|
||||
pConn->outBufs++;
|
||||
|
||||
/* send to transport */
|
||||
hciTrSendAclData(pConn, pData);
|
||||
|
||||
/* decrement available buffer count */
|
||||
if (hciCoreCb.availBufs > 0)
|
||||
{
|
||||
hciCoreCb.availBufs--;
|
||||
}
|
||||
else
|
||||
{
|
||||
HCI_TRACE_WARN0("hciCoreSendAclData availBufs=0");
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreTxReady
|
||||
*
|
||||
* \brief Service the TX data path.
|
||||
*
|
||||
* \param bufs Number of new buffers now available.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreTxReady(uint8_t bufs)
|
||||
{
|
||||
uint8_t *pData;
|
||||
wsfHandlerId_t handlerId;
|
||||
uint16_t handle;
|
||||
uint16_t len;
|
||||
hciCoreConn_t *pConn;
|
||||
|
||||
/* increment available buffers, with ceiling */
|
||||
if (bufs > 0)
|
||||
{
|
||||
hciCoreCb.availBufs += bufs;
|
||||
if (hciCoreCb.availBufs > hciCoreCb.numBufs)
|
||||
{
|
||||
hciCoreCb.availBufs = hciCoreCb.numBufs;
|
||||
}
|
||||
}
|
||||
|
||||
/* service ACL data queue and send as many buffers as we can */
|
||||
while (hciCoreCb.availBufs > 0)
|
||||
{
|
||||
/* send continuation of any fragments first */
|
||||
if (hciCoreTxAclContinue(NULL) == FALSE)
|
||||
{
|
||||
/* if no fragments then check for any queued ACL data */
|
||||
if ((pData = WsfMsgDeq(&hciCoreCb.aclQueue, &handlerId)) != NULL)
|
||||
{
|
||||
/* parse handle and length */
|
||||
BYTES_TO_UINT16(handle, pData);
|
||||
BYTES_TO_UINT16(len, &pData[2]);
|
||||
|
||||
/* look up conn structure and send data */
|
||||
if ((pConn = hciCoreConnByHandle(handle)) != NULL)
|
||||
{
|
||||
hciCoreTxAclStart(pConn, len, pData);
|
||||
}
|
||||
/* handle not found, connection must be closed */
|
||||
else
|
||||
{
|
||||
/* discard buffer */
|
||||
WsfMsgFree(pData);
|
||||
|
||||
HCI_TRACE_WARN1("hciCoreTxReady discarding buffer, handle=%u", handle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no fragments or queued data to send; we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreTxAclStart
|
||||
*
|
||||
* \brief Send ACL packets, start of packet.
|
||||
*
|
||||
* \param pConn Pointer to connection structure.
|
||||
* \param len ACL packet length.
|
||||
* \param pData WSF buffer containing an ACL packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreTxAclStart(hciCoreConn_t *pConn, uint16_t len, uint8_t *pData)
|
||||
{
|
||||
uint16_t hciLen;
|
||||
|
||||
/* make sure not already fragmenting on this connection */
|
||||
WSF_ASSERT(pConn->fragmenting == FALSE);
|
||||
|
||||
hciLen = HciGetBufSize();
|
||||
|
||||
HCI_TRACE_INFO1("hciCoreTxAclStart len=%u", len);
|
||||
|
||||
/* if acl len > controller acl buf len */
|
||||
if (len > hciLen)
|
||||
{
|
||||
/* store remaining acl len = acl len - hci acl buf len */
|
||||
pConn->txAclRemLen = len - hciLen;
|
||||
|
||||
/* store position for next fragment */
|
||||
pConn->pNextTxFrag = pData + hciLen;
|
||||
|
||||
/* store information required for fragmentation */
|
||||
pConn->pTxAclPkt = pData;
|
||||
pConn->fragmenting = TRUE;
|
||||
|
||||
/* set acl len in packet to hci acl buf len */
|
||||
UINT16_TO_BUF(&pData[2], hciLen);
|
||||
|
||||
/* send the packet */
|
||||
hciCoreSendAclData(pConn, pData);
|
||||
|
||||
/* send additional fragments while there are HCI buffers available */
|
||||
while ((hciCoreCb.availBufs > 0) && hciCoreTxAclContinue(pConn));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no fragmentation, just send the packet */
|
||||
hciCoreSendAclData(pConn, pData);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreTxAclContinue
|
||||
*
|
||||
* \brief Send ACL packets, continuation of fragmented packets.
|
||||
*
|
||||
* \param pConn Pointer to connection structure. If set non-NULL, then a fragment is
|
||||
* sent from this connection structure. If NULL the function finds the next
|
||||
* connection structure with a fragment to be sent.
|
||||
*
|
||||
* \return TRUE if packet sent, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t hciCoreTxAclContinue(hciCoreConn_t *pConn)
|
||||
{
|
||||
uint16_t aclLen;
|
||||
|
||||
if (pConn == NULL)
|
||||
{
|
||||
pConn = hciCoreNextConnFragment();
|
||||
}
|
||||
|
||||
if (pConn != NULL)
|
||||
{
|
||||
/* get next fragment length */
|
||||
aclLen = (pConn->txAclRemLen < HciGetBufSize()) ? pConn->txAclRemLen : HciGetBufSize();
|
||||
|
||||
if (aclLen > 0)
|
||||
{
|
||||
/* decrement remaining length */
|
||||
pConn->txAclRemLen -= aclLen;
|
||||
|
||||
/* set handle in packet with continuation bit set */
|
||||
UINT16_TO_BUF(pConn->pNextTxFrag, (pConn->handle | HCI_PB_CONTINUE));
|
||||
|
||||
/* set acl len in packet */
|
||||
UINT16_TO_BUF(&(pConn->pNextTxFrag[2]), aclLen);
|
||||
|
||||
HCI_TRACE_INFO2("hciCoreTxAclContinue aclLen=%u remLen=%u", aclLen, pConn->txAclRemLen);
|
||||
|
||||
/* send the packet */
|
||||
hciCoreSendAclData(pConn, pConn->pNextTxFrag);
|
||||
|
||||
/* set up pointer to next fragment */
|
||||
if (pConn->txAclRemLen > 0)
|
||||
{
|
||||
pConn->pNextTxFrag += aclLen;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreTxAclComplete
|
||||
*
|
||||
* \brief This function is called from the HCI transport layer when transmission of an ACL
|
||||
* packet is complete.
|
||||
*
|
||||
* \param pConn Pointer to connection structure.
|
||||
* \param pData WSF buffer containing an ACL packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreTxAclComplete(hciCoreConn_t *pConn, uint8_t *pData)
|
||||
{
|
||||
/* if fragmenting */
|
||||
if (pConn->fragmenting)
|
||||
{
|
||||
/* check if all fragments sent */
|
||||
if (pConn->txAclRemLen == 0)
|
||||
{
|
||||
/* free original buffer */
|
||||
WsfMsgFree(pConn->pTxAclPkt);
|
||||
pConn->pTxAclPkt = NULL;
|
||||
pConn->fragmenting = FALSE;
|
||||
HCI_TRACE_INFO0("hciCoreTxAclComplete free pTxAclPkt");
|
||||
}
|
||||
}
|
||||
else if (pData != NULL)
|
||||
{
|
||||
WsfMsgFree(pData);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreAclReassembly
|
||||
*
|
||||
* \brief Reassemble an ACL packet.
|
||||
*
|
||||
* \param pData Input ACL packet.
|
||||
*
|
||||
* \return pointer to ACL packet to send, or NULL if no packet to send.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *hciCoreAclReassembly(uint8_t *pData)
|
||||
{
|
||||
hciCoreConn_t *pConn;
|
||||
uint8_t *pDataRtn = NULL;
|
||||
uint16_t handle;
|
||||
uint16_t aclLen;
|
||||
uint16_t l2cLen;
|
||||
uint16_t pbf;
|
||||
bool_t freeData = TRUE;
|
||||
|
||||
BYTES_TO_UINT16(handle, pData);
|
||||
pbf = handle & HCI_PB_FLAG_MASK;
|
||||
handle &= HCI_HANDLE_MASK;
|
||||
BYTES_TO_UINT16(aclLen, &pData[2]);
|
||||
|
||||
/* look up connection */
|
||||
if ((pConn = hciCoreConnByHandle(handle)) != NULL)
|
||||
{
|
||||
/* if this is a start packet */
|
||||
if (pbf == HCI_PB_START_C2H)
|
||||
{
|
||||
/* if currently reassembled packet not complete */
|
||||
if (pConn->pRxAclPkt != NULL)
|
||||
{
|
||||
/* discard currently reassembled packet */
|
||||
WsfMsgFree(pConn->pRxAclPkt);
|
||||
pConn->pRxAclPkt = NULL;
|
||||
HCI_TRACE_WARN1("disarded hci rx pkt handle=0x%04x", handle);
|
||||
}
|
||||
|
||||
/* read l2cap length */
|
||||
if (aclLen >= L2C_HDR_LEN)
|
||||
{
|
||||
BYTES_TO_UINT16(l2cLen, &pData[4]);
|
||||
|
||||
/* check length vs. configured maximum */
|
||||
if ((l2cLen + L2C_HDR_LEN) > hciCoreCb.maxRxAclLen)
|
||||
{
|
||||
HCI_TRACE_WARN1("l2c len=0x%04x to large for reassembly", l2cLen);
|
||||
}
|
||||
/* if reassembly required */
|
||||
else if ((l2cLen + L2C_HDR_LEN) > aclLen)
|
||||
{
|
||||
/* allocate buffer to store complete l2cap packet */
|
||||
if ((pConn->pRxAclPkt = WsfMsgDataAlloc(l2cLen + L2C_HDR_LEN + HCI_ACL_HDR_LEN, 0)) != NULL)
|
||||
{
|
||||
/* store buffer for reassembly */
|
||||
pConn->pNextRxFrag = pConn->pRxAclPkt;
|
||||
|
||||
/* build acl header and copy data */
|
||||
UINT16_TO_BSTREAM(pConn->pNextRxFrag, handle);
|
||||
UINT16_TO_BSTREAM(pConn->pNextRxFrag, l2cLen + L2C_HDR_LEN);
|
||||
memcpy(pConn->pNextRxFrag, &pData[4], aclLen);
|
||||
pConn->pNextRxFrag += aclLen;
|
||||
|
||||
/* store remaining length */
|
||||
pConn->rxAclRemLen = l2cLen + L2C_HDR_LEN - aclLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* alloc failed; discard */
|
||||
HCI_TRACE_WARN1("reassembly alloc failed len=%u", (l2cLen + L2C_HDR_LEN + HCI_ACL_HDR_LEN));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no reassembly required, pData is ready to go */
|
||||
pDataRtn = pData;
|
||||
freeData = FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* invalid l2cap packet; discard */
|
||||
HCI_TRACE_WARN1("invalid l2c pkt aclLen=%u", aclLen);
|
||||
}
|
||||
}
|
||||
/* else if this is a continuation packet */
|
||||
else if (pbf == HCI_PB_CONTINUE)
|
||||
{
|
||||
/* if expecting a continuation */
|
||||
if (pConn->pRxAclPkt != NULL)
|
||||
{
|
||||
if (aclLen <= pConn->rxAclRemLen)
|
||||
{
|
||||
/* copy data to start of next fragment */
|
||||
memcpy(pConn->pNextRxFrag, &pData[HCI_ACL_HDR_LEN], aclLen);
|
||||
pConn->pNextRxFrag += aclLen;
|
||||
|
||||
/* update remaining length */
|
||||
pConn->rxAclRemLen -= aclLen;
|
||||
|
||||
/* if reassembly complete return reassembled packet */
|
||||
if (pConn->rxAclRemLen == 0)
|
||||
{
|
||||
pDataRtn = pConn->pRxAclPkt;
|
||||
pConn->pRxAclPkt = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HCI_TRACE_WARN2("continuation pkt too long len=%u RemLen=%u", aclLen, pConn->rxAclRemLen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HCI_TRACE_WARN1("unexpected continuation pkt handle=0x%04x", handle);
|
||||
}
|
||||
}
|
||||
/* else unknown packet type */
|
||||
else
|
||||
{
|
||||
HCI_TRACE_WARN1("unknown pb flags=0x%04x", pbf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* connection not found */
|
||||
HCI_TRACE_WARN1("pkt rcvd on unknown handle=0x%04x", (handle & HCI_HANDLE_MASK));
|
||||
}
|
||||
|
||||
if (freeData)
|
||||
{
|
||||
WsfMsgFree(pData);
|
||||
}
|
||||
|
||||
return pDataRtn;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreTxAclDataFragmented
|
||||
*
|
||||
* \brief Check if a TX ACL packet is being fragmented.
|
||||
*
|
||||
* \param pContext Connection context.
|
||||
*
|
||||
* \return TRUE if fragmenting a TX ACL packet, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t hciCoreTxAclDataFragmented(hciCoreConn_t *pConn)
|
||||
{
|
||||
return pConn->fragmenting;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciCoreInit
|
||||
*
|
||||
* \brief HCI core initialization.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciCoreInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
WSF_QUEUE_INIT(&hciCoreCb.aclQueue);
|
||||
|
||||
for (i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
hciCoreCb.conn[i].handle = HCI_HANDLE_NONE;
|
||||
}
|
||||
|
||||
hciCoreCb.maxRxAclLen = HCI_MAX_RX_ACL_LEN;
|
||||
hciCoreCb.aclQueueHi = HCI_ACL_QUEUE_HI;
|
||||
hciCoreCb.aclQueueLo = HCI_ACL_QUEUE_LO;
|
||||
|
||||
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
|
||||
if (APOLLO3_GE_B0)
|
||||
{
|
||||
// B0 has only less internal ACL buffers
|
||||
hciCoreCb.aclQueueHi--;
|
||||
hciCoreCb.aclQueueLo--;
|
||||
}
|
||||
#endif
|
||||
hciCoreCb.extResetSeq = NULL;
|
||||
|
||||
hciCoreInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciResetSequence
|
||||
*
|
||||
* \brief Initiate an HCI reset sequence.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciResetSequence(void)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
wsfHandlerId_t handlerId;
|
||||
|
||||
uint8_t i;
|
||||
hciCoreConn_t *pConn = hciCoreCb.conn;
|
||||
|
||||
// free any pending incoming packets
|
||||
while ((pBuf = WsfMsgDeq(&hciCb.rxQueue, &handlerId)) != NULL)
|
||||
{
|
||||
/* Free buffer */
|
||||
WsfMsgFree(pBuf);
|
||||
}
|
||||
|
||||
HCI_TRACE_INFO0("reset sequence");
|
||||
// free any pending tx packets
|
||||
/* find connection struct */
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
|
||||
{
|
||||
/* free any fragmenting ACL packet */
|
||||
if (pConn->pTxAclPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pConn->pTxAclPkt);
|
||||
pConn->pTxAclPkt = NULL;
|
||||
}
|
||||
pConn->fragmenting = FALSE;
|
||||
|
||||
if (pConn->pRxAclPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pConn->pRxAclPkt);
|
||||
pConn->pRxAclPkt = NULL;
|
||||
}
|
||||
|
||||
/* free structure */
|
||||
pConn->handle = HCI_HANDLE_NONE;
|
||||
|
||||
/* optional: iterate through tx ACL queue and free any buffers with this handle */
|
||||
|
||||
/* outstanding buffers are now available; service TX data path */
|
||||
hciCoreTxReady(pConn->outBufs);
|
||||
|
||||
}
|
||||
|
||||
/* set resetting state */
|
||||
hciCb.resetting = TRUE;
|
||||
|
||||
/* start the reset sequence */
|
||||
hciCoreResetStart();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciSetMaxRxAclLen
|
||||
*
|
||||
* \brief Set the maximum reassembled RX ACL packet length. Minimum value is 27.
|
||||
*
|
||||
* \param len ACL packet length.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciSetMaxRxAclLen(uint16_t len)
|
||||
{
|
||||
hciCoreCb.maxRxAclLen = len;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciSetAclQueueWatermarks
|
||||
*
|
||||
* \brief Set TX ACL queue high and low watermarks.
|
||||
*
|
||||
* \param queueHi Disable flow on a connection when this many ACL buffers are queued.
|
||||
* queueLo Disable flow on a connection when this many ACL buffers are queued.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciSetAclQueueWatermarks(uint8_t queueHi, uint8_t queueLo)
|
||||
{
|
||||
hciCoreCb.aclQueueHi = queueHi;
|
||||
hciCoreCb.aclQueueLo = queueLo;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciSetLeSupFeat
|
||||
*
|
||||
* \brief Set LE supported features configuration mask.
|
||||
*
|
||||
* \param feat Feature bit to set or clear
|
||||
* \param flag TRUE to set feature bit and FALSE to clear it
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciSetLeSupFeat(uint32_t feat, bool_t flag)
|
||||
{
|
||||
/* if asked to include feature */
|
||||
if (flag)
|
||||
{
|
||||
/* set feature bit */
|
||||
hciLeSupFeatCfg |= feat;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* clear feature bit */
|
||||
hciLeSupFeatCfg &= ~feat;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciSendAclData
|
||||
*
|
||||
* \brief Send data from the stack to HCI.
|
||||
*
|
||||
* \param pData WSF buffer containing an ACL packet
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciSendAclData(uint8_t *pData)
|
||||
{
|
||||
uint16_t handle;
|
||||
uint16_t len;
|
||||
hciCoreConn_t *pConn;
|
||||
|
||||
/* parse handle and length */
|
||||
BYTES_TO_UINT16(handle, pData);
|
||||
BYTES_TO_UINT16(len, &pData[2]);
|
||||
|
||||
/* look up connection structure */
|
||||
if ((pConn = hciCoreConnByHandle(handle)) != NULL)
|
||||
{
|
||||
/* if queue empty and buffers available */
|
||||
if (WsfQueueEmpty(&hciCoreCb.aclQueue) && hciCoreCb.availBufs > 0)
|
||||
{
|
||||
/* send data */
|
||||
hciCoreTxAclStart(pConn, len, pData);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* queue data - message handler ID 'handerId' not used */
|
||||
WsfMsgEnq(&hciCoreCb.aclQueue, 0, pData);
|
||||
}
|
||||
|
||||
/* increment buffer queue count for this connection with consideration for HCI fragmentation */
|
||||
pConn->queuedBufs += ((len - 1) / HciGetBufSize()) + 1;
|
||||
|
||||
/* manage flow control to stack */
|
||||
if (pConn->queuedBufs >= hciCoreCb.aclQueueHi && pConn->flowDisabled == FALSE)
|
||||
{
|
||||
pConn->flowDisabled = TRUE;
|
||||
(*hciCb.flowCback)(handle, TRUE);
|
||||
}
|
||||
}
|
||||
/* connection not found, connection must be closed */
|
||||
else
|
||||
{
|
||||
/* discard buffer */
|
||||
WsfMsgFree(pData);
|
||||
|
||||
HCI_TRACE_WARN1("HciSendAclData discarding buffer, handle=%u", handle);
|
||||
}
|
||||
}
|
||||
Vendored
+391
@@ -0,0 +1,391 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file hci_core_ps.c
|
||||
*
|
||||
* \brief HCI core platform-specific module for dual-chip.
|
||||
*
|
||||
* $Date: 2016-12-28 16:12:14 -0600 (Wed, 28 Dec 2016) $
|
||||
* $Revision: 10805 $
|
||||
*
|
||||
* Copyright (c) 2009-2017 ARM Ltd., all rights reserved.
|
||||
* ARM Ltd. confidential and proprietary.
|
||||
*
|
||||
* IMPORTANT. Your use of this file is governed by a Software License Agreement
|
||||
* ("Agreement") that must be accepted in order to download or otherwise receive a
|
||||
* copy of this file. You may not use or copy this file for any purpose other than
|
||||
* as described in the Agreement. If you do not agree to all of the terms of the
|
||||
* Agreement do not use this file and delete all copies in your possession or control;
|
||||
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
|
||||
* to any use, copying or further distribution of this software.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "bda.h"
|
||||
#include "bstream.h"
|
||||
#include "hci_core.h"
|
||||
#include "hci_tr.h"
|
||||
#include "hci_cmd.h"
|
||||
#include "hci_evt.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreInit
|
||||
*
|
||||
* \brief HCI core initialization.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreInit(void)
|
||||
{
|
||||
hciCmdInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreNumCmplPkts
|
||||
*
|
||||
* \brief Handle an HCI Number of Completed Packets event.
|
||||
*
|
||||
* \param pMsg Message containing the HCI Number of Completed Packets event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreNumCmplPkts(uint8_t *pMsg)
|
||||
{
|
||||
uint8_t numHandles;
|
||||
uint16_t bufs;
|
||||
uint16_t handle;
|
||||
uint8_t availBufs = 0;
|
||||
hciCoreConn_t *pConn;
|
||||
|
||||
/* parse number of handles */
|
||||
BSTREAM_TO_UINT8(numHandles, pMsg);
|
||||
|
||||
/* for each handle in event */
|
||||
while (numHandles-- > 0)
|
||||
{
|
||||
/* parse handle and number of buffers */
|
||||
BSTREAM_TO_UINT16(handle, pMsg);
|
||||
BSTREAM_TO_UINT16(bufs, pMsg);
|
||||
|
||||
if ((pConn = hciCoreConnByHandle(handle)) != NULL)
|
||||
{
|
||||
/* decrement outstanding buffer count to controller */
|
||||
pConn->outBufs -= (uint8_t) bufs;
|
||||
|
||||
/* decrement queued buffer count for this connection */
|
||||
pConn->queuedBufs -= (uint8_t) bufs;
|
||||
|
||||
/* increment available buffer count */
|
||||
availBufs += (uint8_t) bufs;
|
||||
|
||||
/* call flow control callback */
|
||||
if (pConn->flowDisabled && pConn->queuedBufs <= hciCoreCb.aclQueueLo)
|
||||
{
|
||||
pConn->flowDisabled = FALSE;
|
||||
(*hciCb.flowCback)(handle, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* service TX data path */
|
||||
hciCoreTxReady(availBufs);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreRecv
|
||||
*
|
||||
* \brief Send a received HCI event or ACL packet to the HCI event handler.
|
||||
*
|
||||
* \param msgType Message type: HCI_ACL_TYPE or HCI_EVT_TYPE.
|
||||
* \param pCoreRecvMsg Pointer to received message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreRecv(uint8_t msgType, uint8_t *pCoreRecvMsg)
|
||||
{
|
||||
/* dump event for protocol analysis */
|
||||
if (msgType == HCI_EVT_TYPE)
|
||||
{
|
||||
HCI_PDUMP_EVT(*(pCoreRecvMsg + 1) + HCI_EVT_HDR_LEN, pCoreRecvMsg);
|
||||
}
|
||||
else if (msgType == HCI_ACL_TYPE)
|
||||
{
|
||||
HCI_PDUMP_RX_ACL(*(pCoreRecvMsg + 2) + HCI_ACL_HDR_LEN, pCoreRecvMsg);
|
||||
}
|
||||
|
||||
/* queue buffer */
|
||||
WsfMsgEnq(&hciCb.rxQueue, (wsfHandlerId_t) msgType, pCoreRecvMsg);
|
||||
|
||||
/* set event */
|
||||
WsfSetEvent(hciCb.handlerId, HCI_EVT_RX);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciCoreHandler
|
||||
*
|
||||
* \brief WSF event handler for core HCI.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciCoreHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
wsfHandlerId_t handlerId;
|
||||
|
||||
/* Handle message */
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
/* Handle HCI command timeout */
|
||||
if (pMsg->event == HCI_MSG_CMD_TIMEOUT)
|
||||
{
|
||||
hciCmdTimeout(pMsg);
|
||||
}
|
||||
}
|
||||
/* Handle events */
|
||||
else if (event & HCI_EVT_RX)
|
||||
{
|
||||
/* Process rx queue */
|
||||
while ((pBuf = WsfMsgDeq(&hciCb.rxQueue, &handlerId)) != NULL)
|
||||
{
|
||||
/* Handle incoming HCI events */
|
||||
if (handlerId == HCI_EVT_TYPE)
|
||||
{
|
||||
/* Parse/process events */
|
||||
hciEvtProcessMsg(pBuf);
|
||||
|
||||
/* Handle events during reset sequence */
|
||||
if (hciCb.resetting)
|
||||
{
|
||||
hciCoreResetSequence(pBuf);
|
||||
}
|
||||
|
||||
/* Free buffer */
|
||||
WsfMsgFree(pBuf);
|
||||
}
|
||||
/* Handle ACL data */
|
||||
else
|
||||
{
|
||||
/* Reassemble */
|
||||
if ((pBuf = hciCoreAclReassembly(pBuf)) != NULL)
|
||||
{
|
||||
/* Call ACL callback; client will free buffer */
|
||||
hciCb.aclCback(pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetBdAddr
|
||||
*
|
||||
* \brief Return a pointer to the BD address of this device.
|
||||
*
|
||||
* \return Pointer to the BD address.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *HciGetBdAddr(void)
|
||||
{
|
||||
return hciCoreCb.bdAddr;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetWhiteListSize
|
||||
*
|
||||
* \brief Return the white list size.
|
||||
*
|
||||
* \return White list size.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t HciGetWhiteListSize(void)
|
||||
{
|
||||
return hciCoreCb.whiteListSize;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetAdvTxPwr
|
||||
*
|
||||
* \brief Return the advertising transmit power.
|
||||
*
|
||||
* \return Advertising transmit power.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
int8_t HciGetAdvTxPwr(void)
|
||||
{
|
||||
return hciCoreCb.advTxPwr;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetBufSize
|
||||
*
|
||||
* \brief Return the ACL buffer size supported by the controller.
|
||||
*
|
||||
* \return ACL buffer size.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t HciGetBufSize(void)
|
||||
{
|
||||
return hciCoreCb.bufSize;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetNumBufs
|
||||
*
|
||||
* \brief Return the number of ACL buffers supported by the controller.
|
||||
*
|
||||
* \return Number of ACL buffers.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t HciGetNumBufs(void)
|
||||
{
|
||||
return hciCoreCb.numBufs;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetSupStates
|
||||
*
|
||||
* \brief Return the states supported by the controller.
|
||||
*
|
||||
* \return Pointer to the supported states array.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *HciGetSupStates(void)
|
||||
{
|
||||
return hciCoreCb.leStates;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetLeSupFeat
|
||||
*
|
||||
* \brief Return the LE supported features supported by the controller.
|
||||
*
|
||||
* \return Supported features.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t HciGetLeSupFeat(void)
|
||||
{
|
||||
// disable LL connection parameter update feature for a better
|
||||
// interoperability with Android phones (especially older Android OS).
|
||||
return hciCoreCb.leSupFeat & ~HCI_LE_SUP_FEAT_CONN_PARAM_REQ_PROC;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetMaxRxAclLen
|
||||
*
|
||||
* \brief Get the maximum reassembled RX ACL packet length.
|
||||
*
|
||||
* \return ACL packet length.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t HciGetMaxRxAclLen(void)
|
||||
{
|
||||
return hciCoreCb.maxRxAclLen;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetResolvingListSize
|
||||
*
|
||||
* \brief Return the resolving list size.
|
||||
*
|
||||
* \return resolving list size.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t HciGetResolvingListSize(void)
|
||||
{
|
||||
return hciCoreCb.resListSize;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciLlPrivacySupported
|
||||
*
|
||||
* \brief Whether LL Privacy is supported.
|
||||
*
|
||||
* \return TRUE if LL Privacy is supported. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t HciLlPrivacySupported(void)
|
||||
{
|
||||
return (hciCoreCb.resListSize > 0) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetMaxAdvDataLen
|
||||
*
|
||||
* \brief Get the maximum advertisement (or scan response) data length supported by the Controller.
|
||||
*
|
||||
* \return Maximum advertisement data length.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t HciGetMaxAdvDataLen(void)
|
||||
{
|
||||
return hciCoreCb.maxAdvDataLen;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetNumSupAdvSets
|
||||
*
|
||||
* \brief Get the maximum number of advertising sets supported by the Controller.
|
||||
*
|
||||
* \return Maximum number of advertising sets.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t HciGetNumSupAdvSets(void)
|
||||
{
|
||||
return hciCoreCb.numSupAdvSets;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciLeAdvExtSupported
|
||||
*
|
||||
* \brief Whether LE Advertising Extensions is supported.
|
||||
*
|
||||
* \return TRUE if LE Advertising Extensions is supported. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t HciLeAdvExtSupported(void)
|
||||
{
|
||||
return (hciCoreCb.numSupAdvSets > 0) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciGetPerAdvListSize
|
||||
*
|
||||
* \brief Return the periodic advertising list size.
|
||||
*
|
||||
* \return periodic advertising list size.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t HciGetPerAdvListSize(void)
|
||||
{
|
||||
return hciCoreCb.perAdvListSize;
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file hci_core_ps.h
|
||||
*
|
||||
* \brief HCI core platform-specific interfaces for dual-chip.
|
||||
*
|
||||
* $Date: 2016-12-28 16:12:14 -0600 (Wed, 28 Dec 2016) $
|
||||
* $Revision: 10805 $
|
||||
*
|
||||
* Copyright (c) 2013-2017 ARM Ltd., all rights reserved.
|
||||
* ARM Ltd. confidential and proprietary.
|
||||
*
|
||||
* IMPORTANT. Your use of this file is governed by a Software License Agreement
|
||||
* ("Agreement") that must be accepted in order to download or otherwise receive a
|
||||
* copy of this file. You may not use or copy this file for any purpose other than
|
||||
* as described in the Agreement. If you do not agree to all of the terms of the
|
||||
* Agreement do not use this file and delete all copies in your possession or control;
|
||||
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
|
||||
* to any use, copying or further distribution of this software.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef HCI_CORE_PS_H
|
||||
#define HCI_CORE_PS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void hciCoreResetSequence(uint8_t *pMsg);
|
||||
void hciCoreNumCmplPkts(uint8_t *pMsg);
|
||||
void hciCoreRecv(uint8_t msgType, uint8_t *pCoreRecvMsg);
|
||||
uint8_t hciCoreVsCmdCmplRcvd(uint16_t opcode, uint8_t *pMsg, uint8_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* HCI_CORE_PS_H */
|
||||
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
//*****************************************************************************
|
||||
//
|
||||
//! @file hci_drv_apollo.h
|
||||
//!
|
||||
//! @brief Additional header information for the Apollo implementation of HCI.
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
#ifndef HCI_DRV_APOLLO_H
|
||||
#define HCI_DRV_APOLLO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Errors
|
||||
//
|
||||
//*****************************************************************************
|
||||
#define HCI_DRV_SPECIFIC_ERROR_START 0x09000000
|
||||
typedef enum
|
||||
{
|
||||
HCI_DRV_TRANSMIT_QUEUE_FULL = HCI_DRV_SPECIFIC_ERROR_START,
|
||||
HCI_DRV_TX_PACKET_TOO_LARGE,
|
||||
HCI_DRV_RX_PACKET_TOO_LARGE,
|
||||
HCI_DRV_BLE_STACK_UNABLE_TO_ACCEPT_PACKET,
|
||||
HCI_DRV_PACKET_TRANSMIT_FAILED,
|
||||
HCI_DRV_IRQ_STUCK_HIGH,
|
||||
HCI_DRV_TOO_MANY_PACKETS,
|
||||
}
|
||||
hci_drv_error_t;
|
||||
|
||||
typedef void (*hci_drv_error_handler_t)(uint32_t ui32Error);
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Function prototypes.
|
||||
//
|
||||
//*****************************************************************************
|
||||
extern void HciDrvUartEnable(void);
|
||||
extern void HciDrvUartDisable(void);
|
||||
extern void HciDrvUartFlowOff(void);
|
||||
extern void HciDrvUartFlowOn(void);
|
||||
extern void HciDrvUartPause(void);
|
||||
extern void HciDrvUartUnpause(void);
|
||||
extern bool HciDrvUartSafeShutdown(void);
|
||||
|
||||
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
|
||||
extern void HciDrvRadioBoot(bool bColdBoot);
|
||||
#else
|
||||
extern void HciDrvRadioBoot(uint32_t ui32UartModule);
|
||||
#endif
|
||||
|
||||
extern void HciDrvRadioShutdown(void);
|
||||
extern void HciDrvUartISR(uint32_t ui32Status);
|
||||
extern bool_t HciDataReadyISR(void);
|
||||
extern void HciDrvIntService(void);
|
||||
extern void HciDrvGPIOService(void);
|
||||
extern void HciDrvHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
extern void HciDrvErrorHandlerSet(hci_drv_error_handler_t pfnErrorHandler);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // HCI_DRV_APOLLO_H
|
||||
+2081
File diff suppressed because it is too large
Load Diff
+304
@@ -0,0 +1,304 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file hci_tr.c
|
||||
*
|
||||
* \brief HCI transport module.
|
||||
*
|
||||
* $Date: 2017-03-10 14:08:37 -0600 (Fri, 10 Mar 2017) $
|
||||
* $Revision: 11501 $
|
||||
*
|
||||
* Copyright (c) 2011-2017 ARM Ltd., all rights reserved.
|
||||
* ARM Ltd. confidential and proprietary.
|
||||
*
|
||||
* IMPORTANT. Your use of this file is governed by a Software License Agreement
|
||||
* ("Agreement") that must be accepted in order to download or otherwise receive a
|
||||
* copy of this file. You may not use or copy this file for any purpose other than
|
||||
* as described in the Agreement. If you do not agree to all of the terms of the
|
||||
* Agreement do not use this file and delete all copies in your possession or control;
|
||||
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
|
||||
* to any use, copying or further distribution of this software.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "bstream.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_core.h"
|
||||
#include "hci_drv.h"
|
||||
|
||||
#include "am_mcu_apollo.h"
|
||||
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
State variable
|
||||
**************************************************************************************************/
|
||||
static volatile bool_t g_bHCIReceivingPacket = FALSE;
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
#define HCI_HDR_LEN_MAX HCI_ACL_HDR_LEN
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HCI_RX_STATE_IDLE,
|
||||
HCI_RX_STATE_HEADER,
|
||||
HCI_RX_STATE_DATA,
|
||||
HCI_RX_STATE_COMPLETE
|
||||
} hciRxState_t;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciTrSendAclData
|
||||
*
|
||||
* \brief Send a complete HCI ACL packet to the transport.
|
||||
*
|
||||
* \param pContext Connection context.
|
||||
* \param pData WSF msg buffer containing an ACL packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciTrSendAclData(void *pContext, uint8_t *pData)
|
||||
{
|
||||
uint16_t len;
|
||||
|
||||
/* get 16-bit length */
|
||||
BYTES_TO_UINT16(len, &pData[2]);
|
||||
len += HCI_ACL_HDR_LEN;
|
||||
|
||||
/* dump event for protocol analysis */
|
||||
HCI_PDUMP_TX_ACL(len, pData);
|
||||
|
||||
/* transmit ACL header and data */
|
||||
if (hciDrvWrite(HCI_ACL_TYPE, len, pData) == len)
|
||||
{
|
||||
/* free buffer */
|
||||
hciCoreTxAclComplete(pContext, pData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciTrSendCmd
|
||||
*
|
||||
* \brief Send a complete HCI command to the transport.
|
||||
*
|
||||
* \param pData WSF msg buffer containing an HCI command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciTrSendCmd(uint8_t *pData)
|
||||
{
|
||||
uint8_t len;
|
||||
|
||||
/* get length */
|
||||
len = pData[2] + HCI_CMD_HDR_LEN;
|
||||
|
||||
/* dump event for protocol analysis */
|
||||
HCI_PDUMP_CMD(len, pData);
|
||||
|
||||
/* transmit ACL header and data */
|
||||
if (hciDrvWrite(HCI_CMD_TYPE, len, pData) == len)
|
||||
{
|
||||
/* free buffer */
|
||||
WsfMsgFree(pData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciSerialRxIncoming
|
||||
*
|
||||
* \brief Receive function. Gets called by external code when bytes are received.
|
||||
*
|
||||
* \param pBuf Pointer to buffer of incoming bytes.
|
||||
* \param len Number of bytes in incoming buffer.
|
||||
*
|
||||
* \return The number of bytes consumed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t hciTrSerialRxIncoming(uint8_t *pBuf, uint16_t len)
|
||||
{
|
||||
static uint8_t stateRx = HCI_RX_STATE_IDLE;
|
||||
static uint8_t pktIndRx;
|
||||
static uint16_t iRx;
|
||||
static uint8_t hdrRx[HCI_HDR_LEN_MAX];
|
||||
static uint8_t *pPktRx;
|
||||
static uint8_t *pDataRx;
|
||||
|
||||
uint8_t dataByte;
|
||||
uint16_t consumed_bytes;
|
||||
|
||||
consumed_bytes = 0;
|
||||
/* loop until all bytes of incoming buffer are handled */
|
||||
while (len)
|
||||
{
|
||||
/* read single byte from incoming buffer and advance to next byte */
|
||||
dataByte = *pBuf;
|
||||
|
||||
/* --- Idle State --- */
|
||||
if (stateRx == HCI_RX_STATE_IDLE)
|
||||
{
|
||||
/* save the packet type */
|
||||
pktIndRx = dataByte;
|
||||
iRx = 0;
|
||||
stateRx = HCI_RX_STATE_HEADER;
|
||||
g_bHCIReceivingPacket = TRUE;
|
||||
pBuf++;
|
||||
consumed_bytes++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* --- Header State --- */
|
||||
else if (stateRx == HCI_RX_STATE_HEADER)
|
||||
{
|
||||
uint8_t hdrLen = 0;
|
||||
uint16_t dataLen = 0;
|
||||
|
||||
/* determine header length based on packet type */
|
||||
if (pktIndRx == HCI_EVT_TYPE)
|
||||
{
|
||||
hdrLen = HCI_EVT_HDR_LEN;
|
||||
}
|
||||
else if (pktIndRx == HCI_ACL_TYPE)
|
||||
{
|
||||
hdrLen = HCI_ACL_HDR_LEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* invalid packet type */
|
||||
WSF_ASSERT(0);
|
||||
return consumed_bytes;
|
||||
}
|
||||
|
||||
if (iRx != hdrLen) {
|
||||
/* copy current byte into the temp header buffer */
|
||||
hdrRx[iRx++] = dataByte;
|
||||
pBuf++;
|
||||
consumed_bytes++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* see if entire header has been read */
|
||||
if (iRx == hdrLen)
|
||||
{
|
||||
/* extract data length from header */
|
||||
if (pktIndRx == HCI_EVT_TYPE)
|
||||
{
|
||||
dataLen = hdrRx[1];
|
||||
}
|
||||
else if (pktIndRx == HCI_ACL_TYPE)
|
||||
{
|
||||
BYTES_TO_UINT16(dataLen, &hdrRx[2]);
|
||||
}
|
||||
|
||||
/* allocate data buffer to hold entire packet */
|
||||
if (pktIndRx == HCI_ACL_TYPE)
|
||||
{
|
||||
pPktRx = (uint8_t*)WsfMsgDataAlloc(hdrLen + dataLen, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
pPktRx = (uint8_t*)WsfMsgAlloc(hdrLen + dataLen);
|
||||
}
|
||||
|
||||
if (pPktRx != NULL)
|
||||
{
|
||||
pDataRx = pPktRx;
|
||||
|
||||
/* copy header into data packet (note: memcpy is not so portable) */
|
||||
{
|
||||
uint8_t i;
|
||||
for (i = 0; i < hdrLen; i++)
|
||||
{
|
||||
*pDataRx++ = hdrRx[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* save number of bytes left to read */
|
||||
iRx = dataLen;
|
||||
if (iRx == 0)
|
||||
{
|
||||
stateRx = HCI_RX_STATE_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
stateRx = HCI_RX_STATE_DATA;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WSF_ASSERT(0); /* allocate falied */
|
||||
return consumed_bytes;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Data State --- */
|
||||
else if (stateRx == HCI_RX_STATE_DATA)
|
||||
{
|
||||
/* write incoming byte to allocated buffer */
|
||||
*pDataRx++ = dataByte;
|
||||
|
||||
/* determine if entire packet has been read */
|
||||
iRx--;
|
||||
if (iRx == 0)
|
||||
{
|
||||
stateRx = HCI_RX_STATE_COMPLETE;
|
||||
}
|
||||
pBuf++;
|
||||
consumed_bytes++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* --- Complete State --- */
|
||||
/* ( Note Well! There is no else-if construct by design. ) */
|
||||
if (stateRx == HCI_RX_STATE_COMPLETE)
|
||||
{
|
||||
g_bHCIReceivingPacket = FALSE;
|
||||
|
||||
/* deliver data */
|
||||
if (pPktRx != NULL)
|
||||
{
|
||||
//am_hal_gpio_out_bit_set(13);
|
||||
hciCoreRecv(pktIndRx, pPktRx);
|
||||
//am_hal_gpio_out_bit_clear(13);
|
||||
}
|
||||
|
||||
/* reset state machine */
|
||||
stateRx = HCI_RX_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
return consumed_bytes;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
//! @brief Check to see if the state machine has received part of a packet.
|
||||
//!
|
||||
//! This function checks the HCI packet-receive state machine to see if it is
|
||||
//! in the middle of receiving a packet. This information can be useful in
|
||||
//! determining whether the serial interface should remain enabled.
|
||||
//!
|
||||
//! @return TRUE if there is a packet in progress.
|
||||
//
|
||||
//*****************************************************************************
|
||||
bool_t
|
||||
hciTrReceivingPacket(void)
|
||||
{
|
||||
return g_bHCIReceivingPacket;
|
||||
}
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
//*****************************************************************************
|
||||
//
|
||||
//! @file hci_tr_apollo.h
|
||||
//!
|
||||
//! @brief Additional header information for the Apollo implementation of HCI.
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
#ifndef HCI_TR_APOLLO_H
|
||||
#define HCI_TR_APOLLO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Function prototypes.
|
||||
//
|
||||
//*****************************************************************************
|
||||
extern bool_t hciTrReceivingPacket(void);
|
||||
extern uint16_t hciTrSerialRxIncoming(uint8_t *pBuf, uint16_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // HCI_TR_APOLLO_H
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file hci_vs.c
|
||||
*
|
||||
* \brief HCI vendor specific functions for generic controllers.
|
||||
*
|
||||
* $Date: 2017-02-28 11:31:38 -0600 (Tue, 28 Feb 2017) $
|
||||
* $Revision: 11299 $
|
||||
*
|
||||
* Copyright (c) 2011-2017 ARM Ltd., all rights reserved.
|
||||
* ARM Ltd. confidential and proprietary.
|
||||
*
|
||||
* IMPORTANT. Your use of this file is governed by a Software License Agreement
|
||||
* ("Agreement") that must be accepted in order to download or otherwise receive a
|
||||
* copy of this file. You may not use or copy this file for any purpose other than
|
||||
* as described in the Agreement. If you do not agree to all of the terms of the
|
||||
* Agreement do not use this file and delete all copies in your possession or control;
|
||||
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
|
||||
* to any use, copying or further distribution of this software.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "bda.h"
|
||||
#include "bstream.h"
|
||||
#include "hci_core.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_main.h"
|
||||
#include "hci_cmd.h"
|
||||
#include "hci_apollo_config.h"
|
||||
#include "am_mcu_apollo.h"
|
||||
|
||||
#if HCI_VS_TARGET == HCI_VS_GENERIC
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void hciCoreReadResolvingListSize(void);
|
||||
static void hciCoreReadMaxDataLen(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreReadResolvingListSize
|
||||
*
|
||||
* \brief Read resolving list command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void hciCoreReadResolvingListSize(void)
|
||||
{
|
||||
/* if LL Privacy is supported by Controller and included */
|
||||
if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_PRIVACY) &&
|
||||
(hciLeSupFeatCfg & HCI_LE_SUP_FEAT_PRIVACY))
|
||||
{
|
||||
/* send next command in sequence */
|
||||
HciLeReadResolvingListSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
hciCoreCb.resListSize = 0;
|
||||
|
||||
/* send next command in sequence */
|
||||
hciCoreReadMaxDataLen();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreReadMaxDataLen
|
||||
*
|
||||
* \brief Read maximum data length command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void hciCoreReadMaxDataLen(void)
|
||||
{
|
||||
/* if LE Data Packet Length Extensions is supported by Controller and included */
|
||||
if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_DATA_LEN_EXT) &&
|
||||
(hciLeSupFeatCfg & HCI_LE_SUP_FEAT_DATA_LEN_EXT))
|
||||
{
|
||||
/* send next command in sequence */
|
||||
HciLeReadMaxDataLen();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* send next command in sequence */
|
||||
HciLeRandCmd();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreResetStart
|
||||
*
|
||||
* \brief Start the HCI reset sequence.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreResetStart(void)
|
||||
{
|
||||
/* send an HCI Reset command to start the sequence */
|
||||
HciResetCmd();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreResetSequence
|
||||
*
|
||||
* \brief Implement the HCI reset sequence.
|
||||
*
|
||||
* \param pMsg HCI event message from previous command in the sequence.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreResetSequence(uint8_t *pMsg)
|
||||
{
|
||||
uint16_t opcode;
|
||||
wsfMsgHdr_t hdr;
|
||||
static uint8_t randCnt;
|
||||
|
||||
/* if event is a command complete event */
|
||||
if (*pMsg == HCI_CMD_CMPL_EVT)
|
||||
{
|
||||
/* parse parameters */
|
||||
pMsg += HCI_EVT_HDR_LEN;
|
||||
pMsg++; /* skip num packets */
|
||||
BSTREAM_TO_UINT16(opcode, pMsg);
|
||||
pMsg++; /* skip status */
|
||||
|
||||
/* decode opcode */
|
||||
switch (opcode)
|
||||
{
|
||||
case HCI_OPCODE_RESET:
|
||||
/* initialize rand command count */
|
||||
randCnt = 0;
|
||||
|
||||
#ifdef AM_BSP_GPIO_EM9304_RESET
|
||||
// set the BD address after HCI reset for EM device
|
||||
extern void HciVsEM_SetBDAddress(void);
|
||||
|
||||
HciVsEM_SetBDAddress();
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef AM_BSP_NATIONZ_IOM
|
||||
extern uint8_t g_BLEMacAddress[6];
|
||||
HciVendorSpecificCmd(0xFC32, 6, g_BLEMacAddress);
|
||||
#endif
|
||||
|
||||
/* send next command in sequence */
|
||||
HciSetEventMaskCmd((uint8_t *) hciEventMask);
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_SET_EVENT_MASK:
|
||||
/* send next command in sequence */
|
||||
HciLeSetEventMaskCmd((uint8_t *) hciLeEventMask);
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_SET_EVENT_MASK:
|
||||
/* send next command in sequence */
|
||||
HciSetEventMaskPage2Cmd((uint8_t *) hciEventMaskPage2);
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_SET_EVENT_MASK_PAGE2:
|
||||
/* send next command in sequence */
|
||||
HciReadBdAddrCmd();
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_READ_BD_ADDR:
|
||||
/* parse and store event parameters */
|
||||
BdaCpy(hciCoreCb.bdAddr, pMsg);
|
||||
|
||||
/* send next command in sequence */
|
||||
HciLeReadBufSizeCmd();
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_READ_BUF_SIZE:
|
||||
/* parse and store event parameters */
|
||||
BSTREAM_TO_UINT16(hciCoreCb.bufSize, pMsg);
|
||||
BSTREAM_TO_UINT8(hciCoreCb.numBufs, pMsg);
|
||||
|
||||
|
||||
/* initialize ACL buffer accounting */
|
||||
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
|
||||
// B0 has less data buffer compared to A1 and A0
|
||||
if (!APOLLO3_GE_B0)
|
||||
{
|
||||
hciCoreCb.numBufs--;
|
||||
}
|
||||
#endif
|
||||
hciCoreCb.availBufs = hciCoreCb.numBufs;
|
||||
|
||||
/* send next command in sequence */
|
||||
HciLeReadSupStatesCmd();
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_READ_SUP_STATES:
|
||||
/* parse and store event parameters */
|
||||
memcpy(hciCoreCb.leStates, pMsg, HCI_LE_STATES_LEN);
|
||||
|
||||
/* send next command in sequence */
|
||||
HciLeReadWhiteListSizeCmd();
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_READ_WHITE_LIST_SIZE:
|
||||
/* parse and store event parameters */
|
||||
BSTREAM_TO_UINT8(hciCoreCb.whiteListSize, pMsg);
|
||||
|
||||
/* send next command in sequence */
|
||||
HciLeReadLocalSupFeatCmd();
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_READ_LOCAL_SUP_FEAT:
|
||||
/* parse and store event parameters */
|
||||
BSTREAM_TO_UINT16(hciCoreCb.leSupFeat, pMsg);
|
||||
|
||||
/* send next command in sequence */
|
||||
hciCoreReadResolvingListSize();
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_READ_RES_LIST_SIZE:
|
||||
/* parse and store event parameters */
|
||||
BSTREAM_TO_UINT8(hciCoreCb.resListSize, pMsg);
|
||||
|
||||
/* send next command in sequence */
|
||||
hciCoreReadMaxDataLen();
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_READ_MAX_DATA_LEN:
|
||||
{
|
||||
uint16_t maxTxOctets;
|
||||
uint16_t maxTxTime;
|
||||
|
||||
BSTREAM_TO_UINT16(maxTxOctets, pMsg);
|
||||
BSTREAM_TO_UINT16(maxTxTime, pMsg);
|
||||
|
||||
/* use Controller's maximum supported payload octets and packet duration times
|
||||
* for transmission as Host's suggested values for maximum transmission number
|
||||
* of payload octets and maximum packet transmission time for new connections.
|
||||
*/
|
||||
HciLeWriteDefDataLen(maxTxOctets, maxTxTime);
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_WRITE_DEF_DATA_LEN:
|
||||
if (hciCoreCb.extResetSeq)
|
||||
{
|
||||
/* send first extended command */
|
||||
(*hciCoreCb.extResetSeq)(pMsg, opcode);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* initialize extended parameters */
|
||||
hciCoreCb.maxAdvDataLen = 0;
|
||||
hciCoreCb.numSupAdvSets = 0;
|
||||
hciCoreCb.perAdvListSize = 0;
|
||||
|
||||
/* send next command in sequence */
|
||||
HciLeRandCmd();
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_READ_MAX_ADV_DATA_LEN:
|
||||
case HCI_OPCODE_LE_READ_NUM_SUP_ADV_SETS:
|
||||
case HCI_OPCODE_LE_READ_PER_ADV_LIST_SIZE:
|
||||
if (hciCoreCb.extResetSeq)
|
||||
{
|
||||
/* send next extended command in sequence */
|
||||
(*hciCoreCb.extResetSeq)(pMsg, opcode);
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_RAND:
|
||||
/* check if need to send second rand command */
|
||||
if (randCnt < (HCI_RESET_RAND_CNT-1))
|
||||
{
|
||||
randCnt++;
|
||||
HciLeRandCmd();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* last command in sequence; set resetting state and call callback */
|
||||
hciCb.resetting = FALSE;
|
||||
hdr.param = 0;
|
||||
hdr.event = HCI_RESET_SEQ_CMPL_CBACK_EVT;
|
||||
(*hciCb.evtCback)((hciEvt_t *) &hdr);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreVsCmdCmplRcvd
|
||||
*
|
||||
* \brief Perform internal HCI processing of vendor specific command complete events.
|
||||
*
|
||||
* \param opcode HCI command opcode.
|
||||
* \param pMsg Pointer to input HCI event parameter byte stream.
|
||||
* \param len Parameter byte stream length.
|
||||
*
|
||||
* \return HCI callback event code or zero.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t hciCoreVsCmdCmplRcvd(uint16_t opcode, uint8_t *pMsg, uint8_t len)
|
||||
{
|
||||
return HCI_VENDOR_SPEC_CMD_CMPL_CBACK_EVT;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreVsEvtRcvd
|
||||
*
|
||||
* \brief Perform internal HCI processing of vendor specific HCI events.
|
||||
*
|
||||
* \param p Pointer to input HCI event parameter byte stream.
|
||||
* \param len Parameter byte stream length.
|
||||
*
|
||||
* \return HCI callback event code or zero.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t hciCoreVsEvtRcvd(uint8_t *p, uint8_t len)
|
||||
{
|
||||
return HCI_VENDOR_SPEC_EVT;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciCoreHwErrorRcvd
|
||||
*
|
||||
* \brief Perform internal HCI processing of hardware error event.
|
||||
*
|
||||
* \param p Pointer to input HCI event parameter byte stream.
|
||||
*
|
||||
* \return HCI callback event code or zero.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t hciCoreHwErrorRcvd(uint8_t *p)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn HciVsInit
|
||||
*
|
||||
* \brief Vendor-specific controller initialization function.
|
||||
*
|
||||
* \param param Vendor-specific parameter.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciVsInit(uint8_t param)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
Vendored
+174
@@ -0,0 +1,174 @@
|
||||
/* Copyright (c) 2009-2019 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI vendor specific AE functions for generic controllers.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "hci_core.h"
|
||||
|
||||
#if HCI_VS_TARGET == HCI_VS_GENERIC
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void hciCoreReadMaxAdvDataLen(void);
|
||||
static void hciCoreReadNumSupAdvSets(void);
|
||||
static void hciCoreReadPerAdvListSize(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read maximum advertising data length command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void hciCoreReadMaxAdvDataLen(void)
|
||||
{
|
||||
/* if LE Extended Advertising is supported by Controller and included */
|
||||
if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_LE_EXT_ADV) &&
|
||||
(hciLeSupFeatCfg & HCI_LE_SUP_FEAT_LE_EXT_ADV))
|
||||
{
|
||||
/* send next command in sequence */
|
||||
HciLeReadMaxAdvDataLen();
|
||||
}
|
||||
else
|
||||
{
|
||||
hciCoreCb.maxAdvDataLen = 0;
|
||||
|
||||
/* send next command in sequence */
|
||||
hciCoreReadNumSupAdvSets();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read read number of supported advertising sets command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void hciCoreReadNumSupAdvSets(void)
|
||||
{
|
||||
/* if LE Extended Advertising is supported by Controller and included */
|
||||
if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_LE_EXT_ADV) &&
|
||||
(hciLeSupFeatCfg & HCI_LE_SUP_FEAT_LE_EXT_ADV))
|
||||
{
|
||||
/* send next command in sequence */
|
||||
HciLeReadNumSupAdvSets();
|
||||
}
|
||||
else
|
||||
{
|
||||
hciCoreCb.numSupAdvSets = 0;
|
||||
|
||||
/* send next command in sequence */
|
||||
hciCoreReadPerAdvListSize();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read periodic advertiser list size command.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void hciCoreReadPerAdvListSize(void)
|
||||
{
|
||||
/* if LE Extended Advertising is supported by Controller and included */
|
||||
if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_LE_PER_ADV) &&
|
||||
(hciLeSupFeatCfg & HCI_LE_SUP_FEAT_LE_PER_ADV))
|
||||
{
|
||||
/* send next command in sequence */
|
||||
HciLeReadPerAdvListSizeCmd();
|
||||
}
|
||||
else
|
||||
{
|
||||
hciCoreCb.perAdvListSize = 0;
|
||||
|
||||
/* send next command in sequence */
|
||||
HciLeRandCmd();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Implement the HCI extended reset sequence.
|
||||
*
|
||||
* \param pMsg HCI event message from previous command in the sequence.
|
||||
* \param opcode HCI event message opcode.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void hciCoreExtResetSequence(uint8_t *pMsg, uint16_t opcode)
|
||||
{
|
||||
/* decode opcode */
|
||||
switch (opcode)
|
||||
{
|
||||
case HCI_OPCODE_READ_LOCAL_VER_INFO:
|
||||
/* send next command in sequence */
|
||||
hciCoreReadMaxAdvDataLen();
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_READ_MAX_ADV_DATA_LEN:
|
||||
BSTREAM_TO_UINT16(hciCoreCb.maxAdvDataLen, pMsg);
|
||||
|
||||
/* send next command in sequence */
|
||||
hciCoreReadNumSupAdvSets();
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_READ_NUM_SUP_ADV_SETS:
|
||||
/* parse and store event parameters */
|
||||
BSTREAM_TO_UINT8(hciCoreCb.numSupAdvSets, pMsg);
|
||||
|
||||
/* send next command in sequence */
|
||||
hciCoreReadPerAdvListSize();
|
||||
break;
|
||||
|
||||
case HCI_OPCODE_LE_READ_PER_ADV_LIST_SIZE:
|
||||
/* parse and store event parameters */
|
||||
BSTREAM_TO_UINT8(hciCoreCb.perAdvListSize, pMsg);
|
||||
|
||||
/* send next command in sequence */
|
||||
HciLeRandCmd();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Vendor-specific controller AE initialization function.
|
||||
*
|
||||
* \param param Vendor-specific parameter.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciVsAeInit(uint8_t param)
|
||||
{
|
||||
hciCoreCb.extResetSeq = hciCoreExtResetSequence;
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+838
@@ -0,0 +1,838 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief HCI core module, platform independent functions.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This module implements core platform independent HCI features for transmit data path,
|
||||
* fragmentation, reassembly, and connection management.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "util/bda.h"
|
||||
#include "util/bstream.h"
|
||||
#include "hci_core.h"
|
||||
#include "hci_tr.h"
|
||||
#include "hci_cmd.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_main.h"
|
||||
#include "l2c_defs.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Default ACL buffer flow control watermark levels */
|
||||
#ifndef HCI_ACL_QUEUE_HI
|
||||
#define HCI_ACL_QUEUE_HI 5 /* Disable flow when this many buffers queued */
|
||||
#endif
|
||||
#ifndef HCI_ACL_QUEUE_LO
|
||||
#define HCI_ACL_QUEUE_LO 1 /* Enable flow when this many buffers queued */
|
||||
#endif
|
||||
|
||||
/* Default maximum ACL packet size for reassembly */
|
||||
#ifndef HCI_MAX_RX_ACL_LEN
|
||||
#define HCI_MAX_RX_ACL_LEN HCI_ACL_DEFAULT_LEN
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Event mask */
|
||||
const uint8_t hciEventMask[HCI_EVT_MASK_LEN] =
|
||||
{
|
||||
HCI_EVT_MASK_DISCONNECT_CMPL | /* Byte 0 */
|
||||
HCI_EVT_MASK_ENC_CHANGE, /* Byte 0 */
|
||||
HCI_EVT_MASK_READ_REMOTE_VER_INFO_CMPL | /* Byte 1 */
|
||||
HCI_EVT_MASK_HW_ERROR, /* Byte 1 */
|
||||
0, /* Byte 2 */
|
||||
HCI_EVT_MASK_DATA_BUF_OVERFLOW, /* Byte 3 */
|
||||
0, /* Byte 4 */
|
||||
HCI_EVT_MASK_ENC_KEY_REFRESH_CMPL, /* Byte 5 */
|
||||
0, /* Byte 6 */
|
||||
HCI_EVT_MASK_LE_META /* Byte 7 */
|
||||
};
|
||||
|
||||
/* LE event mask */
|
||||
const uint8_t hciLeEventMask[HCI_LE_EVT_MASK_LEN] =
|
||||
{
|
||||
HCI_EVT_MASK_LE_CONN_CMPL_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_ADV_REPORT_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_CONN_UPDATE_CMPL_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_READ_REMOTE_FEAT_CMPL_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_LTK_REQ_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_REMOTE_CONN_PARAM_REQ_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_DATA_LEN_CHANGE_EVT | /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_READ_LOCAL_P256_PUB_KEY_CMPL, /* Byte 0 */
|
||||
HCI_EVT_MASK_LE_GENERATE_DHKEY_CMPL | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_ENHANCED_CONN_CMPL_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_DIRECT_ADV_REPORT_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_PHY_UPDATE_CMPL_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_EXT_ADV_REPORT_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_PER_ADV_SYNC_EST_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_PER_ADV_REPORT_EVT | /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_PER_ADV_SYNC_LOST_EVT, /* Byte 1 */
|
||||
HCI_EVT_MASK_LE_SCAN_TIMEOUT_EVT | /* Byte 2 */
|
||||
HCI_EVT_MASK_LE_ADV_SET_TERM_EVT | /* Byte 2 */
|
||||
HCI_EVT_MASK_LE_SCAN_REQ_RCVD_EVT | /* Byte 2 */
|
||||
HCI_EVT_MASK_LE_CH_SEL_ALGO_EVT | /* Byte 2 */
|
||||
HCI_EVT_MASK_LE_CONN_IQ_REPORT_EVT | /* Byte 2 */
|
||||
HCI_EVT_MASK_LE_CTE_REQ_FAILED_EVT | /* Byte 2 */
|
||||
HCI_EVT_MASK_LE_PER_SYNC_TRSF_RCVT_EVT, /* Byte 2 */
|
||||
0, /* Byte 3 */
|
||||
0, /* Byte 4 */
|
||||
0, /* Byte 5 */
|
||||
0, /* Byte 6 */
|
||||
0 /* Byte 7 */
|
||||
};
|
||||
|
||||
/* event mask page 2 */
|
||||
const uint8_t hciEventMaskPage2[HCI_EVT_MASK_PAGE_2_LEN] =
|
||||
{
|
||||
0, /* Byte 0 */
|
||||
0, /* Byte 1 */
|
||||
HCI_EVT_MASK_AUTH_PAYLOAD_TIMEOUT, /* Byte 2 */
|
||||
0, /* Byte 3 */
|
||||
0, /* Byte 4 */
|
||||
0, /* Byte 5 */
|
||||
0, /* Byte 6 */
|
||||
0 /* Byte 7 */
|
||||
};
|
||||
|
||||
/* LE supported features configuration mask */
|
||||
uint32_t hciLeSupFeatCfg =
|
||||
HCI_LE_SUP_FEAT_ENCRYPTION | /* LE Encryption */
|
||||
HCI_LE_SUP_FEAT_CONN_PARAM_REQ_PROC | /* Connection Parameters Request Procedure */
|
||||
HCI_LE_SUP_FEAT_EXT_REJECT_IND | /* Extended Reject Indication */
|
||||
HCI_LE_SUP_FEAT_SLV_INIT_FEAT_EXCH | /* Slave-initiated Features Exchange */
|
||||
HCI_LE_SUP_FEAT_LE_PING | /* LE Ping */
|
||||
HCI_LE_SUP_FEAT_DATA_LEN_EXT | /* LE Data Packet Length Extension */
|
||||
HCI_LE_SUP_FEAT_PRIVACY | /* LL Privacy */
|
||||
HCI_LE_SUP_FEAT_EXT_SCAN_FILT_POLICY | /* Extended Scanner Filter Policies */
|
||||
HCI_LE_SUP_FEAT_LE_2M_PHY | /* LE 2M PHY supported */
|
||||
HCI_LE_SUP_FEAT_STABLE_MOD_IDX_TRANSMITTER | /* Stable Modulation Index - Transmitter supported */
|
||||
HCI_LE_SUP_FEAT_STABLE_MOD_IDX_RECEIVER | /* Stable Modulation Index - Receiver supported */
|
||||
HCI_LE_SUP_FEAT_LE_EXT_ADV | /* LE Extended Advertising */
|
||||
HCI_LE_SUP_FEAT_LE_PER_ADV; /* LE Periodic Advertising */
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
hciCoreCb_t hciCoreCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate a connection structure.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void hciCoreConnAlloc(uint16_t handle)
|
||||
{
|
||||
uint8_t i;
|
||||
hciCoreConn_t *pConn = hciCoreCb.conn;
|
||||
|
||||
/* find available connection struct */
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
|
||||
{
|
||||
if (pConn->handle == HCI_HANDLE_NONE)
|
||||
{
|
||||
/* allocate and initialize */
|
||||
pConn->handle = handle;
|
||||
pConn->flowDisabled = FALSE;
|
||||
pConn->outBufs = 0;
|
||||
pConn->queuedBufs = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HCI_TRACE_WARN0("HCI conn struct alloc failure");
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free a connection structure.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void hciCoreConnFree(uint16_t handle)
|
||||
{
|
||||
uint8_t i;
|
||||
hciCoreConn_t *pConn = hciCoreCb.conn;
|
||||
|
||||
/* find connection struct */
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
|
||||
{
|
||||
if (pConn->handle == handle)
|
||||
{
|
||||
/* free any fragmenting ACL packet */
|
||||
if (pConn->pTxAclPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pConn->pTxAclPkt);
|
||||
pConn->pTxAclPkt = NULL;
|
||||
}
|
||||
pConn->fragmenting = FALSE;
|
||||
|
||||
if (pConn->pRxAclPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pConn->pRxAclPkt);
|
||||
pConn->pRxAclPkt = NULL;
|
||||
}
|
||||
|
||||
/* free structure */
|
||||
pConn->handle = HCI_HANDLE_NONE;
|
||||
|
||||
/* optional: iterate through tx ACL queue and free any buffers with this handle */
|
||||
|
||||
/* outstanding buffers are now available; service TX data path */
|
||||
hciCoreTxReady(pConn->outBufs);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HCI_TRACE_WARN1("hciCoreConnFree handle not found:%u", handle);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get a connection structure by handle
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
*
|
||||
* \return Pointer to connection structure or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
hciCoreConn_t *hciCoreConnByHandle(uint16_t handle)
|
||||
{
|
||||
uint8_t i;
|
||||
hciCoreConn_t *pConn = hciCoreCb.conn;
|
||||
|
||||
/* find available connection struct */
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
|
||||
{
|
||||
if (pConn->handle == handle)
|
||||
{
|
||||
return pConn;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the next connection structure with a packet fragment to send.
|
||||
*
|
||||
* \return Pointer to connection structure or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static hciCoreConn_t *hciCoreNextConnFragment(void)
|
||||
{
|
||||
uint8_t i;
|
||||
hciCoreConn_t *pConn = hciCoreCb.conn;
|
||||
|
||||
/* find connection struct */
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
|
||||
{
|
||||
if (pConn->handle != HCI_HANDLE_NONE && pConn->fragmenting)
|
||||
{
|
||||
return pConn;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform internal processing on HCI connection open.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreConnOpen(uint16_t handle)
|
||||
{
|
||||
/* allocate connection structure */
|
||||
hciCoreConnAlloc(handle);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform internal processing on HCI connection close.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreConnClose(uint16_t handle)
|
||||
{
|
||||
/* free connection structure */
|
||||
hciCoreConnFree(handle);
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send ACL data to transport.
|
||||
*
|
||||
* \param pConn Pointer to connection structure.
|
||||
* \param pData WSF buffer containing an ACL packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreSendAclData(hciCoreConn_t *pConn, uint8_t *pData)
|
||||
{
|
||||
/* increment outstanding buf count for handle */
|
||||
pConn->outBufs++;
|
||||
|
||||
/* send to transport */
|
||||
hciTrSendAclData(pConn, pData);
|
||||
|
||||
/* decrement available buffer count */
|
||||
if (hciCoreCb.availBufs > 0)
|
||||
{
|
||||
hciCoreCb.availBufs--;
|
||||
}
|
||||
else
|
||||
{
|
||||
HCI_TRACE_WARN0("hciCoreSendAclData availBufs=0");
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Service the TX data path.
|
||||
*
|
||||
* \param bufs Number of new buffers now available.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreTxReady(uint8_t bufs)
|
||||
{
|
||||
uint8_t *pData;
|
||||
wsfHandlerId_t handlerId;
|
||||
uint16_t handle;
|
||||
uint16_t len;
|
||||
hciCoreConn_t *pConn;
|
||||
|
||||
/* increment available buffers, with ceiling */
|
||||
if (bufs > 0)
|
||||
{
|
||||
hciCoreCb.availBufs += bufs;
|
||||
if (hciCoreCb.availBufs > hciCoreCb.numBufs)
|
||||
{
|
||||
hciCoreCb.availBufs = hciCoreCb.numBufs;
|
||||
}
|
||||
}
|
||||
|
||||
/* service ACL data queue and send as many buffers as we can */
|
||||
while (hciCoreCb.availBufs > 0)
|
||||
{
|
||||
/* send continuation of any fragments first */
|
||||
if (hciCoreTxAclContinue(NULL) == FALSE)
|
||||
{
|
||||
/* if no fragments then check for any queued ACL data */
|
||||
if ((pData = WsfMsgDeq(&hciCoreCb.aclQueue, &handlerId)) != NULL)
|
||||
{
|
||||
/* parse handle and length */
|
||||
BYTES_TO_UINT16(handle, pData);
|
||||
BYTES_TO_UINT16(len, &pData[2]);
|
||||
|
||||
/* look up conn structure and send data */
|
||||
if ((pConn = hciCoreConnByHandle(handle)) != NULL)
|
||||
{
|
||||
hciCoreTxAclStart(pConn, len, pData);
|
||||
}
|
||||
/* handle not found, connection must be closed */
|
||||
else
|
||||
{
|
||||
/* discard buffer */
|
||||
WsfMsgFree(pData);
|
||||
|
||||
HCI_TRACE_WARN1("hciCoreTxReady discarding buffer, handle=%u", handle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no fragments or queued data to send; we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send ACL packets, start of packet.
|
||||
*
|
||||
* \param pConn Pointer to connection structure.
|
||||
* \param len ACL packet length.
|
||||
* \param pData WSF buffer containing an ACL packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreTxAclStart(hciCoreConn_t *pConn, uint16_t len, uint8_t *pData)
|
||||
{
|
||||
uint16_t hciLen;
|
||||
|
||||
/* make sure not already fragmenting on this connection */
|
||||
WSF_ASSERT(pConn->fragmenting == FALSE);
|
||||
|
||||
hciLen = HciGetBufSize();
|
||||
|
||||
HCI_TRACE_INFO1("hciCoreTxAclStart len=%u", len);
|
||||
|
||||
/* if acl len > controller acl buf len */
|
||||
if (len > hciLen)
|
||||
{
|
||||
/* store remaining acl len = acl len - hci acl buf len */
|
||||
pConn->txAclRemLen = len - hciLen;
|
||||
|
||||
/* store position for next fragment */
|
||||
pConn->pNextTxFrag = pData + hciLen;
|
||||
|
||||
/* store information required for fragmentation */
|
||||
pConn->pTxAclPkt = pData;
|
||||
pConn->fragmenting = TRUE;
|
||||
|
||||
/* set acl len in packet to hci acl buf len */
|
||||
UINT16_TO_BUF(&pData[2], hciLen);
|
||||
|
||||
/* send the packet */
|
||||
hciCoreSendAclData(pConn, pData);
|
||||
|
||||
/* send additional fragments while there are HCI buffers available */
|
||||
while ((hciCoreCb.availBufs > 0) && hciCoreTxAclContinue(pConn));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no fragmentation, just send the packet */
|
||||
hciCoreSendAclData(pConn, pData);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send ACL packets, continuation of fragmented packets.
|
||||
*
|
||||
* \param pConn Pointer to connection structure. If set non-NULL, then a fragment is
|
||||
* sent from this connection structure. If NULL the function finds the next
|
||||
* connection structure with a fragment to be sent.
|
||||
*
|
||||
* \return TRUE if packet sent, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t hciCoreTxAclContinue(hciCoreConn_t *pConn)
|
||||
{
|
||||
uint16_t aclLen;
|
||||
|
||||
if (pConn == NULL)
|
||||
{
|
||||
pConn = hciCoreNextConnFragment();
|
||||
}
|
||||
|
||||
if (pConn != NULL)
|
||||
{
|
||||
/* get next fragment length */
|
||||
aclLen = (pConn->txAclRemLen < HciGetBufSize()) ? pConn->txAclRemLen : HciGetBufSize();
|
||||
|
||||
if (aclLen > 0)
|
||||
{
|
||||
/* decrement remaining length */
|
||||
pConn->txAclRemLen -= aclLen;
|
||||
|
||||
/* set handle in packet with continuation bit set */
|
||||
UINT16_TO_BUF(pConn->pNextTxFrag, (pConn->handle | HCI_PB_CONTINUE));
|
||||
|
||||
/* set acl len in packet */
|
||||
UINT16_TO_BUF(&(pConn->pNextTxFrag[2]), aclLen);
|
||||
|
||||
HCI_TRACE_INFO2("hciCoreTxAclContinue aclLen=%u remLen=%u", aclLen, pConn->txAclRemLen);
|
||||
|
||||
/* send the packet */
|
||||
hciCoreSendAclData(pConn, pConn->pNextTxFrag);
|
||||
|
||||
/* set up pointer to next fragment */
|
||||
if (pConn->txAclRemLen > 0)
|
||||
{
|
||||
pConn->pNextTxFrag += aclLen;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called from the HCI transport layer when transmission of an ACL
|
||||
* packet is complete.
|
||||
*
|
||||
* \param pConn Pointer to connection structure.
|
||||
* \param pData WSF buffer containing an ACL packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciCoreTxAclComplete(hciCoreConn_t *pConn, uint8_t *pData)
|
||||
{
|
||||
/* if fragmenting */
|
||||
if (pConn->fragmenting)
|
||||
{
|
||||
/* check if all fragments sent */
|
||||
if (pConn->txAclRemLen == 0)
|
||||
{
|
||||
/* free original buffer */
|
||||
WsfMsgFree(pConn->pTxAclPkt);
|
||||
pConn->pTxAclPkt = NULL;
|
||||
pConn->fragmenting = FALSE;
|
||||
HCI_TRACE_INFO0("hciCoreTxAclComplete free pTxAclPkt");
|
||||
}
|
||||
}
|
||||
else if (pData != NULL)
|
||||
{
|
||||
WsfMsgFree(pData);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reassemble an ACL packet.
|
||||
*
|
||||
* \param pData Input ACL packet.
|
||||
*
|
||||
* \return pointer to ACL packet to send, or NULL if no packet to send.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *hciCoreAclReassembly(uint8_t *pData)
|
||||
{
|
||||
hciCoreConn_t *pConn;
|
||||
uint8_t *pDataRtn = NULL;
|
||||
uint16_t handle;
|
||||
uint16_t aclLen;
|
||||
uint16_t l2cLen;
|
||||
uint16_t pbf;
|
||||
bool_t freeData = TRUE;
|
||||
|
||||
BYTES_TO_UINT16(handle, pData);
|
||||
pbf = handle & HCI_PB_FLAG_MASK;
|
||||
handle &= HCI_HANDLE_MASK;
|
||||
BYTES_TO_UINT16(aclLen, &pData[2]);
|
||||
|
||||
/* look up connection */
|
||||
if ((pConn = hciCoreConnByHandle(handle)) != NULL)
|
||||
{
|
||||
/* if this is a start packet */
|
||||
if (pbf == HCI_PB_START_C2H)
|
||||
{
|
||||
/* if currently reassembled packet not complete */
|
||||
if (pConn->pRxAclPkt != NULL)
|
||||
{
|
||||
/* discard currently reassembled packet */
|
||||
WsfMsgFree(pConn->pRxAclPkt);
|
||||
pConn->pRxAclPkt = NULL;
|
||||
HCI_TRACE_WARN1("disarded hci rx pkt handle=0x%04x", handle);
|
||||
}
|
||||
|
||||
/* read l2cap length */
|
||||
if (aclLen >= L2C_HDR_LEN)
|
||||
{
|
||||
BYTES_TO_UINT16(l2cLen, &pData[4]);
|
||||
|
||||
/* check length vs. configured maximum */
|
||||
if ((l2cLen + L2C_HDR_LEN) > hciCoreCb.maxRxAclLen)
|
||||
{
|
||||
HCI_TRACE_WARN1("l2c len=0x%04x to large for reassembly", l2cLen);
|
||||
}
|
||||
/* if reassembly required */
|
||||
else if ((l2cLen + L2C_HDR_LEN) > aclLen)
|
||||
{
|
||||
/* allocate buffer to store complete l2cap packet */
|
||||
if ((pConn->pRxAclPkt = WsfMsgDataAlloc(l2cLen + L2C_HDR_LEN + HCI_ACL_HDR_LEN, 0)) != NULL)
|
||||
{
|
||||
/* store buffer for reassembly */
|
||||
pConn->pNextRxFrag = pConn->pRxAclPkt;
|
||||
|
||||
/* build acl header and copy data */
|
||||
UINT16_TO_BSTREAM(pConn->pNextRxFrag, handle);
|
||||
UINT16_TO_BSTREAM(pConn->pNextRxFrag, l2cLen + L2C_HDR_LEN);
|
||||
memcpy(pConn->pNextRxFrag, &pData[4], aclLen);
|
||||
pConn->pNextRxFrag += aclLen;
|
||||
|
||||
/* store remaining length */
|
||||
pConn->rxAclRemLen = l2cLen + L2C_HDR_LEN - aclLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* alloc failed; discard */
|
||||
HCI_TRACE_WARN1("reassembly alloc failed len=%u", (l2cLen + L2C_HDR_LEN + HCI_ACL_HDR_LEN));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no reassembly required, pData is ready to go */
|
||||
pDataRtn = pData;
|
||||
freeData = FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* invalid l2cap packet; discard */
|
||||
HCI_TRACE_WARN1("invalid l2c pkt aclLen=%u", aclLen);
|
||||
}
|
||||
}
|
||||
/* else if this is a continuation packet */
|
||||
else if (pbf == HCI_PB_CONTINUE)
|
||||
{
|
||||
/* if expecting a continuation */
|
||||
if (pConn->pRxAclPkt != NULL)
|
||||
{
|
||||
if (aclLen <= pConn->rxAclRemLen)
|
||||
{
|
||||
/* copy data to start of next fragment */
|
||||
memcpy(pConn->pNextRxFrag, &pData[HCI_ACL_HDR_LEN], aclLen);
|
||||
pConn->pNextRxFrag += aclLen;
|
||||
|
||||
/* update remaining length */
|
||||
pConn->rxAclRemLen -= aclLen;
|
||||
|
||||
/* if reassembly complete return reassembled packet */
|
||||
if (pConn->rxAclRemLen == 0)
|
||||
{
|
||||
pDataRtn = pConn->pRxAclPkt;
|
||||
pConn->pRxAclPkt = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HCI_TRACE_WARN2("continuation pkt too long len=%u RemLen=%u", aclLen, pConn->rxAclRemLen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HCI_TRACE_WARN1("unexpected continuation pkt handle=0x%04x", handle);
|
||||
}
|
||||
}
|
||||
/* else unknown packet type */
|
||||
else
|
||||
{
|
||||
HCI_TRACE_WARN1("unknown pb flags=0x%04x", pbf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* connection not found */
|
||||
HCI_TRACE_WARN1("pkt rcvd on unknown handle=0x%04x", (handle & HCI_HANDLE_MASK));
|
||||
}
|
||||
|
||||
if (freeData)
|
||||
{
|
||||
WsfMsgFree(pData);
|
||||
}
|
||||
|
||||
return pDataRtn;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if a TX ACL packet is being fragmented.
|
||||
*
|
||||
* \param pContext Connection context.
|
||||
*
|
||||
* \return TRUE if fragmenting a TX ACL packet, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t hciCoreTxAclDataFragmented(hciCoreConn_t *pConn)
|
||||
{
|
||||
return pConn->fragmenting;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI core initialization.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciCoreInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
WSF_QUEUE_INIT(&hciCoreCb.aclQueue);
|
||||
|
||||
for (i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
hciCoreCb.conn[i].handle = HCI_HANDLE_NONE;
|
||||
}
|
||||
|
||||
hciCoreCb.maxRxAclLen = HCI_MAX_RX_ACL_LEN;
|
||||
hciCoreCb.aclQueueHi = HCI_ACL_QUEUE_HI;
|
||||
hciCoreCb.aclQueueLo = HCI_ACL_QUEUE_LO;
|
||||
hciCoreCb.extResetSeq = NULL;
|
||||
|
||||
hciCoreInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an HCI reset sequence.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciResetSequence(void)
|
||||
{
|
||||
/* set resetting state */
|
||||
hciCb.resetting = TRUE;
|
||||
|
||||
/* start the reset sequence */
|
||||
hciCoreResetStart();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the maximum reassembled RX ACL packet length. Minimum value is 27.
|
||||
*
|
||||
* \param len ACL packet length.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciSetMaxRxAclLen(uint16_t len)
|
||||
{
|
||||
hciCoreCb.maxRxAclLen = len;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set TX ACL queue high and low watermarks.
|
||||
*
|
||||
* \param queueHi Disable flow on a connection when this many ACL buffers are queued.
|
||||
* queueLo Disable flow on a connection when this many ACL buffers are queued.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciSetAclQueueWatermarks(uint8_t queueHi, uint8_t queueLo)
|
||||
{
|
||||
hciCoreCb.aclQueueHi = queueHi;
|
||||
hciCoreCb.aclQueueLo = queueLo;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set LE supported features configuration mask.
|
||||
*
|
||||
* \param feat Feature bit to set or clear
|
||||
* \param flag TRUE to set feature bit and FALSE to clear it
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciSetLeSupFeat(uint32_t feat, bool_t flag)
|
||||
{
|
||||
/* if asked to include feature */
|
||||
if (flag)
|
||||
{
|
||||
/* set feature bit */
|
||||
hciLeSupFeatCfg |= feat;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* clear feature bit */
|
||||
hciLeSupFeatCfg &= ~feat;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send data from the stack to HCI.
|
||||
*
|
||||
* \param pData WSF buffer containing an ACL packet
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciSendAclData(uint8_t *pData)
|
||||
{
|
||||
uint16_t handle;
|
||||
uint16_t len;
|
||||
hciCoreConn_t *pConn;
|
||||
|
||||
/* parse handle and length */
|
||||
BYTES_TO_UINT16(handle, pData);
|
||||
BYTES_TO_UINT16(len, &pData[2]);
|
||||
|
||||
/* look up connection structure */
|
||||
if ((pConn = hciCoreConnByHandle(handle)) != NULL)
|
||||
{
|
||||
/* if queue empty and buffers available */
|
||||
if (WsfQueueEmpty(&hciCoreCb.aclQueue) && hciCoreCb.availBufs > 0)
|
||||
{
|
||||
/* send data */
|
||||
hciCoreTxAclStart(pConn, len, pData);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* queue data - message handler ID 'handerId' not used */
|
||||
WsfMsgEnq(&hciCoreCb.aclQueue, 0, pData);
|
||||
}
|
||||
|
||||
/* increment buffer queue count for this connection with consideration for HCI fragmentation */
|
||||
pConn->queuedBufs += ((len - 1) / HciGetBufSize()) + 1;
|
||||
|
||||
/* manage flow control to stack */
|
||||
if (pConn->queuedBufs >= hciCoreCb.aclQueueHi && pConn->flowDisabled == FALSE)
|
||||
{
|
||||
pConn->flowDisabled = TRUE;
|
||||
(*hciCb.flowCback)(handle, TRUE);
|
||||
}
|
||||
}
|
||||
/* connection not found, connection must be closed */
|
||||
else
|
||||
{
|
||||
/* discard buffer */
|
||||
WsfMsgFree(pData);
|
||||
|
||||
HCI_TRACE_WARN1("HciSendAclData discarding buffer, handle=%u", handle);
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file hci_tr.c
|
||||
*
|
||||
* \brief HCI transport module.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_core.h"
|
||||
#include "hci_tr.h"
|
||||
#include "ll_api.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn hciTrSendAclData
|
||||
*
|
||||
* \brief Send a complete HCI ACL packet to the transport.
|
||||
*
|
||||
* \param pContext Connection context.
|
||||
* \param pData WSF msg buffer containing an ACL packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void hciTrSendAclData(void *pContext, uint8_t *pData)
|
||||
{
|
||||
uint16_t len;
|
||||
uint8_t *p;
|
||||
|
||||
/* if fragmenting */
|
||||
if (hciCoreTxAclDataFragmented(pContext))
|
||||
{
|
||||
/* get 16-bit length */
|
||||
BYTES_TO_UINT16(len, (pData + 2))
|
||||
len += HCI_ACL_HDR_LEN;
|
||||
|
||||
/* allocate LL buffer */
|
||||
if ((p = WsfMsgDataAlloc(len, HCI_TX_DATA_TAILROOM)) != NULL)
|
||||
{
|
||||
/* copy data */
|
||||
memcpy(p, pData, len);
|
||||
|
||||
/* send to LL */
|
||||
LlSendAclData(p);
|
||||
|
||||
/* free HCI buffer */
|
||||
hciCoreTxAclComplete(pContext, pData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* send to LL */
|
||||
LlSendAclData(pData);
|
||||
|
||||
/* LL will free HCI buffer */
|
||||
hciCoreTxAclComplete(pContext, NULL);
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief AES and random number security service implemented using HCI.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "sec_api.h"
|
||||
#include "sec_main.h"
|
||||
#include "hci_api.h"
|
||||
#include "util/calc128.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
External Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
extern secCb_t secCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Returns the next token.
|
||||
*
|
||||
* \return Token value.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t getNextToken()
|
||||
{
|
||||
uint8_t token = secCb.token++;
|
||||
|
||||
if (token == SEC_TOKEN_INVALID)
|
||||
{
|
||||
token = secCb.token++;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute an AES calculation. When the calculation completes, a WSF message will be
|
||||
* sent to the specified handler. This function returns a token value that
|
||||
* the client can use to match calls to this function with messages.
|
||||
*
|
||||
* \param pKey Pointer to 16 byte key.
|
||||
* \param pPlaintext Pointer to 16 byte plaintext.
|
||||
* \param handlerId WSF handler ID.
|
||||
* \param param Client-defined parameter returned in message.
|
||||
* \param event Event for client's WSF handler.
|
||||
*
|
||||
* \return Token value.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t SecAes(uint8_t *pKey, uint8_t *pPlaintext, wsfHandlerId_t handlerId,
|
||||
uint16_t param, uint8_t event)
|
||||
{
|
||||
secQueueBuf_t *pBuf;
|
||||
|
||||
/* allocate a buffer */
|
||||
if ((pBuf = WsfMsgAlloc(sizeof(secQueueBuf_t))) != NULL)
|
||||
{
|
||||
pBuf->msg.hdr.status = getNextToken();
|
||||
pBuf->msg.hdr.param = param;
|
||||
pBuf->msg.hdr.event = event;
|
||||
|
||||
pBuf->type = SEC_TYPE_AES;
|
||||
|
||||
/* queue buffer */
|
||||
WsfMsgEnq(&secCb.aesEncQueue, handlerId, pBuf);
|
||||
|
||||
/* call HCI encrypt function */
|
||||
HciLeEncryptCmd(pKey, pPlaintext);
|
||||
|
||||
return pBuf->msg.hdr.status;
|
||||
}
|
||||
|
||||
return SEC_TOKEN_INVALID;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Callback for HCI encryption for AES operations.
|
||||
*
|
||||
* \param pBuf Pointer to sec queue element.
|
||||
* \param pEvent Pointer to HCI event.
|
||||
* \param handlerId WSF handler ID.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecAesHciCback(secQueueBuf_t *pBuf, hciEvt_t *pEvent, wsfHandlerId_t handlerId)
|
||||
{
|
||||
secAes_t *pAes = (secAes_t *) &pBuf->msg;
|
||||
|
||||
/* set encrypted data pointer and copy */
|
||||
pAes->pCiphertext = pBuf->ciphertext;
|
||||
Calc128Cpy(pAes->pCiphertext, pEvent->leEncryptCmdCmpl.data);
|
||||
|
||||
/* send message */
|
||||
WsfMsgSend(handlerId, pAes);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to initialize AES secuirity.
|
||||
*
|
||||
* \param none.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecAesInit()
|
||||
{
|
||||
secCb.hciCbackTbl[SEC_TYPE_AES] = SecAesHciCback;
|
||||
}
|
||||
Vendored
+137
@@ -0,0 +1,137 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief AES and random number security service implemented using HCI.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "sec_api.h"
|
||||
#include "sec_main.h"
|
||||
#include "hci_api.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
External Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
extern secCb_t secCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Returns the next token.
|
||||
*
|
||||
* \return Token value.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t getNextToken()
|
||||
{
|
||||
uint8_t token = secCb.token++;
|
||||
|
||||
if (token == SEC_TOKEN_INVALID)
|
||||
{
|
||||
token = secCb.token++;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute an AES calculation. When the calculation completes, a WSF message will be
|
||||
* sent to the specified handler. This function returns a token value that
|
||||
* the client can use to match calls to this function with messages. Note this version
|
||||
* reverses the key and plaintext bytes.
|
||||
*
|
||||
* \param pKey Pointer to 16 byte key.
|
||||
* \param pPlaintext Pointer to 16 byte plaintext.
|
||||
* \param handlerId WSF handler ID.
|
||||
* \param param Client-defined parameter returned in message.
|
||||
* \param event Event for client's WSF handler.
|
||||
*
|
||||
* \return Token value.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t SecAesRev(uint8_t *pKey, uint8_t *pPlaintext, wsfHandlerId_t handlerId,
|
||||
uint16_t param, uint8_t event)
|
||||
{
|
||||
secQueueBuf_t *pBuf;
|
||||
|
||||
/* allocate a buffer */
|
||||
if ((pBuf = WsfMsgAlloc(sizeof(secQueueBuf_t))) != NULL)
|
||||
{
|
||||
pBuf->msg.hdr.status = getNextToken();
|
||||
pBuf->msg.hdr.param = param;
|
||||
pBuf->msg.hdr.event = event;
|
||||
|
||||
pBuf->type = SEC_TYPE_AES_REV;
|
||||
|
||||
/* call HCI encrypt function */
|
||||
SecLeEncryptCmd(pKey, pPlaintext, pBuf, handlerId);
|
||||
|
||||
return pBuf->msg.hdr.status;
|
||||
}
|
||||
|
||||
return SEC_TOKEN_INVALID;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Callback for HCI encryption for AES operations. Note this version reverses the
|
||||
* ciphertext bytes.
|
||||
*
|
||||
* \param pBuf Pointer to sec queue element.
|
||||
* \param pEvent Pointer to HCI event.
|
||||
* \param handlerId WSF handler ID.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecAesRevHciCback(secQueueBuf_t *pBuf, hciEvt_t *pEvent, wsfHandlerId_t handlerId)
|
||||
{
|
||||
secAes_t *pAes = (secAes_t *) &pBuf->msg;
|
||||
|
||||
/* set encrypted data pointer and copy */
|
||||
pAes->pCiphertext = pBuf->ciphertext;
|
||||
|
||||
Calc128Cpy(pAes->pCiphertext, pEvent->leEncryptCmdCmpl.data);
|
||||
|
||||
/* send message */
|
||||
WsfMsgSend(handlerId, pAes);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to initialize AES secuirity.
|
||||
*
|
||||
* \param none.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecAesRevInit()
|
||||
{
|
||||
secCb.hciCbackTbl[SEC_TYPE_AES_REV] = SecAesRevHciCback;
|
||||
}
|
||||
Vendored
+595
@@ -0,0 +1,595 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Counter with CBC-MAC (CCM) mode security - HCI AES.
|
||||
*
|
||||
* Copyright (c) 2018-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "sec_api.h"
|
||||
#include "sec_main.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "hci_api.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
|
||||
#ifndef SEC_CCM_CFG
|
||||
#define SEC_CCM_CFG SEC_CCM_CFG_HCI
|
||||
#endif
|
||||
|
||||
#if SEC_CCM_CFG == SEC_CCM_CFG_HCI
|
||||
|
||||
/**************************************************************************************************
|
||||
Constants
|
||||
**************************************************************************************************/
|
||||
|
||||
/* State machine states */
|
||||
enum
|
||||
{
|
||||
SEC_CCM_STATE_XI_HDR,
|
||||
SEC_CCM_STATE_XI_MSG,
|
||||
SEC_CCM_STATE_S0,
|
||||
SEC_CCM_STATE_SI,
|
||||
SEC_CCM_STATE_MIC_COMPLETE,
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
External Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Global security control block */
|
||||
extern secCb_t secCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Exclusive-or two 128-bit integers and return the result in pDst.
|
||||
*
|
||||
* \param pDst Pointer to destination.
|
||||
* \param pSrc Pointer to source.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCcmCalcXor(uint8_t *pDst, uint8_t *pSrc, uint8_t size)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
*pDst++ ^= *pSrc++;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn secCcmBlockEncrypt
|
||||
*
|
||||
* \brief Perform a 16-byte block encryption (HCI AES)
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CCM algorithm control block.
|
||||
* \param pText Pointer to text to encrypt (16 bytes).
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCcmBlockEncrypt(secQueueBuf_t *pBuf, uint8_t *pText)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t*) pBuf->pCb;
|
||||
SecLeEncryptCmd(pCcm->key, pText, pBuf, pCcm->handlerId);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn secCcmGenX0
|
||||
*
|
||||
* \brief Generate X_0 := E(K, B_0)
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CCM algorithm control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCcmGenX0(secQueueBuf_t *pBuf)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t*) pBuf->pCb;
|
||||
|
||||
/* Scratch buffer contains nonce, add flags and message length */
|
||||
pCcm->scratch[0] = (SEC_CCM_L - 1) | (((pCcm->micLen - 2) / 2) << 3) | ((pCcm->clearLen > 0? 1:0) << 6);
|
||||
pCcm->scratch[SEC_BLOCK_LEN - 2] = pCcm->textLen >> 8;
|
||||
pCcm->scratch[SEC_BLOCK_LEN - 1] = pCcm->textLen & 0xFF;
|
||||
|
||||
pCcm->state = pCcm->clearLen > 0 ? SEC_CCM_STATE_XI_HDR : SEC_CCM_STATE_XI_MSG;
|
||||
pCcm->position = 0;
|
||||
|
||||
/* AES Operation */
|
||||
secCcmBlockEncrypt(pBuf, pCcm->scratch);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn secCcmGenXiHdr
|
||||
*
|
||||
* \brief Generate X_i header if clear text (additional data) is present.
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CCM algorithm control block.
|
||||
* \param pPriorX 16 byte buffer containing X_i-1.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCcmGenXiHdr(secQueueBuf_t *pBuf, uint8_t *pPriorX)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t*) pBuf->pCb;
|
||||
uint8_t b_i[SEC_BLOCK_LEN];
|
||||
uint16_t remaining;
|
||||
uint16_t offset = 0;
|
||||
|
||||
if (pCcm->position == 0)
|
||||
{
|
||||
/* Copy additional data into working buffer */
|
||||
memcpy(pCcm->pWorking, pCcm->pClear, pCcm->clearLen);
|
||||
|
||||
/* First two bytes of b_0 contain length of additional data */
|
||||
b_i[0] = pCcm->clearLen >> 8;
|
||||
b_i[1] = pCcm->clearLen & 0xFF;
|
||||
pCcm->position = offset = 2;
|
||||
}
|
||||
|
||||
remaining = (int16_t) pCcm->clearLen - pCcm->position + 2;
|
||||
|
||||
/* Copy additional to b_i */
|
||||
if (remaining >= SEC_BLOCK_LEN - offset)
|
||||
{
|
||||
memcpy(b_i + offset, pCcm->pClear + pCcm->position - 2, SEC_BLOCK_LEN - offset);
|
||||
pCcm->position += SEC_BLOCK_LEN - offset;
|
||||
|
||||
if (remaining == SEC_BLOCK_LEN - offset)
|
||||
{
|
||||
pCcm->state = SEC_CCM_STATE_XI_MSG;
|
||||
pCcm->position = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(b_i + offset, pCcm->pClear + pCcm->position - 2, remaining);
|
||||
memset(b_i + offset + remaining, 0, SEC_BLOCK_LEN - remaining - offset);
|
||||
pCcm->state = SEC_CCM_STATE_XI_MSG;
|
||||
pCcm->position = 0;
|
||||
}
|
||||
|
||||
/* X_i XOR B_i */
|
||||
Calc128Xor(b_i, pPriorX);
|
||||
|
||||
/* AES Operation */
|
||||
secCcmBlockEncrypt(pBuf, b_i);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn secCcmGenXiMsg
|
||||
*
|
||||
* \brief Generate X_i for the message text.
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CCM algorithm control block.
|
||||
* \param pPriorX 16 byte buffer containing X_i-1.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCcmGenXiMsg(secQueueBuf_t *pBuf, uint8_t *pPriorX)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t*) pBuf->pCb;
|
||||
uint8_t b_i[SEC_BLOCK_LEN];
|
||||
uint16_t remaining = (int16_t) pCcm->textLen - pCcm->position;
|
||||
|
||||
/* Copy data to b_i */
|
||||
if (remaining >= SEC_BLOCK_LEN)
|
||||
{
|
||||
Calc128Cpy(b_i, pCcm->pText + pCcm->position);
|
||||
pCcm->position += SEC_BLOCK_LEN;
|
||||
|
||||
if (remaining == SEC_BLOCK_LEN)
|
||||
{
|
||||
pCcm->state = SEC_CCM_STATE_S0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(b_i, pCcm->pText + pCcm->position, remaining);
|
||||
memset(b_i + remaining, 0, SEC_BLOCK_LEN - remaining);
|
||||
pCcm->state = SEC_CCM_STATE_S0;
|
||||
}
|
||||
|
||||
/* X_i XOR B_i */
|
||||
Calc128Xor(b_i, pPriorX);
|
||||
|
||||
/* AES Operation */
|
||||
secCcmBlockEncrypt(pBuf, b_i);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn secCcmGenS0
|
||||
*
|
||||
* \brief Generate S_0 := E(K, A_0)
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CCM algorithm control block.
|
||||
* \param x_n 16 byte buffer containing X_n (containing T).
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCcmGenS0(secQueueBuf_t *pBuf, uint8_t *x_n)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t*) pBuf->pCb;
|
||||
uint16_t offset = pCcm->textLen + pCcm->clearLen;
|
||||
|
||||
/* Copy T to working buffer */
|
||||
memcpy(pCcm->pWorking + offset, x_n, pCcm->micLen);
|
||||
|
||||
/* Scratch buffer contains nonce, add flags and counter */
|
||||
pCcm->scratch[0] = (SEC_CCM_L - 1);
|
||||
pCcm->scratch[SEC_BLOCK_LEN - 2] = pCcm->scratch[SEC_BLOCK_LEN - 1] = 0;
|
||||
|
||||
pCcm->state = SEC_CCM_STATE_MIC_COMPLETE;
|
||||
|
||||
/* AES Operation */
|
||||
secCcmBlockEncrypt(pBuf, pCcm->scratch);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn secCcmGenS1
|
||||
*
|
||||
* \brief Generate S_1 := E(K, A_1)
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CCM algorithm control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCcmGenS1(secQueueBuf_t *pBuf)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t*) pBuf->pCb;
|
||||
|
||||
/* Set counter. */
|
||||
pCcm->counter = 1;
|
||||
|
||||
/* Scratch buffer contains nonce, add flags and counter. */
|
||||
pCcm->scratch[0] = (SEC_CCM_L - 1);
|
||||
pCcm->scratch[SEC_BLOCK_LEN - 2] = 0;
|
||||
pCcm->scratch[SEC_BLOCK_LEN - 1] = 1;
|
||||
|
||||
/* Change state to S_i state. */
|
||||
pCcm->state = SEC_CCM_STATE_SI;
|
||||
|
||||
/* AES Operation. */
|
||||
secCcmBlockEncrypt(pBuf, pCcm->scratch);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn secCcmMicComplete
|
||||
*
|
||||
* \brief Called when MIC calculation is complete.
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CCM algorithm control block.
|
||||
* \param s_0 16 byte buffer containing S_0.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCcmMicComplete(secQueueBuf_t *pBuf, uint8_t *s_0)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t*) pBuf->pCb;
|
||||
int16_t micOffset;
|
||||
|
||||
/* MIC = s_0 XOR T (store in result buffer). */
|
||||
micOffset = pCcm->textLen + pCcm->clearLen;
|
||||
secCcmCalcXor(pCcm->pWorking + micOffset, s_0, pCcm->micLen);
|
||||
|
||||
if (pCcm->operation == SEC_CCM_OP_ENCRYPT)
|
||||
{
|
||||
/* When encrypting, continue S_i calculations */
|
||||
secCcmGenS1(pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Decryption complete. Send notification. */
|
||||
secCcmDecMsg_t *pMsg = (secCcmDecMsg_t *) &pBuf->msg;
|
||||
|
||||
/* Verify MIC value */
|
||||
if (memcmp(pCcm->pRcvMic, pCcm->pWorking + micOffset, pCcm->micLen) == 0)
|
||||
{
|
||||
pMsg->pText = pCcm->pWorking + pCcm->clearLen;
|
||||
pMsg->textLen = pCcm->textLen;
|
||||
pMsg->success = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pMsg->pText = NULL;
|
||||
pMsg->textLen = 0;
|
||||
pMsg->success = FALSE;
|
||||
}
|
||||
|
||||
WsfMsgSend(pCcm->handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn secCcmGenSi
|
||||
*
|
||||
* \brief Generate S_i := E(K, A_i)
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CCM algorithm control block.
|
||||
* \param pPriorS 16 byte buffer containing S_i-1.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCcmGenSi(secQueueBuf_t *pBuf, uint8_t *pPriorS)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t*) pBuf->pCb;
|
||||
int16_t resultOffset = 0;
|
||||
uint16_t len;
|
||||
|
||||
/* Determine length of XOR operation. */
|
||||
len = pCcm->textLen - ((pCcm->counter - 1) * SEC_BLOCK_LEN);
|
||||
len = len > SEC_BLOCK_LEN? SEC_BLOCK_LEN : len;
|
||||
|
||||
/* m_i XOR s_i+1. */
|
||||
resultOffset = (pCcm->counter - 1) * SEC_BLOCK_LEN + pCcm->clearLen;
|
||||
|
||||
secCcmCalcXor(pCcm->pWorking + resultOffset, pPriorS, (uint8_t) len);
|
||||
|
||||
if (pCcm->counter * SEC_BLOCK_LEN >= pCcm->textLen)
|
||||
{
|
||||
if (pCcm->operation == SEC_CCM_OP_ENCRYPT)
|
||||
{
|
||||
/* Encription complete. Send notification. */
|
||||
secCcmEncMsg_t *pMsg = (secCcmEncMsg_t *) &pBuf->msg;
|
||||
|
||||
pMsg->pCiphertext = pCcm->pWorking;
|
||||
pMsg->textLen = pCcm->textLen + pCcm->clearLen + pCcm->micLen;
|
||||
WsfMsgSend(pCcm->handlerId, pMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set pText to point to the decrypted result in pWorking */
|
||||
pCcm->pText = pCcm->pWorking + pCcm->clearLen;
|
||||
|
||||
/* Begin calculating the MIC */
|
||||
secCcmGenX0(pBuf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Update counter. */
|
||||
pCcm->counter++;
|
||||
pCcm->scratch[SEC_BLOCK_LEN - 2] = pCcm->counter >> 8;
|
||||
pCcm->scratch[SEC_BLOCK_LEN - 1] = pCcm->counter & 0xFF;
|
||||
|
||||
/* AES Operation. */
|
||||
secCcmBlockEncrypt(pBuf, pCcm->scratch);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn SecCcmBlockEncryptCmpl
|
||||
*
|
||||
* \brief Called when a block encryption operation completes.
|
||||
*
|
||||
* \param pParam Pointer to security control block.
|
||||
* \param pCypherText Pointer to encrypt result.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecCcmHciCback(secQueueBuf_t *pBuf, hciEvt_t *pEvent, wsfHandlerId_t handlerId)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t *) pBuf->pCb;
|
||||
|
||||
switch (pCcm->state)
|
||||
{
|
||||
case SEC_CCM_STATE_XI_HDR:
|
||||
secCcmGenXiHdr(pBuf, pEvent->leEncryptCmdCmpl.data);
|
||||
break;
|
||||
|
||||
case SEC_CCM_STATE_XI_MSG:
|
||||
secCcmGenXiMsg(pBuf, pEvent->leEncryptCmdCmpl.data);
|
||||
break;
|
||||
|
||||
case SEC_CCM_STATE_S0:
|
||||
secCcmGenS0(pBuf, pEvent->leEncryptCmdCmpl.data);
|
||||
break;
|
||||
|
||||
case SEC_CCM_STATE_SI:
|
||||
secCcmGenSi(pBuf, pEvent->leEncryptCmdCmpl.data);
|
||||
break;
|
||||
|
||||
case SEC_CCM_STATE_MIC_COMPLETE:
|
||||
secCcmMicComplete(pBuf, pEvent->leEncryptCmdCmpl.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn SecCcmEnc
|
||||
*
|
||||
* \brief Execute the CCM-Mode encryption algorithm.
|
||||
*
|
||||
* \param pKey Pointer to encryption key (SEC_CCM_KEY_LEN bytes).
|
||||
* \param pNonce Pointer to nonce (SEC_CCM_NONCE_LEN bytes).
|
||||
* \param pPlainText Pointer to text to encrypt.
|
||||
* \param textLen Length of pPlainText in bytes.
|
||||
* \param pClear Pointer to additional, unencrypted authentication text.
|
||||
* \param clearLen Length of pClear in bytes.
|
||||
* \param micLen Size of MIC in bytes (4, 8 or 16).
|
||||
* \param pResult Buffer to hold result (returned in complete event).
|
||||
* \param handlerId Task handler ID to receive complete event.
|
||||
* \param param Optional parameter passed in complete event.
|
||||
* \param event Event ID of complete event.
|
||||
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecCcmEnc(const uint8_t *pKey, uint8_t *pNonce, uint8_t *pPlainText, uint16_t textLen,
|
||||
uint8_t *pClear, uint16_t clearLen, uint8_t micLen, uint8_t *pResult,
|
||||
wsfHandlerId_t handlerId, uint16_t param, uint8_t event)
|
||||
{
|
||||
secQueueBuf_t *pBuf;
|
||||
uint16_t bufSize = sizeof(secQueueBuf_t) + sizeof(secCcmSecCb_t);
|
||||
|
||||
WSF_ASSERT(clearLen < SEC_CCM_MAX_ADDITIONAL_LEN);
|
||||
|
||||
if ((pBuf = WsfMsgAlloc(bufSize)) != NULL)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t *) (pBuf + 1);
|
||||
|
||||
/* Setup queue buffer */
|
||||
pBuf->pCb = pCcm;
|
||||
pBuf->type = SEC_TYPE_CCM;
|
||||
|
||||
pBuf->msg.hdr.status = secCb.token++;
|
||||
pBuf->msg.hdr.param = param;
|
||||
pBuf->msg.hdr.event = event;
|
||||
|
||||
pCcm->handlerId = handlerId;
|
||||
|
||||
pCcm->pText = pPlainText;
|
||||
pCcm->textLen = textLen;
|
||||
pCcm->pClear = pClear;
|
||||
pCcm->pWorking = pResult;
|
||||
pCcm->clearLen = clearLen;
|
||||
pCcm->micLen = micLen;
|
||||
pCcm->counter = 0;
|
||||
|
||||
memcpy(pCcm->pWorking + clearLen, pPlainText, textLen);
|
||||
memcpy(&pCcm->scratch[1], pNonce, SEC_CCM_NONCE_LEN);
|
||||
Calc128Cpy(pCcm->key, (uint8_t *) pKey);
|
||||
|
||||
pCcm->operation = SEC_CCM_OP_ENCRYPT;
|
||||
|
||||
/* Begin encryption of text by generation of X_0 */
|
||||
secCcmGenX0(pBuf);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn SecCcmDec
|
||||
*
|
||||
* \brief Execute the CCM-Mode verify and decrypt algorithm.
|
||||
*
|
||||
* \param pKey Pointer to encryption key (SEC_CCM_KEY_LEN bytes).
|
||||
* \param pNonce Pointer to nonce (SEC_CCM_NONCE_LEN bytes).
|
||||
* \param pCypherText Pointer to text to decrypt.
|
||||
* \param textLen Length of pCypherText in bytes.
|
||||
* \param pClear Pointer to additional, unencrypted authentication text.
|
||||
* \param clearLen Length of pClear in bytes.
|
||||
* \param pMic Pointer to authentication digest.
|
||||
* \param micLen Size of MIC in bytes (4, 8 or 16).
|
||||
* \param pResult Buffer to hold result (returned in complete event).
|
||||
* \param handlerId Task handler ID to receive complete event.
|
||||
* \param param Optional parameter passed in complete event.
|
||||
* \param event Event ID of complete event.
|
||||
*
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecCcmDec(const uint8_t *pKey, uint8_t *pNonce, uint8_t *pCypherText, uint16_t textLen,
|
||||
uint8_t *pClear, uint16_t clearLen, uint8_t *pMic, uint8_t micLen,
|
||||
uint8_t *pResult, wsfHandlerId_t handlerId, uint16_t param, uint8_t event)
|
||||
{
|
||||
secQueueBuf_t *pBuf;
|
||||
uint16_t bufSize = sizeof(secQueueBuf_t) + sizeof(secCcmSecCb_t);
|
||||
|
||||
WSF_ASSERT(clearLen < SEC_CCM_MAX_ADDITIONAL_LEN);
|
||||
|
||||
if ((pBuf = WsfMsgAlloc(bufSize)) != NULL)
|
||||
{
|
||||
secCcmSecCb_t *pCcm = (secCcmSecCb_t *) (pBuf + 1);
|
||||
|
||||
/* Setup queue buffer */
|
||||
pBuf->pCb = pCcm;
|
||||
pBuf->type = SEC_TYPE_CCM;
|
||||
|
||||
pBuf->msg.hdr.status = secCb.token++;
|
||||
pBuf->msg.hdr.param = param;
|
||||
pBuf->msg.hdr.event = event;
|
||||
|
||||
pCcm->handlerId = handlerId;
|
||||
|
||||
pCcm->pClear = pClear;
|
||||
pCcm->pRcvMic = pMic;
|
||||
pCcm->pWorking = pResult;
|
||||
pCcm->textLen = textLen;
|
||||
pCcm->clearLen = clearLen;
|
||||
pCcm->micLen = micLen;
|
||||
pCcm->counter = 0;
|
||||
|
||||
/* Prepare the working buffer */
|
||||
memcpy(pCcm->pWorking, pClear, clearLen);
|
||||
memcpy(pCcm->pWorking + clearLen, pCypherText, textLen);
|
||||
memcpy(pCcm->pWorking + clearLen + textLen, pMic, micLen);
|
||||
|
||||
memcpy(&pCcm->scratch[1], pNonce, SEC_CCM_NONCE_LEN);
|
||||
Calc128Cpy(pCcm->key, (uint8_t *) pKey);
|
||||
|
||||
pCcm->operation = SEC_CCM_OP_DECRYPT;
|
||||
|
||||
/* Begin decryption of text by generation of S_1 */
|
||||
secCcmGenS1(pBuf);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn SecCcmInit
|
||||
*
|
||||
* \brief Called to initialize CCM-Mode security.
|
||||
*
|
||||
* \param None.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecCcmInit(void)
|
||||
{
|
||||
secCb.hciCbackTbl[SEC_TYPE_CCM] = SecCcmHciCback;
|
||||
}
|
||||
|
||||
#endif /* SEC_CCM_CFG */
|
||||
Vendored
+323
@@ -0,0 +1,323 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief AES and random number security service implemented using HCI.
|
||||
*
|
||||
* Copyright (c) 2010-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "sec_api.h"
|
||||
#include "sec_main.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "hci_api.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
|
||||
#ifndef SEC_CMAC_CFG
|
||||
#define SEC_CMAC_CFG SEC_CMAC_CFG_HCI
|
||||
#endif
|
||||
|
||||
#if SEC_CMAC_CFG == SEC_CMAC_CFG_HCI
|
||||
|
||||
enum
|
||||
{
|
||||
SEC_CMAC_STATE_SUBKEY,
|
||||
SEC_CMAC_STATE_BLOCK,
|
||||
SEC_CMAC_STATE_COMPLETE,
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
External Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
extern secCb_t secCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Continue the execution of the CMAC algorithm over the next message block.
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CMAC algorithm control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCmacProcessBlock(secQueueBuf_t *pBuf)
|
||||
{
|
||||
secCmacSecCb_t *pCmac = (secCmacSecCb_t*) pBuf->pCb;
|
||||
uint8_t text[SEC_BLOCK_LEN];
|
||||
uint8_t *pMn = pCmac->pPlainText + pCmac->position;
|
||||
int16_t remaining = (int16_t) pCmac->len - pCmac->position;
|
||||
|
||||
/* Check for Last Block */
|
||||
if (remaining <= SEC_BLOCK_LEN)
|
||||
{
|
||||
memcpy(text, pMn, remaining);
|
||||
|
||||
/* Pad the message if necessary */
|
||||
if (remaining != SEC_BLOCK_LEN)
|
||||
{
|
||||
memset(text + remaining, 0, SEC_BLOCK_LEN - remaining);
|
||||
text[remaining] = 0x80;
|
||||
}
|
||||
|
||||
/* XOr the subkey */
|
||||
Calc128Xor(text, pCmac->subkey);
|
||||
pCmac->state = SEC_CMAC_STATE_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Copy the block to the buffer */
|
||||
Calc128Cpy(text, pMn);
|
||||
}
|
||||
|
||||
if (pCmac->position != 0)
|
||||
{
|
||||
/* Except for first block, XOr the previous AES calculation */
|
||||
Calc128Xor(text, pBuf->ciphertext);
|
||||
}
|
||||
|
||||
pCmac->position += SEC_BLOCK_LEN;
|
||||
|
||||
SecLeEncryptCmd(pCmac->key, text, pBuf, pCmac->handlerId);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Step 1 to generate the subkey used in the CMAC algorithm.
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CMAC algorithm control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCmacGenSubkey1(secQueueBuf_t *pBuf)
|
||||
{
|
||||
secCmacSecCb_t *pCmac = (secCmacSecCb_t*) pBuf->pCb;
|
||||
uint8_t buf[SEC_BLOCK_LEN];
|
||||
|
||||
/* Perform aes on the key with a constant zero */
|
||||
memset(buf, 0, SEC_BLOCK_LEN);
|
||||
|
||||
SecLeEncryptCmd(pCmac->key, buf, pBuf, pCmac->handlerId);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Left shift a buffer of WSF_CMAC_KEY_LEN bytes by N bits.
|
||||
*
|
||||
* \param pBuf Buffer to left shift.
|
||||
* \param shift Number of bits to shift.
|
||||
*
|
||||
* \return The overflow of the operaiton.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t secCmacKeyShift(uint8_t *pBuf, uint8_t shift)
|
||||
{
|
||||
uint8_t overflow, i;
|
||||
uint8_t finalOverflow = pBuf[0] >> (8 - shift);
|
||||
|
||||
for (i = 0; i < SEC_CMAC_KEY_LEN; i++)
|
||||
{
|
||||
/* store shifted bits for next byte */
|
||||
if (i < SEC_CMAC_KEY_LEN-1)
|
||||
{
|
||||
overflow = pBuf[i+1] >> (8 - shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
overflow = 0;
|
||||
}
|
||||
|
||||
/* shift byte and OR in shifted bits from previous byte */
|
||||
pBuf[i] = (pBuf[i] << shift) | overflow;
|
||||
}
|
||||
|
||||
return finalOverflow;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Complete generation of the subkey used in the CMAC algorithm.
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CMAC algorithm control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCmacGenSubkey2(secQueueBuf_t *pBuf)
|
||||
{
|
||||
secCmacSecCb_t *pCmac = (secCmacSecCb_t*) pBuf->pCb;
|
||||
uint8_t overflow;
|
||||
|
||||
/* Copy the result of the AES oepration */
|
||||
Calc128Cpy(pCmac->subkey, pBuf->ciphertext);
|
||||
|
||||
/* Calculate the K1 subkey */
|
||||
overflow = secCmacKeyShift(pCmac->subkey, 1);
|
||||
|
||||
if (overflow)
|
||||
{
|
||||
pCmac->subkey[SEC_BLOCK_LEN-1] ^= SEC_CMAC_RB;
|
||||
}
|
||||
|
||||
if (pCmac->len % SEC_BLOCK_LEN != 0)
|
||||
{
|
||||
/* If the message len is not a multiple of SEC_BLOCK_LEN */
|
||||
/* Continue with generation of the K2 subkey based on the K1 key */
|
||||
overflow = secCmacKeyShift(pCmac->subkey, 1);
|
||||
|
||||
if (overflow)
|
||||
{
|
||||
pCmac->subkey[SEC_BLOCK_LEN-1] ^= SEC_CMAC_RB;
|
||||
}
|
||||
}
|
||||
|
||||
/* Begin CMAC calculation */
|
||||
pCmac->state = SEC_CMAC_STATE_BLOCK;
|
||||
secCmacProcessBlock(pBuf);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a message to the handler with CMAC result.
|
||||
*
|
||||
* \param pBuf Security queue buffer containing CMAC algorithm control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secCmacComplete(secQueueBuf_t *pBuf)
|
||||
{
|
||||
/* CMAC is complete, copy and send result to handler */
|
||||
secCmacMsg_t *pMsg = (secCmacMsg_t *) &pBuf->msg;
|
||||
secCmacSecCb_t *pCmac = (secCmacSecCb_t *) pBuf->pCb;
|
||||
|
||||
pMsg->pCiphertext = pBuf->ciphertext;
|
||||
pMsg->pPlainText = pCmac->pPlainText;
|
||||
|
||||
WsfMsgSend(pCmac->handlerId, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Callback for HCI encryption for CMAC operations.
|
||||
*
|
||||
* \param pBuf Pointer to sec queue element.
|
||||
* \param pEvent Pointer to HCI event.
|
||||
* \param handlerId WSF handler ID.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecCmacHciCback(secQueueBuf_t *pBuf, hciEvt_t *pEvent, wsfHandlerId_t handlerId)
|
||||
{
|
||||
secCmacSecCb_t *pCmac = (secCmacSecCb_t *) pBuf->pCb;
|
||||
|
||||
if (pCmac)
|
||||
{
|
||||
Calc128Cpy(pBuf->ciphertext, pEvent->leEncryptCmdCmpl.data);
|
||||
|
||||
switch (pCmac->state)
|
||||
{
|
||||
case SEC_CMAC_STATE_SUBKEY:
|
||||
secCmacGenSubkey2(pBuf);
|
||||
break;
|
||||
|
||||
case SEC_CMAC_STATE_BLOCK:
|
||||
secCmacProcessBlock(pBuf);
|
||||
break;
|
||||
|
||||
case SEC_CMAC_STATE_COMPLETE:
|
||||
secCmacComplete(pBuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the CMAC algorithm.
|
||||
*
|
||||
* \param pKey Key used in CMAC operation.
|
||||
* \param pPlainText Data to perform CMAC operation over
|
||||
* \param len Size of pPlaintext in bytes.
|
||||
* \param handlerId WSF handler ID for client.
|
||||
* \param param Optional parameter sent to client's WSF handler.
|
||||
* \param event Event for client's WSF handler.
|
||||
*
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecCmac(const uint8_t *pKey, uint8_t *pPlainText, uint16_t textLen, wsfHandlerId_t handlerId,
|
||||
uint16_t param, uint8_t event)
|
||||
{
|
||||
secQueueBuf_t *pBuf;
|
||||
uint16_t bufSize = sizeof(secQueueBuf_t) + sizeof(secCmacSecCb_t);
|
||||
|
||||
if ((pBuf = WsfMsgAlloc(bufSize)) != NULL)
|
||||
{
|
||||
secCmacSecCb_t *pCmacCb = (secCmacSecCb_t *) (pBuf + 1);
|
||||
|
||||
/* Setup queue buffer */
|
||||
pBuf->pCb = pCmacCb;
|
||||
pBuf->type = SEC_TYPE_CMAC;
|
||||
|
||||
pBuf->msg.hdr.status = secCb.token++;
|
||||
pBuf->msg.hdr.param = param;
|
||||
pBuf->msg.hdr.event = event;
|
||||
|
||||
pCmacCb->pPlainText = pPlainText;
|
||||
|
||||
pCmacCb->len = textLen;
|
||||
pCmacCb->position = 0;
|
||||
pCmacCb->handlerId = handlerId;
|
||||
pCmacCb->state = SEC_CMAC_STATE_SUBKEY;
|
||||
|
||||
/* Copy key */
|
||||
Calc128Cpy(pCmacCb->key, (uint8_t *) pKey);
|
||||
|
||||
/* Start the CMAC process by calculating the subkey */
|
||||
secCmacGenSubkey1(pBuf);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to initialize CMAC security.
|
||||
*
|
||||
* \param None.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecCmacInit()
|
||||
{
|
||||
secCb.hciCbackTbl[SEC_TYPE_CMAC] = SecCmacHciCback;
|
||||
}
|
||||
|
||||
#endif /* SEC_CMAC_CFG */
|
||||
Vendored
+160
@@ -0,0 +1,160 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Security ECC implementation using debug keys.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "sec_api.h"
|
||||
#include "sec_main.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "hci_api.h"
|
||||
#include "util/calc128.h"
|
||||
|
||||
#ifndef SEC_ECC_CFG
|
||||
#define SEC_ECC_CFG SEC_ECC_CFG_DEBUG
|
||||
#endif
|
||||
|
||||
#if SEC_ECC_CFG == SEC_ECC_CFG_DEBUG
|
||||
|
||||
/* Debug Keys */
|
||||
static const uint8_t debugPrivateKey[] = {0x3f, 0x49, 0xf6, 0xd4, 0xa3, 0xc5, 0x5f, 0x38,
|
||||
0x74, 0xc9, 0xb3, 0xe3, 0xd2, 0x10, 0x3f, 0x50,
|
||||
0x4a, 0xff, 0x60, 0x7b, 0xeb, 0x40, 0xb7, 0x99,
|
||||
0x58, 0x99, 0xb8, 0xa6, 0xcd, 0x3c, 0x1a, 0xbd};
|
||||
|
||||
static const uint8_t debugPublicKeyX[] = {0x20, 0xb0, 0x03, 0xd2, 0xf2, 0x97, 0xbe, 0x2c,
|
||||
0x5e, 0x2c, 0x83, 0xa7, 0xe9, 0xf9, 0xa5, 0xb9,
|
||||
0xef, 0xf4, 0x91, 0x11, 0xac, 0xf4, 0xfd, 0xdb,
|
||||
0xcc, 0x03, 0x01, 0x48, 0x0e, 0x35, 0x9d, 0xe6};
|
||||
|
||||
static const uint8_t debugPublicKeyY[] = {0xdc, 0x80, 0x9c, 0x49, 0x65, 0x2a, 0xeb, 0x6d,
|
||||
0x63, 0x32, 0x9a, 0xbf, 0x5a, 0x52, 0x15, 0x5c,
|
||||
0x76, 0x63, 0x45, 0xc2, 0x8f, 0xed, 0x30, 0x24,
|
||||
0x74, 0x1c, 0x8e, 0xd0, 0x15, 0x89, 0xd2, 0x8b};
|
||||
|
||||
static const uint8_t debugSharedSecret[] = {0x49, 0x4c, 0xfd, 0x99, 0x6f, 0x40, 0x17, 0xf5,
|
||||
0xb5, 0x48, 0xba, 0x66, 0x99, 0x60, 0x64, 0x08,
|
||||
0x62, 0x75, 0xd5, 0x1f, 0xe0, 0x8e, 0x56, 0x36,
|
||||
0xb9, 0x36, 0xd1, 0xe4, 0x57, 0x46, 0x4b, 0xed};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Callback for HCI encryption for ECC operations.
|
||||
*
|
||||
* \param pBuf Pointer to sec queue element.
|
||||
* \param pEvent Pointer to HCI event.
|
||||
* \param handlerId WSF handler ID.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecEccHciCback(secQueueBuf_t *pBuf, hciEvt_t *pEvent, wsfHandlerId_t handlerId)
|
||||
{
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate an ECC key.
|
||||
*
|
||||
* \param handlerId WSF handler ID for client.
|
||||
* \param param Optional parameter sent to client's WSF handler.
|
||||
* \param event Event for client's WSF handler.
|
||||
*
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecEccGenKey(wsfHandlerId_t handlerId, uint16_t param, uint8_t event)
|
||||
{
|
||||
secEccMsg_t *pMsg = WsfMsgAlloc(sizeof(secEccMsg_t));
|
||||
|
||||
if (pMsg)
|
||||
{
|
||||
/* Generate the keys */
|
||||
memcpy(pMsg->data.key.pubKey_x, debugPublicKeyX, SEC_ECC_KEY_LEN);
|
||||
memcpy(pMsg->data.key.pubKey_y, debugPublicKeyY, SEC_ECC_KEY_LEN);
|
||||
memcpy(pMsg->data.key.privKey, debugPrivateKey, SEC_ECC_KEY_LEN);
|
||||
|
||||
/* Send shared secret to handler */
|
||||
pMsg->hdr.event = event;
|
||||
pMsg->hdr.param = param;
|
||||
pMsg->hdr.status = HCI_SUCCESS;
|
||||
WsfMsgSend(handlerId, pMsg);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate an ECC key.
|
||||
*
|
||||
* \param pKey ECC Key structure.
|
||||
* \param handlerId WSF handler ID for client.
|
||||
* \param param Optional parameter sent to client's WSF handler.
|
||||
* \param event Event for client's WSF handler.
|
||||
*
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecEccGenSharedSecret(secEccKey_t *pKey, wsfHandlerId_t handlerId, uint16_t param, uint8_t event)
|
||||
{
|
||||
secEccMsg_t *pMsg = WsfMsgAlloc(sizeof(secEccMsg_t));
|
||||
|
||||
if (pMsg)
|
||||
{
|
||||
memcpy(pMsg->data.sharedSecret.secret, debugSharedSecret, SEC_ECC_KEY_LEN);
|
||||
|
||||
/* Send shared secret to handler */
|
||||
pMsg->hdr.event = event;
|
||||
pMsg->hdr.param = param;
|
||||
pMsg->hdr.status = HCI_SUCCESS;
|
||||
WsfMsgSend(handlerId, pMsg);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to initialize ECC security.
|
||||
*
|
||||
* \param None.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecEccInit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif /* SEC_ECC_CFG */
|
||||
Vendored
+183
@@ -0,0 +1,183 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief WSF Security ECC implementation using HCI.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "sec_api.h"
|
||||
#include "sec_main.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "hci_api.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
|
||||
#ifndef SEC_ECC_CFG
|
||||
#define SEC_ECC_CFG SEC_ECC_CFG_HCI
|
||||
#endif
|
||||
|
||||
#if SEC_ECC_CFG == SEC_ECC_CFG_HCI
|
||||
|
||||
/**************************************************************************************************
|
||||
External Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
extern secCb_t secCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Callback for HCI encryption for ECC operations.
|
||||
*
|
||||
* \param pBuf Pointer to sec queue element.
|
||||
* \param pEvent Pointer to HCI event.
|
||||
* \param handlerId WSF handler ID.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecEccHciCback(secQueueBuf_t *pBuf, hciEvt_t *pEvent, wsfHandlerId_t handlerId)
|
||||
{
|
||||
secEccMsg_t *pMsg = (secEccMsg_t *) &pBuf->msg;
|
||||
|
||||
if (pEvent->hdr.event == HCI_LE_READ_LOCAL_P256_PUB_KEY_CMPL_CBACK_EVT)
|
||||
{
|
||||
/* Reverse copy the public key (to big endian) */
|
||||
WStrReverseCpy(pMsg->data.key.pubKey_x, pEvent->leP256.key, SEC_ECC_KEY_LEN);
|
||||
WStrReverseCpy(pMsg->data.key.pubKey_y, pEvent->leP256.key + SEC_ECC_KEY_LEN, SEC_ECC_KEY_LEN);
|
||||
|
||||
/* Send shared secret to handler */
|
||||
pMsg->hdr.status = pEvent->leP256.status;
|
||||
WsfMsgSend(handlerId, pMsg);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_LE_GENERATE_DHKEY_CMPL_CBACK_EVT)
|
||||
{
|
||||
/* Reverse copy the DH key (to big endian) */
|
||||
WStrReverseCpy(pMsg->data.sharedSecret.secret, pEvent->leGenDHKey.key, SEC_ECC_KEY_LEN);
|
||||
|
||||
/* Send shared secret to handler */
|
||||
pMsg->hdr.status = pEvent->leGenDHKey.status;
|
||||
WsfMsgSend(handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate an ECC key.
|
||||
*
|
||||
* \param handlerId WSF handler ID for client.
|
||||
* \param param Optional parameter sent to client's WSF handler.
|
||||
* \param event Event for client's WSF handler.
|
||||
*
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecEccGenKey(wsfHandlerId_t handlerId, uint16_t param, uint8_t event)
|
||||
{
|
||||
secQueueBuf_t *pBuf;
|
||||
uint16_t bufSize = sizeof(secQueueBuf_t) + sizeof(secEccMsg_t);
|
||||
|
||||
if ((pBuf = WsfMsgAlloc(bufSize)) != NULL)
|
||||
{
|
||||
/* Record the event and parameter for use in the HCI response */
|
||||
pBuf->msg.hdr.param = param;
|
||||
pBuf->msg.hdr.event = event;
|
||||
pBuf->type = SEC_TYPE_DH;
|
||||
|
||||
/* queue buffer */
|
||||
WsfMsgEnq(&secCb.pubKeyQueue, handlerId, pBuf);
|
||||
|
||||
/* Request the local public key via HCI */
|
||||
HciLeReadLocalP256PubKey();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate an ECC key.
|
||||
*
|
||||
* \param pKey ECC Key structure.
|
||||
* \param handlerId WSF handler ID for client.
|
||||
* \param param Optional parameter sent to client's WSF handler.
|
||||
* \param event Event for client's WSF handler.
|
||||
*
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecEccGenSharedSecret(secEccKey_t *pKey, wsfHandlerId_t handlerId, uint16_t param, uint8_t event)
|
||||
{
|
||||
secQueueBuf_t *pBuf;
|
||||
uint16_t bufSize = sizeof(secQueueBuf_t) + sizeof(secEccMsg_t);
|
||||
|
||||
if ((pBuf = WsfMsgAlloc(bufSize)) != NULL)
|
||||
{
|
||||
uint8_t pubKeyX[SEC_ECC_KEY_LEN];
|
||||
uint8_t pubKeyY[SEC_ECC_KEY_LEN];
|
||||
|
||||
/* Record the event and parameter for use in the HCI response */
|
||||
pBuf->msg.hdr.param = param;
|
||||
pBuf->msg.hdr.event = event;
|
||||
pBuf->type = SEC_TYPE_DH;
|
||||
|
||||
/* queue buffer */
|
||||
WsfMsgEnq(&secCb.dhKeyQueue, handlerId, pBuf);
|
||||
|
||||
/* Reverse keys (to little endian) */
|
||||
WStrReverseCpy(pubKeyX, pKey->pubKey_x, SEC_ECC_KEY_LEN);
|
||||
WStrReverseCpy(pubKeyY, pKey->pubKey_y, SEC_ECC_KEY_LEN);
|
||||
|
||||
/* Request the DH Key via HCI */
|
||||
HciLeGenerateDHKey(pubKeyX, pubKeyY);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to initialize ECC security.
|
||||
*
|
||||
* \param None.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecEccInit()
|
||||
{
|
||||
secCb.hciCbackTbl[SEC_TYPE_DH] = SecEccHciCback;
|
||||
}
|
||||
|
||||
#endif /* SEC_ECC_CFG */
|
||||
Vendored
+195
@@ -0,0 +1,195 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Security service implemented using HCI.
|
||||
*
|
||||
* Copyright (c) 2010-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "sec_api.h"
|
||||
#include "sec_main.h"
|
||||
#include "hci_api.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
// #include "pal_crypto.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Security Control block */
|
||||
secCb_t secCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Callback for HCI encryption and random number events.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void secHciCback(hciEvt_t *pEvent)
|
||||
{
|
||||
secQueueBuf_t *pBuf = NULL;
|
||||
wsfHandlerId_t handlerId = 0;
|
||||
|
||||
/* Handle random number event. */
|
||||
switch (pEvent->hdr.event)
|
||||
{
|
||||
case HCI_LE_RAND_CMD_CMPL_CBACK_EVT:
|
||||
|
||||
/* Copy new data to circular buffer of random data. */
|
||||
memcpy(&secCb.rand[HCI_RAND_LEN * secCb.randTop], pEvent->leRandCmdCmpl.randNum, HCI_RAND_LEN);
|
||||
secCb.randTop = (secCb.randTop >= SEC_HCI_RAND_MULT - 1) ? 0 : secCb.randTop + 1;
|
||||
break;
|
||||
|
||||
case HCI_LE_ENCRYPT_CMD_CMPL_CBACK_EVT:
|
||||
pBuf = WsfMsgDeq(&secCb.aesEncQueue, &handlerId);
|
||||
|
||||
WSF_ASSERT(pBuf != NULL);
|
||||
|
||||
/* note: pBuf should never be NULL and is checked by assert above. */
|
||||
/* coverity[dereference] */
|
||||
if (pBuf->type == SEC_TYPE_CCM || pBuf->type == SEC_TYPE_CMAC || pBuf->type == SEC_TYPE_AES_REV)
|
||||
{
|
||||
WStrReverse(pEvent->leEncryptCmdCmpl.data, HCI_ENCRYPT_DATA_LEN);
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_LE_READ_LOCAL_P256_PUB_KEY_CMPL_CBACK_EVT:
|
||||
pBuf = WsfMsgDeq(&secCb.pubKeyQueue, &handlerId);
|
||||
break;
|
||||
|
||||
case HCI_LE_GENERATE_DHKEY_CMPL_CBACK_EVT:
|
||||
pBuf = WsfMsgDeq(&secCb.dhKeyQueue, &handlerId);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pBuf)
|
||||
{
|
||||
WSF_ASSERT(secCb.hciCbackTbl[pBuf->type]);
|
||||
secCb.hciCbackTbl[pBuf->type](pBuf, pEvent, handlerId);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the security service. This function should only be called once
|
||||
* upon system initialization.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecInit(void)
|
||||
{
|
||||
WSF_QUEUE_INIT(&secCb.aesEncQueue);
|
||||
WSF_QUEUE_INIT(&secCb.pubKeyQueue);
|
||||
WSF_QUEUE_INIT(&secCb.dhKeyQueue);
|
||||
|
||||
secCb.token = 0;
|
||||
|
||||
/* Register callback with HCI */
|
||||
HciSecRegister(secHciCback);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the random number service. This function should only be called once
|
||||
* upon system initialization.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecRandInit(void)
|
||||
{
|
||||
int8_t i;
|
||||
|
||||
/* get new random numbers */
|
||||
for (i=0; i<SEC_HCI_RAND_MULT; i++)
|
||||
{
|
||||
HciLeRandCmd();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function returns up to HCI_RAND_LEN * SEC_HCI_RAND_MULT bytes of random data to
|
||||
* a buffer provided by the client.
|
||||
*
|
||||
* \param pRand Pointer to returned random data.
|
||||
* \param randLen Length of random data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecRand(uint8_t *pRand, uint8_t randLen)
|
||||
{
|
||||
int8_t count = (randLen + HCI_RAND_LEN - 1) / HCI_RAND_LEN;
|
||||
uint8_t index = secCb.randBtm * HCI_RAND_LEN;
|
||||
|
||||
WSF_ASSERT(randLen <= SEC_RAND_DATA_LEN);
|
||||
|
||||
/* Copy from circular buffer of random data. */
|
||||
while (randLen--)
|
||||
{
|
||||
*pRand++ = secCb.rand[index];
|
||||
index = (index == SEC_RAND_DATA_LEN - 1) ? 0 : index + 1;
|
||||
}
|
||||
|
||||
while (count--)
|
||||
{
|
||||
/* Request more random data. */
|
||||
HciLeRandCmd();
|
||||
|
||||
/* Update copy index. */
|
||||
secCb.randBtm = (secCb.randBtm >= SEC_HCI_RAND_MULT - 1) ? 0 : secCb.randBtm + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Queue callback and call LE encrypt given most significant byte ordered key and data.
|
||||
*
|
||||
* \param pKey Pointer to key.
|
||||
* \param pText Pointer to text to encrypt.
|
||||
* \param pBuf Pointer to queue block.
|
||||
* \param handlerId Handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecLeEncryptCmd(uint8_t *pKey, uint8_t *pText, void *pBuf, wsfHandlerId_t handlerId)
|
||||
{
|
||||
uint8_t revKey[HCI_KEY_LEN];
|
||||
uint8_t revText[HCI_ENCRYPT_DATA_LEN];
|
||||
|
||||
WStrReverseCpy(revKey, pKey, HCI_KEY_LEN);
|
||||
WStrReverseCpy(revText, pText, HCI_ENCRYPT_DATA_LEN);
|
||||
|
||||
WsfMsgEnq(&secCb.aesEncQueue, handlerId, pBuf);
|
||||
HciLeEncryptCmd(revKey, revText);
|
||||
}
|
||||
Vendored
+162
@@ -0,0 +1,162 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Internal security service structures.
|
||||
*
|
||||
* Copyright (c) 2010-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef SEC_MAIN_H
|
||||
#define SEC_MAIN_H
|
||||
|
||||
#include "hci_api.h"
|
||||
// #include "pal_crypto.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
|
||||
/*! AES, CMAC and HCI algorithm block length */
|
||||
#define SEC_BLOCK_LEN 16
|
||||
|
||||
/* CMAC constant Rb */
|
||||
#define SEC_CMAC_RB 0x87
|
||||
|
||||
/*! Number or random bytes to keep in the secCb_t rand data buffer */
|
||||
#define SEC_RAND_DATA_LEN 32
|
||||
|
||||
/*! Multiple of HCI_RAND_LEN kept in the secCb_t rand data buffer */
|
||||
#define SEC_HCI_RAND_MULT (SEC_RAND_DATA_LEN / HCI_RAND_LEN)
|
||||
|
||||
/*! Compile time ECC configuration */
|
||||
#define SEC_ECC_CFG_DEBUG 0
|
||||
#define SEC_ECC_CFG_UECC 1
|
||||
#define SEC_ECC_CFG_HCI 2
|
||||
|
||||
/*! Compile time CMAC configuration */
|
||||
#define SEC_CMAC_CFG_PLATFORM 0
|
||||
#define SEC_CMAC_CFG_HCI 1
|
||||
|
||||
/*! Compile time CCM configuration */
|
||||
#define SEC_CCM_CFG_PLATFORM 0
|
||||
#define SEC_CCM_CFG_HCI 1
|
||||
|
||||
/*! CCM Operation (Encryption or Decryption) */
|
||||
#define SEC_CCM_OP_ENCRYPT 0
|
||||
#define SEC_CCM_OP_DECRYPT 1
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Enumeration of security operation types */
|
||||
enum
|
||||
{
|
||||
SEC_TYPE_AES,
|
||||
SEC_TYPE_CMAC,
|
||||
SEC_TYPE_DH,
|
||||
SEC_TYPE_CCM,
|
||||
SEC_TYPE_AES_REV,
|
||||
SEC_NUM_TYPES
|
||||
};
|
||||
|
||||
/*! Security queue element for CMAC operations */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t *pPlainText;
|
||||
uint8_t key[SEC_CMAC_KEY_LEN];
|
||||
uint8_t subkey[SEC_CMAC_KEY_LEN];
|
||||
uint16_t position;
|
||||
uint16_t len;
|
||||
wsfHandlerId_t handlerId;
|
||||
uint8_t state;
|
||||
} secCmacSecCb_t;
|
||||
|
||||
/*! Security queue element for CCM-Mode operations */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t operation;
|
||||
uint8_t *pText;
|
||||
uint8_t *pClear;
|
||||
uint8_t *pRcvMic;
|
||||
uint16_t textLen;
|
||||
uint16_t clearLen;
|
||||
uint8_t micLen;
|
||||
uint8_t key[SEC_CCM_KEY_LEN];
|
||||
uint8_t scratch[SEC_BLOCK_LEN];
|
||||
uint16_t counter;
|
||||
uint16_t position;
|
||||
wsfHandlerId_t handlerId;
|
||||
uint8_t state;
|
||||
uint8_t *pWorking;
|
||||
} secCcmSecCb_t;
|
||||
|
||||
/*! Security queue element */
|
||||
typedef struct
|
||||
{
|
||||
secMsg_t msg;
|
||||
uint8_t ciphertext[SEC_BLOCK_LEN];
|
||||
uint8_t reserved[SEC_BLOCK_LEN];
|
||||
void *pCb;
|
||||
uint8_t type;
|
||||
} secQueueBuf_t;
|
||||
|
||||
typedef void secHciCback_t(secQueueBuf_t *pBuf, hciEvt_t *pEvent, wsfHandlerId_t handlerId);
|
||||
typedef secHciCback_t *pSecHciCback_t;
|
||||
|
||||
/* Control block */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t rand[SEC_RAND_DATA_LEN]; /* Random data buffer */
|
||||
wsfQueue_t aesEncQueue; /* Queue for AES encrypt requests */
|
||||
wsfQueue_t pubKeyQueue; /* Queue for read p256 public key requests */
|
||||
wsfQueue_t dhKeyQueue; /* Queue for generate dh key requests */
|
||||
uint8_t token; /* Token value */
|
||||
uint8_t randTop; /* Random buffer insert point (HCI_RAND_LEN bytes) */
|
||||
uint8_t randBtm; /* Random buffer copy point (HCI_RAND_LEN bytes) */
|
||||
pSecHciCback_t hciCbackTbl[SEC_NUM_TYPES];
|
||||
} secCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Queue callback and call LE encrypt given most significant byte ordered key and data.
|
||||
*
|
||||
* \param pKey Pointer to key.
|
||||
* \param pText Pointer to text to encrypt.
|
||||
* \param pBuf Pointer to queue block.
|
||||
* \param handlerId Handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecLeEncryptCmd(uint8_t *pKey, uint8_t *pText, void *pBuf, wsfHandlerId_t handlerId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* SEC_MAIN_H */
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Counter with CBC-MAC (CCM) mode security - Native AES.
|
||||
*
|
||||
* Copyright (c) 2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "sec_main.h"
|
||||
|
||||
#ifndef SEC_CCM_CFG
|
||||
#define SEC_CCM_CFG SEC_CCM_CFG_PLATFORM
|
||||
#endif
|
||||
|
||||
#if SEC_CCM_CFG == SEC_CCM_CFG_PLATFORM
|
||||
|
||||
/**************************************************************************************************
|
||||
External Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Global security control block */
|
||||
extern secCb_t secCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the CCM-Mode encryption algorithm.
|
||||
*
|
||||
* \param pKey Pointer to encryption key (SEC_CCM_KEY_LEN bytes).
|
||||
* \param pNonce Pointer to nonce (SEC_CCM_NONCE_LEN bytes).
|
||||
* \param pPlainText Pointer to text to encrypt.
|
||||
* \param textLen Length of pPlainText in bytes.
|
||||
* \param pClear Pointer to additional, unencrypted authentication text.
|
||||
* \param clearLen Length of pClear in bytes.
|
||||
* \param micLen Size of MIC in bytes (4, 8 or 16).
|
||||
* \param pResult Buffer to hold result (returned in complete event).
|
||||
* \param handlerId Task handler ID to receive complete event.
|
||||
* \param param Optional parameter passed in complete event.
|
||||
* \param event Event ID of complete event.
|
||||
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecCcmEnc(const uint8_t *pKey, uint8_t *pNonce, uint8_t *pPlainText, uint16_t textLen,
|
||||
uint8_t *pClear, uint16_t clearLen, uint8_t micLen, uint8_t *pResult,
|
||||
wsfHandlerId_t handlerId, uint16_t param, uint8_t event)
|
||||
{
|
||||
secCcmEncMsg_t *pCcmMsg;
|
||||
|
||||
if ((pCcmMsg = WsfMsgAlloc(sizeof(secCcmEncMsg_t))) != NULL)
|
||||
{
|
||||
/* Encrypt. */
|
||||
PalCryptoCcmEnc(pKey, pNonce, pPlainText, textLen, pClear, clearLen, micLen, pResult,
|
||||
handlerId, param, event);
|
||||
|
||||
memcpy(pResult, pClear, clearLen);
|
||||
|
||||
/* Send notification of encryption complete. */
|
||||
pCcmMsg->hdr.status = secCb.token++;
|
||||
pCcmMsg->hdr.param = param;
|
||||
pCcmMsg->hdr.event = event;
|
||||
pCcmMsg->pCiphertext = pResult;
|
||||
pCcmMsg->textLen = textLen + clearLen + micLen;
|
||||
WsfMsgSend(handlerId, pCcmMsg);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn SecCcmDec
|
||||
*
|
||||
* \brief Execute the CCM-Mode verify and decrypt algorithm.
|
||||
*
|
||||
* \param pKey Pointer to encryption key (SEC_CCM_KEY_LEN bytes).
|
||||
* \param pNonce Pointer to nonce (SEC_CCM_NONCE_LEN bytes).
|
||||
* \param pCypherText Pointer to text to decrypt.
|
||||
* \param textLen Length of pCypherText in bytes.
|
||||
* \param pClear Pointer to additional, unencrypted authentication text.
|
||||
* \param clearLen Length of pClear in bytes.
|
||||
* \param pMic Pointer to authentication digest.
|
||||
* \param micLen Size of MIC in bytes (4, 8 or 16).
|
||||
* \param pResult Buffer to hold result (returned in complete event).
|
||||
* \param handlerId Task handler ID to receive complete event.
|
||||
* \param param Optional parameter passed in complete event.
|
||||
* \param event Event ID of complete event.
|
||||
*
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecCcmDec(const uint8_t *pKey, uint8_t *pNonce, uint8_t *pCypherText, uint16_t textLen,
|
||||
uint8_t *pClear, uint16_t clearLen, uint8_t *pMic, uint8_t micLen,
|
||||
uint8_t *pResult, wsfHandlerId_t handlerId, uint16_t param, uint8_t event)
|
||||
{
|
||||
secCcmDecMsg_t *pCcmMsg;
|
||||
|
||||
if ((pCcmMsg = WsfMsgAlloc(sizeof(secCcmDecMsg_t))) != NULL)
|
||||
{
|
||||
/* Decrypt. */
|
||||
uint32_t error;
|
||||
|
||||
error = PalCryptoCcmDec(pKey, pNonce, pCypherText, textLen, pClear, clearLen, pMic, micLen,
|
||||
pResult, handlerId, param, event);
|
||||
|
||||
/* Send notification of decryption complete. */
|
||||
pCcmMsg->hdr.status = secCb.token++;
|
||||
pCcmMsg->hdr.param = param;
|
||||
pCcmMsg->hdr.event = event;
|
||||
|
||||
/* Compare MIC with computed MIC. */
|
||||
if (error)
|
||||
{
|
||||
/* MIC not authentic. */
|
||||
pCcmMsg->success = FALSE;
|
||||
pCcmMsg->pResult = NULL;
|
||||
pCcmMsg->pText = NULL;
|
||||
pCcmMsg->textLen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* MIC authentic. */
|
||||
pCcmMsg->success = TRUE;
|
||||
pCcmMsg->pResult = pResult;
|
||||
pCcmMsg->pText = pResult;
|
||||
pCcmMsg->textLen = textLen;
|
||||
}
|
||||
|
||||
WsfMsgSend(handlerId, pCcmMsg);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to initialize CCM-Mode security.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecCcmInit()
|
||||
{
|
||||
PalCryptoInit();
|
||||
|
||||
secCb.hciCbackTbl[SEC_TYPE_CCM] = NULL;
|
||||
}
|
||||
|
||||
#endif /* SEC_CCM_CFG */
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Security ECC implementation using uECC.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "sec_api.h"
|
||||
#include "sec_main.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "hci_api.h"
|
||||
#include "util/calc128.h"
|
||||
#include "uECC.h"
|
||||
|
||||
#ifndef SEC_ECC_CFG
|
||||
#define SEC_ECC_CFG SEC_ECC_CFG_UECC
|
||||
#endif
|
||||
|
||||
#if SEC_ECC_CFG == SEC_ECC_CFG_UECC
|
||||
|
||||
/**************************************************************************************************
|
||||
External Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
extern secCb_t secCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Random number generator used by uECC.
|
||||
*
|
||||
* \param p_dest Buffer to hold random number
|
||||
* \param p_size Size of p_dest in bytes .
|
||||
*
|
||||
* \return TRUE if successful.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static int secEccRng(uint8_t *p_dest, unsigned p_size)
|
||||
{
|
||||
SecRand(p_dest, p_size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Callback for HCI encryption for ECC operations.
|
||||
*
|
||||
* \param pBuf Pointer to sec queue element.
|
||||
* \param pEvent Pointer to HCI event.
|
||||
* \param handlerId WSF handler ID.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecEccHciCback(secQueueBuf_t *pBuf, hciEvt_t *pEvent, wsfHandlerId_t handlerId)
|
||||
{
|
||||
/* TBD */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate an ECC key.
|
||||
*
|
||||
* \param handlerId WSF handler ID for client.
|
||||
* \param param Optional parameter sent to client's WSF handler.
|
||||
* \param event Event for client's WSF handler.
|
||||
*
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecEccGenKey(wsfHandlerId_t handlerId, uint16_t param, uint8_t event)
|
||||
{
|
||||
secEccMsg_t *pMsg = WsfMsgAlloc(sizeof(secEccMsg_t));
|
||||
|
||||
if (pMsg)
|
||||
{
|
||||
/* Generate the keys */
|
||||
uECC_make_key(pMsg->data.key.pubKey_x, pMsg->data.key.privKey);
|
||||
|
||||
/* Send shared secret to handler */
|
||||
pMsg->hdr.event = event;
|
||||
pMsg->hdr.param = param;
|
||||
pMsg->hdr.status = HCI_SUCCESS;
|
||||
WsfMsgSend(handlerId, pMsg);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate an ECC key.
|
||||
*
|
||||
* \param pKey ECC Key structure.
|
||||
* \param handlerId WSF handler ID for client.
|
||||
* \param param Optional parameter sent to client's WSF handler.
|
||||
* \param event Event for client's WSF handler.
|
||||
*
|
||||
* \return TRUE if successful, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SecEccGenSharedSecret(secEccKey_t *pKey, wsfHandlerId_t handlerId, uint16_t param, uint8_t event)
|
||||
{
|
||||
secEccMsg_t *pMsg = WsfMsgAlloc(sizeof(secEccMsg_t));
|
||||
|
||||
if (pMsg)
|
||||
{
|
||||
bool_t keyValid = uECC_valid_public_key(pKey->pubKey_x);
|
||||
|
||||
if (keyValid)
|
||||
{
|
||||
uECC_shared_secret(pKey->pubKey_x, pKey->privKey, pMsg->data.sharedSecret.secret);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(pMsg->data.sharedSecret.secret, 0xFF, SEC_ECC_KEY_LEN);
|
||||
}
|
||||
|
||||
/* Send shared secret to handler. */
|
||||
pMsg->hdr.event = event;
|
||||
pMsg->hdr.param = param;
|
||||
pMsg->hdr.status = keyValid ? HCI_SUCCESS : HCI_ERR_INVALID_PARAM;
|
||||
WsfMsgSend(handlerId, pMsg);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to initialize ECC security.
|
||||
*
|
||||
* \param None.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SecEccInit()
|
||||
{
|
||||
//srand((unsigned int)time(NULL));
|
||||
uECC_set_rng(secEccRng);
|
||||
}
|
||||
|
||||
#endif /* SEC_ECC_CFG */
|
||||
+552
@@ -0,0 +1,552 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* indexes into base UUID for 16-to-128 bit UUID conversion */
|
||||
#define ATT_BASE_UUID_POS_0 12
|
||||
#define ATT_BASE_UUID_POS_1 13
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
static uint8_t attBaseUuid[] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
|
||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Default component function inteface */
|
||||
const attFcnIf_t attFcnDefault =
|
||||
{
|
||||
attEmptyDataCback,
|
||||
(l2cCtrlCback_t) attEmptyHandler,
|
||||
(attMsgHandler_t) attEmptyHandler,
|
||||
attEmptyConnCback
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
attCb_t attCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C data callback for ATT.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attL2cDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t pduType;
|
||||
|
||||
/* parse PDU type */
|
||||
pduType = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* if from server */
|
||||
if ((pduType & ATT_PDU_MASK_SERVER) != 0)
|
||||
{
|
||||
/* call client data callback */
|
||||
(*attCb.pClient->dataCback)(handle, len, pPacket);
|
||||
}
|
||||
/* else from client */
|
||||
else
|
||||
{
|
||||
/* call server data callback */
|
||||
(*attCb.pServer->dataCback)(handle, len, pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C control callback for ATT.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attL2cCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
attCcb_t *pCcb;
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = attCcbByConnId((dmConnId_t) pMsg->param);
|
||||
|
||||
/* verify connection is open */
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
if (pMsg->event == L2C_CTRL_FLOW_DISABLE_IND)
|
||||
{
|
||||
/* flow disabled */
|
||||
pCcb->control |= ATT_CCB_STATUS_FLOW_DISABLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* flow enabled */
|
||||
pCcb->control &= ~ATT_CCB_STATUS_FLOW_DISABLED;
|
||||
|
||||
/* call server control callback */
|
||||
(*attCb.pServer->ctrlCback)(pMsg);
|
||||
|
||||
/* check flow again; could be changed recursively */
|
||||
if (!(pCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
/* call client control callback */
|
||||
(*attCb.pClient->ctrlCback)(pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM connection callback for ATT.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attDmConnCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
attCcb_t *pCcb;
|
||||
|
||||
pCcb = attCcbByConnId((dmConnId_t) pDmEvt->hdr.param);
|
||||
|
||||
/* if new connection created */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
/* initialize control block before handling event */
|
||||
pCcb->handle = pDmEvt->connOpen.handle;
|
||||
pCcb->mtu = ATT_DEFAULT_MTU;
|
||||
pCcb->connId = (dmConnId_t) pDmEvt->hdr.param;
|
||||
pCcb->control = 0;
|
||||
pCcb->pPendDbHashRsp = NULL;
|
||||
}
|
||||
|
||||
/* if connection has been opened */
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* pass event to server */
|
||||
(*attCb.pServer->connCback)(pCcb, pDmEvt);
|
||||
|
||||
/* pass event to client */
|
||||
(*attCb.pClient->connCback)(pCcb, pDmEvt);
|
||||
|
||||
/* if connection closed */
|
||||
if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* clear control block after handling event */
|
||||
pCcb->connId = DM_CONN_ID_NONE;
|
||||
|
||||
if (pCcb->pPendDbHashRsp)
|
||||
{
|
||||
WsfBufFree(pCcb->pPendDbHashRsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* execute ATT connection callback */
|
||||
if (attCb.connCback != NULL)
|
||||
{
|
||||
(*attCb.connCback)(pDmEvt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief ATT empty event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attEmptyHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Empty connection callback for ATT.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attEmptyConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Empty data callback for ATT.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attEmptyDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the given handle.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attCcb_t *attCcbByHandle(uint16_t handle)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) != DM_CONN_ID_NONE)
|
||||
{
|
||||
return &attCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attCcb_t *attCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return &attCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare a 16 bit UUID to a 128 bit UUID.
|
||||
*
|
||||
* \param pUuid16 Pointer to 16 bit UUID.
|
||||
* \param pUuid128 Pointer to 128 bit UUID.
|
||||
*
|
||||
* \return TRUE of UUIDs match, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t attUuidCmp16to128(const uint8_t *pUuid16, const uint8_t *pUuid128)
|
||||
{
|
||||
attBaseUuid[ATT_BASE_UUID_POS_0] = pUuid16[0];
|
||||
attBaseUuid[ATT_BASE_UUID_POS_1] = pUuid16[1];
|
||||
|
||||
return (memcmp(attBaseUuid, pUuid128, ATT_128_UUID_LEN) == 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the attribute protocol MTU of a connection.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param peerMtu Peer MTU.
|
||||
* \param localMtu Local MTU.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attSetMtu(attCcb_t *pCcb, uint16_t peerMtu, uint16_t localMtu)
|
||||
{
|
||||
uint16_t mtu;
|
||||
|
||||
/* set negotiated mtu for the connection to the lesser of ours and theirs */
|
||||
mtu = WSF_MIN(peerMtu, localMtu);
|
||||
|
||||
/* if current mtu is not the same as the negotiated value */
|
||||
if (pCcb->mtu != mtu)
|
||||
{
|
||||
/* set mtu to the new value */
|
||||
pCcb->mtu = mtu;
|
||||
|
||||
/* notify app about the new value */
|
||||
attExecCallback(pCcb->connId, ATT_MTU_UPDATE_IND, 0, ATT_SUCCESS, mtu);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute application callback function.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param event Callback event ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Callback event status.
|
||||
* \param mtu Negotiated MTU value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status, uint16_t mtu)
|
||||
{
|
||||
if (attCb.cback)
|
||||
{
|
||||
attEvt_t evt;
|
||||
|
||||
evt.hdr.param = connId;
|
||||
evt.hdr.event = event;
|
||||
evt.hdr.status = status;
|
||||
evt.valueLen = 0;
|
||||
evt.handle = handle;
|
||||
evt.continuing = 0;
|
||||
evt.mtu = mtu;
|
||||
|
||||
(*attCb.cback)(&evt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate an ATT data message buffer to be used for the ATT attribute protocol messages.
|
||||
*
|
||||
* \param len Message length in bytes.
|
||||
*
|
||||
* \return Pointer to data message buffer or NULL if allocation failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *attMsgAlloc(uint16_t len)
|
||||
{
|
||||
return WsfMsgDataAlloc(len, HCI_TX_DATA_TAILROOM);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief ATT handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for ATT.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
/* store handler ID */
|
||||
attCb.handlerId = handlerId;
|
||||
|
||||
/* initialize control block */
|
||||
attCb.pClient = &attFcnDefault;
|
||||
attCb.pServer = &attFcnDefault;
|
||||
|
||||
/* Register with L2C */
|
||||
L2cRegister(L2C_CID_ATT, attL2cDataCback, attL2cCtrlCback);
|
||||
|
||||
/* Register with DM */
|
||||
DmConnRegister(DM_CLIENT_ID_ATT, attDmConnCback);
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for ATT.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* Handle message */
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
if (pMsg->event >= ATTS_MSG_START)
|
||||
{
|
||||
/* pass event to server */
|
||||
(*attCb.pServer->msgCback)(pMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* pass event to client */
|
||||
(*attCb.pClient->msgCback)(pMsg);
|
||||
}
|
||||
}
|
||||
/* Handle events */
|
||||
else if (event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback with ATT.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttRegister(attCback_t cback)
|
||||
{
|
||||
attCb.cback = cback;
|
||||
|
||||
/* if configured MTU size is larger than maximum RX PDU length */
|
||||
if (pAttCfg->mtu > (HciGetMaxRxAclLen() - L2C_HDR_LEN))
|
||||
{
|
||||
/* notify app about MTU misconfiguration */
|
||||
attExecCallback(0, DM_ERROR_IND, 0, DM_ERR_ATT_RX_PDU_LEN_EXCEEDED, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a connection callback with ATT. The callback is typically used to
|
||||
* manage the attribute server database.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttConnRegister(dmCback_t cback)
|
||||
{
|
||||
attCb.connCback = cback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the attribute protocol MTU of a connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return MTU of the connection.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t AttGetMtu(dmConnId_t connId)
|
||||
{
|
||||
return (attCcbByConnId(connId)->mtu);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate an ATT message buffer to be sent with the ATT attribute protocol
|
||||
* zero-copy APIs.
|
||||
*
|
||||
* \param len Message length in bytes.
|
||||
* \param opcode Opcode for ATT message.
|
||||
*
|
||||
* \return Pointer to message buffer or NULL if allocation failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *AttMsgAlloc(uint16_t len, uint8_t opcode)
|
||||
{
|
||||
uint8_t *pMsg;
|
||||
uint8_t hdrLen;
|
||||
|
||||
WSF_ASSERT((opcode == ATT_PDU_VALUE_IND) || (opcode == ATT_PDU_VALUE_NTF));
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case ATT_PDU_VALUE_IND:
|
||||
case ATT_PDU_VALUE_NTF:
|
||||
hdrLen = ATT_VALUE_IND_NTF_BUF_LEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
hdrLen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdrLen > 0)
|
||||
{
|
||||
pMsg = attMsgAlloc(hdrLen + len);
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
/* return pointer to attribute value buffer */
|
||||
return (pMsg + hdrLen);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free an ATT message buffer allocated with AttMsgAlloc().
|
||||
*
|
||||
* \param pMsg Pointer to message buffer.
|
||||
* \param opcode Opcode for ATT message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttMsgFree(void *pMsg, uint8_t opcode)
|
||||
{
|
||||
uint8_t hdrLen;
|
||||
|
||||
WSF_ASSERT((opcode == ATT_PDU_VALUE_IND) || (opcode == ATT_PDU_VALUE_NTF));
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case ATT_PDU_VALUE_IND:
|
||||
case ATT_PDU_VALUE_NTF:
|
||||
hdrLen = ATT_VALUE_IND_NTF_BUF_LEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
hdrLen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
WsfMsgFree(((uint8_t *)pMsg) - hdrLen);
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATT_MAIN_H
|
||||
#define ATT_MAIN_H
|
||||
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "l2c_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATT protocol methods */
|
||||
#define ATT_METHOD_ERR 0 /* Error response */
|
||||
#define ATT_METHOD_MTU 1 /* Exchange mtu */
|
||||
#define ATT_METHOD_FIND_INFO 2 /* Find information */
|
||||
#define ATT_METHOD_FIND_TYPE 3 /* Find by type value */
|
||||
#define ATT_METHOD_READ_TYPE 4 /* Read by type value */
|
||||
#define ATT_METHOD_READ 5 /* Read */
|
||||
#define ATT_METHOD_READ_BLOB 6 /* Read long */
|
||||
#define ATT_METHOD_READ_MULTIPLE 7 /* Read multiple */
|
||||
#define ATT_METHOD_READ_GROUP_TYPE 8 /* Read group type */
|
||||
#define ATT_METHOD_WRITE 9 /* Write */
|
||||
#define ATT_METHOD_WRITE_CMD 10 /* Write command */
|
||||
#define ATT_METHOD_PREPARE_WRITE 11 /* Prepare write */
|
||||
#define ATT_METHOD_EXECUTE_WRITE 12 /* Execute write */
|
||||
#define ATT_METHOD_VALUE_NTF 13 /* Handle value notification */
|
||||
#define ATT_METHOD_VALUE_IND 14 /* Handle value indication */
|
||||
#define ATT_METHOD_VALUE_CNF 15 /* Handle value confirm */
|
||||
#define ATT_METHOD_SIGNED_WRITE_CMD 16 /* Signed write command */
|
||||
|
||||
/* Convert opcode to method */
|
||||
#define ATT_OPCODE_2_METHOD(op) (((op) & ~ATT_PDU_MASK_SERVER) / 2)
|
||||
|
||||
/* Client and server message macros */
|
||||
#define ATTC_MSG_START 0x00
|
||||
#define ATTS_MSG_START 0x20
|
||||
#define ATT_MSG_MASK(msg) ((msg) & 0x1F)
|
||||
|
||||
/* Buffer lengths for messages */
|
||||
#define ATT_VALUE_IND_NTF_BUF_LEN (ATT_VALUE_NTF_LEN + L2C_PAYLOAD_START)
|
||||
|
||||
/* attCcb_t control bits */
|
||||
#define ATT_CCB_STATUS_MTU_SENT (1<<0) /* MTU req or rsp sent */
|
||||
#define ATT_CCB_STATUS_FLOW_DISABLED (1<<1) /* Data flow disabled */
|
||||
#define ATT_CCB_STATUS_TX_TIMEOUT (1<<2) /* ATT transaction timed out */
|
||||
#define ATT_CCB_STATUS_RSP_PENDING (1<<3) /* ATTS write rsp pending */
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t startHandle; /* Start handle of the requested operation. Used if an Error Response is sent. */
|
||||
uint16_t handle; /* Attribute handle of pending response. */
|
||||
} attPendDbHashRsp_t;
|
||||
|
||||
/* Connection control block */
|
||||
typedef struct
|
||||
{
|
||||
wsfQueue_t prepWriteQueue; /* prepare write queue */
|
||||
wsfTimer_t idleTimer; /* service discovery idle timer */
|
||||
uint16_t handle; /* connection handle */
|
||||
uint16_t mtu; /* connection mtu */
|
||||
dmConnId_t connId; /* DM connection ID */
|
||||
uint8_t control; /* Control bitfield */
|
||||
attPendDbHashRsp_t *pPendDbHashRsp; /* Pending ATT Response information. */
|
||||
} attCcb_t;
|
||||
|
||||
/* ATT message handling function type */
|
||||
typedef void (*attMsgHandler_t)(void *pMsg);
|
||||
|
||||
/* ATT connection callback type */
|
||||
typedef void (*attConnCback_t)(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
|
||||
/* Callback interface for client and server */
|
||||
typedef struct
|
||||
{
|
||||
l2cDataCback_t dataCback; /* Data callback */
|
||||
l2cCtrlCback_t ctrlCback; /* Control callback */
|
||||
attMsgHandler_t msgCback; /* Message handling callback */
|
||||
attConnCback_t connCback; /* Connection callback */
|
||||
} attFcnIf_t;
|
||||
|
||||
/* Main control block of the ATT subsystem */
|
||||
typedef struct
|
||||
{
|
||||
attCcb_t ccb[DM_CONN_MAX]; /* Connection control blocks */
|
||||
attFcnIf_t const *pClient; /* Client callback interface */
|
||||
attFcnIf_t const *pServer; /* Server callback interface */
|
||||
attCback_t cback; /* ATT callback function */
|
||||
dmCback_t connCback; /* ATT connection callback function */
|
||||
wsfHandlerId_t handlerId; /* WSF handler ID */
|
||||
uint8_t errTest; /* Status code for error testing */
|
||||
} attCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Default component function inteface */
|
||||
extern const attFcnIf_t attFcnDefault;
|
||||
|
||||
/* Control block */
|
||||
extern attCb_t attCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void attEmptyHandler(wsfMsgHdr_t *pMsg);
|
||||
void attEmptyDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
void attEmptyConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
|
||||
attCcb_t *attCcbByHandle(uint16_t handle);
|
||||
attCcb_t *attCcbByConnId(dmConnId_t connId);
|
||||
|
||||
bool_t attUuidCmp16to128(const uint8_t *pUuid16, const uint8_t *pUuid128);
|
||||
void attSetMtu(attCcb_t *pCcb, uint16_t peerMtu, uint16_t localMtu);
|
||||
void attExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status, uint16_t mtu);
|
||||
void *attMsgAlloc(uint16_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATT_MAIN_H */
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT optional signed PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATT_SIGN_H
|
||||
#define ATT_SIGN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* CMAC algorithm subkey length */
|
||||
#define ATT_CMAC_SUBKEY_LEN 16
|
||||
|
||||
/* CMAC algorithm block length */
|
||||
#define ATT_CMAC_BLOCK_LEN 16
|
||||
|
||||
/* CMAC constant Rb */
|
||||
#define ATT_CMAC_RB 0x87
|
||||
|
||||
/* CMAC signature result length */
|
||||
#define ATT_CMAC_RESULT_LEN 8
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATT_SIGN_H */
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT UUID constants.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_uuid.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Service UUIDs */
|
||||
const uint8_t attGapSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GAP_SERVICE)};
|
||||
const uint8_t attGattSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GATT_SERVICE)};
|
||||
const uint8_t attIasSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_IMMEDIATE_ALERT_SERVICE)};
|
||||
const uint8_t attLlsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LINK_LOSS_SERVICE)};
|
||||
const uint8_t attTpsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TX_POWER_SERVICE)};
|
||||
const uint8_t attCtsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CURRENT_TIME_SERVICE)};
|
||||
const uint8_t attRtusSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_REF_TIME_UPDATE_SERVICE)};
|
||||
const uint8_t attNdcsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DST_CHANGE_SERVICE)};
|
||||
const uint8_t attGlsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_SERVICE)};
|
||||
const uint8_t attHtsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HEALTH_THERM_SERVICE)};
|
||||
const uint8_t attDisSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)};
|
||||
const uint8_t attNwaSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_NETWORK_AVAIL_SERVICE)};
|
||||
const uint8_t attWdsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WATCHDOG_SERVICE)};
|
||||
const uint8_t attHrsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HEART_RATE_SERVICE)};
|
||||
const uint8_t attPassSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PHONE_ALERT_SERVICE)};
|
||||
const uint8_t attBasSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE)};
|
||||
const uint8_t attBpsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BLOOD_PRESSURE_SERVICE)};
|
||||
const uint8_t attAnsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_NOTIF_SERVICE)};
|
||||
const uint8_t attHidSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_SERVICE)};
|
||||
const uint8_t attSpsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SCAN_PARAM_SERVICE)};
|
||||
const uint8_t attPlxsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OXIMITER_SERVICE)};
|
||||
const uint8_t attUdsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_USER_DATA_SERVICE)};
|
||||
const uint8_t attMprvSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PRV_SERVICE)};
|
||||
const uint8_t attMprxSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PROXY_SERVICE)};
|
||||
|
||||
/*! GATT UUIDs */
|
||||
const uint8_t attPrimSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE)};
|
||||
const uint8_t attSecSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SECONDARY_SERVICE)};
|
||||
const uint8_t attIncUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_INCLUDE)};
|
||||
const uint8_t attChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHARACTERISTIC)};
|
||||
|
||||
/*! Descriptor UUIDs */
|
||||
const uint8_t attChExtUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHARACTERISTIC_EXT)};
|
||||
const uint8_t attChUserDescUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHAR_USER_DESC)};
|
||||
const uint8_t attCliChCfgUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CLIENT_CHAR_CONFIG)};
|
||||
const uint8_t attSrvChCfgUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERVER_CHAR_CONFIG)};
|
||||
const uint8_t attChPresFmtUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHAR_PRES_FORMAT)};
|
||||
const uint8_t attAggFmtUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_AGGREGATE_FORMAT)};
|
||||
const uint8_t attValRangeUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_VALID_RANGE)};
|
||||
const uint8_t attHidErmUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_EXT_REPORT_MAPPING)};
|
||||
const uint8_t attHidRimUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_REPORT_ID_MAPPING)};
|
||||
|
||||
/*! Characteristic UUIDs */
|
||||
const uint8_t attDnChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DEVICE_NAME)};
|
||||
const uint8_t attApChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_APPEARANCE)};
|
||||
const uint8_t attPpfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PERIPH_PRIVACY_FLAG)};
|
||||
const uint8_t attRaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RECONN_ADDR)};
|
||||
const uint8_t attPpcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PREF_CONN_PARAM)};
|
||||
const uint8_t attScChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERVICE_CHANGED)};
|
||||
const uint8_t attAlChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_LEVEL)};
|
||||
const uint8_t attTxpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TX_POWER_LEVEL)};
|
||||
const uint8_t attDtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DATE_TIME)};
|
||||
const uint8_t attDwChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DAY_OF_WEEK)};
|
||||
const uint8_t attDdtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DAY_DATE_TIME)};
|
||||
const uint8_t attEt100ChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_EXACT_TIME_100)};
|
||||
const uint8_t attEt256ChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_EXACT_TIME_256)};
|
||||
const uint8_t attDstoChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DST_OFFSET)};
|
||||
const uint8_t attTzChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_ZONE)};
|
||||
const uint8_t attLtiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LOCAL_TIME_INFO)};
|
||||
const uint8_t attStzChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SECONDARY_TIME_ZONE)};
|
||||
const uint8_t attTdstChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_WITH_DST)};
|
||||
const uint8_t attTaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_ACCURACY)};
|
||||
const uint8_t attTsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_SOURCE)};
|
||||
const uint8_t attRtiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_REFERENCE_TIME_INFO)};
|
||||
const uint8_t attTbChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_BROADCAST)};
|
||||
const uint8_t attTucpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_UPDATE_CP)};
|
||||
const uint8_t attTusChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_UPDATE_STATE)};
|
||||
const uint8_t attGlmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_MEAS)};
|
||||
const uint8_t attBlChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_LEVEL)};
|
||||
const uint8_t attBpsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_POWER_STATE)};
|
||||
const uint8_t attBlsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_LEVEL_STATE)};
|
||||
const uint8_t attTmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_MEAS)};
|
||||
const uint8_t attTtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_TYPE)};
|
||||
const uint8_t attItChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_INTERMEDIATE_TEMP)};
|
||||
const uint8_t attTcelChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_C)};
|
||||
const uint8_t attTfahChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_F)};
|
||||
const uint8_t attSidChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SYSTEM_ID)};
|
||||
const uint8_t attMnsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MODEL_NUMBER)};
|
||||
const uint8_t attSnsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERIAL_NUMBER)};
|
||||
const uint8_t attFrsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_FIRMWARE_REV)};
|
||||
const uint8_t attHrsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HARDWARE_REV)};
|
||||
const uint8_t attSrsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SOFTWARE_REV)};
|
||||
const uint8_t attMfnsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MANUFACTURER_NAME)};
|
||||
const uint8_t attIeeeChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_11073_CERT_DATA)};
|
||||
const uint8_t attCtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CURRENT_TIME)};
|
||||
const uint8_t attElChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ELEVATION)};
|
||||
const uint8_t attLatChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LATITUDE)};
|
||||
const uint8_t attLongChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LONGITUDE)};
|
||||
const uint8_t attP2dChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_POSITION_2D)};
|
||||
const uint8_t attP3dChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_POSITION_3D)};
|
||||
const uint8_t attVidChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_VENDOR_ID)};
|
||||
const uint8_t attHbmiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_BOOT_MOUSE_IN)};
|
||||
const uint8_t attGlmcChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_MEAS_CONTEXT)};
|
||||
const uint8_t attBpmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BP_MEAS)};
|
||||
const uint8_t attIcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_INTERMEDIATE_BP)};
|
||||
const uint8_t attHrmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HR_MEAS)};
|
||||
const uint8_t attBslChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HR_SENSOR_LOC)};
|
||||
const uint8_t attHrcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HR_CP)};
|
||||
const uint8_t attRemChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_REMOVABLE)};
|
||||
const uint8_t attSrChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERVICE_REQ)};
|
||||
const uint8_t attStcChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SCI_TEMP_C)};
|
||||
const uint8_t attStrChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_STRING)};
|
||||
const uint8_t attNwaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_NETWORK_AVAIL)};
|
||||
const uint8_t attAsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_STATUS)};
|
||||
const uint8_t attRcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RINGER_CP)};
|
||||
const uint8_t attRsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RINGER_SETTING)};
|
||||
const uint8_t attAcbmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_CAT_ID_MASK)};
|
||||
const uint8_t attAcChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_CAT_ID)};
|
||||
const uint8_t attAncpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_NOTIF_CP)};
|
||||
const uint8_t attUasChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_UNREAD_ALERT_STATUS)};
|
||||
const uint8_t attNaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_NEW_ALERT)};
|
||||
const uint8_t attSnacChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SUP_NEW_ALERT_CAT)};
|
||||
const uint8_t attSuacChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SUP_UNREAD_ALERT_CAT)};
|
||||
const uint8_t attBpfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BP_FEATURE)};
|
||||
const uint8_t attHidBmiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MEAS_INTERVAL)};
|
||||
const uint8_t attHidBkiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_BOOT_KEYBOARD_IN)};
|
||||
const uint8_t attHidBkoChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_BOOT_KEYBOARD_OUT)};
|
||||
const uint8_t attHidiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_INFORMATION)};
|
||||
const uint8_t attHidRmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_REPORT_MAP)};
|
||||
const uint8_t attHidcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_CONTROL_POINT)};
|
||||
const uint8_t attHidRepChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_REPORT)};
|
||||
const uint8_t attHidPmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_PROTOCOL_MODE)};
|
||||
const uint8_t attSiwChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SCAN_INT_WIND)};
|
||||
const uint8_t attPnpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PNP_ID)};
|
||||
const uint8_t attGlfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_FEATURE)};
|
||||
const uint8_t attRacpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RACP)};
|
||||
const uint8_t attCarChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CAR)};
|
||||
const uint8_t attRsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RUNNING_SPEED_FEATURE)};
|
||||
const uint8_t attRsmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RUNNING_SPEED_MEASUREMENT)};
|
||||
const uint8_t attCpfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_POWER_FEATURE)};
|
||||
const uint8_t attCpmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_POWER_MEASUREMENT)};
|
||||
const uint8_t attCsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_SPEED_FEATURE)};
|
||||
const uint8_t attCsmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_SPEED_MEASUREMENT)};
|
||||
const uint8_t attSlChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SENSOR_LOCATION)};
|
||||
const uint8_t attPlxfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OX_FEATURES)};
|
||||
const uint8_t attPlxscmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OX_SPOT_CHECK)};
|
||||
const uint8_t attPlxcmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OX_CONTINUOUS)};
|
||||
const uint8_t attRpaoChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RPAO)};
|
||||
const uint8_t attDbciChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DB_CHANGE_INCREMENT)};
|
||||
const uint8_t attUiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_USER_INDEX)};
|
||||
const uint8_t attUcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_USER_CONTROL_POINT)};
|
||||
const uint8_t attMprvDinChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PRV_DATA_IN)};
|
||||
const uint8_t attMprvDoutChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PRV_DATA_OUT)};
|
||||
const uint8_t attMprxDinChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PROXY_DATA_IN)};
|
||||
const uint8_t attMprxDoutChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PROXY_DATA_OUT)};
|
||||
const uint8_t attWssSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WEIGHT_SCALE_SERVICE)};
|
||||
const uint8_t attWmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WEIGHT_MEAS)};
|
||||
const uint8_t attWsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WEIGHT_SCALE_FEATURE)};
|
||||
const uint8_t attGattCsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CLIENT_SUPPORTED_FEATURES)};
|
||||
const uint8_t attGattDbhChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DATABASE_HASH)};
|
||||
const uint8_t attCteSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CONSTANT_TONE_SERVICE)};
|
||||
const uint8_t attCteEnChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_ENABLE)};
|
||||
const uint8_t attCteMinLenChUuid[ATT_16_UUID_LEN] ={UINT16_TO_BYTES(ATT_UUID_CTE_MIN_LEN)};
|
||||
const uint8_t attCteTxCntChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_TX_CNT)};
|
||||
const uint8_t attCteTxDurChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_TX_DURATION)};
|
||||
const uint8_t attCteIntChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_INTERVAL)};
|
||||
const uint8_t attCtePhyChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_PHY)};
|
||||
Vendored
+706
@@ -0,0 +1,706 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client service and characteristic utility functions.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_uuid.h"
|
||||
#include "att_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
#define ATT_DISC_HDL_IDX_NONE 0xFF
|
||||
|
||||
#define ATT_SET_UUID_MASK ATTC_SET_UUID_128
|
||||
|
||||
/* Characteristic declaration lengths */
|
||||
#define ATT_CHAR_DECL_LEN_UUID16 5
|
||||
#define ATT_CHAR_DECL_LEN_UUID128 19
|
||||
|
||||
/* Read by type response lengths for characteristic discovery */
|
||||
#define ATT_READ_RSP_LEN_UUID16 (ATT_CHAR_DECL_LEN_UUID16 + 2)
|
||||
#define ATT_READ_RSP_LEN_UUID128 (ATT_CHAR_DECL_LEN_UUID128 + 2)
|
||||
|
||||
/* Find info response lengths */
|
||||
#define ATT_FIND_RSP_LEN_UUID16 (ATT_16_UUID_LEN + 2)
|
||||
#define ATT_FIND_RSP_LEN_UUID128 (ATT_128_UUID_LEN + 2)
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare two UUIDs.
|
||||
*
|
||||
* \param pChar Characteristic that we are looking for.
|
||||
* \param pUuid Pointer to peer UUID data.
|
||||
* \param settings Indicates 16 or 128 bit UUID.
|
||||
*
|
||||
* \return TRUE if UUIDs match.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t attcUuidCmp(attcDiscChar_t *pChar, uint8_t *pUuid, uint8_t settings)
|
||||
{
|
||||
/* if both uuids are the same length */
|
||||
if ((pChar->settings & ATT_SET_UUID_MASK) == settings)
|
||||
{
|
||||
/* simply compare the data */
|
||||
return (memcmp(pChar->pUuid, pUuid, (settings == 0) ? ATT_16_UUID_LEN : ATT_128_UUID_LEN) == 0);
|
||||
}
|
||||
/* if discovered UUID is 128 bit and our UUID is 16 bit */
|
||||
else if ((settings == ATTC_SET_UUID_128) && ((pChar->settings & ATTC_SET_UUID_128) == 0))
|
||||
{
|
||||
/* convert our UUID to 128 bit and compare */
|
||||
return attUuidCmp16to128(pChar->pUuid, pUuid);
|
||||
}
|
||||
/* else discovered UUID is 16 bit and our UUID is 128 bit */
|
||||
else
|
||||
{
|
||||
/* no match */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Verify that required characterstics and descriptors have been discovered.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscVerify(attcDiscCb_t *pCb)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint8_t i;
|
||||
|
||||
/* for each characteristic */
|
||||
for (i = 0, pChar = pCb->pCharList; i < pCb->charListLen; i++, pChar++)
|
||||
{
|
||||
/* if characteristic required */
|
||||
if (((*pChar)->settings & ATTC_SET_REQUIRED) != 0)
|
||||
{
|
||||
/* verify handle was discovered */
|
||||
if (pCb->pHdlList[i] == ATT_HANDLE_NONE)
|
||||
{
|
||||
return ATT_ERR_REQ_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find next characteristic that requires descriptor discovery. If none found,
|
||||
* discovery is complete; verify that required characterstics have been discovered.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscDescriptors(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint16_t startHdl = ATT_HANDLE_NONE;
|
||||
uint16_t endHdl = ATT_HANDLE_NONE;
|
||||
|
||||
/* find next descriptor in list */
|
||||
pChar = pCb->pCharList + pCb->charListIdx;
|
||||
while (pCb->charListIdx < pCb->charListLen)
|
||||
{
|
||||
/* if this is a descriptor */
|
||||
if (((*pChar)->settings & ATTC_SET_DESCRIPTOR) != 0)
|
||||
{
|
||||
/* start handle is one greater than characteristic value handle,
|
||||
* which is stored in the previous entry in the list;
|
||||
* end handle is stored at current entry in the list
|
||||
*/
|
||||
startHdl = pCb->pHdlList[pCb->charListIdx - 1] + 1;
|
||||
endHdl = pCb->pHdlList[pCb->charListIdx];
|
||||
|
||||
/* clear temp end handle */
|
||||
pCb->pHdlList[pCb->charListIdx] = ATT_HANDLE_NONE;
|
||||
|
||||
/* if there are descriptors */
|
||||
if (startHdl <= endHdl)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we are looking for descriptors for this characteristic but
|
||||
* there aren't any;
|
||||
* skip over any other descriptors that follow in our list
|
||||
*/
|
||||
while (++pCb->charListIdx < pCb->charListLen)
|
||||
{
|
||||
pChar++;
|
||||
if ((*pChar)->settings & ATTC_SET_DESCRIPTOR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* go to next in list */
|
||||
pChar++;
|
||||
pCb->charListIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no more descriptors to be discovered */
|
||||
if (pCb->charListIdx == pCb->charListLen)
|
||||
{
|
||||
/* we're done; verify required characteristics and descriptors were discovered */
|
||||
return attcDiscVerify(pCb);
|
||||
}
|
||||
/* else initiate characteristic descriptor discovery */
|
||||
else
|
||||
{
|
||||
AttcFindInfoReq(connId, startHdl, endHdl, TRUE);
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a descriptor handle/UUID pair.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param settings Indicates 16 or 128 bit UUID.
|
||||
* \param pPair Pointer to handle/UUID pair.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcDiscProcDescPair(attcDiscCb_t *pCb, uint8_t settings, uint8_t *pPair)
|
||||
{
|
||||
attcDiscChar_t **pDesc;
|
||||
uint16_t hdl;
|
||||
uint8_t i;
|
||||
|
||||
/* parse handle */
|
||||
BSTREAM_TO_UINT16(hdl, pPair);
|
||||
|
||||
/* now pPair points to UUID; find descriptor with matching UUID */
|
||||
pDesc = &pCb->pCharList[pCb->charListIdx];
|
||||
for (i = pCb->charListIdx;
|
||||
(i < pCb->charListLen) && (((*pDesc)->settings & ATTC_SET_DESCRIPTOR) != 0);
|
||||
i++, pDesc++)
|
||||
{
|
||||
/* if characteristic not already found */
|
||||
if (pCb->pHdlList[i] == 0)
|
||||
{
|
||||
/* if UUIDs match */
|
||||
if (attcUuidCmp(*pDesc, pPair, settings))
|
||||
{
|
||||
/* match found; store handle */
|
||||
pCb->pHdlList[i] = hdl;
|
||||
|
||||
ATT_TRACE_INFO1("descriptor found handle:0x%x", hdl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a ATTC_FIND_INFO_RSP received while performing characteristic discovery.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscProcDesc(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint8_t format;
|
||||
uint8_t pairLen;
|
||||
uint8_t settings;
|
||||
|
||||
/* if find info successful */
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
p = pMsg->pValue;
|
||||
pEnd = pMsg->pValue + pMsg->valueLen;
|
||||
|
||||
/* determine UUID format */
|
||||
BSTREAM_TO_UINT8(format, p);
|
||||
if (format == ATT_FIND_HANDLE_16_UUID)
|
||||
{
|
||||
settings = 0;
|
||||
pairLen = ATT_FIND_RSP_LEN_UUID16;
|
||||
}
|
||||
else if (format == ATT_FIND_HANDLE_128_UUID)
|
||||
{
|
||||
settings = ATTC_SET_UUID_128;
|
||||
pairLen = ATT_FIND_RSP_LEN_UUID128;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_INVALID_RSP;
|
||||
}
|
||||
|
||||
/* for each handle/UUID pair */
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* process descriptor handle/UUID pair */
|
||||
attcDiscProcDescPair(pCb, settings, p);
|
||||
|
||||
/* go to next */
|
||||
p += pairLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* if descriptor discovery complete for this characteristic */
|
||||
if (pMsg->hdr.status != ATT_SUCCESS || pMsg->continuing == FALSE)
|
||||
{
|
||||
/* go to next entry in list */
|
||||
pChar = &pCb->pCharList[pCb->charListIdx];
|
||||
do
|
||||
{
|
||||
/* check if at end of list */
|
||||
pCb->charListIdx++;
|
||||
if (pCb->charListIdx == pCb->charListLen)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* skip over descriptors that follow current characteristic */
|
||||
pChar++;
|
||||
} while ((*pChar)->settings & ATTC_SET_DESCRIPTOR);
|
||||
|
||||
/* proceed with descriptor discovery for the next characteristic */
|
||||
return attcDiscDescriptors((dmConnId_t) pMsg->hdr.param, pCb);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* still more to do */
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a characteristic declaration.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param settings Indicates 16 or 128 bit UUID.
|
||||
* \param pDecl Pointer to declaration.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcDiscProcCharDecl(attcDiscCb_t *pCb, uint8_t settings, uint8_t *pDecl)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint16_t hdl;
|
||||
uint16_t declHdl;
|
||||
uint8_t i;
|
||||
|
||||
/* parse it */
|
||||
BSTREAM_TO_UINT16(declHdl, pDecl);
|
||||
pDecl++; /* skip properties field */
|
||||
BSTREAM_TO_UINT16(hdl, pDecl);
|
||||
|
||||
/* if looking for end handle of previous characteristic */
|
||||
if (pCb->endHdlIdx != ATT_DISC_HDL_IDX_NONE)
|
||||
{
|
||||
/* end handle of previous characteristic is one less than
|
||||
* the handle of the current characteristic declaration
|
||||
*/
|
||||
pCb->pHdlList[pCb->endHdlIdx] = declHdl - 1;
|
||||
pCb->endHdlIdx = ATT_DISC_HDL_IDX_NONE;
|
||||
}
|
||||
|
||||
/* check handle */
|
||||
if (hdl > declHdl && hdl <= pCb->svcEndHdl)
|
||||
{
|
||||
/* now pDecl points to UUID; search for UUID in characteristic list */
|
||||
for (i = 0, pChar = pCb->pCharList; i < pCb->charListLen; i++, pChar++)
|
||||
{
|
||||
/* if characteristic not already found */
|
||||
if (pCb->pHdlList[i] == 0)
|
||||
{
|
||||
/* if UUIDs match */
|
||||
if (attcUuidCmp(*pChar, pDecl, settings))
|
||||
{
|
||||
/* match found; store handle */
|
||||
pCb->pHdlList[i] = hdl;
|
||||
|
||||
/* if not at end of list and next in list is a descriptor */
|
||||
if (i < (pCb->charListLen - 1) &&
|
||||
((*(pChar + 1))->settings & ATTC_SET_DESCRIPTOR) == ATTC_SET_DESCRIPTOR)
|
||||
{
|
||||
/* characteristic has descriptors, we need to find end handle
|
||||
* store end handle temporarily in handle list location
|
||||
* for the first descriptor
|
||||
*/
|
||||
pCb->endHdlIdx = i + 1;
|
||||
}
|
||||
|
||||
ATT_TRACE_INFO1("characteristic found handle:0x%x", hdl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* invalid handle; skip this declaration */
|
||||
ATT_TRACE_WARN1("invalid handle:0x%x", hdl);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a ATTC_READ_BY_TYPE_RSP received while performing characteristic discovery.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscProcChar(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint8_t pairLen;
|
||||
uint8_t settings;
|
||||
|
||||
/* if read by type successful */
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
p = pMsg->pValue;
|
||||
pEnd = pMsg->pValue + pMsg->valueLen;
|
||||
|
||||
/* verify attribute-handle pair length and determine UUID length */
|
||||
BSTREAM_TO_UINT8(pairLen, p);
|
||||
if (pairLen == ATT_READ_RSP_LEN_UUID16)
|
||||
{
|
||||
settings = 0;
|
||||
}
|
||||
else if (pairLen == ATT_READ_RSP_LEN_UUID128)
|
||||
{
|
||||
settings = ATTC_SET_UUID_128;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_INVALID_RSP;
|
||||
}
|
||||
|
||||
/* Note that ATT already verifies response length and handle values */
|
||||
|
||||
/* for each characteristic declaration */
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* process characteristic declaration */
|
||||
attcDiscProcCharDecl(pCb, settings, p);
|
||||
|
||||
/* go to next */
|
||||
p += pairLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* if characteristic discovery complete */
|
||||
if (pMsg->hdr.status != ATT_SUCCESS || pMsg->continuing == FALSE)
|
||||
{
|
||||
/* check if characteristic end handle needs to be set */
|
||||
if (pCb->endHdlIdx != ATT_DISC_HDL_IDX_NONE)
|
||||
{
|
||||
/* end handle of characteristic declaration is end handle of service */
|
||||
pCb->pHdlList[pCb->endHdlIdx] = pCb->svcEndHdl;
|
||||
}
|
||||
|
||||
/* proceed with descriptor discovery */
|
||||
pCb->charListIdx = 0;
|
||||
return attcDiscDescriptors((dmConnId_t) pMsg->hdr.param, pCb);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* still more to do */
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiates the next characteristic configuration procedure.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if configuration procedure completed successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscConfigNext(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
attcDiscCfg_t *pCfg;
|
||||
|
||||
pCfg = pCb->pCfgList + pCb->charListIdx;
|
||||
|
||||
/* iterate over list */
|
||||
while (pCb->charListIdx < pCb->cfgListLen)
|
||||
{
|
||||
/* if handle was discovered */
|
||||
if (pCb->pHdlList[pCfg->hdlIdx] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if value present do write req */
|
||||
if (pCfg->valueLen != 0)
|
||||
{
|
||||
AttcWriteReq(connId, pCb->pHdlList[pCfg->hdlIdx], pCfg->valueLen, (uint8_t *) pCfg->pValue);
|
||||
}
|
||||
/* else do read */
|
||||
else
|
||||
{
|
||||
AttcReadReq(connId, pCb->pHdlList[pCfg->hdlIdx]);
|
||||
}
|
||||
|
||||
/* done for now */
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
|
||||
/* next in list */
|
||||
pCb->charListIdx++;
|
||||
pCfg++;
|
||||
}
|
||||
|
||||
/* nothing left to configure; we're done */
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function discovers the given service on a peer device. Function
|
||||
* AttcFindByTypeValueReq() is called to initiate the discovery procedure.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param uuidLen Length of UUID (2 or 16).
|
||||
* \param pUuid Pointer to UUID data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcDiscService(dmConnId_t connId, attcDiscCb_t *pCb, uint8_t uuidLen, uint8_t *pUuid)
|
||||
{
|
||||
AttcFindByTypeValueReq(connId, ATT_HANDLE_START, ATT_HANDLE_MAX, ATT_UUID_PRIMARY_SERVICE,
|
||||
uuidLen, pUuid, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function processes a service discovery result. It should be called
|
||||
* when an ATTC_FIND_BY_TYPE_VALUE_RSP callback event is received after service
|
||||
* discovery is initiated by calling AttcDiscService().
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscServiceCmpl(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
uint8_t *p;
|
||||
|
||||
/* verify callback event */
|
||||
if (pMsg->hdr.event != ATTC_FIND_BY_TYPE_VALUE_RSP)
|
||||
{
|
||||
ATT_TRACE_WARN1("unexpected callback event %d", pMsg->hdr.event);
|
||||
return ATT_ERR_UNDEFINED;
|
||||
}
|
||||
/* verify status */
|
||||
else if (pMsg->hdr.status != ATT_SUCCESS)
|
||||
{
|
||||
return pMsg->hdr.status;
|
||||
}
|
||||
/* verify result was found */
|
||||
else if (pMsg->valueLen == 0)
|
||||
{
|
||||
return ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* get handles of first returned service only; ATT has already performed error checking */
|
||||
p = pMsg->pValue;
|
||||
BSTREAM_TO_UINT16(pCb->svcStartHdl, p);
|
||||
BSTREAM_TO_UINT16(pCb->svcEndHdl, p);
|
||||
|
||||
ATT_TRACE_INFO2("found service startHdl=0x%x endHdl=0x%x", pCb->svcStartHdl, pCb->svcEndHdl);
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function starts characteristic and characteristic descriptor
|
||||
* discovery for a service on a peer device. The service must have been previously
|
||||
* discovered by calling AttcDiscService() and AttcDiscServiceCmpl().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcDiscCharStart(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
/* initialize control block */
|
||||
pCb->charListIdx = 0;
|
||||
pCb->endHdlIdx = ATT_DISC_HDL_IDX_NONE;
|
||||
|
||||
AttcReadByTypeReq(connId, pCb->svcStartHdl, pCb->svcEndHdl, ATT_16_UUID_LEN,
|
||||
(uint8_t *) attChUuid, TRUE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function processes a characteristic discovery result. It should be
|
||||
* called when an ATTC_READ_BY_TYPE_RSP or ATTC_FIND_INFO_RSP callback event is
|
||||
* received after characteristic discovery is initiated by calling AttcDiscCharStart().
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscCharCmpl(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* verify callback event */
|
||||
if (pMsg->hdr.event != ATTC_READ_BY_TYPE_RSP &&
|
||||
pMsg->hdr.event != ATTC_FIND_INFO_RSP)
|
||||
{
|
||||
ATT_TRACE_WARN1("unexpected callback event %d", pMsg->hdr.event);
|
||||
return ATT_ERR_UNDEFINED;
|
||||
}
|
||||
|
||||
/* if read by type (characteristic discovery) */
|
||||
if (pMsg->hdr.event == ATTC_READ_BY_TYPE_RSP)
|
||||
{
|
||||
status = attcDiscProcChar(pCb, pMsg);
|
||||
}
|
||||
/* else if find info (descriptor discovery) */
|
||||
else
|
||||
{
|
||||
status = attcDiscProcDesc(pCb, pMsg);
|
||||
}
|
||||
|
||||
/* if characteristic discovery failed clear any handles */
|
||||
if (status != ATT_SUCCESS && status != ATT_CONTINUING)
|
||||
{
|
||||
memset(pCb->pHdlList, 0, (pCb->charListLen * sizeof(uint16_t)));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function starts characteristic configuration for characteristics on a
|
||||
* peer device. The characteristics must have been previously discovered by calling
|
||||
* AttcDiscCharStart() and AttcDisccharCont().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if nothing to configure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscConfigStart(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
/* use char list index to iterate over config list */
|
||||
pCb->charListIdx = 0;
|
||||
|
||||
return attcDiscConfigNext(connId, pCb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function initiates the next characteristic configuration procedure.
|
||||
* It should be called when an ATTC_READ_RSP or ATTC_WRITE_RSP callback event is received
|
||||
* after characteristic configuration is initiated by calling AttcDiscConfigStart().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if configuration procedure completed successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscConfigCmpl(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
/* go to next in list */
|
||||
pCb->charListIdx++;
|
||||
|
||||
return attcDiscConfigNext(connId, pCb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function resumes the characteristic configuration procedure. It can
|
||||
* be called when an ATTC_READ_RSP or ATTC_WRITE_RSP callback event is received
|
||||
* with failure status to attempt the read or write procedure again.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if configuration procedure completed successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscConfigResume(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
return attcDiscConfigNext(connId, pCb);
|
||||
}
|
||||
Vendored
+840
@@ -0,0 +1,840 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* type for send request function */
|
||||
typedef void (*attcSendReq_t)(attcCcb_t *pCcb);
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attcDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
static void attcCtrlCback(wsfMsgHdr_t *pMsg);
|
||||
static void attcConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
static void attcMsgCback(attcApiMsg_t *pMsg);
|
||||
|
||||
static void attcSendSimpleReq(attcCcb_t *pCcb);
|
||||
static void attcSendContinuingReq(attcCcb_t *pCcb);
|
||||
static void attcSendMtuReq(attcCcb_t *pCcb);
|
||||
static void attcSendWriteCmd(attcCcb_t *pCcb);
|
||||
static void attcSendPrepWriteReq(attcCcb_t *pCcb);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attFcnIf_t attcFcnIf =
|
||||
{
|
||||
attcDataCback,
|
||||
attcCtrlCback,
|
||||
(attMsgHandler_t) attcMsgCback,
|
||||
attcConnCback
|
||||
};
|
||||
|
||||
/* Table of send request functions */
|
||||
static const attcSendReq_t attcSendReqTbl[] =
|
||||
{
|
||||
NULL, /* ATTC_MSG_API_NONE (unused) */
|
||||
attcSendMtuReq, /* ATTC_MSG_API_MTU */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_FIND_INFO */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_FIND_BY_TYPE_VALUE */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_READ_BY_TYPE */
|
||||
attcSendSimpleReq, /* ATTC_MSG_API_READ */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_READ_LONG */
|
||||
attcSendSimpleReq, /* ATTC_MSG_API_READ_MULTIPLE */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_READ_BY_GROUP_TYPE */
|
||||
attcSendSimpleReq, /* ATTC_MSG_API_WRITE */
|
||||
attcSendWriteCmd, /* ATTC_MSG_API_WRITE_CMD */
|
||||
attcSendPrepWriteReq, /* ATTC_MSG_API_PREP_WRITE */
|
||||
attcSendSimpleReq /* ATTC_MSG_API_EXEC_WRITE */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
attcCb_t attcCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if application callback is pending for a given write command, or the maximum
|
||||
* number of simultaneous write commands has been reached.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return TRUE if app callback's pending or maximum number of simultaneous write commands reached.
|
||||
* FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t attcPendWriteCmd(attcCcb_t *pCcb, attcApiMsg_t *pMsg)
|
||||
{
|
||||
uint8_t pendRsp;
|
||||
uint8_t i;
|
||||
|
||||
/* initialize number of response callbacks pending */
|
||||
pendRsp = 0;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_WRITE_CMD; i++)
|
||||
{
|
||||
/* if callback pending for write command */
|
||||
if (pCcb->pendWriteCmdHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if callback pending for this handle */
|
||||
if (pCcb->pendWriteCmdHandle[i] == pMsg->handle)
|
||||
{
|
||||
/* callback pending for this write command */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
pendRsp++;
|
||||
}
|
||||
}
|
||||
|
||||
/* no callback is pending for this write command but see if the maximum number of simultaneous
|
||||
write commands has been reached */
|
||||
return (pendRsp < ATT_NUM_SIMUL_WRITE_CMD) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set pending write command response callback for a given attribute handle.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSetPendWriteCmd(attcCcb_t *pCcb)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_WRITE_CMD; i++)
|
||||
{
|
||||
/* if entry free */
|
||||
if (pCcb->pendWriteCmdHandle[i] == ATT_HANDLE_NONE)
|
||||
{
|
||||
/* set pending write command handle */
|
||||
pCcb->pendWriteCmdHandle[i] = pCcb->outReq.handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Call pending write command response application callback.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCcb ATTC control block.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcWriteCmdCallback(dmConnId_t connId, attcCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if any pending write command callback */
|
||||
for (i = 0; i < ATT_NUM_SIMUL_WRITE_CMD; i++)
|
||||
{
|
||||
if (pCcb->pendWriteCmdHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call callback with status */
|
||||
attcExecCallback(connId, ATTC_WRITE_CMD_RSP, pCcb->pendWriteCmdHandle[i], status);
|
||||
pCcb->pendWriteCmdHandle[i] = ATT_HANDLE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send attribute client request that has already been built.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendSimpleReq(attcCcb_t *pCcb)
|
||||
{
|
||||
attcPktParam_t *pPkt = pCcb->outReq.pPkt;
|
||||
|
||||
/* clear stored packet pointer */
|
||||
pCcb->outReq.pPkt = NULL;
|
||||
|
||||
/* start request timer (except for write command) */
|
||||
if (pCcb->outReq.hdr.event != ATTC_MSG_API_WRITE_CMD)
|
||||
{
|
||||
pCcb->outReqTimer.msg.event = ATTC_MSG_REQ_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outReqTimer, pAttCfg->transTimeout);
|
||||
}
|
||||
|
||||
/* send packet to L2CAP */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, pPkt->len, (uint8_t *) pPkt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a request that may be continuing.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendContinuingReq(attcCcb_t *pCcb)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* allocate new buffer */
|
||||
if ((pPkt = attMsgAlloc(pCcb->outReq.pPkt->len + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* copy stored packet to new */
|
||||
memcpy(pPkt, pCcb->outReq.pPkt, pCcb->outReq.pPkt->len + L2C_PAYLOAD_START);
|
||||
}
|
||||
/* else handle error case of allocation failure */
|
||||
else
|
||||
{
|
||||
/* free stored packet and call callback with failure status */
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* else not continuing */
|
||||
else
|
||||
{
|
||||
/* send the stored packet */
|
||||
pPkt = pCcb->outReq.pPkt;
|
||||
|
||||
/* clear stored packet pointer */
|
||||
pCcb->outReq.pPkt = NULL;
|
||||
}
|
||||
|
||||
/* build remaining fields of packet from stored parameters */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_READ_LONG)
|
||||
{
|
||||
/* build offset field */
|
||||
p += sizeof(uint16_t);
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.o.offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Find Info, Find By Type Value, Read By Type, and Read By Group Type
|
||||
* request packets all begin with a start handle and end handle.
|
||||
*/
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.h.startHandle);
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.h.endHandle);
|
||||
}
|
||||
|
||||
/* start request timer */
|
||||
pCcb->outReqTimer.msg.event = ATTC_MSG_REQ_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outReqTimer, pAttCfg->transTimeout);
|
||||
|
||||
/* send packet to L2CAP */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, pPkt->len, (uint8_t *) pPkt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send MTU request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendMtuReq(attcCcb_t *pCcb)
|
||||
{
|
||||
/* if MTU already exchanged */
|
||||
if (pCcb->pMainCcb->control & ATT_CCB_STATUS_MTU_SENT)
|
||||
{
|
||||
/* discard request */
|
||||
attcFreePkt(&pCcb->outReq);
|
||||
|
||||
/* clear out req */
|
||||
pCcb->outReq.hdr.event = ATTC_MSG_API_NONE;
|
||||
|
||||
ATT_TRACE_WARN0("MTU req discarded");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set MTU sent */
|
||||
pCcb->pMainCcb->control |= ATT_CCB_STATUS_MTU_SENT;
|
||||
|
||||
/* send packet */
|
||||
attcSendSimpleReq(pCcb);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send attribute client Write command.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendWriteCmd(attcCcb_t *pCcb)
|
||||
{
|
||||
/* send packet */
|
||||
attcSendSimpleReq(pCcb);
|
||||
|
||||
/* if flow not disabled */
|
||||
if (!(pCcb->pMainCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
/* call callback */
|
||||
attcExecCallback(pCcb->pMainCcb->connId, ATTC_WRITE_CMD_RSP, pCcb->outReq.handle, ATT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set pending write command callback for this handle */
|
||||
attcSetPendWriteCmd(pCcb);
|
||||
}
|
||||
|
||||
/* clear out req */
|
||||
pCcb->outReq.hdr.event = ATTC_MSG_API_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send attribute client Prepare Write request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendPrepWriteReq(attcCcb_t *pCcb)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint16_t dataLen;
|
||||
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* determine size of buffer to allocate */
|
||||
if (pCcb->outReqParams.w.len < (pCcb->pMainCcb->mtu - ATT_PREP_WRITE_REQ_LEN))
|
||||
{
|
||||
dataLen = pCcb->outReqParams.w.len;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataLen = pCcb->pMainCcb->mtu - ATT_PREP_WRITE_REQ_LEN;
|
||||
}
|
||||
|
||||
/* allocate new buffer */
|
||||
if ((pPkt = attMsgAlloc(dataLen + ATT_PREP_WRITE_REQ_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* copy fixed fields */
|
||||
memcpy(pPkt, pCcb->outReq.pPkt, ATT_PREP_WRITE_REQ_LEN + L2C_PAYLOAD_START);
|
||||
|
||||
/* copy data */
|
||||
memcpy(((uint8_t *) pPkt + L2C_PAYLOAD_START + ATT_PREP_WRITE_REQ_LEN),
|
||||
pCcb->outReqParams.w.pValue, dataLen);
|
||||
|
||||
/* update length and data pointer */
|
||||
pCcb->outReqParams.w.pValue += dataLen;
|
||||
pCcb->outReqParams.w.len -= dataLen;
|
||||
}
|
||||
/* else handle error case of allocation failure */
|
||||
else
|
||||
{
|
||||
/* free stored packet and call callback with failure status */
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* else not continuing */
|
||||
else
|
||||
{
|
||||
dataLen = pCcb->outReqParams.w.len;
|
||||
|
||||
/* send the stored packet */
|
||||
pPkt = pCcb->outReq.pPkt;
|
||||
|
||||
/* clear stored packet pointer */
|
||||
pCcb->outReq.pPkt = NULL;
|
||||
}
|
||||
|
||||
/* build remaining fields of packet from stored parameters */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START + ATT_HDR_LEN + sizeof(uint16_t);
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.w.offset);
|
||||
|
||||
/* update offset after building packet */
|
||||
pCcb->outReqParams.w.offset += dataLen;
|
||||
|
||||
/* start request timer */
|
||||
pCcb->outReqTimer.msg.event = ATTC_MSG_REQ_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outReqTimer, pAttCfg->transTimeout);
|
||||
|
||||
/* send packet to L2CAP */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, dataLen + ATT_PREP_WRITE_REQ_LEN, (uint8_t *) pPkt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Lookup and execute function to build and send request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcSendReq(attcCcb_t *pCcb)
|
||||
{
|
||||
(*attcSendReqTbl[pCcb->outReq.hdr.event])(pCcb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up and send an attribute client request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcSetupReq(attcCcb_t *pCcb, attcApiMsg_t *pMsg)
|
||||
{
|
||||
/* set out req to api message */
|
||||
pCcb->outReq = *pMsg;
|
||||
|
||||
/* store parameters */
|
||||
pCcb->outReqParams = *(pMsg->pPkt);
|
||||
|
||||
/* build and send request */
|
||||
attcSendReq(pCcb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Data callback for ATTC.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t opcode;
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* get connection control block for this handle, ignore packet if not found */
|
||||
if ((pCcb = attcCcbByHandle(handle)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse opcode */
|
||||
opcode = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* if response */
|
||||
if (opcode <= ATT_PDU_EXEC_WRITE_RSP)
|
||||
{
|
||||
attcProcRsp(pCcb, len, pPacket);
|
||||
}
|
||||
/* else if indication or notification */
|
||||
else if ((opcode == ATT_PDU_VALUE_NTF) || (opcode == ATT_PDU_VALUE_IND))
|
||||
{
|
||||
attcProcInd(pCcb, len, pPacket);
|
||||
}
|
||||
/* else unknown opcode */
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("attc unknown opcode 0x%02x", opcode);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2CAP control callback.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* note this function is currently only called when flow is enabled */
|
||||
|
||||
/* get CCB */
|
||||
if ((pCcb = attcCcbByConnId((dmConnId_t) pMsg->param)) != NULL)
|
||||
{
|
||||
/* if confirmation pending try sending now */
|
||||
AttcIndConfirm((dmConnId_t) pMsg->param);
|
||||
|
||||
/* call pending write command callback */
|
||||
attcWriteCmdCallback((dmConnId_t) pMsg->param, pCcb, ATT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection callback for ATTC.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
attcCcb_t *pClient;
|
||||
uint16_t localMtu;
|
||||
uint8_t status;
|
||||
|
||||
/* if connection opened */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
/* if we initiated connection send MTU request */
|
||||
if (DmConnRole(pCcb->connId) == DM_ROLE_MASTER)
|
||||
{
|
||||
localMtu = WSF_MIN(pAttCfg->mtu, (HciGetMaxRxAclLen() - L2C_HDR_LEN));
|
||||
|
||||
/* if desired MTU is not the default */
|
||||
if (localMtu != ATT_DEFAULT_MTU)
|
||||
{
|
||||
AttcMtuReq(pCcb->connId, localMtu);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if connection closed */
|
||||
else if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* set status */
|
||||
if (pDmEvt->connClose.hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
status = pDmEvt->connClose.reason + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = pDmEvt->connClose.hdr.status + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
|
||||
/* get client control block directly */
|
||||
pClient = &attcCb.ccb[pCcb->connId - 1];
|
||||
|
||||
/* free any out req */
|
||||
if (pClient->outReq.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
WsfTimerStop(&pClient->outReqTimer);
|
||||
attcReqClear(pClient, &pClient->outReq, status);
|
||||
}
|
||||
|
||||
/* free any req on deck */
|
||||
if (pClient->onDeck.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pClient, &pClient->onDeck, status);
|
||||
}
|
||||
|
||||
/* initialize other control block variables */
|
||||
pClient->flowDisabled = FALSE;
|
||||
pClient->cnfPending = FALSE;
|
||||
|
||||
/* pass to connection close callback for signed data */
|
||||
if (attcCb.pSign != NULL)
|
||||
{
|
||||
(*attcCb.pSign->closeCback)(pClient, status);
|
||||
}
|
||||
|
||||
/* call pending write command callback */
|
||||
attcWriteCmdCallback(pCcb->connId, pClient, status);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Message handler callback for ATTC.
|
||||
*
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcMsgCback(attcApiMsg_t *pMsg)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attcCcbByConnId((dmConnId_t) pMsg->hdr.param)) == NULL)
|
||||
{
|
||||
/* if message has a packet buffer */
|
||||
if (pMsg->hdr.event >= ATTC_MSG_API_MTU &&
|
||||
pMsg->hdr.event <= ATTC_MSG_API_SIGNED_WRITE_CMD)
|
||||
{
|
||||
/* free packet buffer */
|
||||
attcFreePkt(pMsg);
|
||||
}
|
||||
|
||||
/* ignore if connection not in use */
|
||||
return;
|
||||
}
|
||||
|
||||
/* if an API request to send packet (non-signed) */
|
||||
if (pMsg->hdr.event <= ATTC_MSG_API_EXEC_WRITE)
|
||||
{
|
||||
/* verify no API request already waiting on deck, in progress, or no pending write command
|
||||
already for this handle */
|
||||
if ((pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE) ||
|
||||
(pCcb->outReq.hdr.event > ATTC_MSG_API_MTU) ||
|
||||
((pMsg->hdr.event == ATTC_MSG_API_WRITE_CMD) &&
|
||||
attcPendWriteCmd(pCcb, pMsg)))
|
||||
{
|
||||
/* free request and call callback with failure status */
|
||||
attcReqClear(pCcb, pMsg, ATT_ERR_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if MTU request in progress or flow controlled */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_MTU || pCcb->flowDisabled)
|
||||
{
|
||||
/* put request "on deck" for processing later */
|
||||
pCcb->onDeck = *pMsg;
|
||||
}
|
||||
/* otherwise ready to send; set up request */
|
||||
else
|
||||
{
|
||||
attcSetupReq(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
/* else if signed data event */
|
||||
else if (pMsg->hdr.event >= ATTC_MSG_API_SIGNED_WRITE_CMD &&
|
||||
pMsg->hdr.event <= ATTC_MSG_CMAC_CMPL)
|
||||
{
|
||||
/* pass to message callback for signed data */
|
||||
if (attcCb.pSign != NULL)
|
||||
{
|
||||
(*attcCb.pSign->msgCback)(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
/* else if cancel request */
|
||||
else if (pMsg->hdr.event == ATTC_MSG_API_CANCEL)
|
||||
{
|
||||
/* free any out req (except mtu req) */
|
||||
if (pCcb->outReq.hdr.event != ATTC_MSG_API_NONE &&
|
||||
pCcb->outReq.hdr.event != ATTC_MSG_API_MTU)
|
||||
{
|
||||
WsfTimerStop(&pCcb->outReqTimer);
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_CANCELLED);
|
||||
}
|
||||
/* else free any req on deck */
|
||||
else if (pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pCcb, &pCcb->onDeck, ATT_ERR_CANCELLED);
|
||||
}
|
||||
}
|
||||
/* else if timeout */
|
||||
else if (pMsg->hdr.event == ATTC_MSG_REQ_TIMEOUT)
|
||||
{
|
||||
/* free any out req */
|
||||
if (pCcb->outReq.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_TIMEOUT);
|
||||
pCcb->pMainCcb->control |= ATT_CCB_STATUS_TX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the ATTC connection control block connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not in use.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attcCcb_t *attcCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
if (DmConnInUse(connId))
|
||||
{
|
||||
return &attcCb.ccb[connId - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("attc ccb not in use: %d", connId);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the given handle.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attcCcb_t *attcCcbByHandle(uint16_t handle)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) != DM_CONN_ID_NONE)
|
||||
{
|
||||
return &attcCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free the packet buffer of an API message structure.
|
||||
*
|
||||
* \param pMsg Pointer to API message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcFreePkt(attcApiMsg_t *pMsg)
|
||||
{
|
||||
if (pMsg->pPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pMsg->pPkt);
|
||||
pMsg->pPkt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute application callback function.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param event Callback event ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status)
|
||||
{
|
||||
if (event != ATT_METHOD_MTU)
|
||||
{
|
||||
attExecCallback(connId, event, handle, status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear an outstanding request and execute the callback.
|
||||
*
|
||||
* \param pCcb Pointer to control block.
|
||||
* \param pMsg API message.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcReqClear(attcCcb_t *pCcb, attcApiMsg_t *pMsg, uint8_t status)
|
||||
{
|
||||
attcFreePkt(pMsg);
|
||||
attcExecCallback(pCcb->pMainCcb->connId, pMsg->hdr.event, pMsg->handle, status);
|
||||
pMsg->hdr.event = ATTC_MSG_API_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set automatic Indication Confirmations sent from this ATT Client.
|
||||
*
|
||||
* \param enable \ref TRUE to enable automatic confirmations (default), \ref FALSE to disable.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcSetAutoConfirm(bool_t enable)
|
||||
{
|
||||
attcCb.autoCnf = enable;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT client.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* Initialize control block */
|
||||
attcCb.pSign = NULL;
|
||||
attcCb.autoCnf = TRUE;
|
||||
|
||||
/* Initialize control block CCBs */
|
||||
for (i = 0, pCcb = attcCb.ccb; i < DM_CONN_MAX; i++, pCcb++)
|
||||
{
|
||||
/* set pointer to main CCB */
|
||||
pCcb->pMainCcb = &attCb.ccb[i];
|
||||
|
||||
/* initialize timer */
|
||||
pCcb->outReqTimer.handlerId = attCb.handlerId;
|
||||
pCcb->outReqTimer.msg.param = i + 1; /* param stores the conn id */
|
||||
}
|
||||
|
||||
/* set up callback interface */
|
||||
attCb.pClient = &attcFcnIf;
|
||||
}
|
||||
Vendored
+208
@@ -0,0 +1,208 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATTC_MAIN_H
|
||||
#define ATTC_MAIN_H
|
||||
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "att_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Buffer lengths for requests */
|
||||
#define ATT_MTU_REQ_BUF_LEN (ATT_MTU_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_FIND_INFO_REQ_BUF_LEN (ATT_FIND_INFO_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_FIND_TYPE_REQ_BUF_LEN (ATT_FIND_TYPE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_TYPE_REQ_BUF_LEN (ATT_READ_TYPE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_REQ_BUF_LEN (ATT_READ_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_BLOB_REQ_BUF_LEN (ATT_READ_BLOB_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_MULT_REQ_BUF_LEN (ATT_READ_MULT_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_GROUP_TYPE_REQ_BUF_LEN (ATT_READ_GROUP_TYPE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_WRITE_REQ_BUF_LEN (ATT_WRITE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_WRITE_CMD_BUF_LEN (ATT_WRITE_CMD_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_SIGNED_WRITE_CMD_BUF_LEN (ATT_SIGNED_WRITE_CMD_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_PREP_WRITE_REQ_BUF_LEN (ATT_PREP_WRITE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_EXEC_WRITE_REQ_BUF_LEN (ATT_EXEC_WRITE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
|
||||
/* values for 'continuing' flag */
|
||||
#define ATTC_CONTINUING TRUE
|
||||
#define ATTC_NOT_CONTINUING FALSE
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTC event handler messages */
|
||||
enum
|
||||
{
|
||||
/* messages from API; note these match method values */
|
||||
ATTC_MSG_API_NONE = ATTC_MSG_START,
|
||||
ATTC_MSG_API_MTU = ATT_METHOD_MTU,
|
||||
ATTC_MSG_API_FIND_INFO = ATT_METHOD_FIND_INFO,
|
||||
ATTC_MSG_API_FIND_BY_TYPE_VALUE = ATT_METHOD_FIND_TYPE,
|
||||
ATTC_MSG_API_READ_BY_TYPE = ATT_METHOD_READ_TYPE,
|
||||
ATTC_MSG_API_READ = ATT_METHOD_READ,
|
||||
ATTC_MSG_API_READ_LONG = ATT_METHOD_READ_BLOB,
|
||||
ATTC_MSG_API_READ_MULTIPLE = ATT_METHOD_READ_MULTIPLE,
|
||||
ATTC_MSG_API_READ_BY_GROUP_TYPE = ATT_METHOD_READ_GROUP_TYPE,
|
||||
ATTC_MSG_API_WRITE = ATT_METHOD_WRITE,
|
||||
ATTC_MSG_API_WRITE_CMD = ATT_METHOD_WRITE_CMD,
|
||||
ATTC_MSG_API_PREP_WRITE = ATT_METHOD_PREPARE_WRITE,
|
||||
ATTC_MSG_API_EXEC_WRITE = ATT_METHOD_EXECUTE_WRITE,
|
||||
ATTC_MSG_API_SIGNED_WRITE_CMD,
|
||||
ATTC_MSG_CMAC_CMPL,
|
||||
ATTC_MSG_API_CANCEL,
|
||||
ATTC_MSG_REQ_TIMEOUT
|
||||
};
|
||||
|
||||
/*!
|
||||
* Data buffer format for API request messages:
|
||||
*
|
||||
* | attcPktParam_t | ATT request data |
|
||||
* | bytes 0 to 7 | bytes 8 - |
|
||||
*/
|
||||
|
||||
/* Structure for API with offset parameter */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t offset;
|
||||
} attcPktParamOffset_t;
|
||||
|
||||
/* Structure for API with start and end handle parameters */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
} attcPktParamHandles_t;
|
||||
|
||||
/* Structure for API with offset and value parameters */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t offset;
|
||||
uint8_t *pValue;
|
||||
} attcPktParamPrepWrite_t;
|
||||
|
||||
/* union of API parameter types */
|
||||
typedef union
|
||||
{
|
||||
uint16_t len;
|
||||
attcPktParamOffset_t o;
|
||||
attcPktParamHandles_t h;
|
||||
attcPktParamPrepWrite_t w;
|
||||
} attcPktParam_t;
|
||||
|
||||
/* verify attcPktParam_t will work in data buffer format described above */
|
||||
WSF_CT_ASSERT(sizeof(attcPktParam_t) <= L2C_PAYLOAD_START);
|
||||
|
||||
/* API message structure */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
attcPktParam_t *pPkt;
|
||||
uint16_t handle;
|
||||
} attcApiMsg_t;
|
||||
|
||||
/* ATTC connection control block */
|
||||
typedef struct
|
||||
{
|
||||
attCcb_t *pMainCcb; /* Pointer to ATT main CCB */
|
||||
attcApiMsg_t onDeck; /* API message "on deck" waiting to be sent */
|
||||
attcApiMsg_t outReq; /* Outstanding request waiting for response */
|
||||
attcPktParam_t outReqParams; /* Parameters associated with outstanding request */
|
||||
wsfTimer_t outReqTimer; /* Outstanding request timer */
|
||||
bool_t flowDisabled; /* Data flow disabled */
|
||||
bool_t cnfPending; /* Handle value confirm packet waiting to be sent */
|
||||
uint16_t pendWriteCmdHandle[ATT_NUM_SIMUL_WRITE_CMD]; /* Callback to app pending for this write cmd handle */
|
||||
} attcCcb_t;
|
||||
|
||||
/* Signed data callbacks */
|
||||
typedef void (*attcSignMsgCback_t)(attcCcb_t *pCcb, attcApiMsg_t *pMsg);
|
||||
typedef void (*attcCloseCback_t)(attcCcb_t *pCcb, uint8_t status);
|
||||
|
||||
/* Signed data callback interface */
|
||||
typedef struct
|
||||
{
|
||||
attcSignMsgCback_t msgCback; /* Message handling callback */
|
||||
attcCloseCback_t closeCback; /* Connection close callback */
|
||||
} attcSignFcnIf_t;
|
||||
|
||||
/* Main control block of the ATTC subsystem */
|
||||
typedef struct
|
||||
{
|
||||
attcCcb_t ccb[DM_CONN_MAX];
|
||||
attcSignFcnIf_t const *pSign;
|
||||
bool_t autoCnf;
|
||||
} attcCb_t;
|
||||
|
||||
/* type for response processing functions */
|
||||
typedef void (*attcProcRsp_t)(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
extern attcCb_t attcCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
attcCcb_t *attcCcbByConnId(dmConnId_t connId);
|
||||
attcCcb_t *attcCcbByHandle(uint16_t handle);
|
||||
void attcFreePkt(attcApiMsg_t *pMsg);
|
||||
void attcExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status);
|
||||
void attcReqClear(attcCcb_t *pCcb, attcApiMsg_t *pMsg, uint8_t status);
|
||||
|
||||
void attcSetupReq(attcCcb_t *pCcb, attcApiMsg_t *pMsg);
|
||||
void attcSendReq(attcCcb_t *pCcb);
|
||||
void attcSendMsg(dmConnId_t connId, uint16_t handle, uint8_t msgId, attcPktParam_t *pPkt, bool_t continuing);
|
||||
|
||||
void attcProcRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attcProcInd(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
void attcProcErrRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcMtuRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcFindOrReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcFindByTypeRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcReadLongRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcPrepWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATTC_MAIN_H */
|
||||
Vendored
+699
@@ -0,0 +1,699 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client mandatory PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Table of response processing functions */
|
||||
static const attcProcRsp_t attcProcRspTbl[] =
|
||||
{
|
||||
attcProcErrRsp, /* ATT_METHOD_ERR */
|
||||
attcProcMtuRsp, /* ATT_METHOD_MTU */
|
||||
attcProcFindOrReadRsp, /* ATT_METHOD_FIND_INFO */
|
||||
attcProcFindByTypeRsp, /* ATT_METHOD_FIND_TYPE */
|
||||
attcProcFindOrReadRsp, /* ATT_METHOD_READ_TYPE */
|
||||
attcProcReadRsp, /* ATT_METHOD_READ */
|
||||
attcProcReadLongRsp, /* ATT_METHOD_READ_BLOB */
|
||||
attcProcReadRsp, /* ATT_METHOD_READ_MULTIPLE */
|
||||
attcProcFindOrReadRsp, /* ATT_METHOD_READ_GROUP_TYPE */
|
||||
attcProcWriteRsp, /* ATT_METHOD_WRITE */
|
||||
NULL, /* ATT_METHOD_WRITE_CMD */
|
||||
attcProcPrepWriteRsp, /* ATT_METHOD_PREPARE_WRITE */
|
||||
attcProcWriteRsp /* ATT_METHOD_EXECUTE_WRITE */
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Error response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcErrRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint8_t *p;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* set callback event from stored method */
|
||||
pEvt->hdr.event = pCcb->outReq.hdr.event;
|
||||
|
||||
/* ignore request opcode in the error response */
|
||||
p++;
|
||||
|
||||
/* if request was a read or write with a specific handle */
|
||||
if (pEvt->hdr.event == ATTC_READ_RSP || pEvt->hdr.event == ATTC_READ_LONG_RSP ||
|
||||
pEvt->hdr.event == ATTC_WRITE_RSP || pEvt->hdr.event == ATTC_PREPARE_WRITE_RSP)
|
||||
{
|
||||
/* ignore handle in the error response; callback will use stored handle from request */
|
||||
p += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set handle from packet */
|
||||
BSTREAM_TO_UINT16(pEvt->handle, p);
|
||||
}
|
||||
|
||||
/* set status from error code in packet, but verify it's not 'success' */
|
||||
BSTREAM_TO_UINT8(pEvt->hdr.status, p);
|
||||
if (pEvt->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_UNDEFINED;
|
||||
}
|
||||
|
||||
/* no parameters so clear length */
|
||||
pEvt->valueLen = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received MTU response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcMtuRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint16_t mtu;
|
||||
|
||||
BYTES_TO_UINT16(mtu, pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN);
|
||||
|
||||
/* verify */
|
||||
if (mtu < ATT_DEFAULT_MTU)
|
||||
{
|
||||
mtu = ATT_DEFAULT_MTU;
|
||||
}
|
||||
|
||||
/* set mtu for the connection */
|
||||
attSetMtu(pCcb->pMainCcb, mtu, WSF_MIN(pAttCfg->mtu, (HciGetMaxRxAclLen() - L2C_HDR_LEN)));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Find Information, Read By Type, or Read By Group Type
|
||||
* response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcFindOrReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint16_t handle;
|
||||
uint16_t nextHandle;
|
||||
uint16_t prevHandle;
|
||||
uint8_t paramLen;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
pEnd = pPacket + L2C_PAYLOAD_START + len;
|
||||
|
||||
/* parameter length depends on packet type */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_FIND_INFO)
|
||||
{
|
||||
/* length in find info response is coded by UUID */
|
||||
if (*p++ == ATT_FIND_HANDLE_16_UUID)
|
||||
{
|
||||
paramLen = ATT_16_UUID_LEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
paramLen = ATT_128_UUID_LEN;
|
||||
}
|
||||
}
|
||||
else if (pCcb->outReq.hdr.event == ATTC_MSG_API_READ_BY_TYPE)
|
||||
{
|
||||
/* length in read by type response is handle plus parameter length */
|
||||
paramLen = *p++ - sizeof(uint16_t);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* length in read by group type response is two handles plus parameter length */
|
||||
paramLen = *p++ - (2 * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
/* get and verify all handles */
|
||||
nextHandle = pCcb->outReqParams.h.startHandle;
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* get and compare handle */
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
if (handle == 0 || nextHandle == 0 || handle < nextHandle ||
|
||||
handle > pCcb->outReqParams.h.endHandle)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if read by group type response get second handle */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_READ_BY_GROUP_TYPE)
|
||||
{
|
||||
prevHandle = handle;
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
if (handle == 0 || handle < prevHandle || handle < nextHandle ||
|
||||
handle > pCcb->outReqParams.h.endHandle)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* set next expected handle, with special case for max handle */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
nextHandle = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextHandle = handle + 1;
|
||||
}
|
||||
|
||||
/* skip over parameter */
|
||||
p += paramLen;
|
||||
|
||||
/* check for truncated response */
|
||||
if (p > pEnd)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if response was correct */
|
||||
if (pEvt->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* if all handles read */
|
||||
if (nextHandle == 0 || nextHandle == (pCcb->outReqParams.h.endHandle + 1))
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
/* else set up for next request */
|
||||
else
|
||||
{
|
||||
pCcb->outReqParams.h.startHandle = nextHandle;
|
||||
pCcb->outReq.handle = nextHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Read response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* nothing to process */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Write response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* no parameters so clear length */
|
||||
pEvt->valueLen = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
attEvt_t evt;
|
||||
|
||||
/* if no request in progress ignore response */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* get method */
|
||||
evt.hdr.event = ATT_OPCODE_2_METHOD(*(pPacket + L2C_PAYLOAD_START));
|
||||
|
||||
/* if response method is not error and does not match stored method ignore response */
|
||||
if ((evt.hdr.event != ATT_METHOD_ERR) && (evt.hdr.event != pCcb->outReq.hdr.event))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* stop request timer */
|
||||
WsfTimerStop(&pCcb->outReqTimer);
|
||||
|
||||
/* initialize event structure then process response */
|
||||
evt.pValue = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
evt.valueLen = len - ATT_HDR_LEN;
|
||||
evt.handle = pCcb->outReq.handle;
|
||||
evt.hdr.status = ATT_SUCCESS;
|
||||
(*attcProcRspTbl[evt.hdr.event])(pCcb, len, pPacket, &evt);
|
||||
|
||||
/* if not continuing or status is not success */
|
||||
if ((pCcb->outReq.hdr.status == ATTC_NOT_CONTINUING) || (evt.hdr.status != ATT_SUCCESS))
|
||||
{
|
||||
/* we're not sending another request so clear the out req */
|
||||
pCcb->outReq.hdr.event = ATTC_MSG_API_NONE;
|
||||
attcFreePkt(&pCcb->outReq);
|
||||
}
|
||||
|
||||
/* call callback (if not mtu rsp) */
|
||||
if ((evt.hdr.event != ATT_METHOD_MTU) && attCb.cback)
|
||||
{
|
||||
/* set additional parameters and call callback */
|
||||
evt.continuing = pCcb->outReq.hdr.status; /* continuing flag */
|
||||
evt.hdr.param = pCcb->outReq.hdr.param; /* connId */
|
||||
(*attCb.cback)(&evt);
|
||||
}
|
||||
|
||||
/* if no flow control */
|
||||
if (!pCcb->flowDisabled)
|
||||
{
|
||||
/* if out req ready */
|
||||
if (pCcb->outReq.pPkt != NULL)
|
||||
{
|
||||
/* build and send request */
|
||||
attcSendReq(pCcb);
|
||||
}
|
||||
/* else if api is on deck */
|
||||
else if (pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
/* set up and send request */
|
||||
attcSetupReq(pCcb, &pCcb->onDeck);
|
||||
|
||||
/* clear on deck */
|
||||
pCcb->onDeck.hdr.event = ATTC_MSG_API_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received indication or notification packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcInd(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
attEvt_t evt;
|
||||
uint8_t *p;
|
||||
uint8_t *pPkt;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
|
||||
/* parse packet and set callback event struct */
|
||||
evt.hdr.event = ATT_OPCODE_2_METHOD(*p++);
|
||||
BSTREAM_TO_UINT16(evt.handle, p);
|
||||
evt.pValue = p;
|
||||
evt.valueLen = len - ATT_HDR_LEN - sizeof(uint16_t);
|
||||
evt.hdr.param = pCcb->pMainCcb->connId;
|
||||
evt.hdr.status = ATT_SUCCESS;
|
||||
evt.continuing = FALSE;
|
||||
|
||||
/* verify handle and call callback */
|
||||
if ((evt.handle != 0) && attCb.cback)
|
||||
{
|
||||
(*attCb.cback)(&evt);
|
||||
}
|
||||
|
||||
/* if indication send confirm */
|
||||
if (attcCb.autoCnf && (evt.hdr.event == ATT_METHOD_VALUE_IND))
|
||||
{
|
||||
if (!pCcb->flowDisabled)
|
||||
{
|
||||
if ((pPkt = attMsgAlloc(ATT_VALUE_CNF_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
*(pPkt + L2C_PAYLOAD_START) = ATT_PDU_VALUE_CNF;
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, ATT_VALUE_CNF_LEN, pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* mark confirm as pending; will be sent when flow enabled or application sends it. */
|
||||
pCcb->cnfPending = TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Build and send a WSF message to ATTC.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param msgId Message ID.
|
||||
* \param pPkt Packet parameters.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcSendMsg(dmConnId_t connId, uint16_t handle, uint8_t msgId, attcPktParam_t *pPkt, bool_t continuing)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
uint16_t mtu;
|
||||
bool_t transTimedOut;
|
||||
|
||||
WsfTaskLock();
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attcCcbByConnId(connId)) != NULL)
|
||||
{
|
||||
/* get MTU size */
|
||||
mtu = pCcb->pMainCcb->mtu;
|
||||
transTimedOut = !!(pCcb->pMainCcb->control & ATT_CCB_STATUS_TX_TIMEOUT);
|
||||
}
|
||||
/* else connection not in use */
|
||||
else
|
||||
{
|
||||
/* MTU size unknown */
|
||||
mtu = 0;
|
||||
transTimedOut = FALSE;
|
||||
}
|
||||
|
||||
WsfTaskUnlock();
|
||||
|
||||
/* if MTU size known for connection */
|
||||
if (mtu > 0)
|
||||
{
|
||||
/* if no transaction's timed out */
|
||||
if (!transTimedOut)
|
||||
{
|
||||
uint16_t dataLen = 0;
|
||||
|
||||
/* if packet is not null then find out its length */
|
||||
if (pPkt != NULL)
|
||||
{
|
||||
/* if not prepare write request */
|
||||
if (msgId != ATTC_MSG_API_PREP_WRITE)
|
||||
{
|
||||
dataLen = pPkt->len;
|
||||
}
|
||||
/* else prepare write request */
|
||||
else
|
||||
{
|
||||
/* if not continuing */
|
||||
if (!continuing)
|
||||
{
|
||||
/* single prepare write request */
|
||||
dataLen = ATT_PREP_WRITE_REQ_LEN + pPkt->w.len;
|
||||
}
|
||||
/* else will be sent as multiple prepare write requests */
|
||||
}
|
||||
}
|
||||
|
||||
/* if packet length is less than or equal to negotiated MTU */
|
||||
if (dataLen <= mtu)
|
||||
{
|
||||
attcApiMsg_t *pMsg;
|
||||
|
||||
/* allocate message buffer */
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(attcApiMsg_t))) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->hdr.status = continuing;
|
||||
pMsg->hdr.event = msgId;
|
||||
pMsg->pPkt = pPkt;
|
||||
pMsg->handle = handle;
|
||||
|
||||
/* send message */
|
||||
WsfMsgSend(attCb.handlerId, pMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* else packet length exceeds MTU size */
|
||||
else
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attcExecCallback(connId, msgId, handle, ATT_ERR_MTU_EXCEEDED);
|
||||
}
|
||||
}
|
||||
else
|
||||
/* transaction's timed out */
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attcExecCallback(connId, msgId, handle, ATT_ERR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/* alloc failed, transaction's timed out or packet length exceeded MTU size; free packet buffer */
|
||||
if (pPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Find Information Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcFindInfoReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_FIND_INFO_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_FIND_INFO_REQ_LEN;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_INFO_REQ);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_FIND_INFO, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadReq(dmConnId_t connId, uint16_t handle)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_READ_REQ_LEN;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_READ, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Write Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcWriteReq(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_WRITE_REQ_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_WRITE_REQ_LEN + valueLen;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pValue, valueLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_WRITE, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel an attribute protocol request in progress.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcCancelReq(dmConnId_t connId)
|
||||
{
|
||||
attcSendMsg(connId, 0, ATTC_MSG_API_CANCEL, NULL, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param mtu Attribute protocol MTU.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcMtuReq(dmConnId_t connId, uint16_t mtu)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_MTU_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_MTU_REQ_LEN;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_MTU_REQ);
|
||||
UINT16_TO_BSTREAM(p, mtu);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, 0, ATTC_MSG_API_MTU, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol indication confirmation.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcIndConfirm(dmConnId_t connId)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
uint8_t *pPkt;
|
||||
|
||||
pCcb = attcCcbByHandle(connId - 1);
|
||||
|
||||
/* If confirmation is pending */
|
||||
if (pCcb && pCcb->cnfPending && !pCcb->flowDisabled)
|
||||
{
|
||||
if ((pPkt = attMsgAlloc(ATT_VALUE_CNF_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
pCcb->cnfPending = FALSE;
|
||||
|
||||
*(pPkt + L2C_PAYLOAD_START) = ATT_PDU_VALUE_CNF;
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, ATT_VALUE_CNF_LEN, pPkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+347
@@ -0,0 +1,347 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client optional read PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Find By Type response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcFindByTypeRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t nextHandle;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
pEnd = pPacket + L2C_PAYLOAD_START + len;
|
||||
|
||||
/* get and verify all handles */
|
||||
nextHandle = pCcb->outReqParams.h.startHandle;
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* get handle pair */
|
||||
BSTREAM_TO_UINT16(startHandle, p);
|
||||
BSTREAM_TO_UINT16(endHandle, p);
|
||||
|
||||
/*
|
||||
* start handle of handle pair must be:
|
||||
* not greater than end handle of handle pair
|
||||
* not less than than start handle of request or end handle of previous handle pair
|
||||
* not greater than end handle of request
|
||||
* and no additional handle pairs following end handle = 0xFFFF
|
||||
*/
|
||||
if ((startHandle > endHandle) || (startHandle < nextHandle) ||
|
||||
(startHandle > pCcb->outReqParams.h.endHandle) || (nextHandle == 0))
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set next expected handle, with special case for max handle */
|
||||
if (endHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
nextHandle = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextHandle = endHandle + 1;
|
||||
}
|
||||
|
||||
/* check for truncated response */
|
||||
if (p > pEnd)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if response was correct */
|
||||
if (pEvt->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* if all handles read */
|
||||
if (nextHandle == 0 || nextHandle > pCcb->outReqParams.h.endHandle)
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
/* else set up for next request */
|
||||
else
|
||||
{
|
||||
pCcb->outReqParams.h.startHandle = nextHandle;
|
||||
pCcb->outReq.handle = nextHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Read Long response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcReadLongRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* length of response is less than mtu */
|
||||
if (len < pCcb->pMainCcb->mtu)
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
/* else set up for next request */
|
||||
else
|
||||
{
|
||||
pCcb->outReqParams.o.offset += pEvt->valueLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Find By Type Value Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param uuid16 16-bit UUID to find.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcFindByTypeValueReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle,
|
||||
uint16_t uuid16, uint16_t valueLen, uint8_t *pValue, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_FIND_TYPE_REQ_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_FIND_TYPE_REQ_LEN + valueLen;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_TYPE_REQ);
|
||||
/* skip start and end handle fields */
|
||||
p += (2 * sizeof(uint16_t));
|
||||
UINT16_TO_BSTREAM(p, uuid16);
|
||||
memcpy(p, pValue, valueLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_FIND_BY_TYPE_VALUE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read By Type Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param uuidLen Length of UUID (2 or 16).
|
||||
* \param pUuid Pointer to UUID data.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadByTypeReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle,
|
||||
uint8_t uuidLen, uint8_t *pUuid, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_TYPE_REQ_BUF_LEN + uuidLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_READ_TYPE_REQ_LEN + uuidLen;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_TYPE_REQ);
|
||||
/* skip start and end handle fields */
|
||||
p += (2 * sizeof(uint16_t));
|
||||
memcpy(p, pUuid, uuidLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_READ_BY_TYPE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param offset Read attribute data starting at this offset.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadLongReq(dmConnId_t connId, uint16_t handle, uint16_t offset, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_BLOB_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_READ_BLOB_REQ_LEN;
|
||||
pPkt->o.offset = offset;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_BLOB_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_READ_LONG, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read Multiple Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param numHandles Number of handles in attribute handle list.
|
||||
* \param pHandles List of attribute handles.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadMultipleReq(dmConnId_t connId, uint8_t numHandles, uint16_t *pHandles)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint16_t handle;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_MULT_REQ_BUF_LEN + (numHandles * sizeof(uint16_t)))) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_READ_MULT_REQ_LEN + (numHandles * sizeof(uint16_t));
|
||||
|
||||
/* save first handle */
|
||||
handle = pHandles[0];
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_MULT_REQ);
|
||||
while (numHandles--)
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, *pHandles);
|
||||
pHandles++;
|
||||
}
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_READ_MULTIPLE, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read By Group Type Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param uuidLen Length of UUID (2 or 16).
|
||||
* \param pUuid Pointer to UUID data.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadByGroupTypeReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle,
|
||||
uint8_t uuidLen, uint8_t *pUuid, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_GROUP_TYPE_REQ_BUF_LEN + uuidLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_READ_GROUP_TYPE_REQ_LEN + uuidLen;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_GROUP_TYPE_REQ);
|
||||
/* skip start and end handle fields */
|
||||
p += (2 * sizeof(uint16_t));
|
||||
memcpy(p, pUuid, uuidLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_READ_BY_GROUP_TYPE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
Vendored
+303
@@ -0,0 +1,303 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client optional signed PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
#include "att_sign.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTC signed PDU control block */
|
||||
typedef struct
|
||||
{
|
||||
attcApiMsg_t msg;
|
||||
} attcSignCb_t;
|
||||
|
||||
/* Message parameters */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
attcApiMsg_t api;
|
||||
} attcSignMsg_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attcSignCloseCback(attcCcb_t *pCcb, uint8_t status);
|
||||
static void attcSignMsgCback(attcCcb_t *pCcb, attcSignMsg_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attcSignFcnIf_t attcSignFcnIf =
|
||||
{
|
||||
(attcSignMsgCback_t) attcSignMsgCback,
|
||||
attcSignCloseCback
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
static attcSignCb_t *pAttcSignCb[DM_CONN_MAX];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate a signing control block.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attcSignCb_t *attcSignCbAlloc(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
WSF_ASSERT(pAttcSignCb[connId - 1] == NULL);
|
||||
|
||||
return (pAttcSignCb[connId - 1] = WsfBufAlloc(sizeof(attcSignCb_t)));
|
||||
}
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free a signing control block.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSignCbFree(dmConnId_t connId)
|
||||
{
|
||||
WsfBufFree(pAttcSignCb[connId - 1]);
|
||||
pAttcSignCb[connId - 1] = NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the signing control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attcSignCb_t *attcSignCbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return pAttcSignCb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection close callback for ATTC signed PDU processing.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param status Connection close status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSignCloseCback(attcCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
attcSignCb_t *pCb;
|
||||
|
||||
/* if connection closed while processing in progress */
|
||||
if ((pCb = attcSignCbByConnId(pCcb->pMainCcb->connId)) != NULL)
|
||||
{
|
||||
|
||||
/* message free buffer */
|
||||
if (pCb->msg.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pCcb, &pCb->msg, status);
|
||||
}
|
||||
|
||||
attcSignCbFree(pCcb->pMainCcb->connId);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Message handler callback for ATTC signed PDU processing.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSignMsgCback(attcCcb_t *pCcb, attcSignMsg_t *pMsg)
|
||||
{
|
||||
attcSignCb_t *pCb;
|
||||
|
||||
if (pMsg->hdr.event == ATTC_MSG_API_SIGNED_WRITE_CMD)
|
||||
{
|
||||
/* adjust msg event id for later use with app callback */
|
||||
pMsg->hdr.event = ATTC_MSG_API_WRITE_CMD;
|
||||
|
||||
/* verify no API request already waiting on deck or in progress,
|
||||
* and no signed write already in progress
|
||||
*/
|
||||
if ((pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE) ||
|
||||
(pCcb->outReq.hdr.event > ATTC_MSG_API_MTU) ||
|
||||
(attcSignCbByConnId((dmConnId_t) pMsg->hdr.param) != NULL))
|
||||
{
|
||||
/* free request and call callback with failure status */
|
||||
attcReqClear(pCcb, &pMsg->api, ATT_ERR_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate signing control block */
|
||||
if ((pCb = attcSignCbAlloc((dmConnId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
/* store message */
|
||||
pCb->msg = pMsg->api;
|
||||
|
||||
uint16_t cmacTxtLen = pCb->msg.pPkt->len - ATT_AUTH_SIG_LEN + sizeof(uint32_t);
|
||||
uint8_t *pCmacText = WsfBufAlloc(cmacTxtLen);
|
||||
|
||||
if (pCmacText)
|
||||
{
|
||||
uint8_t revLocalCsrk[SEC_CMAC_KEY_LEN] = {0};
|
||||
|
||||
WStrReverseCpy(revLocalCsrk, DmSecGetLocalCsrk(), SEC_CMAC_KEY_LEN);
|
||||
WStrReverseCpy(pCmacText, (uint8_t *)pCb->msg.pPkt + L2C_PAYLOAD_START, cmacTxtLen);
|
||||
|
||||
if (SecCmac(revLocalCsrk, pCmacText, cmacTxtLen, attCb.handlerId,
|
||||
(dmConnId_t) pMsg->hdr.param, ATTC_MSG_CMAC_CMPL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
WsfBufFree(pCmacText);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocation failure */
|
||||
attcReqClear(pCcb, &pMsg->api, ATT_ERR_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (pMsg->hdr.event == ATTC_MSG_CMAC_CMPL)
|
||||
{
|
||||
secCmacMsg_t *pCmacMsg = (secCmacMsg_t*) pMsg;
|
||||
WsfBufFree(pCmacMsg->pPlainText);
|
||||
|
||||
if ((pCb = attcSignCbByConnId((dmConnId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
/* append signature to packet */
|
||||
WStrReverseCpy((uint8_t *) pCb->msg.pPkt +
|
||||
(L2C_PAYLOAD_START + pCb->msg.pPkt->len - ATT_CMAC_RESULT_LEN),
|
||||
pCmacMsg->pCiphertext, ATT_CMAC_RESULT_LEN);
|
||||
|
||||
/* if MTU request in progress or flow controlled */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_MTU || pCcb->flowDisabled)
|
||||
{
|
||||
/* put request "on deck" for processing later */
|
||||
pCcb->onDeck = pCb->msg;
|
||||
}
|
||||
/* otherwise ready to send */
|
||||
else
|
||||
{
|
||||
attcSetupReq(pCcb, &pCb->msg);
|
||||
}
|
||||
|
||||
/* we're done-- free control block */
|
||||
attcSignCbFree((dmConnId_t) pMsg->hdr.param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol signed Write Command.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param signCounter Value of the sign counter.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcSignedWriteCmd(dmConnId_t connId, uint16_t handle, uint32_t signCounter,
|
||||
uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* if connection already encrypted with security mode 1 level 2 or higher */
|
||||
if (DmConnSecLevel(connId) > DM_SEC_LEVEL_NONE)
|
||||
{
|
||||
/* use write command instead */
|
||||
AttcWriteCmd(connId, handle, valueLen, pValue);
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_SIGNED_WRITE_CMD_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_SIGNED_WRITE_CMD_LEN + valueLen;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_SIGNED_WRITE_CMD);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pValue, valueLen);
|
||||
p += valueLen;
|
||||
UINT32_TO_BSTREAM(p, signCounter);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_SIGNED_WRITE_CMD, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT client for data signing.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcSignInit(void)
|
||||
{
|
||||
/* set up callback interface */
|
||||
attcCb.pSign = &attcSignFcnIf;
|
||||
}
|
||||
Vendored
+189
@@ -0,0 +1,189 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client optional write PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Prepare Write response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcPrepWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* if no more data to send */
|
||||
if (pCcb->outReqParams.w.len == 0)
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/* adjust attribute value and its length (adjusted by ATT header length already) */
|
||||
pEvt->pValue += (ATT_PREP_WRITE_RSP_LEN - ATT_HDR_LEN);
|
||||
pEvt->valueLen -= (ATT_PREP_WRITE_RSP_LEN - ATT_HDR_LEN);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Write Command.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcWriteCmd(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_WRITE_CMD_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_WRITE_CMD_LEN + valueLen;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_CMD);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pValue, valueLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_WRITE_CMD, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Prepare Write Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param offset Write attribute data starting at this offset.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
* \param valueByRef TRUE if pValue data is accessed by reference rather than copied.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcPrepareWriteReq(dmConnId_t connId, uint16_t handle, uint16_t offset, uint16_t valueLen,
|
||||
uint8_t *pValue, bool_t valueByRef, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint16_t bufLen;
|
||||
|
||||
if (continuing && valueByRef)
|
||||
{
|
||||
bufLen = ATT_PREP_WRITE_REQ_BUF_LEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
bufLen = ATT_PREP_WRITE_REQ_BUF_LEN + valueLen;
|
||||
}
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(bufLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->w.len = valueLen;
|
||||
pPkt->w.offset = offset;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_PREP_WRITE_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* skip over offset field */
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
/* set value pointer and copy data to packet, if not valueByRef */
|
||||
if (continuing && valueByRef)
|
||||
{
|
||||
pPkt->w.pValue = pValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(p, pValue, valueLen);
|
||||
pPkt->w.pValue = p;
|
||||
}
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_PREP_WRITE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Execute Write Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param writeAll TRUE to write all queued writes, FALSE to cancel all queued writes.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcExecuteWriteReq(dmConnId_t connId, bool_t writeAll)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_EXEC_WRITE_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_EXEC_WRITE_REQ_LEN;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_EXEC_WRITE_REQ);
|
||||
UINT8_TO_BSTREAM(p, writeAll);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, 0, ATTC_MSG_API_EXEC_WRITE, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client characteristic configuration module.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "util/bstream.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t *pCccTbl[DM_CONN_MAX]; /* Pointer to descriptor value tables */
|
||||
attsCccSet_t *pSet; /* Array of CCC descriptor settings */
|
||||
attsCccCback_t cback; /* Client callback function */
|
||||
uint8_t setLen; /* Length of settings array */
|
||||
} AttsCccCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
static AttsCccCb_t attsCccCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the client callback function.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param value Attribute value of the descriptor.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsCccCback(dmConnId_t connId, uint8_t idx, uint16_t handle, uint16_t value)
|
||||
{
|
||||
attsCccEvt_t evt;
|
||||
|
||||
evt.hdr.event = ATTS_CCC_STATE_IND;
|
||||
evt.hdr.param = connId;
|
||||
evt.idx = idx;
|
||||
evt.handle = handle;
|
||||
evt.value = value;
|
||||
|
||||
(*attsCccCb.cback)(&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate the CCC table for this connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Pointer into the CCC table.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint16_t *attsCccAllocTbl(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
/* if not already allocated */
|
||||
if (attsCccCb.pCccTbl[connId - 1] == NULL)
|
||||
{
|
||||
WSF_ASSERT(attsCccCb.setLen > 0);
|
||||
|
||||
/* allocate new buffer */
|
||||
attsCccCb.pCccTbl[connId - 1] = WsfBufAlloc(attsCccCb.setLen * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
return attsCccCb.pCccTbl[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the pointer into the CCC table for this connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Pointer into the CCC table.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint16_t *attsCccGetTbl(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return attsCccCb.pCccTbl[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free the CCC table for this connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsCccFreeTbl(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
if (attsCccCb.pCccTbl[connId - 1] != NULL)
|
||||
{
|
||||
WsfBufFree(attsCccCb.pCccTbl[connId - 1]);
|
||||
attsCccCb.pCccTbl[connId - 1] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read the value of a client characteristic configuration descriptor. Note the
|
||||
* value is treated as a little endian byte array.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue The attribute value of the descriptor is copied to this pointer.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsCccReadValue(dmConnId_t connId, uint16_t handle, uint8_t *pValue)
|
||||
{
|
||||
attsCccSet_t *pSet;
|
||||
uint16_t *pTbl;
|
||||
uint8_t i;
|
||||
|
||||
/* find handle in handle array */
|
||||
for (pSet = attsCccCb.pSet, i = 0; i < attsCccCb.setLen; i++, pSet++)
|
||||
{
|
||||
if (pSet->handle == handle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if handle not found return error */
|
||||
if (i == attsCccCb.setLen)
|
||||
{
|
||||
return ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* get pointer to the table for this connection */
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
/* read value */
|
||||
UINT16_TO_BSTREAM(pValue, pTbl[i]);
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Write the value of a client characteristic configuration descriptor. Note the
|
||||
* value is treated as a little endian byte array.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue Pointer to the attribute value of the descriptor.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsCccWriteValue(dmConnId_t connId, uint16_t handle, uint8_t *pValue)
|
||||
{
|
||||
attsCccSet_t *pSet;
|
||||
uint16_t *pTbl;
|
||||
uint8_t i;
|
||||
uint16_t value;
|
||||
uint16_t prevValue;
|
||||
|
||||
/* find handle in handle array */
|
||||
for (pSet = attsCccCb.pSet, i = 0; i < attsCccCb.setLen; i++, pSet++)
|
||||
{
|
||||
if (pSet->handle == handle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if handle not found return error */
|
||||
if (i == attsCccCb.setLen)
|
||||
{
|
||||
return ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
BYTES_TO_UINT16(value, pValue);
|
||||
|
||||
/* verify value range */
|
||||
if (((value != 0) && (value != ATT_CLIENT_CFG_NOTIFY) && (value != ATT_CLIENT_CFG_INDICATE)) ||
|
||||
((value != 0) && ((value & pSet->valueRange) == 0)))
|
||||
{
|
||||
return ATT_ERR_VALUE_RANGE;
|
||||
}
|
||||
|
||||
/* get pointer to the table for this connection */
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
/* write value */
|
||||
prevValue = pTbl[i];
|
||||
pTbl[i] = value;
|
||||
|
||||
/* if value changed call callback */
|
||||
if (prevValue != value)
|
||||
{
|
||||
attsCccCback(connId, i, handle, value);
|
||||
}
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief CCC callback function executed by ATTS and CCC read or write.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param method Read or write.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue The attribute value of the descriptor is copied to this pointer.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsCccMainCback(dmConnId_t connId, uint8_t method, uint16_t handle, uint8_t *pValue)
|
||||
{
|
||||
ATT_TRACE_INFO2("attsCccMainCback connId=%d handle=%d", connId, handle);
|
||||
|
||||
if (method == ATT_METHOD_READ)
|
||||
{
|
||||
return attsCccReadValue(connId, handle, pValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return attsCccWriteValue(connId, handle, pValue);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register the utility service for managing client characteristic
|
||||
* configuration descriptors. This function is typically called once on
|
||||
* system initialization.
|
||||
*
|
||||
* \param setLen Length of settings array.
|
||||
* \param pSet Array of CCC descriptor settings.
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccRegister(uint8_t setLen, attsCccSet_t *pSet, attsCccCback_t cback)
|
||||
{
|
||||
attsCccCb.setLen = setLen;
|
||||
attsCccCb.pSet = pSet;
|
||||
attsCccCb.cback = cback;
|
||||
|
||||
attsCb.cccCback = attsCccMainCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the client characteristic configuration descriptor value table for a
|
||||
* connection. This function is typically called when a connection is established
|
||||
* or when a device is bonded.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCccTbl Pointer to the descriptor value array. The length of the array
|
||||
* must equal the value of setLen passed to AttsCccRegister().
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccInitTable(dmConnId_t connId, uint16_t *pCccTbl)
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t *pTbl;
|
||||
|
||||
ATT_TRACE_INFO1("AttsCccInitTable connId=%d", connId);
|
||||
|
||||
if ((pTbl = attsCccAllocTbl(connId)) != NULL)
|
||||
{
|
||||
/* if initializer table is passed in */
|
||||
if (pCccTbl != NULL)
|
||||
{
|
||||
/* initialize table */
|
||||
for (i = 0; i < attsCccCb.setLen; i++, pCccTbl++, pTbl++)
|
||||
{
|
||||
/* copy value */
|
||||
*pTbl = *pCccTbl;
|
||||
|
||||
/* execute callback for each nonzero entry in table */
|
||||
if (*pCccTbl != 0)
|
||||
{
|
||||
attsCccCback(connId, i, ATT_HANDLE_NONE, *pCccTbl);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* initialize table to zero */
|
||||
memset(pTbl, 0, (sizeof(uint16_t) * attsCccCb.setLen));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear and deallocate the client characteristic configuration descriptor value
|
||||
* table for a connection. This function must be called when a connection is closed.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccClearTable(dmConnId_t connId)
|
||||
{
|
||||
ATT_TRACE_INFO1("AttsCccClearTable connId=%d", connId);
|
||||
|
||||
attsCccFreeTbl(connId);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the value of a client characteristic configuration descriptor by its index.
|
||||
* If not found, return zero.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
*
|
||||
* \return Value of the descriptor.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t AttsCccGet(dmConnId_t connId, uint8_t idx)
|
||||
{
|
||||
uint16_t *pTbl;
|
||||
|
||||
WSF_ASSERT(idx < attsCccCb.setLen);
|
||||
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
/* return value from table */
|
||||
return pTbl[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the value of a client characteristic configuration descriptor by its index.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
* \param value Value of the descriptor.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccSet(dmConnId_t connId, uint8_t idx, uint16_t value)
|
||||
{
|
||||
uint16_t *pTbl;
|
||||
|
||||
WSF_ASSERT(idx < attsCccCb.setLen);
|
||||
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
pTbl[idx] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if a client characteristic configuration descriptor is enabled and if
|
||||
* the characteristic's security level has been met.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
*
|
||||
* \return Value of the descriptor if security level is met, otherwise zero.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t AttsCccEnabled(dmConnId_t connId, uint8_t idx)
|
||||
{
|
||||
WSF_ASSERT(idx < attsCccCb.setLen);
|
||||
|
||||
/* check security level */
|
||||
if (DmConnSecLevel(connId) < attsCccCb.pSet[idx].secLevel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get value */
|
||||
return AttsCccGet(connId, idx);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get number of CCC entries in table.
|
||||
*
|
||||
* \param None
|
||||
*
|
||||
* \return Number of CCC entries in table.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsGetCccTableLen(void)
|
||||
{
|
||||
return attsCccCb.setLen;
|
||||
}
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client supported features module.
|
||||
*
|
||||
* Copyright (c) 2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
#include "util/bstream.h"
|
||||
#include "svc_core.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
attsCsfCb_t attsCsfCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set status of on-going database hash update operation.
|
||||
*
|
||||
* \param isUpdating \ref TRUE is updating, otherwise \ref FALSE;
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsCsfSetHashUpdateStatus(bool_t isUpdating)
|
||||
{
|
||||
if (attsCsfCb.isHashUpdating == isUpdating)
|
||||
{
|
||||
/* Already in the current state, nothing to do. */
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Update state. */
|
||||
attsCsfCb.isHashUpdating = isUpdating;
|
||||
}
|
||||
|
||||
/* Update complete.
|
||||
* Check if clients were pending on the hash value and fulfill their requests.
|
||||
*/
|
||||
if (isUpdating == FALSE)
|
||||
{
|
||||
ATT_TRACE_INFO0("Database hash calculation complete");
|
||||
|
||||
attsCheckPendDbHashReadRsp();
|
||||
|
||||
/* Note: Clients which were pending on a Database Hash read from a Read by Type Request are not
|
||||
* transitioned to the change-aware state here. The application is expected to initiate the
|
||||
* state transition of all clients when the new hash is set. If this is not done, the
|
||||
* state of pending Clients will be out of sync, and will be corrected on the next database
|
||||
* sync.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_INFO0("Calculating database hash");
|
||||
|
||||
/* If the application, for whatever reason, previously recalculated the database hash over an
|
||||
* unchanged database and a client pended on a Read By Type Request of the database hash, then
|
||||
* that clients state may be out of step if the application did not initiate a state
|
||||
* transition. That state transition is forced here to keep handle next transition.
|
||||
*/
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
if (attsCsfCb.attsCsfTable[i].changeAwareState == ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING)
|
||||
{
|
||||
attsCsfCb.attsCsfTable[i].changeAwareState = ATTS_CLIENT_CHANGE_PENDING_AWARE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if database hash update is in progress.
|
||||
*
|
||||
* \return \ref TRUE if update in progress, \ref FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsCsfGetHashUpdateStatus(void)
|
||||
{
|
||||
return attsCsfCb.isHashUpdating;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check client awareness to database hash before sending notification or indication.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle ATT handle.
|
||||
*
|
||||
* \return \ref TRUE if client is aware, otherwise \ref FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsCsfIsClientChangeAware(dmConnId_t connId, uint16_t handle)
|
||||
{
|
||||
if ((attsCsfCb.attsCsfTable[connId - 1].csf & ATTS_CSF_ROBUST_CACHING) &&
|
||||
(attsCsfCb.attsCsfTable[connId - 1].changeAwareState == ATTS_CLIENT_CHANGE_UNAWARE) &&
|
||||
(handle != GATT_SC_HDL))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update client change-aware state based on protocol event.
|
||||
*
|
||||
* \param connId Connection handle.
|
||||
* \param opcode ATT PDU type.
|
||||
* \param pPacket Data packet from L2CAP.
|
||||
*
|
||||
* \return \ref ATT_SUCCESS if client is change-aware, else \ref ATT_ERR_DATABASE_OUT_OF_SYNC.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsCsfActClientState(uint16_t handle, uint8_t opcode, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
attsCsfRec_t *pRec;
|
||||
|
||||
/* PDU which do not operate on att handles are handled agnostically of the client's state. */
|
||||
if (opcode == ATT_PDU_MTU_REQ || opcode == ATT_PDU_VALUE_CNF)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
pRec = &attsCsfCb.attsCsfTable[handle];
|
||||
|
||||
/* If the client is change-unaware */
|
||||
if (pRec->changeAwareState == ATTS_CLIENT_CHANGE_UNAWARE)
|
||||
{
|
||||
/* If not a command */
|
||||
if ((opcode & ATT_PDU_MASK_COMMAND) == 0)
|
||||
{
|
||||
/* Note: there is no need to call back to the application here. The application only
|
||||
* needs to know when a transition to or from the change-aware state occurs.
|
||||
*/
|
||||
|
||||
/* Move client change-aware state to pending */
|
||||
pRec->changeAwareState = ATTS_CLIENT_CHANGE_PENDING_AWARE;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", handle + 1,
|
||||
ATTS_CLIENT_CHANGE_PENDING_AWARE);
|
||||
}
|
||||
|
||||
/* If this is a command or the Client has indicated Robust Caching, set an error so that
|
||||
* this command or request is not processed.
|
||||
*/
|
||||
if ((opcode & ATT_PDU_MASK_COMMAND) ||
|
||||
(pRec->csf & ATTS_CSF_ROBUST_CACHING))
|
||||
{
|
||||
/* return a database out of sync error */
|
||||
err = ATT_ERR_DATABASE_OUT_OF_SYNC;
|
||||
}
|
||||
}
|
||||
else if (pRec->changeAwareState == ATTS_CLIENT_CHANGE_PENDING_AWARE)
|
||||
{
|
||||
/* If not a command */
|
||||
if ((opcode & ATT_PDU_MASK_COMMAND) == 0)
|
||||
{
|
||||
/* Move client change-aware state to aware */
|
||||
pRec->changeAwareState = ATTS_CLIENT_CHANGE_AWARE;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", handle + 1, ATTS_CLIENT_CHANGE_AWARE);
|
||||
|
||||
/* Callback to application to store updated awareness, if bonded. */
|
||||
if (attsCsfCb.writeCback != NULL)
|
||||
{
|
||||
attsCsfCb.writeCback(handle + 1, pRec->changeAwareState, &pRec->csf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Return an error so that command is not processed. */
|
||||
err = ATT_ERR_DATABASE_OUT_OF_SYNC;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is Read by Type request */
|
||||
if (opcode == ATT_PDU_READ_TYPE_REQ)
|
||||
{
|
||||
uint16_t uuid;
|
||||
|
||||
/* Extract UUID: Skip L2C, ATT Header and 4 byte handle range */
|
||||
BYTES_TO_UINT16(uuid, (pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN + 4));
|
||||
|
||||
/* If this is a Read By Type Request of the Database Hash characteristic value */
|
||||
if (uuid == ATT_UUID_DATABASE_HASH)
|
||||
{
|
||||
err = ATT_SUCCESS;
|
||||
|
||||
/* Reading the hash during a hash update causes the new hash to be returned and counts
|
||||
* towards the peer's progression towards a change-aware state.
|
||||
*/
|
||||
if (attsCsfCb.isHashUpdating)
|
||||
{
|
||||
/* This read will not be processed until after the hash update completes, so this read
|
||||
* request shall be counted as a move from change-unaware to chang-aware pending.
|
||||
*/
|
||||
pRec->changeAwareState = ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", handle + 1,
|
||||
ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err == ATT_ERR_DATABASE_OUT_OF_SYNC)
|
||||
{
|
||||
ATT_TRACE_INFO2("ConnId %d out of sync, PDU with opcode 0x%02x ignored!", handle + 1, opcode);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update a client's state of awareness to a change in the database.
|
||||
*
|
||||
* \param connId DM connection ID. if \ref DM_CONN_ID_NONE, sets the state for all connected
|
||||
* clients.
|
||||
* \param state The state of awareness to a change, see ::attClientAwareStates.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note A callback to application is not needed as it is expected the caller (i.e. the
|
||||
* application) will have updated all persistent records prior to calling this function.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfSetClientsChangeAwarenessState(dmConnId_t connId, uint8_t state)
|
||||
{
|
||||
if (connId == DM_CONN_ID_NONE)
|
||||
{
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
if (attsCsfCb.attsCsfTable[i].changeAwareState == ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING)
|
||||
{
|
||||
attsCsfCb.attsCsfTable[i].changeAwareState = ATTS_CLIENT_CHANGE_PENDING_AWARE;
|
||||
}
|
||||
else
|
||||
{
|
||||
attsCsfCb.attsCsfTable[i].changeAwareState = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attsCsfCb.attsCsfTable[connId - 1].changeAwareState = state;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", connId, state);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the client supported features for a connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param changeAwareState The state of awareness to a change in the database.
|
||||
* \param pCsf Pointer to the client supported features value to cache. \ref NULL or
|
||||
* buffer of length \ref ATT_CSF_LEN.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfConnOpen(dmConnId_t connId, uint8_t changeAwareState, uint8_t *pCsf)
|
||||
{
|
||||
if (pCsf != NULL)
|
||||
{
|
||||
attsCsfCb.attsCsfTable[connId - 1].changeAwareState = changeAwareState;
|
||||
memcpy(&attsCsfCb.attsCsfTable[connId - 1].csf, pCsf, ATT_CSF_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note: this set client to the change-aware state. */
|
||||
memset(&attsCsfCb.attsCsfTable[connId - 1], 0, sizeof(attsCsfRec_t));
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register callback.
|
||||
*
|
||||
* \param writeCback Application callback for when features or status change.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfRegister(attsCsfWriteCback_t writeCback)
|
||||
{
|
||||
attsCsfCb.writeCback = writeCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATTS client supported features module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfInit(void)
|
||||
{
|
||||
attsCsfCb.isHashUpdating = FALSE;
|
||||
attsCsfCb.writeCback = NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief GATT write of client supported feature characteristic value.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param offset offset into csf characteristic.
|
||||
* \param valueLen length of write in bytes.
|
||||
* \param pValue Pointer to client's supported features characteristic value.
|
||||
*
|
||||
* \return \ref ATT_SUCCESS is successful, \ref ATT_ERR_VALUE_NOT_ALLOWED if any supported
|
||||
* features are flipped from 1 to 0.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsCsfWriteFeatures(dmConnId_t connId, uint16_t offset, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsCsfRec_t *pCsfRec = &attsCsfCb.attsCsfTable[connId - 1];
|
||||
|
||||
/* future parameter in case the client supported features characteristic becomes a multi-octet
|
||||
* structure.
|
||||
*/
|
||||
(void)offset;
|
||||
|
||||
if (valueLen > ATT_CSF_LEN)
|
||||
{
|
||||
return ATT_ERR_LENGTH;
|
||||
}
|
||||
|
||||
/* A client can not clear any bits it has set. */
|
||||
if ((pCsfRec->csf & *pValue) < pCsfRec->csf)
|
||||
{
|
||||
return ATT_ERR_VALUE_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
pCsfRec->csf = *pValue & ATTS_CSF_OCT0_FEATURES;
|
||||
|
||||
ATT_TRACE_INFO2("connId %d updated csf to 0x%02x", connId, pCsfRec->csf);
|
||||
|
||||
/* Callback to application to store updated features, if bonded. */
|
||||
if (attsCsfCb.writeCback != NULL)
|
||||
{
|
||||
attsCsfCb.writeCback(connId, pCsfRec->changeAwareState, &pCsfRec->csf);
|
||||
}
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get client supported feature record.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCsfOut Output parameter for client supported features buffer.
|
||||
* \param pCsfOutLen Length of output parameter buffer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfGetFeatures(dmConnId_t connId, uint8_t *pCsfOut, uint8_t pCsfOutLen)
|
||||
{
|
||||
if (pCsfOutLen <= ATT_CSF_LEN)
|
||||
{
|
||||
memcpy(pCsfOut, &attsCsfCb.attsCsfTable[connId - 1].csf, pCsfOutLen);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get client state of awareness to a change in the database.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Client's change-aware state.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsCsfGetChangeAwareState(dmConnId_t connId)
|
||||
{
|
||||
return attsCsfCb.attsCsfTable[connId - 1].changeAwareState;
|
||||
}
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Dynamic ATT services and attributes.
|
||||
*
|
||||
* Copyright (c) 2017-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configurable number of bytes in the Dynamic Service and Attribute Heap */
|
||||
#ifndef ATTS_DYN_HEAP_SIZE
|
||||
#define ATTS_DYN_HEAP_SIZE 1280
|
||||
#endif
|
||||
|
||||
/* Configurable memory byte alignment Dynamic Service and Attribute Heap */
|
||||
#ifndef ATTS_DYN_ALIGNMENT
|
||||
#define ATTS_DYN_ALIGNMENT 4
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Dynamic service and attributes control block */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t numServices;
|
||||
uint8_t *pNextBuffer;
|
||||
} attsDynCb_t;
|
||||
|
||||
/* Dynamic service group control block */
|
||||
typedef struct
|
||||
{
|
||||
attsGroup_t group;
|
||||
uint16_t currentAttr;
|
||||
} attsDynGroupCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Dynamic Service and Attribute Heap */
|
||||
static uint8_t attsDynHeap[ATTS_DYN_HEAP_SIZE];
|
||||
|
||||
/* Dynamic Service and Attribute control block */
|
||||
static attsDynCb_t attsDynCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate memory from the Dynamic Service and Attribute Heap.
|
||||
*
|
||||
* \param size Size of buffer to allocate in bytes
|
||||
*
|
||||
* \return Allocated buffer or NULL if failed to allocate a buffer.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void *attsDynAlloc(uint16_t size)
|
||||
{
|
||||
uint8_t *pMem = attsDynCb.pNextBuffer;
|
||||
|
||||
/* Verify enough space in heap for buffer */
|
||||
if (pMem + size <= attsDynHeap + ATTS_DYN_HEAP_SIZE)
|
||||
{
|
||||
#if ATTS_DYN_ALIGNMENT > 1
|
||||
/* Increase size if size not a multiple of the memory alignment */
|
||||
if (size % ATTS_DYN_ALIGNMENT)
|
||||
{
|
||||
size += ATTS_DYN_ALIGNMENT - (size % ATTS_DYN_ALIGNMENT);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set the next buffer location */
|
||||
attsDynCb.pNextBuffer += size;
|
||||
|
||||
return pMem;
|
||||
}
|
||||
|
||||
/* Out of heap */
|
||||
WSF_ASSERT(FALSE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the Dynamic ATT Service subsystem.
|
||||
*
|
||||
* \param None
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynInit()
|
||||
{
|
||||
attsDynCb.numServices = 0;
|
||||
attsDynCb.pNextBuffer = attsDynHeap;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically create an ATT Service at runtime.
|
||||
*
|
||||
* \param startHandle Starting attribute handle in the service
|
||||
* \param endHandle Last attribute handle in the service
|
||||
*
|
||||
* \return Service Handle.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *AttsDynCreateGroup(uint16_t startHandle, uint16_t endHandle)
|
||||
{
|
||||
attsDynGroupCb_t *pGroup = NULL;
|
||||
|
||||
/* Allocate memory for the service control block */
|
||||
pGroup = attsDynAlloc(sizeof(attsDynGroupCb_t));
|
||||
WSF_ASSERT(pGroup);
|
||||
|
||||
if (pGroup != NULL)
|
||||
{
|
||||
|
||||
/* Initialize the service group */
|
||||
pGroup->group.startHandle = startHandle;
|
||||
pGroup->group.endHandle = endHandle;
|
||||
pGroup->group.readCback = NULL;
|
||||
pGroup->group.writeCback = NULL;
|
||||
|
||||
/* Allocate memory for the attributes */
|
||||
pGroup->group.pAttr = attsDynAlloc(sizeof(attsAttr_t) * (endHandle - startHandle + 1));
|
||||
WSF_ASSERT(pGroup->group.pAttr);
|
||||
|
||||
pGroup->currentAttr = 0;
|
||||
attsDynCb.numServices++;
|
||||
}
|
||||
|
||||
return pGroup;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically delete an ATT Service at runtime.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynDeleteGroup(void *pSvcHandle)
|
||||
{
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
|
||||
WSF_ASSERT(attsDynCb.numServices);
|
||||
WSF_ASSERT(pGroup);
|
||||
|
||||
/* Remove the group */
|
||||
AttsRemoveGroup(pGroup->group.startHandle);
|
||||
|
||||
attsDynCb.numServices--;
|
||||
|
||||
/* If there are no more dynamic groups, initialize the subsystem to free the memory in the heap */
|
||||
if (attsDynCb.numServices == 0)
|
||||
{
|
||||
AttsDynInit();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register callback functions for a dynamic ATT Service at runtime.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
* \param readCback Read callback function.
|
||||
* \param writeCback Write callback function.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynRegister(void *pSvcHandle, attsReadCback_t readCback, attsWriteCback_t writeCback)
|
||||
{
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
|
||||
WSF_ASSERT(pGroup);
|
||||
|
||||
pGroup->group.readCback = readCback;
|
||||
pGroup->group.writeCback = writeCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically add an attribute to a dynamic ATT Services at app initialization.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
* \param pUuid Constant UUID
|
||||
* \param pValue Initial value of attribute (copied into attribute memory)
|
||||
* \param len Length of pValue in bytes
|
||||
* \param maxLen Maximum length of the attribute in bytes
|
||||
* \param settings Attribute settings
|
||||
* \param permissions Attribute permissions
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynAddAttr(void *pSvcHandle, const uint8_t *pUuid, const uint8_t *pValue, uint16_t len,
|
||||
const uint16_t maxLen, uint8_t settings, uint8_t permissions)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
uint16_t handle = pGroup->group.startHandle + pGroup->currentAttr++;
|
||||
|
||||
/* Verify inputs */
|
||||
WSF_ASSERT(handle <= pGroup->group.endHandle);
|
||||
WSF_ASSERT(pUuid);
|
||||
WSF_ASSERT(len <= maxLen);
|
||||
|
||||
pAttr = pGroup->group.pAttr + (handle - pGroup->group.startHandle);
|
||||
|
||||
|
||||
/* Allocate a buffer for the length of the attribute */
|
||||
pAttr->pLen = attsDynAlloc(sizeof(uint16_t));
|
||||
WSF_ASSERT(pAttr->pLen);
|
||||
|
||||
if (pAttr->pLen == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate a buffer for the value of the attribute */
|
||||
pAttr->pValue = attsDynAlloc(maxLen);
|
||||
WSF_ASSERT(pAttr->pValue);
|
||||
|
||||
if (pAttr->pValue == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the attribute values */
|
||||
pAttr->pUuid = pUuid;
|
||||
pAttr->maxLen = maxLen;
|
||||
pAttr->settings = settings;
|
||||
pAttr->permissions = permissions;
|
||||
|
||||
|
||||
|
||||
if (pValue)
|
||||
{
|
||||
/* Copy the initial value */
|
||||
memcpy(pAttr->pValue, pValue, len);
|
||||
*pAttr->pLen = len;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No initial value, zero value and length */
|
||||
memset(pAttr->pValue, 0, maxLen);
|
||||
*pAttr->pLen = 0;
|
||||
}
|
||||
|
||||
/* Add the service when the last attribute has been added */
|
||||
if (handle == pGroup->group.endHandle)
|
||||
{
|
||||
AttsAddGroup(&pGroup->group);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically add an attribute with a constant value to a dynamic ATT Services.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
* \param pUuid Constant UUID
|
||||
* \param pValue Pointer to constant attribute memory
|
||||
* \param len Length of pValue in bytes
|
||||
* \param settings Attribute settings
|
||||
* \param permissions Attribute permissions
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynAddAttrConst(void *pSvcHandle, const uint8_t *pUuid, const uint8_t *pValue,
|
||||
const uint16_t len, uint8_t settings, uint8_t permissions)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
uint16_t handle = pGroup->group.startHandle + pGroup->currentAttr++;
|
||||
|
||||
/* Verify inputs */
|
||||
WSF_ASSERT(handle <= pGroup->group.endHandle);
|
||||
WSF_ASSERT(pValue);
|
||||
WSF_ASSERT(pUuid);
|
||||
|
||||
pAttr = pGroup->group.pAttr + (handle - pGroup->group.startHandle);
|
||||
|
||||
/* Allocate a buffer for the length of the attribute */
|
||||
pAttr->pLen = attsDynAlloc(sizeof(uint16_t));
|
||||
WSF_ASSERT(pAttr->pLen);
|
||||
|
||||
if (pAttr->pLen == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the attribute values */
|
||||
*pAttr->pLen = len;
|
||||
pAttr->pUuid = pUuid;
|
||||
pAttr->pValue = (uint8_t *) pValue;
|
||||
pAttr->maxLen = len;
|
||||
pAttr->settings = settings;
|
||||
pAttr->permissions = permissions;
|
||||
|
||||
/* Add the service when the last attribute has been added */
|
||||
if (handle == pGroup->group.endHandle)
|
||||
{
|
||||
AttsAddGroup(&pGroup->group);
|
||||
}
|
||||
}
|
||||
+654
@@ -0,0 +1,654 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server indication and notification functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attsIndConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
static void attsIndMsgCback(attsApiMsg_t *pMsg);
|
||||
static void attsIndCtrlCback(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attFcnIf_t attsIndFcnIf =
|
||||
{
|
||||
attEmptyDataCback,
|
||||
attsIndCtrlCback,
|
||||
(attMsgHandler_t) attsIndMsgCback,
|
||||
attsIndConnCback
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
attsIndCb_t attsIndCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the ATTS connection control block connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not in use.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attsIndCcb_t *attsIndCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
if (DmConnInUse(connId))
|
||||
{
|
||||
return &attsIndCb.ccb[connId - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("atts ccb not in use: %d", connId);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if application callback is pending for indication or a given notification, or
|
||||
* the maximum number of simultaneous notifications has been reached.
|
||||
*
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param pPkt Pointer to packet.
|
||||
*
|
||||
* \return TRUE if app callback's pending or max number of simultaneous notifications reached.
|
||||
* FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t attsPendIndNtfHandle(attsIndCcb_t *pCcb, attsPktParam_t *pPkt)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint8_t pendNtfs;
|
||||
uint8_t i;
|
||||
|
||||
/* extract opcode */
|
||||
opcode = *(((uint8_t *) pPkt) + L2C_PAYLOAD_START);
|
||||
|
||||
/* if indication */
|
||||
if (opcode == ATT_PDU_VALUE_IND)
|
||||
{
|
||||
/* see if callback pending for indication */
|
||||
return (pCcb->pendIndHandle == ATT_HANDLE_NONE) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/* initialize number of notification callbacks pending */
|
||||
pendNtfs = 0;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_NTF; i++)
|
||||
{
|
||||
/* if callback pending for notification */
|
||||
if (pCcb->pendNtfHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if callback pending for this handle */
|
||||
if (pCcb->pendNtfHandle[i] == pPkt->handle)
|
||||
{
|
||||
/* callback pending for this notification */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
pendNtfs++;
|
||||
}
|
||||
}
|
||||
|
||||
/* no callback is pending for this notification but see if the maximum number of simultaneous
|
||||
notifications has been reached */
|
||||
return (pendNtfs < ATT_NUM_SIMUL_NTF) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set pending notification callback for a given handle.
|
||||
*
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param handle Notification handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSetPendNtfHandle(attsIndCcb_t *pCcb, uint16_t handle)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_NTF; i++)
|
||||
{
|
||||
/* if entry free */
|
||||
if (pCcb->pendNtfHandle[i] == ATT_HANDLE_NONE)
|
||||
{
|
||||
/* set pending notification handle */
|
||||
pCcb->pendNtfHandle[i] = handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute application callback function with confirmation event.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsExecCallback(dmConnId_t connId, uint16_t handle, uint8_t status)
|
||||
{
|
||||
attExecCallback(connId, ATTS_HANDLE_VALUE_CNF, handle, status, 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Call pending indication or/and notification(s) application callback.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndNtfCallback(dmConnId_t connId, attsIndCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if pending indication callback */
|
||||
if (pCcb->pendIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call indication callback with status */
|
||||
attsExecCallback(connId, pCcb->pendIndHandle, status);
|
||||
pCcb->pendIndHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/* if any pending notification callback */
|
||||
for (i = 0; i < ATT_NUM_SIMUL_NTF; i++)
|
||||
{
|
||||
if (pCcb->pendNtfHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call notification callback with status */
|
||||
attsExecCallback(connId, pCcb->pendNtfHandle[i], status);
|
||||
pCcb->pendNtfHandle[i] = ATT_HANDLE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up and send an attribute server indication or notification.
|
||||
*
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param connId DM connection ID.
|
||||
* \param pPkt Pointer to packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSetupMsg(attsIndCcb_t *pCcb, dmConnId_t connId, attsPktParam_t *pPkt)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint16_t handle;
|
||||
|
||||
/* extract opcode */
|
||||
opcode = *(((uint8_t *) pPkt) + L2C_PAYLOAD_START);
|
||||
|
||||
/* copy handle (it may be overwritten in pPkt) */
|
||||
handle = pPkt->handle;
|
||||
|
||||
/* send pdu */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, pPkt->len, (uint8_t *) pPkt);
|
||||
|
||||
/* if indication store handle and start timer */
|
||||
if (opcode == ATT_PDU_VALUE_IND)
|
||||
{
|
||||
pCcb->outIndHandle = pCcb->pendIndHandle = handle;
|
||||
pCcb->outIndTimer.msg.event = ATTS_MSG_IND_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outIndTimer, pAttCfg->transTimeout);
|
||||
}
|
||||
/* else if a notification and flow not disabled call callback now */
|
||||
else if (!(pCcb->pMainCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
attsExecCallback(connId, handle, ATT_SUCCESS);
|
||||
}
|
||||
/* else set pending notification callback for this handle */
|
||||
else
|
||||
{
|
||||
attsSetPendNtfHandle(pCcb, handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection callback for ATTS indications/notifications.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
attsIndCcb_t *pIndCcb;
|
||||
uint8_t status;
|
||||
|
||||
/* if connection opened */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
|
||||
}
|
||||
/* if connection closed */
|
||||
else if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* set status */
|
||||
if (pDmEvt->connClose.hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
status = pDmEvt->connClose.reason + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = pDmEvt->connClose.hdr.status + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
|
||||
/* get server control block directly */
|
||||
pIndCcb = &attsIndCb.ccb[pCcb->connId - 1];
|
||||
|
||||
/* if outstanding indication */
|
||||
if (pIndCcb->outIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* stop timer */
|
||||
WsfTimerStop(&pIndCcb->outIndTimer);
|
||||
pIndCcb->outIndHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/* call pending indication and notification callback */
|
||||
attsIndNtfCallback(pCcb->connId, pIndCcb, status);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF message handler callback for ATTS indications/notifications.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndMsgCback(attsApiMsg_t *pMsg)
|
||||
{
|
||||
attsIndCcb_t *pCcb;
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attsIndCcbByConnId((dmConnId_t) pMsg->hdr.param)) == NULL)
|
||||
{
|
||||
/* if message has a packet buffer free packet buffer */
|
||||
if (pMsg->hdr.event == ATTS_MSG_API_VALUE_IND_NTF)
|
||||
{
|
||||
WsfMsgFree(pMsg->pPkt);
|
||||
}
|
||||
|
||||
/* ignore if connection not in use */
|
||||
return;
|
||||
}
|
||||
|
||||
/* if an API message to send packet */
|
||||
if (pMsg->hdr.event == ATTS_MSG_API_VALUE_IND_NTF)
|
||||
{
|
||||
/* verify no API message already pending */
|
||||
if (attsPendIndNtfHandle(pCcb, pMsg->pPkt))
|
||||
{
|
||||
/* call callback with failure status and free packet buffer */
|
||||
attsExecCallback((dmConnId_t) pMsg->hdr.param, pMsg->pPkt->handle, ATT_ERR_OVERFLOW);
|
||||
WsfMsgFree(pMsg->pPkt);
|
||||
}
|
||||
/* otherwise ready to send; set up request */
|
||||
else
|
||||
{
|
||||
attsSetupMsg(pCcb, (dmConnId_t) pMsg->hdr.param, pMsg->pPkt);
|
||||
}
|
||||
}
|
||||
/* else if indication timeout */
|
||||
else if (pMsg->hdr.event == ATTS_MSG_IND_TIMEOUT)
|
||||
{
|
||||
/* if outstanding indication */
|
||||
if (pCcb->outIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* clear out handle */
|
||||
pCcb->outIndHandle = ATT_HANDLE_NONE;
|
||||
|
||||
/* call callback with timeout error */
|
||||
attsExecCallback((dmConnId_t) pMsg->hdr.param, pCcb->pendIndHandle, ATT_ERR_TIMEOUT);
|
||||
pCcb->pendIndHandle = ATT_HANDLE_NONE;
|
||||
pCcb->pMainCcb->control |= ATT_CCB_STATUS_TX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2CAP control callback.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
attsIndCcb_t *pCcb;
|
||||
|
||||
/* note this function is currently only called when flow is enabled */
|
||||
|
||||
/* get CCB */
|
||||
if ((pCcb = attsIndCcbByConnId((dmConnId_t) pMsg->param)) != NULL)
|
||||
{
|
||||
/* call pending indication and notification callback */
|
||||
attsIndNtfCallback((dmConnId_t) pMsg->param, pCcb, ATT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Indication or Notification.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
* \param opcode Opcode for notification or indication.
|
||||
* \param zeroCpy Whether or not to copy attribute value data into new buffer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsHandleValueIndNtf(dmConnId_t connId, uint16_t handle, uint16_t valueLen,
|
||||
uint8_t *pValue, uint8_t opcode, bool_t zeroCpy)
|
||||
{
|
||||
attsIndCcb_t *pCcb;
|
||||
uint16_t mtu;
|
||||
bool_t transTimedOut;
|
||||
bool_t pktSent = FALSE;
|
||||
|
||||
WsfTaskLock();
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attsIndCcbByConnId(connId)) != NULL)
|
||||
{
|
||||
/* get MTU size */
|
||||
mtu = pCcb->pMainCcb->mtu;
|
||||
transTimedOut = !!(pCcb->pMainCcb->control & ATT_CCB_STATUS_TX_TIMEOUT);
|
||||
}
|
||||
/* else connection not in use */
|
||||
else
|
||||
{
|
||||
/* MTU size unknown */
|
||||
mtu = 0;
|
||||
transTimedOut = FALSE;
|
||||
}
|
||||
|
||||
WsfTaskUnlock();
|
||||
|
||||
/* if MTU size known for connection */
|
||||
if (mtu > 0)
|
||||
{
|
||||
/* if no transaction's timed out */
|
||||
if (!transTimedOut)
|
||||
{
|
||||
/* Only send notifications and indications if client is aware of any database changes. */
|
||||
if (attsCsfIsClientChangeAware(connId, handle))
|
||||
{
|
||||
/* if packet length is less than or equal to negotiated MTU */
|
||||
if ((valueLen + ATT_VALUE_NTF_LEN) <= mtu)
|
||||
{
|
||||
attsApiMsg_t *pMsg;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate message buffer */
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(attsApiMsg_t))) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->hdr.event = ATTS_MSG_API_VALUE_IND_NTF;
|
||||
|
||||
if (zeroCpy)
|
||||
{
|
||||
/* use packet buffer provided */
|
||||
pMsg->pPkt = (attsPktParam_t *)(pValue - ATT_VALUE_IND_NTF_BUF_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocate packet buffer */
|
||||
pMsg->pPkt = attMsgAlloc(ATT_VALUE_IND_NTF_BUF_LEN + valueLen);
|
||||
}
|
||||
|
||||
if (pMsg->pPkt != NULL)
|
||||
{
|
||||
/* set data length and handle (ind and ntf have same header length) */
|
||||
pMsg->pPkt->len = ATT_VALUE_IND_LEN + valueLen;
|
||||
pMsg->pPkt->handle = handle;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *)pMsg->pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, opcode);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
if (!zeroCpy)
|
||||
{
|
||||
memcpy(p, pValue, valueLen);
|
||||
}
|
||||
|
||||
/* send message */
|
||||
WsfMsgSend(attCb.handlerId, pMsg);
|
||||
pktSent = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* free message buffer if packet buffer alloc failed */
|
||||
WsfMsgFree(pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* packet length exceeds MTU size */
|
||||
else
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attsExecCallback(connId, handle, ATT_ERR_MTU_EXCEEDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
/* transaction's timed out */
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attsExecCallback(connId, handle, ATT_ERR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/* if packet wasn't sent and it's a zero-copy packet */
|
||||
if (!pktSent && zeroCpy)
|
||||
{
|
||||
/* free packet buffer provided */
|
||||
AttMsgFree(pValue, opcode);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received handle value confirm packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcValueCnf(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
attsIndCcb_t *pIndCcb;
|
||||
|
||||
/* get server indication CCB */
|
||||
if ((pIndCcb = attsIndCcbByConnId(pCcb->connId)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if an outstanding indication */
|
||||
if (pIndCcb->outIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* clear outstanding indication */
|
||||
pIndCcb->outIndHandle = ATT_HANDLE_NONE;
|
||||
|
||||
/* stop indication timer */
|
||||
WsfTimerStop(&pIndCcb->outIndTimer);
|
||||
|
||||
/* call callback if flow control permits */
|
||||
if (!(pCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
attsExecCallback(pCcb->connId, pIndCcb->pendIndHandle, ATT_SUCCESS);
|
||||
pIndCcb->pendIndHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT server for indications/notifications.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsIndInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
attsIndCcb_t *pCcb;
|
||||
|
||||
/* Initialize control block CCBs */
|
||||
for (i = 0, pCcb = attsIndCb.ccb; i < DM_CONN_MAX; i++, pCcb++)
|
||||
{
|
||||
/* set pointer to main CCB */
|
||||
pCcb->pMainCcb = &attCb.ccb[i];
|
||||
|
||||
/* initialize timer */
|
||||
pCcb->outIndTimer.handlerId = attCb.handlerId;
|
||||
pCcb->outIndTimer.msg.param = i + 1; /* param stores the conn id */
|
||||
}
|
||||
|
||||
/* set up callback interface */
|
||||
attsCb.pInd = &attsIndFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Indication.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueInd(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_IND, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Notification.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueNtf(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_NTF, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Indication without copying the attribute
|
||||
* value data.
|
||||
*
|
||||
* Note: attribute value buffer 'pValue' must be allocated with AttMsgAlloc().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueIndZeroCpy(dmConnId_t connId, uint16_t handle, uint16_t valueLen,
|
||||
uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_IND, TRUE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Notification without copying the attribute
|
||||
* value data.
|
||||
*
|
||||
* Note: attribute value buffer 'pValue' must be allocated with AttMsgAlloc().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueNtfZeroCpy(dmConnId_t connId, uint16_t handle, uint16_t valueLen,
|
||||
uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_NTF, TRUE);
|
||||
}
|
||||
Vendored
+881
@@ -0,0 +1,881 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/wstr.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
#include "att_uuid.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTS control block */
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attsDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
static void attsConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
static void attsMsgCback(wsfMsgHdr_t *pMsg);
|
||||
static void attsL2cCtrlCback(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attFcnIf_t attsFcnIf =
|
||||
{
|
||||
attsDataCback,
|
||||
attsL2cCtrlCback,
|
||||
(attMsgHandler_t) attsMsgCback,
|
||||
attsConnCback
|
||||
};
|
||||
|
||||
/* Minimum PDU lengths, indexed by method */
|
||||
static const uint8_t attsMinPduLen[] =
|
||||
{
|
||||
0, /* ATT_METHOD_ERR */
|
||||
ATT_MTU_REQ_LEN, /* ATT_METHOD_MTU */
|
||||
ATT_FIND_INFO_REQ_LEN, /* ATT_METHOD_FIND_INFO */
|
||||
ATT_FIND_TYPE_REQ_LEN, /* ATT_METHOD_FIND_TYPE */
|
||||
ATT_READ_TYPE_REQ_LEN, /* ATT_METHOD_READ_TYPE */
|
||||
ATT_READ_REQ_LEN, /* ATT_METHOD_READ */
|
||||
ATT_READ_BLOB_REQ_LEN, /* ATT_METHOD_READ_BLOB */
|
||||
ATT_READ_MULT_REQ_LEN + 4, /* ATT_METHOD_READ_MULTIPLE */
|
||||
ATT_READ_GROUP_TYPE_REQ_LEN, /* ATT_METHOD_READ_GROUP_TYPE */
|
||||
ATT_WRITE_REQ_LEN, /* ATT_METHOD_WRITE */
|
||||
ATT_WRITE_CMD_LEN, /* ATT_METHOD_WRITE_CMD */
|
||||
ATT_PREP_WRITE_REQ_LEN, /* ATT_METHOD_PREPARE_WRITE */
|
||||
ATT_EXEC_WRITE_REQ_LEN, /* ATT_METHOD_EXECUTE_WRITE */
|
||||
0, /* ATT_METHOD_VALUE_NTF */
|
||||
0, /* ATT_METHOD_VALUE_IND */
|
||||
ATT_VALUE_CNF_LEN, /* ATT_METHOD_VALUE_CNF */
|
||||
ATT_SIGNED_WRITE_CMD_LEN /* ATT_METHOD_SIGNED_WRITE_CMD */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* PDU processing function lookup table, indexed by method */
|
||||
attsProcFcn_t attsProcFcnTbl[ATT_METHOD_SIGNED_WRITE_CMD+1] =
|
||||
{
|
||||
NULL, /* ATT_METHOD_ERR */
|
||||
attsProcMtuReq, /* ATT_METHOD_MTU */
|
||||
attsProcFindInfoReq, /* ATT_METHOD_FIND_INFO */
|
||||
attsProcFindTypeReq, /* ATT_METHOD_FIND_TYPE */
|
||||
attsProcReadTypeReq, /* ATT_METHOD_READ_TYPE */
|
||||
attsProcReadReq, /* ATT_METHOD_READ */
|
||||
attsProcReadBlobReq, /* ATT_METHOD_READ_BLOB */
|
||||
attsProcReadMultReq, /* ATT_METHOD_READ_MULT */
|
||||
attsProcReadGroupTypeReq, /* ATT_METHOD_READ_GROUP_TYPE */
|
||||
attsProcWrite, /* ATT_METHOD_WRITE */
|
||||
attsProcWrite, /* ATT_METHOD_WRITE_CMD */
|
||||
attsProcPrepWriteReq, /* ATT_METHOD_PREP_WRITE */
|
||||
attsProcExecWriteReq, /* ATT_METHOD_EXEC_WRITE */
|
||||
NULL, /* ATT_METHOD_VALUE_NTF */
|
||||
NULL, /* ATT_METHOD_VALUE_IND */
|
||||
attsProcValueCnf, /* ATT_METHOD_VALUE_CNF */
|
||||
NULL /* ATT_METHOD_SIGNED_WRITE_CMD */
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
attsCb_t attsCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Data callback for ATTS.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint8_t method;
|
||||
uint8_t err;
|
||||
attsProcFcn_t procFcn;
|
||||
attCcb_t *pCcb;
|
||||
uint16_t attHandle;
|
||||
|
||||
/* get connection cb for this handle */
|
||||
if ((pCcb = attCcbByHandle(handle)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse opcode */
|
||||
opcode = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* get method */
|
||||
if ((opcode <= ATT_PDU_WRITE_REQ) ||
|
||||
((opcode >= ATT_PDU_PREP_WRITE_REQ) && (opcode <= ATT_PDU_VALUE_CNF)))
|
||||
{
|
||||
method = ATT_OPCODE_2_METHOD(opcode);
|
||||
}
|
||||
else if (opcode == ATT_PDU_WRITE_CMD)
|
||||
{
|
||||
method = ATT_METHOD_WRITE_CMD;
|
||||
}
|
||||
else if (opcode == ATT_PDU_SIGNED_WRITE_CMD)
|
||||
{
|
||||
method = ATT_METHOD_SIGNED_WRITE_CMD;
|
||||
}
|
||||
else
|
||||
{
|
||||
method = ATT_METHOD_ERR;
|
||||
}
|
||||
|
||||
/* ignore packet if write response is pending. */
|
||||
if (pCcb->control & ATT_CCB_STATUS_RSP_PENDING)
|
||||
{
|
||||
if (method != ATT_METHOD_VALUE_CNF)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* check client's status to see if server is allowed to process this PDU. */
|
||||
err = attsCsfActClientState(handle, opcode, pPacket);
|
||||
if (err)
|
||||
{
|
||||
BYTES_TO_UINT16(attHandle, pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
attHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
#if defined(ATTS_ERROR_TEST) && (ATTS_ERROR_TEST == TRUE)
|
||||
if (attCb.errTest != ATT_SUCCESS)
|
||||
{
|
||||
BYTES_TO_UINT16(attHandle, pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN);
|
||||
attsErrRsp(handle, opcode, attHandle, attCb.errTest);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if no error process request */
|
||||
if (!err)
|
||||
{
|
||||
/* look up processing function */
|
||||
procFcn = attsProcFcnTbl[method];
|
||||
|
||||
/* if method is supported */
|
||||
if (procFcn != NULL)
|
||||
{
|
||||
/* verify length */
|
||||
if (len >= attsMinPduLen[method])
|
||||
{
|
||||
/* execute processing function */
|
||||
(*procFcn)(pCcb, len, pPacket);
|
||||
err = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* invalid PDU length */
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* PDU not supported */
|
||||
err = ATT_ERR_NOT_SUP;
|
||||
}
|
||||
}
|
||||
|
||||
/* if there's an error and an error response can be sent for this opcode */
|
||||
if (err && (opcode != ATT_PDU_MTU_REQ) && (opcode != ATT_PDU_VALUE_CNF) &&
|
||||
((opcode & ATT_PDU_MASK_COMMAND) == 0))
|
||||
{
|
||||
attsErrRsp(handle, opcode, attHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection callback for ATTS.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
/* if connection closed */
|
||||
if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* clear prepare write queue */
|
||||
attsClearPrepWrites(pCcb);
|
||||
|
||||
/* stop service discovery idle timer, if running */
|
||||
if (DmConnCheckIdle(pCcb->connId) & DM_IDLE_ATTS_DISC)
|
||||
{
|
||||
WsfTimerStop(&pCcb->idleTimer);
|
||||
}
|
||||
}
|
||||
|
||||
/* pass event to indication interface */
|
||||
(*attsCb.pInd->connCback)(pCcb, pDmEvt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF message handler callback for ATTS.
|
||||
*
|
||||
* \param pMsg DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsMsgCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* handle service discovery idle timeout */
|
||||
if (pMsg->event == ATTS_MSG_IDLE_TIMEOUT)
|
||||
{
|
||||
/* set channel as idle */
|
||||
DmConnSetIdle((dmConnId_t) pMsg->param, DM_IDLE_ATTS_DISC, DM_CONN_IDLE);
|
||||
}
|
||||
/* pass event to indication interface */
|
||||
else if (pMsg->event <= ATTS_MSG_IND_TIMEOUT)
|
||||
{
|
||||
(*attsCb.pInd->msgCback)(pMsg);
|
||||
}
|
||||
/* pass event to signed data interface */
|
||||
else if (pMsg->event == ATTS_MSG_SIGN_CMAC_CMPL)
|
||||
{
|
||||
(*attsCb.signMsgCback)(pMsg);
|
||||
}
|
||||
else if (pMsg->event == ATTS_MSG_DBH_CMAC_CMPL)
|
||||
{
|
||||
/* handle database hash update */
|
||||
attsProcessDatabaseHashUpdate((secCmacMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C control callback for ATTS.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsL2cCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* pass event to indication interface */
|
||||
(*attsCb.pInd->ctrlCback)(pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an error response PDU.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param opcode Opcode of the request that generated this error.
|
||||
* \param attHandle Attribute handle in request, if applicable.
|
||||
* \param reason Error reason.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsErrRsp(uint16_t handle, uint8_t opcode, uint16_t attHandle, uint8_t reason)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_ERR_RSP_LEN)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_ERR_RSP);
|
||||
UINT8_TO_BSTREAM(p, opcode);
|
||||
UINT16_TO_BSTREAM(p, attHandle);
|
||||
UINT8_TO_BSTREAM(p, reason);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, handle, ATT_ERR_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear the prepared write queue.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsClearPrepWrites(attCcb_t *pCcb)
|
||||
{
|
||||
void *pBuf;
|
||||
|
||||
while ((pBuf = WsfQueueDeq(&pCcb->prepWriteQueue)) != NULL)
|
||||
{
|
||||
WsfBufFree(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set connection idle status to busy for service discovery.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsDiscBusy(attCcb_t *pCcb)
|
||||
{
|
||||
if (pAttCfg->discIdleTimeout > 0)
|
||||
{
|
||||
/* set channel as busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_ATTS_DISC, DM_CONN_BUSY);
|
||||
|
||||
/* start service discovery idle timer */
|
||||
pCcb->idleTimer.handlerId = attCb.handlerId;
|
||||
pCcb->idleTimer.msg.event = ATTS_MSG_IDLE_TIMEOUT;
|
||||
pCcb->idleTimer.msg.param = pCcb->connId;
|
||||
WsfTimerStartSec(&pCcb->idleTimer, pAttCfg->discIdleTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process updated database hash.
|
||||
*
|
||||
* \param pMsg Event containing the new database hash.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcessDatabaseHashUpdate(secCmacMsg_t *pMsg)
|
||||
{
|
||||
attEvt_t evt;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t dbhCharHandle;
|
||||
|
||||
/* send to application */
|
||||
evt.hdr.event = ATTS_DB_HASH_CALC_CMPL_IND;
|
||||
evt.hdr.status = ATT_SUCCESS;
|
||||
evt.hdr.param = DM_CONN_ID_NONE;
|
||||
|
||||
evt.valueLen = ATT_DATABASE_HASH_LEN;
|
||||
evt.handle = ATT_HANDLE_NONE;
|
||||
evt.continuing = FALSE;
|
||||
evt.mtu = 0;
|
||||
|
||||
/* free plain text buffer */
|
||||
if (pMsg->pPlainText != NULL)
|
||||
{
|
||||
WsfBufFree(pMsg->pPlainText);
|
||||
pMsg->pPlainText = NULL;
|
||||
}
|
||||
|
||||
/* copy in little endian */
|
||||
evt.pValue = pMsg->pCiphertext;
|
||||
|
||||
/* find GATT database handle */
|
||||
dbhCharHandle = attsFindUuidInRange(ATT_HANDLE_START, ATT_HANDLE_MAX, ATT_16_UUID_LEN,
|
||||
(uint8_t *) attGattDbhChUuid, &pAttr, &pGroup);
|
||||
|
||||
if (dbhCharHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* Set hash in service. */
|
||||
AttsSetAttr(dbhCharHandle, SEC_CMAC_HASH_LEN, evt.pValue);
|
||||
}
|
||||
|
||||
/* set hash update complete */
|
||||
attsCsfSetHashUpdateStatus(FALSE);
|
||||
|
||||
attCb.cback(&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if any clients are pending on a new database hash value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsCheckPendDbHashReadRsp(void)
|
||||
{
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
attCcb_t *pCcb = &attCb.ccb[i];
|
||||
|
||||
if (pCcb->pPendDbHashRsp)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_TYPE_RSP);
|
||||
|
||||
/* set length parameter in response message */
|
||||
UINT8_TO_BSTREAM(p, ATT_DATABASE_HASH_LEN + sizeof(uint16_t));
|
||||
|
||||
/* copy result to response message */
|
||||
UINT16_TO_BSTREAM(p, pCcb->pPendDbHashRsp->handle);
|
||||
|
||||
if ((pAttr = attsFindByHandle(pCcb->pPendDbHashRsp->handle, &pGroup)) != NULL)
|
||||
{
|
||||
memcpy(p, pAttr->pValue, *pAttr->pLen);
|
||||
p += *pAttr->pLen;
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, p - (pBuf + L2C_PAYLOAD_START), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->connId, ATT_PDU_READ_TYPE_REQ, pCcb->pPendDbHashRsp->startHandle, ATT_ERR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->connId, ATT_PDU_READ_TYPE_REQ, pCcb->pPendDbHashRsp->startHandle, ATT_ERR_RESOURCES);
|
||||
}
|
||||
|
||||
/* Free pending state information. */
|
||||
WsfBufFree(pCcb->pPendDbHashRsp);
|
||||
pCcb->pPendDbHashRsp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Determine if attribute is hashable and return length of hashable data.
|
||||
*
|
||||
* \param pAttr Attribute to check for inclusion in hash.
|
||||
*
|
||||
* \return 0 if not hashable, else length of hashable data in bytes.
|
||||
*
|
||||
* \note Hashable attributes include Primary Service, Secondary Service, Included Service,
|
||||
* Characteristic Declaration and Characteristic Extended Properties which contribute their
|
||||
* ATT handle, ATT type and ATT value. Hashable attributes also include Characteristic User
|
||||
* Description, Client Characteristic Configuration, Server Characteristic Configuration,
|
||||
* Characteristic Format, and Characteristic Aggreate Format which contribute their ATT
|
||||
* handle and ATT type.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsIsHashableAttr(attsAttr_t *pAttr)
|
||||
{
|
||||
/* Initialize length to 2 for ATT Handle length */
|
||||
uint16_t length = 2;
|
||||
uint16_t uuid;
|
||||
static bool_t isAttrCharVal = FALSE;
|
||||
|
||||
/* Characteristic values are skipped */
|
||||
if (isAttrCharVal)
|
||||
{
|
||||
isAttrCharVal = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BYTES_TO_UINT16(uuid, pAttr->pUuid);
|
||||
switch (uuid)
|
||||
{
|
||||
/* Top cases include Attribute Value length */
|
||||
case ATT_UUID_CHARACTERISTIC:
|
||||
/* Set the next characteristic in database to be skipped */
|
||||
isAttrCharVal = TRUE;
|
||||
/* Fallthrough */
|
||||
case ATT_UUID_PRIMARY_SERVICE:
|
||||
case ATT_UUID_SECONDARY_SERVICE:
|
||||
case ATT_UUID_INCLUDE:
|
||||
case ATT_UUID_CHARACTERISTIC_EXT:
|
||||
length += *pAttr->pLen;
|
||||
/* Fallthrough */
|
||||
|
||||
/* All values fall through to include Attribute Type length */
|
||||
case ATT_UUID_CHAR_USER_DESC:
|
||||
case ATT_UUID_CLIENT_CHAR_CONFIG:
|
||||
case ATT_UUID_SERVER_CHAR_CONFIG:
|
||||
case ATT_UUID_AGGREGATE_FORMAT:
|
||||
if (pAttr->settings & ATTS_SET_UUID_128)
|
||||
{
|
||||
length += 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
length += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
length = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT server.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsInit(void)
|
||||
{
|
||||
/* Initialize control block */
|
||||
WSF_QUEUE_INIT(&attsCb.groupQueue);
|
||||
attsCb.pInd = &attFcnDefault;
|
||||
attsCb.signMsgCback = (attMsgHandler_t) attEmptyHandler;
|
||||
|
||||
/* set up callback interfaces */
|
||||
attCb.pServer = &attsFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register an authorization callback with the attribute server.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsAuthorRegister(attsAuthorCback_t cback)
|
||||
{
|
||||
attsCb.authorCback = cback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Create hash from the database string.
|
||||
*
|
||||
* \param pKey Key for hashing.
|
||||
* \param pMsg Plaintext to hash.
|
||||
* \param msgLen Length of Plaintext data.
|
||||
*
|
||||
* \return \ref TRUE if successful, \ref FALSE if not.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AttsHashDatabaseString(uint8_t *pKey, uint8_t *pMsg, uint16_t msgLen)
|
||||
{
|
||||
return SecCmac(pKey, pMsg, msgLen, attCb.handlerId, 0, ATTS_MSG_DBH_CMAC_CMPL);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate database hash from the GATT database.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCalculateDbHash(void)
|
||||
{
|
||||
uint16_t msgLen = 0;
|
||||
uint8_t *pMsg;
|
||||
attsGroup_t *pGroup = (attsGroup_t *) attsCb.groupQueue.pHead;
|
||||
|
||||
/* Determine length of message. */
|
||||
while (pGroup != NULL)
|
||||
{
|
||||
uint8_t numAttrs = (pGroup->endHandle - pGroup->startHandle) + 1;
|
||||
|
||||
for (attsAttr_t *pAttr = pGroup->pAttr; numAttrs != 0; numAttrs--, pAttr++)
|
||||
{
|
||||
msgLen += attsIsHashableAttr(pAttr);
|
||||
}
|
||||
|
||||
pGroup = pGroup->pNext;
|
||||
}
|
||||
|
||||
/* Allocate buffer for message. */
|
||||
if ((pMsg = WsfBufAlloc(msgLen)) != NULL)
|
||||
{
|
||||
pGroup = (attsGroup_t *)attsCb.groupQueue.pHead;
|
||||
uint8_t hashingKey[16] = { 0, };
|
||||
uint8_t *p = pMsg;
|
||||
|
||||
/* For each service in services */
|
||||
while (pGroup)
|
||||
{
|
||||
uint16_t attHandle = pGroup->startHandle;
|
||||
|
||||
/* For each attribute in the service */
|
||||
for (attsAttr_t *pAttr = pGroup->pAttr; attHandle <= pGroup->endHandle; attHandle++, pAttr++)
|
||||
{
|
||||
uint16_t valLen;
|
||||
uint8_t uuidLen = 2;
|
||||
|
||||
valLen = attsIsHashableAttr(pAttr);
|
||||
if (valLen)
|
||||
{
|
||||
/* Add handle */
|
||||
UINT16_TO_BSTREAM(p, attHandle);
|
||||
|
||||
/* Add attribute type*/
|
||||
if (pAttr->settings & ATTS_SET_UUID_128)
|
||||
{
|
||||
memcpy(p, pAttr->pUuid, 16);
|
||||
p += 16;
|
||||
uuidLen = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t uuid;
|
||||
BYTES_TO_UINT16(uuid, pAttr->pUuid);
|
||||
UINT16_TO_BSTREAM(p,uuid);
|
||||
}
|
||||
|
||||
/* Add Attribute value if required */
|
||||
if (valLen - (uuidLen + 2))
|
||||
{
|
||||
memcpy(p, pAttr->pValue, *pAttr->pLen);
|
||||
p += *pAttr->pLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pGroup = pGroup->pNext;
|
||||
}
|
||||
|
||||
/* Send to CMAC */
|
||||
if (AttsHashDatabaseString(hashingKey, pMsg, msgLen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assert on failure to initiate database hash generation. */
|
||||
WSF_ASSERT(FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add an attribute group to the attribute server.
|
||||
*
|
||||
* \param pGroup Pointer to an attribute group structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsAddGroup(attsGroup_t *pGroup)
|
||||
{
|
||||
attsGroup_t *pElem;
|
||||
attsGroup_t *pPrev = NULL;
|
||||
|
||||
/* task schedule lock */
|
||||
WsfTaskLock();
|
||||
|
||||
pElem = (attsGroup_t *) attsCb.groupQueue.pHead;
|
||||
|
||||
/* iterate over queue sorted by increasing handle value */
|
||||
while (pElem != NULL)
|
||||
{
|
||||
if (pGroup->startHandle < pElem->startHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pPrev = pElem;
|
||||
pElem = pElem->pNext;
|
||||
}
|
||||
|
||||
/* insert new group */
|
||||
WsfQueueInsert(&attsCb.groupQueue, pGroup, pPrev);
|
||||
|
||||
/* set database hash update status to true until a new hash is generated */
|
||||
attsCsfSetHashUpdateStatus(TRUE);
|
||||
|
||||
/* task schedule unlock */
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove an attribute group from the attribute server.
|
||||
*
|
||||
* \param startHandle Start handle of attribute group to be removed.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsRemoveGroup(uint16_t startHandle)
|
||||
{
|
||||
attsGroup_t *pElem;
|
||||
attsGroup_t *pPrev = NULL;
|
||||
|
||||
/* task schedule lock */
|
||||
WsfTaskLock();
|
||||
|
||||
pElem = (attsGroup_t *) attsCb.groupQueue.pHead;
|
||||
|
||||
/* find group in queue */
|
||||
while (pElem != NULL)
|
||||
{
|
||||
if (pElem->startHandle == startHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pPrev = pElem;
|
||||
pElem = pElem->pNext;
|
||||
}
|
||||
|
||||
/* if group found remove from queue */
|
||||
if (pElem != NULL)
|
||||
{
|
||||
WsfQueueRemove(&attsCb.groupQueue, pElem, pPrev);
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("AttsRemoveGroup start handle not found: 0x%04x", startHandle);
|
||||
}
|
||||
|
||||
/* set database hash update status to true until a new hash is generated */
|
||||
attsCsfSetHashUpdateStatus(TRUE);
|
||||
|
||||
/* task schedule unlock */
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set an attribute value in the attribute server.
|
||||
*
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Attribute length.
|
||||
* \param pValue Attribute value.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsSetAttr(uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
WsfTaskLock();
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify write length */
|
||||
if (valueLen > pAttr->maxLen)
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set attribute value */
|
||||
memcpy(pAttr->pValue, pValue, valueLen);
|
||||
|
||||
/* set the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = valueLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else attribute not found */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
WsfTaskUnlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get an attribute value in the attribute server. Tasks should be locked before
|
||||
* calling this function and remain locked until pLen and pValue are no longer used.
|
||||
*
|
||||
* \param handle Attribute handle.
|
||||
* \param pLen Returned attribute length pointer.
|
||||
* \param pValue Returned attribute value pointer.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful or other error code if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsGetAttr(uint16_t handle, uint16_t *pLen, uint8_t **pValue)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* set length and value pointers */
|
||||
*pLen = *(pAttr->pLen);
|
||||
*pValue = pAttr->pValue;
|
||||
}
|
||||
/* else attribute not found */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For testing purposes only.
|
||||
*
|
||||
* \param status ATT status
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsErrorTest(uint8_t status)
|
||||
{
|
||||
attCb.errTest = status;
|
||||
}
|
||||
Vendored
+174
@@ -0,0 +1,174 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATTS_MAIN_H
|
||||
#define ATTS_MAIN_H
|
||||
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "att_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTS event handler messages */
|
||||
enum
|
||||
{
|
||||
ATTS_MSG_IDLE_TIMEOUT = ATTS_MSG_START,
|
||||
ATTS_MSG_API_VALUE_IND_NTF,
|
||||
ATTS_MSG_IND_TIMEOUT,
|
||||
ATTS_MSG_SIGN_CMAC_CMPL,
|
||||
ATTS_MSG_DBH_CMAC_CMPL
|
||||
};
|
||||
|
||||
/*!
|
||||
* Data buffer format for API request messages:
|
||||
*
|
||||
* | attsPktParam_t | ATT request data |
|
||||
* | bytes 0 to 7 | bytes 8 - |
|
||||
*/
|
||||
|
||||
/* API parameters */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t handle;
|
||||
} attsPktParam_t;
|
||||
|
||||
/* verify attsPktParam_t will work in data buffer format described above */
|
||||
WSF_CT_ASSERT(sizeof(attsPktParam_t) <= L2C_PAYLOAD_START);
|
||||
|
||||
/* API message structure */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
attsPktParam_t *pPkt;
|
||||
} attsApiMsg_t;
|
||||
|
||||
/* Connection control block for indications/notifications */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t outIndTimer; /* Outstanding indication timer */
|
||||
attCcb_t *pMainCcb; /* Pointer to ATT main CCB */
|
||||
uint16_t outIndHandle; /* Waiting for confirm from peer for this indication handle */
|
||||
uint16_t pendIndHandle; /* Callback to application pending for this indication handle */
|
||||
uint16_t pendNtfHandle[ATT_NUM_SIMUL_NTF]; /* Callback to application pending for this notification handle */
|
||||
} attsIndCcb_t;
|
||||
|
||||
/* Control block for indications/notifications */
|
||||
typedef struct
|
||||
{
|
||||
attsIndCcb_t ccb[DM_CONN_MAX];
|
||||
} attsIndCb_t;
|
||||
|
||||
/* Client characteristic configuration descriptor callback type
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param method Read or write.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue Pointer to the attribute value of the descriptor.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
typedef uint8_t (*attsCccFcn_t)(dmConnId_t connId, uint8_t method, uint16_t handle, uint8_t *pValue);
|
||||
|
||||
/* Main control block of the ATTS subsystem */
|
||||
typedef struct
|
||||
{
|
||||
wsfQueue_t groupQueue; /* Queue of attribute groups */
|
||||
attFcnIf_t const *pInd; /* Indication callback interface */
|
||||
attMsgHandler_t signMsgCback; /* Signed data callback interface */
|
||||
attsAuthorCback_t authorCback; /* Authorization callback */
|
||||
attsCccFcn_t cccCback; /* CCC callback */
|
||||
} attsCb_t;
|
||||
|
||||
/* PDU processing function type */
|
||||
typedef void (*attsProcFcn_t)(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
/* CSF Control block */
|
||||
typedef struct
|
||||
{
|
||||
attsCsfRec_t attsCsfTable[DM_CONN_MAX]; /* connected clients' supported features record table. */
|
||||
attsCsfWriteCback_t writeCback; /* Write callback. */
|
||||
uint8_t isHashUpdating; /* Database hash update status. */
|
||||
} attsCsfCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* PDU processing function lookup table, indexed by method */
|
||||
extern attsProcFcn_t attsProcFcnTbl[ATT_METHOD_SIGNED_WRITE_CMD+1];
|
||||
|
||||
/* Control block for indications/notifications */
|
||||
extern attsIndCb_t attsIndCb;
|
||||
|
||||
/* Control block */
|
||||
extern attsCb_t attsCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void attsErrRsp(uint16_t handle, uint8_t opcode, uint16_t attHandle, uint8_t reason);
|
||||
void attsClearPrepWrites(attCcb_t *pCcb);
|
||||
bool_t attsUuidCmp(attsAttr_t *pAttr, uint8_t uuidLen, uint8_t *pUuid);
|
||||
bool_t attsUuid16Cmp(uint8_t *pUuid16, uint8_t uuidLen, uint8_t *pUuid);
|
||||
attsAttr_t *attsFindByHandle(uint16_t handle, attsGroup_t **pAttrGroup);
|
||||
uint16_t attsFindInRange(uint16_t startHandle, uint16_t endHandle, attsAttr_t **pAttr);
|
||||
uint16_t attsFindUuidInRange(uint16_t startHandle, uint16_t endHandle, uint8_t uuidLen,
|
||||
uint8_t *pUuid, attsAttr_t **pAttr, attsGroup_t **pAttrGroup);
|
||||
uint8_t attsPermissions(dmConnId_t connId, uint8_t permit, uint16_t handle, uint8_t permissions);
|
||||
void attsDiscBusy(attCcb_t *pCcb);
|
||||
void attsCheckPendDbHashReadRsp(void);
|
||||
void attsProcessDatabaseHashUpdate(secCmacMsg_t *pMsg);
|
||||
uint16_t attsIsHashableAttr(attsAttr_t *pAttr);
|
||||
|
||||
void attsProcMtuReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcFindInfoReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcFindTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadBlobReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadMultReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadGroupTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcWrite(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcPrepWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcExecWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcValueCnf(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
uint8_t attsCsfActClientState(uint16_t handle, uint8_t opcode, uint8_t *pPacket);
|
||||
uint8_t attsCsfIsClientChangeAware(dmConnId_t connId, uint16_t handle);
|
||||
void attsCsfSetHashUpdateStatus(bool_t isUpdating);
|
||||
uint8_t attsCsfGetHashUpdateStatus(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATTS_MAIN_H */
|
||||
Vendored
+469
@@ -0,0 +1,469 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server mandatory PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare the given attribute's UUID to the given UUID.
|
||||
*
|
||||
* \param pAttr Pointer to attribute.
|
||||
* \param uuidLen UUID length, either 2 or 16.
|
||||
* \param pUuid Pointer to UUID.
|
||||
*
|
||||
* \return TRUE of UUIDs match, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t attsUuidCmp(attsAttr_t *pAttr, uint8_t uuidLen, uint8_t *pUuid)
|
||||
{
|
||||
/* if both uuids are the same length */
|
||||
if ((((pAttr->settings & ATTS_SET_UUID_128) == 0) && (uuidLen == ATT_16_UUID_LEN)) ||
|
||||
(((pAttr->settings & ATTS_SET_UUID_128) != 0) && (uuidLen == ATT_128_UUID_LEN)))
|
||||
{
|
||||
/* simply compare the data */
|
||||
return (memcmp(pAttr->pUuid, pUuid, uuidLen) == 0);
|
||||
}
|
||||
/* else we need to convert one of the uuids */
|
||||
else if (((pAttr->settings & ATTS_SET_UUID_128) == 0) && (uuidLen == ATT_128_UUID_LEN))
|
||||
{
|
||||
return attUuidCmp16to128(pAttr->pUuid, pUuid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return attUuidCmp16to128(pUuid, pAttr->pUuid);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare the given 16 bit UUID to the given UUID.
|
||||
*
|
||||
* \param pUuid16 Pointer to the 16 bit UUID.
|
||||
* \param uuidLen UUID length, either 2 or 16.
|
||||
* \param pUuid Pointer to UUID.
|
||||
*
|
||||
* \return TRUE of UUIDs match, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t attsUuid16Cmp(uint8_t *pUuid16, uint8_t uuidLen, uint8_t *pUuid)
|
||||
{
|
||||
if (uuidLen == ATT_16_UUID_LEN)
|
||||
{
|
||||
return ((pUuid16[0] == pUuid[0]) && (pUuid16[1] == pUuid[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
return attUuidCmp16to128(pUuid16, pUuid);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find an attribute with the given handle.
|
||||
*
|
||||
* \param handle Attribute handle.
|
||||
* \param pAttrGroup Return value pointer to found attribute's group.
|
||||
*
|
||||
* \return Pointer to attribute if found, othewise NULL.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attsAttr_t *attsFindByHandle(uint16_t handle, attsGroup_t **pAttrGroup)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle within handle range of group */
|
||||
if ((handle >= pGroup->startHandle) && (handle <= pGroup->endHandle))
|
||||
{
|
||||
/* index by handle into attribute array to return attribute */
|
||||
*pAttrGroup = pGroup;
|
||||
return &pGroup->pAttr[handle - pGroup->startHandle];
|
||||
}
|
||||
}
|
||||
|
||||
/* handle not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find the first attribute within the given handle range.
|
||||
*
|
||||
* \param startHandle Starting attribute handle.
|
||||
* \param endHandle Ending attribute handle.
|
||||
* \param pAttr Return value pointer to found attribute.
|
||||
*
|
||||
* \return Attribute handle or ATT_HANDLE_NONE if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsFindInRange(uint16_t startHandle, uint16_t endHandle, attsAttr_t **pAttr)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle is less than group start handle but handle range is within group */
|
||||
if ((startHandle < pGroup->startHandle) && (endHandle >= pGroup->startHandle))
|
||||
{
|
||||
/* set start handle to first handle in group */
|
||||
startHandle = pGroup->startHandle;
|
||||
}
|
||||
|
||||
/* if start handle within handle range of group */
|
||||
if ((startHandle >= pGroup->startHandle) && (startHandle <= pGroup->endHandle))
|
||||
{
|
||||
/* index by handle into attribute array to return attribute */
|
||||
*pAttr = &pGroup->pAttr[startHandle - pGroup->startHandle];
|
||||
return startHandle;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle within range not found */
|
||||
return ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform required permission and security checks when reading or writing an attribute.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param permit Either ATTS_PERMIT_READ or ATTS_PERMIT_WRITE.
|
||||
* \param handle Attribute handle.
|
||||
* \param permissions Attribute permissions.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful or error code on failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsPermissions(dmConnId_t connId, uint8_t permit, uint16_t handle, uint8_t permissions)
|
||||
{
|
||||
uint8_t secLevel;
|
||||
|
||||
/* verify read or write permissions */
|
||||
if (!(permissions & permit))
|
||||
{
|
||||
return (permit == ATTS_PERMIT_READ) ? ATT_ERR_READ : ATT_ERR_WRITE;
|
||||
}
|
||||
|
||||
/* convert write permissions to read permissions for easier masking */
|
||||
if (permit == ATTS_PERMIT_WRITE)
|
||||
{
|
||||
permissions >>= 4;
|
||||
}
|
||||
|
||||
/* if no security requirements return quickly */
|
||||
if ((permissions & (ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_READ_AUTHORIZ | ATTS_PERMIT_READ_ENC)) == 0)
|
||||
{
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/* get security level for this connection */
|
||||
secLevel = DmConnSecLevel(connId);
|
||||
|
||||
/* check if encryption required */
|
||||
if ((permissions & ATTS_PERMIT_READ_ENC) && (secLevel == DM_SEC_LEVEL_NONE))
|
||||
{
|
||||
return ATT_ERR_AUTH;
|
||||
}
|
||||
|
||||
/* check if encryption required with authenticated key */
|
||||
if (((permissions & (ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_READ_ENC)) ==
|
||||
(ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_READ_ENC)) && (secLevel < DM_SEC_LEVEL_ENC_AUTH))
|
||||
{
|
||||
return ATT_ERR_AUTH;
|
||||
}
|
||||
|
||||
/* authorization check */
|
||||
if (permissions & ATTS_PERMIT_READ_AUTHORIZ)
|
||||
{
|
||||
if (attsCb.authorCback == NULL)
|
||||
{
|
||||
return ATT_ERR_AUTHOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*attsCb.authorCback)(connId, permit, handle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process an MTU request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcMtuReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint16_t mtu;
|
||||
uint16_t localMtu;
|
||||
uint8_t *pRsp;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* parse mtu */
|
||||
BYTES_TO_UINT16(mtu, p);
|
||||
|
||||
/* verify */
|
||||
if (mtu < ATT_DEFAULT_MTU)
|
||||
{
|
||||
mtu = ATT_DEFAULT_MTU;
|
||||
}
|
||||
|
||||
/* get desired MTU */
|
||||
localMtu = WSF_MIN(pAttCfg->mtu, (HciGetMaxRxAclLen() - L2C_HDR_LEN));
|
||||
|
||||
/* send response */
|
||||
if ((pRsp = attMsgAlloc(L2C_PAYLOAD_START + ATT_MTU_RSP_LEN)) != NULL)
|
||||
{
|
||||
p = pRsp + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_MTU_RSP);
|
||||
UINT16_TO_BSTREAM(p, localMtu);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_MTU_RSP_LEN, pRsp);
|
||||
}
|
||||
|
||||
/* set mtu for the connection */
|
||||
attSetMtu(pCcb, mtu, localMtu);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a find information request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcFindInfoReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handles */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
|
||||
/* verify handles */
|
||||
if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_INFO_RSP);
|
||||
|
||||
/* set result format */
|
||||
UINT8_TO_BSTREAM(p, ATT_FIND_HANDLE_16_UUID);
|
||||
|
||||
/* find attributes within handle range */
|
||||
handle = startHandle;
|
||||
while ((handle = attsFindInRange(handle, endHandle, &pAttr)) != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* copy handle and UUID into response buffer */
|
||||
|
||||
/* if 128 bit UUID */
|
||||
if (pAttr->settings & ATTS_SET_UUID_128)
|
||||
{
|
||||
/* if this is the first result */
|
||||
if (p == (pBuf + L2C_PAYLOAD_START + 2))
|
||||
{
|
||||
p--;
|
||||
UINT8_TO_BSTREAM(p, ATT_FIND_HANDLE_128_UUID);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pUuid, ATT_128_UUID_LEN);
|
||||
p += ATT_128_UUID_LEN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* else 16 bit UUID */
|
||||
else
|
||||
{
|
||||
/* check if result fits */
|
||||
if ((p + ATT_16_UUID_LEN + sizeof(uint16_t)) <=
|
||||
(pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
/* copy result */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT8_TO_BSTREAM(p, pAttr->pUuid[0]);
|
||||
UINT8_TO_BSTREAM(p, pAttr->pUuid[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* response buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* special case of handle at max range */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* try next handle */
|
||||
if (++handle > endHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no results found set error, free buffer */
|
||||
if (p == (pBuf + L2C_PAYLOAD_START + 2))
|
||||
{
|
||||
WsfMsgFree(pBuf);
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/* set channel as busy for service discovery */
|
||||
attsDiscBusy(pCcb);
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_FIND_INFO_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t handle;
|
||||
uint16_t readLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handle */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) == ATT_SUCCESS)
|
||||
{
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_REQ, 0, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* determine length of data to read */
|
||||
readLen = (*pAttr->pLen < (pCcb->mtu - ATT_READ_RSP_LEN)) ?
|
||||
*pAttr->pLen : (pCcb->mtu - ATT_READ_RSP_LEN);
|
||||
|
||||
/* Allocate response buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_READ_RSP_LEN + readLen)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_RSP);
|
||||
memcpy(p, pAttr->pValue, readLen);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (ATT_READ_RSP_LEN + readLen), pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else invalid handle */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+833
@@ -0,0 +1,833 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server optional read PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find the first attribute within the given handle range with a matching UUID.
|
||||
*
|
||||
* \param startHandle Starting attribute handle.
|
||||
* \param endHandle Ending attribute handle.
|
||||
* \param uuidLen UUID length, either 2 or 16.
|
||||
* \param pUUID Pointer to UUID.
|
||||
* \param pAttr Return value pointer to found attribute.
|
||||
* \param pAttrGroup Return value pointer to found attribute's group.
|
||||
*
|
||||
* \return Attribute handle or ATT_HANDLE_NONE if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsFindUuidInRange(uint16_t startHandle, uint16_t endHandle, uint8_t uuidLen,
|
||||
uint8_t *pUuid, attsAttr_t **pAttr, attsGroup_t **pAttrGroup)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle is less than group start handle but handle range is within group */
|
||||
if ((startHandle < pGroup->startHandle) && (endHandle >= pGroup->startHandle))
|
||||
{
|
||||
/* set start handle to first handle in group */
|
||||
startHandle = pGroup->startHandle;
|
||||
}
|
||||
|
||||
/* if start handle within handle range of group */
|
||||
if ((startHandle >= pGroup->startHandle) && (startHandle <= pGroup->endHandle))
|
||||
{
|
||||
/* compare uuid with each attribute in group */
|
||||
*pAttr = &pGroup->pAttr[startHandle - pGroup->startHandle];
|
||||
while ((startHandle <= pGroup->endHandle) && (startHandle <= endHandle))
|
||||
{
|
||||
/* compare uuid in attribute */
|
||||
if (attsUuidCmp(*pAttr, uuidLen, pUuid))
|
||||
{
|
||||
*pAttrGroup = pGroup;
|
||||
return startHandle;
|
||||
}
|
||||
|
||||
/* special case of max handle value */
|
||||
if (startHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
startHandle++;
|
||||
(*pAttr)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no match found */
|
||||
return ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find the handle of the last attribute in a service group.
|
||||
*
|
||||
* \param startHandle Starting attribute handle of service.
|
||||
*
|
||||
* \return Service group end handle.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsFindServiceGroupEnd(uint16_t startHandle)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
attsAttr_t *pAttr;
|
||||
uint16_t prevHandle;
|
||||
uint8_t primSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE)};
|
||||
uint8_t secSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SECONDARY_SERVICE)};
|
||||
|
||||
/* special case for max handle */
|
||||
if (startHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
return ATT_HANDLE_MAX;
|
||||
}
|
||||
|
||||
prevHandle = startHandle;
|
||||
startHandle++;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle is less than group start handle */
|
||||
if (startHandle < pGroup->startHandle)
|
||||
{
|
||||
/* set start handle to first handle in group */
|
||||
startHandle = pGroup->startHandle;
|
||||
}
|
||||
|
||||
/* if start handle within handle range of group */
|
||||
if (startHandle <= pGroup->endHandle)
|
||||
{
|
||||
/* compare uuid with each attribute in group */
|
||||
pAttr = &pGroup->pAttr[startHandle - pGroup->startHandle];
|
||||
while (startHandle <= pGroup->endHandle)
|
||||
{
|
||||
/* compare uuid in attribute to service uuids */
|
||||
if (attsUuidCmp(pAttr, ATT_16_UUID_LEN, primSvcUuid) ||
|
||||
attsUuidCmp(pAttr, ATT_16_UUID_LEN, secSvcUuid))
|
||||
{
|
||||
/* found next service; return handle of previous attribute */
|
||||
return prevHandle;
|
||||
}
|
||||
|
||||
/* special case of max handle value */
|
||||
if (startHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
return ATT_HANDLE_MAX;
|
||||
}
|
||||
|
||||
prevHandle = startHandle;
|
||||
startHandle++;
|
||||
pAttr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* next service not found; return 0xFFFF as the last handle in the database */
|
||||
return ATT_HANDLE_MAX;
|
||||
}
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read blob request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadBlobReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t handle;
|
||||
uint16_t offset;
|
||||
uint16_t readLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handle and offset */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
BSTREAM_TO_UINT16(offset, pPacket);
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err has been set; fail */
|
||||
}
|
||||
/* verify offset */
|
||||
else if (offset > *pAttr->pLen)
|
||||
{
|
||||
err = ATT_ERR_OFFSET;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_BLOB_REQ, offset, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* determine length of data to read */
|
||||
readLen = ((*pAttr->pLen - offset) < (pCcb->mtu - ATT_READ_BLOB_RSP_LEN)) ?
|
||||
(*pAttr->pLen - offset) : (pCcb->mtu - ATT_READ_BLOB_RSP_LEN);
|
||||
|
||||
/* Allocate response buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_READ_BLOB_RSP_LEN + readLen)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_BLOB_RSP);
|
||||
memcpy(p, (pAttr->pValue + offset), readLen);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (ATT_READ_BLOB_RSP_LEN + readLen), pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else invalid handle */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_BLOB_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a find by type value request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcFindTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
uint8_t *pUuid;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint16_t nextHandle;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handles and uuid; pPacket then points to the value in the request */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
pUuid = pPacket;
|
||||
pPacket += ATT_16_UUID_LEN;
|
||||
|
||||
/* set len to the value length */
|
||||
len -= ATT_FIND_TYPE_REQ_LEN;
|
||||
|
||||
/* verify handles */
|
||||
if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_TYPE_RSP);
|
||||
|
||||
/* find attributes with matching uuid within handle range */
|
||||
handle = startHandle;
|
||||
while ((handle = attsFindUuidInRange(handle, endHandle, ATT_16_UUID_LEN,
|
||||
pUuid, &pAttr, &pGroup)) != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if value and length matches */
|
||||
if ((pAttr->permissions & ATTS_PERMIT_READ) &&
|
||||
((len == 0) ||
|
||||
((len == *pAttr->pLen) && (memcmp(pPacket, pAttr->pValue, len) == 0))))
|
||||
{
|
||||
/* if uuid in request is for primary service */
|
||||
if (pUuid[0] == UINT16_TO_BYTE0(ATT_UUID_PRIMARY_SERVICE) &&
|
||||
pUuid[1] == UINT16_TO_BYTE1(ATT_UUID_PRIMARY_SERVICE))
|
||||
{
|
||||
/* next handle is service group end handle */
|
||||
nextHandle = attsFindServiceGroupEnd(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* for any other uuid next handle is same as found handle */
|
||||
nextHandle = handle;
|
||||
}
|
||||
|
||||
/* copy result into response buffer; first check if it fits */
|
||||
if ((p + (sizeof(uint16_t) * 2)) <= (pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT16_TO_BSTREAM(p, nextHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* value doesn't match; still need to set next handle */
|
||||
else
|
||||
{
|
||||
nextHandle = handle;
|
||||
}
|
||||
|
||||
/* check if handle has reached end */
|
||||
if ((nextHandle >= endHandle) || (nextHandle == ATT_HANDLE_MAX))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* try next handle */
|
||||
handle = nextHandle + 1;
|
||||
}
|
||||
|
||||
/* if no results found set error, free buffer */
|
||||
if (p == (pBuf + L2C_PAYLOAD_START + 1))
|
||||
{
|
||||
WsfMsgFree(pBuf);
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/* set channel as busy for service discovery */
|
||||
attsDiscBusy(pCcb);
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_FIND_TYPE_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read by type request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint8_t uuidLen;
|
||||
uint8_t attLen;
|
||||
uint8_t cbackErr = ATT_SUCCESS;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handles; pPacket then points to the uuid */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
|
||||
/* get and verify uuid length */
|
||||
uuidLen = len - ATT_READ_TYPE_REQ_LEN;
|
||||
if (!((uuidLen == ATT_16_UUID_LEN) || (uuidLen == ATT_128_UUID_LEN)))
|
||||
{
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
/* verify handles */
|
||||
else if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* find first attribute with matching uuid within handle range */
|
||||
handle = attsFindUuidInRange(startHandle, endHandle, uuidLen, pPacket, &pAttr, &pGroup);
|
||||
startHandle = handle;
|
||||
|
||||
if (handle == ATT_HANDLE_NONE)
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
/* check permissions */
|
||||
else if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err is set above */
|
||||
}
|
||||
/* check if read callback should be called */
|
||||
else if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_TYPE_REQ, 0, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* Check if UUID is the Database Hash Characteristic Value and the value is being
|
||||
* re-calculated
|
||||
*/
|
||||
if ((memcmp(pPacket, attGattDbhChUuid, ATT_16_UUID_LEN) == 0) && attsCsfGetHashUpdateStatus())
|
||||
{
|
||||
/* Store info and return */
|
||||
pCcb->pPendDbHashRsp = WsfBufAlloc(sizeof(attPendDbHashRsp_t));
|
||||
if (pCcb->pPendDbHashRsp)
|
||||
{
|
||||
pCcb->pPendDbHashRsp->startHandle = startHandle;
|
||||
pCcb->pPendDbHashRsp->handle = handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_TYPE_REQ, startHandle, ATT_ERR_RESOURCES);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_TYPE_RSP);
|
||||
|
||||
/* get length of this first attribute */
|
||||
attLen = (*pAttr->pLen < (pCcb->mtu - ATT_READ_TYPE_RSP_LEN - sizeof(uint16_t))) ?
|
||||
*pAttr->pLen : (pCcb->mtu - ATT_READ_TYPE_RSP_LEN - sizeof(uint16_t));
|
||||
|
||||
/* set length parameter in response message */
|
||||
UINT8_TO_BSTREAM(p, attLen + sizeof(uint16_t));
|
||||
|
||||
/* copy result to response message */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
|
||||
/* look for additional attributes */
|
||||
handle++;
|
||||
while ((handle = attsFindUuidInRange(handle, endHandle, uuidLen,
|
||||
pPacket, &pAttr, &pGroup)) != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
cbackErr = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_TYPE_REQ, 0, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
cbackErr = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
/* verify no error from read callback
|
||||
* verify length is same as first found attribute
|
||||
* verify attribute permissions
|
||||
*/
|
||||
if ((cbackErr == ATT_SUCCESS) &&
|
||||
(*pAttr->pLen == attLen) &&
|
||||
(attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions) == ATT_SUCCESS))
|
||||
{
|
||||
/* copy result into response buffer; first check if it fits */
|
||||
if ((p + attLen + sizeof(uint16_t)) <= (pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* problem with read callback, length, or permissions; send what we've got so far */
|
||||
break;
|
||||
}
|
||||
|
||||
/* special case of handle at max range */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* try next handle */
|
||||
if (++handle > endHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_TYPE_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read multiple request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadMultReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t handle = ATT_HANDLE_NONE;
|
||||
uint16_t readLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* points to end of payload */
|
||||
pEnd = pPacket + L2C_PAYLOAD_START + len;
|
||||
|
||||
/* points to first handle */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_MULT_RSP);
|
||||
|
||||
/* while there are handles remaining and there is space in response buffer */
|
||||
while (pPacket < pEnd)
|
||||
{
|
||||
/* parse handle */
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) == NULL)
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_MULT_REQ, 0, pAttr);
|
||||
if (err != ATT_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
if (err != ATT_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p < (pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
/* calculate remaining space in response buffer */
|
||||
readLen = (pBuf + pCcb->mtu + L2C_PAYLOAD_START) - p;
|
||||
|
||||
/* actual length is minimum of remaining space and attribute length */
|
||||
readLen = (*pAttr->pLen < readLen) ? *pAttr->pLen : readLen;
|
||||
|
||||
/* copy attribute to response buffer */
|
||||
memcpy(p, pAttr->pValue, readLen);
|
||||
p += readLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* discard response buffer */
|
||||
if (pBuf != NULL)
|
||||
{
|
||||
WsfMsgFree(pBuf);
|
||||
}
|
||||
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_MULT_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read group by type request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadGroupTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf = NULL;
|
||||
uint8_t *p = NULL;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint8_t uuidLen;
|
||||
uint8_t attLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
uint8_t primSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE)};
|
||||
|
||||
/* parse handles; pPacket then points to the uuid */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
|
||||
/* get and verify uuid length */
|
||||
uuidLen = len - ATT_READ_GROUP_TYPE_REQ_LEN;
|
||||
if (!((uuidLen == ATT_16_UUID_LEN) || (uuidLen == ATT_128_UUID_LEN)))
|
||||
{
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
/* verify handles */
|
||||
else if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
/* verify uuid is primary service group */
|
||||
else if (!attsUuid16Cmp(primSvcUuid, uuidLen, pPacket))
|
||||
{
|
||||
err = ATT_ERR_GROUP_TYPE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* find first attribute with matching uuid within handle range */
|
||||
handle = attsFindUuidInRange(startHandle, endHandle, uuidLen, pPacket, &pAttr, &pGroup);
|
||||
|
||||
if (handle == ATT_HANDLE_NONE)
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
/* check permissions */
|
||||
else if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
startHandle = handle; /* this handle is returned in error response */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_GROUP_TYPE_RSP);
|
||||
|
||||
/* get length of this first attribute */
|
||||
attLen = (*pAttr->pLen < (pCcb->mtu - ATT_READ_GROUP_TYPE_RSP_LEN - (2 * sizeof(uint16_t)))) ?
|
||||
*pAttr->pLen : (pCcb->mtu - ATT_READ_GROUP_TYPE_RSP_LEN - (2 * sizeof(uint16_t)));
|
||||
|
||||
/* set length parameter in response message */
|
||||
UINT8_TO_BSTREAM(p, attLen + (2 * sizeof(uint16_t)));
|
||||
|
||||
/* copy handle to response message */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* get end group handle and copy it to response message */
|
||||
handle = attsFindServiceGroupEnd(handle);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* copy the attribute value to response message */
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
|
||||
/* look for additional attributes */
|
||||
while (TRUE)
|
||||
{
|
||||
/* special case of handle at max range */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* increment to next handle */
|
||||
if (++handle > endHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* find next matching handle */
|
||||
if ((handle = attsFindUuidInRange(handle, endHandle, uuidLen,
|
||||
pPacket, &pAttr, &pGroup)) == ATT_HANDLE_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* verify length is same as first found attribute
|
||||
* verify attribute permissions
|
||||
*/
|
||||
if ((*pAttr->pLen == attLen) &&
|
||||
(attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions) == ATT_SUCCESS))
|
||||
{
|
||||
/* copy result into response buffer; first check if it fits */
|
||||
if ((p + attLen + (2 * sizeof(uint16_t))) <=
|
||||
(pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
handle = attsFindServiceGroupEnd(handle);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* problem with either length or permissions; send what we've got so far */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* set channel as busy for service discovery */
|
||||
attsDiscBusy(pCcb);
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_GROUP_TYPE_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
Vendored
+402
@@ -0,0 +1,402 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server signed PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
#include "att_sign.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Signed data buffer structure */
|
||||
typedef struct attsSignBuf_tag
|
||||
{
|
||||
struct attsSignBuf_tag *pNext; /* pointer to next in queue */
|
||||
attCcb_t *pCcb; /* ATT CCB associated with the packet */
|
||||
uint16_t handle; /* ATT handle */
|
||||
uint16_t writeLen; /* length of data to write */
|
||||
dmConnId_t connId; /* Connection ID associated with the packet */
|
||||
uint8_t packet[1]; /* packet */
|
||||
} attsSignBuf_t;
|
||||
|
||||
/* ATTS signed PDU connection control block */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t signCounter; /* sign counter for this connection */
|
||||
uint8_t *pCsrk; /* signing key for this connection */
|
||||
attsSignBuf_t *pBuf; /* current data being processed */
|
||||
bool_t authenticated; /* Indicate if the CSRK is authenticated or not */
|
||||
} attsSignCcb_t;
|
||||
|
||||
/* ATTS signed PDU control block */
|
||||
typedef struct
|
||||
{
|
||||
attsSignCcb_t ccb[DM_CONN_MAX];
|
||||
wsfQueue_t msgQueue;
|
||||
} attsSignCb_t;
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
static attsSignCb_t attsSignCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the signing connection control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attsSignCcb_t *attsSignCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return &attsSignCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start processing of data for signed write.
|
||||
*
|
||||
* \param pCcb Signed data CCB.
|
||||
* \param pBuf Signed data buffer structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSignedWriteStart(attsSignCcb_t *pCcb, attsSignBuf_t *pBuf)
|
||||
{
|
||||
if (pCcb->pCsrk != NULL)
|
||||
{
|
||||
uint16_t cmacTxtLen = pBuf->writeLen + ATT_WRITE_CMD_LEN + sizeof(uint32_t);
|
||||
uint8_t *pCmacTxt = WsfBufAlloc(cmacTxtLen);
|
||||
|
||||
pCcb->pBuf = pBuf;
|
||||
|
||||
if (pCmacTxt)
|
||||
{
|
||||
uint8_t revPeerCsrk[SEC_CMAC_KEY_LEN] = {0};
|
||||
|
||||
WStrReverseCpy(revPeerCsrk, pCcb->pCsrk, SEC_CMAC_KEY_LEN);
|
||||
WStrReverseCpy(pCmacTxt, pBuf->packet, cmacTxtLen);
|
||||
|
||||
if (SecCmac(revPeerCsrk, pCmacTxt, cmacTxtLen, attCb.handlerId,
|
||||
pBuf->connId, ATTS_MSG_SIGN_CMAC_CMPL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WsfBufFree(pCmacTxt);
|
||||
}
|
||||
}
|
||||
|
||||
/* no CSRK-- free buffer */
|
||||
WsfBufFree(pBuf);
|
||||
ATT_TRACE_WARN0("ATTS CSRK not set");
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a signed write command PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsProcSignedWrite(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
attsSignCcb_t *pSignCcb;
|
||||
attsSignBuf_t *pBuf;
|
||||
uint16_t handle;
|
||||
uint16_t writeLen;
|
||||
|
||||
/* parse handle, calculate write length */
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
writeLen = len - ATT_SIGNED_WRITE_CMD_LEN;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify signed write is permitted */
|
||||
if ((pAttr->settings & ATTS_SET_ALLOW_SIGNED) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify that csrk is present */
|
||||
if (attsSignCcbByConnId(pCcb->connId)->pCsrk == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify basic permissions */
|
||||
if ((pAttr->permissions & (ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC)) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify authentication */
|
||||
if ((pAttr->permissions & ATTS_PERMIT_WRITE_AUTH) &&
|
||||
(attsSignCcbByConnId(pCcb->connId)->authenticated == 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note: authorization not verified at this stage as it is reserved for lesc
|
||||
writes; authorization occurs latter when the write cb is called */
|
||||
|
||||
/* verify write length, fixed length */
|
||||
if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) == 0) &&
|
||||
(writeLen != pAttr->maxLen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify write length, variable length */
|
||||
if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0) &&
|
||||
(writeLen > pAttr->maxLen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate buffer to store packet and parameters */
|
||||
if ((pBuf = WsfBufAlloc(sizeof(attsSignBuf_t) - 1 + len)) != NULL)
|
||||
{
|
||||
/* initialize buffer */
|
||||
pBuf->pCcb = pCcb;
|
||||
pBuf->handle = handle;
|
||||
pBuf->writeLen = writeLen;
|
||||
pBuf->connId = pCcb->connId;
|
||||
memcpy(pBuf->packet, (pPacket + L2C_PAYLOAD_START), len);
|
||||
|
||||
/* check if a signed write is already in progress */
|
||||
pSignCcb = attsSignCcbByConnId(pCcb->connId);
|
||||
|
||||
if (pSignCcb->pBuf != NULL)
|
||||
{
|
||||
/* signed write in progress; queue packet */
|
||||
WsfQueueEnq(&attsSignCb.msgQueue, pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* start signed data processing */
|
||||
attsSignedWriteStart(pSignCcb, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Message handler callback for ATTS signed PDU processing.
|
||||
*
|
||||
* \param pMsg ATTS message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSignMsgCback(secCmacMsg_t *pMsg)
|
||||
{
|
||||
attsSignCcb_t *pCcb;
|
||||
attsSignBuf_t *pBuf;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint32_t signCounter;
|
||||
|
||||
if (pMsg->hdr.event == ATTS_MSG_SIGN_CMAC_CMPL)
|
||||
{
|
||||
uint8_t signature[ATT_CMAC_RESULT_LEN] = {0};
|
||||
|
||||
pCcb = attsSignCcbByConnId((dmConnId_t) pMsg->hdr.param);
|
||||
pBuf = pCcb->pBuf;
|
||||
|
||||
WStrReverseCpy(signature, pMsg->pCiphertext, ATT_CMAC_RESULT_LEN);
|
||||
|
||||
/* if signature ok */
|
||||
if (memcmp(signature, pBuf->packet + pBuf->writeLen + ATT_WRITE_CMD_LEN + 4, ATT_CMAC_RESULT_LEN) == 0)
|
||||
{
|
||||
/* check sign counter */
|
||||
BYTES_TO_UINT32(signCounter, (pBuf->packet + pBuf->writeLen + ATT_WRITE_CMD_LEN));
|
||||
|
||||
if (signCounter >= pCcb->signCounter)
|
||||
{
|
||||
/* update sign counter */
|
||||
pCcb->signCounter = signCounter + 1;
|
||||
|
||||
/* perform write: */
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(pBuf->handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* if write callback is desired */
|
||||
if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
/* write value via write callback */
|
||||
(*pGroup->writeCback)(pBuf->connId, pBuf->handle, ATT_PDU_SIGNED_WRITE_CMD,
|
||||
0, pBuf->writeLen, (pBuf->packet + ATT_WRITE_CMD_LEN), pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
(*attsCb.cccCback)(pBuf->connId, ATT_METHOD_WRITE, pBuf->handle,
|
||||
(pBuf->packet + ATT_WRITE_CMD_LEN));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* write attribute value */
|
||||
memcpy(pAttr->pValue, (pBuf->packet + ATT_WRITE_CMD_LEN), pBuf->writeLen);
|
||||
|
||||
/* write the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = pBuf->writeLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN0("Signed write counter failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN0("Signed write sig failed");
|
||||
}
|
||||
|
||||
/* we're done-- free parameter buffer */
|
||||
WsfBufFree(pBuf);
|
||||
|
||||
WsfBufFree(pMsg->pPlainText);
|
||||
|
||||
/* process next signed write in queue, if any */
|
||||
if ((pBuf = WsfQueueDeq(&attsSignCb.msgQueue)) != NULL)
|
||||
{
|
||||
pCcb = attsSignCcbByConnId(pBuf->connId);
|
||||
attsSignedWriteStart(pCcb, pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCcb->pBuf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT server for data signing.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsSignInit(void)
|
||||
{
|
||||
/* initialize control block */
|
||||
WSF_QUEUE_INIT(&attsSignCb.msgQueue);
|
||||
|
||||
/* set up callback interface */
|
||||
attsCb.signMsgCback = (attMsgHandler_t) attsSignMsgCback;
|
||||
attsProcFcnTbl[ATT_METHOD_SIGNED_WRITE_CMD] = attsProcSignedWrite;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the peer's data signing key on this connection. This function
|
||||
* is typically called from the ATT connection callback when the connection is
|
||||
* established. The caller is responsible for maintaining the memory that
|
||||
* contains the key.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCsrk Pointer to data signing key (CSRK).
|
||||
* \param authenticated True if CSRK is authenticated and false otherwise.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsSetCsrk(dmConnId_t connId, uint8_t *pCsrk, bool_t authenticated)
|
||||
{
|
||||
attsSignCcbByConnId(connId)->pCsrk = pCsrk;
|
||||
attsSignCcbByConnId(connId)->authenticated = authenticated;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the peer's sign counter on this connection. This function
|
||||
* is typically called from the ATT connection callback when the connection is
|
||||
* established. ATT maintains the value of the sign counter internally and
|
||||
* sets the value when a signed packet is successfully received.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param signCounter Sign counter.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsSetSignCounter(dmConnId_t connId, uint32_t signCounter)
|
||||
{
|
||||
attsSignCcbByConnId(connId)->signCounter = signCounter;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the current value peer's sign counter on this connection. This function
|
||||
* is typically called from the ATT connection callback when the connection is
|
||||
* closed so the application can store the sign counter for use on future
|
||||
* connections.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Sign counter.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t AttsGetSignCounter(dmConnId_t connId)
|
||||
{
|
||||
return attsSignCcbByConnId(connId)->signCounter;
|
||||
}
|
||||
Vendored
+463
@@ -0,0 +1,463 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server write PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Queued prepare write structure */
|
||||
typedef struct attsPrepWrite_tag
|
||||
{
|
||||
struct attsPrepWrite_tag *pNext;
|
||||
uint16_t writeLen;
|
||||
uint16_t handle;
|
||||
uint16_t offset;
|
||||
uint8_t packet[1];
|
||||
} attsPrepWrite_t;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute a queued prepared write operation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pPrep Pointer to buffer containing prepared write.
|
||||
*
|
||||
* \return ATT_SUCCESS or failure status.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsExecPrepWrite(attCcb_t *pCcb, attsPrepWrite_t *pPrep)
|
||||
{
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
p = pPrep->packet;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(pPrep->handle, &pGroup)) == NULL)
|
||||
{
|
||||
/* handle not found; only possible unless handle was removed */
|
||||
err = ATT_ERR_UNLIKELY;
|
||||
}
|
||||
/* verify write permissions (in theory could have changed) */
|
||||
else if (!(pAttr->permissions & ATTS_PERMIT_WRITE))
|
||||
{
|
||||
err = ATT_ERR_WRITE;
|
||||
}
|
||||
/* if write callback is desired */
|
||||
else if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
|
||||
/* write callback performs the write */
|
||||
err = (*pGroup->writeCback)(pCcb->connId, pPrep->handle, ATT_PDU_EXEC_WRITE_REQ,
|
||||
pPrep->offset, pPrep->writeLen, p, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_WRITE, pPrep->handle, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* perform write; parameters have already been vetted by previous procedures */
|
||||
|
||||
/* write attribute value */
|
||||
memcpy((pAttr->pValue + pPrep->offset), p, pPrep->writeLen);
|
||||
|
||||
/* write the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = pPrep->writeLen + pPrep->offset;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a write request or write command PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcWrite(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t opcode;
|
||||
uint16_t handle;
|
||||
uint16_t writeLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse opcode handle, calculate write length */
|
||||
pPacket += L2C_PAYLOAD_START;
|
||||
BSTREAM_TO_UINT8(opcode, pPacket);
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
writeLen = len - ATT_WRITE_REQ_LEN;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_WRITE,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err has been set; fail */
|
||||
}
|
||||
/* verify write length, fixed length */
|
||||
else if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) == 0) &&
|
||||
(writeLen != pAttr->maxLen))
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
/* verify write length, variable length */
|
||||
else if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0) &&
|
||||
(writeLen > pAttr->maxLen))
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if write callback is desired */
|
||||
if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
err = (*pGroup->writeCback)(pCcb->connId, handle, opcode, 0, writeLen,
|
||||
pPacket, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_WRITE, handle, pPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* write attribute value */
|
||||
memcpy(pAttr->pValue, pPacket, writeLen);
|
||||
|
||||
/* write the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = writeLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* if success and write req allocate response buffer */
|
||||
if (err == ATT_SUCCESS && opcode == ATT_PDU_WRITE_REQ)
|
||||
{
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_WRITE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_RSP);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_WRITE_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else attribute not found */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
/* send error response for write req only */
|
||||
if (err && (opcode == ATT_PDU_WRITE_REQ))
|
||||
{
|
||||
if (err == ATT_RSP_PENDING)
|
||||
{
|
||||
/* set response pending */
|
||||
pCcb->control |= ATT_CCB_STATUS_RSP_PENDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_WRITE_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a prepare write request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcPrepWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
attsPrepWrite_t *pPrep;
|
||||
uint16_t handle;
|
||||
uint16_t offset;
|
||||
uint16_t writeLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handle and offset, calculate write length */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
BSTREAM_TO_UINT16(offset, pPacket);
|
||||
writeLen = len - ATT_PREP_WRITE_REQ_LEN; /* length of value being written */
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) == NULL)
|
||||
{
|
||||
/* attribute not found */
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
/* verify permissions */
|
||||
else if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_WRITE,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err has been set; fail */
|
||||
}
|
||||
/* verify offset is allowed */
|
||||
else if ((offset != 0) && ((pAttr->settings & ATTS_SET_ALLOW_OFFSET) == 0))
|
||||
{
|
||||
err = ATT_ERR_NOT_LONG;
|
||||
}
|
||||
/* verify write length, fixed length */
|
||||
else if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) == 0) &&
|
||||
(writeLen != pAttr->maxLen))
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
/* verify prepare write queue limit not reached */
|
||||
else if (WsfQueueCount(&pCcb->prepWriteQueue) >= pAttCfg->numPrepWrites)
|
||||
{
|
||||
err = ATT_ERR_QUEUE_FULL;
|
||||
}
|
||||
/* allocate new buffer to hold prepared write */
|
||||
else if ((pPrep = WsfBufAlloc(sizeof(attsPrepWrite_t) - 1 + writeLen)) == NULL)
|
||||
{
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
else if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
err = (*pGroup->writeCback)(pCcb->connId, handle, ATT_PDU_PREP_WRITE_REQ, 0, writeLen,
|
||||
pPacket, pAttr);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* copy data to new buffer and queue it */
|
||||
pPrep->writeLen = writeLen;
|
||||
pPrep->handle = handle;
|
||||
pPrep->offset = offset;
|
||||
memcpy(pPrep->packet, pPacket, writeLen);
|
||||
WsfQueueEnq(&pCcb->prepWriteQueue, pPrep);
|
||||
|
||||
/* allocate response buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_PREP_WRITE_RSP_LEN + writeLen)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_PREP_WRITE_RSP);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT16_TO_BSTREAM(p, offset);
|
||||
memcpy(p, pPacket, writeLen);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (ATT_PREP_WRITE_RSP_LEN + writeLen), pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_PREP_WRITE_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process an execute write request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcExecWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsPrepWrite_t *pPrep;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* if cancelling all prepared writes */
|
||||
if (*pPacket == ATT_EXEC_WRITE_CANCEL)
|
||||
{
|
||||
/* free all queued buffers */
|
||||
attsClearPrepWrites(pCcb);
|
||||
}
|
||||
/* else writing all prepared writes */
|
||||
else if (*pPacket == ATT_EXEC_WRITE_ALL)
|
||||
{
|
||||
/* iterate over prepare write queue and verify offset and length */
|
||||
for (pPrep = pCcb->prepWriteQueue.pHead; pPrep != NULL; pPrep = pPrep->pNext)
|
||||
{
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(pPrep->handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify offset */
|
||||
if (pPrep->offset > pAttr->maxLen)
|
||||
{
|
||||
err = ATT_ERR_OFFSET;
|
||||
}
|
||||
/* verify write length with offset */
|
||||
else if ((pPrep->writeLen + pPrep->offset) > pAttr->maxLen)
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
/* verification failed; discard all prepared writes */
|
||||
attsClearPrepWrites(pCcb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if length and offset checks ok then write all buffers in queue */
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* for each buffer */
|
||||
while ((pPrep = WsfQueueDeq(&pCcb->prepWriteQueue)) != NULL)
|
||||
{
|
||||
/* write buffer */
|
||||
if ((err = attsExecPrepWrite(pCcb, pPrep)) != ATT_SUCCESS)
|
||||
{
|
||||
/* write failed; discard remaining prepared writes */
|
||||
attsClearPrepWrites(pCcb);
|
||||
}
|
||||
|
||||
/* free buffer */
|
||||
WsfBufFree(pPrep);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else unknown operation */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
|
||||
/* send response or error response */
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_EXEC_WRITE_REQ, 0, err);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_EXEC_WRITE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_EXEC_WRITE_RSP);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_EXEC_WRITE_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a response to a pending write request. For use with ATT_RSP_PENDING.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Status of the write request.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note When a higher layer returns ATT_RSP_PENDING to an ATT write callback indicating the
|
||||
* response status is pending, the higher layer must subsequently call this function
|
||||
* with the status of the write request.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsContinueWriteReq(dmConnId_t connId, uint16_t handle, uint8_t status)
|
||||
{
|
||||
attCcb_t *pCcb;
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
/* get connection cb for this handle */
|
||||
if ((pCcb = attCcbByConnId(connId)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* clear response pending */
|
||||
pCcb->control &= ~ATT_CCB_STATUS_RSP_PENDING;
|
||||
|
||||
if (status)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_WRITE_REQ, handle, status);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_WRITE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_RSP);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_WRITE_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+112
@@ -0,0 +1,112 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Stack configuration.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "cfg_stack.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "l2c_api.h"
|
||||
#include "att_api.h"
|
||||
#include "smp_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
HCI
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
DM
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configuration structure */
|
||||
const dmCfg_t dmCfg =
|
||||
{
|
||||
0
|
||||
};
|
||||
|
||||
/* Configuration pointer */
|
||||
dmCfg_t *pDmCfg = (dmCfg_t *) &dmCfg;
|
||||
|
||||
/**************************************************************************************************
|
||||
L2C
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configuration structure */
|
||||
const l2cCfg_t l2cCfg =
|
||||
{
|
||||
30 /* Request timeout in seconds */
|
||||
};
|
||||
|
||||
/* Configuration pointer */
|
||||
l2cCfg_t *pL2cCfg = (l2cCfg_t *) &l2cCfg;
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configuration structure */
|
||||
const attCfg_t attCfg =
|
||||
{
|
||||
15, /* ATT server service discovery connection idle timeout in seconds */
|
||||
480,//LL_MAX_DATA_LEN_ABS_MAX - 4, /* desired ATT MTU */
|
||||
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
|
||||
4 /* number of queued prepare writes supported by server */
|
||||
};
|
||||
|
||||
/* Configuration pointer */
|
||||
attCfg_t *pAttCfg = (attCfg_t *) &attCfg;
|
||||
|
||||
/**************************************************************************************************
|
||||
SMP
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configuration structure */
|
||||
const smpCfg_t smpCfg =
|
||||
{
|
||||
500, /* 'Repeated attempts' timeout in msec */
|
||||
SMP_IO_NO_IN_NO_OUT, /* I/O Capability */
|
||||
7, /* Minimum encryption key length */
|
||||
16, /* Maximum encryption key length */
|
||||
1, /* Attempts to trigger 'repeated attempts' timeout */
|
||||
0, /* Device authentication requirements */
|
||||
64000, /* Maximum repeated attempts timeout in msec */
|
||||
64000, /* Time msec before attemptExp decreases */
|
||||
2 /* Repeated attempts multiplier exponent */
|
||||
};
|
||||
|
||||
/* Configuration pointer */
|
||||
smpCfg_t *pSmpCfg = (smpCfg_t *) &smpCfg;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get Stack version number.
|
||||
*
|
||||
* \param pVersion output parameter for version number.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void StackGetVersionNumber(const char **pVersion)
|
||||
{
|
||||
*pVersion = STACK_VERSION;
|
||||
}
|
||||
Vendored
+170
@@ -0,0 +1,170 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Stack configuration.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef CFG_STACK_H
|
||||
#define CFG_STACK_H
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*! \addtogroup STACK_INIT
|
||||
* \{ */
|
||||
|
||||
/**************************************************************************************************
|
||||
STACK VERSION
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \brief Stack release version label */
|
||||
#define STACK_VERSION ((const char *)"r19.02\n")
|
||||
/*! \brief Stack release version number */
|
||||
#define STACK_VER_NUM 0x1302 /* Default value. Auto-generated by builder. */
|
||||
|
||||
/**************************************************************************************************
|
||||
HCI
|
||||
**************************************************************************************************/
|
||||
|
||||
/** \name HCI Vendor Specific targets
|
||||
*
|
||||
*/
|
||||
/**@{*/
|
||||
#define HCI_VS_GENERIC 0
|
||||
#define HCI_VS_EMM 1
|
||||
|
||||
/*! \brief Vendor specific target configuration */
|
||||
#ifndef HCI_VS_TARGET
|
||||
#define HCI_VS_TARGET HCI_VS_GENERIC
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/** \name HCI Tx Data Tailroom
|
||||
* Extra byte allocation required for LL operations (i.e. MIC) in single-chip implementation
|
||||
*/
|
||||
/**@{*/
|
||||
#ifndef HCI_TX_DATA_TAILROOM
|
||||
/*! \brief Tx data tailroom. */
|
||||
#define HCI_TX_DATA_TAILROOM 0
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**************************************************************************************************
|
||||
DM
|
||||
**************************************************************************************************/
|
||||
|
||||
/** \name DM Configuration
|
||||
* DM build-time configuration parameters
|
||||
*/
|
||||
/**@{*/
|
||||
/*! \brief Maximum number of connections */
|
||||
#ifndef DM_CONN_MAX
|
||||
#define DM_CONN_MAX 3
|
||||
#endif
|
||||
|
||||
/*! \brief Maximum number of periodic advertising synchronizations */
|
||||
#ifndef DM_SYNC_MAX
|
||||
#define DM_SYNC_MAX 1
|
||||
#endif
|
||||
|
||||
/*! \brief Number of supported advertising sets: must be set to 1 for legacy advertising */
|
||||
#ifndef DM_NUM_ADV_SETS
|
||||
#define DM_NUM_ADV_SETS 1
|
||||
#endif
|
||||
|
||||
/*! \brief Number of scanner and initiator PHYs (LE 1M, LE 2M and LE Coded): must be set to 1 for
|
||||
legacy scanner and initiator */
|
||||
#ifndef DM_NUM_PHYS
|
||||
#define DM_NUM_PHYS 1
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**************************************************************************************************
|
||||
L2C
|
||||
**************************************************************************************************/
|
||||
|
||||
/** \name L2CAP Configuration
|
||||
* L2CAP build-time configuration parameters
|
||||
*/
|
||||
/**@{*/
|
||||
/*! \brief Maximum number of connection oriented channels */
|
||||
#ifndef L2C_COC_CHAN_MAX
|
||||
#define L2C_COC_CHAN_MAX 8
|
||||
#endif
|
||||
|
||||
/*! \brief Maximum number of connection oriented channel registered clients */
|
||||
#ifndef L2C_COC_REG_MAX
|
||||
#define L2C_COC_REG_MAX 4
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT
|
||||
**************************************************************************************************/
|
||||
|
||||
/** \name ATT Configuration
|
||||
* ATT build-time configuration parameters
|
||||
*/
|
||||
/**@{*/
|
||||
/*! \brief Maximum number of simultaneous ATT write commands */
|
||||
#ifndef ATT_NUM_SIMUL_WRITE_CMD
|
||||
#define ATT_NUM_SIMUL_WRITE_CMD 1
|
||||
#endif
|
||||
|
||||
/*! \brief Maximum number of simultaneous ATT notifications */
|
||||
#ifndef ATT_NUM_SIMUL_NTF
|
||||
#define ATT_NUM_SIMUL_NTF 1
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**************************************************************************************************
|
||||
SMP
|
||||
**************************************************************************************************/
|
||||
|
||||
/** \name SMP Configuration
|
||||
* SMP build-time configuration parameters
|
||||
*/
|
||||
/**@{*/
|
||||
/*! Max number of devices in the database */
|
||||
#ifndef SMP_DB_MAX_DEVICES
|
||||
#define SMP_DB_MAX_DEVICES 3
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get Stack version number.
|
||||
*
|
||||
* \param pVersion output parameter for version number.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void StackGetVersionNumber(const char **pVersion);
|
||||
|
||||
/*! \} */ /* STACK_INIT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* CFG_STACK_H */
|
||||
+523
@@ -0,0 +1,523 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager advertising module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_adv.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* control block */
|
||||
dmAdvCb_t dmAdvCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the advertising CB for a given handle.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvCbInit(uint8_t advHandle)
|
||||
{
|
||||
/* initialize control block */
|
||||
dmAdvCb.advType[advHandle] = DM_ADV_NONE;
|
||||
dmAdvCb.intervalMin[advHandle] = DM_GAP_ADV_SLOW_INT_MIN;
|
||||
dmAdvCb.intervalMax[advHandle] = DM_GAP_ADV_SLOW_INT_MAX;
|
||||
dmAdvCb.channelMap[advHandle] = DM_ADV_CHAN_ALL;
|
||||
dmCb.advFiltPolicy[advHandle] = HCI_ADV_FILT_NONE;
|
||||
dmAdvCb.advState[advHandle] = DM_ADV_STATE_IDLE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the legacy adv module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* initialize control block */
|
||||
for (i = 0; i < DM_NUM_ADV_SETS; i++)
|
||||
{
|
||||
dmAdvCbInit(i);
|
||||
}
|
||||
|
||||
dmAdvCb.advTimer.handlerId = dmCb.handlerId;
|
||||
dmCb.advAddrType = DM_ADDR_PUBLIC;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate an enhanced connection complete event.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param status Status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvGenConnCmpl(uint8_t advHandle, uint8_t status)
|
||||
{
|
||||
hciLeConnCmplEvt_t leConnCmpl;
|
||||
|
||||
/* generate enhanced connection complete event */
|
||||
memset(&leConnCmpl, 0, sizeof(leConnCmpl));
|
||||
|
||||
leConnCmpl.hdr.event = HCI_LE_ENHANCED_CONN_CMPL_CBACK_EVT;
|
||||
leConnCmpl.hdr.status = leConnCmpl.status = status;
|
||||
leConnCmpl.role = DM_ROLE_SLAVE;
|
||||
leConnCmpl.addrType = dmAdvCb.peerAddrType[advHandle];
|
||||
BdaCpy(leConnCmpl.peerAddr, dmAdvCb.peerAddr[advHandle]);
|
||||
|
||||
/* pass connection complete event to DM connection management module */
|
||||
dmDevPassHciEvtToConn((hciEvt_t *) &leConnCmpl);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the advertising parameters using the given advertising type, and peer address.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param advType Advertising type.
|
||||
* \param peerAddrType Peer address type.
|
||||
* \param pPeerAddr Peer address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvConfig(uint8_t advHandle, uint8_t advType, uint8_t peerAddrType, uint8_t *pPeerAddr)
|
||||
{
|
||||
dmAdvApiConfig_t *pMsg;
|
||||
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiConfig_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_CONFIG;
|
||||
pMsg->advType = advType;
|
||||
pMsg->advHandle = advHandle;
|
||||
pMsg->peerAddrType = peerAddrType;
|
||||
BdaCpy(pMsg->peerAddr, pPeerAddr);
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the advertising or scan response data to the given data.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param op Data operation.
|
||||
* \param location Data location.
|
||||
* \param len Length of the data. Maximum length is 236 bytes.
|
||||
* \param pData Pointer to the data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvSetData(uint8_t advHandle, uint8_t op, uint8_t location, uint8_t len, uint8_t *pData)
|
||||
{
|
||||
dmAdvApiSetData_t *pMsg;
|
||||
|
||||
WSF_ASSERT((location == DM_DATA_LOC_SCAN) || (location == DM_DATA_LOC_ADV));
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiSetData_t) + len)) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_SET_DATA;
|
||||
pMsg->advHandle = advHandle;
|
||||
pMsg->op = op;
|
||||
pMsg->location = location;
|
||||
pMsg->len = len;
|
||||
memcpy(pMsg->pData, pData, len);
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start advertising using the given advertising set and duration.
|
||||
*
|
||||
* \param numSets Number of advertising sets to enable.
|
||||
* \param pAdvHandles Advertising handles array.
|
||||
* \param pDuration Advertising duration (in milliseconds) array.
|
||||
* \param pMaxEaEvents Maximum number of extended advertising events array.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvStart(uint8_t numSets, uint8_t *pAdvHandles, uint16_t *pDuration, uint8_t *pMaxEaEvents)
|
||||
{
|
||||
uint8_t i;
|
||||
dmAdvApiStart_t *pMsg;
|
||||
|
||||
WSF_ASSERT(numSets <= DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiStart_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_START;
|
||||
pMsg->numSets = numSets;
|
||||
|
||||
for (i = 0; i < numSets; i++)
|
||||
{
|
||||
pMsg->advHandle[i] = pAdvHandles[i];
|
||||
pMsg->duration[i] = pDuration[i];
|
||||
pMsg->maxEaEvents[i] = pMaxEaEvents[i];
|
||||
}
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop advertising for the given advertising set. If the number of sets is set to 0
|
||||
* then all advertising sets are disabled.
|
||||
*
|
||||
* \param numSets Number of advertising sets to disable.
|
||||
* \param pAdvHandles Advertising handles array.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvStop(uint8_t numSets, uint8_t *pAdvHandles)
|
||||
{
|
||||
uint8_t i;
|
||||
dmAdvApiStop_t *pMsg;
|
||||
|
||||
WSF_ASSERT(numSets <= DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiStop_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_STOP;
|
||||
pMsg->numSets = numSets;
|
||||
|
||||
for (i = 0; i < numSets; i++)
|
||||
{
|
||||
pMsg->advHandle[i] = pAdvHandles[i];
|
||||
}
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove an advertising set.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvRemoveAdvSet(uint8_t advHandle)
|
||||
{
|
||||
dmAdvApiRemove_t *pMsg;
|
||||
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiRemove_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_REMOVE;
|
||||
pMsg->advHandle = advHandle;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear advertising sets.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvClearAdvSets(void)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->event = DM_ADV_MSG_API_CLEAR;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the random device address for a given advertising set.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param pAddr Random device address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvSetRandAddr(uint8_t advHandle, const uint8_t *pAddr)
|
||||
{
|
||||
dmAdvApiSetRandAddr_t *pMsg;
|
||||
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiSetRandAddr_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_SET_RAND_ADDR;
|
||||
pMsg->advHandle = advHandle;
|
||||
BdaCpy(pMsg->addr, pAddr);
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the minimum and maximum advertising intervals.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param intervalMin Minimum advertising interval.
|
||||
* \param intervalMax Maximum advertising interval.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvSetInterval(uint8_t advHandle, uint16_t intervalMin, uint16_t intervalMax)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
WsfTaskLock();
|
||||
dmAdvCb.intervalMin[advHandle] = intervalMin;
|
||||
dmAdvCb.intervalMax[advHandle] = intervalMax;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Include or exclude certain channels from the advertising channel map.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param channelMap Advertising channel map.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvSetChannelMap(uint8_t advHandle, uint8_t channelMap)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
WsfTaskLock();
|
||||
dmAdvCb.channelMap[advHandle] = channelMap;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the local address type used while advertising. This function can be used to
|
||||
* configure advertising to use a random address.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvSetAddrType(uint8_t addrType)
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmCb.advAddrType = addrType;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the value of an advertising data element in the given advertising or
|
||||
* scan response data. If the element already exists in the data then it is replaced
|
||||
* with the new value. If the element does not exist in the data it is appended
|
||||
* to it, space permitting.
|
||||
*
|
||||
* \param adType Advertising data element type.
|
||||
* \param len Length of the value. Maximum length is 29 bytes.
|
||||
* \param pValue Pointer to the value.
|
||||
* \param pAdvDataLen Advertising or scan response data length. The new length is returned
|
||||
* in this parameter.
|
||||
* \param pAdvData Pointer to advertising or scan response data.
|
||||
* \param advDataBufLen Length of the advertising or scan response data buffer maintained by
|
||||
* Application.
|
||||
*
|
||||
* \return TRUE if the element was successfully added to the data, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmAdvSetAdValue(uint8_t adType, uint8_t len, uint8_t *pValue, uint16_t *pAdvDataLen,
|
||||
uint8_t *pAdvData, uint16_t advDataBufLen)
|
||||
{
|
||||
uint8_t *pElem;
|
||||
uint8_t *pNext;
|
||||
uint16_t totalLen;
|
||||
uint16_t newAdvDataLen;
|
||||
bool_t valueSet = FALSE;
|
||||
|
||||
/* find ad type in data */
|
||||
if ((pElem = DmFindAdType(adType, *pAdvDataLen, pAdvData)) != NULL)
|
||||
{
|
||||
/* if new length equals existing length */
|
||||
if ((len + 1) == pElem[DM_AD_LEN_IDX])
|
||||
{
|
||||
/* copy new ad value to data in existing location */
|
||||
memcpy(&pElem[DM_AD_DATA_IDX], pValue, len);
|
||||
valueSet = TRUE;
|
||||
}
|
||||
/* else if new value can replace old value and still fit */
|
||||
else
|
||||
{
|
||||
/* calculate the advertising data length if old element was replaced with new */
|
||||
newAdvDataLen = *pAdvDataLen + len + 1 - pElem[DM_AD_LEN_IDX];
|
||||
|
||||
/* if length is ok */
|
||||
if (newAdvDataLen <= advDataBufLen)
|
||||
{
|
||||
/* delete item (then we will replace it) */
|
||||
|
||||
/* get the start of element that follows the element to delete */
|
||||
totalLen = pElem[DM_AD_LEN_IDX] + 1;
|
||||
pNext = pElem + totalLen;
|
||||
|
||||
/* move data from start of next element to start of current item;
|
||||
* length is equal the data that remains after pNext
|
||||
*/
|
||||
memmove(pElem, pNext, *pAdvDataLen - (uint8_t)(pNext - pAdvData));
|
||||
|
||||
/* update length */
|
||||
*pAdvDataLen = *pAdvDataLen - totalLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if value not set */
|
||||
if (!valueSet)
|
||||
{
|
||||
/* if new value fits */
|
||||
if ((*pAdvDataLen + len + 2) <= advDataBufLen)
|
||||
{
|
||||
/* construct AD item in advertising data */
|
||||
pElem = &pAdvData[*pAdvDataLen];
|
||||
*pElem++ = len + 1;
|
||||
*pElem++ = adType;
|
||||
memcpy(pElem, pValue, len);
|
||||
|
||||
/* update length */
|
||||
*pAdvDataLen = *pAdvDataLen + len + 2;
|
||||
|
||||
valueSet = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return valueSet;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the device name in the given advertising or scan response data. If the
|
||||
* device can only fit in the data if it is shortened, the name is shortened
|
||||
* and the AD type is changed to DM_ADV_TYPE_SHORT_NAME.
|
||||
*
|
||||
* \param len Length of the name. Maximum length is 29 bytes.
|
||||
* \param pValue Pointer to the name in UTF-8 format.
|
||||
* \param pAdvDataLen Advertising or scan response data length. The new length is returned
|
||||
* in this parameter.
|
||||
* \param pAdvData Pointer to advertising or scan response data.
|
||||
* \param advDataBufLen Length of the advertising or scan response data buffer maintained by
|
||||
* Application.
|
||||
*
|
||||
* \return TRUE if the element was successfully added to the data, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmAdvSetName(uint8_t len, uint8_t *pValue, uint16_t *pAdvDataLen, uint8_t *pAdvData,
|
||||
uint16_t advDataBufLen)
|
||||
{
|
||||
uint8_t *pElem;
|
||||
uint8_t *pNext;
|
||||
uint16_t totalLen;
|
||||
uint8_t adType;
|
||||
|
||||
/* find name in data */
|
||||
if ((pElem = DmFindAdType(DM_ADV_TYPE_LOCAL_NAME, *pAdvDataLen, pAdvData)) == NULL)
|
||||
{
|
||||
pElem = DmFindAdType(DM_ADV_TYPE_SHORT_NAME, *pAdvDataLen, pAdvData);
|
||||
}
|
||||
|
||||
/* if found delete it */
|
||||
if (pElem != NULL)
|
||||
{
|
||||
/* get the start of element that follows the element to delete */
|
||||
totalLen = pElem[DM_AD_LEN_IDX] + 1;
|
||||
pNext = pElem + totalLen;
|
||||
|
||||
/* move data from start of next element to start of current item;
|
||||
* length is equal the data that remains after pNext
|
||||
*/
|
||||
memmove(pElem, pNext, *pAdvDataLen - (uint8_t)(pNext - pAdvData));
|
||||
|
||||
/* update length */
|
||||
*pAdvDataLen = *pAdvDataLen - totalLen;
|
||||
}
|
||||
|
||||
/* if name will fit */
|
||||
if (*pAdvDataLen <= (advDataBufLen - 2))
|
||||
{
|
||||
/* if full device name won't fit */
|
||||
if ((*pAdvDataLen + len + 2) > advDataBufLen)
|
||||
{
|
||||
/* adjust length so that it will fit */
|
||||
len = (advDataBufLen - 2) - *pAdvDataLen;
|
||||
|
||||
/* set ad type to shortened local name */
|
||||
adType = DM_ADV_TYPE_SHORT_NAME;
|
||||
}
|
||||
else
|
||||
{
|
||||
adType = DM_ADV_TYPE_LOCAL_NAME;
|
||||
}
|
||||
|
||||
/* construct AD item in advertising data */
|
||||
pElem = &pAdvData[*pAdvDataLen];
|
||||
*pElem++ = len + 1;
|
||||
*pElem++ = adType;
|
||||
memcpy(pElem, pValue, len);
|
||||
|
||||
/* update length */
|
||||
*pAdvDataLen = *pAdvDataLen + len + 2;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM advertising module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_ADV_H
|
||||
#define DM_ADV_H
|
||||
|
||||
#include "wsf_timer.h"
|
||||
#include "sec_api.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM adv event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_ADV_MSG_API_CONFIG = DM_MSG_START(DM_ID_ADV),
|
||||
DM_ADV_MSG_API_SET_DATA,
|
||||
DM_ADV_MSG_API_START,
|
||||
DM_ADV_MSG_API_STOP,
|
||||
DM_ADV_MSG_API_REMOVE,
|
||||
DM_ADV_MSG_API_CLEAR,
|
||||
DM_ADV_MSG_API_SET_RAND_ADDR,
|
||||
DM_ADV_MSG_TIMEOUT
|
||||
};
|
||||
|
||||
/* DM adv periodic event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_ADV_PER_MSG_API_CONFIG = DM_MSG_START(DM_ID_ADV_PER),
|
||||
DM_ADV_PER_MSG_API_SET_DATA,
|
||||
DM_ADV_PER_MSG_API_START,
|
||||
DM_ADV_PER_MSG_API_STOP,
|
||||
};
|
||||
|
||||
/* DM advertising states */
|
||||
enum
|
||||
{
|
||||
DM_ADV_STATE_IDLE, /* idle */
|
||||
DM_ADV_STATE_ADVERTISING, /* advertising */
|
||||
DM_ADV_STATE_STARTING_DIRECTED, /* starting high duty cycle directed advertising */
|
||||
DM_ADV_STATE_STARTING, /* starting undirected or low duty cycle directed advertising */
|
||||
DM_ADV_STATE_STOPPING_DIRECTED, /* stopping high duty cycle directed advertising */
|
||||
DM_ADV_STATE_STOPPING, /* stopping undirected or low duty cycle directed advertising */
|
||||
DM_ADV_STATE_REMOVING_SET, /* removing advertising set */
|
||||
DM_ADV_STATE_CLEARING_SETS /* clearing all advertising sets */
|
||||
};
|
||||
|
||||
/* DM periodic advertising states */
|
||||
enum
|
||||
{
|
||||
DM_ADV_PER_STATE_IDLE,
|
||||
DM_ADV_PER_STATE_ADVERTISING,
|
||||
DM_ADV_PER_STATE_STARTING,
|
||||
DM_ADV_PER_STATE_STOPPING
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_CONFIG */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
uint8_t advType;
|
||||
uint8_t peerAddrType;
|
||||
bdAddr_t peerAddr;
|
||||
bool_t scanReqNotifEna;
|
||||
} dmAdvApiConfig_t;
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_SET_DATA */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
uint8_t op;
|
||||
uint8_t location;
|
||||
uint8_t len;
|
||||
uint8_t pData[];
|
||||
} dmAdvApiSetData_t;
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t numSets;
|
||||
uint8_t advHandle[DM_NUM_ADV_SETS];
|
||||
uint16_t duration[DM_NUM_ADV_SETS];
|
||||
uint8_t maxEaEvents[DM_NUM_ADV_SETS];
|
||||
} dmAdvApiStart_t;
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_STOP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t numSets;
|
||||
uint8_t advHandle[DM_NUM_ADV_SETS];
|
||||
} dmAdvApiStop_t;
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_REMOVE */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
} dmAdvApiRemove_t;
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_SET_RAND_ADDR */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
bdAddr_t addr;
|
||||
} dmAdvApiSetRandAddr_t;
|
||||
|
||||
/* Data structure for DM_ADV_PER_MSG_API_CONFIG */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
} dmAdvPerApiConfig_t;
|
||||
|
||||
/* Data structure for DM_ADV_PER_MSG_API_SET_DATA */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
uint8_t op;
|
||||
uint8_t len;
|
||||
uint8_t pData[];
|
||||
} dmAdvPerApiSetData_t;
|
||||
|
||||
/* Data structure for DM_ADV_PER_MSG_API_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
} dmAdvPerApiStart_t;
|
||||
|
||||
/* Data structure for DM_ADV_PER_MSG_API_STOP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
} dmAdvPerApiStop_t;
|
||||
|
||||
/* Union of all adv messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmAdvApiConfig_t apiConfig;
|
||||
dmAdvApiSetData_t apiSetData;
|
||||
dmAdvApiStart_t apiStart;
|
||||
dmAdvApiStop_t apiStop;
|
||||
dmAdvApiRemove_t apiRemove;
|
||||
dmAdvApiSetRandAddr_t apiSetRandAddr;
|
||||
dmAdvPerApiConfig_t apiPerConfig;
|
||||
dmAdvPerApiSetData_t apiPerSetData;
|
||||
dmAdvPerApiStart_t apiPerStart;
|
||||
dmAdvPerApiStop_t apiPerStop;
|
||||
secAes_t aes;
|
||||
} dmAdvMsg_t;
|
||||
|
||||
/* Action function */
|
||||
typedef void (*dmAdvAct_t)(dmAdvMsg_t *pMsg);
|
||||
|
||||
/* Control block for advertising module */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t advTimer;
|
||||
uint16_t intervalMin[DM_NUM_ADV_SETS];
|
||||
uint16_t intervalMax[DM_NUM_ADV_SETS];
|
||||
uint8_t advType[DM_NUM_ADV_SETS];
|
||||
uint8_t channelMap[DM_NUM_ADV_SETS];
|
||||
uint8_t localAddrType;
|
||||
uint8_t advState[DM_NUM_ADV_SETS];
|
||||
uint16_t advDuration[DM_NUM_ADV_SETS];
|
||||
bool_t advEnabled;
|
||||
bdAddr_t peerAddr[DM_NUM_ADV_SETS];
|
||||
uint8_t peerAddrType[DM_NUM_ADV_SETS];
|
||||
} dmAdvCb_t;
|
||||
|
||||
extern dmAdvCb_t dmAdvCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* legacy adv component inteface */
|
||||
void dmAdvMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmAdvHciHandler(hciEvt_t *pEvent);
|
||||
void dmAdvReset(void);
|
||||
|
||||
/* legacy adv action functions */
|
||||
void dmAdvActConfig(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActSetData(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActStart(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActStop(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActRemoveSet(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActClearSets(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActSetRandAddr(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActTimeout(dmAdvMsg_t *pMsg);
|
||||
|
||||
/* extended adv component inteface */
|
||||
void dmExtAdvMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmExtAdvHciHandler(hciEvt_t *pEvent);
|
||||
void dmExtAdvReset(void);
|
||||
|
||||
/* extended adv action functions */
|
||||
void dmExtAdvActConfig(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActSetData(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActStart(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActStop(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActRemoveSet(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActClearSets(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActSetRandAddr(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActTimeout(dmAdvMsg_t *pMsg);
|
||||
|
||||
/* periodic adv action functions */
|
||||
void dmPerAdvActConfig(dmAdvMsg_t *pMsg);
|
||||
void dmPerAdvActSetData(dmAdvMsg_t *pMsg);
|
||||
void dmPerAdvActStart(dmAdvMsg_t *pMsg);
|
||||
void dmPerAdvActStop(dmAdvMsg_t *pMsg);
|
||||
|
||||
/* periodic adv component inteface */
|
||||
void dmPerAdvMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmPerAdvHciHandler(hciEvt_t *pEvent);
|
||||
void dmPerAdvReset(void);
|
||||
|
||||
/* legacy adv directed advertising interface */
|
||||
void dmAdvStartDirected(uint8_t advType, uint16_t duration, uint8_t addrType, uint8_t *pAddr);
|
||||
void dmAdvStopDirected(void);
|
||||
void dmAdvConnected(void);
|
||||
void dmAdvConnectFailed(void);
|
||||
|
||||
/* extended adv directed advertising interface */
|
||||
void dmExtAdvStartDirected(dmConnId_t connId, uint8_t advHandle, uint8_t advType,
|
||||
uint16_t duration, uint8_t maxEaEvents, uint8_t addrType, uint8_t *pAddr);
|
||||
void dmExtAdvStopDirected(dmConnId_t connId);
|
||||
void dmExtAdvConnected(dmConnId_t connId);
|
||||
void dmExtAdvConnectFailed(dmConnId_t connId);
|
||||
|
||||
/* adv utility functions */
|
||||
void dmAdvInit(void);
|
||||
void dmAdvCbInit(uint8_t advHandle);
|
||||
void dmAdvGenConnCmpl(uint8_t advHandle, uint8_t status);
|
||||
|
||||
/* extended and periodic adv utility functions */
|
||||
void dmExtAdvInit(void);
|
||||
void dmPerAdvInit(void);
|
||||
uint8_t dmPerAdvState(uint8_t advHandle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_ADV_H */
|
||||
+1941
File diff suppressed because it is too large
Load Diff
Vendored
+562
@@ -0,0 +1,562 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager advertising module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_adv.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block for legacy advertising module */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t advType; /*!< Advertising type. */
|
||||
} dmLegAdvCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmAdvAct_t dmAdvAct[] =
|
||||
{
|
||||
dmAdvActConfig,
|
||||
dmAdvActSetData,
|
||||
dmAdvActStart,
|
||||
dmAdvActStop,
|
||||
dmAdvActRemoveSet,
|
||||
dmAdvActClearSets,
|
||||
dmAdvActSetRandAddr,
|
||||
dmAdvActTimeout
|
||||
};
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmAdvFcnIf =
|
||||
{
|
||||
dmAdvReset,
|
||||
dmAdvHciHandler,
|
||||
dmAdvMsgHandler
|
||||
};
|
||||
|
||||
/* control block */
|
||||
static dmLegAdvCb_t dmLegAdvCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the advertising parameters using the given advertising type, and peer address.
|
||||
*
|
||||
* \param advType Advertising type.
|
||||
* \param peerAddrType Peer address type.
|
||||
* \param pPeerAddr Peer address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmAdvConfig(uint8_t advType, uint8_t peerAddrType, uint8_t *pPeerAddr)
|
||||
{
|
||||
/* set advertising parameters */
|
||||
HciLeSetAdvParamCmd(dmAdvCb.intervalMin[DM_ADV_HANDLE_DEFAULT], /* advIntervalMin */
|
||||
dmAdvCb.intervalMax[DM_ADV_HANDLE_DEFAULT], /* advIntervalMax */
|
||||
advType, /* advType */
|
||||
DmLlAddrType(dmCb.advAddrType), /* ownAddrType */
|
||||
peerAddrType, /* peerAddrType */
|
||||
pPeerAddr, /* pPeerAddr */
|
||||
dmAdvCb.channelMap[DM_ADV_HANDLE_DEFAULT], /* advChanMap */
|
||||
dmCb.advFiltPolicy[DM_ADV_HANDLE_DEFAULT]); /* advFiltPolicy */
|
||||
|
||||
/* store advertising type */
|
||||
dmLegAdvCb.advType = advType;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start advertising action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActConfig(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvActConfig: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_IDLE)
|
||||
{
|
||||
/* if doing directed advertising ignore the request */
|
||||
if ((dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT) ||
|
||||
(dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT_LO_DUTY))
|
||||
{
|
||||
DM_TRACE_WARN0("DmAdvConfig during directed advertising!");
|
||||
return;
|
||||
}
|
||||
|
||||
/* set advertising parameters */
|
||||
dmAdvConfig(pMsg->apiConfig.advType, pMsg->apiConfig.peerAddrType, pMsg->apiConfig.peerAddr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the advertising or scan response data to the given data.
|
||||
*
|
||||
* \param location Data location.
|
||||
* \param len Length of the data. Maximum length is 31 bytes.
|
||||
* \param pData Pointer to the data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActSetData(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
WSF_ASSERT(pMsg->apiSetData.len <= HCI_ADV_DATA_LEN);
|
||||
|
||||
DM_TRACE_INFO1("dmAdvActSetData: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_IDLE)
|
||||
{
|
||||
/* set new data in HCI */
|
||||
if (pMsg->apiSetData.location == DM_DATA_LOC_ADV)
|
||||
{
|
||||
HciLeSetAdvDataCmd(pMsg->apiSetData.len, pMsg->apiSetData.pData);
|
||||
}
|
||||
else
|
||||
{
|
||||
HciLeSetScanRespDataCmd(pMsg->apiSetData.len, pMsg->apiSetData.pData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start advertising action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActStart(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvActStart: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_IDLE)
|
||||
{
|
||||
/* if doing directed advertising ignore the request */
|
||||
if ((dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT) ||
|
||||
(dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT_LO_DUTY))
|
||||
{
|
||||
DM_TRACE_WARN0("dmAdvActStart during directed advertising!");
|
||||
return;
|
||||
}
|
||||
|
||||
/* start advertising */
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_STARTING;
|
||||
dmAdvCb.advDuration[DM_ADV_HANDLE_DEFAULT] = pMsg->apiStart.duration[DM_ADV_HANDLE_DEFAULT];
|
||||
HciLeSetAdvEnableCmd(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop advertising action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActStop(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvActStop: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_ADVERTISING)
|
||||
{
|
||||
/* if doing directed advertising ignore the request */
|
||||
if ((dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT) ||
|
||||
(dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT_LO_DUTY))
|
||||
{
|
||||
DM_TRACE_WARN0("DmAdvStop during directed advertising!");
|
||||
return;
|
||||
}
|
||||
|
||||
/* disable advertising */
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_STOPPING;
|
||||
HciLeSetAdvEnableCmd(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove an advertising set action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActRemoveSet(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear advertising sets action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActClearSets(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the random device address for a given advertising set.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActSetRandAddr(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an advertising timeout.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActTimeout(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
DM_TRACE_INFO0("dmAdvActTimeout!");
|
||||
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_ADVERTISING)
|
||||
{
|
||||
/* disable advertising */
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_STOPPING;
|
||||
HciLeSetAdvEnableCmd(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the legacy adv module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvReset(void)
|
||||
{
|
||||
wsfMsgHdr_t advStop;
|
||||
|
||||
/* if stopping undirected advertisement or advertising but not high duty cycle directed adv */
|
||||
if ((dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_STOPPING) ||
|
||||
((dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_ADVERTISING) &&
|
||||
(dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] != DM_ADV_CONN_DIRECT)))
|
||||
{
|
||||
/* stop advertising timer */
|
||||
WsfTimerStop(&dmAdvCb.advTimer);
|
||||
|
||||
/* generate advertising stop event */
|
||||
advStop.status = HCI_SUCCESS;
|
||||
advStop.event = DM_ADV_STOP_IND;
|
||||
|
||||
/* call callback */
|
||||
(*dmCb.cback)((dmEvt_t *) &advStop);
|
||||
}
|
||||
|
||||
/* reset legacy adv module */
|
||||
dmAdvInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM adv HCI event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
if (pEvent->hdr.event == HCI_LE_ADV_ENABLE_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
uint8_t cbackEvent = 0;
|
||||
|
||||
DM_TRACE_INFO1("HCI_LE_ADV_ENABLE_CMD_CMPL_CBACK_EVT: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
switch (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT])
|
||||
{
|
||||
case DM_ADV_STATE_STARTING:
|
||||
case DM_ADV_STATE_STARTING_DIRECTED:
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_STARTING)
|
||||
{
|
||||
/* start advertising timer if applicable */
|
||||
if (dmAdvCb.advDuration[DM_ADV_HANDLE_DEFAULT] > 0)
|
||||
{
|
||||
dmAdvCb.advTimer.msg.event = DM_ADV_MSG_TIMEOUT;
|
||||
WsfTimerStartMs(&dmAdvCb.advTimer, dmAdvCb.advDuration[DM_ADV_HANDLE_DEFAULT]);
|
||||
}
|
||||
|
||||
/* Application callbacks only sent in undirected state */
|
||||
if (dmLegAdvCb.advType != DM_ADV_CONN_DIRECT_LO_DUTY)
|
||||
{
|
||||
cbackEvent = DM_ADV_START_IND;
|
||||
}
|
||||
}
|
||||
|
||||
/* pass advertising start event to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_RPA_START, DM_ADV_START_IND, 0, 0);
|
||||
|
||||
/* store advertising type and state */
|
||||
dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] = dmLegAdvCb.advType;
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_ADVERTISING;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_ADV_STATE_STOPPING:
|
||||
case DM_ADV_STATE_STOPPING_DIRECTED:
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_STOPPING)
|
||||
{
|
||||
/* stop advertising timer */
|
||||
WsfTimerStop(&dmAdvCb.advTimer);
|
||||
|
||||
/* Application and DM callbacks only sent in undirected or low duty cycle directed state */
|
||||
if (dmLegAdvCb.advType == DM_ADV_CONN_DIRECT_LO_DUTY)
|
||||
{
|
||||
cbackEvent = HCI_LE_ENHANCED_CONN_CMPL_CBACK_EVT;
|
||||
}
|
||||
else
|
||||
{
|
||||
cbackEvent = DM_ADV_STOP_IND;
|
||||
}
|
||||
}
|
||||
|
||||
/* pass advertising stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_RPA_STOP, DM_ADV_STOP_IND, 0, 0);
|
||||
|
||||
/* store advertising type and state */
|
||||
dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] = DM_ADV_NONE;
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_ADVERTISING;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* ignore the event */
|
||||
break;
|
||||
}
|
||||
|
||||
/* if DM conn notify needed */
|
||||
if (cbackEvent == HCI_LE_ENHANCED_CONN_CMPL_CBACK_EVT)
|
||||
{
|
||||
dmAdvGenConnCmpl(DM_ADV_HANDLE_DEFAULT, HCI_ERR_ADV_TIMEOUT);
|
||||
}
|
||||
/* else if app callback needed */
|
||||
else if (cbackEvent)
|
||||
{
|
||||
pEvent->hdr.event = cbackEvent;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM adv event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmAdvAct[DM_MSG_MASK(pMsg->event)])((dmAdvMsg_t *)pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start directed advertising.
|
||||
*
|
||||
* \param advType Advertising type.
|
||||
* \param duration Advertising duration (in ms).
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvStartDirected(uint8_t advType, uint16_t duration, uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvStartDirected: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
/* if not advertising */
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_IDLE)
|
||||
{
|
||||
/* start advertising */
|
||||
HciLeSetAdvEnableCmd(TRUE);
|
||||
|
||||
/* store advertising info */
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = (advType == DM_ADV_CONN_DIRECT) ? \
|
||||
DM_ADV_STATE_STARTING_DIRECTED : DM_ADV_STATE_STARTING;
|
||||
|
||||
dmAdvCb.advDuration[DM_ADV_HANDLE_DEFAULT] = duration;
|
||||
BdaCpy(dmAdvCb.peerAddr[DM_ADV_HANDLE_DEFAULT], pAddr);
|
||||
dmAdvCb.peerAddrType[DM_ADV_HANDLE_DEFAULT] = addrType;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop directed advertising.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvStopDirected(void)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvStopDirected: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
/* if advertising or starting advertising */
|
||||
if ((dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_ADVERTISING) ||
|
||||
(dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_STARTING) ||
|
||||
(dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_STARTING_DIRECTED))
|
||||
{
|
||||
/* disable advertising */
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = (dmLegAdvCb.advType == DM_ADV_CONN_DIRECT) ? \
|
||||
DM_ADV_STATE_STOPPING_DIRECTED : DM_ADV_STATE_STOPPING;
|
||||
HciLeSetAdvEnableCmd(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called when a connection is established from directed advertising.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvConnected(void)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvConnected: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
WsfTimerStop(&dmAdvCb.advTimer);
|
||||
|
||||
/* pass advertising stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_RPA_STOP, DM_ADV_STOP_IND, 0, 0);
|
||||
|
||||
dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] = DM_ADV_NONE;
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_IDLE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called when a directed advertising connection fails.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvConnectFailed(void)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvConnectFailed: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
WsfTimerStop(&dmAdvCb.advTimer);
|
||||
|
||||
/* pass advertising stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_RPA_STOP, DM_ADV_STOP_IND, 0, 0);
|
||||
|
||||
dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] = DM_ADV_NONE;
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_IDLE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM legacy advertising.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvInit(void)
|
||||
{
|
||||
WsfTaskLock();
|
||||
|
||||
/* set function interface table */
|
||||
dmFcnIfTbl[DM_ID_ADV] = (dmFcnIf_t *) &dmAdvFcnIf;
|
||||
|
||||
/* initialize legacy adv module */
|
||||
dmAdvInit();
|
||||
|
||||
/* clear set advertising set random address callback */
|
||||
dmDevCb.advSetRandAddrCback = NULL;
|
||||
|
||||
/* initialize HCI VS module */
|
||||
HciVsInit(0);
|
||||
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Whether DM advertising is in legacy mode.
|
||||
*
|
||||
* \return TRUE if DM advertising is in legacy mode. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmAdvModeLeg(void)
|
||||
{
|
||||
return (dmFcnIfTbl[DM_ID_ADV] == (dmFcnIf_t *) &dmAdvFcnIf) ? TRUE : FALSE;
|
||||
}
|
||||
+1595
File diff suppressed because it is too large
Load Diff
+369
@@ -0,0 +1,369 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM connection management module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_CONN_H
|
||||
#define DM_CONN_H
|
||||
|
||||
#include "dm_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Uninitialized HCI handle */
|
||||
#define DM_CONN_HCI_HANDLE_NONE 0xFFFF
|
||||
|
||||
/* Action set initializer */
|
||||
#define DM_CONN_ACT_SET_INIT(n) ((n) << 4)
|
||||
|
||||
/* Get action set ID from action */
|
||||
#define DM_CONN_ACT_SET_ID(action) ((action) >> 4)
|
||||
|
||||
/* Get action ID from action */
|
||||
#define DM_CONN_ACT_ID(action) ((action) & 0x0F)
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM conn event handler messages for state machine */
|
||||
enum
|
||||
{
|
||||
/* messages from API */
|
||||
DM_CONN_MSG_API_OPEN = DM_MSG_START(DM_ID_CONN), /*!< Open Connection */
|
||||
DM_CONN_MSG_API_CLOSE, /*!< Close Connection */
|
||||
DM_CONN_MSG_API_ACCEPT, /*!< Accept Connection */
|
||||
DM_CONN_MSG_API_UPDATE_MASTER, /*!< Master Connection Parameter Update */
|
||||
DM_CONN_MSG_API_UPDATE_SLAVE, /*!< Slave Connecteion Parameter Update */
|
||||
|
||||
/* messages from L2C */
|
||||
DM_CONN_MSG_L2C_UPDATE_IND, /*!< L2CAP Parameter update indication */
|
||||
DM_CONN_MSG_L2C_UPDATE_CNF, /*!< L2CAP Parameter update confirmation */
|
||||
|
||||
/* messages from HCI */
|
||||
DM_CONN_MSG_HCI_LE_CONN_CMPL_FAIL, /*!< HCI LE Connection Complete Failure Event */
|
||||
DM_CONN_MSG_HCI_LE_CONN_CMPL, /*!< HCI LE Connection Compelte Event */
|
||||
DM_CONN_MSG_HCI_DISCONNECT_CMPL, /*!< HCI Disconnection Complete Event */
|
||||
DM_CONN_MSG_HCI_LE_CONN_UPDATE_CMPL, /*!< HCI LE Connection Update Complete Event */
|
||||
DM_CONN_MSG_HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL, /*!< HCI LE Create Connection Cancel Command Complet Event */
|
||||
|
||||
/* other internal messages */
|
||||
DM_CONN_MSG_INT_UPDATE_TIMEOUT /*!< Internal Update Timeout */
|
||||
};
|
||||
|
||||
/* Number of messages */
|
||||
#define DM_CONN_NUM_MSGS (DM_CONN_MSG_INT_UPDATE_TIMEOUT - DM_CONN_MSG_API_OPEN + 1)
|
||||
|
||||
/* DM conn event handler messages, non-state machine */
|
||||
enum
|
||||
{
|
||||
DM_CONN_MSG_API_FEAT = DM_MSG_START(DM_ID_CONN_2),
|
||||
DM_CONN_MSG_API_READ_RSSI,
|
||||
DM_CONN_MSG_API_REM_CONN_PARAM_REQ_REPLY,
|
||||
DM_CONN_MSG_API_REM_CONN_PARAM_REQ_NEG_REPLY,
|
||||
DM_CONN_MSG_API_SET_DATA_LEN,
|
||||
DM_CONN_MSG_API_WRITE_AUTH_TO,
|
||||
|
||||
/* messages from HCI */
|
||||
DM_CONN_MSG_HCI_READ_RSSI_CMPL,
|
||||
DM_CONN_MSG_HCI_FEAT_CMPL
|
||||
};
|
||||
|
||||
/* State machine action function sets */
|
||||
enum
|
||||
{
|
||||
DM_CONN_ACT_SET_MAIN,
|
||||
DM_CONN_ACT_SET_MASTER,
|
||||
DM_CONN_ACT_SET_SLAVE,
|
||||
DM_CONN_NUM_ACT_SETS
|
||||
};
|
||||
|
||||
/*! State machine actions */
|
||||
enum
|
||||
{
|
||||
DM_CONN_SM_ACT_NONE = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_MAIN), /*!< No Action */
|
||||
DM_CONN_SM_ACT_CLOSE, /*!< Process Connection Close */
|
||||
DM_CONN_SM_ACT_CONN_OPENED, /*!< Procoess Connection Opened */
|
||||
DM_CONN_SM_ACT_CONN_FAILED, /*!< Process Connection Failed */
|
||||
DM_CONN_SM_ACT_CONN_CLOSED, /*!< Process Connection Closed */
|
||||
DM_CONN_SM_ACT_HCI_UPDATED, /*!< Process HCI Connection Update */
|
||||
|
||||
DM_CONN_SM_ACT_OPEN = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_MASTER), /*!< Process Master Connection Open */
|
||||
DM_CONN_SM_ACT_CANCEL_OPEN, /*!< Process Master Cancel Connection Open */
|
||||
DM_CONN_SM_ACT_UPDATE_MASTER, /*!< Process Master Connection Parameter Update */
|
||||
DM_CONN_SM_ACT_L2C_UPDATE_IND, /*!< Process Master L2CAP Connection Parameter Update Indication */
|
||||
|
||||
DM_CONN_SM_ACT_ACCEPT = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_SLAVE), /*!< Process Slave Connection Accept */
|
||||
DM_CONN_SM_ACT_CANCEL_ACCEPT, /*!< Process Slave Cancel Connection Accept */
|
||||
DM_CONN_SM_ACT_UPDATE_SLAVE, /*!< Process Slave Connection Update */
|
||||
DM_CONN_SM_ACT_CONN_ACCEPTED, /*!< Process Slave Connection Accepted */
|
||||
DM_CONN_SM_ACT_ACCEPT_FAILED, /*!< Process Slave Connection Accept Failure */
|
||||
DM_CONN_SM_ACT_L2C_UPDATE_CNF /*!< Process Slave L2CAP Connection Parameter Update Confirmation */
|
||||
};
|
||||
|
||||
/*! State machine states */
|
||||
enum
|
||||
{
|
||||
DM_CONN_SM_ST_IDLE, /*!< Idle State */
|
||||
DM_CONN_SM_ST_CONNECTING, /*!< Connecting State */
|
||||
DM_CONN_SM_ST_ACCEPTING, /*!< Accepting State */
|
||||
DM_CONN_SM_ST_CONNECTED, /*!< Connected State */
|
||||
DM_CONN_SM_ST_DISCONNECTING, /*!< Disconnecting State */
|
||||
DM_CONN_SM_NUM_STATES
|
||||
};
|
||||
|
||||
/* Data structure for DM_CONN_MSG_API_OPEN and DM_CONN_MSG_API_ACCEPT */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t initPhys;
|
||||
uint8_t advHandle;
|
||||
uint8_t advType;
|
||||
uint16_t duration;
|
||||
uint8_t maxEaEvents;
|
||||
bdAddr_t peerAddr;
|
||||
uint8_t addrType;
|
||||
uint8_t clientId;
|
||||
} dmConnApiOpen_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_CLOSE */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t reason;
|
||||
uint8_t clientId;
|
||||
} dmConnApiClose_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_UPDATE_MASTER and SLAVE */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
hciConnSpec_t connSpec;
|
||||
} dmConnApiUpdate_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_L2C_UPDATE_IND */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
hciConnSpec_t *pConnSpec;
|
||||
uint8_t identifier;
|
||||
} dmConnL2cUpdateInd_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_L2C_UPDATE_CNF */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint16_t result;
|
||||
} dmConnL2cUpdateCnf_t;
|
||||
|
||||
/*! Union of all DM Conn state machine messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmConnApiOpen_t apiOpen;
|
||||
dmConnApiClose_t apiClose;
|
||||
dmConnApiUpdate_t apiUpdate;
|
||||
dmConnL2cUpdateInd_t l2cUpdateInd;
|
||||
dmConnL2cUpdateCnf_t l2cUpdateCnf;
|
||||
hciLeConnCmplEvt_t hciLeConnCmpl;
|
||||
hciDisconnectCmplEvt_t hciDisconnectCmpl;
|
||||
hciLeConnUpdateCmplEvt_t hciLeConnUpdateCmpl;
|
||||
} dmConnMsg_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_FEAT */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint16_t handle;
|
||||
uint8_t type;
|
||||
uint8_t feature;
|
||||
} dmConnApiFeat_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_READ_RSSI */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
} dmConnApiReadRssi_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_REM_CONN_PARAM_REQ_REPLY */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
hciConnSpec_t connSpec;
|
||||
} dmConnApiRemConnParamReqReply_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_REM_CONN_PARAM_REQ_NEG_REPLY */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t reason;
|
||||
} dmConnApiRemConnParamReqNegReply_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_SET_DATA_LEN */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint16_t txOctets;
|
||||
uint16_t txTime;
|
||||
} dmConnApiSetDataLen_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_WRITE_AUTH_TO */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint16_t timeout;
|
||||
} dmConnApiWriteAuthPayloadTo_t;
|
||||
|
||||
/*! Union of all DM Conn 2 messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmConnApiFeat_t apiFeat;
|
||||
dmConnApiReadRssi_t apiReadRssi;
|
||||
dmConnApiRemConnParamReqReply_t apiRemConnParamReqReply;
|
||||
dmConnApiRemConnParamReqNegReply_t apiRemConnParamReqNegReply;
|
||||
dmConnApiSetDataLen_t apiSetDataLen;
|
||||
dmConnApiWriteAuthPayloadTo_t apiWriteAuthPayloadTo;
|
||||
} dmConn2Msg_t;
|
||||
|
||||
/*! Connection control block */
|
||||
typedef struct
|
||||
{
|
||||
bdAddr_t peerAddr;
|
||||
bdAddr_t localAddr;
|
||||
uint16_t handle;
|
||||
uint16_t idleMask;
|
||||
dmConnId_t connId;
|
||||
bool_t updating;
|
||||
bool_t usingLtk;
|
||||
uint8_t peerAddrType;
|
||||
uint8_t localAddrType;
|
||||
uint8_t state;
|
||||
uint8_t inUse;
|
||||
uint8_t secLevel;
|
||||
uint8_t tmpSecLevel;
|
||||
uint8_t role;
|
||||
|
||||
/* enhanced fields */
|
||||
bdAddr_t localRpa;
|
||||
bdAddr_t peerRpa;
|
||||
|
||||
uint32_t features;
|
||||
bool_t featuresPresent;
|
||||
} dmConnCcb_t;
|
||||
|
||||
/*! Action function */
|
||||
typedef void (*dmConnAct_t)(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/*! Control block of the DM conn module */
|
||||
typedef struct
|
||||
{
|
||||
dmConnCcb_t ccb[DM_CONN_MAX];
|
||||
dmCback_t connCback[DM_CLIENT_ID_MAX];
|
||||
hciConnSpec_t connSpec[DM_NUM_PHYS];
|
||||
uint16_t scanInterval[DM_NUM_PHYS];
|
||||
uint16_t scanWindow[DM_NUM_PHYS];
|
||||
} dmConnCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! State machine action sets */
|
||||
extern dmConnAct_t *dmConnActSet[DM_CONN_NUM_ACT_SETS];
|
||||
|
||||
/*! Control block */
|
||||
extern dmConnCb_t dmConnCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* utility functions */
|
||||
dmConnCcb_t *dmConnCcbAlloc(uint8_t *pAddr);
|
||||
void dmConnCcbDealloc(dmConnCcb_t *pCcb);
|
||||
dmConnCcb_t *dmConnCcbByHandle(uint16_t handle);
|
||||
dmConnCcb_t *dmConnCcbByBdAddr(uint8_t *pAddr);
|
||||
dmConnCcb_t *dmConnCcbById(dmConnId_t connId);
|
||||
uint8_t dmConnNum(void);
|
||||
dmConnId_t dmConnOpenAccept(uint8_t clientId, uint8_t initPhys, uint8_t advHandle, uint8_t advType,
|
||||
uint16_t duration, uint8_t maxEaEvents, uint8_t addrType, uint8_t *pAddr,
|
||||
uint8_t role);
|
||||
void dmConnExecCback(dmConnMsg_t *pMsg);
|
||||
|
||||
/* component inteface */
|
||||
void dmConnReset(void);
|
||||
void dmConnMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmConnHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* component 2 inteface */
|
||||
void dmConn2MsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmConn2HciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* state machine */
|
||||
void dmConnSmExecute(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* main action functions */
|
||||
void dmConnSmActNone(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActClose(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActConnOpened(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActConnFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActConnClosed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActHciUpdated(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* common master action functions */
|
||||
void dmConnSmActCancelOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActUpdateMaster(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActL2cUpdateInd(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* legacy master action functions */
|
||||
void dmConnSmActOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* extended master action functions */
|
||||
void dmExtConnSmActOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* common slave action functions */
|
||||
void dmConnSmActUpdateSlave(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActL2cUpdateCnf(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* legacy slave action functions */
|
||||
void dmConnSmActAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActCancelAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActConnAccepted(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActAcceptFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* extended slave action functions */
|
||||
void dmExtConnSmActAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmExtConnSmActCancelAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmExtConnSmActConnAccepted(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmExtConnSmActAcceptFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_CONN_H */
|
||||
Vendored
+840
@@ -0,0 +1,840 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager Connection Constant Tone Extension (CTE) module.
|
||||
*
|
||||
* Copyright (c) 2018-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM connection CTE states */
|
||||
enum
|
||||
{
|
||||
DM_CONN_CTE_STATE_IDLE, /*!< Idle */
|
||||
DM_CONN_CTE_STATE_INITIATING, /*!< Initiating CTE request */
|
||||
DM_CONN_CTE_STATE_RESPONDING, /*!< Responding to CTE request */
|
||||
DM_CONN_CTE_STATE_SAMPLING, /*!< Sampling received CTE */
|
||||
DM_CONN_CTE_STATE_STARTING, /*!< Starting CTE request, CTE response or sampling received CTE */
|
||||
DM_CONN_CTE_STATE_STOPPING, /*!< Stopping CTE request, CTE response or sampling received CTE */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_RX_SAMPLE_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
uint8_t slotDurations; /*!< Switching and sampling slot durations */
|
||||
uint8_t switchPatternLen; /*!< Number of Antenna IDs in switching pattern */
|
||||
uint8_t *pAntennaIDs; /*!< List of Antenna IDs in switching pattern */
|
||||
} dmConnCteApiRxSampleStart_t;
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_RX_SAMPLE_STOP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
} dmConnCteApiRxSampleStop_t;
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_TX_CFG */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
uint8_t cteTypeBits; /*!< Permitted CTE type bits */
|
||||
uint8_t switchPatternLen; /*!< Number of Antenna IDs in switching pattern */
|
||||
uint8_t *pAntennaIDs; /*!< List of Antenna IDs in switching pattern */
|
||||
} dmConnCteApiTxConfig_t;
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_REQ_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
uint16_t cteReqInt; /*!< CTE request interval */
|
||||
uint8_t reqCteLen; /*!< Minimum length of CTE being requested in 8 us units */
|
||||
uint8_t reqCteType; /*!< Requested CTE type */
|
||||
} dmConnCteApiReqStart_t;
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_REQ_STOP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
} dmConnCteApiReqStop_t;
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_RSP_START and DM_CONN_CTE_MSG_API_RSP_STOP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
} dmConnCteApiRspEnable_t;
|
||||
|
||||
/*! Union of all DM Connection CTE API messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnCteApiRxSampleStart_t rxSampleStart; /*!< Start sampling received CTE, and configure CTE Rx parameters to be used */
|
||||
dmConnCteApiRxSampleStop_t rxSampleStop; /*!< Stop sampling received CTE */
|
||||
dmConnCteApiTxConfig_t txCfg; /*!< Configure CTE Tx parameters */
|
||||
dmConnCteApiReqStart_t reqStart; /*!< Start initiating CTE request */
|
||||
dmConnCteApiReqStop_t reqStop; /*!< Stop initiating CTE request */
|
||||
dmConnCteApiRspEnable_t rspEnable; /*!< Start or stop responding to CTE request */
|
||||
} dmConnCteMsg_t;
|
||||
|
||||
/*! Action function */
|
||||
typedef void (*dmConnCteAct_t)(dmConnCteMsg_t *pMsg);
|
||||
|
||||
/*! Control block for connection CTE module */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t rxSampleState; /*!< Sampling received CTE state */
|
||||
uint8_t reqState; /*!< Connection CTE Request state */
|
||||
uint8_t rspState; /*!< Connection CTE Response state */
|
||||
} dmConnCteCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
void dmConnCteInit(void);
|
||||
void dmConnCteReset(void);
|
||||
void dmConnCteHciHandler(hciEvt_t *pEvent);
|
||||
void dmConnCteMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/*! Action functions */
|
||||
static void dmConnCteActRxSampleStart(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActRxSampleStop(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActTxCfg(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActReqStart(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActReqStop(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActRspStart(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActRspStop(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActState(dmConnCteMsg_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action function table */
|
||||
static const dmConnCteAct_t dmConnCteAct[] =
|
||||
{
|
||||
dmConnCteActRxSampleStart,
|
||||
dmConnCteActRxSampleStop,
|
||||
dmConnCteActTxCfg,
|
||||
dmConnCteActReqStart,
|
||||
dmConnCteActReqStop,
|
||||
dmConnCteActRspStart,
|
||||
dmConnCteActRspStop,
|
||||
dmConnCteActState
|
||||
};
|
||||
|
||||
/*! DM Connection CTE component function interface */
|
||||
static const dmFcnIf_t dmConnCteFcnIf =
|
||||
{
|
||||
dmConnCteReset,
|
||||
dmConnCteHciHandler,
|
||||
dmConnCteMsgHandler
|
||||
};
|
||||
|
||||
/*! Connection CTE control block */
|
||||
static dmConnCteCb_t dmConnCteCb[DM_CONN_MAX];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize connection CTE control block entry.
|
||||
*
|
||||
* \param pCteCb Pointer to connection CTE control block structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteCbInit(dmConnCteCb_t *pCteCb)
|
||||
{
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_IDLE;
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_IDLE;
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize connection CTE control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* initialize control block */
|
||||
for (i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
dmConnCteCbInit(&dmConnCteCb[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM Connection Constant Tone Extension (CTE) module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteInit(void)
|
||||
{
|
||||
/* set function interface table */
|
||||
dmFcnIfTbl[DM_ID_CONN_CTE] = (dmFcnIf_t *) &dmConnCteFcnIf;
|
||||
|
||||
/* initialize control block */
|
||||
dmConnCteInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the connection CTE module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteReset(void)
|
||||
{
|
||||
/* reset control block */
|
||||
dmConnCteInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM PAST HCI event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
if (pEvent->hdr.event == HCI_LE_READ_ANTENNA_INFO_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
pEvent->hdr.event = DM_READ_ANTENNA_INFO_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if ccb found */
|
||||
if ((pCcb = dmConnCcbByHandle(pEvent->hdr.param)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* set conn id */
|
||||
pEvent->hdr.param = pCcb->connId;
|
||||
|
||||
switch (pEvent->hdr.event)
|
||||
{
|
||||
case HCI_LE_CONN_IQ_REPORT_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_CONN_IQ_REPORT_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_CTE_REQ_FAILED_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_CTE_REQ_FAIL_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_SET_CONN_CTE_RX_PARAMS_CMD_CMPL_CBACK_EVT:
|
||||
/* if enabling sampling */
|
||||
if (pCteCb->rxSampleState == DM_CONN_CTE_STATE_STARTING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_SAMPLING;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_RX_SAMPLE_START_IND;
|
||||
}
|
||||
/* else if disabling sampling */
|
||||
else if (pCteCb->rxSampleState == DM_CONN_CTE_STATE_STOPPING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_SAMPLING;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_RX_SAMPLE_STOP_IND;
|
||||
}
|
||||
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_SET_CONN_CTE_TX_PARAMS_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_CONN_CTE_TX_CFG_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_CONN_CTE_REQ_ENABLE_CMD_CMPL_CBACK_EVT:
|
||||
/* if enabling request */
|
||||
if (pCteCb->reqState == DM_CONN_CTE_STATE_STARTING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_INITIATING;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_REQ_START_IND;
|
||||
}
|
||||
/* else if disabing request */
|
||||
else if (pCteCb->reqState == DM_CONN_CTE_STATE_STOPPING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_INITIATING;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_REQ_STOP_IND;
|
||||
}
|
||||
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_CONN_CTE_RSP_ENABLE_CMD_CMPL_CBACK_EVT:
|
||||
/* if enabling response */
|
||||
if (pCteCb->rspState == DM_CONN_CTE_STATE_STARTING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_RESPONDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_RSP_START_IND;
|
||||
}
|
||||
/* else if disabling response */
|
||||
else if (pCteCb->rspState == DM_CONN_CTE_STATE_STOPPING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_RESPONDING;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_RSP_STOP_IND;
|
||||
}
|
||||
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM Connection CTE event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmConnCteAct[DM_MSG_MASK(pMsg->event)])((dmConnCteMsg_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start sampling received CTE, and configure CTE receive parameters action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteActRxSampleStart(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->rxSampleStart.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently idle */
|
||||
if (pCteCb->rxSampleState == DM_CONN_CTE_STATE_IDLE)
|
||||
{
|
||||
HciLeSetConnCteRxParamsCmd(pCcb->handle, TRUE, pMsg->rxSampleStart.slotDurations,
|
||||
pMsg->rxSampleStart.switchPatternLen,
|
||||
pMsg->rxSampleStart.pAntennaIDs);
|
||||
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_STARTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteRxSampleStart ignored due to rxSampleState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop sampling received CTE action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteActRxSampleStop(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->rxSampleStop.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently sampling */
|
||||
if (pCteCb->rxSampleState == DM_CONN_CTE_STATE_SAMPLING)
|
||||
{
|
||||
HciLeSetConnCteRxParamsCmd(pCcb->handle, FALSE, 0, 0, NULL);
|
||||
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_STOPPING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteRxSampleStop ignored due to rxSampleState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure connection CTE transmit action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteActTxCfg(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->txCfg.connId)) != NULL)
|
||||
{
|
||||
HciLeSetConnCteTxParamsCmd(pCcb->handle, pMsg->txCfg.cteTypeBits, pMsg->txCfg.switchPatternLen,
|
||||
pMsg->txCfg.pAntennaIDs);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start initiating CTE request action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteActReqStart(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->reqStart.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently idle */
|
||||
if (pCteCb->reqState == DM_CONN_CTE_STATE_IDLE)
|
||||
{
|
||||
HciLeConnCteReqEnableCmd(pCcb->handle, TRUE, pMsg->reqStart.cteReqInt,
|
||||
pMsg->reqStart.reqCteLen, pMsg->reqStart.reqCteType);
|
||||
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_STARTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteReqStart ignored due to reqState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop initiating CTE request action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteActReqStop(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->reqStop.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently initiating */
|
||||
if (pCteCb->reqState == DM_CONN_CTE_STATE_INITIATING)
|
||||
{
|
||||
HciLeConnCteReqEnableCmd(pCcb->handle, FALSE, 0, 0, 0);
|
||||
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_STOPPING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteReqStop ignored due to reqState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start responding to CTE request action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteActRspStart(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->rspEnable.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently idle */
|
||||
if (pCteCb->rspState == DM_CONN_CTE_STATE_IDLE)
|
||||
{
|
||||
HciLeConnCteRspEnableCmd(pCcb->handle, TRUE);
|
||||
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_STARTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteRspStart ignored due to rspState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop responding to CTE request action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteActRspStop(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->rspEnable.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently responding */
|
||||
if (pCteCb->rspState == DM_CONN_CTE_STATE_RESPONDING)
|
||||
{
|
||||
HciLeConnCteRspEnableCmd(pCcb->handle, FALSE);
|
||||
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_STOPPING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteRspStop ignored due to rspState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection state change action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteActState(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
/* if connection closed */
|
||||
if (pMsg->hdr.status == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* reset all states */
|
||||
dmConnCteCbInit(&dmConnCteCb[pMsg->hdr.param - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Enable sampling received CTE fields on the specified connection, and configure the
|
||||
* antenna switching pattern, and switching and sampling slot durations to be used.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param slotDurations Switching and sampling slot durations to be used while receiving CTE.
|
||||
* \param switchPatternLen Number of Antenna IDs in switching pattern.
|
||||
* \param pAntennaIDs List of Antenna IDs in switching pattern.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteRxSampleStart(dmConnId_t connId, uint8_t slotDurations, uint8_t switchPatternLen,
|
||||
uint8_t *pAntennaIDs)
|
||||
{
|
||||
dmConnCteApiRxSampleStart_t *pMsg;
|
||||
|
||||
WSF_ASSERT((switchPatternLen >= HCI_MIN_NUM_ANTENNA_IDS) && \
|
||||
(switchPatternLen <= HCI_MAX_NUM_ANTENNA_IDS));
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiRxSampleStart_t) + switchPatternLen)) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_RX_SAMPLE_START;
|
||||
pMsg->connId = connId;
|
||||
pMsg->slotDurations = slotDurations;
|
||||
pMsg->switchPatternLen = switchPatternLen;
|
||||
|
||||
/* Copy antenna IDs to space after end of config struct */
|
||||
pMsg->pAntennaIDs = (uint8_t *)(pMsg + 1);
|
||||
memcpy(pMsg->pAntennaIDs, pAntennaIDs, switchPatternLen);
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Disable sampling received CTE fields on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteRxSampleStop(dmConnId_t connId)
|
||||
{
|
||||
dmConnCteApiRxSampleStop_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiRxSampleStop_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_RX_SAMPLE_STOP;
|
||||
pMsg->connId = connId;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure the antenna switching pattern, and permitted CTE types used for transmitting
|
||||
* CTEs requested by the peer device on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param cteTypeBits Permitted CTE type bits used for transmitting CTEs requested by peer.
|
||||
* \param switchPatternLen Number of Antenna IDs in switching pattern.
|
||||
* \param pAntennaIDs List of Antenna IDs in switching pattern.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteTxConfig(dmConnId_t connId, uint8_t cteTypeBits, uint8_t switchPatternLen,
|
||||
uint8_t *pAntennaIDs)
|
||||
{
|
||||
dmConnCteApiTxConfig_t *pMsg;
|
||||
|
||||
WSF_ASSERT((switchPatternLen >= HCI_MIN_NUM_ANTENNA_IDS) && \
|
||||
(switchPatternLen <= HCI_MAX_NUM_ANTENNA_IDS));
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiTxConfig_t) + switchPatternLen)) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_TX_CFG;
|
||||
pMsg->connId = connId;
|
||||
pMsg->cteTypeBits = cteTypeBits;
|
||||
pMsg->switchPatternLen = switchPatternLen;
|
||||
|
||||
/* Copy antenna IDs to space after end of config struct */
|
||||
pMsg->pAntennaIDs = (uint8_t *)(pMsg + 1);
|
||||
memcpy(pMsg->pAntennaIDs, pAntennaIDs, switchPatternLen);
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate the CTE Request procedure on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param cteReqInt CTE request interval.
|
||||
* \param reqCteLen Minimum length of CTE being requested in 8 us units.
|
||||
* \param reqCteType Requested CTE type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteReqStart(dmConnId_t connId, uint16_t cteReqInt, uint8_t reqCteLen,
|
||||
uint8_t reqCteType)
|
||||
{
|
||||
dmConnCteApiReqStart_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiReqStart_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_REQ_START;
|
||||
pMsg->connId = connId;
|
||||
pMsg->cteReqInt = cteReqInt;
|
||||
pMsg->reqCteLen = reqCteLen;
|
||||
pMsg->reqCteType = reqCteType;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop initiating the CTE Request procedure on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteReqStop(dmConnId_t connId)
|
||||
{
|
||||
dmConnCteApiReqStop_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiReqStop_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_REQ_STOP;
|
||||
pMsg->connId = connId;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start responding to LL_CTE_REQ PDUs with LL_CTE_RSP PDUs on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteRspStart(dmConnId_t connId)
|
||||
{
|
||||
dmConnCteApiRspEnable_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiRspEnable_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_RSP_START;
|
||||
pMsg->connId = connId;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop responding to LL_CTE_REQ PDUs with LL_CTE_RSP PDUs on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteRspStop(dmConnId_t connId)
|
||||
{
|
||||
dmConnCteApiRspEnable_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiRspEnable_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_RSP_STOP;
|
||||
pMsg->connId = connId;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read the switching rates, the sampling rates, the number of antennae, and the maximum
|
||||
* length of a transmitted Constant Tone Extension supported by the Controller.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note The antenna info will be returned with DM indication \ref DM_READ_ANTENNA_INFO_IND.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmReadAntennaInfo(void)
|
||||
{
|
||||
HciLeReadAntennaInfoCmd();
|
||||
}
|
||||
Vendored
+145
@@ -0,0 +1,145 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management for master.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_os.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "l2c_api.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel an opening connection.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActCancelOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
/* cancel create connection */
|
||||
HciLeCreateConnCancelCmd();
|
||||
|
||||
/* pass connection initiation stopped to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_CTRL, DM_DEV_PRIV_MSG_CONN_INIT_STOP, 0, 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update a connection as a master.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActUpdateMaster(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
/* send HCI command */
|
||||
HciLeConnUpdateCmd(pCcb->handle, &pMsg->apiUpdate.connSpec);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an L2CAP connection update indication.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActL2cUpdateInd(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
/* always send back response */
|
||||
L2cDmConnUpdateRsp(pMsg->l2cUpdateInd.identifier, pCcb->handle, L2C_CONN_PARAM_ACCEPTED);
|
||||
|
||||
/* send HCI command */
|
||||
HciLeConnUpdateCmd(pCcb->handle, pMsg->l2cUpdateInd.pConnSpec);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only. L2C calls this function when it receives a connection update
|
||||
* request from a peer device.
|
||||
*
|
||||
* \param identifier Identifier value.
|
||||
* \param handle Connection handle.
|
||||
* \param pConnSpec Connection spec parameters.
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmL2cConnUpdateInd(uint8_t identifier, uint16_t handle, hciConnSpec_t *pConnSpec)
|
||||
{
|
||||
dmConnL2cUpdateInd_t updateInd;
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
if ((pCcb = dmConnCcbByHandle(handle)) != NULL)
|
||||
{
|
||||
updateInd.hdr.event = DM_CONN_MSG_L2C_UPDATE_IND;
|
||||
updateInd.pConnSpec = pConnSpec;
|
||||
updateInd.identifier = identifier;
|
||||
|
||||
dmConnSmExecute(pCcb, (dmConnMsg_t *) &updateInd);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection to a peer device with the given address.
|
||||
*
|
||||
* \param clientId The client identifier.
|
||||
* \param initPhys Initiator PHYs.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmConnId_t DmConnOpen(uint8_t clientId, uint8_t initPhys, uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
return dmConnOpenAccept(clientId, initPhys, 0, 0, 0, 0, addrType, pAddr, DM_ROLE_MASTER);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the local address type used for connections created with DmConnOpen().
|
||||
*
|
||||
* \param addrType Address type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnSetAddrType(uint8_t addrType)
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmCb.connAddrType = addrType;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
Vendored
+118
@@ -0,0 +1,118 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management module for extended master.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Action set for this module */
|
||||
static const dmConnAct_t dmConnActSetMaster[] =
|
||||
{
|
||||
dmExtConnSmActOpen,
|
||||
dmConnSmActCancelOpen,
|
||||
dmConnSmActUpdateMaster,
|
||||
dmConnSmActL2cUpdateInd
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection to a peer device with the given address.
|
||||
*
|
||||
* \param initPhys Initiating PHYs.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmExtConnOpen(uint8_t initPhys, uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t idx;
|
||||
uint8_t phyIdx;
|
||||
hciExtInitParam_t initParam;
|
||||
hciConnSpec_t connSpec[DM_NUM_PHYS];
|
||||
hciExtInitScanParam_t scanParam[DM_NUM_PHYS];
|
||||
|
||||
/* set initiating parameters */
|
||||
initParam.filterPolicy = dmCb.initFiltPolicy;
|
||||
initParam.ownAddrType = DmLlAddrType(dmCb.connAddrType);
|
||||
initParam.peerAddrType = addrType;
|
||||
initParam.pPeerAddr = pAddr;
|
||||
initParam.initPhys = initPhys;
|
||||
|
||||
/* see advertising packets to be received on which PHY */
|
||||
for (i = 0, idx = 0; (i < 8) && (idx < DM_NUM_PHYS); i++)
|
||||
{
|
||||
if (initPhys & (1 << i))
|
||||
{
|
||||
phyIdx = DmInitPhyToIdx(1 << i);
|
||||
|
||||
/* set extended create conection parameters for this PHY */
|
||||
scanParam[idx].scanInterval = dmConnCb.scanInterval[phyIdx];
|
||||
scanParam[idx].scanWindow = dmConnCb.scanWindow[phyIdx];
|
||||
connSpec[idx] = dmConnCb.connSpec[phyIdx];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create connection */
|
||||
HciLeExtCreateConnCmd(&initParam, scanParam, connSpec);
|
||||
|
||||
/* pass connection initiation started to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_CTRL, DM_DEV_PRIV_MSG_CONN_INIT_START, 0, 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtConnSmActOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmExtConnOpen(pMsg->apiOpen.initPhys, pMsg->apiOpen.addrType, pMsg->apiOpen.peerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM connection manager for operation as extended master.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmExtConnMasterInit(void)
|
||||
{
|
||||
dmConnActSet[DM_CONN_ACT_SET_MASTER] = (dmConnAct_t *) dmConnActSetMaster;
|
||||
}
|
||||
Vendored
+92
@@ -0,0 +1,92 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management module for legacy master.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Action set for this module */
|
||||
static const dmConnAct_t dmConnActSetMaster[] =
|
||||
{
|
||||
dmConnSmActOpen,
|
||||
dmConnSmActCancelOpen,
|
||||
dmConnSmActUpdateMaster,
|
||||
dmConnSmActL2cUpdateInd
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection to a peer device with the given address.
|
||||
*
|
||||
* \param initPhys Initiating PHYs.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnOpen(uint8_t initPhys, uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
uint8_t phyIdx = DmScanPhyToIdx(HCI_SCAN_PHY_LE_1M_BIT);
|
||||
|
||||
/* Create connection */
|
||||
HciLeCreateConnCmd(dmConnCb.scanInterval[phyIdx], dmConnCb.scanWindow[phyIdx], dmCb.initFiltPolicy,
|
||||
addrType, pAddr, DmLlAddrType(dmCb.connAddrType), &(dmConnCb.connSpec[phyIdx]));
|
||||
|
||||
/* pass connection initiation started to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_CTRL, DM_DEV_PRIV_MSG_CONN_INIT_START, 0, 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmConnOpen(pMsg->apiOpen.initPhys, pMsg->apiOpen.addrType, pMsg->apiOpen.peerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM connection manager for operation as legacy master.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnMasterInit(void)
|
||||
{
|
||||
dmConnActSet[DM_CONN_ACT_SET_MASTER] = (dmConnAct_t *) dmConnActSetMaster;
|
||||
}
|
||||
Vendored
+182
@@ -0,0 +1,182 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager slave connection management for slave.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_adv.h"
|
||||
#include "l2c_api.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Call application callback with the connection update complete event.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param status Status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnUpdateCback(dmConnCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
hciLeConnUpdateCmplEvt_t evt;
|
||||
|
||||
/* call callback */
|
||||
evt.hdr.event = DM_CONN_UPDATE_IND;
|
||||
evt.hdr.param = pCcb->connId;
|
||||
evt.status = evt.hdr.status = status;
|
||||
evt.handle = pCcb->handle;
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_APP])((dmEvt_t *) &evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update a connection as a slave.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActUpdateSlave(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
if ((pCcb->features & HCI_LE_SUP_FEAT_CONN_PARAM_REQ_PROC) &&
|
||||
(HciGetLeSupFeat() & HCI_LE_SUP_FEAT_CONN_PARAM_REQ_PROC))
|
||||
{
|
||||
HciLeConnUpdateCmd(pCcb->handle, &pMsg->apiUpdate.connSpec);
|
||||
}
|
||||
/* else if L2CAP connection update not already in progress */
|
||||
else if (!pCcb->updating)
|
||||
{
|
||||
pCcb->updating = TRUE;
|
||||
|
||||
/* send request via L2CAP */
|
||||
L2cDmConnUpdateReq(pCcb->handle, &pMsg->apiUpdate.connSpec);
|
||||
}
|
||||
/* else L2CAP connection update pending */
|
||||
else
|
||||
{
|
||||
/* call callback */
|
||||
dmConnUpdateCback(pCcb, (uint8_t) HCI_ERR_CMD_DISALLOWED);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an L2CAP connection update confirm.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActL2cUpdateCnf(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
/* if connection update in progress */
|
||||
if (pCcb->updating)
|
||||
{
|
||||
pCcb->updating = FALSE;
|
||||
|
||||
/* if reason indicates failure */
|
||||
if (pMsg->l2cUpdateCnf.result != L2C_CONN_PARAM_ACCEPTED)
|
||||
{
|
||||
/* call callback */
|
||||
dmConnUpdateCback(pCcb, (uint8_t) pMsg->l2cUpdateCnf.result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only. L2C calls this function to send the result of an L2CAP
|
||||
* connection update response to DM.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
* \param result Connection update result code.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmL2cConnUpdateCnf(uint16_t handle, uint16_t result)
|
||||
{
|
||||
dmConnL2cUpdateCnf_t updateCnf;
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
if ((pCcb = dmConnCcbByHandle(handle)) != NULL)
|
||||
{
|
||||
updateCnf.hdr.event = DM_CONN_MSG_L2C_UPDATE_CNF;
|
||||
updateCnf.result = result;
|
||||
|
||||
dmConnSmExecute(pCcb, (dmConnMsg_t *) &updateCnf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only. L2C calls this function to send the result of an L2CAP
|
||||
* Command Reject indication up to the application.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
* \param result Connection update result code.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmL2cCmdRejInd(uint16_t handle, uint16_t result)
|
||||
{
|
||||
dmL2cCmdRejEvt_t evt;
|
||||
|
||||
/* call callback */
|
||||
evt.hdr.event = DM_L2C_CMD_REJ_IND;
|
||||
evt.hdr.status = HCI_SUCCESS;
|
||||
evt.reason = result;
|
||||
evt.handle = handle;
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_APP])((dmEvt_t *)&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Accept a connection from the given peer device by initiating directed advertising.
|
||||
*
|
||||
* \param clientId The client identifier.
|
||||
* \param advHandle Advertising handle.
|
||||
* \param advType Advertising type.
|
||||
* \param duration Advertising duration (in ms).
|
||||
* \param maxEaEvents Maximum number of extended advertising events.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmConnId_t DmConnAccept(uint8_t clientId, uint8_t advHandle, uint8_t advType, uint16_t duration,
|
||||
uint8_t maxEaEvents, uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
return dmConnOpenAccept(clientId, 0, advHandle, advType, duration, maxEaEvents, addrType, pAddr,
|
||||
DM_ROLE_SLAVE);
|
||||
}
|
||||
Vendored
+126
@@ -0,0 +1,126 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management for extended slave.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_adv.h"
|
||||
#include "l2c_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Action set for this module */
|
||||
static const dmConnAct_t dmConnActSetSlave[] =
|
||||
{
|
||||
dmExtConnSmActAccept,
|
||||
dmExtConnSmActCancelAccept,
|
||||
dmConnSmActUpdateSlave,
|
||||
dmExtConnSmActConnAccepted,
|
||||
dmExtConnSmActAcceptFailed,
|
||||
dmConnSmActL2cUpdateCnf
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Accept a connection.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtConnSmActAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmExtAdvStartDirected(pCcb->connId, pMsg->apiOpen.advHandle, pMsg->apiOpen.advType,
|
||||
pMsg->apiOpen.duration, pMsg->apiOpen.maxEaEvents, pMsg->apiOpen.addrType,
|
||||
pMsg->apiOpen.peerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel a connection accept.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtConnSmActCancelAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmExtAdvStopDirected(pCcb->connId);
|
||||
|
||||
dmConnSmActConnFailed(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection accepted.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtConnSmActConnAccepted(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmExtAdvConnected(pCcb->connId);
|
||||
|
||||
dmConnSmActConnOpened(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection accept failed.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtConnSmActAcceptFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmExtAdvConnectFailed(pCcb->connId);
|
||||
|
||||
dmConnSmActConnFailed(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM connection manager for operation as extended slave.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmExtConnSlaveInit(void)
|
||||
{
|
||||
dmConnActSet[DM_CONN_ACT_SET_SLAVE] = (dmConnAct_t *) dmConnActSetSlave;
|
||||
}
|
||||
Vendored
+125
@@ -0,0 +1,125 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management for legacy slave.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_adv.h"
|
||||
#include "l2c_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Action set for this module */
|
||||
static const dmConnAct_t dmConnActSetSlave[] =
|
||||
{
|
||||
dmConnSmActAccept,
|
||||
dmConnSmActCancelAccept,
|
||||
dmConnSmActUpdateSlave,
|
||||
dmConnSmActConnAccepted,
|
||||
dmConnSmActAcceptFailed,
|
||||
dmConnSmActL2cUpdateCnf
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Accept a connection.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmAdvStartDirected(pMsg->apiOpen.advType, pMsg->apiOpen.duration, pMsg->apiOpen.addrType,
|
||||
pMsg->apiOpen.peerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel a connection accept.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActCancelAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmAdvStopDirected();
|
||||
|
||||
dmConnSmActConnFailed(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection accepted.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActConnAccepted(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmAdvConnected();
|
||||
|
||||
dmConnSmActConnOpened(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection accept failed.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActAcceptFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmAdvConnectFailed();
|
||||
|
||||
dmConnSmActConnFailed(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM connection manager for operation as legacy slave.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnSlaveInit(void)
|
||||
{
|
||||
dmConnActSet[DM_CONN_ACT_SET_SLAVE] = (dmConnAct_t *) dmConnActSetSlave;
|
||||
}
|
||||
Vendored
+187
@@ -0,0 +1,187 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management state machine.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Column position of next state */
|
||||
#define DM_CONN_NEXT_STATE 0
|
||||
|
||||
/*! Column position of action */
|
||||
#define DM_CONN_ACTION 1
|
||||
|
||||
/*! Number of columns in the state machine state tables */
|
||||
#define DM_CONN_NUM_COLS 2
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM Conn state machine state tables */
|
||||
static const uint8_t dmConnStateTbl[DM_CONN_SM_NUM_STATES][DM_CONN_NUM_MSGS][DM_CONN_NUM_COLS] =
|
||||
{
|
||||
/* Idle state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_OPEN */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_OPEN},
|
||||
/* API_CLOSE */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* API_ACCEPT */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_ACCEPT},
|
||||
/* API_UPDATE_MASTER */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_SLAVE */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_IND */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_CNF */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL_FAIL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_CONN_ACCEPTED},
|
||||
/* HCI_DISCONNECT_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_UPDATE_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* INT_UPDATE_TIMEOUT */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE}
|
||||
},
|
||||
/* Connecting state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_OPEN */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_CLOSE */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_CANCEL_OPEN},
|
||||
/* API_ACCEPT */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_MASTER */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_SLAVE */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_IND */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_CNF */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL_FAIL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CONN_FAILED},
|
||||
/* HCI_LE_CONN_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_CONN_OPENED},
|
||||
/* HCI_DISCONNECT_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CONN_FAILED},
|
||||
/* HCI_LE_CONN_UPDATE_CMPL */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* INT_UPDATE_TIMEOUT */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE}
|
||||
},
|
||||
/* Accepting state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_OPEN */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_CLOSE */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CANCEL_ACCEPT},
|
||||
/* API_ACCEPT */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_MASTER */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_SLAVE */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_IND */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_CNF */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL_FAIL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_ACCEPT_FAILED},
|
||||
/* HCI_LE_CONN_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_CONN_ACCEPTED},
|
||||
/* HCI_DISCONNECT_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_ACCEPT_FAILED},
|
||||
/* HCI_LE_CONN_UPDATE_CMPL */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* INT_UPDATE_TIMEOUT */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE}
|
||||
},
|
||||
/* Connected state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_OPEN */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_NONE},
|
||||
/* API_CLOSE */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_CLOSE},
|
||||
/* API_ACCEPT */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_MASTER */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_UPDATE_MASTER},
|
||||
/* API_UPDATE_SLAVE */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_UPDATE_SLAVE},
|
||||
/* L2C_UPDATE_IND */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_L2C_UPDATE_IND},
|
||||
/* L2C_UPDATE_CNF */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_L2C_UPDATE_CNF},
|
||||
/* HCI_LE_CONN_CMPL_FAIL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_DISCONNECT_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CONN_CLOSED},
|
||||
/* HCI_LE_CONN_UPDATE_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_HCI_UPDATED},
|
||||
/* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_NONE},
|
||||
/* INT_UPDATE_TIMEOUT */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_HCI_UPDATED}
|
||||
},
|
||||
/* Disconnecting state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_OPEN */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_CLOSE */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_ACCEPT */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_MASTER */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_SLAVE */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_IND */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_CNF */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL_FAIL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CONN_CLOSED},
|
||||
/* HCI_LE_CONN_CMPL */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_CLOSE},
|
||||
/* HCI_DISCONNECT_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CONN_CLOSED},
|
||||
/* HCI_LE_CONN_UPDATE_CMPL */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* INT_UPDATE_TIMEOUT */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE}
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! State machine action set array */
|
||||
dmConnAct_t *dmConnActSet[DM_CONN_NUM_ACT_SETS];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the DM connection state machine.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmExecute(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmConnAct_t *actSet;
|
||||
uint8_t action;
|
||||
uint8_t event;
|
||||
|
||||
DM_TRACE_INFO2("dmConnSmExecute event=%d state=%d", pMsg->hdr.event, pCcb->state);
|
||||
|
||||
/* get the event */
|
||||
event = DM_MSG_MASK(pMsg->hdr.event);
|
||||
|
||||
/* get action */
|
||||
action = dmConnStateTbl[pCcb->state][event][DM_CONN_ACTION];
|
||||
|
||||
/* set next state */
|
||||
pCcb->state = dmConnStateTbl[pCcb->state][event][DM_CONN_NEXT_STATE];
|
||||
|
||||
/* look up action set */
|
||||
actSet = dmConnActSet[DM_CONN_ACT_SET_ID(action)];
|
||||
|
||||
/* if action set present */
|
||||
if (actSet != NULL)
|
||||
{
|
||||
/* execute action function in action set */
|
||||
(*actSet[DM_CONN_ACT_ID(action)])(pCcb, pMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no action */
|
||||
dmConnSmActNone(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM local device management module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmDevAct_t dmDevAct[] =
|
||||
{
|
||||
dmDevActReset
|
||||
};
|
||||
|
||||
/* Component function interface */
|
||||
const dmFcnIf_t dmDevFcnIf =
|
||||
{
|
||||
dmEmptyReset,
|
||||
dmDevHciHandler,
|
||||
dmDevMsgHandler
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
dmDevCb_t dmDevCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevActReset(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if DM not resetting */
|
||||
if (!dmCb.resetting)
|
||||
{
|
||||
/* set resetting state */
|
||||
dmCb.resetting = TRUE;
|
||||
|
||||
/* for each DM component */
|
||||
for (i = 0; i < DM_NUM_IDS; i++)
|
||||
{
|
||||
/* call component's reset function */
|
||||
(*(dmFcnIfTbl[i]->reset))();
|
||||
}
|
||||
|
||||
/* start HCI reset sequence */
|
||||
HciResetSequence();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a reset complete event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevHciEvtReset(hciEvt_t *pEvent)
|
||||
{
|
||||
/* reset resetting state */
|
||||
dmCb.resetting = FALSE;
|
||||
|
||||
pEvent->hdr.event = DM_RESET_CMPL_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a vendor specific event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevHciEvtVendorSpec(hciEvt_t *pEvent)
|
||||
{
|
||||
pEvent->hdr.event = DM_VENDOR_SPEC_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an hardware error event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevHciEvtHwError(hciEvt_t *pEvent)
|
||||
{
|
||||
pEvent->hdr.event = DM_HW_ERROR_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM dev HCI event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
switch (pEvent->hdr.event)
|
||||
{
|
||||
case HCI_RESET_SEQ_CMPL_CBACK_EVT:
|
||||
dmDevHciEvtReset(pEvent);
|
||||
break;
|
||||
|
||||
case HCI_VENDOR_SPEC_CBACK_EVT:
|
||||
dmDevHciEvtVendorSpec(pEvent);
|
||||
break;
|
||||
|
||||
case HCI_HW_ERROR_CBACK_EVT:
|
||||
dmDevHciEvtHwError(pEvent);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* ignore event */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM dev event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmDevAct[DM_MSG_MASK(pMsg->event)])(pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pass an event to the device privacy module.
|
||||
*
|
||||
* \param event Device privacy event.
|
||||
* \param param DM or Privacy event.
|
||||
* \param advHandle Advertising handle.
|
||||
* \param connectable TRUE if connectable extended advertising. FALSE, otherwise.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPassEvtToDevPriv(uint8_t event, uint8_t param, uint8_t advHandle, bool_t connectable)
|
||||
{
|
||||
dmDevPrivMsg_t evt;
|
||||
|
||||
DM_TRACE_INFO3("dmDevPassEvtToDevPriv: event: %d, param: %d, advHandle: %d", event, param, advHandle);
|
||||
|
||||
/* build event */
|
||||
evt.hdr.event = event;
|
||||
evt.hdr.param = param;
|
||||
evt.privCtrl.advHandle = advHandle;
|
||||
evt.privCtrl.connectable = connectable;
|
||||
|
||||
/* pass event to device privacy */
|
||||
(*(dmFcnIfTbl[DM_ID_DEV_PRIV]->msgHandler))((wsfMsgHdr_t *) &evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pass a connection state change event to the Connection CTE module.
|
||||
*
|
||||
* \param state Connection state.
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPassEvtToConnCte(uint8_t state, dmConnId_t connId)
|
||||
{
|
||||
wsfMsgHdr_t evt;
|
||||
|
||||
/* build event */
|
||||
evt.event = DM_CONN_CTE_MSG_STATE;
|
||||
evt.status = state;
|
||||
evt.param = connId;
|
||||
|
||||
/* pass event to Connection CTE */
|
||||
(*(dmFcnIfTbl[DM_ID_CONN_CTE]->msgHandler))(&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the device.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevReset(void)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->event = DM_DEV_MSG_API_RESET;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the random address to be used by the local device.
|
||||
*
|
||||
* \param pAddr Random address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevSetRandAddr(uint8_t *pAddr)
|
||||
{
|
||||
BdaCpy(dmCb.localAddr, pAddr);
|
||||
HciLeSetRandAddrCmd(pAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add a peer device to the white list. Note that this function cannot be called
|
||||
* while advertising, scanning, or connecting with white list filtering active.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevWhiteListAdd(uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
HciLeAddDevWhiteListCmd(addrType, pAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove a peer device from the white list. Note that this function cannot be called
|
||||
* while advertising, scanning, or connecting with white list filtering active.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevWhiteListRemove(uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
HciLeRemoveDevWhiteListCmd(addrType, pAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear the white list. Note that this function cannot be called while
|
||||
* advertising, scanning, or connecting with white list filtering active.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevWhiteListClear(void)
|
||||
{
|
||||
HciLeClearWhiteListCmd();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the Advertising, Scanning or Initiator filter policy.
|
||||
*
|
||||
* \param advHandle Advertising handle (only applicable to advertising).
|
||||
* \param mode Policy mode.
|
||||
* \param policy Filter policy.
|
||||
*
|
||||
* \return TRUE if the filter policy was successfully set, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t dmDevSetFilterPolicy(uint8_t advHandle, uint8_t mode, uint8_t policy)
|
||||
{
|
||||
bool_t policySet = FALSE;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case DM_FILT_POLICY_MODE_ADV:
|
||||
/* if Advertising filter policy is valid */
|
||||
if (policy <= HCI_ADV_FILT_ALL)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
/* update the filter policy */
|
||||
dmCb.advFiltPolicy[advHandle] = policy;
|
||||
policySet = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_FILT_POLICY_MODE_SCAN:
|
||||
/* if Scanning filter policy is valid */
|
||||
if (policy <= HCI_FILT_WHITE_LIST_RES_INIT)
|
||||
{
|
||||
/* update the filter policy */
|
||||
dmCb.scanFiltPolicy = policy;
|
||||
policySet = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_FILT_POLICY_MODE_INIT:
|
||||
/* if Initiator filter policy is valid */
|
||||
if (policy <= HCI_FILT_WHITE_LIST)
|
||||
{
|
||||
/* update the filter policy */
|
||||
dmCb.initFiltPolicy = policy;
|
||||
policySet = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_FILT_POLICY_MODE_SYNC:
|
||||
/* if Synchronization filter policy is valid */
|
||||
if (policy <= HCI_FILT_PER_ADV_LIST)
|
||||
{
|
||||
/* clear the filter policy bit */
|
||||
dmCb.syncOptions &= ~HCI_OPTIONS_FILT_POLICY_BIT;
|
||||
|
||||
/* set the filter policy bit */
|
||||
dmCb.syncOptions |= policy;
|
||||
policySet = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* invalid filter policy mode */
|
||||
break;
|
||||
}
|
||||
|
||||
return policySet;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the Advertising, Scanning or Initiator filter policy.
|
||||
*
|
||||
* \param mode Policy mode.
|
||||
* \param policy Filter policy.
|
||||
*
|
||||
* \return TRUE if the filter policy was successfully set, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmDevSetFilterPolicy(uint8_t mode, uint8_t policy)
|
||||
{
|
||||
return dmDevSetFilterPolicy(DM_ADV_HANDLE_DEFAULT, mode, policy);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the Advertising filter policy for the given advertising, Scanning or Initiator
|
||||
* filter policy.
|
||||
*
|
||||
* \param advHandle Advertising handle (only applicable to advertising).
|
||||
* \param mode Policy mode.
|
||||
* \param policy Filter policy.
|
||||
*
|
||||
* \return TRUE if the filter policy was successfully set, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmDevSetExtFilterPolicy(uint8_t advHandle, uint8_t mode, uint8_t policy)
|
||||
{
|
||||
return dmDevSetFilterPolicy(advHandle, mode, policy);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Vendor-specific controller initialization function.
|
||||
*
|
||||
* \param param Vendor-specific parameter.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevVsInit(uint8_t param)
|
||||
{
|
||||
HciVsInit(param);
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM local device management module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_DEV_H
|
||||
#define DM_DEV_H
|
||||
|
||||
#include "dm_main.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM device event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_DEV_MSG_API_RESET = DM_MSG_START(DM_ID_DEV)
|
||||
};
|
||||
|
||||
/* DM device privacy event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_DEV_PRIV_MSG_API_START = DM_MSG_START(DM_ID_DEV_PRIV),
|
||||
DM_DEV_PRIV_MSG_API_STOP,
|
||||
DM_DEV_PRIV_MSG_TIMEOUT,
|
||||
DM_DEV_PRIV_MSG_AES_CMPL,
|
||||
DM_DEV_PRIV_MSG_RPA_START,
|
||||
DM_DEV_PRIV_MSG_RPA_STOP,
|
||||
DM_DEV_PRIV_MSG_CTRL,
|
||||
};
|
||||
|
||||
/* DM device privacy control messages */
|
||||
enum
|
||||
{
|
||||
DM_DEV_PRIV_MSG_CONN_INIT_START, /* connection initiation started */
|
||||
DM_DEV_PRIV_MSG_CONN_INIT_STOP, /* connection initiation stopped */
|
||||
DM_DEV_PRIV_MSG_ADV_SET_ADD, /* advertising set created */
|
||||
DM_DEV_PRIV_MSG_ADV_SET_REMOVE, /* advertising set removed */
|
||||
DM_DEV_PRIV_MSG_ADV_SETS_CLEAR /* advertising sets cleared */
|
||||
};
|
||||
|
||||
/*! DM connection CTE event handler messages */
|
||||
enum
|
||||
{
|
||||
/* messages from API */
|
||||
DM_CONN_CTE_MSG_API_RX_SAMPLE_START = DM_MSG_START(DM_ID_CONN_CTE), /*!< Start sampling received CTE, and configure CTE Rx parameters to be used */
|
||||
DM_CONN_CTE_MSG_API_RX_SAMPLE_STOP, /*!< Stop sampling received CTE */
|
||||
DM_CONN_CTE_MSG_API_TX_CFG, /*!< Configure CTE Tx parameters */
|
||||
DM_CONN_CTE_MSG_API_REQ_START, /*!< Start initiating CTE request */
|
||||
DM_CONN_CTE_MSG_API_REQ_STOP, /*!< Stop initiating CTE request */
|
||||
DM_CONN_CTE_MSG_API_RSP_START, /*!< Start responding to CTE request */
|
||||
DM_CONN_CTE_MSG_API_RSP_STOP, /*!< Stop responding to CTE request */
|
||||
DM_CONN_CTE_MSG_STATE /*!< DM connection state change event */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data structure for DM_DEV_PRIV_MSG_API_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint16_t changeInterval;
|
||||
} dmDevPrivApiStart_t;
|
||||
|
||||
/* Data structure for DM_DEV_PRIV_MSG_CTRL */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
bool_t connectable;
|
||||
} dmDevPrivCtrl_t;
|
||||
|
||||
/* Union of all dev priv messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmDevPrivApiStart_t apiPrivStart;
|
||||
dmDevPrivCtrl_t privCtrl;
|
||||
secAes_t aes;
|
||||
} dmDevPrivMsg_t;
|
||||
|
||||
/* Action function */
|
||||
typedef void (*dmDevAct_t)(wsfMsgHdr_t *pMsg);
|
||||
typedef void (*dmDevPrivAct_t)(dmDevPrivMsg_t *pMsg);
|
||||
|
||||
/* DM device set advertising set random address callback type */
|
||||
typedef void (*dmDevAdvSetRandAddrCback_t)(uint8_t advHandle, const uint8_t *pAddr);
|
||||
|
||||
/* Main control block of the DM Dev subsystem */
|
||||
typedef struct
|
||||
{
|
||||
/* Set advertising set random address callback */
|
||||
dmDevAdvSetRandAddrCback_t advSetRandAddrCback;
|
||||
} dmDevCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface */
|
||||
extern const dmFcnIf_t dmDevFcnIf;
|
||||
|
||||
/* Control block */
|
||||
extern dmDevCb_t dmDevCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* dev component inteface */
|
||||
void dmDevActReset(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/* dev action functions */
|
||||
void dmDevMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmDevHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* dev priv component inteface */
|
||||
void dmDevPrivHciHandler(hciEvt_t *pEvent);
|
||||
void dmDevPrivMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmDevPrivReset(void);
|
||||
|
||||
/* dev priv action functions */
|
||||
void dmDevPrivActStart(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActStop(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActTimeout(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActAesCmpl(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActRpaStart(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActRpaStop(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActCtrl(dmDevPrivMsg_t *pMsg);
|
||||
|
||||
/* utility function */
|
||||
void dmDevPassEvtToDevPriv(uint8_t event, uint8_t param, uint8_t advHandle, bool_t connectable);
|
||||
void dmDevPassEvtToConnCte(uint8_t state, dmConnId_t connId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_DEV_H */
|
||||
Vendored
+653
@@ -0,0 +1,653 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager device privacy module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Structure for extended advertising */
|
||||
typedef struct
|
||||
{
|
||||
bool_t configured;
|
||||
bool_t connectable;
|
||||
bool_t advertising;
|
||||
} dmDevPrivExtAdv_t;
|
||||
|
||||
/* Control block for device privacy module */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t addrTimer;
|
||||
bool_t addrTimerStarted;
|
||||
uint8_t prand[DM_PRIV_PRAND_LEN];
|
||||
uint16_t changeInterval;
|
||||
bool_t useResolvable;
|
||||
bool_t addrInitialized;
|
||||
bdAddr_t pendingAddr;
|
||||
bool_t advertising;
|
||||
bool_t scanning;
|
||||
bool_t connecting;
|
||||
bool_t connected;
|
||||
dmDevPrivExtAdv_t extAdv[DM_NUM_ADV_SETS];
|
||||
} dmDevPrivCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmDevPrivAct_t dmDevPrivAct[] =
|
||||
{
|
||||
dmDevPrivActStart,
|
||||
dmDevPrivActStop,
|
||||
dmDevPrivActTimeout,
|
||||
dmDevPrivActAesCmpl,
|
||||
dmDevPrivActRpaStart,
|
||||
dmDevPrivActRpaStop,
|
||||
dmDevPrivActCtrl
|
||||
};
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmDevPrivFcnIf =
|
||||
{
|
||||
dmDevPrivReset,
|
||||
dmDevPrivHciHandler,
|
||||
dmDevPrivMsgHandler
|
||||
};
|
||||
|
||||
/* control block */
|
||||
static dmDevPrivCb_t dmDevPrivCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Whether the legacy advertising or an advertising set using (non)connectable advertising
|
||||
* is enabled.
|
||||
*
|
||||
* \param nonconnectable Non-connectable advertising.
|
||||
*
|
||||
* \return TRUE if advertising is enabled. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t dmDevPrivAdvertising(bool_t nonconnectable)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if legacy advertising enabled */
|
||||
if (dmDevPrivCb.advertising)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* if (non)connectable extended advertising is enabled */
|
||||
for (i = 0; i < DM_NUM_ADV_SETS; i++)
|
||||
{
|
||||
if (dmDevPrivCb.extAdv[i].configured &&
|
||||
dmDevPrivCb.extAdv[i].advertising &&
|
||||
(dmDevPrivCb.extAdv[i].connectable || nonconnectable))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the address generation timer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevPrivTimerStart(void)
|
||||
{
|
||||
/* if LL Privacy not enabled and device advertising, scanning or connected */
|
||||
if (!dmCb.llPrivEnabled && (dmDevPrivAdvertising(TRUE) || dmDevPrivCb.scanning || dmDevPrivCb.connected))
|
||||
{
|
||||
/* start address generation timer */
|
||||
dmDevPrivCb.addrTimerStarted = TRUE;
|
||||
dmDevPrivCb.addrTimer.msg.event = DM_DEV_PRIV_MSG_TIMEOUT;
|
||||
WsfTimerStartSec(&dmDevPrivCb.addrTimer, dmDevPrivCb.changeInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start resolvable address calculation.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevPrivAddrCalc(void)
|
||||
{
|
||||
uint8_t buf[DM_PRIV_PLAINTEXT_LEN];
|
||||
|
||||
/* get random number */
|
||||
SecRand(buf, DM_PRIV_PRAND_LEN);
|
||||
|
||||
/* set address type in random number */
|
||||
buf[2] = (buf[2] & 0x3F) | DM_RAND_ADDR_RESOLV;
|
||||
|
||||
/* pad buffer */
|
||||
memset(buf + DM_PRIV_PRAND_LEN, 0, (DM_PRIV_PLAINTEXT_LEN - DM_PRIV_PRAND_LEN));
|
||||
|
||||
/* run calculation */
|
||||
SecAes(DmSecGetLocalIrk(), buf, dmCb.handlerId, 0, DM_DEV_PRIV_MSG_AES_CMPL);
|
||||
|
||||
/* store random number */
|
||||
memcpy(dmDevPrivCb.prand, buf, DM_PRIV_PRAND_LEN);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the RPA to be used by the local device.
|
||||
*
|
||||
* \param pAddr New RPA.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevPrivSetRpa(uint8_t *pAddr)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* set new RPA as local address */
|
||||
BdaCpy(dmCb.localAddr, pAddr);
|
||||
|
||||
/* set RPA in device */
|
||||
HciLeSetRandAddrCmd(dmCb.localAddr);
|
||||
|
||||
if (dmDevCb.advSetRandAddrCback)
|
||||
{
|
||||
for (i = 0; i < DM_NUM_ADV_SETS; i++)
|
||||
{
|
||||
if (dmDevPrivCb.extAdv[i].configured)
|
||||
{
|
||||
/* set RPA for advertising set */
|
||||
(*dmDevCb.advSetRandAddrCback)(i, dmCb.localAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the pending RPA to be used by the local device.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevPrivSetPendingRpa(void)
|
||||
{
|
||||
/* if not advertising, scanning or initiating connection, and new RPA has been generated */
|
||||
if (!(dmDevPrivAdvertising(FALSE) || dmDevPrivCb.scanning || dmDevPrivCb.connecting) &&
|
||||
!BdaIsZeros(dmDevPrivCb.pendingAddr))
|
||||
{
|
||||
/* use pending RPA as local address */
|
||||
dmDevPrivSetRpa(dmDevPrivCb.pendingAddr);
|
||||
|
||||
/* clear out pending RPA */
|
||||
memset(dmDevPrivCb.pendingAddr, 0, BDA_ADDR_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start device privacy action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActStart(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
/* initialize and store parameters */
|
||||
dmDevPrivCb.useResolvable = TRUE;
|
||||
dmDevPrivCb.changeInterval = pMsg->apiPrivStart.changeInterval;
|
||||
dmDevPrivCb.addrTimer.handlerId = dmCb.handlerId;
|
||||
|
||||
/* set the local address type to random */
|
||||
DmAdvSetAddrType(DM_ADDR_RANDOM);
|
||||
DmScanSetAddrType(DM_ADDR_RANDOM);
|
||||
DmConnSetAddrType(DM_ADDR_RANDOM);
|
||||
|
||||
/* start the address generation timer if applicable */
|
||||
if (dmDevPrivCb.changeInterval > 0)
|
||||
{
|
||||
/* start address generation timer */
|
||||
dmDevPrivTimerStart();
|
||||
|
||||
/* if LL Privacy is supported */
|
||||
if (HciLlPrivacySupported())
|
||||
{
|
||||
/* Set LL resolvable private address timeout */
|
||||
DmPrivSetResolvablePrivateAddrTimeout(dmDevPrivCb.changeInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/* if private address has never been generated */
|
||||
if (dmDevPrivCb.addrInitialized == FALSE)
|
||||
{
|
||||
/* start address calculation */
|
||||
dmDevPrivAddrCalc();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop device privacy action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActStop(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
/* stop address generation timer */
|
||||
dmDevPrivCb.addrTimerStarted = FALSE;
|
||||
WsfTimerStop(&dmDevPrivCb.addrTimer);
|
||||
|
||||
dmDevPrivCb.useResolvable = FALSE;
|
||||
memset(dmDevPrivCb.pendingAddr, 0, BDA_ADDR_LEN);
|
||||
|
||||
/* set the local address type to public */
|
||||
DmAdvSetAddrType(DM_ADDR_PUBLIC);
|
||||
DmScanSetAddrType(DM_ADDR_PUBLIC);
|
||||
DmConnSetAddrType(DM_ADDR_PUBLIC);
|
||||
|
||||
/* if LL Privacy is supported */
|
||||
if (HciLlPrivacySupported())
|
||||
{
|
||||
/* remove all devices from resolving list and disable LL Privacy */
|
||||
DmPrivClearResList();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a private address generation timeout.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActTimeout(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
/* if Host Privacy still enabled */
|
||||
if (dmDevPrivCb.useResolvable)
|
||||
{
|
||||
/* address generation timer has timed out */
|
||||
dmDevPrivCb.addrTimerStarted = FALSE;
|
||||
|
||||
/* restart address generation timer */
|
||||
dmDevPrivTimerStart();
|
||||
|
||||
/* start address calculation */
|
||||
dmDevPrivAddrCalc();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle AES calculation complete.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActAesCmpl(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
bdAddr_t localAddr;
|
||||
|
||||
if (dmDevPrivCb.useResolvable)
|
||||
{
|
||||
/* build and store address; hash is in ls bytes, random part in ms bytes */
|
||||
memcpy(localAddr, pMsg->aes.pCiphertext, DM_PRIV_HASH_LEN);
|
||||
memcpy(&localAddr[DM_PRIV_HASH_LEN], dmDevPrivCb.prand, DM_PRIV_PRAND_LEN);
|
||||
|
||||
/* set generated address as random resolvable */
|
||||
DM_RAND_ADDR_SET(localAddr, DM_RAND_ADDR_RESOLV);
|
||||
|
||||
/* if not advertising, scanning or initiating connection */
|
||||
if (!(dmDevPrivAdvertising(FALSE) || dmDevPrivCb.scanning || dmDevPrivCb.connecting))
|
||||
{
|
||||
/* use new RPA as local address */
|
||||
dmDevPrivSetRpa(localAddr);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* save new RPA for later */
|
||||
BdaCpy(dmDevPrivCb.pendingAddr, localAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a device privacy RPA start event.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActRpaStart(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
switch (pMsg->hdr.param)
|
||||
{
|
||||
case DM_ADV_START_IND:
|
||||
/* advertising started */
|
||||
dmDevPrivCb.advertising = TRUE;
|
||||
break;
|
||||
|
||||
case DM_ADV_SET_START_IND:
|
||||
/* extended advertising started */
|
||||
WSF_ASSERT(pMsg->privCtrl.advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
dmDevPrivCb.extAdv[pMsg->privCtrl.advHandle].advertising = TRUE;
|
||||
break;
|
||||
|
||||
case DM_SCAN_START_IND:
|
||||
case DM_EXT_SCAN_START_IND:
|
||||
/* scanning started */
|
||||
dmDevPrivCb.scanning = TRUE;
|
||||
break;
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
/* connection opened */
|
||||
dmDevPrivCb.connected = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* LL Privacy disabled */
|
||||
break;
|
||||
}
|
||||
|
||||
/* if Host Privacy enabled and address generation timer not already running */
|
||||
if (dmDevPrivCb.useResolvable && !dmDevPrivCb.addrTimerStarted)
|
||||
{
|
||||
/* start address generation timer */
|
||||
dmDevPrivTimerStart();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a device privacy RPA stop event.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActRpaStop(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
switch (pMsg->hdr.param)
|
||||
{
|
||||
case DM_ADV_STOP_IND:
|
||||
/* advertising stopped */
|
||||
dmDevPrivCb.advertising = FALSE;
|
||||
|
||||
/* use pending RPA */
|
||||
dmDevPrivSetPendingRpa();
|
||||
break;
|
||||
|
||||
case DM_ADV_SET_STOP_IND:
|
||||
WSF_ASSERT(pMsg->privCtrl.advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
/* extended advertising stopped */
|
||||
dmDevPrivCb.extAdv[pMsg->privCtrl.advHandle].advertising = FALSE;
|
||||
|
||||
/* use pending RPA */
|
||||
dmDevPrivSetPendingRpa();
|
||||
break;
|
||||
|
||||
case DM_SCAN_STOP_IND:
|
||||
case DM_EXT_SCAN_STOP_IND:
|
||||
/* scanning stopped */
|
||||
dmDevPrivCb.scanning = FALSE;
|
||||
|
||||
/* use pending RPA */
|
||||
dmDevPrivSetPendingRpa();
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
/* connection closed */
|
||||
dmDevPrivCb.connected = FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* LL Privacy enabled see if address generation timer running */
|
||||
if (dmDevPrivCb.addrTimerStarted)
|
||||
{
|
||||
/* stop address generation timer (LL will generate RPAs) */
|
||||
dmDevPrivCb.addrTimerStarted = FALSE;
|
||||
WsfTimerStop(&dmDevPrivCb.addrTimer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* let address generation timer timeout (if still running) and update address */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a device privacy control event.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActCtrl(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
switch (pMsg->hdr.param)
|
||||
{
|
||||
case DM_DEV_PRIV_MSG_CONN_INIT_START:
|
||||
/* connection initiation started */
|
||||
dmDevPrivCb.connecting = TRUE;
|
||||
break;
|
||||
|
||||
case DM_DEV_PRIV_MSG_CONN_INIT_STOP:
|
||||
/* connection initiation stopped */
|
||||
dmDevPrivCb.connecting = FALSE;
|
||||
|
||||
/* use pending RPA */
|
||||
dmDevPrivSetPendingRpa();
|
||||
break;
|
||||
|
||||
case DM_DEV_PRIV_MSG_ADV_SET_ADD:
|
||||
/* advertising set created */
|
||||
WSF_ASSERT(pMsg->privCtrl.advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
dmDevPrivCb.extAdv[pMsg->privCtrl.advHandle].configured = TRUE;
|
||||
dmDevPrivCb.extAdv[pMsg->privCtrl.advHandle].connectable = pMsg->privCtrl.connectable;
|
||||
|
||||
/* if Host Privacy enabled and private address has been generated */
|
||||
if (dmDevPrivCb.useResolvable && dmDevPrivCb.addrInitialized)
|
||||
{
|
||||
WSF_ASSERT(dmDevCb.advSetRandAddrCback != NULL);
|
||||
|
||||
/* set RPA for advertising set */
|
||||
(*dmDevCb.advSetRandAddrCback)(pMsg->privCtrl.advHandle, dmCb.localAddr);
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_DEV_PRIV_MSG_ADV_SET_REMOVE:
|
||||
/* advertising set removed */
|
||||
WSF_ASSERT(pMsg->privCtrl.advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
/* clear advertising set */
|
||||
/* note: advertising handle is checked before reaching this function */
|
||||
/* coverity[overrun-buffer-arg] */
|
||||
memset(&dmDevPrivCb.extAdv[pMsg->privCtrl.advHandle], 0, sizeof(dmDevPrivExtAdv_t));
|
||||
break;
|
||||
|
||||
case DM_DEV_PRIV_MSG_ADV_SETS_CLEAR:
|
||||
/* clear advertising sets */
|
||||
memset(dmDevPrivCb.extAdv, 0, sizeof(dmDevPrivCb.extAdv));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* unknown state message */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM device priv HCI callback event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
/* if Host Privacy enabled */
|
||||
if (dmDevPrivCb.useResolvable)
|
||||
{
|
||||
/* handle incoming event */
|
||||
if (pEvent->hdr.event == HCI_LE_SET_RAND_ADDR_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
dmAdvNewAddrIndEvt_t msg;
|
||||
|
||||
msg.hdr.event = DM_ADV_NEW_ADDR_IND;
|
||||
msg.hdr.status = pEvent->hdr.status;
|
||||
BdaCpy(msg.addr, dmCb.localAddr);
|
||||
msg.firstTime = !dmDevPrivCb.addrInitialized;
|
||||
|
||||
/* call callback */
|
||||
(*dmCb.cback)((dmEvt_t *) &msg);
|
||||
|
||||
dmDevPrivCb.addrInitialized = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM device privacy event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmDevPrivAct[DM_MSG_MASK(pMsg->event)])((dmDevPrivMsg_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the device privacy module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivReset(void)
|
||||
{
|
||||
if (dmDevPrivCb.useResolvable)
|
||||
{
|
||||
/* stop address generation timer */
|
||||
WsfTimerStop(&dmDevPrivCb.addrTimer);
|
||||
}
|
||||
|
||||
/* initialize control block */
|
||||
memset(&dmDevPrivCb, 0, sizeof(dmDevPrivCb));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize device privacy module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevPrivInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_DEV_PRIV] = (dmFcnIf_t *) &dmDevPrivFcnIf;
|
||||
|
||||
/* initialize set advertising set random address callback */
|
||||
dmDevCb.advSetRandAddrCback = NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start using a private resolvable address.
|
||||
*
|
||||
* \param changeInterval Interval between automatic address changes, in seconds.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevPrivStart(uint16_t changeInterval)
|
||||
{
|
||||
dmDevPrivApiStart_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmDevPrivApiStart_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_DEV_PRIV_MSG_API_START;
|
||||
pMsg->changeInterval = changeInterval;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop using a private resolvable address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevPrivStop(void)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->event = DM_DEV_PRIV_MSG_API_STOP;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
+580
@@ -0,0 +1,580 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_api.h"
|
||||
#include "l2c_defs.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_scan.h"
|
||||
#include "dm_adv.h"
|
||||
#include "dm_dev.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* HCI callback event routing table */
|
||||
static const uint8_t dmHciToIdTbl[] =
|
||||
{
|
||||
DM_ID_DEV, /* HCI_RESET_SEQ_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN, /* HCI_LE_CONN_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN, /* HCI_LE_ENHANCED_CONN_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN, /* HCI_DISCONNECT_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN, /* HCI_LE_CONN_UPDATE_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN, /* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SCAN, /* HCI_LE_ADV_REPORT_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_READ_RSSI_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_READ_CHAN_MAP_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_READ_TX_PWR_LVL_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_READ_REMOTE_VER_INFO_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_READ_REMOTE_FEAT_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_LTK_REQ_REPL_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_LTK_REQ_NEG_REPL_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_ENC_KEY_REFRESH_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_ENC_CHANGE_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_LTK_REQ_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_VENDOR_SPEC_CMD_STATUS_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_VENDOR_SPEC_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_VENDOR_SPEC_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_HW_ERROR_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_ADD_DEV_TO_RES_LIST_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_REM_DEV_FROM_RES_LIST_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_CLEAR_RES_LIST_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_READ_PEER_RES_ADDR_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_READ_LOCAL_RES_ADDR_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_SET_ADDR_RES_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_ENCRYPT_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_RAND_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_REM_CONN_PARAM_REP_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_REM_CONN_PARAM_NEG_REP_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_LE_READ_DEF_DATA_LEN_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_LE_WRITE_DEF_DATA_LEN_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_SET_DATA_LEN_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_LE_READ_MAX_DATA_LEN_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_REM_CONN_PARAM_REQ_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_DATA_LEN_CHANGE_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_READ_LOCAL_P256_PUB_KEY_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_GENERATE_DHKEY_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_WRITE_AUTH_PAYLOAD_TO_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_AUTH_PAYLOAD_TO_EXPIRED_EVT */
|
||||
DM_ID_PHY, /* HCI_LE_READ_PHY_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PHY, /* HCI_LE_SET_DEF_PHY_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PHY, /* HCI_LE_PHY_UPDATE_CMPL_CBACK_EVT */
|
||||
DM_ID_SCAN, /* HCI_LE_EXT_ADV_REPORT_CBACK_EVT */
|
||||
DM_ID_SCAN, /* HCI_LE_SCAN_TIMEOUT_CBACK_EVT */
|
||||
DM_ID_ADV, /* HCI_LE_ADV_SET_TERM_CBACK_EVT */
|
||||
DM_ID_ADV, /* HCI_LE_SCAN_REQ_RCVD_CBACK_EVT */
|
||||
DM_ID_SYNC, /* HCI_LE_PER_ADV_SYNC_EST_CBACK_EVT */
|
||||
DM_ID_SYNC, /* HCI_LE_PER_ADV_REPORT_CBACK_EVT */
|
||||
DM_ID_SYNC, /* HCI_LE_PER_ADV_SYNC_LOST_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_LE_CH_SEL_ALGO_CBACK_EVT */
|
||||
DM_ID_SCAN, /* HCI_LE_SCAN_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_ADV, /* HCI_LE_ADV_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SCAN, /* HCI_LE_EXT_SCAN_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_ADV, /* HCI_LE_EXT_ADV_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_ADV_PER, /* HCI_LE_PER_ADV_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_DEV_PRIV, /* HCI_LE_SET_RAND_ADDR_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SYNC, /* HCI_LE_PER_SYNC_TRSF_RCVD_CBACK_EVT */
|
||||
DM_ID_PAST, /* HCI_LE_PER_ADV_SYNC_TRSF_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PAST, /* HCI_LE_PER_ADV_SET_INFO_TRSF_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_CONN_IQ_REPORT_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_CTE_REQ_FAILED_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_SET_CONN_CTE_RX_PARAMS_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_SET_CONN_CTE_TX_PARAMS_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_CONN_CTE_REQ_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_CONN_CTE_RSP_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_CTE /* HCI_LE_READ_ANTENNA_INFO_CMD_CMPL_CBACK_EVT */
|
||||
};
|
||||
|
||||
/* DM callback event length table */
|
||||
static const uint16_t dmEvtCbackLen[] =
|
||||
{
|
||||
sizeof(wsfMsgHdr_t), /* DM_RESET_CMPL_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_ADV_START_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_ADV_STOP_IND */
|
||||
sizeof(dmAdvNewAddrIndEvt_t), /* DM_ADV_NEW_ADDR_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_SCAN_START_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_SCAN_STOP_IND */
|
||||
sizeof(hciLeAdvReportEvt_t), /* DM_SCAN_REPORT_IND */
|
||||
sizeof(hciLeConnCmplEvt_t), /* DM_CONN_OPEN_IND */
|
||||
sizeof(hciDisconnectCmplEvt_t), /* DM_CONN_CLOSE_IND */
|
||||
sizeof(hciLeConnUpdateCmplEvt_t), /* DM_CONN_UPDATE_IND */
|
||||
sizeof(dmSecPairCmplIndEvt_t), /* DM_SEC_PAIR_CMPL_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_SEC_PAIR_FAIL_IND */
|
||||
sizeof(dmSecEncryptIndEvt_t), /* DM_SEC_ENCRYPT_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_SEC_ENCRYPT_FAIL_IND */
|
||||
sizeof(dmSecAuthReqIndEvt_t), /* DM_SEC_AUTH_REQ_IND */
|
||||
sizeof(dmSecKeyIndEvt_t), /* DM_SEC_KEY_IND */
|
||||
sizeof(hciLeLtkReqEvt_t), /* DM_SEC_LTK_REQ_IND */
|
||||
sizeof(dmSecPairIndEvt_t), /* DM_SEC_PAIR_IND */
|
||||
sizeof(dmSecSlaveIndEvt_t), /* DM_SEC_SLAVE_REQ_IND */
|
||||
sizeof(dmSecOobCalcIndEvt_t), /* DM_SEC_CALC_OOB_IND */
|
||||
sizeof(secEccMsg_t), /* DM_SEC_ECC_KEY_IND */
|
||||
sizeof(dmSecCnfIndEvt_t), /* DM_SEC_COMPARE_IND */
|
||||
sizeof(dmSecKeypressIndEvt_t), /* DM_SEC_KEYPRESS_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_PRIV_RESOLVED_ADDR_IND */
|
||||
sizeof(dmPrivGenAddrIndEvt_t), /* DM_PRIV_GENERATE_RPA_IND */
|
||||
sizeof(hciReadRssiCmdCmplEvt_t), /* DM_CONN_READ_RSSI_IND */
|
||||
sizeof(hciLeAddDevToResListCmdCmplEvt_t), /* DM_PRIV_ADD_DEV_TO_RES_LIST_IND */
|
||||
sizeof(hciLeRemDevFromResListCmdCmplEvt_t), /* DM_PRIV_REM_DEV_FROM_RES_LIST_IND */
|
||||
sizeof(hciLeClearResListCmdCmplEvt_t), /* DM_PRIV_CLEAR_RES_LIST_IND */
|
||||
sizeof(hciLeReadPeerResAddrCmdCmplEvt_t), /* DM_PRIV_READ_PEER_RES_ADDR_IND */
|
||||
sizeof(hciLeReadLocalResAddrCmdCmplEvt_t), /* DM_PRIV_READ_LOCAL_RES_ADDR_IND */
|
||||
sizeof(hciLeSetAddrResEnableCmdCmplEvt_t), /* DM_PRIV_SET_ADDR_RES_ENABLE_IND */
|
||||
sizeof(hciLeRemConnParamReqEvt_t), /* DM_REM_CONN_PARAM_REQ_IND */
|
||||
sizeof(hciLeDataLenChangeEvt_t), /* DM_CONN_DATA_LEN_CHANGE_IND */
|
||||
sizeof(hciWriteAuthPayloadToCmdCmplEvt_t), /* DM_CONN_WRITE_AUTH_TO_IND */
|
||||
sizeof(hciAuthPayloadToExpiredEvt_t), /* DM_CONN_AUTH_TO_EXPIRED_IND */
|
||||
sizeof(hciLeReadPhyCmdCmplEvt_t), /* DM_PHY_READ_IND */
|
||||
sizeof(hciLeSetDefPhyCmdCmplEvt_t), /* DM_PHY_SET_DEF_IND */
|
||||
sizeof(hciLePhyUpdateEvt_t), /* DM_PHY_UPDATE_IND */
|
||||
sizeof(dmAdvSetStartEvt_t), /* DM_ADV_SET_START_IND */
|
||||
sizeof(hciLeAdvSetTermEvt_t), /* DM_ADV_SET_STOP_IND */
|
||||
sizeof(hciLeScanReqRcvdEvt_t), /* DM_SCAN_REQ_RCVD_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_EXT_SCAN_START_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_EXT_SCAN_STOP_IND */
|
||||
sizeof(hciLeExtAdvReportEvt_t), /* DM_EXT_SCAN_REPORT_IND */
|
||||
sizeof(dmPerAdvSetStartEvt_t), /* DM_PER_ADV_SET_START_IND */
|
||||
sizeof(dmPerAdvSetStopEvt_t), /* DM_PER_ADV_SET_STOP_IND */
|
||||
sizeof(hciLePerAdvSyncEstEvt_t), /* DM_PER_ADV_SYNC_EST_IND */
|
||||
sizeof(hciLePerAdvSyncEstEvt_t), /* DM_PER_ADV_SYNC_EST_FAIL_IND */
|
||||
sizeof(hciLePerAdvSyncLostEvt_t), /* DM_PER_ADV_SYNC_LOST_IND */
|
||||
sizeof(HciLePerAdvSyncTrsfRcvdEvt_t), /* DM_PER_ADV_SYNC_TRSF_EST_IND */
|
||||
sizeof(HciLePerAdvSyncTrsfRcvdEvt_t), /* DM_PER_ADV_SYNC_TRSF_EST_FAIL_IND */
|
||||
sizeof(hciLePerAdvSyncTrsfCmdCmplEvt_t), /* DM_PER_ADV_SYNC_TRSF_IND */
|
||||
sizeof(hciLePerAdvSetInfoTrsfCmdCmplEvt_t), /* DM_PER_ADV_SET_INFO_TRSF_IND */
|
||||
sizeof(hciLePerAdvReportEvt_t), /* DM_PER_ADV_REPORT_IND */
|
||||
sizeof(hciLeReadRemoteFeatCmplEvt_t), /* DM_REMOTE_FEATURES_IND */
|
||||
sizeof(hciReadRemoteVerInfoCmplEvt_t), /* DM_READ_REMOTE_VER_INFO_IND */
|
||||
sizeof(hciLeConnIQReportEvt_t), /* DM_CONN_IQ_REPORT_IND */
|
||||
sizeof(hciLeCteReqFailedEvt_t), /* DM_CTE_REQ_FAIL_IND */
|
||||
sizeof(hciLeSetConnCteRxParamsCmdCmplEvt_t), /* DM_CONN_CTE_RX_SAMPLE_START_IND */
|
||||
sizeof(hciLeSetConnCteRxParamsCmdCmplEvt_t), /* DM_CONN_CTE_RX_SAMPLE_START_IND */
|
||||
sizeof(hciLeSetConnCteTxParamsCmdCmplEvt_t), /* DM_CONN_CTE_TX_CFG_IND */
|
||||
sizeof(hciLeConnCteReqEnableCmdCmplEvt_t), /* DM_CONN_CTE_REQ_START_IND */
|
||||
sizeof(hciLeConnCteReqEnableCmdCmplEvt_t), /* DM_CONN_CTE_REQ_STOP_IND */
|
||||
sizeof(hciLeConnCteRspEnableCmdCmplEvt_t), /* DM_CONN_CTE_RSP_START_IND */
|
||||
sizeof(hciLeConnCteRspEnableCmdCmplEvt_t), /* DM_CONN_CTE_RSP_STOP_IND */
|
||||
sizeof(hciLeReadAntennaInfoCmdCmplEvt_t), /* DM_READ_ANTENNA_INFO_IND */
|
||||
sizeof(dmL2cCmdRejEvt_t), /* DM_L2C_CMD_REJ_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_ERROR_IND */
|
||||
sizeof(hciHwErrorEvt_t), /* DM_HW_ERROR_IND */
|
||||
sizeof(hciVendorSpecEvt_t) /* DM_VENDOR_SPEC_IND */
|
||||
};
|
||||
|
||||
/* Default component function inteface */
|
||||
static const dmFcnIf_t dmFcnDefault =
|
||||
{
|
||||
dmEmptyReset,
|
||||
(dmHciHandler_t) dmEmptyHandler,
|
||||
(dmMsgHandler_t) dmEmptyHandler
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface table indexed DM component ID */
|
||||
dmFcnIf_t *dmFcnIfTbl[DM_NUM_IDS] =
|
||||
{
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_ADV */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_DEV_PRIV */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_SCAN */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_CONN */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_CONN_2 */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_SEC */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_PRIV */
|
||||
(dmFcnIf_t *) &dmDevFcnIf, /* DM_ID_DEV */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_LESC */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_PHY */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_ADV_PER */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_SYNC */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_PAST */
|
||||
(dmFcnIf_t *) &dmFcnDefault /* DM_ID_CONN_CTE */
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
dmCb_t dmCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI event callback function.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmHciEvtCback(hciEvt_t *pEvent)
|
||||
{
|
||||
WSF_ASSERT(pEvent->hdr.event <= HCI_LE_READ_ANTENNA_INFO_CMD_CMPL_CBACK_EVT);
|
||||
|
||||
/* if DM not resetting or resetting but incoming event is HCI reset sequence complete event */
|
||||
if (!dmCb.resetting || (pEvent->hdr.event == HCI_RESET_SEQ_CMPL_CBACK_EVT))
|
||||
{
|
||||
/* route event to DM component handling function */
|
||||
(*(dmFcnIfTbl[dmHciToIdTbl[pEvent->hdr.event]]->hciHandler))(pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM empty reset handler.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmEmptyReset(void)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM empty event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmEmptyHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pass an HCI event to the DM connection management module.
|
||||
*
|
||||
* \param pEvent HCI event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPassHciEvtToConn(hciEvt_t *pEvent)
|
||||
{
|
||||
/* pass event to DM connection management module */
|
||||
(*(dmFcnIfTbl[DM_ID_CONN]->hciHandler))(pEvent);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback with DM for scan and advertising events.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmRegister(dmCback_t cback)
|
||||
{
|
||||
dmCb.cback = cback;
|
||||
|
||||
/* if LESC is enabled */
|
||||
if (dmFcnIfTbl[DM_ID_LESC] != &dmFcnDefault)
|
||||
{
|
||||
/* if largest LESC key length is larger than maximum RX PDU length */
|
||||
if (SMP_PUB_KEY_MSG_LEN > (HciGetMaxRxAclLen() - L2C_HDR_LEN))
|
||||
{
|
||||
dmEvt_t evt;
|
||||
|
||||
evt.hdr.param = 0;
|
||||
evt.hdr.event = DM_ERROR_IND;
|
||||
evt.hdr.status = DM_ERR_SMP_RX_PDU_LEN_EXCEEDED;
|
||||
|
||||
(*dmCb.cback)(&evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find an advertising data element in the given advertising or scan response data.
|
||||
*
|
||||
* \param adType Advertising data element type to find.
|
||||
* \param dataLen Data length.
|
||||
* \param pData Pointer to advertising or scan response data.
|
||||
*
|
||||
* \return Pointer to advertising data element byte array or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *DmFindAdType(uint8_t adType, uint16_t dataLen, uint8_t *pData)
|
||||
{
|
||||
/* while not at end of data and
|
||||
* data element length is not zero and
|
||||
* data element length is not erroneously more than the data length
|
||||
*/
|
||||
while ((dataLen != 0) && (pData[DM_AD_LEN_IDX] != 0) && (pData[DM_AD_LEN_IDX] < dataLen))
|
||||
{
|
||||
/* if found */
|
||||
if (pData[DM_AD_TYPE_IDX] == adType)
|
||||
{
|
||||
return pData;
|
||||
}
|
||||
|
||||
/* else go to next element */
|
||||
dataLen = dataLen - pData[DM_AD_LEN_IDX] - 1;
|
||||
pData = pData + pData[DM_AD_LEN_IDX] + 1;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for DM.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
/* store handler ID */
|
||||
dmCb.handlerId = handlerId;
|
||||
|
||||
dmCb.llPrivEnabled = FALSE;
|
||||
dmCb.resetting = FALSE;
|
||||
|
||||
/* register with the HCI event interface */
|
||||
HciEvtRegister(dmHciEvtCback);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for DM.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* Handle message */
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
WSF_ASSERT(DM_ID_FROM_MSG(pMsg->event) < DM_NUM_IDS);
|
||||
|
||||
/* if DM not resetting */
|
||||
if (!dmCb.resetting)
|
||||
{
|
||||
/* route message to DM component handling function */
|
||||
(*(dmFcnIfTbl[DM_ID_FROM_MSG(pMsg->event)]->msgHandler))(pMsg);
|
||||
}
|
||||
}
|
||||
/* Handle events */
|
||||
else if (event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Whether LL Privacy is enabled.
|
||||
*
|
||||
* \return TRUE if LL Privacy is enabled. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmLlPrivEnabled(void)
|
||||
{
|
||||
return dmCb.llPrivEnabled;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Map an address type to a type used by LL.
|
||||
*
|
||||
* \param addrType Address type used by Host.
|
||||
*
|
||||
* \return Address type used by LL.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t DmLlAddrType(uint8_t addrType)
|
||||
{
|
||||
uint8_t llAddrType = addrType;
|
||||
|
||||
/* if LL Privacy is enabled */
|
||||
if (dmCb.llPrivEnabled)
|
||||
{
|
||||
if (addrType == DM_ADDR_PUBLIC)
|
||||
{
|
||||
llAddrType = DM_ADDR_PUBLIC_IDENTITY;
|
||||
}
|
||||
else if (addrType == DM_ADDR_RANDOM)
|
||||
{
|
||||
llAddrType = DM_ADDR_RANDOM_IDENTITY;
|
||||
}
|
||||
}
|
||||
|
||||
return llAddrType;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Map an address type to a type used by Host.
|
||||
*
|
||||
* \param addrType Address type used by LL.
|
||||
*
|
||||
* \return Address type used by Host.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t DmHostAddrType(uint8_t addrType)
|
||||
{
|
||||
uint8_t hostAddrType = addrType;
|
||||
|
||||
/* if LL Privacy is enabled */
|
||||
if (dmCb.llPrivEnabled)
|
||||
{
|
||||
if (addrType == DM_ADDR_PUBLIC_IDENTITY)
|
||||
{
|
||||
hostAddrType = DM_ADDR_PUBLIC;
|
||||
}
|
||||
else if (addrType == DM_ADDR_RANDOM_IDENTITY)
|
||||
{
|
||||
hostAddrType = DM_ADDR_RANDOM;
|
||||
}
|
||||
}
|
||||
|
||||
return hostAddrType;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return size of a DM callback event.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return Size of DM callback event.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t DmSizeOfEvt(dmEvt_t *pDmEvt)
|
||||
{
|
||||
uint16_t len;
|
||||
|
||||
/* if a valid DM event ID */
|
||||
if ((pDmEvt->hdr.event >= DM_CBACK_START) && (pDmEvt->hdr.event <= DM_CBACK_END))
|
||||
{
|
||||
len = dmEvtCbackLen[pDmEvt->hdr.event - DM_CBACK_START];
|
||||
}
|
||||
else
|
||||
{
|
||||
len = sizeof(wsfMsgHdr_t);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the PHY index for the given scanner PHY.
|
||||
*
|
||||
* \param numPhys Number of scanner PHYs.
|
||||
* \param scanPhy Scanner PHY.
|
||||
*
|
||||
* \return PHY index.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t dmScanPhyToIdx(uint8_t numPhys, uint8_t scanPhy)
|
||||
{
|
||||
/* if number of supported PHYs is 1 */
|
||||
if (numPhys == 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if number of supported PHYs is 2 */
|
||||
if (numPhys == 2)
|
||||
{
|
||||
return (scanPhy == HCI_SCAN_PHY_LE_1M_BIT) ? 0 : 1;
|
||||
}
|
||||
|
||||
/* all three PHYs are supported */
|
||||
return (scanPhy == HCI_SCAN_PHY_LE_1M_BIT) ? 0 : (scanPhy == HCI_SCAN_PHY_LE_2M_BIT) ? 1 : 2;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the PHY index for the given scanner PHY.
|
||||
*
|
||||
* \param scanPhy Scanner PHY.
|
||||
*
|
||||
* \return PHY index.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t DmScanPhyToIdx(uint8_t scanPhy)
|
||||
{
|
||||
return dmScanPhyToIdx(DM_NUM_PHYS, scanPhy);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the PHY index for the given initiator PHY.
|
||||
*
|
||||
* \param numPhys Number of initiator PHYs.
|
||||
* \param initPhy Initiator PHY.
|
||||
*
|
||||
* \return PHY index.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t dmInitPhyToIdx(uint8_t numPhys, uint8_t initPhy)
|
||||
{
|
||||
/* if number of supported PHYs is 1 */
|
||||
if (numPhys == 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if number of supported PHYs is 2 */
|
||||
if (numPhys == 2)
|
||||
{
|
||||
return (initPhy == HCI_INIT_PHY_LE_1M_BIT) ? 0 : 1;
|
||||
}
|
||||
|
||||
/* all three PHYs are supported */
|
||||
return (initPhy == HCI_INIT_PHY_LE_1M_BIT) ? 0 : (initPhy == HCI_INIT_PHY_LE_2M_BIT) ? 1 : 2;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the PHY index for the given initiator PHY.
|
||||
*
|
||||
* \param initPhy Initiator PHY.
|
||||
*
|
||||
* \return PHY index.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t DmInitPhyToIdx(uint8_t initPhy)
|
||||
{
|
||||
return dmInitPhyToIdx(DM_NUM_PHYS, initPhy);
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_MAIN_H
|
||||
#define DM_MAIN_H
|
||||
|
||||
#include "util/bda.h"
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM component IDs */
|
||||
#define DM_ID_ADV 0
|
||||
#define DM_ID_DEV_PRIV 1
|
||||
#define DM_ID_SCAN 2
|
||||
#define DM_ID_CONN 3
|
||||
#define DM_ID_CONN_2 4
|
||||
#define DM_ID_SEC 5
|
||||
#define DM_ID_PRIV 6
|
||||
#define DM_ID_DEV 7
|
||||
#define DM_ID_LESC 8
|
||||
#define DM_ID_PHY 9
|
||||
#define DM_ID_ADV_PER 10
|
||||
#define DM_ID_SYNC 11
|
||||
#define DM_ID_PAST 12
|
||||
#define DM_ID_CONN_CTE 13
|
||||
#define DM_NUM_IDS 14
|
||||
|
||||
/* Start of component message enumeration */
|
||||
#define DM_MSG_START(id) ((id) << 4)
|
||||
|
||||
/* Get the component ID from a message ID */
|
||||
#define DM_ID_FROM_MSG(msg) ((msg) >> 4)
|
||||
|
||||
/* Mask off the ID from the message ID */
|
||||
#define DM_MSG_MASK(msg) ((msg) & 0x0F)
|
||||
|
||||
/* Length of hash part of private resolvable address */
|
||||
#define DM_PRIV_HASH_LEN 3
|
||||
|
||||
/* Length of random part of private resolvable address */
|
||||
#define DM_PRIV_PRAND_LEN 3
|
||||
|
||||
/* Length of plaintext used for private resolvable address calculation */
|
||||
#define DM_PRIV_PLAINTEXT_LEN 16
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM reset function type */
|
||||
typedef void (*dmReset_t)(void);
|
||||
|
||||
/* DM message handling function types */
|
||||
typedef void (*dmMsgHandler_t)(wsfMsgHdr_t *pMsg);
|
||||
typedef void (*dmHciHandler_t)(hciEvt_t *pEvent);
|
||||
|
||||
/* DM component reset and handler function interface */
|
||||
typedef struct
|
||||
{
|
||||
dmReset_t reset;
|
||||
dmHciHandler_t hciHandler;
|
||||
dmMsgHandler_t msgHandler;
|
||||
} dmFcnIf_t;
|
||||
|
||||
/* Main control block of the DM subsystem */
|
||||
typedef struct
|
||||
{
|
||||
bdAddr_t localAddr;
|
||||
dmCback_t cback;
|
||||
wsfHandlerId_t handlerId;
|
||||
uint8_t connAddrType;
|
||||
uint8_t advAddrType;
|
||||
uint8_t scanAddrType;
|
||||
bool_t resetting;
|
||||
|
||||
/* Filter policies for Advertising, Scanning, Initiator and Synchronization */
|
||||
uint8_t advFiltPolicy[DM_NUM_ADV_SETS];
|
||||
uint8_t scanFiltPolicy;
|
||||
uint8_t initFiltPolicy;
|
||||
|
||||
/* Options (filter policies and periodic advertising report enablement) for Synchronization */
|
||||
uint8_t syncOptions;
|
||||
|
||||
/* LL Privacy */
|
||||
bool_t llPrivEnabled;
|
||||
} dmCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface table indexed DM component ID */
|
||||
extern dmFcnIf_t *dmFcnIfTbl[DM_NUM_IDS];
|
||||
|
||||
/* Control block */
|
||||
extern dmCb_t dmCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void dmEmptyReset(void);
|
||||
void dmEmptyHandler(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/* utility functions */
|
||||
uint8_t DmScanPhyToIdx(uint8_t scanPhy);
|
||||
uint8_t DmInitPhyToIdx(uint8_t initPhy);
|
||||
void dmDevPassHciEvtToConn(hciEvt_t *pEvent);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_MAIN_H */
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager periodic advertising sync transfer (PAST) module.
|
||||
*
|
||||
* Copyright (c) 2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_adv.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_scan.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM past event handler messages */
|
||||
enum
|
||||
{
|
||||
/* messages from API */
|
||||
DM_PAST_MSG_API_RCV_ENABLE = DM_MSG_START(DM_ID_PAST), /*!< Enable receiving report */
|
||||
DM_PAST_MSG_API_SYNC_TRSF, /*!< Transfer sync */
|
||||
DM_PAST_MSG_API_SET_INFO_TRSF, /*!< Transfer set info */
|
||||
DM_PAST_MSG_API_CFG, /*!< Configure PAST parameters */
|
||||
DM_PAST_MSG_API_DEFAULT_CFG /*!< Configure PAST default parameters */
|
||||
};
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmPastAct_t dmPastAct[] =
|
||||
{
|
||||
dmPastActRptRcvEnable,
|
||||
dmPastActSyncTsfr,
|
||||
dmPastActSetInfoTrsf,
|
||||
dmPastActConfig,
|
||||
dmPastActDefaultConfig
|
||||
};
|
||||
|
||||
/*! DM PAST component function interface */
|
||||
static const dmFcnIf_t dmPastFcnIf =
|
||||
{
|
||||
dmEmptyReset,
|
||||
dmPastHciHandler,
|
||||
dmPastMsgHandler
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM Periodic Advertising Sync Transfer (PAST) module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastInit(void)
|
||||
{
|
||||
/* set function interface table */
|
||||
dmFcnIfTbl[DM_ID_PAST] = (dmFcnIf_t *) &dmPastFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM PAST HCI event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
dmConnCcb_t *pCcb = dmConnCcbByHandle(pEvent->hdr.param);
|
||||
|
||||
/* if ccb found */
|
||||
if (pCcb != NULL)
|
||||
{
|
||||
/* set conn id */
|
||||
pEvent->hdr.param = pCcb->connId;
|
||||
|
||||
if (pEvent->hdr.event == HCI_LE_PER_ADV_SYNC_TRSF_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
pEvent->hdr.event = DM_PER_ADV_SYNC_TRSF_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_LE_PER_ADV_SET_INFO_TRSF_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
pEvent->hdr.event = DM_PER_ADV_SET_INFO_TRSF_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM PAST event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmPastAct[DM_MSG_MASK(pMsg->event)])((dmPastMsg_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Enable receiving report action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastActRptRcvEnable(dmPastMsg_t *pMsg)
|
||||
{
|
||||
dmSyncCb_t *pScb;
|
||||
|
||||
/* look up scb from sync id */
|
||||
if ((pScb = dmSyncCbById((dmSyncId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
HciLeSetPerAdvRcvEnableCmd(pScb->handle, (pMsg->hdr.status ? TRUE : FALSE));
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Transfer sync action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastActSyncTsfr(dmPastMsg_t *pMsg)
|
||||
{
|
||||
dmSyncCb_t *pScb;
|
||||
|
||||
/* look up scb from sync id */
|
||||
if ((pScb = dmSyncCbById((dmSyncId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->apiPastTrsf.connId)) != NULL)
|
||||
{
|
||||
HciLePerAdvSyncTrsfCmd(pCcb->handle, pMsg->apiPastTrsf.serviceData, pScb->handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Transfer set info action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastActSetInfoTrsf(dmPastMsg_t *pMsg)
|
||||
{
|
||||
uint8_t advHandle = (uint8_t) pMsg->hdr.param;
|
||||
|
||||
/* if periodic advertising is currently in progress for the advertising set */
|
||||
if (dmPerAdvState(advHandle) == DM_ADV_PER_STATE_ADVERTISING)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->apiPastTrsf.connId)) != NULL)
|
||||
{
|
||||
HciLePerAdvSetInfoTrsfCmd(pCcb->handle, pMsg->apiPastTrsf.serviceData, advHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure PAST action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastActConfig(dmPastMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById((dmConnId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
HciLeSetPerAdvSyncTrsfParamsCmd(pCcb->handle, pMsg->apiPastCfg.mode, pMsg->apiPastCfg.skip,
|
||||
pMsg->apiPastCfg.syncTimeout, pMsg->apiPastCfg.cteType);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure default PAST action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastActDefaultConfig(dmPastMsg_t *pMsg)
|
||||
{
|
||||
HciLeSetDefaultPerAdvSyncTrsfParamsCmd(pMsg->apiPastCfg.mode, pMsg->apiPastCfg.skip,
|
||||
pMsg->apiPastCfg.syncTimeout, pMsg->apiPastCfg.cteType);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Enable or disable reports for the periodic advertising identified by the sync id.
|
||||
*
|
||||
* \param syncId Sync identifier.
|
||||
* \param enable TRUE to enable reporting, FALSE to disable reporting.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastRptRcvEnable(dmSyncId_t syncId, bool_t enable)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->param = syncId;
|
||||
pMsg->event = DM_PAST_MSG_API_RCV_ENABLE;
|
||||
pMsg->status = enable;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send synchronization information about the periodic advertising identified by the
|
||||
* sync id to a connected device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param serviceData Value provided by the Host.
|
||||
* \param syncId Sync identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastSyncTrsf(dmConnId_t connId, uint16_t serviceData, dmSyncId_t syncId)
|
||||
{
|
||||
dmPastApiTrsf_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPastApiTrsf_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.param = syncId;
|
||||
pMsg->hdr.event = DM_PAST_MSG_API_SYNC_TRSF;
|
||||
pMsg->serviceData = serviceData;
|
||||
pMsg->connId = connId;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send synchronization information about the periodic advertising in an advertising
|
||||
* set to a connected device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param serviceData Value provided by the Host.
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastSetInfoTrsf(dmConnId_t connId, uint16_t serviceData, uint8_t advHandle)
|
||||
{
|
||||
dmPastApiTrsf_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPastApiTrsf_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.param = advHandle;
|
||||
pMsg->hdr.event = DM_PAST_MSG_API_SET_INFO_TRSF;
|
||||
pMsg->serviceData = serviceData;
|
||||
pMsg->connId = connId;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Specify how the Controller should process periodic advertising synchronization
|
||||
* information received from the device identified by the connection handle.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param mode Action to be taken when periodic advertising info is received.
|
||||
* \param skip Number of consecutive periodic advertising packets that the receiver
|
||||
* may skip after successfully receiving a periodic advertising packet.
|
||||
* \param syncTimeout Maximum permitted time between successful receives. If this time is
|
||||
* exceeded, synchronization is lost.
|
||||
* \param cteType Whether to only synchronize to periodic advertising with certain
|
||||
* types of Constant Tone Extension.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastConfig(dmConnId_t connId, uint8_t mode, uint16_t skip, uint16_t syncTimeout,
|
||||
uint8_t cteType)
|
||||
{
|
||||
dmPastApiCfg_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPastApiCfg_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->hdr.event = DM_PAST_MSG_API_CFG;
|
||||
pMsg->mode = mode;
|
||||
pMsg->skip = skip;
|
||||
pMsg->syncTimeout = syncTimeout;
|
||||
pMsg->cteType = cteType;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Specify the initial value for the mode, skip, timeout, and Constant Tone Extension type
|
||||
* to be used for all subsequent connections over the LE transport.
|
||||
*
|
||||
* \param mode Action to be taken when periodic advertising info is received.
|
||||
* \param skip Number of consecutive periodic advertising packets that the receiver
|
||||
* may skip after successfully receiving a periodic advertising packet.
|
||||
* \param syncTimeout Maximum permitted time between successful receives. If this time is
|
||||
* exceeded, synchronization is lost.
|
||||
* \param cteType Whether to only synchronize to periodic advertising with certain
|
||||
* types of Constant Tone Extension.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastDefaultConfig(uint8_t mode, uint16_t skip, uint16_t syncTimeout, uint8_t cteType)
|
||||
{
|
||||
dmPastApiCfg_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPastApiCfg_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PAST_MSG_API_DEFAULT_CFG;
|
||||
pMsg->mode = mode;
|
||||
pMsg->skip = skip;
|
||||
pMsg->syncTimeout = syncTimeout;
|
||||
pMsg->cteType = cteType;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM PHY module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_phy.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmPhyFcnIf =
|
||||
{
|
||||
dmEmptyReset,
|
||||
dmPhyHciHandler,
|
||||
(dmMsgHandler_t)dmEmptyHandler
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
static void dmPhyActDefPhySet(hciEvt_t *pEvent);
|
||||
static void dmPhyActPhyRead(dmConnCcb_t *pCcb, hciEvt_t *pEvent);
|
||||
static void dmPhyActPhyUpdate(dmConnCcb_t *pCcb, hciEvt_t *pEvent);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM PHY HCI event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPhyHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
if (pEvent->hdr.event == HCI_LE_SET_DEF_PHY_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
dmPhyActDefPhySet(pEvent);
|
||||
}
|
||||
/* look up ccb from conn handle */
|
||||
else if ((pCcb = dmConnCcbByHandle(pEvent->hdr.param)) != NULL)
|
||||
{
|
||||
/* handle incoming event */
|
||||
switch (pEvent->hdr.event)
|
||||
{
|
||||
case HCI_LE_READ_PHY_CMD_CMPL_CBACK_EVT:
|
||||
dmPhyActPhyRead(pCcb, pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_PHY_UPDATE_CMPL_CBACK_EVT:
|
||||
dmPhyActPhyUpdate(pCcb, pEvent);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* should never get here */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a read PHY event from HCI.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmPhyActPhyRead(dmConnCcb_t *pCcb, hciEvt_t *pEvent)
|
||||
{
|
||||
hciLeReadPhyCmdCmplEvt_t evt;
|
||||
|
||||
/* call callback */
|
||||
evt.hdr.event = DM_PHY_READ_IND;
|
||||
evt.hdr.param = pCcb->connId;
|
||||
evt.status = evt.hdr.status = (uint8_t)pEvent->leReadPhyCmdCmpl.status;
|
||||
evt.handle = pCcb->handle;
|
||||
evt.txPhy = pEvent->leReadPhyCmdCmpl.txPhy;
|
||||
evt.rxPhy = pEvent->leReadPhyCmdCmpl.rxPhy;
|
||||
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_APP])((dmEvt_t *)&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a set default PHY event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmPhyActDefPhySet(hciEvt_t *pEvent)
|
||||
{
|
||||
hciLeSetDefPhyCmdCmplEvt_t evt;
|
||||
|
||||
/* call callback */
|
||||
evt.hdr.event = DM_PHY_SET_DEF_IND;
|
||||
evt.hdr.param = 0;
|
||||
evt.status = evt.hdr.status = (uint8_t)pEvent->leSetDefPhyCmdCmpl.status;
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_APP])((dmEvt_t *)&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a PHY update event from HCI.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmPhyActPhyUpdate(dmConnCcb_t *pCcb, hciEvt_t *pEvent)
|
||||
{
|
||||
hciLePhyUpdateEvt_t evt;
|
||||
|
||||
/* call callback */
|
||||
evt.hdr.event = DM_PHY_UPDATE_IND;
|
||||
evt.hdr.param = pCcb->connId;
|
||||
evt.status = evt.hdr.status = (uint8_t)pEvent->lePhyUpdate.status;
|
||||
evt.handle = pCcb->handle;
|
||||
evt.txPhy = pEvent->lePhyUpdate.txPhy;
|
||||
evt.rxPhy = pEvent->lePhyUpdate.rxPhy;
|
||||
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_APP])((dmEvt_t *)&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read the current transmitter PHY and receiver PHY for a given connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmReadPhy(dmConnId_t connId)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
WsfTaskLock();
|
||||
/* look up ccb from conn id */
|
||||
pCcb = dmConnCcbById(connId);
|
||||
WsfTaskUnlock();
|
||||
|
||||
/* if ccb found */
|
||||
if (pCcb != NULL)
|
||||
{
|
||||
HciLeReadPhyCmd(pCcb->handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the preferred values for the transmitter PHY and receiver PHY for all subsequent
|
||||
* connections.
|
||||
*
|
||||
* \param allPhys All PHYs preferences.
|
||||
* \param txPhys Preferred transmitter PHYs.
|
||||
* \param rxPhys Preferred receiver PHYs.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSetDefaultPhy(uint8_t allPhys, uint8_t txPhys, uint8_t rxPhys)
|
||||
{
|
||||
HciLeSetDefaultPhyCmd(allPhys, txPhys, rxPhys);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the PHY preferences for a given connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param allPhys All PHYs preferences.
|
||||
* \param txPhys Preferred transmitter PHYs.
|
||||
* \param rxPhys Preferred receiver PHYs.
|
||||
* \param phyOptions PHY options.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSetPhy(dmConnId_t connId, uint8_t allPhys, uint8_t txPhys, uint8_t rxPhys, uint16_t phyOptions)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
WsfTaskLock();
|
||||
/* look up ccb from conn id */
|
||||
pCcb = dmConnCcbById(connId);
|
||||
WsfTaskUnlock();
|
||||
|
||||
/* if ccb found */
|
||||
if (pCcb != NULL)
|
||||
{
|
||||
HciLeSetPhyCmd(pCcb->handle, allPhys, txPhys, rxPhys, phyOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM PHY.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPhyInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_PHY] = (dmFcnIf_t *) &dmPhyFcnIf;
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM PHY module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_PHY_H
|
||||
#define DM_PHY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data types
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* component interface */
|
||||
void dmPhyHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_PHY_H */
|
||||
+698
@@ -0,0 +1,698 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager privacy module.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "util/calc128.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_priv.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* progress (dmPrivCb.inProgress) bitmask bits */
|
||||
#define DM_PRIV_INPROGRESS_RES_ADDR (1 << 0) /* resolve address in progress */
|
||||
#define DM_PRIV_INPROGRESS_GEN_ADDR (1 << 1) /* generate address in progress */
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmPrivAct_t dmPrivAct[] =
|
||||
{
|
||||
dmPrivActResolveAddr,
|
||||
dmPrivActResAddrAesCmpl,
|
||||
dmPrivActAddDevToResList,
|
||||
dmPrivActRemDevFromResList,
|
||||
dmPrivActClearResList,
|
||||
dmPrivActSetAddrResEnable,
|
||||
dmPrivActSetPrivacyMode,
|
||||
dmPrivActGenAddr,
|
||||
dmPrivActGenAddrAesCmpl
|
||||
};
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmPrivFcnIf =
|
||||
{
|
||||
dmPrivReset,
|
||||
dmPrivHciHandler,
|
||||
dmPrivMsgHandler
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
dmPrivCb_t dmPrivCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void dmPrivSetAddrResEnable(bool_t enable);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start address resolution procedure
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActResolveAddr(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
uint8_t buf[DM_PRIV_PLAINTEXT_LEN];
|
||||
|
||||
/* verify no resolution procedure currently in progress */
|
||||
if ((dmPrivCb.inProgress & DM_PRIV_INPROGRESS_RES_ADDR) == 0)
|
||||
{
|
||||
/* store hash */
|
||||
memcpy(dmPrivCb.hash, pMsg->apiResolveAddr.addr, DM_PRIV_HASH_LEN);
|
||||
|
||||
/* copy random part of address with padding for address resolution calculation */
|
||||
memcpy(buf, &pMsg->apiResolveAddr.addr[3], DM_PRIV_PRAND_LEN);
|
||||
memset(buf + DM_PRIV_PRAND_LEN, 0, (DM_PRIV_PLAINTEXT_LEN - DM_PRIV_PRAND_LEN));
|
||||
|
||||
/* set in progress */
|
||||
dmPrivCb.inProgress |= DM_PRIV_INPROGRESS_RES_ADDR;
|
||||
|
||||
/* run calculation */
|
||||
SecAes(pMsg->apiResolveAddr.irk, buf, dmCb.handlerId,
|
||||
pMsg->hdr.param, DM_PRIV_MSG_RESOLVE_AES_CMPL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* call callback with error (note hdr.param is already set) */
|
||||
pMsg->hdr.status = HCI_ERR_MEMORY_EXCEEDED;
|
||||
pMsg->hdr.event = DM_PRIV_RESOLVED_ADDR_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Finish address resolution procedure upon completion of AES calculation.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActResAddrAesCmpl(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
/* compare calculated value with hash */
|
||||
if (memcmp(dmPrivCb.hash, pMsg->aes.pCiphertext, DM_PRIV_HASH_LEN) == 0)
|
||||
{
|
||||
pMsg->hdr.status = HCI_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
pMsg->hdr.status = HCI_ERR_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
/* clear in progress */
|
||||
dmPrivCb.inProgress &= ~DM_PRIV_INPROGRESS_RES_ADDR;
|
||||
|
||||
/* call client callback (note hdr.param is already set) */
|
||||
pMsg->hdr.event = DM_PRIV_RESOLVED_ADDR_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add device to resolving list command.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActAddDevToResList(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
dmPrivApiAddDevToResList_t *pDev = &pMsg->apiAddDevToResList;
|
||||
|
||||
/* save whether asked to enable address resolution */
|
||||
dmPrivCb.enableLlPriv = pDev->enableLlPriv;
|
||||
|
||||
/* save client-defined parameter for callback event */
|
||||
dmPrivCb.addDevToResListParam = pMsg->hdr.param;
|
||||
|
||||
/* add device to resolving list */
|
||||
HciLeAddDeviceToResolvingListCmd(pDev->addrType, pDev->peerAddr, pDev->peerIrk, pDev->localIrk);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove device from resolving list command.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActRemDevFromResList(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
dmPrivApiRemDevFromResList_t *pDev = &pMsg->apiRemDevFromResList;
|
||||
|
||||
/* save client-defined parameter for callback event */
|
||||
dmPrivCb.remDevFromResListParam = pMsg->hdr.param;
|
||||
|
||||
/* remove device from resolving list */
|
||||
HciLeRemoveDeviceFromResolvingList(pDev->addrType, pDev->peerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear resolving list command.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActClearResList(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
/* clear resolving list */
|
||||
HciLeClearResolvingList();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set address resolution enable command.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActSetAddrResEnable(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
dmPrivApiSetAddrResEnable_t *pAddrRes = &pMsg->apiSetAddrResEnable;
|
||||
|
||||
/* enable or disable address resolution in LL */
|
||||
dmPrivSetAddrResEnable(pAddrRes->enable);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set privacy mode command.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActSetPrivacyMode(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
dmPrivApiSetPrivacyMode_t *pPrivacyMode = &pMsg->apiSetPrivacyMode;
|
||||
|
||||
/* set privacy mode */
|
||||
HciLeSetPrivacyModeCmd(pPrivacyMode->addrType, pPrivacyMode->peerAddr, pPrivacyMode->mode);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start address generation procedure.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActGenAddr(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
if ((dmPrivCb.inProgress & DM_PRIV_INPROGRESS_GEN_ADDR) == 0)
|
||||
{
|
||||
/* get random number */
|
||||
SecRand(dmPrivCb.genAddrBuf, DM_PRIV_PRAND_LEN);
|
||||
|
||||
/* set address type in random number */
|
||||
dmPrivCb.genAddrBuf[2] = (dmPrivCb.genAddrBuf[2] & 0x3F) | DM_RAND_ADDR_RESOLV;
|
||||
|
||||
/* pad buffer */
|
||||
memset(dmPrivCb.genAddrBuf + DM_PRIV_PRAND_LEN, 0, (DM_PRIV_PLAINTEXT_LEN - DM_PRIV_PRAND_LEN));
|
||||
|
||||
/* set in progress */
|
||||
dmPrivCb.inProgress |= DM_PRIV_INPROGRESS_GEN_ADDR;
|
||||
|
||||
/* run calculation */
|
||||
SecAes(pMsg->apiGenerateAddr.irk, dmPrivCb.genAddrBuf, dmCb.handlerId,
|
||||
pMsg->hdr.param, DM_PRIV_MSG_GEN_ADDR_AES_CMPL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* call callback with error (note hdr.param is already set) */
|
||||
pMsg->hdr.status = HCI_ERR_MEMORY_EXCEEDED;
|
||||
pMsg->hdr.event = DM_PRIV_GENERATE_ADDR_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Finish generate RPA procedure upon completion of AES calculation.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActGenAddrAesCmpl(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
dmPrivGenAddrIndEvt_t *pAddrEvt = (dmPrivGenAddrIndEvt_t*) pMsg;
|
||||
|
||||
/* copy the hash and address to buffer */
|
||||
memcpy(pAddrEvt->addr, pMsg->aes.pCiphertext, DM_PRIV_HASH_LEN);
|
||||
memcpy(pAddrEvt->addr + DM_PRIV_HASH_LEN, dmPrivCb.genAddrBuf, DM_PRIV_PRAND_LEN);
|
||||
|
||||
/* clear in progress */
|
||||
dmPrivCb.inProgress &= ~DM_PRIV_INPROGRESS_GEN_ADDR;
|
||||
|
||||
/* call client callback */
|
||||
pAddrEvt->hdr.event = DM_PRIV_GENERATE_ADDR_IND;
|
||||
pMsg->hdr.status = HCI_SUCCESS;
|
||||
(*dmCb.cback)((dmEvt_t *) pAddrEvt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM priv HCI callback event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
/* handle incoming event */
|
||||
switch (pEvent->hdr.event)
|
||||
{
|
||||
case HCI_LE_ADD_DEV_TO_RES_LIST_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_ADD_DEV_TO_RES_LIST_IND;
|
||||
pEvent->hdr.param = dmPrivCb.addDevToResListParam;
|
||||
|
||||
/* if LE add device to resolving list command succeeded and been asked to enable address
|
||||
* resolution in LL and it's not enabled yet
|
||||
*/
|
||||
if ((pEvent->hdr.status == HCI_SUCCESS) && dmPrivCb.enableLlPriv && !dmCb.llPrivEnabled)
|
||||
{
|
||||
/* enable address resolution in LL */
|
||||
dmPrivSetAddrResEnable(TRUE);
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_LE_REM_DEV_FROM_RES_LIST_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_REM_DEV_FROM_RES_LIST_IND;
|
||||
pEvent->hdr.param = dmPrivCb.remDevFromResListParam;
|
||||
break;
|
||||
|
||||
case HCI_LE_CLEAR_RES_LIST_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_CLEAR_RES_LIST_IND;
|
||||
|
||||
/* if LE clear resolving list command succeeded and address resolution's enabled in LL */
|
||||
if ((pEvent->hdr.status == HCI_SUCCESS) && dmCb.llPrivEnabled)
|
||||
{
|
||||
/* disable address resolution in LL */
|
||||
dmPrivSetAddrResEnable(FALSE);
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_LE_READ_PEER_RES_ADDR_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_READ_PEER_RES_ADDR_IND;
|
||||
break;
|
||||
|
||||
case HCI_LE_READ_LOCAL_RES_ADDR_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_READ_LOCAL_RES_ADDR_IND;
|
||||
break;
|
||||
|
||||
case HCI_LE_SET_ADDR_RES_ENABLE_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_SET_ADDR_RES_ENABLE_IND;
|
||||
|
||||
/* if LE set address resoultion enable command succeeded */
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
/* update LL Privacy Enabled flag */
|
||||
dmCb.llPrivEnabled = dmPrivCb.addrResEnable;
|
||||
|
||||
/* pass LL Privacy enable/disable event to dev priv */
|
||||
dmDevPassEvtToDevPriv(dmCb.llPrivEnabled ? DM_DEV_PRIV_MSG_RPA_STOP : DM_DEV_PRIV_MSG_RPA_START,
|
||||
dmCb.llPrivEnabled ? TRUE : FALSE, 0, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* should never get here */
|
||||
return;
|
||||
}
|
||||
|
||||
/* call callback (note hdr.status is already set) */
|
||||
(*dmCb.cback)((dmEvt_t *)pEvent);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set address resolution enable command.
|
||||
*
|
||||
* \param enable Set to TRUE to enable address resolution or FALSE to disable it.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmPrivSetAddrResEnable(bool_t enable)
|
||||
{
|
||||
/* save input parameter */
|
||||
dmPrivCb.addrResEnable = enable;
|
||||
|
||||
/* enable or disable address resolution in LL */
|
||||
HciLeSetAddrResolutionEnable(enable);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM priv event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmPrivAct[DM_MSG_MASK(pMsg->event)])((dmPrivMsg_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the privacy module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivReset(void)
|
||||
{
|
||||
/* initialize control block */
|
||||
dmPrivCb.inProgress = 0;
|
||||
dmCb.llPrivEnabled = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM privacy module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_PRIV] = (dmFcnIf_t *) &dmPrivFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Resolve a private resolvable address. When complete the client's callback function
|
||||
* is called with a DM_PRIV_RESOLVED_ADDR_IND event. The client must wait to receive
|
||||
* this event before executing this function again.
|
||||
*
|
||||
* \param pAddr Peer device address.
|
||||
* \param pIrk The peer's identity resolving key.
|
||||
* \param param client-defined parameter returned with callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivResolveAddr(uint8_t *pAddr, uint8_t *pIrk, uint16_t param)
|
||||
{
|
||||
dmPrivApiResolveAddr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivApiResolveAddr_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_RESOLVE_ADDR;
|
||||
pMsg->hdr.param = param;
|
||||
Calc128Cpy(pMsg->irk, pIrk);
|
||||
BdaCpy(pMsg->addr, pAddr);
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add device to resolving list. When complete the client's callback function
|
||||
* is called with a DM_PRIV_ADD_DEV_TO_RES_LIST_IND event. The client must wait
|
||||
* to receive this event before executing this function again.
|
||||
*
|
||||
* \param addrType Peer identity address type.
|
||||
* \param pIdentityAddr Peer identity address.
|
||||
* \param pPeerIrk The peer's identity resolving key.
|
||||
* \param pLocalIrk The local identity resolving key.
|
||||
* \param enableLlPriv Set to TRUE to enable address resolution in LL.
|
||||
* \param param client-defined parameter returned with callback event.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This command cannot be used when address resolution is enabled in the Controller and:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*
|
||||
* \Note If the local or peer IRK associated with the peer Identity Address is all zeros then
|
||||
* the Controller will use or accept the local or peer Identity Address respectively.
|
||||
*
|
||||
* \Note Parameter 'enableLlPriv' should be set to TRUE when the last device is being added
|
||||
* to resolving list to enable address resolution in the Controller.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivAddDevToResList(uint8_t addrType, const uint8_t *pIdentityAddr, uint8_t *pPeerIrk,
|
||||
uint8_t *pLocalIrk, bool_t enableLlPriv, uint16_t param)
|
||||
{
|
||||
dmPrivApiAddDevToResList_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivApiAddDevToResList_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_ADD_DEV_TO_RES_LIST;
|
||||
pMsg->hdr.param = param;
|
||||
pMsg->addrType = addrType;
|
||||
BdaCpy(pMsg->peerAddr, pIdentityAddr);
|
||||
Calc128Cpy(pMsg->peerIrk, pPeerIrk);
|
||||
Calc128Cpy(pMsg->localIrk, pLocalIrk);
|
||||
pMsg->enableLlPriv = enableLlPriv;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove device from resolving list. When complete the client's callback function
|
||||
* is called with a DM_PRIV_REM_DEV_FROM_RES_LIST_IND event. The client must wait to
|
||||
* receive this event before executing this function again.
|
||||
*
|
||||
* \param addrType Peer identity address type.
|
||||
* \param pIdentityAddr Peer identity address.
|
||||
* \param param client-defined parameter returned with callback event.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This command cannot be used when address resolution is enabled in the Controller and:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivRemDevFromResList(uint8_t addrType, const uint8_t *pIdentityAddr, uint16_t param)
|
||||
{
|
||||
dmPrivApiRemDevFromResList_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivApiRemDevFromResList_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_REM_DEV_FROM_RES_LIST;
|
||||
pMsg->hdr.param = param;
|
||||
pMsg->addrType = addrType;
|
||||
BdaCpy(pMsg->peerAddr, pIdentityAddr);
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear resolving list. When complete the client's callback function is called with a
|
||||
* DM_PRIV_CLEAR_RES_LIST_IND event. The client must wait to receive this event before
|
||||
* executing this function again.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This command cannot be used when address resolution is enabled in the Controller and:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*
|
||||
* \Note Address resolution in the Controller will be disabled when resolving list's cleared
|
||||
* successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivClearResList(void)
|
||||
{
|
||||
dmPrivMsg_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivMsg_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_CLEAR_RES_LIST;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI read peer resolvable address command. When complete the client's callback
|
||||
* function is called with a DM_PRIV_READ_PEER_RES_ADDR_IND event. The client must
|
||||
* wait to receive this event before executing this function again.
|
||||
*
|
||||
* \param addrType Peer identity address type.
|
||||
* \param pIdentityAddr Peer identity address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivReadPeerResolvableAddr(uint8_t addrType, const uint8_t *pIdentityAddr)
|
||||
{
|
||||
HciLeReadPeerResolvableAddr(addrType, pIdentityAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read local resolvable address command. When complete the client's callback
|
||||
* function is called with a DM_PRIV_READ_LOCAL_RES_ADDR_IND event. The client must
|
||||
* wait to receive this event before executing this function again.
|
||||
*
|
||||
* \param addrType Peer identity address type.
|
||||
* \param pIdentityAddr Peer identity address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivReadLocalResolvableAddr(uint8_t addrType, const uint8_t *pIdentityAddr)
|
||||
{
|
||||
HciLeReadLocalResolvableAddr(addrType, pIdentityAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Enable or disable address resolution in LL. When complete the client's callback
|
||||
* function is called with a DM_PRIV_SET_ADDR_RES_ENABLE_IND event. The client must
|
||||
* wait to receive this event before executing this function again.
|
||||
*
|
||||
* \param enable Set to TRUE to enable address resolution or FALSE to disable it.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This command can be used at any time except when:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivSetAddrResEnable(bool_t enable)
|
||||
{
|
||||
dmPrivApiSetAddrResEnable_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivMsg_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_SET_ADDR_RES_ENABLE;
|
||||
pMsg->hdr.param = 0;
|
||||
pMsg->enable = enable;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set resolvable private address timeout command.
|
||||
*
|
||||
* \param rpaTimeout Timeout measured in seconds.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivSetResolvablePrivateAddrTimeout(uint16_t rpaTimeout)
|
||||
{
|
||||
HciLeSetResolvablePrivateAddrTimeout(rpaTimeout);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set privacy mode for a given entry in the resolving list.
|
||||
*
|
||||
* \param addrType Peer identity address type.
|
||||
* \param pIdentityAddr Peer identity address.
|
||||
* \param mode Privacy mode (by default, network privacy mode is used).
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This command can be used at any time except when:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivSetPrivacyMode(uint8_t addrType, const uint8_t *pIdentityAddr, uint8_t mode)
|
||||
{
|
||||
dmPrivApiSetPrivacyMode_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivApiSetPrivacyMode_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_SET_PRIVACY_MODE;
|
||||
pMsg->addrType = addrType;
|
||||
BdaCpy(pMsg->peerAddr, pIdentityAddr);
|
||||
pMsg->mode = mode;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate a Resolvable Private Address (RPA).
|
||||
*
|
||||
* \param pIrk The identity resolving key.
|
||||
* \param param Client-defined parameter returned with callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivGenerateAddr(uint8_t *pIrk, uint16_t param)
|
||||
{
|
||||
dmPrivApiGenAddr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivApiGenAddr_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_GEN_ADDR;
|
||||
pMsg->hdr.param = param;
|
||||
Calc128Cpy(pMsg->irk, pIrk);
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM privacy module.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_PRIV_H
|
||||
#define DM_PRIV_H
|
||||
|
||||
#include "util/bda.h"
|
||||
#include "wsf_os.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "sec_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "smp_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM priv event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_PRIV_MSG_API_RESOLVE_ADDR = DM_MSG_START(DM_ID_PRIV),
|
||||
DM_PRIV_MSG_RESOLVE_AES_CMPL,
|
||||
DM_PRIV_MSG_API_ADD_DEV_TO_RES_LIST,
|
||||
DM_PRIV_MSG_API_REM_DEV_FROM_RES_LIST,
|
||||
DM_PRIV_MSG_API_CLEAR_RES_LIST,
|
||||
DM_PRIV_MSG_API_SET_ADDR_RES_ENABLE,
|
||||
DM_PRIV_MSG_API_SET_PRIVACY_MODE,
|
||||
DM_PRIV_MSG_API_GEN_ADDR,
|
||||
DM_PRIV_MSG_GEN_ADDR_AES_CMPL
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_RESOLVE_ADDR */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t irk[SMP_KEY_LEN];
|
||||
bdAddr_t addr;
|
||||
} dmPrivApiResolveAddr_t;
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_GEN_ADDR */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t irk[SMP_KEY_LEN];
|
||||
} dmPrivApiGenAddr_t;
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_ADD_DEV_TO_RES_LIST */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t addrType;
|
||||
bdAddr_t peerAddr;
|
||||
uint8_t peerIrk[SMP_KEY_LEN];
|
||||
uint8_t localIrk[SMP_KEY_LEN];
|
||||
bool_t enableLlPriv;
|
||||
} dmPrivApiAddDevToResList_t;
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_REM_DEV_FROM_RES_LIST */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t addrType;
|
||||
bdAddr_t peerAddr;
|
||||
} dmPrivApiRemDevFromResList_t;
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_SET_ADDR_RES_ENABLE */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
bool_t enable;
|
||||
} dmPrivApiSetAddrResEnable_t;
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_SET_PRIVACY_MODE */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t addrType;
|
||||
bdAddr_t peerAddr;
|
||||
uint8_t mode;
|
||||
} dmPrivApiSetPrivacyMode_t;
|
||||
|
||||
/* Union of all priv messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmPrivApiResolveAddr_t apiResolveAddr;
|
||||
dmPrivApiAddDevToResList_t apiAddDevToResList;
|
||||
dmPrivApiRemDevFromResList_t apiRemDevFromResList;
|
||||
dmPrivApiSetAddrResEnable_t apiSetAddrResEnable;
|
||||
dmPrivApiSetPrivacyMode_t apiSetPrivacyMode;
|
||||
dmPrivApiGenAddr_t apiGenerateAddr;
|
||||
dmPrivGenAddrIndEvt_t genAddrInd;
|
||||
secAes_t aes;
|
||||
} dmPrivMsg_t;
|
||||
|
||||
/* Action function */
|
||||
typedef void (*dmPrivAct_t)(dmPrivMsg_t *pMsg);
|
||||
|
||||
/* Control block for privacy module */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t hash[DM_PRIV_HASH_LEN]; /* Hash part of resolvable address */
|
||||
bool_t inProgress; /* Address resolution in progress */
|
||||
uint16_t addDevToResListParam; /* 'Add device to resolving list' callback param */
|
||||
uint16_t remDevFromResListParam; /* 'Remove device from resolving list' callback param */
|
||||
bool_t enableLlPriv; /* 'Add device to resolving list' input param */
|
||||
bool_t addrResEnable; /* 'Set address resolution enable' input param */
|
||||
uint8_t genAddrBuf[HCI_ENCRYPT_DATA_LEN]; /* Random value buffer for generating an RPA */
|
||||
} dmPrivCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* component inteface */
|
||||
void dmPrivMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmPrivHciHandler(hciEvt_t *pEvent);
|
||||
void dmPrivReset(void);
|
||||
|
||||
/* action functions */
|
||||
void dmPrivActResolveAddr(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActResAddrAesCmpl(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActAddDevToResList(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActRemDevFromResList(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActSetAddrResEnable(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActClearResList(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActSetPrivacyMode(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActGenAddr(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActGenAddrAesCmpl(dmPrivMsg_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_PRIV_H */
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager scan module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_scan.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
dmScanCb_t dmScanCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the scan module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* initialize control block */
|
||||
for (i = 0; i < DM_NUM_PHYS; i++)
|
||||
{
|
||||
dmScanCb.scanInterval[i] = DM_GAP_SCAN_FAST_INT_MIN;
|
||||
dmScanCb.scanWindow[i] = DM_GAP_SCAN_FAST_WINDOW;
|
||||
}
|
||||
|
||||
dmCb.scanFiltPolicy = HCI_FILT_NONE;
|
||||
dmScanCb.scanTimer.handlerId = dmCb.handlerId;
|
||||
dmScanCb.scanState = DM_SCAN_STATE_IDLE;
|
||||
dmCb.scanAddrType = DM_ADDR_PUBLIC;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start scanning on the given PHYs.
|
||||
*
|
||||
* \param scanPhys Scanner PHYs.
|
||||
* \param mode Discoverability mode.
|
||||
* \param pScanType Scan type array.
|
||||
* \param filterDup Filter duplicates. Set to TRUE to filter duplicate responses received
|
||||
* from the same device. Set to FALSE to receive all responses.
|
||||
* \param duration The scan duration, in milliseconds. If set to zero or both duration and
|
||||
* period set to non-zero, scanning will continue until DmScanStop() is called.
|
||||
* \param period The scan period, in 1.28 sec units (only applicable to AE). If set to zero,
|
||||
* periodic scanning is disabled.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmScanStart(uint8_t scanPhys, uint8_t mode, const uint8_t *pScanType, bool_t filterDup,
|
||||
uint16_t duration, uint16_t period)
|
||||
{
|
||||
uint8_t i; /* scanPhy bit position */
|
||||
uint8_t idx; /* param array index */
|
||||
dmScanApiStart_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmScanApiStart_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_SCAN_MSG_API_START;
|
||||
pMsg->scanPhys = scanPhys;
|
||||
|
||||
for (i = 0, idx = 0; (i < 8) && (idx < DM_NUM_PHYS); i++)
|
||||
{
|
||||
if (scanPhys & (1 << i))
|
||||
{
|
||||
/* scan type for this PHY */
|
||||
pMsg->scanType[idx] = pScanType[idx];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
pMsg->mode = mode;
|
||||
pMsg->duration = duration;
|
||||
pMsg->period = period;
|
||||
pMsg->filterDup = filterDup;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop scanning.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmScanStop(void)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->event = DM_SCAN_MSG_API_STOP;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the scan interval and window.
|
||||
*
|
||||
* \param phyIdx The scanning PHY.
|
||||
* \param scanInterval The scan interval.
|
||||
* \param scanWindow The scan window.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmScanSetInterval(uint8_t scanPhy, uint16_t scanInterval, uint16_t scanWindow)
|
||||
{
|
||||
uint8_t phyIdx;
|
||||
|
||||
WSF_ASSERT((scanPhy == HCI_INIT_PHY_LE_1M_BIT) || (scanPhy == HCI_INIT_PHY_LE_CODED_BIT));
|
||||
|
||||
WsfTaskLock();
|
||||
phyIdx = DmScanPhyToIdx(scanPhy);
|
||||
dmScanCb.scanInterval[phyIdx] = scanInterval;
|
||||
dmScanCb.scanWindow[phyIdx] = scanWindow;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the scan interval and window for the specified PHYs.
|
||||
*
|
||||
* \param scanPhys Scanning PHYs.
|
||||
* \param pScanInterval Scan interval array.
|
||||
* \param pScanWindow Scan window array.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmScanSetInterval(uint8_t scanPhys, uint16_t *pScanInterval, uint16_t *pScanWindow)
|
||||
{
|
||||
uint8_t i; /* scanPhy bit position */
|
||||
uint8_t idx; /* param array index */
|
||||
|
||||
for (i = 0, idx = 0; (i < 8) && (idx < DM_NUM_PHYS); i++)
|
||||
{
|
||||
if (scanPhys & (1 << i))
|
||||
{
|
||||
dmScanSetInterval((1 << i), pScanInterval[idx], pScanWindow[idx]);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the local address type used while scanning. This function can be used to
|
||||
* configure scanning to use a random address.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmScanSetAddrType(uint8_t addrType)
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmCb.scanAddrType = addrType;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM scan module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_SCAN_H
|
||||
#define DM_SCAN_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM scan event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_SCAN_MSG_API_START = DM_MSG_START(DM_ID_SCAN),
|
||||
DM_SCAN_MSG_API_STOP,
|
||||
DM_SCAN_MSG_TIMEOUT
|
||||
};
|
||||
|
||||
/* DM scan states */
|
||||
enum
|
||||
{
|
||||
DM_SCAN_STATE_IDLE,
|
||||
DM_SCAN_STATE_STARTING,
|
||||
DM_SCAN_STATE_SCANNING,
|
||||
DM_SCAN_STATE_STOPPING
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data structure for DM_SCAN_MSG_API_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t scanPhys;
|
||||
uint8_t scanType[DM_NUM_PHYS];
|
||||
uint8_t mode;
|
||||
uint16_t duration;
|
||||
uint16_t period;
|
||||
bool_t filterDup;
|
||||
} dmScanApiStart_t;
|
||||
|
||||
/* Data structure for DM_SYNC_MSG_API_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advSid;
|
||||
uint8_t advAddrType;
|
||||
bdAddr_t advAddr;
|
||||
uint16_t skip;
|
||||
uint16_t syncTimeout;
|
||||
uint8_t unused;
|
||||
} dmSyncApiStart_t;
|
||||
|
||||
/* Data structure for DM_SYNC_MSG_API_ADD_DEV_TO_PER_ADV_LIST */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advAddrType;
|
||||
bdAddr_t advAddr;
|
||||
uint8_t advSid;
|
||||
} dmSyncApiAddDevToPerAdvList_t;
|
||||
|
||||
/* Data structure for DM_SYNC_MSG_API_REM_DEV_FROM_PER_ADV_LIST */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advAddrType;
|
||||
bdAddr_t advAddr;
|
||||
uint8_t advSid;
|
||||
} dmSyncApiRemDevFromPerAdvList_t;
|
||||
|
||||
/* Union of all scan messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmScanApiStart_t apiStart;
|
||||
} dmScanMsg_t;
|
||||
|
||||
/* Union of all DM Sync state machine messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmSyncApiStart_t apiSyncStart;
|
||||
hciLePerAdvSyncEstEvt_t perAdvSyncEst;
|
||||
hciLePerAdvSyncLostEvt_t perAdvSyncLost;
|
||||
HciLePerAdvSyncTrsfRcvdEvt_t perAdvSyncTrsfEst;
|
||||
} dmSyncMsg_t;
|
||||
|
||||
/* Action function */
|
||||
typedef void (*dmScanAct_t)(dmScanMsg_t *pMsg);
|
||||
|
||||
/* Control block for scan module */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t scanTimer;
|
||||
uint16_t scanInterval[DM_NUM_PHYS];
|
||||
uint16_t scanWindow[DM_NUM_PHYS];
|
||||
bool_t scanState;
|
||||
uint16_t scanDuration;
|
||||
bool_t filterNextScanRsp;
|
||||
uint8_t discFilter;
|
||||
} dmScanCb_t;
|
||||
|
||||
/* Control block for periodic advertising sync module */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t advSid; /*!< advertising SID */
|
||||
bdAddr_t advAddr; /*!< advertiser address */
|
||||
uint8_t advAddrType; /*!< advertiser address type */
|
||||
uint16_t handle; /*!< sync handle */
|
||||
dmSyncId_t syncId; /*!< sync id */
|
||||
uint8_t state; /*!< sync state */
|
||||
uint8_t inUse; /*!< TRUE if entry in use */
|
||||
} dmSyncCb_t;
|
||||
|
||||
/* Data structure for DM_PAST_MSG_API_SYNC_TRSF and DM_PAST_MSG_API_INFO_TRSF */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
uint16_t serviceData; /*!< Value provided by the Host */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
} dmPastApiTrsf_t;
|
||||
|
||||
/* Data structure for DM_PAST_MSG_API_CONFIG and DM_PAST_MSG_API_DEFAULT_CONFIG */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
uint8_t mode; /*!< Mode */
|
||||
uint16_t skip; /*!< Skip */
|
||||
uint16_t syncTimeout; /*!< Sync timeout */
|
||||
uint8_t cteType; /*!< CTE type */
|
||||
} dmPastApiCfg_t;
|
||||
|
||||
/* Union of all DM PAST API messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmPastApiTrsf_t apiPastTrsf;
|
||||
dmPastApiCfg_t apiPastCfg;
|
||||
} dmPastMsg_t;
|
||||
|
||||
/*! Action function */
|
||||
typedef void (*dmPastAct_t)(dmPastMsg_t *pMsg);
|
||||
|
||||
extern dmScanCb_t dmScanCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* common scanning component inteface */
|
||||
void dmScanInit(void);
|
||||
|
||||
/* legacy scanning component inteface */
|
||||
void dmScanReset(void);
|
||||
void dmScanMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmScanHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* legacy scanning action functions */
|
||||
void dmScanActStart(dmScanMsg_t *pMsg);
|
||||
void dmScanActStop(dmScanMsg_t *pMsg);
|
||||
void dmScanActTimeout(dmScanMsg_t *pMsg);
|
||||
|
||||
/* extended scanning component inteface */
|
||||
void dmExtScanReset(void);
|
||||
void dmExtScanMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmExtScanHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* extended scanning action functions */
|
||||
void dmExtScanActStart(dmScanMsg_t *pMsg);
|
||||
void dmExtScanActStop(dmScanMsg_t *pMsg);
|
||||
void dmExtScanActTimeout(dmScanMsg_t *pMsg);
|
||||
|
||||
/* sync and sync transfer action functions */
|
||||
void dmSyncSmActNone(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActStart(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActStop(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActCancelStart(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActSyncEst(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActSyncEstFailed(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActSyncLost(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActSyncTrsfEst(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActSyncTrsfEstFailed(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
|
||||
/* sync component inteface */
|
||||
void dmSyncInit(void);
|
||||
void dmSyncReset(void);
|
||||
void dmSyncMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmSyncHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* past action functions */
|
||||
void dmPastActRptRcvEnable(dmPastMsg_t *pMsg);
|
||||
void dmPastActSyncTsfr(dmPastMsg_t *pMsg);
|
||||
void dmPastActSetInfoTrsf(dmPastMsg_t *pMsg);
|
||||
void dmPastActConfig(dmPastMsg_t *pMsg);
|
||||
void dmPastActDefaultConfig(dmPastMsg_t *pMsg);
|
||||
|
||||
/* past component inteface */
|
||||
void dmPastMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmPastHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* sync utility functions */
|
||||
dmSyncCb_t *dmSyncCbById(dmSyncId_t syncId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_SCAN_H */
|
||||
Vendored
+362
@@ -0,0 +1,362 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager extended scan module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_scan.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* extended scan action function table */
|
||||
static const dmScanAct_t dmScanAct[] =
|
||||
{
|
||||
dmExtScanActStart,
|
||||
dmExtScanActStop,
|
||||
dmExtScanActTimeout
|
||||
};
|
||||
|
||||
/* extended scan component function interface */
|
||||
static const dmFcnIf_t dmScanFcnIf =
|
||||
{
|
||||
dmExtScanReset,
|
||||
dmExtScanHciHandler,
|
||||
dmExtScanMsgHandler
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* event being reported used for a legacy advertising PDU or a scan response */
|
||||
#define DM_ADV_RPT_SCAN_RSP(evtType) ((((evtType) == HCI_ADV_RPT_LEG_CONN_UNDIRECT_SCAN_RSP) || \
|
||||
((evtType) == HCI_ADV_RPT_LEG_SCAN_UNDIRECT_SCAN_RSP)) || \
|
||||
((((evtType) & HCI_ADV_RPT_LEG_ADV_BIT) == 0) && \
|
||||
((evtType) & HCI_ADV_RPT_SCAN_RSP_BIT)))
|
||||
|
||||
/* data status field of extended advertising report event type */
|
||||
#define DM_ADV_RPT_DATA_STATUS(evtType) (((evtType) & HCI_ADV_RPT_DATA_STATUS_BITS) >> 5)
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start extended scanning action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanActStart(dmScanMsg_t *pMsg)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t idx;
|
||||
uint8_t phyIdx;
|
||||
hciExtScanParam_t scanParam[DM_NUM_PHYS];
|
||||
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_IDLE)
|
||||
{
|
||||
/* see advertising packets to be received on which PHY */
|
||||
for (i = 0, idx = 0; (i < 8) && (idx < DM_NUM_PHYS); i++)
|
||||
{
|
||||
if (pMsg->apiStart.scanPhys & (1 << i))
|
||||
{
|
||||
phyIdx = DmScanPhyToIdx(1 << i);
|
||||
|
||||
/* set extended scan parameters for this PHY */
|
||||
scanParam[idx].scanType = pMsg->apiStart.scanType[idx];
|
||||
scanParam[idx].scanInterval = dmScanCb.scanInterval[phyIdx];
|
||||
scanParam[idx].scanWindow = dmScanCb.scanWindow[phyIdx];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
/* set extended scan parameters to be used on primary advertising channel */
|
||||
HciLeSetExtScanParamCmd(DmLlAddrType(dmCb.scanAddrType), dmCb.scanFiltPolicy,
|
||||
pMsg->apiStart.scanPhys, scanParam);
|
||||
|
||||
/* initialize scan result filtering */
|
||||
if (pMsg->apiStart.mode == DM_DISC_MODE_LIMITED)
|
||||
{
|
||||
dmScanCb.discFilter = DM_FLAG_LE_LIMITED_DISC;
|
||||
}
|
||||
else if (pMsg->apiStart.mode == DM_DISC_MODE_GENERAL)
|
||||
{
|
||||
dmScanCb.discFilter = DM_FLAG_LE_LIMITED_DISC | DM_FLAG_LE_GENERAL_DISC;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmScanCb.discFilter = 0;
|
||||
}
|
||||
|
||||
dmScanCb.filterNextScanRsp = FALSE;
|
||||
|
||||
/* enable scan */
|
||||
dmScanCb.scanState = DM_SCAN_STATE_STARTING;
|
||||
HciLeExtScanEnableCmd(TRUE, pMsg->apiStart.filterDup, (pMsg->apiStart.duration / 10), pMsg->apiStart.period);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop extended scanning action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanActStop(dmScanMsg_t *pMsg)
|
||||
{
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_SCANNING)
|
||||
{
|
||||
/* disable scan */
|
||||
dmScanCb.scanState = DM_SCAN_STATE_STOPPING;
|
||||
HciLeExtScanEnableCmd(FALSE, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an scan timeout.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanActTimeout(dmScanMsg_t *pMsg)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an extended advertising report event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmExtScanActHciReport(hciEvt_t *pEvent)
|
||||
{
|
||||
uint8_t *p;
|
||||
static bool_t filtered = FALSE;
|
||||
static bool_t firstFrag = TRUE;
|
||||
|
||||
/* ignore if not scanning */
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_SCANNING)
|
||||
{
|
||||
/* if filtering results for limited or general discovery, and first fragment */
|
||||
if ((dmScanCb.discFilter != 0) && firstFrag)
|
||||
{
|
||||
/* if this is a scan response */
|
||||
if (DM_ADV_RPT_SCAN_RSP(pEvent->leExtAdvReport.eventType))
|
||||
{
|
||||
/* check if filtering next scan response */
|
||||
if (dmScanCb.filterNextScanRsp)
|
||||
{
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = FALSE;
|
||||
}
|
||||
}
|
||||
/* else it's an advertising response */
|
||||
else
|
||||
{
|
||||
/* find flags in advertising data */
|
||||
p = DmFindAdType(DM_ADV_TYPE_FLAGS, pEvent->leExtAdvReport.len, pEvent->leExtAdvReport.pData);
|
||||
if (p == NULL)
|
||||
{
|
||||
/* flags not found */
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = TRUE;
|
||||
}
|
||||
/* else flags found; check them */
|
||||
else if ((p[DM_AD_DATA_IDX] & dmScanCb.discFilter) == 0)
|
||||
{
|
||||
/* flags do not match discovery mode */
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
firstFrag = FALSE;
|
||||
}
|
||||
|
||||
if (!filtered)
|
||||
{
|
||||
pEvent->hdr.event = DM_EXT_SCAN_REPORT_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
|
||||
/* if filtering results for limited or general discovery, and no more fragmented data to come */
|
||||
if ((dmScanCb.discFilter != 0) &&
|
||||
(DM_ADV_RPT_DATA_STATUS(pEvent->leExtAdvReport.eventType) != HCI_ADV_RPT_DATA_INCMPL_MORE))
|
||||
{
|
||||
/* reset our flags */
|
||||
filtered = FALSE;
|
||||
firstFrag = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM extended scan reset function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanReset(void)
|
||||
{
|
||||
hciLeScanTimeoutEvt_t scanTimeout;
|
||||
|
||||
/* if stopping scan or scanning */
|
||||
if ((dmScanCb.scanState == DM_SCAN_STATE_STOPPING) ||
|
||||
(dmScanCb.scanState == DM_SCAN_STATE_SCANNING))
|
||||
{
|
||||
/* generate HCI scan timeout event */
|
||||
scanTimeout.hdr.event = HCI_LE_SCAN_TIMEOUT_CBACK_EVT;
|
||||
scanTimeout.hdr.status = HCI_SUCCESS;
|
||||
|
||||
/* handle the event */
|
||||
dmExtScanHciHandler((hciEvt_t *) &scanTimeout);
|
||||
}
|
||||
|
||||
/* reset scan module */
|
||||
dmScanInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM extended scan HCI event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
DM_TRACE_INFO2("dmExtScanHciHandler: event: %d state: %d", pEvent->hdr.event, dmScanCb.scanState);
|
||||
|
||||
if (pEvent->hdr.event == HCI_LE_EXT_ADV_REPORT_CBACK_EVT)
|
||||
{
|
||||
dmExtScanActHciReport(pEvent);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_LE_SCAN_TIMEOUT_CBACK_EVT)
|
||||
{
|
||||
dmScanCb.scanState = DM_SCAN_STATE_IDLE;
|
||||
|
||||
/* pass scanning stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_RPA_STOP, DM_EXT_SCAN_STOP_IND, 0, 0);
|
||||
|
||||
/* call callback */
|
||||
pEvent->hdr.event = DM_EXT_SCAN_STOP_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_LE_EXT_SCAN_ENABLE_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
switch (dmScanCb.scanState)
|
||||
{
|
||||
case DM_SCAN_STATE_STARTING:
|
||||
pEvent->hdr.event = DM_EXT_SCAN_START_IND;
|
||||
dmScanCb.scanState = pEvent->hdr.status == HCI_SUCCESS? DM_SCAN_STATE_SCANNING : DM_SCAN_STATE_IDLE;
|
||||
break;
|
||||
|
||||
case DM_SCAN_STATE_STOPPING:
|
||||
pEvent->hdr.event = DM_EXT_SCAN_STOP_IND;
|
||||
dmScanCb.scanState = pEvent->hdr.status == HCI_SUCCESS? DM_SCAN_STATE_IDLE : DM_SCAN_STATE_SCANNING;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Should never happen */
|
||||
WSF_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* pass scanning start/stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv((pEvent->hdr.event == DM_EXT_SCAN_START_IND) ? \
|
||||
DM_DEV_PRIV_MSG_RPA_START : DM_DEV_PRIV_MSG_RPA_STOP, pEvent->hdr.event,
|
||||
0, 0);
|
||||
|
||||
/* call callback */
|
||||
(*dmCb.cback)((dmEvt_t *)pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM scan event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmScanAct[DM_MSG_MASK(pMsg->event)])((dmScanMsg_t *)pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM extended scanning.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmExtScanInit(void)
|
||||
{
|
||||
WsfTaskLock();
|
||||
|
||||
/* set function interface table */
|
||||
dmFcnIfTbl[DM_ID_SCAN] = (dmFcnIf_t *) &dmScanFcnIf;
|
||||
|
||||
/* initialize scan/sync modules */
|
||||
dmScanInit();
|
||||
dmSyncInit();
|
||||
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Whether DM scanning is in extended mode.
|
||||
*
|
||||
* \return TRUE if DM scanning is in extended mode. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmScanModeExt(void)
|
||||
{
|
||||
return (dmFcnIfTbl[DM_ID_SCAN] == (dmFcnIf_t *) &dmScanFcnIf) ? TRUE : FALSE;
|
||||
}
|
||||
Vendored
+336
@@ -0,0 +1,336 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager scan module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_scan.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmScanAct_t dmScanAct[] =
|
||||
{
|
||||
dmScanActStart,
|
||||
dmScanActStop,
|
||||
dmScanActTimeout
|
||||
};
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmScanFcnIf =
|
||||
{
|
||||
dmScanReset,
|
||||
dmScanHciHandler,
|
||||
dmScanMsgHandler
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start scanning action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanActStart(dmScanMsg_t *pMsg)
|
||||
{
|
||||
uint8_t phyIdx = DmScanPhyToIdx(HCI_SCAN_PHY_LE_1M_BIT);
|
||||
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_IDLE)
|
||||
{
|
||||
/* set scan parameters */
|
||||
HciLeSetScanParamCmd(pMsg->apiStart.scanType[phyIdx], dmScanCb.scanInterval[phyIdx],
|
||||
dmScanCb.scanWindow[phyIdx], DmLlAddrType(dmCb.scanAddrType),
|
||||
dmCb.scanFiltPolicy);
|
||||
|
||||
/* initialize scan result filtering */
|
||||
if (pMsg->apiStart.mode == DM_DISC_MODE_LIMITED)
|
||||
{
|
||||
dmScanCb.discFilter = DM_FLAG_LE_LIMITED_DISC;
|
||||
}
|
||||
else if (pMsg->apiStart.mode == DM_DISC_MODE_GENERAL)
|
||||
{
|
||||
dmScanCb.discFilter = DM_FLAG_LE_LIMITED_DISC | DM_FLAG_LE_GENERAL_DISC;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmScanCb.discFilter = 0;
|
||||
}
|
||||
dmScanCb.filterNextScanRsp = FALSE;
|
||||
|
||||
/* enable scan */
|
||||
dmScanCb.scanDuration = pMsg->apiStart.duration;
|
||||
dmScanCb.scanState = DM_SCAN_STATE_STARTING;
|
||||
HciLeSetScanEnableCmd(TRUE, pMsg->apiStart.filterDup);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop scanning action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanActStop(dmScanMsg_t *pMsg)
|
||||
{
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_SCANNING)
|
||||
{
|
||||
/* disable scan */
|
||||
dmScanCb.scanState = DM_SCAN_STATE_STOPPING;
|
||||
HciLeSetScanEnableCmd(FALSE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an scan timeout.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanActTimeout(dmScanMsg_t *pMsg)
|
||||
{
|
||||
dmScanActStop(pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an advertising report event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmScanActHciReport(hciEvt_t *pEvent)
|
||||
{
|
||||
uint8_t *p;
|
||||
bool_t filtered = FALSE;
|
||||
|
||||
/* ignore if not scanning */
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_SCANNING)
|
||||
{
|
||||
/* if filtering results for limited or general discovery */
|
||||
if (dmScanCb.discFilter != 0)
|
||||
{
|
||||
/* if this is a scan response */
|
||||
if (pEvent->leAdvReport.eventType == DM_RPT_SCAN_RESPONSE)
|
||||
{
|
||||
/* check if filtering next scan response */
|
||||
if (dmScanCb.filterNextScanRsp)
|
||||
{
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = FALSE;
|
||||
}
|
||||
}
|
||||
/* else it's an advertising response */
|
||||
else
|
||||
{
|
||||
/* find flags in advertising data */
|
||||
p = DmFindAdType(DM_ADV_TYPE_FLAGS, pEvent->leAdvReport.len, pEvent->leAdvReport.pData);
|
||||
if (p == NULL)
|
||||
{
|
||||
/* flags not found */
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = TRUE;
|
||||
}
|
||||
/* else flags found; check them */
|
||||
else if ((p[DM_AD_DATA_IDX] & dmScanCb.discFilter) == 0)
|
||||
{
|
||||
/* flags do not match discovery mode */
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!filtered)
|
||||
{
|
||||
pEvent->hdr.event = DM_SCAN_REPORT_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the scan module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanReset(void)
|
||||
{
|
||||
wsfMsgHdr_t scanStop;
|
||||
|
||||
/* if stopping scan or scanning */
|
||||
if ((dmScanCb.scanState == DM_SCAN_STATE_STOPPING) ||
|
||||
(dmScanCb.scanState == DM_SCAN_STATE_SCANNING))
|
||||
{
|
||||
/* stop scan timer */
|
||||
WsfTimerStop(&dmScanCb.scanTimer);
|
||||
|
||||
/* generate scan stop event */
|
||||
scanStop.event = DM_SCAN_STOP_IND;
|
||||
scanStop.status = HCI_SUCCESS;
|
||||
|
||||
/* call callback */
|
||||
(*dmCb.cback)((dmEvt_t *) &scanStop);
|
||||
|
||||
dmScanCb.scanState = DM_SCAN_STATE_IDLE;
|
||||
}
|
||||
|
||||
/* reset scan module */
|
||||
dmScanInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM scan HCI event handler.
|
||||
*
|
||||
* \param pEvent WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
if (pEvent->hdr.event == HCI_LE_ADV_REPORT_CBACK_EVT)
|
||||
{
|
||||
dmScanActHciReport(pEvent);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_LE_SCAN_ENABLE_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
switch (dmScanCb.scanState)
|
||||
{
|
||||
case DM_SCAN_STATE_STARTING:
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
/* start scan timer if applicable */
|
||||
if (dmScanCb.scanDuration > 0)
|
||||
{
|
||||
dmScanCb.scanTimer.msg.event = DM_SCAN_MSG_TIMEOUT;
|
||||
WsfTimerStartMs(&dmScanCb.scanTimer, dmScanCb.scanDuration);
|
||||
}
|
||||
|
||||
dmScanCb.scanState = DM_SCAN_STATE_SCANNING;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmScanCb.scanState = DM_SCAN_STATE_IDLE;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_SCAN_START_IND;
|
||||
break;
|
||||
|
||||
case DM_SCAN_STATE_STOPPING:
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
/* stop scan timer */
|
||||
WsfTimerStop(&dmScanCb.scanTimer);
|
||||
dmScanCb.scanState = DM_SCAN_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmScanCb.scanState = DM_SCAN_STATE_SCANNING;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_SCAN_STOP_IND;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/* Should never happen */
|
||||
WSF_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* pass scanning start/stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv((pEvent->hdr.event == DM_SCAN_START_IND) ? \
|
||||
DM_DEV_PRIV_MSG_RPA_START : DM_DEV_PRIV_MSG_RPA_STOP, pEvent->hdr.event,
|
||||
0, 0);
|
||||
|
||||
/* call callback */
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM scan event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmScanAct[DM_MSG_MASK(pMsg->event)])((dmScanMsg_t *)pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM legacy scanning.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmScanInit(void)
|
||||
{
|
||||
WsfTaskLock();
|
||||
|
||||
/* set function interface table */
|
||||
dmFcnIfTbl[DM_ID_SCAN] = (dmFcnIf_t *) &dmScanFcnIf;
|
||||
|
||||
/* initialize scan module */
|
||||
dmScanInit();
|
||||
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Whether DM scanning is in legacy mode.
|
||||
*
|
||||
* \return TRUE if DM scanning is in legacy mode. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmScanModeLeg(void)
|
||||
{
|
||||
return (dmFcnIfTbl[DM_ID_SCAN] == (dmFcnIf_t *) &dmScanFcnIf) ? TRUE : FALSE;
|
||||
}
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM security module.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/calc128.h"
|
||||
#include "smp_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_sec.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
dmSecCb_t dmSecCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmSecFcnIf =
|
||||
{
|
||||
dmSecReset,
|
||||
dmSecHciHandler,
|
||||
(dmMsgHandler_t) dmSecMsgHandler
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Utility function for API encrypt request or ltk response.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param status HCI_SUCCESS if LTK available.
|
||||
* \param *pLTK Pointer to LTK parameter structure.
|
||||
* \param event DM handler event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSecApiLtkMsg(dmConnId_t connId, uint8_t status, dmSecLtk_t *pLtk, uint8_t event)
|
||||
{
|
||||
dmSecApiEncryptReq_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmSecApiEncryptReq_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = event;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->hdr.status = status;
|
||||
memcpy(&pMsg->ltk, pLtk, sizeof(dmSecLtk_t));
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM dev HCI event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSecHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
uint8_t *pKey;
|
||||
dmSecEncryptIndEvt_t encryptInd;
|
||||
uint8_t secLevel;
|
||||
|
||||
if ((pCcb = dmConnCcbByHandle(pEvent->hdr.param)) != NULL)
|
||||
{
|
||||
if (pEvent->hdr.event == HCI_LE_LTK_REQ_CBACK_EVT)
|
||||
{
|
||||
/* if ediv and rand are zero then check if STK is available from SMP */
|
||||
if ((pEvent->leLtkReq.encDiversifier == 0) &&
|
||||
(memcmp(pEvent->leLtkReq.randNum, calc128Zeros, HCI_RAND_LEN) == 0))
|
||||
{
|
||||
if ((pKey = SmpDmGetStk(pCcb->connId, &secLevel)) != NULL)
|
||||
{
|
||||
/* store security level */
|
||||
pCcb->tmpSecLevel = secLevel;
|
||||
|
||||
/* not using LTK */
|
||||
pCcb->usingLtk = FALSE;
|
||||
|
||||
/* provide key to HCI */
|
||||
HciLeLtkReqReplCmd(pEvent->hdr.param, pKey);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* call callback to get key from app */
|
||||
|
||||
/* set connection busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_DM_ENC, DM_CONN_BUSY);
|
||||
|
||||
/* using LTK */
|
||||
pCcb->usingLtk = TRUE;
|
||||
|
||||
/* use the header from the encryptInd struct for efficiency */
|
||||
pEvent->hdr.param = pCcb->connId;
|
||||
pEvent->hdr.event = DM_SEC_LTK_REQ_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_ENC_KEY_REFRESH_CMPL_CBACK_EVT ||
|
||||
pEvent->hdr.event == HCI_ENC_CHANGE_CBACK_EVT)
|
||||
{
|
||||
/* set connection idle */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_DM_ENC, DM_CONN_IDLE);
|
||||
|
||||
encryptInd.hdr.param = pCcb->connId;
|
||||
encryptInd.hdr.status = pEvent->hdr.status;
|
||||
if (encryptInd.hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
encryptInd.hdr.event = DM_SEC_ENCRYPT_IND;
|
||||
|
||||
/* update security level of connection */
|
||||
pCcb->secLevel = pCcb->tmpSecLevel;
|
||||
|
||||
/* set LTK flag */
|
||||
encryptInd.usingLtk = pCcb->usingLtk;
|
||||
}
|
||||
else
|
||||
{
|
||||
encryptInd.hdr.event = DM_SEC_ENCRYPT_FAIL_IND;
|
||||
}
|
||||
|
||||
/* call callback before passing to SMP */
|
||||
DmSmpCbackExec((dmEvt_t *) &encryptInd);
|
||||
|
||||
/* pass to SMP */
|
||||
encryptInd.hdr.param = pCcb->connId;
|
||||
encryptInd.hdr.status = pEvent->hdr.status;
|
||||
SmpDmEncryptInd((wsfMsgHdr_t *) &encryptInd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM dev event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSecMsgHandler(dmSecMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb */
|
||||
if ((pCcb = dmConnCcbById((dmConnId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
/* process API encrypt req */
|
||||
switch (pMsg->hdr.event)
|
||||
{
|
||||
case DM_SEC_MSG_API_ENCRYPT_REQ:
|
||||
/* set connection busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_DM_ENC, DM_CONN_BUSY);
|
||||
|
||||
/* store security level */
|
||||
pCcb->tmpSecLevel = pMsg->encryptReq.secLevel;
|
||||
|
||||
/* using LTK */
|
||||
pCcb->usingLtk = TRUE;
|
||||
|
||||
/* start encryption */
|
||||
HciLeStartEncryptionCmd(pCcb->handle, pMsg->encryptReq.ltk.rand,
|
||||
pMsg->encryptReq.ltk.ediv, pMsg->encryptReq.ltk.key);
|
||||
|
||||
break;
|
||||
|
||||
case DM_SEC_MSG_API_LTK_RSP:
|
||||
/* if key found */
|
||||
if (pMsg->ltkRsp.keyFound)
|
||||
{
|
||||
/* store security level */
|
||||
pCcb->tmpSecLevel = pMsg->ltkRsp.secLevel;
|
||||
|
||||
/* provide key to HCI */
|
||||
HciLeLtkReqReplCmd(pCcb->handle, pMsg->ltkRsp.key);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* key not found; set connection idle */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_DM_ENC, DM_CONN_IDLE);
|
||||
|
||||
HciLeLtkReqNegReplCmd(pCcb->handle);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only. Execute DM callback from SMP procedures.
|
||||
*
|
||||
* \param pDmEvt Pointer to callback event data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSmpCbackExec(dmEvt_t *pDmEvt)
|
||||
{
|
||||
/* certain messages need to get to ATT */
|
||||
if (pDmEvt->hdr.event == DM_SEC_PAIR_CMPL_IND ||
|
||||
pDmEvt->hdr.event == DM_SEC_ENCRYPT_IND)
|
||||
{
|
||||
if (dmConnCb.connCback[DM_CLIENT_ID_ATT] != NULL)
|
||||
{
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_ATT])(pDmEvt);
|
||||
}
|
||||
}
|
||||
|
||||
/* execute DM client callback */
|
||||
(*dmCb.cback)(pDmEvt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called to cancel the pairing process.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param reason Failure reason.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecCancelReq(dmConnId_t connId, uint8_t reason)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->event = SMP_MSG_API_CANCEL_REQ;
|
||||
pMsg->param = connId;
|
||||
pMsg->status = reason;
|
||||
|
||||
/* note we're sending this to SMP */
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called in response to a DM_SEC_AUTH_REQ_IND event to provide
|
||||
* PIN or OOB data during pairing.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param authDataLen Length of PIN or OOB data.
|
||||
* \param pAuthData pointer to PIN or OOB data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecAuthRsp(dmConnId_t connId, uint8_t authDataLen, uint8_t *pAuthData)
|
||||
{
|
||||
smpDmAuthRsp_t *pMsg;
|
||||
|
||||
WSF_ASSERT(authDataLen <= SMP_OOB_LEN);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmAuthRsp_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = SMP_MSG_API_AUTH_RSP;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->authDataLen = authDataLen;
|
||||
|
||||
if (pAuthData != NULL)
|
||||
{
|
||||
memcpy(pMsg->authData, pAuthData, authDataLen);
|
||||
}
|
||||
|
||||
/* note we're sending this to SMP */
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM security.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_SEC] = (dmFcnIf_t *) &dmSecFcnIf;
|
||||
|
||||
dmSecCb.pCsrk = dmSecCb.pIrk = (uint8_t *) calc128Zeros;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function sets the local CSRK used by the device.
|
||||
*
|
||||
* \param pCsrk Pointer to CSRK.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSetLocalCsrk(uint8_t *pCsrk)
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmSecCb.pCsrk = pCsrk;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function sets the local IRK used by the device.
|
||||
*
|
||||
* \param pCsrk Pointer to IRK.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSetLocalIrk(uint8_t *pIrk)
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmSecCb.pIrk = pIrk;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function gets the local CSRK used by the device.
|
||||
*
|
||||
* \return Pointer to CSRK.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *DmSecGetLocalCsrk(void)
|
||||
{
|
||||
return dmSecCb.pCsrk;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function gets the local IRK used by the device.
|
||||
*
|
||||
* \return Pointer to IRK.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *DmSecGetLocalIrk(void)
|
||||
{
|
||||
return dmSecCb.pIrk;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the sec module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSecReset(void)
|
||||
{
|
||||
/* initialize smp database */
|
||||
SmpDbInit();
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM security module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_SEC_H
|
||||
#define DM_SEC_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
#include "smp_api.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM sec event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_SEC_MSG_API_ENCRYPT_REQ = DM_MSG_START(DM_ID_SEC),
|
||||
DM_SEC_MSG_API_LTK_RSP
|
||||
};
|
||||
|
||||
/* DM lesc sec event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_SEC_MSG_CALC_OOB_CNF = DM_MSG_START(DM_ID_LESC),
|
||||
DM_SEC_MSG_ECC_KEY_CNF
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data type for DM_SEC_MSG_API_ENCRYPT_REQ */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmSecLtk_t ltk;
|
||||
uint8_t secLevel;
|
||||
} dmSecApiEncryptReq_t;
|
||||
|
||||
/* Data type for DM_SEC_MSG_API_LTK_RSP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t key[SMP_KEY_LEN];
|
||||
bool_t keyFound;
|
||||
uint8_t secLevel;
|
||||
} dmSecApiLtkRsp_t;
|
||||
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmSecApiEncryptReq_t encryptReq;
|
||||
dmSecApiLtkRsp_t ltkRsp;
|
||||
} dmSecMsg_t;
|
||||
|
||||
/* Security control block type */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t *pIrk;
|
||||
uint8_t *pCsrk;
|
||||
} dmSecCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
extern dmSecCb_t dmSecCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* component interface */
|
||||
void dmSecHciHandler(hciEvt_t *pEvent);
|
||||
void dmSecMsgHandler(dmSecMsg_t *pMsg);
|
||||
void dmSecReset(void);
|
||||
|
||||
/* component interface */
|
||||
void dmSecLescMsgHandler(dmSecMsg_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_SEC_H */
|
||||
Vendored
+328
@@ -0,0 +1,328 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM security module for LE Secure Connections.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/calc128.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_sc_main.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_sec.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmSecLescFcnIf =
|
||||
{
|
||||
dmEmptyReset,
|
||||
(dmHciHandler_t) dmEmptyHandler,
|
||||
(dmMsgHandler_t) dmSecLescMsgHandler
|
||||
};
|
||||
|
||||
/* OOB random value */
|
||||
static uint8_t *dmSecOobRand;
|
||||
|
||||
/* Local device's ECC Key */
|
||||
static secEccKey_t localEccKey;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM dev event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSecLescMsgHandler(dmSecMsg_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.event == DM_SEC_MSG_ECC_KEY_CNF)
|
||||
{
|
||||
pMsg->hdr.event = DM_SEC_ECC_KEY_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
else if (pMsg->hdr.event == DM_SEC_MSG_CALC_OOB_CNF)
|
||||
{
|
||||
dmSecOobCalcIndEvt_t oobEvt;
|
||||
secCmacMsg_t *pCmacMsg = (secCmacMsg_t *) pMsg;
|
||||
|
||||
WsfBufFree(pCmacMsg->pPlainText);
|
||||
|
||||
/* Notify the application of the local confirm and random values */
|
||||
oobEvt.hdr.event = DM_SEC_CALC_OOB_IND;
|
||||
oobEvt.hdr.status = HCI_SUCCESS;
|
||||
|
||||
Calc128Cpy(oobEvt.confirm, ((secAes_t *) pMsg)->pCiphertext);
|
||||
Calc128Cpy(oobEvt.random, dmSecOobRand);
|
||||
|
||||
WsfBufFree(dmSecOobRand);
|
||||
|
||||
(*dmCb.cback)((dmEvt_t *) &oobEvt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function sends keypress cmd messages to the peer.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecKeypressReq(dmConnId_t connId, uint8_t keypressType)
|
||||
{
|
||||
smpDmKeypress_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmKeypress_t))) != NULL)
|
||||
{
|
||||
/* Execution an an SMP state machine event to send the keypress to the peer device */
|
||||
pMsg->keypress = keypressType;
|
||||
pMsg->hdr.param = connId;
|
||||
|
||||
pMsg->hdr.event = SMP_MSG_API_USER_KEYPRESS;
|
||||
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function records the peer random value and peer confirm value exchanged via
|
||||
* out-of-band (OOB) methods.
|
||||
*
|
||||
* \param connId ID of the connection
|
||||
* \param pCfg OOB Configuration
|
||||
*
|
||||
* \return Pointer to IRK.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSetOob(dmConnId_t connId, dmSecLescOobCfg_t *pCfg)
|
||||
{
|
||||
/* Update the Security Manager control structure with random and confirm values from the peer */
|
||||
SmpScSetOobCfg(connId, pCfg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function calcualtes the local random and confirm values used in LESC OOB pairing.
|
||||
* The operation's result is posted as a DM_SEC_CALC_OOB_IND event to the application's DM
|
||||
* callback handler. The local rand and confirm values are exchanged with the peer via
|
||||
* out-of-band (OOB) methods and passed into the DmSecSetOob after DM_CONN_OPEN_IND.
|
||||
*
|
||||
* \param pRand Random value used in calculation.
|
||||
* \param pPubKeyX X component of the local public key.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecCalcOobReq(uint8_t *pRand, uint8_t *pPubKeyX)
|
||||
{
|
||||
uint8_t *pCmacText;
|
||||
dmSecOobCalcIndEvt_t pEvt;
|
||||
|
||||
SMP_TRACE_256("DmSecCalcOobReq Key", pPubKeyX);
|
||||
SMP_TRACE_128("DmSecCalcOobReq Rand", pRand);
|
||||
|
||||
if ((dmSecOobRand = WsfBufAlloc(SMP_RAND_LEN)) != NULL)
|
||||
{
|
||||
/* Store the random value */
|
||||
Calc128Cpy(dmSecOobRand, pRand);
|
||||
|
||||
/* Cnf = f4(PKx, PKx, rand, 0x00) where f4(U, V, x, Z) = AES-CMACx (U || V || Z) */
|
||||
if ((pCmacText = (uint8_t*) WsfBufAlloc(SMP_F4_TEXT_LEN)) != NULL)
|
||||
{
|
||||
uint8_t *pCatBuf = pCmacText;
|
||||
|
||||
/* Concatinate PKx, PKx, 0x00 */
|
||||
pCatBuf = SmpScCat(pCatBuf, pPubKeyX, SMP_PUB_KEY_LEN);
|
||||
pCatBuf = SmpScCat(pCatBuf, pPubKeyX, SMP_PUB_KEY_LEN);
|
||||
*pCatBuf = 0;
|
||||
|
||||
/* Execute CMAC with rand as the key */
|
||||
if (SecCmac(dmSecOobRand, pCmacText, SMP_F4_TEXT_LEN, dmCb.handlerId, 0, DM_SEC_MSG_CALC_OOB_CNF))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WsfBufFree(pCmacText);
|
||||
}
|
||||
|
||||
WsfBufFree(dmSecOobRand);
|
||||
}
|
||||
|
||||
/* Notify the application of a failure */
|
||||
memset(&pEvt, 0, sizeof(dmSecOobCalcIndEvt_t));
|
||||
|
||||
pEvt.hdr.event = DM_SEC_CALC_OOB_IND;
|
||||
pEvt.hdr.status = HCI_ERR_MEMORY_EXCEEDED;
|
||||
|
||||
(*dmCb.cback)((dmEvt_t *) &pEvt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function generates an ECC key for use with LESC security. When the calculation
|
||||
* completes, the result is posted as a DM_SEC_ECC_KEY_IND event to the application's
|
||||
* DM callback handler.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecGenerateEccKeyReq()
|
||||
{
|
||||
SecEccGenKey(dmCb.handlerId, 0, DM_SEC_MSG_ECC_KEY_CNF);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function sets the ECC key for use with LESC security to standard debug keys values.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSetDebugEccKey()
|
||||
{
|
||||
const uint8_t privateKey[] = {0x3f, 0x49, 0xf6, 0xd4, 0xa3, 0xc5, 0x5f, 0x38,
|
||||
0x74, 0xc9, 0xb3, 0xe3, 0xd2, 0x10, 0x3f, 0x50,
|
||||
0x4a, 0xff, 0x60, 0x7b, 0xeb, 0x40, 0xb7, 0x99,
|
||||
0x58, 0x99, 0xb8, 0xa6, 0xcd, 0x3c, 0x1a, 0xbd};
|
||||
|
||||
const uint8_t publicKeyX[] = {0x20, 0xb0, 0x03, 0xd2, 0xf2, 0x97, 0xbe, 0x2c,
|
||||
0x5e, 0x2c, 0x83, 0xa7, 0xe9, 0xf9, 0xa5, 0xb9,
|
||||
0xef, 0xf4, 0x91, 0x11, 0xac, 0xf4, 0xfd, 0xdb,
|
||||
0xcc, 0x03, 0x01, 0x48, 0x0e, 0x35, 0x9d, 0xe6};
|
||||
|
||||
const uint8_t publicKeyY[] = {0xdc, 0x80, 0x9c, 0x49, 0x65, 0x2a, 0xeb, 0x6d,
|
||||
0x63, 0x32, 0x9a, 0xbf, 0x5a, 0x52, 0x15, 0x5c,
|
||||
0x76, 0x63, 0x45, 0xc2, 0x8f, 0xed, 0x30, 0x24,
|
||||
0x74, 0x1c, 0x8e, 0xd0, 0x15, 0x89, 0xd2, 0x8b};
|
||||
|
||||
SMP_TRACE_INFO0("Debug LESC Keys Enabled");
|
||||
|
||||
memcpy(localEccKey.privKey, privateKey, SEC_ECC_KEY_LEN);
|
||||
memcpy(localEccKey.pubKey_x, publicKeyX, SEC_ECC_KEY_LEN);
|
||||
memcpy(localEccKey.pubKey_y, publicKeyY, SEC_ECC_KEY_LEN);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function sets the local ECC key for use with LESC security. The key can be
|
||||
* generated using DmSecGenerateEccKeyReq or the key could originate from an application
|
||||
* specific source (e.g. prestored in non-volatile memory, generated with specialized
|
||||
* hardware, etc).
|
||||
*
|
||||
* \param pKey Pointer to local ECC key
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSetEccKey(secEccKey_t *pKey)
|
||||
{
|
||||
memcpy(&localEccKey, pKey, sizeof(secEccKey_t));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function gets the local ECC key for use with LESC security.
|
||||
*
|
||||
* \return Pointer to local ECC key.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
secEccKey_t *DmSecGetEccKey(void)
|
||||
{
|
||||
return &localEccKey;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by the application in response to a DM_SEC_COMPARE_IND event.
|
||||
* The valid parameter indicates if the compare value of the DM_SEC_COMPARE_IND was valid.
|
||||
*
|
||||
* \param connId ID of the connection
|
||||
* \param valid TRUE if compare value was valid
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecCompareRsp(dmConnId_t connId, bool_t valid)
|
||||
{
|
||||
smpDmMsg_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmMsg_t))) != NULL)
|
||||
{
|
||||
/* Execution an an SMP state machine event to send the compare response */
|
||||
pMsg->hdr.param = connId;
|
||||
|
||||
if (valid)
|
||||
{
|
||||
pMsg->hdr.event = SMP_MSG_API_USER_CONFIRM;
|
||||
}
|
||||
else
|
||||
{
|
||||
SmpScGetCancelMsgWithReattempt(connId, (wsfMsgHdr_t *) pMsg, SMP_ERR_NUMERIC_COMPARISON);
|
||||
}
|
||||
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function returns the 6-digit compare value for the specified 128-bit confirm value.
|
||||
*
|
||||
* \param pConfirm Pointer to 128-bit comfirm value.
|
||||
*
|
||||
* \return Six-digit compare value.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t DmSecGetCompareValue(uint8_t *pConfirm)
|
||||
{
|
||||
uint32_t compare = ((uint32_t) pConfirm[15] +
|
||||
((uint32_t) pConfirm[14] << 8) +
|
||||
((uint32_t) pConfirm[13] << 16) +
|
||||
((uint32_t) pConfirm[12] << 24));
|
||||
|
||||
/* return the least significant six digits */
|
||||
return compare % 1000000;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM LE Secure Connections security.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecLescInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_LESC] = (dmFcnIf_t *) &dmSecLescFcnIf;
|
||||
}
|
||||
Vendored
+121
@@ -0,0 +1,121 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM security module for master.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/calc128.h"
|
||||
#include "smp_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_sec.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only. This function is called by SMP to request encryption.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param secLevel Security level of pairing when key was exchanged.
|
||||
* \param pKey Pointer to key.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSmpEncryptReq(dmConnId_t connId, uint8_t secLevel, uint8_t *pKey)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
if ((pCcb = dmConnCcbById(connId)) != NULL)
|
||||
{
|
||||
/* store security level */
|
||||
pCcb->tmpSecLevel = secLevel;
|
||||
|
||||
/* not using LTK */
|
||||
pCcb->usingLtk = FALSE;
|
||||
|
||||
/* start encryption; note EDIV and RAND are zero */
|
||||
HciLeStartEncryptionCmd(pCcb->handle, (uint8_t *) calc128Zeros, 0, pKey);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by a master device to initiate pairing.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param oob Out-of-band pairing data present or not present.
|
||||
* \param auth Authentication and bonding flags.
|
||||
* \param iKeyDist Initiator key distribution flags.
|
||||
* \param rKeyDist Responder key distribution flags.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecPairReq(dmConnId_t connId, uint8_t oob, uint8_t auth, uint8_t iKeyDist, uint8_t rKeyDist)
|
||||
{
|
||||
smpDmPair_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmPair_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = SMP_MSG_API_PAIR_REQ;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->oob = oob;
|
||||
pMsg->auth = auth;
|
||||
|
||||
/* clear any erroneous key dist bits set by app */
|
||||
pMsg->iKeyDist = iKeyDist & SMP_KEY_DIST_MASK;
|
||||
pMsg->rKeyDist = rKeyDist & SMP_KEY_DIST_MASK;
|
||||
|
||||
/* note we're sending this to SMP */
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by a master device to initiate link encryption.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param secLevel Security level of pairing when key was exchanged.
|
||||
* \param pLtk Pointer to LTK parameter structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecEncryptReq(dmConnId_t connId, uint8_t secLevel, dmSecLtk_t *pLtk)
|
||||
{
|
||||
dmSecApiEncryptReq_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmSecApiEncryptReq_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_SEC_MSG_API_ENCRYPT_REQ;
|
||||
pMsg->hdr.param = connId;
|
||||
memcpy(&pMsg->ltk, pLtk, sizeof(dmSecLtk_t));
|
||||
pMsg->secLevel = secLevel;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM security module for slave.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/calc128.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_sec.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by a slave device to proceed with pairing after a
|
||||
* DM_SEC_PAIR_IND event is received.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param oob Out-of-band pairing data present or not present.
|
||||
* \param auth Authentication and bonding flags.
|
||||
* \param iKeyDist Initiator key distribution flags.
|
||||
* \param rKeyDist Responder key distribution flags.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecPairRsp(dmConnId_t connId, uint8_t oob, uint8_t auth, uint8_t iKeyDist, uint8_t rKeyDist)
|
||||
{
|
||||
smpDmPair_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmPair_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = SMP_MSG_API_PAIR_RSP;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->oob = oob;
|
||||
pMsg->auth = auth;
|
||||
|
||||
/* clear any erroneous key dist bits set by app */
|
||||
pMsg->iKeyDist = iKeyDist & SMP_KEY_DIST_MASK;
|
||||
pMsg->rKeyDist = rKeyDist & SMP_KEY_DIST_MASK;
|
||||
|
||||
/* note we're sending this to SMP */
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by a slave device to request that the master initiates
|
||||
* pairing or link encryption.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param auth Authentication flags.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSlaveReq(dmConnId_t connId, uint8_t auth)
|
||||
{
|
||||
smpDmSecurityReq_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmSecurityReq_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = SMP_MSG_API_SECURITY_REQ;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->auth = auth;
|
||||
|
||||
/* note we're sending this to SMP */
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by a slave in response to a DM_SEC_LTK_REQ_IND event
|
||||
* to provide the key used for encryption.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param keyFound TRUE if key found.
|
||||
* \param secLevel Security level of pairing when key was exchanged.
|
||||
* \param *pKey Pointer to the key, if found.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecLtkRsp(dmConnId_t connId, bool_t keyFound, uint8_t secLevel, uint8_t *pKey)
|
||||
{
|
||||
dmSecApiLtkRsp_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmSecApiLtkRsp_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_SEC_MSG_API_LTK_RSP;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->keyFound = keyFound;
|
||||
pMsg->secLevel = secLevel;
|
||||
if (keyFound)
|
||||
{
|
||||
Calc128Cpy(pMsg->key, pKey);
|
||||
}
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
Vendored
+835
@@ -0,0 +1,835 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager periodic advertising synchronization management and state machine
|
||||
* module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_scan.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM sync event handler messages for state machine */
|
||||
enum
|
||||
{
|
||||
/* messages from API */
|
||||
DM_SYNC_MSG_API_START = DM_MSG_START(DM_ID_SYNC), /*!< Start Sync */
|
||||
DM_SYNC_MSG_API_STOP, /*!< Stop Sync */
|
||||
|
||||
/* messages from HCI */
|
||||
DM_SYNC_MSG_HCI_LE_SYNC_EST_FAIL, /*!< HCI LE Sync Establishment Failed */
|
||||
DM_SYNC_MSG_HCI_LE_SYNC_EST, /*!< HCI LE Sync Established */
|
||||
DM_SYNC_MSG_HCI_LE_SYNC_LOST, /*!< HCI LE Sync Lost */
|
||||
DM_SYNC_MSG_HCI_LE_SYNC_TRSF_EST_FAIL, /*!< HCI LE Sync Transfer Establishment Failed */
|
||||
DM_SYNC_MSG_HCI_LE_SYNC_TRSF_EST /*!< HCI LE Sync Transfer Established */
|
||||
};
|
||||
|
||||
/*! State machine states */
|
||||
enum
|
||||
{
|
||||
DM_SYNC_SM_ST_IDLE, /*!< Idle State */
|
||||
DM_SYNC_SM_ST_SYNCING, /*!< Synchronizing State */
|
||||
DM_SYNC_SM_ST_SYNCED, /*!< Synced State */
|
||||
DM_SYNC_SM_ST_DESYNCING, /*!< Desynchronizing State */
|
||||
|
||||
DM_SYNC_SM_NUM_STATES
|
||||
};
|
||||
|
||||
/*! State machine actions */
|
||||
enum
|
||||
{
|
||||
DM_SYNC_SM_ACT_NONE, /*!< No Action */
|
||||
DM_SYNC_SM_ACT_START, /*!< Process Start Sync */
|
||||
DM_SYNC_SM_ACT_STOP, /*!< Process Stop Sync */
|
||||
DM_SYNC_SM_ACT_CANCEL_START, /*!< Process Cancel Start Sync */
|
||||
DM_SYNC_SM_ACT_SYNC_EST, /*!< Process Sync Established */
|
||||
DM_SYNC_SM_ACT_SYNC_EST_FAILED, /*!< Process Sync Establishment Failed */
|
||||
DM_SYNC_SM_ACT_SYNC_LOST, /*!< Process Sync Lost */
|
||||
DM_SYNC_SM_ACT_SYNC_TRSF_EST, /*!< Process Sync Transfer Established */
|
||||
DM_SYNC_SM_ACT_SYNC_TRSF_EST_FAILED /*!< Process Sync Transfer Establishment Failed */
|
||||
};
|
||||
|
||||
/*! Column position of next state */
|
||||
#define DM_SYNC_NEXT_STATE 0
|
||||
|
||||
/*! Column position of action */
|
||||
#define DM_SYNC_ACTION 1
|
||||
|
||||
/*! Number of columns in the state machine state tables */
|
||||
#define DM_SYNC_NUM_COLS 2
|
||||
|
||||
/*! Number of messages */
|
||||
#define DM_SYNC_NUM_MSGS (DM_SYNC_MSG_HCI_LE_SYNC_TRSF_EST - DM_SYNC_MSG_API_START + 1)
|
||||
|
||||
/*! Translate HCI event to state machine message */
|
||||
#define DM_SYNC_HCI_EVT_2_MSG(evt) (DM_SYNC_MSG_HCI_LE_SYNC_LOST - HCI_LE_PER_ADV_SYNC_LOST_CBACK_EVT + (evt))
|
||||
|
||||
/*! Uninitialized HCI sync handle */
|
||||
#define DM_SYNC_HCI_HANDLE_NONE 0xFFFF
|
||||
|
||||
/*! Action function */
|
||||
typedef void (*dmSyncAct_t)(dmSyncCb_t *pCcb, dmSyncMsg_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM Sync state machine state tables */
|
||||
static const uint8_t dmSyncStateTbl[DM_SYNC_SM_NUM_STATES][DM_SYNC_NUM_MSGS][DM_SYNC_NUM_COLS] =
|
||||
{
|
||||
/* Idle state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_START */ {DM_SYNC_SM_ST_SYNCING, DM_SYNC_SM_ACT_START},
|
||||
/* API_STOP */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_EST */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_LOST */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_TRSF_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_TRSF_EST_FAILED},
|
||||
/* HCI_LE_SYNC_TRSF_EST */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_SYNC_TRSF_EST}
|
||||
},
|
||||
/* Syncing state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_START */ {DM_SYNC_SM_ST_SYNCING, DM_SYNC_SM_ACT_NONE},
|
||||
/* API_STOP */ {DM_SYNC_SM_ST_DESYNCING, DM_SYNC_SM_ACT_CANCEL_START},
|
||||
/* HCI_LE_SYNC_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_EST_FAILED},
|
||||
/* HCI_LE_SYNC_EST */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_SYNC_EST},
|
||||
/* HCI_LE_SYNC_LOST */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_EST_FAILED},
|
||||
/* HCI_LE_SYNC_TRSF_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_TRSF_EST_FAILED},
|
||||
/* HCI_LE_SYNC_TRSF_EST */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_SYNC_TRSF_EST},
|
||||
},
|
||||
/* Synced state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_START */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_NONE},
|
||||
/* API_STOP */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_STOP},
|
||||
/* HCI_LE_SYNC_EST_FAIL */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_EST */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_LOST */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_LOST},
|
||||
/* HCI_LE_SYNC_TRSF_EST_FAIL */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_TRSF_EST */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_NONE},
|
||||
},
|
||||
/* Desyncing state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_START */ {DM_SYNC_SM_ST_DESYNCING, DM_SYNC_SM_ACT_NONE},
|
||||
/* API_STOP */ {DM_SYNC_SM_ST_DESYNCING, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_EST_FAILED},
|
||||
/* HCI_LE_SYNC_EST */ {DM_SYNC_SM_ST_DESYNCING, DM_SYNC_SM_ACT_STOP},
|
||||
/* HCI_LE_SYNC_LOST */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_LOST},
|
||||
/* HCI_LE_SYNC_TRSF_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_TRSF_EST_FAILED},
|
||||
/* HCI_LE_SYNC_TRSF_EST */ {DM_SYNC_SM_ST_DESYNCING, DM_SYNC_SM_ACT_STOP},
|
||||
}
|
||||
};
|
||||
|
||||
/*! DM Sync action function table */
|
||||
static const dmSyncAct_t dmSyncAct[] =
|
||||
{
|
||||
dmSyncSmActNone,
|
||||
dmSyncSmActStart,
|
||||
dmSyncSmActStop,
|
||||
dmSyncSmActCancelStart,
|
||||
dmSyncSmActSyncEst,
|
||||
dmSyncSmActSyncEstFailed,
|
||||
dmSyncSmActSyncLost,
|
||||
dmSyncSmActSyncTrsfEst,
|
||||
dmSyncSmActSyncTrsfEstFailed
|
||||
};
|
||||
|
||||
/*! DM Sync component function interface */
|
||||
static const dmFcnIf_t dmSyncFcnIf =
|
||||
{
|
||||
dmSyncReset,
|
||||
dmSyncHciHandler,
|
||||
dmSyncMsgHandler
|
||||
};
|
||||
|
||||
/*! DM Periodic advertising sync control block */
|
||||
static dmSyncCb_t dmSyncCb[DM_SYNC_MAX];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the SCB with particular state conditions.
|
||||
*
|
||||
* \return Pointer to SCB or NULL if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static dmSyncCb_t *dmSyncCmplState(void)
|
||||
{
|
||||
dmSyncCb_t *pScb = dmSyncCb;
|
||||
uint8_t i;
|
||||
|
||||
/* if there's a scb in accepting state */
|
||||
for (i = DM_SYNC_MAX; i > 0; i--, pScb++)
|
||||
{
|
||||
/* look for sync in desyncing state, cancelled sync */
|
||||
if (pScb->inUse &&
|
||||
(pScb->state == DM_SYNC_SM_ST_DESYNCING) &&
|
||||
(pScb->handle == DM_SYNC_HCI_HANDLE_NONE))
|
||||
{
|
||||
DM_TRACE_INFO1("dmSyncCmplState %d", pScb->syncId);
|
||||
return pScb;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate a DM sync control block.
|
||||
*
|
||||
* \param sid Advertising Sid.
|
||||
* \param pAddr Advertiser BD address.
|
||||
*
|
||||
* \return Pointer to sync CB or NULL if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static dmSyncCb_t *dmSyncCbAlloc(uint8_t sid, const uint8_t *pAddr)
|
||||
{
|
||||
dmSyncCb_t *pScb = dmSyncCb;
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < DM_SYNC_MAX; i++, pScb++)
|
||||
{
|
||||
if (pScb->inUse == FALSE)
|
||||
{
|
||||
memset(pScb, 0, sizeof(dmSyncCb_t));
|
||||
|
||||
pScb->advSid = sid;
|
||||
BdaCpy(pScb->advAddr, pAddr);
|
||||
pScb->handle = DM_SYNC_HCI_HANDLE_NONE;
|
||||
pScb->syncId = i + 1;
|
||||
pScb->inUse = TRUE;
|
||||
|
||||
DM_TRACE_ALLOC1("dmSyncCbAlloc %d", pScb->syncId);
|
||||
|
||||
return pScb;
|
||||
}
|
||||
}
|
||||
|
||||
DM_TRACE_ERR0("dmSyncCbAlloc failed");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Deallocate a DM sync control block.
|
||||
*
|
||||
* \param pCb Sync control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmSyncCbDealloc(dmSyncCb_t *pCb)
|
||||
{
|
||||
DM_TRACE_FREE1("dmSyncCbDealloc %d", pCb->syncId);
|
||||
|
||||
pCb->inUse = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find a sync control block with matching handle.
|
||||
*
|
||||
* \param handle Handle to find.
|
||||
*
|
||||
* \return Pointer to sync CB or NULL if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static dmSyncCb_t *dmSyncCbByHandle(uint16_t handle)
|
||||
{
|
||||
dmSyncCb_t *pScb = dmSyncCb;
|
||||
uint8_t i;
|
||||
|
||||
for (i = DM_SYNC_MAX; i > 0; i--, pScb++)
|
||||
{
|
||||
if (pScb->inUse && (pScb->handle == handle))
|
||||
{
|
||||
return pScb;
|
||||
}
|
||||
}
|
||||
|
||||
DM_TRACE_WARN1("dmSyncCbByHandle not found 0x%04x", handle);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find a sync control block with advertising SID and advertiser BD address.
|
||||
*
|
||||
* \param sid Sid to find.
|
||||
* \param pAddr BD address to find.
|
||||
*
|
||||
* \return Pointer to CB or NULL if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmSyncCb_t *dmSyncCbBySidBdAddr(uint8_t sid, const uint8_t *pAddr)
|
||||
{
|
||||
dmSyncCb_t *pScb = dmSyncCb;
|
||||
uint8_t i;
|
||||
|
||||
for (i = DM_SYNC_MAX; i > 0; i--, pScb++)
|
||||
{
|
||||
if (pScb->inUse && (pScb->advSid == sid) && BdaCmp(pScb->advAddr, pAddr))
|
||||
{
|
||||
return pScb;
|
||||
}
|
||||
}
|
||||
|
||||
DM_TRACE_INFO0("dmSyncCbBySidBdAddr not found");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the sync control block for the given sync ID.
|
||||
*
|
||||
* \param syncId Sync ID.
|
||||
*
|
||||
* \return Pointer to CB or NULL if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmSyncCb_t *dmSyncCbById(dmSyncId_t syncId)
|
||||
{
|
||||
WSF_ASSERT((syncId > 0) && (syncId <= DM_SYNC_MAX));
|
||||
|
||||
syncId--;
|
||||
if (dmSyncCb[syncId].inUse)
|
||||
{
|
||||
return &dmSyncCb[syncId];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Empty action.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActNone(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Synchronize with periodic advertising from the given advertiser.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActStart(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
HciLePerAdvCreateSyncCmd(dmCb.syncOptions, pMsg->apiSyncStart.advSid,
|
||||
pMsg->apiSyncStart.advAddrType, pMsg->apiSyncStart.advAddr,
|
||||
pMsg->apiSyncStart.skip, pMsg->apiSyncStart.syncTimeout,
|
||||
pMsg->apiSyncStart.unused);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop reception of the periodic advertising identified by the given sync handle.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActStop(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
HciLePerAdvTerminateSyncCmd(pScb->handle);
|
||||
|
||||
dmSyncSmActSyncLost(pScb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel creation of a sync while it's pending.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActCancelStart(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
HciLePerAdvCreateSyncCancelCmd();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a sync established event from HCI.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActSyncEst(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
/* store adv sid, adv address and sync handle */
|
||||
pScb->advSid = pMsg->perAdvSyncEst.advSid;
|
||||
BdaCpy(pScb->advAddr, pMsg->perAdvSyncEst.advAddr);
|
||||
pScb->advAddrType = DmHostAddrType(pMsg->perAdvSyncEst.advAddrType);
|
||||
pScb->handle = pMsg->perAdvSyncEst.syncHandle;
|
||||
|
||||
pMsg->hdr.event = DM_PER_ADV_SYNC_EST_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a sync established failure event from HCI.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActSyncEstFailed(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
/* deallocate scb */
|
||||
dmSyncCbDealloc(pScb);
|
||||
|
||||
pMsg->hdr.event = DM_PER_ADV_SYNC_EST_FAIL_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a sync lost event from HCI.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActSyncLost(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
/* deallocate scb */
|
||||
dmSyncCbDealloc(pScb);
|
||||
|
||||
pMsg->hdr.event = DM_PER_ADV_SYNC_LOST_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a sync transfer established event from HCI.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActSyncTrsfEst(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
/* store adv sid, adv address and sync handle */
|
||||
pScb->advSid = pMsg->perAdvSyncTrsfEst.advSid;
|
||||
BdaCpy(pScb->advAddr, pMsg->perAdvSyncTrsfEst.advAddr);
|
||||
pScb->advAddrType = DmHostAddrType(pMsg->perAdvSyncTrsfEst.advAddrType);
|
||||
pScb->handle = pMsg->perAdvSyncTrsfEst.syncHandle;
|
||||
|
||||
pMsg->hdr.event = DM_PER_ADV_SYNC_TRSF_EST_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a sync transfer established failure event from HCI.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActSyncTrsfEstFailed(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
/* deallocate scb */
|
||||
dmSyncCbDealloc(pScb);
|
||||
|
||||
pMsg->hdr.event = DM_PER_ADV_SYNC_TRSF_EST_FAIL_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the DM sync state machine.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmExecute(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
uint8_t action;
|
||||
uint8_t event;
|
||||
|
||||
DM_TRACE_INFO2("dmSyncSmExecute event=%d state=%d", pMsg->hdr.event, pScb->state);
|
||||
|
||||
/* get the event */
|
||||
event = DM_MSG_MASK(pMsg->hdr.event);
|
||||
|
||||
/* get action */
|
||||
action = dmSyncStateTbl[pScb->state][event][DM_SYNC_ACTION];
|
||||
|
||||
/* set next state */
|
||||
pScb->state = dmSyncStateTbl[pScb->state][event][DM_SYNC_NEXT_STATE];
|
||||
|
||||
/* execute action function */
|
||||
(*dmSyncAct[action])(pScb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the sync module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_SYNC] = (dmFcnIf_t *) &dmSyncFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the sync module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncReset(void)
|
||||
{
|
||||
dmSyncCb_t *pScb = dmSyncCb;
|
||||
hciLePerAdvSyncLostEvt_t syncLost;
|
||||
uint8_t i;
|
||||
|
||||
/* generate HCI sync lost event */
|
||||
syncLost.hdr.event = HCI_LE_PER_ADV_SYNC_LOST_CBACK_EVT;
|
||||
syncLost.hdr.status = HCI_SUCCESS;
|
||||
|
||||
for (i = DM_SYNC_MAX; i > 0; i--, pScb++)
|
||||
{
|
||||
if (pScb->inUse)
|
||||
{
|
||||
/* set sync handle */
|
||||
syncLost.hdr.param = syncLost.syncHandle = pScb->handle;
|
||||
|
||||
/* handle the event */
|
||||
dmSyncHciHandler((hciEvt_t *) &syncLost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM sync HCI event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
dmSyncCb_t *pScb;
|
||||
|
||||
/* handle special case for periodic advertising report event */
|
||||
if (pEvent->hdr.event == HCI_LE_PER_ADV_REPORT_CBACK_EVT)
|
||||
{
|
||||
pEvent->hdr.event = DM_PER_ADV_REPORT_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle special cases for sync established event */
|
||||
if (pEvent->hdr.event == HCI_LE_PER_ADV_SYNC_EST_CBACK_EVT)
|
||||
{
|
||||
/* first check if scb exists for this sid and bd addr */
|
||||
pScb = dmSyncCbBySidBdAddr(pEvent->lePerAdvSyncEst.advSid, pEvent->lePerAdvSyncEst.advAddr);
|
||||
|
||||
/* if scb not found */
|
||||
if (pScb == NULL)
|
||||
{
|
||||
/* check for special case state */
|
||||
pScb = dmSyncCmplState();
|
||||
}
|
||||
|
||||
/* translate HCI event to state machine event */
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pEvent->hdr.event = DM_SYNC_MSG_HCI_LE_SYNC_EST;
|
||||
}
|
||||
else
|
||||
{
|
||||
pEvent->hdr.event = DM_SYNC_MSG_HCI_LE_SYNC_EST_FAIL;
|
||||
}
|
||||
}
|
||||
/* handle special cases for past received event */
|
||||
else if (pEvent->hdr.event == HCI_LE_PER_SYNC_TRSF_RCVD_CBACK_EVT)
|
||||
{
|
||||
/* first check if scb exists for this sid and bd addr */
|
||||
pScb = dmSyncCbBySidBdAddr(pEvent->lePerAdvSyncTrsfRcvd.advSid, pEvent->lePerAdvSyncTrsfRcvd.advAddr);
|
||||
|
||||
/* if scb not found */
|
||||
if (pScb == NULL)
|
||||
{
|
||||
/* allocate scb */
|
||||
pScb = dmSyncCbAlloc(pEvent->lePerAdvSyncTrsfRcvd.advSid, pEvent->lePerAdvSyncTrsfRcvd.advAddr);
|
||||
}
|
||||
|
||||
/* translate HCI event to state machine event */
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pEvent->hdr.event = DM_SYNC_MSG_HCI_LE_SYNC_TRSF_EST;
|
||||
}
|
||||
else
|
||||
{
|
||||
pEvent->hdr.event = DM_SYNC_MSG_HCI_LE_SYNC_TRSF_EST_FAIL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pScb = dmSyncCbByHandle(pEvent->hdr.param);
|
||||
|
||||
/* translate HCI event to state machine message */
|
||||
pEvent->hdr.event = DM_SYNC_HCI_EVT_2_MSG(pEvent->hdr.event);
|
||||
}
|
||||
|
||||
/* if scb found */
|
||||
if (pScb != NULL)
|
||||
{
|
||||
/* set sync id */
|
||||
pEvent->hdr.param = pScb->syncId;
|
||||
|
||||
/* execute state machine */
|
||||
dmSyncSmExecute(pScb, (dmSyncMsg_t *) pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM sync event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
dmSyncCb_t *pScb;
|
||||
|
||||
/* look up scb from sync id */
|
||||
if ((pScb = dmSyncCbById((dmSyncId_t) pMsg->param)) != NULL)
|
||||
{
|
||||
/* execute state machine */
|
||||
dmSyncSmExecute(pScb, (dmSyncMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Synchronize with periodic advertising from the given advertiser, and start receiving
|
||||
* periodic advertising packets.
|
||||
*
|
||||
* Note: The synchronization filter policy is used to determine whether the periodic
|
||||
* advertiser list is used. If the periodic advertiser list is not used, the
|
||||
* advertising SID, advertiser address type, and advertiser address parameters
|
||||
* specify the periodic advertising device to listen to; otherwise these parameters
|
||||
* are ignored.
|
||||
*
|
||||
* \param advSid Advertising SID.
|
||||
* \param advAddrType Advertiser address type.
|
||||
* \param pAdvAddr Advertiser address.
|
||||
* \param skip Number of periodic advertising packets that can be skipped after
|
||||
* successful receive.
|
||||
* \param syncTimeout Synchronization timeout.
|
||||
*
|
||||
* \return Sync identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmSyncId_t DmSyncStart(uint8_t advSid, uint8_t advAddrType, const uint8_t *pAdvAddr, uint16_t skip,
|
||||
uint16_t syncTimeout)
|
||||
{
|
||||
dmSyncCb_t *pScb = NULL;
|
||||
dmSyncApiStart_t *pMsg;
|
||||
|
||||
/* make sure scb not already allocated */
|
||||
WsfTaskLock();
|
||||
if ((pScb = dmSyncCbBySidBdAddr(advSid, pAdvAddr)) == NULL)
|
||||
{
|
||||
/* allocate scb */
|
||||
pScb = dmSyncCbAlloc(advSid, pAdvAddr);
|
||||
}
|
||||
WsfTaskUnlock();
|
||||
|
||||
if (pScb != NULL)
|
||||
{
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmSyncApiStart_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.param = pScb->syncId;
|
||||
pMsg->hdr.event = DM_SYNC_MSG_API_START;
|
||||
pMsg->advSid = advSid;
|
||||
pMsg->advAddrType = advAddrType;
|
||||
BdaCpy(pMsg->advAddr, pAdvAddr);
|
||||
pMsg->skip = skip;
|
||||
pMsg->syncTimeout = syncTimeout;
|
||||
pMsg->unused = 0;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
|
||||
/* return sync id */
|
||||
return pScb->syncId;
|
||||
}
|
||||
else
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmSyncCbDealloc(pScb);
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* open failed */
|
||||
return DM_SYNC_ID_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop reception of the periodic advertising identified by the given sync identifier.
|
||||
*
|
||||
* \param syncId Sync identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSyncStop(dmSyncId_t syncId)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->param = syncId;
|
||||
pMsg->event = DM_SYNC_MSG_API_STOP;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add device to periodic advertiser list.
|
||||
*
|
||||
* \param advAddrType Advertiser address type.
|
||||
* \param pAdvAddr Advertiser address.
|
||||
* \param advSid Advertising SID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAddDeviceToPerAdvList(uint8_t advAddrType, uint8_t *pAdvAddr, uint8_t advSid)
|
||||
{
|
||||
HciLeAddDeviceToPerAdvListCmd(advAddrType, pAdvAddr, advSid);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM remove device from periodic advertiser list.
|
||||
*
|
||||
* \param advAddrType Advertiser address type.
|
||||
* \param pAdvAddr Advertiser address.
|
||||
* \param advSid Advertising SID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmRemoveDeviceFromPerAdvList(uint8_t advAddrType, uint8_t *pAdvAddr, uint8_t advSid)
|
||||
{
|
||||
HciLeRemoveDeviceFromPerAdvListCmd(advAddrType, pAdvAddr, advSid);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM clear periodic advertiser list.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmClearPerAdvList(void)
|
||||
{
|
||||
HciLeClearPerAdvListCmd();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM enable or disable initial periodic advertising reports once synchronized.
|
||||
*
|
||||
* \param enable TRUE to enable initial reporting, FALSE to disable initial reporting.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSyncInitialRptEnable(bool_t enable)
|
||||
{
|
||||
WsfTaskLock();
|
||||
if (enable)
|
||||
{
|
||||
/* Enable initial periodic advertisement reporting */
|
||||
dmCb.syncOptions &= ~HCI_OPTIONS_INIT_RPT_ENABLE_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Disable initial periodic advertisement reporting */
|
||||
dmCb.syncOptions |= HCI_OPTIONS_INIT_RPT_ENABLE_BIT;
|
||||
}
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief HCI main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This is the main module of the HCI subsystem. It contains the API functions for initialization
|
||||
* and registration.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
hciCb_t hciCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback for HCI events.
|
||||
*
|
||||
* \param evtCback Callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciEvtRegister(hciEvtCback_t evtCback)
|
||||
{
|
||||
hciCb.evtCback = evtCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback for certain HCI security events.
|
||||
*
|
||||
* \param secCback Callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciSecRegister(hciSecCback_t secCback)
|
||||
{
|
||||
hciCb.secCback = secCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register callbacks for the HCI data path.
|
||||
*
|
||||
* \param aclCback ACL data callback function.
|
||||
* \param flowCback Flow control callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciAclRegister(hciAclCback_t aclCback, hciFlowCback_t flowCback)
|
||||
{
|
||||
hciCb.aclCback = aclCback;
|
||||
hciCb.flowCback = flowCback;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for HCI.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
/* store handler ID */
|
||||
hciCb.handlerId = handlerId;
|
||||
|
||||
/* init rx queue */
|
||||
WSF_QUEUE_INIT(&hciCb.rxQueue);
|
||||
|
||||
/* perform other hci initialization */
|
||||
HciCoreInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for HCI.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
HciCoreHandler(event, pMsg);
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief HCI main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef HCI_MAIN_H
|
||||
#define HCI_MAIN_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "hci_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Message types for HCI event handler */
|
||||
#define HCI_MSG_CMD_TIMEOUT 1 /* Command timeout timer expired */
|
||||
|
||||
/* Event types for HCI event handler */
|
||||
#define HCI_EVT_RX 0x01 /* Data received on rx queue */
|
||||
|
||||
/* Number of times to send HCI_RAND_CMD on reset to prefill random buffer */
|
||||
#define HCI_RESET_RAND_CNT 4
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Type used in number of completed packets event type */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t handle;
|
||||
uint16_t num;
|
||||
} hciNumPkts_t;
|
||||
|
||||
/* Type for HCI number of completed packets event */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t numHandles;
|
||||
hciNumPkts_t numPkts[1];
|
||||
} hciNumCmplPktsEvt_t;
|
||||
|
||||
/* Main control block of the HCI subsystem */
|
||||
typedef struct
|
||||
{
|
||||
wsfQueue_t rxQueue;
|
||||
hciEvtCback_t evtCback;
|
||||
hciSecCback_t secCback;
|
||||
hciAclCback_t aclCback;
|
||||
hciFlowCback_t flowCback;
|
||||
wsfHandlerId_t handlerId;
|
||||
bool_t resetting;
|
||||
} hciCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
extern hciCb_t hciCb;
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* HCI_MAIN_H */
|
||||
+1876
File diff suppressed because it is too large
Load Diff
+349
@@ -0,0 +1,349 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief L2CAP main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "l2c_api.h"
|
||||
#include "l2c_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
l2cCb_t l2cCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Default callback function for unregistered CID.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cDefaultDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
L2C_TRACE_WARN0("rcvd data on uregistered cid");
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Default callback function for unregistered CID.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param cid The L2CAP connection ID.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cDefaultDataCidCback(uint16_t handle, uint16_t cid, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
L2C_TRACE_WARN1("unknown cid=0x%04x", cid);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Default L2CAP control callback function.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cDefaultCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received L2CAP signaling packets.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void l2cRxSignalingPkt(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t role;
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) == DM_CONN_ID_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
role = DmConnRole(connId);
|
||||
|
||||
if ((role == DM_ROLE_MASTER) && (l2cCb.masterRxSignalingPkt != NULL))
|
||||
{
|
||||
(*l2cCb.masterRxSignalingPkt)(handle, len, pPacket);
|
||||
}
|
||||
else if ((role == DM_ROLE_SLAVE) && (l2cCb.slaveRxSignalingPkt != NULL))
|
||||
{
|
||||
(*l2cCb.slaveRxSignalingPkt)(handle, len, pPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
L2C_TRACE_ERR1("Invalid role configuration: role=%d", role);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI ACL data callback function.
|
||||
*
|
||||
* \param pPacket A buffer containing an ACL packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cHciAclCback(uint8_t *pPacket)
|
||||
{
|
||||
uint16_t handle;
|
||||
uint16_t hciLen;
|
||||
uint16_t cid;
|
||||
uint16_t l2cLen;
|
||||
uint8_t *p = pPacket;
|
||||
|
||||
/* parse HCI handle and length */
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
handle &= HCI_HANDLE_MASK;
|
||||
BSTREAM_TO_UINT16(hciLen, p);
|
||||
|
||||
/* parse L2CAP length */
|
||||
if (hciLen >= L2C_HDR_LEN)
|
||||
{
|
||||
BSTREAM_TO_UINT16(l2cLen, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
l2cLen = 0;
|
||||
}
|
||||
|
||||
/* verify L2CAP length vs HCI length */
|
||||
if (hciLen == (l2cLen + L2C_HDR_LEN))
|
||||
{
|
||||
/* parse CID */
|
||||
BSTREAM_TO_UINT16(cid, p);
|
||||
|
||||
switch (cid)
|
||||
{
|
||||
case L2C_CID_LE_SIGNALING:
|
||||
(*l2cCb.l2cSignalingCback)(handle, l2cLen, pPacket);
|
||||
break;
|
||||
|
||||
case L2C_CID_ATT:
|
||||
(*l2cCb.attDataCback)(handle, l2cLen, pPacket);
|
||||
break;
|
||||
|
||||
case L2C_CID_SMP:
|
||||
(*l2cCb.smpDataCback)(handle, l2cLen, pPacket);
|
||||
break;
|
||||
|
||||
default:
|
||||
(*l2cCb.l2cDataCidCback)(handle, cid, l2cLen, pPacket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* else length mismatch */
|
||||
else
|
||||
{
|
||||
L2C_TRACE_WARN2("length mismatch: l2c=%u hci=%u", l2cLen, hciLen);
|
||||
}
|
||||
|
||||
/* deallocate buffer */
|
||||
WsfMsgFree(pPacket);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI flow control callback function.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param flowDisabled TRUE if data flow is disabled.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cHciFlowCback(uint16_t handle, bool_t flowDisabled)
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
/* get conn ID for handle */
|
||||
if ((hdr.param = DmConnIdByHandle(handle)) != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* execute higher layer flow control callbacks */
|
||||
hdr.event = flowDisabled;
|
||||
(*l2cCb.attCtrlCback)(&hdr);
|
||||
hdr.event = flowDisabled;
|
||||
(*l2cCb.smpCtrlCback)(&hdr);
|
||||
|
||||
/* execute connection oriented channel flow control callback */
|
||||
hdr.event = flowDisabled;
|
||||
(*l2cCb.l2cCocCtrlCback)(&hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a command reject message with reason "not understood".
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param identifier Identifier value in received message being rejected.
|
||||
* \param reason Why request was rejected.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void l2cSendCmdReject(uint16_t handle, uint8_t identifier, uint16_t reason)
|
||||
{
|
||||
uint8_t *pPacket;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate msg buffer */
|
||||
if ((pPacket = l2cMsgAlloc(L2C_SIG_PKT_BASE_LEN + L2C_SIG_CMD_REJ_LEN)) != NULL)
|
||||
{
|
||||
/* build message */
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, L2C_SIG_CMD_REJ); /* command code */
|
||||
UINT8_TO_BSTREAM(p, identifier); /* identifier */
|
||||
UINT16_TO_BSTREAM(p, L2C_SIG_CMD_REJ_LEN); /* parameter length */
|
||||
UINT16_TO_BSTREAM(p, reason); /* reason */
|
||||
|
||||
/* send packet */
|
||||
L2cDataReq(L2C_CID_LE_SIGNALING, handle, (L2C_SIG_HDR_LEN + L2C_SIG_CMD_REJ_LEN), pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate an L2CAP data message buffer to be used for the L2CAP protocol messages.
|
||||
*
|
||||
* \param len Message length in bytes.
|
||||
*
|
||||
* \return Pointer to data message buffer or NULL if allocation failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *l2cMsgAlloc(uint16_t len)
|
||||
{
|
||||
return WsfMsgDataAlloc(len, HCI_TX_DATA_TAILROOM);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize L2C subsystem.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cInit(void)
|
||||
{
|
||||
/* Initialize control block */
|
||||
l2cCb.attDataCback = l2cDefaultDataCback;
|
||||
l2cCb.smpDataCback = l2cDefaultDataCback;
|
||||
l2cCb.l2cSignalingCback = l2cRxSignalingPkt;
|
||||
l2cCb.attCtrlCback = l2cDefaultCtrlCback;
|
||||
l2cCb.smpCtrlCback = l2cDefaultCtrlCback;
|
||||
l2cCb.l2cCocCtrlCback = l2cDefaultCtrlCback;
|
||||
l2cCb.l2cDataCidCback = l2cDefaultDataCidCback;
|
||||
l2cCb.identifier = 1;
|
||||
|
||||
/* Register with HCI */
|
||||
HciAclRegister(l2cHciAclCback, l2cHciFlowCback);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief called by the L2C client, such as ATT or SMP, to register for the given CID.
|
||||
*
|
||||
* \param cid channel identifier.
|
||||
* \param dataCback Callback function for L2CAP data received for this CID.
|
||||
* \param ctrlCback Callback function for control events for this CID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cRegister(uint16_t cid, l2cDataCback_t dataCback, l2cCtrlCback_t ctrlCback)
|
||||
{
|
||||
WSF_ASSERT((cid == L2C_CID_ATT) || (cid == L2C_CID_SMP));
|
||||
|
||||
/* store the callbacks */
|
||||
if (cid == L2C_CID_ATT)
|
||||
{
|
||||
/* registering for attribute protocol */
|
||||
l2cCb.attDataCback = dataCback;
|
||||
l2cCb.attCtrlCback = ctrlCback;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* registering for security manager protocol */
|
||||
l2cCb.smpDataCback = dataCback;
|
||||
l2cCb.smpCtrlCback = ctrlCback;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an L2CAP data packet on the given CID.
|
||||
*
|
||||
* \param cid The channel identifier.
|
||||
* \param handle The connection handle. The client receives this handle from DM.
|
||||
* \param len The length of the payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cDataReq(uint16_t cid, uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *p = pPacket;
|
||||
|
||||
/* Set HCI header */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT16_TO_BSTREAM(p, (len + L2C_HDR_LEN));
|
||||
|
||||
/* Set L2CAP header */
|
||||
UINT16_TO_BSTREAM(p, len);
|
||||
UINT16_TO_BSTREAM(p, cid);
|
||||
|
||||
/* Send to HCI */
|
||||
HciSendAclData(pPacket);
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief L2CAP main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef L2C_MAIN_H
|
||||
#define L2C_MAIN_H
|
||||
|
||||
#include "l2c_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Return the next L2CAP signaling req identifier. Cannot be zero. */
|
||||
#define L2C_NEXT_ID(id) (((id) == 255) ? (1) : ((id) + 1))
|
||||
|
||||
/* L2C event handler message types */
|
||||
#define L2C_MSG_REQ_TIMEOUT 1 /* L2CAP slave signaling request timeout */
|
||||
#define L2C_MSG_TYPE_MAX 1
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data callback with CID */
|
||||
typedef void (*l2cDataCidCback_t)(uint16_t handle, uint16_t cid, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
/* Main control block of the L2C subsystem */
|
||||
typedef struct
|
||||
{
|
||||
l2cDataCback_t attDataCback; /* Data callback for ATT */
|
||||
l2cDataCback_t smpDataCback; /* Data callback for SMP */
|
||||
l2cDataCback_t l2cSignalingCback; /* Data callback for L2CAP signaling */
|
||||
l2cCtrlCback_t attCtrlCback; /* Control callback for ATT */
|
||||
l2cCtrlCback_t smpCtrlCback; /* Control callback for SMP */
|
||||
l2cCtrlCback_t l2cCocCtrlCback; /* Control callback for L2CAP connection oriented channels */
|
||||
l2cDataCback_t masterRxSignalingPkt; /* Master signaling packet processing function */
|
||||
l2cDataCback_t slaveRxSignalingPkt; /* Slave signaling packet processing function */
|
||||
l2cDataCidCback_t l2cDataCidCback; /* Data callback for L2CAP on other CIDs */
|
||||
uint8_t identifier; /* Signaling request identifier */
|
||||
} l2cCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
void l2cSendCmdReject(uint16_t handle, uint8_t identifier, uint16_t reason);
|
||||
void l2cRxSignalingPkt(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
void *l2cMsgAlloc(uint16_t len);
|
||||
|
||||
/* Control block */
|
||||
extern l2cCb_t l2cCb;
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* L2C_MAIN_H */
|
||||
Vendored
+147
@@ -0,0 +1,147 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief L2CAP module for master operations.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "l2c_api.h"
|
||||
#include "l2c_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Master processing of received L2CAP signaling packets.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param l2cLen The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cMasterRxSignalingPkt(uint16_t handle, uint16_t l2cLen, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t code;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
hciConnSpec_t connSpec;
|
||||
|
||||
/* parse code, len, and identifier */
|
||||
pPacket += L2C_PAYLOAD_START;
|
||||
BSTREAM_TO_UINT8(code, pPacket);
|
||||
BSTREAM_TO_UINT8(id, pPacket);
|
||||
BSTREAM_TO_UINT16(len, pPacket);
|
||||
|
||||
/* verify signaling length vs. l2c length
|
||||
* verify this is a conn param update rsp
|
||||
* verify parameter length
|
||||
*/
|
||||
if ((l2cLen != (len + L2C_SIG_HDR_LEN)) ||
|
||||
(code != L2C_SIG_CONN_UPDATE_REQ) ||
|
||||
(len != L2C_SIG_CONN_UPDATE_REQ_LEN))
|
||||
{
|
||||
L2C_TRACE_WARN3("invalid msg code:%d len:%d l2cLen:%d", code, len, l2cLen);
|
||||
|
||||
/* reject all unknown or invalid commands except command reject. */
|
||||
if (code != L2C_SIG_CMD_REJ)
|
||||
{
|
||||
l2cSendCmdReject(handle, id, L2C_REJ_NOT_UNDERSTOOD);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse parameters */
|
||||
BSTREAM_TO_UINT16(connSpec.connIntervalMin, pPacket);
|
||||
BSTREAM_TO_UINT16(connSpec.connIntervalMax, pPacket);
|
||||
BSTREAM_TO_UINT16(connSpec.connLatency, pPacket);
|
||||
BSTREAM_TO_UINT16(connSpec.supTimeout, pPacket);
|
||||
connSpec.minCeLen = 0;
|
||||
connSpec.maxCeLen = 0;
|
||||
|
||||
/* check parameter range */
|
||||
if ((connSpec.connIntervalMin < HCI_CONN_INTERVAL_MIN) ||
|
||||
(connSpec.connIntervalMin > HCI_CONN_INTERVAL_MAX) ||
|
||||
(connSpec.connIntervalMin > connSpec.connIntervalMax) ||
|
||||
(connSpec.connIntervalMax < HCI_CONN_INTERVAL_MIN) ||
|
||||
(connSpec.connIntervalMax > HCI_CONN_INTERVAL_MAX) ||
|
||||
(connSpec.connLatency > HCI_CONN_LATENCY_MAX) ||
|
||||
(connSpec.supTimeout < HCI_SUP_TIMEOUT_MIN) ||
|
||||
(connSpec.supTimeout > HCI_SUP_TIMEOUT_MAX))
|
||||
{
|
||||
L2cDmConnUpdateRsp(id, handle, L2C_CONN_PARAM_REJECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
DmL2cConnUpdateInd(id, handle, &connSpec);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize L2C for operation as a Bluetooth LE master.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cMasterInit(void)
|
||||
{
|
||||
l2cCb.masterRxSignalingPkt = l2cMasterRxSignalingPkt;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by DM to send an L2CAP connection update response.
|
||||
*
|
||||
* \param identifier Identifier value previously passed from L2C to DM.
|
||||
* \param handle The connection handle.
|
||||
* \param result Connection update response result.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cDmConnUpdateRsp(uint8_t identifier, uint16_t handle, uint16_t result)
|
||||
{
|
||||
uint8_t *pPacket;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate msg buffer */
|
||||
if ((pPacket = l2cMsgAlloc(L2C_SIG_PKT_BASE_LEN + L2C_SIG_CONN_UPDATE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build message */
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, L2C_SIG_CONN_UPDATE_RSP); /* command code */
|
||||
UINT8_TO_BSTREAM(p, identifier); /* identifier */
|
||||
UINT16_TO_BSTREAM(p, L2C_SIG_CONN_UPDATE_RSP_LEN); /* parameter length */
|
||||
UINT16_TO_BSTREAM(p, result); /* result */
|
||||
|
||||
/* send packet */
|
||||
L2cDataReq(L2C_CID_LE_SIGNALING, handle, (L2C_SIG_HDR_LEN + L2C_SIG_CONN_UPDATE_RSP_LEN), pPacket);
|
||||
}
|
||||
}
|
||||
Vendored
+311
@@ -0,0 +1,311 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief L2CAP module for slave operations.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_os.h"
|
||||
#include "util/bstream.h"
|
||||
#include "l2c_api.h"
|
||||
#include "l2c_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Signaling request timeout in seconds */
|
||||
#define L2C_SIG_REQ_TIMEOUT 30
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Slave control block */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t reqTimer; /* Signaling request timeout timer */
|
||||
wsfHandlerId_t handlerId; /* ID for this event handler */
|
||||
uint8_t lastCode[DM_CONN_MAX]; /* last code sent on each handle */
|
||||
uint8_t signId[DM_CONN_MAX]; /* expected signaling identifier */
|
||||
} l2cSlaveCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
static l2cSlaveCb_t l2cSlaveCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle slave signaling request timeout.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cSlaveReqTimeout(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
L2C_TRACE_WARN0("conn update req timeout");
|
||||
|
||||
/* Notify DM that connection update has failed (handle is stored in param) */
|
||||
DmL2cConnUpdateCnf(pMsg->param, L2C_CONN_PARAM_REJECTED);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Slave processing of received L2CAP signaling packets.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param l2cLen The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cSlaveRxSignalingPkt(uint16_t handle, uint16_t l2cLen, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t code;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
uint16_t result;
|
||||
|
||||
/* parse code, len, and identifier */
|
||||
pPacket += L2C_PAYLOAD_START;
|
||||
BSTREAM_TO_UINT8(code, pPacket);
|
||||
BSTREAM_TO_UINT8(id, pPacket);
|
||||
BSTREAM_TO_UINT16(len, pPacket);
|
||||
|
||||
/* verify signal identifier is valid */
|
||||
if (id == L2C_SIGNAL_ID_INVALID)
|
||||
{
|
||||
/* not expected, ignore */
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify signal identifier is expected
|
||||
* verify signaling length vs. l2c length
|
||||
* verify this is a conn param update rsp or command reject
|
||||
* verify parameter length
|
||||
*/
|
||||
if ((id == l2cSlaveCb.signId[handle]) &&
|
||||
(l2cLen == (len + L2C_SIG_HDR_LEN)) &&
|
||||
(((code == L2C_SIG_CONN_UPDATE_RSP) && (len == L2C_SIG_CONN_UPDATE_RSP_LEN)) ||
|
||||
(code == L2C_SIG_CMD_REJ)))
|
||||
{
|
||||
/* get last sent code */
|
||||
uint8_t lastCode = l2cSlaveCb.lastCode[handle];
|
||||
|
||||
/* clear pending signal id */
|
||||
l2cSlaveCb.signId[handle] = L2C_SIGNAL_ID_INVALID;
|
||||
|
||||
/* parse result parameter */
|
||||
BSTREAM_TO_UINT16(result, pPacket);
|
||||
|
||||
/* stop req timer */
|
||||
WsfTimerStop(&l2cSlaveCb.reqTimer);
|
||||
|
||||
if (lastCode == L2C_SIG_CONN_UPDATE_REQ)
|
||||
{
|
||||
if (code == L2C_SIG_CMD_REJ)
|
||||
{
|
||||
/* got command reject */
|
||||
result = L2C_CONN_PARAM_REJECTED;
|
||||
}
|
||||
|
||||
/* send to DM */
|
||||
DmL2cConnUpdateCnf(handle, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* send to DM */
|
||||
DmL2cCmdRejInd(handle, result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
L2C_TRACE_WARN3("invalid msg code:%d len:%d l2cLen:%d", code, len, l2cLen);
|
||||
|
||||
/* reject all unknown, invalid or unidentified commands except command reject. */
|
||||
if (code != L2C_SIG_CMD_REJ)
|
||||
{
|
||||
l2cSendCmdReject(handle, id, L2C_REJ_NOT_UNDERSTOOD);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize L2C for operation as a Bluetooth LE slave.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cSlaveInit(void)
|
||||
{
|
||||
l2cCb.slaveRxSignalingPkt = l2cSlaveRxSignalingPkt;
|
||||
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
l2cSlaveCb.signId[i] = L2C_SIGNAL_ID_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Build and send a signaling packet.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param code Type of command.
|
||||
* \param len Length of \ref pParam.
|
||||
* \param pParam parameters of command to send.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cDmSigReq(uint16_t handle, uint8_t code, uint16_t len, uint8_t *pParam)
|
||||
{
|
||||
uint8_t *pPacket;
|
||||
uint8_t *p;
|
||||
|
||||
WSF_ASSERT(handle < DM_CONN_MAX);
|
||||
|
||||
/* record code */
|
||||
l2cSlaveCb.lastCode[handle] = code;
|
||||
|
||||
/* Start signaling request timer and store handle */
|
||||
WsfTimerStartSec(&l2cSlaveCb.reqTimer, L2C_SIG_REQ_TIMEOUT);
|
||||
l2cSlaveCb.reqTimer.msg.param = handle;
|
||||
|
||||
/* allocate msg buffer */
|
||||
if ((pPacket = l2cMsgAlloc(L2C_SIG_PKT_BASE_LEN + len)) != NULL)
|
||||
{
|
||||
/* build message */
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, code); /* command code */
|
||||
l2cSlaveCb.signId[handle] = l2cCb.identifier;
|
||||
UINT8_TO_BSTREAM(p, l2cCb.identifier); /* identifier */
|
||||
l2cCb.identifier = L2C_NEXT_ID(l2cCb.identifier);
|
||||
UINT16_TO_BSTREAM(p, len); /* parameter length */
|
||||
memcpy(p, pParam, len); /* parameters */
|
||||
|
||||
/* send packet */
|
||||
L2cDataReq(L2C_CID_LE_SIGNALING, handle, (L2C_SIG_HDR_LEN + len), pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by DM to send an L2CAP connection update request.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param pConnSpec Pointer to the connection specification structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cDmConnUpdateReq(uint16_t handle, hciConnSpec_t *pConnSpec)
|
||||
{
|
||||
uint8_t *pPacket;
|
||||
uint8_t *p;
|
||||
|
||||
/* record code */
|
||||
l2cSlaveCb.lastCode[handle] = L2C_SIG_CONN_UPDATE_REQ;
|
||||
|
||||
/* Start signaling request timer and store handle */
|
||||
WsfTimerStartSec(&l2cSlaveCb.reqTimer, L2C_SIG_REQ_TIMEOUT);
|
||||
l2cSlaveCb.reqTimer.msg.param = handle;
|
||||
|
||||
/* allocate msg buffer */
|
||||
if ((pPacket = l2cMsgAlloc(L2C_SIG_PKT_BASE_LEN + L2C_SIG_CONN_UPDATE_REQ_LEN)) != NULL)
|
||||
{
|
||||
/* build message */
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, L2C_SIG_CONN_UPDATE_REQ); /* command code */
|
||||
UINT8_TO_BSTREAM(p, l2cCb.identifier); /* identifier */
|
||||
l2cSlaveCb.signId[handle] = l2cCb.identifier;
|
||||
l2cCb.identifier = L2C_NEXT_ID(l2cCb.identifier);
|
||||
UINT16_TO_BSTREAM(p, L2C_SIG_CONN_UPDATE_REQ_LEN); /* parameter length */
|
||||
UINT16_TO_BSTREAM(p, pConnSpec->connIntervalMin); /* interval min */
|
||||
UINT16_TO_BSTREAM(p, pConnSpec->connIntervalMax); /* interval max */
|
||||
UINT16_TO_BSTREAM(p, pConnSpec->connLatency); /* slave latency */
|
||||
UINT16_TO_BSTREAM(p, pConnSpec->supTimeout); /* timeout multiplier */
|
||||
|
||||
/* send packet */
|
||||
L2cDataReq(L2C_CID_LE_SIGNALING, handle, (L2C_SIG_HDR_LEN + L2C_SIG_CONN_UPDATE_REQ_LEN), pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Event handler initialization function for L2C when operating as a slave.
|
||||
*
|
||||
* \param handlerId ID for this event handler.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cSlaveHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
l2cSlaveCb.reqTimer.msg.event = L2C_MSG_REQ_TIMEOUT;
|
||||
l2cSlaveCb.reqTimer.handlerId = handlerId;
|
||||
l2cSlaveCb.handlerId = handlerId;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief The WSF event handler for L2C when operating as a slave.
|
||||
*
|
||||
* \param event Event mask.
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cSlaveHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* Handle message */
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
WSF_ASSERT(pMsg->event > 0 && pMsg->event <= L2C_MSG_TYPE_MAX);
|
||||
|
||||
/* handle slave signaling request timeout */
|
||||
if (pMsg->event == L2C_MSG_REQ_TIMEOUT)
|
||||
{
|
||||
l2cSlaveReqTimeout(pMsg);
|
||||
}
|
||||
}
|
||||
/* Handle events */
|
||||
else if (event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+874
@@ -0,0 +1,874 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP common utility functions and action functions.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "dm_api.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
|
||||
#ifndef SMP_EXTRA_TRACE
|
||||
#define SMP_EXTRA_TRACE FALSE
|
||||
#endif
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start SMP response timer.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpStartRspTimer(smpCcb_t *pCcb)
|
||||
{
|
||||
/* start smp response timer */
|
||||
pCcb->rspTimer.msg.event = SMP_MSG_INT_RSP_TIMEOUT;
|
||||
pCcb->rspTimer.msg.status = SMP_ERR_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->rspTimer, SMP_TIMEOUT);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief No action.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActNone(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cleanup CCB.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpCleanup(smpCcb_t *pCcb)
|
||||
{
|
||||
/* free scratch buffer */
|
||||
if (pCcb->pScr != NULL)
|
||||
{
|
||||
WsfBufFree(pCcb->pScr);
|
||||
pCcb->pScr = NULL;
|
||||
}
|
||||
|
||||
/* stop response timer */
|
||||
WsfTimerStop(&pCcb->rspTimer);
|
||||
|
||||
/* stop wait interval timer */
|
||||
WsfTimerStop(&pCcb->waitTimer);
|
||||
|
||||
pCcb->secReq = FALSE;
|
||||
pCcb->nextCmdCode = (pCcb->initiator) ? SMP_CMD_SECURITY_REQ : SMP_CMD_PAIR_REQ;
|
||||
pCcb->lastSentKey = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cleanup CCB.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActCleanup(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smpCleanup(pCcb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a pairing failed packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param reason Failure reason.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpSendPairingFailed(smpCcb_t *pCcb, uint8_t reason)
|
||||
{
|
||||
uint8_t *pPacket;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pPacket = smpMsgAlloc(L2C_PAYLOAD_START + SMP_PAIR_FAIL_LEN)) != NULL)
|
||||
{
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_FAIL);
|
||||
UINT8_TO_BSTREAM(p, reason);
|
||||
|
||||
smpSendPkt(pCcb, pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pairing failed.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairingFailed(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* clean up */
|
||||
smpCleanup(pCcb);
|
||||
|
||||
/* set connection idle */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_SMP_PAIR, DM_CONN_IDLE);
|
||||
|
||||
/* notify DM of pairing failure */
|
||||
pMsg->hdr.event = DM_SEC_PAIR_FAIL_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pairing cancelled by user.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairingCancel(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* send pairing failed packet */
|
||||
smpSendPairingFailed(pCcb, pMsg->hdr.status);
|
||||
|
||||
smpActPairingFailed(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store the authentication data.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActStorePin(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* copy authentication data to scratchpad */
|
||||
memcpy(pCcb->pScr->buf.b1, pMsg->dm.authRsp.authData, pMsg->dm.authRsp.authDataLen);
|
||||
|
||||
/* zero out unused pin data */
|
||||
if (pMsg->dm.authRsp.authDataLen == SMP_PIN_LEN)
|
||||
{
|
||||
memset(&pCcb->pScr->buf.b1[SMP_PIN_LEN], 0, SMP_OOB_LEN - SMP_PIN_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a pairing request and response data.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pOob Return parameter, TRUE if out-of-band data requested.
|
||||
* \param pDisplay Return parameter, TRUE if pin is to be displayed.
|
||||
*
|
||||
* \return TRUE on success, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t smpProcPairing(smpCcb_t *pCcb, uint8_t *pOob, uint8_t *pDisplay)
|
||||
{
|
||||
bool_t justWorks = TRUE;
|
||||
uint8_t localAuth;
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
*pDisplay = FALSE;
|
||||
*pOob = FALSE;
|
||||
|
||||
/* if OOB available use that */
|
||||
if (pCcb->pairReq[SMP_OOB_POS] == SMP_OOB_DATA_PRESENT &&
|
||||
pCcb->pairRsp[SMP_OOB_POS] == SMP_OOB_DATA_PRESENT)
|
||||
{
|
||||
*pOob = SMP_OOB_DATA_PRESENT;
|
||||
justWorks = FALSE;
|
||||
}
|
||||
/* if either device set mitm flag */
|
||||
else if ((pCcb->pairReq[SMP_AUTHREQ_POS] & SMP_AUTH_MITM_FLAG) ||
|
||||
(pCcb->pairRsp[SMP_AUTHREQ_POS] & SMP_AUTH_MITM_FLAG))
|
||||
{
|
||||
/* check for compatible I/O settings */
|
||||
if ((pCcb->pairReq[SMP_IO_POS] != SMP_IO_NO_IN_NO_OUT) && /* initiator has i/o and */
|
||||
(pCcb->pairRsp[SMP_IO_POS] != SMP_IO_NO_IN_NO_OUT) && /* responder has i/o and */
|
||||
!(((pCcb->pairReq[SMP_IO_POS] == SMP_IO_DISP_ONLY) || /* both don't have display only */
|
||||
(pCcb->pairReq[SMP_IO_POS] == SMP_IO_DISP_YES_NO)) &&
|
||||
((pCcb->pairRsp[SMP_IO_POS] == SMP_IO_DISP_ONLY) ||
|
||||
(pCcb->pairRsp[SMP_IO_POS] == SMP_IO_DISP_YES_NO))))
|
||||
{
|
||||
/* use pin */
|
||||
justWorks = FALSE;
|
||||
|
||||
/* check if pin should be displayed (as initiator) */
|
||||
*pDisplay =
|
||||
((pCcb->pairReq[SMP_IO_POS] == SMP_IO_DISP_ONLY) || /* initiator is display only or */
|
||||
(pCcb->pairReq[SMP_IO_POS] == SMP_IO_DISP_YES_NO) || /* initiator is display y/n or */
|
||||
((pCcb->pairReq[SMP_IO_POS] == SMP_IO_KEY_DISP) && /* initiator is key/display and */
|
||||
(pCcb->pairRsp[SMP_IO_POS] == SMP_IO_KEY_ONLY || /* responder is key only or key/display */
|
||||
pCcb->pairRsp[SMP_IO_POS] == SMP_IO_KEY_DISP)));
|
||||
|
||||
/* invert display setting if we are not initiator and both are not key only */
|
||||
if (!(pCcb->pairRsp[SMP_IO_POS] == SMP_IO_KEY_ONLY &&
|
||||
pCcb->pairReq[SMP_IO_POS] == SMP_IO_KEY_ONLY))
|
||||
{
|
||||
*pDisplay ^= !pCcb->initiator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!justWorks)
|
||||
{
|
||||
/* set auth flags with mitm bit set */
|
||||
pCcb->auth = (pCcb->pairReq[SMP_AUTHREQ_POS] & pCcb->pairRsp[SMP_AUTHREQ_POS]) | SMP_AUTH_MITM_FLAG;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set auth flags with mitm bit cleared */
|
||||
pCcb->auth = pCcb->pairReq[SMP_AUTHREQ_POS] & pCcb->pairRsp[SMP_AUTHREQ_POS] & ~SMP_AUTH_MITM_FLAG;
|
||||
}
|
||||
|
||||
/* if we ended up with 'just works' but the device configuration requires authentication */
|
||||
localAuth = (pCcb->initiator) ? pCcb->pairReq[SMP_AUTHREQ_POS] : pCcb->pairRsp[SMP_AUTHREQ_POS];
|
||||
if (justWorks && (pSmpCfg->auth & localAuth & SMP_AUTH_MITM_FLAG))
|
||||
{
|
||||
/* cancel pairing */
|
||||
hdr.param = pCcb->connId;
|
||||
hdr.status = SMP_ERR_AUTH_REQ;
|
||||
hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* if max encryption key is less than our minimum */
|
||||
if (pCcb->pairReq[SMP_MAXKEY_POS] < pSmpCfg->minKeyLen ||
|
||||
pCcb->pairRsp[SMP_MAXKEY_POS] < pSmpCfg->minKeyLen)
|
||||
{
|
||||
/* cancel pairing */
|
||||
hdr.param = pCcb->connId;
|
||||
hdr.status = SMP_ERR_ENC_KEY_SIZE;
|
||||
hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Request authentication data or send ourselves an authentication response.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param oob Out-of-band data requested.
|
||||
* \param display TRUE if pin is to be displayed.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpAuthReq(smpCcb_t *pCcb, uint8_t oob, uint8_t display)
|
||||
{
|
||||
/* use a union to save a bit of memory on the stack */
|
||||
union
|
||||
{
|
||||
smpDmAuthRsp_t authRsp;
|
||||
dmSecAuthReqIndEvt_t authReq;
|
||||
} buf;
|
||||
|
||||
/* if authenticated pairing */
|
||||
if (pCcb->auth & SMP_AUTH_MITM_FLAG)
|
||||
{
|
||||
/* request pin or oob from user */
|
||||
buf.authReq.hdr.param = pCcb->connId;
|
||||
buf.authReq.hdr.event = DM_SEC_AUTH_REQ_IND;
|
||||
buf.authReq.oob = oob;
|
||||
buf.authReq.display = display;
|
||||
DmSmpCbackExec((dmEvt_t *) &buf.authReq);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* else use just works; send ourselves a auth rsp with all zero pin */
|
||||
buf.authRsp.hdr.param = pCcb->connId;
|
||||
buf.authRsp.hdr.event = SMP_MSG_API_AUTH_RSP;
|
||||
buf.authRsp.authData[0] = 0;
|
||||
buf.authRsp.authData[1] = 0;
|
||||
buf.authRsp.authData[2] = 0;
|
||||
buf.authRsp.authDataLen = SMP_PIN_LEN;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &buf.authRsp);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform first part of pairing confirm calculation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairCnfCalc1(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* store authentication data */
|
||||
smpActStorePin(pCcb, pMsg);
|
||||
|
||||
/* get random number to scratchpad */
|
||||
SecRand(pCcb->pScr->buf.b4, SMP_RAND_LEN);
|
||||
|
||||
/* execute calculation */
|
||||
smpCalcC1Part1(pCcb, pCcb->pScr->buf.b1, pCcb->pScr->buf.b4);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform second part of pairing confirm calculation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairCnfCalc2(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smpCalcC1Part2(pCcb, pCcb->pScr->buf.b1, pMsg->aes.pCiphertext);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a pairing confirm packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActSendPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* set next expected packet */
|
||||
pCcb->nextCmdCode = (pCcb->initiator) ? SMP_CMD_PAIR_CNF : SMP_CMD_PAIR_RAND;
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* allocate packet buffer */
|
||||
if ((pPkt = smpMsgAlloc(SMP_PAIR_CNF_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet */
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_CNF);
|
||||
memcpy(p, pMsg->aes.pCiphertext, SMP_CONFIRM_LEN);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform first part of the pairing confirm verification calculation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairCnfVerCalc1(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *p;
|
||||
|
||||
/* go to start of received pairing random packet */
|
||||
p = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* store random value */
|
||||
memcpy(pCcb->pScr->buf.b2, p, SMP_RAND_LEN);
|
||||
|
||||
/* execute calculation */
|
||||
smpCalcC1Part1(pCcb, pCcb->pScr->buf.b1, p);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform first part of the pairing confirm verification calculation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairCnfVerCalc2(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smpCalcC1Part2(pCcb, pCcb->pScr->buf.b1, pMsg->aes.pCiphertext);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a key.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param keyDist Key distribution mask.
|
||||
*
|
||||
* \return TRUE if done sending keys, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t smpSendKey(smpCcb_t *pCcb, uint8_t keyDist)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
wsfMsgHdr_t *pHdr;
|
||||
|
||||
if (smpCb.lescSupported && pCcb->pScCcb->lescEnabled && pCcb->lastSentKey == 0)
|
||||
{
|
||||
dmSecKeyIndEvt_t keyInd;
|
||||
|
||||
/* pass LTK to app via DM */
|
||||
if (DmConnRole(pCcb->connId) == DM_ROLE_MASTER)
|
||||
{
|
||||
keyInd.type = DM_KEY_PEER_LTK;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyInd.type = DM_KEY_LOCAL_LTK;
|
||||
}
|
||||
|
||||
keyInd.hdr.event = DM_SEC_KEY_IND;
|
||||
keyInd.hdr.param = pCcb->connId;
|
||||
keyInd.secLevel = smpGetScSecLevel(pCcb);
|
||||
keyInd.keyData.ltk.ediv = 0;
|
||||
memset(keyInd.keyData.ltk.rand, 0, SMP_RAND8_LEN);
|
||||
Calc128Cpy(keyInd.keyData.ltk.key, pCcb->pScCcb->pLtk->ltk_t);
|
||||
DmSmpCbackExec((dmEvt_t *)&keyInd);
|
||||
|
||||
pCcb->lastSentKey = SMP_CMD_MASTER_ID;
|
||||
}
|
||||
|
||||
/* check if we're done sending keys */
|
||||
if ((keyDist == 0) ||
|
||||
(keyDist == SMP_KEY_DIST_ENC && pCcb->lastSentKey == SMP_CMD_MASTER_ID) ||
|
||||
(keyDist <= (SMP_KEY_DIST_ENC | SMP_KEY_DIST_ID) && pCcb->lastSentKey == SMP_CMD_ID_ADDR_INFO) ||
|
||||
(pCcb->lastSentKey == SMP_CMD_SIGN_INFO))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* if flow disabled return */
|
||||
if (pCcb->flowDisabled)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* allocate packet buffer for largest packet size */
|
||||
if ((pPkt = smpMsgAlloc(SMP_ENC_INFO_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
|
||||
/* determine next key to send */
|
||||
if (pCcb->lastSentKey == 0 && (keyDist & SMP_KEY_DIST_ENC))
|
||||
{
|
||||
/* generate LTK, EDIV, and RAND */
|
||||
smpGenerateLtk(pCcb);
|
||||
|
||||
/* send first part of LTK */
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_ENC_INFO);
|
||||
Calc128Cpy(p, pCcb->pScr->keyInd.keyData.ltk.key);
|
||||
}
|
||||
else if (pCcb->lastSentKey == SMP_CMD_ENC_INFO)
|
||||
{
|
||||
/* send second part of LTK */
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_MASTER_ID);
|
||||
UINT16_TO_BSTREAM(p, pCcb->pScr->keyInd.keyData.ltk.ediv);
|
||||
memcpy(p, pCcb->pScr->keyInd.keyData.ltk.rand, SMP_RAND8_LEN);
|
||||
}
|
||||
else if ((keyDist & SMP_KEY_DIST_ID) &&
|
||||
(pCcb->lastSentKey == 0 || pCcb->lastSentKey == SMP_CMD_MASTER_ID))
|
||||
{
|
||||
/* send first part of IRK */
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_ID_INFO);
|
||||
Calc128Cpy(p, DmSecGetLocalIrk());
|
||||
}
|
||||
else if (pCcb->lastSentKey == SMP_CMD_ID_INFO)
|
||||
{
|
||||
/* send second part of IRK */
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_ID_ADDR_INFO);
|
||||
UINT8_TO_BSTREAM(p, DM_ADDR_PUBLIC);
|
||||
BDA_TO_BSTREAM(p, HciGetBdAddr());
|
||||
|
||||
}
|
||||
else if ((keyDist & SMP_KEY_DIST_SIGN) &&
|
||||
(pCcb->lastSentKey == 0 || pCcb->lastSentKey == SMP_CMD_ID_ADDR_INFO ||
|
||||
pCcb->lastSentKey == SMP_CMD_MASTER_ID))
|
||||
{
|
||||
/* send SRK */
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_SIGN_INFO);
|
||||
Calc128Cpy(p, DmSecGetLocalCsrk());
|
||||
}
|
||||
else
|
||||
{
|
||||
/* should never get here */
|
||||
WsfMsgFree(pPkt);
|
||||
SMP_TRACE_WARN2("smpSendKey unexpected state keyDist:%d lastSentKey:%d", keyDist, pCcb->lastSentKey);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* set last sent key to command code */
|
||||
pCcb->lastSentKey = pPkt[L2C_PAYLOAD_START];
|
||||
|
||||
/* send command packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
|
||||
/* if flow not disabled set up to send next key */
|
||||
if (!pCcb->flowDisabled)
|
||||
{
|
||||
if ((pHdr = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pHdr->event = SMP_MSG_INT_SEND_NEXT_KEY;
|
||||
pHdr->param = pCcb->connId;
|
||||
WsfMsgSend(smpCb.handlerId, pHdr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received key packet and pass it to DM if complete.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pKeyInd Key data structure allocated by caller.
|
||||
* \param pBuf Buffer containing packet.
|
||||
* \param keyDist Key distribution mask.
|
||||
*
|
||||
* \return TRUE if done receiving keys, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t smpProcRcvKey(smpCcb_t *pCcb, dmSecKeyIndEvt_t *pKeyInd, uint8_t *pBuf, uint8_t keyDist)
|
||||
{
|
||||
bool_t keyIndReady = FALSE;
|
||||
bool_t done = FALSE;
|
||||
uint8_t cmdCode;
|
||||
|
||||
/* go to start of packet */
|
||||
pBuf += L2C_PAYLOAD_START;
|
||||
cmdCode = *pBuf++;
|
||||
|
||||
if (cmdCode == SMP_CMD_ENC_INFO)
|
||||
{
|
||||
/* parse encryption information packet */
|
||||
Calc128Cpy(pKeyInd->keyData.ltk.key, pBuf);
|
||||
}
|
||||
else if (cmdCode == SMP_CMD_MASTER_ID)
|
||||
{
|
||||
/* parse master identification packet */
|
||||
BSTREAM_TO_UINT16(pKeyInd->keyData.ltk.ediv, pBuf);
|
||||
memcpy(pKeyInd->keyData.ltk.rand, pBuf, SMP_RAND8_LEN);
|
||||
pKeyInd->secLevel = (pCcb->auth & SMP_AUTH_MITM_FLAG) ? DM_SEC_LEVEL_ENC_AUTH : DM_SEC_LEVEL_ENC;
|
||||
pKeyInd->type = DM_KEY_PEER_LTK;
|
||||
keyIndReady = TRUE;
|
||||
}
|
||||
else if (cmdCode == SMP_CMD_ID_INFO)
|
||||
{
|
||||
/* parse identity information packet */
|
||||
Calc128Cpy(pKeyInd->keyData.irk.key, pBuf);
|
||||
}
|
||||
else if (cmdCode == SMP_CMD_ID_ADDR_INFO)
|
||||
{
|
||||
/* parse identity address information packet */
|
||||
BSTREAM_TO_UINT8(pKeyInd->keyData.irk.addrType, pBuf);
|
||||
BSTREAM_TO_BDA(pKeyInd->keyData.irk.bdAddr, pBuf);
|
||||
pKeyInd->type = DM_KEY_IRK;
|
||||
keyIndReady = TRUE;
|
||||
}
|
||||
else if (cmdCode == SMP_CMD_SIGN_INFO)
|
||||
{
|
||||
/* parse signing information packet */
|
||||
Calc128Cpy(pKeyInd->keyData.csrk.key, pBuf);
|
||||
pKeyInd->type = DM_KEY_CSRK;
|
||||
keyIndReady = TRUE;
|
||||
}
|
||||
|
||||
/* set up to receive next key */
|
||||
|
||||
/* if just got first part of LTK or IRK */
|
||||
if (pCcb->nextCmdCode == SMP_CMD_ENC_INFO || pCcb->nextCmdCode == SMP_CMD_ID_INFO)
|
||||
{
|
||||
/* wait for second part of LTK or IRK info */
|
||||
pCcb->nextCmdCode++;
|
||||
}
|
||||
/* else if got LTK and need IRK */
|
||||
else if ((keyDist & SMP_KEY_DIST_ID) && (pCcb->nextCmdCode == SMP_CMD_MASTER_ID))
|
||||
{
|
||||
/* wait for first part of IRK */
|
||||
pCcb->nextCmdCode = SMP_CMD_ID_INFO;
|
||||
}
|
||||
/* else if got LTK or IRK and need SRK */
|
||||
else if ((keyDist & SMP_KEY_DIST_SIGN) &&
|
||||
(pCcb->nextCmdCode == SMP_CMD_MASTER_ID || pCcb->nextCmdCode == SMP_CMD_ID_ADDR_INFO))
|
||||
{
|
||||
/* wait for SRK */
|
||||
pCcb->nextCmdCode = SMP_CMD_SIGN_INFO;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* done receiving keys */
|
||||
done = TRUE;
|
||||
}
|
||||
|
||||
/* call callback if key ready */
|
||||
if (keyIndReady)
|
||||
{
|
||||
pKeyInd->hdr.event = DM_SEC_KEY_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) pKeyInd);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Maximum unsuccessful pairing attempts reached.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActMaxAttempts(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint32_t timeout;
|
||||
|
||||
/* send paring failed packet; note this stops the timer so call this first */
|
||||
smpActPairingCancel(pCcb, pMsg);
|
||||
|
||||
/* Check SMP device DB to determine time to wait before pairing can happen again */
|
||||
timeout = SmpDbMaxAttemptReached(pCcb->connId);
|
||||
|
||||
/* start wait interval timer */
|
||||
pCcb->waitTimer.msg.event = SMP_MSG_INT_WI_TIMEOUT;
|
||||
WsfTimerStartMs(&pCcb->waitTimer, timeout);
|
||||
|
||||
/* clear attempts count */
|
||||
pCcb->attempts = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief A pairing attempt was received while in 'repeated attempts' state.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActAttemptRcvd(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* set that attempt was received */
|
||||
pCcb->attempts = 1;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Notify DM of a pairing failure due to max attempt failures.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActNotifyDmAttemptsFailure(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* notify DM of pairing failure */
|
||||
pMsg->hdr.status = SMP_ERR_ATTEMPTS;
|
||||
pMsg->hdr.event = DM_SEC_PAIR_FAIL_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Notify DM of a pairing failure due to response timeout.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActNotifyDmRspToFailure(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* notify DM of pairing failure */
|
||||
pMsg->hdr.status = SMP_ERR_TIMEOUT;
|
||||
pMsg->hdr.event = DM_SEC_PAIR_FAIL_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if any pairing attempts received when leaving 'repeated attempts' state.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActCheckAttempts(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* check if attempt was received */
|
||||
if (pCcb->attempts)
|
||||
{
|
||||
pCcb->attempts = 0;
|
||||
smpSendPairingFailed(pCcb, SMP_ERR_ATTEMPTS);
|
||||
|
||||
/* notify DM of pairing failure */
|
||||
smpActNotifyDmAttemptsFailure(pCcb, pMsg);
|
||||
|
||||
smpCleanup(pCcb);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pairing completed successfully.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairingCmpl(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
dmSecPairCmplIndEvt_t pairCmpl;
|
||||
|
||||
smpCleanup(pCcb);
|
||||
|
||||
/* set connection idle */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_SMP_PAIR, DM_CONN_IDLE);
|
||||
|
||||
pairCmpl.auth = pCcb->auth;
|
||||
pairCmpl.hdr.param = pCcb->connId;
|
||||
pairCmpl.hdr.event = DM_SEC_PAIR_CMPL_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) &pairCmpl);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the SMP state machine.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpSmExecute(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smpTblEntry_t const *pTblEntry;
|
||||
smpSmIf_t const *pSmIf;
|
||||
|
||||
#if SMP_EXTRA_TRACE == TRUE
|
||||
if (smpCb.lescSupported)
|
||||
SMP_TRACE_INFO2("SMP Exe: evt=%s st=%s", smpEventStr(pMsg->hdr.event), smpStateStr(pCcb->state));
|
||||
else
|
||||
#endif
|
||||
SMP_TRACE_INFO2("smpSmExecute event=%d state=%d", pMsg->hdr.event, pCcb->state);
|
||||
|
||||
/* look up state table for state */
|
||||
pSmIf = DmConnRole(pCcb->connId) == DM_ROLE_SLAVE? smpCb.pSlave : smpCb.pMaster;
|
||||
pTblEntry = pSmIf->pStateTbl[pCcb->state];
|
||||
|
||||
/* run through state machine twice; once with state table for current state
|
||||
* and once with the state table for common events
|
||||
*/
|
||||
for(;;)
|
||||
{
|
||||
/* look for event match and execute action */
|
||||
do
|
||||
{
|
||||
/* if match */
|
||||
if ((*pTblEntry)[SMP_SM_POS_EVENT] == pMsg->hdr.event)
|
||||
{
|
||||
/* set next state */
|
||||
pCcb->state = (*pTblEntry)[SMP_SM_POS_NEXT_STATE];
|
||||
|
||||
/* execute action */
|
||||
(*pSmIf->pActionTbl[(*pTblEntry)[SMP_SM_POS_ACTION]])(pCcb, pMsg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* next entry */
|
||||
pTblEntry++;
|
||||
|
||||
/* while not at end */
|
||||
} while ((*pTblEntry)[SMP_SM_POS_EVENT] != 0);
|
||||
|
||||
/* if we've reached end of the common state table */
|
||||
if (pTblEntry == (pSmIf->pCommonTbl + SMP_STATE_TBL_COMMON_MAX - 1))
|
||||
{
|
||||
/* we're done */
|
||||
break;
|
||||
}
|
||||
/* else we haven't run through common state table yet */
|
||||
else
|
||||
{
|
||||
/* set it up */
|
||||
pTblEntry = pSmIf->pCommonTbl;
|
||||
}
|
||||
}
|
||||
}
|
||||
+433
@@ -0,0 +1,433 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP device database.
|
||||
*
|
||||
* Copyright (c) 2018-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Period in ms to service the database. */
|
||||
#define SMP_DB_SRV_MS 1000
|
||||
|
||||
/* Decrement a timer. */
|
||||
#define SMP_DB_DEC_TIMER(a) do { a = a > SMP_DB_SRV_MS ? a - SMP_DB_SRV_MS : 0;} while (0)
|
||||
|
||||
/* Device database indicies. */
|
||||
#define SMP_DB_COMMON_REC 0 /*! Common record used when database is full. */
|
||||
#define SMP_DB_FIRST_REC 1 /*! Index of first device specific record in database. */
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bdAddr_t peerAddr; /*! Peer address. */
|
||||
uint8_t addrType; /*! Peer address type. */
|
||||
uint8_t failCount; /*! Prior attempt failure count. */
|
||||
uint16_t attemptMult; /*! Attempts timeout multiplier (0 - record not used). */
|
||||
uint32_t lockMs; /*! Time remaining until device can attempt pairing. */
|
||||
uint32_t expDecrementMs; /*! Time remaining until attempt attemptMult decreases. */
|
||||
uint32_t failCountToMs; /*! Time remaining until failCount is cleared. */
|
||||
} smpDbDevice_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block. */
|
||||
static struct
|
||||
{
|
||||
smpDbDevice_t db[SMP_DB_MAX_DEVICES]; /*! Device database. */
|
||||
wsfTimer_t serviceTimer; /*! Timer to service database. */
|
||||
} smpDbCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Ensure the SMP DB service timer is running.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpDbStartServiceTimer(void)
|
||||
{
|
||||
if (smpDbCb.serviceTimer.isStarted == FALSE)
|
||||
{
|
||||
WsfTimerStartMs(&smpDbCb.serviceTimer, SMP_DB_SRV_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if a database record is in use.
|
||||
*
|
||||
* \param pRec Pointer to the record.
|
||||
*
|
||||
* \return TRUE if record in use, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t smpDbRecordInUse(smpDbDevice_t *pRec)
|
||||
{
|
||||
/* When failCount, lockMs, and attemptMult are zero, the record can be used for another device. */
|
||||
if (pRec->failCount > 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (pRec->lockMs > 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (pRec->attemptMult > 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add a device to the database.
|
||||
*
|
||||
* \param pAddr Peer BD address.
|
||||
* \param addrType Peer BD address type.
|
||||
*
|
||||
* \return Pointer to DB record or NULL if database full.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static smpDbDevice_t *smpDbAddDevice(uint8_t *pAddr, uint8_t addrType)
|
||||
{
|
||||
smpDbDevice_t *pRec = &smpDbCb.db[SMP_DB_FIRST_REC];
|
||||
uint8_t i;
|
||||
|
||||
SMP_TRACE_INFO0("smpDbAddDevice");
|
||||
|
||||
for (i = SMP_DB_FIRST_REC; i < SMP_DB_MAX_DEVICES; i++, pRec++)
|
||||
{
|
||||
if (smpDbRecordInUse(pRec) == FALSE)
|
||||
{
|
||||
/* Reset record. */
|
||||
memset(pRec, 0, sizeof(smpDbDevice_t));
|
||||
|
||||
pRec->addrType = addrType;
|
||||
BdaCpy(pRec->peerAddr, pAddr);
|
||||
return pRec;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get a record in the database.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return Pointer to the record associated with the connection or the common record.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static smpDbDevice_t *smpDbGetRecord(dmConnId_t connId)
|
||||
{
|
||||
smpDbDevice_t *pRec = &smpDbCb.db[SMP_DB_FIRST_REC];
|
||||
uint8_t addrType = DmHostAddrType(DmConnPeerAddrType(connId));
|
||||
uint8_t *pAddr = DmConnPeerAddr(connId);
|
||||
uint8_t i;
|
||||
|
||||
SMP_TRACE_INFO2("smpDbGetRecord: connId: %d type: %d", connId, addrType);
|
||||
|
||||
for (i = SMP_DB_FIRST_REC; i < SMP_DB_MAX_DEVICES; i++, pRec++)
|
||||
{
|
||||
if (smpDbRecordInUse(pRec) && (pRec->addrType == addrType) && BdaCmp(pRec->peerAddr, pAddr))
|
||||
{
|
||||
return pRec;
|
||||
}
|
||||
}
|
||||
|
||||
/* Device is not in the database, add the device. */
|
||||
pRec = smpDbAddDevice(pAddr, addrType);
|
||||
|
||||
if (pRec == NULL)
|
||||
{
|
||||
SMP_TRACE_INFO0("smpDbGetRecord: common record");
|
||||
|
||||
/* Database is full, use the common record. */
|
||||
pRec = &smpDbCb.db[SMP_DB_COMMON_REC];
|
||||
}
|
||||
|
||||
return pRec;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the SMP Database.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbInit(void)
|
||||
{
|
||||
/* Stop active service timer. */
|
||||
if (smpDbCb.serviceTimer.isStarted == TRUE)
|
||||
{
|
||||
WsfTimerStop(&smpDbCb.serviceTimer);
|
||||
}
|
||||
|
||||
/* Reset control block. */
|
||||
memset(&smpDbCb, 0, sizeof(smpDbCb));
|
||||
|
||||
/* Setup service timer. */
|
||||
smpDbCb.serviceTimer.handlerId = smpCb.handlerId;
|
||||
smpDbCb.serviceTimer.msg.event = SMP_DB_SERVICE_IND;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the time (msec) that pairing is disabled for a device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return Time pairing is disabled (msec), or zero if pairing isn't disabled.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t SmpDbGetPairingDisabledTime(dmConnId_t connId)
|
||||
{
|
||||
smpDbDevice_t *pRec = smpDbGetRecord(connId);
|
||||
|
||||
SMP_TRACE_INFO3("SmpDbGetPairingDisabledTime: connId: %d period: %d attemptMult: %d",
|
||||
connId, pRec->lockMs, pRec->attemptMult);
|
||||
|
||||
return pRec->lockMs;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the count of prior failures for a device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param count Failure count.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbSetFailureCount(dmConnId_t connId, uint8_t count)
|
||||
{
|
||||
smpDbDevice_t *pRec = smpDbGetRecord(connId);
|
||||
|
||||
SMP_TRACE_INFO2("SmpDbSetFailureCount: connId: %d count: %d", connId, count);
|
||||
|
||||
pRec->failCount = count;
|
||||
|
||||
if (count != 0)
|
||||
{
|
||||
pRec->failCountToMs = pSmpCfg->maxAttemptTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the count of prior failures for a device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return The failure count.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t SmpDbGetFailureCount(dmConnId_t connId)
|
||||
{
|
||||
smpDbDevice_t *pRec = smpDbGetRecord(connId);
|
||||
|
||||
SMP_TRACE_INFO2("SmpDbGetFailureCount: connId: %d count: %d", connId, pRec->failCount);
|
||||
|
||||
return pRec->failCount;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to report max pairing attempts was reached for a device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return Time until device can retry pairing.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t SmpDbMaxAttemptReached(dmConnId_t connId)
|
||||
{
|
||||
smpDbDevice_t *pRec = smpDbGetRecord(connId);
|
||||
uint16_t multiplier;
|
||||
|
||||
SMP_TRACE_INFO1("SmpDbMaxAttemptReached: connId: %d", connId);
|
||||
|
||||
if (pRec->attemptMult == 0)
|
||||
{
|
||||
/* Due to a disconnection, a record exists but the attempt multipier hasn't been set. */
|
||||
multiplier = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
multiplier = (pRec->attemptMult * pSmpCfg->attemptExp);
|
||||
}
|
||||
|
||||
if ((pSmpCfg->attemptTimeout * multiplier) <= pSmpCfg->maxAttemptTimeout)
|
||||
{
|
||||
pRec->lockMs = pSmpCfg->attemptTimeout * multiplier;
|
||||
pRec->attemptMult = multiplier;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Exponential increase is greater than max timeout. */
|
||||
pRec->lockMs = pSmpCfg->maxAttemptTimeout;
|
||||
}
|
||||
|
||||
pRec->expDecrementMs = pSmpCfg->attemptDecTimeout;
|
||||
|
||||
/* Ensure the service timer is running. */
|
||||
smpDbStartServiceTimer();
|
||||
|
||||
return pRec->lockMs;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to report to the SMP DB that a pairing operation failed
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbPairingFailed(dmConnId_t connId)
|
||||
{
|
||||
smpDbDevice_t *pRec = smpDbGetRecord(connId);
|
||||
|
||||
SMP_TRACE_INFO1("SmpDbPairingFailed: connId: %d", connId);
|
||||
|
||||
/* Reset exponent decrement timer. */
|
||||
pRec->expDecrementMs = pSmpCfg->attemptDecTimeout;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Service the device database timers
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbService(void)
|
||||
{
|
||||
uint8_t i;
|
||||
smpDbDevice_t *pRec = smpDbCb.db;
|
||||
|
||||
/* Service device specific records. */
|
||||
for (i = 0; i < SMP_DB_MAX_DEVICES; i++, pRec++)
|
||||
{
|
||||
if (smpDbRecordInUse(pRec))
|
||||
{
|
||||
/* Decrement all time periods. */
|
||||
SMP_DB_DEC_TIMER(pRec->expDecrementMs);
|
||||
SMP_DB_DEC_TIMER(pRec->lockMs);
|
||||
SMP_DB_DEC_TIMER(pRec->failCountToMs);
|
||||
|
||||
/* Process expDecrementMs timeout. */
|
||||
if (pRec->expDecrementMs == 0)
|
||||
{
|
||||
/* Exponential decrease of multiplier. */
|
||||
pRec->attemptMult /= pSmpCfg->attemptExp;
|
||||
|
||||
if (pRec->attemptMult)
|
||||
{
|
||||
pRec->expDecrementMs = pSmpCfg->attemptDecTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
/* Process failCountToMs timeout. */
|
||||
if (pRec->failCountToMs == 0)
|
||||
{
|
||||
pRec->failCount = 0;
|
||||
}
|
||||
|
||||
/* If the record is in use, ensure the service timer is running. */
|
||||
if (smpDbRecordInUse(pRec))
|
||||
{
|
||||
smpDbStartServiceTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove all records from the SMP database.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbRemoveAllDevices(void)
|
||||
{
|
||||
/* Stop active service timer. */
|
||||
if (smpDbCb.serviceTimer.isStarted == TRUE)
|
||||
{
|
||||
WsfTimerStop(&smpDbCb.serviceTimer);
|
||||
}
|
||||
|
||||
/* Reset database. */
|
||||
memset(&smpDbCb.db, 0, sizeof(smpDbCb.db));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove a device with the given identity from the SMP database.
|
||||
*`
|
||||
* \param pAddr Pointer to BD Address.
|
||||
* \param addrType BD Address type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbRemoveDevice(uint8_t *pAddr, uint8_t addrType)
|
||||
{
|
||||
smpDbDevice_t *pRec = &smpDbCb.db[SMP_DB_FIRST_REC];
|
||||
uint8_t peerAddrType = DmHostAddrType(addrType);
|
||||
uint8_t i;
|
||||
|
||||
for (i = SMP_DB_FIRST_REC; i < SMP_DB_MAX_DEVICES; i++, pRec++)
|
||||
{
|
||||
if (smpDbRecordInUse(pRec) && (pRec->addrType == peerAddrType) && BdaCmp(pRec->peerAddr, pAddr))
|
||||
{
|
||||
/* Reset record. */
|
||||
memset(pRec, 0, sizeof(smpDbDevice_t));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
+862
@@ -0,0 +1,862 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP main module.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "dm_api.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
#include "smp_handler.h"
|
||||
#include "sec_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* SMP packet length table */
|
||||
const uint8_t smpPktLenTbl[] =
|
||||
{
|
||||
0,
|
||||
SMP_PAIR_REQ_LEN,
|
||||
SMP_PAIR_RSP_LEN,
|
||||
SMP_PAIR_CNF_LEN,
|
||||
SMP_PAIR_RAND_LEN,
|
||||
SMP_PAIR_FAIL_LEN,
|
||||
SMP_ENC_INFO_LEN,
|
||||
SMP_MASTER_ID_LEN,
|
||||
SMP_ID_INFO_LEN,
|
||||
SMP_ID_ADDR_INFO_LEN,
|
||||
SMP_SIGN_INFO_LEN,
|
||||
SMP_SECURITY_REQ_LEN,
|
||||
SMP_PUB_KEY_MSG_LEN,
|
||||
SMP_DHKEY_CHECK_MSG_LEN,
|
||||
SMP_KEYPRESS_MSG_LEN
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
smpCb_t smpCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C data callback for SMP.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpL2cDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t cmdCode;
|
||||
smpCcb_t *pCcb;
|
||||
|
||||
/* get connection control block for this handle, ignore packet if not found */
|
||||
if ((pCcb = smpCcbByHandle(handle)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse command code */
|
||||
cmdCode = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* verify length and that command is the expected command or pairing failed */
|
||||
if ((cmdCode >= SMP_CMD_PAIR_REQ && cmdCode < SMP_CMD_MAX) &&
|
||||
(len == smpPktLenTbl[cmdCode]) &&
|
||||
((cmdCode == pCcb->nextCmdCode) || (cmdCode == SMP_CMD_PAIR_FAIL)))
|
||||
{
|
||||
smpMsg_t msg;
|
||||
|
||||
/* send to state machine */
|
||||
if (cmdCode == SMP_CMD_PAIR_FAIL)
|
||||
{
|
||||
msg.hdr.event = SMP_MSG_CMD_PAIRING_FAILED;
|
||||
msg.hdr.status = *(pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.hdr.event = SMP_MSG_CMD_PKT;
|
||||
}
|
||||
|
||||
msg.hdr.param = pCcb->connId;
|
||||
msg.data.pPacket = pPacket;
|
||||
smpSmExecute(pCcb, &msg);
|
||||
}
|
||||
/* else ignore it */
|
||||
else
|
||||
{
|
||||
SMP_TRACE_WARN3("unexpected packet cmd:%d len:%d, expected:%d", cmdCode, len, pCcb->nextCmdCode);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C control callback for SMP.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpL2cCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
smpCcb_t *pCcb;
|
||||
uint8_t *pPkt;
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = smpCcbByConnId((dmConnId_t) pMsg->param);
|
||||
|
||||
/* verify connection is open */
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* set flow */
|
||||
pCcb->flowDisabled = (pMsg->event == L2C_CTRL_FLOW_DISABLE_IND);
|
||||
|
||||
/* if data flow enabled */
|
||||
if (!pCcb->flowDisabled)
|
||||
{
|
||||
/* if packet in qeueue */
|
||||
if (pCcb->pQueued != NULL)
|
||||
{
|
||||
/* send queued packet */
|
||||
pPkt = pCcb->pQueued;
|
||||
pCcb->pQueued = NULL;
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
|
||||
/* if SMP state not idle */
|
||||
if (!smpStateIdle(pCcb))
|
||||
{
|
||||
/* trigger send of next key */
|
||||
pMsg->event = SMP_MSG_INT_SEND_NEXT_KEY;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to resume attempts state if a disconnect and reconnect occured while in the
|
||||
* attempts state.
|
||||
*
|
||||
* \param connId Connection ID to prevent pairing.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpResumeAttemptsState(dmConnId_t connId)
|
||||
{
|
||||
smpCcb_t *pCcb = smpCcbByConnId(connId);
|
||||
uint32_t timeMs = SmpDbGetPairingDisabledTime(connId);
|
||||
|
||||
if (timeMs)
|
||||
{
|
||||
if (smpCb.lescSupported)
|
||||
{
|
||||
pCcb->state = DmConnRole(connId) == DM_ROLE_SLAVE? SMPR_SC_SM_ST_ATTEMPTS : SMPI_SC_SM_ST_ATTEMPTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCcb->state = DmConnRole(connId) == DM_ROLE_SLAVE? SMPR_SM_ST_ATTEMPTS : SMPI_SM_ST_ATTEMPTS;
|
||||
}
|
||||
|
||||
/* Start smp timer indicating the time to prevent pairing in the attempts state */
|
||||
pCcb->waitTimer.msg.event = SMP_MSG_INT_WI_TIMEOUT;
|
||||
WsfTimerStartMs(&pCcb->waitTimer, timeMs);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM connection callback for SMP.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpDmConnCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
smpCcb_t *pCcb;
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
pCcb = smpCcbByConnId((dmConnId_t) pDmEvt->hdr.param);
|
||||
|
||||
/* if new connection created */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
/* set up state machine for master or slave */
|
||||
if (DmConnRole((dmConnId_t) pDmEvt->hdr.param) == DM_ROLE_MASTER)
|
||||
{
|
||||
pCcb->initiator = TRUE;
|
||||
pCcb->nextCmdCode = SMP_CMD_SECURITY_REQ;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCcb->initiator = FALSE;
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_REQ;
|
||||
}
|
||||
|
||||
/* initialize control block */
|
||||
pCcb->handle = pDmEvt->connOpen.handle;
|
||||
pCcb->connId = (dmConnId_t) pDmEvt->hdr.param;
|
||||
pCcb->secReq = FALSE;
|
||||
pCcb->flowDisabled = FALSE;
|
||||
pCcb->attempts = SmpDbGetFailureCount((dmConnId_t) pDmEvt->hdr.param);
|
||||
pCcb->lastSentKey = 0;
|
||||
pCcb->state = 0;
|
||||
|
||||
/* Resume the attempts state if necessary */
|
||||
smpResumeAttemptsState((dmConnId_t) pDmEvt->hdr.param);
|
||||
}
|
||||
/* else if connection has been opened */
|
||||
else if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* handle close */
|
||||
if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* store attempts count */
|
||||
SmpDbSetFailureCount((dmConnId_t) pDmEvt->hdr.param, pCcb->attempts);
|
||||
|
||||
/* send to state machine */
|
||||
hdr.param = pDmEvt->hdr.param;
|
||||
hdr.event = SMP_MSG_DM_CONN_CLOSE;
|
||||
hdr.status = pDmEvt->connClose.reason + DM_SEC_HCI_ERR_BASE;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
|
||||
/* clear conn ID after handling event */
|
||||
pCcb->connId = DM_CONN_ID_NONE;
|
||||
|
||||
/* free queued packet buffer */
|
||||
if (pCcb->pQueued != NULL)
|
||||
{
|
||||
WsfMsgFree(pCcb->pQueued);
|
||||
pCcb->pQueued = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the given handle.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
smpCcb_t *smpCcbByHandle(uint16_t handle)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) != DM_CONN_ID_NONE)
|
||||
{
|
||||
return &smpCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
smpCcb_t *smpCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return &smpCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform the first part of SMP calculation C1.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pKey Encryption key parameter 'k'.
|
||||
* \param pRand Random value 'r'.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpCalcC1Part1(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pRand)
|
||||
{
|
||||
uint8_t buf[HCI_ENCRYPT_DATA_LEN];
|
||||
uint8_t *p;
|
||||
uint8_t i;
|
||||
uint8_t iAddrType;
|
||||
uint8_t rAddrType;
|
||||
|
||||
/* set initiator/responder address types */
|
||||
if (pCcb->initiator)
|
||||
{
|
||||
/* if local device's using RPA */
|
||||
if (!BdaIsZeros(DmConnLocalRpa(pCcb->connId)))
|
||||
{
|
||||
iAddrType = DM_ADDR_RANDOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAddrType = DmConnLocalAddrType(pCcb->connId);
|
||||
}
|
||||
|
||||
/* if peer device's using RPA */
|
||||
if (!BdaIsZeros(DmConnPeerRpa(pCcb->connId)))
|
||||
{
|
||||
rAddrType = DM_ADDR_RANDOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
rAddrType = DmConnPeerAddrType(pCcb->connId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if peer device's using RPA */
|
||||
if (!BdaIsZeros(DmConnPeerRpa(pCcb->connId)))
|
||||
{
|
||||
iAddrType = DM_ADDR_RANDOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAddrType = DmConnPeerAddrType(pCcb->connId);
|
||||
}
|
||||
|
||||
/* if local device's using RPA */
|
||||
if (!BdaIsZeros(DmConnLocalRpa(pCcb->connId)))
|
||||
{
|
||||
rAddrType = DM_ADDR_RANDOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
rAddrType = DmConnLocalAddrType(pCcb->connId);
|
||||
}
|
||||
}
|
||||
|
||||
/* note all numbers contained in byte arrays are little endian */
|
||||
|
||||
/* create parameter from xor of r and pres, preq, rat, and iat */
|
||||
p = buf;
|
||||
*p++ = iAddrType ^ *pRand++;
|
||||
*p++ = rAddrType ^ *pRand++;
|
||||
for (i = 0; i < SMP_PAIR_REQ_LEN; i++)
|
||||
{
|
||||
*p++ = pCcb->pairReq[i] ^ *pRand++;
|
||||
}
|
||||
for (i = 0; i < SMP_PAIR_RSP_LEN; i++)
|
||||
{
|
||||
*p++ = pCcb->pairRsp[i] ^ *pRand++;
|
||||
}
|
||||
|
||||
/* encrypt */
|
||||
pCcb->token = SecAes(pKey, buf, smpCb.handlerId, pCcb->connId, SMP_MSG_WSF_AES_CMPL);
|
||||
|
||||
if (pCcb->token == SEC_TOKEN_INVALID)
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
/* fail on invalid token */
|
||||
hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform the second part of SMP calculation C1.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pKey Encryption key parameter 'k'.
|
||||
* \param pPart1 Result from part 1.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpCalcC1Part2(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pPart1)
|
||||
{
|
||||
uint8_t buf[HCI_ENCRYPT_DATA_LEN];
|
||||
uint8_t *p;
|
||||
uint8_t i;
|
||||
uint8_t *pIaddr;
|
||||
uint8_t *pRaddr;
|
||||
|
||||
/* set initiator/responder addresss */
|
||||
if (pCcb->initiator)
|
||||
{
|
||||
/* use local device's RPA */
|
||||
pIaddr = DmConnLocalRpa(pCcb->connId);
|
||||
|
||||
/* if local device's not using RPA */
|
||||
if (BdaIsZeros(pIaddr))
|
||||
{
|
||||
/* use local device's address */
|
||||
pIaddr = DmConnLocalAddr(pCcb->connId);
|
||||
}
|
||||
|
||||
/* use peer device's RPA */
|
||||
pRaddr = DmConnPeerRpa(pCcb->connId);
|
||||
|
||||
/* if peer device's not using RPA */
|
||||
if (BdaIsZeros(pRaddr))
|
||||
{
|
||||
/* use peer device's address */
|
||||
pRaddr = DmConnPeerAddr(pCcb->connId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* use peer device's RPA */
|
||||
pIaddr = DmConnPeerRpa(pCcb->connId);
|
||||
|
||||
/* if peer device's not using RPA */
|
||||
if (BdaIsZeros(pIaddr))
|
||||
{
|
||||
/* use peer device's address */
|
||||
pIaddr = DmConnPeerAddr(pCcb->connId);
|
||||
}
|
||||
|
||||
/* use local device's RPA */
|
||||
pRaddr = DmConnLocalRpa(pCcb->connId);
|
||||
|
||||
/* if local device's not using RPA */
|
||||
if (BdaIsZeros(pRaddr))
|
||||
{
|
||||
/* use local device's address */
|
||||
pRaddr = DmConnLocalAddr(pCcb->connId);
|
||||
}
|
||||
}
|
||||
|
||||
/* note all numbers contained in byte arrays are little endian */
|
||||
|
||||
/* create parameter from xor of part 1 result with ia, ra, and pad */
|
||||
p = buf;
|
||||
for (i = BDA_ADDR_LEN; i > 0; i--)
|
||||
{
|
||||
*p++ = *pRaddr++ ^ *pPart1++;
|
||||
}
|
||||
for (i = BDA_ADDR_LEN; i > 0; i--)
|
||||
{
|
||||
*p++ = *pIaddr++ ^ *pPart1++;
|
||||
}
|
||||
*p++ = *pPart1++;
|
||||
*p++ = *pPart1++;
|
||||
*p++ = *pPart1++;
|
||||
*p++ = *pPart1++;
|
||||
|
||||
/* encrypt */
|
||||
pCcb->token = SecAes(pKey, buf, smpCb.handlerId, pCcb->connId, SMP_MSG_WSF_AES_CMPL);
|
||||
|
||||
if (pCcb->token == SEC_TOKEN_INVALID)
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
/* fail on invalid token */
|
||||
hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform calculation S1.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pKey Encryption key parameter 'k'.
|
||||
* \param pRand1 Random value 1.
|
||||
* \param pRand2 Random value 2.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpCalcS1(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pRand1, uint8_t *pRand2)
|
||||
{
|
||||
uint8_t buf[HCI_ENCRYPT_DATA_LEN];
|
||||
|
||||
/* note all numbers contained in byte arrays are little endian */
|
||||
|
||||
/* construct parameter r' from r1 and r2 */
|
||||
Calc128Cpy64(buf, pRand2);
|
||||
Calc128Cpy64(&buf[SMP_RAND8_LEN], pRand1);
|
||||
|
||||
/* encrypt */
|
||||
pCcb->token = SecAes(pKey, buf, smpCb.handlerId, pCcb->connId, SMP_MSG_WSF_AES_CMPL);
|
||||
|
||||
if (pCcb->token == SEC_TOKEN_INVALID)
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
/* fail on invalid token */
|
||||
hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate LTK, EDIV, and RAND.
|
||||
*
|
||||
* \param pScr Pointer to scratch buffer containing input and output data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpGenerateLtk(smpCcb_t *pCcb)
|
||||
{
|
||||
uint8_t *p;
|
||||
smpScratch_t *pScr = pCcb->pScr;
|
||||
|
||||
/* generated results are stored in scratch buffer */
|
||||
p = pScr->keyInd.keyData.ltk.key;
|
||||
|
||||
/* generate LTK from random number */
|
||||
SecRand(p, pScr->keyInd.encKeyLen);
|
||||
p += pScr->keyInd.encKeyLen;
|
||||
|
||||
/* set remaining key bytes to zero */
|
||||
memset(p, 0, (SMP_KEY_LEN - pScr->keyInd.encKeyLen));
|
||||
|
||||
/* use existing random number stored in scratch buf b4 for EDIV and RAND */
|
||||
BYTES_TO_UINT16(pScr->keyInd.keyData.ltk.ediv, pScr->buf.b4);
|
||||
memcpy(pScr->keyInd.keyData.ltk.rand, &pScr->buf.b4[2], SMP_RAND8_LEN);
|
||||
|
||||
/* pass key to app via DM */
|
||||
pScr->keyInd.type = DM_KEY_LOCAL_LTK;
|
||||
pScr->keyInd.secLevel = (pCcb->auth & SMP_AUTH_MITM_FLAG) ? DM_SEC_LEVEL_ENC_AUTH : DM_SEC_LEVEL_ENC;
|
||||
pScr->keyInd.hdr.event = DM_SEC_KEY_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) &pScr->keyInd);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an SMP command packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pPkt Buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpSendPkt(smpCcb_t *pCcb, uint8_t *pPkt)
|
||||
{
|
||||
/* if flow disabled */
|
||||
if (pCcb->flowDisabled)
|
||||
{
|
||||
/* if packet already queued discard it and replace it with this new packet */
|
||||
if (pCcb->pQueued != NULL)
|
||||
{
|
||||
SMP_TRACE_WARN1("smpSendPkt packet discarded cmd:%d", pCcb->pQueued[L2C_PAYLOAD_START]);
|
||||
WsfMsgFree(pCcb->pQueued);
|
||||
}
|
||||
|
||||
/* queue packet */
|
||||
pCcb->pQueued = pPkt;
|
||||
}
|
||||
/* else send it to L2CAP */
|
||||
else
|
||||
{
|
||||
L2cDataReq(L2C_CID_SMP, pCcb->handle, smpPktLenTbl[pPkt[L2C_PAYLOAD_START]], pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if SMP connection is in idle state.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return TRUE if in idle state, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t smpStateIdle(smpCcb_t *pCcb)
|
||||
{
|
||||
return (pCcb->state == 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate an SMP data message buffer to be used for the SMP protocol messages.
|
||||
*
|
||||
* \param len Message length in bytes.
|
||||
*
|
||||
* \return Pointer to data message buffer or NULL if allocation failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *smpMsgAlloc(uint16_t len)
|
||||
{
|
||||
return WsfMsgDataAlloc(len, HCI_TX_DATA_TAILROOM);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by DM to send a message to SMP.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDmMsgSend(smpDmMsg_t *pMsg)
|
||||
{
|
||||
WsfMsgSend(smpCb.handlerId, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by DM to notify SMP of encrypted link status.
|
||||
*
|
||||
* \param pMsg Pointer to HCI message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDmEncryptInd(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* set event to SMP event type */
|
||||
pMsg->event = (pMsg->status == HCI_SUCCESS) ?
|
||||
SMP_MSG_DM_ENCRYPT_CMPL : SMP_MSG_DM_ENCRYPT_FAILED;
|
||||
|
||||
/* pass event to handler */
|
||||
SmpHandler(0, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate Secure Connections security level.
|
||||
* Note: calculation assumes Secure Connections was used. This function cannot be used to
|
||||
* calculate a legacy pairing's security level.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return Security level.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t smpGetScSecLevel(smpCcb_t *pCcb)
|
||||
{
|
||||
uint8_t secLevel;
|
||||
|
||||
if (pCcb->auth & SMP_AUTH_MITM_FLAG)
|
||||
{
|
||||
if (WSF_MIN(pCcb->pairReq[SMP_MAXKEY_POS], pCcb->pairRsp[SMP_MAXKEY_POS]) == SMP_KEY_SIZE_MAX)
|
||||
{
|
||||
secLevel = DM_SEC_LEVEL_ENC_LESC;
|
||||
}
|
||||
else
|
||||
{
|
||||
secLevel = DM_SEC_LEVEL_ENC_AUTH;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
secLevel = DM_SEC_LEVEL_ENC;
|
||||
}
|
||||
|
||||
return secLevel;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the STK for the given connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param pSecLevel Returns the security level of pairing when STK was created.
|
||||
*
|
||||
* \return Pointer to STK or NULL if not available.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *SmpDmGetStk(dmConnId_t connId, uint8_t *pSecLevel)
|
||||
{
|
||||
smpCcb_t *pCcb;
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = smpCcbByConnId(connId);
|
||||
|
||||
if (smpCb.lescSupported && pCcb->pScCcb->lescEnabled && (pCcb->pScCcb->pLtk != NULL))
|
||||
{
|
||||
/* set security level */
|
||||
*pSecLevel = smpGetScSecLevel(pCcb);
|
||||
|
||||
/* return buffer containing STK */
|
||||
return pCcb->pScCcb->pLtk->ltk_t;
|
||||
}
|
||||
else if (pCcb->pScr != NULL)
|
||||
{
|
||||
/* set security level */
|
||||
*pSecLevel = (pCcb->auth & SMP_AUTH_MITM_FLAG) ? DM_SEC_LEVEL_ENC_AUTH : DM_SEC_LEVEL_ENC;
|
||||
|
||||
/* return buffer containing STK */
|
||||
return pCcb->pScr->buf.b3;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the LTK for the given connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return Pointer to STK or NULL if not available.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *SmpDmGetLtk(dmConnId_t connId)
|
||||
{
|
||||
smpCcb_t *pCcb;
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = smpCcbByConnId(connId);
|
||||
|
||||
if (smpCb.lescSupported)
|
||||
{
|
||||
/* return buffer containing STK */
|
||||
return pCcb->pScCcb->pLtk->ltk_t;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief SMP handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for SMP.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
uint8_t i;
|
||||
smpCcb_t *pCcb;
|
||||
|
||||
/* store handler ID */
|
||||
smpCb.handlerId = handlerId;
|
||||
|
||||
/* Initialize the SMP device database */
|
||||
SmpDbInit();
|
||||
|
||||
/* Initialize control block CCBs */
|
||||
for (i = 0, pCcb = smpCb.ccb; i < DM_CONN_MAX; i++, pCcb++)
|
||||
{
|
||||
/* initialize response timer */
|
||||
pCcb->rspTimer.handlerId = handlerId;
|
||||
pCcb->rspTimer.msg.param = i + 1; /* param stores the conn id */
|
||||
|
||||
/* initialize wait interval timer */
|
||||
pCcb->waitTimer.handlerId = handlerId;
|
||||
pCcb->waitTimer.msg.param = i + 1; /* param stores the conn id */
|
||||
}
|
||||
|
||||
/* Register with L2C */
|
||||
L2cRegister(L2C_CID_SMP, smpL2cDataCback, smpL2cCtrlCback);
|
||||
|
||||
/* Register with DM */
|
||||
DmConnRegister(DM_CLIENT_ID_SMP, smpDmConnCback);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
*
|
||||
* \brief WSF event handler for SMP.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
smpCcb_t *pCcb;
|
||||
|
||||
/* Handle message */
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
if (pMsg->event == SMP_DB_SERVICE_IND)
|
||||
{
|
||||
SmpDbService();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pMsg->event == SMP_MSG_WSF_CMAC_CMPL)
|
||||
{
|
||||
secCmacMsg_t *pCmac = (secCmacMsg_t *) pMsg;
|
||||
|
||||
/* Free the plain text buffer that was allocated and passed into SecCmac */
|
||||
if (pCmac->pPlainText)
|
||||
{
|
||||
WsfBufFree(pCmac->pPlainText);
|
||||
}
|
||||
}
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = smpCcbByConnId((dmConnId_t) pMsg->param);
|
||||
|
||||
/* verify connection is open */
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* if AES result verify it is not stale */
|
||||
if (pMsg->event == SMP_MSG_WSF_AES_CMPL && pCcb->token != pMsg->status)
|
||||
{
|
||||
SMP_TRACE_WARN2("AES token mismatch: %d %d", pCcb->token, pMsg->status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* send to state machine */
|
||||
smpSmExecute(pCcb, (smpMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Handle events */
|
||||
else if (event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
+419
@@ -0,0 +1,419 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP main module.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef SMP_MAIN_H
|
||||
#define SMP_MAIN_H
|
||||
|
||||
#include "sec_api.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "l2c_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "smp_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* State machine table constants */
|
||||
#define SMP_SM_POS_EVENT 0 /* Column position for event */
|
||||
#define SMP_SM_POS_NEXT_STATE 1 /* Column position for next state */
|
||||
#define SMP_SM_POS_ACTION 2 /* Column position for action */
|
||||
#define SMP_SM_NUM_COLS 3 /* Number of columns in state table */
|
||||
#define SMP_STATE_TBL_COMMON_MAX 5 /* Number of entries in common state table */
|
||||
|
||||
/* Position of parameters in pairing request/response */
|
||||
#define SMP_IO_POS 1
|
||||
#define SMP_OOB_POS 2
|
||||
#define SMP_AUTHREQ_POS 3
|
||||
#define SMP_MAXKEY_POS 4
|
||||
#define SMP_IKEYDIST_POS 5
|
||||
#define SMP_RKEYDIST_POS 6
|
||||
|
||||
/* Position of parameters in public key message */
|
||||
#define SMP_PUB_KEY_X_POS 1
|
||||
#define SMP_PUB_KEY_Y_POS 33
|
||||
|
||||
/* LESC Authorization Types */
|
||||
#define SMP_AUTH_TYPE_UNKNOWN 0
|
||||
#define SMP_AUTH_TYPE_JUST_WORKS 1
|
||||
#define SMP_AUTH_TYPE_OOB 2
|
||||
#define SMP_AUTH_TYPE_PASSKEY 3
|
||||
#define SMP_AUTH_TYPE_NUM_COMP 4
|
||||
|
||||
/*! smpi_sm state machine states */
|
||||
enum
|
||||
{
|
||||
SMPI_SM_ST_IDLE, /*!< Idle */
|
||||
SMPI_SM_ST_PAIR_RSP, /*!< Wait for pairing response */
|
||||
SMPI_SM_ST_PIN, /*!< Wait for PIN */
|
||||
SMPI_SM_ST_CNF_CALC_1, /*!< Wait for confirm calc 1 */
|
||||
SMPI_SM_ST_CNF_CALC_2, /*!< Wait for confirm calc 2 */
|
||||
SMPI_SM_ST_PAIR_CNF, /*!< Wait for pairing confirm */
|
||||
SMPI_SM_ST_PAIR_RAND, /*!< Wait for pairing random */
|
||||
SMPI_SM_ST_CNF_VER_CALC_1, /*!< Wait for confirm verify calc 1 */
|
||||
SMPI_SM_ST_CNF_VER_CALC_2, /*!< Wait for confirm verify calc 2 */
|
||||
SMPI_SM_ST_STK_CALC, /*!< Wait for STK calc */
|
||||
SMPI_SM_ST_ENCRYPT, /*!< Wait for link encryption */
|
||||
SMPI_SM_ST_KEY_DIST, /*!< Key distribution */
|
||||
SMPI_SM_ST_ATTEMPTS, /*!< Repeated attempts */
|
||||
SMPI_SM_ST_RSP_TO /*!< Response timeout state */
|
||||
};
|
||||
|
||||
/*! smpr_sm state machine states */
|
||||
enum
|
||||
{
|
||||
SMPR_SM_ST_IDLE, /*!< Idle */
|
||||
SMPR_SM_ST_API_PAIR_REQ, /*!< Wait for API pairing request */
|
||||
SMPR_SM_ST_API_PAIR_RSP, /*!< Wait for API pairing response */
|
||||
SMPR_SM_ST_PIN_PAIR_1, /*!< Wait for PIN or pairing confirm 1 */
|
||||
SMPR_SM_ST_PIN_PAIR_2, /*!< Wait for PIN or pairing confirm 2 */
|
||||
SMPR_SM_ST_CNF_CALC_1, /*!< Wait for confirm calc 1 */
|
||||
SMPR_SM_ST_CNF_CALC_2, /*!< Wait for confirm calc 2 */
|
||||
SMPR_SM_ST_PAIR_RAND, /*!< Wait for pairing random */
|
||||
SMPR_SM_ST_CNF_VER_CALC_1, /*!< Wait for confirm verify calc 1 */
|
||||
SMPR_SM_ST_CNF_VER_CALC_2, /*!< Wait for confirm verify calc 2 */
|
||||
SMPR_SM_ST_STK_CALC, /*!< Wait for STK calc */
|
||||
SMPR_SM_ST_ENCRYPT, /*!< Wait for link encryption */
|
||||
SMPR_SM_ST_KEY_DIST, /*!< Key distribution */
|
||||
SMPR_SM_ST_ATTEMPTS, /*!< Repeated attempts */
|
||||
SMPR_SM_ST_RSP_TO /*!< Response timeout state */
|
||||
};
|
||||
|
||||
/*! smpi_sc_sm state machine states */
|
||||
enum
|
||||
{
|
||||
SMPI_SC_SM_ST_IDLE, /*!< Idle */
|
||||
SMPI_SC_SM_ST_PAIR_RSP, /*!< Wait for pairing response */
|
||||
SMPI_SC_SM_ST_MODE_SELECT, /*!< Select the security mode (LESC or Legacy) */
|
||||
SMPI_SC_SM_ST_LESC_PIN, /*!< Wait for PIN in LESC */
|
||||
SMPI_SC_SM_ST_PUB_KEY, /*!< Wait for public key from peer */
|
||||
SMPI_SC_SM_ST_AUTH_SELECT, /*!< Select the type of LESC pairing (Just Works, Passkey, or OOB) */
|
||||
|
||||
SMPI_SC_SM_ST_JWNC_WAIT_CNF, /*!< Wait for confirm in Just Works/Numeric Comparison Pairing */
|
||||
SMPI_SC_SM_ST_JWNC_RAND, /*!< Wait for rand in Just Works/Numeric Comparison Pairing */
|
||||
SMPI_SC_SM_ST_JWNC_CHECK_1, /*!< Calculate confirm value in Just Works/Numeric Comparison Pairing */
|
||||
SMPI_SC_SM_ST_JWNC_CHECK_2, /*!< Calculate user validate value in Just Works/Numeric Comparison Pairing */
|
||||
SMPI_SC_SM_ST_JWNC_WAIT_USER, /* Wait for user to validate the confirm value */
|
||||
|
||||
SMPI_SC_SM_ST_PK_KEYPRESS, /*!< Process a keypress command in passkey pairing */
|
||||
SMPI_SC_SM_ST_PK_CALC, /*!< Calculate the confirm in passkey pairing */
|
||||
SMPI_SC_SM_ST_PK_CNF, /*!< Wait for confirm in passkey pairing */
|
||||
SMPI_SC_SM_ST_PK_RAND, /*!< Wait for rand in passkey pairing */
|
||||
SMPI_SC_SM_ST_PK_CHECK, /*!< Check the conform value in passkey pairing */
|
||||
SMPI_SC_SM_ST_PK_REPEAT, /*!< Repeat or complete the passkey pairing */
|
||||
|
||||
SMPI_SC_SM_ST_OOB_SEND_RAND, /*!< Send the rand in OOB pairing */
|
||||
SMPI_SC_SM_ST_OOB_WAIT_RAND, /*!< Wait for a rand in OOB pairing */
|
||||
|
||||
SMPI_SC_SM_ST_CALC_DHKEY, /*!< Calculate the DHKEY shared secret */
|
||||
SMPI_SC_SM_ST_CALC_F5_TKEY, /*!< Calculate the DHKEY T Key */
|
||||
SMPI_SC_SM_ST_CALC_F5_MACKEY, /*!< Calculate the DHKEY MAC Key */
|
||||
SMPI_SC_SM_ST_CALC_F5_LTK, /*!< Calculate the DHKEY LTK Key */
|
||||
SMPI_SC_SM_ST_CALC_F6_EA, /*!< Calculate the DHKEY Ea Key */
|
||||
SMPI_SC_SM_ST_CALC_F6_EB, /*!< Calculate the DHKEY Eb Key */
|
||||
SMPI_SC_SM_ST_VERIFY_DH_CHECK, /*!< Verify the DHKEY check value */
|
||||
|
||||
SMPI_SC_SM_ST_LEGACY_PIN, /*!< Wait for PIN */
|
||||
SMPI_SC_SM_ST_CNF_CALC_1, /*!< Wait for confirm calc 1 */
|
||||
SMPI_SC_SM_ST_CNF_CALC_2, /*!< Wait for confirm calc 2 */
|
||||
SMPI_SC_SM_ST_PAIR_CNF, /*!< Wait for pairing confirm */
|
||||
SMPI_SC_SM_ST_PAIR_RAND, /*!< Wait for pairing random */
|
||||
SMPI_SC_SM_ST_CNF_VER_CALC_1, /*!< Wait for confirm verify calc 1 */
|
||||
SMPI_SC_SM_ST_CNF_VER_CALC_2, /*!< Wait for confirm verify calc 2 */
|
||||
SMPI_SC_SM_ST_STK_CALC, /*!< Wait for STK calc */
|
||||
SMPI_SC_SM_ST_ENCRYPT, /*!< Wait for link encryption */
|
||||
SMPI_SC_SM_ST_KEY_DIST, /*!< Key distribution */
|
||||
SMPI_SC_SM_ST_ATTEMPTS, /*!< Repeated attempts */
|
||||
SMPI_SC_SM_ST_RSP_TO /*!< Response timeout state */
|
||||
};
|
||||
|
||||
/*! smpr_sc_sm state machine states */
|
||||
enum
|
||||
{
|
||||
SMPR_SC_SM_ST_IDLE, /*!< Idle */
|
||||
SMPR_SC_SM_ST_API_PAIR_REQ, /*!< Wait for API pairing request */
|
||||
SMPR_SC_SM_ST_API_PAIR_RSP, /*!< Wait for API pairing response */
|
||||
SMPR_SC_SM_ST_MODE_SELECT, /*!< Select the security mode (LESC or Legacy) */
|
||||
SMPR_SC_SM_ST_PUB_KEY, /*!< Wait for public key from peer */
|
||||
SMPR_SC_SM_ST_LESC_PIN, /*!< Wait for pin in LESC */
|
||||
SMPR_SC_SM_ST_AUTH_SELECT, /*!< Select the type of LESC pairing (Just Works, Passkey, or OOB) */
|
||||
|
||||
SMPR_SC_SM_ST_JWNC_SETUP, /*!< Prepare for Just Works/Numeric Comparison Pairing */
|
||||
SMPR_SC_SM_ST_JWNC_WAIT_RAND, /*!< Wait for Rand in Just Works/Numeric Comparison Pairing */
|
||||
SMPR_SC_SM_ST_JWNC_CALC_G2, /*!< Calculate the user validate value in Just Works/Numeric Comparison Pairing */
|
||||
SMPR_SC_SM_ST_JWNC_WAIT_USER, /*!< Wait for user to validate the confirm value */
|
||||
SMPR_SC_SM_ST_JWNC_WAIT_USER_DH_CHECK_RCVD, /*!< Wait for user to validate the confirm value with a received DH Key Check */
|
||||
|
||||
SMPR_SC_SM_ST_PK_KEYPRESS, /*!< Process a keypress command in passkey pairing */
|
||||
SMPR_SC_SM_ST_PK_WAIT_AUTH, /*!< The confirm was received before the auth from the API, wait for the auth */
|
||||
SMPR_SC_SM_ST_PK_WAIT_CNF, /*!< Wait for the confirm command in passkey pairing */
|
||||
SMPR_SC_SM_ST_PK_CALC, /*!< Calculate the confirm in passkey pairing */
|
||||
SMPR_SC_SM_ST_PK_RAND, /*!< Send the rand command in passkey pairing */
|
||||
SMPR_SC_SM_ST_PK_CHECK, /*!< Check the confirm in passkey pairing */
|
||||
SMPR_SC_SM_ST_PK_REPEAT, /*!< Repeat or complete the passkey pairing */
|
||||
|
||||
SMPR_SC_SM_ST_OOB_SEND_RAND, /*!< Send the rand in OOB pairing */
|
||||
SMPR_SC_SM_ST_OOB_WAIT_RAND, /*!< Wait for a rand in OOB pairing */
|
||||
|
||||
SMPR_SC_SM_ST_WAIT_DH_CHECK, /*!< Wait for the DH Check command */
|
||||
SMPR_SC_SM_ST_CALC_DHKEY, /*!< Calculate the DHKEY shared secret */
|
||||
SMPR_SC_SM_ST_CALC_F5_TKEY, /*!< Calculate the DHKEY T Key */
|
||||
SMPR_SC_SM_ST_CALC_F5_MACKEY, /*!< Calculate the DHKEY MAC Key */
|
||||
SMPR_SC_SM_ST_CALC_F5_LTK, /*!< Calculate the DHKEY LTK Key */
|
||||
SMPR_SC_SM_ST_CALC_F6_EA, /*!< Calculate the DHKEY Ea Key */
|
||||
SMPR_SC_SM_ST_CALC_F6_EB, /*!< Calculate the DHKEY Eb Key */
|
||||
|
||||
SMPR_SC_SM_ST_PIN_PAIR_1, /*!< Wait for PIN or pairing confirm 1 */
|
||||
SMPR_SC_SM_ST_PIN_PAIR_2, /*!< Wait for PIN or pairing confirm 2 */
|
||||
SMPR_SC_SM_ST_CNF_CALC_1, /*!< Wait for confirm calc 1 */
|
||||
SMPR_SC_SM_ST_CNF_CALC_2, /*!< Wait for confirm calc 2 */
|
||||
SMPR_SC_SM_ST_PAIR_RAND, /*!< Wait for pairing random */
|
||||
SMPR_SC_SM_ST_CNF_VER_CALC_1, /*!< Wait for confirm verify calc 1 */
|
||||
SMPR_SC_SM_ST_CNF_VER_CALC_2, /*!< Wait for confirm verify calc 2 */
|
||||
SMPR_SC_SM_ST_STK_CALC, /*!< Wait for STK calc */
|
||||
SMPR_SC_SM_ST_ENCRYPT, /*!< Wait for link encryption */
|
||||
SMPR_SC_SM_ST_KEY_DIST, /*!< Key distribution */
|
||||
SMPR_SC_SM_ST_ATTEMPTS, /*!< Repeated attempts */
|
||||
SMPR_SC_SM_ST_RSP_TO /*!< Response timeout state */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data type for state machine table entry */
|
||||
typedef uint8_t smpTblEntry_t[SMP_SM_NUM_COLS];
|
||||
|
||||
/* Pairing request/response parameters */
|
||||
typedef uint8_t smpPair_t[SMP_PAIR_REQ_LEN];
|
||||
|
||||
/* Temporary storage structure for calculations and other data.
|
||||
* Note that keyInd overlaps b1 through b3 so these will be overwritten
|
||||
* when key exchange occurs.
|
||||
*
|
||||
* Notes on usage from start of pairing to encryption with STK:
|
||||
* buf.b1: PIN or OOB data
|
||||
* buf.b2: RAND from peer
|
||||
* buf.b3: pairing cnf received from peer, then STK (if responder)
|
||||
* buf.b4: RAND used in pairing calc
|
||||
*
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint8_t b1[SMP_KEY_LEN];
|
||||
uint8_t b2[SMP_KEY_LEN];
|
||||
uint8_t b3[SMP_KEY_LEN];
|
||||
uint8_t b4[SMP_KEY_LEN];
|
||||
} buf;
|
||||
dmSecKeyIndEvt_t keyInd;
|
||||
} smpScratch_t;
|
||||
|
||||
/* SMP message handling function type */
|
||||
typedef void (*smpMsgHandler_t)(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/* SMP connection callback type */
|
||||
typedef void (*smpConnCback_t)(dmEvt_t *pDmEvt);
|
||||
|
||||
/* SMP data message type */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /* Header structure */
|
||||
uint8_t *pPacket; /* Pointer to buffer containing L2CAP packet */
|
||||
} smpDataMsg_t;
|
||||
|
||||
/* Union of event handler data types */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr; /* Header structure */
|
||||
smpDmMsg_t dm; /* Union SMP DM message data types */
|
||||
secAes_t aes; /* AES Security callback parameters structure */
|
||||
smpDataMsg_t data; /* SMP data message */
|
||||
} smpMsg_t;
|
||||
|
||||
/* LE LTK Calc Scratch buffer */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t mac[SEC_CMAC_KEY_LEN]; /* MAC Key */
|
||||
uint8_t ltk_t[SEC_CMAC_KEY_LEN]; /* LTK Key or T value of MAC/LTK calculation */
|
||||
} smpScLtk_t;
|
||||
|
||||
/* ECC DH Key */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t pubKeyX[SMP_PUB_KEY_LEN]; /* Public Key X */
|
||||
uint8_t pubKeyY[SMP_PUB_KEY_LEN]; /* Public Key Y */
|
||||
} smpScPubKey_t;
|
||||
|
||||
/* LESC Scratch Buffers */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t Na_Ea[SMP_RAND_LEN]; /* Initiator Na or Ea */
|
||||
uint8_t Nb_Eb[SMP_RAND_LEN]; /* Responder Nb or Eb */
|
||||
uint8_t Ra[SMP_RAND_LEN]; /* Initiator Ra */
|
||||
uint8_t Rb[SMP_RAND_LEN]; /* Responder Nb */
|
||||
uint8_t PeerCb[SMP_RAND_LEN]; /* Peer Responder Confirm */
|
||||
uint8_t PeerCa_Ea[SMP_RAND_LEN]; /* Peer Responder Confirm or DH Key Check */
|
||||
} smpScScratch_t;
|
||||
|
||||
/* SMP LESC Control Block */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t lescEnabled; /* TRUE when LE Secure Connection pairing in use */
|
||||
uint8_t authType; /* Type of authentication (Just Works, Numeric Comparison, Passkey, or OOB) */
|
||||
uint8_t kpNotify; /* TRUE when Keypress notification in use */
|
||||
uint8_t pkPos; /* Current passkey bit position */
|
||||
bool_t display; /* Passkey display setting */
|
||||
smpScPubKey_t *pPeerPublicKey; /* Peer device's ECC Key */
|
||||
smpScPubKey_t *pLocalPublicKey; /* Local device's public ECC Key */
|
||||
uint8_t *pPrivateKey; /* Local device's private ECC Key */
|
||||
smpScScratch_t *pScratch; /* Scratch Buffer */
|
||||
smpScLtk_t *pLtk; /* LTK calculation control */
|
||||
} smpScCcb_t;
|
||||
|
||||
/* SMP connection control block */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t rspTimer; /* Response timer */
|
||||
wsfTimer_t waitTimer; /* Wait interval timer after max attempts */
|
||||
smpPair_t pairReq; /* Pair request parameters */
|
||||
smpPair_t pairRsp; /* Pair response parameters */
|
||||
smpScratch_t *pScr; /* Pointer to scratchpad buffer */
|
||||
uint8_t *pQueued; /* Pointer to queued packet buffer */
|
||||
uint16_t handle; /* Connection handle */
|
||||
bool_t initiator; /* TRUE if device is initiator */
|
||||
bool_t secReq; /* TRUE if security request sent or received */
|
||||
bool_t flowDisabled; /* TRUE if data flow disabled */
|
||||
dmConnId_t connId; /* DM connection ID */
|
||||
uint8_t state; /* State machine state */
|
||||
uint8_t nextCmdCode; /* Command code of next expected packet */
|
||||
uint8_t auth; /* Resulting authentication and bonding flags */
|
||||
uint8_t token; /* AES transaction token */
|
||||
uint8_t attempts; /* Failed pairing attempts */
|
||||
uint8_t lastSentKey; /* Command code of last sent key */
|
||||
smpScCcb_t *pScCcb; /* LE Secure Connection control blocks */
|
||||
} smpCcb_t;
|
||||
|
||||
/* State machine action function type */
|
||||
typedef void (*smpAct_t)(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
|
||||
/* SMP process pairing function */
|
||||
typedef bool_t smpProcPairing_t(smpCcb_t *pCcb, uint8_t *pOob, uint8_t *pDisplay);
|
||||
|
||||
/* SMP process pairing function */
|
||||
typedef void smpProcAuthReq_t(smpCcb_t *pCcb, uint8_t oob, uint8_t display);
|
||||
|
||||
/* State machine interface type */
|
||||
typedef struct
|
||||
{
|
||||
smpTblEntry_t const * const *pStateTbl; /* Pointer to state table */
|
||||
smpAct_t const *pActionTbl; /* Pointer to action table */
|
||||
smpTblEntry_t const *pCommonTbl; /* Pointer to common action table */
|
||||
} smpSmIf_t;
|
||||
|
||||
/* SMP main control block */
|
||||
typedef struct
|
||||
{
|
||||
smpCcb_t ccb[DM_CONN_MAX]; /* Legacy Connection control blocks */
|
||||
smpSmIf_t const *pSlave; /* Slave state machine interface */
|
||||
smpSmIf_t const *pMaster; /* Master state machine interface */
|
||||
wsfHandlerId_t handlerId; /* WSF handler ID */
|
||||
smpProcPairing_t *procPairing; /* Pointer to process pairing function */
|
||||
smpProcAuthReq_t *procAuthReq; /* Pointer to process auth request function */
|
||||
bool_t lescSupported; /* TRUE if LE Secure Connections is supported */
|
||||
} smpCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* SMP packet length table */
|
||||
extern const uint8_t smpPktLenTbl[];
|
||||
|
||||
/* Control block */
|
||||
extern smpCb_t smpCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* utility functions from smp main */
|
||||
smpCcb_t *smpCcbByHandle(uint16_t handle);
|
||||
smpCcb_t *smpCcbByConnId(dmConnId_t connId);
|
||||
void smpCalcC1Part1(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pRand);
|
||||
void smpCalcC1Part2(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pPart1);
|
||||
void smpCalcS1(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pRand1, uint8_t *pRand2);
|
||||
void smpGenerateLtk(smpCcb_t *pCcb);
|
||||
void smpSendPkt(smpCcb_t *pCcb, uint8_t *pPkt);
|
||||
bool_t smpStateIdle(smpCcb_t *pCcb);
|
||||
void *smpMsgAlloc(uint16_t len);
|
||||
uint8_t smpGetScSecLevel(smpCcb_t *pCcb);
|
||||
|
||||
/* action functions and utility functions from smp act */
|
||||
void smpStartRspTimer(smpCcb_t *pCcb);
|
||||
void smpActNone(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpCleanup(smpCcb_t *pCcb);
|
||||
void smpActCleanup(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpSendPairingFailed(smpCcb_t *pCcb, uint8_t reason);
|
||||
void smpActPairingFailed(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActPairingCancel(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActStorePin(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
bool_t smpProcPairing(smpCcb_t *pCcb, uint8_t *pOob, uint8_t *pDisplay);
|
||||
void smpAuthReq(smpCcb_t *pCcb, uint8_t oob, uint8_t display);
|
||||
void smpActPairCnfCalc1(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActPairCnfCalc2(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActSendPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActPairCnfVerCalc1(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActPairCnfVerCalc2(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
bool_t smpSendKey(smpCcb_t *pCcb, uint8_t keyDist);
|
||||
bool_t smpProcRcvKey(smpCcb_t *pCcb, dmSecKeyIndEvt_t *pKeyInd, uint8_t *pBuf, uint8_t keyDist);
|
||||
void smpActMaxAttempts(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActAttemptRcvd(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActCheckAttempts(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActNotifyDmAttemptsFailure(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActNotifyDmRspToFailure(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActPairingCmpl(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpSmExecute(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
|
||||
/* diagnostic functions */
|
||||
uint8_t *smpEventStr(uint8_t eventId);
|
||||
uint8_t *smpStateStr(uint8_t state);
|
||||
|
||||
/* SMP DB functions */
|
||||
uint32_t SmpDbMaxAttemptReached(dmConnId_t connId);
|
||||
void SmpDbPairingFailed(dmConnId_t connId);
|
||||
uint32_t SmpDbGetPairingDisabledTime(dmConnId_t connId);
|
||||
void SmpDbService(void);
|
||||
uint8_t SmpDbGetFailureCount(dmConnId_t connId);
|
||||
void SmpDbSetFailureCount(dmConnId_t connId, uint8_t count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* SMP_MAIN_H */
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Simple implementation of SMP when not supported.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "dm_api.h"
|
||||
#include "l2c_api.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C control callback for SMP.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpNonL2cCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2CAP data callback for SMP when SMP not supported.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpNonL2cDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pRsp;
|
||||
uint8_t *p;
|
||||
uint8_t role;
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) == DM_CONN_ID_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
role = DmConnRole(connId);
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
|
||||
/* SMP is not supported so fail gracefully */
|
||||
|
||||
/* if slave and pairing request received, or master and security request received */
|
||||
if ((role == DM_ROLE_SLAVE && *p == SMP_CMD_PAIR_REQ) ||
|
||||
(role == DM_ROLE_MASTER && *p == SMP_CMD_SECURITY_REQ))
|
||||
{
|
||||
/* send pairing failed */
|
||||
if ((pRsp = smpMsgAlloc(L2C_PAYLOAD_START + SMP_PAIR_FAIL_LEN)) != NULL)
|
||||
{
|
||||
p = pRsp + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_FAIL);
|
||||
UINT8_TO_BSTREAM(p, SMP_ERR_PAIRING_NOT_SUP);
|
||||
|
||||
L2cDataReq(L2C_CID_SMP, handle, SMP_PAIR_FAIL_LEN, pRsp);
|
||||
}
|
||||
}
|
||||
|
||||
/* all other messages are ignored */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Use this SMP init function when SMP is not supported.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpNonInit(void)
|
||||
{
|
||||
/* Register with L2C */
|
||||
L2cRegister(L2C_CID_SMP, smpNonL2cDataCback, smpNonL2cCtrlCback);
|
||||
}
|
||||
Vendored
+1004
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user