Linux Serial subsystem development
 help / color / mirror / Atom feed
* Re: [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
From: Alan Cox @ 2012-12-03 11:46 UTC (permalink / raw)
  To: Lv Zheng
  Cc: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, linux-acpi,
	linux-serial
In-Reply-To: <6d5fc2e0799c12554aa8acdb2d7782ba8643902b.1354505472.git.lv.zheng@intel.com>

> + * uart_tty_find - find the associated TTY device for the UART target
> + *		   device
> + * @drv: the low level UART driver
> + * @dev: the parent physical device for the TTY devices
> + * @line: the line number of the UART target device
> + *

Our top level abstraction is struct tty and struct tty_port. There is
nothing requiring a serial tty is using the uart helper layer, nor
should there be, so your code needs to support devices not using the
uart layer. Otherwise it looks quite reasonable.

our 3.7 tty layer has sysfs nodes on the tty which may also help

Alan

^ permalink raw reply

* [PATCHv2 5/5] serial: 8250_dw: Set FIFO size dynamically
From: Heikki Krogerus @ 2012-12-03 11:17 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Alan Cox, Jamie Iles, linux-serial, LKML
In-Reply-To: <1354533479-27306-1-git-send-email-heikki.krogerus@linux.intel.com>

Designware UART provides optional Component Parameter
Register that lists most of the capabilities of the UART,
including FIFO size. This uses that register to set FIFO
size for the port before registering it.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/tty/serial/8250/8250_dw.c |   57 ++++++++++++++++++++++++++++++++++---
 1 file changed, 53 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index c4e28df..e834d41 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -25,6 +25,28 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
+/* Offsets for the DesignWare specific registers */
+#define DW_UART_USR	0x1f /* UART Status Register */
+#define DW_UART_CPR	0xf4 /* Component Parameter Register */
+#define DW_UART_UCV	0xf8 /* UART Component Version */
+
+/* Component Parameter Register bits */
+#define DW_UART_CPR_ABP_DATA_WIDTH	(3 << 0)
+#define DW_UART_CPR_AFCE_MODE		(1 << 4)
+#define DW_UART_CPR_THRE_MODE		(1 << 5)
+#define DW_UART_CPR_SIR_MODE		(1 << 6)
+#define DW_UART_CPR_SIR_LP_MODE		(1 << 7)
+#define DW_UART_CPR_ADDITIONAL_FEATURES	(1 << 8)
+#define DW_UART_CPR_FIFO_ACCESS		(1 << 9)
+#define DW_UART_CPR_FIFO_STAT		(1 << 10)
+#define DW_UART_CPR_SHADOW		(1 << 11)
+#define DW_UART_CPR_ENCODED_PARMS	(1 << 12)
+#define DW_UART_CPR_DMA_EXTRA		(1 << 13)
+#define DW_UART_CPR_FIFO_MODE		(0xff << 16)
+/* Helper for fifo size calculation */
+#define DW_UART_CPR_FIFO_SIZE(a)	(((a >> 16) & 0xff) * 16)
+
+
 struct dw8250_data {
 	int	last_lcr;
 	int	line;
@@ -66,9 +88,6 @@ static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
 	return readl(p->membase + offset);
 }
 
-/* Offset for the DesignWare's UART Status Register. */
-#define UART_USR	0x1f
-
 static int dw8250_handle_irq(struct uart_port *p)
 {
 	struct dw8250_data *d = p->private_data;
@@ -78,7 +97,7 @@ static int dw8250_handle_irq(struct uart_port *p)
 		return 1;
 	} else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
 		/* Clear the USR and write the LCR again. */
-		(void)p->serial_in(p, UART_USR);
+		(void)p->serial_in(p, DW_UART_USR);
 		p->serial_out(p, d->last_lcr, UART_LCR);
 
 		return 1;
@@ -119,6 +138,34 @@ static int dw8250_probe_of(struct uart_port *p)
 	return 0;
 }
 
+static void dw8250_setup_port(struct uart_8250_port *up)
+{
+	struct uart_port	*p = &up->port;
+	u32			reg = readl(p->membase + DW_UART_UCV);
+
+	/*
+	 * If the Component Version Register returns zero, we know that
+	 * ADDITIONAL_FEATURES are not enabled. No need to go any further.
+	 */
+	if (!reg)
+		return;
+
+	dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n",
+		(reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
+
+	reg = readl(p->membase + DW_UART_CPR);
+	if (!reg)
+		return;
+
+	/* Select the type based on fifo */
+	if (reg & DW_UART_CPR_FIFO_MODE) {
+		p->type = PORT_16550A;
+		p->flags |= UPF_FIXED_TYPE;
+		p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
+		up->tx_loadsz = p->fifosize;
+	}
+}
+
 static int dw8250_probe(struct platform_device *pdev)
 {
 	struct uart_8250_port uart = {};
@@ -156,6 +203,8 @@ static int dw8250_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	dw8250_setup_port(&uart);
+
 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
-- 
1.7.10.4

^ permalink raw reply related

* [PATCHv2 4/5] serial: 8250_dw: Move device tree code to separate function
From: Heikki Krogerus @ 2012-12-03 11:17 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Alan Cox, Jamie Iles, linux-serial, LKML
In-Reply-To: <1354533479-27306-1-git-send-email-heikki.krogerus@linux.intel.com>

Trivial cleanup. This makes it easier to add different
methods to enumerate the device, for example ACPI 5.0
enumeration.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/tty/serial/8250/8250_dw.c |   76 ++++++++++++++++++++++---------------
 1 file changed, 46 insertions(+), 30 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 300bbed..c4e28df 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -87,25 +87,51 @@ static int dw8250_handle_irq(struct uart_port *p)
 	return 0;
 }
 
+static int dw8250_probe_of(struct uart_port *p)
+{
+	struct device_node	*np = p->dev->of_node;
+	u32			val;
+
+	if (!of_property_read_u32(np, "reg-io-width", &val)) {
+		switch (val) {
+		case 1:
+			break;
+		case 4:
+			p->iotype = UPIO_MEM32;
+			p->serial_in = dw8250_serial_in32;
+			p->serial_out = dw8250_serial_out32;
+			break;
+		default:
+			dev_err(p->dev, "unsupported reg-io-width (%u)\n", val);
+			return -EINVAL;
+		}
+	}
+
+	if (!of_property_read_u32(np, "reg-shift", &val))
+		p->regshift = val;
+
+	if (of_property_read_u32(np, "clock-frequency", &val)) {
+		dev_err(p->dev, "no clock-frequency property set\n");
+		return -EINVAL;
+	}
+	p->uartclk = val;
+
+	return 0;
+}
+
 static int dw8250_probe(struct platform_device *pdev)
 {
 	struct uart_8250_port uart = {};
 	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-	struct device_node *np = pdev->dev.of_node;
-	u32 val;
 	struct dw8250_data *data;
+	int err;
 
 	if (!regs || !irq) {
 		dev_err(&pdev->dev, "no registers/irq defined\n");
 		return -EINVAL;
 	}
 
-	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-	uart.port.private_data = data;
-
 	spin_lock_init(&uart.port.lock);
 	uart.port.mapbase = regs->start;
 	uart.port.irq = irq->start;
@@ -121,30 +147,20 @@ static int dw8250_probe(struct platform_device *pdev)
 	uart.port.iotype = UPIO_MEM;
 	uart.port.serial_in = dw8250_serial_in;
 	uart.port.serial_out = dw8250_serial_out;
-	if (!of_property_read_u32(np, "reg-io-width", &val)) {
-		switch (val) {
-		case 1:
-			break;
-		case 4:
-			uart.port.iotype = UPIO_MEM32;
-			uart.port.serial_in = dw8250_serial_in32;
-			uart.port.serial_out = dw8250_serial_out32;
-			break;
-		default:
-			dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n",
-				val);
-			return -EINVAL;
-		}
+
+	if (pdev->dev.of_node) {
+		err = dw8250_probe_of(&uart.port);
+		if (err)
+			return err;
+	} else {
+		return -ENODEV;
 	}
 
-	if (!of_property_read_u32(np, "reg-shift", &val))
-		uart.port.regshift = val;
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
 
-	if (of_property_read_u32(np, "clock-frequency", &val)) {
-		dev_err(&pdev->dev, "no clock-frequency property set\n");
-		return -EINVAL;
-	}
-	uart.port.uartclk = val;
+	uart.port.private_data = data;
 
 	data->line = serial8250_register_8250_port(&uart);
 	if (data->line < 0)
@@ -187,7 +203,7 @@ static int dw8250_resume(struct platform_device *pdev)
 #define dw8250_resume NULL
 #endif /* CONFIG_PM */
 
-static const struct of_device_id dw8250_match[] = {
+static const struct of_device_id dw8250_of_match[] = {
 	{ .compatible = "snps,dw-apb-uart" },
 	{ /* Sentinel */ }
 };
@@ -197,7 +213,7 @@ static struct platform_driver dw8250_platform_driver = {
 	.driver = {
 		.name		= "dw-apb-uart",
 		.owner		= THIS_MODULE,
-		.of_match_table	= dw8250_match,
+		.of_match_table	= dw8250_of_match,
 	},
 	.probe			= dw8250_probe,
 	.remove			= dw8250_remove,
-- 
1.7.10.4

^ permalink raw reply related

* [PATCHv2 3/5] serial: 8250_dw: Map IO memory
From: Heikki Krogerus @ 2012-12-03 11:17 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Alan Cox, Jamie Iles, linux-serial, LKML
In-Reply-To: <1354533479-27306-1-git-send-email-heikki.krogerus@linux.intel.com>

This needs to be done in order to later access the
Designware specific registers.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/tty/serial/8250/8250_dw.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index ff83ea5..300bbed 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -111,10 +111,13 @@ static int dw8250_probe(struct platform_device *pdev)
 	uart.port.irq = irq->start;
 	uart.port.handle_irq = dw8250_handle_irq;
 	uart.port.type = PORT_8250;
-	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
-		UPF_FIXED_PORT;
+	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT;
 	uart.port.dev = &pdev->dev;
 
+	uart.port.membase = ioremap(regs->start, regs->end - regs->start);
+	if (!uart.port.membase)
+		return -ENOMEM;
+
 	uart.port.iotype = UPIO_MEM;
 	uart.port.serial_in = dw8250_serial_in;
 	uart.port.serial_out = dw8250_serial_out;
-- 
1.7.10.4

^ permalink raw reply related

* [PATCHv2 2/5] serial: 8250_dw: Don't use UPF_FIXED_TYPE
From: Heikki Krogerus @ 2012-12-03 11:17 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Alan Cox, Jamie Iles, linux-serial, LKML
In-Reply-To: <1354533479-27306-1-git-send-email-heikki.krogerus@linux.intel.com>

Allow 8250.c to determine the port type for us. This allows
the driver take advantage of FIFO on Designware UARTs that
have it.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/tty/serial/8250/8250_dw.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 1d0dba2..ff83ea5 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -112,7 +112,7 @@ static int dw8250_probe(struct platform_device *pdev)
 	uart.port.handle_irq = dw8250_handle_irq;
 	uart.port.type = PORT_8250;
 	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
-		UPF_FIXED_PORT | UPF_FIXED_TYPE;
+		UPF_FIXED_PORT;
 	uart.port.dev = &pdev->dev;
 
 	uart.port.iotype = UPIO_MEM;
-- 
1.7.10.4

^ permalink raw reply related

* [PATCHv2 1/5] serial: 8250: Allow drivers to deliver capabilities
From: Heikki Krogerus @ 2012-12-03 11:17 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Alan Cox, Jamie Iles, linux-serial, LKML
In-Reply-To: <1354533479-27306-1-git-send-email-heikki.krogerus@linux.intel.com>

Modern UARTs are able to provide information about their
capabilities such as FIFO size. This allows the drivers to
deliver this information to 8250.c when they are registering
ports.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/tty/serial/8250/8250.c |   22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index d085e3a..da3f828 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -1980,9 +1980,12 @@ static int serial8250_startup(struct uart_port *port)
 	if (port->type == PORT_8250_CIR)
 		return -ENODEV;
 
-	port->fifosize = uart_config[up->port.type].fifo_size;
-	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
-	up->capabilities = uart_config[up->port.type].flags;
+	if (!port->fifosize)
+		port->fifosize = uart_config[port->type].fifo_size;
+	if (!up->tx_loadsz)
+		up->tx_loadsz = uart_config[port->type].tx_loadsz;
+	if (!up->capabilities)
+		up->capabilities = uart_config[port->type].flags;
 	up->mcr = 0;
 
 	if (port->iotype != up->cur_iotype)
@@ -2815,9 +2818,12 @@ static void
 serial8250_init_fixed_type_port(struct uart_8250_port *up, unsigned int type)
 {
 	up->port.type = type;
-	up->port.fifosize = uart_config[type].fifo_size;
-	up->capabilities = uart_config[type].flags;
-	up->tx_loadsz = uart_config[type].tx_loadsz;
+	if (!up->port.fifosize)
+		up->port.fifosize = uart_config[type].fifo_size;
+	if (!up->tx_loadsz)
+		up->tx_loadsz = uart_config[type].tx_loadsz;
+	if (!up->capabilities)
+		up->capabilities = uart_config[type].flags;
 }
 
 static void __init
@@ -3251,6 +3257,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 		uart->bugs		= up->bugs;
 		uart->port.mapbase      = up->port.mapbase;
 		uart->port.private_data = up->port.private_data;
+		uart->port.fifosize	= up->port.fifosize;
+		uart->tx_loadsz		= up->tx_loadsz;
+		uart->capabilities	= up->capabilities;
+
 		if (up->port.dev)
 			uart->port.dev = up->port.dev;
 
-- 
1.7.10.4

^ permalink raw reply related

* [PATCHv2 0/5] serial: 8250: 8250_dw changes and dynamic capabilities
From: Heikki Krogerus @ 2012-12-03 11:17 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Alan Cox, Jamie Iles, linux-serial, LKML


Changes since v1:
- rebased on top of Greg's tty-next

These are mainly small 8250_dw.c changes. The interesting patch is
probable the first one that changes 8250.c so the drivers are able to
deliver their UART's capabilities when they are registering ports.


Heikki Krogerus (5):
  serial: 8250: Allow drivers to deliver capabilities
  serial: 8250_dw: Don't use UPF_FIXED_TYPE
  serial: 8250_dw: Map IO memory
  serial: 8250_dw: Move device tree code to separate function
  serial: 8250_dw: Set FIFO size dynamically

 drivers/tty/serial/8250/8250.c    |   22 ++++--
 drivers/tty/serial/8250/8250_dw.c |  140 +++++++++++++++++++++++++++----------
 2 files changed, 120 insertions(+), 42 deletions(-)

-- 
1.7.10.4

^ permalink raw reply

* [RFC PATCH 3/3] UART: Add dummy devices to test the enumeration.
From: Lv Zheng @ 2012-12-03  3:40 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox
  Cc: linux-acpi, linux-serial, Lv Zheng
In-Reply-To: <cover.1354505471.git.lv.zheng@intel.com>

This is a hidden test patch that would not be sent to the public.
There might be some udev add-ons for matching the UART target devices from
the user space.

1. The result of the UART dummy target device is as follows:

# udevadm monitor --kernel --environment > ~/uart.uevents
# echo add > /sys/bus/uart/uevent
# echo add > /sys/bus/uart/devices/DUMMY/uevent
# cat ~/uart.uevents
monitor will print the received events for:
KERNEL - the kernel uevent

KERNEL[252.443458] add      /bus/uart (bus)
ACTION=add
DEVPATH=/bus/uart
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/serial8250/DUMMY (uart)
ACTION=add
DEVPATH=/devices/platform/serial8250/DUMMY
DEVTYPE=uart_device
MODALIAS=uart:DUMMY
SEQNUM=1144
SUBSYSTEM=uart

# cat /sys/bus/uart/devices/DUMMY/modalias
uart:DUMMY
# cat /sys/bus/uart/devices/DUMMY/tty_dev
ttyS0
# cat /sys/bus/uart/devices/DUMMY/tty_attrs
115200 8N1 HW SW
# cat /sys/bus/uart/devices/DUMMY/modem_lines
LE:RTS,

2. The result of the UART customized DSDT target device is as follows:

The test result is attached as part of the first patch in this series.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |   10 ++-
 drivers/tty/serial/8250/8250_dummy.c |  128 ++++++++++++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 +++
 drivers/tty/serial/8250/Makefile     |    1 +
 drivers/tty/serial/serial_bus.c      |   17 +++++
 include/linux/serial_8250.h          |    2 +
 include/linux/serial_core.h          |    3 +
 8 files changed, 170 insertions(+), 2 deletions(-)
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 67a7fa6..4892015 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -36,6 +36,7 @@ static const char *dummy_hid = "device";
 static const struct acpi_device_id acpi_platform_device_ids[] = {
 
 	{ "PNP0D40" },
+	{ "INTF000" },
 
 	{ }
 };
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index 3ba4234..6f503f4 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -56,7 +56,7 @@ static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
 
 static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
 
-static struct uart_driver serial8250_reg;
+struct uart_driver serial8250_reg;
 
 static int serial_index(struct uart_port *port)
 {
@@ -2902,7 +2902,7 @@ int serial8250_find_port(struct uart_port *p)
 #define SERIAL8250_CONSOLE	NULL
 #endif
 
-static struct uart_driver serial8250_reg = {
+struct uart_driver serial8250_reg = {
 	.owner			= THIS_MODULE,
 	.driver_name		= "serial",
 	.dev_name		= "ttyS",
@@ -2910,6 +2910,7 @@ static struct uart_driver serial8250_reg = {
 	.minor			= 64,
 	.cons			= SERIAL8250_CONSOLE,
 };
+EXPORT_SYMBOL_GPL(serial8250_reg);
 
 /*
  * early_serial_setup - early registration for 8250 ports
@@ -2987,6 +2988,8 @@ void serial8250_resume_port(int line)
 	uart_resume_port(&serial8250_reg, port);
 }
 
+struct uart_device *uart_dummy;
+
 /*
  * Register a set of serial devices attached to a platform device.  The
  * list is terminated with a zero flags entry, which means we expect
@@ -3032,6 +3035,8 @@ static int __devinit serial8250_probe(struct platform_device *dev)
 				p->irq, ret);
 		}
 	}
+	uart_dummy = uart_register_dummy(&serial8250_reg, &dev->dev, 0);
+
 	return 0;
 }
 
@@ -3042,6 +3047,7 @@ static int __devexit serial8250_remove(struct platform_device *dev)
 {
 	int i;
 
+	uart_unregister_device(uart_dummy);
 	for (i = 0; i < nr_uarts; i++) {
 		struct uart_8250_port *up = &serial8250_ports[i];
 
diff --git a/drivers/tty/serial/8250/8250_dummy.c b/drivers/tty/serial/8250/8250_dummy.c
new file mode 100644
index 0000000..5720fbb
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dummy.c
@@ -0,0 +1,128 @@
+/*
+ * 8250_dummy.c: Dummy 8250 UART target device enumerator
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * 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; version 2
+ * of the License.
+ */
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/acpi.h>
+#include <linux/acpi_uart.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct dummy8250_data {
+	int	last_lcr;
+	int	line;
+};
+
+static void dummy8250_serial_out(struct uart_port *p, int offset, int value)
+{
+}
+
+static unsigned int dummy8250_serial_in(struct uart_port *p, int offset)
+{
+	return 0;
+}
+
+struct klist *dummy8250_mgr;
+int dummy8250_num;
+
+static int __devinit dummy8250_probe(struct platform_device *pdev)
+{
+	struct uart_8250_port uart = {};
+	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct dummy8250_data *data;
+#ifdef CONFIG_ACPI_UART
+	struct klist *mgr;
+#endif
+
+	dev_info(&pdev->dev, "1\n");
+	if (!regs) {
+		dev_err(&pdev->dev, "no registers defined\n");
+		return -EINVAL;
+	}
+
+	dev_info(&pdev->dev, "2\n");
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	uart.port.private_data = data;
+
+	spin_lock_init(&uart.port.lock);
+	uart.port.mapbase = regs->start;
+	uart.port.type = PORT_8250;
+	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+		UPF_FIXED_PORT | UPF_FIXED_TYPE;
+	uart.port.dev = &pdev->dev;
+
+	uart.port.iotype = UPIO_MEM;
+	uart.port.serial_in = dummy8250_serial_in;
+	uart.port.serial_out = dummy8250_serial_out;
+	uart.port.uartclk = 40000000;
+
+	dev_info(&pdev->dev, "3\n");
+	data->line = serial8250_register_8250_port(&uart);
+	if (data->line < 0)
+		return data->line;
+
+#ifdef CONFIG_ACPI_UART
+	dev_info(&pdev->dev, "4\n");
+	mgr = acpi_uart_register_devices(&serial8250_reg, dummy8250_mgr,
+					 &pdev->dev, data->line);
+	if (mgr) {
+		dummy8250_mgr = mgr;
+		dummy8250_num++;
+	}
+#endif
+	dev_info(&pdev->dev, "5\n");
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int __devexit dummy8250_remove(struct platform_device *pdev)
+{
+	struct dummy8250_data *data = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_ACPI_UART
+	dummy8250_num--;
+	if (!dummy8250_num)
+		acpi_uart_unregister_devices(dummy8250_mgr);
+#endif
+	serial8250_unregister_port(data->line);
+
+	return 0;
+}
+
+static const struct acpi_device_id dummy8250_match[] = {
+	{ .id = "INTF000" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, dummy8250_match);
+
+static struct platform_driver dummy8250_platform_driver = {
+	.driver = {
+		.name			= "dummy-uart",
+		.owner			= THIS_MODULE,
+		.acpi_match_table	= dummy8250_match,
+	},
+	.probe				= dummy8250_probe,
+	.remove				= __devexit_p(dummy8250_remove),
+};
+
+module_platform_driver(dummy8250_platform_driver);
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Dummy 8250 serial port driver");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index f3d283f..3ba480a 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -270,6 +270,16 @@ config SERIAL_8250_DW
 	  Selecting this option will enable handling of the extra features
 	  present in the Synopsys DesignWare APB UART.
 
+config SERIAL_8250_DUMMY
+	tristate "Support for dummy ACPI 8250"
+	depends on SERIAL_8250 && ACPI
+	help
+	  Selecting this option will enable a test UART target device DUMMY
+	  under the ISA serial8250 and a test UART host adapter INTF000
+	  as an platform device for the purpose of testing the ACPI UART
+	  enumeration support.
+	  If unsure, say "N" here.
+
 config SERIAL_8250_EM
 	tristate "Support for Emma Mobile intergrated serial port"
 	depends on SERIAL_8250 && ARM && HAVE_CLK
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 108fe7f..fb82aa9 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
 obj-$(CONFIG_SERIAL_8250_FSL)		+= 8250_fsl.o
 obj-$(CONFIG_SERIAL_8250_DW)		+= 8250_dw.o
 obj-$(CONFIG_SERIAL_8250_EM)		+= 8250_em.o
+obj-$(CONFIG_SERIAL_8250_DUMMY)		+= 8250_dummy.o
diff --git a/drivers/tty/serial/serial_bus.c b/drivers/tty/serial/serial_bus.c
index a6674da..e3d0509 100644
--- a/drivers/tty/serial/serial_bus.c
+++ b/drivers/tty/serial/serial_bus.c
@@ -384,6 +384,23 @@ void uart_del_adapter(struct klist *adap)
 }
 EXPORT_SYMBOL_GPL(uart_del_adapter);
 
+static struct uart_board_info dummy_target = {
+	.type = "DUMMY",
+	.baud = 115200,
+	.cflag = CS8 | CSTOPB | CRTSCTS,
+	.iflag = (IXON | IXOFF),
+	.mctrl = TIOCM_LE | TIOCM_RTS,
+};
+
+struct uart_device *uart_register_dummy(struct uart_driver *drv,
+					struct device *dev,
+					unsigned int line)
+{
+	dummy_target.line = line;
+	return uart_register_device(drv, NULL, dev, &dummy_target);
+}
+EXPORT_SYMBOL_GPL(uart_register_dummy);
+
 struct bus_type uart_bus_type = {
 	.name		= "uart",
 };
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index c174c90..3b6c260 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -120,4 +120,6 @@ extern void serial8250_set_isa_configurator(void (*v)
 					(int port, struct uart_port *up,
 						unsigned short *capabilities));
 
+extern struct uart_driver serial8250_reg;
+
 #endif
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 422f8cc4..a3d1d5d 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -418,6 +418,9 @@ struct uart_device *uart_register_device(struct uart_driver *drv,
 					 struct device *dev,
 					 struct uart_board_info const *info);
 void uart_unregister_device(struct uart_device *udev);
+struct uart_device *uart_register_dummy(struct uart_driver *drv,
+					struct device *dev,
+					unsigned int line);
 
 struct device *uart_tty_find(struct uart_driver *drv,
 			     struct device *dev, unsigned int line);
-- 
1.7.10


^ permalink raw reply related

* [RFC PATCH 2/3] ACPI / UART: Add ACPI enumeration support for UART bus.
From: Lv Zheng @ 2012-12-03  3:40 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox
  Cc: linux-acpi, linux-serial, Lv Zheng
In-Reply-To: <cover.1354505471.git.lv.zheng@intel.com>

ACPI 5.0 specification introduces the methods of enumerating the target
devices connected on the serial buses.
This patch follows the specification, implementing such UART enumeration
machanism for the Linux.

In order to use this UART device enumeration mechanism, driver writers
are required to call the following APIs:
1. APIs called _after_ the creation of the uart ports:
   adap = acpi_uart_register_devices(driver, adap, parent, line);
   Where:
    driver: the low level UART driver
    parent: the physical device of the UART ports
    adap: the management list of the target devices, can be set as NULL
    line: the line number of the target device
2. APIs called _before_ the deletion of the uart ports:
   acpi_uart_unregister_devices(adap);
   Where:
    adap: the UART target device manager

NOTE: If the driver writer has already created the management list for
      the UART target devices, the adap parameter can be set to the
      already created non-NULL value.
NOTE: The ACPI 5.0 specification assumes one physical device per-port.
      In this situation, the line parameter might be set to 0 when the
      acpi_uart_register_devices() is called.
      This patch set can also support the multi-port UART adapters,
      where the line should be set as ACPI_UART_LINE_UNKNOWN.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/Kconfig      |    7 ++
 drivers/acpi/Makefile     |    1 +
 drivers/acpi/acpi_uart.c  |  255 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/acpi_uart.h |   40 +++++++
 4 files changed, 303 insertions(+)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 include/linux/acpi_uart.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 0300bf6..d40203f 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -187,6 +187,12 @@ config ACPI_I2C
 	help
 	  ACPI I2C enumeration support.
 
+config ACPI_UART
+	def_tristate SERIAL_CORE
+	depends on SERIAL_CORE
+	help
+	  ACPI UART enumeration support.
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	select THERMAL
@@ -200,6 +206,7 @@ config ACPI_PROCESSOR
 
 	  To compile this driver as a module, choose M here:
 	  the module will be called processor.
+
 config ACPI_IPMI
 	tristate "IPMI"
 	depends on EXPERIMENTAL && IPMI_SI && IPMI_HANDLER
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 2a4502b..784f332 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
 obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
 obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
 obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
+obj-$(CONFIG_ACPI_UART)		+= acpi_uart.o
 
 # processor has its own "processor." module_param namespace
 processor-y			:= processor_driver.o processor_throttling.o
diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c
new file mode 100644
index 0000000..82bbfd4
--- /dev/null
+++ b/drivers/acpi/acpi_uart.c
@@ -0,0 +1,255 @@
+/*
+ * acpi_uart.c - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * 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; version 2
+ * of the License.
+ */
+
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+
+
+static int
+acpi_uart_add_resources(struct acpi_resource *ares, void *context)
+{
+	struct uart_board_info *info = context;
+
+	if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+		struct acpi_resource_uart_serialbus *sb;
+
+		sb = &ares->data.uart_serial_bus;
+		if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART)
+			return 1;
+
+		/* baud rate */
+		info->baud = sb->default_baud_rate;
+
+		/* data bits */
+		info->cflag &= ~CSIZE;
+		switch (sb->data_bits) {
+		case ACPI_UART_5_DATA_BITS:
+			info->cflag |= CS5;
+			break;
+		case ACPI_UART_6_DATA_BITS:
+			info->cflag |= CS6;
+			break;
+		case ACPI_UART_7_DATA_BITS:
+			info->cflag |= CS7;
+			break;
+		case ACPI_UART_8_DATA_BITS:
+		default:
+			info->cflag |= CS8;
+			break;
+		}
+
+		/* parity */
+		info->cflag &= ~(PARENB | PARODD);
+		if (sb->parity == ACPI_UART_PARITY_EVEN)
+			info->cflag |= PARENB;
+		else if (sb->parity == ACPI_UART_PARITY_ODD)
+			info->cflag |= (PARENB | PARODD);
+
+		/* stop bits */
+		if (sb->stop_bits == ACPI_UART_2_STOP_BITS)
+			info->cflag |= CSTOPB;
+		else
+			info->cflag &= ~CSTOPB;
+
+		/* HW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_HW)
+			info->cflag |= CRTSCTS;
+		else
+			info->cflag &= ~CRTSCTS;
+
+		/* SW control */
+		if (sb->flow_control & ACPI_UART_FLOW_CONTROL_XON_XOFF)
+			info->iflag |= (IXON | IXOFF);
+		else
+			info->iflag &= ~(IXON|IXOFF|IXANY);
+
+		/* endianess */
+		if (sb->endian == ACPI_UART_LITTLE_ENDIAN)
+			info->mctrl |= TIOCM_LE;
+		else
+			info->mctrl &= ~TIOCM_LE;
+
+		/* terminal lines */
+		if (sb->lines_enabled & ACPI_UART_DATA_TERMINAL_READY)
+			info->mctrl |= TIOCM_DTR;
+		else
+			info->mctrl &= ~TIOCM_DTR;
+		if (sb->lines_enabled & ACPI_UART_REQUEST_TO_SEND)
+			info->mctrl |= TIOCM_RTS;
+		else
+			info->mctrl &= ~TIOCM_RTS;
+
+		/* modem lines */
+		if (sb->lines_enabled & ACPI_UART_CLEAR_TO_SEND)
+			info->mctrl |= TIOCM_CTS;
+		else
+			info->mctrl &= ~TIOCM_CTS;
+		if (sb->lines_enabled & ACPI_UART_CARRIER_DETECT)
+			info->mctrl |= TIOCM_CAR;
+		else
+			info->mctrl &= ~TIOCM_CAR;
+		if (sb->lines_enabled & ACPI_UART_RING_INDICATOR)
+			info->mctrl |= TIOCM_RNG;
+		else
+			info->mctrl &= ~TIOCM_RNG;
+		if (sb->lines_enabled & ACPI_UART_DATA_SET_READY)
+			info->mctrl |= TIOCM_DSR;
+		else
+			info->mctrl &= ~TIOCM_DSR;
+	} else if (info->irq < 0) {
+		struct resource r;
+
+		if (acpi_dev_resource_interrupt(ares, 0, &r))
+			info->irq = r.start;
+	}
+
+	return 1;
+}
+
+struct acpi_uart_walk {
+	struct uart_driver *drv;
+	struct device *parent;
+	unsigned int line;
+};
+
+static acpi_status acpi_uart_add_device(acpi_handle handle, u32 level,
+					void *context, void **return_value)
+{
+	struct acpi_uart_walk *walk = context;
+	struct uart_driver *drv = walk->drv;
+	struct device *parent = walk->parent;
+	unsigned int line = walk->line;
+	struct acpi_device_info *info;
+	struct uart_board_info board_info;
+	struct acpi_device *adev;
+	struct list_head resource_list;
+	int ret;
+	acpi_status status;
+	struct klist *adap = *return_value;
+	struct uart_device *udev;
+
+	BUG_ON(!parent || !adap || !drv);
+
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+	if (acpi_bus_get_status(adev) || !adev->status.present)
+		return AE_OK;
+
+	/*
+	 * Current BIOS will create physical adapter DEVICE per port.
+	 * This implementation assumes multi-port DEVICE will use _ADR
+	 * as serial port line number though there is no such explicit
+	 * proof for this assumption in the ACPI specification.
+	 *
+	 * NOTE: There is no real BIOS implementation using _ADR as line
+	 *       number.
+	 */
+	if (line >= drv->nr) {
+		status = acpi_get_object_info(handle, &info);
+		if (ACPI_FAILURE(status))
+			return AE_OK;
+		if (info->valid & ACPI_VALID_ADR)
+			line = info->address;
+		if (line >= drv->nr)
+			return AE_OK;
+	}
+
+	BUG_ON(line >= drv->nr);
+
+	memset(&board_info, 0, sizeof(board_info));
+	board_info.irq = -1;
+	board_info.line = line;
+
+	INIT_LIST_HEAD(&resource_list);
+	ret = acpi_dev_get_resources(adev, &resource_list,
+				     acpi_uart_add_resources, &board_info);
+	acpi_dev_free_resource_list(&resource_list);
+
+	if (ret < 0 || !board_info.baud)
+		return AE_OK;
+
+	strlcpy(board_info.type, dev_name(&adev->dev), sizeof(board_info.type));
+
+	udev = uart_register_device(drv, adap, parent, &board_info);
+	if (!udev) {
+		dev_err(&adev->dev, "failed to add #%d uart device from ACPI\n",
+			line);
+		return AE_OK;
+	}
+
+	return AE_OK;
+}
+
+/**
+ * acpi_uart_register_devices - enumerate the UART slave devices behind the
+ *                              physical UART adapter
+ * @drv: pointer to the low level UART driver
+ * @parent: the physical adapter device containing the port(s)
+ * @line: serial line number
+ *
+ * Enumerate all UART slave devices behind the adapter by walking the ACPI
+ * namespace. When a device is found, it will be added to the Linux device
+ * model and bound to the corresponding ACPI handle.
+ * If the physical device is a multiple port device, line should be -1.
+ * If the physical device is a single port device, line should be the line
+ * number.
+ */
+struct klist *acpi_uart_register_devices(struct uart_driver *drv,
+					 struct klist *adap,
+					 struct device *parent,
+					 unsigned int line)
+{
+	acpi_handle handle;
+	acpi_status status;
+	struct klist *klist = NULL;
+	struct acpi_uart_walk walk = {drv, parent, line};
+
+	BUG_ON(!drv || !parent);
+
+	handle = ACPI_HANDLE(parent);
+	if (!handle)
+		return NULL;
+
+	if (!adap) {
+		klist = kzalloc(sizeof(struct klist), GFP_KERNEL);
+		if (!klist)
+			return NULL;
+		(void)uart_add_adapter(klist);
+		adap = klist;
+	}
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+				     acpi_uart_add_device, NULL, &walk,
+				     (void **)&adap);
+	if (ACPI_FAILURE(status)) {
+		dev_warn(parent, "failed to enumerate UART slaves\n");
+		goto fail;
+	}
+
+	return adap;
+
+fail:
+	uart_del_adapter(klist);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(acpi_uart_register_devices);
+
+void acpi_uart_unregister_devices(struct klist *adap)
+{
+	uart_del_adapter(adap);
+	kfree(adap);
+}
+EXPORT_SYMBOL_GPL(acpi_uart_unregister_devices);
diff --git a/include/linux/acpi_uart.h b/include/linux/acpi_uart.h
new file mode 100644
index 0000000..512f047
--- /dev/null
+++ b/include/linux/acpi_uart.h
@@ -0,0 +1,40 @@
+/*
+ * acpi_uart.h - ACPI UART enumeration support
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * 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; version 2
+ * of the License.
+ */
+
+#ifndef _LINUX_ACPI_UART_H
+#define _LINUX_ACPI_UART_H
+
+struct uart_driver;
+
+#define ACPI_UART_LINE_UNKNOWN	((unsigned int)-1)
+
+#if IS_ENABLED(CONFIG_ACPI_UART)
+struct klist *acpi_uart_register_devices(struct uart_driver *drv,
+					 struct klist *adap,
+					 struct device *parent,
+					 unsigned int line);
+void acpi_uart_unregister_devices(struct klist *adap);
+#else
+static inline struct klist *acpi_uart_register_devices(struct uart_driver *drv,
+						       struct klist *adap,
+						       struct device *parent,
+						       unsigned int line)
+{
+	return NULL;
+}
+
+static inline void acpi_uart_unregister_devices(struct klist *adap)
+{
+}
+#endif
+
+#endif /* _LINUX_ACPI_UART_H */
-- 
1.7.10


^ permalink raw reply related

* [RFC PATCH 1/3] UART: Add UART subsystem as a bus.
From: Lv Zheng @ 2012-12-03  3:39 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox
  Cc: linux-acpi, linux-serial, Lv Zheng
In-Reply-To: <cover.1354505471.git.lv.zheng@intel.com>

Tranditional UARTs are used as communication pipes between the hosts
while the modern computing systems equipped with HSU (High Speed UARTs)
would connect on-board target devices using the UART ports. The role of
the UART controllers are changed from the communication facility to the
platform bus.

In the recent ACPI 5.0 specification updates, firmwares are provided the
possibilities to enumerate the UART target devices known to the platform
vendors.
Thus there is the needs for enumerating the UART target devices:
1. hotplug uevent
2. serial configuration
Currently, only serial cards on the specific bus (ex. PCMCIA) can be
enumerated and userspace can obtain the hotplug event of the UART target
devices. Linux kernel is lack of an overall enumeration mechanism for
UART target devices.
In order to send uevent, a device need to be a class device or a bus
device. This patch introduces a bus_type subsystem to manage the new
UART target device type for the purpose of the possible future
extensions.
When the UART target devices are created, userspace uevent rules can
pass the creation details to the userspace driver managers
(ex. hciattach). Here is an example of the uevents and exported
attributes of the new type of devices:

Test DSDT (dummy UART host adapter INTF000 and target device INTF001):
  Device (UA00)
  {
      Name (_HID, "INTF000")  // _HID: Hardware ID
      Name (RBUF, ResourceTemplate ()
      {
          Memory32Fixed (ReadWrite,
              0x00000000,         // Address Base
              0x00001000)         // Address Length
      })
      Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
      {
          Return (RBUF)
      }
      Method (_STA, 0, NotSerialized)  // _STA: Status
      {
          Return (0x0F)
      }
      Device (BTH0)
      {
          Name (_HID, "INTF001")  // _HID: Hardware ID
          Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
          {
              Name (UBUF, ResourceTemplate ()
              {
                  UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne,
                      0xC0, LittleEndian, ParityTypeNone, FlowControlHardware,
                      0x0020, 0x0020, "\\_SB.PCI0.UA00",
                      0x00, ResourceConsumer, ,
                      )
              })
              Return (UBUF)
          }
          Method (_STA, 0, NotSerialized)  // _STA: Status
          {
              Return (0x0F)
          }
      }
  }

uevent and environments:
KERNEL[252.443458] add      /bus/uart (bus)
ACTION=add
DEVPATH=/bus/uart
SEQNUM=1142
SUBSYSTEM=bus

KERNEL[268.491709] add      /devices/platform/INTF000:00/INTF001:00 (uart)
ACTION=add
DEVPATH=/devices/platform/INTF000:00/INTF001:00
DEVTYPE=uart_device
MODALIAS=uart:INTF001:00
SEQNUM=1144
SUBSYSTEM=uart

kobject attribute files:
# cat /sys/bus/uart/devices/INTF001:00/modalias
uart:INTF001:00
# cat /sys/bus/uart/devices/INTF001:00/tty_dev
ttyS0
# cat /sys/bus/uart/devices/INTF001:00/tty_attrs
115200 8N0 HW
# cat /sys/bus/uart/devices/INTF001:00/modem_lines
LE:RTS,CTS,

kobject sysfs links:
# ls -l /sys/bus/uart/devices
INTF001:00 -> ../../../devices/platform/INTF000:00/INTF001:00
# ls -l /sys/devices/platform/INTF000:00/INTF001:00
subsystem -> ../../../../bus/uart
host_node -> ../tty/ttyS0
# ls -l /sys/devices/platform/INTF000:00/tty/ttyS0
target_node -> ../../INTF001:00

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/tty/serial/Makefile     |    2 +-
 drivers/tty/serial/serial_bus.c |  409 +++++++++++++++++++++++++++++++++++++++
 include/linux/mod_devicetable.h |    5 +
 include/linux/serial_core.h     |   56 ++++++
 4 files changed, 471 insertions(+), 1 deletion(-)
 create mode 100644 drivers/tty/serial/serial_bus.c

diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 4f694da..4400521 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -2,7 +2,7 @@
 # Makefile for the kernel serial device drivers.
 #
 
-obj-$(CONFIG_SERIAL_CORE) += serial_core.o
+obj-$(CONFIG_SERIAL_CORE) += serial_core.o serial_bus.o
 obj-$(CONFIG_SERIAL_21285) += 21285.o
 
 # These Sparc drivers have to appear before others such as 8250
diff --git a/drivers/tty/serial/serial_bus.c b/drivers/tty/serial/serial_bus.c
new file mode 100644
index 0000000..a6674da
--- /dev/null
+++ b/drivers/tty/serial/serial_bus.c
@@ -0,0 +1,409 @@
+/*
+ * serial_bus.c - UART bus implementation
+ *
+ * Copyright (c) 2012, Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ *
+ * 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; version 2
+ * of the License.
+ */
+
+/*
+ * Tranditional UARTs are used as communication pipes between the hosts
+ * while the modern computing systems equipped with HSU (High Speed UARTs)
+ * would connect on-board target devices using the UART ports. The role of
+ * the UART controllers are changed from the communication facility to the
+ * platform bus.
+ *
+ * UART target devices are created in the kernel as struct uart_device.
+ * It is defined for the following purposes:
+ * 1. Sending hotplug notifications to the userspace
+ * 2. Exporting serial configuration parameters to the userspace
+ * 3. Allowing target device based PM to be added easily
+ *
+ */
+
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+
+struct uart_match {
+	dev_t devt;
+};
+
+static int do_uart_tty_find(struct device *dev, void *data)
+{
+	struct uart_match *match = data;
+	return dev->devt == match->devt;
+}
+
+/**
+ * uart_tty_find - find the associated TTY device for the UART target
+ *		   device
+ * @drv: the low level UART driver
+ * @dev: the parent physical device for the TTY devices
+ * @line: the line number of the UART target device
+ *
+ * Return a matching TTY device for the UART target device.
+ */
+struct device *uart_tty_find(struct uart_driver *drv,
+			     struct device *dev, unsigned int line)
+{
+	struct uart_match match;
+
+	match.devt = MKDEV(drv->tty_driver->major,
+			   drv->tty_driver->minor_start + line);
+
+	return device_find_child(dev, &match, do_uart_tty_find);
+}
+EXPORT_SYMBOL_GPL(uart_tty_find);
+
+/**
+ * uart_tty_name - get the associated TTY device name for the UART target
+ *		   device
+ * @drv: the low level UART driver
+ * @line: the line number of the UART target device
+ * @p: pointer to the buffer containing the returned name
+ *
+ * Return a TTY device name for the UART target device.
+ */
+void uart_tty_name(struct uart_driver *driver, int line, char *p)
+{
+	struct tty_driver *drv;
+
+	BUG_ON(!driver || !driver->tty_driver || !p);
+	drv = driver->tty_driver;
+
+	if (drv->flags & TTY_DRIVER_UNNUMBERED_NODE)
+		strcpy(p, drv->name);
+	else
+		sprintf(p, "%s%d", drv->name, line + drv->name_base);
+}
+EXPORT_SYMBOL_GPL(uart_tty_name);
+
+static ssize_t uart_dev_show_name(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s\n", udev->name);
+}
+
+static ssize_t uart_dev_show_modalias(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s%s\n", UART_MODULE_PREFIX, udev->name);
+}
+
+static ssize_t uart_dev_show_tty_dev(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	return sprintf(buf, "%s\n", udev->tty_name);
+}
+
+static ssize_t uart_dev_show_tty_attrs(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	int len = 0;
+
+	/* baud rate */
+	len += sprintf(buf+len, "%d ", udev->baud);
+
+	/* data bits */
+	switch (udev->cflag & CSIZE) {
+	case CS5:
+		len += sprintf(buf+len, "5");
+		break;
+	case CS6:
+		len += sprintf(buf+len, "6");
+		break;
+	case CS7:
+		len += sprintf(buf+len, "7");
+		break;
+	case CS8:
+	default:
+		len += sprintf(buf+len, "8");
+		break;
+	}
+
+	/* parity */
+	if (udev->cflag & PARODD)
+		len += sprintf(buf+len, "O");
+	else if (udev->cflag & PARENB)
+		len += sprintf(buf+len, "E");
+	else
+		len += sprintf(buf+len, "N");
+
+	/* stop bits */
+	len += sprintf(buf+len, "%d", udev->cflag & CSTOPB ? 1 : 0);
+
+	/* HW/SW control */
+	if (udev->cflag & CRTSCTS)
+		len += sprintf(buf+len, " HW");
+	if ((udev->iflag & (IXON|IXOFF|IXANY)) != 0)
+		len += sprintf(buf+len, " SW");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static ssize_t uart_dev_show_modem_lines(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct uart_device *udev = to_uart_device(dev);
+	int len = 0;
+
+	/* endian */
+	if (udev->mctrl & TIOCM_LE)
+		len += sprintf(buf+len, "LE:");
+	else
+		len += sprintf(buf+len, "BE:");
+
+	/* terminal lines */
+	if (udev->mctrl & TIOCM_DTR)
+		len += sprintf(buf+len, "DTR,");
+	if (udev->mctrl & TIOCM_RTS)
+		len += sprintf(buf+len, "RTS,");
+
+	/* modem lines */
+	if (udev->mctrl & TIOCM_CTS)
+		len += sprintf(buf+len, "CTS,");
+	if (udev->mctrl & TIOCM_CAR)
+		len += sprintf(buf+len, "CAR,");
+	if (udev->mctrl & TIOCM_RNG)
+		len += sprintf(buf+len, "RNG,");
+	if (udev->mctrl & TIOCM_DSR)
+		len += sprintf(buf+len, "DSR,");
+
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static DEVICE_ATTR(name, S_IRUGO, uart_dev_show_name, NULL);
+static DEVICE_ATTR(modalias, S_IRUGO, uart_dev_show_modalias, NULL);
+static DEVICE_ATTR(tty_dev, S_IRUGO, uart_dev_show_tty_dev, NULL);
+static DEVICE_ATTR(tty_attrs, S_IRUGO, uart_dev_show_tty_attrs, NULL);
+static DEVICE_ATTR(modem_lines, S_IRUGO, uart_dev_show_modem_lines, NULL);
+
+static struct attribute *uart_dev_attrs[] = {
+	&dev_attr_name.attr,
+	/* coldplug: modprobe $(cat .../modalias) */
+	&dev_attr_modalias.attr,
+	&dev_attr_tty_dev.attr,
+	&dev_attr_tty_attrs.attr,
+	&dev_attr_modem_lines.attr,
+	NULL,
+};
+
+static struct attribute_group uart_dev_attr_group = {
+	.attrs	= uart_dev_attrs,
+};
+
+static const struct attribute_group *uart_dev_attr_groups[] = {
+	&uart_dev_attr_group,
+	NULL,
+};
+
+#ifdef CONFIG_HOTPLUG
+static int uart_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct uart_device *udev = to_uart_device(dev);
+
+	if (add_uevent_var(env, "MODALIAS=%s%s",
+			   UART_MODULE_PREFIX, udev->name))
+		return -ENOMEM;
+
+	dev_dbg(dev, "uevent\n");
+	return 0;
+}
+#else
+#define uart_device_uevent	NULL
+#endif
+
+static void uart_device_release(struct device *dev)
+{
+	struct uart_device *udev = to_uart_device(dev);
+
+	put_device(udev->tty);
+	kfree(udev);
+}
+
+struct device_type uart_device_type = {
+	.name		= "uart_device",
+	.groups		= uart_dev_attr_groups,
+	.uevent		= uart_device_uevent,
+	.release	= uart_device_release,
+};
+EXPORT_SYMBOL_GPL(uart_device_type);
+
+/**
+ * uart_register_device - instantiate a UART device
+ * @drv: low level driver structure
+ * @adap: pointer to the UART target device manager
+ * @device: the physical device communicating with the target device
+ * @info: describes one UART target
+ *
+ * Create a UART target device.
+ *
+ * This returns the new UART target device, which may be saved for later use
+ * with uart_unregister_device; or NULL to indicate an error.
+ */
+struct uart_device *uart_register_device(struct uart_driver *drv,
+					 struct klist *adap,
+					 struct device *dev,
+					 struct uart_board_info const *info)
+{
+	struct uart_device *udev;
+	struct device *tty;
+	int status;
+
+	BUG_ON(info->line >= drv->nr);
+	BUG_ON((!adap && !dev) || !drv);
+
+	udev = kzalloc(sizeof(struct uart_device), GFP_KERNEL);
+	if (!udev)
+		return NULL;
+
+	strlcpy(udev->name, info->type, sizeof(udev->name));
+
+	udev->baud = info->baud;
+	udev->cflag = info->cflag;
+	udev->iflag = info->iflag;
+	udev->mctrl = info->mctrl;
+
+	udev->dev.parent = dev;
+	udev->dev.bus = &uart_bus_type;
+	udev->dev.type = &uart_device_type;
+
+	tty = uart_tty_find(drv, dev, info->line);
+	if (!tty) {
+		dev_err(&udev->dev, "Cannot find associated tty device.\n");
+		goto fail;
+	}
+	udev->tty = get_device(tty);
+	uart_tty_name(drv, info->line, udev->tty_name);
+
+	dev_set_name(&udev->dev, "%s", udev->name);
+	status = device_register(&udev->dev);
+	if (status) {
+		dev_err(&udev->dev, "Failed to register uart device.\n");
+		goto fail2;
+	}
+
+	status = sysfs_create_link(&tty->kobj, &udev->dev.kobj, "target_node");
+	status = sysfs_create_link(&udev->dev.kobj, &tty->kobj, "host_node");
+
+	if (adap) {
+		udev->adap = adap;
+		klist_add_tail(&udev->klist_parent, adap);
+	}
+
+	return udev;
+
+fail2:
+	put_device(tty);
+fail:
+	kfree(udev);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(uart_register_device);
+
+/**
+ * uart_unregister_device - unregister a UART device
+ * @device: value returned from uart_register_device()
+ *
+ * Reverse effect of uart_register_device().
+ */
+void uart_unregister_device(struct uart_device *udev)
+{
+	if (udev->adap)
+		klist_del(&udev->klist_parent);
+	sysfs_remove_link(&udev->dev.kobj, "host_node");
+	if (udev->tty)
+		sysfs_remove_link(&udev->tty->kobj, "target_node");
+	device_unregister(&udev->dev);
+}
+EXPORT_SYMBOL_GPL(uart_unreigster_device);
+
+static void klist_uart_get(struct klist_node *n)
+{
+	struct uart_device *udev = uart_device_from_parent(n);
+	get_device(&udev->dev);
+}
+
+static void klist_uart_put(struct klist_node *n)
+{
+	struct uart_device *udev = uart_device_from_parent(n);
+	put_device(&udev->dev);
+}
+
+static struct uart_device *uart_next_device(struct klist_iter *i)
+{
+	struct klist_node *n = klist_next(i);
+	return n ? uart_device_from_parent(n) : NULL;
+}
+
+/**
+ * uart_add_adapter - register a UART adapter
+ * @adap: pointer to the device manager
+ *
+ * Initialize a UART target device manager - adapter.
+ */
+int uart_add_adapter(struct klist *adap)
+{
+	klist_init(adap, klist_uart_get, klist_uart_put);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(uart_add_adapter);
+
+/**
+ * uart_del_adapter - unregister a UART adapter
+ * @adap: pointer to the device manager
+ *
+ * Reverse effect of uart_add_adapter().
+ */
+void uart_del_adapter(struct klist *adap)
+{
+	struct klist_iter i;
+	struct uart_device *udev;
+
+	klist_iter_init(adap, &i);
+	while ((udev = uart_next_device(&i)))
+		uart_unregister_device(udev);
+	klist_iter_exit(&i);
+}
+EXPORT_SYMBOL_GPL(uart_del_adapter);
+
+struct bus_type uart_bus_type = {
+	.name		= "uart",
+};
+EXPORT_SYMBOL_GPL(uart_bus_type);
+
+static int __init uart_bus_init(void)
+{
+	int retval;
+
+	retval = bus_register(&uart_bus_type);
+	if (retval)
+		return retval;
+
+	return 0;
+}
+
+static void __exit uart_bus_exit(void)
+{
+	bus_unregister(&uart_bus_type);
+}
+
+subsys_initcall(uart_bus_init);
+module_exit(uart_bus_exit);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index fed3def..28df140 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -455,6 +455,11 @@ struct spi_device_id {
 			__attribute__((aligned(sizeof(kernel_ulong_t))));
 };
 
+/* uart */
+
+#define UART_NAME_SIZE	32
+#define UART_MODULE_PREFIX "uart:"
+
 /* dmi */
 enum dmi_field {
 	DMI_NONE,
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 3c43022..422f8cc4 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -31,10 +31,13 @@
 #include <linux/sysrq.h>
 #include <linux/pps_kernel.h>
 #include <uapi/linux/serial_core.h>
+#include <linux/mod_devicetable.h>
 
 struct uart_port;
+struct uart_device;
 struct serial_struct;
 struct device;
+struct uart_board_info;
 
 /*
  * This structure describes all the operations that can be
@@ -367,4 +370,57 @@ static inline int uart_handle_break(struct uart_port *port)
 					 (cflag) & CRTSCTS || \
 					 !((cflag) & CLOCAL))
 
+/*
+ * UART Bus
+ */
+struct uart_board_info {
+	char type[UART_NAME_SIZE];
+	unsigned int line;	/* port index */
+	unsigned int cflag;	/* termio cflag */
+	unsigned int iflag;	/* termio iflag */
+	unsigned int mctrl;	/* modem ctrl settings */
+	unsigned int baud;
+	int irq;
+};
+
+extern struct bus_type uart_bus_type;
+
+int uart_add_adapter(struct klist *adap);
+void uart_del_adapter(struct klist *adap);
+
+struct uart_device {
+	char			name[UART_NAME_SIZE];
+	char			tty_name[64];
+	unsigned int		cflag;	/* termio cflag */
+	unsigned int		iflag;	/* termio iflag */
+	unsigned int		mctrl;	/* modem ctrl settings */
+	unsigned int		baud;
+	struct device		dev;
+	struct device		*tty;
+	struct klist_node	klist_parent;
+	struct klist		*adap;	/* set for multi-port adapter */
+};
+
+extern struct device_type uart_device_type;
+
+#define is_uart_device(d) ((d) && (d)->type == &uart_device_type)
+#define to_uart_device(d) container_of(d, struct uart_device, dev)
+#define uart_device_from_parent(n)	\
+	container_of(n, struct uart_device, klist_parent)
+
+static inline struct uart_device *uart_verify_device(struct device *dev)
+{
+	return is_uart_device(dev) ? to_uart_device(dev) : NULL;
+}
+
+struct uart_device *uart_register_device(struct uart_driver *drv,
+					 struct klist *adap,
+					 struct device *dev,
+					 struct uart_board_info const *info);
+void uart_unregister_device(struct uart_device *udev);
+
+struct device *uart_tty_find(struct uart_driver *drv,
+			     struct device *dev, unsigned int line);
+void uart_tty_name(struct uart_driver *driver, int index, char *p);
+
 #endif /* LINUX_SERIAL_CORE_H */
-- 
1.7.10


^ permalink raw reply related

* [RFC PATCH 0/3] ACPI/UART: Add ACPI 5.0 enueration support for UART.
From: Lv Zheng @ 2012-12-03  3:39 UTC (permalink / raw)
  To: Len Brown, Rafael J Wysocki, Greg Kroah-Hartman, Alan Cox
  Cc: linux-acpi, linux-serial, Lv Zheng

ACPI 5.0 specification introduces enumeration support for SPB buses. This
patch set adds the UART serial bus enumeration support to Linux using such
mechanism.

NOTE: The [PATCH 3/3] is only for the demonstration purpose and should not
      be merged into any of the published Linux source tree.

Lv Zheng (3):
  UART: Add UART subsystem as a bus.
  ACPI / UART: Add ACPI enumeration support for UART bus.
  UART: Add dummy devices to test the enumeration.

 drivers/acpi/Kconfig                 |    7 +
 drivers/acpi/Makefile                |    1 +
 drivers/acpi/acpi_uart.c             |  255 ++++++++++++++++++++
 drivers/acpi/scan.c                  |    1 +
 drivers/tty/serial/8250/8250.c       |   10 +-
 drivers/tty/serial/8250/8250_dummy.c |  128 ++++++++++
 drivers/tty/serial/8250/Kconfig      |   10 +
 drivers/tty/serial/8250/Makefile     |    1 +
 drivers/tty/serial/Makefile          |    2 +-
 drivers/tty/serial/serial_bus.c      |  426 ++++++++++++++++++++++++++++++++++
 include/linux/acpi_uart.h            |   40 ++++
 include/linux/mod_devicetable.h      |    5 +
 include/linux/serial_8250.h          |    2 +
 include/linux/serial_core.h          |   59 +++++
 14 files changed, 944 insertions(+), 3 deletions(-)
 create mode 100644 drivers/acpi/acpi_uart.c
 create mode 100644 drivers/tty/serial/8250/8250_dummy.c
 create mode 100644 drivers/tty/serial/serial_bus.c
 create mode 100644 include/linux/acpi_uart.h

-- 
1.7.10


^ permalink raw reply

* Re: [PATCH] synclink fix ldisc buffer argument
From: Chen Gang @ 2012-12-03  2:20 UTC (permalink / raw)
  To: Paul Fulghum; +Cc: Alan Cox, Greg KH, Linux Kernel Mailing List, linux-serial
In-Reply-To: <989CB961-79F8-479B-B16C-41358A60AC94@microgate.com>

于 2012年12月03日 04:05, Paul Fulghum 写道:
> OK, I’ll do that.
> 

pardon (I am just learning)
  does 65535 mean HDLC_MAX_FRAME_SIZE ?
  why do we need info->max_frame_size >= 4096 ?

in drivers/tty/synclink_gt.c:
3550         if (info->max_frame_size < 4096)
3551                 info->max_frame_size = 4096;
3552         else if (info->max_frame_size > 65535)
3553                 info->max_frame_size = 65535;
3554
 ...
3603                 info->max_frame_size = 4096;


if possible:
  can we move the relative comments (which are inside function) to the
location just above ldisc_receive_buf ?


  thanks.

gchen.


> On Dec 2, 2012, at 12:10 PM, Alan Cox <alan@lxorguk.ukuu.org.uk
> <mailto:alan@lxorguk.ukuu.org.uk>> wrote:
> 
>> On Sun, 2 Dec 2012 10:11:58 -0600
>> Paul Fulghum <paulkf@microgate.com <mailto:paulkf@microgate.com>> wrote:
>>
>>> True, in this mode line disciplines other than N_HDLC would not be
>>> functional and N_HDLC ignores the flag buffer.
>>> This change won’t make other line disciplines useful, it will just
>>> prevent the case of a mistakenly selected line discipline accessing
>>> beyond the end of the (dummy) flag buffer.
>>>
>>> I’m fine with or without the change. It is functional now with a
>>> chance to read past then end of a buffer if misconfigured. With the
>>> change, it has the same functionality without the ability to read
>>> past the end of a buffer if misconfigured.
>>
>> With the change its feeding crap in the flags buffer, which may matter in
>> future depending what happens to the other bits.
>>
>> If this is a real issue far better to just kzalloc a blank flag buffer to
>> match the mtu.
>>
> 
> -- 
> Paul Fulghum
> MicroGate Systems, Ltd.
> =Customer Driven, by Design=
> (800)444-1982
> (512)345-7791 (Direct)
> (512)343-9046 (Fax)
> Central Time Zone (GMT -5h)
> www.microgate.com <http://www.microgate.com/>
> 


-- 
Chen Gang

Asianux Corporation

^ permalink raw reply

* Re: flush_to_ldisc accesses tty after free  (was: [PATCH 21/21] TTY: move tty buffers to tty_port)
From: Peter Hurley @ 2012-12-02 19:57 UTC (permalink / raw)
  To: Jiri Slaby, Jiri Slaby, alan
  Cc: gregkh, linux-kernel, Dave Jones, Sasha Levin, linux-serial
In-Reply-To: <1354392383.2531.118.camel@thor>

[whoops... cc: linux-serial]

On Sat, 2012-12-01 at 15:06 -0500, Peter Hurley wrote:
> On Sat, 2012-12-01 at 09:59 -0500, Peter Hurley wrote:
> ....
> > From instrumenting the tty_release() path, it's clear that tty_buffer
> > work is still scheduled even after tty_release_ldisc() has run. For
> > example, with this patch I get the warning below it.
> > 
> > [Further analysis to follow in subsequent mail...]
> 
> [ Please note: this analysis only refers to the pty driver. The
> situation with hardware drivers has further complications.]
> 
> Firstly, this problem predates Jiri's changes; only because he was
> cautious by checking the lifetime of the itty in flush_to_ldisc(), did
> he uncover this existing problem.
> 
> One example of how it is possible for buffer work to be scheduled even
> after tty_release_ldisc() stems from how tty_ldisc_halt() works (or
> rather doesn't). (I've snipped out the relevant code from tty_ldisc.c
> for annotation below.)

Naturally, I found the least obvious problem first.

The more obvious problem is that the pty driver doesn't have an ldisc
reference to the 'other' tty when pty_write() is called. So doing the
tty_flip_buffer_push() has scheduled buffer work for a potentially
halted ldisc.

static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
{
	struct tty_struct *to = tty->link;	<==== this is the 'other' tty

	if (tty->stopped)
		return 0;

	if (c > 0) {
		/* Stuff the data into the input queue of the other end */
		c = tty_insert_flip_string(to, buf, c);
		/* And shovel */
		if (c) {
			tty_flip_buffer_push(to);
			tty_wakeup(tty);
		}
	}
	return c;
}

There are several possible ways to fix this:
1. Halt both ldiscs and ensure that both ldiscs have no outstanding
references before cancelling their work.
2. Claim an ldisc reference for the 'other' ldisc in things like
tty_write().
3. I'm sure there's other ways....

Regards,
Peter Hurley


^ permalink raw reply

* Re: [PATCH] synclink fix ldisc buffer argument
From: Alan Cox @ 2012-12-02 18:10 UTC (permalink / raw)
  To: Paul Fulghum; +Cc: Greg KH, Chen Gang, Linux Kernel Mailing List, linux-serial
In-Reply-To: <F6B8A325-7DBF-4623-B16C-CDC5642EFD16@microgate.com>

On Sun, 2 Dec 2012 10:11:58 -0600
Paul Fulghum <paulkf@microgate.com> wrote:

> True, in this mode line disciplines other than N_HDLC would not be functional and N_HDLC ignores the flag buffer.
> This change won’t make other line disciplines useful, it will just prevent the case of a mistakenly selected line discipline accessing beyond the end of the (dummy) flag buffer.
> 
> I’m fine with or without the change. It is functional now with a chance to read past then end of a buffer if misconfigured. With the change, it has the same functionality without the ability to read past the end of a buffer if misconfigured.

With the change its feeding crap in the flags buffer, which may matter in
future depending what happens to the other bits.

If this is a real issue far better to just kzalloc a blank flag buffer to
match the mtu.
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] tty: serial: vt8500: fix return value check in vt8500_serial_probe()
From: Tony Prisk @ 2012-12-02 17:47 UTC (permalink / raw)
  To: Wei Yongjun
  Cc: alan, gregkh, grant.likely, rob.herring, yongjun_wei,
	linux-arm-kernel, linux-serial, devicetree-discuss
In-Reply-To: <CAPgLHd_gx3BmZh+L1k6koa6eH5SKKEVFedSC_M=nju+Dzq=rug@mail.gmail.com>

On Sun, 2012-12-02 at 05:10 -0500, Wei Yongjun wrote:
> From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
> 
> In case of error, function of_clk_get() returns ERR_PTR()
> and never returns NULL. The NULL test in the return value
> check should be replaced with IS_ERR().
> 
> Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
> ---
>  drivers/tty/serial/vt8500_serial.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
> index 205d4cf..f528cc2 100644
> --- a/drivers/tty/serial/vt8500_serial.c
> +++ b/drivers/tty/serial/vt8500_serial.c
> @@ -604,7 +604,7 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
>  	vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
>  
>  	vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
> -	if (vt8500_port->clk) {
> +	if (!IS_ERR(vt8500_port->clk)) {
>  		vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);
>  	} else {
>  		/* use the default of 24Mhz if not specified and warn */
> 

Acked-by: Tony Prisk <linux@prisktech.co.nz>


^ permalink raw reply

* Re: [PATCH] synclink fix ldisc buffer argument
From: Alan Cox @ 2012-12-02 15:13 UTC (permalink / raw)
  To: Paul Fulghum; +Cc: Greg KH, Chen Gang, Linux Kernel Mailing List, linux-serial
In-Reply-To: <50B90D0D.9040401@microgate.com>

> +				 * If a different line discipline is selected by mistake it
> +				 * will have valid memory for both arguments.
> +				 */
> +				ldisc_receive_buf(tty, buf->data, buf->data, framesize);

But not valid content it seems 

^ permalink raw reply

* [PATCH -next] serial: xilinx_uartps: fix return value check in xuartps_probe()
From: Wei Yongjun @ 2012-12-02 10:12 UTC (permalink / raw)
  To: alan, gregkh, jslaby, grant.likely, rob.herring
  Cc: yongjun_wei, linux-serial, devicetree-discuss

From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>

In case of error, function of_clk_get() returns ERR_PTR()
and never returns NULL. The NULL test in the return value
check should be replaced with IS_ERR().

Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
---
 drivers/tty/serial/xilinx_uartps.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 2be22a2..1eb4657 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -948,9 +948,9 @@ static int xuartps_probe(struct platform_device *pdev)
 	struct clk *clk;
 
 	clk = of_clk_get(pdev->dev.of_node, 0);
-	if (!clk) {
+	if (IS_ERR(clk)) {
 		dev_err(&pdev->dev, "no clock specified\n");
-		return -ENODEV;
+		return PTR_ERR(clk);
 	}
 
 	rc = clk_prepare_enable(clk);



^ permalink raw reply related

* [PATCH] tty: serial: vt8500: fix return value check in vt8500_serial_probe()
From: Wei Yongjun @ 2012-12-02 10:10 UTC (permalink / raw)
  To: linux, alan, gregkh, grant.likely, rob.herring
  Cc: yongjun_wei, linux-arm-kernel, linux-serial, devicetree-discuss

From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>

In case of error, function of_clk_get() returns ERR_PTR()
and never returns NULL. The NULL test in the return value
check should be replaced with IS_ERR().

Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
---
 drivers/tty/serial/vt8500_serial.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 205d4cf..f528cc2 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -604,7 +604,7 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
 	vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
 
 	vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
-	if (vt8500_port->clk) {
+	if (!IS_ERR(vt8500_port->clk)) {
 		vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);
 	} else {
 		/* use the default of 24Mhz if not specified and warn */



^ permalink raw reply related

* [Suggestion] drivers/tty/serial: sprintf for filename[32].
From: Chen Gang @ 2012-12-01 10:01 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-serial

Hello Alan Cox:

in drives/tty/serial/ucc_uart.c:
  at line 1231, the filename buffer length is 32.
  at line 1242, "fsl_qe_ucode_uart_%u_%u%u.bin" is 23 + 3*%u.
  at line 1237, after call soc_info. soc within 4 bytes, rev_h within 2 bytes, rev_l within 2 bytes.

my suggestion is:
  use "fsl_qe_ucode_uart_%.4u_%.2u%.2u.bin" instead of "fsl_qe_ucode_uart_%u_%u%u.bin";
  or use "char filename[64]" instead of "char filename[32]".
  please give your choice (or provide new choice).

  it is not a bug, it is only to make source code clearer or extensible.

  thanks.


1231                 } else {
1232                         char filename[32];
1233                         unsigned int soc;
1234                         unsigned int rev_h;
1235                         unsigned int rev_l;
1236 
1237                         soc = soc_info(&rev_h, &rev_l);
1238                         if (!soc) {
1239                                 dev_err(&ofdev->dev, "unknown CPU model\n");
1240                                 return -ENXIO;
1241                         }
1242                         sprintf(filename, "fsl_qe_ucode_uart_%u_%u%u.bin",
1243                                 soc, rev_h, rev_l);
1244 
1245                         dev_info(&ofdev->dev, "waiting for firmware %s\n",
1246                                 filename);
1247 



-- 
Chen Gang

Asianux Corporation

^ permalink raw reply

* Re: [Suggestion] drivers/tty: drivers/char/:  for MAX_ASYNC_BUFFER_SIZE
From: Chen Gang @ 2012-12-01  9:01 UTC (permalink / raw)
  To: Paul Fulghum
  Cc: Greg KH, linux-kernel@vger.kernel.org, linux-serial, Alan Cox
In-Reply-To: <50B8DDAC.8070901@microgate.com>

于 2012年12月01日 00:24, Paul Fulghum 写道:
> My suggestion is to leave it as is for now until I can make
> those changes. I admit the current code is ugly enough to
> cause confusion (sorry Chen Gang), but I don't see any immediate danger.
> 

  do not need 'sorry', learn with each other. (I am just learning
through read kernel source code -- "code review")

  I am glad to know that my suggestion is useful (although it seems a
minor suggestion).  for me, it is enough.

  :-)

  thanks.

-- 
Chen Gang

Asianux Corporation
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] synclink fix ldisc buffer argument
From: Paul Fulghum @ 2012-11-30 19:46 UTC (permalink / raw)
  To: Greg KH; +Cc: Chen Gang, Linux Kernel Mailing List, linux-serial, Alan Cox
In-Reply-To: <50B8DDAC.8070901@microgate.com>

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. Calls are changed to use data buffer argument for
both data and flag buffer so valid memory is provided if the wrong
line discipline is used. Unused char_buf and flag_buf are removed.

Signed-off-by: Paul Fulghum <paulkf@microgate.com>


--- a/drivers/char/pcmcia/synclink_cs.c	2012-11-26 14:15:45.000000000 -0600
+++ b/drivers/char/pcmcia/synclink_cs.c	2012-11-30 12:50:23.000000000 -0600
@@ -210,7 +210,6 @@ typedef struct _mgslpc_info {
 	char testing_irq;
 	unsigned int init_error;	/* startup error (DIAGS)	*/
 
-	char flag_buf[MAX_ASYNC_BUFFER_SIZE];
 	bool drop_rts_on_tx_done;
 
 	struct	_input_signal_events	input_signal_events;
@@ -3707,7 +3706,16 @@ static bool rx_get_frame(MGSLPC_INFO *in
 				hdlcdev_rx(info, buf->data, framesize);
 			else
 #endif
-				ldisc_receive_buf(tty, buf->data, info->flag_buf, framesize);
+			{
+				/*
+				 * Call N_HDLC line discipline directly to maintain
+				 * frame boundaries. Reuse the data buffer argument for the
+				 * flag buffer argument. The flag buffer is ignored by N_HDLC.
+				 * If a different line discipline is selected by mistake it
+				 * will have valid memory for both arguments.
+				 */
+				ldisc_receive_buf(tty, buf->data, buf->data, framesize);
+			}
 		}
 	}
 
--- a/drivers/tty/synclink.c	2012-11-26 14:15:45.000000000 -0600
+++ b/drivers/tty/synclink.c	2012-11-30 12:59:29.000000000 -0600
@@ -291,8 +291,6 @@ 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];	
 	bool drop_rts_on_tx_done;
 
 	bool loopmode_insert_requested;
@@ -6661,7 +6659,17 @@ static bool mgsl_get_rx_frame(struct mgs
 				hdlcdev_rx(info,info->intermediate_rxbuffer,framesize);
 			else
 #endif
-				ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+			{
+				/*
+				 * Call N_HDLC line discipline directly to maintain
+				 * frame boundaries. Reuse the data buffer argument for the
+				 * flag buffer argument. The flag buffer is ignored by N_HDLC.
+				 * If a different line discipline is selected by mistake it
+				 * will have valid memory for both arguments.
+				 */
+				ldisc_receive_buf(tty, info->intermediate_rxbuffer,
+						  info->intermediate_rxbuffer, framesize);
+			}
 		}
 	}
 	/* Free the buffers used by this frame. */
@@ -6833,7 +6841,15 @@ static bool mgsl_get_raw_rx_frame(struct
 			memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize);
 			info->icount.rxok++;
 
-			ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+			/*
+			 * Call N_HDLC line discipline directly to maintain
+			 * block boundaries. Reuse the data buffer argument for the
+			 * flag buffer argument. The flag buffer is ignored by N_HDLC.
+			 * If a different line discipline is selected by mistake it
+			 * will have valid memory for both arguments.
+			 */
+			ldisc_receive_buf(tty, info->intermediate_rxbuffer,
+					   info->intermediate_rxbuffer, framesize);
 		}
 
 		/* Free the buffers used by this frame. */
--- a/drivers/tty/synclinkmp.c	2012-11-26 14:15:45.000000000 -0600
+++ b/drivers/tty/synclinkmp.c	2012-11-30 13:01:36.000000000 -0600
@@ -262,8 +262,6 @@ 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];
 	bool drop_rts_on_tx_done;
 
 	struct	_input_signal_events	input_signal_events;
@@ -4979,8 +4977,17 @@ CheckAgain:
 				hdlcdev_rx(info,info->tmp_rx_buf,framesize);
 			else
 #endif
-				ldisc_receive_buf(tty,info->tmp_rx_buf,
-						  info->flag_buf, framesize);
+			{
+				/*
+				 * Call N_HDLC line discipline directly to maintain
+				 * frame boundaries. Reuse the data buffer argument for the
+				 * flag buffer argument. The flag buffer is ignored by N_HDLC.
+				 * If a different line discipline is selected by mistake it
+				 * will have valid memory for both arguments.
+				 */
+				ldisc_receive_buf(tty, info->tmp_rx_buf,
+						  info->tmp_rx_buf, framesize);
+			}
 		}
 	}
 	/* Free the buffers used by this frame. */
--- a/drivers/tty/synclink_gt.c	2012-11-26 14:15:45.000000000 -0600
+++ b/drivers/tty/synclink_gt.c	2012-11-30 12:53:25.000000000 -0600
@@ -317,8 +317,6 @@ struct slgt_info {
 	unsigned char *tx_buf;
 	int tx_count;
 
-	char flag_buf[MAX_ASYNC_BUFFER_SIZE];
-	char char_buf[MAX_ASYNC_BUFFER_SIZE];
 	bool drop_rts_on_tx_done;
 	struct	_input_signal_events	input_signal_events;
 
@@ -4760,7 +4758,16 @@ check_again:
 				hdlcdev_rx(info,info->tmp_rbuf, framesize);
 			else
 #endif
-				ldisc_receive_buf(tty, info->tmp_rbuf, info->flag_buf, framesize);
+			{
+				/*
+				 * Call N_HDLC line discipline directly to maintain
+				 * frame boundaries. Reuse the data buffer argument for the
+				 * flag buffer argument. The flag buffer is ignored by N_HDLC.
+				 * If a different line discipline is selected by mistake it
+				 * will have valid memory for both arguments.
+				 */
+				ldisc_receive_buf(tty, info->tmp_rbuf, info->tmp_rbuf, framesize);
+			}
 		}
 	}
 	free_rbufs(info, start, end);
@@ -4793,9 +4800,17 @@ static bool rx_get_buf(struct slgt_info 
 	}
 	DBGDATA(info, info->rbufs[i].buf, count, "rx");
 	DBGINFO(("rx_get_buf size=%d\n", count));
-	if (count)
+	if (count) {
+		/*
+		 * Call N_HDLC line discipline directly to maintain
+		 * block boundaries. Reuse the data buffer argument for the
+		 * flag buffer argument. The flag buffer is ignored by N_HDLC.
+		 * If a different line discipline is selected by mistake it
+		 * will have valid memory for both arguments.
+		 */
 		ldisc_receive_buf(info->port.tty, info->rbufs[i].buf,
-				  info->flag_buf, count);
+				  info->rbufs[i].buf, count);
+	}
 	free_rbufs(info, i, i);
 	return true;
 }


^ permalink raw reply

* Re: [Suggestion] drivers/tty: drivers/char/:  for MAX_ASYNC_BUFFER_SIZE
From: Paul Fulghum @ 2012-11-30 16:24 UTC (permalink / raw)
  To: Chen Gang; +Cc: Greg KH, linux-kernel@vger.kernel.org, linux-serial, Alan Cox
In-Reply-To: <50B81F76.8020508@asianux.com>

On 11/29/2012 8:52 PM, Chen Gang wrote:
> 于 2012年11月30日 02:32, Greg KH 写道:
>> On Thu, Nov 29, 2012 at 01:57:59PM +0800, Chen Gang wrote:
>>>> And, I really don't understand here, why do you want to change this?
>>>> What is it going to change?  And why?
>>>
>>> Why:
>>>   for the context MGSLPC_INFO *info in drivers/char/pcmcia/synclink_cs.c
>>>     info->max_frame_size can be the value between 4096 .. 65535 (can be
>>> set by its module input parameter)
>>>     info->flag_buf length is 4096 (MAX_ASYNC_BUFFER_SIZE)
>>>   in function rx_get_frame
>>>     the framesize is limit by info->max_frame_size, but may still be
>>> larger that 4096.
>>>     when call function ldisc_receive_buf, info->flag_buf is equal to
>>> 4096, but framesize can be more than 4096. it will cause memory over flow.

The confusion centers on calling the line discipline receive_buf
function with a data buffer larger than the flag buffer.

The synclink drivers support asynchronous and synchronous (HDLC)
serial communications.

In asynchronous mode, the tty flip buffer is used to feed
data to the line discipline. In this mode, the above argument
does not apply. The receive_buf function is not called directly.

In synchronous mode, the driver calls the line discipline
receive_buf function directly to feed one HDLC frame
of data per call. Maintaining frame boundaries is needed
in this mode. This is done only with the N_HDLC line
discipline which expects this format and ignores the flag buffer.
The flag buffer passed is just a place holder to meet the
calling conventions of the line discipline receive_buf function.

The only danger is if:
1. driver is configured for synchronous mode
2. driver is configured for frames > 4K
3. line discipline other than N_HDLC is selected

In this case the line discipline might try to access
beyond the end of the flag buffer. This is a non-functional
configuration that would not occur on purpose.

Increasing the flag buffer size would prevent a problem
in this degenerate case of purposeful misconfiguration.
This would be at the expense of larger allocations that are
not used.

I think the correct fix is for me to change the direct
calls to pass the same buffer for both data and flag and
add a comment describing the fact the flag buffer is ignored
when using N_HDLC. That way a misconfigured setup won't
cause problems and no unneeded allocations are made.

My suggestion is to leave it as is for now until I can make
those changes. I admit the current code is ugly enough to
cause confusion (sorry Chen Gang), but I don't see any immediate danger.

-- 
Paul Fulghum
MicroGate Systems, Ltd.
=Customer Driven, by Design=
(800)444-1982 (US Sales)
(512)345-7791 x102 (Direct)
(512)343-9046 (Fax)
Central Time Zone (GMT -6h)
www.microgate.com

^ permalink raw reply

* Re: [PATCH 0/5] serial: 8250: 8250_dw changes and dynamic capabilities
From: Heikki Krogerus @ 2012-11-30 13:48 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Alan Cox, Jamie Iles, linux-serial, LKML
In-Reply-To: <1354106924-11013-1-git-send-email-heikki.krogerus@linux.intel.com>

On Wed, Nov 28, 2012 at 02:48:39PM +0200, Heikki Krogerus wrote:
> Hi,
> 
> These are mainly small changes to 8250_dw.c. The interesting patch is
> probable the addition to 8250.c that would allow the drivers to
> deliver their UART's capabilities when they are registering ports.

These don't apply on top of tty-next so I need to make version two.
I'll send it next week in case somebody has comments.

-- 
heikki

^ permalink raw reply

* Re: [Suggestion] drivers/tty: drivers/char/:  for MAX_ASYNC_BUFFER_SIZE
From: Chen Gang @ 2012-11-30  7:14 UTC (permalink / raw)
  To: Paul Fulghum
  Cc: Greg KH, linux-kernel@vger.kernel.org, linux-serial, Alan Cox
In-Reply-To: <C7D3911F-7B6B-4353-A84B-0218FAB27198@microgate.com>

于 2012年11月30日 11:27, Paul Fulghum 写道:
> 
> I’m the maintainer for these drivers. I only caught this message by
> chance and
> have not had a chance to review the entire thread and original patches.
> It’s late and I’m tired so I won’t be able to look at this until tomorrow.
> 
> I do not doubt there is a problem that needs cleaning up. I just need a
> day to
> review and make sure this does not cause any problems.

  if it is surely an issue,
    is it suitable to let Paul Fulghum to provide the relative patch ?
    for synclink, he is more expert than me.
    for test and test environments, he is also more expert than me.

  thanks.

-- 
Chen Gang

Asianux Corporation

^ permalink raw reply

* Re: [Suggestion] drivers/tty: drivers/char/:  for MAX_ASYNC_BUFFER_SIZE
From: Chen Gang @ 2012-11-30  6:28 UTC (permalink / raw)
  To: Paul Fulghum
  Cc: Greg KH, linux-kernel@vger.kernel.org, linux-serial, Alan Cox
In-Reply-To: <C7D3911F-7B6B-4353-A84B-0218FAB27198@microgate.com>

于 2012年11月30日 11:27, Paul Fulghum 写道:
> 
> I’m the maintainer for these drivers. I only caught this message by
> chance and

  it seems you are not in MAINTAINER file.
  is it suitable to add your name into MAINTAINER file ?
    (if it was, please help adding ?  I am not quite familiar with it)

  thanks.

-- 
Chen Gang

Asianux Corporation

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox