linux-fbdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Bruno Prémont" <bonbons@linux-vserver.org>
To: Jiri Kosina <jkosina@suse.cz>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>,
	linux-input@vger.kernel.org, linux-usb@vger.kernel.org,
	linux-fbdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	"Rick L. Vinyard Jr." <rvinyard@cs.nmsu.edu>,
	Nicu Pavel <npavel@ituner.com>, Oliver Neukum <oliver@neukum.org>,
	Jaya Kumar <jayakumar.lkml@gmail.com>
Subject: [PATCH v6 6/8] hid: add experimental access to PicoLCD device's
Date: Tue, 30 Mar 2010 20:38:09 +0000	[thread overview]
Message-ID: <20100330223809.205324be@neptune.home> (raw)
In-Reply-To: <20100330223224.18fe4f3e@neptune.home>

The PicoLCD device has a small amount of EEPROM and also provides access
to its FLASH where firmware and splash image are saved.
In flasher mode FLASH access is the only active feature.

Give read/write access to both via debugfs files.

NOTE: EEPROM and FLASH access should be switched to better suited API,
      until then the will reside in debugfs

Changes since v3:
 - Fix return value of picolcd_debug_reset_open
Changes since v2:
 - Drop inline keyword on non-stub functions
 - Prevent interleaved flash writes via mutex
 - Add description of flash/eeprom access schematincs

Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---
 drivers/hid/Kconfig       |    2 +-
 drivers/hid/hid-picolcd.c |  439 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 423 insertions(+), 18 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 34f6593..a2ecd83 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -281,9 +281,9 @@ config HID_PICOLCD
 	  - Backlight control         (needs CONFIG_BACKLIGHT_CLASS_DEVICE)
 	  - Contrast control          (needs CONFIG_LCD_CLASS_DEVICE)
 	  - General purpose outputs   (needs CONFIG_LEDS_CLASS)
+	  - EEProm / Flash access     (via debugfs)
 	  Features that are not (yet) supported:
 	  - IR
