diff --git a/nimble/host/include/host/ble_gap.h b/nimble/host/include/host/ble_gap.h index d9c1bf34d..9f633865b 100644 --- a/nimble/host/include/host/ble_gap.h +++ b/nimble/host/include/host/ble_gap.h @@ -172,6 +172,16 @@ struct hci_conn_update; #define BLE_GAP_EVENT_PER_SUBEV_RESP 41 #define BLE_GAP_EVENT_PERIODIC_TRANSFER_V2 42 +/* ISO events */ +#define BLE_GAP_EVENT_CIS_ESTAB 43 +#define BLE_GAP_EVENT_CIS_REQUEST 44 +#define BLE_GAP_EVENT_CREATE_BIG_COMP 45 +#define BLE_GAP_EVENT_TERM_BIG_COMP 46 +#define BLE_GAP_EVENT_BIG_SYNC_ESTAB 47 +#define BLE_GAP_EVENT_BIG_SYNC_LOST 48 +#define BLE_GAP_EVENT_BIGINFO_ADV_RPT 49 +#define BLE_GAP_EVENT_CIS_ESTAB_V2 50 + /* DTM events */ #define BLE_GAP_DTM_TX_START_EVT 0 #define BLE_GAP_DTM_RX_START_EVT 1 @@ -1207,7 +1217,7 @@ struct ble_gap_event { } periodic_transfer; #endif -#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) && !MYNEWT_VAL(BLE_ISO) /** * Represents a periodic advertising sync transfer received. Valid for * the following event types: @@ -1653,6 +1663,293 @@ struct ble_gap_event { } cte_req_fail; #endif + +#if MYNEWT_VAL(BLE_ISO) + struct { + /** BLE_ERR_SUCCESS on success or error code on failure */ + uint8_t status; + + /** Connection handle of the CIS */ + uint16_t cis_handle; + + /** The maximum time, in microseconds, for transmission of PDUs + * of all CISes in a CIG event. + */ + uint32_t cig_sync_delay; + + /** The maximum time, in microseconds, for transmission of PDUs + * of the specified CIS in a CIG event. + */ + uint32_t cis_sync_delay; + + /** The actual transport latency, in microseconds, from Central to Peripheral */ + uint32_t transport_latency_c_to_p; + + /** The actual transport latency, in microseconds, from Peripheral to Central */ + uint32_t transport_latency_p_to_c; + + /** TX PHY (Central to Peripheral) */ + uint8_t phy_c_to_p; + + /** TX PHY (Peripheral to Central) */ + uint8_t phy_p_to_c; + + /** Maximum number of subevents in each CIS event */ + uint8_t nse; + + /** The burst number for Central to Peripheral transmission */ + uint8_t bn_c_to_p; + + /** The burst number for Peripheral to Central transmission */ + uint8_t bn_p_to_c; + + /** The flush timeout, in multiple of the ISO_Interval for the CIS, + * for each payload sent from Central to Peripheral. + */ + uint8_t ft_c_to_p; + + /** The flush timeout, in multiple of the ISO_Interval for the CIS, + * for each payload sent from Peripheral to Central. + */ + uint8_t ft_p_to_c; + + /** Maximum size, in octets, of the payload from Central to Peripheral */ + uint16_t max_pdu_c_to_p; + + /** Maximum size, in octets, of the payload from Peripheral to Central */ + uint16_t max_pdu_p_to_c; + + /** The time between two consecutive CIS anchor points (unit 1.25ms) */ + uint16_t iso_interval; + } cis_estab; + + struct { + /** Connection handle of the ACL */ + uint16_t conn_handle; + + /** Connection handle of the CIS */ + uint16_t cis_handle; + + /** Identifier of the CIG */ + uint8_t cig_id; + + /** Identifier of the CIS */ + uint8_t cis_id; + } cis_request; + + struct { + /** BLE_ERR_SUCCESS on success or error code on failure */ + uint8_t status; + + /** Identifier of the BIG */ + uint8_t big_handle; + + /** The maximum time, in microseconds, for transmission of PDUs + * of all BISes in a BIG event. + */ + uint32_t big_sync_delay; + + /** The actual transport latency, in microseconds */ + uint32_t transport_latency; + + /** PHY used to create the BIG */ + uint8_t phy; + + /** The number of subevents in each BIS event in the BIG */ + uint8_t nse; + + /** The number of new payloads in each BIS event */ + uint8_t bn; + + /** Offset used for pre-transmissions */ + uint8_t pto; + + /** The number of times a payload is transmitted in a BIS event */ + uint8_t irc; + + /** Maximum size, in octets, of the payload */ + uint16_t max_pdu; + + /** The time between two consecutive BIG anchor points (unit 1.25ms) */ + uint16_t iso_interval; + + /** Total number of BISes in the BIG */ + uint8_t bis_cnt; + + /** Connection handles of the BISes */ + uint16_t bis_handle[MYNEWT_VAL(BLE_ISO_BIS_PER_BIG)]; + } create_big_comp; + + struct { + /** Identifier of the BIG */ + uint8_t big_handle; + + /** Reason for termination */ + uint8_t reason; + } term_big_comp; + + struct { + /** BLE_ERR_SUCCESS on success or error code on failure */ + uint8_t status; + + /** Identifier of the BIG */ + uint8_t big_handle; + + /** The actual transport latency, in microseconds */ + uint32_t transport_latency; + + /** The number of subevents in each BIS event in the BIG */ + uint8_t nse; + + /** The number of new payloads in each BIS event */ + uint8_t bn; + + /** Offset used for pre-transmissions */ + uint8_t pto; + + /** The number of times a payload is transmitted in a BIS event */ + uint8_t irc; + + /** Maximum size, in octets, of the payload */ + uint16_t max_pdu; + + /** The time between two consecutive BIG anchor points (unit 1.25ms) */ + uint16_t iso_interval; + + /** Total number of BISes in the BIG */ + uint8_t bis_cnt; + + /** Connection handles of the BISes */ + uint16_t bis_handle[MYNEWT_VAL(BLE_ISO_BIS_PER_BIG)]; + } big_sync_estab; + + struct { + /** Identifier of the BIG */ + uint8_t big_handle; + + /** Reason for termination */ + uint8_t reason; + } big_sync_lost; + + struct { + /** Sync_Handle identifying the periodic advertising train */ + uint16_t sync_handle; + + /** Total number of BISes in the BIG */ + uint8_t bis_cnt; + + /** The number of subevents in each BIS event in the BIG */ + uint8_t nse; + + /** The time between two consecutive BIG anchor points (unit 1.25ms) */ + uint16_t iso_interval; + + /** The number of new payloads in each BIS event */ + uint8_t bn; + + /** Offset used for pre-transmissions */ + uint8_t pto; + + /** The number of times a payload is transmitted in a BIS event */ + uint8_t irc; + + /** Maximum size, in octets, of the payload */ + uint16_t max_pdu; + + /** Time, in microseconds, between the start of consecutive SDUs */ + uint32_t sdu_interval; + + /** Maximum size of an SDU, in octets */ + uint16_t max_sdu; + + /** PHY used by the BIG */ + uint8_t phy; + + /** Unframed or framed PDUs */ + uint8_t framing; + + /** BIG encryption */ + uint8_t encryption; + } biginfo_report; + +#if MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) + struct { + /** BLE_ERR_SUCCESS on success or error code on failure */ + uint8_t status; + + /** Connection handle of the CIS */ + uint16_t cis_handle; + + /** The maximum time, in microseconds, for transmission of PDUs + * of all CISes in a CIG event. + */ + uint32_t cig_sync_delay; + + /** The maximum time, in microseconds, for transmission of PDUs + * of the specified CIS in a CIG event. + */ + uint32_t cis_sync_delay; + + /** The actual transport latency, in microseconds, from Central to Peripheral */ + uint32_t transport_latency_c_to_p; + + /** The actual transport latency, in microseconds, from Peripheral to Central */ + uint32_t transport_latency_p_to_c; + + /** TX PHY (Central to Peripheral) */ + uint8_t phy_c_to_p; + + /** TX PHY (Peripheral to Central) */ + uint8_t phy_p_to_c; + + /** Maximum number of subevents in each CIS event */ + uint8_t nse; + + /** The burst number for Central to Peripheral transmission */ + uint8_t bn_c_to_p; + + /** The burst number for Peripheral to Central transmission */ + uint8_t bn_p_to_c; + + /** The flush timeout, in multiple of the ISO_Interval for the CIS, + * for each payload sent from Central to Peripheral. + */ + uint8_t ft_c_to_p; + + /** The flush timeout, in multiple of the ISO_Interval for the CIS, + * for each payload sent from Peripheral to Central. + */ + uint8_t ft_p_to_c; + + /** Maximum size, in octets, of the payload from Central to Peripheral */ + uint16_t max_pdu_c_to_p; + + /** Maximum size, in octets, of the payload from Peripheral to Central */ + uint16_t max_pdu_p_to_c; + + /** The time between two consecutive CIS anchor points (unit 1.25ms) */ + uint16_t iso_interval; + + /** Time, in microseconds, between the start of consecutive subevents in a CIS event */ + uint32_t sub_interval; + + /** Maximum size, in octets, of the payload from Central's Host */ + uint16_t max_sdu_c_to_p; + + /** Maximum size, in octets, of the payload from Peripheral's Host */ + uint16_t max_sdu_p_to_c; + + /** Time, in microseconds, between the start of consecutive SDUs sent by Central */ + uint32_t sdu_interval_c_to_p; + + /** Time, in microseconds, between the start of consecutive SDUs sent by Peripheral */ + uint32_t sdu_interval_p_to_c; + + /** Unframed or framed PDUs */ + uint8_t framing; + } cis_estab_v2; +#endif /* MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) */ +#endif /* MYNEWT_VAL(BLE_ISO) */ }; }; @@ -3372,6 +3669,164 @@ ble_gap_subrate_req(uint16_t conn_handle, uint16_t subrate_min, uint16_t subrate uint16_t max_latency, uint16_t cont_num, uint16_t supervision_timeout); #endif + +#if MYNEWT_VAL(BLE_ISO) +/** + * Create CIS by Central. + * + * @param cis_cnt The number of CISes to create. + * @param params The parameters used to create CISes. + * @param cb The callback to associate with the CIS creation procedure. + * When the CIS creation procedure completes, the result is + * reported through this callback. + * @param cb_arg The optional argument to pass to the callback function. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_create_cis(uint8_t cis_cnt, const struct ble_hci_le_create_cis_params *params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Accept CIS request by Peripheral. + * + * @param cis_handle Connection handle of the CIS. + * @param cb The callback to associate with the CIS creation procedure. + * When the CIS creation procedure completes, the result is + * reported through this callback. + * @param cb_arg The optional argument to pass to the callback function. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_accept_cis_request(uint16_t cis_handle, ble_gap_event_fn *cb, void *cb_arg); + +/** + * Reject CIS request by Peripheral. + * + * @param cis_handle Connection handle of the CIS. + * @param reason Reason the CIS request was rejected. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_reject_cis_request(uint16_t cis_handle, uint8_t reason); + +/** + * Disconnect CIS. + * + * @param cis_handle Connection handle of the CIS. + * @param reason Reason the CIS was disconnected. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_iso_disconnect(uint16_t cis_handle, uint8_t reason); + +/** + * Create BIG Broadcaster. + * + * @param big_handle Identifier of the BIG. + * @param adv_handle Associated periodic advertising train. + * @param num_bis Total number of BISes in the BIG. + * @param sdu_interval The interval, in microseconds, of SDUs. + * @param max_sdu Maximum size, in octets, of an SDU. + * @param max_transport_latency Maximum transport latency, in milliseconds. + * @param rtn The number of times that every BIS PDU should be retransmitted. + * @param phy Transmitter PHY. + * @param packing Sequential or Interleaved. + * @param framing Unframed or framed. + * @param encryption Unencrypted or encrypted. + * @param broadcast_code The code used to derive the BIG session key. + * @param cb The callback to associate with the BIG creation procedure. + * When the BIG creation procedure completes, the result is + * reported through this callback. + * @param cb_arg The optional argument to pass to the callback function. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_create_big(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, + uint32_t sdu_interval, uint16_t max_sdu, + uint16_t max_transport_latency, uint8_t rtn, + uint8_t phy, uint8_t packing, uint8_t framing, + uint8_t encryption, uint8_t broadcast_code[16], + ble_gap_event_fn *cb, void *cb_arg); + +#if MYNEWT_VAL(BLE_ISO_TEST) +/** + * Create BIG Broadcaster with test command. + * + * @param big_handle Identifier of the BIG. + * @param adv_handle Associated periodic advertising train. + * @param num_bis Total number of BISes in the BIG. + * @param sdu_interval The interval, in microseconds, of ISO SDUs. + * @param iso_interval The time between consecutive BIG anchor points (unit 1.25ms). + * @param nse The total number of subevents in each interval of each BIS in the BIG. + * @param max_sdu Maximum size, in octets, of an SDU. + * @param max_pdu Maximum size, in octets, of payload. + * @param phy Transmitter PHY. + * @param packing Sequential or Interleaved. + * @param framing Unframed or framed. + * @param bn The number of new payloads in each interval for each BIS. + * @param irc The number of times the payloads are transmitted in a given event. + * @param pto Offset used for pre-transmissions. + * @param encryption Unencrypted or encrypted. + * @param broadcast_code The code used to derive the BIG session key. + * @param cb The callback to associate with the BIG creation procedure. + * When the BIG creation procedure completes, the result is + * reported through this callback. + * @param cb_arg The optional argument to pass to the callback function. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_create_big_test(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, + uint32_t sdu_interval, uint16_t iso_interval, uint8_t nse, + uint16_t max_sdu, uint16_t max_pdu, uint8_t phy, + uint8_t packing, uint8_t framing, uint8_t bn, uint8_t irc, + uint8_t pto, uint8_t encryption, uint8_t broadcast_code[16], + ble_gap_event_fn *cb, void *cb_arg); +#endif /* MYNEWT_VAL(BLE_ISO_TEST) */ + +/** + * Terminate BIG Broadcaster. + * + * @param big_handle Identifier of the BIG. + * @param reason Reason the BIG is terminated. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_terminate_big(uint8_t big_handle, uint8_t reason); + +/** + * Create BIG Synchronized Receiver. + * + * @param big_handle Identifier of the BIG. + * @param sync_handle Associated periodic advertising train. + * @param encryption Unencrypted or encrypted. + * @param broadcast_code The code used to derive the BIG session key. + * @param mse Maximum number of subevents that should be used to receive in each BIS event. + * @param sync_timeout Synchronization timeout for the BIG (unit 10ms). + * @param num_bis Total number of BISes in the BIG. + * @param bis_index Indexes of the BISes. + * @param cb The callback to associate with the BIG creation procedure. + * When the BIG creation procedure completes, the result is + * reported through this callback. + * @param cb_arg The optional argument to pass to the callback function. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_big_create_sync(uint8_t big_handle, uint16_t sync_handle, + uint8_t encryption, uint8_t broadcast_code[16], + uint8_t mse, uint16_t sync_timeout, + uint8_t num_bis, uint8_t *bis_index, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Terminate BIG Synchronized Receiver. + * + * @param big_handle Identifier of the BIG. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_big_terminate_sync(uint8_t big_handle); +#endif /* MYNEWT_VAL(BLE_ISO) */ + /** * Event listener structure * diff --git a/nimble/host/include/host/ble_hs_hci.h b/nimble/host/include/host/ble_hs_hci.h index 5b28d9991..d5a8ee5b9 100644 --- a/nimble/host/include/host/ble_hs_hci.h +++ b/nimble/host/include/host/ble_hs_hci.h @@ -120,6 +120,16 @@ int ble_hs_hci_send_vs_cmd(uint16_t ocf, const void *cmdbuf, uint8_t cmdlen, void *rspbuf, uint8_t rsplen); #endif +/** + * Instructs the controller to set or clear a bit controlled by the Host in the + * Link Layer FeatureSet stored in the Controller. + * + * @param bit_num Bit position in the FeatureSet. + * @param bit_val 0: The Host feature is disabled; 1: The Host feature is enabled. + * @return 0 on success, error code on failure + */ +int ble_hs_hci_set_host_feature(uint8_t bit_num, uint8_t bit_val); + #ifdef __cplusplus } #endif diff --git a/nimble/host/include/host/ble_hs_iso.h b/nimble/host/include/host/ble_hs_iso.h new file mode 100644 index 000000000..4588b1bce --- /dev/null +++ b/nimble/host/include/host/ble_hs_iso.h @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#ifndef H_BLE_HS_ISO_ +#define H_BLE_HS_ISO_ + +#include +#include "os/os.h" +#include "mem/mem.h" +#include "syscfg/syscfg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int ble_hs_hci_set_iso_buf_sz(uint16_t pktlen, uint16_t max_pkts); + +void ble_hs_hci_add_iso_avail_pkts(uint16_t conn_handle, uint16_t delta); + +int ble_hs_hci_iso_tx(uint16_t conn_handle, const uint8_t *sdu, uint16_t sdu_len, + bool ts_flag, uint32_t time_stamp, uint16_t pkt_seq_num); + +typedef int (*ble_hs_iso_pkt_rx_fn)(const uint8_t *data, uint16_t len, void *arg); + +int ble_hs_iso_pkt_rx_cb_set(void *cb); + +int ble_hs_rx_iso_data(const uint8_t *data, uint16_t len, void *arg); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_HS_ISO_ */ diff --git a/nimble/host/include/host/ble_hs_iso_hci.h b/nimble/host/include/host/ble_hs_iso_hci.h new file mode 100644 index 000000000..670e71911 --- /dev/null +++ b/nimble/host/include/host/ble_hs_iso_hci.h @@ -0,0 +1,120 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#ifndef H_BLE_HS_ISO_HCI_ +#define H_BLE_HS_ISO_HCI_ + +#include +#include "os/os.h" +#include "mem/mem.h" +#include "syscfg/syscfg.h" +#include "nimble/hci_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MYNEWT_VAL(BLE_ISO) +int ble_hs_hci_iso_disconnect(uint16_t cis_handle, uint8_t reason); + +int ble_hs_hci_read_local_supp_codec(uint8_t *rsp_buf, uint8_t rsp_len); + +int ble_hs_hci_read_local_supp_codec_caps(uint8_t coding_fmt, uint16_t company_id, + uint16_t vs_codec_id, uint8_t logical_transport_type, + uint8_t direction, uint8_t *rsp_buf, uint8_t rsp_len); + +int ble_hs_hci_read_local_supp_controller_delay(uint8_t coding_fmt, uint16_t company_id, + uint16_t vs_codec_id, uint8_t logical_transport_type, + uint8_t direction, + uint8_t codec_cfg_len, uint8_t *codec_cfg, + uint32_t *min_controller_delay, + uint32_t *max_controller_delay); + +int ble_hs_hci_cfg_data_path(uint8_t data_path_direction, uint8_t data_path_id, + uint8_t vs_cfg_len, uint8_t *vs_cfg); + +int ble_hs_hci_read_buf_sz_v2(uint16_t *data_len, uint8_t *data_packets, + uint16_t *iso_data_len, uint8_t *iso_data_packets); + +int ble_hs_hci_read_iso_tx_sync(uint16_t conn_handle, uint16_t *packet_seq_num, + uint32_t *timestamp, uint32_t *timeoffset); + +int ble_hs_hci_set_cig_params(uint8_t cig_id, uint32_t sdu_interval_c_to_p, uint32_t sdu_interval_p_to_c, + uint8_t slaves_clock_accuracy, uint8_t packing, uint8_t framing, + uint16_t max_transport_latency_c_to_p, uint16_t max_transport_latency_p_to_c, + uint8_t cis_cnt, struct ble_hci_le_cis_params *cis_params, + uint8_t *rsp_buf, uint8_t rsp_len); + +#if MYNEWT_VAL(BLE_ISO_TEST) +int ble_hs_hci_set_cig_params_test(uint8_t cig_id, uint32_t sdu_interval_c_to_p, uint32_t sdu_interval_p_to_c, + uint8_t ft_c_to_p, uint8_t ft_p_to_c, uint16_t iso_interval, + uint8_t slaves_clock_accuracy, uint8_t packing, uint8_t framing, + uint8_t cis_cnt, struct ble_hci_le_cis_params_test *cis_params, + uint8_t *rsp_buf, uint8_t rsp_len); +#endif /* MYNEWT_VAL(BLE_ISO_TEST) */ + +int ble_hs_hci_create_cis(uint8_t cis_cnt, const struct ble_hci_le_create_cis_params *params); + +int ble_hs_hci_remove_cig(uint8_t cig_id); + +int ble_hs_hci_accept_cis_request(uint16_t cis_handle); + +int ble_hs_hci_reject_cis_request(uint16_t cis_handle, uint8_t reason); + +int ble_hs_hci_create_big(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, + uint32_t sdu_interval, uint16_t max_sdu, + uint16_t max_transport_latency, uint8_t rtn, uint8_t phy, + uint8_t packing, uint8_t framing, uint8_t encryption, + uint8_t broadcast_code[16]); + +#if MYNEWT_VAL(BLE_ISO_TEST) +int ble_hs_hci_create_big_test(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, + uint32_t sdu_interval, uint16_t iso_interval, uint8_t nse, + uint16_t max_sdu, uint16_t max_pdu, uint8_t phy, + uint8_t packing, uint8_t framing, uint8_t bn, uint8_t irc, + uint8_t pto, uint8_t encryption, uint8_t broadcast_code[16]); +#endif /* MYNEWT_VAL(BLE_ISO_TEST) */ + +int ble_hs_hci_terminate_big(uint8_t big_handle, uint8_t reason); + +int ble_hs_hci_big_create_sync(uint8_t big_handle, uint16_t sync_handle, + uint8_t encryption, uint8_t broadcast_code[16], + uint8_t mse, uint16_t sync_timeout, + uint8_t num_bis, uint8_t *bis_index); + +int ble_hs_hci_big_terminate_sync(uint8_t big_handle); + +int ble_hs_hci_setup_iso_data_path(uint16_t conn_handle, uint8_t data_path_direction, + uint8_t data_path_id, uint8_t coding_fmt, + uint16_t company_id, uint16_t vs_codec_id, + uint32_t controller_delay, uint8_t codec_cfg_len, + uint8_t *codec_cfg); + +int ble_hs_hci_remove_iso_data_path(uint16_t conn_handle, uint8_t data_path_direction); + +#if MYNEWT_VAL(BLE_ISO_TEST) +int ble_hs_hci_iso_transmit_test(uint16_t conn_handle, uint8_t payload_type); + +int ble_hs_hci_iso_receive_test(uint16_t conn_handle, uint8_t payload_type); + +int ble_hs_hci_iso_read_test_counters(uint16_t conn_handle, uint32_t *received_sdu_count, + uint32_t *missed_sdu_count, uint32_t *failed_sdu_count); + +int ble_hs_hci_iso_test_end(uint16_t conn_handle, uint32_t *received_sdu_count, + uint32_t *missed_sdu_count, uint32_t *failed_sdu_count); +#endif /* MYNEWT_VAL(BLE_ISO_TEST) */ + +int ble_hs_hci_read_iso_link_quality(uint16_t conn_handle, uint32_t *tx_unacked_pkts, + uint32_t *tx_flushed_pkts, uint32_t *tx_last_subev_pkts, + uint32_t *retransmitted_pkts, uint32_t *crc_error_pkts, + uint32_t *rx_unreceived_pkts, uint32_t *duplicate_pkts); +#endif /* MYNEWT_VAL(BLE_ISO) */ + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_HS_ISO_HCI_ */ diff --git a/nimble/host/src/ble_gap.c b/nimble/host/src/ble_gap.c index f76aad252..07f0aa03b 100644 --- a/nimble/host/src/ble_gap.c +++ b/nimble/host/src/ble_gap.c @@ -34,6 +34,10 @@ #include "host/ble_hs_pvcy.h" #include "host/util/util.h" +#if MYNEWT_VAL(BLE_ISO) +#include "host/ble_hs_iso_hci.h" +#endif /* MYNEWT_VAL(BLE_ISO) */ + #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #endif @@ -256,6 +260,29 @@ struct ble_gap_slave_state { static bssnz_t struct ble_gap_slave_state ble_gap_slave[BLE_ADV_INSTANCES]; +#if MYNEWT_VAL(BLE_ISO) +struct ble_gap_cis_state { + ble_gap_event_fn *cb; + void *cb_arg; +}; + +static bssnz_t struct ble_gap_cis_state ble_gap_cis; + +struct ble_gap_big_brd_state { + ble_gap_event_fn *cb; + void *cb_arg; +}; + +static bssnz_t struct ble_gap_big_brd_state ble_gap_big_brd; + +struct ble_gap_big_snc_state { + ble_gap_event_fn *cb; + void *cb_arg; +}; + +static bssnz_t struct ble_gap_big_snc_state ble_gap_big_snc; +#endif /* MYNEWT_VAL(BLE_ISO) */ + struct ble_gap_update_entry { SLIST_ENTRY(ble_gap_update_entry) next; struct ble_gap_upd_params params; @@ -1934,6 +1961,248 @@ ble_gap_rx_adv_report_sanity_check(const uint8_t *adv_data, uint8_t adv_data_len } #endif +#if MYNEWT_VAL(BLE_ISO) +void +ble_gap_rx_cis_disconn(const struct ble_hci_ev_disconn_cmp *ev) +{ + struct ble_gap_event event; + + memset(&event, 0, sizeof(event)); + + event.type = BLE_GAP_EVENT_DISCONNECT; + event.disconnect.reason = ev->reason; + event.disconnect.conn.conn_handle = ev->conn_handle; + + ble_gap_event_listener_call(&event); + if (ble_gap_cis.cb) { + ble_gap_cis.cb(&event, ble_gap_cis.cb_arg); + } +} + +void +ble_gap_rx_cis_estab(const struct ble_hci_ev_le_subev_cis_established *ev) +{ + struct ble_gap_event event; + + memset(&event, 0, sizeof(event)); + + event.type = BLE_GAP_EVENT_CIS_ESTAB; + + event.cis_estab.status = ev->status; + event.cis_estab.cis_handle = ev->conn_handle; + event.cis_estab.cig_sync_delay = get_le24(ev->cig_sync_delay); + event.cis_estab.cis_sync_delay = get_le24(ev->cis_sync_delay); + event.cis_estab.transport_latency_c_to_p = get_le24(ev->transport_latency_c_to_p); + event.cis_estab.transport_latency_p_to_c = get_le24(ev->transport_latency_p_to_c); + event.cis_estab.phy_c_to_p = ev->phy_c_to_p; + event.cis_estab.phy_p_to_c = ev->phy_p_to_c; + event.cis_estab.nse = ev->nse; + event.cis_estab.bn_c_to_p = ev->bn_c_to_p; + event.cis_estab.bn_p_to_c = ev->bn_p_to_c; + event.cis_estab.ft_c_to_p = ev->ft_c_to_p; + event.cis_estab.ft_p_to_c = ev->ft_p_to_c; + event.cis_estab.max_pdu_c_to_p = ev->max_pdu_c_to_p; + event.cis_estab.max_pdu_p_to_c = ev->max_pdu_p_to_c; + event.cis_estab.iso_interval = ev->iso_interval; + + ble_gap_event_listener_call(&event); + if (ble_gap_cis.cb) { + ble_gap_cis.cb(&event, ble_gap_cis.cb_arg); + } +} + +void +ble_gap_rx_cis_request(const struct ble_hci_ev_le_subev_cis_request *ev) +{ + struct ble_gap_event event; + ble_gap_event_fn *cb; + void *cb_arg; + + memset(&event, 0, sizeof(event)); + + event.type = BLE_GAP_EVENT_CIS_REQUEST; + + event.cis_request.conn_handle = ev->acl_conn_handle; + event.cis_request.cis_handle = ev->cis_conn_handle; + event.cis_request.cig_id = ev->cig_id; + event.cis_request.cis_id = ev->cis_id; + + ble_gap_event_listener_call(&event); + + ble_gap_slave_extract_cb(0, &cb, &cb_arg); + if (cb) { + cb(&event, cb_arg); + } +} + +void +ble_gap_rx_create_big_comp(const struct ble_hci_ev_le_subev_create_big_complete *ev) +{ + struct ble_gap_event event; + + memset(&event, 0, sizeof(event)); + + event.type = BLE_GAP_EVENT_CREATE_BIG_COMP; + + event.create_big_comp.status = ev->status; + event.create_big_comp.big_handle = ev->big_handle; + event.create_big_comp.big_sync_delay = get_le24(ev->big_sync_delay); + event.create_big_comp.transport_latency = get_le24(ev->transport_latency_big); + event.create_big_comp.phy = ev->phy; + event.create_big_comp.nse = ev->nse; + event.create_big_comp.bn = ev->bn; + event.create_big_comp.pto = ev->pto; + event.create_big_comp.irc = ev->irc; + event.create_big_comp.max_pdu = ev->max_pdu; + event.create_big_comp.iso_interval = ev->iso_interval; + event.create_big_comp.bis_cnt = ev->num_bis; + memcpy(event.create_big_comp.bis_handle, ev->conn_handle, ev->num_bis * 2); + + ble_gap_event_listener_call(&event); + if (ble_gap_big_brd.cb) { + ble_gap_big_brd.cb(&event, ble_gap_big_brd.cb_arg); + } +} + +void +ble_gap_rx_term_big_comp(const struct ble_hci_ev_le_subev_terminate_big_complete *ev) +{ + struct ble_gap_event event; + + memset(&event, 0, sizeof(event)); + + event.type = BLE_GAP_EVENT_TERM_BIG_COMP; + + event.term_big_comp.big_handle = ev->big_handle; + event.term_big_comp.reason = ev->reason; + + ble_gap_event_listener_call(&event); + if (ble_gap_big_brd.cb) { + ble_gap_big_brd.cb(&event, ble_gap_big_brd.cb_arg); + } +} + +void +ble_gap_rx_big_sync_estab(const struct ble_hci_ev_le_subev_big_sync_established *ev) +{ + struct ble_gap_event event; + + memset(&event, 0, sizeof(event)); + + event.type = BLE_GAP_EVENT_BIG_SYNC_ESTAB; + + event.big_sync_estab.status = ev->status; + event.big_sync_estab.big_handle = ev->big_handle; + event.big_sync_estab.transport_latency = get_le24(ev->transport_latency_big); + event.big_sync_estab.nse = ev->nse; + event.big_sync_estab.bn = ev->bn; + event.big_sync_estab.pto = ev->pto; + event.big_sync_estab.irc = ev->irc; + event.big_sync_estab.max_pdu = ev->max_pdu; + event.big_sync_estab.iso_interval = ev->iso_interval; + event.big_sync_estab.bis_cnt = ev->num_bis; + for(size_t i = 0; i < ev->num_bis; i++){ + event.big_sync_estab.bis_handle[i] = ev->conn_handle[i]; + } + + ble_gap_event_listener_call(&event); + if (ble_gap_big_snc.cb) { + ble_gap_big_snc.cb(&event, ble_gap_big_snc.cb_arg); + } +} + +void +ble_gap_rx_big_sync_lost(const struct ble_hci_ev_le_subev_big_sync_lost *ev) +{ + struct ble_gap_event event; + + memset(&event, 0, sizeof(event)); + + event.type = BLE_GAP_EVENT_BIG_SYNC_LOST; + + event.big_sync_lost.big_handle = ev->big_handle; + event.big_sync_lost.reason = ev->reason; + + ble_gap_event_listener_call(&event); + if (ble_gap_big_snc.cb) { + ble_gap_big_snc.cb(&event, ble_gap_big_snc.cb_arg); + } +} + +void +ble_gap_rx_biginfo_adv_rpt(const struct ble_hci_ev_le_subev_biginfo_adv_report *ev) +{ + struct ble_gap_master_state state; + struct ble_gap_event event; + + memset(&event, 0, sizeof(event)); + + event.type = BLE_GAP_EVENT_BIGINFO_ADV_RPT; + + event.biginfo_report.sync_handle = ev->sync_handle; + event.biginfo_report.bis_cnt = ev->bis_cnt; + event.biginfo_report.nse = ev->nse; + event.biginfo_report.iso_interval = ev->iso_interval; + event.biginfo_report.bn = ev->bn; + event.biginfo_report.pto = ev->pto; + event.biginfo_report.irc = ev->irc; + event.biginfo_report.max_pdu = ev->max_pdu; + event.biginfo_report.sdu_interval = get_le24(ev->sdu_interval); + event.biginfo_report.max_sdu = ev->max_sdu; + event.biginfo_report.phy = ev->phy; + event.biginfo_report.framing = ev->framing; + event.biginfo_report.encryption = ev->encryption; + + ble_gap_master_extract_state(&state, 0); + + if (ble_gap_has_client(&state)) { + state.cb(&event, state.cb_arg); + } + + ble_gap_event_listener_call(&event); +} + +#if MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) +void +ble_gap_rx_cis_estab_v2(const struct ble_hci_ev_le_subev_cis_established_v2 *ev) +{ + struct ble_gap_event event; + + memset(&event, 0, sizeof(event)); + + event.type = BLE_GAP_EVENT_CIS_ESTAB_V2; + + event.cis_estab_v2.status = ev->status; + event.cis_estab_v2.cis_handle = ev->conn_handle; + event.cis_estab_v2.cig_sync_delay = get_le24(ev->cig_sync_delay); + event.cis_estab_v2.cis_sync_delay = get_le24(ev->cis_sync_delay); + event.cis_estab_v2.transport_latency_c_to_p = get_le24(ev->transport_latency_c_to_p); + event.cis_estab_v2.transport_latency_p_to_c = get_le24(ev->transport_latency_p_to_c); + event.cis_estab_v2.phy_c_to_p = ev->phy_c_to_p; + event.cis_estab_v2.phy_p_to_c = ev->phy_p_to_c; + event.cis_estab_v2.nse = ev->nse; + event.cis_estab_v2.bn_c_to_p = ev->bn_c_to_p; + event.cis_estab_v2.bn_p_to_c = ev->bn_p_to_c; + event.cis_estab_v2.ft_c_to_p = ev->ft_c_to_p; + event.cis_estab_v2.ft_p_to_c = ev->ft_p_to_c; + event.cis_estab_v2.max_pdu_c_to_p = ev->max_pdu_c_to_p; + event.cis_estab_v2.max_pdu_p_to_c = ev->max_pdu_p_to_c; + event.cis_estab_v2.iso_interval = ev->iso_interval; + event.cis_estab_v2.sub_interval = get_le24(ev->sub_interval); + event.cis_estab_v2.max_sdu_c_to_p = ev->max_sdu_c_to_p; + event.cis_estab_v2.max_sdu_p_to_c = ev->max_sdu_p_to_c; + event.cis_estab_v2.sdu_interval_c_to_p = get_le24(ev->sdu_interval_c_to_p); + event.cis_estab_v2.sdu_interval_p_to_c = get_le24(ev->sdu_interval_p_to_c); + event.cis_estab_v2.framing = ev->framing; + + ble_gap_event_listener_call(&event); + if (ble_gap_cis.cb) { + ble_gap_cis.cb(&event, ble_gap_cis.cb_arg); + } +} +#endif /* MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) */ +#endif /* MYNEWT_VAL(BLE_ISO) */ + void ble_gap_rx_adv_report(struct ble_gap_disc_desc *desc) { @@ -2433,7 +2702,7 @@ ble_gap_rx_periodic_adv_sync_transfer(const struct ble_hci_ev_le_subev_periodic_ } #endif -#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) && !MYNEWT_VAL(BLE_ISO) void ble_gap_rx_biginfo_adv_rpt(const struct ble_hci_ev_le_subev_biginfo_adv_report *ev) { @@ -9083,6 +9352,187 @@ ble_gap_event_listener_call(struct ble_gap_event *event) return 0; } +#if MYNEWT_VAL(BLE_ISO) +int +ble_gap_create_cis(uint8_t cis_cnt, const struct ble_hci_le_create_cis_params *params, + ble_gap_event_fn *cb, void *cb_arg) +{ + int rc; + + ble_hs_lock(); + + rc = ble_hs_hci_create_cis(cis_cnt, params); + if (!rc) { + ble_gap_cis.cb = cb; + ble_gap_cis.cb_arg = cb_arg; + } + + ble_hs_unlock(); + + return rc; +} + +int +ble_gap_accept_cis_request(uint16_t cis_handle, ble_gap_event_fn *cb, void *cb_arg) +{ + int rc; + + ble_hs_lock(); + + rc = ble_hs_hci_accept_cis_request(cis_handle); + if (!rc) { + ble_gap_cis.cb = cb; + ble_gap_cis.cb_arg = cb_arg; + } + + ble_hs_unlock(); + + return rc; +} + +int +ble_gap_reject_cis_request(uint16_t cis_handle, uint8_t reason) +{ + int rc; + + ble_hs_lock(); + + rc = ble_hs_hci_reject_cis_request(cis_handle, reason); + + ble_hs_unlock(); + + return rc; +} + +int +ble_gap_iso_disconnect(uint16_t cis_handle, uint8_t reason) +{ + struct ble_hci_lc_disconnect_cp cmd; + int rc; + + ble_hs_lock(); + + cmd.conn_handle = cis_handle; + cmd.reason = reason; + + rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LINK_CTRL, + BLE_HCI_OCF_DISCONNECT_CMD), + &cmd, sizeof(cmd), NULL, 0); + + ble_hs_unlock(); + + return rc; +} + +int +ble_gap_create_big(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, + uint32_t sdu_interval, uint16_t max_sdu, uint16_t max_transport_latency, + uint8_t rtn, uint8_t phy, uint8_t packing, uint8_t framing, + uint8_t encryption, uint8_t broadcast_code[16], + ble_gap_event_fn *cb, void *cb_arg) +{ + int rc; + + ble_hs_lock(); + + rc = ble_hs_hci_create_big(big_handle, adv_handle, num_bis, sdu_interval, + max_sdu, max_transport_latency, rtn, phy, + packing, framing, encryption, broadcast_code); + if (!rc) { + ble_gap_big_brd.cb = cb; + ble_gap_big_brd.cb_arg = cb_arg; + } + + ble_hs_unlock(); + + return rc; +} + +#if MYNEWT_VAL(BLE_ISO_TEST) +int +ble_gap_create_big_test(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, + uint32_t sdu_interval, uint16_t iso_interval, uint8_t nse, + uint16_t max_sdu, uint16_t max_pdu, uint8_t phy, + uint8_t packing, uint8_t framing, uint8_t bn, uint8_t irc, + uint8_t pto, uint8_t encryption, uint8_t broadcast_code[16], + ble_gap_event_fn *cb, void *cb_arg) +{ + int rc; + + ble_hs_lock(); + + rc = ble_hs_hci_create_big_test(big_handle, adv_handle, num_bis, sdu_interval, + iso_interval, nse, max_sdu, max_pdu, phy, + packing, framing, bn, irc, pto, encryption, + broadcast_code); + if (!rc) { + ble_gap_big_brd.cb = cb; + ble_gap_big_brd.cb_arg = cb_arg; + } + + ble_hs_unlock(); + + return rc; +} +#endif /* MYNEWT_VAL(BLE_ISO_TEST) */ + +int +ble_gap_terminate_big(uint8_t big_handle, uint8_t reason) +{ + int rc; + + ble_hs_lock(); + + rc = ble_hs_hci_terminate_big(big_handle, reason); + + ble_hs_unlock(); + + return rc; +} + +int +ble_gap_big_create_sync(uint8_t big_handle, uint16_t sync_handle, + uint8_t encryption, uint8_t broadcast_code[16], + uint8_t mse, uint16_t sync_timeout, + uint8_t num_bis, uint8_t *bis_index, + ble_gap_event_fn *cb, void *cb_arg) +{ + int rc; + + ble_hs_lock(); + + rc = ble_hs_hci_big_create_sync(big_handle, sync_handle, encryption, + broadcast_code, mse, sync_timeout, + num_bis, bis_index); + if (!rc) { + ble_gap_big_snc.cb = cb; + ble_gap_big_snc.cb_arg = cb_arg; + } + + ble_hs_unlock(); + + return rc; +} + +int +ble_gap_big_terminate_sync(uint8_t big_handle) +{ + int rc; + + ble_hs_lock(); + + rc = ble_hs_hci_big_terminate_sync(big_handle); + if (!rc) { + ble_gap_big_snc.cb = NULL; + ble_gap_big_snc.cb_arg = NULL; + } + + ble_hs_unlock(); + + return rc; +} +#endif /* MYNEWT_VAL(BLE_ISO) */ + /***************************************************************************** * $init * *****************************************************************************/ @@ -9103,6 +9553,12 @@ ble_gap_init(void) memset(&ble_gap_sync, 0, sizeof(ble_gap_sync)); #endif +#if MYNEWT_VAL(BLE_ISO) + memset(&ble_gap_cis, 0, sizeof(ble_gap_cis)); + memset(&ble_gap_big_brd, 0, sizeof(ble_gap_big_brd)); + memset(&ble_gap_big_snc, 0, sizeof(ble_gap_big_snc)); +#endif /* MYNEWT_VAL(BLE_ISO) */ + rc = ble_npl_mutex_init(&preempt_done_mutex); if (rc) { diff --git a/nimble/host/src/ble_gap_priv.h b/nimble/host/src/ble_gap_priv.h index a9f029f98..f59c8763e 100644 --- a/nimble/host/src/ble_gap_priv.h +++ b/nimble/host/src/ble_gap_priv.h @@ -88,7 +88,7 @@ void ble_gap_rx_periodic_adv_rpt(const struct ble_hci_ev_le_subev_periodic_adv_r void ble_gap_rx_periodic_adv_sync_lost(const struct ble_hci_ev_le_subev_periodic_adv_sync_lost *ev); void ble_gap_rx_periodic_adv_sync_transfer(const struct ble_hci_ev_le_subev_periodic_adv_sync_transfer *ev); #endif -#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) && !MYNEWT_VAL(BLE_ISO) void ble_gap_rx_biginfo_adv_rpt(const struct ble_hci_ev_le_subev_biginfo_adv_report *ev); #endif void ble_gap_rx_scan_req_rcvd(const struct ble_hci_ev_le_subev_scan_req_rcvd *ev); @@ -110,6 +110,20 @@ void ble_gap_rx_conn_iq_report(const struct ble_hci_ev_le_subev_conn_iq_rpt *ev) void ble_gap_rx_cte_req_failed(const struct ble_hci_ev_le_subev_cte_req_failed *ev); #endif +#if MYNEWT_VAL(BLE_ISO) +void ble_gap_rx_cis_disconn(const struct ble_hci_ev_disconn_cmp *ev); +void ble_gap_rx_cis_estab(const struct ble_hci_ev_le_subev_cis_established *ev); +void ble_gap_rx_cis_request(const struct ble_hci_ev_le_subev_cis_request *ev); +void ble_gap_rx_create_big_comp(const struct ble_hci_ev_le_subev_create_big_complete *ev); +void ble_gap_rx_term_big_comp(const struct ble_hci_ev_le_subev_terminate_big_complete *ev); +void ble_gap_rx_big_sync_estab(const struct ble_hci_ev_le_subev_big_sync_established *ev); +void ble_gap_rx_big_sync_lost(const struct ble_hci_ev_le_subev_big_sync_lost *ev); +void ble_gap_rx_biginfo_adv_rpt(const struct ble_hci_ev_le_subev_biginfo_adv_report *ev); +#if MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) +void ble_gap_rx_cis_estab_v2(const struct ble_hci_ev_le_subev_cis_established_v2 *ev); +#endif /* MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) */ +#endif /* MYNEWT_VAL(BLE_ISO) */ + struct ble_gap_conn_complete { uint8_t status; diff --git a/nimble/host/src/ble_hs.c b/nimble/host/src/ble_hs.c index 2fa0bb553..1afb3b94f 100644 --- a/nimble/host/src/ble_hs.c +++ b/nimble/host/src/ble_hs.c @@ -36,6 +36,10 @@ #include "hci_log/bt_hci_log.h" #endif // (BT_HCI_LOG_INCLUDED == TRUE) +#if MYNEWT_VAL(BLE_ISO) +#include "host/ble_hs_iso.h" +#endif /* MYNEWT_VAL(BLE_ISO) */ + #define BLE_HS_HCI_EVT_COUNT (MYNEWT_VAL(BLE_TRANSPORT_EVT_COUNT) + \ MYNEWT_VAL(BLE_TRANSPORT_EVT_DISCARDABLE_COUNT)) @@ -901,6 +905,14 @@ ble_transport_to_hs_iso_impl(struct os_mbuf *om) return 0; } +#if MYNEWT_VAL(BLE_ISO) +int +ble_transport_to_hs_iso_impl_v2(const uint8_t *data, uint16_t len) +{ + return ble_hs_rx_iso_data(data, len, NULL); +} +#endif /* MYNEWT_VAL(BLE_ISO) */ + void ble_transport_hs_init(void) { diff --git a/nimble/host/src/ble_hs_hci_evt.c b/nimble/host/src/ble_hs_hci_evt.c index 7b410d35f..f2383a416 100644 --- a/nimble/host/src/ble_hs_hci_evt.c +++ b/nimble/host/src/ble_hs_hci_evt.c @@ -26,6 +26,9 @@ #include "ble_hs_priv.h" #include "ble_hs_resolv_priv.h" #include "esp_nimble_mem.h" +#if MYNEWT_VAL(BLE_ISO) +#include "host/ble_hs_iso.h" +#endif /* MYNEWT_VAL(BLE_ISO) */ #if MYNEWT_VAL(BLE_ENABLE_CONN_REATTEMPT) struct ble_gap_reattempt_ctxt { @@ -89,7 +92,7 @@ static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_rpt; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_sync_lost; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_scan_req_rcvd; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_sync_transfer; -#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) && !MYNEWT_VAL(BLE_ISO) static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_biginfo_adv_report; #endif #if MYNEWT_VAL(BLE_POWER_CONTROL) @@ -109,6 +112,19 @@ static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_subev_data_req; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_subev_resp_rep; #endif +#if MYNEWT_VAL(BLE_ISO) +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_cis_estab; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_cis_request; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_create_big_comp; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_term_big_comp; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_big_sync_estab; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_big_sync_lost; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_biginfo_adv_rpt; +#if MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_cis_estab_v2; +#endif /* MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) */ +#endif /* MYNEWT_VAL(BLE_ISO) */ + /* Statistics */ struct host_hci_stats { uint32_t events_rxd; @@ -173,7 +189,7 @@ static ble_hs_hci_evt_le_fn * const ble_hs_hci_evt_le_dispatch[] = { [BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED] = ble_hs_hci_evt_le_adv_set_terminated, [BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD] = ble_hs_hci_evt_le_scan_req_rcvd, [BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_TRANSFER] = ble_hs_hci_evt_le_periodic_adv_sync_transfer, -#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) && !MYNEWT_VAL(BLE_ISO) [BLE_HCI_LE_SUBEV_BIGINFO_ADV_REPORT] = ble_hs_hci_evt_le_biginfo_adv_report, #endif #if MYNEWT_VAL(BLE_POWER_CONTROL) @@ -196,6 +212,18 @@ static ble_hs_hci_evt_le_fn * const ble_hs_hci_evt_le_dispatch[] = { [BLE_HCI_LE_SUBEV_PERIODIC_ADV_RESP_REPORT] = ble_hs_hci_evt_le_periodic_adv_subev_resp_rep, [BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE_V2] = ble_hs_hci_evt_le_enh_conn_complete, #endif +#if MYNEWT_VAL(BLE_ISO) + [BLE_HCI_LE_SUBEV_CIS_ESTABLISHED] = ble_hs_hci_evt_le_cis_estab, + [BLE_HCI_LE_SUBEV_CIS_REQUEST] = ble_hs_hci_evt_le_cis_request, + [BLE_HCI_LE_SUBEV_CREATE_BIG_COMPLETE] = ble_hs_hci_evt_le_create_big_comp, + [BLE_HCI_LE_SUBEV_TERMINATE_BIG_COMPLETE] = ble_hs_hci_evt_le_term_big_comp, + [BLE_HCI_LE_SUBEV_BIG_SYNC_ESTABLISHED] = ble_hs_hci_evt_le_big_sync_estab, + [BLE_HCI_LE_SUBEV_BIG_SYNC_LOST] = ble_hs_hci_evt_le_big_sync_lost, + [BLE_HCI_LE_SUBEV_BIGINFO_ADV_REPORT] = ble_hs_hci_evt_le_biginfo_adv_rpt, +#if MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) + [BLE_HCI_LE_SUBEV_CIS_ESTABLISHED_V2] = ble_hs_hci_evt_le_cis_estab_v2, +#endif /* MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) */ +#endif /* MYNEWT_VAL(BLE_ISO) */ }; #define BLE_HS_HCI_EVT_LE_DISPATCH_SZ \ @@ -228,6 +256,10 @@ ble_hs_hci_evt_le_dispatch_find(uint8_t event_code) return ble_hs_hci_evt_le_dispatch[event_code]; } +#if MYNEWT_VAL(BLE_ISO) +static int ble_hs_hci_evt_le_cis_disconn(const struct ble_hci_ev_disconn_cmp *ev); +#endif /* MYNEWT_VAL(BLE_ISO) */ + #if NIMBLE_BLE_CONNECT static int ble_hs_hci_evt_disconn_complete(uint8_t event_code, const void *data, @@ -317,7 +349,15 @@ ble_hs_hci_evt_disconn_complete(uint8_t event_code, const void *data, } #endif +#if MYNEWT_VAL(BLE_ISO) + if (conn) { + ble_gap_rx_disconn_complete(ev); + } else { + ble_hs_hci_evt_le_cis_disconn(ev); + } +#else /* MYNEWT_VAL(BLE_ISO) */ ble_gap_rx_disconn_complete(ev); +#endif /* MYNEWT_VAL(BLE_ISO) */ /* The connection termination may have freed up some capacity in the * controller for additional ACL data packets. Wake up any stalled @@ -405,6 +445,10 @@ ble_hs_hci_evt_num_completed_pkts(uint8_t event_code, const void *data, } ble_hs_hci_add_avail_pkts(num_pkts); +#if MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) + } else { + ble_hs_hci_add_iso_avail_pkts(le16toh(ev->completed[i].handle), num_pkts); +#endif /* MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) */ } ble_hs_unlock(); } @@ -791,6 +835,189 @@ ble_hs_hci_decode_legacy_type(uint16_t evt_type) } } +#if MYNEWT_VAL(BLE_ISO) +typedef void (*ble_hs_hci_evt_iso_fn)(uint8_t event, const void *data, + unsigned int len, bool le_meta); + +static ble_hs_hci_evt_iso_fn iso_evt_cb; + +int +ble_hs_iso_evt_rx_cb_set(void *cb) +{ + if (cb == NULL) { + return -BLE_HS_EINVAL; + } + + if (iso_evt_cb) { + return -BLE_HS_EALREADY; + } + + iso_evt_cb = cb; + + return 0; +} + +static int +ble_hs_hci_evt_le_cis_disconn(const struct ble_hci_ev_disconn_cmp *ev) +{ + if (iso_evt_cb) { + iso_evt_cb(BLE_HCI_EVCODE_DISCONN_CMP, (const void *)ev, sizeof(*ev), false); + } else { + ble_gap_rx_cis_disconn(ev); + } + + return 0; +} + +static int +ble_hs_hci_evt_le_cis_estab(uint8_t subevent, const void *data, unsigned int len) +{ + const struct ble_hci_ev_le_subev_cis_established *ev = data; + + if (len != sizeof(*ev)) { + return BLE_HS_ECONTROLLER; + } + + if (iso_evt_cb) { + iso_evt_cb(BLE_HCI_LE_SUBEV_CIS_ESTABLISHED, data, len, true); + } else { + ble_gap_rx_cis_estab(ev); + } + + return 0; +} + +static int +ble_hs_hci_evt_le_cis_request(uint8_t subevent, const void *data, unsigned int len) +{ + const struct ble_hci_ev_le_subev_cis_request *ev = data; + + if (len != sizeof(*ev)) { + return BLE_HS_ECONTROLLER; + } + + if (iso_evt_cb) { + iso_evt_cb(BLE_HCI_LE_SUBEV_CIS_REQUEST, data, len, true); + } else { + ble_gap_rx_cis_request(ev); + } + + return 0; +} + +static int +ble_hs_hci_evt_le_create_big_comp(uint8_t subevent, const void *data, unsigned int len) +{ + const struct ble_hci_ev_le_subev_create_big_complete *ev = data; + + if ((len != sizeof(*ev) + ev->num_bis * 2) || + (ev->num_bis > MYNEWT_VAL(BLE_ISO_BIS_PER_BIG))) { + return BLE_HS_ECONTROLLER; + } + + if (iso_evt_cb) { + iso_evt_cb(BLE_HCI_LE_SUBEV_CREATE_BIG_COMPLETE, data, len, true); + } else { + ble_gap_rx_create_big_comp(ev); + } + + return 0; +} + +static int +ble_hs_hci_evt_le_term_big_comp(uint8_t subevent, const void *data, unsigned int len) +{ + const struct ble_hci_ev_le_subev_terminate_big_complete *ev = data; + + if (len != sizeof(*ev)) { + return BLE_HS_ECONTROLLER; + } + + if (iso_evt_cb) { + iso_evt_cb(BLE_HCI_LE_SUBEV_TERMINATE_BIG_COMPLETE, data, len, true); + } else { + ble_gap_rx_term_big_comp(ev); + } + + return 0; +} + +static int +ble_hs_hci_evt_le_big_sync_estab(uint8_t subevent, const void *data, unsigned int len) +{ + const struct ble_hci_ev_le_subev_big_sync_established *ev = data; + + if ((len != sizeof(*ev) + ev->num_bis * 2) || + (ev->num_bis > MYNEWT_VAL(BLE_ISO_BIS_PER_BIG))) { + return BLE_HS_ECONTROLLER; + } + + if (iso_evt_cb) { + iso_evt_cb(BLE_HCI_LE_SUBEV_BIG_SYNC_ESTABLISHED, data, len, true); + } else { + ble_gap_rx_big_sync_estab(ev); + } + + return 0; +} + +static int +ble_hs_hci_evt_le_big_sync_lost(uint8_t subevent, const void *data, unsigned int len) +{ + const struct ble_hci_ev_le_subev_big_sync_lost *ev = data; + + if (len != sizeof(*ev)) { + return BLE_HS_ECONTROLLER; + } + + if (iso_evt_cb) { + iso_evt_cb(BLE_HCI_LE_SUBEV_BIG_SYNC_LOST, data, len, true); + } else { + ble_gap_rx_big_sync_lost(ev); + } + + return 0; +} + +static int +ble_hs_hci_evt_le_biginfo_adv_rpt(uint8_t subevent, const void *data, unsigned int len) +{ + const struct ble_hci_ev_le_subev_biginfo_adv_report *ev = data; + + if (len != sizeof(*ev)) { + return BLE_HS_ECONTROLLER; + } + + if (iso_evt_cb) { + iso_evt_cb(BLE_HCI_LE_SUBEV_BIGINFO_ADV_REPORT, data, len, true); + } else { + ble_gap_rx_biginfo_adv_rpt(ev); + } + + return 0; +} + +#if MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) +static int +ble_hs_hci_evt_le_cis_estab_v2(uint8_t subevent, const void *data, unsigned int len) +{ + const struct ble_hci_ev_le_subev_cis_established_v2 *ev = data; + + if (len != sizeof(*ev)) { + return BLE_HS_ECONTROLLER; + } + + if (iso_evt_cb) { + iso_evt_cb(BLE_HCI_LE_SUBEV_CIS_ESTABLISHED_V2, data, len, true); + } else { + ble_gap_rx_cis_estab_v2(ev); + } + + return 0; +} +#endif /* MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) */ +#endif /* MYNEWT_VAL(BLE_ISO) */ + static int ble_hs_hci_evt_le_ext_adv_rpt_first_pass(const void *data, unsigned int len) { @@ -998,7 +1225,7 @@ ble_hs_hci_evt_le_periodic_adv_sync_transfer(uint8_t subevent, const void *data, return 0; } -#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) && !MYNEWT_VAL(BLE_ISO) static int ble_hs_hci_evt_le_biginfo_adv_report(uint8_t subevent, const void *data, unsigned int len) diff --git a/nimble/host/src/ble_hs_hci_util.c b/nimble/host/src/ble_hs_hci_util.c index 573b44252..f36507859 100644 --- a/nimble/host/src/ble_hs_hci_util.c +++ b/nimble/host/src/ble_hs_hci_util.c @@ -360,3 +360,20 @@ ble_hs_hci_dtm_enh_rx_start(const uint8_t rx_chan, const uint8_t index, return ble_hs_hci_cmd_tx_no_rsp(opcode, &cmd, sizeof(cmd)); } + +int +ble_hs_hci_set_host_feature(uint8_t bit_num, uint8_t bit_val) +{ + struct ble_hci_le_set_host_feature_cp cmd; + + if (bit_val > 0x01) { + return BLE_HS_EINVAL; + } + + cmd.bit_num = bit_num; + cmd.bit_val = bit_val; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_SET_HOST_FEATURE), + &cmd, sizeof(cmd), NULL, 0); +} diff --git a/nimble/host/src/ble_hs_iso.c b/nimble/host/src/ble_hs_iso.c new file mode 100644 index 000000000..4eff61012 --- /dev/null +++ b/nimble/host/src/ble_hs_iso.c @@ -0,0 +1,252 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include "os/os.h" +#include "mem/mem.h" +#include "nimble/hci_common.h" +#include "host/ble_hs_hci.h" +#include "ble_hs_priv.h" +#include "bt_osi_mem.h" +#include "host/ble_hs_iso.h" + +#if MYNEWT_VAL(BLE_ISO) + +_Static_assert((MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) && + MYNEWT_VAL(BLE_ISO_NON_STD_FLOW_CTRL)) == 0, + "Only one of standard or non-standard can be enabled"); + +#define BLE_HCI_ISO_PB_FIRST_FRAG 0 +#define BLE_HCI_ISO_PB_CONT_FRAG 1 +#define BLE_HCI_ISO_PB_COMP_SDU 2 +#define BLE_HCI_ISO_PB_LAST_FRAG 3 + +#define BLE_HCI_ISO_DATA_HDR_SZ 4 +#define BLE_HCI_ISO_DATA_LOAD_TS_SZ 4 +#define BLE_HCI_ISO_DATA_LOAD_HDR_SZ 4 + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +static uint16_t ble_hs_iso_buf_sz; +static uint8_t ble_hs_iso_max_pkts; + +#if MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) +/* Number of available ISO transmit buffers on the controller. + * It must only be accessed while the host mutex is locked. + */ +static uint16_t ble_hs_iso_avail_pkts; +#endif /* MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) */ + +int +ble_hs_hci_set_iso_buf_sz(uint16_t pktlen, uint16_t max_pkts) +{ + if (pktlen == 0 || max_pkts == 0) { + return BLE_HS_EINVAL; + } + + ble_hs_iso_buf_sz = pktlen; + ble_hs_iso_max_pkts = max_pkts; + +#if MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) + ble_hs_iso_avail_pkts = max_pkts; +#endif /* MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) */ + + BLE_HS_LOG(INFO, "ISO Flow Control:"); + BLE_HS_LOG(INFO, " Length: %u", pktlen); + BLE_HS_LOG(INFO, " Count: %u", max_pkts); + BLE_HS_LOG(INFO, " Status: "); + if (MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL)) { + BLE_HS_LOG(INFO, "%s", "Standard"); + } else if (MYNEWT_VAL(BLE_ISO_NON_STD_FLOW_CTRL)) { + BLE_HS_LOG(INFO, "%s", "Non-standard"); + } else { + BLE_HS_LOG(INFO, "%s", "Not support"); + } + + return 0; +} + +#if MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) +void +ble_hs_hci_add_iso_avail_pkts(uint16_t conn_handle, uint16_t delta) +{ + BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task()); + + if (ble_hs_iso_avail_pkts + delta > ble_hs_iso_max_pkts) { + BLE_HS_LOG(ERROR, "ISO_HS_RESET %u %u %u", ble_hs_iso_avail_pkts, delta, ble_hs_iso_max_pkts); + ble_hs_sched_reset(BLE_HS_ECONTROLLER); + } else { + ble_hs_iso_avail_pkts += delta; + } +} +#endif /* MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) */ + +#if MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) +static uint8_t +ble_hs_hci_iso_buf_needed(uint16_t sdu_len, bool ts_flag) +{ + uint16_t sdu_offset; + uint16_t dl_len; + uint8_t dlh_len; + uint8_t count; + + dlh_len = (ts_flag ? BLE_HCI_ISO_DATA_LOAD_TS_SZ : 0) + BLE_HCI_ISO_DATA_LOAD_HDR_SZ; + sdu_offset = 0; + count = 1; /* 1 extra since framed pdu may be used */ + + while (1) { + dl_len = min(dlh_len + sdu_len - sdu_offset, ble_hs_iso_buf_sz); + + count += 1; + + sdu_offset += dl_len - dlh_len; + assert(sdu_offset <= sdu_len); + + if (sdu_offset == sdu_len) { + break; + } + + /* No data load header for continuation/last segment */ + dlh_len = 0; + } + + return count; +} +#endif /* MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) */ + +static void +ble_hs_hci_iso_hdr_append(uint16_t conn_handle, uint16_t sdu_len, + bool ts_flag, uint32_t time_stamp, + uint16_t pkt_seq_num, uint8_t *frag, + uint8_t pb_flag, uint16_t dl_len) +{ + uint32_t pkt_hdr; + uint32_t dl_hdr; + + pkt_hdr = ((pb_flag << 12) | conn_handle); + if (pb_flag == BLE_HCI_ISO_PB_FIRST_FRAG || + pb_flag == BLE_HCI_ISO_PB_COMP_SDU) { + pkt_hdr |= (ts_flag << 14); + } + pkt_hdr |= (dl_len << 16); + + memcpy(frag, &pkt_hdr, BLE_HCI_ISO_DATA_HDR_SZ); + + /* No data load header for continuation/last segment */ + if (pb_flag == BLE_HCI_ISO_PB_CONT_FRAG || + pb_flag == BLE_HCI_ISO_PB_LAST_FRAG) { + return; + } + + if (ts_flag) { + memcpy(frag + BLE_HCI_ISO_DATA_HDR_SZ, &time_stamp, BLE_HCI_ISO_DATA_LOAD_TS_SZ); + } + + dl_hdr = (sdu_len << 16) | pkt_seq_num; + + memcpy(frag + BLE_HCI_ISO_DATA_HDR_SZ + (ts_flag ? BLE_HCI_ISO_DATA_LOAD_TS_SZ : 0), + &dl_hdr, BLE_HCI_ISO_DATA_LOAD_HDR_SZ); +} + +static int +ble_hs_hci_iso_tx_now(uint16_t conn_handle, const uint8_t *sdu, uint16_t sdu_len, + bool ts_flag, uint32_t time_stamp, uint16_t pkt_seq_num) +{ + uint8_t dlh_len; + uint8_t *frag; + int rc; + +#if MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) + /* Get the Controller ISO buffer needed for the SDU */ + uint8_t count = ble_hs_hci_iso_buf_needed(sdu_len, ts_flag); + + /* Make sure the Controller ISO buffer can accommodate the SDU completely */ + if (count > ble_hs_iso_avail_pkts) { + BLE_HS_LOG(WARN, "ISO flow control(%u/%u)!", count, ble_hs_iso_avail_pkts); + return BLE_HS_EAGAIN; + } +#elif MYNEWT_VAL(BLE_ISO_NON_STD_FLOW_CTRL) + extern uint16_t ble_ll_iso_free_buf_num_get(uint16_t conn_handle); + if (ble_ll_iso_free_buf_num_get(conn_handle) == 0) { + BLE_HS_LOG(WARN, "ISO flow control!"); + return BLE_HS_EAGAIN; + } +#endif + + dlh_len = (ts_flag ? BLE_HCI_ISO_DATA_LOAD_TS_SZ : 0) + BLE_HCI_ISO_DATA_LOAD_HDR_SZ; + + frag = malloc(BLE_HCI_ISO_DATA_HDR_SZ + dlh_len + sdu_len); + if (frag == NULL) { + return BLE_HS_ENOMEM; + } + + ble_hs_hci_iso_hdr_append(conn_handle, sdu_len, ts_flag, time_stamp, pkt_seq_num, + frag, BLE_HCI_ISO_PB_COMP_SDU, dlh_len + sdu_len); + + memcpy(frag + BLE_HCI_ISO_DATA_HDR_SZ + dlh_len, sdu, sdu_len); + + extern int ble_hci_trans_hs_iso_tx(const uint8_t *data, uint16_t length, void *arg); + rc = ble_hci_trans_hs_iso_tx(frag, BLE_HCI_ISO_DATA_HDR_SZ + dlh_len + sdu_len, NULL); + if (rc) { + return BLE_HS_EDONE; + } + +#if MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) + /* If an ISO SDU is fragmented into fragments, flow control is not supported. + * + * Currently even if an SDU is larger than the ISO buffer size, fragmentation + * will not happen here, the SDU will be posted to Controller completely. + */ + ble_hs_iso_avail_pkts -= 1; +#endif /* MYNEWT_VAL(BLE_ISO_STD_FLOW_CTRL) */ + + return 0; +} + +int +ble_hs_hci_iso_tx(uint16_t conn_handle, const uint8_t *sdu, uint16_t sdu_len, + bool ts_flag, uint32_t time_stamp, uint16_t pkt_seq_num) +{ + BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task()); + + return ble_hs_hci_iso_tx_now(conn_handle, sdu, sdu_len, ts_flag, time_stamp, pkt_seq_num); +} + +static ble_hs_iso_pkt_rx_fn ble_hs_iso_pkt_rx_cb; + +int +ble_hs_iso_pkt_rx_cb_set(void *cb) +{ + if (cb == NULL) { + return -BLE_HS_EINVAL; + } + + if (ble_hs_iso_pkt_rx_cb) { + return -BLE_HS_EALREADY; + } + + ble_hs_iso_pkt_rx_cb = cb; + + return 0; +} + +int +ble_hs_rx_iso_data(const uint8_t *data, uint16_t len, void *arg) +{ + if (ble_hs_iso_pkt_rx_cb) { + ble_hs_iso_pkt_rx_cb(data, len, arg); + } + + /* The `data` is dynamically allocated by Controller, free it here */ + bt_osi_mem_free((void *)data); + + return 0; +} + +#endif /* MYNEWT_VAL(BLE_ISO) */ diff --git a/nimble/host/src/ble_hs_iso_hci.c b/nimble/host/src/ble_hs_iso_hci.c new file mode 100644 index 000000000..871b9057e --- /dev/null +++ b/nimble/host/src/ble_hs_iso_hci.c @@ -0,0 +1,639 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "os/os.h" +#include "mem/mem.h" +#include "nimble/hci_common.h" +#include "host/ble_hs_hci.h" +#include "ble_hs_priv.h" + +#if MYNEWT_VAL(BLE_ISO) +int +ble_hs_hci_iso_disconnect(uint16_t cis_handle, uint8_t reason) +{ + struct ble_hci_lc_disconnect_cp cmd; + + cmd.conn_handle = htole16(cis_handle); + cmd.reason = reason; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LINK_CTRL, + BLE_HCI_OCF_DISCONNECT_CMD), + &cmd, sizeof(cmd), NULL, 0); +} + +int +ble_hs_hci_read_local_supp_codec(uint8_t *rsp_buf, uint8_t rsp_len) +{ + if (rsp_buf == NULL || rsp_len < 2) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_INFO_PARAMS, + BLE_HCI_OCF_IP_RD_LOCAL_SUPP_CODEC), + NULL, 0, rsp_buf, rsp_len); +} + +int +ble_hs_hci_read_local_supp_codec_caps(uint8_t coding_fmt, uint16_t company_id, + uint16_t vs_codec_id, uint8_t logical_transport_type, + uint8_t direction, uint8_t *rsp_buf, uint8_t rsp_len) +{ + struct ble_hci_ip_rd_local_supp_codec_caps_cp cmd; + + if (rsp_buf == NULL || rsp_len == 0) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd.coding_fmt = coding_fmt; + cmd.company_id = company_id; + cmd.vs_codec_id = vs_codec_id; + cmd.logical_tpt_type = logical_transport_type; + cmd.direction = direction; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_INFO_PARAMS, + BLE_HCI_OCF_IP_RD_LOCAL_SUPP_CODEC_CAPS), + &cmd, sizeof(cmd), rsp_buf, rsp_len); +} + +int +ble_hs_hci_read_local_supp_controller_delay(uint8_t coding_fmt, uint16_t company_id, + uint16_t vs_codec_id, uint8_t logical_transport_type, + uint8_t direction, + uint8_t codec_cfg_len, uint8_t *codec_cfg, + uint32_t *min_controller_delay, + uint32_t *max_controller_delay) +{ + struct ble_hci_ip_rd_local_supp_controller_delay_cp cmd; + struct ble_hci_ip_rd_local_supp_controller_delay_rp rsp; + int rc; + + if ((codec_cfg_len && codec_cfg == NULL) || + min_controller_delay == NULL || + max_controller_delay == NULL) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd.coding_fmt = coding_fmt; + cmd.company_id = company_id; + cmd.vs_codec_id = vs_codec_id; + cmd.logical_tpt_type = logical_transport_type; + cmd.direction = direction; + cmd.codec_cfg_len = codec_cfg_len; + /* TODO: Use Codec Configuration */ + + rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_INFO_PARAMS, + BLE_HCI_OCF_IP_RD_LOCAL_SUPP_CONTROLLER_DELAY), + &cmd, sizeof(cmd), &rsp, sizeof(rsp)); + if (rc) { + return rc; + } + + memcpy(min_controller_delay, rsp.min_controller_delay, sizeof(rsp.min_controller_delay)); + memcpy(max_controller_delay, rsp.max_controller_delay, sizeof(rsp.max_controller_delay)); + + return 0; +} + +int +ble_hs_hci_cfg_data_path(uint8_t data_path_direction, uint8_t data_path_id, + uint8_t vs_cfg_len, uint8_t *vs_cfg) +{ + struct ble_hci_cb_cfg_data_path_cp cmd; + + if (vs_cfg_len && vs_cfg == NULL) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd.data_path_dir = data_path_direction; + cmd.data_path_id = data_path_id; + cmd.vs_cfg_len = vs_cfg_len; + /* TODO: Use Vendor-Specific Configuration */ + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_CTLR_BASEBAND, + BLE_HCI_OCF_CB_CFG_DATA_PATH), + &cmd, sizeof(cmd), NULL, 0); +} + +int +ble_hs_hci_read_buf_sz_v2(uint16_t *data_len, uint8_t *data_packets, + uint16_t *iso_data_len, uint8_t *iso_data_packets) +{ + struct ble_hci_le_rd_buf_size_v2_rp rsp; + int rc; + + if (data_len == NULL || data_packets == NULL || + iso_data_len == NULL || iso_data_packets == NULL) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_RD_BUF_SIZE_V2), + NULL, 0, &rsp, sizeof(rsp)); + if (rc) { + return rc; + } + + *data_len = rsp.data_len; + *data_packets = rsp.data_packets; + *iso_data_len = rsp.iso_data_len; + *iso_data_packets = rsp.iso_data_packets; + + return 0; +} + +int +ble_hs_hci_read_iso_tx_sync(uint16_t conn_handle, uint16_t *packet_seq_num, + uint32_t *timestamp, uint32_t *timeoffset) +{ + struct ble_hci_le_read_iso_tx_sync_cp cmd; + struct ble_hci_le_read_iso_tx_sync_rp rsp; + int rc; + + if (packet_seq_num == NULL || timestamp == NULL || timeoffset == NULL) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd.conn_handle = htole16(conn_handle); + + rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_READ_ISO_TX_SYNC), + &cmd, sizeof(cmd), &rsp, sizeof(rsp)); + if (rc) { + return rc; + } + + if (le16toh(rsp.conn_handle) != conn_handle) { + return BLE_HS_ECONTROLLER; + } + + *packet_seq_num = rsp.packet_seq_num; + *timestamp = rsp.tx_timestamp; + memcpy(timeoffset, rsp.time_offset, sizeof(rsp.time_offset)); + + return 0; +} + +int +ble_hs_hci_set_cig_params(uint8_t cig_id, uint32_t sdu_interval_c_to_p, uint32_t sdu_interval_p_to_c, + uint8_t slaves_clock_accuracy, uint8_t packing, uint8_t framing, + uint16_t max_transport_latency_c_to_p, uint16_t max_transport_latency_p_to_c, + uint8_t cis_cnt, struct ble_hci_le_cis_params *cis_params, + uint8_t *rsp_buf, uint8_t rsp_len) +{ + struct ble_hci_le_set_cig_params_cp *cmd; + struct ble_hci_le_cis_params *cis_param; + uint8_t cmd_buf[sizeof(*cmd) + cis_cnt * sizeof(*cis_params)]; + uint8_t cmd_len = sizeof(*cmd); + + if (cis_cnt == 0 || cis_params == NULL) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (rsp_buf == NULL || + rsp_len < sizeof(struct ble_hci_le_set_cig_params_rp) + cis_cnt * 2) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd = (void *)cmd_buf; + cis_param = cmd->cis; + + cmd->cig_id = cig_id; + memcpy(cmd->sdu_interval_c_to_p, &sdu_interval_c_to_p, sizeof(cmd->sdu_interval_c_to_p)); + memcpy(cmd->sdu_interval_p_to_c, &sdu_interval_p_to_c, sizeof(cmd->sdu_interval_p_to_c)); + cmd->worst_sca = slaves_clock_accuracy; + cmd->packing = packing; + cmd->framing = framing; + cmd->max_latency_c_to_p = max_transport_latency_c_to_p; + cmd->max_latency_p_to_c = max_transport_latency_p_to_c; + cmd->cis_count = cis_cnt; + + for (size_t i = 0; i < cis_cnt; i++) { + cis_param->cis_id = cis_params[i].cis_id; + cis_param->max_sdu_c_to_p = cis_params[i].max_sdu_c_to_p; + cis_param->max_sdu_p_to_c = cis_params[i].max_sdu_p_to_c; + cis_param->phy_c_to_p = cis_params[i].phy_c_to_p; + cis_param->phy_p_to_c = cis_params[i].phy_p_to_c; + cis_param->rtn_c_to_p = cis_params[i].rtn_c_to_p; + cis_param->rtn_p_to_c = cis_params[i].rtn_p_to_c; + + cmd_len += sizeof(*cis_param); + cis_param++; + } + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_SET_CIG_PARAMS), + cmd, cmd_len, rsp_buf, rsp_len); +} + +#if MYNEWT_VAL(BLE_ISO_TEST) +int +ble_hs_hci_set_cig_params_test(uint8_t cig_id, uint32_t sdu_interval_c_to_p, uint32_t sdu_interval_p_to_c, + uint8_t ft_c_to_p, uint8_t ft_p_to_c, uint16_t iso_interval, + uint8_t slaves_clock_accuracy, uint8_t packing, uint8_t framing, + uint8_t cis_cnt, struct ble_hci_le_cis_params_test *cis_params, + uint8_t *rsp_buf, uint8_t rsp_len) +{ + struct ble_hci_le_set_cig_params_test_cp *cmd; + struct ble_hci_le_cis_params_test *cis_param; + uint8_t cmd_buf[sizeof(*cmd) + cis_cnt * sizeof(*cis_params)]; + uint8_t cmd_len = sizeof(*cmd); + + if (cis_cnt == 0 || cis_params == NULL) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (rsp_buf == NULL|| + rsp_len < sizeof(struct ble_hci_le_set_cig_params_rp) + cis_cnt * 2) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd = (void *)cmd_buf; + cis_param = cmd->cis; + + cmd->cig_id = cig_id; + memcpy(cmd->sdu_interval_c_to_p, &sdu_interval_c_to_p, sizeof(cmd->sdu_interval_c_to_p)); + memcpy(cmd->sdu_interval_p_to_c, &sdu_interval_p_to_c, sizeof(cmd->sdu_interval_p_to_c)); + cmd->ft_c_to_p = ft_c_to_p; + cmd->ft_p_to_c = ft_p_to_c; + cmd->iso_interval = iso_interval; + cmd->worst_sca = slaves_clock_accuracy; + cmd->packing = packing; + cmd->framing = framing; + cmd->cis_count = cis_cnt; + + for (size_t i = 0; i < cis_cnt; i++) { + cis_param->cis_id = cis_params[i].cis_id; + cis_param->nse = cis_params[i].nse; + cis_param->max_sdu_c_to_p = cis_params[i].max_sdu_c_to_p; + cis_param->max_sdu_p_to_c = cis_params[i].max_sdu_p_to_c; + cis_param->max_pdu_c_to_p = cis_params[i].max_pdu_c_to_p; + cis_param->max_pdu_p_to_c = cis_params[i].max_pdu_p_to_c; + cis_param->phy_c_to_p = cis_params[i].phy_c_to_p; + cis_param->phy_p_to_c = cis_params[i].phy_p_to_c; + cis_param->bn_c_to_p = cis_params[i].bn_c_to_p; + cis_param->bn_p_to_c = cis_params[i].bn_p_to_c; + + cmd_len += sizeof(*cis_param); + cis_param++; + } + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_SET_CIG_PARAMS_TEST), + cmd_buf, cmd_len, rsp_buf, rsp_len); +} +#endif /* MYNEWT_VAL(BLE_ISO_TEST) */ + +int +ble_hs_hci_create_cis(uint8_t cis_cnt, const struct ble_hci_le_create_cis_params *params) +{ + struct ble_hci_le_create_cis_cp *cmd; + struct ble_hci_le_create_cis_params *param; + uint8_t cmd_buf[sizeof(*cmd) + cis_cnt * sizeof(*params)]; + uint8_t cmd_len = sizeof(*cmd); + + if (cis_cnt == 0 || params == NULL) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd = (void *)cmd_buf; + param = cmd->cis; + + cmd->cis_count = cis_cnt; + + for (size_t i = 0; i < cis_cnt; i++) { + param->cis_handle = params[i].cis_handle; + param->conn_handle = params[i].conn_handle; + + cmd_len += sizeof(*param); + param++; + } + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_CREATE_CIS), + cmd_buf, cmd_len, NULL, 0); +} + +int +ble_hs_hci_remove_cig(uint8_t cig_id) +{ + struct ble_hci_le_remove_cig_cp cmd; + struct ble_hci_le_remove_cig_rp rsp; + + cmd.cig_id = cig_id; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_REMOVE_CIG), + &cmd, sizeof(cmd), &rsp, sizeof(rsp)); +} + +int +ble_hs_hci_accept_cis_request(uint16_t cis_handle) +{ + struct ble_hci_le_accept_cis_request_cp cmd; + + cmd.conn_handle = cis_handle; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_ACCEPT_CIS_REQ), + &cmd, sizeof(cmd), NULL, 0); +} + +int +ble_hs_hci_reject_cis_request(uint16_t cis_handle, uint8_t reason) +{ + struct ble_hci_le_reject_cis_request_cp cmd; + + cmd.conn_handle = cis_handle; + cmd.reason = reason; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_REJECT_CIS_REQ), + &cmd, sizeof(cmd), NULL, 0); +} + +int +ble_hs_hci_create_big(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, + uint32_t sdu_interval, uint16_t max_sdu, + uint16_t max_transport_latency, uint8_t rtn, uint8_t phy, + uint8_t packing, uint8_t framing, uint8_t encryption, + uint8_t broadcast_code[16]) +{ + struct ble_hci_le_create_big_cp cmd; + + if (num_bis == 0 || (encryption && broadcast_code == NULL)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd.big_handle = big_handle; + cmd.adv_handle = adv_handle; + cmd.num_bis = num_bis; + memcpy(cmd.sdu_interval, &sdu_interval, sizeof(cmd.sdu_interval)); + cmd.max_sdu = max_sdu; + cmd.max_transport_latency = max_transport_latency; + cmd.rtn = rtn; + cmd.phy = phy; + cmd.packing = packing; + cmd.framing = framing; + cmd.encryption = encryption; + if (encryption) { + memcpy(cmd.broadcast_code, broadcast_code, sizeof(cmd.broadcast_code)); + } + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_CREATE_BIG), + &cmd, sizeof(cmd), NULL, 0); +} + +#if MYNEWT_VAL(BLE_ISO_TEST) +int +ble_hs_hci_create_big_test(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, + uint32_t sdu_interval, uint16_t iso_interval, uint8_t nse, + uint16_t max_sdu, uint16_t max_pdu, uint8_t phy, + uint8_t packing, uint8_t framing, uint8_t bn, uint8_t irc, + uint8_t pto, uint8_t encryption, uint8_t broadcast_code[16]) +{ + struct ble_hci_le_create_big_test_cp cmd; + + if (num_bis == 0 || (encryption && broadcast_code == NULL)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd.big_handle = big_handle; + cmd.adv_handle = adv_handle; + cmd.num_bis = num_bis; + memcpy(cmd.sdu_interval, &sdu_interval, sizeof(cmd.sdu_interval)); + cmd.iso_interval = iso_interval; + cmd.nse = nse; + cmd.max_sdu = max_sdu; + cmd.max_pdu = max_pdu; + cmd.phy = phy; + cmd.packing = packing; + cmd.framing = framing; + cmd.bn = bn; + cmd.irc = irc; + cmd.pto = pto; + cmd.encryption = encryption; + if (encryption) { + memcpy(cmd.broadcast_code, broadcast_code, sizeof(cmd.broadcast_code)); + } + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_CREATE_BIG_TEST), + &cmd, sizeof(cmd), NULL, 0); +} +#endif /* MYNEWT_VAL(BLE_ISO_TEST) */ + +int +ble_hs_hci_terminate_big(uint8_t big_handle, uint8_t reason) +{ + struct ble_hci_le_terminate_big_cp cmd; + + cmd.big_handle = big_handle; + cmd.reason = reason; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_TERMINATE_BIG), + &cmd, sizeof(cmd), NULL, 0); +} + +int +ble_hs_hci_big_create_sync(uint8_t big_handle, uint16_t sync_handle, + uint8_t encryption, uint8_t broadcast_code[16], + uint8_t mse, uint16_t sync_timeout, + uint8_t num_bis, uint8_t *bis_index) +{ + struct ble_hci_le_big_create_sync_cp *cmd; + uint8_t cmd_buf[sizeof(*cmd) + MYNEWT_VAL(BLE_ISO_BIS_PER_BIG)]; + + if ((encryption && broadcast_code == NULL) || num_bis == 0 || bis_index == NULL) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd = (void *)cmd_buf; + + cmd->big_handle = big_handle; + cmd->sync_handle = sync_handle; + cmd->encryption = encryption; + if (encryption) { + memcpy(cmd->broadcast_code, broadcast_code, 16); + } + cmd->mse = mse; + cmd->sync_timeout = sync_timeout; + cmd->num_bis = num_bis; + memcpy(cmd->bis, bis_index, num_bis); + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_BIG_CREATE_SYNC), + cmd, sizeof(*cmd) + num_bis, NULL, 0); +} + +int +ble_hs_hci_big_terminate_sync(uint8_t big_handle) +{ + struct ble_hci_le_big_terminate_sync_cp cmd; + + cmd.big_handle = big_handle; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_BIG_TERMINATE_SYNC), + &cmd, sizeof(cmd), NULL, 0); +} + +int +ble_hs_hci_setup_iso_data_path(uint16_t conn_handle, uint8_t data_path_direction, + uint8_t data_path_id, uint8_t coding_fmt, + uint16_t company_id, uint16_t vs_codec_id, + uint32_t controller_delay, uint8_t codec_cfg_len, + uint8_t *codec_cfg) +{ + struct ble_hci_le_setup_iso_data_path_cp cmd; + + if (codec_cfg_len && codec_cfg == NULL) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd.conn_handle = conn_handle; + cmd.data_path_dir = data_path_direction; + cmd.data_path_id = data_path_id; + cmd.codec_id[0] = coding_fmt; + put_le16(cmd.codec_id + 1, company_id); + put_le16(cmd.codec_id + 3, vs_codec_id); + memcpy(cmd.controller_delay, &controller_delay, sizeof(cmd.controller_delay)); + cmd.codec_config_len = codec_cfg_len; + /* TODO: Codec Configuration */ + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_SETUP_ISO_DATA_PATH), + &cmd, sizeof(cmd), NULL, 0); +} + +int +ble_hs_hci_remove_iso_data_path(uint16_t conn_handle, uint8_t data_path_direction) +{ + struct ble_hci_le_remove_iso_data_path_cp cmd; + + cmd.conn_handle = conn_handle; + cmd.data_path_dir = data_path_direction; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_REMOVE_ISO_DATA_PATH), + &cmd, sizeof(cmd), NULL, 0); +} + +#if MYNEWT_VAL(BLE_ISO_TEST) +int +ble_hs_hci_iso_transmit_test(uint16_t conn_handle, uint8_t payload_type) +{ + struct ble_hci_le_iso_transmit_test_cp cmd; + + cmd.conn_handle = conn_handle; + cmd.payload_type = payload_type; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_ISO_TRANSMIT_TEST), + &cmd, sizeof(cmd), NULL, 0); +} + +int +ble_hs_hci_iso_receive_test(uint16_t conn_handle, uint8_t payload_type) +{ + struct ble_hci_le_iso_receive_test_cp cmd; + + cmd.conn_handle = conn_handle; + cmd.payload_type = payload_type; + + return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_ISO_RECEIVE_TEST), + &cmd, sizeof(cmd), NULL, 0); +} + +int +ble_hs_hci_iso_read_test_counters(uint16_t conn_handle, uint32_t *received_sdu_count, + uint32_t *missed_sdu_count, uint32_t *failed_sdu_count) +{ + struct ble_hci_le_iso_read_test_counters_cp cmd; + struct ble_hci_le_iso_read_test_counters_rp rsp; + int rc; + + cmd.conn_handle = conn_handle; + + rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_ISO_READ_TEST_COUNTERS), + &cmd, sizeof(cmd), &rsp, sizeof(rsp)); + if (rc) { + return rc; + } + + *received_sdu_count = rsp.received_sdu_count; + *missed_sdu_count = rsp.missed_sdu_count; + *failed_sdu_count = rsp.failed_sdu_count; + + return 0; +} + +int +ble_hs_hci_iso_test_end(uint16_t conn_handle, uint32_t *received_sdu_count, + uint32_t *missed_sdu_count, uint32_t *failed_sdu_count) +{ + struct ble_hci_le_iso_test_end_cp cmd; + struct ble_hci_le_iso_test_end_rp rsp; + int rc; + + cmd.conn_handle = conn_handle; + + rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_ISO_TEST_END), + &cmd, sizeof(cmd), &rsp, sizeof(rsp)); + if (rc) { + return rc; + } + + *received_sdu_count = rsp.received_sdu_count; + *missed_sdu_count = rsp.missed_sdu_count; + *failed_sdu_count = rsp.failed_sdu_count; + + return 0; +} +#endif /* MYNEWT_VAL(BLE_ISO_TEST) */ + +int +ble_hs_hci_read_iso_link_quality(uint16_t conn_handle, uint32_t *tx_unacked_pkts, + uint32_t *tx_flushed_pkts, uint32_t *tx_last_subev_pkts, + uint32_t *retransmitted_pkts, uint32_t *crc_error_pkts, + uint32_t *rx_unreceived_pkts, uint32_t *duplicate_pkts) +{ + struct ble_hci_le_read_iso_link_quality_cp cmd; + struct ble_hci_le_read_iso_link_quality_rp rsp; + int rc; + + if (!tx_unacked_pkts || !tx_flushed_pkts || + !tx_last_subev_pkts || !retransmitted_pkts || + !crc_error_pkts || !rx_unreceived_pkts || + !duplicate_pkts) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd.conn_handle = conn_handle; + + rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_READ_ISO_LINK_QUALITY), + &cmd, sizeof(cmd), &rsp, sizeof(rsp)); + if (rc) { + return rc; + } + + *tx_unacked_pkts = rsp.tx_unacked_pkts; + *tx_flushed_pkts = rsp.tx_flushed_pkts; + *tx_last_subev_pkts = rsp.tx_last_subevent_pkts; + *retransmitted_pkts = rsp.retransmitted_pkts; + *crc_error_pkts = rsp.crc_error_pkts; + *rx_unreceived_pkts = rsp.rx_unreceived_pkts; + *duplicate_pkts = rsp.duplicate_pkts; + + return 0; +} +#endif /* MYNEWT_VAL(BLE_ISO) */ diff --git a/nimble/host/src/ble_hs_startup.c b/nimble/host/src/ble_hs_startup.c index ff3ad0552..8960ac39e 100644 --- a/nimble/host/src/ble_hs_startup.c +++ b/nimble/host/src/ble_hs_startup.c @@ -22,6 +22,9 @@ #include "host/ble_hs.h" #include "host/ble_hs_hci.h" #include "ble_hs_priv.h" +#if MYNEWT_VAL(BLE_ISO) +#include "host/ble_hs_iso.h" +#endif /* MYNEWT_VAL(BLE_ISO) */ #if !MYNEWT_VAL(BLE_CONTROLLER) static int @@ -107,6 +110,33 @@ ble_hs_startup_le_read_sup_f_tx(void) } #if MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_PERIPHERAL) +#if MYNEWT_VAL(BLE_ISO) +static int +ble_hs_startup_le_read_buf_sz_v2_tx(uint16_t *out_acl_pktlen, uint8_t *out_max_acl_pkts, + uint16_t *out_iso_pktlen, uint8_t *out_max_iso_pkts) +{ + struct ble_hci_le_rd_buf_size_v2_rp rsp; + int rc; + + rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_RD_BUF_SIZE_V2), NULL, 0, + &rsp, sizeof(rsp)); + if (rc != 0) { + return rc; + } + + *out_acl_pktlen = le16toh(rsp.data_len); + *out_max_acl_pkts = rsp.data_packets; + *out_iso_pktlen = le16toh(rsp.iso_data_len); + *out_max_iso_pkts = rsp.iso_data_packets; + + BLE_HS_LOG(INFO, "LE Read Buffer Size v2: %u %u %u %u", + *out_acl_pktlen, *out_max_acl_pkts, + *out_iso_pktlen, *out_max_iso_pkts); + + return 0; +} +#else /* MYNEWT_VAL(BLE_ISO) */ static int ble_hs_startup_le_read_buf_sz_tx(uint16_t *out_pktlen, uint8_t *out_max_pkts) { @@ -125,6 +155,7 @@ ble_hs_startup_le_read_buf_sz_tx(uint16_t *out_pktlen, uint8_t *out_max_pkts) return 0; } +#endif /* MYNEWT_VAL(BLE_ISO) */ static int ble_hs_startup_read_buf_sz_tx(uint16_t *out_pktlen, uint16_t *out_max_pkts) @@ -152,9 +183,17 @@ ble_hs_startup_read_buf_sz(void) uint16_t max_pkts = 0; uint16_t pktlen = 0; uint8_t le_max_pkts = 0; +#if MYNEWT_VAL(BLE_ISO) + uint16_t iso_pktlen = 0; + uint8_t iso_max_pkts = 0; +#endif /* MYNEWT_VAL(BLE_ISO) */ int rc; +#if MYNEWT_VAL(BLE_ISO) + rc = ble_hs_startup_le_read_buf_sz_v2_tx(&le_pktlen, &le_max_pkts, &iso_pktlen, &iso_max_pkts); +#else /* MYNEWT_VAL(BLE_ISO) */ rc = ble_hs_startup_le_read_buf_sz_tx(&le_pktlen, &le_max_pkts); +#endif /* MYNEWT_VAL(BLE_ISO) */ if (rc != 0) { return rc; } @@ -174,6 +213,13 @@ ble_hs_startup_read_buf_sz(void) return rc; } +#if MYNEWT_VAL(BLE_ISO) + rc = ble_hs_hci_set_iso_buf_sz(iso_pktlen, iso_max_pkts); + if (rc != 0) { + return rc; + } +#endif /* MYNEWT_VAL(BLE_ISO) */ + return 0; } #endif @@ -292,7 +338,7 @@ ble_hs_startup_le_set_evmask_tx(void) } #endif -#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) && !MYNEWT_VAL(BLE_ISO) if (version >= BLE_HCI_VER_BCS_5_2) { /** * Enable the following LE events: @@ -338,6 +384,26 @@ ble_hs_startup_le_set_evmask_tx(void) } #endif +#if MYNEWT_VAL(BLE_ISO) + if (version >= BLE_HCI_VER_BCS_5_2) { + /** + * Enable the following LE events: + * 0x0000000001000000 LE CIS Established event [v1] + * 0x0000000002000000 LE CIS Request event + * 0x0000000004000000 LE Create BIG Complete event + * 0x0000000008000000 LE Terminate BIG Complete event + * 0x0000000010000000 LE BIG Sync Established event + * 0x0000000020000000 LE BIG Sync Lost event + * 0x0000000200000000 LE BIGInfo Advertising Report event + * 0x0000020000000000 LE CIS Established event [v2] + */ + mask |= 0x000000023f000000; +#if MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) + mask |= 0x0000020000000000; +#endif /* MYNEWT_VAL(BLE_ISO_CIS_ESTAB_V2) */ + } +#endif /* MYNEWT_VAL(BLE_ISO) */ + cmd.event_mask = htole64(mask); rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, diff --git a/nimble/include/nimble/hci_common.h b/nimble/include/nimble/hci_common.h index 1a5469108..2997eafdc 100644 --- a/nimble/include/nimble/hci_common.h +++ b/nimble/include/nimble/hci_common.h @@ -154,6 +154,14 @@ struct ble_hci_cb_wr_auth_pyld_tmo_rp { uint16_t conn_handle; } __attribute__((packed)); +#define BLE_HCI_OCF_CB_CFG_DATA_PATH (0x0083) +struct ble_hci_cb_cfg_data_path_cp { + uint8_t data_path_dir; + uint8_t data_path_id; + uint8_t vs_cfg_len; + uint8_t vs_cfg[0]; +} __attribute__((packed)); + /* List of OCF for Info Param commands (OGF=0x04) */ #define BLE_HCI_OCF_IP_RD_LOCAL_VER (0x0001) struct ble_hci_ip_rd_local_ver_rp { @@ -187,6 +195,60 @@ struct ble_hci_ip_rd_bd_addr_rp { uint8_t addr[6]; } __attribute__((packed)); +#define BLE_HCI_OCF_IP_RD_LOCAL_SUPP_CODEC (0x000D) +struct ble_hci_ip_rd_local_supp_std_codec { + uint8_t coding_fmt; + uint8_t transport; +} __attribute__((packed)); + +struct ble_hci_ip_rd_local_supp_vs_codec { + uint16_t company_id; + uint16_t vs_codec_id; + uint8_t transport; +} __attribute__((packed)); + +struct ble_hci_ip_rd_local_supp_codec_rp { + uint8_t num_std_codec; + struct ble_hci_ip_rd_local_supp_std_codec std; + uint8_t num_vs_codec; + struct ble_hci_ip_rd_local_supp_vs_codec vs; +} __attribute__((packed)); + +#define BLE_HCI_OCF_IP_RD_LOCAL_SUPP_CODEC_CAPS (0x000E) +struct ble_hci_ip_rd_local_supp_codec_caps_cp { + uint8_t coding_fmt; + uint16_t company_id; + uint16_t vs_codec_id; + uint8_t logical_tpt_type; + uint8_t direction; +} __attribute__((packed)); + +struct ble_hci_ip_rd_local_supp_codec_caps { + uint8_t codec_cap_len; + uint8_t codec_cap[0]; +} __attribute__((packed)); + +struct ble_hci_ip_rd_local_supp_codec_caps_rp { + uint8_t num_codec_caps; + struct ble_hci_ip_rd_local_supp_codec_caps codec_caps; +} __attribute__((packed)); + +#define BLE_HCI_OCF_IP_RD_LOCAL_SUPP_CONTROLLER_DELAY (0x000F) +struct ble_hci_ip_rd_local_supp_controller_delay_cp { + uint8_t coding_fmt; + uint16_t company_id; + uint16_t vs_codec_id; + uint8_t logical_tpt_type; + uint8_t direction; + uint8_t codec_cfg_len; + uint8_t codec_cfg[0]; +} __attribute__((packed)); + +struct ble_hci_ip_rd_local_supp_controller_delay_rp { + uint8_t min_controller_delay[3]; + uint8_t max_controller_delay[3]; +} __attribute__((packed)); + /* List of OCF for Status parameters commands (OGF = 0x05) */ #define BLE_HCI_OCF_RD_RSSI (0x0005) struct ble_hci_rd_rssi_cp { @@ -947,8 +1009,8 @@ struct ble_hci_le_cis_params { uint16_t max_sdu_p_to_c; uint8_t phy_c_to_p; uint8_t phy_p_to_c; - uint8_t rnt_c_to_p; - uint8_t rnt_p_to_c; + uint8_t rtn_c_to_p; + uint8_t rtn_p_to_c; } __attribute__((packed)); struct ble_hci_le_set_cig_params_cp { uint8_t cig_id; @@ -2284,6 +2346,33 @@ struct ble_hci_ev_le_subev_periodic_adv_resp_rep { struct periodic_adv_response responses[0]; } __attribute__((packed)); +#define BLE_HCI_LE_SUBEV_CIS_ESTABLISHED_V2 (0x2A) +struct ble_hci_ev_le_subev_cis_established_v2 { + uint8_t subev_code; + uint8_t status; + uint16_t conn_handle; + uint8_t cig_sync_delay[3]; + uint8_t cis_sync_delay[3]; + uint8_t transport_latency_c_to_p[3]; + uint8_t transport_latency_p_to_c[3]; + uint8_t phy_c_to_p; + uint8_t phy_p_to_c; + uint8_t nse; + uint8_t bn_c_to_p; + uint8_t bn_p_to_c; + uint8_t ft_c_to_p; + uint8_t ft_p_to_c; + uint16_t max_pdu_c_to_p; + uint16_t max_pdu_p_to_c; + uint16_t iso_interval; + uint8_t sub_interval[3]; + uint16_t max_sdu_c_to_p; + uint16_t max_sdu_p_to_c; + uint8_t sdu_interval_c_to_p[3]; + uint8_t sdu_interval_p_to_c[3]; + uint8_t framing; +} __attribute__((packed)); + #if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) // LE vendor hci event #define BLE_HCI_LE_SUBEV_DISCARD_REPORT_EVT 0XF0 diff --git a/nimble/transport/esp_ipc/src/hci_esp_ipc.c b/nimble/transport/esp_ipc/src/hci_esp_ipc.c index 22f589a2d..e7c34003f 100644 --- a/nimble/transport/esp_ipc/src/hci_esp_ipc.c +++ b/nimble/transport/esp_ipc/src/hci_esp_ipc.c @@ -28,7 +28,13 @@ ble_transport_host_recv_cb(hci_trans_pkt_ind_t type, uint8_t *data, uint16_t len if (type == HCI_ACL_IND) { rc = ble_transport_to_hs_acl((struct os_mbuf *)data); - } else { + } +#if MYNEWT_VAL(BLE_ISO) + else if (type == HCI_ISO_IND) { + rc = ble_transport_to_hs_iso_v2(data, len); + } +#endif /* MYNEWT_VAL(BLE_ISO) */ + else { rc = ble_transport_to_hs_evt(data); } return rc; diff --git a/nimble/transport/include/nimble/transport/monitor.h b/nimble/transport/include/nimble/transport/monitor.h index dc12c69c4..1a9a52a47 100644 --- a/nimble/transport/include/nimble/transport/monitor.h +++ b/nimble/transport/include/nimble/transport/monitor.h @@ -82,6 +82,14 @@ ble_transport_to_hs_iso(struct os_mbuf *om) { return ble_transport_to_hs_iso_impl(om); } + +#if MYNEWT_VAL(BLE_ISO) +static inline int +ble_transport_to_hs_iso_v2(const uint8_t *data, uint16_t len) +{ + return ble_transport_to_hs_iso_impl_v2(data, len); +} +#endif /* MYNEWT_VAL(BLE_ISO) */ #endif /* BLE_MONITOR */ #ifdef __cplusplus diff --git a/nimble/transport/include/nimble/transport_impl.h b/nimble/transport/include/nimble/transport_impl.h index 1faec3161..5bdc37c82 100644 --- a/nimble/transport/include/nimble/transport_impl.h +++ b/nimble/transport/include/nimble/transport_impl.h @@ -40,6 +40,9 @@ extern int ble_transport_to_ll_iso_impl(struct os_mbuf *om); extern int ble_transport_to_hs_evt_impl(void *buf); extern int ble_transport_to_hs_acl_impl(struct os_mbuf *om); extern int ble_transport_to_hs_iso_impl(struct os_mbuf *om); +#if MYNEWT_VAL(BLE_ISO) +extern int ble_transport_to_hs_iso_impl_v2(const uint8_t *data, uint16_t len); +#endif /* MYNEWT_VAL(BLE_ISO) */ #ifdef __cplusplus }