Bluetooth: Add support for deferring RFCOMM connection setup
[safe/jmp/linux-2.6] / net / bluetooth / rfcomm / core.c
index 6de33fe..edee49e 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    RFCOMM implementation for Linux Bluetooth stack (BlueZ).
    Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
    Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
    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 RFCOMM core.
- *
- * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $
  */
 
 #include <linux/module.h>
@@ -37,6 +35,7 @@
 #include <linux/device.h>
 #include <linux/net.h>
 #include <linux/mutex.h>
+#include <linux/kthread.h>
 
 #include <net/sock.h>
 #include <asm/uaccess.h>
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/rfcomm.h>
 
-#ifndef CONFIG_BT_RFCOMM_DEBUG
-#undef  BT_DBG
-#define BT_DBG(D...)
-#endif
-
-#define VERSION "1.7"
+#define VERSION "1.10"
 
+static int disable_cfc = 0;
+static int channel_mtu = -1;
 static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU;
 
 static struct task_struct *rfcomm_thread;
@@ -65,7 +61,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);
@@ -132,7 +127,7 @@ static inline void rfcomm_session_put(struct rfcomm_session *s)
 /* ---- RFCOMM FCS computation ---- */
 
 /* reversed, 8-bit, poly=0x07 */
-static unsigned char rfcomm_crc_table[256] = { 
+static unsigned char rfcomm_crc_table[256] = {
        0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
        0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
        0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
@@ -177,13 +172,13 @@ static unsigned char rfcomm_crc_table[256] = {
 /* CRC on 2 bytes */
 #define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
 
-/* FCS on 2 bytes */ 
+/* FCS on 2 bytes */
 static inline u8 __fcs(u8 *data)
 {
        return (0xff - __crc(data));
 }
 
-/* FCS on 3 bytes */ 
+/* FCS on 3 bytes */
 static inline u8 __fcs2(u8 *data)
 {
        return (0xff - rfcomm_crc_table[__crc(data) ^ data[2]]);
@@ -228,6 +223,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)
 {
@@ -272,22 +282,21 @@ static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d)
 
 struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio)
 {
-       struct rfcomm_dlc *d = kmalloc(sizeof(*d), prio);
+       struct rfcomm_dlc *d = kzalloc(sizeof(*d), prio);
+
        if (!d)
                return NULL;
-       memset(d, 0, sizeof(*d));
 
-       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);
        atomic_set(&d->refcnt, 1);
 
        rfcomm_dlc_clear_state(d);
-       
+
        BT_DBG("%p", d);
+
        return d;
 }
 
@@ -342,7 +351,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
        int err = 0;
        u8 dlci;
 
-       BT_DBG("dlc %p state %ld %s %s channel %d", 
+       BT_DBG("dlc %p state %ld %s %s channel %d",
                        d, d->state, batostr(src), batostr(dst), channel);
 
        if (channel < 1 || channel > 30)
@@ -370,15 +379,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;
 }
 
@@ -404,9 +421,16 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
                        d, d->state, d->dlci, err, s);
 
        switch (d->state) {
-       case BT_CONNECTED:
-       case BT_CONFIG:
        case BT_CONNECT:
+       case BT_CONFIG:
+               if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
+                       set_bit(RFCOMM_AUTH_REJECT, &d->flags);
+                       rfcomm_schedule(RFCOMM_SCHED_AUTH);
+                       break;
+               }
+               /* Fall through */
+
+       case BT_CONNECTED:
                d->state = BT_DISCONN;
                if (skb_queue_empty(&d->tx_queue)) {
                        rfcomm_send_disc(s, d->dlci);
@@ -417,6 +441,14 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
                }
                break;
 
+       case BT_OPEN:
+               if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
+                       set_bit(RFCOMM_AUTH_REJECT, &d->flags);
+                       rfcomm_schedule(RFCOMM_SCHED_AUTH);
+                       break;
+               }
+               /* Fall through */
+
        default:
                rfcomm_dlc_clear_timer(d);
 
@@ -464,7 +496,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);
 
@@ -475,7 +507,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);
 
@@ -486,21 +518,21 @@ void fastcall __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
        rfcomm_schedule(RFCOMM_SCHED_TX);
 }
 
