LE Audio CAP Acceptor

The purpose of this document is to give an overview of the LE audio CAP acceptor sample. The LE audio CAP acceptor sample implements a very simple LE audio CAP acceptor device and it can be used as a framework to develop many different CAP acceptor-role applications.

../../../_images/acc_architecture.png

The sample uses LE host, LE manager, and LE audio module. The above figure shows the relationships between several modules in the sample.

More information about these modules can be found in the LE Host, LE Manager and LE Audio Manager documentation.

Requirements

The sample supports the following development kits:

Development Kits

Hardware Platforms

Board Name

Build Target

RTL87x3E HDK

RTL87x3E EVB

lea_cap_acceptor_4M_bank0

lea_cap_acceptor_16M_bank0

RTL87x3EP HDK

RTL87x3EP EVB

lea_cap_acceptor_4M_bank0

lea_cap_acceptor_16M_bank0

RTL87x3D HDK

RTL87x3D EVB

lea_cap_acceptor_8M_bank0

lea_cap_acceptor_16M_bank0

lea_cap_acceptor_cs_16M_bank0

This sample project can be found under board\evb\lea_cap_acceptor in SDK folder structure. Developers can choose the project according to the Board Name and choose the Build Target according to the flash map.

When built for an xxx_4M_xxx build target, the sample is configured to build and run with a 4M flash map.

When built for an xxx_8M_xxx build target, the sample is configured to build and run with an 8M flash map.

When built for an xxx_16M_xxx build target, the sample is configured to build and run with a 16M flash map.

For more requirements, please refer to Quick Start.

Wiring

The sample requires support for user command interface. For specific wiring instructions, please refer to Data UART Connection in User Command Interface.

Configurations

The configurations for the sample include Configurable Items and Generating System Config File.

Configurable Items

The sample’s configurable functions are defined in src\sample\lea_samples\lea_cap_acceptor\app_lea_cap_acc_flags.h:

  • LE link number

    The LE link number is configured by the macro definition APP_MAX_BLE_LINK_NUM.

    /**
     * @brief Config APP LE link number
     *
     */
    #define APP_MAX_BLE_LINK_NUM 1
    
  • Input audio data test

    If the macro definition APP_LEA_INPUT_AUDIO_DATA_TEST is enabled, the sample will test the ISO data send procedure.

    #define APP_LEA_INPUT_AUDIO_DATA_TEST 1 /**< Input (Host to Controller).*/
    
  • EATT support

    EATT is configured by the macro definition APP_LEA_EATT_SUPPORT. If the macro APP_LEA_EATT_SUPPORT is enabled, the sample will support EATT.

    /** @brief  Config EATT: 0-Disable EATT, 1-Enable EATT */
    #define APP_LEA_EATT_SUPPORT 0
    
  • GATT client service table storage

    The macro definition GATTC_TBL_STORAGE_SUPPORT is used to configure the sample to support GATT discovery result storage.

    • 0 - Do not store GATT discovery result.

    • 1 - Store GATT discovery result to FTL.

    #define GATTC_TBL_STORAGE_SUPPORT 1
    
  • CAP role component

    #define BAP_UNICAST_CLIENT       0
    #define BAP_UNICAST_SERVER       1
    #define BAP_BROADCAST_SOURCE     0
    #define BAP_BROADCAST_SINK       1
    #define BAP_BROADCAST_ASSISTANT  0
    #define BAP_SCAN_DELEGATOR       1
    #define VCP_VOLUME_CONTROLLER    0
    #define VCP_VOLUME_RENDERER      1
    #define MICP_MIC_CONTROLLER      0
    #define MICP_MIC_DEVICE          1
    #define CCP_CALL_CONTROL_SERVER  0
    #define CCP_CALL_CONTROL_CLIENT  1
    #define MCP_MEDIA_CONTROL_SERVER 0
    #define MCP_MEDIA_CONTROL_CLIENT 1
    #define CSIP_SET_COORDINATOR     0
    #define CSIP_SET_MEMBER          1
    

Generating System Config File

Developers shall configure the following items through MCUConfig Tool:

MCUConfig Tool Configuration

Configurable Item

Value

LE Link Number

APP_MAX_BLE_LINK_NUM

LE Slave Link Number

APP_MAX_BLE_LINK_NUM

CCCD Count

64

CCCD Per Link Count

32

LE Bond Device number

APP_MAX_BLE_LINK_NUM

Support LE Isochronous

Enable

LE CIG number

2

LE CIS number

4

LE BIG number

2

LE BIS number

4

Support LE PA

Enable

LE PA number

2

Building and Downloading

Take the project rtl87x3e_lea_cap_acceptor.uvprojx and target lea_cap_acceptor_4M_bank0 as an example, to build and run the sample with the Keil development environment, follow the steps listed below:

  1. Open rtl87x3e_lea_cap_acceptor.uvprojx.

  2. Choose the build target lea_cap_acceptor_4M_bank0.

    ../../../_images/chose_build_target4.png
  3. Building the target.

    ../../../_images/building4.png

    After a successful compilation, the sample bin file lea_cap_acceptor_bank0_MP-v0.0.0.0-xxx.bin will be generated in the directory bin\rtl87x3e\flash_4M_dualbank\bank0.

  4. Download sample bin into EVB board.

  5. Press reset button on the EVB board and it will start running.

Experimental Verification

After downloading the sample bin to the EVB board, the developer can test it either by using another kit that is running the LE Audio CAP Commander or the LE Audio CAP Initiator sample.

Testing with Another Kit

Testing with CAP Initiator

Please refer to Testing with Another Kit in LE Audio CAP Initiator.

Testing with CAP Commander

Please refer to Testing with Another Kit in LE Audio CAP Commander.

Code Overview

The main purpose of this chapter is to help sample developers become familiar with the development process related to the CAP Acceptor Role. LE audio CAP acceptor sample code overview will be introduced according to the following parts:

  • LE audio CAP acceptor sample project overview will be introduced in chapter Source Code Directory.

  • For CAP acceptor related parameters and Bluetooth Host initialization, please refer to the chapter Initialization.

  • For CAP acceptor related GAP message handler, please refer to the chapter GAP Message Handler.

  • For CAP acceptor related callback handler, please refer to the chapter Callback Message Handler.

  • For CAP acceptor related test flow, please refer to the chapter LE Audio Flow.

Source Code Directory

This section describes the project directory and project structure. Reference files directory is as follows:

  • Project directory: board\evb\lea_cap_acceptor.

  • Project source code directory: src\sample\lea_samples\lea_cap_acceptor.

Source files in the sample project are currently categorized into several groups as below:

└── lea_cap_acceptor_4M_bank0
    ├── include                  ROM UUID header files. Developers do not need to modify it.
    ├── lib                      The protocol stack and GAP library file. Developers do not need to modify it.
        ├── gap_utils.lib
        ├── ROM.lib
        ├── upperstack_4M.lib
        ├── hal_utils.lib
        ├── ble_mgr.lib
        ├── leaudio.lib
        └── sysm.lib

    ├── cmsis                    The cmsis source code. Developers do not need to modify it.
    ├── APP                      The sample source code.
        ├── data_uart.c
        ├── user_cmd_parse.c
        ├── bt_ext_ftl.c                   GATT discovery result storage
        ├── gattc_tbl_storage.c
        ├── app_lea_cap_acc_main.c         Main entry
        ├── app_lea_cap_acc_gap.c          LE GAP initialization and message handler
        ├── app_lea_cap_acc_profile.c      LE audio profile initialization
        ├── app_lea_cap_acc_cfg.c          NV config
        ├── app_lea_cap_acc_link.c         LE link utility
        ├── app_lea_cap_acc_user_cmd.c     User Command
        ├── app_lea_cap_acc_adv.c          Advertising manager
        ├── app_lea_cap_acc_scan.c         Scan manager
        ├── app_lea_cap_acc_csis.c         CSIS message handler
        ├── app_lea_cap_acc_bap.c          BAP message handler
        ├── app_lea_cap_acc_vc_mic.c       VCS and MISC message handler
        ├── app_lea_cap_acc_audio_data.c   Audio data test code
        ├── app_lea_cap_acc_mcp.c          MCP message handler
        ├── app_lea_cap_acc_content.c      CCP/MCP content
        ├── app_lea_cap_acc_ccp.c          CCP message handler
        └── app_lea_cap_acc_ecfc.c         ECFC message handler

    └── io_hal

Initialization

The main() function is invoked when the board is powered on or the chip boots up, and it performs the following initialization functions:

int main(void)
{
   app_cfg_init();
   app_msg_init();
   board_init();
   app_gap_init();
   framework_init();
#if APP_LEA_INPUT_AUDIO_DATA_TEST
   app_init_timer(app_evt_queue_handle, MAX_NUMBER_OF_APP_TIMER_MODULE);
#endif
   pwr_mgr_init();
   task_init();
   os_sched_start();

   return 0;
}

GAP Parameter Initialization

The app_gap_init() function is used to initialize the GAP parameters. Developers can easily customize the sample by modifying the following parameter values:

void app_gap_init(void)
{
   le_gap_init(APP_MAX_BLE_LINK_NUM);
   gap_lib_init();

   /* Device name and device appearance */
   uint8_t  device_name[GAP_DEVICE_NAME_LEN] = "CAP Acceptor";
   uint16_t appearance = GAP_GATT_APPEARANCE_UNKNOWN;

   le_set_gap_param(GAP_PARAM_DEVICE_NAME, GAP_DEVICE_NAME_LEN, device_name);
   le_set_gap_param(GAP_PARAM_APPEARANCE, sizeof(appearance), &appearance);

   /* Use LE Advertising Extensions */
   uint8_t use_extended = true;
   le_set_gap_param(GAP_PARAM_USE_EXTENDED_ADV, sizeof(use_extended), &use_extended);

   /* GAP Bond Manager parameters */
   uint8_t  auth_pair_mode = GAP_PAIRING_MODE_PAIRABLE;
   uint16_t auth_flags = GAP_AUTHEN_BIT_BONDING_FLAG | GAP_AUTHEN_BIT_SC_FLAG;
   uint8_t  auth_io_cap = GAP_IO_CAP_NO_INPUT_NO_OUTPUT;
   uint8_t  auth_oob = false;
   uint8_t  auth_use_fix_passkey = false;
   uint32_t auth_fix_passkey = 0;
   uint8_t  auth_sec_req_enable = false;
   uint16_t auth_sec_req_flags = GAP_AUTHEN_BIT_BONDING_FLAG | GAP_AUTHEN_BIT_SC_FLAG;
   uint8_t  irk_auto = true;

   gap_set_param(GAP_PARAM_BOND_PAIRING_MODE, sizeof(auth_pair_mode), &auth_pair_mode);
   gap_set_param(GAP_PARAM_BOND_AUTHEN_REQUIREMENTS_FLAGS, sizeof(auth_flags), &auth_flags);
   gap_set_param(GAP_PARAM_BOND_IO_CAPABILITIES, sizeof(auth_io_cap), &auth_io_cap);
   gap_set_param(GAP_PARAM_BOND_OOB_ENABLED, sizeof(auth_oob), &auth_oob);
   le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY, sizeof(auth_fix_passkey), &auth_fix_passkey);
   le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY_ENABLE, sizeof(auth_use_fix_passkey),
                     &auth_use_fix_passkey);
   le_bond_set_param(GAP_PARAM_BOND_SEC_REQ_ENABLE, sizeof(auth_sec_req_enable), &auth_sec_req_enable);
   le_bond_set_param(GAP_PARAM_BOND_SEC_REQ_REQUIREMENT, sizeof(auth_sec_req_flags),
                     &auth_sec_req_flags);
   le_bond_set_param(GAP_PARAM_BOND_GEN_LOCAL_IRK_AUTO, sizeof(uint8_t), &irk_auto);

   /* register gap message callback */
   le_register_app_cb(app_gap_callback);
   /* register gap common message callback */
   gap_register_app_cb(app_gap_common_callback);

   /* ble manager module initialize */
   app_ble_mgr_lib_init();
