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