-/* 
+/*
    Set/get modem status functions use _local_ status i.e. what we report
    to the other side.
    Remote status is provided by dlc->modem_status() callback.
  */
 int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig)
 {
-       BT_DBG("dlc %p state %ld v24_sig 0x%x", 
+       BT_DBG("dlc %p state %ld v24_sig 0x%x",
                        d, d->state, v24_sig);
 
        if (test_bit(RFCOMM_RX_THROTTLED, &d->flags))
                v24_sig |= RFCOMM_V24_FC;
        else
                v24_sig &= ~RFCOMM_V24_FC;
-       
+
        d->v24_sig = v24_sig;
 
        if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags))
@@ -511,7 +543,7 @@ int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig)
 
 int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig)
 {
-       BT_DBG("dlc %p state %ld v24_sig 0x%x", 
+       BT_DBG("dlc %p state %ld v24_sig 0x%x",
                        d, d->state, d->v24_sig);
 
        *v24_sig = d->v24_sig;
@@ -521,10 +553,10 @@ int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig)
 /* ---- RFCOMM sessions ---- */
 static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
 {
-       struct rfcomm_session *s = kmalloc(sizeof(*s), GFP_KERNEL);
+       struct rfcomm_session *s = kzalloc(sizeof(*s), GFP_KERNEL);
+
        if (!s)
                return NULL;
-       memset(s, 0, sizeof(*s));
 
        BT_DBG("session %p sock %p", s, sock);
 
@@ -533,7 +565,7 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
        s->sock  = sock;
 
        s->mtu = RFCOMM_DEFAULT_MTU;
-       s->cfc = RFCOMM_CFC_UNKNOWN;
+       s->cfc = disable_cfc ? RFCOMM_CFC_DISABLED : RFCOMM_CFC_UNKNOWN;
 
        /* Do not increment module usage count for listening sessions.
         * Otherwise we won't be able to unload the module. */
@@ -573,7 +605,7 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
        struct bt_sock *sk;
        list_for_each_safe(p, n, &session_list) {
                s = list_entry(p, struct rfcomm_session, list);
-               sk = bt_sk(s->sock->sk); 
+               sk = bt_sk(s->sock->sk);
 
                if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) &&
                                !bacmp(&sk->dst, dst))
@@ -619,7 +651,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
        bacpy(&addr.l2_bdaddr, src);
        addr.l2_family = AF_BLUETOOTH;
        addr.l2_psm    = 0;
-       *err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+       *err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
        if (*err < 0)
                goto failed;
 
@@ -640,8 +672,8 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
        bacpy(&addr.l2_bdaddr, dst);
        addr.l2_family = AF_BLUETOOTH;
        addr.l2_psm    = htobs(RFCOMM_PSM);
-       *err = sock->ops->connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
-       if (*err == 0 || *err == -EAGAIN)
+       *err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
+       if (*err == 0 || *err == -EINPROGRESS)
                return s;
 
        rfcomm_session_del(s);
@@ -810,7 +842,10 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d
                pn->credits   = 0;
        }
 
-       pn->mtu = htobs(d->mtu);
+       if (cr && channel_mtu >= 0)
+               pn->mtu = htobs(channel_mtu);
+       else
+               pn->mtu = htobs(d->mtu);
 
        *ptr = __fcs(buf); ptr++;
 
@@ -819,7 +854,7 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d
 
 int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
                        u8 bit_rate, u8 data_bits, u8 stop_bits,
-                       u8 parity, u8 flow_ctrl_settings, 
+                       u8 parity, u8 flow_ctrl_settings,
                        u8 xon_char, u8 xoff_char, u16 param_mask)
 {
        struct rfcomm_hdr *hdr;
@@ -828,8 +863,8 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
        u8 buf[16], *ptr = buf;
 
        BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x"
-                       " flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", 
-               s, cr, dlci, bit_rate, data_bits, stop_bits, parity, 
+                       " flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x",
+               s, cr, dlci, bit_rate, data_bits, stop_bits, parity,
                flow_ctrl_settings, xon_char, xoff_char, param_mask);
 
        hdr = (void *) ptr; ptr += sizeof(*hdr);
@@ -848,7 +883,7 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
        rpn->flow_ctrl     = flow_ctrl_settings;
        rpn->xon_char      = xon_char;
        rpn->xoff_char     = xoff_char;
-       rpn->param_mask    = param_mask;
+       rpn->param_mask    = cpu_to_le16(param_mask);
 
        *ptr = __fcs(buf); ptr++;
 
@@ -1012,7 +1047,7 @@ static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
 
        if (len > 127) {
                hdr = (void *) skb_push(skb, 4);
-               put_unaligned(htobs(__len16(len)), (u16 *) &hdr->len);
+               put_unaligned(htobs(__len16(len)), (__le16 *) &hdr->len);
        } else {
                hdr = (void *) skb_push(skb, 3);
                hdr->len = __len8(len);
@@ -1052,6 +1087,12 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
                case BT_DISCONN:
                        d->state = BT_CLOSED;
                        __rfcomm_dlc_close(d, 0);
+
+                       if (list_empty(&s->dlcs)) {
+                               s->state = BT_DISCONN;
+                               rfcomm_send_disc(s, 0);
+                       }
+
                        break;
                }
        } else {
@@ -1061,6 +1102,10 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
                        s->state = BT_CONNECTED;
                        rfcomm_process_connect(s);
                        break;
+
+               case BT_DISCONN:
+                       rfcomm_session_put(s);
+                       break;
                }
        }
        return 0;
@@ -1114,9 +1159,9 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
 
                        d->state = BT_CLOSED;
                        __rfcomm_dlc_close(d, err);
-               } else 
+               } else
                        rfcomm_send_dm(s, dlci);
-                       
+
        } else {
                rfcomm_send_ua(s, 0);
 
@@ -1132,23 +1177,10 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
        return 0;
 }
 
-static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
+void rfcomm_dlc_accept(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);
@@ -1158,9 +1190,26 @@ static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
        d->state_change(d, 0);
        rfcomm_dlc_unlock(d);
 
+       if (d->link_mode & RFCOMM_LM_MASTER)
+               hci_conn_switch_role(l2cap_pi(sk)->conn->hcon, 0x00);
+
        rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
 }
 
+static void rfcomm_check_accept(struct rfcomm_dlc *d)
+{
+       if (rfcomm_check_link_mode(d)) {
+               set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+               rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+       } else {
+               if (d->defer_setup) {
+                       set_bit(RFCOMM_DEFER_SETUP, &d->flags);
+                       rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+               } else
+                       rfcomm_dlc_accept(d);
+       }
+}
+
 static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
 {
        struct rfcomm_dlc *d;
@@ -1183,13 +1232,7 @@ 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 */
-                       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);
+                       rfcomm_check_accept(d);
                }
                return 0;
        }
@@ -1201,13 +1244,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
                d->addr = __addr(s->initiator, dlci);
                rfcomm_dlc_link(s, 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_dlc_accept(d);
+               rfcomm_check_accept(d);
        } else {
                rfcomm_send_dm(s, dlci);
        }
@@ -1219,20 +1256,27 @@ static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
 {
        struct rfcomm_session *s = d->session;
 
-       BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", 
+       BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d",
                        d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits);
 
-       if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) {
-               d->cfc = s->cfc = RFCOMM_CFC_ENABLED;
+       if ((pn->flow_ctrl == 0xf0 && s->cfc != RFCOMM_CFC_DISABLED) ||
+                                               pn->flow_ctrl == 0xe0) {
+               d->cfc = RFCOMM_CFC_ENABLED;
                d->tx_credits = pn->credits;
        } else {
-               d->cfc = s->cfc = RFCOMM_CFC_DISABLED;
+               d->cfc = RFCOMM_CFC_DISABLED;
                set_bit(RFCOMM_TX_THROTTLED, &d->flags);
        }
 
+       if (s->cfc == RFCOMM_CFC_UNKNOWN)
+               s->cfc = d->cfc;
+
        d->priority = pn->priority;
 
-       d->mtu = s->mtu = btohs(pn->mtu);
+       d->mtu = btohs(pn->mtu);
+
+       if (cr && d->mtu > s->mtu)
+               d->mtu = s->mtu;
 
        return 0;
 }
