HFP_AG

HFP (Hands-Free Profile) is a Bluetooth hands-free protocol that allows Bluetooth devices to control phone calls on peer Bluetooth devices, such as Bluetooth headsets controlling phone call answering, hanging up, rejecting, voice dialing, etc. Data interaction between the two Bluetooth ends in HFP is communicated through predefined AT commands. HFP defines two roles: Audio Gateway (AG) and Hands-Free component (HF):

  • Audio Gateway (AG): This device serves as the gateway for audio input/output. Typical gateway devices are mobile phones

  • Hands-Free component (HF): This device serves as the remote audio input/output mechanism for the audio gateway and can provide several remote control functions. Typical hands-free component devices are car systems and Bluetooth headsets HFP Role

  • AT command rules:

    • One command line can only represent one AT command

    • < cr > abbreviation for carriage return, equivalent to the enter key, ASCII code is 0x0D

    • < lf > abbreviation for NL line feed, new line, equivalent to the line feed key, ASCII code is 0x0A

    • HF -> AG sends AT command format: < AT command >< cr >

    • AG -> HF sends AT command format: < cr >< lf >result code< cr >< lf >

    • If the AT command result code sent by AG to HF is a message reply, it must be followed by an OK message, unless the reply is a +CME ERROR message, where the following parameters represent the reason for failure.

This document is mainly based on Sifli SDK, introducing how to use basic functions of AG role. Related files include:

  • bts2_app_interface

  • bts2_app_hfp_ag

HFP_AG Initialization

  • AG initialization function: bt_hfp_ag_app_init, assigns initial values to AG-related states and flags

  • AG service startup function: bt_hfp_start_profile_service, configures +BRSF related attributes, users can adjust corresponding feature values according to requirements

  • AT cmd:

    • +BRSF: AG support features (Bluetooth Retrieve Supported Features)

    • Format: +BRSF:< AG support features >

    • After the HF side sends its supported features to the AG side, the AG end must also send its supported features to HF through “+BRSF:< AG support features >”.

U32 features = (U32)(HFP_AG_FEAT_ECNR | \
                             HFP_AG_FEAT_INBAND | \
                             HFP_AG_FEAT_REJECT | \
                             HFP_AG_FEAT_ECS | \
                             HFP_AG_FEAT_EXTERR | \
                             HFP_AG_FEAT_CODEC | \
                             HFP_AG_FEAT_ESCO);

/* AG feature masks */
#define HFP_AG_FEAT_3WAY    0x00000001      /* Three-way calling */
#define HFP_AG_FEAT_ECNR    0x00000002      /* Echo cancellation/noise reduction */
#define HFP_AG_FEAT_VREC    0x00000004      /* Voice recognition */
#define HFP_AG_FEAT_INBAND  0x00000008      /* In-band ring tone */
#define HFP_AG_FEAT_VTAG    0x00000010      /* Attach a phone number to a voice tag */
#define HFP_AG_FEAT_REJECT  0x00000020      /* Ability to reject incoming call */
#define HFP_AG_FEAT_ECS     0x00000040      /* Enhanced Call Status */
#define HFP_AG_FEAT_ECC     0x00000080      /* Enhanced Call Control */
#define HFP_AG_FEAT_EXTERR  0x00000100      /* Extended error codes */
#define HFP_AG_FEAT_CODEC   0x00000200      /* Codec Negotiation */

/* Valid feature bit mask for HFP 1.6 (and below) */
#define HFP_1_6_FEAT_MASK   0x000003FF

/* HFP 1.7+ */
#define HFP_AG_FEAT_HF_IND  0x00000400      /* HF Indicators */
#define HFP_AG_FEAT_ESCO    0x00000800      /* eSCO S4 (and T2) setting supported */

/* Proprietary features: using 31 ~ 16 bits */
#define HFP_AG_FEAT_BTRH    0x00010000      /* CCAP incoming call hold */
#define HFP_AG_FEAT_UNAT    0x00020000      /* Pass unknown AT commands to app */
#define HFP_AG_FEAT_NOSCO   0x00040000      /* No SCO control performed by BTA AG */
#define HFP_AG_FEAT_NO_ESCO 0x00080000      /* Do not allow or use eSCO */
#define HFP_AG_FEAT_VOIP    0x00100000      /* VoIP call */

HFP_AG Device Connection

