Skip to content

Commit

Permalink
[Bluetooth] Move pending packets from RFCOMM socket to TTY
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Marcel Holtmann committed Jul 14, 2008
1 parent 8b6b3da commit a0c22f2
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 2 deletions.
2 changes: 1 addition & 1 deletion net/bluetooth/rfcomm/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
55 changes: 54 additions & 1 deletion net/bluetooth/rfcomm/tty.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}

Expand Down

0 comments on commit a0c22f2

Please sign in to comment.