linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Input: serio_raw - return proper result when serio_raw_read fails
@ 2012-02-01  8:19 Che-Liang Chiou
  2012-02-08  3:24 ` [PATCH 0/5] Input: serio_raw - add monitor/replay debugging interface Che-Liang Chiou
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Che-Liang Chiou @ 2012-02-01  8:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: Che-Liang Chiou, Dmitry Torokhov, linux-input

serio_raw_read now returns (sometimes partially) successful number of
bytes transferred to the caller, and only returns error code to the
caller on completely failed transfers.

Signed-off-by: Che-Liang Chiou <clchiou@chromium.org>
---

I think serio_raw_write() and serio_raw_read() should have the same returning
value behavior.

---
 drivers/input/serio/serio_raw.c |   15 +++++++++------
 1 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index 8250299..4494233 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -164,7 +164,8 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer,
 	struct serio_raw_client *client = file->private_data;
 	struct serio_raw *serio_raw = client->serio_raw;
 	char uninitialized_var(c);
-	ssize_t retval = 0;
+	ssize_t read = 0;
+	int retval;
 
 	if (serio_raw->dead)
 		return -ENODEV;
@@ -180,13 +181,15 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer,
 	if (serio_raw->dead)
 		return -ENODEV;
 
-	while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) {
-		if (put_user(c, buffer++))
-			return -EFAULT;
-		retval++;
+	while (read < count && serio_raw_fetch_byte(serio_raw, &c)) {
+		if (put_user(c, buffer++)) {
+			retval = -EFAULT;
+			break;
+		}
+		read++;
 	}
 
-	return retval;
+	return read ?: retval;
 }
 
 static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 0/5] Input: serio_raw - add monitor/replay debugging interface
  2012-02-01  8:19 [PATCH] Input: serio_raw - return proper result when serio_raw_read fails Che-Liang Chiou
@ 2012-02-08  3:24 ` Che-Liang Chiou
  2012-02-14  3:18   ` Daniel Kurtz
  2012-02-08  3:24 ` [PATCH 1/5] Input: serio_raw - return proper result when serio_raw_read fails Che-Liang Chiou
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Che-Liang Chiou @ 2012-02-08  3:24 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dmitry Torokhov, linux-input, Che-Liang Chiou

The serio_raw driver is designed to provide "raw access" to mice, keyboards
etc; thus, a driver accessing serio_raw may live entirely in userland.

However, serio_raw lacks testability.  It is practically impossible to do
regression tests on changes to a serio_raw -based userland driver.  On the other
hand, the kernel's input subsystem has good testability support.  With the help
of tools like utouch-evemu, we may capture and replay input events for evdev
drivers in regression tests.

This patchset contains extensions to the serio_raw driver which add debugfs
entries for monitoring and replaying byte sequence between a userland driver
and device.  These byte sequences can be used in regression tests of the
userland driver.  This patchset closes the gap between serio_raw and the input
subsystem regarding testability.

This patchset is successfully applied on kernel version 3.3-rc2.

Che-Liang Chiou (5):
  Input: serio_raw - return proper result when serio_raw_read fails
  Input: serio_raw - extract queue interface
  Input: serio_raw - factor out common pattern of write
  Input: serio_raw - add debugfs interface
  Input: serio_raw - implement debugfs interface

 drivers/input/serio/serio_raw.c |  394 +++++++++++++++++++++++++++++++++------
 1 files changed, 337 insertions(+), 57 deletions(-)

