Release 4.11 drivers/input/misc/uinput.c
/*
* User level driver support for input subsystem
*
* Heavily based on evdev.c by Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
* 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
* - add UI_GET_SYSNAME ioctl
* 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
* - updated ff support for the changes in kernel interface
* - added MODULE_VERSION
* 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>)
* - added force feedback support
* - added UI_SET_PHYS
* 0.1 20/06/2002
* - first public version
*/
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uinput.h>
#include <linux/input/mt.h>
#include "../input-compat.h"
static int uinput_dev_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct uinput_device *udev = input_get_drvdata(dev);
udev->buff[udev->head].type = type;
udev->buff[udev->head].code = code;
udev->buff[udev->head].value = value;
do_gettimeofday(&udev->buff[udev->head].time);
udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
wake_up_interruptible(&udev->waitq);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Vojtech Pavlik | 92 | 82.88% | 1 | 33.33% |
Neil Brown | 14 | 12.61% | 1 | 33.33% |
Dmitry Torokhov | 5 | 4.50% | 1 | 33.33% |
Total | 111 | 100.00% | 3 | 100.00% |
/* Atomically allocate an ID for the given request. Returns 0 on success. */
static bool uinput_request_alloc_id(struct uinput_device *udev,
struct uinput_request *request)
{
unsigned int id;
bool reserved = false;
spin_lock(&udev->requests_lock);
for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
if (!udev->requests[id]) {
request->id = id;
udev->requests[id] = request;
reserved = true;
break;
}
}
spin_unlock(&udev->requests_lock);
return reserved;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Micah Dowty | 35 | 38.46% | 1 | 12.50% |
Vojtech Pavlik | 26 | 28.57% | 1 | 12.50% |
Dmitry Torokhov | 25 | 27.47% | 4 | 50.00% |
Zach Welch | 3 | 3.30% | 1 | 12.50% |
Aristeu Sergio Rozanski Filho | 2 | 2.20% | 1 | 12.50% |
Total | 91 | 100.00% | 8 | 100.00% |
static struct uinput_request *uinput_request_find(struct uinput_device *udev,
unsigned int id)
{
/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
if (id >= UINPUT_NUM_REQUESTS)
return NULL;
return udev->requests[id];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Micah Dowty | 19 | 54.29% | 1 | 33.33% |
Vojtech Pavlik | 15 | 42.86% | 1 | 33.33% |
Dmitry Torokhov | 1 | 2.86% | 1 | 33.33% |
Total | 35 | 100.00% | 3 | 100.00% |
static int uinput_request_reserve_slot(struct uinput_device *udev,
struct uinput_request *request)
{
/* Allocate slot. If none are available right away, wait. */
return wait_event_interruptible(udev->requests_waitq,
uinput_request_alloc_id(udev, request));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 15 | 46.88% | 1 | 33.33% |
Vojtech Pavlik | 12 | 37.50% | 1 | 33.33% |
Micah Dowty | 5 | 15.62% | 1 | 33.33% |
Total | 32 | 100.00% | 3 | 100.00% |
static void uinput_request_done(struct uinput_device *udev,
struct uinput_request *request)
{
/* Mark slot as available */
udev->requests[request->id] = NULL;
wake_up(&udev->requests_waitq);
complete(&request->done);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 33 | 75.00% | 3 | 60.00% |
Micah Dowty | 7 | 15.91% | 1 | 20.00% |
Vojtech Pavlik | 4 | 9.09% | 1 | 20.00% |
Total | 44 | 100.00% | 5 | 100.00% |
static int uinput_request_send(struct uinput_device *udev,
struct uinput_request *request)
{
int retval;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
return retval;
if (udev->state != UIST_CREATED) {
retval = -ENODEV;
goto out;
}
init_completion(&request->done);
/*
* Tell our userspace application about this new request
* by queueing an input event.
*/
uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
out:
mutex_unlock(&udev->mutex);
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Aristeu Sergio Rozanski Filho | 50 | 53.76% | 1 | 16.67% |
Micah Dowty | 24 | 25.81% | 1 | 16.67% |
Dmitry Torokhov | 13 | 13.98% | 3 | 50.00% |
Vojtech Pavlik | 6 | 6.45% | 1 | 16.67% |
Total | 93 | 100.00% | 6 | 100.00% |
static int uinput_request_submit(struct uinput_device *udev,
struct uinput_request *request)
{
int error;
error = uinput_request_reserve_slot(udev, request);
if (error)
return error;
error = uinput_request_send(udev, request);
if (error) {
uinput_request_done(udev, request);
return error;
}
wait_for_completion(&request->done);
return request->retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 73 | 100.00% | 1 | 100.00% |
Total | 73 | 100.00% | 1 | 100.00% |
/*
* Fail all outstanding requests so handlers don't wait for the userspace
* to finish processing them.
*/
static void uinput_flush_requests(struct uinput_device *udev)
{
struct uinput_request *request;
int i;
spin_lock(&udev->requests_lock);
for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {
request = udev->requests[i];
if (request) {
request->retval = -ENODEV;
uinput_request_done(udev, request);
}
}
spin_unlock(&udev->requests_lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Aristeu Sergio Rozanski Filho | 75 | 94.94% | 1 | 25.00% |
Micah Dowty | 2 | 2.53% | 1 | 25.00% |
Dmitry Torokhov | 1 | 1.27% | 1 | 25.00% |
Vojtech Pavlik | 1 | 1.27% | 1 | 25.00% |
Total | 79 | 100.00% | 4 | 100.00% |
static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
{
uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Anssi Hannula | 25 | 100.00% | 1 | 100.00% |
Total | 25 | 100.00% | 1 | 100.00% |
static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
{
uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Anssi Hannula | 25 | 100.00% | 1 | 100.00% |
Total | 25 | 100.00% | 1 | 100.00% |
static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
{
return uinput_dev_event(dev, EV_FF, effect_id, value);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Anssi Hannula | 29 | 100.00% | 1 | 100.00% |
Total | 29 | 100.00% | 1 | 100.00% |
static int uinput_dev_upload_effect(struct input_dev *dev,
struct ff_effect *effect,
struct ff_effect *old)
{
struct uinput_device *udev = input_get_drvdata(dev);
struct uinput_request request;
/*
* uinput driver does not currently support periodic effects with
* custom waveform since it does not have a way to pass buffer of
* samples (custom_data) to userspace. If ever there is a device
* supporting custom waveforms we would need to define an additional
* ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
*/
if (effect->type == FF_PERIODIC &&
effect->u.periodic.waveform == FF_CUSTOM)
return -EINVAL;
request.code = UI_FF_UPLOAD;
request.u.upload.effect = effect;
request.u.upload.old = old;
return uinput_request_submit(udev, &request);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Philip Langdale | 23 | 24.73% | 1 | 12.50% |
Micah Dowty | 18 | 19.35% | 1 | 12.50% |
Anssi Hannula | 17 | 18.28% | 1 | 12.50% |
Aristeu Sergio Rozanski Filho | 12 | 12.90% | 1 | 12.50% |
Dmitry Torokhov | 12 | 12.90% | 3 | 37.50% |
Zach Welch | 11 | 11.83% | 1 | 12.50% |
Total | 93 | 100.00% | 8 | 100.00% |
static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
{
struct uinput_device *udev = input_get_drvdata(dev);
struct uinput_request request;
if (!test_bit(EV_FF, dev->evbit))
return -ENOSYS;
request.code = UI_FF_ERASE;
request.u.effect_id = effect_id;
return uinput_request_submit(udev, &request);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Micah Dowty | 34 | 50.75% | 1 | 16.67% |
Aristeu Sergio Rozanski Filho | 12 | 17.91% | 1 | 16.67% |
Dmitry Torokhov | 12 | 17.91% | 3 | 50.00% |
Zach Welch | 9 | 13.43% | 1 | 16.67% |
Total | 67 | 100.00% | 6 | 100.00% |
static void uinput_destroy_device(struct uinput_device *udev)
{
const char *name, *phys;
struct input_dev *dev = udev->dev;
enum uinput_state old_state = udev->state;
udev->state = UIST_NEW_DEVICE;
if (dev) {
name = dev->name;
phys = dev->phys;
if (old_state == UIST_CREATED) {
uinput_flush_requests(udev);
input_unregister_device(dev);
} else {
input_free_device(dev);
}
kfree(name);
kfree(phys);
udev->dev = NULL;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 38 | 37.25% | 1 | 16.67% |
Aristeu Sergio Rozanski Filho | 33 | 32.35% | 1 | 16.67% |
Micah Dowty | 20 | 19.61% | 1 | 16.67% |
Vojtech Pavlik | 7 | 6.86% | 1 | 16.67% |
Zach Welch | 2 | 1.96% | 1 | 16.67% |
Randy Dunlap | 2 | 1.96% | 1 | 16.67% |
Total | 102 | 100.00% | 6 | 100.00% |
static int uinput_create_device(struct uinput_device *udev)
{
struct input_dev *dev = udev->dev;
int error, nslot;
if (udev->state != UIST_SETUP_COMPLETE) {
printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
return -EINVAL;
}
if (test_bit(EV_ABS, dev->evbit)) {
input_alloc_absinfo(dev);
if (!dev->absinfo) {
error = -EINVAL;
goto fail1;
}
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
error = input_mt_init_slots(dev, nslot, 0);
if (error)
goto fail1;
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
input_set_events_per_packet(dev, 60);
}
}
if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) {
printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n",
UINPUT_NAME);
error = -EINVAL;
goto fail1;
}
if (udev->ff_effects_max) {
error = input_ff_create(dev, udev->ff_effects_max);
if (error)
goto fail1;
dev->ff->upload = uinput_dev_upload_effect;
dev->ff->erase = uinput_dev_erase_effect;
dev->ff->playback = uinput_dev_playback;
dev->ff->set_gain = uinput_dev_set_gain;
dev->ff->set_autocenter = uinput_dev_set_autocenter;
}
error = input_register_device(udev->dev);
if (error)
goto fail2;
udev->state = UIST_CREATED;
return 0;
fail2: input_ff_destroy(dev);
fail1: uinput_destroy_device(udev);
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Anssi Hannula | 92 | 32.06% | 1 | 12.50% |
David Herrmann | 65 | 22.65% | 1 | 12.50% |
Dmitry Torokhov | 55 | 19.16% | 2 | 25.00% |
Elias Vanderstuyft | 34 | 11.85% | 1 | 12.50% |
Micah Dowty | 34 | 11.85% | 1 | 12.50% |
Randy Dunlap | 6 | 2.09% | 1 | 12.50% |
Vojtech Pavlik | 1 | 0.35% | 1 | 12.50% |
Total | 287 | 100.00% | 8 | 100.00% |
static int uinput_open(struct inode *inode, struct file *file)
{
struct uinput_device *newdev;
newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
if (!newdev)
return -ENOMEM;
mutex_init(&newdev->mutex);
spin_lock_init(&newdev->requests_lock);
init_waitqueue_head(&newdev->requests_waitq);
init_waitqueue_head(&newdev->waitq);
newdev->state = UIST_NEW_DEVICE;
file->private_data = newdev;
nonseekable_open(inode, file);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Micah Dowty | 47 | 48.45% | 1 | 12.50% |
Dmitry Torokhov | 25 | 25.77% | 4 | 50.00% |
Vojtech Pavlik | 18 | 18.56% | 1 | 12.50% |
Zach Welch | 5 | 5.15% | 1 | 12.50% |
Randy Dunlap | 2 | 2.06% | 1 | 12.50% |
Total | 97 | 100.00% | 8 | 100.00% |
static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code,
const struct input_absinfo *abs)
{
int min, max;
min = abs->minimum;
max = abs->maximum;
if ((min != 0 || max != 0) && max <= min) {
printk(KERN_DEBUG
"%s: invalid abs[%02x] min:%d max:%d\n",
UINPUT_NAME, code, min, max);
return -EINVAL;
}
if (abs->flat > max - min) {
printk(KERN_DEBUG
"%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n",
UINPUT_NAME, code, abs->flat, min, max);
return -EINVAL;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Herrmann | 40 | 36.04% | 2 | 28.57% |
Micah Dowty | 30 | 27.03% | 1 | 14.29% |
Peter Hutterer | 26 | 23.42% | 1 | 14.29% |
Vojtech Pavlik | 12 | 10.81% | 2 | 28.57% |
Daniel Mack | 3 | 2.70% | 1 | 14.29% |
Total | 111 | 100.00% | 7 | 100.00% |
static int uinput_validate_absbits(struct input_dev *dev)
{
unsigned int cnt;
int error;
if (!test_bit(EV_ABS, dev->evbit))
return 0;
/*
* Check if absmin/absmax/absfuzz/absflat are sane.
*/
for_each_set_bit(cnt, dev->absbit, ABS_CNT) {
if (!dev->absinfo)
return -EINVAL;
error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]);
if (error)
return error;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Herrmann | 78 | 95.12% | 2 | 40.00% |
Zach Welch | 2 | 2.44% | 1 | 20.00% |
Vojtech Pavlik | 1 | 1.22% | 1 | 20.00% |
Micah Dowty | 1 | 1.22% | 1 | 20.00% |
Total | 82 | 100.00% | 5 | 100.00% |
static int uinput_allocate_device(struct uinput_device *udev)
{
udev->dev = input_allocate_device();
if (!udev->dev)
return -ENOMEM;
udev->dev->event = uinput_dev_event;
input_set_drvdata(udev->dev, udev);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 32 | 65.31% | 2 | 50.00% |
Vojtech Pavlik | 10 | 20.41% | 1 | 25.00% |
Micah Dowty | 7 | 14.29% | 1 | 25.00% |
Total | 49 | 100.00% | 4 | 100.00% |
static int uinput_dev_setup(struct uinput_device *udev,
struct uinput_setup __user *arg)
{
struct uinput_setup setup;
struct input_dev *dev;
if (udev->state == UIST_CREATED)
return -EINVAL;
if (copy_from_user(&setup, arg, sizeof(setup)))
return -EFAULT;
if (!setup.name[0])
return -EINVAL;
dev = udev->dev;
dev->id = setup.id;
udev->ff_effects_max = setup.ff_effects_max;
kfree(dev->name);
dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL);
if (!dev->name)
return -ENOMEM;
udev->state = UIST_SETUP_COMPLETE;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Tissoires | 133 | 98.52% | 1 | 50.00% |
Dmitry Torokhov | 2 | 1.48% | 1 | 50.00% |
Total | 135 | 100.00% | 2 | 100.00% |
static int uinput_abs_setup(struct uinput_device *udev,
struct uinput_setup __user *arg, size_t size)
{
struct uinput_abs_setup setup = {};
struct input_dev *dev;
int error;
if (size > sizeof(setup))
return -E2BIG;
if (udev->state == UIST_CREATED)
return -EINVAL;
if (copy_from_user(&setup, arg, size))
return -EFAULT;
if (setup.code > ABS_MAX)
return -ERANGE;
dev = udev->dev;
error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo);
if (error)
return error;
input_alloc_absinfo(dev);
if (!dev->absinfo)
return -ENOMEM;
set_bit(setup.code, dev->absbit);
dev->absinfo[setup.code] = setup.absinfo;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Tissoires | 133 | 83.65% | 1 | 50.00% |
David Herrmann | 26 | 16.35% | 1 | 50.00% |
Total | 159 | 100.00% | 2 | 100.00% |
/* legacy setup via write() */
static int uinput_setup_device_legacy(struct uinput_device *udev,
const char __user *buffer, size_t count)
{
struct uinput_user_dev *user_dev;
struct input_dev *dev;
int i;
int retval;
if (count != sizeof(struct uinput_user_dev))
return -EINVAL;
if (!udev->dev) {
retval = uinput_allocate_device(udev);
if (retval)
return retval;
}
dev = udev->dev;
user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
if (IS_ERR(user_dev))
return PTR_ERR(user_dev);
udev->ff_effects_max = user_dev->ff_effects_max;
/* Ensure name is filled in */
if (!user_dev->name[0]) {
retval = -EINVAL;
goto exit;
}
kfree(dev->name);
dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,
GFP_KERNEL);
if (!dev->name) {
retval = -ENOMEM;
goto exit;
}
dev->id.bustype = user_dev->id.bustype;
dev->id.vendor = user_dev->id.vendor;
dev->id.product = user_dev->id.product;
dev->id.version = user_dev->id.version;
for (i = 0; i < ABS_CNT; i++) {
input_abs_set_max(dev, i, user_dev->absmax[i]);
input_abs_set_min(dev, i, user_dev->absmin[i]);
input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
input_abs_set_flat(dev, i, user_dev->absflat[i]);
}
retval = uinput_validate_absbits(dev);
if (retval < 0)
goto exit;
udev->state = UIST_SETUP_COMPLETE;
retval = count;
exit:
kfree(user_dev);
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 137 | 41.77% | 4 | 40.00% |
Micah Dowty | 136 | 41.46% | 1 | 10.00% |
Daniel Mack | 27 | 8.23% | 1 | 10.00% |
David Herrmann | 15 | 4.57% | 1 | 10.00% |
Anssi Hannula | 8 | 2.44% | 1 | 10.00% |
Benjamin Tissoires | 3 | 0.91% | 1 | 10.00% |
Ian Campbell | 2 | 0.61% | 1 | 10.00% |
Total | 328 | 100.00% | 10 | 100.00% |
static ssize_t uinput_inject_events(struct uinput_device *udev,
const char __user *buffer, size_t count)
{
struct input_event ev;
size_t bytes = 0;
if (count != 0 && count < input_event_size())
return -EINVAL;
while (bytes + input_event_size() <= count) {
/*
* Note that even if some events were fetched successfully
* we are still going to return EFAULT instead of partial
* count to let userspace know that it got it's buffers
* all wrong.
*/
if (input_event_from_user(buffer + bytes, &ev))
return -EFAULT;
input_event(udev->dev, ev.type, ev.code, ev.value);
bytes += input_event_size();
}
return bytes;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Micah Dowty | 52 | 52.53% | 1 | 25.00% |
Ryan Mallon | 29 | 29.29% | 1 | 25.00% |
Dmitry Torokhov | 10 | 10.10% | 1 | 25.00% |
Philip Langdale | 8 | 8.08% | 1 | 25.00% |
Total | 99 | 100.00% | 4 | 100.00% |
static ssize_t uinput_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct uinput_device *udev = file->private_data;
int retval;
if (count == 0)
return 0;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
return retval;
retval = udev->state == UIST_CREATED ?
uinput_inject_events(udev, buffer, count) :
uinput_setup_device_legacy(udev, buffer, count);
mutex_unlock(&udev->mutex);
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 85 | 85.86% | 3 | 50.00% |
Micah Dowty | 12 | 12.12% | 1 | 16.67% |
Ryan Mallon | 1 | 1.01% | 1 | 16.67% |
Benjamin Tissoires | 1 | 1.01% | 1 | 16.67% |
Total | 99 | 100.00% | 6 | 100.00% |
static bool uinput_fetch_next_event(struct uinput_device *udev,
struct input_event *event)
{
bool have_event;
spin_lock_irq(&udev->dev->event_lock);
have_event = udev->head != udev->tail;
if (have_event) {
*event = udev->buff[udev->tail];
udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
}
spin_unlock_irq(&udev->dev->event_lock);
return have_event;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 84 | 100.00% | 1 | 100.00% |
Total | 84 | 100.00% | 1 | 100.00% |
static ssize_t uinput_events_to_user(