Bluetooth: Add support for HCI_Enhanced_Setup_Synchronous_Connection command
authorKiran K <kiran.k@intel.com>
Tue, 7 Sep 2021 10:12:43 +0000 (15:42 +0530)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Tue, 7 Sep 2021 21:09:18 +0000 (14:09 -0700)
< HCI Command: Enhanced Setup Synchronous Connection (0x01|0x003d) plen 59
        Handle: 256
        Transmit bandwidth: 8000
        Receive bandwidth: 8000
        Max latency: 13
        Packet type: 0x0380
          3-EV3 may not be used
          2-EV5 may not be used
          3-EV5 may not be used
        Retransmission effort: Optimize for link quality (0x02)
> HCI Event: Command Status (0x0f) plen 4
      Enhanced Setup Synchronous Connection (0x01|0x003d) ncmd 1
        Status: Success (0x00)
> HCI Event: Synchronous Connect Complete (0x2c) plen 17
        Status: Success (0x00)
        Handle: 257
        Address: CC:98:8B:92:04:FD (SONY Visual Products Inc.)
        Link type: eSCO (0x02)
        Transmission interval: 0x0c
        Retransmission window: 0x06
        RX packet length: 60
        TX packet length: 60
        Air mode: Transparent (0x03)

Signed-off-by: Kiran K <kiran.k@intel.com>
Reviewed-by: Chethan T N <chethan.tumkur.narayan@intel.com>
Reviewed-by: Srivatsa Ravishankar <ravishankar.srivatsa@intel.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_event.c
net/bluetooth/sco.c

index 536cae9a8bd7488aef5dc677f105d2a51e4ecb6e..981ce32adc2e3d90ba3eaf208ce68b24c5edaf79 100644 (file)
@@ -173,7 +173,8 @@ struct bt_codecs {
        struct bt_codec codecs[];
 } __packed;
 
-#define BT_CODEC_CVSD  0x02
+#define BT_CODEC_CVSD          0x02
+#define BT_CODEC_TRANSPARENT   0x03
 
 __printf(1, 2)
 void bt_info(const char *fmt, ...);
index c20e11f4b54c95b6117f52fadf28d94d847cb1b0..f03bbdf5f9a92cdec07293ce8aff8c341b91a7d1 100644 (file)
@@ -873,6 +873,40 @@ struct hci_cp_logical_link_cancel {
        __u8     flow_spec_id;
 } __packed;
 
+#define HCI_OP_ENHANCED_SETUP_SYNC_CONN                0x043d
+struct hci_coding_format {
+       __u8    id;
+       __le16  cid;
+       __le16  vid;
+} __packed;
+
+struct hci_cp_enhanced_setup_sync_conn {
+       __le16   handle;
+       __le32   tx_bandwidth;
+       __le32   rx_bandwidth;
+       struct   hci_coding_format tx_coding_format;
+       struct   hci_coding_format rx_coding_format;
+       __le16   tx_codec_frame_size;
+       __le16   rx_codec_frame_size;
+       __le32   in_bandwidth;
+       __le32   out_bandwidth;
+       struct   hci_coding_format in_coding_format;
+       struct   hci_coding_format out_coding_format;
+       __le16   in_coded_data_size;
+       __le16   out_coded_data_size;
+       __u8     in_pcm_data_format;
+       __u8     out_pcm_data_format;
+       __u8     in_pcm_sample_payload_msb_pos;
+       __u8     out_pcm_sample_payload_msb_pos;
+       __u8     in_data_path;
+       __u8     out_data_path;
+       __u8     in_transport_unit_size;
+       __u8     out_transport_unit_size;
+       __le16   max_latency;
+       __le16   pkt_type;
+       __u8     retrans_effort;
+} __packed;
+
 struct hci_rp_logical_link_cancel {
        __u8     status;
        __u8     phy_handle;
index 655e2a119e7fc42605ea32d10b4073edb87351d5..fdfdd3bdde169eead657665b0171eef78f5abbbe 100644 (file)
@@ -713,6 +713,7 @@ struct hci_conn {
        struct amp_mgr  *amp_mgr;
 
        struct hci_conn *link;
+       struct bt_codec codec;
 
        void (*connect_cfm_cb)  (struct hci_conn *conn, u8 status);
        void (*security_cfm_cb) (struct hci_conn *conn, u8 status);
@@ -1121,7 +1122,7 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
                                 u8 sec_level, u8 auth_type,
                                 enum conn_reasons conn_reason);
 struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
-                                __u16 setting);
+                                __u16 setting, struct bt_codec *codec);
 int hci_conn_check_link_mode(struct hci_conn *conn);
 int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
 int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
@@ -1458,6 +1459,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
 /* Use LL Privacy based address resolution if supported */
 #define use_ll_privacy(dev) ((dev)->le_features[0] & HCI_LE_LL_PRIVACY)
 
+/* Use enhanced synchronous connection if command is supported */
+#define enhanced_sco_capable(dev) ((dev)->commands[29] & 0x08)
+
 /* Use ext scanning if set ext scan param and ext scan enable is supported */
 #define use_ext_scan(dev) (((dev)->commands[37] & 0x20) && \
                           ((dev)->commands[37] & 0x40))
index 3ab60b75d865f03305348219ff3eb3b75d6710a5..5600b04d50b509ee009c979932b6c88ecdc5104b 100644 (file)
@@ -307,13 +307,103 @@ static bool find_next_esco_param(struct hci_conn *conn,
        return conn->attempt <= size;
 }
 
-bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
+static bool hci_enhanced_setup_sync_conn(struct hci_conn *conn, __u16 handle)
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_cp_enhanced_setup_sync_conn cp;
+       const struct sco_param *param;
+
+       bt_dev_dbg(hdev, "hcon %p", conn);
+
+       conn->state = BT_CONNECT;
+       conn->out = true;
+
+       conn->attempt++;
+
+       memset(&cp, 0x00, sizeof(cp));
+
+       cp.handle   = cpu_to_le16(handle);
+
+       cp.tx_bandwidth   = cpu_to_le32(0x00001f40);
+       cp.rx_bandwidth   = cpu_to_le32(0x00001f40);
+
+       switch (conn->codec.id) {
+       case BT_CODEC_TRANSPARENT:
+               if (!find_next_esco_param(conn, esco_param_msbc,
+                                         ARRAY_SIZE(esco_param_msbc)))
+                       return false;
+               param = &esco_param_msbc[conn->attempt - 1];
+               cp.tx_coding_format.id = 0x03;
+               cp.rx_coding_format.id = 0x03;
+               cp.tx_codec_frame_size = __cpu_to_le16(60);
+               cp.rx_codec_frame_size = __cpu_to_le16(60);
+               cp.in_bandwidth = __cpu_to_le32(0x1f40);
+               cp.out_bandwidth = __cpu_to_le32(0x1f40);
+               cp.in_coding_format.id = 0x03;
+               cp.out_coding_format.id = 0x03;
+               cp.in_coded_data_size = __cpu_to_le16(16);
+               cp.out_coded_data_size = __cpu_to_le16(16);
+               cp.in_pcm_data_format = 2;
+               cp.out_pcm_data_format = 2;
+               cp.in_pcm_sample_payload_msb_pos = 0;
+               cp.out_pcm_sample_payload_msb_pos = 0;
+               cp.in_data_path = conn->codec.data_path;
+               cp.out_data_path = conn->codec.data_path;
+               cp.in_transport_unit_size = 1;
+               cp.out_transport_unit_size = 1;
+               break;
+
+       case BT_CODEC_CVSD:
+               if (lmp_esco_capable(conn->link)) {
+                       if (!find_next_esco_param(conn, esco_param_cvsd,
+                                                 ARRAY_SIZE(esco_param_cvsd)))
+                               return false;
+                       param = &esco_param_cvsd[conn->attempt - 1];
+               } else {
+                       if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
+                               return false;
+                       param = &sco_param_cvsd[conn->attempt - 1];
+               }
+               cp.tx_coding_format.id = 2;
+               cp.rx_coding_format.id = 2;
+               cp.tx_codec_frame_size = __cpu_to_le16(60);
+               cp.rx_codec_frame_size = __cpu_to_le16(60);
+               cp.in_bandwidth = __cpu_to_le32(16000);
+               cp.out_bandwidth = __cpu_to_le32(16000);
+               cp.in_coding_format.id = 4;
+               cp.out_coding_format.id = 4;
+               cp.in_coded_data_size = __cpu_to_le16(16);
+               cp.out_coded_data_size = __cpu_to_le16(16);
+               cp.in_pcm_data_format = 2;
+               cp.out_pcm_data_format = 2;
+               cp.in_pcm_sample_payload_msb_pos = 0;
+               cp.out_pcm_sample_payload_msb_pos = 0;
+               cp.in_data_path = conn->codec.data_path;
+               cp.out_data_path = conn->codec.data_path;
+               cp.in_transport_unit_size = 16;
+               cp.out_transport_unit_size = 16;
+               break;
+       default:
+               return false;
+       }
+
+       cp.retrans_effort = param->retrans_effort;
+       cp.pkt_type = __cpu_to_le16(param->pkt_type);
+       cp.max_latency = __cpu_to_le16(param->max_latency);
+
+       if (hci_send_cmd(hdev, HCI_OP_ENHANCED_SETUP_SYNC_CONN, sizeof(cp), &cp) < 0)
+               return false;
+
+       return true;
+}
+
+static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
 {
        struct hci_dev *hdev = conn->hdev;
        struct hci_cp_setup_sync_conn cp;
        const struct sco_param *param;
 
-       BT_DBG("hcon %p", conn);
+       bt_dev_dbg(hdev, "hcon %p", conn);
 
        conn->state = BT_CONNECT;
        conn->out = true;
@@ -359,6 +449,14 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
        return true;
 }
 
+bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
+{
+       if (enhanced_sco_capable(conn->hdev))
+               return hci_enhanced_setup_sync_conn(conn, handle);
+
+       return hci_setup_sync_conn(conn, handle);
+}
+
 u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
                      u16 to_multiplier)
 {
@@ -1324,7 +1422,7 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
 }
 
 struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
-                                __u16 setting)
+                                __u16 setting, struct bt_codec *codec)
 {
        struct hci_conn *acl;
        struct hci_conn *sco;
@@ -1349,6 +1447,7 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
        hci_conn_hold(sco);
 
        sco->setting = setting;
+       sco->codec = *codec;
 
        if (acl->state == BT_CONNECTED &&
            (sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
index b1062f5cf48887f636610352d0b0b56a7dcfbae6..b48e24629fa4adf2270a7df3cd94d30fbbdf114b 100644 (file)
@@ -2278,6 +2278,41 @@ static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
        hci_dev_unlock(hdev);
 }
 
+static void hci_cs_enhanced_setup_sync_conn(struct hci_dev *hdev, __u8 status)
+{
+       struct hci_cp_enhanced_setup_sync_conn *cp;
+       struct hci_conn *acl, *sco;
+       __u16 handle;
+
+       bt_dev_dbg(hdev, "status 0x%2.2x", status);
+
+       if (!status)
+               return;
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_ENHANCED_SETUP_SYNC_CONN);
+       if (!cp)
+               return;
+
+       handle = __le16_to_cpu(cp->handle);
+
+       bt_dev_dbg(hdev, "handle 0x%4.4x", handle);
+
+       hci_dev_lock(hdev);
+
+       acl = hci_conn_hash_lookup_handle(hdev, handle);
+       if (acl) {
+               sco = acl->link;
+               if (sco) {
+                       sco->state = BT_CLOSED;
+
+                       hci_connect_cfm(sco, status);
+                       hci_conn_del(sco);
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
 static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)
 {
        struct hci_cp_sniff_mode *cp;
@@ -3764,6 +3799,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
                hci_cs_setup_sync_conn(hdev, ev->status);
                break;
 
+       case HCI_OP_ENHANCED_SETUP_SYNC_CONN:
+               hci_cs_enhanced_setup_sync_conn(hdev, ev->status);
+               break;
+
        case HCI_OP_SNIFF_MODE:
                hci_cs_sniff_mode(hdev, ev->status);
                break;
index aaf89c41113ac31fd596dbc38a305eb4834602d9..a7dd4650e142c721bd2946602975560ba54f2cfe 100644 (file)
@@ -253,7 +253,7 @@ static int sco_connect(struct hci_dev *hdev, struct sock *sk)
                return -EOPNOTSUPP;
 
        hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
-                              sco_pi(sk)->setting);
+                              sco_pi(sk)->setting, &sco_pi(sk)->codec);
        if (IS_ERR(hcon))
                return PTR_ERR(hcon);
 
@@ -889,6 +889,16 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
                }
 
                sco_pi(sk)->setting = voice.setting;
+               hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src,
+                                    BDADDR_BREDR);
+               if (!hdev) {
+                       err = -EBADFD;
+                       break;
+               }
+               if (enhanced_sco_capable(hdev) &&
+                   voice.setting == BT_VOICE_TRANSPARENT)
+                       sco_pi(sk)->codec.id = BT_CODEC_TRANSPARENT;
+               hci_dev_put(hdev);
                break;
 
        case BT_PKT_STATUS: