TTY/Serial driver patches for 4.17-rc1

Here is the big set of tty and serial driver patches for 4.17-rc1
 
 Not all that big really, most are just small fixes and additions to
 existing drivers.  There's a bunch of work on the imx serial driver
 recently for some reason, and a new embedded serial driver added as
 well.
 
 Full details are in the shortlog.
 
 All of these have been in the linux-next tree for a while with no
 reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWsSn+w8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynb6wCdEf5dAUrSB37ptZY78n4kc6nI6NAAniDO+rjL
 ppZZp7QTIB/bnPfW8cOH
 =+tfY
 -----END PGP SIGNATURE-----

Merge tag 'tty-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial driver updates from Greg KH:
 "Here is the big set of tty and serial driver patches for 4.17-rc1

  Not all that big really, most are just small fixes and additions to
  existing drivers. There's a bunch of work on the imx serial driver
  recently for some reason, and a new embedded serial driver added as
  well.

  Full details are in the shortlog.

  All of these have been in the linux-next tree for a while with no
  reported issues"

* tag 'tty-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (66 commits)
  serial: expose buf_overrun count through proc interface
  serial: mvebu-uart: fix tx lost characters
  tty: serial: msm_geni_serial: Fix return value check in qcom_geni_serial_probe()
  tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  8250-men-mcb: add support for 16z025 and 16z057
  powerpc: Mark the variable earlycon_acpi_spcr_enable maybe_unused
  serial: stm32: fix initialization of RS485 mode
  ARM: dts: STi: Remove "console=ttyASN" from bootargs for STi boards
  vt: change SGR 21 to follow the standards
  serdev: Fix typo in serdev_device_alloc
  ARM: dts: STi: Fix aliases property name for STi boards
  tty: st-asc: Update tty alias
  serial: stm32: add support for RS485 hardware control mode
  dt-bindings: serial: stm32: add RS485 optional properties
  selftests: add devpts selftests
  devpts: comment devpts_mntget()
  devpts: resolve devpts bind-mounts
  devpts: hoist out check for DEVPTS_SUPER_MAGIC
  serial: 8250: Add Nuvoton NPCM UART
  serial: mxs-auart: disable clks of Alphascale ASM9260
  ...
This commit is contained in:
Linus Torvalds 2018-04-04 18:43:49 -07:00
commit 9abf8acea2
53 changed files with 2903 additions and 739 deletions

View File

@ -24,6 +24,7 @@ Required properties:
- "ti,da830-uart"
- "aspeed,ast2400-vuart"
- "aspeed,ast2500-vuart"
- "nuvoton,npcm750-uart"
- "serial" if the port type is unknown.
- reg : offset and length of the register set for the device.
- interrupts : should contain uart interrupt.

View File

@ -43,6 +43,8 @@ Required properties:
- "renesas,hscif-r8a7796" for R8A7796 (R-Car M3-W) HSCIF compatible UART.
- "renesas,scif-r8a77970" for R8A77970 (R-Car V3M) SCIF compatible UART.
- "renesas,hscif-r8a77970" for R8A77970 (R-Car V3M) HSCIF compatible UART.
- "renesas,scif-r8a77980" for R8A77980 (R-Car V3H) SCIF compatible UART.
- "renesas,hscif-r8a77980" for R8A77980 (R-Car V3H) HSCIF compatible UART.
- "renesas,scif-r8a77995" for R8A77995 (R-Car D3) SCIF compatible UART.
- "renesas,hscif-r8a77995" for R8A77995 (R-Car D3) HSCIF compatible UART.
- "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART.

View File

@ -15,6 +15,8 @@ Required properties:
Optional properties:
- pinctrl: The reference on the pins configuration
- st,hw-flow-ctrl: bool flag to enable hardware flow control.
- rs485-rts-delay, rs485-rx-during-tx, rs485-rts-active-low,
linux,rs485-enabled-at-boot-time: see rs485.txt.
- dmas: phandle(s) to DMA controller node(s). Refer to stm32-dma.txt
- dma-names: "rx" and/or "tx"

View File

@ -14,7 +14,7 @@ / {
compatible = "st,stih407-b2120", "st,stih407";
chosen {
bootargs = "console=ttyAS0,115200 clk_ignore_unused";
bootargs = "clk_ignore_unused";
linux,stdout-path = &sbc_serial0;
};
@ -24,7 +24,7 @@ memory {
};
aliases {
ttyAS0 = &sbc_serial0;
serial0 = &sbc_serial0;
ethernet0 = &ethernet0;
};

View File

@ -14,7 +14,7 @@ / {
compatible = "st,stih410-b2120", "st,stih410";
chosen {
bootargs = "console=ttyAS0,115200 clk_ignore_unused";
bootargs = "clk_ignore_unused";
linux,stdout-path = &sbc_serial0;
};
@ -24,7 +24,7 @@ memory {
};
aliases {
ttyAS0 = &sbc_serial0;
serial0 = &sbc_serial0;
ethernet0 = &ethernet0;
};

View File

@ -15,7 +15,7 @@ / {
compatible = "st,stih410-b2260", "st,stih410";
chosen {
bootargs = "console=ttyAS1,115200 clk_ignore_unused";
bootargs = "clk_ignore_unused";
linux,stdout-path = &uart1;
};
@ -25,7 +25,7 @@ memory {
};
aliases {
ttyAS1 = &uart1;
serial1 = &uart1;
ethernet0 = &ethernet0;
};

View File

@ -14,7 +14,7 @@ / {
compatible = "st,stih418-b2199", "st,stih418";
chosen {
bootargs = "console=ttyAS0,115200 clk_ignore_unused";
bootargs = "clk_ignore_unused";
linux,stdout-path = &sbc_serial0;
};
@ -24,7 +24,7 @@ memory {
};
aliases {
ttyAS0 = &sbc_serial0;
serial0 = &sbc_serial0;
ethernet0 = &ethernet0;
};

View File

@ -156,9 +156,6 @@ static struct parport_pc_pci cards[] = {
/* sunix_2s1p */ { 1, { { 3, -1 }, } },
};
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
static struct pci_device_id parport_serial_pci_tbl[] = {
/* PCI cards */
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L,

View File

@ -88,6 +88,16 @@ config HVC_DCC
driver. This console is used through a JTAG only on ARM. If you don't have
a JTAG then you probably don't want this option.
config HVC_RISCV_SBI
bool "RISC-V SBI console support"
depends on RISCV
select HVC_DRIVER
help
This enables support for console output via RISC-V SBI calls, which
is normally used only during boot to output printk.
If you don't know what do to here, say Y.
config HVCS
tristate "IBM Hypervisor Virtual Console Server support"
depends on PPC_PSERIES && HVC_CONSOLE

View File

@ -9,4 +9,5 @@ obj-$(CONFIG_HVC_IRQ) += hvc_irq.o
obj-$(CONFIG_HVC_XEN) += hvc_xen.o
obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o
obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o
obj-$(CONFIG_HVC_RISCV_SBI) += hvc_riscv_sbi.o
obj-$(CONFIG_HVCS) += hvcs.o

View File

@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2008 David Gibson, IBM Corporation
* Copyright (C) 2012 Regents of the University of California
* Copyright (C) 2017 SiFive
*/
#include <linux/console.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <asm/sbi.h>
#include "hvc_console.h"
static int hvc_sbi_tty_put(uint32_t vtermno, const char *buf, int count)
{
int i;
for (i = 0; i < count; i++)
sbi_console_putchar(buf[i]);
return i;
}
static int hvc_sbi_tty_get(uint32_t vtermno, char *buf, int count)
{
int i, c;
for (i = 0; i < count; i++) {
c = sbi_console_getchar();
if (c < 0)
break;
buf[i] = c;
}
return i;
}
static const struct hv_ops hvc_sbi_ops = {
.get_chars = hvc_sbi_tty_get,
.put_chars = hvc_sbi_tty_put,
};
static int __init hvc_sbi_init(void)
{
return PTR_ERR_OR_ZERO(hvc_alloc(0, 0, &hvc_sbi_ops, 16));
}
device_initcall(hvc_sbi_init);
static int __init hvc_sbi_console_init(void)
{
hvc_instantiate(0, 0, &hvc_sbi_ops);
add_preferred_console("hvc", 0, NULL);
return 0;
}
console_initcall(hvc_sbi_console_init);

View File

@ -350,7 +350,7 @@ static struct bus_type serdev_bus_type = {
};
/**
* serdev_controller_alloc() - Allocate a new serdev device
* serdev_device_alloc() - Allocate a new serdev device
* @ctrl: associated controller
*
* Caller is responsible for either calling serdev_device_add() to add the

View File

@ -9,6 +9,7 @@
* LCR is written whilst busy. If it is, then a busy detect interrupt is
* raised, the LCR needs to be rewritten and the uart status register read.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
@ -119,10 +120,27 @@ static void dw8250_check_lcr(struct uart_port *p, int value)
*/
}
/* Returns once the transmitter is empty or we run out of retries */
static void dw8250_tx_wait_empty(struct uart_port *p, int tries)
{
unsigned int lsr;
while (tries--) {
lsr = readb (p->membase + (UART_LSR << p->regshift));
if (lsr & UART_LSR_TEMT)
break;
udelay (10);
}
}
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
{
struct dw8250_data *d = p->private_data;
/* Allow the TX to drain before we reconfigure */
if (offset == UART_LCR)
dw8250_tx_wait_empty(p, 1000);
writeb(value, p->membase + (offset << p->regshift));
if (offset == UART_LCR && !d->uart_16550_compatible)
@ -339,17 +357,11 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
p->serial_in = dw8250_serial_in32be;
p->serial_out = dw8250_serial_out32be;
}
} else if (has_acpi_companion(p->dev)) {
const struct acpi_device_id *id;
id = acpi_match_device(p->dev->driver->acpi_match_table,
p->dev);
if (id && !strcmp(id->id, "APMC0D08")) {
p->iotype = UPIO_MEM32;
p->regshift = 2;
p->serial_in = dw8250_serial_in32;
data->uart_16550_compatible = true;
}
} else if (acpi_dev_present("APMC0D08", NULL, -1)) {
p->iotype = UPIO_MEM32;
p->regshift = 2;
p->serial_in = dw8250_serial_in32;
data->uart_16550_compatible = true;
}
/* Platforms with iDMA */

View File

@ -1,12 +1,19 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/mcb.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <uapi/linux/serial_core.h>
#define MEN_UART_ID_Z025 0x19
#define MEN_UART_ID_Z057 0x39
#define MEN_UART_ID_Z125 0x7d
#define MEN_UART_MEM_SIZE 0x10
struct serial_8250_men_mcb_data {
struct uart_8250_port uart;
int line;
@ -18,7 +25,7 @@ struct serial_8250_men_mcb_data {
* parameter in order to really set the correct baudrate, and
* do so if possible without user interaction
*/
static u32 men_z125_lookup_uartclk(struct mcb_device *mdev)
static u32 men_lookup_uartclk(struct mcb_device *mdev)
{
/* use default value if board is not available below */
u32 clkval = 1041666;
@ -28,10 +35,12 @@ static u32 men_z125_lookup_uartclk(struct mcb_device *mdev)
mdev->bus->name);
if (strncmp(mdev->bus->name, "F075", 4) == 0)
clkval = 1041666;
else if (strncmp(mdev->bus->name, "F216", 4) == 0)
else if (strncmp(mdev->bus->name, "F216", 4) == 0)
clkval = 1843200;
else if (strncmp(mdev->bus->name, "G215", 4) == 0)
clkval = 1843200;
else if (strncmp(mdev->bus->name, "F210", 4) == 0)
clkval = 115200;
else
dev_info(&mdev->dev,
"board not detected, using default uartclk\n");
@ -41,62 +50,108 @@ static u32 men_z125_lookup_uartclk(struct mcb_device *mdev)
return clkval;
}
static unsigned int get_num_ports(struct mcb_device *mdev,
void __iomem *membase)
{
switch (mdev->id) {
case MEN_UART_ID_Z125:
return 1U;
case MEN_UART_ID_Z025:
return readb(membase) >> 4;
case MEN_UART_ID_Z057:
return 4U;
default:
dev_err(&mdev->dev, "no supported device!\n");
return -ENODEV;
}
}
static int serial_8250_men_mcb_probe(struct mcb_device *mdev,
const struct mcb_device_id *id)
{
struct serial_8250_men_mcb_data *data;
struct resource *mem;
unsigned int num_ports;
unsigned int i;
void __iomem *membase;
data = devm_kzalloc(&mdev->dev,
mem = mcb_get_resource(mdev, IORESOURCE_MEM);
if (mem == NULL)
return -ENXIO;
membase = devm_ioremap_resource(&mdev->dev, mem);
if (IS_ERR(membase))
return PTR_ERR_OR_ZERO(membase);
num_ports = get_num_ports(mdev, membase);
dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n",
mdev->id, num_ports);
if (num_ports == 0 || num_ports > 4) {
dev_err(&mdev->dev, "unexpected number of ports: %u\n",
num_ports);
return -ENODEV;
}
data = devm_kcalloc(&mdev->dev, num_ports,
sizeof(struct serial_8250_men_mcb_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
mcb_set_drvdata(mdev, data);
data->uart.port.dev = mdev->dma_dev;
spin_lock_init(&data->uart.port.lock);
data->uart.port.type = PORT_16550;
data->uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
data->uart.port.iotype = UPIO_MEM;
data->uart.port.uartclk = men_z125_lookup_uartclk(mdev);
data->uart.port.regshift = 0;
data->uart.port.fifosize = 60;
for (i = 0; i < num_ports; i++) {
data[i].uart.port.dev = mdev->dma_dev;
spin_lock_init(&data[i].uart.port.lock);
mem = mcb_get_resource(mdev, IORESOURCE_MEM);
if (mem == NULL)
return -ENXIO;
data[i].uart.port.type = PORT_16550;
data[i].uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ
| UPF_FIXED_TYPE;
data[i].uart.port.iotype = UPIO_MEM;
data[i].uart.port.uartclk = men_lookup_uartclk(mdev);
data[i].uart.port.regshift = 0;
data[i].uart.port.irq = mcb_get_irq(mdev);
data[i].uart.port.membase = membase;
data[i].uart.port.fifosize = 60;
data[i].uart.port.mapbase = (unsigned long) mem->start
+ i * MEN_UART_MEM_SIZE;
data[i].uart.port.iobase = data[i].uart.port.mapbase;
data->uart.port.irq = mcb_get_irq(mdev);
data->uart.port.membase = devm_ioremap_resource(&mdev->dev, mem);
if (IS_ERR(data->uart.port.membase))
return PTR_ERR_OR_ZERO(data->uart.port.membase);
data->uart.port.mapbase = (unsigned long) mem->start;
data->uart.port.iobase = data->uart.port.mapbase;
/* ok, register the port */
data->line = serial8250_register_8250_port(&data->uart);
if (data->line < 0)
return data->line;
dev_info(&mdev->dev, "found 16Z125 UART: ttyS%d\n", data->line);
/* ok, register the port */
data[i].line = serial8250_register_8250_port(&data[i].uart);
if (data[i].line < 0) {
dev_err(&mdev->dev, "unable to register UART port\n");
return data[i].line;
}
dev_info(&mdev->dev, "found MCB UART: ttyS%d\n", data[i].line);
}
return 0;
}
static void serial_8250_men_mcb_remove(struct mcb_device *mdev)
{
unsigned int num_ports, i;
struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev);
if (data)
serial8250_unregister_port(data->line);
if (!data)
return;
num_ports = get_num_ports(mdev, data[0].uart.port.membase);
if (num_ports < 0 || num_ports > 4) {
dev_err(&mdev->dev, "error retrieving number of ports!\n");
return;
}
for (i = 0; i < num_ports; i++)
serial8250_unregister_port(data[i].line);
}
static const struct mcb_device_id serial_8250_men_mcb_ids[] = {
{ .device = 0x7d },
{ .device = MEN_UART_ID_Z025 },
{ .device = MEN_UART_ID_Z057 },
{ .device = MEN_UART_ID_Z125 },
{ }
};
MODULE_DEVICE_TABLE(mcb, serial_8250_men_mcb_ids);
@ -113,6 +168,8 @@ static struct mcb_driver mcb_driver = {
module_mcb_driver(mcb_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MEN 16z125 8250 UART driver");
MODULE_DESCRIPTION("MEN 8250 UART driver");
MODULE_AUTHOR("Michael Moese <michael.moese@men.de");
MODULE_ALIAS("mcb:16z125");
MODULE_ALIAS("mcb:16z025");
MODULE_ALIAS("mcb:16z057");

View File

@ -316,6 +316,7 @@ static const struct of_device_id of_platform_serial_table[] = {
{ .compatible = "mrvl,mmp-uart",
.data = (void *)PORT_XSCALE, },
{ .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, },
{ .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, of_platform_serial_table);

View File

@ -114,6 +114,7 @@ struct omap8250_priv {
struct uart_8250_dma omap8250_dma;
spinlock_t rx_dma_lock;
bool rx_dma_broken;
bool throttled;
};
#ifdef CONFIG_SERIAL_8250_DMA
@ -692,6 +693,7 @@ static void omap_8250_shutdown(struct uart_port *port)
static void omap_8250_throttle(struct uart_port *port)
{
struct omap8250_priv *priv = port->private_data;
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
@ -700,6 +702,7 @@ static void omap_8250_throttle(struct uart_port *port)
spin_lock_irqsave(&port->lock, flags);
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
serial_out(up, UART_IER, up->ier);
priv->throttled = true;
spin_unlock_irqrestore(&port->lock, flags);
pm_runtime_mark_last_busy(port->dev);
@ -738,12 +741,16 @@ static int omap_8250_rs485_config(struct uart_port *port,
static void omap_8250_unthrottle(struct uart_port *port)
{
struct omap8250_priv *priv = port->private_data;
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
pm_runtime_get_sync(port->dev);
spin_lock_irqsave(&port->lock, flags);
priv->throttled = false;
if (up->dma)
up->dma->rx_dma(up);
up->ier |= UART_IER_RLSI | UART_IER_RDI;
serial_out(up, UART_IER, up->ier);
spin_unlock_irqrestore(&port->lock, flags);
@ -788,6 +795,7 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
static void __dma_rx_complete(void *param)
{
struct uart_8250_port *p = param;
struct omap8250_priv *priv = p->port.private_data;
struct uart_8250_dma *dma = p->dma;
struct dma_tx_state state;
unsigned long flags;
@ -805,7 +813,8 @@ static void __dma_rx_complete(void *param)
return;
}
__dma_rx_do_complete(p);
omap_8250_rx_dma(p);
if (!priv->throttled)
omap_8250_rx_dma(p);
spin_unlock_irqrestore(&p->port.lock, flags);
}

View File

@ -1685,9 +1685,6 @@ pci_wch_ch38x_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
#define PCIE_VENDOR_ID_WCH 0x1c00
#define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250
#define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470

View File

@ -47,6 +47,10 @@
#define UART_EXAR_SLEEP 0x8b /* Sleep mode */
#define UART_EXAR_DVID 0x8d /* Device identification */
/* Nuvoton NPCM timeout register */
#define UART_NPCM_TOR 7
#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */
/*
* Debugging.
*/
@ -293,6 +297,15 @@ static const struct serial8250_config uart_config[] = {
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
.flags = UART_CAP_FIFO,
},
[PORT_NPCM] = {
.name = "Nuvoton 16550",
.fifo_size = 16,
.tx_loadsz = 16,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
.rxtrig_bytes = {1, 4, 8, 14},
.flags = UART_CAP_FIFO,
},
};
/* Uart divisor latch read */
@ -1854,7 +1867,8 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
status = serial_port_in(port, UART_LSR);
if (status & (UART_LSR_DR | UART_LSR_BI)) {
if (status & (UART_LSR_DR | UART_LSR_BI) &&
iir & UART_IIR_RDI) {
if (!up->dma || handle_rx_dma(up, iir))
status = serial8250_rx_chars(up, status);
}
@ -2140,6 +2154,15 @@ int serial8250_do_startup(struct uart_port *port)
UART_DA830_PWREMU_MGMT_FREE);
}
if (port->type == PORT_NPCM) {
/*
* Nuvoton calls the scratch register 'UART_TOR' (timeout
* register). Enable it, and set TIOC (timeout interrupt
* comparator) to be 0x20 for correct operation.
*/
serial_port_out(port, UART_NPCM_TOR, UART_NPCM_TOIE | 0x20);
}
#ifdef CONFIG_SERIAL_8250_RSA
/*
* If this is an RSA port, see if we can kick it up to the
@ -2462,6 +2485,15 @@ static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up,
return quot_16 >> 4;
}
/* Nuvoton NPCM UARTs have a custom divisor calculation */
static unsigned int npcm_get_divisor(struct uart_8250_port *up,
unsigned int baud)
{
struct uart_port *port = &up->port;
return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2;
}
static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
unsigned int baud,
unsigned int *frac)
@ -2482,6 +2514,8 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
quot = 0x8002;
else if (up->port.type == PORT_XR17V35X)
quot = xr17v35x_get_divisor(up, baud, frac);
else if (up->port.type == PORT_NPCM)
quot = npcm_get_divisor(up, baud);
else
quot = uart_get_divisor(port, baud);

View File

@ -157,11 +157,12 @@ config SERIAL_8250_CS
If unsure, say N.
config SERIAL_8250_MEN_MCB
tristate "MEN Z125 UART device support"
tristate "MEN MCB UART device support"
depends on MCB && SERIAL_8250
help
This enables support for FPGA based UARTs found on many MEN
boards. This driver enables support for the Z125 UARTs.
boards. This driver enables support for the 16z025, 16z057
and 16z125 UARTs.
To compile this driver as a module, chose M here: the
module will be called 8250_men_mcb.

View File

@ -989,6 +989,21 @@ config SERIAL_MSM_CONSOLE
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
config SERIAL_QCOM_GENI
tristate "QCOM on-chip GENI based serial port support"
depends on ARCH_QCOM || COMPILE_TEST
depends on QCOM_GENI_SE
select SERIAL_CORE
config SERIAL_QCOM_GENI_CONSOLE
bool "QCOM GENI Serial Console support"
depends on SERIAL_QCOM_GENI=y
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
help
Serial console driver for Qualcomm Technologies Inc's GENI based
QUP hardware.
config SERIAL_VT8500
bool "VIA VT8500 on-chip serial port support"
depends on ARCH_VT8500

View File

@ -58,6 +58,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o

View File

@ -109,6 +109,20 @@ static unsigned int altera_uart_get_mctrl(struct uart_port *port)
return sigs;
}
static void altera_uart_update_ctrl_reg(struct altera_uart *pp)
{
unsigned short imr = pp->imr;
/*
* If the device doesn't have an irq, ensure that the irq bits are
* masked out to keep the irq line inactive.
*/
if (!pp->port.irq)
imr &= ALTERA_UART_CONTROL_TRBK_MSK | ALTERA_UART_CONTROL_RTS_MSK;
altera_uart_writel(&pp->port, imr, ALTERA_UART_CONTROL_REG);
}
static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
{
struct altera_uart *pp = container_of(port, struct altera_uart, port);
@ -118,7 +132,7 @@ static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
pp->imr |= ALTERA_UART_CONTROL_RTS_MSK;
else
pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_start_tx(struct uart_port *port)
@ -126,7 +140,7 @@ static void altera_uart_start_tx(struct uart_port *port)
struct altera_uart *pp = container_of(port, struct altera_uart, port);
pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_stop_tx(struct uart_port *port)
@ -134,7 +148,7 @@ static void altera_uart_stop_tx(struct uart_port *port)
struct altera_uart *pp = container_of(port, struct altera_uart, port);
pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_stop_rx(struct uart_port *port)
@ -142,7 +156,7 @@ static void altera_uart_stop_rx(struct uart_port *port)
struct altera_uart *pp = container_of(port, struct altera_uart, port);
pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_break_ctl(struct uart_port *port, int break_state)
@ -155,7 +169,7 @@ static void altera_uart_break_ctl(struct uart_port *port, int break_state)
pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK;
else
pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
spin_unlock_irqrestore(&port->lock, flags);
}
@ -262,7 +276,7 @@ static void altera_uart_tx_chars(struct altera_uart *pp)
if (xmit->head == xmit->tail) {
pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
}
}
@ -307,27 +321,27 @@ static int altera_uart_startup(struct uart_port *port)
{
struct altera_uart *pp = container_of(port, struct altera_uart, port);
unsigned long flags;
int ret;
if (!port->irq) {
timer_setup(&pp->tmr, altera_uart_timer, 0);
mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
return 0;
}
} else {
int ret;
ret = request_irq(port->irq, altera_uart_interrupt, 0,
DRV_NAME, port);
if (ret) {
pr_err(DRV_NAME ": unable to attach Altera UART %d "
"interrupt vector=%d\n", port->line, port->irq);
return ret;
ret = request_irq(port->irq, altera_uart_interrupt, 0,
DRV_NAME, port);
if (ret) {
pr_err(DRV_NAME ": unable to attach Altera UART %d "
"interrupt vector=%d\n", port->line, port->irq);
return ret;
}
}
spin_lock_irqsave(&port->lock, flags);
/* Enable RX interrupts now */
pp->imr = ALTERA_UART_CONTROL_RRDY_MSK;
writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
spin_unlock_irqrestore(&port->lock, flags);
@ -343,7 +357,7 @@ static void altera_uart_shutdown(struct uart_port *port)
/* Disable all interrupts now */
pp->imr = 0;
writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
spin_unlock_irqrestore(&port->lock, flags);
@ -432,7 +446,7 @@ static void altera_uart_console_putc(struct uart_port *port, int c)
ALTERA_UART_STATUS_TRDY_MSK))
cpu_relax();
writel(c, port->membase + ALTERA_UART_TXDATA_REG);
altera_uart_writel(port, c, ALTERA_UART_TXDATA_REG);
}
static void altera_uart_console_write(struct console *co, const char *s,
@ -502,13 +516,13 @@ static int __init altera_uart_earlycon_setup(struct earlycon_device *dev,
return -ENODEV;
/* Enable RX interrupts now */
writel(ALTERA_UART_CONTROL_RRDY_MSK,
port->membase + ALTERA_UART_CONTROL_REG);
altera_uart_writel(port, ALTERA_UART_CONTROL_RRDY_MSK,
ALTERA_UART_CONTROL_REG);
if (dev->baud) {
unsigned int baudclk = port->uartclk / dev->baud;
writel(baudclk, port->membase + ALTERA_UART_DIVISOR_REG);
altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG);
}
dev->con->write = altera_uart_earlycon_write;

View File

@ -593,6 +593,11 @@ static int arc_serial_probe(struct platform_device *pdev)
if (dev_id < 0)
dev_id = 0;
if (dev_id >= ARRAY_SIZE(arc_uart_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n", dev_id);
return -EINVAL;
}
uart = &arc_uart_ports[dev_id];
port = &uart->port;

View File

@ -2145,6 +2145,10 @@ static int lpuart_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
return ret;
}
if (ret >= ARRAY_SIZE(lpuart_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n", ret);
return -EINVAL;
}
sport->port.line = ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sport->port.membase = devm_ioremap_resource(&pdev->dev, res);

File diff suppressed because it is too large Load Diff

View File

@ -1318,7 +1318,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype,
/* Setup GPIO cotroller */
s->gpio.owner = THIS_MODULE;
s->gpio.parent = dev;
s->gpio.label = dev_name(dev);
s->gpio.label = devtype->name;
s->gpio.direction_input = max310x_gpio_direction_input;
s->gpio.get = max310x_gpio_get;
s->gpio.direction_output= max310x_gpio_direction_output;

View File

@ -65,7 +65,7 @@
#define STAT_FRM_ERR BIT(2)
#define STAT_PAR_ERR BIT(1)
#define STAT_OVR_ERR BIT(0)
#define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR | STAT_FRM_ERR\
#define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR \
| STAT_PAR_ERR | STAT_OVR_ERR)
#define UART_BRDV 0x10
@ -618,7 +618,7 @@ static void wait_for_xmitr(struct uart_port *port)
u32 val;
readl_poll_timeout_atomic(port->membase + UART_STAT, val,
(val & STAT_TX_EMP), 1, 10000);
(val & STAT_TX_RDY(port)), 1, 10000);
}
static void mvebu_uart_console_putchar(struct uart_port *port, int ch)

View File

@ -1663,6 +1663,10 @@ static int mxs_auart_probe(struct platform_device *pdev)
s->port.line = pdev->id < 0 ? 0 : pdev->id;
else if (ret < 0)
return ret;
if (s->port.line >= ARRAY_SIZE(auart_port)) {
dev_err(&pdev->dev, "serial%d out of range\n", s->port.line);
return -EINVAL;
}
if (of_id) {
pdev->id_entry = of_id->data;
@ -1674,8 +1678,10 @@ static int mxs_auart_probe(struct platform_device *pdev)
return ret;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
return -ENXIO;
if (!r) {
ret = -ENXIO;
goto out_disable_clks;
}
s->port.mapbase = r->start;
s->port.membase = ioremap(r->start, resource_size(r));
@ -1690,21 +1696,23 @@ static int mxs_auart_probe(struct platform_device *pdev)
s->mctrl_prev = 0;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
if (irq < 0) {
ret = irq;
goto out_disable_clks;
}
s->port.irq = irq;
ret = devm_request_irq(&pdev->dev, irq, mxs_auart_irq_handle, 0,
dev_name(&pdev->dev), s);
if (ret)
return ret;
goto out_disable_clks;
platform_set_drvdata(pdev, s);
ret = mxs_auart_init_gpios(s, &pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize GPIOs.\n");
return ret;
goto out_disable_clks;
}
/*
@ -1712,7 +1720,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
*/
ret = mxs_auart_request_gpio_irq(s);
if (ret)
return ret;
goto out_disable_clks;
auart_port[s->port.line] = s;
@ -1720,7 +1728,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
ret = uart_add_one_port(&auart_driver, &s->port);
if (ret)
goto out_disable_clks_free_qpio_irq;
goto out_free_qpio_irq;
/* ASM9260 don't have version reg */
if (is_asm9260_auart(s)) {
@ -1734,13 +1742,15 @@ static int mxs_auart_probe(struct platform_device *pdev)
return 0;
out_disable_clks_free_qpio_irq:
if (s->clk)
clk_disable_unprepare(s->clk_ahb);
if (s->clk_ahb)
clk_disable_unprepare(s->clk_ahb);
out_free_qpio_irq:
mxs_auart_free_gpio_irq(s);
auart_port[pdev->id] = NULL;
out_disable_clks:
if (is_asm9260_auart(s)) {
clk_disable_unprepare(s->clk);
clk_disable_unprepare(s->clk_ahb);
}
return ret;
}
@ -1751,6 +1761,10 @@ static int mxs_auart_remove(struct platform_device *pdev)
uart_remove_one_port(&auart_driver, &s->port);
auart_port[pdev->id] = NULL;
mxs_auart_free_gpio_irq(s);
if (is_asm9260_auart(s)) {
clk_disable_unprepare(s->clk);
clk_disable_unprepare(s->clk_ahb);
}
return 0;
}

View File

@ -885,6 +885,10 @@ static int serial_pxa_probe(struct platform_device *dev)
sport->port.line = dev->id;
else if (ret < 0)
goto err_clk;
if (sport->port.line >= ARRAY_SIZE(serial_pxa_ports)) {
dev_err(&dev->dev, "serial%d out of range\n", sport->port.line);
return -EINVAL;
}
snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);
sport->port.membase = ioremap(mmres->start, resource_size(mmres));

File diff suppressed because it is too large Load Diff

View File

@ -1818,6 +1818,10 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
dbg("s3c24xx_serial_probe(%p) %d\n", pdev, index);
if (index >= ARRAY_SIZE(s3c24xx_serial_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n", index);
return -EINVAL;
}
ourport = &s3c24xx_serial_ports[index];
ourport->drv_data = s3c24xx_get_driver_data(pdev);

View File

@ -1786,6 +1786,8 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i)
seq_printf(m, " brk:%d", uport->icount.brk);
if (uport->icount.overrun)
seq_printf(m, " oe:%d", uport->icount.overrun);
if (uport->icount.buf_overrun)
seq_printf(m, " bo:%d", uport->icount.buf_overrun);
#define INFOBIT(bit, str) \
if (uport->mctrl & (bit)) \

View File

@ -33,6 +33,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/ktime.h>
#include <linux/major.h>
#include <linux/module.h>
#include <linux/mm.h>
@ -143,8 +144,8 @@ struct sci_port {
void *rx_buf[2];
size_t buf_len_rx;
struct work_struct work_tx;
struct timer_list rx_timer;
unsigned int rx_timeout;
struct hrtimer rx_timer;
unsigned int rx_timeout; /* microseconds */
#endif
unsigned int rx_frame;
int rx_trigger;
@ -1231,6 +1232,15 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio)
}
}
static void start_hrtimer_us(struct hrtimer *hrt, unsigned long usec)
{
long sec = usec / 1000000;
long nsec = (usec % 1000000) * 1000;
ktime_t t = ktime_set(sec, nsec);
hrtimer_start(hrt, t, HRTIMER_MODE_REL);
}
static void sci_dma_rx_complete(void *arg)
{
struct sci_port *s = arg;
@ -1249,7 +1259,7 @@ static void sci_dma_rx_complete(void *arg)
if (active >= 0)
count = sci_dma_rx_push(s, s->rx_buf[active], s->buf_len_rx);
mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
start_hrtimer_us(&s->rx_timer, s->rx_timeout);
if (count)
tty_flip_buffer_push(&port->state->port);
@ -1393,9 +1403,9 @@ static void work_fn_tx(struct work_struct *work)
dma_async_issue_pending(chan);
}
static void rx_timer_fn(struct timer_list *t)
static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
{
struct sci_port *s = from_timer(s, t, rx_timer);
struct sci_port *s = container_of(t, struct sci_port, rx_timer);
struct dma_chan *chan = s->chan_rx;
struct uart_port *port = &s->port;
struct dma_tx_state state;
@ -1412,7 +1422,7 @@ static void rx_timer_fn(struct timer_list *t)
active = sci_dma_rx_find_active(s);
if (active < 0) {
spin_unlock_irqrestore(&port->lock, flags);
return;
return HRTIMER_NORESTART;
}
status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);
@ -1422,7 +1432,7 @@ static void rx_timer_fn(struct timer_list *t)
s->active_rx, active);
/* Let packet complete handler take care of the packet */
return;
return HRTIMER_NORESTART;
}
dmaengine_pause(chan);
@ -1437,7 +1447,7 @@ static void rx_timer_fn(struct timer_list *t)
if (status == DMA_COMPLETE) {
spin_unlock_irqrestore(&port->lock, flags);
dev_dbg(port->dev, "Transaction complete after DMA engine was stopped");
return;
return HRTIMER_NORESTART;
}
/* Handle incomplete DMA receive */
@ -1462,6 +1472,8 @@ static void rx_timer_fn(struct timer_list *t)
serial_port_out(port, SCSCR, scr | SCSCR_RIE);
spin_unlock_irqrestore(&port->lock, flags);
return HRTIMER_NORESTART;
}
static struct dma_chan *sci_request_dma_chan(struct uart_port *port,
@ -1573,7 +1585,8 @@ static void sci_request_dma(struct uart_port *port)
dma += s->buf_len_rx;
}
timer_setup(&s->rx_timer, rx_timer_fn, 0);
hrtimer_init(&s->rx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
s->rx_timer.function = rx_timer_fn;
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
sci_submit_rx(s);
@ -1632,9 +1645,9 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
/* Clear current interrupt */
serial_port_out(port, SCxSR,
ssr & ~(SCIF_DR | SCxSR_RDxF(port)));
dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n",
dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u us\n",
jiffies, s->rx_timeout);
mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
start_hrtimer_us(&s->rx_timer, s->rx_timeout);
return IRQ_HANDLED;
}
@ -1645,7 +1658,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
scif_set_rtrg(port, s->rx_trigger);
mod_timer(&s->rx_fifo_timer, jiffies + DIV_ROUND_UP(
s->rx_frame * s->rx_fifo_timeout, 1000));
s->rx_frame * HZ * s->rx_fifo_timeout, 1000000));
}
/* I think sci_receive_chars has to be called irrespective
@ -2081,7 +2094,7 @@ static void sci_shutdown(struct uart_port *port)
if (s->chan_rx) {
dev_dbg(port->dev, "%s(%d) deleting rx_timer\n", __func__,
port->line);
del_timer_sync(&s->rx_timer);
hrtimer_cancel(&s->rx_timer);
}
#endif
@ -2482,11 +2495,11 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
if (termios->c_cflag & PARENB)
bits++;
s->rx_frame = (100 * bits * HZ) / (baud / 10);
s->rx_frame = (10000 * bits) / (baud / 100);
#ifdef CONFIG_SERIAL_SH_SCI_DMA
s->rx_timeout = DIV_ROUND_UP(s->buf_len_rx * 2 * s->rx_frame, 1000);
if (s->rx_timeout < msecs_to_jiffies(20))
s->rx_timeout = msecs_to_jiffies(20);
s->rx_timeout = s->buf_len_rx * 2 * s->rx_frame;
if (s->rx_timeout < 20)
s->rx_timeout = 20;
#endif
if ((termios->c_cflag & CREAD) != 0)
@ -3098,6 +3111,10 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
dev_err(&pdev->dev, "failed to get alias id (%d)\n", id);
return NULL;
}
if (id >= ARRAY_SIZE(sci_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n", id);
return NULL;
}
sp = &sci_ports[id];
*dev_id = id;

View File

@ -1283,6 +1283,11 @@ static int sirfsoc_uart_probe(struct platform_device *pdev)
goto err;
}
sirfport->port.line = of_alias_get_id(np, "serial");
if (sirfport->port.line >= ARRAY_SIZE(sirf_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n",
sirfport->port.line);
return -EINVAL;
}
sirf_ports[sirfport->port.line] = sirfport;
sirfport->port.iotype = UPIO_MEM;
sirfport->port.flags = UPF_BOOT_AUTOCONF;

View File

@ -782,7 +782,9 @@ static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev)
if (!np)
return NULL;
id = of_alias_get_id(np, ASC_SERIAL_NAME);
id = of_alias_get_id(np, "serial");
if (id < 0)
id = of_alias_get_id(np, ASC_SERIAL_NAME);
if (id < 0)
id = 0;

View File

@ -62,6 +62,113 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits)
writel_relaxed(val, port->membase + reg);
}
static void stm32_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE,
u32 delay_DDE, u32 baud)
{
u32 rs485_deat_dedt;
u32 rs485_deat_dedt_max = (USART_CR1_DEAT_MASK >> USART_CR1_DEAT_SHIFT);
bool over8;
*cr3 |= USART_CR3_DEM;
over8 = *cr1 & USART_CR1_OVER8;
if (over8)
rs485_deat_dedt = delay_ADE * baud * 8;
else
rs485_deat_dedt = delay_ADE * baud * 16;
rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
rs485_deat_dedt_max : rs485_deat_dedt;
rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEAT_SHIFT) &
USART_CR1_DEAT_MASK;
*cr1 |= rs485_deat_dedt;
if (over8)
rs485_deat_dedt = delay_DDE * baud * 8;
else
rs485_deat_dedt = delay_DDE * baud * 16;
rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
rs485_deat_dedt_max : rs485_deat_dedt;
rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEDT_SHIFT) &
USART_CR1_DEDT_MASK;
*cr1 |= rs485_deat_dedt;
}
static int stm32_config_rs485(struct uart_port *port,
struct serial_rs485 *rs485conf)
{
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct stm32_usart_config *cfg = &stm32_port->info->cfg;
u32 usartdiv, baud, cr1, cr3;
bool over8;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
port->rs485 = *rs485conf;
rs485conf->flags |= SER_RS485_RX_DURING_TX;
if (rs485conf->flags & SER_RS485_ENABLED) {
cr1 = readl_relaxed(port->membase + ofs->cr1);
cr3 = readl_relaxed(port->membase + ofs->cr3);
usartdiv = readl_relaxed(port->membase + ofs->brr);
usartdiv = usartdiv & GENMASK(15, 0);
over8 = cr1 & USART_CR1_OVER8;
if (over8)
usartdiv = usartdiv | (usartdiv & GENMASK(4, 0))
<< USART_BRR_04_R_SHIFT;
baud = DIV_ROUND_CLOSEST(port->uartclk, usartdiv);
stm32_config_reg_rs485(&cr1, &cr3,
rs485conf->delay_rts_before_send,
rs485conf->delay_rts_after_send, baud);
if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
cr3 &= ~USART_CR3_DEP;
rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
} else {
cr3 |= USART_CR3_DEP;
rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
}
writel_relaxed(cr3, port->membase + ofs->cr3);
writel_relaxed(cr1, port->membase + ofs->cr1);
} else {
stm32_clr_bits(port, ofs->cr3, USART_CR3_DEM | USART_CR3_DEP);
stm32_clr_bits(port, ofs->cr1,
USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
}
stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}
static int stm32_init_rs485(struct uart_port *port,
struct platform_device *pdev)
{
struct serial_rs485 *rs485conf = &port->rs485;
rs485conf->flags = 0;
rs485conf->delay_rts_before_send = 0;
rs485conf->delay_rts_after_send = 0;
if (!pdev->dev.of_node)
return -ENODEV;
uart_get_rs485_mode(&pdev->dev, rs485conf);
return 0;
}
static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res,
bool threaded)
{
@ -498,6 +605,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct stm32_usart_config *cfg = &stm32_port->info->cfg;
struct serial_rs485 *rs485conf = &port->rs485;
unsigned int baud;
u32 usartdiv, mantissa, fraction, oversampling;
tcflag_t cflag = termios->c_cflag;
@ -515,7 +623,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
writel_relaxed(0, port->membase + ofs->cr1);
cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
cr1 |= BIT(cfg->uart_enable_bit);
if (stm32_port->fifoen)
cr1 |= USART_CR1_FIFOEN;
cr2 = 0;
@ -553,9 +661,11 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
*/
if (usartdiv < 16) {
oversampling = 8;
cr1 |= USART_CR1_OVER8;
stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8);
} else {
oversampling = 16;
cr1 &= ~USART_CR1_OVER8;
stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8);
}
@ -592,10 +702,28 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
if (stm32_port->rx_ch)
cr3 |= USART_CR3_DMAR;
if (rs485conf->flags & SER_RS485_ENABLED) {
stm32_config_reg_rs485(&cr1, &cr3,
rs485conf->delay_rts_before_send,
rs485conf->delay_rts_after_send, baud);
if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
cr3 &= ~USART_CR3_DEP;
rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
} else {
cr3 |= USART_CR3_DEP;
rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
}
} else {
cr3 &= ~(USART_CR3_DEM | USART_CR3_DEP);
cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
}
writel_relaxed(cr3, port->membase + ofs->cr3);
writel_relaxed(cr2, port->membase + ofs->cr2);
writel_relaxed(cr1, port->membase + ofs->cr1);
stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
spin_unlock_irqrestore(&port->lock, flags);
}
@ -681,6 +809,10 @@ static int stm32_init_port(struct stm32_port *stm32port,
port->ops = &stm32_uart_ops;
port->dev = &pdev->dev;
port->irq = platform_get_irq(pdev, 0);
port->rs485_config = stm32_config_rs485;
stm32_init_rs485(port, pdev);
stm32port->wakeirq = platform_get_irq(pdev, 1);
stm32port->fifoen = stm32port->info->cfg.has_fifo;

View File

@ -135,6 +135,7 @@ struct stm32_usart_info stm32h7_info = {
#define USART_BRR_DIV_F_MASK GENMASK(3, 0)
#define USART_BRR_DIV_M_MASK GENMASK(15, 4)
#define USART_BRR_DIV_M_SHIFT 4
#define USART_BRR_04_R_SHIFT 1
/* USART_CR1 */
#define USART_CR1_SBK BIT(0)
@ -162,6 +163,8 @@ struct stm32_usart_info stm32h7_info = {
#define USART_CR1_M1 BIT(28) /* F7 */
#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27))
#define USART_CR1_FIFOEN BIT(29) /* H7 */
#define USART_CR1_DEAT_SHIFT 21
#define USART_CR1_DEDT_SHIFT 16
/* USART_CR2 */
#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */

View File

@ -1110,7 +1110,7 @@ static struct uart_port *cdns_uart_get_port(int id)
struct uart_port *port;
/* Try the given port id if failed use default method */
if (cdns_uart_port[id].mapbase != 0) {
if (id < CDNS_UART_NR_PORTS && cdns_uart_port[id].mapbase != 0) {
/* Find the next unused port */
for (id = 0; id < CDNS_UART_NR_PORTS; id++)
if (cdns_uart_port[id].mapbase == 0)

View File

@ -1354,6 +1354,11 @@ static void csi_m(struct vc_data *vc)
case 3:
vc->vc_italic = 1;
break;
case 21:
/*
* No console drivers support double underline, so
* convert it to a single underline.
*/
case 4:
vc->vc_underline = 1;
break;
@ -1389,7 +1394,6 @@ static void csi_m(struct vc_data *vc)
vc->vc_disp_ctrl = 1;
vc->vc_toggle_meta = 1;
break;
case 21:
case 22:
vc->vc_intensity = 1;
break;

View File

@ -1217,7 +1217,7 @@ sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
/* Interface routine */
static int
sisusbcon_font_set(struct vc_data *c, struct console_font *font,
unsigned flags)
unsigned int flags)
{
struct sisusb_usb_data *sisusb;
unsigned charcount = font->charcount;
@ -1338,29 +1338,65 @@ static void sisusbdummycon_init(struct vc_data *vc, int init)
vc_resize(vc, 80, 25);
}
static int sisusbdummycon_dummy(void)
static void sisusbdummycon_deinit(struct vc_data *vc) { }
static void sisusbdummycon_clear(struct vc_data *vc, int sy, int sx,
int height, int width) { }
static void sisusbdummycon_putc(struct vc_data *vc, int c, int ypos,
int xpos) { }
static void sisusbdummycon_putcs(struct vc_data *vc, const unsigned short *s,
int count, int ypos, int xpos) { }
static void sisusbdummycon_cursor(struct vc_data *vc, int mode) { }
static bool sisusbdummycon_scroll(struct vc_data *vc, unsigned int top,
unsigned int bottom, enum con_scroll dir,
unsigned int lines)
{
return 0;
return false;
}
#define SISUSBCONDUMMY (void *)sisusbdummycon_dummy
static int sisusbdummycon_switch(struct vc_data *vc)
{
return 0;
}
static int sisusbdummycon_blank(struct vc_data *vc, int blank, int mode_switch)
{
return 0;
}
static int sisusbdummycon_font_set(struct vc_data *vc,
struct console_font *font,
unsigned int flags)
{
return 0;
}
static int sisusbdummycon_font_default(struct vc_data *vc,
struct console_font *font, char *name)
{
return 0;
}
static int sisusbdummycon_font_copy(struct vc_data *vc, int con)
{
return 0;
}
static const struct consw sisusb_dummy_con = {
.owner = THIS_MODULE,
.con_startup = sisusbdummycon_startup,
.con_init = sisusbdummycon_init,
.con_deinit = SISUSBCONDUMMY,
.con_clear = SISUSBCONDUMMY,
.con_putc = SISUSBCONDUMMY,
.con_putcs = SISUSBCONDUMMY,
.con_cursor = SISUSBCONDUMMY,
.con_scroll = SISUSBCONDUMMY,
.con_switch = SISUSBCONDUMMY,
.con_blank = SISUSBCONDUMMY,
.con_font_set = SISUSBCONDUMMY,
.con_font_get = SISUSBCONDUMMY,
.con_font_default = SISUSBCONDUMMY,
.con_font_copy = SISUSBCONDUMMY,
.con_deinit = sisusbdummycon_deinit,
.con_clear = sisusbdummycon_clear,
.con_putc = sisusbdummycon_putc,
.con_putcs = sisusbdummycon_putcs,
.con_cursor = sisusbdummycon_cursor,
.con_scroll = sisusbdummycon_scroll,
.con_switch = sisusbdummycon_switch,
.con_blank = sisusbdummycon_blank,
.con_font_set = sisusbdummycon_font_set,
.con_font_default = sisusbdummycon_font_default,
.con_font_copy = sisusbdummycon_font_copy,
};
int

View File

@ -41,12 +41,47 @@ static void dummycon_init(struct vc_data *vc, int init)
vc_resize(vc, DUMMY_COLUMNS, DUMMY_ROWS);
}
static int dummycon_dummy(void)
static void dummycon_deinit(struct vc_data *vc) { }
static void dummycon_clear(struct vc_data *vc, int sy, int sx, int height,
int width) { }
static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
int count, int ypos, int xpos) { }
static void dummycon_cursor(struct vc_data *vc, int mode) { }
static bool dummycon_scroll(struct vc_data *vc, unsigned int top,
unsigned int bottom, enum con_scroll dir,
unsigned int lines)
{
return 0;
return false;
}
#define DUMMY (void *)dummycon_dummy
static int dummycon_switch(struct vc_data *vc)
{
return 0;
}
static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch)
{
return 0;
}
static int dummycon_font_set(struct vc_data *vc, struct console_font *font,
unsigned int flags)
{
return 0;
}
static int dummycon_font_default(struct vc_data *vc,
struct console_font *font, char *name)
{
return 0;
}
static int dummycon_font_copy(struct vc_data *vc, int con)
{
return 0;
}
/*
* The console `switch' structure for the dummy console
@ -55,19 +90,19 @@ static int dummycon_dummy(void)
*/
const struct consw dummy_con = {
.owner = THIS_MODULE,
.con_startup = dummycon_startup,
.con_init = dummycon_init,
.con_deinit = DUMMY,
.con_clear = DUMMY,
.con_putc = DUMMY,
.con_putcs = DUMMY,
.con_cursor = DUMMY,
.con_scroll = DUMMY,
.con_switch = DUMMY,
.con_blank = DUMMY,
.con_font_set = DUMMY,
.con_font_default = DUMMY,
.con_font_copy = DUMMY,
.owner = THIS_MODULE,
.con_startup = dummycon_startup,
.con_init = dummycon_init,
.con_deinit = dummycon_deinit,
.con_clear = dummycon_clear,
.con_putc = dummycon_putc,
.con_putcs = dummycon_putcs,
.con_cursor = dummycon_cursor,
.con_scroll = dummycon_scroll,
.con_switch = dummycon_switch,
.con_blank = dummycon_blank,
.con_font_set = dummycon_font_set,
.con_font_default = dummycon_font_default,
.con_font_copy = dummycon_font_copy,
};
EXPORT_SYMBOL_GPL(dummy_con);

View File

@ -673,12 +673,12 @@ static bool newport_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
return true;
}
static int newport_dummy(struct vc_data *c)
static int newport_set_origin(struct vc_data *vc)
{
return 0;
}
#define DUMMY (void *) newport_dummy
static void newport_save_screen(struct vc_data *vc) { }
const struct consw newport_con = {
.owner = THIS_MODULE,
@ -694,8 +694,8 @@ const struct consw newport_con = {
.con_blank = newport_blank,
.con_font_set = newport_font_set,
.con_font_default = newport_font_default,
.con_set_origin = DUMMY,
.con_save_screen = DUMMY
.con_set_origin = newport_set_origin,
.con_save_screen = newport_save_screen
};
static int newport_probe(struct gio_device *dev,

View File

@ -1272,7 +1272,8 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
return 0;
}
static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigned flags)
static int vgacon_font_set(struct vc_data *c, struct console_font *font,
unsigned int flags)
{
unsigned charcount = font->charcount;
int rc;
@ -1407,21 +1408,20 @@ static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
* The console `switch' structure for the VGA based console
*/
static int vgacon_dummy(struct vc_data *c)
{
return 0;
}
#define DUMMY (void *) vgacon_dummy
static void vgacon_clear(struct vc_data *vc, int sy, int sx, int height,
int width) { }
static void vgacon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
static void vgacon_putcs(struct vc_data *vc, const unsigned short *s,
int count, int ypos, int xpos) { }
const struct consw vga_con = {
.owner = THIS_MODULE,
.con_startup = vgacon_startup,
.con_init = vgacon_init,
.con_deinit = vgacon_deinit,
.con_clear = DUMMY,
.con_putc = DUMMY,
.con_putcs = DUMMY,
.con_clear = vgacon_clear,
.con_putc = vgacon_putc,
.con_putcs = vgacon_putcs,
.con_cursor = vgacon_cursor,
.con_scroll = vgacon_scroll,
.con_switch = vgacon_switch,

View File

@ -2595,7 +2595,8 @@ static int fbcon_copy_font(struct vc_data *vc, int con)
* is ever implemented.
*/
static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags)
static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
unsigned int flags)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
unsigned charcount = font->charcount;

View File

@ -138,10 +138,6 @@ static int devpts_ptmx_path(struct path *path)
struct super_block *sb;
int err;
/* Has the devpts filesystem already been found? */
if (path->mnt->mnt_sb->s_magic == DEVPTS_SUPER_MAGIC)
return 0;
/* Is a devpts filesystem at "pts" in the same directory? */
err = path_pts(path);
if (err)
@ -156,25 +152,53 @@ static int devpts_ptmx_path(struct path *path)
return 0;
}
/*
* Try to find a suitable devpts filesystem. We support the following
* scenarios:
* - The ptmx device node is located in the same directory as the devpts
* mount where the pts device nodes are located.
* This is e.g. the case when calling open on the /dev/pts/ptmx device
* node when the devpts filesystem is mounted at /dev/pts.
* - The ptmx device node is located outside the devpts filesystem mount
* where the pts device nodes are located. For example, the ptmx device
* is a symlink, separate device node, or bind-mount.
* A supported scenario is bind-mounting /dev/pts/ptmx to /dev/ptmx and
* then calling open on /dev/ptmx. In this case a suitable pts
* subdirectory can be found in the common parent directory /dev of the
* devpts mount and the ptmx bind-mount, after resolving the /dev/ptmx
* bind-mount.
* If no suitable pts subdirectory can be found this function will fail.
* This is e.g. the case when bind-mounting /dev/pts/ptmx to /ptmx.
*/
struct vfsmount *devpts_mntget(struct file *filp, struct pts_fs_info *fsi)
{
struct path path;
int err;
int err = 0;
path = filp->f_path;
path_get(&path);
err = devpts_ptmx_path(&path);
/* Walk upward while the start point is a bind mount of
* a single file.
*/
while (path.mnt->mnt_root == path.dentry)
if (follow_up(&path) == 0)
break;
/* devpts_ptmx_path() finds a devpts fs or returns an error. */
if ((path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) ||
(DEVPTS_SB(path.mnt->mnt_sb) != fsi))
err = devpts_ptmx_path(&path);
dput(path.dentry);
if (err) {
mntput(path.mnt);
return ERR_PTR(err);
if (!err) {
if (DEVPTS_SB(path.mnt->mnt_sb) == fsi)
return path.mnt;
err = -ENODEV;
}
if (DEVPTS_SB(path.mnt->mnt_sb) != fsi) {
mntput(path.mnt);
return ERR_PTR(-ENODEV);
}
return path.mnt;
mntput(path.mnt);
return ERR_PTR(err);
}
struct pts_fs_info *devpts_acquire(struct file *filp)
@ -182,15 +206,19 @@ struct pts_fs_info *devpts_acquire(struct file *filp)
struct pts_fs_info *result;
struct path path;
struct super_block *sb;
int err;
path = filp->f_path;
path_get(&path);
err = devpts_ptmx_path(&path);
if (err) {
result = ERR_PTR(err);
goto out;
/* Has the devpts filesystem already been found? */
if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) {
int err;
err = devpts_ptmx_path(&path);
if (err) {
result = ERR_PTR(err);
goto out;
}
}
/*

View File

@ -46,46 +46,52 @@ enum con_scroll {
struct consw {
struct module *owner;
const char *(*con_startup)(void);
void (*con_init)(struct vc_data *, int);
void (*con_deinit)(struct vc_data *);
void (*con_clear)(struct vc_data *, int, int, int, int);
void (*con_putc)(struct vc_data *, int, int, int);
void (*con_putcs)(struct vc_data *, const unsigned short *, int, int, int);
void (*con_cursor)(struct vc_data *, int);
bool (*con_scroll)(struct vc_data *, unsigned int top,
void (*con_init)(struct vc_data *vc, int init);
void (*con_deinit)(struct vc_data *vc);
void (*con_clear)(struct vc_data *vc, int sy, int sx, int height,
int width);
void (*con_putc)(struct vc_data *vc, int c, int ypos, int xpos);
void (*con_putcs)(struct vc_data *vc, const unsigned short *s,
int count, int ypos, int xpos);
void (*con_cursor)(struct vc_data *vc, int mode);
bool (*con_scroll)(struct vc_data *vc, unsigned int top,
unsigned int bottom, enum con_scroll dir,
unsigned int lines);
int (*con_switch)(struct vc_data *);
int (*con_blank)(struct vc_data *, int, int);
int (*con_font_set)(struct vc_data *, struct console_font *, unsigned);
int (*con_font_get)(struct vc_data *, struct console_font *);
int (*con_font_default)(struct vc_data *, struct console_font *, char *);
int (*con_font_copy)(struct vc_data *, int);
int (*con_resize)(struct vc_data *, unsigned int, unsigned int,
unsigned int);
void (*con_set_palette)(struct vc_data *,
int (*con_switch)(struct vc_data *vc);
int (*con_blank)(struct vc_data *vc, int blank, int mode_switch);
int (*con_font_set)(struct vc_data *vc, struct console_font *font,
unsigned int flags);
int (*con_font_get)(struct vc_data *vc, struct console_font *font);
int (*con_font_default)(struct vc_data *vc,
struct console_font *font, char *name);
int (*con_font_copy)(struct vc_data *vc, int con);
int (*con_resize)(struct vc_data *vc, unsigned int width,
unsigned int height, unsigned int user);
void (*con_set_palette)(struct vc_data *vc,
const unsigned char *table);
void (*con_scrolldelta)(struct vc_data *, int lines);
int (*con_set_origin)(struct vc_data *);
void (*con_save_screen)(struct vc_data *);
u8 (*con_build_attr)(struct vc_data *, u8, u8, u8, u8, u8, u8);
void (*con_invert_region)(struct vc_data *, u16 *, int);
u16 *(*con_screen_pos)(struct vc_data *, int);
unsigned long (*con_getxy)(struct vc_data *, unsigned long, int *, int *);
void (*con_scrolldelta)(struct vc_data *vc, int lines);
int (*con_set_origin)(struct vc_data *vc);
void (*con_save_screen)(struct vc_data *vc);
u8 (*con_build_attr)(struct vc_data *vc, u8 color, u8 intensity,
u8 blink, u8 underline, u8 reverse, u8 italic);
void (*con_invert_region)(struct vc_data *vc, u16 *p, int count);
u16 *(*con_screen_pos)(struct vc_data *vc, int offset);
unsigned long (*con_getxy)(struct vc_data *vc, unsigned long position,
int *px, int *py);
/*
* Flush the video console driver's scrollback buffer
*/
void (*con_flush_scrollback)(struct vc_data *);
void (*con_flush_scrollback)(struct vc_data *vc);
/*
* Prepare the console for the debugger. This includes, but is not
* limited to, unblanking the console, loading an appropriate
* palette, and allowing debugger generated output.
*/
int (*con_debug_enter)(struct vc_data *);
int (*con_debug_enter)(struct vc_data *vc);
/*
* Restore the console to its pre-debug state as closely as possible.
*/
int (*con_debug_leave)(struct vc_data *);
int (*con_debug_leave)(struct vc_data *vc);
};
extern const struct consw *conswitchp;

View File

@ -2557,6 +2557,9 @@
#define PCI_DEVICE_ID_TEHUTI_3010 0x3010
#define PCI_DEVICE_ID_TEHUTI_3014 0x3014
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
#define PCI_VENDOR_ID_HINT 0x3388
#define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013

View File

@ -379,7 +379,7 @@ extern int of_setup_earlycon(const struct earlycon_id *match,
extern bool earlycon_acpi_spcr_enable __initdata;
int setup_earlycon(char *buf);
#else
static const bool earlycon_acpi_spcr_enable;
static const bool earlycon_acpi_spcr_enable EARLYCON_USED_OR_UNUSED;
static inline int setup_earlycon(char *buf) { return 0; }
#endif

View File

@ -76,6 +76,9 @@
#define PORT_SUNZILOG 38
#define PORT_SUNSAB 39
/* Nuvoton UART */
#define PORT_NPCM 40
/* Intel EG20 */
#define PORT_PCH_8LINE 44
#define PORT_PCH_2LINE 45

View File

@ -7,6 +7,7 @@ TARGETS += cpufreq
TARGETS += cpu-hotplug
TARGETS += efivarfs
TARGETS += exec
TARGETS += filesystems
TARGETS += firmware
TARGETS += ftrace
TARGETS += futex

View File

@ -1 +1,2 @@
dnotify_test
devpts_pts

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
TEST_PROGS := dnotify_test
TEST_PROGS := dnotify_test devpts_pts
all: $(TEST_PROGS)
include ../lib.mk

View File

@ -0,0 +1,313 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/wait.h>
static bool terminal_dup2(int duplicate, int original)
{
int ret;
ret = dup2(duplicate, original);
if (ret < 0)
return false;
return true;
}
static int terminal_set_stdfds(int fd)
{
int i;
if (fd < 0)
return 0;
for (i = 0; i < 3; i++)
if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO,
STDERR_FILENO}[i]))
return -1;
return 0;
}
static int login_pty(int fd)
{
int ret;
setsid();
ret = ioctl(fd, TIOCSCTTY, NULL);
if (ret < 0)
return -1;
ret = terminal_set_stdfds(fd);
if (ret < 0)
return -1;
if (fd > STDERR_FILENO)
close(fd);
return 0;
}
static int wait_for_pid(pid_t pid)
{
int status, ret;
again:
ret = waitpid(pid, &status, 0);
if (ret == -1) {
if (errno == EINTR)
goto again;
return -1;
}
if (ret != pid)
goto again;
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
return -1;
return 0;
}
static int resolve_procfd_symlink(int fd, char *buf, size_t buflen)
{
int ret;
char procfd[4096];
ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd);
if (ret < 0 || ret >= 4096)
return -1;
ret = readlink(procfd, buf, buflen);
if (ret < 0 || (size_t)ret >= buflen)
return -1;
buf[ret] = '\0';
return 0;
}
static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents)
{
int ret;
int master = -1, slave = -1, fret = -1;
master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC);
if (master < 0) {
fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx,
strerror(errno));
return -1;
}
/*
* grantpt() makes assumptions about /dev/pts/ so ignore it. It's also
* not really needed.
*/
ret = unlockpt(master);
if (ret < 0) {
fprintf(stderr, "Failed to unlock terminal\n");
goto do_cleanup;
}
#ifdef TIOCGPTPEER
slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);
#endif
if (slave < 0) {
if (errno == EINVAL) {
fprintf(stderr, "TIOCGPTPEER is not supported. "
"Skipping test.\n");
fret = EXIT_SUCCESS;
}
fprintf(stderr, "Failed to perform TIOCGPTPEER ioctl\n");
goto do_cleanup;
}
pid_t pid = fork();
if (pid < 0)
goto do_cleanup;
if (pid == 0) {
char buf[4096];
ret = login_pty(slave);
if (ret < 0) {
fprintf(stderr, "Failed to setup terminal\n");
_exit(EXIT_FAILURE);
}
ret = resolve_procfd_symlink(STDIN_FILENO, buf, sizeof(buf));
if (ret < 0) {
fprintf(stderr, "Failed to retrieve pathname of pts "
"slave file descriptor\n");
_exit(EXIT_FAILURE);
}
if (strncmp(expected_procfd_contents, buf,
strlen(expected_procfd_contents)) != 0) {
fprintf(stderr, "Received invalid contents for "
"\"/proc/<pid>/fd/%d\" symlink: %s\n",
STDIN_FILENO, buf);
_exit(-1);
}
fprintf(stderr, "Contents of \"/proc/<pid>/fd/%d\" "
"symlink are valid: %s\n", STDIN_FILENO, buf);
_exit(EXIT_SUCCESS);
}
ret = wait_for_pid(pid);
if (ret < 0)
goto do_cleanup;
fret = EXIT_SUCCESS;
do_cleanup:
if (master >= 0)
close(master);
if (slave >= 0)
close(slave);
return fret;
}
static int verify_non_standard_devpts_mount(void)
{
char *mntpoint;
int ret = -1;
char devpts[] = P_tmpdir "/devpts_fs_XXXXXX";
char ptmx[] = P_tmpdir "/devpts_fs_XXXXXX/ptmx";
ret = umount("/dev/pts");
if (ret < 0) {
fprintf(stderr, "Failed to unmount \"/dev/pts\": %s\n",
strerror(errno));
return -1;
}
(void)umount("/dev/ptmx");
mntpoint = mkdtemp(devpts);
if (!mntpoint) {
fprintf(stderr, "Failed to create temporary mountpoint: %s\n",
strerror(errno));
return -1;
}
ret = mount("devpts", mntpoint, "devpts", MS_NOSUID | MS_NOEXEC,
"newinstance,ptmxmode=0666,mode=0620,gid=5");
if (ret < 0) {
fprintf(stderr, "Failed to mount devpts fs to \"%s\" in new "
"mount namespace: %s\n", mntpoint,
strerror(errno));
unlink(mntpoint);
return -1;
}
ret = snprintf(ptmx, sizeof(ptmx), "%s/ptmx", devpts);
if (ret < 0 || (size_t)ret >= sizeof(ptmx)) {
unlink(mntpoint);
return -1;
}
ret = do_tiocgptpeer(ptmx, mntpoint);
unlink(mntpoint);
if (ret < 0)
return -1;
return 0;
}
static int verify_ptmx_bind_mount(void)
{
int ret;
ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL);
if (ret < 0) {
fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
"\"/dev/ptmx\" mount namespace\n");
return -1;
}
ret = do_tiocgptpeer("/dev/ptmx", "/dev/pts/");
if (ret < 0)
return -1;
return 0;
}
static int verify_invalid_ptmx_bind_mount(void)
{
int ret;
char mntpoint_fd;
char ptmx[] = P_tmpdir "/devpts_ptmx_XXXXXX";
mntpoint_fd = mkstemp(ptmx);
if (mntpoint_fd < 0) {
fprintf(stderr, "Failed to create temporary directory: %s\n",
strerror(errno));
return -1;
}
ret = mount("/dev/pts/ptmx", ptmx, NULL, MS_BIND, NULL);
close(mntpoint_fd);
if (ret < 0) {
fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
"\"%s\" mount namespace\n", ptmx);
return -1;
}
ret = do_tiocgptpeer(ptmx, "/dev/pts/");
if (ret == 0)
return -1;
return 0;
}
int main(int argc, char *argv[])
{
int ret;
if (!isatty(STDIN_FILENO)) {
fprintf(stderr, "Standard input file desciptor is not attached "
"to a terminal. Skipping test\n");
exit(EXIT_FAILURE);
}
ret = unshare(CLONE_NEWNS);
if (ret < 0) {
fprintf(stderr, "Failed to unshare mount namespace\n");
exit(EXIT_FAILURE);
}
ret = mount("", "/", NULL, MS_PRIVATE | MS_REC, 0);
if (ret < 0) {
fprintf(stderr, "Failed to make \"/\" MS_PRIVATE in new mount "
"namespace\n");
exit(EXIT_FAILURE);
}
ret = verify_ptmx_bind_mount();
if (ret < 0)
exit(EXIT_FAILURE);
ret = verify_invalid_ptmx_bind_mount();
if (ret < 0)
exit(EXIT_FAILURE);
ret = verify_non_standard_devpts_mount();
if (ret < 0)
exit(EXIT_FAILURE);
exit(EXIT_SUCCESS);
}