Bluetooth: Add RFCOMM option to use L2CAP ERTM mode
[pandora-kernel.git] / net / bluetooth / rfcomm / core.c
index 94b3388..fc5ee32 100644 (file)
@@ -51,6 +51,7 @@
 static int disable_cfc = 0;
 static int channel_mtu = -1;
 static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU;
+static int l2cap_ertm = 0;
 
 static struct task_struct *rfcomm_thread;
 
@@ -244,6 +245,33 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d)
                                                                auth_type);
 }
 
+static void rfcomm_session_timeout(unsigned long arg)
+{
+       struct rfcomm_session *s = (void *) arg;
+
+       BT_DBG("session %p state %ld", s, s->state);
+
+       set_bit(RFCOMM_TIMED_OUT, &s->flags);
+       rfcomm_session_put(s);
+       rfcomm_schedule(RFCOMM_SCHED_TIMEO);
+}
+
+static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout)
+{
+       BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout);
+
+       if (!mod_timer(&s->timer, jiffies + timeout))
+               rfcomm_session_hold(s);
+}
+
+static void rfcomm_session_clear_timer(struct rfcomm_session *s)
+{
+       BT_DBG("session %p state %ld", s, s->state);
+
+       if (timer_pending(&s->timer) && del_timer(&s->timer))
+               rfcomm_session_put(s);
+}
+
 /* ---- RFCOMM DLCs ---- */
 static void rfcomm_dlc_timeout(unsigned long arg)
 {
@@ -320,6 +348,7 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
 
        rfcomm_session_hold(s);
 
+       rfcomm_session_clear_timer(s);
        rfcomm_dlc_hold(d);
        list_add(&d->list, &s->dlcs);
        d->session = s;
@@ -335,6 +364,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
        d->session = NULL;
        rfcomm_dlc_put(d);
 
+       if (list_empty(&s->dlcs))
+               rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT);
+
        rfcomm_session_put(s);
 }
 
@@ -567,6 +599,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
 
        BT_DBG("session %p sock %p", s, sock);
 
+       setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s);
+
        INIT_LIST_HEAD(&s->dlcs);
        s->state = state;
        s->sock  = sock;
@@ -598,6 +632,7 @@ static void rfcomm_session_del(struct rfcomm_session *s)
        if (state == BT_CONNECTED)
                rfcomm_send_disc(s, 0);
 
+       rfcomm_session_clear_timer(s);
        sock_release(s->sock);
        kfree(s);
 
@@ -639,6 +674,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err)
                __rfcomm_dlc_close(d, err);
        }
 
+       rfcomm_session_clear_timer(s);
        rfcomm_session_put(s);
 }
 
@@ -667,6 +703,8 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
        sk = sock->sk;
        lock_sock(sk);
        l2cap_pi(sk)->imtu = l2cap_mtu;
+       if (l2cap_ertm)
+               l2cap_pi(sk)->mode = L2CAP_MODE_ERTM;
        release_sock(sk);
 
        s = rfcomm_session_add(sock, BT_BOUND);
@@ -1879,6 +1917,12 @@ static inline void rfcomm_process_sessions(void)
                struct rfcomm_session *s;
                s = list_entry(p, struct rfcomm_session, list);
 
+               if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) {
+                       s->state = BT_DISCONN;
+                       rfcomm_send_disc(s, 0);
+                       continue;
+               }
+
                if (s->state == BT_LISTEN) {
                        rfcomm_accept_connection(s);
                        continue;
@@ -2080,7 +2124,7 @@ static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL);
 /* ---- Initialization ---- */
 static int __init rfcomm_init(void)
 {
-       int ret;
+       int err;
 
        l2cap_load();
 
@@ -2088,33 +2132,35 @@ static int __init rfcomm_init(void)
 
        rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");
        if (IS_ERR(rfcomm_thread)) {
-               ret = PTR_ERR(rfcomm_thread);
-               goto out_thread;
+               err = PTR_ERR(rfcomm_thread);
+               goto unregister;
        }
 
        if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0)
                BT_ERR("Failed to create RFCOMM info file");
 
-       ret = rfcomm_init_ttys();
-       if (ret)
-               goto out_tty;
+       err = rfcomm_init_ttys();
+       if (err < 0)
+               goto stop;
 
-       ret = rfcomm_init_sockets();
-       if (ret)
-               goto out_sock;
+       err = rfcomm_init_sockets();
+       if (err < 0)
+               goto cleanup;
 
        BT_INFO("RFCOMM ver %s", VERSION);
 
        return 0;
 
-out_sock:
+cleanup:
        rfcomm_cleanup_ttys();
-out_tty:
+
+stop:
        kthread_stop(rfcomm_thread);
-out_thread:
+
+unregister:
        hci_unregister_cb(&rfcomm_cb);
 
-       return ret;
+       return err;
 }
 
 static void __exit rfcomm_exit(void)
@@ -2142,6 +2188,9 @@ 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_param(l2cap_ertm, bool, 0644);
+MODULE_PARM_DESC(l2cap_ertm, "Use L2CAP ERTM mode for connection");
+
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION);
 MODULE_VERSION(VERSION);