Commit f34d7a5b authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

tty: The big operations rework

- Operations are now a shared const function block as with most other Linux
  objects

- Introduce wrappers for some optional functions to get consistent behaviour

- Wrap put_char which used to be patched by the tty layer

- Document which functions are needed/optional

- Make put_char report success/fail

- Cache the driver->ops pointer in the tty as tty->ops

- Remove various surplus lock calls we no longer need

- Remove proc_write method as noted by Alexey Dobriyan

- Introduce some missing sanity checks where certain driver/ldisc
  combinations would oops as they didn't check needed methods were present

[akpm@linux-foundation.org: fix fs/compat_ioctl.c build]
[akpm@linux-foundation.org: fix isicom]
[akpm@linux-foundation.org: fix arch/ia64/hp/sim/simserial.c build]
[akpm@linux-foundation.org: fix kgdb]
Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Acked-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
Cc: Jason Wessel <jason.wessel@windriver.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 251b8dd7
......@@ -210,21 +210,23 @@ static void do_softint(struct work_struct *private_)
printk(KERN_ERR "simserial: do_softint called\n");
}
static void rs_put_char(struct tty_struct *tty, unsigned char ch)
static int rs_put_char(struct tty_struct *tty, unsigned char ch)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
if (!tty || !info->xmit.buf) return;
if (!tty || !info->xmit.buf)
return 0;
local_irq_save(flags);
if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) {
local_irq_restore(flags);
return;
return 0;
}
info->xmit.buf[info->xmit.head] = ch;
info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
local_irq_restore(flags);
return 1;
}
static void transmit_chars(struct async_struct *info, int *intr_done)
......@@ -621,7 +623,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
* the line discipline to only process XON/XOFF characters.
*/
shutdown(info);
if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty);
if (tty->ops->flush_buffer)
tty->ops->flush_buffer(tty);
if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty);
info->event = 0;
info->tty = NULL;
......
......@@ -143,7 +143,7 @@ restart:
int len;
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
len = tty->driver->write(tty, skb->data, skb->len);
len = tty->ops->write(tty, skb->data, skb->len);
hdev->stat.byte_tx += len;
skb_pull(skb, len);
......@@ -190,8 +190,7 @@ static int hci_uart_flush(struct hci_dev *hdev)
/* Flush any pending characters in the driver and discipline. */
tty_ldisc_flush(tty);
if (tty->driver && tty->driver->flush_buffer)
tty->driver->flush_buffer(tty);
tty_driver_flush_buffer(tty);
if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
hu->proto->flush(hu);
......@@ -285,9 +284,7 @@ static int hci_uart_tty_open(struct tty_struct *tty)
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
if (tty->driver && tty->driver->flush_buffer)
tty->driver->flush_buffer(tty);
tty_driver_flush_buffer(tty);
return 0;
}
......@@ -374,8 +371,8 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *f
spin_unlock(&hu->rx_lock);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
tty->driver->unthrottle)
tty->driver->unthrottle(tty);
tty->ops->unthrottle)
tty->ops->unthrottle(tty);
}
static int hci_uart_register_dev(struct hci_uart *hu)
......
......@@ -169,7 +169,7 @@ static int Fip_firmware_size;
static int ip2_open(PTTY, struct file *);
static void ip2_close(PTTY, struct file *);
static int ip2_write(PTTY, const unsigned char *, int);
static void ip2_putchar(PTTY, unsigned char);
static int ip2_putchar(PTTY, unsigned char);
static void ip2_flush_chars(PTTY);
static int ip2_write_room(PTTY);
static int ip2_chars_in_buf(PTTY);
......@@ -1616,10 +1616,9 @@ ip2_close( PTTY tty, struct file *pFile )
serviceOutgoingFifo ( pCh->pMyBord );
if ( tty->driver->flush_buffer )
tty->driver->flush_buffer(tty);
if ( tty->ldisc.flush_buffer )
tty->ldisc.flush_buffer(tty);
if ( tty->driver->ops->flush_buffer )
tty->driver->ops->flush_buffer(tty);
tty_ldisc_flush(tty);
tty->closing = 0;
pCh->pTTY = NULL;
......@@ -1738,7 +1737,7 @@ ip2_write( PTTY tty, const unsigned char *pData, int count)
/* */
/* */
/******************************************************************************/
static void
static int
ip2_putchar( PTTY tty, unsigned char ch )
{
i2ChanStrPtr pCh = tty->driver_data;
......@@ -1753,6 +1752,7 @@ ip2_putchar( PTTY tty, unsigned char ch )
ip2_flush_chars( tty );
} else
write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
return 1;
// ip2trace (CHANN, ITRC_PUTC, ITRC_RETURN, 1, ch );
}
......
......@@ -1140,28 +1140,29 @@ static int isicom_write(struct tty_struct *tty, const unsigned char *buf,
}
/* put_char et all */
static void isicom_put_char(struct tty_struct *tty, unsigned char ch)
static int isicom_put_char(struct tty_struct *tty, unsigned char ch)
{
struct isi_port *port = tty->driver_data;
struct isi_board *card = port->card;
unsigned long flags;
if (isicom_paranoia_check(port, tty->name, "isicom_put_char"))
return;
return 0;
if (!port->xmit_buf)
return;
return 0;
spin_lock_irqsave(&card->card_lock, flags);
if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
goto out;
if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
spin_unlock_irqrestore(&card->card_lock, flags);
return 0;
}
port->xmit_buf[port->xmit_head++] = ch;
port->xmit_head &= (SERIAL_XMIT_SIZE - 1);
port->xmit_cnt++;
spin_unlock_irqrestore(&card->card_lock, flags);
out:
return;
return 1;
}
/* flush_chars et all */
......
......@@ -1230,7 +1230,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
if (rep &&
(!vc_kbd_mode(kbd, VC_REPEAT) ||
(tty && !L_ECHO(tty) && tty->driver->chars_in_buffer(tty)))) {
(tty && !L_ECHO(tty) && tty_chars_in_buffer(tty)))) {
/*
* Don't repeat a key if the input buffers are not empty and the
* characters get aren't echoed locally. This makes key repeat
......
......@@ -342,12 +342,10 @@ static int n_hdlc_tty_open (struct tty_struct *tty)
#endif
/* Flush any pending characters in the driver and discipline. */
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer (tty);
tty->ldisc.flush_buffer(tty);
if (tty->driver->flush_buffer)
tty->driver->flush_buffer (tty);
tty_driver_flush_buffer(tty);
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)n_hdlc_tty_open() success\n",__FILE__,__LINE__);
......@@ -399,7 +397,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
/* Send the next block of data to device */
tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
actual = tty->driver->write(tty, tbuf->buf, tbuf->count);
actual = tty->ops->write(tty, tbuf->buf, tbuf->count);
/* rollback was possible and has been done */
if (actual == -ERESTARTSYS) {
......@@ -752,8 +750,7 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
case TIOCOUTQ:
/* get the pending tx byte count in the driver */
count = tty->driver->chars_in_buffer ?
tty->driver->chars_in_buffer(tty) : 0;
count = tty_chars_in_buffer(tty);
/* add size of next output frame in queue */
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags);
if (n_hdlc->tx_buf_list.head)
......
......@@ -376,8 +376,9 @@ static void put_char(struct r3964_info *pInfo, unsigned char ch)
if (tty == NULL)
return;
if (tty->driver->put_char) {
tty->driver->put_char(tty, ch);
/* FIXME: put_char should not be called from an IRQ */
if (tty->ops->put_char) {
tty->ops->put_char(tty, ch);
}
pInfo->bcc ^= ch;
}
......@@ -386,12 +387,9 @@ static void flush(struct r3964_info *pInfo)
{
struct tty_struct *tty = pInfo->tty;
if (tty == NULL)
if (tty == NULL || tty->ops->flush_chars == NULL)
return;
if (tty->driver->flush_chars) {
tty->driver->flush_chars(tty);
}
tty->ops->flush_chars(tty);
}
static void trigger_transmit(struct r3964_info *pInfo)
......@@ -449,12 +447,11 @@ static void transmit_block(struct r3964_info *pInfo)
struct r3964_block_header *pBlock = pInfo->tx_first;
int room = 0;
if ((tty == NULL) || (pBlock == NULL)) {
if (tty == NULL || pBlock == NULL) {
return;
}
if (tty->driver->write_room)
room = tty->driver->write_room(tty);
room = tty_write_room(tty);
TRACE_PS("transmit_block %p, room %d, length %d",
pBlock, room, pBlock->length);
......
......@@ -149,8 +149,8 @@ static void check_unthrottle(struct tty_struct *tty)
{
if (tty->count &&
test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
tty->driver->unthrottle)
tty->driver->unthrottle(tty);
tty->ops->unthrottle)
tty->ops->unthrottle(tty);
}
/**
......@@ -273,7 +273,7 @@ static int opost(unsigned char c, struct tty_struct *tty)
{
int space, spaces;
space = tty->driver->write_room(tty);
space = tty_write_room(tty);
if (!space)
return -1;
......@@ -286,7 +286,7 @@ static int opost(unsigned char c, struct tty_struct *tty)
if (O_ONLCR(tty)) {
if (space < 2)
return -1;
tty->driver->put_char(tty, '\r');
tty_put_char(tty, '\r');
tty->column = 0;
}
tty->canon_column = tty->column;
......@@ -308,7 +308,7 @@ static int opost(unsigned char c, struct tty_struct *tty)
if (space < spaces)
return -1;
tty->column += spaces;
tty->driver->write(tty, " ", spaces);
tty->ops->write(tty, " ", spaces);
return 0;
}
tty->column += spaces;
......@@ -325,7 +325,7 @@ static int opost(unsigned char c, struct tty_struct *tty)
break;
}
}
tty->driver->put_char(tty, c);
tty_put_char(tty, c);
unlock_kernel();
return 0;
}
......@@ -352,7 +352,7 @@ static ssize_t opost_block(struct tty_struct *tty,
int i;
const unsigned char *cp;
space = tty->driver->write_room(tty);
space = tty_write_room(tty);
if (!space)
return 0;
if (nr > space)
......@@ -390,27 +390,14 @@ static ssize_t opost_block(struct tty_struct *tty,
}
}
break_out:
if (tty->driver->flush_chars)
tty->driver->flush_chars(tty);
i = tty->driver->write(tty, buf, i);
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
i = tty->ops->write(tty, buf, i);
unlock_kernel();
return i;
}
/**
* put_char - write character to driver
* @c: character (or part of unicode symbol)
* @tty: terminal device
*
* Queue a byte to the driver layer for output
*/
static inline void put_char(unsigned char c, struct tty_struct *tty)
{
tty->driver->put_char(tty, c);
}
/**
* echo_char - echo characters
* @c: unicode byte to echo
......@@ -423,8 +410,8 @@ static inline void put_char(unsigned char c, struct tty_struct *tty)
static void echo_char(unsigned char c, struct tty_struct *tty)
{
if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {
put_char('^', tty);
put_char(c ^ 0100, tty);
tty_put_char(tty, '^');
tty_put_char(tty, c ^ 0100);
tty->column += 2;
} else
opost(c, tty);
......@@ -433,7 +420,7 @@ static void echo_char(unsigned char c, struct tty_struct *tty)
static inline void finish_erasing(struct tty_struct *tty)
{
if (tty->erasing) {
put_char('/', tty);
tty_put_char(tty, '/');
tty->column++;
tty->erasing = 0;
}
......@@ -517,7 +504,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
if (L_ECHO(tty)) {
if (L_ECHOPRT(tty)) {
if (!tty->erasing) {
put_char('\\', tty);
tty_put_char(tty, '\\');
tty->column++;
tty->erasing = 1;
}
......@@ -525,7 +512,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
echo_char(c, tty);
while (--cnt > 0) {
head = (head+1) & (N_TTY_BUF_SIZE-1);
put_char(tty->read_buf[head], tty);
tty_put_char(tty, tty->read_buf[head]);
}
} else if (kill_type == ERASE && !L_ECHOE(tty)) {
echo_char(ERASE_CHAR(tty), tty);
......@@ -553,22 +540,22 @@ static void eraser(unsigned char c, struct tty_struct *tty)
/* Now backup to that column. */
while (tty->column > col) {
/* Can't use opost here. */
put_char('\b', tty);
tty_put_char(tty, '\b');
if (tty->column > 0)
tty->column--;
}
} else {
if (iscntrl(c) && L_ECHOCTL(tty)) {
put_char('\b', tty);
put_char(' ', tty);
put_char('\b', tty);
tty_put_char(tty, '\b');
tty_put_char(tty, ' ');
tty_put_char(tty, '\b');
if (tty->column > 0)
tty->column--;
}
if (!iscntrl(c) || L_ECHOCTL(tty)) {
put_char('\b', tty);
put_char(' ', tty);
put_char('\b', tty);
tty_put_char(tty, '\b');
tty_put_char(tty, ' ');
tty_put_char(tty, '\b');
if (tty->column > 0)
tty->column--;
}
......@@ -599,8 +586,7 @@ static inline void isig(int sig, struct tty_struct *tty, int flush)
kill_pgrp(tty->pgrp, sig, 1);
if (flush || !L_NOFLSH(tty)) {
n_tty_flush_buffer(tty);
if (tty->driver->flush_buffer)
tty->driver->flush_buffer(tty);
tty_driver_flush_buffer(tty);
}
}
......@@ -732,7 +718,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
tty->lnext = 0;
if (L_ECHO(tty)) {
if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
put_char('\a', tty); /* beep if no space */
tty_put_char(tty, '\a'); /* beep if no space */
return;
}
/* Record the column of first canon char. */
......@@ -776,8 +762,7 @@ send_signal:
*/
if (!L_NOFLSH(tty)) {
n_tty_flush_buffer(tty);
if (tty->driver->flush_buffer)
tty->driver->flush_buffer(tty);
tty_driver_flush_buffer(tty);
}
if (L_ECHO(tty))
echo_char(c, tty);
......@@ -806,8 +791,8 @@ send_signal:
if (L_ECHO(tty)) {
finish_erasing(tty);
if (L_ECHOCTL(tty)) {
put_char('^', tty);
put_char('\b', tty);
tty_put_char(tty, '^');
tty_put_char(tty, '\b');
}
}
return;
......@@ -828,7 +813,7 @@ send_signal:
if (c == '\n') {
if (L_ECHO(tty) || L_ECHONL(tty)) {
if (tty->read_cnt >= N_TTY_BUF_SIZE-1)
put_char('\a', tty);
tty_put_char(tty, '\a');
opost('\n', tty);
}
goto handle_newline;
......@@ -846,7 +831,7 @@ send_signal:
*/
if (L_ECHO(tty)) {
if (tty->read_cnt >= N_TTY_BUF_SIZE-1)
put_char('\a', tty);
tty_put_char(tty, '\a');
/* Record the column of first canon char. */
if (tty->canon_head == tty->read_head)
tty->canon_column = tty->column;
......@@ -876,7 +861,7 @@ handle_newline:
finish_erasing(tty);
if (L_ECHO(tty)) {
if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
put_char('\a', tty); /* beep if no space */
tty_put_char(tty, '\a'); /* beep if no space */
return;
}
if (c == '\n')
......@@ -980,8 +965,8 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
break;
}
}
if (tty->driver->flush_chars)
tty->driver->flush_chars(tty);
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
n_tty_set_room(tty);
......@@ -1000,8 +985,8 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
if (tty->receive_room < TTY_THRESHOLD_THROTTLE) {
/* check TTY_THROTTLED first so it indicates our state */
if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&
tty->driver->throttle)
tty->driver->throttle(tty);
tty->ops->throttle)
tty->ops->throttle(tty);
}
}
......@@ -1086,6 +1071,9 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
tty->real_raw = 0;
}
n_tty_set_room(tty);
/* The termios change make the tty ready for I/O */
wake_up_interruptible(&tty->write_wait);
wake_up_interruptible(&tty->read_wait);
}
/**
......@@ -1513,11 +1501,11 @@ static ssize_t write_chan(struct tty_struct *tty, struct file *file,
break;
b++; nr--;
}
if (tty->driver->flush_chars)
tty->driver->flush_chars(tty);
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
} else {
while (nr > 0) {
c = tty->driver->write(tty, b, nr);
c = tty->ops->write(tty, b, nr);
if (c < 0) {
retval = c;
goto break_out;
......@@ -1554,11 +1542,6 @@ break_out:
*
* This code must be sure never to sleep through a hangup.
* Called without the kernel lock held - fine
*
* FIXME: if someone changes the VMIN or discipline settings for the
* terminal while another process is in poll() the poll does not
* recompute the new limits. Possibly set_termios should issue
* a read wakeup to fix this bug.
*/
static unsigned int normal_poll(struct tty_struct *tty, struct file *file,
......@@ -1582,9 +1565,9 @@ static unsigned int normal_poll(struct tty_struct *tty, struct file *file,
else
tty->minimum_to_wake = 1;
}
if (!tty_is_writelocked(tty) &&
tty->driver->chars_in_buffer(tty) < WAKEUP_CHARS &&
tty->driver->write_room(tty) > 0)
if (tty->ops->write && !tty_is_writelocked(tty) &&
tty_chars_in_buffer(tty) < WAKEUP_CHARS &&
tty_write_room(tty) > 0)
mask |= POLLOUT | POLLWRNORM;
return mask;
}
......
......@@ -1108,8 +1108,8 @@ restart:
a reference to the old ldisc. If we ended up flipping back
to the existing ldisc we have two references to it */
if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc)
tty->driver->set_ldisc(tty);
if (tty->ldisc.num != o_ldisc.num && tty->ops->set_ldisc)
tty->ops->set_ldisc(tty);
tty_ldisc_put(o_ldisc.num);
......@@ -1181,9 +1181,8 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
if (*str == '\0')
str = NULL;
if (tty_line >= 0 && tty_line <= p->num && p->poll_init &&
!p->poll_init(p, tty_line, str)) {
if (tty_line >= 0 && tty_line <= p->num && p->ops &&
p->ops->poll_init && !p->ops->poll_init(p, tty_line, str)) {
res = p;
*line = tty_line;
break;
......@@ -1452,8 +1451,7 @@ static void do_tty_hangup(struct work_struct *work)
/* We may have no line discipline at this point */
if (ld->flush_buffer)
ld->flush_buffer(tty);
if (tty->driver->flush_buffer)
tty->driver->flush_buffer(tty);
tty_driver_flush_buffer(tty);
if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
ld->write_wakeup)
ld->write_wakeup(tty);
......@@ -1516,11 +1514,11 @@ static void do_tty_hangup(struct work_struct *work)
* So we just call close() the right number of times.
*/
if (cons_filp) {
if (tty->driver->close)
if (tty->ops->close)
for (n = 0; n < closecount; n++)
tty->driver->close(tty, cons_filp);
} else if (tty->driver->hangup)
(tty->driver->hangup)(tty);
tty->ops->close(tty, cons_filp);
} else if (tty->ops->hangup)
(tty->ops->hangup)(tty);
/*
* We don't want to have driver/ldisc interactions beyond
* the ones we did here. The driver layer expects no
......@@ -1752,8 +1750,8 @@ void stop_tty(struct tty_struct *tty)
wake_up_interruptible(&tty->link->read_wait);
}
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
if (tty->driver->stop)
(tty->driver->stop)(tty);
if (tty->ops->stop)
(tty->ops->stop)(tty);
}
EXPORT_SYMBOL(stop_tty);
......@@ -1786,8 +1784,8 @@ void start_tty(struct tty_struct *tty)
wake_up_interruptible(&tty->link->read_wait);
}
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
if (tty->driver->start)
(tty->driver->start)(tty);
if (tty->ops->start)
(tty->ops->start)(tty);
/* If we have a running line discipline it may need kicking */
tty_wakeup(tty);
}
......@@ -1972,10 +1970,13 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode, "tty_write"))
return -EIO;
if (!tty || !tty->driver->write ||
if (!tty || !tty->ops->write ||
(test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
tty->driver->name);
ld = tty_ldisc_ref_wait(tty);
if (!ld->write)
ret = -EIO;
......@@ -2122,6 +2123,7 @@ static int init_dev(struct tty_driver *driver, int idx,
goto fail_no_mem;
initialize_tty_struct(tty);
tty->driver = driver;
tty->ops = driver->ops;
tty->index = idx;
tty_line_name(driver, idx, tty->name);
......@@ -2152,6 +2154,7 @@ static int init_dev(struct tty_driver *driver, int idx,
goto free_mem_out;
initialize_tty_struct(o_tty);
o_tty->driver = driver->other;
o_tty->ops = driver->ops;
o_tty->index = idx;
tty_line_name(driver->other, idx, o_tty->name);
......@@ -2456,8 +2459,8 @@ static void release_dev(struct file *filp)
}
}
#endif
if (tty->driver->close)
tty->driver->close(tty, filp);
if (tty->ops->close)
tty->ops->close(tty, filp);
/*
* Sanity check: if tty->count is going to zero, there shouldn't be
......@@ -2740,8 +2743,8 @@ got_driver: