| /* |
| * 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 <stdint.h> |
| #include <string.h> |
| #include <assert.h> |
| #include "syscfg/syscfg.h" |
| #include "os/os.h" |
| #include "ble/xcvr.h" |
| #include "nimble/ble.h" |
| #include "nimble/nimble_opt.h" |
| #include "controller/ble_phy.h" |
| #include "controller/ble_ll.h" |
| |
| /* BLE PHY data structure */ |
| struct ble_phy_obj |
| { |
| uint8_t phy_stats_initialized; |
| int8_t phy_txpwr_dbm; |
| int16_t rx_pwr_compensation; |
| uint8_t phy_chan; |
| uint8_t phy_state; |
| uint8_t phy_transition; |
| uint8_t phy_rx_started; |
| uint8_t phy_encrypted; |
| uint8_t phy_privacy; |
| uint8_t phy_tx_pyld_len; |
| uint32_t phy_aar_scratch; |
| uint32_t phy_access_address; |
| struct ble_mbuf_hdr rxhdr; |
| void *txend_arg; |
| uint8_t *rxdptr; |
| ble_phy_tx_end_func txend_cb; |
| }; |
| struct ble_phy_obj g_ble_phy_data; |
| |
| /* Statistics */ |
| struct ble_phy_statistics |
| { |
| uint32_t tx_good; |
| uint32_t tx_fail; |
| uint32_t tx_late; |
| uint32_t tx_bytes; |
| uint32_t rx_starts; |
| uint32_t rx_aborts; |
| uint32_t rx_valid; |
| uint32_t rx_crc_err; |
| uint32_t phy_isrs; |
| uint32_t radio_state_errs; |
| uint32_t no_bufs; |
| }; |
| |
| struct ble_phy_statistics g_ble_phy_stats; |
| |
| static uint8_t g_ble_phy_tx_buf[BLE_PHY_MAX_PDU_LEN]; |
| |
| /* XCVR object to emulate transceiver */ |
| struct xcvr_data |
| { |
| uint32_t irq_status; |
| }; |
| static struct xcvr_data g_xcvr_data; |
| |
| #define BLE_XCVR_IRQ_F_RX_START (0x00000001) |
| #define BLE_XCVR_IRQ_F_RX_END (0x00000002) |
| #define BLE_XCVR_IRQ_F_TX_START (0x00000004) |
| #define BLE_XCVR_IRQ_F_TX_END (0x00000008) |
| #define BLE_XCVR_IRQ_F_BYTE_CNTR (0x00000010) |
| |
| /* "Rail" power level if outside supported range */ |
| #define BLE_XCVR_TX_PWR_MAX_DBM (30) |
| #define BLE_XCVR_TX_PWR_MIN_DBM (-20) |
| |
| /* 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(no_bufs) |
| 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, no_bufs) |
| 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) |
| |
| /* XXX: TODO: |
| |
| * 1) Test the following to make sure it works: suppose an event is already |
| * set to 1 and the interrupt is not enabled. What happens if you enable the |
| * interrupt with the event bit already set to 1 |
| * 2) how to deal with interrupts? |
| */ |
| static uint32_t |
| ble_xcvr_get_irq_status(void) |
| { |
| return g_xcvr_data.irq_status; |
| } |
| |
| static void |
| ble_xcvr_clear_irq(uint32_t mask) |
| { |
| g_xcvr_data.irq_status &= ~mask; |
| } |
| |
| /** |
| * 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) |
| { |
| uint16_t rem_bytes; |
| uint16_t mb_bytes; |
| uint16_t copylen; |
| uint32_t *dst; |
| uint32_t *src; |
| struct os_mbuf *m; |
| struct ble_mbuf_hdr *ble_hdr; |
| struct os_mbuf_pkthdr *pkthdr; |
| |
| /* Better be aligned */ |
| assert(((uintptr_t)dptr & 3) == 0); |
| |
| pkthdr = OS_MBUF_PKTHDR(rxpdu); |
| rem_bytes = pkthdr->omp_len; |
| |
| /* Fill in the mbuf pkthdr first. */ |
| dst = (uint32_t *)(rxpdu->om_data); |
| src = (uint32_t *)dptr; |
| |
| mb_bytes = (rxpdu->om_omp->omp_databuf_len - rxpdu->om_pkthdr_len - 4); |
| copylen = min(mb_bytes, rem_bytes); |
| copylen &= 0xFFFC; |
| rem_bytes -= copylen; |
| mb_bytes -= copylen; |
| rxpdu->om_len = copylen; |
| while (copylen > 0) { |
| *dst = *src; |
| ++dst; |
| ++src; |
| copylen -= 4; |
| } |
| |
| /* Copy remaining bytes */ |
| m = rxpdu; |
| while (rem_bytes > 0) { |
| /* If there are enough bytes in the mbuf, copy them and leave */ |
| if (rem_bytes <= mb_bytes) { |
| memcpy(m->om_data + m->om_len, src, rem_bytes); |
| m->om_len += rem_bytes; |
| break; |
| } |
| |
| m = SLIST_NEXT(m, om_next); |
| assert(m != NULL); |
| |
| mb_bytes = m->om_omp->omp_databuf_len; |
| copylen = min(mb_bytes, rem_bytes); |
| copylen &= 0xFFFC; |
| rem_bytes -= copylen; |
| mb_bytes -= copylen; |
| m->om_len = copylen; |
| dst = (uint32_t *)m->om_data; |
| while (copylen > 0) { |
| *dst = *src; |
| ++dst; |
| ++src; |
| copylen -= 4; |
| } |
| } |
| |
| /* Copy ble header */ |
| ble_hdr = BLE_MBUF_HDR_PTR(rxpdu); |
| memcpy(ble_hdr, &g_ble_phy_data.rxhdr, sizeof(struct ble_mbuf_hdr)); |
| } |
| |
| void |
| ble_phy_isr(void) |
| { |
| int rc; |
| uint8_t transition; |
| uint32_t irq_en; |
| struct ble_mbuf_hdr *ble_hdr; |
| |
| /* Check for disabled event. This only happens for transmits now */ |
| irq_en = ble_xcvr_get_irq_status(); |
| if (irq_en & BLE_XCVR_IRQ_F_TX_END) { |
| |
| /* Better be in TX state! */ |
| assert(g_ble_phy_data.phy_state == BLE_PHY_STATE_TX); |
| ble_xcvr_clear_irq(BLE_XCVR_IRQ_F_TX_END); |
| |
| transition = g_ble_phy_data.phy_transition; |
| if (transition == BLE_PHY_TRANSITION_TX_RX) { |
| /* Disable the phy */ |
| /* XXX: count no bufs? */ |
| ble_phy_disable(); |
| } else { |
| /* Better not be going from rx to tx! */ |
| assert(transition == BLE_PHY_TRANSITION_NONE); |
| } |
| } |
| |
| /* We get this if we have started to receive a frame */ |
| if (irq_en & BLE_XCVR_IRQ_F_RX_START) { |
| |
| ble_xcvr_clear_irq(BLE_XCVR_IRQ_F_RX_START); |
| |
| /* Call Link Layer receive start function */ |
| rc = ble_ll_rx_start(g_ble_phy_data.rxdptr, g_ble_phy_data.phy_chan, |
| &g_ble_phy_data.rxhdr); |
| if (rc >= 0) { |
| /* XXX: set rx end enable isr */ |
| } else { |
| /* Disable PHY */ |
| ble_phy_disable(); |
| irq_en = 0; |
| ++g_ble_phy_stats.rx_aborts; |
| } |
| |
| /* Count rx starts */ |
| ++g_ble_phy_stats.rx_starts; |
| } |
| |
| /* Receive packet end (we dont enable this for transmit) */ |
| if (irq_en & BLE_XCVR_IRQ_F_RX_END) { |
| |
| ble_xcvr_clear_irq(BLE_XCVR_IRQ_F_RX_END); |
| |
| /* Construct BLE header before handing up */ |
| ble_hdr = &g_ble_phy_data.rxhdr; |
| ble_hdr->rxinfo.flags = 0; |
| /* XXX: dummy rssi */ |
| ble_hdr->rxinfo.rssi = -77 + g_ble_phy_data.rx_pwr_compensation; |
| ble_hdr->rxinfo.channel = g_ble_phy_data.phy_chan; |
| ble_hdr->rxinfo.phy = BLE_PHY_1M; |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| ble_hdr->rxinfo.user_data = NULL; |
| #endif |
| |
| /* Count PHY valid packets */ |
| ++g_ble_phy_stats.rx_valid; |
| ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK; |
| |
| /* Call Link Layer receive payload function */ |
| rc = ble_ll_rx_end(g_ble_phy_data.rxdptr, ble_hdr); |
| if (rc < 0) { |
| /* Disable the PHY. */ |
| ble_phy_disable(); |
| } |
| } |
| |
| /* Count # of interrupts */ |
| ++g_ble_phy_stats.phy_isrs; |
| } |
| |
| /** |
| * ble phy init |
| * |
| * Initialize the PHY. This is expected to be called once. |
| * |
| * @return int 0: success; PHY error code otherwise |
| */ |
| int |
| ble_phy_init(void) |
| { |
| /* Set phy channel to an invalid channel so first set channel works */ |
| g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; |
| g_ble_phy_data.phy_chan = BLE_PHY_NUM_CHANS; |
| |
| g_ble_phy_data.rx_pwr_compensation = 0; |
| |
| /* XXX: emulate ISR? */ |
| |
| return 0; |
| } |
| |
| int |
| ble_phy_rx(void) |
| { |
| /* Check radio state */ |
| if (ble_phy_state_get() != BLE_PHY_STATE_IDLE) { |
| ble_phy_disable(); |
| ++g_ble_phy_stats.radio_state_errs; |
| return BLE_PHY_ERR_RADIO_STATE; |
| } |
| |
| g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; |
| |
| return 0; |
| } |
| |
| void |
| ble_phy_restart_rx(void) |
| { |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) |
| void |
| ble_phy_encrypt_enable(const uint8_t *key) |
| { |
| } |
| |
| void |
| ble_phy_encrypt_header_mask_set(uint8_t mask) |
| { |
| } |
| |
| void |
| ble_phy_encrypt_iv_set(const uint8_t *iv) |
| { |
| } |
| |
| void |
| ble_phy_encrypt_counter_set(uint64_t counter, uint8_t dir_bit) |
| { |
| } |
| |
| void |
| ble_phy_encrypt_disable(void) |
| { |
| } |
| #endif |
| |
| void |
| ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg) |
| { |
| /* Set transmit end callback and arg */ |
| g_ble_phy_data.txend_cb = txend_cb; |
| g_ble_phy_data.txend_arg = arg; |
| } |
| |
| /** |
| * Called to set the start time of a transmission. |
| * |
| * This function is called to set the start time when we are not going from |
| * rx to tx automatically. |
| * |
| * NOTE: care must be taken when calling this function. The channel should |
| * already be set. |
| * |
| * @param cputime |
| * @param rem_usecs |
| * |
| * @return int |
| */ |
| int |
| ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs) |
| { |
| return 0; |
| } |
| |
| /** |
| * Called to set the start time of a reception |
| * |
| * This function acts a bit differently than transmit. If we are late getting |
| * here we will still attempt to receive. |
| * |
| * NOTE: care must be taken when calling this function. The channel should |
| * already be set. |
| * |
| * @param cputime |
| * @param rem_usecs |
| * |
| * @return int |
| */ |
| int |
| ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) |
| { |
| return 0; |
| } |
| |
| |
| int |
| ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) |
| { |
| uint8_t hdr_byte; |
| int rc; |
| |
| if (ble_phy_state_get() != BLE_PHY_STATE_IDLE) { |
| ble_phy_disable(); |
| ++g_ble_phy_stats.radio_state_errs; |
| return BLE_PHY_ERR_RADIO_STATE; |
| } |
| |
| /* Select tx address */ |
| if (g_ble_phy_data.phy_chan < BLE_PHY_NUM_DATA_CHANS) { |
| /* XXX: fix this */ |
| assert(0); |
| } else { |
| } |
| |
| /* Set the PHY transition */ |
| g_ble_phy_data.phy_transition = end_trans; |
| |
| /* Set phy state to transmitting and count packet statistics */ |
| g_ble_phy_data.phy_state = BLE_PHY_STATE_TX; |
| ++g_ble_phy_stats.tx_good; |
| g_ble_phy_stats.tx_bytes += pducb(g_ble_phy_tx_buf, pducb_arg, &hdr_byte) + |
| BLE_LL_PDU_HDR_LEN; |
| rc = BLE_ERR_SUCCESS; |
| |
| return rc; |
| } |
| |
| /** |
| * ble phy txpwr set |
| * |
| * Set the transmit output power (in dBm). |
| * |
| * NOTE: If the output power specified is within the BLE limits but outside |
| * the chip limits, we "rail" the power level so we dont exceed the min/max |
| * chip values. |
| * |
| * @param dbm Power output in dBm. |
| * |
| * @return int 0: success; anything else is an error |
| */ |
| int |
| ble_phy_tx_power_set(int dbm) |
| { |
| /* Check valid range */ |
| assert(dbm <= BLE_PHY_MAX_PWR_DBM); |
| |
| /* "Rail" power level if outside supported range */ |
| if (dbm > BLE_XCVR_TX_PWR_MAX_DBM) { |
| dbm = BLE_XCVR_TX_PWR_MAX_DBM; |
| } else { |
| if (dbm < BLE_XCVR_TX_PWR_MIN_DBM) { |
| dbm = BLE_XCVR_TX_PWR_MIN_DBM; |
| } |
| } |
| |
| g_ble_phy_data.phy_txpwr_dbm = dbm; |
| |
| return 0; |
| } |
| |
| /** |
| * ble phy txpwr round |
| * |
| * Get the rounded transmit output power (in dBm). |
| * |
| * @param dbm Power output in dBm. |
| * |
| * @return int Rounded power in dBm |
| */ |
| int |
| ble_phy_tx_power_round(int dbm) |
| { |
| /* "Rail" power level if outside supported range */ |
| if (dbm > BLE_XCVR_TX_PWR_MAX_DBM) { |
| dbm = BLE_XCVR_TX_PWR_MAX_DBM; |
| } else { |
| if (dbm < BLE_XCVR_TX_PWR_MIN_DBM) { |
| dbm = BLE_XCVR_TX_PWR_MIN_DBM; |
| } |
| } |
| |
| return dbm; |
| } |
| |
| /** |
| * ble phy txpwr get |
| * |
| * Get the transmit power. |
| * |
| * @return int The current PHY transmit power, in dBm |
| */ |
| int |
| ble_phy_tx_power_get(void) |
| { |
| return g_ble_phy_data.phy_txpwr_dbm; |
| } |
| |
| void |
| ble_phy_set_rx_pwr_compensation(int8_t compensation) |
| { |
| g_ble_phy_data.rx_pwr_compensation = compensation; |
| } |
| |
| /** |
| * ble phy setchan |
| * |
| * Sets the logical frequency of the transceiver. The input parameter is the |
| * BLE channel index (0 to 39, inclusive). The NRF52 frequency register |
| * works like this: logical frequency = 2400 + FREQ (MHz). |
| * |
| * Thus, to get a logical frequency of 2402 MHz, you would program the |
| * FREQUENCY register to 2. |
| * |
| * @param chan This is the Data Channel Index or Advertising Channel index |
| * |
| * @return int 0: success; PHY error code otherwise |
| */ |
| int |
| ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit) |
| { |
| assert(chan < BLE_PHY_NUM_CHANS); |
| |
| /* Check for valid channel range */ |
| if (chan >= BLE_PHY_NUM_CHANS) { |
| return BLE_PHY_ERR_INV_PARAM; |
| } |
| |
| g_ble_phy_data.phy_access_address = access_addr; |
| |
| g_ble_phy_data.phy_chan = chan; |
| |
| return 0; |
| } |
| |
| uint8_t |
| ble_phy_chan_get(void) |
| { |
| return g_ble_phy_data.phy_chan; |
| } |
| |
| /** |
| * Disable the PHY. This will do the following: |
| * -> Turn off all phy interrupts. |
| * -> Disable internal shortcuts. |
| * -> Disable the radio. |
| * -> Sets phy state to idle. |
| */ |
| void |
| ble_phy_disable(void) |
| { |
| g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; |
| } |
| |
| /* Gets the current access address */ |
| uint32_t ble_phy_access_addr_get(void) |
| { |
| return g_ble_phy_data.phy_access_address; |
| } |
| |
| /** |
| * Return the phy state |
| * |
| * @return int The current PHY state. |
| */ |
| int |
| ble_phy_state_get(void) |
| { |
| return g_ble_phy_data.phy_state; |
| } |
| |
| /** |
| * Called to see if a reception has started |
| * |
| * @return int |
| */ |
| int |
| ble_phy_rx_started(void) |
| { |
| return g_ble_phy_data.phy_rx_started; |
| } |
| |
| /** |
| * Called to return the maximum data pdu payload length supported by the |
| * phy. For this chip, if encryption is enabled, the maximum payload is 27 |
| * bytes. |
| * |
| * @return uint8_t Maximum data channel PDU payload size supported |
| */ |
| uint8_t |
| ble_phy_max_data_pdu_pyld(void) |
| { |
| return BLE_LL_DATA_PDU_MAX_PYLD; |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) |
| void |
| ble_phy_resolv_list_enable(void) |
| { |
| g_ble_phy_data.phy_privacy = 1; |
| } |
| |
| void |
| ble_phy_resolv_list_disable(void) |
| { |
| g_ble_phy_data.phy_privacy = 0; |
| } |
| |
| /** |
| * Return the transceiver state |
| * |
| * @return int transceiver state. |
| */ |
| uint8_t |
| ble_phy_xcvr_state_get(void) |
| { |
| return g_ble_phy_data.phy_state; |
| } |
| |
| #endif |
| |
| void |
| ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs) |
| { |
| } |
| |
| void |
| ble_phy_rfclk_enable(void) |
| { |
| } |
| |
| void |
| ble_phy_rfclk_disable(void) |
| { |
| } |
| |
| void |
| ble_phy_tifs_txtx_set(uint16_t usecs, uint8_t anchor) |
| { |
| } |