summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSudha Bheemanna <b.sudha@samsung.com>2016-08-25 11:23:02 +0530
committerJaehoon Chung <jh80.chung@samsung.com>2023-07-25 08:25:06 +0900
commit8e7b2d5aaaf57f05a79dd7dee5107e19c2b37f5c (patch)
treebd92e25828da5573013d4e681b0ed79f05bf2675
parent33917d2dcb46b86d824dea78e5add22198c9ffee (diff)
downloadlinux-starfive-8e7b2d5aaaf57f05a79dd7dee5107e19c2b37f5c.tar.gz
linux-starfive-8e7b2d5aaaf57f05a79dd7dee5107e19c2b37f5c.tar.bz2
linux-starfive-8e7b2d5aaaf57f05a79dd7dee5107e19c2b37f5c.zip
Bluetooth: Add BT LE discovery feature
This patch adds new MGMT commands to start LE discovery separately and handles LE discovery state. Change-Id: I85958b8c2b5c7e28f57c69e86037ab1e61a75db0 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>
-rw-r--r--include/net/bluetooth/hci_core.h7
-rw-r--r--include/net/bluetooth/mgmt_tizen.h14
-rw-r--r--net/bluetooth/hci_core.c45
-rw-r--r--net/bluetooth/hci_event.c4
-rw-r--r--net/bluetooth/mgmt.c266
5 files changed, 336 insertions, 0 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index d518740ae373..8121d9c94938 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -563,6 +563,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;
@@ -1365,6 +1368,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);
@@ -2183,6 +2189,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);
diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h
index 102faf0f5478..844af75d1fe0 100644
--- a/include/net/bluetooth/mgmt_tizen.h
+++ b/include/net/bluetooth/mgmt_tizen.h
@@ -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 */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 6f27771be8d5..5146e967efff 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -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;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 424e85bac8b1..291a08f106d2 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1792,7 +1792,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);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 718b982607aa..592fc5c91c21 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -8293,6 +8293,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)
@@ -10549,6 +10813,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