From e69a66d715687ef923950cfd2f0f17fb88efc71f Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Wed, 2 Sep 2020 15:33:12 +0200 Subject: [PATCH] nimble/phy: Add PHY for Dialog CMAC This adds PHY for Dialog CMAC. This allows to run complete NimBLE controller on Dialog DA1469x MCU family. 1M and 2M PHYs are supported, Coded support is missing in hardware at the moment. Some parts of code that access RF block are obfuscated, but there are no proprietary binaries used so fixes are possible if necessary. Co-authored-by: Will San Filippo --- nimble/drivers/dialog_cmac/include/ble/xcvr.h | 39 + nimble/drivers/dialog_cmac/pkg.yml | 32 + nimble/drivers/dialog_cmac/src/aes/aes.h | 130 ++ .../drivers/dialog_cmac/src/aes/aes_encrypt.c | 193 ++ .../drivers/dialog_cmac/src/aes/constants.h | 61 + nimble/drivers/dialog_cmac/src/aes/utils.c | 74 + nimble/drivers/dialog_cmac/src/aes/utils.h | 95 + nimble/drivers/dialog_cmac/src/ble_hw.c | 340 ++++ nimble/drivers/dialog_cmac/src/ble_hw_priv.h | 29 + nimble/drivers/dialog_cmac/src/ble_phy.c | 1781 +++++++++++++++++ nimble/drivers/dialog_cmac/src/ble_rf.c | 729 +++++++ nimble/drivers/dialog_cmac/src/ble_rf_priv.h | 39 + nimble/drivers/dialog_cmac/syscfg.yml | 29 + 13 files changed, 3571 insertions(+) create mode 100644 nimble/drivers/dialog_cmac/include/ble/xcvr.h create mode 100644 nimble/drivers/dialog_cmac/pkg.yml create mode 100644 nimble/drivers/dialog_cmac/src/aes/aes.h create mode 100644 nimble/drivers/dialog_cmac/src/aes/aes_encrypt.c create mode 100644 nimble/drivers/dialog_cmac/src/aes/constants.h create mode 100644 nimble/drivers/dialog_cmac/src/aes/utils.c create mode 100644 nimble/drivers/dialog_cmac/src/aes/utils.h create mode 100644 nimble/drivers/dialog_cmac/src/ble_hw.c create mode 100644 nimble/drivers/dialog_cmac/src/ble_hw_priv.h create mode 100644 nimble/drivers/dialog_cmac/src/ble_phy.c create mode 100644 nimble/drivers/dialog_cmac/src/ble_rf.c create mode 100644 nimble/drivers/dialog_cmac/src/ble_rf_priv.h create mode 100644 nimble/drivers/dialog_cmac/syscfg.yml diff --git a/nimble/drivers/dialog_cmac/include/ble/xcvr.h b/nimble/drivers/dialog_cmac/include/ble/xcvr.h new file mode 100644 index 000000000..e4e741c5d --- /dev/null +++ b/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/nimble/drivers/dialog_cmac/pkg.yml b/nimble/drivers/dialog_cmac/pkg.yml new file mode 100644 index 000000000..6d158fce5 --- /dev/null +++ b/nimble/drivers/dialog_cmac/pkg.yml @@ -0,0 +1,32 @@ +# +# 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 " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth +pkg.deps: + - "@apache-mynewt-nimble/nimble/controller" +pkg.apis: + - ble_driver +pkg.req_apis: + - ble_transport diff --git a/nimble/drivers/dialog_cmac/src/aes/aes.h b/nimble/drivers/dialog_cmac/src/aes/aes.h new file mode 100644 index 000000000..b612213a0 --- /dev/null +++ b/nimble/drivers/dialog_cmac/src/aes/aes.h @@ -0,0 +1,130 @@ +/* aes.h - TinyCrypt interface to an AES-128 implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to an AES-128 implementation. + * + * Overview: AES-128 is a NIST approved block cipher specified in + * FIPS 197. Block ciphers are deterministic algorithms that + * perform a transformation specified by a symmetric key in fixed- + * length data sets, also called blocks. + * + * Security: AES-128 provides approximately 128 bits of security. + * + * Usage: 1) call tc_aes128_set_encrypt/decrypt_key to set the key. + * + * 2) call tc_aes_encrypt/decrypt to process the data. + */ + +#ifndef __TC_AES_H__ +#define __TC_AES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define Nb (4) /* number of columns (32-bit words) comprising the state */ +#define Nk (4) /* number of 32-bit words comprising the key */ +#define Nr (10) /* number of rounds */ +#define TC_AES_BLOCK_SIZE (Nb*Nk) +#define TC_AES_KEY_SIZE (Nb*Nk) + +typedef struct tc_aes_key_sched_struct { + unsigned int words[Nb*(Nr+1)]; +} *TCAesKeySched_t; + +/** + * @brief Set AES-128 encryption key + * Uses key k to initialize s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: s == NULL or k == NULL + * @note This implementation skips the additional steps required for keys + * larger than 128 bits, and must not be used for AES-192 or + * AES-256 key schedule -- see FIPS 197 for details + * @param s IN/OUT -- initialized struct tc_aes_key_sched_struct + * @param k IN -- points to the AES key + */ +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k); + +/** + * @brief AES-128 Encryption procedure + * Encrypts contents of in buffer into out buffer under key; + * schedule s + * @note Assumes s was initialized by aes_set_encrypt_key; + * out and in point to 16 byte buffers + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: out == NULL or in == NULL or s == NULL + * @param out IN/OUT -- buffer to receive ciphertext block + * @param in IN -- a plaintext block to encrypt + * @param s IN -- initialized AES key schedule + */ +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, + const TCAesKeySched_t s); + +/** + * @brief Set the AES-128 decryption key + * Uses key k to initialize s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: s == NULL or k == NULL + * @note This is the implementation of the straightforward inverse cipher + * using the cipher documented in FIPS-197 figure 12, not the + * equivalent inverse cipher presented in Figure 15 + * @warning This routine skips the additional steps required for keys larger + * than 128, and must not be used for AES-192 or AES-256 key + * schedule -- see FIPS 197 for details + * @param s IN/OUT -- initialized struct tc_aes_key_sched_struct + * @param k IN -- points to the AES key + */ +int tc_aes128_set_decrypt_key(TCAesKeySched_t s, const uint8_t *k); + +/** + * @brief AES-128 Encryption procedure + * Decrypts in buffer into out buffer under key schedule s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: out is NULL or in is NULL or s is NULL + * @note Assumes s was initialized by aes_set_encrypt_key + * out and in point to 16 byte buffers + * @param out IN/OUT -- buffer to receive ciphertext block + * @param in IN -- a plaintext block to encrypt + * @param s IN -- initialized AES key schedule + */ +int tc_aes_decrypt(uint8_t *out, const uint8_t *in, + const TCAesKeySched_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_AES_H__ */ diff --git a/nimble/drivers/dialog_cmac/src/aes/aes_encrypt.c b/nimble/drivers/dialog_cmac/src/aes/aes_encrypt.c new file mode 100644 index 000000000..4288c422b --- /dev/null +++ b/nimble/drivers/dialog_cmac/src/aes/aes_encrypt.c @@ -0,0 +1,193 @@ +/* aes_encrypt.c - TinyCrypt implementation of AES encryption procedure */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "aes.h" +#include "utils.h" +#include "constants.h" + +static const uint8_t sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, + 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, + 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, + 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, + 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, + 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, + 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, + 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, + 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, + 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, + 0xb0, 0x54, 0xbb, 0x16 +}; + +static const unsigned int rconst[11] = { + 0x00000000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 + }; + +/* AES state and temp buffer */ +uint8_t g_aes_state[Nk*Nb]; +uint8_t g_t[Nb*Nk]; + +static inline unsigned int rotword(unsigned int a) +{ + return (((a) >> 24)|((a) << 8)); +} + +#define subbyte(a, o)(sbox[((a) >> (o))&0xff] << (o)) +#define subword(a)(subbyte(a, 24)|subbyte(a, 16)|subbyte(a, 8)|subbyte(a, 0)) + +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k) +{ + unsigned int i; + unsigned int t; + + if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } else if (k == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + for (i = 0; i < Nk; ++i) { + s->words[i] = (k[Nb*i]<<24) | (k[Nb*i+1]<<16) | + (k[Nb*i+2]<<8) | (k[Nb*i+3]); + } + + for (; i < (Nb * (Nr + 1)); ++i) { + t = s->words[i-1]; + if ((i % Nk) == 0) { + t = subword(rotword(t)) ^ rconst[i/Nk]; + } + s->words[i] = s->words[i-Nk] ^ t; + } + + return TC_CRYPTO_SUCCESS; +} + +static inline void add_round_key(uint8_t *s, const unsigned int *k) +{ + s[0] ^= (uint8_t)(k[0] >> 24); s[1] ^= (uint8_t)(k[0] >> 16); + s[2] ^= (uint8_t)(k[0] >> 8); s[3] ^= (uint8_t)(k[0]); + s[4] ^= (uint8_t)(k[1] >> 24); s[5] ^= (uint8_t)(k[1] >> 16); + s[6] ^= (uint8_t)(k[1] >> 8); s[7] ^= (uint8_t)(k[1]); + s[8] ^= (uint8_t)(k[2] >> 24); s[9] ^= (uint8_t)(k[2] >> 16); + s[10] ^= (uint8_t)(k[2] >> 8); s[11] ^= (uint8_t)(k[2]); + s[12] ^= (uint8_t)(k[3] >> 24); s[13] ^= (uint8_t)(k[3] >> 16); + s[14] ^= (uint8_t)(k[3] >> 8); s[15] ^= (uint8_t)(k[3]); +} + +static inline void sub_bytes(uint8_t *s) +{ + unsigned int i; + + for (i = 0; i < (Nb * Nk); ++i) { + s[i] = sbox[s[i]]; + } +} + +#define triple(a)(_double_byte(a)^(a)) + +static inline void mult_row_column(uint8_t *out, const uint8_t *in) +{ + out[0] = _double_byte(in[0]) ^ triple(in[1]) ^ in[2] ^ in[3]; + out[1] = in[0] ^ _double_byte(in[1]) ^ triple(in[2]) ^ in[3]; + out[2] = in[0] ^ in[1] ^ _double_byte(in[2]) ^ triple(in[3]); + out[3] = triple(in[0]) ^ in[1] ^ in[2] ^ _double_byte(in[3]); +} + +static inline void mix_columns(uint8_t *s) +{ + mult_row_column(g_t, s); + mult_row_column(&g_t[Nb], s+Nb); + mult_row_column(&g_t[2 * Nb], s + (2 * Nb)); + mult_row_column(&g_t[3 * Nb], s + (3 * Nb)); + (void) _copy(s, sizeof(g_t), g_t, sizeof(g_t)); +} + +/* + * This shift_rows also implements the matrix flip required for mix_columns, but + * performs it here to reduce the number of memory operations. + */ +static inline void shift_rows(uint8_t *s) +{ + g_t[0] = s[0]; g_t[1] = s[5]; g_t[2] = s[10]; g_t[3] = s[15]; + g_t[4] = s[4]; g_t[5] = s[9]; g_t[6] = s[14]; g_t[7] = s[3]; + g_t[8] = s[8]; g_t[9] = s[13]; g_t[10] = s[2]; g_t[11] = s[7]; + g_t[12] = s[12]; g_t[13] = s[1]; g_t[14] = s[6]; g_t[15] = s[11]; + (void) _copy(s, sizeof(g_t), g_t, sizeof(g_t)); +} + +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, const TCAesKeySched_t s) +{ + unsigned int i; + + if (out == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (in == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)_copy(g_aes_state, sizeof(g_aes_state), in, + sizeof(g_aes_state)); + add_round_key(g_aes_state, s->words); + + for (i = 0; i < (Nr - 1); ++i) { + sub_bytes(g_aes_state); + shift_rows(g_aes_state); + mix_columns(g_aes_state); + add_round_key(g_aes_state, s->words + Nb*(i+1)); + } + + sub_bytes(g_aes_state); + shift_rows(g_aes_state); + add_round_key(g_aes_state, s->words + Nb*(i+1)); + + (void)_copy(out, sizeof(g_aes_state), g_aes_state, + sizeof(g_aes_state)); + + /* zeroing out the state buffer */ + _set(g_aes_state, TC_ZERO_BYTE, sizeof(g_aes_state)); + + return TC_CRYPTO_SUCCESS; +} diff --git a/nimble/drivers/dialog_cmac/src/aes/constants.h b/nimble/drivers/dialog_cmac/src/aes/constants.h new file mode 100644 index 000000000..965490e00 --- /dev/null +++ b/nimble/drivers/dialog_cmac/src/aes/constants.h @@ -0,0 +1,61 @@ +/* constants.h - TinyCrypt interface to constants */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to constants. + * + */ + +#ifndef __TC_CONSTANTS_H__ +#define __TC_CONSTANTS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define TC_CRYPTO_SUCCESS 1 +#define TC_CRYPTO_FAIL 0 + +#define TC_ZERO_BYTE 0x00 + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CONSTANTS_H__ */ diff --git a/nimble/drivers/dialog_cmac/src/aes/utils.c b/nimble/drivers/dialog_cmac/src/aes/utils.c new file mode 100644 index 000000000..61a567bf3 --- /dev/null +++ b/nimble/drivers/dialog_cmac/src/aes/utils.c @@ -0,0 +1,74 @@ +/* utils.c - TinyCrypt platform-dependent run-time operations */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "utils.h" +#include "constants.h" + +#include + +#define MASK_TWENTY_SEVEN 0x1b + +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len) +{ + if (from_len <= to_len) { + (void)memcpy(to, from, from_len); + return from_len; + } else { + return TC_CRYPTO_FAIL; + } +} + +void _set(void *to, uint8_t val, unsigned int len) +{ + (void)memset(to, val, len); +} + +/* + * Doubles the value of a byte for values up to 127. + */ +uint8_t _double_byte(uint8_t a) +{ + return ((a<<1) ^ ((a>>7) * MASK_TWENTY_SEVEN)); +} + +int _compare(const uint8_t *a, const uint8_t *b, size_t size) +{ + const uint8_t *tempa = a; + const uint8_t *tempb = b; + uint8_t result = 0; + + for (unsigned int i = 0; i < size; i++) { + result |= tempa[i] ^ tempb[i]; + } + return result; +} diff --git a/nimble/drivers/dialog_cmac/src/aes/utils.h b/nimble/drivers/dialog_cmac/src/aes/utils.h new file mode 100644 index 000000000..bab5c3202 --- /dev/null +++ b/nimble/drivers/dialog_cmac/src/aes/utils.h @@ -0,0 +1,95 @@ +/* utils.h - TinyCrypt interface to platform-dependent run-time operations */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to platform-dependent run-time operations. + * + */ + +#ifndef __TC_UTILS_H__ +#define __TC_UTILS_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Copy the the buffer 'from' to the buffer 'to'. + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * from_len > to_len. + * + * @param to OUT -- destination buffer + * @param to_len IN -- length of destination buffer + * @param from IN -- origin buffer + * @param from_len IN -- length of origin buffer + */ +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len); + +/** + * @brief Set the value 'val' into the buffer 'to', 'len' times. + * + * @param to OUT -- destination buffer + * @param val IN -- value to be set in 'to' + * @param len IN -- number of times the value will be copied + */ +void _set(void *to, uint8_t val, unsigned int len); + +/* + * @brief AES specific doubling function, which utilizes + * the finite field used by AES. + * @return Returns a^2 + * + * @param a IN/OUT -- value to be doubled + */ +uint8_t _double_byte(uint8_t a); + +/* + * @brief Constant-time algorithm to compare if two sequences of bytes are equal + * @return Returns 0 if equal, and non-zero otherwise + * + * @param a IN -- sequence of bytes a + * @param b IN -- sequence of bytes b + * @param size IN -- size of sequences a and b + */ +int _compare(const uint8_t *a, const uint8_t *b, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_UTILS_H__ */ diff --git a/nimble/drivers/dialog_cmac/src/ble_hw.c b/nimble/drivers/dialog_cmac/src/ble_hw.c new file mode 100644 index 000000000..d8ee544e9 --- /dev/null +++ b/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 +#include +#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 "aes/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/nimble/drivers/dialog_cmac/src/ble_hw_priv.h b/nimble/drivers/dialog_cmac/src/ble_hw_priv.h new file mode 100644 index 000000000..627994ffc --- /dev/null +++ b/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 + +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/nimble/drivers/dialog_cmac/src/ble_phy.c b/nimble/drivers/dialog_cmac/src/ble_phy.c new file mode 100644 index 000000000..20c64096c --- /dev/null +++ b/nimble/drivers/dialog_cmac/src/ble_phy.c @@ -0,0 +1,1781 @@ +/* + * 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 +#include +#include +#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(); + ble_rf_calibrate_once(); + + /* + * 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. If we won't make it on time, we + * 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_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) { + STATS_INC(ble_phy_stats, tx_late); + ble_phy_disable(); + NVIC_EnableIRQ(FRAME_IRQn); + NVIC_EnableIRQ(FIELD_IRQn); + rc = BLE_PHY_ERR_TX_LATE; + } else { + /* + * 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_2_REG_FRAME_EXC_ON_BS_START_Msk; + rc = 0; + } + + 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/nimble/drivers/dialog_cmac/src/ble_rf.c b/nimble/drivers/dialog_cmac/src/ble_rf.c new file mode 100644 index 000000000..6197c4518 --- /dev/null +++ b/nimble/drivers/dialog_cmac/src/ble_rf.c @@ -0,0 +1,729 @@ +/* + * 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 +#include +#include +#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; +}; + +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(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_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 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) +{ + uint32_t val; + + 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 +} + +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; +} + +static void +ble_rf_calibrate_int(uint8_t mask) +{ + ble_rf_rfcu_enable(); + ble_rf_rfcu_apply_settings(); + ble_rf_ldo_on(); + 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_ldo_off(); + ble_rf_rfcu_disable(); + +#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 +} + +void +ble_rf_calibrate(void) +{ + ble_rf_calibrate_int(RF_CALIBRATION_1 | RF_CALIBRATION_2); +} + +void +ble_rf_calibrate_once(void) +{ + static bool calibrated = false; + + if (calibrated) { + return; + } + + 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); + + calibrated = true; +} + +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/nimble/drivers/dialog_cmac/src/ble_rf_priv.h b/nimble/drivers/dialog_cmac/src/ble_rf_priv.h new file mode 100644 index 000000000..358300d3e --- /dev/null +++ b/nimble/drivers/dialog_cmac/src/ble_rf_priv.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 _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_calibrate_once(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/nimble/drivers/dialog_cmac/syscfg.yml b/nimble/drivers/dialog_cmac/syscfg.yml new file mode 100644 index 000000000..a82b62e5f --- /dev/null +++ b/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