diff options
Diffstat (limited to 'drivers/tty/tty_port.c')
-rw-r--r-- | drivers/tty/tty_port.c | 277 |
1 files changed, 186 insertions, 91 deletions
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index ea80bf872f54..dce08a6d7b5e 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -18,6 +18,7 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/serdev.h> +#include "tty.h" static int tty_port_default_receive_buf(struct tty_port *port, const unsigned char *p, @@ -42,6 +43,26 @@ static int tty_port_default_receive_buf(struct tty_port *port, return ret; } +static void tty_port_default_lookahead_buf(struct tty_port *port, const unsigned char *p, + const unsigned char *f, unsigned int count) +{ + struct tty_struct *tty; + struct tty_ldisc *disc; + + tty = READ_ONCE(port->itty); + if (!tty) + return; + + disc = tty_ldisc_ref(tty); + if (!disc) + return; + + if (disc->ops->lookahead_buf) + disc->ops->lookahead_buf(disc->tty, p, f, count); + + tty_ldisc_deref(disc); +} + static void tty_port_default_wakeup(struct tty_port *port) { struct tty_struct *tty = tty_port_tty_get(port); @@ -54,10 +75,20 @@ static void tty_port_default_wakeup(struct tty_port *port) 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 + * @port: tty_port to initialize + * + * Initializes the state of struct tty_port. When a port was initialized using + * this function, one has to destroy the port by tty_port_destroy(). Either + * indirectly by using &tty_port refcounting (tty_port_put()) or directly if + * refcounting is not used. + */ void tty_port_init(struct tty_port *port) { memset(port, 0, sizeof(*port)); @@ -81,9 +112,9 @@ EXPORT_SYMBOL(tty_port_init); * @index: index of the tty * * Provide the tty layer with a link from a tty (specified by @index) to a - * tty_port (@port). Use this only if neither tty_port_register_device nor - * tty_port_install is used in the driver. If used, this has to be called before - * tty_register_driver. + * tty_port (@port). Use this only if neither tty_port_register_device() nor + * tty_port_install() is used in the driver. If used, this has to be called + * before tty_register_driver(). */ void tty_port_link_device(struct tty_port *port, struct tty_driver *driver, unsigned index) @@ -101,9 +132,9 @@ EXPORT_SYMBOL_GPL(tty_port_link_device); * @index: index of the tty * @device: parent if exists, otherwise NULL * - * It is the same as tty_register_device except the provided @port is linked to - * a concrete tty specified by @index. Use this or tty_port_install (or both). - * Call tty_port_link_device as a last resort. + * It is the same as tty_register_device() except the provided @port is linked + * to a concrete tty specified by @index. Use this or tty_port_install() (or + * both). Call tty_port_link_device() as a last resort. */ struct device *tty_port_register_device(struct tty_port *port, struct tty_driver *driver, unsigned index, @@ -122,9 +153,9 @@ EXPORT_SYMBOL_GPL(tty_port_register_device); * @drvdata: Driver data to be set to device. * @attr_grp: Attribute group to be set on device. * - * It is the same as tty_register_device_attr except the provided @port is - * linked to a concrete tty specified by @index. Use this or tty_port_install - * (or both). Call tty_port_link_device as a last resort. + * It is the same as tty_register_device_attr() except the provided @port is + * linked to a concrete tty specified by @index. Use this or tty_port_install() + * (or both). Call tty_port_link_device() as a last resort. */ struct device *tty_port_register_device_attr(struct tty_port *port, struct tty_driver *driver, unsigned index, @@ -215,8 +246,11 @@ 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) + if (port->xmit_buf == NULL) { port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); + if (port->xmit_buf) + kfifo_init(&port->xmit_fifo, port->xmit_buf, PAGE_SIZE); + } mutex_unlock(&port->buf_mutex); if (port->xmit_buf == NULL) return -ENOMEM; @@ -227,10 +261,9 @@ 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; - } + free_page((unsigned long)port->xmit_buf); + port->xmit_buf = NULL; + INIT_KFIFO(port->xmit_fifo); mutex_unlock(&port->buf_mutex); } EXPORT_SYMBOL(tty_port_free_xmit_buf); @@ -239,9 +272,9 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf); * 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 - * port by this function. Either indirectly by using tty_port refcounting - * (tty_port_put) or directly if refcounting is not used. + * When a port was initialized using tty_port_init(), one has to destroy the + * port by this function. Either indirectly by using &tty_port refcounting + * (tty_port_put()) or directly if refcounting is not used. */ void tty_port_destroy(struct tty_port *port) { @@ -257,8 +290,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); @@ -266,6 +298,13 @@ static void tty_port_destructor(struct kref *kref) kfree(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 + * @port->ops->destruct() hook, or using kfree() if not provided. + */ void tty_port_put(struct tty_port *port) { if (port) @@ -274,11 +313,11 @@ void tty_port_put(struct tty_port *port) EXPORT_SYMBOL(tty_port_put); /** - * tty_port_tty_get - get a tty reference - * @port: tty port + * tty_port_tty_get - get a tty reference + * @port: tty port * - * Return a refcount protected tty instance or NULL if the port is not - * associated with a tty (eg due to close or hangup) + * Return a refcount protected tty instance or %NULL if the port is not + * associated with a tty (eg due to close or hangup). */ struct tty_struct *tty_port_tty_get(struct tty_port *port) { @@ -293,12 +332,12 @@ struct tty_struct *tty_port_tty_get(struct tty_port *port) EXPORT_SYMBOL(tty_port_tty_get); /** - * tty_port_tty_set - set the tty of a port - * @port: tty port - * @tty: the tty + * tty_port_tty_set - set the tty of a port + * @port: tty port + * @tty: the tty * - * Associate the port and tty pair. Manages any internal refcounts. - * Pass NULL to deassociate a port + * Associate the port and tty pair. Manages any internal refcounts. Pass %NULL + * to deassociate a port. */ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) { @@ -311,6 +350,16 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) } EXPORT_SYMBOL(tty_port_tty_set); +/** + * tty_port_shutdown - internal helper to shutdown the device + * @port: tty port to be shut down + * @tty: the associated tty + * + * It is used by tty_port_hangup() and tty_port_close(). Its task is to + * shutdown the device if it was initialized (note consoles remain + * functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes + * @port->ops->shutdown(). + */ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) { mutex_lock(&port->mutex); @@ -334,13 +383,13 @@ out: } /** - * tty_port_hangup - hangup helper - * @port: tty port + * tty_port_hangup - hangup helper + * @port: tty port * - * Perform port level tty hangup flag and count changes. Drop the tty - * reference. + * Perform port level tty hangup flag and count changes. Drop the tty + * reference. * - * Caller holds tty lock. + * Caller holds tty lock. */ void tty_port_hangup(struct tty_port *port) { @@ -364,9 +413,8 @@ 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? + * @check_clocal: hang only ttys with %CLOCAL unset? */ void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) { @@ -380,7 +428,6 @@ 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) @@ -390,12 +437,12 @@ void tty_port_tty_wakeup(struct tty_port *port) EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); /** - * tty_port_carrier_raised - carrier raised check - * @port: tty port + * tty_port_carrier_raised - carrier raised check + * @port: tty port * - * Wrapper for the carrier detect logic. For the moment this is used - * to hide some internal details. This will eventually become entirely - * internal to the tty port. + * Wrapper for the carrier detect logic. For the moment this is used + * to hide some internal details. This will eventually become entirely + * internal to the tty port. */ int tty_port_carrier_raised(struct tty_port *port) { @@ -406,12 +453,12 @@ int tty_port_carrier_raised(struct tty_port *port) EXPORT_SYMBOL(tty_port_carrier_raised); /** - * tty_port_raise_dtr_rts - Raise DTR/RTS - * @port: tty port + * tty_port_raise_dtr_rts - Raise DTR/RTS + * @port: tty port * - * Wrapper for the DTR/RTS raise logic. For the moment this is used - * to hide some internal details. This will eventually become entirely - * internal to the tty port. + * Wrapper for the DTR/RTS raise logic. For the moment this is used to hide + * some internal details. This will eventually become entirely internal to the + * tty port. */ void tty_port_raise_dtr_rts(struct tty_port *port) { @@ -421,12 +468,12 @@ void tty_port_raise_dtr_rts(struct tty_port *port) EXPORT_SYMBOL(tty_port_raise_dtr_rts); /** - * tty_port_lower_dtr_rts - Lower DTR/RTS - * @port: tty port + * tty_port_lower_dtr_rts - Lower DTR/RTS + * @port: tty port * - * Wrapper for the DTR/RTS raise logic. For the moment this is used - * to hide some internal details. This will eventually become entirely - * internal to the tty port. + * Wrapper for the DTR/RTS raise logic. For the moment this is used to hide + * some internal details. This will eventually become entirely internal to the + * tty port. */ void tty_port_lower_dtr_rts(struct tty_port *port) { @@ -436,28 +483,29 @@ void tty_port_lower_dtr_rts(struct tty_port *port) EXPORT_SYMBOL(tty_port_lower_dtr_rts); /** - * tty_port_block_til_ready - Waiting logic for tty open - * @port: the tty port being opened - * @tty: the tty device being bound - * @filp: the file pointer of the opener or NULL - * - * Implement the core POSIX/SuS tty behaviour when opening a tty device. - * Handles: - * - hangup (both before and during) - * - non blocking open - * - rts/dtr/dcd - * - signals - * - port flags and counts - * - * The passed tty_port must implement the carrier_raised method if it can - * do carrier detect and the dtr_rts method if it supports software - * management of these lines. Note that the dtr/rts raise is done each - * iteration as a hangup may have previously dropped them while we wait. - * - * Caller holds tty lock. - * - * NB: May drop and reacquire tty lock when blocking, so tty and tty_port - * may have changed state (eg., may have been hung up). + * tty_port_block_til_ready - Waiting logic for tty open + * @port: the tty port being opened + * @tty: the tty device being bound + * @filp: the file pointer of the opener or %NULL + * + * Implement the core POSIX/SuS tty behaviour when opening a tty device. + * Handles: + * + * - hangup (both before and during) + * - non blocking open + * - rts/dtr/dcd + * - signals + * - port flags and counts + * + * The passed @port must implement the @port->ops->carrier_raised method if it + * can do carrier detect and the @port->ops->dtr_rts method if it supports + * software management of these lines. Note that the dtr/rts raise is done each + * iteration as a hangup may have previously dropped them while we wait. + * + * Caller holds tty lock. + * + * Note: May drop and reacquire tty lock when blocking, so @tty and @port may + * have changed state (eg., may have been hung up). */ int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty, struct file *filp) @@ -467,7 +515,8 @@ int tty_port_block_til_ready(struct tty_port *port, 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 */ + * the port has just hung up or is in another error state. + */ if (tty_io_error(tty)) { tty_port_set_active(port, 1); return 0; @@ -484,8 +533,9 @@ int tty_port_block_til_ready(struct tty_port *port, do_clocal = 1; /* Block waiting until we can proceed. We may need to wait for the - carrier, but we must also wait for any close that is in progress - before the next open may complete */ + * carrier, but we must also wait for any close that is in progress + * before the next open may complete. + */ retval = 0; @@ -502,7 +552,8 @@ int tty_port_block_til_ready(struct tty_port *port, prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); /* Check for a hangup or uninitialised port. - Return accordingly */ + * Return accordingly. + */ if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; @@ -529,7 +580,8 @@ int tty_port_block_til_ready(struct tty_port *port, finish_wait(&port->open_wait, &wait); /* Update counts. A parallel hangup will have set count to zero and - we must not mess that up further */ + * we must not mess that up further. + */ spin_lock_irqsave(&port->lock, flags); if (!tty_hung_up_p(filp)) port->count++; @@ -555,7 +607,21 @@ static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) schedule_timeout_interruptible(timeout); } -/* Caller holds tty lock. */ +/** + * tty_port_close_start - helper for tty->ops->close, part 1/2 + * @port: tty_port of the device + * @tty: tty being closed + * @filp: passed file pointer + * + * Decrements and checks open count. Flushes the port if this is the last + * close. That means, dropping the data from the outpu buffer on the device and + * waiting for sending logic to finish. The rest of close handling is performed + * in tty_port_close_end(). + * + * Locking: Caller holds tty lock. + * + * Return: 1 if this is the last close, otherwise 0 + */ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp) { @@ -586,7 +652,7 @@ int tty_port_close_start(struct tty_port *port, if (tty_port_initialized(port)) { /* Don't block on a stalled port, just pull the chain */ - if (tty->flow_stopped) + if (tty->flow.tco_stopped) tty_driver_flush_buffer(tty); if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, port->closing_wait); @@ -601,7 +667,17 @@ int tty_port_close_start(struct tty_port *port, } EXPORT_SYMBOL(tty_port_close_start); -/* Caller holds tty lock */ +/** + * tty_port_close_end - helper for tty->ops->close, part 2/2 + * @port: tty_port of the device + * @tty: tty being closed + * + * This is a continuation of the first part: tty_port_close_start(). This + * should be called after turning off the device. It flushes the data from the + * line discipline and delays the close by @port->close_delay. + * + * Locking: Caller holds tty lock. + */ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) { unsigned long flags; @@ -624,9 +700,17 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) EXPORT_SYMBOL(tty_port_close_end); /** - * tty_port_close + * tty_port_close - generic tty->ops->close handler + * @port: tty_port of the device + * @tty: tty being closed + * @filp: passed file pointer + * + * It is a generic helper to be used in driver's @tty->ops->close. It wraps a + * sequence of tty_port_close_start(), tty_port_shutdown(), and + * tty_port_close_end(). The latter two are called only if this is the last + * close. See the respective functions for the details. * - * Caller holds tty lock + * Locking: Caller holds tty lock */ void tty_port_close(struct tty_port *port, struct tty_struct *tty, struct file *filp) @@ -647,9 +731,9 @@ EXPORT_SYMBOL(tty_port_close); * @driver: tty_driver for this device * @tty: tty to be installed * - * It is the same as tty_standard_install except the provided @port is linked - * to a concrete tty specified by @tty. Use this or tty_port_register_device - * (or both). Call tty_port_link_device as a last resort. + * It is the same as tty_standard_install() except the provided @port is linked + * to a concrete tty specified by @tty. Use this or tty_port_register_device() + * (or both). Call tty_port_link_device() as a last resort. */ int tty_port_install(struct tty_port *port, struct tty_driver *driver, struct tty_struct *tty) @@ -660,12 +744,23 @@ int tty_port_install(struct tty_port *port, struct tty_driver *driver, EXPORT_SYMBOL_GPL(tty_port_install); /** - * tty_port_open + * tty_port_open - generic tty->ops->open handler + * @port: tty_port of the device + * @tty: tty to be opened + * @filp: passed file pointer * - * Caller holds tty lock. + * It is a generic helper to be used in driver's @tty->ops->open. It activates + * the devices using @port->ops->activate if not active already. And waits for + * the device to be ready using tty_port_block_til_ready() (e.g. raises + * DTR/CTS and waits for carrier). * - * NB: may drop and reacquire tty lock (in tty_port_block_til_ready()) so - * tty and tty_port may have changed state (eg., may be hung up now) + * 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 + * @tty and @port may have changed state (eg., may be hung up now). */ int tty_port_open(struct tty_port *port, struct tty_struct *tty, struct file *filp) @@ -687,6 +782,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty, clear_bit(TTY_IO_ERROR, &tty->flags); if (port->ops->activate) { int retval = port->ops->activate(port, tty); + if (retval) { mutex_unlock(&port->mutex); return retval; @@ -697,5 +793,4 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty, mutex_unlock(&port->mutex); return tty_port_block_til_ready(port, tty, filp); } - EXPORT_SYMBOL(tty_port_open); |