11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * User level driver support for input subsystem 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Heavily based on evdev.c by Vojtech Pavlik 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 91da177e4SLinus Torvalds * (at your option) any later version. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 141da177e4SLinus Torvalds * GNU General Public License for more details. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 171da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 181da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> 211da177e4SLinus Torvalds * 221da177e4SLinus Torvalds * Changes/Revisions: 23e3480a61SBenjamin Tissoires * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>) 24e3480a61SBenjamin Tissoires * - add UI_GET_SYSNAME ioctl 25ff462551SAnssi Hannula * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>) 26ff462551SAnssi Hannula * - updated ff support for the changes in kernel interface 27ff462551SAnssi Hannula * - added MODULE_VERSION 281da177e4SLinus Torvalds * 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>) 291da177e4SLinus Torvalds * - added force feedback support 301da177e4SLinus Torvalds * - added UI_SET_PHYS 311da177e4SLinus Torvalds * 0.1 20/06/2002 321da177e4SLinus Torvalds * - first public version 331da177e4SLinus Torvalds */ 341da177e4SLinus Torvalds #include <linux/poll.h> 35a99bbaf5SAlexey Dobriyan #include <linux/sched.h> 361da177e4SLinus Torvalds #include <linux/slab.h> 371da177e4SLinus Torvalds #include <linux/module.h> 381da177e4SLinus Torvalds #include <linux/init.h> 391da177e4SLinus Torvalds #include <linux/fs.h> 401da177e4SLinus Torvalds #include <linux/miscdevice.h> 411da177e4SLinus Torvalds #include <linux/uinput.h> 4247c78e89SHenrik Rydberg #include <linux/input/mt.h> 432d56f3a3SPhilip Langdale #include "../input-compat.h" 441da177e4SLinus Torvalds 4554ce165eSDmitry Torokhov static int uinput_dev_event(struct input_dev *dev, 4654ce165eSDmitry Torokhov unsigned int type, unsigned int code, int value) 471da177e4SLinus Torvalds { 48373f9713SDmitry Torokhov struct uinput_device *udev = input_get_drvdata(dev); 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds udev->buff[udev->head].type = type; 511da177e4SLinus Torvalds udev->buff[udev->head].code = code; 521da177e4SLinus Torvalds udev->buff[udev->head].value = value; 531da177e4SLinus Torvalds do_gettimeofday(&udev->buff[udev->head].time); 541da177e4SLinus Torvalds udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE; 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds wake_up_interruptible(&udev->waitq); 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds return 0; 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds 6105cebd38SAristeu Sergio Rozanski Filho /* Atomically allocate an ID for the given request. Returns 0 on success. */ 6200ce756cSDmitry Torokhov static bool uinput_request_alloc_id(struct uinput_device *udev, 6300ce756cSDmitry Torokhov struct uinput_request *request) 641da177e4SLinus Torvalds { 65c5b3533aSDmitry Torokhov unsigned int id; 6600ce756cSDmitry Torokhov bool reserved = false; 671da177e4SLinus Torvalds 680048e603SDmitry Torokhov spin_lock(&udev->requests_lock); 69152c12f5SDmitry Torokhov 7005cebd38SAristeu Sergio Rozanski Filho for (id = 0; id < UINPUT_NUM_REQUESTS; id++) { 711da177e4SLinus Torvalds if (!udev->requests[id]) { 721da177e4SLinus Torvalds request->id = id; 730048e603SDmitry Torokhov udev->requests[id] = request; 7400ce756cSDmitry Torokhov reserved = true; 75152c12f5SDmitry Torokhov break; 761da177e4SLinus Torvalds } 7705cebd38SAristeu Sergio Rozanski Filho } 78152c12f5SDmitry Torokhov 790048e603SDmitry Torokhov spin_unlock(&udev->requests_lock); 8000ce756cSDmitry Torokhov return reserved; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 83c5b3533aSDmitry Torokhov static struct uinput_request *uinput_request_find(struct uinput_device *udev, 84c5b3533aSDmitry Torokhov unsigned int id) 851da177e4SLinus Torvalds { 861da177e4SLinus Torvalds /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ 87c5b3533aSDmitry Torokhov if (id >= UINPUT_NUM_REQUESTS) 881da177e4SLinus Torvalds return NULL; 892d56f3a3SPhilip Langdale 901da177e4SLinus Torvalds return udev->requests[id]; 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 9300ce756cSDmitry Torokhov static int uinput_request_reserve_slot(struct uinput_device *udev, 9400ce756cSDmitry Torokhov struct uinput_request *request) 951da177e4SLinus Torvalds { 960048e603SDmitry Torokhov /* Allocate slot. If none are available right away, wait. */ 970048e603SDmitry Torokhov return wait_event_interruptible(udev->requests_waitq, 9800ce756cSDmitry Torokhov uinput_request_alloc_id(udev, request)); 991da177e4SLinus Torvalds } 1001da177e4SLinus Torvalds 1016b4877c7SDmitry Torokhov static void uinput_request_release_slot(struct uinput_device *udev, 1026b4877c7SDmitry Torokhov unsigned int id) 1031da177e4SLinus Torvalds { 1040048e603SDmitry Torokhov /* Mark slot as available */ 1056b4877c7SDmitry Torokhov spin_lock(&udev->requests_lock); 1066b4877c7SDmitry Torokhov udev->requests[id] = NULL; 1076b4877c7SDmitry Torokhov spin_unlock(&udev->requests_lock); 108e7507ed9SDmitry Torokhov 1096b4877c7SDmitry Torokhov wake_up(&udev->requests_waitq); 1100048e603SDmitry Torokhov } 1110048e603SDmitry Torokhov 11200ce756cSDmitry Torokhov static int uinput_request_send(struct uinput_device *udev, 11300ce756cSDmitry Torokhov struct uinput_request *request) 1140048e603SDmitry Torokhov { 11505cebd38SAristeu Sergio Rozanski Filho int retval; 1161da177e4SLinus Torvalds 11705cebd38SAristeu Sergio Rozanski Filho retval = mutex_lock_interruptible(&udev->mutex); 11805cebd38SAristeu Sergio Rozanski Filho if (retval) 11905cebd38SAristeu Sergio Rozanski Filho return retval; 12005cebd38SAristeu Sergio Rozanski Filho 12105cebd38SAristeu Sergio Rozanski Filho if (udev->state != UIST_CREATED) { 12205cebd38SAristeu Sergio Rozanski Filho retval = -ENODEV; 12305cebd38SAristeu Sergio Rozanski Filho goto out; 12405cebd38SAristeu Sergio Rozanski Filho } 12505cebd38SAristeu Sergio Rozanski Filho 12600ce756cSDmitry Torokhov init_completion(&request->done); 12700ce756cSDmitry Torokhov 12800ce756cSDmitry Torokhov /* 12900ce756cSDmitry Torokhov * Tell our userspace application about this new request 13000ce756cSDmitry Torokhov * by queueing an input event. 13100ce756cSDmitry Torokhov */ 13205cebd38SAristeu Sergio Rozanski Filho uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id); 13305cebd38SAristeu Sergio Rozanski Filho 13405cebd38SAristeu Sergio Rozanski Filho out: 13505cebd38SAristeu Sergio Rozanski Filho mutex_unlock(&udev->mutex); 13605cebd38SAristeu Sergio Rozanski Filho return retval; 13705cebd38SAristeu Sergio Rozanski Filho } 13805cebd38SAristeu Sergio Rozanski Filho 13900ce756cSDmitry Torokhov static int uinput_request_submit(struct uinput_device *udev, 14000ce756cSDmitry Torokhov struct uinput_request *request) 14100ce756cSDmitry Torokhov { 1426b4877c7SDmitry Torokhov int retval; 14300ce756cSDmitry Torokhov 1446b4877c7SDmitry Torokhov retval = uinput_request_reserve_slot(udev, request); 1456b4877c7SDmitry Torokhov if (retval) 1466b4877c7SDmitry Torokhov return retval; 14700ce756cSDmitry Torokhov 1486b4877c7SDmitry Torokhov retval = uinput_request_send(udev, request); 1496b4877c7SDmitry Torokhov if (retval) 1506b4877c7SDmitry Torokhov goto out; 15100ce756cSDmitry Torokhov 1528e009118SDmitry Torokhov if (!wait_for_completion_timeout(&request->done, 30 * HZ)) { 1538e009118SDmitry Torokhov retval = -ETIMEDOUT; 1548e009118SDmitry Torokhov goto out; 1558e009118SDmitry Torokhov } 1568e009118SDmitry Torokhov 1576b4877c7SDmitry Torokhov retval = request->retval; 1586b4877c7SDmitry Torokhov 1596b4877c7SDmitry Torokhov out: 1606b4877c7SDmitry Torokhov uinput_request_release_slot(udev, request->id); 1616b4877c7SDmitry Torokhov return retval; 16200ce756cSDmitry Torokhov } 16300ce756cSDmitry Torokhov 16405cebd38SAristeu Sergio Rozanski Filho /* 16554ce165eSDmitry Torokhov * Fail all outstanding requests so handlers don't wait for the userspace 16605cebd38SAristeu Sergio Rozanski Filho * to finish processing them. 16705cebd38SAristeu Sergio Rozanski Filho */ 16805cebd38SAristeu Sergio Rozanski Filho static void uinput_flush_requests(struct uinput_device *udev) 16905cebd38SAristeu Sergio Rozanski Filho { 17005cebd38SAristeu Sergio Rozanski Filho struct uinput_request *request; 17105cebd38SAristeu Sergio Rozanski Filho int i; 17205cebd38SAristeu Sergio Rozanski Filho 17305cebd38SAristeu Sergio Rozanski Filho spin_lock(&udev->requests_lock); 17405cebd38SAristeu Sergio Rozanski Filho 17505cebd38SAristeu Sergio Rozanski Filho for (i = 0; i < UINPUT_NUM_REQUESTS; i++) { 17605cebd38SAristeu Sergio Rozanski Filho request = udev->requests[i]; 17705cebd38SAristeu Sergio Rozanski Filho if (request) { 17805cebd38SAristeu Sergio Rozanski Filho request->retval = -ENODEV; 1796b4877c7SDmitry Torokhov complete(&request->done); 18005cebd38SAristeu Sergio Rozanski Filho } 18105cebd38SAristeu Sergio Rozanski Filho } 18205cebd38SAristeu Sergio Rozanski Filho 18305cebd38SAristeu Sergio Rozanski Filho spin_unlock(&udev->requests_lock); 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 186ff462551SAnssi Hannula static void uinput_dev_set_gain(struct input_dev *dev, u16 gain) 187ff462551SAnssi Hannula { 188ff462551SAnssi Hannula uinput_dev_event(dev, EV_FF, FF_GAIN, gain); 189ff462551SAnssi Hannula } 190ff462551SAnssi Hannula 191ff462551SAnssi Hannula static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude) 192ff462551SAnssi Hannula { 193ff462551SAnssi Hannula uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude); 194ff462551SAnssi Hannula } 195ff462551SAnssi Hannula 196ff462551SAnssi Hannula static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value) 197ff462551SAnssi Hannula { 198ff462551SAnssi Hannula return uinput_dev_event(dev, EV_FF, effect_id, value); 199ff462551SAnssi Hannula } 200ff462551SAnssi Hannula 20154ce165eSDmitry Torokhov static int uinput_dev_upload_effect(struct input_dev *dev, 20254ce165eSDmitry Torokhov struct ff_effect *effect, 20354ce165eSDmitry Torokhov struct ff_effect *old) 2041da177e4SLinus Torvalds { 20505cebd38SAristeu Sergio Rozanski Filho struct uinput_device *udev = input_get_drvdata(dev); 2061da177e4SLinus Torvalds struct uinput_request request; 2071da177e4SLinus Torvalds 2082d56f3a3SPhilip Langdale /* 2092d56f3a3SPhilip Langdale * uinput driver does not currently support periodic effects with 2102d56f3a3SPhilip Langdale * custom waveform since it does not have a way to pass buffer of 2112d56f3a3SPhilip Langdale * samples (custom_data) to userspace. If ever there is a device 2122d56f3a3SPhilip Langdale * supporting custom waveforms we would need to define an additional 2132d56f3a3SPhilip Langdale * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out. 2142d56f3a3SPhilip Langdale */ 2152d56f3a3SPhilip Langdale if (effect->type == FF_PERIODIC && 2162d56f3a3SPhilip Langdale effect->u.periodic.waveform == FF_CUSTOM) 2172d56f3a3SPhilip Langdale return -EINVAL; 2182d56f3a3SPhilip Langdale 2190048e603SDmitry Torokhov request.code = UI_FF_UPLOAD; 220ff462551SAnssi Hannula request.u.upload.effect = effect; 221ff462551SAnssi Hannula request.u.upload.old = old; 2220048e603SDmitry Torokhov 22300ce756cSDmitry Torokhov return uinput_request_submit(udev, &request); 2241da177e4SLinus Torvalds } 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) 2271da177e4SLinus Torvalds { 22805cebd38SAristeu Sergio Rozanski Filho struct uinput_device *udev = input_get_drvdata(dev); 2291da177e4SLinus Torvalds struct uinput_request request; 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds if (!test_bit(EV_FF, dev->evbit)) 2321da177e4SLinus Torvalds return -ENOSYS; 2331da177e4SLinus Torvalds 2340048e603SDmitry Torokhov request.code = UI_FF_ERASE; 2351da177e4SLinus Torvalds request.u.effect_id = effect_id; 2360048e603SDmitry Torokhov 23700ce756cSDmitry Torokhov return uinput_request_submit(udev, &request); 2381da177e4SLinus Torvalds } 2391da177e4SLinus Torvalds 240e8b95728SDmitry Torokhov static int uinput_dev_flush(struct input_dev *dev, struct file *file) 241e8b95728SDmitry Torokhov { 242e8b95728SDmitry Torokhov /* 243e8b95728SDmitry Torokhov * If we are called with file == NULL that means we are tearing 244e8b95728SDmitry Torokhov * down the device, and therefore we can not handle FF erase 245e8b95728SDmitry Torokhov * requests: either we are handling UI_DEV_DESTROY (and holding 246e8b95728SDmitry Torokhov * the udev->mutex), or the file descriptor is closed and there is 247e8b95728SDmitry Torokhov * nobody on the other side anymore. 248e8b95728SDmitry Torokhov */ 249e8b95728SDmitry Torokhov return file ? input_ff_flush(dev, file) : 0; 250e8b95728SDmitry Torokhov } 251e8b95728SDmitry Torokhov 25229506415SDmitry Torokhov static void uinput_destroy_device(struct uinput_device *udev) 25329506415SDmitry Torokhov { 25429506415SDmitry Torokhov const char *name, *phys; 25505cebd38SAristeu Sergio Rozanski Filho struct input_dev *dev = udev->dev; 25605cebd38SAristeu Sergio Rozanski Filho enum uinput_state old_state = udev->state; 25729506415SDmitry Torokhov 25805cebd38SAristeu Sergio Rozanski Filho udev->state = UIST_NEW_DEVICE; 25905cebd38SAristeu Sergio Rozanski Filho 26005cebd38SAristeu Sergio Rozanski Filho if (dev) { 26105cebd38SAristeu Sergio Rozanski Filho name = dev->name; 26205cebd38SAristeu Sergio Rozanski Filho phys = dev->phys; 26305cebd38SAristeu Sergio Rozanski Filho if (old_state == UIST_CREATED) { 26405cebd38SAristeu Sergio Rozanski Filho uinput_flush_requests(udev); 26505cebd38SAristeu Sergio Rozanski Filho input_unregister_device(dev); 26605cebd38SAristeu Sergio Rozanski Filho } else { 26705cebd38SAristeu Sergio Rozanski Filho input_free_device(dev); 26805cebd38SAristeu Sergio Rozanski Filho } 26929506415SDmitry Torokhov kfree(name); 27029506415SDmitry Torokhov kfree(phys); 27129506415SDmitry Torokhov udev->dev = NULL; 27229506415SDmitry Torokhov } 27329506415SDmitry Torokhov } 27429506415SDmitry Torokhov 2751da177e4SLinus Torvalds static int uinput_create_device(struct uinput_device *udev) 2761da177e4SLinus Torvalds { 277ff462551SAnssi Hannula struct input_dev *dev = udev->dev; 278fbae10dbSDavid Herrmann int error, nslot; 27929506415SDmitry Torokhov 28029506415SDmitry Torokhov if (udev->state != UIST_SETUP_COMPLETE) { 2811da177e4SLinus Torvalds printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); 2821da177e4SLinus Torvalds return -EINVAL; 2831da177e4SLinus Torvalds } 2841da177e4SLinus Torvalds 285601bbbe0SDmitry Torokhov if (test_bit(EV_ABS, dev->evbit)) { 286601bbbe0SDmitry Torokhov input_alloc_absinfo(dev); 287601bbbe0SDmitry Torokhov if (!dev->absinfo) { 288601bbbe0SDmitry Torokhov error = -EINVAL; 289601bbbe0SDmitry Torokhov goto fail1; 290601bbbe0SDmitry Torokhov } 291601bbbe0SDmitry Torokhov 292fbae10dbSDavid Herrmann if (test_bit(ABS_MT_SLOT, dev->absbit)) { 293fbae10dbSDavid Herrmann nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; 294fbae10dbSDavid Herrmann error = input_mt_init_slots(dev, nslot, 0); 295fbae10dbSDavid Herrmann if (error) 296fbae10dbSDavid Herrmann goto fail1; 297fbae10dbSDavid Herrmann } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { 298fbae10dbSDavid Herrmann input_set_events_per_packet(dev, 60); 299fbae10dbSDavid Herrmann } 300601bbbe0SDmitry Torokhov } 301fbae10dbSDavid Herrmann 302daf6cd0cSElias Vanderstuyft if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) { 303daf6cd0cSElias Vanderstuyft printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n", 304daf6cd0cSElias Vanderstuyft UINPUT_NAME); 305daf6cd0cSElias Vanderstuyft error = -EINVAL; 306daf6cd0cSElias Vanderstuyft goto fail1; 307daf6cd0cSElias Vanderstuyft } 308daf6cd0cSElias Vanderstuyft 309ff462551SAnssi Hannula if (udev->ff_effects_max) { 310ff462551SAnssi Hannula error = input_ff_create(dev, udev->ff_effects_max); 311ff462551SAnssi Hannula if (error) 312ff462551SAnssi Hannula goto fail1; 313ff462551SAnssi Hannula 314ff462551SAnssi Hannula dev->ff->upload = uinput_dev_upload_effect; 315ff462551SAnssi Hannula dev->ff->erase = uinput_dev_erase_effect; 316ff462551SAnssi Hannula dev->ff->playback = uinput_dev_playback; 317ff462551SAnssi Hannula dev->ff->set_gain = uinput_dev_set_gain; 318ff462551SAnssi Hannula dev->ff->set_autocenter = uinput_dev_set_autocenter; 319e8b95728SDmitry Torokhov /* 320e8b95728SDmitry Torokhov * The standard input_ff_flush() implementation does 321e8b95728SDmitry Torokhov * not quite work for uinput as we can't reasonably 322e8b95728SDmitry Torokhov * handle FF requests during device teardown. 323e8b95728SDmitry Torokhov */ 324e8b95728SDmitry Torokhov dev->flush = uinput_dev_flush; 3251da177e4SLinus Torvalds } 3261da177e4SLinus Torvalds 327ff462551SAnssi Hannula error = input_register_device(udev->dev); 328ff462551SAnssi Hannula if (error) 329ff462551SAnssi Hannula goto fail2; 330ff462551SAnssi Hannula 33129506415SDmitry Torokhov udev->state = UIST_CREATED; 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds return 0; 334ff462551SAnssi Hannula 335ff462551SAnssi Hannula fail2: input_ff_destroy(dev); 336ff462551SAnssi Hannula fail1: uinput_destroy_device(udev); 337ff462551SAnssi Hannula return error; 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds static int uinput_open(struct inode *inode, struct file *file) 3411da177e4SLinus Torvalds { 3421da177e4SLinus Torvalds struct uinput_device *newdev; 3431da177e4SLinus Torvalds 34429506415SDmitry Torokhov newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL); 3451da177e4SLinus Torvalds if (!newdev) 34629506415SDmitry Torokhov return -ENOMEM; 34729506415SDmitry Torokhov 348221979aaSDmitry Torokhov mutex_init(&newdev->mutex); 3490048e603SDmitry Torokhov spin_lock_init(&newdev->requests_lock); 3501da177e4SLinus Torvalds init_waitqueue_head(&newdev->requests_waitq); 35129506415SDmitry Torokhov init_waitqueue_head(&newdev->waitq); 35229506415SDmitry Torokhov newdev->state = UIST_NEW_DEVICE; 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds file->private_data = newdev; 355daf8a96bSDmitry Torokhov nonseekable_open(inode, file); 3561da177e4SLinus Torvalds 3571da177e4SLinus Torvalds return 0; 3581da177e4SLinus Torvalds } 3591da177e4SLinus Torvalds 360fbae10dbSDavid Herrmann static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code, 361fbae10dbSDavid Herrmann const struct input_absinfo *abs) 362fbae10dbSDavid Herrmann { 363fbae10dbSDavid Herrmann int min, max; 364fbae10dbSDavid Herrmann 365fbae10dbSDavid Herrmann min = abs->minimum; 366fbae10dbSDavid Herrmann max = abs->maximum; 367fbae10dbSDavid Herrmann 368fbae10dbSDavid Herrmann if ((min != 0 || max != 0) && max <= min) { 369fbae10dbSDavid Herrmann printk(KERN_DEBUG 370fbae10dbSDavid Herrmann "%s: invalid abs[%02x] min:%d max:%d\n", 371fbae10dbSDavid Herrmann UINPUT_NAME, code, min, max); 372fbae10dbSDavid Herrmann return -EINVAL; 373fbae10dbSDavid Herrmann } 374fbae10dbSDavid Herrmann 375fbae10dbSDavid Herrmann if (abs->flat > max - min) { 376fbae10dbSDavid Herrmann printk(KERN_DEBUG 377fbae10dbSDavid Herrmann "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n", 378fbae10dbSDavid Herrmann UINPUT_NAME, code, abs->flat, min, max); 379fbae10dbSDavid Herrmann return -EINVAL; 380fbae10dbSDavid Herrmann } 381fbae10dbSDavid Herrmann 382fbae10dbSDavid Herrmann return 0; 383fbae10dbSDavid Herrmann } 384fbae10dbSDavid Herrmann 3851da177e4SLinus Torvalds static int uinput_validate_absbits(struct input_dev *dev) 3861da177e4SLinus Torvalds { 3871da177e4SLinus Torvalds unsigned int cnt; 388fbae10dbSDavid Herrmann int error; 389bcb898e5SDavid Herrmann 390bcb898e5SDavid Herrmann if (!test_bit(EV_ABS, dev->evbit)) 391bcb898e5SDavid Herrmann return 0; 392bcb898e5SDavid Herrmann 393bcb898e5SDavid Herrmann /* 394bcb898e5SDavid Herrmann * Check if absmin/absmax/absfuzz/absflat are sane. 395bcb898e5SDavid Herrmann */ 3961da177e4SLinus Torvalds 397b6d30968SAnshul Garg for_each_set_bit(cnt, dev->absbit, ABS_CNT) { 398fbae10dbSDavid Herrmann if (!dev->absinfo) 399bcb898e5SDavid Herrmann return -EINVAL; 4001da177e4SLinus Torvalds 401fbae10dbSDavid Herrmann error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]); 402fbae10dbSDavid Herrmann if (error) 403fbae10dbSDavid Herrmann return error; 404bcb898e5SDavid Herrmann } 405bcb898e5SDavid Herrmann 406bcb898e5SDavid Herrmann return 0; 4071da177e4SLinus Torvalds } 4081da177e4SLinus Torvalds 40929506415SDmitry Torokhov static int uinput_allocate_device(struct uinput_device *udev) 41029506415SDmitry Torokhov { 41129506415SDmitry Torokhov udev->dev = input_allocate_device(); 41229506415SDmitry Torokhov if (!udev->dev) 41329506415SDmitry Torokhov return -ENOMEM; 41429506415SDmitry Torokhov 41529506415SDmitry Torokhov udev->dev->event = uinput_dev_event; 416373f9713SDmitry Torokhov input_set_drvdata(udev->dev, udev); 41729506415SDmitry Torokhov 41829506415SDmitry Torokhov return 0; 41929506415SDmitry Torokhov } 42029506415SDmitry Torokhov 421052876f8SBenjamin Tissoires static int uinput_dev_setup(struct uinput_device *udev, 422052876f8SBenjamin Tissoires struct uinput_setup __user *arg) 423052876f8SBenjamin Tissoires { 424052876f8SBenjamin Tissoires struct uinput_setup setup; 425052876f8SBenjamin Tissoires struct input_dev *dev; 426052876f8SBenjamin Tissoires 427052876f8SBenjamin Tissoires if (udev->state == UIST_CREATED) 428052876f8SBenjamin Tissoires return -EINVAL; 429052876f8SBenjamin Tissoires 430052876f8SBenjamin Tissoires if (copy_from_user(&setup, arg, sizeof(setup))) 431052876f8SBenjamin Tissoires return -EFAULT; 432052876f8SBenjamin Tissoires 433052876f8SBenjamin Tissoires if (!setup.name[0]) 434052876f8SBenjamin Tissoires return -EINVAL; 435052876f8SBenjamin Tissoires 436052876f8SBenjamin Tissoires dev = udev->dev; 437052876f8SBenjamin Tissoires dev->id = setup.id; 438052876f8SBenjamin Tissoires udev->ff_effects_max = setup.ff_effects_max; 439052876f8SBenjamin Tissoires 440052876f8SBenjamin Tissoires kfree(dev->name); 441052876f8SBenjamin Tissoires dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL); 442052876f8SBenjamin Tissoires if (!dev->name) 443052876f8SBenjamin Tissoires return -ENOMEM; 444052876f8SBenjamin Tissoires 445052876f8SBenjamin Tissoires udev->state = UIST_SETUP_COMPLETE; 446052876f8SBenjamin Tissoires return 0; 447052876f8SBenjamin Tissoires } 448052876f8SBenjamin Tissoires 449052876f8SBenjamin Tissoires static int uinput_abs_setup(struct uinput_device *udev, 450052876f8SBenjamin Tissoires struct uinput_setup __user *arg, size_t size) 451052876f8SBenjamin Tissoires { 452052876f8SBenjamin Tissoires struct uinput_abs_setup setup = {}; 453052876f8SBenjamin Tissoires struct input_dev *dev; 454fbae10dbSDavid Herrmann int error; 455052876f8SBenjamin Tissoires 456052876f8SBenjamin Tissoires if (size > sizeof(setup)) 457052876f8SBenjamin Tissoires return -E2BIG; 458052876f8SBenjamin Tissoires 459052876f8SBenjamin Tissoires if (udev->state == UIST_CREATED) 460052876f8SBenjamin Tissoires return -EINVAL; 461052876f8SBenjamin Tissoires 462052876f8SBenjamin Tissoires if (copy_from_user(&setup, arg, size)) 463052876f8SBenjamin Tissoires return -EFAULT; 464052876f8SBenjamin Tissoires 465052876f8SBenjamin Tissoires if (setup.code > ABS_MAX) 466052876f8SBenjamin Tissoires return -ERANGE; 467052876f8SBenjamin Tissoires 468052876f8SBenjamin Tissoires dev = udev->dev; 469052876f8SBenjamin Tissoires 470fbae10dbSDavid Herrmann error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo); 471fbae10dbSDavid Herrmann if (error) 472fbae10dbSDavid Herrmann return error; 473fbae10dbSDavid Herrmann 474052876f8SBenjamin Tissoires input_alloc_absinfo(dev); 475052876f8SBenjamin Tissoires if (!dev->absinfo) 476052876f8SBenjamin Tissoires return -ENOMEM; 477052876f8SBenjamin Tissoires 478052876f8SBenjamin Tissoires set_bit(setup.code, dev->absbit); 479052876f8SBenjamin Tissoires dev->absinfo[setup.code] = setup.absinfo; 480052876f8SBenjamin Tissoires return 0; 481052876f8SBenjamin Tissoires } 482052876f8SBenjamin Tissoires 483052876f8SBenjamin Tissoires /* legacy setup via write() */ 484052876f8SBenjamin Tissoires static int uinput_setup_device_legacy(struct uinput_device *udev, 48554ce165eSDmitry Torokhov const char __user *buffer, size_t count) 4861da177e4SLinus Torvalds { 4871da177e4SLinus Torvalds struct uinput_user_dev *user_dev; 4881da177e4SLinus Torvalds struct input_dev *dev; 4895d9d6e91SDavid Herrmann int i; 490152c12f5SDmitry Torokhov int retval; 4911da177e4SLinus Torvalds 49229506415SDmitry Torokhov if (count != sizeof(struct uinput_user_dev)) 49329506415SDmitry Torokhov return -EINVAL; 4941da177e4SLinus Torvalds 49529506415SDmitry Torokhov if (!udev->dev) { 49629506415SDmitry Torokhov retval = uinput_allocate_device(udev); 49729506415SDmitry Torokhov if (retval) 49829506415SDmitry Torokhov return retval; 49929506415SDmitry Torokhov } 50029506415SDmitry Torokhov 5011da177e4SLinus Torvalds dev = udev->dev; 5021da177e4SLinus Torvalds 5034dfcc271SDmitry Torokhov user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev)); 504163d2770SDan Carpenter if (IS_ERR(user_dev)) 5054dfcc271SDmitry Torokhov return PTR_ERR(user_dev); 5061da177e4SLinus Torvalds 507ff462551SAnssi Hannula udev->ff_effects_max = user_dev->ff_effects_max; 508ff462551SAnssi Hannula 5095d9d6e91SDavid Herrmann /* Ensure name is filled in */ 5105d9d6e91SDavid Herrmann if (!user_dev->name[0]) { 51129506415SDmitry Torokhov retval = -EINVAL; 51229506415SDmitry Torokhov goto exit; 51329506415SDmitry Torokhov } 51429506415SDmitry Torokhov 51529506415SDmitry Torokhov kfree(dev->name); 5165d9d6e91SDavid Herrmann dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE, 5175d9d6e91SDavid Herrmann GFP_KERNEL); 5185d9d6e91SDavid Herrmann if (!dev->name) { 5191da177e4SLinus Torvalds retval = -ENOMEM; 5201da177e4SLinus Torvalds goto exit; 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds dev->id.bustype = user_dev->id.bustype; 5241da177e4SLinus Torvalds dev->id.vendor = user_dev->id.vendor; 5251da177e4SLinus Torvalds dev->id.product = user_dev->id.product; 5261da177e4SLinus Torvalds dev->id.version = user_dev->id.version; 5271da177e4SLinus Torvalds 52872d47362SDmitry Torokhov for (i = 0; i < ABS_CNT; i++) { 529987a6c02SDaniel Mack input_abs_set_max(dev, i, user_dev->absmax[i]); 530987a6c02SDaniel Mack input_abs_set_min(dev, i, user_dev->absmin[i]); 531987a6c02SDaniel Mack input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]); 532987a6c02SDaniel Mack input_abs_set_flat(dev, i, user_dev->absflat[i]); 533987a6c02SDaniel Mack } 5341da177e4SLinus Torvalds 53529506415SDmitry Torokhov retval = uinput_validate_absbits(dev); 53629506415SDmitry Torokhov if (retval < 0) 53729506415SDmitry Torokhov goto exit; 53829506415SDmitry Torokhov 53929506415SDmitry Torokhov udev->state = UIST_SETUP_COMPLETE; 54029506415SDmitry Torokhov retval = count; 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds exit: 5431da177e4SLinus Torvalds kfree(user_dev); 5441da177e4SLinus Torvalds return retval; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds 547cbf05413SRyan Mallon static ssize_t uinput_inject_events(struct uinput_device *udev, 54854ce165eSDmitry Torokhov const char __user *buffer, size_t count) 5491da177e4SLinus Torvalds { 5501da177e4SLinus Torvalds struct input_event ev; 551cbf05413SRyan Mallon size_t bytes = 0; 5521da177e4SLinus Torvalds 553cbf05413SRyan Mallon if (count != 0 && count < input_event_size()) 55429506415SDmitry Torokhov return -EINVAL; 55529506415SDmitry Torokhov 556cbf05413SRyan Mallon while (bytes + input_event_size() <= count) { 557cbf05413SRyan Mallon /* 558cbf05413SRyan Mallon * Note that even if some events were fetched successfully 559cbf05413SRyan Mallon * we are still going to return EFAULT instead of partial 560cbf05413SRyan Mallon * count to let userspace know that it got it's buffers 561cbf05413SRyan Mallon * all wrong. 562cbf05413SRyan Mallon */ 563cbf05413SRyan Mallon if (input_event_from_user(buffer + bytes, &ev)) 5641da177e4SLinus Torvalds return -EFAULT; 5651da177e4SLinus Torvalds 56629506415SDmitry Torokhov input_event(udev->dev, ev.type, ev.code, ev.value); 567cbf05413SRyan Mallon bytes += input_event_size(); 568cbf05413SRyan Mallon } 56929506415SDmitry Torokhov 570cbf05413SRyan Mallon return bytes; 57129506415SDmitry Torokhov } 57229506415SDmitry Torokhov 57354ce165eSDmitry Torokhov static ssize_t uinput_write(struct file *file, const char __user *buffer, 57454ce165eSDmitry Torokhov size_t count, loff_t *ppos) 57529506415SDmitry Torokhov { 57629506415SDmitry Torokhov struct uinput_device *udev = file->private_data; 57729506415SDmitry Torokhov int retval; 57829506415SDmitry Torokhov 57922ae19c6SDmitry Torokhov if (count == 0) 58022ae19c6SDmitry Torokhov return 0; 58122ae19c6SDmitry Torokhov 582221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex); 58329506415SDmitry Torokhov if (retval) 58429506415SDmitry Torokhov return retval; 58529506415SDmitry Torokhov 58629506415SDmitry Torokhov retval = udev->state == UIST_CREATED ? 587cbf05413SRyan Mallon uinput_inject_events(udev, buffer, count) : 588052876f8SBenjamin Tissoires uinput_setup_device_legacy(udev, buffer, count); 58929506415SDmitry Torokhov 590221979aaSDmitry Torokhov mutex_unlock(&udev->mutex); 59129506415SDmitry Torokhov 59229506415SDmitry Torokhov return retval; 5931da177e4SLinus Torvalds } 5941da177e4SLinus Torvalds 595929d1af5SDmitry Torokhov static bool uinput_fetch_next_event(struct uinput_device *udev, 596929d1af5SDmitry Torokhov struct input_event *event) 597929d1af5SDmitry Torokhov { 598929d1af5SDmitry Torokhov bool have_event; 599929d1af5SDmitry Torokhov 600929d1af5SDmitry Torokhov spin_lock_irq(&udev->dev->event_lock); 601929d1af5SDmitry Torokhov 602929d1af5SDmitry Torokhov have_event = udev->head != udev->tail; 603929d1af5SDmitry Torokhov if (have_event) { 604929d1af5SDmitry Torokhov *event = udev->buff[udev->tail]; 605929d1af5SDmitry Torokhov udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; 606929d1af5SDmitry Torokhov } 607929d1af5SDmitry Torokhov 608929d1af5SDmitry Torokhov spin_unlock_irq(&udev->dev->event_lock); 609929d1af5SDmitry Torokhov 610929d1af5SDmitry Torokhov return have_event; 611929d1af5SDmitry Torokhov } 612929d1af5SDmitry Torokhov 61322ae19c6SDmitry Torokhov static ssize_t uinput_events_to_user(struct uinput_device *udev, 61422ae19c6SDmitry Torokhov char __user *buffer, size_t count) 61522ae19c6SDmitry Torokhov { 61622ae19c6SDmitry Torokhov struct input_event event; 61722ae19c6SDmitry Torokhov size_t read = 0; 61822ae19c6SDmitry Torokhov 61922ae19c6SDmitry Torokhov while (read + input_event_size() <= count && 62022ae19c6SDmitry Torokhov uinput_fetch_next_event(udev, &event)) { 62122ae19c6SDmitry Torokhov 62200ce756cSDmitry Torokhov if (input_event_to_user(buffer + read, &event)) 62300ce756cSDmitry Torokhov return -EFAULT; 62422ae19c6SDmitry Torokhov 62522ae19c6SDmitry Torokhov read += input_event_size(); 62622ae19c6SDmitry Torokhov } 62722ae19c6SDmitry Torokhov 62800ce756cSDmitry Torokhov return read; 62922ae19c6SDmitry Torokhov } 63022ae19c6SDmitry Torokhov 63122ae19c6SDmitry Torokhov static ssize_t uinput_read(struct file *file, char __user *buffer, 63222ae19c6SDmitry Torokhov size_t count, loff_t *ppos) 6331da177e4SLinus Torvalds { 6341da177e4SLinus Torvalds struct uinput_device *udev = file->private_data; 63522ae19c6SDmitry Torokhov ssize_t retval; 6361da177e4SLinus Torvalds 637f40033acSDavid Herrmann if (count != 0 && count < input_event_size()) 638f40033acSDavid Herrmann return -EINVAL; 639f40033acSDavid Herrmann 64022ae19c6SDmitry Torokhov do { 641221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex); 64229506415SDmitry Torokhov if (retval) 64329506415SDmitry Torokhov return retval; 6441da177e4SLinus Torvalds 64522ae19c6SDmitry Torokhov if (udev->state != UIST_CREATED) 64629506415SDmitry Torokhov retval = -ENODEV; 64722ae19c6SDmitry Torokhov else if (udev->head == udev->tail && 64822ae19c6SDmitry Torokhov (file->f_flags & O_NONBLOCK)) 64922ae19c6SDmitry Torokhov retval = -EAGAIN; 65022ae19c6SDmitry Torokhov else 65122ae19c6SDmitry Torokhov retval = uinput_events_to_user(udev, buffer, count); 65229506415SDmitry Torokhov 653221979aaSDmitry Torokhov mutex_unlock(&udev->mutex); 65429506415SDmitry Torokhov 65522ae19c6SDmitry Torokhov if (retval || count == 0) 65622ae19c6SDmitry Torokhov break; 65722ae19c6SDmitry Torokhov 65822ae19c6SDmitry Torokhov if (!(file->f_flags & O_NONBLOCK)) 65922ae19c6SDmitry Torokhov retval = wait_event_interruptible(udev->waitq, 66022ae19c6SDmitry Torokhov udev->head != udev->tail || 66122ae19c6SDmitry Torokhov udev->state != UIST_CREATED); 66222ae19c6SDmitry Torokhov } while (retval == 0); 66322ae19c6SDmitry Torokhov 6641da177e4SLinus Torvalds return retval; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds static unsigned int uinput_poll(struct file *file, poll_table *wait) 6681da177e4SLinus Torvalds { 6691da177e4SLinus Torvalds struct uinput_device *udev = file->private_data; 6701da177e4SLinus Torvalds 6711da177e4SLinus Torvalds poll_wait(file, &udev->waitq, wait); 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds if (udev->head != udev->tail) 6741da177e4SLinus Torvalds return POLLIN | POLLRDNORM; 6751da177e4SLinus Torvalds 6761da177e4SLinus Torvalds return 0; 6771da177e4SLinus Torvalds } 6781da177e4SLinus Torvalds 67929506415SDmitry Torokhov static int uinput_release(struct inode *inode, struct file *file) 6801da177e4SLinus Torvalds { 68129506415SDmitry Torokhov struct uinput_device *udev = file->private_data; 6821da177e4SLinus Torvalds 68329506415SDmitry Torokhov uinput_destroy_device(udev); 6841da177e4SLinus Torvalds kfree(udev); 6851da177e4SLinus Torvalds 6861da177e4SLinus Torvalds return 0; 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds 6892d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT 6902d56f3a3SPhilip Langdale struct uinput_ff_upload_compat { 691c5b3533aSDmitry Torokhov __u32 request_id; 692c5b3533aSDmitry Torokhov __s32 retval; 6932d56f3a3SPhilip Langdale struct ff_effect_compat effect; 6942d56f3a3SPhilip Langdale struct ff_effect_compat old; 6952d56f3a3SPhilip Langdale }; 6962d56f3a3SPhilip Langdale 6972d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer, 6982d56f3a3SPhilip Langdale const struct uinput_ff_upload *ff_up) 6992d56f3a3SPhilip Langdale { 700b8b4ead1SAndrew Morton if (in_compat_syscall()) { 7012d56f3a3SPhilip Langdale struct uinput_ff_upload_compat ff_up_compat; 7022d56f3a3SPhilip Langdale 7032d56f3a3SPhilip Langdale ff_up_compat.request_id = ff_up->request_id; 7042d56f3a3SPhilip Langdale ff_up_compat.retval = ff_up->retval; 7052d56f3a3SPhilip Langdale /* 7062d56f3a3SPhilip Langdale * It so happens that the pointer that gives us the trouble 7072d56f3a3SPhilip Langdale * is the last field in the structure. Since we don't support 7082d56f3a3SPhilip Langdale * custom waveforms in uinput anyway we can just copy the whole 7092d56f3a3SPhilip Langdale * thing (to the compat size) and ignore the pointer. 7102d56f3a3SPhilip Langdale */ 7112d56f3a3SPhilip Langdale memcpy(&ff_up_compat.effect, &ff_up->effect, 7122d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat)); 7132d56f3a3SPhilip Langdale memcpy(&ff_up_compat.old, &ff_up->old, 7142d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat)); 7152d56f3a3SPhilip Langdale 7162d56f3a3SPhilip Langdale if (copy_to_user(buffer, &ff_up_compat, 7172d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload_compat))) 7182d56f3a3SPhilip Langdale return -EFAULT; 7192d56f3a3SPhilip Langdale } else { 7202d56f3a3SPhilip Langdale if (copy_to_user(buffer, ff_up, 7212d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload))) 7222d56f3a3SPhilip Langdale return -EFAULT; 7232d56f3a3SPhilip Langdale } 7242d56f3a3SPhilip Langdale 7252d56f3a3SPhilip Langdale return 0; 7262d56f3a3SPhilip Langdale } 7272d56f3a3SPhilip Langdale 7282d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer, 7292d56f3a3SPhilip Langdale struct uinput_ff_upload *ff_up) 7302d56f3a3SPhilip Langdale { 731b8b4ead1SAndrew Morton if (in_compat_syscall()) { 7322d56f3a3SPhilip Langdale struct uinput_ff_upload_compat ff_up_compat; 7332d56f3a3SPhilip Langdale 7342d56f3a3SPhilip Langdale if (copy_from_user(&ff_up_compat, buffer, 7352d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload_compat))) 7362d56f3a3SPhilip Langdale return -EFAULT; 7372d56f3a3SPhilip Langdale 7382d56f3a3SPhilip Langdale ff_up->request_id = ff_up_compat.request_id; 7392d56f3a3SPhilip Langdale ff_up->retval = ff_up_compat.retval; 7402d56f3a3SPhilip Langdale memcpy(&ff_up->effect, &ff_up_compat.effect, 7412d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat)); 7422d56f3a3SPhilip Langdale memcpy(&ff_up->old, &ff_up_compat.old, 7432d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat)); 7442d56f3a3SPhilip Langdale 7452d56f3a3SPhilip Langdale } else { 7462d56f3a3SPhilip Langdale if (copy_from_user(ff_up, buffer, 7472d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload))) 7482d56f3a3SPhilip Langdale return -EFAULT; 7492d56f3a3SPhilip Langdale } 7502d56f3a3SPhilip Langdale 7512d56f3a3SPhilip Langdale return 0; 7522d56f3a3SPhilip Langdale } 7532d56f3a3SPhilip Langdale 7542d56f3a3SPhilip Langdale #else 7552d56f3a3SPhilip Langdale 7562d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer, 7572d56f3a3SPhilip Langdale const struct uinput_ff_upload *ff_up) 7582d56f3a3SPhilip Langdale { 7592d56f3a3SPhilip Langdale if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload))) 7602d56f3a3SPhilip Langdale return -EFAULT; 7612d56f3a3SPhilip Langdale 7622d56f3a3SPhilip Langdale return 0; 7632d56f3a3SPhilip Langdale } 7642d56f3a3SPhilip Langdale 7652d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer, 7662d56f3a3SPhilip Langdale struct uinput_ff_upload *ff_up) 7672d56f3a3SPhilip Langdale { 7682d56f3a3SPhilip Langdale if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload))) 7692d56f3a3SPhilip Langdale return -EFAULT; 7702d56f3a3SPhilip Langdale 7712d56f3a3SPhilip Langdale return 0; 7722d56f3a3SPhilip Langdale } 7732d56f3a3SPhilip Langdale 7742d56f3a3SPhilip Langdale #endif 7752d56f3a3SPhilip Langdale 77629506415SDmitry Torokhov #define uinput_set_bit(_arg, _bit, _max) \ 77729506415SDmitry Torokhov ({ \ 77829506415SDmitry Torokhov int __ret = 0; \ 77929506415SDmitry Torokhov if (udev->state == UIST_CREATED) \ 78029506415SDmitry Torokhov __ret = -EINVAL; \ 78129506415SDmitry Torokhov else if ((_arg) > (_max)) \ 78229506415SDmitry Torokhov __ret = -EINVAL; \ 78329506415SDmitry Torokhov else set_bit((_arg), udev->dev->_bit); \ 78429506415SDmitry Torokhov __ret; \ 78529506415SDmitry Torokhov }) 7861da177e4SLinus Torvalds 787e3480a61SBenjamin Tissoires static int uinput_str_to_user(void __user *dest, const char *str, 788e3480a61SBenjamin Tissoires unsigned int maxlen) 789e3480a61SBenjamin Tissoires { 790e3480a61SBenjamin Tissoires char __user *p = dest; 791e3480a61SBenjamin Tissoires int len, ret; 792e3480a61SBenjamin Tissoires 793e3480a61SBenjamin Tissoires if (!str) 794e3480a61SBenjamin Tissoires return -ENOENT; 795e3480a61SBenjamin Tissoires 796e3480a61SBenjamin Tissoires if (maxlen == 0) 797e3480a61SBenjamin Tissoires return -EINVAL; 798e3480a61SBenjamin Tissoires 799e3480a61SBenjamin Tissoires len = strlen(str) + 1; 800e3480a61SBenjamin Tissoires if (len > maxlen) 801e3480a61SBenjamin Tissoires len = maxlen; 802e3480a61SBenjamin Tissoires 803e3480a61SBenjamin Tissoires ret = copy_to_user(p, str, len); 804e3480a61SBenjamin Tissoires if (ret) 805e3480a61SBenjamin Tissoires return -EFAULT; 806e3480a61SBenjamin Tissoires 807e3480a61SBenjamin Tissoires /* force terminating '\0' */ 808e3480a61SBenjamin Tissoires ret = put_user(0, p + len - 1); 809e3480a61SBenjamin Tissoires return ret ? -EFAULT : len; 810e3480a61SBenjamin Tissoires } 811e3480a61SBenjamin Tissoires 8122d56f3a3SPhilip Langdale static long uinput_ioctl_handler(struct file *file, unsigned int cmd, 8132d56f3a3SPhilip Langdale unsigned long arg, void __user *p) 8141da177e4SLinus Torvalds { 81529506415SDmitry Torokhov int retval; 8162d56f3a3SPhilip Langdale struct uinput_device *udev = file->private_data; 8171da177e4SLinus Torvalds struct uinput_ff_upload ff_up; 8181da177e4SLinus Torvalds struct uinput_ff_erase ff_erase; 8191da177e4SLinus Torvalds struct uinput_request *req; 8205b6271bdSDmitry Torokhov char *phys; 821e3480a61SBenjamin Tissoires const char *name; 822e3480a61SBenjamin Tissoires unsigned int size; 8231da177e4SLinus Torvalds 824221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex); 82529506415SDmitry Torokhov if (retval) 82629506415SDmitry Torokhov return retval; 82729506415SDmitry Torokhov 82829506415SDmitry Torokhov if (!udev->dev) { 82929506415SDmitry Torokhov retval = uinput_allocate_device(udev); 83029506415SDmitry Torokhov if (retval) 83129506415SDmitry Torokhov goto out; 8321da177e4SLinus Torvalds } 8331da177e4SLinus Torvalds 8341da177e4SLinus Torvalds switch (cmd) { 835ba4e9a61SDavid Herrmann case UI_GET_VERSION: 836ba4e9a61SDavid Herrmann if (put_user(UINPUT_VERSION, 837ba4e9a61SDavid Herrmann (unsigned int __user *)p)) 838ba4e9a61SDavid Herrmann retval = -EFAULT; 839ba4e9a61SDavid Herrmann goto out; 840ba4e9a61SDavid Herrmann 8411da177e4SLinus Torvalds case UI_DEV_CREATE: 8421da177e4SLinus Torvalds retval = uinput_create_device(udev); 8439d51e801SBenjamin Tisssoires goto out; 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds case UI_DEV_DESTROY: 84629506415SDmitry Torokhov uinput_destroy_device(udev); 8479d51e801SBenjamin Tisssoires goto out; 8481da177e4SLinus Torvalds 849052876f8SBenjamin Tissoires case UI_DEV_SETUP: 850052876f8SBenjamin Tissoires retval = uinput_dev_setup(udev, p); 851052876f8SBenjamin Tissoires goto out; 852052876f8SBenjamin Tissoires 853052876f8SBenjamin Tissoires /* UI_ABS_SETUP is handled in the variable size ioctls */ 854052876f8SBenjamin Tissoires 8551da177e4SLinus Torvalds case UI_SET_EVBIT: 85629506415SDmitry Torokhov retval = uinput_set_bit(arg, evbit, EV_MAX); 8579d51e801SBenjamin Tisssoires goto out; 8581da177e4SLinus Torvalds 8591da177e4SLinus Torvalds case UI_SET_KEYBIT: 86029506415SDmitry Torokhov retval = uinput_set_bit(arg, keybit, KEY_MAX); 8619d51e801SBenjamin Tisssoires goto out; 8621da177e4SLinus Torvalds 8631da177e4SLinus Torvalds case UI_SET_RELBIT: 86429506415SDmitry Torokhov retval = uinput_set_bit(arg, relbit, REL_MAX); 8659d51e801SBenjamin Tisssoires goto out; 8661da177e4SLinus Torvalds 8671da177e4SLinus Torvalds case UI_SET_ABSBIT: 86829506415SDmitry Torokhov retval = uinput_set_bit(arg, absbit, ABS_MAX); 8699d51e801SBenjamin Tisssoires goto out; 8701da177e4SLinus Torvalds 8711da177e4SLinus Torvalds case UI_SET_MSCBIT: 87229506415SDmitry Torokhov retval = uinput_set_bit(arg, mscbit, MSC_MAX); 8739d51e801SBenjamin Tisssoires goto out; 8741da177e4SLinus Torvalds 8751da177e4SLinus Torvalds case UI_SET_LEDBIT: 87629506415SDmitry Torokhov retval = uinput_set_bit(arg, ledbit, LED_MAX); 8779d51e801SBenjamin Tisssoires goto out; 8781da177e4SLinus Torvalds 8791da177e4SLinus Torvalds case UI_SET_SNDBIT: 88029506415SDmitry Torokhov retval = uinput_set_bit(arg, sndbit, SND_MAX); 8819d51e801SBenjamin Tisssoires goto out; 8821da177e4SLinus Torvalds 8831da177e4SLinus Torvalds case UI_SET_FFBIT: 88429506415SDmitry Torokhov retval = uinput_set_bit(arg, ffbit, FF_MAX); 8859d51e801SBenjamin Tisssoires goto out; 8861da177e4SLinus Torvalds 88759c7c037SDmitry Torokhov case UI_SET_SWBIT: 88859c7c037SDmitry Torokhov retval = uinput_set_bit(arg, swbit, SW_MAX); 8899d51e801SBenjamin Tisssoires goto out; 89059c7c037SDmitry Torokhov 89185b77200SHenrik Rydberg case UI_SET_PROPBIT: 89285b77200SHenrik Rydberg retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX); 8939d51e801SBenjamin Tisssoires goto out; 89485b77200SHenrik Rydberg 8951da177e4SLinus Torvalds case UI_SET_PHYS: 89629506415SDmitry Torokhov if (udev->state == UIST_CREATED) { 89729506415SDmitry Torokhov retval = -EINVAL; 89829506415SDmitry Torokhov goto out; 89929506415SDmitry Torokhov } 9004dfcc271SDmitry Torokhov 9014dfcc271SDmitry Torokhov phys = strndup_user(p, 1024); 9024dfcc271SDmitry Torokhov if (IS_ERR(phys)) { 9034dfcc271SDmitry Torokhov retval = PTR_ERR(phys); 9044dfcc271SDmitry Torokhov goto out; 9051da177e4SLinus Torvalds } 9064dfcc271SDmitry Torokhov 9071da177e4SLinus Torvalds kfree(udev->dev->phys); 9084dfcc271SDmitry Torokhov udev->dev->phys = phys; 9099d51e801SBenjamin Tisssoires goto out; 9101da177e4SLinus Torvalds 9111da177e4SLinus Torvalds case UI_BEGIN_FF_UPLOAD: 9122d56f3a3SPhilip Langdale retval = uinput_ff_upload_from_user(p, &ff_up); 9132d56f3a3SPhilip Langdale if (retval) 9149d51e801SBenjamin Tisssoires goto out; 9152d56f3a3SPhilip Langdale 9161da177e4SLinus Torvalds req = uinput_request_find(udev, ff_up.request_id); 91754ce165eSDmitry Torokhov if (!req || req->code != UI_FF_UPLOAD || 91854ce165eSDmitry Torokhov !req->u.upload.effect) { 9191da177e4SLinus Torvalds retval = -EINVAL; 9209d51e801SBenjamin Tisssoires goto out; 9211da177e4SLinus Torvalds } 9222d56f3a3SPhilip Langdale 9231da177e4SLinus Torvalds ff_up.retval = 0; 9242d56f3a3SPhilip Langdale ff_up.effect = *req->u.upload.effect; 925ff462551SAnssi Hannula if (req->u.upload.old) 9262d56f3a3SPhilip Langdale ff_up.old = *req->u.upload.old; 927ff462551SAnssi Hannula else 928ff462551SAnssi Hannula memset(&ff_up.old, 0, sizeof(struct ff_effect)); 929ff462551SAnssi Hannula 9302d56f3a3SPhilip Langdale retval = uinput_ff_upload_to_user(p, &ff_up); 9319d51e801SBenjamin Tisssoires goto out; 9321da177e4SLinus Torvalds 9331da177e4SLinus Torvalds case UI_BEGIN_FF_ERASE: 9341da177e4SLinus Torvalds if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { 9351da177e4SLinus Torvalds retval = -EFAULT; 9369d51e801SBenjamin Tisssoires goto out; 9371da177e4SLinus Torvalds } 9382d56f3a3SPhilip Langdale 9391da177e4SLinus Torvalds req = uinput_request_find(udev, ff_erase.request_id); 9402d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_ERASE) { 9411da177e4SLinus Torvalds retval = -EINVAL; 9429d51e801SBenjamin Tisssoires goto out; 9431da177e4SLinus Torvalds } 9442d56f3a3SPhilip Langdale 9451da177e4SLinus Torvalds ff_erase.retval = 0; 9461da177e4SLinus Torvalds ff_erase.effect_id = req->u.effect_id; 9471da177e4SLinus Torvalds if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { 9481da177e4SLinus Torvalds retval = -EFAULT; 9499d51e801SBenjamin Tisssoires goto out; 9501da177e4SLinus Torvalds } 9512d56f3a3SPhilip Langdale 9529d51e801SBenjamin Tisssoires goto out; 9531da177e4SLinus Torvalds 9541da177e4SLinus Torvalds case UI_END_FF_UPLOAD: 9552d56f3a3SPhilip Langdale retval = uinput_ff_upload_from_user(p, &ff_up); 9562d56f3a3SPhilip Langdale if (retval) 9579d51e801SBenjamin Tisssoires goto out; 9582d56f3a3SPhilip Langdale 9591da177e4SLinus Torvalds req = uinput_request_find(udev, ff_up.request_id); 9602d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_UPLOAD || 9612d56f3a3SPhilip Langdale !req->u.upload.effect) { 9621da177e4SLinus Torvalds retval = -EINVAL; 9639d51e801SBenjamin Tisssoires goto out; 9641da177e4SLinus Torvalds } 9652d56f3a3SPhilip Langdale 9661da177e4SLinus Torvalds req->retval = ff_up.retval; 9676b4877c7SDmitry Torokhov complete(&req->done); 9689d51e801SBenjamin Tisssoires goto out; 9691da177e4SLinus Torvalds 9701da177e4SLinus Torvalds case UI_END_FF_ERASE: 9711da177e4SLinus Torvalds if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { 9721da177e4SLinus Torvalds retval = -EFAULT; 9739d51e801SBenjamin Tisssoires goto out; 9741da177e4SLinus Torvalds } 9752d56f3a3SPhilip Langdale 9761da177e4SLinus Torvalds req = uinput_request_find(udev, ff_erase.request_id); 9772d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_ERASE) { 9781da177e4SLinus Torvalds retval = -EINVAL; 9799d51e801SBenjamin Tisssoires goto out; 9801da177e4SLinus Torvalds } 9812d56f3a3SPhilip Langdale 9821da177e4SLinus Torvalds req->retval = ff_erase.retval; 9836b4877c7SDmitry Torokhov complete(&req->done); 9849d51e801SBenjamin Tisssoires goto out; 9851da177e4SLinus Torvalds } 98629506415SDmitry Torokhov 987e3480a61SBenjamin Tissoires size = _IOC_SIZE(cmd); 988e3480a61SBenjamin Tissoires 989e3480a61SBenjamin Tissoires /* Now check variable-length commands */ 990e3480a61SBenjamin Tissoires switch (cmd & ~IOCSIZE_MASK) { 991e3480a61SBenjamin Tissoires case UI_GET_SYSNAME(0): 992e3480a61SBenjamin Tissoires if (udev->state != UIST_CREATED) { 993e3480a61SBenjamin Tissoires retval = -ENOENT; 994e3480a61SBenjamin Tissoires goto out; 995e3480a61SBenjamin Tissoires } 996e3480a61SBenjamin Tissoires name = dev_name(&udev->dev->dev); 997e3480a61SBenjamin Tissoires retval = uinput_str_to_user(p, name, size); 998e3480a61SBenjamin Tissoires goto out; 999052876f8SBenjamin Tissoires 1000052876f8SBenjamin Tissoires case UI_ABS_SETUP & ~IOCSIZE_MASK: 1001052876f8SBenjamin Tissoires retval = uinput_abs_setup(udev, p, size); 1002052876f8SBenjamin Tissoires goto out; 1003e3480a61SBenjamin Tissoires } 1004e3480a61SBenjamin Tissoires 10059d51e801SBenjamin Tisssoires retval = -EINVAL; 100629506415SDmitry Torokhov out: 1007221979aaSDmitry Torokhov mutex_unlock(&udev->mutex); 10081da177e4SLinus Torvalds return retval; 10091da177e4SLinus Torvalds } 10101da177e4SLinus Torvalds 10112d56f3a3SPhilip Langdale static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 10122d56f3a3SPhilip Langdale { 10132d56f3a3SPhilip Langdale return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg); 10142d56f3a3SPhilip Langdale } 10152d56f3a3SPhilip Langdale 10162d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT 1017affa80bdSRicky Liang 1018affa80bdSRicky Liang #define UI_SET_PHYS_COMPAT _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t) 1019affa80bdSRicky Liang 102054ce165eSDmitry Torokhov static long uinput_compat_ioctl(struct file *file, 102154ce165eSDmitry Torokhov unsigned int cmd, unsigned long arg) 10222d56f3a3SPhilip Langdale { 1023affa80bdSRicky Liang if (cmd == UI_SET_PHYS_COMPAT) 1024affa80bdSRicky Liang cmd = UI_SET_PHYS; 1025affa80bdSRicky Liang 10262d56f3a3SPhilip Langdale return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg)); 10272d56f3a3SPhilip Langdale } 10282d56f3a3SPhilip Langdale #endif 10292d56f3a3SPhilip Langdale 10302b8693c0SArjan van de Ven static const struct file_operations uinput_fops = { 10311da177e4SLinus Torvalds .owner = THIS_MODULE, 10321da177e4SLinus Torvalds .open = uinput_open, 103329506415SDmitry Torokhov .release = uinput_release, 10341da177e4SLinus Torvalds .read = uinput_read, 10351da177e4SLinus Torvalds .write = uinput_write, 10361da177e4SLinus Torvalds .poll = uinput_poll, 103729506415SDmitry Torokhov .unlocked_ioctl = uinput_ioctl, 10382d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT 10392d56f3a3SPhilip Langdale .compat_ioctl = uinput_compat_ioctl, 10402d56f3a3SPhilip Langdale #endif 10416038f373SArnd Bergmann .llseek = no_llseek, 10421da177e4SLinus Torvalds }; 10431da177e4SLinus Torvalds 10441da177e4SLinus Torvalds static struct miscdevice uinput_misc = { 10451da177e4SLinus Torvalds .fops = &uinput_fops, 10461da177e4SLinus Torvalds .minor = UINPUT_MINOR, 10471da177e4SLinus Torvalds .name = UINPUT_NAME, 10481da177e4SLinus Torvalds }; 1049ca75d601SPrasannaKumar Muralidharan module_misc_device(uinput_misc); 1050ca75d601SPrasannaKumar Muralidharan 10518905aaafSKay Sievers MODULE_ALIAS_MISCDEV(UINPUT_MINOR); 10528905aaafSKay Sievers MODULE_ALIAS("devname:" UINPUT_NAME); 10531da177e4SLinus Torvalds 10541da177e4SLinus Torvalds MODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); 10551da177e4SLinus Torvalds MODULE_DESCRIPTION("User level driver support for input subsystem"); 10561da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 1057ff462551SAnssi Hannula MODULE_VERSION("0.3"); 1058