summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorSudha Bheemanna <b.sudha@samsung.com>2016-09-06 16:38:36 +0530
committerSeung-Woo Kim <sw0312.kim@samsung.com>2016-12-14 13:53:14 +0900
commit9eedcb2b6671f73072428184f496af2e8963f425 (patch)
tree41083645a51ed7755c0ab5cb7e13cded24241e5a /net
parentf886fd6851474d9cfc5f0bdf679d2463bc87111f (diff)
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>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/hci_conn.c19
-rw-r--r--net/bluetooth/mgmt.c57
-rw-r--r--net/bluetooth/sco.c183
3 files changed, 259 insertions, 0 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index ee5e59839b02..0a6ac44f5cfe 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
static void hci_le_create_connection_cancel(struct hci_conn *conn)
{
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 37b5c4a50f40..7e573fa97c66 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"
@@ -7164,6 +7165,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;
@@ -9081,6 +9137,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 8611bc7bdd32..805282b2225f 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -32,6 +32,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;
@@ -829,8 +834,15 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
}
/* 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;
}
@@ -999,6 +1011,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;
@@ -1020,7 +1036,13 @@ static void sco_conn_ready(struct sco_conn *conn)
} else {
sco_conn_lock(conn);
+#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;
@@ -1058,6 +1080,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)
{
@@ -1072,8 +1219,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))
@@ -1083,6 +1236,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;
}