[Bluetooth] Move pending packets from RFCOMM socket to TTY

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>
This commit is contained in:
Marcel Holtmann 2008-07-14 20:13:52 +02:00
parent 8b6b3da765
commit a0c22f2265
2 changed files with 55 additions and 2 deletions

View File

@ -53,7 +53,7 @@
#define BT_DBG(D...) #define BT_DBG(D...)
#endif #endif
#define VERSION "1.9" #define VERSION "1.10"
static int disable_cfc = 0; static int disable_cfc = 0;
static int channel_mtu = -1; static int channel_mtu = -1;

View File

@ -77,6 +77,8 @@ struct rfcomm_dev {
struct device *tty_dev; struct device *tty_dev;
atomic_t wmem_alloc; atomic_t wmem_alloc;
struct sk_buff_head pending;
}; };
static LIST_HEAD(rfcomm_dev_list); 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); init_waitqueue_head(&dev->wait);
tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
skb_queue_head_init(&dev->pending);
rfcomm_dlc_lock(dlc); 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->data_ready = rfcomm_dev_data_ready;
dlc->state_change = rfcomm_dev_state_change; dlc->state_change = rfcomm_dev_state_change;
dlc->modem_status = rfcomm_dev_modem_status; 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 rfcomm_dev *dev = dlc->owner;
struct tty_struct *tty; struct tty_struct *tty;
if (!dev || !(tty = dev->tty)) { if (!dev) {
kfree_skb(skb); kfree_skb(skb);
return; 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); BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len);
tty_insert_flip_string(tty, skb->data, skb->len); tty_insert_flip_string(tty, skb->data, skb->len);
@ -630,6 +655,30 @@ static void rfcomm_tty_wakeup(unsigned long arg)
#endif #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) static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
{ {
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
@ -694,6 +743,10 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
if (err == 0) if (err == 0)
device_move(dev->tty_dev, rfcomm_get_device(dev)); device_move(dev->tty_dev, rfcomm_get_device(dev));
rfcomm_tty_copy_pending(dev);
rfcomm_dlc_unthrottle(dev->dlc);
return err; return err;
} }