[Bluetooth] Add RFCOMM role switch support
[pandora-kernel.git] / net / bluetooth / rfcomm / core.c
index 0d89d64..155a2b9 100644 (file)
@@ -27,7 +27,6 @@
  * $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>
@@ -37,6 +36,8 @@
 #include <linux/wait.h>
 #include <linux/device.h>
 #include <linux/net.h>
+#include <linux/mutex.h>
+
 #include <net/sock.h>
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/rfcomm.h>
 
-#define VERSION "1.6"
-
 #ifndef CONFIG_BT_RFCOMM_DEBUG
 #undef  BT_DBG
 #define BT_DBG(D...)
 #endif
 
+#define VERSION "1.8"
+
+static int disable_cfc = 0;
+static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU;
+
 static struct task_struct *rfcomm_thread;
 
-static DECLARE_MUTEX(rfcomm_sem);
-#define rfcomm_lock()  down(&rfcomm_sem);
-#define rfcomm_unlock()        up(&rfcomm_sem);
+static DEFINE_MUTEX(rfcomm_mutex);
+#define rfcomm_lock()  mutex_lock(&rfcomm_mutex)
+#define rfcomm_unlock()        mutex_unlock(&rfcomm_mutex)
 
 static unsigned long rfcomm_event;
 
@@ -530,7 +534,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. */
@@ -623,7 +627,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
        /* Set L2CAP options */
        sk = sock->sk;
        lock_sock(sk);
-       l2cap_pi(sk)->imtu = RFCOMM_MAX_L2CAP_MTU;
+       l2cap_pi(sk)->imtu = l2cap_mtu;
        release_sock(sk);
 
        s = rfcomm_session_add(sock, BT_BOUND);
@@ -1146,6 +1150,8 @@ static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
 
 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);
@@ -1155,6 +1161,9 @@ 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);
 }
 
@@ -1219,14 +1228,18 @@ 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);
@@ -1868,7 +1881,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
        /* Set L2CAP options */
        sk = sock->sk;
        lock_sock(sk);
-       l2cap_pi(sk)->imtu = RFCOMM_MAX_L2CAP_MTU;
+       l2cap_pi(sk)->imtu = l2cap_mtu;
        release_sock(sk);
 
        /* Start listening on the socket */
@@ -2032,7 +2045,7 @@ static int __init rfcomm_init(void)
 
        kernel_thread(rfcomm_run, NULL, CLONE_KERNEL);
 
-       class_create_file(&bt_class, &class_attr_rfcomm_dlc);
+       class_create_file(bt_class, &class_attr_rfcomm_dlc);
 
        rfcomm_init_sockets();
 
@@ -2047,7 +2060,7 @@ static int __init rfcomm_init(void)
 
 static void __exit rfcomm_exit(void)
 {
-       class_remove_file(&bt_class, &class_attr_rfcomm_dlc);
+       class_remove_file(bt_class, &class_attr_rfcomm_dlc);
 
        hci_unregister_cb(&rfcomm_cb);
 
@@ -2070,6 +2083,12 @@ 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(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);