diff -rU 6 --exclude-from=kernelexcludes.txt --exclude='*hci*' --exclude='af_bluetooth.*' /home/fchevalier/tmp/linux-2.6.18-mh4/include/net/bluetooth/sco.h /home/fchevalier/tmp/linux-2.6.18-mh4-fch/include/net/bluetooth/sco.h --- /home/fchevalier/tmp/linux-2.6.18-mh4/include/net/bluetooth/sco.h 2006-09-20 05:42:06.000000000 +0200 +++ /home/fchevalier/tmp/linux-2.6.18-mh4-fch/include/net/bluetooth/sco.h 2006-10-12 12:39:05.000000000 +0200 @@ -23,18 +23,13 @@ */ #ifndef __SCO_H #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 { sa_family_t sco_family; bdaddr_t sco_bdaddr; }; @@ -48,12 +43,15 @@ #define SCO_CONNINFO 0x02 struct sco_conninfo { __u16 hci_handle; __u8 dev_class[3]; }; +#define SCO_TXBUFS 0x03 +#define SCO_RXBUFS 0x04 + /* ---- SCO connections ---- */ struct sco_conn { struct hci_conn *hcon; bdaddr_t *dst; bdaddr_t *src; diff -rU 6 --exclude-from=kernelexcludes.txt --exclude='*hci*' --exclude='af_bluetooth.*' /home/fchevalier/tmp/linux-2.6.18-mh4/net/bluetooth/sco.c /home/fchevalier/tmp/linux-2.6.18-mh4-fch/net/bluetooth/sco.c --- /home/fchevalier/tmp/linux-2.6.18-mh4/net/bluetooth/sco.c 2006-09-20 05:42:06.000000000 +0200 +++ /home/fchevalier/tmp/linux-2.6.18-mh4-fch/net/bluetooth/sco.c 2006-10-12 12:39:05.000000000 +0200 @@ -50,28 +50,40 @@ #ifndef CONFIG_BT_SCO_DEBUG #undef BT_DBG #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; 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); 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 void sco_sock_rfree(struct sk_buff *skb); + +static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); + /* ---- SCO timers ---- */ static void sco_sock_timeout(unsigned long arg) { struct sock *sk = (struct sock *) arg; BT_DBG("sock %p state %d", sk, sk->sk_state); @@ -227,37 +239,64 @@ done: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; } +static void sco_send_complete_cb(struct hci_conn *hcon) +{ + struct sco_conn *conn = hcon->sco_data; + struct sock *sk = conn->sk; + BT_DBG("conn %p, sock %p", conn, sk); + + if(sk) { + bh_lock_sock(sk); + read_lock(&sk->sk_callback_lock); + + if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) + wake_up_interruptible(sk->sk_sleep); + + sk_wake_async(sk, 2, POLL_OUT); + + read_unlock(&sk->sk_callback_lock); + bh_unlock_sock(sk); + } +} + 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; + int err; /* Check outgoing MTU */ if (len > conn->mtu) - return -EINVAL; + return -EMSGSIZE; 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))) - return err; + if (!(skb = bt_skb_alloc(len, GFP_KERNEL))) + return -ENOBUFS; - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { + 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; + if(msg->msg_flags & MSG_DONTWAIT) { + err = hci_send_sco(conn->hcon, skb, sk->sk_sndbuf, sco_send_complete_cb); + } + else { + err = wait_event_interruptible(*sk->sk_sleep, + hci_send_sco(conn->hcon, skb, sk->sk_sndbuf, sco_send_complete_cb) == 0); + } + + if (err < 0) + goto fail; - return count; + return len; fail: kfree_skb(skb); return err; } @@ -270,14 +309,15 @@ BT_DBG("sk %p len %d", sk, skb->len); 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); return; } @@ -325,13 +365,12 @@ 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) { struct sock *sk; @@ -357,12 +396,14 @@ BT_DBG("sk %p state %d", sk, sk->sk_state); /* Kill poor orphan */ bt_sock_unlink(&sco_sk_list, sk); sock_set_flag(sk, SOCK_DEAD); + + /* release socket */ sock_put(sk); } /* Close socket. * Must be called on unlocked socket. */ @@ -373,13 +414,13 @@ sco_sock_clear_timer(sk); lock_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: sco_sock_cleanup_listen(sk); break; @@ -423,12 +464,20 @@ return NULL; sock_init_data(sock, sk); INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->sk_destruct = sco_sock_destruct; + + /* 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); sk->sk_protocol = proto; sk->sk_state = BT_OPEN; @@ -652,19 +701,44 @@ return err; } 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); 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; + 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; } release_sock(sk); @@ -673,22 +747,41 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { 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); if (get_user(len, optlen)) return -EFAULT; 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; break; } @@ -887,12 +980,48 @@ drop: kfree_skb(skb); return 0; } +static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + int err = 0; + + /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces + number of warnings when compiling with -W --ANK + */ + if (atomic_read(&sk->sk_rmem_alloc) + 1 > + (unsigned)sk->sk_rcvbuf) { + err = -ENOMEM; + goto out; + } + + skb->dev = NULL; + skb->sk = sk; + skb->destructor = sco_sock_rfree; + atomic_add(1, &sk->sk_rmem_alloc); + + skb_queue_tail(&sk->sk_receive_queue, skb); + + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk, 1); +out: + return err; +} + +static void sco_sock_rfree(struct sk_buff *skb) +{ + struct sock *sk = skb->sk; + + atomic_sub(1, &sk->sk_rmem_alloc); +} + + +/* ------- Others ------- */ + static ssize_t sco_sysfs_show(struct class *dev, char *buf) { struct sock *sk; struct hlist_node *node; char *str = buf;