vserver 1.9.5.x5
[linux-2.6.git] / net / bluetooth / rfcomm / core.c
index f53a8b0..061677e 100644 (file)
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/rfcomm.h>
 
-#define VERSION "1.3"
+#define VERSION "1.5"
 
 #ifndef CONFIG_BT_RFCOMM_DEBUG
 #undef  BT_DBG
 struct proc_dir_entry *proc_bt_rfcomm;
 #endif
 
-struct task_struct *rfcomm_thread;
-DECLARE_MUTEX(rfcomm_sem);
+static struct task_struct *rfcomm_thread;
+
+static DECLARE_MUTEX(rfcomm_sem);
+#define rfcomm_lock()  down(&rfcomm_sem);
+#define rfcomm_unlock()        up(&rfcomm_sem);
+
 unsigned long rfcomm_event;
 
 static LIST_HEAD(session_list);
@@ -81,6 +86,10 @@ static void rfcomm_make_uih(struct sk_buff *skb, u8 addr);
 
 static void rfcomm_process_connect(struct rfcomm_session *s);
 
+static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err);
+static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst);
+static void rfcomm_session_del(struct rfcomm_session *s);
+
 /* ---- RFCOMM frame parsing macros ---- */
 #define __get_dlci(b)     ((b & 0xfc) >> 2)
 #define __get_channel(b)  ((b & 0xf8) >> 3)
@@ -111,6 +120,21 @@ static void rfcomm_process_connect(struct rfcomm_session *s);
 #define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
 #define __get_rpn_parity(line)    (((line) >> 3) & 0x3)
 
+static inline void rfcomm_schedule(uint event)
+{
+       if (!rfcomm_thread)
+               return;
+       //set_bit(event, &rfcomm_event);
+       set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
+       wake_up_process(rfcomm_thread);
+}
+
+static inline void rfcomm_session_put(struct rfcomm_session *s)
+{
+       if (atomic_dec_and_test(&s->refcnt))
+               rfcomm_session_del(s);
+}
+
 /* ---- RFCOMM FCS computation ---- */
 
 /* CRC on 2 bytes */
@@ -458,7 +482,7 @@ int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig)
 }
 
 /* ---- RFCOMM sessions ---- */
-struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
+static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
 {
        struct rfcomm_session *s = kmalloc(sizeof(*s), GFP_KERNEL);
        if (!s)
@@ -487,7 +511,7 @@ struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
        return s;
 }
 
-void rfcomm_session_del(struct rfcomm_session *s)
+static void rfcomm_session_del(struct rfcomm_session *s)
 {
        int state = s->state;
 
@@ -505,7 +529,7 @@ void rfcomm_session_del(struct rfcomm_session *s)
                module_put(THIS_MODULE);
 }
 
-struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
+static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
 {
        struct rfcomm_session *s;
        struct list_head *p, *n;
@@ -521,7 +545,7 @@ struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
        return NULL;
 }
 
-void rfcomm_session_close(struct rfcomm_session *s, int err)
+static void rfcomm_session_close(struct rfcomm_session *s, int err)
 {
        struct rfcomm_dlc *d;
        struct list_head *p, *n;
@@ -542,7 +566,7 @@ void rfcomm_session_close(struct rfcomm_session *s, int err)
        rfcomm_session_put(s);
 }
 
-struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err)
+static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err)
 {
        struct rfcomm_session *s = NULL;
        struct sockaddr_l2 addr;
@@ -1071,6 +1095,35 @@ 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)
+{
+       BT_DBG("dlc %p", d);
+
+       rfcomm_send_ua(d->session, d->dlci);
+
+       rfcomm_dlc_lock(d);
+       d->state = BT_CONNECTED;
+       d->state_change(d, 0);
+       rfcomm_dlc_unlock(d);
+
+       rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
+}
+
 static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
 {
        struct rfcomm_dlc *d;
@@ -1093,14 +1146,13 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
        if (d) {
                if (d->state == BT_OPEN) {
                        /* DLC was previously opened by PN request */
-                       rfcomm_send_ua(s, dlci);
-
-                       rfcomm_dlc_lock(d);
-                       d->state = BT_CONNECTED;
-                       d->state_change(d, 0);
-                       rfcomm_dlc_unlock(d);
+                       if (rfcomm_check_link_mode(d)) {
+                               set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+                               rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+                               return 0;
+                       }
 
-                       rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+                       rfcomm_dlc_accept(d);
                }
                return 0;
        }
@@ -1112,14 +1164,13 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
                d->addr = __addr(s->initiator, dlci);
                rfcomm_dlc_link(s, d);
 
-               rfcomm_send_ua(s, dlci);
-
-               rfcomm_dlc_lock(d);
-               d->state = BT_CONNECTED;
-               d->state_change(d, 0);
-               rfcomm_dlc_unlock(d);
+               if (rfcomm_check_link_mode(d)) {
+                       set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+                       rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+                       return 0;
+               }
 
-               rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+               rfcomm_dlc_accept(d);
        } else {
                rfcomm_send_dm(s, dlci);
        }
