18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Force feedback driver for USB HID PID compliant devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* #define DEBUG */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/input.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/usb.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/hid.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "usbhid.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define PID_EFFECTS_MAX 64 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Report usage table used to put reports into an array */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define PID_SET_EFFECT 0 288c2ecf20Sopenharmony_ci#define PID_EFFECT_OPERATION 1 298c2ecf20Sopenharmony_ci#define PID_DEVICE_GAIN 2 308c2ecf20Sopenharmony_ci#define PID_POOL 3 318c2ecf20Sopenharmony_ci#define PID_BLOCK_LOAD 4 328c2ecf20Sopenharmony_ci#define PID_BLOCK_FREE 5 338c2ecf20Sopenharmony_ci#define PID_DEVICE_CONTROL 6 348c2ecf20Sopenharmony_ci#define PID_CREATE_NEW_EFFECT 7 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define PID_REQUIRED_REPORTS 7 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define PID_SET_ENVELOPE 8 398c2ecf20Sopenharmony_ci#define PID_SET_CONDITION 9 408c2ecf20Sopenharmony_ci#define PID_SET_PERIODIC 10 418c2ecf20Sopenharmony_ci#define PID_SET_CONSTANT 11 428c2ecf20Sopenharmony_ci#define PID_SET_RAMP 12 438c2ecf20Sopenharmony_cistatic const u8 pidff_reports[] = { 448c2ecf20Sopenharmony_ci 0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab, 458c2ecf20Sopenharmony_ci 0x5a, 0x5f, 0x6e, 0x73, 0x74 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* device_control is really 0x95, but 0x96 specified as it is the usage of 498c2ecf20Sopenharmony_cithe only field in that report */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Value usage tables used to put fields and values into arrays */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define PID_EFFECT_BLOCK_INDEX 0 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define PID_DURATION 1 568c2ecf20Sopenharmony_ci#define PID_GAIN 2 578c2ecf20Sopenharmony_ci#define PID_TRIGGER_BUTTON 3 588c2ecf20Sopenharmony_ci#define PID_TRIGGER_REPEAT_INT 4 598c2ecf20Sopenharmony_ci#define PID_DIRECTION_ENABLE 5 608c2ecf20Sopenharmony_ci#define PID_START_DELAY 6 618c2ecf20Sopenharmony_cistatic const u8 pidff_set_effect[] = { 628c2ecf20Sopenharmony_ci 0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define PID_ATTACK_LEVEL 1 668c2ecf20Sopenharmony_ci#define PID_ATTACK_TIME 2 678c2ecf20Sopenharmony_ci#define PID_FADE_LEVEL 3 688c2ecf20Sopenharmony_ci#define PID_FADE_TIME 4 698c2ecf20Sopenharmony_cistatic const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e }; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define PID_PARAM_BLOCK_OFFSET 1 728c2ecf20Sopenharmony_ci#define PID_CP_OFFSET 2 738c2ecf20Sopenharmony_ci#define PID_POS_COEFFICIENT 3 748c2ecf20Sopenharmony_ci#define PID_NEG_COEFFICIENT 4 758c2ecf20Sopenharmony_ci#define PID_POS_SATURATION 5 768c2ecf20Sopenharmony_ci#define PID_NEG_SATURATION 6 778c2ecf20Sopenharmony_ci#define PID_DEAD_BAND 7 788c2ecf20Sopenharmony_cistatic const u8 pidff_set_condition[] = { 798c2ecf20Sopenharmony_ci 0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define PID_MAGNITUDE 1 838c2ecf20Sopenharmony_ci#define PID_OFFSET 2 848c2ecf20Sopenharmony_ci#define PID_PHASE 3 858c2ecf20Sopenharmony_ci#define PID_PERIOD 4 868c2ecf20Sopenharmony_cistatic const u8 pidff_set_periodic[] = { 0x22, 0x70, 0x6f, 0x71, 0x72 }; 878c2ecf20Sopenharmony_cistatic const u8 pidff_set_constant[] = { 0x22, 0x70 }; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define PID_RAMP_START 1 908c2ecf20Sopenharmony_ci#define PID_RAMP_END 2 918c2ecf20Sopenharmony_cistatic const u8 pidff_set_ramp[] = { 0x22, 0x75, 0x76 }; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define PID_RAM_POOL_AVAILABLE 1 948c2ecf20Sopenharmony_cistatic const u8 pidff_block_load[] = { 0x22, 0xac }; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define PID_LOOP_COUNT 1 978c2ecf20Sopenharmony_cistatic const u8 pidff_effect_operation[] = { 0x22, 0x7c }; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const u8 pidff_block_free[] = { 0x22 }; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define PID_DEVICE_GAIN_FIELD 0 1028c2ecf20Sopenharmony_cistatic const u8 pidff_device_gain[] = { 0x7e }; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define PID_RAM_POOL_SIZE 0 1058c2ecf20Sopenharmony_ci#define PID_SIMULTANEOUS_MAX 1 1068c2ecf20Sopenharmony_ci#define PID_DEVICE_MANAGED_POOL 2 1078c2ecf20Sopenharmony_cistatic const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 }; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* Special field key tables used to put special field keys into arrays */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define PID_ENABLE_ACTUATORS 0 1128c2ecf20Sopenharmony_ci#define PID_RESET 1 1138c2ecf20Sopenharmony_cistatic const u8 pidff_device_control[] = { 0x97, 0x9a }; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define PID_CONSTANT 0 1168c2ecf20Sopenharmony_ci#define PID_RAMP 1 1178c2ecf20Sopenharmony_ci#define PID_SQUARE 2 1188c2ecf20Sopenharmony_ci#define PID_SINE 3 1198c2ecf20Sopenharmony_ci#define PID_TRIANGLE 4 1208c2ecf20Sopenharmony_ci#define PID_SAW_UP 5 1218c2ecf20Sopenharmony_ci#define PID_SAW_DOWN 6 1228c2ecf20Sopenharmony_ci#define PID_SPRING 7 1238c2ecf20Sopenharmony_ci#define PID_DAMPER 8 1248c2ecf20Sopenharmony_ci#define PID_INERTIA 9 1258c2ecf20Sopenharmony_ci#define PID_FRICTION 10 1268c2ecf20Sopenharmony_cistatic const u8 pidff_effect_types[] = { 1278c2ecf20Sopenharmony_ci 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34, 1288c2ecf20Sopenharmony_ci 0x40, 0x41, 0x42, 0x43 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define PID_BLOCK_LOAD_SUCCESS 0 1328c2ecf20Sopenharmony_ci#define PID_BLOCK_LOAD_FULL 1 1338c2ecf20Sopenharmony_cistatic const u8 pidff_block_load_status[] = { 0x8c, 0x8d }; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#define PID_EFFECT_START 0 1368c2ecf20Sopenharmony_ci#define PID_EFFECT_STOP 1 1378c2ecf20Sopenharmony_cistatic const u8 pidff_effect_operation_status[] = { 0x79, 0x7b }; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistruct pidff_usage { 1408c2ecf20Sopenharmony_ci struct hid_field *field; 1418c2ecf20Sopenharmony_ci s32 *value; 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistruct pidff_device { 1458c2ecf20Sopenharmony_ci struct hid_device *hid; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci struct hid_report *reports[sizeof(pidff_reports)]; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci struct pidff_usage set_effect[sizeof(pidff_set_effect)]; 1508c2ecf20Sopenharmony_ci struct pidff_usage set_envelope[sizeof(pidff_set_envelope)]; 1518c2ecf20Sopenharmony_ci struct pidff_usage set_condition[sizeof(pidff_set_condition)]; 1528c2ecf20Sopenharmony_ci struct pidff_usage set_periodic[sizeof(pidff_set_periodic)]; 1538c2ecf20Sopenharmony_ci struct pidff_usage set_constant[sizeof(pidff_set_constant)]; 1548c2ecf20Sopenharmony_ci struct pidff_usage set_ramp[sizeof(pidff_set_ramp)]; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci struct pidff_usage device_gain[sizeof(pidff_device_gain)]; 1578c2ecf20Sopenharmony_ci struct pidff_usage block_load[sizeof(pidff_block_load)]; 1588c2ecf20Sopenharmony_ci struct pidff_usage pool[sizeof(pidff_pool)]; 1598c2ecf20Sopenharmony_ci struct pidff_usage effect_operation[sizeof(pidff_effect_operation)]; 1608c2ecf20Sopenharmony_ci struct pidff_usage block_free[sizeof(pidff_block_free)]; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* Special field is a field that is not composed of 1638c2ecf20Sopenharmony_ci usage<->value pairs that pidff_usage values are */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Special field in create_new_effect */ 1668c2ecf20Sopenharmony_ci struct hid_field *create_new_effect_type; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Special fields in set_effect */ 1698c2ecf20Sopenharmony_ci struct hid_field *set_effect_type; 1708c2ecf20Sopenharmony_ci struct hid_field *effect_direction; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Special field in device_control */ 1738c2ecf20Sopenharmony_ci struct hid_field *device_control; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Special field in block_load */ 1768c2ecf20Sopenharmony_ci struct hid_field *block_load_status; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Special field in effect_operation */ 1798c2ecf20Sopenharmony_ci struct hid_field *effect_operation_status; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci int control_id[sizeof(pidff_device_control)]; 1828c2ecf20Sopenharmony_ci int type_id[sizeof(pidff_effect_types)]; 1838c2ecf20Sopenharmony_ci int status_id[sizeof(pidff_block_load_status)]; 1848c2ecf20Sopenharmony_ci int operation_id[sizeof(pidff_effect_operation_status)]; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci int pid_id[PID_EFFECTS_MAX]; 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* 1908c2ecf20Sopenharmony_ci * Scale an unsigned value with range 0..max for the given field 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic int pidff_rescale(int i, int max, struct hid_field *field) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci return i * (field->logical_maximum - field->logical_minimum) / max + 1958c2ecf20Sopenharmony_ci field->logical_minimum; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * Scale a signed value in range -0x8000..0x7fff for the given field 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_cistatic int pidff_rescale_signed(int i, struct hid_field *field) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci return i == 0 ? 0 : i > 2048c2ecf20Sopenharmony_ci 0 ? i * field->logical_maximum / 0x7fff : i * 2058c2ecf20Sopenharmony_ci field->logical_minimum / -0x8000; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void pidff_set(struct pidff_usage *usage, u16 value) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci usage->value[0] = pidff_rescale(value, 0xffff, usage->field); 2118c2ecf20Sopenharmony_ci pr_debug("calculated from %d to %d\n", value, usage->value[0]); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void pidff_set_signed(struct pidff_usage *usage, s16 value) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci if (usage->field->logical_minimum < 0) 2178c2ecf20Sopenharmony_ci usage->value[0] = pidff_rescale_signed(value, usage->field); 2188c2ecf20Sopenharmony_ci else { 2198c2ecf20Sopenharmony_ci if (value < 0) 2208c2ecf20Sopenharmony_ci usage->value[0] = 2218c2ecf20Sopenharmony_ci pidff_rescale(-value, 0x8000, usage->field); 2228c2ecf20Sopenharmony_ci else 2238c2ecf20Sopenharmony_ci usage->value[0] = 2248c2ecf20Sopenharmony_ci pidff_rescale(value, 0x7fff, usage->field); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci pr_debug("calculated from %d to %d\n", value, usage->value[0]); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* 2308c2ecf20Sopenharmony_ci * Send envelope report to the device 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_cistatic void pidff_set_envelope_report(struct pidff_device *pidff, 2338c2ecf20Sopenharmony_ci struct ff_envelope *envelope) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] = 2368c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci pidff->set_envelope[PID_ATTACK_LEVEL].value[0] = 2398c2ecf20Sopenharmony_ci pidff_rescale(envelope->attack_level > 2408c2ecf20Sopenharmony_ci 0x7fff ? 0x7fff : envelope->attack_level, 0x7fff, 2418c2ecf20Sopenharmony_ci pidff->set_envelope[PID_ATTACK_LEVEL].field); 2428c2ecf20Sopenharmony_ci pidff->set_envelope[PID_FADE_LEVEL].value[0] = 2438c2ecf20Sopenharmony_ci pidff_rescale(envelope->fade_level > 2448c2ecf20Sopenharmony_ci 0x7fff ? 0x7fff : envelope->fade_level, 0x7fff, 2458c2ecf20Sopenharmony_ci pidff->set_envelope[PID_FADE_LEVEL].field); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length; 2488c2ecf20Sopenharmony_ci pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "attack %u => %d\n", 2518c2ecf20Sopenharmony_ci envelope->attack_level, 2528c2ecf20Sopenharmony_ci pidff->set_envelope[PID_ATTACK_LEVEL].value[0]); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_SET_ENVELOPE], 2558c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* 2598c2ecf20Sopenharmony_ci * Test if the new envelope differs from old one 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_cistatic int pidff_needs_set_envelope(struct ff_envelope *envelope, 2628c2ecf20Sopenharmony_ci struct ff_envelope *old) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci return envelope->attack_level != old->attack_level || 2658c2ecf20Sopenharmony_ci envelope->fade_level != old->fade_level || 2668c2ecf20Sopenharmony_ci envelope->attack_length != old->attack_length || 2678c2ecf20Sopenharmony_ci envelope->fade_length != old->fade_length; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* 2718c2ecf20Sopenharmony_ci * Send constant force report to the device 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_cistatic void pidff_set_constant_force_report(struct pidff_device *pidff, 2748c2ecf20Sopenharmony_ci struct ff_effect *effect) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci pidff->set_constant[PID_EFFECT_BLOCK_INDEX].value[0] = 2778c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; 2788c2ecf20Sopenharmony_ci pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE], 2798c2ecf20Sopenharmony_ci effect->u.constant.level); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_SET_CONSTANT], 2828c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* 2868c2ecf20Sopenharmony_ci * Test if the constant parameters have changed between effects 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_cistatic int pidff_needs_set_constant(struct ff_effect *effect, 2898c2ecf20Sopenharmony_ci struct ff_effect *old) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci return effect->u.constant.level != old->u.constant.level; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * Send set effect report to the device 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistatic void pidff_set_effect_report(struct pidff_device *pidff, 2988c2ecf20Sopenharmony_ci struct ff_effect *effect) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = 3018c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; 3028c2ecf20Sopenharmony_ci pidff->set_effect_type->value[0] = 3038c2ecf20Sopenharmony_ci pidff->create_new_effect_type->value[0]; 3048c2ecf20Sopenharmony_ci pidff->set_effect[PID_DURATION].value[0] = effect->replay.length; 3058c2ecf20Sopenharmony_ci pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button; 3068c2ecf20Sopenharmony_ci pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 3078c2ecf20Sopenharmony_ci effect->trigger.interval; 3088c2ecf20Sopenharmony_ci pidff->set_effect[PID_GAIN].value[0] = 3098c2ecf20Sopenharmony_ci pidff->set_effect[PID_GAIN].field->logical_maximum; 3108c2ecf20Sopenharmony_ci pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; 3118c2ecf20Sopenharmony_ci pidff->effect_direction->value[0] = 3128c2ecf20Sopenharmony_ci pidff_rescale(effect->direction, 0xffff, 3138c2ecf20Sopenharmony_ci pidff->effect_direction); 3148c2ecf20Sopenharmony_ci pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT], 3178c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/* 3218c2ecf20Sopenharmony_ci * Test if the values used in set_effect have changed 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_cistatic int pidff_needs_set_effect(struct ff_effect *effect, 3248c2ecf20Sopenharmony_ci struct ff_effect *old) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci return effect->replay.length != old->replay.length || 3278c2ecf20Sopenharmony_ci effect->trigger.interval != old->trigger.interval || 3288c2ecf20Sopenharmony_ci effect->trigger.button != old->trigger.button || 3298c2ecf20Sopenharmony_ci effect->direction != old->direction || 3308c2ecf20Sopenharmony_ci effect->replay.delay != old->replay.delay; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * Send periodic effect report to the device 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_cistatic void pidff_set_periodic_report(struct pidff_device *pidff, 3378c2ecf20Sopenharmony_ci struct ff_effect *effect) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci pidff->set_periodic[PID_EFFECT_BLOCK_INDEX].value[0] = 3408c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; 3418c2ecf20Sopenharmony_ci pidff_set_signed(&pidff->set_periodic[PID_MAGNITUDE], 3428c2ecf20Sopenharmony_ci effect->u.periodic.magnitude); 3438c2ecf20Sopenharmony_ci pidff_set_signed(&pidff->set_periodic[PID_OFFSET], 3448c2ecf20Sopenharmony_ci effect->u.periodic.offset); 3458c2ecf20Sopenharmony_ci pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase); 3468c2ecf20Sopenharmony_ci pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_SET_PERIODIC], 3498c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/* 3548c2ecf20Sopenharmony_ci * Test if periodic effect parameters have changed 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_cistatic int pidff_needs_set_periodic(struct ff_effect *effect, 3578c2ecf20Sopenharmony_ci struct ff_effect *old) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci return effect->u.periodic.magnitude != old->u.periodic.magnitude || 3608c2ecf20Sopenharmony_ci effect->u.periodic.offset != old->u.periodic.offset || 3618c2ecf20Sopenharmony_ci effect->u.periodic.phase != old->u.periodic.phase || 3628c2ecf20Sopenharmony_ci effect->u.periodic.period != old->u.periodic.period; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci/* 3668c2ecf20Sopenharmony_ci * Send condition effect reports to the device 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_cistatic void pidff_set_condition_report(struct pidff_device *pidff, 3698c2ecf20Sopenharmony_ci struct ff_effect *effect) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci int i; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci pidff->set_condition[PID_EFFECT_BLOCK_INDEX].value[0] = 3748c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 3778c2ecf20Sopenharmony_ci pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i; 3788c2ecf20Sopenharmony_ci pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET], 3798c2ecf20Sopenharmony_ci effect->u.condition[i].center); 3808c2ecf20Sopenharmony_ci pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT], 3818c2ecf20Sopenharmony_ci effect->u.condition[i].right_coeff); 3828c2ecf20Sopenharmony_ci pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT], 3838c2ecf20Sopenharmony_ci effect->u.condition[i].left_coeff); 3848c2ecf20Sopenharmony_ci pidff_set(&pidff->set_condition[PID_POS_SATURATION], 3858c2ecf20Sopenharmony_ci effect->u.condition[i].right_saturation); 3868c2ecf20Sopenharmony_ci pidff_set(&pidff->set_condition[PID_NEG_SATURATION], 3878c2ecf20Sopenharmony_ci effect->u.condition[i].left_saturation); 3888c2ecf20Sopenharmony_ci pidff_set(&pidff->set_condition[PID_DEAD_BAND], 3898c2ecf20Sopenharmony_ci effect->u.condition[i].deadband); 3908c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_SET_CONDITION], 3918c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* 3968c2ecf20Sopenharmony_ci * Test if condition effect parameters have changed 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_cistatic int pidff_needs_set_condition(struct ff_effect *effect, 3998c2ecf20Sopenharmony_ci struct ff_effect *old) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci int i; 4028c2ecf20Sopenharmony_ci int ret = 0; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 4058c2ecf20Sopenharmony_ci struct ff_condition_effect *cond = &effect->u.condition[i]; 4068c2ecf20Sopenharmony_ci struct ff_condition_effect *old_cond = &old->u.condition[i]; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci ret |= cond->center != old_cond->center || 4098c2ecf20Sopenharmony_ci cond->right_coeff != old_cond->right_coeff || 4108c2ecf20Sopenharmony_ci cond->left_coeff != old_cond->left_coeff || 4118c2ecf20Sopenharmony_ci cond->right_saturation != old_cond->right_saturation || 4128c2ecf20Sopenharmony_ci cond->left_saturation != old_cond->left_saturation || 4138c2ecf20Sopenharmony_ci cond->deadband != old_cond->deadband; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return ret; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci/* 4208c2ecf20Sopenharmony_ci * Send ramp force report to the device 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_cistatic void pidff_set_ramp_force_report(struct pidff_device *pidff, 4238c2ecf20Sopenharmony_ci struct ff_effect *effect) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci pidff->set_ramp[PID_EFFECT_BLOCK_INDEX].value[0] = 4268c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; 4278c2ecf20Sopenharmony_ci pidff_set_signed(&pidff->set_ramp[PID_RAMP_START], 4288c2ecf20Sopenharmony_ci effect->u.ramp.start_level); 4298c2ecf20Sopenharmony_ci pidff_set_signed(&pidff->set_ramp[PID_RAMP_END], 4308c2ecf20Sopenharmony_ci effect->u.ramp.end_level); 4318c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_SET_RAMP], 4328c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/* 4368c2ecf20Sopenharmony_ci * Test if ramp force parameters have changed 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_cistatic int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci return effect->u.ramp.start_level != old->u.ramp.start_level || 4418c2ecf20Sopenharmony_ci effect->u.ramp.end_level != old->u.ramp.end_level; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci/* 4458c2ecf20Sopenharmony_ci * Send a request for effect upload to the device 4468c2ecf20Sopenharmony_ci * 4478c2ecf20Sopenharmony_ci * Returns 0 if device reported success, -ENOSPC if the device reported memory 4488c2ecf20Sopenharmony_ci * is full. Upon unknown response the function will retry for 60 times, if 4498c2ecf20Sopenharmony_ci * still unsuccessful -EIO is returned. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_cistatic int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci int j; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci pidff->create_new_effect_type->value[0] = efnum; 4568c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT], 4578c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 4588c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "create_new_effect sent, type: %d\n", efnum); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; 4618c2ecf20Sopenharmony_ci pidff->block_load_status->value[0] = 0; 4628c2ecf20Sopenharmony_ci hid_hw_wait(pidff->hid); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci for (j = 0; j < 60; j++) { 4658c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "pid_block_load requested\n"); 4668c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_BLOCK_LOAD], 4678c2ecf20Sopenharmony_ci HID_REQ_GET_REPORT); 4688c2ecf20Sopenharmony_ci hid_hw_wait(pidff->hid); 4698c2ecf20Sopenharmony_ci if (pidff->block_load_status->value[0] == 4708c2ecf20Sopenharmony_ci pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) { 4718c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "device reported free memory: %d bytes\n", 4728c2ecf20Sopenharmony_ci pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? 4738c2ecf20Sopenharmony_ci pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci if (pidff->block_load_status->value[0] == 4778c2ecf20Sopenharmony_ci pidff->status_id[PID_BLOCK_LOAD_FULL]) { 4788c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "not enough memory free: %d bytes\n", 4798c2ecf20Sopenharmony_ci pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? 4808c2ecf20Sopenharmony_ci pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); 4818c2ecf20Sopenharmony_ci return -ENOSPC; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci hid_err(pidff->hid, "pid_block_load failed 60 times\n"); 4858c2ecf20Sopenharmony_ci return -EIO; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci/* 4898c2ecf20Sopenharmony_ci * Play the effect with PID id n times 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_cistatic void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci pidff->effect_operation[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (n == 0) { 4968c2ecf20Sopenharmony_ci pidff->effect_operation_status->value[0] = 4978c2ecf20Sopenharmony_ci pidff->operation_id[PID_EFFECT_STOP]; 4988c2ecf20Sopenharmony_ci } else { 4998c2ecf20Sopenharmony_ci pidff->effect_operation_status->value[0] = 5008c2ecf20Sopenharmony_ci pidff->operation_id[PID_EFFECT_START]; 5018c2ecf20Sopenharmony_ci pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], 5058c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci/** 5098c2ecf20Sopenharmony_ci * Play the effect with effect id @effect_id for @value times 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_cistatic int pidff_playback(struct input_dev *dev, int effect_id, int value) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct pidff_device *pidff = dev->ff->private; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci pidff_playback_pid(pidff, pidff->pid_id[effect_id], value); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci/* 5218c2ecf20Sopenharmony_ci * Erase effect with PID id 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_cistatic void pidff_erase_pid(struct pidff_device *pidff, int pid_id) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; 5268c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_BLOCK_FREE], 5278c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci/* 5318c2ecf20Sopenharmony_ci * Stop and erase effect with effect_id 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_cistatic int pidff_erase_effect(struct input_dev *dev, int effect_id) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct pidff_device *pidff = dev->ff->private; 5368c2ecf20Sopenharmony_ci int pid_id = pidff->pid_id[effect_id]; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "starting to erase %d/%d\n", 5398c2ecf20Sopenharmony_ci effect_id, pidff->pid_id[effect_id]); 5408c2ecf20Sopenharmony_ci /* Wait for the queue to clear. We do not want a full fifo to 5418c2ecf20Sopenharmony_ci prevent the effect removal. */ 5428c2ecf20Sopenharmony_ci hid_hw_wait(pidff->hid); 5438c2ecf20Sopenharmony_ci pidff_playback_pid(pidff, pid_id, 0); 5448c2ecf20Sopenharmony_ci pidff_erase_pid(pidff, pid_id); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/* 5508c2ecf20Sopenharmony_ci * Effect upload handler 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_cistatic int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect, 5538c2ecf20Sopenharmony_ci struct ff_effect *old) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct pidff_device *pidff = dev->ff->private; 5568c2ecf20Sopenharmony_ci int type_id; 5578c2ecf20Sopenharmony_ci int error; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; 5608c2ecf20Sopenharmony_ci if (old) { 5618c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 5628c2ecf20Sopenharmony_ci pidff->pid_id[effect->id]; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci switch (effect->type) { 5668c2ecf20Sopenharmony_ci case FF_CONSTANT: 5678c2ecf20Sopenharmony_ci if (!old) { 5688c2ecf20Sopenharmony_ci error = pidff_request_effect_upload(pidff, 5698c2ecf20Sopenharmony_ci pidff->type_id[PID_CONSTANT]); 5708c2ecf20Sopenharmony_ci if (error) 5718c2ecf20Sopenharmony_ci return error; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_effect(effect, old)) 5748c2ecf20Sopenharmony_ci pidff_set_effect_report(pidff, effect); 5758c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_constant(effect, old)) 5768c2ecf20Sopenharmony_ci pidff_set_constant_force_report(pidff, effect); 5778c2ecf20Sopenharmony_ci if (!old || 5788c2ecf20Sopenharmony_ci pidff_needs_set_envelope(&effect->u.constant.envelope, 5798c2ecf20Sopenharmony_ci &old->u.constant.envelope)) 5808c2ecf20Sopenharmony_ci pidff_set_envelope_report(pidff, 5818c2ecf20Sopenharmony_ci &effect->u.constant.envelope); 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci case FF_PERIODIC: 5858c2ecf20Sopenharmony_ci if (!old) { 5868c2ecf20Sopenharmony_ci switch (effect->u.periodic.waveform) { 5878c2ecf20Sopenharmony_ci case FF_SQUARE: 5888c2ecf20Sopenharmony_ci type_id = PID_SQUARE; 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci case FF_TRIANGLE: 5918c2ecf20Sopenharmony_ci type_id = PID_TRIANGLE; 5928c2ecf20Sopenharmony_ci break; 5938c2ecf20Sopenharmony_ci case FF_SINE: 5948c2ecf20Sopenharmony_ci type_id = PID_SINE; 5958c2ecf20Sopenharmony_ci break; 5968c2ecf20Sopenharmony_ci case FF_SAW_UP: 5978c2ecf20Sopenharmony_ci type_id = PID_SAW_UP; 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci case FF_SAW_DOWN: 6008c2ecf20Sopenharmony_ci type_id = PID_SAW_DOWN; 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci default: 6038c2ecf20Sopenharmony_ci hid_err(pidff->hid, "invalid waveform\n"); 6048c2ecf20Sopenharmony_ci return -EINVAL; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci error = pidff_request_effect_upload(pidff, 6088c2ecf20Sopenharmony_ci pidff->type_id[type_id]); 6098c2ecf20Sopenharmony_ci if (error) 6108c2ecf20Sopenharmony_ci return error; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_effect(effect, old)) 6138c2ecf20Sopenharmony_ci pidff_set_effect_report(pidff, effect); 6148c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_periodic(effect, old)) 6158c2ecf20Sopenharmony_ci pidff_set_periodic_report(pidff, effect); 6168c2ecf20Sopenharmony_ci if (!old || 6178c2ecf20Sopenharmony_ci pidff_needs_set_envelope(&effect->u.periodic.envelope, 6188c2ecf20Sopenharmony_ci &old->u.periodic.envelope)) 6198c2ecf20Sopenharmony_ci pidff_set_envelope_report(pidff, 6208c2ecf20Sopenharmony_ci &effect->u.periodic.envelope); 6218c2ecf20Sopenharmony_ci break; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci case FF_RAMP: 6248c2ecf20Sopenharmony_ci if (!old) { 6258c2ecf20Sopenharmony_ci error = pidff_request_effect_upload(pidff, 6268c2ecf20Sopenharmony_ci pidff->type_id[PID_RAMP]); 6278c2ecf20Sopenharmony_ci if (error) 6288c2ecf20Sopenharmony_ci return error; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_effect(effect, old)) 6318c2ecf20Sopenharmony_ci pidff_set_effect_report(pidff, effect); 6328c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_ramp(effect, old)) 6338c2ecf20Sopenharmony_ci pidff_set_ramp_force_report(pidff, effect); 6348c2ecf20Sopenharmony_ci if (!old || 6358c2ecf20Sopenharmony_ci pidff_needs_set_envelope(&effect->u.ramp.envelope, 6368c2ecf20Sopenharmony_ci &old->u.ramp.envelope)) 6378c2ecf20Sopenharmony_ci pidff_set_envelope_report(pidff, 6388c2ecf20Sopenharmony_ci &effect->u.ramp.envelope); 6398c2ecf20Sopenharmony_ci break; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci case FF_SPRING: 6428c2ecf20Sopenharmony_ci if (!old) { 6438c2ecf20Sopenharmony_ci error = pidff_request_effect_upload(pidff, 6448c2ecf20Sopenharmony_ci pidff->type_id[PID_SPRING]); 6458c2ecf20Sopenharmony_ci if (error) 6468c2ecf20Sopenharmony_ci return error; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_effect(effect, old)) 6498c2ecf20Sopenharmony_ci pidff_set_effect_report(pidff, effect); 6508c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_condition(effect, old)) 6518c2ecf20Sopenharmony_ci pidff_set_condition_report(pidff, effect); 6528c2ecf20Sopenharmony_ci break; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci case FF_FRICTION: 6558c2ecf20Sopenharmony_ci if (!old) { 6568c2ecf20Sopenharmony_ci error = pidff_request_effect_upload(pidff, 6578c2ecf20Sopenharmony_ci pidff->type_id[PID_FRICTION]); 6588c2ecf20Sopenharmony_ci if (error) 6598c2ecf20Sopenharmony_ci return error; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_effect(effect, old)) 6628c2ecf20Sopenharmony_ci pidff_set_effect_report(pidff, effect); 6638c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_condition(effect, old)) 6648c2ecf20Sopenharmony_ci pidff_set_condition_report(pidff, effect); 6658c2ecf20Sopenharmony_ci break; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci case FF_DAMPER: 6688c2ecf20Sopenharmony_ci if (!old) { 6698c2ecf20Sopenharmony_ci error = pidff_request_effect_upload(pidff, 6708c2ecf20Sopenharmony_ci pidff->type_id[PID_DAMPER]); 6718c2ecf20Sopenharmony_ci if (error) 6728c2ecf20Sopenharmony_ci return error; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_effect(effect, old)) 6758c2ecf20Sopenharmony_ci pidff_set_effect_report(pidff, effect); 6768c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_condition(effect, old)) 6778c2ecf20Sopenharmony_ci pidff_set_condition_report(pidff, effect); 6788c2ecf20Sopenharmony_ci break; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci case FF_INERTIA: 6818c2ecf20Sopenharmony_ci if (!old) { 6828c2ecf20Sopenharmony_ci error = pidff_request_effect_upload(pidff, 6838c2ecf20Sopenharmony_ci pidff->type_id[PID_INERTIA]); 6848c2ecf20Sopenharmony_ci if (error) 6858c2ecf20Sopenharmony_ci return error; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_effect(effect, old)) 6888c2ecf20Sopenharmony_ci pidff_set_effect_report(pidff, effect); 6898c2ecf20Sopenharmony_ci if (!old || pidff_needs_set_condition(effect, old)) 6908c2ecf20Sopenharmony_ci pidff_set_condition_report(pidff, effect); 6918c2ecf20Sopenharmony_ci break; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci default: 6948c2ecf20Sopenharmony_ci hid_err(pidff->hid, "invalid type\n"); 6958c2ecf20Sopenharmony_ci return -EINVAL; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (!old) 6998c2ecf20Sopenharmony_ci pidff->pid_id[effect->id] = 7008c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "uploaded\n"); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return 0; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci/* 7088c2ecf20Sopenharmony_ci * set_gain() handler 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_cistatic void pidff_set_gain(struct input_dev *dev, u16 gain) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct pidff_device *pidff = dev->ff->private; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain); 7158c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_DEVICE_GAIN], 7168c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct hid_field *field = 7228c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].field; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (!magnitude) { 7258c2ecf20Sopenharmony_ci pidff_playback_pid(pidff, field->logical_minimum, 0); 7268c2ecf20Sopenharmony_ci return; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci pidff_playback_pid(pidff, field->logical_minimum, 1); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = 7328c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum; 7338c2ecf20Sopenharmony_ci pidff->set_effect_type->value[0] = pidff->type_id[PID_SPRING]; 7348c2ecf20Sopenharmony_ci pidff->set_effect[PID_DURATION].value[0] = 0; 7358c2ecf20Sopenharmony_ci pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = 0; 7368c2ecf20Sopenharmony_ci pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0; 7378c2ecf20Sopenharmony_ci pidff_set(&pidff->set_effect[PID_GAIN], magnitude); 7388c2ecf20Sopenharmony_ci pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; 7398c2ecf20Sopenharmony_ci pidff->set_effect[PID_START_DELAY].value[0] = 0; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT], 7428c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci/* 7468c2ecf20Sopenharmony_ci * pidff_set_autocenter() handler 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_cistatic void pidff_set_autocenter(struct input_dev *dev, u16 magnitude) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct pidff_device *pidff = dev->ff->private; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci pidff_autocenter(pidff, magnitude); 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/* 7568c2ecf20Sopenharmony_ci * Find fields from a report and fill a pidff_usage 7578c2ecf20Sopenharmony_ci */ 7588c2ecf20Sopenharmony_cistatic int pidff_find_fields(struct pidff_usage *usage, const u8 *table, 7598c2ecf20Sopenharmony_ci struct hid_report *report, int count, int strict) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci int i, j, k, found; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci for (k = 0; k < count; k++) { 7648c2ecf20Sopenharmony_ci found = 0; 7658c2ecf20Sopenharmony_ci for (i = 0; i < report->maxfield; i++) { 7668c2ecf20Sopenharmony_ci if (report->field[i]->maxusage != 7678c2ecf20Sopenharmony_ci report->field[i]->report_count) { 7688c2ecf20Sopenharmony_ci pr_debug("maxusage and report_count do not match, skipping\n"); 7698c2ecf20Sopenharmony_ci continue; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci for (j = 0; j < report->field[i]->maxusage; j++) { 7728c2ecf20Sopenharmony_ci if (report->field[i]->usage[j].hid == 7738c2ecf20Sopenharmony_ci (HID_UP_PID | table[k])) { 7748c2ecf20Sopenharmony_ci pr_debug("found %d at %d->%d\n", 7758c2ecf20Sopenharmony_ci k, i, j); 7768c2ecf20Sopenharmony_ci usage[k].field = report->field[i]; 7778c2ecf20Sopenharmony_ci usage[k].value = 7788c2ecf20Sopenharmony_ci &report->field[i]->value[j]; 7798c2ecf20Sopenharmony_ci found = 1; 7808c2ecf20Sopenharmony_ci break; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci if (found) 7848c2ecf20Sopenharmony_ci break; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci if (!found && strict) { 7878c2ecf20Sopenharmony_ci pr_debug("failed to locate %d\n", k); 7888c2ecf20Sopenharmony_ci return -1; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci/* 7958c2ecf20Sopenharmony_ci * Return index into pidff_reports for the given usage 7968c2ecf20Sopenharmony_ci */ 7978c2ecf20Sopenharmony_cistatic int pidff_check_usage(int usage) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci int i; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(pidff_reports); i++) 8028c2ecf20Sopenharmony_ci if (usage == (HID_UP_PID | pidff_reports[i])) 8038c2ecf20Sopenharmony_ci return i; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci return -1; 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci/* 8098c2ecf20Sopenharmony_ci * Find the reports and fill pidff->reports[] 8108c2ecf20Sopenharmony_ci * report_type specifies either OUTPUT or FEATURE reports 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_cistatic void pidff_find_reports(struct hid_device *hid, int report_type, 8138c2ecf20Sopenharmony_ci struct pidff_device *pidff) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct hid_report *report; 8168c2ecf20Sopenharmony_ci int i, ret; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci list_for_each_entry(report, 8198c2ecf20Sopenharmony_ci &hid->report_enum[report_type].report_list, list) { 8208c2ecf20Sopenharmony_ci if (report->maxfield < 1) 8218c2ecf20Sopenharmony_ci continue; 8228c2ecf20Sopenharmony_ci ret = pidff_check_usage(report->field[0]->logical); 8238c2ecf20Sopenharmony_ci if (ret != -1) { 8248c2ecf20Sopenharmony_ci hid_dbg(hid, "found usage 0x%02x from field->logical\n", 8258c2ecf20Sopenharmony_ci pidff_reports[ret]); 8268c2ecf20Sopenharmony_ci pidff->reports[ret] = report; 8278c2ecf20Sopenharmony_ci continue; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* 8318c2ecf20Sopenharmony_ci * Sometimes logical collections are stacked to indicate 8328c2ecf20Sopenharmony_ci * different usages for the report and the field, in which 8338c2ecf20Sopenharmony_ci * case we want the usage of the parent. However, Linux HID 8348c2ecf20Sopenharmony_ci * implementation hides this fact, so we have to dig it up 8358c2ecf20Sopenharmony_ci * ourselves 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_ci i = report->field[0]->usage[0].collection_index; 8388c2ecf20Sopenharmony_ci if (i <= 0 || 8398c2ecf20Sopenharmony_ci hid->collection[i - 1].type != HID_COLLECTION_LOGICAL) 8408c2ecf20Sopenharmony_ci continue; 8418c2ecf20Sopenharmony_ci ret = pidff_check_usage(hid->collection[i - 1].usage); 8428c2ecf20Sopenharmony_ci if (ret != -1 && !pidff->reports[ret]) { 8438c2ecf20Sopenharmony_ci hid_dbg(hid, 8448c2ecf20Sopenharmony_ci "found usage 0x%02x from collection array\n", 8458c2ecf20Sopenharmony_ci pidff_reports[ret]); 8468c2ecf20Sopenharmony_ci pidff->reports[ret] = report; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci/* 8528c2ecf20Sopenharmony_ci * Test if the required reports have been found 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_cistatic int pidff_reports_ok(struct pidff_device *pidff) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci int i; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci for (i = 0; i <= PID_REQUIRED_REPORTS; i++) { 8598c2ecf20Sopenharmony_ci if (!pidff->reports[i]) { 8608c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "%d missing\n", i); 8618c2ecf20Sopenharmony_ci return 0; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci return 1; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci/* 8698c2ecf20Sopenharmony_ci * Find a field with a specific usage within a report 8708c2ecf20Sopenharmony_ci */ 8718c2ecf20Sopenharmony_cistatic struct hid_field *pidff_find_special_field(struct hid_report *report, 8728c2ecf20Sopenharmony_ci int usage, int enforce_min) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci int i; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci for (i = 0; i < report->maxfield; i++) { 8778c2ecf20Sopenharmony_ci if (report->field[i]->logical == (HID_UP_PID | usage) && 8788c2ecf20Sopenharmony_ci report->field[i]->report_count > 0) { 8798c2ecf20Sopenharmony_ci if (!enforce_min || 8808c2ecf20Sopenharmony_ci report->field[i]->logical_minimum == 1) 8818c2ecf20Sopenharmony_ci return report->field[i]; 8828c2ecf20Sopenharmony_ci else { 8838c2ecf20Sopenharmony_ci pr_err("logical_minimum is not 1 as it should be\n"); 8848c2ecf20Sopenharmony_ci return NULL; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci return NULL; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci/* 8928c2ecf20Sopenharmony_ci * Fill a pidff->*_id struct table 8938c2ecf20Sopenharmony_ci */ 8948c2ecf20Sopenharmony_cistatic int pidff_find_special_keys(int *keys, struct hid_field *fld, 8958c2ecf20Sopenharmony_ci const u8 *usagetable, int count) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci int i, j; 8998c2ecf20Sopenharmony_ci int found = 0; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 9028c2ecf20Sopenharmony_ci for (j = 0; j < fld->maxusage; j++) { 9038c2ecf20Sopenharmony_ci if (fld->usage[j].hid == (HID_UP_PID | usagetable[i])) { 9048c2ecf20Sopenharmony_ci keys[i] = j + 1; 9058c2ecf20Sopenharmony_ci found++; 9068c2ecf20Sopenharmony_ci break; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci return found; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci#define PIDFF_FIND_SPECIAL_KEYS(keys, field, name) \ 9148c2ecf20Sopenharmony_ci pidff_find_special_keys(pidff->keys, pidff->field, pidff_ ## name, \ 9158c2ecf20Sopenharmony_ci sizeof(pidff_ ## name)) 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci/* 9188c2ecf20Sopenharmony_ci * Find and check the special fields 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_cistatic int pidff_find_special_fields(struct pidff_device *pidff) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "finding special fields\n"); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci pidff->create_new_effect_type = 9258c2ecf20Sopenharmony_ci pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT], 9268c2ecf20Sopenharmony_ci 0x25, 1); 9278c2ecf20Sopenharmony_ci pidff->set_effect_type = 9288c2ecf20Sopenharmony_ci pidff_find_special_field(pidff->reports[PID_SET_EFFECT], 9298c2ecf20Sopenharmony_ci 0x25, 1); 9308c2ecf20Sopenharmony_ci pidff->effect_direction = 9318c2ecf20Sopenharmony_ci pidff_find_special_field(pidff->reports[PID_SET_EFFECT], 9328c2ecf20Sopenharmony_ci 0x57, 0); 9338c2ecf20Sopenharmony_ci pidff->device_control = 9348c2ecf20Sopenharmony_ci pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL], 9358c2ecf20Sopenharmony_ci 0x96, 1); 9368c2ecf20Sopenharmony_ci pidff->block_load_status = 9378c2ecf20Sopenharmony_ci pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD], 9388c2ecf20Sopenharmony_ci 0x8b, 1); 9398c2ecf20Sopenharmony_ci pidff->effect_operation_status = 9408c2ecf20Sopenharmony_ci pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION], 9418c2ecf20Sopenharmony_ci 0x78, 1); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "search done\n"); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci if (!pidff->create_new_effect_type || !pidff->set_effect_type) { 9468c2ecf20Sopenharmony_ci hid_err(pidff->hid, "effect lists not found\n"); 9478c2ecf20Sopenharmony_ci return -1; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (!pidff->effect_direction) { 9518c2ecf20Sopenharmony_ci hid_err(pidff->hid, "direction field not found\n"); 9528c2ecf20Sopenharmony_ci return -1; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci if (!pidff->device_control) { 9568c2ecf20Sopenharmony_ci hid_err(pidff->hid, "device control field not found\n"); 9578c2ecf20Sopenharmony_ci return -1; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (!pidff->block_load_status) { 9618c2ecf20Sopenharmony_ci hid_err(pidff->hid, "block load status field not found\n"); 9628c2ecf20Sopenharmony_ci return -1; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci if (!pidff->effect_operation_status) { 9668c2ecf20Sopenharmony_ci hid_err(pidff->hid, "effect operation field not found\n"); 9678c2ecf20Sopenharmony_ci return -1; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci pidff_find_special_keys(pidff->control_id, pidff->device_control, 9718c2ecf20Sopenharmony_ci pidff_device_control, 9728c2ecf20Sopenharmony_ci sizeof(pidff_device_control)); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type, 9778c2ecf20Sopenharmony_ci effect_types)) { 9788c2ecf20Sopenharmony_ci hid_err(pidff->hid, "no effect types found\n"); 9798c2ecf20Sopenharmony_ci return -1; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (PIDFF_FIND_SPECIAL_KEYS(status_id, block_load_status, 9838c2ecf20Sopenharmony_ci block_load_status) != 9848c2ecf20Sopenharmony_ci sizeof(pidff_block_load_status)) { 9858c2ecf20Sopenharmony_ci hid_err(pidff->hid, 9868c2ecf20Sopenharmony_ci "block load status identifiers not found\n"); 9878c2ecf20Sopenharmony_ci return -1; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (PIDFF_FIND_SPECIAL_KEYS(operation_id, effect_operation_status, 9918c2ecf20Sopenharmony_ci effect_operation_status) != 9928c2ecf20Sopenharmony_ci sizeof(pidff_effect_operation_status)) { 9938c2ecf20Sopenharmony_ci hid_err(pidff->hid, "effect operation identifiers not found\n"); 9948c2ecf20Sopenharmony_ci return -1; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return 0; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci/** 10018c2ecf20Sopenharmony_ci * Find the implemented effect types 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_cistatic int pidff_find_effects(struct pidff_device *pidff, 10048c2ecf20Sopenharmony_ci struct input_dev *dev) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci int i; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(pidff_effect_types); i++) { 10098c2ecf20Sopenharmony_ci int pidff_type = pidff->type_id[i]; 10108c2ecf20Sopenharmony_ci if (pidff->set_effect_type->usage[pidff_type].hid != 10118c2ecf20Sopenharmony_ci pidff->create_new_effect_type->usage[pidff_type].hid) { 10128c2ecf20Sopenharmony_ci hid_err(pidff->hid, 10138c2ecf20Sopenharmony_ci "effect type number %d is invalid\n", i); 10148c2ecf20Sopenharmony_ci return -1; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (pidff->type_id[PID_CONSTANT]) 10198c2ecf20Sopenharmony_ci set_bit(FF_CONSTANT, dev->ffbit); 10208c2ecf20Sopenharmony_ci if (pidff->type_id[PID_RAMP]) 10218c2ecf20Sopenharmony_ci set_bit(FF_RAMP, dev->ffbit); 10228c2ecf20Sopenharmony_ci if (pidff->type_id[PID_SQUARE]) { 10238c2ecf20Sopenharmony_ci set_bit(FF_SQUARE, dev->ffbit); 10248c2ecf20Sopenharmony_ci set_bit(FF_PERIODIC, dev->ffbit); 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci if (pidff->type_id[PID_SINE]) { 10278c2ecf20Sopenharmony_ci set_bit(FF_SINE, dev->ffbit); 10288c2ecf20Sopenharmony_ci set_bit(FF_PERIODIC, dev->ffbit); 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci if (pidff->type_id[PID_TRIANGLE]) { 10318c2ecf20Sopenharmony_ci set_bit(FF_TRIANGLE, dev->ffbit); 10328c2ecf20Sopenharmony_ci set_bit(FF_PERIODIC, dev->ffbit); 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci if (pidff->type_id[PID_SAW_UP]) { 10358c2ecf20Sopenharmony_ci set_bit(FF_SAW_UP, dev->ffbit); 10368c2ecf20Sopenharmony_ci set_bit(FF_PERIODIC, dev->ffbit); 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci if (pidff->type_id[PID_SAW_DOWN]) { 10398c2ecf20Sopenharmony_ci set_bit(FF_SAW_DOWN, dev->ffbit); 10408c2ecf20Sopenharmony_ci set_bit(FF_PERIODIC, dev->ffbit); 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci if (pidff->type_id[PID_SPRING]) 10438c2ecf20Sopenharmony_ci set_bit(FF_SPRING, dev->ffbit); 10448c2ecf20Sopenharmony_ci if (pidff->type_id[PID_DAMPER]) 10458c2ecf20Sopenharmony_ci set_bit(FF_DAMPER, dev->ffbit); 10468c2ecf20Sopenharmony_ci if (pidff->type_id[PID_INERTIA]) 10478c2ecf20Sopenharmony_ci set_bit(FF_INERTIA, dev->ffbit); 10488c2ecf20Sopenharmony_ci if (pidff->type_id[PID_FRICTION]) 10498c2ecf20Sopenharmony_ci set_bit(FF_FRICTION, dev->ffbit); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci return 0; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci#define PIDFF_FIND_FIELDS(name, report, strict) \ 10568c2ecf20Sopenharmony_ci pidff_find_fields(pidff->name, pidff_ ## name, \ 10578c2ecf20Sopenharmony_ci pidff->reports[report], \ 10588c2ecf20Sopenharmony_ci sizeof(pidff_ ## name), strict) 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci/* 10618c2ecf20Sopenharmony_ci * Fill and check the pidff_usages 10628c2ecf20Sopenharmony_ci */ 10638c2ecf20Sopenharmony_cistatic int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci int envelope_ok = 0; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) { 10688c2ecf20Sopenharmony_ci hid_err(pidff->hid, "unknown set_effect report layout\n"); 10698c2ecf20Sopenharmony_ci return -ENODEV; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0); 10738c2ecf20Sopenharmony_ci if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) { 10748c2ecf20Sopenharmony_ci hid_err(pidff->hid, "unknown pid_block_load report layout\n"); 10758c2ecf20Sopenharmony_ci return -ENODEV; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (PIDFF_FIND_FIELDS(effect_operation, PID_EFFECT_OPERATION, 1)) { 10798c2ecf20Sopenharmony_ci hid_err(pidff->hid, "unknown effect_operation report layout\n"); 10808c2ecf20Sopenharmony_ci return -ENODEV; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if (PIDFF_FIND_FIELDS(block_free, PID_BLOCK_FREE, 1)) { 10848c2ecf20Sopenharmony_ci hid_err(pidff->hid, "unknown pid_block_free report layout\n"); 10858c2ecf20Sopenharmony_ci return -ENODEV; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (!PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1)) 10898c2ecf20Sopenharmony_ci envelope_ok = 1; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev)) 10928c2ecf20Sopenharmony_ci return -ENODEV; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (!envelope_ok) { 10958c2ecf20Sopenharmony_ci if (test_and_clear_bit(FF_CONSTANT, dev->ffbit)) 10968c2ecf20Sopenharmony_ci hid_warn(pidff->hid, 10978c2ecf20Sopenharmony_ci "has constant effect but no envelope\n"); 10988c2ecf20Sopenharmony_ci if (test_and_clear_bit(FF_RAMP, dev->ffbit)) 10998c2ecf20Sopenharmony_ci hid_warn(pidff->hid, 11008c2ecf20Sopenharmony_ci "has ramp effect but no envelope\n"); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (test_and_clear_bit(FF_PERIODIC, dev->ffbit)) 11038c2ecf20Sopenharmony_ci hid_warn(pidff->hid, 11048c2ecf20Sopenharmony_ci "has periodic effect but no envelope\n"); 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (test_bit(FF_CONSTANT, dev->ffbit) && 11088c2ecf20Sopenharmony_ci PIDFF_FIND_FIELDS(set_constant, PID_SET_CONSTANT, 1)) { 11098c2ecf20Sopenharmony_ci hid_warn(pidff->hid, "unknown constant effect layout\n"); 11108c2ecf20Sopenharmony_ci clear_bit(FF_CONSTANT, dev->ffbit); 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci if (test_bit(FF_RAMP, dev->ffbit) && 11148c2ecf20Sopenharmony_ci PIDFF_FIND_FIELDS(set_ramp, PID_SET_RAMP, 1)) { 11158c2ecf20Sopenharmony_ci hid_warn(pidff->hid, "unknown ramp effect layout\n"); 11168c2ecf20Sopenharmony_ci clear_bit(FF_RAMP, dev->ffbit); 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if ((test_bit(FF_SPRING, dev->ffbit) || 11208c2ecf20Sopenharmony_ci test_bit(FF_DAMPER, dev->ffbit) || 11218c2ecf20Sopenharmony_ci test_bit(FF_FRICTION, dev->ffbit) || 11228c2ecf20Sopenharmony_ci test_bit(FF_INERTIA, dev->ffbit)) && 11238c2ecf20Sopenharmony_ci PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1)) { 11248c2ecf20Sopenharmony_ci hid_warn(pidff->hid, "unknown condition effect layout\n"); 11258c2ecf20Sopenharmony_ci clear_bit(FF_SPRING, dev->ffbit); 11268c2ecf20Sopenharmony_ci clear_bit(FF_DAMPER, dev->ffbit); 11278c2ecf20Sopenharmony_ci clear_bit(FF_FRICTION, dev->ffbit); 11288c2ecf20Sopenharmony_ci clear_bit(FF_INERTIA, dev->ffbit); 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci if (test_bit(FF_PERIODIC, dev->ffbit) && 11328c2ecf20Sopenharmony_ci PIDFF_FIND_FIELDS(set_periodic, PID_SET_PERIODIC, 1)) { 11338c2ecf20Sopenharmony_ci hid_warn(pidff->hid, "unknown periodic effect layout\n"); 11348c2ecf20Sopenharmony_ci clear_bit(FF_PERIODIC, dev->ffbit); 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci PIDFF_FIND_FIELDS(pool, PID_POOL, 0); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (!PIDFF_FIND_FIELDS(device_gain, PID_DEVICE_GAIN, 1)) 11408c2ecf20Sopenharmony_ci set_bit(FF_GAIN, dev->ffbit); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci return 0; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci/* 11468c2ecf20Sopenharmony_ci * Reset the device 11478c2ecf20Sopenharmony_ci */ 11488c2ecf20Sopenharmony_cistatic void pidff_reset(struct pidff_device *pidff) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci struct hid_device *hid = pidff->hid; 11518c2ecf20Sopenharmony_ci int i = 0; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci pidff->device_control->value[0] = pidff->control_id[PID_RESET]; 11548c2ecf20Sopenharmony_ci /* We reset twice as sometimes hid_wait_io isn't waiting long enough */ 11558c2ecf20Sopenharmony_ci hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); 11568c2ecf20Sopenharmony_ci hid_hw_wait(hid); 11578c2ecf20Sopenharmony_ci hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); 11588c2ecf20Sopenharmony_ci hid_hw_wait(hid); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci pidff->device_control->value[0] = 11618c2ecf20Sopenharmony_ci pidff->control_id[PID_ENABLE_ACTUATORS]; 11628c2ecf20Sopenharmony_ci hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); 11638c2ecf20Sopenharmony_ci hid_hw_wait(hid); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci /* pool report is sometimes messed up, refetch it */ 11668c2ecf20Sopenharmony_ci hid_hw_request(hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT); 11678c2ecf20Sopenharmony_ci hid_hw_wait(hid); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (pidff->pool[PID_SIMULTANEOUS_MAX].value) { 11708c2ecf20Sopenharmony_ci while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) { 11718c2ecf20Sopenharmony_ci if (i++ > 20) { 11728c2ecf20Sopenharmony_ci hid_warn(pidff->hid, 11738c2ecf20Sopenharmony_ci "device reports %d simultaneous effects\n", 11748c2ecf20Sopenharmony_ci pidff->pool[PID_SIMULTANEOUS_MAX].value[0]); 11758c2ecf20Sopenharmony_ci break; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci hid_dbg(pidff->hid, "pid_pool requested again\n"); 11788c2ecf20Sopenharmony_ci hid_hw_request(hid, pidff->reports[PID_POOL], 11798c2ecf20Sopenharmony_ci HID_REQ_GET_REPORT); 11808c2ecf20Sopenharmony_ci hid_hw_wait(hid); 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci/* 11868c2ecf20Sopenharmony_ci * Test if autocenter modification is using the supported method 11878c2ecf20Sopenharmony_ci */ 11888c2ecf20Sopenharmony_cistatic int pidff_check_autocenter(struct pidff_device *pidff, 11898c2ecf20Sopenharmony_ci struct input_dev *dev) 11908c2ecf20Sopenharmony_ci{ 11918c2ecf20Sopenharmony_ci int error; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* 11948c2ecf20Sopenharmony_ci * Let's find out if autocenter modification is supported 11958c2ecf20Sopenharmony_ci * Specification doesn't specify anything, so we request an 11968c2ecf20Sopenharmony_ci * effect upload and cancel it immediately. If the approved 11978c2ecf20Sopenharmony_ci * effect id was one above the minimum, then we assume the first 11988c2ecf20Sopenharmony_ci * effect id is a built-in spring type effect used for autocenter 11998c2ecf20Sopenharmony_ci */ 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci error = pidff_request_effect_upload(pidff, 1); 12028c2ecf20Sopenharmony_ci if (error) { 12038c2ecf20Sopenharmony_ci hid_err(pidff->hid, "upload request failed\n"); 12048c2ecf20Sopenharmony_ci return error; 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] == 12088c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) { 12098c2ecf20Sopenharmony_ci pidff_autocenter(pidff, 0xffff); 12108c2ecf20Sopenharmony_ci set_bit(FF_AUTOCENTER, dev->ffbit); 12118c2ecf20Sopenharmony_ci } else { 12128c2ecf20Sopenharmony_ci hid_notice(pidff->hid, 12138c2ecf20Sopenharmony_ci "device has unknown autocenter control method\n"); 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci pidff_erase_pid(pidff, 12178c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci return 0; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci/* 12248c2ecf20Sopenharmony_ci * Check if the device is PID and initialize it 12258c2ecf20Sopenharmony_ci */ 12268c2ecf20Sopenharmony_ciint hid_pidff_init(struct hid_device *hid) 12278c2ecf20Sopenharmony_ci{ 12288c2ecf20Sopenharmony_ci struct pidff_device *pidff; 12298c2ecf20Sopenharmony_ci struct hid_input *hidinput = list_entry(hid->inputs.next, 12308c2ecf20Sopenharmony_ci struct hid_input, list); 12318c2ecf20Sopenharmony_ci struct input_dev *dev = hidinput->input; 12328c2ecf20Sopenharmony_ci struct ff_device *ff; 12338c2ecf20Sopenharmony_ci int max_effects; 12348c2ecf20Sopenharmony_ci int error; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci hid_dbg(hid, "starting pid init\n"); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) { 12398c2ecf20Sopenharmony_ci hid_dbg(hid, "not a PID device, no output report\n"); 12408c2ecf20Sopenharmony_ci return -ENODEV; 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci pidff = kzalloc(sizeof(*pidff), GFP_KERNEL); 12448c2ecf20Sopenharmony_ci if (!pidff) 12458c2ecf20Sopenharmony_ci return -ENOMEM; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci pidff->hid = hid; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci hid_device_io_start(hid); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci pidff_find_reports(hid, HID_OUTPUT_REPORT, pidff); 12528c2ecf20Sopenharmony_ci pidff_find_reports(hid, HID_FEATURE_REPORT, pidff); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (!pidff_reports_ok(pidff)) { 12558c2ecf20Sopenharmony_ci hid_dbg(hid, "reports not ok, aborting\n"); 12568c2ecf20Sopenharmony_ci error = -ENODEV; 12578c2ecf20Sopenharmony_ci goto fail; 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci error = pidff_init_fields(pidff, dev); 12618c2ecf20Sopenharmony_ci if (error) 12628c2ecf20Sopenharmony_ci goto fail; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci pidff_reset(pidff); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci if (test_bit(FF_GAIN, dev->ffbit)) { 12678c2ecf20Sopenharmony_ci pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff); 12688c2ecf20Sopenharmony_ci hid_hw_request(hid, pidff->reports[PID_DEVICE_GAIN], 12698c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci error = pidff_check_autocenter(pidff, dev); 12738c2ecf20Sopenharmony_ci if (error) 12748c2ecf20Sopenharmony_ci goto fail; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci max_effects = 12778c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_maximum - 12788c2ecf20Sopenharmony_ci pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 12798c2ecf20Sopenharmony_ci 1; 12808c2ecf20Sopenharmony_ci hid_dbg(hid, "max effects is %d\n", max_effects); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci if (max_effects > PID_EFFECTS_MAX) 12838c2ecf20Sopenharmony_ci max_effects = PID_EFFECTS_MAX; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci if (pidff->pool[PID_SIMULTANEOUS_MAX].value) 12868c2ecf20Sopenharmony_ci hid_dbg(hid, "max simultaneous effects is %d\n", 12878c2ecf20Sopenharmony_ci pidff->pool[PID_SIMULTANEOUS_MAX].value[0]); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (pidff->pool[PID_RAM_POOL_SIZE].value) 12908c2ecf20Sopenharmony_ci hid_dbg(hid, "device memory size is %d bytes\n", 12918c2ecf20Sopenharmony_ci pidff->pool[PID_RAM_POOL_SIZE].value[0]); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci if (pidff->pool[PID_DEVICE_MANAGED_POOL].value && 12948c2ecf20Sopenharmony_ci pidff->pool[PID_DEVICE_MANAGED_POOL].value[0] == 0) { 12958c2ecf20Sopenharmony_ci error = -EPERM; 12968c2ecf20Sopenharmony_ci hid_notice(hid, 12978c2ecf20Sopenharmony_ci "device does not support device managed pool\n"); 12988c2ecf20Sopenharmony_ci goto fail; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci error = input_ff_create(dev, max_effects); 13028c2ecf20Sopenharmony_ci if (error) 13038c2ecf20Sopenharmony_ci goto fail; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci ff = dev->ff; 13068c2ecf20Sopenharmony_ci ff->private = pidff; 13078c2ecf20Sopenharmony_ci ff->upload = pidff_upload_effect; 13088c2ecf20Sopenharmony_ci ff->erase = pidff_erase_effect; 13098c2ecf20Sopenharmony_ci ff->set_gain = pidff_set_gain; 13108c2ecf20Sopenharmony_ci ff->set_autocenter = pidff_set_autocenter; 13118c2ecf20Sopenharmony_ci ff->playback = pidff_playback; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci hid_info(dev, "Force feedback for USB HID PID devices by Anssi Hannula <anssi.hannula@gmail.com>\n"); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci hid_device_io_stop(hid); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci return 0; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci fail: 13208c2ecf20Sopenharmony_ci hid_device_io_stop(hid); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci kfree(pidff); 13238c2ecf20Sopenharmony_ci return error; 13248c2ecf20Sopenharmony_ci} 1325