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 230468a807SJiri Slaby (SUSE) static size_t tty_port_default_receive_buf(struct tty_port *port, const u8 *p, 240b7a2b28SJiri Slaby (SUSE) const u8 *f, size_t count) 25c3485ee0SRob Herring { 26c3485ee0SRob Herring struct tty_struct *tty; 27c6e37fe0SJiri Slaby (SUSE) struct tty_ldisc *ld; 28c3485ee0SRob Herring 29c3485ee0SRob Herring tty = READ_ONCE(port->itty); 30c3485ee0SRob Herring if (!tty) 31c3485ee0SRob Herring return 0; 32c3485ee0SRob Herring 33c6e37fe0SJiri Slaby (SUSE) ld = tty_ldisc_ref(tty); 34c6e37fe0SJiri Slaby (SUSE) if (!ld) 35c3485ee0SRob Herring return 0; 36c3485ee0SRob Herring 37*892bc209SJiri Slaby (SUSE) count = tty_ldisc_receive_buf(ld, p, f, count); 38c3485ee0SRob Herring 39c6e37fe0SJiri Slaby (SUSE) tty_ldisc_deref(ld); 40c3485ee0SRob Herring 410468a807SJiri Slaby (SUSE) return count; 42c3485ee0SRob Herring } 43c3485ee0SRob Herring 440b7a2b28SJiri Slaby (SUSE) static void tty_port_default_lookahead_buf(struct tty_port *port, const u8 *p, 450468a807SJiri Slaby (SUSE) const u8 *f, size_t count) 466bb6fa69SIlpo Järvinen { 476bb6fa69SIlpo Järvinen struct tty_struct *tty; 48c6e37fe0SJiri Slaby (SUSE) struct tty_ldisc *ld; 496bb6fa69SIlpo Järvinen 506bb6fa69SIlpo Järvinen tty = READ_ONCE(port->itty); 516bb6fa69SIlpo Järvinen if (!tty) 526bb6fa69SIlpo Järvinen return; 536bb6fa69SIlpo Järvinen 54c6e37fe0SJiri Slaby (SUSE) ld = tty_ldisc_ref(tty); 55c6e37fe0SJiri Slaby (SUSE) if (!ld) 566bb6fa69SIlpo Järvinen return; 576bb6fa69SIlpo Järvinen 58c6e37fe0SJiri Slaby (SUSE) if (ld->ops->lookahead_buf) 59c6e37fe0SJiri Slaby (SUSE) ld->ops->lookahead_buf(ld->tty, p, f, count); 606bb6fa69SIlpo Järvinen 61c6e37fe0SJiri Slaby (SUSE) tty_ldisc_deref(ld); 626bb6fa69SIlpo Järvinen } 636bb6fa69SIlpo Järvinen 64c3485ee0SRob Herring static void tty_port_default_wakeup(struct tty_port *port) 65c3485ee0SRob Herring { 66c3485ee0SRob Herring struct tty_struct *tty = tty_port_tty_get(port); 67c3485ee0SRob Herring 68c3485ee0SRob Herring if (tty) { 69c3485ee0SRob Herring tty_wakeup(tty); 70c3485ee0SRob Herring tty_kref_put(tty); 71c3485ee0SRob Herring } 72c3485ee0SRob Herring } 73c3485ee0SRob Herring 740c5aae59SJohan Hovold const struct tty_port_client_operations tty_port_default_client_ops = { 75c3485ee0SRob Herring .receive_buf = tty_port_default_receive_buf, 766bb6fa69SIlpo Järvinen .lookahead_buf = tty_port_default_lookahead_buf, 77c3485ee0SRob Herring .write_wakeup = tty_port_default_wakeup, 78c3485ee0SRob Herring }; 790c5aae59SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_default_client_ops); 80c3485ee0SRob Herring 813be491d7SJiri Slaby /** 823be491d7SJiri Slaby * tty_port_init -- initialize tty_port 833be491d7SJiri Slaby * @port: tty_port to initialize 843be491d7SJiri Slaby * 853be491d7SJiri Slaby * Initializes the state of struct tty_port. When a port was initialized using 863be491d7SJiri Slaby * this function, one has to destroy the port by tty_port_destroy(). Either 873be491d7SJiri Slaby * indirectly by using &tty_port refcounting (tty_port_put()) or directly if 883be491d7SJiri Slaby * refcounting is not used. 893be491d7SJiri Slaby */ 9096fd7ce5SGreg Kroah-Hartman void tty_port_init(struct tty_port *port) 9196fd7ce5SGreg Kroah-Hartman { 9296fd7ce5SGreg Kroah-Hartman memset(port, 0, sizeof(*port)); 93ecbbfd44SJiri Slaby tty_buffer_init(port); 9496fd7ce5SGreg Kroah-Hartman init_waitqueue_head(&port->open_wait); 9596fd7ce5SGreg Kroah-Hartman init_waitqueue_head(&port->delta_msr_wait); 9696fd7ce5SGreg Kroah-Hartman mutex_init(&port->mutex); 9796fd7ce5SGreg Kroah-Hartman mutex_init(&port->buf_mutex); 9896fd7ce5SGreg Kroah-Hartman spin_lock_init(&port->lock); 9996fd7ce5SGreg Kroah-Hartman port->close_delay = (50 * HZ) / 100; 10096fd7ce5SGreg Kroah-Hartman port->closing_wait = (3000 * HZ) / 100; 1010c5aae59SJohan Hovold port->client_ops = &tty_port_default_client_ops; 10296fd7ce5SGreg Kroah-Hartman kref_init(&port->kref); 10396fd7ce5SGreg Kroah-Hartman } 10496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_init); 10596fd7ce5SGreg Kroah-Hartman 10672a33bf5SJiri Slaby /** 1072cb4ca02SJiri Slaby * tty_port_link_device - link tty and tty_port 1082cb4ca02SJiri Slaby * @port: tty_port of the device 1092cb4ca02SJiri Slaby * @driver: tty_driver for this device 1102cb4ca02SJiri Slaby * @index: index of the tty 1112cb4ca02SJiri Slaby * 1122cb4ca02SJiri Slaby * Provide the tty layer with a link from a tty (specified by @index) to a 113cb6f6f98SJiri Slaby * tty_port (@port). Use this only if neither tty_port_register_device() nor 114cb6f6f98SJiri Slaby * tty_port_install() is used in the driver. If used, this has to be called 115cb6f6f98SJiri Slaby * before tty_register_driver(). 1162cb4ca02SJiri Slaby */ 1172cb4ca02SJiri Slaby void tty_port_link_device(struct tty_port *port, 1182cb4ca02SJiri Slaby struct tty_driver *driver, unsigned index) 1192cb4ca02SJiri Slaby { 1202cb4ca02SJiri Slaby if (WARN_ON(index >= driver->num)) 1212cb4ca02SJiri Slaby return; 1222cb4ca02SJiri Slaby driver->ports[index] = port; 1232cb4ca02SJiri Slaby } 1242cb4ca02SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_link_device); 1252cb4ca02SJiri Slaby 1262cb4ca02SJiri Slaby /** 12772a33bf5SJiri Slaby * tty_port_register_device - register tty device 12872a33bf5SJiri Slaby * @port: tty_port of the device 12972a33bf5SJiri Slaby * @driver: tty_driver for this device 13072a33bf5SJiri Slaby * @index: index of the tty 13172a33bf5SJiri Slaby * @device: parent if exists, otherwise NULL 13272a33bf5SJiri Slaby * 133cb6f6f98SJiri Slaby * It is the same as tty_register_device() except the provided @port is linked 134cb6f6f98SJiri Slaby * to a concrete tty specified by @index. Use this or tty_port_install() (or 135cb6f6f98SJiri Slaby * both). Call tty_port_link_device() as a last resort. 13672a33bf5SJiri Slaby */ 137057eb856SJiri Slaby struct device *tty_port_register_device(struct tty_port *port, 138057eb856SJiri Slaby struct tty_driver *driver, unsigned index, 139057eb856SJiri Slaby struct device *device) 140057eb856SJiri Slaby { 1413086365dSRob Herring return tty_port_register_device_attr(port, driver, index, device, NULL, NULL); 142057eb856SJiri Slaby } 143057eb856SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_register_device); 144057eb856SJiri Slaby 145b1b79916STomas Hlavacek /** 146b1b79916STomas Hlavacek * tty_port_register_device_attr - register tty device 147b1b79916STomas Hlavacek * @port: tty_port of the device 148b1b79916STomas Hlavacek * @driver: tty_driver for this device 149b1b79916STomas Hlavacek * @index: index of the tty 150b1b79916STomas Hlavacek * @device: parent if exists, otherwise NULL 151b1b79916STomas Hlavacek * @drvdata: Driver data to be set to device. 152b1b79916STomas Hlavacek * @attr_grp: Attribute group to be set on device. 153b1b79916STomas Hlavacek * 154cb6f6f98SJiri Slaby * It is the same as tty_register_device_attr() except the provided @port is 155cb6f6f98SJiri Slaby * linked to a concrete tty specified by @index. Use this or tty_port_install() 156cb6f6f98SJiri Slaby * (or both). Call tty_port_link_device() as a last resort. 157b1b79916STomas Hlavacek */ 158b1b79916STomas Hlavacek struct device *tty_port_register_device_attr(struct tty_port *port, 159b1b79916STomas Hlavacek struct tty_driver *driver, unsigned index, 160b1b79916STomas Hlavacek struct device *device, void *drvdata, 161b1b79916STomas Hlavacek const struct attribute_group **attr_grp) 162b1b79916STomas Hlavacek { 163b1b79916STomas Hlavacek tty_port_link_device(port, driver, index); 164b1b79916STomas Hlavacek return tty_register_device_attr(driver, index, device, drvdata, 165b1b79916STomas Hlavacek attr_grp); 166b1b79916STomas Hlavacek } 167b1b79916STomas Hlavacek EXPORT_SYMBOL_GPL(tty_port_register_device_attr); 168b1b79916STomas Hlavacek 1698cde11b2SJohan Hovold /** 1708cde11b2SJohan Hovold * tty_port_register_device_attr_serdev - register tty or serdev device 1718cde11b2SJohan Hovold * @port: tty_port of the device 1728cde11b2SJohan Hovold * @driver: tty_driver for this device 1738cde11b2SJohan Hovold * @index: index of the tty 1748cde11b2SJohan Hovold * @device: parent if exists, otherwise NULL 1758cde11b2SJohan Hovold * @drvdata: driver data for the device 1768cde11b2SJohan Hovold * @attr_grp: attribute group for the device 1778cde11b2SJohan Hovold * 1788cde11b2SJohan Hovold * Register a serdev or tty device depending on if the parent device has any 1798cde11b2SJohan Hovold * defined serdev clients or not. 1808cde11b2SJohan Hovold */ 1818cde11b2SJohan Hovold struct device *tty_port_register_device_attr_serdev(struct tty_port *port, 1828cde11b2SJohan Hovold struct tty_driver *driver, unsigned index, 1838cde11b2SJohan Hovold struct device *device, void *drvdata, 1848cde11b2SJohan Hovold const struct attribute_group **attr_grp) 1858cde11b2SJohan Hovold { 1868cde11b2SJohan Hovold struct device *dev; 1878cde11b2SJohan Hovold 1888cde11b2SJohan Hovold tty_port_link_device(port, driver, index); 1898cde11b2SJohan Hovold 1908cde11b2SJohan Hovold dev = serdev_tty_port_register(port, device, driver, index); 1918cde11b2SJohan Hovold if (PTR_ERR(dev) != -ENODEV) { 1928cde11b2SJohan Hovold /* Skip creating cdev if we registered a serdev device */ 1938cde11b2SJohan Hovold return dev; 1948cde11b2SJohan Hovold } 1958cde11b2SJohan Hovold 1968cde11b2SJohan Hovold return tty_register_device_attr(driver, index, device, drvdata, 1978cde11b2SJohan Hovold attr_grp); 1988cde11b2SJohan Hovold } 1998cde11b2SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_register_device_attr_serdev); 2008cde11b2SJohan Hovold 2018cde11b2SJohan Hovold /** 2028cde11b2SJohan Hovold * tty_port_register_device_serdev - register tty or serdev device 2038cde11b2SJohan Hovold * @port: tty_port of the device 2048cde11b2SJohan Hovold * @driver: tty_driver for this device 2058cde11b2SJohan Hovold * @index: index of the tty 2068cde11b2SJohan Hovold * @device: parent if exists, otherwise NULL 2078cde11b2SJohan Hovold * 2088cde11b2SJohan Hovold * Register a serdev or tty device depending on if the parent device has any 2098cde11b2SJohan Hovold * defined serdev clients or not. 2108cde11b2SJohan Hovold */ 2118cde11b2SJohan Hovold struct device *tty_port_register_device_serdev(struct tty_port *port, 2128cde11b2SJohan Hovold struct tty_driver *driver, unsigned index, 2138cde11b2SJohan Hovold struct device *device) 2148cde11b2SJohan Hovold { 2158cde11b2SJohan Hovold return tty_port_register_device_attr_serdev(port, driver, index, 2168cde11b2SJohan Hovold device, NULL, NULL); 2178cde11b2SJohan Hovold } 2188cde11b2SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_register_device_serdev); 2198cde11b2SJohan Hovold 2208cde11b2SJohan Hovold /** 2218cde11b2SJohan Hovold * tty_port_unregister_device - deregister a tty or serdev device 2228cde11b2SJohan Hovold * @port: tty_port of the device 2238cde11b2SJohan Hovold * @driver: tty_driver for this device 2248cde11b2SJohan Hovold * @index: index of the tty 2258cde11b2SJohan Hovold * 2268cde11b2SJohan Hovold * If a tty or serdev device is registered with a call to 2278cde11b2SJohan Hovold * tty_port_register_device_serdev() then this function must be called when 2288cde11b2SJohan Hovold * the device is gone. 2298cde11b2SJohan Hovold */ 2308cde11b2SJohan Hovold void tty_port_unregister_device(struct tty_port *port, 2318cde11b2SJohan Hovold struct tty_driver *driver, unsigned index) 2328cde11b2SJohan Hovold { 2338cde11b2SJohan Hovold int ret; 2348cde11b2SJohan Hovold 2358cde11b2SJohan Hovold ret = serdev_tty_port_unregister(port); 2368cde11b2SJohan Hovold if (ret == 0) 2378cde11b2SJohan Hovold return; 2388cde11b2SJohan Hovold 2398cde11b2SJohan Hovold tty_unregister_device(driver, index); 2408cde11b2SJohan Hovold } 2418cde11b2SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_unregister_device); 2428cde11b2SJohan Hovold 24396fd7ce5SGreg Kroah-Hartman int tty_port_alloc_xmit_buf(struct tty_port *port) 24496fd7ce5SGreg Kroah-Hartman { 24596fd7ce5SGreg Kroah-Hartman /* We may sleep in get_zeroed_page() */ 24696fd7ce5SGreg Kroah-Hartman mutex_lock(&port->buf_mutex); 2474e2a44c1SJiri Slaby if (port->xmit_buf == NULL) { 24896fd7ce5SGreg Kroah-Hartman port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); 2494e2a44c1SJiri Slaby if (port->xmit_buf) 2504e2a44c1SJiri Slaby kfifo_init(&port->xmit_fifo, port->xmit_buf, PAGE_SIZE); 2514e2a44c1SJiri Slaby } 25296fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->buf_mutex); 25396fd7ce5SGreg Kroah-Hartman if (port->xmit_buf == NULL) 25496fd7ce5SGreg Kroah-Hartman return -ENOMEM; 25596fd7ce5SGreg Kroah-Hartman return 0; 25696fd7ce5SGreg Kroah-Hartman } 25796fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_alloc_xmit_buf); 25896fd7ce5SGreg Kroah-Hartman 25996fd7ce5SGreg Kroah-Hartman void tty_port_free_xmit_buf(struct tty_port *port) 26096fd7ce5SGreg Kroah-Hartman { 26196fd7ce5SGreg Kroah-Hartman mutex_lock(&port->buf_mutex); 26296fd7ce5SGreg Kroah-Hartman free_page((unsigned long)port->xmit_buf); 26396fd7ce5SGreg Kroah-Hartman port->xmit_buf = NULL; 2644e2a44c1SJiri Slaby INIT_KFIFO(port->xmit_fifo); 26596fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->buf_mutex); 26696fd7ce5SGreg Kroah-Hartman } 26796fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_free_xmit_buf); 26896fd7ce5SGreg Kroah-Hartman 269de274bfeSJiri Slaby /** 270de274bfeSJiri Slaby * tty_port_destroy -- destroy inited port 2711926e5d3SAntonio Borneo * @port: tty port to be destroyed 272de274bfeSJiri Slaby * 273cb6f6f98SJiri Slaby * When a port was initialized using tty_port_init(), one has to destroy the 274cb6f6f98SJiri Slaby * port by this function. Either indirectly by using &tty_port refcounting 275cb6f6f98SJiri Slaby * (tty_port_put()) or directly if refcounting is not used. 276de274bfeSJiri Slaby */ 277de274bfeSJiri Slaby void tty_port_destroy(struct tty_port *port) 278de274bfeSJiri Slaby { 279e176058fSPeter Hurley tty_buffer_cancel_work(port); 280de274bfeSJiri Slaby tty_buffer_free_all(port); 281de274bfeSJiri Slaby } 282de274bfeSJiri Slaby EXPORT_SYMBOL(tty_port_destroy); 283de274bfeSJiri Slaby 28496fd7ce5SGreg Kroah-Hartman static void tty_port_destructor(struct kref *kref) 28596fd7ce5SGreg Kroah-Hartman { 28696fd7ce5SGreg Kroah-Hartman struct tty_port *port = container_of(kref, struct tty_port, kref); 287e3bfea23SPeter Hurley 288e3bfea23SPeter Hurley /* check if last port ref was dropped before tty release */ 289e3bfea23SPeter Hurley if (WARN_ON(port->itty)) 290e3bfea23SPeter Hurley return; 29196fd7ce5SGreg Kroah-Hartman free_page((unsigned long)port->xmit_buf); 292de274bfeSJiri Slaby tty_port_destroy(port); 29381c79838SJiri Slaby if (port->ops && port->ops->destruct) 29496fd7ce5SGreg Kroah-Hartman port->ops->destruct(port); 29596fd7ce5SGreg Kroah-Hartman else 29696fd7ce5SGreg Kroah-Hartman kfree(port); 29796fd7ce5SGreg Kroah-Hartman } 29896fd7ce5SGreg Kroah-Hartman 2993be491d7SJiri Slaby /** 3003be491d7SJiri Slaby * tty_port_put -- drop a reference to tty_port 3013be491d7SJiri Slaby * @port: port to drop a reference of (can be NULL) 3023be491d7SJiri Slaby * 3033be491d7SJiri Slaby * The final put will destroy and free up the @port using 3043be491d7SJiri Slaby * @port->ops->destruct() hook, or using kfree() if not provided. 3053be491d7SJiri Slaby */ 30696fd7ce5SGreg Kroah-Hartman void tty_port_put(struct tty_port *port) 30796fd7ce5SGreg Kroah-Hartman { 30896fd7ce5SGreg Kroah-Hartman if (port) 30996fd7ce5SGreg Kroah-Hartman kref_put(&port->kref, tty_port_destructor); 31096fd7ce5SGreg Kroah-Hartman } 31196fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_put); 31296fd7ce5SGreg Kroah-Hartman 31396fd7ce5SGreg Kroah-Hartman /** 31496fd7ce5SGreg Kroah-Hartman * tty_port_tty_get - get a tty reference 31596fd7ce5SGreg Kroah-Hartman * @port: tty port 31696fd7ce5SGreg Kroah-Hartman * 317cb6f6f98SJiri Slaby * Return a refcount protected tty instance or %NULL if the port is not 318cb6f6f98SJiri Slaby * associated with a tty (eg due to close or hangup). 31996fd7ce5SGreg Kroah-Hartman */ 32096fd7ce5SGreg Kroah-Hartman struct tty_struct *tty_port_tty_get(struct tty_port *port) 32196fd7ce5SGreg Kroah-Hartman { 32296fd7ce5SGreg Kroah-Hartman unsigned long flags; 32396fd7ce5SGreg Kroah-Hartman struct tty_struct *tty; 32496fd7ce5SGreg Kroah-Hartman 32596fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 32696fd7ce5SGreg Kroah-Hartman tty = tty_kref_get(port->tty); 32796fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 32896fd7ce5SGreg Kroah-Hartman return tty; 32996fd7ce5SGreg Kroah-Hartman } 33096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_tty_get); 33196fd7ce5SGreg Kroah-Hartman 33296fd7ce5SGreg Kroah-Hartman /** 33396fd7ce5SGreg Kroah-Hartman * tty_port_tty_set - set the tty of a port 33496fd7ce5SGreg Kroah-Hartman * @port: tty port 33596fd7ce5SGreg Kroah-Hartman * @tty: the tty 33696fd7ce5SGreg Kroah-Hartman * 337cb6f6f98SJiri Slaby * Associate the port and tty pair. Manages any internal refcounts. Pass %NULL 338cb6f6f98SJiri Slaby * to deassociate a port. 33996fd7ce5SGreg Kroah-Hartman */ 34096fd7ce5SGreg Kroah-Hartman void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) 34196fd7ce5SGreg Kroah-Hartman { 34296fd7ce5SGreg Kroah-Hartman unsigned long flags; 34396fd7ce5SGreg Kroah-Hartman 34496fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 34596fd7ce5SGreg Kroah-Hartman tty_kref_put(port->tty); 34696fd7ce5SGreg Kroah-Hartman port->tty = tty_kref_get(tty); 34796fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 34896fd7ce5SGreg Kroah-Hartman } 34996fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_tty_set); 35096fd7ce5SGreg Kroah-Hartman 3513be491d7SJiri Slaby /** 3523be491d7SJiri Slaby * tty_port_shutdown - internal helper to shutdown the device 3533be491d7SJiri Slaby * @port: tty port to be shut down 3543be491d7SJiri Slaby * @tty: the associated tty 3553be491d7SJiri Slaby * 3563be491d7SJiri Slaby * It is used by tty_port_hangup() and tty_port_close(). Its task is to 3573be491d7SJiri Slaby * shutdown the device if it was initialized (note consoles remain 3583be491d7SJiri Slaby * functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes 3593be491d7SJiri Slaby * @port->ops->shutdown(). 3603be491d7SJiri Slaby */ 361957dacaeSJohan Hovold static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) 36296fd7ce5SGreg Kroah-Hartman { 36396fd7ce5SGreg Kroah-Hartman mutex_lock(&port->mutex); 3648bde9658SJohan Hovold if (port->console) 3658bde9658SJohan Hovold goto out; 3668bde9658SJohan Hovold 367d41861caSPeter Hurley if (tty_port_initialized(port)) { 368515be7baSIlpo Järvinen tty_port_set_initialized(port, false); 369957dacaeSJohan Hovold /* 370957dacaeSJohan Hovold * Drop DTR/RTS if HUPCL is set. This causes any attached 371957dacaeSJohan Hovold * modem to hang up the line. 372957dacaeSJohan Hovold */ 373957dacaeSJohan Hovold if (tty && C_HUPCL(tty)) 374957dacaeSJohan Hovold tty_port_lower_dtr_rts(port); 375957dacaeSJohan Hovold 3760d3cb6f6SJohan Hovold if (port->ops->shutdown) 37796fd7ce5SGreg Kroah-Hartman port->ops->shutdown(port); 3788bde9658SJohan Hovold } 3798bde9658SJohan Hovold out: 38096fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 38196fd7ce5SGreg Kroah-Hartman } 38296fd7ce5SGreg Kroah-Hartman 38396fd7ce5SGreg Kroah-Hartman /** 38496fd7ce5SGreg Kroah-Hartman * tty_port_hangup - hangup helper 38596fd7ce5SGreg Kroah-Hartman * @port: tty port 38696fd7ce5SGreg Kroah-Hartman * 38796fd7ce5SGreg Kroah-Hartman * Perform port level tty hangup flag and count changes. Drop the tty 38896fd7ce5SGreg Kroah-Hartman * reference. 3899c9928bdSPeter Hurley * 3909c9928bdSPeter Hurley * Caller holds tty lock. 39196fd7ce5SGreg Kroah-Hartman */ 39296fd7ce5SGreg Kroah-Hartman void tty_port_hangup(struct tty_port *port) 39396fd7ce5SGreg Kroah-Hartman { 394957dacaeSJohan Hovold struct tty_struct *tty; 39596fd7ce5SGreg Kroah-Hartman unsigned long flags; 39696fd7ce5SGreg Kroah-Hartman 39796fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 39896fd7ce5SGreg Kroah-Hartman port->count = 0; 399957dacaeSJohan Hovold tty = port->tty; 400957dacaeSJohan Hovold if (tty) 401957dacaeSJohan Hovold set_bit(TTY_IO_ERROR, &tty->flags); 40296fd7ce5SGreg Kroah-Hartman port->tty = NULL; 40396fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 4049b5aa549SIlpo Järvinen tty_port_set_active(port, false); 405957dacaeSJohan Hovold tty_port_shutdown(port, tty); 406957dacaeSJohan Hovold tty_kref_put(tty); 40796fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->open_wait); 40896fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->delta_msr_wait); 40996fd7ce5SGreg Kroah-Hartman } 41096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_hangup); 41196fd7ce5SGreg Kroah-Hartman 41296fd7ce5SGreg Kroah-Hartman /** 413aa27a094SJiri Slaby * tty_port_tty_hangup - helper to hang up a tty 414aa27a094SJiri Slaby * @port: tty port 415cb6f6f98SJiri Slaby * @check_clocal: hang only ttys with %CLOCAL unset? 416aa27a094SJiri Slaby */ 417aa27a094SJiri Slaby void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) 418aa27a094SJiri Slaby { 419aa27a094SJiri Slaby struct tty_struct *tty = tty_port_tty_get(port); 420aa27a094SJiri Slaby 4211d9e689cSGianluca Anzolin if (tty && (!check_clocal || !C_CLOCAL(tty))) 422aa27a094SJiri Slaby tty_hangup(tty); 423aa27a094SJiri Slaby tty_kref_put(tty); 424aa27a094SJiri Slaby } 425aa27a094SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_tty_hangup); 426aa27a094SJiri Slaby 427aa27a094SJiri Slaby /** 4286aad04f2SJiri Slaby * tty_port_tty_wakeup - helper to wake up a tty 4296aad04f2SJiri Slaby * @port: tty port 4306aad04f2SJiri Slaby */ 4316aad04f2SJiri Slaby void tty_port_tty_wakeup(struct tty_port *port) 4326aad04f2SJiri Slaby { 433c3485ee0SRob Herring port->client_ops->write_wakeup(port); 4346aad04f2SJiri Slaby } 4356aad04f2SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); 4366aad04f2SJiri Slaby 4376aad04f2SJiri Slaby /** 43896fd7ce5SGreg Kroah-Hartman * tty_port_carrier_raised - carrier raised check 43996fd7ce5SGreg Kroah-Hartman * @port: tty port 44096fd7ce5SGreg Kroah-Hartman * 44196fd7ce5SGreg Kroah-Hartman * Wrapper for the carrier detect logic. For the moment this is used 44296fd7ce5SGreg Kroah-Hartman * to hide some internal details. This will eventually become entirely 44396fd7ce5SGreg Kroah-Hartman * internal to the tty port. 44496fd7ce5SGreg Kroah-Hartman */ 445b300fb26SIlpo Järvinen bool tty_port_carrier_raised(struct tty_port *port) 44696fd7ce5SGreg Kroah-Hartman { 4470d3cb6f6SJohan Hovold if (port->ops->carrier_raised == NULL) 448b300fb26SIlpo Järvinen return true; 44996fd7ce5SGreg Kroah-Hartman return port->ops->carrier_raised(port); 45096fd7ce5SGreg Kroah-Hartman } 45196fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_carrier_raised); 45296fd7ce5SGreg Kroah-Hartman 45396fd7ce5SGreg Kroah-Hartman /** 45496fd7ce5SGreg Kroah-Hartman * tty_port_raise_dtr_rts - Raise DTR/RTS 45596fd7ce5SGreg Kroah-Hartman * @port: tty port 45696fd7ce5SGreg Kroah-Hartman * 457cb6f6f98SJiri Slaby * Wrapper for the DTR/RTS raise logic. For the moment this is used to hide 458cb6f6f98SJiri Slaby * some internal details. This will eventually become entirely internal to the 459cb6f6f98SJiri Slaby * tty port. 46096fd7ce5SGreg Kroah-Hartman */ 46196fd7ce5SGreg Kroah-Hartman void tty_port_raise_dtr_rts(struct tty_port *port) 46296fd7ce5SGreg Kroah-Hartman { 4630d3cb6f6SJohan Hovold if (port->ops->dtr_rts) 4645d420399SIlpo Järvinen port->ops->dtr_rts(port, true); 46596fd7ce5SGreg Kroah-Hartman } 46696fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_raise_dtr_rts); 46796fd7ce5SGreg Kroah-Hartman 46896fd7ce5SGreg Kroah-Hartman /** 46996fd7ce5SGreg Kroah-Hartman * tty_port_lower_dtr_rts - Lower DTR/RTS 47096fd7ce5SGreg Kroah-Hartman * @port: tty port 47196fd7ce5SGreg Kroah-Hartman * 472cb6f6f98SJiri Slaby * Wrapper for the DTR/RTS raise logic. For the moment this is used to hide 473cb6f6f98SJiri Slaby * some internal details. This will eventually become entirely internal to the 474cb6f6f98SJiri Slaby * tty port. 47596fd7ce5SGreg Kroah-Hartman */ 47696fd7ce5SGreg Kroah-Hartman void tty_port_lower_dtr_rts(struct tty_port *port) 47796fd7ce5SGreg Kroah-Hartman { 4780d3cb6f6SJohan Hovold if (port->ops->dtr_rts) 4795d420399SIlpo Järvinen port->ops->dtr_rts(port, false); 48096fd7ce5SGreg Kroah-Hartman } 48196fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_lower_dtr_rts); 48296fd7ce5SGreg Kroah-Hartman 48396fd7ce5SGreg Kroah-Hartman /** 48496fd7ce5SGreg Kroah-Hartman * tty_port_block_til_ready - Waiting logic for tty open 48596fd7ce5SGreg Kroah-Hartman * @port: the tty port being opened 48696fd7ce5SGreg Kroah-Hartman * @tty: the tty device being bound 487cb6f6f98SJiri Slaby * @filp: the file pointer of the opener or %NULL 48896fd7ce5SGreg Kroah-Hartman * 48996fd7ce5SGreg Kroah-Hartman * Implement the core POSIX/SuS tty behaviour when opening a tty device. 49096fd7ce5SGreg Kroah-Hartman * Handles: 491cb6f6f98SJiri Slaby * 49296fd7ce5SGreg Kroah-Hartman * - hangup (both before and during) 49396fd7ce5SGreg Kroah-Hartman * - non blocking open 49496fd7ce5SGreg Kroah-Hartman * - rts/dtr/dcd 49596fd7ce5SGreg Kroah-Hartman * - signals 49696fd7ce5SGreg Kroah-Hartman * - port flags and counts 49796fd7ce5SGreg Kroah-Hartman * 498cb6f6f98SJiri Slaby * The passed @port must implement the @port->ops->carrier_raised method if it 499cb6f6f98SJiri Slaby * can do carrier detect and the @port->ops->dtr_rts method if it supports 500cb6f6f98SJiri Slaby * software management of these lines. Note that the dtr/rts raise is done each 50196fd7ce5SGreg Kroah-Hartman * iteration as a hangup may have previously dropped them while we wait. 502c590f6b6SPeter Hurley * 503c590f6b6SPeter Hurley * Caller holds tty lock. 504c590f6b6SPeter Hurley * 505cb6f6f98SJiri Slaby * Note: May drop and reacquire tty lock when blocking, so @tty and @port may 506cb6f6f98SJiri Slaby * have changed state (eg., may have been hung up). 50796fd7ce5SGreg Kroah-Hartman */ 50896fd7ce5SGreg Kroah-Hartman int tty_port_block_til_ready(struct tty_port *port, 50996fd7ce5SGreg Kroah-Hartman struct tty_struct *tty, struct file *filp) 51096fd7ce5SGreg Kroah-Hartman { 51196fd7ce5SGreg Kroah-Hartman int do_clocal = 0, retval; 51296fd7ce5SGreg Kroah-Hartman unsigned long flags; 51396fd7ce5SGreg Kroah-Hartman DEFINE_WAIT(wait); 51496fd7ce5SGreg Kroah-Hartman 51596fd7ce5SGreg Kroah-Hartman /* if non-blocking mode is set we can pass directly to open unless 5161df92640SXiaofei Tan * the port has just hung up or is in another error state. 5171df92640SXiaofei Tan */ 51818900ca6SPeter Hurley if (tty_io_error(tty)) { 5199b5aa549SIlpo Järvinen tty_port_set_active(port, true); 52096fd7ce5SGreg Kroah-Hartman return 0; 52196fd7ce5SGreg Kroah-Hartman } 522ed3f0af8SAlan Cox if (filp == NULL || (filp->f_flags & O_NONBLOCK)) { 52396fd7ce5SGreg Kroah-Hartman /* Indicate we are open */ 5249db276f8SPeter Hurley if (C_BAUD(tty)) 52596fd7ce5SGreg Kroah-Hartman tty_port_raise_dtr_rts(port); 5269b5aa549SIlpo Järvinen tty_port_set_active(port, true); 52796fd7ce5SGreg Kroah-Hartman return 0; 52896fd7ce5SGreg Kroah-Hartman } 52996fd7ce5SGreg Kroah-Hartman 53096fd7ce5SGreg Kroah-Hartman if (C_CLOCAL(tty)) 53196fd7ce5SGreg Kroah-Hartman do_clocal = 1; 53296fd7ce5SGreg Kroah-Hartman 53396fd7ce5SGreg Kroah-Hartman /* Block waiting until we can proceed. We may need to wait for the 5341df92640SXiaofei Tan * carrier, but we must also wait for any close that is in progress 5351df92640SXiaofei Tan * before the next open may complete. 5361df92640SXiaofei Tan */ 53796fd7ce5SGreg Kroah-Hartman 53896fd7ce5SGreg Kroah-Hartman retval = 0; 53996fd7ce5SGreg Kroah-Hartman 54096fd7ce5SGreg Kroah-Hartman /* The port lock protects the port counts */ 54196fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 54296fd7ce5SGreg Kroah-Hartman port->count--; 54396fd7ce5SGreg Kroah-Hartman port->blocked_open++; 54496fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 54596fd7ce5SGreg Kroah-Hartman 54696fd7ce5SGreg Kroah-Hartman while (1) { 54796fd7ce5SGreg Kroah-Hartman /* Indicate we are open */ 548d41861caSPeter Hurley if (C_BAUD(tty) && tty_port_initialized(port)) 54996fd7ce5SGreg Kroah-Hartman tty_port_raise_dtr_rts(port); 55096fd7ce5SGreg Kroah-Hartman 55196fd7ce5SGreg Kroah-Hartman prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); 55296fd7ce5SGreg Kroah-Hartman /* Check for a hangup or uninitialised port. 5531df92640SXiaofei Tan * Return accordingly. 5541df92640SXiaofei Tan */ 555d41861caSPeter Hurley if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { 55696fd7ce5SGreg Kroah-Hartman if (port->flags & ASYNC_HUP_NOTIFY) 55796fd7ce5SGreg Kroah-Hartman retval = -EAGAIN; 55896fd7ce5SGreg Kroah-Hartman else 55996fd7ce5SGreg Kroah-Hartman retval = -ERESTARTSYS; 56096fd7ce5SGreg Kroah-Hartman break; 56196fd7ce5SGreg Kroah-Hartman } 5620eee50afSJiri Slaby /* 5630eee50afSJiri Slaby * Probe the carrier. For devices with no carrier detect 5640eee50afSJiri Slaby * tty_port_carrier_raised will always return true. 5650eee50afSJiri Slaby * Never ask drivers if CLOCAL is set, this causes troubles 5660eee50afSJiri Slaby * on some hardware. 5670eee50afSJiri Slaby */ 568fef062cbSPeter Hurley if (do_clocal || tty_port_carrier_raised(port)) 56996fd7ce5SGreg Kroah-Hartman break; 57096fd7ce5SGreg Kroah-Hartman if (signal_pending(current)) { 57196fd7ce5SGreg Kroah-Hartman retval = -ERESTARTSYS; 57296fd7ce5SGreg Kroah-Hartman break; 57396fd7ce5SGreg Kroah-Hartman } 57489c8d91eSAlan Cox tty_unlock(tty); 57596fd7ce5SGreg Kroah-Hartman schedule(); 57689c8d91eSAlan Cox tty_lock(tty); 57796fd7ce5SGreg Kroah-Hartman } 57896fd7ce5SGreg Kroah-Hartman finish_wait(&port->open_wait, &wait); 57996fd7ce5SGreg Kroah-Hartman 58096fd7ce5SGreg Kroah-Hartman /* Update counts. A parallel hangup will have set count to zero and 5811df92640SXiaofei Tan * we must not mess that up further. 5821df92640SXiaofei Tan */ 58396fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 58496fd7ce5SGreg Kroah-Hartman if (!tty_hung_up_p(filp)) 58596fd7ce5SGreg Kroah-Hartman port->count++; 58696fd7ce5SGreg Kroah-Hartman port->blocked_open--; 58796fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 588807c8d81SPeter Hurley if (retval == 0) 5899b5aa549SIlpo Järvinen tty_port_set_active(port, true); 59096fd7ce5SGreg Kroah-Hartman return retval; 59196fd7ce5SGreg Kroah-Hartman } 59296fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_block_til_ready); 59396fd7ce5SGreg Kroah-Hartman 594b74414f5SJohan Hovold static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) 595b74414f5SJohan Hovold { 596b74414f5SJohan Hovold unsigned int bps = tty_get_baud_rate(tty); 597b74414f5SJohan Hovold long timeout; 598b74414f5SJohan Hovold 599b74414f5SJohan Hovold if (bps > 1200) { 600b74414f5SJohan Hovold timeout = (HZ * 10 * port->drain_delay) / bps; 601b74414f5SJohan Hovold timeout = max_t(long, timeout, HZ / 10); 602b74414f5SJohan Hovold } else { 603b74414f5SJohan Hovold timeout = 2 * HZ; 604b74414f5SJohan Hovold } 605b74414f5SJohan Hovold schedule_timeout_interruptible(timeout); 606b74414f5SJohan Hovold } 607b74414f5SJohan Hovold 6083be491d7SJiri Slaby /** 6093be491d7SJiri Slaby * tty_port_close_start - helper for tty->ops->close, part 1/2 6103be491d7SJiri Slaby * @port: tty_port of the device 6113be491d7SJiri Slaby * @tty: tty being closed 6123be491d7SJiri Slaby * @filp: passed file pointer 6133be491d7SJiri Slaby * 6143be491d7SJiri Slaby * Decrements and checks open count. Flushes the port if this is the last 6153be491d7SJiri Slaby * close. That means, dropping the data from the outpu buffer on the device and 6163be491d7SJiri Slaby * waiting for sending logic to finish. The rest of close handling is performed 6173be491d7SJiri Slaby * in tty_port_close_end(). 6183be491d7SJiri Slaby * 6193be491d7SJiri Slaby * Locking: Caller holds tty lock. 6203be491d7SJiri Slaby * 6213be491d7SJiri Slaby * Return: 1 if this is the last close, otherwise 0 6223be491d7SJiri Slaby */ 62396fd7ce5SGreg Kroah-Hartman int tty_port_close_start(struct tty_port *port, 62496fd7ce5SGreg Kroah-Hartman struct tty_struct *tty, struct file *filp) 62596fd7ce5SGreg Kroah-Hartman { 62696fd7ce5SGreg Kroah-Hartman unsigned long flags; 62796fd7ce5SGreg Kroah-Hartman 628633caba8SPeter Hurley if (tty_hung_up_p(filp)) 62996fd7ce5SGreg Kroah-Hartman return 0; 63096fd7ce5SGreg Kroah-Hartman 631633caba8SPeter Hurley spin_lock_irqsave(&port->lock, flags); 63296fd7ce5SGreg Kroah-Hartman if (tty->count == 1 && port->count != 1) { 633339f36baSPeter Hurley tty_warn(tty, "%s: tty->count = 1 port count = %d\n", __func__, 63496fd7ce5SGreg Kroah-Hartman port->count); 63596fd7ce5SGreg Kroah-Hartman port->count = 1; 63696fd7ce5SGreg Kroah-Hartman } 63796fd7ce5SGreg Kroah-Hartman if (--port->count < 0) { 638339f36baSPeter Hurley tty_warn(tty, "%s: bad port count (%d)\n", __func__, 63996fd7ce5SGreg Kroah-Hartman port->count); 64096fd7ce5SGreg Kroah-Hartman port->count = 0; 64196fd7ce5SGreg Kroah-Hartman } 64296fd7ce5SGreg Kroah-Hartman 64396fd7ce5SGreg Kroah-Hartman if (port->count) { 64496fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 64596fd7ce5SGreg Kroah-Hartman return 0; 64696fd7ce5SGreg Kroah-Hartman } 64796fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 6480b2588caSJohan Hovold 649ddc7b758SPeter Hurley tty->closing = 1; 650ddc7b758SPeter Hurley 651d41861caSPeter Hurley if (tty_port_initialized(port)) { 65296fd7ce5SGreg Kroah-Hartman /* Don't block on a stalled port, just pull the chain */ 6536e94dbc7SJiri Slaby if (tty->flow.tco_stopped) 65496fd7ce5SGreg Kroah-Hartman tty_driver_flush_buffer(tty); 6550b2588caSJohan Hovold if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) 65679c1faa4SPeter Hurley tty_wait_until_sent(tty, port->closing_wait); 657b74414f5SJohan Hovold if (port->drain_delay) 658b74414f5SJohan Hovold tty_port_drain_delay(port, tty); 6590b2588caSJohan Hovold } 66096fd7ce5SGreg Kroah-Hartman /* Flush the ldisc buffering */ 66196fd7ce5SGreg Kroah-Hartman tty_ldisc_flush(tty); 66296fd7ce5SGreg Kroah-Hartman 663469d6d06SPeter Hurley /* Report to caller this is the last port reference */ 66496fd7ce5SGreg Kroah-Hartman return 1; 66596fd7ce5SGreg Kroah-Hartman } 66696fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close_start); 66796fd7ce5SGreg Kroah-Hartman 6683be491d7SJiri Slaby /** 6693be491d7SJiri Slaby * tty_port_close_end - helper for tty->ops->close, part 2/2 6703be491d7SJiri Slaby * @port: tty_port of the device 6713be491d7SJiri Slaby * @tty: tty being closed 6723be491d7SJiri Slaby * 6733be491d7SJiri Slaby * This is a continuation of the first part: tty_port_close_start(). This 6743be491d7SJiri Slaby * should be called after turning off the device. It flushes the data from the 6753be491d7SJiri Slaby * line discipline and delays the close by @port->close_delay. 6763be491d7SJiri Slaby * 6773be491d7SJiri Slaby * Locking: Caller holds tty lock. 6783be491d7SJiri Slaby */ 67996fd7ce5SGreg Kroah-Hartman void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) 68096fd7ce5SGreg Kroah-Hartman { 68196fd7ce5SGreg Kroah-Hartman unsigned long flags; 68296fd7ce5SGreg Kroah-Hartman 6833f40f5b2SPeter Hurley tty_ldisc_flush(tty); 68496fd7ce5SGreg Kroah-Hartman tty->closing = 0; 68596fd7ce5SGreg Kroah-Hartman 686ddc7b758SPeter Hurley spin_lock_irqsave(&port->lock, flags); 687ddc7b758SPeter Hurley 68896fd7ce5SGreg Kroah-Hartman if (port->blocked_open) { 68996fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 6905823323eSPeter Hurley if (port->close_delay) 6915823323eSPeter Hurley msleep_interruptible(jiffies_to_msecs(port->close_delay)); 69296fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 69396fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->open_wait); 69496fd7ce5SGreg Kroah-Hartman } 69596fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 6969b5aa549SIlpo Järvinen tty_port_set_active(port, false); 69796fd7ce5SGreg Kroah-Hartman } 69896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close_end); 69996fd7ce5SGreg Kroah-Hartman 700cb6f6f98SJiri Slaby /** 701cb6f6f98SJiri Slaby * tty_port_close - generic tty->ops->close handler 702cb6f6f98SJiri Slaby * @port: tty_port of the device 703cb6f6f98SJiri Slaby * @tty: tty being closed 704cb6f6f98SJiri Slaby * @filp: passed file pointer 7050733db91SPeter Hurley * 706cb6f6f98SJiri Slaby * It is a generic helper to be used in driver's @tty->ops->close. It wraps a 707cb6f6f98SJiri Slaby * sequence of tty_port_close_start(), tty_port_shutdown(), and 708cb6f6f98SJiri Slaby * tty_port_close_end(). The latter two are called only if this is the last 709cb6f6f98SJiri Slaby * close. See the respective functions for the details. 710cb6f6f98SJiri Slaby * 711cb6f6f98SJiri Slaby * Locking: Caller holds tty lock 7120733db91SPeter Hurley */ 71396fd7ce5SGreg Kroah-Hartman void tty_port_close(struct tty_port *port, struct tty_struct *tty, 71496fd7ce5SGreg Kroah-Hartman struct file *filp) 71596fd7ce5SGreg Kroah-Hartman { 71696fd7ce5SGreg Kroah-Hartman if (tty_port_close_start(port, tty, filp) == 0) 71796fd7ce5SGreg Kroah-Hartman return; 718957dacaeSJohan Hovold tty_port_shutdown(port, tty); 7192a486026SChanho Park if (!port->console) 72096fd7ce5SGreg Kroah-Hartman set_bit(TTY_IO_ERROR, &tty->flags); 72196fd7ce5SGreg Kroah-Hartman tty_port_close_end(port, tty); 72296fd7ce5SGreg Kroah-Hartman tty_port_tty_set(port, NULL); 72396fd7ce5SGreg Kroah-Hartman } 72496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close); 72596fd7ce5SGreg Kroah-Hartman 72672a33bf5SJiri Slaby /** 72772a33bf5SJiri Slaby * tty_port_install - generic tty->ops->install handler 72872a33bf5SJiri Slaby * @port: tty_port of the device 72972a33bf5SJiri Slaby * @driver: tty_driver for this device 73072a33bf5SJiri Slaby * @tty: tty to be installed 73172a33bf5SJiri Slaby * 732cb6f6f98SJiri Slaby * It is the same as tty_standard_install() except the provided @port is linked 733cb6f6f98SJiri Slaby * to a concrete tty specified by @tty. Use this or tty_port_register_device() 734cb6f6f98SJiri Slaby * (or both). Call tty_port_link_device() as a last resort. 73572a33bf5SJiri Slaby */ 736695586caSJiri Slaby int tty_port_install(struct tty_port *port, struct tty_driver *driver, 737695586caSJiri Slaby struct tty_struct *tty) 738695586caSJiri Slaby { 739695586caSJiri Slaby tty->port = port; 740695586caSJiri Slaby return tty_standard_install(driver, tty); 741695586caSJiri Slaby } 742695586caSJiri Slaby EXPORT_SYMBOL_GPL(tty_port_install); 743695586caSJiri Slaby 744cb6f6f98SJiri Slaby /** 745cb6f6f98SJiri Slaby * tty_port_open - generic tty->ops->open handler 746cb6f6f98SJiri Slaby * @port: tty_port of the device 747cb6f6f98SJiri Slaby * @tty: tty to be opened 748cb6f6f98SJiri Slaby * @filp: passed file pointer 749addd4672SPeter Hurley * 750cb6f6f98SJiri Slaby * It is a generic helper to be used in driver's @tty->ops->open. It activates 751cb6f6f98SJiri Slaby * the devices using @port->ops->activate if not active already. And waits for 752cb6f6f98SJiri Slaby * the device to be ready using tty_port_block_til_ready() (e.g. raises 753cb6f6f98SJiri Slaby * DTR/CTS and waits for carrier). 754addd4672SPeter Hurley * 755d56738a3SJiri Slaby * Note that @port->ops->shutdown is not called when @port->ops->activate 756d56738a3SJiri Slaby * returns an error (on the contrary, @tty->ops->close is). 757d56738a3SJiri Slaby * 758cb6f6f98SJiri Slaby * Locking: Caller holds tty lock. 759cb6f6f98SJiri Slaby * 760cb6f6f98SJiri Slaby * Note: may drop and reacquire tty lock (in tty_port_block_til_ready()) so 761cb6f6f98SJiri Slaby * @tty and @port may have changed state (eg., may be hung up now). 762addd4672SPeter Hurley */ 76396fd7ce5SGreg Kroah-Hartman int tty_port_open(struct tty_port *port, struct tty_struct *tty, 76496fd7ce5SGreg Kroah-Hartman struct file *filp) 76596fd7ce5SGreg Kroah-Hartman { 76696fd7ce5SGreg Kroah-Hartman spin_lock_irq(&port->lock); 76796fd7ce5SGreg Kroah-Hartman ++port->count; 76896fd7ce5SGreg Kroah-Hartman spin_unlock_irq(&port->lock); 76996fd7ce5SGreg Kroah-Hartman tty_port_tty_set(port, tty); 77096fd7ce5SGreg Kroah-Hartman 77196fd7ce5SGreg Kroah-Hartman /* 77296fd7ce5SGreg Kroah-Hartman * Do the device-specific open only if the hardware isn't 77396fd7ce5SGreg Kroah-Hartman * already initialized. Serialize open and shutdown using the 77496fd7ce5SGreg Kroah-Hartman * port mutex. 77596fd7ce5SGreg Kroah-Hartman */ 77696fd7ce5SGreg Kroah-Hartman 77796fd7ce5SGreg Kroah-Hartman mutex_lock(&port->mutex); 77896fd7ce5SGreg Kroah-Hartman 779d41861caSPeter Hurley if (!tty_port_initialized(port)) { 78096fd7ce5SGreg Kroah-Hartman clear_bit(TTY_IO_ERROR, &tty->flags); 7810d3cb6f6SJohan Hovold if (port->ops->activate) { 78296fd7ce5SGreg Kroah-Hartman int retval = port->ops->activate(port, tty); 78354ad59a2SXiaofei Tan 78496fd7ce5SGreg Kroah-Hartman if (retval) { 78596fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 78696fd7ce5SGreg Kroah-Hartman return retval; 78796fd7ce5SGreg Kroah-Hartman } 78896fd7ce5SGreg Kroah-Hartman } 789515be7baSIlpo Järvinen tty_port_set_initialized(port, true); 79096fd7ce5SGreg Kroah-Hartman } 79196fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 79296fd7ce5SGreg Kroah-Hartman return tty_port_block_til_ready(port, tty, filp); 79396fd7ce5SGreg Kroah-Hartman } 79496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_open); 795