diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index e28a2a7..4ba976b 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -26,12 +26,7 @@ #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 { @@ -51,6 +46,9 @@ struct sco_conninfo { __u8 dev_class[3]; }; +#define SCO_TXBUFS 0x03 +#define SCO_RXBUFS 0x04 + /* ---- SCO connections ---- */ struct sco_conn { struct hci_conn *hcon; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 5d13d4f..50f7590 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -53,7 +53,13 @@ #define BT_DBG(D...) #endif -#define VERSION "0.5" +#define VERSION "0.6" + +#define MAX_SCO_TXBUFS 200 +#define MAX_SCO_RXBUFS 200 + +#define DEFAULT_SCO_TXBUFS 5 +#define DEFAULT_SCO_RXBUFS 5 static const struct proto_ops sco_sock_ops; @@ -61,6 +67,8 @@ static struct bt_sock_list sco_sk_list = { .lock = RW_LOCK_UNLOCKED }; +/* Local functions declaration */ + static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); static void sco_chan_del(struct sock *sk, int err); @@ -69,6 +77,35 @@ 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); +static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); + +/* + * 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) { @@ -234,27 +271,30 @@ 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, count; - - /* Check outgoing MTU */ - if (len > conn->mtu) - return -EINVAL; + int err; BT_DBG("sk %p len %d", sk, len); - count = min_t(unsigned int, conn->mtu, len); - if (!(skb = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err))) + if (!(skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err))) return err; - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { + /* 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)) { err = -EFAULT; goto fail; } - if ((err = hci_send_sco(conn->hcon, skb)) < 0) - return err; + err = hci_send_sco(conn->hcon, skb); - return count; + if (err < 0) + goto fail; + + return len; fail: kfree_skb(skb); @@ -273,8 +313,9 @@ static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb) if (sk->sk_state != BT_CONNECTED) goto drop; - if (!sock_queue_rcv_skb(sk, skb)) + if (sco_sock_queue_rcv_skb(sk, skb) == 0) { return; + } drop: kfree_skb(skb); @@ -328,7 +369,6 @@ 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) @@ -360,6 +400,8 @@ static void sco_sock_kill(struct sock *sk) /* Kill poor orphan */ bt_sock_unlink(&sco_sk_list, sk); sock_set_flag(sk, SOCK_DEAD); + + /* release socket */ sock_put(sk); } @@ -376,7 +418,7 @@ static void sco_sock_close(struct sock *sk) conn = sco_pi(sk)->conn; - BT_DBG("sk %p state %d conn %p socket %p", sk, sk->sk_state, conn, sk->sk_socket); + BT_DBG("sk %p state %d conn %p socket %p refcnt %d", sk, sk->sk_state, conn, sk->sk_socket, atomic_read(&sk->sk_refcnt)); switch (sk->sk_state) { case BT_LISTEN: @@ -426,6 +468,15 @@ static struct sock *sco_sock_alloc(struct socket *sock, int proto, gfp_t prio) INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->sk_destruct = sco_sock_destruct; + sk->sk_write_space = sco_sock_write_space; + + /* Put sensible values for a voice link (i.e. not too big), + as sysctl_rmem_default & sysctl_wmem_default are + really not designed for that -- In our case we use sk_**buf to + store a count of SCO packets, not a number of bytes as most of other type of + sockets do */ + sk->sk_sndbuf = DEFAULT_SCO_TXBUFS; + sk->sk_rcvbuf = DEFAULT_SCO_RXBUFS; sk->sk_sndtimeo = SCO_CONN_TIMEOUT; sock_reset_flag(sk, SOCK_ZAPPED); @@ -656,6 +707,7 @@ 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); @@ -663,6 +715,35 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char lock_sock(sk); switch (optname) { + case SCO_TXBUFS: + 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 SCO_RXBUFS: + 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; @@ -677,7 +758,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char struct sock *sk = sock->sk; struct sco_options opts; struct sco_conninfo cinfo; - int len, err = 0; + int len, err = 0; + int val; BT_DBG("sk %p", sk); @@ -687,6 +769,24 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char lock_sock(sk); switch (optname) { + case SCO_RXBUFS: + val = sk->sk_rcvbuf; + + len = min_t(unsigned int, len, sizeof(val)); + if (copy_to_user(optval, (char *)&val, len)) + err = -EFAULT; + + break; + + case SCO_TXBUFS: + 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; @@ -891,6 +991,34 @@ drop: return 0; } +static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + int err = 0; + + BT_DBG("sock %p, sk_rcvbuf %d, qlen %d", sk, sk->sk_rcvbuf, skb_queue_len(&sk->sk_receive_queue)); + /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces + number of warnings when compiling with -W --ANK + */ + if (skb_queue_len(&sk->sk_receive_queue) + 1 > + (unsigned)sk->sk_rcvbuf) { + err = -ENOMEM; + goto out; + } + + 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); +out: + return err; +} + +/* ------- Others ------- */ + static ssize_t sco_sysfs_show(struct class *dev, char *buf) { struct sock *sk;