fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / bluetooth / rfcomm / core.c
index d9b4ce0..278c867 100644 (file)
    SOFTWARE IS DISCLAIMED.
 */
 
-/* 
-   RPN support    -    Dirk Husemann <hud@zurich.ibm.com>
-*/
-
 /*
  * Bluetooth RFCOMM core.
  *
  * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/signal.h>
 #include <linux/init.h>
 #include <linux/wait.h>
+#include <linux/device.h>
 #include <linux/net.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
+#include <linux/mutex.h>
+
 #include <net/sock.h>
 #include <asm/uaccess.h>
 #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.2"
-
 #ifndef CONFIG_BT_RFCOMM_DEBUG
 #undef  BT_DBG
 #define BT_DBG(D...)
 #endif
 
-#ifdef CONFIG_PROC_FS
-struct proc_dir_entry *proc_bt_rfcomm;
-#endif
+#define VERSION "1.8"
 
-struct task_struct *rfcomm_thread;
-DECLARE_MUTEX(rfcomm_sem);
-unsigned long rfcomm_event;
+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;
+
+static DEFINE_MUTEX(rfcomm_mutex);
+#define rfcomm_lock()  mutex_lock(&rfcomm_mutex)
+#define rfcomm_unlock()        mutex_unlock(&rfcomm_mutex)
+
+static unsigned long rfcomm_event;
 
 static LIST_HEAD(session_list);
 static atomic_t terminate, running;
@@ -81,6 +82,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)
@@ -106,13 +111,71 @@ static void rfcomm_process_connect(struct rfcomm_session *s);
 #define __get_mcc_len(b)  ((b & 0xfe) >> 1)
 
 /* RPN macros */
-#define __rpn_line_settings(data, stop, parity)  ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x3) << 3))
+#define __rpn_line_settings(data, stop, parity)  ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x7) << 3))
 #define __get_rpn_data_bits(line) ((line) & 0x3)
 #define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
-#define __get_rpn_parity(line)    (((line) >> 3) & 0x3)
+#define __get_rpn_parity(line)    (((line) >> 3) & 0x7)
+
+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 ---- */
 
+/* reversed, 8-bit, poly=0x07 */
+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,
+       0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
+
+       0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
+       0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
+       0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
+       0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
+
+       0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
+       0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
+       0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
+       0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
+
+       0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
+       0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
+       0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
+       0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
+
+       0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
+       0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
+       0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
+       0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
+
+       0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
+       0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
+       0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
+       0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
+
+       0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
+       0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
+       0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
+       0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
+
+       0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
+       0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
+       0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
+       0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
+};
+
 /* CRC on 2 bytes */
 #define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
 
@@ -158,8 +221,7 @@ static int rfcomm_l2sock_create(struct socket **sock)
 
        BT_DBG("");
 
-       err = sock_create_kern(PF_BLUETOOTH, SOCK_SEQPACKET,
-                              BTPROTO_L2CAP, sock);
+       err = sock_create_kern(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock);
        if (!err) {
                struct sock *sk = (*sock)->sk;
                sk->sk_data_ready   = rfcomm_l2data_ready;
@@ -210,12 +272,12 @@ static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d)
        d->rx_credits = RFCOMM_DEFAULT_CREDITS;
 }
 
-struct rfcomm_dlc *rfcomm_dlc_alloc(int prio)
+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;
@@ -228,6 +290,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(int prio)
        rfcomm_dlc_clear_state(d);
        
        BT_DBG("%p", d);
+
        return d;
 }
 
@@ -324,14 +387,11 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
 
 int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
 {
-       mm_segment_t fs;
        int r;
 
        rfcomm_lock();
 
-       fs = get_fs(); set_fs(KERNEL_DS);
        r = __rfcomm_dlc_open(d, src, dst, channel);
-       set_fs(fs);
 
        rfcomm_unlock();
        return r;
@@ -377,14 +437,11 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
 
 int rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
 {
-       mm_segment_t fs;
        int r;
 
        rfcomm_lock();
 
-       fs = get_fs(); set_fs(KERNEL_DS);
        r = __rfcomm_dlc_close(d, err);
-       set_fs(fs);
 
        rfcomm_unlock();
        return r;
@@ -465,38 +522,39 @@ 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);
+       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);
 
        INIT_LIST_HEAD(&s->dlcs);
        s->state = state;
        s->sock  = sock;
 
