Bluetooth: Add MGMT command to set SCO settings 32/293132/1
authorSudha Bheemanna <b.sudha@samsung.com>
Tue, 6 Sep 2016 11:08:36 +0000 (16:38 +0530)
committerWootak Jung <wootak.jung@samsung.com>
Mon, 22 May 2023 01:03:49 +0000 (10:03 +0900)
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>
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/mgmt_tizen.h
include/net/bluetooth/sco.h
net/bluetooth/hci_conn.c
net/bluetooth/mgmt.c
net/bluetooth/sco.c

index 92ab504..77a7eb5 100644 (file)
@@ -1948,6 +1948,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 ---- */
index 13773eb..3d23b4f 100644 (file)
@@ -714,6 +714,8 @@ struct hci_conn {
 
 #ifdef TIZEN_BT
        bool            rssi_monitored;
+       __u8            sco_role;
+       __u16           voice_setting;
 #endif
        struct hci_conn *link;
 
@@ -1161,6 +1163,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);
index 504c2d4..4eaa867 100644 (file)
@@ -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 */
index 1aa2e14..a886e96 100644 (file)
@@ -48,4 +48,11 @@ struct sco_conninfo {
 
 #define SCO_CMSG_PKT_STATUS    0x01
 
+#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 */
index 2b5059a..e08c228 100644 (file)
@@ -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)
index 9cd7d2c..5ae90e7 100644 (file)
@@ -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"
@@ -7841,6 +7842,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;
@@ -10280,6 +10336,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
 
index 98a8815..53e4d79 100644 (file)
 #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;
@@ -865,8 +870,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;
                }
@@ -1086,6 +1098,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;
@@ -1112,7 +1128,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;
@@ -1150,6 +1171,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)
 {
@@ -1164,8 +1310,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))
@@ -1175,6 +1327,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;
 }