Bluetooth: Add BT LE discovery feature 65/306565/1
authorSudha Bheemanna <b.sudha@samsung.com>
Thu, 25 Aug 2016 05:53:02 +0000 (11:23 +0530)
committerJaehoon Chung <jh80.chung@samsung.com>
Fri, 23 Feb 2024 02:12:37 +0000 (11:12 +0900)
This patch adds new MGMT commands to start LE discovery separately
and handles LE discovery state.

Change-Id: I68218f1e666ed5850ca2b81efe956272c83d30d1
Signed-off-by: Sudha Bheemanna <b.sudha@samsung.com>
Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Signed-off-by: Amit Purwar <amit.purwar@samsung.com>
Signed-off-by: Wootak Jung <wootak.jung@samsung.com>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
include/net/bluetooth/hci_core.h
include/net/bluetooth/mgmt_tizen.h
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/mgmt.c

index b65780d..03a6c65 100644 (file)
@@ -572,6 +572,9 @@ struct hci_dev {
        u8                      wake_reason;
        bdaddr_t                wake_addr;
        u8                      wake_addr_type;
+#ifdef TIZEN_BT
+       struct discovery_state  le_discovery;
+#endif
 
        struct hci_conn_hash    conn_hash;
 
@@ -1517,6 +1520,9 @@ static inline int hci_conn_hash_lookup_rssi_count(struct hci_dev *hdev)
 
        return count;
 }
+
+bool hci_le_discovery_active(struct hci_dev *hdev);
+void hci_le_discovery_set_state(struct hci_dev *hdev, int state);
 #endif
 
 int hci_disconnect(struct hci_conn *conn, __u8 reason);
@@ -2370,6 +2376,7 @@ void mgmt_raw_rssi_response(struct hci_dev *hdev,
 void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status);
 int mgmt_device_name_update(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name,
                u8 name_len);
+void mgmt_le_discovering(struct hci_dev *hdev, u8 discovering);
 #endif
 
 int hci_abort_conn(struct hci_conn *conn, u8 reason);
index 102faf0..844af75 100644 (file)
@@ -104,6 +104,20 @@ struct mgmt_cc_rp_disable_rssi {
 } __packed;
 /* RSSI monitoring */
 
+/* For le discovery */
+#define MGMT_OP_START_LE_DISCOVERY             (TIZEN_OP_CODE_BASE + 0x0a)
+struct mgmt_cp_start_le_discovery {
+       __u8    type;
+} __packed;
+#define MGMT_START_LE_DISCOVERY_SIZE           1
+
+#define MGMT_OP_STOP_LE_DISCOVERY              (TIZEN_OP_CODE_BASE + 0x0b)
+struct mgmt_cp_stop_le_discovery {
+       __u8    type;
+} __packed;
+#define MGMT_STOP_LE_DISCOVERY_SIZE            1
+/* le discovery */
+
 /* EVENTS */
 
 /* For device name update changes */
index 5943609..24d6dae 100644 (file)
@@ -175,6 +175,51 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state)
        }
 }
 
+#ifdef TIZEN_BT
+bool hci_le_discovery_active(struct hci_dev *hdev)
+{
+       struct discovery_state *discov = &hdev->le_discovery;
+
+       switch (discov->state) {
+       case DISCOVERY_FINDING:
+       case DISCOVERY_RESOLVING:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
+void hci_le_discovery_set_state(struct hci_dev *hdev, int state)
+{
+       BT_DBG("%s state %u -> %u", hdev->name,
+                       hdev->le_discovery.state, state);
+
+       if (hdev->le_discovery.state == state)
+               return;
+
+       switch (state) {
+       case DISCOVERY_STOPPED:
+               hci_update_passive_scan(hdev);
+
+               if (hdev->le_discovery.state != DISCOVERY_STARTING)
+                       mgmt_le_discovering(hdev, 0);
+               break;
+       case DISCOVERY_STARTING:
+               break;
+       case DISCOVERY_FINDING:
+               mgmt_le_discovering(hdev, 1);
+               break;
+       case DISCOVERY_RESOLVING:
+               break;
+       case DISCOVERY_STOPPING:
+               break;
+       }
+
+       hdev->le_discovery.state = state;
+}
+#endif
+
 void hci_inquiry_cache_flush(struct hci_dev *hdev)
 {
        struct discovery_state *cache = &hdev->discovery;
index 6f2f46b..19dc4f9 100644 (file)
@@ -1816,7 +1816,11 @@ static void le_set_scan_enable_complete(struct hci_dev *hdev, u8 enable)
                 * therefore discovery as stopped.
                 */
                if (hci_dev_test_and_clear_flag(hdev, HCI_LE_SCAN_INTERRUPTED))
+#ifndef TIZEN_BT /* The below line is kernel bug. */
                        hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+#else
+                       hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED);
+#endif
                else if (!hci_dev_test_flag(hdev, HCI_LE_ADV) &&
                         hdev->discovery.state == DISCOVERY_FINDING)
                        queue_work(hdev->workqueue, &hdev->reenable_adv_work);
index 65e6c0a..5495ab7 100644 (file)
@@ -8300,6 +8300,270 @@ void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb)
                        sizeof(struct mgmt_ev_vendor_specific_rssi_alert),
                        NULL);
 }