-       s->mtu   = RFCOMM_DEFAULT_MTU;
-       s->cfc   = RFCOMM_CFC_UNKNOWN;
-       
-       list_add(&s->list, &session_list);
+       s->mtu = RFCOMM_DEFAULT_MTU;
+       s->cfc = disable_cfc ? RFCOMM_CFC_DISABLED : RFCOMM_CFC_UNKNOWN;
 
-       /* Do not increment module usage count for listeting sessions.
-        * Otherwise we won't be able to unload the module.
-        * Non listening session are added either by a socket or a TTYs
-        * which means that we already hold refcount to this module.
-        */
+       /* Do not increment module usage count for listening sessions.
+        * Otherwise we won't be able to unload the module. */
        if (state != BT_LISTEN)
-               __module_get(THIS_MODULE);
+               if (!try_module_get(THIS_MODULE)) {
+                       kfree(s);
+                       return NULL;
+               }
+
+       list_add(&s->list, &session_list);
+
        return s;
 }
 
-void rfcomm_session_del(struct rfcomm_session *s)
+static void rfcomm_session_del(struct rfcomm_session *s)
 {
        int state = s->state;
-       
+
        BT_DBG("session %p state %ld", s, s->state);
 
        list_del(&s->list);
@@ -511,7 +569,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;
@@ -527,7 +585,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;
@@ -548,13 +606,12 @@ 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;
-       struct l2cap_options opts;
        struct socket *sock;
-       int    size;
+       struct sock *sk;
 
        BT_DBG("%s %s", batostr(src), batostr(dst));
 
@@ -570,11 +627,10 @@ struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *
                goto failed;
 
        /* Set L2CAP options */
-       size = sizeof(opts);
-       sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
-       
-       opts.imtu = RFCOMM_MAX_L2CAP_MTU;
-       sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
+       sk = sock->sk;
+       lock_sock(sk);
+       l2cap_pi(sk)->imtu = l2cap_mtu;
+       release_sock(sk);
 
        s = rfcomm_session_add(sock, BT_BOUND);
        if (!s) {
@@ -588,7 +644,7 @@ struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *
        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)
+       if (*err == 0 || *err == -EINPROGRESS)
                return s;
 
        rfcomm_session_del(s);
@@ -612,16 +668,14 @@ void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *d
 static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
 {
        struct socket *sock = s->sock;
-       struct iovec iv = { data, len };
+       struct kvec iv = { data, len };
        struct msghdr msg;
 
        BT_DBG("session %p len %d", s, len);
 
        memset(&msg, 0, sizeof(msg));
-       msg.msg_iovlen = 1;
-       msg.msg_iov = &iv;
 
-       return sock_sendmsg(sock, &msg, len);
+       return kernel_sendmsg(sock, &msg, &iv, 1, len);
 }
 
 static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
@@ -759,17 +813,20 @@ 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++;
 
        return rfcomm_send_frame(s, buf, ptr - buf);
 }
 
-static 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 xon_char, u8 xoff_char, u16 param_mask)
+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 xon_char, u8 xoff_char, u16 param_mask)
 {
        struct rfcomm_hdr *hdr;
        struct rfcomm_mcc *mcc;
@@ -777,9 +834,9 @@ static 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, 
-                       flow_ctrl_settings, xon_char, xoff_char, param_mask);
+                       " 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);
        hdr->addr = __addr(s->initiator, 0);
@@ -797,7 +854,7 @@ static 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++;
 
@@ -905,7 +962,7 @@ static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
 static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
 {
        struct socket *sock = s->sock;
-       struct iovec iv[3];
+       struct kvec iv[3];
        struct msghdr msg;
        unsigned char hdr[5], crc[1];
 
@@ -930,10 +987,8 @@ static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int l
        iv[2].iov_len  = 1;
 
        memset(&msg, 0, sizeof(msg));
-       msg.msg_iovlen = 3;
-       msg.msg_iov = iv;
 
-       return sock_sendmsg(sock, &msg, 6 + len);
+       return kernel_sendmsg(sock, &msg, iv, 3, 6 + len);
 }
 
 static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
@@ -963,7 +1018,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);
@@ -1083,6 +1138,40 @@ 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;
+
+       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);
+
+       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 int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
 {
        struct rfcomm_dlc *d;
@@ -1105,14 +1194,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;
        }
@@ -1124,14 +1212,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);
        }
@@ -1146,17 +1233,24 @@ static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
        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;
 }
