/* * 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 "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 }; uint32_t ble_ll_utils_calc_access_addr(void) { 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; /* Calculate a random access address */ aa = 0; while (1) { /* Get two, 16-bit random numbers */ aa_low = ble_ll_rand() & 0xFFFF; aa_high = ble_ll_rand() & 0xFFFF; /* All four bytes cannot be equal */ if (aa_low == aa_high) { continue; } /* Upper 6 bits must have 2 transitions */ tmp = (int16_t)aa_high >> 10; if (__builtin_popcount(tmp ^ (tmp >> 1)) < 2) { continue; } /* 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) { continue; } /* 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) { continue; } /* Cannot be more than 24 transitions */ if (transitions > 24) { continue; } /* We have a valid access address */ break; } return aa; } uint8_t ble_ll_utils_remapped_channel(uint8_t remap_index, const uint8_t *chanmap) { 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 = chanmap[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_calc_num_used_chans(const uint8_t *chan_map) { uint32_t u32 = 0; uint32_t num_used_chans = 0; unsigned idx; for (idx = 0; idx < 37; idx++) { if ((idx % 8) == 0) { u32 = chan_map[idx / 8]; } if (u32 & 1) { num_used_chans++; } u32 >>= 1; } return num_used_chans; } #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 num_used_chans, 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, num_used_chans, 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 num_used_chans, 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, num_used_chans - 5), min(11, (num_used_chans - 10) / 2))); *remap_idx = (*remap_idx + d + prn_sub_se * (num_used_chans - 2 * d + 1) / 65536) % num_used_chans; 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; }