cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ff-core.c (8865B)


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