-	  - EEProm / Flash access
 
 config HID_QUANTA
 	tristate "Quanta Optical Touch"
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c
index 9c8bbaf..fe1f772 100644
--- a/drivers/hid/hid-picolcd.c
+++ b/drivers/hid/hid-picolcd.c
@@ -169,6 +169,10 @@ struct picolcd_pending {
 struct picolcd_data {
 	struct hid_device *hdev;
 #ifdef CONFIG_DEBUG_FS
+	struct dentry *debug_reset;
+	struct dentry *debug_eeprom;
+	struct dentry *debug_flash;
+	struct mutex mutex_flash;
 	int addr_sz;
 #endif
 	u8 version[2];
@@ -1327,6 +1331,357 @@ static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
 
 #ifdef CONFIG_DEBUG_FS
 /*
+ * The "reset" file
+ */
+static int picolcd_debug_reset_show(struct seq_file *f, void *p)
+{
+	if (picolcd_fbinfo((struct picolcd_data *)f->private))
+		seq_printf(f, "all fb\n");
+	else
+		seq_printf(f, "all\n");
+	return 0;
+}
+
+static int picolcd_debug_reset_open(struct inode *inode, struct file *f)
+{
+	return single_open(f, picolcd_debug_reset_show, inode->i_private);
+}
+
+static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
+		size_t count, loff_t *ppos)
+{
+	struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
+	char buf[32];
+	size_t cnt = min(count, sizeof(buf)-1);
+	if (copy_from_user(buf, user_buf, cnt))
+		return -EFAULT;
+
+	while (cnt > 0 && (buf[cnt-1] = ' ' || buf[cnt-1] = '\n'))
+		cnt--;
+	buf[cnt] = '\0';
+	if (strcmp(buf, "all") = 0) {
+		picolcd_reset(data->hdev);
+		picolcd_fb_reset(data, 1);
+	} else if (strcmp(buf, "fb") = 0) {
+		picolcd_fb_reset(data, 1);
+	} else {
+		return -EINVAL;
+	}
+	return count;
+}
+
+static const struct file_operations picolcd_debug_reset_fops = {
+	.owner    = THIS_MODULE,
+	.open     = picolcd_debug_reset_open,
+	.read     = seq_read,
+	.llseek   = seq_lseek,
+	.write    = picolcd_debug_reset_write,
+	.release  = single_release,
+};
+
+/*
+ * The "eeprom" file
+ */
+static int picolcd_debug_eeprom_open(struct inode *i, struct file *f)
+{
+	f->private_data = i->i_private;
+	return 0;
+}
+
+static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
+		size_t s, loff_t *off)
+{
+	struct picolcd_data *data = f->private_data;
+	struct picolcd_pending *resp;
+	u8 raw_data[3];
+	ssize_t ret = -EIO;
+
+	if (s = 0)
+		return -EINVAL;
+	if (*off > 0x0ff)
+		return 0;
+
+	/* prepare buffer with info about what we want to read (addr & len) */
+	raw_data[0] = *off & 0xff;
+	raw_data[1] = (*off >> 8) && 0xff;
+	raw_data[2] = s < 20 ? s : 20;
+	if (*off + raw_data[2] > 0xff)
+		raw_data[2] = 0x100 - *off;
+	resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
+			sizeof(raw_data));
+	if (!resp)
+		return -EIO;
+
+	if (resp->in_report && resp->in_report->id = REPORT_EE_DATA) {
+		/* successful read :) */
+		ret = resp->raw_data[2];
+		if (ret > s)
+			ret = s;
+		if (copy_to_user(u, resp->raw_data+3, ret))
+			ret = -EFAULT;
+		else
+			*off += ret;
+	} /* anything else is some kind of IO error */
+
+	kfree(resp);
+	return ret;
+}
+
+static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
+		size_t s, loff_t *off)
+{
+	struct picolcd_data *data = f->private_data;
+	struct picolcd_pending *resp;
+	ssize_t ret = -EIO;
+	u8 raw_data[23];
+
+	if (s = 0)
+		return -EINVAL;
+	if (*off > 0x0ff)
+		return -ENOSPC;
+
+	memset(raw_data, 0, sizeof(raw_data));
+	raw_data[0] = *off & 0xff;
+	raw_data[1] = (*off >> 8) && 0xff;
+	raw_data[2] = s < 20 ? s : 20;
+	if (*off + raw_data[2] > 0xff)
+		raw_data[2] = 0x100 - *off;
+
+	if (copy_from_user(raw_data+3, u, raw_data[2]))
+		return -EFAULT;
+	resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
+			sizeof(raw_data));
+
+	if (!resp)
+		return -EIO;
+
+	if (resp->in_report && resp->in_report->id = REPORT_EE_DATA) {
+		/* check if written data matches */
+		if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) = 0) {
+			*off += raw_data[2];
+			ret = raw_data[2];
+		}
+	}
+	kfree(resp);
+	return ret;
+}
+
+/*
+ * Notes:
+ * - read/write happens in chunks of at most 20 bytes, it's up to userspace
+ *   to loop in order to get more data.
+ * - on write errors on otherwise correct write request the bytes
+ *   that should have been written are in undefined state.
+ */
+static const struct file_operations picolcd_debug_eeprom_fops = {
+	.owner    = THIS_MODULE,
+	.open     = picolcd_debug_eeprom_open,
+	.read     = picolcd_debug_eeprom_read,
+	.write    = picolcd_debug_eeprom_write,
+	.llseek   = generic_file_llseek,
+};
+
+/*
+ * The "flash" file
+ */
+static int picolcd_debug_flash_open(struct inode *i, struct file *f)
+{
+	f->private_data = i->i_private;
+	return 0;
+}
+
+/* record a flash address to buf (bounds check to be done by caller) */
+static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
+{
+	buf[0] = off & 0xff;
+	buf[1] = (off >> 8) & 0xff;
+	if (data->addr_sz = 3)
+		buf[2] = (off >> 16) & 0xff;
+	return data->addr_sz = 2 ? 2 : 3;
+}
+
+/* read a given size of data (bounds check to be done by caller) */
+static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
+		char __user *u, size_t s, loff_t *off)
+{
+	struct picolcd_pending *resp;
+	u8 raw_data[4];
+	ssize_t ret = 0;
+	int len_off, err = -EIO;
+
+	while (s > 0) {
+		err = -EIO;
+		len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+		raw_data[len_off] = s > 32 ? 32 : s;
+		resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
+		if (!resp || !resp->in_report)
+			goto skip;
+		if (resp->in_report->id = REPORT_MEMORY ||
+			resp->in_report->id = REPORT_BL_READ_MEMORY) {
+			if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
+				goto skip;
+			if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
+				err = -EFAULT;
+				goto skip;
+			}
+			*off += raw_data[len_off];
+			s    -= raw_data[len_off];
+			ret  += raw_data[len_off];
+			err   = 0;
+		}
+skip:
+		kfree(resp);
+		if (err)
+			return ret > 0 ? ret : err;
+	}
+	return ret;
+}
+
+static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
+		size_t s, loff_t *off)
+{
+	struct picolcd_data *data = f->private_data;
+
+	if (s = 0)
+		return -EINVAL;
+	if (*off > 0x05fff)
+		return 0;
+	if (*off + s > 0x05fff)
+		s = 0x06000 - *off;
+
+	if (data->status & PICOLCD_BOOTLOADER)
+		return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
+	else
+		return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
+}
+
+/* erase block aligned to 64bytes boundary */
+static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
+		loff_t *off)
+{
+	struct picolcd_pending *resp;
+	u8 raw_data[3];
+	int len_off;
+	ssize_t ret = -EIO;
+
+	if (*off & 0x3f)
+		return -EINVAL;
+
+	len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+	resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
+	if (!resp || !resp->in_report)
+		goto skip;
+	if (resp->in_report->id = REPORT_MEMORY ||
+		resp->in_report->id = REPORT_BL_ERASE_MEMORY) {
+		if (memcmp(raw_data, resp->raw_data, len_off) != 0)
+			goto skip;
+		ret = 0;
+	}
+skip:
+	kfree(resp);
+	return ret;
+}
+
+/* write a given size of data (bounds check to be done by caller) */
+static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
+		const char __user *u, size_t s, loff_t *off)
+{
+	struct picolcd_pending *resp;
+	u8 raw_data[36];
+	ssize_t ret = 0;
+	int len_off, err = -EIO;
+
+	while (s > 0) {
+		err = -EIO;
+		len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+		raw_data[len_off] = s > 32 ? 32 : s;
+		if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
+			err = -EFAULT;
+			goto skip;
+		}
+		resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
+				len_off+1+raw_data[len_off]);
+		if (!resp || !resp->in_report)
+			goto skip;
+		if (resp->in_report->id = REPORT_MEMORY ||
+			resp->in_report->id = REPORT_BL_WRITE_MEMORY) {
+			if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
+				goto skip;
+			*off += raw_data[len_off];
+			s    -= raw_data[len_off];
+			ret  += raw_data[len_off];
+			err   = 0;
+		}
+skip:
+		kfree(resp);
+		if (err)
+			break;
+	}
+	return ret > 0 ? ret : err;
+}
+
+static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
+		size_t s, loff_t *off)
+{
+	struct picolcd_data *data = f->private_data;
+	ssize_t err, ret = 0;
+	int report_erase, report_write;
+
+	if (s = 0)
+		return -EINVAL;
+	if (*off > 0x5fff)
+		return -ENOSPC;
+	if (s & 0x3f)
+		return -EINVAL;
+	if (*off & 0x3f)
+		return -EINVAL;
+
+	if (data->status & PICOLCD_BOOTLOADER) {
+		report_erase = REPORT_BL_ERASE_MEMORY;
+		report_write = REPORT_BL_WRITE_MEMORY;
+	} else {
+		report_erase = REPORT_ERASE_MEMORY;
+		report_write = REPORT_WRITE_MEMORY;
+	}
+	mutex_lock(&data->mutex_flash);
+	while (s > 0) {
+		err = _picolcd_flash_erase64(data, report_erase, off);
+		if (err)
+			break;
+		err = _picolcd_flash_write(data, report_write, u, 64, off);
+		if (err < 0)
+			break;
+		ret += err;
+		*off += err;
+		s -= err;
+		if (err != 64)
+			break;
+	}
+	mutex_unlock(&data->mutex_flash);
+	return ret > 0 ? ret : err;
+}
+
+/*
+ * Notes:
+ * - concurrent writing is prevented by mutex and all writes must be
+ *   n*64 bytes and 64-byte aligned, each write being preceeded by an
+ *   ERASE which erases a 64byte block.
+ *   If less than requested was written or an error is returned for an
+ *   otherwise correct write request the next 64-byte block which should
+ *   have been written is in undefined state (mostly: original, erased,
+ *   (half-)written with write error)
+ * - reading can happend without special restriction
+ */
+static const struct file_operations picolcd_debug_flash_fops = {
+	.owner    = THIS_MODULE,
+	.open     = picolcd_debug_flash_open,
+	.read     = picolcd_debug_flash_read,
+	.write    = picolcd_debug_flash_write,
+	.llseek   = generic_file_llseek,
+};
+
+
+/*
  * Helper code for HID report level dumping/debugging
  */
 static const char *error_codes[] = {
@@ -1801,9 +2156,66 @@ static void picolcd_debug_raw_event(struct picolcd_data *data,
 	wake_up_interruptible(&hdev->debug_wait);
 	kfree(buff);
 }
+
+static void picolcd_init_devfs(struct picolcd_data *data,
+		struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+		struct hid_report *flash_r, struct hid_report *flash_w,
+		struct hid_report *reset)
+{
+	struct hid_device *hdev = data->hdev;
+
+	mutex_init(&data->mutex_flash);
+
+	/* reset */
+	if (reset)
+		data->debug_reset = debugfs_create_file("reset", 0600,
+				hdev->debug_dir, data, &picolcd_debug_reset_fops);
+
+	/* eeprom */
+	if (eeprom_r || eeprom_w)
+		data->debug_eeprom = debugfs_create_file("eeprom",
+			(eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
+			hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
+
+	/* flash */
+	if (flash_r && flash_r->maxfield = 1 && flash_r->field[0]->report_size = 8)
+		data->addr_sz = flash_r->field[0]->report_count - 1;
+	else
+		data->addr_sz = -1;
+	if (data->addr_sz = 2 || data->addr_sz = 3) {
+		data->debug_flash = debugfs_create_file("flash",
+			(flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
+			hdev->debug_dir, data, &picolcd_debug_flash_fops);
+	} else if (flash_r || flash_w)
+		dev_warn(&hdev->dev, "Unexpected FLASH access reports, "
+				"please submit rdesc for review\n");
+}
+
+static void picolcd_exit_devfs(struct picolcd_data *data)
+{
+	struct dentry *dent;
+
+	dent = data->debug_reset;
+	data->debug_reset = NULL;
+	if (dent)
+		debugfs_remove(dent);
+	dent = data->debug_eeprom;
+	data->debug_eeprom = NULL;
+	if (dent)
+		debugfs_remove(dent);
+	dent = data->debug_flash;
+	data->debug_flash = NULL;
+	if (dent)
+		debugfs_remove(dent);
+	mutex_destroy(&data->mutex_flash);
+}
 #else
 #define picolcd_debug_raw_event(data, hdev, report, raw_data, size)
-#endif
+#define picolcd_init_devfs(data, eeprom_r, eeprom_w, flash_r, flash_w, reset)
+static void picolcd_exit_devfs(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
 
 /*
  * Handle raw report as sent by device
@@ -1913,7 +2325,6 @@ static inline void picolcd_exit_cir(struct picolcd_data *data)
 
 static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
 {
-	struct hid_report *report;
 	int error;
 
 	error = picolcd_check_version(hdev);
@@ -1955,13 +2366,11 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
 	if (error)
 		goto err;
 
-#ifdef CONFIG_DEBUG_FS
-	report = picolcd_out_report(REPORT_READ_MEMORY, hdev);
-	if (report && report->maxfield = 1 && report->field[0]->report_size = 8)
-		data->addr_sz = report->field[0]->report_count - 1;
-	else
-		data->addr_sz = -1;
-#endif
+	picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
+			picolcd_out_report(REPORT_EE_WRITE, hdev),
+			picolcd_out_report(REPORT_READ_MEMORY, hdev),
+			picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
+			picolcd_out_report(REPORT_RESET, hdev));
 	return 0;
 err:
 	picolcd_exit_leds(data);
@@ -1975,7 +2384,6 @@ err:
 
 static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
 {
-	struct hid_report *report;
 	int error;
 
 	error = picolcd_check_version(hdev);
@@ -1987,13 +2395,9 @@ static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data
 				"please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
 				dev_name(&hdev->dev));
 
-#ifdef CONFIG_DEBUG_FS
-	report = picolcd_out_report(REPORT_BL_READ_MEMORY, hdev);
-	if (report && report->maxfield = 1 && report->field[0]->report_size = 8)
-		data->addr_sz = report->field[0]->report_count - 1;
-	else
-		data->addr_sz = -1;
-#endif
+	picolcd_init_devfs(data, NULL, NULL,
+			picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
+			picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
 	return 0;
 }
 
@@ -2086,6 +2490,7 @@ static void picolcd_remove(struct hid_device *hdev)
 	data->status |= PICOLCD_FAILED;
 	spin_unlock_irqrestore(&data->lock, flags);
 
+	picolcd_exit_devfs(data);
 	device_remove_file(&hdev->dev, &dev_attr_operation_mode);
 	hdev->ll_driver->close(hdev);
 	hid_hw_stop(hdev);
-- 
1.6.4.4


  parent reply	other threads:[~2010-03-30 20:38 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-03-24 22:37 [PATCH v3 0/6] hid: new driver for PicoLCD device Bruno Prémont
2010-03-24 22:49 ` [PATCH v3 2/6] hid: add framebuffer support to " Bruno Prémont
2010-03-27  0:22   ` [PATCH v4 " Bruno Prémont
2010-03-29 20:30     ` [PATCH v5 " Bruno Prémont
2010-03-24 22:51 ` [PATCH v3 3/6] hid: add backlight " Bruno Prémont
2010-03-24 22:54 ` [PATCH v3 4/6] hid: add lcd " Bruno Prémont
     [not found] ` <20100324233707.7243b04d-hY15tx4IgV39zxVx7UNMDg@public.gmane.org>
2010-03-24 22:40   ` [PATCH v3 1/6] hid: new driver for " Bruno Prémont
2010-03-26  6:56     ` Dmitry Torokhov
2010-03-26  9:29       ` Bruno Prémont
2010-03-26 20:59         ` Jiri Kosina
2010-03-26 21:16           ` Dmitry Torokhov
2010-03-26 21:39             ` Bruno Prémont
2010-03-27  0:22           ` [PATCH v4 " Bruno Prémont
2010-03-29  9:47             ` Jiri Kosina
2010-03-29 10:16               ` Bruno Prémont
2010-03-30  8:12                 ` Jiri Kosina
2010-03-30 20:32                   ` [PATCH v6 0/8] " Bruno Prémont
2010-03-30 20:33                     ` [PATCH v6 1/8] " Bruno Prémont
2010-04-01 16:58                       ` Oliver Neukum
2010-04-25 19:29                         ` [PATCH] hid: split picolcd's operation_mode sysfs attribute Bruno Prémont
2010-04-27 13:32                           ` Jiri Kosina
2010-03-30 20:34                     ` [PATCH v6 2/8] hid: add framebuffer support to PicoLCD device Bruno Prémont
     [not found]                     ` <20100330223224.18fe4f3e-hY15tx4IgV39zxVx7UNMDg@public.gmane.org>
2010-03-30 20:35                       ` [PATCH v6 3/8] hid: add backlight " Bruno Prémont
2010-03-30 20:36                     ` [PATCH v6 4/8] hid: add lcd " Bruno Prémont
2010-03-30 20:36                     ` [PATCH v6 5/8] hid: add GPO (leds) " Bruno Prémont
2010-03-30 20:38                     ` Bruno Prémont [this message]
2010-03-30 20:42                     ` [PATCH v6 7/8, needs improvement] hid: add suspend/resume hooks for Bruno Prémont
2010-03-31 12:15                       ` [PATCH v6 7/8, needs improvement] hid: add suspend/resume hooks Jiri Kosina
2010-03-30 20:43                     ` [PATCH v6 8/8] hid: add PM support to PicoLCD device Bruno Prémont
2010-03-31  9:28                     ` [PATCH v6 0/8] hid: new driver for " Jiri Kosina
2010-03-24 22:55   ` [PATCH v3 5/6] hid: add GPO (leds) support to " Bruno Prémont
2010-03-24 22:58   ` [PATCH v3 6/6] hid: add experimental access to PicoLCD device's Bruno Prémont
2010-03-29  9:44     ` Jiri Kosina
     [not found]       ` <alpine.LNX.2.00.1003291142400.24576-ztGlSCb7Y1iN3ZZ/Hiejyg@public.gmane.org>
2010-03-29 20:34         ` [PATCH v4 " Bruno Prémont

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=20100330223809.205324be@neptune.home \
    --to=bonbons@linux-vserver.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=jayakumar.lkml@gmail.com \
    --cc=jkosina@suse.cz \
    --cc=linux-fbdev@vger.kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=npavel@ituner.com \
    --cc=oliver@neukum.org \
    --cc=rvinyard@cs.nmsu.edu \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).