Переглянути джерело

component/bt: implement Classic Bluetooth GAP APIs for device and service discovery

wangmengyang 8 роки тому
батько
коміт
c23af0b5bb

+ 105 - 3
components/bt/bluedroid/api/esp_gap_bt_api.c

@@ -20,7 +20,21 @@
 #include "btc_manage.h"
 #include "btc_gap_bt.h"
 
-#if BTC_GAP_BT_INCLUDED
+#if (BTC_GAP_BT_INCLUDED == TRUE)
+
+esp_err_t esp_bt_gap_register_callback(esp_bt_gap_cb_t callback)
+{
+    if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    if (callback == NULL) {
+        return ESP_FAIL;
+    }
+
+    btc_profile_cb_set(BTC_PID_GAP_BT, callback);
+    return ESP_OK;
+}
 
 esp_err_t esp_bt_gap_set_scan_mode(esp_bt_scan_mode_t mode)
 {
@@ -30,7 +44,7 @@ esp_err_t esp_bt_gap_set_scan_mode(esp_bt_scan_mode_t mode)
     if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
         return ESP_ERR_INVALID_STATE;
     }
-    
+
     msg.sig = BTC_SIG_API_CALL;
     msg.pid = BTC_PID_GAP_BT;
     msg.act = BTC_GAP_BT_ACT_SET_SCAN_MODE;
@@ -39,4 +53,92 @@ esp_err_t esp_bt_gap_set_scan_mode(esp_bt_scan_mode_t mode)
     return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
 }
 
-#endif /* #if BTC_GAP_BT_INCLUDED */
+esp_err_t esp_bt_gap_start_discovery(esp_bt_inq_mode_t mode, uint8_t inq_len, uint8_t num_rsps)
+{
+    btc_msg_t msg;
+    btc_gap_bt_args_t arg;
+
+    if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    if (mode != ESP_BT_INQ_MODE_GENERAL_INQUIRY &&
+            mode != ESP_BT_INQ_MODE_LIMITED_INQIURY) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    if (inq_len < ESP_BT_GAP_MIN_INQ_LEN ||
+            inq_len > ESP_BT_GAP_MAX_INQ_LEN) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    msg.sig = BTC_SIG_API_CALL;
+    msg.pid = BTC_PID_GAP_BT;
+    msg.act = BTC_GAP_BT_ACT_START_DISCOVERY;
+
+    arg.start_disc.mode = mode;
+    arg.start_disc.inq_len = inq_len;
+    arg.start_disc.num_rsps = num_rsps;
+
+    return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+esp_err_t esp_bt_gap_cancel_discovery(void)
+{
+    btc_msg_t msg;
+
+    if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    msg.sig = BTC_SIG_API_CALL;
+    msg.pid = BTC_PID_GAP_BT;
+    msg.act = BTC_GAP_BT_ACT_CANCEL_DISCOVERY;
+
+    return (btc_transfer_context(&msg, NULL, 0, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+esp_err_t esp_bt_gap_get_remote_services(esp_bd_addr_t remote_bda)
+{
+    btc_msg_t msg;
+    btc_gap_bt_args_t arg;
+
+    if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    msg.sig = BTC_SIG_API_CALL;
+    msg.pid = BTC_PID_GAP_BT;
+    msg.act = BTC_GAP_BT_ACT_GET_REMOTE_SERVICES;
+
+    memcpy(&arg.bda, remote_bda, sizeof(bt_bdaddr_t));
+    return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+esp_err_t esp_bt_gap_get_remote_service_record(esp_bd_addr_t remote_bda, esp_bt_uuid_t *uuid)
+{
+    btc_msg_t msg;
+    btc_gap_bt_args_t arg;
+
+    if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    msg.sig = BTC_SIG_API_CALL;
+    msg.pid = BTC_PID_GAP_BT;
+    msg.act = BTC_GAP_BT_ACT_GET_REMOTE_SERVICE_RECORD;
+
+    memcpy(&arg.get_rmt_srv_rcd.bda, remote_bda, sizeof(bt_bdaddr_t));
+    memcpy(&arg.get_rmt_srv_rcd.uuid, uuid, sizeof(esp_bt_uuid_t));
+    return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+uint8_t *esp_bt_gap_resolve_eir_data(uint8_t *eir, esp_bt_eir_type_t type, uint8_t *length)
+{
+    if (!eir) {
+        return NULL;
+    }
+
+    return BTM_CheckEirData(eir, type, length);
+}
+#endif /* #if BTC_GAP_BT_INCLUDED == TRUE */

+ 282 - 1
components/bt/bluedroid/api/include/esp_gap_bt_api.h

@@ -28,9 +28,227 @@ extern "C" {
 typedef enum {
     ESP_BT_SCAN_MODE_NONE = 0,                      /*!< Neither discoverable nor connectable */
     ESP_BT_SCAN_MODE_CONNECTABLE,                   /*!< Connectable but not discoverable */
-    ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE       /*!< both discoverable and connectaable */
+    ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE       /*!< both discoverable and connectable */
 } esp_bt_scan_mode_t;
 
+/// Bluetooth Device Property type
+typedef enum {
+    ESP_BT_GAP_DEV_PROP_BDNAME = 1,                 /*!< Bluetooth device name, value type is int8_t [] */
+    ESP_BT_GAP_DEV_PROP_COD,                        /*!< Class of Device, value type is uint32_t */
+    ESP_BT_GAP_DEV_PROP_RSSI,                       /*!< Received Signal strength Indication, value type is int8_t, ranging from -128 to 127 */
+    ESP_BT_GAP_DEV_PROP_EIR,                        /*!< Extended Inquiry Response, value type is uint8_t [] */
+} esp_bt_gap_dev_prop_type_t;
+
+/// Maximum bytes of Bluetooth device name
+#define ESP_BT_GAP_MAX_BDNAME_LEN             (248)
+
+/// Maximum size of EIR Significant part
+#define ESP_BT_GAP_EIR_DATA_LEN               (240)
+
+/// Bluetooth Device Property Descriptor
+typedef struct {
+    esp_bt_gap_dev_prop_type_t type;                /*!< device property type */
+    int len;                                        /*!< device property value length */
+    void *val;                                      /*!< devlice prpoerty value */
+} esp_bt_gap_dev_prop_t;
+
+/// Extended Inquiry Response data type
+typedef enum {
+    ESP_BT_EIR_TYPE_FLAGS                    = 0x01,     /*!< Flag with information such as BR/EDR and LE support */
+    ESP_BT_EIR_TYPE_INCMPL_16BITS_UUID       = 0x02,     /*!< Incomplete list of 16-bit service UUIDs */
+    ESP_BT_EIR_TYPE_CMPL_16BITS_UUID         = 0x03,     /*!< Complete list of 16-bit service UUIDs */
+    ESP_BT_EIR_TYPE_INCMPL_32BITS_UUID       = 0x04,     /*!< Incomplete list of 32-bit service UUIDs */
+    ESP_BT_EIR_TYPE_CMPL_32BITS_UUID         = 0x05,     /*!< Complete list of 32-bit service UUIDs */
+    ESP_BT_EIR_TYPE_INCMPL_128BITS_UUID      = 0x06,     /*!< Incomplete list of 128-bit service UUIDs */
+    ESP_BT_EIR_TYPE_CMPL_128BITS_UUID        = 0x07,     /*!< Complete list of 128-bit service UUIDs */
+    ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME         = 0x08,     /*!< Shortened Local Name */
+    ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME          = 0x09,     /*!< Complete Local Name */
+    ESP_BT_EIR_TYPE_TX_POWER_LEVEL           = 0x0a,     /*!< Tx power level, value is 1 octet ranging from  -127 to 127, unit is dBm*/
+    ESP_BT_EIR_TYPE_MANU_SPECIFIC            = 0xff,     /*!< Manufacturer specific data */
+} esp_bt_eir_type_t;
+
+/// Major service class field of Class of Device, mutiple bits can be set
+typedef enum {
+    ESP_BT_COD_SRVC_NONE                     =     0,    /*!< None indicates an invalid value */
+    ESP_BT_COD_SRVC_LMTD_DISCOVER            =   0x1,    /*!< Limited Discoverable Mode */
+    ESP_BT_COD_SRVC_POSITIONING              =   0x8,    /*!< Positioning (Location identification) */
+    ESP_BT_COD_SRVC_NETWORKING               =  0x10,    /*!< Networking, e.g. LAN, Ad hoc */
+    ESP_BT_COD_SRVC_RENDERING                =  0x20,    /*!< Rendering, e.g. Printing, Speakers */
+    ESP_BT_COD_SRVC_CAPTURING                =  0x40,    /*!< Capturing, e.g. Scanner, Microphone */
+    ESP_BT_COD_SRVC_OBJ_TRANSFER             =  0x80,    /*!< Object Transfer, e.g. v-Inbox, v-Folder */
+    ESP_BT_COD_SRVC_AUDIO                    = 0x100,    /*!< Audio, e.g. Speaker, Microphone, Headerset service */
+    ESP_BT_COD_SRVC_TELEPHONY                = 0x200,    /*!< Telephony, e.g. Cordless telephony, Modem, Headset service */
+    ESP_BT_COD_SRVC_INFORMATION              = 0x400,    /*!< Information, e.g., WEB-server, WAP-server */
+} esp_bt_cod_srvc_t;
+
+/// Bits of major service class field
+#define ESP_BT_COD_SRVC_BIT_MASK              (0xffe000) /*!< Major service bit mask */
+#define ESP_BT_COD_SRVC_BIT_OFFSET            (13)       /*!< Major service bit offset */
+
+/// Major device class field of Class of Device
+typedef enum {
+    ESP_BT_COD_MAJOR_DEV_MISC                = 0,    /*!< Miscellaneous */
+    ESP_BT_COD_MAJOR_DEV_COMPUTER            = 1,    /*!< Computer */
+    ESP_BT_COD_MAJOR_DEV_PHONE               = 2,    /*!< Phone(cellular, cordless, pay phone, modem */
+    ESP_BT_COD_MAJOR_DEV_LAN_NAP             = 3,    /*!< LAN, Network Access Point */
+    ESP_BT_COD_MAJOR_DEV_AV                  = 4,    /*!< Audio/Video(headset, speaker, stereo, video display, VCR */
+    ESP_BT_COD_MAJOR_DEV_PERIPHERAL          = 5,    /*!< Peripheral(mouse, joystick, keyboard) */
+    ESP_BT_COD_MAJOR_DEV_IMAGING             = 6,    /*!< Imaging(printer, scanner, camera, display */
+    ESP_BT_COD_MAJOR_DEV_WEARABLE            = 7,    /*!< Wearable */
+    ESP_BT_COD_MAJOR_DEV_TOY                 = 8,    /*!< Toy */
+    ESP_BT_COD_MAJOR_DEV_HEALTH              = 9,    /*!< Health */
+    ESP_BT_COD_MAJOR_DEV_UNCATEGORIZED       = 31,   /*!< Uncategorized: device not specified */
+} esp_bt_cod_major_dev_t;
+
+/// Bits of major device class field
+#define ESP_BT_COD_MAJOR_DEV_BIT_MASK         (0x1f00) /*!< Major device bit mask */
+#define ESP_BT_COD_MAJOR_DEV_BIT_OFFSET       (8)      /*!< Major device bit offset */
+
+/// Bits of minor device class field
+#define ESP_BT_COD_MINOR_DEV_BIT_MASK         (0xfc)   /*!< Minor device bit mask */
+#define ESP_BT_COD_MINOR_DEV_BIT_OFFSET       (2)      /*!< Minor device bit offset */
+
+/// Bits of format type
+#define ESP_BT_COD_FORMAT_TYPE_BIT_MASK       (0x03)   /*!< Format type bit mask */
+#define ESP_BT_COD_FORMAT_TYPE_BIT_OFFSET     (0)      /*!< Format type bit offset */
+
+/// Class of device format type 1
+#define ESP_BT_COD_FORMAT_TYPE_1              (0x00)
+
+/** Bluetooth Device Discovery state */
+typedef enum {
+    ESP_BT_GAP_DISCOVERY_STOPPED,                   /*!< device discovery stopped */
+    ESP_BT_GAP_DISCOVERY_STARTED,                   /*!< device discovery started */
+} esp_bt_gap_discovery_state_t;
+
+/// BT GAP callback events
+typedef enum {
+    ESP_BT_GAP_DISC_RES_EVT = 0,                    /*!< device discovery result event */
+    ESP_BT_GAP_DISC_STATE_CHANGED_EVT,              /*!< discovery state changed event */
+    ESP_BT_GAP_RMT_SRVCS_EVT,                       /*!< get remote services event */
+    ESP_BT_GAP_RMT_SRVC_REC_EVT,                    /*!< get remote service record event */
+} esp_bt_gap_cb_event_t;
+
+/** Inquiry Mode */
+typedef enum {
+    ESP_BT_INQ_MODE_GENERAL_INQUIRY,                /*!< General inquiry mode */
+    ESP_BT_INQ_MODE_LIMITED_INQIURY,                /*!< Limited inquiry mode */
+} esp_bt_inq_mode_t;
+
+/** Minimum and Maximum inquiry length*/
+#define ESP_BT_GAP_MIN_INQ_LEN                (0x01)  /*!< Minimum inquiry duration, unit is 1.28s */
+#define ESP_BT_GAP_MAX_INQ_LEN                (0x30)  /*!< Maximum inquiry duration, unit is 1.28s */
+
+/// A2DP state callback parameters
+typedef union {
+    /**
+     * @brief ESP_BT_GAP_DISC_RES_EVT
+     */
+    struct disc_res_param {
+        esp_bd_addr_t bda;                     /*!< remote bluetooth device address*/
+        int num_prop;                          /*!< number of properties got */
+        esp_bt_gap_dev_prop_t *prop;           /*!< properties discovered from the new device */
+    } disc_res;                                /*!< discovery result paramter struct */
+
+    /**
+     * @brief  ESP_BT_GAP_DISC_STATE_CHANGED_EVT
+     */
+    struct disc_state_changed_param {
+        esp_bt_gap_discovery_state_t state;    /*!< discovery state */
+    } disc_st_chg;                             /*!< discovery state changed parameter struct */
+
+    /**
+     * @brief ESP_BT_GAP_RMT_SRVCS_EVT
+     */
+    struct rmt_srvcs_param {
+        esp_bd_addr_t bda;                     /*!< remote bluetooth device address*/
+        esp_bt_status_t stat;                  /*!< service search status */
+        int num_uuids;                         /*!< number of UUID in uuid_list */
+        esp_bt_uuid_t *uuid_list;              /*!< list of service UUIDs of remote device */
+    } rmt_srvcs;                               /*!< services of remote device parameter struct */
+
+    /**
+     * @brief ESP_BT_GAP_RMT_SRVC_REC_EVT
+     */
+    struct rmt_srvc_rec_param {
+        esp_bd_addr_t bda;                     /*!< remote bluetooth device address*/
+        esp_bt_status_t stat;                  /*!< service search status */
+    } rmt_srvc_rec;                            /*!< specific service record from remote device parameter struct */
+} esp_bt_gap_cb_param_t;
+
+/**
+ * @brief           bluetooth GAP callback function type
+ * @param           event : Event type
+ * @param           param : Pointer to callback parameter
+ */
+typedef void (* esp_bt_gap_cb_t)(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param);
+
+/**
+ * @brief           get major service field of COD
+ * @param[in]       cod: Class of Device
+ * @return          major service bits
+ */
+inline uint32_t esp_bt_gap_get_cod_srvc(uint32_t cod)
+{
+    return (cod & ESP_BT_COD_SRVC_BIT_MASK) >> ESP_BT_COD_SRVC_BIT_OFFSET;
+}
+
+/**
+ * @brief           get major device field of COD
+ * @param[in]       cod: Class of Device
+ * @return          major device bits
+ */
+inline uint32_t esp_bt_gap_get_cod_major_dev(uint32_t cod)
+{
+    return (cod & ESP_BT_COD_MAJOR_DEV_BIT_MASK) >> ESP_BT_COD_MAJOR_DEV_BIT_OFFSET;
+}
+
+/**
+ * @brief           get minor service field of COD
+ * @param[in]       cod: Class of Device
+ * @return          minor service bits
+ */
+inline uint32_t esp_bt_gap_get_cod_minor_dev(uint32_t cod)
+{
+    return (cod & ESP_BT_COD_MINOR_DEV_BIT_MASK) >> ESP_BT_COD_MINOR_DEV_BIT_OFFSET;
+}
+
+/**
+ * @brief           get format type of COD
+ * @param[in]       cod: Class of Device
+ * @return          format type
+ */
+inline uint32_t esp_bt_gap_get_cod_format_type(uint32_t cod)
+{
+    return (cod & ESP_BT_COD_FORMAT_TYPE_BIT_MASK);
+}
+
+/**
+ * @brief           decide the integrity of COD
+ * @param[in]       cod: Class of Device
+ * @return
+ *                  - true if cod is valid
+ *                  - false otherise
+ */
+inline bool esp_bt_gap_is_valid_cod(uint32_t cod)
+{
+    if (esp_bt_gap_get_cod_format_type(cod) == ESP_BT_COD_FORMAT_TYPE_1 &&
+            esp_bt_gap_get_cod_srvc(cod) != ESP_BT_COD_SRVC_NONE) {
+        return true;
+    }
+
+    return false;
+}
+
+/**
+ * @brief           register callback function. This function should be called after esp_bluedroid_enable() completes successfully
+ *
+ * @return
+ *                  - ESP_OK : Succeed
+ *                  - ESP_FAIL: others
+ */
+esp_err_t esp_bt_gap_register_callback(esp_bt_gap_cb_t callback);
+
 /**
  * @brief           Set discoverability and connectability mode for legacy bluetooth. This function should
  *                  be called after esp_bluedroid_enable() completes successfully
@@ -45,6 +263,69 @@ typedef enum {
  */
 esp_err_t esp_bt_gap_set_scan_mode(esp_bt_scan_mode_t mode);
 
+/**
+ * @brief           Start device discovery. This function should be called after esp_bluedroid_enable() completes successfully.
+ *                  esp_bt_gap_cb_t will is called with ESP_BT_GAP_DISC_STATE_CHANGED_EVT if discovery is started or halted.
+ *                  esp_bt_gap_cb_t will is called with ESP_BT_GAP_DISC_RES_EVT if discovery result is got.
+ *
+ * @param[in]       mode - inquiry mode
+ * @param[in]       inq_len - inquiry duration in 1.28 sec units, ranging from 0x01 to 0x30
+ * @param[in]       num_rsps - number of inquiry responses that can be received, value 0 indicates an unlimited number of responses
+ *
+ * @return
+ *                  - ESP_OK : Succeed
+ *                  - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
+ *                  - ESP_ERR_INVALID_ARG: if invalid parameters are provided
+ *                  - ESP_FAIL: others
+ */
+esp_err_t esp_bt_gap_start_discovery(esp_bt_inq_mode_t mode, uint8_t inq_len, uint8_t num_rsps);
+
+/**
+ * @brief           Cancel device discovery. This function should be called after esp_bluedroid_enable() completes successfully
+ *                  esp_bt_gap_cb_t will is called with ESP_BT_GAP_DISC_STATE_CHANGED_EVT if discovery is stopped.
+ *
+ * @return
+ *                  - ESP_OK : Succeed
+ *                  - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
+ *                  - ESP_FAIL: others
+ */
+esp_err_t esp_bt_gap_cancel_discovery(void);
+
+/**
+ * @brief           Start SDP to get remote services. This function should be called after esp_bluedroid_enable() completes successfully.
+ *                  esp_bt_gap_cb_t will is called with ESP_BT_GAP_RMT_SRVCS_EVT after service discovery ends
+ *
+ * @return
+ *                  - ESP_OK : Succeed
+ *                  - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
+ *                  - ESP_FAIL: others
+ */
+esp_err_t esp_bt_gap_get_remote_services(esp_bd_addr_t remote_bda);
+
+/**
+ * @brief           Start SDP to look up the service matching uuid on the remote device. This function should be called after
+ *                  esp_bluedroid_enable() completes successfully
+ *
+ *                  esp_bt_gap_cb_t will is called with ESP_BT_GAP_RMT_SRVC_REC_EVT after service discovery ends
+ * @return
+ *                  - ESP_OK : Succeed
+ *                  - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
+ *                  - ESP_FAIL: others
+ */
+esp_err_t esp_bt_gap_get_remote_service_record(esp_bd_addr_t remote_bda, esp_bt_uuid_t *uuid);
+
+/**
+ * @brief           This function is called to get EIR data for a specific type.
+ *
+ * @param[in]       eir - pointer of raw eir data to be resolved
+ * @param[in]       type   - specific EIR data type
+ * @param[out]      length - return the length of EIR data excluding fields of length and data type
+ *
+ * @return          pointer of starting position of eir data excluding eir data type, NULL if not found
+ *
+ */
+uint8_t *esp_bt_gap_resolve_eir_data(uint8_t *eir, esp_bt_eir_type_t type, uint8_t *length);
+
 #ifdef __cplusplus
 }
 #endif

+ 9 - 15
components/bt/bluedroid/bta/dm/bta_dm_act.c

@@ -1560,11 +1560,9 @@ void bta_dm_sdp_result (tBTA_DM_MSG *p_data)
             || (p_data->sdp_event.sdp_result == SDP_DB_FULL)) {
         APPL_TRACE_DEBUG("sdp_result::0x%x", p_data->sdp_event.sdp_result);
         do {
-
             p_sdp_rec = NULL;
             if ( bta_dm_search_cb.service_index == (BTA_USER_SERVICE_ID + 1) ) {
                 p_sdp_rec = SDP_FindServiceUUIDInDb(bta_dm_search_cb.p_sdp_db, &bta_dm_search_cb.uuid, p_sdp_rec);
-
                 if (p_sdp_rec && SDP_FindProtocolListElemInRec(p_sdp_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
                     bta_dm_search_cb.peer_scn = (UINT8) pe.params[0];
                     scn_found = TRUE;
@@ -1580,7 +1578,6 @@ void bta_dm_sdp_result (tBTA_DM_MSG *p_data)
                     p_uuid +=  (bta_dm_search_cb.num_uuid - bta_dm_search_cb.uuid_to_search);
                     /* only support 16 bits UUID for now */
                     service = p_uuid->uu.uuid16;
-
                 }
                 /* all GATT based services */
                 do {
@@ -1613,7 +1610,7 @@ void bta_dm_sdp_result (tBTA_DM_MSG *p_data)
                 if (((p_data->sdp_event.sdp_result == SDP_DB_FULL) &&
                         bta_dm_search_cb.services != BTA_ALL_SERVICE_MASK) ||
                         (p_sdp_rec != NULL)) {
-                    if (service != UUID_SERVCLASS_PNP_INFORMATION) {
+                    if (service != UUID_SERVCLASS_PNP_INFORMATION && service != 0) {
                         UINT16 tmp_svc = 0xFFFF;
                         bta_dm_search_cb.services_found |=
                             (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index - 1));
@@ -1640,11 +1637,8 @@ void bta_dm_sdp_result (tBTA_DM_MSG *p_data)
             } else { /* regular one service per search or PNP search */
                 break;
             }
-
         } while (bta_dm_search_cb.service_index <= BTA_MAX_SERVICE_ID);
 
-//        osi_free(bta_dm_search_cb.p_sdp_db);
-//        bta_dm_search_cb.p_sdp_db = NULL;
         APPL_TRACE_DEBUG("%s services_found = %04x", __FUNCTION__,
                          bta_dm_search_cb.services_found);
 
@@ -2054,13 +2048,13 @@ static void bta_dm_find_services ( BD_ADDR bd_addr)
     memset (&uuid, 0, sizeof(tSDP_UUID));
 
     while (bta_dm_search_cb.service_index < BTA_MAX_SERVICE_ID) {
-        if ( bta_dm_search_cb.services_to_search
-                & (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index))) {
+        tBTA_SERVICE_MASK this_service_mask = (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index));
+        if ( bta_dm_search_cb.services_to_search & this_service_mask) {
             if ((bta_dm_search_cb.p_sdp_db = (tSDP_DISCOVERY_DB *)osi_malloc(BTA_DM_SDP_DB_SIZE)) != NULL) {
                 APPL_TRACE_DEBUG("bta_dm_search_cb.services = %04x***********", bta_dm_search_cb.services);
                 /* try to search all services by search based on L2CAP UUID */
                 if (bta_dm_search_cb.services == BTA_ALL_SERVICE_MASK ) {
-                    LOG_INFO("%s services_to_search=%08x", __func__, bta_dm_search_cb.services_to_search);
+                    APPL_TRACE_DEBUG("%s services_to_search=%08x", __func__, bta_dm_search_cb.services_to_search);
                     if (bta_dm_search_cb.services_to_search & BTA_RES_SERVICE_MASK) {
                         uuid.uu.uuid16 = bta_service_id_to_uuid_lkup_tbl[0];
                         bta_dm_search_cb.services_to_search &= ~BTA_RES_SERVICE_MASK;
@@ -2071,7 +2065,7 @@ static void bta_dm_find_services ( BD_ADDR bd_addr)
                 } else {
 #if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE
                     /* for LE only profile */
-                    if (bta_dm_search_cb.service_index == BTA_BLE_SERVICE_ID) {
+                    if (this_service_mask == BTA_BLE_SERVICE_MASK) {
                         if (bta_dm_search_cb.uuid_to_search > 0 && bta_dm_search_cb.p_srvc_uuid) {
                             memcpy(&uuid,
                                    (const void *)(bta_dm_search_cb.p_srvc_uuid + \
@@ -2102,11 +2096,11 @@ static void bta_dm_find_services ( BD_ADDR bd_addr)
                     uuid.len = LEN_UUID_16;
                 }
 
-                if (bta_dm_search_cb.service_index == BTA_USER_SERVICE_ID) {
+                if (this_service_mask == BTA_USER_SERVICE_MASK) {
                     memcpy(&uuid, &bta_dm_search_cb.uuid, sizeof(tSDP_UUID));
                 }
 
-                LOG_INFO("%s search UUID = %04x", __func__, uuid.uu.uuid16);
+                APPL_TRACE_DEBUG("%s search UUID = %04x", __func__, uuid.uu.uuid16);
                 SDP_InitDiscoveryDb (bta_dm_search_cb.p_sdp_db, BTA_DM_SDP_DB_SIZE, 1, &uuid, 0, NULL);
 
                 memset(g_disc_raw_data_buf, 0, sizeof(g_disc_raw_data_buf));
@@ -2123,9 +2117,9 @@ static void bta_dm_find_services ( BD_ADDR bd_addr)
 
                 } else {
 #if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE
-                    if ((bta_dm_search_cb.service_index == BTA_BLE_SERVICE_ID &&
+                    if ((this_service_mask == BTA_BLE_SERVICE_MASK &&
                             bta_dm_search_cb.uuid_to_search == 0) ||
-                            bta_dm_search_cb.service_index != BTA_BLE_SERVICE_ID)
+                            this_service_mask != BTA_BLE_SERVICE_MASK)
 #endif
                         bta_dm_search_cb.service_index++;
                     return;

+ 11 - 0
components/bt/bluedroid/btc/core/btc_dm.c

@@ -28,6 +28,9 @@
 #include "bta_gatt_api.h"
 #include "allocator.h"
 
+#if (BTC_GAP_BT_INCLUDED == TRUE)
+#include "btc_gap_bt.h"
+#endif /* BTC_GAP_BT_INCLUDED == TRUE */
 
 /******************************************************************************
 **  Constants & Macros
@@ -481,6 +484,14 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg)
         break;
     }
     case BTA_DM_BUSY_LEVEL_EVT:
+#if (BTC_GAP_BT_INCLUDED == TRUE)
+        {
+        if (p_data->busy_level.level_flags & BTM_BL_INQUIRY_PAGING_MASK) {
+            btc_gap_bt_busy_level_updated(p_data->busy_level.level_flags);
+        }
+        break;
+        }
+#endif /* BTC_GAP_BT_INCLUDED  == TRUE */
     case BTA_DM_LINK_UP_EVT:
     case BTA_DM_LINK_DOWN_EVT:
     case BTA_DM_HW_ERROR_EVT:

+ 5 - 0
components/bt/bluedroid/btc/core/btc_task.c

@@ -14,6 +14,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include "bt_target.h"
 #include "btc_task.h"
 #include "bt_trace.h"
 #include "thread.h"
@@ -29,7 +30,9 @@
 #include "btc_alarm.h"
 #include "bta_gatt_api.h"
 #if CONFIG_CLASSIC_BT_ENABLED
+#if (BTC_GAP_BT_INCLUDED == TRUE)
 #include "btc_gap_bt.h"
+#endif /* BTC_GAP_BT_INCLUDED == TRUE */
 #include "btc_profile_queue.h"
 #include "btc_av.h"
 #include "btc_avrc.h"
@@ -57,7 +60,9 @@ static btc_func_t profile_tab[BTC_PID_NUM] = {
     [BTC_PID_DM_SEC]    = {NULL,                        btc_dm_sec_cb_handler   },
     [BTC_PID_ALARM]     = {btc_alarm_handler,           NULL                    },
 #if CONFIG_CLASSIC_BT_ENABLED
+#if (BTC_GAP_BT_INCLUDED == TRUE)
     [BTC_PID_GAP_BT]    = {btc_gap_bt_call_handler,     NULL                    },
+#endif /* (BTC_GAP_BT_INCLUDED == TRUE) */
     [BTC_PID_PRF_QUE]   = {btc_profile_queue_handler,   NULL                    },
     [BTC_PID_A2DP]      = {btc_a2dp_call_handler,       btc_a2dp_cb_handler     },
     [BTC_PID_AVRC]      = {btc_avrc_call_handler,       NULL                    },

+ 19 - 53
components/bt/bluedroid/btc/core/btc_util.c

@@ -129,6 +129,7 @@ UINT32 devclass2uint(DEV_CLASS dev_class)
     }
     return cod;
 }
+
 void uint2devclass(UINT32 cod, DEV_CLASS dev_class)
 {
     dev_class[2] = (UINT8)cod;
@@ -136,61 +137,26 @@ void uint2devclass(UINT32 cod, DEV_CLASS dev_class)
     dev_class[0] = (UINT8)(cod >> 16);
 }
 
-static const UINT8  sdp_base_uuid[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
-                                       0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
-                                      };
-
-void uuid16_to_uuid128(uint16_t uuid16, bt_uuid_t *uuid128)
-{
-    uint16_t uuid16_bo;
-    memset(uuid128, 0, sizeof(bt_uuid_t));
-
-    memcpy(uuid128->uu, sdp_base_uuid, MAX_UUID_SIZE);
-    uuid16_bo = ntohs(uuid16);
-    memcpy(uuid128->uu + 2, &uuid16_bo, sizeof(uint16_t));
-}
+static const UINT8  base_uuid_be[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+                                      0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
 
-void string_to_uuid(char *str, bt_uuid_t *p_uuid)
+void uuid128_be_to_esp_uuid(esp_bt_uuid_t *u, uint8_t* uuid128)
 {
-    uint32_t uuid0, uuid4;
-    uint16_t uuid1, uuid2, uuid3, uuid5;
-
-    sscanf(str, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
-           &uuid0, &uuid1, &uuid2, &uuid3, &uuid4, &uuid5);
-
-    uuid0 = htonl(uuid0);
-    uuid1 = htons(uuid1);
-    uuid2 = htons(uuid2);
-    uuid3 = htons(uuid3);
-    uuid4 = htonl(uuid4);
-    uuid5 = htons(uuid5);
-
-    memcpy(&(p_uuid->uu[0]), &uuid0, 4);
-    memcpy(&(p_uuid->uu[4]), &uuid1, 2);
-    memcpy(&(p_uuid->uu[6]), &uuid2, 2);
-    memcpy(&(p_uuid->uu[8]), &uuid3, 2);
-    memcpy(&(p_uuid->uu[10]), &uuid4, 4);
-    memcpy(&(p_uuid->uu[14]), &uuid5, 2);
-
-    return;
-
-}
+    if (memcmp(base_uuid_be+4, uuid128 + 4, 12) != 0) {
+        u->len = ESP_UUID_LEN_128;
+        uint8_t *p_i = uuid128 + ESP_UUID_LEN_128 - 1;
+        uint8_t *p_o = u->uuid.uuid128;
+        uint8_t *p_end = p_o + ESP_UUID_LEN_128;
+        for (; p_o != p_end; *p_o++ = *p_i--)
+            ;
+    } else if (uuid128[0] == 0 && uuid128[1] == 0) {
+        u->len = 2;
+        u->uuid.uuid16 = (uuid128[2] << 8) + uuid128[3];
+    } else {
+        u->len = 4;
+        u->uuid.uuid32 = (uuid128[2] << 8) + uuid128[3];
+        u->uuid.uuid32 += (uuid128[0] << 24) + (uuid128[1] << 16);
+    }
 
-void uuid_to_string_legacy(bt_uuid_t *p_uuid, char *str)
-{
-    uint32_t uuid0, uuid4;
-    uint16_t uuid1, uuid2, uuid3, uuid5;
-
-    memcpy(&uuid0, &(p_uuid->uu[0]), 4);
-    memcpy(&uuid1, &(p_uuid->uu[4]), 2);
-    memcpy(&uuid2, &(p_uuid->uu[6]), 2);
-    memcpy(&uuid3, &(p_uuid->uu[8]), 2);
-    memcpy(&uuid4, &(p_uuid->uu[10]), 4);
-    memcpy(&uuid5, &(p_uuid->uu[14]), 2);
-
-    sprintf((char *)str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
-            ntohl(uuid0), ntohs(uuid1),
-            ntohs(uuid2), ntohs(uuid3),
-            ntohl(uuid4), ntohs(uuid5));
     return;
 }

+ 2 - 4
components/bt/bluedroid/btc/include/btc_util.h

@@ -18,6 +18,7 @@
 #include <stdbool.h>
 #include "bt_types.h"
 #include "bt_defs.h"
+#include "esp_bt_defs.h"
 
 /*******************************************************************************
 **  Constants & Macros
@@ -39,9 +40,6 @@ const char *dump_rc_pdu(UINT8 pdu);
 
 UINT32 devclass2uint(DEV_CLASS dev_class);
 void uint2devclass(UINT32 dev, DEV_CLASS dev_class);
-void uuid16_to_uuid128(uint16_t uuid16, bt_uuid_t *uuid128);
-
-void uuid_to_string_legacy(bt_uuid_t *p_uuid, char *str);
-void string_to_uuid(char *str, bt_uuid_t *p_uuid);
+void uuid128_be_to_esp_uuid(esp_bt_uuid_t *u, uint8_t* uuid128);
 
 #endif /*  __BTC_UTIL_H__ */

+ 561 - 18
components/bt/bluedroid/btc/profile/std/gap/btc_gap_bt.c

@@ -12,30 +12,37 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <string.h>
+#include "esp_bt_defs.h"
 #include "esp_gap_bt_api.h"
 #include "btc_gap_bt.h"
 #include "bta_api.h"
 #include "bt_trace.h"
-#include <string.h>
 #include "bt_target.h"
+#include "btc_manage.h"
+#include "btc_util.h"
+#include "allocator.h"
 
-#if BTC_GAP_BT_INCLUDED
-void btc_gap_bt_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
-{
-    switch (msg->act) {
-    default:
-        // LOG_ERROR("Unhandled deep copy %d\n", msg->act);
-        break;
-    }
-}
+#if (BTC_GAP_BT_INCLUDED == TRUE)
+
+#define COD_UNCLASSIFIED ((0x1F) << 8)
+
+#define BTC_STORAGE_FILL_PROPERTY(p_prop, t, l, p_v) \
+         (p_prop)->type = t;(p_prop)->len = l; (p_prop)->val = (p_v);
+
+static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data);
+static void bte_dm_search_services_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data);
+static void bte_dm_remote_service_record_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data);
+static void search_services_copy_cb(btc_msg_t *msg, void *p_dest, void *p_src);
+static void search_service_record_copy_cb(btc_msg_t *msg, void *p_dest, void *p_src);
 
-static void btc_gap_bt_arg_deep_free(btc_msg_t *msg)
+static bool btc_gap_bt_inquiry_in_progress = false;
+
+static inline void btc_gap_bt_cb_to_app(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
 {
-    LOG_DEBUG("%s \n", __func__);
-    switch (msg->act) {
-    default:
-        // LOG_DEBUG("Unhandled deep free %d\n", msg->act);
-        break;
+    esp_bt_gap_cb_t cb = (esp_bt_gap_cb_t)btc_profile_cb_get(BTC_PID_GAP_BT);
+    if (cb) {
+        cb(event, param);
     }
 }
 
@@ -69,6 +76,497 @@ static void btc_bt_set_scan_mode(esp_bt_scan_mode_t mode)
     return;
 }
 
+static void btc_gap_bt_start_discovery(btc_gap_bt_args_t *arg)
+{
+    tBTA_DM_INQ inq_params;
+    tBTA_SERVICE_MASK services = 0;
+
+    BTIF_TRACE_EVENT("%s", __FUNCTION__);
+
+    inq_params.mode = (arg->start_disc.mode == ESP_BT_INQ_MODE_GENERAL_INQUIRY) ?
+                      BTA_DM_GENERAL_INQUIRY : BTA_DM_LIMITED_INQUIRY;
+    inq_params.duration = arg->start_disc.inq_len;
+    inq_params.max_resps = arg->start_disc.num_rsps;
+
+    inq_params.report_dup = TRUE;
+    inq_params.filter_type = BTA_DM_INQ_CLR;
+    /* TODO: Filter device by BDA needs to be implemented here */
+
+    /* Will be enabled to TRUE once inquiry busy level has been received */
+    btc_gap_bt_inquiry_in_progress = FALSE;
+    /* find nearby devices */
+    BTA_DmSearch(&inq_params, services, bte_search_devices_evt);
+
+    return;
+}
+
+static void btc_gap_bt_cancel_discovery(void)
+{
+    BTA_DmSearchCancel();
+}
+
+static void btc_gap_bt_get_remote_services(bt_bdaddr_t *remote_bda)
+{
+    BTA_DmDiscover(remote_bda->address, BTA_ALL_SERVICE_MASK,
+                   bte_dm_search_services_evt, TRUE);
+}
+
+static void btc_gap_bt_get_remote_service_record(btc_gap_bt_args_t *arg)
+{
+    esp_bt_uuid_t *uuid = &arg->get_rmt_srv_rcd.uuid;
+    bt_bdaddr_t *remote_bda = &arg->get_rmt_srv_rcd.bda;
+
+    tSDP_UUID sdp_uuid;
+
+    sdp_uuid.len = uuid->len;
+    memcpy(&sdp_uuid.uu, &uuid->uuid, uuid->len);
+
+    BTA_DmDiscoverUUID(remote_bda->address, &sdp_uuid,
+                       bte_dm_remote_service_record_evt, TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function         search_devices_copy_cb
+**
+** Description      Deep copy callback for search devices event
+**
+** Returns          void
+**
+*******************************************************************************/
+static void search_devices_copy_cb(btc_msg_t *msg, void *p_dest, void *p_src)
+{
+    tBTA_DM_SEARCH *p_dest_data =  (tBTA_DM_SEARCH *) p_dest;
+    tBTA_DM_SEARCH *p_src_data =  (tBTA_DM_SEARCH *) p_src;
+
+    if (!p_src) {
+        return;
+    }
+
+    switch (msg->aid) {
+    case BTA_DM_INQ_RES_EVT: {
+        if (p_src_data->inq_res.p_eir) {
+            p_dest_data->inq_res.p_eir = (UINT8 *)(p_dest + sizeof(tBTA_DM_SEARCH));
+            memcpy(p_dest_data->inq_res.p_eir, p_src_data->inq_res.p_eir, HCI_EXT_INQ_RESPONSE_LEN);
+        }
+    }
+    break;
+
+    case BTA_DM_DISC_RES_EVT: {
+        if (p_src_data->disc_res.raw_data_size && p_src_data->disc_res.p_raw_data) {
+            p_dest_data->disc_res.p_raw_data = (UINT8 *)(p_dest + sizeof(tBTA_DM_SEARCH));
+            memcpy(p_dest_data->disc_res.p_raw_data,
+                   p_src_data->disc_res.p_raw_data,
+                   p_src_data->disc_res.raw_data_size);
+        }
+    }
+    break;
+    }
+}
+
+/*******************************************************************************
+**
+** Function         search_service_record_copy_cb
+**
+** Description      Deep copy callback for search service record event
+**
+** Returns          void
+**
+*******************************************************************************/
+static void search_service_record_copy_cb(btc_msg_t *msg, void *p_dest, void *p_src)
+{
+    tBTA_DM_SEARCH *p_dest_data =  (tBTA_DM_SEARCH *) p_dest;
+    tBTA_DM_SEARCH *p_src_data =  (tBTA_DM_SEARCH *) p_src;
+
+    if (!p_src) {
+        return;
+    }
+
+    switch (msg->aid) {
+    case BTA_DM_DISC_RES_EVT: {
+        if (p_src_data->disc_res.p_raw_data && p_src_data->disc_res.raw_data_size > 0) {
+            p_dest_data->disc_res.p_raw_data = (UINT8 *)(p_dest + sizeof(tBTA_DM_SEARCH));
+            memcpy(p_dest_data->disc_res.p_raw_data,
+                   p_src_data->disc_res.p_raw_data,
+                   p_src_data->disc_res.raw_data_size);
+        }
+    }
+    break;
+
+    default:
+        break;
+    }
+}
+
+/*******************************************************************************
+**
+** Function         check_eir_remote_name
+**
+** Description      Check if remote name is in the EIR data
+**
+** Returns          TRUE if remote name found
+**                  Populate p_remote_name, if provided and remote name found
+**
+*******************************************************************************/
+static BOOLEAN check_eir_remote_name(tBTA_DM_SEARCH *p_search_data,
+                                     UINT8 *p_remote_name, UINT8 *p_remote_name_len)
+{
+    UINT8 *p_eir_remote_name = NULL;
+    UINT8 remote_name_len = 0;
+
+    /* Check EIR for remote name and services */
+    if (p_search_data->inq_res.p_eir) {
+        p_eir_remote_name = BTM_CheckEirData(p_search_data->inq_res.p_eir,
+                                             BTM_EIR_COMPLETE_LOCAL_NAME_TYPE, &remote_name_len);
+        if (!p_eir_remote_name) {
+            p_eir_remote_name = BTM_CheckEirData(p_search_data->inq_res.p_eir,
+                                                 BTM_EIR_SHORTENED_LOCAL_NAME_TYPE, &remote_name_len);
+        }
+
+        if (p_eir_remote_name) {
+            if (remote_name_len > BD_NAME_LEN) {
+                remote_name_len = BD_NAME_LEN;
+            }
+
+            if (p_remote_name && p_remote_name_len) {
+                memcpy(p_remote_name, p_eir_remote_name, remote_name_len);
+                *(p_remote_name + remote_name_len) = 0;
+                *p_remote_name_len = remote_name_len;
+            }
+
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+
+}
+
+/*******************************************************************************
+**
+** Function         bte_search_devices_evt
+**
+** Description      Switches context from BTE to BTIF for DM search events
+**
+** Returns          void
+**
+*******************************************************************************/
+static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
+{
+    UINT16 param_len = 0;
+
+    if (p_data) {
+        param_len += sizeof(tBTA_DM_SEARCH);
+    }
+    /* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */
+    switch (event) {
+    case BTA_DM_INQ_RES_EVT: {
+        if (p_data->inq_res.p_eir) {
+            param_len += HCI_EXT_INQ_RESPONSE_LEN;
+        }
+    }
+    break;
+
+    case BTA_DM_DISC_RES_EVT: {
+        if (p_data->disc_res.raw_data_size && p_data->disc_res.p_raw_data) {
+            param_len += p_data->disc_res.raw_data_size;
+        }
+    }
+    break;
+    }
+
+    /* if remote name is available in EIR, set the flag so that stack doesn't trigger RNR */
+    if (event == BTA_DM_INQ_RES_EVT) {
+        p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL);
+    }
+
+    do {
+        btc_msg_t msg;
+        msg.sig = BTC_SIG_API_CALL;
+        msg.pid = BTC_PID_GAP_BT;
+        msg.act = BTC_GAP_BT_ACT_SEARCH_DEVICES;
+        msg.aid = event;
+
+        btc_transfer_context(&msg, p_data, param_len,
+                             (param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL);
+    } while (0);
+}
+
+static void btc_gap_bt_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
+{
+    switch (event) {
+    case BTA_DM_DISC_RES_EVT: {
+        /* remote name update */
+        uint32_t bdname_len = strlen((const char *)p_data->disc_res.bd_name);
+        if (bdname_len) {
+            esp_bt_gap_dev_prop_t prop[1];
+
+            BTC_STORAGE_FILL_PROPERTY(&prop[0], ESP_BT_GAP_DEV_PROP_BDNAME, bdname_len + 1, p_data->disc_res.bd_name);
+
+            esp_bt_gap_cb_param_t param;
+            bdcpy(param.disc_res.bda, p_data->disc_res.bd_addr);
+            param.disc_res.num_prop = 1;
+            param.disc_res.prop = prop;
+            btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_RES_EVT, &param);
+        }
+        break;
+    }
+    case BTA_DM_INQ_RES_EVT: {
+        /* inquiry result */
+        uint32_t cod = devclass2uint (p_data->inq_res.dev_class);
+
+        if (cod == 0) {
+            LOG_DEBUG("%s cod is 0, set as unclassified", __func__);
+            cod = COD_UNCLASSIFIED;
+        }
+
+        do {
+            esp_bt_gap_dev_prop_t prop[3];
+            int num_prop = 0;
+
+            memset(prop, 0, sizeof(prop));
+            BTC_STORAGE_FILL_PROPERTY(&prop[0], ESP_BT_GAP_DEV_PROP_COD, sizeof(cod), &cod);
+            num_prop++;
+
+            BTC_STORAGE_FILL_PROPERTY(&prop[1], ESP_BT_GAP_DEV_PROP_RSSI, 1, &(p_data->inq_res.rssi));
+            num_prop++;
+
+            if (p_data->inq_res.p_eir) {
+                BTC_STORAGE_FILL_PROPERTY(&prop[2], ESP_BT_GAP_DEV_PROP_EIR, HCI_EXT_INQ_RESPONSE_LEN, p_data->inq_res.p_eir);
+                num_prop++;
+            }
+
+            /* Callback to notify upper layer of device */
+            esp_bt_gap_cb_param_t param;
+            bdcpy(param.disc_res.bda, p_data->inq_res.bd_addr);
+            param.disc_res.num_prop = num_prop;
+            param.disc_res.prop = prop;
+            btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_RES_EVT, &param);
+        } while (0);
+    }
+    break;
+
+    case BTA_DM_INQ_CMPL_EVT:
+        break;
+    case BTA_DM_DISC_CMPL_EVT: {
+        esp_bt_gap_cb_param_t param;
+        param.disc_st_chg.state = ESP_BT_GAP_DISCOVERY_STOPPED;
+        btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_STATE_CHANGED_EVT, &param);
+        break;
+    }
+    case BTA_DM_SEARCH_CANCEL_CMPL_EVT: {
+        /* if inquiry is not in progress and we get a cancel event, then
+         * it means we are done with inquiry, but remote_name fetches are in
+         * progress
+         *
+         * if inquiry is in progress, then we don't want to act on this cancel_cmpl_evt
+         * but instead wait for the cancel_cmpl_evt_via the busy level
+         */
+        if (btc_gap_bt_inquiry_in_progress == false) {
+            esp_bt_gap_cb_param_t param;
+            param.disc_st_chg.state = ESP_BT_GAP_DISCOVERY_STOPPED;
+            btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_STATE_CHANGED_EVT, &param);
+        }
+        break;
+    }
+    }
+}
+/*******************************************************************************
+**
+** Function         btc_gap_bt_search_service_record
+**
+** Description      Executes search service record event in btif context
+**
+** Returns          void
+**
+*******************************************************************************/
+static void btc_gap_bt_search_service_record(UINT16 event, char *p_param)
+{
+    tBTA_DM_SEARCH *p_data = (tBTA_DM_SEARCH *)p_param;
+
+    switch (event) {
+    case BTA_DM_DISC_RES_EVT: {
+        esp_bt_gap_cb_param_t param;
+        memcpy(param.rmt_srvcs.bda, p_data->disc_res.bd_addr, BD_ADDR_LEN);
+        if (p_data->disc_res.p_raw_data && p_data->disc_res.raw_data_size > 0) {
+            param.rmt_srvc_rec.stat = ESP_BT_STATUS_SUCCESS;
+            // param.rmt_srvc_rec.raw_data_size = p_data->disc_res.raw_data_size;
+            // param.rmt_srvc_rec.raw_data = p_data->disc_res.p_raw_data;
+        } else {
+            param.rmt_srvc_rec.stat = ESP_BT_STATUS_FAIL;
+            // param.rmt_srvc_rec.raw_data_size = 0;
+            // param.rmt_srvc_rec.raw_data = NULL;
+        }
+        btc_gap_bt_cb_to_app(ESP_BT_GAP_RMT_SRVC_REC_EVT, &param);
+    }
+    break;
+    case BTA_DM_DISC_CMPL_EVT:
+    default:
+        break;
+    }
+}
+
+
+/*******************************************************************************
+**
+** Function         bte_dm_remote_service_record_evt
+**
+** Description      Switches context from BTE to BTC for DM search service
+**                  record event
+**
+** Returns          void
+**
+*******************************************************************************/
+static void bte_dm_remote_service_record_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
+{
+    UINT16 param_len = 0;
+
+    if (p_data) {
+        param_len += sizeof(tBTA_DM_SEARCH);
+    }
+    /* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */
+    if (event == BTA_DM_DISC_RES_EVT) {
+        if (p_data->disc_res.raw_data_size && p_data->disc_res.p_raw_data) {
+            param_len += p_data->disc_res.raw_data_size;
+        }
+    }
+
+    do {
+        btc_msg_t msg;
+        msg.sig = BTC_SIG_API_CALL;
+        msg.pid = BTC_PID_GAP_BT;
+        msg.act = BTC_GAP_BT_ACT_SEARCH_SERVICE_RECORD;
+        msg.aid = event;
+
+        btc_transfer_context(&msg, p_data, param_len,
+                             (param_len > sizeof(tBTA_DM_SEARCH)) ? search_service_record_copy_cb : NULL);
+
+    } while (0);
+
+}
+
+/*******************************************************************************
+**
+** Function         btc_gap_bt_search_services
+**
+** Description      Executes search services event in btc context
+**
+** Returns          void
+**
+*******************************************************************************/
+static void btc_gap_bt_search_services(UINT16 event, char *p_param)
+{
+    tBTA_DM_SEARCH *p_data = (tBTA_DM_SEARCH *)p_param;
+
+    switch (event) {
+    case BTA_DM_DISC_RES_EVT: {
+        esp_bt_gap_cb_param_t param;
+        esp_bt_uuid_t *uuid_list = NULL;
+        memcpy(param.rmt_srvcs.bda, p_data->disc_res.bd_addr, BD_ADDR_LEN);
+
+        param.rmt_srvcs.stat = ESP_BT_STATUS_FAIL;
+        if (p_data->disc_res.result == BTA_SUCCESS) {
+            uuid_list = malloc(sizeof(esp_bt_uuid_t) * p_data->disc_res.num_uuids);
+            if (uuid_list) {
+                param.rmt_srvcs.stat = ESP_BT_STATUS_SUCCESS;
+                param.rmt_srvcs.num_uuids = p_data->disc_res.num_uuids;
+                param.rmt_srvcs.uuid_list = uuid_list;
+                // copy UUID list
+                uint8_t *i_uu = (uint8_t *)p_data->disc_res.p_uuid_list;
+                esp_bt_uuid_t *o_uu = uuid_list;
+                for (int i = 0; i < p_data->disc_res.num_uuids; i++, i_uu += ESP_UUID_LEN_128, o_uu++) {
+                    uuid128_be_to_esp_uuid(o_uu, i_uu);
+                }
+            }
+        }
+
+        if (param.rmt_srvcs.stat == ESP_BT_STATUS_FAIL) {
+            param.rmt_srvcs.num_uuids = 0;
+            param.rmt_srvcs.uuid_list = NULL;
+        }
+        btc_gap_bt_cb_to_app(ESP_BT_GAP_RMT_SRVCS_EVT, &param);
+
+        if (uuid_list) {
+            osi_free(uuid_list);
+        }
+    }
+    break;
+
+    case BTA_DM_DISC_BLE_RES_EVT:
+    case BTA_DM_DISC_CMPL_EVT:
+    default:
+        break;
+    }
+}
+
+/*******************************************************************************
+**
+** Function         bte_dm_search_services_evt
+**
+** Description      Switches context from BTE to BTIF for DM search services
+**                  event
+**
+** Returns          void
+**
+*******************************************************************************/
+static void bte_dm_search_services_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
+{
+    UINT16 param_len = 0;
+    if (p_data) {
+        param_len += sizeof(tBTA_DM_SEARCH);
+    }
+
+    switch (event) {
+    case BTA_DM_DISC_RES_EVT: {
+        if ((p_data->disc_res.result == BTA_SUCCESS) && (p_data->disc_res.num_uuids > 0)) {
+            param_len += (p_data->disc_res.num_uuids * MAX_UUID_SIZE);
+        }
+    } break;
+    }
+
+    /* TODO: The only other member that needs a deep copy is the p_raw_data. But not sure
+     * if raw_data is needed. */
+    do {
+        btc_msg_t msg;
+        msg.sig = BTC_SIG_API_CALL;
+        msg.pid = BTC_PID_GAP_BT;
+        msg.act = BTC_GAP_BT_ACT_SEARCH_SERVICES;
+        msg.aid = event;
+
+        btc_transfer_context(&msg, p_data, param_len,
+                             (param_len > sizeof(tBTA_DM_SEARCH)) ? search_services_copy_cb : NULL);
+    } while (0);
+}
+
+static void search_services_copy_cb(btc_msg_t *msg, void *p_dest, void *p_src)
+{
+    tBTA_DM_SEARCH *p_dest_data =  (tBTA_DM_SEARCH *) p_dest;
+    tBTA_DM_SEARCH *p_src_data =  (tBTA_DM_SEARCH *) p_src;
+
+    if (!p_src) {
+        return;
+    }
+
+    switch (msg->aid) {
+    case BTA_DM_DISC_RES_EVT: {
+        if (p_src_data->disc_res.result == BTA_SUCCESS) {
+            if (p_src_data->disc_res.num_uuids > 0) {
+                p_dest_data->disc_res.p_uuid_list = (UINT8 *)(p_dest + sizeof(tBTA_DM_SEARCH));
+                memcpy(p_dest_data->disc_res.p_uuid_list, p_src_data->disc_res.p_uuid_list,
+                       p_src_data->disc_res.num_uuids * MAX_UUID_SIZE);
+                osi_free(p_src_data->disc_res.p_uuid_list);
+                p_src_data->disc_res.p_uuid_list = NULL;
+            }
+            if (p_src_data->disc_res.p_raw_data != NULL) {
+                osi_free(p_src_data->disc_res.p_raw_data);
+                p_src_data->disc_res.p_raw_data = NULL;
+            }
+        }
+    } break;
+    }
+}
+
 void btc_gap_bt_call_handler(btc_msg_t *msg)
 {
     btc_gap_bt_args_t *arg = (btc_gap_bt_args_t *)msg->arg;
@@ -78,10 +576,55 @@ void btc_gap_bt_call_handler(btc_msg_t *msg)
         btc_bt_set_scan_mode(arg->set_scan_mode.mode);
         break;
     }
+    case BTC_GAP_BT_ACT_START_DISCOVERY: {
+        btc_gap_bt_start_discovery(msg->arg);
+        break;
+    }
+    case BTC_GAP_BT_ACT_SEARCH_DEVICES: {
+        btc_gap_bt_search_devices_evt(msg->aid, msg->arg);
+        break;
+    }
+    case BTC_GAP_BT_ACT_CANCEL_DISCOVERY: {
+        btc_gap_bt_cancel_discovery();
+        break;
+    }
+    case BTC_GAP_BT_ACT_GET_REMOTE_SERVICES: {
+        btc_gap_bt_get_remote_services(msg->arg);
+        break;
+    }
+    case BTC_GAP_BT_ACT_SEARCH_SERVICES: {
+        btc_gap_bt_search_services(msg->aid, msg->arg);
+        break;
+    }
+    case BTC_GAP_BT_ACT_GET_REMOTE_SERVICE_RECORD: {
+        btc_gap_bt_get_remote_service_record(msg->arg);
+        break;
+    }
+    case BTC_GAP_BT_ACT_SEARCH_SERVICE_RECORD: {
+        btc_gap_bt_search_service_record(msg->aid, msg->arg);
+        break;
+    }
     default:
         break;
     }
-    btc_gap_bt_arg_deep_free(msg);
+
+    return;
+}
+
+void btc_gap_bt_busy_level_updated(uint8_t bl_flags)
+{
+    esp_bt_gap_cb_param_t param;
+
+    if (bl_flags == BTM_BL_INQUIRY_STARTED) {
+        param.disc_st_chg.state = ESP_BT_GAP_DISCOVERY_STARTED;
+        btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_STATE_CHANGED_EVT, &param);
+        btc_gap_bt_inquiry_in_progress = true;
+    } else if (bl_flags == BTM_BL_INQUIRY_CANCELLED ||
+               bl_flags == BTM_BL_INQUIRY_COMPLETE) {
+        param.disc_st_chg.state = ESP_BT_GAP_DISCOVERY_STOPPED;
+        btc_gap_bt_cb_to_app(ESP_BT_GAP_DISC_STATE_CHANGED_EVT, &param);
+        btc_gap_bt_inquiry_in_progress = false;
+    }
 }
 
-#endif /* #if BTC_GAP_BT_INCLUDED */
+#endif /* (BTC_GAP_BT_INCLUDED == TRUE) */

+ 30 - 1
components/bt/bluedroid/btc/profile/std/include/btc_gap_bt.h

@@ -15,12 +15,23 @@
 #ifndef __BTC_GAP_BT_H__
 #define __BTC_GAP_BT_H__
 
+#include "bt_target.h"
 #include "esp_bt_defs.h"
 #include "esp_gap_bt_api.h"
 #include "btc_task.h"
 
+#if (BTC_GAP_BT_INCLUDED == TRUE)
+
 typedef enum {
     BTC_GAP_BT_ACT_SET_SCAN_MODE = 0,
+    BTC_GAP_BT_ACT_REG_CB,
+    BTC_GAP_BT_ACT_START_DISCOVERY,
+    BTC_GAP_BT_ACT_SEARCH_DEVICES,
+    BTC_GAP_BT_ACT_CANCEL_DISCOVERY,
+    BTC_GAP_BT_ACT_GET_REMOTE_SERVICES,
+    BTC_GAP_BT_ACT_SEARCH_SERVICES,
+    BTC_GAP_BT_ACT_GET_REMOTE_SERVICE_RECORD,
+    BTC_GAP_BT_ACT_SEARCH_SERVICE_RECORD,
 } btc_gap_bt_act_t;
 
 /* btc_bt_gap_args_t */
@@ -29,10 +40,28 @@ typedef union {
     struct set_bt_scan_mode_args {
         esp_bt_scan_mode_t mode;
     } set_scan_mode;
+
+    // BTC_GAP_BT_ACT_START_DISCOVERY
+    struct start_disc_args {
+        esp_bt_inq_mode_t mode;
+        uint8_t inq_len;
+        uint8_t num_rsps;
+    } start_disc;
+
+    // BTC_BT_GAP_ACT_GET_REMOTE_SERVICES
+    bt_bdaddr_t bda;
+
+    // BTC_BT_GAP_ACT_GET_REMTOE_SERVICE_RECORD
+    struct get_rmt_srv_rcd_args {
+        bt_bdaddr_t bda;
+        esp_bt_uuid_t uuid;
+    } get_rmt_srv_rcd;
 } btc_gap_bt_args_t;
 
 void btc_gap_bt_call_handler(btc_msg_t *msg);
 
-void btc_gap_bt_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
+void btc_gap_bt_busy_level_updated(uint8_t bl_flags);
+
+#endif /* #if BTC_GAP_BT_INCLUDED */
 
 #endif /* __BTC_GAP_BT_H__ */

+ 9 - 0
examples/bluetooth/bt_discovery/Makefile

@@ -0,0 +1,9 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := bt_discovery
+
+include $(IDF_PATH)/make/project.mk
+

+ 14 - 0
examples/bluetooth/bt_discovery/README.rst

@@ -0,0 +1,14 @@
+ESP-IDF BT-INQUIRY demo
+======================
+
+Demo of Classic Bluetooth Device and Service Discovery
+
+This is the demo for user to use ESP_APIs to perform inquiry to search for a target device and then performs service search via SDP.
+
+Options choose step:
+    1. make menuconfig.
+    2. enter menuconfig "Component config", choose "Bluetooth"
+    3. enter menu Bluetooth, choose "Classic Bluetooth" and do not choose "Release DRAM from Classic BT controller"
+    4. choose your options.
+
+After the program started, the device will start inquiry to search for a device with Major device type "PHONE" in the Class of Device Field. Then it will cancel the inquiry and started to perform service discovering on this remote device.

+ 310 - 0
examples/bluetooth/bt_discovery/main/bt_discovery.c

@@ -0,0 +1,310 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+
+/****************************************************************************
+*
+* This file is for Classic Bluetooth device and service discovery Demo.
+*
+****************************************************************************/
+
+#include <stdint.h>
+#include <string.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "nvs.h"
+#include "nvs_flash.h"
+#include "esp_system.h"
+#include "esp_log.h"
+#include "bt.h"
+#include "esp_bt_main.h"
+#include "esp_bt_device.h"
+#include "esp_gap_bt_api.h"
+
+#define GAP_TAG          "GAP"
+
+typedef enum {
+    APP_GAP_STATE_IDLE = 0,
+    APP_GAP_STATE_DEVICE_DISCOVERING,
+    APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE,
+    APP_GAP_STATE_SERVICE_DISCOVERING,
+    APP_GAP_STATE_SERVICE_DISCOVER_COMPLETE,
+} app_gap_state_t;
+
+typedef struct {
+    bool dev_found;
+    uint8_t bdname_len;
+    uint8_t eir_len;
+    uint8_t rssi;
+    uint32_t cod;
+    uint8_t eir[ESP_BT_GAP_EIR_DATA_LEN];
+    uint8_t bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
+    esp_bd_addr_t bda;
+    app_gap_state_t state;
+} app_gap_cb_t;
+
+static app_gap_cb_t m_dev_info;
+
+static char *bda2str(esp_bd_addr_t bda, char *str, size_t size)
+{
+    if (bda == NULL || str == NULL || size < 18) {
+        return NULL;
+    }
+
+    uint8_t *p = bda;
+    sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
+            p[0], p[1], p[2], p[3], p[4], p[5]);
+    return str;
+}
+
+static char *uuid2str(esp_bt_uuid_t *uuid, char *str, size_t size)
+{
+    if (uuid == NULL || str == NULL) {
+        return NULL;
+    }
+
+    if (uuid->len == 2 && size >= 5) {
+        sprintf(str, "%04x", uuid->uuid.uuid16);
+    } else if (uuid->len == 4 && size >= 9) {
+        sprintf(str, "%08x", uuid->uuid.uuid32);
+    } else if (uuid->len == 16 && size >= 37) {
+        uint8_t *p = uuid->uuid.uuid128;
+        sprintf(str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+                p[15], p[14], p[13], p[12], p[11], p[10], p[9], p[8],
+                p[7], p[6], p[5], p[4], p[3], p[2], p[1], p[0]);
+    } else {
+        return NULL;
+    }
+
+    return str;
+}
+
+static bool get_name_from_eir(uint8_t *eir, uint8_t *bdname, uint8_t *bdname_len)
+{
+    uint8_t *rmt_bdname = NULL;
+    uint8_t rmt_bdname_len = 0;
+
+    if (!eir) {
+        return false;
+    }
+
+    rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len);
+    if (!rmt_bdname) {
+        rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len);
+    }
+
+    if (rmt_bdname) {
+        if (rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN) {
+            rmt_bdname_len = ESP_BT_GAP_MAX_BDNAME_LEN;
+        }
+
+        if (bdname) {
+            memcpy(bdname, rmt_bdname, rmt_bdname_len);
+            bdname[rmt_bdname_len] = '\0';
+        }
+        if (bdname_len) {
+            *bdname_len = rmt_bdname_len;
+        }
+        return true;
+    }
+
+    return false;
+}
+
+static void update_device_info(esp_bt_gap_cb_param_t *param)
+{
+    char bda_str[18];
+    uint32_t cod = 0;
+    int32_t rssi = -129; /* invalid value */
+    esp_bt_gap_dev_prop_t *p;
+
+    ESP_LOGI(GAP_TAG, "Device found: %s", bda2str(param->disc_res.bda, bda_str, 18));
+    for (int i = 0; i < param->disc_res.num_prop; i++) {
+        p = param->disc_res.prop + i;
+        switch (p->type) {
+        case ESP_BT_GAP_DEV_PROP_COD:
+            cod = *(uint32_t *)(p->val);
+            ESP_LOGI(GAP_TAG, "--Class of Device: 0x%x", cod);
+            break;
+        case ESP_BT_GAP_DEV_PROP_RSSI:
+            rssi = *(int8_t *)(p->val);
+            ESP_LOGI(GAP_TAG, "--RSSI: %d", rssi);
+            break;
+        case ESP_BT_GAP_DEV_PROP_BDNAME:
+        default:
+            break;
+        }
+    }
+
+    /* search for device with MAJOR service class as "rendering" in COD */
+    app_gap_cb_t *p_dev = &m_dev_info;
+    if (p_dev->dev_found && 0 != memcmp(param->disc_res.bda, p_dev->bda, ESP_BD_ADDR_LEN)) {
+        return;
+    }
+
+    if (!esp_bt_gap_is_valid_cod(cod) ||
+            !(esp_bt_gap_get_cod_major_dev(cod) == ESP_BT_COD_MAJOR_DEV_PHONE)) {
+        return;
+    }
+
+    memcpy(p_dev->bda, param->disc_res.bda, ESP_BD_ADDR_LEN);
+    p_dev->dev_found = true;
+    for (int i = 0; i < param->disc_res.num_prop; i++) {
+        p = param->disc_res.prop + i;
+        switch (p->type) {
+        case ESP_BT_GAP_DEV_PROP_COD:
+            p_dev->cod = *(uint32_t *)(p->val);
+            break;
+        case ESP_BT_GAP_DEV_PROP_RSSI:
+            p_dev->rssi = *(int8_t *)(p->val);
+            break;
+        case ESP_BT_GAP_DEV_PROP_BDNAME: {
+            uint8_t len = (p->len > ESP_BT_GAP_MAX_BDNAME_LEN) ? ESP_BT_GAP_MAX_BDNAME_LEN :
+                          (uint8_t)p->len;
+            memcpy(p_dev->bdname, (uint8_t *)(p->val), len);
+            p_dev->bdname[len] = '\0';
+            p_dev->bdname_len = len;
+            break;
+        }
+        case ESP_BT_GAP_DEV_PROP_EIR: {
+            memcpy(p_dev->eir, (uint8_t *)(p->val), p->len);
+            p_dev->eir_len = p->len;
+            break;
+        }
+        default:
+            break;
+        }
+    }
+
+    if (p_dev->eir && p_dev->bdname_len == 0) {
+        get_name_from_eir(p_dev->eir, p_dev->bdname, &p_dev->bdname_len);
+        ESP_LOGI(GAP_TAG, "Found a target device, address %s, name %s", bda_str, p_dev->bdname);
+        p_dev->state = APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE;
+        ESP_LOGI(GAP_TAG, "Cancel device discovery ...");
+        esp_bt_gap_cancel_discovery();
+    }
+}
+
+void bt_app_gap_init(void)
+{
+    app_gap_cb_t *p_dev = &m_dev_info;
+    memset(p_dev, 0, sizeof(app_gap_cb_t));
+}
+
+void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
+{
+    app_gap_cb_t *p_dev = &m_dev_info;
+    char bda_str[18];
+    char uuid_str[37];
+
+    switch (event) {
+    case ESP_BT_GAP_DISC_RES_EVT: {
+        update_device_info(param);
+        break;
+    }
+    case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: {
+        if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) {
+            ESP_LOGI(GAP_TAG, "Device discovery stopped.");
+            if ( (p_dev->state == APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE ||
+                    p_dev->state == APP_GAP_STATE_DEVICE_DISCOVERING)
+                    && p_dev->dev_found) {
+                p_dev->state = APP_GAP_STATE_SERVICE_DISCOVERING;
+                ESP_LOGI(GAP_TAG, "Discover services ...");
+                esp_bt_gap_get_remote_services(p_dev->bda);
+            }
+        } else if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) {
+            ESP_LOGI(GAP_TAG, "Discovery started.");
+        }
+        break;
+    }
+    case ESP_BT_GAP_RMT_SRVCS_EVT: {
+        if (memcmp(param->rmt_srvcs.bda, p_dev->bda, ESP_BD_ADDR_LEN) == 0 &&
+                p_dev->state == APP_GAP_STATE_SERVICE_DISCOVERING) {
+            p_dev->state = APP_GAP_STATE_SERVICE_DISCOVER_COMPLETE;
+            if (param->rmt_srvcs.stat == ESP_BT_STATUS_SUCCESS) {
+                ESP_LOGI(GAP_TAG, "Services for device %s found",  bda2str(p_dev->bda, bda_str, 18));
+                for (int i = 0; i < param->rmt_srvcs.num_uuids; i++) {
+                    esp_bt_uuid_t *u = param->rmt_srvcs.uuid_list + i;
+                    ESP_LOGI(GAP_TAG, "--%s", uuid2str(u, uuid_str, 37));
+                    // ESP_LOGI(GAP_TAG, "--%d", u->len);
+                }
+            } else {
+                ESP_LOGI(GAP_TAG, "Services for device %s not found",  bda2str(p_dev->bda, bda_str, 18));
+            }
+        }
+        break;
+    }
+    case ESP_BT_GAP_RMT_SRVC_REC_EVT:
+    default: {
+        ESP_LOGI(GAP_TAG, "event: %d", event);
+        break;
+    }
+    }
+    return;
+}
+
+void bt_app_gap_start_up(void)
+{
+    char *dev_name = "ESP_GAP_INQRUIY";
+    esp_bt_dev_set_device_name(dev_name);
+
+    /* set discoverable and connectable mode, wait to be connected */
+    esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+
+    /* register GAP callback function */
+    esp_bt_gap_register_callback(bt_app_gap_cb);
+
+    /* inititialize device information and status */
+    app_gap_cb_t *p_dev = &m_dev_info;
+    memset(p_dev, 0, sizeof(app_gap_cb_t));
+
+    /* start to discover nearby Bluetooth devices */
+    p_dev->state = APP_GAP_STATE_DEVICE_DISCOVERING;
+    esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
+}
+
+void app_main()
+{
+    /* Initialize NVS — it is used to store PHY calibration data */
+    esp_err_t ret = nvs_flash_init();
+    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
+        ESP_ERROR_CHECK(nvs_flash_erase());
+        ret = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK( ret );
+
+    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
+    if (esp_bt_controller_init(&bt_cfg) != ESP_OK) {
+        ESP_LOGE(GAP_TAG, "%s initialize controller failed\n", __func__);
+        return;
+    }
+
+    if (esp_bt_controller_enable(ESP_BT_MODE_BTDM) != ESP_OK) {
+        ESP_LOGE(GAP_TAG, "%s enable controller failed\n", __func__);
+        return;
+    }
+
+    if (esp_bluedroid_init() != ESP_OK) {
+        ESP_LOGE(GAP_TAG, "%s initialize bluedroid failed\n", __func__);
+        return;
+    }
+
+    if (esp_bluedroid_enable() != ESP_OK) {
+        ESP_LOGE(GAP_TAG, "%s enable bluedroid failed\n", __func__);
+        return;
+    }
+
+    bt_app_gap_start_up();
+}

+ 5 - 0
examples/bluetooth/bt_discovery/main/component.mk

@@ -0,0 +1,5 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+

+ 5 - 0
examples/bluetooth/bt_discovery/sdkconfig.defaults

@@ -0,0 +1,5 @@
+# Override some defaults so BT stack is enabled and
+# Classic BT is enabled and BT_DRAM_RELEASE is disabled
+CONFIG_BT_ENABLED=y
+CONFIG_CLASSIC_BT_ENABLED=y
+