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; 29c3485ee0SRob Herring struct tty_ldisc *disc; 30c3485ee0SRob Herring 31c3485ee0SRob Herring tty = READ_ONCE(port->itty); 32c3485ee0SRob Herring if (!tty) 33c3485ee0SRob Herring return 0; 34c3485ee0SRob Herring 35c3485ee0SRob Herring disc = tty_ldisc_ref(tty); 36c3485ee0SRob Herring if (!disc) 37c3485ee0SRob Herring return 0; 38c3485ee0SRob Herring 39c3485ee0SRob Herring ret = tty_ldisc_receive_buf(disc, p, (char *)f, count); 40c3485ee0SRob Herring 41c3485ee0SRob Herring tty_ldisc_deref(disc); 42c3485ee0SRob Herring 43c3485ee0SRob Herring return ret; 44c3485ee0SRob Herring } 45c3485ee0SRob Herring 46c3485ee0SRob Herring static void tty_port_default_wakeup(struct tty_port *port) 47c3485ee0SRob Herring { 48c3485ee0SRob Herring struct tty_struct *tty = tty_port_tty_get(port); 49c3485ee0SRob Herring 50c3485ee0SRob Herring if (tty) { 51c3485ee0SRob Herring tty_wakeup(tty); 52c3485ee0SRob Herring tty_kref_put(tty); 53c3485ee0SRob Herring } 54c3485ee0SRob Herring } 55c3485ee0SRob Herring 560c5aae59SJohan Hovold const struct tty_port_client_operations tty_port_default_client_ops = { 57c3485ee0SRob Herring .receive_buf = tty_port_default_receive_buf, 58c3485ee0SRob Herring .write_wakeup = tty_port_default_wakeup, 59c3485ee0SRob Herring }; 600c5aae59SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_default_client_ops); 61c3485ee0SRob Herring 623be491d7SJiri Slaby /** 633be491d7SJiri Slaby * tty_port_init -- initialize tty_port 643be491d7SJiri Slaby * @port: tty_port to initialize 653be491d7SJiri Slaby * 663be491d7SJiri Slaby * Initializes the state of struct tty_port. When a port was initialized using 673be491d7SJiri Slaby * this function, one has to destroy the port by tty_port_destroy(). Either 683be491d7SJiri Slaby * indirectly by using &tty_port refcounting (tty_port_put()) or directly if 693be491d7SJiri Slaby * refcounting is not used. 703be491d7SJiri Slaby */ 7196fd7ce5SGreg Kroah-Hartman void tty_port_init(struct tty_port *port) 7296fd7ce5SGreg Kroah-Hartman { 7396fd7ce5SGreg Kroah-Hartman memset(port, 0, sizeof(*port)); 74ecbbfd44SJiri Slaby tty_buffer_init(port); 7596fd7ce5SGreg Kroah-Hartman init_waitqueue_head(&port->open_wait); 7696fd7ce5SGreg Kroah-Hartman init_waitqueue_head(&port->delta_msr_wait); 7796fd7ce5SGreg Kroah-Hartman mutex_init(&port->mutex); 7896fd7ce5SGreg Kroah-Hartman mutex_init(&port->buf_mutex); 7996fd7ce5SGreg Kroah-Hartman spin_lock_init(&port->lock); 8096fd7ce5SGreg Kroah-Hartman port->close_delay = (50 * HZ) / 100; 8196fd7ce5SGreg Kroah-Hartman port->closing_wait = (3000 * HZ) / 100; 820c5aae59SJohan Hovold port->client_ops = &tty_port_default_client_ops; 8396fd7ce5SGreg Kroah-Hartman kref_init(&port->kref); 8496fd7ce5SGreg Kroah-Hartman } 8596fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_init); 8696fd7ce5SGreg Kroah-Hartman 8772a33bf5SJiri Slaby /** 882cb4ca02SJiri Slaby * tty_port_link_device - link tty and tty_port 892cb4ca02SJiri Slaby * @port: tty_port of the device 902cb4ca02SJiri Slaby * @driver: tty_driver for this device 912cb4ca02SJiri Slaby * @index: index of the tty 922cb4ca02SJiri Slaby * 932cb4ca02SJiri Slaby * Provide the tty layer with a link from a tty (specified by @index) to a 94cb6f6f98SJiri Slaby * tty_port (@port). Use this only if neither tty_port_register_device() nor 95cb6f6f98SJiri Slaby * tty_port_install() is used in the driver. If used, this has to be called 96cb6f6f98SJiri Slaby * before tty_register_driver(). 972cb4ca02SJiri Slaby */ 982cb4ca02SJiri Slaby void tty_port_link_device(struct tty_port *port, 992cb4ca02SJiri Slaby struct tty_driver *driver, unsigned index) 1002cb4ca02SJiri Slaby { 1012cb4ca02SJiri Slaby if (WARN_ON(index >= driver->num)) 1022cb4ca02SJiri Slaby return; 1032cb4ca02SJiri Slaby driver->ports[index] = port; 1042cb4ca02SJiri Slaby } 1052cb4ca02SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_link_device); 1062cb4ca02SJiri Slaby 1072cb4ca02SJiri Slaby /** 10872a33bf5SJiri Slaby * tty_port_register_device - register tty device 10972a33bf5SJiri Slaby * @port: tty_port of the device 11072a33bf5SJiri Slaby * @driver: tty_driver for this device 11172a33bf5SJiri Slaby * @index: index of the tty 11272a33bf5SJiri Slaby * @device: parent if exists, otherwise NULL 11372a33bf5SJiri Slaby * 114cb6f6f98SJiri Slaby * It is the same as tty_register_device() except the provided @port is linked 115cb6f6f98SJiri Slaby * to a concrete tty specified by @index. Use this or tty_port_install() (or 116cb6f6f98SJiri Slaby * both). Call tty_port_link_device() as a last resort. 11772a33bf5SJiri Slaby */ 118057eb856SJiri Slaby struct device *tty_port_register_device(struct tty_port *port, 119057eb856SJiri Slaby struct tty_driver *driver, unsigned index, 120057eb856SJiri Slaby struct device *device) 121057eb856SJiri Slaby { 1223086365dSRob Herring return tty_port_register_device_attr(port, driver, index, device, NULL, NULL); 123057eb856SJiri Slaby } 124057eb856SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_register_device); 125057eb856SJiri Slaby 126b1b79916STomas Hlavacek /** 127b1b79916STomas Hlavacek * tty_port_register_device_attr - register tty device 128b1b79916STomas Hlavacek * @port: tty_port of the device 129b1b79916STomas Hlavacek * @driver: tty_driver for this device 130b1b79916STomas Hlavacek * @index: index of the tty 131b1b79916STomas Hlavacek * @device: parent if exists, otherwise NULL 132b1b79916STomas Hlavacek * @drvdata: Driver data to be set to device. 133b1b79916STomas Hlavacek * @attr_grp: Attribute group to be set on device. 134b1b79916STomas Hlavacek * 135cb6f6f98SJiri Slaby * It is the same as tty_register_device_attr() except the provided @port is 136cb6f6f98SJiri Slaby * linked to a concrete tty specified by @index. Use this or tty_port_install() 137cb6f6f98SJiri Slaby * (or both). Call tty_port_link_device() as a last resort. 138b1b79916STomas Hlavacek */ 139b1b79916STomas Hlavacek struct device *tty_port_register_device_attr(struct tty_port *port, 140b1b79916STomas Hlavacek struct tty_driver *driver, unsigned index, 141b1b79916STomas Hlavacek struct device *device, void *drvdata, 142b1b79916STomas Hlavacek const struct attribute_group **attr_grp) 143b1b79916STomas Hlavacek { 144b1b79916STomas Hlavacek tty_port_link_device(port, driver, index); 145b1b79916STomas Hlavacek return tty_register_device_attr(driver, index, device, drvdata, 146b1b79916STomas Hlavacek attr_grp); 147b1b79916STomas Hlavacek } 148b1b79916STomas Hlavacek EXPORT_SYMBOL_GPL(tty_port_register_device_attr); 149b1b79916STomas Hlavacek 1508cde11b2SJohan Hovold /** 1518cde11b2SJohan Hovold * tty_port_register_device_attr_serdev - register tty or serdev device 1528cde11b2SJohan Hovold * @port: tty_port of the device 1538cde11b2SJohan Hovold * @driver: tty_driver for this device 1548cde11b2SJohan Hovold * @index: index of the tty 1558cde11b2SJohan Hovold * @device: parent if exists, otherwise NULL 1568cde11b2SJohan Hovold * @drvdata: driver data for the device 1578cde11b2SJohan Hovold * @attr_grp: attribute group for the device 1588cde11b2SJohan Hovold * 1598cde11b2SJohan Hovold * Register a serdev or tty device depending on if the parent device has any 1608cde11b2SJohan Hovold * defined serdev clients or not. 1618cde11b2SJohan Hovold */ 1628cde11b2SJohan Hovold struct device *tty_port_register_device_attr_serdev(struct tty_port *port, 1638cde11b2SJohan Hovold struct tty_driver *driver, unsigned index, 1648cde11b2SJohan Hovold struct device *device, void *drvdata, 1658cde11b2SJohan Hovold const struct attribute_group **attr_grp) 1668cde11b2SJohan Hovold { 1678cde11b2SJohan Hovold struct device *dev; 1688cde11b2SJohan Hovold 1698cde11b2SJohan Hovold tty_port_link_device(port, driver, index); 1708cde11b2SJohan Hovold 1718cde11b2SJohan Hovold dev = serdev_tty_port_register(port, device, driver, index); 1728cde11b2SJohan Hovold if (PTR_ERR(dev) != -ENODEV) { 1738cde11b2SJohan Hovold /* Skip creating cdev if we registered a serdev device */ 1748cde11b2SJohan Hovold return dev; 1758cde11b2SJohan Hovold } 1768cde11b2SJohan Hovold 1778cde11b2SJohan Hovold return tty_register_device_attr(driver, index, device, drvdata, 1788cde11b2SJohan Hovold attr_grp); 1798cde11b2SJohan Hovold } 1808cde11b2SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_register_device_attr_serdev); 1818cde11b2SJohan Hovold 1828cde11b2SJohan Hovold /** 1838cde11b2SJohan Hovold * tty_port_register_device_serdev - register tty or serdev device 1848cde11b2SJohan Hovold * @port: tty_port of the device 1858cde11b2SJohan Hovold * @driver: tty_driver for this device 1868cde11b2SJohan Hovold * @index: index of the tty 1878cde11b2SJohan Hovold * @device: parent if exists, otherwise NULL 1888cde11b2SJohan Hovold * 1898cde11b2SJohan Hovold * Register a serdev or tty device depending on if the parent device has any 1908cde11b2SJohan Hovold * defined serdev clients or not. 1918cde11b2SJohan Hovold */ 1928cde11b2SJohan Hovold struct device *tty_port_register_device_serdev(struct tty_port *port, 1938cde11b2SJohan Hovold struct tty_driver *driver, unsigned index, 1948cde11b2SJohan Hovold struct device *device) 1958cde11b2SJohan Hovold { 1968cde11b2SJohan Hovold return tty_port_register_device_attr_serdev(port, driver, index, 1978cde11b2SJohan Hovold device, NULL, NULL); 1988cde11b2SJohan Hovold } 1998cde11b2SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_register_device_serdev); 2008cde11b2SJohan Hovold 2018cde11b2SJohan Hovold /** 2028cde11b2SJohan Hovold * tty_port_unregister_device - deregister a 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 * 2078cde11b2SJohan Hovold * If a tty or serdev device is registered with a call to 2088cde11b2SJohan Hovold * tty_port_register_device_serdev() then this function must be called when 2098cde11b2SJohan Hovold * the device is gone. 2108cde11b2SJohan Hovold */ 2118cde11b2SJohan Hovold void tty_port_unregister_device(struct tty_port *port, 2128cde11b2SJohan Hovold struct tty_driver *driver, unsigned index) 2138cde11b2SJohan Hovold { 2148cde11b2SJohan Hovold int ret; 2158cde11b2SJohan Hovold 2168cde11b2SJohan Hovold ret = serdev_tty_port_unregister(port); 2178cde11b2SJohan Hovold if (ret == 0) 2188cde11b2SJohan Hovold return; 2198cde11b2SJohan Hovold 2208cde11b2SJohan Hovold tty_unregister_device(driver, index); 2218cde11b2SJohan Hovold } 2228cde11b2SJohan Hovold EXPORT_SYMBOL_GPL(tty_port_unregister_device); 2238cde11b2SJohan Hovold 22496fd7ce5SGreg Kroah-Hartman int tty_port_alloc_xmit_buf(struct tty_port *port) 22596fd7ce5SGreg Kroah-Hartman { 22696fd7ce5SGreg Kroah-Hartman /* We may sleep in get_zeroed_page() */ 22796fd7ce5SGreg Kroah-Hartman mutex_lock(&port->buf_mutex); 228*4e2a44c1SJiri Slaby if (port->xmit_buf == NULL) { 22996fd7ce5SGreg Kroah-Hartman port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); 230*4e2a44c1SJiri Slaby if (port->xmit_buf) 231*4e2a44c1SJiri Slaby kfifo_init(&port->xmit_fifo, port->xmit_buf, PAGE_SIZE); 232*4e2a44c1SJiri Slaby } 23396fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->buf_mutex); 23496fd7ce5SGreg Kroah-Hartman if (port->xmit_buf == NULL) 23596fd7ce5SGreg Kroah-Hartman return -ENOMEM; 23696fd7ce5SGreg Kroah-Hartman return 0; 23796fd7ce5SGreg Kroah-Hartman } 23896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_alloc_xmit_buf); 23996fd7ce5SGreg Kroah-Hartman 24096fd7ce5SGreg Kroah-Hartman void tty_port_free_xmit_buf(struct tty_port *port) 24196fd7ce5SGreg Kroah-Hartman { 24296fd7ce5SGreg Kroah-Hartman mutex_lock(&port->buf_mutex); 24396fd7ce5SGreg Kroah-Hartman if (port->xmit_buf != NULL) { 24496fd7ce5SGreg Kroah-Hartman free_page((unsigned long)port->xmit_buf); 24596fd7ce5SGreg Kroah-Hartman port->xmit_buf = NULL; 246*4e2a44c1SJiri Slaby INIT_KFIFO(port->xmit_fifo); 24796fd7ce5SGreg Kroah-Hartman } 24896fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->buf_mutex); 24996fd7ce5SGreg Kroah-Hartman } 25096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_free_xmit_buf); 25196fd7ce5SGreg Kroah-Hartman 252de274bfeSJiri Slaby /** 253de274bfeSJiri Slaby * tty_port_destroy -- destroy inited port 2541926e5d3SAntonio Borneo * @port: tty port to be destroyed 255de274bfeSJiri Slaby * 256cb6f6f98SJiri Slaby * When a port was initialized using tty_port_init(), one has to destroy the 257cb6f6f98SJiri Slaby * port by this function. Either indirectly by using &tty_port refcounting 258cb6f6f98SJiri Slaby * (tty_port_put()) or directly if refcounting is not used. 259de274bfeSJiri Slaby */ 260de274bfeSJiri Slaby void tty_port_destroy(struct tty_port *port) 261de274bfeSJiri Slaby { 262e176058fSPeter Hurley tty_buffer_cancel_work(port); 263de274bfeSJiri Slaby tty_buffer_free_all(port); 264de274bfeSJiri Slaby } 265de274bfeSJiri Slaby EXPORT_SYMBOL(tty_port_destroy); 266de274bfeSJiri Slaby 26796fd7ce5SGreg Kroah-Hartman static void tty_port_destructor(struct kref *kref) 26896fd7ce5SGreg Kroah-Hartman { 26996fd7ce5SGreg Kroah-Hartman struct tty_port *port = container_of(kref, struct tty_port, kref); 270e3bfea23SPeter Hurley 271e3bfea23SPeter Hurley /* check if last port ref was dropped before tty release */ 272e3bfea23SPeter Hurley if (WARN_ON(port->itty)) 273e3bfea23SPeter Hurley return; 27496fd7ce5SGreg Kroah-Hartman if (port->xmit_buf) 27596fd7ce5SGreg Kroah-Hartman free_page((unsigned long)port->xmit_buf); 276de274bfeSJiri Slaby tty_port_destroy(port); 27781c79838SJiri Slaby if (port->ops && port->ops->destruct) 27896fd7ce5SGreg Kroah-Hartman port->ops->destruct(port); 27996fd7ce5SGreg Kroah-Hartman else 28096fd7ce5SGreg Kroah-Hartman kfree(port); 28196fd7ce5SGreg Kroah-Hartman } 28296fd7ce5SGreg Kroah-Hartman 2833be491d7SJiri Slaby /** 2843be491d7SJiri Slaby * tty_port_put -- drop a reference to tty_port 2853be491d7SJiri Slaby * @port: port to drop a reference of (can be NULL) 2863be491d7SJiri Slaby * 2873be491d7SJiri Slaby * The final put will destroy and free up the @port using 2883be491d7SJiri Slaby * @port->ops->destruct() hook, or using kfree() if not provided. 2893be491d7SJiri Slaby */ 29096fd7ce5SGreg Kroah-Hartman void tty_port_put(struct tty_port *port) 29196fd7ce5SGreg Kroah-Hartman { 29296fd7ce5SGreg Kroah-Hartman if (port) 29396fd7ce5SGreg Kroah-Hartman kref_put(&port->kref, tty_port_destructor); 29496fd7ce5SGreg Kroah-Hartman } 29596fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_put); 29696fd7ce5SGreg Kroah-Hartman 29796fd7ce5SGreg Kroah-Hartman /** 29896fd7ce5SGreg Kroah-Hartman * tty_port_tty_get - get a tty reference 29996fd7ce5SGreg Kroah-Hartman * @port: tty port 30096fd7ce5SGreg Kroah-Hartman * 301cb6f6f98SJiri Slaby * Return a refcount protected tty instance or %NULL if the port is not 302cb6f6f98SJiri Slaby * associated with a tty (eg due to close or hangup). 30396fd7ce5SGreg Kroah-Hartman */ 30496fd7ce5SGreg Kroah-Hartman struct tty_struct *tty_port_tty_get(struct tty_port *port) 30596fd7ce5SGreg Kroah-Hartman { 30696fd7ce5SGreg Kroah-Hartman unsigned long flags; 30796fd7ce5SGreg Kroah-Hartman struct tty_struct *tty; 30896fd7ce5SGreg Kroah-Hartman 30996fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 31096fd7ce5SGreg Kroah-Hartman tty = tty_kref_get(port->tty); 31196fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 31296fd7ce5SGreg Kroah-Hartman return tty; 31396fd7ce5SGreg Kroah-Hartman } 31496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_tty_get); 31596fd7ce5SGreg Kroah-Hartman 31696fd7ce5SGreg Kroah-Hartman /** 31796fd7ce5SGreg Kroah-Hartman * tty_port_tty_set - set the tty of a port 31896fd7ce5SGreg Kroah-Hartman * @port: tty port 31996fd7ce5SGreg Kroah-Hartman * @tty: the tty 32096fd7ce5SGreg Kroah-Hartman * 321cb6f6f98SJiri Slaby * Associate the port and tty pair. Manages any internal refcounts. Pass %NULL 322cb6f6f98SJiri Slaby * to deassociate a port. 32396fd7ce5SGreg Kroah-Hartman */ 32496fd7ce5SGreg Kroah-Hartman void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) 32596fd7ce5SGreg Kroah-Hartman { 32696fd7ce5SGreg Kroah-Hartman unsigned long flags; 32796fd7ce5SGreg Kroah-Hartman 32896fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 32996fd7ce5SGreg Kroah-Hartman tty_kref_put(port->tty); 33096fd7ce5SGreg Kroah-Hartman port->tty = tty_kref_get(tty); 33196fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 33296fd7ce5SGreg Kroah-Hartman } 33396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_tty_set); 33496fd7ce5SGreg Kroah-Hartman 3353be491d7SJiri Slaby /** 3363be491d7SJiri Slaby * tty_port_shutdown - internal helper to shutdown the device 3373be491d7SJiri Slaby * @port: tty port to be shut down 3383be491d7SJiri Slaby * @tty: the associated tty 3393be491d7SJiri Slaby * 3403be491d7SJiri Slaby * It is used by tty_port_hangup() and tty_port_close(). Its task is to 3413be491d7SJiri Slaby * shutdown the device if it was initialized (note consoles remain 3423be491d7SJiri Slaby * functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes 3433be491d7SJiri Slaby * @port->ops->shutdown(). 3443be491d7SJiri Slaby */ 345957dacaeSJohan Hovold static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) 34696fd7ce5SGreg Kroah-Hartman { 34796fd7ce5SGreg Kroah-Hartman mutex_lock(&port->mutex); 3488bde9658SJohan Hovold if (port->console) 3498bde9658SJohan Hovold goto out; 3508bde9658SJohan Hovold 351d41861caSPeter Hurley if (tty_port_initialized(port)) { 352d41861caSPeter Hurley tty_port_set_initialized(port, 0); 353957dacaeSJohan Hovold /* 354957dacaeSJohan Hovold * Drop DTR/RTS if HUPCL is set. This causes any attached 355957dacaeSJohan Hovold * modem to hang up the line. 356957dacaeSJohan Hovold */ 357957dacaeSJohan Hovold if (tty && C_HUPCL(tty)) 358957dacaeSJohan Hovold tty_port_lower_dtr_rts(port); 359957dacaeSJohan Hovold 3600d3cb6f6SJohan Hovold if (port->ops->shutdown) 36196fd7ce5SGreg Kroah-Hartman port->ops->shutdown(port); 3628bde9658SJohan Hovold } 3638bde9658SJohan Hovold out: 36496fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 36596fd7ce5SGreg Kroah-Hartman } 36696fd7ce5SGreg Kroah-Hartman 36796fd7ce5SGreg Kroah-Hartman /** 36896fd7ce5SGreg Kroah-Hartman * tty_port_hangup - hangup helper 36996fd7ce5SGreg Kroah-Hartman * @port: tty port 37096fd7ce5SGreg Kroah-Hartman * 37196fd7ce5SGreg Kroah-Hartman * Perform port level tty hangup flag and count changes. Drop the tty 37296fd7ce5SGreg Kroah-Hartman * reference. 3739c9928bdSPeter Hurley * 3749c9928bdSPeter Hurley * Caller holds tty lock. 37596fd7ce5SGreg Kroah-Hartman */ 37696fd7ce5SGreg Kroah-Hartman void tty_port_hangup(struct tty_port *port) 37796fd7ce5SGreg Kroah-Hartman { 378957dacaeSJohan Hovold struct tty_struct *tty; 37996fd7ce5SGreg Kroah-Hartman unsigned long flags; 38096fd7ce5SGreg Kroah-Hartman 38196fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 38296fd7ce5SGreg Kroah-Hartman port->count = 0; 383957dacaeSJohan Hovold tty = port->tty; 384957dacaeSJohan Hovold if (tty) 385957dacaeSJohan Hovold set_bit(TTY_IO_ERROR, &tty->flags); 38696fd7ce5SGreg Kroah-Hartman port->tty = NULL; 38796fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 388807c8d81SPeter Hurley tty_port_set_active(port, 0); 389957dacaeSJohan Hovold tty_port_shutdown(port, tty); 390957dacaeSJohan Hovold tty_kref_put(tty); 39196fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->open_wait); 39296fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->delta_msr_wait); 39396fd7ce5SGreg Kroah-Hartman } 39496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_hangup); 39596fd7ce5SGreg Kroah-Hartman 39696fd7ce5SGreg Kroah-Hartman /** 397aa27a094SJiri Slaby * tty_port_tty_hangup - helper to hang up a tty 398aa27a094SJiri Slaby * @port: tty port 399cb6f6f98SJiri Slaby * @check_clocal: hang only ttys with %CLOCAL unset? 400aa27a094SJiri Slaby */ 401aa27a094SJiri Slaby void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) 402aa27a094SJiri Slaby { 403aa27a094SJiri Slaby struct tty_struct *tty = tty_port_tty_get(port); 404aa27a094SJiri Slaby 4051d9e689cSGianluca Anzolin if (tty && (!check_clocal || !C_CLOCAL(tty))) 406aa27a094SJiri Slaby tty_hangup(tty); 407aa27a094SJiri Slaby tty_kref_put(tty); 408aa27a094SJiri Slaby } 409aa27a094SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_tty_hangup); 410aa27a094SJiri Slaby 411aa27a094SJiri Slaby /** 4126aad04f2SJiri Slaby * tty_port_tty_wakeup - helper to wake up a tty 4136aad04f2SJiri Slaby * @port: tty port 4146aad04f2SJiri Slaby */ 4156aad04f2SJiri Slaby void tty_port_tty_wakeup(struct tty_port *port) 4166aad04f2SJiri Slaby { 417c3485ee0SRob Herring port->client_ops->write_wakeup(port); 4186aad04f2SJiri Slaby } 4196aad04f2SJiri Slaby EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); 4206aad04f2SJiri Slaby 4216aad04f2SJiri Slaby /** 42296fd7ce5SGreg Kroah-Hartman * tty_port_carrier_raised - carrier raised check 42396fd7ce5SGreg Kroah-Hartman * @port: tty port 42496fd7ce5SGreg Kroah-Hartman * 42596fd7ce5SGreg Kroah-Hartman * Wrapper for the carrier detect logic. For the moment this is used 42696fd7ce5SGreg Kroah-Hartman * to hide some internal details. This will eventually become entirely 42796fd7ce5SGreg Kroah-Hartman * internal to the tty port. 42896fd7ce5SGreg Kroah-Hartman */ 42996fd7ce5SGreg Kroah-Hartman int tty_port_carrier_raised(struct tty_port *port) 43096fd7ce5SGreg Kroah-Hartman { 4310d3cb6f6SJohan Hovold if (port->ops->carrier_raised == NULL) 43296fd7ce5SGreg Kroah-Hartman return 1; 43396fd7ce5SGreg Kroah-Hartman return port->ops->carrier_raised(port); 43496fd7ce5SGreg Kroah-Hartman } 43596fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_carrier_raised); 43696fd7ce5SGreg Kroah-Hartman 43796fd7ce5SGreg Kroah-Hartman /** 43896fd7ce5SGreg Kroah-Hartman * tty_port_raise_dtr_rts - Raise DTR/RTS 43996fd7ce5SGreg Kroah-Hartman * @port: tty port 44096fd7ce5SGreg Kroah-Hartman * 441cb6f6f98SJiri Slaby * Wrapper for the DTR/RTS raise logic. For the moment this is used to hide 442cb6f6f98SJiri Slaby * some internal details. This will eventually become entirely internal to the 443cb6f6f98SJiri Slaby * tty port. 44496fd7ce5SGreg Kroah-Hartman */ 44596fd7ce5SGreg Kroah-Hartman void tty_port_raise_dtr_rts(struct tty_port *port) 44696fd7ce5SGreg Kroah-Hartman { 4470d3cb6f6SJohan Hovold if (port->ops->dtr_rts) 44896fd7ce5SGreg Kroah-Hartman port->ops->dtr_rts(port, 1); 44996fd7ce5SGreg Kroah-Hartman } 45096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_raise_dtr_rts); 45196fd7ce5SGreg Kroah-Hartman 45296fd7ce5SGreg Kroah-Hartman /** 45396fd7ce5SGreg Kroah-Hartman * tty_port_lower_dtr_rts - Lower DTR/RTS 45496fd7ce5SGreg Kroah-Hartman * @port: tty port 45596fd7ce5SGreg Kroah-Hartman * 456cb6f6f98SJiri Slaby * Wrapper for the DTR/RTS raise logic. For the moment this is used to hide 457cb6f6f98SJiri Slaby * some internal details. This will eventually become entirely internal to the 458cb6f6f98SJiri Slaby * tty port. 45996fd7ce5SGreg Kroah-Hartman */ 46096fd7ce5SGreg Kroah-Hartman void tty_port_lower_dtr_rts(struct tty_port *port) 46196fd7ce5SGreg Kroah-Hartman { 4620d3cb6f6SJohan Hovold if (port->ops->dtr_rts) 46396fd7ce5SGreg Kroah-Hartman port->ops->dtr_rts(port, 0); 46496fd7ce5SGreg Kroah-Hartman } 46596fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_lower_dtr_rts); 46696fd7ce5SGreg Kroah-Hartman 46796fd7ce5SGreg Kroah-Hartman /** 46896fd7ce5SGreg Kroah-Hartman * tty_port_block_til_ready - Waiting logic for tty open 46996fd7ce5SGreg Kroah-Hartman * @port: the tty port being opened 47096fd7ce5SGreg Kroah-Hartman * @tty: the tty device being bound 471cb6f6f98SJiri Slaby * @filp: the file pointer of the opener or %NULL 47296fd7ce5SGreg Kroah-Hartman * 47396fd7ce5SGreg Kroah-Hartman * Implement the core POSIX/SuS tty behaviour when opening a tty device. 47496fd7ce5SGreg Kroah-Hartman * Handles: 475cb6f6f98SJiri Slaby * 47696fd7ce5SGreg Kroah-Hartman * - hangup (both before and during) 47796fd7ce5SGreg Kroah-Hartman * - non blocking open 47896fd7ce5SGreg Kroah-Hartman * - rts/dtr/dcd 47996fd7ce5SGreg Kroah-Hartman * - signals 48096fd7ce5SGreg Kroah-Hartman * - port flags and counts 48196fd7ce5SGreg Kroah-Hartman * 482cb6f6f98SJiri Slaby * The passed @port must implement the @port->ops->carrier_raised method if it 483cb6f6f98SJiri Slaby * can do carrier detect and the @port->ops->dtr_rts method if it supports 484cb6f6f98SJiri Slaby * software management of these lines. Note that the dtr/rts raise is done each 48596fd7ce5SGreg Kroah-Hartman * iteration as a hangup may have previously dropped them while we wait. 486c590f6b6SPeter Hurley * 487c590f6b6SPeter Hurley * Caller holds tty lock. 488c590f6b6SPeter Hurley * 489cb6f6f98SJiri Slaby * Note: May drop and reacquire tty lock when blocking, so @tty and @port may 490cb6f6f98SJiri Slaby * have changed state (eg., may have been hung up). 49196fd7ce5SGreg Kroah-Hartman */ 49296fd7ce5SGreg Kroah-Hartman int tty_port_block_til_ready(struct tty_port *port, 49396fd7ce5SGreg Kroah-Hartman struct tty_struct *tty, struct file *filp) 49496fd7ce5SGreg Kroah-Hartman { 49596fd7ce5SGreg Kroah-Hartman int do_clocal = 0, retval; 49696fd7ce5SGreg Kroah-Hartman unsigned long flags; 49796fd7ce5SGreg Kroah-Hartman DEFINE_WAIT(wait); 49896fd7ce5SGreg Kroah-Hartman 49996fd7ce5SGreg Kroah-Hartman /* if non-blocking mode is set we can pass directly to open unless 5001df92640SXiaofei Tan * the port has just hung up or is in another error state. 5011df92640SXiaofei Tan */ 50218900ca6SPeter Hurley if (tty_io_error(tty)) { 503807c8d81SPeter Hurley tty_port_set_active(port, 1); 50496fd7ce5SGreg Kroah-Hartman return 0; 50596fd7ce5SGreg Kroah-Hartman } 506ed3f0af8SAlan Cox if (filp == NULL || (filp->f_flags & O_NONBLOCK)) { 50796fd7ce5SGreg Kroah-Hartman /* Indicate we are open */ 5089db276f8SPeter Hurley if (C_BAUD(tty)) 50996fd7ce5SGreg Kroah-Hartman tty_port_raise_dtr_rts(port); 510807c8d81SPeter Hurley tty_port_set_active(port, 1); 51196fd7ce5SGreg Kroah-Hartman return 0; 51296fd7ce5SGreg Kroah-Hartman } 51396fd7ce5SGreg Kroah-Hartman 51496fd7ce5SGreg Kroah-Hartman if (C_CLOCAL(tty)) 51596fd7ce5SGreg Kroah-Hartman do_clocal = 1; 51696fd7ce5SGreg Kroah-Hartman 51796fd7ce5SGreg Kroah-Hartman /* Block waiting until we can proceed. We may need to wait for the 5181df92640SXiaofei Tan * carrier, but we must also wait for any close that is in progress 5191df92640SXiaofei Tan * before the next open may complete. 5201df92640SXiaofei Tan */ 52196fd7ce5SGreg Kroah-Hartman 52296fd7ce5SGreg Kroah-Hartman retval = 0; 52396fd7ce5SGreg Kroah-Hartman 52496fd7ce5SGreg Kroah-Hartman /* The port lock protects the port counts */ 52596fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 52696fd7ce5SGreg Kroah-Hartman port->count--; 52796fd7ce5SGreg Kroah-Hartman port->blocked_open++; 52896fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 52996fd7ce5SGreg Kroah-Hartman 53096fd7ce5SGreg Kroah-Hartman while (1) { 53196fd7ce5SGreg Kroah-Hartman /* Indicate we are open */ 532d41861caSPeter Hurley if (C_BAUD(tty) && tty_port_initialized(port)) 53396fd7ce5SGreg Kroah-Hartman tty_port_raise_dtr_rts(port); 53496fd7ce5SGreg Kroah-Hartman 53596fd7ce5SGreg Kroah-Hartman prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); 53696fd7ce5SGreg Kroah-Hartman /* Check for a hangup or uninitialised port. 5371df92640SXiaofei Tan * Return accordingly. 5381df92640SXiaofei Tan */ 539d41861caSPeter Hurley if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { 54096fd7ce5SGreg Kroah-Hartman if (port->flags & ASYNC_HUP_NOTIFY) 54196fd7ce5SGreg Kroah-Hartman retval = -EAGAIN; 54296fd7ce5SGreg Kroah-Hartman else 54396fd7ce5SGreg Kroah-Hartman retval = -ERESTARTSYS; 54496fd7ce5SGreg Kroah-Hartman break; 54596fd7ce5SGreg Kroah-Hartman } 5460eee50afSJiri Slaby /* 5470eee50afSJiri Slaby * Probe the carrier. For devices with no carrier detect 5480eee50afSJiri Slaby * tty_port_carrier_raised will always return true. 5490eee50afSJiri Slaby * Never ask drivers if CLOCAL is set, this causes troubles 5500eee50afSJiri Slaby * on some hardware. 5510eee50afSJiri Slaby */ 552fef062cbSPeter Hurley if (do_clocal || tty_port_carrier_raised(port)) 55396fd7ce5SGreg Kroah-Hartman break; 55496fd7ce5SGreg Kroah-Hartman if (signal_pending(current)) { 55596fd7ce5SGreg Kroah-Hartman retval = -ERESTARTSYS; 55696fd7ce5SGreg Kroah-Hartman break; 55796fd7ce5SGreg Kroah-Hartman } 55889c8d91eSAlan Cox tty_unlock(tty); 55996fd7ce5SGreg Kroah-Hartman schedule(); 56089c8d91eSAlan Cox tty_lock(tty); 56196fd7ce5SGreg Kroah-Hartman } 56296fd7ce5SGreg Kroah-Hartman finish_wait(&port->open_wait, &wait); 56396fd7ce5SGreg Kroah-Hartman 56496fd7ce5SGreg Kroah-Hartman /* Update counts. A parallel hangup will have set count to zero and 5651df92640SXiaofei Tan * we must not mess that up further. 5661df92640SXiaofei Tan */ 56796fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 56896fd7ce5SGreg Kroah-Hartman if (!tty_hung_up_p(filp)) 56996fd7ce5SGreg Kroah-Hartman port->count++; 57096fd7ce5SGreg Kroah-Hartman port->blocked_open--; 57196fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 572807c8d81SPeter Hurley if (retval == 0) 573807c8d81SPeter Hurley tty_port_set_active(port, 1); 57496fd7ce5SGreg Kroah-Hartman return retval; 57596fd7ce5SGreg Kroah-Hartman } 57696fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_block_til_ready); 57796fd7ce5SGreg Kroah-Hartman 578b74414f5SJohan Hovold static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) 579b74414f5SJohan Hovold { 580b74414f5SJohan Hovold unsigned int bps = tty_get_baud_rate(tty); 581b74414f5SJohan Hovold long timeout; 582b74414f5SJohan Hovold 583b74414f5SJohan Hovold if (bps > 1200) { 584b74414f5SJohan Hovold timeout = (HZ * 10 * port->drain_delay) / bps; 585b74414f5SJohan Hovold timeout = max_t(long, timeout, HZ / 10); 586b74414f5SJohan Hovold } else { 587b74414f5SJohan Hovold timeout = 2 * HZ; 588b74414f5SJohan Hovold } 589b74414f5SJohan Hovold schedule_timeout_interruptible(timeout); 590b74414f5SJohan Hovold } 591b74414f5SJohan Hovold 5923be491d7SJiri Slaby /** 5933be491d7SJiri Slaby * tty_port_close_start - helper for tty->ops->close, part 1/2 5943be491d7SJiri Slaby * @port: tty_port of the device 5953be491d7SJiri Slaby * @tty: tty being closed 5963be491d7SJiri Slaby * @filp: passed file pointer 5973be491d7SJiri Slaby * 5983be491d7SJiri Slaby * Decrements and checks open count. Flushes the port if this is the last 5993be491d7SJiri Slaby * close. That means, dropping the data from the outpu buffer on the device and 6003be491d7SJiri Slaby * waiting for sending logic to finish. The rest of close handling is performed 6013be491d7SJiri Slaby * in tty_port_close_end(). 6023be491d7SJiri Slaby * 6033be491d7SJiri Slaby * Locking: Caller holds tty lock. 6043be491d7SJiri Slaby * 6053be491d7SJiri Slaby * Return: 1 if this is the last close, otherwise 0 6063be491d7SJiri Slaby */ 60796fd7ce5SGreg Kroah-Hartman int tty_port_close_start(struct tty_port *port, 60896fd7ce5SGreg Kroah-Hartman struct tty_struct *tty, struct file *filp) 60996fd7ce5SGreg Kroah-Hartman { 61096fd7ce5SGreg Kroah-Hartman unsigned long flags; 61196fd7ce5SGreg Kroah-Hartman 612633caba8SPeter Hurley if (tty_hung_up_p(filp)) 61396fd7ce5SGreg Kroah-Hartman return 0; 61496fd7ce5SGreg Kroah-Hartman 615633caba8SPeter Hurley spin_lock_irqsave(&port->lock, flags); 61696fd7ce5SGreg Kroah-Hartman if (tty->count == 1 && port->count != 1) { 617339f36baSPeter Hurley tty_warn(tty, "%s: tty->count = 1 port count = %d\n", __func__, 61896fd7ce5SGreg Kroah-Hartman port->count); 61996fd7ce5SGreg Kroah-Hartman port->count = 1; 62096fd7ce5SGreg Kroah-Hartman } 62196fd7ce5SGreg Kroah-Hartman if (--port->count < 0) { 622339f36baSPeter Hurley tty_warn(tty, "%s: bad port count (%d)\n", __func__, 62396fd7ce5SGreg Kroah-Hartman port->count); 62496fd7ce5SGreg Kroah-Hartman port->count = 0; 62596fd7ce5SGreg Kroah-Hartman } 62696fd7ce5SGreg Kroah-Hartman 62796fd7ce5SGreg Kroah-Hartman if (port->count) { 62896fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 62996fd7ce5SGreg Kroah-Hartman return 0; 63096fd7ce5SGreg Kroah-Hartman } 63196fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 6320b2588caSJohan Hovold 633ddc7b758SPeter Hurley tty->closing = 1; 634ddc7b758SPeter Hurley 635d41861caSPeter Hurley if (tty_port_initialized(port)) { 63696fd7ce5SGreg Kroah-Hartman /* Don't block on a stalled port, just pull the chain */ 6376e94dbc7SJiri Slaby if (tty->flow.tco_stopped) 63896fd7ce5SGreg Kroah-Hartman tty_driver_flush_buffer(tty); 6390b2588caSJohan Hovold if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) 64079c1faa4SPeter Hurley tty_wait_until_sent(tty, port->closing_wait); 641b74414f5SJohan Hovold if (port->drain_delay) 642b74414f5SJohan Hovold tty_port_drain_delay(port, tty); 6430b2588caSJohan Hovold } 64496fd7ce5SGreg Kroah-Hartman /* Flush the ldisc buffering */ 64596fd7ce5SGreg Kroah-Hartman tty_ldisc_flush(tty); 64696fd7ce5SGreg Kroah-Hartman 647469d6d06SPeter Hurley /* Report to caller this is the last port reference */ 64896fd7ce5SGreg Kroah-Hartman return 1; 64996fd7ce5SGreg Kroah-Hartman } 65096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close_start); 65196fd7ce5SGreg Kroah-Hartman 6523be491d7SJiri Slaby /** 6533be491d7SJiri Slaby * tty_port_close_end - helper for tty->ops->close, part 2/2 6543be491d7SJiri Slaby * @port: tty_port of the device 6553be491d7SJiri Slaby * @tty: tty being closed 6563be491d7SJiri Slaby * 6573be491d7SJiri Slaby * This is a continuation of the first part: tty_port_close_start(). This 6583be491d7SJiri Slaby * should be called after turning off the device. It flushes the data from the 6593be491d7SJiri Slaby * line discipline and delays the close by @port->close_delay. 6603be491d7SJiri Slaby * 6613be491d7SJiri Slaby * Locking: Caller holds tty lock. 6623be491d7SJiri Slaby */ 66396fd7ce5SGreg Kroah-Hartman void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) 66496fd7ce5SGreg Kroah-Hartman { 66596fd7ce5SGreg Kroah-Hartman unsigned long flags; 66696fd7ce5SGreg Kroah-Hartman 6673f40f5b2SPeter Hurley tty_ldisc_flush(tty); 66896fd7ce5SGreg Kroah-Hartman tty->closing = 0; 66996fd7ce5SGreg Kroah-Hartman 670ddc7b758SPeter Hurley spin_lock_irqsave(&port->lock, flags); 671ddc7b758SPeter Hurley 67296fd7ce5SGreg Kroah-Hartman if (port->blocked_open) { 67396fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 6745823323eSPeter Hurley if (port->close_delay) 6755823323eSPeter Hurley msleep_interruptible(jiffies_to_msecs(port->close_delay)); 67696fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags); 67796fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&port->open_wait); 67896fd7ce5SGreg Kroah-Hartman } 67996fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags); 680807c8d81SPeter Hurley tty_port_set_active(port, 0); 68196fd7ce5SGreg Kroah-Hartman } 68296fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close_end); 68396fd7ce5SGreg Kroah-Hartman 684cb6f6f98SJiri Slaby /** 685cb6f6f98SJiri Slaby * tty_port_close - generic tty->ops->close handler 686cb6f6f98SJiri Slaby * @port: tty_port of the device 687cb6f6f98SJiri Slaby * @tty: tty being closed 688cb6f6f98SJiri Slaby * @filp: passed file pointer 6890733db91SPeter Hurley * 690cb6f6f98SJiri Slaby * It is a generic helper to be used in driver's @tty->ops->close. It wraps a 691cb6f6f98SJiri Slaby * sequence of tty_port_close_start(), tty_port_shutdown(), and 692cb6f6f98SJiri Slaby * tty_port_close_end(). The latter two are called only if this is the last 693cb6f6f98SJiri Slaby * close. See the respective functions for the details. 694cb6f6f98SJiri Slaby * 695cb6f6f98SJiri Slaby * Locking: Caller holds tty lock 6960733db91SPeter Hurley */ 69796fd7ce5SGreg Kroah-Hartman void tty_port_close(struct tty_port *port, struct tty_struct *tty, 69896fd7ce5SGreg Kroah-Hartman struct file *filp) 69996fd7ce5SGreg Kroah-Hartman { 70096fd7ce5SGreg Kroah-Hartman if (tty_port_close_start(port, tty, filp) == 0) 70196fd7ce5SGreg Kroah-Hartman return; 702957dacaeSJohan Hovold tty_port_shutdown(port, tty); 7032a486026SChanho Park if (!port->console) 70496fd7ce5SGreg Kroah-Hartman set_bit(TTY_IO_ERROR, &tty->flags); 70596fd7ce5SGreg Kroah-Hartman tty_port_close_end(port, tty); 70696fd7ce5SGreg Kroah-Hartman tty_port_tty_set(port, NULL); 70796fd7ce5SGreg Kroah-Hartman } 70896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_close); 70996fd7ce5SGreg Kroah-Hartman 71072a33bf5SJiri Slaby /** 71172a33bf5SJiri Slaby * tty_port_install - generic tty->ops->install handler 71272a33bf5SJiri Slaby * @port: tty_port of the device 71372a33bf5SJiri Slaby * @driver: tty_driver for this device 71472a33bf5SJiri Slaby * @tty: tty to be installed 71572a33bf5SJiri Slaby * 716cb6f6f98SJiri Slaby * It is the same as tty_standard_install() except the provided @port is linked 717cb6f6f98SJiri Slaby * to a concrete tty specified by @tty. Use this or tty_port_register_device() 718cb6f6f98SJiri Slaby * (or both). Call tty_port_link_device() as a last resort. 71972a33bf5SJiri Slaby */ 720695586caSJiri Slaby int tty_port_install(struct tty_port *port, struct tty_driver *driver, 721695586caSJiri Slaby struct tty_struct *tty) 722695586caSJiri Slaby { 723695586caSJiri Slaby tty->port = port; 724695586caSJiri Slaby return tty_standard_install(driver, tty); 725695586caSJiri Slaby } 726695586caSJiri Slaby EXPORT_SYMBOL_GPL(tty_port_install); 727695586caSJiri Slaby 728cb6f6f98SJiri Slaby /** 729cb6f6f98SJiri Slaby * tty_port_open - generic tty->ops->open handler 730cb6f6f98SJiri Slaby * @port: tty_port of the device 731cb6f6f98SJiri Slaby * @tty: tty to be opened 732cb6f6f98SJiri Slaby * @filp: passed file pointer 733addd4672SPeter Hurley * 734cb6f6f98SJiri Slaby * It is a generic helper to be used in driver's @tty->ops->open. It activates 735cb6f6f98SJiri Slaby * the devices using @port->ops->activate if not active already. And waits for 736cb6f6f98SJiri Slaby * the device to be ready using tty_port_block_til_ready() (e.g. raises 737cb6f6f98SJiri Slaby * DTR/CTS and waits for carrier). 738addd4672SPeter Hurley * 739cb6f6f98SJiri Slaby * Locking: Caller holds tty lock. 740cb6f6f98SJiri Slaby * 741cb6f6f98SJiri Slaby * Note: may drop and reacquire tty lock (in tty_port_block_til_ready()) so 742cb6f6f98SJiri Slaby * @tty and @port may have changed state (eg., may be hung up now). 743addd4672SPeter Hurley */ 74496fd7ce5SGreg Kroah-Hartman int tty_port_open(struct tty_port *port, struct tty_struct *tty, 74596fd7ce5SGreg Kroah-Hartman struct file *filp) 74696fd7ce5SGreg Kroah-Hartman { 74796fd7ce5SGreg Kroah-Hartman spin_lock_irq(&port->lock); 74896fd7ce5SGreg Kroah-Hartman ++port->count; 74996fd7ce5SGreg Kroah-Hartman spin_unlock_irq(&port->lock); 75096fd7ce5SGreg Kroah-Hartman tty_port_tty_set(port, tty); 75196fd7ce5SGreg Kroah-Hartman 75296fd7ce5SGreg Kroah-Hartman /* 75396fd7ce5SGreg Kroah-Hartman * Do the device-specific open only if the hardware isn't 75496fd7ce5SGreg Kroah-Hartman * already initialized. Serialize open and shutdown using the 75596fd7ce5SGreg Kroah-Hartman * port mutex. 75696fd7ce5SGreg Kroah-Hartman */ 75796fd7ce5SGreg Kroah-Hartman 75896fd7ce5SGreg Kroah-Hartman mutex_lock(&port->mutex); 75996fd7ce5SGreg Kroah-Hartman 760d41861caSPeter Hurley if (!tty_port_initialized(port)) { 76196fd7ce5SGreg Kroah-Hartman clear_bit(TTY_IO_ERROR, &tty->flags); 7620d3cb6f6SJohan Hovold if (port->ops->activate) { 76396fd7ce5SGreg Kroah-Hartman int retval = port->ops->activate(port, tty); 76454ad59a2SXiaofei Tan 76596fd7ce5SGreg Kroah-Hartman if (retval) { 76696fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 76796fd7ce5SGreg Kroah-Hartman return retval; 76896fd7ce5SGreg Kroah-Hartman } 76996fd7ce5SGreg Kroah-Hartman } 770d41861caSPeter Hurley tty_port_set_initialized(port, 1); 77196fd7ce5SGreg Kroah-Hartman } 77296fd7ce5SGreg Kroah-Hartman mutex_unlock(&port->mutex); 77396fd7ce5SGreg Kroah-Hartman return tty_port_block_til_ready(port, tty, filp); 77496fd7ce5SGreg Kroah-Hartman } 77596fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_port_open); 776