* [serial driver PATCH] Fix typo in sanity check
From: Thomas Jarosch @ 2012-12-28 23:16 UTC (permalink / raw)
To: Alan Cox; +Cc: linux-serial
Detected by cppcheck:
[others/linux/drivers/tty/serial/mxs-auart.c:553]: (style) Same expression on both sides of '||'.
Signed-off-by: Thomas Jarosch <thomas.jarosch@intra2net.com>
---
drivers/tty/serial/mxs-auart.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 6db23b0..fa31bc3 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -550,7 +550,7 @@ static int mxs_auart_dma_init(struct mxs_auart_port *s)
return 0;
/* We do not get the right DMA channels. */
- if (s->dma_channel_rx == -1 || s->dma_channel_rx == -1)
+ if (s->dma_channel_rx == -1 || s->dma_channel_tx == -1)
return -EINVAL;
/* init for RX */
--
1.7.11.7
^ permalink raw reply related
* Re: [PATCH 3/3] serial: rp2: New driver for Comtrol RocketPort 2 cards
From: Alan Cox @ 2012-12-28 10:56 UTC (permalink / raw)
To: Kevin Cernekee; +Cc: alan, gregkh, jslaby, linux-serial
In-Reply-To: <10876da5ca515a76207af4fe303f1e43@localhost>
> - Systems that have a combination of RocketPort 1 + RocketPort 2 cards
> (the PCI ID table in rocket.c actually matches anything with Comtrol's
> vendor ID)
In which case that will need updating to only include the identifiers for
the original rocket port, or ensuring that if it sees an rp2 it returns
an error on the probe so the other probe occurs. The first is probably
preferable as it avoids an un-needed driver load.
> +static const u8 rp2_ucode[] = {
> + 0xF6, 0x8B, 0xE2, 0x86, 0x30, 0x40, 0x66, 0x19, 0x0A, 0x31, 0x40, 0x67,
> + 0x19, 0x0A, 0x86, 0x01, 0x40, 0x11, 0x19, 0x0A, 0x00, 0x40, 0x13, 0x19,
> + 0x0A, 0xFA, 0x83, 0x01, 0x0A, 0x00, 0x0A, 0x41, 0xFF, 0x89, 0xC2, 0x66,
> + 0x86, 0x81, 0x91, 0x40, 0x65, 0x8E, 0x89, 0xC2, 0x66, 0x86, 0x81, 0x88,
> + 0x40, 0x65, 0x85, 0x84, 0x00, 0x82, 0x0A, 0x08, 0x0A, 0x0A, 0x0A, 0x0A,
> + 0x0A, 0x08, 0x0A,
> +};
If that is firmware so is actually code executed by the hardware on the
RocketPort 2 then it should be dynamically loaded with request_firmware.
That is preferred as it avoids some potential licence funnies with GPLv2
> +static void rp2_rx_chars(struct rp2_uart_port *up)
> +{
> + u16 bytes = readw(up->base + RP2_RX_FIFO_COUNT);
> + struct tty_struct *tty = up->port.state->port.tty;
You need to take a reference here and allow for tty being NULL so
tty = tty_port_tty_get(&up->port.state->port);
if (tty)
> + tty_flip_buffer_push(tty);
tty_kref_put()
(you may need to flush the bytes to clear the interrupt in the tty == NULL
case). This allows for a parallel close/hangup clearing the tty and
ensures the tty itself won't be deleted until your tty_kref_put
> +static inline void rp2_flush_fifos(struct rp2_uart_port *up)
> +{
> + rp2_rmw_set(up, RP2_UART_CTL,
> + RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m);
> + udelay(10);
> + rp2_rmw_clr(up, RP2_UART_CTL,
> + RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m);
> +}
This looks wrong. A writel can be posted by the PCI bridge. That means it
may occur after the udelay(10) finishes. However it cannot pass another
read to the same device.
You probably want
rp2_rmw_set(....)
readl(some harmless register)
udelay(10);
rpm2_rmw_clr(....)
> +static void rp2_reset_asic(struct rp2_card *card, unsigned int asic_id)
> +{
> + void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id);
> + u32 clk_cfg;
> +
> + writew(1, base + RP2_GLOBAL_CMD);
> + msleep(100);
> + writel(0, base + RP2_CLK_PRESCALER);
Same problem here
> +static void rp2_init_port(struct rp2_uart_port *up)
> +{
> + int i;
> +
> + writel(RP2_UART_CTL_RESET_CH_m, up->base + RP2_UART_CTL);
> + udelay(1);
> +
> + writel(0, up->base + RP2_TXRX_CTL);
> + writel(0, up->base + RP2_UART_CTL);
> + udelay(1);
And probably here
Alan
^ permalink raw reply
* Re: [PATCH 2/3] tty: Update serial core API documentation
From: Alan Cox @ 2012-12-28 10:46 UTC (permalink / raw)
To: Kevin Cernekee; +Cc: alan, gregkh, jslaby, linux-serial
In-Reply-To: <36136cf35664198e8f218ad347ae566f@localhost>
On Wed, 26 Dec 2012 20:43:42 -0800
Kevin Cernekee <cernekee@gmail.com> wrote:
> Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
> ---
> Documentation/serial/driver | 39 +++++++++++++++++++++++++++++++++++++++
> include/linux/serial_core.h | 8 ++++----
> 2 files changed, 43 insertions(+), 4 deletions(-)
Acked-by: Alan Cox <alan@linux.intel.com>
^ permalink raw reply
* Re: [PATCH 1/3] tty: Fix comments that reference BKL, eventd, old paths
From: Alan Cox @ 2012-12-28 10:45 UTC (permalink / raw)
To: Kevin Cernekee; +Cc: alan, gregkh, jslaby, linux-serial
In-Reply-To: <c141e9cf9329550e53d18c9a8a2c6246@localhost>
On Wed, 26 Dec 2012 20:43:41 -0800
Kevin Cernekee <cernekee@gmail.com> wrote:
> Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
Acked-by: Alan Cox <alan@linux.intel.com>
^ permalink raw reply
* N/A
From: mrs philips Rita @ 2012-12-22 22:09 UTC (permalink / raw)
Dear
friend please i need your quick reply do not ignore this email for no reason
i am Mrs.Rita Philips
Thanks and God Be with you
Best Regards
^ permalink raw reply
* [PATCH 3/3] serial: rp2: New driver for Comtrol RocketPort 2 cards
From: Kevin Cernekee @ 2012-12-27 4:43 UTC (permalink / raw)
To: alan, gregkh, jslaby; +Cc: linux-serial
In-Reply-To: <c141e9cf9329550e53d18c9a8a2c6246@localhost>
Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
---
drivers/tty/serial/Kconfig | 24 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/rp2.c | 828 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/serial_core.h | 3 +
4 files changed, 856 insertions(+)
create mode 100644 drivers/tty/serial/rp2.c
I am able to run basic TX/RX serial console traffic, zmodem, etc. on a
RocketPort Express Octa DB-9 card.
It would be helpful to get test coverage on other configurations like:
- Dual ASIC (32-port) cards
- Systems with multiple cards installed
- Multiple-card systems with more than CONFIG_SERIAL_RP2_NR_UARTS ports
- Anything involving heavy load, terminal servers, etc.
- Anything that uses the modem status lines or flow control
- Systems that have a combination of RocketPort 1 + RocketPort 2 cards
(the PCI ID table in rocket.c actually matches anything with Comtrol's
vendor ID)
Non-RS232 interfaces, such as RS422/RS485/SMPTE, are not currently
supported. They could be, if somebody has a use for them.
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 59c23d0..555f3ca 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1447,4 +1447,28 @@ config SERIAL_ARC_NR_PORTS
Set this to the number of serial ports you want the driver
to support.
+config SERIAL_RP2
+ tristate "Comtrol RocketPort EXPRESS/INFINITY support"
+ depends on PCI
+ select SERIAL_CORE
+ help
+ This driver supports the Comtrol RocketPort EXPRESS and
+ RocketPort INFINITY families of PCI/PCIe multiport serial adapters.
+ These adapters use a "RocketPort 2" ASIC that is not compatible
+ with the original RocketPort driver (CONFIG_ROCKETPORT).
+
+ To compile this driver as a module, choose M here: the
+ module will be called rp2.
+
+ If you want to compile this driver into the kernel, say Y here. If
+ you don't have a suitable RocketPort card installed, say N.
+
+config SERIAL_RP2_NR_UARTS
+ int "Maximum number of RocketPort EXPRESS/INFINITY ports"
+ depends on SERIAL_RP2
+ default "32"
+ help
+ If multiple cards are present, the default limit of 32 ports may
+ need to be increased.
+
endmenu
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index df1b998..a582ee9 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -83,3 +83,4 @@ obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o
obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o
obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
obj-$(CONFIG_SERIAL_ARC) += arc_uart.o
+obj-$(CONFIG_SERIAL_RP2) += rp2.o
diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c
new file mode 100644
index 0000000..587e411
--- /dev/null
+++ b/drivers/tty/serial/rp2.c
@@ -0,0 +1,828 @@
+/*
+ * Driver for Comtrol RocketPort EXPRESS/INFINITY cards
+ *
+ * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com>
+ *
+ * Inspired by, and loosely based on:
+ *
+ * ar933x_uart.c
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * rocketport_infinity_express-linux-1.20.tar.gz
+ * Copyright (C) 2004-2011 Comtrol, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/pci.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/types.h>
+
+#define DRV_NAME "rp2"
+
+#define PORTS_PER_ASIC 16
+#define ALL_PORTS_MASK (BIT(PORTS_PER_ASIC) - 1)
+
+#define UART_CLOCK 44236800
+#define DEFAULT_BAUD_DIV (UART_CLOCK / (9600 * 16))
+#define FIFO_SIZE 512
+
+/* BAR0 registers */
+#define RP2_FPGA_CTL0 0x110
+#define RP2_FPGA_CTL1 0x11c
+#define RP2_IRQ_MASK 0x1ec
+#define RP2_IRQ_MASK_EN_m BIT(0)
+#define RP2_IRQ_STATUS 0x1f0
+
+/* BAR1 registers */
+#define RP2_ASIC_SPACING 0x1000
+#define RP2_ASIC_OFFSET(i) ((i) << ilog2(RP2_ASIC_SPACING))
+
+#define RP2_PORT_BASE 0x000
+#define RP2_PORT_SPACING 0x040
+
+#define RP2_UCODE_BASE 0x400
+#define RP2_UCODE_SPACING 0x80
+
+#define RP2_CLK_PRESCALER 0xc00
+#define RP2_CH_IRQ_STAT 0xc04
+#define RP2_CH_IRQ_MASK 0xc08
+#define RP2_ASIC_IRQ 0xd00
+#define RP2_ASIC_IRQ_EN_m BIT(20)
+#define RP2_GLOBAL_CMD 0xd0c
+#define RP2_ASIC_CFG 0xd04
+
+/* port registers */
+#define RP2_DATA_DWORD 0x000
+
+#define RP2_DATA_BYTE 0x008
+#define RP2_DATA_BYTE_ERR_PARITY_m BIT(8)
+#define RP2_DATA_BYTE_ERR_OVERRUN_m BIT(9)
+#define RP2_DATA_BYTE_ERR_FRAMING_m BIT(10)
+#define RP2_DATA_BYTE_BREAK_m BIT(11)
+
+/* This lets uart_insert_char() drop bytes received on a !CREAD port */
+#define RP2_DUMMY_READ BIT(16)
+
+#define RP2_DATA_BYTE_EXCEPTION_MASK (RP2_DATA_BYTE_ERR_PARITY_m | \
+ RP2_DATA_BYTE_ERR_OVERRUN_m | \
+ RP2_DATA_BYTE_ERR_FRAMING_m | \
+ RP2_DATA_BYTE_BREAK_m)
+
+#define RP2_RX_FIFO_COUNT 0x00c
+#define RP2_TX_FIFO_COUNT 0x00e
+
+#define RP2_CHAN_STAT 0x010
+#define RP2_CHAN_STAT_RXDATA_m BIT(0)
+#define RP2_CHAN_STAT_DCD_m BIT(3)
+#define RP2_CHAN_STAT_DSR_m BIT(4)
+#define RP2_CHAN_STAT_CTS_m BIT(5)
+#define RP2_CHAN_STAT_RI_m BIT(6)
+#define RP2_CHAN_STAT_OVERRUN_m BIT(13)
+#define RP2_CHAN_STAT_DSR_CHANGED_m BIT(16)
+#define RP2_CHAN_STAT_CTS_CHANGED_m BIT(17)
+#define RP2_CHAN_STAT_CD_CHANGED_m BIT(18)
+#define RP2_CHAN_STAT_RI_CHANGED_m BIT(22)
+#define RP2_CHAN_STAT_TXEMPTY_m BIT(25)
+
+#define RP2_CHAN_STAT_MS_CHANGED_MASK (RP2_CHAN_STAT_DSR_CHANGED_m | \
+ RP2_CHAN_STAT_CTS_CHANGED_m | \
+ RP2_CHAN_STAT_CD_CHANGED_m | \
+ RP2_CHAN_STAT_RI_CHANGED_m)
+
+#define RP2_TXRX_CTL 0x014
+#define RP2_TXRX_CTL_MSRIRQ_m BIT(0)
+#define RP2_TXRX_CTL_RXIRQ_m BIT(2)
+#define RP2_TXRX_CTL_RX_TRIG_s 3
+#define RP2_TXRX_CTL_RX_TRIG_m (0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_1 (0x1 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_256 (0x2 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_448 (0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_EN_m BIT(5)
+#define RP2_TXRX_CTL_RTSFLOW_m BIT(6)
+#define RP2_TXRX_CTL_DTRFLOW_m BIT(7)
+#define RP2_TXRX_CTL_TX_TRIG_s 16
+#define RP2_TXRX_CTL_TX_TRIG_m (0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_DSRFLOW_m BIT(18)
+#define RP2_TXRX_CTL_TXIRQ_m BIT(19)
+#define RP2_TXRX_CTL_CTSFLOW_m BIT(23)
+#define RP2_TXRX_CTL_TX_EN_m BIT(24)
+#define RP2_TXRX_CTL_RTS_m BIT(25)
+#define RP2_TXRX_CTL_DTR_m BIT(26)
+#define RP2_TXRX_CTL_LOOP_m BIT(27)
+#define RP2_TXRX_CTL_BREAK_m BIT(28)
+#define RP2_TXRX_CTL_CMSPAR_m BIT(29)
+#define RP2_TXRX_CTL_nPARODD_m BIT(30)
+#define RP2_TXRX_CTL_PARENB_m BIT(31)
+
+#define RP2_UART_CTL 0x018
+#define RP2_UART_CTL_MODE_s 0
+#define RP2_UART_CTL_MODE_m (0x7 << RP2_UART_CTL_MODE_s)
+#define RP2_UART_CTL_MODE_rs232 (0x1 << RP2_UART_CTL_MODE_s)
+#define RP2_UART_CTL_FLUSH_RX_m BIT(3)
+#define RP2_UART_CTL_FLUSH_TX_m BIT(4)
+#define RP2_UART_CTL_RESET_CH_m BIT(5)
+#define RP2_UART_CTL_XMIT_EN_m BIT(6)
+#define RP2_UART_CTL_DATABITS_s 8
+#define RP2_UART_CTL_DATABITS_m (0x3 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_8 (0x3 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_7 (0x2 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_6 (0x1 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_5 (0x0 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_STOPBITS_m BIT(10)
+
+#define RP2_BAUD 0x01c
+
+/* ucode registers */
+#define RP2_TX_SWFLOW 0x02
+#define RP2_TX_SWFLOW_ena 0x81
+#define RP2_TX_SWFLOW_dis 0x9d
+
+#define RP2_RX_SWFLOW 0x0c
+#define RP2_RX_SWFLOW_ena 0x81
+#define RP2_RX_SWFLOW_dis 0x8d
+
+#define RP2_RX_FIFO 0x37
+#define RP2_RX_FIFO_ena 0x08
+#define RP2_RX_FIFO_dis 0x81
+
+static struct uart_driver rp2_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = DRV_NAME,
+ .dev_name = "ttyR",
+ .nr = CONFIG_SERIAL_RP2_NR_UARTS,
+};
+
+static const u8 rp2_ucode[] = {
+ 0xF6, 0x8B, 0xE2, 0x86, 0x30, 0x40, 0x66, 0x19, 0x0A, 0x31, 0x40, 0x67,
+ 0x19, 0x0A, 0x86, 0x01, 0x40, 0x11, 0x19, 0x0A, 0x00, 0x40, 0x13, 0x19,
+ 0x0A, 0xFA, 0x83, 0x01, 0x0A, 0x00, 0x0A, 0x41, 0xFF, 0x89, 0xC2, 0x66,
+ 0x86, 0x81, 0x91, 0x40, 0x65, 0x8E, 0x89, 0xC2, 0x66, 0x86, 0x81, 0x88,
+ 0x40, 0x65, 0x85, 0x84, 0x00, 0x82, 0x0A, 0x08, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x08, 0x0A,
+};
+
+struct rp2_card;
+
+struct rp2_uart_port {
+ struct uart_port port;
+ int idx;
+ int ignore_rx;
+ struct rp2_card *card;
+ void __iomem *asic_base;
+ void __iomem *base;
+ void __iomem *ucode;
+};
+
+struct rp2_card {
+ struct rp2_uart_port *ports;
+ int n_ports;
+ int smpte;
+ void __iomem *bar0;
+ void __iomem *bar1;
+ spinlock_t card_lock;
+};
+
+#define RP_ID(prod) PCI_VDEVICE(RP, (prod))
+#define RP_CAP(ports, smpte) (((ports) << 8) | ((smpte) << 0))
+
+static inline void rp2_decode_cap(const struct pci_device_id *id,
+ int *ports, int *smpte)
+{
+ *ports = id->driver_data >> 8;
+ *smpte = id->driver_data & 0xff;
+}
+
+static DEFINE_SPINLOCK(rp2_minor_lock);
+static int rp2_minor_next;
+
+static int rp2_alloc_ports(int n_ports)
+{
+ int ret = -ENOSPC;
+
+ spin_lock(&rp2_minor_lock);
+ if (rp2_minor_next + n_ports <= CONFIG_SERIAL_RP2_NR_UARTS) {
+ /* sorry, no support for hot unplugging individual cards */
+ ret = rp2_minor_next;
+ rp2_minor_next += n_ports;
+ }
+ spin_unlock(&rp2_minor_lock);
+
+ return ret;
+}
+
+static inline struct rp2_uart_port *port_to_up(struct uart_port *port)
+{
+ return container_of(port, struct rp2_uart_port, port);
+}
+
+static void rp2_rmw(struct rp2_uart_port *up, int reg,
+ u32 clr_bits, u32 set_bits)
+{
+ u32 tmp = readl(up->base + reg);
+ tmp &= ~clr_bits;
+ tmp |= set_bits;
+ writel(tmp, up->base + reg);
+}
+
+static void rp2_rmw_clr(struct rp2_uart_port *up, int reg, u32 val)
+{
+ rp2_rmw(up, reg, val, 0);
+}
+
+static void rp2_rmw_set(struct rp2_uart_port *up, int reg, u32 val)
+{
+ rp2_rmw(up, reg, 0, val);
+}
+
+static void rp2_mask_ch_irq(struct rp2_uart_port *up, int ch_num,
+ int is_enabled)
+{
+ unsigned long flags, irq_mask;
+
+ spin_lock_irqsave(&up->card->card_lock, flags);
+
+ irq_mask = readl(up->asic_base + RP2_CH_IRQ_MASK);
+ if (is_enabled)
+ irq_mask &= ~BIT(ch_num);
+ else
+ irq_mask |= BIT(ch_num);
+ writel(irq_mask, up->asic_base + RP2_CH_IRQ_MASK);
+
+ spin_unlock_irqrestore(&up->card->card_lock, flags);
+}
+
+static unsigned int rp2_uart_tx_empty(struct uart_port *port)
+{
+ struct rp2_uart_port *up = port_to_up(port);
+ unsigned long tx_fifo_bytes, flags;
+
+ /*
+ * This should probably check the transmitter, not the FIFO.
+ * But the TXEMPTY bit doesn't seem to work unless the TX IRQ is
+ * enabled.
+ */
+ spin_lock_irqsave(&up->port.lock, flags);
+ tx_fifo_bytes = readw(up->base + RP2_TX_FIFO_COUNT);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ return tx_fifo_bytes ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int rp2_uart_get_mctrl(struct uart_port *port)
+{
+ struct rp2_uart_port *up = port_to_up(port);
+ u32 status;
+
+ status = readl(up->base + RP2_CHAN_STAT);
+ return ((status & RP2_CHAN_STAT_DCD_m) ? TIOCM_CAR : 0) |
+ ((status & RP2_CHAN_STAT_DSR_m) ? TIOCM_DSR : 0) |
+ ((status & RP2_CHAN_STAT_CTS_m) ? TIOCM_CTS : 0) |
+ ((status & RP2_CHAN_STAT_RI_m) ? TIOCM_RI : 0);
+}
+
+static void rp2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ rp2_rmw(port_to_up(port), RP2_TXRX_CTL,
+ RP2_TXRX_CTL_DTR_m | RP2_TXRX_CTL_RTS_m | RP2_TXRX_CTL_LOOP_m,
+ ((mctrl & TIOCM_DTR) ? RP2_TXRX_CTL_DTR_m : 0) |
+ ((mctrl & TIOCM_RTS) ? RP2_TXRX_CTL_RTS_m : 0) |
+ ((mctrl & TIOCM_LOOP) ? RP2_TXRX_CTL_LOOP_m : 0));
+}
+
+static void rp2_uart_start_tx(struct uart_port *port)
+{
+ rp2_rmw_set(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_TXIRQ_m);
+}
+
+static void rp2_uart_stop_tx(struct uart_port *port)
+{
+ rp2_rmw_clr(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_TXIRQ_m);
+}
+
+static void rp2_uart_stop_rx(struct uart_port *port)
+{
+ rp2_rmw_clr(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_RXIRQ_m);
+}
+
+static void rp2_uart_break_ctl(struct uart_port *port, int break_state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ rp2_rmw(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_BREAK_m,
+ break_state ? RP2_TXRX_CTL_BREAK_m : 0);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void rp2_uart_enable_ms(struct uart_port *port)
+{
+ rp2_rmw_set(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_MSRIRQ_m);
+}
+
+static void __rp2_uart_set_termios(struct rp2_uart_port *up,
+ unsigned long cfl,
+ unsigned long ifl,
+ unsigned int baud_div)
+{
+ /* baud rate divisor (calculated elsewhere). 0 = divide-by-1 */
+ writew(baud_div - 1, up->base + RP2_BAUD);
+
+ /* data bits and stop bits */
+ rp2_rmw(up, RP2_UART_CTL,
+ RP2_UART_CTL_STOPBITS_m | RP2_UART_CTL_DATABITS_m,
+ ((cfl & CSTOPB) ? RP2_UART_CTL_STOPBITS_m : 0) |
+ (((cfl & CSIZE) == CS8) ? RP2_UART_CTL_DATABITS_8 : 0) |
+ (((cfl & CSIZE) == CS7) ? RP2_UART_CTL_DATABITS_7 : 0) |
+ (((cfl & CSIZE) == CS6) ? RP2_UART_CTL_DATABITS_6 : 0) |
+ (((cfl & CSIZE) == CS5) ? RP2_UART_CTL_DATABITS_5 : 0));
+
+ /* parity and hardware flow control */
+ rp2_rmw(up, RP2_TXRX_CTL,
+ RP2_TXRX_CTL_PARENB_m | RP2_TXRX_CTL_nPARODD_m |
+ RP2_TXRX_CTL_CMSPAR_m | RP2_TXRX_CTL_DTRFLOW_m |
+ RP2_TXRX_CTL_DSRFLOW_m | RP2_TXRX_CTL_RTSFLOW_m |
+ RP2_TXRX_CTL_CTSFLOW_m,
+ ((cfl & PARENB) ? RP2_TXRX_CTL_PARENB_m : 0) |
+ ((cfl & PARODD) ? 0 : RP2_TXRX_CTL_nPARODD_m) |
+ ((cfl & CMSPAR) ? RP2_TXRX_CTL_CMSPAR_m : 0) |
+ ((cfl & CRTSCTS) ? (RP2_TXRX_CTL_RTSFLOW_m |
+ RP2_TXRX_CTL_CTSFLOW_m) : 0));
+
+ /* XON/XOFF software flow control */
+ writeb((ifl & IXON) ? RP2_TX_SWFLOW_ena : RP2_TX_SWFLOW_dis,
+ up->ucode + RP2_TX_SWFLOW);
+ writeb((ifl & IXOFF) ? RP2_RX_SWFLOW_ena : RP2_RX_SWFLOW_dis,
+ up->ucode + RP2_RX_SWFLOW);
+}
+
+static void rp2_uart_set_termios(struct uart_port *port,
+ struct ktermios *new,
+ struct ktermios *old)
+{
+ struct rp2_uart_port *up = port_to_up(port);
+ unsigned long flags;
+ unsigned int baud, baud_div;
+
+ baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+ baud_div = uart_get_divisor(port, baud);
+
+ if (tty_termios_baud_rate(new))
+ tty_termios_encode_baud_rate(new, baud, baud);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* ignore all characters if CREAD is not set */
+ port->ignore_status_mask = (new->c_cflag & CREAD) ? 0 : RP2_DUMMY_READ;
+
+ __rp2_uart_set_termios(up, new->c_cflag, new->c_iflag, baud_div);
+ uart_update_timeout(port, new->c_cflag, baud);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void rp2_rx_chars(struct rp2_uart_port *up)
+{
+ u16 bytes = readw(up->base + RP2_RX_FIFO_COUNT);
+ struct tty_struct *tty = up->port.state->port.tty;
+
+ for (; bytes != 0; bytes--) {
+ u32 byte = readw(up->base + RP2_DATA_BYTE) | RP2_DUMMY_READ;
+ char ch = byte & 0xff;
+
+ if (likely(!(byte & RP2_DATA_BYTE_EXCEPTION_MASK))) {
+ if (!uart_handle_sysrq_char(&up->port, ch))
+ uart_insert_char(&up->port, byte, 0, ch,
+ TTY_NORMAL);
+ } else {
+ char flag = TTY_NORMAL;
+
+ if (byte & RP2_DATA_BYTE_BREAK_m)
+ flag = TTY_BREAK;
+ else if (byte & RP2_DATA_BYTE_ERR_FRAMING_m)
+ flag = TTY_FRAME;
+ else if (byte & RP2_DATA_BYTE_ERR_PARITY_m)
+ flag = TTY_PARITY;
+ uart_insert_char(&up->port, byte,
+ RP2_DATA_BYTE_ERR_OVERRUN_m, ch, flag);
+ }
+ up->port.icount.rx++;
+ }
+ tty_flip_buffer_push(tty);
+}
+
+static void rp2_tx_chars(struct rp2_uart_port *up)
+{
+ u16 max_tx = FIFO_SIZE - readw(up->base + RP2_TX_FIFO_COUNT);
+ struct circ_buf *xmit = &up->port.state->xmit;
+
+ if (uart_tx_stopped(&up->port)) {
+ rp2_uart_stop_tx(&up->port);
+ return;
+ }
+
+ for (; max_tx != 0; max_tx--) {
+ if (up->port.x_char) {
+ writeb(up->port.x_char, up->base + RP2_DATA_BYTE);
+ up->port.x_char = 0;
+ up->port.icount.tx++;
+ continue;
+ }
+ if (uart_circ_empty(xmit)) {
+ rp2_uart_stop_tx(&up->port);
+ break;
+ }
+ writeb(xmit->buf[xmit->tail], up->base + RP2_DATA_BYTE);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ up->port.icount.tx++;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+}
+
+static void rp2_ch_interrupt(struct rp2_uart_port *up)
+{
+ u32 status;
+
+ spin_lock(&up->port.lock);
+
+ /*
+ * The IRQ status bits are clear-on-write. Other status bits in
+ * this register aren't, so it's harmless to write to them.
+ */
+ status = readl(up->base + RP2_CHAN_STAT);
+ writel(status, up->base + RP2_CHAN_STAT);
+
+ if (status & RP2_CHAN_STAT_RXDATA_m)
+ rp2_rx_chars(up);
+ if (status & RP2_CHAN_STAT_TXEMPTY_m)
+ rp2_tx_chars(up);
+ if (status & RP2_CHAN_STAT_MS_CHANGED_MASK)
+ wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+
+ spin_unlock(&up->port.lock);
+}
+
+static int rp2_asic_interrupt(struct rp2_card *card, unsigned int asic_id)
+{
+ void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id);
+ int ch, handled = 0;
+ unsigned long status = readl(base + RP2_CH_IRQ_STAT) &
+ ~readl(base + RP2_CH_IRQ_MASK);
+
+ for_each_set_bit(ch, &status, PORTS_PER_ASIC) {
+ rp2_ch_interrupt(&card->ports[ch]);
+ handled++;
+ }
+ return handled;
+}
+
+static irqreturn_t rp2_uart_interrupt(int irq, void *dev_id)
+{
+ struct rp2_card *card = dev_id;
+ int handled;
+
+ handled = rp2_asic_interrupt(card, 0);
+ if (card->n_ports >= PORTS_PER_ASIC)
+ handled += rp2_asic_interrupt(card, 1);
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static inline void rp2_flush_fifos(struct rp2_uart_port *up)
+{
+ rp2_rmw_set(up, RP2_UART_CTL,
+ RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m);
+ udelay(10);
+ rp2_rmw_clr(up, RP2_UART_CTL,
+ RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m);
+}
+
+static int rp2_uart_startup(struct uart_port *port)
+{
+ struct rp2_uart_port *up = port_to_up(port);
+
+ rp2_flush_fifos(up);
+ rp2_rmw(up, RP2_TXRX_CTL, RP2_TXRX_CTL_MSRIRQ_m, RP2_TXRX_CTL_RXIRQ_m);
+ rp2_rmw(up, RP2_TXRX_CTL, RP2_TXRX_CTL_RX_TRIG_m,
+ RP2_TXRX_CTL_RX_TRIG_1);
+ rp2_rmw(up, RP2_CHAN_STAT, 0, 0);
+ rp2_mask_ch_irq(up, up->idx, 1);
+
+ return 0;
+}
+
+static void rp2_uart_shutdown(struct uart_port *port)
+{
+ struct rp2_uart_port *up = port_to_up(port);
+ unsigned long flags;
+
+ rp2_uart_break_ctl(port, 0);
+
+ spin_lock_irqsave(&port->lock, flags);
+ rp2_mask_ch_irq(up, up->idx, 0);
+ rp2_rmw(up, RP2_CHAN_STAT, 0, 0);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *rp2_uart_type(struct uart_port *port)
+{
+ return (port->type == PORT_RP2) ? "RocketPort 2 UART" : NULL;
+}
+
+static void rp2_uart_release_port(struct uart_port *port)
+{
+ /* Nothing to release ... */
+}
+
+static int rp2_uart_request_port(struct uart_port *port)
+{
+ /* UARTs always present */
+ return 0;
+}
+
+static void rp2_uart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_RP2;
+}
+
+static int rp2_uart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_RP2)
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct uart_ops rp2_uart_ops = {
+ .tx_empty = rp2_uart_tx_empty,
+ .set_mctrl = rp2_uart_set_mctrl,
+ .get_mctrl = rp2_uart_get_mctrl,
+ .stop_tx = rp2_uart_stop_tx,
+ .start_tx = rp2_uart_start_tx,
+ .stop_rx = rp2_uart_stop_rx,
+ .enable_ms = rp2_uart_enable_ms,
+ .break_ctl = rp2_uart_break_ctl,
+ .startup = rp2_uart_startup,
+ .shutdown = rp2_uart_shutdown,
+ .set_termios = rp2_uart_set_termios,
+ .type = rp2_uart_type,
+ .release_port = rp2_uart_release_port,
+ .request_port = rp2_uart_request_port,
+ .config_port = rp2_uart_config_port,
+ .verify_port = rp2_uart_verify_port,
+};
+
+static void rp2_reset_asic(struct rp2_card *card, unsigned int asic_id)
+{
+ void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id);
+ u32 clk_cfg;
+
+ writew(1, base + RP2_GLOBAL_CMD);
+ msleep(100);
+ writel(0, base + RP2_CLK_PRESCALER);
+
+ /* TDM clock configuration */
+ clk_cfg = readw(base + RP2_ASIC_CFG);
+ clk_cfg = (clk_cfg & ~BIT(8)) | BIT(9);
+ writew(clk_cfg, base + RP2_ASIC_CFG);
+
+ /* IRQ routing */
+ writel(ALL_PORTS_MASK, base + RP2_CH_IRQ_MASK);
+ writel(RP2_ASIC_IRQ_EN_m, base + RP2_ASIC_IRQ);
+}
+
+static void rp2_init_card(struct rp2_card *card)
+{
+ writel(4, card->bar0 + RP2_FPGA_CTL0);
+ writel(0, card->bar0 + RP2_FPGA_CTL1);
+
+ rp2_reset_asic(card, 0);
+ if (card->n_ports >= PORTS_PER_ASIC)
+ rp2_reset_asic(card, 1);
+
+ writel(RP2_IRQ_MASK_EN_m, card->bar0 + RP2_IRQ_MASK);
+}
+
+static void rp2_init_port(struct rp2_uart_port *up)
+{
+ int i;
+
+ writel(RP2_UART_CTL_RESET_CH_m, up->base + RP2_UART_CTL);
+ udelay(1);
+
+ writel(0, up->base + RP2_TXRX_CTL);
+ writel(0, up->base + RP2_UART_CTL);
+ udelay(1);
+
+ rp2_flush_fifos(up);
+
+ for (i = 0; i < ARRAY_SIZE(rp2_ucode); i++)
+ writeb(rp2_ucode[i], up->ucode + i);
+
+ __rp2_uart_set_termios(up, CS8 | CREAD | CLOCAL, 0, DEFAULT_BAUD_DIV);
+ rp2_uart_set_mctrl(&up->port, 0);
+
+ writeb(RP2_RX_FIFO_ena, up->ucode + RP2_RX_FIFO);
+ rp2_rmw(up, RP2_UART_CTL, RP2_UART_CTL_MODE_m,
+ RP2_UART_CTL_XMIT_EN_m | RP2_UART_CTL_MODE_rs232);
+ rp2_rmw_set(up, RP2_TXRX_CTL,
+ RP2_TXRX_CTL_TX_EN_m | RP2_TXRX_CTL_RX_EN_m);
+}
+
+static int __devinit rp2_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct rp2_card *card;
+ struct rp2_uart_port *ports;
+ int rc, i, minor_start;
+ void __iomem * const *bars;
+ resource_size_t phys_base;
+
+ card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+ pci_set_drvdata(pdev, card);
+ spin_lock_init(&card->card_lock);
+
+ rc = pcim_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pcim_iomap_regions_request_all(pdev, 0x03, DRV_NAME);
+ if (rc)
+ return rc;
+
+ bars = pcim_iomap_table(pdev);
+ card->bar0 = bars[0];
+ card->bar1 = bars[1];
+ phys_base = pci_resource_start(pdev, 1);
+
+ rp2_decode_cap(id, &card->n_ports, &card->smpte);
+
+ minor_start = rp2_alloc_ports(card->n_ports);
+ if (minor_start < 0) {
+ dev_err(&pdev->dev,
+ "too many ports (try increasing CONFIG_SERIAL_RP2_NR_UARTS)\n");
+ return -EINVAL;
+ }
+
+ rp2_init_card(card);
+
+ ports = devm_kzalloc(&pdev->dev, sizeof(*ports) * card->n_ports,
+ GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+ card->ports = ports;
+
+ rc = devm_request_irq(&pdev->dev, pdev->irq, rp2_uart_interrupt,
+ IRQF_SHARED, DRV_NAME, card);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < card->n_ports; i++) {
+ struct rp2_uart_port *rp = &ports[i];
+ struct uart_port *p;
+ int j = (unsigned)i % PORTS_PER_ASIC;
+
+ rp->asic_base = card->bar1;
+ rp->base = card->bar1 + RP2_PORT_BASE + j*RP2_PORT_SPACING;
+ rp->ucode = card->bar1 + RP2_UCODE_BASE + j*RP2_UCODE_SPACING;
+ rp->card = card;
+ rp->idx = j;
+
+ p = &rp->port;
+ p->line = minor_start + i;
+ p->dev = &pdev->dev;
+ p->type = PORT_RP2;
+ p->iotype = UPIO_MEM32;
+ p->uartclk = UART_CLOCK;
+ p->regshift = 2;
+ p->fifosize = FIFO_SIZE;
+ p->ops = &rp2_uart_ops;
+ p->irq = pdev->irq;
+ p->membase = rp->base;
+ p->mapbase = phys_base + RP2_PORT_BASE + j*RP2_PORT_SPACING;
+
+ if (i >= PORTS_PER_ASIC) {
+ rp->asic_base += RP2_ASIC_SPACING;
+ rp->base += RP2_ASIC_SPACING;
+ rp->ucode += RP2_ASIC_SPACING;
+ p->mapbase += RP2_ASIC_SPACING;
+ }
+
+ rp2_init_port(rp);
+ rc = uart_add_one_port(&rp2_uart_driver, p);
+ if (rc) {
+ for (i--; i >= 0; i--)
+ uart_remove_one_port(&rp2_uart_driver,
+ &ports[i].port);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static void __devexit rp2_remove(struct pci_dev *pdev)
+{
+ struct rp2_card *card = pci_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < card->n_ports; i++)
+ uart_remove_one_port(&rp2_uart_driver, &card->ports[i].port);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(rp2_pci_tbl) = {
+
+ /* RocketPort INFINITY cards */
+
+ { RP_ID(0x0040), RP_CAP(8, 0) }, /* INF Octa, RJ45, selectable */
+ { RP_ID(0x0041), RP_CAP(32, 0) }, /* INF 32, ext interface */
+ { RP_ID(0x0042), RP_CAP(8, 0) }, /* INF Octa, ext interface */
+ { RP_ID(0x0043), RP_CAP(16, 0) }, /* INF 16, ext interface */
+ { RP_ID(0x0044), RP_CAP(4, 0) }, /* INF Quad, DB, selectable */
+ { RP_ID(0x0045), RP_CAP(8, 0) }, /* INF Octa, DB, selectable */
+ { RP_ID(0x0046), RP_CAP(4, 0) }, /* INF Quad, ext interface */
+ { RP_ID(0x0047), RP_CAP(4, 0) }, /* INF Quad, RJ45 */
+ { RP_ID(0x004a), RP_CAP(4, 0) }, /* INF Plus, Quad */
+ { RP_ID(0x004b), RP_CAP(8, 0) }, /* INF Plus, Octa */
+ { RP_ID(0x004c), RP_CAP(8, 0) }, /* INF III, Octa */
+ { RP_ID(0x004d), RP_CAP(4, 0) }, /* INF III, Quad */
+ { RP_ID(0x004e), RP_CAP(2, 0) }, /* INF Plus, 2, RS232 */
+ { RP_ID(0x004f), RP_CAP(2, 1) }, /* INF Plus, 2, SMPTE */
+ { RP_ID(0x0050), RP_CAP(4, 0) }, /* INF Plus, Quad, RJ45 */
+ { RP_ID(0x0051), RP_CAP(8, 0) }, /* INF Plus, Octa, RJ45 */
+ { RP_ID(0x0052), RP_CAP(8, 1) }, /* INF Octa, SMPTE */
+
+ /* RocketPort EXPRESS cards */
+
+ { RP_ID(0x0060), RP_CAP(8, 0) }, /* EXP Octa, RJ45, selectable */
+ { RP_ID(0x0061), RP_CAP(32, 0) }, /* EXP 32, ext interface */
+ { RP_ID(0x0062), RP_CAP(8, 0) }, /* EXP Octa, ext interface */
+ { RP_ID(0x0063), RP_CAP(16, 0) }, /* EXP 16, ext interface */
+ { RP_ID(0x0064), RP_CAP(4, 0) }, /* EXP Quad, DB, selectable */
+ { RP_ID(0x0065), RP_CAP(8, 0) }, /* EXP Octa, DB, selectable */
+ { RP_ID(0x0066), RP_CAP(4, 0) }, /* EXP Quad, ext interface */
+ { RP_ID(0x0067), RP_CAP(4, 0) }, /* EXP Quad, RJ45 */
+ { RP_ID(0x0068), RP_CAP(8, 0) }, /* EXP Octa, RJ11 */
+ { RP_ID(0x0072), RP_CAP(8, 1) }, /* EXP Octa, SMPTE */
+};
+
+static struct pci_driver rp2_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = rp2_pci_tbl,
+ .probe = rp2_probe,
+ .remove = __devexit_p(rp2_remove),
+};
+
+static int __init rp2_uart_init(void)
+{
+ int rc;
+
+ rc = uart_register_driver(&rp2_uart_driver);
+ if (rc)
+ return rc;
+
+ rc = pci_register_driver(&rp2_pci_driver);
+ if (rc) {
+ uart_unregister_driver(&rp2_uart_driver);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void __exit rp2_uart_exit(void)
+{
+ pci_unregister_driver(&rp2_pci_driver);
+ uart_unregister_driver(&rp2_uart_driver);
+}
+
+module_init(rp2_uart_init);
+module_exit(rp2_uart_exit);
+
+MODULE_DESCRIPTION("Comtrol RocketPort EXPRESS/INFINITY driver");
+MODULE_AUTHOR("Kevin Cernekee <cernekee@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 78f99d9..9dd47a5 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -219,4 +219,7 @@
/* ARC (Synopsys) on-chip UART */
#define PORT_ARC 101
+/* Rocketport EXPRESS/INFINITY */
+#define PORT_RP2 102
+
#endif /* _UAPILINUX_SERIAL_CORE_H */
--
1.7.10.4
^ permalink raw reply related
* [PATCH 2/3] tty: Update serial core API documentation
From: Kevin Cernekee @ 2012-12-27 4:43 UTC (permalink / raw)
To: alan, gregkh, jslaby; +Cc: linux-serial
In-Reply-To: <c141e9cf9329550e53d18c9a8a2c6246@localhost>
Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
---
Documentation/serial/driver | 39 +++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 8 ++++----
2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/Documentation/serial/driver b/Documentation/serial/driver
index 0a25a91..c680678 100644
--- a/Documentation/serial/driver
+++ b/Documentation/serial/driver
@@ -133,6 +133,16 @@ hardware.
Interrupts: locally disabled.
This call must not sleep
+ send_xchar(port,ch)
+ Transmit a high priority character, even if the port is stopped.
+ This is used to implement XON/XOFF flow control and tcflow(). If
+ the serial driver does not implement this function, the tty core
+ will append the character to the circular buffer and then call
+ start_tx() / stop_tx() to flush the data out.
+
+ Locking: none.
+ Interrupts: caller dependent.
+
stop_rx(port)
Stop receiving characters; the port is in the process of
being closed.
@@ -255,6 +265,10 @@ hardware.
Locking: none.
Interrupts: caller dependent.
+ set_wake(port,state)
+ Enable/disable power management wakeup on serial activity. Not
+ currently implemented.
+
type(port)
Return a pointer to a string constant describing the specified
port, or return NULL, in which case the string 'unknown' is
@@ -307,6 +321,31 @@ hardware.
Locking: none.
Interrupts: caller dependent.
+ poll_init(port)
+ Called by kgdb to perform the minimal hardware initialization needed
+ to support poll_put_char() and poll_get_char(). Unlike ->startup()
+ this should not request interrupts.
+
+ Locking: tty_mutex and tty_port->mutex taken.
+ Interrupts: n/a.
+
+ poll_put_char(port,ch)
+ Called by kgdb to write a single character directly to the serial
+ port. It can and should block until there is space in the TX FIFO.
+
+ Locking: none.
+ Interrupts: caller dependent.
+ This call must not sleep
+
+ poll_get_char(port)
+ Called by kgdb to read a single character directly from the serial
+ port. If data is available, it should be returned; otherwise
+ the function should return NO_POLL_CHAR immediately.
+
+ Locking: none.
+ Interrupts: caller dependent.
+ This call must not sleep
+
Other functions
---------------
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index c6690a2..ccfeab2 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -37,8 +37,8 @@ struct serial_struct;
struct device;
/*
- * This structure describes all the operations that can be
- * done on the physical hardware.
+ * This structure describes all the operations that can be done on the
+ * physical hardware. See Documentation/serial/driver for details.
*/
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
@@ -65,7 +65,7 @@ struct uart_ops {
/*
* Return a string describing the type of the port
*/
- const char *(*type)(struct uart_port *);
+ const char *(*type)(struct uart_port *);
/*
* Release IO and memory resources used by the port.
@@ -83,7 +83,7 @@ struct uart_ops {
int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct uart_port *);
- void (*poll_put_char)(struct uart_port *, unsigned char);
+ void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};
--
1.7.10.4
^ permalink raw reply related
* [PATCH 1/3] tty: Fix comments that reference BKL, eventd, old paths
From: Kevin Cernekee @ 2012-12-27 4:43 UTC (permalink / raw)
To: alan, gregkh, jslaby; +Cc: linux-serial
Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
---
drivers/tty/serial/serial_core.c | 18 +++++++++---------
drivers/tty/tty_io.c | 2 +-
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 2c7230a..83d127f 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1308,9 +1308,10 @@ static void uart_set_termios(struct tty_struct *tty,
}
/*
- * In 2.4.5, calls to this will be serialized via the BKL in
- * linux/drivers/char/tty_io.c:tty_release()
- * linux/drivers/char/tty_io.c:do_tty_handup()
+ * Calls to uart_close() are serialised via the tty_lock in
+ * drivers/tty/tty_io.c:tty_release()
+ * drivers/tty/tty_io.c:do_tty_hangup()
+ * This runs from a workqueue and can sleep for a _short_ time only.
*/
static void uart_close(struct tty_struct *tty, struct file *filp)
{
@@ -1437,10 +1438,9 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
}
/*
- * This is called with the BKL held in
- * linux/drivers/char/tty_io.c:do_tty_hangup()
- * We're called from the eventd thread, so we can sleep for
- * a _short_ time only.
+ * Calls to uart_hangup() are serialised by the tty_lock in
+ * drivers/tty/tty_io.c:do_tty_hangup()
+ * This runs from a workqueue and can sleep for a _short_ time only.
*/
static void uart_hangup(struct tty_struct *tty)
{
@@ -1521,8 +1521,8 @@ static void uart_dtr_rts(struct tty_port *port, int onoff)
}
/*
- * calls to uart_open are serialised by the BKL in
- * fs/char_dev.c:chrdev_open()
+ * Calls to uart_open are serialised by the tty_lock in
+ * drivers/tty/tty_io.c:tty_open()
* Note that if this fails, then uart_close() _will_ be called.
*
* In time, we want to scrap the "opening nonpresent ports"
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index da9fde8..54a254a 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -536,7 +536,7 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
* __tty_hangup - actual handler for hangup events
* @work: tty device
*
- * This can be called by the "eventd" kernel thread. That is process
+ * This can be called by a "kworker" kernel thread. That is process
* synchronous but doesn't hold any locks, so we need to make sure we
* have the appropriate locks for what we're doing.
*
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH] seria: sirf: only use lookup table to set baudrate when ioclk=150MHz
From: Russell King - ARM Linux @ 2012-12-26 15:04 UTC (permalink / raw)
To: Barry Song
Cc: alan, workgroup.linux, linux-arm-kernel, linux-serial, Barry Song
In-Reply-To: <1356431162-8372-1-git-send-email-Barry.Song@csr.com>
On Tue, Dec 25, 2012 at 06:26:02PM +0800, Barry Song wrote:
> @@ -375,7 +375,12 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
> int threshold_div;
> int temp;
>
> - ioclk_rate = 150000000;
> + struct clk *clk = clk_get_sys("io", NULL);
> + BUG_ON(IS_ERR(clk));
No. Really, no. Stop using BUG_ON() as some kind of crappy assert().
BUG_ON() takes the entire kernel out when it fails. There's absolutely
no need for this what so ever - especially here.
Get the clock at probe or port initialization time. Save that pointer.
Only give it up when the port is torn down. And treat it as any other
clock - prepare and enable it, and disable and unprepare it when you're
done with it.
And there's no need to use this clk_get_sys() crap in drivers. Add the
necessary clkdev entries or deal with it in DT. Absolutely do not use
clk_get_sys() in drivers; it's there for *PLATFORM* code to use when
there's no other possibility for them and NOT drivers.
^ permalink raw reply
* [PATCH] drivers/tty/serial: avoid double free after call ioc4_serial_remove_one
From: Chen Gang @ 2012-12-26 10:06 UTC (permalink / raw)
To: alan; +Cc: linux-serial
before goto out5, soft, control, serial are all assigned to idd
after finish call ioc4_serial_remove_one, all resources are released
we need return instead of go on, or double free
Signed-off-by: Chen Gang <gang.chen@asianux.com>
---
drivers/tty/serial/ioc4_serial.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c
index 3e7da10..c4e30b8 100644
--- a/drivers/tty/serial/ioc4_serial.c
+++ b/drivers/tty/serial/ioc4_serial.c
@@ -2883,6 +2883,7 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd)
/* error exits that give back resources */
out5:
ioc4_serial_remove_one(idd);
+ return ret;
out4:
kfree(soft);
out3:
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH] seria: sirf: only use lookup table to set baudrate when ioclk=150MHz
From: Sergei Shtylyov @ 2012-12-25 18:53 UTC (permalink / raw)
To: Barry Song
Cc: Barry Song, linux-serial, linux-arm-kernel, workgroup.linux, alan
In-Reply-To: <1356431162-8372-1-git-send-email-Barry.Song@csr.com>
Hello.
On 12/25/2012 01:26 PM, Barry Song wrote:
> From: Barry Song <Baohua.Song@csr.com>
> The fast lookup table to set baudrate is only right when ioclk
> is 150MHz. for most platforms, ioclk is 150MHz, but some boards
> might set ioclk to other frequency.
> so re-calc the clk_div_reg when ioclk is not 150MHz.
> Signed-off-by: Barry Song <Baohua.Song@csr.com>
> ---
> drivers/tty/serial/sirfsoc_uart.c | 18 +++++++++++++-----
> 1 files changed, 13 insertions(+), 5 deletions(-)
> diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
> index 142217c..fe28b1f 100644
> --- a/drivers/tty/serial/sirfsoc_uart.c
> +++ b/drivers/tty/serial/sirfsoc_uart.c
> @@ -375,7 +375,12 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
> int threshold_div;
> int temp;
>
> - ioclk_rate = 150000000;
> + struct clk *clk = clk_get_sys("io", NULL);
Please keep the coding style and insert emoty line here; you can also remove
preceding empty line (not to break the declaration block).
> + BUG_ON(IS_ERR(clk));
> +
> + ioclk_rate = clk_get_rate(clk);
> + clk_put(clk);
> +
> switch (termios->c_cflag & CSIZE) {
> default:
> case CS8:
WBR, Sergei
^ permalink raw reply
* [PATCH] seria: sirf: only use lookup table to set baudrate when ioclk=150MHz
From: Barry Song @ 2012-12-25 10:26 UTC (permalink / raw)
To: alan; +Cc: linux-serial, linux-arm-kernel, workgroup.linux, Barry Song
From: Barry Song <Baohua.Song@csr.com>
The fast lookup table to set baudrate is only right when ioclk
is 150MHz. for most platforms, ioclk is 150MHz, but some boards
might set ioclk to other frequency.
so re-calc the clk_div_reg when ioclk is not 150MHz.
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
drivers/tty/serial/sirfsoc_uart.c | 18 +++++++++++++-----
1 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index 142217c..fe28b1f 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -375,7 +375,12 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
int threshold_div;
int temp;
- ioclk_rate = 150000000;
+ struct clk *clk = clk_get_sys("io", NULL);
+ BUG_ON(IS_ERR(clk));
+
+ ioclk_rate = clk_get_rate(clk);
+ clk_put(clk);
+
switch (termios->c_cflag & CSIZE) {
default:
case CS8:
@@ -431,10 +436,13 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
sirfsoc_uart_disable_ms(port);
}
- /* common rate: fast calculation */
- for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++)
- if (baud_rate == baudrate_to_regv[ic].baud_rate)
- clk_div_reg = baudrate_to_regv[ic].reg_val;
+ if (ioclk_rate == 150000000) {
+ /* common rate: fast calculation */
+ for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++)
+ if (baud_rate == baudrate_to_regv[ic].baud_rate)
+ clk_div_reg = baudrate_to_regv[ic].reg_val;
+ }
+
setted_baud = baud_rate;
/* arbitary rate setting */
if (unlikely(clk_div_reg == 0))
--
1.7.5.4
Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom
More information can be found at www.csr.com. Follow CSR on Twitter at http://twitter.com/CSR_PLC and read our blog at www.csr.com/blog
^ permalink raw reply related
* [PATCH] serial: sirf: add support for new SiRFmarco SMP SoC
From: Barry Song @ 2012-12-25 9:32 UTC (permalink / raw)
To: alan; +Cc: linux-serial, linux-arm-kernel, workgroup.linux, Barry Song
From: Barry Song <Baohua.Song@csr.com>
CSR SiRFmarco's UART IP is same with SiRFprimaII except that
it has two more uart ports.
this patch makes the old driver support new SiRFmarco as well:
1. add .compatible = "sirf,marco-uart" to OF match table
2. add two ports in the port table
3. take spin_lock in isr to avoid the conflict of threads opening
uart on CPU1 and isr running on CPU0.
For 3, we did see some problems on SiRFmarco as SiRFmarco is a
SMP SoC but the old SiRFprimaII is UP.
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
drivers/tty/serial/sirfsoc_uart.c | 19 +++++++++++++++++++
drivers/tty/serial/sirfsoc_uart.h | 2 +-
2 files changed, 20 insertions(+), 1 deletions(-)
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index 5da5cb9..142217c 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -75,6 +75,20 @@ static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = {
.line = 2,
},
},
+ [3] = {
+ .port = {
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 3,
+ },
+ },
+ [4] = {
+ .port = {
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 4,
+ },
+ },
};
static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port)
@@ -245,6 +259,7 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id)
struct uart_port *port = &sirfport->port;
struct uart_state *state = port->state;
struct circ_buf *xmit = &port->state->xmit;
+ spin_lock(&port->lock);
intr_status = rd_regl(port, SIRFUART_INT_STATUS);
wr_regl(port, SIRFUART_INT_STATUS, intr_status);
intr_status &= rd_regl(port, SIRFUART_INT_EN);
@@ -254,6 +269,7 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id)
goto recv_char;
uart_insert_char(port, intr_status,
SIRFUART_RX_OFLOW, 0, TTY_BREAK);
+ spin_unlock(&port->lock);
return IRQ_HANDLED;
}
if (intr_status & SIRFUART_RX_OFLOW)
@@ -286,6 +302,7 @@ recv_char:
sirfsoc_uart_pio_rx_chars(port, SIRFSOC_UART_IO_RX_MAX_CNT);
if (intr_status & SIRFUART_TX_INT_EN) {
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ spin_unlock(&port->lock);
return IRQ_HANDLED;
} else {
sirfsoc_uart_pio_tx_chars(sirfport,
@@ -296,6 +313,7 @@ recv_char:
sirfsoc_uart_stop_tx(port);
}
}
+ spin_unlock(&port->lock);
return IRQ_HANDLED;
}
@@ -729,6 +747,7 @@ static int sirfsoc_uart_resume(struct platform_device *pdev)
static struct of_device_id sirfsoc_uart_ids[] = {
{ .compatible = "sirf,prima2-uart", },
+ { .compatible = "sirf,marco-uart", },
{}
};
MODULE_DEVICE_TABLE(of, sirfsoc_serial_of_match);
diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h
index 6e207fd..6431640 100644
--- a/drivers/tty/serial/sirfsoc_uart.h
+++ b/drivers/tty/serial/sirfsoc_uart.h
@@ -139,7 +139,7 @@
#define SIRFSOC_UART_MINOR 0
#define SIRFUART_PORT_NAME "sirfsoc-uart"
#define SIRFUART_MAP_SIZE 0x200
-#define SIRFSOC_UART_NR 3
+#define SIRFSOC_UART_NR 5
#define SIRFSOC_PORT_TYPE 0xa5
/* Baud Rate Calculation */
--
1.7.5.4
Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom
More information can be found at www.csr.com. Follow CSR on Twitter at http://twitter.com/CSR_PLC and read our blog at www.csr.com/blog
^ permalink raw reply related
* Re: [PATCH] tty: Fix unreasonable write toward closed pty.
From: Alan Cox @ 2012-12-21 21:55 UTC (permalink / raw)
To: Ilya Zykov
Cc: Greg Kroah-Hartman, Jiri Slaby, Peter Hurley, Sasha Levin,
linux-serial, linux-kernel
In-Reply-To: <50D20E87.9060403@ilyx.ru>
On Wed, 19 Dec 2012 22:59:19 +0400
Ilya Zykov <ilya@ilyx.ru> wrote:
> We should not write toward the closed pty.
> Now it happens, if one side close last file descriptor,
> and other side in this moment write to it.
> It also prevents scheduling unnecessary work.
>
> Signed-off-by: Ilya Zykov <ilya@ilyx.ru>
> ---
> drivers/tty/pty.c | 2 ++
> 1 files changed, 2 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
> index a82b399..1ce1362 100644
> --- a/drivers/tty/pty.c
> +++ b/drivers/tty/pty.c
> @@ -116,6 +116,8 @@ static int pty_space(struct tty_struct *to)
>
> static int pty_write(struct tty_struct *tty, const unsigned char
> *buf, int c) {
> + if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
> + return -EIO;
> struct tty_struct *to = tty->link;
This
a) doesn't do anything in many cases because there is no lock to make
the test_bit meaningful
b) produces an obvious compiler warning
^ permalink raw reply
* Re: [PATCH v2 00/11] tty: Fix buffer work access-after-free
From: Peter Hurley @ 2012-12-20 19:03 UTC (permalink / raw)
To: Sasha Levin
Cc: Alan Cox, Jiri Slaby, linux-serial, Greg Kroah-Hartman,
Ilya Zykov, linux-kernel
In-Reply-To: <CA+1xoqdtkeuN+nAO5L56r77vrNs40mxwGkkgaCt3aA9=5G_e7g@mail.gmail.com>
On Wed, 2012-12-19 at 15:38 -0500, Sasha Levin wrote:
> On Tue, Dec 18, 2012 at 11:48 AM, Peter Hurley <peter@hurleysoftware.com> wrote:
> > On Tue, 2012-12-18 at 10:44 -0500, Sasha Levin wrote:
> >> I'm still seeing that warning with the new patch series:
> >>
> >> [ 549.561769] ------------[ cut here ]------------
> >> [ 549.598755] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room+0xff/0x130()
> >> [ 549.604058] scheduling buffer work for halted ldisc
> >> [ 549.607741] Pid: 9417, comm: trinity-child28 Tainted: G D W
> >> 3.7.0-next-20121217-sasha-00023-g8689ef9 #219
> >> [ 549.652580] Call Trace:
> >> [ 549.662754] [<ffffffff81c432cf>] ? n_tty_set_room+0xff/0x130
> >> [ 549.665458] [<ffffffff8110cae7>] warn_slowpath_common+0x87/0xb0
> >> [ 549.668257] [<ffffffff8110cb71>] warn_slowpath_fmt+0x41/0x50
> >> [ 549.671007] [<ffffffff81c432cf>] n_tty_set_room+0xff/0x130
> >> [ 549.673268] [<ffffffff81c44597>] reset_buffer_flags+0x137/0x150
> >> [ 549.675607] [<ffffffff81c45b71>] n_tty_open+0x131/0x1c0
> >
> > This is a false-positive warning that means I need to refine the warning
> > condition to not include this code path.
> >
> > Thanks again.
>
> I'm really having a hard time doing any fuzzing after applying this
> patch. I'm not sure it's related directly, but
> the ldisc hangup lockup happens quite quickly and every time, so I
> can't really get any good fuzzing done.
I think you mean this ldisc livelock, right?
[ 243.840596] INFO: task init:1 blocked for more than 120 seconds.
[ 243.844695] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 243.850777] init D 0000000000000000 3192 1 0 0x00000002
[ 243.852430] ffff8800bf909c28 0000000000000002 ffff8800bf909bc8 ffffffff8118018e
[ 243.853722] ffff8800bf909fd8 ffff8800bf909fd8 ffff8800bf909fd8 ffff8800bf909fd8
[ 243.854901] ffff8800bf970000 ffff8800bf900000 0000000000000007 ffff880063b1e8d0
[ 243.856198] Call Trace:
[ 243.856602] [<ffffffff8118018e>] ? put_lock_stats.isra.16+0xe/0x40
[ 243.857580] [<ffffffff83ce7ab5>] schedule+0x55/0x60
[ 243.858413] [<ffffffff83ce5afa>] schedule_timeout+0x3a/0x370
[ 243.859400] [<ffffffff81183cd8>] ? trace_hardirqs_on_caller+0x118/0x140
[ 243.860569] [<ffffffff81183d0d>] ? trace_hardirqs_on+0xd/0x10
[ 243.861554] [<ffffffff83ce91bc>] ? _raw_spin_unlock_irqrestore+0x7c/0xa0
[ 243.863046] [<ffffffff8113c7a2>] ? prepare_to_wait+0x72/0x80
[ 243.864064] [<ffffffff81c463c5>] tty_ldisc_wait_idle.isra.6+0x75/0xb0
[ 243.865209] [<ffffffff8113c960>] ? abort_exclusive_wait+0xb0/0xb0
[ 243.866272] [<ffffffff81c465e2>] tty_ldisc_hangup_halt+0xd2/0x140
[ 243.867337] [<ffffffff81c46d75>] tty_ldisc_hangup+0xc5/0x1e0
[ 243.868326] [<ffffffff81c3e537>] __tty_hangup+0x137/0x440
[ 243.869293] [<ffffffff83ce91bc>] ? _raw_spin_unlock_irqrestore+0x7c/0xa0
[ 243.870737] [<ffffffff81c4062c>] disassociate_ctty+0x6c/0x230
[ 243.871986] [<ffffffff81113a5c>] do_exit+0x41c/0x580
[ 243.873208] [<ffffffff8107d964>] ? syscall_trace_enter+0x24/0x2e0
[ 243.874720] [<ffffffff81113c8a>] do_group_exit+0x8a/0xc0
[ 243.876075] [<ffffffff81113cd2>] sys_exit_group+0x12/0x20
[ 243.877477] [<ffffffff83ceac98>] tracesys+0xe1/0xe6
[ 243.879065] 1 lock held by init/1:
[ 243.880054] #0: (&tty->ldisc_mutex){+.+...}, at: [<ffffffff81c46d6d>] tty_ldisc_hangup+0xbd/0x1e0
> I'm not saying that this patch series is causing it, just saying that
> I can't really test it at this point due to
> that other lockup.
Well, I know why this is happening but I haven't quite figured out how
to fix it yet.
void tty_ldisc_hangup(struct tty_struct *tty)
{
...
wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
...
mutex_lock(&tty->ldisc_mutex);
...
if (tty_ldisc_hangup_halt(tty)) {
...
The 2 wakeups are meant to kick out waiters from the read and write
queues that already have ldisc references before waiting in
tty_ldisc_hangup_halt() for all the ldisc references to be released.
Although the ldisc hasn't yet been halted when the wakeups are 'sent',
that's not actually a problem because before a reader or writer will
sleep on its wait queue, it checks if the file pointer has been 'hung
up'.
static ssize_t n_tty_read(........)
{
....
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
....
set_current_state(TASK_INTERRUPTIBLE);
....
if (!input_available_p(tty, 0)) {
....
hung up? -----> if (tty_hung_up_p(file))
break;
....
no, sleep ----> timeout = schedule_timeout(timeout);
continue;
}
But, console & vt file pointers are not 'hung up'.
static void __tty_hangup(.....)
{
....
list_for_each_entry(priv, &tty->tty_files, list) {
....
if (filp->f_op->write == redirected_tty_write)
cons_filp = filp;
if (filp->f_op->write != tty_write)
continue;
.....
filp->f_op = &hung_up_tty_fops;
}
So waking up a reader sleeping on a console file pointer does nothing.
It just goes back to sleep. Which means it doesn't release its ldisc
reference, which means everything waits for something that will never
happen.
I'm researching how best to fix this, but right now, I'm unsure what the
correct solution is, especially since this probably never worked.
In the meantime, you can use the patch below on top of my other patches
to reduce the frequency of this happening.
--- >% ---
Subject: [PATCH] tty: Kick waiters _after_ the ldisc is locked
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
---
drivers/tty/tty_ldisc.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index a6d3078..7027bd3 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -603,6 +603,12 @@ static bool tty_ldisc_hangup_halt(struct tty_struct *tty)
clear_bit(TTY_LDISC, &tty->flags);
+ /* Wakeup waiters so they can discover the tty is hupping, abort
+ * their i/o loops, and release their ldisc references
+ */
+ wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
+ wake_up_interruptible_poll(&tty->read_wait, POLLIN);
+
if (tty->ldisc) { /* Not yet closed */
tty_unlock(tty);
@@ -874,12 +880,6 @@ void tty_ldisc_hangup(struct tty_struct *tty)
tty_ldisc_deref(ld);
}
/*
- * FIXME: Once we trust the LDISC code better we can wait here for
- * ldisc completion and fix the driver call race
- */
- wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
- wake_up_interruptible_poll(&tty->read_wait, POLLIN);
- /*
* Shutdown the current line discipline, and reset it to
* N_TTY if need be.
*
--
1.8.0.2
^ permalink raw reply related
* Re: [PATCH 1/2] tty: remove redundant initialization in initialize_tty_struct()
From: Alan Cox @ 2012-12-20 15:55 UTC (permalink / raw)
To: Yan Hong; +Cc: gregkh, linux-serial
In-Reply-To: <CAH+=+MEpjwXUnD0Hey18fRbvY56wDHVmD5L1PsHwK_QmHCg4TA@mail.gmail.com>
On Fri, 23 Nov 2012 23:04:52 +0800
Yan Hong <clouds.yan@gmail.com> wrote:
> 2012/11/23 Alan Cox <alan@lxorguk.ukuu.org.uk>:
> > On Fri, 23 Nov 2012 22:41:47 +0800
> > Yan Hong <clouds.yan@gmail.com> wrote:
> >
> >> tty_struct is already zeroed, no need to zero its field again.
> >
> > True but its asking for impossible to find bugs later if this changes
>
> If it's true, we should have concern for all memset() usage. Is this
> case special?
Slowly catchng up on stuff. The problem in this case is that the
allocator and init methods are in different function. As you rightly
observe they don't need to be. Perhaps a better patch would be told fold
them together and remove the memset ?
Alan
^ permalink raw reply
* [PATCH] drivers/tty/synclink: let receive buffer size match max frame size
From: Chen Gang @ 2012-12-20 4:16 UTC (permalink / raw)
To: Greg KH; +Cc: Paul Fulghum, Alan Cox, Linux Kernel Mailing List, linux-serial
In-Reply-To: <20121219040907.GA22719@kroah.com>
By Paul Fulghum:
Fix call to line discipline receive_buf by synclink drivers.
Dummy flag buffer argument is ignored by N_HDLC line discipline but might
be of insufficient size if accessed by a different line discipline
selected by mistake. flag buffer allocation now matches max size of data
buffer. Unused char_buf buffers are removed.
By Chen Gang:
Give a comment for rx_get_buf
the receive buffer size is DMABUFSIZE limited, which alloc in alloc_bufs
it is always less than max_frame_size
so do not need check the length whether larger than max_frame_size.
Extern the limitation.
the maxframe parameter (which input from outside) has value limitition
so define macro in include/linux/synclink.h to extern the limitation
and use macro instead of hard code numbers in all relative c source files
Beautify source code:
for function mgslpc_probe in drivers/char/pcmcia/synclink_cs.c
use tab instead of 4 spaces for each line header.
Unit test:
for "Give a comment for rx_get_buf":
only a comment, not need test.
also can reference free_buf, so can confirm our conclusion.
for "Extern the limitation":
modify source code, call the relative function when insmod module.
pass maxframe=6000, for synclink_gt, synclink_cs, synclink, synclinkmp.
pass maxframe=3000, for synclink_gt, synclink_cs, synclink, synclinkmp.
pass maxframe=70000, for synclink_gt, synclink_cs, synclink, synclinkmp.
for "Beautify source code":
vimdiff the new file and the original file.
restore the new file to the original file, word by word.
make sure that we only use tab instead of 4 spaces for each line header.
Signed-off-by: Chen Gang <gang.chen@asianux.com>
Signed-off-by: Paul Fulghum <paulkf@microgate.com>
---
drivers/char/pcmcia/synclink_cs.c | 102 ++++++++++++++++++++-----------------
drivers/tty/synclink.c | 23 ++++++---
drivers/tty/synclink_gt.c | 29 ++++++++---
drivers/tty/synclinkmp.c | 22 +++++---
include/linux/synclink.h | 3 ++
5 files changed, 111 insertions(+), 68 deletions(-)
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index b66eaa0..4f78e30 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -210,7 +210,7 @@ typedef struct _mgslpc_info {
char testing_irq;
unsigned int init_error; /* startup error (DIAGS) */
- char flag_buf[MAX_ASYNC_BUFFER_SIZE];
+ char *flag_buf;
bool drop_rts_on_tx_done;
struct _input_signal_events input_signal_events;
@@ -514,49 +514,49 @@ static const struct tty_port_operations mgslpc_port_ops = {
static int mgslpc_probe(struct pcmcia_device *link)
{
- MGSLPC_INFO *info;
- int ret;
+ MGSLPC_INFO *info;
+ int ret;
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("mgslpc_attach\n");
-
- info = kzalloc(sizeof(MGSLPC_INFO), GFP_KERNEL);
- if (!info) {
- printk("Error can't allocate device instance data\n");
- return -ENOMEM;
- }
-
- info->magic = MGSLPC_MAGIC;
- tty_port_init(&info->port);
- info->port.ops = &mgslpc_port_ops;
- INIT_WORK(&info->task, bh_handler);
- info->max_frame_size = 4096;
- info->port.close_delay = 5*HZ/10;
- info->port.closing_wait = 30*HZ;
- init_waitqueue_head(&info->status_event_wait_q);
- init_waitqueue_head(&info->event_wait_q);
- spin_lock_init(&info->lock);
- spin_lock_init(&info->netlock);
- memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
- info->idle_mode = HDLC_TXIDLE_FLAGS;
- info->imra_value = 0xffff;
- info->imrb_value = 0xffff;
- info->pim_value = 0xff;
-
- info->p_dev = link;
- link->priv = info;
-
- /* Initialize the struct pcmcia_device structure */
-
- ret = mgslpc_config(link);
- if (ret) {
- tty_port_destroy(&info->port);
- return ret;
- }
-
- mgslpc_add_device(info);
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgslpc_attach\n");
- return 0;
+ info = kzalloc(sizeof(MGSLPC_INFO), GFP_KERNEL);
+ if (!info) {
+ printk("Error can't allocate device instance data\n");
+ return -ENOMEM;
+ }
+
+ info->magic = MGSLPC_MAGIC;
+ tty_port_init(&info->port);
+ info->port.ops = &mgslpc_port_ops;
+ INIT_WORK(&info->task, bh_handler);
+ info->max_frame_size = SYNCLINK_MIN_FRAME_SIZE;
+ info->port.close_delay = 5*HZ/10;
+ info->port.closing_wait = 30*HZ;
+ init_waitqueue_head(&info->status_event_wait_q);
+ init_waitqueue_head(&info->event_wait_q);
+ spin_lock_init(&info->lock);
+ spin_lock_init(&info->netlock);
+ memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
+ info->idle_mode = HDLC_TXIDLE_FLAGS;
+ info->imra_value = 0xffff;
+ info->imrb_value = 0xffff;
+ info->pim_value = 0xff;
+
+ info->p_dev = link;
+ link->priv = info;
+
+ /* Initialize the struct pcmcia_device structure */
+
+ ret = mgslpc_config(link);
+ if (ret) {
+ tty_port_destroy(&info->port);
+ return ret;
+ }
+
+ mgslpc_add_device(info);
+
+ return 0;
}
/* Card has been inserted.
@@ -2674,6 +2674,14 @@ static int rx_alloc_buffers(MGSLPC_INFO *info)
if (info->rx_buf == NULL)
return -ENOMEM;
+ /* unused flag buffer to satisfy receive_buf calling interface */
+ info->flag_buf = kzalloc(info->max_frame_size, GFP_KERNEL);
+ if (!info->flag_buf) {
+ kfree(info->rx_buf);
+ info->rx_buf = NULL;
+ return -ENOMEM;
+ }
+
rx_reset_buffers(info);
return 0;
}
@@ -2682,6 +2690,8 @@ static void rx_free_buffers(MGSLPC_INFO *info)
{
kfree(info->rx_buf);
info->rx_buf = NULL;
+ kfree(info->flag_buf);
+ info->flag_buf = NULL;
}
static int claim_resources(MGSLPC_INFO *info)
@@ -2728,10 +2738,10 @@ static void mgslpc_add_device(MGSLPC_INFO *info)
current_dev->next_device = info;
}
- if (info->max_frame_size < 4096)
- info->max_frame_size = 4096;
- else if (info->max_frame_size > 65535)
- info->max_frame_size = 65535;
+ if (info->max_frame_size < SYNCLINK_MIN_FRAME_SIZE)
+ info->max_frame_size = SYNCLINK_MIN_FRAME_SIZE;
+ else if (info->max_frame_size > SYNCLINK_MAX_FRAME_SIZE)
+ info->max_frame_size = SYNCLINK_MAX_FRAME_SIZE;
printk( "SyncLink PC Card %s:IO=%04X IRQ=%d\n",
info->device_name, info->io_base, info->irq_level);
diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c
index 9e071f6..94ce422 100644
--- a/drivers/tty/synclink.c
+++ b/drivers/tty/synclink.c
@@ -291,8 +291,7 @@ struct mgsl_struct {
bool lcr_mem_requested;
u32 misc_ctrl_value;
- char flag_buf[MAX_ASYNC_BUFFER_SIZE];
- char char_buf[MAX_ASYNC_BUFFER_SIZE];
+ char *flag_buf;
bool drop_rts_on_tx_done;
bool loopmode_insert_requested;
@@ -3898,7 +3897,13 @@ static int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info)
info->intermediate_rxbuffer = kmalloc(info->max_frame_size, GFP_KERNEL | GFP_DMA);
if ( info->intermediate_rxbuffer == NULL )
return -ENOMEM;
-
+ /* unused flag buffer to satisfy receive_buf calling interface */
+ info->flag_buf = kzalloc(info->max_frame_size, GFP_KERNEL);
+ if (!info->flag_buf) {
+ kfree(info->intermediate_rxbuffer);
+ info->intermediate_rxbuffer = NULL;
+ return -ENOMEM;
+ }
return 0;
} /* end of mgsl_alloc_intermediate_rxbuffer_memory() */
@@ -3917,6 +3922,8 @@ static void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info)
{
kfree(info->intermediate_rxbuffer);
info->intermediate_rxbuffer = NULL;
+ kfree(info->flag_buf);
+ info->flag_buf = NULL;
} /* end of mgsl_free_intermediate_rxbuffer_memory() */
@@ -4237,10 +4244,10 @@ static void mgsl_add_device( struct mgsl_struct *info )
current_dev->next_device = info;
}
- if ( info->max_frame_size < 4096 )
- info->max_frame_size = 4096;
- else if ( info->max_frame_size > 65535 )
- info->max_frame_size = 65535;
+ if (info->max_frame_size < SYNCLINK_MIN_FRAME_SIZE)
+ info->max_frame_size = SYNCLINK_MIN_FRAME_SIZE;
+ else if (info->max_frame_size > SYNCLINK_MAX_FRAME_SIZE)
+ info->max_frame_size = SYNCLINK_MAX_FRAME_SIZE;
if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
printk( "SyncLink PCI v%d %s: IO=%04X IRQ=%d Mem=%08X,%08X MaxFrameSize=%u\n",
@@ -4286,7 +4293,7 @@ static struct mgsl_struct* mgsl_allocate_device(void)
info->port.ops = &mgsl_port_ops;
info->magic = MGSL_MAGIC;
INIT_WORK(&info->task, mgsl_bh_handler);
- info->max_frame_size = 4096;
+ info->max_frame_size = SYNCLINK_MIN_FRAME_SIZE;
info->port.close_delay = 5*HZ/10;
info->port.closing_wait = 30*HZ;
init_waitqueue_head(&info->status_event_wait_q);
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
index aba1e59..4dda746 100644
--- a/drivers/tty/synclink_gt.c
+++ b/drivers/tty/synclink_gt.c
@@ -317,8 +317,7 @@ struct slgt_info {
unsigned char *tx_buf;
int tx_count;
- char flag_buf[MAX_ASYNC_BUFFER_SIZE];
- char char_buf[MAX_ASYNC_BUFFER_SIZE];
+ char *flag_buf;
bool drop_rts_on_tx_done;
struct _input_signal_events input_signal_events;
@@ -3355,11 +3354,24 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
return retval;
}
+/*
+ * allocate buffers used for calling line discipline receive_buf
+ * directly in synchronous mode
+ * note: add 5 bytes to max frame size to allow appending
+ * 32-bit CRC and status byte when configured to do so
+ */
static int alloc_tmp_rbuf(struct slgt_info *info)
{
info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL);
if (info->tmp_rbuf == NULL)
return -ENOMEM;
+ /* unused flag buffer to satisfy receive_buf calling interface */
+ info->flag_buf = kzalloc(info->max_frame_size + 5, GFP_KERNEL);
+ if (!info->flag_buf) {
+ kfree(info->tmp_rbuf);
+ info->tmp_rbuf = NULL;
+ return -ENOMEM;
+ }
return 0;
}
@@ -3367,6 +3379,8 @@ static void free_tmp_rbuf(struct slgt_info *info)
{
kfree(info->tmp_rbuf);
info->tmp_rbuf = NULL;
+ kfree(info->flag_buf);
+ info->flag_buf = NULL;
}
/*
@@ -3547,10 +3561,10 @@ static void add_device(struct slgt_info *info)
current_dev->next_device = info;
}
- if (info->max_frame_size < 4096)
- info->max_frame_size = 4096;
- else if (info->max_frame_size > 65535)
- info->max_frame_size = 65535;
+ if (info->max_frame_size < SYNCLINK_MIN_FRAME_SIZE)
+ info->max_frame_size = SYNCLINK_MIN_FRAME_SIZE;
+ else if (info->max_frame_size > SYNCLINK_MAX_FRAME_SIZE)
+ info->max_frame_size = SYNCLINK_MAX_FRAME_SIZE;
switch(info->pdev->device) {
case SYNCLINK_GT_DEVICE_ID:
@@ -3600,7 +3614,7 @@ static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev
info->port.ops = &slgt_port_ops;
info->magic = MGSL_MAGIC;
INIT_WORK(&info->task, bh_handler);
- info->max_frame_size = 4096;
+ info->max_frame_size = SYNCLINK_MIN_FRAME_SIZE;
info->base_clock = 14745600;
info->rbuf_fill_level = DMABUFSIZE;
info->port.close_delay = 5*HZ/10;
@@ -4778,6 +4792,7 @@ cleanup:
/*
* pass receive buffer (RAW synchronous mode) to tty layer
+ * the receive buffer size is DMABUFSIZE limited
* return true if buffer available, otherwise false
*/
static bool rx_get_buf(struct slgt_info *info)
diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c
index fd43fb6..621c562 100644
--- a/drivers/tty/synclinkmp.c
+++ b/drivers/tty/synclinkmp.c
@@ -262,8 +262,7 @@ typedef struct _synclinkmp_info {
bool sca_statctrl_requested;
u32 misc_ctrl_value;
- char flag_buf[MAX_ASYNC_BUFFER_SIZE];
- char char_buf[MAX_ASYNC_BUFFER_SIZE];
+ char *flag_buf;
bool drop_rts_on_tx_done;
struct _input_signal_events input_signal_events;
@@ -3553,6 +3552,13 @@ static int alloc_tmp_rx_buf(SLMP_INFO *info)
info->tmp_rx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
if (info->tmp_rx_buf == NULL)
return -ENOMEM;
+ /* unused flag buffer to satisfy receive_buf calling interface */
+ info->flag_buf = kzalloc(info->max_frame_size, GFP_KERNEL);
+ if (!info->flag_buf) {
+ kfree(info->tmp_rx_buf);
+ info->tmp_rx_buf = NULL;
+ return -ENOMEM;
+ }
return 0;
}
@@ -3560,6 +3566,8 @@ static void free_tmp_rx_buf(SLMP_INFO *info)
{
kfree(info->tmp_rx_buf);
info->tmp_rx_buf = NULL;
+ kfree(info->flag_buf);
+ info->flag_buf = NULL;
}
static int claim_resources(SLMP_INFO *info)
@@ -3729,10 +3737,10 @@ static void add_device(SLMP_INFO *info)
current_dev->next_device = info;
}
- if ( info->max_frame_size < 4096 )
- info->max_frame_size = 4096;
- else if ( info->max_frame_size > 65535 )
- info->max_frame_size = 65535;
+ if (info->max_frame_size < SYNCLINK_MIN_FRAME_SIZE)
+ info->max_frame_size = SYNCLINK_MIN_FRAME_SIZE;
+ else if (info->max_frame_size > SYNCLINK_MAX_FRAME_SIZE)
+ info->max_frame_size = SYNCLINK_MAX_FRAME_SIZE;
printk( "SyncLink MultiPort %s: "
"Mem=(%08x %08X %08x %08X) IRQ=%d MaxFrameSize=%u\n",
@@ -3773,7 +3781,7 @@ static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
info->port.ops = &port_ops;
info->magic = MGSL_MAGIC;
INIT_WORK(&info->task, bh_handler);
- info->max_frame_size = 4096;
+ info->max_frame_size = SYNCLINK_MIN_FRAME_SIZE;
info->port.close_delay = 5*HZ/10;
info->port.closing_wait = 30*HZ;
init_waitqueue_head(&info->status_event_wait_q);
diff --git a/include/linux/synclink.h b/include/linux/synclink.h
index f1405b1..61ac4b2 100644
--- a/include/linux/synclink.h
+++ b/include/linux/synclink.h
@@ -13,6 +13,9 @@
#include <uapi/linux/synclink.h>
+#define SYNCLINK_MIN_FRAME_SIZE 4096
+#define SYNCLINK_MAX_FRAME_SIZE 65535
+
/* provide 32 bit ioctl compatibility on 64 bit systems */
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH] serial: tegra: add serial driver
From: Grant Likely @ 2012-12-20 1:09 UTC (permalink / raw)
To: Stephen Warren, Laxman Dewangan
Cc: alan@linux.intel.com, gregkh@linuxfoundation.org, jslaby@suse.cz,
rob.herring@calxeda.com, devicetree-discuss@lists.ozlabs.org,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-serial@vger.kernel.org, linux-tegra@vger.kernel.org
In-Reply-To: <50D1F22A.6090801@wwwdotorg.org>
On Wed, 19 Dec 2012 09:58:18 -0700, Stephen Warren <swarren@wwwdotorg.org> wrote:
> On 12/19/2012 07:15 AM, Laxman Dewangan wrote:
> > On Wednesday 19 December 2012 06:31 PM, Grant Likely wrote:
> >> On Mon, 17 Dec 2012 14:31:34 -0700, Stephen
> >> Warren<swarren@wwwdotorg.org> wrote:
> >>> On 12/17/2012 10:10 AM, Grant Likely wrote:
> >>>> On Mon, 17 Dec 2012 17:40:49 +0530, Laxman
> >>>> Dewangan<ldewangan@nvidia.com> wrote:
> >>>
> >>> Aren't we still supposed to support platform data so that it can
> >>> override what's in DT in order to fix up bad DTs? Or, has that
> >>> requirement been dropped. If it has, we can drop a bunch of code from a
> >>> variety of Tegra-specific drivers, I expect.
> >> Do you have an actual user for this? If not, then don't borrow trouble.
> >> Just drop it. Things like platform_data can always be added later only
> >> if it is needed.
> >
> > Currently all our board supports DT. we are not using any driver
> > instantiated by board files.
> > I will remove the platform data for current patch and if it is require
> > then will add later with reasoning.
> >
> > Hope this will be fine with Stephen also so that this basic patch can
> > be included into tree soon.
>
> I'm fine with it; it's just a change in policy that hadn't been
> communicated before.
Not really. For as long as I can remember there has been a strong bias
against unused code in the kernel. That goes for platform_data support
code as much as anything else.
What has been policy is that adding DT support must never break existing
non-DT support as long as non-DT booting is supported by a platform.
g.
^ permalink raw reply
* Re: [PATCH v2 00/11] tty: Fix buffer work access-after-free
From: Sasha Levin @ 2012-12-19 20:38 UTC (permalink / raw)
To: Peter Hurley
Cc: Alan Cox, Jiri Slaby, linux-serial, Greg Kroah-Hartman,
Ilya Zykov, linux-kernel
In-Reply-To: <1355849302.26487.15.camel@thor.lan>
On Tue, Dec 18, 2012 at 11:48 AM, Peter Hurley <peter@hurleysoftware.com> wrote:
> On Tue, 2012-12-18 at 10:44 -0500, Sasha Levin wrote:
>> I'm still seeing that warning with the new patch series:
>>
>> [ 549.561769] ------------[ cut here ]------------
>> [ 549.598755] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room+0xff/0x130()
>> [ 549.604058] scheduling buffer work for halted ldisc
>> [ 549.607741] Pid: 9417, comm: trinity-child28 Tainted: G D W
>> 3.7.0-next-20121217-sasha-00023-g8689ef9 #219
>> [ 549.652580] Call Trace:
>> [ 549.662754] [<ffffffff81c432cf>] ? n_tty_set_room+0xff/0x130
>> [ 549.665458] [<ffffffff8110cae7>] warn_slowpath_common+0x87/0xb0
>> [ 549.668257] [<ffffffff8110cb71>] warn_slowpath_fmt+0x41/0x50
>> [ 549.671007] [<ffffffff81c432cf>] n_tty_set_room+0xff/0x130
>> [ 549.673268] [<ffffffff81c44597>] reset_buffer_flags+0x137/0x150
>> [ 549.675607] [<ffffffff81c45b71>] n_tty_open+0x131/0x1c0
>
> This is a false-positive warning that means I need to refine the warning
> condition to not include this code path.
>
> Thanks again.
I'm really having a hard time doing any fuzzing after applying this
patch. I'm not sure it's related directly, but
the ldisc hangup lockup happens quite quickly and every time, so I
can't really get any good fuzzing done.
I'm not saying that this patch series is causing it, just saying that
I can't really test it at this point due to
that other lockup.
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH v2 00/11] tty: Fix buffer work access-after-free
From: Peter Hurley @ 2012-12-19 20:27 UTC (permalink / raw)
To: Ilya Zykov
Cc: Sasha Levin, Alan Cox, Jiri Slaby, linux-serial,
Greg Kroah-Hartman, linux-kernel
In-Reply-To: <50D0D5B5.5080506@ilyx.ru>
On Wed, 2012-12-19 at 00:44 +0400, Ilya Zykov wrote:
> Stress test for tty. :)
> You can use this program for debug new tty changes.
> Use with caution.
Thanks a lot for writing this. I was really struggling to come up with a
test that would exercise the code races in tty properly. I'm going test
this tonight and tomorrow (During the interlull, I've been doing the
yearly refresh of my desktop with mixed results :).
> In any case(with/without Peter's patches) I have BUG():
>
> BUG: unable to handle kernel NULL pointer dereference at 000000000000004c
> IP: [<ffffffff81116650>] devpts_pty_kill+0x17/0x81
> PGD 48696067 PUD a79c5067 PMD 0
> Oops: 0000 [#1] SMP
> Pid: 7877, comm: a.out Tainted: P O 3.7.0-next-20121214-tty.1+ #9 System manufacturer P5K Premium/P5K Premium
> RIP: 0010:[<ffffffff81116650>] [<ffffffff81116650>] devpts_pty_kill+0x17/0x81
> RSP: 0018:ffff8800484a3aa8 EFLAGS: 00010292
> RAX: ffff88012f0385a0 RBX: 0000000000000000 RCX: 0000000000000000
> RDX: 0000000000000000 RSI: 0000000000000282 RDI: 0000000000000000
> RBP: ffff8800484a3ac8 R08: 0000000000000000 R09: ffff880046f26d40
> R10: ffffffff81426ec8 R11: 0000000000000246 R12: ffff8800486a6c00
> R13: ffff8800484c7180 R14: ffff880046ec4890 R15: 00000000fffffffb
> FS: 00007f9a64345700(0000) GS:ffff88012fd00000(0000) knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
> CR2: 000000000000004c CR3: 00000000a7a01000 CR4: 00000000000407e0
> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> Process a.out (pid: 7877, threadinfo ffff8800484a2000, task ffff88007576d220)
> Stack:
> ffff880000000001 ffff88004854a400 ffff8800486a6c00 ffff8800484c7180
> ffff8800484a3ae8 ffffffff811e0c1b ffff8800484c7180 ffff88004854a400
> ffff8800484a3bd8 ffffffff811d83aa ffff880046f26d78 0000000000000009
> Call Trace:
> [<ffffffff811e0c1b>] pty_close+0x123/0x14f
> [<ffffffff811d83aa>] tty_release+0x17a/0x53d
> [<ffffffff812e7442>] ? __mutex_unlock_slowpath+0x15/0x39
> [<ffffffff811e1003>] ptmx_open+0x12c/0x161
> [<ffffffff810c6d4b>] chrdev_open+0x12a/0x14b
> [<ffffffff810c6c21>] ? cdev_put+0x23/0x23
> [<ffffffff810c27a9>] do_dentry_open+0x170/0x217
> [<ffffffff810c2933>] finish_open+0x34/0x40
> [<ffffffff810ce069>] do_last+0x8c4/0xa72
> [<ffffffff810ce2ed>] ? path_init+0xd6/0x2fe
> [<ffffffff810ceaf4>] path_openat+0xcb/0x363
> [<ffffffff81051033>] ? __dequeue_entity+0x2e/0x33
> [<ffffffff810cee91>] do_filp_open+0x38/0x84
> [<ffffffff810d9846>] ? __alloc_fd+0x51/0x110
> [<ffffffff810c24ed>] do_sys_open+0x6d/0xff
> [<ffffffff810c25ac>] sys_open+0x1c/0x1e
> [<ffffffff812ee652>] system_call_fastpath+0x16/0x1b
> Code: 08 02 00 00 48 89 c7 e8 6c f3 fb ff 5b 4c 89 e0 41 5c c9 c3 55 48 89 e5 41 55 41 54 53 48 89 fb 48 83 ec 08 48 8b 05 80 43 71 00 <81> 7f 4c 02 00 50 00 48 8b 40 08 4c 8b 60 60 75 04 0f 0b eb fe
> RIP [<ffffffff81116650>] devpts_pty_kill+0x17/0x81
> RSP <ffff8800484a3aa8>
> CR2: 000000000000004c
[...]
> With Peter's patches I have WARN():
Yep. Sasha found this Saturday. It's a false positive that I need to
correct for this code path explicitly.
> WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room+0xe7/0xf8()
> Hardware name: P5K Premium
> scheduling buffer work for halted ldisc
> Pid: 3127, comm: a.out Tainted: P W O 3.7.0-next-20121214-tty.1+ #9
> Call Trace:
> [<ffffffff8102ce58>] warn_slowpath_common+0x80/0x98
> [<ffffffff8102cf04>] warn_slowpath_fmt+0x41/0x43
> [<ffffffff811dae01>] n_tty_set_room+0xe7/0xf8
> [<ffffffff811db2cf>] reset_buffer_flags+0xad/0xb6
> [<ffffffff811dd01b>] n_tty_open+0xca/0x11f
> [<ffffffff811de4c9>] tty_ldisc_open+0x4e/0x5f
> [<ffffffff811ded14>] tty_ldisc_hangup+0x1f5/0x292
> [<ffffffff810d0289>] ? fasync_helper+0x22/0x6c
> [<ffffffff811d7a03>] __tty_hangup+0x102/0x30e
> [<ffffffff810d52ad>] ? d_delete+0x12d/0x136
> [<ffffffff811d7c2a>] tty_vhangup+0x9/0xb
> [<ffffffff811e0c3b>] pty_close+0x143/0x14f
> [<ffffffff811d83aa>] tty_release+0x17a/0x53d
> [<ffffffff8104b9f7>] ? __wake_up+0x3f/0x48
> [<ffffffff810efb55>] ? fsnotify+0x21d/0x244
> [<ffffffff810c4bc5>] __fput+0xf9/0x1bd
> [<ffffffff810c4ccf>] ____fput+0x9/0xb
> [<ffffffff81041cd4>] task_work_run+0x80/0x98
> [<ffffffff810025bd>] do_notify_resume+0x58/0x69
> [<ffffffff812ee8da>] int_signal+0x12/0x17
>
>
> ---
> /*
> * stress_test_tty.c
> *
> * Created on: Dec, 2012
> * Copyright (C) 2012 Ilya Zykov
> *
> * This program is free software: you can redistribute it and/or modify
> * it under the terms of the GNU General Public License as published by
> * the Free Software Foundation, either version 2 of the License, or
> * (at your option) any later version.
> *
> * This program is distributed in the hope that it will be useful,
> * but WITHOUT ANY WARRANTY; without even the implied warranty of
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> *
> * You should have received a copy of the GNU General Public License
> * along with this program. If not, see <http://www.gnu.org/licenses/>.
> */
Thanks for GPL'ing this test. It will make things much easier to test
and comment on.
Happy Holidays,
Peter Hurley
^ permalink raw reply
* [PATCH 5/6] tty: of_serial: Add pinctrl support
From: Maxime Ripard @ 2012-12-19 20:18 UTC (permalink / raw)
To: Arnd Bergmann, Olof Johansson
Cc: Alejandro Mery, Greg Kroah-Hartman, linux-serial, Stefan Roese,
linux-arm-kernel, Alan Cox
In-Reply-To: <1355948295-19680-1-git-send-email-maxime.ripard@free-electrons.com>
Use pinctrl to configure the SoCs pins directly from the driver.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-serial@vger.kernel.org
---
drivers/tty/serial/of_serial.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index df443b9..f66bdab 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <linux/serial_reg.h>
@@ -54,6 +55,7 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
int type, struct uart_port *port)
{
struct resource resource;
+ struct pinctrl *pinctrl;
struct device_node *np = ofdev->dev.of_node;
u32 clk, spd, prop;
int ret;
@@ -73,6 +75,11 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
return ret;
}
+ pinctrl = devm_pinctrl_get_select_default(&ofdev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&ofdev->dev,
+ "pins are not configured from the driver\n");
+
spin_lock_init(&port->lock);
port->mapbase = resource.start;
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH] tty: Fix unreasonable write toward closed pty.
From: Ilya Zykov @ 2012-12-19 19:38 UTC (permalink / raw)
To: Alan Cox
Cc: Greg Kroah-Hartman, Alan Cox, Jiri Slaby, Peter Hurley,
Sasha Levin, linux-serial, linux-kernel
In-Reply-To: <20121219191030.4ee20a1c@pyramind.ukuu.org.uk>
On 19.12.2012 23:10, Alan Cox wrote:
>> diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
>> index a82b399..1ce1362 100644
>> --- a/drivers/tty/pty.c
>> +++ b/drivers/tty/pty.c
>> @@ -116,6 +116,8 @@ static int pty_space(struct tty_struct *to)
>>
>> static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
>> {
>> + if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
>> + return -EIO;
>
> The flag can change between the test and ny further code being executed ?
>
> Alan
>
Yes, if I understand you correctly, but this is no matter here,
because ldisc's layer will wait, flush this data later.
Here, only beginning stage of tty_close.
This is safe later stage from unnecessary data.
Ilya
^ permalink raw reply
* Re: [PATCH] tty: Fix unreasonable write toward closed pty.
From: Alan Cox @ 2012-12-19 19:10 UTC (permalink / raw)
To: Ilya Zykov
Cc: Greg Kroah-Hartman, Alan Cox, Jiri Slaby, Peter Hurley,
Sasha Levin, linux-serial, linux-kernel
In-Reply-To: <50D20E87.9060403@ilyx.ru>
> diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
> index a82b399..1ce1362 100644
> --- a/drivers/tty/pty.c
> +++ b/drivers/tty/pty.c
> @@ -116,6 +116,8 @@ static int pty_space(struct tty_struct *to)
>
> static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
> {
> + if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
> + return -EIO;
The flag can change between the test and ny further code being executed ?
Alan
^ permalink raw reply
* [PATCH] tty: Fix ptmx open without closed slave.
From: Ilya Zykov @ 2012-12-19 19:00 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: Alan Cox, Jiri Slaby, linux-kernel, linux-serial
When we are opening ptmx, we have closed pts, by description.
Now only if we open and after close all pts' descriptions, pty_close() sets
this bit correctly
Signed-off-by: Ilya Zykov <ilya@ilyx.ru>
---
drivers/tty/pty.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 1ce1362..7b69307 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -659,6 +659,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
retval = ptm_driver->ops->open(tty, filp);
if (retval)
goto err_release;
+ set_bit(TTY_OTHER_CLOSED, &tty->flags); /* THE SLAVE STILL CLOSED */
tty_unlock(tty);
return 0;
^ permalink raw reply related
* [PATCH] tty: Fix unreasonable write toward closed pty.
From: Ilya Zykov @ 2012-12-19 18:59 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Alan Cox, Jiri Slaby, Peter Hurley, Sasha Levin, linux-serial,
linux-kernel
We should not write toward the closed pty.
Now it happens, if one side close last file descriptor,
and other side in this moment write to it.
It also prevents scheduling unnecessary work.
Signed-off-by: Ilya Zykov <ilya@ilyx.ru>
---
drivers/tty/pty.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index a82b399..1ce1362 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -116,6 +116,8 @@ static int pty_space(struct tty_struct *to)
static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
{
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+ return -EIO;
struct tty_struct *to = tty->link;
if (tty->stopped)
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox