Bluetooth: Ask upper layers for HCI disconnect reason
[safe/jmp/linux-2.6] / net / bluetooth / sco.c
index 746c11f..51ae0c3 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    BlueZ - Bluetooth protocol stack for Linux
    Copyright (C) 2000-2001 Qualcomm Incorporated
 
    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
    IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
    SOFTWARE IS DISCLAIMED.
 */
 
 /* Bluetooth SCO sockets. */
 
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/types.h>
@@ -38,8 +37,7 @@
 #include <linux/interrupt.h>
 #include <linux/socket.h>
 #include <linux/skbuff.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
+#include <linux/device.h>
 #include <linux/list.h>
 #include <net/sock.h>
 
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/sco.h>
 
-#ifndef CONFIG_BT_SCO_DEBUG
-#undef  BT_DBG
-#define BT_DBG(D...)
-#endif
+#define VERSION "0.6"
 
-#define VERSION "0.4"
+static int disable_esco = 0;
 
-static struct proto_ops sco_sock_ops;
+static const struct proto_ops sco_sock_ops;
 
 static struct bt_sock_list sco_sk_list = {
-       .lock = RW_LOCK_UNLOCKED
+       .lock = __RW_LOCK_UNLOCKED(sco_sk_list.lock)
 };
 
 static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
@@ -99,28 +94,18 @@ static void sco_sock_clear_timer(struct sock *sk)
        sk_stop_timer(sk, &sk->sk_timer);
 }
 
-static void sco_sock_init_timer(struct sock *sk)
-{
-       init_timer(&sk->sk_timer);
-       sk->sk_timer.function = sco_sock_timeout;
-       sk->sk_timer.data = (unsigned long)sk;
-}
-
 /* ---- SCO connections ---- */
 static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
 {
        struct hci_dev *hdev = hcon->hdev;
-       struct sco_conn *conn;
-
-       if ((conn = hcon->sco_data))
-               return conn;
+       struct sco_conn *conn = hcon->sco_data;
 
-       if (status)
+       if (conn || status)
                return conn;
 
-       if (!(conn = kmalloc(sizeof(struct sco_conn), GFP_ATOMIC)))
+       conn = kzalloc(sizeof(struct sco_conn), GFP_ATOMIC);
+       if (!conn)
                return NULL;
-       memset(conn, 0, sizeof(struct sco_conn));
 
        spin_lock_init(&conn->lock);
 
@@ -136,6 +121,7 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
                conn->mtu = 60;
 
        BT_DBG("hcon %p conn %p", hcon, conn);
+
        return conn;
 }
 
@@ -153,7 +139,7 @@ static int sco_conn_del(struct hci_conn *hcon, int err)
        struct sco_conn *conn;
        struct sock *sk;
 
-       if (!(conn = hcon->sco_data)) 
+       if (!(conn = hcon->sco_data))
                return 0;
 
        BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
@@ -193,7 +179,7 @@ static int sco_connect(struct sock *sk)
        struct sco_conn *conn;
        struct hci_conn *hcon;
        struct hci_dev  *hdev;
-       int err = 0;
+       int err, type;
 
        BT_DBG("%s -> %s", batostr(src), batostr(dst));
 
@@ -204,7 +190,12 @@ static int sco_connect(struct sock *sk)
 
        err = -ENOMEM;
 
-       hcon = hci_connect(hdev, SCO_LINK, dst);
+       if (lmp_esco_capable(hdev) && !disable_esco)
+               type = ESCO_LINK;
+       else
+               type = SCO_LINK;
+
+       hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING);
        if (!hcon)
                goto done;
 
@@ -228,6 +219,7 @@ static int sco_connect(struct sock *sk)
                sk->sk_state = BT_CONNECT;
                sco_sock_set_timer(sk, sk->sk_sndtimeo);
        }
+
 done:
        hci_dev_unlock_bh(hdev);
        hci_dev_put(hdev);
@@ -256,7 +248,7 @@ static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
        }
 
        if ((err = hci_send_sco(conn->hcon, skb)) < 0)
-               goto fail;
+               return err;
 
        return count;
 
@@ -397,7 +389,7 @@ static void sco_sock_close(struct sock *sk)
        default:
                sock_set_flag(sk, SOCK_ZAPPED);
                break;
-       };
+       }
 
        release_sock(sk);
 
@@ -408,7 +400,7 @@ static void sco_sock_init(struct sock *sk, struct sock *parent)
 {
        BT_DBG("sk %p", sk);
 
-       if (parent) 
+       if (parent)
                sk->sk_type = parent->sk_type;
 }
 
@@ -418,11 +410,11 @@ static struct proto sco_proto = {
        .obj_size       = sizeof(struct sco_pinfo)
 };
 
-static struct sock *sco_sock_alloc(struct socket *sock, int proto, int prio)
+static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
 {
        struct sock *sk;
 
-       sk = sk_alloc(PF_BLUETOOTH, prio, &sco_proto, 1);
+       sk = sk_alloc(net, PF_BLUETOOTH, prio, &sco_proto);
        if (!sk)
                return NULL;
 
@@ -437,13 +429,13 @@ static struct sock *sco_sock_alloc(struct socket *sock, int proto, int prio)
        sk->sk_protocol = proto;
        sk->sk_state    = BT_OPEN;
 
-       sco_sock_init_timer(sk);
+       setup_timer(&sk->sk_timer, sco_sock_timeout, (unsigned long)sk);
 
        bt_sock_link(&sco_sk_list, sk);
        return sk;
 }
 
-static int sco_sock_create(struct socket *sock, int protocol)
+static int sco_sock_create(struct net *net, struct socket *sock, int protocol)
 {
        struct sock *sk;
 
@@ -456,7 +448,8 @@ static int sco_sock_create(struct socket *sock, int protocol)
 
        sock->ops = &sco_sock_ops;
 
-       if (!(sk = sco_sock_alloc(sock, protocol, GFP_KERNEL)))
+       sk = sco_sock_alloc(net, sock, protocol, GFP_ATOMIC);
+       if (!sk)
                return -ENOMEM;
 
        sco_sock_init(sk, NULL);
@@ -525,7 +518,7 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
        if ((err = sco_connect(sk)))
                goto done;
 
-       err = bt_sock_wait_state(sk, BT_CONNECTED, 
+       err = bt_sock_wait_state(sk, BT_CONNECTED,
                        sock_sndtimeo(sk, flags & O_NONBLOCK));
 
 done:
@@ -630,7 +623,7 @@ static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len
        return 0;
 }
 
-static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock, 
+static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                            struct msghdr *msg, size_t len)
 {
        struct sock *sk = sock->sk;
@@ -638,8 +631,9 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 
        BT_DBG("sock %p, sk %p", sock, sk);
 
-       if (sk->sk_err)
-               return sock_error(sk);
+       err = sock_error(sk);
+       if (err)
+               return err;
 
        if (msg->msg_flags & MSG_OOB)
                return -EOPNOTSUPP;
@@ -674,12 +668,12 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
        return err;
 }
 
-static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+static int sco_sock_getsockopt_old(struct socket *sock, 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;
 
        BT_DBG("sk %p", sk);
 
@@ -729,6 +723,31 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
        return err;
 }
 
