Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[pandora-kernel.git] / net / bluetooth / rfcomm / tty.c
index 0a387f2..d3340dd 100644 (file)
@@ -23,8 +23,6 @@
 
 /*
  * RFCOMM TTY.
- *
- * $Id: tty.c,v 1.24 2002/10/03 01:54:38 holtmann Exp $
  */
 
 #include <linux/module.h>
@@ -77,6 +75,8 @@ struct rfcomm_dev {
        struct device           *tty_dev;
 
        atomic_t                wmem_alloc;
+
+       struct sk_buff_head     pending;
 };
 
 static LIST_HEAD(rfcomm_dev_list);
@@ -264,13 +264,34 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
        init_waitqueue_head(&dev->wait);
        tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
 
+       skb_queue_head_init(&dev->pending);
+
        rfcomm_dlc_lock(dlc);
+
+       if (req->flags & (1 << RFCOMM_REUSE_DLC)) {
+               struct sock *sk = dlc->owner;
+               struct sk_buff *skb;
+
+               BUG_ON(!sk);
+
+               rfcomm_dlc_throttle(dlc);
+
+               while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
+                       skb_orphan(skb);
+                       skb_queue_tail(&dev->pending, skb);
+                       atomic_sub(skb->len, &sk->sk_rmem_alloc);
+               }
+       }
+
        dlc->data_ready   = rfcomm_dev_data_ready;
        dlc->state_change = rfcomm_dev_state_change;
        dlc->modem_status = rfcomm_dev_modem_status;
 
        dlc->owner = dev;
        dev->dlc   = dlc;
+
+       rfcomm_dev_modem_status(dlc, dlc->remote_v24_sig);
+
        rfcomm_dlc_unlock(dlc);
 
        /* It's safe to call __module_get() here because socket already
@@ -539,11 +560,16 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
        struct rfcomm_dev *dev = dlc->owner;
        struct tty_struct *tty;
 
-       if (!dev || !(tty = dev->tty)) {
+       if (!dev) {
                kfree_skb(skb);
                return;
        }
 
+       if (!(tty = dev->tty) || !skb_queue_empty(&dev->pending)) {
+               skb_queue_tail(&dev->pending, skb);
+               return;
+       }
+
        BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len);
 
        tty_insert_flip_string(tty, skb->data, skb->len);
@@ -620,6 +646,30 @@ static void rfcomm_tty_wakeup(unsigned long arg)
        tty_wakeup(tty);
 }
 
+static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev)
+{
+       struct tty_struct *tty = dev->tty;
+       struct sk_buff *skb;
+       int inserted = 0;
+
+       if (!tty)
+               return;
+
+       BT_DBG("dev %p tty %p", dev, tty);
+
+       rfcomm_dlc_lock(dev->dlc);
+
+       while ((skb = skb_dequeue(&dev->pending))) {
+               inserted += tty_insert_flip_string(tty, skb->data, skb->len);
+               kfree_skb(skb);
+       }
+
+       rfcomm_dlc_unlock(dev->dlc);
+
+       if (inserted > 0)
+               tty_flip_buffer_push(tty);
+}
+
 static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
 {
        DECLARE_WAITQUEUE(wait, current);
@@ -684,6 +734,10 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
        if (err == 0)
                device_move(dev->tty_dev, rfcomm_get_device(dev));
 
+       rfcomm_tty_copy_pending(dev);
+
+       rfcomm_dlc_unthrottle(dev->dlc);
+
        return err;
 }
 
@@ -1114,6 +1168,7 @@ int rfcomm_init_ttys(void)
        rfcomm_tty_driver->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
        rfcomm_tty_driver->init_termios = tty_std_termios;
        rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
        tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
 
        if (tty_register_driver(rfcomm_tty_driver)) {