@@ -1226,16 +1320,16 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
        u8 xon_char  = 0;
        u8 xoff_char = 0;
        u16 rpn_mask = RFCOMM_RPN_PM_ALL;
-       
-       BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", 
-              dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
-              rpn->xon_char, rpn->xoff_char, rpn->param_mask);
-       
-       if (!cr) 
+
+       BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x",
+               dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
+               rpn->xon_char, rpn->xoff_char, rpn->param_mask);
+
+       if (!cr)
                return 0;
-       
+
        if (len == 1) {
-               /* request: return default setting */
+               /* This is a request, return default settings */
                bit_rate  = RFCOMM_RPN_BR_115200;
                data_bits = RFCOMM_RPN_DATA_8;
                stop_bits = RFCOMM_RPN_STOP_1;
@@ -1243,12 +1337,13 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                flow_ctrl = RFCOMM_RPN_FLOW_NONE;
                xon_char  = RFCOMM_RPN_XON_CHAR;
                xoff_char = RFCOMM_RPN_XOFF_CHAR;
-
                goto rpn_out;
        }
-       /* 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) {
+
+       /* 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 & 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);
@@ -1256,7 +1351,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                        rpn_mask ^= RFCOMM_RPN_PM_BITRATE;
                }
        }
-       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);
@@ -1264,7 +1360,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                        rpn_mask ^= RFCOMM_RPN_PM_DATA;
                }
        }
-       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);
@@ -1272,7 +1369,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                        rpn_mask ^= RFCOMM_RPN_PM_STOP;
                }
        }
-       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);
@@ -1280,7 +1378,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                        rpn_mask ^= RFCOMM_RPN_PM_PARITY;
                }
        }
-       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);
@@ -1288,7 +1387,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                        rpn_mask ^= RFCOMM_RPN_PM_FLOW;
                }
        }
-       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);
@@ -1296,7 +1396,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                        rpn_mask ^= RFCOMM_RPN_PM_XON;
                }
        }
-       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);
@@ -1306,9 +1407,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
        }
 
 rpn_out:
-       rfcomm_send_rpn(s, 0, dlci, 
-                       bit_rate, data_bits, stop_bits, parity, flow_ctrl,
-                       xon_char, xoff_char, rpn_mask);
+       rfcomm_send_rpn(s, 0, dlci, bit_rate, data_bits, stop_bits,
+                       parity, flow_ctrl, xon_char, xoff_char, rpn_mask);
 
        return 0;
 }
@@ -1319,14 +1419,13 @@ static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb
        u8 dlci = __get_dlci(rls->dlci);
 
        BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status);
-       
+
        if (!cr)
                return 0;
 
-       /* FIXME: We should probably do something with this
-          information here. But for now it's sufficient just
-          to reply -- Bluetooth 1.1 says it's mandatory to 
-          recognise and respond to RLS */
+       /* We should probably do something with this information here. But
+        * for now it's sufficient just to reply -- Bluetooth 1.1 says it's
+        * mandatory to recognise and respond to RLS */
 
        rfcomm_send_rls(s, 0, dlci, rls->status);
 
@@ -1342,7 +1441,7 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb
        BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
 
        d = rfcomm_dlc_get(s, dlci);
-       if (!d) 
+       if (!d)
                return 0;
 
        if (cr) {
@@ -1350,7 +1449,7 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb
                        set_bit(RFCOMM_TX_THROTTLED, &d->flags);
                else
                        clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
-               
+
                rfcomm_dlc_lock(d);
                if (d->modem_status)
                        d->modem_status(d, msc->v24_sig);
@@ -1359,7 +1458,7 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb
                rfcomm_send_msc(s, 0, dlci, msc->v24_sig);
 
                d->mscex |= RFCOMM_MSCEX_RX;
-       } else 
+       } else
                d->mscex |= RFCOMM_MSCEX_TX;
 
        return 0;
@@ -1469,7 +1568,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);
@@ -1480,7 +1579,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))
@@ -1548,7 +1647,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 */
@@ -1594,11 +1693,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;
 
@@ -1642,9 +1757,9 @@ static inline void rfcomm_accept_connection(struct rfcomm_session *s)
 
        BT_DBG("session %p", s);
 
-       if (sock_create_lite(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, &nsock))
+       if (sock_create_lite(PF_BLUETOOTH, sock->type, BTPROTO_L2CAP, &nsock))
                return;
