mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-09-22 00:47:23 +00:00
480 lines
11 KiB
C
480 lines
11 KiB
C
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include "nimble/ble.h"
|
|
#include "controller/ble_ll.h"
|
|
#include "controller/ble_ll_tmr.h"
|
|
#include "controller/ble_ll_utils.h"
|
|
|
|
/* 37 bits require 5 bytes */
|
|
#define BLE_LL_CHMAP_LEN (5)
|
|
|
|
/* Sleep clock accuracy table (in ppm) */
|
|
static const uint16_t g_ble_sca_ppm_tbl[8] = {
|
|
500, 250, 150, 100, 75, 50, 30, 20
|
|
};
|
|
|
|
int
|
|
ble_ll_utils_verify_aa(uint32_t aa)
|
|
{
|
|
uint16_t aa_low;
|
|
uint16_t aa_high;
|
|
uint32_t temp;
|
|
uint32_t mask;
|
|
uint32_t prev_bit;
|
|
uint8_t bits_diff;
|
|
uint8_t consecutive;
|
|
uint8_t transitions;
|
|
uint8_t ones;
|
|
int tmp;
|
|
|
|
aa_low = aa & 0xffff;
|
|
aa_high = aa >> 16;
|
|
|
|
/* All four bytes cannot be equal */
|
|
if (aa_low == aa_high) {
|
|
return 0;
|
|
}
|
|
|
|
/* Upper 6 bits must have 2 transitions */
|
|
tmp = (int16_t)aa_high >> 10;
|
|
if (__builtin_popcount(tmp ^ (tmp >> 1)) < 2) {
|
|
return 0;
|
|
}
|
|
|
|
/* Cannot be access address or be 1 bit different */
|
|
aa = aa_high;
|
|
aa = (aa << 16) | aa_low;
|
|
bits_diff = 0;
|
|
temp = aa ^ BLE_ACCESS_ADDR_ADV;
|
|
for (mask = 0x00000001; mask != 0; mask <<= 1) {
|
|
if (mask & temp) {
|
|
++bits_diff;
|
|
if (bits_diff > 1) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (bits_diff <= 1) {
|
|
return 0;
|
|
}
|
|
|
|
/* Cannot have more than 24 transitions */
|
|
transitions = 0;
|
|
consecutive = 1;
|
|
ones = 0;
|
|
mask = 0x00000001;
|
|
while (mask < 0x80000000) {
|
|
prev_bit = aa & mask;
|
|
mask <<= 1;
|
|
if (mask & aa) {
|
|
if (prev_bit == 0) {
|
|
++transitions;
|
|
consecutive = 1;
|
|
} else {
|
|
++consecutive;
|
|
}
|
|
} else {
|
|
if (prev_bit == 0) {
|
|
++consecutive;
|
|
} else {
|
|
++transitions;
|
|
consecutive = 1;
|
|
}
|
|
}
|
|
|
|
if (prev_bit) {
|
|
ones++;
|
|
}
|
|
|
|
/* 8 lsb should have at least three 1 */
|
|
if (mask == 0x00000100 && ones < 3) {
|
|
break;
|
|
}
|
|
|
|
/* 16 lsb should have no more than 11 transitions */
|
|
if (mask == 0x00010000 && transitions > 11) {
|
|
break;
|
|
}
|
|
|
|
/* This is invalid! */
|
|
if (consecutive > 6) {
|
|
/* Make sure we always detect invalid sequence below */
|
|
mask = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Invalid sequence found */
|
|
if (mask != 0x80000000) {
|
|
return 0;
|
|
}
|
|
|
|
/* Cannot be more than 24 transitions */
|
|
if (transitions > 24) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
uint32_t
|
|
ble_ll_utils_calc_aa(void)
|
|
{
|
|
uint32_t aa;
|
|
|
|
do {
|
|
aa = ble_ll_rand();
|
|
} while (!ble_ll_utils_verify_aa(aa));
|
|
|
|
return aa;
|
|
}
|
|
|
|
uint32_t
|
|
ble_ll_utils_calc_seed_aa(void)
|
|
{
|
|
uint32_t seed_aa;
|
|
|
|
while (1) {
|
|
seed_aa = ble_ll_rand();
|
|
|
|
/* saa(19) == saa(15) */
|
|
if (!!(seed_aa & (1 << 19)) != !!(seed_aa & (1 << 15))) {
|
|
continue;
|
|
}
|
|
|
|
/* saa(22) = saa(16) */
|
|
if (!!(seed_aa & (1 << 22)) != !!(seed_aa & (1 << 16))) {
|
|
continue;
|
|
}
|
|
|
|
/* saa(22) != saa(15) */
|
|
if (!!(seed_aa & (1 << 22)) == !!(seed_aa & (1 << 15))) {
|
|
continue;
|
|
}
|
|
|
|
/* saa(25) == 0 */
|
|
if (seed_aa & (1 << 25)) {
|
|
continue;
|
|
}
|
|
|
|
/* saa(23) == 1 */
|
|
if (!(seed_aa & (1 << 23))) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return seed_aa;
|
|
}
|
|
|
|
uint32_t
|
|
ble_ll_utils_calc_big_aa(uint32_t seed_aa, uint32_t n)
|
|
{
|
|
uint32_t d;
|
|
uint32_t dw;
|
|
|
|
/* Core 5.3, Vol 6, Part B, 2.1.2 */
|
|
/* TODO simplify? */
|
|
d = ((35 * n) + 42) % 128;
|
|
dw = (!!(d & (1 << 0)) << 31) |
|
|
(!!(d & (1 << 0)) << 30) |
|
|
(!!(d & (1 << 0)) << 29) |
|
|
(!!(d & (1 << 0)) << 28) |
|
|
(!!(d & (1 << 0)) << 27) |
|
|
(!!(d & (1 << 0)) << 26) |
|
|
(!!(d & (1 << 1)) << 25) |
|
|
(!!(d & (1 << 6)) << 24) |
|
|
(!!(d & (1 << 1)) << 23) |
|
|
(!!(d & (1 << 5)) << 21) |
|
|
(!!(d & (1 << 4)) << 20) |
|
|
(!!(d & (1 << 3)) << 18) |
|
|
(!!(d & (1 << 2)) << 17);
|
|
|
|
return seed_aa ^ dw;
|
|
}
|
|
|
|
uint8_t
|
|
ble_ll_utils_chan_map_remap(const uint8_t *chan_map, uint8_t remap_index)
|
|
{
|
|
uint8_t cntr;
|
|
uint8_t mask;
|
|
uint8_t usable_chans;
|
|
uint8_t chan;
|
|
int i, j;
|
|
|
|
/* NOTE: possible to build a map but this would use memory. For now,
|
|
* we just calculate
|
|
* Iterate through channel map to find this channel
|
|
*/
|
|
chan = 0;
|
|
cntr = 0;
|
|
for (i = 0; i < BLE_LL_CHMAP_LEN; i++) {
|
|
usable_chans = chan_map[i];
|
|
if (usable_chans != 0) {
|
|
mask = 0x01;
|
|
for (j = 0; j < 8; j++) {
|
|
if (usable_chans & mask) {
|
|
if (cntr == remap_index) {
|
|
return (chan + j);
|
|
}
|
|
++cntr;
|
|
}
|
|
mask <<= 1;
|
|
}
|
|
}
|
|
chan += 8;
|
|
}
|
|
|
|
/* we should never reach here */
|
|
BLE_LL_ASSERT(0);
|
|
return 0;
|
|
}
|
|
|
|
uint8_t
|
|
ble_ll_utils_chan_map_used_get(const uint8_t *chan_map)
|
|
{
|
|
return __builtin_popcountll(((uint64_t)(chan_map[4] & 0x1f) << 32) |
|
|
get_le32(chan_map));
|
|
}
|
|
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2)
|
|
#if __thumb2__
|
|
static inline uint32_t
|
|
ble_ll_utils_csa2_perm(uint32_t val)
|
|
{
|
|
__asm__ volatile (".syntax unified \n"
|
|
"rbit %[val], %[val] \n"
|
|
"rev %[val], %[val] \n"
|
|
: [val] "+r" (val));
|
|
|
|
return val;
|
|
}
|
|
#else
|
|
static uint32_t
|
|
ble_ll_utils_csa2_perm(uint32_t in)
|
|
{
|
|
uint32_t out = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
out |= ((in >> i) & 0x00000001) << (7 - i);
|
|
}
|
|
|
|
for (i = 8; i < 16; i++) {
|
|
out |= ((in >> i) & 0x00000001) << (15 + 8 - i);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
#endif
|
|
|
|
static inline uint32_t
|
|
ble_ll_utils_csa2_mam(uint32_t a, uint32_t b)
|
|
{
|
|
return (17 * a + b) % 65536;
|
|
}
|
|
|
|
static uint16_t
|
|
ble_ll_utils_csa2_prn_s(uint16_t counter, uint16_t ch_id)
|
|
{
|
|
uint32_t prn_s;
|
|
|
|
prn_s = counter ^ ch_id;
|
|
|
|
prn_s = ble_ll_utils_csa2_perm(prn_s);
|
|
prn_s = ble_ll_utils_csa2_mam(prn_s, ch_id);
|
|
|
|
prn_s = ble_ll_utils_csa2_perm(prn_s);
|
|
prn_s = ble_ll_utils_csa2_mam(prn_s, ch_id);
|
|
|
|
prn_s = ble_ll_utils_csa2_perm(prn_s);
|
|
prn_s = ble_ll_utils_csa2_mam(prn_s, ch_id);
|
|
|
|
return prn_s;
|
|
}
|
|
|
|
static uint16_t
|
|
ble_ll_utils_csa2_prng(uint16_t counter, uint16_t ch_id)
|
|
{
|
|
uint16_t prn_s;
|
|
uint16_t prn_e;
|
|
|
|
prn_s = ble_ll_utils_csa2_prn_s(counter, ch_id);
|
|
prn_e = prn_s ^ ch_id;
|
|
|
|
return prn_e;
|
|
}
|
|
|
|
/* Find remap_idx for given chan_idx */
|
|
static uint16_t
|
|
ble_ll_utils_csa2_chan2remap(uint16_t chan_idx, const uint8_t *chan_map)
|
|
{
|
|
uint16_t remap_idx = 0;
|
|
uint32_t u32 = 0;
|
|
unsigned idx;
|
|
|
|
for (idx = 0; idx < 37; idx++) {
|
|
if ((idx % 8) == 0) {
|
|
u32 = chan_map[idx / 8];
|
|
}
|
|
if (u32 & 1) {
|
|
if (idx == chan_idx) {
|
|
return remap_idx;
|
|
}
|
|
remap_idx++;
|
|
}
|
|
u32 >>= 1;
|
|
}
|
|
|
|
BLE_LL_ASSERT(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Find chan_idx at given remap_idx */
|
|
static uint16_t
|
|
ble_ll_utils_csa2_remap2chan(uint16_t remap_idx, const uint8_t *chan_map)
|
|
{
|
|
uint32_t u32 = 0;
|
|
unsigned idx;
|
|
|
|
for (idx = 0; idx < 37; idx++) {
|
|
if ((idx % 8) == 0) {
|
|
u32 = chan_map[idx / 8];
|
|
}
|
|
if (u32 & 1) {
|
|
if (!remap_idx) {
|
|
return idx;
|
|
}
|
|
remap_idx--;
|
|
}
|
|
u32 >>= 1;
|
|
}
|
|
|
|
BLE_LL_ASSERT(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint16_t
|
|
ble_ll_utils_csa2_calc_chan_idx(uint16_t prn_e, uint8_t num_used_chans,
|
|
const uint8_t *chanm_map, uint16_t *remap_idx)
|
|
{
|
|
uint16_t chan_idx;
|
|
|
|
chan_idx = prn_e % 37;
|
|
if (chanm_map[chan_idx / 8] & (1 << (chan_idx % 8))) {
|
|
*remap_idx = ble_ll_utils_csa2_chan2remap(chan_idx, chanm_map);
|
|
return chan_idx;
|
|
}
|
|
|
|
*remap_idx = (num_used_chans * prn_e) / 65536;
|
|
chan_idx = ble_ll_utils_csa2_remap2chan(*remap_idx, chanm_map);
|
|
|
|
return chan_idx;
|
|
}
|
|
|
|
uint8_t
|
|
ble_ll_utils_dci_csa2(uint16_t counter, uint16_t chan_id,
|
|
uint8_t num_used_chans, const uint8_t *chan_map)
|
|
{
|
|
uint16_t prn_e;
|
|
uint16_t chan_idx;
|
|
uint16_t remap_idx;
|
|
|
|
prn_e = ble_ll_utils_csa2_prng(counter, chan_id);
|
|
|
|
chan_idx = ble_ll_utils_csa2_calc_chan_idx(prn_e, num_used_chans, chan_map,
|
|
&remap_idx);
|
|
|
|
return chan_idx;
|
|
}
|
|
|
|
uint16_t
|
|
ble_ll_utils_dci_iso_event(uint16_t counter, uint16_t chan_id,
|
|
uint16_t *prn_sub_lu, uint8_t chan_map_used,
|
|
const uint8_t *chan_map, uint16_t *remap_idx)
|
|
{
|
|
uint16_t prn_s;
|
|
uint16_t prn_e;
|
|
uint16_t chan_idx;
|
|
|
|
prn_s = ble_ll_utils_csa2_prn_s(counter, chan_id);
|
|
prn_e = prn_s ^ chan_id;
|
|
|
|
*prn_sub_lu = prn_s;
|
|
|
|
chan_idx = ble_ll_utils_csa2_calc_chan_idx(prn_e, chan_map_used, chan_map,
|
|
remap_idx);
|
|
|
|
return chan_idx;
|
|
}
|
|
|
|
uint16_t
|
|
ble_ll_utils_dci_iso_subevent(uint16_t chan_id, uint16_t *prn_sub_lu,
|
|
uint8_t chan_map_used, const uint8_t *chan_map,
|
|
uint16_t *remap_idx)
|
|
{
|
|
uint16_t prn_sub_se;
|
|
uint16_t chan_idx;
|
|
uint16_t d;
|
|
|
|
*prn_sub_lu = ble_ll_utils_csa2_perm(*prn_sub_lu);
|
|
*prn_sub_lu = ble_ll_utils_csa2_mam(*prn_sub_lu, chan_id);
|
|
prn_sub_se = *prn_sub_lu ^ chan_id;
|
|
|
|
/* Core 5.3, Vol 6, Part B, 4.5.8.3.6 (enjoy!) */
|
|
/* TODO optimize this somehow */
|
|
d = MAX(1, MAX(MIN(3, chan_map_used - 5),
|
|
MIN(11, (chan_map_used - 10) / 2)));
|
|
*remap_idx = (*remap_idx + d + prn_sub_se *
|
|
(chan_map_used - 2 * d + 1) / 65536) % chan_map_used;
|
|
|
|
chan_idx = ble_ll_utils_csa2_remap2chan(*remap_idx, chan_map);
|
|
|
|
return chan_idx;
|
|
}
|
|
#endif
|
|
|
|
uint32_t
|
|
ble_ll_utils_calc_window_widening(uint32_t anchor_point,
|
|
uint32_t last_anchor_point,
|
|
uint8_t central_sca)
|
|
{
|
|
uint32_t total_sca_ppm;
|
|
uint32_t window_widening;
|
|
int32_t time_since_last_anchor;
|
|
uint32_t delta_msec;
|
|
|
|
window_widening = 0;
|
|
|
|
time_since_last_anchor = (int32_t)(anchor_point - last_anchor_point);
|
|
if (time_since_last_anchor > 0) {
|
|
delta_msec = ble_ll_tmr_t2u(time_since_last_anchor) / 1000;
|
|
total_sca_ppm = g_ble_sca_ppm_tbl[central_sca] + MYNEWT_VAL(BLE_LL_SCA);
|
|
window_widening = (total_sca_ppm * delta_msec) / 1000;
|
|
}
|
|
|
|
return window_widening;
|
|
}
|