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: 23ff462551SAnssi Hannula * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>) 24ff462551SAnssi Hannula * - updated ff support for the changes in kernel interface 25ff462551SAnssi Hannula * - added MODULE_VERSION 261da177e4SLinus Torvalds * 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>) 271da177e4SLinus Torvalds * - added force feedback support 281da177e4SLinus Torvalds * - added UI_SET_PHYS 291da177e4SLinus Torvalds * 0.1 20/06/2002 301da177e4SLinus Torvalds * - first public version 311da177e4SLinus Torvalds */ 321da177e4SLinus Torvalds #include <linux/poll.h> 33a99bbaf5SAlexey Dobriyan #include <linux/sched.h> 341da177e4SLinus Torvalds #include <linux/slab.h> 351da177e4SLinus Torvalds #include <linux/module.h> 361da177e4SLinus Torvalds #include <linux/init.h> 371da177e4SLinus Torvalds #include <linux/fs.h> 381da177e4SLinus Torvalds #include <linux/miscdevice.h> 391da177e4SLinus Torvalds #include <linux/uinput.h> 4047c78e89SHenrik Rydberg #include <linux/input/mt.h> 412d56f3a3SPhilip Langdale #include "../input-compat.h" 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 441da177e4SLinus Torvalds { 45373f9713SDmitry Torokhov struct uinput_device *udev = input_get_drvdata(dev); 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds udev->buff[udev->head].type = type; 481da177e4SLinus Torvalds udev->buff[udev->head].code = code; 491da177e4SLinus Torvalds udev->buff[udev->head].value = value; 501da177e4SLinus Torvalds do_gettimeofday(&udev->buff[udev->head].time); 511da177e4SLinus Torvalds udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds wake_up_interruptible(&udev->waitq); 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds return 0; 561da177e4SLinus Torvalds } 571da177e4SLinus Torvalds 5805cebd38SAristeu Sergio Rozanski Filho /* Atomically allocate an ID for the given request. Returns 0 on success. */ 590048e603SDmitry Torokhov static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request) 601da177e4SLinus Torvalds { 611da177e4SLinus Torvalds int id; 62152c12f5SDmitry Torokhov int err = -1; 631da177e4SLinus Torvalds 640048e603SDmitry Torokhov spin_lock(&udev->requests_lock); 65152c12f5SDmitry Torokhov 6605cebd38SAristeu Sergio Rozanski Filho for (id = 0; id < UINPUT_NUM_REQUESTS; id++) { 671da177e4SLinus Torvalds if (!udev->requests[id]) { 681da177e4SLinus Torvalds request->id = id; 690048e603SDmitry Torokhov udev->requests[id] = request; 70152c12f5SDmitry Torokhov err = 0; 71152c12f5SDmitry Torokhov break; 721da177e4SLinus Torvalds } 7305cebd38SAristeu Sergio Rozanski Filho } 74152c12f5SDmitry Torokhov 750048e603SDmitry Torokhov spin_unlock(&udev->requests_lock); 76152c12f5SDmitry Torokhov return err; 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id) 801da177e4SLinus Torvalds { 811da177e4SLinus Torvalds /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ 821da177e4SLinus Torvalds if (id >= UINPUT_NUM_REQUESTS || id < 0) 831da177e4SLinus Torvalds return NULL; 842d56f3a3SPhilip Langdale 851da177e4SLinus Torvalds return udev->requests[id]; 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds 880048e603SDmitry Torokhov static inline int uinput_request_reserve_slot(struct uinput_device *udev, struct uinput_request *request) 891da177e4SLinus Torvalds { 900048e603SDmitry Torokhov /* Allocate slot. If none are available right away, wait. */ 910048e603SDmitry Torokhov return wait_event_interruptible(udev->requests_waitq, 920048e603SDmitry Torokhov !uinput_request_alloc_id(udev, request)); 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 950048e603SDmitry Torokhov static void uinput_request_done(struct uinput_device *udev, struct uinput_request *request) 961da177e4SLinus Torvalds { 970048e603SDmitry Torokhov /* Mark slot as available */ 980048e603SDmitry Torokhov udev->requests[request->id] = NULL; 99e597f0c8SDmitry Torokhov wake_up(&udev->requests_waitq); 100e7507ed9SDmitry Torokhov 101e7507ed9SDmitry Torokhov complete(&request->done); 1020048e603SDmitry Torokhov } 1030048e603SDmitry Torokhov 10405cebd38SAristeu Sergio Rozanski Filho static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request) 1050048e603SDmitry Torokhov { 10605cebd38SAristeu Sergio Rozanski Filho int retval; 1071da177e4SLinus Torvalds 10805cebd38SAristeu Sergio Rozanski Filho retval = uinput_request_reserve_slot(udev, request); 10905cebd38SAristeu Sergio Rozanski Filho if (retval) 11005cebd38SAristeu Sergio Rozanski Filho return retval; 11105cebd38SAristeu Sergio Rozanski Filho 11205cebd38SAristeu Sergio Rozanski Filho retval = mutex_lock_interruptible(&udev->mutex); 11305cebd38SAristeu Sergio Rozanski Filho if (retval) 11405cebd38SAristeu Sergio Rozanski Filho return retval; 11505cebd38SAristeu Sergio Rozanski Filho 11605cebd38SAristeu Sergio Rozanski Filho if (udev->state != UIST_CREATED) { 11705cebd38SAristeu Sergio Rozanski Filho retval = -ENODEV; 11805cebd38SAristeu Sergio Rozanski Filho goto out; 11905cebd38SAristeu Sergio Rozanski Filho } 12005cebd38SAristeu Sergio Rozanski Filho 12105cebd38SAristeu Sergio Rozanski Filho /* Tell our userspace app about this new request by queueing an input event */ 12205cebd38SAristeu Sergio Rozanski Filho uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id); 12305cebd38SAristeu Sergio Rozanski Filho 12405cebd38SAristeu Sergio Rozanski Filho out: 12505cebd38SAristeu Sergio Rozanski Filho mutex_unlock(&udev->mutex); 12605cebd38SAristeu Sergio Rozanski Filho return retval; 12705cebd38SAristeu Sergio Rozanski Filho } 12805cebd38SAristeu Sergio Rozanski Filho 12905cebd38SAristeu Sergio Rozanski Filho /* 13005cebd38SAristeu Sergio Rozanski Filho * Fail all ouitstanding requests so handlers don't wait for the userspace 13105cebd38SAristeu Sergio Rozanski Filho * to finish processing them. 13205cebd38SAristeu Sergio Rozanski Filho */ 13305cebd38SAristeu Sergio Rozanski Filho static void uinput_flush_requests(struct uinput_device *udev) 13405cebd38SAristeu Sergio Rozanski Filho { 13505cebd38SAristeu Sergio Rozanski Filho struct uinput_request *request; 13605cebd38SAristeu Sergio Rozanski Filho int i; 13705cebd38SAristeu Sergio Rozanski Filho 13805cebd38SAristeu Sergio Rozanski Filho spin_lock(&udev->requests_lock); 13905cebd38SAristeu Sergio Rozanski Filho 14005cebd38SAristeu Sergio Rozanski Filho for (i = 0; i < UINPUT_NUM_REQUESTS; i++) { 14105cebd38SAristeu Sergio Rozanski Filho request = udev->requests[i]; 14205cebd38SAristeu Sergio Rozanski Filho if (request) { 14305cebd38SAristeu Sergio Rozanski Filho request->retval = -ENODEV; 14405cebd38SAristeu Sergio Rozanski Filho uinput_request_done(udev, request); 14505cebd38SAristeu Sergio Rozanski Filho } 14605cebd38SAristeu Sergio Rozanski Filho } 14705cebd38SAristeu Sergio Rozanski Filho 14805cebd38SAristeu Sergio Rozanski Filho spin_unlock(&udev->requests_lock); 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds 151ff462551SAnssi Hannula static void uinput_dev_set_gain(struct input_dev *dev, u16 gain) 152ff462551SAnssi Hannula { 153ff462551SAnssi Hannula uinput_dev_event(dev, EV_FF, FF_GAIN, gain); 154ff462551SAnssi Hannula } 155ff462551SAnssi Hannula 156ff462551SAnssi Hannula static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude) 157ff462551SAnssi Hannula { 158ff462551SAnssi Hannula uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude); 159ff462551SAnssi Hannula } 160ff462551SAnssi Hannula 161ff462551SAnssi Hannula static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value) 162ff462551SAnssi Hannula { 163ff462551SAnssi Hannula return uinput_dev_event(dev, EV_FF, effect_id, value); 164ff462551SAnssi Hannula } 165ff462551SAnssi Hannula 166ff462551SAnssi Hannula static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) 1671da177e4SLinus Torvalds { 16805cebd38SAristeu Sergio Rozanski Filho struct uinput_device *udev = input_get_drvdata(dev); 1691da177e4SLinus Torvalds struct uinput_request request; 1700048e603SDmitry Torokhov int retval; 1711da177e4SLinus Torvalds 1722d56f3a3SPhilip Langdale /* 1732d56f3a3SPhilip Langdale * uinput driver does not currently support periodic effects with 1742d56f3a3SPhilip Langdale * custom waveform since it does not have a way to pass buffer of 1752d56f3a3SPhilip Langdale * samples (custom_data) to userspace. If ever there is a device 1762d56f3a3SPhilip Langdale * supporting custom waveforms we would need to define an additional 1772d56f3a3SPhilip Langdale * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out. 1782d56f3a3SPhilip Langdale */ 1792d56f3a3SPhilip Langdale if (effect->type == FF_PERIODIC && 1802d56f3a3SPhilip Langdale effect->u.periodic.waveform == FF_CUSTOM) 1812d56f3a3SPhilip Langdale return -EINVAL; 1822d56f3a3SPhilip Langdale 1830048e603SDmitry Torokhov request.id = -1; 1840048e603SDmitry Torokhov init_completion(&request.done); 1850048e603SDmitry Torokhov request.code = UI_FF_UPLOAD; 186ff462551SAnssi Hannula request.u.upload.effect = effect; 187ff462551SAnssi Hannula request.u.upload.old = old; 1880048e603SDmitry Torokhov 18905cebd38SAristeu Sergio Rozanski Filho retval = uinput_request_submit(udev, &request); 19005cebd38SAristeu Sergio Rozanski Filho if (!retval) { 19105cebd38SAristeu Sergio Rozanski Filho wait_for_completion(&request.done); 19205cebd38SAristeu Sergio Rozanski Filho retval = request.retval; 19305cebd38SAristeu Sergio Rozanski Filho } 1940048e603SDmitry Torokhov 1950048e603SDmitry Torokhov return retval; 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) 1991da177e4SLinus Torvalds { 20005cebd38SAristeu Sergio Rozanski Filho struct uinput_device *udev = input_get_drvdata(dev); 2011da177e4SLinus Torvalds struct uinput_request request; 2020048e603SDmitry Torokhov int retval; 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds if (!test_bit(EV_FF, dev->evbit)) 2051da177e4SLinus Torvalds return -ENOSYS; 2061da177e4SLinus Torvalds 2070048e603SDmitry Torokhov request.id = -1; 2080048e603SDmitry Torokhov init_completion(&request.done); 2090048e603SDmitry Torokhov request.code = UI_FF_ERASE; 2101da177e4SLinus Torvalds request.u.effect_id = effect_id; 2110048e603SDmitry Torokhov 21205cebd38SAristeu Sergio Rozanski Filho retval = uinput_request_submit(udev, &request); 21305cebd38SAristeu Sergio Rozanski Filho if (!retval) { 21405cebd38SAristeu Sergio Rozanski Filho wait_for_completion(&request.done); 21505cebd38SAristeu Sergio Rozanski Filho retval = request.retval; 21605cebd38SAristeu Sergio Rozanski Filho } 2170048e603SDmitry Torokhov 2180048e603SDmitry Torokhov return retval; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 22129506415SDmitry Torokhov static void uinput_destroy_device(struct uinput_device *udev) 22229506415SDmitry Torokhov { 22329506415SDmitry Torokhov const char *name, *phys; 22405cebd38SAristeu Sergio Rozanski Filho struct input_dev *dev = udev->dev; 22505cebd38SAristeu Sergio Rozanski Filho enum uinput_state old_state = udev->state; 22629506415SDmitry Torokhov 22705cebd38SAristeu Sergio Rozanski Filho udev->state = UIST_NEW_DEVICE; 22805cebd38SAristeu Sergio Rozanski Filho 22905cebd38SAristeu Sergio Rozanski Filho if (dev) { 23005cebd38SAristeu Sergio Rozanski Filho name = dev->name; 23105cebd38SAristeu Sergio Rozanski Filho phys = dev->phys; 23205cebd38SAristeu Sergio Rozanski Filho if (old_state == UIST_CREATED) { 23305cebd38SAristeu Sergio Rozanski Filho uinput_flush_requests(udev); 23405cebd38SAristeu Sergio Rozanski Filho input_unregister_device(dev); 23505cebd38SAristeu Sergio Rozanski Filho } else { 23605cebd38SAristeu Sergio Rozanski Filho input_free_device(dev); 23705cebd38SAristeu Sergio Rozanski Filho } 23829506415SDmitry Torokhov kfree(name); 23929506415SDmitry Torokhov kfree(phys); 24029506415SDmitry Torokhov udev->dev = NULL; 24129506415SDmitry Torokhov } 24229506415SDmitry Torokhov } 24329506415SDmitry Torokhov 2441da177e4SLinus Torvalds static int uinput_create_device(struct uinput_device *udev) 2451da177e4SLinus Torvalds { 246ff462551SAnssi Hannula struct input_dev *dev = udev->dev; 24729506415SDmitry Torokhov int error; 24829506415SDmitry Torokhov 24929506415SDmitry Torokhov if (udev->state != UIST_SETUP_COMPLETE) { 2501da177e4SLinus Torvalds printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); 2511da177e4SLinus Torvalds return -EINVAL; 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds 254ff462551SAnssi Hannula if (udev->ff_effects_max) { 255ff462551SAnssi Hannula error = input_ff_create(dev, udev->ff_effects_max); 256ff462551SAnssi Hannula if (error) 257ff462551SAnssi Hannula goto fail1; 258ff462551SAnssi Hannula 259ff462551SAnssi Hannula dev->ff->upload = uinput_dev_upload_effect; 260ff462551SAnssi Hannula dev->ff->erase = uinput_dev_erase_effect; 261ff462551SAnssi Hannula dev->ff->playback = uinput_dev_playback; 262ff462551SAnssi Hannula dev->ff->set_gain = uinput_dev_set_gain; 263ff462551SAnssi Hannula dev->ff->set_autocenter = uinput_dev_set_autocenter; 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds 266ff462551SAnssi Hannula error = input_register_device(udev->dev); 267ff462551SAnssi Hannula if (error) 268ff462551SAnssi Hannula goto fail2; 269ff462551SAnssi Hannula 27029506415SDmitry Torokhov udev->state = UIST_CREATED; 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds return 0; 273ff462551SAnssi Hannula 274ff462551SAnssi Hannula fail2: input_ff_destroy(dev); 275ff462551SAnssi Hannula fail1: uinput_destroy_device(udev); 276ff462551SAnssi Hannula return error; 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds static int uinput_open(struct inode *inode, struct file *file) 2801da177e4SLinus Torvalds { 2811da177e4SLinus Torvalds struct uinput_device *newdev; 2821da177e4SLinus Torvalds 28329506415SDmitry Torokhov newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL); 2841da177e4SLinus Torvalds if (!newdev) 28529506415SDmitry Torokhov return -ENOMEM; 28629506415SDmitry Torokhov 287221979aaSDmitry Torokhov mutex_init(&newdev->mutex); 2880048e603SDmitry Torokhov spin_lock_init(&newdev->requests_lock); 2891da177e4SLinus Torvalds init_waitqueue_head(&newdev->requests_waitq); 29029506415SDmitry Torokhov init_waitqueue_head(&newdev->waitq); 29129506415SDmitry Torokhov newdev->state = UIST_NEW_DEVICE; 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds file->private_data = newdev; 294daf8a96bSDmitry Torokhov nonseekable_open(inode, file); 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds return 0; 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds static int uinput_validate_absbits(struct input_dev *dev) 3001da177e4SLinus Torvalds { 3011da177e4SLinus Torvalds unsigned int cnt; 3021da177e4SLinus Torvalds int retval = 0; 3031da177e4SLinus Torvalds 30481c2a3baSDaniel Mack for (cnt = 0; cnt < ABS_CNT; cnt++) { 3051da177e4SLinus Torvalds if (!test_bit(cnt, dev->absbit)) 3061da177e4SLinus Torvalds continue; 3071da177e4SLinus Torvalds 308987a6c02SDaniel Mack if (input_abs_get_max(dev, cnt) <= input_abs_get_min(dev, cnt)) { 3091da177e4SLinus Torvalds printk(KERN_DEBUG 3101da177e4SLinus Torvalds "%s: invalid abs[%02x] min:%d max:%d\n", 3111da177e4SLinus Torvalds UINPUT_NAME, cnt, 312987a6c02SDaniel Mack input_abs_get_min(dev, cnt), 313987a6c02SDaniel Mack input_abs_get_max(dev, cnt)); 3141da177e4SLinus Torvalds retval = -EINVAL; 3151da177e4SLinus Torvalds break; 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 318987a6c02SDaniel Mack if (input_abs_get_flat(dev, cnt) > 319987a6c02SDaniel Mack input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) { 3201da177e4SLinus Torvalds printk(KERN_DEBUG 321987a6c02SDaniel Mack "%s: abs_flat #%02x out of range: %d " 3221da177e4SLinus Torvalds "(min:%d/max:%d)\n", 323987a6c02SDaniel Mack UINPUT_NAME, cnt, 324987a6c02SDaniel Mack input_abs_get_flat(dev, cnt), 325987a6c02SDaniel Mack input_abs_get_min(dev, cnt), 326987a6c02SDaniel Mack input_abs_get_max(dev, cnt)); 3271da177e4SLinus Torvalds retval = -EINVAL; 3281da177e4SLinus Torvalds break; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds return retval; 3321da177e4SLinus Torvalds } 3331da177e4SLinus Torvalds 33429506415SDmitry Torokhov static int uinput_allocate_device(struct uinput_device *udev) 33529506415SDmitry Torokhov { 33629506415SDmitry Torokhov udev->dev = input_allocate_device(); 33729506415SDmitry Torokhov if (!udev->dev) 33829506415SDmitry Torokhov return -ENOMEM; 33929506415SDmitry Torokhov 34029506415SDmitry Torokhov udev->dev->event = uinput_dev_event; 341373f9713SDmitry Torokhov input_set_drvdata(udev->dev, udev); 34229506415SDmitry Torokhov 34329506415SDmitry Torokhov return 0; 34429506415SDmitry Torokhov } 34529506415SDmitry Torokhov 34629506415SDmitry Torokhov static int uinput_setup_device(struct uinput_device *udev, const char __user *buffer, size_t count) 3471da177e4SLinus Torvalds { 3481da177e4SLinus Torvalds struct uinput_user_dev *user_dev; 3491da177e4SLinus Torvalds struct input_dev *dev; 3505b6271bdSDmitry Torokhov char *name; 351987a6c02SDaniel Mack int i, size; 352152c12f5SDmitry Torokhov int retval; 3531da177e4SLinus Torvalds 35429506415SDmitry Torokhov if (count != sizeof(struct uinput_user_dev)) 35529506415SDmitry Torokhov return -EINVAL; 3561da177e4SLinus Torvalds 35729506415SDmitry Torokhov if (!udev->dev) { 35829506415SDmitry Torokhov retval = uinput_allocate_device(udev); 35929506415SDmitry Torokhov if (retval) 36029506415SDmitry Torokhov return retval; 36129506415SDmitry Torokhov } 36229506415SDmitry Torokhov 3631da177e4SLinus Torvalds dev = udev->dev; 3641da177e4SLinus Torvalds 365152c12f5SDmitry Torokhov user_dev = kmalloc(sizeof(struct uinput_user_dev), GFP_KERNEL); 36629506415SDmitry Torokhov if (!user_dev) 36729506415SDmitry Torokhov return -ENOMEM; 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) { 3701da177e4SLinus Torvalds retval = -EFAULT; 3711da177e4SLinus Torvalds goto exit; 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds 374ff462551SAnssi Hannula udev->ff_effects_max = user_dev->ff_effects_max; 375ff462551SAnssi Hannula 3761da177e4SLinus Torvalds size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1; 37729506415SDmitry Torokhov if (!size) { 37829506415SDmitry Torokhov retval = -EINVAL; 37929506415SDmitry Torokhov goto exit; 38029506415SDmitry Torokhov } 38129506415SDmitry Torokhov 38229506415SDmitry Torokhov kfree(dev->name); 3835b6271bdSDmitry Torokhov dev->name = name = kmalloc(size, GFP_KERNEL); 3845b6271bdSDmitry Torokhov if (!name) { 3851da177e4SLinus Torvalds retval = -ENOMEM; 3861da177e4SLinus Torvalds goto exit; 3871da177e4SLinus Torvalds } 3885b6271bdSDmitry Torokhov strlcpy(name, user_dev->name, size); 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvalds dev->id.bustype = user_dev->id.bustype; 3911da177e4SLinus Torvalds dev->id.vendor = user_dev->id.vendor; 3921da177e4SLinus Torvalds dev->id.product = user_dev->id.product; 3931da177e4SLinus Torvalds dev->id.version = user_dev->id.version; 3941da177e4SLinus Torvalds 395987a6c02SDaniel Mack for (i = 0; i < ABS_CNT; i++) { 396987a6c02SDaniel Mack input_abs_set_max(dev, i, user_dev->absmax[i]); 397987a6c02SDaniel Mack input_abs_set_min(dev, i, user_dev->absmin[i]); 398987a6c02SDaniel Mack input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]); 399987a6c02SDaniel Mack input_abs_set_flat(dev, i, user_dev->absflat[i]); 400987a6c02SDaniel Mack } 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds /* check if absmin/absmax/absfuzz/absflat are filled as 4031da177e4SLinus Torvalds * told in Documentation/input/input-programming.txt */ 4041da177e4SLinus Torvalds if (test_bit(EV_ABS, dev->evbit)) { 40529506415SDmitry Torokhov retval = uinput_validate_absbits(dev); 40629506415SDmitry Torokhov if (retval < 0) 40729506415SDmitry Torokhov goto exit; 40838e7afe9SHenrik Rydberg if (test_bit(ABS_MT_SLOT, dev->absbit)) { 40938e7afe9SHenrik Rydberg int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; 4108cde8100SHenrik Rydberg input_mt_init_slots(dev, nslot); 41138e7afe9SHenrik Rydberg } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { 41238e7afe9SHenrik Rydberg input_set_events_per_packet(dev, 60); 41338e7afe9SHenrik Rydberg } 4141da177e4SLinus Torvalds } 41529506415SDmitry Torokhov 41629506415SDmitry Torokhov udev->state = UIST_SETUP_COMPLETE; 41729506415SDmitry Torokhov retval = count; 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds exit: 4201da177e4SLinus Torvalds kfree(user_dev); 4211da177e4SLinus Torvalds return retval; 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds 42429506415SDmitry Torokhov static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char __user *buffer, size_t count) 4251da177e4SLinus Torvalds { 4261da177e4SLinus Torvalds struct input_event ev; 4271da177e4SLinus Torvalds 4282d56f3a3SPhilip Langdale if (count < input_event_size()) 42929506415SDmitry Torokhov return -EINVAL; 43029506415SDmitry Torokhov 4312d56f3a3SPhilip Langdale if (input_event_from_user(buffer, &ev)) 4321da177e4SLinus Torvalds return -EFAULT; 4331da177e4SLinus Torvalds 43429506415SDmitry Torokhov input_event(udev->dev, ev.type, ev.code, ev.value); 43529506415SDmitry Torokhov 4362d56f3a3SPhilip Langdale return input_event_size(); 43729506415SDmitry Torokhov } 43829506415SDmitry Torokhov 43929506415SDmitry Torokhov static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 44029506415SDmitry Torokhov { 44129506415SDmitry Torokhov struct uinput_device *udev = file->private_data; 44229506415SDmitry Torokhov int retval; 44329506415SDmitry Torokhov 444221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex); 44529506415SDmitry Torokhov if (retval) 44629506415SDmitry Torokhov return retval; 44729506415SDmitry Torokhov 44829506415SDmitry Torokhov retval = udev->state == UIST_CREATED ? 44929506415SDmitry Torokhov uinput_inject_event(udev, buffer, count) : 45029506415SDmitry Torokhov uinput_setup_device(udev, buffer, count); 45129506415SDmitry Torokhov 452221979aaSDmitry Torokhov mutex_unlock(&udev->mutex); 45329506415SDmitry Torokhov 45429506415SDmitry Torokhov return retval; 4551da177e4SLinus Torvalds } 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 4581da177e4SLinus Torvalds { 4591da177e4SLinus Torvalds struct uinput_device *udev = file->private_data; 4601da177e4SLinus Torvalds int retval = 0; 4611da177e4SLinus Torvalds 46229506415SDmitry Torokhov if (udev->state != UIST_CREATED) 4631da177e4SLinus Torvalds return -ENODEV; 4641da177e4SLinus Torvalds 465152c12f5SDmitry Torokhov if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK)) 4661da177e4SLinus Torvalds return -EAGAIN; 4671da177e4SLinus Torvalds 4681da177e4SLinus Torvalds retval = wait_event_interruptible(udev->waitq, 46929506415SDmitry Torokhov udev->head != udev->tail || udev->state != UIST_CREATED); 4701da177e4SLinus Torvalds if (retval) 4711da177e4SLinus Torvalds return retval; 4721da177e4SLinus Torvalds 473221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex); 47429506415SDmitry Torokhov if (retval) 47529506415SDmitry Torokhov return retval; 4761da177e4SLinus Torvalds 47729506415SDmitry Torokhov if (udev->state != UIST_CREATED) { 47829506415SDmitry Torokhov retval = -ENODEV; 47929506415SDmitry Torokhov goto out; 48029506415SDmitry Torokhov } 48129506415SDmitry Torokhov 4822d56f3a3SPhilip Langdale while (udev->head != udev->tail && retval + input_event_size() <= count) { 4832d56f3a3SPhilip Langdale if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) { 48429506415SDmitry Torokhov retval = -EFAULT; 48529506415SDmitry Torokhov goto out; 48629506415SDmitry Torokhov } 4871da177e4SLinus Torvalds udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; 4882d56f3a3SPhilip Langdale retval += input_event_size(); 4891da177e4SLinus Torvalds } 4901da177e4SLinus Torvalds 49129506415SDmitry Torokhov out: 492221979aaSDmitry Torokhov mutex_unlock(&udev->mutex); 49329506415SDmitry Torokhov 4941da177e4SLinus Torvalds return retval; 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds 4971da177e4SLinus Torvalds static unsigned int uinput_poll(struct file *file, poll_table *wait) 4981da177e4SLinus Torvalds { 4991da177e4SLinus Torvalds struct uinput_device *udev = file->private_data; 5001da177e4SLinus Torvalds 5011da177e4SLinus Torvalds poll_wait(file, &udev->waitq, wait); 5021da177e4SLinus Torvalds 5031da177e4SLinus Torvalds if (udev->head != udev->tail) 5041da177e4SLinus Torvalds return POLLIN | POLLRDNORM; 5051da177e4SLinus Torvalds 5061da177e4SLinus Torvalds return 0; 5071da177e4SLinus Torvalds } 5081da177e4SLinus Torvalds 50929506415SDmitry Torokhov static int uinput_release(struct inode *inode, struct file *file) 5101da177e4SLinus Torvalds { 51129506415SDmitry Torokhov struct uinput_device *udev = file->private_data; 5121da177e4SLinus Torvalds 51329506415SDmitry Torokhov uinput_destroy_device(udev); 5141da177e4SLinus Torvalds kfree(udev); 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds return 0; 5171da177e4SLinus Torvalds } 5181da177e4SLinus Torvalds 5192d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT 5202d56f3a3SPhilip Langdale struct uinput_ff_upload_compat { 5212d56f3a3SPhilip Langdale int request_id; 5222d56f3a3SPhilip Langdale int retval; 5232d56f3a3SPhilip Langdale struct ff_effect_compat effect; 5242d56f3a3SPhilip Langdale struct ff_effect_compat old; 5252d56f3a3SPhilip Langdale }; 5262d56f3a3SPhilip Langdale 5272d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer, 5282d56f3a3SPhilip Langdale const struct uinput_ff_upload *ff_up) 5292d56f3a3SPhilip Langdale { 5302d56f3a3SPhilip Langdale if (INPUT_COMPAT_TEST) { 5312d56f3a3SPhilip Langdale struct uinput_ff_upload_compat ff_up_compat; 5322d56f3a3SPhilip Langdale 5332d56f3a3SPhilip Langdale ff_up_compat.request_id = ff_up->request_id; 5342d56f3a3SPhilip Langdale ff_up_compat.retval = ff_up->retval; 5352d56f3a3SPhilip Langdale /* 5362d56f3a3SPhilip Langdale * It so happens that the pointer that gives us the trouble 5372d56f3a3SPhilip Langdale * is the last field in the structure. Since we don't support 5382d56f3a3SPhilip Langdale * custom waveforms in uinput anyway we can just copy the whole 5392d56f3a3SPhilip Langdale * thing (to the compat size) and ignore the pointer. 5402d56f3a3SPhilip Langdale */ 5412d56f3a3SPhilip Langdale memcpy(&ff_up_compat.effect, &ff_up->effect, 5422d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat)); 5432d56f3a3SPhilip Langdale memcpy(&ff_up_compat.old, &ff_up->old, 5442d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat)); 5452d56f3a3SPhilip Langdale 5462d56f3a3SPhilip Langdale if (copy_to_user(buffer, &ff_up_compat, 5472d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload_compat))) 5482d56f3a3SPhilip Langdale return -EFAULT; 5492d56f3a3SPhilip Langdale } else { 5502d56f3a3SPhilip Langdale if (copy_to_user(buffer, ff_up, 5512d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload))) 5522d56f3a3SPhilip Langdale return -EFAULT; 5532d56f3a3SPhilip Langdale } 5542d56f3a3SPhilip Langdale 5552d56f3a3SPhilip Langdale return 0; 5562d56f3a3SPhilip Langdale } 5572d56f3a3SPhilip Langdale 5582d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer, 5592d56f3a3SPhilip Langdale struct uinput_ff_upload *ff_up) 5602d56f3a3SPhilip Langdale { 5612d56f3a3SPhilip Langdale if (INPUT_COMPAT_TEST) { 5622d56f3a3SPhilip Langdale struct uinput_ff_upload_compat ff_up_compat; 5632d56f3a3SPhilip Langdale 5642d56f3a3SPhilip Langdale if (copy_from_user(&ff_up_compat, buffer, 5652d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload_compat))) 5662d56f3a3SPhilip Langdale return -EFAULT; 5672d56f3a3SPhilip Langdale 5682d56f3a3SPhilip Langdale ff_up->request_id = ff_up_compat.request_id; 5692d56f3a3SPhilip Langdale ff_up->retval = ff_up_compat.retval; 5702d56f3a3SPhilip Langdale memcpy(&ff_up->effect, &ff_up_compat.effect, 5712d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat)); 5722d56f3a3SPhilip Langdale memcpy(&ff_up->old, &ff_up_compat.old, 5732d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat)); 5742d56f3a3SPhilip Langdale 5752d56f3a3SPhilip Langdale } else { 5762d56f3a3SPhilip Langdale if (copy_from_user(ff_up, buffer, 5772d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload))) 5782d56f3a3SPhilip Langdale return -EFAULT; 5792d56f3a3SPhilip Langdale } 5802d56f3a3SPhilip Langdale 5812d56f3a3SPhilip Langdale return 0; 5822d56f3a3SPhilip Langdale } 5832d56f3a3SPhilip Langdale 5842d56f3a3SPhilip Langdale #else 5852d56f3a3SPhilip Langdale 5862d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer, 5872d56f3a3SPhilip Langdale const struct uinput_ff_upload *ff_up) 5882d56f3a3SPhilip Langdale { 5892d56f3a3SPhilip Langdale if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload))) 5902d56f3a3SPhilip Langdale return -EFAULT; 5912d56f3a3SPhilip Langdale 5922d56f3a3SPhilip Langdale return 0; 5932d56f3a3SPhilip Langdale } 5942d56f3a3SPhilip Langdale 5952d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer, 5962d56f3a3SPhilip Langdale struct uinput_ff_upload *ff_up) 5972d56f3a3SPhilip Langdale { 5982d56f3a3SPhilip Langdale if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload))) 5992d56f3a3SPhilip Langdale return -EFAULT; 6002d56f3a3SPhilip Langdale 6012d56f3a3SPhilip Langdale return 0; 6022d56f3a3SPhilip Langdale } 6032d56f3a3SPhilip Langdale 6042d56f3a3SPhilip Langdale #endif 6052d56f3a3SPhilip Langdale 60629506415SDmitry Torokhov #define uinput_set_bit(_arg, _bit, _max) \ 60729506415SDmitry Torokhov ({ \ 60829506415SDmitry Torokhov int __ret = 0; \ 60929506415SDmitry Torokhov if (udev->state == UIST_CREATED) \ 61029506415SDmitry Torokhov __ret = -EINVAL; \ 61129506415SDmitry Torokhov else if ((_arg) > (_max)) \ 61229506415SDmitry Torokhov __ret = -EINVAL; \ 61329506415SDmitry Torokhov else set_bit((_arg), udev->dev->_bit); \ 61429506415SDmitry Torokhov __ret; \ 61529506415SDmitry Torokhov }) 6161da177e4SLinus Torvalds 6172d56f3a3SPhilip Langdale static long uinput_ioctl_handler(struct file *file, unsigned int cmd, 6182d56f3a3SPhilip Langdale unsigned long arg, void __user *p) 6191da177e4SLinus Torvalds { 62029506415SDmitry Torokhov int retval; 6212d56f3a3SPhilip Langdale struct uinput_device *udev = file->private_data; 6221da177e4SLinus Torvalds struct uinput_ff_upload ff_up; 6231da177e4SLinus Torvalds struct uinput_ff_erase ff_erase; 6241da177e4SLinus Torvalds struct uinput_request *req; 6251da177e4SLinus Torvalds int length; 6265b6271bdSDmitry Torokhov char *phys; 6271da177e4SLinus Torvalds 628221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex); 62929506415SDmitry Torokhov if (retval) 63029506415SDmitry Torokhov return retval; 63129506415SDmitry Torokhov 63229506415SDmitry Torokhov if (!udev->dev) { 63329506415SDmitry Torokhov retval = uinput_allocate_device(udev); 63429506415SDmitry Torokhov if (retval) 63529506415SDmitry Torokhov goto out; 6361da177e4SLinus Torvalds } 6371da177e4SLinus Torvalds 6381da177e4SLinus Torvalds switch (cmd) { 6391da177e4SLinus Torvalds case UI_DEV_CREATE: 6401da177e4SLinus Torvalds retval = uinput_create_device(udev); 6411da177e4SLinus Torvalds break; 6421da177e4SLinus Torvalds 6431da177e4SLinus Torvalds case UI_DEV_DESTROY: 64429506415SDmitry Torokhov uinput_destroy_device(udev); 6451da177e4SLinus Torvalds break; 6461da177e4SLinus Torvalds 6471da177e4SLinus Torvalds case UI_SET_EVBIT: 64829506415SDmitry Torokhov retval = uinput_set_bit(arg, evbit, EV_MAX); 6491da177e4SLinus Torvalds break; 6501da177e4SLinus Torvalds 6511da177e4SLinus Torvalds case UI_SET_KEYBIT: 65229506415SDmitry Torokhov retval = uinput_set_bit(arg, keybit, KEY_MAX); 6531da177e4SLinus Torvalds break; 6541da177e4SLinus Torvalds 6551da177e4SLinus Torvalds case UI_SET_RELBIT: 65629506415SDmitry Torokhov retval = uinput_set_bit(arg, relbit, REL_MAX); 6571da177e4SLinus Torvalds break; 6581da177e4SLinus Torvalds 6591da177e4SLinus Torvalds case UI_SET_ABSBIT: 66029506415SDmitry Torokhov retval = uinput_set_bit(arg, absbit, ABS_MAX); 6611da177e4SLinus Torvalds break; 6621da177e4SLinus Torvalds 6631da177e4SLinus Torvalds case UI_SET_MSCBIT: 66429506415SDmitry Torokhov retval = uinput_set_bit(arg, mscbit, MSC_MAX); 6651da177e4SLinus Torvalds break; 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds case UI_SET_LEDBIT: 66829506415SDmitry Torokhov retval = uinput_set_bit(arg, ledbit, LED_MAX); 6691da177e4SLinus Torvalds break; 6701da177e4SLinus Torvalds 6711da177e4SLinus Torvalds case UI_SET_SNDBIT: 67229506415SDmitry Torokhov retval = uinput_set_bit(arg, sndbit, SND_MAX); 6731da177e4SLinus Torvalds break; 6741da177e4SLinus Torvalds 6751da177e4SLinus Torvalds case UI_SET_FFBIT: 67629506415SDmitry Torokhov retval = uinput_set_bit(arg, ffbit, FF_MAX); 6771da177e4SLinus Torvalds break; 6781da177e4SLinus Torvalds 67959c7c037SDmitry Torokhov case UI_SET_SWBIT: 68059c7c037SDmitry Torokhov retval = uinput_set_bit(arg, swbit, SW_MAX); 68159c7c037SDmitry Torokhov break; 68259c7c037SDmitry Torokhov 68385b77200SHenrik Rydberg case UI_SET_PROPBIT: 68485b77200SHenrik Rydberg retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX); 68585b77200SHenrik Rydberg break; 68685b77200SHenrik Rydberg 6871da177e4SLinus Torvalds case UI_SET_PHYS: 68829506415SDmitry Torokhov if (udev->state == UIST_CREATED) { 68929506415SDmitry Torokhov retval = -EINVAL; 69029506415SDmitry Torokhov goto out; 69129506415SDmitry Torokhov } 6921da177e4SLinus Torvalds length = strnlen_user(p, 1024); 6931da177e4SLinus Torvalds if (length <= 0) { 6941da177e4SLinus Torvalds retval = -EFAULT; 6951da177e4SLinus Torvalds break; 6961da177e4SLinus Torvalds } 6971da177e4SLinus Torvalds kfree(udev->dev->phys); 6985b6271bdSDmitry Torokhov udev->dev->phys = phys = kmalloc(length, GFP_KERNEL); 6995b6271bdSDmitry Torokhov if (!phys) { 7001da177e4SLinus Torvalds retval = -ENOMEM; 7011da177e4SLinus Torvalds break; 7021da177e4SLinus Torvalds } 7035b6271bdSDmitry Torokhov if (copy_from_user(phys, p, length)) { 7041da177e4SLinus Torvalds udev->dev->phys = NULL; 7055b6271bdSDmitry Torokhov kfree(phys); 7065b6271bdSDmitry Torokhov retval = -EFAULT; 7071da177e4SLinus Torvalds break; 7081da177e4SLinus Torvalds } 7095b6271bdSDmitry Torokhov phys[length - 1] = '\0'; 7101da177e4SLinus Torvalds break; 7111da177e4SLinus Torvalds 7121da177e4SLinus Torvalds case UI_BEGIN_FF_UPLOAD: 7132d56f3a3SPhilip Langdale retval = uinput_ff_upload_from_user(p, &ff_up); 7142d56f3a3SPhilip Langdale if (retval) 7151da177e4SLinus Torvalds break; 7162d56f3a3SPhilip Langdale 7171da177e4SLinus Torvalds req = uinput_request_find(udev, ff_up.request_id); 7182d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) { 7191da177e4SLinus Torvalds retval = -EINVAL; 7201da177e4SLinus Torvalds break; 7211da177e4SLinus Torvalds } 7222d56f3a3SPhilip Langdale 7231da177e4SLinus Torvalds ff_up.retval = 0; 7242d56f3a3SPhilip Langdale ff_up.effect = *req->u.upload.effect; 725ff462551SAnssi Hannula if (req->u.upload.old) 7262d56f3a3SPhilip Langdale ff_up.old = *req->u.upload.old; 727ff462551SAnssi Hannula else 728ff462551SAnssi Hannula memset(&ff_up.old, 0, sizeof(struct ff_effect)); 729ff462551SAnssi Hannula 7302d56f3a3SPhilip Langdale retval = uinput_ff_upload_to_user(p, &ff_up); 7311da177e4SLinus Torvalds break; 7321da177e4SLinus Torvalds 7331da177e4SLinus Torvalds case UI_BEGIN_FF_ERASE: 7341da177e4SLinus Torvalds if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { 7351da177e4SLinus Torvalds retval = -EFAULT; 7361da177e4SLinus Torvalds break; 7371da177e4SLinus Torvalds } 7382d56f3a3SPhilip Langdale 7391da177e4SLinus Torvalds req = uinput_request_find(udev, ff_erase.request_id); 7402d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_ERASE) { 7411da177e4SLinus Torvalds retval = -EINVAL; 7421da177e4SLinus Torvalds break; 7431da177e4SLinus Torvalds } 7442d56f3a3SPhilip Langdale 7451da177e4SLinus Torvalds ff_erase.retval = 0; 7461da177e4SLinus Torvalds ff_erase.effect_id = req->u.effect_id; 7471da177e4SLinus Torvalds if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { 7481da177e4SLinus Torvalds retval = -EFAULT; 7491da177e4SLinus Torvalds break; 7501da177e4SLinus Torvalds } 7512d56f3a3SPhilip Langdale 7521da177e4SLinus Torvalds break; 7531da177e4SLinus Torvalds 7541da177e4SLinus Torvalds case UI_END_FF_UPLOAD: 7552d56f3a3SPhilip Langdale retval = uinput_ff_upload_from_user(p, &ff_up); 7562d56f3a3SPhilip Langdale if (retval) 7571da177e4SLinus Torvalds break; 7582d56f3a3SPhilip Langdale 7591da177e4SLinus Torvalds req = uinput_request_find(udev, ff_up.request_id); 7602d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_UPLOAD || 7612d56f3a3SPhilip Langdale !req->u.upload.effect) { 7621da177e4SLinus Torvalds retval = -EINVAL; 7631da177e4SLinus Torvalds break; 7641da177e4SLinus Torvalds } 7652d56f3a3SPhilip Langdale 7661da177e4SLinus Torvalds req->retval = ff_up.retval; 7670048e603SDmitry Torokhov uinput_request_done(udev, req); 7681da177e4SLinus Torvalds break; 7691da177e4SLinus Torvalds 7701da177e4SLinus Torvalds case UI_END_FF_ERASE: 7711da177e4SLinus Torvalds if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { 7721da177e4SLinus Torvalds retval = -EFAULT; 7731da177e4SLinus Torvalds break; 7741da177e4SLinus Torvalds } 7752d56f3a3SPhilip Langdale 7761da177e4SLinus Torvalds req = uinput_request_find(udev, ff_erase.request_id); 7772d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_ERASE) { 7781da177e4SLinus Torvalds retval = -EINVAL; 7791da177e4SLinus Torvalds break; 7801da177e4SLinus Torvalds } 7812d56f3a3SPhilip Langdale 7821da177e4SLinus Torvalds req->retval = ff_erase.retval; 7830048e603SDmitry Torokhov uinput_request_done(udev, req); 7841da177e4SLinus Torvalds break; 7851da177e4SLinus Torvalds 7861da177e4SLinus Torvalds default: 7871da177e4SLinus Torvalds retval = -EINVAL; 7881da177e4SLinus Torvalds } 78929506415SDmitry Torokhov 79029506415SDmitry Torokhov out: 791221979aaSDmitry Torokhov mutex_unlock(&udev->mutex); 7921da177e4SLinus Torvalds return retval; 7931da177e4SLinus Torvalds } 7941da177e4SLinus Torvalds 7952d56f3a3SPhilip Langdale static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 7962d56f3a3SPhilip Langdale { 7972d56f3a3SPhilip Langdale return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg); 7982d56f3a3SPhilip Langdale } 7992d56f3a3SPhilip Langdale 8002d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT 8012d56f3a3SPhilip Langdale static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 8022d56f3a3SPhilip Langdale { 8032d56f3a3SPhilip Langdale return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg)); 8042d56f3a3SPhilip Langdale } 8052d56f3a3SPhilip Langdale #endif 8062d56f3a3SPhilip Langdale 8072b8693c0SArjan van de Ven static const struct file_operations uinput_fops = { 8081da177e4SLinus Torvalds .owner = THIS_MODULE, 8091da177e4SLinus Torvalds .open = uinput_open, 81029506415SDmitry Torokhov .release = uinput_release, 8111da177e4SLinus Torvalds .read = uinput_read, 8121da177e4SLinus Torvalds .write = uinput_write, 8131da177e4SLinus Torvalds .poll = uinput_poll, 81429506415SDmitry Torokhov .unlocked_ioctl = uinput_ioctl, 8152d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT 8162d56f3a3SPhilip Langdale .compat_ioctl = uinput_compat_ioctl, 8172d56f3a3SPhilip Langdale #endif 8186038f373SArnd Bergmann .llseek = no_llseek, 8191da177e4SLinus Torvalds }; 8201da177e4SLinus Torvalds 8211da177e4SLinus Torvalds static struct miscdevice uinput_misc = { 8221da177e4SLinus Torvalds .fops = &uinput_fops, 8231da177e4SLinus Torvalds .minor = UINPUT_MINOR, 8241da177e4SLinus Torvalds .name = UINPUT_NAME, 8251da177e4SLinus Torvalds }; 8268905aaafSKay Sievers MODULE_ALIAS_MISCDEV(UINPUT_MINOR); 8278905aaafSKay Sievers MODULE_ALIAS("devname:" UINPUT_NAME); 8281da177e4SLinus Torvalds 8291da177e4SLinus Torvalds static int __init uinput_init(void) 8301da177e4SLinus Torvalds { 8311da177e4SLinus Torvalds return misc_register(&uinput_misc); 8321da177e4SLinus Torvalds } 8331da177e4SLinus Torvalds 8341da177e4SLinus Torvalds static void __exit uinput_exit(void) 8351da177e4SLinus Torvalds { 8361da177e4SLinus Torvalds misc_deregister(&uinput_misc); 8371da177e4SLinus Torvalds } 8381da177e4SLinus Torvalds 8391da177e4SLinus Torvalds MODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); 8401da177e4SLinus Torvalds MODULE_DESCRIPTION("User level driver support for input subsystem"); 8411da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 842ff462551SAnssi Hannula MODULE_VERSION("0.3"); 8431da177e4SLinus Torvalds 8441da177e4SLinus Torvalds module_init(uinput_init); 8451da177e4SLinus Torvalds module_exit(uinput_exit); 8461da177e4SLinus Torvalds 847