2 * userio kernel serio device emulation module
3 * Copyright (C) 2015 Red Hat
4 * Copyright (C) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
14 * General Public License for more details.
17 #include <linux/circ_buf.h>
18 #include <linux/mutex.h>
19 #include <linux/module.h>
20 #include <linux/init.h>
21 #include <linux/kernel.h>
22 #include <linux/serio.h>
23 #include <linux/slab.h>
25 #include <linux/miscdevice.h>
26 #include <linux/sched.h>
27 #include <linux/poll.h>
28 #include <uapi/linux/userio.h>
30 #define USERIO_NAME "userio"
31 #define USERIO_BUFSIZE 16
33 static struct miscdevice userio_misc
;
35 struct userio_device
{
45 unsigned char buf
[USERIO_BUFSIZE
];
47 wait_queue_head_t waitq
;
51 * userio_device_write - Write data from serio to a userio device in userspace
52 * @id: The serio port for the userio device
53 * @val: The data to write to the device
55 static int userio_device_write(struct serio
*id
, unsigned char val
)
57 struct userio_device
*userio
= id
->port_data
;
59 scoped_guard(spinlock_irqsave
, &userio
->buf_lock
) {
60 userio
->buf
[userio
->head
] = val
;
61 userio
->head
= (userio
->head
+ 1) % USERIO_BUFSIZE
;
63 if (userio
->head
== userio
->tail
)
64 dev_warn(userio_misc
.this_device
,
65 "Buffer overflowed, userio client isn't keeping up");
68 wake_up_interruptible(&userio
->waitq
);
73 static int userio_char_open(struct inode
*inode
, struct file
*file
)
75 struct userio_device
*userio
__free(kfree
) =
76 kzalloc(sizeof(*userio
), GFP_KERNEL
);
80 mutex_init(&userio
->mutex
);
81 spin_lock_init(&userio
->buf_lock
);
82 init_waitqueue_head(&userio
->waitq
);
84 userio
->serio
= kzalloc(sizeof(*userio
->serio
), GFP_KERNEL
);
88 userio
->serio
->write
= userio_device_write
;
89 userio
->serio
->port_data
= userio
;
91 file
->private_data
= no_free_ptr(userio
);
96 static int userio_char_release(struct inode
*inode
, struct file
*file
)
98 struct userio_device
*userio
= file
->private_data
;
100 if (userio
->running
) {
102 * Don't free the serio port here, serio_unregister_port()
105 serio_unregister_port(userio
->serio
);
107 kfree(userio
->serio
);
115 static size_t userio_fetch_data(struct userio_device
*userio
, u8
*buf
,
116 size_t count
, size_t *copylen
)
118 size_t available
, len
;
120 guard(spinlock_irqsave
)(&userio
->buf_lock
);
122 available
= CIRC_CNT_TO_END(userio
->head
, userio
->tail
,
124 len
= min(available
, count
);
126 memcpy(buf
, &userio
->buf
[userio
->tail
], len
);
127 userio
->tail
= (userio
->tail
+ len
) % USERIO_BUFSIZE
;
134 static ssize_t
userio_char_read(struct file
*file
, char __user
*user_buffer
,
135 size_t count
, loff_t
*ppos
)
137 struct userio_device
*userio
= file
->private_data
;
139 size_t available
, copylen
;
140 u8 buf
[USERIO_BUFSIZE
];
143 * By the time we get here, the data that was waiting might have
144 * been taken by another thread. Grab the buffer lock and check if
145 * there's still any data waiting, otherwise repeat this process
146 * until we have data (unless the file descriptor is non-blocking
150 available
= userio_fetch_data(userio
, buf
, count
, ©len
);
154 /* buffer was/is empty */
155 if (file
->f_flags
& O_NONBLOCK
)
159 * count == 0 is special - no IO is done but we check
160 * for error conditions (see above).
165 error
= wait_event_interruptible(userio
->waitq
,
166 userio
->head
!= userio
->tail
);
172 if (copy_to_user(user_buffer
, buf
, copylen
))
178 static int userio_execute_cmd(struct userio_device
*userio
,
179 const struct userio_cmd
*cmd
)
182 case USERIO_CMD_REGISTER
:
183 if (!userio
->serio
->id
.type
) {
184 dev_warn(userio_misc
.this_device
,
185 "No port type given on /dev/userio\n");
189 if (userio
->running
) {
190 dev_warn(userio_misc
.this_device
,
191 "Begin command sent, but we're already running\n");
195 userio
->running
= true;
196 serio_register_port(userio
->serio
);
199 case USERIO_CMD_SET_PORT_TYPE
:
200 if (userio
->running
) {
201 dev_warn(userio_misc
.this_device
,
202 "Can't change port type on an already running userio instance\n");
206 userio
->serio
->id
.type
= cmd
->data
;
209 case USERIO_CMD_SEND_INTERRUPT
:
210 if (!userio
->running
) {
211 dev_warn(userio_misc
.this_device
,
212 "The device must be registered before sending interrupts\n");
216 serio_interrupt(userio
->serio
, cmd
->data
, 0);
226 static ssize_t
userio_char_write(struct file
*file
, const char __user
*buffer
,
227 size_t count
, loff_t
*ppos
)
229 struct userio_device
*userio
= file
->private_data
;
230 struct userio_cmd cmd
;
233 if (count
!= sizeof(cmd
)) {
234 dev_warn(userio_misc
.this_device
, "Invalid payload size\n");
238 if (copy_from_user(&cmd
, buffer
, sizeof(cmd
)))
241 scoped_cond_guard(mutex_intr
, return -EINTR
, &userio
->mutex
) {
242 error
= userio_execute_cmd(userio
, &cmd
);
250 static __poll_t
userio_char_poll(struct file
*file
, poll_table
*wait
)
252 struct userio_device
*userio
= file
->private_data
;
254 poll_wait(file
, &userio
->waitq
, wait
);
256 if (userio
->head
!= userio
->tail
)
257 return EPOLLIN
| EPOLLRDNORM
;
262 static const struct file_operations userio_fops
= {
263 .owner
= THIS_MODULE
,
264 .open
= userio_char_open
,
265 .release
= userio_char_release
,
266 .read
= userio_char_read
,
267 .write
= userio_char_write
,
268 .poll
= userio_char_poll
,
271 static struct miscdevice userio_misc
= {
272 .fops
= &userio_fops
,
273 .minor
= USERIO_MINOR
,
276 module_driver(userio_misc
, misc_register
, misc_deregister
);
278 MODULE_ALIAS_MISCDEV(USERIO_MINOR
);
279 MODULE_ALIAS("devname:" USERIO_NAME
);
281 MODULE_AUTHOR("Stephen Chandler Paul <thatslyude@gmail.com>");
282 MODULE_DESCRIPTION("Virtual Serio Device Support");
283 MODULE_LICENSE("GPL");