All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michal Nazarewicz <m.nazarewicz@samsung.com>
To: linux-usb@vger.kernel.org
Cc: David Brownell <dbrownell@users.sourceforge.net>,
	Kyungmin Park <kyungmin.park@samsung.com>,
	Marek Szyprowski <m.szyprowski@samsung.com>,
	linux-kernel@vger.kernel.org
Subject: [PATCH] USB: gadget: g_fs: code cleanup and bug fixes
Date: Mon, 14 Jun 2010 10:43:36 +0200	[thread overview]
Message-ID: <b51342e058f33dc9d78491c1209d0e86d09ed9da.1276170819.git.m.nazarewicz@samsung.com> (raw)
In-Reply-To: <b34cbaab37bf0590cb254a03478c5297bc4a1723.1275906835.git.m.nazarewicz@samsung.com>

This commit cleans the g_fs gadget hopefully making it more
readable.  This is achieved by usage of the usb_string_ids_tab()
function for bulk string IDs registration as well as
generalising provided configuration so that a single routine is
used to add each configuration and bind interfaces.  As an
effect, the code is shorter and has fewer #ifdefs.

Also, a bug has been fixed where bConfigurationValue were
not affected by what configuration user chose to compile in via
Kconfig option.  As an effect, RDNIS and ECM were always number
1 and the "pure" configuration was always number 2.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
Requires "USB: gadget: composite: usb_string_ids_*() functions added"
patch to work.

 drivers/usb/gadget/Kconfig |   23 +++---
 drivers/usb/gadget/g_ffs.c |  174 ++++++++++++--------------------------------
 2 files changed, 60 insertions(+), 137 deletions(-)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index d5b65d3..d3d0e2d 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -750,6 +750,7 @@ config USB_GADGETFS
 config USB_FUNCTIONFS
 	tristate "Function Filesystem (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
+	select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
 	help
 	  The Function Filesystem (FunctioFS) lets one create USB
 	  composite functions in user space in the same way as GadgetFS
@@ -758,31 +759,31 @@ config USB_FUNCTIONFS
 	  implemented in kernel space (for instance Ethernet, serial or
 	  mass storage) and other are implemented in user space.
 
+	  If you say "y" or "m" here you will be able what kind of
+	  configurations the gadget will provide.
+
 	  Say "y" to link the driver statically, or "m" to build
 	  a dynamically linked module called "g_ffs".
 
 config USB_FUNCTIONFS_ETH
-	bool "Include CDC ECM (Ethernet) function"
+	bool "Include configuration with CDC ECM (Ethernet)"
 	depends on USB_FUNCTIONFS && NET
 	help
-	  Include an CDC ECM (Ethernet) funcion in the CDC ECM (Funcion)
-	  Filesystem.  If you also say "y" to the RNDIS query below the
-	  gadget will have two configurations.
+	  Include a configuration with CDC ECM funcion (Ethernet) and the
+	  Funcion Filesystem.
 
 config USB_FUNCTIONFS_RNDIS
-	bool "Include RNDIS (Ethernet) function"
+	bool "Include configuration with RNDIS (Ethernet)"
 	depends on USB_FUNCTIONFS && NET
 	help
-	  Include an RNDIS (Ethernet) funcion in the Funcion Filesystem.
-	  If you also say "y" to the CDC ECM query above the gadget will
-	  have two configurations.
+	  Include a configuration with RNDIS funcion (Ethernet) and the Filesystem.
 
 config USB_FUNCTIONFS_GENERIC
 	bool "Include 'pure' configuration"
-	depends on USB_FUNCTIONFS && (USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
+	depends on USB_FUNCTIONFS
 	help
-	  Include a configuration with FunctionFS and no Ethernet
-	  configuration.
+	  Include a configuration with the Function Filesystem alone with
+	  no Ethernet interface.
 
 config USB_FILE_STORAGE
 	tristate "File-backed Storage Gadget"
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index da3a9e4..a9474f8 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -32,12 +32,13 @@
 #  include "u_ether.c"
 
 static u8 gfs_hostaddr[ETH_ALEN];
-#else
-#  if !defined CONFIG_USB_FUNCTIONFS_GENERIC
-#    define CONFIG_USB_FUNCTIONFS_GENERIC
+#  ifdef CONFIG_USB_FUNCTIONFS_ETH
+static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
 #  endif
+#else
 #  define gether_cleanup() do { } while (0)
 #  define gether_setup(gadget, hostaddr)   ((int)0)
+#  define gfs_hostaddr NULL
 #endif
 
 #include "f_fs.c"
@@ -107,15 +108,7 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = {
 enum {
 	GFS_STRING_MANUFACTURER_IDX,
 	GFS_STRING_PRODUCT_IDX,
-#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
-	GFS_STRING_RNDIS_CONFIG_IDX,
-#endif
-#ifdef CONFIG_USB_FUNCTIONFS_ETH
-	GFS_STRING_ECM_CONFIG_IDX,
-#endif
-#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
-	GFS_STRING_GENERIC_CONFIG_IDX,
-#endif
+	GFS_STRING_FIRST_CONFIG_IDX,
 };
 
 static       char gfs_manufacturer[50];
@@ -126,13 +119,13 @@ static struct usb_string gfs_strings[] = {
 	[GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer,
 	[GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc,
 #ifdef CONFIG_USB_FUNCTIONFS_RNDIS
-	[GFS_STRING_RNDIS_CONFIG_IDX].s = "FunctionFS + RNDIS",
+	{ .s = "FunctionFS + RNDIS" },
 #endif
 #ifdef CONFIG_USB_FUNCTIONFS_ETH
-	[GFS_STRING_ECM_CONFIG_IDX].s = "FunctionFS + ECM",
+	{ .s = "FunctionFS + ECM" },
 #endif
 #ifdef CONFIG_USB_FUNCTIONFS_GENERIC
-	[GFS_STRING_GENERIC_CONFIG_IDX].s = "FunctionFS",
+	{ .s = "FunctionFS" },
 #endif
 	{  } /* end of list */
 };
@@ -146,59 +139,33 @@ static struct usb_gadget_strings *gfs_dev_strings[] = {
 };
 
 
+
+struct gfs_configuration {
+	struct usb_configuration c;
+	int (*eth)(struct usb_configuration *c, u8 *ethaddr);
+} gfs_configurations[] = {
 #ifdef CONFIG_USB_FUNCTIONFS_RNDIS
-static int gfs_do_rndis_config(struct usb_configuration *c);
-
-static struct usb_configuration gfs_rndis_config_driver = {
-	.label			= "FunctionFS + RNDIS",
-	.bind			= gfs_do_rndis_config,
-	.bConfigurationValue	= 1,
-	/* .iConfiguration	= DYNAMIC */
-	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
-};
-#  define gfs_add_rndis_config(cdev) \
-	usb_add_config(cdev, &gfs_rndis_config_driver)
-#else
-#  define gfs_add_rndis_config(cdev) 0
+	{
+		.eth		= rndis_bind_config,
+	},
 #endif
 
-
 #ifdef CONFIG_USB_FUNCTIONFS_ETH
-static int gfs_do_ecm_config(struct usb_configuration *c);
-
-static struct usb_configuration gfs_ecm_config_driver = {
-	.label			= "FunctionFS + ECM",
-	.bind			= gfs_do_ecm_config,
-	.bConfigurationValue	= 1,
-	/* .iConfiguration	= DYNAMIC */
-	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
-};
-#  define gfs_add_ecm_config(cdev) \
-	usb_add_config(cdev, &gfs_ecm_config_driver)
-#else
-#  define gfs_add_ecm_config(cdev) 0
+	{
+		.eth		= eth_bind_config,
+	},
 #endif
 
-
 #ifdef CONFIG_USB_FUNCTIONFS_GENERIC
-static int gfs_do_generic_config(struct usb_configuration *c);
-
-static struct usb_configuration gfs_generic_config_driver = {
-	.label			= "FunctionFS",
-	.bind			= gfs_do_generic_config,
-	.bConfigurationValue	= 2,
-	/* .iConfiguration	= DYNAMIC */
-	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
-};
-#  define gfs_add_generic_config(cdev) \
-	usb_add_config(cdev, &gfs_generic_config_driver)
-#else
-#  define gfs_add_generic_config(cdev) 0
+	{
+	},
 #endif
+};
 
 
 static int gfs_bind(struct usb_composite_dev *cdev);
 static int gfs_unbind(struct usb_composite_dev *cdev);
+static int gfs_do_config(struct usb_configuration *c);
 
 static struct usb_composite_driver gfs_driver = {
 	.name		= gfs_short_name,
@@ -267,7 +234,7 @@ static int functionfs_check_dev_callback(const char *dev_name)
 
 static int gfs_bind(struct usb_composite_dev *cdev)
 {
-	int ret;
+	int ret, i;
 
 	ENTER();
 
@@ -284,57 +251,32 @@ static int gfs_bind(struct usb_composite_dev *cdev)
 	snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s",
 		 init_utsname()->sysname, init_utsname()->release,
 		 cdev->gadget->name);
-	ret = usb_string_id(cdev);
-	if (unlikely(ret < 0))
-		goto error;
-	gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret;
-	gfs_dev_desc.iManufacturer = ret;
-
-	ret = usb_string_id(cdev);
-	if (unlikely(ret < 0))
-		goto error;
-	gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret;
-	gfs_dev_desc.iProduct = ret;
-
-#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
-	ret = usb_string_id(cdev);
-	if (unlikely(ret < 0))
-		goto error;
-	gfs_strings[GFS_STRING_RNDIS_CONFIG_IDX].id = ret;
-	gfs_rndis_config_driver.iConfiguration = ret;
-#endif
 
-#ifdef CONFIG_USB_FUNCTIONFS_ETH
-	ret = usb_string_id(cdev);
+	ret = usb_string_ids_tab(cdev, gfs_strings);
 	if (unlikely(ret < 0))
 		goto error;
-	gfs_strings[GFS_STRING_ECM_CONFIG_IDX].id = ret;
-	gfs_ecm_config_driver.iConfiguration = ret;
-#endif
 
-#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
-	ret = usb_string_id(cdev);
-	if (unlikely(ret < 0))
-		goto error;
-	gfs_strings[GFS_STRING_GENERIC_CONFIG_IDX].id = ret;
-	gfs_generic_config_driver.iConfiguration = ret;
-#endif
+	gfs_dev_desc.iManufacturer = gfs_strings[GFS_STRING_MANUFACTURER_IDX].id;
+	gfs_dev_desc.iProduct      = gfs_strings[GFS_STRING_PRODUCT_IDX].id;
 
 	ret = functionfs_bind(gfs_ffs_data, cdev);
 	if (unlikely(ret < 0))
 		goto error;
 
-	ret = gfs_add_rndis_config(cdev);
-	if (unlikely(ret < 0))
-		goto error_unbind;
+	for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
+		struct gfs_configuration *c = gfs_configurations + i;
 
-	ret = gfs_add_ecm_config(cdev);
-	if (unlikely(ret < 0))
-		goto error_unbind;
+		ret = GFS_STRING_FIRST_CONFIG_IDX + i;
+		c->c.label			= gfs_strings[ret].s;
+		c->c.iConfiguration		= gfs_strings[ret].id;
+		c->c.bind			= gfs_do_config;
+		c->c.bConfigurationValue	= 1 + i;
+		c->c.bmAttributes		= USB_CONFIG_ATT_SELFPOWER;
 
-	ret = gfs_add_generic_config(cdev);
-	if (unlikely(ret < 0))
-		goto error_unbind;
+		ret = usb_add_config(cdev, &c->c);
+		if (unlikely(ret < 0))
+			goto error_unbind;
+	}
 
 	return 0;
 
@@ -368,10 +310,10 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
 }
 
 
-static int __gfs_do_config(struct usb_configuration *c,
-			   int (*eth)(struct usb_configuration *c, u8 *ethaddr),
-			   u8 *ethaddr)
+static int gfs_do_config(struct usb_configuration *c)
 {
+	struct gfs_configuration *gc =
+		container_of(c, struct gfs_configuration, c);
 	int ret;
 
 	if (WARN_ON(!gfs_ffs_data))
@@ -382,8 +324,8 @@ static int __gfs_do_config(struct usb_configuration *c,
 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
 	}
 
-	if (eth) {
-		ret = eth(c, ethaddr);
+	if (gc->eth) {
+		ret = gc->eth(c, gfs_hostaddr);
 		if (unlikely(ret < 0))
 			return ret;
 	}
@@ -406,32 +348,12 @@ static int __gfs_do_config(struct usb_configuration *c,
 	return 0;
 }
 
-#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
-static int gfs_do_rndis_config(struct usb_configuration *c)
-{
-	ENTER();
-
-	return __gfs_do_config(c, rndis_bind_config, gfs_hostaddr);
-}
-#endif
 
 #ifdef CONFIG_USB_FUNCTIONFS_ETH
-static int gfs_do_ecm_config(struct usb_configuration *c)
-{
-	ENTER();
-
-	return __gfs_do_config(c,
-			       can_support_ecm(c->cdev->gadget)
-			     ? ecm_bind_config : geth_bind_config,
-			       gfs_hostaddr);
-}
-#endif
-
-#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
-static int gfs_do_generic_config(struct usb_configuration *c)
+static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
 {
-	ENTER();
-
-	return __gfs_do_config(c, NULL, NULL);
+	return can_support_ecm(c->cdev->gadget)
+		? ecm_bind_config(c, ethaddr)
+		: geth_bind_config(c, ethaddr);
 }
 #endif
-- 
1.7.1


  parent reply	other threads:[~2010-06-14  8:42 UTC|newest]

Thread overview: 72+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-06-07 12:39 [PATCHv4 00/13] USB improvements and cleanpus, g_multi install mode Michal Nazarewicz
2010-06-07 12:39 ` [PATCHv4 01/13] USB: gadget: g_mass_storage: static data instead of dynamic allocation Michal Nazarewicz
2010-06-07 12:39   ` [PATCHv4 02/13] USB: gadget: f_mass_storage: fsg_add() renamed to fsg_bind_config() Michal Nazarewicz
2010-06-07 12:39     ` [PATCHv4 03/13] USB: gadget: f_fs: functionfs_add() renamed to functionfs_bind_config() Michal Nazarewicz
2010-06-07 12:39       ` [PATCHv4 04/13] USB: gadget: composite: usb_string_ids_*() functions added Michal Nazarewicz
2010-06-07 12:39         ` [PATCHv4 05/13] USB: gadget: f_fs: use usb_string_ids_n() Michal Nazarewicz
2010-06-07 12:39           ` [PATCHv4 06/13] USB: gadget: g_multi: code clean up and refactoring Michal Nazarewicz
2010-06-07 12:39             ` [PATCHv4 07/13] USB: gadget: g_ether: updated INF file Michal Nazarewicz
2010-06-07 12:39               ` [PATCHv4 08/13] USB: gadget: g_serial: INF file updated Michal Nazarewicz
2010-06-07 12:39                 ` [PATCHv4 09/13] USB: gadget: g_multi: added documentation and INF files Michal Nazarewicz
2010-06-07 12:39                   ` [PATCHv4 10/13] USB: gadget: g_multi: more configurable Michal Nazarewicz
2010-06-07 12:39                     ` [PATCHv4 11/13] USB: gadget: composite: addad disconnect callback Michal Nazarewicz
2010-06-07 12:39                       ` [PATCHv4 12/13] USB: gadget: f_mass_storage: added eject callback Michal Nazarewicz
2010-06-07 12:40                         ` [PATCHv4 13/13] USB: gadget: g_multi: Install Mode added Michal Nazarewicz
2010-06-14  8:43                         ` [PATCHv4.1 12/13] USB: gadget: f_mass_storage: added eject callback Michal Nazarewicz
2010-06-08 14:02                       ` [PATCHv4 11/13] USB: gadget: composite: addad disconnect callback David Brownell
2010-06-08 13:37                     ` [PATCHv4 10/13] USB: gadget: g_multi: more configurable David Brownell
2010-06-09 10:15                       ` Michał Nazarewicz
2010-06-08 13:13                 ` [PATCHv4 08/13] USB: gadget: g_serial: INF file updated David Brownell
2010-06-08 13:20                   ` Xiaofan Chen
2010-06-09  8:55                   ` Michał Nazarewicz
2010-06-09  9:29                     ` Xiaofan Chen
2010-06-09 10:01                       ` Michał Nazarewicz
2010-06-09 14:59                         ` Greg KH
2010-06-09 15:22                           ` Michał Nazarewicz
2010-06-09 15:33                             ` Greg KH
2010-06-09 15:53                               ` Michał Nazarewicz
2010-06-08 12:57               ` [PATCHv4 07/13] USB: gadget: g_ether: updated INF file David Brownell
2010-06-08 13:17                 ` Xiaofan Chen
2010-06-14  8:43               ` [PATCHv4.1 " Michal Nazarewicz
2010-06-14 11:32                 ` Xiaofan Chen
2010-06-14 19:42                   ` David Brownell
2010-06-14 20:04                     ` Michał Nazarewicz
2010-06-14  8:43         ` Michal Nazarewicz [this message]
2010-06-14  8:43   ` [PATCHv4.1 01/13] USB: gadget: g_mass_storage: static data instead of dynamic allocation Michal Nazarewicz
2010-06-15 18:55 ` [PATCHv4 00/13] USB improvements and cleanpus, g_multi install mode Greg KH
2010-06-16 10:07   ` [PATCHv5 00/11] g_multi & other improvements and cleanpus Michal Nazarewicz
2010-06-16 10:07     ` [PATCHv5 01/11] USB: gadget: g_mass_storage: static data instead of dynamic allocation Michal Nazarewicz
2010-06-16 10:07       ` [PATCHv5 02/11] USB: gadget: f_mass_storage: fsg_add() renamed to fsg_bind_config() Michal Nazarewicz
2010-06-16 10:07         ` [PATCHv5 03/11] USB: gadget: f_fs: functionfs_add() renamed to functionfs_bind_config() Michal Nazarewicz
2010-06-16 10:07           ` [PATCHv5 04/11] USB: gadget: composite: usb_string_ids_*() functions added Michal Nazarewicz
2010-06-16 10:08             ` [PATCHv5 05/11] USB: gadget: f_fs: use usb_string_ids_n() Michal Nazarewicz
2010-06-16 10:08               ` [PATCHv5 06/11] USB: gadget: g_multi: code clean up and refactoring Michal Nazarewicz
2010-06-16 10:08                 ` [PATCHv5 07/11] USB: gadget: g_ether: updated INF file Michal Nazarewicz
2010-06-16 10:08                   ` [PATCHv5 08/11] USB: gadget: g_serial: INF file updated Michal Nazarewicz
2010-06-16 10:08                     ` [PATCHv5 09/11] USB: gadget: g_multi: added documentation and INF files Michal Nazarewicz
2010-06-16 10:08                       ` [PATCHv5 10/11] USB: gadget: composite: addad disconnect callback Michal Nazarewicz
2010-06-16 10:08                         ` [PATCHv5 11/11] USB: gadget: f_mass_storage: added eject callback Michal Nazarewicz
2010-06-17 11:04                         ` [PATCHv5 10/11] USB: gadget: composite: addad disconnect callback Sergei Shtylyov
2010-06-18 14:19                           ` Sergei Shtylyov
2010-06-16 13:56                     ` [PATCHv5 08/11] USB: gadget: g_serial: INF file updated David Brownell
2010-06-16 14:12                   ` [PATCHv5 07/11] USB: gadget: g_ether: updated INF file David Brownell
2010-06-16 14:41                     ` [PATCHv5.1 " Michal Nazarewicz
2010-06-17 17:51                 ` [PATCHv5 06/11] USB: gadget: g_multi: code clean up and refactoring Greg KH
2010-06-18 10:31                   ` Michał Nazarewicz
2010-06-18 14:39                     ` Greg KH
2010-06-17 17:52     ` [PATCHv5 00/11] g_multi & other improvements and cleanpus Greg KH
2010-06-18 10:31       ` Michał Nazarewicz
     [not found]       ` <2809a031d19ac6ef793d7fe343e225b8d80099c3.1276864406.git.m.nazarewicz@samsung.com>
2010-06-18 12:48         ` [PATCHv6 07/11] USB: gadget: g_ether: updated INF file Michal Nazarewicz
2010-06-18 12:48           ` [PATCHv6 08/11] USB: gadget: g_serial: INF file updated Michal Nazarewicz
2010-06-18 12:48             ` [PATCHv6 09/11] USB: gadget: g_multi: added documentation and INF files Michal Nazarewicz
2010-06-18 12:48               ` [PATCHv6 10/11] USB: gadget: composite: added disconnect callback Michal Nazarewicz
2010-06-18 12:48                 ` [PATCHv6 11/11] USB: gadget: f_mass_storage: added eject callback Michal Nazarewicz
2010-06-18 13:07               ` [PATCHv6.1 09/11] USB: gadget: g_multi: added documentation and INF files Michal Nazarewicz
2010-06-18 15:52                 ` Greg KH
2010-06-18 16:14                   ` Michał Nazarewicz
2010-06-18 15:04       ` [PATCHv6.1 06/11] USB: gadget: g_multi: code clean up and refactoring Michal Nazarewicz
2010-06-18 21:25         ` Sergei Shtylyov
2010-06-21  8:43           ` Michał Nazarewicz
2010-06-14  8:43 [PATCH] USB: gadget: g_fs: possible invalid pointer reference bug fixed Michal Nazarewicz
2010-06-15 10:10 ` Sergei Shtylyov
2010-06-16  9:30   ` Michał Nazarewicz

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=b51342e058f33dc9d78491c1209d0e86d09ed9da.1276170819.git.m.nazarewicz@samsung.com \
    --to=m.nazarewicz@samsung.com \
    --cc=dbrownell@users.sourceforge.net \
    --cc=kyungmin.park@samsung.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=m.szyprowski@samsung.com \
    /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.