From aa1f4fe003d1c8fe2c1eed5a43bcee4b87303d83 Mon Sep 17 00:00:00 2001 From: Joel King Date: Wed, 11 Jul 2012 16:18:54 -0700 Subject: [PATCH] msm: tty: update receive room just before writing data to the ldisc There is a corner case in the tty driver where the value of tty->receive_room is not being updated before writing data to the line discipline. When this happens, data is lost because of failure to re-submit any remaining data to the ldisc. This fix is dependent on a new tty flag that is set only when the tty driver is used for efs sync betweem the mdm modem and the applications processor. CRs-Fixed: 358868 Change-Id: I0ba02980504b4d8187b8c83111c2c883d194efa2 Signed-off-by: Joel King --- drivers/tty/tty_buffer.c | 29 +++++++++++++++++++++++++++-- drivers/usb/serial/usb_wwan.c | 1 + include/linux/tty.h | 2 ++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 6c9b7cd6778..ccbaa6d00dc 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -418,6 +418,8 @@ static void flush_to_ldisc(struct work_struct *work) int count; char *char_buf; unsigned char *flag_buf; + unsigned int left = 0; + unsigned int max_space; count = head->commit - head->read; if (!count) { @@ -432,10 +434,33 @@ static void flush_to_ldisc(struct work_struct *work) line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; + + /* update receive room */ + spin_lock(&tty->read_lock); + if (tty->update_room_in_ldisc) { + if ((tty->read_cnt == N_TTY_BUF_SIZE - 1) && + (tty->receive_room == + N_TTY_BUF_SIZE - 1)) + tty->rr_bug++; + left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + } + spin_unlock(&tty->read_lock); + if (!tty->receive_room) break; - if (count > tty->receive_room) - count = tty->receive_room; + + if (tty->update_room_in_ldisc && !left) { + schedule_work(&tty->buf.work); + break; + } + + if (tty->update_room_in_ldisc) + max_space = min(left, tty->receive_room); + else + max_space = tty->receive_room; + + if (count > max_space) + count = max_space; char_buf = head->char_buf_ptr + head->read; flag_buf = head->flag_buf_ptr + head->read; head->read += count; diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 366df674645..c3f741bb830 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -495,6 +495,7 @@ int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port) /* explicitly set the driver mode to raw */ tty->raw = 1; tty->real_raw = 1; + tty->update_room_in_ldisc = 1; set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); dbg("%s", __func__); diff --git a/include/linux/tty.h b/include/linux/tty.h index 6a0259d94c9..6fcafa8fe38 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -282,8 +282,10 @@ struct tty_struct { struct winsize winsize; /* termios mutex */ unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; unsigned char low_latency:1, warned:1; + unsigned char update_room_in_ldisc:1; unsigned char ctrl_status; /* ctrl_lock */ unsigned int receive_room; /* Bytes free for queue */ + unsigned int rr_bug; struct tty_struct *link; struct fasync_struct *fasync;