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@xxxxxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html