Path: blob/master/drivers/input/joystick/iforce/iforce-ff.c
15115 views
/*1* Copyright (c) 2000-2002 Vojtech Pavlik <[email protected]>2* Copyright (c) 2001-2002, 2007 Johann Deneux <[email protected]>3*4* USB/RS232 I-Force joysticks and wheels.5*/67/*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License, or11* (at your option) any later version.12*13* This program is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA21*22* Should you need to contact me, the author, you can do so either by23* e-mail - mail your message to <[email protected]>, or by paper mail:24* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic25*/2627#include "iforce.h"2829/*30* Set the magnitude of a constant force effect31* Return error code32*33* Note: caller must ensure exclusive access to device34*/3536static int make_magnitude_modifier(struct iforce* iforce,37struct resource* mod_chunk, int no_alloc, __s16 level)38{39unsigned char data[3];4041if (!no_alloc) {42mutex_lock(&iforce->mem_mutex);43if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,44iforce->device_memory.start, iforce->device_memory.end, 2L,45NULL, NULL)) {46mutex_unlock(&iforce->mem_mutex);47return -ENOSPC;48}49mutex_unlock(&iforce->mem_mutex);50}5152data[0] = LO(mod_chunk->start);53data[1] = HI(mod_chunk->start);54data[2] = HIFIX80(level);5556iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data);5758iforce_dump_packet("magnitude: ", FF_CMD_MAGNITUDE, data);59return 0;60}6162/*63* Upload the component of an effect dealing with the period, phase and magnitude64*/6566static int make_period_modifier(struct iforce* iforce,67struct resource* mod_chunk, int no_alloc,68__s16 magnitude, __s16 offset, u16 period, u16 phase)69{70unsigned char data[7];7172period = TIME_SCALE(period);7374if (!no_alloc) {75mutex_lock(&iforce->mem_mutex);76if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,77iforce->device_memory.start, iforce->device_memory.end, 2L,78NULL, NULL)) {79mutex_unlock(&iforce->mem_mutex);80return -ENOSPC;81}82mutex_unlock(&iforce->mem_mutex);83}8485data[0] = LO(mod_chunk->start);86data[1] = HI(mod_chunk->start);8788data[2] = HIFIX80(magnitude);89data[3] = HIFIX80(offset);90data[4] = HI(phase);9192data[5] = LO(period);93data[6] = HI(period);9495iforce_send_packet(iforce, FF_CMD_PERIOD, data);9697return 0;98}99100/*101* Uploads the part of an effect setting the envelope of the force102*/103104static int make_envelope_modifier(struct iforce* iforce,105struct resource* mod_chunk, int no_alloc,106u16 attack_duration, __s16 initial_level,107u16 fade_duration, __s16 final_level)108{109unsigned char data[8];110111attack_duration = TIME_SCALE(attack_duration);112fade_duration = TIME_SCALE(fade_duration);113114if (!no_alloc) {115mutex_lock(&iforce->mem_mutex);116if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,117iforce->device_memory.start, iforce->device_memory.end, 2L,118NULL, NULL)) {119mutex_unlock(&iforce->mem_mutex);120return -ENOSPC;121}122mutex_unlock(&iforce->mem_mutex);123}124125data[0] = LO(mod_chunk->start);126data[1] = HI(mod_chunk->start);127128data[2] = LO(attack_duration);129data[3] = HI(attack_duration);130data[4] = HI(initial_level);131132data[5] = LO(fade_duration);133data[6] = HI(fade_duration);134data[7] = HI(final_level);135136iforce_send_packet(iforce, FF_CMD_ENVELOPE, data);137138return 0;139}140141/*142* Component of spring, friction, inertia... effects143*/144145static int make_condition_modifier(struct iforce* iforce,146struct resource* mod_chunk, int no_alloc,147__u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)148{149unsigned char data[10];150151if (!no_alloc) {152mutex_lock(&iforce->mem_mutex);153if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,154iforce->device_memory.start, iforce->device_memory.end, 2L,155NULL, NULL)) {156mutex_unlock(&iforce->mem_mutex);157return -ENOSPC;158}159mutex_unlock(&iforce->mem_mutex);160}161162data[0] = LO(mod_chunk->start);163data[1] = HI(mod_chunk->start);164165data[2] = (100 * rk) >> 15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */166data[3] = (100 * lk) >> 15; /* This code is incorrect on cpus lacking arith shift */167168center = (500 * center) >> 15;169data[4] = LO(center);170data[5] = HI(center);171172db = (1000 * db) >> 16;173data[6] = LO(db);174data[7] = HI(db);175176data[8] = (100 * rsat) >> 16;177data[9] = (100 * lsat) >> 16;178179iforce_send_packet(iforce, FF_CMD_CONDITION, data);180iforce_dump_packet("condition", FF_CMD_CONDITION, data);181182return 0;183}184185static unsigned char find_button(struct iforce *iforce, signed short button)186{187int i;188189for (i = 1; iforce->type->btn[i] >= 0; i++)190if (iforce->type->btn[i] == button)191return i + 1;192return 0;193}194195/*196* Analyse the changes in an effect, and tell if we need to send an condition197* parameter packet198*/199static int need_condition_modifier(struct iforce *iforce,200struct ff_effect *old,201struct ff_effect *new)202{203int ret = 0;204int i;205206if (new->type != FF_SPRING && new->type != FF_FRICTION) {207dev_warn(&iforce->dev->dev, "bad effect type in %s\n",208__func__);209return 0;210}211212for (i = 0; i < 2; i++) {213ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation214|| old->u.condition[i].left_saturation != new->u.condition[i].left_saturation215|| old->u.condition[i].right_coeff != new->u.condition[i].right_coeff216|| old->u.condition[i].left_coeff != new->u.condition[i].left_coeff217|| old->u.condition[i].deadband != new->u.condition[i].deadband218|| old->u.condition[i].center != new->u.condition[i].center;219}220return ret;221}222223/*224* Analyse the changes in an effect, and tell if we need to send a magnitude225* parameter packet226*/227static int need_magnitude_modifier(struct iforce *iforce,228struct ff_effect *old,229struct ff_effect *effect)230{231if (effect->type != FF_CONSTANT) {232dev_warn(&iforce->dev->dev, "bad effect type in %s\n",233__func__);234return 0;235}236237return old->u.constant.level != effect->u.constant.level;238}239240/*241* Analyse the changes in an effect, and tell if we need to send an envelope242* parameter packet243*/244static int need_envelope_modifier(struct iforce *iforce, struct ff_effect *old,245struct ff_effect *effect)246{247switch (effect->type) {248case FF_CONSTANT:249if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length250|| old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level251|| old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length252|| old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level)253return 1;254break;255256case FF_PERIODIC:257if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length258|| old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level259|| old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length260|| old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level)261return 1;262break;263264default:265dev_warn(&iforce->dev->dev, "bad effect type in %s\n",266__func__);267}268269return 0;270}271272/*273* Analyse the changes in an effect, and tell if we need to send a periodic274* parameter effect275*/276static int need_period_modifier(struct iforce *iforce, struct ff_effect *old,277struct ff_effect *new)278{279if (new->type != FF_PERIODIC) {280dev_warn(&iforce->dev->dev, "bad effect type in %s\n",281__func__);282return 0;283}284return (old->u.periodic.period != new->u.periodic.period285|| old->u.periodic.magnitude != new->u.periodic.magnitude286|| old->u.periodic.offset != new->u.periodic.offset287|| old->u.periodic.phase != new->u.periodic.phase);288}289290/*291* Analyse the changes in an effect, and tell if we need to send an effect292* packet293*/294static int need_core(struct ff_effect *old, struct ff_effect *new)295{296if (old->direction != new->direction297|| old->trigger.button != new->trigger.button298|| old->trigger.interval != new->trigger.interval299|| old->replay.length != new->replay.length300|| old->replay.delay != new->replay.delay)301return 1;302303return 0;304}305/*306* Send the part common to all effects to the device307*/308static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,309u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,310u16 interval, u16 direction)311{312unsigned char data[14];313314duration = TIME_SCALE(duration);315delay = TIME_SCALE(delay);316interval = TIME_SCALE(interval);317318data[0] = LO(id);319data[1] = effect_type;320data[2] = LO(axes) | find_button(iforce, button);321322data[3] = LO(duration);323data[4] = HI(duration);324325data[5] = HI(direction);326327data[6] = LO(interval);328data[7] = HI(interval);329330data[8] = LO(mod_id1);331data[9] = HI(mod_id1);332data[10] = LO(mod_id2);333data[11] = HI(mod_id2);334335data[12] = LO(delay);336data[13] = HI(delay);337338/* Stop effect */339/* iforce_control_playback(iforce, id, 0);*/340341iforce_send_packet(iforce, FF_CMD_EFFECT, data);342343/* If needed, restart effect */344if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) {345/* BUG: perhaps we should replay n times, instead of 1. But we do not know n */346iforce_control_playback(iforce, id, 1);347}348349return 0;350}351352/*353* Upload a periodic effect to the device354* See also iforce_upload_constant.355*/356int iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)357{358u8 wave_code;359int core_id = effect->id;360struct iforce_core_effect* core_effect = iforce->core_effects + core_id;361struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);362struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);363int param1_err = 1;364int param2_err = 1;365int core_err = 0;366367if (!old || need_period_modifier(iforce, old, effect)) {368param1_err = make_period_modifier(iforce, mod1_chunk,369old != NULL,370effect->u.periodic.magnitude, effect->u.periodic.offset,371effect->u.periodic.period, effect->u.periodic.phase);372if (param1_err)373return param1_err;374set_bit(FF_MOD1_IS_USED, core_effect->flags);375}376377if (!old || need_envelope_modifier(iforce, old, effect)) {378param2_err = make_envelope_modifier(iforce, mod2_chunk,379old !=NULL,380effect->u.periodic.envelope.attack_length,381effect->u.periodic.envelope.attack_level,382effect->u.periodic.envelope.fade_length,383effect->u.periodic.envelope.fade_level);384if (param2_err)385return param2_err;386set_bit(FF_MOD2_IS_USED, core_effect->flags);387}388389switch (effect->u.periodic.waveform) {390case FF_SQUARE: wave_code = 0x20; break;391case FF_TRIANGLE: wave_code = 0x21; break;392case FF_SINE: wave_code = 0x22; break;393case FF_SAW_UP: wave_code = 0x23; break;394case FF_SAW_DOWN: wave_code = 0x24; break;395default: wave_code = 0x20; break;396}397398if (!old || need_core(old, effect)) {399core_err = make_core(iforce, effect->id,400mod1_chunk->start,401mod2_chunk->start,402wave_code,4030x20,404effect->replay.length,405effect->replay.delay,406effect->trigger.button,407effect->trigger.interval,408effect->direction);409}410411/* If one of the parameter creation failed, we already returned an412* error code.413* If the core creation failed, we return its error code.414* Else: if one parameter at least was created, we return 0415* else we return 1;416*/417return core_err < 0 ? core_err : (param1_err && param2_err);418}419420/*421* Upload a constant force effect422* Return value:423* <0 Error code424* 0 Ok, effect created or updated425* 1 effect did not change since last upload, and no packet was therefore sent426*/427int iforce_upload_constant(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)428{429int core_id = effect->id;430struct iforce_core_effect* core_effect = iforce->core_effects + core_id;431struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);432struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);433int param1_err = 1;434int param2_err = 1;435int core_err = 0;436437if (!old || need_magnitude_modifier(iforce, old, effect)) {438param1_err = make_magnitude_modifier(iforce, mod1_chunk,439old != NULL,440effect->u.constant.level);441if (param1_err)442return param1_err;443set_bit(FF_MOD1_IS_USED, core_effect->flags);444}445446if (!old || need_envelope_modifier(iforce, old, effect)) {447param2_err = make_envelope_modifier(iforce, mod2_chunk,448old != NULL,449effect->u.constant.envelope.attack_length,450effect->u.constant.envelope.attack_level,451effect->u.constant.envelope.fade_length,452effect->u.constant.envelope.fade_level);453if (param2_err)454return param2_err;455set_bit(FF_MOD2_IS_USED, core_effect->flags);456}457458if (!old || need_core(old, effect)) {459core_err = make_core(iforce, effect->id,460mod1_chunk->start,461mod2_chunk->start,4620x00,4630x20,464effect->replay.length,465effect->replay.delay,466effect->trigger.button,467effect->trigger.interval,468effect->direction);469}470471/* If one of the parameter creation failed, we already returned an472* error code.473* If the core creation failed, we return its error code.474* Else: if one parameter at least was created, we return 0475* else we return 1;476*/477return core_err < 0 ? core_err : (param1_err && param2_err);478}479480/*481* Upload an condition effect. Those are for example friction, inertia, springs...482*/483int iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)484{485int core_id = effect->id;486struct iforce_core_effect* core_effect = iforce->core_effects + core_id;487struct resource* mod1_chunk = &(core_effect->mod1_chunk);488struct resource* mod2_chunk = &(core_effect->mod2_chunk);489u8 type;490int param_err = 1;491int core_err = 0;492493switch (effect->type) {494case FF_SPRING: type = 0x40; break;495case FF_DAMPER: type = 0x41; break;496default: return -1;497}498499if (!old || need_condition_modifier(iforce, old, effect)) {500param_err = make_condition_modifier(iforce, mod1_chunk,501old != NULL,502effect->u.condition[0].right_saturation,503effect->u.condition[0].left_saturation,504effect->u.condition[0].right_coeff,505effect->u.condition[0].left_coeff,506effect->u.condition[0].deadband,507effect->u.condition[0].center);508if (param_err)509return param_err;510set_bit(FF_MOD1_IS_USED, core_effect->flags);511512param_err = make_condition_modifier(iforce, mod2_chunk,513old != NULL,514effect->u.condition[1].right_saturation,515effect->u.condition[1].left_saturation,516effect->u.condition[1].right_coeff,517effect->u.condition[1].left_coeff,518effect->u.condition[1].deadband,519effect->u.condition[1].center);520if (param_err)521return param_err;522set_bit(FF_MOD2_IS_USED, core_effect->flags);523524}525526if (!old || need_core(old, effect)) {527core_err = make_core(iforce, effect->id,528mod1_chunk->start, mod2_chunk->start,529type, 0xc0,530effect->replay.length, effect->replay.delay,531effect->trigger.button, effect->trigger.interval,532effect->direction);533}534535/* If the parameter creation failed, we already returned an536* error code.537* If the core creation failed, we return its error code.538* Else: if a parameter was created, we return 0539* else we return 1;540*/541return core_err < 0 ? core_err : param_err;542}543544545