/* * 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 #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; 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(((uint32_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; ble_hdr->rxinfo.rssi = -77; /* XXX: dummy rssi */ 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.aux_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; /* 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) == 1) /** * Called to enable encryption at the PHY. Note that this state will persist * in the PHY; in other words, if you call this function you have to call * disable so that future PHY transmits/receives will not be encrypted. * * @param pkt_counter * @param iv * @param key * @param is_master */ void ble_phy_encrypt_enable(uint64_t pkt_counter, uint8_t *iv, uint8_t *key, uint8_t is_master) { } void ble_phy_encrypt_set_pkt_cntr(uint64_t pkt_counter, int dir) { } 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_txpwr_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_txpower_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_txpwr_get(void) { return g_ble_phy_data.phy_txpwr_dbm; } /** * 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; } /** * 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) == 1) 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) { }