-- 
1.7.7.3


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/5] Input: serio_raw - return proper result when serio_raw_read fails
  2012-02-01  8:19 [PATCH] Input: serio_raw - return proper result when serio_raw_read fails Che-Liang Chiou
  2012-02-08  3:24 ` [PATCH 0/5] Input: serio_raw - add monitor/replay debugging interface Che-Liang Chiou
@ 2012-02-08  3:24 ` Che-Liang Chiou
  2012-02-08  3:24 ` [PATCH 2/5] Input: serio_raw - extract queue interface Che-Liang Chiou
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Che-Liang Chiou @ 2012-02-08  3:24 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dmitry Torokhov, linux-input, Che-Liang Chiou

serio_raw_read now returns (sometimes partially) successful number of
bytes transferred to the caller, and only returns error code to the
caller on completely failed transfers.

Signed-off-by: Che-Liang Chiou <clchiou@chromium.org>
---
 drivers/input/serio/serio_raw.c |   15 +++++++++------
 1 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index 8250299..4494233 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -164,7 +164,8 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer,
 	struct serio_raw_client *client = file->private_data;
 	struct serio_raw *serio_raw = client->serio_raw;
 	char uninitialized_var(c);
-	ssize_t retval = 0;
+	ssize_t read = 0;
+	int retval;
 
 	if (serio_raw->dead)
 		return -ENODEV;
@@ -180,13 +181,15 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer,
 	if (serio_raw->dead)
 		return -ENODEV;
 
-	while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) {
-		if (put_user(c, buffer++))
-			return -EFAULT;
-		retval++;
+	while (read < count && serio_raw_fetch_byte(serio_raw, &c)) {
+		if (put_user(c, buffer++)) {
+			retval = -EFAULT;
+			break;
+		}
+		read++;
 	}
 
-	return retval;
+	return read ?: retval;
 }
 
 static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/5] Input: serio_raw - extract queue interface
  2012-02-01  8:19 [PATCH] Input: serio_raw - return proper result when serio_raw_read fails Che-Liang Chiou
  2012-02-08  3:24 ` [PATCH 0/5] Input: serio_raw - add monitor/replay debugging interface Che-Liang Chiou
  2012-02-08  3:24 ` [PATCH 1/5] Input: serio_raw - return proper result when serio_raw_read fails Che-Liang Chiou
@ 2012-02-08  3:24 ` Che-Liang Chiou
  2012-02-08  3:24 ` [PATCH 3/5] Input: serio_raw - factor out common pattern of write Che-Liang Chiou
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Che-Liang Chiou @ 2012-02-08  3:24 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dmitry Torokhov, linux-input, Che-Liang Chiou

serio_raw operates on an internal data queue that buffers input packets
from device interrupt.  As a queue is a generic data structure, it is
useful for implementing debugging facilities.  This patch extracts its
interface in preparation for implementing debugging facilities.

Signed-off-by: Che-Liang Chiou <clchiou@chromium.org>
---
 drivers/input/serio/serio_raw.c |  175 +++++++++++++++++++++++++++------------
 1 files changed, 121 insertions(+), 54 deletions(-)

diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index 4494233..bc2a8c7 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -29,15 +29,19 @@ MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
 #define SERIO_RAW_QUEUE_LEN	64
-struct serio_raw {
+struct queue {
 	unsigned char queue[SERIO_RAW_QUEUE_LEN];
 	unsigned int tail, head;
+	wait_queue_head_t wait;
+};
+
+struct serio_raw {
+	struct queue queue;
 
 	char name[16];
 	struct kref kref;
 	struct serio *serio;
 	struct miscdevice dev;
-	wait_queue_head_t wait;
 	struct list_head client_list;
 	struct list_head node;
 	bool dead;
@@ -53,6 +57,105 @@ static DEFINE_MUTEX(serio_raw_mutex);
 static LIST_HEAD(serio_raw_list);
 
 /*********************************************************************
+ *             Interface with queue                                  *
+ *********************************************************************/
+
+static void queue_init(struct queue *queue)
+{
+	init_waitqueue_head(&queue->wait);
+}
+
+static void queue_clear(struct queue *queue)
+{
+	queue->head = queue->tail = 0;
+}
+
+static bool queue_available(struct queue *queue)
+{
+	return queue->head != queue->tail;
+}
+
+static void queue_wakeup(struct queue *queue)
+{
+	wake_up_interruptible(&queue->wait);
+}
+
+static bool queue_fetch_byte(struct queue *queue, char *c)
+{
+	bool available;
+
+	available = queue_available(queue);
+	if (available) {
+		*c = queue->queue[queue->tail];
+		queue->tail = (queue->tail + 1) % SERIO_RAW_QUEUE_LEN;
+	}
+
+	return available;
+}
+
+static ssize_t queue_read(struct queue *queue,
+		char __user *buffer, size_t count, bool *dead, bool nonblock,
+		bool (*fetch_byte)(struct queue *, char *))
+{
+	char uninitialized_var(c);
+	ssize_t read = 0;
+	int retval;
+
+	if (*dead)
+		return -ENODEV;
+
+	if (!queue_available(queue) && nonblock)
+		return -EAGAIN;
+
+	retval = wait_event_interruptible(queue->wait,
+			queue_available(queue) || *dead);
+	if (retval)
+		return retval;
+
+	if (*dead)
+		return -ENODEV;
+
+	while (read < count && fetch_byte(queue, &c)) {
+		if (put_user(c, buffer++)) {
+			retval = -EFAULT;
+			break;
+		}
+		read++;
+	}
+
+	return read ?: retval;
+}
+
+static bool queue_write_byte(struct queue *queue, char c)
+{
+	unsigned int head;
+	bool may_write;
+
+	queue->queue[queue->head] = c;
+	head = (queue->head + 1) % SERIO_RAW_QUEUE_LEN;
+	may_write = head != queue->tail;
+	if (may_write)
+		queue->head = head;
+
+	return may_write;
+}
+
+static unsigned int queue_poll(struct queue *queue, struct file *file,
+		poll_table *wait, bool *dead)
+{
+	unsigned int mask;
+
+	poll_wait(file, &queue->wait, wait);
+
+	mask = *dead ? POLLHUP | POLLERR : POLLOUT | POLLWRNORM;
+	if (queue_available(queue))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+
+/*********************************************************************
  *             Interface with userspace (file operations)            *
  *********************************************************************/
 
@@ -141,21 +244,17 @@ static int serio_raw_release(struct inode *inode, struct file *file)
 	return 0;
 }
 
-static bool serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
+static bool serio_raw_fetch_byte(struct queue *queue, char *c)
 {
-	bool empty;
+	struct serio_raw *serio_raw;
+	bool available;
 
+	serio_raw = container_of(queue, struct serio_raw, queue);
 	serio_pause_rx(serio_raw->serio);
-
-	empty = serio_raw->head == serio_raw->tail;
-	if (!empty) {
-		*c = serio_raw->queue[serio_raw->tail];
-		serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN;
-	}
-
+	available = queue_fetch_byte(queue, c);
 	serio_continue_rx(serio_raw->serio);
 
-	return !empty;
+	return available;
 }
 
 static ssize_t serio_raw_read(struct file *file, char __user *buffer,
@@ -163,33 +262,11 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer,
 {
 	struct serio_raw_client *client = file->private_data;
 	struct serio_raw *serio_raw = client->serio_raw;
-	char uninitialized_var(c);
-	ssize_t read = 0;
-	int retval;
-
-	if (serio_raw->dead)
-		return -ENODEV;
-
-	if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK))
-		return -EAGAIN;
-
-	retval = wait_event_interruptible(serio_raw->wait,
-			serio_raw->head != serio_raw->tail || serio_raw->dead);
-	if (retval)
-		return retval;
-
-	if (serio_raw->dead)
-		return -ENODEV;
+	struct queue *queue = &serio_raw->queue;
 
-	while (read < count && serio_raw_fetch_byte(serio_raw, &c)) {
-		if (put_user(c, buffer++)) {
-			retval = -EFAULT;
-			break;
-		}
-		read++;
-	}
-
-	return read ?: retval;
+	return queue_read(queue, buffer, count,
+			&serio_raw->dead, file->f_flags & O_NONBLOCK,
+			serio_raw_fetch_byte);
 }
 
 static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
@@ -234,15 +311,9 @@ static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
 {
 	struct serio_raw_client *client = file->private_data;
 	struct serio_raw *serio_raw = client->serio_raw;
-	unsigned int mask;
-
-	poll_wait(file, &serio_raw->wait, wait);
+	struct queue *queue = &serio_raw->queue;
 
-	mask = serio_raw->dead ? POLLHUP | POLLERR : POLLOUT | POLLWRNORM;
-	if (serio_raw->head != serio_raw->tail)
-		mask |= POLLIN | POLLRDNORM;
-
-	return mask;
+	return queue_poll(queue, file, wait, &serio_raw->dead);
 }
 
 static const struct file_operations serio_raw_fops = {
@@ -266,16 +337,12 @@ static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
 {
 	struct serio_raw *serio_raw = serio_get_drvdata(serio);
 	struct serio_raw_client *client;
-	unsigned int head = serio_raw->head;
 
 	/* we are holding serio->lock here so we are protected */
-	serio_raw->queue[head] = data;
-	head = (head + 1) % SERIO_RAW_QUEUE_LEN;
-	if (likely(head != serio_raw->tail)) {
-		serio_raw->head = head;
+	if (queue_write_byte(&serio_raw->queue, data)) {
 		list_for_each_entry(client, &serio_raw->client_list, node)
 			kill_fasync(&client->fasync, SIGIO, POLL_IN);
-		wake_up_interruptible(&serio_raw->wait);
+		queue_wakeup(&serio_raw->queue);
 	}
 
 	return IRQ_HANDLED;
@@ -297,7 +364,7 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
 		 "serio_raw%ld", (long)atomic_inc_return(&serio_raw_no) - 1);
 	kref_init(&serio_raw->kref);
 	INIT_LIST_HEAD(&serio_raw->client_list);
-	init_waitqueue_head(&serio_raw->wait);
+	queue_init(&serio_raw->queue);
 
 	serio_raw->serio = serio;
 	get_device(&serio->dev);
@@ -378,7 +445,7 @@ static void serio_raw_hangup(struct serio_raw *serio_raw)
 		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
 	serio_continue_rx(serio_raw->serio);
 
-	wake_up_interruptible(&serio_raw->wait);
+	queue_wakeup(&serio_raw->queue);
 }
 
 
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/5] Input: serio_raw - factor out common pattern of write
  2012-02-01  8:19 [PATCH] Input: serio_raw - return proper result when serio_raw_read fails Che-Liang Chiou
                   ` (2 preceding siblings ...)
  2012-02-08  3:24 ` [PATCH 2/5] Input: serio_raw - extract queue interface Che-Liang Chiou
@ 2012-02-08  3:24 ` Che-Liang Chiou
  2012-02-08  3:24 ` [PATCH 4/5] Input: serio_raw - add debugfs interface Che-Liang Chiou
  2012-02-08  3:24 ` [PATCH 5/5] Input: serio_raw - implement " Che-Liang Chiou
  5 siblings, 0 replies; 10+ messages in thread
From: Che-Liang Chiou @ 2012-02-08  3:24 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dmitry Torokhov, linux-input, Che-Liang Chiou

As write to /dev/serio_raw* and to /sys/kernel/debug/serio_raw*/device
share a common pattern, this patch extracts that pattern and put it in
the serio_raw_write_mainloop() function.

Signed-off-by: Che-Liang Chiou <clchiou@chromium.org>
---
 drivers/input/serio/serio_raw.c |   27 ++++++++++++++++++++-------
 1 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index bc2a8c7..5d13c64 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -269,12 +269,12 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer,
 			serio_raw_fetch_byte);
 }
 
-static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
-			       size_t count, loff_t *ppos)
+static ssize_t serio_raw_write_mainloop(struct serio_raw *serio_raw,
+		const char __user *buffer, size_t count,
+		bool write_to_dev, struct queue *queue)
 {
-	struct serio_raw_client *client = file->private_data;
-	struct serio_raw *serio_raw = client->serio_raw;
 	ssize_t written = 0;
+	bool wakeup = false;
 	int retval;
 	unsigned char c;
 
@@ -293,20 +293,33 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
 	while (count--) {
 		if (get_user(c, buffer++)) {
 			retval = -EFAULT;
-			goto out;
+			break;
 		}
-		if (serio_write(serio_raw->serio, c)) {
+		if (write_to_dev && serio_write(serio_raw->serio, c)) {
 			retval = -EIO;
-			goto out;
+			break;
 		}
+		if (queue)
+			wakeup |= queue_write_byte(queue, c);
 		written++;
 	}
+	if (queue && wakeup)
+		queue_wakeup(queue);
 
 out:
 	mutex_unlock(&serio_raw_mutex);
 	return written ?: retval;
 }
 
+static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
+			       size_t count, loff_t *ppos)
+{
+	struct serio_raw_client *client = file->private_data;
+	struct serio_raw *serio_raw = client->serio_raw;
+
+	return serio_raw_write_mainloop(serio_raw, buffer, count, true, NULL);
+}
+
 static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
 {
 	struct serio_raw_client *client = file->private_data;
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/5] Input: serio_raw - add debugfs interface
  2012-02-01  8:19 [PATCH] Input: serio_raw - return proper result when serio_raw_read fails Che-Liang Chiou
                   ` (3 preceding siblings ...)
  2012-02-08  3:24 ` [PATCH 3/5] Input: serio_raw - factor out common pattern of write Che-Liang Chiou
@ 2012-02-08  3:24 ` Che-Liang Chiou
  2012-02-08  3:24 ` [PATCH 5/5] Input: serio_raw - implement " Che-Liang Chiou
  5 siblings, 0 replies; 10+ messages in thread
From: Che-Liang Chiou @ 2012-02-08  3:24 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dmitry Torokhov, linux-input, Che-Liang Chiou

Add debugfs interface for each serio_raw module instance. Take
serio_raw0 for example, the interface should look like:

  /sys/kernel/debug/serio_raw0/replay
                           .../user
                           .../device

Signed-off-by: Che-Liang Chiou <clchiou@chromium.org>
---
 drivers/input/serio/serio_raw.c |   51 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index 5d13c64..7b02691 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -21,6 +21,7 @@
 #include <linux/miscdevice.h>
 #include <linux/wait.h>
 #include <linux/mutex.h>
+#include <linux/debugfs.h>
 
 #define DRIVER_DESC	"Raw serio driver"
 
@@ -45,6 +46,9 @@ struct serio_raw {
 	struct list_head client_list;
 	struct list_head node;
 	bool dead;
+	u32 replay;  /* not bool because debugfs_create_bool() takes u32 */
+
+	struct dentry *dentry;
 };
 
 struct serio_raw_client {
@@ -342,6 +346,17 @@ static const struct file_operations serio_raw_fops = {
 
 
 /*********************************************************************
+ *             Interface with debugfs (file operations)              *
+ *********************************************************************/
+
+static const struct file_operations debug_user_fops = {
+};
+
+static const struct file_operations debug_device_fops = {
+};
+
+
+/*********************************************************************
  *                   Interface with serio port                       *
  *********************************************************************/
 
@@ -361,6 +376,36 @@ static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
 	return IRQ_HANDLED;
 }
 
+static int serio_raw_debug_init(struct serio_raw *serio_raw)
+{
+	const mode_t mode = S_IRUSR | S_IWUSR;
+
+	serio_raw->dentry = debugfs_create_dir(serio_raw->name, NULL);
+	if (!serio_raw->dentry)
+		return -ENOENT;
+
+	if (!debugfs_create_bool("replay", mode, serio_raw->dentry,
+				&serio_raw->replay))
+		goto err;
+	if (!debugfs_create_file("user", mode, serio_raw->dentry, serio_raw,
+				&debug_user_fops))
+		goto err;
+	if (!debugfs_create_file("device", mode, serio_raw->dentry, serio_raw,
+				&debug_device_fops))
+		goto err;
+
+	return 0;
+
+err:
+	debugfs_remove_recursive(serio_raw->dentry);
+	return -ENOENT;
+}
+
+static void serio_raw_debug_cleanup(struct serio_raw *serio_raw)
+{
+	debugfs_remove_recursive(serio_raw->dentry);
+}
+
 static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
 {
 	static atomic_t serio_raw_no = ATOMIC_INIT(0);
@@ -413,6 +458,10 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
 		goto err_unlink;
 	}
 
+	if (serio_raw_debug_init(serio_raw))
+		dev_warn(&serio->dev, "failed to register debugfs for %s\n",
+				serio->phys);
+
 	dev_info(&serio->dev, "raw access enabled on %s (%s, minor %d)\n",
 		 serio->phys, serio_raw->name, serio_raw->dev.minor);
 	return 0;
@@ -475,6 +524,8 @@ static void serio_raw_disconnect(struct serio *serio)
 
 	serio_raw_hangup(serio_raw);
 
+	serio_raw_debug_cleanup(serio_raw);
+
 	serio_close(serio);
 	kref_put(&serio_raw->kref, serio_raw_free);
 
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/5] Input: serio_raw - implement debugfs interface
  2012-02-01  8:19 [PATCH] Input: serio_raw - return proper result when serio_raw_read fails Che-Liang Chiou
                   ` (4 preceding siblings ...)
  2012-02-08  3:24 ` [PATCH 4/5] Input: serio_raw - add debugfs interface Che-Liang Chiou
