| /* |
| * Copyright (c) 2017-2021 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <stddef.h> |
| #include <stdbool.h> |
| #include <errno.h> |
| |
| #include <zephyr/zephyr.h> |
| #include <soc.h> |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/entropy.h> |
| #include <zephyr/bluetooth/hci.h> |
| |
| #include "hal/cpu.h" |
| #include "hal/ccm.h" |
| #include "hal/cntr.h" |
| #include "hal/ticker.h" |
| |
| #include "util/util.h" |
| #include "util/mem.h" |
| #include "util/mfifo.h" |
| #include "util/memq.h" |
| #include "util/mayfly.h" |
| #include "util/dbuf.h" |
| |
| #include "ticker/ticker.h" |
| |
| #include "pdu.h" |
| |
| #include "lll.h" |
| #include "lll/lll_vendor.h" |
| #include "lll/lll_adv_types.h" |
| #include "lll_adv.h" |
| #include "lll/lll_adv_pdu.h" |
| #include "lll_chan.h" |
| #include "lll_scan.h" |
| #include "lll/lll_df_types.h" |
| #include "lll_sync.h" |
| #include "lll_sync_iso.h" |
| #include "lll_iso_tx.h" |
| #include "lll_conn.h" |
| #include "lll_df.h" |
| |
| #include "ull_adv_types.h" |
| #include "ull_scan_types.h" |
| #include "ull_sync_types.h" |
| #if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY) |
| #include "ull_tx_queue.h" |
| #endif |
| #include "ull_conn_types.h" |
| #include "ull_filter.h" |
| #include "ull_df_types.h" |
| #include "ull_df_internal.h" |
| |
| #if defined(CONFIG_BT_CTLR_USER_EXT) |
| #include "ull_vendor.h" |
| #endif /* CONFIG_BT_CTLR_USER_EXT */ |
| |
| #include "isoal.h" |
| #include "ull_internal.h" |
| #include "ull_iso_internal.h" |
| #include "ull_adv_internal.h" |
| #include "ull_scan_internal.h" |
| #include "ull_sync_internal.h" |
| #include "ull_sync_iso_internal.h" |
| #include "ull_central_internal.h" |
| #include "ull_iso_types.h" |
| #include "ull_conn_internal.h" |
| #include "lll_conn_iso.h" |
| #include "ull_conn_iso_types.h" |
| #include "ull_central_iso_internal.h" |
| |
| #include "ull_conn_iso_internal.h" |
| #include "ull_peripheral_iso_internal.h" |
| |
| #include "ll.h" |
| #include "ll_feat.h" |
| #include "ll_test.h" |
| #include "ll_settings.h" |
| |
| #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) |
| #define LOG_MODULE_NAME bt_ctlr_ull |
| #include "common/log.h" |
| #include "hal/debug.h" |
| |
| #if defined(CONFIG_BT_BROADCASTER) |
| #define BT_ADV_TICKER_NODES ((TICKER_ID_ADV_LAST) - (TICKER_ID_ADV_STOP) + 1) |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) && (CONFIG_BT_CTLR_ADV_AUX_SET > 0) |
| #define BT_ADV_AUX_TICKER_NODES ((TICKER_ID_ADV_AUX_LAST) - \ |
| (TICKER_ID_ADV_AUX_BASE) + 1) |
| #if defined(CONFIG_BT_CTLR_ADV_PERIODIC) |
| #define BT_ADV_SYNC_TICKER_NODES ((TICKER_ID_ADV_SYNC_LAST) - \ |
| (TICKER_ID_ADV_SYNC_BASE) + 1) |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) |
| #define BT_ADV_ISO_TICKER_NODES ((TICKER_ID_ADV_ISO_LAST) - \ |
| (TICKER_ID_ADV_ISO_BASE) + 1) |
| #else /* !CONFIG_BT_CTLR_ADV_ISO */ |
| #define BT_ADV_ISO_TICKER_NODES 0 |
| #endif /* !CONFIG_BT_CTLR_ADV_ISO */ |
| #else /* !CONFIG_BT_CTLR_ADV_PERIODIC */ |
| #define BT_ADV_SYNC_TICKER_NODES 0 |
| #define BT_ADV_ISO_TICKER_NODES 0 |
| #endif /* !CONFIG_BT_CTLR_ADV_PERIODIC */ |
| #else /* (CONFIG_BT_CTLR_ADV_AUX_SET > 0) */ |
| #define BT_ADV_AUX_TICKER_NODES 0 |
| #define BT_ADV_SYNC_TICKER_NODES 0 |
| #define BT_ADV_ISO_TICKER_NODES 0 |
| #endif /* (CONFIG_BT_CTLR_ADV_AUX_SET > 0) */ |
| #else /* !CONFIG_BT_BROADCASTER */ |
| #define BT_ADV_TICKER_NODES 0 |
| #define BT_ADV_AUX_TICKER_NODES 0 |
| #define BT_ADV_SYNC_TICKER_NODES 0 |
| #define BT_ADV_ISO_TICKER_NODES 0 |
| #endif /* !CONFIG_BT_BROADCASTER */ |
| |
| #if defined(CONFIG_BT_OBSERVER) |
| #define BT_SCAN_TICKER_NODES ((TICKER_ID_SCAN_LAST) - (TICKER_ID_SCAN_STOP) + 1) |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) |
| #define BT_SCAN_AUX_TICKER_NODES ((TICKER_ID_SCAN_AUX_LAST) - \ |
| (TICKER_ID_SCAN_AUX_BASE) + 1) |
| #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| #define BT_SCAN_SYNC_TICKER_NODES ((TICKER_ID_SCAN_SYNC_LAST) - \ |
| (TICKER_ID_SCAN_SYNC_BASE) + 1) |
| #if defined(CONFIG_BT_CTLR_SYNC_ISO) |
| #define BT_SCAN_SYNC_ISO_TICKER_NODES ((TICKER_ID_SCAN_SYNC_ISO_LAST) - \ |
| (TICKER_ID_SCAN_SYNC_ISO_BASE) + 1) |
| #else /* !CONFIG_BT_CTLR_SYNC_ISO */ |
| #define BT_SCAN_SYNC_ISO_TICKER_NODES 0 |
| #endif /* !CONFIG_BT_CTLR_SYNC_ISO */ |
| #else /* !CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| #define BT_SCAN_SYNC_TICKER_NODES 0 |
| #define BT_SCAN_SYNC_ISO_TICKER_NODES 0 |
| #endif /* !CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| #else /* !CONFIG_BT_CTLR_ADV_EXT */ |
| #define BT_SCAN_AUX_TICKER_NODES 0 |
| #define BT_SCAN_SYNC_TICKER_NODES 0 |
| #define BT_SCAN_SYNC_ISO_TICKER_NODES 0 |
| #endif /* !CONFIG_BT_CTLR_ADV_EXT */ |
| #else |
| #define BT_SCAN_TICKER_NODES 0 |
| #define BT_SCAN_AUX_TICKER_NODES 0 |
| #define BT_SCAN_SYNC_TICKER_NODES 0 |
| #define BT_SCAN_SYNC_ISO_TICKER_NODES 0 |
| #endif |
| |
| #if defined(CONFIG_BT_CONN) |
| #define BT_CONN_TICKER_NODES ((TICKER_ID_CONN_LAST) - (TICKER_ID_CONN_BASE) + 1) |
| #else |
| #define BT_CONN_TICKER_NODES 0 |
| #endif |
| |
| #if defined(CONFIG_BT_CTLR_CONN_ISO) |
| #define BT_CIG_TICKER_NODES ((TICKER_ID_CONN_ISO_LAST) - \ |
| (TICKER_ID_CONN_ISO_BASE) + 1 + \ |
| (TICKER_ID_CONN_ISO_RESUME_LAST) - \ |
| (TICKER_ID_CONN_ISO_RESUME_BASE) + 1) |
| |
| #else |
| #define BT_CIG_TICKER_NODES 0 |
| #endif |
| |
| #if defined(CONFIG_BT_CTLR_USER_EXT) |
| #define USER_TICKER_NODES CONFIG_BT_CTLR_USER_TICKER_ID_RANGE |
| #else |
| #define USER_TICKER_NODES 0 |
| #endif |
| |
| #if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC_TICKER) |
| #define FLASH_TICKER_NODES 2 /* No. of tickers reserved for flash |
| * driver |
| */ |
| #define TICKER_USER_ULL_HIGH_FLASH_OPS 1 /* No. of additional ticker ULL_HIGH |
| * context operations |
| */ |
| #define TICKER_USER_THREAD_FLASH_OPS 1 /* No. of additional ticker thread |
| * context operations |
| */ |
| #else |
| #define FLASH_TICKER_NODES 0 |
| #define TICKER_USER_ULL_HIGH_FLASH_OPS 0 |
| #define TICKER_USER_THREAD_FLASH_OPS 0 |
| #endif |
| |
| /* Define ticker nodes */ |
| /* NOTE: FLASH_TICKER_NODES shall be after Link Layer's list of ticker id |
| * allocations, refer to ll_timeslice_ticker_id_get on how ticker id |
| * used by flash driver is returned. |
| */ |
| #define TICKER_NODES (TICKER_ID_ULL_BASE + \ |
| BT_ADV_TICKER_NODES + \ |
| BT_ADV_AUX_TICKER_NODES + \ |
| BT_ADV_SYNC_TICKER_NODES + \ |
| BT_ADV_ISO_TICKER_NODES + \ |
| BT_SCAN_TICKER_NODES + \ |
| BT_SCAN_AUX_TICKER_NODES + \ |
| BT_SCAN_SYNC_TICKER_NODES + \ |
| BT_SCAN_SYNC_ISO_TICKER_NODES + \ |
| BT_CONN_TICKER_NODES + \ |
| BT_CIG_TICKER_NODES + \ |
| USER_TICKER_NODES + \ |
| FLASH_TICKER_NODES) |
| |
| /* When both central and peripheral are supported, one each Rx node will be |
| * needed by connectable advertising and the initiator to generate connection |
| * complete event, hence conditionally set the count. |
| */ |
| #if defined(CONFIG_BT_MAX_CONN) |
| #if defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_PERIPHERAL) |
| #define BT_CTLR_MAX_CONNECTABLE 2 |
| #else |
| #define BT_CTLR_MAX_CONNECTABLE 1 |
| #endif |
| #define BT_CTLR_MAX_CONN CONFIG_BT_MAX_CONN |
| #else |
| #define BT_CTLR_MAX_CONNECTABLE 0 |
| #define BT_CTLR_MAX_CONN 0 |
| #endif |
| |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_OBSERVER) |
| #if defined(CONFIG_BT_CTLR_DF_PER_SCAN_CTE_NUM_MAX) |
| /* Note: Need node for PDU and CTE sample */ |
| #define BT_CTLR_ADV_EXT_RX_CNT (CONFIG_BT_CTLR_SCAN_AUX_SET * \ |
| CONFIG_BT_CTLR_DF_PER_SCAN_CTE_NUM_MAX * 2) |
| #else /* !CONFIG_BT_CTLR_DF_PER_SCAN_CTE_NUM_MAX */ |
| #define BT_CTLR_ADV_EXT_RX_CNT 1 |
| #endif /* !CONFIG_BT_CTLR_DF_PER_SCAN_CTE_NUM_MAX */ |
| #else /* !CONFIG_BT_CTLR_ADV_EXT || !CONFIG_BT_OBSERVER */ |
| #define BT_CTLR_ADV_EXT_RX_CNT 0 |
| #endif /* !CONFIG_BT_CTLR_ADV_EXT || !CONFIG_BT_OBSERVER */ |
| |
| #if !defined(TICKER_USER_LLL_VENDOR_OPS) |
| #define TICKER_USER_LLL_VENDOR_OPS 0 |
| #endif /* TICKER_USER_LLL_VENDOR_OPS */ |
| |
| #if !defined(TICKER_USER_ULL_HIGH_VENDOR_OPS) |
| #define TICKER_USER_ULL_HIGH_VENDOR_OPS 0 |
| #endif /* TICKER_USER_ULL_HIGH_VENDOR_OPS */ |
| |
| #if !defined(TICKER_USER_THREAD_VENDOR_OPS) |
| #define TICKER_USER_THREAD_VENDOR_OPS 0 |
| #endif /* TICKER_USER_THREAD_VENDOR_OPS */ |
| |
| /* Define ticker user operations */ |
| #if defined(CONFIG_BT_CTLR_LOW_LAT) && \ |
| (CONFIG_BT_CTLR_LLL_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO) |
| /* NOTE: When ticker job is disabled inside radio events then all advertising, |
| * scanning, and peripheral latency cancel ticker operations will be deferred, |
| * requiring increased ticker thread context operation queue count. |
| */ |
| #define TICKER_USER_THREAD_OPS (BT_CTLR_ADV_SET + BT_CTLR_SCAN_SET + \ |
| BT_CTLR_MAX_CONN + \ |
| TICKER_USER_THREAD_VENDOR_OPS + \ |
| TICKER_USER_THREAD_FLASH_OPS + \ |
| 1) |
| #else /* !CONFIG_BT_CTLR_LOW_LAT */ |
| /* NOTE: As ticker job is not disabled inside radio events, no need for extra |
| * thread operations queue element for flash driver. |
| */ |
| #define TICKER_USER_THREAD_OPS (1 + TICKER_USER_THREAD_VENDOR_OPS + 1) |
| #endif /* !CONFIG_BT_CTLR_LOW_LAT */ |
| |
| #define TICKER_USER_ULL_LOW_OPS (1 + 1) |
| |
| /* NOTE: When ULL_LOW priority is configured to lower than ULL_HIGH, then extra |
| * ULL_HIGH operations queue elements are required to buffer the |
| * requested ticker operations. |
| */ |
| #if defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_CTLR_ADV_EXT) && \ |
| defined(CONFIG_BT_CTLR_PHY_CODED) |
| #define TICKER_USER_ULL_HIGH_OPS (4 + TICKER_USER_ULL_HIGH_VENDOR_OPS + \ |
| TICKER_USER_ULL_HIGH_FLASH_OPS + 1) |
| #else /* !CONFIG_BT_CENTRAL || !CONFIG_BT_CTLR_ADV_EXT || |
| * !CONFIG_BT_CTLR_PHY_CODED |
| */ |
| #define TICKER_USER_ULL_HIGH_OPS (3 + TICKER_USER_ULL_HIGH_VENDOR_OPS + \ |
| TICKER_USER_ULL_HIGH_FLASH_OPS + 1) |
| #endif /* !CONFIG_BT_CENTRAL || !CONFIG_BT_CTLR_ADV_EXT || |
| * !CONFIG_BT_CTLR_PHY_CODED |
| */ |
| |
| #define TICKER_USER_LLL_OPS (3 + TICKER_USER_LLL_VENDOR_OPS + 1) |
| |
| #define TICKER_USER_OPS (TICKER_USER_LLL_OPS + \ |
| TICKER_USER_ULL_HIGH_OPS + \ |
| TICKER_USER_ULL_LOW_OPS + \ |
| TICKER_USER_THREAD_OPS) |
| |
| /* Memory for ticker nodes/instances */ |
| static uint8_t MALIGN(4) ticker_nodes[TICKER_NODES][TICKER_NODE_T_SIZE]; |
| |
| /* Memory for users/contexts operating on ticker module */ |
| static uint8_t MALIGN(4) ticker_users[MAYFLY_CALLER_COUNT][TICKER_USER_T_SIZE]; |
| |
| /* Memory for user/context simultaneous API operations */ |
| static uint8_t MALIGN(4) ticker_user_ops[TICKER_USER_OPS][TICKER_USER_OP_T_SIZE]; |
| |
| /* Semaphore to wakeup thread on ticker API callback */ |
| static struct k_sem sem_ticker_api_cb; |
| |
| /* Semaphore to wakeup thread on Rx-ed objects */ |
| static struct k_sem *sem_recv; |
| |
| /* Declare prepare-event FIFO: mfifo_prep. |
| * Queue of struct node_rx_event_done |
| */ |
| static MFIFO_DEFINE(prep, sizeof(struct lll_event), EVENT_PIPELINE_MAX); |
| |
| /* Declare done-event RXFIFO. This is a composite pool-backed MFIFO for rx_nodes. |
| * The declaration constructs the following data structures: |
| * - mfifo_done: FIFO with pointers to struct node_rx_event_done |
| * - mem_done: Backing data pool for struct node_rx_event_done elements |
| * - mem_link_done: Pool of memq_link_t elements |
| * |
| * An extra link may be reserved for use by the ull_done memq (EVENT_DONE_LINK_CNT). |
| * |
| * Queue of pointers to struct node_rx_event_done. |
| * The actual backing behind these pointers is mem_done. |
| * |
| * When there are radio events with time reservations lower than the preemption |
| * timeout of 1.5 ms, the pipeline has to account for the maximum radio events |
| * that can be enqueued during the preempt timeout duration. All these enqueued |
| * events could be aborted in case of late scheduling, needing as many done |
| * event buffers. |
| * |
| * During continuous scanning, there can be 1 active radio event, 1 scan resume |
| * and 1 new scan prepare. If there are peripheral prepares in addition, and due |
| * to late scheduling all these will abort needing 4 done buffers. |
| * |
| * If there are additional peripheral prepares enqueued, which are apart by |
| * their time reservations, these are not yet late and hence no more additional |
| * done buffers are needed. |
| * |
| * If Extended Scanning is supported, then an additional auxiliary scan event's |
| * prepare could be enqueued in the pipeline during the preemption duration. |
| * |
| * If Extended Scanning with Coded PHY is supported, then an additional 1 resume |
| * prepare could be enqueued in the pipeline during the preemption duration. |
| */ |
| #if !defined(VENDOR_EVENT_DONE_MAX) |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_OBSERVER) |
| #if defined(CONFIG_BT_CTLR_PHY_CODED) |
| #define EVENT_DONE_MAX 6 |
| #else /* !CONFIG_BT_CTLR_PHY_CODED */ |
| #define EVENT_DONE_MAX 5 |
| #endif /* !CONFIG_BT_CTLR_PHY_CODED */ |
| #else /* !CONFIG_BT_CTLR_ADV_EXT || !CONFIG_BT_OBSERVER */ |
| #define EVENT_DONE_MAX 4 |
| #endif /* !CONFIG_BT_CTLR_ADV_EXT || !CONFIG_BT_OBSERVER */ |
| #else |
| #define EVENT_DONE_MAX VENDOR_EVENT_DONE_MAX |
| #endif |
| |
| static RXFIFO_DEFINE(done, sizeof(struct node_rx_event_done), |
| EVENT_DONE_MAX, EVENT_DONE_LINK_CNT); |
| |
| /* Minimum number of node rx for ULL to LL/HCI thread per connection. |
| * Increasing this by times the max. simultaneous connection count will permit |
| * simultaneous parallel PHY update or Connection Update procedures amongst |
| * active connections. |
| * Minimum node rx of 2 that can be reserved happens when: |
| * - for legacy LLCPs: |
| * Local central initiated PHY Update reserves 2 node rx, |
| * one for PHY update complete and another for Data Length Update complete |
| * notification. Otherwise, a peripheral only needs 1 additional node rx to |
| * generate Data Length Update complete when PHY Update completes; node rx for |
| * PHY update complete is reserved as the received PHY Update Ind PDU. |
| * - for new LLCPs: |
| * Central and peripheral always use two new nodes for handling completion |
| * notification one for PHY update complete and another for Data Length Update |
| * complete. |
| */ |
| #if defined(CONFIG_BT_CTLR_DATA_LENGTH) && defined(CONFIG_BT_CTLR_PHY) && \ |
| (defined(CONFIG_BT_LL_SW_LLCP_LEGACY) && defined(CONFIG_BT_CENTRAL) || \ |
| !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)) |
| #define LL_PDU_RX_CNT (2 * (CONFIG_BT_CTLR_LLCP_CONN)) |
| #elif defined(CONFIG_BT_CONN) |
| #define LL_PDU_RX_CNT (CONFIG_BT_CTLR_LLCP_CONN) |
| #else |
| #define LL_PDU_RX_CNT 0 |
| #endif |
| |
| /* No. of node rx for LLL to ULL. |
| * Reserve 3, 1 for adv data, 1 for scan response and 1 for empty PDU reception. |
| */ |
| #define PDU_RX_CNT (3 + BT_CTLR_ADV_EXT_RX_CNT + CONFIG_BT_CTLR_RX_BUFFERS) |
| |
| /* Part sum of LLL to ULL and ULL to LL/HCI thread node rx count. |
| * Will be used below in allocating node rx pool. |
| */ |
| #define RX_CNT (PDU_RX_CNT + LL_PDU_RX_CNT) |
| |
| static MFIFO_DEFINE(pdu_rx_free, sizeof(void *), PDU_RX_CNT); |
| |
| #if defined(CONFIG_BT_RX_USER_PDU_LEN) |
| #define PDU_RX_USER_PDU_OCTETS_MAX (CONFIG_BT_RX_USER_PDU_LEN) |
| #else |
| #define PDU_RX_USER_PDU_OCTETS_MAX 0 |
| #endif |
| |
| #define PDU_ADV_SIZE MAX(PDU_AC_LL_SIZE_MAX, \ |
| (PDU_AC_LL_HEADER_SIZE + LL_EXT_OCTETS_RX_MAX)) |
| |
| #define PDU_DATA_SIZE MAX((PDU_DC_LL_HEADER_SIZE + LL_LENGTH_OCTETS_RX_MAX), \ |
| (PDU_BIS_LL_HEADER_SIZE + LL_BIS_OCTETS_RX_MAX)) |
| |
| #define NODE_RX_HEADER_SIZE (offsetof(struct node_rx_pdu, pdu)) |
| |
| #define PDU_RX_NODE_POOL_ELEMENT_SIZE MROUND(NODE_RX_HEADER_SIZE + \ |
| MAX(MAX(PDU_ADV_SIZE, \ |
| PDU_DATA_SIZE), \ |
| PDU_RX_USER_PDU_OCTETS_MAX)) |
| |
| #if defined(CONFIG_BT_CTLR_ADV_ISO_SET) |
| #define BT_CTLR_ADV_ISO_SET CONFIG_BT_CTLR_ADV_ISO_SET |
| #else |
| #define BT_CTLR_ADV_ISO_SET 0 |
| #endif |
| |
| #if defined(CONFIG_BT_PER_ADV_SYNC_MAX) |
| #define BT_CTLR_SCAN_SYNC_SET CONFIG_BT_PER_ADV_SYNC_MAX |
| #else |
| #define BT_CTLR_SCAN_SYNC_SET 0 |
| #endif |
| |
| #if defined(CONFIG_BT_CTLR_SCAN_SYNC_ISO_SET) |
| #define BT_CTLR_SCAN_SYNC_ISO_SET CONFIG_BT_CTLR_SCAN_SYNC_ISO_SET |
| #else |
| #define BT_CTLR_SCAN_SYNC_ISO_SET 0 |
| #endif |
| |
| #define PDU_RX_POOL_SIZE (PDU_RX_NODE_POOL_ELEMENT_SIZE * \ |
| (RX_CNT + BT_CTLR_MAX_CONNECTABLE + \ |
| BT_CTLR_ADV_SET + BT_CTLR_SCAN_SYNC_SET)) |
| |
| static struct { |
| void *free; |
| uint8_t pool[PDU_RX_POOL_SIZE]; |
| } mem_pdu_rx; |
| |
| /* NOTE: Two memq_link structures are reserved in the case of periodic sync, |
| * one each for sync established and sync lost respectively. Where as in |
| * comparison to a connection, the connection established uses incoming Rx-ed |
| * CONNECT_IND PDU to piggy back generation of connection complete, and hence |
| * only one is reserved for the generation of disconnection event (which can |
| * happen due to supervision timeout and other reasons that dont have an |
| * incoming Rx-ed PDU). |
| */ |
| #define LINK_RX_POOL_SIZE \ |
| (sizeof(memq_link_t) * \ |
| (RX_CNT + 2 + BT_CTLR_MAX_CONN + BT_CTLR_ADV_SET + \ |
| (BT_CTLR_ADV_ISO_SET * 2) + (BT_CTLR_SCAN_SYNC_SET * 2) + \ |
| (BT_CTLR_SCAN_SYNC_ISO_SET * 2) + \ |
| (IQ_REPORT_CNT))) |
| static struct { |
| uint16_t quota_pdu; /* Number of un-utilized buffers */ |
| |
| void *free; |
| uint8_t pool[LINK_RX_POOL_SIZE]; |
| } mem_link_rx; |
| |
| static MEMQ_DECLARE(ull_rx); |
| static MEMQ_DECLARE(ll_rx); |
| #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| static MEMQ_DECLARE(ull_done); |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| #if defined(CONFIG_BT_CONN) |
| static MFIFO_DEFINE(ll_pdu_rx_free, sizeof(void *), LL_PDU_RX_CNT); |
| |
| static void *mark_update; |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_ADV_ISO) |
| #if defined(CONFIG_BT_CONN) |
| #define BT_BUF_ACL_TX_COUNT CONFIG_BT_BUF_ACL_TX_COUNT |
| #else |
| #define BT_BUF_ACL_TX_COUNT 0 |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO) |
| #define BT_CTLR_ISO_TX_BUFFERS CONFIG_BT_CTLR_ISO_TX_BUFFERS |
| #else |
| #define BT_CTLR_ISO_TX_BUFFERS 0 |
| #endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */ |
| |
| static MFIFO_DEFINE(tx_ack, sizeof(struct lll_tx), |
| BT_BUF_ACL_TX_COUNT + BT_CTLR_ISO_TX_BUFFERS); |
| #endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_ADV_ISO */ |
| |
| static void *mark_disable; |
| |
| static inline int init_reset(void); |
| static void perform_lll_reset(void *param); |
| static inline void *mark_set(void **m, void *param); |
| static inline void *mark_unset(void **m, void *param); |
| static inline void *mark_get(void *m); |
| static void rx_replenish_all(void); |
| #if defined(CONFIG_BT_CONN) || \ |
| (defined(CONFIG_BT_OBSERVER) && defined(CONFIG_BT_CTLR_ADV_EXT)) || \ |
| defined(CONFIG_BT_CTLR_ADV_PERIODIC) || \ |
| defined(CONFIG_BT_CTLR_ADV_ISO) |
| static void rx_release_replenish(struct node_rx_hdr *rx); |
| static void rx_link_dequeue_release_quota_inc(memq_link_t *link); |
| #endif /* CONFIG_BT_CONN || |
| * (CONFIG_BT_OBSERVER && CONFIG_BT_CTLR_ADV_EXT) || |
| * CONFIG_BT_CTLR_ADV_PERIODIC || |
| * CONFIG_BT_CTLR_ADV_ISO |
| */ |
| static void rx_demux(void *param); |
| #if defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| static void rx_demux_yield(void); |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| #if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_ADV_ISO) |
| static uint8_t tx_cmplt_get(uint16_t *handle, uint8_t *first, uint8_t last); |
| static inline void rx_demux_conn_tx_ack(uint8_t ack_last, uint16_t handle, |
| memq_link_t *link, |
| struct node_tx *node_tx); |
| #endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_ADV_ISO */ |
| static inline int rx_demux_rx(memq_link_t *link, struct node_rx_hdr *rx); |
| static inline void rx_demux_event_done(memq_link_t *link, |
| struct node_rx_hdr *rx); |
| static void ll_rx_link_quota_inc(void); |
| static void ll_rx_link_quota_dec(void); |
| static void disabled_cb(void *param); |
| #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| static void ull_done(void *param); |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| int ll_init(struct k_sem *sem_rx) |
| { |
| int err; |
| |
| /* Store the semaphore to be used to wakeup Thread context */ |
| sem_recv = sem_rx; |
| |
| /* Initialize counter */ |
| /* TODO: Bind and use counter driver? */ |
| cntr_init(); |
| |
| /* Initialize Mayfly */ |
| mayfly_init(); |
| |
| /* Initialize Ticker */ |
| ticker_users[MAYFLY_CALL_ID_0][0] = TICKER_USER_LLL_OPS; |
| ticker_users[MAYFLY_CALL_ID_1][0] = TICKER_USER_ULL_HIGH_OPS; |
| ticker_users[MAYFLY_CALL_ID_2][0] = TICKER_USER_ULL_LOW_OPS; |
| ticker_users[MAYFLY_CALL_ID_PROGRAM][0] = TICKER_USER_THREAD_OPS; |
| |
| err = ticker_init(TICKER_INSTANCE_ID_CTLR, |
| TICKER_NODES, &ticker_nodes[0], |
| MAYFLY_CALLER_COUNT, &ticker_users[0], |
| TICKER_USER_OPS, &ticker_user_ops[0], |
| hal_ticker_instance0_caller_id_get, |
| hal_ticker_instance0_sched, |
| hal_ticker_instance0_trigger_set); |
| LL_ASSERT(!err); |
| |
| /* Initialize semaphore for ticker API blocking wait */ |
| k_sem_init(&sem_ticker_api_cb, 0, 1); |
| |
| /* Initialize LLL */ |
| err = lll_init(); |
| if (err) { |
| return err; |
| } |
| |
| /* Initialize ULL internals */ |
| /* TODO: globals? */ |
| |
| /* Common to init and reset */ |
| err = init_reset(); |
| if (err) { |
| return err; |
| } |
| |
| #if defined(CONFIG_BT_BROADCASTER) |
| err = lll_adv_init(); |
| if (err) { |
| return err; |
| } |
| |
| err = ull_adv_init(); |
| if (err) { |
| return err; |
| } |
| #endif /* CONFIG_BT_BROADCASTER */ |
| |
| #if defined(CONFIG_BT_OBSERVER) |
| err = lll_scan_init(); |
| if (err) { |
| return err; |
| } |
| |
| err = ull_scan_init(); |
| if (err) { |
| return err; |
| } |
| #endif /* CONFIG_BT_OBSERVER */ |
| |
| #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| err = lll_sync_init(); |
| if (err) { |
| return err; |
| } |
| |
| err = ull_sync_init(); |
| if (err) { |
| return err; |
| } |
| |
| #if defined(CONFIG_BT_CTLR_SYNC_ISO) |
| err = ull_sync_iso_init(); |
| if (err) { |
| return err; |
| } |
| #endif /* CONFIG_BT_CTLR_SYNC_ISO */ |
| #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| |
| #if defined(CONFIG_BT_CONN) |
| err = lll_conn_init(); |
| if (err) { |
| return err; |
| } |
| |
| err = ull_conn_init(); |
| if (err) { |
| return err; |
| } |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_CTLR_DF) |
| err = ull_df_init(); |
| if (err) { |
| return err; |
| } |
| #endif |
| |
| #if defined(CONFIG_BT_CTLR_ISO) |
| err = ull_iso_init(); |
| if (err) { |
| return err; |
| } |
| #endif /* CONFIG_BT_CTLR_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_CONN_ISO) |
| err = ull_conn_iso_init(); |
| if (err) { |
| return err; |
| } |
| #endif /* CONFIG_BT_CTLR_CONN_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) |
| err = ull_peripheral_iso_init(); |
| if (err) { |
| return err; |
| } |
| #endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) |
| err = ull_central_iso_init(); |
| if (err) { |
| return err; |
| } |
| #endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) |
| err = ull_adv_iso_init(); |
| if (err) { |
| return err; |
| } |
| #endif /* CONFIG_BT_CTLR_ADV_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_DF) |
| err = lll_df_init(); |
| if (err) { |
| return err; |
| } |
| #endif |
| |
| #if defined(CONFIG_BT_CTLR_USER_EXT) |
| err = ull_user_init(); |
| if (err) { |
| return err; |
| } |
| #endif /* CONFIG_BT_CTLR_USER_EXT */ |
| |
| /* reset filter accept list, resolving list and initialise RPA timeout*/ |
| if (IS_ENABLED(CONFIG_BT_CTLR_FILTER_ACCEPT_LIST)) { |
| ull_filter_reset(true); |
| } |
| |
| #if defined(CONFIG_BT_CTLR_TEST) |
| lll_chan_sel_2_ut(); |
| #endif /* CONFIG_BT_CTLR_TEST */ |
| |
| return 0; |
| } |
| |
| void ll_reset(void) |
| { |
| int err; |
| |
| /* Note: The sequence of reset control flow is as follows: |
| * - Reset ULL context, i.e. stop ULL scheduling, abort LLL events etc. |
| * - Reset LLL context, i.e. post LLL event abort, let LLL cleanup its |
| * variables, if any. |
| * - Reset ULL static variables (which otherwise was mem-zeroed in cases |
| * if power-on reset wherein architecture startup mem-zeroes .bss |
| * sections. |
| * - Initialize ULL context variable, similar to on-power-up. |
| */ |
| |
| #if defined(CONFIG_BT_BROADCASTER) |
| /* Reset adv state */ |
| err = ull_adv_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_BROADCASTER */ |
| |
| #if defined(CONFIG_BT_OBSERVER) |
| #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| #if defined(CONFIG_BT_CTLR_SYNC_ISO) |
| /* Reset periodic sync sets */ |
| err = ull_sync_iso_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_CTLR_SYNC_ISO */ |
| |
| /* Reset periodic sync sets */ |
| err = ull_sync_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| |
| /* Reset scan state */ |
| err = ull_scan_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_OBSERVER */ |
| |
| #if defined(CONFIG_BT_CTLR_ISO) |
| err = ull_iso_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_CTLR_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_CONN_ISO) |
| err = ull_conn_iso_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_CTLR_CONN_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) |
| err = ull_peripheral_iso_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) |
| err = ull_central_iso_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) |
| /* Reset periodic sync sets */ |
| err = ull_adv_iso_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_CTLR_ADV_ISO */ |
| |
| #if defined(CONFIG_BT_CONN) |
| /* Reset conn role */ |
| err = ull_conn_reset(); |
| LL_ASSERT(!err); |
| |
| MFIFO_INIT(tx_ack); |
| #endif /* CONFIG_BT_CONN */ |
| |
| /* reset filter accept list and resolving list */ |
| if (IS_ENABLED(CONFIG_BT_CTLR_FILTER_ACCEPT_LIST)) { |
| ull_filter_reset(false); |
| } |
| |
| /* Re-initialize ULL internals */ |
| |
| /* Re-initialize the prep mfifo */ |
| MFIFO_INIT(prep); |
| |
| /* Re-initialize the free rx mfifo */ |
| MFIFO_INIT(pdu_rx_free); |
| |
| #if defined(CONFIG_BT_CONN) |
| /* Re-initialize the free ll rx mfifo */ |
| MFIFO_INIT(ll_pdu_rx_free); |
| #endif /* CONFIG_BT_CONN */ |
| |
| /* Reset LLL via mayfly */ |
| { |
| static memq_link_t link; |
| static struct mayfly mfy = {0, 0, &link, NULL, |
| perform_lll_reset}; |
| uint32_t retval; |
| |
| /* NOTE: If Zero Latency Interrupt is used, then LLL context |
| * will be the highest priority IRQ in the system, hence |
| * mayfly_enqueue will be done running the callee inline |
| * (vector to the callee function) in this function. Else |
| * we use semaphore to wait for perform_lll_reset to |
| * complete. |
| */ |
| |
| #if !defined(CONFIG_BT_CTLR_ZLI) |
| struct k_sem sem; |
| |
| k_sem_init(&sem, 0, 1); |
| mfy.param = &sem; |
| #endif /* !CONFIG_BT_CTLR_ZLI */ |
| |
| retval = mayfly_enqueue(TICKER_USER_ID_THREAD, |
| TICKER_USER_ID_LLL, 0, &mfy); |
| LL_ASSERT(!retval); |
| |
| #if !defined(CONFIG_BT_CTLR_ZLI) |
| /* LLL reset must complete before returning - wait for |
| * reset completion in LLL mayfly thread |
| */ |
| k_sem_take(&sem, K_FOREVER); |
| #endif /* !CONFIG_BT_CTLR_ZLI */ |
| } |
| |
| #if defined(CONFIG_BT_BROADCASTER) |
| /* Finalize after adv state LLL context reset */ |
| err = ull_adv_reset_finalize(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_BROADCASTER */ |
| |
| /* Reset/End DTM Tx or Rx commands */ |
| if (IS_ENABLED(CONFIG_BT_CTLR_DTM)) { |
| uint16_t num_rx; |
| |
| (void)ll_test_end(&num_rx); |
| ARG_UNUSED(num_rx); |
| } |
| |
| /* Common to init and reset */ |
| err = init_reset(); |
| LL_ASSERT(!err); |
| |
| #if defined(CONFIG_BT_CTLR_DF) |
| /* Direction Finding has to be reset after ull init_reset call because |
| * it uses mem_link_rx for node_rx_iq_report. The mem_linx_rx is reset |
| * in common ull init_reset. |
| */ |
| err = ull_df_reset(); |
| LL_ASSERT(!err); |
| #endif |
| |
| /* clear static random address */ |
| (void)ll_addr_set(1U, NULL); |
| } |
| |
| /** |
| * @brief Peek the next node_rx to send up to Host |
| * @details Tightly coupled with prio_recv_thread() |
| * Execution context: Controller thread |
| * |
| * @param node_rx[out] Pointer to rx node at head of queue |
| * @param handle[out] Connection handle |
| * @return TX completed |
| */ |
| uint8_t ll_rx_get(void **node_rx, uint16_t *handle) |
| { |
| struct node_rx_hdr *rx; |
| memq_link_t *link; |
| uint8_t cmplt = 0U; |
| |
| #if defined(CONFIG_BT_CONN) || \ |
| (defined(CONFIG_BT_OBSERVER) && defined(CONFIG_BT_CTLR_ADV_EXT)) || \ |
| defined(CONFIG_BT_CTLR_ADV_PERIODIC) || \ |
| defined(CONFIG_BT_CTLR_ADV_ISO) |
| ll_rx_get_again: |
| #endif /* CONFIG_BT_CONN || |
| * (CONFIG_BT_OBSERVER && CONFIG_BT_CTLR_ADV_EXT) || |
| * CONFIG_BT_CTLR_ADV_PERIODIC || |
| * CONFIG_BT_CTLR_ADV_ISO |
| */ |
| |
| *node_rx = NULL; |
| |
| link = memq_peek(memq_ll_rx.head, memq_ll_rx.tail, (void **)&rx); |
| if (link) { |
| #if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_ADV_ISO) |
| cmplt = tx_cmplt_get(handle, &mfifo_tx_ack.f, rx->ack_last); |
| if (!cmplt) { |
| uint8_t f, cmplt_prev, cmplt_curr; |
| uint16_t h; |
| |
| cmplt_curr = 0U; |
| f = mfifo_tx_ack.f; |
| do { |
| cmplt_prev = cmplt_curr; |
| cmplt_curr = tx_cmplt_get(&h, &f, |
| mfifo_tx_ack.l); |
| } while ((cmplt_prev != 0U) || |
| (cmplt_prev != cmplt_curr)); |
| #endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_ADV_ISO */ |
| |
| if (0) { |
| #if defined(CONFIG_BT_CONN) || \ |
| (defined(CONFIG_BT_OBSERVER) && defined(CONFIG_BT_CTLR_ADV_EXT)) |
| /* Do not send up buffers to Host thread that are |
| * marked for release |
| */ |
| } else if (rx->type == NODE_RX_TYPE_RELEASE) { |
| rx_link_dequeue_release_quota_inc(link); |
| rx_release_replenish(rx); |
| |
| goto ll_rx_get_again; |
| #endif /* CONFIG_BT_CONN || |
| * (CONFIG_BT_OBSERVER && CONFIG_BT_CTLR_ADV_EXT) |
| */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_PERIODIC) |
| } else if (rx->type == NODE_RX_TYPE_SYNC_CHM_COMPLETE) { |
| rx_link_dequeue_release_quota_inc(link); |
| |
| /* Remove Channel Map Update Indication from |
| * ACAD. |
| */ |
| ull_adv_sync_chm_complete(rx); |
| |
| rx_release_replenish(rx); |
| |
| goto ll_rx_get_again; |
| #endif /* CONFIG_BT_CTLR_ADV_PERIODIC */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) |
| } else if (rx->type == NODE_RX_TYPE_BIG_CHM_COMPLETE) { |
| rx_link_dequeue_release_quota_inc(link); |
| |
| /* Update Channel Map in BIGInfo present in |
| * Periodic Advertising PDU. |
| */ |
| ull_adv_iso_chm_complete(rx); |
| |
| rx_release_replenish(rx); |
| |
| goto ll_rx_get_again; |
| #endif /* CONFIG_BT_CTLR_ADV_ISO */ |
| } |
| |
| *node_rx = rx; |
| |
| #if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_ADV_ISO) |
| } |
| } else { |
| cmplt = tx_cmplt_get(handle, &mfifo_tx_ack.f, mfifo_tx_ack.l); |
| #endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_ADV_ISO */ |
| } |
| |
| return cmplt; |
| } |
| |
| /** |
| * @brief Commit the dequeue from memq_ll_rx, where ll_rx_get() did the peek |
| * @details Execution context: Controller thread |
| */ |
| void ll_rx_dequeue(void) |
| { |
| struct node_rx_hdr *rx = NULL; |
| memq_link_t *link; |
| |
| link = memq_dequeue(memq_ll_rx.tail, &memq_ll_rx.head, |
| (void **)&rx); |
| LL_ASSERT(link); |
| |
| ll_rx_link_release(link); |
| |
| /* handle object specific clean up */ |
| switch (rx->type) { |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) |
| #if defined(CONFIG_BT_OBSERVER) |
| case NODE_RX_TYPE_EXT_1M_REPORT: |
| case NODE_RX_TYPE_EXT_2M_REPORT: |
| case NODE_RX_TYPE_EXT_CODED_REPORT: |
| #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| case NODE_RX_TYPE_SYNC_REPORT: |
| #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| { |
| struct node_rx_hdr *rx_curr; |
| struct pdu_adv *adv; |
| |
| adv = (void *)((struct node_rx_pdu *)rx)->pdu; |
| if (adv->type != PDU_ADV_TYPE_EXT_IND) { |
| break; |
| } |
| |
| rx_curr = rx->rx_ftr.extra; |
| while (rx_curr) { |
| memq_link_t *link_free; |
| |
| link_free = rx_curr->link; |
| rx_curr = rx_curr->rx_ftr.extra; |
| |
| ll_rx_link_release(link_free); |
| } |
| } |
| break; |
| |
| case NODE_RX_TYPE_EXT_SCAN_TERMINATE: |
| { |
| ull_scan_term_dequeue(rx->handle); |
| } |
| break; |
| #endif /* CONFIG_BT_OBSERVER */ |
| |
| #if defined(CONFIG_BT_BROADCASTER) |
| case NODE_RX_TYPE_EXT_ADV_TERMINATE: |
| { |
| struct ll_adv_set *adv; |
| struct lll_adv_aux *lll_aux; |
| |
| adv = ull_adv_set_get(rx->handle); |
| LL_ASSERT(adv); |
| |
| lll_aux = adv->lll.aux; |
| if (lll_aux) { |
| struct ll_adv_aux_set *aux; |
| |
| aux = HDR_LLL2ULL(lll_aux); |
| |
| aux->is_started = 0U; |
| } |
| |
| #if defined(CONFIG_BT_PERIPHERAL) |
| struct lll_conn *lll_conn = adv->lll.conn; |
| |
| if (!lll_conn) { |
| adv->is_enabled = 0U; |
| |
| break; |
| } |
| |
| LL_ASSERT(!lll_conn->link_tx_free); |
| |
| memq_link_t *link = memq_deinit(&lll_conn->memq_tx.head, |
| &lll_conn->memq_tx.tail); |
| LL_ASSERT(link); |
| |
| lll_conn->link_tx_free = link; |
| |
| struct ll_conn *conn = HDR_LLL2ULL(lll_conn); |
| |
| ll_conn_release(conn); |
| adv->lll.conn = NULL; |
| |
| ll_rx_release(adv->node_rx_cc_free); |
| adv->node_rx_cc_free = NULL; |
| |
| ll_rx_link_release(adv->link_cc_free); |
| adv->link_cc_free = NULL; |
| #endif /* CONFIG_BT_PERIPHERAL */ |
| |
| adv->is_enabled = 0U; |
| } |
| break; |
| #endif /* CONFIG_BT_BROADCASTER */ |
| #endif /* CONFIG_BT_CTLR_ADV_EXT */ |
| |
| #if defined(CONFIG_BT_CONN) |
| case NODE_RX_TYPE_CONNECTION: |
| { |
| struct node_rx_cc *cc = (void *)((struct node_rx_pdu *)rx)->pdu; |
| struct node_rx_ftr *ftr = &(rx->rx_ftr); |
| |
| if (0) { |
| |
| #if defined(CONFIG_BT_PERIPHERAL) |
| } else if ((cc->status == BT_HCI_ERR_ADV_TIMEOUT) || cc->role) { |
| struct ll_adv_set *adv; |
| struct lll_adv *lll; |
| |
| /* Get reference to ULL context */ |
| lll = ftr->param; |
| adv = HDR_LLL2ULL(lll); |
| |
| if (cc->status == BT_HCI_ERR_ADV_TIMEOUT) { |
| struct lll_conn *conn_lll; |
| struct ll_conn *conn; |
| memq_link_t *link; |
| |
| conn_lll = lll->conn; |
| LL_ASSERT(conn_lll); |
| lll->conn = NULL; |
| |
| LL_ASSERT(!conn_lll->link_tx_free); |
| link = memq_deinit(&conn_lll->memq_tx.head, |
| &conn_lll->memq_tx.tail); |
| LL_ASSERT(link); |
| conn_lll->link_tx_free = link; |
| |
| conn = HDR_LLL2ULL(conn_lll); |
| ll_conn_release(conn); |
| } else { |
| /* Release un-utilized node rx */ |
| if (adv->node_rx_cc_free) { |
| void *rx_free; |
| |
| rx_free = adv->node_rx_cc_free; |
| adv->node_rx_cc_free = NULL; |
| |
| ll_rx_release(rx_free); |
| } |
| } |
| |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) |
| if (lll->aux) { |
| struct ll_adv_aux_set *aux; |
| |
| aux = HDR_LLL2ULL(lll->aux); |
| aux->is_started = 0U; |
| } |
| #endif /* CONFIG_BT_CTLR_ADV_EXT */ |
| |
| adv->is_enabled = 0U; |
| #else /* !CONFIG_BT_PERIPHERAL */ |
| ARG_UNUSED(cc); |
| #endif /* !CONFIG_BT_PERIPHERAL */ |
| |
| #if defined(CONFIG_BT_CENTRAL) |
| } else { |
| struct ll_scan_set *scan = HDR_LLL2ULL(ftr->param); |
| |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_CTLR_PHY_CODED) |
| struct ll_scan_set *scan_other = |
| ull_scan_is_enabled_get(SCAN_HANDLE_PHY_CODED); |
| |
| if (scan_other) { |
| if (scan_other == scan) { |
| scan_other = ull_scan_is_enabled_get(SCAN_HANDLE_1M); |
| } |
| |
| if (scan_other) { |
| scan_other->lll.conn = NULL; |
| scan_other->is_enabled = 0U; |
| } |
| } |
| #endif /* CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_CTLR_PHY_CODED */ |
| |
| scan->lll.conn = NULL; |
| scan->is_enabled = 0U; |
| #else /* !CONFIG_BT_CENTRAL */ |
| } else { |
| LL_ASSERT(0); |
| #endif /* !CONFIG_BT_CENTRAL */ |
| } |
| |
| if (IS_ENABLED(CONFIG_BT_CTLR_PRIVACY)) { |
| uint8_t bm; |
| |
| /* FIXME: use the correct adv and scan set to get |
| * enabled status bitmask |
| */ |
| bm = (IS_ENABLED(CONFIG_BT_OBSERVER) && |
| (ull_scan_is_enabled(0) << 1)) | |
| (IS_ENABLED(CONFIG_BT_BROADCASTER) && |
| ull_adv_is_enabled(0)); |
| |
| if (!bm) { |
| ull_filter_adv_scan_state_cb(0); |
| } |
| } |
| } |
| break; |
| |
| case NODE_RX_TYPE_TERMINATE: |
| case NODE_RX_TYPE_DC_PDU: |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) |
| case NODE_RX_TYPE_BIG_COMPLETE: |
| case NODE_RX_TYPE_BIG_TERMINATE: |
| #endif /* CONFIG_BT_CTLR_ADV_ISO */ |
| |
| #if defined(CONFIG_BT_OBSERVER) |
| case NODE_RX_TYPE_REPORT: |
| |
| #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| /* fall through */ |
| case NODE_RX_TYPE_SYNC: |
| case NODE_RX_TYPE_SYNC_LOST: |
| #if defined(CONFIG_BT_CTLR_SYNC_ISO) |
| /* fall through */ |
| case NODE_RX_TYPE_SYNC_ISO: |
| case NODE_RX_TYPE_SYNC_ISO_LOST: |
| #endif /* CONFIG_BT_CTLR_SYNC_ISO */ |
| #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| #endif /* CONFIG_BT_OBSERVER */ |
| |
| #if defined(CONFIG_BT_CTLR_SCAN_REQ_NOTIFY) |
| case NODE_RX_TYPE_SCAN_REQ: |
| #endif /* CONFIG_BT_CTLR_SCAN_REQ_NOTIFY */ |
| |
| #if defined(CONFIG_BT_CONN) |
| case NODE_RX_TYPE_CONN_UPDATE: |
| case NODE_RX_TYPE_ENC_REFRESH: |
| |
| #if defined(CONFIG_BT_CTLR_LE_PING) |
| case NODE_RX_TYPE_APTO: |
| #endif /* CONFIG_BT_CTLR_LE_PING */ |
| |
| case NODE_RX_TYPE_CHAN_SEL_ALGO: |
| |
| #if defined(CONFIG_BT_CTLR_PHY) |
| case NODE_RX_TYPE_PHY_UPDATE: |
| #endif /* CONFIG_BT_CTLR_PHY */ |
| |
| #if defined(CONFIG_BT_CTLR_CONN_RSSI_EVENT) |
| case NODE_RX_TYPE_RSSI: |
| #endif /* CONFIG_BT_CTLR_CONN_RSSI_EVENT */ |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_CTLR_PROFILE_ISR) |
| case NODE_RX_TYPE_PROFILE: |
| #endif /* CONFIG_BT_CTLR_PROFILE_ISR */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_INDICATION) |
| case NODE_RX_TYPE_ADV_INDICATION: |
| #endif /* CONFIG_BT_CTLR_ADV_INDICATION */ |
| |
| #if defined(CONFIG_BT_CTLR_SCAN_INDICATION) |
| case NODE_RX_TYPE_SCAN_INDICATION: |
| #endif /* CONFIG_BT_CTLR_SCAN_INDICATION */ |
| |
| #if defined(CONFIG_BT_HCI_MESH_EXT) |
| case NODE_RX_TYPE_MESH_ADV_CPLT: |
| case NODE_RX_TYPE_MESH_REPORT: |
| #endif /* CONFIG_BT_HCI_MESH_EXT */ |
| |
| #if CONFIG_BT_CTLR_USER_EVT_RANGE > 0 |
| case NODE_RX_TYPE_USER_START ... NODE_RX_TYPE_USER_END - 1: |
| __fallthrough; |
| #endif /* CONFIG_BT_CTLR_USER_EVT_RANGE > 0 */ |
| |
| #if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) |
| case NODE_RX_TYPE_CIS_REQUEST: |
| #endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_CONN_ISO) |
| case NODE_RX_TYPE_CIS_ESTABLISHED: |
| #endif /* CONFIG_BT_CTLR_CONN_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) |
| case NODE_RX_TYPE_SYNC_IQ_SAMPLE_REPORT: |
| #endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */ |
| |
| #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX) |
| case NODE_RX_TYPE_CONN_IQ_SAMPLE_REPORT: |
| #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RX */ |
| |
| #if defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT) |
| case NODE_RX_TYPE_DTM_IQ_SAMPLE_REPORT: |
| #endif /* CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT */ |
| |
| /* Ensure that at least one 'case' statement is present for this |
| * code block. |
| */ |
| case NODE_RX_TYPE_NONE: |
| LL_ASSERT(rx->type != NODE_RX_TYPE_NONE); |
| break; |
| |
| default: |
| LL_ASSERT(0); |
| break; |
| } |
| |
| /* FIXME: clean up when porting Mesh Ext. */ |
| if (0) { |
| #if defined(CONFIG_BT_HCI_MESH_EXT) |
| } else if (rx->type == NODE_RX_TYPE_MESH_ADV_CPLT) { |
| struct ll_adv_set *adv; |
| struct ll_scan_set *scan; |
| |
| adv = ull_adv_is_enabled_get(0); |
| LL_ASSERT(adv); |
| adv->is_enabled = 0U; |
| |
| scan = ull_scan_is_enabled_get(0); |
| LL_ASSERT(scan); |
| |
| scan->is_enabled = 0U; |
| |
| ll_adv_scan_state_cb(0); |
| #endif /* CONFIG_BT_HCI_MESH_EXT */ |
| } |
| } |
| |
| void ll_rx_mem_release(void **node_rx) |
| { |
| struct node_rx_hdr *rx; |
| |
| rx = *node_rx; |
| while (rx) { |
| struct node_rx_hdr *rx_free; |
| |
| rx_free = rx; |
| rx = rx->next; |
| |
| switch (rx_free->type) { |
| #if defined(CONFIG_BT_BROADCASTER) |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) |
| case NODE_RX_TYPE_EXT_ADV_TERMINATE: |
| ll_rx_release(rx_free); |
| break; |
| |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) |
| case NODE_RX_TYPE_BIG_COMPLETE: |
| /* Nothing to release */ |
| break; |
| |
| case NODE_RX_TYPE_BIG_TERMINATE: |
| { |
| struct ll_adv_iso_set *adv_iso = rx_free->rx_ftr.param; |
| |
| ull_adv_iso_stream_release(adv_iso); |
| } |
| break; |
| #endif /* CONFIG_BT_CTLR_ADV_ISO */ |
| #endif /* CONFIG_BT_CTLR_ADV_EXT */ |
| #endif /* CONFIG_BT_BROADCASTER */ |
| |
| #if defined(CONFIG_BT_OBSERVER) |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) |
| case NODE_RX_TYPE_EXT_SCAN_TERMINATE: |
| { |
| ll_rx_release(rx_free); |
| } |
| break; |
| #endif /* CONFIG_BT_CTLR_ADV_EXT */ |
| #endif /* CONFIG_BT_OBSERVER */ |
| |
| #if defined(CONFIG_BT_CONN) |
| case NODE_RX_TYPE_CONNECTION: |
| { |
| struct node_rx_cc *cc = |
| (void *)((struct node_rx_pdu *)rx_free)->pdu; |
| |
| if (0) { |
| |
| #if defined(CONFIG_BT_PERIPHERAL) |
| } else if (cc->status == BT_HCI_ERR_ADV_TIMEOUT) { |
| ll_rx_release(rx_free); |
| |
| break; |
| #endif /* !CONFIG_BT_PERIPHERAL */ |
| |
| #if defined(CONFIG_BT_CENTRAL) |
| } else if (cc->status == BT_HCI_ERR_UNKNOWN_CONN_ID) { |
| ull_central_cleanup(rx_free); |
| |
| #if defined(CONFIG_BT_CTLR_PRIVACY) |
| #if defined(CONFIG_BT_BROADCASTER) |
| if (!ull_adv_is_enabled_get(0)) |
| #endif /* CONFIG_BT_BROADCASTER */ |
| { |
| ull_filter_adv_scan_state_cb(0); |
| } |
| #endif /* CONFIG_BT_CTLR_PRIVACY */ |
| break; |
| #endif /* CONFIG_BT_CENTRAL */ |
| |
| } else { |
| LL_ASSERT(!cc->status); |
| } |
| } |
| |
| __fallthrough; |
| case NODE_RX_TYPE_DC_PDU: |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_OBSERVER) |
| case NODE_RX_TYPE_REPORT: |
| |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) |
| __fallthrough; |
| case NODE_RX_TYPE_EXT_1M_REPORT: |
| case NODE_RX_TYPE_EXT_2M_REPORT: |
| case NODE_RX_TYPE_EXT_CODED_REPORT: |
| #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| case NODE_RX_TYPE_SYNC_REPORT: |
| #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| #endif /* CONFIG_BT_CTLR_ADV_EXT */ |
| #endif /* CONFIG_BT_OBSERVER */ |
| |
| #if defined(CONFIG_BT_CTLR_SCAN_REQ_NOTIFY) |
| case NODE_RX_TYPE_SCAN_REQ: |
| #endif /* CONFIG_BT_CTLR_SCAN_REQ_NOTIFY */ |
| |
| #if defined(CONFIG_BT_CONN) |
| case NODE_RX_TYPE_CONN_UPDATE: |
| case NODE_RX_TYPE_ENC_REFRESH: |
| |
| #if defined(CONFIG_BT_CTLR_LE_PING) |
| case NODE_RX_TYPE_APTO: |
| #endif /* CONFIG_BT_CTLR_LE_PING */ |
| |
| case NODE_RX_TYPE_CHAN_SEL_ALGO: |
| |
| #if defined(CONFIG_BT_CTLR_PHY) |
| case NODE_RX_TYPE_PHY_UPDATE: |
| #endif /* CONFIG_BT_CTLR_PHY */ |
| |
| #if defined(CONFIG_BT_CTLR_CONN_RSSI_EVENT) |
| case NODE_RX_TYPE_RSSI: |
| #endif /* CONFIG_BT_CTLR_CONN_RSSI_EVENT */ |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_CTLR_PROFILE_ISR) |
| case NODE_RX_TYPE_PROFILE: |
| #endif /* CONFIG_BT_CTLR_PROFILE_ISR */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_INDICATION) |
| case NODE_RX_TYPE_ADV_INDICATION: |
| #endif /* CONFIG_BT_CTLR_ADV_INDICATION */ |
| |
| #if defined(CONFIG_BT_CTLR_SCAN_INDICATION) |
| case NODE_RX_TYPE_SCAN_INDICATION: |
| #endif /* CONFIG_BT_CTLR_SCAN_INDICATION */ |
| |
| #if defined(CONFIG_BT_HCI_MESH_EXT) |
| case NODE_RX_TYPE_MESH_ADV_CPLT: |
| case NODE_RX_TYPE_MESH_REPORT: |
| #endif /* CONFIG_BT_HCI_MESH_EXT */ |
| |
| #if CONFIG_BT_CTLR_USER_EVT_RANGE > 0 |
| case NODE_RX_TYPE_USER_START ... NODE_RX_TYPE_USER_END - 1: |
| #endif /* CONFIG_BT_CTLR_USER_EVT_RANGE > 0 */ |
| |
| #if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) |
| case NODE_RX_TYPE_CIS_REQUEST: |
| #endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_CONN_ISO) |
| case NODE_RX_TYPE_CIS_ESTABLISHED: |
| #endif /* CONFIG_BT_CTLR_CONN_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_ISO) |
| case NODE_RX_TYPE_ISO_PDU: |
| #endif |
| |
| /* Ensure that at least one 'case' statement is present for this |
| * code block. |
| */ |
| case NODE_RX_TYPE_NONE: |
| LL_ASSERT(rx_free->type != NODE_RX_TYPE_NONE); |
| ll_rx_link_quota_inc(); |
| ll_rx_release(rx_free); |
| break; |
| |
| #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| case NODE_RX_TYPE_SYNC: |
| { |
| struct node_rx_sync *se = |
| (void *)((struct node_rx_pdu *)rx_free)->pdu; |
| uint8_t status = se->status; |
| |
| /* Below status codes use node_rx_sync_estab, hence |
| * release the node_rx memory and release sync context |
| * if sync establishment failed. |
| */ |
| if ((status == BT_HCI_ERR_SUCCESS) || |
| (status == BT_HCI_ERR_UNSUPP_REMOTE_FEATURE) || |
| (status == BT_HCI_ERR_CONN_FAIL_TO_ESTAB)) { |
| struct ll_sync_set *sync; |
| struct ll_scan_set *scan; |
| |
| /* pick the scan context before node_rx |
| * release. |
| */ |
| scan = (void *)rx_free->rx_ftr.param; |
| |
| ll_rx_release(rx_free); |
| |
| /* pick the sync context before scan context |
| * is cleanup of sync context association. |
| */ |
| sync = scan->periodic.sync; |
| |
| ull_sync_setup_reset(scan); |
| |
| if (status != BT_HCI_ERR_SUCCESS) { |
| memq_link_t *link_sync_lost; |
| |
| link_sync_lost = |
| sync->node_rx_lost.hdr.link; |
| ll_rx_link_release(link_sync_lost); |
| |
| ull_sync_release(sync); |
| } |
| |
| break; |
| } else { |
| LL_ASSERT(status == BT_HCI_ERR_OP_CANCELLED_BY_HOST); |
| |
| /* Fall through and release sync context */ |
| } |
| } |
| /* Pass through */ |
| |
| case NODE_RX_TYPE_SYNC_LOST: |
| { |
| struct ll_sync_set *sync = |
| (void *)rx_free->rx_ftr.param; |
| |
| ull_sync_release(sync); |
| } |
| break; |
| |
| #if defined(CONFIG_BT_CTLR_SYNC_ISO) |
| case NODE_RX_TYPE_SYNC_ISO: |
| { |
| struct node_rx_sync_iso *se = |
| (void *)((struct node_rx_pdu *)rx_free)->pdu; |
| |
| if (!se->status) { |
| ll_rx_release(rx_free); |
| |
| break; |
| } |
| } |
| /* Pass through */ |
| |
| case NODE_RX_TYPE_SYNC_ISO_LOST: |
| { |
| struct ll_sync_iso_set *sync_iso = |
| (void *)rx_free->rx_ftr.param; |
| |
| ull_sync_iso_stream_release(sync_iso); |
| } |
| break; |
| #endif /* CONFIG_BT_CTLR_SYNC_ISO */ |
| #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| |
| #if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) || defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX) || \ |
| defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT) |
| case NODE_RX_TYPE_SYNC_IQ_SAMPLE_REPORT: |
| case NODE_RX_TYPE_CONN_IQ_SAMPLE_REPORT: |
| case NODE_RX_TYPE_DTM_IQ_SAMPLE_REPORT: |
| { |
| const uint8_t report_cnt = 1U; |
| |
| ull_iq_report_link_inc_quota(report_cnt); |
| ull_df_iq_report_mem_release(rx_free); |
| ull_df_rx_iq_report_alloc(report_cnt); |
| } |
| break; |
| #endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX || CONFIG_BT_CTLR_DF_CONN_CTE_RX */ |
| |
| #if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_CONN_ISO) |
| case NODE_RX_TYPE_TERMINATE: |
| { |
| if (IS_ACL_HANDLE(rx_free->handle)) { |
| struct ll_conn *conn; |
| memq_link_t *link; |
| |
| conn = ll_conn_get(rx_free->handle); |
| |
| LL_ASSERT(!conn->lll.link_tx_free); |
| link = memq_deinit(&conn->lll.memq_tx.head, |
| &conn->lll.memq_tx.tail); |
| LL_ASSERT(link); |
| conn->lll.link_tx_free = link; |
| |
| ll_conn_release(conn); |
| } |
| } |
| break; |
| #endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_CONN_ISO */ |
| |
| case NODE_RX_TYPE_EVENT_DONE: |
| default: |
| LL_ASSERT(0); |
| break; |
| } |
| } |
| |
| *node_rx = rx; |
| |
| rx_replenish_all(); |
| } |
| |
| static void ll_rx_link_quota_update(int8_t delta) |
| { |
| LL_ASSERT(delta <= 0 || mem_link_rx.quota_pdu < RX_CNT); |
| mem_link_rx.quota_pdu += delta; |
| } |
| |
| static void ll_rx_link_quota_inc(void) |
| { |
| ll_rx_link_quota_update(1); |
| } |
| |
| static void ll_rx_link_quota_dec(void) |
| { |
| ll_rx_link_quota_update(-1); |
| } |
| |
| void *ll_rx_link_alloc(void) |
| { |
| return mem_acquire(&mem_link_rx.free); |
| } |
| |
| void ll_rx_link_release(memq_link_t *link) |
| { |
| mem_release(link, &mem_link_rx.free); |
| } |
| |
| void *ll_rx_alloc(void) |
| { |
| return mem_acquire(&mem_pdu_rx.free); |
| } |
| |
| void ll_rx_release(void *node_rx) |
| { |
| mem_release(node_rx, &mem_pdu_rx.free); |
| } |
| |
| void ll_rx_put(memq_link_t *link, void *rx) |
| { |
| #if defined(CONFIG_BT_CONN) |
| struct node_rx_hdr *rx_hdr = rx; |
| |
| /* Serialize Tx ack with Rx enqueue by storing reference to |
| * last element index in Tx ack FIFO. |
| */ |
| rx_hdr->ack_last = mfifo_tx_ack.l; |
| #endif /* CONFIG_BT_CONN */ |
| |
| /* Enqueue the Rx object */ |
| memq_enqueue(link, rx, &memq_ll_rx.tail); |
| } |
| |
| /** |
| * @brief Permit another loop in the controller thread (prio_recv_thread) |
| * @details Execution context: ULL mayfly |
| */ |
| void ll_rx_sched(void) |
| { |
| /* sem_recv references the same semaphore (sem_prio_recv) |
| * in prio_recv_thread |
| */ |
| k_sem_give(sem_recv); |
| } |
| |
| #if defined(CONFIG_BT_CONN) |
| void *ll_pdu_rx_alloc_peek(uint8_t count) |
| { |
| if (count > MFIFO_AVAIL_COUNT_GET(ll_pdu_rx_free)) { |
| return NULL; |
| } |
| |
| return MFIFO_DEQUEUE_PEEK(ll_pdu_rx_free); |
| } |
| |
| void *ll_pdu_rx_alloc(void) |
| { |
| return MFIFO_DEQUEUE(ll_pdu_rx_free); |
| } |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_ADV_ISO) |
| void ll_tx_ack_put(uint16_t handle, struct node_tx *node_tx) |
| { |
| struct lll_tx *tx; |
| uint8_t idx; |
| |
| idx = MFIFO_ENQUEUE_GET(tx_ack, (void **)&tx); |
| LL_ASSERT(tx); |
| |
| tx->handle = handle; |
| tx->node = node_tx; |
| |
| MFIFO_ENQUEUE(tx_ack, idx); |
| } |
| #endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_ADV_ISO */ |
| |
| void ll_timeslice_ticker_id_get(uint8_t * const instance_index, |
| uint8_t * const ticker_id) |
| { |
| *instance_index = TICKER_INSTANCE_ID_CTLR; |
| *ticker_id = (TICKER_NODES - FLASH_TICKER_NODES); |
| } |
| |
| void ll_radio_state_abort(void) |
| { |
| static memq_link_t link; |
| static struct mayfly mfy = {0, 0, &link, NULL, lll_disable}; |
| uint32_t ret; |
| |
| ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_LLL, 0, |
| &mfy); |
| LL_ASSERT(!ret); |
| } |
| |
| uint32_t ll_radio_state_is_idle(void) |
| { |
| return lll_radio_is_idle(); |
| } |
| |
| void ull_ticker_status_give(uint32_t status, void *param) |
| { |
| *((uint32_t volatile *)param) = status; |
| |
| k_sem_give(&sem_ticker_api_cb); |
| } |
| |
| uint32_t ull_ticker_status_take(uint32_t ret, uint32_t volatile *ret_cb) |
| { |
| if (ret == TICKER_STATUS_BUSY) { |
| /* TODO: Enable ticker job in case of CONFIG_BT_CTLR_LOW_LAT */ |
| } else { |
| /* Check for ticker operation enqueue failed, in which case |
| * function return value (ret) will be TICKER_STATUS_FAILURE |
| * and callback return value (ret_cb) will remain as |
| * TICKER_STATUS_BUSY. |
| * This assert check will avoid waiting forever to take the |
| * semaphore that will never be given when the ticker operation |
| * callback does not get called due to enqueue failure. |
| */ |
| LL_ASSERT((ret == TICKER_STATUS_SUCCESS) || |
| (*ret_cb != TICKER_STATUS_BUSY)); |
| } |
| |
| k_sem_take(&sem_ticker_api_cb, K_FOREVER); |
| |
| return *ret_cb; |
| } |
| |
| void *ull_disable_mark(void *param) |
| { |
| return mark_set(&mark_disable, param); |
| } |
| |
| void *ull_disable_unmark(void *param) |
| { |
| return mark_unset(&mark_disable, param); |
| } |
| |
| void *ull_disable_mark_get(void) |
| { |
| return mark_get(mark_disable); |
| } |
| |
| /** |
| * @brief Stops a specified ticker using the ull_disable_(un)mark functions. |
| * |
| * @param ticker_handle The handle of the ticker. |
| * @param param The object to mark. |
| * @param lll_disable Optional object when calling @ref ull_disable |
| * |
| * @return 0 if success, else ERRNO. |
| */ |
| int ull_ticker_stop_with_mark(uint8_t ticker_handle, void *param, |
| void *lll_disable) |
| { |
| uint32_t volatile ret_cb; |
| uint32_t ret; |
| void *mark; |
| int err; |
| |
| mark = ull_disable_mark(param); |
| if (mark != param) { |
| return -ENOLCK; |
| } |
| |
| ret_cb = TICKER_STATUS_BUSY; |
| ret = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_THREAD, |
| ticker_handle, ull_ticker_status_give, |
| (void *)&ret_cb); |
| ret = ull_ticker_status_take(ret, &ret_cb); |
| if (ret) { |
| mark = ull_disable_unmark(param); |
| if (mark != param) { |
| return -ENOLCK; |
| } |
| |
| return -EALREADY; |
| } |
| |
| err = ull_disable(lll_disable); |
| |
| mark = ull_disable_unmark(param); |
| if (mark != param) { |
| return -ENOLCK; |
| } |
| |
| if (err && (err != -EALREADY)) { |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_BT_CONN) |
| void *ull_update_mark(void *param) |
| { |
| return mark_set(&mark_update, param); |
| } |
| |
| void *ull_update_unmark(void *param) |
| { |
| return mark_unset(&mark_update, param); |
| } |
| |
| void *ull_update_mark_get(void) |
| { |
| return mark_get(mark_update); |
| } |
| #endif /* CONFIG_BT_CONN */ |
| |
| int ull_disable(void *lll) |
| { |
| static memq_link_t link; |
| static struct mayfly mfy = {0, 0, &link, NULL, lll_disable}; |
| struct ull_hdr *hdr; |
| struct k_sem sem; |
| uint32_t ret; |
| |
| hdr = HDR_LLL2ULL(lll); |
| if (!ull_ref_get(hdr)) { |
| return -EALREADY; |
| } |
| |
| k_sem_init(&sem, 0, 1); |
| |
| hdr->disabled_param = &sem; |
| hdr->disabled_cb = disabled_cb; |
| |
| /* ULL_HIGH can run after we have call `ull_ref_get` and it can |
| * decrement the ref count. Hence, handle this race condition by |
| * ensuring that `disabled_cb` has been set while the ref count is still |
| * set. |
| * No need to call `lll_disable` and take the semaphore thereafter if |
| * reference count is zero. |
| * If the `sem` is given when reference count was decremented, we do not |
| * care. |
| */ |
| if (!ull_ref_get(hdr)) { |
| return -EALREADY; |
| } |
| |
| mfy.param = lll; |
| ret = mayfly_enqueue(TICKER_USER_ID_THREAD, TICKER_USER_ID_LLL, 0, |
| &mfy); |
| LL_ASSERT(!ret); |
| |
| return k_sem_take(&sem, K_FOREVER); |
| } |
| |
| void *ull_pdu_rx_alloc_peek(uint8_t count) |
| { |
| if (count > MFIFO_AVAIL_COUNT_GET(pdu_rx_free)) { |
| return NULL; |
| } |
| |
| return MFIFO_DEQUEUE_PEEK(pdu_rx_free); |
| } |
| |
| void *ull_pdu_rx_alloc_peek_iter(uint8_t *idx) |
| { |
| return *(void **)MFIFO_DEQUEUE_ITER_GET(pdu_rx_free, idx); |
| } |
| |
| void *ull_pdu_rx_alloc(void) |
| { |
| return MFIFO_DEQUEUE(pdu_rx_free); |
| } |
| |
| void ull_rx_put(memq_link_t *link, void *rx) |
| { |
| #if defined(CONFIG_BT_CONN) |
| struct node_rx_hdr *rx_hdr = rx; |
| |
| /* Serialize Tx ack with Rx enqueue by storing reference to |
| * last element index in Tx ack FIFO. |
| */ |
| rx_hdr->ack_last = ull_conn_ack_last_idx_get(); |
| #endif /* CONFIG_BT_CONN */ |
| |
| /* Enqueue the Rx object */ |
| memq_enqueue(link, rx, &memq_ull_rx.tail); |
| } |
| |
| void ull_rx_sched(void) |
| { |
| static memq_link_t link; |
| static struct mayfly mfy = {0, 0, &link, NULL, rx_demux}; |
| |
| /* Kick the ULL (using the mayfly, tailchain it) */ |
| mayfly_enqueue(TICKER_USER_ID_LLL, TICKER_USER_ID_ULL_HIGH, 1, &mfy); |
| } |
| |
| #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| void ull_rx_put_done(memq_link_t *link, void *done) |
| { |
| /* Enqueue the done object */ |
| memq_enqueue(link, done, &memq_ull_done.tail); |
| } |
| |
| void ull_rx_sched_done(void) |
| { |
| static memq_link_t link; |
| static struct mayfly mfy = {0, 0, &link, NULL, ull_done}; |
| |
| /* Kick the ULL (using the mayfly, tailchain it) */ |
| mayfly_enqueue(TICKER_USER_ID_LLL, TICKER_USER_ID_ULL_HIGH, 1, &mfy); |
| } |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| struct lll_event *ull_prepare_enqueue(lll_is_abort_cb_t is_abort_cb, |
| lll_abort_cb_t abort_cb, |
| struct lll_prepare_param *prepare_param, |
| lll_prepare_cb_t prepare_cb, |
| uint8_t is_resume) |
| { |
| struct lll_event *e; |
| uint8_t idx; |
| |
| idx = MFIFO_ENQUEUE_GET(prep, (void **)&e); |
| if (!e) { |
| return NULL; |
| } |
| |
| memcpy(&e->prepare_param, prepare_param, sizeof(e->prepare_param)); |
| e->prepare_cb = prepare_cb; |
| e->is_abort_cb = is_abort_cb; |
| e->abort_cb = abort_cb; |
| e->is_resume = is_resume; |
| e->is_aborted = 0U; |
| |
| MFIFO_ENQUEUE(prep, idx); |
| |
| return e; |
| } |
| |
| void *ull_prepare_dequeue_get(void) |
| { |
| return MFIFO_DEQUEUE_GET(prep); |
| } |
| |
| void *ull_prepare_dequeue_iter(uint8_t *idx) |
| { |
| return MFIFO_DEQUEUE_ITER_GET(prep, idx); |
| } |
| |
| void ull_prepare_dequeue(uint8_t caller_id) |
| { |
| void *param_resume_head = NULL; |
| void *param_resume_next = NULL; |
| struct lll_event *next; |
| |
| next = ull_prepare_dequeue_get(); |
| while (next) { |
| void *param = next->prepare_param.param; |
| uint8_t is_aborted = next->is_aborted; |
| uint8_t is_resume = next->is_resume; |
| |
| /* Let LLL invoke the `prepare` interface if radio not in active |
| * use. Otherwise, enqueue at end of the prepare pipeline queue. |
| */ |
| if (!is_aborted) { |
| static memq_link_t link; |
| static struct mayfly mfy = {0, 0, &link, NULL, |
| lll_resume}; |
| uint32_t ret; |
| |
| mfy.param = next; |
| ret = mayfly_enqueue(caller_id, TICKER_USER_ID_LLL, 0, |
| &mfy); |
| LL_ASSERT(!ret); |
| } |
| |
| MFIFO_DEQUEUE(prep); |
| |
| /* Check for anymore more prepare elements in queue */ |
| next = ull_prepare_dequeue_get(); |
| if (!next) { |
| break; |
| } |
| |
| /* A valid prepare element has its `prepare` invoked or was |
| * enqueued back into prepare pipeline. |
| */ |
| if (!is_aborted) { |
| /* The prepare element was not a resume event, it would |
| * use the radio or was enqueued back into prepare |
| * pipeline with a preempt timeout being set. |
| */ |
| if (!is_resume) { |
| break; |
| } |
| |
| /* Remember the first encountered resume and the next |
| * resume element in the prepare pipeline so that we do |
| * not infinitely loop through the resume events in |
| * prepare pipeline. |
| */ |
| if (!param_resume_head) { |
| param_resume_head = param; |
| } else if (!param_resume_next) { |
| param_resume_next = param; |
| } |
| |
| /* Stop traversing the prepare pipeline when we reach |
| * back to the first or next resume event where we |
| * initially started processing the prepare pipeline. |
| */ |
| if (next->is_resume && |
| ((next->prepare_param.param == |
| param_resume_head) || |
| (next->prepare_param.param == |
| param_resume_next))) { |
| break; |
| } |
| } |
| } |
| } |
| |
| struct event_done_extra *ull_event_done_extra_get(void) |
| { |
| struct node_rx_event_done *evdone; |
| |
| evdone = MFIFO_DEQUEUE_PEEK(done); |
| if (!evdone) { |
| return NULL; |
| } |
| |
| return &evdone->extra; |
| } |
| |
| struct event_done_extra *ull_done_extra_type_set(uint8_t type) |
| { |
| struct event_done_extra *extra; |
| |
| extra = ull_event_done_extra_get(); |
| if (!extra) { |
| return NULL; |
| } |
| |
| extra->type = type; |
| |
| return extra; |
| } |
| |
| void *ull_event_done(void *param) |
| { |
| struct node_rx_event_done *evdone; |
| memq_link_t *link; |
| |
| /* Obtain new node that signals "Done of an RX-event". |
| * Obtain this by dequeuing from the global 'mfifo_done' queue. |
| * Note that 'mfifo_done' is a queue of pointers, not of |
| * struct node_rx_event_done |
| */ |
| evdone = MFIFO_DEQUEUE(done); |
| if (!evdone) { |
| /* Not fatal if we can not obtain node, though |
| * we will loose the packets in software stack. |
| * If this happens during Conn Upd, this could cause LSTO |
| */ |
| return NULL; |
| } |
| |
| link = evdone->hdr.link; |
| evdone->hdr.link = NULL; |
| |
| evdone->hdr.type = NODE_RX_TYPE_EVENT_DONE; |
| evdone->param = param; |
| |
| #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| ull_rx_put_done(link, evdone); |
| ull_rx_sched_done(); |
| #else |
| ull_rx_put(link, evdone); |
| ull_rx_sched(); |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| return evdone; |
| } |
| |
| #if defined(CONFIG_BT_PERIPHERAL) || defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| /** |
| * @brief Extract timing from completed event |
| * |
| * @param node_rx_event_done[in] Done event containing fresh timing information |
| * @param ticks_drift_plus[out] Positive part of drift uncertainty window |
| * @param ticks_drift_minus[out] Negative part of drift uncertainty window |
| */ |
| void ull_drift_ticks_get(struct node_rx_event_done *done, |
| uint32_t *ticks_drift_plus, |
| uint32_t *ticks_drift_minus) |
| { |
| uint32_t start_to_address_expected_us; |
| uint32_t start_to_address_actual_us; |
| uint32_t window_widening_event_us; |
| uint32_t preamble_to_addr_us; |
| |
| start_to_address_actual_us = |
| done->extra.drift.start_to_address_actual_us; |
| window_widening_event_us = |
| done->extra.drift.window_widening_event_us; |
| preamble_to_addr_us = |
| done->extra.drift.preamble_to_addr_us; |
| |
| start_to_address_expected_us = EVENT_JITTER_US + |
| EVENT_TICKER_RES_MARGIN_US + |
| window_widening_event_us + |
| preamble_to_addr_us; |
| |
| if (start_to_address_actual_us <= start_to_address_expected_us) { |
| *ticks_drift_plus = |
| HAL_TICKER_US_TO_TICKS(window_widening_event_us); |
| *ticks_drift_minus = |
| HAL_TICKER_US_TO_TICKS((start_to_address_expected_us - |
| start_to_address_actual_us)); |
| } else { |
| *ticks_drift_plus = |
| HAL_TICKER_US_TO_TICKS(start_to_address_actual_us); |
| *ticks_drift_minus = |
| HAL_TICKER_US_TO_TICKS(EVENT_JITTER_US + |
| EVENT_TICKER_RES_MARGIN_US + |
| preamble_to_addr_us); |
| } |
| } |
| #endif /* CONFIG_BT_PERIPHERAL || CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| |
| static inline int init_reset(void) |
| { |
| memq_link_t *link; |
| |
| /* Initialize and allocate done pool */ |
| RXFIFO_INIT_ALLOC(done); |
| |
| /* Initialize rx pool. */ |
| mem_init(mem_pdu_rx.pool, (PDU_RX_NODE_POOL_ELEMENT_SIZE), |
| sizeof(mem_pdu_rx.pool) / (PDU_RX_NODE_POOL_ELEMENT_SIZE), |
| &mem_pdu_rx.free); |
| |
| /* Initialize rx link pool. */ |
| mem_init(mem_link_rx.pool, sizeof(memq_link_t), |
| sizeof(mem_link_rx.pool) / sizeof(memq_link_t), |
| &mem_link_rx.free); |
| |
| /* Acquire a link to initialize ull rx memq */ |
| link = mem_acquire(&mem_link_rx.free); |
| LL_ASSERT(link); |
| |
| /* Initialize ull rx memq */ |
| MEMQ_INIT(ull_rx, link); |
| |
| #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| /* Acquire a link to initialize ull done memq */ |
| link = mem_acquire(&mem_link_done.free); |
| LL_ASSERT(link); |
| |
| /* Initialize ull done memq */ |
| MEMQ_INIT(ull_done, link); |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| /* Acquire a link to initialize ll rx memq */ |
| link = mem_acquire(&mem_link_rx.free); |
| LL_ASSERT(link); |
| |
| /* Initialize ll rx memq */ |
| MEMQ_INIT(ll_rx, link); |
| |
| /* Allocate rx free buffers */ |
| mem_link_rx.quota_pdu = RX_CNT; |
| rx_replenish_all(); |
| |
| return 0; |
| } |
| |
| static void perform_lll_reset(void *param) |
| { |
| int err; |
| |
| /* Reset LLL */ |
| err = lll_reset(); |
| LL_ASSERT(!err); |
| |
| #if defined(CONFIG_BT_BROADCASTER) |
| /* Reset adv state */ |
| err = lll_adv_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_BROADCASTER */ |
| |
| #if defined(CONFIG_BT_OBSERVER) |
| /* Reset scan state */ |
| err = lll_scan_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_OBSERVER */ |
| |
| #if defined(CONFIG_BT_CONN) |
| /* Reset conn role */ |
| err = lll_conn_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_CTLR_DF) |
| err = lll_df_reset(); |
| LL_ASSERT(!err); |
| #endif /* CONFIG_BT_CTLR_DF */ |
| |
| #if !defined(CONFIG_BT_CTLR_ZLI) |
| k_sem_give(param); |
| #endif /* !CONFIG_BT_CTLR_ZLI */ |
| } |
| |
| static inline void *mark_set(void **m, void *param) |
| { |
| if (!*m) { |
| *m = param; |
| } |
| |
| return *m; |
| } |
| |
| static inline void *mark_unset(void **m, void *param) |
| { |
| if (*m && *m == param) { |
| *m = NULL; |
| |
| return param; |
| } |
| |
| return NULL; |
| } |
| |
| static inline void *mark_get(void *m) |
| { |
| return m; |
| } |
| |
| static void rx_replenish(uint8_t max) |
| { |
| uint8_t idx; |
| |
| if (max > mem_link_rx.quota_pdu) { |
| max = mem_link_rx.quota_pdu; |
| } |
| |
| while (max && MFIFO_ENQUEUE_IDX_GET(pdu_rx_free, &idx)) { |
| memq_link_t *link; |
| struct node_rx_hdr *rx; |
| |
| link = mem_acquire(&mem_link_rx.free); |
| if (!link) { |
| return; |
| } |
| |
| rx = mem_acquire(&mem_pdu_rx.free); |
| if (!rx) { |
| ll_rx_link_release(link); |
| return; |
| } |
| |
| rx->link = link; |
| |
| MFIFO_BY_IDX_ENQUEUE(pdu_rx_free, idx, rx); |
| |
| ll_rx_link_quota_dec(); |
| |
| max--; |
| } |
| |
| #if defined(CONFIG_BT_CONN) |
| if (!max) { |
| return; |
| } |
| |
| /* Replenish the ULL to LL/HCI free Rx PDU queue after LLL to ULL free |
| * Rx PDU queue has been filled. |
| */ |
| while (mem_link_rx.quota_pdu && |
| MFIFO_ENQUEUE_IDX_GET(ll_pdu_rx_free, &idx)) { |
| memq_link_t *link; |
| struct node_rx_hdr *rx; |
| |
| link = mem_acquire(&mem_link_rx.free); |
| if (!link) { |
| return; |
| } |
| |
| rx = mem_acquire(&mem_pdu_rx.free); |
| if (!rx) { |
| ll_rx_link_release(link); |
| return; |
| } |
| |
| link->mem = NULL; |
| rx->link = link; |
| |
| MFIFO_BY_IDX_ENQUEUE(ll_pdu_rx_free, idx, rx); |
| |
| ll_rx_link_quota_dec(); |
| } |
| #endif /* CONFIG_BT_CONN */ |
| } |
| |
| static void rx_replenish_all(void) |
| { |
| rx_replenish(UINT8_MAX); |
| } |
| |
| #if defined(CONFIG_BT_CONN) || \ |
| (defined(CONFIG_BT_OBSERVER) && defined(CONFIG_BT_CTLR_ADV_EXT)) || \ |
| defined(CONFIG_BT_CTLR_ADV_PERIODIC) || \ |
| defined(CONFIG_BT_CTLR_ADV_ISO) |
| |
| static void rx_replenish_one(void) |
| { |
| rx_replenish(1U); |
| } |
| |
| static void rx_release_replenish(struct node_rx_hdr *rx) |
| { |
| ll_rx_release(rx); |
| rx_replenish_one(); |
| } |
| |
| static void rx_link_dequeue_release_quota_inc(memq_link_t *link) |
| { |
| (void)memq_dequeue(memq_ll_rx.tail, |
| &memq_ll_rx.head, NULL); |
| ll_rx_link_release(link); |
| ll_rx_link_quota_inc(); |
| } |
| #endif /* CONFIG_BT_CONN || |
| * (CONFIG_BT_OBSERVER && CONFIG_BT_CTLR_ADV_EXT) || |
| * CONFIG_BT_CTLR_ADV_PERIODIC || |
| * CONFIG_BT_CTLR_ADV_ISO |
| */ |
| |
| static void rx_demux(void *param) |
| { |
| memq_link_t *link; |
| |
| #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| do { |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| struct node_rx_hdr *rx; |
| |
| link = memq_peek(memq_ull_rx.head, memq_ull_rx.tail, |
| (void **)&rx); |
| if (link) { |
| #if defined(CONFIG_BT_CONN) |
| struct node_tx *node_tx; |
| memq_link_t *link_tx; |
| uint16_t handle; /* Handle to Ack TX */ |
| #endif /* CONFIG_BT_CONN */ |
| int nack = 0; |
| |
| LL_ASSERT(rx); |
| |
| #if defined(CONFIG_BT_CONN) |
| link_tx = ull_conn_ack_by_last_peek(rx->ack_last, |
| &handle, &node_tx); |
| if (link_tx) { |
| rx_demux_conn_tx_ack(rx->ack_last, handle, |
| link_tx, node_tx); |
| } else |
| #endif /* CONFIG_BT_CONN */ |
| { |
| nack = rx_demux_rx(link, rx); |
| } |
| |
| #if defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| if (!nack) { |
| rx_demux_yield(); |
| } |
| #else /* !CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| if (nack) { |
| break; |
| } |
| #endif /* !CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| #if defined(CONFIG_BT_CONN) |
| } else { |
| struct node_tx *node_tx; |
| uint8_t ack_last; |
| uint16_t handle; |
| |
| link = ull_conn_ack_peek(&ack_last, &handle, &node_tx); |
| if (link) { |
| rx_demux_conn_tx_ack(ack_last, handle, |
| link, node_tx); |
| |
| #if defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| rx_demux_yield(); |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| } |
| #endif /* CONFIG_BT_CONN */ |
| } |
| |
| #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| } while (link); |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| } |
| |
| #if defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| static void rx_demux_yield(void) |
| { |
| static memq_link_t link; |
| static struct mayfly mfy = {0, 0, &link, NULL, rx_demux}; |
| struct node_rx_hdr *rx; |
| memq_link_t *link_peek; |
| |
| link_peek = memq_peek(memq_ull_rx.head, memq_ull_rx.tail, (void **)&rx); |
| if (!link_peek) { |
| #if defined(CONFIG_BT_CONN) |
| struct node_tx *node_tx; |
| uint8_t ack_last; |
| uint16_t handle; |
| |
| link_peek = ull_conn_ack_peek(&ack_last, &handle, &node_tx); |
| if (!link_peek) { |
| return; |
| } |
| #else /* !CONFIG_BT_CONN */ |
| return; |
| #endif /* !CONFIG_BT_CONN */ |
| } |
| |
| /* Kick the ULL (using the mayfly, tailchain it) */ |
| mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_HIGH, 1, |
| &mfy); |
| } |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| #if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_ADV_ISO) |
| static uint8_t tx_cmplt_get(uint16_t *handle, uint8_t *first, uint8_t last) |
| { |
| struct lll_tx *tx; |
| uint8_t cmplt; |
| |
| tx = mfifo_dequeue_iter_get(mfifo_tx_ack.m, mfifo_tx_ack.s, |
| mfifo_tx_ack.n, mfifo_tx_ack.f, last, |
| first); |
| if (!tx) { |
| return 0; |
| } |
| |
| *handle = tx->handle; |
| cmplt = 0U; |
| do { |
| if (false) { |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) || \ |
| defined(CONFIG_BT_CTLR_CONN_ISO) |
| } else if (IS_CIS_HANDLE(tx->handle) || |
| IS_ADV_ISO_HANDLE(tx->handle)) { |
| struct node_tx_iso *tx_node_iso; |
| struct pdu_data *p; |
| uint8_t fragments; |
| |
| tx_node_iso = tx->node; |
| p = (void *)tx_node_iso->pdu; |
| /* TODO: We may need something more advanced for framed */ |
| if (p->ll_id == PDU_CIS_LLID_COMPLETE_END || |
| p->ll_id == PDU_BIS_LLID_COMPLETE_END) { |
| /* We must count each SDU HCI fragment */ |
| fragments = tx_node_iso->sdu_fragments; |
| if (fragments == 0) { |
| /* FIXME: If ISOAL is not used for TX, |
| * sdu_fragments is not incremented. In |
| * that case we assume unfragmented for |
| * now. |
| */ |
| fragments = 1; |
| } |
| cmplt += fragments; |
| } |
| |
| ll_iso_link_tx_release(tx_node_iso->link); |
| ll_iso_tx_mem_release(tx_node_iso); |
| |
| goto next_ack; |
| #endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */ |
| |
| #if defined(CONFIG_BT_CONN) |
| } else { |
| struct node_tx *tx_node; |
| struct pdu_data *p; |
| |
| tx_node = tx->node; |
| p = (void *)tx_node->pdu; |
| if (!tx_node || (tx_node == (void *)1) || |
| (((uint32_t)tx_node & ~3) && |
| (p->ll_id == PDU_DATA_LLID_DATA_START || |
| p->ll_id == PDU_DATA_LLID_DATA_CONTINUE))) { |
| /* data packet, hence count num cmplt */ |
| tx->node = (void *)1; |
| cmplt++; |
| } else { |
| /* ctrl packet or flushed, hence dont count num cmplt */ |
| tx->node = (void *)2; |
| } |
| |
| if (((uint32_t)tx_node & ~3)) { |
| ll_tx_mem_release(tx_node); |
| } |
| #endif /* CONFIG_BT_CONN */ |
| |
| } |
| |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) || \ |
| defined(CONFIG_BT_CTLR_CONN_ISO) |
| next_ack: |
| #endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */ |
| |
| tx = mfifo_dequeue_iter_get(mfifo_tx_ack.m, mfifo_tx_ack.s, |
| mfifo_tx_ack.n, mfifo_tx_ack.f, |
| last, first); |
| } while (tx && tx->handle == *handle); |
| |
| return cmplt; |
| } |
| |
| static inline void rx_demux_conn_tx_ack(uint8_t ack_last, uint16_t handle, |
| memq_link_t *link, |
| struct node_tx *node_tx) |
| { |
| #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| do { |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| /* Dequeue node */ |
| ull_conn_ack_dequeue(); |
| |
| /* Process Tx ack */ |
| ull_conn_tx_ack(handle, link, node_tx); |
| |
| /* Release link mem */ |
| ull_conn_link_tx_release(link); |
| |
| /* check for more rx ack */ |
| link = ull_conn_ack_by_last_peek(ack_last, &handle, &node_tx); |
| |
| #if defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| if (!link) |
| #else /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| } while (link); |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| { |
| /* trigger thread to call ll_rx_get() */ |
| ll_rx_sched(); |
| } |
| } |
| #endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_ADV_ISO */ |
| |
| #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| static void ull_done(void *param) |
| { |
| memq_link_t *link; |
| struct node_rx_hdr *done; |
| |
| do { |
| link = memq_peek(memq_ull_done.head, memq_ull_done.tail, |
| (void **)&done); |
| |
| if (link) { |
| /* Process done event */ |
| (void)memq_dequeue(memq_ull_done.tail, |
| &memq_ull_done.head, NULL); |
| rx_demux_event_done(link, done); |
| } |
| } while (link); |
| } |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| /** |
| * @brief Dispatch rx objects |
| * @details Rx objects are only peeked, not dequeued yet. |
| * Execution context: ULL high priority Mayfly |
| */ |
| static inline int rx_demux_rx(memq_link_t *link, struct node_rx_hdr *rx) |
| { |
| /* Demux Rx objects */ |
| switch (rx->type) { |
| #if defined(CONFIG_BT_CTLR_LOW_LAT_ULL) |
| case NODE_RX_TYPE_EVENT_DONE: |
| { |
| (void)memq_dequeue(memq_ull_rx.tail, &memq_ull_rx.head, NULL); |
| rx_demux_event_done(link, rx); |
| } |
| break; |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */ |
| |
| #if defined(CONFIG_BT_OBSERVER) |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) |
| case NODE_RX_TYPE_EXT_1M_REPORT: |
| case NODE_RX_TYPE_EXT_CODED_REPORT: |
| case NODE_RX_TYPE_EXT_AUX_REPORT: |
| #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| case NODE_RX_TYPE_SYNC_REPORT: |
| #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| { |
| struct pdu_adv *adv; |
| |
| (void)memq_dequeue(memq_ull_rx.tail, &memq_ull_rx.head, NULL); |
| |
| adv = (void *)((struct node_rx_pdu *)rx)->pdu; |
| if (adv->type != PDU_ADV_TYPE_EXT_IND) { |
| ll_rx_put(link, rx); |
| ll_rx_sched(); |
| break; |
| } |
| |
| ull_scan_aux_setup(link, rx); |
| } |
| break; |
| |
| case NODE_RX_TYPE_EXT_AUX_RELEASE: |
| { |
| (void)memq_dequeue(memq_ull_rx.tail, &memq_ull_rx.head, NULL); |
| ull_scan_aux_release(link, rx); |
| } |
| break; |
| #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| case NODE_RX_TYPE_SYNC: |
| { |
| (void)memq_dequeue(memq_ull_rx.tail, &memq_ull_rx.head, NULL); |
| ull_sync_established_report(link, rx); |
| } |
| break; |
| #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| #endif /* CONFIG_BT_CTLR_ADV_EXT */ |
| #endif /* CONFIG_BT_OBSERVER */ |
| |
| #if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) || defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX) || \ |
| defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT) |
| case NODE_RX_TYPE_SYNC_IQ_SAMPLE_REPORT: |
| case NODE_RX_TYPE_CONN_IQ_SAMPLE_REPORT: |
| case NODE_RX_TYPE_DTM_IQ_SAMPLE_REPORT: |
| { |
| (void)memq_dequeue(memq_ull_rx.tail, &memq_ull_rx.head, NULL); |
| ll_rx_put(link, rx); |
| ll_rx_sched(); |
| } |
| break; |
| #endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX || CONFIG_BT_CTLR_DF_CONN_CTE_RX */ |
| |
| #if defined(CONFIG_BT_CONN) |
| case NODE_RX_TYPE_CONNECTION: |
| { |
| (void)memq_dequeue(memq_ull_rx.tail, &memq_ull_rx.head, NULL); |
| ull_conn_setup(link, rx); |
| } |
| break; |
| |
| case NODE_RX_TYPE_DC_PDU: |
| { |
| int nack; |
| |
| nack = ull_conn_rx(link, (void *)&rx); |
| if (nack) { |
| return nack; |
| } |
| |
| (void)memq_dequeue(memq_ull_rx.tail, &memq_ull_rx.head, NULL); |
| |
| if (rx) { |
| ll_rx_put(link, rx); |
| ll_rx_sched(); |
| } |
| } |
| break; |
| |
| case NODE_RX_TYPE_TERMINATE: |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_OBSERVER) || \ |
| defined(CONFIG_BT_CTLR_ADV_PERIODIC) || \ |
| defined(CONFIG_BT_CTLR_BROADCAST_ISO) || \ |
| defined(CONFIG_BT_CTLR_SCAN_REQ_NOTIFY) || \ |
| defined(CONFIG_BT_CTLR_PROFILE_ISR) || \ |
| defined(CONFIG_BT_CTLR_ADV_INDICATION) || \ |
| defined(CONFIG_BT_CTLR_SCAN_INDICATION) || \ |
| defined(CONFIG_BT_CONN) |
| |
| #if defined(CONFIG_BT_CTLR_ADV_PERIODIC) |
| case NODE_RX_TYPE_SYNC_CHM_COMPLETE: |
| #endif /* CONFIG_BT_CTLR_ADV_PERIODIC */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) |
| case NODE_RX_TYPE_BIG_CHM_COMPLETE: |
| case NODE_RX_TYPE_BIG_TERMINATE: |
| #endif /* CONFIG_BT_CTLR_ADV_ISO */ |
| |
| #if defined(CONFIG_BT_OBSERVER) |
| case NODE_RX_TYPE_REPORT: |
| #endif /* CONFIG_BT_OBSERVER */ |
| |
| #if defined(CONFIG_BT_CTLR_SCAN_REQ_NOTIFY) |
| case NODE_RX_TYPE_SCAN_REQ: |
| #endif /* CONFIG_BT_CTLR_SCAN_REQ_NOTIFY */ |
| |
| #if defined(CONFIG_BT_CTLR_PROFILE_ISR) |
| case NODE_RX_TYPE_PROFILE: |
| #endif /* CONFIG_BT_CTLR_PROFILE_ISR */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_INDICATION) |
| case NODE_RX_TYPE_ADV_INDICATION: |
| #endif /* CONFIG_BT_CTLR_ADV_INDICATION */ |
| |
| #if defined(CONFIG_BT_CTLR_SCAN_INDICATION) |
| case NODE_RX_TYPE_SCAN_INDICATION: |
| #endif /* CONFIG_BT_CTLR_SCAN_INDICATION */ |
| |
| case NODE_RX_TYPE_RELEASE: |
| { |
| (void)memq_dequeue(memq_ull_rx.tail, &memq_ull_rx.head, NULL); |
| ll_rx_put(link, rx); |
| ll_rx_sched(); |
| } |
| break; |
| #endif /* CONFIG_BT_OBSERVER || |
| * CONFIG_BT_CTLR_ADV_PERIODIC || |
| * CONFIG_BT_CTLR_BROADCAST_ISO || |
| * CONFIG_BT_CTLR_SCAN_REQ_NOTIFY || |
| * CONFIG_BT_CTLR_PROFILE_ISR || |
| * CONFIG_BT_CTLR_ADV_INDICATION || |
| * CONFIG_BT_CTLR_SCAN_INDICATION || |
| * CONFIG_BT_CONN |
| */ |
| |
| default: |
| { |
| #if defined(CONFIG_BT_CTLR_USER_EXT) |
| /* Try proprietary demuxing */ |
| rx_demux_rx_proprietary(link, rx, memq_ull_rx.tail, |
| &memq_ull_rx.head); |
| #else |
| LL_ASSERT(0); |
| #endif /* CONFIG_BT_CTLR_USER_EXT */ |
| } |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static inline void rx_demux_event_done(memq_link_t *link, |
| struct node_rx_hdr *rx) |
| { |
| struct node_rx_event_done *done = (void *)rx; |
| struct ull_hdr *ull_hdr; |
| void *release; |
| |
| /* Decrement prepare reference if ULL will not resume */ |
| ull_hdr = done->param; |
| if (ull_hdr) { |
| LL_ASSERT(ull_ref_get(ull_hdr)); |
| ull_ref_dec(ull_hdr); |
| } |
| |
| /* Process role dependent event done */ |
| switch (done->extra.type) { |
| #if defined(CONFIG_BT_CONN) |
| case EVENT_DONE_EXTRA_TYPE_CONN: |
| ull_conn_done(done); |
| break; |
| #endif /* CONFIG_BT_CONN */ |
| |
| #if defined(CONFIG_BT_BROADCASTER) |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) || \ |
| defined(CONFIG_BT_CTLR_JIT_SCHEDULING) |
| case EVENT_DONE_EXTRA_TYPE_ADV: |
| ull_adv_done(done); |
| break; |
| |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) |
| case EVENT_DONE_EXTRA_TYPE_ADV_AUX: |
| ull_adv_aux_done(done); |
| break; |
| |
| #if defined(CONFIG_BT_CTLR_ADV_ISO) |
| case EVENT_DONE_EXTRA_TYPE_ADV_ISO_COMPLETE: |
| ull_adv_iso_done_complete(done); |
| break; |
| |
| case EVENT_DONE_EXTRA_TYPE_ADV_ISO_TERMINATE: |
| ull_adv_iso_done_terminate(done); |
| break; |
| #endif /* CONFIG_BT_CTLR_ADV_ISO */ |
| #endif /* CONFIG_BT_CTLR_ADV_EXT */ |
| #endif /* CONFIG_BT_CTLR_ADV_EXT || CONFIG_BT_CTLR_JIT_SCHEDULING */ |
| #endif /* CONFIG_BT_BROADCASTER */ |
| |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) |
| #if defined(CONFIG_BT_OBSERVER) |
| case EVENT_DONE_EXTRA_TYPE_SCAN: |
| ull_scan_done(done); |
| break; |
| |
| case EVENT_DONE_EXTRA_TYPE_SCAN_AUX: |
| ull_scan_aux_done(done); |
| break; |
| |
| #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) |
| case EVENT_DONE_EXTRA_TYPE_SYNC: |
| ull_sync_done(done); |
| break; |
| |
| #if defined(CONFIG_BT_CTLR_SYNC_ISO) |
| case EVENT_DONE_EXTRA_TYPE_SYNC_ISO_ESTAB: |
| ull_sync_iso_estab_done(done); |
| break; |
| |
| case EVENT_DONE_EXTRA_TYPE_SYNC_ISO: |
| ull_sync_iso_done(done); |
| break; |
| |
| case EVENT_DONE_EXTRA_TYPE_SYNC_ISO_TERMINATE: |
| ull_sync_iso_done_terminate(done); |
| break; |
| #endif /* CONFIG_BT_CTLR_SYNC_ISO */ |
| #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ |
| #endif /* CONFIG_BT_OBSERVER */ |
| #endif /* CONFIG_BT_CTLR_ADV_EXT */ |
| |
| #if defined(CONFIG_BT_CTLR_CONN_ISO) |
| case EVENT_DONE_EXTRA_TYPE_CIS: |
| ull_conn_iso_done(done); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_USER_EXT) |
| case EVENT_DONE_EXTRA_TYPE_USER_START |
| ... EVENT_DONE_EXTRA_TYPE_USER_END: |
| ull_proprietary_done(done); |
| break; |
| #endif /* CONFIG_BT_CTLR_USER_EXT */ |
| |
| case EVENT_DONE_EXTRA_TYPE_NONE: |
| /* ignore */ |
| break; |
| |
| default: |
| LL_ASSERT(0); |
| break; |
| } |
| |
| /* Release done */ |
| done->extra.type = 0U; |
| release = RXFIFO_RELEASE(done, link, done); |
| LL_ASSERT(release == done); |
| |
| #if defined(CONFIG_BT_CTLR_LOW_LAT_ULL_DONE) |
| /* dequeue prepare pipeline */ |
| ull_prepare_dequeue(TICKER_USER_ID_ULL_HIGH); |
| |
| /* LLL done synchronize count */ |
| lll_done_ull_inc(); |
| #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL_DONE */ |
| |
| /* If disable initiated, signal the semaphore */ |
| if (ull_hdr && !ull_ref_get(ull_hdr) && ull_hdr->disabled_cb) { |
| ull_hdr->disabled_cb(ull_hdr->disabled_param); |
| } |
| } |
| |
| static void disabled_cb(void *param) |
| { |
| k_sem_give(param); |
| } |
| |
| /** |
| * @brief Support function for RXFIFO_ALLOC macro |
| * @details This function allocates up to 'max' number of MFIFO elements by |
| * enqueuing pointers to memory elements with associated memq links. |
| */ |
| void ull_rxfifo_alloc(uint8_t s, uint8_t n, uint8_t f, uint8_t *l, uint8_t *m, |
| void *mem_free, void *link_free, uint8_t max) |
| { |
| uint8_t idx; |
| |
| while ((max--) && mfifo_enqueue_idx_get(n, f, *l, &idx)) { |
| memq_link_t *link; |
| struct node_rx_hdr *rx; |
| |
| link = mem_acquire(link_free); |
| if (!link) { |
| break; |
| } |
| |
| rx = mem_acquire(mem_free); |
| if (!rx) { |
| mem_release(link, link_free); |
| break; |
| } |
| |
| link->mem = NULL; |
| rx->link = link; |
| |
| mfifo_by_idx_enqueue(m, s, idx, rx, l); |
| } |
| } |
| |
| /** |
| * @brief Support function for RXFIFO_RELEASE macro |
| * @details This function releases a node by returning it to the FIFO. |
| */ |
| void *ull_rxfifo_release(uint8_t s, uint8_t n, uint8_t f, uint8_t *l, uint8_t *m, |
| memq_link_t *link, struct node_rx_hdr *rx) |
| { |
| uint8_t idx; |
| |
| if (!mfifo_enqueue_idx_get(n, f, *l, &idx)) { |
| return NULL; |
| } |
| |
| rx->link = link; |
| |
| mfifo_by_idx_enqueue(m, s, idx, rx, l); |
| |
| return rx; |
| } |