[PATCH 1/6] Revert "UBUNTU: Bluetooth: SCO flow control to enable bluetooth headsets OriginalLocation: http://bluetooth-alsa.cvs.sourceforge.net/*checkout*/bluetooth-alsa/plugz/patches/sco-flowcontrol-v4.4.diff"
Andres Salomon
dilinger at canonical.com
Mon Apr 20 19:58:36 UTC 2009
These are against ubuntu-hardy's netbook-lpia branch. They probably
shouldn't be committed until we Ulisses's userspace patches are ready for
netbook-common, but it would be good to get the kernel team's ACKs for now.
This reverts commit 20424eae73ff0bc78802b8fdf5b2bc9c0d587772.
As Marcel points out in #130870, this patch is incorrect. The bug is
fixed in later bluez userspace packages.
Signed-off-by: Andres Salomon <dilinger at canonical.com>
---
include/net/bluetooth/hci_core.h | 8 +--
include/net/bluetooth/sco.h | 5 ++
net/bluetooth/hci_conn.c | 30 +--------
net/bluetooth/hci_core.c | 108 ++++++++++++------------------
net/bluetooth/hci_event.c | 7 ++-
net/bluetooth/sco.c | 136 +++++---------------------------------
6 files changed, 73 insertions(+), 221 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 65a8e6b..ea13baa 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -25,8 +25,6 @@
#ifndef __HCI_CORE_H
#define __HCI_CORE_H
-#include <linux/hrtimer.h>
-
#include <net/bluetooth/hci.h>
/* HCI upper protocols */
@@ -95,7 +93,7 @@ struct hci_dev {
atomic_t cmd_cnt;
unsigned int acl_cnt;
- atomic_t sco_cnt;
+ unsigned int sco_cnt;
unsigned int acl_mtu;
unsigned int sco_mtu;
@@ -152,6 +150,7 @@ struct hci_conn {
struct list_head list;
atomic_t refcnt;
+ spinlock_t lock;
bdaddr_t dst;
__u16 handle;
@@ -168,11 +167,10 @@ struct hci_conn {
__u8 power_save;
unsigned long pend;
- atomic_t sent;
+ unsigned int sent;
struct sk_buff_head data_q;
- struct hrtimer tx_timer;
struct timer_list disc_timer;
struct timer_list idle_timer;
diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h
index 599b3d0..e28a2a7 100644
--- a/include/net/bluetooth/sco.h
+++ b/include/net/bluetooth/sco.h
@@ -26,7 +26,12 @@
#define __SCO_H
/* SCO defaults */
+#define SCO_DEFAULT_MTU 500
+#define SCO_DEFAULT_FLUSH_TO 0xFFFF
+
#define SCO_CONN_TIMEOUT (HZ * 40)
+#define SCO_DISCONN_TIMEOUT (HZ * 2)
+#define SCO_CONN_IDLE_TIMEOUT (HZ * 60)
/* SCO socket address */
struct sockaddr_sco {
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index e71af27..34d1a3c 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -188,26 +188,6 @@ static void hci_conn_idle(unsigned long arg)
hci_conn_enter_sniff_mode(conn);
}
-static enum hrtimer_restart hci_sco_tx_timer(struct hrtimer *timer)
-{
- struct hci_conn *conn = container_of(timer, struct hci_conn, tx_timer);
-#ifdef CONFIG_BT_HCI_CORE_DEBUG
- ktime_t now = timer->base->get_time();
-
- BT_DBG("%s, conn %p, time %5lu.%06lu", conn->hdev->name, conn,
- (unsigned long) now.tv64,
- do_div(now.tv64, NSEC_PER_SEC) / 1000);
-#endif
-
- if (atomic_read(&conn->sent) > 0) {
- atomic_dec(&conn->sent);
- atomic_inc(&conn->hdev->sco_cnt);
- hci_sched_tx(conn->hdev);
- }
- return HRTIMER_NORESTART;
-}
-
-
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
{
struct hci_conn *conn;
@@ -228,11 +208,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
skb_queue_head_init(&conn->data_q);
- hrtimer_init(&conn->tx_timer, CLOCK_MONOTONIC, HRTIMER_NORESTART);
-
- if(type == SCO_LINK)
- conn->tx_timer.function = hci_sco_tx_timer;
-
init_timer(&conn->disc_timer);
conn->disc_timer.function = hci_conn_timeout;
conn->disc_timer.data = (unsigned long) conn;
@@ -242,7 +217,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
conn->idle_timer.data = (unsigned long) conn;
atomic_set(&conn->refcnt, 0);
- atomic_set(&conn->sent, 0);
hci_dev_hold(hdev);
@@ -269,15 +243,13 @@ int hci_conn_del(struct hci_conn *conn)
del_timer(&conn->disc_timer);
- hrtimer_cancel(&conn->tx_timer);
-
if (conn->type == ACL_LINK) {
struct hci_conn *sco = conn->link;
if (sco)
sco->link = NULL;
/* Unacked frames */
- hdev->acl_cnt += atomic_read(&conn->sent);
+ hdev->acl_cnt += conn->sent;
} else {
struct hci_conn *acl = conn->link;
if (acl) {
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 0800d3b..372b0d3 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -624,8 +624,7 @@ int hci_dev_reset(__u16 dev)
hdev->flush(hdev);
atomic_set(&hdev->cmd_cnt, 1);
- atomic_set(&hdev->sco_cnt, 0);
- hdev->acl_cnt = 0;
+ hdev->acl_cnt = 0; hdev->sco_cnt = 0;
if (!test_bit(HCI_RAW, &hdev->flags))
ret = __hci_request(hdev, hci_reset_req, 0,
@@ -1231,7 +1230,6 @@ int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
{
struct hci_dev *hdev = conn->hdev;
struct hci_sco_hdr hdr;
- ktime_t now;
BT_DBG("%s len %d", hdev->name, skb->len);
@@ -1240,13 +1238,6 @@ int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
return -EINVAL;
}
- now = conn->tx_timer.base->get_time();
-
- /* force a clean start for 100 ms or later underrun */
- if (conn->tx_timer.expires.tv64 + NSEC_PER_SEC / 10 <= now.tv64) {
- conn->tx_timer.expires = now;
- }
-
hdr.handle = cpu_to_le16(conn->handle);
hdr.dlen = skb->len;
@@ -1264,12 +1255,12 @@ EXPORT_SYMBOL(hci_send_sco);
/* ---- HCI TX task (outgoing data) ---- */
-/* HCI ACL Connection scheduler */
-static inline struct hci_conn *hci_low_sent_acl(struct hci_dev *hdev, int *quote)
+/* HCI Connection scheduler */
+static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *conn = NULL;
- unsigned int num = 0, min = ~0;
+ int num = 0, min = ~0;
struct list_head *p;
/* We don't have to lock device here. Connections are always
@@ -1278,22 +1269,20 @@ static inline struct hci_conn *hci_low_sent_acl(struct hci_dev *hdev, int *quote
struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
- BT_DBG("c->type %d c->state %d len(c->data_q) %d min %d c->sent %d",
- c->type, c->state, skb_queue_len(&c->data_q), min, atomic_read(&c->sent));
-
- if (c->type != ACL_LINK || c->state != BT_CONNECTED
+ if (c->type != type || c->state != BT_CONNECTED
|| skb_queue_empty(&c->data_q))
continue;
num++;
- if (atomic_read(&c->sent) < min) {
- min = atomic_read(&c->sent);
+ if (c->sent < min) {
+ min = c->sent;
conn = c;
}
}
if (conn) {
- int q = hdev->acl_cnt / num;
+ int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
+ int q = cnt / num;
*quote = q ? q : 1;
} else
*quote = 0;
@@ -1313,7 +1302,7 @@ static inline void hci_acl_tx_to(struct hci_dev *hdev)
/* Kill stalled connections */
list_for_each(p, &h->list) {
c = list_entry(p, struct hci_conn, list);
- if (c->type == ACL_LINK && atomic_read(&c->sent)) {
+ if (c->type == ACL_LINK && c->sent) {
BT_ERR("%s killing stalled ACL connection %s",
hdev->name, batostr(&c->dst));
hci_acl_disconn(c, 0x13);
@@ -1336,7 +1325,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
hci_acl_tx_to(hdev);
}
- while (hdev->acl_cnt && (conn = hci_low_sent_acl(hdev, "e))) {
+ while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
@@ -1346,61 +1335,48 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
hdev->acl_last_tx = jiffies;
hdev->acl_cnt--;
- atomic_inc(&conn->sent);
+ conn->sent++;
}
}
}
-/* HCI SCO Connection scheduler */
-
+/* Schedule SCO */
static inline void hci_sched_sco(struct hci_dev *hdev)
{
- struct hci_conn_hash *h = &hdev->conn_hash;
+ struct hci_conn *conn;
struct sk_buff *skb;
- struct list_head *p;
- struct hci_conn *c;
- ktime_t now, pkt_time;
-
+ int quote;
+
BT_DBG("%s", hdev->name);
-
- /* We don't have to lock device here. Connections are always
- added and removed with TX task disabled. */
- list_for_each(p, &h->list) {
- c = list_entry(p, struct hci_conn, list);
-
- /* SCO scheduling algorithm makes sure there is never more than
- 1 outstanding packet for each connection */
- if (c->type == ACL_LINK)
- continue;
+ while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) {
+ while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+ BT_DBG("skb %p len %d", skb, skb->len);
+ hci_send_frame(skb);
- if (atomic_read(&c->sent) >= 1)
- continue;
+ conn->sent++;
+ if (conn->sent == ~0)
+ conn->sent = 0;
+ }
+ }
+}
- if (c->state != BT_CONNECTED)
- continue;
+static inline void hci_sched_esco(struct hci_dev *hdev)
+{
+ struct hci_conn *conn;
+ struct sk_buff *skb;
+ int quote;
- if (atomic_read(&hdev->sco_cnt) <= 0)
- continue;
+ BT_DBG("%s", hdev->name);
- if ((skb = skb_dequeue(&c->data_q)) == NULL)
- continue;
+ while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK, "e))) {
+ while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+ BT_DBG("skb %p len %d", skb, skb->len);
+ hci_send_frame(skb);
- hci_send_frame(skb);
-
- atomic_inc(&c->sent);
- atomic_dec(&hdev->sco_cnt);
-
- pkt_time = ktime_set(0, NSEC_PER_SEC / 16000 * (skb->len - HCI_SCO_HDR_SIZE));
- now = c->tx_timer.base->get_time();
-
- c->tx_timer.expires.tv64 += pkt_time.tv64;
- if (c->tx_timer.expires.tv64 > now.tv64) {
- hrtimer_restart(&c->tx_timer);
- } else {
- /* Timer is to expire in the past - force timer expiration.
- this can happen if timer base precision is less than pkt_time */
- c->tx_timer.function(&c->tx_timer);
+ conn->sent++;
+ if (conn->sent == ~0)
+ conn->sent = 0;
}
}
}
@@ -1412,13 +1388,15 @@ static void hci_tx_task(unsigned long arg)
read_lock(&hci_task_lock);
- BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, atomic_read(&hdev->sco_cnt));
+ BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
/* Schedule queues and send stuff to HCI driver */
+ hci_sched_acl(hdev);
+
hci_sched_sco(hdev);
- hci_sched_acl(hdev);
+ hci_sched_esco(hdev);
/* Send next queued raw (unknown type) packet */
while ((skb = skb_dequeue(&hdev->raw_q)))
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 9a99247..46df2e4 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -434,7 +434,7 @@ static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
}
hdev->acl_cnt = hdev->acl_pkts;
- atomic_set(&hdev->sco_cnt, hdev->sco_pkts);
+ hdev->sco_cnt = hdev->sco_pkts;
BT_DBG("%s acl mtu %d:%d sco mtu %d:%d", hdev->name,
hdev->acl_mtu, hdev->acl_pkts,
@@ -1157,11 +1157,14 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
conn = hci_conn_hash_lookup_handle(hdev, handle);
if (conn) {
- atomic_sub(count, &conn->sent);
+ conn->sent -= count;
if (conn->type == ACL_LINK) {
if ((hdev->acl_cnt += count) > hdev->acl_pkts)
hdev->acl_cnt = hdev->acl_pkts;
+ } else {
+ if ((hdev->sco_cnt += count) > hdev->sco_pkts)
+ hdev->sco_cnt = hdev->sco_pkts;
}
}
}
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 97f13c4..153e7f6 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -53,13 +53,7 @@
#define BT_DBG(D...)
#endif
-#define VERSION "0.6"
-
-#define MAX_SCO_TXBUFS 200
-#define MAX_SCO_RXBUFS 200
-
-#define DEFAULT_SCO_TXBUFS 5
-#define DEFAULT_SCO_RXBUFS 5
+#define VERSION "0.5"
static const struct proto_ops sco_sock_ops;
@@ -75,33 +69,6 @@ static int sco_conn_del(struct hci_conn *conn, int err);
static void sco_sock_close(struct sock *sk);
static void sco_sock_kill(struct sock *sk);
-/*
- * Write buffer destructor automatically called from kfree_skb.
- */
-void sco_sock_wfree(struct sk_buff *skb)
-{
- struct sock *sk = skb->sk;
-
- atomic_dec(&sk->sk_wmem_alloc);
- sk->sk_write_space(sk);
- sock_put(sk);
-}
-
-static void sco_sock_write_space(struct sock *sk)
-{
- read_lock(&sk->sk_callback_lock);
-
- if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
- if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
- wake_up_interruptible(sk->sk_sleep);
-
- if (sock_writeable(sk))
- sk_wake_async(sk, 2, POLL_OUT);
- }
-
- read_unlock(&sk->sk_callback_lock);
-}
-
/* ---- SCO timers ---- */
static void sco_sock_timeout(unsigned long arg)
{
@@ -274,55 +241,33 @@ static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
{
struct sco_conn *conn = sco_pi(sk)->conn;
struct sk_buff *skb;
- int err;
+ int err, count;
+
+ /* Check outgoing MTU */
+ if (len > conn->mtu)
+ return -EINVAL;
BT_DBG("sk %p len %d", sk, len);
- if (!(skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
+ count = min_t(unsigned int, conn->mtu, len);
+ if (!(skb = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err)))
return err;
- /* fix sk_wmem_alloc value : by default it is increased by skb->truesize, but
- we want it only increased by 1 */
- atomic_sub(skb->truesize - 1, &sk->sk_wmem_alloc);
- /* fix destructor */
- skb->destructor = sco_sock_wfree;
-
- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
err = -EFAULT;
goto fail;
}
- err = hci_send_sco(conn->hcon, skb);
-
- if (err < 0)
- goto fail;
+ if ((err = hci_send_sco(conn->hcon, skb)) < 0)
+ return err;
- return len;
+ return count;
fail:
kfree_skb(skb);
return err;
}
-static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
-{
- BT_DBG("sock %p, sk_rcvbuf %d, qlen %d", sk, sk->sk_rcvbuf, skb_queue_len(&sk->sk_receive_queue));
-
- if (skb_queue_len(&sk->sk_receive_queue) + 1 > (unsigned)sk->sk_rcvbuf)
- return -ENOMEM;
-
- skb->dev = NULL;
- skb->sk = sk;
- skb->destructor = NULL;
-
- skb_queue_tail(&sk->sk_receive_queue, skb);
-
- if (!sock_flag(sk, SOCK_DEAD))
- sk->sk_data_ready(sk, 1);
-
- return 0;
-}
-
static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
{
struct sock *sk = sco_chan_get(conn);
@@ -335,7 +280,7 @@ static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
if (sk->sk_state != BT_CONNECTED)
goto drop;
- if (!sco_sock_queue_rcv_skb(sk, skb))
+ if (!sock_queue_rcv_skb(sk, skb))
return;
drop:
@@ -390,6 +335,7 @@ static void sco_sock_destruct(struct sock *sk)
BT_DBG("sk %p", sk);
skb_queue_purge(&sk->sk_receive_queue);
+ skb_queue_purge(&sk->sk_write_queue);
}
static void sco_sock_cleanup_listen(struct sock *parent)
@@ -487,10 +433,6 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int pro
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
sk->sk_destruct = sco_sock_destruct;
- sk->sk_write_space = sco_sock_write_space;
-
- sk->sk_sndbuf = DEFAULT_SCO_TXBUFS;
- sk->sk_rcvbuf = DEFAULT_SCO_RXBUFS;
sk->sk_sndtimeo = SCO_CONN_TIMEOUT;
sock_reset_flag(sk, SOCK_ZAPPED);
@@ -721,7 +663,6 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
{
struct sock *sk = sock->sk;
- u32 opt;
int err = 0;
BT_DBG("sk %p", sk);
@@ -729,32 +670,6 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
lock_sock(sk);
switch (optname) {
- case SO_SNDBUF:
- if (get_user(opt, (u32 __user *) optval)) {
- err = -EFAULT;
- break;
- }
- if (opt > MAX_SCO_TXBUFS) {
- err = -EINVAL;
- break;
- }
-
- sk->sk_sndbuf = opt;
- /* Wake up sending tasks if we upped the value */
- sk->sk_write_space(sk);
- break;
- case SO_RCVBUF:
- if (get_user(opt, (u32 __user *) optval)) {
- err = -EFAULT;
- break;
- }
- if (opt > MAX_SCO_RXBUFS) {
- err = -EINVAL;
- break;
- }
-
- sk->sk_rcvbuf = opt;
- break;
default:
err = -ENOPROTOOPT;
break;
@@ -770,7 +685,6 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
struct sco_options opts;
struct sco_conninfo cinfo;
int len, err = 0;
- int val;
BT_DBG("sk %p", sk);
@@ -780,24 +694,6 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
lock_sock(sk);
switch (optname) {
- case SO_RCVBUF:
- val = sk->sk_rcvbuf;
-
- len = min_t(unsigned int, len, sizeof(val));
- if (copy_to_user(optval, (char *) &val, len))
- err = -EFAULT;
-
- break;
-
- case SO_SNDBUF:
- val = sk->sk_sndbuf;
-
- len = min_t(unsigned int, len, sizeof(val));
- if (copy_to_user(optval, (char *) &val, len))
- err = -EFAULT;
-
- break;
-
case SCO_OPTIONS:
if (sk->sk_state != BT_CONNECTED) {
err = -ENOTCONN;
@@ -809,7 +705,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
BT_DBG("mtu %d", opts.mtu);
len = min_t(unsigned int, len, sizeof(opts));
- if (copy_to_user(optval, (char *) &opts, len))
+ if (copy_to_user(optval, (char *)&opts, len))
err = -EFAULT;
break;
@@ -824,7 +720,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3);
len = min_t(unsigned int, len, sizeof(cinfo));
- if (copy_to_user(optval, (char *) &cinfo, len))
+ if (copy_to_user(optval, (char *)&cinfo, len))
err = -EFAULT;
break;
--
1.5.6.5
More information about the kernel-team
mailing list