@ 2012-02-08  3:24 ` Che-Liang Chiou
  5 siblings, 0 replies; 10+ messages in thread
From: Che-Liang Chiou @ 2012-02-08  3:24 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dmitry Torokhov, linux-input, Che-Liang Chiou

The debugfs interface has two modes, 'monitor' and 'replay'. The
'monitor' mode is on by default. Writing '1' to the replay debugfs entry
to enter 'replay' mode.

In monitor mode, a 'Monitor Process' can monitor traffic between a
userspace client and a serio device. The 'user' debugfs entry echoes
data written from userspace, and the 'device' debugfs entry echoes data
sent from the device.

  Userland driver <--->---+
                          |
                   /dev/serio_raw0
                          |
                 +--------+--------+
                 |                 |
                 v   /sys/kernel/debug/serio_raw0/user
                 |                 |
                 |                 v
                 |          Monitor Process
                 |                 ^
                 |                 |
                 ^   /sys/kernel/debug/serio_raw0/device
                 |                 |
  device <--->---+-----------------+

In replay mode, a 'Replay Process' sits in the middle of all traffics.
Note that the 'user' and 'device' debugfs entry are now operated in full
duplex mode.

  Userland driver <--->---+
                          |
                   /dev/serio_raw0
                          |
            /sys/kernel/debug/serio_raw0/user
                          ^
                          |
                          v
                    Replay Process
                          ^
                          |
                          v
            /sys/kernel/debug/serio_raw0/device
                          |
  device <--->------------+

Signed-off-by: Che-Liang Chiou <clchiou@chromium.org>
---
 drivers/input/serio/serio_raw.c |  152 ++++++++++++++++++++++++++++++++++++++-
 1 files changed, 149 insertions(+), 3 deletions(-)

diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index 7b02691..5865103 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -37,7 +37,7 @@ struct queue {
 };
 
 struct serio_raw {
-	struct queue queue;
+	struct queue queue, debug_user, debug_device;
 
 	char name[16];
 	struct kref kref;
@@ -46,6 +46,7 @@ struct serio_raw {
 	struct list_head client_list;
 	struct list_head node;
 	bool dead;
+	bool debug_user_opened, debug_device_opened;
 	u32 replay;  /* not bool because debugfs_create_bool() takes u32 */
 
 	struct dentry *dentry;
@@ -320,8 +321,11 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
 {
 	struct serio_raw_client *client = file->private_data;
 	struct serio_raw *serio_raw = client->serio_raw;
+	struct queue *queue = serio_raw->debug_user_opened ?
+			&serio_raw->debug_user : NULL;
 
-	return serio_raw_write_mainloop(serio_raw, buffer, count, true, NULL);
+	return serio_raw_write_mainloop(serio_raw, buffer, count,
+			!serio_raw->replay, queue);
 }
 
 static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
@@ -349,10 +353,144 @@ static const struct file_operations serio_raw_fops = {
  *             Interface with debugfs (file operations)              *
  *********************************************************************/
 
+static int debug_user_open(struct inode *inode, struct file *file)
+{
+	struct serio_raw *serio_raw = inode->i_private;
+
+	file->private_data = serio_raw;
+	queue_clear(&serio_raw->debug_user);
+	serio_raw->debug_user_opened = true;
+	kref_get(&serio_raw->kref);
+
+	return 0;
+}
+
+static int debug_device_open(struct inode *inode, struct file *file)
+{
+	struct serio_raw *serio_raw = inode->i_private;
+
+	file->private_data = serio_raw;
+	queue_clear(&serio_raw->debug_device);
+	serio_raw->debug_device_opened = true;
+	kref_get(&serio_raw->kref);
+
+	return 0;
+}
+
+static int debug_user_release(struct inode *inode, struct file *file)
+{
+	struct serio_raw *serio_raw = file->private_data;
+
+	file->private_data = NULL;
+	serio_raw->debug_user_opened = false;
+	kref_put(&serio_raw->kref, serio_raw_free);
+
+	return 0;
+}
+
+static int debug_device_release(struct inode *inode, struct file *file)
+{
+	struct serio_raw *serio_raw = file->private_data;
+
+	file->private_data = NULL;
+	serio_raw->debug_device_opened = false;
+	kref_put(&serio_raw->kref, serio_raw_free);
+
+	return 0;
+}
+
+static ssize_t debug_user_read(struct file *file,
+		char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct serio_raw *serio_raw = file->private_data;
+	struct queue *queue = &serio_raw->debug_user;
+
+	return queue_read(queue, buffer, count,
+			&serio_raw->dead, file->f_flags & O_NONBLOCK,
+			queue_fetch_byte);
+}
+
+static ssize_t debug_device_read(struct file *file,
+		char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct serio_raw *serio_raw = file->private_data;
+	struct queue *queue = &serio_raw->debug_device;
+
+	return queue_read(queue, buffer, count,
+			&serio_raw->dead, file->f_flags & O_NONBLOCK,
+			queue_fetch_byte);
+}
+
+static ssize_t debug_user_write(struct file *file,
+		const char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct serio_raw *serio_raw = file->private_data;
+	struct serio_raw_client *client;
+	size_t written = 0;
+
+	if (!serio_raw->replay)
+		return -EIO;
+
+	serio_pause_rx(serio_raw->serio);
+
+	for (written = 0; written < count; written++)
+		if (!queue_write_byte(&serio_raw->queue, buffer[written]))
+			break;
+	if (written) {
+		list_for_each_entry(client, &serio_raw->client_list, node)
+			kill_fasync(&client->fasync, SIGIO, POLL_IN);
+		queue_wakeup(&serio_raw->queue);
+	}
+
+	serio_continue_rx(serio_raw->serio);
+	return written ?: -EIO;
+}
+
+static ssize_t debug_device_write(struct file *file,
+		const char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct serio_raw *serio_raw = file->private_data;
+
+	if (!serio_raw->replay)
+		return -EIO;
+
+	return serio_raw_write_mainloop(serio_raw, buffer, count, true, NULL);
+}
+
+static unsigned int debug_user_poll(struct file *file, poll_table *wait)
+{
+	struct serio_raw *serio_raw = file->private_data;
+	struct queue *queue = &serio_raw->debug_user;
+
+	return queue_poll(queue, file, wait, &serio_raw->dead);
+}
+
+static unsigned int debug_device_poll(struct file *file, poll_table *wait)
+{
+	struct serio_raw *serio_raw = file->private_data;
+	struct queue *queue = &serio_raw->debug_device;
+
+	return queue_poll(queue, file, wait, &serio_raw->dead);
+}
+
 static const struct file_operations debug_user_fops = {
+	.owner		= THIS_MODULE,
+	.open		= debug_user_open,
+	.release	= debug_user_release,
+	.read		= debug_user_read,
+	.write		= debug_user_write,
+	.poll		= debug_user_poll,
+	.llseek		= noop_llseek,
 };
 
 static const struct file_operations debug_device_fops = {
+	.owner		= THIS_MODULE,
+	.open		= debug_device_open,
+	.release	= debug_device_release,
+	.read		= debug_device_read,
+	.write		= debug_device_write,
+	.poll		= debug_device_poll,
+	.llseek		= noop_llseek,
 };
 
 
@@ -367,7 +505,12 @@ static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
 	struct serio_raw_client *client;
 
 	/* we are holding serio->lock here so we are protected */
-	if (queue_write_byte(&serio_raw->queue, data)) {
+
+	if (serio_raw->debug_device_opened &&
+			queue_write_byte(&serio_raw->debug_device, data))
+		queue_wakeup(&serio_raw->debug_device);
+
+	if (!serio_raw->replay && queue_write_byte(&serio_raw->queue, data)) {
 		list_for_each_entry(client, &serio_raw->client_list, node)
 			kill_fasync(&client->fasync, SIGIO, POLL_IN);
 		queue_wakeup(&serio_raw->queue);
@@ -394,6 +537,9 @@ static int serio_raw_debug_init(struct serio_raw *serio_raw)
 				&debug_device_fops))
 		goto err;
 
+	queue_init(&serio_raw->debug_user);
+	queue_init(&serio_raw->debug_device);
+
 	return 0;
 
 err:
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/5] Input: serio_raw - add monitor/replay debugging interface
  2012-02-08  3:24 ` [PATCH 0/5] Input: serio_raw - add monitor/replay debugging interface Che-Liang Chiou
@ 2012-02-14  3:18   ` Daniel Kurtz
  2012-04-21  6:21     ` Dmitry Torokhov
  0 siblings, 1 reply; 10+ messages in thread
From: Daniel Kurtz @ 2012-02-14  3:18 UTC (permalink / raw)
  To: Che-Liang Chiou; +Cc: linux-kernel, Dmitry Torokhov, linux-input

On Wed, Feb 8, 2012 at 11:24 AM, Che-Liang Chiou <clchiou@chromium.org> wrote:
>
> The serio_raw driver is designed to provide "raw access" to mice, keyboards
> etc; thus, a driver accessing serio_raw may live entirely in userland.
>
> However, serio_raw lacks testability.  It is practically impossible to do
> regression tests on changes to a serio_raw -based userland driver.  On the other
> hand, the kernel's input subsystem has good testability support.  With the help
> of tools like utouch-evemu, we may capture and replay input events for evdev
> drivers in regression tests.
>
> This patchset contains extensions to the serio_raw driver which add debugfs
> entries for monitoring and replaying byte sequence between a userland driver
> and device.  These byte sequences can be used in regression tests of the
> userland driver.  This patchset closes the gap between serio_raw and the input
> subsystem regarding testability.
>
> This patchset is successfully applied on kernel version 3.3-rc2.
>
> Che-Liang Chiou (5):
>  Input: serio_raw - return proper result when serio_raw_read fails
>  Input: serio_raw - extract queue interface
>  Input: serio_raw - factor out common pattern of write
>  Input: serio_raw - add debugfs interface
>  Input: serio_raw - implement debugfs interface

For patches 3-5:
Reviewed-by: Daniel Kurtz <djkurtz@google.com>

For patch 1 (and its implication on queue_read() in patch 2, I'd
prefer to hear feedback from the list.

-Daniel

>
>  drivers/input/serio/serio_raw.c |  394 +++++++++++++++++++++++++++++++++------
>  1 files changed, 337 insertions(+), 57 deletions(-)
>
> --
> 1.7.7.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html




--
Daniel Kurtz | Software Engineer | djkurtz@google.com | 650.204.0722

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/5] Input: serio_raw - add monitor/replay debugging interface
  2012-02-14  3:18   ` Daniel Kurtz
@ 2012-04-21  6:21     ` Dmitry Torokhov
  2012-05-28  9:41       ` Che-liang Chiou
  0 siblings, 1 reply; 10+ messages in thread
From: Dmitry Torokhov @ 2012-04-21  6:21 UTC (permalink / raw)
  To: Daniel Kurtz; +Cc: Che-Liang Chiou, linux-kernel, linux-input

On Tue, Feb 14, 2012 at 11:18:38AM +0800, Daniel Kurtz wrote:
> On Wed, Feb 8, 2012 at 11:24 AM, Che-Liang Chiou <clchiou@chromium.org> wrote:
> >
> > The serio_raw driver is designed to provide "raw access" to mice, keyboards
> > etc; thus, a driver accessing serio_raw may live entirely in userland.
> >
> > However, serio_raw lacks testability.  It is practically impossible to do
> > regression tests on changes to a serio_raw -based userland driver.  On the other
> > hand, the kernel's input subsystem has good testability support.  With the help
> > of tools like utouch-evemu, we may capture and replay input events for evdev
> > drivers in regression tests.
> >
> > This patchset contains extensions to the serio_raw driver which add debugfs
> > entries for monitoring and replaying byte sequence between a userland driver
> > and device.  These byte sequences can be used in regression tests of the
> > userland driver.  This patchset closes the gap between serio_raw and the input
> > subsystem regarding testability.
> >
> > This patchset is successfully applied on kernel version 3.3-rc2.
> >
> > Che-Liang Chiou (5):
> >  Input: serio_raw - return proper result when serio_raw_read fails
> >  Input: serio_raw - extract queue interface
> >  Input: serio_raw - factor out common pattern of write
> >  Input: serio_raw - add debugfs interface
> >  Input: serio_raw - implement debugfs interface
> 
> For patches 3-5:
> Reviewed-by: Daniel Kurtz <djkurtz@google.com>
> 
> For patch 1 (and its implication on queue_read() in patch 2, I'd
> prefer to hear feedback from the list.

I have already taken the patch 1, but I am unsure why we need kernel
support for the monitor and replay. It seems you should only need a
userspace program that would sit between the driver being tested and
serio_raw misc device and pipe data back and forth, optionally saving
it for subsequent replay.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/5] Input: serio_raw - add monitor/replay debugging interface
  2012-04-21  6:21     ` Dmitry Torokhov
