diff options
Diffstat (limited to 'drivers/tty/tty_port.c')
| -rw-r--r-- | drivers/tty/tty_port.c | 278 |
1 files changed, 131 insertions, 147 deletions
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 7709ce655f44..fe67c5cb0a3f 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -20,47 +20,62 @@ #include <linux/serdev.h> #include "tty.h" -static int tty_port_default_receive_buf(struct tty_port *port, - const unsigned char *p, - const unsigned char *f, size_t count) +static size_t tty_port_default_receive_buf(struct tty_port *port, const u8 *p, + const u8 *f, size_t count) { - int ret; struct tty_struct *tty; - struct tty_ldisc *disc; + struct tty_ldisc *ld; tty = READ_ONCE(port->itty); if (!tty) return 0; - disc = tty_ldisc_ref(tty); - if (!disc) + ld = tty_ldisc_ref(tty); + if (!ld) return 0; - ret = tty_ldisc_receive_buf(disc, p, (char *)f, count); + count = tty_ldisc_receive_buf(ld, p, f, count); - tty_ldisc_deref(disc); + tty_ldisc_deref(ld); - return ret; + return count; } -static void tty_port_default_wakeup(struct tty_port *port) +static void tty_port_default_lookahead_buf(struct tty_port *port, const u8 *p, + const u8 *f, size_t count) { - struct tty_struct *tty = tty_port_tty_get(port); + struct tty_struct *tty; + struct tty_ldisc *ld; - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty = READ_ONCE(port->itty); + if (!tty) + return; + + ld = tty_ldisc_ref(tty); + if (!ld) + return; + + if (ld->ops->lookahead_buf) + ld->ops->lookahead_buf(ld->tty, p, f, count); + + tty_ldisc_deref(ld); +} + +static void tty_port_default_wakeup(struct tty_port *port) +{ + scoped_guard(tty_port_tty, port) + tty_wakeup(scoped_tty()); } const struct tty_port_client_operations tty_port_default_client_ops = { .receive_buf = tty_port_default_receive_buf, + .lookahead_buf = tty_port_default_lookahead_buf, .write_wakeup = tty_port_default_wakeup, }; EXPORT_SYMBOL_GPL(tty_port_default_client_ops); /** - * tty_port_init -- initialize tty_port + * tty_port_init - initialize tty_port * @port: tty_port to initialize * * Initializes the state of struct tty_port. When a port was initialized using @@ -152,7 +167,8 @@ EXPORT_SYMBOL_GPL(tty_port_register_device_attr); * @port: tty_port of the device * @driver: tty_driver for this device * @index: index of the tty - * @device: parent if exists, otherwise NULL + * @host: serial port hardware device + * @parent: parent if exists, otherwise NULL * @drvdata: driver data for the device * @attr_grp: attribute group for the device * @@ -161,44 +177,25 @@ EXPORT_SYMBOL_GPL(tty_port_register_device_attr); */ struct device *tty_port_register_device_attr_serdev(struct tty_port *port, struct tty_driver *driver, unsigned index, - struct device *device, void *drvdata, + struct device *host, struct device *parent, void *drvdata, const struct attribute_group **attr_grp) { struct device *dev; tty_port_link_device(port, driver, index); - dev = serdev_tty_port_register(port, device, driver, index); + dev = serdev_tty_port_register(port, host, parent, driver, index); if (PTR_ERR(dev) != -ENODEV) { /* Skip creating cdev if we registered a serdev device */ return dev; } - return tty_register_device_attr(driver, index, device, drvdata, + return tty_register_device_attr(driver, index, parent, drvdata, attr_grp); } EXPORT_SYMBOL_GPL(tty_port_register_device_attr_serdev); /** - * tty_port_register_device_serdev - register tty or serdev device - * @port: tty_port of the device - * @driver: tty_driver for this device - * @index: index of the tty - * @device: parent if exists, otherwise NULL - * - * Register a serdev or tty device depending on if the parent device has any - * defined serdev clients or not. - */ -struct device *tty_port_register_device_serdev(struct tty_port *port, - struct tty_driver *driver, unsigned index, - struct device *device) -{ - return tty_port_register_device_attr_serdev(port, driver, index, - device, NULL, NULL); -} -EXPORT_SYMBOL_GPL(tty_port_register_device_serdev); - -/** * tty_port_unregister_device - deregister a tty or serdev device * @port: tty_port of the device * @driver: tty_driver for this device @@ -224,29 +221,32 @@ EXPORT_SYMBOL_GPL(tty_port_unregister_device); int tty_port_alloc_xmit_buf(struct tty_port *port) { /* We may sleep in get_zeroed_page() */ - mutex_lock(&port->buf_mutex); - if (port->xmit_buf == NULL) - port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); - mutex_unlock(&port->buf_mutex); + guard(mutex)(&port->buf_mutex); + + if (port->xmit_buf) + return 0; + + port->xmit_buf = (u8 *)get_zeroed_page(GFP_KERNEL); if (port->xmit_buf == NULL) return -ENOMEM; + + kfifo_init(&port->xmit_fifo, port->xmit_buf, PAGE_SIZE); + return 0; } EXPORT_SYMBOL(tty_port_alloc_xmit_buf); void tty_port_free_xmit_buf(struct tty_port *port) { - mutex_lock(&port->buf_mutex); - if (port->xmit_buf != NULL) { - free_page((unsigned long)port->xmit_buf); - port->xmit_buf = NULL; - } - mutex_unlock(&port->buf_mutex); + guard(mutex)(&port->buf_mutex); + free_page((unsigned long)port->xmit_buf); + port->xmit_buf = NULL; + INIT_KFIFO(port->xmit_fifo); } EXPORT_SYMBOL(tty_port_free_xmit_buf); /** - * tty_port_destroy -- destroy inited port + * tty_port_destroy - destroy inited port * @port: tty port to be destroyed * * When a port was initialized using tty_port_init(), one has to destroy the @@ -267,8 +267,7 @@ static void tty_port_destructor(struct kref *kref) /* check if last port ref was dropped before tty release */ if (WARN_ON(port->itty)) return; - if (port->xmit_buf) - free_page((unsigned long)port->xmit_buf); + free_page((unsigned long)port->xmit_buf); tty_port_destroy(port); if (port->ops && port->ops->destruct) port->ops->destruct(port); @@ -277,7 +276,7 @@ static void tty_port_destructor(struct kref *kref) } /** - * tty_port_put -- drop a reference to tty_port + * tty_port_put - drop a reference to tty_port * @port: port to drop a reference of (can be NULL) * * The final put will destroy and free up the @port using @@ -299,13 +298,8 @@ EXPORT_SYMBOL(tty_port_put); */ struct tty_struct *tty_port_tty_get(struct tty_port *port) { - unsigned long flags; - struct tty_struct *tty; - - spin_lock_irqsave(&port->lock, flags); - tty = tty_kref_get(port->tty); - spin_unlock_irqrestore(&port->lock, flags); - return tty; + guard(spinlock_irqsave)(&port->lock); + return tty_kref_get(port->tty); } EXPORT_SYMBOL(tty_port_tty_get); @@ -319,12 +313,9 @@ EXPORT_SYMBOL(tty_port_tty_get); */ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) { - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); + guard(spinlock_irqsave)(&port->lock); tty_kref_put(port->tty); port->tty = tty_kref_get(tty); - spin_unlock_irqrestore(&port->lock, flags); } EXPORT_SYMBOL(tty_port_tty_set); @@ -340,24 +331,24 @@ EXPORT_SYMBOL(tty_port_tty_set); */ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) { - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); + if (port->console) - goto out; + return; - if (tty_port_initialized(port)) { - tty_port_set_initialized(port, 0); - /* - * 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 (!tty_port_initialized(port)) + return; - if (port->ops->shutdown) - port->ops->shutdown(port); - } -out: - mutex_unlock(&port->mutex); + tty_port_set_initialized(port, false); + /* + * 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); } /** @@ -372,16 +363,16 @@ out: void tty_port_hangup(struct tty_port *port) { struct tty_struct *tty; - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - port->count = 0; - tty = port->tty; - if (tty) - set_bit(TTY_IO_ERROR, &tty->flags); - port->tty = NULL; - spin_unlock_irqrestore(&port->lock, flags); - tty_port_set_active(port, 0); + scoped_guard(spinlock_irqsave, &port->lock) { + port->count = 0; + tty = port->tty; + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); + port->tty = NULL; + } + + tty_port_set_active(port, false); tty_port_shutdown(port, tty); tty_kref_put(tty); wake_up_interruptible(&port->open_wait); @@ -389,20 +380,20 @@ void tty_port_hangup(struct tty_port *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) +void __tty_port_tty_hangup(struct tty_port *port, bool check_clocal, bool async) { - struct tty_struct *tty = tty_port_tty_get(port); + scoped_guard(tty_port_tty, port) { + struct tty_struct *tty = scoped_tty(); - if (tty && (!check_clocal || !C_CLOCAL(tty))) - tty_hangup(tty); - tty_kref_put(tty); + if (!check_clocal || !C_CLOCAL(tty)) { + if (async) + tty_hangup(tty); + else + tty_vhangup(tty); + } + } } -EXPORT_SYMBOL_GPL(tty_port_tty_hangup); +EXPORT_SYMBOL_GPL(__tty_port_tty_hangup); /** * tty_port_tty_wakeup - helper to wake up a tty @@ -422,10 +413,10 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); * to hide some internal details. This will eventually become entirely * internal to the tty port. */ -int tty_port_carrier_raised(struct tty_port *port) +bool tty_port_carrier_raised(struct tty_port *port) { if (port->ops->carrier_raised == NULL) - return 1; + return true; return port->ops->carrier_raised(port); } EXPORT_SYMBOL(tty_port_carrier_raised); @@ -441,7 +432,7 @@ EXPORT_SYMBOL(tty_port_carrier_raised); void tty_port_raise_dtr_rts(struct tty_port *port) { if (port->ops->dtr_rts) - port->ops->dtr_rts(port, 1); + port->ops->dtr_rts(port, true); } EXPORT_SYMBOL(tty_port_raise_dtr_rts); @@ -456,7 +447,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts); void tty_port_lower_dtr_rts(struct tty_port *port) { if (port->ops->dtr_rts) - port->ops->dtr_rts(port, 0); + port->ops->dtr_rts(port, false); } EXPORT_SYMBOL(tty_port_lower_dtr_rts); @@ -489,21 +480,20 @@ int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty, struct file *filp) { int do_clocal = 0, retval; - unsigned long flags; DEFINE_WAIT(wait); /* if non-blocking mode is set we can pass directly to open unless * the port has just hung up or is in another error state. */ if (tty_io_error(tty)) { - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return 0; } if (filp == NULL || (filp->f_flags & O_NONBLOCK)) { /* Indicate we are open */ if (C_BAUD(tty)) tty_port_raise_dtr_rts(port); - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return 0; } @@ -518,10 +508,10 @@ int tty_port_block_til_ready(struct tty_port *port, retval = 0; /* The port lock protects the port counts */ - spin_lock_irqsave(&port->lock, flags); - port->count--; - port->blocked_open++; - spin_unlock_irqrestore(&port->lock, flags); + scoped_guard(spinlock_irqsave, &port->lock) { + port->count--; + port->blocked_open++; + } while (1) { /* Indicate we are open */ @@ -560,13 +550,13 @@ int tty_port_block_til_ready(struct tty_port *port, /* Update counts. A parallel hangup will have set count to zero and * we must not mess that up further. */ - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - spin_unlock_irqrestore(&port->lock, flags); + scoped_guard(spinlock_irqsave, &port->lock) { + if (!tty_hung_up_p(filp)) + port->count++; + port->blocked_open--; + } if (retval == 0) - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return retval; } EXPORT_SYMBOL(tty_port_block_til_ready); @@ -603,28 +593,24 @@ static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp) { - unsigned long flags; - if (tty_hung_up_p(filp)) return 0; - spin_lock_irqsave(&port->lock, flags); - if (tty->count == 1 && port->count != 1) { - tty_warn(tty, "%s: tty->count = 1 port count = %d\n", __func__, - port->count); - port->count = 1; - } - if (--port->count < 0) { - tty_warn(tty, "%s: bad port count (%d)\n", __func__, - port->count); - port->count = 0; - } + scoped_guard(spinlock_irqsave, &port->lock) { + if (tty->count == 1 && port->count != 1) { + tty_warn(tty, "%s: tty->count = 1 port count = %d\n", __func__, + port->count); + port->count = 1; + } + if (--port->count < 0) { + tty_warn(tty, "%s: bad port count (%d)\n", __func__, + port->count); + port->count = 0; + } - if (port->count) { - spin_unlock_irqrestore(&port->lock, flags); - return 0; + if (port->count) + return 0; } - spin_unlock_irqrestore(&port->lock, flags); tty->closing = 1; @@ -673,7 +659,7 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) wake_up_interruptible(&port->open_wait); } spin_unlock_irqrestore(&port->lock, flags); - tty_port_set_active(port, 0); + tty_port_set_active(port, false); } EXPORT_SYMBOL(tty_port_close_end); @@ -732,6 +718,9 @@ EXPORT_SYMBOL_GPL(tty_port_install); * the device to be ready using tty_port_block_til_ready() (e.g. raises * DTR/CTS and waits for carrier). * + * Note that @port->ops->shutdown is not called when @port->ops->activate + * returns an error (on the contrary, @tty->ops->close is). + * * Locking: Caller holds tty lock. * * Note: may drop and reacquire tty lock (in tty_port_block_til_ready()) so @@ -740,9 +729,8 @@ EXPORT_SYMBOL_GPL(tty_port_install); int tty_port_open(struct tty_port *port, struct tty_struct *tty, struct file *filp) { - spin_lock_irq(&port->lock); - ++port->count; - spin_unlock_irq(&port->lock); + scoped_guard(spinlock_irq, &port->lock) + ++port->count; tty_port_tty_set(port, tty); /* @@ -751,21 +739,17 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty, * port mutex. */ - mutex_lock(&port->mutex); - - if (!tty_port_initialized(port)) { + scoped_guard(mutex, &port->mutex) { + if (tty_port_initialized(port)) + break; clear_bit(TTY_IO_ERROR, &tty->flags); if (port->ops->activate) { int retval = port->ops->activate(port, tty); - - if (retval) { - mutex_unlock(&port->mutex); + if (retval) return retval; - } } - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); } - mutex_unlock(&port->mutex); return tty_port_block_til_ready(port, tty, filp); } EXPORT_SYMBOL(tty_port_open); |
