diff options
Diffstat (limited to 'src/libs/mynewt-nimble/nimble/drivers')
16 files changed, 5583 insertions, 4 deletions
diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/README.md b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/README.md new file mode 100644 index 00000000..2e76e2c1 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/README.md @@ -0,0 +1,68 @@ +<!-- +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# +--> + +## How to run NimBLE controller on Dialog DA1469x + +Dialog DA1469x has separate Cortex-M0+ core inside CMAC hw block which can run +NimBLE controller. This means DA1469x can run full NimBLE stack: host is running +on M33 core while controller is running on M0+ core. Both communicate using +standard HCI H4 protocol exchanged over mailboxes located in shared memory. + +### Basic setup + +In order to run full NimBLE stack on DA1469x you will need two newt targets: one +for M33 (e.g. `dialog_da1469x-dk-pro` BSP) and one for M0+ (`dialog_cmac` BSP). + +Once everything is configured properly, you only need to build target for M33. +Target configured for M0+ will be build automatically and image is linked with +M33 image so everything can be flashed at once just as if there is only single +target used. + +Target for M33 should be set and configured as any other BLE application. In +order to use NimBLE controller on CMAC, set proper HCI transport via syscfg: + + BLE_HCI_TRANSPORT: dialog_cmac + +This will include proper transport, driver and add M0+ target to build process. + +For M0+, there is sample target provided in `targets/dialog_cmac` and it's used +by default unless overrided by syscfg in M33 target: + + CMAC_IMAGE_TARGET_NAME: "@apache-mynewt-nimble/targets/dialog_cmac" + +If you wish to create own target for M0+, make sure your target is set the same +way (`app`, `bsp` and `build_profile`) as sample. Also it is recommended to use +syscfg settings from sample target in new target. + +### NimBLE configuration + +Since host and controller are running on different cores, they both use separate +configuration: host configuration is in M33 target, controller configuration is +in M0+ target. There is currently no way to automatically synchronize both, so +care needs to be taken when enabling features in either of targets. + +A possible workaround is to use separate `.yml` file with all the NimBLE syscfg +values settings and include it in both targets using `$import` directive which +is supported by recent versions of `newt` tool. + +### Advanced settings + +(tbd) diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/include/ble/xcvr.h b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/include/ble/xcvr.h new file mode 100644 index 00000000..e4e741c5 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/include/ble/xcvr.h @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_XCVR_ +#define H_BLE_XCVR_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define XCVR_TX_SCHED_DELAY_USECS (250) + +/* + * Define HW whitelist size. This is the total possible whitelist size; + * not necessarily the size that will be used (may be smaller) + */ +#define BLE_HW_WHITE_LIST_SIZE (8) + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_XCVR_ */ diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/pkg.yml b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/pkg.yml new file mode 100644 index 00000000..9cf63ffc --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/pkg.yml @@ -0,0 +1,33 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/drivers/dialog_cmac +pkg.description: BLE driver for Dialog CMAC +pkg.author: "Apache Mynewt <dev@mynewt.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth +pkg.deps: + - "@apache-mynewt-nimble/nimble/controller" + - "@apache-mynewt-core/crypto/tinycrypt" +pkg.apis: + - ble_driver +pkg.req_apis: + - ble_transport diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw.c b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw.c new file mode 100644 index 00000000..98c8144b --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw.c @@ -0,0 +1,340 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <assert.h> +#include <stdint.h> +#include "mcu/mcu.h" +#include "nimble/ble.h" +#include "controller/ble_hw.h" +#include "CMAC.h" +#include "cmac_driver/cmac_shared.h" +#include "mcu/mcu.h" +#include "tinycrypt/aes.h" + +static struct tc_aes_key_sched_struct g_ctx; + +int +ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) +{ + cmac_rand_set_isr_cb(cb); + return 0; +} + +int +ble_hw_rng_start(void) +{ + /* Chime the M33 in case we need random numbers generated */ + cmac_rand_start(); + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_CMAC2SYS_IRQ_SET_Msk; + return 0; +} + +int +ble_hw_rng_stop(void) +{ + cmac_rand_stop(); + return 0; +} + +#define BLE_HW_RESOLV_LIST_SIZE (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE)) + +struct ble_hw_resolv_irk { + uint32_t key[4]; +}; + +struct ble_hw_resolv_list { + uint8_t count; + struct ble_hw_resolv_irk irk[BLE_HW_RESOLV_LIST_SIZE]; +}; + +struct ble_hw_resolv_proc { + uint32_t hash; + uint8_t f_configured; + uint8_t f_active; + uint8_t f_match; + uint8_t f_done; + struct ble_hw_resolv_irk *irk; + struct ble_hw_resolv_irk *irk_end; + uint32_t crypto_prand_in[4]; + uint32_t crypto_e_out[4]; +}; + +static struct ble_hw_resolv_list g_ble_hw_resolv_list; +static struct ble_hw_resolv_proc g_ble_hw_resolv_proc; + +int +ble_hw_get_public_addr(ble_addr_t *addr) +{ + return -1; +} + +int +ble_hw_get_static_addr(ble_addr_t *addr) +{ + return -1; +} + +void +ble_hw_whitelist_clear(void) +{ +} + +int +ble_hw_whitelist_add(const uint8_t *addr, uint8_t addr_type) +{ + return 0; +} + +void +ble_hw_whitelist_rmv(const uint8_t *addr, uint8_t addr_type) +{ +} + +uint8_t +ble_hw_whitelist_size(void) +{ + return 0; +} + +void +ble_hw_whitelist_enable(void) +{ +} + + +void +ble_hw_whitelist_disable(void) +{ +} + +int +ble_hw_whitelist_match(void) +{ + return 0; +} + +int +ble_hw_encrypt_block(struct ble_encryption_block *ecb) +{ + uint32_t in_addr; + uint32_t out_addr; + + /* + * The following code bears some explanation. This function is called by + * the LL task to encrypt blocks and calculate session keys. Address + * resolution also calls this function. Furthermore, during connections, + * the M0 crypto accelerator is used but this function is not called when + * using it. During the entire connection event, the M0 crypto block cannot + * be used as the crypto state (some of it) needs to remain un-changed. + * Note that this is also true when address resolution is enabled: the + * HW crypto block is set up and cannot be modified. + * + * Rather than attempt to share the M0 crypto block between the various + * controller features which require it, we decided to use software to + * perform the encryption task for anything being done at the link-layer + * (outside of an ISR). If this function is called inside an ISR, and it + * is when resolving addresses, the crypto accelerator is not being used + * by a connection event. Thus, we check to see if we are inside of an ISR. + * If so, we use the M0 crypto block. If outside of an ISR, we use the M33 + */ + if (!os_arch_in_isr()) { + tc_aes128_set_encrypt_key(&g_ctx, ecb->key); + tc_aes_encrypt(ecb->cipher_text, ecb->plain_text, &g_ctx); + return 0; + } + + /* Need to retain state of in/out pointers */ + in_addr = CMAC->CM_CRYPTO_IN_ADR2_REG; + out_addr = CMAC->CM_CRYPTO_OUT_ADR_REG; + + while (CMAC->CM_CRYPTO_STAT_REG & CMAC_CM_CRYPTO_STAT_REG_CM_CRYPTO_BUSY_Msk); + + /* RECB, memory in/out, encryption */ + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ECB_ENC_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_IN_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_OUT_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ENC_DECN_Msk; + + CMAC->CM_CRYPTO_KEY_31_0_REG = get_le32(&ecb->key[0]); + CMAC->CM_CRYPTO_KEY_63_32_REG = get_le32(&ecb->key[4]); + CMAC->CM_CRYPTO_KEY_95_64_REG = get_le32(&ecb->key[8]); + CMAC->CM_CRYPTO_KEY_127_96_REG = get_le32(&ecb->key[12]); + CMAC->CM_CRYPTO_IN_ADR2_REG = (uint32_t)ecb->plain_text; + CMAC->CM_CRYPTO_OUT_ADR_REG = (uint32_t)ecb->cipher_text; + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk; + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV_CRYPTO_START_Msk; + while (!(CMAC->CM_EXC_STAT_REG & CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk)); + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk; + + CMAC->CM_CRYPTO_IN_ADR2_REG = in_addr; + CMAC->CM_CRYPTO_OUT_ADR_REG = out_addr; + + return 0; +} + +void +ble_hw_resolv_list_clear(void) +{ + g_ble_hw_resolv_list.count = 0; +} + +int +ble_hw_resolv_list_add(uint8_t *irk) +{ + struct ble_hw_resolv_irk *e; + + if (g_ble_hw_resolv_list.count == BLE_HW_RESOLV_LIST_SIZE) { + return BLE_ERR_MEM_CAPACITY; + } + + e = &g_ble_hw_resolv_list.irk[g_ble_hw_resolv_list.count]; + /* Prepare key here so we do not need to do it during resolving */ + e->key[0] = get_le32(&irk[0]); + e->key[1] = get_le32(&irk[4]); + e->key[2] = get_le32(&irk[8]); + e->key[3] = get_le32(&irk[12]); + + g_ble_hw_resolv_list.count++; + + return BLE_ERR_SUCCESS; +} + +void +ble_hw_resolv_list_rmv(int index) +{ + struct ble_hw_resolv_irk *e; + + if (index < g_ble_hw_resolv_list.count) { + g_ble_hw_resolv_list.count--; + + e = &g_ble_hw_resolv_list.irk[index]; + memmove(e, e + 1, (g_ble_hw_resolv_list.count - index) * sizeof(e->key)); + } +} + +uint8_t +ble_hw_resolv_list_size(void) +{ + return BLE_HW_RESOLV_LIST_SIZE; +} + +int +ble_hw_resolv_list_match(void) +{ + return g_ble_hw_resolv_proc.f_match ? + g_ble_hw_resolv_proc.irk - g_ble_hw_resolv_list.irk : -1; +} + +static void +ble_hw_resolv_proc_next(void) +{ + void *src = &g_ble_hw_resolv_proc.irk->key; + + if (g_ble_hw_resolv_proc.irk == g_ble_hw_resolv_proc.irk_end) { + g_ble_hw_resolv_proc.f_done = 1; + g_ble_hw_resolv_proc.f_active = 0; + } else { + __asm__ volatile (".syntax unified \n" + " ldm %[ptr]!, {r1, r2, r3, r4} \n" + " ldr %[ptr], =%[reg] \n" + " stm %[ptr]!, {r1, r2, r3, r4} \n" + : [ptr] "+l" (src) + : [reg] "i" (&CMAC->CM_CRYPTO_KEY_31_0_REG) + : "r1", "r2", "r3", "r4", "memory"); + + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV_CRYPTO_START_Msk; + } +} + +void +ble_hw_resolv_proc_enable(void) +{ + assert(!g_ble_hw_resolv_proc.f_active); + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_ABORT_Msk; + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ECB_ENC_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_IN_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_OUT_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ENC_DECN_Msk; + + CMAC->CM_CRYPTO_IN_ADR2_REG = (uint32_t)g_ble_hw_resolv_proc.crypto_prand_in; + CMAC->CM_CRYPTO_OUT_ADR_REG = (uint32_t)g_ble_hw_resolv_proc.crypto_e_out; + + g_ble_hw_resolv_proc.irk = g_ble_hw_resolv_list.irk; + g_ble_hw_resolv_proc.irk_end = g_ble_hw_resolv_list.irk + + g_ble_hw_resolv_list.count; + g_ble_hw_resolv_proc.f_configured = 1; + g_ble_hw_resolv_proc.f_active = 0; + + /* + * It would be better to enable IRQ in ble_hw_resolv_proc_start, but this + * would introduce a bit of latency when starting resolving procedure and + * we need to save every us possible there in order to be able to resolve + * RPA on time. + */ + NVIC_ClearPendingIRQ(CRYPTO_IRQn); + NVIC_EnableIRQ(CRYPTO_IRQn); +} + +void +ble_hw_resolv_proc_disable(void) +{ + g_ble_hw_resolv_proc.f_configured = 0; + g_ble_hw_resolv_proc.f_active = 0; + g_ble_hw_resolv_proc.f_match = 0; + g_ble_hw_resolv_proc.f_done = 1; + + NVIC_DisableIRQ(CRYPTO_IRQn); +} + +void +ble_hw_resolv_proc_start(const uint8_t *addr) +{ + assert(g_ble_hw_resolv_proc.f_configured); + + /* crypto_prand_in is already zeroed so prand is properly padded */ + g_ble_hw_resolv_proc.crypto_prand_in[3] = get_be24(&addr[3]) << 8; + g_ble_hw_resolv_proc.hash = get_be24(&addr[0]); + + g_ble_hw_resolv_proc.f_match = 0; + g_ble_hw_resolv_proc.f_done = 0; + g_ble_hw_resolv_proc.f_active = 1; + + ble_hw_resolv_proc_next(); +} + +void +CRYPTO_IRQHandler(void) +{ + uint32_t hash; + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk; + + hash = g_ble_hw_resolv_proc.crypto_e_out[3] >> 8; + if (g_ble_hw_resolv_proc.hash == hash) { + g_ble_hw_resolv_proc.f_active = 0; + g_ble_hw_resolv_proc.f_match = 1; + g_ble_hw_resolv_proc.f_done = 1; + } else { + g_ble_hw_resolv_proc.irk++; + ble_hw_resolv_proc_next(); + } +} diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw_priv.h b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw_priv.h new file mode 100644 index 00000000..627994ff --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw_priv.h @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _BLE_HW_PRIV_H_ +#define _BLE_HW_PRIV_H_ + +#include <stdint.h> + +void ble_hw_resolv_proc_enable(void); +void ble_hw_resolv_proc_disable(void); +void ble_hw_resolv_proc_start(const uint8_t *addr); + +#endif /* _BLE_HW_PRIV_H_ */ diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_phy.c b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_phy.c new file mode 100644 index 00000000..d5767c56 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_phy.c @@ -0,0 +1,1798 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "syscfg/syscfg.h" +#if !MYNEWT_VAL(BLE_PHY_DEBUG_DSER) +#define MCU_DIAG_SER_DISABLE +#endif + +#include <assert.h> +#include <stdint.h> +#include <assert.h> +#include "nimble/ble.h" +#include "mcu/mcu.h" +#include "mcu/cmac_timer.h" +#include "cmac_driver/cmac_shared.h" +#include "controller/ble_phy.h" +#include "controller/ble_ll.h" +#include "stats/stats.h" +#include "CMAC.h" +#include "ble_hw_priv.h" +#include "ble_rf_priv.h" + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) +#error LE Coded PHY cannot be enabled on DA1469x +#endif + +/* Statistics */ +STATS_SECT_START(ble_phy_stats) + STATS_SECT_ENTRY(phy_isrs) + STATS_SECT_ENTRY(tx_good) + STATS_SECT_ENTRY(tx_fail) + STATS_SECT_ENTRY(tx_late) + STATS_SECT_ENTRY(tx_bytes) + STATS_SECT_ENTRY(rx_starts) + STATS_SECT_ENTRY(rx_aborts) + STATS_SECT_ENTRY(rx_valid) + STATS_SECT_ENTRY(rx_crc_err) + STATS_SECT_ENTRY(rx_late) + STATS_SECT_ENTRY(radio_state_errs) + STATS_SECT_ENTRY(rx_hw_err) + STATS_SECT_ENTRY(tx_hw_err) +STATS_SECT_END +STATS_SECT_DECL(ble_phy_stats) ble_phy_stats; + +STATS_NAME_START(ble_phy_stats) + STATS_NAME(ble_phy_stats, phy_isrs) + STATS_NAME(ble_phy_stats, tx_good) + STATS_NAME(ble_phy_stats, tx_fail) + STATS_NAME(ble_phy_stats, tx_late) + STATS_NAME(ble_phy_stats, tx_bytes) + STATS_NAME(ble_phy_stats, rx_starts) + STATS_NAME(ble_phy_stats, rx_aborts) + STATS_NAME(ble_phy_stats, rx_valid) + STATS_NAME(ble_phy_stats, rx_crc_err) + STATS_NAME(ble_phy_stats, rx_late) + STATS_NAME(ble_phy_stats, radio_state_errs) + STATS_NAME(ble_phy_stats, rx_hw_err) + STATS_NAME(ble_phy_stats, tx_hw_err) +STATS_NAME_END(ble_phy_stats) + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) +#error LE Coded PHY is not supported +#endif + +/* An easy way to get and set bit field value in CMAC registers */ +#define CMAC_SETREGF(_reg, _field, _val) \ + CMAC->_reg = (CMAC->_reg & ~(CMAC_ ## _reg ## _ ## _field ## _Msk)) | \ + ((_val) << (CMAC_ ## _reg ## _ ## _field ## _Pos)); +#define CMAC_GETREGF(_reg, _field) \ + (CMAC->_reg & (CMAC_ ## _reg ## _ ## _field ## _Msk)) >> \ + (CMAC_ ## _reg ## _ ## _field ## _Pos) + +/* Definitions for fields queue */ +#define FIELD_DATA_REG_DMA_TX(_offset, _len) \ + ((uint32_t)&g_ble_phy_tx_buf[(_offset)] & 0x3ffff) | ((_len) << 20) +#define FIELD_DATA_REG_DMA_RX(_offset, _len) \ + ((uint32_t)&g_ble_phy_rx_buf[(_offset)] & 0x3ffff) | ((_len) << 20) + +#if MYNEWT_VAL(BLE_LL_DTM) +#define PHY_WHITENING (g_ble_phy_data.phy_whitening) +#else +#define PHY_WHITENING (1) +#endif + +#define FIELD_CTRL_REG_TX_PREAMBLE \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_TX_DATA_SRC_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) | \ + (g_ble_phy_data.phy_mode_pre_len << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_SIZE_M1_Pos) +#define FIELD_CTRL_REG_TX_ACCESS_ADDR \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EXC_ON_EXP_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_TX_DATA_SRC_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) | \ + (31 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_SIZE_M1_Pos) +#define FIELD_CTRL_REG_TX_PAYLOAD \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (PHY_WHITENING << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_MEM_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos); +#define FIELD_CTRL_REG_TX_ENC_PAYLOAD \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_CRYPTO_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) +#define FIELD_CTRL_REG_TX_MIC \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_CRYPTO_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) +#define FIELD_CTRL_REG_TX_CRC \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (PHY_WHITENING << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_TX_DATA_SRC_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_LAST_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_MSB_FIRST_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) | \ + (23 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_SIZE_M1_Pos) +#define FIELD_CTRL_REG_RX_ACCESS_ADDR \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_TX_DATA_SRC_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CORR_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) | \ + (31 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_SIZE_M1_Pos) +#define FIELD_CTRL_REG_RX_HEADER \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EXC_ON_EXP_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (PHY_WHITENING << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_MEM_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) +#define FIELD_CTRL_REG_RX_CRC \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (PHY_WHITENING << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_LAST_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_MSB_FIRST_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_MEM_Pos) +#define FIELD_CTRL_REG_RX_PAYLOAD \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (PHY_WHITENING << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_MEM_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) +#define FIELD_CTRL_REG_RX_PAYLOAD_WITH_EXC \ + FIELD_CTRL_REG_RX_PAYLOAD | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EXC_ON_EXP_Pos) +#define FIELD_CTRL_REG_RX_ENC_PAYLOAD \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_CRYPTO_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) + +/* RF power up/down delays */ +#define PHY_DELAY_POWER_DN_RX (23) +#define PHY_DELAY_POWER_DN_TX (23) +#define PHY_DELAY_POWER_UP_RX (90) +#define PHY_DELAY_POWER_UP_TX (75) +#define PHY_DELAY_TX_RX ((PHY_DELAY_POWER_DN_TX) + (PHY_DELAY_POWER_UP_RX)) +#define PHY_DELAY_RX_TX ((PHY_DELAY_POWER_DN_RX) + (PHY_DELAY_POWER_UP_TX)) + +/* RF TX/RX path delays */ +static const uint8_t g_ble_phy_path_delay_tx[2] = { + 4, /* 1M = 3.8us */ + 0, /* 2M = 0.2us */ +}; +static const uint8_t g_ble_phy_path_delay_rx[2] = { + 2, /* 1M = 2.2us */ + 1, /* 2M = 0.8us */ +}; + +/* Measured and pre-calculated offsets for transitions */ +static const uint8_t g_ble_phy_frame_offset_txrx[4] = { + ((BLE_LL_IFS) - (PHY_DELAY_TX_RX) + (4)), /* 2M/1M */ + ((BLE_LL_IFS) - (PHY_DELAY_TX_RX) + (5)), /* 1M/1M */ + ((BLE_LL_IFS) - (PHY_DELAY_TX_RX) + (4)), /* 2M/2M */ + ((BLE_LL_IFS) - (PHY_DELAY_TX_RX) + (5)), /* 1M/2M */ +}; +static const uint8_t g_ble_phy_frame_offset_rxtx[4] = { + ((BLE_LL_IFS) - (PHY_DELAY_RX_TX) - (5)), /* 2M/1M */ + ((BLE_LL_IFS) - (PHY_DELAY_RX_TX) - (6)), /* 1M/1M */ + ((BLE_LL_IFS) - (PHY_DELAY_RX_TX) - (3)), /* 2M/2M */ + ((BLE_LL_IFS) - (PHY_DELAY_RX_TX) - (5)), /* 1M/2M */ +}; + +/* packet start offsets (in usecs) */ +static const uint16_t g_ble_phy_mode_pkt_start_off[BLE_PHY_NUM_MODE] = { 376, 40, 24, 376 }; + +struct ble_phy_data { + uint8_t phy_state; /* Current state */ + uint8_t channel; /* Current PHY channel */ + uint8_t phy_mode_cur; /* Current PHY mode */ + uint8_t phy_mode_tx; /* TX PHY mode */ + uint8_t phy_mode_rx; /* RX PHY mode */ + uint8_t phy_mode_pre_len; /* Preamble length - 1 */ + uint8_t phy_mode_evpsym; /* EVPSYMBOL_LUT value for fields */ + uint8_t end_transition; /* Scheduled transition */ + uint8_t path_delay_tx; + uint8_t path_delay_rx; + uint8_t frame_offset_txrx; + uint8_t frame_offset_rxtx; + uint8_t phy_rx_started; + uint8_t phy_encrypted; + uint8_t phy_privacy; +#if MYNEWT_VAL(BLE_LL_DTM) + uint8_t phy_whitening; /* Whitening state (disabled for DTM) */ +#endif + uint32_t access_addr; /* Current access address */ + uint32_t crc_init; + uint32_t llt_at_cputime; + uint32_t cputime_at_llt; + uint64_t start_llt; + struct ble_mbuf_hdr rxhdr; + ble_phy_tx_end_func txend_cb; + void *txend_arg; +}; + +static struct ble_phy_data g_ble_phy_data; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/* Encryption related variables */ +struct ble_phy_encrypt_obj { + uint8_t key[16]; + uint8_t b0[16]; + uint8_t b1[16]; + uint8_t ai[16]; +}; + +struct ble_phy_encrypt_obj g_ble_phy_encrypt_data; + +static void ble_phy_tx_enc_start(void); +static void ble_phy_rx_enc_start(uint8_t len); +#endif + +#define SW_MAC_EXC_NONE (0) +#define SW_MAC_EXC_LL_RX_END (1) +#define SW_MAC_EXC_TXEND_CB (2) +#define SW_MAC_EXC_LL_RX_START (3) +#define SW_MAC_EXC_WFR_TIMER_EXP (4) + +static volatile uint8_t g_sw_mac_exc; + +/* Channel index to RF channel mapping */ +static const uint8_t g_ble_phy_chan_to_rf[BLE_PHY_NUM_CHANS] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, /* 0-9 */ + 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, /* 10-19 */ + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 20-29 */ + 32, 33, 34, 35, 36, 37, 38, 0, 12, 39, /* 30-39 */ +}; + +__attribute__((aligned(4))) +static uint8_t g_ble_phy_tx_buf[BLE_PHY_MAX_PDU_LEN + 3]; +__attribute__((aligned(4))) +static uint8_t g_ble_phy_rx_buf[BLE_PHY_MAX_PDU_LEN + 3]; + +static void ble_phy_irq_field_tx(void); +static void ble_phy_irq_field_rx(void); +static void ble_phy_irq_frame_tx(void); +static void ble_phy_irq_frame_rx(void); +static bool ble_phy_rx_start_isr(void); +static void ble_phy_rx_setup_fields(void); +static void ble_phy_rx_setup_xcvr(void); +static void ble_phy_mode_apply(uint8_t phy_mode); + +void +FIELD_IRQHandler(void) +{ + MCU_DIAG_SER('E'); + + switch (g_ble_phy_data.phy_state) { + case BLE_PHY_STATE_TX: + ble_phy_irq_field_tx(); + break; + case BLE_PHY_STATE_RX: + ble_phy_irq_field_rx(); + break; + default: + STATS_INC(ble_phy_stats, radio_state_errs); + CMAC->CM_EXC_STAT_REG = 0xfffffffe; + break; + } + + MCU_DIAG_SER('e'); +} + +void +CALLBACK_IRQHandler(void) +{ + MCU_DIAG_SER('C'); + + /* XXX: clear these for now. */ + (void)CMAC->CM_BS_SMPL_D_REG; + + /* Clear IRQ*/ + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_CALLBACK_VALID_CLR_Msk; + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk; + + /* + * Program next frame for transition to TX. CM_EV_LINKUP_REG register to + * enable actual transition can be set later, we just need to make sure 2nd + * frame is already set before current frame is finished - this guarantees + * that frame for transition will be moved to 1st frame once current frame + * is popped off the queue. + */ + CMAC->CM_FRAME_2_REG = CMAC_CM_FRAME_2_REG_FRAME_VALID_Msk | + CMAC_CM_FRAME_2_REG_FRAME_TX_Msk | + CMAC_CM_FRAME_2_REG_FRAME_EXC_ON_BS_START_Msk | + ((g_ble_phy_data.frame_offset_rxtx) << + CMAC_CM_FRAME_2_REG_FRAME_START_OFFSET_Pos); + + /* + * We just got an access address match so do this as early as possible + * to save time in the field rx isr. + */ + ble_phy_rx_start_isr(); + + MCU_DIAG_SER('c'); +} + +void +FRAME_IRQHandler(void) +{ + MCU_DIAG_SER('F'); + + switch (g_ble_phy_data.phy_state) { + case BLE_PHY_STATE_TX: + ble_phy_irq_frame_tx(); + break; + case BLE_PHY_STATE_RX: + ble_phy_irq_frame_rx(); + break; + default: + STATS_INC(ble_phy_stats, radio_state_errs); + CMAC->CM_EXC_STAT_REG = 0xfffffffe; + break; + } + + MCU_DIAG_SER('f'); +} + +void +SW_MAC_IRQHandler(void) +{ + uint8_t exc; + int rc; + + MCU_DIAG_SER('S'); + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_SW_MAC_Msk; + assert(g_sw_mac_exc); + + exc = g_sw_mac_exc; + g_sw_mac_exc = 0; + + MCU_DIAG_SER('0' + exc); + + /* Next SW_MAC handover can now be queued */ + os_arch_cmac_bs_ctrl_irq_unblock(); + + switch (exc) { + case SW_MAC_EXC_TXEND_CB: + assert(g_ble_phy_data.txend_cb); + g_ble_phy_data.txend_cb(g_ble_phy_data.txend_arg); + break; + case SW_MAC_EXC_WFR_TIMER_EXP: + ble_ll_wfr_timer_exp(NULL); + break; + case SW_MAC_EXC_LL_RX_START: + /* Call Link Layer receive start function */ + rc = ble_ll_rx_start(&g_ble_phy_rx_buf[0], g_ble_phy_data.channel, + &g_ble_phy_data.rxhdr); + if (rc == 0) { + /* Set rx started flag and enable rx end ISR */ + g_ble_phy_data.phy_rx_started = 1; + + /* No transition */ + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_PHY_TO_IDLE_2_NONE_Msk | + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_NONE_Msk; + } else if (rc > 0) { + g_ble_phy_data.phy_rx_started = 1; + + /* Setup transition */ + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_PHY_TO_IDLE_2_EXC_Msk | + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_PHY_TO_IDLE_Msk; + } else { + /* Disable PHY */ + ble_phy_disable(); + STATS_INC(ble_phy_stats, rx_aborts); + } + break; + case SW_MAC_EXC_LL_RX_END: + /* Call LL end processing */ + rc = ble_ll_rx_end(&g_ble_phy_rx_buf[0], &g_ble_phy_data.rxhdr); + if (rc < 0) { + ble_phy_disable(); + } + break; + default: + assert(0); + break; + } + + MCU_DIAG_SER('s'); +} + +static inline uint32_t +ble_phy_convert_and_record_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + uint64_t ll_val; + + ll_val = cmac_timer_convert_hal2llt(cputime); + + /* + * Since we just converted cputime to the LL timer, record both these + * values as they will be used to calculate packet reception start time. + */ + g_ble_phy_data.cputime_at_llt = cputime; + g_ble_phy_data.llt_at_cputime = ll_val; + g_ble_phy_data.start_llt = ll_val + rem_usecs; + + return ll_val; +} + +static inline void +ble_phy_sw_mac_handover(uint8_t exc) +{ + assert(!g_sw_mac_exc); + + g_sw_mac_exc = exc; + + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_SW_MAC_Msk; + + /* + * We want SW_MAC to be fired just after BS_CTRL interrupt so we block + * BS_CTRL temporarily and SW_MAC is next in order of interrupts priority. + */ + os_arch_cmac_bs_ctrl_irq_block(); +} + +static void +ble_phy_rx_end_isr(void) +{ + struct ble_mbuf_hdr *ble_hdr; + + /* XXX just clear captured timer for now. Handle rx end time */ + (void)CMAC->CM_TS1_REG; + + /* Set RSSI and CRC status flag in header */ + ble_hdr = &g_ble_phy_data.rxhdr; + + /* Count PHY crc errors and valid packets */ + if (CMAC->CM_CRC_REG != 0) { + STATS_INC(ble_phy_stats, rx_crc_err); + } else { + STATS_INC(ble_phy_stats, rx_valid); + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + /* Only set MIC failure flag if frame is not zero length */ + if (g_ble_phy_rx_buf[1] != 0) { + if (CMAC->CM_CRYPTO_STAT_REG != 0) { + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE; + } else { + g_ble_phy_rx_buf[1] = g_ble_phy_rx_buf[1] - 4; + } + } + } +#endif + } + + ble_phy_sw_mac_handover(SW_MAC_EXC_LL_RX_END); +} + +static bool +ble_phy_rx_start_isr(void) +{ + uint32_t llt32; + uint32_t llt_10_0; + uint32_t llt_10_0_mask; + uint32_t timestamp; + uint32_t ticks; + uint32_t usecs; + struct ble_mbuf_hdr *ble_hdr; + + /* Initialize the ble mbuf header */ + ble_hdr = &g_ble_phy_data.rxhdr; + ble_hdr->rxinfo.flags = ble_ll_state_get(); + ble_hdr->rxinfo.channel = g_ble_phy_data.channel; + ble_hdr->rxinfo.handle = 0; + ble_hdr->rxinfo.phy = ble_phy_get_cur_phy(); + ble_hdr->rxinfo.phy_mode = g_ble_phy_data.phy_mode_rx; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_hdr->rxinfo.user_data = NULL; +#endif + + /* Read the latched RSSI value */ + ble_hdr->rxinfo.rssi = ble_rf_get_rssi(); +#if MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE) + g_cmac_shared_data.debug.last_rx_rssi = ble_hdr->rxinfo.rssi; +#endif + + /* Count rx starts */ + STATS_INC(ble_phy_stats, rx_starts); + + /* + * Calculate packet start time. Note that we have only received the + * access address at this point but we should have the 1st symbol and + * thus the timestamp should be set (this is based on looking at the diag + * signals). For now, lets make sure that the dirty bit is set. The + * dirty bit means that the timestamp was set since the last time cleared. + * Note that we need to read the timestamp first to guarantee it was set + * before reading the LL timer. + */ + timestamp = CMAC->CM_TS1_REG; + assert((timestamp & CMAC_CM_TS1_REG_TS1_DIRTY_Msk) != 0); + + /* Get the LL timer (only need 32 bits) */ + llt32 = cmac_timer_read32(); + + /* + * We assume that the timestamp was set within 11 bits, or 2047 usecs, of + * when we read the ll timer. We assume this because we need to calculate + * the LL timer value at the timestamp. If the low 11 bits of the LL timer + * are greater than the timestamp, it means that the upper bits of the + * timestamp are correct. If the timestamp value is greater, it means the + * timer wrapped the 11 bits and we need to adjust the LL timer value. + */ + llt_10_0_mask = (CMAC_CM_TS1_REG_TS1_TIMER1_9_0_Msk | + CMAC_CM_TS1_REG_TS1_TIMER1_10_Msk); + timestamp &= llt_10_0_mask; + llt_10_0 = llt32 & llt_10_0_mask; + llt32 &= ~llt_10_0_mask; + if (timestamp > llt_10_0) { + llt32 -= 2048; + } + llt32 |= timestamp; + + /* Actual RX start time needs to account for preamble and access address */ + llt32 -= g_ble_phy_mode_pkt_start_off[g_ble_phy_data.phy_mode_rx] + + g_ble_phy_data.path_delay_rx; + + if (llt32 < g_ble_phy_data.llt_at_cputime) { + g_ble_phy_data.llt_at_cputime -= 31; + g_ble_phy_data.cputime_at_llt--; + } + + /* + * We now have the LL timer when the packet was received. Get the cputime + * and the leftover usecs. + */ + usecs = llt32 - g_ble_phy_data.llt_at_cputime; + ticks = os_cputime_usecs_to_ticks(usecs); + ble_hdr->beg_cputime = g_ble_phy_data.cputime_at_llt + ticks; + ble_hdr->rem_usecs = usecs - os_cputime_ticks_to_usecs(ticks); + + return true; +} + +static void +ble_phy_irq_field_tx_exc_bs_start_4this(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_BS_START_4THIS_Msk; + + if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TX_RX) { + /* + * Setup 2nd frame that will start after current one. + * -2us offset to adjust for allowed active clock accuracy. + */ + CMAC->CM_FRAME_2_REG = CMAC_CM_FRAME_2_REG_FRAME_VALID_Msk | + (((g_ble_phy_data.frame_offset_txrx) - 2) << + CMAC_CM_FRAME_2_REG_FRAME_START_OFFSET_Pos); + + /* Next frame starts automatically on phy2idle */ + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_PHY_TO_IDLE_2_EXC_Msk | + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_PHY_TO_IDLE_Msk; + + ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_TXRX, g_ble_phy_data.phy_mode_rx, 0); + } else { + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_PHY_TO_IDLE_2_EXC_Msk | + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_NONE_Msk; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted && (g_ble_phy_tx_buf[1] != 0)) { + ble_phy_tx_enc_start(); + } +#endif +} + +static void +ble_phy_irq_field_tx_exc_field_on_thr_exp(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk; + (void)CMAC->CM_TS1_REG; + + /* Set up remaining field (CRC) */ + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_CRC; +} + +static void +ble_phy_irq_field_tx(void) +{ + uint32_t stat; + + stat = CMAC->CM_EXC_STAT_REG; + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_BS_START_4THIS_Msk) { + MCU_DIAG_SER('6'); + ble_phy_irq_field_tx_exc_bs_start_4this(); + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk) { + MCU_DIAG_SER('7'); + ble_phy_irq_field_tx_exc_field_on_thr_exp(); + } +} + +static void +ble_phy_irq_frame_tx_exc_bs_stop(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk; + + /* Clear latched timestamp so we do not have error on next frame */ + (void)CMAC->CM_TS1_REG; + + if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TX_RX) { +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_mode_rx); +#endif + ble_phy_rx_setup_fields(); + } else { + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_PHY_TO_IDLE_2_EXC_Msk | + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_NONE_Msk; + } + + if (g_ble_phy_data.txend_cb) { + ble_phy_sw_mac_handover(SW_MAC_EXC_TXEND_CB); + return; + } +} + +static void +ble_phy_irq_frame_tx_exc_phy_to_idle_4this(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk; + + if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TX_RX) { + ble_phy_rx_setup_xcvr(); + + g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; + } else { + /* + * Disable explicitly in case RX-TX was done (we cannot setup for auto + * disable in such case) */ + ble_rf_stop(); + + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; + } + + g_ble_phy_data.end_transition = BLE_PHY_TRANSITION_NONE; +} + +static void +ble_phy_irq_frame_tx(void) +{ + uint32_t stat; + + stat = CMAC->CM_EXC_STAT_REG; + + /* + * In case of phy2idle this should be first and only exception we handle + * here. This is because in case of TX-RX transition frame_start will occur + * at the same as phy2idle so we will have 2 exceptions here. To handle this + * properly we first need to handle phy2idle in TX state and keep frame_start + * pending so it will be called again in RX state. + */ + if (stat & CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk) { + MCU_DIAG_SER('6'); + ble_phy_irq_frame_tx_exc_phy_to_idle_4this(); + return; + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk) { + MCU_DIAG_SER('7'); + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk; + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk) { + MCU_DIAG_SER('8'); + ble_phy_irq_frame_tx_exc_bs_stop(); + } +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +static void +ble_phy_field_rx_encrypted(uint32_t len) +{ + if (len) { + /* + * An encrypted frame should have a minimum length of 5 + * bytes (at least one for payload and 4 for MIC). If the + * length is less than 5 this frame is bogus and will most + * likely fail CRC. We still need to process this frame + * though as we need to call the handover function with + * the frame. If this happens we will not bother to + * run the remaining bytes through the accelerator; just + * process them like normal and generate (a hopefully + * incorrect) CRC. + */ + if (len >= 5) { + /* Start the crypto accelerator */ + ble_phy_rx_enc_start(len); + + /* + * We have already processed one byte; process remaining + * payload and MIC. Note: length contains MIC. + */ + len -= 2; + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4, len); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_ENC_PAYLOAD; + + /* CRC */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4 + len, 3); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_CRC; + } else { + /* We have processed one byte so far. Send remaining + payload bytes to normal rx payload processing */ + len -= 2; + if (len) { + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4, len); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_PAYLOAD; + } + + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4 + len, 3); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_CRC; + + /* Clear crypto pre-buffer */ + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_PBUF_CLR_Msk; + } + } else { + /* We programmed one byte, so get next two bytes for CRC */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4, 1); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_CRC; + } +} +#endif + +static void +ble_phy_field_rx_unencrypted(uint32_t len) +{ + uint8_t pduhdr; + uint8_t adva_thr; + + if (len) { + pduhdr = g_ble_phy_rx_buf[0]; + adva_thr = 0; + + /* + * Setup interrupt after AdvA to start address resolving if + * privacy is enabled and TxAdd bit is set. + */ + if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) && + g_ble_phy_data.phy_privacy && (pduhdr & 0x40)) { + + /* + * For legacy advertising AdvA ends at 6th byte. + * For extended advertising AdvA ends at 8th byte. + * We already programmed 2 bytes of payload so need + * to adjust threshold accordingly or just reset it + * in case there is not enough bytes in PDU to fit AdvA. + */ + adva_thr = (pduhdr & 0x0f) == 0x07 ? 6 : 4; + if (len >= adva_thr + 2) { + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4, adva_thr); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_PAYLOAD_WITH_EXC; + } else { + adva_thr = 0; + } + } + + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4 + adva_thr, + len - adva_thr); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_PAYLOAD; + } + + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4 + len, 1); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_CRC; +} + +static void +ble_phy_irq_field_rx_exc_field_on_thr_exp(void) +{ + uint32_t len; + uint32_t smpl; + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk; + + smpl = CMAC->CM_BS_SMPL_ST_REG; + + if ((smpl & CMAC_CM_BS_SMPL_ST_REG_FIELD_CNT_LATCHED_Msk) == 1) { + assert(g_ble_phy_data.phy_rx_started == 0); + + /* Clear this */ + (void)CMAC->CM_TS1_REG; + + /* Read length of frame */ + len = CMAC->CM_BS_SMPL_D_REG; + len = len & 0xFF; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + ble_phy_field_rx_encrypted(len); + } else { + ble_phy_field_rx_unencrypted(len); + } +#else + ble_phy_field_rx_unencrypted(len); +#endif + + ble_phy_sw_mac_handover(SW_MAC_EXC_LL_RX_START); + } else if ((smpl & CMAC_CM_BS_SMPL_ST_REG_FIELD_CNT_LATCHED_Msk) == 3) { + (void)CMAC->CM_BS_SMPL_D_REG; + + assert(g_ble_phy_data.phy_privacy); + + /* + * Resolve only if RPA is received. AdvA is at different offset + * in ExtAdv PDU. TxAdd was already checked before programming + * field threshold. + */ + if ((g_ble_phy_rx_buf[0] & 0x0f) == 0x07) { + if ((g_ble_phy_rx_buf[9] & 0xc0) == 0x40) { + ble_hw_resolv_proc_start(&g_ble_phy_rx_buf[4]); + } + } else { + if ((g_ble_phy_rx_buf[7] & 0xc0) == 0x40) { + ble_hw_resolv_proc_start(&g_ble_phy_rx_buf[2]); + } + } + } else { + assert(0); + } +} + +static void +ble_phy_irq_field_rx_exc_stat_reg_exc_corr_timeout(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CORR_TIMEOUT_Msk; + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk; + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_NONE_Msk; + + ble_phy_sw_mac_handover(SW_MAC_EXC_WFR_TIMER_EXP); +} + +static void +ble_phy_irq_field_rx(void) +{ + uint32_t stat; + + stat = CMAC->CM_EXC_STAT_REG; + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk) { + MCU_DIAG_SER('1'); + ble_phy_irq_field_rx_exc_field_on_thr_exp(); + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_CORR_TIMEOUT_Msk) { + MCU_DIAG_SER('2'); + ble_phy_irq_field_rx_exc_stat_reg_exc_corr_timeout(); + } +} + +static void +ble_phy_irq_frame_rx_exc_phy_to_idle_4this(void) +{ + uint8_t rf_chan; + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk; + + /* We are here only on transition, so switch to TX */ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_mode_tx); +#endif + rf_chan = g_ble_phy_chan_to_rf[g_ble_phy_data.channel]; + ble_rf_setup_tx(rf_chan, g_ble_phy_data.phy_mode_tx); + g_ble_phy_data.phy_state = BLE_PHY_STATE_TX; +} + +static void +ble_phy_irq_frame_rx_exc_frame_start(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk; +} + +static void +ble_phy_irq_frame_rx_exc_bs_stop(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk; + ble_phy_rx_end_isr(); +} + +static void +ble_phy_irq_frame_rx(void) +{ + uint32_t stat; + + stat = CMAC->CM_EXC_STAT_REG; + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk) { + MCU_DIAG_SER('3'); + ble_phy_irq_frame_rx_exc_phy_to_idle_4this(); + return; + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk) { + MCU_DIAG_SER('1'); + ble_phy_irq_frame_rx_exc_frame_start(); + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk) { + MCU_DIAG_SER('2'); + ble_phy_irq_frame_rx_exc_bs_stop(); + } +} + +static void +ble_phy_mode_apply(uint8_t phy_mode) +{ + if (phy_mode == g_ble_phy_data.phy_mode_cur) { + return; + } + + switch (phy_mode) { + case BLE_PHY_MODE_1M: +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_CLK_MODE, 3); +#endif + CMAC_SETREGF(CM_PHY_CTRL2_REG, PHY_MODE, 1); + g_ble_phy_data.phy_mode_evpsym = 1; /* 1000 ns per symbol */ + g_ble_phy_data.phy_mode_pre_len = 7; + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case BLE_PHY_MODE_2M: +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_CLK_MODE, 2); +#endif + CMAC_SETREGF(CM_PHY_CTRL2_REG, PHY_MODE, 0); + g_ble_phy_data.phy_mode_evpsym = 0; /* 500 ns per symbol */ + g_ble_phy_data.phy_mode_pre_len = 15; + break; +#endif + default: + assert(0); + return; + } + + g_ble_phy_data.phy_mode_cur = phy_mode; +} + +void +ble_phy_mode_set(uint8_t tx_phy_mode, uint8_t rx_phy_mode) +{ + uint8_t txrx; + uint8_t rxtx; + + g_ble_phy_data.phy_mode_tx = tx_phy_mode; + g_ble_phy_data.phy_mode_rx = rx_phy_mode; + + g_ble_phy_data.path_delay_tx = g_ble_phy_path_delay_tx[tx_phy_mode - 1]; + g_ble_phy_data.path_delay_rx = g_ble_phy_path_delay_rx[rx_phy_mode - 1]; + + /* + * Calculate index of transition in frame offset array without tons of + * branches. Note that transitions have to be in specific order in array. + * + * phy_mode 1M = 01b + * phy_mode 2M = 10b + * + * 1M/1M = 01b | 00b = 01b + * 1M/2M = 01b | 10b = 11b + * 2M/1M = 00b | 00b = 00b + * 2M/2M = 00b | 10b = 10b + */ + txrx = (tx_phy_mode & 0x01) | (rx_phy_mode & 0x02); + rxtx = (rx_phy_mode & 0x01) | (tx_phy_mode & 0x02); + g_ble_phy_data.frame_offset_txrx = g_ble_phy_frame_offset_txrx[txrx]; + g_ble_phy_data.frame_offset_rxtx = g_ble_phy_frame_offset_rxtx[rxtx]; +} + +int +ble_phy_get_cur_phy(void) +{ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + switch (g_ble_phy_data.phy_mode_cur) { + case BLE_PHY_MODE_1M: + return BLE_PHY_1M; + case BLE_PHY_MODE_2M: + return BLE_PHY_2M; + default: + assert(0); + return -1; + } +#else + return BLE_PHY_1M; +#endif +} + +/** + * Copies the data from the phy receive buffer into a mbuf chain. + * + * @param dptr Pointer to receive buffer + * @param rxpdu Pointer to already allocated mbuf chain + * + * NOTE: the packet header already has the total mbuf length in it. The + * lengths of the individual mbufs are not set prior to calling. + * + */ +void +ble_phy_rxpdu_copy(uint8_t *dptr, struct os_mbuf *rxpdu) +{ + uint32_t rem_len; + uint32_t copy_len; + uint32_t block_len; + uint32_t block_rem_len; + void *dst; + void *src; + struct os_mbuf * om; + + /* Better be aligned */ + assert(((uint32_t)dptr & 3) == 0); + + block_len = rxpdu->om_omp->omp_databuf_len; + rem_len = OS_MBUF_PKTHDR(rxpdu)->omp_len; + src = dptr; + + /* + * Setup for copying from first mbuf which is shorter due to packet header + * and extra leading space + */ + copy_len = block_len - rxpdu->om_pkthdr_len - 4; + om = rxpdu; + dst = om->om_data; + + while (om) { + /* + * Always copy blocks of length aligned to word size, only last mbuf + * will have remaining non-word size bytes appended. + */ + block_rem_len = copy_len; + copy_len = min(copy_len, rem_len); + copy_len &= ~3; + + dst = om->om_data; + om->om_len = copy_len; + rem_len -= copy_len; + block_rem_len -= copy_len; + + __asm__ volatile (".syntax unified \n" + " mov r4, %[len] \n" + " b 2f \n" + "1: ldr r3, [%[src], %[len]] \n" + " str r3, [%[dst], %[len]] \n" + "2: subs %[len], #4 \n" + " bpl 1b \n" + " adds %[src], %[src], r4 \n" + " adds %[dst], %[dst], r4 \n" + : [dst] "+l" (dst), [src] "+l" (src), + [len] "+l" (copy_len) + : + : "r3", "r4", "memory"); + + if ((rem_len < 4) && (block_rem_len >= rem_len)) { + break; + } + + /* Move to next mbuf */ + om = SLIST_NEXT(om, om_next); + copy_len = block_len; + } + + /* Copy remaining bytes, if any, to last mbuf */ + om->om_len += rem_len; + __asm__ volatile (".syntax unified \n" + " b 2f \n" + "1: ldrb r3, [%[src], %[len]] \n" + " strb r3, [%[dst], %[len]] \n" + "2: subs %[len], #1 \n" + " bpl 1b \n" + : [len] "+l" (rem_len) + : [dst] "l" (dst), [src] "l" (src) + : "r3", "memory"); + + /* Copy header */ + memcpy(BLE_MBUF_HDR_PTR(rxpdu), &g_ble_phy_data.rxhdr, + sizeof(struct ble_mbuf_hdr)); +} + +void +ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs) +{ + uint64_t llt; + uint32_t corr_window; + uint32_t llt_z_ticks; + uint32_t aa_time; + + /* + * RX is started 2us earlier due to allowed clock accuracy and it should end + * 2us later for the same reason. Preamble is always 8us (8 symbols on 1M, + * 16 symbols on 2M) and Access Address is 32us on 1M and 16us on 2M. Add + * 1us just in case... + */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + aa_time = 2 + (g_ble_phy_data.phy_mode_rx == BLE_PHY_MODE_1M ? 40 : 24) + 2 + 1; +#else + aa_time = 45; +#endif + + if (txrx == BLE_PHY_WFR_ENABLE_TXRX) { + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_WINDOW, aa_time); + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_CORR_TMR_LD_2_CORR_START_Msk; + } else if (wfr_usecs < 16384) { + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_WINDOW, wfr_usecs + aa_time); + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_CORR_TMR_LD_2_CORR_START_Msk; + } else { + wfr_usecs += aa_time; + llt = g_ble_phy_data.start_llt; + + /* + * wfr is outside range of CORR_WINDOW so we need to use LLT to start + * correlator timeout with some delay. Let's use ~10ms as new CORR_WINDOW + * value (does not really matter, just had to pick something) so need to + * calculate how many hi-Z ticks of delay we need. + */ + llt_z_ticks = (wfr_usecs - 10000) / 1024; + + /* New CORR_WINDOW is wfr adjusted by hi-Z ticks and remainder of 1st tick. */ + corr_window = wfr_usecs; + corr_window -= llt_z_ticks * 1024; + corr_window -= 1024 - (llt & 0x3ff); + + CMAC->CM_LL_TIMER1_36_10_EQ_Z_REG = (llt >> 10) + llt_z_ticks; + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_WINDOW, corr_window); + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_CORR_TMR_LD_2_TMR1_36_10_EQ_Z_Msk; + } +} + +int +ble_phy_init(void) +{ + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; +#if MYNEWT_VAL(BLE_LL_DTM) + g_ble_phy_data.phy_whitening = 1; +#endif + + ble_rf_init(); + + /* + * 9_0_EQ_X can be linked to start RX/TX so we'll use this one for + * scheduling TX/RX start - make sure it's not linked to LL_TIMER2LLC + */ + CMAC->CM_LL_INT_SEL_REG &= ~CMAC_CM_LL_INT_SEL_REG_LL_TIMER1_9_0_EQ_X_SEL_Msk; + + CMAC->CM_PHY_CTRL_REG = ((PHY_DELAY_POWER_DN_RX - 1) << 24) | + ((PHY_DELAY_POWER_DN_TX - 1) << 16) | + ((PHY_DELAY_POWER_UP_RX - 1) << 8) | + ((PHY_DELAY_POWER_UP_TX - 1)); + +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + CMAC->CM_PHY_CTRL2_REG = CMAC_CM_PHY_CTRL2_REG_PHY_MODE_Msk | + (3 << CMAC_CM_PHY_CTRL2_REG_CORR_CLK_MODE_Pos); +#else + CMAC->CM_PHY_CTRL2_REG = CMAC_CM_PHY_CTRL2_REG_PHY_MODE_Msk | + (2 << CMAC_CM_PHY_CTRL2_REG_CORR_CLK_MODE_Pos); +#endif + + CMAC_SETREGF(CM_CTRL2_REG, WHITENING_MODE, 0); + CMAC_SETREGF(CM_CTRL2_REG, CRC_MODE, 0); + + /* Setup for 1M by default */ + ble_phy_mode_set(BLE_PHY_MODE_1M, BLE_PHY_MODE_1M); + ble_phy_mode_apply(BLE_PHY_MODE_1M); + + NVIC_SetPriority(FIELD_IRQn, 0); + NVIC_SetPriority(CALLBACK_IRQn, 0); + NVIC_SetPriority(FRAME_IRQn, 0); + NVIC_SetPriority(CRYPTO_IRQn, 1); + NVIC_SetPriority(SW_MAC_IRQn, 1); + NVIC_EnableIRQ(FIELD_IRQn); + NVIC_EnableIRQ(CALLBACK_IRQn); + NVIC_EnableIRQ(FRAME_IRQn); + NVIC_EnableIRQ(SW_MAC_IRQn); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* Initialize non-zero fixed values in CCM blocks */ + g_ble_phy_encrypt_data.b0[0] = 0x49; + g_ble_phy_encrypt_data.b1[1] = 0x01; + g_ble_phy_encrypt_data.ai[0] = 0x01; +#endif + + return 0; +} + +void +ble_phy_disable(void) +{ + MCU_DIAG_SER('D'); + + __disable_irq(); + + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_BS_CLEAR_Msk; + + __NOP(); + __NOP(); + __NOP(); + __NOP(); + __NOP(); + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk | + CMAC_CM_EXC_STAT_REG_EXC_BS_START_4THIS_Msk | + CMAC_CM_EXC_STAT_REG_EXC_CORR_TIMEOUT_Msk | + CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk | + CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk | + CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk | + CMAC_CM_EXC_STAT_REG_EXC_SW_MAC_Msk; + + NVIC->ICPR[0] = (1 << FIELD_IRQn) | (1 << CALLBACK_IRQn) | + (1 << FRAME_IRQn) | (1 << SW_MAC_IRQn); + + os_arch_cmac_bs_ctrl_irq_unblock(); + g_sw_mac_exc = 0; + + CMAC->CM_ERROR_DIS_REG = 0; + + ble_rf_stop(); + + /* + * If ble_phy_disable is called precisely when access address was matched, + * ts1_dirty may not be cleared properly. This is because bs_clear will + * cause bitstream controller to be stopped and we won't get callback_irq, + * but seems like demodulator is still active for a while and will trigger + * ev1c_ts1_trigger on 1st symbol which will set ts1_dirty. We do not expect + * ts1_dirty to be set after bs_clear so we won't clear it. To workaround + * his, we can just clear it explicitly here after everything is already + * disabled. + */ + (void)CMAC->CM_TS1_REG; + + __enable_irq(); + + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; +} + +static void +ble_phy_rx_setup_fields(void) +{ + /* Make sure CRC LFSR initial value is set */ + CMAC_SETREGF(CM_CRC_REG, CRC_INIT_VAL, g_ble_phy_data.crc_init); + + CMAC->CM_FIELD_PUSH_DATA_REG = g_ble_phy_data.access_addr; + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_ACCESS_ADDR; + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(0, 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_HEADER; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + /* Only program one byte for encrypted payloads */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(2, 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_ENC_PAYLOAD; + } else { + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(2, 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_PAYLOAD; + } +#else + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(2, 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_PAYLOAD; +#endif +} + +static void +ble_phy_rx_setup_xcvr(void) +{ + uint8_t rf_chan = g_ble_phy_chan_to_rf[g_ble_phy_data.channel]; + + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_CALLBACK_VALID_SET_Msk; + + ble_rf_setup_rx(rf_chan, g_ble_phy_data.phy_mode_rx); + + g_ble_phy_data.phy_rx_started = 0; +} + +int +ble_phy_rx(void) +{ + MCU_DIAG_SER('R'); + + ble_rf_configure(); + ble_phy_rx_setup_xcvr(); + + CMAC->CM_FRAME_1_REG = CMAC_CM_FRAME_1_REG_FRAME_VALID_Msk; + + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_WINDOW, 0); + + ble_phy_rx_setup_fields(); + + g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; + + return 0; +} + +int +ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + uint32_t ll_val32; + int32_t time_till_start; + + MCU_DIAG_SER('r'); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_mode_rx); +#endif + + assert(ble_rf_is_enabled()); + + ble_phy_rx(); + + /* Get LL timer at cputime */ + ll_val32 = ble_phy_convert_and_record_start_time(cputime, rem_usecs); + + /* Add remaining usecs to get exact receive start time */ + ll_val32 += rem_usecs; + + /* Adjust start time for rx delays */ + ll_val32 -= PHY_DELAY_POWER_UP_RX - g_ble_phy_data.path_delay_rx; + + __disable_irq(); + CMAC->CM_LL_TIMER1_9_0_EQ_X_REG = ll_val32; + CMAC->CM_EV_LINKUP_REG = + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_TMR1_9_0_EQ_X_Msk; + + time_till_start = (int32_t)(ll_val32 - cmac_timer_read32()); + if (time_till_start <= 0) { + /* + * Possible we missed the frame start! If we have, we need to start + * ASAP. + */ + if ((CMAC->CM_EXC_STAT_REG & CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk) == 0) { + /* We missed start. Start now */ + CMAC->CM_EV_LINKUP_REG = + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_ASAP_Msk; + } + } + __enable_irq(); + + return 0; +} + +int +ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) +{ + uint8_t *txbuf = g_ble_phy_tx_buf; + int rc; + + MCU_DIAG_SER('T'); + + assert(CMAC->CM_FRAME_1_REG & CMAC_CM_FRAME_1_REG_FRAME_TX_Msk); + + g_ble_phy_data.end_transition = end_trans; + + /* + * Program required fields now so in worst case TX can continue while we + * are still preparing header and payload. + */ + CMAC->CM_FIELD_PUSH_DATA_REG = g_ble_phy_data.access_addr & 1 ? 0x5555 : 0xaaaa; + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_PREAMBLE; + CMAC->CM_FIELD_PUSH_DATA_REG = g_ble_phy_data.access_addr; + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_ACCESS_ADDR; + + /* Make sure CRC LFSR initial value is set */ + CMAC_SETREGF(CM_CRC_REG, CRC_INIT_VAL, g_ble_phy_data.crc_init); + + /* txbuf[0] is hdr_byte, txbuf[1] is pkt_len */ + txbuf[1] = pducb(&txbuf[2], pducb_arg, &txbuf[0]); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted && (txbuf[1] != 0)) { + /* We have to add the MIC to the length */ + txbuf[1] += BLE_LL_DATA_MIC_LEN; + + /* Program header field */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_TX(0, 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_PAYLOAD; + + /* Program payload (and MIC) */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_TX(2, txbuf[1]); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_ENC_PAYLOAD; + } else { + /* Program header and payload fields */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_TX(0, txbuf[1] + 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_PAYLOAD; + } +#else + /* Program header and payload fields */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_TX(0, txbuf[1] + 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_PAYLOAD; +#endif + + /* + * If there was FIELD_ON_THR exception it means access address was already + * sent and we are likely too late here - abort. + */ + if (CMAC->CM_EXC_STAT_REG & CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk) { + ble_phy_disable(); + g_ble_phy_data.end_transition = BLE_PHY_TRANSITION_NONE; + STATS_INC(ble_phy_stats, tx_late); + rc = BLE_PHY_ERR_RADIO_STATE; + } else { + if (g_ble_phy_data.phy_state == BLE_PHY_STATE_IDLE) { + g_ble_phy_data.phy_state = BLE_PHY_STATE_TX; + } + STATS_INC(ble_phy_stats, tx_good); + STATS_INCN(ble_phy_stats, tx_bytes, txbuf[1] + 2); + rc = BLE_ERR_SUCCESS; + } + + /* Now we can handle BS_CTRL */ + CMAC->CM_ERROR_DIS_REG &= ~CMAC_CM_ERROR_DIS_REG_CM_FIELD1_ERR_Msk; + NVIC_EnableIRQ(FRAME_IRQn); + NVIC_EnableIRQ(FIELD_IRQn); + + return rc; +} + +int +ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + uint8_t rf_chan = g_ble_phy_chan_to_rf[g_ble_phy_data.channel]; + uint32_t ll_val32; + int rc; + + MCU_DIAG_SER('t'); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_mode_tx); +#endif + + assert(ble_rf_is_enabled()); + + ble_rf_configure(); + ble_rf_setup_tx(rf_chan, g_ble_phy_data.phy_mode_tx); + + ll_val32 = ble_phy_convert_and_record_start_time(cputime, rem_usecs); + ll_val32 += rem_usecs; + ll_val32 -= PHY_DELAY_POWER_UP_TX + g_ble_phy_data.path_delay_tx; + /* we can schedule TX only up to 1023us in advance */ + assert((int32_t)(ll_val32 - cmac_timer_read32()) < 1024); + + /* + * We do not want FIELD/FRAME interrupts or FIELD1_ERR until ble_phy_tx() + * has finished pushing all the fields. Also we do not want premature + * FRAME_ERR so disable it until we program FRAME1 properly. If we won't + * make configuration on time, assume tx_late and abort TX. + */ + NVIC_DisableIRQ(FRAME_IRQn); + NVIC_DisableIRQ(FIELD_IRQn); + CMAC->CM_ERROR_DIS_REG |= CMAC_CM_ERROR_DIS_REG_CM_FIELD1_ERR_Msk | + CMAC_CM_ERROR_DIS_REG_CM_FRAME_ERR_Msk; + + CMAC->CM_LL_TIMER1_9_0_EQ_X_REG = ll_val32; + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_TMR1_9_0_EQ_X_Msk; + + if ((int32_t)(ll_val32 - cmac_timer_read32()) < 0) { + goto tx_late; + } + + /* + * Program frame now since it needs to be ready for FRAME_START, we can + * push fields later + */ + CMAC->CM_FRAME_1_REG = CMAC_CM_FRAME_1_REG_FRAME_VALID_Msk | + CMAC_CM_FRAME_1_REG_FRAME_TX_Msk | + CMAC_CM_FRAME_1_REG_FRAME_EXC_ON_BS_START_Msk; + + /* + * There should be no EXC_FRAME_START here so if it already happened we + * need to assume tx_late and abort. + */ + if (CMAC->CM_EXC_STAT_REG & CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk) { + goto tx_late; + } + + CMAC->CM_ERROR_DIS_REG &= ~CMAC_CM_ERROR_DIS_REG_CM_FRAME_ERR_Msk; + rc = 0; + + goto done; + +tx_late: + STATS_INC(ble_phy_stats, tx_late); + ble_phy_disable(); + NVIC_EnableIRQ(FRAME_IRQn); + NVIC_EnableIRQ(FIELD_IRQn); + rc = BLE_PHY_ERR_TX_LATE; + +done: + return rc; +} + +void +ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg) +{ + g_ble_phy_data.txend_cb = txend_cb; + g_ble_phy_data.txend_arg = arg; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +void +ble_phy_tx_enc_start(void) +{ + struct ble_phy_encrypt_obj *enc; + + enc = &g_ble_phy_encrypt_data; + enc->b0[15] = g_ble_phy_tx_buf[1] - 4; + enc->b1[2] = g_ble_phy_tx_buf[0] & BLE_LL_DATA_HDR_LLID_MASK; + + /* XXX: should we check for busy? */ + /* XXX: might not be needed, but for now terminate any crypto operations. */ + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_ABORT_Msk; + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_IN_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_CTR_MAC_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_CTR_PLD_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_AUTH_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ENC_DECN_Msk; + + /* Start crypto */ + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV_CRYPTO_START_Msk; +} + +void +ble_phy_rx_enc_start(uint8_t len) +{ + struct ble_phy_encrypt_obj *enc; + + enc = &g_ble_phy_encrypt_data; + enc->b0[15] = len - 4; /* length without MIC as length includes MIC */ + enc->b1[2] = g_ble_phy_rx_buf[0] & BLE_LL_DATA_HDR_LLID_MASK; + + /* XXX: should we check for busy? */ + /* XXX: might not be needed, but for now terminate any crypto operations. */ + //CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_ABORT_Msk; + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_OUT_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_CTR_MAC_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_CTR_PLD_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_AUTH_EN_Msk; + + /* Start crypto */ + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV_CRYPTO_START_Msk; +} + +void +ble_phy_encrypt_enable(uint64_t pkt_counter, uint8_t *iv, uint8_t *key, + uint8_t is_master) +{ + struct ble_phy_encrypt_obj *enc; + + enc = &g_ble_phy_encrypt_data; + memcpy(enc->key, key, 16); + memcpy(&enc->b0[6], iv, 8); + put_le32(&enc->b0[1], pkt_counter); + enc->b0[5] = is_master ? 0x80 : 0; + memcpy(&enc->ai[6], iv, 8); + put_le32(&enc->ai[1], pkt_counter); + enc->ai[5] = enc->b0[5]; + + g_ble_phy_data.phy_encrypted = 1; + + /* Program key registers */ + CMAC->CM_CRYPTO_KEY_31_0_REG = get_le32(&enc->key[0]); + CMAC->CM_CRYPTO_KEY_63_32_REG = get_le32(&enc->key[4]); + CMAC->CM_CRYPTO_KEY_95_64_REG = get_le32(&enc->key[8]); + CMAC->CM_CRYPTO_KEY_127_96_REG = get_le32(&enc->key[12]); + + /* Program ADRx registers */ + CMAC->CM_CRYPTO_IN_ADR0_REG = (uint32_t)enc->b1; + CMAC->CM_CRYPTO_IN_ADR1_REG = (uint32_t)enc->b0; + CMAC->CM_CRYPTO_IN_ADR2_REG = (uint32_t)&g_ble_phy_tx_buf[2]; + CMAC->CM_CRYPTO_IN_ADR3_REG = (uint32_t)enc->ai; + CMAC->CM_CRYPTO_OUT_ADR_REG = (uint32_t)&g_ble_phy_rx_buf[2]; + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_PBUF_CLR_Msk; +} + +void +ble_phy_encrypt_set_pkt_cntr(uint64_t pkt_counter, int dir) +{ + struct ble_phy_encrypt_obj *enc; + + enc = &g_ble_phy_encrypt_data; + put_le32(&enc->b0[1], pkt_counter); + enc->b0[5] = dir ? 0x80 : 0; + put_le32(&enc->ai[1], pkt_counter); + enc->ai[5] = enc->b0[5]; + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_PBUF_CLR_Msk; +} + +void +ble_phy_encrypt_disable(void) +{ + g_ble_phy_data.phy_encrypted = 0; +} +#endif + +int +ble_phy_txpwr_set(int dbm) +{ +#if MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE) + if (g_cmac_shared_data.debug.tx_power_override != INT8_MAX) { + ble_rf_set_tx_power(g_cmac_shared_data.debug.tx_power_override); + } else { + ble_rf_set_tx_power(dbm); + } +#else + ble_rf_set_tx_power(dbm); +#endif + + return 0; +} + +int +ble_phy_txpower_round(int dbm) +{ + return 0; +} + +void +ble_phy_set_rx_pwr_compensation(int8_t compensation) +{ +} + +int +ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crc_init) +{ + uint8_t rf_chan = g_ble_phy_chan_to_rf[chan]; + + assert(chan < BLE_PHY_NUM_CHANS); + + if (chan >= BLE_PHY_NUM_CHANS) { + return BLE_PHY_ERR_INV_PARAM; + } + + g_ble_phy_data.channel = chan; + g_ble_phy_data.access_addr = access_addr; + g_ble_phy_data.crc_init = crc_init; + + CMAC_SETREGF(CM_PHY_CTRL2_REG, PHY_RF_CHANNEL, rf_chan); + + return 0; +} + +void +ble_phy_restart_rx(void) +{ + /* XXX: for now, we will disable the phy using ble_phy_disable and then + re-enable it + */ + ble_phy_disable(); + + /* Apply mode before starting RX */ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_mode_rx); +#endif + + /* Setup phy to rx again */ + ble_phy_rx(); + + /* Start reception now */ + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_ASAP_Msk; +} + +uint32_t +ble_phy_access_addr_get(void) +{ + return g_ble_phy_data.access_addr; +} + +int +ble_phy_state_get(void) +{ + return g_ble_phy_data.phy_state; +} + +int +ble_phy_rx_started(void) +{ + return g_ble_phy_data.phy_rx_started; +} + +uint8_t +ble_phy_xcvr_state_get(void) +{ + return ble_rf_is_enabled(); +} + +uint8_t +ble_phy_max_data_pdu_pyld(void) +{ + return BLE_LL_DATA_PDU_MAX_PYLD; +} + +void +ble_phy_resolv_list_enable(void) +{ + g_ble_phy_data.phy_privacy = 1; + + ble_hw_resolv_proc_enable(); +} + +void +ble_phy_resolv_list_disable(void) +{ + ble_hw_resolv_proc_disable(); + + g_ble_phy_data.phy_privacy = 0; +} + +void +ble_phy_rfclk_enable(void) +{ + ble_rf_enable(); +} + +void +ble_phy_rfclk_disable(void) +{ + /* XXX We can't disable RF while PHY_BUSY is asserted so let's wait a bit */ + while (CMAC->CM_DIAG_WORD2_REG & CMAC_CM_DIAG_WORD2_REG_DIAG2_PHY_BUSY_BUF_Msk); + + ble_rf_disable(); +} + +#if MYNEWT_VAL(BLE_LL_DTM) +void +ble_phy_enable_dtm(void) +{ + g_ble_phy_data.phy_whitening = 0; +} + +void +ble_phy_disable_dtm(void) +{ + g_ble_phy_data.phy_whitening = 1; +} +#endif diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf.c b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf.c new file mode 100644 index 00000000..806f4ea8 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf.c @@ -0,0 +1,747 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include "mcu/mcu.h" +#include "mcu/cmac_timer.h" +#include "controller/ble_phy.h" +#include "cmac_driver/cmac_shared.h" +#include "ble_rf_priv.h" + +#define RF_CALIBRATION_0 (0x01) +#define RF_CALIBRATION_1 (0x02) +#define RF_CALIBRATION_2 (0x04) + +static const int8_t g_ble_rf_power_lvls[] = { + -18, -12, -8, -6, -3, -2, -1, 0, 1, 2, 3, 4, 4, 5, 6 +}; + +struct ble_phy_rf_data { + uint8_t tx_power_cfg0; + uint8_t tx_power_cfg1; + uint8_t tx_power_cfg2; + uint8_t tx_power_cfg3; + uint32_t cal_res_1; + uint32_t cal_res_2; + uint32_t trim_val1_tx_1; + uint32_t trim_val1_tx_2; + uint32_t trim_val2_tx; + uint32_t trim_val2_rx; + uint8_t calibrate_req; +}; + +static struct ble_phy_rf_data g_ble_phy_rf_data; + +static inline uint32_t +get_reg32(uint32_t addr) +{ + volatile uint32_t *reg = (volatile uint32_t *)addr; + + return *reg; +} + +static inline uint32_t +get_reg32_bits(uint32_t addr, uint32_t mask) +{ + volatile uint32_t *reg = (volatile uint32_t *)addr; + + return (*reg & mask) >> __builtin_ctz(mask); +} + +static inline void +set_reg8(uint32_t addr, uint8_t val) +{ + volatile uint8_t *reg = (volatile uint8_t *)addr; + + *reg = val; +} + +static inline void +set_reg16(uint32_t addr, uint16_t val) +{ + volatile uint16_t *reg = (volatile uint16_t *)addr; + + *reg = val; +} + +static inline void +set_reg32(uint32_t addr, uint32_t val) +{ + volatile uint32_t *reg = (volatile uint32_t *)addr; + + *reg = val; +} + +static inline void +set_reg32_bits(uint32_t addr, uint32_t mask, uint32_t val) +{ + volatile uint32_t *reg = (volatile uint32_t *)addr; + + *reg = (*reg & (~mask)) | (val << __builtin_ctz(mask)); +} + +static inline void +set_reg32_mask(uint32_t addr, uint32_t mask, uint32_t val) +{ + volatile uint32_t *reg = (volatile uint32_t *)addr; + + *reg = (*reg & (~mask)) | (val & mask); +} + +static inline void +set_reg16_mask(uint32_t addr, uint16_t mask, uint16_t val) +{ + volatile uint16_t *reg = (volatile uint16_t *)addr; + + *reg = (*reg & (~mask)) | (val & mask); +} + +static void +delay_us(uint32_t delay_us) +{ + while (delay_us--) { + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + } +} + +static void +ble_rf_apply_trim(volatile uint32_t *tv, unsigned len) +{ + while (len) { + *(volatile uint32_t *)tv[0] = tv[1]; + len -= 2; + tv += 2; + } +} + +static void +ble_rf_apply_calibration(void) +{ + set_reg32(0x40020094, g_ble_phy_rf_data.cal_res_1); + if (g_ble_phy_rf_data.cal_res_2) { + set_reg32_bits(0x40022018, 0xff800000, g_ble_phy_rf_data.cal_res_2); + set_reg32_bits(0x40022018, 0x00007fc0, g_ble_phy_rf_data.cal_res_2); + } +} + +static inline void +ble_rf_ldo_on(void) +{ + set_reg8(0x40020004, 9); +} + +static inline void +ble_rf_ldo_off(void) +{ + set_reg8(0x40020004, 0); +} + +static inline void +ble_rf_rfcu_enable(void) +{ + set_reg32_bits(0x50000010, 0x00000020, 1); +} + +static inline void +ble_rf_rfcu_disable(void) +{ + set_reg32_bits(0x50000010, 0x00000020, 0); +} + +static void +ble_rf_rfcu_apply_recommended_settings(void) +{ + set_reg16_mask(0x400200a0, 0x0001, 0x0001); + set_reg16_mask(0x40021020, 0x03f0, 0x02f5); + set_reg32_mask(0x40021018, 0x001fffff, 0x005a5809); + set_reg32_mask(0x4002101c, 0x00001e01, 0x0040128c); + set_reg32_mask(0x40021004, 0xffffff1f, 0x64442404); + set_reg32_mask(0x40021008, 0xfcfcffff, 0x6b676665); + set_reg32_mask(0x4002100c, 0x00fcfcfc, 0x9793736f); + set_reg32_mask(0x40021010, 0x1f1f1c1f, 0x04072646); + set_reg32_mask(0x40020000, 0x001ff000, 0x0f099820); + set_reg16_mask(0x40020348, 0x00ff, 0x0855); + set_reg16(0x40020350, 0x0234); + set_reg16(0x40020354, 0x0a34); + set_reg16(0x40020358, 0x0851); + set_reg16(0x4002035c, 0x0a26); + set_reg16(0x40020360, 0x0858); + set_reg16(0x4002102c, 0xdfe7); + set_reg32_mask(0x4002103c, 0x00c00000, 0x0024a19f); + set_reg16_mask(0x40021000, 0x0008, 0x000b); + set_reg16_mask(0x40020238, 0x03e0, 0x02c0); + set_reg16_mask(0x4002023c, 0x03e0, 0x02c0); + set_reg16_mask(0x40020244, 0x03e0, 0x0250); + set_reg16_mask(0x40020248, 0x03e0, 0x02a0); + set_reg16_mask(0x4002024c, 0x03e0, 0x02c0); + set_reg16_mask(0x40020288, 0x03e0, 0x0300); + set_reg16_mask(0x4002029c, 0x001f, 0x0019); + set_reg16_mask(0x4002003c, 0x6000, 0x0788); + set_reg16_mask(0x40020074, 0x7f00, 0x2007); + set_reg32_mask(0x40020080, 0x00333330, 0x00222224); + set_reg32_mask(0x40020068, 0x00000f0f, 0x00000f0d); +} + +static void +ble_rf_rfcu_apply_settings(void) +{ + ble_rf_apply_trim(g_cmac_shared_data.trim.rfcu, + g_cmac_shared_data.trim.rfcu_len); + ble_rf_rfcu_apply_recommended_settings(); +} + +static inline void +ble_rf_synth_enable(void) +{ + set_reg8(0x40020005, 3); +} + +static inline void +ble_rf_synth_disable(void) +{ + set_reg8(0x40020005, 0); + __NOP(); + __NOP(); +} + +static bool +ble_rf_synth_is_enabled(void) +{ + return get_reg32_bits(0x40020004, 256); +} + +static void +ble_rf_synth_apply_recommended_settings(void) +{ + set_reg32_mask(0x40022048, 0x0000000c, 0x000000d5); + set_reg32_mask(0x40022050, 0x00000300, 0x00000300); + set_reg16_mask(0x40022024, 0x0001, 0x0001); +} + +static void +ble_rf_synth_apply_settings(void) +{ + ble_rf_apply_trim(g_cmac_shared_data.trim.synth, + g_cmac_shared_data.trim.synth_len); + ble_rf_synth_apply_recommended_settings(); +} + +static void +ble_rf_calibration_0(void) +{ + uint32_t bkp[10]; + + bkp[0] = get_reg32(0x40020208); + bkp[1] = get_reg32(0x40020250); + bkp[2] = get_reg32(0x40020254); + bkp[3] = get_reg32(0x40021028); + bkp[4] = get_reg32(0x40020020); + bkp[5] = get_reg32(0x40020294); + bkp[6] = get_reg32(0x4002103C); + bkp[7] = get_reg32(0x400200A8); + bkp[8] = get_reg32(0x40020000); + bkp[9] = get_reg32(0x40022000); + + set_reg32_bits(0x40020000, 0x00000002, 0); + set_reg32_bits(0x40022000, 0x00000001, 0); + set_reg32_mask(0x4002103C, 0x00201c00, 0x00001c00); + set_reg32_bits(0x400200A8, 0x00000001, 1); + set_reg8(0x40020006, 1); + set_reg32(0x40020208, 0); + set_reg32(0x40020250, 0); + set_reg32(0x40020254, 0); + set_reg32(0x40021028, 0x00F8A494); + set_reg32(0x40020020, 8); + set_reg32(0x40020294, 0); + set_reg32(0x40020024, 0); + + delay_us(5); + if (get_reg32_bits(0x40020020, 0x00000002)) { + goto done; + } + + set_reg32_bits(0x40020020, 0x00000001, 1); + delay_us(15); + if (!get_reg32_bits(0x40020020, 0x00000001)) { + goto done; + } + + delay_us(300); + if (get_reg32_bits(0x40020020, 0x00000001)) { + goto done; + } + +done: + set_reg32(0x40020024, 0); + set_reg32(0x40020208, bkp[0]); + set_reg32(0x40020250, bkp[1]); + set_reg32(0x40020254, bkp[2]); + set_reg32(0x40021028, bkp[3]); + set_reg32(0x40020020, bkp[4]); + set_reg32(0x40020294, bkp[5]); + set_reg32(0x4002103C, bkp[6]); + set_reg32(0x400200A8, bkp[7]); + set_reg32(0x40020000, bkp[8]); + set_reg32(0x40022000, bkp[9]); +} + +static void +ble_rf_calibration_1(void) +{ + uint32_t bkp[12]; + uint32_t val; + + bkp[0] = get_reg32(0x40020020); + bkp[1] = get_reg32(0x40020208); + bkp[2] = get_reg32(0x40020250); + bkp[3] = get_reg32(0x40020254); + bkp[4] = get_reg32(0x40020218); + bkp[5] = get_reg32(0x4002021c); + bkp[6] = get_reg32(0x40020220); + bkp[7] = get_reg32(0x40020270); + bkp[8] = get_reg32(0x4002027c); + bkp[9] = get_reg32(0x4002101c); + bkp[10] = get_reg32(0x40020000); + bkp[11] = get_reg32(0x40022000); + + set_reg32(0x4002103c, 0x0124a21f); + set_reg32(0x40020208, 0); + set_reg32(0x40020250, 0); + set_reg32(0x40020254, 0); + set_reg32(0x40020218, 0); + set_reg32(0x4002021c, 0); + set_reg32(0x40020220, 0); + set_reg32(0x40020270, 0); + set_reg32(0x4002027c, 0); + set_reg32(0x40020000, 0x0f168820); + set_reg32_bits(0x40022000, 0x00000001, 0); + set_reg32_bits(0x4002101c, 0x00001e00, 0); + set_reg32_bits(0x4002001c, 0x0000003f, 47); + set_reg8(0x40020006, 1); + set_reg32(0x40020020, 16); + set_reg32_bits(0x4002003c, 0x00000800, 1); + set_reg32(0x40020024, 0); + + delay_us(5); + if (get_reg32_bits(0x40020020, 0x00000002)) { + goto done; + } + + set_reg32_bits(0x40020020, 0x00000001, 1); + delay_us(15); + if (!get_reg32_bits(0x40020020, 0x00000001)) { + goto done; + } + + delay_us(300); + if (get_reg32_bits(0x40020020, 0x00000001)) { + goto done; + } + + val = get_reg32(0x40020090); + set_reg32_bits(0x40020094, 0x0000000f, val); + set_reg32_bits(0x40020094, 0x00000f00, val); + set_reg32_bits(0x40020094, 0x000f0000, val); + set_reg32_bits(0x40020094, 0x0f000000, val); + g_ble_phy_rf_data.cal_res_1 = get_reg32(0x40020094); + +done: + set_reg32(0x40020024, 0); + set_reg32(0x40020020, bkp[0]); + set_reg32(0x40020208, bkp[1]); + set_reg32(0x40020250, bkp[2]); + set_reg32(0x40020254, bkp[3]); + set_reg32(0x40020218, bkp[4]); + set_reg32(0x4002021c, bkp[5]); + set_reg32(0x40020220, bkp[6]); + set_reg32(0x40020270, bkp[7]); + set_reg32(0x4002027c, bkp[8]); + set_reg32(0x4002101c, bkp[9]); + set_reg32(0x40020000, bkp[10]); + set_reg32(0x40022000, bkp[11]); + set_reg32_bits(0x4002003c, 0x00000800, 0); +} + +static void +ble_rf_calibration_2(void) +{ + uint32_t bkp[2]; + uint32_t k1; + + set_reg8(0x40020005, 3); + set_reg32(0x40022000, 0x00000300); + set_reg32_bits(0x40022004, 0x0000007f, 20); + bkp[0] = get_reg32(0x40022040); + set_reg32(0x40022040, 0xffffffff); + set_reg32_bits(0x40022018, 0x0000003f, 0); + set_reg32_bits(0x40022018, 0x00008000, 0); + set_reg32_bits(0x4002201c, 0x00000600, 2); + set_reg32_bits(0x4002201c, 0x00000070, 4); + set_reg32_bits(0x40022030, 0x3f000000, 22); + set_reg32_bits(0x40022030, 0x00000fc0, 24); + set_reg32_bits(0x40022030, 0x0000003f, 24); + set_reg8(0x4002201c, 0x43); + set_reg8(0x40020006, 2); + delay_us(2); + bkp[1] = get_reg32_bits(0x4002024c, 0x000003e0); + set_reg32_bits(0x4002024c, 0x000003e0, 0); + set_reg8(0x40020006, 1); + set_reg32_bits(0x400200ac, 0x00000003, 3); + delay_us(30); + delay_us(100); + set_reg8(0x40020005, 3); + k1 = get_reg32_bits(0x40022088, 0x000001ff); + set_reg32(0x400200ac, 0); + delay_us(20); + set_reg32_bits(0x4002024c, 0x000003e0, bkp[1]); + delay_us(10); + + set_reg32_bits(0x40022018, 0xff800000, k1); + set_reg32_bits(0x40022018, 0x00007fc0, k1); + set_reg8(0x4002201c, 0x41); + set_reg32_bits(0x4002201c, 0x00000600, 2); + set_reg8(0x40020006, 2); + delay_us(2); + bkp[1] = get_reg32_bits(0x4002024c, 0x000003e0); + set_reg32_bits(0x4002024c, 0x000003e0, 0); + set_reg8(0x40020006, 1); + set_reg32_bits(0x400200ac, 0x00000003, 3); + delay_us(30); + delay_us(100); + set_reg8(0x40020005, 3); + k1 = get_reg32_bits(0x40022088, 0x1ff); + set_reg32(0x400200ac, 0); + delay_us(20); + set_reg32_bits(0x4002024c, 0x000003e0, bkp[1]); + delay_us(10); + + set_reg32_bits(0x40022018, 0xff800000, k1); + set_reg32_bits(0x40022018, 0x00007fc0, k1); + set_reg8(0x4002201c, 0x41); + set_reg32_bits(0x4002201c, 0x00000600, 2); + set_reg8(0x40020006, 2); + delay_us(2); + bkp[1] = get_reg32_bits(0x4002024c, 0x000003e0); + set_reg32_bits(0x4002024c, 0x000003e0, 0); + set_reg8(0x40020006, 1); + set_reg32_bits(0x400200ac, 0x00000003, 3); + delay_us(30); + delay_us(100); + set_reg8(0x40020005, 3); + k1 = get_reg32_bits(0x40022088, 0x000001ff); + set_reg32_bits(0x40022018, 0xff800000, k1); + set_reg32_bits(0x40022018, 0x00007fc0, k1); + set_reg32_bits(0x4002201c, 0x00000001, 0); + set_reg32(0x40022040, bkp[0]); + set_reg32_bits(0x40022018, 0x0000003f, 0x1c); + set_reg32_bits(0x40022018, 0x00008000, 0); + set_reg32_bits(0x40022030, 0x3f000000, 28); + set_reg32_bits(0x40022030, 0x00000fc0, 30); + set_reg32_bits(0x40022030, 0x0000003f, 30); + set_reg32(0x400200ac, 0); + delay_us(20); + set_reg32_bits(0x4002024c, 0x000003e0, bkp[1]); + delay_us(10); + + g_ble_phy_rf_data.cal_res_2 = k1; +} + +static void +ble_rf_calibrate_int(uint8_t mask) +{ + __disable_irq(); + + ble_rf_enable(); + delay_us(20); + + ble_rf_synth_disable(); + ble_rf_synth_enable(); + ble_rf_synth_apply_settings(); + set_reg8(0x40020005, 1); + + if (mask & RF_CALIBRATION_0) { + ble_rf_calibration_0(); + } + if (mask & RF_CALIBRATION_1) { + ble_rf_calibration_1(); + } + if (mask & RF_CALIBRATION_2) { + ble_rf_calibration_2(); + } + + ble_rf_disable(); + + __enable_irq(); + +#if MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE) + g_cmac_shared_data.debug.cal_res_1 = g_ble_phy_rf_data.cal_res_1; + g_cmac_shared_data.debug.cal_res_2 = g_ble_phy_rf_data.cal_res_2; +#endif +} + +bool +ble_rf_try_recalibrate(uint32_t idle_time_us) +{ + /* Run recalibration if we have at least 1ms of time to spare and RF is + * currently disabled. Calibration is much shorter than 1ms, but that gives + * us good margin to make sure we can finish before next event. + */ + if (!g_ble_phy_rf_data.calibrate_req || (idle_time_us < 1000) || + ble_rf_is_enabled()) { + return false; + } + + ble_rf_calibrate_int(RF_CALIBRATION_2); + + g_ble_phy_rf_data.calibrate_req = 0; + + return true; +} + +static uint32_t +ble_rf_find_trim_reg(volatile uint32_t *tv, unsigned len, uint32_t reg) +{ + while (len) { + if (tv[0] == reg) { + return tv[1]; + } + len -= 2; + tv += 2; + } + + return 0; +} + +void +ble_rf_init(void) +{ + static bool done = false; + uint32_t val; + + ble_rf_disable(); + + if (done) { + return; + } + + val = ble_rf_find_trim_reg(g_cmac_shared_data.trim.rfcu_mode1, + g_cmac_shared_data.trim.rfcu_mode1_len, + 0x4002004c); + g_ble_phy_rf_data.trim_val1_tx_1 = val; + + val = ble_rf_find_trim_reg(g_cmac_shared_data.trim.rfcu_mode2, + g_cmac_shared_data.trim.rfcu_mode2_len, + 0x4002004c); + g_ble_phy_rf_data.trim_val1_tx_2 = val; + + if (!g_ble_phy_rf_data.trim_val1_tx_1 || !g_ble_phy_rf_data.trim_val1_tx_2) { + val = ble_rf_find_trim_reg(g_cmac_shared_data.trim.rfcu, + g_cmac_shared_data.trim.rfcu_len, + 0x4002004c); + if (!val) { + val = 0x0300; + } + g_ble_phy_rf_data.trim_val1_tx_1 = val; + g_ble_phy_rf_data.trim_val1_tx_2 = val; + } + + val = ble_rf_find_trim_reg(g_cmac_shared_data.trim.synth, + g_cmac_shared_data.trim.synth_len, + 0x40022038); + if (!val) { + val = 0x0198ff03; + } + g_ble_phy_rf_data.trim_val2_rx = val; + g_ble_phy_rf_data.trim_val2_tx = val; + set_reg32_bits((uint32_t)&g_ble_phy_rf_data.trim_val2_tx, 0x0001ff00, 0x87); + +#if MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE) + g_cmac_shared_data.debug.trim_val1_tx_1 = g_ble_phy_rf_data.trim_val1_tx_1; + g_cmac_shared_data.debug.trim_val1_tx_2 = g_ble_phy_rf_data.trim_val1_tx_2; + g_cmac_shared_data.debug.trim_val2_tx = g_ble_phy_rf_data.trim_val2_tx; + g_cmac_shared_data.debug.trim_val2_rx = g_ble_phy_rf_data.trim_val2_rx; +#endif + + ble_rf_rfcu_enable(); + ble_rf_rfcu_apply_settings(); + g_ble_phy_rf_data.tx_power_cfg1 = get_reg32_bits(0x500000a4, 0xf0); + g_ble_phy_rf_data.tx_power_cfg2 = get_reg32_bits(0x40020238, 0x000003e0); + g_ble_phy_rf_data.tx_power_cfg3 = 0; + ble_rf_rfcu_disable(); + + ble_rf_calibrate_int(RF_CALIBRATION_0 | RF_CALIBRATION_1 | RF_CALIBRATION_2); + + done = true; +} + +void +ble_rf_enable(void) +{ + if (ble_rf_is_enabled()) { + return; + } + + ble_rf_rfcu_enable(); + ble_rf_rfcu_apply_settings(); + ble_rf_ldo_on(); +} + +void +ble_rf_configure(void) +{ + if (ble_rf_synth_is_enabled()) { + return; + } + + ble_rf_synth_enable(); + ble_rf_synth_apply_settings(); +} + +void +ble_rf_stop(void) +{ + ble_rf_synth_disable(); + set_reg8(0x40020006, 0); +} + +void +ble_rf_disable(void) +{ + ble_rf_stop(); + ble_rf_ldo_off(); + ble_rf_rfcu_disable(); +} + +bool +ble_rf_is_enabled(void) +{ + return get_reg32_bits(0x40020008, 5) == 5; +} + +void +ble_rf_calibrate_req(void) +{ + g_ble_phy_rf_data.calibrate_req = 1; +} + +void +ble_rf_setup_tx(uint8_t rf_chan, uint8_t phy_mode) +{ + set_reg32_bits(0x40020000, 0x0f000000, g_ble_phy_rf_data.tx_power_cfg0); + set_reg32_bits(0x500000a4, 0x000000f0, g_ble_phy_rf_data.tx_power_cfg1); + set_reg32_bits(0x40020238, 0x000003e0, g_ble_phy_rf_data.tx_power_cfg2); + set_reg32_bits(0x40020234, 0x000003e0, g_ble_phy_rf_data.tx_power_cfg3); + + if (g_ble_phy_rf_data.tx_power_cfg0 < 13) { + set_reg32(0x4002004c, g_ble_phy_rf_data.trim_val1_tx_1); + } else { + set_reg32(0x4002004c, g_ble_phy_rf_data.trim_val1_tx_2); + } + set_reg8(0x40020005, 3); + set_reg8(0x40022004, rf_chan); + if (phy_mode == BLE_PHY_MODE_2M) { +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + set_reg32(0x40022000, 0x00000303); +#else + set_reg32(0x40022000, 0x00000003); +#endif + } else { +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + set_reg32(0x40022000, 0x00000300); +#else + set_reg32(0x40022000, 0x00000000); +#endif + } + + ble_rf_apply_calibration(); + + set_reg32_bits(0x40022050, 0x00000200, 1); + set_reg32_bits(0x40022050, 0x00000100, 0); + set_reg32_bits(0x40022048, 0x01ffff00, 0x7700); + set_reg32(0x40022038, g_ble_phy_rf_data.trim_val2_tx); + + set_reg8(0x40020006, 3); +} + +void +ble_rf_setup_rx(uint8_t rf_chan, uint8_t phy_mode) +{ + set_reg32_bits(0x500000a4, 0x000000f0, g_ble_phy_rf_data.tx_power_cfg1); + set_reg8(0x40020005, 3); + set_reg8(0x40022004, rf_chan); + if (phy_mode == BLE_PHY_MODE_2M) { +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + set_reg32(0x40022000, 0x00000303); + set_reg32(0x40020000, 0x0f11b823); + set_reg32(0x4002103c, 0x0125261b); +#else + set_reg32(0x40022000, 0x00000003); + set_reg32(0x40020000, 0x0f0c2803); + set_reg32(0x4002103c, 0x0125a61b); +#endif + set_reg32(0x40021020, 0x000002f5); + set_reg32(0x4002102c, 0x0000d1d5); + } else { +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + set_reg32(0x40022000, 0x00000300); + set_reg32(0x40020000, 0x0f099820); + set_reg32(0x4002103c, 0x0124a21f); +#else + set_reg32(0x40022000, 0x00000000); + set_reg32(0x40020000, 0x0f062800); + set_reg32(0x4002103c, 0x01051e1f); +#endif + set_reg32(0x40021020, 0x000002f5); + set_reg32(0x4002102c, 0x0000dfe7); + } + + ble_rf_apply_calibration(); + + set_reg32_bits(0x40022050, 0x00000200, 1); + set_reg32_bits(0x40022050, 0x00000100, 1); + set_reg32_bits(0x40022048, 0x01ffff00, 0); + set_reg32(0x40022038, g_ble_phy_rf_data.trim_val2_rx); + + set_reg8(0x40020006, 3); +} + +void +ble_rf_set_tx_power(int dbm) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(g_ble_rf_power_lvls); i++) { + if (g_ble_rf_power_lvls[i] >= dbm) { + break; + } + } + + g_ble_phy_rf_data.tx_power_cfg0 = i + 1; +} + +int8_t +ble_rf_get_rssi(void) +{ + return (501 * get_reg32_bits(0x40021038, 0x000003ff) - 493000) / 4096; +} diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf_priv.h b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf_priv.h new file mode 100644 index 00000000..50c2e4a7 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf_priv.h @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _BLE_RF_PRIV_H_ +#define _BLE_RF_PRIV_H_ + +void ble_rf_init(void); +void ble_rf_enable(void); +void ble_rf_stop(void); +void ble_rf_disable(void); +bool ble_rf_is_enabled(void); +void ble_rf_configure(void); + +void ble_rf_calibrate(void); + +void ble_rf_setup_tx(uint8_t rf_chan, uint8_t mode); +void ble_rf_setup_rx(uint8_t rf_chan, uint8_t mode); + +void ble_rf_set_tx_power(int dbm); +int8_t ble_rf_get_rssi(void); + +#endif /* _BLE_RF_PRIV_H_ */ diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/syscfg.yml b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/syscfg.yml new file mode 100644 index 00000000..a82b62e5 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/syscfg.yml @@ -0,0 +1,29 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +syscfg.defs: + BLE_PHY_RF_HP_MODE: + description: Enable high-performance RF mode. + value: 1 + + BLE_PHY_DEBUG_DSER: + description: Enable DSER output from PHY + value: 0 + +syscfg.restrictions: + - BLE_LL_RFMGMT_ENABLE_TIME == 0 || BLE_LL_RFMGMT_ENABLE_TIME >= 20 diff --git a/src/libs/mynewt-nimble/nimble/drivers/native/src/ble_hw.c b/src/libs/mynewt-nimble/nimble/drivers/native/src/ble_hw.c index 5eb1eb95..8aa29d32 100644 --- a/src/libs/mynewt-nimble/nimble/drivers/native/src/ble_hw.c +++ b/src/libs/mynewt-nimble/nimble/drivers/native/src/ble_hw.c @@ -20,6 +20,8 @@ #include <stdint.h> #include <assert.h> #include <string.h> +#include <stdlib.h> +#include <stdbool.h> #include "syscfg/syscfg.h" #include "os/os.h" #include "nimble/ble.h" @@ -32,6 +34,9 @@ /* We use this to keep track of which entries are set to valid addresses */ static uint8_t g_ble_hw_whitelist_mask; +static ble_rng_isr_cb_t rng_cb; +static bool rng_started; + /* Returns public device address or -1 if not present */ int ble_hw_get_public_addr(ble_addr_t *addr) @@ -143,7 +148,8 @@ ble_hw_encrypt_block(struct ble_encryption_block *ecb) int ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) { - return -1; + rng_cb = cb; + return 0; } /** @@ -154,7 +160,15 @@ ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) int ble_hw_rng_start(void) { - return -1; + rng_started = true; + + if (rng_cb) { + while (rng_started) { + rng_cb(rand()); + } + } + + return 0; } /** @@ -165,7 +179,8 @@ ble_hw_rng_start(void) int ble_hw_rng_stop(void) { - return -1; + rng_started = false; + return 0; } /** @@ -176,7 +191,7 @@ ble_hw_rng_stop(void) uint8_t ble_hw_rng_read(void) { - return 0; + return rand(); } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf5340/include/ble/xcvr.h b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/include/ble/xcvr.h new file mode 100644 index 00000000..df6ef700 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/include/ble/xcvr.h @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_XCVR_ +#define H_BLE_XCVR_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define XCVR_RX_RADIO_RAMPUP_USECS (40) +#define XCVR_TX_RADIO_RAMPUP_USECS (40) + +/* + * NOTE: we have to account for the RTC output compare issue. We want it to be + * 5 ticks. + */ +#define XCVR_PROC_DELAY_USECS (153) +#define XCVR_RX_START_DELAY_USECS (XCVR_RX_RADIO_RAMPUP_USECS) +#define XCVR_TX_START_DELAY_USECS (XCVR_TX_RADIO_RAMPUP_USECS) +#define XCVR_TX_SCHED_DELAY_USECS (XCVR_TX_START_DELAY_USECS + XCVR_PROC_DELAY_USECS) +#define XCVR_RX_SCHED_DELAY_USECS (XCVR_RX_START_DELAY_USECS + XCVR_PROC_DELAY_USECS) + +/* + * Define HW whitelist size. This is the total possible whitelist size; + * not necessarily the size that will be used (may be smaller) + */ +#define BLE_HW_WHITE_LIST_SIZE (8) + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_XCVR_ */ diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf5340/pkg.yml b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/pkg.yml new file mode 100644 index 00000000..3ff44212 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/pkg.yml @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/drivers/nrf5340 +pkg.description: BLE driver for nRF5340 systems. +pkg.author: "Apache Mynewt <dev@mynewt.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.apis: ble_driver +pkg.deps: + - nimble + - nimble/controller diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf5340/src/ble_hw.c b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/src/ble_hw.c new file mode 100644 index 00000000..bbe61697 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/src/ble_hw.c @@ -0,0 +1,475 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <stdint.h> +#include <assert.h> +#include <string.h> +#include <syscfg/syscfg.h> +#include <os/os.h> +#include <nimble/ble.h> +#include <nimble/nimble_opt.h> +#include <controller/ble_hw.h> + +#include <ble/xcvr.h> +#include <mcu/cmsis_nvic.h> +#include <os/os_trace_api.h> + +/* Total number of resolving list elements */ +#define BLE_HW_RESOLV_LIST_SIZE (16) + +/* We use this to keep track of which entries are set to valid addresses */ +static uint8_t g_ble_hw_whitelist_mask; + +/* Random number generator isr callback */ +static ble_rng_isr_cb_t ble_rng_isr_cb; + +/* If LL privacy is enabled, allocate memory for AAR */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + +/* The NRF5340 supports up to 16 IRK entries */ +#if (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE) < 16) +#define NRF_IRK_LIST_ENTRIES (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE)) +#else +#define NRF_IRK_LIST_ENTRIES (16) +#endif + +/* NOTE: each entry is 16 bytes long. */ +uint32_t g_nrf_irk_list[NRF_IRK_LIST_ENTRIES * 4]; + +/* Current number of IRK entries */ +uint8_t g_nrf_num_irks; + +#endif + +/* Returns public device address or -1 if not present */ +int +ble_hw_get_public_addr(ble_addr_t *addr) +{ + uint32_t addr_high; + uint32_t addr_low; + + /* Does FICR have a public address */ + if ((NRF_FICR_NS->DEVICEADDRTYPE & 1) != 0) { + return -1; + } + + /* Copy into device address. We can do this because we know platform */ + addr_low = NRF_FICR_NS->DEVICEADDR[0]; + addr_high = NRF_FICR_NS->DEVICEADDR[1]; + memcpy(addr->val, &addr_low, 4); + memcpy(&addr->val[4], &addr_high, 2); + addr->type = BLE_ADDR_PUBLIC; + + return 0; +} + +/* Returns random static address or -1 if not present */ +int +ble_hw_get_static_addr(ble_addr_t *addr) +{ + int rc; + + if ((NRF_FICR_NS->DEVICEADDRTYPE & 1) == 1) { + memcpy(addr->val, (void *)&NRF_FICR_NS->DEVICEADDR[0], 4); + memcpy(&addr->val[4], (void *)&NRF_FICR_NS->DEVICEADDR[1], 2); + addr->val[5] |= 0xc0; + addr->type = BLE_ADDR_RANDOM; + rc = 0; + } else { + rc = -1; + } + + return rc; +} + +/** + * Clear the whitelist + * + * @return int + */ +void +ble_hw_whitelist_clear(void) +{ + NRF_RADIO_NS->DACNF = 0; + g_ble_hw_whitelist_mask = 0; +} + +/** + * Add a device to the hw whitelist + * + * @param addr + * @param addr_type + * + * @return int 0: success, BLE error code otherwise + */ +int +ble_hw_whitelist_add(const uint8_t *addr, uint8_t addr_type) +{ + int i; + uint32_t mask; + + /* Find first ununsed device address match element */ + mask = 0x01; + for (i = 0; i < BLE_HW_WHITE_LIST_SIZE; ++i) { + if ((mask & g_ble_hw_whitelist_mask) == 0) { + NRF_RADIO_NS->DAB[i] = get_le32(addr); + NRF_RADIO_NS->DAP[i] = get_le16(addr + 4); + if (addr_type == BLE_ADDR_RANDOM) { + NRF_RADIO_NS->DACNF |= (mask << 8); + } + g_ble_hw_whitelist_mask |= mask; + return BLE_ERR_SUCCESS; + } + mask <<= 1; + } + + return BLE_ERR_MEM_CAPACITY; +} + +/** + * Remove a device from the hw whitelist + * + * @param addr + * @param addr_type + * + */ +void +ble_hw_whitelist_rmv(const uint8_t *addr, uint8_t addr_type) +{ + int i; + uint16_t dap; + uint16_t txadd; + uint32_t dab; + uint32_t mask; + + /* Find first ununsed device address match element */ + dab = get_le32(addr); + dap = get_le16(addr + 4); + txadd = NRF_RADIO_NS->DACNF >> 8; + mask = 0x01; + for (i = 0; i < BLE_HW_WHITE_LIST_SIZE; ++i) { + if (mask & g_ble_hw_whitelist_mask) { + if ((dab == NRF_RADIO_NS->DAB[i]) && (dap == NRF_RADIO_NS->DAP[i])) { + if (addr_type == !!(txadd & mask)) { + break; + } + } + } + mask <<= 1; + } + + if (i < BLE_HW_WHITE_LIST_SIZE) { + g_ble_hw_whitelist_mask &= ~mask; + NRF_RADIO_NS->DACNF &= ~mask; + } +} + +/** + * Returns the size of the whitelist in HW + * + * @return int Number of devices allowed in whitelist + */ +uint8_t +ble_hw_whitelist_size(void) +{ + return BLE_HW_WHITE_LIST_SIZE; +} + +/** + * Enable the whitelisted devices + */ +void +ble_hw_whitelist_enable(void) +{ + /* Enable the configured device addresses */ + NRF_RADIO_NS->DACNF |= g_ble_hw_whitelist_mask; +} + +/** + * Disables the whitelisted devices + */ +void +ble_hw_whitelist_disable(void) +{ + /* Disable all whitelist devices */ + NRF_RADIO_NS->DACNF &= 0x0000ff00; +} + +/** + * Boolean function which returns true ('1') if there is a match on the + * whitelist. + * + * @return int + */ +int +ble_hw_whitelist_match(void) +{ + return NRF_RADIO_NS->EVENTS_DEVMATCH; +} + +/* Encrypt data */ +int +ble_hw_encrypt_block(struct ble_encryption_block *ecb) +{ + int rc; + uint32_t end; + uint32_t err; + + /* Stop ECB */ + NRF_ECB_NS->TASKS_STOPECB = 1; + /* XXX: does task stop clear these counters? Anyway to do this quicker? */ + NRF_ECB_NS->EVENTS_ENDECB = 0; + NRF_ECB_NS->EVENTS_ERRORECB = 0; + NRF_ECB_NS->ECBDATAPTR = (uint32_t)ecb; + + /* Start ECB */ + NRF_ECB_NS->TASKS_STARTECB = 1; + + /* Wait till error or done */ + rc = 0; + while (1) { + end = NRF_ECB_NS->EVENTS_ENDECB; + err = NRF_ECB_NS->EVENTS_ERRORECB; + if (end || err) { + if (err) { + rc = -1; + } + break; + } + } + + return rc; +} + +/** + * Random number generator ISR. + */ +static void +ble_rng_isr(void) +{ + uint8_t rnum; + + os_trace_isr_enter(); + + /* No callback? Clear and disable interrupts */ + if (ble_rng_isr_cb == NULL) { + NRF_RNG_NS->INTENCLR = 1; + NRF_RNG_NS->EVENTS_VALRDY = 0; + (void)NRF_RNG_NS->SHORTS; + os_trace_isr_exit(); + return; + } + + /* If there is a value ready grab it */ + if (NRF_RNG_NS->EVENTS_VALRDY) { + NRF_RNG_NS->EVENTS_VALRDY = 0; + rnum = (uint8_t)NRF_RNG_NS->VALUE; + (*ble_rng_isr_cb)(rnum); + } + + os_trace_isr_exit(); +} + +/** + * Initialize the random number generator + * + * @param cb + * @param bias + * + * @return int + */ +int +ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) +{ + /* Set bias */ + if (bias) { + NRF_RNG_NS->CONFIG = 1; + } else { + NRF_RNG_NS->CONFIG = 0; + } + + /* If we were passed a function pointer we need to enable the interrupt */ + if (cb != NULL) { +#ifndef RIOT_VERSION + NVIC_SetPriority(RNG_IRQn, (1 << __NVIC_PRIO_BITS) - 1); +#endif +#if MYNEWT + NVIC_SetVector(RNG_IRQn, (uint32_t)ble_rng_isr); +#else + ble_npl_hw_set_isr(RNG_IRQn, ble_rng_isr); +#endif + NVIC_EnableIRQ(RNG_IRQn); + ble_rng_isr_cb = cb; + } + + return 0; +} + +/** + * Start the random number generator + * + * @return int + */ +int +ble_hw_rng_start(void) +{ + os_sr_t sr; + + /* No need for interrupt if there is no callback */ + OS_ENTER_CRITICAL(sr); + NRF_RNG_NS->EVENTS_VALRDY = 0; + if (ble_rng_isr_cb) { + NRF_RNG_NS->INTENSET = 1; + } + NRF_RNG_NS->TASKS_START = 1; + OS_EXIT_CRITICAL(sr); + + return 0; +} + +/** + * Stop the random generator + * + * @return int + */ +int +ble_hw_rng_stop(void) +{ + os_sr_t sr; + + /* No need for interrupt if there is no callback */ + OS_ENTER_CRITICAL(sr); + NRF_RNG_NS->INTENCLR = 1; + NRF_RNG_NS->TASKS_STOP = 1; + NRF_RNG_NS->EVENTS_VALRDY = 0; + OS_EXIT_CRITICAL(sr); + + return 0; +} + +/** + * Read the random number generator. + * + * @return uint8_t + */ +uint8_t +ble_hw_rng_read(void) +{ + uint8_t rnum; + + /* Wait for a sample */ + while (NRF_RNG_NS->EVENTS_VALRDY == 0) { + } + + NRF_RNG_NS->EVENTS_VALRDY = 0; + rnum = (uint8_t)NRF_RNG_NS->VALUE; + + return rnum; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +/** + * Clear the resolving list + * + * @return int + */ +void +ble_hw_resolv_list_clear(void) +{ + g_nrf_num_irks = 0; +} + +/** + * Add a device to the hw resolving list + * + * @param irk Pointer to IRK to add + * + * @return int 0: success, BLE error code otherwise + */ +int +ble_hw_resolv_list_add(uint8_t *irk) +{ + uint32_t *nrf_entry; + + /* Find first ununsed device address match element */ + if (g_nrf_num_irks == NRF_IRK_LIST_ENTRIES) { + return BLE_ERR_MEM_CAPACITY; + } + + /* Copy into irk list */ + nrf_entry = &g_nrf_irk_list[4 * g_nrf_num_irks]; + memcpy(nrf_entry, irk, 16); + + /* Add to total */ + ++g_nrf_num_irks; + return BLE_ERR_SUCCESS; +} + +/** + * Remove a device from the hw resolving list + * + * @param index Index of IRK to remove + */ +void +ble_hw_resolv_list_rmv(int index) +{ + uint32_t *irk_entry; + + if (index < g_nrf_num_irks) { + --g_nrf_num_irks; + irk_entry = &g_nrf_irk_list[index]; + if (g_nrf_num_irks > index) { + memmove(irk_entry, irk_entry + 4, 16 * (g_nrf_num_irks - index)); + } + } +} + +/** + * Returns the size of the resolving list. NOTE: this returns the maximum + * allowable entries in the HW. Configuration options may limit this. + * + * @return int Number of devices allowed in resolving list + */ +uint8_t +ble_hw_resolv_list_size(void) +{ + return BLE_HW_RESOLV_LIST_SIZE; +} + +/** + * Called to determine if the address received was resolved. + * + * @return int Negative values indicate unresolved address; positive values + * indicate index in resolving list of resolved address. + */ +int +ble_hw_resolv_list_match(void) +{ + uint32_t index; + + if (NRF_AAR_NS->EVENTS_END) { + if (NRF_AAR_NS->EVENTS_RESOLVED) { + index = NRF_AAR_NS->STATUS; + return (int)index; + } + } + + return -1; +} +#endif diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf5340/src/ble_phy.c b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/src/ble_phy.c new file mode 100644 index 00000000..e07bbaa1 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/src/ble_phy.c @@ -0,0 +1,1820 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <stdint.h> +#include <string.h> +#include <assert.h> +#include <syscfg/syscfg.h> +#include <os/os.h> +#include <nimble/ble.h> +#include <nimble/nimble_opt.h> +#include <nimble/nimble_npl.h> +#include <controller/ble_phy.h> + +#include <ble/xcvr.h> +#include <controller/ble_phy_trace.h> +#include <controller/ble_ll.h> +#include <mcu/nrf5340_net_clock.h> +#include <mcu/cmsis_nvic.h> + +/* + * NOTE: This code uses 0-5 DPPI channels so care should be taken when using + * DPPI somewhere else. + * TODO maybe we could reduce number of used channels if we reuse same channel + * for mutually exclusive events but for now make it simpler to debug. + */ + +#define DPPI_CH_TIMER0_EVENTS_COMPARE_0 0 +#define DPPI_CH_TIMER0_EVENTS_COMPARE_3 1 +#define DPPI_CH_RADIO_EVENTS_END 2 +#define DPPI_CH_RADIO_EVENTS_BCMATCH 3 +#define DPPI_CH_RADIO_EVENTS_ADDRESS 4 +#define DPPI_CH_RTC0_EVENTS_COMPARE_0 5 + +#define DPPI_CH_ENABLE_ALL (DPPIC_CHEN_CH0_Msk | DPPIC_CHEN_CH1_Msk | DPPIC_CHEN_CH2_Msk | \ + DPPIC_CHEN_CH3_Msk | DPPIC_CHEN_CH4_Msk | DPPIC_CHEN_CH5_Msk) + +#define DPPI_PUBLISH_TIMER0_EVENTS_COMPARE_0 ((DPPI_CH_TIMER0_EVENTS_COMPARE_0 << TIMER_PUBLISH_COMPARE_CHIDX_Pos) | \ + (TIMER_PUBLISH_COMPARE_EN_Enabled << TIMER_PUBLISH_COMPARE_EN_Pos)) +#define DPPI_PUBLISH_TIMER0_EVENTS_COMPARE_3 ((DPPI_CH_TIMER0_EVENTS_COMPARE_3 << TIMER_PUBLISH_COMPARE_CHIDX_Pos) | \ + (TIMER_PUBLISH_COMPARE_EN_Enabled << TIMER_PUBLISH_COMPARE_EN_Pos)) +#define DPPI_PUBLISH_RADIO_EVENTS_END ((DPPI_CH_RADIO_EVENTS_END << RADIO_PUBLISH_END_CHIDX_Pos) | \ + (RADIO_PUBLISH_END_EN_Enabled << RADIO_PUBLISH_END_EN_Pos)) +#define DPPI_PUBLISH_RADIO_EVENTS_BCMATCH ((DPPI_CH_RADIO_EVENTS_BCMATCH << RADIO_PUBLISH_BCMATCH_CHIDX_Pos) | \ + (RADIO_PUBLISH_BCMATCH_EN_Enabled << RADIO_PUBLISH_BCMATCH_EN_Pos)) +#define DPPI_PUBLISH_RADIO_EVENTS_ADDRESS ((DPPI_CH_RADIO_EVENTS_ADDRESS << RADIO_PUBLISH_ADDRESS_CHIDX_Pos) | \ + (RADIO_PUBLISH_ADDRESS_EN_Enabled << RADIO_PUBLISH_ADDRESS_EN_Pos)) +#define DPPI_PUBLISH_RTC0_EVENTS_COMPARE_0 ((DPPI_CH_RTC0_EVENTS_COMPARE_0 << RTC_PUBLISH_COMPARE_CHIDX_Pos) | \ + (RTC_PUBLISH_COMPARE_EN_Enabled << RTC_PUBLISH_COMPARE_EN_Pos)) + +#define DPPI_SUBSCRIBE_TIMER0_TASKS_START(_enable) ((DPPI_CH_RTC0_EVENTS_COMPARE_0 << TIMER_SUBSCRIBE_START_CHIDX_Pos) | \ + ((_enable) << TIMER_SUBSCRIBE_START_EN_Pos)) +#define DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE1(_enable) ((DPPI_CH_RADIO_EVENTS_ADDRESS << TIMER_SUBSCRIBE_CAPTURE_CHIDX_Pos) | \ + ((_enable) << TIMER_SUBSCRIBE_CAPTURE_EN_Pos)) +#define DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE2(_enable) ((DPPI_CH_RADIO_EVENTS_END << TIMER_SUBSCRIBE_CAPTURE_CHIDX_Pos) | \ + ((_enable) << TIMER_SUBSCRIBE_CAPTURE_EN_Pos)) +#define DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE3(_enable) ((DPPI_CH_RADIO_EVENTS_ADDRESS << TIMER_SUBSCRIBE_CAPTURE_CHIDX_Pos) | \ + ((_enable) << TIMER_SUBSCRIBE_CAPTURE_EN_Pos)) +#define DPPI_SUBSCRIBE_RADIO_TASKS_DISABLE(_enable) ((DPPI_CH_TIMER0_EVENTS_COMPARE_3 << RADIO_SUBSCRIBE_DISABLE_CHIDX_Pos) | \ + ((_enable) << RADIO_SUBSCRIBE_DISABLE_EN_Pos)) +#define DPPI_SUBSCRIBE_RADIO_TASKS_RXEN(_enable) ((DPPI_CH_TIMER0_EVENTS_COMPARE_0 << RADIO_SUBSCRIBE_RXEN_CHIDX_Pos) | \ + ((_enable) << RADIO_SUBSCRIBE_RXEN_EN_Pos)) +#define DPPI_SUBSCRIBE_RADIO_TASKS_TXEN(_enable) ((DPPI_CH_TIMER0_EVENTS_COMPARE_0 << RADIO_SUBSCRIBE_TXEN_CHIDX_Pos) | \ + ((_enable) << RADIO_SUBSCRIBE_TXEN_EN_Pos)) +#define DPPI_SUBSCRIBE_AAR_TASKS_START(_enable) ((DPPI_CH_RADIO_EVENTS_BCMATCH << AAR_SUBSCRIBE_START_CHIDX_Pos) | \ + ((_enable) << AAR_SUBSCRIBE_START_EN_Pos)) +#define DPPI_SUBSCRIBE_CCM_TASKS_CRYPT(_enable) ((DPPI_CH_RADIO_EVENTS_ADDRESS << CCM_SUBSCRIBE_CRYPT_CHIDX_Pos) | \ + ((_enable) << CCM_SUBSCRIBE_CRYPT_EN_Pos)) + +extern uint8_t g_nrf_num_irks; +extern uint32_t g_nrf_irk_list[]; + +/* To disable all radio interrupts */ +#define NRF_RADIO_IRQ_MASK_ALL (0x34FF) + +/* + * We configure the nrf with a 1 byte S0 field, 8 bit length field, and + * zero bit S1 field. The preamble is 8 bits long. + */ +#define NRF_LFLEN_BITS (8) +#define NRF_S0LEN (1) +#define NRF_S1LEN_BITS (0) +#define NRF_CILEN_BITS (2) +#define NRF_TERMLEN_BITS (3) + +/* Maximum length of frames */ +#define NRF_MAXLEN (255) +#define NRF_BALEN (3) /* For base address of 3 bytes */ + +/* NRF_RADIO_NS->PCNF0 configuration values */ +#define NRF_PCNF0 (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | \ + (RADIO_PCNF0_S1INCL_Msk) | \ + (NRF_S0LEN << RADIO_PCNF0_S0LEN_Pos) | \ + (NRF_S1LEN_BITS << RADIO_PCNF0_S1LEN_Pos) +#define NRF_PCNF0_1M (NRF_PCNF0) | \ + (RADIO_PCNF0_PLEN_8bit << RADIO_PCNF0_PLEN_Pos) +#define NRF_PCNF0_2M (NRF_PCNF0) | \ + (RADIO_PCNF0_PLEN_16bit << RADIO_PCNF0_PLEN_Pos) +#define NRF_PCNF0_CODED (NRF_PCNF0) | \ + (RADIO_PCNF0_PLEN_LongRange << RADIO_PCNF0_PLEN_Pos) | \ + (NRF_CILEN_BITS << RADIO_PCNF0_CILEN_Pos) | \ + (NRF_TERMLEN_BITS << RADIO_PCNF0_TERMLEN_Pos) + +/* BLE PHY data structure */ +struct ble_phy_obj { + uint8_t phy_stats_initialized; + int8_t phy_txpwr_dbm; + uint8_t phy_chan; + uint8_t phy_state; + uint8_t phy_transition; + uint8_t phy_transition_late; + uint8_t phy_rx_started; + uint8_t phy_encrypted; + uint8_t phy_privacy; + uint8_t phy_tx_pyld_len; + uint8_t phy_cur_phy_mode; + uint8_t phy_tx_phy_mode; + uint8_t phy_rx_phy_mode; + uint8_t phy_bcc_offset; + int8_t rx_pwr_compensation; + uint32_t phy_aar_scratch; + uint32_t phy_access_address; + struct ble_mbuf_hdr rxhdr; + void *txend_arg; + ble_phy_tx_end_func txend_cb; + uint32_t phy_start_cputime; +}; +struct ble_phy_obj g_ble_phy_data; + +/* XXX: if 27 byte packets desired we can make this smaller */ +/* Global transmit/receive buffer */ +static uint32_t g_ble_phy_tx_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4]; +static uint32_t g_ble_phy_rx_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4]; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/* Make sure word-aligned for faster copies */ +static uint32_t g_ble_phy_enc_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4]; +#endif + +/* RF center frequency for each channel index (offset from 2400 MHz) */ +static const uint8_t g_ble_phy_chan_freq[BLE_PHY_NUM_CHANS] = { + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, /* 0-9 */ + 24, 28, 30, 32, 34, 36, 38, 40, 42, 44, /* 10-19 */ + 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, /* 20-29 */ + 66, 68, 70, 72, 74, 76, 78, 2, 26, 80, /* 30-39 */ +}; + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +/* packet start offsets (in usecs) */ +static const uint16_t g_ble_phy_mode_pkt_start_off[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 40, + [BLE_PHY_MODE_2M] = 24, + [BLE_PHY_MODE_CODED_125KBPS] = 376, + [BLE_PHY_MODE_CODED_500KBPS] = 376 +}; +#endif + +/* Various radio timings */ +/* Radio ramp-up times in usecs (fast mode) */ +#define BLE_PHY_T_TXENFAST (XCVR_TX_RADIO_RAMPUP_USECS) +#define BLE_PHY_T_RXENFAST (XCVR_RX_RADIO_RAMPUP_USECS) + +/* delay between EVENTS_READY and start of tx */ +static const uint8_t g_ble_phy_t_txdelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 4, + [BLE_PHY_MODE_2M] = 3, + [BLE_PHY_MODE_CODED_125KBPS] = 5, + [BLE_PHY_MODE_CODED_500KBPS] = 5 +}; +/* delay between EVENTS_END and end of txd packet */ +static const uint8_t g_ble_phy_t_txenddelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 4, + [BLE_PHY_MODE_2M] = 3, + [BLE_PHY_MODE_CODED_125KBPS] = 9, + [BLE_PHY_MODE_CODED_500KBPS] = 3 +}; +/* delay between rxd access address (w/ TERM1 for coded) and EVENTS_ADDRESS */ +static const uint8_t g_ble_phy_t_rxaddrdelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 6, + [BLE_PHY_MODE_2M] = 2, + [BLE_PHY_MODE_CODED_125KBPS] = 17, + [BLE_PHY_MODE_CODED_500KBPS] = 17 +}; +/* delay between end of rxd packet and EVENTS_END */ +static const uint8_t g_ble_phy_t_rxenddelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 6, + [BLE_PHY_MODE_2M] = 2, + [BLE_PHY_MODE_CODED_125KBPS] = 27, + [BLE_PHY_MODE_CODED_500KBPS] = 22 +}; + +/* Statistics */ +STATS_SECT_START(ble_phy_stats) +STATS_SECT_ENTRY(phy_isrs) +STATS_SECT_ENTRY(tx_good) +STATS_SECT_ENTRY(tx_fail) +STATS_SECT_ENTRY(tx_late) +STATS_SECT_ENTRY(tx_bytes) +STATS_SECT_ENTRY(rx_starts) +STATS_SECT_ENTRY(rx_aborts) +STATS_SECT_ENTRY(rx_valid) +STATS_SECT_ENTRY(rx_crc_err) +STATS_SECT_ENTRY(rx_late) +STATS_SECT_ENTRY(radio_state_errs) +STATS_SECT_ENTRY(rx_hw_err) +STATS_SECT_ENTRY(tx_hw_err) +STATS_SECT_END +STATS_SECT_DECL(ble_phy_stats) ble_phy_stats; + +STATS_NAME_START(ble_phy_stats) +STATS_NAME(ble_phy_stats, phy_isrs) +STATS_NAME(ble_phy_stats, tx_good) +STATS_NAME(ble_phy_stats, tx_fail) +STATS_NAME(ble_phy_stats, tx_late) +STATS_NAME(ble_phy_stats, tx_bytes) +STATS_NAME(ble_phy_stats, rx_starts) +STATS_NAME(ble_phy_stats, rx_aborts) +STATS_NAME(ble_phy_stats, rx_valid) +STATS_NAME(ble_phy_stats, rx_crc_err) +STATS_NAME(ble_phy_stats, rx_late) +STATS_NAME(ble_phy_stats, radio_state_errs) +STATS_NAME(ble_phy_stats, rx_hw_err) +STATS_NAME(ble_phy_stats, tx_hw_err) +STATS_NAME_END(ble_phy_stats) + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/* + * Per nordic, the number of bytes needed for scratch is 16 + MAX_PKT_SIZE. + * However, when I used a smaller size it still overwrote the scratchpad. Until + * I figure this out I am just going to allocate 67 words so we have enough + * space for 267 bytes of scratch. I used 268 bytes since not sure if this + * needs to be aligned and burning a byte is no big deal. + * + *#define NRF_ENC_SCRATCH_WORDS (((MYNEWT_VAL(BLE_LL_MAX_PKT_SIZE) + 16) + 3) / 4) + */ +#define NRF_ENC_SCRATCH_WORDS (67) + +static uint32_t nrf_encrypt_scratchpad[NRF_ENC_SCRATCH_WORDS]; + +struct nrf_ccm_data { + uint8_t key[16]; + uint64_t pkt_counter; + uint8_t dir_bit; + uint8_t iv[8]; +} __attribute__((packed)); + +static struct nrf_ccm_data nrf_ccm_data; +#endif + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + +uint32_t +ble_phy_mode_pdu_start_off(int phy_mode) +{ + return g_ble_phy_mode_pkt_start_off[phy_mode]; +} + +static void +ble_phy_mode_apply(uint8_t phy_mode) +{ + if (phy_mode == g_ble_phy_data.phy_cur_phy_mode) { + return; + } + + switch (phy_mode) { + case BLE_PHY_MODE_1M: + NRF_RADIO_NS->MODE = RADIO_MODE_MODE_Ble_1Mbit; + NRF_RADIO_NS->PCNF0 = NRF_PCNF0_1M; + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case BLE_PHY_MODE_2M: + NRF_RADIO_NS->MODE = RADIO_MODE_MODE_Ble_2Mbit; + NRF_RADIO_NS->PCNF0 = NRF_PCNF0_2M; + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case BLE_PHY_MODE_CODED_125KBPS: + NRF_RADIO_NS->MODE = RADIO_MODE_MODE_Ble_LR125Kbit; + NRF_RADIO_NS->PCNF0 = NRF_PCNF0_CODED; + break; + case BLE_PHY_MODE_CODED_500KBPS: + NRF_RADIO_NS->MODE = RADIO_MODE_MODE_Ble_LR500Kbit; + NRF_RADIO_NS->PCNF0 = NRF_PCNF0_CODED; + break; +#endif + default: + assert(0); + } + + g_ble_phy_data.phy_cur_phy_mode = phy_mode; +} +#endif + +void +ble_phy_mode_set(uint8_t tx_phy_mode, uint8_t rx_phy_mode) +{ + g_ble_phy_data.phy_tx_phy_mode = tx_phy_mode; + g_ble_phy_data.phy_rx_phy_mode = rx_phy_mode; +} + +int +ble_phy_get_cur_phy(void) +{ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + switch (g_ble_phy_data.phy_cur_phy_mode) { + case BLE_PHY_MODE_1M: + return BLE_PHY_1M; + case BLE_PHY_MODE_2M: + return BLE_PHY_2M; + case BLE_PHY_MODE_CODED_125KBPS: + case BLE_PHY_MODE_CODED_500KBPS: + return BLE_PHY_CODED; + default: + assert(0); + return -1; + } +#else + return BLE_PHY_1M; +#endif +} + +void +ble_phy_rxpdu_copy(uint8_t *dptr, struct os_mbuf *rxpdu) +{ + uint32_t rem_len; + uint32_t copy_len; + uint32_t block_len; + uint32_t block_rem_len; + void *dst; + void *src; + struct os_mbuf * om; + + /* Better be aligned */ + assert(((uint32_t)dptr & 3) == 0); + + block_len = rxpdu->om_omp->omp_databuf_len; + rem_len = OS_MBUF_PKTHDR(rxpdu)->omp_len; + src = dptr; + + /* + * Setup for copying from first mbuf which is shorter due to packet header + * and extra leading space + */ + copy_len = block_len - rxpdu->om_pkthdr_len - 4; + om = rxpdu; + dst = om->om_data; + + while (true) { + /* + * Always copy blocks of length aligned to word size, only last mbuf + * will have remaining non-word size bytes appended. + */ + block_rem_len = copy_len; + copy_len = min(copy_len, rem_len); + copy_len &= ~3; + + dst = om->om_data; + om->om_len = copy_len; + rem_len -= copy_len; + block_rem_len -= copy_len; + + __asm__ volatile (".syntax unified \n" + " mov r4, %[len] \n" + " b 2f \n" + "1: ldr r3, [%[src], %[len]] \n" + " str r3, [%[dst], %[len]] \n" + "2: subs %[len], #4 \n" + " bpl 1b \n" + " adds %[src], %[src], r4 \n" + " adds %[dst], %[dst], r4 \n" + : [dst] "+r" (dst), [src] "+r" (src), + [len] "+r" (copy_len) + : + : "r3", "r4", "memory" + ); + + if ((rem_len < 4) && (block_rem_len >= rem_len)) { + break; + } + + /* Move to next mbuf */ + om = SLIST_NEXT(om, om_next); + copy_len = block_len; + } + + /* Copy remaining bytes, if any, to last mbuf */ + om->om_len += rem_len; + __asm__ volatile (".syntax unified \n" + " b 2f \n" + "1: ldrb r3, [%[src], %[len]] \n" + " strb r3, [%[dst], %[len]] \n" + "2: subs %[len], #1 \n" + " bpl 1b \n" + : [len] "+r" (rem_len) + : [dst] "r" (dst), [src] "r" (src) + : "r3", "memory" + ); + + /* Copy header */ + memcpy(BLE_MBUF_HDR_PTR(rxpdu), &g_ble_phy_data.rxhdr, + sizeof(struct ble_mbuf_hdr)); +} + +/** + * Called when we want to wait if the radio is in either the rx or tx + * disable states. We want to wait until that state is over before doing + * anything to the radio + */ +static void +nrf_wait_disabled(void) +{ + uint32_t state; + + state = NRF_RADIO_NS->STATE; + if (state != RADIO_STATE_STATE_Disabled) { + if ((state == RADIO_STATE_STATE_RxDisable) || + (state == RADIO_STATE_STATE_TxDisable)) { + /* This will end within a short time (6 usecs). Just poll */ + while (NRF_RADIO_NS->STATE == state) { + /* If this fails, something is really wrong. Should last + * no more than 6 usecs + */ + } + } + } +} + +static int +ble_phy_set_start_time(uint32_t cputime, uint8_t rem_usecs, bool tx) +{ + uint32_t next_cc; + uint32_t cur_cc; + uint32_t cntr; + uint32_t delta; + + /* + * We need to adjust start time to include radio ramp-up and TX pipeline + * delay (the latter only if applicable, so only for TX). + * + * Radio ramp-up time is 40 usecs and TX delay is 3 or 5 usecs depending on + * phy, thus we'll offset RTC by 2 full ticks (61 usecs) and then compensate + * using TIMER0 with 1 usec precision. + */ + + cputime -= 2; + rem_usecs += 61; + if (tx) { + rem_usecs -= BLE_PHY_T_TXENFAST; + rem_usecs -= g_ble_phy_t_txdelay[g_ble_phy_data.phy_cur_phy_mode]; + } else { + rem_usecs -= BLE_PHY_T_RXENFAST; + } + + /* + * rem_usecs will be no more than 2 ticks, but if it is more than single + * tick then we should better count one more low-power tick rather than + * 30 high-power usecs. Also make sure we don't set TIMER0 CC to 0 as the + * compare won't occur. + */ + + if (rem_usecs > 30) { + cputime++; + rem_usecs -= 30; + } + + /* + * Can we set the RTC compare to start TIMER0? We can do it if: + * a) Current compare value is not N+1 or N+2 ticks from current + * counter. + * b) The value we want to set is not at least N+2 from current + * counter. + * + * NOTE: since the counter can tick 1 while we do these calculations we + * need to account for it. + */ + next_cc = cputime & 0xffffff; + cur_cc = NRF_RTC0_NS->CC[0]; + cntr = NRF_RTC0_NS->COUNTER; + + delta = (cur_cc - cntr) & 0xffffff; + if ((delta <= 3) && (delta != 0)) { + return -1; + } + delta = (next_cc - cntr) & 0xffffff; + if ((delta & 0x800000) || (delta < 3)) { + return -1; + } + + /* Clear and set TIMER0 to fire off at proper time */ + NRF_TIMER0_NS->TASKS_CLEAR = 1; + NRF_TIMER0_NS->CC[0] = rem_usecs; + NRF_TIMER0_NS->EVENTS_COMPARE[0] = 0; + + /* Set RTC compare to start TIMER0 */ + NRF_RTC0_NS->EVENTS_COMPARE[0] = 0; + NRF_RTC0_NS->CC[0] = next_cc; + NRF_RTC0_NS->EVTENSET = RTC_EVTENSET_COMPARE0_Msk; + + /* Enable PPI */ + NRF_TIMER0_NS->SUBSCRIBE_START = DPPI_SUBSCRIBE_TIMER0_TASKS_START(1); + + /* Store the cputime at which we set the RTC */ + g_ble_phy_data.phy_start_cputime = cputime; + + return 0; +} + +static int +ble_phy_set_start_now(void) +{ + os_sr_t sr; + uint32_t now; + + OS_ENTER_CRITICAL(sr); + + /* + * Set TIMER0 to fire immediately. We can't set CC to 0 as compare will not + * occur in such case. + */ + NRF_TIMER0_NS->TASKS_CLEAR = 1; + NRF_TIMER0_NS->CC[0] = 1; + NRF_TIMER0_NS->EVENTS_COMPARE[0] = 0; + + /* + * Set RTC compare to start TIMER0. We need to set it to at least N+2 ticks + * from current value to guarantee triggering compare event, but let's set + * it to N+3 to account for possible extra tick on RTC0 during these + * operations. + */ + now = os_cputime_get32(); + NRF_RTC0_NS->EVENTS_COMPARE[0] = 0; + NRF_RTC0_NS->CC[0] = now + 3; + NRF_RTC0_NS->EVTENSET = RTC_EVTENSET_COMPARE0_Msk; + + /* Enable PPI */ + NRF_TIMER0_NS->SUBSCRIBE_START = DPPI_SUBSCRIBE_TIMER0_TASKS_START(1); + + /* + * Store the cputime at which we set the RTC + * + * XXX Compare event may be triggered on previous CC value (if it was set to + * less than N+2) so in rare cases actual start time may be 2 ticks earlier + * than what we expect. Since this is only used on RX, it may cause AUX scan + * to be scheduled 1 or 2 ticks too late so we'll miss it - it's acceptable + * for now. + */ + g_ble_phy_data.phy_start_cputime = now + 3; + + OS_EXIT_CRITICAL(sr); + + return 0; +} + +void +ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs) +{ + uint32_t end_time; + uint8_t phy; + + phy = g_ble_phy_data.phy_cur_phy_mode; + + if (txrx == BLE_PHY_WFR_ENABLE_TXRX) { + /* RX shall start exactly T_IFS after TX end captured in CC[2] */ + end_time = NRF_TIMER0_NS->CC[2] + BLE_LL_IFS; + /* Adjust for delay between EVENT_END and actual TX end time */ + end_time += g_ble_phy_t_txenddelay[tx_phy_mode]; + /* Wait a bit longer due to allowed active clock accuracy */ + end_time += 2; + /* + * It's possible that we'll capture PDU start time at the end of timer + * cycle and since wfr expires at the beginning of calculated timer + * cycle it can be almost 1 usec too early. Let's compensate for this + * by waiting 1 usec more. + */ + end_time += 1; + } else { + /* + * RX shall start no later than wfr_usecs after RX enabled. + * CC[0] is the time of RXEN so adjust for radio ram-up. + * Do not add jitter since this is already covered by LL. + */ + end_time = NRF_TIMER0_NS->CC[0] + BLE_PHY_T_RXENFAST + wfr_usecs; + } + + /* + * Note: on LE Coded EVENT_ADDRESS is fired after TERM1 is received, so + * we are actually calculating relative to start of packet payload + * which is fine. + */ + + /* Adjust for receiving access address since this triggers EVENT_ADDRESS */ + end_time += ble_phy_mode_pdu_start_off(phy); + /* Adjust for delay between actual access address RX and EVENT_ADDRESS */ + end_time += g_ble_phy_t_rxaddrdelay[phy]; + + /* wfr_secs is the time from rxen until timeout */ + NRF_TIMER0_NS->CC[3] = end_time; + NRF_TIMER0_NS->EVENTS_COMPARE[3] = 0; + + /* Subscribe for wait for response events */ + NRF_TIMER0_NS->SUBSCRIBE_CAPTURE[3] = DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE3(1); + NRF_RADIO_NS->SUBSCRIBE_DISABLE = DPPI_SUBSCRIBE_RADIO_TASKS_DISABLE(1); + + /* Enable the disabled interrupt so we time out on events compare */ + NRF_RADIO_NS->INTENSET = RADIO_INTENSET_DISABLED_Msk; + + /* + * It may happen that if CPU is halted for a brief moment (e.g. during flash + * erase or write), TIMER0 already counted past CC[3] and thus wfr will not + * fire as expected. In case this happened, let's just disable PPIs for wfr + * and trigger wfr manually (i.e. disable radio). + * + * Note that the same applies to RX start time set in CC[0] but since it + * should fire earlier than wfr, fixing wfr is enough. + * + * CC[1] is only used as a reference on RX start, we do not need it here so + * it can be used to read TIMER0 counter. + */ + NRF_TIMER0_NS->TASKS_CAPTURE[1] = 1; + if (NRF_TIMER0_NS->CC[1] > NRF_TIMER0_NS->CC[3]) { + /* Unsubscribe from wfr events */ + NRF_TIMER0_NS->SUBSCRIBE_CAPTURE[3] = DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE3(0); + NRF_RADIO_NS->SUBSCRIBE_DISABLE = DPPI_SUBSCRIBE_RADIO_TASKS_DISABLE(0); + + NRF_RADIO_NS->TASKS_DISABLE = 1; + } +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +static uint32_t +ble_phy_get_ccm_datarate(void) +{ +#if BLE_LL_BT5_PHY_SUPPORTED + switch (g_ble_phy_data.phy_cur_phy_mode) { + case BLE_PHY_MODE_1M: + return CCM_MODE_DATARATE_1Mbit << CCM_MODE_DATARATE_Pos; + case BLE_PHY_MODE_2M: + return CCM_MODE_DATARATE_2Mbit << CCM_MODE_DATARATE_Pos; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case BLE_PHY_MODE_CODED_125KBPS: + return CCM_MODE_DATARATE_125Kbps << CCM_MODE_DATARATE_Pos; + case BLE_PHY_MODE_CODED_500KBPS: + return CCM_MODE_DATARATE_500Kbps << CCM_MODE_DATARATE_Pos; +#endif + } + + assert(0); + return 0; +#else + return CCM_MODE_DATARATE_1Mbit << CCM_MODE_DATARATE_Pos; +#endif +} +#endif + +/** + * Setup transceiver for receive. + */ +static void +ble_phy_rx_xcvr_setup(void) +{ + uint8_t *dptr; + + dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; + dptr += 3; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + NRF_RADIO_NS->PACKETPTR = (uint32_t)&g_ble_phy_enc_buf[0]; + NRF_CCM_NS->INPTR = (uint32_t)&g_ble_phy_enc_buf[0]; + NRF_CCM_NS->OUTPTR = (uint32_t)dptr; + NRF_CCM_NS->SCRATCHPTR = (uint32_t)&nrf_encrypt_scratchpad[0]; + NRF_CCM_NS->MODE = CCM_MODE_LENGTH_Msk | CCM_MODE_MODE_Decryption | + ble_phy_get_ccm_datarate(); + NRF_CCM_NS->CNFPTR = (uint32_t)&nrf_ccm_data; + NRF_CCM_NS->SHORTS = 0; + NRF_CCM_NS->EVENTS_ERROR = 0; + NRF_CCM_NS->EVENTS_ENDCRYPT = 0; + NRF_CCM_NS->TASKS_KSGEN = 1; + + /* Subscribe to radio address event */ + NRF_CCM_NS->SUBSCRIBE_CRYPT = DPPI_SUBSCRIBE_CCM_TASKS_CRYPT(1); + } else { + NRF_RADIO_NS->PACKETPTR = (uint32_t)dptr; + } +#else + NRF_RADIO_NS->PACKETPTR = (uint32_t)dptr; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (g_ble_phy_data.phy_privacy) { + NRF_AAR_NS->ENABLE = AAR_ENABLE_ENABLE_Enabled; + NRF_AAR_NS->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; + NRF_AAR_NS->SCRATCHPTR = (uint32_t)&g_ble_phy_data.phy_aar_scratch; + NRF_AAR_NS->EVENTS_END = 0; + NRF_AAR_NS->EVENTS_RESOLVED = 0; + NRF_AAR_NS->EVENTS_NOTRESOLVED = 0; + } else { + if (g_ble_phy_data.phy_encrypted == 0) { + NRF_AAR_NS->ENABLE = AAR_ENABLE_ENABLE_Disabled; + } + } +#endif + + /* Turn off trigger TXEN on output compare match and AAR on bcmatch */ + NRF_RADIO_NS->SUBSCRIBE_TXEN = DPPI_SUBSCRIBE_RADIO_TASKS_TXEN(0); + NRF_AAR_NS->SUBSCRIBE_START = DPPI_SUBSCRIBE_AAR_TASKS_START(0); + + /* Reset the rx started flag. Used for the wait for response */ + g_ble_phy_data.phy_rx_started = 0; + g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; + +#if BLE_LL_BT5_PHY_SUPPORTED + /* + * On Coded PHY there are CI and TERM1 fields before PDU starts so we need + * to take this into account when setting up BCC. + */ + if (g_ble_phy_data.phy_cur_phy_mode == BLE_PHY_MODE_CODED_125KBPS || + g_ble_phy_data.phy_cur_phy_mode == BLE_PHY_MODE_CODED_500KBPS) { + g_ble_phy_data.phy_bcc_offset = 5; + } else { + g_ble_phy_data.phy_bcc_offset = 0; + } +#else + g_ble_phy_data.phy_bcc_offset = 0; +#endif + + /* I want to know when 1st byte received (after address) */ + NRF_RADIO_NS->BCC = 8 + g_ble_phy_data.phy_bcc_offset; /* in bits */ + NRF_RADIO_NS->EVENTS_ADDRESS = 0; + NRF_RADIO_NS->EVENTS_DEVMATCH = 0; + NRF_RADIO_NS->EVENTS_BCMATCH = 0; + NRF_RADIO_NS->EVENTS_RSSIEND = 0; + NRF_RADIO_NS->EVENTS_CRCOK = 0; + NRF_RADIO_NS->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | + RADIO_SHORTS_READY_START_Msk | + RADIO_SHORTS_ADDRESS_BCSTART_Msk | + RADIO_SHORTS_ADDRESS_RSSISTART_Msk | + RADIO_SHORTS_DISABLED_RSSISTOP_Msk; + + NRF_RADIO_NS->INTENSET = RADIO_INTENSET_ADDRESS_Msk; +} + +/** + * Called from interrupt context when the transmit ends + * + */ +static void +ble_phy_tx_end_isr(void) +{ + uint8_t tx_phy_mode; + uint8_t was_encrypted; + uint8_t transition; + uint32_t rx_time; + uint32_t wfr_time; + + /* Store PHY on which we've just transmitted smth */ + tx_phy_mode = g_ble_phy_data.phy_cur_phy_mode; + + /* If this transmission was encrypted we need to remember it */ + was_encrypted = g_ble_phy_data.phy_encrypted; + (void)was_encrypted; + + /* Better be in TX state! */ + assert(g_ble_phy_data.phy_state == BLE_PHY_STATE_TX); + + /* Clear events and clear interrupt on disabled event */ + NRF_RADIO_NS->EVENTS_DISABLED = 0; + NRF_RADIO_NS->INTENCLR = RADIO_INTENCLR_DISABLED_Msk; + NRF_RADIO_NS->EVENTS_END = 0; + wfr_time = NRF_RADIO_NS->SHORTS; + (void)wfr_time; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* + * XXX: not sure what to do. We had a HW error during transmission. + * For now I just count a stat but continue on like all is good. + */ + if (was_encrypted) { + if (NRF_CCM_NS->EVENTS_ERROR) { + STATS_INC(ble_phy_stats, tx_hw_err); + NRF_CCM_NS->EVENTS_ERROR = 0; + } + } +#endif + + /* Call transmit end callback */ + if (g_ble_phy_data.txend_cb) { + g_ble_phy_data.txend_cb(g_ble_phy_data.txend_arg); + } + + transition = g_ble_phy_data.phy_transition; + if (transition == BLE_PHY_TRANSITION_TX_RX) { + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_rx_phy_mode); +#endif + + /* Packet pointer needs to be reset. */ + ble_phy_rx_xcvr_setup(); + + ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_TXRX, tx_phy_mode, 0); + + /* Schedule RX exactly T_IFS after TX end captured in CC[2] */ + rx_time = NRF_TIMER0_NS->CC[2] + BLE_LL_IFS; + /* Adjust for delay between EVENT_END and actual TX end time */ + rx_time += g_ble_phy_t_txenddelay[tx_phy_mode]; + /* Adjust for radio ramp-up */ + rx_time -= BLE_PHY_T_RXENFAST; + /* Start listening a bit earlier due to allowed active clock accuracy */ + rx_time -= 2; + + NRF_TIMER0_NS->CC[0] = rx_time; + NRF_TIMER0_NS->EVENTS_COMPARE[0] = 0; + + /* Start radio on timer */ + NRF_RADIO_NS->SUBSCRIBE_RXEN = DPPI_SUBSCRIBE_RADIO_TASKS_RXEN(1); + + } else { + NRF_TIMER0_NS->TASKS_STOP = 1; + NRF_TIMER0_NS->TASKS_SHUTDOWN = 1; + + NRF_TIMER0_NS->SUBSCRIBE_CAPTURE[3] = DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE3(0); + NRF_RADIO_NS->SUBSCRIBE_DISABLE = DPPI_SUBSCRIBE_RADIO_TASKS_DISABLE(0); + NRF_RADIO_NS->SUBSCRIBE_TXEN = DPPI_SUBSCRIBE_RADIO_TASKS_TXEN(0); + NRF_TIMER0_NS->SUBSCRIBE_START = DPPI_SUBSCRIBE_TIMER0_TASKS_START(0); + + assert(transition == BLE_PHY_TRANSITION_NONE); + } +} + +static inline uint8_t +ble_phy_get_cur_rx_phy_mode(void) +{ + uint8_t phy; + + phy = g_ble_phy_data.phy_cur_phy_mode; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + /* + * For Coded PHY mode can be set to either codings since actual coding is + * set in packet header. However, here we need actual coding of received + * packet as this determines pipeline delays so need to figure this out + * using CI field. + */ + if ((phy == BLE_PHY_MODE_CODED_125KBPS) || + (phy == BLE_PHY_MODE_CODED_500KBPS)) { + phy = NRF_RADIO_NS->PDUSTAT & RADIO_PDUSTAT_CISTAT_Msk ? + BLE_PHY_MODE_CODED_500KBPS : BLE_PHY_MODE_CODED_125KBPS; + } +#endif + + return phy; +} + +static void +ble_phy_rx_end_isr(void) +{ + int rc; + uint8_t *dptr; + uint8_t crcok; + uint32_t tx_time; + struct ble_mbuf_hdr *ble_hdr; + + /* Clear events and clear interrupt */ + NRF_RADIO_NS->EVENTS_END = 0; + NRF_RADIO_NS->INTENCLR = RADIO_INTENCLR_END_Msk; + + /* Disable automatic RXEN */ + NRF_RADIO_NS->SUBSCRIBE_RXEN = DPPI_SUBSCRIBE_RADIO_TASKS_RXEN(0); + + /* Set RSSI and CRC status flag in header */ + ble_hdr = &g_ble_phy_data.rxhdr; + assert(NRF_RADIO_NS->EVENTS_RSSIEND != 0); + ble_hdr->rxinfo.rssi = (-1 * NRF_RADIO_NS->RSSISAMPLE) + + g_ble_phy_data.rx_pwr_compensation; + + dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; + dptr += 3; + + /* Count PHY crc errors and valid packets */ + crcok = NRF_RADIO_NS->EVENTS_CRCOK; + if (!crcok) { + STATS_INC(ble_phy_stats, rx_crc_err); + } else { + STATS_INC(ble_phy_stats, rx_valid); + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + /* Only set MIC failure flag if frame is not zero length */ + if ((dptr[1] != 0) && (NRF_CCM_NS->MICSTATUS == 0)) { + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE; + } + + /* + * XXX: not sure how to deal with this. This should not + * be a MIC failure but we should not hand it up. I guess + * this is just some form of rx error and that is how we + * handle it? For now, just set CRC error flags + */ + if (NRF_CCM_NS->EVENTS_ERROR) { + STATS_INC(ble_phy_stats, rx_hw_err); + ble_hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_CRC_OK; + } + + /* + * XXX: This is a total hack work-around for now but I dont + * know what else to do. If ENDCRYPT is not set and we are + * encrypted we need to not trust this frame and drop it. + */ + if (NRF_CCM_NS->EVENTS_ENDCRYPT == 0) { + STATS_INC(ble_phy_stats, rx_hw_err); + ble_hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_CRC_OK; + } + } +#endif + } + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_tx_phy_mode); +#endif + + /* + * Let's schedule TX now and we will just cancel it after processing RXed + * packet if we don't need TX. + * + * We need this to initiate connection in case AUX_CONNECT_REQ was sent on + * LE Coded S8. In this case the time we process RXed packet is roughly the + * same as the limit when we need to have TX scheduled (i.e. TIMER0 and PPI + * armed) so we may simply miss the slot and set the timer in the past. + * + * When TX is scheduled in advance, we may event process packet a bit longer + * during radio ramp-up - this gives us extra 40 usecs which is more than + * enough. + */ + + /* Schedule TX exactly T_IFS after RX end captured in CC[2] */ + tx_time = NRF_TIMER0_NS->CC[2] + BLE_LL_IFS; + /* Adjust for delay between actual RX end time and EVENT_END */ + tx_time -= g_ble_phy_t_rxenddelay[ble_hdr->rxinfo.phy_mode]; + /* Adjust for radio ramp-up */ + tx_time -= BLE_PHY_T_TXENFAST; + /* Adjust for delay between EVENT_READY and actual TX start time */ + tx_time -= g_ble_phy_t_txdelay[g_ble_phy_data.phy_cur_phy_mode]; + + NRF_TIMER0_NS->CC[0] = tx_time; + NRF_TIMER0_NS->EVENTS_COMPARE[0] = 0; + + /* Enable automatic TX */ + NRF_RADIO_NS->SUBSCRIBE_TXEN = DPPI_SUBSCRIBE_RADIO_TASKS_TXEN(1); + + /* + * XXX: Hack warning! + * + * It may happen (during flash erase) that CPU is stopped for a moment and + * TIMER0 already counted past CC[0]. In such case we will be stuck waiting + * for TX to start since EVENTS_COMPARE[0] will not happen any time soon. + * For now let's set a flag denoting that we are late in RX-TX transition so + * ble_phy_tx() will fail - this allows everything to cleanup nicely without + * the need for extra handling in many places. + * + * Note: CC[3] is used only for wfr which we do not need here. + */ + NRF_TIMER0_NS->TASKS_CAPTURE[3] = 1; + if (NRF_TIMER0_NS->CC[3] > NRF_TIMER0_NS->CC[0]) { + NRF_RADIO_NS->SUBSCRIBE_TXEN = DPPI_SUBSCRIBE_RADIO_TASKS_TXEN(0); + + g_ble_phy_data.phy_transition_late = 1; + } + + /* + * XXX: This is a horrible ugly hack to deal with the RAM S1 byte + * that is not sent over the air but is present here. Simply move the + * data pointer to deal with it. Fix this later. + */ + dptr[2] = dptr[1]; + dptr[1] = dptr[0]; + rc = ble_ll_rx_end(dptr + 1, ble_hdr); + if (rc < 0) { + ble_phy_disable(); + } +} + +static bool +ble_phy_rx_start_isr(void) +{ + int rc; + uint32_t state; + uint32_t usecs; + uint32_t pdu_usecs; + uint32_t ticks; + struct ble_mbuf_hdr *ble_hdr; + uint8_t *dptr; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + int adva_offset; +#endif + + dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; + + /* Clear events and clear interrupt */ + NRF_RADIO_NS->EVENTS_ADDRESS = 0; + + /* Clear wfr timer channels and DISABLED interrupt */ + NRF_RADIO_NS->INTENCLR = RADIO_INTENCLR_DISABLED_Msk | RADIO_INTENCLR_ADDRESS_Msk; + NRF_TIMER0_NS->SUBSCRIBE_CAPTURE[3] = DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE3(0); + NRF_RADIO_NS->SUBSCRIBE_DISABLE = DPPI_SUBSCRIBE_RADIO_TASKS_DISABLE(0); + + /* Initialize the ble mbuf header */ + ble_hdr = &g_ble_phy_data.rxhdr; + ble_hdr->rxinfo.flags = ble_ll_state_get(); + ble_hdr->rxinfo.channel = g_ble_phy_data.phy_chan; + ble_hdr->rxinfo.handle = 0; + ble_hdr->rxinfo.phy = ble_phy_get_cur_phy(); + ble_hdr->rxinfo.phy_mode = ble_phy_get_cur_rx_phy_mode(); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_hdr->rxinfo.user_data = NULL; +#endif + + /* + * Calculate accurate packets start time (with remainder) + * + * We may start receiving packet somewhere during preamble in which case + * it is possible that actual transmission started before TIMER0 was + * running - need to take this into account. + */ + ble_hdr->beg_cputime = g_ble_phy_data.phy_start_cputime; + + usecs = NRF_TIMER0_NS->CC[1]; + pdu_usecs = ble_phy_mode_pdu_start_off(ble_hdr->rxinfo.phy_mode) + + g_ble_phy_t_rxaddrdelay[ble_hdr->rxinfo.phy_mode]; + if (usecs < pdu_usecs) { + g_ble_phy_data.phy_start_cputime--; + usecs += 30; + } + usecs -= pdu_usecs; + + ticks = os_cputime_usecs_to_ticks(usecs); + usecs -= os_cputime_ticks_to_usecs(ticks); + if (usecs == 31) { + usecs = 0; + ++ticks; + } + + ble_hdr->beg_cputime += ticks; + ble_hdr->rem_usecs = usecs; + + /* Wait to get 1st byte of frame */ + while (1) { + state = NRF_RADIO_NS->STATE; + if (NRF_RADIO_NS->EVENTS_BCMATCH != 0) { + break; + } + + /* + * If state is disabled, we should have the BCMATCH. If not, + * something is wrong! + */ + if (state == RADIO_STATE_STATE_Disabled) { + NRF_RADIO_NS->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + NRF_RADIO_NS->SHORTS = 0; + return false; + } + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* + * If privacy is enabled and received PDU has TxAdd bit set (i.e. random + * address) we try to resolve address using AAR. + */ + if (g_ble_phy_data.phy_privacy && (dptr[3] & 0x40)) { + /* + * AdvA is located at 4th octet in RX buffer (after S0, length an S1 + * fields). In case of extended advertising PDU we need to add 2 more + * octets for extended header. + */ + adva_offset = (dptr[3] & 0x0f) == 0x07 ? 2 : 0; + NRF_AAR_NS->ADDRPTR = (uint32_t)(dptr + 3 + adva_offset); + + /* Trigger AAR after last bit of AdvA is received */ + NRF_RADIO_NS->EVENTS_BCMATCH = 0; + NRF_AAR_NS->SUBSCRIBE_START = DPPI_SUBSCRIBE_AAR_TASKS_START(1); + NRF_RADIO_NS->BCC = (BLE_LL_PDU_HDR_LEN + adva_offset + BLE_DEV_ADDR_LEN) * 8 + + g_ble_phy_data.phy_bcc_offset; + } +#endif + + /* Call Link Layer receive start function */ + rc = ble_ll_rx_start(dptr + 3, + g_ble_phy_data.phy_chan, + &g_ble_phy_data.rxhdr); + if (rc >= 0) { + /* Set rx started flag and enable rx end ISR */ + g_ble_phy_data.phy_rx_started = 1; + NRF_RADIO_NS->INTENSET = RADIO_INTENSET_END_Msk; + } else { + /* Disable PHY */ + ble_phy_disable(); + STATS_INC(ble_phy_stats, rx_aborts); + } + + /* Count rx starts */ + STATS_INC(ble_phy_stats, rx_starts); + + return true; +} + +static void +ble_phy_isr(void) +{ + uint32_t irq_en; + + os_trace_isr_enter(); + + /* Read irq register to determine which interrupts are enabled */ + irq_en = NRF_RADIO_NS->INTENCLR; + + /* + * NOTE: order of checking is important! Possible, if things get delayed, + * we have both an ADDRESS and DISABLED interrupt in rx state. If we get + * an address, we disable the DISABLED interrupt. + */ + + /* We get this if we have started to receive a frame */ + if ((irq_en & RADIO_INTENCLR_ADDRESS_Msk) && NRF_RADIO_NS->EVENTS_ADDRESS) { + /* + * wfr timer is calculated to expire at the exact time we should start + * receiving a packet (with 1 usec precision) so it is possible it will + * fire at the same time as EVENT_ADDRESS. If this happens, radio will + * be disabled while we are waiting for EVENT_BCCMATCH after 1st byte + * of payload is received and ble_phy_rx_start_isr() will fail. In this + * case we should not clear DISABLED irq mask so it will be handled as + * regular radio disabled event below. In other case radio was disabled + * on purpose and there's nothing more to handle so we can clear mask. + */ + if (ble_phy_rx_start_isr()) { + irq_en &= ~RADIO_INTENCLR_DISABLED_Msk; + } + } + + /* Check for disabled event. This only happens for transmits now */ + if ((irq_en & RADIO_INTENCLR_DISABLED_Msk) && NRF_RADIO_NS->EVENTS_DISABLED) { + if (g_ble_phy_data.phy_state == BLE_PHY_STATE_RX) { + NRF_RADIO_NS->EVENTS_DISABLED = 0; + ble_ll_wfr_timer_exp(NULL); + } else if (g_ble_phy_data.phy_state == BLE_PHY_STATE_IDLE) { + assert(0); + } else { + ble_phy_tx_end_isr(); + } + } + + /* Receive packet end (we dont enable this for transmit) */ + if ((irq_en & RADIO_INTENCLR_END_Msk) && NRF_RADIO_NS->EVENTS_END) { + ble_phy_rx_end_isr(); + } + + g_ble_phy_data.phy_transition_late = 0; + + /* Ensures IRQ is cleared */ + irq_en = NRF_RADIO_NS->SHORTS; + + /* Count # of interrupts */ + STATS_INC(ble_phy_stats, phy_isrs); + + os_trace_isr_exit(); +} + +int +ble_phy_init(void) +{ + int rc; + + /* Default phy to use is 1M */ + g_ble_phy_data.phy_cur_phy_mode = BLE_PHY_MODE_1M; + g_ble_phy_data.phy_tx_phy_mode = BLE_PHY_MODE_1M; + g_ble_phy_data.phy_rx_phy_mode = BLE_PHY_MODE_1M; + + g_ble_phy_data.rx_pwr_compensation = 0; + + /* Set phy channel to an invalid channel so first set channel works */ + g_ble_phy_data.phy_chan = BLE_PHY_NUM_CHANS; + + /* Toggle peripheral power to reset (just in case) */ + NRF_RADIO_NS->POWER = 0; + NRF_RADIO_NS->POWER = 1; + + /* Errata 16 - RADIO: POWER register is not functional + * Workaround: Reset all RADIO registers in firmware. + */ + NRF_RADIO_NS->SUBSCRIBE_TXEN = 0; + NRF_RADIO_NS->SUBSCRIBE_RXEN = 0; + NRF_RADIO_NS->SUBSCRIBE_DISABLE = 0; + + /* Disable all interrupts */ + NRF_RADIO_NS->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + + /* Set configuration registers */ + NRF_RADIO_NS->MODE = RADIO_MODE_MODE_Ble_1Mbit; + NRF_RADIO_NS->PCNF0 = NRF_PCNF0; + + /* XXX: should maxlen be 251 for encryption? */ + NRF_RADIO_NS->PCNF1 = NRF_MAXLEN | + (RADIO_PCNF1_ENDIAN_Little << RADIO_PCNF1_ENDIAN_Pos) | + (NRF_BALEN << RADIO_PCNF1_BALEN_Pos) | + RADIO_PCNF1_WHITEEN_Msk; + + /* Enable radio fast ramp-up */ + NRF_RADIO_NS->MODECNF0 |= (RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos) & RADIO_MODECNF0_RU_Msk; + + /* Set logical address 1 for TX and RX */ + NRF_RADIO_NS->TXADDRESS = 0; + NRF_RADIO_NS->RXADDRESSES = (1 << 0); + + /* Configure the CRC registers */ + NRF_RADIO_NS->CRCCNF = (RADIO_CRCCNF_SKIPADDR_Skip << RADIO_CRCCNF_SKIPADDR_Pos) | RADIO_CRCCNF_LEN_Three; + + /* Configure BLE poly */ + NRF_RADIO_NS->CRCPOLY = 0x0000065B; + + /* Configure IFS */ + NRF_RADIO_NS->TIFS = BLE_LL_IFS; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + NRF_CCM_NS->INTENCLR = 0xffffffff; + NRF_CCM_NS->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; + NRF_CCM_NS->EVENTS_ERROR = 0; + memset(nrf_encrypt_scratchpad, 0, sizeof(nrf_encrypt_scratchpad)); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + g_ble_phy_data.phy_aar_scratch = 0; + NRF_AAR_NS->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; + NRF_AAR_NS->INTENCLR = 0xffffffff; + NRF_AAR_NS->EVENTS_END = 0; + NRF_AAR_NS->EVENTS_RESOLVED = 0; + NRF_AAR_NS->EVENTS_NOTRESOLVED = 0; + NRF_AAR_NS->NIRK = 0; +#endif + + /* TIMER0 setup for PHY when using RTC */ + NRF_TIMER0_NS->TASKS_STOP = 1; + NRF_TIMER0_NS->TASKS_SHUTDOWN = 1; + NRF_TIMER0_NS->BITMODE = 3; /* 32-bit timer */ + NRF_TIMER0_NS->MODE = 0; /* Timer mode */ + NRF_TIMER0_NS->PRESCALER = 4; /* gives us 1 MHz */ + + /* Publish events */ + NRF_TIMER0_NS->PUBLISH_COMPARE[0] = DPPI_PUBLISH_TIMER0_EVENTS_COMPARE_0; + NRF_TIMER0_NS->PUBLISH_COMPARE[3] = DPPI_PUBLISH_TIMER0_EVENTS_COMPARE_3; + NRF_RADIO_NS->PUBLISH_END = DPPI_PUBLISH_RADIO_EVENTS_END; + NRF_RADIO_NS->PUBLISH_BCMATCH = DPPI_PUBLISH_RADIO_EVENTS_BCMATCH; + NRF_RADIO_NS->PUBLISH_ADDRESS = DPPI_PUBLISH_RADIO_EVENTS_ADDRESS; + NRF_RTC0_NS->PUBLISH_COMPARE[0] = DPPI_PUBLISH_RTC0_EVENTS_COMPARE_0; + + /* Enable channels we publish on */ + NRF_DPPIC_NS->CHENSET = DPPI_CH_ENABLE_ALL; + + /* Captures tx/rx start in timer0 cc 1 and tx/rx end in timer0 cc 2 */ + NRF_TIMER0_NS->SUBSCRIBE_CAPTURE[1] = DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE1(1); + NRF_TIMER0_NS->SUBSCRIBE_CAPTURE[2] = DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE2(1); + + /* Set isr in vector table and enable interrupt */ +#ifndef RIOT_VERSION + NVIC_SetPriority(RADIO_IRQn, 0); +#endif +#if MYNEWT + NVIC_SetVector(RADIO_IRQn, (uint32_t)ble_phy_isr); +#else + ble_npl_hw_set_isr(RADIO_IRQn, ble_phy_isr); +#endif + NVIC_EnableIRQ(RADIO_IRQn); + + /* Register phy statistics */ + if (!g_ble_phy_data.phy_stats_initialized) { + rc = stats_init_and_reg(STATS_HDR(ble_phy_stats), + STATS_SIZE_INIT_PARMS(ble_phy_stats, + STATS_SIZE_32), + STATS_NAME_INIT_PARMS(ble_phy_stats), + "ble_phy"); + assert(rc == 0); + + g_ble_phy_data.phy_stats_initialized = 1; + } + + return 0; +} + +int +ble_phy_rx(void) +{ + /* + * Check radio state. + * + * In case radio is now disabling we'll wait for it to finish, but if for + * any reason it's just in idle state we proceed with RX as usual since + * nRF52 radio can ramp-up from idle state as well. + * + * Note that TX and RX states values are the same except for 3rd bit so we + * can make a shortcut here when checking for idle state. + */ + nrf_wait_disabled(); + if ((NRF_RADIO_NS->STATE != RADIO_STATE_STATE_Disabled) && + ((NRF_RADIO_NS->STATE & 0x07) != RADIO_STATE_STATE_RxIdle)) { + ble_phy_disable(); + STATS_INC(ble_phy_stats, radio_state_errs); + return BLE_PHY_ERR_RADIO_STATE; + } + + /* Make sure all interrupts are disabled */ + NRF_RADIO_NS->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + + /* Clear events prior to enabling receive */ + NRF_RADIO_NS->EVENTS_END = 0; + NRF_RADIO_NS->EVENTS_DISABLED = 0; + + /* Setup for rx */ + ble_phy_rx_xcvr_setup(); + + /* task to start RX should be subscribed here */ + assert(NRF_RADIO_NS->SUBSCRIBE_RXEN & DPPI_SUBSCRIBE_RADIO_TASKS_RXEN(1)); + + return 0; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +void +ble_phy_encrypt_enable(uint64_t pkt_counter, uint8_t *iv, uint8_t *key, + uint8_t is_master) +{ + memcpy(nrf_ccm_data.key, key, 16); + nrf_ccm_data.pkt_counter = pkt_counter; + memcpy(nrf_ccm_data.iv, iv, 8); + nrf_ccm_data.dir_bit = is_master; + g_ble_phy_data.phy_encrypted = 1; + /* Enable the module (AAR cannot be on while CCM on) */ + NRF_AAR_NS->ENABLE = AAR_ENABLE_ENABLE_Disabled; + NRF_CCM_NS->ENABLE = CCM_ENABLE_ENABLE_Enabled; +} + +void +ble_phy_encrypt_set_pkt_cntr(uint64_t pkt_counter, int dir) +{ + nrf_ccm_data.pkt_counter = pkt_counter; + nrf_ccm_data.dir_bit = dir; +} + +void +ble_phy_encrypt_disable(void) +{ + NRF_CCM_NS->SUBSCRIBE_CRYPT = DPPI_SUBSCRIBE_CCM_TASKS_CRYPT(0); + NRF_CCM_NS->TASKS_STOP = 1; + NRF_CCM_NS->EVENTS_ERROR = 0; + NRF_CCM_NS->ENABLE = CCM_ENABLE_ENABLE_Disabled; + + g_ble_phy_data.phy_encrypted = 0; +} +#endif + +void +ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg) +{ + /* Set transmit end callback and arg */ + g_ble_phy_data.txend_cb = txend_cb; + g_ble_phy_data.txend_arg = arg; +} + +int +ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + int rc; + + ble_phy_trace_u32x2(BLE_PHY_TRACE_ID_START_TX, cputime, rem_usecs); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_tx_phy_mode); +#endif + + /* XXX: This should not be necessary, but paranoia is good! */ + /* Clear timer0 compare to RXEN since we are transmitting */ + NRF_RADIO_NS->SUBSCRIBE_RXEN = DPPI_SUBSCRIBE_RADIO_TASKS_RXEN(0); + + if (ble_phy_set_start_time(cputime, rem_usecs, true) != 0) { + STATS_INC(ble_phy_stats, tx_late); + ble_phy_disable(); + rc = BLE_PHY_ERR_TX_LATE; + } else { + /* Enable PPI to automatically start TXEN */ + NRF_RADIO_NS->SUBSCRIBE_TXEN = DPPI_SUBSCRIBE_RADIO_TASKS_TXEN(1); + rc = 0; + } + return rc; +} + +int +ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + bool late = false; + int rc = 0; + + ble_phy_trace_u32x2(BLE_PHY_TRACE_ID_START_RX, cputime, rem_usecs); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_rx_phy_mode); +#endif + + /* XXX: This should not be necessary, but paranoia is good! */ + /* Clear timer0 compare to TXEN since we are transmitting */ + NRF_RADIO_NS->SUBSCRIBE_TXEN = DPPI_SUBSCRIBE_RADIO_TASKS_TXEN(0); + + if (ble_phy_set_start_time(cputime, rem_usecs, false) != 0) { + STATS_INC(ble_phy_stats, rx_late); + + /* We're late so let's just try to start RX as soon as possible */ + ble_phy_set_start_now(); + + late = true; + } + + /* Enable PPI to automatically start RXEN */ + NRF_RADIO_NS->SUBSCRIBE_RXEN = DPPI_SUBSCRIBE_RADIO_TASKS_RXEN(1); + + /* Start rx */ + rc = ble_phy_rx(); + + /* + * If we enabled receiver but were late, let's return proper error code so + * caller can handle this. + */ + if (!rc && late) { + rc = BLE_PHY_ERR_RX_LATE; + } + + return rc; +} + +int +ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) +{ + int rc; + uint8_t *dptr; + uint8_t *pktptr; + uint8_t payload_len; + uint8_t hdr_byte; + uint32_t state; + uint32_t shortcuts; + + if (g_ble_phy_data.phy_transition_late) { + ble_phy_disable(); + STATS_INC(ble_phy_stats, tx_late); + return BLE_PHY_ERR_TX_LATE; + } + + /* + * This check is to make sure that the radio is not in a state where + * it is moving to disabled state. If so, let it get there. + */ + nrf_wait_disabled(); + + /* + * XXX: Although we may not have to do this here, I clear all the PPI + * that should not be used when transmitting. Some of them are only enabled + * if encryption and/or privacy is on, but I dont care. Better to be + * paranoid, and if you are going to clear one, might as well clear them + * all. + */ + NRF_TIMER0_NS->SUBSCRIBE_CAPTURE[3] = DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE3(0); + NRF_RADIO_NS->SUBSCRIBE_DISABLE = DPPI_SUBSCRIBE_RADIO_TASKS_DISABLE(0); + NRF_AAR_NS->SUBSCRIBE_START = DPPI_SUBSCRIBE_AAR_TASKS_START(0); + NRF_CCM_NS->SUBSCRIBE_CRYPT = DPPI_SUBSCRIBE_CCM_TASKS_CRYPT(0); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + dptr = (uint8_t *)&g_ble_phy_enc_buf[0]; + pktptr = (uint8_t *)&g_ble_phy_tx_buf[0]; + NRF_CCM_NS->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; + NRF_CCM_NS->INPTR = (uint32_t)dptr; + NRF_CCM_NS->OUTPTR = (uint32_t)pktptr; + NRF_CCM_NS->SCRATCHPTR = (uint32_t)&nrf_encrypt_scratchpad[0]; + NRF_CCM_NS->EVENTS_ERROR = 0; + NRF_CCM_NS->MODE = CCM_MODE_LENGTH_Msk | ble_phy_get_ccm_datarate(); + NRF_CCM_NS->CNFPTR = (uint32_t)&nrf_ccm_data; + } else { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + NRF_AAR_NS->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; +#endif + dptr = (uint8_t *)&g_ble_phy_tx_buf[0]; + pktptr = dptr; + } +#else + dptr = (uint8_t *)&g_ble_phy_tx_buf[0]; + pktptr = dptr; +#endif + + /* Set PDU payload */ + payload_len = pducb(&dptr[3], pducb_arg, &hdr_byte); + + /* RAM representation has S0, LENGTH and S1 fields. (3 bytes) */ + dptr[0] = hdr_byte; + dptr[1] = payload_len; + dptr[2] = 0; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* Start key-stream generation and encryption (via short) */ + if (g_ble_phy_data.phy_encrypted) { + NRF_CCM_NS->TASKS_KSGEN = 1; + } +#endif + + NRF_RADIO_NS->PACKETPTR = (uint32_t)pktptr; + + /* Clear the ready, end and disabled events */ + NRF_RADIO_NS->EVENTS_READY = 0; + NRF_RADIO_NS->EVENTS_END = 0; + NRF_RADIO_NS->EVENTS_DISABLED = 0; + + /* Enable shortcuts for transmit start/end. */ + shortcuts = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk; + NRF_RADIO_NS->SHORTS = shortcuts; + NRF_RADIO_NS->INTENSET = RADIO_INTENSET_DISABLED_Msk; + + /* Set the PHY transition */ + g_ble_phy_data.phy_transition = end_trans; + + /* Set transmitted payload length */ + g_ble_phy_data.phy_tx_pyld_len = payload_len; + + /* If we already started transmitting, abort it! */ + state = NRF_RADIO_NS->STATE; + if (state != RADIO_STATE_STATE_Tx) { + /* Set phy state to transmitting and count packet statistics */ + g_ble_phy_data.phy_state = BLE_PHY_STATE_TX; + STATS_INC(ble_phy_stats, tx_good); + STATS_INCN(ble_phy_stats, tx_bytes, payload_len + BLE_LL_PDU_HDR_LEN); + rc = BLE_ERR_SUCCESS; + } else { + ble_phy_disable(); + STATS_INC(ble_phy_stats, tx_late); + rc = BLE_PHY_ERR_RADIO_STATE; + } + + return rc; +} + +int +ble_phy_txpwr_set(int dbm) +{ + /* "Rail" power level if outside supported range */ + dbm = ble_phy_txpower_round(dbm); + + NRF_RADIO_NS->TXPOWER = dbm; + g_ble_phy_data.phy_txpwr_dbm = dbm; + + return 0; +} + +int +ble_phy_txpower_round(int dbm) +{ + /* "Rail" power level if outside supported range */ + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_0dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_0dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg1dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg1dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg2dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg2dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg3dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg5dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg5dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg6dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg6dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg7dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg7dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg8dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg8dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg12dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg12dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg16dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg16dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg20dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg20dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg40dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg40dBm; + } + + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg40dBm; +} + +static int +ble_phy_set_access_addr(uint32_t access_addr) +{ + NRF_RADIO_NS->BASE0 = (access_addr << 8); + NRF_RADIO_NS->PREFIX0 = (NRF_RADIO_NS->PREFIX0 & 0xFFFFFF00) | (access_addr >> 24); + + g_ble_phy_data.phy_access_address = access_addr; + + return 0; +} + +int +ble_phy_txpwr_get(void) +{ + return g_ble_phy_data.phy_txpwr_dbm; +} + +void +ble_phy_set_rx_pwr_compensation(int8_t compensation) +{ + g_ble_phy_data.rx_pwr_compensation = compensation; +} + +int +ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit) +{ + assert(chan < BLE_PHY_NUM_CHANS); + + /* Check for valid channel range */ + if (chan >= BLE_PHY_NUM_CHANS) { + return BLE_PHY_ERR_INV_PARAM; + } + + /* Set current access address */ + ble_phy_set_access_addr(access_addr); + + /* Configure crcinit */ + NRF_RADIO_NS->CRCINIT = crcinit; + + /* Set the frequency and the data whitening initial value */ + g_ble_phy_data.phy_chan = chan; + NRF_RADIO_NS->FREQUENCY = g_ble_phy_chan_freq[chan]; + NRF_RADIO_NS->DATAWHITEIV = chan; + + return 0; +} + +/** + * Stop the timer used to count microseconds when using RTC for cputime + */ +static void +ble_phy_stop_usec_timer(void) +{ + NRF_TIMER0_NS->TASKS_STOP = 1; + NRF_TIMER0_NS->TASKS_SHUTDOWN = 1; + NRF_RTC0_NS->EVTENCLR = RTC_EVTENSET_COMPARE0_Msk; +} + +/** + * ble phy disable irq and ppi + * + * This routine is to be called when reception was stopped due to either a + * wait for response timeout or a packet being received and the phy is to be + * restarted in receive mode. Generally, the disable routine is called to stop + * the phy. + */ +static void +ble_phy_disable_irq_and_ppi(void) +{ + NRF_RADIO_NS->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + NRF_RADIO_NS->SHORTS = 0; + NRF_RADIO_NS->TASKS_DISABLE = 1; + + NRF_TIMER0_NS->SUBSCRIBE_START = DPPI_SUBSCRIBE_TIMER0_TASKS_START(0); + NRF_TIMER0_NS->SUBSCRIBE_CAPTURE[3] = DPPI_SUBSCRIBE_TIMER0_TASKS_CAPTURE3(0); + NRF_RADIO_NS->SUBSCRIBE_DISABLE = DPPI_SUBSCRIBE_RADIO_TASKS_DISABLE(0); + NRF_RADIO_NS->SUBSCRIBE_TXEN = DPPI_SUBSCRIBE_RADIO_TASKS_TXEN(0); + NRF_RADIO_NS->SUBSCRIBE_RXEN = DPPI_SUBSCRIBE_RADIO_TASKS_RXEN(0); + NRF_AAR_NS->SUBSCRIBE_START = DPPI_SUBSCRIBE_AAR_TASKS_START(0); + NRF_CCM_NS->SUBSCRIBE_CRYPT = DPPI_SUBSCRIBE_CCM_TASKS_CRYPT(0); + + NVIC_ClearPendingIRQ(RADIO_IRQn); + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; +} + +void +ble_phy_restart_rx(void) +{ + ble_phy_stop_usec_timer(); + ble_phy_disable_irq_and_ppi(); + + ble_phy_set_start_now(); + /* Enable PPI to automatically start RXEN */ + NRF_RADIO_NS->SUBSCRIBE_RXEN = DPPI_SUBSCRIBE_RADIO_TASKS_RXEN(1); + + ble_phy_rx(); +} + +void +ble_phy_disable(void) +{ + ble_phy_trace_void(BLE_PHY_TRACE_ID_DISABLE); + + ble_phy_stop_usec_timer(); + ble_phy_disable_irq_and_ppi(); +} + +uint32_t +ble_phy_access_addr_get(void) +{ + return g_ble_phy_data.phy_access_address; +} + +int +ble_phy_state_get(void) +{ + return g_ble_phy_data.phy_state; +} + +int +ble_phy_rx_started(void) +{ + return g_ble_phy_data.phy_rx_started; +} + +uint8_t +ble_phy_xcvr_state_get(void) +{ + uint32_t state; + state = NRF_RADIO_NS->STATE; + return (uint8_t)state; +} + +uint8_t +ble_phy_max_data_pdu_pyld(void) +{ + return BLE_LL_DATA_PDU_MAX_PYLD; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +void +ble_phy_resolv_list_enable(void) +{ + NRF_AAR_NS->NIRK = (uint32_t)g_nrf_num_irks; + g_ble_phy_data.phy_privacy = 1; +} + +void +ble_phy_resolv_list_disable(void) +{ + g_ble_phy_data.phy_privacy = 0; +} +#endif + +#if MYNEWT_VAL(BLE_LL_DTM) +void +ble_phy_enable_dtm(void) +{ + /* When DTM is enabled we need to disable whitening as per + * Bluetooth v5.0 Vol 6. Part F. 4.1.1 + */ + NRF_RADIO_NS->PCNF1 &= ~RADIO_PCNF1_WHITEEN_Msk; +} + +void +ble_phy_disable_dtm(void) +{ + /* Enable whitening */ + NRF_RADIO_NS->PCNF1 |= RADIO_PCNF1_WHITEEN_Msk; +} +#endif + +void +ble_phy_rfclk_enable(void) +{ +#if MYNEWT + nrf5340_net_clock_hfxo_request(); +#else + NRF_CLOCK_NS->TASKS_HFCLKSTART = 1; +#endif +} + +void +ble_phy_rfclk_disable(void) +{ +#if MYNEWT + nrf5340_net_clock_hfxo_release(); +#else + NRF_CLOCK_NS->TASKS_HFCLKSTOP = 1; +#endif +} diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf5340/src/ble_phy_trace.c b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/src/ble_phy_trace.c new file mode 100644 index 00000000..6967c3f7 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/src/ble_phy_trace.c @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <stdint.h> +#include <syscfg/syscfg.h> +#include <os/os_trace_api.h> + +#if MYNEWT_VAL(BLE_PHY_SYSVIEW) + +static os_trace_module_t g_ble_phy_trace_mod; +uint32_t ble_phy_trace_off; + +static void +ble_phy_trace_module_send_desc(void) +{ + os_trace_module_desc(&g_ble_phy_trace_mod, "0 phy_set_tx cputime=%u usecs=%u"); + os_trace_module_desc(&g_ble_phy_trace_mod, "1 phy_set_rx cputime=%u usecs=%u"); + os_trace_module_desc(&g_ble_phy_trace_mod, "2 phy_disable"); +} + +void +ble_phy_trace_init(void) +{ + ble_phy_trace_off = + os_trace_module_register(&g_ble_phy_trace_mod, "ble_phy", 3, + ble_phy_trace_module_send_desc); +} +#endif diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf5340/syscfg.yml b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/syscfg.yml new file mode 100644 index 00000000..dd8b9304 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf5340/syscfg.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +syscfg.defs: + BLE_PHY_SYSVIEW: + description: > + Enable SystemView tracing module for radio driver. + value: 0 |