Bluetooth: Fix enhance audio streaming chopping issue 02/136502/3
authorSeungyoun Ju <sy39.ju@samsung.com>
Fri, 30 Jun 2017 02:46:53 +0000 (11:46 +0900)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Fri, 30 Jun 2017 06:38:43 +0000 (15:38 +0900)
If BT controller's buffer is occupied by another profile such as
OPP or SPP, and it is blocked due to RF condition, A2DP packets
couldn't be sent properly. It causes the A2DP chopping issue.
It is because HCI buffer is limited but another tx requests
occupy it and audio sreaming packet delay is occurred. So this
patch reserves some HCI buffer for A2DP to guarantee A2DP QoS
better.

Change-Id: I7f423f885ef6b7b0a56880d259fd7cd47ce60213
Signed-off-by: Seungyoun Ju <sy39.ju@samsung.com>
include/net/bluetooth/hci_core.h
include/net/bluetooth/mgmt_tizen.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/mgmt.c

index fe6eb86fba909741aac08e609a9810bd2be3c011..3bc674f0873d66411fc69598a40df333d695ee8b 100644 (file)
 /* HCI priority */
 #define HCI_PRIO_MAX   7
 
+#ifdef TIZEN_BT
+/* Reserved ACL slots for Streaming packets */
+#define STREAMING_RESERVED_SLOTS       2
+#endif
+
 /* HCI Core structures */
 struct inquiry_data {
        bdaddr_t        bdaddr;
@@ -293,6 +298,9 @@ struct hci_dev {
        unsigned int    acl_cnt;
        unsigned int    sco_cnt;
        unsigned int    le_cnt;
+#ifdef TIZEN_BT
+       unsigned int    streaming_cnt;
+#endif
 
        unsigned int    acl_mtu;
        unsigned int    sco_mtu;
@@ -348,6 +356,9 @@ struct hci_dev {
        struct discovery_state  le_discovery;
 #endif
        struct hci_conn_hash    conn_hash;
+#ifdef TIZEN_BT
+       struct hci_conn         *streaming_conn;
+#endif
 
        struct list_head        mgmt_pending;
        struct list_head        blacklist;
@@ -469,6 +480,7 @@ struct hci_conn {
 
        unsigned int    sent;
 #ifdef TIZEN_BT
+       unsigned int    streaming_sent;
        __u16           tx_len;
        __u16           tx_time;
        __u16           rx_len;
@@ -1577,6 +1589,7 @@ void mgmt_le_data_length_change_complete(struct hci_dev *hdev,
                bdaddr_t *bdaddr, u16 tx_octets, u16 tx_time,
                u16 rx_octets, u16 rx_time);
 int hci_le_set_data_length(struct hci_conn *conn, u16 tx_octets, u16 tx_time);
+int hci_conn_streaming_mode(struct hci_conn *conn, bool streaming_mode);
 #endif
 
 u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
index 3fe7c7d4ea8b311cde76ccc9156fe0b7c1628881..066985a2b1fbcb62d6ffa15033b2090223ead469 100644 (file)
@@ -235,6 +235,13 @@ struct mgmt_cp_set_dev_rpa_res_support {
 } __packed;
 #define MGMT_OP_SET_DEV_RPA_RES_SUPPORT_SIZE   8
 
+#define MGMT_OP_SET_STREAMING_MODE             (TIZEN_OP_CODE_BASE + 0x1b)
+struct mgmt_cp_set_streaming_mode {
+       uint8_t streaming_mode;
+       bdaddr_t bdaddr;
+} __packed;
+#define MGMT_SET_STREAMING_MODE_SIZE           7
+
 /* EVENTS */
 
 /* For device name update changes */
index 359ff4976805690d9e57ad22d9b767e8d38345e3..ecd95d99433ad41cfcde43735eb5faea8d167e76 100644 (file)
@@ -549,6 +549,12 @@ int hci_conn_del(struct hci_conn *conn)
 
                /* Unacked frames */
                hdev->acl_cnt += conn->sent;
+#ifdef TIZEN_BT
+               if (hdev->streaming_conn == conn) {
+                       hdev->streaming_conn = NULL;
+                       hdev->streaming_cnt = 0;
+               }
+#endif
        } else if (conn->type == LE_LINK) {
                cancel_delayed_work(&conn->le_conn_timeout);
 
@@ -1511,3 +1517,44 @@ struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle)
 
        return hchan;
 }
+
+#ifdef TIZEN_BT
+int hci_conn_streaming_mode(struct hci_conn *conn, bool streaming_mode)
+{
+       struct hci_dev *hdev = conn->hdev;
+
+       if (streaming_mode &&
+           conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
+               return -ENOTCONN;
+
+       if (conn->type != ACL_LINK)
+               return -EOPNOTSUPP;
+
+       if (hdev->acl_pkts < STREAMING_RESERVED_SLOTS * 2)
+               return -EOPNOTSUPP;
+
+       if (streaming_mode) {
+               if (hdev->streaming_conn) {
+                       if (hdev->streaming_conn == conn)
+                               return 0;
+
+                       BT_ERR("already set [%p], request conn [%p]",
+                              hdev->streaming_conn, conn);
+                       return -EOPNOTSUPP;
+               }
+
+               hdev->streaming_cnt = STREAMING_RESERVED_SLOTS;
+               conn->streaming_sent = 0;
+               hdev->streaming_conn = conn;
+       } else {
+               if (hdev->streaming_conn != conn)
+                       return -ENOENT;
+
+               hdev->streaming_conn = NULL;
+               hdev->streaming_cnt = 0;
+               conn->streaming_sent = 0;
+       }
+
+       return 0;
+}
+#endif
index 0d066020e77e28b9ed80d405713e1cdc1e2f772a..65ebba36d00c6f63f53a98c215c42559828cfaf1 100644 (file)
@@ -3773,8 +3773,15 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
 
        switch (chan->conn->type) {
        case ACL_LINK:
+#ifdef TIZEN_BT
+               cnt = hdev->acl_cnt - hdev->streaming_cnt;
+               if (cnt <= 0)
+                       return NULL;
+               break;
+#else
                cnt = hdev->acl_cnt;
                break;
+#endif
        case AMP_LINK:
                cnt = hdev->block_cnt;
                break;
@@ -3863,6 +3870,69 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt)
        }
 }
 
+#ifdef TIZEN_BT
+static void hci_sched_streaming_conn(struct hci_dev *hdev)
+{
+       struct hci_conn *conn = hdev->streaming_conn;
+
+       if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
+               return;
+
+       while (hdev->streaming_cnt && hdev->acl_cnt) {
+               struct hci_chan *tmp;
+               struct sk_buff *skb;
+               struct hci_chan *chan = NULL;
+               unsigned int priority = 0;
+
+               rcu_read_lock();
+
+               list_for_each_entry_rcu(tmp, &conn->chan_list, list) {
+                       if (skb_queue_empty(&tmp->data_q))
+                               continue;
+
+                       skb = skb_peek(&tmp->data_q);
+                       if (skb->priority < priority)
+                               continue;
+
+                       if (skb->priority > priority) {
+                               chan = tmp;
+                               priority = skb->priority;
+                       } else if (!chan) {
+                               chan = tmp;
+                               priority = skb->priority;
+                       }
+               }
+
+               rcu_read_unlock();
+
+               if (!chan)
+                       break;
+
+               while (hdev->streaming_cnt && hdev->acl_cnt &&
+                      (skb = skb_peek(&chan->data_q))) {
+                       /* Stop if priority has changed */
+                       if (skb->priority < priority)
+                               break;
+
+                       skb = skb_dequeue(&chan->data_q);
+
+                       hci_conn_enter_active_mode(chan->conn,
+                                                  bt_cb(skb)->force_active);
+
+                       hci_send_frame(hdev, skb);
+                       hdev->acl_last_tx = jiffies;
+
+                       hdev->acl_cnt--;
+                       hdev->streaming_cnt--;
+                       chan->sent++;
+
+                       conn->streaming_sent++;
+                       conn->sent++;
+               }
+       }
+}
+#endif
+
 static void hci_sched_acl_pkt(struct hci_dev *hdev)
 {
        unsigned int cnt = hdev->acl_cnt;
@@ -3872,8 +3942,13 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
 
        __check_timeout(hdev, cnt);
 
+#ifdef TIZEN_BT
+       while (hdev->acl_cnt > hdev->streaming_cnt &&
+              (chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
+#else
        while (hdev->acl_cnt &&
               (chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
+#endif
                u32 priority = (skb_peek(&chan->data_q))->priority;
                while (quote-- && (skb = skb_peek(&chan->data_q))) {
                        BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
@@ -3893,10 +3968,16 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
 
                        hdev->acl_cnt--;
                        chan->sent++;
+
                        chan->conn->sent++;
                }
        }
 
+#ifdef TIZEN_BT
+       if (hdev->streaming_conn)
+               hci_sched_streaming_conn(hdev);
+#endif
+
        if (cnt != hdev->acl_cnt)
                hci_prio_recalculate(hdev, ACL_LINK);
 }
index f49d81e5ba952524f6925fbd5b7f54ef92eaa73a..d29a3861920cbec9f3d06e965f6f3b699ee02e09 100644 (file)
@@ -3480,6 +3480,16 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
                        continue;
 
                conn->sent -= count;
+#ifdef TIZEN_BT
+               if (hdev->streaming_conn && conn->sent < conn->streaming_sent) {
+                       hdev->streaming_cnt +=
+                                       (conn->streaming_sent - conn->sent);
+                       if (hdev->streaming_cnt > STREAMING_RESERVED_SLOTS)
+                               hdev->streaming_cnt = STREAMING_RESERVED_SLOTS;
+
+                       conn->streaming_sent = conn->sent;
+               }
+#endif
 
                switch (conn->type) {
                case ACL_LINK:
index 185513200931a0b50202b507f8e81702d42edf1e..460c9a4be9d3983f5938c387244c18bfc61f72ed 100644 (file)
@@ -8970,6 +8970,42 @@ void mgmt_le_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        mgmt_event(MGMT_EV_LE_DEVICE_FOUND, hdev, ev, ev_size, NULL);
 }
+
+static int set_streaming_mode(struct sock *sk, struct hci_dev *hdev,
+                             void *cp_data, u16 len)
+{
+       struct mgmt_cp_set_streaming_mode *cp = cp_data;
+       struct hci_conn *conn;
+       int err;
+       int status = MGMT_STATUS_SUCCESS;
+
+       BT_DBG("request for %s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+       if (!conn) {
+               err = -ENOTCONN;
+               goto unlock;
+       }
+
+       err = hci_conn_streaming_mode(conn, cp->streaming_mode);
+
+unlock:
+       if (err == -ENOTCONN)
+               status = MGMT_STATUS_NOT_CONNECTED;
+       else if (err == -EOPNOTSUPP)
+               status = MGMT_STATUS_NOT_SUPPORTED;
+       else if (err == -ENOENT)
+               status = MGMT_STATUS_INVALID_PARAMS;
+
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_STREAMING_MODE,
+                               status, NULL, 0);
+
+       hci_dev_unlock(hdev);
+
+       return err;
+}
 #endif
 
 static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status,
@@ -9777,6 +9813,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = {
                                   MGMT_LE_SET_DATA_LENGTH_SIZE },
        { set_irk,                 MGMT_SET_IRK_SIZE },
        { set_dev_rpa_res_support, MGMT_OP_SET_DEV_RPA_RES_SUPPORT_SIZE },
+       { set_streaming_mode,      MGMT_SET_STREAMING_MODE_SIZE },
 };
 #endif