xref: /openbmc/linux/drivers/input/serio/userio.c (revision a9a08845)
15523662eSStephen Chandler Paul /*
25523662eSStephen Chandler Paul  * userio kernel serio device emulation module
35523662eSStephen Chandler Paul  * Copyright (C) 2015 Red Hat
45523662eSStephen Chandler Paul  * Copyright (C) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
55523662eSStephen Chandler Paul  *
65523662eSStephen Chandler Paul  * This program is free software; you can redistribute it and/or modify it
75523662eSStephen Chandler Paul  * under the terms of the GNU Lesser General Public License as published by
85523662eSStephen Chandler Paul  * the Free Software Foundation; either version 2 of the License, or (at
95523662eSStephen Chandler Paul  * your option) any later version.
105523662eSStephen Chandler Paul  *
115523662eSStephen Chandler Paul  * This program is distributed in the hope that it will be useful, but
125523662eSStephen Chandler Paul  * WITHOUT ANY WARRANTY; without even the implied warranty of
135523662eSStephen Chandler Paul  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
145523662eSStephen Chandler Paul  * General Public License for more details.
155523662eSStephen Chandler Paul  */
165523662eSStephen Chandler Paul 
175523662eSStephen Chandler Paul #include <linux/circ_buf.h>
185523662eSStephen Chandler Paul #include <linux/mutex.h>
195523662eSStephen Chandler Paul #include <linux/module.h>
205523662eSStephen Chandler Paul #include <linux/init.h>
215523662eSStephen Chandler Paul #include <linux/kernel.h>
225523662eSStephen Chandler Paul #include <linux/serio.h>
235523662eSStephen Chandler Paul #include <linux/slab.h>
245523662eSStephen Chandler Paul #include <linux/fs.h>
255523662eSStephen Chandler Paul #include <linux/miscdevice.h>
265523662eSStephen Chandler Paul #include <linux/sched.h>
275523662eSStephen Chandler Paul #include <linux/poll.h>
285523662eSStephen Chandler Paul #include <uapi/linux/userio.h>
295523662eSStephen Chandler Paul 
305523662eSStephen Chandler Paul #define USERIO_NAME		"userio"
315523662eSStephen Chandler Paul #define USERIO_BUFSIZE		16
325523662eSStephen Chandler Paul 
335523662eSStephen Chandler Paul static struct miscdevice userio_misc;
345523662eSStephen Chandler Paul 
355523662eSStephen Chandler Paul struct userio_device {
365523662eSStephen Chandler Paul 	struct serio *serio;
375523662eSStephen Chandler Paul 	struct mutex mutex;
385523662eSStephen Chandler Paul 
395523662eSStephen Chandler Paul 	bool running;
405523662eSStephen Chandler Paul 
415523662eSStephen Chandler Paul 	u8 head;
425523662eSStephen Chandler Paul 	u8 tail;
435523662eSStephen Chandler Paul 
445523662eSStephen Chandler Paul 	spinlock_t buf_lock;
455523662eSStephen Chandler Paul 	unsigned char buf[USERIO_BUFSIZE];
465523662eSStephen Chandler Paul 
475523662eSStephen Chandler Paul 	wait_queue_head_t waitq;
485523662eSStephen Chandler Paul };
495523662eSStephen Chandler Paul 
505523662eSStephen Chandler Paul /**
515523662eSStephen Chandler Paul  * userio_device_write - Write data from serio to a userio device in userspace
525523662eSStephen Chandler Paul  * @id: The serio port for the userio device
535523662eSStephen Chandler Paul  * @val: The data to write to the device
545523662eSStephen Chandler Paul  */
userio_device_write(struct serio * id,unsigned char val)555523662eSStephen Chandler Paul static int userio_device_write(struct serio *id, unsigned char val)
565523662eSStephen Chandler Paul {
575523662eSStephen Chandler Paul 	struct userio_device *userio = id->port_data;
585523662eSStephen Chandler Paul 	unsigned long flags;
595523662eSStephen Chandler Paul 
605523662eSStephen Chandler Paul 	spin_lock_irqsave(&userio->buf_lock, flags);
615523662eSStephen Chandler Paul 
625523662eSStephen Chandler Paul 	userio->buf[userio->head] = val;
635523662eSStephen Chandler Paul 	userio->head = (userio->head + 1) % USERIO_BUFSIZE;
645523662eSStephen Chandler Paul 
655523662eSStephen Chandler Paul 	if (userio->head == userio->tail)
665523662eSStephen Chandler Paul 		dev_warn(userio_misc.this_device,
675523662eSStephen Chandler Paul 			 "Buffer overflowed, userio client isn't keeping up");
685523662eSStephen Chandler Paul 
695523662eSStephen Chandler Paul 	spin_unlock_irqrestore(&userio->buf_lock, flags);
705523662eSStephen Chandler Paul 
715523662eSStephen Chandler Paul 	wake_up_interruptible(&userio->waitq);
725523662eSStephen Chandler Paul 
735523662eSStephen Chandler Paul 	return 0;
745523662eSStephen Chandler Paul }
755523662eSStephen Chandler Paul 
userio_char_open(struct inode * inode,struct file * file)765523662eSStephen Chandler Paul static int userio_char_open(struct inode *inode, struct file *file)
775523662eSStephen Chandler Paul {
785523662eSStephen Chandler Paul 	struct userio_device *userio;
795523662eSStephen Chandler Paul 
805523662eSStephen Chandler Paul 	userio = kzalloc(sizeof(struct userio_device), GFP_KERNEL);
815523662eSStephen Chandler Paul 	if (!userio)
825523662eSStephen Chandler Paul 		return -ENOMEM;
835523662eSStephen Chandler Paul 
845523662eSStephen Chandler Paul 	mutex_init(&userio->mutex);
855523662eSStephen Chandler Paul 	spin_lock_init(&userio->buf_lock);
865523662eSStephen Chandler Paul 	init_waitqueue_head(&userio->waitq);
875523662eSStephen Chandler Paul 
885523662eSStephen Chandler Paul 	userio->serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
895523662eSStephen Chandler Paul 	if (!userio->serio) {
905523662eSStephen Chandler Paul 		kfree(userio);
915523662eSStephen Chandler Paul 		return -ENOMEM;
925523662eSStephen Chandler Paul 	}
935523662eSStephen Chandler Paul 
945523662eSStephen Chandler Paul 	userio->serio->write = userio_device_write;
955523662eSStephen Chandler Paul 	userio->serio->port_data = userio;
965523662eSStephen Chandler Paul 
975523662eSStephen Chandler Paul 	file->private_data = userio;
985523662eSStephen Chandler Paul 
995523662eSStephen Chandler Paul 	return 0;
1005523662eSStephen Chandler Paul }
1015523662eSStephen Chandler Paul 
userio_char_release(struct inode * inode,struct file * file)1025523662eSStephen Chandler Paul static int userio_char_release(struct inode *inode, struct file *file)
1035523662eSStephen Chandler Paul {
1045523662eSStephen Chandler Paul 	struct userio_device *userio = file->private_data;
1055523662eSStephen Chandler Paul 
1065523662eSStephen Chandler Paul 	if (userio->running) {
1075523662eSStephen Chandler Paul 		/*
1085523662eSStephen Chandler Paul 		 * Don't free the serio port here, serio_unregister_port()
1095523662eSStephen Chandler Paul 		 * does it for us.
1105523662eSStephen Chandler Paul 		 */
1115523662eSStephen Chandler Paul 		serio_unregister_port(userio->serio);
1125523662eSStephen Chandler Paul 	} else {
1135523662eSStephen Chandler Paul 		kfree(userio->serio);
1145523662eSStephen Chandler Paul 	}
1155523662eSStephen Chandler Paul 
1165523662eSStephen Chandler Paul 	kfree(userio);
1175523662eSStephen Chandler Paul 
1185523662eSStephen Chandler Paul 	return 0;
1195523662eSStephen Chandler Paul }
1205523662eSStephen Chandler Paul 
userio_char_read(struct file * file,char __user * user_buffer,size_t count,loff_t * ppos)1215523662eSStephen Chandler Paul static ssize_t userio_char_read(struct file *file, char __user *user_buffer,
1225523662eSStephen Chandler Paul 				size_t count, loff_t *ppos)
1235523662eSStephen Chandler Paul {
1245523662eSStephen Chandler Paul 	struct userio_device *userio = file->private_data;
1255523662eSStephen Chandler Paul 	int error;
1265523662eSStephen Chandler Paul 	size_t nonwrap_len, copylen;
1275523662eSStephen Chandler Paul 	unsigned char buf[USERIO_BUFSIZE];
1285523662eSStephen Chandler Paul 	unsigned long flags;
1295523662eSStephen Chandler Paul 
1305523662eSStephen Chandler Paul 	/*
1315523662eSStephen Chandler Paul 	 * By the time we get here, the data that was waiting might have
1325523662eSStephen Chandler Paul 	 * been taken by another thread. Grab the buffer lock and check if
1335523662eSStephen Chandler Paul 	 * there's still any data waiting, otherwise repeat this process
1345523662eSStephen Chandler Paul 	 * until we have data (unless the file descriptor is non-blocking
1355523662eSStephen Chandler Paul 	 * of course).
1365523662eSStephen Chandler Paul 	 */
1375523662eSStephen Chandler Paul 	for (;;) {
1385523662eSStephen Chandler Paul 		spin_lock_irqsave(&userio->buf_lock, flags);
1395523662eSStephen Chandler Paul 
1405523662eSStephen Chandler Paul 		nonwrap_len = CIRC_CNT_TO_END(userio->head,
1415523662eSStephen Chandler Paul 					      userio->tail,
1425523662eSStephen Chandler Paul 					      USERIO_BUFSIZE);
1435523662eSStephen Chandler Paul 		copylen = min(nonwrap_len, count);
1445523662eSStephen Chandler Paul 		if (copylen) {
1455523662eSStephen Chandler Paul 			memcpy(buf, &userio->buf[userio->tail], copylen);
1465523662eSStephen Chandler Paul 			userio->tail = (userio->tail + copylen) %
1475523662eSStephen Chandler Paul 							USERIO_BUFSIZE;
1485523662eSStephen Chandler Paul 		}
1495523662eSStephen Chandler Paul 
1505523662eSStephen Chandler Paul 		spin_unlock_irqrestore(&userio->buf_lock, flags);
1515523662eSStephen Chandler Paul 
1525523662eSStephen Chandler Paul 		if (nonwrap_len)
1535523662eSStephen Chandler Paul 			break;
1545523662eSStephen Chandler Paul 
1555523662eSStephen Chandler Paul 		/* buffer was/is empty */
1565523662eSStephen Chandler Paul 		if (file->f_flags & O_NONBLOCK)
1575523662eSStephen Chandler Paul 			return -EAGAIN;
1585523662eSStephen Chandler Paul 
1595523662eSStephen Chandler Paul 		/*
1605523662eSStephen Chandler Paul 		 * count == 0 is special - no IO is done but we check
1615523662eSStephen Chandler Paul 		 * for error conditions (see above).
1625523662eSStephen Chandler Paul 		 */
1635523662eSStephen Chandler Paul 		if (count == 0)
1645523662eSStephen Chandler Paul 			return 0;
1655523662eSStephen Chandler Paul 
1665523662eSStephen Chandler Paul 		error = wait_event_interruptible(userio->waitq,
1675523662eSStephen Chandler Paul 						 userio->head != userio->tail);
1685523662eSStephen Chandler Paul 		if (error)
1695523662eSStephen Chandler Paul 			return error;
1705523662eSStephen Chandler Paul 	}
1715523662eSStephen Chandler Paul 
1725523662eSStephen Chandler Paul 	if (copylen)
1735523662eSStephen Chandler Paul 		if (copy_to_user(user_buffer, buf, copylen))
1745523662eSStephen Chandler Paul 			return -EFAULT;
1755523662eSStephen Chandler Paul 
1765523662eSStephen Chandler Paul 	return copylen;
1775523662eSStephen Chandler Paul }
1785523662eSStephen Chandler Paul 
userio_char_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)1795523662eSStephen Chandler Paul static ssize_t userio_char_write(struct file *file, const char __user *buffer,
1805523662eSStephen Chandler Paul 				 size_t count, loff_t *ppos)
1815523662eSStephen Chandler Paul {
1825523662eSStephen Chandler Paul 	struct userio_device *userio = file->private_data;
1835523662eSStephen Chandler Paul 	struct userio_cmd cmd;
1845523662eSStephen Chandler Paul 	int error;
1855523662eSStephen Chandler Paul 
1865523662eSStephen Chandler Paul 	if (count != sizeof(cmd)) {
1875523662eSStephen Chandler Paul 		dev_warn(userio_misc.this_device, "Invalid payload size\n");
1885523662eSStephen Chandler Paul 		return -EINVAL;
1895523662eSStephen Chandler Paul 	}
1905523662eSStephen Chandler Paul 
1915523662eSStephen Chandler Paul 	if (copy_from_user(&cmd, buffer, sizeof(cmd)))
1925523662eSStephen Chandler Paul 		return -EFAULT;
1935523662eSStephen Chandler Paul 
1945523662eSStephen Chandler Paul 	error = mutex_lock_interruptible(&userio->mutex);
1955523662eSStephen Chandler Paul 	if (error)
1965523662eSStephen Chandler Paul 		return error;
1975523662eSStephen Chandler Paul 
1985523662eSStephen Chandler Paul 	switch (cmd.type) {
1995523662eSStephen Chandler Paul 	case USERIO_CMD_REGISTER:
2005523662eSStephen Chandler Paul 		if (!userio->serio->id.type) {
2015523662eSStephen Chandler Paul 			dev_warn(userio_misc.this_device,
2025523662eSStephen Chandler Paul 				 "No port type given on /dev/userio\n");
2035523662eSStephen Chandler Paul 
2045523662eSStephen Chandler Paul 			error = -EINVAL;
2055523662eSStephen Chandler Paul 			goto out;
2065523662eSStephen Chandler Paul 		}
2075523662eSStephen Chandler Paul 
2085523662eSStephen Chandler Paul 		if (userio->running) {
2095523662eSStephen Chandler Paul 			dev_warn(userio_misc.this_device,
2105523662eSStephen Chandler Paul 				 "Begin command sent, but we're already running\n");
2115523662eSStephen Chandler Paul 			error = -EBUSY;
2125523662eSStephen Chandler Paul 			goto out;
2135523662eSStephen Chandler Paul 		}
2145523662eSStephen Chandler Paul 
2155523662eSStephen Chandler Paul 		userio->running = true;
2165523662eSStephen Chandler Paul 		serio_register_port(userio->serio);
2175523662eSStephen Chandler Paul 		break;
2185523662eSStephen Chandler Paul 
2195523662eSStephen Chandler Paul 	case USERIO_CMD_SET_PORT_TYPE:
2205523662eSStephen Chandler Paul 		if (userio->running) {
2215523662eSStephen Chandler Paul 			dev_warn(userio_misc.this_device,
2225523662eSStephen Chandler Paul 				 "Can't change port type on an already running userio instance\n");
2235523662eSStephen Chandler Paul 			error = -EBUSY;
2245523662eSStephen Chandler Paul 			goto out;
2255523662eSStephen Chandler Paul 		}
2265523662eSStephen Chandler Paul 
2275523662eSStephen Chandler Paul 		userio->serio->id.type = cmd.data;
2285523662eSStephen Chandler Paul 		break;
2295523662eSStephen Chandler Paul 
2305523662eSStephen Chandler Paul 	case USERIO_CMD_SEND_INTERRUPT:
2315523662eSStephen Chandler Paul 		if (!userio->running) {
2325523662eSStephen Chandler Paul 			dev_warn(userio_misc.this_device,
2335523662eSStephen Chandler Paul 				 "The device must be registered before sending interrupts\n");
2345523662eSStephen Chandler Paul 			error = -ENODEV;
2355523662eSStephen Chandler Paul 			goto out;
2365523662eSStephen Chandler Paul 		}
2375523662eSStephen Chandler Paul 
2385523662eSStephen Chandler Paul 		serio_interrupt(userio->serio, cmd.data, 0);
2395523662eSStephen Chandler Paul 		break;
2405523662eSStephen Chandler Paul 
2415523662eSStephen Chandler Paul 	default:
2425523662eSStephen Chandler Paul 		error = -EOPNOTSUPP;
2435523662eSStephen Chandler Paul 		goto out;
2445523662eSStephen Chandler Paul 	}
2455523662eSStephen Chandler Paul 
2465523662eSStephen Chandler Paul out:
2475523662eSStephen Chandler Paul 	mutex_unlock(&userio->mutex);
2485523662eSStephen Chandler Paul 	return error ?: count;
2495523662eSStephen Chandler Paul }
2505523662eSStephen Chandler Paul 
userio_char_poll(struct file * file,poll_table * wait)251afc9a42bSAl Viro static __poll_t userio_char_poll(struct file *file, poll_table *wait)
2525523662eSStephen Chandler Paul {
2535523662eSStephen Chandler Paul 	struct userio_device *userio = file->private_data;
2545523662eSStephen Chandler Paul 
2555523662eSStephen Chandler Paul 	poll_wait(file, &userio->waitq, wait);
2565523662eSStephen Chandler Paul 
2575523662eSStephen Chandler Paul 	if (userio->head != userio->tail)
258a9a08845SLinus Torvalds 		return EPOLLIN | EPOLLRDNORM;
2595523662eSStephen Chandler Paul 
2605523662eSStephen Chandler Paul 	return 0;
2615523662eSStephen Chandler Paul }
2625523662eSStephen Chandler Paul 
2635523662eSStephen Chandler Paul static const struct file_operations userio_fops = {
2645523662eSStephen Chandler Paul 	.owner		= THIS_MODULE,
2655523662eSStephen Chandler Paul 	.open		= userio_char_open,
2665523662eSStephen Chandler Paul 	.release	= userio_char_release,
2675523662eSStephen Chandler Paul 	.read		= userio_char_read,
2685523662eSStephen Chandler Paul 	.write		= userio_char_write,
2695523662eSStephen Chandler Paul 	.poll		= userio_char_poll,
2705523662eSStephen Chandler Paul 	.llseek		= no_llseek,
2715523662eSStephen Chandler Paul };
2725523662eSStephen Chandler Paul 
2735523662eSStephen Chandler Paul static struct miscdevice userio_misc = {
2745523662eSStephen Chandler Paul 	.fops	= &userio_fops,
2755523662eSStephen Chandler Paul 	.minor	= USERIO_MINOR,
2765523662eSStephen Chandler Paul 	.name	= USERIO_NAME,
2775523662eSStephen Chandler Paul };
2785523662eSStephen Chandler Paul module_driver(userio_misc, misc_register, misc_deregister);
2795523662eSStephen Chandler Paul 
2805523662eSStephen Chandler Paul MODULE_ALIAS_MISCDEV(USERIO_MINOR);
2815523662eSStephen Chandler Paul MODULE_ALIAS("devname:" USERIO_NAME);
2825523662eSStephen Chandler Paul 
2835523662eSStephen Chandler Paul MODULE_AUTHOR("Stephen Chandler Paul <thatslyude@gmail.com>");
2845523662eSStephen Chandler Paul MODULE_DESCRIPTION("Virtual Serio Device Support");
2855523662eSStephen Chandler Paul MODULE_LICENSE("GPL");
286