#if APP_LEA_EATT_SUPPORT
   app_lea_ecfc_init();
#endif
}
  1. le_gap_init()

    Initialize GAP and set the link number.

  2. gap_lib_init()

    Initialize gap_utils.lib.

  3. le_set_gap_param()

  4. gap_set_param() and le_bond_set_param()

    Initialize GAP pairing parameters.

  5. le_register_app_cb() and gap_register_app_cb()

    Register the GAP message callback function app_gap_callback() and GAP common message callback function app_gap_common_callback(), and all GAP callback messages will be handled in these callbacks.

  6. app_ble_mgr_lib_init()

    Initialize the LE manager library. More information can be found in the chapter LE Manager Initialization of LE Manager.

    void app_ble_mgr_lib_init(void)
    {
       BLE_MGR_PARAMS param = {0};
    
       param.ble_ext_adv.enable = true;
       param.ble_ext_adv.adv_num = 1;
    #if (BAP_BROADCAST_SINK || BAP_SCAN_DELEGATOR)
       param.ble_scan.enable = true;
       gap_vendor_le_set_host_feature(0, 1);
    #endif
       param.ble_conn.enable = true;
       param.ble_conn.link_num = APP_MAX_BLE_LINK_NUM;
       ble_mgr_init(&param);
    }
    
  7. app_lea_ecfc_init()

    Initialize ECFC if the sample enables APP_LEA_EATT_SUPPORT.

    void app_lea_ecfc_init(void)
    {
       gap_ecfc_init(1);
       gap_ecfc_reg_proto(PSM_EATT, app_lea_ecfc_callback, true, &app_ecfc_le_proto_id,
                          GAP_ECFC_DATA_PATH_GATT);
    }
    

More information on LE GAP initialization and startup flow can be found in the GAP Parameters Initialization chapter of the LE Host.

LE Audio Profile Initialization

When the CAP acceptor application starts running, if the developer inputs the command reg 0, the app_lea_profile_init() will be called to initialize the LE audio profile parameters. More information can be found in the LE Audio Manager.

void app_lea_profile_init(void)
{
   gatt_svc_init(GATT_SVC_USE_EXT_SERVER, MAX_BLE_SRV_NUM);

#if GATTC_TBL_STORAGE_SUPPORT
   gattc_tbl_storage_init();
#endif
   T_BLE_AUDIO_PARAMS ble_audio_param = {0};

   ble_audio_param.evt_queue_handle = app_evt_queue_handle;
   ble_audio_param.io_queue_handle = app_io_queue_handle;
#if APP_LEA_EATT_SUPPORT
   ble_audio_param.bt_gatt_client_init = (GATT_CLIENT_DISCOV_MODE_REG_SVC_BIT |
                                          GATT_CLIENT_DISCOV_MODE_CCCD_STORAGE_BIT |
                                          GATT_CLIENT_DISCOV_MODE_USE_EXT_CLIENT |
                                          GATT_CLIENT_DISCOV_MODE_GATT_SVC);
   gatt_client_cfg_client_supported_feature(GATT_SVC_CLIENT_SUPPORTED_FEATURES_EATT_BEARER_BIT);
#else
   ble_audio_param.bt_gatt_client_init = (GATT_CLIENT_DISCOV_MODE_REG_SVC_BIT |
                                          GATT_CLIENT_DISCOV_MODE_CCCD_STORAGE_BIT |
                                          GATT_CLIENT_DISCOV_MODE_USE_EXT_CLIENT);
#endif
   ble_audio_param.acl_link_num = APP_MAX_BLE_LINK_NUM;
   ble_audio_param.io_event_type = IO_MSG_TYPE_LE_AUDIO;
   ble_audio_init(&ble_audio_param);
   app_lea_acc_vc_mic_init();
   app_lea_acc_bap_init();
   app_lea_acc_cap_init();
   app_lea_audio_data_init();
}
  1. gatt_svc_init()

    Initialize the GATT service module and service number in the LE manager library.

  2. gattc_tbl_storage_init()

    Initialize the GATT client service table storage function.

  3. ble_audio_init()

    Initialize LE audio related parameters.

  4. app_lea_acc_vc_mic_init()

    Initialize the parameters for VCS and MICS, and register the callback function to handle related messages.

    void app_lea_acc_vc_mic_init(void)
    {
       T_BLE_AUDIO_VC_MIC_PARAMS vc_mic_param = {0};
       T_MICS_PARAM mics_param = {MICS_NOT_MUTE};
       T_VCS_PARAM vcs_param = {0};
    
       ......
    
       ble_audio_vc_mic_init(&vc_mic_param);
    
    #if APP_LEA_VOCS_SUPPORT
       ......
       vocs_set_param(0, &param_vocs);
    #endif
    
    #if (APP_LEA_AICS_FOR_VCS_SUPPORT || APP_LEA_AICS_FOR_MICS_SUPPORT)
       ......
    #if APP_LEA_AICS_FOR_VCS_SUPPORT
       ......
       aics_set_param(aics_vcs_srv_instance_id, AICS_PARAM_INPUT_STATE, sizeof(T_AICS_INPUT_STATE),
                      (uint8_t *)&input_state,
                      true);
       aics_set_param(aics_vcs_srv_instance_id, AICS_PARAM_GAIN_SETTING_PROP,
                      sizeof(T_AICS_GAIN_SETTING_PROP),
                      (uint8_t *)&gain_prop, false);
       aics_set_param(aics_vcs_srv_instance_id, AICS_PARAM_INPUT_TYPE, sizeof(uint8_t), &input_type,
                      false);
       aics_set_param(aics_vcs_srv_instance_id, AICS_PARAM_INPUT_STATUS, sizeof(uint8_t), &input_status,
                      false);
       aics_set_param(aics_vcs_srv_instance_id, AICS_PARAM_INPUT_DES, strlen(app_audio_input_des_vcs),
                      (uint8_t *)app_audio_input_des_vcs,
                      false);
    #endif
    
    #if APP_LEA_AICS_FOR_MICS_SUPPORT
       ......
       aics_set_param(aics_mics_srv_instance_id, AICS_PARAM_INPUT_STATE, sizeof(T_AICS_INPUT_STATE),
                      (uint8_t *)&input_state,
                      true);
       aics_set_param(aics_mics_srv_instance_id, AICS_PARAM_GAIN_SETTING_PROP,
                      sizeof(T_AICS_GAIN_SETTING_PROP),
                      (uint8_t *)&gain_prop, false);
       aics_set_param(aics_mics_srv_instance_id, AICS_PARAM_INPUT_TYPE, sizeof(uint8_t), &input_type,
                      false);
       aics_set_param(aics_mics_srv_instance_id, AICS_PARAM_INPUT_STATUS, sizeof(uint8_t), &input_status,
                      false);
       aics_set_param(aics_mics_srv_instance_id, AICS_PARAM_INPUT_DES, strlen(app_audio_input_des_mics),
                      (uint8_t *)app_audio_input_des_mics, false);
    #endif
    #endif
    
    #if MICP_MIC_DEVICE
        mics_param.mic_mute = MICS_NOT_MUTE;
        mics_set_param(&mics_param);
    #endif
    
    #if VCP_VOLUME_RENDERER
        vcs_param.volume_setting = app_cfg_nv.lea_vcs_vol_setting;
        vcs_param.mute = app_cfg_nv.lea_vcs_mute;
        vcs_param.change_counter = app_cfg_nv.lea_vcs_change_cnt;
        vcs_param.volume_flags = app_cfg_nv.lea_vcs_vol_flag;
        vcs_param.step_size = app_cfg_nv.lea_vcs_step_size;
        vcs_set_param(&vcs_param);
    #endif
    
        ble_audio_cback_register(app_lea_acc_vc_mic_handle_msg);
    }
    
  5. app_lea_acc_bap_init()

    Initialize LE audio BAP role.

    void app_lea_acc_bap_init(void)
    {
       T_BAP_ROLE_INFO role_info = {0};
    
       ble_audio_cback_register(app_lea_acc_bap_handle_msg);
    
       ......
    
       bap_role_init(&role_info);
    
       T_PACS_PARAMS pacs_params = {0};
    
       ......
    
       pacs_params.sink_locations.is_exist = true;
       pacs_params.sink_locations.is_notify = true;
       pacs_params.sink_locations.is_write = false;
       pacs_params.sink_locations.sink_audio_location = app_db.lea_sink_audio_location;
       pacs_params.source_locations.is_exist = true;
       pacs_params.source_locations.is_notify = true;
       pacs_params.source_locations.is_write = false;
       pacs_params.source_locations.source_audio_location = app_db.lea_source_audio_location;
       pacs_params.supported_contexts.is_notify = true;
       pacs_params.supported_contexts.sink_supported_contexts = app_db.lea_sink_supported_contexts;
       pacs_params.supported_contexts.source_supported_contexts = app_db.lea_source_supported_contexts;
    
       pacs_pac_add(SERVER_AUDIO_SINK, app_lea_sink_codec,
                    sizeof(app_lea_sink_codec),
                    true);
       pacs_pac_add(SERVER_AUDIO_SOURCE, app_lea_source_codec,
                    sizeof(app_lea_source_codec),
                    true);
    
       pacs_init(&pacs_params);
    #if BAP_UNICAST_SERVER
       app_ext_adv_adv_cfg(LE_EXT_ADV_ASCS);
    #endif
    #if BAP_SCAN_DELEGATOR
       app_ext_adv_adv_cfg(LE_EXT_ADV_BASS);
    #endif
    }
    
  6. app_lea_acc_cap_init()

    Initialize LE audio CAP related parameters.

    void app_lea_acc_cap_init(void)
    {
       T_CAP_INIT_PARAMS cap_init_param = {0};
    
       cap_init_param.cap_role = CAP_ROLE;
       cap_init_param.cas.enable = true;
    #if CSIP_SET_MEMBER
       if (app_db.csis_cfg != APP_LEA_CSIS_CFG_NOT_EXIST)
       {
           app_lea_acc_csis_init(&cap_init_param);
       }
    #endif
    #if MCP_MEDIA_CONTROL_CLIENT
       app_lea_acc_mcp_init_cap(&cap_init_param);
    #endif
    #if CCP_CALL_CONTROL_CLIENT
       app_lea_acc_ccp_init_cap(&cap_init_param);
    #endif
       cap_init(&cap_init_param);
    }
    
  7. app_lea_audio_data_init()

    Initialize the LE audio data test code.

    void app_lea_audio_data_init(void)
    {
       os_queue_init(&app_db.iso_input_queue);
       os_queue_init(&app_db.iso_output_queue);
       gap_register_direct_cb(app_lea_data_direct_cb);
    }
    

GAP Message Handler

The app_handle_gap_msg() function is invoked whenever a GAP message is received from the GAP. More information on GAP messages can be found in the Bluetooth LE GAP Message chapter of the LE Host.

void app_handle_gap_msg(T_IO_MSG *p_gap_msg)
{
   T_LE_GAP_MSG gap_msg;

   memcpy(&gap_msg, &p_gap_msg->u.param, sizeof(p_gap_msg->u.param));
   ble_mgr_handle_gap_msg(p_gap_msg->subtype, &gap_msg);
   APP_PRINT_TRACE1("app_handle_gap_msg: subtype %d", p_gap_msg->subtype);
   switch (p_gap_msg->subtype)
   {
   case GAP_MSG_LE_DEV_STATE_CHANGE:
      {
         app_handle_dev_state_evt(gap_msg.msg_data.gap_dev_state_change.new_state,
                                  gap_msg.msg_data.gap_dev_state_change.cause);
      }
      break;

   case GAP_MSG_LE_CONN_STATE_CHANGE:
      {
         app_handle_conn_state_evt(gap_msg.msg_data.gap_conn_state_change.conn_id,
                                   (T_GAP_CONN_STATE)gap_msg.msg_data.gap_conn_state_change.new_state,
                                   gap_msg.msg_data.gap_conn_state_change.disc_cause);
      }
      break;
   ......
   case GAP_MSG_LE_AUTHEN_STATE_CHANGE:
     {
         app_handle_authen_state_evt(gap_msg.msg_data.gap_authen_state.conn_id,
                                     gap_msg.msg_data.gap_authen_state.new_state,
                                     gap_msg.msg_data.gap_authen_state.status);
     }
     break;
   ......
   }
   ble_audio_handle_gap_msg(p_gap_msg->subtype, gap_msg);
}
  1. The CAP acceptor sample will call app_ext_adv_params_init() and app_ext_adv_start() to start advertising when receiving GAP_INIT_STATE_STACK_READY.

    void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause)
    {
       APP_PRINT_INFO3("app_handle_dev_state_evt: init state %d, scan state %d, cause 0x%x",
                       new_state.gap_init_state,
                       new_state.gap_scan_state, cause);
       if (gap_dev_state.gap_init_state != new_state.gap_init_state)
       {
          if (new_state.gap_init_state == GAP_INIT_STATE_STACK_READY)
          {
             uint8_t bt_addr[6];
             APP_PRINT_INFO0("app_handle_dev_state_evt: GAP stack ready");
             /*stack ready*/
             gap_get_param(GAP_PARAM_BD_ADDR, bt_addr);
             data_uart_print("local bd addr: 0x%2x:%2x:%2x:%2x:%2x:%2x\r\n",
                             bt_addr[5],
                             bt_addr[4],
                             bt_addr[3],
                             bt_addr[2],
                             bt_addr[1],
                             bt_addr[0]);
             app_ext_adv_params_init();
             app_ext_adv_start();
          }
       }
       gap_dev_state = new_state;
    }
    
  2. The CAP acceptor sample will call app_ext_adv_start() to start advertising when receiving GAP_CONN_STATE_DISCONNECTED.

    void app_handle_conn_state_evt(uint8_t conn_id, T_GAP_CONN_STATE new_state, uint16_t disc_cause)
    {
       T_APP_LE_LINK *p_link;
    
       APP_PRINT_INFO3("app_handle_conn_state_evt: conn_id %d, new_state %d, disc_cause 0x%x",
                       conn_id, new_state, disc_cause);
    
       p_link = app_link_find_le_link_by_conn_id(conn_id);
    
       switch (new_state)
       {
       ......
       case GAP_CONN_STATE_DISCONNECTED:
          if (p_link != NULL)
          {
             if ((disc_cause != (HCI_ERR | HCI_ERR_REMOTE_USER_TERMINATE))
                 && (disc_cause != (HCI_ERR | HCI_ERR_LOCAL_HOST_TERMINATE)))
             {
                APP_PRINT_ERROR2("app_handle_conn_state_evt: connection lost, conn_id %d, cause 0x%x", conn_id,
                                 disc_cause);
             }
    
             data_uart_print("Disconnect conn_id %d\r\n", conn_id);
             app_link_free_le_link(p_link);
             app_ext_adv_start();
          }
          break;
       ......
    
       default:
          break;
       }
    }
    
  3. If the MCP client or the CCP client is supported, the CAP acceptor sample will call gatt_client_start_discovery_all() to start the service discovery procedure when receiving GAP_AUTHEN_STATE_COMPLETE with cause GAP_SUCCESS.

    void app_handle_authen_state_evt(uint8_t conn_id, uint8_t new_state, uint16_t cause)
    {
       APP_PRINT_INFO2("app_handle_authen_state_evt: conn_id %d, cause 0x%x", conn_id, cause);
    
       switch (new_state)
       {
       case GAP_AUTHEN_STATE_COMPLETE:
          {
             if (cause == GAP_SUCCESS)
             {
                data_uart_print("Pair success\r\n");
                APP_PRINT_INFO0("app_handle_authen_state_evt: GAP_AUTHEN_STATE_COMPLETE pair success");
    #if (MCP_MEDIA_CONTROL_CLIENT || CCP_CALL_CONTROL_CLIENT)
                gatt_client_start_discovery_all(le_get_conn_handle(conn_id),
                                                app_bt_gatt_client_cb);
    #endif
             }
             else
             {
                data_uart_print("Pair failed: cause 0x%x\r\n", cause);
                APP_PRINT_INFO0("app_handle_authen_state_evt: GAP_AUTHEN_STATE_COMPLETE pair failed");
             }
          }
          break;
    
       default:
          {
             APP_PRINT_ERROR1("app_handle_authen_state_evt: unknown new_state %d", new_state);
          }
          break;
       }
    }
    

Callback Message Handler

When the CAP acceptor sample handles the GAP callback, the ble_mgr_handle_gap_cb() and ble_audio_handle_gap_cb() will be called to handle the callback message.

If the GATTC_TBL_STORAGE_SUPPORT is enabled, the CAP acceptor sample will call gattc_tbl_storage_handle_bond_modify() when receiving GAP_MSG_LE_BOND_MODIFY_INFO. If the bond information of the remote device is deleted, the GATT service table of the same remote device will be removed together.

T_APP_RESULT app_gap_callback(uint8_t cb_type, void *p_cb_data)
{
   T_APP_RESULT result = APP_RESULT_SUCCESS;
   T_LE_CB_DATA *p_data = (T_LE_CB_DATA *)p_cb_data;

   ble_mgr_handle_gap_cb(cb_type, p_cb_data);
   ble_audio_handle_gap_cb(cb_type, p_cb_data);

   //APP_PRINT_TRACE1("app_gap_callback: cb_type 0x%x", cb_type);

   switch (cb_type)
   {
   case GAP_MSG_LE_CONN_UPDATE_IND:
      APP_PRINT_INFO5("GAP_MSG_LE_CONN_UPDATE_IND: conn_id %d, conn_interval_max 0x%x, conn_interval_min 0x%x, conn_latency 0x%x,supervision_timeout 0x%x",
                      p_data->p_le_conn_update_ind->conn_id,
                      p_data->p_le_conn_update_ind->conn_interval_max,
                      p_data->p_le_conn_update_ind->conn_interval_min,
                      p_data->p_le_conn_update_ind->conn_latency,
                      p_data->p_le_conn_update_ind->supervision_timeout);
      /* if reject the proposed connection parameter from peer device, use APP_RESULT_REJECT. */
      result = APP_RESULT_ACCEPT;
      break;

   case GAP_MSG_LE_BOND_MODIFY_INFO:
      APP_PRINT_INFO1("GAP_MSG_LE_BOND_MODIFY_INFO: type 0x%x",
                      p_data->p_le_bond_modify_info->type);
#if GATTC_TBL_STORAGE_SUPPORT
      gattc_tbl_storage_handle_bond_modify(p_data->p_le_bond_modify_info);
#endif
      break;

   default:
      //APP_PRINT_ERROR1("app_gap_callback: unhandled cb_type 0x%x", cb_type);
      break;
   }
   return result;
}

LE Audio Flow

This chapter describes the LE audio flows related to the Acceptor Role.

CSIS Configuration

  • Dev_ACC_NO_CSIS: Do not register CSIS service.

    Developers enter user command: reg 0

  • Dev_ACC_CSIS_RANK1: Register CSIS service, RANK characteristic value is 1.

    Developers enter user command: reg 1

  • Dev_ACC_CSIS_RANK2: Register CSIS service, RANK characteristic value is 2.

    Developers enter user command: reg 2

Unicast Audio Start and Stop Flow

An Initiator acting in the role of BAP Unicast Client uses this procedure to start and stop one unicast Audio Stream. Unicast audio start and stop procedure is shown below:

../../../_images/acc_bap_unicast.png

Broadcast Audio Reception Start and Stop Flow

Commander acting in the BAP Broadcast Assistant role uses this procedure to start and stop the reception of broadcast Audio Streams by one Acceptor. Broadcast audio reception start and stop are shown as below:

../../../_images/acc_bap_ba.png

Broadcast Sink Role Flow

An acceptor, acting in the role of BAP Broadcast Sink, uses this procedure to start the reception of broadcast audio streams. Broadcast sink role flow is shown as below:

../../../_images/acc_bap_bsnk.png