All of lore.kernel.org
 help / color / mirror / Atom feed
From: Rob Herring <robherring2@gmail.com>
To: linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Jiri Slaby <jslaby@suse.cz>,
	Catalin Marinas <catalin.marinas@arm.com>,
	Russell King <linux@arm.linux.org.uk>,
	Will Deacon <will.deacon@arm.com>,
	x86@kernel.org, Arnd Bergmann <arnd@arndb.de>,
	Rob Herring <robh@kernel.org>
Subject: [PATCH 4/8] tty/serial: add generic serial earlycon
Date: Fri, 21 Mar 2014 16:08:44 -0500	[thread overview]
Message-ID: <1395436128-11244-5-git-send-email-robherring2@gmail.com> (raw)
In-Reply-To: <1395436128-11244-1-git-send-email-robherring2@gmail.com>

From: Rob Herring <robh@kernel.org>

This introduces generic earlycon infrastructure for serial devices
based on the 8250 earlycon. This allows for supporting earlycon option
with other serial devices. The earlycon output is enabled at the time
early_params are processed.

Only architectures that have fixmap support or have functional ioremap
when early_params are processed are supported. This is the same
restriction that the 8250 driver had.

Signed-off-by: Rob Herring <robh@kernel.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.cz>
---
 drivers/tty/serial/Kconfig    |   7 ++
 drivers/tty/serial/Makefile   |   2 +
 drivers/tty/serial/earlycon.c | 148 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/serial_core.h   |  16 +++++
 4 files changed, 173 insertions(+)
 create mode 100644 drivers/tty/serial/earlycon.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 2e6d8dd..1685189 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -7,6 +7,13 @@ if TTY
 menu "Serial drivers"
 	depends on HAS_IOMEM
 
+config SERIAL_EARLYCON
+	bool "Early console for serial ports"
+	help
+	  Support for early consoles with the earlycon parameter. This enables
+	  the console before standard serial driver is probed. The console is
+	  enabled when early_param is processed.
+
 source "drivers/tty/serial/8250/Kconfig"
 
 comment "Non-8250 serial port support"
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 3680854..8af1415 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -5,6 +5,8 @@
 obj-$(CONFIG_SERIAL_CORE) += serial_core.o
 obj-$(CONFIG_SERIAL_21285) += 21285.o
 
+obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
+
 # These Sparc drivers have to appear before others such as 8250
 # which share ttySx minor node space.  Otherwise console device
 # names change and other unplesantries.
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
new file mode 100644
index 0000000..241757a
--- /dev/null
+++ b/drivers/tty/serial/earlycon.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 Linaro Ltd.
+ * Author: Rob Herring <robh@kernel.org>
+ *
+ * Based on 8250 earlycon:
+ * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
+ *	Bjorn Helgaas <bjorn.helgaas@hp.com>
+ *
+ * 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/console.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/serial_core.h>
+
+#include <asm/fixmap.h>
+#include <asm/serial.h>
+
+static struct console early_con = {
+	.name =		"earlycon",
+	.flags =	CON_PRINTBUFFER | CON_BOOT,
+	.index =	-1,
+};
+
+static struct earlycon_device early_console_dev = {
+	.con = &early_con,
+};
+
+static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
+{
+	void __iomem *base;
+#ifdef CONFIG_FIX_EARLYCON_MEM
+	set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK);
+	base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
+	base += paddr & ~PAGE_MASK;
+#else
+	base = ioremap_nocache(paddr, size);
+#endif
+	if (!base)
+		pr_err("%s: Couldn't map 0x%llx\n", __func__,
+		       (unsigned long long)paddr);
+
+	return base;
+}
+
+static int __init parse_options(struct earlycon_device *device,
+				char *options)
+{
+	struct uart_port *port = &device->port;
+	int mmio, mmio32, length, ret;
+	unsigned long addr;
+
+	if (!options)
+		return -ENODEV;
+
+	mmio = !strncmp(options, "mmio,", 5);
+	mmio32 = !strncmp(options, "mmio32,", 7);
+	if (mmio || mmio32) {
+		port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
+		options += mmio ? 5 : 7;
+		ret = kstrtoul(options, 0, &addr);
+		if (ret)
+			return ret;
+		port->mapbase = addr;
+		if (mmio32)
+			port->regshift = 2;
+	} else if (!strncmp(options, "io,", 3)) {
+		port->iotype = UPIO_PORT;
+		options += 3;
+		ret = kstrtoul(options, 0, &addr);
+		if (ret)
+			return ret;
+		port->iobase = addr;
+		mmio = 0;
+	} else if (!strncmp(options, "0x", 2)) {
+		port->iotype = UPIO_MEM;
+		ret = kstrtoul(options, 0, &addr);
+		if (ret)
+			return ret;
+		port->mapbase = addr;
+	} else {
+		return -EINVAL;
+	}
+
+	if (port->mapbase)
+		port->membase = earlycon_map(port->mapbase, 64);
+
+	port->uartclk = BASE_BAUD * 16;
+
+	options = strchr(options, ',');
+	if (options) {
+		options++;
+		ret = kstrtouint(options, 0, &device->baud);
+		if (ret)
+			return ret;
+		length = min(strcspn(options, " ") + 1,
+			     (size_t)(sizeof(device->options)));
+		strlcpy(device->options, options, length);
+	}
+
+	if (mmio || mmio32)
+		pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n",
+			mmio32 ? "32" : "",
+			(unsigned long long)port->mapbase,
+			device->options);
+	else
+		pr_info("Early serial console at I/O port 0x%lx (options '%s')\n",
+			port->iobase,
+			device->options);
+
+	return 0;
+}
+
+int __init setup_earlycon(char *buf, const char *match,
+			  int (*setup)(struct earlycon_device *, const char *))
+{
+	int err;
+	size_t len;
+
+	if (!buf || !match || !setup)
+		return -EINVAL;
+
+	len = strlen(match);
+	if (strncmp(buf, match, len))
+		return 0;
+	if (buf[len] && (buf[len] != ','))
+		return 0;
+
+	buf += len + 1;
+
+	err = parse_options(&early_console_dev, buf);
+	/* On parsing error, pass the options buf to the setup function */
+	if (!err)
+		buf = NULL;
+
+	early_console_dev.con->data = &early_console_dev;
+	err = setup(&early_console_dev, buf);
+	if (err < 0)
+		return err;
+	if (!early_console_dev.con->write)
+		return -ENODEV;
+
+	register_console(early_console_dev.con);
+	return 0;
+}
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index f729be9..7a15b5b 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -285,6 +285,22 @@ static inline int uart_poll_timeout(struct uart_port *port)
 /*
  * Console helpers.
  */
+struct earlycon_device {
+	struct console *con;
+	struct uart_port port;
+	char options[16];		/* e.g., 115200n8 */
+	unsigned int baud;
+};
+int setup_earlycon(char *buf, const char *match,
+		   int (*setup)(struct earlycon_device *, const char *));
+
+#define EARLYCON_DECLARE(name, func) \
+static int __init name ## _setup_earlycon(char *buf) \
+{ \
+	return setup_earlycon(buf, __stringify(name), func); \
+} \
+early_param("earlycon", name ## _setup_earlycon);
+
 struct uart_port *uart_get_console(struct uart_port *ports, int nr,
 				   struct console *c);
 void uart_parse_options(char *options, int *baud, int *parity, int *bits,
-- 
1.8.3.2


WARNING: multiple messages have this Message-ID (diff)
From: robherring2@gmail.com (Rob Herring)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 4/8] tty/serial: add generic serial earlycon
Date: Fri, 21 Mar 2014 16:08:44 -0500	[thread overview]
Message-ID: <1395436128-11244-5-git-send-email-robherring2@gmail.com> (raw)
In-Reply-To: <1395436128-11244-1-git-send-email-robherring2@gmail.com>

From: Rob Herring <robh@kernel.org>

This introduces generic earlycon infrastructure for serial devices
based on the 8250 earlycon. This allows for supporting earlycon option
with other serial devices. The earlycon output is enabled at the time
early_params are processed.

Only architectures that have fixmap support or have functional ioremap
when early_params are processed are supported. This is the same
restriction that the 8250 driver had.

Signed-off-by: Rob Herring <robh@kernel.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.cz>
---
 drivers/tty/serial/Kconfig    |   7 ++
 drivers/tty/serial/Makefile   |   2 +
 drivers/tty/serial/earlycon.c | 148 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/serial_core.h   |  16 +++++
 4 files changed, 173 insertions(+)
 create mode 100644 drivers/tty/serial/earlycon.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 2e6d8dd..1685189 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -7,6 +7,13 @@ if TTY
 menu "Serial drivers"
 	depends on HAS_IOMEM
 
+config SERIAL_EARLYCON
+	bool "Early console for serial ports"
+	help
+	  Support for early consoles with the earlycon parameter. This enables
+	  the console before standard serial driver is probed. The console is
+	  enabled when early_param is processed.
+
 source "drivers/tty/serial/8250/Kconfig"
 
 comment "Non-8250 serial port support"
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 3680854..8af1415 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -5,6 +5,8 @@
 obj-$(CONFIG_SERIAL_CORE) += serial_core.o
 obj-$(CONFIG_SERIAL_21285) += 21285.o
 
+obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
+
 # These Sparc drivers have to appear before others such as 8250
 # which share ttySx minor node space.  Otherwise console device
 # names change and other unplesantries.
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
new file mode 100644
index 0000000..241757a
--- /dev/null
+++ b/drivers/tty/serial/earlycon.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 Linaro Ltd.
+ * Author: Rob Herring <robh@kernel.org>
+ *
+ * Based on 8250 earlycon:
+ * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
+ *	Bjorn Helgaas <bjorn.helgaas@hp.com>
+ *
+ * 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/console.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/serial_core.h>
+
+#include <asm/fixmap.h>
+#include <asm/serial.h>
+
+static struct console early_con = {
+	.name =		"earlycon",
+	.flags =	CON_PRINTBUFFER | CON_BOOT,
+	.index =	-1,
+};
+
+static struct earlycon_device early_console_dev = {
+	.con = &early_con,
+};
+
+static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
+{
+	void __iomem *base;
+#ifdef CONFIG_FIX_EARLYCON_MEM
+	set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK);
+	base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
+	base += paddr & ~PAGE_MASK;
+#else
+	base = ioremap_nocache(paddr, size);
+#endif
+	if (!base)
+		pr_err("%s: Couldn't map 0x%llx\n", __func__,
+		       (unsigned long long)paddr);
+
+	return base;
+}
+
+static int __init parse_options(struct earlycon_device *device,
+				char *options)
+{
+	struct uart_port *port = &device->port;
+	int mmio, mmio32, length, ret;
+	unsigned long addr;
+
+	if (!options)
+		return -ENODEV;
+
+	mmio = !strncmp(options, "mmio,", 5);
+	mmio32 = !strncmp(options, "mmio32,", 7);
+	if (mmio || mmio32) {
+		port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
+		options += mmio ? 5 : 7;
+		ret = kstrtoul(options, 0, &addr);
+		if (ret)
+			return ret;
+		port->mapbase = addr;
+		if (mmio32)
+			port->regshift = 2;
+	} else if (!strncmp(options, "io,", 3)) {
+		port->iotype = UPIO_PORT;
+		options += 3;
+		ret = kstrtoul(options, 0, &addr);
+		if (ret)
+			return ret;
+		port->iobase = addr;
+		mmio = 0;
+	} else if (!strncmp(options, "0x", 2)) {
+		port->iotype = UPIO_MEM;
+		ret = kstrtoul(options, 0, &addr);
+		if (ret)
+			return ret;
+		port->mapbase = addr;
+	} else {
+		return -EINVAL;
+	}
+
+	if (port->mapbase)
+		port->membase = earlycon_map(port->mapbase, 64);
+
+	port->uartclk = BASE_BAUD * 16;
+
+	options = strchr(options, ',');
+	if (options) {
+		options++;
+		ret = kstrtouint(options, 0, &device->baud);
+		if (ret)
+			return ret;
+		length = min(strcspn(options, " ") + 1,
+			     (size_t)(sizeof(device->options)));
+		strlcpy(device->options, options, length);
+	}
+
+	if (mmio || mmio32)
+		pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n",
+			mmio32 ? "32" : "",
+			(unsigned long long)port->mapbase,
+			device->options);
+	else
+		pr_info("Early serial console at I/O port 0x%lx (options '%s')\n",
+			port->iobase,
+			device->options);
+
+	return 0;
+}
+
+int __init setup_earlycon(char *buf, const char *match,
+			  int (*setup)(struct earlycon_device *, const char *))
+{
+	int err;
+	size_t len;
+
+	if (!buf || !match || !setup)
+		return -EINVAL;
+
+	len = strlen(match);
+	if (strncmp(buf, match, len))
+		return 0;
+	if (buf[len] && (buf[len] != ','))
+		return 0;
+
+	buf += len + 1;
+
+	err = parse_options(&early_console_dev, buf);
+	/* On parsing error, pass the options buf to the setup function */
+	if (!err)
+		buf = NULL;
+
+	early_console_dev.con->data = &early_console_dev;
+	err = setup(&early_console_dev, buf);
+	if (err < 0)
+		return err;
+	if (!early_console_dev.con->write)
+		return -ENODEV;
+
+	register_console(early_console_dev.con);
+	return 0;
+}
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index f729be9..7a15b5b 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -285,6 +285,22 @@ static inline int uart_poll_timeout(struct uart_port *port)
 /*
  * Console helpers.
  */
+struct earlycon_device {
+	struct console *con;
+	struct uart_port port;
+	char options[16];		/* e.g., 115200n8 */
+	unsigned int baud;
+};
+int setup_earlycon(char *buf, const char *match,
+		   int (*setup)(struct earlycon_device *, const char *));
+
+#define EARLYCON_DECLARE(name, func) \
+static int __init name ## _setup_earlycon(char *buf) \
+{ \
+	return setup_earlycon(buf, __stringify(name), func); \
+} \
+early_param("earlycon", name ## _setup_earlycon);
+
 struct uart_port *uart_get_console(struct uart_port *ports, int nr,
 				   struct console *c);
 void uart_parse_options(char *options, int *baud, int *parity, int *bits,
-- 
1.8.3.2

  parent reply	other threads:[~2014-03-21 21:09 UTC|newest]

Thread overview: 90+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-03-21 21:08 [PATCH 0/8] Generic serial earlycon Rob Herring
2014-03-21 21:08 ` Rob Herring
     [not found] ` < 201403221054.39799.arnd@arndb.de>
     [not found]   ` < CAL_JsqKjGjyYypwneCmNc1qeKe0ZOG9gz3QSJ37-HwHXKu3iRA@mail.gmail.com>
     [not found]     ` < 20140329001732.BECE2C41FF4@trevor.secretlab.ca>
2014-03-21 21:08 ` [PATCH 1/8] x86: move FIX_EARLYCON_MEM kconfig into x86 Rob Herring
2014-03-21 21:08   ` Rob Herring
2014-03-21 21:08   ` Rob Herring
2014-03-21 21:08 ` [PATCH 2/8] arm64: add FIXMAP_PAGE_NOCACHE definition Rob Herring
2014-03-21 21:08   ` Rob Herring
2014-03-24 11:13   ` Catalin Marinas
2014-03-24 11:13     ` Catalin Marinas
2014-03-24 11:13     ` Catalin Marinas
2014-03-21 21:08 ` [PATCH 3/8] arm64: enable FIX_EARLYCON_MEM kconfig Rob Herring
2014-03-21 21:08   ` Rob Herring
2014-03-24 11:38   ` Catalin Marinas
2014-03-24 11:38     ` Catalin Marinas
2014-03-24 11:38     ` Catalin Marinas
2014-03-21 21:08 ` Rob Herring [this message]
2014-03-21 21:08   ` [PATCH 4/8] tty/serial: add generic serial earlycon Rob Herring
2014-03-24 11:22   ` Catalin Marinas
2014-03-24 11:22     ` Catalin Marinas
2014-03-24 11:22     ` Catalin Marinas
2014-03-24 11:29     ` Arnd Bergmann
2014-03-24 11:29       ` Arnd Bergmann
2014-03-24 13:36       ` Rob Herring
2014-03-24 13:36         ` Rob Herring
2014-03-24 13:36         ` Rob Herring
2014-03-24 15:42         ` Arnd Bergmann
2014-03-24 15:42           ` Arnd Bergmann
2014-03-24 15:42           ` Arnd Bergmann
2014-04-17 18:27           ` Rob Herring
2014-04-17 18:27             ` Rob Herring
2014-04-17 18:27             ` Rob Herring
2014-04-17 20:16             ` Alan Cox
2014-04-17 20:16               ` Alan Cox
2014-04-17 20:16               ` Alan Cox
2014-04-19 11:32             ` Arnd Bergmann
2014-04-19 11:32               ` Arnd Bergmann
2014-04-19 11:32               ` Arnd Bergmann
2014-03-21 21:08 ` [PATCH 5/8] tty/serial: convert 8250 to generic earlycon Rob Herring
2014-03-21 21:08   ` Rob Herring
2014-03-21 21:08 ` [PATCH 6/8] tty/serial: pl011: add generic earlycon support Rob Herring
2014-03-21 21:08   ` Rob Herring
2014-03-24 11:28   ` Catalin Marinas
2014-03-24 11:28     ` Catalin Marinas
2014-03-24 11:28     ` Catalin Marinas
2014-04-16 22:14     ` Rob Herring
2014-04-16 22:14       ` Rob Herring
2014-04-16 22:14       ` Rob Herring
2014-04-23 16:39       ` Catalin Marinas
2014-04-23 16:39         ` Catalin Marinas
2014-04-23 16:39         ` Catalin Marinas
2014-03-21 21:08 ` [PATCH 7/8] tty/serial: add arm64 semihosting earlycon Rob Herring
2014-03-21 21:08   ` Rob Herring
2014-03-23 20:04   ` Nicolas Pitre
2014-03-23 20:04     ` Nicolas Pitre
2014-03-23 20:23     ` Arnd Bergmann
2014-03-23 20:23       ` Arnd Bergmann
2014-03-23 21:48       ` Nicolas Pitre
2014-03-23 21:48         ` Nicolas Pitre
2014-03-24 11:38   ` Catalin Marinas
2014-03-24 11:38     ` Catalin Marinas
2014-03-24 11:38     ` Catalin Marinas
2014-03-24 11:48     ` Catalin Marinas
2014-03-24 11:48       ` Catalin Marinas
2014-03-24 11:48       ` Catalin Marinas
2014-03-21 21:08 ` [PATCH 8/8] arm64: remove arch specific earlyprintk Rob Herring
2014-03-21 21:08   ` Rob Herring
2014-03-22  9:54 ` [PATCH 0/8] Generic serial earlycon Arnd Bergmann
2014-03-22  9:54   ` Arnd Bergmann
2014-03-22  9:54   ` Arnd Bergmann
2014-03-22 14:14   ` Rob Herring
2014-03-22 14:14     ` Rob Herring
2014-03-22 14:14     ` Rob Herring
2014-03-22 22:01     ` Arnd Bergmann
2014-03-22 22:01       ` Arnd Bergmann
2014-03-22 22:01       ` Arnd Bergmann
2014-03-23 15:09       ` Rob Herring
2014-03-23 15:09         ` Rob Herring
2014-03-23 15:09         ` Rob Herring
2014-03-23 19:49         ` Arnd Bergmann
2014-03-23 19:49           ` Arnd Bergmann
2014-03-23 19:49           ` Arnd Bergmann
2014-03-29  0:17     ` Grant Likely
2014-03-29  0:17       ` Grant Likely
2014-03-29  0:17       ` Grant Likely
2014-03-29 14:33       ` Rob Herring
2014-03-29 14:33         ` Rob Herring
2014-03-29 14:33         ` Rob Herring
2014-03-29 15:34         ` Grant Likely
2014-03-29 15:34           ` Grant Likely
2014-03-29 15:34           ` Grant Likely

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1395436128-11244-5-git-send-email-robherring2@gmail.com \
    --to=robherring2@gmail.com \
    --cc=arnd@arndb.de \
    --cc=catalin.marinas@arm.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=jslaby@suse.cz \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=linux@arm.linux.org.uk \
    --cc=robh@kernel.org \
    --cc=will.deacon@arm.com \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.