@@ -1457,7 +1508,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
        /* Trim FCS */
        skb->len--; skb->tail--;
        fcs = *(u8 *) skb->tail;
-       
+
        if (__check_fcs(skb->data, type, fcs)) {
                BT_ERR("bad checksum in packet");
                kfree_skb(skb);
@@ -1468,7 +1519,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
                skb_pull(skb, 3);
        else
                skb_pull(skb, 4);
-       
+
        switch (type) {
        case RFCOMM_SABM:
                if (__test_pf(hdr->ctrl))
@@ -1536,7 +1587,7 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
        /* Send pending MSC */
        if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
                rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); 
-       
+
        if (d->cfc) {
                /* CFC enabled. 
                 * Give them some credits */
@@ -1582,11 +1633,27 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
 
        list_for_each_safe(p, n, &s->dlcs) {
                d = list_entry(p, struct rfcomm_dlc, list);
+
                if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) {
                        __rfcomm_dlc_close(d, ETIMEDOUT);
                        continue;
                }
 
+               if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) {
+                       rfcomm_dlc_clear_timer(d);
+                       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);
+                       }
+                       continue;
+               } else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {
+                       rfcomm_dlc_clear_timer(d);
+                       rfcomm_send_dm(s, d->dlci);
+                       __rfcomm_dlc_close(d, ECONNREFUSED);
+                       continue;
+               }
+
                if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
                        continue;
 
@@ -1710,7 +1777,7 @@ static inline void rfcomm_process_sessions(void)
 
                rfcomm_session_put(s);
        }
-       
+
        rfcomm_unlock();
 }
 
@@ -1819,6 +1886,77 @@ static int rfcomm_run(void *unused)
        return 0;
 }
 
+static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status)
+{
+       struct rfcomm_session *s;
+       struct rfcomm_dlc *d;
+       struct list_head *p, *n;
+
+       BT_DBG("conn %p status 0x%02x", conn, status);
+
+       s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
+       if (!s)
+               return;
+
+       rfcomm_session_hold(s);
+
+       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))
+                       continue;
+
+               if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
+                       continue;
+
+               if (!status)
+                       set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
+               else
+                       set_bit(RFCOMM_AUTH_REJECT, &d->flags);
+       }
+
+       rfcomm_session_put(s);
+
+       rfcomm_schedule(RFCOMM_SCHED_AUTH);
+}
+
+static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
+{
+       struct rfcomm_session *s;
+       struct rfcomm_dlc *d;
+       struct list_head *p, *n;
+
+       BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt);
+
+       s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
+       if (!s)
+               return;
+
+       rfcomm_session_hold(s);
+
+       list_for_each_safe(p, n, &s->dlcs) {
+               d = list_entry(p, struct rfcomm_dlc, list);
+
+               if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
+                       continue;
+
+               if (!status && encrypt)
+                       set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
+               else
+                       set_bit(RFCOMM_AUTH_REJECT, &d->flags);
+       }
+
+       rfcomm_session_put(s);
+
+       rfcomm_schedule(RFCOMM_SCHED_AUTH);
+}
+
+static struct hci_cb rfcomm_cb = {
+       .name           = "RFCOMM",
+       .auth_cfm       = rfcomm_auth_cfm,
+       .encrypt_cfm    = rfcomm_encrypt_cfm
+};
+
 /* ---- Proc fs support ---- */
 #ifdef CONFIG_PROC_FS
 static void *rfcomm_seq_start(struct seq_file *seq, loff_t *pos)
@@ -1936,6 +2074,8 @@ static int __init rfcomm_init(void)
 {
        l2cap_load();
 
+       hci_register_cb(&rfcomm_cb);
+
        kernel_thread(rfcomm_run, NULL, CLONE_KERNEL);
 
        BT_INFO("RFCOMM ver %s", VERSION);
@@ -1953,6 +2093,8 @@ static int __init rfcomm_init(void)
 
 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);