summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSudha Bheemanna <b.sudha@samsung.com>2016-09-06 16:38:36 +0530
committerAmit Purwar <amit.purwar@samsung.com>2019-06-28 09:39:41 +0530
commiteb317400cf70c2f0d44f96cc638b053ce6e34520 (patch)
tree3b52e74ede2c5e537dd18ecc51cb4a2ddd9db123
parent1c116e14d23c157f5b19a359648b314a2a70e4aa (diff)
downloadlinux-rpi3-eb317400cf70c2f0d44f96cc638b053ce6e34520.tar.gz
linux-rpi3-eb317400cf70c2f0d44f96cc638b053ce6e34520.tar.bz2
linux-rpi3-eb317400cf70c2f0d44f96cc638b053ce6e34520.zip
Bluetooth: Add MGMT command to set SCO settings
Added code to set sco settings. Change-Id: I37aa572436241b06e00d1e9e75964aac747eeba5 Signed-off-by: Sudha Bheemanna <b.sudha@samsung.com> [remove sco link policy part] Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com> Signed-off-by: Amit Purwar <amit.purwar@samsung.com>
-rw-r--r--include/net/bluetooth/hci.h23
-rw-r--r--include/net/bluetooth/hci_core.h19
-rw-r--r--include/net/bluetooth/mgmt_tizen.h10
-rw-r--r--include/net/bluetooth/sco.h7
-rw-r--r--net/bluetooth/hci_conn.c19
-rw-r--r--net/bluetooth/mgmt.c57
-rw-r--r--net/bluetooth/sco.c182
7 files changed, 317 insertions, 0 deletions
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 87f9ffc8a5b0..bcce0e8fd98f 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -1724,6 +1724,29 @@ struct hci_cc_rp_get_raw_rssi {
__le16 conn_handle;
__s8 rssi_dbm;
} __packed;
+
+#define HCI_BCM_ENABLE_WBS_REQ 0xfc7e
+struct hci_cp_bcm_wbs_req {
+ __u8 role;
+ __le16 pkt_type;
+} __packed;
+
+#define HCI_BCM_I2C_PCM_REQ 0xfc6d
+struct hci_cp_i2c_pcm_req {
+ __u8 i2c_enable;
+ __u8 is_master;
+ __u8 pcm_rate;
+ __u8 clock_rate;
+} __packed;
+
+#define HCI_BCM_SCO_PCM_REQ 0xfc1c
+struct hci_cp_sco_pcm_req {
+ __u8 sco_routing;
+ __u8 pcm_rate;
+ __u8 frame_type;
+ __u8 sync_mode;
+ __u8 clock_mode;
+} __packed;
#endif
/* ---- HCI Events ---- */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 2780ba9e0d67..29d7b6782f2a 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -528,6 +528,8 @@ struct hci_conn {
#ifdef TIZEN_BT
bool rssi_monitored;
+ __u8 sco_role;
+ __u16 voice_setting;
#endif
struct hci_conn *link;
@@ -972,6 +974,23 @@ static inline int hci_conn_hash_lookup_rssi_count(struct hci_dev *hdev)
bool hci_le_discovery_active(struct hci_dev *hdev);
void hci_le_discovery_set_state(struct hci_dev *hdev, int state);
+
+static inline struct hci_conn *hci_conn_hash_lookup_sco(struct hci_dev *hdev)
+{
+ struct hci_conn_hash *h = &hdev->conn_hash;
+ struct hci_conn *c;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(c, &h->list, list) {
+ if (c->type == SCO_LINK || c->type == ESCO_LINK) {
+ rcu_read_unlock();
+ return c;
+ }
+ }
+ rcu_read_unlock();
+
+ return NULL;
+}
#endif
int hci_disconnect(struct hci_conn *conn, __u8 reason);
diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h
index 504c2d47b07d..4eaa867bb126 100644
--- a/include/net/bluetooth/mgmt_tizen.h
+++ b/include/net/bluetooth/mgmt_tizen.h
@@ -151,6 +151,16 @@ struct mgmt_cp_le_set_scan_params {
} __packed;
#define MGMT_LE_SET_SCAN_PARAMS_SIZE 5
+#define MGMT_SCO_ROLE_HANDSFREE 0x00
+#define MGMT_SCO_ROLE_AUDIO_GATEWAY 0x01
+#define MGMT_OP_SET_VOICE_SETTING (TIZEN_OP_CODE_BASE + 0x10)
+struct mgmt_cp_set_voice_setting {
+ bdaddr_t bdaddr;
+ uint8_t sco_role;
+ uint16_t voice_setting;
+} __packed;
+#define MGMT_SET_VOICE_SETTING_SIZE 9
+
/* EVENTS */
/* For device name update changes */
diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h
index f40ddb4264fc..bca05bf7bc31 100644
--- a/include/net/bluetooth/sco.h
+++ b/include/net/bluetooth/sco.h
@@ -46,4 +46,11 @@ struct sco_conninfo {
__u8 dev_class[3];
};
+#ifdef TIZEN_BT
+void sco_connect_set_gw_nbc(struct hci_dev *hdev);
+void sco_connect_set_gw_wbc(struct hci_dev *hdev);
+void sco_connect_set_nbc(struct hci_dev *hdev);
+void sco_connect_set_wbc(struct hci_dev *hdev);
+#endif
+
#endif /* __SCO_H */
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 7516cdde3373..dfad61295cab 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -41,6 +41,16 @@ struct sco_param {
u8 retrans_effort;
};
+#ifdef TIZEN_BT
+static const struct sco_param esco_param_cvsd[] = {
+ { (EDR_ESCO_MASK & ~ESCO_2EV3) | SCO_ESCO_MASK | ESCO_EV3,
+ 0x000a, 0x01 }, /* S3 */
+ { EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007, 0x01 }, /* S2 */
+ { EDR_ESCO_MASK | ESCO_EV3, 0x0007, 0x01 }, /* S1 */
+ { EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0x01 }, /* D1 */
+ { EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0x01 }, /* D0 */
+};
+#else
static const struct sco_param esco_param_cvsd[] = {
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a, 0x01 }, /* S3 */
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007, 0x01 }, /* S2 */
@@ -48,16 +58,25 @@ static const struct sco_param esco_param_cvsd[] = {
{ EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0x01 }, /* D1 */
{ EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0x01 }, /* D0 */
};
+#endif
static const struct sco_param sco_param_cvsd[] = {
{ EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0xff }, /* D1 */
{ EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0xff }, /* D0 */
};
+#ifdef TIZEN_BT
+static const struct sco_param esco_param_msbc[] = {
+ { (EDR_ESCO_MASK & ~ESCO_2EV3) | ESCO_EV3,
+ 0x000d, 0x02 }, /* T2 */
+ { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d, 0x02 }, /* T2 */
+};
+#else
static const struct sco_param esco_param_msbc[] = {
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d, 0x02 }, /* T2 */
{ EDR_ESCO_MASK | ESCO_EV3, 0x0008, 0x02 }, /* T1 */
};
+#endif
/* This function requires the caller holds hdev->lock */
static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index aaf9d6252095..794fb46e2a26 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -34,6 +34,7 @@
#include <net/bluetooth/mgmt.h>
#ifdef TIZEN_BT
#include <net/bluetooth/mgmt_tizen.h>
+#include <net/bluetooth/sco.h>
#endif
#include "hci_request.h"
@@ -6744,6 +6745,61 @@ static int le_set_scan_params(struct sock *sk, struct hci_dev *hdev,
return err;
}
+static int set_voice_setting(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_set_voice_setting *cp = data;
+ struct hci_conn *conn;
+ struct hci_conn *sco_conn;
+
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!lmp_bredr_capable(hdev)) {
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_VOICE_SETTING,
+ MGMT_STATUS_NOT_SUPPORTED);
+ }
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ if (!conn) {
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_SET_VOICE_SETTING, 0, NULL, 0);
+ goto unlock;
+ }
+
+ conn->voice_setting = cp->voice_setting;
+ conn->sco_role = cp->sco_role;
+
+ sco_conn = hci_conn_hash_lookup_sco(hdev);
+ if (sco_conn && bacmp(&sco_conn->dst, &cp->bdaddr) != 0) {
+ BT_ERR("There is other SCO connection.");
+ goto done;
+ }
+
+ if (conn->sco_role == MGMT_SCO_ROLE_HANDSFREE) {
+ if (conn->voice_setting == 0x0063)
+ sco_connect_set_wbc(hdev);
+ else
+ sco_connect_set_nbc(hdev);
+ } else {
+ if (conn->voice_setting == 0x0063)
+ sco_connect_set_gw_wbc(hdev);
+ else
+ sco_connect_set_gw_nbc(hdev);
+ }
+
+done:
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_VOICE_SETTING, 0,
+ cp, sizeof(cp));
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
void mgmt_hardware_error(struct hci_dev *hdev, u8 err_code)
{
struct mgmt_ev_hardware_error ev;
@@ -8743,6 +8799,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = {
{ le_conn_update, MGMT_LE_CONN_UPDATE_SIZE },
{ set_manufacturer_data, MGMT_SET_MANUFACTURER_DATA_SIZE },
{ le_set_scan_params, MGMT_LE_SET_SCAN_PARAMS_SIZE },
+ { set_voice_setting, MGMT_SET_VOICE_SETTING_SIZE },
};
#endif
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index a4ca55df7390..6b4c6236fb17 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -33,6 +33,11 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/sco.h>
+#ifdef TIZEN_BT
+#include <net/bluetooth/mgmt.h>
+#include <net/bluetooth/mgmt_tizen.h>
+#endif
+
static bool disable_esco;
static const struct proto_ops sco_sock_ops;
@@ -837,8 +842,15 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
}
/* Explicitly check for these values */
+#ifdef TIZEN_BT
+ /* Codec defer accept */
+ if (voice.setting != (BT_VOICE_TRANSPARENT |
+ BT_VOICE_CVSD_16BIT) &&
+ voice.setting != BT_VOICE_CVSD_16BIT) {
+#else
if (voice.setting != BT_VOICE_TRANSPARENT &&
voice.setting != BT_VOICE_CVSD_16BIT) {
+#endif
err = -EINVAL;
break;
}
@@ -1014,6 +1026,10 @@ static int sco_sock_release(struct socket *sock)
release_sock(sk);
}
+#ifdef TIZEN_BT
+ /* SCO kernel panic fix */
+ bt_sock_unlink(&sco_sk_list, sk);
+#endif
sock_orphan(sk);
sco_sock_kill(sk);
return err;
@@ -1040,7 +1056,12 @@ static void sco_conn_ready(struct sco_conn *conn)
return;
}
+#ifdef TIZEN_BT
+ /* Multiple SCO accept feature */
+ parent = sco_get_sock_listen(&conn->hcon->dst);
+#else
parent = sco_get_sock_listen(&conn->hcon->src);
+#endif
if (!parent) {
sco_conn_unlock(conn);
return;
@@ -1078,6 +1099,131 @@ static void sco_conn_ready(struct sco_conn *conn)
}
}
+#ifdef TIZEN_BT
+/* WBC/NBC feature */
+void sco_connect_set_gw_nbc(struct hci_dev *hdev)
+{
+ struct hci_cp_write_voice_setting cp1;
+ struct hci_cp_bcm_wbs_req cp2;
+ struct hci_cp_i2c_pcm_req cp3;
+ struct hci_cp_sco_pcm_req cp4;
+
+ BT_DBG("Setting the NBC params, hdev = %p", hdev);
+
+ cp1.voice_setting = cpu_to_le16(0x0060);
+ hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1);
+ hdev->voice_setting = cpu_to_le16(0x0060);
+
+ cp2.role = 0x00; /* WbDisable */
+ cp2.pkt_type = cpu_to_le16(0x0002);
+ hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2);
+
+ cp3.i2c_enable = 0x01;
+ cp3.is_master = 0x00;
+ cp3.pcm_rate = 0x00;
+ cp3.clock_rate = 0x01;
+ hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3);
+
+ cp4.sco_routing = 0x00;
+ cp4.pcm_rate = 0x01;
+ cp4.frame_type = 0x00;
+ cp4.sync_mode = 0x00;
+ cp4.clock_mode = 0x00;
+ hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4);
+}
+
+void sco_connect_set_gw_wbc(struct hci_dev *hdev)
+{
+ struct hci_cp_write_voice_setting cp1;
+ struct hci_cp_bcm_wbs_req cp2;
+ struct hci_cp_i2c_pcm_req cp3;
+ struct hci_cp_sco_pcm_req cp4;
+
+ BT_DBG("Setting the WBC params, hdev = %p", hdev);
+ cp1.voice_setting = cpu_to_le16(0x0003 | 0x0060);
+ hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1);
+ hdev->voice_setting = cpu_to_le16(0x0003 | 0x0060);
+
+ cp2.role = 0x01; /* Enable */
+ cp2.pkt_type = cpu_to_le16(0x0002);
+ hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2);
+
+ cp3.i2c_enable = 0x00;
+ cp3.is_master = 0x00;
+ cp3.pcm_rate = 0x01;
+ cp3.clock_rate = 0x02;
+ hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3);
+
+ cp4.sco_routing = 0x00;
+ cp4.pcm_rate = 0x00;
+ cp4.frame_type = 0x00;
+ cp4.sync_mode = 0x00;
+ cp4.clock_mode = 0x00;
+ hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4);
+}
+
+void sco_connect_set_nbc(struct hci_dev *hdev)
+{
+ struct hci_cp_write_voice_setting cp1;
+ struct hci_cp_bcm_wbs_req cp2;
+ struct hci_cp_i2c_pcm_req cp3;
+ struct hci_cp_sco_pcm_req cp4;
+
+ BT_DBG("Setting the NBC params, hdev = %p", hdev);
+
+ cp1.voice_setting = cpu_to_le16(0x0060);
+ hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1);
+ hdev->voice_setting = cpu_to_le16(0x0060);
+
+ cp2.role = 0x00; /* WbDisable */
+ cp2.pkt_type = cpu_to_le16(0x0002);
+ hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2);
+
+ cp3.i2c_enable = 0x00;
+ cp3.is_master = 0x00;
+ cp3.pcm_rate = 0x00;
+ cp3.clock_rate = 0x04;
+ hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3);
+
+ cp4.sco_routing = 0x00;
+ cp4.pcm_rate = 0x04;
+ cp4.frame_type = 0x00;
+ cp4.sync_mode = 0x00;
+ cp4.clock_mode = 0x00;
+ hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4);
+}
+
+void sco_connect_set_wbc(struct hci_dev *hdev)
+{
+ struct hci_cp_write_voice_setting cp1;
+ struct hci_cp_bcm_wbs_req cp2;
+ struct hci_cp_i2c_pcm_req cp3;
+ struct hci_cp_sco_pcm_req cp4;
+
+ BT_DBG("Setting the WBC params, hdev = %p", hdev);
+ cp1.voice_setting = cpu_to_le16(0x0003 | 0x0060);
+ hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1);
+ hdev->voice_setting = cpu_to_le16(0x0003 | 0x0060);
+
+ cp2.role = 0x01; /* Enable */
+ cp2.pkt_type = cpu_to_le16(0x0002);
+ hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2);
+
+ cp3.i2c_enable = 0x00;
+ cp3.is_master = 0x00;
+ cp3.pcm_rate = 0x01;
+ cp3.clock_rate = 0x04;
+ hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3);
+
+ cp4.sco_routing = 0x00;
+ cp4.pcm_rate = 0x04;
+ cp4.frame_type = 0x00;
+ cp4.sync_mode = 0x00;
+ cp4.clock_mode = 0x00;
+ hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4);
+}
+#endif
+
/* ----- SCO interface with lower layer (HCI) ----- */
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
{
@@ -1092,8 +1238,14 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
if (sk->sk_state != BT_LISTEN)
continue;
+#ifdef TIZEN_BT
+ /* Multiple SCO accept feature */
+ if (!bacmp(&sco_pi(sk)->src, bdaddr) ||
+ !bacmp(&sco_pi(sk)->src, BDADDR_ANY)) {
+#else
if (!bacmp(&sco_pi(sk)->src, &hdev->bdaddr) ||
!bacmp(&sco_pi(sk)->src, BDADDR_ANY)) {
+#endif
lm |= HCI_LM_ACCEPT;
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
@@ -1103,6 +1255,36 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
}
read_unlock(&sco_sk_list.lock);
+#ifdef TIZEN_BT
+ /* WBC/NBC feature */
+ if ((lm & HCI_LM_ACCEPT) && !hci_conn_hash_lookup_sco(hdev)) {
+ struct hci_conn *hcon_acl;
+
+ hcon_acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
+ if (!hcon_acl) {
+ BT_ERR("ACL doesn't alive. Use 0x%X",
+ hdev->voice_setting);
+
+ if (hdev->voice_setting == 0x0063)
+ sco_connect_set_wbc(hdev);
+ else
+ sco_connect_set_nbc(hdev);
+ } else if (hcon_acl->sco_role == MGMT_SCO_ROLE_HANDSFREE) {
+ BT_DBG("Handsfree SCO 0x%X", hcon_acl->voice_setting);
+ if (hcon_acl->voice_setting == 0x0063)
+ sco_connect_set_wbc(hdev);
+ else
+ sco_connect_set_nbc(hdev);
+ } else {
+ BT_DBG("Gateway SCO 0x%X", hcon_acl->voice_setting);
+ if (hcon_acl->voice_setting == 0x0063)
+ sco_connect_set_gw_wbc(hdev);
+ else
+ sco_connect_set_gw_nbc(hdev);
+ }
+ }
+#endif
+
return lm;
}