diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-06-27 23:00:25 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-06-27 23:00:25 -0700 |
commit | 31881d74b6dd1a6c530cff61248def4f2da38bee (patch) | |
tree | be62420cf39192074e13b25553d172b9d5e58a33 /drivers/tty | |
parent | 8855f30cd2b68012571932c7b01290c20be4508c (diff) | |
parent | 257867dc8d893690c175c1f717f91c3b6d44a63d (diff) |
Merge branch 'for-next' of git://github.com/rydberg/linux into next
Pull in changes from Henrik: "a trivial MT documentation fix".
Diffstat (limited to 'drivers/tty')
71 files changed, 1630 insertions, 1362 deletions
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index fc700342d43..083710e0236 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1798,19 +1798,7 @@ static struct platform_driver amiga_serial_driver = { }, }; -static int __init amiga_serial_init(void) -{ - return platform_driver_probe(&amiga_serial_driver, amiga_serial_probe); -} - -module_init(amiga_serial_init); - -static void __exit amiga_serial_exit(void) -{ - platform_driver_unregister(&amiga_serial_driver); -} - -module_exit(amiga_serial_exit); +module_platform_driver_probe(amiga_serial_driver, amiga_serial_probe); #if defined(CONFIG_SERIAL_CONSOLE) && !defined(MODULE) diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 345bd0e0884..33f83fee9fa 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -1124,14 +1124,8 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) readl(&info->u.cyz.ch_ctrl->rs_status); if (dcd & C_RS_DCD) wake_up_interruptible(&info->port.open_wait); - else { - struct tty_struct *tty; - tty = tty_port_tty_get(&info->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } + else + tty_port_tty_hangup(&info->port, false); } break; case C_CM_MCTS: diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index ed92622b894..9bffcec5ad8 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -472,13 +472,9 @@ static void ehv_bc_tx_dequeue(struct ehv_bc_data *bc) static irqreturn_t ehv_bc_tty_tx_isr(int irq, void *data) { struct ehv_bc_data *bc = data; - struct tty_struct *ttys = tty_port_tty_get(&bc->port); ehv_bc_tx_dequeue(bc); - if (ttys) { - tty_wakeup(ttys); - tty_kref_put(ttys); - } + tty_port_tty_wakeup(&bc->port); return IRQ_HANDLED; } @@ -863,6 +859,7 @@ error: */ static void __exit ehv_bc_exit(void) { + platform_driver_unregister(&ehv_bc_tty_driver); tty_unregister_driver(ehv_bc_driver); put_tty_driver(ehv_bc_driver); kfree(bcs); diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index ef95a154854..41901997c0d 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -861,7 +861,6 @@ static void hvsi_write_worker(struct work_struct *work) { struct hvsi_struct *hp = container_of(work, struct hvsi_struct, writer.work); - struct tty_struct *tty; unsigned long flags; #ifdef DEBUG static long start_j = 0; @@ -895,11 +894,7 @@ static void hvsi_write_worker(struct work_struct *work) start_j = 0; #endif /* DEBUG */ wake_up_all(&hp->emptyq); - tty = tty_port_tty_get(&hp->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&hp->port); } out: diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c index 97a511f4185..2c14842541d 100644 --- a/drivers/tty/ipwireless/hardware.c +++ b/drivers/tty/ipwireless/hardware.c @@ -1732,8 +1732,7 @@ void ipwireless_hardware_free(struct ipw_hardware *hw) flush_work(&hw->work_rx); for (i = 0; i < NL_NUM_OF_ADDRESSES; i++) - if (hw->packet_assembler[i] != NULL) - kfree(hw->packet_assembler[i]); + kfree(hw->packet_assembler[i]); for (i = 0; i < NL_NUM_OF_PRIORITIES; i++) list_for_each_entry_safe(tp, tq, &hw->tx_queue[i], queue) { diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index adeac255e52..1deaca4674e 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -913,16 +913,12 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) /* pci hot-un-plug support */ for (a = 0; a < brd->numPorts; a++) - if (brd->ports[a].port.flags & ASYNC_INITIALIZED) { - struct tty_struct *tty = tty_port_tty_get( - &brd->ports[a].port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } + if (brd->ports[a].port.flags & ASYNC_INITIALIZED) + tty_port_tty_hangup(&brd->ports[a].port, false); + for (a = 0; a < MAX_PORTS_PER_BOARD; a++) tty_port_destroy(&brd->ports[a].port); + while (1) { opened = 0; for (a = 0; a < brd->numPorts; a++) @@ -1365,7 +1361,6 @@ static void moxa_hangup(struct tty_struct *tty) static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) { - struct tty_struct *tty; unsigned long flags; dcd = !!dcd; @@ -1373,10 +1368,8 @@ static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) if (dcd != p->DCDState) { p->DCDState = dcd; spin_unlock_irqrestore(&p->port.lock, flags); - tty = tty_port_tty_get(&p->port); - if (tty && !C_CLOCAL(tty) && !dcd) - tty_hangup(tty); - tty_kref_put(tty); + if (!dcd) + tty_port_tty_hangup(&p->port, true); } else spin_unlock_irqrestore(&p->port.lock, flags); diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 484b6a3c9b0..4c4a2367456 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -1084,6 +1084,10 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) mutex_lock(&port->mutex); mxser_close_port(port); mxser_flush_buffer(tty); + if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (C_HUPCL(tty)) + tty_port_lower_dtr_rts(port); + } mxser_shutdown_port(port); clear_bit(ASYNCB_INITIALIZED, &port->flags); mutex_unlock(&port->mutex); @@ -1614,8 +1618,12 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) if (ip->type == PORT_16550A) me->fifo[p] = 1; - opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2); - opmode &= OP_MODE_MASK; + if (ip->board->chip_flag == MOXA_MUST_MU860_HWID) { + opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2); + opmode &= OP_MODE_MASK; + } else { + opmode = RS232_MODE; + } me->iftype[p] = opmode; mutex_unlock(&port->mutex); } @@ -1672,6 +1680,9 @@ static int mxser_ioctl(struct tty_struct *tty, int shiftbit; unsigned char val, mask; + if (info->board->chip_flag != MOXA_MUST_MU860_HWID) + return -EFAULT; + p = tty->index % 4; if (cmd == MOXA_SET_OP_MODE) { if (get_user(opmode, (int __user *) argp)) @@ -2643,9 +2654,9 @@ static int mxser_probe(struct pci_dev *pdev, mxvar_sdriver, brd->idx + i, &pdev->dev); if (IS_ERR(tty_dev)) { retval = PTR_ERR(tty_dev); - for (i--; i >= 0; i--) + for (; i > 0; i--) tty_unregister_device(mxvar_sdriver, - brd->idx + i); + brd->idx + i - 1); goto err_relbrd; } } @@ -2751,9 +2762,9 @@ static int __init mxser_module_init(void) tty_dev = tty_port_register_device(&brd->ports[i].port, mxvar_sdriver, brd->idx + i, NULL); if (IS_ERR(tty_dev)) { - for (i--; i >= 0; i--) + for (; i > 0; i--) tty_unregister_device(mxvar_sdriver, - brd->idx + i); + brd->idx + i - 1); for (i = 0; i < brd->info->nports; i++) tty_port_destroy(&brd->ports[i].port); free_irq(brd->irq, brd); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 4a43ef5d796..642239015b4 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1418,11 +1418,7 @@ static void gsm_dlci_close(struct gsm_dlci *dlci) pr_debug("DLCI %d goes closed.\n", dlci->addr); dlci->state = DLCI_CLOSED; if (dlci->addr != 0) { - struct tty_struct *tty = tty_port_tty_get(&dlci->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&dlci->port, false); kfifo_reset(dlci->fifo); } else dlci->gsm->dead = 1; @@ -2968,6 +2964,10 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) if (tty_port_close_start(&dlci->port, tty, filp) == 0) goto out; gsm_dlci_begin_close(dlci); + if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) { + if (C_HUPCL(tty)) + tty_port_lower_dtr_rts(&dlci->port); + } tty_port_close_end(&dlci->port, tty); tty_port_tty_set(&dlci->port, NULL); out: diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 05e72bea9b0..6c7fe90ad72 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -153,6 +153,12 @@ static void n_tty_set_room(struct tty_struct *tty) if (left && !old_left) { WARN_RATELIMIT(tty->port->itty == NULL, "scheduling with invalid itty\n"); + /* see if ldisc has been killed - if so, this means that + * even though the ldisc has been halted and ->buf.work + * cancelled, ->buf.work is about to be rescheduled + */ + WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags), + "scheduling buffer work for halted ldisc\n"); schedule_work(&tty->port->buf.work); } } @@ -189,34 +195,17 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata) } /** - * check_unthrottle - allow new receive data - * @tty; tty device - * - * Check whether to call the driver unthrottle functions - * - * Can sleep, may be called under the atomic_read_lock mutex but - * this is not guaranteed. - */ -static void check_unthrottle(struct tty_struct *tty) -{ - if (tty->count) - tty_unthrottle(tty); -} - -/** * reset_buffer_flags - reset buffer state * @tty: terminal to reset * - * Reset the read buffer counters, clear the flags, - * and make sure the driver is unthrottled. Called - * from n_tty_open() and n_tty_flush_buffer(). + * Reset the read buffer counters and clear the flags. + * Called from n_tty_open() and n_tty_flush_buffer(). * * Locking: tty_read_lock for read fields. */ -static void reset_buffer_flags(struct tty_struct *tty) +static void reset_buffer_flags(struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; unsigned long flags; raw_spin_lock_irqsave(&ldata->read_lock, flags); @@ -229,36 +218,38 @@ static void reset_buffer_flags(struct tty_struct *tty) ldata->canon_head = ldata->canon_data = ldata->erasing = 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); - n_tty_set_room(tty); +} + +static void n_tty_packet_mode_flush(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (tty->link->packet) { + tty->ctrl_status |= TIOCPKT_FLUSHREAD; + wake_up_interruptible(&tty->link->read_wait); + } + spin_unlock_irqrestore(&tty->ctrl_lock, flags); } /** * n_tty_flush_buffer - clean input queue * @tty: terminal device * - * Flush the input buffer. Called when the line discipline is - * being closed, when the tty layer wants the buffer flushed (eg - * at hangup) or when the N_TTY line discipline internally has to - * clean the pending queue (for example some signals). + * Flush the input buffer. Called when the tty layer wants the + * buffer flushed (eg at hangup) or when the N_TTY line discipline + * internally has to clean the pending queue (for example some signals). * * Locking: ctrl_lock, read_lock. */ static void n_tty_flush_buffer(struct tty_struct *tty) { - unsigned long flags; - /* clear everything and unthrottle the driver */ - reset_buffer_flags(tty); - - if (!tty->link) - return; + reset_buffer_flags(tty->disc_data); + n_tty_set_room(tty); - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->link->packet) { - tty->ctrl_status |= TIOCPKT_FLUSHREAD; - wake_up_interruptible(&tty->link->read_wait); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + if (tty->link) + n_tty_packet_mode_flush(tty); } /** @@ -1032,23 +1023,19 @@ static void eraser(unsigned char c, struct tty_struct *tty) * isig - handle the ISIG optio * @sig: signal * @tty: terminal - * @flush: force flush * - * Called when a signal is being sent due to terminal input. This - * may caus terminal flushing to take place according to the termios - * settings and character used. Called from the driver receive_buf - * path so serialized. + * Called when a signal is being sent due to terminal input. + * Called from the driver receive_buf path so serialized. * - * Locking: ctrl_lock, read_lock (both via flush buffer) + * Locking: ctrl_lock */ -static inline void isig(int sig, struct tty_struct *tty, int flush) +static inline void isig(int sig, struct tty_struct *tty) { - if (tty->pgrp) - kill_pgrp(tty->pgrp, sig, 1); - if (flush || !L_NOFLSH(tty)) { - n_tty_flush_buffer(tty); - tty_driver_flush_buffer(tty); + struct pid *tty_pgrp = tty_get_pgrp(tty); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, sig, 1); + put_pid(tty_pgrp); } } @@ -1069,7 +1056,11 @@ static inline void n_tty_receive_break(struct tty_struct *tty) if (I_IGNBRK(tty)) return; if (I_BRKINT(tty)) { - isig(SIGINT, tty, 1); + isig(SIGINT, tty); + if (!L_NOFLSH(tty)) { + n_tty_flush_buffer(tty); + tty_driver_flush_buffer(tty); + } return; } if (I_PARMRK(tty)) { @@ -1236,11 +1227,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) signal = SIGTSTP; if (c == SUSP_CHAR(tty)) { send_signal: - /* - * Note that we do not use isig() here because we want - * the order to be: - * 1) flush, 2) echo, 3) signal - */ if (!L_NOFLSH(tty)) { n_tty_flush_buffer(tty); tty_driver_flush_buffer(tty); @@ -1251,8 +1237,7 @@ send_signal: echo_char(c, tty); process_echoes(tty); } - if (tty->pgrp) - kill_pgrp(tty->pgrp, signal, 1); + isig(signal, tty); return; } } @@ -1483,14 +1468,14 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, * mode. We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet! */ - if (tty->receive_room < TTY_THRESHOLD_THROTTLE) - tty_throttle(tty); - - /* FIXME: there is a tiny race here if the receive room check runs - before the other work executes and empties the buffer (upping - the receiving room and unthrottling. We then throttle and get - stuck. This has been observed and traced down by Vincent Pillet/ - We need to address this when we sort out out the rx path locking */ + while (1) { + tty_set_flow_change(tty, TTY_THROTTLE_SAFE); + if (tty->receive_room >= TTY_THRESHOLD_THROTTLE) + break; + if (!tty_throttle_safe(tty)) + break; + } + __tty_set_flow_change(tty, 0); } int is_ignored(int sig) @@ -1588,6 +1573,14 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) ldata->real_raw = 0; } n_tty_set_room(tty); + /* + * Fix tty hang when I_IXON(tty) is cleared, but the tty + * been stopped by STOP_CHAR(tty) before it. + */ + if (!I_IXON(tty) && old && (old->c_iflag & IXON) && !tty->flow_stopped) { + start_tty(tty); + } + /* The termios change make the tty ready for I/O */ wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); @@ -1607,7 +1600,9 @@ static void n_tty_close(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - n_tty_flush_buffer(tty); + if (tty->link) + n_tty_packet_mode_flush(tty); + kfree(ldata->read_buf); kfree(ldata->echo_buf); kfree(ldata); @@ -1645,12 +1640,14 @@ static int n_tty_open(struct tty_struct *tty) goto err_free_bufs; tty->disc_data = ldata; - reset_buffer_flags(tty); - tty_unthrottle(tty); + reset_buffer_flags(tty->disc_data); ldata->column = 0; - n_tty_set_termios(tty, NULL); tty->minimum_to_wake = 1; tty->closing = 0; + /* indicate buffer work may resume */ + clear_bit(TTY_LDISC_HALTED, &tty->flags); + n_tty_set_termios(tty, NULL); + tty_unthrottle(tty); return 0; err_free_bufs: @@ -1740,10 +1737,9 @@ extern ssize_t redirected_tty_write(struct file *, const char __user *, * and if appropriate send any needed signals and return a negative * error code if action should be taken. * - * FIXME: - * Locking: None - redirected write test is safe, testing - * current->signal should possibly lock current->sighand - * pgrp locking ? + * Locking: redirected write test is safe + * current->signal->tty check is safe + * ctrl_lock to safely reference tty->pgrp */ static int job_control(struct tty_struct *tty, struct file *file) @@ -1753,19 +1749,22 @@ static int job_control(struct tty_struct *tty, struct file *file) /* NOTE: not yet done after every sleep pending a thorough check of the logic of this change. -- jlc */ /* don't stop on /dev/console */ - if (file->f_op->write != redirected_tty_write && - current->signal->tty == tty) { - if (!tty->pgrp) - printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); - else if (task_pgrp(current) != tty->pgrp) { - if (is_ignored(SIGTTIN) || - is_current_pgrp_orphaned()) - return -EIO; - kill_pgrp(task_pgrp(current), SIGTTIN, 1); - set_thread_flag(TIF_SIGPENDING); - return -ERESTARTSYS; - } + if (file->f_op->write == redirected_tty_write || + current->signal->tty != tty) + return 0; + + spin_lock_irq(&tty->ctrl_lock); + if (!tty->pgrp) + printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); + else if (task_pgrp(current) != tty->pgrp) { + spin_unlock_irq(&tty->ctrl_lock); + if (is_ignored(SIGTTIN) || is_current_pgrp_orphaned()) + return -EIO; + kill_pgrp(task_pgrp(current), SIGTTIN, 1); + set_thread_flag(TIF_SIGPENDING); + return -ERESTARTSYS; } + spin_unlock_irq(&tty->ctrl_lock); return 0; } @@ -1959,10 +1958,17 @@ do_it_again: * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */ - if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { + while (1) { + tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); + if (n_tty_chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + break; + if (!tty->count) + break; n_tty_set_room(tty); - check_unthrottle(tty); + if (!tty_unthrottle_safe(tty)) + break; } + __tty_set_flow_change(tty, 0); if (b - buf >= minimum) break; diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 2dff1979615..d6080c3831e 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -791,7 +791,6 @@ static int send_data(enum port_type index, struct nozomi *dc) const u8 toggle = port->toggle_ul; void __iomem *addr = port->ul_addr[toggle]; const u32 ul_size = port->ul_size[toggle]; - struct tty_struct *tty = tty_port_tty_get(&port->port); /* Get data from tty and place in buf for now */ size = kfifo_out(&port->fifo_ul, dc->send_buf, @@ -799,7 +798,6 @@ static int send_data(enum port_type index, struct nozomi *dc) if (size == 0) { DBG4("No more data to send, disable link:"); - tty_kref_put(tty); return 0; } @@ -809,10 +807,8 @@ static int send_data(enum port_type index, struct nozomi *dc) write_mem32(addr, (u32 *) &size, 4); write_mem32(addr + 4, (u32 *) dc->send_buf, size); - if (tty) - tty_wakeup(tty); + tty_port_tty_wakeup(&port->port); - tty_kref_put(tty); return 1; } @@ -1505,12 +1501,9 @@ static void tty_exit(struct nozomi *dc) DBG1(" "); - for (i = 0; i < MAX_PORT; ++i) { - struct tty_struct *tty = tty_port_tty_get(&dc->port[i].port); - if (tty && list_empty(&tty->hangup_work.entry)) - tty_hangup(tty); - tty_kref_put(tty); - } + for (i = 0; i < MAX_PORT; ++i) + tty_port_tty_hangup(&dc->port[i].port, false); + /* Racy below - surely should wait for scheduled work to be done or complete off a hangup method ? */ while (dc->open_ttys) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index c24b4db243b..59bfaecc4e1 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -405,15 +405,8 @@ err: return retval; } -/* this is called once with whichever end is closed last */ -static void pty_unix98_shutdown(struct tty_struct *tty) -{ - devpts_kill_index(tty->driver_data, tty->index); -} - static void pty_cleanup(struct tty_struct *tty) { - tty->port->itty = NULL; tty_port_put(tty->port); } @@ -627,6 +620,12 @@ static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) { } +/* this is called once with whichever end is closed last */ +static void pty_unix98_shutdown(struct tty_struct *tty) +{ + devpts_kill_index(tty->driver_data, tty->index); +} + static const struct tty_operations ptm_unix98_ops = { .lookup = ptm_unix98_lookup, .install = pty_unix98_install, @@ -682,6 +681,9 @@ static int ptmx_open(struct inode *inode, struct file *filp) nonseekable_open(inode, filp); + /* We refuse fsnotify events on ptmx, since it's a shared resource */ + filp->f_mode |= FMODE_NONOTIFY; + retval = tty_alloc_file(filp); if (retval) return retval; diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 1d270034bfc..354564ea47c 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -150,12 +150,14 @@ static Word_t aiop_intr_bits[AIOP_CTL_SIZE] = { AIOP_INTR_BIT_3 }; +#ifdef CONFIG_PCI static Word_t upci_aiop_intr_bits[AIOP_CTL_SIZE] = { UPCI_AIOP_INTR_BIT_0, UPCI_AIOP_INTR_BIT_1, UPCI_AIOP_INTR_BIT_2, UPCI_AIOP_INTR_BIT_3 }; +#endif static Byte_t RData[RDATASIZE] = { 0x00, 0x09, 0xf6, 0x82, @@ -227,7 +229,6 @@ static unsigned long nextLineNumber; static int __init init_ISA(int i); static void rp_wait_until_sent(struct tty_struct *tty, int timeout); static void rp_flush_buffer(struct tty_struct *tty); -static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model); static unsigned char GetLineNumber(int ctrl, int aiop, int ch); static unsigned char SetLineNumber(int ctrl, int aiop, int ch); static void rp_start(struct tty_struct *tty); @@ -241,11 +242,6 @@ static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags); static void sModemReset(CONTROLLER_T * CtlP, int chan, int on); static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on); static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data); -static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, - ByteIO_t * AiopIOList, int AiopIOListSize, - WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, - int PeriodicOnly, int altChanRingIndicator, - int UPCIRingInd); static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, ByteIO_t * AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly); @@ -449,7 +445,7 @@ static void rp_do_transmit(struct r_port *info) /* Loop sending data to FIFO until done or FIFO full */ while (1) { - if (tty->stopped || tty->hw_stopped) + if (tty->stopped) break; c = min(info->xmit_fifo_room, info->xmit_cnt); c = min(c, XMIT_BUF_SIZE - info->xmit_tail); @@ -521,15 +517,10 @@ static void rp_handle_port(struct r_port *info) (ChanStatus & CD_ACT) ? "on" : "off"); #endif if (!(ChanStatus & CD_ACT) && info->cd_status) { - struct tty_struct *tty; #ifdef ROCKET_DEBUG_HANGUP printk(KERN_INFO "CD drop, calling hangup.\n"); #endif - tty = tty_port_tty_get(&info->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&info->port, false); } info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0; wake_up_interruptible(&info->port.open_wait); @@ -1111,15 +1102,12 @@ static void rp_set_termios(struct tty_struct *tty, /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (tty->termios.c_cflag & CBAUD)) { - if (!tty->hw_stopped || !(tty->termios.c_cflag & CRTSCTS)) - sSetRTS(cp); + sSetRTS(cp); sSetDTR(cp); } - if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios.c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; + if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios.c_cflag & CRTSCTS)) rp_start(tty); - } } static int rp_break(struct tty_struct *tty, int break_state) @@ -1575,10 +1563,10 @@ static int rp_put_char(struct tty_struct *tty, unsigned char ch) spin_lock_irqsave(&info->slock, flags); cp = &info->channel; - if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0) + if (!tty->stopped && info->xmit_fifo_room == 0) info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - if (tty->stopped || tty->hw_stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { + if (tty->stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= XMIT_BUF_SIZE - 1; info->xmit_cnt++; @@ -1619,14 +1607,14 @@ static int rp_write(struct tty_struct *tty, #endif cp = &info->channel; - if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room < count) + if (!tty->stopped && info->xmit_fifo_room < count) info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); /* * If the write queue for the port is empty, and there is FIFO space, stuff bytes * into FIFO. Use the write queue for temp storage. */ - if (!tty->stopped && !tty->hw_stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { + if (!tty->stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { c = min(count, info->xmit_fifo_room); b = buf; @@ -1674,7 +1662,7 @@ static int rp_write(struct tty_struct *tty, retval += c; } - if ((retval > 0) && !tty->stopped && !tty->hw_stopped) + if ((retval > 0) && !tty->stopped) set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); end: @@ -1783,6 +1771,145 @@ static DEFINE_PCI_DEVICE_TABLE(rocket_pci_ids) = { }; MODULE_DEVICE_TABLE(pci, rocket_pci_ids); +/* Resets the speaker controller on RocketModem II and III devices */ +static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) +{ + ByteIO_t addr; + + /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ + if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { + addr = CtlP->AiopIO[0] + 0x4F; + sOutB(addr, 0); + } + + /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ + if ((model == MODEL_UPCI_RM3_8PORT) + || (model == MODEL_UPCI_RM3_4PORT)) { + addr = CtlP->AiopIO[0] + 0x88; + sOutB(addr, 0); + } +} + +/*************************************************************************** +Function: sPCIInitController +Purpose: Initialization of controller global registers and controller + structure. +Call: sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize, + IRQNum,Frequency,PeriodicOnly) + CONTROLLER_T *CtlP; Ptr to controller structure + int CtlNum; Controller number + ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. + This list must be in the order the AIOPs will be found on the + controller. Once an AIOP in the list is not found, it is + assumed that there are no more AIOPs on the controller. + int AiopIOListSize; Number of addresses in AiopIOList + int IRQNum; Interrupt Request number. Can be any of the following: + 0: Disable global interrupts + 3: IRQ 3 + 4: IRQ 4 + 5: IRQ 5 + 9: IRQ 9 + 10: IRQ 10 + 11: IRQ 11 + 12: IRQ 12 + 15: IRQ 15 + Byte_t Frequency: A flag identifying the frequency + of the periodic interrupt, can be any one of the following: + FREQ_DIS - periodic interrupt disabled + FREQ_137HZ - 137 Hertz + FREQ_69HZ - 69 Hertz + FREQ_34HZ - 34 Hertz + FREQ_17HZ - 17 Hertz + FREQ_9HZ - 9 Hertz + FREQ_4HZ - 4 Hertz + If IRQNum is set to 0 the Frequency parameter is + overidden, it is forced to a value of FREQ_DIS. + int PeriodicOnly: 1 if all interrupts except the periodic + interrupt are to be blocked. + 0 is both the periodic interrupt and + other channel interrupts are allowed. + If IRQNum is set to 0 the PeriodicOnly parameter is + overidden, it is forced to a value of 0. +Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller + initialization failed. + +Comments: + If periodic interrupts are to be disabled but AIOP interrupts + are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. + + If interrupts are to be completely disabled set IRQNum to 0. + + Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an + invalid combination. + + This function performs initialization of global interrupt modes, + but it does not actually enable global interrupts. To enable + and disable global interrupts use functions sEnGlobalInt() and + sDisGlobalInt(). Enabling of global interrupts is normally not + done until all other initializations are complete. + + Even if interrupts are globally enabled, they must also be + individually enabled for each channel that is to generate + interrupts. + +Warnings: No range checking on any of the parameters is done. + + No context switches are allowed while executing this function. + + After this function all AIOPs on the controller are disabled, + they can be enabled with sEnAiop(). +*/ +static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, + ByteIO_t * AiopIOList, int AiopIOListSize, + WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, + int PeriodicOnly, int altChanRingIndicator, + int UPCIRingInd) +{ + int i; + ByteIO_t io; + + CtlP->AltChanRingIndicator = altChanRingIndicator; + CtlP->UPCIRingInd = UPCIRingInd; + CtlP->CtlNum = CtlNum; + CtlP->CtlID = CTLID_0001; /* controller release 1 */ + CtlP->BusType = isPCI; /* controller release 1 */ + + if (ConfigIO) { + CtlP->isUPCI = 1; + CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL; + CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL; + CtlP->AiopIntrBits = upci_aiop_intr_bits; + } else { + CtlP->isUPCI = 0; + CtlP->PCIIO = + (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC); + CtlP->AiopIntrBits = aiop_intr_bits; + } + + sPCIControllerEOI(CtlP); /* clear EOI if warm init */ + /* Init AIOPs */ + CtlP->NumAiop = 0; + for (i = 0; i < AiopIOListSize; i++) { + io = AiopIOList[i]; + CtlP->AiopIO[i] = (WordIO_t) io; + CtlP->AiopIntChanIO[i] = io + _INT_CHAN; + + CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ + if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ + break; /* done looking for AIOPs */ + + CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ + sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ + sOutB(io + _INDX_DATA, sClockPrescale); + CtlP->NumAiop++; /* bump count of AIOPs */ + } + + if (CtlP->NumAiop == 0) + return (-1); + else + return (CtlP->NumAiop); +} + /* * Called when a PCI card is found. Retrieves and stores model information, * init's aiopic and serial port hardware. @@ -2528,126 +2655,6 @@ static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, } /*************************************************************************** -Function: sPCIInitController -Purpose: Initialization of controller global registers and controller - structure. -Call: sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize, - IRQNum,Frequency,PeriodicOnly) - CONTROLLER_T *CtlP; Ptr to controller structure - int CtlNum; Controller number - ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. - This list must be in the order the AIOPs will be found on the - controller. Once an AIOP in the list is not found, it is - assumed that there are no more AIOPs on the controller. - int AiopIOListSize; Number of addresses in AiopIOList - int IRQNum; Interrupt Request number. Can be any of the following: - 0: Disable global interrupts - 3: IRQ 3 - 4: IRQ 4 - 5: IRQ 5 - 9: IRQ 9 - 10: IRQ 10 - 11: IRQ 11 - 12: IRQ 12 - 15: IRQ 15 - Byte_t Frequency: A flag identifying the frequency - of the periodic interrupt, can be any one of the following: - FREQ_DIS - periodic interrupt disabled - FREQ_137HZ - 137 Hertz - FREQ_69HZ - 69 Hertz - FREQ_34HZ - 34 Hertz - FREQ_17HZ - 17 Hertz - FREQ_9HZ - 9 Hertz - FREQ_4HZ - 4 Hertz - If IRQNum is set to 0 the Frequency parameter is - overidden, it is forced to a value of FREQ_DIS. - int PeriodicOnly: 1 if all interrupts except the periodic - interrupt are to be blocked. - 0 is both the periodic interrupt and - other channel interrupts are allowed. - If IRQNum is set to 0 the PeriodicOnly parameter is - overidden, it is forced to a value of 0. -Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller - initialization failed. - -Comments: - If periodic interrupts are to be disabled but AIOP interrupts - are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. - - If interrupts are to be completely disabled set IRQNum to 0. - - Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an - invalid combination. - - This function performs initialization of global interrupt modes, - but it does not actually enable global interrupts. To enable - and disable global interrupts use functions sEnGlobalInt() and - sDisGlobalInt(). Enabling of global interrupts is normally not - done until all other initializations are complete. - - Even if interrupts are globally enabled, they must also be - individually enabled for each channel that is to generate - interrupts. - -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. - - After this function all AIOPs on the controller are disabled, - they can be enabled with sEnAiop(). -*/ -static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, - ByteIO_t * AiopIOList, int AiopIOListSize, - WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, - int PeriodicOnly, int altChanRingIndicator, - int UPCIRingInd) -{ - int i; - ByteIO_t io; - - CtlP->AltChanRingIndicator = altChanRingIndicator; - CtlP->UPCIRingInd = UPCIRingInd; - CtlP->CtlNum = CtlNum; - CtlP->CtlID = CTLID_0001; /* controller release 1 */ - CtlP->BusType = isPCI; /* controller release 1 */ - - if (ConfigIO) { - CtlP->isUPCI = 1; - CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL; - CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL; - CtlP->AiopIntrBits = upci_aiop_intr_bits; - } else { - CtlP->isUPCI = 0; - CtlP->PCIIO = - (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC); - CtlP->AiopIntrBits = aiop_intr_bits; - } - - sPCIControllerEOI(CtlP); /* clear EOI if warm init */ - /* Init AIOPs */ - CtlP->NumAiop = 0; - for (i = 0; i < AiopIOListSize; i++) { - io = AiopIOList[i]; - CtlP->AiopIO[i] = (WordIO_t) io; - CtlP->AiopIntChanIO[i] = io + _INT_CHAN; - - CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ - if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ - break; /* done looking for AIOPs */ - - CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ - sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ - sOutB(io + _INDX_DATA, sClockPrescale); - CtlP->NumAiop++; /* bump count of AIOPs */ - } - - if (CtlP->NumAiop == 0) - return (-1); - else - return (CtlP->NumAiop); -} - -/*************************************************************************** Function: sReadAiopID Purpose: Read the AIOP idenfication number directly from an AIOP. Call: sReadAiopID(io) @@ -3136,25 +3143,6 @@ static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on) sOutB(addr + chan, 0); /* apply or remove reset */ } -/* Resets the speaker controller on RocketModem II and III devices */ -static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) -{ - ByteIO_t addr; - - /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ - if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { - addr = CtlP->AiopIO[0] + 0x4F; - sOutB(addr, 0); - } - - /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ - if ((model == MODEL_UPCI_RM3_8PORT) - || (model == MODEL_UPCI_RM3_4PORT)) { - addr = CtlP->AiopIO[0] + 0x88; - sOutB(addr, 0); - } -} - /* Returns the line number given the controller (board), aiop and channel number */ static unsigned char GetLineNumber(int ctrl, int aiop, int ch) { diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index 49399470794..5dc9c4bfa66 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -14,7 +14,6 @@ * 2.4/2.5 port David McCullough */ -#include <asm/dbg.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/serial.h> @@ -630,8 +629,7 @@ static void rs_flush_chars(struct tty_struct *tty) /* Enable transmitter */ local_irq_save(flags); - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) { + if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf) { local_irq_restore(flags); return; } @@ -697,7 +695,7 @@ static int rs_write(struct tty_struct * tty, total += c; } - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { + if (info->xmit_cnt && !tty->stopped) { /* Enable transmitter */ local_irq_disable(); #ifndef USE_INTS @@ -978,10 +976,8 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) change_speed(info, tty); if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; + !(tty->termios.c_cflag & CRTSCTS)) rs_start(tty); - } } diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 34eb676916f..1ebf8538b4f 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -117,13 +117,6 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) * is cleared, the machine locks up with endless interrupts. */ #define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) -#elif defined(CONFIG_SBC8560) -/* - * WindRiver did something similarly broken on their SBC8560 board. The - * UART tristates its IRQ output while OUT2 is clear, but they pulled - * the interrupt line _up_ instead of down, so if we register the IRQ - * while the UART is in that state, we die in an IRQ storm. */ -#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2) #else #define ALPHA_KLUDGE_MCR 0 #endif diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250_core.c index cf6a5383748..86c00b1c558 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3247,6 +3247,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->tx_loadsz = up->tx_loadsz; uart->capabilities = up->capabilities; + /* Take tx_loadsz from fifosize if it wasn't set separately */ + if (uart->port.fifosize && !uart->tx_loadsz) + uart->tx_loadsz = uart->port.fifosize; + if (up->port.dev) uart->port.dev = up->port.dev; @@ -3418,6 +3422,7 @@ MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); #endif MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); +#ifdef CONFIG_SERIAL_8250_DEPRECATED_OPTIONS #ifndef MODULE /* This module was renamed to 8250_core in 3.7. Keep the old "8250" name * working as well for the module options so we don't break people. We @@ -3432,7 +3437,7 @@ MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); static void __used s8250_options(void) { #undef MODULE_PARAM_PREFIX -#define MODULE_PARAM_PREFIX "8250." +#define MODULE_PARAM_PREFIX "8250_core." module_param_cb(share_irqs, ¶m_ops_uint, &share_irqs, 0644); module_param_cb(nr_uarts, ¶m_ops_uint, &nr_uarts, 0644); @@ -3444,5 +3449,6 @@ static void __used s8250_options(void) #endif } #else -MODULE_ALIAS("8250"); +MODULE_ALIAS("8250_core"); +#endif #endif diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index b9f7fd28112..7046769608d 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -33,10 +33,8 @@ static void __dma_tx_complete(void *param) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&p->port); - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) { + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) serial8250_tx_dma(p); - uart_write_wakeup(&p->port); - } } static void __dma_rx_complete(void *param) @@ -67,12 +65,11 @@ int serial8250_tx_dma(struct uart_8250_port *p) struct circ_buf *xmit = &p->port.state->xmit; struct dma_async_tx_descriptor *desc; - if (dma->tx_running) - return -EBUSY; + if (uart_tx_stopped(&p->port) || dma->tx_running || + uart_circ_empty(xmit)) + return 0; dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - if (!dma->tx_size) - return -EINVAL; desc = dmaengine_prep_slave_single(dma->txchan, dma->tx_addr + xmit->tail, @@ -104,20 +101,29 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) struct dma_tx_state state; int dma_status; - /* - * If RCVR FIFO trigger level was not reached, complete the transfer and - * let 8250.c copy the remaining data. - */ - if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) { - dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, - &state); + dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + + switch (iir & 0x3f) { + case UART_IIR_RLSI: + /* 8250_core handles errors and break interrupts */ + return -EIO; + case UART_IIR_RX_TIMEOUT: + /* + * If RCVR FIFO trigger level was not reached, complete the + * transfer and let 8250_core copy the remaining data. + */ if (dma_status == DMA_IN_PROGRESS) { dmaengine_pause(dma->rxchan); __dma_rx_complete(p); } return -ETIMEDOUT; + default: + break; } + if (dma_status) + return 0; + desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, dma->rx_size, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -143,21 +149,31 @@ int serial8250_request_dma(struct uart_8250_port *p) struct uart_8250_dma *dma = p->dma; dma_cap_mask_t mask; - dma->rxconf.src_addr = p->port.mapbase + UART_RX; - dma->txconf.dst_addr = p->port.mapbase + UART_TX; + /* Default slave configuration parameters */ + dma->rxconf.direction = DMA_DEV_TO_MEM; + dma->rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma->rxconf.src_addr = p->port.mapbase + UART_RX; + + dma->txconf.direction = DMA_MEM_TO_DEV; + dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma->txconf.dst_addr = p->port.mapbase + UART_TX; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); /* Get a channel for RX */ - dma->rxchan = dma_request_channel(mask, dma->fn, dma->rx_param); + dma->rxchan = dma_request_slave_channel_compat(mask, + dma->fn, dma->rx_param, + p->port.dev, "rx"); if (!dma->rxchan) return -ENODEV; dmaengine_slave_config(dma->rxchan, &dma->rxconf); /* Get a channel for TX */ - dma->txchan = dma_request_channel(mask, dma->fn, dma->tx_param); + dma->txchan = dma_request_slave_channel_compat(mask, + dma->fn, dma->tx_param, + p->port.dev, "tx"); if (!dma->txchan) { dma_release_channel(dma->rxchan); return -ENODEV; diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index db0e66f6dd0..d07b6af3a93 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -26,6 +26,8 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> #include "8250.h" @@ -34,9 +36,6 @@ #define DW_UART_CPR 0xf4 /* Component Parameter Register */ #define DW_UART_UCV 0xf8 /* UART Component Version */ -/* Intel Low Power Subsystem specific */ -#define LPSS_PRV_CLOCK_PARAMS 0x800 - /* Component Parameter Register bits */ #define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) #define DW_UART_CPR_AFCE_MODE (1 << 4) @@ -55,8 +54,9 @@ struct dw8250_data { - int last_lcr; - int line; + int last_lcr; + int line; + struct clk *clk; }; static void dw8250_serial_out(struct uart_port *p, int offset, int value) @@ -113,6 +113,18 @@ static int dw8250_handle_irq(struct uart_port *p) return 0; } +static void +dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) +{ + if (!state) + pm_runtime_get_sync(port->dev); + + serial8250_do_pm(port, state, old); + + if (state) + pm_runtime_put_sync_suspend(port->dev); +} + static int dw8250_probe_of(struct uart_port *p) { struct device_node *np = p->dev->of_node; @@ -136,8 +148,13 @@ static int dw8250_probe_of(struct uart_port *p) if (!of_property_read_u32(np, "reg-shift", &val)) p->regshift = val; + /* clock got configured through clk api, all done */ + if (p->uartclk) + return 0; + + /* try to find out clock frequency from DT as fallback */ if (of_property_read_u32(np, "clock-frequency", &val)) { - dev_err(p->dev, "no clock-frequency property set\n"); + dev_err(p->dev, "clk or clock-frequency not defined\n"); return -EINVAL; } p->uartclk = val; @@ -146,67 +163,10 @@ static int dw8250_probe_of(struct uart_port *p) } #ifdef CONFIG_ACPI -static bool dw8250_acpi_dma_filter(struct dma_chan *chan, void *parm) -{ - return chan->chan_id == *(int *)parm; -} - -static acpi_status -dw8250_acpi_walk_resource(struct acpi_resource *res, void *data) -{ - struct uart_port *p = data; - struct uart_8250_port *port; - struct uart_8250_dma *dma; - struct acpi_resource_fixed_dma *fixed_dma; - struct dma_slave_config *slave; - - port = container_of(p, struct uart_8250_port, port); - - switch (res->type) { - case ACPI_RESOURCE_TYPE_FIXED_DMA: - fixed_dma = &res->data.fixed_dma; - - /* TX comes first */ - if (!port->dma) { - dma = devm_kzalloc(p->dev, sizeof(*dma), GFP_KERNEL); - if (!dma) - return AE_NO_MEMORY; - - port->dma = dma; - slave = &dma->txconf; - - slave->direction = DMA_MEM_TO_DEV; - slave->dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave->slave_id = fixed_dma->request_lines; - slave->dst_maxburst = port->tx_loadsz / 4; - - dma->tx_chan_id = fixed_dma->channels; - dma->tx_param = &dma->tx_chan_id; - dma->fn = dw8250_acpi_dma_filter; - } else { - dma = port->dma; - slave = &dma->rxconf; - - slave->direction = DMA_DEV_TO_MEM; - slave->src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave->slave_id = fixed_dma->request_lines; - slave->src_maxburst = p->fifosize / 4; - - dma->rx_chan_id = fixed_dma->channels; - dma->rx_param = &dma->rx_chan_id; - } - - break; - } - - return AE_OK; -} - -static int dw8250_probe_acpi(struct uart_port *p) +static int dw8250_probe_acpi(struct uart_8250_port *up) { const struct acpi_device_id *id; - acpi_status status; - u32 reg; + struct uart_port *p = &up->port; id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev); if (!id) @@ -216,26 +176,21 @@ static int dw8250_probe_acpi(struct uart_port *p) p->serial_in = dw8250_serial_in32; p->serial_out = dw8250_serial_out32; p->regshift = 2; - p->uartclk = (unsigned int)id->driver_data; - status = acpi_walk_resources(ACPI_HANDLE(p->dev), METHOD_NAME__CRS, - dw8250_acpi_walk_resource, p); - if (ACPI_FAILURE(status)) { - dev_err_ratelimited(p->dev, "%s failed \"%s\"\n", __func__, - acpi_format_exception(status)); - return -ENODEV; - } + if (!p->uartclk) + p->uartclk = (unsigned int)id->driver_data; - /* Fix Haswell issue where the clocks do not get enabled */ - if (!strcmp(id->id, "INT33C4") || !strcmp(id->id, "INT33C5")) { - reg = readl(p->membase + LPSS_PRV_CLOCK_PARAMS); - writel(reg | 1, p->membase + LPSS_PRV_CLOCK_PARAMS); - } + up->dma = devm_kzalloc(p->dev, sizeof(*up->dma), GFP_KERNEL); + if (!up->dma) + return -ENOMEM; + + up->dma->rxconf.src_maxburst = p->fifosize / 4; + up->dma->txconf.dst_maxburst = p->fifosize / 4; return 0; } #else -static inline int dw8250_probe_acpi(struct uart_port *p) +static inline int dw8250_probe_acpi(struct uart_8250_port *up) { return -ENODEV; } @@ -266,7 +221,11 @@ static void dw8250_setup_port(struct uart_8250_port *up) p->flags |= UPF_FIXED_TYPE; p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); up->tx_loadsz = p->fifosize; + up->capabilities = UART_CAP_FIFO; } + + if (reg & DW_UART_CPR_AFCE_MODE) + up->capabilities |= UART_CAP_AFE; } static int dw8250_probe(struct platform_device *pdev) @@ -286,17 +245,30 @@ static int dw8250_probe(struct platform_device *pdev) uart.port.mapbase = regs->start; uart.port.irq = irq->start; uart.port.handle_irq = dw8250_handle_irq; + uart.port.pm = dw8250_do_pm; uart.port.type = PORT_8250; uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; uart.port.dev = &pdev->dev; - uart.port.membase = ioremap(regs->start, resource_size(regs)); + uart.port.membase = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); if (!uart.port.membase) return -ENOMEM; + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(data->clk)) { + clk_prepare_enable(data->clk); + uart.port.uartclk = clk_get_rate(data->clk); + } + uart.port.iotype = UPIO_MEM; uart.port.serial_in = dw8250_serial_in; uart.port.serial_out = dw8250_serial_out; + uart.port.private_data = data; dw8250_setup_port(&uart); @@ -305,25 +277,22 @@ static int dw8250_probe(struct platform_device *pdev) if (err) return err; } else if (ACPI_HANDLE(&pdev->dev)) { - err = dw8250_probe_acpi(&uart.port); + err = dw8250_probe_acpi(&uart); if (err) return err; } else { return -ENODEV; } - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - uart.port.private_data = data; - data->line = serial8250_register_8250_port(&uart); if (data->line < 0) return data->line; platform_set_drvdata(pdev, data); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return 0; } @@ -331,34 +300,66 @@ static int dw8250_remove(struct platform_device *pdev) { struct dw8250_data *data = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + serial8250_unregister_port(data->line); + if (!IS_ERR(data->clk)) + clk_disable_unprepare(data->clk); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return 0; } #ifdef CONFIG_PM -static int dw8250_suspend(struct platform_device *pdev, pm_message_t state) +static int dw8250_suspend(struct device *dev) { - struct dw8250_data *data = platform_get_drvdata(pdev); + struct dw8250_data *data = dev_get_drvdata(dev); serial8250_suspend_port(data->line); return 0; } -static int dw8250_resume(struct platform_device *pdev) +static int dw8250_resume(struct device *dev) { - struct dw8250_data *data = platform_get_drvdata(pdev); + struct dw8250_data *data = dev_get_drvdata(dev); serial8250_resume_port(data->line); return 0; } -#else -#define dw8250_suspend NULL -#define dw8250_resume NULL #endif /* CONFIG_PM */ +#ifdef CONFIG_PM_RUNTIME +static int dw8250_runtime_suspend(struct device *dev) +{ + struct dw8250_data *data = dev_get_drvdata(dev); + + if (!IS_ERR(data->clk)) + clk_disable_unprepare(data->clk); + + return 0; +} + +static int dw8250_runtime_resume(struct device *dev) +{ + struct dw8250_data *data = dev_get_drvdata(dev); + + if (!IS_ERR(data->clk)) + clk_prepare_enable(data->clk); + + return 0; +} +#endif + +static const struct dev_pm_ops dw8250_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dw8250_suspend, dw8250_resume) + SET_RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL) +}; + static const struct of_device_id dw8250_of_match[] = { { .compatible = "snps,dw-apb-uart" }, { /* Sentinel */ } @@ -366,8 +367,9 @@ static const struct of_device_id dw8250_of_match[] = { MODULE_DEVICE_TABLE(of, dw8250_of_match); static const struct acpi_device_id dw8250_acpi_match[] = { - { "INT33C4", 100000000 }, - { "INT33C5", 100000000 }, + { "INT33C4", 0 }, + { "INT33C5", 0 }, + { "80860F0A", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); @@ -376,13 +378,12 @@ static struct platform_driver dw8250_platform_driver = { .driver = { .name = "dw-apb-uart", .owner = THIS_MODULE, + .pm = &dw8250_pm_ops, .of_match_table = dw8250_of_match, .acpi_match_table = ACPI_PTR(dw8250_acpi_match), }, .probe = dw8250_probe, .remove = dw8250_remove, - .suspend = dw8250_suspend, - .resume = dw8250_resume, }; module_platform_driver(dw8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index aa76825229d..26e3a97ab15 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1554,6 +1554,7 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 #define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d #define PCI_VENDOR_ID_WCH 0x4348 +#define PCI_DEVICE_ID_WCH_CH352_2S 0x3253 #define PCI_DEVICE_ID_WCH_CH353_4S 0x3453 #define PCI_DEVICE_ID_WCH_CH353_2S1PF 0x5046 #define PCI_DEVICE_ID_WCH_CH353_2S1P 0x7053 @@ -2172,6 +2173,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, + /* WCH CH352 2S card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH352_2S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, /* * ASIX devices with FIFO bug */ @@ -4870,6 +4879,10 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH352_2S, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_b0_bt_2_115200 }, + /* * Commtech, Inc. Fastcom adapters */ diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index b3455a970a1..35d9ab95c5c 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -429,7 +429,6 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { struct uart_8250_port uart; int ret, line, flags = dev_id->driver_data; - struct resource *res = NULL; if (flags & UNKNOWN_DEV) { ret = serial_pnp_guess_board(dev); @@ -440,12 +439,11 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) memset(&uart, 0, sizeof(uart)); if (pnp_irq_valid(dev, 0)) uart.port.irq = pnp_irq(dev, 0); - if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) - res = pnp_get_resource(dev, IORESOURCE_IO, 2); - else if (pnp_port_valid(dev, 0)) - res = pnp_get_resource(dev, IORESOURCE_IO, 0); - if (pnp_resource_enabled(res)) { - uart.port.iobase = res->start; + if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) { + uart.port.iobase = pnp_port_start(dev, 2); + uart.port.iotype = UPIO_PORT; + } else if (pnp_port_valid(dev, 0)) { + uart.port.iobase = pnp_port_start(dev, 0); uart.port.iotype = UPIO_PORT; } else if (pnp_mem_valid(dev, 0)) { uart.port.mapbase = pnp_mem_start(dev, 0); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 2ef9537bcb2..80fe91e64a5 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -33,6 +33,23 @@ config SERIAL_8250 Most people will say Y or M here, so that they can use serial mice, modems and similar devices connecting to the standard serial ports. +config SERIAL_8250_DEPRECATED_OPTIONS + bool "Support 8250_core.* kernel options (DEPRECATED)" + depends on SERIAL_8250 + default y + ---help--- + In 3.7 we renamed 8250 to 8250_core by mistake, so now we have to + accept kernel parameters in both forms like 8250_core.nr_uarts=4 and + 8250.nr_uarts=4. We now renamed the module back to 8250, but if + anybody noticed in 3.7 and changed their userspace we still have to + keep the 8350_core.* options around until they revert the changes + they already did. + + If 8250 is built as a module, this adds 8250_core alias instead. + + If you did not notice yet and/or you have userspace from pre-3.7, it + is safe (and recommended) to say N here. + config SERIAL_8250_PNP bool "8250/16550 PNP device support" if EXPERT depends on SERIAL_8250 && PNP diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index a23838a4d53..36d68d05430 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -2,10 +2,10 @@ # Makefile for the 8250 serial device drivers. # -obj-$(CONFIG_SERIAL_8250) += 8250_core.o -8250_core-y := 8250.o -8250_core-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o -8250_core-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o +obj-$(CONFIG_SERIAL_8250) += 8250.o +8250-y := 8250_core.o +8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o +8250-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index b7d48b34639..1b74b88e1e1 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -852,18 +852,6 @@ static struct pcmcia_driver serial_cs_driver = { .suspend = serial_suspend, .resume = serial_resume, }; - -static int __init init_serial_cs(void) -{ - return pcmcia_register_driver(&serial_cs_driver); -} - -static void __exit exit_serial_cs(void) -{ - pcmcia_unregister_driver(&serial_cs_driver); -} - -module_init(init_serial_cs); -module_exit(exit_serial_cs); +module_pcmcia_driver(serial_cs_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 3ea5408fcbe..e2774f9ecd5 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -29,6 +29,7 @@ * and hooked into this driver. */ + #if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #endif @@ -72,32 +73,44 @@ /* There is by now at least one vendor with differing details, so handle it */ struct vendor_data { unsigned int ifls; - unsigned int fifosize; unsigned int lcrh_tx; unsigned int lcrh_rx; bool oversampling; bool dma_threshold; bool cts_event_workaround; + + unsigned int (*get_fifosize)(unsigned int periphid); }; +static unsigned int get_fifosize_arm(unsigned int periphid) +{ + unsigned int rev = (periphid >> 20) & 0xf; + return rev < 3 ? 16 : 32; +} + static struct vendor_data vendor_arm = { .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, - .fifosize = 16, .lcrh_tx = UART011_LCRH, .lcrh_rx = UART011_LCRH, .oversampling = false, .dma_threshold = false, .cts_event_workaround = false, + .get_fifosize = get_fifosize_arm, }; +static unsigned int get_fifosize_st(unsigned int periphid) +{ + return 64; +} + static struct vendor_data vendor_st = { .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, - .fifosize = 64, .lcrh_tx = ST_UART011_LCRH_TX, .lcrh_rx = ST_UART011_LCRH_RX, .oversampling = true, .dma_threshold = true, .cts_event_workaround = true, + .get_fifosize = get_fifosize_st, }; static struct uart_amba_port *amba_ports[UART_NR]; @@ -117,6 +130,12 @@ struct pl011_dmarx_data { struct pl011_sgbuf sgbuf_b; dma_cookie_t cookie; bool running; + struct timer_list timer; + unsigned int last_residue; + unsigned long last_jiffies; + bool auto_poll_rate; + unsigned int poll_rate; + unsigned int poll_timeout; }; struct pl011_dmatx_data { @@ -223,16 +242,18 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap) static int pl011_sgbuf_init(struct dma_chan *chan, struct pl011_sgbuf *sg, enum dma_data_direction dir) { - sg->buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL); + dma_addr_t dma_addr; + + sg->buf = dma_alloc_coherent(chan->device->dev, + PL011_DMA_BUFFER_SIZE, &dma_addr, GFP_KERNEL); if (!sg->buf) return -ENOMEM; - sg_init_one(&sg->sg, sg->buf, PL011_DMA_BUFFER_SIZE); + sg_init_table(&sg->sg, 1); + sg_set_page(&sg->sg, phys_to_page(dma_addr), + PL011_DMA_BUFFER_SIZE, offset_in_page(dma_addr)); + sg_dma_address(&sg->sg) = dma_addr; - if (dma_map_sg(chan->device->dev, &sg->sg, 1, dir) != 1) { - kfree(sg->buf); - return -EINVAL; - } return 0; } @@ -240,12 +261,13 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg, enum dma_data_direction dir) { if (sg->buf) { - dma_unmap_sg(chan->device->dev, &sg->sg, 1, dir); - kfree(sg->buf); + dma_free_coherent(chan->device->dev, + PL011_DMA_BUFFER_SIZE, sg->buf, + sg_dma_address(&sg->sg)); } } -static void pl011_dma_probe_initcall(struct uart_amba_port *uap) +static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap) { /* DMA is the sole user of the platform data right now */ struct amba_pl011_data *plat = uap->port.dev->platform_data; @@ -259,20 +281,25 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) struct dma_chan *chan; dma_cap_mask_t mask; - /* We need platform data */ - if (!plat || !plat->dma_filter) { - dev_info(uap->port.dev, "no DMA platform data\n"); - return; - } - - /* Try to acquire a generic DMA engine slave TX channel */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); + chan = dma_request_slave_channel(dev, "tx"); - chan = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param); if (!chan) { - dev_err(uap->port.dev, "no TX DMA channel!\n"); - return; + /* We need platform data */ + if (!plat || !plat->dma_filter) { + dev_info(uap->port.dev, "no DMA platform data\n"); + return; + } + + /* Try to acquire a generic DMA engine slave TX channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + chan = dma_request_channel(mask, plat->dma_filter, + plat->dma_tx_param); + if (!chan) { + dev_err(uap->port.dev, "no TX DMA channel!\n"); + return; + } } dmaengine_slave_config(chan, &tx_conf); @@ -282,7 +309,18 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) dma_chan_name(uap->dmatx.chan)); /* Optionally make use of an RX channel as well */ - if (plat->dma_rx_param) { + chan = dma_request_slave_channel(dev, "rx"); + + if (!chan && plat->dma_rx_param) { + chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param); + + if (!chan) { + dev_err(uap->port.dev, "no RX DMA channel!\n"); + return; + } + } + + if (chan) { struct dma_slave_config rx_conf = { .src_addr = uap->port.mapbase + UART01x_DR, .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, @@ -291,15 +329,32 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) .device_fc = false, }; - chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param); - if (!chan) { - dev_err(uap->port.dev, "no RX DMA channel!\n"); - return; - } - dmaengine_slave_config(chan, &rx_conf); uap->dmarx.chan = chan; + if (plat && plat->dma_rx_poll_enable) { + /* Set poll rate if specified. */ + if (plat->dma_rx_poll_rate) { + uap->dmarx.auto_poll_rate = false; + uap->dmarx.poll_rate = plat->dma_rx_poll_rate; + } else { + /* + * 100 ms defaults to poll rate if not + * specified. This will be adjusted with + * the baud rate at set_termios. + */ + uap->dmarx.auto_poll_rate = true; + uap->dmarx.poll_rate = 100; + } + /* 3 secs defaults poll_timeout if not specified. */ + if (plat->dma_rx_poll_timeout) + uap->dmarx.poll_timeout = + plat->dma_rx_poll_timeout; + else + uap->dmarx.poll_timeout = 3000; + } else + uap->dmarx.auto_poll_rate = false; + dev_info(uap->port.dev, "DMA channel RX %s\n", dma_chan_name(uap->dmarx.chan)); } @@ -315,6 +370,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) struct dma_uap { struct list_head node; struct uart_amba_port *uap; + struct device *dev; }; static LIST_HEAD(pl011_dma_uarts); @@ -325,7 +381,7 @@ static int __init pl011_dma_initcall(void) list_for_each_safe(node, tmp, &pl011_dma_uarts) { struct dma_uap *dmau = list_entry(node, struct dma_uap, node); - pl011_dma_probe_initcall(dmau->uap); + pl011_dma_probe_initcall(dmau->dev, dmau->uap); list_del(node); kfree(dmau); } @@ -334,18 +390,19 @@ static int __init pl011_dma_initcall(void) device_initcall(pl011_dma_initcall); -static void pl011_dma_probe(struct uart_amba_port *uap) +static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap) { struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL); if (dmau) { dmau->uap = uap; + dmau->dev = dev; list_add_tail(&dmau->node, &pl011_dma_uarts); } } #else -static void pl011_dma_probe(struct uart_amba_port *uap) +static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap) { - pl011_dma_probe_initcall(uap); + pl011_dma_probe_initcall(dev, uap); } #endif @@ -701,24 +758,30 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, struct tty_port *port = &uap->port.state->port; struct pl011_sgbuf *sgbuf = use_buf_b ? &uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a; - struct device *dev = uap->dmarx.chan->device->dev; int dma_count = 0; u32 fifotaken = 0; /* only used for vdbg() */ - /* Pick everything from the DMA first */ + struct pl011_dmarx_data *dmarx = &uap->dmarx; + int dmataken = 0; + + if (uap->dmarx.poll_rate) { + /* The data can be taken by polling */ + dmataken = sgbuf->sg.length - dmarx->last_residue; + /* Recalculate the pending size */ + if (pending >= dmataken) + pending -= dmataken; + } + + /* Pick the remain data from the DMA */ if (pending) { - /* Sync in buffer */ - dma_sync_sg_for_cpu(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE); /* * First take all chars in the DMA pipe, then look in the FIFO. * Note that tty_insert_flip_buf() tries to take as many chars * as it can. */ - dma_count = tty_insert_flip_string(port, sgbuf->buf, pending); - - /* Return buffer to device */ - dma_sync_sg_for_device(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE); + dma_count = tty_insert_flip_string(port, sgbuf->buf + dmataken, + pending); uap->port.icount.rx += dma_count; if (dma_count < pending) @@ -726,6 +789,10 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, "couldn't insert all characters (TTY is full?)\n"); } + /* Reset the last_residue for Rx DMA poll */ + if (uap->dmarx.poll_rate) + dmarx->last_residue = sgbuf->sg.length; + /* * Only continue with trying to read the FIFO if all DMA chars have * been taken first. @@ -865,6 +932,57 @@ static inline void pl011_dma_rx_stop(struct uart_amba_port *uap) writew(uap->dmacr, uap->port.membase + UART011_DMACR); } +/* + * Timer handler for Rx DMA polling. + * Every polling, It checks the residue in the dma buffer and transfer + * data to the tty. Also, last_residue is updated for the next polling. + */ +static void pl011_dma_rx_poll(unsigned long args) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)args; + struct tty_port *port = &uap->port.state->port; + struct pl011_dmarx_data *dmarx = &uap->dmarx; + struct dma_chan *rxchan = uap->dmarx.chan; + unsigned long flags = 0; + unsigned int dmataken = 0; + unsigned int size = 0; + struct pl011_sgbuf *sgbuf; + int dma_count; + struct dma_tx_state state; + + sgbuf = dmarx->use_buf_b ? &uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a; + rxchan->device->device_tx_status(rxchan, dmarx->cookie, &state); + if (likely(state.residue < dmarx->last_residue)) { + dmataken = sgbuf->sg.length - dmarx->last_residue; + size = dmarx->last_residue - state.residue; + dma_count = tty_insert_flip_string(port, sgbuf->buf + dmataken, + size); + if (dma_count == size) + dmarx->last_residue = state.residue; + dmarx->last_jiffies = jiffies; + } + tty_flip_buffer_push(port); + + /* + * If no data is received in poll_timeout, the driver will fall back + * to interrupt mode. We will retrigger DMA at the first interrupt. + */ + if (jiffies_to_msecs(jiffies - dmarx->last_jiffies) + > uap->dmarx.poll_timeout) { + + spin_lock_irqsave(&uap->port.lock, flags); + pl011_dma_rx_stop(uap); + spin_unlock_irqrestore(&uap->port.lock, flags); + + uap->dmarx.running = false; + dmaengine_terminate_all(rxchan); + del_timer(&uap->dmarx.timer); + } else { + mod_timer(&uap->dmarx.timer, + jiffies + msecs_to_jiffies(uap->dmarx.poll_rate)); + } +} + static void pl011_dma_startup(struct uart_amba_port *uap) { int ret; @@ -927,6 +1045,16 @@ skip_rx: if (pl011_dma_rx_trigger_dma(uap)) dev_dbg(uap->port.dev, "could not trigger initial " "RX DMA job, fall back to interrupt mode\n"); + if (uap->dmarx.poll_rate) { + init_timer(&(uap->dmarx.timer)); + uap->dmarx.timer.function = pl011_dma_rx_poll; + uap->dmarx.timer.data = (unsigned long)uap; + mod_timer(&uap->dmarx.timer, + jiffies + + msecs_to_jiffies(uap->dmarx.poll_rate)); + uap->dmarx.last_residue = PL011_DMA_BUFFER_SIZE; + uap->dmarx.last_jiffies = jiffies; + } } } @@ -962,6 +1090,8 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) /* Clean up the RX DMA */ pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_a, DMA_FROM_DEVICE); pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_b, DMA_FROM_DEVICE); + if (uap->dmarx.poll_rate) + del_timer_sync(&uap->dmarx.timer); uap->using_rx_dma = false; } } @@ -976,10 +1106,9 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) return uap->using_rx_dma && uap->dmarx.running; } - #else /* Blank functions if the DMA engine is not available */ -static inline void pl011_dma_probe(struct uart_amba_port *uap) +static inline void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap) { } @@ -1088,8 +1217,20 @@ static void pl011_rx_chars(struct uart_amba_port *uap) dev_dbg(uap->port.dev, "could not trigger RX DMA job " "fall back to interrupt mode again\n"); uap->im |= UART011_RXIM; - } else + } else { uap->im &= ~UART011_RXIM; +#ifdef CONFIG_DMA_ENGINE + /* Start Rx DMA poll */ + if (uap->dmarx.poll_rate) { + uap->dmarx.last_jiffies = jiffies; + uap->dmarx.last_residue = PL011_DMA_BUFFER_SIZE; + mod_timer(&uap->dmarx.timer, + jiffies + + msecs_to_jiffies(uap->dmarx.poll_rate)); + } +#endif + } + writew(uap->im, uap->port.membase + UART011_IMSC); } spin_lock(&uap->port.lock); @@ -1164,7 +1305,6 @@ static irqreturn_t pl011_int(int irq, void *dev_id) unsigned int dummy_read; spin_lock_irqsave(&uap->port.lock, flags); - status = readw(uap->port.membase + UART011_MIS); if (status) { do { @@ -1551,6 +1691,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, */ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / clkdiv); +#ifdef CONFIG_DMA_ENGINE + /* + * Adjust RX DMA polling rate with baud rate if not specified. + */ + if (uap->dmarx.auto_poll_rate) + uap->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud); +#endif if (baud > port->uartclk/16) quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); @@ -2010,7 +2157,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; uap->old_cr = 0; - uap->fifosize = vendor->fifosize; + uap->fifosize = vendor->get_fifosize(dev->periphid); uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; @@ -2020,7 +2167,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->port.ops = &amba_pl011_pops; uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = i; - pl011_dma_probe(uap); + pl011_dma_probe(&dev->dev, uap); /* Ensure interrupts from this UART are masked and cleared */ writew(0, uap->port.membase + UART011_IMSC); diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index d97e194b6bc..cbf1d155b7b 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -162,7 +162,7 @@ static unsigned int arc_serial_tx_empty(struct uart_port *port) /* * Driver internal routine, used by both tty(serial core) as well as tx-isr * -Called under spinlock in either cases - * -also tty->stopped / tty->hw_stopped has already been checked + * -also tty->stopped has already been checked * = by uart_start( ) before calling us * = tx_ist checks that too before calling */ diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index d4a7c241b75..3467462869c 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -158,7 +158,7 @@ struct atmel_uart_port { }; static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; -static unsigned long atmel_ports_in_use; +static DECLARE_BITMAP(atmel_ports_in_use, ATMEL_MAX_UART); #ifdef SUPPORT_SYSRQ static struct console atmel_console; @@ -1769,15 +1769,14 @@ static int atmel_serial_probe(struct platform_device *pdev) if (ret < 0) /* port id not found in platform data nor device-tree aliases: * auto-enumerate it */ - ret = find_first_zero_bit(&atmel_ports_in_use, - sizeof(atmel_ports_in_use)); + ret = find_first_zero_bit(atmel_ports_in_use, ATMEL_MAX_UART); - if (ret > ATMEL_MAX_UART) { + if (ret >= ATMEL_MAX_UART) { ret = -ENODEV; goto err; } - if (test_and_set_bit(ret, &atmel_ports_in_use)) { + if (test_and_set_bit(ret, atmel_ports_in_use)) { /* port already in use */ ret = -EBUSY; goto err; @@ -1857,7 +1856,7 @@ static int atmel_serial_remove(struct platform_device *pdev) /* "port" is allocated statically, so we shouldn't free it */ - clear_bit(port->line, &atmel_ports_in_use); + clear_bit(port->line, atmel_ports_in_use); clk_put(atmel_port->clk); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 52a3ecd4042..6fa2ae77fff 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -30,7 +30,6 @@ #include <linux/serial.h> #include <linux/serial_core.h> -#include <bcm63xx_clk.h> #include <bcm63xx_irq.h> #include <bcm63xx_regs.h> #include <bcm63xx_io.h> diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 12dceda9db3..26a3be7ced7 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -1011,24 +1011,6 @@ static int bfin_serial_poll_get_char(struct uart_port *port) } #endif -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) -static void bfin_kgdboc_port_shutdown(struct uart_port *port) -{ - if (kgdboc_break_enabled) { - kgdboc_break_enabled = 0; - bfin_serial_shutdown(port); - } -} - -static int bfin_kgdboc_port_startup(struct uart_port *port) -{ - kgdboc_port_line = port->line; - kgdboc_break_enabled = !bfin_serial_startup(port); - return 0; -} -#endif - static struct uart_ops bfin_serial_pops = { .tx_empty = bfin_serial_tx_empty, .set_mctrl = bfin_serial_set_mctrl, @@ -1047,11 +1029,6 @@ static struct uart_ops bfin_serial_pops = { .request_port = bfin_serial_request_port, .config_port = bfin_serial_config_port, .verify_port = bfin_serial_verify_port, -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - .kgdboc_port_startup = bfin_kgdboc_port_startup, - .kgdboc_port_shutdown = bfin_kgdboc_port_shutdown, -#endif #ifdef CONFIG_CONSOLE_POLL .poll_put_char = bfin_serial_poll_put_char, .poll_get_char = bfin_serial_poll_get_char, diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 5f37c31e32b..477f22f773f 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -169,7 +169,6 @@ static int get_lsr_info(struct e100_serial *info, unsigned int *value); #define DEF_BAUD 115200 /* 115.2 kbit/s */ -#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) #define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */ /* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */ #define DEF_TX 0x80 /* or SERIAL_CTRL_B */ @@ -246,7 +245,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH7_FIRST, .icmdadr = R_DMA_CH7_CMD, .idescradr = R_DMA_CH7_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 2, @@ -300,7 +298,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH9_FIRST, .icmdadr = R_DMA_CH9_CMD, .idescradr = R_DMA_CH9_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 3, @@ -356,7 +353,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH3_FIRST, .icmdadr = R_DMA_CH3_CMD, .idescradr = R_DMA_CH3_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 0, @@ -410,7 +406,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH5_FIRST, .icmdadr = R_DMA_CH5_CMD, .idescradr = R_DMA_CH5_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 1, @@ -2263,8 +2258,7 @@ TODO: The break will be delayed until an F or V character is received. */ -static -struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info) +static void handle_ser_rx_interrupt_no_dma(struct e100_serial *info) { unsigned long data_read; @@ -2370,10 +2364,9 @@ more_data: } tty_flip_buffer_push(&info->port); - return info; } -static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) +static void handle_ser_rx_interrupt(struct e100_serial *info) { unsigned char rstat; @@ -2382,7 +2375,8 @@ static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) #endif /* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */ if (!info->uses_dma_in) { - return handle_ser_rx_interrupt_no_dma(info); + handle_ser_rx_interrupt_no_dma(info); + return; } /* DMA is used */ rstat = info->ioport[REG_STATUS]; @@ -2489,7 +2483,6 @@ static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) /* Restarting the DMA never hurts */ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); START_FLUSH_FAST_TIMER(info, "ser_int"); - return info; } /* handle_ser_rx_interrupt */ static void handle_ser_tx_interrupt(struct e100_serial *info) @@ -2534,8 +2527,7 @@ static void handle_ser_tx_interrupt(struct e100_serial *info) } /* Normal char-by-char interrupt */ if (info->xmit.head == info->xmit.tail - || info->port.tty->stopped - || info->port.tty->hw_stopped) { + || info->port.tty->stopped) { DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n", info->port.tty->stopped)); e100_disable_serial_tx_ready_irq(info); @@ -2722,7 +2714,7 @@ startup(struct e100_serial * info) /* if it was already initialized, skip this */ - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { local_irq_restore(flags); free_page(xmit_page); return 0; @@ -2847,7 +2839,7 @@ startup(struct e100_serial * info) #endif /* CONFIG_SVINTO_SIM */ - info->flags |= ASYNC_INITIALIZED; + info->port.flags |= ASYNC_INITIALIZED; local_irq_restore(flags); return 0; @@ -2892,7 +2884,7 @@ shutdown(struct e100_serial * info) #endif /* CONFIG_SVINTO_SIM */ - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) return; #ifdef SERIAL_DEBUG_OPEN @@ -2923,7 +2915,7 @@ shutdown(struct e100_serial * info) if (info->port.tty) set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + info->port.flags &= ~ASYNC_INITIALIZED; local_irq_restore(flags); } @@ -2948,7 +2940,7 @@ change_speed(struct e100_serial *info) /* possibly, the tx/rx should be disabled first to do this safely */ /* change baud-rate and write it to the hardware */ - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { /* Special baudrate */ u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */ unsigned long alt_source = @@ -3098,7 +3090,6 @@ rs_flush_chars(struct tty_struct *tty) if (info->tr_running || info->xmit.head == info->xmit.tail || tty->stopped || - tty->hw_stopped || !info->xmit.buf) return; @@ -3176,7 +3167,6 @@ static int rs_raw_write(struct tty_struct *tty, if (info->xmit.head != info->xmit.tail && !tty->stopped && - !tty->hw_stopped && !info->tr_running) { start_transmit(info); } @@ -3400,10 +3390,10 @@ get_serial_info(struct e100_serial * info, tmp.line = info->line; tmp.port = (int)info->ioport; tmp.irq = info->irq; - tmp.flags = info->flags; + tmp.flags = info->port.flags; tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; + tmp.close_delay = info->port.close_delay; + tmp.closing_wait = info->port.closing_wait; tmp.custom_divisor = info->custom_divisor; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -3425,16 +3415,16 @@ set_serial_info(struct e100_serial *info, if (!capable(CAP_SYS_ADMIN)) { if ((new_serial.type != info->type) || - (new_serial.close_delay != info->close_delay) || + (new_serial.close_delay != info->port.close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != - (info->flags & ~ASYNC_USR_MASK))) + (info->port.flags & ~ASYNC_USR_MASK))) return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | + info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); goto check_and_exit; } - if (info->count > 1) + if (info->port.count > 1) return -EBUSY; /* @@ -3443,16 +3433,16 @@ set_serial_info(struct e100_serial *info, */ info->baud_base = new_serial.baud_base; - info->flags = ((info->flags & ~ASYNC_FLAGS) | + info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->custom_divisor = new_serial.custom_divisor; info->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; - info->port.low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + info->port.close_delay = new_serial.close_delay; + info->port.closing_wait = new_serial.closing_wait; + info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { change_speed(info); } else retval = startup(info); @@ -3733,10 +3723,8 @@ rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; + !(tty->termios.c_cflag & CRTSCTS)) rs_start(tty); - } } @@ -3772,7 +3760,7 @@ rs_close(struct tty_struct *tty, struct file * filp) printk("[%d] rs_close ttyS%d, count = %d\n", current->pid, info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((tty->count == 1) && (info->port.count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -3782,32 +3770,32 @@ rs_close(struct tty_struct *tty, struct file * filp) */ printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; + "info->count is %d\n", info->port.count); + info->port.count = 1; } - if (--info->count < 0) { + if (--info->port.count < 0) { printk(KERN_ERR "rs_close: bad serial port count for ttyS%d: %d\n", - info->line, info->count); - info->count = 0; + info->line, info->port.count); + info->port.count = 0; } - if (info->count) { + if (info->port.count) { local_irq_restore(flags); return; } - info->flags |= ASYNC_CLOSING; + info->port.flags |= ASYNC_CLOSING; /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ - if (info->flags & ASYNC_NORMAL_ACTIVE) + if (info->port.flags & ASYNC_NORMAL_ACTIVE) info->normal_termios = tty->termios; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->port.closing_wait); /* * At this point we stop accepting input. To do this, we * disable the serial receiver and the DMA receive interrupt. @@ -3820,7 +3808,7 @@ rs_close(struct tty_struct *tty, struct file * filp) e100_disable_rx(info); e100_disable_rx_irq(info); - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially @@ -3836,13 +3824,13 @@ rs_close(struct tty_struct *tty, struct file * filp) tty->closing = 0; info->event = 0; info->port.tty = NULL; - if (info->blocked_open) { - if (info->close_delay) - schedule_timeout_interruptible(info->close_delay); - wake_up_interruptible(&info->open_wait); + if (info->port.blocked_open) { + if (info->port.close_delay) + schedule_timeout_interruptible(info->port.close_delay); + wake_up_interruptible(&info->port.open_wait); } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&info->port.close_wait); local_irq_restore(flags); /* port closed */ @@ -3935,10 +3923,10 @@ rs_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(info); info->event = 0; - info->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.count = 0; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->port.tty = NULL; - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } /* @@ -3960,11 +3948,11 @@ block_til_ready(struct tty_struct *tty, struct file * filp, * until it's done, and then try again. */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(tty, info->close_wait, - !(info->flags & ASYNC_CLOSING)); + (info->port.flags & ASYNC_CLOSING)) { + wait_event_interruptible_tty(tty, info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -3979,7 +3967,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -3990,23 +3978,23 @@ block_til_ready(struct tty_struct *tty, struct file * filp, /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that + * this loop, info->port.count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->port.open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttyS%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif local_irq_save(flags); if (!tty_hung_up_p(filp)) { extra_count++; - info->count--; + info->port.count--; } local_irq_restore(flags); - info->blocked_open++; + info->port.blocked_open++; while (1) { local_irq_save(flags); /* assert RTS and DTR */ @@ -4015,9 +4003,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, local_irq_restore(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { + !(info->port.flags & ASYNC_INITIALIZED)) { #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -4026,7 +4014,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, #endif break; } - if (!(info->flags & ASYNC_CLOSING) && do_clocal) + if (!(info->port.flags & ASYNC_CLOSING) && do_clocal) /* && (do_clocal || DCD_IS_ASSERTED) */ break; if (signal_pending(current)) { @@ -4035,24 +4023,24 @@ block_til_ready(struct tty_struct *tty, struct file * filp, } #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: ttyS%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif tty_unlock(tty); schedule(); tty_lock(tty); } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); if (extra_count) - info->count++; - info->blocked_open--; + info->port.count++; + info->port.blocked_open--; #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: ttyS%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif if (retval) return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -4086,24 +4074,24 @@ rs_open(struct tty_struct *tty, struct file * filp) #ifdef SERIAL_DEBUG_OPEN printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name, - info->count); + info->port.count); #endif - info->count++; + info->port.count++; tty->driver_data = info; info->port.tty = tty; - info->port.low_latency = !!(info->flags & ASYNC_LOW_LATENCY); + info->port.low_latency = !!(info->port.flags & ASYNC_LOW_LATENCY); /* * If the port is in the middle of closing, bail out now */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(tty, info->close_wait, - !(info->flags & ASYNC_CLOSING)); + (info->port.flags & ASYNC_CLOSING)) { + wait_event_interruptible_tty(tty, info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_HUP_NOTIFY) ? + return ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; @@ -4113,7 +4101,7 @@ rs_open(struct tty_struct *tty, struct file * filp) /* * If DMA is enabled try to allocate the irq's. */ - if (info->count == 1) { + if (info->port.count == 1) { allocated_resources = 1; if (info->dma_in_enabled) { if (request_irq(info->dma_in_irq_nbr, @@ -4186,7 +4174,7 @@ rs_open(struct tty_struct *tty, struct file * filp) if (allocated_resources) deinit_port(info); - /* FIXME Decrease count info->count here too? */ + /* FIXME Decrease count info->port.count here too? */ return retval; } @@ -4203,7 +4191,7 @@ rs_open(struct tty_struct *tty, struct file * filp) return retval; } - if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if ((info->port.count == 1) && (info->port.flags & ASYNC_SPLIT_TERMIOS)) { tty->termios = info->normal_termios; change_speed(info); } @@ -4256,9 +4244,6 @@ static void seq_line_info(struct seq_file *m, struct e100_serial *info) if (info->port.tty->stopped) seq_printf(m, " stopped:%i", (int)info->port.tty->stopped); - if (info->port.tty->hw_stopped) - seq_printf(m, " hw_stopped:%i", - (int)info->port.tty->hw_stopped); } { @@ -4455,16 +4440,9 @@ static int __init rs_init(void) info->forced_eop = 0; info->baud_base = DEF_BAUD_BASE; info->custom_divisor = 0; - info->flags = 0; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; info->x_char = 0; info->event = 0; - info->count = 0; - info->blocked_open = 0; info->normal_termios = driver->init_termios; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); info->xmit.buf = NULL; info->xmit.tail = info->xmit.head = 0; info->first_recv_buffer = info->last_recv_buffer = NULL; diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h index ea0beb46a10..7599014ae03 100644 --- a/drivers/tty/serial/crisv10.h +++ b/drivers/tty/serial/crisv10.h @@ -53,8 +53,6 @@ struct e100_serial { volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */ volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */ - int flags; /* defined in tty.h */ - u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */ u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */ u8 iseteop; /* bit number for R_SET_EOP for the input dma */ @@ -88,19 +86,10 @@ struct e100_serial { volatile int tr_running; /* 1 if output is running */ - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; int x_char; /* xon/xoff character */ - int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; unsigned long event; - unsigned long last_active; int line; int type; /* PORT_ETRAX */ - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ struct circ_buf xmit; struct etrax_recv_buffer *first_recv_buffer; struct etrax_recv_buffer *last_recv_buffer; @@ -110,9 +99,6 @@ struct e100_serial { struct work_struct work; struct async_icount icount; /* error-statistics etc.*/ struct ktermios normal_termios; - struct ktermios callout_termios; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; unsigned long char_time_usec; /* The time for 1 char, in usecs */ unsigned long flush_time_usec; /* How often we should flush */ diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index bc9e6b017b0..18ed5aebb16 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -1415,8 +1415,7 @@ static int icom_alloc_adapter(struct icom_adapter struct icom_adapter *cur_adapter_entry; struct list_head *tmp; - icom_adapter = (struct icom_adapter *) - kzalloc(sizeof(struct icom_adapter), GFP_KERNEL); + icom_adapter = kzalloc(sizeof(struct icom_adapter), GFP_KERNEL); if (!icom_adapter) { return -ENOMEM; diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 68d7ce997ed..8b1534c424a 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -270,23 +270,6 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev) } /** - * ifx_spi_hangup - hang up an IFX device - * @ifx_dev: our SPI device - * - * Hang up the tty attached to the IFX device if one is currently - * open. If not take no action - */ -static void ifx_spi_ttyhangup(struct ifx_spi_device *ifx_dev) -{ - struct tty_port *pport = &ifx_dev->tty_port; - struct tty_struct *tty = tty_port_tty_get(pport); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } -} - -/** * ifx_spi_timeout - SPI timeout * @arg: our SPI device * @@ -298,7 +281,7 @@ static void ifx_spi_timeout(unsigned long arg) struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)arg; dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***"); - ifx_spi_ttyhangup(ifx_dev); + tty_port_tty_hangup(&ifx_dev->tty_port, false); mrdy_set_low(ifx_dev); clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); } @@ -443,25 +426,6 @@ static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count, } /** - * ifx_spi_wakeup_serial - SPI space made - * @port_data: our SPI device - * - * We have emptied the FIFO enough that we want to get more data - * queued into it. Poke the line discipline via tty_wakeup so that - * it will feed us more bits - */ -static void ifx_spi_wakeup_serial(struct ifx_spi_device *ifx_dev) -{ - struct tty_struct *tty; - - tty = tty_port_tty_get(&ifx_dev->tty_port); - if (!tty) - return; - tty_wakeup(tty); - tty_kref_put(tty); -} - -/** * ifx_spi_prepare_tx_buffer - prepare transmit frame * @ifx_dev: our SPI device * @@ -506,7 +470,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) tx_count += temp_count; if (temp_count == queue_length) /* poke port to get more data */ - ifx_spi_wakeup_serial(ifx_dev); + tty_port_tty_wakeup(&ifx_dev->tty_port); else /* more data in port, use next SPI message */ ifx_dev->spi_more = 1; } @@ -683,8 +647,6 @@ static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev, static void ifx_spi_complete(void *ctx) { struct ifx_spi_device *ifx_dev = ctx; - struct tty_struct *tty; - struct tty_ldisc *ldisc = NULL; int length; int actual_length; unsigned char more; @@ -762,15 +724,7 @@ complete_exit: */ ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_DATA_PENDING); - tty = tty_port_tty_get(&ifx_dev->tty_port); - if (tty) { - ldisc = tty_ldisc_ref(tty); - if (ldisc) { - ldisc->ops->write_wakeup(tty); - tty_ldisc_deref(ldisc); - } - tty_kref_put(tty); - } + tty_port_tty_wakeup(&ifx_dev->tty_port); } } } @@ -962,7 +916,7 @@ static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev) set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); if (!solreset) { /* unsolicited reset */ - ifx_spi_ttyhangup(ifx_dev); + tty_port_tty_hangup(&ifx_dev->tty_port, false); } } else { /* exited reset */ @@ -1325,30 +1279,6 @@ static void ifx_spi_spi_shutdown(struct spi_device *spi) */ /** - * ifx_spi_spi_suspend - suspend SPI on system suspend - * @dev: device being suspended - * - * Suspend the SPI side. No action needed on Intel MID platforms, may - * need extending for other systems. - */ -static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg) -{ - return 0; -} - -/** - * ifx_spi_spi_resume - resume SPI side on system resume - * @dev: device being suspended - * - * Suspend the SPI side. No action needed on Intel MID platforms, may - * need extending for other systems. - */ -static int ifx_spi_spi_resume(struct spi_device *spi) -{ - return 0; -} - -/** * ifx_spi_pm_suspend - suspend modem on system suspend * @dev: device being suspended * @@ -1437,8 +1367,6 @@ static struct spi_driver ifx_spi_driver = { .probe = ifx_spi_spi_probe, .shutdown = ifx_spi_spi_shutdown, .remove = ifx_spi_spi_remove, - .suspend = ifx_spi_spi_suspend, - .resume = ifx_spi_spi_resume, .id_table = ifx_id_table }; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 147c9e19359..8cdfbd36589 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -761,6 +761,8 @@ static int imx_startup(struct uart_port *port) temp = readl(sport->port.membase + UCR2); temp |= (UCR2_RXEN | UCR2_TXEN); + if (!sport->have_rtscts) + temp |= UCR2_IRTS; writel(temp, sport->port.membase + UCR2); if (USE_IRDA(sport)) { diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 00f250ae14c..27bb75070c9 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -596,12 +596,6 @@ void jsm_input(struct jsm_channel *ch) jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n"); - if (data_len <= 0) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - jsm_dbg(READ, &ch->ch_bd->pci_dev, "jsm_input 1\n"); - return; - } - len = tty_buffer_request_room(port, data_len); n = len; diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 32517d4bcea..35866d5872a 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -778,7 +778,7 @@ static int max3100_probe(struct spi_device *spi) max3100s[i]->spi = spi; max3100s[i]->irq = spi->irq; spin_lock_init(&max3100s[i]->conf_lock); - dev_set_drvdata(&spi->dev, max3100s[i]); + spi_set_drvdata(spi, max3100s[i]); pdata = spi->dev.platform_data; max3100s[i]->crystal = pdata->crystal; max3100s[i]->loopback = pdata->loopback; @@ -819,7 +819,7 @@ static int max3100_probe(struct spi_device *spi) static int max3100_remove(struct spi_device *spi) { - struct max3100_port *s = dev_get_drvdata(&spi->dev); + struct max3100_port *s = spi_get_drvdata(spi); int i; mutex_lock(&max3100s_lock); @@ -849,11 +849,11 @@ static int max3100_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP -static int max3100_suspend(struct spi_device *spi, pm_message_t state) +static int max3100_suspend(struct device *dev) { - struct max3100_port *s = dev_get_drvdata(&spi->dev); + struct max3100_port *s = dev_get_drvdata(dev); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -874,9 +874,9 @@ static int max3100_suspend(struct spi_device *spi, pm_message_t state) return 0; } -static int max3100_resume(struct spi_device *spi) +static int max3100_resume(struct device *dev) { - struct max3100_port *s = dev_get_drvdata(&spi->dev); + struct max3100_port *s = dev_get_drvdata(dev); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -894,21 +894,21 @@ static int max3100_resume(struct spi_device *spi) return 0; } +static SIMPLE_DEV_PM_OPS(max3100_pm_ops, max3100_suspend, max3100_resume); +#define MAX3100_PM_OPS (&max3100_pm_ops) + #else -#define max3100_suspend NULL -#define max3100_resume NULL +#define MAX3100_PM_OPS NULL #endif static struct spi_driver max3100_driver = { .driver = { .name = "max3100", .owner = THIS_MODULE, + .pm = MAX3100_PM_OPS, }, - .probe = max3100_probe, .remove = max3100_remove, - .suspend = max3100_suspend, - .resume = max3100_resume, }; module_spi_driver(max3100_driver); diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 0c2422cb04e..8941e641894 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -881,12 +881,14 @@ static struct uart_ops max310x_ops = { .verify_port = max310x_verify_port, }; -static int max310x_suspend(struct spi_device *spi, pm_message_t state) +#ifdef CONFIG_PM_SLEEP + +static int max310x_suspend(struct device *dev) { int ret; - struct max310x_port *s = dev_get_drvdata(&spi->dev); + struct max310x_port *s = dev_get_drvdata(dev); - dev_dbg(&spi->dev, "Suspend\n"); + dev_dbg(dev, "Suspend\n"); ret = uart_suspend_port(&s->uart, &s->port); @@ -905,11 +907,11 @@ static int max310x_suspend(struct spi_device *spi, pm_message_t state) return ret; } -static int max310x_resume(struct spi_device *spi) +static int max310x_resume(struct device *dev) { - struct max310x_port *s = dev_get_drvdata(&spi->dev); + struct max310x_port *s = dev_get_drvdata(dev); - dev_dbg(&spi->dev, "Resume\n"); + dev_dbg(dev, "Resume\n"); if (s->pdata->suspend) s->pdata->suspend(0); @@ -928,6 +930,13 @@ static int max310x_resume(struct spi_device *spi) return uart_resume_port(&s->uart, &s->port); } +static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume); +#define MAX310X_PM_OPS (&max310x_pm_ops) + +#else +#define MAX310X_PM_OPS NULL +#endif + #ifdef CONFIG_GPIOLIB static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) { @@ -1242,11 +1251,10 @@ static struct spi_driver max310x_driver = { .driver = { .name = "max310x", .owner = THIS_MODULE, + .pm = MAX310X_PM_OPS, }, .probe = max310x_probe, .remove = max310x_remove, - .suspend = max310x_suspend, - .resume = max310x_resume, .id_table = max310x_id_table, }; module_spi_driver(max310x_driver); diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index e956377a38f..65be0c00c4b 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -707,8 +707,10 @@ static int __init mcf_init(void) if (rc) return rc; rc = platform_driver_register(&mcf_platform_driver); - if (rc) + if (rc) { + uart_unregister_driver(&mcf_driver); return rc; + } return 0; } diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 018bad92255..f51b280f3bf 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1497,18 +1497,23 @@ mpc52xx_uart_init(void) if (psc_ops && psc_ops->fifoc_init) { ret = psc_ops->fifoc_init(); if (ret) - return ret; + goto err_init; } ret = platform_driver_register(&mpc52xx_uart_of_driver); if (ret) { printk(KERN_ERR "%s: platform_driver_register failed (%i)\n", __FILE__, ret); - uart_unregister_driver(&mpc52xx_uart_driver); - return ret; + goto err_reg; } return 0; +err_reg: + if (psc_ops && psc_ops->fifoc_uninit) + psc_ops->fifoc_uninit(); +err_init: + uart_unregister_driver(&mpc52xx_uart_driver); + return ret; } static void __exit diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index f641c232bec..9b6ef20420c 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -743,9 +743,10 @@ static struct uart_driver serial_m3110_reg = { .cons = &serial_m3110_console, }; -#ifdef CONFIG_PM -static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int serial_m3110_suspend(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct uart_max3110 *max = spi_get_drvdata(spi); disable_irq(max->irq); @@ -754,8 +755,9 @@ static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) return 0; } -static int serial_m3110_resume(struct spi_device *spi) +static int serial_m3110_resume(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct uart_max3110 *max = spi_get_drvdata(spi); max3110_out(max, max->cur_conf); @@ -763,9 +765,13 @@ static int serial_m3110_resume(struct spi_device *spi) enable_irq(max->irq); return 0; } + +static SIMPLE_DEV_PM_OPS(serial_m3110_pm_ops, serial_m3110_suspend, + serial_m3110_resume); +#define SERIAL_M3110_PM_OPS (&serial_m3110_pm_ops) + #else -#define serial_m3110_suspend NULL -#define serial_m3110_resume NULL +#define SERIAL_M3110_PM_OPS NULL #endif static int serial_m3110_probe(struct spi_device *spi) @@ -872,11 +878,10 @@ static struct spi_driver uart_max3110_driver = { .driver = { .name = "spi_max3111", .owner = THIS_MODULE, + .pm = SERIAL_M3110_PM_OPS, }, .probe = serial_m3110_probe, .remove = serial_m3110_remove, - .suspend = serial_m3110_suspend, - .resume = serial_m3110_resume, }; static int __init serial_m3110_init(void) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 4a942c78347..4ca2f64861e 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -907,7 +907,6 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, unsigned int error_f = 0; unsigned long flags; unsigned int flush; - struct tty_struct *tty; struct tty_port *port; struct uart_port *uport; struct msm_hs_port *msm_uport; @@ -919,7 +918,6 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, clk_enable(msm_uport->clk); port = &uport->state->port; - tty = port->tty; msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); diff --git a/drivers/tty/serial/msm_smd_tty.c b/drivers/tty/serial/msm_smd_tty.c index e722ff163d9..1238ac370bf 100644 --- a/drivers/tty/serial/msm_smd_tty.c +++ b/drivers/tty/serial/msm_smd_tty.c @@ -90,13 +90,13 @@ static void smd_tty_notify(void *priv, unsigned event) static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty) { + struct smd_tty_info *info = container_of(tport, struct smd_tty_info, + port); int i, res = 0; - int n = tty->index; const char *name = NULL; - struct smd_tty_info *info = smd_tty + n; for (i = 0; i < smd_tty_channels_len; i++) { - if (smd_tty_channels[i].id == n) { + if (smd_tty_channels[i].id == tty->index) { name = smd_tty_channels[i].name; break; } @@ -117,17 +117,13 @@ static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty) static void smd_tty_port_shutdown(struct tty_port *tport) { - struct smd_tty_info *info; - struct tty_struct *tty = tty_port_tty_get(tport); + struct smd_tty_info *info = container_of(tport, struct smd_tty_info, + port); - info = tty->driver_data; if (info->ch) { smd_close(info->ch); info->ch = 0; } - - tty->driver_data = 0; - tty_kref_put(tty); } static int smd_tty_open(struct tty_struct *tty, struct file *f) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index d549fe1fa42..4f5f161896a 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -35,7 +35,7 @@ #include <linux/pinctrl/consumer.h> #include <linux/of_device.h> #include <linux/dma-mapping.h> -#include <linux/fsl/mxs-dma.h> +#include <linux/dmaengine.h> #include <asm/cacheflush.h> @@ -148,11 +148,6 @@ struct mxs_auart_port { struct device *dev; /* for DMA */ - struct mxs_dma_data dma_data; - int dma_channel_rx, dma_channel_tx; - int dma_irq_rx, dma_irq_tx; - int dma_channel; - struct scatterlist tx_sgl; struct dma_chan *tx_dma_chan; void *tx_dma_buf; @@ -440,20 +435,6 @@ static u32 mxs_auart_get_mctrl(struct uart_port *u) return mctrl; } -static bool mxs_auart_dma_filter(struct dma_chan *chan, void *param) -{ - struct mxs_auart_port *s = param; - - if (!mxs_dma_is_apbx(chan)) - return false; - - if (s->dma_channel == chan->chan_id) { - chan->private = &s->dma_data; - return true; - } - return false; -} - static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s); static void dma_rx_callback(void *arg) { @@ -545,21 +526,11 @@ static void mxs_auart_dma_exit(struct mxs_auart_port *s) static int mxs_auart_dma_init(struct mxs_auart_port *s) { - dma_cap_mask_t mask; - if (auart_dma_enabled(s)) return 0; - /* We do not get the right DMA channels. */ - if (s->dma_channel_rx == -1 || s->dma_channel_tx == -1) - return -EINVAL; - /* init for RX */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - s->dma_channel = s->dma_channel_rx; - s->dma_data.chan_irq = s->dma_irq_rx; - s->rx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + s->rx_dma_chan = dma_request_slave_channel(s->dev, "rx"); if (!s->rx_dma_chan) goto err_out; s->rx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); @@ -567,9 +538,7 @@ static int mxs_auart_dma_init(struct mxs_auart_port *s) goto err_out; /* init for TX */ - s->dma_channel = s->dma_channel_tx; - s->dma_data.chan_irq = s->dma_irq_tx; - s->tx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + s->tx_dma_chan = dma_request_slave_channel(s->dev, "tx"); if (!s->tx_dma_chan) goto err_out; s->tx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); @@ -883,7 +852,7 @@ auart_console_write(struct console *co, const char *str, unsigned int count) unsigned int old_ctrl0, old_ctrl2; unsigned int to = 1000; - if (co->index > MXS_AUART_PORTS || co->index < 0) + if (co->index >= MXS_AUART_PORTS || co->index < 0) return; s = auart_port[co->index]; @@ -1020,7 +989,6 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - u32 dma_channel[2]; int ret; if (!np) @@ -1034,20 +1002,8 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, } s->port.line = ret; - s->dma_irq_rx = platform_get_irq(pdev, 1); - s->dma_irq_tx = platform_get_irq(pdev, 2); + s->flags |= MXS_AUART_DMA_CONFIG; - ret = of_property_read_u32_array(np, "fsl,auart-dma-channel", - dma_channel, 2); - if (ret == 0) { - s->dma_channel_rx = dma_channel[0]; - s->dma_channel_tx = dma_channel[1]; - - s->flags |= MXS_AUART_DMA_CONFIG; - } else { - s->dma_channel_rx = -1; - s->dma_channel_tx = -1; - } return 0; } @@ -1103,7 +1059,7 @@ static int mxs_auart_probe(struct platform_device *pdev) s->port.fifosize = 16; s->port.uartclk = clk_get_rate(s->clk); s->port.type = PORT_IMX; - s->port.dev = s->dev = get_device(&pdev->dev); + s->port.dev = s->dev = &pdev->dev; s->ctrl = 0; @@ -1134,7 +1090,6 @@ out_free_irq: auart_port[pdev->id] = NULL; free_irq(s->irq, s); out_free_clk: - put_device(s->dev); clk_put(s->clk); out_free: kfree(s); @@ -1150,7 +1105,6 @@ static int mxs_auart_remove(struct platform_device *pdev) auart_port[pdev->id] = NULL; - put_device(s->dev); clk_put(s->clk); free_irq(s->irq, s); kfree(s); diff --git a/drivers/tty/serial/nwpserial.c b/drivers/tty/serial/nwpserial.c index 77287c54f33..549c70a2a63 100644 --- a/drivers/tty/serial/nwpserial.c +++ b/drivers/tty/serial/nwpserial.c @@ -199,7 +199,7 @@ static void nwpserial_shutdown(struct uart_port *port) dcr_write(up->dcr_host, UART_IER, up->ier); /* free irq */ - free_irq(up->port.irq, port); + free_irq(up->port.irq, up); } static int nwpserial_verify_port(struct uart_port *port, diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index b025d543827..39c7ea4cb14 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -14,7 +14,6 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/serial_core.h> -#include <linux/serial_8250.h> #include <linux/serial_reg.h> #include <linux/of_address.h> #include <linux/of_irq.h> @@ -22,6 +21,8 @@ #include <linux/nwpserial.h> #include <linux/clk.h> +#include "8250/8250.h" + struct of_serial_info { struct clk *clk; int type; @@ -97,6 +98,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (of_property_read_u32(np, "reg-shift", &prop) == 0) port->regshift = prop; + /* Check for fifo size */ + if (of_property_read_u32(np, "fifo-size", &prop) == 0) + port->fifosize = prop; + port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { @@ -167,11 +172,17 @@ static int of_platform_serial_probe(struct platform_device *ofdev) #ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: { - /* For now the of bindings don't support the extra - 8250 specific bits */ struct uart_8250_port port8250; memset(&port8250, 0, sizeof(port8250)); port8250.port = port; + + if (port.fifosize) + port8250.capabilities = UART_CAP_FIFO; + + if (of_property_read_bool(ofdev->dev.of_node, + "auto-flow-control")) + port8250.capabilities |= UART_CAP_AFE; + ret = serial8250_register_8250_port(&port8250); break; } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 4dc41408ecb..f0b9f6b52b3 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -202,26 +202,6 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up) return pdata->get_context_loss_count(up->dev); } -static void serial_omap_set_forceidle(struct uart_omap_port *up) -{ - struct omap_uart_port_info *pdata = up->dev->platform_data; - - if (!pdata || !pdata->set_forceidle) - return; - - pdata->set_forceidle(up->dev); -} - -static void serial_omap_set_noidle(struct uart_omap_port *up) -{ - struct omap_uart_port_info *pdata = up->dev->platform_data; - - if (!pdata || !pdata->set_noidle) - return; - - pdata->set_noidle(up->dev); -} - static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) { struct omap_uart_port_info *pdata = up->dev->platform_data; @@ -298,8 +278,6 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_IER, up->ier); } - serial_omap_set_forceidle(up); - pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); } @@ -364,7 +342,6 @@ static void serial_omap_start_tx(struct uart_port *port) pm_runtime_get_sync(up->dev); serial_omap_enable_ier_thri(up); - serial_omap_set_noidle(up); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); } @@ -886,6 +863,17 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); /* FIFO ENABLE, DMA MODE */ + up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK; + /* + * NOTE: Setting OMAP_UART_SCR_RX_TRIG_GRANU1_MASK + * sets Enables the granularity of 1 for TRIGGER RX + * level. Along with setting RX FIFO trigger level + * to 1 (as noted below, 16 characters) and TLR[3:0] + * to zero this will result RX FIFO threshold level + * to 1 character, instead of 16 as noted in comment + * below. + */ + /* Set receive FIFO threshold to 16 characters and * transmit FIFO threshold to 16 spaces */ diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 7a6c989924b..21a7e179edf 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1493,29 +1493,6 @@ static int pch_uart_verify_port(struct uart_port *port, return 0; } -static struct uart_ops pch_uart_ops = { - .tx_empty = pch_uart_tx_empty, - .set_mctrl = pch_uart_set_mctrl, - .get_mctrl = pch_uart_get_mctrl, - .stop_tx = pch_uart_stop_tx, - .start_tx = pch_uart_start_tx, - .stop_rx = pch_uart_stop_rx, - .enable_ms = pch_uart_enable_ms, - .break_ctl = pch_uart_break_ctl, - .startup = pch_uart_startup, - .shutdown = pch_uart_shutdown, - .set_termios = pch_uart_set_termios, -/* .pm = pch_uart_pm, Not supported yet */ -/* .set_wake = pch_uart_set_wake, Not supported yet */ - .type = pch_uart_type, - .release_port = pch_uart_release_port, - .request_port = pch_uart_request_port, - .config_port = pch_uart_config_port, - .verify_port = pch_uart_verify_port -}; - -#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE - /* * Wait for transmitter & holding register to empty */ @@ -1547,6 +1524,84 @@ static void wait_for_xmitr(struct eg20t_port *up, int bits) } } +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for communicate via uart while + * in an interrupt or debug context. + */ +static int pch_uart_get_poll_char(struct uart_port *port) +{ + struct eg20t_port *priv = + container_of(port, struct eg20t_port, port); + u8 lsr = ioread8(priv->membase + UART_LSR); + + if (!(lsr & UART_LSR_DR)) + return NO_POLL_CHAR; + + return ioread8(priv->membase + PCH_UART_RBR); +} + + +static void pch_uart_put_poll_char(struct uart_port *port, + unsigned char c) +{ + unsigned int ier; + struct eg20t_port *priv = + container_of(port, struct eg20t_port, port); + + /* + * First save the IER then disable the interrupts + */ + ier = ioread8(priv->membase + UART_IER); + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); + + wait_for_xmitr(priv, UART_LSR_THRE); + /* + * Send the character out. + * If a LF, also do CR... + */ + iowrite8(c, priv->membase + PCH_UART_THR); + if (c == 10) { + wait_for_xmitr(priv, UART_LSR_THRE); + iowrite8(13, priv->membase + PCH_UART_THR); + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(priv, BOTH_EMPTY); + iowrite8(ier, priv->membase + UART_IER); +} +#endif /* CONFIG_CONSOLE_POLL */ + +static struct uart_ops pch_uart_ops = { + .tx_empty = pch_uart_tx_empty, + .set_mctrl = pch_uart_set_mctrl, + .get_mctrl = pch_uart_get_mctrl, + .stop_tx = pch_uart_stop_tx, + .start_tx = pch_uart_start_tx, + .stop_rx = pch_uart_stop_rx, + .enable_ms = pch_uart_enable_ms, + .break_ctl = pch_uart_break_ctl, + .startup = pch_uart_startup, + .shutdown = pch_uart_shutdown, + .set_termios = pch_uart_set_termios, +/* .pm = pch_uart_pm, Not supported yet */ +/* .set_wake = pch_uart_set_wake, Not supported yet */ + .type = pch_uart_type, + .release_port = pch_uart_release_port, + .request_port = pch_uart_request_port, + .config_port = pch_uart_config_port, + .verify_port = pch_uart_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = pch_uart_get_poll_char, + .poll_put_char = pch_uart_put_poll_char, +#endif +}; + +#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE + static void pch_console_putchar(struct uart_port *port, int ch) { struct eg20t_port *priv = @@ -1655,7 +1710,7 @@ static struct console pch_console = { #define PCH_CONSOLE (&pch_console) #else #define PCH_CONSOLE NULL -#endif +#endif /* CONFIG_SERIAL_PCH_UART_CONSOLE */ static struct uart_driver pch_uart_driver = { .owner = THIS_MODULE, diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 2769a38d15b..0c8a9fa2be6 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -39,6 +39,7 @@ #include <linux/tty_flip.h> #include <linux/serial_core.h> #include <linux/serial.h> +#include <linux/serial_s3c.h> #include <linux/delay.h> #include <linux/clk.h> #include <linux/cpufreq.h> @@ -46,10 +47,9 @@ #include <asm/irq.h> -#include <mach/hardware.h> - -#include <plat/regs-serial.h> +#ifdef CONFIG_SAMSUNG_CLOCK #include <plat/clock.h> +#endif #include "samsung.h" @@ -446,6 +446,8 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) /* Clear pending interrupts and mask all interrupts */ if (s3c24xx_serial_has_interrupt_mask(port)) { + free_irq(port->irq, ourport); + wr_regl(port, S3C64XX_UINTP, 0xf); wr_regl(port, S3C64XX_UINTM, 0xf); } @@ -505,6 +507,8 @@ static int s3c64xx_serial_startup(struct uart_port *port) dbg("s3c64xx_serial_startup: port=%p (%08lx,%p)\n", port->mapbase, port->membase); + wr_regl(port, S3C64XX_UINTM, 0xf); + ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED, s3c24xx_serial_portname(port), ourport); if (ret) { @@ -894,7 +898,7 @@ console_initcall(s3c24xx_serial_console_init); #define S3C24XX_SERIAL_CONSOLE NULL #endif -#ifdef CONFIG_CONSOLE_POLL +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) static int s3c24xx_serial_get_poll_char(struct uart_port *port); static void s3c24xx_serial_put_poll_char(struct uart_port *port, unsigned char c); @@ -918,7 +922,7 @@ static struct uart_ops s3c24xx_serial_ops = { .request_port = s3c24xx_serial_request_port, .config_port = s3c24xx_serial_config_port, .verify_port = s3c24xx_serial_verify_port, -#ifdef CONFIG_CONSOLE_POLL +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) .poll_get_char = s3c24xx_serial_get_poll_char, .poll_put_char = s3c24xx_serial_put_poll_char, #endif @@ -1162,6 +1166,18 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, ourport->tx_irq = ret; ourport->clk = clk_get(&platdev->dev, "uart"); + if (IS_ERR(ourport->clk)) { + pr_err("%s: Controller clock not found\n", + dev_name(&platdev->dev)); + return PTR_ERR(ourport->clk); + } + + ret = clk_prepare_enable(ourport->clk); + if (ret) { + pr_err("uart: clock failed to prepare+enable: %d\n", ret); + clk_put(ourport->clk); + return ret; + } /* Keep all interrupts masked and cleared */ if (s3c24xx_serial_has_interrupt_mask(port)) { @@ -1176,9 +1192,11 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, /* reset the fifos (and setup the uart) */ s3c24xx_serial_resetport(port, cfg); + clk_disable_unprepare(ourport->clk); return 0; } +#ifdef CONFIG_SAMSUNG_CLOCK static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, struct device_attribute *attr, char *buf) @@ -1194,7 +1212,7 @@ static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, } static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); - +#endif /* Device driver serial port probe */ @@ -1252,9 +1270,11 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); platform_set_drvdata(pdev, &ourport->port); +#ifdef CONFIG_SAMSUNG_CLOCK ret = device_create_file(&pdev->dev, &dev_attr_clock_source); if (ret < 0) dev_err(&pdev->dev, "failed to add clock source attr.\n"); +#endif ret = s3c24xx_serial_cpufreq_register(ourport); if (ret < 0) @@ -1272,7 +1292,9 @@ static int s3c24xx_serial_remove(struct platform_device *dev) if (port) { s3c24xx_serial_cpufreq_deregister(to_ourport(port)); +#ifdef CONFIG_SAMSUNG_CLOCK device_remove_file(&dev->dev, &dev_attr_clock_source); +#endif uart_remove_one_port(&s3c24xx_uart_drv, port); } @@ -1307,9 +1329,29 @@ static int s3c24xx_serial_resume(struct device *dev) return 0; } +static int s3c24xx_serial_resume_noirq(struct device *dev) +{ + struct uart_port *port = s3c24xx_dev_to_port(dev); + + if (port) { + /* restore IRQ mask */ + if (s3c24xx_serial_has_interrupt_mask(port)) { + unsigned int uintm = 0xf; + if (tx_enabled(port)) + uintm &= ~S3C64XX_UINTM_TXD_MSK; + if (rx_enabled(port)) + uintm &= ~S3C64XX_UINTM_RXD_MSK; + wr_regl(port, S3C64XX_UINTM, uintm); + } + } + + return 0; +} + static const struct dev_pm_ops s3c24xx_serial_pm_ops = { .suspend = s3c24xx_serial_suspend, .resume = s3c24xx_serial_resume, + .resume_noirq = s3c24xx_serial_resume_noirq, }; #define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops) @@ -1343,6 +1385,13 @@ s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0; } +static bool +s3c24xx_port_configured(unsigned int ucon) +{ + /* consider the serial port configured if the tx/rx mode set */ + return (ucon & 0xf) != 0; +} + #ifdef CONFIG_CONSOLE_POLL /* * Console polling routines for writing and reading from the uart while @@ -1365,6 +1414,11 @@ static void s3c24xx_serial_put_poll_char(struct uart_port *port, unsigned char c) { unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); + unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON); + + /* not possible to xmit on unconfigured port */ + if (!s3c24xx_port_configured(ucon)) + return; while (!s3c24xx_serial_console_txrdy(port, ufcon)) cpu_relax(); @@ -1377,6 +1431,12 @@ static void s3c24xx_serial_console_putchar(struct uart_port *port, int ch) { unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); + unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON); + + /* not possible to xmit on unconfigured port */ + if (!s3c24xx_port_configured(ucon)) + return; + while (!s3c24xx_serial_console_txrdy(port, ufcon)) barrier(); wr_regb(cons_uart, S3C2410_UTXH, ch); @@ -1409,9 +1469,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud, "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n", port, ulcon, ucon, ubrdiv); - if ((ucon & 0xf) != 0) { - /* consider the serial port configured if the tx/rx mode set */ - + if (s3c24xx_port_configured(ucon)) { switch (ulcon & S3C2410_LCON_CSMASK) { case S3C2410_LCON_CS5: *bits = 5; @@ -1758,6 +1816,7 @@ static int __init s3c24xx_serial_modinit(void) static void __exit s3c24xx_serial_modexit(void) { + platform_driver_unregister(&samsung_serial_driver); uart_unregister_driver(&s3c24xx_uart_drv); } diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index 1a4bca3e417..00a499ecd38 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -76,7 +76,9 @@ struct s3c24xx_uart_port { #define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) #define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) -#ifdef CONFIG_SERIAL_SAMSUNG_DEBUG +#if defined(CONFIG_SERIAL_SAMSUNG_DEBUG) && \ + defined(CONFIG_DEBUG_LL) && \ + !defined(MODULE) extern void printascii(const char *); diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 08dbfb88d42..c7730415541 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -27,6 +27,7 @@ #include <linux/spinlock.h> #include <linux/platform_device.h> #include <linux/platform_data/serial-sccnxp.h> +#include <linux/regulator/consumer.h> #define SCCNXP_NAME "uart-sccnxp" #define SCCNXP_MAJOR 204 @@ -131,6 +132,8 @@ struct sccnxp_port { struct timer_list timer; struct sccnxp_pdata pdata; + + struct regulator *regulator; }; static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift) @@ -789,8 +792,6 @@ static int sccnxp_probe(struct platform_device *pdev) return -EADDRNOTAVAIL; } - dev_set_name(&pdev->dev, SCCNXP_NAME); - s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL); if (!s) { dev_err(&pdev->dev, "Error allocating port structure\n"); @@ -918,6 +919,16 @@ static int sccnxp_probe(struct platform_device *pdev) goto err_out; } + s->regulator = devm_regulator_get(&pdev->dev, "VCC"); + if (!IS_ERR(s->regulator)) { + ret = regulator_enable(s->regulator); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable regulator: %i\n", ret); + return ret; + } + } + membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(membase)) { ret = PTR_ERR(membase); @@ -967,10 +978,6 @@ static int sccnxp_probe(struct platform_device *pdev) s->imr = 0; sccnxp_write(&s->port[0], SCCNXP_IMR_REG, 0); - /* Board specific configure */ - if (s->pdata.init) - s->pdata.init(); - if (!s->poll) { ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, sccnxp_ist, @@ -1011,8 +1018,8 @@ static int sccnxp_remove(struct platform_device *pdev) uart_unregister_driver(&s->uart); platform_set_drvdata(pdev, NULL); - if (s->pdata.exit) - s->pdata.exit(); + if (!IS_ERR(s->regulator)) + return regulator_disable(s->regulator); return 0; } diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 372de8ade76..9799d043a9b 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -26,6 +26,7 @@ #include <linux/dmaengine.h> #include <linux/dma-mapping.h> #include <linux/dmapool.h> +#include <linux/err.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/module.h> @@ -1301,11 +1302,9 @@ static int tegra_uart_probe(struct platform_device *pdev) } u->mapbase = resource->start; - u->membase = devm_request_and_ioremap(&pdev->dev, resource); - if (!u->membase) { - dev_err(&pdev->dev, "memregion/iomap address req failed\n"); - return -EADDRNOTAVAIL; - } + u->membase = devm_ioremap_resource(&pdev->dev, resource); + if (IS_ERR(u->membase)) + return PTR_ERR(u->membase); tup->uart_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(tup->uart_clk)) { diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a400002dfa8..f87dbfd3277 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1711,7 +1711,7 @@ static int uart_proc_show(struct seq_file *m, void *v) static int uart_proc_open(struct inode *inode, struct file *file) { - return single_open(file, uart_proc_show, PDE(inode)->data); + return single_open(file, uart_proc_show, PDE_DATA(inode)); } static const struct file_operations uart_proc_fops = { @@ -1941,6 +1941,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) mutex_unlock(&port->mutex); return 0; } + put_device(tty_dev); + if (console_suspend_enabled || !uart_console(uport)) uport->suspended = 1; @@ -2006,9 +2008,11 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) disable_irq_wake(uport->irq); uport->irq_wake = 0; } + put_device(tty_dev); mutex_unlock(&port->mutex); return 0; } + put_device(tty_dev); uport->suspended = 0; /* diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 4c22a1529aa..5aca7364634 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -15,8 +15,6 @@ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ defined(CONFIG_ARCH_SH7372) || \ defined(CONFIG_ARCH_R8A7740) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 6bbfe9934a4..03465b67394 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -758,7 +758,7 @@ static struct of_device_id sirfsoc_uart_ids[] = { { .compatible = "sirf,marco-uart", }, {} }; -MODULE_DEVICE_TABLE(of, sirfsoc_serial_of_match); +MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids); static struct platform_driver sirfsoc_uart_driver = { .probe = sirfsoc_uart_probe, diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index 8de2213664e..a422c8b55a4 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -203,7 +203,7 @@ receive_chars(struct uart_sunsab_port *up, flag = TTY_FRAME; } - if (uart_handle_sysrq_char(&up->port, ch)) + if (uart_handle_sysrq_char(&up->port, ch) || !port) continue; if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 && diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index e343d667085..0d846572847 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -968,6 +968,7 @@ static struct uart_ops sunsu_pops = { #define UART_NR 4 static struct uart_sunsu_port sunsu_ports[UART_NR]; +static int nr_inst; /* Number of already registered ports */ #ifdef CONFIG_SERIO @@ -1337,13 +1338,8 @@ static int __init sunsu_console_setup(struct console *co, char *options) printk("Console: ttyS%d (SU)\n", (sunsu_reg.minor - 64) + co->index); - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index >= UART_NR) - co->index = 0; + if (co->index > nr_inst) + return -ENODEV; port = &sunsu_ports[co->index].port; /* @@ -1408,7 +1404,6 @@ static enum su_type su_get_type(struct device_node *dp) static int su_probe(struct platform_device *op) { - static int inst; struct device_node *dp = op->dev.of_node; struct uart_sunsu_port *up; struct resource *rp; @@ -1418,16 +1413,16 @@ static int su_probe(struct platform_device *op) type = su_get_type(dp); if (type == SU_PORT_PORT) { - if (inst >= UART_NR) + if (nr_inst >= UART_NR) return -EINVAL; - up = &sunsu_ports[inst]; + up = &sunsu_ports[nr_inst]; } else { up = kzalloc(sizeof(*up), GFP_KERNEL); if (!up) return -ENOMEM; } - up->port.line = inst; + up->port.line = nr_inst; spin_lock_init(&up->port.lock); @@ -1461,6 +1456,8 @@ static int su_probe(struct platform_device *op) } dev_set_drvdata(&op->dev, up); + nr_inst++; + return 0; } @@ -1488,7 +1485,7 @@ static int su_probe(struct platform_device *op) dev_set_drvdata(&op->dev, up); - inst++; + nr_inst++; return 0; @@ -1595,6 +1592,7 @@ static int __init sunsu_init(void) static void __exit sunsu_exit(void) { + platform_driver_unregister(&su_driver); if (sunsu_reg.nr) sunserial_unregister_minors(&sunsu_reg, sunsu_reg.nr); } diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index 27669ff3d44..813ef8eb8ef 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -388,7 +388,7 @@ sunzilog_receive_chars(struct uart_sunzilog_port *up, else if (r1 & CRC_ERR) flag = TTY_FRAME; } - if (uart_handle_sysrq_char(&up->port, ch)) + if (uart_handle_sysrq_char(&up->port, ch) || !port) continue; if (up->port.ignore_status_mask == 0xff || diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 705240e6c4e..1a8bc2275ea 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -35,6 +35,7 @@ #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/of.h> +#include <linux/err.h> /* * UART Register offsets @@ -585,9 +586,9 @@ static int vt8500_serial_probe(struct platform_device *pdev) if (!vt8500_port) return -ENOMEM; - vt8500_port->uart.membase = devm_request_and_ioremap(&pdev->dev, mmres); - if (!vt8500_port->uart.membase) - return -EADDRNOTAVAIL; + vt8500_port->uart.membase = devm_ioremap_resource(&pdev->dev, mmres); + if (IS_ERR(vt8500_port->uart.membase)) + return PTR_ERR(vt8500_port->uart.membase); vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0); if (IS_ERR(vt8500_port->clk)) { diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index ba451c7209f..4e5c77834c5 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -578,14 +578,13 @@ static int xuartps_startup(struct uart_port *port) /* Receive Timeout register is enabled with value of 10 */ xuartps_writel(10, XUARTPS_RXTOUT_OFFSET); + /* Clear out any pending interrupts before enabling them */ + xuartps_writel(xuartps_readl(XUARTPS_ISR_OFFSET), XUARTPS_ISR_OFFSET); /* Set the Interrupt Registers with desired interrupts */ xuartps_writel(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN | XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET); - xuartps_writel(~(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN | - XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT), XUARTPS_IDR_OFFSET); return retval; } diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 8983276aa35..8eaf1ab8add 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -1058,9 +1058,6 @@ static void mgsl_bh_handler(struct work_struct *work) container_of(work, struct mgsl_struct, task); int action; - if (!info) - return; - if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_handler(%s) entry\n", __FILE__,__LINE__,info->device_name); @@ -3311,7 +3308,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, port->blocked_open++; while (1) { - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index aa9eece35c3..1abf946463f 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3308,7 +3308,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if ((tty->termios.c_cflag & CBAUD)) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index 6d5780cf1d5..ff171384ea5 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3329,7 +3329,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 80310f7f58d..55ba46f6207 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -106,7 +106,7 @@ static void sysrq_handle_SAK(int key) } static struct sysrq_key_op sysrq_SAK_op = { .handler = sysrq_handle_SAK, - .help_msg = "saK", + .help_msg = "sak(k)", .action_msg = "SAK", .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; @@ -122,7 +122,7 @@ static void sysrq_handle_unraw(int key) static struct sysrq_key_op sysrq_unraw_op = { .handler = sysrq_handle_unraw, - .help_msg = "unRaw", + .help_msg = "unraw(r)", .action_msg = "Keyboard mode set to system default", .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; @@ -140,7 +140,7 @@ static void sysrq_handle_crash(int key) } static struct sysrq_key_op sysrq_crash_op = { .handler = sysrq_handle_crash, - .help_msg = "Crash", + .help_msg = "crash(c)", .action_msg = "Trigger a crash", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -153,7 +153,7 @@ static void sysrq_handle_reboot(int key) } static struct sysrq_key_op sysrq_reboot_op = { .handler = sysrq_handle_reboot, - .help_msg = "reBoot", + .help_msg = "reboot(b)", .action_msg = "Resetting", .enable_mask = SYSRQ_ENABLE_BOOT, }; @@ -164,7 +164,7 @@ static void sysrq_handle_sync(int key) } static struct sysrq_key_op sysrq_sync_op = { .handler = sysrq_handle_sync, - .help_msg = "Sync", + .help_msg = "sync(s)", .action_msg = "Emergency Sync", .enable_mask = SYSRQ_ENABLE_SYNC, }; @@ -176,7 +176,7 @@ static void sysrq_handle_show_timers(int key) static struct sysrq_key_op sysrq_show_timers_op = { .handler = sysrq_handle_show_timers, - .help_msg = "show-all-timers(Q)", + .help_msg = "show-all-timers(q)", .action_msg = "Show clockevent devices & pending hrtimers (no others)", }; @@ -186,7 +186,7 @@ static void sysrq_handle_mountro(int key) } static struct sysrq_key_op sysrq_mountro_op = { .handler = sysrq_handle_mountro, - .help_msg = "Unmount", + .help_msg = "unmount(u)", .action_msg = "Emergency Remount R/O", .enable_mask = SYSRQ_ENABLE_REMOUNT, }; @@ -199,7 +199,7 @@ static void sysrq_handle_showlocks(int key) static struct sysrq_key_op sysrq_showlocks_op = { .handler = sysrq_handle_showlocks, - .help_msg = "show-all-locks(D)", + .help_msg = "show-all-locks(d)", .action_msg = "Show Locks Held", }; #else @@ -250,7 +250,7 @@ static void sysrq_handle_showallcpus(int key) static struct sysrq_key_op sysrq_showallcpus_op = { .handler = sysrq_handle_showallcpus, - .help_msg = "show-backtrace-all-active-cpus(L)", + .help_msg = "show-backtrace-all-active-cpus(l)", .action_msg = "Show backtrace of all active CPUs", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -265,7 +265,7 @@ static void sysrq_handle_showregs(int key) } static struct sysrq_key_op sysrq_showregs_op = { .handler = sysrq_handle_showregs, - .help_msg = "show-registers(P)", + .help_msg = "show-registers(p)", .action_msg = "Show Regs", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -276,7 +276,7 @@ static void sysrq_handle_showstate(int key) } static struct sysrq_key_op sysrq_showstate_op = { .handler = sysrq_handle_showstate, - .help_msg = "show-task-states(T)", + .help_msg = "show-task-states(t)", .action_msg = "Show State", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -287,7 +287,7 @@ static void sysrq_handle_showstate_blocked(int key) } static struct sysrq_key_op sysrq_showstate_blocked_op = { .handler = sysrq_handle_showstate_blocked, - .help_msg = "show-blocked-tasks(W)", + .help_msg = "show-blocked-tasks(w)", .action_msg = "Show Blocked State", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -301,7 +301,7 @@ static void sysrq_ftrace_dump(int key) } static struct sysrq_key_op sysrq_ftrace_dump_op = { .handler = sysrq_ftrace_dump, - .help_msg = "dump-ftrace-buffer(Z)", + .help_msg = "dump-ftrace-buffer(z)", .action_msg = "Dump ftrace buffer", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -315,7 +315,7 @@ static void sysrq_handle_showmem(int key) } static struct sysrq_key_op sysrq_showmem_op = { .handler = sysrq_handle_showmem, - .help_msg = "show-memory-usage(M)", + .help_msg = "show-memory-usage(m)", .action_msg = "Show Memory", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -346,7 +346,7 @@ static void sysrq_handle_term(int key) } static struct sysrq_key_op sysrq_term_op = { .handler = sysrq_handle_term, - .help_msg = "terminate-all-tasks(E)", + .help_msg = "terminate-all-tasks(e)", .action_msg = "Terminate All Tasks", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -365,7 +365,7 @@ static void sysrq_handle_moom(int key) } static struct sysrq_key_op sysrq_moom_op = { .handler = sysrq_handle_moom, - .help_msg = "memory-full-oom-kill(F)", + .help_msg = "memory-full-oom-kill(f)", .action_msg = "Manual OOM execution", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -377,7 +377,7 @@ static void sysrq_handle_thaw(int key) } static struct sysrq_key_op sysrq_thaw_op = { .handler = sysrq_handle_thaw, - .help_msg = "thaw-filesystems(J)", + .help_msg = "thaw-filesystems(j)", .action_msg = "Emergency Thaw of all frozen filesystems", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -390,7 +390,7 @@ static void sysrq_handle_kill(int key) } static struct sysrq_key_op sysrq_kill_op = { .handler = sysrq_handle_kill, - .help_msg = "kill-all-tasks(I)", + .help_msg = "kill-all-tasks(i)", .action_msg = "Kill All Tasks", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -401,7 +401,7 @@ static void sysrq_handle_unrt(int key) } static struct sysrq_key_op sysrq_unrt_op = { .handler = sysrq_handle_unrt, - .help_msg = "nice-all-RT-tasks(N)", + .help_msg = "nice-all-RT-tasks(n)", .action_msg = "Nice All RT Tasks", .enable_mask = SYSRQ_ENABLE_RTNICE, }; diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 6953dc82850..a4fdce74f88 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -60,24 +60,22 @@ static void tty_audit_buf_put(struct tty_audit_buf *buf) tty_audit_buf_free(buf); } -static void tty_audit_log(const char *description, struct task_struct *tsk, - kuid_t loginuid, unsigned sessionid, int major, - int minor, unsigned char *data, size_t size) +static void tty_audit_log(const char *description, int major, int minor, + unsigned char *data, size_t size) { struct audit_buffer *ab; + struct task_struct *tsk = current; + uid_t uid = from_kuid(&init_user_ns, task_uid(tsk)); + uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk)); + u32 sessionid = audit_get_sessionid(tsk); ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY); if (ab) { char name[sizeof(tsk->comm)]; - kuid_t uid = task_uid(tsk); - - audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u " - "major=%d minor=%d comm=", description, - tsk->pid, - from_kuid(&init_user_ns, uid), - from_kuid(&init_user_ns, loginuid), - sessionid, - major, minor); + + audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d" + " minor=%d comm=", description, tsk->pid, uid, + loginuid, sessionid, major, minor); get_task_comm(name, tsk); audit_log_untrustedstring(ab, name); audit_log_format(ab, " data="); @@ -90,11 +88,9 @@ static void tty_audit_log(const char *description, struct task_struct *tsk, * tty_audit_buf_push - Push buffered data out * * Generate an audit message from the contents of @buf, which is owned by - * @tsk with @loginuid. @buf->mutex must be locked. + * the current task. @buf->mutex must be locked. */ -static void tty_audit_buf_push(struct task_struct *tsk, kuid_t loginuid, - unsigned int sessionid, - struct tty_audit_buf *buf) +static void tty_audit_buf_push(struct tty_audit_buf *buf) { if (buf->valid == 0) return; @@ -102,25 +98,11 @@ static void tty_audit_buf_push(struct task_struct *tsk, kuid_t loginuid, buf->valid = 0; return; } - tty_audit_log("tty", tsk, loginuid, sessionid, buf->major, buf->minor, - buf->data, buf->valid); + tty_audit_log("tty", buf->major, buf->minor, buf->data, buf->valid); buf->valid = 0; } /** - * tty_audit_buf_push_current - Push buffered data out - * - * Generate an audit message from the contents of @buf, which is owned by - * the current task. @buf->mutex must be locked. - */ -static void tty_audit_buf_push_current(struct tty_audit_buf *buf) -{ - kuid_t auid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - tty_audit_buf_push(current, auid, sessionid, buf); -} - -/** * tty_audit_exit - Handle a task exit * * Make sure all buffered data is written out and deallocate the buffer. @@ -130,15 +112,13 @@ void tty_audit_exit(void) { struct tty_audit_buf *buf; - spin_lock_irq(¤t->sighand->siglock); buf = current->signal->tty_audit_buf; current->signal->tty_audit_buf = NULL; - spin_unlock_irq(¤t->sighand->siglock); if (!buf) return; mutex_lock(&buf->mutex); - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); @@ -151,9 +131,8 @@ void tty_audit_exit(void) */ void tty_audit_fork(struct signal_struct *sig) { - spin_lock_irq(¤t->sighand->siglock); sig->audit_tty = current->signal->audit_tty; - spin_unlock_irq(¤t->sighand->siglock); + sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd; } /** @@ -163,20 +142,21 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) { struct tty_audit_buf *buf; int major, minor, should_audit; + unsigned long flags; - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); should_audit = current->signal->audit_tty; buf = current->signal->tty_audit_buf; if (buf) atomic_inc(&buf->count); - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); major = tty->driver->major; minor = tty->driver->minor_start + tty->index; if (buf) { mutex_lock(&buf->mutex); if (buf->major == major && buf->minor == minor) - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); } @@ -187,24 +167,20 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) auid = audit_get_loginuid(current); sessionid = audit_get_sessionid(current); - tty_audit_log("ioctl=TIOCSTI", current, auid, sessionid, major, - minor, &ch, 1); + tty_audit_log("ioctl=TIOCSTI", major, minor, &ch, 1); } } /** - * tty_audit_push_task - Flush task's pending audit data - * @tsk: task pointer - * @loginuid: sender login uid - * @sessionid: sender session id + * tty_audit_push_current - Flush current's pending audit data * - * Called with a ref on @tsk held. Try to lock sighand and get a - * reference to the tty audit buffer if available. + * Try to lock sighand and get a reference to the tty audit buffer if available. * Flush the buffer or return an appropriate error code. */ -int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid) +int tty_audit_push_current(void) { struct tty_audit_buf *buf = ERR_PTR(-EPERM); + struct task_struct *tsk = current; unsigned long flags; if (!lock_task_sighand(tsk, &flags)) @@ -225,7 +201,7 @@ int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid) return PTR_ERR(buf); mutex_lock(&buf->mutex); - tty_audit_buf_push(tsk, loginuid, sessionid, buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); @@ -243,10 +219,11 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, unsigned icanon) { struct tty_audit_buf *buf, *buf2; + unsigned long flags; buf = NULL; buf2 = NULL; - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); if (likely(!current->signal->audit_tty)) goto out; buf = current->signal->tty_audit_buf; @@ -254,7 +231,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, atomic_inc(&buf->count); goto out; } - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); buf2 = tty_audit_buf_alloc(tty->driver->major, tty->driver->minor_start + tty->index, @@ -264,7 +241,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, return NULL; } - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); if (!current->signal->audit_tty) goto out; buf = current->signal->tty_audit_buf; @@ -276,7 +253,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, atomic_inc(&buf->count); /* Fall through */ out: - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); if (buf2) tty_audit_buf_free(buf2); return buf; @@ -292,10 +269,18 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, { struct tty_audit_buf *buf; int major, minor; + int audit_log_tty_passwd; + unsigned long flags; if (unlikely(size == 0)) return; + spin_lock_irqsave(¤t->sighand->siglock, flags); + audit_log_tty_passwd = current->signal->audit_tty_log_passwd; + spin_unlock_irqrestore(¤t->sighand->siglock, flags); + if (!audit_log_tty_passwd && icanon && !L_ECHO(tty)) + return; + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) return; @@ -309,7 +294,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, minor = tty->driver->minor_start + tty->index; if (buf->major != major || buf->minor != minor || buf->icanon != icanon) { - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); buf->major = major; buf->minor = minor; buf->icanon = icanon; @@ -325,7 +310,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, data += run; size -= run; if (buf->valid == N_TTY_BUF_SIZE) - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); } while (size != 0); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); @@ -339,16 +324,17 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, void tty_audit_push(struct tty_struct *tty) { struct tty_audit_buf *buf; + unsigned long flags; - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); if (likely(!current->signal->audit_tty)) { - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); return; } buf = current->signal->tty_audit_buf; if (buf) atomic_inc(&buf->count); - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); if (buf) { int major, minor; @@ -357,7 +343,7 @@ void tty_audit_push(struct tty_struct *tty) minor = tty->driver->minor_start + tty->index; mutex_lock(&buf->mutex); if (buf->major == major && buf->minor == minor) - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 578aa7594b1..9121c1f7aee 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -449,11 +449,6 @@ static void flush_to_ldisc(struct work_struct *work) tty_buffer_free(port, head); continue; } - /* Ldisc or user is trying to flush the buffers - we are feeding to the ldisc, stop feeding the - line discipline as we want to empty the queue */ - if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) - break; if (!tty->receive_room) break; if (count > tty->receive_room) @@ -465,17 +460,20 @@ static void flush_to_ldisc(struct work_struct *work) disc->ops->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&buf->lock, flags); + /* Ldisc or user is trying to flush the buffers. + We may have a deferred request to flush the + input buffer, if so pull the chain under the lock + and empty the queue */ + if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { + __tty_buffer_flush(port); + clear_bit(TTYP_FLUSHPENDING, &port->iflags); + wake_up(&tty->read_wait); + break; + } } clear_bit(TTYP_FLUSHING, &port->iflags); } - /* We may have a deferred request to flush the input buffer, - if so pull the chain under the lock and empty the queue */ - if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { - __tty_buffer_flush(port); - clear_bit(TTYP_FLUSHPENDING, &port->iflags); - wake_up(&tty->read_wait); - } spin_unlock_irqrestore(&buf->lock, flags); tty_ldisc_deref(disc); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 05400acbc45..6464029e486 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -533,6 +533,60 @@ void tty_wakeup(struct tty_struct *tty) EXPORT_SYMBOL_GPL(tty_wakeup); /** + * tty_signal_session_leader - sends SIGHUP to session leader + * @tty controlling tty + * @exit_session if non-zero, signal all foreground group processes + * + * Send SIGHUP and SIGCONT to the session leader and its process group. + * Optionally, signal all processes in the foreground process group. + * + * Returns the number of processes in the session with this tty + * as their controlling terminal. This value is used to drop + * tty references for those processes. + */ +static int tty_signal_session_leader(struct tty_struct *tty, int exit_session) +{ + struct task_struct *p; + int refs = 0; + struct pid *tty_pgrp = NULL; + + read_lock(&tasklist_lock); + if (tty->session) { + do_each_pid_task(tty->session, PIDTYPE_SID, p) { + spin_lock_irq(&p->sighand->siglock); + if (p->signal->tty == tty) { + p->signal->tty = NULL; + /* We defer the dereferences outside fo + the tasklist lock */ + refs++; + } + if (!p->signal->leader) { + spin_unlock_irq(&p->sighand->siglock); + continue; + } + __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); + __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); + put_pid(p->signal->tty_old_pgrp); /* A noop */ + spin_lock(&tty->ctrl_lock); + tty_pgrp = get_pid(tty->pgrp); + if (tty->pgrp) + p->signal->tty_old_pgrp = get_pid(tty->pgrp); + spin_unlock(&tty->ctrl_lock); + spin_unlock_irq(&p->sighand->siglock); + } while_each_pid_task(tty->session, PIDTYPE_SID, p); + } + read_unlock(&tasklist_lock); + + if (tty_pgrp) { + if (exit_session) + kill_pgrp(tty_pgrp, SIGHUP, exit_session); + put_pid(tty_pgrp); + } + + return refs; +} + +/** * __tty_hangup - actual handler for hangup events * @work: tty device * @@ -554,15 +608,13 @@ EXPORT_SYMBOL_GPL(tty_wakeup); * tasklist_lock to walk task list for hangup event * ->siglock to protect ->signal/->sighand */ -static void __tty_hangup(struct tty_struct *tty) +static void __tty_hangup(struct tty_struct *tty, int exit_session) { struct file *cons_filp = NULL; struct file *filp, *f = NULL; - struct task_struct *p; struct tty_file_private *priv; int closecount = 0, n; - unsigned long flags; - int refs = 0; + int refs; if (!tty) return; @@ -599,39 +651,18 @@ static void __tty_hangup(struct tty_struct *tty) } spin_unlock(&tty_files_lock); + refs = tty_signal_session_leader(tty, exit_session); + /* Account for the p->signal references we killed */ + while (refs--) + tty_kref_put(tty); + /* * it drops BTM and thus races with reopen * we protect the race by TTY_HUPPING */ tty_ldisc_hangup(tty); - read_lock(&tasklist_lock); - if (tty->session) { - do_each_pid_task(tty->session, PIDTYPE_SID, p) { - spin_lock_irq(&p->sighand->siglock); - if (p->signal->tty == tty) { - p->signal->tty = NULL; - /* We defer the dereferences outside fo - the tasklist lock */ - refs++; - } - if (!p->signal->leader) { - spin_unlock_irq(&p->sighand->siglock); - continue; - } - __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); - __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); - put_pid(p->signal->tty_old_pgrp); /* A noop */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->pgrp) - p->signal->tty_old_pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - spin_unlock_irq(&p->sighand->siglock); - } while_each_pid_task(tty->session, PIDTYPE_SID, p); - } - read_unlock(&tasklist_lock); - - spin_lock_irqsave(&tty->ctrl_lock, flags); + spin_lock_irq(&tty->ctrl_lock); clear_bit(TTY_THROTTLED, &tty->flags); clear_bit(TTY_PUSH, &tty->flags); clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); @@ -640,11 +671,7 @@ static void __tty_hangup(struct tty_struct *tty) tty->session = NULL; tty->pgrp = NULL; tty->ctrl_status = 0; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - - /* Account for the p->signal references we killed */ - while (refs--) - tty_kref_put(tty); + spin_unlock_irq(&tty->ctrl_lock); /* * If one of the devices matches a console pointer, we @@ -666,7 +693,6 @@ static void __tty_hangup(struct tty_struct *tty) */ set_bit(TTY_HUPPED, &tty->flags); clear_bit(TTY_HUPPING, &tty->flags); - tty_ldisc_enable(tty); tty_unlock(tty); @@ -679,7 +705,7 @@ static void do_tty_hangup(struct work_struct *work) struct tty_struct *tty = container_of(work, struct tty_struct, hangup_work); - __tty_hangup(tty); + __tty_hangup(tty, 0); } /** @@ -717,7 +743,7 @@ void tty_vhangup(struct tty_struct *tty) printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf)); #endif - __tty_hangup(tty); + __tty_hangup(tty, 0); } EXPORT_SYMBOL(tty_vhangup); @@ -741,6 +767,27 @@ void tty_vhangup_self(void) } /** + * tty_vhangup_session - hangup session leader exit + * @tty: tty to hangup + * + * The session leader is exiting and hanging up its controlling terminal. + * Every process in the foreground process group is signalled SIGHUP. + * + * We do this synchronously so that when the syscall returns the process + * is complete. That guarantee is necessary for security reasons. + */ + +static void tty_vhangup_session(struct tty_struct *tty) +{ +#ifdef TTY_DEBUG_HANGUP + char buf[64]; + + printk(KERN_DEBUG "%s vhangup session...\n", tty_name(tty, buf)); +#endif + __tty_hangup(tty, 1); +} + +/** * tty_hung_up_p - was tty hung up * @filp: file pointer of tty * @@ -797,18 +844,18 @@ void disassociate_ctty(int on_exit) tty = get_current_tty(); if (tty) { - struct pid *tty_pgrp = get_pid(tty->pgrp); - if (on_exit) { - if (tty->driver->type != TTY_DRIVER_TYPE_PTY) - tty_vhangup(tty); - } - tty_kref_put(tty); - if (tty_pgrp) { - kill_pgrp(tty_pgrp, SIGHUP, on_exit); - if (!on_exit) + if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) { + tty_vhangup_session(tty); + } else { + struct pid *tty_pgrp = tty_get_pgrp(tty); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, SIGHUP, on_exit); kill_pgrp(tty_pgrp, SIGCONT, on_exit); - put_pid(tty_pgrp); + put_pid(tty_pgrp); + } } + tty_kref_put(tty); + } else if (on_exit) { struct pid *old_pgrp; spin_lock_irq(¤t->sighand->siglock); @@ -941,6 +988,14 @@ void start_tty(struct tty_struct *tty) EXPORT_SYMBOL(start_tty); +/* We limit tty time update visibility to every 8 seconds or so. */ +static void tty_update_time(struct timespec *time) +{ + unsigned long sec = get_seconds() & ~7; + if ((long)(sec - time->tv_sec) > 0) + time->tv_sec = sec; +} + /** * tty_read - read method for tty device files * @file: pointer to tty file @@ -960,10 +1015,11 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int i; + struct inode *inode = file_inode(file); struct tty_struct *tty = file_tty(file); struct tty_ldisc *ld; - if (tty_paranoia_check(tty, file_inode(file), "tty_read")) + if (tty_paranoia_check(tty, inode, "tty_read")) return -EIO; if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; @@ -977,6 +1033,9 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, i = -EIO; tty_ldisc_deref(ld); + if (i > 0) + tty_update_time(&inode->i_atime); + return i; } @@ -1077,8 +1136,10 @@ static inline ssize_t do_tty_write( break; cond_resched(); } - if (written) + if (written) { + tty_update_time(&file_inode(file)->i_mtime); ret = written; + } out: tty_write_unlock(tty); return ret; @@ -1344,9 +1405,7 @@ static int tty_reopen(struct tty_struct *tty) } tty->count++; - mutex_lock(&tty->ldisc_mutex); WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); - mutex_unlock(&tty->ldisc_mutex); return 0; } @@ -1463,6 +1522,17 @@ void tty_free_termios(struct tty_struct *tty) } EXPORT_SYMBOL(tty_free_termios); +/** + * tty_flush_works - flush all works of a tty + * @tty: tty device to flush works for + * + * Sync flush all works belonging to @tty. + */ +static void tty_flush_works(struct tty_struct *tty) +{ + flush_work(&tty->SAK_work); + flush_work(&tty->hangup_work); +} /** * release_one_tty - release tty structure memory @@ -1548,6 +1618,7 @@ static void release_tty(struct tty_struct *tty, int idx) tty_free_termios(tty); tty_driver_remove_tty(tty->driver, tty); tty->port->itty = NULL; + cancel_work_sync(&tty->port->buf.work); if (tty->link) tty_kref_put(tty->link); @@ -1777,12 +1848,21 @@ int tty_release(struct inode *inode, struct file *filp) return 0; #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__); + printk(KERN_DEBUG "%s: %s: final close\n", __func__, tty_name(tty, buf)); #endif /* * Ask the line discipline code to release its structures */ tty_ldisc_release(tty, o_tty); + + /* Wait for pending work before tty destruction commmences */ + tty_flush_works(tty); + if (o_tty) + tty_flush_works(o_tty); + +#ifdef TTY_DEBUG_HANGUP + printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, tty_name(tty, buf)); +#endif /* * The release_tty function takes care of the details of clearing * the slots and preserving the termios structure. The tty_unlock_pair diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index d58b92cc187..3500d411414 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -106,6 +106,7 @@ void tty_throttle(struct tty_struct *tty) if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && tty->ops->throttle) tty->ops->throttle(tty); + tty->flow_change = 0; mutex_unlock(&tty->termios_mutex); } EXPORT_SYMBOL(tty_throttle); @@ -129,11 +130,74 @@ void tty_unthrottle(struct tty_struct *tty) if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->ops->unthrottle) tty->ops->unthrottle(tty); + tty->flow_change = 0; mutex_unlock(&tty->termios_mutex); } EXPORT_SYMBOL(tty_unthrottle); /** + * tty_throttle_safe - flow control + * @tty: terminal + * + * Similar to tty_throttle() but will only attempt throttle + * if tty->flow_change is TTY_THROTTLE_SAFE. Prevents an accidental + * throttle due to race conditions when throttling is conditional + * on factors evaluated prior to throttling. + * + * Returns 0 if tty is throttled (or was already throttled) + */ + +int tty_throttle_safe(struct tty_struct *tty) +{ + int ret = 0; + + mutex_lock(&tty->termios_mutex); + if (!test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty->flow_change != TTY_THROTTLE_SAFE) + ret = 1; + else { + set_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->throttle) + tty->ops->throttle(tty); + } + } + mutex_unlock(&tty->termios_mutex); + + return ret; +} + +/** + * tty_unthrottle_safe - flow control + * @tty: terminal + * + * Similar to tty_unthrottle() but will only attempt unthrottle + * if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental + * unthrottle due to race conditions when unthrottling is conditional + * on factors evaluated prior to unthrottling. + * + * Returns 0 if tty is unthrottled (or was already unthrottled) + */ + +int tty_unthrottle_safe(struct tty_struct *tty) +{ + int ret = 0; + + mutex_lock(&tty->termios_mutex); + if (test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty->flow_change != TTY_UNTHROTTLE_SAFE) + ret = 1; + else { + clear_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->unthrottle) + tty->ops->unthrottle(tty); + } + } + mutex_unlock(&tty->termios_mutex); + + return ret; +} + +/** * tty_wait_until_sent - wait for I/O to finish * @tty: tty we are waiting for * @timeout: how long we will wait @@ -415,34 +479,6 @@ void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) EXPORT_SYMBOL_GPL(tty_encode_baud_rate); /** - * tty_get_baud_rate - get tty bit rates - * @tty: tty to query - * - * Returns the baud rate as an integer for this terminal. The - * termios lock must be held by the caller and the terminal bit - * flags may be updated. - * - * Locking: none - */ - -speed_t tty_get_baud_rate(struct tty_struct *tty) -{ - speed_t baud = tty_termios_baud_rate(&tty->termios); - - if (baud == 38400 && tty->alt_speed) { - if (!tty->warned) { - printk(KERN_WARNING "Use of setserial/setrocket to " - "set SPD_* flags is deprecated\n"); - tty->warned = 1; - } - baud = tty->alt_speed; - } - - return baud; -} -EXPORT_SYMBOL(tty_get_baud_rate); - -/** * tty_termios_copy_hw - copy hardware settings * @new: New termios * @old: Old termios @@ -1086,14 +1122,12 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, } EXPORT_SYMBOL_GPL(tty_mode_ioctl); -int tty_perform_flush(struct tty_struct *tty, unsigned long arg) + +/* Caller guarantees ldisc reference is held */ +static int __tty_perform_flush(struct tty_struct *tty, unsigned long arg) { - struct tty_ldisc *ld; - int retval = tty_check_change(tty); - if (retval) - return retval; + struct tty_ldisc *ld = tty->ldisc; - ld = tty_ldisc_ref_wait(tty); switch (arg) { case TCIFLUSH: if (ld && ld->ops->flush_buffer) { @@ -1111,12 +1145,24 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg) tty_driver_flush_buffer(tty); break; default: - tty_ldisc_deref(ld); return -EINVAL; } - tty_ldisc_deref(ld); return 0; } + +int tty_perform_flush(struct tty_struct *tty, unsigned long arg) +{ + struct tty_ldisc *ld; + int retval = tty_check_change(tty); + if (retval) + return retval; + + ld = tty_ldisc_ref_wait(tty); + retval = __tty_perform_flush(tty, arg); + if (ld) + tty_ldisc_deref(ld); + return retval; +} EXPORT_SYMBOL_GPL(tty_perform_flush); int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, @@ -1155,7 +1201,7 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, } return 0; case TCFLSH: - return tty_perform_flush(tty, arg); + return __tty_perform_flush(tty, arg); default: /* Try the mode commands */ return tty_mode_ioctl(tty, file, cmd, arg); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index d794087c327..1afe192bef6 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -20,6 +20,17 @@ #include <linux/uaccess.h> #include <linux/ratelimit.h> +#undef LDISC_DEBUG_HANGUP + +#ifdef LDISC_DEBUG_HANGUP +#define tty_ldisc_debug(tty, f, args...) ({ \ + char __b[64]; \ + printk(KERN_DEBUG "%s: %s: " f, __func__, tty_name(tty, __b), ##args); \ +}) +#else +#define tty_ldisc_debug(tty, f, args...) +#endif + /* * This guards the refcounted line discipline lists. The lock * must be taken with irqs off because there are hangup path @@ -31,44 +42,6 @@ static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); /* Line disc dispatch table */ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; -static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld) -{ - if (ld) - atomic_inc(&ld->users); - return ld; -} - -static void put_ldisc(struct tty_ldisc *ld) -{ - unsigned long flags; - - if (WARN_ON_ONCE(!ld)) - return; - - /* - * If this is the last user, free the ldisc, and - * release the ldisc ops. - * - * We really want an "atomic_dec_and_raw_lock_irqsave()", - * but we don't have it, so this does it by hand. - */ - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); - if (atomic_dec_and_test(&ld->users)) { - struct tty_ldisc_ops *ldo = ld->ops; - - ldo->refcount--; - module_put(ldo->owner); - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - kfree(ld); - return; - } - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - if (waitqueue_active(&ld->wq_idle)) - wake_up(&ld->wq_idle); -} - /** * tty_register_ldisc - install a line discipline * @disc: ldisc number @@ -206,6 +179,29 @@ static struct tty_ldisc *tty_ldisc_get(int disc) return ld; } +/** + * tty_ldisc_put - release the ldisc + * + * Complement of tty_ldisc_get(). + */ +static inline void tty_ldisc_put(struct tty_ldisc *ld) +{ + unsigned long flags; + + if (WARN_ON_ONCE(!ld)) + return; + + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + + /* unreleased reader reference(s) will cause this WARN */ + WARN_ON(!atomic_dec_and_test(&ld->users)); + + ld->ops->refcount--; + module_put(ld->ops->owner); + kfree(ld); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} + static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) { return (*pos < NR_LDISCS) ? pos : NULL; @@ -255,24 +251,6 @@ const struct file_operations tty_ldiscs_proc_fops = { }; /** - * tty_ldisc_assign - set ldisc on a tty - * @tty: tty to assign - * @ld: line discipline - * - * Install an instance of a line discipline into a tty structure. The - * ldisc must have a reference count above zero to ensure it remains. - * The tty instance refcount starts at zero. - * - * Locking: - * Caller must hold references - */ - -static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) -{ - tty->ldisc = ld; -} - -/** * tty_ldisc_try - internal helper * @tty: the tty * @@ -289,10 +267,13 @@ static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) unsigned long flags; struct tty_ldisc *ld; + /* FIXME: this allows reference acquire after TTY_LDISC is cleared */ raw_spin_lock_irqsave(&tty_ldisc_lock, flags); ld = NULL; - if (test_bit(TTY_LDISC, &tty->flags)) - ld = get_ldisc(tty->ldisc); + if (test_bit(TTY_LDISC, &tty->flags) && tty->ldisc) { + ld = tty->ldisc; + atomic_inc(&ld->users); + } raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ld; } @@ -352,14 +333,23 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref); void tty_ldisc_deref(struct tty_ldisc *ld) { - put_ldisc(ld); -} -EXPORT_SYMBOL_GPL(tty_ldisc_deref); + unsigned long flags; -static inline void tty_ldisc_put(struct tty_ldisc *ld) -{ - put_ldisc(ld); + if (WARN_ON_ONCE(!ld)) + return; + + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + /* + * WARNs if one-too-many reader references were released + * - the last reference must be released with tty_ldisc_put + */ + WARN_ON(atomic_dec_and_test(&ld->users)); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + if (waitqueue_active(&ld->wq_idle)) + wake_up(&ld->wq_idle); } +EXPORT_SYMBOL_GPL(tty_ldisc_deref); /** * tty_ldisc_enable - allow ldisc use @@ -373,8 +363,9 @@ static inline void tty_ldisc_put(struct tty_ldisc *ld) * Clearing directly is allowed. */ -void tty_ldisc_enable(struct tty_struct *tty) +static void tty_ldisc_enable(struct tty_struct *tty) { + clear_bit(TTY_LDISC_HALTED, &tty->flags); set_bit(TTY_LDISC, &tty->flags); clear_bit(TTY_LDISC_CHANGING, &tty->flags); wake_up(&tty_ldisc_wait); @@ -479,7 +470,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) /* There is an outstanding reference here so this is safe */ old = tty_ldisc_get(old->ops->num); WARN_ON(IS_ERR(old)); - tty_ldisc_assign(tty, old); + tty->ldisc = old; tty_set_termios_ldisc(tty, old->ops->num); if (tty_ldisc_open(tty, old) < 0) { tty_ldisc_put(old); @@ -487,7 +478,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) new_ldisc = tty_ldisc_get(N_TTY); if (IS_ERR(new_ldisc)) panic("n_tty: get"); - tty_ldisc_assign(tty, new_ldisc); + tty->ldisc = new_ldisc; tty_set_termios_ldisc(tty, N_TTY); r = tty_ldisc_open(tty, new_ldisc); if (r < 0) @@ -498,52 +489,98 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) } /** + * tty_ldisc_wait_idle - wait for the ldisc to become idle + * @tty: tty to wait for + * @timeout: for how long to wait at most + * + * Wait for the line discipline to become idle. The discipline must + * have been halted for this to guarantee it remains idle. + */ +static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) +{ + long ret; + ret = wait_event_timeout(tty->ldisc->wq_idle, + atomic_read(&tty->ldisc->users) == 1, timeout); + return ret > 0 ? 0 : -EBUSY; +} + +/** * tty_ldisc_halt - shut down the line discipline * @tty: tty device + * @o_tty: paired pty device (can be NULL) + * @timeout: # of jiffies to wait for ldisc refs to be released * - * Shut down the line discipline and work queue for this tty device. - * The TTY_LDISC flag being cleared ensures no further references can - * be obtained while the delayed work queue halt ensures that no more - * data is fed to the ldisc. + * Shut down the line discipline and work queue for this tty device and + * its paired pty (if exists). Clearing the TTY_LDISC flag ensures + * no further references can be obtained, while waiting for existing + * references to be released ensures no more data is fed to the ldisc. * * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex) * in order to make sure any currently executing ldisc work is also * flushed. */ -static int tty_ldisc_halt(struct tty_struct *tty) +static int tty_ldisc_halt(struct tty_struct *tty, struct tty_struct *o_tty, + long timeout) { + int retval; + clear_bit(TTY_LDISC, &tty->flags); - return cancel_work_sync(&tty->port->buf.work); -} + if (o_tty) + clear_bit(TTY_LDISC, &o_tty->flags); -/** - * tty_ldisc_flush_works - flush all works of a tty - * @tty: tty device to flush works for - * - * Sync flush all works belonging to @tty. - */ -static void tty_ldisc_flush_works(struct tty_struct *tty) -{ - flush_work(&tty->hangup_work); - flush_work(&tty->SAK_work); - flush_work(&tty->port->buf.work); + retval = tty_ldisc_wait_idle(tty, timeout); + if (!retval && o_tty) + retval = tty_ldisc_wait_idle(o_tty, timeout); + if (retval) + return retval; + + set_bit(TTY_LDISC_HALTED, &tty->flags); + if (o_tty) + set_bit(TTY_LDISC_HALTED, &o_tty->flags); + + return 0; } /** - * tty_ldisc_wait_idle - wait for the ldisc to become idle - * @tty: tty to wait for - * @timeout: for how long to wait at most + * tty_ldisc_hangup_halt - halt the line discipline for hangup + * @tty: tty being hung up * - * Wait for the line discipline to become idle. The discipline must - * have been halted for this to guarantee it remains idle. + * Shut down the line discipline and work queue for the tty device + * being hungup. Clear the TTY_LDISC flag to ensure no further + * references can be obtained and wait for remaining references to be + * released to ensure no more data is fed to this ldisc. + * Caller must hold legacy and ->ldisc_mutex. + * + * NB: tty_set_ldisc() is prevented from changing the ldisc concurrently + * with this function by checking the TTY_HUPPING flag. */ -static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) +static bool tty_ldisc_hangup_halt(struct tty_struct *tty) { - long ret; - ret = wait_event_timeout(tty->ldisc->wq_idle, - atomic_read(&tty->ldisc->users) == 1, timeout); - return ret > 0 ? 0 : -EBUSY; + char cur_n[TASK_COMM_LEN], tty_n[64]; + long timeout = 3 * HZ; + + clear_bit(TTY_LDISC, &tty->flags); + + if (tty->ldisc) { /* Not yet closed */ + tty_unlock(tty); + + while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { + timeout = MAX_SCHEDULE_TIMEOUT; + printk_ratelimited(KERN_WARNING + "%s: waiting (%s) for %s took too long, but we keep waiting...\n", + __func__, get_task_comm(cur_n, current), + tty_name(tty, tty_n)); + } + + set_bit(TTY_LDISC_HALTED, &tty->flags); + + /* must reacquire both locks and preserve lock order */ + mutex_unlock(&tty->ldisc_mutex); + tty_lock(tty); + mutex_lock(&tty->ldisc_mutex); + } + return !!tty->ldisc; } /** @@ -563,7 +600,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) { int retval; struct tty_ldisc *o_ldisc, *new_ldisc; - int work, o_work = 0; struct tty_struct *o_tty; new_ldisc = tty_ldisc_get(ldisc); @@ -589,15 +625,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) return 0; } - tty_unlock(tty); - /* - * Problem: What do we do if this blocks ? - * We could deadlock here - */ - - tty_wait_until_sent(tty, 0); - - tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* @@ -637,20 +664,16 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) * parallel to the change and re-referencing the tty. */ - work = tty_ldisc_halt(tty); - if (o_tty) - o_work = tty_ldisc_halt(o_tty); + retval = tty_ldisc_halt(tty, o_tty, 5 * HZ); /* - * Wait for ->hangup_work and ->buf.work handlers to terminate. + * Wait for hangup to complete, if pending. * We must drop the mutex here in case a hangup is also in process. */ mutex_unlock(&tty->ldisc_mutex); - tty_ldisc_flush_works(tty); - - retval = tty_ldisc_wait_idle(tty, 5 * HZ); + flush_work(&tty->hangup_work); tty_lock(tty); mutex_lock(&tty->ldisc_mutex); @@ -675,7 +698,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_ldisc_close(tty, o_ldisc); /* Now set up the new line discipline. */ - tty_ldisc_assign(tty, new_ldisc); + tty->ldisc = new_ldisc; tty_set_termios_ldisc(tty, ldisc); retval = tty_ldisc_open(tty, new_ldisc); @@ -705,10 +728,10 @@ enable: /* Restart the work queue in case no characters kick it off. Safe if already running */ - if (work) - schedule_work(&tty->port->buf.work); - if (o_work) + schedule_work(&tty->port->buf.work); + if (o_tty) schedule_work(&o_tty->port->buf.work); + mutex_unlock(&tty->ldisc_mutex); tty_unlock(tty); return retval; @@ -749,11 +772,10 @@ static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc) tty_ldisc_close(tty, tty->ldisc); tty_ldisc_put(tty->ldisc); - tty->ldisc = NULL; /* * Switch the line discipline back */ - tty_ldisc_assign(tty, ld); + tty->ldisc = ld; tty_set_termios_ldisc(tty, ldisc); return 0; @@ -780,6 +802,8 @@ void tty_ldisc_hangup(struct tty_struct *tty) int reset = tty->driver->flags & TTY_DRIVER_RESET_TERMIOS; int err = 0; + tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); + /* * FIXME! What are the locking issues here? This may me overdoing * things... This question is especially important now that we've @@ -812,40 +836,12 @@ void tty_ldisc_hangup(struct tty_struct *tty) */ mutex_lock(&tty->ldisc_mutex); - /* - * this is like tty_ldisc_halt, but we need to give up - * the BTM before calling cancel_work_sync, which may - * need to wait for another function taking the BTM - */ - clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(tty); - cancel_work_sync(&tty->port->buf.work); - mutex_unlock(&tty->ldisc_mutex); -retry: - tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - - /* At this point we have a closed ldisc and we want to - reopen it. We could defer this to the next open but - it means auditing a lot of other paths so this is - a FIXME */ - if (tty->ldisc) { /* Not yet closed */ - if (atomic_read(&tty->ldisc->users) != 1) { - char cur_n[TASK_COMM_LEN], tty_n[64]; - long timeout = 3 * HZ; - tty_unlock(tty); - - while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { - timeout = MAX_SCHEDULE_TIMEOUT; - printk_ratelimited(KERN_WARNING - "%s: waiting (%s) for %s took too long, but we keep waiting...\n", - __func__, get_task_comm(cur_n, current), - tty_name(tty, tty_n)); - } - mutex_unlock(&tty->ldisc_mutex); - goto retry; - } + if (tty_ldisc_hangup_halt(tty)) { + /* At this point we have a halted ldisc; we want to close it and + reopen a new ldisc. We could defer the reopen to the next + open but it means auditing a lot of other paths so this is + a FIXME */ if (reset == 0) { if (!tty_ldisc_reinit(tty, tty->termios.c_line)) @@ -864,6 +860,8 @@ retry: mutex_unlock(&tty->ldisc_mutex); if (reset) tty_reset_termios(tty); + + tty_ldisc_debug(tty, "re-opened ldisc: %p\n", tty->ldisc); } /** @@ -899,11 +897,6 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) static void tty_ldisc_kill(struct tty_struct *tty) { - /* There cannot be users from userspace now. But there still might be - * drivers holding a reference via tty_ldisc_ref. Do not steal them the - * ldisc until they are done. */ - tty_ldisc_wait_idle(tty, MAX_SCHEDULE_TIMEOUT); - mutex_lock(&tty->ldisc_mutex); /* * Now kill off the ldisc @@ -931,18 +924,13 @@ static void tty_ldisc_kill(struct tty_struct *tty) void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) { /* - * Prevent flush_to_ldisc() from rescheduling the work for later. Then - * kill any delayed work. As this is the final close it does not - * race with the set_ldisc code path. + * Shutdown this line discipline. As this is the final close, + * it does not race with the set_ldisc code path. */ - tty_ldisc_halt(tty); - if (o_tty) - tty_ldisc_halt(o_tty); + tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); - tty_ldisc_flush_works(tty); - if (o_tty) - tty_ldisc_flush_works(o_tty); + tty_ldisc_halt(tty, o_tty, MAX_SCHEDULE_TIMEOUT); tty_lock_pair(tty, o_tty); /* This will need doing differently if we need to lock */ @@ -953,6 +941,8 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) tty_unlock_pair(tty, o_tty); /* And the memory resources remaining (buffers, termios) will be disposed of when the kref hits zero */ + + tty_ldisc_debug(tty, "ldisc closed\n"); } /** @@ -968,7 +958,7 @@ void tty_ldisc_init(struct tty_struct *tty) struct tty_ldisc *ld = tty_ldisc_get(N_TTY); if (IS_ERR(ld)) panic("n_tty: init_tty"); - tty_ldisc_assign(tty, ld); + tty->ldisc = ld; } /** @@ -980,8 +970,8 @@ void tty_ldisc_init(struct tty_struct *tty) */ void tty_ldisc_deinit(struct tty_struct *tty) { - put_ldisc(tty->ldisc); - tty_ldisc_assign(tty, NULL); + tty_ldisc_put(tty->ldisc); + tty->ldisc = NULL; } void tty_ldisc_begin(void) diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index b7ff59d3db8..121aeb9393e 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -132,6 +132,7 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf); */ void tty_port_destroy(struct tty_port *port) { + cancel_work_sync(&port->buf.work); tty_buffer_free_all(port); } EXPORT_SYMBOL(tty_port_destroy); @@ -196,12 +197,24 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) } EXPORT_SYMBOL(tty_port_tty_set); -static void tty_port_shutdown(struct tty_port *port) +static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) { mutex_lock(&port->mutex); - if (port->ops->shutdown && !port->console && - test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) + if (port->console) + goto out; + + if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { + /* + * Drop DTR/RTS if HUPCL is set. This causes any attached + * modem to hang up the line. + */ + if (tty && C_HUPCL(tty)) + tty_port_lower_dtr_rts(port); + + if (port->ops->shutdown) port->ops->shutdown(port); + } +out: mutex_unlock(&port->mutex); } @@ -215,24 +228,58 @@ static void tty_port_shutdown(struct tty_port *port) void tty_port_hangup(struct tty_port *port) { + struct tty_struct *tty; unsigned long flags; spin_lock_irqsave(&port->lock, flags); port->count = 0; port->flags &= ~ASYNC_NORMAL_ACTIVE; - if (port->tty) { - set_bit(TTY_IO_ERROR, &port->tty->flags); - tty_kref_put(port->tty); - } + tty = port->tty; + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); + tty_port_shutdown(port, tty); + tty_kref_put(tty); wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->delta_msr_wait); - tty_port_shutdown(port); } EXPORT_SYMBOL(tty_port_hangup); /** + * tty_port_tty_hangup - helper to hang up a tty + * + * @port: tty port + * @check_clocal: hang only ttys with CLOCAL unset? + */ +void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) +{ + struct tty_struct *tty = tty_port_tty_get(port); + + if (tty && (!check_clocal || !C_CLOCAL(tty))) { + tty_hangup(tty); + tty_kref_put(tty); + } +} +EXPORT_SYMBOL_GPL(tty_port_tty_hangup); + +/** + * tty_port_tty_wakeup - helper to wake up a tty + * + * @port: tty port + */ +void tty_port_tty_wakeup(struct tty_port *port) +{ + struct tty_struct *tty = tty_port_tty_get(port); + + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } +} +EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); + +/** * tty_port_carrier_raised - carrier raised check * @port: tty port * @@ -350,7 +397,7 @@ int tty_port_block_til_ready(struct tty_port *port, while (1) { /* Indicate we are open */ - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); @@ -395,6 +442,20 @@ int tty_port_block_til_ready(struct tty_port *port, } EXPORT_SYMBOL(tty_port_block_til_ready); +static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) +{ + unsigned int bps = tty_get_baud_rate(tty); + long timeout; + + if (bps > 1200) { + timeout = (HZ * 10 * port->drain_delay) / bps; + timeout = max_t(long, timeout, HZ / 10); + } else { + timeout = 2 * HZ; + } + schedule_timeout_interruptible(timeout); +} + int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp) { @@ -427,31 +488,19 @@ int tty_port_close_start(struct tty_port *port, set_bit(ASYNCB_CLOSING, &port->flags); tty->closing = 1; spin_unlock_irqrestore(&port->lock, flags); - /* Don't block on a stalled port, just pull the chain */ - if (tty->flow_stopped) - tty_driver_flush_buffer(tty); - if (test_bit(ASYNCB_INITIALIZED, &port->flags) && - port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent_from_close(tty, port->closing_wait); - if (port->drain_delay) { - unsigned int bps = tty_get_baud_rate(tty); - long timeout; - - if (bps > 1200) - timeout = max_t(long, - (HZ * 10 * port->drain_delay) / bps, HZ / 10); - else - timeout = 2 * HZ; - schedule_timeout_interruptible(timeout); + + if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { + /* Don't block on a stalled port, just pull the chain */ + if (tty->flow_stopped) + tty_driver_flush_buffer(tty); + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent_from_close(tty, port->closing_wait); + if (port->drain_delay) + tty_port_drain_delay(port, tty); } /* Flush the ldisc buffering */ tty_ldisc_flush(tty); - /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to - hang up the line */ - if (tty->termios.c_cflag & HUPCL) - tty_port_lower_dtr_rts(port); - /* Don't call port->drop for the last reference. Callers will want to drop the last active reference in ->shutdown() or the tty shutdown path */ @@ -486,7 +535,7 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty, { if (tty_port_close_start(port, tty, filp) == 0) return; - tty_port_shutdown(port); + tty_port_shutdown(port, tty); set_bit(TTY_IO_ERROR, &tty->flags); tty_port_close_end(port, tty); tty_port_tty_set(port, NULL); diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 248381b3072..2978ca596a7 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -194,8 +194,7 @@ static void set_inverse_transl(struct vc_data *conp, struct uni_pagedir *p, int q = p->inverse_translations[i]; if (!q) { - q = p->inverse_translations[i] = (unsigned char *) - kmalloc(MAX_GLYPH, GFP_KERNEL); + q = p->inverse_translations[i] = kmalloc(MAX_GLYPH, GFP_KERNEL); if (!q) return; } memset(q, 0, MAX_GLYPH); diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index e4ca345873c..d7799deacb2 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -93,7 +93,7 @@ vcs_poll_data_free(struct vcs_poll_data *poll) static struct vcs_poll_data * vcs_poll_data_get(struct file *file) { - struct vcs_poll_data *poll = file->private_data; + struct vcs_poll_data *poll = file->private_data, *kill = NULL; if (poll) return poll; @@ -122,10 +122,12 @@ vcs_poll_data_get(struct file *file) file->private_data = poll; } else { /* someone else raced ahead of us */ - vcs_poll_data_free(poll); + kill = poll; poll = file->private_data; } spin_unlock(&file->f_lock); + if (kill) + vcs_poll_data_free(kill); return poll; } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index fbd447b390f..740202d8a5c 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -779,7 +779,6 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ con_set_default_unimap(vc); vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); if (!vc->vc_screenbuf) { - tty_port_destroy(&vc->port); kfree(vc); vc_cons[currcons].d = NULL; return -ENOMEM; @@ -986,26 +985,25 @@ static int vt_resize(struct tty_struct *tty, struct winsize *ws) return ret; } -void vc_deallocate(unsigned int currcons) +struct vc_data *vc_deallocate(unsigned int currcons) { + struct vc_data *vc = NULL; + WARN_CONSOLE_UNLOCKED(); if (vc_cons_allocated(currcons)) { - struct vc_data *vc = vc_cons[currcons].d; - struct vt_notifier_param param = { .vc = vc }; + struct vt_notifier_param param; + param.vc = vc = vc_cons[currcons].d; atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, ¶m); vcs_remove_sysfs(currcons); vc->vc_sw->con_deinit(vc); put_pid(vc->vt_pid); module_put(vc->vc_sw->owner); kfree(vc->vc_screenbuf); - if (currcons >= MIN_NR_CONSOLES) { - tty_port_destroy(&vc->port); - kfree(vc); - } vc_cons[currcons].d = NULL; } + return vc; } /* diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 98ff1735eaf..fc2c06c66e8 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -283,6 +283,51 @@ do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_ return 0; } +/* deallocate a single console, if possible (leave 0) */ +static int vt_disallocate(unsigned int vc_num) +{ + struct vc_data *vc = NULL; + int ret = 0; + + if (!vc_num) + return 0; + + console_lock(); + if (VT_BUSY(vc_num)) + ret = -EBUSY; + else + vc = vc_deallocate(vc_num); + console_unlock(); + + if (vc && vc_num >= MIN_NR_CONSOLES) { + tty_port_destroy(&vc->port); + kfree(vc); + } + + return ret; +} + +/* deallocate all unused consoles, but leave 0 */ +static void vt_disallocate_all(void) +{ + struct vc_data *vc[MAX_NR_CONSOLES]; + int i; + + console_lock(); + for (i = 1; i < MAX_NR_CONSOLES; i++) + if (!VT_BUSY(i)) + vc[i] = vc_deallocate(i); + else + vc[i] = NULL; + console_unlock(); + + for (i = 1; i < MAX_NR_CONSOLES; i++) { + if (vc[i] && i >= MIN_NR_CONSOLES) { + tty_port_destroy(&vc[i]->port); + kfree(vc[i]); + } + } +} /* @@ -769,24 +814,10 @@ int vt_ioctl(struct tty_struct *tty, ret = -ENXIO; break; } - if (arg == 0) { - /* deallocate all unused consoles, but leave 0 */ - console_lock(); - for (i=1; i<MAX_NR_CONSOLES; i++) - if (! VT_BUSY(i)) - vc_deallocate(i); - console_unlock(); - } else { - /* deallocate a single console, if possible */ - arg--; - if (VT_BUSY(arg)) - ret = -EBUSY; - else if (arg) { /* leave 0 */ - console_lock(); - vc_deallocate(arg); - console_unlock(); - } - } + if (arg == 0) + vt_disallocate_all(); + else + ret = vt_disallocate(--arg); break; case VT_RESIZE: |