[Bluetooth] Handle return values from driver core functions
[pandora-kernel.git] / net / bluetooth / rfcomm / core.c
index e99010c..ddc4e9d 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>
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "1.7"
+#define VERSION "1.8"
 
+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;
@@ -273,10 +274,10 @@ 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;
@@ -289,6 +290,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio)
        rfcomm_dlc_clear_state(d);
        
        BT_DBG("%p", d);
+
        return d;
 }
 
@@ -522,10 +524,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);
 
@@ -534,7 +536,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. */
@@ -642,7 +644,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *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)
+       if (*err == 0 || *err == -EINPROGRESS)
                return s;
 
        rfcomm_session_del(s);
@@ -811,7 +813,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++;
 
@@ -1150,6 +1155,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);
@@ -1159,6 +1166,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);
 }
 
@@ -1223,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;
 }
@@ -1760,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);
@@ -2036,7 +2058,8 @@ static int __init rfcomm_init(void)
 
        kernel_thread(rfcomm_run, NULL, CLONE_KERNEL);
 
-       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();
 
@@ -2051,7 +2074,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);
 
@@ -2074,6 +2097,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(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");