summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorSudha Bheemanna <b.sudha@samsung.com>2016-08-25 11:23:02 +0530
committerSeung-Woo Kim <sw0312.kim@samsung.com>2016-12-14 13:53:07 +0900
commit6f2fa4bc0de1289963096b09aed8b4e7cb418564 (patch)
treeee68492ae04effcd3932bcdefda7fafab40bd7b7 /net
parent12867e37edd1d2f33f648cbb32ba9552f9790961 (diff)
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>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/hci_core.c45
-rw-r--r--net/bluetooth/hci_event.c4
-rw-r--r--net/bluetooth/mgmt.c269
3 files changed, 318 insertions, 0 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index de6be1330cf3..bd6557aa8e3e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1063,6 +1063,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_background_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 24d6845cd76f..9d98c0e4d400 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1215,7 +1215,11 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
* re-enable it again if necessary.
*/
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)
mgmt_reenable_advertising(hdev);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index c97ef3317e6c..230c7b801293 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -6631,6 +6631,273 @@ 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);
+
+ hci_dev_lock(hdev);
+
+ 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);
+
+ 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(&req, true, &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:
+ hci_dev_unlock(hdev);
+ 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);
+
+ 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)
@@ -8442,6 +8709,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