/*
* Event char devices, giving access to raw input device events.
*
* Copyright (c) 1999-2002 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define EVDEV_MINOR_BASE 64
#define EVDEV_MINORS 32
#define EVDEV_MIN_BUFFER_SIZE 64U
#define EVDEV_BUF_PACKETS 8
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input/mt.h>
#include <linux/major.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include "input-compat.h"
enum evdev_clock_type {
EV_CLK_REAL = 0,
EV_CLK_MONO,
EV_CLK_BOOT,
EV_CLK_MAX
};
struct evdev {
int open;
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client __rcu *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
struct cdev cdev;
bool exist;
};
struct evdev_client {
unsigned int head;
unsigned int tail;
unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
int clk_type;
bool revoked;
unsigned int bufsize;
struct input_event buffer[];
};
/* flush queued events of type @type, caller must hold client->buffer_lock */
static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
{
unsigned int i, head, num;
unsigned int mask = client->bufsize - 1;
bool is_report;
struct input_event *ev;
BUG_ON(type == EV_SYN);
head = client->tail;
client->packet_head = client->tail;
/* init to 1 so a leading SYN_REPORT will not be dropped */
num = 1;
for (i = client->tail; i != client->head; i = (i + 1) & mask) {
ev = &client->buffer[i];
is_report = ev->type == EV_SYN && ev->code == SYN_REPORT;
if (ev->type == type) {
/* drop matched entry */
continue;
} else if (is_report && !num) {
/* drop empty SYN_REPORT groups */
continue;
} else if (head != i) {
/* move entry to fill the gap */
client->buffer[head].time = ev->time;
client->buffer[he