-       
+
        nsock->ops  = sock->ops;
 
        __module_get(nsock->ops->owner);
@@ -1662,6 +1777,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);
@@ -1722,7 +1842,7 @@ static inline void rfcomm_process_sessions(void)
 
                rfcomm_session_put(s);
        }
-       
+
        rfcomm_unlock();
 }
 
@@ -1749,10 +1869,10 @@ static void rfcomm_worker(void)
 static int rfcomm_add_listener(bdaddr_t *ba)
 {
        struct sockaddr_l2 addr;
-       struct l2cap_options opts;
        struct socket *sock;
+       struct sock *sk;
        struct rfcomm_session *s;
-       int    size, err = 0;
+       int    err = 0;
 
        /* Create socket */
        err = rfcomm_l2sock_create(&sock);
@@ -1772,11 +1892,10 @@ static int rfcomm_add_listener(bdaddr_t *ba)
        }
 
        /* Set L2CAP options */
-       size = sizeof(opts);
-       sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
-
-       opts.imtu = RFCOMM_MAX_L2CAP_MTU;
-       sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
+       sk = sock->sk;
+       lock_sock(sk);
+       l2cap_pi(sk)->imtu = l2cap_mtu;
+       release_sock(sk);
 
        /* Start listening on the socket */
        err = sock->ops->listen(sock, 10);
@@ -1820,8 +1939,6 @@ static int rfcomm_run(void *unused)
        set_user_nice(current, -10);
        current->flags |= PF_NOFREEZE;
 
-       set_fs(KERNEL_DS);
-
        BT_DBG("");
 
        rfcomm_add_listener(BDADDR_ANY);
@@ -1834,128 +1951,115 @@ static int rfcomm_run(void *unused)
        return 0;
 }
 
-/* ---- Proc fs support ---- */
-#ifdef CONFIG_PROC_FS
-static void *rfcomm_seq_start(struct seq_file *seq, loff_t *pos)
+static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status)
 {
        struct rfcomm_session *s;
-       struct list_head *pp, *p;
-       loff_t l = *pos;
+       struct rfcomm_dlc *d;
+       struct list_head *p, *n;
 
-       rfcomm_lock();
+       BT_DBG("conn %p status 0x%02x", conn, status);
 
-       list_for_each(p, &session_list) {
-               s = list_entry(p, struct rfcomm_session, list);
-               list_for_each(pp, &s->dlcs)
-                       if (!l--) {
-                               seq->private = s;
-                               return pp;
-                       }
-       }
-       return NULL;
-}
+       s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
+       if (!s)
+               return;
 
-static void *rfcomm_seq_next(struct seq_file *seq, void *e, loff_t *pos)
-{
-       struct rfcomm_session *s = seq->private;
-       struct list_head *pp, *p = e;
-       (*pos)++;
+       rfcomm_session_hold(s);
+
+       list_for_each_safe(p, n, &s->dlcs) {
+               d = list_entry(p, struct rfcomm_dlc, list);
 
-       if (p->next != &s->dlcs)
-               return p->next;
+               if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE))
+                       continue;
 
-       list_for_each(p, &session_list) {
-               s = list_entry(p, struct rfcomm_session, list);
-               __list_for_each(pp, &s->dlcs) {
-                       seq->private = s;
-                       return pp;
-               }
+               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);
        }
-       return NULL;
-}
 
-static void rfcomm_seq_stop(struct seq_file *seq, void *e)
-{
-       rfcomm_unlock();
+       rfcomm_session_put(s);
+
+       rfcomm_schedule(RFCOMM_SCHED_AUTH);
 }
 
-static int  rfcomm_seq_show(struct seq_file *seq, void *e)
+static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
 {
-       struct rfcomm_session *s = seq->private;
-       struct sock *sk = s->sock->sk;
-       struct rfcomm_dlc *d = list_entry(e, struct rfcomm_dlc, list);
-
-       seq_printf(seq, "%s %s %ld %d %d %d %d\n",
-                       batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
-                       d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits);
-       return 0;
-}
+       struct rfcomm_session *s;
+       struct rfcomm_dlc *d;
+       struct list_head *p, *n;
 
-static struct seq_operations rfcomm_seq_ops = {
-       .start  = rfcomm_seq_start,
-       .next   = rfcomm_seq_next,
-       .stop   = rfcomm_seq_stop,
-       .show   = rfcomm_seq_show 
-};
+       BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt);
 
