162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
462306a36Sopenharmony_ci *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  USB/RS232 I-Force joysticks and wheels.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "iforce.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Set the magnitude of a constant force effect
1362306a36Sopenharmony_ci * Return error code
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Note: caller must ensure exclusive access to device
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int make_magnitude_modifier(struct iforce* iforce,
1962306a36Sopenharmony_ci	struct resource* mod_chunk, int no_alloc, __s16 level)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	unsigned char data[3];
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	if (!no_alloc) {
2462306a36Sopenharmony_ci		mutex_lock(&iforce->mem_mutex);
2562306a36Sopenharmony_ci		if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
2662306a36Sopenharmony_ci			iforce->device_memory.start, iforce->device_memory.end, 2L,
2762306a36Sopenharmony_ci			NULL, NULL)) {
2862306a36Sopenharmony_ci			mutex_unlock(&iforce->mem_mutex);
2962306a36Sopenharmony_ci			return -ENOSPC;
3062306a36Sopenharmony_ci		}
3162306a36Sopenharmony_ci		mutex_unlock(&iforce->mem_mutex);
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	data[0] = LO(mod_chunk->start);
3562306a36Sopenharmony_ci	data[1] = HI(mod_chunk->start);
3662306a36Sopenharmony_ci	data[2] = HIFIX80(level);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	iforce_dump_packet(iforce, "magnitude", FF_CMD_MAGNITUDE, data);
4162306a36Sopenharmony_ci	return 0;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/*
4562306a36Sopenharmony_ci * Upload the component of an effect dealing with the period, phase and magnitude
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int make_period_modifier(struct iforce* iforce,
4962306a36Sopenharmony_ci	struct resource* mod_chunk, int no_alloc,
5062306a36Sopenharmony_ci	__s16 magnitude, __s16 offset, u16 period, u16 phase)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	unsigned char data[7];
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	period = TIME_SCALE(period);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (!no_alloc) {
5762306a36Sopenharmony_ci		mutex_lock(&iforce->mem_mutex);
5862306a36Sopenharmony_ci		if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
5962306a36Sopenharmony_ci			iforce->device_memory.start, iforce->device_memory.end, 2L,
6062306a36Sopenharmony_ci			NULL, NULL)) {
6162306a36Sopenharmony_ci			mutex_unlock(&iforce->mem_mutex);
6262306a36Sopenharmony_ci			return -ENOSPC;
6362306a36Sopenharmony_ci		}
6462306a36Sopenharmony_ci		mutex_unlock(&iforce->mem_mutex);
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	data[0] = LO(mod_chunk->start);
6862306a36Sopenharmony_ci	data[1] = HI(mod_chunk->start);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	data[2] = HIFIX80(magnitude);
7162306a36Sopenharmony_ci	data[3] = HIFIX80(offset);
7262306a36Sopenharmony_ci	data[4] = HI(phase);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	data[5] = LO(period);
7562306a36Sopenharmony_ci	data[6] = HI(period);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	iforce_send_packet(iforce, FF_CMD_PERIOD, data);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/*
8362306a36Sopenharmony_ci * Uploads the part of an effect setting the envelope of the force
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int make_envelope_modifier(struct iforce* iforce,
8762306a36Sopenharmony_ci	struct resource* mod_chunk, int no_alloc,
8862306a36Sopenharmony_ci	u16 attack_duration, __s16 initial_level,
8962306a36Sopenharmony_ci	u16 fade_duration, __s16 final_level)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	unsigned char data[8];
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	attack_duration = TIME_SCALE(attack_duration);
9462306a36Sopenharmony_ci	fade_duration = TIME_SCALE(fade_duration);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (!no_alloc) {
9762306a36Sopenharmony_ci		mutex_lock(&iforce->mem_mutex);
9862306a36Sopenharmony_ci		if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
9962306a36Sopenharmony_ci			iforce->device_memory.start, iforce->device_memory.end, 2L,
10062306a36Sopenharmony_ci			NULL, NULL)) {
10162306a36Sopenharmony_ci			mutex_unlock(&iforce->mem_mutex);
10262306a36Sopenharmony_ci			return -ENOSPC;
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci		mutex_unlock(&iforce->mem_mutex);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	data[0] = LO(mod_chunk->start);
10862306a36Sopenharmony_ci	data[1] = HI(mod_chunk->start);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	data[2] = LO(attack_duration);
11162306a36Sopenharmony_ci	data[3] = HI(attack_duration);
11262306a36Sopenharmony_ci	data[4] = HI(initial_level);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	data[5] = LO(fade_duration);
11562306a36Sopenharmony_ci	data[6] = HI(fade_duration);
11662306a36Sopenharmony_ci	data[7] = HI(final_level);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	iforce_send_packet(iforce, FF_CMD_ENVELOPE, data);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return 0;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/*
12462306a36Sopenharmony_ci * Component of spring, friction, inertia... effects
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int make_condition_modifier(struct iforce* iforce,
12862306a36Sopenharmony_ci	struct resource* mod_chunk, int no_alloc,
12962306a36Sopenharmony_ci	__u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	unsigned char data[10];
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (!no_alloc) {
13462306a36Sopenharmony_ci		mutex_lock(&iforce->mem_mutex);
13562306a36Sopenharmony_ci		if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
13662306a36Sopenharmony_ci			iforce->device_memory.start, iforce->device_memory.end, 2L,
13762306a36Sopenharmony_ci			NULL, NULL)) {
13862306a36Sopenharmony_ci			mutex_unlock(&iforce->mem_mutex);
13962306a36Sopenharmony_ci			return -ENOSPC;
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci		mutex_unlock(&iforce->mem_mutex);
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	data[0] = LO(mod_chunk->start);
14562306a36Sopenharmony_ci	data[1] = HI(mod_chunk->start);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	data[2] = (100 * rk) >> 15;	/* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */
14862306a36Sopenharmony_ci	data[3] = (100 * lk) >> 15; /* This code is incorrect on cpus lacking arith shift */
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	center = (500 * center) >> 15;
15162306a36Sopenharmony_ci	data[4] = LO(center);
15262306a36Sopenharmony_ci	data[5] = HI(center);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	db = (1000 * db) >> 16;
15562306a36Sopenharmony_ci	data[6] = LO(db);
15662306a36Sopenharmony_ci	data[7] = HI(db);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	data[8] = (100 * rsat) >> 16;
15962306a36Sopenharmony_ci	data[9] = (100 * lsat) >> 16;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	iforce_send_packet(iforce, FF_CMD_CONDITION, data);
16262306a36Sopenharmony_ci	iforce_dump_packet(iforce, "condition", FF_CMD_CONDITION, data);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return 0;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic unsigned char find_button(struct iforce *iforce, signed short button)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	int i;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	for (i = 1; iforce->type->btn[i] >= 0; i++)
17262306a36Sopenharmony_ci		if (iforce->type->btn[i] == button)
17362306a36Sopenharmony_ci			return i + 1;
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/*
17862306a36Sopenharmony_ci * Analyse the changes in an effect, and tell if we need to send an condition
17962306a36Sopenharmony_ci * parameter packet
18062306a36Sopenharmony_ci */
18162306a36Sopenharmony_cistatic int need_condition_modifier(struct iforce *iforce,
18262306a36Sopenharmony_ci				   struct ff_effect *old,
18362306a36Sopenharmony_ci				   struct ff_effect *new)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	int ret = 0;
18662306a36Sopenharmony_ci	int i;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (new->type != FF_SPRING && new->type != FF_FRICTION) {
18962306a36Sopenharmony_ci		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
19062306a36Sopenharmony_ci			 __func__);
19162306a36Sopenharmony_ci		return 0;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
19562306a36Sopenharmony_ci		ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation
19662306a36Sopenharmony_ci			|| old->u.condition[i].left_saturation != new->u.condition[i].left_saturation
19762306a36Sopenharmony_ci			|| old->u.condition[i].right_coeff != new->u.condition[i].right_coeff
19862306a36Sopenharmony_ci			|| old->u.condition[i].left_coeff != new->u.condition[i].left_coeff
19962306a36Sopenharmony_ci			|| old->u.condition[i].deadband != new->u.condition[i].deadband
20062306a36Sopenharmony_ci			|| old->u.condition[i].center != new->u.condition[i].center;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	return ret;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/*
20662306a36Sopenharmony_ci * Analyse the changes in an effect, and tell if we need to send a magnitude
20762306a36Sopenharmony_ci * parameter packet
20862306a36Sopenharmony_ci */
20962306a36Sopenharmony_cistatic int need_magnitude_modifier(struct iforce *iforce,
21062306a36Sopenharmony_ci				   struct ff_effect *old,
21162306a36Sopenharmony_ci				   struct ff_effect *effect)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	if (effect->type != FF_CONSTANT) {
21462306a36Sopenharmony_ci		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
21562306a36Sopenharmony_ci			 __func__);
21662306a36Sopenharmony_ci		return 0;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return old->u.constant.level != effect->u.constant.level;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/*
22362306a36Sopenharmony_ci * Analyse the changes in an effect, and tell if we need to send an envelope
22462306a36Sopenharmony_ci * parameter packet
22562306a36Sopenharmony_ci */
22662306a36Sopenharmony_cistatic int need_envelope_modifier(struct iforce *iforce, struct ff_effect *old,
22762306a36Sopenharmony_ci				  struct ff_effect *effect)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	switch (effect->type) {
23062306a36Sopenharmony_ci	case FF_CONSTANT:
23162306a36Sopenharmony_ci		if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length
23262306a36Sopenharmony_ci		|| old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level
23362306a36Sopenharmony_ci		|| old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length
23462306a36Sopenharmony_ci		|| old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level)
23562306a36Sopenharmony_ci			return 1;
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	case FF_PERIODIC:
23962306a36Sopenharmony_ci		if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length
24062306a36Sopenharmony_ci		|| old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level
24162306a36Sopenharmony_ci		|| old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length
24262306a36Sopenharmony_ci		|| old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level)
24362306a36Sopenharmony_ci			return 1;
24462306a36Sopenharmony_ci		break;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	default:
24762306a36Sopenharmony_ci		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
24862306a36Sopenharmony_ci			 __func__);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/*
25562306a36Sopenharmony_ci * Analyse the changes in an effect, and tell if we need to send a periodic
25662306a36Sopenharmony_ci * parameter effect
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_cistatic int need_period_modifier(struct iforce *iforce, struct ff_effect *old,
25962306a36Sopenharmony_ci				struct ff_effect *new)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	if (new->type != FF_PERIODIC) {
26262306a36Sopenharmony_ci		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
26362306a36Sopenharmony_ci			 __func__);
26462306a36Sopenharmony_ci		return 0;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	return (old->u.periodic.period != new->u.periodic.period
26762306a36Sopenharmony_ci		|| old->u.periodic.magnitude != new->u.periodic.magnitude
26862306a36Sopenharmony_ci		|| old->u.periodic.offset != new->u.periodic.offset
26962306a36Sopenharmony_ci		|| old->u.periodic.phase != new->u.periodic.phase);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/*
27362306a36Sopenharmony_ci * Analyse the changes in an effect, and tell if we need to send an effect
27462306a36Sopenharmony_ci * packet
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_cistatic int need_core(struct ff_effect *old, struct ff_effect *new)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	if (old->direction != new->direction
27962306a36Sopenharmony_ci		|| old->trigger.button != new->trigger.button
28062306a36Sopenharmony_ci		|| old->trigger.interval != new->trigger.interval
28162306a36Sopenharmony_ci		|| old->replay.length != new->replay.length
28262306a36Sopenharmony_ci		|| old->replay.delay != new->replay.delay)
28362306a36Sopenharmony_ci		return 1;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci/*
28862306a36Sopenharmony_ci * Send the part common to all effects to the device
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cistatic int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
29162306a36Sopenharmony_ci	u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,
29262306a36Sopenharmony_ci	u16 interval, u16 direction)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	unsigned char data[14];
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	duration = TIME_SCALE(duration);
29762306a36Sopenharmony_ci	delay    = TIME_SCALE(delay);
29862306a36Sopenharmony_ci	interval = TIME_SCALE(interval);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	data[0]  = LO(id);
30162306a36Sopenharmony_ci	data[1]  = effect_type;
30262306a36Sopenharmony_ci	data[2]  = LO(axes) | find_button(iforce, button);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	data[3]  = LO(duration);
30562306a36Sopenharmony_ci	data[4]  = HI(duration);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	data[5]  = HI(direction);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	data[6]  = LO(interval);
31062306a36Sopenharmony_ci	data[7]  = HI(interval);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	data[8]  = LO(mod_id1);
31362306a36Sopenharmony_ci	data[9]  = HI(mod_id1);
31462306a36Sopenharmony_ci	data[10] = LO(mod_id2);
31562306a36Sopenharmony_ci	data[11] = HI(mod_id2);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	data[12] = LO(delay);
31862306a36Sopenharmony_ci	data[13] = HI(delay);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Stop effect */
32162306a36Sopenharmony_ci/*	iforce_control_playback(iforce, id, 0);*/
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	iforce_send_packet(iforce, FF_CMD_EFFECT, data);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* If needed, restart effect */
32662306a36Sopenharmony_ci	if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) {
32762306a36Sopenharmony_ci		/* BUG: perhaps we should replay n times, instead of 1. But we do not know n */
32862306a36Sopenharmony_ci		iforce_control_playback(iforce, id, 1);
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return 0;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/*
33562306a36Sopenharmony_ci * Upload a periodic effect to the device
33662306a36Sopenharmony_ci * See also iforce_upload_constant.
33762306a36Sopenharmony_ci */
33862306a36Sopenharmony_ciint iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	u8 wave_code;
34162306a36Sopenharmony_ci	int core_id = effect->id;
34262306a36Sopenharmony_ci	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
34362306a36Sopenharmony_ci	struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
34462306a36Sopenharmony_ci	struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
34562306a36Sopenharmony_ci	int param1_err = 1;
34662306a36Sopenharmony_ci	int param2_err = 1;
34762306a36Sopenharmony_ci	int core_err = 0;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (!old || need_period_modifier(iforce, old, effect)) {
35062306a36Sopenharmony_ci		param1_err = make_period_modifier(iforce, mod1_chunk,
35162306a36Sopenharmony_ci			old != NULL,
35262306a36Sopenharmony_ci			effect->u.periodic.magnitude, effect->u.periodic.offset,
35362306a36Sopenharmony_ci			effect->u.periodic.period, effect->u.periodic.phase);
35462306a36Sopenharmony_ci		if (param1_err)
35562306a36Sopenharmony_ci			return param1_err;
35662306a36Sopenharmony_ci		set_bit(FF_MOD1_IS_USED, core_effect->flags);
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (!old || need_envelope_modifier(iforce, old, effect)) {
36062306a36Sopenharmony_ci		param2_err = make_envelope_modifier(iforce, mod2_chunk,
36162306a36Sopenharmony_ci			old !=NULL,
36262306a36Sopenharmony_ci			effect->u.periodic.envelope.attack_length,
36362306a36Sopenharmony_ci			effect->u.periodic.envelope.attack_level,
36462306a36Sopenharmony_ci			effect->u.periodic.envelope.fade_length,
36562306a36Sopenharmony_ci			effect->u.periodic.envelope.fade_level);
36662306a36Sopenharmony_ci		if (param2_err)
36762306a36Sopenharmony_ci			return param2_err;
36862306a36Sopenharmony_ci		set_bit(FF_MOD2_IS_USED, core_effect->flags);
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	switch (effect->u.periodic.waveform) {
37262306a36Sopenharmony_ci	case FF_SQUARE:		wave_code = 0x20; break;
37362306a36Sopenharmony_ci	case FF_TRIANGLE:	wave_code = 0x21; break;
37462306a36Sopenharmony_ci	case FF_SINE:		wave_code = 0x22; break;
37562306a36Sopenharmony_ci	case FF_SAW_UP:		wave_code = 0x23; break;
37662306a36Sopenharmony_ci	case FF_SAW_DOWN:	wave_code = 0x24; break;
37762306a36Sopenharmony_ci	default:		wave_code = 0x20; break;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (!old || need_core(old, effect)) {
38162306a36Sopenharmony_ci		core_err = make_core(iforce, effect->id,
38262306a36Sopenharmony_ci			mod1_chunk->start,
38362306a36Sopenharmony_ci			mod2_chunk->start,
38462306a36Sopenharmony_ci			wave_code,
38562306a36Sopenharmony_ci			0x20,
38662306a36Sopenharmony_ci			effect->replay.length,
38762306a36Sopenharmony_ci			effect->replay.delay,
38862306a36Sopenharmony_ci			effect->trigger.button,
38962306a36Sopenharmony_ci			effect->trigger.interval,
39062306a36Sopenharmony_ci			effect->direction);
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* If one of the parameter creation failed, we already returned an
39462306a36Sopenharmony_ci	 * error code.
39562306a36Sopenharmony_ci	 * If the core creation failed, we return its error code.
39662306a36Sopenharmony_ci	 * Else: if one parameter at least was created, we return 0
39762306a36Sopenharmony_ci	 *       else we return 1;
39862306a36Sopenharmony_ci	 */
39962306a36Sopenharmony_ci	return core_err < 0 ? core_err : (param1_err && param2_err);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci/*
40362306a36Sopenharmony_ci * Upload a constant force effect
40462306a36Sopenharmony_ci * Return value:
40562306a36Sopenharmony_ci *  <0 Error code
40662306a36Sopenharmony_ci *  0 Ok, effect created or updated
40762306a36Sopenharmony_ci *  1 effect did not change since last upload, and no packet was therefore sent
40862306a36Sopenharmony_ci */
40962306a36Sopenharmony_ciint iforce_upload_constant(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	int core_id = effect->id;
41262306a36Sopenharmony_ci	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
41362306a36Sopenharmony_ci	struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
41462306a36Sopenharmony_ci	struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
41562306a36Sopenharmony_ci	int param1_err = 1;
41662306a36Sopenharmony_ci	int param2_err = 1;
41762306a36Sopenharmony_ci	int core_err = 0;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (!old || need_magnitude_modifier(iforce, old, effect)) {
42062306a36Sopenharmony_ci		param1_err = make_magnitude_modifier(iforce, mod1_chunk,
42162306a36Sopenharmony_ci			old != NULL,
42262306a36Sopenharmony_ci			effect->u.constant.level);
42362306a36Sopenharmony_ci		if (param1_err)
42462306a36Sopenharmony_ci			return param1_err;
42562306a36Sopenharmony_ci		set_bit(FF_MOD1_IS_USED, core_effect->flags);
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (!old || need_envelope_modifier(iforce, old, effect)) {
42962306a36Sopenharmony_ci		param2_err = make_envelope_modifier(iforce, mod2_chunk,
43062306a36Sopenharmony_ci			old != NULL,
43162306a36Sopenharmony_ci			effect->u.constant.envelope.attack_length,
43262306a36Sopenharmony_ci			effect->u.constant.envelope.attack_level,
43362306a36Sopenharmony_ci			effect->u.constant.envelope.fade_length,
43462306a36Sopenharmony_ci			effect->u.constant.envelope.fade_level);
43562306a36Sopenharmony_ci		if (param2_err)
43662306a36Sopenharmony_ci			return param2_err;
43762306a36Sopenharmony_ci		set_bit(FF_MOD2_IS_USED, core_effect->flags);
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (!old || need_core(old, effect)) {
44162306a36Sopenharmony_ci		core_err = make_core(iforce, effect->id,
44262306a36Sopenharmony_ci			mod1_chunk->start,
44362306a36Sopenharmony_ci			mod2_chunk->start,
44462306a36Sopenharmony_ci			0x00,
44562306a36Sopenharmony_ci			0x20,
44662306a36Sopenharmony_ci			effect->replay.length,
44762306a36Sopenharmony_ci			effect->replay.delay,
44862306a36Sopenharmony_ci			effect->trigger.button,
44962306a36Sopenharmony_ci			effect->trigger.interval,
45062306a36Sopenharmony_ci			effect->direction);
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/* If one of the parameter creation failed, we already returned an
45462306a36Sopenharmony_ci	 * error code.
45562306a36Sopenharmony_ci	 * If the core creation failed, we return its error code.
45662306a36Sopenharmony_ci	 * Else: if one parameter at least was created, we return 0
45762306a36Sopenharmony_ci	 *       else we return 1;
45862306a36Sopenharmony_ci	 */
45962306a36Sopenharmony_ci	return core_err < 0 ? core_err : (param1_err && param2_err);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci/*
46362306a36Sopenharmony_ci * Upload an condition effect. Those are for example friction, inertia, springs...
46462306a36Sopenharmony_ci */
46562306a36Sopenharmony_ciint iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	int core_id = effect->id;
46862306a36Sopenharmony_ci	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
46962306a36Sopenharmony_ci	struct resource* mod1_chunk = &(core_effect->mod1_chunk);
47062306a36Sopenharmony_ci	struct resource* mod2_chunk = &(core_effect->mod2_chunk);
47162306a36Sopenharmony_ci	u8 type;
47262306a36Sopenharmony_ci	int param_err = 1;
47362306a36Sopenharmony_ci	int core_err = 0;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	switch (effect->type) {
47662306a36Sopenharmony_ci	case FF_SPRING:	type = 0x40; break;
47762306a36Sopenharmony_ci	case FF_DAMPER:	type = 0x41; break;
47862306a36Sopenharmony_ci	default: return -1;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (!old || need_condition_modifier(iforce, old, effect)) {
48262306a36Sopenharmony_ci		param_err = make_condition_modifier(iforce, mod1_chunk,
48362306a36Sopenharmony_ci			old != NULL,
48462306a36Sopenharmony_ci			effect->u.condition[0].right_saturation,
48562306a36Sopenharmony_ci			effect->u.condition[0].left_saturation,
48662306a36Sopenharmony_ci			effect->u.condition[0].right_coeff,
48762306a36Sopenharmony_ci			effect->u.condition[0].left_coeff,
48862306a36Sopenharmony_ci			effect->u.condition[0].deadband,
48962306a36Sopenharmony_ci			effect->u.condition[0].center);
49062306a36Sopenharmony_ci		if (param_err)
49162306a36Sopenharmony_ci			return param_err;
49262306a36Sopenharmony_ci		set_bit(FF_MOD1_IS_USED, core_effect->flags);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		param_err = make_condition_modifier(iforce, mod2_chunk,
49562306a36Sopenharmony_ci			old != NULL,
49662306a36Sopenharmony_ci			effect->u.condition[1].right_saturation,
49762306a36Sopenharmony_ci			effect->u.condition[1].left_saturation,
49862306a36Sopenharmony_ci			effect->u.condition[1].right_coeff,
49962306a36Sopenharmony_ci			effect->u.condition[1].left_coeff,
50062306a36Sopenharmony_ci			effect->u.condition[1].deadband,
50162306a36Sopenharmony_ci			effect->u.condition[1].center);
50262306a36Sopenharmony_ci		if (param_err)
50362306a36Sopenharmony_ci			return param_err;
50462306a36Sopenharmony_ci		set_bit(FF_MOD2_IS_USED, core_effect->flags);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	if (!old || need_core(old, effect)) {
50962306a36Sopenharmony_ci		core_err = make_core(iforce, effect->id,
51062306a36Sopenharmony_ci			mod1_chunk->start, mod2_chunk->start,
51162306a36Sopenharmony_ci			type, 0xc0,
51262306a36Sopenharmony_ci			effect->replay.length, effect->replay.delay,
51362306a36Sopenharmony_ci			effect->trigger.button, effect->trigger.interval,
51462306a36Sopenharmony_ci			effect->direction);
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* If the parameter creation failed, we already returned an
51862306a36Sopenharmony_ci	 * error code.
51962306a36Sopenharmony_ci	 * If the core creation failed, we return its error code.
52062306a36Sopenharmony_ci	 * Else: if a parameter  was created, we return 0
52162306a36Sopenharmony_ci	 *       else we return 1;
52262306a36Sopenharmony_ci	 */
52362306a36Sopenharmony_ci	return core_err < 0 ? core_err : param_err;
52462306a36Sopenharmony_ci}
525