18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Input driver to ExplorerPS/2 device driver module.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 1999-2002 Vojtech Pavlik
68c2ecf20Sopenharmony_ci * Copyright (c) 2004      Dmitry Torokhov
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define MOUSEDEV_MINOR_BASE	32
128c2ecf20Sopenharmony_ci#define MOUSEDEV_MINORS		31
138c2ecf20Sopenharmony_ci#define MOUSEDEV_MIX		63
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/bitops.h>
168c2ecf20Sopenharmony_ci#include <linux/sched.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/poll.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/init.h>
218c2ecf20Sopenharmony_ci#include <linux/input.h>
228c2ecf20Sopenharmony_ci#include <linux/random.h>
238c2ecf20Sopenharmony_ci#include <linux/major.h>
248c2ecf20Sopenharmony_ci#include <linux/device.h>
258c2ecf20Sopenharmony_ci#include <linux/cdev.h>
268c2ecf20Sopenharmony_ci#include <linux/kernel.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces");
308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
338c2ecf20Sopenharmony_ci#define CONFIG_INPUT_MOUSEDEV_SCREEN_X	1024
348c2ecf20Sopenharmony_ci#endif
358c2ecf20Sopenharmony_ci#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
368c2ecf20Sopenharmony_ci#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y	768
378c2ecf20Sopenharmony_ci#endif
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X;
408c2ecf20Sopenharmony_cimodule_param(xres, uint, 0644);
418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(xres, "Horizontal screen resolution");
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
448c2ecf20Sopenharmony_cimodule_param(yres, uint, 0644);
458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(yres, "Vertical screen resolution");
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic unsigned tap_time = 200;
488c2ecf20Sopenharmony_cimodule_param(tap_time, uint, 0644);
498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)");
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistruct mousedev_hw_data {
528c2ecf20Sopenharmony_ci	int dx, dy, dz;
538c2ecf20Sopenharmony_ci	int x, y;
548c2ecf20Sopenharmony_ci	int abs_event;
558c2ecf20Sopenharmony_ci	unsigned long buttons;
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistruct mousedev {
598c2ecf20Sopenharmony_ci	int open;
608c2ecf20Sopenharmony_ci	struct input_handle handle;
618c2ecf20Sopenharmony_ci	wait_queue_head_t wait;
628c2ecf20Sopenharmony_ci	struct list_head client_list;
638c2ecf20Sopenharmony_ci	spinlock_t client_lock; /* protects client_list */
648c2ecf20Sopenharmony_ci	struct mutex mutex;
658c2ecf20Sopenharmony_ci	struct device dev;
668c2ecf20Sopenharmony_ci	struct cdev cdev;
678c2ecf20Sopenharmony_ci	bool exist;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	struct list_head mixdev_node;
708c2ecf20Sopenharmony_ci	bool opened_by_mixdev;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	struct mousedev_hw_data packet;
738c2ecf20Sopenharmony_ci	unsigned int pkt_count;
748c2ecf20Sopenharmony_ci	int old_x[4], old_y[4];
758c2ecf20Sopenharmony_ci	int frac_dx, frac_dy;
768c2ecf20Sopenharmony_ci	unsigned long touch;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	int (*open_device)(struct mousedev *mousedev);
798c2ecf20Sopenharmony_ci	void (*close_device)(struct mousedev *mousedev);
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cienum mousedev_emul {
838c2ecf20Sopenharmony_ci	MOUSEDEV_EMUL_PS2,
848c2ecf20Sopenharmony_ci	MOUSEDEV_EMUL_IMPS,
858c2ecf20Sopenharmony_ci	MOUSEDEV_EMUL_EXPS
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistruct mousedev_motion {
898c2ecf20Sopenharmony_ci	int dx, dy, dz;
908c2ecf20Sopenharmony_ci	unsigned long buttons;
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define PACKET_QUEUE_LEN	16
948c2ecf20Sopenharmony_cistruct mousedev_client {
958c2ecf20Sopenharmony_ci	struct fasync_struct *fasync;
968c2ecf20Sopenharmony_ci	struct mousedev *mousedev;
978c2ecf20Sopenharmony_ci	struct list_head node;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	struct mousedev_motion packets[PACKET_QUEUE_LEN];
1008c2ecf20Sopenharmony_ci	unsigned int head, tail;
1018c2ecf20Sopenharmony_ci	spinlock_t packet_lock;
1028c2ecf20Sopenharmony_ci	int pos_x, pos_y;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	u8 ps2[6];
1058c2ecf20Sopenharmony_ci	unsigned char ready, buffer, bufsiz;
1068c2ecf20Sopenharmony_ci	unsigned char imexseq, impsseq;
1078c2ecf20Sopenharmony_ci	enum mousedev_emul mode;
1088c2ecf20Sopenharmony_ci	unsigned long last_buttons;
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci#define MOUSEDEV_SEQ_LEN	6
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
1148c2ecf20Sopenharmony_cistatic unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic struct mousedev *mousedev_mix;
1178c2ecf20Sopenharmony_cistatic LIST_HEAD(mousedev_mix_list);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci#define fx(i)  (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
1208c2ecf20Sopenharmony_ci#define fy(i)  (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void mousedev_touchpad_event(struct input_dev *dev,
1238c2ecf20Sopenharmony_ci				    struct mousedev *mousedev,
1248c2ecf20Sopenharmony_ci				    unsigned int code, int value)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	int size, tmp;
1278c2ecf20Sopenharmony_ci	enum { FRACTION_DENOM = 128 };
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	switch (code) {
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	case ABS_X:
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		fx(0) = value;
1348c2ecf20Sopenharmony_ci		if (mousedev->touch && mousedev->pkt_count >= 2) {
1358c2ecf20Sopenharmony_ci			size = input_abs_get_max(dev, ABS_X) -
1368c2ecf20Sopenharmony_ci					input_abs_get_min(dev, ABS_X);
1378c2ecf20Sopenharmony_ci			if (size == 0)
1388c2ecf20Sopenharmony_ci				size = 256 * 2;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci			tmp = ((value - fx(2)) * 256 * FRACTION_DENOM) / size;
1418c2ecf20Sopenharmony_ci			tmp += mousedev->frac_dx;
1428c2ecf20Sopenharmony_ci			mousedev->packet.dx = tmp / FRACTION_DENOM;
1438c2ecf20Sopenharmony_ci			mousedev->frac_dx =
1448c2ecf20Sopenharmony_ci				tmp - mousedev->packet.dx * FRACTION_DENOM;
1458c2ecf20Sopenharmony_ci		}
1468c2ecf20Sopenharmony_ci		break;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	case ABS_Y:
1498c2ecf20Sopenharmony_ci		fy(0) = value;
1508c2ecf20Sopenharmony_ci		if (mousedev->touch && mousedev->pkt_count >= 2) {
1518c2ecf20Sopenharmony_ci			/* use X size for ABS_Y to keep the same scale */
1528c2ecf20Sopenharmony_ci			size = input_abs_get_max(dev, ABS_X) -
1538c2ecf20Sopenharmony_ci					input_abs_get_min(dev, ABS_X);
1548c2ecf20Sopenharmony_ci			if (size == 0)
1558c2ecf20Sopenharmony_ci				size = 256 * 2;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci			tmp = -((value - fy(2)) * 256 * FRACTION_DENOM) / size;
1588c2ecf20Sopenharmony_ci			tmp += mousedev->frac_dy;
1598c2ecf20Sopenharmony_ci			mousedev->packet.dy = tmp / FRACTION_DENOM;
1608c2ecf20Sopenharmony_ci			mousedev->frac_dy = tmp -
1618c2ecf20Sopenharmony_ci				mousedev->packet.dy * FRACTION_DENOM;
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci		break;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
1688c2ecf20Sopenharmony_ci				unsigned int code, int value)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	int min, max, size;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	switch (code) {
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	case ABS_X:
1758c2ecf20Sopenharmony_ci		min = input_abs_get_min(dev, ABS_X);
1768c2ecf20Sopenharmony_ci		max = input_abs_get_max(dev, ABS_X);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		size = max - min;
1798c2ecf20Sopenharmony_ci		if (size == 0)
1808c2ecf20Sopenharmony_ci			size = xres ? : 1;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		value = clamp(value, min, max);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		mousedev->packet.x = ((value - min) * xres) / size;
1858c2ecf20Sopenharmony_ci		mousedev->packet.abs_event = 1;
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	case ABS_Y:
1898c2ecf20Sopenharmony_ci		min = input_abs_get_min(dev, ABS_Y);
1908c2ecf20Sopenharmony_ci		max = input_abs_get_max(dev, ABS_Y);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		size = max - min;
1938c2ecf20Sopenharmony_ci		if (size == 0)
1948c2ecf20Sopenharmony_ci			size = yres ? : 1;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci		value = clamp(value, min, max);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		mousedev->packet.y = yres - ((value - min) * yres) / size;
1998c2ecf20Sopenharmony_ci		mousedev->packet.abs_event = 1;
2008c2ecf20Sopenharmony_ci		break;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic void mousedev_rel_event(struct mousedev *mousedev,
2058c2ecf20Sopenharmony_ci				unsigned int code, int value)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	switch (code) {
2088c2ecf20Sopenharmony_ci	case REL_X:
2098c2ecf20Sopenharmony_ci		mousedev->packet.dx += value;
2108c2ecf20Sopenharmony_ci		break;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	case REL_Y:
2138c2ecf20Sopenharmony_ci		mousedev->packet.dy -= value;
2148c2ecf20Sopenharmony_ci		break;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	case REL_WHEEL:
2178c2ecf20Sopenharmony_ci		mousedev->packet.dz -= value;
2188c2ecf20Sopenharmony_ci		break;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic void mousedev_key_event(struct mousedev *mousedev,
2238c2ecf20Sopenharmony_ci				unsigned int code, int value)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	int index;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	switch (code) {
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	case BTN_TOUCH:
2308c2ecf20Sopenharmony_ci	case BTN_0:
2318c2ecf20Sopenharmony_ci	case BTN_LEFT:		index = 0; break;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	case BTN_STYLUS:
2348c2ecf20Sopenharmony_ci	case BTN_1:
2358c2ecf20Sopenharmony_ci	case BTN_RIGHT:		index = 1; break;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	case BTN_2:
2388c2ecf20Sopenharmony_ci	case BTN_FORWARD:
2398c2ecf20Sopenharmony_ci	case BTN_STYLUS2:
2408c2ecf20Sopenharmony_ci	case BTN_MIDDLE:	index = 2; break;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	case BTN_3:
2438c2ecf20Sopenharmony_ci	case BTN_BACK:
2448c2ecf20Sopenharmony_ci	case BTN_SIDE:		index = 3; break;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	case BTN_4:
2478c2ecf20Sopenharmony_ci	case BTN_EXTRA:		index = 4; break;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	default:		return;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (value) {
2538c2ecf20Sopenharmony_ci		set_bit(index, &mousedev->packet.buttons);
2548c2ecf20Sopenharmony_ci		set_bit(index, &mousedev_mix->packet.buttons);
2558c2ecf20Sopenharmony_ci	} else {
2568c2ecf20Sopenharmony_ci		clear_bit(index, &mousedev->packet.buttons);
2578c2ecf20Sopenharmony_ci		clear_bit(index, &mousedev_mix->packet.buttons);
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic void mousedev_notify_readers(struct mousedev *mousedev,
2628c2ecf20Sopenharmony_ci				    struct mousedev_hw_data *packet)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct mousedev_client *client;
2658c2ecf20Sopenharmony_ci	struct mousedev_motion *p;
2668c2ecf20Sopenharmony_ci	unsigned int new_head;
2678c2ecf20Sopenharmony_ci	int wake_readers = 0;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	rcu_read_lock();
2708c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(client, &mousedev->client_list, node) {
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		/* Just acquire the lock, interrupts already disabled */
2738c2ecf20Sopenharmony_ci		spin_lock(&client->packet_lock);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		p = &client->packets[client->head];
2768c2ecf20Sopenharmony_ci		if (client->ready && p->buttons != mousedev->packet.buttons) {
2778c2ecf20Sopenharmony_ci			new_head = (client->head + 1) % PACKET_QUEUE_LEN;
2788c2ecf20Sopenharmony_ci			if (new_head != client->tail) {
2798c2ecf20Sopenharmony_ci				p = &client->packets[client->head = new_head];
2808c2ecf20Sopenharmony_ci				memset(p, 0, sizeof(struct mousedev_motion));
2818c2ecf20Sopenharmony_ci			}
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		if (packet->abs_event) {
2858c2ecf20Sopenharmony_ci			p->dx += packet->x - client->pos_x;
2868c2ecf20Sopenharmony_ci			p->dy += packet->y - client->pos_y;
2878c2ecf20Sopenharmony_ci			client->pos_x = packet->x;
2888c2ecf20Sopenharmony_ci			client->pos_y = packet->y;
2898c2ecf20Sopenharmony_ci		}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci		client->pos_x += packet->dx;
2928c2ecf20Sopenharmony_ci		client->pos_x = clamp_val(client->pos_x, 0, xres);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		client->pos_y += packet->dy;
2958c2ecf20Sopenharmony_ci		client->pos_y = clamp_val(client->pos_y, 0, yres);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci		p->dx += packet->dx;
2988c2ecf20Sopenharmony_ci		p->dy += packet->dy;
2998c2ecf20Sopenharmony_ci		p->dz += packet->dz;
3008c2ecf20Sopenharmony_ci		p->buttons = mousedev->packet.buttons;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		if (p->dx || p->dy || p->dz ||
3038c2ecf20Sopenharmony_ci		    p->buttons != client->last_buttons)
3048c2ecf20Sopenharmony_ci			client->ready = 1;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci		spin_unlock(&client->packet_lock);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		if (client->ready) {
3098c2ecf20Sopenharmony_ci			kill_fasync(&client->fasync, SIGIO, POLL_IN);
3108c2ecf20Sopenharmony_ci			wake_readers = 1;
3118c2ecf20Sopenharmony_ci		}
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci	rcu_read_unlock();
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (wake_readers)
3168c2ecf20Sopenharmony_ci		wake_up_interruptible(&mousedev->wait);
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	if (!value) {
3228c2ecf20Sopenharmony_ci		if (mousedev->touch &&
3238c2ecf20Sopenharmony_ci		    time_before(jiffies,
3248c2ecf20Sopenharmony_ci				mousedev->touch + msecs_to_jiffies(tap_time))) {
3258c2ecf20Sopenharmony_ci			/*
3268c2ecf20Sopenharmony_ci			 * Toggle left button to emulate tap.
3278c2ecf20Sopenharmony_ci			 * We rely on the fact that mousedev_mix always has 0
3288c2ecf20Sopenharmony_ci			 * motion packet so we won't mess current position.
3298c2ecf20Sopenharmony_ci			 */
3308c2ecf20Sopenharmony_ci			set_bit(0, &mousedev->packet.buttons);
3318c2ecf20Sopenharmony_ci			set_bit(0, &mousedev_mix->packet.buttons);
3328c2ecf20Sopenharmony_ci			mousedev_notify_readers(mousedev, &mousedev_mix->packet);
3338c2ecf20Sopenharmony_ci			mousedev_notify_readers(mousedev_mix,
3348c2ecf20Sopenharmony_ci						&mousedev_mix->packet);
3358c2ecf20Sopenharmony_ci			clear_bit(0, &mousedev->packet.buttons);
3368c2ecf20Sopenharmony_ci			clear_bit(0, &mousedev_mix->packet.buttons);
3378c2ecf20Sopenharmony_ci		}
3388c2ecf20Sopenharmony_ci		mousedev->touch = mousedev->pkt_count = 0;
3398c2ecf20Sopenharmony_ci		mousedev->frac_dx = 0;
3408c2ecf20Sopenharmony_ci		mousedev->frac_dy = 0;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	} else if (!mousedev->touch)
3438c2ecf20Sopenharmony_ci		mousedev->touch = jiffies;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic void mousedev_event(struct input_handle *handle,
3478c2ecf20Sopenharmony_ci			   unsigned int type, unsigned int code, int value)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct mousedev *mousedev = handle->private;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	switch (type) {
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	case EV_ABS:
3548c2ecf20Sopenharmony_ci		/* Ignore joysticks */
3558c2ecf20Sopenharmony_ci		if (test_bit(BTN_TRIGGER, handle->dev->keybit))
3568c2ecf20Sopenharmony_ci			return;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
3598c2ecf20Sopenharmony_ci			mousedev_touchpad_event(handle->dev,
3608c2ecf20Sopenharmony_ci						mousedev, code, value);
3618c2ecf20Sopenharmony_ci		else
3628c2ecf20Sopenharmony_ci			mousedev_abs_event(handle->dev, mousedev, code, value);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		break;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	case EV_REL:
3678c2ecf20Sopenharmony_ci		mousedev_rel_event(mousedev, code, value);
3688c2ecf20Sopenharmony_ci		break;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	case EV_KEY:
3718c2ecf20Sopenharmony_ci		if (value != 2) {
3728c2ecf20Sopenharmony_ci			if (code == BTN_TOUCH &&
3738c2ecf20Sopenharmony_ci			    test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
3748c2ecf20Sopenharmony_ci				mousedev_touchpad_touch(mousedev, value);
3758c2ecf20Sopenharmony_ci			else
3768c2ecf20Sopenharmony_ci				mousedev_key_event(mousedev, code, value);
3778c2ecf20Sopenharmony_ci		}
3788c2ecf20Sopenharmony_ci		break;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	case EV_SYN:
3818c2ecf20Sopenharmony_ci		if (code == SYN_REPORT) {
3828c2ecf20Sopenharmony_ci			if (mousedev->touch) {
3838c2ecf20Sopenharmony_ci				mousedev->pkt_count++;
3848c2ecf20Sopenharmony_ci				/*
3858c2ecf20Sopenharmony_ci				 * Input system eats duplicate events,
3868c2ecf20Sopenharmony_ci				 * but we need all of them to do correct
3878c2ecf20Sopenharmony_ci				 * averaging so apply present one forward
3888c2ecf20Sopenharmony_ci				 */
3898c2ecf20Sopenharmony_ci				fx(0) = fx(1);
3908c2ecf20Sopenharmony_ci				fy(0) = fy(1);
3918c2ecf20Sopenharmony_ci			}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci			mousedev_notify_readers(mousedev, &mousedev->packet);
3948c2ecf20Sopenharmony_ci			mousedev_notify_readers(mousedev_mix, &mousedev->packet);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci			mousedev->packet.dx = mousedev->packet.dy =
3978c2ecf20Sopenharmony_ci				mousedev->packet.dz = 0;
3988c2ecf20Sopenharmony_ci			mousedev->packet.abs_event = 0;
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci		break;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int mousedev_fasync(int fd, struct file *file, int on)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct mousedev_client *client = file->private_data;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	return fasync_helper(fd, file, on, &client->fasync);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic void mousedev_free(struct device *dev)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct mousedev *mousedev = container_of(dev, struct mousedev, dev);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	input_put_device(mousedev->handle.dev);
4168c2ecf20Sopenharmony_ci	kfree(mousedev);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic int mousedev_open_device(struct mousedev *mousedev)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	int retval;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	retval = mutex_lock_interruptible(&mousedev->mutex);
4248c2ecf20Sopenharmony_ci	if (retval)
4258c2ecf20Sopenharmony_ci		return retval;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (!mousedev->exist)
4288c2ecf20Sopenharmony_ci		retval = -ENODEV;
4298c2ecf20Sopenharmony_ci	else if (!mousedev->open++) {
4308c2ecf20Sopenharmony_ci		retval = input_open_device(&mousedev->handle);
4318c2ecf20Sopenharmony_ci		if (retval)
4328c2ecf20Sopenharmony_ci			mousedev->open--;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	mutex_unlock(&mousedev->mutex);
4368c2ecf20Sopenharmony_ci	return retval;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic void mousedev_close_device(struct mousedev *mousedev)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	mutex_lock(&mousedev->mutex);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (mousedev->exist && !--mousedev->open)
4448c2ecf20Sopenharmony_ci		input_close_device(&mousedev->handle);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	mutex_unlock(&mousedev->mutex);
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci/*
4508c2ecf20Sopenharmony_ci * Open all available devices so they can all be multiplexed in one.
4518c2ecf20Sopenharmony_ci * stream. Note that this function is called with mousedev_mix->mutex
4528c2ecf20Sopenharmony_ci * held.
4538c2ecf20Sopenharmony_ci */
4548c2ecf20Sopenharmony_cistatic int mixdev_open_devices(struct mousedev *mixdev)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	int error;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	error = mutex_lock_interruptible(&mixdev->mutex);
4598c2ecf20Sopenharmony_ci	if (error)
4608c2ecf20Sopenharmony_ci		return error;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (!mixdev->open++) {
4638c2ecf20Sopenharmony_ci		struct mousedev *mousedev;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
4668c2ecf20Sopenharmony_ci			if (!mousedev->opened_by_mixdev) {
4678c2ecf20Sopenharmony_ci				if (mousedev_open_device(mousedev))
4688c2ecf20Sopenharmony_ci					continue;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci				mousedev->opened_by_mixdev = true;
4718c2ecf20Sopenharmony_ci			}
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	mutex_unlock(&mixdev->mutex);
4768c2ecf20Sopenharmony_ci	return 0;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci/*
4808c2ecf20Sopenharmony_ci * Close all devices that were opened as part of multiplexed
4818c2ecf20Sopenharmony_ci * device. Note that this function is called with mousedev_mix->mutex
4828c2ecf20Sopenharmony_ci * held.
4838c2ecf20Sopenharmony_ci */
4848c2ecf20Sopenharmony_cistatic void mixdev_close_devices(struct mousedev *mixdev)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	mutex_lock(&mixdev->mutex);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (!--mixdev->open) {
4898c2ecf20Sopenharmony_ci		struct mousedev *mousedev;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
4928c2ecf20Sopenharmony_ci			if (mousedev->opened_by_mixdev) {
4938c2ecf20Sopenharmony_ci				mousedev->opened_by_mixdev = false;
4948c2ecf20Sopenharmony_ci				mousedev_close_device(mousedev);
4958c2ecf20Sopenharmony_ci			}
4968c2ecf20Sopenharmony_ci		}
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	mutex_unlock(&mixdev->mutex);
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic void mousedev_attach_client(struct mousedev *mousedev,
5048c2ecf20Sopenharmony_ci				   struct mousedev_client *client)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	spin_lock(&mousedev->client_lock);
5078c2ecf20Sopenharmony_ci	list_add_tail_rcu(&client->node, &mousedev->client_list);
5088c2ecf20Sopenharmony_ci	spin_unlock(&mousedev->client_lock);
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic void mousedev_detach_client(struct mousedev *mousedev,
5128c2ecf20Sopenharmony_ci				   struct mousedev_client *client)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	spin_lock(&mousedev->client_lock);
5158c2ecf20Sopenharmony_ci	list_del_rcu(&client->node);
5168c2ecf20Sopenharmony_ci	spin_unlock(&mousedev->client_lock);
5178c2ecf20Sopenharmony_ci	synchronize_rcu();
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic int mousedev_release(struct inode *inode, struct file *file)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct mousedev_client *client = file->private_data;
5238c2ecf20Sopenharmony_ci	struct mousedev *mousedev = client->mousedev;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	mousedev_detach_client(mousedev, client);
5268c2ecf20Sopenharmony_ci	kfree(client);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	mousedev->close_device(mousedev);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	return 0;
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic int mousedev_open(struct inode *inode, struct file *file)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	struct mousedev_client *client;
5368c2ecf20Sopenharmony_ci	struct mousedev *mousedev;
5378c2ecf20Sopenharmony_ci	int error;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
5408c2ecf20Sopenharmony_ci	if (imajor(inode) == MISC_MAJOR)
5418c2ecf20Sopenharmony_ci		mousedev = mousedev_mix;
5428c2ecf20Sopenharmony_ci	else
5438c2ecf20Sopenharmony_ci#endif
5448c2ecf20Sopenharmony_ci		mousedev = container_of(inode->i_cdev, struct mousedev, cdev);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
5478c2ecf20Sopenharmony_ci	if (!client)
5488c2ecf20Sopenharmony_ci		return -ENOMEM;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	spin_lock_init(&client->packet_lock);
5518c2ecf20Sopenharmony_ci	client->pos_x = xres / 2;
5528c2ecf20Sopenharmony_ci	client->pos_y = yres / 2;
5538c2ecf20Sopenharmony_ci	client->mousedev = mousedev;
5548c2ecf20Sopenharmony_ci	mousedev_attach_client(mousedev, client);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	error = mousedev->open_device(mousedev);
5578c2ecf20Sopenharmony_ci	if (error)
5588c2ecf20Sopenharmony_ci		goto err_free_client;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	file->private_data = client;
5618c2ecf20Sopenharmony_ci	stream_open(inode, file);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	return 0;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci err_free_client:
5668c2ecf20Sopenharmony_ci	mousedev_detach_client(mousedev, client);
5678c2ecf20Sopenharmony_ci	kfree(client);
5688c2ecf20Sopenharmony_ci	return error;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic void mousedev_packet(struct mousedev_client *client, u8 *ps2_data)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct mousedev_motion *p = &client->packets[client->tail];
5748c2ecf20Sopenharmony_ci	s8 dx, dy, dz;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	dx = clamp_val(p->dx, -127, 127);
5778c2ecf20Sopenharmony_ci	p->dx -= dx;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	dy = clamp_val(p->dy, -127, 127);
5808c2ecf20Sopenharmony_ci	p->dy -= dy;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	ps2_data[0] = BIT(3);
5838c2ecf20Sopenharmony_ci	ps2_data[0] |= ((dx & BIT(7)) >> 3) | ((dy & BIT(7)) >> 2);
5848c2ecf20Sopenharmony_ci	ps2_data[0] |= p->buttons & 0x07;
5858c2ecf20Sopenharmony_ci	ps2_data[1] = dx;
5868c2ecf20Sopenharmony_ci	ps2_data[2] = dy;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	switch (client->mode) {
5898c2ecf20Sopenharmony_ci	case MOUSEDEV_EMUL_EXPS:
5908c2ecf20Sopenharmony_ci		dz = clamp_val(p->dz, -7, 7);
5918c2ecf20Sopenharmony_ci		p->dz -= dz;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci		ps2_data[3] = (dz & 0x0f) | ((p->buttons & 0x18) << 1);
5948c2ecf20Sopenharmony_ci		client->bufsiz = 4;
5958c2ecf20Sopenharmony_ci		break;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	case MOUSEDEV_EMUL_IMPS:
5988c2ecf20Sopenharmony_ci		dz = clamp_val(p->dz, -127, 127);
5998c2ecf20Sopenharmony_ci		p->dz -= dz;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci		ps2_data[0] |= ((p->buttons & 0x10) >> 3) |
6028c2ecf20Sopenharmony_ci			       ((p->buttons & 0x08) >> 1);
6038c2ecf20Sopenharmony_ci		ps2_data[3] = dz;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci		client->bufsiz = 4;
6068c2ecf20Sopenharmony_ci		break;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	case MOUSEDEV_EMUL_PS2:
6098c2ecf20Sopenharmony_ci	default:
6108c2ecf20Sopenharmony_ci		p->dz = 0;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci		ps2_data[0] |= ((p->buttons & 0x10) >> 3) |
6138c2ecf20Sopenharmony_ci			       ((p->buttons & 0x08) >> 1);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci		client->bufsiz = 3;
6168c2ecf20Sopenharmony_ci		break;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (!p->dx && !p->dy && !p->dz) {
6208c2ecf20Sopenharmony_ci		if (client->tail == client->head) {
6218c2ecf20Sopenharmony_ci			client->ready = 0;
6228c2ecf20Sopenharmony_ci			client->last_buttons = p->buttons;
6238c2ecf20Sopenharmony_ci		} else
6248c2ecf20Sopenharmony_ci			client->tail = (client->tail + 1) % PACKET_QUEUE_LEN;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic void mousedev_generate_response(struct mousedev_client *client,
6298c2ecf20Sopenharmony_ci					int command)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	client->ps2[0] = 0xfa; /* ACK */
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	switch (command) {
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	case 0xeb: /* Poll */
6368c2ecf20Sopenharmony_ci		mousedev_packet(client, &client->ps2[1]);
6378c2ecf20Sopenharmony_ci		client->bufsiz++; /* account for leading ACK */
6388c2ecf20Sopenharmony_ci		break;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	case 0xf2: /* Get ID */
6418c2ecf20Sopenharmony_ci		switch (client->mode) {
6428c2ecf20Sopenharmony_ci		case MOUSEDEV_EMUL_PS2:
6438c2ecf20Sopenharmony_ci			client->ps2[1] = 0;
6448c2ecf20Sopenharmony_ci			break;
6458c2ecf20Sopenharmony_ci		case MOUSEDEV_EMUL_IMPS:
6468c2ecf20Sopenharmony_ci			client->ps2[1] = 3;
6478c2ecf20Sopenharmony_ci			break;
6488c2ecf20Sopenharmony_ci		case MOUSEDEV_EMUL_EXPS:
6498c2ecf20Sopenharmony_ci			client->ps2[1] = 4;
6508c2ecf20Sopenharmony_ci			break;
6518c2ecf20Sopenharmony_ci		}
6528c2ecf20Sopenharmony_ci		client->bufsiz = 2;
6538c2ecf20Sopenharmony_ci		break;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	case 0xe9: /* Get info */
6568c2ecf20Sopenharmony_ci		client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200;
6578c2ecf20Sopenharmony_ci		client->bufsiz = 4;
6588c2ecf20Sopenharmony_ci		break;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	case 0xff: /* Reset */
6618c2ecf20Sopenharmony_ci		client->impsseq = client->imexseq = 0;
6628c2ecf20Sopenharmony_ci		client->mode = MOUSEDEV_EMUL_PS2;
6638c2ecf20Sopenharmony_ci		client->ps2[1] = 0xaa; client->ps2[2] = 0x00;
6648c2ecf20Sopenharmony_ci		client->bufsiz = 3;
6658c2ecf20Sopenharmony_ci		break;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	default:
6688c2ecf20Sopenharmony_ci		client->bufsiz = 1;
6698c2ecf20Sopenharmony_ci		break;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci	client->buffer = client->bufsiz;
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic ssize_t mousedev_write(struct file *file, const char __user *buffer,
6758c2ecf20Sopenharmony_ci				size_t count, loff_t *ppos)
6768c2ecf20Sopenharmony_ci{
6778c2ecf20Sopenharmony_ci	struct mousedev_client *client = file->private_data;
6788c2ecf20Sopenharmony_ci	unsigned char c;
6798c2ecf20Sopenharmony_ci	unsigned int i;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci		if (get_user(c, buffer + i))
6848c2ecf20Sopenharmony_ci			return -EFAULT;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		spin_lock_irq(&client->packet_lock);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci		if (c == mousedev_imex_seq[client->imexseq]) {
6898c2ecf20Sopenharmony_ci			if (++client->imexseq == MOUSEDEV_SEQ_LEN) {
6908c2ecf20Sopenharmony_ci				client->imexseq = 0;
6918c2ecf20Sopenharmony_ci				client->mode = MOUSEDEV_EMUL_EXPS;
6928c2ecf20Sopenharmony_ci			}
6938c2ecf20Sopenharmony_ci		} else
6948c2ecf20Sopenharmony_ci			client->imexseq = 0;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci		if (c == mousedev_imps_seq[client->impsseq]) {
6978c2ecf20Sopenharmony_ci			if (++client->impsseq == MOUSEDEV_SEQ_LEN) {
6988c2ecf20Sopenharmony_ci				client->impsseq = 0;
6998c2ecf20Sopenharmony_ci				client->mode = MOUSEDEV_EMUL_IMPS;
7008c2ecf20Sopenharmony_ci			}
7018c2ecf20Sopenharmony_ci		} else
7028c2ecf20Sopenharmony_ci			client->impsseq = 0;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci		mousedev_generate_response(client, c);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci		spin_unlock_irq(&client->packet_lock);
7078c2ecf20Sopenharmony_ci		cond_resched();
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	kill_fasync(&client->fasync, SIGIO, POLL_IN);
7118c2ecf20Sopenharmony_ci	wake_up_interruptible(&client->mousedev->wait);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	return count;
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic ssize_t mousedev_read(struct file *file, char __user *buffer,
7178c2ecf20Sopenharmony_ci			     size_t count, loff_t *ppos)
7188c2ecf20Sopenharmony_ci{
7198c2ecf20Sopenharmony_ci	struct mousedev_client *client = file->private_data;
7208c2ecf20Sopenharmony_ci	struct mousedev *mousedev = client->mousedev;
7218c2ecf20Sopenharmony_ci	u8 data[sizeof(client->ps2)];
7228c2ecf20Sopenharmony_ci	int retval = 0;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	if (!client->ready && !client->buffer && mousedev->exist &&
7258c2ecf20Sopenharmony_ci	    (file->f_flags & O_NONBLOCK))
7268c2ecf20Sopenharmony_ci		return -EAGAIN;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	retval = wait_event_interruptible(mousedev->wait,
7298c2ecf20Sopenharmony_ci			!mousedev->exist || client->ready || client->buffer);
7308c2ecf20Sopenharmony_ci	if (retval)
7318c2ecf20Sopenharmony_ci		return retval;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	if (!mousedev->exist)
7348c2ecf20Sopenharmony_ci		return -ENODEV;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	spin_lock_irq(&client->packet_lock);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	if (!client->buffer && client->ready) {
7398c2ecf20Sopenharmony_ci		mousedev_packet(client, client->ps2);
7408c2ecf20Sopenharmony_ci		client->buffer = client->bufsiz;
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	if (count > client->buffer)
7448c2ecf20Sopenharmony_ci		count = client->buffer;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	memcpy(data, client->ps2 + client->bufsiz - client->buffer, count);
7478c2ecf20Sopenharmony_ci	client->buffer -= count;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	spin_unlock_irq(&client->packet_lock);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (copy_to_user(buffer, data, count))
7528c2ecf20Sopenharmony_ci		return -EFAULT;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	return count;
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci/* No kernel lock - fine */
7588c2ecf20Sopenharmony_cistatic __poll_t mousedev_poll(struct file *file, poll_table *wait)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	struct mousedev_client *client = file->private_data;
7618c2ecf20Sopenharmony_ci	struct mousedev *mousedev = client->mousedev;
7628c2ecf20Sopenharmony_ci	__poll_t mask;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	poll_wait(file, &mousedev->wait, wait);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	mask = mousedev->exist ? EPOLLOUT | EPOLLWRNORM : EPOLLHUP | EPOLLERR;
7678c2ecf20Sopenharmony_ci	if (client->ready || client->buffer)
7688c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	return mask;
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cistatic const struct file_operations mousedev_fops = {
7748c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7758c2ecf20Sopenharmony_ci	.read		= mousedev_read,
7768c2ecf20Sopenharmony_ci	.write		= mousedev_write,
7778c2ecf20Sopenharmony_ci	.poll		= mousedev_poll,
7788c2ecf20Sopenharmony_ci	.open		= mousedev_open,
7798c2ecf20Sopenharmony_ci	.release	= mousedev_release,
7808c2ecf20Sopenharmony_ci	.fasync		= mousedev_fasync,
7818c2ecf20Sopenharmony_ci	.llseek		= noop_llseek,
7828c2ecf20Sopenharmony_ci};
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci/*
7858c2ecf20Sopenharmony_ci * Mark device non-existent. This disables writes, ioctls and
7868c2ecf20Sopenharmony_ci * prevents new users from opening the device. Already posted
7878c2ecf20Sopenharmony_ci * blocking reads will stay, however new ones will fail.
7888c2ecf20Sopenharmony_ci */
7898c2ecf20Sopenharmony_cistatic void mousedev_mark_dead(struct mousedev *mousedev)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	mutex_lock(&mousedev->mutex);
7928c2ecf20Sopenharmony_ci	mousedev->exist = false;
7938c2ecf20Sopenharmony_ci	mutex_unlock(&mousedev->mutex);
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci/*
7978c2ecf20Sopenharmony_ci * Wake up users waiting for IO so they can disconnect from
7988c2ecf20Sopenharmony_ci * dead device.
7998c2ecf20Sopenharmony_ci */
8008c2ecf20Sopenharmony_cistatic void mousedev_hangup(struct mousedev *mousedev)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	struct mousedev_client *client;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	spin_lock(&mousedev->client_lock);
8058c2ecf20Sopenharmony_ci	list_for_each_entry(client, &mousedev->client_list, node)
8068c2ecf20Sopenharmony_ci		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
8078c2ecf20Sopenharmony_ci	spin_unlock(&mousedev->client_lock);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	wake_up_interruptible(&mousedev->wait);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic void mousedev_cleanup(struct mousedev *mousedev)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	struct input_handle *handle = &mousedev->handle;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	mousedev_mark_dead(mousedev);
8178c2ecf20Sopenharmony_ci	mousedev_hangup(mousedev);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	/* mousedev is marked dead so no one else accesses mousedev->open */
8208c2ecf20Sopenharmony_ci	if (mousedev->open)
8218c2ecf20Sopenharmony_ci		input_close_device(handle);
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic int mousedev_reserve_minor(bool mixdev)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	int minor;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (mixdev) {
8298c2ecf20Sopenharmony_ci		minor = input_get_new_minor(MOUSEDEV_MIX, 1, false);
8308c2ecf20Sopenharmony_ci		if (minor < 0)
8318c2ecf20Sopenharmony_ci			pr_err("failed to reserve mixdev minor: %d\n", minor);
8328c2ecf20Sopenharmony_ci	} else {
8338c2ecf20Sopenharmony_ci		minor = input_get_new_minor(MOUSEDEV_MINOR_BASE,
8348c2ecf20Sopenharmony_ci					    MOUSEDEV_MINORS, true);
8358c2ecf20Sopenharmony_ci		if (minor < 0)
8368c2ecf20Sopenharmony_ci			pr_err("failed to reserve new minor: %d\n", minor);
8378c2ecf20Sopenharmony_ci	}
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	return minor;
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_cistatic struct mousedev *mousedev_create(struct input_dev *dev,
8438c2ecf20Sopenharmony_ci					struct input_handler *handler,
8448c2ecf20Sopenharmony_ci					bool mixdev)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	struct mousedev *mousedev;
8478c2ecf20Sopenharmony_ci	int minor;
8488c2ecf20Sopenharmony_ci	int error;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	minor = mousedev_reserve_minor(mixdev);
8518c2ecf20Sopenharmony_ci	if (minor < 0) {
8528c2ecf20Sopenharmony_ci		error = minor;
8538c2ecf20Sopenharmony_ci		goto err_out;
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL);
8578c2ecf20Sopenharmony_ci	if (!mousedev) {
8588c2ecf20Sopenharmony_ci		error = -ENOMEM;
8598c2ecf20Sopenharmony_ci		goto err_free_minor;
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mousedev->client_list);
8638c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mousedev->mixdev_node);
8648c2ecf20Sopenharmony_ci	spin_lock_init(&mousedev->client_lock);
8658c2ecf20Sopenharmony_ci	mutex_init(&mousedev->mutex);
8668c2ecf20Sopenharmony_ci	lockdep_set_subclass(&mousedev->mutex,
8678c2ecf20Sopenharmony_ci			     mixdev ? SINGLE_DEPTH_NESTING : 0);
8688c2ecf20Sopenharmony_ci	init_waitqueue_head(&mousedev->wait);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	if (mixdev) {
8718c2ecf20Sopenharmony_ci		dev_set_name(&mousedev->dev, "mice");
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci		mousedev->open_device = mixdev_open_devices;
8748c2ecf20Sopenharmony_ci		mousedev->close_device = mixdev_close_devices;
8758c2ecf20Sopenharmony_ci	} else {
8768c2ecf20Sopenharmony_ci		int dev_no = minor;
8778c2ecf20Sopenharmony_ci		/* Normalize device number if it falls into legacy range */
8788c2ecf20Sopenharmony_ci		if (dev_no < MOUSEDEV_MINOR_BASE + MOUSEDEV_MINORS)
8798c2ecf20Sopenharmony_ci			dev_no -= MOUSEDEV_MINOR_BASE;
8808c2ecf20Sopenharmony_ci		dev_set_name(&mousedev->dev, "mouse%d", dev_no);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci		mousedev->open_device = mousedev_open_device;
8838c2ecf20Sopenharmony_ci		mousedev->close_device = mousedev_close_device;
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	mousedev->exist = true;
8878c2ecf20Sopenharmony_ci	mousedev->handle.dev = input_get_device(dev);
8888c2ecf20Sopenharmony_ci	mousedev->handle.name = dev_name(&mousedev->dev);
8898c2ecf20Sopenharmony_ci	mousedev->handle.handler = handler;
8908c2ecf20Sopenharmony_ci	mousedev->handle.private = mousedev;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	mousedev->dev.class = &input_class;
8938c2ecf20Sopenharmony_ci	if (dev)
8948c2ecf20Sopenharmony_ci		mousedev->dev.parent = &dev->dev;
8958c2ecf20Sopenharmony_ci	mousedev->dev.devt = MKDEV(INPUT_MAJOR, minor);
8968c2ecf20Sopenharmony_ci	mousedev->dev.release = mousedev_free;
8978c2ecf20Sopenharmony_ci	device_initialize(&mousedev->dev);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	if (!mixdev) {
9008c2ecf20Sopenharmony_ci		error = input_register_handle(&mousedev->handle);
9018c2ecf20Sopenharmony_ci		if (error)
9028c2ecf20Sopenharmony_ci			goto err_free_mousedev;
9038c2ecf20Sopenharmony_ci	}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	cdev_init(&mousedev->cdev, &mousedev_fops);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	error = cdev_device_add(&mousedev->cdev, &mousedev->dev);
9088c2ecf20Sopenharmony_ci	if (error)
9098c2ecf20Sopenharmony_ci		goto err_cleanup_mousedev;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	return mousedev;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci err_cleanup_mousedev:
9148c2ecf20Sopenharmony_ci	mousedev_cleanup(mousedev);
9158c2ecf20Sopenharmony_ci	if (!mixdev)
9168c2ecf20Sopenharmony_ci		input_unregister_handle(&mousedev->handle);
9178c2ecf20Sopenharmony_ci err_free_mousedev:
9188c2ecf20Sopenharmony_ci	put_device(&mousedev->dev);
9198c2ecf20Sopenharmony_ci err_free_minor:
9208c2ecf20Sopenharmony_ci	input_free_minor(minor);
9218c2ecf20Sopenharmony_ci err_out:
9228c2ecf20Sopenharmony_ci	return ERR_PTR(error);
9238c2ecf20Sopenharmony_ci}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_cistatic void mousedev_destroy(struct mousedev *mousedev)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	cdev_device_del(&mousedev->cdev, &mousedev->dev);
9288c2ecf20Sopenharmony_ci	mousedev_cleanup(mousedev);
9298c2ecf20Sopenharmony_ci	input_free_minor(MINOR(mousedev->dev.devt));
9308c2ecf20Sopenharmony_ci	if (mousedev != mousedev_mix)
9318c2ecf20Sopenharmony_ci		input_unregister_handle(&mousedev->handle);
9328c2ecf20Sopenharmony_ci	put_device(&mousedev->dev);
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic int mixdev_add_device(struct mousedev *mousedev)
9368c2ecf20Sopenharmony_ci{
9378c2ecf20Sopenharmony_ci	int retval;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	retval = mutex_lock_interruptible(&mousedev_mix->mutex);
9408c2ecf20Sopenharmony_ci	if (retval)
9418c2ecf20Sopenharmony_ci		return retval;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	if (mousedev_mix->open) {
9448c2ecf20Sopenharmony_ci		retval = mousedev_open_device(mousedev);
9458c2ecf20Sopenharmony_ci		if (retval)
9468c2ecf20Sopenharmony_ci			goto out;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci		mousedev->opened_by_mixdev = true;
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	get_device(&mousedev->dev);
9528c2ecf20Sopenharmony_ci	list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list);
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci out:
9558c2ecf20Sopenharmony_ci	mutex_unlock(&mousedev_mix->mutex);
9568c2ecf20Sopenharmony_ci	return retval;
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_cistatic void mixdev_remove_device(struct mousedev *mousedev)
9608c2ecf20Sopenharmony_ci{
9618c2ecf20Sopenharmony_ci	mutex_lock(&mousedev_mix->mutex);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	if (mousedev->opened_by_mixdev) {
9648c2ecf20Sopenharmony_ci		mousedev->opened_by_mixdev = false;
9658c2ecf20Sopenharmony_ci		mousedev_close_device(mousedev);
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	list_del_init(&mousedev->mixdev_node);
9698c2ecf20Sopenharmony_ci	mutex_unlock(&mousedev_mix->mutex);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	put_device(&mousedev->dev);
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_cistatic int mousedev_connect(struct input_handler *handler,
9758c2ecf20Sopenharmony_ci			    struct input_dev *dev,
9768c2ecf20Sopenharmony_ci			    const struct input_device_id *id)
9778c2ecf20Sopenharmony_ci{
9788c2ecf20Sopenharmony_ci	struct mousedev *mousedev;
9798c2ecf20Sopenharmony_ci	int error;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	mousedev = mousedev_create(dev, handler, false);
9828c2ecf20Sopenharmony_ci	if (IS_ERR(mousedev))
9838c2ecf20Sopenharmony_ci		return PTR_ERR(mousedev);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	error = mixdev_add_device(mousedev);
9868c2ecf20Sopenharmony_ci	if (error) {
9878c2ecf20Sopenharmony_ci		mousedev_destroy(mousedev);
9888c2ecf20Sopenharmony_ci		return error;
9898c2ecf20Sopenharmony_ci	}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	return 0;
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic void mousedev_disconnect(struct input_handle *handle)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	struct mousedev *mousedev = handle->private;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	mixdev_remove_device(mousedev);
9998c2ecf20Sopenharmony_ci	mousedev_destroy(mousedev);
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic const struct input_device_id mousedev_ids[] = {
10038c2ecf20Sopenharmony_ci	{
10048c2ecf20Sopenharmony_ci		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
10058c2ecf20Sopenharmony_ci				INPUT_DEVICE_ID_MATCH_KEYBIT |
10068c2ecf20Sopenharmony_ci				INPUT_DEVICE_ID_MATCH_RELBIT,
10078c2ecf20Sopenharmony_ci		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
10088c2ecf20Sopenharmony_ci		.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
10098c2ecf20Sopenharmony_ci		.relbit = { BIT_MASK(REL_X) | BIT_MASK(REL_Y) },
10108c2ecf20Sopenharmony_ci	},	/* A mouse like device, at least one button,
10118c2ecf20Sopenharmony_ci		   two relative axes */
10128c2ecf20Sopenharmony_ci	{
10138c2ecf20Sopenharmony_ci		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
10148c2ecf20Sopenharmony_ci				INPUT_DEVICE_ID_MATCH_RELBIT,
10158c2ecf20Sopenharmony_ci		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
10168c2ecf20Sopenharmony_ci		.relbit = { BIT_MASK(REL_WHEEL) },
10178c2ecf20Sopenharmony_ci	},	/* A separate scrollwheel */
10188c2ecf20Sopenharmony_ci	{
10198c2ecf20Sopenharmony_ci		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
10208c2ecf20Sopenharmony_ci				INPUT_DEVICE_ID_MATCH_KEYBIT |
10218c2ecf20Sopenharmony_ci				INPUT_DEVICE_ID_MATCH_ABSBIT,
10228c2ecf20Sopenharmony_ci		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
10238c2ecf20Sopenharmony_ci		.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
10248c2ecf20Sopenharmony_ci		.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
10258c2ecf20Sopenharmony_ci	},	/* A tablet like device, at least touch detection,
10268c2ecf20Sopenharmony_ci		   two absolute axes */
10278c2ecf20Sopenharmony_ci	{
10288c2ecf20Sopenharmony_ci		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
10298c2ecf20Sopenharmony_ci				INPUT_DEVICE_ID_MATCH_KEYBIT |
10308c2ecf20Sopenharmony_ci				INPUT_DEVICE_ID_MATCH_ABSBIT,
10318c2ecf20Sopenharmony_ci		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
10328c2ecf20Sopenharmony_ci		.keybit = { [BIT_WORD(BTN_TOOL_FINGER)] =
10338c2ecf20Sopenharmony_ci				BIT_MASK(BTN_TOOL_FINGER) },
10348c2ecf20Sopenharmony_ci		.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
10358c2ecf20Sopenharmony_ci				BIT_MASK(ABS_PRESSURE) |
10368c2ecf20Sopenharmony_ci				BIT_MASK(ABS_TOOL_WIDTH) },
10378c2ecf20Sopenharmony_ci	},	/* A touchpad */
10388c2ecf20Sopenharmony_ci	{
10398c2ecf20Sopenharmony_ci		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
10408c2ecf20Sopenharmony_ci			INPUT_DEVICE_ID_MATCH_KEYBIT |
10418c2ecf20Sopenharmony_ci			INPUT_DEVICE_ID_MATCH_ABSBIT,
10428c2ecf20Sopenharmony_ci		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
10438c2ecf20Sopenharmony_ci		.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
10448c2ecf20Sopenharmony_ci		.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
10458c2ecf20Sopenharmony_ci	},	/* Mouse-like device with absolute X and Y but ordinary
10468c2ecf20Sopenharmony_ci		   clicks, like hp ILO2 High Performance mouse */
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	{ },	/* Terminating entry */
10498c2ecf20Sopenharmony_ci};
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(input, mousedev_ids);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_cistatic struct input_handler mousedev_handler = {
10548c2ecf20Sopenharmony_ci	.event		= mousedev_event,
10558c2ecf20Sopenharmony_ci	.connect	= mousedev_connect,
10568c2ecf20Sopenharmony_ci	.disconnect	= mousedev_disconnect,
10578c2ecf20Sopenharmony_ci	.legacy_minors	= true,
10588c2ecf20Sopenharmony_ci	.minor		= MOUSEDEV_MINOR_BASE,
10598c2ecf20Sopenharmony_ci	.name		= "mousedev",
10608c2ecf20Sopenharmony_ci	.id_table	= mousedev_ids,
10618c2ecf20Sopenharmony_ci};
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
10648c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_cistatic struct miscdevice psaux_mouse = {
10678c2ecf20Sopenharmony_ci	.minor	= PSMOUSE_MINOR,
10688c2ecf20Sopenharmony_ci	.name	= "psaux",
10698c2ecf20Sopenharmony_ci	.fops	= &mousedev_fops,
10708c2ecf20Sopenharmony_ci};
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_cistatic bool psaux_registered;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_cistatic void __init mousedev_psaux_register(void)
10758c2ecf20Sopenharmony_ci{
10768c2ecf20Sopenharmony_ci	int error;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	error = misc_register(&psaux_mouse);
10798c2ecf20Sopenharmony_ci	if (error)
10808c2ecf20Sopenharmony_ci		pr_warn("could not register psaux device, error: %d\n",
10818c2ecf20Sopenharmony_ci			   error);
10828c2ecf20Sopenharmony_ci	else
10838c2ecf20Sopenharmony_ci		psaux_registered = true;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic void __exit mousedev_psaux_unregister(void)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	if (psaux_registered)
10898c2ecf20Sopenharmony_ci		misc_deregister(&psaux_mouse);
10908c2ecf20Sopenharmony_ci}
10918c2ecf20Sopenharmony_ci#else
10928c2ecf20Sopenharmony_cistatic inline void mousedev_psaux_register(void) { }
10938c2ecf20Sopenharmony_cistatic inline void mousedev_psaux_unregister(void) { }
10948c2ecf20Sopenharmony_ci#endif
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_cistatic int __init mousedev_init(void)
10978c2ecf20Sopenharmony_ci{
10988c2ecf20Sopenharmony_ci	int error;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	mousedev_mix = mousedev_create(NULL, &mousedev_handler, true);
11018c2ecf20Sopenharmony_ci	if (IS_ERR(mousedev_mix))
11028c2ecf20Sopenharmony_ci		return PTR_ERR(mousedev_mix);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	error = input_register_handler(&mousedev_handler);
11058c2ecf20Sopenharmony_ci	if (error) {
11068c2ecf20Sopenharmony_ci		mousedev_destroy(mousedev_mix);
11078c2ecf20Sopenharmony_ci		return error;
11088c2ecf20Sopenharmony_ci	}
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	mousedev_psaux_register();
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	pr_info("PS/2 mouse device common for all mice\n");
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	return 0;
11158c2ecf20Sopenharmony_ci}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_cistatic void __exit mousedev_exit(void)
11188c2ecf20Sopenharmony_ci{
11198c2ecf20Sopenharmony_ci	mousedev_psaux_unregister();
11208c2ecf20Sopenharmony_ci	input_unregister_handler(&mousedev_handler);
11218c2ecf20Sopenharmony_ci	mousedev_destroy(mousedev_mix);
11228c2ecf20Sopenharmony_ci}
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_cimodule_init(mousedev_init);
11258c2ecf20Sopenharmony_cimodule_exit(mousedev_exit);
1126