Merge tag 'tty-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 May 2017 01:49:23 +0000 (18:49 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 May 2017 01:49:23 +0000 (18:49 -0700)
Pull tty/serial updates from Greg KH:
 "Here is the "big" TTY/Serial patch updates for 4.12-rc1

  Not a lot of new things here, the normal number of serial driver
  updates and additions, tiny bugs fixed, and some core files split up
  to make future changes a bit easier for Nicolas's "tiny-tty" work.

  All of these have been in linux-next for a while"

* tag 'tty-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (62 commits)
  serial: small Makefile reordering
  tty: split job control support into a file of its own
  tty: move baudrate handling code to a file of its own
  console: move console_init() out of tty_io.c
  serial: 8250_early: Add earlycon support for Palmchip UART
  tty: pl011: use "qdf2400_e44" as the earlycon name for QDF2400 E44
  vt: make mouse selection of non-ASCII consistent
  vt: set mouse selection word-chars to gpm's default
  imx-serial: Reduce RX DMA startup latency when opening for reading
  serial: omap: suspend device on probe errors
  serial: omap: fix runtime-pm handling on unbind
  tty: serial: omap: add UPF_BOOT_AUTOCONF flag for DT init
  serial: samsung: Remove useless spinlock
  serial: samsung: Add missing checks for dma_map_single failure
  serial: samsung: Use right device for DMA-mapping calls
  serial: imx: setup DCEDTE early and ensure DCD and RI irqs to be off
  tty: fix comment typo s/repsonsible/responsible/
  tty: amba-pl011: Fix spurious TX interrupts
  serial: xuartps: Enable clocks in the pm disable case also
  serial: core: Re-use struct uart_port {name} field
  ...

48 files changed:
Documentation/devicetree/bindings/arm/sprd.txt
Documentation/devicetree/bindings/serial/sprd-uart.txt
MAINTAINERS
drivers/tty/Makefile
drivers/tty/hvc/hvc_console.c
drivers/tty/hvc/hvcs.c
drivers/tty/n_gsm.c
drivers/tty/pty.c
drivers/tty/serdev/core.c
drivers/tty/serial/8250/8250_core.c
drivers/tty/serial/8250/8250_dw.c
drivers/tty/serial/8250/8250_early.c
drivers/tty/serial/8250/8250_exar.c
drivers/tty/serial/8250/8250_fintek.c
drivers/tty/serial/8250/8250_lpss.c
drivers/tty/serial/8250/8250_port.c
drivers/tty/serial/Kconfig
drivers/tty/serial/Makefile
drivers/tty/serial/altera_jtaguart.c
drivers/tty/serial/altera_uart.c
drivers/tty/serial/amba-pl011.c
drivers/tty/serial/atmel_serial.c
drivers/tty/serial/atmel_serial.h [moved from include/linux/atmel_serial.h with 100% similarity]
drivers/tty/serial/fsl_lpuart.c
drivers/tty/serial/imx.c
drivers/tty/serial/omap-serial.c
drivers/tty/serial/samsung.c
drivers/tty/serial/sb1250-duart.c
drivers/tty/serial/serial_core.c
drivers/tty/serial/sh-sci.c
drivers/tty/serial/sprd_serial.c
drivers/tty/serial/st-asc.c
drivers/tty/serial/uartlite.c
drivers/tty/serial/xilinx_uartps.c
drivers/tty/tty_baudrate.c [new file with mode: 0644]
drivers/tty/tty_io.c
drivers/tty/tty_ioctl.c
drivers/tty/tty_jobctrl.c [new file with mode: 0644]
drivers/tty/vt/selection.c
drivers/tty/vt/vt.c
include/linux/console.h
include/linux/serdev.h
include/linux/serial_core.h
include/linux/tty.h
init/main.c
kernel/printk/braille.c
kernel/printk/braille.h
kernel/printk/printk.c

index 31a629dc75b8a959a3ad937a60364fa8dddccff0..3df034b13e284338df88af478101b4c4cd1542d0 100644 (file)
@@ -1,11 +1,14 @@
 Spreadtrum SoC Platforms Device Tree Bindings
 ----------------------------------------------------
 
-Sharkl64 is a Spreadtrum's SoC Platform which is based
-on ARM 64-bit processor.
+SC9836 openphone Board
+Required root node properties:
+       - compatible = "sprd,sc9836-openphone", "sprd,sc9836";
 
-SC9836 openphone board with SC9836 SoC based on the
-Sharkl64 Platform shall have the following properties.
+SC9860 SoC
+Required root node properties:
+       - compatible = "sprd,sc9860"
 
+SP9860G 3GFHD Board
 Required root node properties:
-        - compatible = "sprd,sc9836-openphone", "sprd,sc9836";
+       - compatible = "sprd,sp9860g-1h10", "sprd,sc9860";
index 2aff0f22c9fa31837b9f49cd6d4213d14186f9e4..cab40f0f6f497ab7bc0b3ab9640bcc0563a8cddb 100644 (file)
@@ -1,7 +1,19 @@
 * Spreadtrum serial UART
 
 Required properties:
-- compatible: must be "sprd,sc9836-uart"
+- compatible: must be one of:
+  * "sprd,sc9836-uart"
+  * "sprd,sc9860-uart", "sprd,sc9836-uart"
+
 - reg: offset and length of the register set for the device
 - interrupts: exactly one interrupt specifier
 - clocks: phandles to input clocks.
+
+Example:
+       uart0: serial@0 {
+               compatible = "sprd,sc9860-uart",
+                            "sprd,sc9836-uart";
+               reg = <0x0 0x100>;
+               interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&ext_26m>;
+       };
index 08360bb0468bed0f0f5764ae8ded3c5dfde46c7e..56b1111cc9c1d3e106862a961bca05f1b0ee6c40 100644 (file)
@@ -8418,7 +8418,7 @@ MICROCHIP / ATMEL AT91 / AT32 SERIAL DRIVER
 M:     Richard Genoud <richard.genoud@gmail.com>
 S:     Maintained
 F:     drivers/tty/serial/atmel_serial.c
-F:     include/linux/atmel_serial.h
+F:     drivers/tty/serial/atmel_serial.h
 
 MICROCHIP / ATMEL DMA DRIVER
 M:     Ludovic Desroches <ludovic.desroches@microchip.com>
index b95bed92da9f050c576ce891309876cf53c837af..f02becdb3e33850c368d2903656ebf16265c654c 100644 (file)
@@ -1,5 +1,6 @@
 obj-$(CONFIG_TTY)              += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
-                                  tty_buffer.o tty_port.o tty_mutex.o tty_ldsem.o
+                                  tty_buffer.o tty_port.o tty_mutex.o \
+                                  tty_ldsem.o tty_baudrate.o tty_jobctrl.o
 obj-$(CONFIG_LEGACY_PTYS)      += pty.o
 obj-$(CONFIG_UNIX98_PTYS)      += pty.o
 obj-$(CONFIG_AUDIT)            += tty_audit.o
index b19ae36a05ec753c4edade42ff1f55c036a28c8a..a8d399188242efe428bb579f845b716396465d9f 100644 (file)
@@ -920,17 +920,17 @@ int hvc_remove(struct hvc_struct *hp)
 
        tty = tty_port_tty_get(&hp->port);
 
+       console_lock();
        spin_lock_irqsave(&hp->lock, flags);
        if (hp->index < MAX_NR_HVC_CONSOLES) {
-               console_lock();
                vtermnos[hp->index] = -1;
                cons_ops[hp->index] = NULL;
-               console_unlock();
        }
 
        /* Don't whack hp->irq because tty_hangup() will need to free the irq. */
 
        spin_unlock_irqrestore(&hp->lock, flags);
+       console_unlock();
 
        /*
         * We 'put' the instance that was grabbed when the kref instance
index 7823d6d998cfd8ca70ab32eb2f51f43c34ef9f3c..99bb875178d7170f50d6500a3d6ec5fb8be757b0 100644 (file)
@@ -1575,7 +1575,7 @@ static int __init hvcs_module_init(void)
         */
        rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan);
        if (rc)
-               pr_warning("HVCS: Failed to create rescan file (err %d)\n", rc);
+               pr_warn("HVCS: Failed to create rescan file (err %d)\n", rc);
 
        return 0;
 }
index 55577cf9b6a4e0eba43462bba0bd5b5039172f17..2667a205a5abcf47ed4ab059ff350b5f35ea3fe1 100644 (file)
@@ -89,18 +89,14 @@ module_param(debug, int, 0600);
 /**
  *     struct gsm_mux_net      -       network interface
  *     @struct gsm_dlci* dlci
- *     @struct net_device_stats stats;
  *
  *     Created when net interface is initialized.
  **/
 struct gsm_mux_net {
        struct kref ref;
        struct gsm_dlci *dlci;
-       struct net_device_stats stats;
 };
 
-#define STATS(net) (((struct gsm_mux_net *)netdev_priv(net))->stats)
-
 /*
  *     Each block of data we have queued to go out is in the form of
  *     a gsm_msg which holds everything we need in a link layer independent
@@ -2613,10 +2609,6 @@ static int gsm_mux_net_close(struct net_device *net)
        return 0;
 }
 
-static struct net_device_stats *gsm_mux_net_get_stats(struct net_device *net)
-{
-       return &((struct gsm_mux_net *)netdev_priv(net))->stats;
-}
 static void dlci_net_free(struct gsm_dlci *dlci)
 {
        if (!dlci->net) {
@@ -2660,8 +2652,8 @@ static int gsm_mux_net_start_xmit(struct sk_buff *skb,
        muxnet_get(mux_net);
 
        skb_queue_head(&dlci->skb_list, skb);
-       STATS(net).tx_packets++;
-       STATS(net).tx_bytes += skb->len;
+       net->stats.tx_packets++;
+       net->stats.tx_bytes += skb->len;
        gsm_dlci_data_kick(dlci);
        /* And tell the kernel when the last transmit started. */
        netif_trans_update(net);
@@ -2676,7 +2668,7 @@ static void gsm_mux_net_tx_timeout(struct net_device *net)
        dev_dbg(&net->dev, "Tx timed out.\n");
 
        /* Update statistics */
-       STATS(net).tx_errors++;
+       net->stats.tx_errors++;
 }
 
 static void gsm_mux_rx_netchar(struct gsm_dlci *dlci,
@@ -2691,7 +2683,7 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci,
        skb = dev_alloc_skb(size + NET_IP_ALIGN);
        if (!skb) {
                /* We got no receive buffer. */
-               STATS(net).rx_dropped++;
+               net->stats.rx_dropped++;
                muxnet_put(mux_net);
                return;
        }
@@ -2705,8 +2697,8 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci,
        netif_rx(skb);
 
        /* update out statistics */
-       STATS(net).rx_packets++;
-       STATS(net).rx_bytes += size;
+       net->stats.rx_packets++;
+       net->stats.rx_bytes += size;
        muxnet_put(mux_net);
        return;
 }
@@ -2718,7 +2710,6 @@ static void gsm_mux_net_init(struct net_device *net)
                .ndo_stop               = gsm_mux_net_close,
                .ndo_start_xmit         = gsm_mux_net_start_xmit,
                .ndo_tx_timeout         = gsm_mux_net_tx_timeout,
-               .ndo_get_stats          = gsm_mux_net_get_stats,
        };
 
        net->netdev_ops = &gsm_netdev_ops;
index 66b59a15780db0c33021d32251ecb9886c758703..65799575c66681184057cc0565e5a45bcdd25681 100644 (file)
@@ -216,16 +216,11 @@ static int pty_signal(struct tty_struct *tty, int sig)
 static void pty_flush_buffer(struct tty_struct *tty)
 {
        struct tty_struct *to = tty->link;
-       struct tty_ldisc *ld;
 
        if (!to)
                return;
 
-       ld = tty_ldisc_ref(to);
-       tty_buffer_flush(to, ld);
-       if (ld)
-               tty_ldisc_deref(ld);
-
+       tty_buffer_flush(to, NULL);
        if (to->packet) {
                spin_lock_irq(&tty->ctrl_lock);
                tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
index 1c4829a56351d4fa1bda397afa09004680d0cab4..433de5ea9b02f53386b9367e64ad880ac6795e07 100644 (file)
@@ -116,17 +116,41 @@ void serdev_device_close(struct serdev_device *serdev)
 }
 EXPORT_SYMBOL_GPL(serdev_device_close);
 
-int serdev_device_write_buf(struct serdev_device *serdev,
-                           const unsigned char *buf, size_t count)
+void serdev_device_write_wakeup(struct serdev_device *serdev)
+{
+       complete(&serdev->write_comp);
+}
+EXPORT_SYMBOL_GPL(serdev_device_write_wakeup);
+
+int serdev_device_write(struct serdev_device *serdev,
+                       const unsigned char *buf, size_t count,
+                       unsigned long timeout)
 {
        struct serdev_controller *ctrl = serdev->ctrl;
+       int ret;
 
-       if (!ctrl || !ctrl->ops->write_buf)
+       if (!ctrl || !ctrl->ops->write_buf ||
+           (timeout && !serdev->ops->write_wakeup))
                return -EINVAL;
 
-       return ctrl->ops->write_buf(ctrl, buf, count);
+       mutex_lock(&serdev->write_lock);
+       do {
+               reinit_completion(&serdev->write_comp);
+
+               ret = ctrl->ops->write_buf(ctrl, buf, count);
+               if (ret < 0)
+                       break;
+
+               buf += ret;
+               count -= ret;
+
+       } while (count &&
+                (timeout = wait_for_completion_timeout(&serdev->write_comp,
+                                                       timeout)));
+       mutex_unlock(&serdev->write_lock);
+       return ret < 0 ? ret : (count ? -ETIMEDOUT : 0);
 }
-EXPORT_SYMBOL_GPL(serdev_device_write_buf);
+EXPORT_SYMBOL_GPL(serdev_device_write);
 
 void serdev_device_write_flush(struct serdev_device *serdev)
 {
@@ -262,6 +286,8 @@ struct serdev_device *serdev_device_alloc(struct serdev_controller *ctrl)
        serdev->dev.parent = &ctrl->dev;
        serdev->dev.bus = &serdev_bus_type;
        serdev->dev.type = &serdev_device_type;
+       init_completion(&serdev->write_comp);
+       mutex_init(&serdev->write_lock);
        return serdev;
 }
 EXPORT_SYMBOL_GPL(serdev_device_alloc);
index 76e03a7de9cc3d790a230948f599ef2db4b93249..48a07e2f617fb69dbcd55cd03205e7abcde8a39b 100644 (file)
@@ -218,7 +218,7 @@ static int serial_link_irq_chain(struct uart_8250_port *up)
                spin_unlock_irq(&i->lock);
                irq_flags |= up->port.irqflags;
                ret = request_irq(up->port.irq, serial8250_interrupt,
-                                 irq_flags, "serial", i);
+                                 irq_flags, up->port.name, i);
                if (ret < 0)
                        serial_do_unlink(i, up);
        }
index e65808c482f1847d09d819a24defb0e5cf6508b5..787b1160d3a535aceafc40f742652deb9d5f2182 100644 (file)
@@ -530,12 +530,11 @@ static int dw8250_probe(struct platform_device *pdev)
        }
 
        data->rst = devm_reset_control_get_optional(dev, NULL);
-       if (IS_ERR(data->rst) && PTR_ERR(data->rst) == -EPROBE_DEFER) {
-               err = -EPROBE_DEFER;
+       if (IS_ERR(data->rst)) {
+               err = PTR_ERR(data->rst);
                goto err_pclk;
        }
-       if (!IS_ERR(data->rst))
-               reset_control_deassert(data->rst);
+       reset_control_deassert(data->rst);
 
        dw8250_quirks(p, data);
 
@@ -567,8 +566,7 @@ static int dw8250_probe(struct platform_device *pdev)
        return 0;
 
 err_reset:
-       if (!IS_ERR(data->rst))
-               reset_control_assert(data->rst);
+       reset_control_assert(data->rst);
 
 err_pclk:
        if (!IS_ERR(data->pclk))
@@ -589,8 +587,7 @@ static int dw8250_remove(struct platform_device *pdev)
 
        serial8250_unregister_port(data->line);
 
-       if (!IS_ERR(data->rst))
-               reset_control_assert(data->rst);
+       reset_control_assert(data->rst);
 
        if (!IS_ERR(data->pclk))
                clk_disable_unprepare(data->pclk);
index 85a12f03240247494366a231d24fa86ba042dfdb..82fc48eca1df705521c99036d1a0d7f6e7d63402 100644 (file)
@@ -39,6 +39,7 @@
 
 static unsigned int __init serial8250_early_in(struct uart_port *port, int offset)
 {
+       int reg_offset = offset;
        offset <<= port->regshift;
 
        switch (port->iotype) {
@@ -52,6 +53,8 @@ static unsigned int __init serial8250_early_in(struct uart_port *port, int offse
                return ioread32be(port->membase + offset);
        case UPIO_PORT:
                return inb(port->iobase + offset);
+       case UPIO_AU:
+               return port->serial_in(port, reg_offset);
        default:
                return 0;
        }
@@ -59,6 +62,7 @@ static unsigned int __init serial8250_early_in(struct uart_port *port, int offse
 
 static void __init serial8250_early_out(struct uart_port *port, int offset, int value)
 {
+       int reg_offset = offset;
        offset <<= port->regshift;
 
        switch (port->iotype) {
@@ -77,6 +81,9 @@ static void __init serial8250_early_out(struct uart_port *port, int offset, int
        case UPIO_PORT:
                outb(value, port->iobase + offset);
                break;
+       case UPIO_AU:
+               port->serial_out(port, reg_offset, value);
+               break;
        }
 }
 
@@ -172,3 +179,20 @@ OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup);
 OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);
 
 #endif
+
+#ifdef CONFIG_SERIAL_8250_RT288X
+
+unsigned int au_serial_in(struct uart_port *p, int offset);
+void au_serial_out(struct uart_port *p, int offset, int value);
+
+static int __init early_au_setup(struct earlycon_device *dev, const char *opt)
+{
+       dev->port.serial_in = au_serial_in;
+       dev->port.serial_out = au_serial_out;
+       dev->port.iotype = UPIO_AU;
+       dev->con->write = early_serial8250_write;
+       return 0;
+}
+OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup);
+
+#endif
index b89c4ffc048655dc65c241afef5bfb21c13841bd..1270ff163f637bf5c61f7cbf3ad3ff51c69366b9 100644 (file)
@@ -483,5 +483,5 @@ static struct pci_driver exar_pci_driver = {
 module_pci_driver(exar_pci_driver);
 
 MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Exar Serial Dricer");
+MODULE_DESCRIPTION("Exar Serial Driver");
 MODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>");
index b67e7a5449353ca8ac2f3a67e0af132517c4f643..e500f7dd2470a28254b1d8d57374a4fbd0fc02fa 100644 (file)
  * The IRQ setting mode of F81866 is not the same with F81216 series.
  *     Level/Low: IRQ_MODE0:0, IRQ_MODE1:0
  *     Edge/High: IRQ_MODE0:1, IRQ_MODE1:0
+ *
+ * Clock speeds for UART (register F2h)
+ * 00: 1.8432MHz.
+ * 01: 18.432MHz.
+ * 10: 24MHz.
+ * 11: 14.769MHz.
  */
 #define F81866_IRQ_MODE                0xf0
 #define F81866_IRQ_SHARE       BIT(0)
 #define F81866_LDN_LOW         0x10
 #define F81866_LDN_HIGH                0x16
 
+#define F81866_UART_CLK 0xF2
+#define F81866_UART_CLK_MASK (BIT(1) | BIT(0))
+#define F81866_UART_CLK_1_8432MHZ 0
+#define F81866_UART_CLK_14_769MHZ (BIT(1) | BIT(0))
+#define F81866_UART_CLK_18_432MHZ BIT(0)
+#define F81866_UART_CLK_24MHZ BIT(1)
+
 struct fintek_8250 {
        u16 pid;
        u16 base_port;
@@ -256,8 +269,26 @@ static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata)
        }
 }
 
-static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address,
-                         unsigned int irq)
+static void fintek_8250_goto_highspeed(struct uart_8250_port *uart,
+                             struct fintek_8250 *pdata)
+{
+       sio_write_reg(pdata, LDN, pdata->index);
+
+       switch (pdata->pid) {
+       case CHIP_ID_F81866: /* set uart clock for high speed serial mode */
+               sio_write_mask_reg(pdata, F81866_UART_CLK,
+                       F81866_UART_CLK_MASK,
+                       F81866_UART_CLK_14_769MHZ);
+
+                       uart->port.uartclk = 921600 * 16;
+               break;
+       default: /* leave clock speed untouched */
+               break;
+       }
+}
+
+static int probe_setup_port(struct fintek_8250 *pdata,
+                                       struct uart_8250_port *uart)
 {
        static const u16 addr[] = {0x4e, 0x2e};
        static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67};
@@ -284,18 +315,20 @@ static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address,
                                sio_write_reg(pdata, LDN, k);
                                aux = sio_read_reg(pdata, IO_ADDR1);
                                aux |= sio_read_reg(pdata, IO_ADDR2) << 8;
-                               if (aux != io_address)
+                               if (aux != uart->port.iobase)
                                        continue;
 
                                pdata->index = k;
 
-                               irq_data = irq_get_irq_data(irq);
+                               irq_data = irq_get_irq_data(uart->port.irq);
                                if (irq_data)
                                        level_mode =
                                                irqd_is_level_type(irq_data);
 
                                fintek_8250_set_irq_mode(pdata, level_mode);
                                fintek_8250_set_max_fifo(pdata);
+                               fintek_8250_goto_highspeed(uart, pdata);
+
                                fintek_8250_exit_key(addr[i]);
 
                                return 0;
@@ -330,7 +363,7 @@ int fintek_8250_probe(struct uart_8250_port *uart)
        struct fintek_8250 *pdata;
        struct fintek_8250 probe_data;
 
-       if (probe_setup_port(&probe_data, uart->port.iobase, uart->port.irq))
+       if (probe_setup_port(&probe_data, uart))
                return -ENODEV;
 
        pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_KERNEL);
index f3ea90f0e411bcec99312d51fe77b0af88377006..7dddd7e6a01cbd45308efe1860fc34ddd154275c 100644 (file)
@@ -183,7 +183,6 @@ static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port)
        if (ret)
                return;
 
-       pci_set_master(pdev);
        pci_try_set_mwi(pdev);
 
        /* Special DMA address for UART */
@@ -216,6 +215,8 @@ static int qrk_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
        struct pci_dev *pdev = to_pci_dev(port->dev);
        int ret;
 
+       pci_set_master(pdev);
+
        ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
        if (ret < 0)
                return ret;
index 6119516ef5fcd3a604ae0a41a70b9f6f51f7fa1d..09a65a3ec7f7df0bb3f7355fd7758d797169f960 100644 (file)
@@ -328,7 +328,7 @@ static const s8 au_io_out_map[8] = {
        -1,     /* UART_SCR (unmapped) */
 };
 
-static unsigned int au_serial_in(struct uart_port *p, int offset)
+unsigned int au_serial_in(struct uart_port *p, int offset)
 {
        if (offset >= ARRAY_SIZE(au_io_in_map))
                return UINT_MAX;
@@ -338,7 +338,7 @@ static unsigned int au_serial_in(struct uart_port *p, int offset)
        return __raw_readl(p->membase + (offset << p->regshift));
 }
 
-static void au_serial_out(struct uart_port *p, int offset, int value)
+void au_serial_out(struct uart_port *p, int offset, int value)
 {
        if (offset >= ARRAY_SIZE(au_io_out_map))
                return;
index 6117ac8da48f739fc4bf904b5a52db31b5861b50..5c8850f7a2a05a252ccf4762f1b594fd67697877 100644 (file)
@@ -630,6 +630,15 @@ config SERIAL_UARTLITE_CONSOLE
          console (the system console is the device which receives all kernel
          messages and warnings and which allows logins in single user mode).
 
+config SERIAL_UARTLITE_NR_UARTS
+       int "Maximum number of uartlite serial ports"
+       depends on SERIAL_UARTLITE
+       range 1 256
+       default 1
+       help
+         Set this to the number of uartlites in your system, or the number
+         you think you might implement.
+
 config SERIAL_SUNCORE
        bool
        depends on SPARC
@@ -1343,6 +1352,7 @@ config SERIAL_ALTERA_JTAGUART_CONSOLE
        bool "Altera JTAG UART console support"
        depends on SERIAL_ALTERA_JTAGUART=y
        select SERIAL_CORE_CONSOLE
+       select SERIAL_EARLYCON
        help
          Enable a Altera JTAG UART port to be the system console.
 
@@ -1382,6 +1392,7 @@ config SERIAL_ALTERA_UART_CONSOLE
        bool "Altera UART console support"
        depends on SERIAL_ALTERA_UART=y
        select SERIAL_CORE_CONSOLE
+       select SERIAL_EARLYCON
        help
          Enable a Altera UART port to be the system console.
 
index 2d6288bc45543f4d68ada0e44538564230db79c6..53c03e005132105a6735e17fcbb132ca6d9582dc 100644 (file)
@@ -3,7 +3,6 @@
 #
 
 obj-$(CONFIG_SERIAL_CORE) += serial_core.o
-obj-$(CONFIG_SERIAL_21285) += 21285.o
 
 obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
 obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o
@@ -17,6 +16,8 @@ obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o
 obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o
 obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o
 
+obj-$(CONFIG_SERIAL_21285) += 21285.o
+
 # Now bring in any enabled 8250/16450/16550 type drivers.
 obj-$(CONFIG_SERIAL_8250) += 8250/
 
index e409d7dac7abfd5800a5b9a56d013cb569ca26f1..18e3f8342b8554ae4e7d414dd39653fb3a33b26f 100644 (file)
@@ -383,6 +383,26 @@ console_initcall(altera_jtaguart_console_init);
 
 #define        ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console)
 
+static void altera_jtaguart_earlycon_write(struct console *co, const char *s,
+                                          unsigned int count)
+{
+       struct earlycon_device *dev = co->data;
+
+       uart_console_write(&dev->port, s, count, altera_jtaguart_console_putc);
+}
+
+static int __init altera_jtaguart_earlycon_setup(struct earlycon_device *dev,
+                                                const char *options)
+{
+       if (!dev->port.membase)
+               return -ENODEV;
+
+       dev->con->write = altera_jtaguart_earlycon_write;
+       return 0;
+}
+
+OF_EARLYCON_DECLARE(juart, "altr,juart-1.0", altera_jtaguart_earlycon_setup);
+
 #else
 
 #define        ALTERA_JTAGUART_CONSOLE NULL
index 820a74208696bf57b275542ef810663c6ea175b7..46d3438a0d27014ce97115e7a17b0eb024f12035 100644 (file)
@@ -489,6 +489,38 @@ console_initcall(altera_uart_console_init);
 
 #define        ALTERA_UART_CONSOLE     (&altera_uart_console)
 
+static void altera_uart_earlycon_write(struct console *co, const char *s,
+                                      unsigned int count)
+{
+       struct earlycon_device *dev = co->data;
+
+       uart_console_write(&dev->port, s, count, altera_uart_console_putc);
+}
+
+static int __init altera_uart_earlycon_setup(struct earlycon_device *dev,
+                                            const char *options)
+{
+       struct uart_port *port = &dev->port;
+
+       if (!port->membase)
+               return -ENODEV;
+
+       /* Enable RX interrupts now */
+       writel(ALTERA_UART_CONTROL_RRDY_MSK,
+              port->membase + ALTERA_UART_CONTROL_REG);
+
+       if (dev->baud) {
+               unsigned int baudclk = port->uartclk / dev->baud;
+
+               writel(baudclk, port->membase + ALTERA_UART_DIVISOR_REG);
+       }
+
+       dev->con->write = altera_uart_earlycon_write;
+       return 0;
+}
+
+OF_EARLYCON_DECLARE(uart, "altr,uart-1.0", altera_uart_earlycon_setup);
+
 #else
 
 #define        ALTERA_UART_CONSOLE     NULL
index b0a377725d636c11cddad62470d821a31f1fff96..8a857bb34fbb26c6d60784d3fe7576730a9aa5b3 100644 (file)
@@ -1327,14 +1327,15 @@ static void pl011_stop_tx(struct uart_port *port)
        pl011_dma_tx_stop(uap);
 }
 
-static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq);
+static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq);
 
 /* Start TX with programmed I/O only (no DMA) */
 static void pl011_start_tx_pio(struct uart_amba_port *uap)
 {
-       uap->im |= UART011_TXIM;
-       pl011_write(uap->im, uap, REG_IMSC);
-       pl011_tx_chars(uap, false);
+       if (pl011_tx_chars(uap, false)) {
+               uap->im |= UART011_TXIM;
+               pl011_write(uap->im, uap, REG_IMSC);
+       }
 }
 
 static void pl011_start_tx(struct uart_port *port)
@@ -1414,25 +1415,26 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c,
        return true;
 }
 
-static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
+/* Returns true if tx interrupts have to be (kept) enabled  */
+static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
 {
        struct circ_buf *xmit = &uap->port.state->xmit;
        int count = uap->fifosize >> 1;
 
        if (uap->port.x_char) {
                if (!pl011_tx_char(uap, uap->port.x_char, from_irq))
-                       return;
+                       return true;
                uap->port.x_char = 0;
                --count;
        }
        if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
                pl011_stop_tx(&uap->port);
-               return;
+               return false;
        }
 
        /* If we are using DMA mode, try to send some characters. */
        if (pl011_dma_tx_irq(uap))
-               return;
+               return true;
 
        do {
                if (likely(from_irq) && count-- == 0)
@@ -1447,8 +1449,11 @@ static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(&uap->port);
 
-       if (uart_circ_empty(xmit))
+       if (uart_circ_empty(xmit)) {
                pl011_stop_tx(&uap->port);
+               return false;
+       }
+       return true;
 }
 
 static void pl011_modem_status(struct uart_amba_port *uap)
@@ -2470,19 +2475,34 @@ static int __init pl011_early_console_setup(struct earlycon_device *device,
        if (!device->port.membase)
                return -ENODEV;
 
-       /* On QDF2400 SOCs affected by Erratum 44, the "qdf2400_e44" must
-        * also be specified, e.g. "earlycon=pl011,<address>,qdf2400_e44".
-        */
-       if (!strcmp(device->options, "qdf2400_e44"))
-               device->con->write = qdf2400_e44_early_write;
-       else
-               device->con->write = pl011_early_write;
+       device->con->write = pl011_early_write;
 
        return 0;
 }
 OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);
 OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", pl011_early_console_setup);
-EARLYCON_DECLARE(qdf2400_e44, pl011_early_console_setup);
+
+/*
+ * On Qualcomm Datacenter Technologies QDF2400 SOCs affected by
+ * Erratum 44, traditional earlycon can be enabled by specifying
+ * "earlycon=qdf2400_e44,<address>".  Any options are ignored.
+ *
+ * Alternatively, you can just specify "earlycon", and the early console
+ * will be enabled with the information from the SPCR table.  In this
+ * case, the SPCR code will detect the need for the E44 work-around,
+ * and set the console name to "qdf2400_e44".
+ */
+static int __init
+qdf2400_e44_early_console_setup(struct earlycon_device *device,
+                               const char *opt)
+{
+       if (!device->port.membase)
+               return -ENODEV;
+
+       device->con->write = qdf2400_e44_early_write;
+       return 0;
+}
+EARLYCON_DECLARE(qdf2400_e44, qdf2400_e44_early_console_setup);
 
 #else
 #define AMBA_CONSOLE   NULL
index 1f50a83ef958609c3f27473b135e84e65303330d..c355ac9abafcc12adb5f8fd3205adaf4a16f86c1 100644 (file)
@@ -38,7 +38,6 @@
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 #include <linux/atmel_pdc.h>
-#include <linux/atmel_serial.h>
 #include <linux/uaccess.h>
 #include <linux/platform_data/atmel.h>
 #include <linux/timer.h>
@@ -71,6 +70,7 @@
 #include <linux/serial_core.h>
 
 #include "serial_mctrl_gpio.h"
+#include "atmel_serial.h"
 
 static void atmel_start_rx(struct uart_port *port);
 static void atmel_stop_rx(struct uart_port *port);
@@ -119,8 +119,9 @@ struct atmel_uart_char {
 /*
  * at91: 6 USARTs and one DBGU port (SAM9260)
  * avr32: 4
+ * samx7: 3 USARTs and 5 UARTs
  */
-#define ATMEL_MAX_UART         7
+#define ATMEL_MAX_UART         8
 
 /*
  * We wrap our port structure around the generic uart_port.
@@ -175,6 +176,7 @@ struct atmel_uart_port {
        unsigned int            pending_status;
        spinlock_t              lock_suspended;
 
+#ifdef CONFIG_PM
        struct {
                u32             cr;
                u32             mr;
@@ -185,6 +187,7 @@ struct atmel_uart_port {
                u32             fmr;
                u32             fimr;
        } cache;
+#endif
 
        int (*prepare_rx)(struct uart_port *port);
        int (*prepare_tx)(struct uart_port *port);
index f02934ffb3293de45601f434be9dd7786cafdfe4..15df1ba7809510b45c6d96b17d72e92064301973 100644 (file)
@@ -1705,6 +1705,13 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count)
 {
        struct lpuart_port *sport = lpuart_ports[co->index];
        unsigned char  old_cr2, cr2;
+       unsigned long flags;
+       int locked = 1;
+
+       if (sport->port.sysrq || oops_in_progress)
+               locked = spin_trylock_irqsave(&sport->port.lock, flags);
+       else
+               spin_lock_irqsave(&sport->port.lock, flags);
 
        /* first save CR2 and then disable interrupts */
        cr2 = old_cr2 = readb(sport->port.membase + UARTCR2);
@@ -1719,6 +1726,9 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count)
                barrier();
 
        writeb(old_cr2, sport->port.membase + UARTCR2);
+
+       if (locked)
+               spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 static void
@@ -1726,6 +1736,13 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count)
 {
        struct lpuart_port *sport = lpuart_ports[co->index];
        unsigned long  old_cr, cr;
+       unsigned long flags;
+       int locked = 1;
+
+       if (sport->port.sysrq || oops_in_progress)
+               locked = spin_trylock_irqsave(&sport->port.lock, flags);
+       else
+               spin_lock_irqsave(&sport->port.lock, flags);
 
        /* first save CR2 and then disable interrupts */
        cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL);
@@ -1740,6 +1757,9 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count)
                barrier();
 
        lpuart32_write(old_cr, sport->port.membase + UARTCTRL);
+
+       if (locked)
+               spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 /*
index e3e152cbc75e71d7bc4e966f8a4ec3a1884c2b95..33509b4beaec237715de1e8febd63e861fbb9c15 100644 (file)
@@ -719,6 +719,27 @@ out:
        return IRQ_HANDLED;
 }
 
+static void imx_disable_rx_int(struct imx_port *sport)
+{
+       unsigned long temp;
+
+       sport->dma_is_rxing = 1;
+
+       /* disable the receiver ready and aging timer interrupts */
+       temp = readl(sport->port.membase + UCR1);
+       temp &= ~(UCR1_RRDYEN);
+       writel(temp, sport->port.membase + UCR1);
+
+       temp = readl(sport->port.membase + UCR2);
+       temp &= ~(UCR2_ATEN);
+       writel(temp, sport->port.membase + UCR2);
+
+       /* disable the rx errors interrupts */
+       temp = readl(sport->port.membase + UCR4);
+       temp &= ~UCR4_OREN;
+       writel(temp, sport->port.membase + UCR4);
+}
+
 static void clear_rx_errors(struct imx_port *sport);
 static int start_rx_dma(struct imx_port *sport);
 /*
@@ -734,21 +755,8 @@ static void imx_dma_rxint(struct imx_port *sport)
 
        temp = readl(sport->port.membase + USR2);
        if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
-               sport->dma_is_rxing = 1;
 
-               /* disable the receiver ready and aging timer interrupts */
-               temp = readl(sport->port.membase + UCR1);
-               temp &= ~(UCR1_RRDYEN);
-               writel(temp, sport->port.membase + UCR1);
-
-               temp = readl(sport->port.membase + UCR2);
-               temp &= ~(UCR2_ATEN);
-               writel(temp, sport->port.membase + UCR2);
-
-               /* disable the rx errors interrupts */
-               temp = readl(sport->port.membase + UCR4);
-               temp &= ~UCR4_OREN;
-               writel(temp, sport->port.membase + UCR4);
+               imx_disable_rx_int(sport);
 
                /* tell the DMA to receive the data. */
                start_rx_dma(sport);
@@ -1317,19 +1325,10 @@ static int imx_startup(struct uart_port *port)
        if (!is_imx1_uart(sport)) {
                temp = readl(sport->port.membase + UCR3);
 
-               /*
-                * The effect of RI and DCD differs depending on the UFCR_DCEDTE
-                * bit. In DCE mode they control the outputs, in DTE mode they
-                * enable the respective irqs. At least the DCD irq cannot be
-                * cleared on i.MX25 at least, so it's not usable and must be
-                * disabled. I don't have test hardware to check if RI has the
-                * same problem but I consider this likely so it's disabled for
-                * now, too.
-                */
-               temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP |
-                       UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
+               temp |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
 
                if (sport->dte_mode)
+                       /* disable broken interrupts */
                        temp &= ~(UCR3_RI | UCR3_DCD);
 
                writel(temp, sport->port.membase + UCR3);
@@ -1339,6 +1338,33 @@ static int imx_startup(struct uart_port *port)
         * Enable modem status interrupts
         */
        imx_enable_ms(&sport->port);
+
+       /*
+        * If the serial port is opened for reading start RX DMA immediately
+        * instead of waiting for RX FIFO interrupts. In our iMX53 the average
+        * delay for the first reception dropped from approximately 35000
+        * microseconds to 1000 microseconds.
+        */
+       if (sport->dma_is_enabled) {
+               struct tty_struct *tty = sport->port.state->port.tty;
+               struct tty_file_private *file_priv;
+               int readcnt = 0;
+
+               spin_lock(&tty->files_lock);
+
+               if (!list_empty(&tty->tty_files))
+                       list_for_each_entry(file_priv, &tty->tty_files, list)
+                               if (!(file_priv->file->f_flags & O_WRONLY))
+                                       readcnt++;
+
+               spin_unlock(&tty->files_lock);
+
+               if (readcnt > 0) {
+                       imx_disable_rx_int(sport);
+                       start_rx_dma(sport);
+               }
+       }
+
        spin_unlock_irqrestore(&sport->port.lock, flags);
 
        return 0;
@@ -1584,8 +1610,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 
        ufcr = readl(sport->port.membase + UFCR);
        ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
-       if (sport->dte_mode)
-               ufcr |= UFCR_DCEDTE;
        writel(ufcr, sport->port.membase + UFCR);
 
        writel(num, sport->port.membase + UBIR);
@@ -2153,6 +2177,27 @@ static int serial_imx_probe(struct platform_device *pdev)
                 UCR1_TXMPTYEN | UCR1_RTSDEN);
        writel_relaxed(reg, sport->port.membase + UCR1);
 
+       if (!is_imx1_uart(sport) && sport->dte_mode) {
+               /*
+                * The DCEDTE bit changes the direction of DSR, DCD, DTR and RI
+                * and influences if UCR3_RI and UCR3_DCD changes the level of RI
+                * and DCD (when they are outputs) or enables the respective
+                * irqs. So set this bit early, i.e. before requesting irqs.
+                */
+               writel(UFCR_DCEDTE, sport->port.membase + UFCR);
+
+               /*
+                * Disable UCR3_RI and UCR3_DCD irqs. They are also not
+                * enabled later because they cannot be cleared
+                * (confirmed on i.MX25) which makes them unusable.
+                */
+               writel(IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR,
+                      sport->port.membase + UCR3);
+
+       } else {
+               writel(0, sport->port.membase + UFCR);
+       }
+
        clk_disable_unprepare(sport->clk_ipg);
 
        /*
index a4734649a0f0f8b0ea3de17c5c26c3a2acc54c19..1ea05ac57aa7197b4f690776acc609d376faade4 100644 (file)
@@ -1770,7 +1770,8 @@ static int serial_omap_probe(struct platform_device *pdev)
        return 0;
 
 err_add_port:
-       pm_runtime_put(&pdev->dev);
+       pm_runtime_dont_use_autosuspend(&pdev->dev);
+       pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        pm_qos_remove_request(&up->pm_qos_request);
        device_init_wakeup(up->dev, false);
@@ -1783,9 +1784,13 @@ static int serial_omap_remove(struct platform_device *dev)
 {
        struct uart_omap_port *up = platform_get_drvdata(dev);
 
+       pm_runtime_get_sync(up->dev);
+
+       uart_remove_one_port(&serial_omap_reg, &up->port);
+
+       pm_runtime_dont_use_autosuspend(up->dev);
        pm_runtime_put_sync(up->dev);
        pm_runtime_disable(up->dev);
-       uart_remove_one_port(&serial_omap_reg, &up->port);
        pm_qos_remove_request(&up->pm_qos_request);
        device_init_wakeup(&dev->dev, false);
 
index 7a17aedbf902e05034129a832941f27fe5dc38c8..8aca18c4cdea4076e8666baff7d7f4953d6a6e1f 100644 (file)
@@ -859,7 +859,7 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
 static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
 {
        struct s3c24xx_uart_dma *dma = p->dma;
-       unsigned long flags;
+       int ret;
 
        /* Default slave configuration parameters */
        dma->rx_conf.direction          = DMA_DEV_TO_MEM;
@@ -884,8 +884,8 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
 
        dma->tx_chan = dma_request_chan(p->port.dev, "tx");
        if (IS_ERR(dma->tx_chan)) {
-               dma_release_channel(dma->rx_chan);
-               return PTR_ERR(dma->tx_chan);
+               ret = PTR_ERR(dma->tx_chan);
+               goto err_release_rx;
        }
 
        dmaengine_slave_config(dma->tx_chan, &dma->tx_conf);
@@ -894,26 +894,38 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
        dma->rx_size = PAGE_SIZE;
 
        dma->rx_buf = kmalloc(dma->rx_size, GFP_KERNEL);
-
        if (!dma->rx_buf) {
-               dma_release_channel(dma->rx_chan);
-               dma_release_channel(dma->tx_chan);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_release_tx;
        }
 
-       dma->rx_addr = dma_map_single(dma->rx_chan->device->dev, dma->rx_buf,
+       dma->rx_addr = dma_map_single(p->port.dev, dma->rx_buf,
                                dma->rx_size, DMA_FROM_DEVICE);
-
-       spin_lock_irqsave(&p->port.lock, flags);
+       if (dma_mapping_error(p->port.dev, dma->rx_addr)) {
+               ret = -EIO;
+               goto err_free_rx;
+       }
 
        /* TX buffer */
-       dma->tx_addr = dma_map_single(dma->tx_chan->device->dev,
-                               p->port.state->xmit.buf,
+       dma->tx_addr = dma_map_single(p->port.dev, p->port.state->xmit.buf,
                                UART_XMIT_SIZE, DMA_TO_DEVICE);
-
-       spin_unlock_irqrestore(&p->port.lock, flags);
+       if (dma_mapping_error(p->port.dev, dma->tx_addr)) {
+               ret = -EIO;
+               goto err_unmap_rx;
+       }
 
        return 0;
+
+err_unmap_rx:
+       dma_unmap_single(p->port.dev, dma->rx_addr, dma->rx_size,
+                        DMA_FROM_DEVICE);
+err_free_rx:
+       kfree(dma->rx_buf);
+err_release_tx:
+       dma_release_channel(dma->tx_chan);
+err_release_rx:
+       dma_release_channel(dma->rx_chan);
+       return ret;
 }
 
 static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p)
@@ -922,7 +934,7 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p)
 
        if (dma->rx_chan) {
                dmaengine_terminate_all(dma->rx_chan);
-               dma_unmap_single(dma->rx_chan->device->dev, dma->rx_addr,
+               dma_unmap_single(p->port.dev, dma->rx_addr,
                                dma->rx_size, DMA_FROM_DEVICE);
                kfree(dma->rx_buf);
                dma_release_channel(dma->rx_chan);
@@ -931,7 +943,7 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p)
 
        if (dma->tx_chan) {
                dmaengine_terminate_all(dma->tx_chan);
-               dma_unmap_single(dma->tx_chan->device->dev, dma->tx_addr,
+               dma_unmap_single(p->port.dev, dma->tx_addr,
                                UART_XMIT_SIZE, DMA_TO_DEVICE);
                dma_release_channel(dma->tx_chan);
                dma->tx_chan = NULL;
index 771f361c47ea17850cf4b946d135795e100c0865..041625cc24bbe736cdc021fa0385fbe332f2d792 100644 (file)
@@ -41,7 +41,7 @@
 #include <linux/tty_flip.h>
 #include <linux/types.h>
 
-#include <linux/atomic.h>
+#include <linux/refcount.h>
 #include <asm/io.h>
 #include <asm/war.h>
 
@@ -103,7 +103,7 @@ struct sbd_port {
 struct sbd_duart {
        struct sbd_port         sport[2];
        unsigned long           mapctrl;
-       atomic_t                map_guard;
+       refcount_t              map_guard;
 };
 
 #define to_sport(uport) container_of(uport, struct sbd_port, port)
@@ -654,15 +654,13 @@ static void sbd_release_port(struct uart_port *uport)
 {
        struct sbd_port *sport = to_sport(uport);
        struct sbd_duart *duart = sport->duart;
-       int map_guard;
 
        iounmap(sport->memctrl);
        sport->memctrl = NULL;
        iounmap(uport->membase);
        uport->membase = NULL;
 
-       map_guard = atomic_add_return(-1, &duart->map_guard);
-       if (!map_guard)
+       if(refcount_dec_and_test(&duart->map_guard))
                release_mem_region(duart->mapctrl, DUART_CHANREG_SPACING);
        release_mem_region(uport->mapbase, DUART_CHANREG_SPACING);
 }
@@ -698,7 +696,6 @@ static int sbd_request_port(struct uart_port *uport)
 {
        const char *err = KERN_ERR "sbd: Unable to reserve MMIO resource\n";
        struct sbd_duart *duart = to_sport(uport)->duart;
-       int map_guard;
        int ret = 0;
 
        if (!request_mem_region(uport->mapbase, DUART_CHANREG_SPACING,
@@ -706,11 +703,11 @@ static int sbd_request_port(struct uart_port *uport)
                printk(err);
                return -EBUSY;
        }
-       map_guard = atomic_add_return(1, &duart->map_guard);
-       if (map_guard == 1) {
+       refcount_inc(&duart->map_guard);
+       if (refcount_read(&duart->map_guard) == 1) {
                if (!request_mem_region(duart->mapctrl, DUART_CHANREG_SPACING,
                                        "sb1250-duart")) {
-                       atomic_add(-1, &duart->map_guard);
+                       refcount_dec(&duart->map_guard);
                        printk(err);
                        ret = -EBUSY;
                }
@@ -718,8 +715,7 @@ static int sbd_request_port(struct uart_port *uport)
        if (!ret) {
                ret = sbd_map_port(uport);
                if (ret) {
-                       map_guard = atomic_add_return(-1, &duart->map_guard);
-                       if (!map_guard)
+                       if (refcount_dec_and_test(&duart->map_guard))
                                release_mem_region(duart->mapctrl,
                                                   DUART_CHANREG_SPACING);
                }
index 3fe56894974a7c4c3ae04a4d54946043be1d243c..0f45b7884a2c58a299668591e87bead1f6628f98 100644 (file)
@@ -2117,9 +2117,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
                for (tries = 3; !ops->tx_empty(uport) && tries; tries--)
                        msleep(10);
                if (!tries)
-                       dev_err(uport->dev, "%s%d: Unable to drain transmitter\n",
-                               drv->dev_name,
-                               drv->tty_driver->name_base + uport->line);
+                       dev_err(uport->dev, "%s: Unable to drain transmitter\n",
+                               uport->name);
 
                ops->shutdown(uport);
        }
@@ -2248,11 +2247,10 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port)
                break;
        }
 
-       printk(KERN_INFO "%s%s%s%d at %s (irq = %d, base_baud = %d) is a %s\n",
+       pr_info("%s%s%s at %s (irq = %d, base_baud = %d) is a %s\n",
               port->dev ? dev_name(port->dev) : "",
               port->dev ? ": " : "",
-              drv->dev_name,
-              drv->tty_driver->name_base + port->line,
+              port->name,
               address, port->irq, port->uartclk / 16, uart_type(port));
 }
 
@@ -2331,9 +2329,6 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
        int flow = 'n';
        int ret = 0;
 
-       if (!state)
-               return -1;
-
        tport = &state->port;
        mutex_lock(&tport->mutex);
 
@@ -2368,13 +2363,12 @@ static int uart_poll_get_char(struct tty_driver *driver, int line)
        struct uart_port *port;
        int ret = -1;
 
-       if (state) {
-               port = uart_port_ref(state);
-               if (port) {
-                       ret = port->ops->poll_get_char(port);
-                       uart_port_deref(port);
-               }
+       port = uart_port_ref(state);
+       if (port) {
+               ret = port->ops->poll_get_char(port);
+               uart_port_deref(port);
        }
+
        return ret;
 }
 
@@ -2384,9 +2378,6 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch)
        struct uart_state *state = drv->state + line;
        struct uart_port *port;
 
-       if (!state)
-               return;
-
        port = uart_port_ref(state);
        if (!port)
                return;
@@ -2751,6 +2742,12 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
        state->pm_state = UART_PM_STATE_UNDEFINED;
        uport->cons = drv->cons;
        uport->minor = drv->tty_driver->minor_start + uport->line;
+       uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name,
+                               drv->tty_driver->name_base + uport->line);
+       if (!uport->name) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        /*
         * If this port is a console, then the spinlock is already
@@ -2868,6 +2865,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
        if (uport->type != PORT_UNKNOWN && uport->ops->release_port)
                uport->ops->release_port(uport);
        kfree(uport->tty_groups);
+       kfree(uport->name);
 
        /*
         * Indicate that there isn't a port here anymore.
index 9a47cc4f16a2798f7c0a084d44621d371a2f59a9..71707e8e6e3ffe768ad76e52b118b74e32ad35e0 100644 (file)
@@ -683,24 +683,37 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag)
        }
 
        if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+               u16 data = serial_port_in(port, SCPDR);
                u16 ctrl = serial_port_in(port, SCPCR);
 
                /* Enable RXD and TXD pin functions */
                ctrl &= ~(SCPCR_RXDC | SCPCR_TXDC);
                if (to_sci_port(port)->has_rtscts) {
-                       /* RTS# is output, driven 1 */
-                       ctrl |= SCPCR_RTSC;
-                       serial_port_out(port, SCPDR,
-                               serial_port_in(port, SCPDR) | SCPDR_RTSD);
+                       /* RTS# is output, active low, unless autorts */
+                       if (!(port->mctrl & TIOCM_RTS)) {
+                               ctrl |= SCPCR_RTSC;
+                               data |= SCPDR_RTSD;
+                       } else if (!s->autorts) {
+                               ctrl |= SCPCR_RTSC;
+                               data &= ~SCPDR_RTSD;
+                       } else {
+                               /* Enable RTS# pin function */
+                               ctrl &= ~SCPCR_RTSC;
+                       }
                        /* Enable CTS# pin function */
                        ctrl &= ~SCPCR_CTSC;
                }
+               serial_port_out(port, SCPDR, data);
                serial_port_out(port, SCPCR, ctrl);
        } else if (sci_getreg(port, SCSPTR)->size) {
                u16 status = serial_port_in(port, SCSPTR);
 
-               /* RTS# is output, driven 1 */
-               status |= SCSPTR_RTSIO | SCSPTR_RTSDT;
+               /* RTS# is always output; and active low, unless autorts */
+               status |= SCSPTR_RTSIO;
+               if (!(port->mctrl & TIOCM_RTS))
+                       status |= SCSPTR_RTSDT;
+               else if (!s->autorts)
+                       status &= ~SCSPTR_RTSDT;
                /* CTS# and SCK are inputs */
                status &= ~(SCSPTR_CTSIO | SCSPTR_SCKIO);
                serial_port_out(port, SCSPTR, status);
@@ -1985,11 +1998,13 @@ static int sci_startup(struct uart_port *port)
 
        dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
 
+       sci_request_dma(port);
+
        ret = sci_request_irq(s);
-       if (unlikely(ret < 0))
+       if (unlikely(ret < 0)) {
+               sci_free_dma(port);
                return ret;
-
-       sci_request_dma(port);
+       }
 
        return 0;
 }
@@ -2021,8 +2036,8 @@ static void sci_shutdown(struct uart_port *port)
        }
 #endif
 
-       sci_free_dma(port);
        sci_free_irq(s);
+       sci_free_dma(port);
 }
 
 static int sci_sck_calc(struct sci_port *s, unsigned int bps,
@@ -2157,10 +2172,6 @@ static void sci_reset(struct uart_port *port)
        unsigned int status;
        struct sci_port *s = to_sci_port(port);
 
-       do {
-               status = serial_port_in(port, SCxSR);
-       } while (!(status & SCxSR_TEND(port)));
-
        serial_port_out(port, SCSCR, 0x00);     /* TE=0, RE=0, CKE1=0 */
 
        reg = sci_getreg(port, SCFCR);
@@ -2374,6 +2385,10 @@ done:
 
                serial_port_out(port, SCFCR, ctrl);
        }
+       if (port->flags & UPF_HARD_FLOW) {
+               /* Refresh (Auto) RTS */
+               sci_set_mctrl(port, port->mctrl);
+       }
 
        scr_val |= SCSCR_RE | SCSCR_TE |
                   (s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0));
index d98e3dc4838e637b34d3e3984c9570c656fa37b3..90996ad97b3718cfa85a0505a47b7e8d7be770c9 100644 (file)
@@ -36,7 +36,7 @@
 #define SPRD_FIFO_SIZE         128
 #define SPRD_DEF_RATE          26000000
 #define SPRD_BAUD_IO_LIMIT     3000000
-#define SPRD_TIMEOUT           256
+#define SPRD_TIMEOUT           256000
 
 /* the offset of serial registers and BITs for them */
 /* data registers */
index a93a3167a9c6f913d434462468a2327a25255d93..f5335be344f67ced2a601c81e3713a7d90e275c7 100644 (file)
@@ -887,13 +887,12 @@ static void asc_console_write(struct console *co, const char *s, unsigned count)
        int locked = 1;
        u32 intenable;
 
-       local_irq_save(flags);
        if (port->sysrq)
                locked = 0; /* asc_interrupt has already claimed the lock */
        else if (oops_in_progress)
-               locked = spin_trylock(&port->lock);
+               locked = spin_trylock_irqsave(&port->lock, flags);
        else
-               spin_lock(&port->lock);
+               spin_lock_irqsave(&port->lock, flags);
 
        /*
         * Disable interrupts so we don't get the IRQ line bouncing
@@ -911,14 +910,13 @@ static void asc_console_write(struct console *co, const char *s, unsigned count)
        asc_out(port, ASC_INTEN, intenable);
 
        if (locked)
-               spin_unlock(&port->lock);
-       local_irq_restore(flags);
+               spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static int asc_console_setup(struct console *co, char *options)
 {
        struct asc_port *ascport;
-       int baud = 9600;
+       int baud = 115200;
        int bits = 8;
        int parity = 'n';
        int flow = 'n';
index 817bb0d3f326ed982631c135ddd5f7a4b170b7a5..c9b8d702dadc35abcb6f3b74a07f62e836b2f9e2 100644 (file)
@@ -28,7 +28,7 @@
 #define ULITE_NAME             "ttyUL"
 #define ULITE_MAJOR            204
 #define ULITE_MINOR            187
-#define ULITE_NR_UARTS         16
+#define ULITE_NR_UARTS         CONFIG_SERIAL_UARTLITE_NR_UARTS
 
 /* ---------------------------------------------------------------------
  * Register definitions
index ad77d0ed0c467dbc2b20f41a9959ed10cf3e8390..c0539950f8d7f29725dd4433e6905d5e3d07b1c7 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/module.h>
+#include <linux/pm_runtime.h>
 
 #define CDNS_UART_TTY_NAME     "ttyPS"
 #define CDNS_UART_NAME         "xuartps"
@@ -176,6 +177,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
 #define CDNS_UART_BDIV_MIN     4
 #define CDNS_UART_BDIV_MAX     255
 #define CDNS_UART_CD_MAX       65535
+#define UART_AUTOSUSPEND_TIMEOUT       3000
 
 /**
  * struct cdns_uart - device data
@@ -1065,16 +1067,13 @@ static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c)
 static void cdns_uart_pm(struct uart_port *port, unsigned int state,
                   unsigned int oldstate)
 {
-       struct cdns_uart *cdns_uart = port->private_data;
-
        switch (state) {
        case UART_PM_STATE_OFF:
-               clk_disable(cdns_uart->uartclk);
-               clk_disable(cdns_uart->pclk);
+               pm_runtime_mark_last_busy(port->dev);
+               pm_runtime_put_autosuspend(port->dev);
                break;
        default:
-               clk_enable(cdns_uart->pclk);
-               clk_enable(cdns_uart->uartclk);
+               pm_runtime_get_sync(port->dev);
                break;
        }
 }
@@ -1353,12 +1352,7 @@ static int cdns_uart_suspend(struct device *device)
         * the suspend.
         */
        uart_suspend_port(&cdns_uart_uart_driver, port);
-       if (console_suspend_enabled && !may_wake) {
-               struct cdns_uart *cdns_uart = port->private_data;
-
-               clk_disable(cdns_uart->uartclk);
-               clk_disable(cdns_uart->pclk);
-       } else {
+       if (!(console_suspend_enabled && !may_wake)) {
                unsigned long flags = 0;
 
                spin_lock_irqsave(&port->lock, flags);
@@ -1423,6 +1417,8 @@ static int cdns_uart_resume(struct device *device)
                ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN;
                writel(ctrl_reg, port->membase + CDNS_UART_CR);
 
+               clk_disable(cdns_uart->uartclk);
+               clk_disable(cdns_uart->pclk);
                spin_unlock_irqrestore(&port->lock, flags);
        } else {
                spin_lock_irqsave(&port->lock, flags);
@@ -1436,9 +1432,33 @@ static int cdns_uart_resume(struct device *device)
        return uart_resume_port(&cdns_uart_uart_driver, port);
 }
 #endif /* ! CONFIG_PM_SLEEP */
+static int __maybe_unused cdns_runtime_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct uart_port *port = platform_get_drvdata(pdev);
+       struct cdns_uart *cdns_uart = port->private_data;
+
+       clk_disable(cdns_uart->uartclk);
+       clk_disable(cdns_uart->pclk);
+       return 0;
+};
+
+static int __maybe_unused cdns_runtime_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct uart_port *port = platform_get_drvdata(pdev);
+       struct cdns_uart *cdns_uart = port->private_data;
 
-static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend,
-               cdns_uart_resume);
+       clk_enable(cdns_uart->pclk);
+       clk_enable(cdns_uart->uartclk);
+       return 0;
+};
+
+static const struct dev_pm_ops cdns_uart_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(cdns_uart_suspend, cdns_uart_resume)
+       SET_RUNTIME_PM_OPS(cdns_runtime_suspend,
+                          cdns_runtime_resume, NULL)
+};
 
 static const struct cdns_platform_data zynqmp_uart_def = {
                                .quirks = CDNS_UART_RXBS_SUPPORT, };
@@ -1501,12 +1521,12 @@ static int cdns_uart_probe(struct platform_device *pdev)
                return PTR_ERR(cdns_uart_data->uartclk);
        }
 
-       rc = clk_prepare(cdns_uart_data->pclk);
+       rc = clk_prepare_enable(cdns_uart_data->pclk);
        if (rc) {
                dev_err(&pdev->dev, "Unable to enable pclk clock.\n");
                return rc;
        }
-       rc = clk_prepare(cdns_uart_data->uartclk);
+       rc = clk_prepare_enable(cdns_uart_data->uartclk);
        if (rc) {
                dev_err(&pdev->dev, "Unable to enable device clock.\n");
                goto err_out_clk_dis_pclk;
@@ -1558,6 +1578,11 @@ static int cdns_uart_probe(struct platform_device *pdev)
        cdns_uart_data->port = port;
        platform_set_drvdata(pdev, port);
 
+       pm_runtime_use_autosuspend(&pdev->dev);
+       pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
+       pm_runtime_set_active(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+
        rc = uart_add_one_port(&cdns_uart_uart_driver, port);
        if (rc) {
                dev_err(&pdev->dev,
@@ -1573,9 +1598,12 @@ err_out_notif_unreg:
                        &cdns_uart_data->clk_rate_change_nb);
 #endif
 err_out_clk_disable:
-       clk_unprepare(cdns_uart_data->uartclk);
+       pm_runtime_disable(&pdev->dev);
+       pm_runtime_set_suspended(&pdev->dev);
+       pm_runtime_dont_use_autosuspend(&pdev->dev);
+       clk_disable_unprepare(cdns_uart_data->uartclk);
 err_out_clk_dis_pclk:
-       clk_unprepare(cdns_uart_data->pclk);
+       clk_disable_unprepare(cdns_uart_data->pclk);
 
        return rc;
 }
@@ -1599,8 +1627,11 @@ static int cdns_uart_remove(struct platform_device *pdev)
 #endif
        rc = uart_remove_one_port(&cdns_uart_uart_driver, port);
        port->mapbase = 0;
-       clk_unprepare(cdns_uart_data->uartclk);
-       clk_unprepare(cdns_uart_data->pclk);
+       clk_disable_unprepare(cdns_uart_data->uartclk);
+       clk_disable_unprepare(cdns_uart_data->pclk);
+       pm_runtime_disable(&pdev->dev);
+       pm_runtime_set_suspended(&pdev->dev);
+       pm_runtime_dont_use_autosuspend(&pdev->dev);
        return rc;
 }
 
diff --git a/drivers/tty/tty_baudrate.c b/drivers/tty/tty_baudrate.c
new file mode 100644 (file)
index 0000000..5c33fd2
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/export.h>
+
+
+/*
+ * Routine which returns the baud rate of the tty
+ *
+ * Note that the baud_table needs to be kept in sync with the
+ * include/asm/termbits.h file.
+ */
+static const speed_t baud_table[] = {
+       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+       9600, 19200, 38400, 57600, 115200, 230400, 460800,
+#ifdef __sparc__
+       76800, 153600, 307200, 614400, 921600
+#else
+       500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
+       2500000, 3000000, 3500000, 4000000
+#endif
+};
+
+#ifndef __sparc__
+static const tcflag_t baud_bits[] = {
+       B0, B50, B75, B110, B134, B150, B200, B300, B600,
+       B1200, B1800, B2400, B4800, B9600, B19200, B38400,
+       B57600, B115200, B230400, B460800, B500000, B576000,
+       B921600, B1000000, B1152000, B1500000, B2000000, B2500000,
+       B3000000, B3500000, B4000000
+};
+#else
+static const tcflag_t baud_bits[] = {
+       B0, B50, B75, B110, B134, B150, B200, B300, B600,
+       B1200, B1800, B2400, B4800, B9600, B19200, B38400,
+       B57600, B115200, B230400, B460800, B76800, B153600,
+       B307200, B614400, B921600
+};
+#endif
+
+static int n_baud_table = ARRAY_SIZE(baud_table);
+
+/**
+ *     tty_termios_baud_rate
+ *     @termios: termios structure
+ *
+ *     Convert termios baud rate data into a speed. This should be called
+ *     with the termios lock held if this termios is a terminal termios
+ *     structure. May change the termios data. Device drivers can call this
+ *     function but should use ->c_[io]speed directly as they are updated.
+ *
+ *     Locking: none
+ */
+
+speed_t tty_termios_baud_rate(struct ktermios *termios)
+{
+       unsigned int cbaud;
+
+       cbaud = termios->c_cflag & CBAUD;
+
+#ifdef BOTHER
+       /* Magic token for arbitrary speed via c_ispeed/c_ospeed */
+       if (cbaud == BOTHER)
+               return termios->c_ospeed;
+#endif
+       if (cbaud & CBAUDEX) {
+               cbaud &= ~CBAUDEX;
+
+               if (cbaud < 1 || cbaud + 15 > n_baud_table)
+                       termios->c_cflag &= ~CBAUDEX;
+               else
+                       cbaud += 15;
+       }
+       return baud_table[cbaud];
+}
+EXPORT_SYMBOL(tty_termios_baud_rate);
+
+/**
+ *     tty_termios_input_baud_rate
+ *     @termios: termios structure
+ *
+ *     Convert termios baud rate data into a speed. This should be called
+ *     with the termios lock held if this termios is a terminal termios
+ *     structure. May change the termios data. Device drivers can call this
+ *     function but should use ->c_[io]speed directly as they are updated.
+ *
+ *     Locking: none
+ */
+
+speed_t tty_termios_input_baud_rate(struct ktermios *termios)
+{
+#ifdef IBSHIFT
+       unsigned int cbaud = (termios->c_cflag >> IBSHIFT) & CBAUD;
+
+       if (cbaud == B0)
+               return tty_termios_baud_rate(termios);
+
+       /* Magic token for arbitrary speed via c_ispeed*/
+       if (cbaud == BOTHER)
+               return termios->c_ispeed;
+
+       if (cbaud & CBAUDEX) {
+               cbaud &= ~CBAUDEX;
+
+               if (cbaud < 1 || cbaud + 15 > n_baud_table)
+                       termios->c_cflag &= ~(CBAUDEX << IBSHIFT);
+               else
+                       cbaud += 15;
+       }
+       return baud_table[cbaud];
+#else
+       return tty_termios_baud_rate(termios);
+#endif
+}
+EXPORT_SYMBOL(tty_termios_input_baud_rate);
+
+/**
+ *     tty_termios_encode_baud_rate
+ *     @termios: ktermios structure holding user requested state
+ *     @ispeed: input speed
+ *     @ospeed: output speed
+ *
+ *     Encode the speeds set into the passed termios structure. This is
+ *     used as a library helper for drivers so that they can report back
+ *     the actual speed selected when it differs from the speed requested
+ *
+ *     For maximal back compatibility with legacy SYS5/POSIX *nix behaviour
+ *     we need to carefully set the bits when the user does not get the
+ *     desired speed. We allow small margins and preserve as much of possible
+ *     of the input intent to keep compatibility.
+ *
+ *     Locking: Caller should hold termios lock. This is already held
+ *     when calling this function from the driver termios handler.
+ *
+ *     The ifdefs deal with platforms whose owners have yet to update them
+ *     and will all go away once this is done.
+ */
+
+void tty_termios_encode_baud_rate(struct ktermios *termios,
+                                 speed_t ibaud, speed_t obaud)
+{
+       int i = 0;
+       int ifound = -1, ofound = -1;
+       int iclose = ibaud/50, oclose = obaud/50;
+       int ibinput = 0;
+
+       if (obaud == 0)                 /* CD dropped             */
+               ibaud = 0;              /* Clear ibaud to be sure */
+
+       termios->c_ispeed = ibaud;
+       termios->c_ospeed = obaud;
+
+#ifdef BOTHER
+       /* If the user asked for a precise weird speed give a precise weird
+          answer. If they asked for a Bfoo speed they may have problems
+          digesting non-exact replies so fuzz a bit */
+
+       if ((termios->c_cflag & CBAUD) == BOTHER)
+               oclose = 0;
+       if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER)
+               iclose = 0;
+       if ((termios->c_cflag >> IBSHIFT) & CBAUD)
+               ibinput = 1;    /* An input speed was specified */
+#endif
+       termios->c_cflag &= ~CBAUD;
+
+       /*
+        *      Our goal is to find a close match to the standard baud rate
+        *      returned. Walk the baud rate table and if we get a very close
+        *      match then report back the speed as a POSIX Bxxxx value by
+        *      preference
+        */
+
+       do {
+               if (obaud - oclose <= baud_table[i] &&
+                   obaud + oclose >= baud_table[i]) {
+                       termios->c_cflag |= baud_bits[i];
+                       ofound = i;
+               }
+               if (ibaud - iclose <= baud_table[i] &&
+                   ibaud + iclose >= baud_table[i]) {
+                       /* For the case input == output don't set IBAUD bits
+                          if the user didn't do so */
+                       if (ofound == i && !ibinput)
+                               ifound  = i;
+#ifdef IBSHIFT
+                       else {
+                               ifound = i;
+                               termios->c_cflag |= (baud_bits[i] << IBSHIFT);
+                       }
+#endif
+               }
+       } while (++i < n_baud_table);
+
+       /*
+        *      If we found no match then use BOTHER if provided or warn
+        *      the user their platform maintainer needs to wake up if not.
+        */
+#ifdef BOTHER
+       if (ofound == -1)
+               termios->c_cflag |= BOTHER;
+       /* Set exact input bits only if the input and output differ or the
+          user already did */
+       if (ifound == -1 && (ibaud != obaud || ibinput))
+               termios->c_cflag |= (BOTHER << IBSHIFT);
+#else
+       if (ifound == -1 || ofound == -1)
+               pr_warn_once("tty: Unable to return correct speed data as your architecture needs updating.\n");
+#endif
+}
+EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate);
+
+/**
+ *     tty_encode_baud_rate            -       set baud rate of the tty
+ *     @ibaud: input baud rate
+ *     @obad: output baud rate
+ *
+ *     Update the current termios data for the tty with the new speed
+ *     settings. The caller must hold the termios_rwsem for the tty in
+ *     question.
+ */
+
+void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud)
+{
+       tty_termios_encode_baud_rate(&tty->termios, ibaud, obaud);
+}
+EXPORT_SYMBOL_GPL(tty_encode_baud_rate);
index 309d25065bb69da0dda1820458b1b98fc1cd6c7c..0c150b5a9dd68d7bba0e91a50c45ecd5df831cac 100644 (file)
@@ -377,65 +377,6 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
 EXPORT_SYMBOL_GPL(tty_find_polling_driver);
 #endif
 
-static int is_ignored(int sig)
-{
-       return (sigismember(&current->blocked, sig) ||
-               current->sighand->action[sig-1].sa.sa_handler == SIG_IGN);
-}
-
-/**
- *     tty_check_change        -       check for POSIX terminal changes
- *     @tty: tty to check
- *
- *     If we try to write to, or set the state of, a terminal and we're
- *     not in the foreground, send a SIGTTOU.  If the signal is blocked or
- *     ignored, go ahead and perform the operation.  (POSIX 7.2)
- *
- *     Locking: ctrl_lock
- */
-
-int __tty_check_change(struct tty_struct *tty, int sig)
-{
-       unsigned long flags;
-       struct pid *pgrp, *tty_pgrp;
-       int ret = 0;
-
-       if (current->signal->tty != tty)
-               return 0;
-
-       rcu_read_lock();
-       pgrp = task_pgrp(current);
-
-       spin_lock_irqsave(&tty->ctrl_lock, flags);
-       tty_pgrp = tty->pgrp;
-       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-
-       if (tty_pgrp && pgrp != tty->pgrp) {
-               if (is_ignored(sig)) {
-                       if (sig == SIGTTIN)
-                               ret = -EIO;
-               } else if (is_current_pgrp_orphaned())
-                       ret = -EIO;
-               else {
-                       kill_pgrp(pgrp, sig, 1);
-                       set_thread_flag(TIF_SIGPENDING);
-                       ret = -ERESTARTSYS;
-               }
-       }
-       rcu_read_unlock();
-
-       if (!tty_pgrp)
-               tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig);
-
-       return ret;
-}
-
-int tty_check_change(struct tty_struct *tty)
-{
-       return __tty_check_change(tty, SIGTTOU);
-}
-EXPORT_SYMBOL(tty_check_change);
-
 static ssize_t hung_up_tty_read(struct file *file, char __user *buf,
                                size_t count, loff_t *ppos)
 {
@@ -509,79 +450,6 @@ static const struct file_operations hung_up_tty_fops = {
 static DEFINE_SPINLOCK(redirect_lock);
 static struct file *redirect;
 
-
-void proc_clear_tty(struct task_struct *p)
-{
-       unsigned long flags;
-       struct tty_struct *tty;
-       spin_lock_irqsave(&p->sighand->siglock, flags);
-       tty = p->signal->tty;
-       p->signal->tty = NULL;
-       spin_unlock_irqrestore(&p->sighand->siglock, flags);
-       tty_kref_put(tty);
-}
-
-/**
- * proc_set_tty -  set the controlling terminal
- *
- * Only callable by the session leader and only if it does not already have
- * a controlling terminal.
- *
- * Caller must hold:  tty_lock()
- *                   a readlock on tasklist_lock
- *                   sighand lock
- */
-static void __proc_set_tty(struct tty_struct *tty)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&tty->ctrl_lock, flags);
-       /*
-        * The session and fg pgrp references will be non-NULL if
-        * tiocsctty() is stealing the controlling tty
-        */
-       put_pid(tty->session);
-       put_pid(tty->pgrp);
-       tty->pgrp = get_pid(task_pgrp(current));
-       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-       tty->session = get_pid(task_session(current));
-       if (current->signal->tty) {
-               tty_debug(tty, "current tty %s not NULL!!\n",
-                         current->signal->tty->name);
-               tty_kref_put(current->signal->tty);
-       }
-       put_pid(current->signal->tty_old_pgrp);
-       current->signal->tty = tty_kref_get(tty);
-       current->signal->tty_old_pgrp = NULL;
-}
-
-static void proc_set_tty(struct tty_struct *tty)
-{
-       spin_lock_irq(&current->sighand->siglock);
-       __proc_set_tty(tty);
-       spin_unlock_irq(&current->sighand->siglock);
-}
-
-struct tty_struct *get_current_tty(void)
-{
-       struct tty_struct *tty;
-       unsigned long flags;
-
-       spin_lock_irqsave(&current->sighand->siglock, flags);
-       tty = tty_kref_get(current->signal->tty);
-       spin_unlock_irqrestore(&current->sighand->siglock, flags);
-       return tty;
-}
-EXPORT_SYMBOL_GPL(get_current_tty);
-
-static void session_clear_tty(struct pid *session)
-{
-       struct task_struct *p;
-       do_each_pid_task(session, PIDTYPE_SID, p) {
-               proc_clear_tty(p);
-       } while_each_pid_task(session, PIDTYPE_SID, p);
-}
-
 /**
  *     tty_wakeup      -       request more data
  *     @tty: terminal
@@ -608,60 +476,6 @@ void tty_wakeup(struct tty_struct *tty)
 
 EXPORT_SYMBOL_GPL(tty_wakeup);
 
-/**
- *     tty_signal_session_leader       - sends SIGHUP to session leader
- *     @tty            controlling tty
- *     @exit_session   if non-zero, signal all foreground group processes
- *
- *     Send SIGHUP and SIGCONT to the session leader and its process group.
- *     Optionally, signal all processes in the foreground process group.
- *
- *     Returns the number of processes in the session with this tty
- *     as their controlling terminal. This value is used to drop
- *     tty references for those processes.
- */
-static int tty_signal_session_leader(struct tty_struct *tty, int exit_session)
-{
-       struct task_struct *p;
-       int refs = 0;
-       struct pid *tty_pgrp = NULL;
-
-       read_lock(&tasklist_lock);
-       if (tty->session) {
-               do_each_pid_task(tty->session, PIDTYPE_SID, p) {
-                       spin_lock_irq(&p->sighand->siglock);
-                       if (p->signal->tty == tty) {
-                               p->signal->tty = NULL;
-                               /* We defer the dereferences outside fo
-                                  the tasklist lock */
-                               refs++;
-                       }
-                       if (!p->signal->leader) {
-                               spin_unlock_irq(&p->sighand->siglock);
-                               continue;
-                       }
-                       __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
-                       __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
-                       put_pid(p->signal->tty_old_pgrp);  /* A noop */
-                       spin_lock(&tty->ctrl_lock);
-                       tty_pgrp = get_pid(tty->pgrp);
-                       if (tty->pgrp)
-                               p->signal->tty_old_pgrp = get_pid(tty->pgrp);
-                       spin_unlock(&tty->ctrl_lock);
-                       spin_unlock_irq(&p->sighand->siglock);
-               } while_each_pid_task(tty->session, PIDTYPE_SID, p);
-       }
-       read_unlock(&tasklist_lock);
-
-       if (tty_pgrp) {
-               if (exit_session)
-                       kill_pgrp(tty_pgrp, SIGHUP, exit_session);
-               put_pid(tty_pgrp);
-       }
-
-       return refs;
-}
-
 /**
  *     __tty_hangup            -       actual handler for hangup events
  *     @work: tty device
@@ -840,7 +654,7 @@ void tty_vhangup_self(void)
  *     is complete. That guarantee is necessary for security reasons.
  */
 
-static void tty_vhangup_session(struct tty_struct *tty)
+void tty_vhangup_session(struct tty_struct *tty)
 {
        tty_debug_hangup(tty, "session hangup\n");
        __tty_hangup(tty, 1);
@@ -861,106 +675,6 @@ int tty_hung_up_p(struct file *filp)
 
 EXPORT_SYMBOL(tty_hung_up_p);
 
-/**
- *     disassociate_ctty       -       disconnect controlling tty
- *     @on_exit: true if exiting so need to "hang up" the session
- *
- *     This function is typically called only by the session leader, when
- *     it wants to disassociate itself from its controlling tty.
- *
- *     It performs the following functions:
- *     (1)  Sends a SIGHUP and SIGCONT to the foreground process group
- *     (2)  Clears the tty from being controlling the session
- *     (3)  Clears the controlling tty for all processes in the
- *             session group.
- *
- *     The argument on_exit is set to 1 if called when a process is
- *     exiting; it is 0 if called by the ioctl TIOCNOTTY.
- *
- *     Locking:
- *             BTM is taken for hysterical raisins, and held when
- *               called from no_tty().
- *               tty_mutex is taken to protect tty
- *               ->siglock is taken to protect ->signal/->sighand
- *               tasklist_lock is taken to walk process list for sessions
- *                 ->siglock is taken to protect ->signal/->sighand
- */
-
-void disassociate_ctty(int on_exit)
-{
-       struct tty_struct *tty;
-
-       if (!current->signal->leader)
-               return;
-
-       tty = get_current_tty();
-       if (tty) {
-               if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) {
-                       tty_vhangup_session(tty);
-               } else {
-                       struct pid *tty_pgrp = tty_get_pgrp(tty);
-                       if (tty_pgrp) {
-                               kill_pgrp(tty_pgrp, SIGHUP, on_exit);
-                               if (!on_exit)
-                                       kill_pgrp(tty_pgrp, SIGCONT, on_exit);
-                               put_pid(tty_pgrp);
-                       }
-               }
-               tty_kref_put(tty);
-
-       } else if (on_exit) {
-               struct pid *old_pgrp;
-               spin_lock_irq(&current->sighand->siglock);
-               old_pgrp = current->signal->tty_old_pgrp;
-               current->signal->tty_old_pgrp = NULL;
-               spin_unlock_irq(&current->sighand->siglock);
-               if (old_pgrp) {
-                       kill_pgrp(old_pgrp, SIGHUP, on_exit);
-                       kill_pgrp(old_pgrp, SIGCONT, on_exit);
-                       put_pid(old_pgrp);
-               }
-               return;
-       }
-
-       spin_lock_irq(&current->sighand->siglock);
-       put_pid(current->signal->tty_old_pgrp);
-       current->signal->tty_old_pgrp = NULL;
-
-       tty = tty_kref_get(current->signal->tty);
-       if (tty) {
-               unsigned long flags;
-               spin_lock_irqsave(&tty->ctrl_lock, flags);
-               put_pid(tty->session);
-               put_pid(tty->pgrp);
-               tty->session = NULL;
-               tty->pgrp = NULL;
-               spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-               tty_kref_put(tty);
-       } else
-               tty_debug_hangup(tty, "no current tty\n");
-
-       spin_unlock_irq(&current->sighand->siglock);
-       /* Now clear signal->tty under the lock */
-       read_lock(&tasklist_lock);
-       session_clear_tty(task_session(current));
-       read_unlock(&tasklist_lock);
-}
-
-/**
- *
- *     no_tty  - Ensure the current process does not have a controlling tty
- */
-void no_tty(void)
-{
-       /* FIXME: Review locking here. The tty_lock never covered any race
-          between a new association and proc_clear_tty but possible we need
-          to protect against this anyway */
-       struct task_struct *tsk = current;
-       disassociate_ctty(0);
-       proc_clear_tty(tsk);
-}
-
-
 /**
  *     stop_tty        -       propagate flow control
  *     @tty: tty to stop
@@ -1520,7 +1234,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
         * This code guarantees that either everything succeeds and the
         * TTY is ready for operation, or else the table slots are vacated
         * and the allocated memory released.  (Except that the termios
-        * and locked termios may be retained.)
+        * may be retained.)
         */
 
        if (!try_module_get(driver->owner))
@@ -2163,38 +1877,13 @@ retry_open:
        }
        clear_bit(TTY_HUPPED, &tty->flags);
 
-
-       read_lock(&tasklist_lock);
-       spin_lock_irq(&current->sighand->siglock);
        noctty = (filp->f_flags & O_NOCTTY) ||
-                       (IS_ENABLED(CONFIG_VT) && device == MKDEV(TTY_MAJOR, 0)) ||
-                       device == MKDEV(TTYAUX_MAJOR, 1) ||
-                       (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-                        tty->driver->subtype == PTY_TYPE_MASTER);
-
-       if (!noctty &&
-           current->signal->leader &&
-           !current->signal->tty &&
-           tty->session == NULL) {
-               /*
-                * Don't let a process that only has write access to the tty
-                * obtain the privileges associated with having a tty as
-                * controlling terminal (being able to reopen it with full
-                * access through /dev/tty, being able to perform pushback).
-                * Many distributions set the group of all ttys to "tty" and
-                * grant write-only access to all terminals for setgid tty
-                * binaries, which should not imply full privileges on all ttys.
-                *
-                * This could theoretically break old code that performs open()
-                * on a write-only file descriptor. In that case, it might be
-                * necessary to also permit this if
-                * inode_permission(inode, MAY_READ) == 0.
-                */
-               if (filp->f_mode & FMODE_READ)
-                       __proc_set_tty(tty);
-       }
-       spin_unlock_irq(&current->sighand->siglock);
-       read_unlock(&tasklist_lock);
+                (IS_ENABLED(CONFIG_VT) && device == MKDEV(TTY_MAJOR, 0)) ||
+                device == MKDEV(TTYAUX_MAJOR, 1) ||
+                (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+                 tty->driver->subtype == PTY_TYPE_MASTER);
+       if (!noctty)
+               tty_open_proc_set_tty(filp, tty);
        tty_unlock(tty);
        return 0;
 }
@@ -2456,211 +2145,6 @@ static int fionbio(struct file *file, int __user *p)
        return 0;
 }
 
-/**
- *     tiocsctty       -       set controlling tty
- *     @tty: tty structure
- *     @arg: user argument
- *
- *     This ioctl is used to manage job control. It permits a session
- *     leader to set this tty as the controlling tty for the session.
- *
- *     Locking:
- *             Takes tty_lock() to serialize proc_set_tty() for this tty
- *             Takes tasklist_lock internally to walk sessions
- *             Takes ->siglock() when updating signal->tty
- */
-
-static int tiocsctty(struct tty_struct *tty, struct file *file, int arg)
-{
-       int ret = 0;
-
-       tty_lock(tty);
-       read_lock(&tasklist_lock);
-
-       if (current->signal->leader && (task_session(current) == tty->session))
-               goto unlock;
-
-       /*
-        * The process must be a session leader and
-        * not have a controlling tty already.
-        */
-       if (!current->signal->leader || current->signal->tty) {
-               ret = -EPERM;
-               goto unlock;
-       }
-
-       if (tty->session) {
-               /*
-                * This tty is already the controlling
-                * tty for another session group!
-                */
-               if (arg == 1 && capable(CAP_SYS_ADMIN)) {
-                       /*
-                        * Steal it away
-                        */
-                       session_clear_tty(tty->session);
-               } else {
-                       ret = -EPERM;
-                       goto unlock;
-               }
-       }
-
-       /* See the comment in tty_open(). */
-       if ((file->f_mode & FMODE_READ) == 0 && !capable(CAP_SYS_ADMIN)) {
-               ret = -EPERM;
-               goto unlock;
-       }
-
-       proc_set_tty(tty);
-unlock:
-       read_unlock(&tasklist_lock);
-       tty_unlock(tty);
-       return ret;
-}
-
-/**
- *     tty_get_pgrp    -       return a ref counted pgrp pid
- *     @tty: tty to read
- *
- *     Returns a refcounted instance of the pid struct for the process
- *     group controlling the tty.
- */
-
-struct pid *tty_get_pgrp(struct tty_struct *tty)
-{
-       unsigned long flags;
-       struct pid *pgrp;
-
-       spin_lock_irqsave(&tty->ctrl_lock, flags);
-       pgrp = get_pid(tty->pgrp);
-       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-
-       return pgrp;
-}
-EXPORT_SYMBOL_GPL(tty_get_pgrp);
-
-/*
- * This checks not only the pgrp, but falls back on the pid if no
- * satisfactory pgrp is found. I dunno - gdb doesn't work correctly
- * without this...
- *
- * The caller must hold rcu lock or the tasklist lock.
- */
-static struct pid *session_of_pgrp(struct pid *pgrp)
-{
-       struct task_struct *p;
-       struct pid *sid = NULL;
-
-       p = pid_task(pgrp, PIDTYPE_PGID);
-       if (p == NULL)
-               p = pid_task(pgrp, PIDTYPE_PID);
-       if (p != NULL)
-               sid = task_session(p);
-
-       return sid;
-}
-
-/**
- *     tiocgpgrp               -       get process group
- *     @tty: tty passed by user
- *     @real_tty: tty side of the tty passed by the user if a pty else the tty
- *     @p: returned pid
- *
- *     Obtain the process group of the tty. If there is no process group
- *     return an error.
- *
- *     Locking: none. Reference to current->signal->tty is safe.
- */
-
-static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
-{
-       struct pid *pid;
-       int ret;
-       /*
-        * (tty == real_tty) is a cheap way of
-        * testing if the tty is NOT a master pty.
-        */
-       if (tty == real_tty && current->signal->tty != real_tty)
-               return -ENOTTY;
-       pid = tty_get_pgrp(real_tty);
-       ret =  put_user(pid_vnr(pid), p);
-       put_pid(pid);
-       return ret;
-}
-
-/**
- *     tiocspgrp               -       attempt to set process group
- *     @tty: tty passed by user
- *     @real_tty: tty side device matching tty passed by user
- *     @p: pid pointer
- *
- *     Set the process group of the tty to the session passed. Only
- *     permitted where the tty session is our session.
- *
- *     Locking: RCU, ctrl lock
- */
-
-static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
-{
-       struct pid *pgrp;
-       pid_t pgrp_nr;
-       int retval = tty_check_change(real_tty);
-
-       if (retval == -EIO)
-               return -ENOTTY;
-       if (retval)
-               return retval;
-       if (!current->signal->tty ||
-           (current->signal->tty != real_tty) ||
-           (real_tty->session != task_session(current)))
-               return -ENOTTY;
-       if (get_user(pgrp_nr, p))
-               return -EFAULT;
-       if (pgrp_nr < 0)
-               return -EINVAL;
-       rcu_read_lock();
-       pgrp = find_vpid(pgrp_nr);
-       retval = -ESRCH;
-       if (!pgrp)
-               goto out_unlock;
-       retval = -EPERM;
-       if (session_of_pgrp(pgrp) != task_session(current))
-               goto out_unlock;
-       retval = 0;
-       spin_lock_irq(&tty->ctrl_lock);
-       put_pid(real_tty->pgrp);
-       real_tty->pgrp = get_pid(pgrp);
-       spin_unlock_irq(&tty->ctrl_lock);
-out_unlock:
-       rcu_read_unlock();
-       return retval;
-}
-
-/**
- *     tiocgsid                -       get session id
- *     @tty: tty passed by user
- *     @real_tty: tty side of the tty passed by the user if a pty else the tty
- *     @p: pointer to returned session id
- *
- *     Obtain the session id of the tty. If there is no session
- *     return an error.
- *
- *     Locking: none. Reference to current->signal->tty is safe.
- */
-
-static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
-{
-       /*
-        * (tty == real_tty) is a cheap way of
-        * testing if the tty is NOT a master pty.
-       */
-       if (tty == real_tty && current->signal->tty != real_tty)
-               return -ENOTTY;
-       if (!real_tty->session)
-               return -ENOTTY;
-       return put_user(pid_vnr(real_tty->session), p);
-}
-
 /**
  *     tiocsetd        -       set line discipline
  *     @tty: tty device
@@ -2843,8 +2327,8 @@ static void tty_warn_deprecated_flags(struct serial_struct __user *ss)
        flags &= ASYNC_DEPRECATED;
 
        if (flags && __ratelimit(&depr_flags))
-               pr_warning("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
-                               __func__, get_task_comm(comm, current), flags);
+               pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
+                       __func__, get_task_comm(comm, current), flags);
 }
 
 /*
@@ -2920,19 +2404,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                int excl = test_bit(TTY_EXCLUSIVE, &tty->flags);
                return put_user(excl, (int __user *)p);
        }
-       case TIOCNOTTY:
-               if (current->signal->tty != tty)
-                       return -ENOTTY;
-               no_tty();
-               return 0;
-       case TIOCSCTTY:
-               return tiocsctty(real_tty, file, arg);
-       case TIOCGPGRP:
-               return tiocgpgrp(tty, real_tty, p);
-       case TIOCSPGRP:
-               return tiocspgrp(tty, real_tty, p);
-       case TIOCGSID:
-               return tiocgsid(tty, real_tty, p);
        case TIOCGETD:
                return tiocgetd(tty, p);
        case TIOCSETD:
@@ -2993,6 +2464,10 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case TIOCSSERIAL:
                tty_warn_deprecated_flags(p);
                break;
+       default:
+               retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg);
+               if (retval != -ENOIOCTLCMD)
+                       return retval;
        }
        if (tty->ops->ioctl) {
                retval = tty->ops->ioctl(tty, cmd, arg);
@@ -3293,9 +2768,9 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
 {
        char name[64];
        dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
-       struct device *dev = NULL;
-       int retval = -ENODEV;
-       bool cdev = false;
+       struct ktermios *tp;
+       struct device *dev;
+       int retval;
 
        if (index >= driver->num) {
                pr_err("%s: Attempt to register invalid tty line number (%d)\n",
@@ -3308,18 +2783,9 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
        else
                tty_line_name(driver, index, name);
 
-       if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
-               retval = tty_cdev_add(driver, devt, index, 1);
-               if (retval)
-                       goto error;
-               cdev = true;
-       }
-
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (!dev) {
-               retval = -ENOMEM;
-               goto error;
-       }
+       if (!dev)
+               return ERR_PTR(-ENOMEM);
 
        dev->devt = devt;
        dev->class = tty_class;
@@ -3329,18 +2795,38 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
        dev->groups = attr_grp;
        dev_set_drvdata(dev, drvdata);
 
+       dev_set_uevent_suppress(dev, 1);
+
        retval = device_register(dev);
        if (retval)
-               goto error;
+               goto err_put;
+
+       if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
+               /*
+                * Free any saved termios data so that the termios state is
+                * reset when reusing a minor number.
+                */
+               tp = driver->termios[index];
+               if (tp) {
+                       driver->termios[index] = NULL;
+                       kfree(tp);
+               }
+
+               retval = tty_cdev_add(driver, devt, index, 1);
+               if (retval)
+                       goto err_del;
+       }
+
+       dev_set_uevent_suppress(dev, 0);
+       kobject_uevent(&dev->kobj, KOBJ_ADD);
 
        return dev;
 
-error:
+err_del:
+       device_del(dev);
+err_put:
        put_device(dev);
-       if (cdev) {
-               cdev_del(driver->cdevs[index]);
-               driver->cdevs[index] = NULL;
-       }
+
        return ERR_PTR(retval);
 }
 EXPORT_SYMBOL_GPL(tty_register_device_attr);
@@ -3441,11 +2927,6 @@ static void destruct_tty_driver(struct kref *kref)
        struct ktermios *tp;
 
        if (driver->flags & TTY_DRIVER_INSTALLED) {
-               /*
-                * Free the termios and termios_locked structures because
-                * we don't want to get memory leaks when modular tty
-                * drivers are removed from the kernel.
-                */
                for (i = 0; i < driver->num; i++) {
                        tp = driver->termios[i];
                        if (tp) {
@@ -3578,30 +3059,6 @@ void tty_default_fops(struct file_operations *fops)
        *fops = tty_fops;
 }
 
-/*
- * Initialize the console device. This is called *early*, so
- * we can't necessarily depend on lots of kernel help here.
- * Just do some early initializations, and do the complex setup
- * later.
- */
-void __init console_init(void)
-{
-       initcall_t *call;
-
-       /* Setup the default TTY line discipline. */
-       n_tty_init();
-
-       /*
-        * set up the console device so that later boot sequences can
-        * inform about problems etc..
-        */
-       call = __con_initcall_start;
-       while (call < __con_initcall_end) {
-               (*call)();
-               call++;
-       }
-}
-
 static char *tty_devnode(struct device *dev, umode_t *mode)
 {
        if (!mode)
index a9a978731c5b0d66f3a96b8cb076ef84ac7b3b0d..efa96e6c4c1b44f1e37a8fa94c8a4caa52e9a688 100644 (file)
@@ -258,228 +258,6 @@ static void unset_locked_termios(struct tty_struct *tty, struct ktermios *old)
        /* FIXME: What should we do for i/ospeed */
 }
 
-/*
- * Routine which returns the baud rate of the tty
- *
- * Note that the baud_table needs to be kept in sync with the
- * include/asm/termbits.h file.
- */
-static const speed_t baud_table[] = {
-       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-       9600, 19200, 38400, 57600, 115200, 230400, 460800,
-#ifdef __sparc__
-       76800, 153600, 307200, 614400, 921600
-#else
-       500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
-       2500000, 3000000, 3500000, 4000000
-#endif
-};
-
-#ifndef __sparc__
-static const tcflag_t baud_bits[] = {
-       B0, B50, B75, B110, B134, B150, B200, B300, B600,
-       B1200, B1800, B2400, B4800, B9600, B19200, B38400,
-       B57600, B115200, B230400, B460800, B500000, B576000,
-       B921600, B1000000, B1152000, B1500000, B2000000, B2500000,
-       B3000000, B3500000, B4000000
-};
-#else
-static const tcflag_t baud_bits[] = {
-       B0, B50, B75, B110, B134, B150, B200, B300, B600,
-       B1200, B1800, B2400, B4800, B9600, B19200, B38400,
-       B57600, B115200, B230400, B460800, B76800, B153600,
-       B307200, B614400, B921600
-};
-#endif
-
-static int n_baud_table = ARRAY_SIZE(baud_table);
-
-/**
- *     tty_termios_baud_rate
- *     @termios: termios structure
- *
- *     Convert termios baud rate data into a speed. This should be called
- *     with the termios lock held if this termios is a terminal termios
- *     structure. May change the termios data. Device drivers can call this
- *     function but should use ->c_[io]speed directly as they are updated.
- *
- *     Locking: none
- */
-
-speed_t tty_termios_baud_rate(struct ktermios *termios)
-{
-       unsigned int cbaud;
-
-       cbaud = termios->c_cflag & CBAUD;
-
-#ifdef BOTHER
-       /* Magic token for arbitrary speed via c_ispeed/c_ospeed */
-       if (cbaud == BOTHER)
-               return termios->c_ospeed;
-#endif
-       if (cbaud & CBAUDEX) {
-               cbaud &= ~CBAUDEX;
-
-               if (cbaud < 1 || cbaud + 15 > n_baud_table)
-                       termios->c_cflag &= ~CBAUDEX;
-               else
-                       cbaud += 15;
-       }
-       return baud_table[cbaud];
-}
-EXPORT_SYMBOL(tty_termios_baud_rate);
-
-/**
- *     tty_termios_input_baud_rate
- *     @termios: termios structure
- *
- *     Convert termios baud rate data into a speed. This should be called
- *     with the termios lock held if this termios is a terminal termios
- *     structure. May change the termios data. Device drivers can call this
- *     function but should use ->c_[io]speed directly as they are updated.
- *
- *     Locking: none
- */
-
-speed_t tty_termios_input_baud_rate(struct ktermios *termios)
-{
-#ifdef IBSHIFT
-       unsigned int cbaud = (termios->c_cflag >> IBSHIFT) & CBAUD;
-
-       if (cbaud == B0)
-               return tty_termios_baud_rate(termios);
-
-       /* Magic token for arbitrary speed via c_ispeed*/
-       if (cbaud == BOTHER)
-               return termios->c_ispeed;
-
-       if (cbaud & CBAUDEX) {
-               cbaud &= ~CBAUDEX;
-
-               if (cbaud < 1 || cbaud + 15 > n_baud_table)
-                       termios->c_cflag &= ~(CBAUDEX << IBSHIFT);
-               else
-                       cbaud += 15;
-       }
-       return baud_table[cbaud];
-#else
-       return tty_termios_baud_rate(termios);
-#endif
-}
-EXPORT_SYMBOL(tty_termios_input_baud_rate);
-
-/**
- *     tty_termios_encode_baud_rate
- *     @termios: ktermios structure holding user requested state
- *     @ispeed: input speed
- *     @ospeed: output speed
- *
- *     Encode the speeds set into the passed termios structure. This is
- *     used as a library helper for drivers so that they can report back
- *     the actual speed selected when it differs from the speed requested
- *
- *     For maximal back compatibility with legacy SYS5/POSIX *nix behaviour
- *     we need to carefully set the bits when the user does not get the
- *     desired speed. We allow small margins and preserve as much of possible
- *     of the input intent to keep compatibility.
- *
- *     Locking: Caller should hold termios lock. This is already held
- *     when calling this function from the driver termios handler.
- *
- *     The ifdefs deal with platforms whose owners have yet to update them
- *     and will all go away once this is done.
- */
-
-void tty_termios_encode_baud_rate(struct ktermios *termios,
-                                 speed_t ibaud, speed_t obaud)
-{
-       int i = 0;
-       int ifound = -1, ofound = -1;
-       int iclose = ibaud/50, oclose = obaud/50;
-       int ibinput = 0;
-
-       if (obaud == 0)                 /* CD dropped             */
-               ibaud = 0;              /* Clear ibaud to be sure */
-
-       termios->c_ispeed = ibaud;
-       termios->c_ospeed = obaud;
-
-#ifdef BOTHER
-       /* If the user asked for a precise weird speed give a precise weird
-          answer. If they asked for a Bfoo speed they may have problems
-          digesting non-exact replies so fuzz a bit */
-
-       if ((termios->c_cflag & CBAUD) == BOTHER)
-               oclose = 0;
-       if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER)
-               iclose = 0;
-       if ((termios->c_cflag >> IBSHIFT) & CBAUD)
-               ibinput = 1;    /* An input speed was specified */
-#endif
-       termios->c_cflag &= ~CBAUD;
-
-       /*
-        *      Our goal is to find a close match to the standard baud rate
-        *      returned. Walk the baud rate table and if we get a very close
-        *      match then report back the speed as a POSIX Bxxxx value by
-        *      preference
-        */
-
-       do {
-               if (obaud - oclose <= baud_table[i] &&
-                   obaud + oclose >= baud_table[i]) {
-                       termios->c_cflag |= baud_bits[i];
-                       ofound = i;
-               }
-               if (ibaud - iclose <= baud_table[i] &&
-                   ibaud + iclose >= baud_table[i]) {
-                       /* For the case input == output don't set IBAUD bits
-                          if the user didn't do so */
-                       if (ofound == i && !ibinput)
-                               ifound  = i;
-#ifdef IBSHIFT
-                       else {
-                               ifound = i;
-                               termios->c_cflag |= (baud_bits[i] << IBSHIFT);
-                       }
-#endif
-               }
-       } while (++i < n_baud_table);
-
-       /*
-        *      If we found no match then use BOTHER if provided or warn
-        *      the user their platform maintainer needs to wake up if not.
-        */
-#ifdef BOTHER
-       if (ofound == -1)
-               termios->c_cflag |= BOTHER;
-       /* Set exact input bits only if the input and output differ or the
-          user already did */
-       if (ifound == -1 && (ibaud != obaud || ibinput))
-               termios->c_cflag |= (BOTHER << IBSHIFT);
-#else
-       if (ifound == -1 || ofound == -1)
-               pr_warn_once("tty: Unable to return correct speed data as your architecture needs updating.\n");
-#endif
-}
-EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate);
-
-/**
- *     tty_encode_baud_rate            -       set baud rate of the tty
- *     @ibaud: input baud rate
- *     @obad: output baud rate
- *
- *     Update the current termios data for the tty with the new speed
- *     settings. The caller must hold the termios_rwsem for the tty in
- *     question.
- */
-
-void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud)
-{
-       tty_termios_encode_baud_rate(&tty->termios, ibaud, obaud);
-}
-EXPORT_SYMBOL_GPL(tty_encode_baud_rate);
-
 /**
  *     tty_termios_copy_hw     -       copy hardware settings
  *     @new: New termios
diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c
new file mode 100644 (file)
index 0000000..e703230
--- /dev/null
@@ -0,0 +1,554 @@
+/*
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched/signal.h>
+#include <linux/sched/task.h>
+#include <linux/tty.h>
+#include <linux/fcntl.h>
+#include <linux/uaccess.h>
+
+static int is_ignored(int sig)
+{
+       return (sigismember(&current->blocked, sig) ||
+               current->sighand->action[sig-1].sa.sa_handler == SIG_IGN);
+}
+
+/**
+ *     tty_check_change        -       check for POSIX terminal changes
+ *     @tty: tty to check
+ *
+ *     If we try to write to, or set the state of, a terminal and we're
+ *     not in the foreground, send a SIGTTOU.  If the signal is blocked or
+ *     ignored, go ahead and perform the operation.  (POSIX 7.2)
+ *
+ *     Locking: ctrl_lock
+ */
+int __tty_check_change(struct tty_struct *tty, int sig)
+{
+       unsigned long flags;
+       struct pid *pgrp, *tty_pgrp;
+       int ret = 0;
+
+       if (current->signal->tty != tty)
+               return 0;
+
+       rcu_read_lock();
+       pgrp = task_pgrp(current);
+
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       tty_pgrp = tty->pgrp;
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+       if (tty_pgrp && pgrp != tty->pgrp) {
+               if (is_ignored(sig)) {
+                       if (sig == SIGTTIN)
+                               ret = -EIO;
+               } else if (is_current_pgrp_orphaned())
+                       ret = -EIO;
+               else {
+                       kill_pgrp(pgrp, sig, 1);
+                       set_thread_flag(TIF_SIGPENDING);
+                       ret = -ERESTARTSYS;
+               }
+       }
+       rcu_read_unlock();
+
+       if (!tty_pgrp)
+               tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig);
+
+       return ret;
+}
+
+int tty_check_change(struct tty_struct *tty)
+{
+       return __tty_check_change(tty, SIGTTOU);
+}
+EXPORT_SYMBOL(tty_check_change);
+
+void proc_clear_tty(struct task_struct *p)
+{
+       unsigned long flags;
+       struct tty_struct *tty;
+       spin_lock_irqsave(&p->sighand->siglock, flags);
+       tty = p->signal->tty;
+       p->signal->tty = NULL;
+       spin_unlock_irqrestore(&p->sighand->siglock, flags);
+       tty_kref_put(tty);
+}
+
+/**
+ * proc_set_tty -  set the controlling terminal
+ *
+ * Only callable by the session leader and only if it does not already have
+ * a controlling terminal.
+ *
+ * Caller must hold:  tty_lock()
+ *                   a readlock on tasklist_lock
+ *                   sighand lock
+ */
+static void __proc_set_tty(struct tty_struct *tty)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       /*
+        * The session and fg pgrp references will be non-NULL if
+        * tiocsctty() is stealing the controlling tty
+        */
+       put_pid(tty->session);
+       put_pid(tty->pgrp);
+       tty->pgrp = get_pid(task_pgrp(current));
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+       tty->session = get_pid(task_session(current));
+       if (current->signal->tty) {
+               tty_debug(tty, "current tty %s not NULL!!\n",
+                         current->signal->tty->name);
+               tty_kref_put(current->signal->tty);
+       }
+       put_pid(current->signal->tty_old_pgrp);
+       current->signal->tty = tty_kref_get(tty);
+       current->signal->tty_old_pgrp = NULL;
+}
+
+static void proc_set_tty(struct tty_struct *tty)
+{
+       spin_lock_irq(&current->sighand->siglock);
+       __proc_set_tty(tty);
+       spin_unlock_irq(&current->sighand->siglock);
+}
+
+/*
+ * Called by tty_open() to set the controlling tty if applicable.
+ */
+void tty_open_proc_set_tty(struct file *filp, struct tty_struct *tty)
+{
+       read_lock(&tasklist_lock);
+       spin_lock_irq(&current->sighand->siglock);
+       if (current->signal->leader &&
+           !current->signal->tty &&
+           tty->session == NULL) {
+               /*
+                * Don't let a process that only has write access to the tty
+                * obtain the privileges associated with having a tty as
+                * controlling terminal (being able to reopen it with full
+                * access through /dev/tty, being able to perform pushback).
+                * Many distributions set the group of all ttys to "tty" and
+                * grant write-only access to all terminals for setgid tty
+                * binaries, which should not imply full privileges on all ttys.
+                *
+                * This could theoretically break old code that performs open()
+                * on a write-only file descriptor. In that case, it might be
+                * necessary to also permit this if
+                * inode_permission(inode, MAY_READ) == 0.
+                */
+               if (filp->f_mode & FMODE_READ)
+                       __proc_set_tty(tty);
+       }
+       spin_unlock_irq(&current->sighand->siglock);
+       read_unlock(&tasklist_lock);
+}
+
+struct tty_struct *get_current_tty(void)
+{
+       struct tty_struct *tty;
+       unsigned long flags;
+
+       spin_lock_irqsave(&current->sighand->siglock, flags);
+       tty = tty_kref_get(current->signal->tty);
+       spin_unlock_irqrestore(&current->sighand->siglock, flags);
+       return tty;
+}
+EXPORT_SYMBOL_GPL(get_current_tty);
+
+/*
+ * Called from tty_release().
+ */
+void session_clear_tty(struct pid *session)
+{
+       struct task_struct *p;
+       do_each_pid_task(session, PIDTYPE_SID, p) {
+               proc_clear_tty(p);
+       } while_each_pid_task(session, PIDTYPE_SID, p);
+}
+
+/**
+ *     tty_signal_session_leader       - sends SIGHUP to session leader
+ *     @tty            controlling tty
+ *     @exit_session   if non-zero, signal all foreground group processes
+ *
+ *     Send SIGHUP and SIGCONT to the session leader and its process group.
+ *     Optionally, signal all processes in the foreground process group.
+ *
+ *     Returns the number of processes in the session with this tty
+ *     as their controlling terminal. This value is used to drop
+ *     tty references for those processes.
+ */
+int tty_signal_session_leader(struct tty_struct *tty, int exit_session)
+{
+       struct task_struct *p;
+       int refs = 0;
+       struct pid *tty_pgrp = NULL;
+
+       read_lock(&tasklist_lock);
+       if (tty->session) {
+               do_each_pid_task(tty->session, PIDTYPE_SID, p) {
+                       spin_lock_irq(&p->sighand->siglock);
+                       if (p->signal->tty == tty) {
+                               p->signal->tty = NULL;
+                               /* We defer the dereferences outside fo
+                                  the tasklist lock */
+                               refs++;
+                       }
+                       if (!p->signal->leader) {
+                               spin_unlock_irq(&p->sighand->siglock);
+                               continue;
+                       }
+                       __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
+                       __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
+                       put_pid(p->signal->tty_old_pgrp);  /* A noop */
+                       spin_lock(&tty->ctrl_lock);
+                       tty_pgrp = get_pid(tty->pgrp);
+                       if (tty->pgrp)
+                               p->signal->tty_old_pgrp = get_pid(tty->pgrp);
+                       spin_unlock(&tty->ctrl_lock);
+                       spin_unlock_irq(&p->sighand->siglock);
+               } while_each_pid_task(tty->session, PIDTYPE_SID, p);
+       }
+       read_unlock(&tasklist_lock);
+
+       if (tty_pgrp) {
+               if (exit_session)
+                       kill_pgrp(tty_pgrp, SIGHUP, exit_session);
+               put_pid(tty_pgrp);
+       }
+
+       return refs;
+}
+
+/**
+ *     disassociate_ctty       -       disconnect controlling tty
+ *     @on_exit: true if exiting so need to "hang up" the session
+ *
+ *     This function is typically called only by the session leader, when
+ *     it wants to disassociate itself from its controlling tty.
+ *
+ *     It performs the following functions:
+ *     (1)  Sends a SIGHUP and SIGCONT to the foreground process group
+ *     (2)  Clears the tty from being controlling the session
+ *     (3)  Clears the controlling tty for all processes in the
+ *             session group.
+ *
+ *     The argument on_exit is set to 1 if called when a process is
+ *     exiting; it is 0 if called by the ioctl TIOCNOTTY.
+ *
+ *     Locking:
+ *             BTM is taken for hysterical raisons, and held when
+ *               called from no_tty().
+ *               tty_mutex is taken to protect tty
+ *               ->siglock is taken to protect ->signal/->sighand
+ *               tasklist_lock is taken to walk process list for sessions
+ *                 ->siglock is taken to protect ->signal/->sighand
+ */
+void disassociate_ctty(int on_exit)
+{
+       struct tty_struct *tty;
+
+       if (!current->signal->leader)
+               return;
+
+       tty = get_current_tty();
+       if (tty) {
+               if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) {
+                       tty_vhangup_session(tty);
+               } else {
+                       struct pid *tty_pgrp = tty_get_pgrp(tty);
+                       if (tty_pgrp) {
+                               kill_pgrp(tty_pgrp, SIGHUP, on_exit);
+                               if (!on_exit)
+                                       kill_pgrp(tty_pgrp, SIGCONT, on_exit);
+                               put_pid(tty_pgrp);
+                       }
+               }
+               tty_kref_put(tty);
+
+       } else if (on_exit) {
+               struct pid *old_pgrp;
+               spin_lock_irq(&current->sighand->siglock);
+               old_pgrp = current->signal->tty_old_pgrp;
+               current->signal->tty_old_pgrp = NULL;
+               spin_unlock_irq(&current->sighand->siglock);
+               if (old_pgrp) {
+                       kill_pgrp(old_pgrp, SIGHUP, on_exit);
+                       kill_pgrp(old_pgrp, SIGCONT, on_exit);
+                       put_pid(old_pgrp);
+               }
+               return;
+       }
+
+       spin_lock_irq(&current->sighand->siglock);
+       put_pid(current->signal->tty_old_pgrp);
+       current->signal->tty_old_pgrp = NULL;
+
+       tty = tty_kref_get(current->signal->tty);
+       if (tty) {
+               unsigned long flags;
+               spin_lock_irqsave(&tty->ctrl_lock, flags);
+               put_pid(tty->session);
+               put_pid(tty->pgrp);
+               tty->session = NULL;
+               tty->pgrp = NULL;
+               spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+               tty_kref_put(tty);
+       }
+
+       spin_unlock_irq(&current->sighand->siglock);
+       /* Now clear signal->tty under the lock */
+       read_lock(&tasklist_lock);
+       session_clear_tty(task_session(current));
+       read_unlock(&tasklist_lock);
+}
+
+/**
+ *
+ *     no_tty  - Ensure the current process does not have a controlling tty
+ */
+void no_tty(void)
+{
+       /* FIXME: Review locking here. The tty_lock never covered any race
+          between a new association and proc_clear_tty but possible we need
+          to protect against this anyway */
+       struct task_struct *tsk = current;
+       disassociate_ctty(0);
+       proc_clear_tty(tsk);
+}
+
+/**
+ *     tiocsctty       -       set controlling tty
+ *     @tty: tty structure
+ *     @arg: user argument
+ *
+ *     This ioctl is used to manage job control. It permits a session
+ *     leader to set this tty as the controlling tty for the session.
+ *
+ *     Locking:
+ *             Takes tty_lock() to serialize proc_set_tty() for this tty
+ *             Takes tasklist_lock internally to walk sessions
+ *             Takes ->siglock() when updating signal->tty
+ */
+static int tiocsctty(struct tty_struct *tty, struct file *file, int arg)
+{
+       int ret = 0;
+
+       tty_lock(tty);
+       read_lock(&tasklist_lock);
+
+       if (current->signal->leader && (task_session(current) == tty->session))
+               goto unlock;
+
+       /*
+        * The process must be a session leader and
+        * not have a controlling tty already.
+        */
+       if (!current->signal->leader || current->signal->tty) {
+               ret = -EPERM;
+               goto unlock;
+       }
+
+       if (tty->session) {
+               /*
+                * This tty is already the controlling
+                * tty for another session group!
+                */
+               if (arg == 1 && capable(CAP_SYS_ADMIN)) {
+                       /*
+                        * Steal it away
+                        */
+                       session_clear_tty(tty->session);
+               } else {
+                       ret = -EPERM;
+                       goto unlock;
+               }
+       }
+
+       /* See the comment in tty_open_proc_set_tty(). */
+       if ((file->f_mode & FMODE_READ) == 0 && !capable(CAP_SYS_ADMIN)) {
+               ret = -EPERM;
+               goto unlock;
+       }
+
+       proc_set_tty(tty);
+unlock:
+       read_unlock(&tasklist_lock);
+       tty_unlock(tty);
+       return ret;
+}
+
+/**
+ *     tty_get_pgrp    -       return a ref counted pgrp pid
+ *     @tty: tty to read
+ *
+ *     Returns a refcounted instance of the pid struct for the process
+ *     group controlling the tty.
+ */
+struct pid *tty_get_pgrp(struct tty_struct *tty)
+{
+       unsigned long flags;
+       struct pid *pgrp;
+
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       pgrp = get_pid(tty->pgrp);
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+       return pgrp;
+}
+EXPORT_SYMBOL_GPL(tty_get_pgrp);
+
+/*
+ * This checks not only the pgrp, but falls back on the pid if no
+ * satisfactory pgrp is found. I dunno - gdb doesn't work correctly
+ * without this...
+ *
+ * The caller must hold rcu lock or the tasklist lock.
+ */
+static struct pid *session_of_pgrp(struct pid *pgrp)
+{
+       struct task_struct *p;
+       struct pid *sid = NULL;
+
+       p = pid_task(pgrp, PIDTYPE_PGID);
+       if (p == NULL)
+               p = pid_task(pgrp, PIDTYPE_PID);
+       if (p != NULL)
+               sid = task_session(p);
+
+       return sid;
+}
+
+/**
+ *     tiocgpgrp               -       get process group
+ *     @tty: tty passed by user
+ *     @real_tty: tty side of the tty passed by the user if a pty else the tty
+ *     @p: returned pid
+ *
+ *     Obtain the process group of the tty. If there is no process group
+ *     return an error.
+ *
+ *     Locking: none. Reference to current->signal->tty is safe.
+ */
+static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
+{
+       struct pid *pid;
+       int ret;
+       /*
+        * (tty == real_tty) is a cheap way of
+        * testing if the tty is NOT a master pty.
+        */
+       if (tty == real_tty && current->signal->tty != real_tty)
+               return -ENOTTY;
+       pid = tty_get_pgrp(real_tty);
+       ret =  put_user(pid_vnr(pid), p);
+       put_pid(pid);
+       return ret;
+}
+
+/**
+ *     tiocspgrp               -       attempt to set process group
+ *     @tty: tty passed by user
+ *     @real_tty: tty side device matching tty passed by user
+ *     @p: pid pointer
+ *
+ *     Set the process group of the tty to the session passed. Only
+ *     permitted where the tty session is our session.
+ *
+ *     Locking: RCU, ctrl lock
+ */
+static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
+{
+       struct pid *pgrp;
+       pid_t pgrp_nr;
+       int retval = tty_check_change(real_tty);
+
+       if (retval == -EIO)
+               return -ENOTTY;
+       if (retval)
+               return retval;
+       if (!current->signal->tty ||
+           (current->signal->tty != real_tty) ||
+           (real_tty->session != task_session(current)))
+               return -ENOTTY;
+       if (get_user(pgrp_nr, p))
+               return -EFAULT;
+       if (pgrp_nr < 0)
+               return -EINVAL;
+       rcu_read_lock();
+       pgrp = find_vpid(pgrp_nr);
+       retval = -ESRCH;
+       if (!pgrp)
+               goto out_unlock;
+       retval = -EPERM;
+       if (session_of_pgrp(pgrp) != task_session(current))
+               goto out_unlock;
+       retval = 0;
+       spin_lock_irq(&tty->ctrl_lock);
+       put_pid(real_tty->pgrp);
+       real_tty->pgrp = get_pid(pgrp);
+       spin_unlock_irq(&tty->ctrl_lock);
+out_unlock:
+       rcu_read_unlock();
+       return retval;
+}
+
+/**
+ *     tiocgsid                -       get session id
+ *     @tty: tty passed by user
+ *     @real_tty: tty side of the tty passed by the user if a pty else the tty
+ *     @p: pointer to returned session id
+ *
+ *     Obtain the session id of the tty. If there is no session
+ *     return an error.
+ *
+ *     Locking: none. Reference to current->signal->tty is safe.
+ */
+static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
+{
+       /*
+        * (tty == real_tty) is a cheap way of
+        * testing if the tty is NOT a master pty.
+       */
+       if (tty == real_tty && current->signal->tty != real_tty)
+               return -ENOTTY;
+       if (!real_tty->session)
+               return -ENOTTY;
+       return put_user(pid_vnr(real_tty->session), p);
+}
+
+/*
+ * Called from tty_ioctl(). If tty is a pty then real_tty is the slave side,
+ * if not then tty == real_tty.
+ */
+long tty_jobctrl_ioctl(struct tty_struct *tty, struct tty_struct *real_tty,
+                      struct file *file, unsigned int cmd, unsigned long arg)
+{
+       void __user *p = (void __user *)arg;
+
+       switch (cmd) {
+       case TIOCNOTTY:
+               if (current->signal->tty != tty)
+                       return -ENOTTY;
+               no_tty();
+               return 0;
+       case TIOCSCTTY:
+               return tiocsctty(real_tty, file, arg);
+       case TIOCGPGRP:
+               return tiocgpgrp(tty, real_tty, p);
+       case TIOCSPGRP:
+               return tiocspgrp(tty, real_tty, p);
+       case TIOCGSID:
+               return tiocgsid(tty, real_tty, p);
+       }
+       return -ENOIOCTLCMD;
+}
index 36e1b8c7680f1d8fc5a64ba16e2fc52a0a379b40..accbd1257bc4a36938918965592e4a1e470098a6 100644 (file)
@@ -80,21 +80,17 @@ void clear_selection(void)
 
 /*
  * User settable table: what characters are to be considered alphabetic?
- * 256 bits. Locked by the console lock.
+ * 128 bits. Locked by the console lock.
  */
-static u32 inwordLut[8]={
+static u32 inwordLut[]={
   0x00000000, /* control chars     */
-  0x03FF0000, /* digits            */
+  0x03FFE000, /* digits and "-./"  */
   0x87FFFFFE, /* uppercase and '_' */
   0x07FFFFFE, /* lowercase         */
-  0x00000000,
-  0x00000000,
-  0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */
-  0xFF7FFFFF  /* latin-1 accented letters, not division sign */
 };
 
 static inline int inword(const u16 c) {
-       return c > 0xff || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
+       return c > 0x7f || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
 }
 
 /**
@@ -106,10 +102,10 @@ static inline int inword(const u16 c) {
  */
 int sel_loadlut(char __user *p)
 {
-       u32 tmplut[8];
-       if (copy_from_user(tmplut, (u32 __user *)(p+4), 32))
+       u32 tmplut[ARRAY_SIZE(inwordLut)];
+       if (copy_from_user(tmplut, (u32 __user *)(p+4), sizeof(inwordLut)))
                return -EFAULT;
-       memcpy(inwordLut, tmplut, 32);
+       memcpy(inwordLut, tmplut, sizeof(inwordLut));
        return 0;
 }
 
index 5c4933bb4b5336258f3d31f22971a1aea87c0dd9..9c9945284bcf240d319b98f986c2573b4d16e307 100644 (file)
@@ -181,7 +181,7 @@ int console_blanked;
 
 static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
 static int vesa_off_interval;
-static int blankinterval = 10*60;
+static int blankinterval;
 core_param(consoleblank, blankinterval, int, 0444);
 
 static DECLARE_WORK(console_work, console_callback);
index 5949d185558990daed3b4c76bbdfdad20fb34608..b8920a031a3e357d71101905e3396ba245160781 100644 (file)
@@ -212,4 +212,6 @@ extern bool vgacon_text_force(void);
 static inline bool vgacon_text_force(void) { return false; }
 #endif
 
+extern void console_init(void);
+
 #endif /* _LINUX_CONSOLE_H */
index 37395b8eb8f1e39fb8166c6d215d6abe8e056690..cda76c6506ca411c42c5e1cdf6d287263d97dde6 100644 (file)
@@ -41,12 +41,16 @@ struct serdev_device_ops {
  * @nr:                Device number on serdev bus.
  * @ctrl:      serdev controller managing this device.
  * @ops:       Device operations.
+ * @write_comp Completion used by serdev_device_write() internally
+ * @write_lock Lock to serialize access when writing data
  */
 struct serdev_device {
        struct device dev;
        int nr;
        struct serdev_controller *ctrl;
        const struct serdev_device_ops *ops;
+       struct completion write_comp;
+       struct mutex write_lock;
 };
 
 static inline struct serdev_device *to_serdev_device(struct device *d)
@@ -170,7 +174,7 @@ static inline void serdev_controller_write_wakeup(struct serdev_controller *ctrl
        if (!serdev || !serdev->ops->write_wakeup)
                return;
 
-       serdev->ops->write_wakeup(ctrl->serdev);
+       serdev->ops->write_wakeup(serdev);
 }
 
 static inline int serdev_controller_receive_buf(struct serdev_controller *ctrl,
@@ -182,7 +186,7 @@ static inline int serdev_controller_receive_buf(struct serdev_controller *ctrl,
        if (!serdev || !serdev->ops->receive_buf)
                return -EINVAL;
 
-       return serdev->ops->receive_buf(ctrl->serdev, data, count);
+       return serdev->ops->receive_buf(serdev, data, count);
 }
 
 #if IS_ENABLED(CONFIG_SERIAL_DEV_BUS)
@@ -194,7 +198,8 @@ void serdev_device_set_flow_control(struct serdev_device *, bool);
 void serdev_device_wait_until_sent(struct serdev_device *, long);
 int serdev_device_get_tiocm(struct serdev_device *);
 int serdev_device_set_tiocm(struct serdev_device *, int, int);
-int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t);
+void serdev_device_write_wakeup(struct serdev_device *);
+int serdev_device_write(struct serdev_device *, const unsigned char *, size_t, unsigned long);
 void serdev_device_write_flush(struct serdev_device *);
 int serdev_device_write_room(struct serdev_device *);
 
@@ -240,7 +245,8 @@ static inline int serdev_device_set_tiocm(struct serdev_device *serdev, int set,
 {
        return -ENOTSUPP;
 }
-static inline int serdev_device_write_buf(struct serdev_device *sdev, const unsigned char *buf, size_t count)
+static inline int serdev_device_write(struct serdev_device *sdev, const unsigned char *buf,
+                                     size_t count, unsigned long timeout)
 {
        return -ENODEV;
 }
@@ -306,4 +312,11 @@ static inline struct device *serdev_tty_port_register(struct tty_port *port,
 static inline void serdev_tty_port_unregister(struct tty_port *port) {}
 #endif /* CONFIG_SERIAL_DEV_CTRL_TTYPORT */
 
+static inline int serdev_device_write_buf(struct serdev_device *serdev,
+                                         const unsigned char *data,
+                                         size_t count)
+{
+       return serdev_device_write(serdev, data, count, 0);
+}
+
 #endif /*_LINUX_SERDEV_H */
index 58484fb35cc869b63fe0a1dec5fa39f1b9eace84..64d892f1e5cd833bd30003e6f64c99da3a73f5f7 100644 (file)
@@ -247,6 +247,7 @@ struct uart_port {
        unsigned char           suspended;
        unsigned char           irq_wake;
        unsigned char           unused[2];
+       const char              *name;                  /* port name */
        struct attribute_group  *attr_group;            /* port specific attributes */
        const struct attribute_group **tty_groups;      /* all attributes (serial core use only) */
        struct serial_rs485     rs485;
index 1017e904c0a3f90507183570b0897199b4ace563..d07cd2105a6c6a3bdcac20c918622c7ad4ea0840 100644 (file)
@@ -390,7 +390,6 @@ static inline bool tty_throttled(struct tty_struct *tty)
 }
 
 #ifdef CONFIG_TTY
-extern void console_init(void);
 extern void tty_kref_put(struct tty_struct *tty);
 extern struct pid *tty_get_pgrp(struct tty_struct *tty);
 extern void tty_vhangup_self(void);
@@ -402,8 +401,6 @@ extern struct tty_struct *get_current_tty(void);
 extern int __init tty_init(void);
 extern const char *tty_name(const struct tty_struct *tty);
 #else
-static inline void console_init(void)
-{ }
 static inline void tty_kref_put(struct tty_struct *tty)
 { }
 static inline struct pid *tty_get_pgrp(struct tty_struct *tty)
@@ -478,9 +475,13 @@ extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws);
 extern int is_current_pgrp_orphaned(void);
 extern void tty_hangup(struct tty_struct *tty);
 extern void tty_vhangup(struct tty_struct *tty);
+extern void tty_vhangup_session(struct tty_struct *tty);
 extern int tty_hung_up_p(struct file *filp);
 extern void do_SAK(struct tty_struct *tty);
 extern void __do_SAK(struct tty_struct *tty);
+extern void tty_open_proc_set_tty(struct file *filp, struct tty_struct *tty);
+extern int tty_signal_session_leader(struct tty_struct *tty, int exit_session);
+extern void session_clear_tty(struct pid *session);
 extern void no_tty(void);
 extern void tty_buffer_free_all(struct tty_port *port);
 extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld);
@@ -528,6 +529,8 @@ extern void tty_ldisc_flush(struct tty_struct *tty);
 extern long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 extern int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
                        unsigned int cmd, unsigned long arg);
+extern long tty_jobctrl_ioctl(struct tty_struct *tty, struct tty_struct *real_tty,
+                             struct file *file, unsigned int cmd, unsigned long arg);
 extern int tty_perform_flush(struct tty_struct *tty, unsigned long arg);
 extern void tty_default_fops(struct file_operations *fops);
 extern struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx);
@@ -669,7 +672,11 @@ extern int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p,
 
 /* n_tty.c */
 extern void n_tty_inherit_ops(struct tty_ldisc_ops *ops);
+#ifdef CONFIG_TTY
 extern void __init n_tty_init(void);
+#else
+static inline void n_tty_init(void) { }
+#endif
 
 /* tty_audit.c */
 #ifdef CONFIG_AUDIT
index cc48053bb39febfb6261fb337ed4a63ca018d200..f866510472d7a263f03bc21b7a158e5b288185f9 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/initrd.h>
 #include <linux/bootmem.h>
 #include <linux/acpi.h>
-#include <linux/tty.h>
+#include <linux/console.h>
 #include <linux/nmi.h>
 #include <linux/percpu.h>
 #include <linux/kmod.h>
index d5760c42f042f4accfb65de23784bcaa877d9b00..61d41ca418441a15945b376964f6f3ed07ac84b8 100644 (file)
@@ -2,12 +2,13 @@
 
 #include <linux/kernel.h>
 #include <linux/console.h>
+#include <linux/errno.h>
 #include <linux/string.h>
 
 #include "console_cmdline.h"
 #include "braille.h"
 
-char *_braille_console_setup(char **str, char **brl_options)
+int _braille_console_setup(char **str, char **brl_options)
 {
        if (!strncmp(*str, "brl,", 4)) {
                *brl_options = "";
@@ -15,14 +16,14 @@ char *_braille_console_setup(char **str, char **brl_options)
        } else if (!strncmp(*str, "brl=", 4)) {
                *brl_options = *str + 4;
                *str = strchr(*brl_options, ',');
-               if (!*str)
+               if (!*str) {
                        pr_err("need port name after brl=\n");
-               else
-                       *((*str)++) = 0;
-       } else
-               return NULL;
+                       return -EINVAL;
+               }
+               *((*str)++) = 0;
+       }
 
-       return *str;
+       return 0;
 }
 
 int
index 769d771145c881cd78f43c035ea0866adb96b9b9..749a6756843a08e2a5f2a9ae4fa984658b43a65b 100644 (file)
@@ -9,7 +9,14 @@ braille_set_options(struct console_cmdline *c, char *brl_options)
        c->brl_options = brl_options;
 }
 
-char *
+/*
+ * Setup console according to braille options.
+ * Return -EINVAL on syntax error, 0 on success (or no braille option was
+ * actually given).
+ * Modifies str to point to the serial options
+ * Sets brl_options to the parsed braille options.
+ */
+int
 _braille_console_setup(char **str, char **brl_options);
 
 int
@@ -25,10 +32,10 @@ braille_set_options(struct console_cmdline *c, char *brl_options)
 {
 }
 
-static inline char *
+static inline int
 _braille_console_setup(char **str, char **brl_options)
 {
-       return NULL;
+       return 0;
 }
 
 static inline int
index fb2d1591f671abf08a6bae9b6c33b5baf3f91f68..a1aecf44ab07c70ab9f33d455646559313926344 100644 (file)
@@ -2641,6 +2641,30 @@ int unregister_console(struct console *console)
 }
 EXPORT_SYMBOL(unregister_console);
 
+/*
+ * Initialize the console device. This is called *early*, so
+ * we can't necessarily depend on lots of kernel help here.
+ * Just do some early initializations, and do the complex setup
+ * later.
+ */
+void __init console_init(void)
+{
+       initcall_t *call;
+
+       /* Setup the default TTY line discipline. */
+       n_tty_init();
+
+       /*
+        * set up the console device so that later boot sequences can
+        * inform about problems etc..
+        */
+       call = __con_initcall_start;
+       while (call < __con_initcall_end) {
+               (*call)();
+               call++;
+       }
+}
+
 /*
  * Some boot consoles access data that is in the init section and which will
  * be discarded after the initcalls have been run. To make sure that no code