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:
commit
9abf8acea2
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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 @@
|
||||
};
|
||||
|
||||
aliases {
|
||||
ttyAS0 = &sbc_serial0;
|
||||
serial0 = &sbc_serial0;
|
||||
ethernet0 = ðernet0;
|
||||
};
|
||||
|
||||
|
@ -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 @@
|
||||
};
|
||||
|
||||
aliases {
|
||||
ttyAS0 = &sbc_serial0;
|
||||
serial0 = &sbc_serial0;
|
||||
ethernet0 = ðernet0;
|
||||
};
|
||||
|
||||
|
@ -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 @@
|
||||
};
|
||||
|
||||
aliases {
|
||||
ttyAS1 = &uart1;
|
||||
serial1 = &uart1;
|
||||
ethernet0 = ðernet0;
|
||||
};
|
||||
|
||||
|
@ -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 @@
|
||||
};
|
||||
|
||||
aliases {
|
||||
ttyAS0 = &sbc_serial0;
|
||||
serial0 = &sbc_serial0;
|
||||
ethernet0 = ðernet0;
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
60
drivers/tty/hvc/hvc_riscv_sbi.c
Normal file
60
drivers/tty/hvc/hvc_riscv_sbi.c
Normal 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);
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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 @@ unlock:
|
||||
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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
|
1158
drivers/tty/serial/qcom_geni_serial.c
Normal file
1158
drivers/tty/serial/qcom_geni_serial.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
@ -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)) \
|
||||
|
@ -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 @@ done:
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -1217,7 +1217,7 @@ font_op_error:
|
||||
/* 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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -7,6 +7,7 @@ TARGETS += cpufreq
|
||||
TARGETS += cpu-hotplug
|
||||
TARGETS += efivarfs
|
||||
TARGETS += exec
|
||||
TARGETS += filesystems
|
||||
TARGETS += firmware
|
||||
TARGETS += ftrace
|
||||
TARGETS += futex
|
||||
|
@ -1 +1,2 @@
|
||||
dnotify_test
|
||||
devpts_pts
|
||||
|
@ -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
|
||||
|
313
tools/testing/selftests/filesystems/devpts_pts.c
Normal file
313
tools/testing/selftests/filesystems/devpts_pts.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user