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