-static int rfcomm_seq_open(struct inode *inode, struct file *file)
-{
-       return seq_open(file, &rfcomm_seq_ops);
-}
+       s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
+       if (!s)
+               return;
 
-static struct file_operations rfcomm_seq_fops = {
-       .owner   = THIS_MODULE,
-       .open    = rfcomm_seq_open,
-       .read    = seq_read,
-       .llseek  = seq_lseek,
-       .release = seq_release,
-};
+       rfcomm_session_hold(s);
 
-static int  __init rfcomm_proc_init(void)
-{
-        struct proc_dir_entry *p;
+       list_for_each_safe(p, n, &s->dlcs) {
+               d = list_entry(p, struct rfcomm_dlc, list);
 
-       proc_bt_rfcomm = proc_mkdir("rfcomm", proc_bt);
-       if (proc_bt_rfcomm) {
-               proc_bt_rfcomm->owner = THIS_MODULE;
+               if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
+                       continue;
 
-               p = create_proc_entry("dlc", S_IRUGO, proc_bt_rfcomm);
-               if (p)
-                       p->proc_fops = &rfcomm_seq_fops;
+               if (!status && encrypt)
+                       set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
+               else
+                       set_bit(RFCOMM_AUTH_REJECT, &d->flags);
        }
-        return 0;
-}
 
-static void __exit rfcomm_proc_cleanup(void)
-{
-        remove_proc_entry("dlc", proc_bt_rfcomm);
+       rfcomm_session_put(s);
 
-       remove_proc_entry("rfcomm", proc_bt);
+       rfcomm_schedule(RFCOMM_SCHED_AUTH);
 }
 
-#else /* CONFIG_PROC_FS */
+static struct hci_cb rfcomm_cb = {
+       .name           = "RFCOMM",
+       .auth_cfm       = rfcomm_auth_cfm,
+       .encrypt_cfm    = rfcomm_encrypt_cfm
+};
 
-static int  __init rfcomm_proc_init(void)
+static ssize_t rfcomm_dlc_sysfs_show(struct class *dev, char *buf)
 {
-        return 0;
-}
+       struct rfcomm_session *s;
+       struct list_head *pp, *p;
+       char *str = buf;
 
-static void __exit rfcomm_proc_cleanup(void)
-{
-        return;
+       rfcomm_lock();
+
+       list_for_each(p, &session_list) {
+               s = list_entry(p, struct rfcomm_session, list);
+               list_for_each(pp, &s->dlcs) {
+                       struct sock *sk = s->sock->sk;
+                       struct rfcomm_dlc *d = list_entry(pp, struct rfcomm_dlc, list);
+
+                       str += sprintf(str, "%s %s %ld %d %d %d %d\n",
+                                       batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
+                                       d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits);
+               }
+       }
+
+       rfcomm_unlock();
+
+       return (str - buf);
 }
-#endif /* CONFIG_PROC_FS */
+
+static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL);
 
 /* ---- Initialization ---- */
 static int __init rfcomm_init(void)
 {
        l2cap_load();
 
-       kernel_thread(rfcomm_run, NULL, CLONE_KERNEL);
+       hci_register_cb(&rfcomm_cb);
 
-       BT_INFO("RFCOMM ver %s", VERSION);
+       kernel_thread(rfcomm_run, NULL, CLONE_KERNEL);
 
-       rfcomm_proc_init();
+       if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0)
+               BT_ERR("Failed to create RFCOMM info file");
 
        rfcomm_init_sockets();
 
@@ -1963,11 +2067,17 @@ static int __init rfcomm_init(void)
        rfcomm_init_ttys();
 #endif
 
+       BT_INFO("RFCOMM ver %s", VERSION);
+
        return 0;
 }
 
 static void __exit rfcomm_exit(void)
 {
+       class_remove_file(bt_class, &class_attr_rfcomm_dlc);
+
+       hci_unregister_cb(&rfcomm_cb);
+
        /* Terminate working thread.
         * ie. Set terminate flag and wake it up */
        atomic_inc(&terminate);
@@ -1982,13 +2092,20 @@ static void __exit rfcomm_exit(void)
 #endif
 
        rfcomm_cleanup_sockets();
-
-       rfcomm_proc_cleanup();
 }
 
 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_DESCRIPTION("Bluetooth RFCOMM ver " VERSION);
 MODULE_VERSION(VERSION);