1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 296fd7ce5SGreg Kroah-Hartman /* 396fd7ce5SGreg Kroah-Hartman * Tty port functions 496fd7ce5SGreg Kroah-Hartman */ 596fd7ce5SGreg Kroah-Hartman 696fd7ce5SGreg Kroah-Hartman #include <linux/types.h> 796fd7ce5SGreg Kroah-Hartman #include <linux/errno.h> 896fd7ce5SGreg Kroah-Hartman #include <linux/tty.h> 996fd7ce5SGreg Kroah-Hartman #include <linux/tty_driver.h> 1096fd7ce5SGreg Kroah-Hartman #include <linux/tty_flip.h> 1196fd7ce5SGreg Kroah-Hartman #include <linux/serial.h> 1296fd7ce5SGreg Kroah-Hartman #include <linux/timer.h> 1396fd7ce5SGreg Kroah-Hartman #include <linux/string.h> 1496fd7ce5SGreg Kroah-Hartman #include <linux/slab.h> 15174cd4b1SIngo Molnar #include <linux/sched/signal.h> 1696fd7ce5SGreg Kroah-Hartman #include <linux/wait.h> 1796fd7ce5SGreg Kroah-Hartman #include <linux/bitops.h> 1896fd7ce5SGreg Kroah-Hartman #include <linux/delay.h> 1996fd7ce5SGreg Kroah-Hartman #include <linux/module.h> 208cde11b2SJohan Hovold #include <linux/serdev.h> 2198602c01SGreg Kroah-Hartman #include "tty.h" 2296fd7ce5SGreg Kroah-Hartman 23c3485ee0SRob Herring static int tty_port_default_receive_buf(struct tty_port *port, 24c3485ee0SRob Herring const unsigned char *p, 25c3485ee0SRob Herring const unsigned char *f, size_t count) 26c3485ee0SRob Herring { 27c3485ee0SRob Herring int ret; 28c3485ee0SRob Herring struct tty_struct *tty; 29*c6e37fe0SJiri Slaby (SUSE) struct tty_ldisc *ld; 30c3485ee0SRob Herring 31c3485ee0SRob Herring tty = READ_ONCE(port->itty); 32c3485ee0SRob Herring if (!tty) 33c3485ee0SRob Herring return 0; 34c3485ee0SRob Herring 35*c6e37fe0SJiri Slaby (SUSE) ld = tty_ldisc_ref(tty); 36*c6e37fe0SJiri Slaby (SUSE) if (!ld) 37c3485ee0SRob Herring return 0; 38c3485ee0SRob Herring 39*c6e37fe0SJiri Slaby (SUSE) ret = tty_ldisc_receive_buf(ld, p, (char *)f, count); 40c3485ee0SRob Herring 41*c6e37fe0SJiri Slaby (SUSE) tty_ldisc_deref(ld); 42c3485ee0SRob Herring 43c3485ee0SRob Herring return ret; 44c3485ee0SRob Herring } 45c3485ee0SRob Herring 466bb6fa69SIlpo Järvinen static void tty_port_default_lookahead_buf(struct tty_port *port, const unsigned char *p, 476bb6fa69SIlpo Järvinen const unsigned char *f, unsigned int count) 486bb6fa69SIlpo Järvinen { 496bb6fa69SIlpo Järvinen struct tty_struct *tty; 50*c6e37fe0SJiri Slaby (SUSE) struct tty_ldisc *ld; 516bb6fa69SIlpo Järvinen 526bb6fa69SIlpo Järvinen tty = READ_ONCE(port->itty); 536bb6fa69SIlpo Järvinen if (!tty) 546bb6fa69SIlpo Järvinen return; 556bb6fa69SIlpo Järvinen 56*c6e37fe0SJiri Slaby (SUSE) ld = tty_ldisc_ref(tty); 57*c6e37fe0SJiri Slaby (SUSE) if (!ld) 586bb6fa69SIlpo Järvinen return; 596bb6fa69SIlpo Järvinen 60*c6e37fe0SJiri Slaby (SUSE) if (ld->ops->lookahead_buf) 61*c6e37fe0SJiri Slaby (SUSE) ld->ops->lookahead_buf(ld->tty, p, f, count); 626bb6fa69SIlpo Järvinen 63*c6e37fe0SJiri Slaby (SUSE) tty_ldisc_deref(ld); 646bb6fa69SIlpo Järvinen } 656bb6fa69SIlpo Järvinen 66c3485ee0SRob Herring static void tty_port_default_wakeup(struct tty_port *port) 67c3485ee0SRob Herring { 68c3485ee0SRob Herring struct tty_struct *tty = tty_port_tty_get(port); 69c3485ee0SRob Herring 70c3485ee0SRob Herring if (tty) { 71c3485ee0SRob Herring tty_wakeup(tty); 72c3485ee0SRob Herring tty_kref_put(tty); 73c3485ee0SRob Herring } 74c3485ee0SRob Herring } 75c3485ee0SRob Herring 760c5aae59SJohan Hovold const struct tty_port_client_operations tty_port_default_client_ops = { 77c3485ee0SRob Herring .receive_buf = tty_port_default_receive_buf, 786bb6fa69SIlpo Järvinen .lookahead_buf = tty_port_default_lookahead_buf, 79c3485ee0SRob Herring .write_wakeup = tty_port_default_wakeup, 80c3485ee0SRob Herring }; 810c5aae59SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_default_client_ops); 82c3485ee0SRob Herring 833be491d7SJiri Slaby /** 843be491d7SJiri Slaby * tty_port_init -- initialize tty_port 853be491d7SJiri Slaby * @port: tty_port to initialize 863be491d7SJiri Slaby * 873be491d7SJiri Slaby * Initializes the state of struct tty_port. When a port was initialized using 883be491d7SJiri Slaby * this function, one has to destroy the port by tty_port_destroy(). Either 893be491d7SJiri Slaby * indirectly by using &tty_port refcounting (tty_port_put()) or directly if 903be491d7SJiri Slaby * refcounting is not used. 913be491d7SJiri Slaby */ 9296fd7ce5SGreg Kroah-Hartman void tty_port_init(struct tty_port *port) 9396fd7ce5SGreg Kroah-Hartman { 9496fd7ce5SGreg Kroah-Hartman memset(port, 0, sizeof(*port)); 95ecbbfd44SJiri Slaby tty_buffer_init(port); 9696fd7ce5SGreg Kroah-Hartman init_waitqueue_head(&port->open_wait); 9796fd7ce5SGreg Kroah-Hartman init_waitqueue_head(&port->delta_msr_wait); 9896fd7ce5SGreg Kroah-Hartman mutex_init(&port->mutex); 9996fd7ce5SGreg Kroah-Hartman mutex_init(&port->buf_mutex); 10096fd7ce5SGreg Kroah-Hartman spin_lock_init(&port->lock); 10196fd7ce5SGreg Kroah-Hartman port->close_delay = (50 * HZ) / 100; 10296fd7ce5SGreg Kroah-Hartman port->closing_wait = (3000 * HZ) / 100; 1030c5aae59SJohan Hovold port->client_ops = &tty_port_default_client_ops; 10496fd7ce5SGreg Kroah-Hartman kref_init(&port->kref); 10596fd7ce5SGreg Kroah-Hartman } 10696fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_init); 10796fd7ce5SGreg Kroah-Hartman 10872a33bf5SJiri Slaby /** 1092cb4ca02SJiri Slaby * tty_port_link_device - link tty and tty_port 1102cb4ca02SJiri Slaby * @port: tty_port of the device 1112cb4ca02SJiri Slaby * @driver: tty_driver for this device 1122cb4ca02SJiri Slaby * @index: index of the tty 1132cb4ca02SJiri Slaby * 1142cb4ca02SJiri Slaby * Provide the tty layer with a link from a tty (specified by @index) to a 115cb6f6f98SJiri Slaby * tty_port (@port). Use this only if neither tty_port_register_device() nor 116cb6f6f98SJiri Slaby * tty_port_install() is used in the driver. If used, this has to be called 117cb6f6f98SJiri Slaby * before tty_register_driver(). 1182cb4ca02SJiri Slaby */ 1192cb4ca02SJiri Slaby void tty_port_link_device(struct tty_port *port, 1202cb4ca02SJiri Slaby struct tty_driver *driver, unsigned index) 1212cb4ca02SJiri Slaby { 1222cb4ca02SJiri Slaby if (WARN_ON(index >= driver->num)) 1232cb4ca02SJiri Slaby return; 1242cb4ca02SJiri Slaby driver->ports[index] = port; 1252cb4ca02SJiri Slaby } 1262cb4ca02SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_link_device); 1272cb4ca02SJiri Slaby 1282cb4ca02SJiri Slaby /** 12972a33bf5SJiri Slaby * tty_port_register_device - register tty device 13072a33bf5SJiri Slaby * @port: tty_port of the device 13172a33bf5SJiri Slaby * @driver: tty_driver for this device 13272a33bf5SJiri Slaby * @index: index of the tty 13372a33bf5SJiri Slaby * @device: parent if exists, otherwise NULL 13472a33bf5SJiri Slaby * 135cb6f6f98SJiri Slaby * It is the same as tty_register_device() except the provided @port is linked 136cb6f6f98SJiri Slaby * to a concrete tty specified by @index. Use this or tty_port_install() (or 137cb6f6f98SJiri Slaby * both). Call tty_port_link_device() as a last resort. 13872a33bf5SJiri Slaby */ 139057eb856SJiri Slaby struct device *tty_port_register_device(struct tty_port *port, 140057eb856SJiri Slaby struct tty_driver *driver, unsigned index, 141057eb856SJiri Slaby struct device *device) 142057eb856SJiri Slaby { 1433086365dSRob Herring return tty_port_register_device_attr(port, driver, index, device, NULL, NULL); 144057eb856SJiri Slaby } 145057eb856SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_register_device); 146057eb856SJiri Slaby 147b1b79916STomas Hlavacek /** 148b1b79916STomas Hlavacek * tty_port_register_device_attr - register tty device 149b1b79916STomas Hlavacek * @port: tty_port of the device 150b1b79916STomas Hlavacek * @driver: tty_driver for this device 151b1b79916STomas Hlavacek * @index: index of the tty 152b1b79916STomas Hlavacek * @device: parent if exists, otherwise NULL 153b1b79916STomas Hlavacek * @drvdata: Driver data to be set to device. 154b1b79916STomas Hlavacek * @attr_grp: Attribute group to be set on device. 155b1b79916STomas Hlavacek * 156cb6f6f98SJiri Slaby * It is the same as tty_register_device_attr() except the provided @port is 157cb6f6f98SJiri Slaby * linked to a concrete tty specified by @index. Use this or tty_port_install() 158cb6f6f98SJiri Slaby * (or both). Call tty_port_link_device() as a last resort. 159b1b79916STomas Hlavacek */ 160b1b79916STomas Hlavacek struct device *tty_port_register_device_attr(struct tty_port *port, 161b1b79916STomas Hlavacek struct tty_driver *driver, unsigned index, 162b1b79916STomas Hlavacek struct device *device, void *drvdata, 163b1b79916STomas Hlavacek const struct attribute_group **attr_grp) 164b1b79916STomas Hlavacek { 165b1b79916STomas Hlavacek tty_port_link_device(port, driver, index); 166b1b79916STomas Hlavacek return tty_register_device_attr(driver, index, device, drvdata, 167b1b79916STomas Hlavacek attr_grp); 168b1b79916STomas Hlavacek } 169b1b79916STomas Hlavacek EXPORT_SYMBOL_GPL(tty_port_register_device_attr); 170b1b79916STomas Hlavacek 1718cde11b2SJohan Hovold /** 1728cde11b2SJohan Hovold * tty_port_register_device_attr_serdev - register tty or serdev device 1738cde11b2SJohan Hovold * @port: tty_port of the device 1748cde11b2SJohan Hovold * @driver: tty_driver for this device 1758cde11b2SJohan Hovold * @index: index of the tty 1768cde11b2SJohan Hovold * @device: parent if exists, otherwise NULL 1778cde11b2SJohan Hovold * @drvdata: driver data for the device 1788cde11b2SJohan Hovold * @attr_grp: attribute group for the device 1798cde11b2SJohan Hovold * 1808cde11b2SJohan Hovold * Register a serdev or tty device depending on if the parent device has any 1818cde11b2SJohan Hovold * defined serdev clients or not. 1828cde11b2SJohan Hovold */ 1838cde11b2SJohan Hovold struct device *tty_port_register_device_attr_serdev(struct tty_port *port, 1848cde11b2SJohan Hovold struct tty_driver *driver, unsigned index, 1858cde11b2SJohan Hovold struct device *device, void *drvdata, 1868cde11b2SJohan Hovold const struct attribute_group **attr_grp) 1878cde11b2SJohan Hovold { 1888cde11b2SJohan Hovold struct device *dev; 1898cde11b2SJohan Hovold 1908cde11b2SJohan Hovold tty_port_link_device(port, driver, index); 1918cde11b2SJohan Hovold 1928cde11b2SJohan Hovold dev = serdev_tty_port_register(port, device, driver, index); 1938cde11b2SJohan Hovold if (PTR_ERR(dev) != -ENODEV) { 1948cde11b2SJohan Hovold /* Skip creating cdev if we registered a serdev device */ 1958cde11b2SJohan Hovold return dev; 1968cde11b2SJohan Hovold } 1978cde11b2SJohan Hovold 1988cde11b2SJohan Hovold return tty_register_device_attr(driver, index, device, drvdata, 1998cde11b2SJohan Hovold attr_grp); 2008cde11b2SJohan Hovold } 2018cde11b2SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_register_device_attr_serdev); 2028cde11b2SJohan Hovold 2038cde11b2SJohan Hovold /** 2048cde11b2SJohan Hovold * tty_port_register_device_serdev - register tty or serdev device 2058cde11b2SJohan Hovold * @port: tty_port of the device 2068cde11b2SJohan Hovold * @driver: tty_driver for this device 2078cde11b2SJohan Hovold * @index: index of the tty 2088cde11b2SJohan Hovold * @device: parent if exists, otherwise NULL 2098cde11b2SJohan Hovold * 2108cde11b2SJohan Hovold * Register a serdev or tty device depending on if the parent device has any 2118cde11b2SJohan Hovold * defined serdev clients or not. 2128cde11b2SJohan Hovold */ 2138cde11b2SJohan Hovold struct device *tty_port_register_device_serdev(struct tty_port *port, 2148cde11b2SJohan Hovold struct tty_driver *driver, unsigned index, 2158cde11b2SJohan Hovold struct device *device) 2168cde11b2SJohan Hovold { 2178cde11b2SJohan Hovold return tty_port_register_device_attr_serdev(port, driver, index, 2188cde11b2SJohan Hovold device, NULL, NULL); 2198cde11b2SJohan Hovold } 2208cde11b2SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_register_device_serdev); 2218cde11b2SJohan Hovold 2228cde11b2SJohan Hovold /** 2238cde11b2SJohan Hovold * tty_port_unregister_device - deregister a tty or serdev device 2248cde11b2SJohan Hovold * @port: tty_port of the device 2258cde11b2SJohan Hovold * @driver: tty_driver for this device 2268cde11b2SJohan Hovold * @index: index of the tty 2278cde11b2SJohan Hovold * 2288cde11b2SJohan Hovold * If a tty or serdev device is registered with a call to 2298cde11b2SJohan Hovold * tty_port_register_device_serdev() then this function must be called when 2308cde11b2SJohan Hovold * the device is gone. 2318cde11b2SJohan Hovold */ 2328cde11b2SJohan Hovold void tty_port_unregister_device(struct tty_port *port, 2338cde11b2SJohan Hovold struct tty_driver *driver, unsigned index) 2348cde11b2SJohan Hovold { 2358cde11b2SJohan Hovold int ret; 2368cde11b2SJohan Hovold 2378cde11b2SJohan Hovold ret = serdev_tty_port_unregister(port); 2388cde11b2SJohan Hovold if (ret == 0) 2398cde11b2SJohan Hovold return; 2408cde11b2SJohan Hovold 2418cde11b2SJohan Hovold tty_unregister_device(driver, index); 2428cde11b2SJohan Hovold } 2438cde11b2SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_unregister_device); 2448cde11b2SJohan Hovold 24596fd7ce5SGreg Kroah-Hartman int tty_port_alloc_xmit_buf(struct tty_port *port) 24696fd7ce5SGreg Kroah-Hartman { 24796fd7ce5SGreg Kroah-Hartman /* We may sleep in get_zeroed_page() */ 24896fd7ce5SGreg Kroah-Hartman mutex_lock(&port->buf_mutex); 2494e2a44c1SJiri Slaby if (port->xmit_buf == NULL) { 25096fd7ce5SGreg Kroah-Hartman port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); 2514e2a44c1SJiri Slaby if (port->xmit_buf) 2524e2a44c1SJiri Slaby kfifo_init(&port->xmit_fifo, port->xmit_buf, PAGE_SIZE); 2534e2a44c1SJiri Slaby } 25496fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->buf_mutex); 25596fd7ce5SGreg Kroah-Hartman if (port->xmit_buf == NULL) 25696fd7ce5SGreg Kroah-Hartman return -ENOMEM; 25796fd7ce5SGreg Kroah-Hartman return 0; 25896fd7ce5SGreg Kroah-Hartman } 25996fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_alloc_xmit_buf); 26096fd7ce5SGreg Kroah-Hartman 26196fd7ce5SGreg Kroah-Hartman void tty_port_free_xmit_buf(struct tty_port *port) 26296fd7ce5SGreg Kroah-Hartman { 26396fd7ce5SGreg Kroah-Hartman mutex_lock(&port->buf_mutex); 26496fd7ce5SGreg Kroah-Hartman free_page((unsigned long)port->xmit_buf); 26596fd7ce5SGreg Kroah-Hartman port->xmit_buf = NULL; 2664e2a44c1SJiri Slaby INIT_KFIFO(port->xmit_fifo); 26796fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->buf_mutex); 26896fd7ce5SGreg Kroah-Hartman } 26996fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_free_xmit_buf); 27096fd7ce5SGreg Kroah-Hartman 271de274bfeSJiri Slaby /** 272de274bfeSJiri Slaby * tty_port_destroy -- destroy inited port 2731926e5d3SAntonio Borneo * @port: tty port to be destroyed 274de274bfeSJiri Slaby * 275cb6f6f98SJiri Slaby * When a port was initialized using tty_port_init(), one has to destroy the 276cb6f6f98SJiri Slaby * port by this function. Either indirectly by using &tty_port refcounting 277cb6f6f98SJiri Slaby * (tty_port_put()) or directly if refcounting is not used. 278de274bfeSJiri Slaby */ 279de274bfeSJiri Slaby void tty_port_destroy(struct tty_port *port) 280de274bfeSJiri Slaby { 281e176058fSPeter Hurley tty_buffer_cancel_work(port); 282de274bfeSJiri Slaby tty_buffer_free_all(port); 283de274bfeSJiri Slaby } 284de274bfeSJiri Slaby EXPORT_SYMBOL(tty_port_destroy); 285de274bfeSJiri Slaby 28696fd7ce5SGreg Kroah-Hartman static void tty_port_destructor(struct kref *kref) 28796fd7ce5SGreg Kroah-Hartman { 28896fd7ce5SGreg Kroah-Hartman struct tty_port *port = container_of(kref, struct tty_port, kref); 289e3bfea23SPeter Hurley 290e3bfea23SPeter Hurley /* check if last port ref was dropped before tty release */ 291e3bfea23SPeter Hurley if (WARN_ON(port->itty)) 292e3bfea23SPeter Hurley return; 29396fd7ce5SGreg Kroah-Hartman free_page((unsigned long)port->xmit_buf); 294de274bfeSJiri Slaby tty_port_destroy(port); 29581c79838SJiri Slaby if (port->ops && port->ops->destruct) 29696fd7ce5SGreg Kroah-Hartman port->ops->destruct(port); 29796fd7ce5SGreg Kroah-Hartman else 29896fd7ce5SGreg Kroah-Hartman kfree(port); 29996fd7ce5SGreg Kroah-Hartman } 30096fd7ce5SGreg Kroah-Hartman 3013be491d7SJiri Slaby /** 3023be491d7SJiri Slaby * tty_port_put -- drop a reference to tty_port 3033be491d7SJiri Slaby * @port: port to drop a reference of (can be NULL) 3043be491d7SJiri Slaby * 3053be491d7SJiri Slaby * The final put will destroy and free up the @port using 3063be491d7SJiri Slaby * @port->ops->destruct() hook, or using kfree() if not provided. 3073be491d7SJiri Slaby */ 30896fd7ce5SGreg Kroah-Hartman void tty_port_put(struct tty_port *port) 30996fd7ce5SGreg Kroah-Hartman { 31096fd7ce5SGreg Kroah-Hartman if (port) 31196fd7ce5SGreg Kroah-Hartman kref_put(&port->kref, tty_port_destructor); 31296fd7ce5SGreg Kroah-Hartman } 31396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_put); 31496fd7ce5SGreg Kroah-Hartman 31596fd7ce5SGreg Kroah-Hartman /** 31696fd7ce5SGreg Kroah-Hartman * tty_port_tty_get - get a tty reference 31796fd7ce5SGreg Kroah-Hartman * @port: tty port 31896fd7ce5SGreg Kroah-Hartman * 319cb6f6f98SJiri Slaby * Return a refcount protected tty instance or %NULL if the port is not 320cb6f6f98SJiri Slaby * associated with a tty (eg due to close or hangup). 32196fd7ce5SGreg Kroah-Hartman */ 32296fd7ce5SGreg Kroah-Hartman struct tty_struct *tty_port_tty_get(struct tty_port *port) 32396fd7ce5SGreg Kroah-Hartman { 32496fd7ce5SGreg Kroah-Hartman unsigned long flags; 32596fd7ce5SGreg Kroah-Hartman struct tty_struct *tty; 32696fd7ce5SGreg Kroah-Hartman 32796fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 32896fd7ce5SGreg Kroah-Hartman tty = tty_kref_get(port->tty); 32996fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 33096fd7ce5SGreg Kroah-Hartman return tty; 33196fd7ce5SGreg Kroah-Hartman } 33296fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_tty_get); 33396fd7ce5SGreg Kroah-Hartman 33496fd7ce5SGreg Kroah-Hartman /** 33596fd7ce5SGreg Kroah-Hartman * tty_port_tty_set - set the tty of a port 33696fd7ce5SGreg Kroah-Hartman * @port: tty port 33796fd7ce5SGreg Kroah-Hartman * @tty: the tty 33896fd7ce5SGreg Kroah-Hartman * 339cb6f6f98SJiri Slaby * Associate the port and tty pair. Manages any internal refcounts. Pass %NULL 340cb6f6f98SJiri Slaby * to deassociate a port. 34196fd7ce5SGreg Kroah-Hartman */ 34296fd7ce5SGreg Kroah-Hartman void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) 34396fd7ce5SGreg Kroah-Hartman { 34496fd7ce5SGreg Kroah-Hartman unsigned long flags; 34596fd7ce5SGreg Kroah-Hartman 34696fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 34796fd7ce5SGreg Kroah-Hartman tty_kref_put(port->tty); 34896fd7ce5SGreg Kroah-Hartman port->tty = tty_kref_get(tty); 34996fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 35096fd7ce5SGreg Kroah-Hartman } 35196fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_tty_set); 35296fd7ce5SGreg Kroah-Hartman 3533be491d7SJiri Slaby /** 3543be491d7SJiri Slaby * tty_port_shutdown - internal helper to shutdown the device 3553be491d7SJiri Slaby * @port: tty port to be shut down 3563be491d7SJiri Slaby * @tty: the associated tty 3573be491d7SJiri Slaby * 3583be491d7SJiri Slaby * It is used by tty_port_hangup() and tty_port_close(). Its task is to 3593be491d7SJiri Slaby * shutdown the device if it was initialized (note consoles remain 3603be491d7SJiri Slaby * functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes 3613be491d7SJiri Slaby * @port->ops->shutdown(). 3623be491d7SJiri Slaby */ 363957dacaeSJohan Hovold static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) 36496fd7ce5SGreg Kroah-Hartman { 36596fd7ce5SGreg Kroah-Hartman mutex_lock(&port->mutex); 3668bde9658SJohan Hovold if (port->console) 3678bde9658SJohan Hovold goto out; 3688bde9658SJohan Hovold 369d41861caSPeter Hurley if (tty_port_initialized(port)) { 370515be7baSIlpo Järvinen tty_port_set_initialized(port, false); 371957dacaeSJohan Hovold /* 372957dacaeSJohan Hovold * Drop DTR/RTS if HUPCL is set. This causes any attached 373957dacaeSJohan Hovold * modem to hang up the line. 374957dacaeSJohan Hovold */ 375957dacaeSJohan Hovold if (tty && C_HUPCL(tty)) 376957dacaeSJohan Hovold tty_port_lower_dtr_rts(port); 377957dacaeSJohan Hovold 3780d3cb6f6SJohan Hovold if (port->ops->shutdown) 37996fd7ce5SGreg Kroah-Hartman port->ops->shutdown(port); 3808bde9658SJohan Hovold } 3818bde9658SJohan Hovold out: 38296fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 38396fd7ce5SGreg Kroah-Hartman } 38496fd7ce5SGreg Kroah-Hartman 38596fd7ce5SGreg Kroah-Hartman /** 38696fd7ce5SGreg Kroah-Hartman * tty_port_hangup - hangup helper 38796fd7ce5SGreg Kroah-Hartman * @port: tty port 38896fd7ce5SGreg Kroah-Hartman * 38996fd7ce5SGreg Kroah-Hartman * Perform port level tty hangup flag and count changes. Drop the tty 39096fd7ce5SGreg Kroah-Hartman * reference. 3919c9928bdSPeter Hurley * 3929c9928bdSPeter Hurley * Caller holds tty lock. 39396fd7ce5SGreg Kroah-Hartman */ 39496fd7ce5SGreg Kroah-Hartman void tty_port_hangup(struct tty_port *port) 39596fd7ce5SGreg Kroah-Hartman { 396957dacaeSJohan Hovold struct tty_struct *tty; 39796fd7ce5SGreg Kroah-Hartman unsigned long flags; 39896fd7ce5SGreg Kroah-Hartman 39996fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 40096fd7ce5SGreg Kroah-Hartman port->count = 0; 401957dacaeSJohan Hovold tty = port->tty; 402957dacaeSJohan Hovold if (tty) 403957dacaeSJohan Hovold set_bit(TTY_IO_ERROR, &tty->flags); 40496fd7ce5SGreg Kroah-Hartman port->tty = NULL; 40596fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 4069b5aa549SIlpo Järvinen tty_port_set_active(port, false); 407957dacaeSJohan Hovold tty_port_shutdown(port, tty); 408957dacaeSJohan Hovold tty_kref_put(tty); 40996fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->open_wait); 41096fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->delta_msr_wait); 41196fd7ce5SGreg Kroah-Hartman } 41296fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_hangup); 41396fd7ce5SGreg Kroah-Hartman 41496fd7ce5SGreg Kroah-Hartman /** 415aa27a094SJiri Slaby * tty_port_tty_hangup - helper to hang up a tty 416aa27a094SJiri Slaby * @port: tty port 417cb6f6f98SJiri Slaby * @check_clocal: hang only ttys with %CLOCAL unset? 418aa27a094SJiri Slaby */ 419aa27a094SJiri Slaby void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) 420aa27a094SJiri Slaby { 421aa27a094SJiri Slaby struct tty_struct *tty = tty_port_tty_get(port); 422aa27a094SJiri Slaby 4231d9e689cSGianluca Anzolin if (tty && (!check_clocal || !C_CLOCAL(tty))) 424aa27a094SJiri Slaby tty_hangup(tty); 425aa27a094SJiri Slaby tty_kref_put(tty); 426aa27a094SJiri Slaby } 427aa27a094SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_tty_hangup); 428aa27a094SJiri Slaby 429aa27a094SJiri Slaby /** 4306aad04f2SJiri Slaby * tty_port_tty_wakeup - helper to wake up a tty 4316aad04f2SJiri Slaby * @port: tty port 4326aad04f2SJiri Slaby */ 4336aad04f2SJiri Slaby void tty_port_tty_wakeup(struct tty_port *port) 4346aad04f2SJiri Slaby { 435c3485ee0SRob Herring port->client_ops->write_wakeup(port); 4366aad04f2SJiri Slaby } 4376aad04f2SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); 4386aad04f2SJiri Slaby 4396aad04f2SJiri Slaby /** 44096fd7ce5SGreg Kroah-Hartman * tty_port_carrier_raised - carrier raised check 44196fd7ce5SGreg Kroah-Hartman * @port: tty port 44296fd7ce5SGreg Kroah-Hartman * 44396fd7ce5SGreg Kroah-Hartman * Wrapper for the carrier detect logic. For the moment this is used 44496fd7ce5SGreg Kroah-Hartman * to hide some internal details. This will eventually become entirely 44596fd7ce5SGreg Kroah-Hartman * internal to the tty port. 44696fd7ce5SGreg Kroah-Hartman */ 447b300fb26SIlpo Järvinen bool tty_port_carrier_raised(struct tty_port *port) 44896fd7ce5SGreg Kroah-Hartman { 4490d3cb6f6SJohan Hovold if (port->ops->carrier_raised == NULL) 450b300fb26SIlpo Järvinen return true; 45196fd7ce5SGreg Kroah-Hartman return port->ops->carrier_raised(port); 45296fd7ce5SGreg Kroah-Hartman } 45396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_carrier_raised); 45496fd7ce5SGreg Kroah-Hartman 45596fd7ce5SGreg Kroah-Hartman /** 45696fd7ce5SGreg Kroah-Hartman * tty_port_raise_dtr_rts - Raise DTR/RTS 45796fd7ce5SGreg Kroah-Hartman * @port: tty port 45896fd7ce5SGreg Kroah-Hartman * 459cb6f6f98SJiri Slaby * Wrapper for the DTR/RTS raise logic. For the moment this is used to hide 460cb6f6f98SJiri Slaby * some internal details. This will eventually become entirely internal to the 461cb6f6f98SJiri Slaby * tty port. 46296fd7ce5SGreg Kroah-Hartman */ 46396fd7ce5SGreg Kroah-Hartman void tty_port_raise_dtr_rts(struct tty_port *port) 46496fd7ce5SGreg Kroah-Hartman { 4650d3cb6f6SJohan Hovold if (port->ops->dtr_rts) 4665d420399SIlpo Järvinen port->ops->dtr_rts(port, true); 46796fd7ce5SGreg Kroah-Hartman } 46896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_raise_dtr_rts); 46996fd7ce5SGreg Kroah-Hartman 47096fd7ce5SGreg Kroah-Hartman /** 47196fd7ce5SGreg Kroah-Hartman * tty_port_lower_dtr_rts - Lower DTR/RTS 47296fd7ce5SGreg Kroah-Hartman * @port: tty port 47396fd7ce5SGreg Kroah-Hartman * 474cb6f6f98SJiri Slaby * Wrapper for the DTR/RTS raise logic. For the moment this is used to hide 475cb6f6f98SJiri Slaby * some internal details. This will eventually become entirely internal to the 476cb6f6f98SJiri Slaby * tty port. 47796fd7ce5SGreg Kroah-Hartman */ 47896fd7ce5SGreg Kroah-Hartman void tty_port_lower_dtr_rts(struct tty_port *port) 47996fd7ce5SGreg Kroah-Hartman { 4800d3cb6f6SJohan Hovold if (port->ops->dtr_rts) 4815d420399SIlpo Järvinen port->ops->dtr_rts(port, false); 48296fd7ce5SGreg Kroah-Hartman } 48396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_lower_dtr_rts); 48496fd7ce5SGreg Kroah-Hartman 48596fd7ce5SGreg Kroah-Hartman /** 48696fd7ce5SGreg Kroah-Hartman * tty_port_block_til_ready - Waiting logic for tty open 48796fd7ce5SGreg Kroah-Hartman * @port: the tty port being opened 48896fd7ce5SGreg Kroah-Hartman * @tty: the tty device being bound 489cb6f6f98SJiri Slaby * @filp: the file pointer of the opener or %NULL 49096fd7ce5SGreg Kroah-Hartman * 49196fd7ce5SGreg Kroah-Hartman * Implement the core POSIX/SuS tty behaviour when opening a tty device. 49296fd7ce5SGreg Kroah-Hartman * Handles: 493cb6f6f98SJiri Slaby * 49496fd7ce5SGreg Kroah-Hartman * - hangup (both before and during) 49596fd7ce5SGreg Kroah-Hartman * - non blocking open 49696fd7ce5SGreg Kroah-Hartman * - rts/dtr/dcd 49796fd7ce5SGreg Kroah-Hartman * - signals 49896fd7ce5SGreg Kroah-Hartman * - port flags and counts 49996fd7ce5SGreg Kroah-Hartman * 500cb6f6f98SJiri Slaby * The passed @port must implement the @port->ops->carrier_raised method if it 501cb6f6f98SJiri Slaby * can do carrier detect and the @port->ops->dtr_rts method if it supports 502cb6f6f98SJiri Slaby * software management of these lines. Note that the dtr/rts raise is done each 50396fd7ce5SGreg Kroah-Hartman * iteration as a hangup may have previously dropped them while we wait. 504c590f6b6SPeter Hurley * 505c590f6b6SPeter Hurley * Caller holds tty lock. 506c590f6b6SPeter Hurley * 507cb6f6f98SJiri Slaby * Note: May drop and reacquire tty lock when blocking, so @tty and @port may 508cb6f6f98SJiri Slaby * have changed state (eg., may have been hung up). 50996fd7ce5SGreg Kroah-Hartman */ 51096fd7ce5SGreg Kroah-Hartman int tty_port_block_til_ready(struct tty_port *port, 51196fd7ce5SGreg Kroah-Hartman struct tty_struct *tty, struct file *filp) 51296fd7ce5SGreg Kroah-Hartman { 51396fd7ce5SGreg Kroah-Hartman int do_clocal = 0, retval; 51496fd7ce5SGreg Kroah-Hartman unsigned long flags; 51596fd7ce5SGreg Kroah-Hartman DEFINE_WAIT(wait); 51696fd7ce5SGreg Kroah-Hartman 51796fd7ce5SGreg Kroah-Hartman /* if non-blocking mode is set we can pass directly to open unless 5181df92640SXiaofei Tan * the port has just hung up or is in another error state. 5191df92640SXiaofei Tan */ 52018900ca6SPeter Hurley if (tty_io_error(tty)) { 5219b5aa549SIlpo Järvinen tty_port_set_active(port, true); 52296fd7ce5SGreg Kroah-Hartman return 0; 52396fd7ce5SGreg Kroah-Hartman } 524ed3f0af8SAlan Cox if (filp == NULL || (filp->f_flags & O_NONBLOCK)) { 52596fd7ce5SGreg Kroah-Hartman /* Indicate we are open */ 5269db276f8SPeter Hurley if (C_BAUD(tty)) 52796fd7ce5SGreg Kroah-Hartman tty_port_raise_dtr_rts(port); 5289b5aa549SIlpo Järvinen tty_port_set_active(port, true); 52996fd7ce5SGreg Kroah-Hartman return 0; 53096fd7ce5SGreg Kroah-Hartman } 53196fd7ce5SGreg Kroah-Hartman 53296fd7ce5SGreg Kroah-Hartman if (C_CLOCAL(tty)) 53396fd7ce5SGreg Kroah-Hartman do_clocal = 1; 53496fd7ce5SGreg Kroah-Hartman 53596fd7ce5SGreg Kroah-Hartman /* Block waiting until we can proceed. We may need to wait for the 5361df92640SXiaofei Tan * carrier, but we must also wait for any close that is in progress 5371df92640SXiaofei Tan * before the next open may complete. 5381df92640SXiaofei Tan */ 53996fd7ce5SGreg Kroah-Hartman 54096fd7ce5SGreg Kroah-Hartman retval = 0; 54196fd7ce5SGreg Kroah-Hartman 54296fd7ce5SGreg Kroah-Hartman /* The port lock protects the port counts */ 54396fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 54496fd7ce5SGreg Kroah-Hartman port->count--; 54596fd7ce5SGreg Kroah-Hartman port->blocked_open++; 54696fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 54796fd7ce5SGreg Kroah-Hartman 54896fd7ce5SGreg Kroah-Hartman while (1) { 54996fd7ce5SGreg Kroah-Hartman /* Indicate we are open */ 550d41861caSPeter Hurley if (C_BAUD(tty) && tty_port_initialized(port)) 55196fd7ce5SGreg Kroah-Hartman tty_port_raise_dtr_rts(port); 55296fd7ce5SGreg Kroah-Hartman 55396fd7ce5SGreg Kroah-Hartman prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); 55496fd7ce5SGreg Kroah-Hartman /* Check for a hangup or uninitialised port. 5551df92640SXiaofei Tan * Return accordingly. 5561df92640SXiaofei Tan */ 557d41861caSPeter Hurley if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { 55896fd7ce5SGreg Kroah-Hartman if (port->flags & ASYNC_HUP_NOTIFY) 55996fd7ce5SGreg Kroah-Hartman retval = -EAGAIN; 56096fd7ce5SGreg Kroah-Hartman else 56196fd7ce5SGreg Kroah-Hartman retval = -ERESTARTSYS; 56296fd7ce5SGreg Kroah-Hartman break; 56396fd7ce5SGreg Kroah-Hartman } 5640eee50afSJiri Slaby /* 5650eee50afSJiri Slaby * Probe the carrier. For devices with no carrier detect 5660eee50afSJiri Slaby * tty_port_carrier_raised will always return true. 5670eee50afSJiri Slaby * Never ask drivers if CLOCAL is set, this causes troubles 5680eee50afSJiri Slaby * on some hardware. 5690eee50afSJiri Slaby */ 570fef062cbSPeter Hurley if (do_clocal || tty_port_carrier_raised(port)) 57196fd7ce5SGreg Kroah-Hartman break; 57296fd7ce5SGreg Kroah-Hartman if (signal_pending(current)) { 57396fd7ce5SGreg Kroah-Hartman retval = -ERESTARTSYS; 57496fd7ce5SGreg Kroah-Hartman break; 57596fd7ce5SGreg Kroah-Hartman } 57689c8d91eSAlan Cox tty_unlock(tty); 57796fd7ce5SGreg Kroah-Hartman schedule(); 57889c8d91eSAlan Cox tty_lock(tty); 57996fd7ce5SGreg Kroah-Hartman } 58096fd7ce5SGreg Kroah-Hartman finish_wait(&port->open_wait, &wait); 58196fd7ce5SGreg Kroah-Hartman 58296fd7ce5SGreg Kroah-Hartman /* Update counts. A parallel hangup will have set count to zero and 5831df92640SXiaofei Tan * we must not mess that up further. 5841df92640SXiaofei Tan */ 58596fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 58696fd7ce5SGreg Kroah-Hartman if (!tty_hung_up_p(filp)) 58796fd7ce5SGreg Kroah-Hartman port->count++; 58896fd7ce5SGreg Kroah-Hartman port->blocked_open--; 58996fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 590807c8d81SPeter Hurley if (retval == 0) 5919b5aa549SIlpo Järvinen tty_port_set_active(port, true); 59296fd7ce5SGreg Kroah-Hartman return retval; 59396fd7ce5SGreg Kroah-Hartman } 59496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_block_til_ready); 59596fd7ce5SGreg Kroah-Hartman 596b74414f5SJohan Hovold static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) 597b74414f5SJohan Hovold { 598b74414f5SJohan Hovold unsigned int bps = tty_get_baud_rate(tty); 599b74414f5SJohan Hovold long timeout; 600b74414f5SJohan Hovold 601b74414f5SJohan Hovold if (bps > 1200) { 602b74414f5SJohan Hovold timeout = (HZ * 10 * port->drain_delay) / bps; 603b74414f5SJohan Hovold timeout = max_t(long, timeout, HZ / 10); 604b74414f5SJohan Hovold } else { 605b74414f5SJohan Hovold timeout = 2 * HZ; 606b74414f5SJohan Hovold } 607b74414f5SJohan Hovold schedule_timeout_interruptible(timeout); 608b74414f5SJohan Hovold } 609b74414f5SJohan Hovold 6103be491d7SJiri Slaby /** 6113be491d7SJiri Slaby * tty_port_close_start - helper for tty->ops->close, part 1/2 6123be491d7SJiri Slaby * @port: tty_port of the device 6133be491d7SJiri Slaby * @tty: tty being closed 6143be491d7SJiri Slaby * @filp: passed file pointer 6153be491d7SJiri Slaby * 6163be491d7SJiri Slaby * Decrements and checks open count. Flushes the port if this is the last 6173be491d7SJiri Slaby * close. That means, dropping the data from the outpu buffer on the device and 6183be491d7SJiri Slaby * waiting for sending logic to finish. The rest of close handling is performed 6193be491d7SJiri Slaby * in tty_port_close_end(). 6203be491d7SJiri Slaby * 6213be491d7SJiri Slaby * Locking: Caller holds tty lock. 6223be491d7SJiri Slaby * 6233be491d7SJiri Slaby * Return: 1 if this is the last close, otherwise 0 6243be491d7SJiri Slaby */ 62596fd7ce5SGreg Kroah-Hartman int tty_port_close_start(struct tty_port *port, 62696fd7ce5SGreg Kroah-Hartman struct tty_struct *tty, struct file *filp) 62796fd7ce5SGreg Kroah-Hartman { 62896fd7ce5SGreg Kroah-Hartman unsigned long flags; 62996fd7ce5SGreg Kroah-Hartman 630633caba8SPeter Hurley if (tty_hung_up_p(filp)) 63196fd7ce5SGreg Kroah-Hartman return 0; 63296fd7ce5SGreg Kroah-Hartman 633633caba8SPeter Hurley spin_lock_irqsave(&port->lock, flags); 63496fd7ce5SGreg Kroah-Hartman if (tty->count == 1 && port->count != 1) { 635339f36baSPeter Hurley tty_warn(tty, "%s: tty->count = 1 port count = %d\n", __func__, 63696fd7ce5SGreg Kroah-Hartman port->count); 63796fd7ce5SGreg Kroah-Hartman port->count = 1; 63896fd7ce5SGreg Kroah-Hartman } 63996fd7ce5SGreg Kroah-Hartman if (--port->count < 0) { 640339f36baSPeter Hurley tty_warn(tty, "%s: bad port count (%d)\n", __func__, 64196fd7ce5SGreg Kroah-Hartman port->count); 64296fd7ce5SGreg Kroah-Hartman port->count = 0; 64396fd7ce5SGreg Kroah-Hartman } 64496fd7ce5SGreg Kroah-Hartman 64596fd7ce5SGreg Kroah-Hartman if (port->count) { 64696fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 64796fd7ce5SGreg Kroah-Hartman return 0; 64896fd7ce5SGreg Kroah-Hartman } 64996fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 6500b2588caSJohan Hovold 651ddc7b758SPeter Hurley tty->closing = 1; 652ddc7b758SPeter Hurley 653d41861caSPeter Hurley if (tty_port_initialized(port)) { 65496fd7ce5SGreg Kroah-Hartman /* Don't block on a stalled port, just pull the chain */ 6556e94dbc7SJiri Slaby if (tty->flow.tco_stopped) 65696fd7ce5SGreg Kroah-Hartman tty_driver_flush_buffer(tty); 6570b2588caSJohan Hovold if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) 65879c1faa4SPeter Hurley tty_wait_until_sent(tty, port->closing_wait); 659b74414f5SJohan Hovold if (port->drain_delay) 660b74414f5SJohan Hovold tty_port_drain_delay(port, tty); 6610b2588caSJohan Hovold } 66296fd7ce5SGreg Kroah-Hartman /* Flush the ldisc buffering */ 66396fd7ce5SGreg Kroah-Hartman tty_ldisc_flush(tty); 66496fd7ce5SGreg Kroah-Hartman 665469d6d06SPeter Hurley /* Report to caller this is the last port reference */ 66696fd7ce5SGreg Kroah-Hartman return 1; 66796fd7ce5SGreg Kroah-Hartman } 66896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close_start); 66996fd7ce5SGreg Kroah-Hartman 6703be491d7SJiri Slaby /** 6713be491d7SJiri Slaby * tty_port_close_end - helper for tty->ops->close, part 2/2 6723be491d7SJiri Slaby * @port: tty_port of the device 6733be491d7SJiri Slaby * @tty: tty being closed 6743be491d7SJiri Slaby * 6753be491d7SJiri Slaby * This is a continuation of the first part: tty_port_close_start(). This 6763be491d7SJiri Slaby * should be called after turning off the device. It flushes the data from the 6773be491d7SJiri Slaby * line discipline and delays the close by @port->close_delay. 6783be491d7SJiri Slaby * 6793be491d7SJiri Slaby * Locking: Caller holds tty lock. 6803be491d7SJiri Slaby */ 68196fd7ce5SGreg Kroah-Hartman void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) 68296fd7ce5SGreg Kroah-Hartman { 68396fd7ce5SGreg Kroah-Hartman unsigned long flags; 68496fd7ce5SGreg Kroah-Hartman 6853f40f5b2SPeter Hurley tty_ldisc_flush(tty); 68696fd7ce5SGreg Kroah-Hartman tty->closing = 0; 68796fd7ce5SGreg Kroah-Hartman 688ddc7b758SPeter Hurley spin_lock_irqsave(&port->lock, flags); 689ddc7b758SPeter Hurley 69096fd7ce5SGreg Kroah-Hartman if (port->blocked_open) { 69196fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 6925823323eSPeter Hurley if (port->close_delay) 6935823323eSPeter Hurley msleep_interruptible(jiffies_to_msecs(port->close_delay)); 69496fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 69596fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->open_wait); 69696fd7ce5SGreg Kroah-Hartman } 69796fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 6989b5aa549SIlpo Järvinen tty_port_set_active(port, false); 69996fd7ce5SGreg Kroah-Hartman } 70096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close_end); 70196fd7ce5SGreg Kroah-Hartman 702cb6f6f98SJiri Slaby /** 703cb6f6f98SJiri Slaby * tty_port_close - generic tty->ops->close handler 704cb6f6f98SJiri Slaby * @port: tty_port of the device 705cb6f6f98SJiri Slaby * @tty: tty being closed 706cb6f6f98SJiri Slaby * @filp: passed file pointer 7070733db91SPeter Hurley * 708cb6f6f98SJiri Slaby * It is a generic helper to be used in driver's @tty->ops->close. It wraps a 709cb6f6f98SJiri Slaby * sequence of tty_port_close_start(), tty_port_shutdown(), and 710cb6f6f98SJiri Slaby * tty_port_close_end(). The latter two are called only if this is the last 711cb6f6f98SJiri Slaby * close. See the respective functions for the details. 712cb6f6f98SJiri Slaby * 713cb6f6f98SJiri Slaby * Locking: Caller holds tty lock 7140733db91SPeter Hurley */ 71596fd7ce5SGreg Kroah-Hartman void tty_port_close(struct tty_port *port, struct tty_struct *tty, 71696fd7ce5SGreg Kroah-Hartman struct file *filp) 71796fd7ce5SGreg Kroah-Hartman { 71896fd7ce5SGreg Kroah-Hartman if (tty_port_close_start(port, tty, filp) == 0) 71996fd7ce5SGreg Kroah-Hartman return; 720957dacaeSJohan Hovold tty_port_shutdown(port, tty); 7212a486026SChanho Park if (!port->console) 72296fd7ce5SGreg Kroah-Hartman set_bit(TTY_IO_ERROR, &tty->flags); 72396fd7ce5SGreg Kroah-Hartman tty_port_close_end(port, tty); 72496fd7ce5SGreg Kroah-Hartman tty_port_tty_set(port, NULL); 72596fd7ce5SGreg Kroah-Hartman } 72696fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close); 72796fd7ce5SGreg Kroah-Hartman 72872a33bf5SJiri Slaby /** 72972a33bf5SJiri Slaby * tty_port_install - generic tty->ops->install handler 73072a33bf5SJiri Slaby * @port: tty_port of the device 73172a33bf5SJiri Slaby * @driver: tty_driver for this device 73272a33bf5SJiri Slaby * @tty: tty to be installed 73372a33bf5SJiri Slaby * 734cb6f6f98SJiri Slaby * It is the same as tty_standard_install() except the provided @port is linked 735cb6f6f98SJiri Slaby * to a concrete tty specified by @tty. Use this or tty_port_register_device() 736cb6f6f98SJiri Slaby * (or both). Call tty_port_link_device() as a last resort. 73772a33bf5SJiri Slaby */ 738695586caSJiri Slaby int tty_port_install(struct tty_port *port, struct tty_driver *driver, 739695586caSJiri Slaby struct tty_struct *tty) 740695586caSJiri Slaby { 741695586caSJiri Slaby tty->port = port; 742695586caSJiri Slaby return tty_standard_install(driver, tty); 743695586caSJiri Slaby } 744695586caSJiri Slaby EXPORT_SYMBOL_GPL(tty_port_install); 745695586caSJiri Slaby 746cb6f6f98SJiri Slaby /** 747cb6f6f98SJiri Slaby * tty_port_open - generic tty->ops->open handler 748cb6f6f98SJiri Slaby * @port: tty_port of the device 749cb6f6f98SJiri Slaby * @tty: tty to be opened 750cb6f6f98SJiri Slaby * @filp: passed file pointer 751addd4672SPeter Hurley * 752cb6f6f98SJiri Slaby * It is a generic helper to be used in driver's @tty->ops->open. It activates 753cb6f6f98SJiri Slaby * the devices using @port->ops->activate if not active already. And waits for 754cb6f6f98SJiri Slaby * the device to be ready using tty_port_block_til_ready() (e.g. raises 755cb6f6f98SJiri Slaby * DTR/CTS and waits for carrier). 756addd4672SPeter Hurley * 757d56738a3SJiri Slaby * Note that @port->ops->shutdown is not called when @port->ops->activate 758d56738a3SJiri Slaby * returns an error (on the contrary, @tty->ops->close is). 759d56738a3SJiri Slaby * 760cb6f6f98SJiri Slaby * Locking: Caller holds tty lock. 761cb6f6f98SJiri Slaby * 762cb6f6f98SJiri Slaby * Note: may drop and reacquire tty lock (in tty_port_block_til_ready()) so 763cb6f6f98SJiri Slaby * @tty and @port may have changed state (eg., may be hung up now). 764addd4672SPeter Hurley */ 76596fd7ce5SGreg Kroah-Hartman int tty_port_open(struct tty_port *port, struct tty_struct *tty, 76696fd7ce5SGreg Kroah-Hartman struct file *filp) 76796fd7ce5SGreg Kroah-Hartman { 76896fd7ce5SGreg Kroah-Hartman spin_lock_irq(&port->lock); 76996fd7ce5SGreg Kroah-Hartman ++port->count; 77096fd7ce5SGreg Kroah-Hartman spin_unlock_irq(&port->lock); 77196fd7ce5SGreg Kroah-Hartman tty_port_tty_set(port, tty); 77296fd7ce5SGreg Kroah-Hartman 77396fd7ce5SGreg Kroah-Hartman /* 77496fd7ce5SGreg Kroah-Hartman * Do the device-specific open only if the hardware isn't 77596fd7ce5SGreg Kroah-Hartman * already initialized. Serialize open and shutdown using the 77696fd7ce5SGreg Kroah-Hartman * port mutex. 77796fd7ce5SGreg Kroah-Hartman */ 77896fd7ce5SGreg Kroah-Hartman 77996fd7ce5SGreg Kroah-Hartman mutex_lock(&port->mutex); 78096fd7ce5SGreg Kroah-Hartman 781d41861caSPeter Hurley if (!tty_port_initialized(port)) { 78296fd7ce5SGreg Kroah-Hartman clear_bit(TTY_IO_ERROR, &tty->flags); 7830d3cb6f6SJohan Hovold if (port->ops->activate) { 78496fd7ce5SGreg Kroah-Hartman int retval = port->ops->activate(port, tty); 78554ad59a2SXiaofei Tan 78696fd7ce5SGreg Kroah-Hartman if (retval) { 78796fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 78896fd7ce5SGreg Kroah-Hartman return retval; 78996fd7ce5SGreg Kroah-Hartman } 79096fd7ce5SGreg Kroah-Hartman } 791515be7baSIlpo Järvinen tty_port_set_initialized(port, true); 79296fd7ce5SGreg Kroah-Hartman } 79396fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 79496fd7ce5SGreg Kroah-Hartman return tty_port_block_til_ready(port, tty, filp); 79596fd7ce5SGreg Kroah-Hartman } 79696fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_open); 797