TTY/Serial patches for 4.12-rc1
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. There will be a merge conflict with include/linux/serdev.h coming from the bluetooth tree merge, which we knew about, as we wanted some of the serdev changes to go in through that tree. I'll send the expected merge result as a follow-on message. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iGwEABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWRA9rw8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yn8OwCXSoCtZMGl25ohu1osCL5G0UEMtgCg2Z9k7hDk LpQTTN98hHn/VwM47ro= =X8sk -----END PGP SIGNATURE----- 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:
commit
8f3207c7ea
|
@ -1,11 +1,14 @@
|
||||||
Spreadtrum SoC Platforms Device Tree Bindings
|
Spreadtrum SoC Platforms Device Tree Bindings
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
Sharkl64 is a Spreadtrum's SoC Platform which is based
|
SC9836 openphone Board
|
||||||
on ARM 64-bit processor.
|
|
||||||
|
|
||||||
SC9836 openphone board with SC9836 SoC based on the
|
|
||||||
Sharkl64 Platform shall have the following properties.
|
|
||||||
|
|
||||||
Required root node properties:
|
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
|
* Spreadtrum serial UART
|
||||||
|
|
||||||
Required properties:
|
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
|
- reg: offset and length of the register set for the device
|
||||||
- interrupts: exactly one interrupt specifier
|
- interrupts: exactly one interrupt specifier
|
||||||
- clocks: phandles to input clocks.
|
- 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>
|
M: Richard Genoud <richard.genoud@gmail.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/tty/serial/atmel_serial.c
|
F: drivers/tty/serial/atmel_serial.c
|
||||||
F: include/linux/atmel_serial.h
|
F: drivers/tty/serial/atmel_serial.h
|
||||||
|
|
||||||
MICROCHIP / ATMEL DMA DRIVER
|
MICROCHIP / ATMEL DMA DRIVER
|
||||||
M: Ludovic Desroches <ludovic.desroches@microchip.com>
|
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 \
|
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_LEGACY_PTYS) += pty.o
|
||||||
obj-$(CONFIG_UNIX98_PTYS) += pty.o
|
obj-$(CONFIG_UNIX98_PTYS) += pty.o
|
||||||
obj-$(CONFIG_AUDIT) += tty_audit.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);
|
tty = tty_port_tty_get(&hp->port);
|
||||||
|
|
||||||
|
console_lock();
|
||||||
spin_lock_irqsave(&hp->lock, flags);
|
spin_lock_irqsave(&hp->lock, flags);
|
||||||
if (hp->index < MAX_NR_HVC_CONSOLES) {
|
if (hp->index < MAX_NR_HVC_CONSOLES) {
|
||||||
console_lock();
|
|
||||||
vtermnos[hp->index] = -1;
|
vtermnos[hp->index] = -1;
|
||||||
cons_ops[hp->index] = NULL;
|
cons_ops[hp->index] = NULL;
|
||||||
console_unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't whack hp->irq because tty_hangup() will need to free the irq. */
|
/* Don't whack hp->irq because tty_hangup() will need to free the irq. */
|
||||||
|
|
||||||
spin_unlock_irqrestore(&hp->lock, flags);
|
spin_unlock_irqrestore(&hp->lock, flags);
|
||||||
|
console_unlock();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We 'put' the instance that was grabbed when the kref instance
|
* 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);
|
rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan);
|
||||||
if (rc)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,18 +89,14 @@ module_param(debug, int, 0600);
|
||||||
/**
|
/**
|
||||||
* struct gsm_mux_net - network interface
|
* struct gsm_mux_net - network interface
|
||||||
* @struct gsm_dlci* dlci
|
* @struct gsm_dlci* dlci
|
||||||
* @struct net_device_stats stats;
|
|
||||||
*
|
*
|
||||||
* Created when net interface is initialized.
|
* Created when net interface is initialized.
|
||||||
**/
|
**/
|
||||||
struct gsm_mux_net {
|
struct gsm_mux_net {
|
||||||
struct kref ref;
|
struct kref ref;
|
||||||
struct gsm_dlci *dlci;
|
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
|
* 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
|
* 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;
|
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)
|
static void dlci_net_free(struct gsm_dlci *dlci)
|
||||||
{
|
{
|
||||||
if (!dlci->net) {
|
if (!dlci->net) {
|
||||||
|
@ -2660,8 +2652,8 @@ static int gsm_mux_net_start_xmit(struct sk_buff *skb,
|
||||||
muxnet_get(mux_net);
|
muxnet_get(mux_net);
|
||||||
|
|
||||||
skb_queue_head(&dlci->skb_list, skb);
|
skb_queue_head(&dlci->skb_list, skb);
|
||||||
STATS(net).tx_packets++;
|
net->stats.tx_packets++;
|
||||||
STATS(net).tx_bytes += skb->len;
|
net->stats.tx_bytes += skb->len;
|
||||||
gsm_dlci_data_kick(dlci);
|
gsm_dlci_data_kick(dlci);
|
||||||
/* And tell the kernel when the last transmit started. */
|
/* And tell the kernel when the last transmit started. */
|
||||||
netif_trans_update(net);
|
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");
|
dev_dbg(&net->dev, "Tx timed out.\n");
|
||||||
|
|
||||||
/* Update statistics */
|
/* Update statistics */
|
||||||
STATS(net).tx_errors++;
|
net->stats.tx_errors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gsm_mux_rx_netchar(struct gsm_dlci *dlci,
|
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);
|
skb = dev_alloc_skb(size + NET_IP_ALIGN);
|
||||||
if (!skb) {
|
if (!skb) {
|
||||||
/* We got no receive buffer. */
|
/* We got no receive buffer. */
|
||||||
STATS(net).rx_dropped++;
|
net->stats.rx_dropped++;
|
||||||
muxnet_put(mux_net);
|
muxnet_put(mux_net);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2705,8 +2697,8 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci,
|
||||||
netif_rx(skb);
|
netif_rx(skb);
|
||||||
|
|
||||||
/* update out statistics */
|
/* update out statistics */
|
||||||
STATS(net).rx_packets++;
|
net->stats.rx_packets++;
|
||||||
STATS(net).rx_bytes += size;
|
net->stats.rx_bytes += size;
|
||||||
muxnet_put(mux_net);
|
muxnet_put(mux_net);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2718,7 +2710,6 @@ static void gsm_mux_net_init(struct net_device *net)
|
||||||
.ndo_stop = gsm_mux_net_close,
|
.ndo_stop = gsm_mux_net_close,
|
||||||
.ndo_start_xmit = gsm_mux_net_start_xmit,
|
.ndo_start_xmit = gsm_mux_net_start_xmit,
|
||||||
.ndo_tx_timeout = gsm_mux_net_tx_timeout,
|
.ndo_tx_timeout = gsm_mux_net_tx_timeout,
|
||||||
.ndo_get_stats = gsm_mux_net_get_stats,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
net->netdev_ops = &gsm_netdev_ops;
|
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)
|
static void pty_flush_buffer(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct tty_struct *to = tty->link;
|
struct tty_struct *to = tty->link;
|
||||||
struct tty_ldisc *ld;
|
|
||||||
|
|
||||||
if (!to)
|
if (!to)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ld = tty_ldisc_ref(to);
|
tty_buffer_flush(to, NULL);
|
||||||
tty_buffer_flush(to, ld);
|
|
||||||
if (ld)
|
|
||||||
tty_ldisc_deref(ld);
|
|
||||||
|
|
||||||
if (to->packet) {
|
if (to->packet) {
|
||||||
spin_lock_irq(&tty->ctrl_lock);
|
spin_lock_irq(&tty->ctrl_lock);
|
||||||
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
|
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
|
||||||
|
|
|
@ -116,17 +116,41 @@ void serdev_device_close(struct serdev_device *serdev)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(serdev_device_close);
|
EXPORT_SYMBOL_GPL(serdev_device_close);
|
||||||
|
|
||||||
int serdev_device_write_buf(struct serdev_device *serdev,
|
void serdev_device_write_wakeup(struct serdev_device *serdev)
|
||||||
const unsigned char *buf, size_t count)
|
{
|
||||||
|
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;
|
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 -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)
|
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.parent = &ctrl->dev;
|
||||||
serdev->dev.bus = &serdev_bus_type;
|
serdev->dev.bus = &serdev_bus_type;
|
||||||
serdev->dev.type = &serdev_device_type;
|
serdev->dev.type = &serdev_device_type;
|
||||||
|
init_completion(&serdev->write_comp);
|
||||||
|
mutex_init(&serdev->write_lock);
|
||||||
return serdev;
|
return serdev;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(serdev_device_alloc);
|
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);
|
spin_unlock_irq(&i->lock);
|
||||||
irq_flags |= up->port.irqflags;
|
irq_flags |= up->port.irqflags;
|
||||||
ret = request_irq(up->port.irq, serial8250_interrupt,
|
ret = request_irq(up->port.irq, serial8250_interrupt,
|
||||||
irq_flags, "serial", i);
|
irq_flags, up->port.name, i);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
serial_do_unlink(i, up);
|
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);
|
data->rst = devm_reset_control_get_optional(dev, NULL);
|
||||||
if (IS_ERR(data->rst) && PTR_ERR(data->rst) == -EPROBE_DEFER) {
|
if (IS_ERR(data->rst)) {
|
||||||
err = -EPROBE_DEFER;
|
err = PTR_ERR(data->rst);
|
||||||
goto err_pclk;
|
goto err_pclk;
|
||||||
}
|
}
|
||||||
if (!IS_ERR(data->rst))
|
reset_control_deassert(data->rst);
|
||||||
reset_control_deassert(data->rst);
|
|
||||||
|
|
||||||
dw8250_quirks(p, data);
|
dw8250_quirks(p, data);
|
||||||
|
|
||||||
|
@ -567,8 +566,7 @@ static int dw8250_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_reset:
|
err_reset:
|
||||||
if (!IS_ERR(data->rst))
|
reset_control_assert(data->rst);
|
||||||
reset_control_assert(data->rst);
|
|
||||||
|
|
||||||
err_pclk:
|
err_pclk:
|
||||||
if (!IS_ERR(data->pclk))
|
if (!IS_ERR(data->pclk))
|
||||||
|
@ -589,8 +587,7 @@ static int dw8250_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
serial8250_unregister_port(data->line);
|
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))
|
if (!IS_ERR(data->pclk))
|
||||||
clk_disable_unprepare(data->pclk);
|
clk_disable_unprepare(data->pclk);
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
|
|
||||||
static unsigned int __init serial8250_early_in(struct uart_port *port, int offset)
|
static unsigned int __init serial8250_early_in(struct uart_port *port, int offset)
|
||||||
{
|
{
|
||||||
|
int reg_offset = offset;
|
||||||
offset <<= port->regshift;
|
offset <<= port->regshift;
|
||||||
|
|
||||||
switch (port->iotype) {
|
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);
|
return ioread32be(port->membase + offset);
|
||||||
case UPIO_PORT:
|
case UPIO_PORT:
|
||||||
return inb(port->iobase + offset);
|
return inb(port->iobase + offset);
|
||||||
|
case UPIO_AU:
|
||||||
|
return port->serial_in(port, reg_offset);
|
||||||
default:
|
default:
|
||||||
return 0;
|
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)
|
static void __init serial8250_early_out(struct uart_port *port, int offset, int value)
|
||||||
{
|
{
|
||||||
|
int reg_offset = offset;
|
||||||
offset <<= port->regshift;
|
offset <<= port->regshift;
|
||||||
|
|
||||||
switch (port->iotype) {
|
switch (port->iotype) {
|
||||||
|
@ -77,6 +81,9 @@ static void __init serial8250_early_out(struct uart_port *port, int offset, int
|
||||||
case UPIO_PORT:
|
case UPIO_PORT:
|
||||||
outb(value, port->iobase + offset);
|
outb(value, port->iobase + offset);
|
||||||
break;
|
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);
|
OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);
|
||||||
|
|
||||||
#endif
|
#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_pci_driver(exar_pci_driver);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_DESCRIPTION("Exar Serial Dricer");
|
MODULE_DESCRIPTION("Exar Serial Driver");
|
||||||
MODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>");
|
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.
|
* The IRQ setting mode of F81866 is not the same with F81216 series.
|
||||||
* Level/Low: IRQ_MODE0:0, IRQ_MODE1:0
|
* Level/Low: IRQ_MODE0:0, IRQ_MODE1:0
|
||||||
* Edge/High: IRQ_MODE0:1, 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_MODE 0xf0
|
||||||
#define F81866_IRQ_SHARE BIT(0)
|
#define F81866_IRQ_SHARE BIT(0)
|
||||||
|
@ -72,6 +78,13 @@
|
||||||
#define F81866_LDN_LOW 0x10
|
#define F81866_LDN_LOW 0x10
|
||||||
#define F81866_LDN_HIGH 0x16
|
#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 {
|
struct fintek_8250 {
|
||||||
u16 pid;
|
u16 pid;
|
||||||
u16 base_port;
|
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,
|
static void fintek_8250_goto_highspeed(struct uart_8250_port *uart,
|
||||||
unsigned int irq)
|
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 u16 addr[] = {0x4e, 0x2e};
|
||||||
static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67};
|
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);
|
sio_write_reg(pdata, LDN, k);
|
||||||
aux = sio_read_reg(pdata, IO_ADDR1);
|
aux = sio_read_reg(pdata, IO_ADDR1);
|
||||||
aux |= sio_read_reg(pdata, IO_ADDR2) << 8;
|
aux |= sio_read_reg(pdata, IO_ADDR2) << 8;
|
||||||
if (aux != io_address)
|
if (aux != uart->port.iobase)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
pdata->index = k;
|
pdata->index = k;
|
||||||
|
|
||||||
irq_data = irq_get_irq_data(irq);
|
irq_data = irq_get_irq_data(uart->port.irq);
|
||||||
if (irq_data)
|
if (irq_data)
|
||||||
level_mode =
|
level_mode =
|
||||||
irqd_is_level_type(irq_data);
|
irqd_is_level_type(irq_data);
|
||||||
|
|
||||||
fintek_8250_set_irq_mode(pdata, level_mode);
|
fintek_8250_set_irq_mode(pdata, level_mode);
|
||||||
fintek_8250_set_max_fifo(pdata);
|
fintek_8250_set_max_fifo(pdata);
|
||||||
|
fintek_8250_goto_highspeed(uart, pdata);
|
||||||
|
|
||||||
fintek_8250_exit_key(addr[i]);
|
fintek_8250_exit_key(addr[i]);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -330,7 +363,7 @@ int fintek_8250_probe(struct uart_8250_port *uart)
|
||||||
struct fintek_8250 *pdata;
|
struct fintek_8250 *pdata;
|
||||||
struct fintek_8250 probe_data;
|
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;
|
return -ENODEV;
|
||||||
|
|
||||||
pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_KERNEL);
|
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)
|
if (ret)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pci_set_master(pdev);
|
|
||||||
pci_try_set_mwi(pdev);
|
pci_try_set_mwi(pdev);
|
||||||
|
|
||||||
/* Special DMA address for UART */
|
/* 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);
|
struct pci_dev *pdev = to_pci_dev(port->dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
pci_set_master(pdev);
|
||||||
|
|
||||||
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
|
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -328,7 +328,7 @@ static const s8 au_io_out_map[8] = {
|
||||||
-1, /* UART_SCR (unmapped) */
|
-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))
|
if (offset >= ARRAY_SIZE(au_io_in_map))
|
||||||
return UINT_MAX;
|
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));
|
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))
|
if (offset >= ARRAY_SIZE(au_io_out_map))
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -630,6 +630,15 @@ config SERIAL_UARTLITE_CONSOLE
|
||||||
console (the system console is the device which receives all kernel
|
console (the system console is the device which receives all kernel
|
||||||
messages and warnings and which allows logins in single user mode).
|
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
|
config SERIAL_SUNCORE
|
||||||
bool
|
bool
|
||||||
depends on SPARC
|
depends on SPARC
|
||||||
|
@ -1343,6 +1352,7 @@ config SERIAL_ALTERA_JTAGUART_CONSOLE
|
||||||
bool "Altera JTAG UART console support"
|
bool "Altera JTAG UART console support"
|
||||||
depends on SERIAL_ALTERA_JTAGUART=y
|
depends on SERIAL_ALTERA_JTAGUART=y
|
||||||
select SERIAL_CORE_CONSOLE
|
select SERIAL_CORE_CONSOLE
|
||||||
|
select SERIAL_EARLYCON
|
||||||
help
|
help
|
||||||
Enable a Altera JTAG UART port to be the system console.
|
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"
|
bool "Altera UART console support"
|
||||||
depends on SERIAL_ALTERA_UART=y
|
depends on SERIAL_ALTERA_UART=y
|
||||||
select SERIAL_CORE_CONSOLE
|
select SERIAL_CORE_CONSOLE
|
||||||
|
select SERIAL_EARLYCON
|
||||||
help
|
help
|
||||||
Enable a Altera UART port to be the system console.
|
Enable a Altera UART port to be the system console.
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
|
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
|
||||||
obj-$(CONFIG_SERIAL_21285) += 21285.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
|
obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
|
||||||
obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.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_SUNSU) += sunsu.o
|
||||||
obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o
|
obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_SERIAL_21285) += 21285.o
|
||||||
|
|
||||||
# Now bring in any enabled 8250/16450/16550 type drivers.
|
# Now bring in any enabled 8250/16450/16550 type drivers.
|
||||||
obj-$(CONFIG_SERIAL_8250) += 8250/
|
obj-$(CONFIG_SERIAL_8250) += 8250/
|
||||||
|
|
||||||
|
|
|
@ -383,6 +383,26 @@ console_initcall(altera_jtaguart_console_init);
|
||||||
|
|
||||||
#define ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console)
|
#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
|
#else
|
||||||
|
|
||||||
#define ALTERA_JTAGUART_CONSOLE NULL
|
#define ALTERA_JTAGUART_CONSOLE NULL
|
||||||
|
|
|
@ -489,6 +489,38 @@ console_initcall(altera_uart_console_init);
|
||||||
|
|
||||||
#define ALTERA_UART_CONSOLE (&altera_uart_console)
|
#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
|
#else
|
||||||
|
|
||||||
#define ALTERA_UART_CONSOLE NULL
|
#define ALTERA_UART_CONSOLE NULL
|
||||||
|
|
|
@ -1327,14 +1327,15 @@ static void pl011_stop_tx(struct uart_port *port)
|
||||||
pl011_dma_tx_stop(uap);
|
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) */
|
/* Start TX with programmed I/O only (no DMA) */
|
||||||
static void pl011_start_tx_pio(struct uart_amba_port *uap)
|
static void pl011_start_tx_pio(struct uart_amba_port *uap)
|
||||||
{
|
{
|
||||||
uap->im |= UART011_TXIM;
|
if (pl011_tx_chars(uap, false)) {
|
||||||
pl011_write(uap->im, uap, REG_IMSC);
|
uap->im |= UART011_TXIM;
|
||||||
pl011_tx_chars(uap, false);
|
pl011_write(uap->im, uap, REG_IMSC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pl011_start_tx(struct uart_port *port)
|
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;
|
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;
|
struct circ_buf *xmit = &uap->port.state->xmit;
|
||||||
int count = uap->fifosize >> 1;
|
int count = uap->fifosize >> 1;
|
||||||
|
|
||||||
if (uap->port.x_char) {
|
if (uap->port.x_char) {
|
||||||
if (!pl011_tx_char(uap, uap->port.x_char, from_irq))
|
if (!pl011_tx_char(uap, uap->port.x_char, from_irq))
|
||||||
return;
|
return true;
|
||||||
uap->port.x_char = 0;
|
uap->port.x_char = 0;
|
||||||
--count;
|
--count;
|
||||||
}
|
}
|
||||||
if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
|
if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
|
||||||
pl011_stop_tx(&uap->port);
|
pl011_stop_tx(&uap->port);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we are using DMA mode, try to send some characters. */
|
/* If we are using DMA mode, try to send some characters. */
|
||||||
if (pl011_dma_tx_irq(uap))
|
if (pl011_dma_tx_irq(uap))
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (likely(from_irq) && count-- == 0)
|
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)
|
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||||
uart_write_wakeup(&uap->port);
|
uart_write_wakeup(&uap->port);
|
||||||
|
|
||||||
if (uart_circ_empty(xmit))
|
if (uart_circ_empty(xmit)) {
|
||||||
pl011_stop_tx(&uap->port);
|
pl011_stop_tx(&uap->port);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pl011_modem_status(struct uart_amba_port *uap)
|
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)
|
if (!device->port.membase)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
/* On QDF2400 SOCs affected by Erratum 44, the "qdf2400_e44" must
|
device->con->write = pl011_early_write;
|
||||||
* 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;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);
|
OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);
|
||||||
OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", 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
|
#else
|
||||||
#define AMBA_CONSOLE NULL
|
#define AMBA_CONSOLE NULL
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/atmel_pdc.h>
|
#include <linux/atmel_pdc.h>
|
||||||
#include <linux/atmel_serial.h>
|
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/platform_data/atmel.h>
|
#include <linux/platform_data/atmel.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
|
@ -71,6 +70,7 @@
|
||||||
#include <linux/serial_core.h>
|
#include <linux/serial_core.h>
|
||||||
|
|
||||||
#include "serial_mctrl_gpio.h"
|
#include "serial_mctrl_gpio.h"
|
||||||
|
#include "atmel_serial.h"
|
||||||
|
|
||||||
static void atmel_start_rx(struct uart_port *port);
|
static void atmel_start_rx(struct uart_port *port);
|
||||||
static void atmel_stop_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)
|
* at91: 6 USARTs and one DBGU port (SAM9260)
|
||||||
* avr32: 4
|
* 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.
|
* We wrap our port structure around the generic uart_port.
|
||||||
|
@ -175,6 +176,7 @@ struct atmel_uart_port {
|
||||||
unsigned int pending_status;
|
unsigned int pending_status;
|
||||||
spinlock_t lock_suspended;
|
spinlock_t lock_suspended;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
struct {
|
struct {
|
||||||
u32 cr;
|
u32 cr;
|
||||||
u32 mr;
|
u32 mr;
|
||||||
|
@ -185,6 +187,7 @@ struct atmel_uart_port {
|
||||||
u32 fmr;
|
u32 fmr;
|
||||||
u32 fimr;
|
u32 fimr;
|
||||||
} cache;
|
} cache;
|
||||||
|
#endif
|
||||||
|
|
||||||
int (*prepare_rx)(struct uart_port *port);
|
int (*prepare_rx)(struct uart_port *port);
|
||||||
int (*prepare_tx)(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];
|
struct lpuart_port *sport = lpuart_ports[co->index];
|
||||||
unsigned char old_cr2, cr2;
|
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 */
|
/* first save CR2 and then disable interrupts */
|
||||||
cr2 = old_cr2 = readb(sport->port.membase + UARTCR2);
|
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();
|
barrier();
|
||||||
|
|
||||||
writeb(old_cr2, sport->port.membase + UARTCR2);
|
writeb(old_cr2, sport->port.membase + UARTCR2);
|
||||||
|
|
||||||
|
if (locked)
|
||||||
|
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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];
|
struct lpuart_port *sport = lpuart_ports[co->index];
|
||||||
unsigned long old_cr, cr;
|
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 */
|
/* first save CR2 and then disable interrupts */
|
||||||
cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL);
|
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();
|
barrier();
|
||||||
|
|
||||||
lpuart32_write(old_cr, sport->port.membase + UARTCTRL);
|
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;
|
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 void clear_rx_errors(struct imx_port *sport);
|
||||||
static int start_rx_dma(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);
|
temp = readl(sport->port.membase + USR2);
|
||||||
if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
|
if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
|
||||||
sport->dma_is_rxing = 1;
|
|
||||||
|
|
||||||
/* disable the receiver ready and aging timer interrupts */
|
imx_disable_rx_int(sport);
|
||||||
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);
|
|
||||||
|
|
||||||
/* tell the DMA to receive the data. */
|
/* tell the DMA to receive the data. */
|
||||||
start_rx_dma(sport);
|
start_rx_dma(sport);
|
||||||
|
@ -1317,19 +1325,10 @@ static int imx_startup(struct uart_port *port)
|
||||||
if (!is_imx1_uart(sport)) {
|
if (!is_imx1_uart(sport)) {
|
||||||
temp = readl(sport->port.membase + UCR3);
|
temp = readl(sport->port.membase + UCR3);
|
||||||
|
|
||||||
/*
|
temp |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
|
||||||
* 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;
|
|
||||||
|
|
||||||
if (sport->dte_mode)
|
if (sport->dte_mode)
|
||||||
|
/* disable broken interrupts */
|
||||||
temp &= ~(UCR3_RI | UCR3_DCD);
|
temp &= ~(UCR3_RI | UCR3_DCD);
|
||||||
|
|
||||||
writel(temp, sport->port.membase + UCR3);
|
writel(temp, sport->port.membase + UCR3);
|
||||||
|
@ -1339,6 +1338,33 @@ static int imx_startup(struct uart_port *port)
|
||||||
* Enable modem status interrupts
|
* Enable modem status interrupts
|
||||||
*/
|
*/
|
||||||
imx_enable_ms(&sport->port);
|
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);
|
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1584,8 +1610,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||||
|
|
||||||
ufcr = readl(sport->port.membase + UFCR);
|
ufcr = readl(sport->port.membase + UFCR);
|
||||||
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
|
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
|
||||||
if (sport->dte_mode)
|
|
||||||
ufcr |= UFCR_DCEDTE;
|
|
||||||
writel(ufcr, sport->port.membase + UFCR);
|
writel(ufcr, sport->port.membase + UFCR);
|
||||||
|
|
||||||
writel(num, sport->port.membase + UBIR);
|
writel(num, sport->port.membase + UBIR);
|
||||||
|
@ -2153,6 +2177,27 @@ static int serial_imx_probe(struct platform_device *pdev)
|
||||||
UCR1_TXMPTYEN | UCR1_RTSDEN);
|
UCR1_TXMPTYEN | UCR1_RTSDEN);
|
||||||
writel_relaxed(reg, sport->port.membase + UCR1);
|
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);
|
clk_disable_unprepare(sport->clk_ipg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1770,7 +1770,8 @@ static int serial_omap_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_add_port:
|
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_runtime_disable(&pdev->dev);
|
||||||
pm_qos_remove_request(&up->pm_qos_request);
|
pm_qos_remove_request(&up->pm_qos_request);
|
||||||
device_init_wakeup(up->dev, false);
|
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);
|
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_put_sync(up->dev);
|
||||||
pm_runtime_disable(up->dev);
|
pm_runtime_disable(up->dev);
|
||||||
uart_remove_one_port(&serial_omap_reg, &up->port);
|
|
||||||
pm_qos_remove_request(&up->pm_qos_request);
|
pm_qos_remove_request(&up->pm_qos_request);
|
||||||
device_init_wakeup(&dev->dev, false);
|
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)
|
static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
|
||||||
{
|
{
|
||||||
struct s3c24xx_uart_dma *dma = p->dma;
|
struct s3c24xx_uart_dma *dma = p->dma;
|
||||||
unsigned long flags;
|
int ret;
|
||||||
|
|
||||||
/* Default slave configuration parameters */
|
/* Default slave configuration parameters */
|
||||||
dma->rx_conf.direction = DMA_DEV_TO_MEM;
|
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");
|
dma->tx_chan = dma_request_chan(p->port.dev, "tx");
|
||||||
if (IS_ERR(dma->tx_chan)) {
|
if (IS_ERR(dma->tx_chan)) {
|
||||||
dma_release_channel(dma->rx_chan);
|
ret = PTR_ERR(dma->tx_chan);
|
||||||
return PTR_ERR(dma->tx_chan);
|
goto err_release_rx;
|
||||||
}
|
}
|
||||||
|
|
||||||
dmaengine_slave_config(dma->tx_chan, &dma->tx_conf);
|
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_size = PAGE_SIZE;
|
||||||
|
|
||||||
dma->rx_buf = kmalloc(dma->rx_size, GFP_KERNEL);
|
dma->rx_buf = kmalloc(dma->rx_size, GFP_KERNEL);
|
||||||
|
|
||||||
if (!dma->rx_buf) {
|
if (!dma->rx_buf) {
|
||||||
dma_release_channel(dma->rx_chan);
|
ret = -ENOMEM;
|
||||||
dma_release_channel(dma->tx_chan);
|
goto err_release_tx;
|
||||||
return -ENOMEM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
dma->rx_size, DMA_FROM_DEVICE);
|
||||||
|
if (dma_mapping_error(p->port.dev, dma->rx_addr)) {
|
||||||
spin_lock_irqsave(&p->port.lock, flags);
|
ret = -EIO;
|
||||||
|
goto err_free_rx;
|
||||||
|
}
|
||||||
|
|
||||||
/* TX buffer */
|
/* TX buffer */
|
||||||
dma->tx_addr = dma_map_single(dma->tx_chan->device->dev,
|
dma->tx_addr = dma_map_single(p->port.dev, p->port.state->xmit.buf,
|
||||||
p->port.state->xmit.buf,
|
|
||||||
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
||||||
|
if (dma_mapping_error(p->port.dev, dma->tx_addr)) {
|
||||||
spin_unlock_irqrestore(&p->port.lock, flags);
|
ret = -EIO;
|
||||||
|
goto err_unmap_rx;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
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) {
|
if (dma->rx_chan) {
|
||||||
dmaengine_terminate_all(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);
|
dma->rx_size, DMA_FROM_DEVICE);
|
||||||
kfree(dma->rx_buf);
|
kfree(dma->rx_buf);
|
||||||
dma_release_channel(dma->rx_chan);
|
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) {
|
if (dma->tx_chan) {
|
||||||
dmaengine_terminate_all(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);
|
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
||||||
dma_release_channel(dma->tx_chan);
|
dma_release_channel(dma->tx_chan);
|
||||||
dma->tx_chan = NULL;
|
dma->tx_chan = NULL;
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
#include <linux/tty_flip.h>
|
#include <linux/tty_flip.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
#include <linux/atomic.h>
|
#include <linux/refcount.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/war.h>
|
#include <asm/war.h>
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ struct sbd_port {
|
||||||
struct sbd_duart {
|
struct sbd_duart {
|
||||||
struct sbd_port sport[2];
|
struct sbd_port sport[2];
|
||||||
unsigned long mapctrl;
|
unsigned long mapctrl;
|
||||||
atomic_t map_guard;
|
refcount_t map_guard;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_sport(uport) container_of(uport, struct sbd_port, port)
|
#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_port *sport = to_sport(uport);
|
||||||
struct sbd_duart *duart = sport->duart;
|
struct sbd_duart *duart = sport->duart;
|
||||||
int map_guard;
|
|
||||||
|
|
||||||
iounmap(sport->memctrl);
|
iounmap(sport->memctrl);
|
||||||
sport->memctrl = NULL;
|
sport->memctrl = NULL;
|
||||||
iounmap(uport->membase);
|
iounmap(uport->membase);
|
||||||
uport->membase = NULL;
|
uport->membase = NULL;
|
||||||
|
|
||||||
map_guard = atomic_add_return(-1, &duart->map_guard);
|
if(refcount_dec_and_test(&duart->map_guard))
|
||||||
if (!map_guard)
|
|
||||||
release_mem_region(duart->mapctrl, DUART_CHANREG_SPACING);
|
release_mem_region(duart->mapctrl, DUART_CHANREG_SPACING);
|
||||||
release_mem_region(uport->mapbase, 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";
|
const char *err = KERN_ERR "sbd: Unable to reserve MMIO resource\n";
|
||||||
struct sbd_duart *duart = to_sport(uport)->duart;
|
struct sbd_duart *duart = to_sport(uport)->duart;
|
||||||
int map_guard;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!request_mem_region(uport->mapbase, DUART_CHANREG_SPACING,
|
if (!request_mem_region(uport->mapbase, DUART_CHANREG_SPACING,
|
||||||
|
@ -706,11 +703,11 @@ static int sbd_request_port(struct uart_port *uport)
|
||||||
printk(err);
|
printk(err);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
map_guard = atomic_add_return(1, &duart->map_guard);
|
refcount_inc(&duart->map_guard);
|
||||||
if (map_guard == 1) {
|
if (refcount_read(&duart->map_guard) == 1) {
|
||||||
if (!request_mem_region(duart->mapctrl, DUART_CHANREG_SPACING,
|
if (!request_mem_region(duart->mapctrl, DUART_CHANREG_SPACING,
|
||||||
"sb1250-duart")) {
|
"sb1250-duart")) {
|
||||||
atomic_add(-1, &duart->map_guard);
|
refcount_dec(&duart->map_guard);
|
||||||
printk(err);
|
printk(err);
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
}
|
}
|
||||||
|
@ -718,8 +715,7 @@ static int sbd_request_port(struct uart_port *uport)
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ret = sbd_map_port(uport);
|
ret = sbd_map_port(uport);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
map_guard = atomic_add_return(-1, &duart->map_guard);
|
if (refcount_dec_and_test(&duart->map_guard))
|
||||||
if (!map_guard)
|
|
||||||
release_mem_region(duart->mapctrl,
|
release_mem_region(duart->mapctrl,
|
||||||
DUART_CHANREG_SPACING);
|
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--)
|
for (tries = 3; !ops->tx_empty(uport) && tries; tries--)
|
||||||
msleep(10);
|
msleep(10);
|
||||||
if (!tries)
|
if (!tries)
|
||||||
dev_err(uport->dev, "%s%d: Unable to drain transmitter\n",
|
dev_err(uport->dev, "%s: Unable to drain transmitter\n",
|
||||||
drv->dev_name,
|
uport->name);
|
||||||
drv->tty_driver->name_base + uport->line);
|
|
||||||
|
|
||||||
ops->shutdown(uport);
|
ops->shutdown(uport);
|
||||||
}
|
}
|
||||||
|
@ -2248,11 +2247,10 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port)
|
||||||
break;
|
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 ? dev_name(port->dev) : "",
|
||||||
port->dev ? ": " : "",
|
port->dev ? ": " : "",
|
||||||
drv->dev_name,
|
port->name,
|
||||||
drv->tty_driver->name_base + port->line,
|
|
||||||
address, port->irq, port->uartclk / 16, uart_type(port));
|
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 flow = 'n';
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!state)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
tport = &state->port;
|
tport = &state->port;
|
||||||
mutex_lock(&tport->mutex);
|
mutex_lock(&tport->mutex);
|
||||||
|
|
||||||
|
@ -2368,13 +2363,12 @@ static int uart_poll_get_char(struct tty_driver *driver, int line)
|
||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
if (state) {
|
port = uart_port_ref(state);
|
||||||
port = uart_port_ref(state);
|
if (port) {
|
||||||
if (port) {
|
ret = port->ops->poll_get_char(port);
|
||||||
ret = port->ops->poll_get_char(port);
|
uart_port_deref(port);
|
||||||
uart_port_deref(port);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
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_state *state = drv->state + line;
|
||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
|
|
||||||
if (!state)
|
|
||||||
return;
|
|
||||||
|
|
||||||
port = uart_port_ref(state);
|
port = uart_port_ref(state);
|
||||||
if (!port)
|
if (!port)
|
||||||
return;
|
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;
|
state->pm_state = UART_PM_STATE_UNDEFINED;
|
||||||
uport->cons = drv->cons;
|
uport->cons = drv->cons;
|
||||||
uport->minor = drv->tty_driver->minor_start + uport->line;
|
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
|
* 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)
|
if (uport->type != PORT_UNKNOWN && uport->ops->release_port)
|
||||||
uport->ops->release_port(uport);
|
uport->ops->release_port(uport);
|
||||||
kfree(uport->tty_groups);
|
kfree(uport->tty_groups);
|
||||||
|
kfree(uport->name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Indicate that there isn't a port here anymore.
|
* 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) {
|
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
|
||||||
|
u16 data = serial_port_in(port, SCPDR);
|
||||||
u16 ctrl = serial_port_in(port, SCPCR);
|
u16 ctrl = serial_port_in(port, SCPCR);
|
||||||
|
|
||||||
/* Enable RXD and TXD pin functions */
|
/* Enable RXD and TXD pin functions */
|
||||||
ctrl &= ~(SCPCR_RXDC | SCPCR_TXDC);
|
ctrl &= ~(SCPCR_RXDC | SCPCR_TXDC);
|
||||||
if (to_sci_port(port)->has_rtscts) {
|
if (to_sci_port(port)->has_rtscts) {
|
||||||
/* RTS# is output, driven 1 */
|
/* RTS# is output, active low, unless autorts */
|
||||||
ctrl |= SCPCR_RTSC;
|
if (!(port->mctrl & TIOCM_RTS)) {
|
||||||
serial_port_out(port, SCPDR,
|
ctrl |= SCPCR_RTSC;
|
||||||
serial_port_in(port, SCPDR) | SCPDR_RTSD);
|
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 */
|
/* Enable CTS# pin function */
|
||||||
ctrl &= ~SCPCR_CTSC;
|
ctrl &= ~SCPCR_CTSC;
|
||||||
}
|
}
|
||||||
|
serial_port_out(port, SCPDR, data);
|
||||||
serial_port_out(port, SCPCR, ctrl);
|
serial_port_out(port, SCPCR, ctrl);
|
||||||
} else if (sci_getreg(port, SCSPTR)->size) {
|
} else if (sci_getreg(port, SCSPTR)->size) {
|
||||||
u16 status = serial_port_in(port, SCSPTR);
|
u16 status = serial_port_in(port, SCSPTR);
|
||||||
|
|
||||||
/* RTS# is output, driven 1 */
|
/* RTS# is always output; and active low, unless autorts */
|
||||||
status |= SCSPTR_RTSIO | SCSPTR_RTSDT;
|
status |= SCSPTR_RTSIO;
|
||||||
|
if (!(port->mctrl & TIOCM_RTS))
|
||||||
|
status |= SCSPTR_RTSDT;
|
||||||
|
else if (!s->autorts)
|
||||||
|
status &= ~SCSPTR_RTSDT;
|
||||||
/* CTS# and SCK are inputs */
|
/* CTS# and SCK are inputs */
|
||||||
status &= ~(SCSPTR_CTSIO | SCSPTR_SCKIO);
|
status &= ~(SCSPTR_CTSIO | SCSPTR_SCKIO);
|
||||||
serial_port_out(port, SCSPTR, status);
|
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);
|
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);
|
sci_request_dma(port);
|
||||||
|
|
||||||
|
ret = sci_request_irq(s);
|
||||||
|
if (unlikely(ret < 0)) {
|
||||||
|
sci_free_dma(port);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2021,8 +2036,8 @@ static void sci_shutdown(struct uart_port *port)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sci_free_dma(port);
|
|
||||||
sci_free_irq(s);
|
sci_free_irq(s);
|
||||||
|
sci_free_dma(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sci_sck_calc(struct sci_port *s, unsigned int bps,
|
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;
|
unsigned int status;
|
||||||
struct sci_port *s = to_sci_port(port);
|
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 */
|
serial_port_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */
|
||||||
|
|
||||||
reg = sci_getreg(port, SCFCR);
|
reg = sci_getreg(port, SCFCR);
|
||||||
|
@ -2374,6 +2385,10 @@ done:
|
||||||
|
|
||||||
serial_port_out(port, SCFCR, ctrl);
|
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 |
|
scr_val |= SCSCR_RE | SCSCR_TE |
|
||||||
(s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0));
|
(s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0));
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
#define SPRD_FIFO_SIZE 128
|
#define SPRD_FIFO_SIZE 128
|
||||||
#define SPRD_DEF_RATE 26000000
|
#define SPRD_DEF_RATE 26000000
|
||||||
#define SPRD_BAUD_IO_LIMIT 3000000
|
#define SPRD_BAUD_IO_LIMIT 3000000
|
||||||
#define SPRD_TIMEOUT 256
|
#define SPRD_TIMEOUT 256000
|
||||||
|
|
||||||
/* the offset of serial registers and BITs for them */
|
/* the offset of serial registers and BITs for them */
|
||||||
/* data registers */
|
/* data registers */
|
||||||
|
|
|
@ -887,13 +887,12 @@ static void asc_console_write(struct console *co, const char *s, unsigned count)
|
||||||
int locked = 1;
|
int locked = 1;
|
||||||
u32 intenable;
|
u32 intenable;
|
||||||
|
|
||||||
local_irq_save(flags);
|
|
||||||
if (port->sysrq)
|
if (port->sysrq)
|
||||||
locked = 0; /* asc_interrupt has already claimed the lock */
|
locked = 0; /* asc_interrupt has already claimed the lock */
|
||||||
else if (oops_in_progress)
|
else if (oops_in_progress)
|
||||||
locked = spin_trylock(&port->lock);
|
locked = spin_trylock_irqsave(&port->lock, flags);
|
||||||
else
|
else
|
||||||
spin_lock(&port->lock);
|
spin_lock_irqsave(&port->lock, flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disable interrupts so we don't get the IRQ line bouncing
|
* 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);
|
asc_out(port, ASC_INTEN, intenable);
|
||||||
|
|
||||||
if (locked)
|
if (locked)
|
||||||
spin_unlock(&port->lock);
|
spin_unlock_irqrestore(&port->lock, flags);
|
||||||
local_irq_restore(flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int asc_console_setup(struct console *co, char *options)
|
static int asc_console_setup(struct console *co, char *options)
|
||||||
{
|
{
|
||||||
struct asc_port *ascport;
|
struct asc_port *ascport;
|
||||||
int baud = 9600;
|
int baud = 115200;
|
||||||
int bits = 8;
|
int bits = 8;
|
||||||
int parity = 'n';
|
int parity = 'n';
|
||||||
int flow = 'n';
|
int flow = 'n';
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
#define ULITE_NAME "ttyUL"
|
#define ULITE_NAME "ttyUL"
|
||||||
#define ULITE_MAJOR 204
|
#define ULITE_MAJOR 204
|
||||||
#define ULITE_MINOR 187
|
#define ULITE_MINOR 187
|
||||||
#define ULITE_NR_UARTS 16
|
#define ULITE_NR_UARTS CONFIG_SERIAL_UARTLITE_NR_UARTS
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
* Register definitions
|
* Register definitions
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#define CDNS_UART_TTY_NAME "ttyPS"
|
#define CDNS_UART_TTY_NAME "ttyPS"
|
||||||
#define CDNS_UART_NAME "xuartps"
|
#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_MIN 4
|
||||||
#define CDNS_UART_BDIV_MAX 255
|
#define CDNS_UART_BDIV_MAX 255
|
||||||
#define CDNS_UART_CD_MAX 65535
|
#define CDNS_UART_CD_MAX 65535
|
||||||
|
#define UART_AUTOSUSPEND_TIMEOUT 3000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct cdns_uart - device data
|
* 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,
|
static void cdns_uart_pm(struct uart_port *port, unsigned int state,
|
||||||
unsigned int oldstate)
|
unsigned int oldstate)
|
||||||
{
|
{
|
||||||
struct cdns_uart *cdns_uart = port->private_data;
|
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case UART_PM_STATE_OFF:
|
case UART_PM_STATE_OFF:
|
||||||
clk_disable(cdns_uart->uartclk);
|
pm_runtime_mark_last_busy(port->dev);
|
||||||
clk_disable(cdns_uart->pclk);
|
pm_runtime_put_autosuspend(port->dev);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
clk_enable(cdns_uart->pclk);
|
pm_runtime_get_sync(port->dev);
|
||||||
clk_enable(cdns_uart->uartclk);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1353,12 +1352,7 @@ static int cdns_uart_suspend(struct device *device)
|
||||||
* the suspend.
|
* the suspend.
|
||||||
*/
|
*/
|
||||||
uart_suspend_port(&cdns_uart_uart_driver, port);
|
uart_suspend_port(&cdns_uart_uart_driver, port);
|
||||||
if (console_suspend_enabled && !may_wake) {
|
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 {
|
|
||||||
unsigned long flags = 0;
|
unsigned long flags = 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&port->lock, flags);
|
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;
|
ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN;
|
||||||
writel(ctrl_reg, port->membase + CDNS_UART_CR);
|
writel(ctrl_reg, port->membase + CDNS_UART_CR);
|
||||||
|
|
||||||
|
clk_disable(cdns_uart->uartclk);
|
||||||
|
clk_disable(cdns_uart->pclk);
|
||||||
spin_unlock_irqrestore(&port->lock, flags);
|
spin_unlock_irqrestore(&port->lock, flags);
|
||||||
} else {
|
} else {
|
||||||
spin_lock_irqsave(&port->lock, flags);
|
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);
|
return uart_resume_port(&cdns_uart_uart_driver, port);
|
||||||
}
|
}
|
||||||
#endif /* ! CONFIG_PM_SLEEP */
|
#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,
|
clk_disable(cdns_uart->uartclk);
|
||||||
cdns_uart_resume);
|
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 = {
|
static const struct cdns_platform_data zynqmp_uart_def = {
|
||||||
.quirks = CDNS_UART_RXBS_SUPPORT, };
|
.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);
|
return PTR_ERR(cdns_uart_data->uartclk);
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = clk_prepare(cdns_uart_data->pclk);
|
rc = clk_prepare_enable(cdns_uart_data->pclk);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&pdev->dev, "Unable to enable pclk clock.\n");
|
dev_err(&pdev->dev, "Unable to enable pclk clock.\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
rc = clk_prepare(cdns_uart_data->uartclk);
|
rc = clk_prepare_enable(cdns_uart_data->uartclk);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&pdev->dev, "Unable to enable device clock.\n");
|
dev_err(&pdev->dev, "Unable to enable device clock.\n");
|
||||||
goto err_out_clk_dis_pclk;
|
goto err_out_clk_dis_pclk;
|
||||||
|
@ -1558,6 +1578,11 @@ static int cdns_uart_probe(struct platform_device *pdev)
|
||||||
cdns_uart_data->port = port;
|
cdns_uart_data->port = port;
|
||||||
platform_set_drvdata(pdev, 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);
|
rc = uart_add_one_port(&cdns_uart_uart_driver, port);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
|
@ -1573,9 +1598,12 @@ err_out_notif_unreg:
|
||||||
&cdns_uart_data->clk_rate_change_nb);
|
&cdns_uart_data->clk_rate_change_nb);
|
||||||
#endif
|
#endif
|
||||||
err_out_clk_disable:
|
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:
|
err_out_clk_dis_pclk:
|
||||||
clk_unprepare(cdns_uart_data->pclk);
|
clk_disable_unprepare(cdns_uart_data->pclk);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1599,8 +1627,11 @@ static int cdns_uart_remove(struct platform_device *pdev)
|
||||||
#endif
|
#endif
|
||||||
rc = uart_remove_one_port(&cdns_uart_uart_driver, port);
|
rc = uart_remove_one_port(&cdns_uart_uart_driver, port);
|
||||||
port->mapbase = 0;
|
port->mapbase = 0;
|
||||||
clk_unprepare(cdns_uart_data->uartclk);
|
clk_disable_unprepare(cdns_uart_data->uartclk);
|
||||||
clk_unprepare(cdns_uart_data->pclk);
|
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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
EXPORT_SYMBOL_GPL(tty_find_polling_driver);
|
||||||
#endif
|
#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,
|
static ssize_t hung_up_tty_read(struct file *file, char __user *buf,
|
||||||
size_t count, loff_t *ppos)
|
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 DEFINE_SPINLOCK(redirect_lock);
|
||||||
static struct file *redirect;
|
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_wakeup - request more data
|
||||||
* @tty: terminal
|
* @tty: terminal
|
||||||
|
@ -608,60 +476,6 @@ void tty_wakeup(struct tty_struct *tty)
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(tty_wakeup);
|
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
|
* __tty_hangup - actual handler for hangup events
|
||||||
* @work: tty device
|
* @work: tty device
|
||||||
|
@ -840,7 +654,7 @@ void tty_vhangup_self(void)
|
||||||
* is complete. That guarantee is necessary for security reasons.
|
* 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_debug_hangup(tty, "session hangup\n");
|
||||||
__tty_hangup(tty, 1);
|
__tty_hangup(tty, 1);
|
||||||
|
@ -861,106 +675,6 @@ int tty_hung_up_p(struct file *filp)
|
||||||
|
|
||||||
EXPORT_SYMBOL(tty_hung_up_p);
|
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
|
* stop_tty - propagate flow control
|
||||||
* @tty: tty to stop
|
* @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
|
* This code guarantees that either everything succeeds and the
|
||||||
* TTY is ready for operation, or else the table slots are vacated
|
* TTY is ready for operation, or else the table slots are vacated
|
||||||
* and the allocated memory released. (Except that the termios
|
* and the allocated memory released. (Except that the termios
|
||||||
* and locked termios may be retained.)
|
* may be retained.)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!try_module_get(driver->owner))
|
if (!try_module_get(driver->owner))
|
||||||
|
@ -2163,38 +1877,13 @@ retry_open:
|
||||||
}
|
}
|
||||||
clear_bit(TTY_HUPPED, &tty->flags);
|
clear_bit(TTY_HUPPED, &tty->flags);
|
||||||
|
|
||||||
|
|
||||||
read_lock(&tasklist_lock);
|
|
||||||
spin_lock_irq(¤t->sighand->siglock);
|
|
||||||
noctty = (filp->f_flags & O_NOCTTY) ||
|
noctty = (filp->f_flags & O_NOCTTY) ||
|
||||||
(IS_ENABLED(CONFIG_VT) && device == MKDEV(TTY_MAJOR, 0)) ||
|
(IS_ENABLED(CONFIG_VT) && device == MKDEV(TTY_MAJOR, 0)) ||
|
||||||
device == MKDEV(TTYAUX_MAJOR, 1) ||
|
device == MKDEV(TTYAUX_MAJOR, 1) ||
|
||||||
(tty->driver->type == TTY_DRIVER_TYPE_PTY &&
|
(tty->driver->type == TTY_DRIVER_TYPE_PTY &&
|
||||||
tty->driver->subtype == PTY_TYPE_MASTER);
|
tty->driver->subtype == PTY_TYPE_MASTER);
|
||||||
|
if (!noctty)
|
||||||
if (!noctty &&
|
tty_open_proc_set_tty(filp, tty);
|
||||||
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);
|
|
||||||
tty_unlock(tty);
|
tty_unlock(tty);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2456,211 +2145,6 @@ static int fionbio(struct file *file, int __user *p)
|
||||||
return 0;
|
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
|
* tiocsetd - set line discipline
|
||||||
* @tty: tty device
|
* @tty: tty device
|
||||||
|
@ -2843,8 +2327,8 @@ static void tty_warn_deprecated_flags(struct serial_struct __user *ss)
|
||||||
flags &= ASYNC_DEPRECATED;
|
flags &= ASYNC_DEPRECATED;
|
||||||
|
|
||||||
if (flags && __ratelimit(&depr_flags))
|
if (flags && __ratelimit(&depr_flags))
|
||||||
pr_warning("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
|
pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
|
||||||
__func__, get_task_comm(comm, current), flags);
|
__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);
|
int excl = test_bit(TTY_EXCLUSIVE, &tty->flags);
|
||||||
return put_user(excl, (int __user *)p);
|
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:
|
case TIOCGETD:
|
||||||
return tiocgetd(tty, p);
|
return tiocgetd(tty, p);
|
||||||
case TIOCSETD:
|
case TIOCSETD:
|
||||||
|
@ -2993,6 +2464,10 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
case TIOCSSERIAL:
|
case TIOCSSERIAL:
|
||||||
tty_warn_deprecated_flags(p);
|
tty_warn_deprecated_flags(p);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg);
|
||||||
|
if (retval != -ENOIOCTLCMD)
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
if (tty->ops->ioctl) {
|
if (tty->ops->ioctl) {
|
||||||
retval = tty->ops->ioctl(tty, cmd, arg);
|
retval = tty->ops->ioctl(tty, cmd, arg);
|
||||||
|
@ -3293,9 +2768,9 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
|
||||||
{
|
{
|
||||||
char name[64];
|
char name[64];
|
||||||
dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
|
dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
|
||||||
struct device *dev = NULL;
|
struct ktermios *tp;
|
||||||
int retval = -ENODEV;
|
struct device *dev;
|
||||||
bool cdev = false;
|
int retval;
|
||||||
|
|
||||||
if (index >= driver->num) {
|
if (index >= driver->num) {
|
||||||
pr_err("%s: Attempt to register invalid tty line number (%d)\n",
|
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
|
else
|
||||||
tty_line_name(driver, index, name);
|
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);
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||||
if (!dev) {
|
if (!dev)
|
||||||
retval = -ENOMEM;
|
return ERR_PTR(-ENOMEM);
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->devt = devt;
|
dev->devt = devt;
|
||||||
dev->class = tty_class;
|
dev->class = tty_class;
|
||||||
|
@ -3329,18 +2795,38 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
|
||||||
dev->groups = attr_grp;
|
dev->groups = attr_grp;
|
||||||
dev_set_drvdata(dev, drvdata);
|
dev_set_drvdata(dev, drvdata);
|
||||||
|
|
||||||
|
dev_set_uevent_suppress(dev, 1);
|
||||||
|
|
||||||
retval = device_register(dev);
|
retval = device_register(dev);
|
||||||
if (retval)
|
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;
|
return dev;
|
||||||
|
|
||||||
error:
|
err_del:
|
||||||
|
device_del(dev);
|
||||||
|
err_put:
|
||||||
put_device(dev);
|
put_device(dev);
|
||||||
if (cdev) {
|
|
||||||
cdev_del(driver->cdevs[index]);
|
|
||||||
driver->cdevs[index] = NULL;
|
|
||||||
}
|
|
||||||
return ERR_PTR(retval);
|
return ERR_PTR(retval);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(tty_register_device_attr);
|
EXPORT_SYMBOL_GPL(tty_register_device_attr);
|
||||||
|
@ -3441,11 +2927,6 @@ static void destruct_tty_driver(struct kref *kref)
|
||||||
struct ktermios *tp;
|
struct ktermios *tp;
|
||||||
|
|
||||||
if (driver->flags & TTY_DRIVER_INSTALLED) {
|
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++) {
|
for (i = 0; i < driver->num; i++) {
|
||||||
tp = driver->termios[i];
|
tp = driver->termios[i];
|
||||||
if (tp) {
|
if (tp) {
|
||||||
|
@ -3578,30 +3059,6 @@ void tty_default_fops(struct file_operations *fops)
|
||||||
*fops = tty_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)
|
static char *tty_devnode(struct device *dev, umode_t *mode)
|
||||||
{
|
{
|
||||||
if (!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 */
|
/* 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
|
* tty_termios_copy_hw - copy hardware settings
|
||||||
* @new: New termios
|
* @new: New termios
|
||||||
|
|
|
@ -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?
|
* 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 */
|
0x00000000, /* control chars */
|
||||||
0x03FF0000, /* digits */
|
0x03FFE000, /* digits and "-./" */
|
||||||
0x87FFFFFE, /* uppercase and '_' */
|
0x87FFFFFE, /* uppercase and '_' */
|
||||||
0x07FFFFFE, /* lowercase */
|
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) {
|
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)
|
int sel_loadlut(char __user *p)
|
||||||
{
|
{
|
||||||
u32 tmplut[8];
|
u32 tmplut[ARRAY_SIZE(inwordLut)];
|
||||||
if (copy_from_user(tmplut, (u32 __user *)(p+4), 32))
|
if (copy_from_user(tmplut, (u32 __user *)(p+4), sizeof(inwordLut)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
memcpy(inwordLut, tmplut, 32);
|
memcpy(inwordLut, tmplut, sizeof(inwordLut));
|
||||||
return 0;
|
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_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
|
||||||
static int vesa_off_interval;
|
static int vesa_off_interval;
|
||||||
static int blankinterval = 10*60;
|
static int blankinterval;
|
||||||
core_param(consoleblank, blankinterval, int, 0444);
|
core_param(consoleblank, blankinterval, int, 0444);
|
||||||
|
|
||||||
static DECLARE_WORK(console_work, console_callback);
|
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; }
|
static inline bool vgacon_text_force(void) { return false; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern void console_init(void);
|
||||||
|
|
||||||
#endif /* _LINUX_CONSOLE_H */
|
#endif /* _LINUX_CONSOLE_H */
|
||||||
|
|
|
@ -41,12 +41,16 @@ struct serdev_device_ops {
|
||||||
* @nr: Device number on serdev bus.
|
* @nr: Device number on serdev bus.
|
||||||
* @ctrl: serdev controller managing this device.
|
* @ctrl: serdev controller managing this device.
|
||||||
* @ops: Device operations.
|
* @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 serdev_device {
|
||||||
struct device dev;
|
struct device dev;
|
||||||
int nr;
|
int nr;
|
||||||
struct serdev_controller *ctrl;
|
struct serdev_controller *ctrl;
|
||||||
const struct serdev_device_ops *ops;
|
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)
|
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)
|
if (!serdev || !serdev->ops->write_wakeup)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
serdev->ops->write_wakeup(ctrl->serdev);
|
serdev->ops->write_wakeup(serdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int serdev_controller_receive_buf(struct serdev_controller *ctrl,
|
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)
|
if (!serdev || !serdev->ops->receive_buf)
|
||||||
return -EINVAL;
|
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)
|
#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);
|
void serdev_device_wait_until_sent(struct serdev_device *, long);
|
||||||
int serdev_device_get_tiocm(struct serdev_device *);
|
int serdev_device_get_tiocm(struct serdev_device *);
|
||||||
int serdev_device_set_tiocm(struct serdev_device *, int, int);
|
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 *);
|
void serdev_device_write_flush(struct serdev_device *);
|
||||||
int serdev_device_write_room(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;
|
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;
|
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) {}
|
static inline void serdev_tty_port_unregister(struct tty_port *port) {}
|
||||||
#endif /* CONFIG_SERIAL_DEV_CTRL_TTYPORT */
|
#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 */
|
#endif /*_LINUX_SERDEV_H */
|
||||||
|
|
|
@ -247,6 +247,7 @@ struct uart_port {
|
||||||
unsigned char suspended;
|
unsigned char suspended;
|
||||||
unsigned char irq_wake;
|
unsigned char irq_wake;
|
||||||
unsigned char unused[2];
|
unsigned char unused[2];
|
||||||
|
const char *name; /* port name */
|
||||||
struct attribute_group *attr_group; /* port specific attributes */
|
struct attribute_group *attr_group; /* port specific attributes */
|
||||||
const struct attribute_group **tty_groups; /* all attributes (serial core use only) */
|
const struct attribute_group **tty_groups; /* all attributes (serial core use only) */
|
||||||
struct serial_rs485 rs485;
|
struct serial_rs485 rs485;
|
||||||
|
|
|
@ -390,7 +390,6 @@ static inline bool tty_throttled(struct tty_struct *tty)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_TTY
|
#ifdef CONFIG_TTY
|
||||||
extern void console_init(void);
|
|
||||||
extern void tty_kref_put(struct tty_struct *tty);
|
extern void tty_kref_put(struct tty_struct *tty);
|
||||||
extern struct pid *tty_get_pgrp(struct tty_struct *tty);
|
extern struct pid *tty_get_pgrp(struct tty_struct *tty);
|
||||||
extern void tty_vhangup_self(void);
|
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 int __init tty_init(void);
|
||||||
extern const char *tty_name(const struct tty_struct *tty);
|
extern const char *tty_name(const struct tty_struct *tty);
|
||||||
#else
|
#else
|
||||||
static inline void console_init(void)
|
|
||||||
{ }
|
|
||||||
static inline void tty_kref_put(struct tty_struct *tty)
|
static inline void tty_kref_put(struct tty_struct *tty)
|
||||||
{ }
|
{ }
|
||||||
static inline struct pid *tty_get_pgrp(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 int is_current_pgrp_orphaned(void);
|
||||||
extern void tty_hangup(struct tty_struct *tty);
|
extern void tty_hangup(struct tty_struct *tty);
|
||||||
extern void tty_vhangup(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 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 __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 no_tty(void);
|
||||||
extern void tty_buffer_free_all(struct tty_port *port);
|
extern void tty_buffer_free_all(struct tty_port *port);
|
||||||
extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld);
|
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 long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||||
extern int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
|
extern int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
|
||||||
unsigned int cmd, unsigned long arg);
|
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 int tty_perform_flush(struct tty_struct *tty, unsigned long arg);
|
||||||
extern void tty_default_fops(struct file_operations *fops);
|
extern void tty_default_fops(struct file_operations *fops);
|
||||||
extern struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx);
|
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 */
|
/* n_tty.c */
|
||||||
extern void n_tty_inherit_ops(struct tty_ldisc_ops *ops);
|
extern void n_tty_inherit_ops(struct tty_ldisc_ops *ops);
|
||||||
|
#ifdef CONFIG_TTY
|
||||||
extern void __init n_tty_init(void);
|
extern void __init n_tty_init(void);
|
||||||
|
#else
|
||||||
|
static inline void n_tty_init(void) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
/* tty_audit.c */
|
/* tty_audit.c */
|
||||||
#ifdef CONFIG_AUDIT
|
#ifdef CONFIG_AUDIT
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#include <linux/initrd.h>
|
#include <linux/initrd.h>
|
||||||
#include <linux/bootmem.h>
|
#include <linux/bootmem.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/tty.h>
|
#include <linux/console.h>
|
||||||
#include <linux/nmi.h>
|
#include <linux/nmi.h>
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
#include <linux/kmod.h>
|
#include <linux/kmod.h>
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/console.h>
|
#include <linux/console.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
|
|
||||||
#include "console_cmdline.h"
|
#include "console_cmdline.h"
|
||||||
#include "braille.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)) {
|
if (!strncmp(*str, "brl,", 4)) {
|
||||||
*brl_options = "";
|
*brl_options = "";
|
||||||
|
@ -15,14 +16,14 @@ char *_braille_console_setup(char **str, char **brl_options)
|
||||||
} else if (!strncmp(*str, "brl=", 4)) {
|
} else if (!strncmp(*str, "brl=", 4)) {
|
||||||
*brl_options = *str + 4;
|
*brl_options = *str + 4;
|
||||||
*str = strchr(*brl_options, ',');
|
*str = strchr(*brl_options, ',');
|
||||||
if (!*str)
|
if (!*str) {
|
||||||
pr_err("need port name after brl=\n");
|
pr_err("need port name after brl=\n");
|
||||||
else
|
return -EINVAL;
|
||||||
*((*str)++) = 0;
|
}
|
||||||
} else
|
*((*str)++) = 0;
|
||||||
return NULL;
|
}
|
||||||
|
|
||||||
return *str;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -9,7 +9,14 @@ braille_set_options(struct console_cmdline *c, char *brl_options)
|
||||||
c->brl_options = 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);
|
_braille_console_setup(char **str, char **brl_options);
|
||||||
|
|
||||||
int
|
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)
|
_braille_console_setup(char **str, char **brl_options)
|
||||||
{
|
{
|
||||||
return NULL;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
|
|
@ -2641,6 +2641,30 @@ int unregister_console(struct console *console)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(unregister_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
|
* 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
|
* be discarded after the initcalls have been run. To make sure that no code
|
||||||
|
|
Loading…
Reference in New Issue