[Bluetooth] Export details about authentication requirements
[pandora-kernel.git] / net / bluetooth / rfcomm / core.c
index bb72207..e56bcfc 100644 (file)
 #include <linux/sched.h>
 #include <linux/signal.h>
 #include <linux/init.h>
-#include <linux/freezer.h>
 #include <linux/wait.h>
 #include <linux/device.h>
 #include <linux/net.h>
 #include <linux/mutex.h>
+#include <linux/kthread.h>
 
 #include <net/sock.h>
 #include <asm/uaccess.h>
@@ -53,7 +53,7 @@
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "1.8"
+#define VERSION "1.9"
 
 static int disable_cfc = 0;
 static int channel_mtu = -1;
@@ -68,7 +68,6 @@ static DEFINE_MUTEX(rfcomm_mutex);
 static unsigned long rfcomm_event;
 
 static LIST_HEAD(session_list);
-static atomic_t terminate, running;
 
 static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
 static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
@@ -231,6 +230,21 @@ static int rfcomm_l2sock_create(struct socket **sock)
        return err;
 }
 
+static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
+{
+       struct sock *sk = d->session->sock->sk;
+
+       if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) {
+               if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon))
+                       return 1;
+       } else if (d->link_mode & RFCOMM_LM_AUTH) {
+               if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon))
+                       return 1;
+       }
+
+       return 0;
+}
+
 /* ---- RFCOMM DLCs ---- */
 static void rfcomm_dlc_timeout(unsigned long arg)
 {
@@ -280,9 +294,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio)
        if (!d)
                return NULL;
 
-       init_timer(&d->timer);
-       d->timer.function = rfcomm_dlc_timeout;
-       d->timer.data = (unsigned long) d;
+       setup_timer(&d->timer, rfcomm_dlc_timeout, (unsigned long)d);
 
        skb_queue_head_init(&d->tx_queue);
        spin_lock_init(&d->lock);
@@ -374,15 +386,23 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
        d->addr     = __addr(s->initiator, dlci);
        d->priority = 7;
 
-       d->state    = BT_CONFIG;
+       d->state = BT_CONFIG;
        rfcomm_dlc_link(s, d);
 
+       d->out = 1;
+
        d->mtu = s->mtu;
        d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
 
-       if (s->state == BT_CONNECTED)
-               rfcomm_send_pn(s, 1, d);
+       if (s->state == BT_CONNECTED) {
+               if (rfcomm_check_link_mode(d))
+                       set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+               else
+                       rfcomm_send_pn(s, 1, d);
+       }
+
        rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
+
        return 0;
 }
 
@@ -468,7 +488,7 @@ int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
        return len;
 }
 
-void fastcall __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
+void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
 {
        BT_DBG("dlc %p state %ld", d, d->state);
 
@@ -479,7 +499,7 @@ void fastcall __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
        rfcomm_schedule(RFCOMM_SCHED_TX);
 }
 
-void fastcall __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
+void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
 {
        BT_DBG("dlc %p state %ld", d, d->state);
 
@@ -1149,21 +1169,6 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
        return 0;
 }
 
-static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
-{
-       struct sock *sk = d->session->sock->sk;
-
-       if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) {
-               if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon))
-                       return 1;
-       } else if (d->link_mode & RFCOMM_LM_AUTH) {
-               if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon))
-                       return 1;
-       }
-
-       return 0;
-}
-
 static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
 {
        struct sock *sk = d->session->sock->sk;
@@ -1208,10 +1213,8 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
                        if (rfcomm_check_link_mode(d)) {
                                set_bit(RFCOMM_AUTH_PENDING, &d->flags);
                                rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
-                               return 0;
-                       }
-
-                       rfcomm_dlc_accept(d);
+                       } else
+                               rfcomm_dlc_accept(d);
                }
                return 0;
        }
@@ -1226,10 +1229,8 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
                if (rfcomm_check_link_mode(d)) {
                        set_bit(RFCOMM_AUTH_PENDING, &d->flags);
                        rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
-                       return 0;
-               }
-
-               rfcomm_dlc_accept(d);
+               } else
+                       rfcomm_dlc_accept(d);
        } else {
                rfcomm_send_dm(s, dlci);
        }
@@ -1639,7 +1640,11 @@ static void rfcomm_process_connect(struct rfcomm_session *s)
                d = list_entry(p, struct rfcomm_dlc, list);
                if (d->state == BT_CONFIG) {
                        d->mtu = s->mtu;
-                       rfcomm_send_pn(s, 1, d);
+                       if (rfcomm_check_link_mode(d)) {
+                               set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+                               rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+                       } else
+                               rfcomm_send_pn(s, 1, d);
                }
        }
 }
@@ -1712,7 +1717,11 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
 
                if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) {
                        rfcomm_dlc_clear_timer(d);
-                       rfcomm_dlc_accept(d);
+                       if (d->out) {
+                               rfcomm_send_pn(s, 1, d);
+                               rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
+                       } else
+                               rfcomm_dlc_accept(d);
                        if (d->link_mode & RFCOMM_LM_SECURE) {
                                struct sock *sk = s->sock->sk;
                                hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon);
@@ -1720,7 +1729,10 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
                        continue;
                } else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {
                        rfcomm_dlc_clear_timer(d);
-                       rfcomm_send_dm(s, d->dlci);
+                       if (!d->out)
+                               rfcomm_send_dm(s, d->dlci);
+                       else
+                               d->state = BT_CLOSED;
                        __rfcomm_dlc_close(d, ECONNREFUSED);
                        continue;
                }
@@ -1729,7 +1741,7 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
                        continue;
 
                if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) &&
-                               d->mscex == RFCOMM_MSCEX_OK)
+                                               d->mscex == RFCOMM_MSCEX_OK)
                        rfcomm_process_tx(d);
        }
 }
@@ -1850,26 +1862,6 @@ static inline void rfcomm_process_sessions(void)
        rfcomm_unlock();
 }
 
-static void rfcomm_worker(void)
-{
-       BT_DBG("");
-
-       while (!atomic_read(&terminate)) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) {
-                       /* No pending events. Let's sleep.
-                        * Incoming connections and data will wake us up. */
-                       schedule();
-               }
-               set_current_state(TASK_RUNNING);
-
-               /* Process stuff */
-               clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
-               rfcomm_process_sessions();
-       }
-       return;
-}
-
 static int rfcomm_add_listener(bdaddr_t *ba)
 {
        struct sockaddr_l2 addr;
@@ -1935,22 +1927,28 @@ static void rfcomm_kill_listener(void)
 
 static int rfcomm_run(void *unused)
 {
-       rfcomm_thread = current;
-
-       atomic_inc(&running);
+       BT_DBG("");
 
-       daemonize("krfcommd");
        set_user_nice(current, -10);
 
-       BT_DBG("");
-
        rfcomm_add_listener(BDADDR_ANY);
 
-       rfcomm_worker();
+       while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) {
+                       /* No pending events. Let's sleep.
+                        * Incoming connections and data will wake us up. */
+                       schedule();
+               }
+               set_current_state(TASK_RUNNING);
+
+               /* Process stuff */
+               clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
+               rfcomm_process_sessions();
+       }
 
        rfcomm_kill_listener();
 
-       atomic_dec(&running);
        return 0;
 }
 
@@ -1971,7 +1969,8 @@ static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status)
        list_for_each_safe(p, n, &s->dlcs) {
                d = list_entry(p, struct rfcomm_dlc, list);
 
-               if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE))
+               if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) &&
+                               !(conn->link_mode & HCI_LM_ENCRYPT) && !status)
                        continue;
 
                if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
@@ -2005,6 +2004,14 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
        list_for_each_safe(p, n, &s->dlcs) {
                d = list_entry(p, struct rfcomm_dlc, list);
 
+               if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) &&
+                                       (d->state == BT_CONNECTED ||
+                                               d->state == BT_CONFIG) &&
+                                               !status && encrypt == 0x00) {
+                       __rfcomm_dlc_close(d, ECONNREFUSED);
+                       continue;
+               }
+
                if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
                        continue;
 
@@ -2059,7 +2066,11 @@ static int __init rfcomm_init(void)
 
        hci_register_cb(&rfcomm_cb);
 
-       kernel_thread(rfcomm_run, NULL, CLONE_KERNEL);
+       rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");
+       if (IS_ERR(rfcomm_thread)) {
+               hci_unregister_cb(&rfcomm_cb);
+               return PTR_ERR(rfcomm_thread);
+       }
 
        if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0)
                BT_ERR("Failed to create RFCOMM info file");
@@ -2081,14 +2092,7 @@ static void __exit rfcomm_exit(void)
 
        hci_unregister_cb(&rfcomm_cb);
 
-       /* Terminate working thread.
-        * ie. Set terminate flag and wake it up */
-       atomic_inc(&terminate);
-       rfcomm_schedule(RFCOMM_SCHED_STATE);
-
-       /* Wait until thread is running */
-       while (atomic_read(&running))
-               schedule();
+       kthread_stop(rfcomm_thread);
 
 #ifdef CONFIG_BT_RFCOMM_TTY
        rfcomm_cleanup_ttys();