When LM Link and RFCOMM Connection already exist, user behavior or other internal events that want to use HFP service need to first establish SLC (Service Level Connection). Establishing SLC connection requires the following 5 phases:

  1. Supported features exchange (AT+BRSF)

  2. Codec Negotiation (AT+BAC)

  3. AG Indicators (AT+CIND, AT+CMER, +CIEV, AT+CHLD)

  4. HF Indicators (AT+BIND, AT+BIEV)

  5. End of Service Level Connection HFP Connect Progress

  • AT cmd:

    • AT+BAC: (Bluetooth Available Codecs)

    • Format: AT+BAC=< codec_id1 >,< codec_id2 >

    • HF side informs AG side which encoding methods are supported

    • codec: CVSD and msbc

  • AT cmd:

    • AT+CIND: (Standard indicator update AT command)

    • Format:

      • AT+CIND=? Test command. HF obtains indicator index values and ranges supported by AG side. Must be requested at least once before sending indicator-related commands (AT+CIND? or AT_CMER).

      • AT+CIND? Read command, HF reads current indicator values from AG side

  • AT cmd:

    • +CIEV: unsolicited result code (Standard indicator events reporting unsolicited result code)

    • Format: +CIEV: < ind >,< value >

    • When AG indicators change, AG needs to actively notify HF side using +ciev AT cmd indicator type

  • AG device connection interface:

    • bts2_app_interface connection interface: bt_interface_conn_to_source_ext

    • bts2_app_hfp_ag connection interface: bt_hfp_connect_profile

  • AG device disconnection interface:

    • bts2_app_hfp_ag disconnection interface: bt_hfp_disconnect_profile

  • AG connection status callback events: - AG connection successful: BT_NOTIFY_AG_PROFILE_CONNECTED - AG connection failed: BT_NOTIFY_AG_PROFILE_DISCONNECTED

// After calling the API to connect AG role, the AG connection success message is sent to user through notify
// Users need to implement hdl functions to receive notify events, such as: bt_notify_handle
// SLC message: BT_NOTIFY_AG_PROFILE_CONNECTED, users can forward the message to user task for processing.
// BT_NOTIFY_AG_PROFILE_CONNECTED event includes: address information, profile_type, res: 0 (success)
// To disconnect device connection, call bt_hfp_disconnect_profile
// BT_NOTIFY_AG_PROFILE_DISCONNECTED event includes: address information, profile_type, reason
// For specific structure information, refer to API comments
static int bt_notify_handle(uint16_t type, uint16_t event_id, uint8_t *data, uint16_t data_len)
{
    int ret = -1;

    switch (type)
    {
    case BT_NOTIFY_HFP_AG:
    {
        bt_sifli_notify_hfp_ag_event_hdl(event_id, data, data_len);
    }
    break;
    }
    return 0;
}


int bt_sifli_notify_hfp_ag_event_hdl(uint16_t event_id, uint8_t *data, uint16_t data_len)
{
    switch (event_id)
    {
    case BT_NOTIFY_AG_PROFILE_CONNECTED:
    {
        //handle connected msg(bt_notify_profile_state_info_t *)
        break;
    }
    case BT_NOTIFY_AG_PROFILE_DISCONNECTED:
    {
        //handle disconnected msg(bt_notify_profile_state_info_t *)
        break;
    }
    }
    return 0;
}

Note

Note: The address parameters passed by the two interfaces need to be converted accordingly.

  • During AG SLC connection process, event when receiving AT+CIND=? cmd from HF: BT_NOTIFY_AG_GET_INDICATOR_STATUS_REQ

  • AG needs to reply to event: BT_NOTIFY_AG_GET_INDICATOR_STATUS_REQ

    • bts2_app_interface indicator status reply interface: bt_interface_get_all_indicator_info_res

    • bts2_app_hfp_ag indicator status reply interface: bt_hfp_ag_cind_response

typedef struct
{
    // 0 (No home/roam network available) / 1 (home/roam network available)
    U8 service_status;
    // 0 (There are no calls in progress) / 1 (At least one call in progress)
    U8 call;
    // 0 (No currently in call set up)
    // 1 (An incoming call process ongoing)
    // 2 (An outgoing call process ongoing)
    // 3 (Remote party being alert in an outgoing call)
    U8 callsetup;
    // Phone battery val (0~5)
    U8 batt_level;
    // Phone signal val (0~5)
    U8 signal;
    // 0 (Roaming is not active) / 1 (Roaming is active)
    U8 roam_status;
    // 0 (No call held)
    // 1 (Call is placed on hold or active/held call swapped)
    // 2 (Call on hold, no active call)
    U8 callheld;
} hfp_cind_status_t;

    hfp_cind_status_t cind_status;
    cind_status.service_status = 1;
    cind_status.call = 0;
    cind_status.callsetup = 0;
    cind_status.batt_level = 5;
    cind_status.signal = 3;
    cind_status.roam_status = 0;
    cind_status.callheld = 0;
    bt_interface_get_all_indicator_info_res(&cind_status);