@ 2012-05-28  9:41       ` Che-liang Chiou
  0 siblings, 0 replies; 10+ messages in thread
From: Che-liang Chiou @ 2012-05-28  9:41 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Daniel Kurtz, linux-kernel, linux-input

Hi Dmitry,

I am sorry for getting back to you so late. See below.

On Sat, Apr 21, 2012 at 2:21 PM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> On Tue, Feb 14, 2012 at 11:18:38AM +0800, Daniel Kurtz wrote:
>> On Wed, Feb 8, 2012 at 11:24 AM, Che-Liang Chiou <clchiou@chromium.org> wrote:
>> >
>> > The serio_raw driver is designed to provide "raw access" to mice, keyboards
>> > etc; thus, a driver accessing serio_raw may live entirely in userland.
>> >
>> > However, serio_raw lacks testability.  It is practically impossible to do
>> > regression tests on changes to a serio_raw -based userland driver.  On the other
>> > hand, the kernel's input subsystem has good testability support.  With the help
>> > of tools like utouch-evemu, we may capture and replay input events for evdev
>> > drivers in regression tests.
>> >
>> > This patchset contains extensions to the serio_raw driver which add debugfs
>> > entries for monitoring and replaying byte sequence between a userland driver
>> > and device.  These byte sequences can be used in regression tests of the
>> > userland driver.  This patchset closes the gap between serio_raw and the input
>> > subsystem regarding testability.
>> >
>> > This patchset is successfully applied on kernel version 3.3-rc2.
>> >
>> > Che-Liang Chiou (5):
>> >  Input: serio_raw - return proper result when serio_raw_read fails
>> >  Input: serio_raw - extract queue interface
>> >  Input: serio_raw - factor out common pattern of write
>> >  Input: serio_raw - add debugfs interface
>> >  Input: serio_raw - implement debugfs interface
>>
>> For patches 3-5:
>> Reviewed-by: Daniel Kurtz <djkurtz@google.com>
>>
>> For patch 1 (and its implication on queue_read() in patch 2, I'd
>> prefer to hear feedback from the list.
>
> I have already taken the patch 1, but I am unsure why we need kernel
> support for the monitor and replay. It seems you should only need a
> userspace program that would sit between the driver being tested and
> serio_raw misc device and pipe data back and forth, optionally saving
> it for subsequent replay.

Sadly some user space drivers hard codes path, like
"/dev/serio_raw%d", and quite often their source codes are not
available; so it is effectively impossible to monitor these drivers
without kernel's help.

Besides, changing from accessing a char device to communicating
through IPC requires rewriting the user space driver, no matter it's a
pair of named pipes or a socket. This may sometimes be quite hard
depending on the I/O strategy of the driver.

> Thanks.
>
> --
> Dmitry

Regards,
Che-Liang

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2012-05-28  9:41 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-01  8:19 [PATCH] Input: serio_raw - return proper result when serio_raw_read fails Che-Liang Chiou
2012-02-08  3:24 ` [PATCH 0/5] Input: serio_raw - add monitor/replay debugging interface Che-Liang Chiou
2012-02-14  3:18   ` Daniel Kurtz
2012-04-21  6:21     ` Dmitry Torokhov
2012-05-28  9:41       ` Che-liang Chiou
2012-02-08  3:24 ` [PATCH 1/5] Input: serio_raw - return proper result when serio_raw_read fails Che-Liang Chiou
2012-02-08  3:24 ` [PATCH 2/5] Input: serio_raw - extract queue interface Che-Liang Chiou
2012-02-08  3:24 ` [PATCH 3/5] Input: serio_raw - factor out common pattern of write Che-Liang Chiou
2012-02-08  3:24 ` [PATCH 4/5] Input: serio_raw - add debugfs interface Che-Liang Chiou
2012-02-08  3:24 ` [PATCH 5/5] Input: serio_raw - implement " Che-Liang Chiou

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).