text: /*
   * 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
  };
  
  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 *chmap)
  {
      int i;
      int j;
      uint8_t mask;
      uint8_t chanbyte;
      uint8_t used_channels;
  
      used_channels = 0;
      for (i = 0; i < BLE_LL_CHMAP_LEN; ++i) {
          chanbyte = chmap[i];
          if (chanbyte) {
              if (chanbyte == 0xff) {
                  used_channels += 8;
              } else {
                  mask = 0x01;
                  for (j = 0; j < 8; ++j) {
                      if (chanbyte & mask) {
                          ++used_channels;
                      }
                      mask <<= 1;
                  }
              }
          }
      }
      return used_channels;
  }
  
  #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2)
  static uint16_t
  ble_ll_utils_csa2_perm(uint16_t in)
  {
      uint16_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;
  }
  
  static uint16_t
  ble_ll_utils_csa2_prng(uint16_t counter, uint16_t ch_id)
  {
      uint16_t prn_e;
  
      prn_e = counter ^ ch_id;
  
      prn_e = ble_ll_utils_csa2_perm(prn_e);
      prn_e = (prn_e * 17) + ch_id;
  
      prn_e = ble_ll_utils_csa2_perm(prn_e);
      prn_e = (prn_e * 17) + ch_id;
  
      prn_e = ble_ll_utils_csa2_perm(prn_e);
      prn_e = (prn_e * 17) + ch_id;
  
      prn_e = prn_e ^ ch_id;
  
      return prn_e;
  }
  
  uint8_t
  ble_ll_utils_calc_dci_csa2(uint16_t event_cntr, uint16_t channel_id,
                             uint8_t num_used_chans, const uint8_t *chanmap)
  {
      uint16_t channel_unmapped;
      uint8_t remap_index;
  
      uint16_t prn_e;
      uint8_t bitpos;
  
      prn_e = ble_ll_utils_csa2_prng(event_cntr, channel_id);
  
      channel_unmapped = prn_e % 37;
  
      /*
       * If unmapped channel is the channel index of a used channel it is used
       * as channel index.
       */
      bitpos = 1 << (channel_unmapped & 0x07);
      if (chanmap[channel_unmapped >> 3] & bitpos) {
          return channel_unmapped;
      }
  
      remap_index = (num_used_chans * prn_e) / 0x10000;
  
      return ble_ll_utils_remapped_channel(remap_index, chanmap);
  }
  #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;
  }
  