@@ -1325,7 +1369,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
        /* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit,
         * no parity, no flow control lines, normal XON/XOFF chars */
 
-       if (rpn->param_mask & RFCOMM_RPN_PM_BITRATE) {
+       if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_BITRATE)) {
                bit_rate = rpn->bit_rate;
                if (bit_rate != RFCOMM_RPN_BR_115200) {
                        BT_DBG("RPN bit rate mismatch 0x%x", bit_rate);
@@ -1334,7 +1378,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                }
        }
 
-       if (rpn->param_mask & RFCOMM_RPN_PM_DATA) {
+       if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_DATA)) {
                data_bits = __get_rpn_data_bits(rpn->line_settings);
                if (data_bits != RFCOMM_RPN_DATA_8) {
                        BT_DBG("RPN data bits mismatch 0x%x", data_bits);
@@ -1343,7 +1387,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                }
        }
 
-       if (rpn->param_mask & RFCOMM_RPN_PM_STOP) {
+       if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_STOP)) {
                stop_bits = __get_rpn_stop_bits(rpn->line_settings);
                if (stop_bits != RFCOMM_RPN_STOP_1) {
                        BT_DBG("RPN stop bits mismatch 0x%x", stop_bits);
@@ -1352,7 +1396,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                }
        }
 
-       if (rpn->param_mask & RFCOMM_RPN_PM_PARITY) {
+       if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_PARITY)) {
                parity = __get_rpn_parity(rpn->line_settings);
                if (parity != RFCOMM_RPN_PARITY_NONE) {
                        BT_DBG("RPN parity mismatch 0x%x", parity);
@@ -1361,7 +1405,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                }
        }
 
-       if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
+       if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_FLOW)) {
                flow_ctrl = rpn->flow_ctrl;
                if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
                        BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl);
@@ -1370,7 +1414,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                }
        }
 
-       if (rpn->param_mask & RFCOMM_RPN_PM_XON) {
+       if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XON)) {
                xon_char = rpn->xon_char;
                if (xon_char != RFCOMM_RPN_XON_CHAR) {
                        BT_DBG("RPN XON char mismatch 0x%x", xon_char);
@@ -1379,7 +1423,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                }
        }
 
-       if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) {
+       if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XOFF)) {
                xoff_char = rpn->xoff_char;
                if (xoff_char != RFCOMM_RPN_XOFF_CHAR) {
                        BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char);
@@ -1433,10 +1477,14 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb
                        clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
 
                rfcomm_dlc_lock(d);
+
+               d->remote_v24_sig = msc->v24_sig;
+
                if (d->modem_status)
                        d->modem_status(d, msc->v24_sig);
+
                rfcomm_dlc_unlock(d);
-               
+
                rfcomm_send_msc(s, 0, dlci, msc->v24_sig);
 
                d->mscex |= RFCOMM_MSCEX_RX;
@@ -1549,7 +1597,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
 
        /* Trim FCS */
        skb->len--; skb->tail--;
-       fcs = *(u8 *) skb->tail;
+       fcs = *(u8 *)skb_tail_pointer(skb);
 
        if (__check_fcs(skb->data, type, fcs)) {
                BT_ERR("bad checksum in packet");
@@ -1610,7 +1658,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);
                }
        }
 }
@@ -1623,18 +1675,18 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
        struct sk_buff *skb;
        int err;
 
-       BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d", 
+       BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d",
                        d, d->state, d->cfc, d->rx_credits, d->tx_credits);
 
        /* Send pending MSC */
        if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
-               rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); 
+               rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
 
        if (d->cfc) {
-               /* CFC enabled. 
+               /* CFC enabled.
                 * Give them some credits */
                if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
-                               d->rx_credits <= (d->cfc >> 2)) {
+                               d->rx_credits <= (d->cfc >> 2)) {
                        rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits);
                        d->rx_credits = d->cfc;
                }
@@ -1683,7 +1735,16 @@ 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 {
+                               if (d->defer_setup) {
+                                       set_bit(RFCOMM_DEFER_SETUP, &d->flags);
+                                       rfcomm_dlc_set_timer(d, RFCOMM_AUTH_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);
@@ -1691,7 +1752,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;
                }
@@ -1700,7 +1764,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);
        }
 }
