| /* |
| * 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; |
| } |