934 lines · 21.3 KB · C
RawDownload
/* *  User level driver support for input subsystem * * Heavily based on evdev.c by Vojtech Pavlik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> * * Changes/Revisions: *	0.4	01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>) *		- add UI_GET_SYSNAME ioctl *	0.3	09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>) *		- updated ff support for the changes in kernel interface *		- added MODULE_VERSION *	0.2	16/10/2004 (Micah Dowty <micah@navi.cx>) *		- added force feedback support *              - added UI_SET_PHYS *	0.1	20/06/2002 *		- first public version */#include <linux/poll.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/miscdevice.h>#include <linux/uinput.h>#include <linux/input/mt.h>#include "../input-compat.h" static int uinput_dev_event(struct input_dev *dev,			    unsigned int type, unsigned int code, int value){	struct uinput_device	*udev = input_get_drvdata(dev); 	udev->buff[udev->head].type = type;	udev->buff[udev->head].code = code;	udev->buff[udev->head].value = value;	do_gettimeofday(&udev->buff[udev->head].time);	udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE; 	wake_up_interruptible(&udev->waitq); 	return 0;} /* Atomically allocate an ID for the given request. Returns 0 on success. */static bool uinput_request_alloc_id(struct uinput_device *udev,				    struct uinput_request *request){	unsigned int id;	bool reserved = false; 	spin_lock(&udev->requests_lock); 	for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {		if (!udev->requests[id]) {			request->id = id;			udev->requests[id] = request;			reserved = true;			break;		}	} 	spin_unlock(&udev->requests_lock);	return reserved;} static struct uinput_request *uinput_request_find(struct uinput_device *udev,						  unsigned int id){	/* Find an input request, by ID. Returns NULL if the ID isn't valid. */	if (id >= UINPUT_NUM_REQUESTS)		return NULL; 	return udev->requests[id];} static int uinput_request_reserve_slot(struct uinput_device *udev,				       struct uinput_request *request){	/* Allocate slot. If none are available right away, wait. */	return wait_event_interruptible(udev->requests_waitq,					uinput_request_alloc_id(udev, request));} static void uinput_request_done(struct uinput_device *udev,				struct uinput_request *request){	/* Mark slot as available */	udev->requests[request->id] = NULL;	wake_up(&udev->requests_waitq); 	complete(&request->done);} static int uinput_request_send(struct uinput_device *udev,			       struct uinput_request *request){	int retval; 	retval = mutex_lock_interruptible(&udev->mutex);	if (retval)		return retval; 	if (udev->state != UIST_CREATED) {		retval = -ENODEV;		goto out;	} 	init_completion(&request->done); 	/*	 * Tell our userspace application about this new request	 * by queueing an input event.	 */	uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);  out:	mutex_unlock(&udev->mutex);	return retval;} static int uinput_request_submit(struct uinput_device *udev,				 struct uinput_request *request){	int error; 	error = uinput_request_reserve_slot(udev, request);	if (error)		return error; 	error = uinput_request_send(udev, request);	if (error) {		uinput_request_done(udev, request);		return error;	} 	wait_for_completion(&request->done);	return request->retval;} /* * Fail all outstanding requests so handlers don't wait for the userspace * to finish processing them. */static void uinput_flush_requests(struct uinput_device *udev){	struct uinput_request *request;	int i; 	spin_lock(&udev->requests_lock); 	for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {		request = udev->requests[i];		if (request) {			request->retval = -ENODEV;			uinput_request_done(udev, request);		}	} 	spin_unlock(&udev->requests_lock);} static void uinput_dev_set_gain(struct input_dev *dev, u16 gain){	uinput_dev_event(dev, EV_FF, FF_GAIN, gain);} static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude){	uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);} static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value){	return uinput_dev_event(dev, EV_FF, effect_id, value);} static int uinput_dev_upload_effect(struct input_dev *dev,				    struct ff_effect *effect,				    struct ff_effect *old){	struct uinput_device *udev = input_get_drvdata(dev);	struct uinput_request request; 	/*	 * uinput driver does not currently support periodic effects with	 * custom waveform since it does not have a way to pass buffer of	 * samples (custom_data) to userspace. If ever there is a device	 * supporting custom waveforms we would need to define an additional	 * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.	 */	if (effect->type == FF_PERIODIC &&			effect->u.periodic.waveform == FF_CUSTOM)		return -EINVAL; 	request.code = UI_FF_UPLOAD;	request.u.upload.effect = effect;	request.u.upload.old = old; 	return uinput_request_submit(udev, &request);} static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id){	struct uinput_device *udev = input_get_drvdata(dev);	struct uinput_request request; 	if (!test_bit(EV_FF, dev->evbit))		return -ENOSYS; 	request.code = UI_FF_ERASE;	request.u.effect_id = effect_id; 	return uinput_request_submit(udev, &request);} static void uinput_destroy_device(struct uinput_device *udev){	const char *name, *phys;	struct input_dev *dev = udev->dev;	enum uinput_state old_state = udev->state; 	udev->state = UIST_NEW_DEVICE; 	if (dev) {		name = dev->name;		phys = dev->phys;		if (old_state == UIST_CREATED) {			uinput_flush_requests(udev);			input_unregister_device(dev);		} else {			input_free_device(dev);		}		kfree(name);		kfree(phys);		udev->dev = NULL;	}} static int uinput_create_device(struct uinput_device *udev){	struct input_dev *dev = udev->dev;	int error; 	if (udev->state != UIST_SETUP_COMPLETE) {		printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);		return -EINVAL;	} 	if (udev->ff_effects_max) {		error = input_ff_create(dev, udev->ff_effects_max);		if (error)			goto fail1; 		dev->ff->upload = uinput_dev_upload_effect;		dev->ff->erase = uinput_dev_erase_effect;		dev->ff->playback = uinput_dev_playback;		dev->ff->set_gain = uinput_dev_set_gain;		dev->ff->set_autocenter = uinput_dev_set_autocenter;	} 	error = input_register_device(udev->dev);	if (error)		goto fail2; 	udev->state = UIST_CREATED; 	return 0;  fail2:	input_ff_destroy(dev); fail1: uinput_destroy_device(udev);	return error;} static int uinput_open(struct inode *inode, struct file *file){	struct uinput_device *newdev; 	newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);	if (!newdev)		return -ENOMEM; 	mutex_init(&newdev->mutex);	spin_lock_init(&newdev->requests_lock);	init_waitqueue_head(&newdev->requests_waitq);	init_waitqueue_head(&newdev->waitq);	newdev->state = UIST_NEW_DEVICE; 	file->private_data = newdev;	nonseekable_open(inode, file); 	return 0;} static int uinput_validate_absbits(struct input_dev *dev){	unsigned int cnt;	int retval = 0; 	for (cnt = 0; cnt < ABS_CNT; cnt++) {		int min, max;		if (!test_bit(cnt, dev->absbit))			continue; 		min = input_abs_get_min(dev, cnt);		max = input_abs_get_max(dev, cnt); 		if ((min != 0 || max != 0) && max <= min) {			printk(KERN_DEBUG				"%s: invalid abs[%02x] min:%d max:%d\n",				UINPUT_NAME, cnt,				input_abs_get_min(dev, cnt),				input_abs_get_max(dev, cnt));			retval = -EINVAL;			break;		} 		if (input_abs_get_flat(dev, cnt) >		    input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) {			printk(KERN_DEBUG				"%s: abs_flat #%02x out of range: %d "				"(min:%d/max:%d)\n",				UINPUT_NAME, cnt,				input_abs_get_flat(dev, cnt),				input_abs_get_min(dev, cnt),				input_abs_get_max(dev, cnt));			retval = -EINVAL;			break;		}	}	return retval;} static int uinput_allocate_device(struct uinput_device *udev){	udev->dev = input_allocate_device();	if (!udev->dev)		return -ENOMEM; 	udev->dev->event = uinput_dev_event;	input_set_drvdata(udev->dev, udev); 	return 0;} static int uinput_setup_device(struct uinput_device *udev,			       const char __user *buffer, size_t count){	struct uinput_user_dev	*user_dev;	struct input_dev	*dev;	int			i;	int			retval; 	if (count != sizeof(struct uinput_user_dev))		return -EINVAL; 	if (!udev->dev) {		retval = uinput_allocate_device(udev);		if (retval)			return retval;	} 	dev = udev->dev; 	user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));	if (IS_ERR(user_dev))		return PTR_ERR(user_dev); 	udev->ff_effects_max = user_dev->ff_effects_max; 	/* Ensure name is filled in */	if (!user_dev->name[0]) {		retval = -EINVAL;		goto exit;	} 	kfree(dev->name);	dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,			     GFP_KERNEL);	if (!dev->name) {		retval = -ENOMEM;		goto exit;	} 	dev->id.bustype	= user_dev->id.bustype;	dev->id.vendor	= user_dev->id.vendor;	dev->id.product	= user_dev->id.product;	dev->id.version	= user_dev->id.version; 	for (i = 0; i < ABS_CNT; i++) {		input_abs_set_max(dev, i, user_dev->absmax[i]);		input_abs_set_min(dev, i, user_dev->absmin[i]);		input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);		input_abs_set_flat(dev, i, user_dev->absflat[i]);	} 	/* check if absmin/absmax/absfuzz/absflat are filled as	 * told in Documentation/input/input-programming.txt */	if (test_bit(EV_ABS, dev->evbit)) {		retval = uinput_validate_absbits(dev);		if (retval < 0)			goto exit;		if (test_bit(ABS_MT_SLOT, dev->absbit)) {			int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;			input_mt_init_slots(dev, nslot, 0);		} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {			input_set_events_per_packet(dev, 60);		}	} 	udev->state = UIST_SETUP_COMPLETE;	retval = count;  exit:	kfree(user_dev);	return retval;} static ssize_t uinput_inject_events(struct uinput_device *udev,				    const char __user *buffer, size_t count){	struct input_event ev;	size_t bytes = 0; 	if (count != 0 && count < input_event_size())		return -EINVAL; 	while (bytes + input_event_size() <= count) {		/*		 * Note that even if some events were fetched successfully		 * we are still going to return EFAULT instead of partial		 * count to let userspace know that it got it's buffers		 * all wrong.		 */		if (input_event_from_user(buffer + bytes, &ev))			return -EFAULT; 		input_event(udev->dev, ev.type, ev.code, ev.value);		bytes += input_event_size();	} 	return bytes;} static ssize_t uinput_write(struct file *file, const char __user *buffer,			    size_t count, loff_t *ppos){	struct uinput_device *udev = file->private_data;	int retval; 	if (count == 0)		return 0; 	retval = mutex_lock_interruptible(&udev->mutex);	if (retval)		return retval; 	retval = udev->state == UIST_CREATED ?			uinput_inject_events(udev, buffer, count) :			uinput_setup_device(udev, buffer, count); 	mutex_unlock(&udev->mutex); 	return retval;} static bool uinput_fetch_next_event(struct uinput_device *udev,				    struct input_event *event){	bool have_event; 	spin_lock_irq(&udev->dev->event_lock); 	have_event = udev->head != udev->tail;	if (have_event) {		*event = udev->buff[udev->tail];		udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;	} 	spin_unlock_irq(&udev->dev->event_lock); 	return have_event;} 

Lines 1–500 of 934.