From: Ivo van Doorn <ivdoorn@gmail.com>
To: netdev@vger.kernel.org
Cc: rt2x00-devel@lfcorreia.dyndns.org
Subject: [PATCH 18/32] rt2x00: Make sure device has reached requested state while suspend/resume
Date: Fri, 28 Apr 2006 00:03:10 +0200 [thread overview]
Message-ID: <200604280003.10710.IvDoorn@gmail.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 17190 bytes --]
From: Ivo van Doorn <IvDoorn@gmail.com>
Add the *_set_state functions which makes sure
the device is switching state to awake or sleep.
Fix bad behaviour in the suspend routine,
and disable the radio before suspending.
Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2400pci.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2400pci.c 2006-04-27 21:49:08.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2400pci.c 2006-04-27 21:49:59.000000000 +0200
@@ -653,6 +653,44 @@ rt2400pci_link_tuner(struct rt2x00_pci *
}
/*
+ * Device state switch.
+ * This will put the device to sleep, or awake it.
+ */
+static int
+rt2400pci_set_state(struct rt2x00_pci *rt2x00pci, enum dev_state state)
+{
+ u32 reg;
+ int counter;
+ char put_to_sleep;
+
+ put_to_sleep = (state != STATE_AWAKE);
+
+ rt2x00_register_read(rt2x00pci, PWRCSR1, ®);
+ rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1);
+ rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state);
+ rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state);
+ rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep);
+ rt2x00_register_write(rt2x00pci, PWRCSR1, reg);
+
+ /*
+ * Device is not guarenteed to be in the requested state yet.
+ * We must wait untill the register indicates that the
+ * device has entered the correct state.
+ */
+ for (counter = 0; counter < REGISTER_BUSY_COUNT; counter++) {
+ rt2x00_register_read(rt2x00pci, PWRCSR1, ®);
+ if ((rt2x00_get_field32(reg, PWRCSR1_BBP_CURR_STATE) == state)
+ && (rt2x00_get_field32(reg, PWRCSR1_RF_CURR_STATE) == state))
+ return 0;
+ msleep(10);
+ }
+
+ NOTICE("Device failed to %s.\n" , put_to_sleep ? "suspend" : "resume");
+
+ return -EBUSY;
+}
+
+/*
* TX descriptor initialization
*/
static void
@@ -1190,6 +1228,9 @@ rt2400pci_init_registers(struct rt2x00_p
{
u32 reg;
+ if (rt2400pci_set_state(rt2x00pci, STATE_AWAKE))
+ return -EBUSY;
+
rt2x00_register_write(rt2x00pci, PWRCSR0, cpu_to_le32(0x3f3b3100));
rt2x00_register_write(rt2x00pci, PSCSR0, cpu_to_le32(0x00020002));
@@ -2342,57 +2383,42 @@ rt2400pci_remove(struct pci_dev *pci_dev
}
#ifdef CONFIG_PM
-static int rt2400pci_resume(struct pci_dev *pci_dev);
-
static int
rt2400pci_suspend(struct pci_dev *pci_dev, pm_message_t state)
{
struct net_device *net_dev = pci_get_drvdata(pci_dev);
struct rt2x00_pci *rt2x00pci = ieee80211_dev_hw_data(net_dev);
- int counter;
- u32 reg;
NOTICE("Going to sleep.\n");
- if (ieee80211_netif_oper(net_dev, NETIF_DETACH))
- return -EBUSY;
-
- rt2x00_register_read(rt2x00pci, PWRCSR1, ®);
- rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1);
- rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, 1);
- rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, 1);
- rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, 1);
- rt2x00_register_write(rt2x00pci, PWRCSR1, reg);
-
/*
- * Device is not guarenteed to be asleep yet.
- * We must wait untill the register indicates
- * device has been correctly put to sleep.
+ * If radio was enabled, stop radio and
+ * set the resume flag to the radio will be enabled
+ * when resuming.
*/
- for (counter = 0; counter < REGISTER_BUSY_COUNT; counter++) {
- rt2x00_register_read(rt2x00pci, PWRCSR1, ®);
- if ((rt2x00_get_field32(reg, PWRCSR1_BBP_CURR_STATE) == 1)
- && (rt2x00_get_field32(reg, PWRCSR1_RF_CURR_STATE) == 1))
- break;
- NOTICE("Waiting for device to sleep.\n");
- msleep(10);
+ if (GET_FLAG(rt2x00pci, RADIO_ENABLED)) {
+ if (net_dev->stop(net_dev))
+ return -EBUSY;
+ SET_FLAG(rt2x00pci, RADIO_RESUME);
}
- if (counter == REGISTER_BUSY_COUNT)
- goto exit;
-
- if (pci_enable_wake(pci_dev, pci_choose_state(pci_dev, state), 1)
- || pci_save_state(pci_dev)
- || pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)))
- goto exit;
-
- return 0;
+ /*
+ * Set device mode to sleep for power management.
+ */
+ if (rt2400pci_set_state(rt2x00pci, STATE_SLEEP))
+ return -EBUSY;
-exit:
- ERROR("Failed to suspend device.\n");
+ /*
+ * Uninitialize hardware.
+ */
+ rt2400pci_uninitialize(net_dev);
- rt2400pci_resume(pci_dev);
- return -EIO;
+ /*
+ * Disable PCI.
+ */
+ pci_save_state(pci_dev);
+ pci_disable_device(pci_dev);
+ return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
}
static int
@@ -2400,25 +2426,44 @@ rt2400pci_resume(struct pci_dev *pci_dev
{
struct net_device *net_dev = pci_get_drvdata(pci_dev);
struct rt2x00_pci *rt2x00pci = ieee80211_dev_hw_data(net_dev);
- u32 reg;
NOTICE("Waking up.\n");
+ /*
+ * Enable PCI.
+ */
if (pci_set_power_state(pci_dev, PCI_D0)
- || pci_restore_state(pci_dev)
- || pci_enable_wake(pci_dev, PCI_D0, 0)) {
+ || pci_enable_device(pci_dev)
+ || pci_restore_state(pci_dev)) {
ERROR("Failed to resume device.\n");
return -EIO;
}
- rt2x00_register_read(rt2x00pci, PWRCSR1, ®);
- rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1);
- rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, 3);
- rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, 3);
- rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, 0);
- rt2x00_register_write(rt2x00pci, PWRCSR1, reg);
+ /*
+ * Initialize hardware.
+ */
+ if (rt2400pci_initialize(pci_dev, net_dev)) {
+ ERROR("Failed to initialize device.\n");
+ return -ENOMEM;
+ }
- return ieee80211_netif_oper(net_dev, NETIF_ATTACH);
+ /*
+ * Set device mode to awake.
+ */
+ if (rt2400pci_set_state(rt2x00pci, STATE_AWAKE))
+ return -EBUSY;
+
+ /*
+ * Only enable radio when it was enabled
+ * when we suspended.
+ */
+ if (GET_FLAG(rt2x00pci, RADIO_RESUME)) {
+ if (net_dev->open(net_dev))
+ return -EBUSY;
+ CLEAR_FLAG(rt2x00pci, RADIO_RESUME);
+ }
+
+ return 0;
}
#endif /* CONFIG_PM */
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500pci.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500pci.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500pci.c 2006-04-27 21:49:08.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500pci.c 2006-04-27 21:49:59.000000000 +0200
@@ -706,6 +706,44 @@ dynamic_cca_tune:
}
/*
+ * Device state switch.
+ * This will put the device to sleep, or awake it.
+ */
+static int
+rt2500pci_set_state(struct rt2x00_pci *rt2x00pci, enum dev_state state)
+{
+ u32 reg;
+ int counter;
+ char put_to_sleep;
+
+ put_to_sleep = (state != STATE_AWAKE);
+
+ rt2x00_register_read(rt2x00pci, PWRCSR1, ®);
+ rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1);
+ rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state);
+ rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state);
+ rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep);
+ rt2x00_register_write(rt2x00pci, PWRCSR1, reg);
+
+ /*
+ * Device is not guarenteed to be in the requested state yet.
+ * We must wait untill the register indicates that the
+ * device has entered the correct state.
+ */
+ for (counter = 0; counter < REGISTER_BUSY_COUNT; counter++) {
+ rt2x00_register_read(rt2x00pci, PWRCSR1, ®);
+ if ((rt2x00_get_field32(reg, PWRCSR1_BBP_CURR_STATE) == state)
+ && (rt2x00_get_field32(reg, PWRCSR1_RF_CURR_STATE) == state))
+ return 0;
+ msleep(10);
+ }
+
+ NOTICE("Device failed to %s.\n" , put_to_sleep ? "suspend" : "resume");
+
+ return -EBUSY;
+}
+
+/*
* TX descriptor initialization
*/
static void
@@ -1272,6 +1310,9 @@ rt2500pci_init_registers(struct rt2x00_p
{
u32 reg;
+ if (rt2500pci_set_state(rt2x00pci, STATE_AWAKE))
+ return -EBUSY;
+
rt2x00_register_write(rt2x00pci, PWRCSR0, cpu_to_le32(0x3f3b3100));
rt2x00_register_write(rt2x00pci, PSCSR0, cpu_to_le32(0x00020002));
@@ -2636,57 +2677,42 @@ rt2500pci_remove(struct pci_dev *pci_dev
}
#ifdef CONFIG_PM
-static int rt2500pci_resume(struct pci_dev *pci_dev);
-
static int
rt2500pci_suspend(struct pci_dev *pci_dev, pm_message_t state)
{
struct net_device *net_dev = pci_get_drvdata(pci_dev);
struct rt2x00_pci *rt2x00pci = ieee80211_dev_hw_data(net_dev);
- int counter;
- u32 reg;
NOTICE("Going to sleep.\n");
- if (ieee80211_netif_oper(net_dev, NETIF_DETACH))
- return -EBUSY;
-
- rt2x00_register_read(rt2x00pci, PWRCSR1, ®);
- rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1);
- rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, 1);
- rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, 1);
- rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, 1);
- rt2x00_register_write(rt2x00pci, PWRCSR1, reg);
-
/*
- * Device is not guarenteed to be asleep yet.
- * We must wait untill the register indicates
- * device has been correctly put to sleep.
+ * If radio was enabled, stop radio and
+ * set the resume flag to the radio will be enabled
+ * when resuming.
*/
- for (counter = 0; counter < REGISTER_BUSY_COUNT; counter++) {
- rt2x00_register_read(rt2x00pci, PWRCSR1, ®);
- if ((rt2x00_get_field32(reg, PWRCSR1_BBP_CURR_STATE) == 1)
- && (rt2x00_get_field32(reg, PWRCSR1_RF_CURR_STATE) == 1))
- break;
- NOTICE("Waiting for device to sleep.\n");
- msleep(10);
+ if (GET_FLAG(rt2x00pci, RADIO_ENABLED)) {
+ if (net_dev->stop(net_dev))
+ return -EBUSY;
+ SET_FLAG(rt2x00pci, RADIO_RESUME);
}
- if (counter == REGISTER_BUSY_COUNT)
- goto exit;
-
- if (pci_enable_wake(pci_dev, pci_choose_state(pci_dev, state), 1)
- || pci_save_state(pci_dev)
- || pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)))
- goto exit;
-
- return 0;
+ /*
+ * Set device mode to sleep for power management.
+ */
+ if (rt2500pci_set_state(rt2x00pci, STATE_SLEEP))
+ return -EBUSY;
-exit:
- ERROR("Failed to suspend device.\n");
+ /*
+ * Uninitialize hardware.
+ */
+ rt2500pci_uninitialize(net_dev);
- rt2500pci_resume(pci_dev);
- return -EIO;
+ /*
+ * Disable PCI.
+ */
+ pci_save_state(pci_dev);
+ pci_disable_device(pci_dev);
+ return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
}
static int
@@ -2694,25 +2720,44 @@ rt2500pci_resume(struct pci_dev *pci_dev
{
struct net_device *net_dev = pci_get_drvdata(pci_dev);
struct rt2x00_pci *rt2x00pci = ieee80211_dev_hw_data(net_dev);
- u32 reg;
NOTICE("Waking up.\n");
+ /*
+ * Enable PCI.
+ */
if (pci_set_power_state(pci_dev, PCI_D0)
- || pci_restore_state(pci_dev)
- || pci_enable_wake(pci_dev, PCI_D0, 0)) {
+ || pci_enable_device(pci_dev)
+ || pci_restore_state(pci_dev)) {
ERROR("Failed to resume device.\n");
return -EIO;
}
- rt2x00_register_read(rt2x00pci, PWRCSR1, ®);
- rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1);
- rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, 3);
- rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, 3);
- rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, 0);
- rt2x00_register_write(rt2x00pci, PWRCSR1, reg);
+ /*
+ * Initialize hardware.
+ */
+ if (rt2500pci_initialize(pci_dev, net_dev)) {
+ ERROR("Failed to initialize device.\n");
+ return -ENOMEM;
+ }
- return ieee80211_netif_oper(net_dev, NETIF_ATTACH);
+ /*
+ * Set device mode to awake.
+ */
+ if (rt2500pci_set_state(rt2x00pci, STATE_AWAKE))
+ return -EBUSY;
+
+ /*
+ * Only enable radio when it was enabled
+ * when we suspended.
+ */
+ if (GET_FLAG(rt2x00pci, RADIO_RESUME)) {
+ if (net_dev->open(net_dev))
+ return -EBUSY;
+ CLEAR_FLAG(rt2x00pci, RADIO_RESUME);
+ }
+
+ return 0;
}
#endif /* CONFIG_PM */
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500usb.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500usb.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500usb.c 2006-04-27 21:49:08.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500usb.c 2006-04-27 21:49:59.000000000 +0200
@@ -547,6 +547,46 @@ rt2500usb_link_tuner(struct rt2x00_usb *
}
/*
+ * Device state switch.
+ * This will put the device to sleep, or awake it.
+ */
+static int
+rt2500usb_set_state(struct rt2x00_usb *rt2x00usb, enum dev_state state)
+{
+ u16 reg;
+ int counter;
+ char put_to_sleep;
+
+ put_to_sleep = (state != STATE_AWAKE);
+
+ rt2x00_register_read(rt2x00usb, MAC_CSR17, ®);
+ rt2x00_set_field16_nb(®, MAC_CSR17_SET_STATE, 1);
+ rt2x00_set_field16_nb(®, MAC_CSR17_BBP_DESIRE_STATE, state);
+ rt2x00_set_field16_nb(®, MAC_CSR17_RF_DESIRE_STATE, state);
+ rt2x00_set_field16_nb(®, MAC_CSR17_PUT_TO_SLEEP, put_to_sleep);
+ rt2x00_register_write(rt2x00usb, MAC_CSR17, reg);
+
+ /*
+ * Device is not guarenteed to be in the requested state yet.
+ * We must wait untill the register indicates that the
+ * device has entered the correct state.
+ */
+ for (counter = 0; counter < REGISTER_BUSY_COUNT; counter++) {
+ rt2x00_register_read(rt2x00usb, MAC_CSR17, ®);
+ if ((rt2x00_get_field16_nb(
+ reg, MAC_CSR17_BBP_CURR_STATE) == state)
+ && (rt2x00_get_field16_nb(
+ reg, MAC_CSR17_RF_CURR_STATE) == state))
+ return 0;
+ msleep(10);
+ }
+
+ NOTICE("Device failed to %s.\n" , put_to_sleep ? "suspend" : "resume");
+
+ return -EBUSY;
+}
+
+/*
* TX descriptor initialization
*/
static void
@@ -1082,6 +1122,9 @@ rt2500usb_init_registers(struct rt2x00_u
{
u16 reg;
+ if (rt2500usb_set_state(rt2x00usb, STATE_AWAKE))
+ return -EBUSY;
+
rt2x00_vendor_request(rt2x00usb, USB_DEVICE_MODE,
USB_VENDOR_REQUEST_OUT, USB_MODE_TEST, 0x00, NULL, 0);
rt2x00_vendor_request(rt2x00usb, USB_SINGLE_WRITE,
@@ -2221,44 +2264,42 @@ rt2500usb_disconnect(struct usb_interfac
}
#ifdef CONFIG_PM
-static int rt2500usb_resume(struct usb_interface *usb_intf);
-
static int
rt2500usb_suspend(struct usb_interface *usb_intf, pm_message_t state)
{
struct net_device *net_dev = usb_get_intfdata(usb_intf);
struct rt2x00_usb *rt2x00usb = ieee80211_dev_hw_data(net_dev);
- int counter;
- u16 reg;
NOTICE("Going to sleep.\n");
- if (ieee80211_netif_oper(net_dev, NETIF_DETACH))
+ /*
+ * If radio was enabled, stop radio and
+ * set the resume flag to the radio will be enabled
+ * when resuming.
+ */
+ if (GET_FLAG(rt2x00usb, RADIO_ENABLED)) {
+ if (net_dev->stop(net_dev))
+ return -EBUSY;
+ SET_FLAG(rt2x00usb, RADIO_RESUME);
+ }
+
+ /*
+ * Set device mode to sleep for power management.
+ */
+ if (rt2500usb_set_state(rt2x00usb, STATE_SLEEP))
return -EBUSY;
- rt2x00_register_read(rt2x00usb, MAC_CSR17, ®);
- rt2x00_set_field16_nb(®, MAC_CSR17_SET_STATE, 1);
- rt2x00_set_field16_nb(®, MAC_CSR17_BBP_DESIRE_STATE, 1);
- rt2x00_set_field16_nb(®, MAC_CSR17_RF_DESIRE_STATE, 1);
- rt2x00_set_field16_nb(®, MAC_CSR17_PUT_TO_SLEEP, 1);
- rt2x00_register_write(rt2x00usb, MAC_CSR17, reg);
+ /*
+ * Uninitialize hardware.
+ */
+ rt2500usb_uninitialize(net_dev);
/*
- * Device is not guarenteed to be asleep yet.
- * We must wait untill the register indicates
- * device has been correctly put to sleep.
+ * Decrease usbdev refcount.
*/
- for (counter = 0; counter < REGISTER_BUSY_COUNT; counter++) {
- rt2x00_register_read(rt2x00usb, MAC_CSR17, ®);
- if ((rt2x00_get_field16_nb(reg, MAC_CSR17_BBP_CURR_STATE) == 1)
- && (rt2x00_get_field16_nb(reg, MAC_CSR17_RF_CURR_STATE) == 1))
- return 0;
- NOTICE("Waiting for device to sleep.\n");
- msleep(10);
- }
+ usb_put_dev(interface_to_usbdev(usb_intf));
- rt2500usb_resume(usb_intf);
- return -EIO;
+ return 0;
}
static int
@@ -2266,18 +2307,39 @@ rt2500usb_resume(struct usb_interface *u
{
struct net_device *net_dev = usb_get_intfdata(usb_intf);
struct rt2x00_usb *rt2x00usb = ieee80211_dev_hw_data(net_dev);
- u16 reg;
NOTICE("Waking up.\n");
- rt2x00_register_read(rt2x00usb, MAC_CSR17, ®);
- rt2x00_set_field16_nb(®, MAC_CSR17_SET_STATE, 1);
- rt2x00_set_field16_nb(®, MAC_CSR17_BBP_DESIRE_STATE, 3);
- rt2x00_set_field16_nb(®, MAC_CSR17_RF_DESIRE_STATE, 3);
- rt2x00_set_field16_nb(®, MAC_CSR17_PUT_TO_SLEEP, 0);
- rt2x00_register_write(rt2x00usb, MAC_CSR17, reg);
+ /*
+ * Increase usbdev refcount.
+ */
+ usb_get_dev(interface_to_usbdev(usb_intf));
- return ieee80211_netif_oper(net_dev, NETIF_ATTACH);
+ /*
+ * Initialize hardware.
+ */
+ if (rt2500usb_initialize(usb_intf, net_dev)) {
+ ERROR("Failed to initialize device.\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Set device mode to awake.
+ */
+ if (rt2500usb_set_state(rt2x00usb, STATE_AWAKE))
+ return -EBUSY;
+
+ /*
+ * Only enable radio when it was enabled
+ * when we suspended.
+ */
+ if (GET_FLAG(rt2x00usb, RADIO_RESUME)) {
+ if (net_dev->open(net_dev))
+ return -EBUSY;
+ CLEAR_FLAG(rt2x00usb, RADIO_RESUME);
+ }
+
+ return 0;
}
#endif /* CONFIG_PM */
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
reply other threads:[~2006-04-27 22:02 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=200604280003.10710.IvDoorn@gmail.com \
--to=ivdoorn@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=rt2x00-devel@lfcorreia.dyndns.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.