HFP_AG Basic Functionality Usage

Call Audio Establishment

Audio Connection in HFP specification usually refers to SCO/eSCO voice path connection. Before SCO/eSCO, HF (AT+BCC) needs to notify AG to first select codec algorithm.

  • AT cmd:

    • AT+BCC: Bluetooth Codec Connection

    • Format: AT+BCC

    • HF sends to AG, triggers AG to initiate codec connection process

    • If AG decides to initiate codec connection process, reply OK; otherwise ERROR. After replying OK, AG side will send +BCS:< codec_id > and HF side replies: AT+BCS=< codec_id >. After that, eSCO link is established

  • AT cmd:

    • AT+BCS: Bluetooth Codec Selection

    • Format: AT+BCS=< codec_id >

    • +BCS: Bluetooth Codec Selection

    • Format: +BCS:< codec_id >

    • codec: (codec_id=1) CVSD and (codec_id=2) msbc

    • Before AG establishes eSCO, it will send command +BCS:< codec_id > to HF. HF side replies: AT+BCS=< codec_id >. If both AG and HF support this ID, the secondary link will be established. But if ID is not supported, HF will respond with AT+BAC and its available codecs. If (e)SCO link cannot be established, AG will restart the Codec Connection establishment process. Before Codec connection establishment, CVSD encoding will be enabled.

Sifli HFP_AG Connect/Disconnect Call Audio

  • bts2_app_interface call audio connection interface: bt_interface_ag_audio_switch

  • bts2_app_hfp_ag call audio connection interface: bt_hfp_connect_audio

  • bts2_app_interface call audio disconnection interface: bt_interface_ag_audio_switch

  • bts2_app_hfp_ag call audio disconnection interface: bt_hfp_disconnect_audio

    // Call audio establishment connection interface bt_interface_ag_audio_switch parameters: type 0:connect audio type 1:disconnect audio + device mac address
    // Call audio connection successful BT_NOTIFY_COMMON_SCO_CONNECTED event: sco type (distinguish HF or AG) status (status)
    // For specific structure explanation, please refer to relevant descriptions in interface
static int bt_notify_handle(uint16_t type, uint16_t event_id, uint8_t *data, uint16_t data_len)
{
    int ret = -1;

    switch (type)
    {
    case BT_NOTIFY_COMMON:
    {
        bt_sifli_notify_common_event_hdl(event_id, data, data_len);
    }
    break;
    }
    return 0;
}


int bt_sifli_notify_common_event_hdl(uint16_t event_id, uint8_t *data, uint16_t data_len)
{
    switch (event_id)
    {
    case BT_NOTIFY_COMMON_SCO_CONNECTED:
    {
        //handle sco connected msg(bt_notify_device_sco_info_t *)
        break;
    }
    case BT_NOTIFY_COMMON_SCO_DISCONNECTED:
    {
        //handle sco disconnected msg(bt_notify_device_sco_info_t *)
        break;
    }
    }
    return 0;
}

AG Phone Status Update

  • AT cmd:

    • AT+CIND: (Standard indicator update AT command)

    • Format:

      • AT+CIND=? Test command. HF obtains indicator index values and ranges supported by AG side. Must be requested at least once before sending indicator-related commands (AT+CIND? or AT_CMER).

      • AT+CIND? Read command, HF reads current indicator values from AG side

  • AT cmd:

    • +CIEV: unsolicited result code (Standard indicator events reporting unsolicited result code)

    • Format: +CIEV: < ind >,< value >

    • When AG indicators change, AG needs to actively notify HF side using +ciev AT cmd

  • AT cmd:

    • AT+CLCC: Standard list current calls command

    • Format: AT+CLCC

    • +CLCC: Standard list current calls command

    • Format: +CLCC: < idx >,< dir >,< status >,< mode >,< mpty >,< number >,< type >

    • HF requests current call information list, AG side replies current call information list through “+CLCC”. If there are currently no calls, AG side must also reply with OK command.

