Bluetooth: Fix enhance audio streaming chopping issue tizen_3.0
authorSeungyoun Ju <sy39.ju@samsung.com>
Fri, 30 Jun 2017 02:46:53 +0000 (11:46 +0900)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Tue, 4 Jul 2017 02:52:01 +0000 (11:52 +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 f9d117c1bf14f68da1b91807835d6d5fb4a33147..e39ee3cfbf2d0a5c24afa69703f0aea6760ca09b 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;
@@ -290,6 +295,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;
@@ -345,6 +353,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;
@@ -466,6 +477,7 @@ struct hci_conn {
 
        unsigned int    sent;
 #ifdef TIZEN_BT
+       unsigned int    streaming_sent;
        __u16           tx_len;
        __u16           tx_time;
        __u16           rx_len;
@@ -1572,6 +1584,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 b4e30aeca4fbcd706237849daea3c9d7232697f0..09fd5fe795a89766d0a6c7b98123b7164ff5f1b5 100644 (file)
@@ -222,6 +222,13 @@ struct mgmt_rp_le_set_data_length {
 } __packed;
 #define MGMT_LE_SET_DATA_LENGTH_RSP_SIZE               3
 
+#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 1848ff8e93dafabf57fb0d9fce4ba82fd6705e2f..0c96fd868e0cc005efb59ff945f5ea943b65dbc9 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);
 
@@ -1483,3 +1489,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 39f2026dc477546aae9a844e2b1db9c2ab5aec8d..5f2f4da4712b15a3c3269986c0162a6f6df379c9 100644 (file)
@@ -3758,8 +3758,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;
@@ -3848,6 +3855,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;
@@ -3857,8 +3927,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,
@@ -3878,10 +3953,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 fc56baff26002df425cdf52a0b5dab15aa6b03ca..620d4f30a6f184671d8b90f5cba2ed410e558001 100644 (file)
@@ -8903,6 +8903,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,
@@ -9708,6 +9744,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = {
                                   MGMT_LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE },
        { set_le_data_length_params,
                                   MGMT_LE_SET_DATA_LENGTH_SIZE },
+       { set_streaming_mode,      MGMT_SET_STREAMING_MODE_SIZE },
 };
 #endif