blob: 5969dff1b68c2abdf0c655d202443624b3f3d0f7 [file] [log] [blame]
/*
* 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)
{
}