diff --git a/components/bt/host/bluedroid/api/esp_hf_client_api.c b/components/bt/host/bluedroid/api/esp_hf_client_api.c index 62a32f75ad..4963272427 100644 --- a/components/bt/host/bluedroid/api/esp_hf_client_api.c +++ b/components/bt/host/bluedroid/api/esp_hf_client_api.c @@ -407,6 +407,59 @@ esp_err_t esp_hf_client_send_dtmf(char code) return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; } +esp_err_t esp_hf_client_send_xapl(char *information, uint32_t features) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (information == NULL || strlen(information) != ESP_BT_HF_AT_SEND_XAPL_LEN) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + btc_hf_client_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_SEND_XAPL_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + strcpy(arg.send_xapl.information, information); + arg.send_xapl.features = features; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_client_send_iphoneaccev(uint32_t bat_level, bool docked) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (bat_level > 9) { + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + btc_hf_client_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF_CLIENT; + msg.act = BTC_HF_CLIENT_SEND_IPHONEACCEV_EVT; + + memset(&arg, 0, sizeof(btc_hf_client_args_t)); + arg.send_iphoneaccev.bat_level = bat_level; + arg.send_iphoneaccev.docked = docked; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + + esp_err_t esp_hf_client_request_last_voice_tag_number(void) { if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { diff --git a/components/bt/host/bluedroid/api/include/api/esp_hf_client_api.h b/components/bt/host/bluedroid/api/include/api/esp_hf_client_api.h index 47a5d13b5b..1214669492 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_hf_client_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_hf_client_api.h @@ -17,6 +17,7 @@ extern "C" { #define ESP_BT_HF_CLIENT_NUMBER_LEN (32) #define ESP_BT_HF_CLIENT_OPERATOR_NAME_LEN (16) +#define ESP_BT_HF_AT_SEND_XAPL_LEN (14) /// Bluetooth HFP RFCOMM connection and service level connection status typedef enum { @@ -65,6 +66,13 @@ typedef enum { #define ESP_HF_CLIENT_CHLD_FEAT_MERGE 0x20 /* 3 Add held call to multiparty */ #define ESP_HF_CLIENT_CHLD_FEAT_MERGE_DETACH 0x40 /* 4 Connect two calls and leave(disconnect from multiparty) */ +/* XAPL feature masks*/ +#define ESP_HF_CLIENT_XAPL_FEAT_RESERVED 0x01 /* reserved */ +#define ESP_HF_CLIENT_XAPL_FEAT_BATTERY_REPORT 0x02 /* The accessory supports battery reporting (reserved only for battery operated accessories) */ +#define ESP_HF_CLIENT_XAPL_FEAT_DOCKED 0x04 /* The accessory is docked or powered (reserved only for battery operated accessories). */ +#define ESP_HF_CLIENT_XAPL_FEAT_SIRI_STATUS_REPORT 0x08 /* The accessory supports Siri status reporting */ +#define ESP_HF_CLIENT_XAPL_NR_STATUS_REPORT 0x10 /* the accessory supports noise reduction (NR) status reporting */ + /// HF CLIENT callback events typedef enum { ESP_HF_CLIENT_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */ @@ -565,6 +573,44 @@ esp_err_t esp_hf_client_retrieve_subscriber_info(void); */ esp_err_t esp_hf_client_send_dtmf(char code); +/** + * + * @brief Send command to enable Vendor sepecific feature to indicate battery level + * and docker status + * This is Apple-specific commands, but used by most device, including Android and Windows + * + * @param[in] information: XAPL vendorID-productID-version, such as "0505-1995-0610" + * vendorID: A string representation of the hex value of the vendor ID from the manufacturer, without the 0x prefix. + * productID: A string representation of the hex value of the product ID from the manufacturer, without the 0x prefix. + * version: The revision of the software + * features: A base-10 representation of a bit field. such as ESP_HF_CLIENT_XAPL_FEAT_BATTERY_REPORT + * + * @return + * - ESP_OK: Feature enable request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_send_xapl(char *information, uint32_t features); + +/** + * + * @brief Send Battery level and docker status + * Enable this feature using XAPL command first + * This is Apple-specific commands, but used by most device, including Android and Windows + * + * + * @param[in] bat_level: Battery Level: value between 0 and 9 + * docked: Dock State: false = undocked, true = docked + * + * @return + * - ESP_OK: battery level is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_client_send_iphoneaccev(uint32_t bat_level, bool docked); + /** * * @brief Request a phone number from AG corresponding to last voice tag recorded (send AT+BINP command). diff --git a/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_api.c b/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_api.c index 4cb1c9b953..5f0fcf660c 100644 --- a/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_api.c +++ b/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_api.c @@ -284,8 +284,13 @@ void BTA_HfClientSendAT(UINT16 handle, tBTA_HF_CLIENT_AT_CMD_TYPE at, UINT32 val p_buf->uint32_val2 = val2; if (str) { - strlcpy(p_buf->str, str, BTA_HF_CLIENT_NUMBER_LEN + 1); - p_buf->str[BTA_HF_CLIENT_NUMBER_LEN] = '\0'; + UINT32 str_len = strlen(str); + if (str_len > BTA_HF_CLIENT_MAX_LEN) { + APPL_TRACE_WARNING("%s, str length(%d) is more than %d, truncate it.", __FUNCTION__, str_len, BTA_HF_CLIENT_MAX_LEN); + str_len = BTA_HF_CLIENT_MAX_LEN; + } + + strlcpy(p_buf->str, str, str_len + 1); } else { p_buf->str[0] = '\0'; } diff --git a/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_at.c b/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_at.c index 184f9b2c0f..199bbcf978 100644 --- a/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_at.c +++ b/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_at.c @@ -1546,6 +1546,58 @@ void bta_hf_client_send_at_clcc(void) bta_hf_client_send_at(BTA_HF_CLIENT_AT_CLCC, buf, strlen(buf)); } + +void bta_hf_client_send_at_xapl(char *information, UINT32 features) +{ + APPL_TRACE_DEBUG("%s(%s, %u)", __FUNCTION__, information, features); + + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + + /* + Format: AT+XAPL=vendorID-productID-version,features + Parameters: + *vendorID: A string representation of the hex value of the vendor ID from the manufacturer, without the 0x prefix. + *productID: A string representation of the hex value of the product ID from the manufacturer, without the 0x prefix. + *version: The revision of the software. + *Fatures: A base-10 representation of a bit field. Available features are: + *Bit 0 = reserved + *Bit 1 = The accessory supports battery reporting (reserved only for battery operated accessories). + *Bit 2 = The accessory is docked or powered (reserved only for battery operated accessories). + *Bit 3 = The accessory supports Siri status reporting. + *Bit 4 = the accessory supports noise reduction (NR) status reporting. + *All other values are reserved. + */ + + snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+XAPL=%s,%u\r", information, features); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_XAPL, buf, strlen(buf)); + osi_free(buf); +} + +void bta_hf_client_send_at_iphoneaccev(UINT32 bat_level, BOOLEAN docked) +{ + APPL_TRACE_DEBUG("%s(%u, %s)", __FUNCTION__, bat_level, docked ? "docked" : "undocked"); + + char *buf = osi_malloc(BTA_HF_CLIENT_AT_MAX_LEN); + + /* + Format: AT+IPHONEACCEV=Number of key/value pairs,key1,val1,key2,val2,... + Parameters: + * Number of key/value pairs: The number of parameters coming next. + * key: the type of change being reported: + * 1 = Battery Level + * 2 = Dock State + * val: the value of the change: + * Battery Level: string value between '0' and '9' + * Dock State: 0 = undocked, 1 = docked + */ + + snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+IPHONEACCEV=2,1,%u,2,%u\r", bat_level, docked ? 1 : 0); + + bta_hf_client_send_at(BTA_HF_CLIENT_AT_IPHONEACCEV, buf, strlen(buf)); + osi_free(buf); +} + void bta_hf_client_send_at_bvra(BOOLEAN enable) { char *buf; diff --git a/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_cmd.c b/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_cmd.c index df761e4e92..08e321d351 100644 --- a/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_cmd.c +++ b/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_cmd.c @@ -77,6 +77,12 @@ void bta_hf_client_send_at_cmd(tBTA_HF_CLIENT_DATA *p_data) case BTA_HF_CLIENT_AT_CMD_NREC: bta_hf_client_send_at_nrec(); break; + case BTA_HF_CLIENT_AT_CMD_XAPL: + bta_hf_client_send_at_xapl(p_val->str, p_val->uint32_val1); + break; + case BTA_HF_CLIENT_AT_CMD_IPHONEACCEV: + bta_hf_client_send_at_iphoneaccev(p_val->uint32_val1, p_val->uint32_val1 == 0 ? FALSE : TRUE); + break; default: APPL_TRACE_ERROR("Default case, %s", __FUNCTION__); break; diff --git a/components/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_at.h b/components/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_at.h index 922e5e4548..caed82861b 100644 --- a/components/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_at.h +++ b/components/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_at.h @@ -70,6 +70,8 @@ enum { BTA_HF_CLIENT_AT_CNUM, BTA_HF_CLIENT_AT_NREC, BTA_HF_CLIENT_AT_BINP, + BTA_HF_CLIENT_AT_XAPL, + BTA_HF_CLIENT_AT_IPHONEACCEV, }; typedef UINT8 tBTA_HF_CLIENT_AT_CMD; diff --git a/components/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_int.h b/components/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_int.h index ddfc62442d..d77d1b0111 100644 --- a/components/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_int.h +++ b/components/bt/host/bluedroid/bta/hf_client/include/bta_hf_client_int.h @@ -117,7 +117,7 @@ typedef struct { UINT8 uint8_val; UINT32 uint32_val1; UINT32 uint32_val2; - char str[BTA_HF_CLIENT_NUMBER_LEN + 1]; + char str[BTA_HF_CLIENT_MAX_LEN + 1]; } tBTA_HF_CLIENT_DATA_VAL; /* union of all event datatypes */ @@ -268,6 +268,8 @@ extern void bta_hf_client_send_at_cnum(void); extern void bta_hf_client_send_at_nrec(void); extern void bta_hf_client_send_at_binp(UINT32 action); extern void bta_hf_client_send_at_bia(void); +extern void bta_hf_client_send_at_xapl(char *information, UINT32 features); +extern void bta_hf_client_send_at_iphoneaccev(UINT32 bat_level, BOOLEAN docked); /* Action functions */ extern void bta_hf_client_register(tBTA_HF_CLIENT_DATA *p_data); diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_hf_client_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_hf_client_api.h index e01b7a1c75..15e785e871 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_hf_client_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_hf_client_api.h @@ -153,9 +153,13 @@ typedef UINT8 tBTA_HF_CLIENT_IND_TYPE; #define BTA_HF_CLIENT_AT_CMD_BINP 13 #define BTA_HF_CLIENT_AT_CMD_BLDN 14 #define BTA_HF_CLIENT_AT_CMD_NREC 15 +#define BTA_HF_CLIENT_AT_CMD_XAPL 16 +#define BTA_HF_CLIENT_AT_CMD_IPHONEACCEV 17 typedef UINT8 tBTA_HF_CLIENT_AT_CMD_TYPE; +#define BTA_HF_CLIENT_MAX_LEN 32 + /* data associated with most non-AT events */ /* placeholder, if not needed should be removed*/ typedef struct { diff --git a/components/bt/host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c b/components/bt/host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c index d1d659325c..41248f8d92 100644 --- a/components/bt/host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c +++ b/components/bt/host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c @@ -598,6 +598,41 @@ static bt_status_t btc_hf_client_send_dtmf(char code) return BT_STATUS_SUCCESS; } +/******************************************************************************* +** +** Function btc_hf_client_send_xapl +** +** Description send xapl +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_send_xapl(char *buf, UINT32 features) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_XAPL, features, 0, buf); + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btc_hf_client_send_iphoneaccev +** +** Description send IPHONEACCEV +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_hf_client_send_iphoneaccev(uint32_t bat_level, BOOLEAN docked) +{ + CHECK_HF_CLIENT_SLC_CONNECTED(); + + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_IPHONEACCEV, bat_level, (UINT32)docked, NULL); + + return BT_STATUS_SUCCESS; +} /******************************************************************************* ** ** Function btc_hf_client_request_last_voice_tag_number @@ -1091,6 +1126,12 @@ void btc_hf_client_call_handler(btc_msg_t *msg) case BTC_HF_CLIENT_SEND_NREC_EVT: btc_hf_client_send_nrec(); break; + case BTC_HF_CLIENT_SEND_XAPL_EVT: + btc_hf_client_send_xapl(arg->send_xapl.information, arg->send_xapl.features); + break; + case BTC_HF_CLIENT_SEND_IPHONEACCEV_EVT: + btc_hf_client_send_iphoneaccev(arg->send_iphoneaccev.bat_level, arg->send_iphoneaccev.docked); + break; default: BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act); } diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_hf_client.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_hf_client.h index 4924bd910d..e8e65986a1 100644 --- a/components/bt/host/bluedroid/btc/profile/std/include/btc_hf_client.h +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_hf_client.h @@ -50,6 +50,8 @@ typedef enum { BTC_HF_CLIENT_REQUEST_LAST_VOICE_TAG_NUMBER_EVT, BTC_HF_CLIENT_REGISTER_DATA_CALLBACK_EVT, BTC_HF_CLIENT_SEND_NREC_EVT, + BTC_HF_CLIENT_SEND_XAPL_EVT, + BTC_HF_CLIENT_SEND_IPHONEACCEV_EVT, } btc_hf_client_act_t; /* btc_hf_client_args_t */ @@ -103,6 +105,18 @@ typedef union { esp_hf_client_incoming_data_cb_t recv; esp_hf_client_outgoing_data_cb_t send; } reg_data_cb; + + //BTC_HF_CLIENT_SEND_XAPL_EVT + struct send_xapl_args { + char information[ESP_BT_HF_AT_SEND_XAPL_LEN + 1]; + uint32_t features; + } send_xapl; + + // BTC_HF_CLIENT_SEND_IPHONEACCEV_EVT + struct send_iphoneaccev_args { + uint32_t bat_level; + bool docked; + } send_iphoneaccev; } btc_hf_client_args_t; /************************************************************************************ diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_hf/main/app_hf_msg_set.c b/examples/bluetooth/bluedroid/classic_bt/hfp_hf/main/app_hf_msg_set.c index ec166be192..381417d7d1 100644 --- a/examples/bluetooth/bluedroid/classic_bt/hfp_hf/main/app_hf_msg_set.c +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_hf/main/app_hf_msg_set.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -14,6 +14,27 @@ extern esp_bd_addr_t peer_addr; +typedef struct { + struct arg_str *tgt; + struct arg_str *vol; + struct arg_end *end; +} vu_args_t; + +typedef struct { + struct arg_str *btrh; + struct arg_end *end; +} rh_args_t; + +typedef struct { + struct arg_int *bat_level; + struct arg_int *docked; + struct arg_end *end; +} bat_args_t; + +static vu_args_t vu_args; +static rh_args_t rh_args; +static bat_args_t bat_args; + void hf_msg_show_usage(void) { printf("########################################################################\n"); @@ -45,6 +66,8 @@ void hf_msg_show_usage(void) printf(" 2 -reject the held call\n"); printf("hf k ; -- send dtmf code.\n"); printf(" dtmf: single character in set 0-9, *, #, A-D\n"); + printf("hf xp; -- Enable Vendor specific feature for battery status\n"); + printf("hf bat; -- Send battery status\n"); printf("hf h; -- show command manual\n"); printf("########################################################################\n"); } @@ -254,6 +277,35 @@ HF_CMD_HANDLER(dtmf) return 0; } +HF_CMD_HANDLER(xapl) +{ + printf("send XAPL feature enable command to indicate battery level\n"); + esp_hf_client_send_xapl("0505-1995-0610", ESP_HF_CLIENT_XAPL_FEAT_BATTERY_REPORT | ESP_HF_CLIENT_XAPL_FEAT_DOCKED); + return 0; +} + +HF_CMD_HANDLER(iphoneaccev) +{ + int nerrors = arg_parse(argn, argv, (void**) &bat_args); + if (nerrors != 0) { + arg_print_errors(stderr, bat_args.end, argv[0]); + return 1; + } + + int bat_level = bat_args.bat_level->ival[0]; + bool docked = bat_args.docked->ival[0] == 0 ? false : true; + + if (bat_level > 9 || bat_level < 0) { + printf("Invalid argument for battery level %d\n", bat_level); + return 1; + } + + printf("send battery level and docker status\n"); + esp_hf_client_send_iphoneaccev(bat_level, docked); + return 0; +} + + static hf_msg_hdl_t hf_cmd_tbl[] = { {0, "h", hf_help_handler}, {5, "con", hf_conn_handler}, @@ -274,6 +326,8 @@ static hf_msg_hdl_t hf_cmd_tbl[] = { {140, "rv", hf_request_last_voice_tag_handler}, {150, "rh", hf_btrh_handler}, {160, "k", hf_dtmf_handler}, + {170, "xp", hf_xapl_handler}, + {180, "bat", hf_iphoneaccev_handler}, }; hf_msg_hdl_t *hf_get_cmd_tbl(void) @@ -306,7 +360,9 @@ enum hf_cmd_name { rs, /*retrieve subscriber information*/ rv, /*retrieve last voice tag number*/ rh, /*response and hold*/ - k /*send dtmf code*/ + k, /*send dtmf code*/ + xp, /*send XAPL feature enable command to indicate battery level*/ + bat, /*send battery level and docker status*/ }; static char *hf_cmd_explain[] = { "show command manual", @@ -328,20 +384,9 @@ static char *hf_cmd_explain[] = { "retrieve last voice tag number", "response and hold", "send dtmf code.\n single character in set 0-9, *, #, A-D", + "send XAPL feature enable command to indicate battery level", + "send battery level and docker status.", }; -typedef struct { - struct arg_str *tgt; - struct arg_str *vol; - struct arg_end *end; -} vu_args_t; - -typedef struct { - struct arg_str *btrh; - struct arg_end *end; -} rh_args_t; - -static vu_args_t vu_args; -static rh_args_t rh_args; void register_hfp_hf(void) { @@ -496,4 +541,24 @@ void register_hfp_hf(void) .func = hf_cmd_tbl[k].handler, }; ESP_ERROR_CHECK(esp_console_cmd_register(&k_cmd)); + + const esp_console_cmd_t xp_cmd = { + .command = "xp", + .help = hf_cmd_explain[xp], + .hint = NULL, + .func = hf_cmd_tbl[xp].handler, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&xp_cmd)); + + bat_args.bat_level = arg_int0(NULL, NULL, "", "battery level ranges from 0 to 9"); + bat_args.docked = arg_int0(NULL, NULL, "", "0 - undocked; 1 - docked"); + bat_args.end = arg_end(1); + const esp_console_cmd_t bat_cmd = { + .command = "bat", + .help = hf_cmd_explain[bat], + .hint = " ", + .func = hf_cmd_tbl[bat].handler, + .argtable = &bat_args, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&bat_cmd)); }