brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · 877e3b8 Raw
378 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  Force feedback support for Linux input subsystem4 *5 *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>6 *  Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>7 */8 9/* #define DEBUG */10 11#include <linux/input.h>12#include <linux/limits.h>13#include <linux/module.h>14#include <linux/mutex.h>15#include <linux/overflow.h>16#include <linux/sched.h>17#include <linux/slab.h>18 19/*20 * Check that the effect_id is a valid effect and whether the user21 * is the owner22 */23static int check_effect_access(struct ff_device *ff, int effect_id,24				struct file *file)25{26	if (effect_id < 0 || effect_id >= ff->max_effects ||27	    !ff->effect_owners[effect_id])28		return -EINVAL;29 30	if (file && ff->effect_owners[effect_id] != file)31		return -EACCES;32 33	return 0;34}35 36/*37 * Checks whether 2 effects can be combined together38 */39static inline int check_effects_compatible(struct ff_effect *e1,40					   struct ff_effect *e2)41{42	return e1->type == e2->type &&43	       (e1->type != FF_PERIODIC ||44		e1->u.periodic.waveform == e2->u.periodic.waveform);45}46 47/*48 * Convert an effect into compatible one49 */50static int compat_effect(struct ff_device *ff, struct ff_effect *effect)51{52	int magnitude;53 54	switch (effect->type) {55	case FF_RUMBLE:56		if (!test_bit(FF_PERIODIC, ff->ffbit))57			return -EINVAL;58 59		/*60		 * calculate magnitude of sine wave as average of rumble's61		 * 2/3 of strong magnitude and 1/3 of weak magnitude62		 */63		magnitude = effect->u.rumble.strong_magnitude / 3 +64			    effect->u.rumble.weak_magnitude / 6;65 66		effect->type = FF_PERIODIC;67		effect->u.periodic.waveform = FF_SINE;68		effect->u.periodic.period = 50;69		effect->u.periodic.magnitude = magnitude;70		effect->u.periodic.offset = 0;71		effect->u.periodic.phase = 0;72		effect->u.periodic.envelope.attack_length = 0;73		effect->u.periodic.envelope.attack_level = 0;74		effect->u.periodic.envelope.fade_length = 0;75		effect->u.periodic.envelope.fade_level = 0;76 77		return 0;78 79	default:80		/* Let driver handle conversion */81		return 0;82	}83}84 85/**86 * input_ff_upload() - upload effect into force-feedback device87 * @dev: input device88 * @effect: effect to be uploaded89 * @file: owner of the effect90 */91int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,92		    struct file *file)93{94	struct ff_device *ff = dev->ff;95	struct ff_effect *old;96	int ret = 0;97	int id;98 99	if (!test_bit(EV_FF, dev->evbit))100		return -ENOSYS;101 102	if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||103	    !test_bit(effect->type, dev->ffbit)) {104		dev_dbg(&dev->dev, "invalid or not supported effect type in upload\n");105		return -EINVAL;106	}107 108	if (effect->type == FF_PERIODIC &&109	    (effect->u.periodic.waveform < FF_WAVEFORM_MIN ||110	     effect->u.periodic.waveform > FF_WAVEFORM_MAX ||111	     !test_bit(effect->u.periodic.waveform, dev->ffbit))) {112		dev_dbg(&dev->dev, "invalid or not supported wave form in upload\n");113		return -EINVAL;114	}115 116	if (!test_bit(effect->type, ff->ffbit)) {117		ret = compat_effect(ff, effect);118		if (ret)119			return ret;120	}121 122	mutex_lock(&ff->mutex);123 124	if (effect->id == -1) {125		for (id = 0; id < ff->max_effects; id++)126			if (!ff->effect_owners[id])127				break;128 129		if (id >= ff->max_effects) {130			ret = -ENOSPC;131			goto out;132		}133 134		effect->id = id;135		old = NULL;136 137	} else {138		id = effect->id;139 140		ret = check_effect_access(ff, id, file);141		if (ret)142			goto out;143 144		old = &ff->effects[id];145 146		if (!check_effects_compatible(effect, old)) {147			ret = -EINVAL;148			goto out;149		}150	}151 152	ret = ff->upload(dev, effect, old);153	if (ret)154		goto out;155 156	spin_lock_irq(&dev->event_lock);157	ff->effects[id] = *effect;158	ff->effect_owners[id] = file;159	spin_unlock_irq(&dev->event_lock);160 161 out:162	mutex_unlock(&ff->mutex);163	return ret;164}165EXPORT_SYMBOL_GPL(input_ff_upload);166 167/*168 * Erases the effect if the requester is also the effect owner. The mutex169 * should already be locked before calling this function.170 */171static HWJS_SUSPENDS int erase_effect(struct input_dev *dev, int effect_id,172			struct file *file)173{174	struct ff_device *ff = dev->ff;175	int error;176 177	error = check_effect_access(ff, effect_id, file);178	if (error)179		return error;180 181	spin_lock_irq(&dev->event_lock);182	ff->playback(dev, effect_id, 0);183	ff->effect_owners[effect_id] = NULL;184	spin_unlock_irq(&dev->event_lock);185 186	if (ff->erase) {187		error = ff->erase(dev, effect_id);188		if (error) {189			spin_lock_irq(&dev->event_lock);190			ff->effect_owners[effect_id] = file;191			spin_unlock_irq(&dev->event_lock);192 193			return error;194		}195	}196 197	return 0;198}199 200/**201 * input_ff_erase - erase a force-feedback effect from device202 * @dev: input device to erase effect from203 * @effect_id: id of the effect to be erased204 * @file: purported owner of the request205 *206 * This function erases a force-feedback effect from specified device.207 * The effect will only be erased if it was uploaded through the same208 * file handle that is requesting erase.209 */210int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file)211{212	struct ff_device *ff = dev->ff;213	int ret;214 215	if (!test_bit(EV_FF, dev->evbit))216		return -ENOSYS;217 218	mutex_lock(&ff->mutex);219	ret = erase_effect(dev, effect_id, file);220	mutex_unlock(&ff->mutex);221 222	return ret;223}224EXPORT_SYMBOL_GPL(input_ff_erase);225 226/*227 * input_ff_flush - erase all effects owned by a file handle228 * @dev: input device to erase effect from229 * @file: purported owner of the effects230 *231 * This function erases all force-feedback effects associated with232 * the given owner from specified device. Note that @file may be %NULL,233 * in which case all effects will be erased.234 */235int input_ff_flush(struct input_dev *dev, struct file *file)236{237	struct ff_device *ff = dev->ff;238	int i;239 240	dev_dbg(&dev->dev, "flushing now\n");241 242	mutex_lock(&ff->mutex);243 244	for (i = 0; i < ff->max_effects; i++)245		erase_effect(dev, i, file);246 247	mutex_unlock(&ff->mutex);248 249	return 0;250}251EXPORT_SYMBOL_GPL(input_ff_flush);252 253/**254 * input_ff_event() - generic handler for force-feedback events255 * @dev: input device to send the effect to256 * @type: event type (anything but EV_FF is ignored)257 * @code: event code258 * @value: event value259 */260int input_ff_event(struct input_dev *dev, unsigned int type,261		   unsigned int code, int value)262{263	struct ff_device *ff = dev->ff;264 265	if (type != EV_FF)266		return 0;267 268	switch (code) {269	case FF_GAIN:270		if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffffU)271			break;272 273		ff->set_gain(dev, value);274		break;275 276	case FF_AUTOCENTER:277		if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffffU)278			break;279 280		ff->set_autocenter(dev, value);281		break;282 283	default:284		if (check_effect_access(ff, code, NULL) == 0)285			ff->playback(dev, code, value);286		break;287	}288 289	return 0;290}291EXPORT_SYMBOL_GPL(input_ff_event);292 293/**294 * input_ff_create() - create force-feedback device295 * @dev: input device supporting force-feedback296 * @max_effects: maximum number of effects supported by the device297 *298 * This function allocates all necessary memory for a force feedback299 * portion of an input device and installs all default handlers.300 * @dev->ffbit should be already set up before calling this function.301 * Once ff device is created you need to setup its upload, erase,302 * playback and other handlers before registering input device303 */304int input_ff_create(struct input_dev *dev, unsigned int max_effects)305{306	struct ff_device *ff;307	size_t ff_dev_size;308	int i;309 310	if (!max_effects) {311		dev_err(&dev->dev, "cannot allocate device without any effects\n");312		return -EINVAL;313	}314 315	if (max_effects > FF_MAX_EFFECTS) {316		dev_err(&dev->dev, "cannot allocate more than FF_MAX_EFFECTS effects\n");317		return -EINVAL;318	}319 320	ff_dev_size = struct_size(ff, effect_owners, max_effects);321	if (ff_dev_size == SIZE_MAX) /* overflow */322		return -EINVAL;323 324	ff = kzalloc(ff_dev_size, GFP_KERNEL);325	if (!ff)326		return -ENOMEM;327 328	ff->effects = kcalloc(max_effects, sizeof(struct ff_effect),329			      GFP_KERNEL);330	if (!ff->effects) {331		kfree(ff);332		return -ENOMEM;333	}334 335	ff->max_effects = max_effects;336	mutex_init(&ff->mutex);337 338	dev->ff = ff;339	dev->flush = input_ff_flush;340	dev->event = input_ff_event;341	__set_bit(EV_FF, dev->evbit);342 343	/* Copy "true" bits into ff device bitmap */344	for_each_set_bit(i, dev->ffbit, FF_CNT)345		__set_bit(i, ff->ffbit);346 347	/* we can emulate RUMBLE with periodic effects */348	if (test_bit(FF_PERIODIC, ff->ffbit))349		__set_bit(FF_RUMBLE, dev->ffbit);350 351	return 0;352}353EXPORT_SYMBOL_GPL(input_ff_create);354 355/**356 * input_ff_destroy() - frees force feedback portion of input device357 * @dev: input device supporting force feedback358 *359 * This function is only needed in error path as input core will360 * automatically free force feedback structures when device is361 * destroyed.362 */363void input_ff_destroy(struct input_dev *dev)364{365	struct ff_device *ff = dev->ff;366 367	__clear_bit(EV_FF, dev->evbit);368	if (ff) {369		if (ff->destroy)370			ff->destroy(ff);371		kfree(ff->private);372		kfree(ff->effects);373		kfree(ff);374		dev->ff = NULL;375	}376}377EXPORT_SYMBOL_GPL(input_ff_destroy);378