/* * Copyright (c) 2021 Demant * * SPDX-License-Identifier: Apache-2.0 */ #if defined(CONFIG_BT_CTLR_ISO_RX_SDU_BUFFERS) && (CONFIG_BT_CTLR_ISO_RX_SDU_BUFFERS > 0) #define ISOAL_BUFFER_RX_SDUS_ENABLE #endif /* CONFIG_BT_CTLR_ISO_RX_SDU_BUFFERS > 0 */ /** Function return error codes */ typedef uint8_t isoal_status_t; #define ISOAL_STATUS_OK ((isoal_status_t) 0x00) /* No error */ #define ISOAL_STATUS_ERR_SINK_ALLOC ((isoal_status_t) 0x01) /* Sink pool full */ #define ISOAL_STATUS_ERR_SOURCE_ALLOC ((isoal_status_t) 0x02) /* Source pool full */ #define ISOAL_STATUS_ERR_SDU_ALLOC ((isoal_status_t) 0x04) /* SDU allocation */ #define ISOAL_STATUS_ERR_SDU_EMIT ((isoal_status_t) 0x08) /* SDU emission */ #define ISOAL_STATUS_ERR_PDU_ALLOC ((isoal_status_t) 0x10) /* PDU allocation */ #define ISOAL_STATUS_ERR_PDU_EMIT ((isoal_status_t) 0x20) /* PDU emission */ #define ISOAL_STATUS_ERR_UNSPECIFIED ((isoal_status_t) 0x80) /* Unspecified error */ #define ISOAL_ROLE_CENTRAL (BT_CONN_ROLE_CENTRAL) #define ISOAL_ROLE_PERIPHERAL (BT_CONN_ROLE_PERIPHERAL) #define ISOAL_ROLE_BROADCAST_SOURCE (BT_CONN_ROLE_PERIPHERAL + 1U) #define ISOAL_ROLE_BROADCAST_SINK (BT_CONN_ROLE_PERIPHERAL + 2U) /** Handle to a registered ISO Sub-System sink */ typedef uint8_t isoal_sink_handle_t; /** Handle to a registered ISO Sub-System source */ typedef uint8_t isoal_source_handle_t; /** Byte length of an ISO SDU */ typedef uint16_t isoal_sdu_len_t; /** Byte length of an ISO PDU */ typedef uint8_t isoal_pdu_len_t; /** Count (ID number) of an ISO SDU */ typedef uint16_t isoal_sdu_cnt_t; /** Count (ID number) of an ISO PDU */ typedef uint64_t isoal_pdu_cnt_t; /** Ticks. Used for timestamp */ typedef uint32_t isoal_time_t; /** SDU status codes */ typedef uint8_t isoal_sdu_status_t; #define ISOAL_SDU_STATUS_VALID ((isoal_sdu_status_t) 0x00) #define ISOAL_SDU_STATUS_ERRORS ((isoal_sdu_status_t) 0x01) #define ISOAL_SDU_STATUS_LOST_DATA ((isoal_sdu_status_t) 0x02) /** PDU status codes */ typedef uint8_t isoal_pdu_status_t; #define ISOAL_PDU_STATUS_VALID ((isoal_pdu_status_t) 0x00) #define ISOAL_PDU_STATUS_ERRORS ((isoal_pdu_status_t) 0x01) #define ISOAL_PDU_STATUS_LOST_DATA ((isoal_pdu_status_t) 0x02) /** Production mode */ typedef uint8_t isoal_production_mode_t; #define ISOAL_PRODUCTION_MODE_DISABLED ((isoal_production_mode_t) 0x00) #define ISOAL_PRODUCTION_MODE_ENABLED ((isoal_production_mode_t) 0x01) enum isoal_mode { ISOAL_MODE_CIS, ISOAL_MODE_BIS }; /** * @brief ISO frame SDU buffer - typically an Audio frame buffer * * This provides the underlying vehicle of ISO SDU interchange through the * ISO socket, the contents of which is generally an array of encoded bytes. * Access and life time of this should be limited to ISO and the ISO socket. * We assume no byte-fractional code words. * * Decoding code words to samples is the responsibility of the ISO sub system. */ struct isoal_sdu_buffer { /** Code word buffer * Type, location and alignment decided by ISO sub system */ void *dbuf; /** Number of bytes accessible behind the dbuf pointer */ isoal_sdu_len_t size; }; /** * @brief ISO frame PDU buffer */ struct isoal_pdu_buffer { /** Additional handle for the provided pdu */ void *handle; /** PDU contents */ struct pdu_iso *pdu; /** Maximum size of the data buffer allocated for the payload in the PDU. * Should be at least Max_PDU_C_To_P / Max_PDU_P_To_C depending on * the role. */ isoal_pdu_len_t size; }; /** @brief Produced ISO SDU frame with associated meta data */ struct isoal_sdu_produced { /** Status of contents, if valid or SDU was lost. * This maps directly to the HCI ISO Data packet Packet_Status_Flag. * BT Core V5.3 : Vol 4 HCI I/F : Part G HCI Func. Spec.: * 5.4.5 HCI ISO Data packets : Table 5.2 : * Packet_Status_Flag (in packets sent by the Controller) */ isoal_sdu_status_t status; /** Regardless of status, we always have timing */ isoal_time_t timestamp; /** Sequence number of SDU */ isoal_sdu_cnt_t sn; /** Contents and length can only be trusted if status is valid */ struct isoal_sdu_buffer contents; /** Optional context to be carried from PDU at alloc-time */ void *ctx; }; /** @brief Produced ISO SDU fragment and status information to be emitted */ struct isoal_emitted_sdu_frag { /** Produced SDU to be emitted */ struct isoal_sdu_produced sdu; /** SDU fragment Packet Boundary flags */ uint8_t sdu_state; /** Size of the SDU fragment */ isoal_sdu_len_t sdu_frag_size; }; /** @brief Produced ISO SDU and required status information to be emitted */ struct isoal_emitted_sdu { /** Size of the SDU across all fragments */ isoal_sdu_len_t total_sdu_size; /** Status of contents, if valid or SDU was lost. * This maps directly to the HCI ISO Data packet Packet_Status_Flag. * BT Core V5.3 : Vol 4 HCI I/F : Part G HCI Func. Spec.: * 5.4.5 HCI ISO Data packets : Table 5.2 : * Packet_Status_Flag (in packets sent by the Controller) */ isoal_sdu_status_t collated_status; }; #if defined(ISOAL_BUFFER_RX_SDUS_ENABLE) struct isoal_emit_sdu_queue { struct isoal_emitted_sdu_frag list[CONFIG_BT_CTLR_ISO_RX_SDU_BUFFERS]; uint16_t next_write_indx; }; #endif /* ISOAL_BUFFER_RX_SDUS_ENABLE */ /** @brief Produced ISO PDU encapsulation */ struct isoal_pdu_produced { /** Contents information provided at PDU allocation */ struct isoal_pdu_buffer contents; }; /** @brief Received ISO PDU with associated meta data */ struct isoal_pdu_rx { /** Meta */ struct node_rx_iso_meta *meta; /** PDU contents and length can only be trusted if status is valid */ struct pdu_iso *pdu; }; /** @brief Received ISO SDU with associated meta data */ struct isoal_sdu_tx { /** Code word buffer * Type, location and alignment decided by ISO sub system */ void *dbuf; /** Number of bytes accessible behind the dbuf pointer */ isoal_sdu_len_t size; /** SDU packet boundary flags from HCI indicating the fragment type * can be directly assigned to the SDU state */ uint8_t sdu_state; /** Packet sequence number from HCI ISO Data Header (ISO Data Load * Field) */ uint16_t packet_sn; /** ISO SDU length from HCI ISO Data Header (ISO Data Load Field) */ uint16_t iso_sdu_length; /** Time stamp from HCI or vendor specific path (us) */ uint32_t time_stamp; /** Capture time stamp from controller (us) */ uint32_t cntr_time_stamp; /** CIG Reference of target event (us, compensated for drift) */ uint32_t grp_ref_point; /** Target Event of SDU */ uint64_t target_event:39; }; /* Forward declaration */ struct isoal_sink; struct node_tx_iso; /** * @brief Callback: Request memory for a new ISO SDU buffer * * Proprietary ISO sub systems may have * specific requirements or opinions on where to locate ISO SDUs; some * memories may be faster, may be dynamically mapped in, etc. * * @return ISOAL_STATUS_ERR_ALLOC if size_request could not be fulfilled, otherwise * ISOAL_STATUS_OK. */ typedef isoal_status_t (*isoal_sink_sdu_alloc_cb)( /*!< [in] Sink context */ const struct isoal_sink *sink_ctx, /*!< [in] Received PDU */ const struct isoal_pdu_rx *valid_pdu, /*!< [out] Struct is modified. Must not be NULL */ struct isoal_sdu_buffer *sdu_buffer ); /** * @brief Callback: Push an ISO SDU into an ISO sink * * Call also handing back buffer ownership */ typedef isoal_status_t (*isoal_sink_sdu_emit_cb)( /*!< [in] Sink context */ const struct isoal_sink *sink_ctx, /*!< [in] Emitted SDU fragment and status information */ const struct isoal_emitted_sdu_frag *sdu_frag, /*!< [in] Emitted SDU status information */ const struct isoal_emitted_sdu *sdu ); /** * @brief Callback: Write a number of bytes to SDU buffer */ typedef isoal_status_t (*isoal_sink_sdu_write_cb)( /*!< [in] Destination buffer */ void *dbuf, /*!< [in] Number of bytes already written to this SDU */ const size_t sdu_written, /*!< [in] Source data */ const uint8_t *pdu_payload, /*!< [in] Number of bytes to be copied */ const size_t consume_len ); struct isoal_sink_session { isoal_sink_sdu_alloc_cb sdu_alloc; isoal_sink_sdu_emit_cb sdu_emit; isoal_sink_sdu_write_cb sdu_write; isoal_sdu_cnt_t sn; uint16_t handle; uint16_t iso_interval; uint8_t pdus_per_sdu; uint8_t framed; uint8_t burst_number; uint32_t sdu_interval; uint32_t sdu_sync_const; }; struct isoal_sdu_production { #if defined(ISOAL_BUFFER_RX_SDUS_ENABLE) /* Buffered SDUs */ struct isoal_emit_sdu_queue sdu_list; #endif /* Permit atomic enable/disable of SDU production */ volatile isoal_production_mode_t mode; /* We are constructing an SDU from {<1 or =1 or >1} PDUs */ struct isoal_sdu_produced sdu; /* Bookkeeping */ isoal_pdu_cnt_t prev_pdu_id : 39; /* Assumes that isoal_pdu_cnt_t is a uint64_t bit field */ uint64_t prev_pdu_is_end:1; uint64_t prev_pdu_is_padding:1; /* Indicates that only padding PDUs have been received for this SDU */ uint64_t only_padding:1; uint64_t sdu_allocated:1; uint64_t initialized:1; enum { ISOAL_START, ISOAL_CONTINUE, ISOAL_ERR_SPOOL } fsm; uint8_t pdu_cnt; uint8_t sdu_state; isoal_sdu_len_t sdu_written; isoal_sdu_len_t sdu_available; isoal_sdu_status_t sdu_status; }; struct isoal_sink { /* Session-constant */ struct isoal_sink_session session; /* State for SDU production */ struct isoal_sdu_production sdu_production; }; /* Forward declaration */ struct isoal_source; /** * @brief Callback: Request memory for a new ISO PDU buffer * * Proprietary ISO sub systems may have * specific requirements or opinions on where to locate ISO PDUs; some * memories may be faster, may be dynamically mapped in, etc. * * @return ISOAL_STATUS_ERR_ALLOC if size_request could not be fulfilled, otherwise * ISOAL_STATUS_OK. */ typedef isoal_status_t (*isoal_source_pdu_alloc_cb)( /*!< [out] Struct is modified. Must not be NULL */ struct isoal_pdu_buffer *pdu_buffer ); /** * @brief Callback: Release an ISO PDU */ typedef isoal_status_t (*isoal_source_pdu_release_cb)( /*!< [in] PDU to be released */ struct node_tx_iso *node_tx, /*!< [in] CIS/BIS handle */ const uint16_t handle, /*!< [in] Cause of release. ISOAL_STATUS_OK means PDU was enqueued towards * LLL and sent or flushed, and may need further handling before * memory is released, e.g. forwarded to HCI thread for complete- * counting. Any other status indicates cause of error during SDU * fragmentation/segmentation, in which case the PDU will not be * produced. */ const isoal_status_t status ); /** * @brief Callback: Write a number of bytes to PDU buffer */ typedef isoal_status_t (*isoal_source_pdu_write_cb)( /*!< [out] PDU under production */ struct isoal_pdu_buffer *pdu_buffer, /*!< [in] Offset within PDU buffer */ const size_t offset, /*!< [in] Source data */ const uint8_t *sdu_payload, /*!< [in] Number of bytes to be copied */ const size_t consume_len ); /** * @brief Callback: Enqueue an ISO PDU */ typedef isoal_status_t (*isoal_source_pdu_emit_cb)( /*!< [in] PDU to be enqueued */ struct node_tx_iso *node_tx, /*!< [in] CIS/BIS handle */ const uint16_t handle ); struct isoal_source_session { isoal_source_pdu_alloc_cb pdu_alloc; isoal_source_pdu_write_cb pdu_write; isoal_source_pdu_emit_cb pdu_emit; isoal_source_pdu_release_cb pdu_release; isoal_sdu_cnt_t sn; uint16_t last_input_sn; uint32_t last_input_time_stamp; uint32_t tx_time_stamp; uint32_t tx_time_offset; uint32_t sdu_interval; uint16_t handle; uint16_t iso_interval; uint8_t framed: 1; uint8_t bis: 1; uint8_t burst_number; uint8_t pdus_per_sdu; uint8_t max_pdu_size; }; struct isoal_pdu_production { /* Permit atomic enable/disable of PDU production */ volatile isoal_production_mode_t mode; /* We are constructing a PDU from {<1 or =1 or >1} SDUs */ struct isoal_pdu_produced pdu; uint8_t pdu_state; /* PDUs produced for current SDU */ uint8_t pdu_cnt; uint64_t payload_number:39; uint64_t seg_hdr_sc:1; uint64_t seg_hdr_length:8; uint64_t sdu_fragments:8; uint64_t initialized:1; uint64_t pdu_allocated:1; isoal_pdu_len_t pdu_written; isoal_pdu_len_t pdu_available; /* Location (byte index) of last segmentation header */ isoal_pdu_len_t last_seg_hdr_loc; }; struct isoal_source { /* Session-constant */ struct isoal_source_session session; /* State for PDU production */ struct isoal_pdu_production pdu_production; /* Context Control */ uint64_t timeout_event_count:39; uint64_t timeout_trigger:1; uint64_t context_active:1; }; uint32_t isoal_get_wrapped_time_us(uint32_t time_now_us, int32_t time_diff_us); isoal_status_t isoal_init(void); isoal_status_t isoal_reset(void); isoal_status_t isoal_sink_create(uint16_t handle, uint8_t role, uint8_t framed, uint8_t burst_number, uint8_t flush_timeout, uint32_t sdu_interval, uint16_t iso_interval, uint32_t stream_sync_delay, uint32_t group_sync_delay, isoal_sink_sdu_alloc_cb sdu_alloc, isoal_sink_sdu_emit_cb sdu_emit, isoal_sink_sdu_write_cb sdu_write, isoal_sink_handle_t *hdl); void isoal_sink_enable(isoal_sink_handle_t hdl); void isoal_sink_disable(isoal_sink_handle_t hdl); void isoal_sink_destroy(isoal_sink_handle_t hdl); isoal_status_t isoal_rx_pdu_recombine(isoal_sink_handle_t sink_hdl, const struct isoal_pdu_rx *pdu_meta); /* SDU Call backs for HCI interface */ isoal_status_t sink_sdu_alloc_hci(const struct isoal_sink *sink_ctx, const struct isoal_pdu_rx *valid_pdu, struct isoal_sdu_buffer *sdu_buffer); isoal_status_t sink_sdu_emit_hci(const struct isoal_sink *sink_ctx, const struct isoal_emitted_sdu_frag *sdu_frag, const struct isoal_emitted_sdu *sdu); isoal_status_t sink_sdu_write_hci(void *dbuf, const size_t sdu_written, const uint8_t *pdu_payload, const size_t consume_len); isoal_status_t isoal_source_create(uint16_t handle, uint8_t role, uint8_t framed, uint8_t burst_number, uint8_t flush_timeout, uint8_t max_octets, uint32_t sdu_interval, uint16_t iso_interval, uint32_t stream_sync_delay, uint32_t group_sync_delay, isoal_source_pdu_alloc_cb pdu_alloc, isoal_source_pdu_write_cb pdu_write, isoal_source_pdu_emit_cb pdu_emit, isoal_source_pdu_release_cb pdu_release, isoal_source_handle_t *hdl); void isoal_source_enable(isoal_source_handle_t hdl); void isoal_source_disable(isoal_source_handle_t hdl); void isoal_source_destroy(isoal_source_handle_t hdl); isoal_status_t isoal_tx_sdu_fragment(isoal_source_handle_t source_hdl, struct isoal_sdu_tx *tx_sdu); uint16_t isoal_tx_unframed_get_next_payload_number(isoal_source_handle_t source_hdl, const struct isoal_sdu_tx *tx_sdu, uint64_t *payload_number); void isoal_tx_pdu_release(isoal_source_handle_t source_hdl, struct node_tx_iso *node_tx); isoal_status_t isoal_tx_get_sync_info(isoal_source_handle_t source_hdl, uint16_t *seq, uint32_t *timestamp, uint32_t *offset); void isoal_tx_event_prepare(isoal_source_handle_t source_hdl, uint64_t event_number);