@@ -1739,18 +1803,9 @@ static inline void rfcomm_accept_connection(struct rfcomm_session *s)
 
        BT_DBG("session %p", s);
 
-       if (sock_create_lite(PF_BLUETOOTH, sock->type, BTPROTO_L2CAP, &nsock))
-               return;
-
-       nsock->ops  = sock->ops;
-
-       __module_get(nsock->ops->owner);
-
-       err = sock->ops->accept(sock, nsock, O_NONBLOCK);
-       if (err < 0) {
-               sock_release(nsock);
+       err = kernel_accept(sock, &nsock, O_NONBLOCK);
+       if (err < 0)
                return;
-       }
 
        /* Set our callbacks */
        nsock->sk->sk_data_ready   = rfcomm_l2data_ready;
@@ -1759,6 +1814,11 @@ static inline void rfcomm_accept_connection(struct rfcomm_session *s)
        s = rfcomm_session_add(nsock, BT_OPEN);
        if (s) {
                rfcomm_session_hold(s);
+
+               /* We should adjust MTU on incoming sessions.
+                * L2CAP MTU minus UIH header and FCS. */
+               s->mtu = min(l2cap_pi(nsock->sk)->omtu, l2cap_pi(nsock->sk)->imtu) - 5;
+
                rfcomm_schedule(RFCOMM_SCHED_RX);
        } else
                sock_release(nsock);
@@ -1823,26 +1883,6 @@ static inline void rfcomm_process_sessions(void)
        rfcomm_unlock();
 }
 
-static void rfcomm_worker(void)
-{
-       BT_DBG("");
-
-       while (!atomic_read(&terminate)) {
-               if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) {
-                       /* No pending events. Let's sleep.
-                        * Incoming connections and data will wake us up. */
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       schedule();
-               }
-
-               /* Process stuff */
-               clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
-               rfcomm_process_sessions();
-       }
-       set_current_state(TASK_RUNNING);
-       return;
-}
-
 static int rfcomm_add_listener(bdaddr_t *ba)
 {
        struct sockaddr_l2 addr;
@@ -1853,7 +1893,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
 
        /* Create socket */
        err = rfcomm_l2sock_create(&sock);
-       if (err < 0) { 
+       if (err < 0) {
                BT_ERR("Create socket failed %d", err);
                return err;
        }
@@ -1862,7 +1902,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
        bacpy(&addr.l2_bdaddr, ba);
        addr.l2_family = AF_BLUETOOTH;
        addr.l2_psm    = htobs(RFCOMM_PSM);
-       err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+       err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
        if (err < 0) {
                BT_ERR("Bind failed %d", err);
                goto failed;
@@ -1875,7 +1915,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
        release_sock(sk);
 
        /* Start listening on the socket */
-       err = sock->ops->listen(sock, 10);
+       err = kernel_listen(sock, 10);
        if (err) {
                BT_ERR("Listen failed %d", err);
                goto failed;
@@ -1908,23 +1948,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);
-       current->flags |= PF_NOFREEZE;
-
-       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;
 }
 
@@ -1945,7 +1990,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))
@@ -1979,6 +2025,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;
 
@@ -2033,9 +2087,14 @@ 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);
+       }
 
-       class_create_file(bt_class, &class_attr_rfcomm_dlc);
+       if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0)
+               BT_ERR("Failed to create RFCOMM info file");
 
        rfcomm_init_sockets();
 
@@ -2054,14 +2113,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();
@@ -2073,10 +2125,16 @@ static void __exit rfcomm_exit(void)
 module_init(rfcomm_init);
 module_exit(rfcomm_exit);
 
+module_param(disable_cfc, bool, 0644);
+MODULE_PARM_DESC(disable_cfc, "Disable credit based flow control");
+
+module_param(channel_mtu, int, 0644);
+MODULE_PARM_DESC(channel_mtu, "Default MTU for the RFCOMM channel");
+
 module_param(l2cap_mtu, uint, 0644);
 MODULE_PARM_DESC(l2cap_mtu, "Default MTU for the L2CAP connection");
 
-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION);
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL");