From: Andy Shevchenko <andy.shevchenko@gmail.com> To: "Jonathan Neuschäfer" <j.neuschaefer@gmx.net> Cc: "Linux Kernel Mailing List" <linux-kernel@vger.kernel.org>, "Lee Jones" <lee.jones@linaro.org>, "Rob Herring" <robh+dt@kernel.org>, "Thierry Reding" <thierry.reding@gmail.com>, "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>, "Alessandro Zummo" <a.zummo@towertech.it>, "Alexandre Belloni" <alexandre.belloni@bootlin.com>, "Shawn Guo" <shawnguo@kernel.org>, "Sascha Hauer" <s.hauer@pengutronix.de>, "Pengutronix Kernel Team" <kernel@pengutronix.de>, "Fabio Estevam" <festevam@gmail.com>, "NXP Linux Team" <linux-imx@nxp.com>, "Sam Ravnborg" <sam@ravnborg.org>, "Linus Walleij" <linus.walleij@linaro.org>, "Heiko Stuebner" <heiko.stuebner@theobroma-systems.com>, "Stephan Gerhold" <stephan@gerhold.net>, "Lubomir Rintel" <lkundrak@v3.sk>, "Mark Brown" <broonie@kernel.org>, allen <allen.chen@ite.com.tw>, "Mauro Carvalho Chehab" <mchehab+huawei@kernel.org>, "David S. Miller" <davem@davemloft.net>, devicetree <devicetree@vger.kernel.org>, linux-pwm@vger.kernel.org, "open list:REAL TIME CLOCK (RTC) SUBSYSTEM" <linux-rtc@vger.kernel.org>, "linux-arm Mailing List" <linux-arm-kernel@lists.infradead.org>, "Heiko Stuebner" <heiko@sntech.de>, "Josua Mayer" <josua.mayer@jm0.eu>, "Andreas Kemnade" <andreas@kemnade.info>, "Arnd Bergmann" <arnd@arndb.de>, "Daniel Palmer" <daniel@0x0f.com> Subject: Re: [PATCH v3 3/7] mfd: Add base driver for Netronix embedded controller Date: Fri, 25 Sep 2020 12:29:45 +0300 [thread overview] Message-ID: <CAHp75VdUHPsuvDPLnfP9sM2p1FDiCsjkCf1SSM-y02ZsQxSDWQ@mail.gmail.com> (raw) In-Reply-To: <20200924192455.2484005-4-j.neuschaefer@gmx.net> On Thu, Sep 24, 2020 at 10:26 PM Jonathan Neuschäfer <j.neuschaefer@gmx.net> wrote: > > The Netronix embedded controller is a microcontroller found in some > e-book readers designed by the ODM Netronix, Inc. It contains RTC, > battery monitoring, system power management, and PWM functionality. > > This driver implements register access and version detection. > > Third-party hardware documentation is available at: > > https://github.com/neuschaefer/linux/wiki/Netronix-MSP430-embedded-controller > > The EC supports interrupts, but the driver doesn't make use of them so > far. ... > +#include <asm/unaligned.h> This usually goes after linux/*.h (and actually not visible how it's being used, but see below first) > +#include <linux/delay.h> > +#include <linux/errno.h> > +#include <linux/i2c.h> > +#include <linux/mfd/core.h> > +#include <linux/mfd/ntxec.h> > +#include <linux/module.h> > +#include <linux/pm.h> > +#include <linux/reboot.h> > +#include <linux/regmap.h> > +#include <linux/types.h> ... > +static void ntxec_poweroff(void) > +{ > + int res; > + u8 buf[] = { > + NTXEC_REG_POWEROFF, > + (NTXEC_POWEROFF_VALUE >> 8) & 0xff, > + NTXEC_POWEROFF_VALUE & 0xff, '& 0xff' parts are redundant. *u8 implies that. Fix in all cases. Also I would rather see something like buf[0] = _POWEROFF; put_unaligned_be16(_VALUE, &buf[1]); to explicitly show the endianess of the register values. > + }; > + struct i2c_msg msgs[] = { > + { > + .addr = poweroff_restart_client->addr, > + .flags = 0, > + .len = sizeof(buf), > + .buf = buf It's slightly better to keep trailing commas in cases like this. > + } > + }; > + > + res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs)); > + if (res < 0) > + dev_alert(&poweroff_restart_client->dev, > + "Failed to power off (err = %d)\n", res); alert? This needs to be explained. > + /* > + * The time from the register write until the host CPU is powered off > + * has been observed to be about 2.5 to 3 seconds. Sleep long enough to > + * safely avoid returning from the poweroff handler. > + */ > + msleep(5000); > +} > + > +static int ntxec_restart(struct notifier_block *nb, > + unsigned long action, void *data) > +{ > + int res; > + /* > + * NOTE: The lower half of the reset value is not sent, because sending > + * it causes an error Why? Any root cause? Perhaps you need to send 0xffff ? > + */ > + u8 buf[] = { > + NTXEC_REG_RESET, > + (NTXEC_RESET_VALUE >> 8) & 0xff, Here you may still use put_unaligned_be16() but move the comment to be before len hardcoded to sizeof(buf) - 1. > + }; > + struct i2c_msg msgs[] = { > + { > + .addr = poweroff_restart_client->addr, > + .flags = 0, > + .len = sizeof(buf), > + .buf = buf > + } > + }; > + > + res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs)); > + if (res < 0) > + dev_alert(&poweroff_restart_client->dev, > + "Failed to restart (err = %d)\n", res); > + > + return NOTIFY_DONE; > +} ... > +static int ntxec_probe(struct i2c_client *client) > +{ > + struct ntxec *ec; > + unsigned int version; > + int res; > + > + ec = devm_kmalloc(&client->dev, sizeof(*ec), GFP_KERNEL); > + if (!ec) > + return -ENOMEM; > + > + ec->dev = &client->dev; > + > + ec->regmap = devm_regmap_init_i2c(client, ®map_config); > + if (IS_ERR(ec->regmap)) { > + dev_err(ec->dev, "Failed to set up regmap for device\n"); > + return res; > + } > + > + /* Determine the firmware version */ > + res = regmap_read(ec->regmap, NTXEC_REG_VERSION, &version); > + if (res < 0) { > + dev_err(ec->dev, "Failed to read firmware version number\n"); > + return res; > + } > + dev_info(ec->dev, > + "Netronix embedded controller version %04x detected.\n", > + version); This info level may confuse users if followed by an error path. > + /* Bail out if we encounter an unknown firmware version */ > + switch (version) { > + case 0xd726: /* found in Kobo Aura */ > + break; > + default: > + return -ENODEV; > + } > + > + if (of_device_is_system_power_controller(ec->dev->of_node)) { > + /* > + * Set the 'powerkeep' bit. This is necessary on some boards > + * in order to keep the system running. > + */ > + res = regmap_write(ec->regmap, NTXEC_REG_POWERKEEP, > + NTXEC_POWERKEEP_VALUE); > + if (res < 0) > + return res; > + WARN_ON(poweroff_restart_client); WARN_ON? All these alerts, WARNs, BUGs must be explained. Screaming to the user is not good if it wasn't justified. > + poweroff_restart_client = client; > + if (pm_power_off) > + dev_err(ec->dev, "pm_power_off already assigned\n"); > + else > + pm_power_off = ntxec_poweroff; > + > + res = register_restart_handler(&ntxec_restart_handler); > + if (res) > + dev_err(ec->dev, > + "Failed to register restart handler: %d\n", res); > + } > + > + i2c_set_clientdata(client, ec); > + > + res = devm_mfd_add_devices(ec->dev, PLATFORM_DEVID_NONE, ntxec_subdevices, > + ARRAY_SIZE(ntxec_subdevices), NULL, 0, NULL); > + if (res) > + dev_warn(ec->dev, "Failed to add subdevices: %d\n", res); 'warn' is inconsistent with 'return err'. Either do not return an error, or mark a message as an error one. And above with the restart handler has the same issue. > + return res; > +} > + > +static int ntxec_remove(struct i2c_client *client) > +{ > + if (client == poweroff_restart_client) { When it's not the case? > + poweroff_restart_client = NULL; > + pm_power_off = NULL; > + unregister_restart_handler(&ntxec_restart_handler); > + } > + > + return 0; > +} ... > +#include <linux/types.h> > + Missed struct device; struct regmap; here. > +struct ntxec { > + struct device *dev; > + struct regmap *regmap; > +}; > +/* > + * Some registers, such as the battery status register (0x41), are in > + * big-endian, but others only have eight significant bits, which are in the > + * first byte transmitted over I2C (the MSB of the big-endian value). > + * This convenience function converts an 8-bit value to 16-bit for use in the > + * second kind of register. > + */ > +static inline u16 ntxec_reg8(u8 value) > +{ > + return value << 8; > +} I'm wondering why __be16 is not used as returned type. -- With Best Regards, Andy Shevchenko
WARNING: multiple messages have this Message-ID (diff)
From: Andy Shevchenko <andy.shevchenko@gmail.com> To: "Jonathan Neuschäfer" <j.neuschaefer@gmx.net> Cc: "Alexandre Belloni" <alexandre.belloni@bootlin.com>, "Heiko Stuebner" <heiko@sntech.de>, linux-pwm@vger.kernel.org, "Linus Walleij" <linus.walleij@linaro.org>, "Thierry Reding" <thierry.reding@gmail.com>, "Fabio Estevam" <festevam@gmail.com>, "open list:REAL TIME CLOCK (RTC) SUBSYSTEM" <linux-rtc@vger.kernel.org>, "Arnd Bergmann" <arnd@arndb.de>, "Mauro Carvalho Chehab" <mchehab+huawei@kernel.org>, "Sam Ravnborg" <sam@ravnborg.org>, "Daniel Palmer" <daniel@0x0f.com>, "Andreas Kemnade" <andreas@kemnade.info>, "NXP Linux Team" <linux-imx@nxp.com>, "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>, devicetree <devicetree@vger.kernel.org>, "Stephan Gerhold" <stephan@gerhold.net>, allen <allen.chen@ite.com.tw>, "Sascha Hauer" <s.hauer@pengutronix.de>, "Lubomir Rintel" <lkundrak@v3.sk>, "Rob Herring" <robh+dt@kernel.org>, "Lee Jones" <lee.jones@linaro.org>, "linux-arm Mailing List" <linux-arm-kernel@lists.infradead.org>, "Alessandro Zummo" <a.zummo@towertech.it>, "Linux Kernel Mailing List" <linux-kernel@vger.kernel.org>, "Mark Brown" <broonie@kernel.org>, "Pengutronix Kernel Team" <kernel@pengutronix.de>, "Heiko Stuebner" <heiko.stuebner@theobroma-systems.com>, "Josua Mayer" <josua.mayer@jm0.eu>, "Shawn Guo" <shawnguo@kernel.org>, "David S. Miller" <davem@davemloft.net> Subject: Re: [PATCH v3 3/7] mfd: Add base driver for Netronix embedded controller Date: Fri, 25 Sep 2020 12:29:45 +0300 [thread overview] Message-ID: <CAHp75VdUHPsuvDPLnfP9sM2p1FDiCsjkCf1SSM-y02ZsQxSDWQ@mail.gmail.com> (raw) In-Reply-To: <20200924192455.2484005-4-j.neuschaefer@gmx.net> On Thu, Sep 24, 2020 at 10:26 PM Jonathan Neuschäfer <j.neuschaefer@gmx.net> wrote: > > The Netronix embedded controller is a microcontroller found in some > e-book readers designed by the ODM Netronix, Inc. It contains RTC, > battery monitoring, system power management, and PWM functionality. > > This driver implements register access and version detection. > > Third-party hardware documentation is available at: > > https://github.com/neuschaefer/linux/wiki/Netronix-MSP430-embedded-controller > > The EC supports interrupts, but the driver doesn't make use of them so > far. ... > +#include <asm/unaligned.h> This usually goes after linux/*.h (and actually not visible how it's being used, but see below first) > +#include <linux/delay.h> > +#include <linux/errno.h> > +#include <linux/i2c.h> > +#include <linux/mfd/core.h> > +#include <linux/mfd/ntxec.h> > +#include <linux/module.h> > +#include <linux/pm.h> > +#include <linux/reboot.h> > +#include <linux/regmap.h> > +#include <linux/types.h> ... > +static void ntxec_poweroff(void) > +{ > + int res; > + u8 buf[] = { > + NTXEC_REG_POWEROFF, > + (NTXEC_POWEROFF_VALUE >> 8) & 0xff, > + NTXEC_POWEROFF_VALUE & 0xff, '& 0xff' parts are redundant. *u8 implies that. Fix in all cases. Also I would rather see something like buf[0] = _POWEROFF; put_unaligned_be16(_VALUE, &buf[1]); to explicitly show the endianess of the register values. > + }; > + struct i2c_msg msgs[] = { > + { > + .addr = poweroff_restart_client->addr, > + .flags = 0, > + .len = sizeof(buf), > + .buf = buf It's slightly better to keep trailing commas in cases like this. > + } > + }; > + > + res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs)); > + if (res < 0) > + dev_alert(&poweroff_restart_client->dev, > + "Failed to power off (err = %d)\n", res); alert? This needs to be explained. > + /* > + * The time from the register write until the host CPU is powered off > + * has been observed to be about 2.5 to 3 seconds. Sleep long enough to > + * safely avoid returning from the poweroff handler. > + */ > + msleep(5000); > +} > + > +static int ntxec_restart(struct notifier_block *nb, > + unsigned long action, void *data) > +{ > + int res; > + /* > + * NOTE: The lower half of the reset value is not sent, because sending > + * it causes an error Why? Any root cause? Perhaps you need to send 0xffff ? > + */ > + u8 buf[] = { > + NTXEC_REG_RESET, > + (NTXEC_RESET_VALUE >> 8) & 0xff, Here you may still use put_unaligned_be16() but move the comment to be before len hardcoded to sizeof(buf) - 1. > + }; > + struct i2c_msg msgs[] = { > + { > + .addr = poweroff_restart_client->addr, > + .flags = 0, > + .len = sizeof(buf), > + .buf = buf > + } > + }; > + > + res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs)); > + if (res < 0) > + dev_alert(&poweroff_restart_client->dev, > + "Failed to restart (err = %d)\n", res); > + > + return NOTIFY_DONE; > +} ... > +static int ntxec_probe(struct i2c_client *client) > +{ > + struct ntxec *ec; > + unsigned int version; > + int res; > + > + ec = devm_kmalloc(&client->dev, sizeof(*ec), GFP_KERNEL); > + if (!ec) > + return -ENOMEM; > + > + ec->dev = &client->dev; > + > + ec->regmap = devm_regmap_init_i2c(client, ®map_config); > + if (IS_ERR(ec->regmap)) { > + dev_err(ec->dev, "Failed to set up regmap for device\n"); > + return res; > + } > + > + /* Determine the firmware version */ > + res = regmap_read(ec->regmap, NTXEC_REG_VERSION, &version); > + if (res < 0) { > + dev_err(ec->dev, "Failed to read firmware version number\n"); > + return res; > + } > + dev_info(ec->dev, > + "Netronix embedded controller version %04x detected.\n", > + version); This info level may confuse users if followed by an error path. > + /* Bail out if we encounter an unknown firmware version */ > + switch (version) { > + case 0xd726: /* found in Kobo Aura */ > + break; > + default: > + return -ENODEV; > + } > + > + if (of_device_is_system_power_controller(ec->dev->of_node)) { > + /* > + * Set the 'powerkeep' bit. This is necessary on some boards > + * in order to keep the system running. > + */ > + res = regmap_write(ec->regmap, NTXEC_REG_POWERKEEP, > + NTXEC_POWERKEEP_VALUE); > + if (res < 0) > + return res; > + WARN_ON(poweroff_restart_client); WARN_ON? All these alerts, WARNs, BUGs must be explained. Screaming to the user is not good if it wasn't justified. > + poweroff_restart_client = client; > + if (pm_power_off) > + dev_err(ec->dev, "pm_power_off already assigned\n"); > + else > + pm_power_off = ntxec_poweroff; > + > + res = register_restart_handler(&ntxec_restart_handler); > + if (res) > + dev_err(ec->dev, > + "Failed to register restart handler: %d\n", res); > + } > + > + i2c_set_clientdata(client, ec); > + > + res = devm_mfd_add_devices(ec->dev, PLATFORM_DEVID_NONE, ntxec_subdevices, > + ARRAY_SIZE(ntxec_subdevices), NULL, 0, NULL); > + if (res) > + dev_warn(ec->dev, "Failed to add subdevices: %d\n", res); 'warn' is inconsistent with 'return err'. Either do not return an error, or mark a message as an error one. And above with the restart handler has the same issue. > + return res; > +} > + > +static int ntxec_remove(struct i2c_client *client) > +{ > + if (client == poweroff_restart_client) { When it's not the case? > + poweroff_restart_client = NULL; > + pm_power_off = NULL; > + unregister_restart_handler(&ntxec_restart_handler); > + } > + > + return 0; > +} ... > +#include <linux/types.h> > + Missed struct device; struct regmap; here. > +struct ntxec { > + struct device *dev; > + struct regmap *regmap; > +}; > +/* > + * Some registers, such as the battery status register (0x41), are in > + * big-endian, but others only have eight significant bits, which are in the > + * first byte transmitted over I2C (the MSB of the big-endian value). > + * This convenience function converts an 8-bit value to 16-bit for use in the > + * second kind of register. > + */ > +static inline u16 ntxec_reg8(u8 value) > +{ > + return value << 8; > +} I'm wondering why __be16 is not used as returned type. -- With Best Regards, Andy Shevchenko _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
next prev parent reply other threads:[~2020-09-25 9:30 UTC|newest] Thread overview: 62+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-09-24 19:24 [PATCH v3 0/7] Netronix embedded controller driver for Kobo and Tolino ebook readers Jonathan Neuschäfer 2020-09-24 19:24 ` Jonathan Neuschäfer 2020-09-24 19:24 ` [PATCH v3 1/7] dt-bindings: Add vendor prefix for Netronix, Inc Jonathan Neuschäfer 2020-09-24 19:24 ` Jonathan Neuschäfer 2020-10-12 7:19 ` Uwe Kleine-König 2020-10-12 7:19 ` Uwe Kleine-König 2020-10-12 11:48 ` Lee Jones 2020-10-12 11:48 ` Lee Jones 2020-09-24 19:24 ` [PATCH v3 2/7] dt-bindings: mfd: Add binding for Netronix embedded controller Jonathan Neuschäfer 2020-09-24 19:24 ` Jonathan Neuschäfer 2020-09-29 18:52 ` Rob Herring 2020-09-29 18:52 ` Rob Herring 2020-09-24 19:24 ` [PATCH v3 3/7] mfd: Add base driver " Jonathan Neuschäfer 2020-09-24 19:24 ` Jonathan Neuschäfer 2020-09-25 9:29 ` Andy Shevchenko [this message] 2020-09-25 9:29 ` Andy Shevchenko 2020-09-25 21:32 ` Jonathan Neuschäfer 2020-09-25 21:32 ` Jonathan Neuschäfer 2020-09-29 16:37 ` Andy Shevchenko 2020-09-29 16:37 ` Andy Shevchenko 2020-10-02 22:54 ` Jonathan Neuschäfer 2020-10-02 22:54 ` Jonathan Neuschäfer 2020-09-24 19:24 ` [PATCH v3 4/7] pwm: ntxec: Add driver for PWM function in Netronix EC Jonathan Neuschäfer 2020-09-24 19:24 ` Jonathan Neuschäfer 2020-09-25 6:30 ` Uwe Kleine-König 2020-09-25 6:30 ` Uwe Kleine-König 2020-09-27 21:10 ` Jonathan Neuschäfer 2020-09-27 21:10 ` Jonathan Neuschäfer 2020-09-28 8:35 ` Uwe Kleine-König 2020-09-28 8:35 ` Uwe Kleine-König 2020-10-02 23:36 ` Jonathan Neuschäfer 2020-10-02 23:36 ` Jonathan Neuschäfer 2020-10-03 8:17 ` Andy Shevchenko 2020-10-03 8:17 ` Andy Shevchenko 2020-09-24 19:24 ` [PATCH v3 5/7] rtc: New driver for RTC in Netronix embedded controller Jonathan Neuschäfer 2020-09-24 19:24 ` Jonathan Neuschäfer 2020-09-24 20:40 ` Andreas Kemnade 2020-09-24 20:40 ` Andreas Kemnade 2020-10-02 23:41 ` Jonathan Neuschäfer 2020-10-02 23:41 ` Jonathan Neuschäfer 2020-09-25 5:44 ` Uwe Kleine-König 2020-09-25 5:44 ` Uwe Kleine-König 2020-10-04 1:43 ` Jonathan Neuschäfer 2020-10-04 1:43 ` Jonathan Neuschäfer 2020-10-04 8:42 ` Alexandre Belloni 2020-10-04 8:42 ` Alexandre Belloni 2020-10-11 19:09 ` Jonathan Neuschäfer 2020-10-11 19:09 ` Jonathan Neuschäfer 2020-10-05 7:35 ` Uwe Kleine-König 2020-10-05 7:35 ` Uwe Kleine-König 2020-09-25 9:36 ` Alexandre Belloni 2020-09-25 9:36 ` Alexandre Belloni 2020-09-25 22:00 ` Jonathan Neuschäfer 2020-09-25 22:00 ` Jonathan Neuschäfer 2020-09-24 19:24 ` [PATCH v3 6/7] MAINTAINERS: Add entry for " Jonathan Neuschäfer 2020-09-24 19:24 ` Jonathan Neuschäfer 2020-09-25 5:08 ` [PATCH v3 7/7] ARM: dts: imx50-kobo-aura: Add " Jonathan Neuschäfer 2020-09-25 5:08 ` Jonathan Neuschäfer 2020-10-07 7:46 ` Krzysztof Kozlowski 2020-10-07 7:46 ` Krzysztof Kozlowski 2020-10-11 19:42 ` Jonathan Neuschäfer 2020-10-11 19:42 ` Jonathan Neuschäfer
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=CAHp75VdUHPsuvDPLnfP9sM2p1FDiCsjkCf1SSM-y02ZsQxSDWQ@mail.gmail.com \ --to=andy.shevchenko@gmail.com \ --cc=a.zummo@towertech.it \ --cc=alexandre.belloni@bootlin.com \ --cc=allen.chen@ite.com.tw \ --cc=andreas@kemnade.info \ --cc=arnd@arndb.de \ --cc=broonie@kernel.org \ --cc=daniel@0x0f.com \ --cc=davem@davemloft.net \ --cc=devicetree@vger.kernel.org \ --cc=festevam@gmail.com \ --cc=heiko.stuebner@theobroma-systems.com \ --cc=heiko@sntech.de \ --cc=j.neuschaefer@gmx.net \ --cc=josua.mayer@jm0.eu \ --cc=kernel@pengutronix.de \ --cc=lee.jones@linaro.org \ --cc=linus.walleij@linaro.org \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-imx@nxp.com \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-pwm@vger.kernel.org \ --cc=linux-rtc@vger.kernel.org \ --cc=lkundrak@v3.sk \ --cc=mchehab+huawei@kernel.org \ --cc=robh+dt@kernel.org \ --cc=s.hauer@pengutronix.de \ --cc=sam@ravnborg.org \ --cc=shawnguo@kernel.org \ --cc=stephan@gerhold.net \ --cc=thierry.reding@gmail.com \ --cc=u.kleine-koenig@pengutronix.de \ /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: linkBe 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.