initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
@@ -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
@@ -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), &params[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);
}
}
@@ -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
File diff suppressed because it is too large Load Diff
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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;
}
@@ -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 */
@@ -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
File diff suppressed because it is too large Load Diff
@@ -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;
}
@@ -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
@@ -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
@@ -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
@@ -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);
}
}
@@ -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);
}
}