Преглед изворни кода

Merge branch 'feature/hfp_hf_vendor_batt_level' into 'master'

components/bt: support for vendor specific battery level and docker status...

See merge request espressif/esp-idf!17601
Wei Tian Hua пре 3 година
родитељ
комит
dbe4606eab

+ 53 - 0
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) {

+ 46 - 0
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).

+ 7 - 2
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';
         }

+ 52 - 0
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;

+ 6 - 0
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;

+ 2 - 0
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;

+ 3 - 1
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);

+ 4 - 0
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 {

+ 41 - 0
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);
     }

+ 14 - 0
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;
 
 /************************************************************************************

+ 80 - 15
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 <dtmf>;       -- 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        <dtmf>  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, "<bat_level>", "battery level ranges from 0 to 9");
+        bat_args.docked = arg_int0(NULL, NULL, "<docked>", "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 = "<bat_level> <docked>",
+            .func = hf_cmd_tbl[bat].handler,
+            .argtable = &bat_args,
+        };
+        ESP_ERROR_CHECK(esp_console_cmd_register(&bat_cmd));
 }