18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Force feedback support for Linux input subsystem
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
68c2ecf20Sopenharmony_ci *  Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/* #define DEBUG */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/input.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/mutex.h>
178c2ecf20Sopenharmony_ci#include <linux/sched.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * Check that the effect_id is a valid effect and whether the user
228c2ecf20Sopenharmony_ci * is the owner
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cistatic int check_effect_access(struct ff_device *ff, int effect_id,
258c2ecf20Sopenharmony_ci				struct file *file)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	if (effect_id < 0 || effect_id >= ff->max_effects ||
288c2ecf20Sopenharmony_ci	    !ff->effect_owners[effect_id])
298c2ecf20Sopenharmony_ci		return -EINVAL;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (file && ff->effect_owners[effect_id] != file)
328c2ecf20Sopenharmony_ci		return -EACCES;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	return 0;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/*
388c2ecf20Sopenharmony_ci * Checks whether 2 effects can be combined together
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_cistatic inline int check_effects_compatible(struct ff_effect *e1,
418c2ecf20Sopenharmony_ci					   struct ff_effect *e2)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	return e1->type == e2->type &&
448c2ecf20Sopenharmony_ci	       (e1->type != FF_PERIODIC ||
458c2ecf20Sopenharmony_ci		e1->u.periodic.waveform == e2->u.periodic.waveform);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * Convert an effect into compatible one
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_cistatic int compat_effect(struct ff_device *ff, struct ff_effect *effect)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	int magnitude;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	switch (effect->type) {
568c2ecf20Sopenharmony_ci	case FF_RUMBLE:
578c2ecf20Sopenharmony_ci		if (!test_bit(FF_PERIODIC, ff->ffbit))
588c2ecf20Sopenharmony_ci			return -EINVAL;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci		/*
618c2ecf20Sopenharmony_ci		 * calculate magnitude of sine wave as average of rumble's
628c2ecf20Sopenharmony_ci		 * 2/3 of strong magnitude and 1/3 of weak magnitude
638c2ecf20Sopenharmony_ci		 */
648c2ecf20Sopenharmony_ci		magnitude = effect->u.rumble.strong_magnitude / 3 +
658c2ecf20Sopenharmony_ci			    effect->u.rumble.weak_magnitude / 6;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		effect->type = FF_PERIODIC;
688c2ecf20Sopenharmony_ci		effect->u.periodic.waveform = FF_SINE;
698c2ecf20Sopenharmony_ci		effect->u.periodic.period = 50;
708c2ecf20Sopenharmony_ci		effect->u.periodic.magnitude = max(magnitude, 0x7fff);
718c2ecf20Sopenharmony_ci		effect->u.periodic.offset = 0;
728c2ecf20Sopenharmony_ci		effect->u.periodic.phase = 0;
738c2ecf20Sopenharmony_ci		effect->u.periodic.envelope.attack_length = 0;
748c2ecf20Sopenharmony_ci		effect->u.periodic.envelope.attack_level = 0;
758c2ecf20Sopenharmony_ci		effect->u.periodic.envelope.fade_length = 0;
768c2ecf20Sopenharmony_ci		effect->u.periodic.envelope.fade_level = 0;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		return 0;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	default:
818c2ecf20Sopenharmony_ci		/* Let driver handle conversion */
828c2ecf20Sopenharmony_ci		return 0;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/**
878c2ecf20Sopenharmony_ci * input_ff_upload() - upload effect into force-feedback device
888c2ecf20Sopenharmony_ci * @dev: input device
898c2ecf20Sopenharmony_ci * @effect: effect to be uploaded
908c2ecf20Sopenharmony_ci * @file: owner of the effect
918c2ecf20Sopenharmony_ci */
928c2ecf20Sopenharmony_ciint input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
938c2ecf20Sopenharmony_ci		    struct file *file)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct ff_device *ff = dev->ff;
968c2ecf20Sopenharmony_ci	struct ff_effect *old;
978c2ecf20Sopenharmony_ci	int ret = 0;
988c2ecf20Sopenharmony_ci	int id;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (!test_bit(EV_FF, dev->evbit))
1018c2ecf20Sopenharmony_ci		return -ENOSYS;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||
1048c2ecf20Sopenharmony_ci	    !test_bit(effect->type, dev->ffbit)) {
1058c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "invalid or not supported effect type in upload\n");
1068c2ecf20Sopenharmony_ci		return -EINVAL;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (effect->type == FF_PERIODIC &&
1108c2ecf20Sopenharmony_ci	    (effect->u.periodic.waveform < FF_WAVEFORM_MIN ||
1118c2ecf20Sopenharmony_ci	     effect->u.periodic.waveform > FF_WAVEFORM_MAX ||
1128c2ecf20Sopenharmony_ci	     !test_bit(effect->u.periodic.waveform, dev->ffbit))) {
1138c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "invalid or not supported wave form in upload\n");
1148c2ecf20Sopenharmony_ci		return -EINVAL;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (!test_bit(effect->type, ff->ffbit)) {
1188c2ecf20Sopenharmony_ci		ret = compat_effect(ff, effect);
1198c2ecf20Sopenharmony_ci		if (ret)
1208c2ecf20Sopenharmony_ci			return ret;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	mutex_lock(&ff->mutex);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (effect->id == -1) {
1268c2ecf20Sopenharmony_ci		for (id = 0; id < ff->max_effects; id++)
1278c2ecf20Sopenharmony_ci			if (!ff->effect_owners[id])
1288c2ecf20Sopenharmony_ci				break;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		if (id >= ff->max_effects) {
1318c2ecf20Sopenharmony_ci			ret = -ENOSPC;
1328c2ecf20Sopenharmony_ci			goto out;
1338c2ecf20Sopenharmony_ci		}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		effect->id = id;
1368c2ecf20Sopenharmony_ci		old = NULL;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	} else {
1398c2ecf20Sopenharmony_ci		id = effect->id;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		ret = check_effect_access(ff, id, file);
1428c2ecf20Sopenharmony_ci		if (ret)
1438c2ecf20Sopenharmony_ci			goto out;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		old = &ff->effects[id];
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci		if (!check_effects_compatible(effect, old)) {
1488c2ecf20Sopenharmony_ci			ret = -EINVAL;
1498c2ecf20Sopenharmony_ci			goto out;
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ret = ff->upload(dev, effect, old);
1548c2ecf20Sopenharmony_ci	if (ret)
1558c2ecf20Sopenharmony_ci		goto out;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->event_lock);
1588c2ecf20Sopenharmony_ci	ff->effects[id] = *effect;
1598c2ecf20Sopenharmony_ci	ff->effect_owners[id] = file;
1608c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->event_lock);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci out:
1638c2ecf20Sopenharmony_ci	mutex_unlock(&ff->mutex);
1648c2ecf20Sopenharmony_ci	return ret;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(input_ff_upload);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci/*
1698c2ecf20Sopenharmony_ci * Erases the effect if the requester is also the effect owner. The mutex
1708c2ecf20Sopenharmony_ci * should already be locked before calling this function.
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_cistatic int erase_effect(struct input_dev *dev, int effect_id,
1738c2ecf20Sopenharmony_ci			struct file *file)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct ff_device *ff = dev->ff;
1768c2ecf20Sopenharmony_ci	int error;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	error = check_effect_access(ff, effect_id, file);
1798c2ecf20Sopenharmony_ci	if (error)
1808c2ecf20Sopenharmony_ci		return error;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->event_lock);
1838c2ecf20Sopenharmony_ci	ff->playback(dev, effect_id, 0);
1848c2ecf20Sopenharmony_ci	ff->effect_owners[effect_id] = NULL;
1858c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->event_lock);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (ff->erase) {
1888c2ecf20Sopenharmony_ci		error = ff->erase(dev, effect_id);
1898c2ecf20Sopenharmony_ci		if (error) {
1908c2ecf20Sopenharmony_ci			spin_lock_irq(&dev->event_lock);
1918c2ecf20Sopenharmony_ci			ff->effect_owners[effect_id] = file;
1928c2ecf20Sopenharmony_ci			spin_unlock_irq(&dev->event_lock);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci			return error;
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return 0;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/**
2028c2ecf20Sopenharmony_ci * input_ff_erase - erase a force-feedback effect from device
2038c2ecf20Sopenharmony_ci * @dev: input device to erase effect from
2048c2ecf20Sopenharmony_ci * @effect_id: id of the effect to be erased
2058c2ecf20Sopenharmony_ci * @file: purported owner of the request
2068c2ecf20Sopenharmony_ci *
2078c2ecf20Sopenharmony_ci * This function erases a force-feedback effect from specified device.
2088c2ecf20Sopenharmony_ci * The effect will only be erased if it was uploaded through the same
2098c2ecf20Sopenharmony_ci * file handle that is requesting erase.
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_ciint input_ff_erase(struct input_dev *dev, int effect_id, struct file *file)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct ff_device *ff = dev->ff;
2148c2ecf20Sopenharmony_ci	int ret;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (!test_bit(EV_FF, dev->evbit))
2178c2ecf20Sopenharmony_ci		return -ENOSYS;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	mutex_lock(&ff->mutex);
2208c2ecf20Sopenharmony_ci	ret = erase_effect(dev, effect_id, file);
2218c2ecf20Sopenharmony_ci	mutex_unlock(&ff->mutex);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return ret;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(input_ff_erase);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/*
2288c2ecf20Sopenharmony_ci * input_ff_flush - erase all effects owned by a file handle
2298c2ecf20Sopenharmony_ci * @dev: input device to erase effect from
2308c2ecf20Sopenharmony_ci * @file: purported owner of the effects
2318c2ecf20Sopenharmony_ci *
2328c2ecf20Sopenharmony_ci * This function erases all force-feedback effects associated with
2338c2ecf20Sopenharmony_ci * the given owner from specified device. Note that @file may be %NULL,
2348c2ecf20Sopenharmony_ci * in which case all effects will be erased.
2358c2ecf20Sopenharmony_ci */
2368c2ecf20Sopenharmony_ciint input_ff_flush(struct input_dev *dev, struct file *file)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct ff_device *ff = dev->ff;
2398c2ecf20Sopenharmony_ci	int i;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "flushing now\n");
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	mutex_lock(&ff->mutex);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	for (i = 0; i < ff->max_effects; i++)
2468c2ecf20Sopenharmony_ci		erase_effect(dev, i, file);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	mutex_unlock(&ff->mutex);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(input_ff_flush);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/**
2558c2ecf20Sopenharmony_ci * input_ff_event() - generic handler for force-feedback events
2568c2ecf20Sopenharmony_ci * @dev: input device to send the effect to
2578c2ecf20Sopenharmony_ci * @type: event type (anything but EV_FF is ignored)
2588c2ecf20Sopenharmony_ci * @code: event code
2598c2ecf20Sopenharmony_ci * @value: event value
2608c2ecf20Sopenharmony_ci */
2618c2ecf20Sopenharmony_ciint input_ff_event(struct input_dev *dev, unsigned int type,
2628c2ecf20Sopenharmony_ci		   unsigned int code, int value)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct ff_device *ff = dev->ff;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (type != EV_FF)
2678c2ecf20Sopenharmony_ci		return 0;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	switch (code) {
2708c2ecf20Sopenharmony_ci	case FF_GAIN:
2718c2ecf20Sopenharmony_ci		if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffffU)
2728c2ecf20Sopenharmony_ci			break;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		ff->set_gain(dev, value);
2758c2ecf20Sopenharmony_ci		break;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	case FF_AUTOCENTER:
2788c2ecf20Sopenharmony_ci		if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffffU)
2798c2ecf20Sopenharmony_ci			break;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		ff->set_autocenter(dev, value);
2828c2ecf20Sopenharmony_ci		break;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	default:
2858c2ecf20Sopenharmony_ci		if (check_effect_access(ff, code, NULL) == 0)
2868c2ecf20Sopenharmony_ci			ff->playback(dev, code, value);
2878c2ecf20Sopenharmony_ci		break;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return 0;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(input_ff_event);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci/**
2958c2ecf20Sopenharmony_ci * input_ff_create() - create force-feedback device
2968c2ecf20Sopenharmony_ci * @dev: input device supporting force-feedback
2978c2ecf20Sopenharmony_ci * @max_effects: maximum number of effects supported by the device
2988c2ecf20Sopenharmony_ci *
2998c2ecf20Sopenharmony_ci * This function allocates all necessary memory for a force feedback
3008c2ecf20Sopenharmony_ci * portion of an input device and installs all default handlers.
3018c2ecf20Sopenharmony_ci * @dev->ffbit should be already set up before calling this function.
3028c2ecf20Sopenharmony_ci * Once ff device is created you need to setup its upload, erase,
3038c2ecf20Sopenharmony_ci * playback and other handlers before registering input device
3048c2ecf20Sopenharmony_ci */
3058c2ecf20Sopenharmony_ciint input_ff_create(struct input_dev *dev, unsigned int max_effects)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct ff_device *ff;
3088c2ecf20Sopenharmony_ci	size_t ff_dev_size;
3098c2ecf20Sopenharmony_ci	int i;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (!max_effects) {
3128c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "cannot allocate device without any effects\n");
3138c2ecf20Sopenharmony_ci		return -EINVAL;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (max_effects > FF_MAX_EFFECTS) {
3178c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "cannot allocate more than FF_MAX_EFFECTS effects\n");
3188c2ecf20Sopenharmony_ci		return -EINVAL;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	ff_dev_size = sizeof(struct ff_device) +
3228c2ecf20Sopenharmony_ci				max_effects * sizeof(struct file *);
3238c2ecf20Sopenharmony_ci	if (ff_dev_size < max_effects) /* overflow */
3248c2ecf20Sopenharmony_ci		return -EINVAL;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	ff = kzalloc(ff_dev_size, GFP_KERNEL);
3278c2ecf20Sopenharmony_ci	if (!ff)
3288c2ecf20Sopenharmony_ci		return -ENOMEM;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ff->effects = kcalloc(max_effects, sizeof(struct ff_effect),
3318c2ecf20Sopenharmony_ci			      GFP_KERNEL);
3328c2ecf20Sopenharmony_ci	if (!ff->effects) {
3338c2ecf20Sopenharmony_ci		kfree(ff);
3348c2ecf20Sopenharmony_ci		return -ENOMEM;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	ff->max_effects = max_effects;
3388c2ecf20Sopenharmony_ci	mutex_init(&ff->mutex);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	dev->ff = ff;
3418c2ecf20Sopenharmony_ci	dev->flush = input_ff_flush;
3428c2ecf20Sopenharmony_ci	dev->event = input_ff_event;
3438c2ecf20Sopenharmony_ci	__set_bit(EV_FF, dev->evbit);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* Copy "true" bits into ff device bitmap */
3468c2ecf20Sopenharmony_ci	for_each_set_bit(i, dev->ffbit, FF_CNT)
3478c2ecf20Sopenharmony_ci		__set_bit(i, ff->ffbit);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* we can emulate RUMBLE with periodic effects */
3508c2ecf20Sopenharmony_ci	if (test_bit(FF_PERIODIC, ff->ffbit))
3518c2ecf20Sopenharmony_ci		__set_bit(FF_RUMBLE, dev->ffbit);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	return 0;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(input_ff_create);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci/**
3588c2ecf20Sopenharmony_ci * input_ff_destroy() - frees force feedback portion of input device
3598c2ecf20Sopenharmony_ci * @dev: input device supporting force feedback
3608c2ecf20Sopenharmony_ci *
3618c2ecf20Sopenharmony_ci * This function is only needed in error path as input core will
3628c2ecf20Sopenharmony_ci * automatically free force feedback structures when device is
3638c2ecf20Sopenharmony_ci * destroyed.
3648c2ecf20Sopenharmony_ci */
3658c2ecf20Sopenharmony_civoid input_ff_destroy(struct input_dev *dev)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct ff_device *ff = dev->ff;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	__clear_bit(EV_FF, dev->evbit);
3708c2ecf20Sopenharmony_ci	if (ff) {
3718c2ecf20Sopenharmony_ci		if (ff->destroy)
3728c2ecf20Sopenharmony_ci			ff->destroy(ff);
3738c2ecf20Sopenharmony_ci		kfree(ff->private);
3748c2ecf20Sopenharmony_ci		kfree(ff->effects);
3758c2ecf20Sopenharmony_ci		kfree(ff);
3768c2ecf20Sopenharmony_ci		dev->ff = NULL;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(input_ff_destroy);
380