Once the phone AG end call status changes, AG should notify HF of the current changed call status. For example, after an incoming call, Bluetooth headset HF end answers, call status changes, AG side +CIEV:2,1 notifies HF to reject or AG hangs up, AG side +CIEV:2,0 notifies HF end.

  • call Status

    • 0: No calls (held or active)

    • 1: Call is present (active or held)

  • callsetup status

    • 0: No call setup in progress

    • 1: Incoming call setup in progress

    • 2: Outgoing call setup in dialing state

    • 3: Outgoing call setup in alerting state

  • callheld status

    • 0: No call held

    • 1: Call is placed on hold or active/held calls swapped

    • 2: Call on hold, no active call

  • Incoming call state transition:

    • call_idle <—-> call_incoming <—–> call_active <—-> call_idle

  • Outgoing call state transition:

    • call_idle <—-> call_outgoing_dialing —-> call_outgoing_alerting <—–> call_active <—-> call_idle

    • call_idle <—-> call_outgoing_dialing —-> call_outgoing_alerting <—–> call_idle

Sifli AG Update Phone Information

  • bts2_app_interface phone status update interface: bt_interface_phone_state_changed

  • bts2_app_hfp_ag phone status update interface: bt_hfp_ag_call_state_update_listener

typedef struct
{
    U16 type;  //ignore
    U8 num_active;
    U8 num_held;
    U8 callsetup_state;
    U8 phone_type;
    U8 phone_len;
    U8 phone_number[1];
} HFP_CALL_INFO_T;

// a call incoming
HFP_CALL_INFO_T call_info;
call_info.num_active = 0;
call_info.num_held = 0;
call_info.callsetup_state = 1;
call_info.phone_type = 0x81;
char *str = "1234567";
bmemcpy(&call_info.phone_number, str, strlen(str) + 1);
call_info.phone_len = strlen(str) + 1;
bt_interface_phone_state_changed(&call_info);


// a call active
HFP_CALL_INFO_T call_info;
call_info.num_active = 1;
call_info.num_held = 0;
call_info.callsetup_state = 0;
call_info.phone_type = 0x81;
char *str = "1234567";
bmemcpy(&call_info.phone_number, str, strlen(str) + 1);
call_info.phone_len = strlen(str) + 1;
bt_interface_phone_state_changed(&call_info);

// a call idle
HFP_CALL_INFO_T call_info;
call_info.num_active = 0;
call_info.num_held = 0;
call_info.callsetup_state = 0;
call_info.phone_type = 0x81;
char *str = "1234567";
bmemcpy(&call_info.phone_number, str, strlen(str) + 1);
call_info.phone_len = strlen(str) + 1;
bt_interface_phone_state_changed(&call_info);


// a call outgoing dialing
HFP_CALL_INFO_T call_info;
call_info.num_active = 0;
call_info.num_held = 0;
call_info.callsetup_state = 2;
call_info.phone_type = 0x81;
char *str = "1234567";
bmemcpy(&call_info.phone_number, str, strlen(str) + 1);
call_info.phone_len = strlen(str) + 1;
bt_interface_phone_state_changed(&call_info);

// a call outgoing alerting
HFP_CALL_INFO_T call_info;
call_info.num_active = 0;
call_info.num_held = 0;
call_info.callsetup_state = 3;
call_info.phone_type = 0x81;
char *str = "1234567";
bmemcpy(&call_info.phone_number, str, strlen(str) + 1);
call_info.phone_len = strlen(str) + 1;
bt_interface_phone_state_changed(&call_info);

// a call active
HFP_CALL_INFO_T call_info;
call_info.num_active = 1;
call_info.num_held = 0;
call_info.callsetup_state = 0;
call_info.phone_type = 0x81;
char *str = "1234567";
bmemcpy(&call_info.phone_number, str, strlen(str) + 1);
call_info.phone_len = strlen(str) + 1;
bt_interface_phone_state_changed(&call_info);

// a call idle
HFP_CALL_INFO_T call_info;
call_info.num_active = 0;
call_info.num_held = 0;
call_info.callsetup_state = 0;
call_info.phone_type = 0x81;
char *str = "1234567";
bmemcpy(&call_info.phone_number, str, strlen(str) + 1);
call_info.phone_len = strlen(str) + 1;
bt_interface_phone_state_changed(&call_info);

AG Information Synchronization Processing

  • AT cmd:

    • +CIEV: unsolicited result code (Standard indicator events reporting unsolicited result code)

    • Format: +CIEV: < ind >,< value >

    • When AG indicators change, AG needs to actively notify HF side using +ciev AT cmd

  • Indicator status value update

    • When AG end indicators change, AG needs to actively update status

    • bts2_app_interface status update interface: bt_interface_indicator_status_changed

    • bts2_app_hfp_ag status update interface: bt_hfp_ag_ind_status_update

    • Common indicator types are shown in the following figure: Figure 1: indicator type

    • Service status update demo:

