From f34d7a5b7010b82fe97da95496b9971435530062 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 30 Apr 2008 00:54:13 -0700 Subject: 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: Alan Cox Acked-by: Greg Kroah-Hartman Cc: Jason Wessel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ip2/ip2main.c | 12 ++-- drivers/char/isicom.c | 15 ++-- drivers/char/keyboard.c | 2 +- drivers/char/n_hdlc.c | 11 ++- drivers/char/n_r3964.c | 17 ++--- drivers/char/n_tty.c | 103 +++++++++++---------------- drivers/char/tty_io.c | 174 +++++++++++++++++++-------------------------- drivers/char/tty_ioctl.c | 62 ++++++++++------ 8 files changed, 183 insertions(+), 213 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index c4f4ca31f7c..5ef69dcd258 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -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 ); } diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 57b115272aa..9c6be8da220 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -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 */ diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 60b934adea6..d1c50b3302e 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -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 diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index a07c0af4819..a35bfd7ee80 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -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) diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index 3f6486e9f1e..90216906233 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -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); diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index e1518e17e09..abc93a93dcd 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -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; } diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index b1692afd797..f69fb8d7a68 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -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: printk(KERN_DEBUG "opening %s...", tty->name); #endif if (!retval) { - if (tty->driver->open) - retval = tty->driver->open(tty, filp); + if (tty->ops->open) + retval = tty->ops->open(tty, filp); else retval = -ENODEV; } @@ -2840,7 +2843,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) goto out1; check_tty_count(tty, "tty_open"); - retval = ptm_driver->open(tty, filp); + retval = ptm_driver->ops->open(tty, filp); if (!retval) return 0; out1: @@ -3336,25 +3339,20 @@ static int tiocsetd(struct tty_struct *tty, int __user *p) static int send_break(struct tty_struct *tty, unsigned int duration) { - int retval = -EINTR; - - lock_kernel(); if (tty_write_lock(tty, 0) < 0) - goto out; - tty->driver->break_ctl(tty, -1); + return -EINTR; + tty->ops->break_ctl(tty, -1); if (!signal_pending(current)) msleep_interruptible(duration); - tty->driver->break_ctl(tty, 0); + tty->ops->break_ctl(tty, 0); tty_write_unlock(tty); if (!signal_pending(current)) - retval = 0; -out: - unlock_kernel(); - return retval; + return -EINTR; + return 0; } /** - * tiocmget - get modem status + * tty_tiocmget - get modem status * @tty: tty device * @file: user file pointer * @p: pointer to result @@ -3369,10 +3367,8 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p { int retval = -EINVAL; - if (tty->driver->tiocmget) { - lock_kernel(); - retval = tty->driver->tiocmget(tty, file); - unlock_kernel(); + if (tty->ops->tiocmget) { + retval = tty->ops->tiocmget(tty, file); if (retval >= 0) retval = put_user(retval, p); @@ -3381,7 +3377,7 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p } /** - * tiocmset - set modem status + * tty_tiocmset - set modem status * @tty: tty device * @file: user file pointer * @cmd: command - clear bits, set bits or set all @@ -3398,7 +3394,7 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int { int retval = -EINVAL; - if (tty->driver->tiocmset) { + if (tty->ops->tiocmset) { unsigned int set, clear, val; retval = get_user(val, p); @@ -3422,9 +3418,7 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; - lock_kernel(); - retval = tty->driver->tiocmset(tty, file, set, clear); - unlock_kernel(); + retval = tty->ops->tiocmset(tty, file, set, clear); } return retval; } @@ -3455,23 +3449,25 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) retval = -EINVAL; - if (!tty->driver->break_ctl) { + if (!tty->ops->break_ctl) { switch (cmd) { case TIOCSBRK: case TIOCCBRK: - if (tty->driver->ioctl) - retval = tty->driver->ioctl(tty, file, cmd, arg); + if (tty->ops->ioctl) + retval = tty->ops->ioctl(tty, file, cmd, arg); + if (retval != -EINVAL && retval != -ENOIOCTLCMD) + printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name); return retval; /* These two ioctl's always return success; even if */ /* the driver doesn't support them. */ case TCSBRK: case TCSBRKP: - if (!tty->driver->ioctl) + if (!tty->ops->ioctl) return 0; - lock_kernel(); - retval = tty->driver->ioctl(tty, file, cmd, arg); - unlock_kernel(); + retval = tty->ops->ioctl(tty, file, cmd, arg); + if (retval != -EINVAL && retval != -ENOIOCTLCMD) + printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name); if (retval == -ENOIOCTLCMD) retval = 0; return retval; @@ -3491,9 +3487,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (retval) return retval; if (cmd != TIOCCBRK) { - lock_kernel(); tty_wait_until_sent(tty, 0); - unlock_kernel(); if (signal_pending(current)) return -EINTR; } @@ -3531,7 +3525,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCGSID: return tiocgsid(tty, real_tty, p); case TIOCGETD: - /* FIXME: check this is ok */ return put_user(tty->ldisc.num, (int __user *)p); case TIOCSETD: return tiocsetd(tty, p); @@ -3543,15 +3536,13 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * Break handling */ case TIOCSBRK: /* Turn break on, unconditionally */ - lock_kernel(); - tty->driver->break_ctl(tty, -1); - unlock_kernel(); + if (tty->ops->break_ctl) + tty->ops->break_ctl(tty, -1); return 0; case TIOCCBRK: /* Turn break off, unconditionally */ - lock_kernel(); - tty->driver->break_ctl(tty, 0); - unlock_kernel(); + if (tty->ops->break_ctl) + tty->ops->break_ctl(tty, 0); return 0; case TCSBRK: /* SVID version: non-zero arg --> no break */ /* non-zero arg means wait for all output data @@ -3580,8 +3571,8 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } break; } - if (tty->driver->ioctl) { - retval = (tty->driver->ioctl)(tty, file, cmd, arg); + if (tty->ops->ioctl) { + retval = (tty->ops->ioctl)(tty, file, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } @@ -3608,8 +3599,8 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, if (tty_paranoia_check(tty, inode, "tty_ioctl")) return -EINVAL; - if (tty->driver->compat_ioctl) { - retval = (tty->driver->compat_ioctl)(tty, file, cmd, arg); + if (tty->ops->compat_ioctl) { + retval = (tty->ops->compat_ioctl)(tty, file, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } @@ -3659,8 +3650,7 @@ void __do_SAK(struct tty_struct *tty) tty_ldisc_flush(tty); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); read_lock(&tasklist_lock); /* Kill the entire session */ @@ -3871,15 +3861,27 @@ static void initialize_tty_struct(struct tty_struct *tty) INIT_WORK(&tty->SAK_work, do_SAK_work); } -/* - * The default put_char routine if the driver did not define one. +/** + * tty_put_char - write one character to a tty + * @tty: tty + * @ch: character + * + * Write one byte to the tty using the provided put_char method + * if present. Returns the number of characters successfully output. + * + * Note: the specific put_char operation in the driver layer may go + * away soon. Don't call it directly, use this method */ -static void tty_default_put_char(struct tty_struct *tty, unsigned char ch) +int tty_put_char(struct tty_struct *tty, unsigned char ch) { - tty->driver->write(tty, &ch, 1); + if (tty->ops->put_char) + return tty->ops->put_char(tty, ch); + return tty->ops->write(tty, &ch, 1); } +EXPORT_SYMBOL_GPL(tty_put_char); + static struct class *tty_class; /** @@ -3962,37 +3964,8 @@ void put_tty_driver(struct tty_driver *driver) void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op) { - driver->open = op->open; - driver->close = op->close; - driver->write = op->write; - driver->put_char = op->put_char; - driver->flush_chars = op->flush_chars; - driver->write_room = op->write_room; - driver->chars_in_buffer = op->chars_in_buffer; - driver->ioctl = op->ioctl; - driver->compat_ioctl = op->compat_ioctl; - driver->set_termios = op->set_termios; - driver->throttle = op->throttle; - driver->unthrottle = op->unthrottle; - driver->stop = op->stop; - driver->start = op->start; - driver->hangup = op->hangup; - driver->break_ctl = op->break_ctl; - driver->flush_buffer = op->flush_buffer; - driver->set_ldisc = op->set_ldisc; - driver->wait_until_sent = op->wait_until_sent; - driver->send_xchar = op->send_xchar; - driver->read_proc = op->read_proc; - driver->write_proc = op->write_proc; - driver->tiocmget = op->tiocmget; - driver->tiocmset = op->tiocmset; -#ifdef CONFIG_CONSOLE_POLL - driver->poll_init = op->poll_init; - driver->poll_get_char = op->poll_get_char; - driver->poll_put_char = op->poll_put_char; -#endif -} - + driver->ops = op; +}; EXPORT_SYMBOL(alloc_tty_driver); EXPORT_SYMBOL(put_tty_driver); @@ -4055,9 +4028,6 @@ int tty_register_driver(struct tty_driver *driver) return error; } - if (!driver->put_char) - driver->put_char = tty_default_put_char; - mutex_lock(&tty_mutex); list_add(&driver->tty_drivers, &tty_drivers); mutex_unlock(&tty_mutex); diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 8c4bf3e48d5..c10d40c4c5c 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -40,6 +40,34 @@ #define TERMIOS_OLD 8 +int tty_chars_in_buffer(struct tty_struct *tty) +{ + if (tty->ops->chars_in_buffer) + return tty->ops->chars_in_buffer(tty); + else + return 0; +} + +EXPORT_SYMBOL(tty_chars_in_buffer); + +int tty_write_room(struct tty_struct *tty) +{ + if (tty->ops->write_room) + return tty->ops->write_room(tty); + return 2048; +} + +EXPORT_SYMBOL(tty_write_room); + +void tty_driver_flush_buffer(struct tty_struct *tty) +{ + if (tty->ops->flush_buffer) + tty->ops->flush_buffer(tty); +} + +EXPORT_SYMBOL(tty_driver_flush_buffer); + + /** * tty_wait_until_sent - wait for I/O to finish * @tty: tty we are waiting for @@ -58,17 +86,13 @@ void tty_wait_until_sent(struct tty_struct *tty, long timeout) printk(KERN_DEBUG "%s wait until sent...\n", tty_name(tty, buf)); #endif - if (!tty->driver->chars_in_buffer) - return; if (!timeout) timeout = MAX_SCHEDULE_TIMEOUT; - lock_kernel(); if (wait_event_interruptible_timeout(tty->write_wait, - !tty->driver->chars_in_buffer(tty), timeout) >= 0) { - if (tty->driver->wait_until_sent) - tty->driver->wait_until_sent(tty, timeout); + !tty_chars_in_buffer(tty), timeout) >= 0) { + if (tty->ops->wait_until_sent) + tty->ops->wait_until_sent(tty, timeout); } - unlock_kernel(); } EXPORT_SYMBOL(tty_wait_until_sent); @@ -444,8 +468,8 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) } } - if (tty->driver->set_termios) - (*tty->driver->set_termios)(tty, &old_termios); + if (tty->ops->set_termios) + (*tty->ops->set_termios)(tty, &old_termios); else tty_termios_copy_hw(tty->termios, &old_termios); @@ -748,8 +772,8 @@ static int send_prio_char(struct tty_struct *tty, char ch) { int was_stopped = tty->stopped; - if (tty->driver->send_xchar) { - tty->driver->send_xchar(tty, ch); + if (tty->ops->send_xchar) { + tty->ops->send_xchar(tty, ch); return 0; } @@ -758,7 +782,7 @@ static int send_prio_char(struct tty_struct *tty, char ch) if (was_stopped) start_tty(tty); - tty->driver->write(tty, &ch, 1); + tty->ops->write(tty, &ch, 1); if (was_stopped) stop_tty(tty); tty_write_unlock(tty); @@ -778,13 +802,14 @@ static int tty_change_softcar(struct tty_struct *tty, int arg) { int ret = 0; int bit = arg ? CLOCAL : 0; - struct ktermios old = *tty->termios; + struct ktermios old; mutex_lock(&tty->termios_mutex); + old = *tty->termios; tty->termios->c_cflag &= ~CLOCAL; tty->termios->c_cflag |= bit; - if (tty->driver->set_termios) - tty->driver->set_termios(tty, &old); + if (tty->ops->set_termios) + tty->ops->set_termios(tty, &old); if ((tty->termios->c_cflag & CLOCAL) != bit) ret = -EINVAL; mutex_unlock(&tty->termios_mutex); @@ -926,8 +951,7 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg) ld->flush_buffer(tty); /* fall through */ case TCOFLUSH: - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); break; default: tty_ldisc_deref(ld); @@ -984,9 +1008,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, case TCFLSH: return tty_perform_flush(tty, arg); case TIOCOUTQ: - return put_user(tty->driver->chars_in_buffer ? - tty->driver->chars_in_buffer(tty) : 0, - (int __user *) arg); + return put_user(tty_chars_in_buffer(tty), (int __user *) arg); case TIOCINQ: retval = tty->read_cnt; if (L_ICANON(tty)) -- cgit v1.2.3-18-g5258