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