diff options
Diffstat (limited to 'drivers/tty/serial/serial_core.c')
-rw-r--r-- | drivers/tty/serial/serial_core.c | 682 |
1 files changed, 319 insertions, 363 deletions
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index c476d884356d..1f7708a91fc6 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -75,22 +75,23 @@ static inline void uart_port_deref(struct uart_port *uport) wake_up(&uport->state->remove_wait); } -#define uart_port_lock(state, flags) \ - ({ \ - struct uart_port *__uport = uart_port_ref(state); \ - if (__uport) \ - uart_port_lock_irqsave(__uport, &flags); \ - __uport; \ - }) - -#define uart_port_unlock(uport, flags) \ - ({ \ - struct uart_port *__uport = uport; \ - if (__uport) { \ - uart_port_unlock_irqrestore(__uport, flags); \ - uart_port_deref(__uport); \ - } \ - }) +static inline struct uart_port *uart_port_ref_lock(struct uart_state *state, unsigned long *flags) +{ + struct uart_port *uport = uart_port_ref(state); + + if (uport) + uart_port_lock_irqsave(uport, flags); + + return uport; +} + +static inline void uart_port_unlock_deref(struct uart_port *uport, unsigned long flags) +{ + if (uport) { + uart_port_unlock_irqrestore(uport, flags); + uart_port_deref(uport); + } +} static inline struct uart_port *uart_port_check(struct uart_state *state) { @@ -127,10 +128,10 @@ static void uart_stop(struct tty_struct *tty) struct uart_port *port; unsigned long flags; - port = uart_port_lock(state, flags); + port = uart_port_ref_lock(state, &flags); if (port) port->ops->stop_tx(port); - uart_port_unlock(port, flags); + uart_port_unlock_deref(port, flags); } static void __uart_start(struct uart_state *state) @@ -168,9 +169,9 @@ static void uart_start(struct tty_struct *tty) struct uart_port *port; unsigned long flags; - port = uart_port_lock(state, flags); + port = uart_port_ref_lock(state, &flags); __uart_start(state); - uart_port_unlock(port, flags); + uart_port_unlock_deref(port, flags); } static void @@ -243,25 +244,12 @@ static void uart_change_line_settings(struct tty_struct *tty, struct uart_state uart_port_unlock_irq(uport); } -/* - * Startup the port. This will be called once per open. All calls - * will be serialised by the per-port mutex. - */ -static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, - bool init_hw) +static int uart_alloc_xmit_buf(struct tty_port *port) { - struct uart_port *uport = uart_port_check(state); + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport; unsigned long flags; unsigned long page; - int retval = 0; - - if (uport->type == PORT_UNKNOWN) - return 1; - - /* - * Make sure the device is in D0 state. - */ - uart_change_pm(state, UART_PM_STATE_ON); /* * Initialise and allocate the transmit and temporary @@ -271,20 +259,68 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, if (!page) return -ENOMEM; - uart_port_lock(state, flags); - if (!state->xmit.buf) { - state->xmit.buf = (unsigned char *) page; - uart_circ_clear(&state->xmit); - uart_port_unlock(uport, flags); + uport = uart_port_ref_lock(state, &flags); + if (!state->port.xmit_buf) { + state->port.xmit_buf = (unsigned char *)page; + kfifo_init(&state->port.xmit_fifo, state->port.xmit_buf, + PAGE_SIZE); + uart_port_unlock_deref(uport, flags); } else { - uart_port_unlock(uport, flags); + uart_port_unlock_deref(uport, flags); /* * Do not free() the page under the port lock, see - * uart_shutdown(). + * uart_free_xmit_buf(). */ free_page(page); } + return 0; +} + +static void uart_free_xmit_buf(struct tty_port *port) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport; + unsigned long flags; + char *xmit_buf; + + /* + * Do not free() the transmit buffer page under the port lock since + * this can create various circular locking scenarios. For instance, + * console driver may need to allocate/free a debug object, which + * can end up in printk() recursion. + */ + uport = uart_port_ref_lock(state, &flags); + xmit_buf = port->xmit_buf; + port->xmit_buf = NULL; + INIT_KFIFO(port->xmit_fifo); + uart_port_unlock_deref(uport, flags); + + free_page((unsigned long)xmit_buf); +} + +/* + * Startup the port. This will be called once per open. All calls + * will be serialised by the per-port mutex. + */ +static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, + bool init_hw) +{ + struct uart_port *uport = uart_port_check(state); + int retval; + + if (uport->type == PORT_UNKNOWN) + return 1; + + /* + * Make sure the device is in D0 state. + */ + uart_change_pm(state, UART_PM_STATE_ON); + + retval = uart_alloc_xmit_buf(&state->port); + if (retval) + return retval; + retval = uport->ops->startup(uport); if (retval == 0) { if (uart_console(uport) && uport->cons->cflag) { @@ -356,8 +392,6 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) { struct uart_port *uport = uart_port_check(state); struct tty_port *port = &state->port; - unsigned long flags; - char *xmit_buf = NULL; /* * Set the TTY IO error marker @@ -374,14 +408,16 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) /* * Turn off DTR and RTS early. */ - if (uport && uart_console(uport) && tty) { - uport->cons->cflag = tty->termios.c_cflag; - uport->cons->ispeed = tty->termios.c_ispeed; - uport->cons->ospeed = tty->termios.c_ospeed; - } + if (uport) { + if (uart_console(uport) && tty) { + uport->cons->cflag = tty->termios.c_cflag; + uport->cons->ispeed = tty->termios.c_ispeed; + uport->cons->ospeed = tty->termios.c_ospeed; + } - if (!tty || C_HUPCL(tty)) - uart_port_dtr_rts(uport, false); + if (!tty || C_HUPCL(tty)) + uart_port_dtr_rts(uport, false); + } uart_port_shutdown(port); } @@ -393,18 +429,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) */ tty_port_set_suspended(port, false); - /* - * Do not free() the transmit buffer page under the port lock since - * this can create various circular locking scenarios. For instance, - * console driver may need to allocate/free a debug object, which - * can endup in printk() recursion. - */ - uart_port_lock(state, flags); - xmit_buf = state->xmit.buf; - state->xmit.buf = NULL; - uart_port_unlock(uport, flags); - - free_page((unsigned long)xmit_buf); + uart_free_xmit_buf(port); } /** @@ -565,23 +590,18 @@ static int uart_put_char(struct tty_struct *tty, u8 c) { struct uart_state *state = tty->driver_data; struct uart_port *port; - struct circ_buf *circ; unsigned long flags; int ret = 0; - circ = &state->xmit; - port = uart_port_lock(state, flags); - if (!circ->buf) { - uart_port_unlock(port, flags); + port = uart_port_ref_lock(state, &flags); + if (!state->port.xmit_buf) { + uart_port_unlock_deref(port, flags); return 0; } - if (port && uart_circ_chars_free(circ) != 0) { - circ->buf[circ->head] = c; - circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); - ret = 1; - } - uart_port_unlock(port, flags); + if (port) + ret = kfifo_put(&state->port.xmit_fifo, c); + uart_port_unlock_deref(port, flags); return ret; } @@ -594,9 +614,8 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 *buf, size_t count) { struct uart_state *state = tty->driver_data; struct uart_port *port; - struct circ_buf *circ; unsigned long flags; - int c, ret = 0; + int ret = 0; /* * This means you called this function _after_ the port was @@ -605,28 +624,17 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 *buf, size_t count) if (WARN_ON(!state)) return -EL3HLT; - port = uart_port_lock(state, flags); - circ = &state->xmit; - if (!circ->buf) { - uart_port_unlock(port, flags); + port = uart_port_ref_lock(state, &flags); + if (!state->port.xmit_buf) { + uart_port_unlock_deref(port, flags); return 0; } - while (port) { - c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(circ->buf + circ->head, buf, c); - circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); - buf += c; - count -= c; - ret += c; - } + if (port) + ret = kfifo_in(&state->port.xmit_fifo, buf, count); __uart_start(state); - uart_port_unlock(port, flags); + uart_port_unlock_deref(port, flags); return ret; } @@ -637,9 +645,9 @@ static unsigned int uart_write_room(struct tty_struct *tty) unsigned long flags; unsigned int ret; - port = uart_port_lock(state, flags); - ret = uart_circ_chars_free(&state->xmit); - uart_port_unlock(port, flags); + port = uart_port_ref_lock(state, &flags); + ret = kfifo_avail(&state->port.xmit_fifo); + uart_port_unlock_deref(port, flags); return ret; } @@ -650,9 +658,9 @@ static unsigned int uart_chars_in_buffer(struct tty_struct *tty) unsigned long flags; unsigned int ret; - port = uart_port_lock(state, flags); - ret = uart_circ_chars_pending(&state->xmit); - uart_port_unlock(port, flags); + port = uart_port_ref_lock(state, &flags); + ret = kfifo_len(&state->port.xmit_fifo); + uart_port_unlock_deref(port, flags); return ret; } @@ -671,13 +679,13 @@ static void uart_flush_buffer(struct tty_struct *tty) pr_debug("uart_flush_buffer(%d) called\n", tty->index); - port = uart_port_lock(state, flags); + port = uart_port_ref_lock(state, &flags); if (!port) return; - uart_circ_clear(&state->xmit); + kfifo_reset(&state->port.xmit_fifo); if (port->ops->flush_buffer) port->ops->flush_buffer(port); - uart_port_unlock(port, flags); + uart_port_unlock_deref(port, flags); tty_port_tty_wakeup(&state->port); } @@ -783,7 +791,6 @@ static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport; - int ret = -ENODEV; /* Initialize structure in case we error out later to prevent any stack info leakage. */ *retinfo = (struct serial_struct){}; @@ -792,10 +799,10 @@ static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo) * Ensure the state we copy is consistent and no hardware changes * occur as we go */ - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); uport = uart_port_check(state); if (!uport) - goto out; + return -ENODEV; retinfo->type = uport->type; retinfo->line = uport->line; @@ -816,10 +823,7 @@ static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo) retinfo->iomem_reg_shift = uport->regshift; retinfo->iomem_base = (void *)(unsigned long)uport->mapbase; - ret = 0; -out: - mutex_unlock(&port->mutex); - return ret; + return 0; } static int uart_get_info_user(struct tty_struct *tty, @@ -831,16 +835,71 @@ static int uart_get_info_user(struct tty_struct *tty, return uart_get_info(port, ss) < 0 ? -EIO : 0; } +static int uart_change_port(struct uart_port *uport, + const struct serial_struct *new_info, + unsigned long new_port) +{ + unsigned long old_iobase, old_mapbase; + unsigned int old_type, old_iotype, old_hub6, old_shift; + int retval; + + old_iobase = uport->iobase; + old_mapbase = uport->mapbase; + old_type = uport->type; + old_hub6 = uport->hub6; + old_iotype = uport->iotype; + old_shift = uport->regshift; + + if (old_type != PORT_UNKNOWN && uport->ops->release_port) + uport->ops->release_port(uport); + + uport->iobase = new_port; + uport->type = new_info->type; + uport->hub6 = new_info->hub6; + uport->iotype = new_info->io_type; + uport->regshift = new_info->iomem_reg_shift; + uport->mapbase = (unsigned long)new_info->iomem_base; + + if (uport->type == PORT_UNKNOWN || !uport->ops->request_port) + return 0; + + retval = uport->ops->request_port(uport); + if (retval == 0) + return 0; /* succeeded => done */ + + /* + * If we fail to request resources for the new port, try to restore the + * old settings. + */ + uport->iobase = old_iobase; + uport->type = old_type; + uport->hub6 = old_hub6; + uport->iotype = old_iotype; + uport->regshift = old_shift; + uport->mapbase = old_mapbase; + + if (old_type == PORT_UNKNOWN) + return retval; + + retval = uport->ops->request_port(uport); + /* If we failed to restore the old settings, we fail like this. */ + if (retval) + uport->type = PORT_UNKNOWN; + + /* We failed anyway. */ + return -EBUSY; +} + static int uart_set_info(struct tty_struct *tty, struct tty_port *port, struct uart_state *state, struct serial_struct *new_info) { struct uart_port *uport = uart_port_check(state); unsigned long new_port; - unsigned int change_irq, change_port, closing_wait; - unsigned int old_custom_divisor, close_delay; + unsigned int old_custom_divisor, close_delay, closing_wait; + bool change_irq, change_port; upf_t old_flags, new_flags; - int retval = 0; + int retval; if (!uport) return -EIO; @@ -876,8 +935,13 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, new_flags = (__force upf_t)new_info->flags; old_custom_divisor = uport->custom_divisor; + if (!(uport->flags & UPF_FIXED_PORT)) { + unsigned int uartclk = new_info->baud_base * 16; + /* check needs to be done here before other settings made */ + if (uartclk == 0) + return -EINVAL; + } if (!capable(CAP_SYS_ADMIN)) { - retval = -EPERM; if (change_irq || change_port || (new_info->baud_base != uport->uartclk / 16) || (close_delay != port->close_delay) || @@ -885,7 +949,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, (new_info->xmit_fifo_size && new_info->xmit_fifo_size != uport->fifosize) || (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) - goto exit; + return -EPERM; uport->flags = ((uport->flags & ~UPF_USR_MASK) | (new_flags & UPF_USR_MASK)); uport->custom_divisor = new_info->custom_divisor; @@ -895,30 +959,24 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, if (change_irq || change_port) { retval = security_locked_down(LOCKDOWN_TIOCSSERIAL); if (retval) - goto exit; + return retval; } - /* - * Ask the low level driver to verify the settings. - */ - if (uport->ops->verify_port) + /* Ask the low level driver to verify the settings. */ + if (uport->ops->verify_port) { retval = uport->ops->verify_port(uport, new_info); + if (retval) + return retval; + } - if ((new_info->irq >= nr_irqs) || (new_info->irq < 0) || + if ((new_info->irq >= irq_get_nr_irqs()) || (new_info->irq < 0) || (new_info->baud_base < 9600)) - retval = -EINVAL; - - if (retval) - goto exit; + return -EINVAL; if (change_port || change_irq) { - retval = -EBUSY; - - /* - * Make sure that we are the sole user of this port. - */ + /* Make sure that we are the sole user of this port. */ if (tty_port_users(port) > 1) - goto exit; + return -EBUSY; /* * We need to shutdown the serial port at the old @@ -928,69 +986,9 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, } if (change_port) { - unsigned long old_iobase, old_mapbase; - unsigned int old_type, old_iotype, old_hub6, old_shift; - - old_iobase = uport->iobase; - old_mapbase = uport->mapbase; - old_type = uport->type; - old_hub6 = uport->hub6; - old_iotype = uport->iotype; - old_shift = uport->regshift; - - /* - * Free and release old regions - */ - if (old_type != PORT_UNKNOWN && uport->ops->release_port) - uport->ops->release_port(uport); - - uport->iobase = new_port; - uport->type = new_info->type; - uport->hub6 = new_info->hub6; - uport->iotype = new_info->io_type; - uport->regshift = new_info->iomem_reg_shift; - uport->mapbase = (unsigned long)new_info->iomem_base; - - /* - * Claim and map the new regions - */ - if (uport->type != PORT_UNKNOWN && uport->ops->request_port) { - retval = uport->ops->request_port(uport); - } else { - /* Always success - Jean II */ - retval = 0; - } - - /* - * If we fail to request resources for the - * new port, try to restore the old settings. - */ - if (retval) { - uport->iobase = old_iobase; - uport->type = old_type; - uport->hub6 = old_hub6; - uport->iotype = old_iotype; - uport->regshift = old_shift; - uport->mapbase = old_mapbase; - - if (old_type != PORT_UNKNOWN) { - retval = uport->ops->request_port(uport); - /* - * If we failed to restore the old settings, - * we fail like this. - */ - if (retval) - uport->type = PORT_UNKNOWN; - - /* - * We failed anyway. - */ - retval = -EBUSY; - } - - /* Added to return the correct error -Ram Gupta */ - goto exit; - } + retval = uart_change_port(uport, new_info, new_port); + if (retval) + return retval; } if (change_irq) @@ -1006,9 +1004,9 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, uport->fifosize = new_info->xmit_fifo_size; check_and_exit: - retval = 0; if (uport->type == PORT_UNKNOWN) - goto exit; + return 0; + if (tty_port_initialized(port)) { if (((old_flags ^ uport->flags) & UPF_SPD_MASK) || old_custom_divisor != uport->custom_divisor) { @@ -1024,15 +1022,17 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, } uart_change_line_settings(tty, state, NULL); } - } else { - retval = uart_startup(tty, state, true); - if (retval == 0) - tty_port_set_initialized(port, true); - if (retval > 0) - retval = 0; + + return 0; } - exit: - return retval; + + retval = uart_startup(tty, state, true); + if (retval < 0) + return retval; + if (retval == 0) + tty_port_set_initialized(port, true); + + return 0; } static int uart_set_info_user(struct tty_struct *tty, struct serial_struct *ss) @@ -1077,7 +1077,7 @@ static int uart_get_lsr_info(struct tty_struct *tty, * interrupt happens). */ if (uport->x_char || - ((uart_circ_chars_pending(&state->xmit) > 0) && + (!kfifo_is_empty(&state->port.xmit_fifo) && !uart_tx_stopped(uport))) result &= ~TIOCSER_TEMT; @@ -1089,21 +1089,19 @@ static int uart_tiocmget(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; struct uart_port *uport; - int result = -EIO; + int result; + + guard(mutex)(&port->mutex); - mutex_lock(&port->mutex); uport = uart_port_check(state); - if (!uport) - goto out; + if (!uport || tty_io_error(tty)) + return -EIO; + + uart_port_lock_irq(uport); + result = uport->mctrl; + result |= uport->ops->get_mctrl(uport); + uart_port_unlock_irq(uport); - if (!tty_io_error(tty)) { - uart_port_lock_irq(uport); - result = uport->mctrl; - result |= uport->ops->get_mctrl(uport); - uart_port_unlock_irq(uport); - } -out: - mutex_unlock(&port->mutex); return result; } @@ -1113,20 +1111,16 @@ uart_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; struct uart_port *uport; - int ret = -EIO; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); + uport = uart_port_check(state); - if (!uport) - goto out; + if (!uport || tty_io_error(tty)) + return -EIO; - if (!tty_io_error(tty)) { - uart_update_mctrl(uport, set, clear); - ret = 0; - } -out: - mutex_unlock(&port->mutex); - return ret; + uart_update_mctrl(uport, set, clear); + + return 0; } static int uart_break_ctl(struct tty_struct *tty, int break_state) @@ -1134,19 +1128,17 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state) struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; struct uart_port *uport; - int ret = -EIO; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); + uport = uart_port_check(state); if (!uport) - goto out; + return -EIO; if (uport->type != PORT_UNKNOWN && uport->ops->break_ctl) uport->ops->break_ctl(uport, break_state); - ret = 0; -out: - mutex_unlock(&port->mutex); - return ret; + + return 0; } static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) @@ -1163,17 +1155,14 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) * changing, and hence any extra opens of the port while * we're auto-configuring. */ - if (mutex_lock_interruptible(&port->mutex)) - return -ERESTARTSYS; + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &port->mutex) { + uport = uart_port_check(state); + if (!uport) + return -EIO; - uport = uart_port_check(state); - if (!uport) { - ret = -EIO; - goto out; - } + if (tty_port_users(port) != 1) + return -EBUSY; - ret = -EBUSY; - if (tty_port_users(port) == 1) { uart_shutdown(tty, state); /* @@ -1194,14 +1183,15 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) uport->ops->config_port(uport, flags); ret = uart_startup(tty, state, true); - if (ret == 0) - tty_port_set_initialized(port, true); + if (ret < 0) + return ret; if (ret > 0) - ret = 0; + return 0; + + tty_port_set_initialized(port, true); } -out: - mutex_unlock(&port->mutex); - return ret; + + return 0; } static void uart_enable_ms(struct uart_port *uport) @@ -1286,14 +1276,13 @@ static int uart_get_icount(struct tty_struct *tty, struct uart_state *state = tty->driver_data; struct uart_icount cnow; struct uart_port *uport; + unsigned long flags; - uport = uart_port_ref(state); + uport = uart_port_ref_lock(state, &flags); if (!uport) return -EIO; - uart_port_lock_irq(uport); memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); - uart_port_unlock_irq(uport); - uart_port_deref(uport); + uart_port_unlock_deref(uport, flags); icount->cts = cnow.cts; icount->dsr = cnow.dsr; @@ -1696,10 +1685,11 @@ static void uart_set_termios(struct tty_struct *tty, unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK; bool sw_changed = false; - mutex_lock(&state->port.mutex); + guard(mutex)(&state->port.mutex); + uport = uart_port_check(state); if (!uport) - goto out; + return; /* * Drivers doing software flow control also need to know @@ -1722,9 +1712,8 @@ static void uart_set_termios(struct tty_struct *tty, tty->termios.c_ospeed == old_termios->c_ospeed && tty->termios.c_ispeed == old_termios->c_ispeed && ((tty->termios.c_iflag ^ old_termios->c_iflag) & iflag_mask) == 0 && - !sw_changed) { - goto out; - } + !sw_changed) + return; uart_change_line_settings(tty, state, old_termios); /* reload cflag from termios; port driver may have overridden flags */ @@ -1741,8 +1730,6 @@ static void uart_set_termios(struct tty_struct *tty, mask |= TIOCM_RTS; uart_set_mctrl(uport, mask); } -out: - mutex_unlock(&state->port.mutex); } /* @@ -1775,7 +1762,6 @@ static void uart_tty_port_shutdown(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = uart_port_check(state); - char *buf; /* * At this point, we stop accepting input. To do this, we @@ -1798,16 +1784,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) */ tty_port_set_suspended(port, false); - /* - * Free the transmit buffer. - */ - uart_port_lock_irq(uport); - uart_circ_clear(&state->xmit); - buf = state->xmit.buf; - state->xmit.buf = NULL; - uart_port_unlock_irq(uport); - - free_page((unsigned long)buf); + uart_free_xmit_buf(port); uart_change_pm(state, UART_PM_STATE_OFF); } @@ -1937,9 +1914,10 @@ static bool uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport; + unsigned long flags; int mctrl; - uport = uart_port_ref(state); + uport = uart_port_ref_lock(state, &flags); /* * Should never observe uport == NULL since checks for hangup should * abort the tty_port_block_til_ready() loop before checking for carrier @@ -1948,11 +1926,9 @@ static bool uart_carrier_raised(struct tty_port *port) */ if (WARN_ON(!uport)) return true; - uart_port_lock_irq(uport); uart_enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); - uart_port_unlock_irq(uport); - uart_port_deref(uport); + uart_port_unlock_deref(uport, flags); return mctrl & TIOCM_CAR; } @@ -2036,9 +2012,8 @@ static const char *uart_type(struct uart_port *port) #ifdef CONFIG_PROC_FS -static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) +static void uart_line_info(struct seq_file *m, struct uart_state *state) { - struct uart_state *state = drv->state + i; struct tty_port *port = &state->port; enum uart_pm_state pm_state; struct uart_port *uport; @@ -2046,10 +2021,11 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) unsigned int status; int mmio; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); + uport = uart_port_check(state); if (!uport) - goto out; + return; mmio = uport->iotype >= UPIO_MEM; seq_printf(m, "%d: uart:%s %s%08llX irq:%d", @@ -2061,7 +2037,7 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) if (uport->type == PORT_UNKNOWN) { seq_putc(m, '\n'); - goto out; + return; } if (capable(CAP_SYS_ADMIN)) { @@ -2112,8 +2088,6 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) seq_putc(m, '\n'); #undef STATBIT #undef INFOBIT -out: - mutex_unlock(&port->mutex); } static int uart_proc_show(struct seq_file *m, void *v) @@ -2124,7 +2098,7 @@ static int uart_proc_show(struct seq_file *m, void *v) seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n", "", "", ""); for (i = 0; i < drv->nr; i++) - uart_line_info(m, drv, i); + uart_line_info(m, drv->state + i); return 0; } #endif @@ -2203,8 +2177,8 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) * * Returns: 0 on success or -%EINVAL on failure */ -int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, - char **options) +int uart_parse_earlycon(char *p, enum uart_iotype *iotype, + resource_size_t *addr, char **options) { if (strncmp(p, "mmio,", 5) == 0) { *iotype = UPIO_MEM; @@ -2373,9 +2347,9 @@ struct uart_match { struct uart_driver *driver; }; -static int serial_match_port(struct device *dev, void *data) +static int serial_match_port(struct device *dev, const void *data) { - struct uart_match *match = data; + const struct uart_match *match = data; struct tty_driver *tty_drv = match->driver->tty_driver; dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) + match->port->line; @@ -2390,13 +2364,12 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) struct device *tty_dev; struct uart_match match = {uport, drv}; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); tty_dev = device_find_child(&uport->port_dev->dev, &match, serial_match_port); if (tty_dev && device_may_wakeup(tty_dev)) { enable_irq_wake(uport->irq); put_device(tty_dev); - mutex_unlock(&port->mutex); return 0; } put_device(tty_dev); @@ -2413,7 +2386,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) uport->ops->stop_rx(uport); uart_port_unlock_irq(uport); } - goto unlock; + device_set_awake_path(uport->dev); + return 0; } uport->suspended = 1; @@ -2450,14 +2424,12 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) } /* - * Disable the console device before suspending. + * Suspend the console device before suspending the port. */ if (uart_console(uport)) - console_stop(uport->cons); + console_suspend(uport->cons); uart_change_pm(state, UART_PM_STATE_OFF); -unlock: - mutex_unlock(&port->mutex); return 0; } @@ -2471,14 +2443,13 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) struct uart_match match = {uport, drv}; struct ktermios termios; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); tty_dev = device_find_child(&uport->port_dev->dev, &match, serial_match_port); if (!uport->suspended && device_may_wakeup(tty_dev)) { if (irqd_is_wakeup_set(irq_get_irq_data((uport->irq)))) disable_irq_wake(uport->irq); put_device(tty_dev); - mutex_unlock(&port->mutex); return 0; } put_device(tty_dev); @@ -2511,7 +2482,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) uart_port_unlock_irq(uport); } if (console_suspend_enabled) - console_start(uport->cons); + console_resume(uport->cons); } if (tty_port_suspended(port)) { @@ -2551,8 +2522,6 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) tty_port_set_suspended(port, false); } - mutex_unlock(&port->mutex); - return 0; } EXPORT_SYMBOL(uart_resume_port); @@ -2692,14 +2661,13 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) int ret = 0; tport = &state->port; - mutex_lock(&tport->mutex); + + guard(mutex)(&tport->mutex); port = uart_port_check(state); if (!port || port->type == PORT_UNKNOWN || - !(port->ops->poll_get_char && port->ops->poll_put_char)) { - ret = -1; - goto out; - } + !(port->ops->poll_get_char && port->ops->poll_put_char)) + return -1; pm_state = state->pm_state; uart_change_pm(state, UART_PM_STATE_ON); @@ -2719,10 +2687,10 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) ret = uart_set_options(port, NULL, baud, parity, bits, flow); console_list_unlock(); } -out: + if (ret) uart_change_pm(state, pm_state); - mutex_unlock(&tport->mutex); + return ret; } @@ -3075,26 +3043,25 @@ static ssize_t console_store(struct device *dev, if (ret) return ret; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); uport = uart_port_check(state); - if (uport) { - oldconsole = uart_console_registered(uport); - if (oldconsole && !newconsole) { - ret = unregister_console(uport->cons); - } else if (!oldconsole && newconsole) { - if (uart_console(uport)) { - uport->console_reinit = 1; - register_console(uport->cons); - } else { - ret = -ENOENT; - } - } - } else { - ret = -ENXIO; + if (!uport) + return -ENXIO; + + oldconsole = uart_console_registered(uport); + if (oldconsole && !newconsole) { + ret = unregister_console(uport->cons); + if (ret < 0) + return ret; + } else if (!oldconsole && newconsole) { + if (!uart_console(uport)) + return -ENOENT; + + uport->console_reinit = 1; + register_console(uport->cons); } - mutex_unlock(&port->mutex); - return ret < 0 ? ret : count; + return count; } static DEVICE_ATTR_RO(uartclk); @@ -3150,7 +3117,6 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u { struct uart_state *state; struct tty_port *port; - int ret = 0; struct device *tty_dev; int num_groups; @@ -3160,11 +3126,9 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u state = drv->state + uport->line; port = &state->port; - mutex_lock(&port->mutex); - if (state->uart_port) { - ret = -EINVAL; - goto out; - } + guard(mutex)(&port->mutex); + if (state->uart_port) + return -EINVAL; /* Link the port to the driver state table and vice versa */ atomic_set(&state->refcount, 1); @@ -3172,16 +3136,6 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u state->uart_port = uport; uport->state = state; - state->pm_state = UART_PM_STATE_UNDEFINED; - uport->cons = drv->cons; - uport->minor = drv->tty_driver->minor_start + uport->line; - uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name, - drv->tty_driver->name_base + uport->line); - if (!uport->name) { - ret = -ENOMEM; - goto out; - } - /* * If this port is in use as a console then the spinlock is already * initialised. @@ -3189,10 +3143,17 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u if (!uart_console_registered(uport)) uart_port_spin_lock_init(uport); + state->pm_state = UART_PM_STATE_UNDEFINED; + uart_port_set_cons(uport, drv->cons); + uport->minor = drv->tty_driver->minor_start + uport->line; + uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name, + drv->tty_driver->name_base + uport->line); + if (!uport->name) + return -ENOMEM; + if (uport->cons && uport->dev) of_console_check(uport->dev->of_node, uport->cons->name, uport->line); - tty_port_link_device(port, drv->tty_driver, uport->line); uart_configure_port(drv, state, uport); port->console = uart_console(uport); @@ -3203,14 +3164,16 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups), GFP_KERNEL); - if (!uport->tty_groups) { - ret = -ENOMEM; - goto out; - } + if (!uport->tty_groups) + return -ENOMEM; + uport->tty_groups[0] = &tty_dev_attr_group; if (uport->attr_group) uport->tty_groups[1] = uport->attr_group; + /* Ensure serdev drivers can call serdev_device_open() right away */ + uport->flags &= ~UPF_DEAD; + /* * Register the port whether it's detected or not. This allows * setserial to be used to alter this port's parameters. @@ -3221,14 +3184,12 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u if (!IS_ERR(tty_dev)) { device_set_wakeup_capable(tty_dev, 1); } else { + uport->flags |= UPF_DEAD; dev_err(uport->dev, "Cannot register tty device on line %d\n", uport->line); } - out: - mutex_unlock(&port->mutex); - - return ret; + return 0; } /** @@ -3327,9 +3288,9 @@ bool uart_match_port(const struct uart_port *port1, case UPIO_AU: case UPIO_TSI: return port1->mapbase == port2->mapbase; + default: + return false; } - - return false; } EXPORT_SYMBOL(uart_match_port); @@ -3394,7 +3355,7 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port) struct serial_ctrl_device *ctrl_dev, *new_ctrl_dev = NULL; int ret; - mutex_lock(&port_mutex); + guard(mutex)(&port_mutex); /* * Prevent serial_port_runtime_resume() from trying to use the port @@ -3406,10 +3367,8 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port) ctrl_dev = serial_core_ctrl_find(drv, port->dev, port->ctrl_id); if (!ctrl_dev) { new_ctrl_dev = serial_core_ctrl_device_add(port); - if (IS_ERR(new_ctrl_dev)) { - ret = PTR_ERR(new_ctrl_dev); - goto err_unlock; - } + if (IS_ERR(new_ctrl_dev)) + return PTR_ERR(new_ctrl_dev); ctrl_dev = new_ctrl_dev; } @@ -3422,13 +3381,13 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port) if (ret) goto err_unregister_ctrl_dev; - ret = serial_core_add_one_port(drv, port); + ret = serial_base_match_and_update_preferred_console(drv, port); if (ret) goto err_unregister_port_dev; - port->flags &= ~UPF_DEAD; - - mutex_unlock(&port_mutex); + ret = serial_core_add_one_port(drv, port); + if (ret) + goto err_unregister_port_dev; return 0; @@ -3438,9 +3397,6 @@ err_unregister_port_dev: err_unregister_ctrl_dev: serial_base_ctrl_device_remove(new_ctrl_dev); -err_unlock: - mutex_unlock(&port_mutex); - return ret; } |