+
+static int mgmt_start_le_discovery_failed(struct hci_dev *hdev, u8 status)
+{
+       struct mgmt_pending_cmd *cmd;
+       u8 type;
+       int err;
+
+       hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
+       cmd = pending_find(MGMT_OP_START_LE_DISCOVERY, hdev);
+       if (!cmd)
+               return -ENOENT;
+
+       type = hdev->le_discovery.type;
+
+       err = mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode,
+                               mgmt_status(status), &type, sizeof(type));
+       mgmt_pending_remove(cmd);
+
+       return err;
+}
+
+static void start_le_discovery_complete(struct hci_dev *hdev, u8 status,
+               u16 opcode)
+{
+       unsigned long timeout = 0;
+
+       BT_DBG("status %d", status);
+
+       if (status) {
+               hci_dev_lock(hdev);
+               mgmt_start_le_discovery_failed(hdev, status);
+               hci_dev_unlock(hdev);
+               return;
+       }
+
+       hci_dev_lock(hdev);
+       hci_le_discovery_set_state(hdev, DISCOVERY_FINDING);
+       hci_dev_unlock(hdev);
+
+       if (hdev->le_discovery.type != DISCOV_TYPE_LE)
+               BT_ERR("Invalid discovery type %d", hdev->le_discovery.type);
+
+       if (!timeout)
+               return;
+
+       queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout);
+}
+
+static int start_le_discovery(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_cp_start_le_discovery *cp = data;
+       struct mgmt_pending_cmd *cmd;
+       struct hci_cp_le_set_scan_param param_cp;
+       struct hci_cp_le_set_scan_enable enable_cp;
+       struct hci_request req;
+       u8 status, own_addr_type;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!hdev_is_powered(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY,
+                               MGMT_STATUS_NOT_POWERED);
+               goto unlock;
+       }
+
+       if (hdev->le_discovery.state != DISCOVERY_STOPPED) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY,
+                               MGMT_STATUS_BUSY);
+               goto unlock;
+       }
+
+       if (cp->type != DISCOV_TYPE_LE) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY,
+                               MGMT_STATUS_INVALID_PARAMS);
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_START_LE_DISCOVERY, hdev, NULL, 0);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       hdev->le_discovery.type = cp->type;
+
+       hci_req_init(&req, hdev);
+
+       status = mgmt_le_support(hdev);
+       if (status) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY,
+                               status);
+               mgmt_pending_remove(cmd);
+               goto unlock;
+       }
+
+       /* If controller is scanning, it means the background scanning
+        * is running. Thus, we should temporarily stop it in order to
+        * set the discovery scanning parameters.
+        */
+       if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
+               hci_req_add_le_scan_disable(&req, false);
+
+       memset(&param_cp, 0, sizeof(param_cp));
+
+       /* All active scans will be done with either a resolvable
+        * private address (when privacy feature has been enabled)
+        * or unresolvable private address.
+        */
+       err = hci_update_random_address_sync(hdev, true, hci_dev_test_flag(hdev, HCI_PRIVACY), &own_addr_type);
+       if (err < 0) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY,
+                               MGMT_STATUS_FAILED);
+               mgmt_pending_remove(cmd);
+               goto unlock;
+       }
+
+       param_cp.type = hdev->le_scan_type;
+       param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
+       param_cp.window = cpu_to_le16(hdev->le_scan_window);
+       param_cp.own_address_type = own_addr_type;
+       hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+                   &param_cp);
+
+       memset(&enable_cp, 0, sizeof(enable_cp));
+       enable_cp.enable = LE_SCAN_ENABLE;
+       enable_cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE;
+
+       hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+                   &enable_cp);
+
+       err = hci_req_run(&req, start_le_discovery_complete);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+       else
+               hci_le_discovery_set_state(hdev, DISCOVERY_STARTING);
+
+unlock:
+       return err;
+}
+
+static int mgmt_stop_le_discovery_failed(struct hci_dev *hdev, u8 status)
+{
+       struct mgmt_pending_cmd *cmd;
+       int err;
+
+       cmd = pending_find(MGMT_OP_STOP_LE_DISCOVERY, hdev);
+       if (!cmd)
+               return -ENOENT;
+
+       err = mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode,
+                               mgmt_status(status), &hdev->le_discovery.type,
+                               sizeof(hdev->le_discovery.type));
+       mgmt_pending_remove(cmd);
+
+       return err;
+}
+
+static void stop_le_discovery_complete(struct hci_dev *hdev, u8 status,
+               u16 opcode)
+{
+       BT_DBG("status %d", status);
+
+       hci_dev_lock(hdev);
+
+       if (status) {
+               mgmt_stop_le_discovery_failed(hdev, status);
+               goto unlock;
+       }
+
+       hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int stop_le_discovery(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_cp_stop_le_discovery *mgmt_cp = data;
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (!hci_le_discovery_active(hdev)) {
+               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY,
+                                       MGMT_STATUS_REJECTED, &mgmt_cp->type,
+                                       sizeof(mgmt_cp->type));
+               goto unlock;
+       }
+
+       if (hdev->le_discovery.type != mgmt_cp->type) {
+               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY,
+                                       MGMT_STATUS_INVALID_PARAMS,
+                                       &mgmt_cp->type, sizeof(mgmt_cp->type));
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_STOP_LE_DISCOVERY, hdev, NULL, 0);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       hci_req_init(&req, hdev);
+
+       if (hdev->le_discovery.state  != DISCOVERY_FINDING) {
+               BT_DBG("unknown le discovery state %u",
+                                       hdev->le_discovery.state);
+
+               mgmt_pending_remove(cmd);
+               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY,
+                                       MGMT_STATUS_FAILED, &mgmt_cp->type,
+                                       sizeof(mgmt_cp->type));
+               goto unlock;
+       }
+
+       cancel_delayed_work(&hdev->le_scan_disable);
+       hci_req_add_le_scan_disable(&req, false);
+
+       err = hci_req_run(&req, stop_le_discovery_complete);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+       else
+               hci_le_discovery_set_state(hdev, DISCOVERY_STOPPING);
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+/* Separate LE discovery */
+void mgmt_le_discovering(struct hci_dev *hdev, u8 discovering)
+{
+       struct mgmt_ev_discovering ev;
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("%s le discovering %u", hdev->name, discovering);
+
+       if (discovering)
+               cmd = pending_find(MGMT_OP_START_LE_DISCOVERY, hdev);
+       else
+               cmd = pending_find(MGMT_OP_STOP_LE_DISCOVERY, hdev);
+
+       if (cmd) {
+               u8 type = hdev->le_discovery.type;
+
+               mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type,
+                                 sizeof(type));
+               mgmt_pending_remove(cmd);
+       }
+
+       memset(&ev, 0, sizeof(ev));
+       ev.type = hdev->le_discovery.type;
+       ev.discovering = discovering;
+
+       mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
+}
 #endif /* TIZEN_BT */
 
 static bool ltk_is_valid(struct mgmt_ltk_info *key)
@@ -10556,6 +10820,8 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = {
        { set_enable_rssi,         MGMT_SET_RSSI_ENABLE_SIZE },
        { get_raw_rssi,            MGMT_GET_RAW_RSSI_SIZE },
        { set_disable_threshold,   MGMT_SET_RSSI_DISABLE_SIZE },
+       { start_le_discovery,      MGMT_START_LE_DISCOVERY_SIZE },
+       { stop_le_discovery,       MGMT_STOP_LE_DISCOVERY_SIZE },
 };
 #endif