[Bluetooth] Move pending packets from RFCOMM socket to TTY
authorMarcel Holtmann <marcel@holtmann.org>
Mon, 14 Jul 2008 18:13:52 +0000 (20:13 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Mon, 14 Jul 2008 18:13:52 +0000 (20:13 +0200)
When an incoming RFCOMM socket connection gets converted into a TTY,
it can happen that packets are lost. This mainly happens with the
Handsfree profile where the remote side starts sending data right
away. The problem is that these packets are in the socket receive
queue. So when creating the TTY make sure to copy all pending packets
from the socket receive queue to a private queue inside the TTY.

To make this actually work, the flow control on the newly created TTY
will be disabled and only enabled again when the TTY is opened by an
application. And right before that, the pending packets will be put
into the TTY flip buffer.

Signed-off-by: Denis Kenzior <denis.kenzior@trolltech.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/tty.c

index fcd2caf..b6b3d9b 100644 (file)
@@ -53,7 +53,7 @@
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "1.9"
+#define VERSION "1.10"
 
 static int disable_cfc = 0;
 static int channel_mtu = -1;
index 8fcca08..ec22ebe 100644 (file)
@@ -77,6 +77,8 @@ struct rfcomm_dev {
        struct device           *tty_dev;
 
        atomic_t                wmem_alloc;
+
+       struct sk_buff_head     pending;
 };
 
 static LIST_HEAD(rfcomm_dev_list);
@@ -264,7 +266,25 @@ 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;
@@ -542,11 +562,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);
@@ -630,6 +655,30 @@ static void rfcomm_tty_wakeup(unsigned long arg)
 #endif
 }
 
+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);
@@ -694,6 +743,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;
 }