typedef struct
{
    uint8_t ind_type;
    uint8_t ind_val;
} hfp_ind_info_t;

enum
{
    HFP_AG_CIND_SERVICE_TYPE = 0x01,    //(0,1)
    HFP_AG_CIND_CALL_TYPE,              //(0,1)
    HFP_AG_CIND_CALLSETUP_TYPE,         //(0,3)
    HFP_AG_CIND_BATT_TYPE,              //(0,5)
    HFP_AG_CIND_SIGNAL_TYPE,            //(0,5)
    HFP_AG_CIND_ROAM_TYPE,              //(0,1)
    HFP_AG_CIND_CALLHELD_TYPE,          //(0,2)
};

hfp_ind_info_t ind_info;
ind_info.ind_type = HFP_AG_CIND_SERVICE_TYPE;
ind_info.ind_val = 3;
bt_interface_indicator_status_changed(&ind_info);

AG Local Number Synchronization

  • AT cmd:

    • AT+CNUM: Subscriber Number Information

    • Format: AT+CNUM

    • This command is used to query local number

    • +CNUM: Subscriber Number Information

    • Format: +CNUM: ,< number >,< type >[, < service >]

    • After AG receives this request, it will reply the phone’s local number to HF side through +CNUM.

  • Event when receiving HF end request for local number: BT_NOTIFY_AG_GET_LOCAL_PHONE_INFO_REQ

  • Interface to reply local number:

    • bts2_app_interface local number reply interface: bt_interface_local_phone_info_res

    • bts2_app_hfp_ag local number reply interface: bt_hfp_ag_cnum_response

typedef struct
{
    char phone_number[PHONE_NUM_LEN];
    U8 type;
} hfp_phone_number_t;

hfp_phone_number_t local_phone_num;
char *str = "19396395979";
bmemcpy(&local_phone_num.phone_number, str, strlen(str));
local_phone_num.type = 0x81;
bt_interface_local_phone_info_res(&local_phone_num);

AG All Call Status Information

  • AT cmd:

    • AT+CLCC: Standard list current calls command

    • Format: AT+CLCC

    • +CLCC: Standard list current calls command

    • Format: +CLCC: < idx >,< dir >,< status >,< mode >,< mpty >,< number >,< type >

    • HF requests current call information list, AG side replies current call information list through “+CLCC”. If there are currently no calls, AG side must also reply with OK command.

  • Event when receiving HF end request for all call status information: BT_NOTIFY_AG_GET_ALL_REMT_CALLS_INFO_REQ

  • Interface to reply all call status information:

    • bts2_app_interface all call status information interface: bt_interface_remote_call_info_res

    • bts2_app_hfp_ag all call status information interface: bt_hfp_ag_remote_calls_res_hdl

typedef struct
{
    // Number of phone calls
    U8 num_call;
    hfp_phone_call_info_t *calls;
} hfp_remote_calls_info_t;

typedef struct
{
    // Which call this is, counting from 1
    U8 call_idx;
    // Call direction, 0: outgoing call; 1: incoming call
    U8 call_dir;
    // 0: Active
    // 1: held
    // 2: Dialing (outgoing calls only)
    // 3: Alerting (outgoing calls only)
    // 4: Incoming (incoming calls only)
    // 5: Waiting (incoming calls only)
    // 6: Call held by Response and Hold
    U8 call_status;
    // Call mode, 0 (Voice), 1 (Data), 2 (FAX)
    U8 call_mode;
    // Whether it's a conference call. 0: not conference call, 1: is conference call
    U8 call_mtpty;
    // Phone number + phone number type
    hfp_phone_number_t phone_info;
} hfp_phone_call_info_t;

hfp_phone_call_info_t call_info;
bmemset(&call_info, 0x00, sizeof(hfp_phone_call_info_t));
call_info.call_idx = 1;
call_info.call_dir = 1;
call_info.call_status = 1;
call_info.call_mode = 0;
call_info.call_mtpty = 0;
char *str = "123456";
bmemcpy(&call_info.phone_info.phone_number, str, strlen(str) + 1);
call_info.phone_info.type = 0x81;

hfp_remote_calls_info_t calls;
calls.num_call = 1;
calls.calls = &call_info;
bt_interface_remote_call_info_res((hfp_remote_calls_info_t *)calls);