18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * User level driver support for input subsystem 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Heavily based on evdev.c by Vojtech Pavlik 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Changes/Revisions: 108c2ecf20Sopenharmony_ci * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>) 118c2ecf20Sopenharmony_ci * - add UI_GET_SYSNAME ioctl 128c2ecf20Sopenharmony_ci * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>) 138c2ecf20Sopenharmony_ci * - updated ff support for the changes in kernel interface 148c2ecf20Sopenharmony_ci * - added MODULE_VERSION 158c2ecf20Sopenharmony_ci * 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>) 168c2ecf20Sopenharmony_ci * - added force feedback support 178c2ecf20Sopenharmony_ci * - added UI_SET_PHYS 188c2ecf20Sopenharmony_ci * 0.1 20/06/2002 198c2ecf20Sopenharmony_ci * - first public version 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci#include <uapi/linux/uinput.h> 228c2ecf20Sopenharmony_ci#include <linux/poll.h> 238c2ecf20Sopenharmony_ci#include <linux/sched.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/init.h> 278c2ecf20Sopenharmony_ci#include <linux/fs.h> 288c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 298c2ecf20Sopenharmony_ci#include <linux/overflow.h> 308c2ecf20Sopenharmony_ci#include <linux/input/mt.h> 318c2ecf20Sopenharmony_ci#include "../input-compat.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define UINPUT_NAME "uinput" 348c2ecf20Sopenharmony_ci#define UINPUT_BUFFER_SIZE 16 358c2ecf20Sopenharmony_ci#define UINPUT_NUM_REQUESTS 16 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cienum uinput_state { UIST_NEW_DEVICE, UIST_SETUP_COMPLETE, UIST_CREATED }; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct uinput_request { 408c2ecf20Sopenharmony_ci unsigned int id; 418c2ecf20Sopenharmony_ci unsigned int code; /* UI_FF_UPLOAD, UI_FF_ERASE */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci int retval; 448c2ecf20Sopenharmony_ci struct completion done; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci union { 478c2ecf20Sopenharmony_ci unsigned int effect_id; 488c2ecf20Sopenharmony_ci struct { 498c2ecf20Sopenharmony_ci struct ff_effect *effect; 508c2ecf20Sopenharmony_ci struct ff_effect *old; 518c2ecf20Sopenharmony_ci } upload; 528c2ecf20Sopenharmony_ci } u; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct uinput_device { 568c2ecf20Sopenharmony_ci struct input_dev *dev; 578c2ecf20Sopenharmony_ci struct mutex mutex; 588c2ecf20Sopenharmony_ci enum uinput_state state; 598c2ecf20Sopenharmony_ci wait_queue_head_t waitq; 608c2ecf20Sopenharmony_ci unsigned char ready; 618c2ecf20Sopenharmony_ci unsigned char head; 628c2ecf20Sopenharmony_ci unsigned char tail; 638c2ecf20Sopenharmony_ci struct input_event buff[UINPUT_BUFFER_SIZE]; 648c2ecf20Sopenharmony_ci unsigned int ff_effects_max; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci struct uinput_request *requests[UINPUT_NUM_REQUESTS]; 678c2ecf20Sopenharmony_ci wait_queue_head_t requests_waitq; 688c2ecf20Sopenharmony_ci spinlock_t requests_lock; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int uinput_dev_event(struct input_dev *dev, 728c2ecf20Sopenharmony_ci unsigned int type, unsigned int code, int value) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct uinput_device *udev = input_get_drvdata(dev); 758c2ecf20Sopenharmony_ci struct timespec64 ts; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci ktime_get_ts64(&ts); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci udev->buff[udev->head] = (struct input_event) { 808c2ecf20Sopenharmony_ci .input_event_sec = ts.tv_sec, 818c2ecf20Sopenharmony_ci .input_event_usec = ts.tv_nsec / NSEC_PER_USEC, 828c2ecf20Sopenharmony_ci .type = type, 838c2ecf20Sopenharmony_ci .code = code, 848c2ecf20Sopenharmony_ci .value = value, 858c2ecf20Sopenharmony_ci }; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci wake_up_interruptible(&udev->waitq); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* Atomically allocate an ID for the given request. Returns 0 on success. */ 958c2ecf20Sopenharmony_cistatic bool uinput_request_alloc_id(struct uinput_device *udev, 968c2ecf20Sopenharmony_ci struct uinput_request *request) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci unsigned int id; 998c2ecf20Sopenharmony_ci bool reserved = false; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci spin_lock(&udev->requests_lock); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci for (id = 0; id < UINPUT_NUM_REQUESTS; id++) { 1048c2ecf20Sopenharmony_ci if (!udev->requests[id]) { 1058c2ecf20Sopenharmony_ci request->id = id; 1068c2ecf20Sopenharmony_ci udev->requests[id] = request; 1078c2ecf20Sopenharmony_ci reserved = true; 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci spin_unlock(&udev->requests_lock); 1138c2ecf20Sopenharmony_ci return reserved; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic struct uinput_request *uinput_request_find(struct uinput_device *udev, 1178c2ecf20Sopenharmony_ci unsigned int id) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ 1208c2ecf20Sopenharmony_ci if (id >= UINPUT_NUM_REQUESTS) 1218c2ecf20Sopenharmony_ci return NULL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return udev->requests[id]; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int uinput_request_reserve_slot(struct uinput_device *udev, 1278c2ecf20Sopenharmony_ci struct uinput_request *request) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci /* Allocate slot. If none are available right away, wait. */ 1308c2ecf20Sopenharmony_ci return wait_event_interruptible(udev->requests_waitq, 1318c2ecf20Sopenharmony_ci uinput_request_alloc_id(udev, request)); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void uinput_request_release_slot(struct uinput_device *udev, 1358c2ecf20Sopenharmony_ci unsigned int id) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci /* Mark slot as available */ 1388c2ecf20Sopenharmony_ci spin_lock(&udev->requests_lock); 1398c2ecf20Sopenharmony_ci udev->requests[id] = NULL; 1408c2ecf20Sopenharmony_ci spin_unlock(&udev->requests_lock); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci wake_up(&udev->requests_waitq); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int uinput_request_send(struct uinput_device *udev, 1468c2ecf20Sopenharmony_ci struct uinput_request *request) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci int retval; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci retval = mutex_lock_interruptible(&udev->mutex); 1518c2ecf20Sopenharmony_ci if (retval) 1528c2ecf20Sopenharmony_ci return retval; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (udev->state != UIST_CREATED) { 1558c2ecf20Sopenharmony_ci retval = -ENODEV; 1568c2ecf20Sopenharmony_ci goto out; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci init_completion(&request->done); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * Tell our userspace application about this new request 1638c2ecf20Sopenharmony_ci * by queueing an input event. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci out: 1688c2ecf20Sopenharmony_ci mutex_unlock(&udev->mutex); 1698c2ecf20Sopenharmony_ci return retval; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int uinput_request_submit(struct uinput_device *udev, 1738c2ecf20Sopenharmony_ci struct uinput_request *request) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int retval; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci retval = uinput_request_reserve_slot(udev, request); 1788c2ecf20Sopenharmony_ci if (retval) 1798c2ecf20Sopenharmony_ci return retval; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci retval = uinput_request_send(udev, request); 1828c2ecf20Sopenharmony_ci if (retval) 1838c2ecf20Sopenharmony_ci goto out; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&request->done, 30 * HZ)) { 1868c2ecf20Sopenharmony_ci retval = -ETIMEDOUT; 1878c2ecf20Sopenharmony_ci goto out; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci retval = request->retval; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci out: 1938c2ecf20Sopenharmony_ci uinput_request_release_slot(udev, request->id); 1948c2ecf20Sopenharmony_ci return retval; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* 1988c2ecf20Sopenharmony_ci * Fail all outstanding requests so handlers don't wait for the userspace 1998c2ecf20Sopenharmony_ci * to finish processing them. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_cistatic void uinput_flush_requests(struct uinput_device *udev) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct uinput_request *request; 2048c2ecf20Sopenharmony_ci int i; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci spin_lock(&udev->requests_lock); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci for (i = 0; i < UINPUT_NUM_REQUESTS; i++) { 2098c2ecf20Sopenharmony_ci request = udev->requests[i]; 2108c2ecf20Sopenharmony_ci if (request) { 2118c2ecf20Sopenharmony_ci request->retval = -ENODEV; 2128c2ecf20Sopenharmony_ci complete(&request->done); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci spin_unlock(&udev->requests_lock); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void uinput_dev_set_gain(struct input_dev *dev, u16 gain) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci uinput_dev_event(dev, EV_FF, FF_GAIN, gain); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int uinput_dev_playback(struct input_dev *dev, int effect_id, int value) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci return uinput_dev_event(dev, EV_FF, effect_id, value); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int uinput_dev_upload_effect(struct input_dev *dev, 2358c2ecf20Sopenharmony_ci struct ff_effect *effect, 2368c2ecf20Sopenharmony_ci struct ff_effect *old) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct uinput_device *udev = input_get_drvdata(dev); 2398c2ecf20Sopenharmony_ci struct uinput_request request; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* 2428c2ecf20Sopenharmony_ci * uinput driver does not currently support periodic effects with 2438c2ecf20Sopenharmony_ci * custom waveform since it does not have a way to pass buffer of 2448c2ecf20Sopenharmony_ci * samples (custom_data) to userspace. If ever there is a device 2458c2ecf20Sopenharmony_ci * supporting custom waveforms we would need to define an additional 2468c2ecf20Sopenharmony_ci * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci if (effect->type == FF_PERIODIC && 2498c2ecf20Sopenharmony_ci effect->u.periodic.waveform == FF_CUSTOM) 2508c2ecf20Sopenharmony_ci return -EINVAL; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci request.code = UI_FF_UPLOAD; 2538c2ecf20Sopenharmony_ci request.u.upload.effect = effect; 2548c2ecf20Sopenharmony_ci request.u.upload.old = old; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return uinput_request_submit(udev, &request); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct uinput_device *udev = input_get_drvdata(dev); 2628c2ecf20Sopenharmony_ci struct uinput_request request; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (!test_bit(EV_FF, dev->evbit)) 2658c2ecf20Sopenharmony_ci return -ENOSYS; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci request.code = UI_FF_ERASE; 2688c2ecf20Sopenharmony_ci request.u.effect_id = effect_id; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return uinput_request_submit(udev, &request); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int uinput_dev_flush(struct input_dev *dev, struct file *file) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci /* 2768c2ecf20Sopenharmony_ci * If we are called with file == NULL that means we are tearing 2778c2ecf20Sopenharmony_ci * down the device, and therefore we can not handle FF erase 2788c2ecf20Sopenharmony_ci * requests: either we are handling UI_DEV_DESTROY (and holding 2798c2ecf20Sopenharmony_ci * the udev->mutex), or the file descriptor is closed and there is 2808c2ecf20Sopenharmony_ci * nobody on the other side anymore. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci return file ? input_ff_flush(dev, file) : 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic void uinput_destroy_device(struct uinput_device *udev) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci const char *name, *phys; 2888c2ecf20Sopenharmony_ci struct input_dev *dev = udev->dev; 2898c2ecf20Sopenharmony_ci enum uinput_state old_state = udev->state; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci udev->state = UIST_NEW_DEVICE; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (dev) { 2948c2ecf20Sopenharmony_ci name = dev->name; 2958c2ecf20Sopenharmony_ci phys = dev->phys; 2968c2ecf20Sopenharmony_ci if (old_state == UIST_CREATED) { 2978c2ecf20Sopenharmony_ci uinput_flush_requests(udev); 2988c2ecf20Sopenharmony_ci input_unregister_device(dev); 2998c2ecf20Sopenharmony_ci } else { 3008c2ecf20Sopenharmony_ci input_free_device(dev); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci kfree(name); 3038c2ecf20Sopenharmony_ci kfree(phys); 3048c2ecf20Sopenharmony_ci udev->dev = NULL; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int uinput_create_device(struct uinput_device *udev) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct input_dev *dev = udev->dev; 3118c2ecf20Sopenharmony_ci int error, nslot; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (udev->state != UIST_SETUP_COMPLETE) { 3148c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); 3158c2ecf20Sopenharmony_ci return -EINVAL; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (test_bit(EV_ABS, dev->evbit)) { 3198c2ecf20Sopenharmony_ci input_alloc_absinfo(dev); 3208c2ecf20Sopenharmony_ci if (!dev->absinfo) { 3218c2ecf20Sopenharmony_ci error = -EINVAL; 3228c2ecf20Sopenharmony_ci goto fail1; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (test_bit(ABS_MT_SLOT, dev->absbit)) { 3268c2ecf20Sopenharmony_ci nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; 3278c2ecf20Sopenharmony_ci error = input_mt_init_slots(dev, nslot, 0); 3288c2ecf20Sopenharmony_ci if (error) 3298c2ecf20Sopenharmony_ci goto fail1; 3308c2ecf20Sopenharmony_ci } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { 3318c2ecf20Sopenharmony_ci input_set_events_per_packet(dev, 60); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) { 3368c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n", 3378c2ecf20Sopenharmony_ci UINPUT_NAME); 3388c2ecf20Sopenharmony_ci error = -EINVAL; 3398c2ecf20Sopenharmony_ci goto fail1; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (udev->ff_effects_max) { 3438c2ecf20Sopenharmony_ci error = input_ff_create(dev, udev->ff_effects_max); 3448c2ecf20Sopenharmony_ci if (error) 3458c2ecf20Sopenharmony_ci goto fail1; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci dev->ff->upload = uinput_dev_upload_effect; 3488c2ecf20Sopenharmony_ci dev->ff->erase = uinput_dev_erase_effect; 3498c2ecf20Sopenharmony_ci dev->ff->playback = uinput_dev_playback; 3508c2ecf20Sopenharmony_ci dev->ff->set_gain = uinput_dev_set_gain; 3518c2ecf20Sopenharmony_ci dev->ff->set_autocenter = uinput_dev_set_autocenter; 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * The standard input_ff_flush() implementation does 3548c2ecf20Sopenharmony_ci * not quite work for uinput as we can't reasonably 3558c2ecf20Sopenharmony_ci * handle FF requests during device teardown. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci dev->flush = uinput_dev_flush; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci dev->event = uinput_dev_event; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci input_set_drvdata(udev->dev, udev); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci error = input_register_device(udev->dev); 3658c2ecf20Sopenharmony_ci if (error) 3668c2ecf20Sopenharmony_ci goto fail2; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci udev->state = UIST_CREATED; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci fail2: input_ff_destroy(dev); 3738c2ecf20Sopenharmony_ci fail1: uinput_destroy_device(udev); 3748c2ecf20Sopenharmony_ci return error; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int uinput_open(struct inode *inode, struct file *file) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct uinput_device *newdev; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL); 3828c2ecf20Sopenharmony_ci if (!newdev) 3838c2ecf20Sopenharmony_ci return -ENOMEM; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci mutex_init(&newdev->mutex); 3868c2ecf20Sopenharmony_ci spin_lock_init(&newdev->requests_lock); 3878c2ecf20Sopenharmony_ci init_waitqueue_head(&newdev->requests_waitq); 3888c2ecf20Sopenharmony_ci init_waitqueue_head(&newdev->waitq); 3898c2ecf20Sopenharmony_ci newdev->state = UIST_NEW_DEVICE; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci file->private_data = newdev; 3928c2ecf20Sopenharmony_ci stream_open(inode, file); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int uinput_validate_absinfo(struct input_dev *dev, unsigned int code, 3988c2ecf20Sopenharmony_ci const struct input_absinfo *abs) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci int min, max, range; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci min = abs->minimum; 4038c2ecf20Sopenharmony_ci max = abs->maximum; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if ((min != 0 || max != 0) && max < min) { 4068c2ecf20Sopenharmony_ci printk(KERN_DEBUG 4078c2ecf20Sopenharmony_ci "%s: invalid abs[%02x] min:%d max:%d\n", 4088c2ecf20Sopenharmony_ci UINPUT_NAME, code, min, max); 4098c2ecf20Sopenharmony_ci return -EINVAL; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!check_sub_overflow(max, min, &range) && abs->flat > range) { 4138c2ecf20Sopenharmony_ci printk(KERN_DEBUG 4148c2ecf20Sopenharmony_ci "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n", 4158c2ecf20Sopenharmony_ci UINPUT_NAME, code, abs->flat, min, max); 4168c2ecf20Sopenharmony_ci return -EINVAL; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* 4208c2ecf20Sopenharmony_ci * Limit number of contacts to a reasonable value (100). This 4218c2ecf20Sopenharmony_ci * ensures that we need less than 2 pages for struct input_mt 4228c2ecf20Sopenharmony_ci * (we are not using in-kernel slot assignment so not going to 4238c2ecf20Sopenharmony_ci * allocate memory for the "red" table), and we should have no 4248c2ecf20Sopenharmony_ci * trouble getting this much memory. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci if (code == ABS_MT_SLOT && max > 99) { 4278c2ecf20Sopenharmony_ci printk(KERN_DEBUG 4288c2ecf20Sopenharmony_ci "%s: unreasonably large number of slots requested: %d\n", 4298c2ecf20Sopenharmony_ci UINPUT_NAME, max); 4308c2ecf20Sopenharmony_ci return -EINVAL; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int uinput_validate_absbits(struct input_dev *dev) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci unsigned int cnt; 4398c2ecf20Sopenharmony_ci int error; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (!test_bit(EV_ABS, dev->evbit)) 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* 4458c2ecf20Sopenharmony_ci * Check if absmin/absmax/absfuzz/absflat are sane. 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci for_each_set_bit(cnt, dev->absbit, ABS_CNT) { 4498c2ecf20Sopenharmony_ci if (!dev->absinfo) 4508c2ecf20Sopenharmony_ci return -EINVAL; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]); 4538c2ecf20Sopenharmony_ci if (error) 4548c2ecf20Sopenharmony_ci return error; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic int uinput_dev_setup(struct uinput_device *udev, 4618c2ecf20Sopenharmony_ci struct uinput_setup __user *arg) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct uinput_setup setup; 4648c2ecf20Sopenharmony_ci struct input_dev *dev; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (udev->state == UIST_CREATED) 4678c2ecf20Sopenharmony_ci return -EINVAL; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (copy_from_user(&setup, arg, sizeof(setup))) 4708c2ecf20Sopenharmony_ci return -EFAULT; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (!setup.name[0]) 4738c2ecf20Sopenharmony_ci return -EINVAL; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci dev = udev->dev; 4768c2ecf20Sopenharmony_ci dev->id = setup.id; 4778c2ecf20Sopenharmony_ci udev->ff_effects_max = setup.ff_effects_max; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci kfree(dev->name); 4808c2ecf20Sopenharmony_ci dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL); 4818c2ecf20Sopenharmony_ci if (!dev->name) 4828c2ecf20Sopenharmony_ci return -ENOMEM; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci udev->state = UIST_SETUP_COMPLETE; 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic int uinput_abs_setup(struct uinput_device *udev, 4898c2ecf20Sopenharmony_ci struct uinput_setup __user *arg, size_t size) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct uinput_abs_setup setup = {}; 4928c2ecf20Sopenharmony_ci struct input_dev *dev; 4938c2ecf20Sopenharmony_ci int error; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (size > sizeof(setup)) 4968c2ecf20Sopenharmony_ci return -E2BIG; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (udev->state == UIST_CREATED) 4998c2ecf20Sopenharmony_ci return -EINVAL; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (copy_from_user(&setup, arg, size)) 5028c2ecf20Sopenharmony_ci return -EFAULT; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (setup.code > ABS_MAX) 5058c2ecf20Sopenharmony_ci return -ERANGE; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci dev = udev->dev; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo); 5108c2ecf20Sopenharmony_ci if (error) 5118c2ecf20Sopenharmony_ci return error; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci input_alloc_absinfo(dev); 5148c2ecf20Sopenharmony_ci if (!dev->absinfo) 5158c2ecf20Sopenharmony_ci return -ENOMEM; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci set_bit(setup.code, dev->absbit); 5188c2ecf20Sopenharmony_ci dev->absinfo[setup.code] = setup.absinfo; 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/* legacy setup via write() */ 5238c2ecf20Sopenharmony_cistatic int uinput_setup_device_legacy(struct uinput_device *udev, 5248c2ecf20Sopenharmony_ci const char __user *buffer, size_t count) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct uinput_user_dev *user_dev; 5278c2ecf20Sopenharmony_ci struct input_dev *dev; 5288c2ecf20Sopenharmony_ci int i; 5298c2ecf20Sopenharmony_ci int retval; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (count != sizeof(struct uinput_user_dev)) 5328c2ecf20Sopenharmony_ci return -EINVAL; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (!udev->dev) { 5358c2ecf20Sopenharmony_ci udev->dev = input_allocate_device(); 5368c2ecf20Sopenharmony_ci if (!udev->dev) 5378c2ecf20Sopenharmony_ci return -ENOMEM; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci dev = udev->dev; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev)); 5438c2ecf20Sopenharmony_ci if (IS_ERR(user_dev)) 5448c2ecf20Sopenharmony_ci return PTR_ERR(user_dev); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci udev->ff_effects_max = user_dev->ff_effects_max; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* Ensure name is filled in */ 5498c2ecf20Sopenharmony_ci if (!user_dev->name[0]) { 5508c2ecf20Sopenharmony_ci retval = -EINVAL; 5518c2ecf20Sopenharmony_ci goto exit; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci kfree(dev->name); 5558c2ecf20Sopenharmony_ci dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE, 5568c2ecf20Sopenharmony_ci GFP_KERNEL); 5578c2ecf20Sopenharmony_ci if (!dev->name) { 5588c2ecf20Sopenharmony_ci retval = -ENOMEM; 5598c2ecf20Sopenharmony_ci goto exit; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci dev->id.bustype = user_dev->id.bustype; 5638c2ecf20Sopenharmony_ci dev->id.vendor = user_dev->id.vendor; 5648c2ecf20Sopenharmony_ci dev->id.product = user_dev->id.product; 5658c2ecf20Sopenharmony_ci dev->id.version = user_dev->id.version; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci for (i = 0; i < ABS_CNT; i++) { 5688c2ecf20Sopenharmony_ci input_abs_set_max(dev, i, user_dev->absmax[i]); 5698c2ecf20Sopenharmony_ci input_abs_set_min(dev, i, user_dev->absmin[i]); 5708c2ecf20Sopenharmony_ci input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]); 5718c2ecf20Sopenharmony_ci input_abs_set_flat(dev, i, user_dev->absflat[i]); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci retval = uinput_validate_absbits(dev); 5758c2ecf20Sopenharmony_ci if (retval < 0) 5768c2ecf20Sopenharmony_ci goto exit; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci udev->state = UIST_SETUP_COMPLETE; 5798c2ecf20Sopenharmony_ci retval = count; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci exit: 5828c2ecf20Sopenharmony_ci kfree(user_dev); 5838c2ecf20Sopenharmony_ci return retval; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic ssize_t uinput_inject_events(struct uinput_device *udev, 5878c2ecf20Sopenharmony_ci const char __user *buffer, size_t count) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci struct input_event ev; 5908c2ecf20Sopenharmony_ci size_t bytes = 0; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (count != 0 && count < input_event_size()) 5938c2ecf20Sopenharmony_ci return -EINVAL; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci while (bytes + input_event_size() <= count) { 5968c2ecf20Sopenharmony_ci /* 5978c2ecf20Sopenharmony_ci * Note that even if some events were fetched successfully 5988c2ecf20Sopenharmony_ci * we are still going to return EFAULT instead of partial 5998c2ecf20Sopenharmony_ci * count to let userspace know that it got it's buffers 6008c2ecf20Sopenharmony_ci * all wrong. 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_ci if (input_event_from_user(buffer + bytes, &ev)) 6038c2ecf20Sopenharmony_ci return -EFAULT; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci input_event(udev->dev, ev.type, ev.code, ev.value); 6068c2ecf20Sopenharmony_ci bytes += input_event_size(); 6078c2ecf20Sopenharmony_ci cond_resched(); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return bytes; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic ssize_t uinput_write(struct file *file, const char __user *buffer, 6148c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct uinput_device *udev = file->private_data; 6178c2ecf20Sopenharmony_ci int retval; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (count == 0) 6208c2ecf20Sopenharmony_ci return 0; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci retval = mutex_lock_interruptible(&udev->mutex); 6238c2ecf20Sopenharmony_ci if (retval) 6248c2ecf20Sopenharmony_ci return retval; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci retval = udev->state == UIST_CREATED ? 6278c2ecf20Sopenharmony_ci uinput_inject_events(udev, buffer, count) : 6288c2ecf20Sopenharmony_ci uinput_setup_device_legacy(udev, buffer, count); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci mutex_unlock(&udev->mutex); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return retval; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic bool uinput_fetch_next_event(struct uinput_device *udev, 6368c2ecf20Sopenharmony_ci struct input_event *event) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci bool have_event; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci spin_lock_irq(&udev->dev->event_lock); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci have_event = udev->head != udev->tail; 6438c2ecf20Sopenharmony_ci if (have_event) { 6448c2ecf20Sopenharmony_ci *event = udev->buff[udev->tail]; 6458c2ecf20Sopenharmony_ci udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci spin_unlock_irq(&udev->dev->event_lock); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return have_event; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic ssize_t uinput_events_to_user(struct uinput_device *udev, 6548c2ecf20Sopenharmony_ci char __user *buffer, size_t count) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct input_event event; 6578c2ecf20Sopenharmony_ci size_t read = 0; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci while (read + input_event_size() <= count && 6608c2ecf20Sopenharmony_ci uinput_fetch_next_event(udev, &event)) { 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (input_event_to_user(buffer + read, &event)) 6638c2ecf20Sopenharmony_ci return -EFAULT; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci read += input_event_size(); 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return read; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic ssize_t uinput_read(struct file *file, char __user *buffer, 6728c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct uinput_device *udev = file->private_data; 6758c2ecf20Sopenharmony_ci ssize_t retval; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (count != 0 && count < input_event_size()) 6788c2ecf20Sopenharmony_ci return -EINVAL; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci do { 6818c2ecf20Sopenharmony_ci retval = mutex_lock_interruptible(&udev->mutex); 6828c2ecf20Sopenharmony_ci if (retval) 6838c2ecf20Sopenharmony_ci return retval; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (udev->state != UIST_CREATED) 6868c2ecf20Sopenharmony_ci retval = -ENODEV; 6878c2ecf20Sopenharmony_ci else if (udev->head == udev->tail && 6888c2ecf20Sopenharmony_ci (file->f_flags & O_NONBLOCK)) 6898c2ecf20Sopenharmony_ci retval = -EAGAIN; 6908c2ecf20Sopenharmony_ci else 6918c2ecf20Sopenharmony_ci retval = uinput_events_to_user(udev, buffer, count); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci mutex_unlock(&udev->mutex); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (retval || count == 0) 6968c2ecf20Sopenharmony_ci break; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (!(file->f_flags & O_NONBLOCK)) 6998c2ecf20Sopenharmony_ci retval = wait_event_interruptible(udev->waitq, 7008c2ecf20Sopenharmony_ci udev->head != udev->tail || 7018c2ecf20Sopenharmony_ci udev->state != UIST_CREATED); 7028c2ecf20Sopenharmony_ci } while (retval == 0); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return retval; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic __poll_t uinput_poll(struct file *file, poll_table *wait) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci struct uinput_device *udev = file->private_data; 7108c2ecf20Sopenharmony_ci __poll_t mask = EPOLLOUT | EPOLLWRNORM; /* uinput is always writable */ 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci poll_wait(file, &udev->waitq, wait); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (udev->head != udev->tail) 7158c2ecf20Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return mask; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int uinput_release(struct inode *inode, struct file *file) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct uinput_device *udev = file->private_data; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci uinput_destroy_device(udev); 7258c2ecf20Sopenharmony_ci kfree(udev); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci return 0; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 7318c2ecf20Sopenharmony_cistruct uinput_ff_upload_compat { 7328c2ecf20Sopenharmony_ci __u32 request_id; 7338c2ecf20Sopenharmony_ci __s32 retval; 7348c2ecf20Sopenharmony_ci struct ff_effect_compat effect; 7358c2ecf20Sopenharmony_ci struct ff_effect_compat old; 7368c2ecf20Sopenharmony_ci}; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic int uinput_ff_upload_to_user(char __user *buffer, 7398c2ecf20Sopenharmony_ci const struct uinput_ff_upload *ff_up) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci if (in_compat_syscall()) { 7428c2ecf20Sopenharmony_ci struct uinput_ff_upload_compat ff_up_compat; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci ff_up_compat.request_id = ff_up->request_id; 7458c2ecf20Sopenharmony_ci ff_up_compat.retval = ff_up->retval; 7468c2ecf20Sopenharmony_ci /* 7478c2ecf20Sopenharmony_ci * It so happens that the pointer that gives us the trouble 7488c2ecf20Sopenharmony_ci * is the last field in the structure. Since we don't support 7498c2ecf20Sopenharmony_ci * custom waveforms in uinput anyway we can just copy the whole 7508c2ecf20Sopenharmony_ci * thing (to the compat size) and ignore the pointer. 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_ci memcpy(&ff_up_compat.effect, &ff_up->effect, 7538c2ecf20Sopenharmony_ci sizeof(struct ff_effect_compat)); 7548c2ecf20Sopenharmony_ci memcpy(&ff_up_compat.old, &ff_up->old, 7558c2ecf20Sopenharmony_ci sizeof(struct ff_effect_compat)); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (copy_to_user(buffer, &ff_up_compat, 7588c2ecf20Sopenharmony_ci sizeof(struct uinput_ff_upload_compat))) 7598c2ecf20Sopenharmony_ci return -EFAULT; 7608c2ecf20Sopenharmony_ci } else { 7618c2ecf20Sopenharmony_ci if (copy_to_user(buffer, ff_up, 7628c2ecf20Sopenharmony_ci sizeof(struct uinput_ff_upload))) 7638c2ecf20Sopenharmony_ci return -EFAULT; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci return 0; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic int uinput_ff_upload_from_user(const char __user *buffer, 7708c2ecf20Sopenharmony_ci struct uinput_ff_upload *ff_up) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci if (in_compat_syscall()) { 7738c2ecf20Sopenharmony_ci struct uinput_ff_upload_compat ff_up_compat; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (copy_from_user(&ff_up_compat, buffer, 7768c2ecf20Sopenharmony_ci sizeof(struct uinput_ff_upload_compat))) 7778c2ecf20Sopenharmony_ci return -EFAULT; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci ff_up->request_id = ff_up_compat.request_id; 7808c2ecf20Sopenharmony_ci ff_up->retval = ff_up_compat.retval; 7818c2ecf20Sopenharmony_ci memcpy(&ff_up->effect, &ff_up_compat.effect, 7828c2ecf20Sopenharmony_ci sizeof(struct ff_effect_compat)); 7838c2ecf20Sopenharmony_ci memcpy(&ff_up->old, &ff_up_compat.old, 7848c2ecf20Sopenharmony_ci sizeof(struct ff_effect_compat)); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci } else { 7878c2ecf20Sopenharmony_ci if (copy_from_user(ff_up, buffer, 7888c2ecf20Sopenharmony_ci sizeof(struct uinput_ff_upload))) 7898c2ecf20Sopenharmony_ci return -EFAULT; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci return 0; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci#else 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic int uinput_ff_upload_to_user(char __user *buffer, 7988c2ecf20Sopenharmony_ci const struct uinput_ff_upload *ff_up) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload))) 8018c2ecf20Sopenharmony_ci return -EFAULT; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci return 0; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic int uinput_ff_upload_from_user(const char __user *buffer, 8078c2ecf20Sopenharmony_ci struct uinput_ff_upload *ff_up) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload))) 8108c2ecf20Sopenharmony_ci return -EFAULT; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci return 0; 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci#endif 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci#define uinput_set_bit(_arg, _bit, _max) \ 8188c2ecf20Sopenharmony_ci({ \ 8198c2ecf20Sopenharmony_ci int __ret = 0; \ 8208c2ecf20Sopenharmony_ci if (udev->state == UIST_CREATED) \ 8218c2ecf20Sopenharmony_ci __ret = -EINVAL; \ 8228c2ecf20Sopenharmony_ci else if ((_arg) > (_max)) \ 8238c2ecf20Sopenharmony_ci __ret = -EINVAL; \ 8248c2ecf20Sopenharmony_ci else set_bit((_arg), udev->dev->_bit); \ 8258c2ecf20Sopenharmony_ci __ret; \ 8268c2ecf20Sopenharmony_ci}) 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic int uinput_str_to_user(void __user *dest, const char *str, 8298c2ecf20Sopenharmony_ci unsigned int maxlen) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci char __user *p = dest; 8328c2ecf20Sopenharmony_ci int len, ret; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (!str) 8358c2ecf20Sopenharmony_ci return -ENOENT; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (maxlen == 0) 8388c2ecf20Sopenharmony_ci return -EINVAL; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci len = strlen(str) + 1; 8418c2ecf20Sopenharmony_ci if (len > maxlen) 8428c2ecf20Sopenharmony_ci len = maxlen; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci ret = copy_to_user(p, str, len); 8458c2ecf20Sopenharmony_ci if (ret) 8468c2ecf20Sopenharmony_ci return -EFAULT; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* force terminating '\0' */ 8498c2ecf20Sopenharmony_ci ret = put_user(0, p + len - 1); 8508c2ecf20Sopenharmony_ci return ret ? -EFAULT : len; 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic long uinput_ioctl_handler(struct file *file, unsigned int cmd, 8548c2ecf20Sopenharmony_ci unsigned long arg, void __user *p) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci int retval; 8578c2ecf20Sopenharmony_ci struct uinput_device *udev = file->private_data; 8588c2ecf20Sopenharmony_ci struct uinput_ff_upload ff_up; 8598c2ecf20Sopenharmony_ci struct uinput_ff_erase ff_erase; 8608c2ecf20Sopenharmony_ci struct uinput_request *req; 8618c2ecf20Sopenharmony_ci char *phys; 8628c2ecf20Sopenharmony_ci const char *name; 8638c2ecf20Sopenharmony_ci unsigned int size; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci retval = mutex_lock_interruptible(&udev->mutex); 8668c2ecf20Sopenharmony_ci if (retval) 8678c2ecf20Sopenharmony_ci return retval; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (!udev->dev) { 8708c2ecf20Sopenharmony_ci udev->dev = input_allocate_device(); 8718c2ecf20Sopenharmony_ci if (!udev->dev) { 8728c2ecf20Sopenharmony_ci retval = -ENOMEM; 8738c2ecf20Sopenharmony_ci goto out; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci switch (cmd) { 8788c2ecf20Sopenharmony_ci case UI_GET_VERSION: 8798c2ecf20Sopenharmony_ci if (put_user(UINPUT_VERSION, (unsigned int __user *)p)) 8808c2ecf20Sopenharmony_ci retval = -EFAULT; 8818c2ecf20Sopenharmony_ci goto out; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci case UI_DEV_CREATE: 8848c2ecf20Sopenharmony_ci retval = uinput_create_device(udev); 8858c2ecf20Sopenharmony_ci goto out; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci case UI_DEV_DESTROY: 8888c2ecf20Sopenharmony_ci uinput_destroy_device(udev); 8898c2ecf20Sopenharmony_ci goto out; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci case UI_DEV_SETUP: 8928c2ecf20Sopenharmony_ci retval = uinput_dev_setup(udev, p); 8938c2ecf20Sopenharmony_ci goto out; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* UI_ABS_SETUP is handled in the variable size ioctls */ 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci case UI_SET_EVBIT: 8988c2ecf20Sopenharmony_ci retval = uinput_set_bit(arg, evbit, EV_MAX); 8998c2ecf20Sopenharmony_ci goto out; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci case UI_SET_KEYBIT: 9028c2ecf20Sopenharmony_ci retval = uinput_set_bit(arg, keybit, KEY_MAX); 9038c2ecf20Sopenharmony_ci goto out; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci case UI_SET_RELBIT: 9068c2ecf20Sopenharmony_ci retval = uinput_set_bit(arg, relbit, REL_MAX); 9078c2ecf20Sopenharmony_ci goto out; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci case UI_SET_ABSBIT: 9108c2ecf20Sopenharmony_ci retval = uinput_set_bit(arg, absbit, ABS_MAX); 9118c2ecf20Sopenharmony_ci goto out; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci case UI_SET_MSCBIT: 9148c2ecf20Sopenharmony_ci retval = uinput_set_bit(arg, mscbit, MSC_MAX); 9158c2ecf20Sopenharmony_ci goto out; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci case UI_SET_LEDBIT: 9188c2ecf20Sopenharmony_ci retval = uinput_set_bit(arg, ledbit, LED_MAX); 9198c2ecf20Sopenharmony_ci goto out; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci case UI_SET_SNDBIT: 9228c2ecf20Sopenharmony_ci retval = uinput_set_bit(arg, sndbit, SND_MAX); 9238c2ecf20Sopenharmony_ci goto out; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci case UI_SET_FFBIT: 9268c2ecf20Sopenharmony_ci retval = uinput_set_bit(arg, ffbit, FF_MAX); 9278c2ecf20Sopenharmony_ci goto out; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci case UI_SET_SWBIT: 9308c2ecf20Sopenharmony_ci retval = uinput_set_bit(arg, swbit, SW_MAX); 9318c2ecf20Sopenharmony_ci goto out; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci case UI_SET_PROPBIT: 9348c2ecf20Sopenharmony_ci retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX); 9358c2ecf20Sopenharmony_ci goto out; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci case UI_SET_PHYS: 9388c2ecf20Sopenharmony_ci if (udev->state == UIST_CREATED) { 9398c2ecf20Sopenharmony_ci retval = -EINVAL; 9408c2ecf20Sopenharmony_ci goto out; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci phys = strndup_user(p, 1024); 9448c2ecf20Sopenharmony_ci if (IS_ERR(phys)) { 9458c2ecf20Sopenharmony_ci retval = PTR_ERR(phys); 9468c2ecf20Sopenharmony_ci goto out; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci kfree(udev->dev->phys); 9508c2ecf20Sopenharmony_ci udev->dev->phys = phys; 9518c2ecf20Sopenharmony_ci goto out; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci case UI_BEGIN_FF_UPLOAD: 9548c2ecf20Sopenharmony_ci retval = uinput_ff_upload_from_user(p, &ff_up); 9558c2ecf20Sopenharmony_ci if (retval) 9568c2ecf20Sopenharmony_ci goto out; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci req = uinput_request_find(udev, ff_up.request_id); 9598c2ecf20Sopenharmony_ci if (!req || req->code != UI_FF_UPLOAD || 9608c2ecf20Sopenharmony_ci !req->u.upload.effect) { 9618c2ecf20Sopenharmony_ci retval = -EINVAL; 9628c2ecf20Sopenharmony_ci goto out; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci ff_up.retval = 0; 9668c2ecf20Sopenharmony_ci ff_up.effect = *req->u.upload.effect; 9678c2ecf20Sopenharmony_ci if (req->u.upload.old) 9688c2ecf20Sopenharmony_ci ff_up.old = *req->u.upload.old; 9698c2ecf20Sopenharmony_ci else 9708c2ecf20Sopenharmony_ci memset(&ff_up.old, 0, sizeof(struct ff_effect)); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci retval = uinput_ff_upload_to_user(p, &ff_up); 9738c2ecf20Sopenharmony_ci goto out; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci case UI_BEGIN_FF_ERASE: 9768c2ecf20Sopenharmony_ci if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { 9778c2ecf20Sopenharmony_ci retval = -EFAULT; 9788c2ecf20Sopenharmony_ci goto out; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci req = uinput_request_find(udev, ff_erase.request_id); 9828c2ecf20Sopenharmony_ci if (!req || req->code != UI_FF_ERASE) { 9838c2ecf20Sopenharmony_ci retval = -EINVAL; 9848c2ecf20Sopenharmony_ci goto out; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci ff_erase.retval = 0; 9888c2ecf20Sopenharmony_ci ff_erase.effect_id = req->u.effect_id; 9898c2ecf20Sopenharmony_ci if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { 9908c2ecf20Sopenharmony_ci retval = -EFAULT; 9918c2ecf20Sopenharmony_ci goto out; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci goto out; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci case UI_END_FF_UPLOAD: 9978c2ecf20Sopenharmony_ci retval = uinput_ff_upload_from_user(p, &ff_up); 9988c2ecf20Sopenharmony_ci if (retval) 9998c2ecf20Sopenharmony_ci goto out; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci req = uinput_request_find(udev, ff_up.request_id); 10028c2ecf20Sopenharmony_ci if (!req || req->code != UI_FF_UPLOAD || 10038c2ecf20Sopenharmony_ci !req->u.upload.effect) { 10048c2ecf20Sopenharmony_ci retval = -EINVAL; 10058c2ecf20Sopenharmony_ci goto out; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci req->retval = ff_up.retval; 10098c2ecf20Sopenharmony_ci complete(&req->done); 10108c2ecf20Sopenharmony_ci goto out; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci case UI_END_FF_ERASE: 10138c2ecf20Sopenharmony_ci if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { 10148c2ecf20Sopenharmony_ci retval = -EFAULT; 10158c2ecf20Sopenharmony_ci goto out; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci req = uinput_request_find(udev, ff_erase.request_id); 10198c2ecf20Sopenharmony_ci if (!req || req->code != UI_FF_ERASE) { 10208c2ecf20Sopenharmony_ci retval = -EINVAL; 10218c2ecf20Sopenharmony_ci goto out; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci req->retval = ff_erase.retval; 10258c2ecf20Sopenharmony_ci complete(&req->done); 10268c2ecf20Sopenharmony_ci goto out; 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci size = _IOC_SIZE(cmd); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* Now check variable-length commands */ 10328c2ecf20Sopenharmony_ci switch (cmd & ~IOCSIZE_MASK) { 10338c2ecf20Sopenharmony_ci case UI_GET_SYSNAME(0): 10348c2ecf20Sopenharmony_ci if (udev->state != UIST_CREATED) { 10358c2ecf20Sopenharmony_ci retval = -ENOENT; 10368c2ecf20Sopenharmony_ci goto out; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci name = dev_name(&udev->dev->dev); 10398c2ecf20Sopenharmony_ci retval = uinput_str_to_user(p, name, size); 10408c2ecf20Sopenharmony_ci goto out; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci case UI_ABS_SETUP & ~IOCSIZE_MASK: 10438c2ecf20Sopenharmony_ci retval = uinput_abs_setup(udev, p, size); 10448c2ecf20Sopenharmony_ci goto out; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci retval = -EINVAL; 10488c2ecf20Sopenharmony_ci out: 10498c2ecf20Sopenharmony_ci mutex_unlock(&udev->mutex); 10508c2ecf20Sopenharmony_ci return retval; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_cistatic long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg); 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci/* 10618c2ecf20Sopenharmony_ci * These IOCTLs change their size and thus their numbers between 10628c2ecf20Sopenharmony_ci * 32 and 64 bits. 10638c2ecf20Sopenharmony_ci */ 10648c2ecf20Sopenharmony_ci#define UI_SET_PHYS_COMPAT \ 10658c2ecf20Sopenharmony_ci _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t) 10668c2ecf20Sopenharmony_ci#define UI_BEGIN_FF_UPLOAD_COMPAT \ 10678c2ecf20Sopenharmony_ci _IOWR(UINPUT_IOCTL_BASE, 200, struct uinput_ff_upload_compat) 10688c2ecf20Sopenharmony_ci#define UI_END_FF_UPLOAD_COMPAT \ 10698c2ecf20Sopenharmony_ci _IOW(UINPUT_IOCTL_BASE, 201, struct uinput_ff_upload_compat) 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_cistatic long uinput_compat_ioctl(struct file *file, 10728c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci switch (cmd) { 10758c2ecf20Sopenharmony_ci case UI_SET_PHYS_COMPAT: 10768c2ecf20Sopenharmony_ci cmd = UI_SET_PHYS; 10778c2ecf20Sopenharmony_ci break; 10788c2ecf20Sopenharmony_ci case UI_BEGIN_FF_UPLOAD_COMPAT: 10798c2ecf20Sopenharmony_ci cmd = UI_BEGIN_FF_UPLOAD; 10808c2ecf20Sopenharmony_ci break; 10818c2ecf20Sopenharmony_ci case UI_END_FF_UPLOAD_COMPAT: 10828c2ecf20Sopenharmony_ci cmd = UI_END_FF_UPLOAD; 10838c2ecf20Sopenharmony_ci break; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg)); 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci#endif 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_cistatic const struct file_operations uinput_fops = { 10918c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10928c2ecf20Sopenharmony_ci .open = uinput_open, 10938c2ecf20Sopenharmony_ci .release = uinput_release, 10948c2ecf20Sopenharmony_ci .read = uinput_read, 10958c2ecf20Sopenharmony_ci .write = uinput_write, 10968c2ecf20Sopenharmony_ci .poll = uinput_poll, 10978c2ecf20Sopenharmony_ci .unlocked_ioctl = uinput_ioctl, 10988c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 10998c2ecf20Sopenharmony_ci .compat_ioctl = uinput_compat_ioctl, 11008c2ecf20Sopenharmony_ci#endif 11018c2ecf20Sopenharmony_ci .llseek = no_llseek, 11028c2ecf20Sopenharmony_ci}; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic struct miscdevice uinput_misc = { 11058c2ecf20Sopenharmony_ci .fops = &uinput_fops, 11068c2ecf20Sopenharmony_ci .minor = UINPUT_MINOR, 11078c2ecf20Sopenharmony_ci .name = UINPUT_NAME, 11088c2ecf20Sopenharmony_ci}; 11098c2ecf20Sopenharmony_cimodule_misc_device(uinput_misc); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ciMODULE_ALIAS_MISCDEV(UINPUT_MINOR); 11128c2ecf20Sopenharmony_ciMODULE_ALIAS("devname:" UINPUT_NAME); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ciMODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); 11158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("User level driver support for input subsystem"); 11168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1117