+static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+{
+       struct sock *sk = sock->sk;
+       int len, err = 0;
+
+       BT_DBG("sk %p", sk);
+
+       if (level == SOL_SCO)
+               return sco_sock_getsockopt_old(sock, optname, optval, optlen);
+
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       lock_sock(sk);
+
+       switch (optname) {
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+
+       release_sock(sk);
+       return err;
+}
+
 static int sco_sock_release(struct socket *sock)
 {
        struct sock *sk = sock->sk;
@@ -763,7 +782,7 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *
                bt_accept_enqueue(parent, sk);
 }
 
-/* Delete channel. 
+/* Delete channel.
  * Must be called on the locked socket. */
 static void sco_chan_del(struct sock *sk, int err)
 {
@@ -773,7 +792,7 @@ static void sco_chan_del(struct sock *sk, int err)
 
        BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
 
-       if (conn) { 
+       if (conn) {
                sco_conn_lock(conn);
                conn->sk = NULL;
                sco_pi(sk)->conn = NULL;
@@ -809,7 +828,7 @@ static void sco_conn_ready(struct sco_conn *conn)
 
                bh_lock_sock(parent);
 
-               sk = sco_sock_alloc(NULL, BTPROTO_SCO, GFP_ATOMIC);
+               sk = sco_sock_alloc(sock_net(parent), NULL, BTPROTO_SCO, GFP_ATOMIC);
                if (!sk) {
                        bh_unlock_sock(parent);
                        goto done;
@@ -838,17 +857,37 @@ done:
 /* ----- SCO interface with lower layer (HCI) ----- */
 static int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
 {
+       register struct sock *sk;
+       struct hlist_node *node;
+       int lm = 0;
+
+       if (type != SCO_LINK && type != ESCO_LINK)
+               return 0;
+
        BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
 
-       /* Always accept connection */
-       return HCI_LM_ACCEPT;
+       /* Find listening sockets */
+       read_lock(&sco_sk_list.lock);
+       sk_for_each(sk, node, &sco_sk_list.head) {
+               if (sk->sk_state != BT_LISTEN)
+                       continue;
+
+               if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
+                               !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+                       lm |= HCI_LM_ACCEPT;
+                       break;
+               }
+       }
+       read_unlock(&sco_sk_list.lock);
+
+       return lm;
 }
 
 static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
 {
        BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
 
-       if (hcon->type != SCO_LINK)
+       if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
                return 0;
 
        if (!status) {
@@ -857,20 +896,21 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
                conn = sco_conn_add(hcon, status);
                if (conn)
                        sco_conn_ready(conn);
-       } else 
+       } else
                sco_conn_del(hcon, bt_err(status));
 
        return 0;
 }
 
-static int sco_disconn_ind(struct hci_conn *hcon, __u8 reason)
+static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
 {
        BT_DBG("hcon %p reason %d", hcon, reason);
 
-       if (hcon->type != SCO_LINK)
+       if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
                return 0;
 
        sco_conn_del(hcon, bt_err(reason));
+
        return 0;
 }
 
@@ -889,97 +929,32 @@ static int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
        }
 
 drop:
-       kfree_skb(skb); 
+       kfree_skb(skb);
        return 0;
 }
 
-/* ---- Proc fs support ---- */
-#ifdef CONFIG_PROC_FS
-static void *sco_seq_start(struct seq_file *seq, loff_t *pos)
+static ssize_t sco_sysfs_show(struct class *dev, char *buf)
 {
        struct sock *sk;
        struct hlist_node *node;
-       loff_t l = *pos;
+       char *str = buf;
 
        read_lock_bh(&sco_sk_list.lock);
 
-       sk_for_each(sk, node, &sco_sk_list.head)
-               if (!l--)
-                       goto found;
-       sk = NULL;
-found:
-       return sk;
-}
-
-static void *sco_seq_next(struct seq_file *seq, void *e, loff_t *pos)
-{
-       struct sock *sk = e;
-       (*pos)++;
-       return sk_next(sk);
-}
+       sk_for_each(sk, node, &sco_sk_list.head) {
+               str += sprintf(str, "%s %s %d\n",
+                               batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
+                               sk->sk_state);
+       }
 
-static void sco_seq_stop(struct seq_file *seq, void *e)
-{
        read_unlock_bh(&sco_sk_list.lock);
-}
 
-static int  sco_seq_show(struct seq_file *seq, void *e)
-{
-       struct sock *sk = e;
-       seq_printf(seq, "%s %s %d\n",
-                       batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->sk_state);
-       return 0;
+       return (str - buf);
 }
 
-static struct seq_operations sco_seq_ops = {
-       .start  = sco_seq_start,
-       .next   = sco_seq_next,
-       .stop   = sco_seq_stop,
-       .show   = sco_seq_show 
-};
+static CLASS_ATTR(sco, S_IRUGO, sco_sysfs_show, NULL);
 
-static int sco_seq_open(struct inode *inode, struct file *file)
-{
-       return seq_open(file, &sco_seq_ops);
-}
-
-static struct file_operations sco_seq_fops = {
-       .owner          = THIS_MODULE,
-       .open           = sco_seq_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release,
-};
-
-static int __init sco_proc_init(void)
-{
-       struct proc_dir_entry *p = create_proc_entry("sco", S_IRUGO, proc_bt);
-       if (!p)
-               return -ENOMEM;
-       p->owner     = THIS_MODULE;
-       p->proc_fops = &sco_seq_fops;
-       return 0;
-}
-
-static void __exit sco_proc_cleanup(void)
-{
-       remove_proc_entry("sco", proc_bt);
-}
-
-#else /* CONFIG_PROC_FS */
-
-static int __init sco_proc_init(void)
-{
-       return 0;
-}
-
-static void __exit sco_proc_cleanup(void)
-{
-       return;
-}
-#endif /* CONFIG_PROC_FS */
-
-static struct proto_ops sco_sock_ops = {
+static const struct proto_ops sco_sock_ops = {
        .family         = PF_BLUETOOTH,
        .owner          = THIS_MODULE,
        .release        = sco_sock_release,
@@ -991,7 +966,7 @@ static struct proto_ops sco_sock_ops = {
        .sendmsg        = sco_sock_sendmsg,
        .recvmsg        = bt_sock_recvmsg,
        .poll           = bt_sock_poll,
-       .ioctl          = sock_no_ioctl,
+       .ioctl          = bt_sock_ioctl,
        .mmap           = sock_no_mmap,
        .socketpair     = sock_no_socketpair,
        .shutdown       = sock_no_shutdown,
@@ -1010,7 +985,7 @@ static struct hci_proto sco_hci_proto = {
        .id             = HCI_PROTO_SCO,
        .connect_ind    = sco_connect_ind,
        .connect_cfm    = sco_connect_cfm,
-       .disconn_ind    = sco_disconn_ind,
+       .disconn_cfm    = sco_disconn_cfm,
        .recv_scodata   = sco_recv_scodata
 };
 
@@ -1035,7 +1010,8 @@ static int __init sco_init(void)
                goto error;
        }
 
-       sco_proc_init();
+       if (class_create_file(bt_class, &class_attr_sco) < 0)
+               BT_ERR("Failed to create SCO info file");
 
        BT_INFO("SCO (Voice Link) ver %s", VERSION);
        BT_INFO("SCO socket layer initialized");
@@ -1049,7 +1025,7 @@ error:
 
 static void __exit sco_exit(void)
 {
-       sco_proc_cleanup();
+       class_remove_file(bt_class, &class_attr_sco);
 
        if (bt_sock_unregister(BTPROTO_SCO) < 0)
                BT_ERR("SCO socket unregistration failed");
@@ -1063,7 +1039,10 @@ static void __exit sco_exit(void)
 module_init(sco_init);
 module_exit(sco_exit);
 
-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
+module_param(disable_esco, bool, 0644);
+MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL");