18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
48c2ecf20Sopenharmony_ci *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  USB/RS232 I-Force joysticks and wheels.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "iforce.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * Set the magnitude of a constant force effect
138c2ecf20Sopenharmony_ci * Return error code
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Note: caller must ensure exclusive access to device
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic int make_magnitude_modifier(struct iforce* iforce,
198c2ecf20Sopenharmony_ci	struct resource* mod_chunk, int no_alloc, __s16 level)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	unsigned char data[3];
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	if (!no_alloc) {
248c2ecf20Sopenharmony_ci		mutex_lock(&iforce->mem_mutex);
258c2ecf20Sopenharmony_ci		if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
268c2ecf20Sopenharmony_ci			iforce->device_memory.start, iforce->device_memory.end, 2L,
278c2ecf20Sopenharmony_ci			NULL, NULL)) {
288c2ecf20Sopenharmony_ci			mutex_unlock(&iforce->mem_mutex);
298c2ecf20Sopenharmony_ci			return -ENOSPC;
308c2ecf20Sopenharmony_ci		}
318c2ecf20Sopenharmony_ci		mutex_unlock(&iforce->mem_mutex);
328c2ecf20Sopenharmony_ci	}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	data[0] = LO(mod_chunk->start);
358c2ecf20Sopenharmony_ci	data[1] = HI(mod_chunk->start);
368c2ecf20Sopenharmony_ci	data[2] = HIFIX80(level);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	iforce_dump_packet(iforce, "magnitude", FF_CMD_MAGNITUDE, data);
418c2ecf20Sopenharmony_ci	return 0;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/*
458c2ecf20Sopenharmony_ci * Upload the component of an effect dealing with the period, phase and magnitude
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int make_period_modifier(struct iforce* iforce,
498c2ecf20Sopenharmony_ci	struct resource* mod_chunk, int no_alloc,
508c2ecf20Sopenharmony_ci	__s16 magnitude, __s16 offset, u16 period, u16 phase)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	unsigned char data[7];
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	period = TIME_SCALE(period);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (!no_alloc) {
578c2ecf20Sopenharmony_ci		mutex_lock(&iforce->mem_mutex);
588c2ecf20Sopenharmony_ci		if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
598c2ecf20Sopenharmony_ci			iforce->device_memory.start, iforce->device_memory.end, 2L,
608c2ecf20Sopenharmony_ci			NULL, NULL)) {
618c2ecf20Sopenharmony_ci			mutex_unlock(&iforce->mem_mutex);
628c2ecf20Sopenharmony_ci			return -ENOSPC;
638c2ecf20Sopenharmony_ci		}
648c2ecf20Sopenharmony_ci		mutex_unlock(&iforce->mem_mutex);
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	data[0] = LO(mod_chunk->start);
688c2ecf20Sopenharmony_ci	data[1] = HI(mod_chunk->start);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	data[2] = HIFIX80(magnitude);
718c2ecf20Sopenharmony_ci	data[3] = HIFIX80(offset);
728c2ecf20Sopenharmony_ci	data[4] = HI(phase);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	data[5] = LO(period);
758c2ecf20Sopenharmony_ci	data[6] = HI(period);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	iforce_send_packet(iforce, FF_CMD_PERIOD, data);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return 0;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/*
838c2ecf20Sopenharmony_ci * Uploads the part of an effect setting the envelope of the force
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int make_envelope_modifier(struct iforce* iforce,
878c2ecf20Sopenharmony_ci	struct resource* mod_chunk, int no_alloc,
888c2ecf20Sopenharmony_ci	u16 attack_duration, __s16 initial_level,
898c2ecf20Sopenharmony_ci	u16 fade_duration, __s16 final_level)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	unsigned char data[8];
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	attack_duration = TIME_SCALE(attack_duration);
948c2ecf20Sopenharmony_ci	fade_duration = TIME_SCALE(fade_duration);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (!no_alloc) {
978c2ecf20Sopenharmony_ci		mutex_lock(&iforce->mem_mutex);
988c2ecf20Sopenharmony_ci		if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
998c2ecf20Sopenharmony_ci			iforce->device_memory.start, iforce->device_memory.end, 2L,
1008c2ecf20Sopenharmony_ci			NULL, NULL)) {
1018c2ecf20Sopenharmony_ci			mutex_unlock(&iforce->mem_mutex);
1028c2ecf20Sopenharmony_ci			return -ENOSPC;
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci		mutex_unlock(&iforce->mem_mutex);
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	data[0] = LO(mod_chunk->start);
1088c2ecf20Sopenharmony_ci	data[1] = HI(mod_chunk->start);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	data[2] = LO(attack_duration);
1118c2ecf20Sopenharmony_ci	data[3] = HI(attack_duration);
1128c2ecf20Sopenharmony_ci	data[4] = HI(initial_level);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	data[5] = LO(fade_duration);
1158c2ecf20Sopenharmony_ci	data[6] = HI(fade_duration);
1168c2ecf20Sopenharmony_ci	data[7] = HI(final_level);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	iforce_send_packet(iforce, FF_CMD_ENVELOPE, data);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/*
1248c2ecf20Sopenharmony_ci * Component of spring, friction, inertia... effects
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int make_condition_modifier(struct iforce* iforce,
1288c2ecf20Sopenharmony_ci	struct resource* mod_chunk, int no_alloc,
1298c2ecf20Sopenharmony_ci	__u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	unsigned char data[10];
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (!no_alloc) {
1348c2ecf20Sopenharmony_ci		mutex_lock(&iforce->mem_mutex);
1358c2ecf20Sopenharmony_ci		if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
1368c2ecf20Sopenharmony_ci			iforce->device_memory.start, iforce->device_memory.end, 2L,
1378c2ecf20Sopenharmony_ci			NULL, NULL)) {
1388c2ecf20Sopenharmony_ci			mutex_unlock(&iforce->mem_mutex);
1398c2ecf20Sopenharmony_ci			return -ENOSPC;
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci		mutex_unlock(&iforce->mem_mutex);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	data[0] = LO(mod_chunk->start);
1458c2ecf20Sopenharmony_ci	data[1] = HI(mod_chunk->start);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	data[2] = (100 * rk) >> 15;	/* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */
1488c2ecf20Sopenharmony_ci	data[3] = (100 * lk) >> 15; /* This code is incorrect on cpus lacking arith shift */
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	center = (500 * center) >> 15;
1518c2ecf20Sopenharmony_ci	data[4] = LO(center);
1528c2ecf20Sopenharmony_ci	data[5] = HI(center);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	db = (1000 * db) >> 16;
1558c2ecf20Sopenharmony_ci	data[6] = LO(db);
1568c2ecf20Sopenharmony_ci	data[7] = HI(db);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	data[8] = (100 * rsat) >> 16;
1598c2ecf20Sopenharmony_ci	data[9] = (100 * lsat) >> 16;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	iforce_send_packet(iforce, FF_CMD_CONDITION, data);
1628c2ecf20Sopenharmony_ci	iforce_dump_packet(iforce, "condition", FF_CMD_CONDITION, data);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic unsigned char find_button(struct iforce *iforce, signed short button)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	int i;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	for (i = 1; iforce->type->btn[i] >= 0; i++)
1728c2ecf20Sopenharmony_ci		if (iforce->type->btn[i] == button)
1738c2ecf20Sopenharmony_ci			return i + 1;
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/*
1788c2ecf20Sopenharmony_ci * Analyse the changes in an effect, and tell if we need to send an condition
1798c2ecf20Sopenharmony_ci * parameter packet
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_cistatic int need_condition_modifier(struct iforce *iforce,
1828c2ecf20Sopenharmony_ci				   struct ff_effect *old,
1838c2ecf20Sopenharmony_ci				   struct ff_effect *new)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	int ret = 0;
1868c2ecf20Sopenharmony_ci	int i;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (new->type != FF_SPRING && new->type != FF_FRICTION) {
1898c2ecf20Sopenharmony_ci		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
1908c2ecf20Sopenharmony_ci			 __func__);
1918c2ecf20Sopenharmony_ci		return 0;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
1958c2ecf20Sopenharmony_ci		ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation
1968c2ecf20Sopenharmony_ci			|| old->u.condition[i].left_saturation != new->u.condition[i].left_saturation
1978c2ecf20Sopenharmony_ci			|| old->u.condition[i].right_coeff != new->u.condition[i].right_coeff
1988c2ecf20Sopenharmony_ci			|| old->u.condition[i].left_coeff != new->u.condition[i].left_coeff
1998c2ecf20Sopenharmony_ci			|| old->u.condition[i].deadband != new->u.condition[i].deadband
2008c2ecf20Sopenharmony_ci			|| old->u.condition[i].center != new->u.condition[i].center;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	return ret;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/*
2068c2ecf20Sopenharmony_ci * Analyse the changes in an effect, and tell if we need to send a magnitude
2078c2ecf20Sopenharmony_ci * parameter packet
2088c2ecf20Sopenharmony_ci */
2098c2ecf20Sopenharmony_cistatic int need_magnitude_modifier(struct iforce *iforce,
2108c2ecf20Sopenharmony_ci				   struct ff_effect *old,
2118c2ecf20Sopenharmony_ci				   struct ff_effect *effect)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	if (effect->type != FF_CONSTANT) {
2148c2ecf20Sopenharmony_ci		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
2158c2ecf20Sopenharmony_ci			 __func__);
2168c2ecf20Sopenharmony_ci		return 0;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return old->u.constant.level != effect->u.constant.level;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/*
2238c2ecf20Sopenharmony_ci * Analyse the changes in an effect, and tell if we need to send an envelope
2248c2ecf20Sopenharmony_ci * parameter packet
2258c2ecf20Sopenharmony_ci */
2268c2ecf20Sopenharmony_cistatic int need_envelope_modifier(struct iforce *iforce, struct ff_effect *old,
2278c2ecf20Sopenharmony_ci				  struct ff_effect *effect)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	switch (effect->type) {
2308c2ecf20Sopenharmony_ci	case FF_CONSTANT:
2318c2ecf20Sopenharmony_ci		if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length
2328c2ecf20Sopenharmony_ci		|| old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level
2338c2ecf20Sopenharmony_ci		|| old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length
2348c2ecf20Sopenharmony_ci		|| old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level)
2358c2ecf20Sopenharmony_ci			return 1;
2368c2ecf20Sopenharmony_ci		break;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	case FF_PERIODIC:
2398c2ecf20Sopenharmony_ci		if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length
2408c2ecf20Sopenharmony_ci		|| old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level
2418c2ecf20Sopenharmony_ci		|| old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length
2428c2ecf20Sopenharmony_ci		|| old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level)
2438c2ecf20Sopenharmony_ci			return 1;
2448c2ecf20Sopenharmony_ci		break;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	default:
2478c2ecf20Sopenharmony_ci		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
2488c2ecf20Sopenharmony_ci			 __func__);
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return 0;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/*
2558c2ecf20Sopenharmony_ci * Analyse the changes in an effect, and tell if we need to send a periodic
2568c2ecf20Sopenharmony_ci * parameter effect
2578c2ecf20Sopenharmony_ci */
2588c2ecf20Sopenharmony_cistatic int need_period_modifier(struct iforce *iforce, struct ff_effect *old,
2598c2ecf20Sopenharmony_ci				struct ff_effect *new)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	if (new->type != FF_PERIODIC) {
2628c2ecf20Sopenharmony_ci		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
2638c2ecf20Sopenharmony_ci			 __func__);
2648c2ecf20Sopenharmony_ci		return 0;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	return (old->u.periodic.period != new->u.periodic.period
2678c2ecf20Sopenharmony_ci		|| old->u.periodic.magnitude != new->u.periodic.magnitude
2688c2ecf20Sopenharmony_ci		|| old->u.periodic.offset != new->u.periodic.offset
2698c2ecf20Sopenharmony_ci		|| old->u.periodic.phase != new->u.periodic.phase);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/*
2738c2ecf20Sopenharmony_ci * Analyse the changes in an effect, and tell if we need to send an effect
2748c2ecf20Sopenharmony_ci * packet
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_cistatic int need_core(struct ff_effect *old, struct ff_effect *new)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	if (old->direction != new->direction
2798c2ecf20Sopenharmony_ci		|| old->trigger.button != new->trigger.button
2808c2ecf20Sopenharmony_ci		|| old->trigger.interval != new->trigger.interval
2818c2ecf20Sopenharmony_ci		|| old->replay.length != new->replay.length
2828c2ecf20Sopenharmony_ci		|| old->replay.delay != new->replay.delay)
2838c2ecf20Sopenharmony_ci		return 1;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci/*
2888c2ecf20Sopenharmony_ci * Send the part common to all effects to the device
2898c2ecf20Sopenharmony_ci */
2908c2ecf20Sopenharmony_cistatic int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
2918c2ecf20Sopenharmony_ci	u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,
2928c2ecf20Sopenharmony_ci	u16 interval, u16 direction)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	unsigned char data[14];
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	duration = TIME_SCALE(duration);
2978c2ecf20Sopenharmony_ci	delay    = TIME_SCALE(delay);
2988c2ecf20Sopenharmony_ci	interval = TIME_SCALE(interval);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	data[0]  = LO(id);
3018c2ecf20Sopenharmony_ci	data[1]  = effect_type;
3028c2ecf20Sopenharmony_ci	data[2]  = LO(axes) | find_button(iforce, button);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	data[3]  = LO(duration);
3058c2ecf20Sopenharmony_ci	data[4]  = HI(duration);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	data[5]  = HI(direction);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	data[6]  = LO(interval);
3108c2ecf20Sopenharmony_ci	data[7]  = HI(interval);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	data[8]  = LO(mod_id1);
3138c2ecf20Sopenharmony_ci	data[9]  = HI(mod_id1);
3148c2ecf20Sopenharmony_ci	data[10] = LO(mod_id2);
3158c2ecf20Sopenharmony_ci	data[11] = HI(mod_id2);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	data[12] = LO(delay);
3188c2ecf20Sopenharmony_ci	data[13] = HI(delay);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* Stop effect */
3218c2ecf20Sopenharmony_ci/*	iforce_control_playback(iforce, id, 0);*/
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	iforce_send_packet(iforce, FF_CMD_EFFECT, data);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* If needed, restart effect */
3268c2ecf20Sopenharmony_ci	if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) {
3278c2ecf20Sopenharmony_ci		/* BUG: perhaps we should replay n times, instead of 1. But we do not know n */
3288c2ecf20Sopenharmony_ci		iforce_control_playback(iforce, id, 1);
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	return 0;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/*
3358c2ecf20Sopenharmony_ci * Upload a periodic effect to the device
3368c2ecf20Sopenharmony_ci * See also iforce_upload_constant.
3378c2ecf20Sopenharmony_ci */
3388c2ecf20Sopenharmony_ciint iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	u8 wave_code;
3418c2ecf20Sopenharmony_ci	int core_id = effect->id;
3428c2ecf20Sopenharmony_ci	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
3438c2ecf20Sopenharmony_ci	struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
3448c2ecf20Sopenharmony_ci	struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
3458c2ecf20Sopenharmony_ci	int param1_err = 1;
3468c2ecf20Sopenharmony_ci	int param2_err = 1;
3478c2ecf20Sopenharmony_ci	int core_err = 0;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	if (!old || need_period_modifier(iforce, old, effect)) {
3508c2ecf20Sopenharmony_ci		param1_err = make_period_modifier(iforce, mod1_chunk,
3518c2ecf20Sopenharmony_ci			old != NULL,
3528c2ecf20Sopenharmony_ci			effect->u.periodic.magnitude, effect->u.periodic.offset,
3538c2ecf20Sopenharmony_ci			effect->u.periodic.period, effect->u.periodic.phase);
3548c2ecf20Sopenharmony_ci		if (param1_err)
3558c2ecf20Sopenharmony_ci			return param1_err;
3568c2ecf20Sopenharmony_ci		set_bit(FF_MOD1_IS_USED, core_effect->flags);
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (!old || need_envelope_modifier(iforce, old, effect)) {
3608c2ecf20Sopenharmony_ci		param2_err = make_envelope_modifier(iforce, mod2_chunk,
3618c2ecf20Sopenharmony_ci			old !=NULL,
3628c2ecf20Sopenharmony_ci			effect->u.periodic.envelope.attack_length,
3638c2ecf20Sopenharmony_ci			effect->u.periodic.envelope.attack_level,
3648c2ecf20Sopenharmony_ci			effect->u.periodic.envelope.fade_length,
3658c2ecf20Sopenharmony_ci			effect->u.periodic.envelope.fade_level);
3668c2ecf20Sopenharmony_ci		if (param2_err)
3678c2ecf20Sopenharmony_ci			return param2_err;
3688c2ecf20Sopenharmony_ci		set_bit(FF_MOD2_IS_USED, core_effect->flags);
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	switch (effect->u.periodic.waveform) {
3728c2ecf20Sopenharmony_ci	case FF_SQUARE:		wave_code = 0x20; break;
3738c2ecf20Sopenharmony_ci	case FF_TRIANGLE:	wave_code = 0x21; break;
3748c2ecf20Sopenharmony_ci	case FF_SINE:		wave_code = 0x22; break;
3758c2ecf20Sopenharmony_ci	case FF_SAW_UP:		wave_code = 0x23; break;
3768c2ecf20Sopenharmony_ci	case FF_SAW_DOWN:	wave_code = 0x24; break;
3778c2ecf20Sopenharmony_ci	default:		wave_code = 0x20; break;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (!old || need_core(old, effect)) {
3818c2ecf20Sopenharmony_ci		core_err = make_core(iforce, effect->id,
3828c2ecf20Sopenharmony_ci			mod1_chunk->start,
3838c2ecf20Sopenharmony_ci			mod2_chunk->start,
3848c2ecf20Sopenharmony_ci			wave_code,
3858c2ecf20Sopenharmony_ci			0x20,
3868c2ecf20Sopenharmony_ci			effect->replay.length,
3878c2ecf20Sopenharmony_ci			effect->replay.delay,
3888c2ecf20Sopenharmony_ci			effect->trigger.button,
3898c2ecf20Sopenharmony_ci			effect->trigger.interval,
3908c2ecf20Sopenharmony_ci			effect->direction);
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	/* If one of the parameter creation failed, we already returned an
3948c2ecf20Sopenharmony_ci	 * error code.
3958c2ecf20Sopenharmony_ci	 * If the core creation failed, we return its error code.
3968c2ecf20Sopenharmony_ci	 * Else: if one parameter at least was created, we return 0
3978c2ecf20Sopenharmony_ci	 *       else we return 1;
3988c2ecf20Sopenharmony_ci	 */
3998c2ecf20Sopenharmony_ci	return core_err < 0 ? core_err : (param1_err && param2_err);
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci/*
4038c2ecf20Sopenharmony_ci * Upload a constant force effect
4048c2ecf20Sopenharmony_ci * Return value:
4058c2ecf20Sopenharmony_ci *  <0 Error code
4068c2ecf20Sopenharmony_ci *  0 Ok, effect created or updated
4078c2ecf20Sopenharmony_ci *  1 effect did not change since last upload, and no packet was therefore sent
4088c2ecf20Sopenharmony_ci */
4098c2ecf20Sopenharmony_ciint iforce_upload_constant(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	int core_id = effect->id;
4128c2ecf20Sopenharmony_ci	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
4138c2ecf20Sopenharmony_ci	struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
4148c2ecf20Sopenharmony_ci	struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
4158c2ecf20Sopenharmony_ci	int param1_err = 1;
4168c2ecf20Sopenharmony_ci	int param2_err = 1;
4178c2ecf20Sopenharmony_ci	int core_err = 0;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (!old || need_magnitude_modifier(iforce, old, effect)) {
4208c2ecf20Sopenharmony_ci		param1_err = make_magnitude_modifier(iforce, mod1_chunk,
4218c2ecf20Sopenharmony_ci			old != NULL,
4228c2ecf20Sopenharmony_ci			effect->u.constant.level);
4238c2ecf20Sopenharmony_ci		if (param1_err)
4248c2ecf20Sopenharmony_ci			return param1_err;
4258c2ecf20Sopenharmony_ci		set_bit(FF_MOD1_IS_USED, core_effect->flags);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (!old || need_envelope_modifier(iforce, old, effect)) {
4298c2ecf20Sopenharmony_ci		param2_err = make_envelope_modifier(iforce, mod2_chunk,
4308c2ecf20Sopenharmony_ci			old != NULL,
4318c2ecf20Sopenharmony_ci			effect->u.constant.envelope.attack_length,
4328c2ecf20Sopenharmony_ci			effect->u.constant.envelope.attack_level,
4338c2ecf20Sopenharmony_ci			effect->u.constant.envelope.fade_length,
4348c2ecf20Sopenharmony_ci			effect->u.constant.envelope.fade_level);
4358c2ecf20Sopenharmony_ci		if (param2_err)
4368c2ecf20Sopenharmony_ci			return param2_err;
4378c2ecf20Sopenharmony_ci		set_bit(FF_MOD2_IS_USED, core_effect->flags);
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (!old || need_core(old, effect)) {
4418c2ecf20Sopenharmony_ci		core_err = make_core(iforce, effect->id,
4428c2ecf20Sopenharmony_ci			mod1_chunk->start,
4438c2ecf20Sopenharmony_ci			mod2_chunk->start,
4448c2ecf20Sopenharmony_ci			0x00,
4458c2ecf20Sopenharmony_ci			0x20,
4468c2ecf20Sopenharmony_ci			effect->replay.length,
4478c2ecf20Sopenharmony_ci			effect->replay.delay,
4488c2ecf20Sopenharmony_ci			effect->trigger.button,
4498c2ecf20Sopenharmony_ci			effect->trigger.interval,
4508c2ecf20Sopenharmony_ci			effect->direction);
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* If one of the parameter creation failed, we already returned an
4548c2ecf20Sopenharmony_ci	 * error code.
4558c2ecf20Sopenharmony_ci	 * If the core creation failed, we return its error code.
4568c2ecf20Sopenharmony_ci	 * Else: if one parameter at least was created, we return 0
4578c2ecf20Sopenharmony_ci	 *       else we return 1;
4588c2ecf20Sopenharmony_ci	 */
4598c2ecf20Sopenharmony_ci	return core_err < 0 ? core_err : (param1_err && param2_err);
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci/*
4638c2ecf20Sopenharmony_ci * Upload an condition effect. Those are for example friction, inertia, springs...
4648c2ecf20Sopenharmony_ci */
4658c2ecf20Sopenharmony_ciint iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	int core_id = effect->id;
4688c2ecf20Sopenharmony_ci	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
4698c2ecf20Sopenharmony_ci	struct resource* mod1_chunk = &(core_effect->mod1_chunk);
4708c2ecf20Sopenharmony_ci	struct resource* mod2_chunk = &(core_effect->mod2_chunk);
4718c2ecf20Sopenharmony_ci	u8 type;
4728c2ecf20Sopenharmony_ci	int param_err = 1;
4738c2ecf20Sopenharmony_ci	int core_err = 0;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	switch (effect->type) {
4768c2ecf20Sopenharmony_ci	case FF_SPRING:	type = 0x40; break;
4778c2ecf20Sopenharmony_ci	case FF_DAMPER:	type = 0x41; break;
4788c2ecf20Sopenharmony_ci	default: return -1;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (!old || need_condition_modifier(iforce, old, effect)) {
4828c2ecf20Sopenharmony_ci		param_err = make_condition_modifier(iforce, mod1_chunk,
4838c2ecf20Sopenharmony_ci			old != NULL,
4848c2ecf20Sopenharmony_ci			effect->u.condition[0].right_saturation,
4858c2ecf20Sopenharmony_ci			effect->u.condition[0].left_saturation,
4868c2ecf20Sopenharmony_ci			effect->u.condition[0].right_coeff,
4878c2ecf20Sopenharmony_ci			effect->u.condition[0].left_coeff,
4888c2ecf20Sopenharmony_ci			effect->u.condition[0].deadband,
4898c2ecf20Sopenharmony_ci			effect->u.condition[0].center);
4908c2ecf20Sopenharmony_ci		if (param_err)
4918c2ecf20Sopenharmony_ci			return param_err;
4928c2ecf20Sopenharmony_ci		set_bit(FF_MOD1_IS_USED, core_effect->flags);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		param_err = make_condition_modifier(iforce, mod2_chunk,
4958c2ecf20Sopenharmony_ci			old != NULL,
4968c2ecf20Sopenharmony_ci			effect->u.condition[1].right_saturation,
4978c2ecf20Sopenharmony_ci			effect->u.condition[1].left_saturation,
4988c2ecf20Sopenharmony_ci			effect->u.condition[1].right_coeff,
4998c2ecf20Sopenharmony_ci			effect->u.condition[1].left_coeff,
5008c2ecf20Sopenharmony_ci			effect->u.condition[1].deadband,
5018c2ecf20Sopenharmony_ci			effect->u.condition[1].center);
5028c2ecf20Sopenharmony_ci		if (param_err)
5038c2ecf20Sopenharmony_ci			return param_err;
5048c2ecf20Sopenharmony_ci		set_bit(FF_MOD2_IS_USED, core_effect->flags);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if (!old || need_core(old, effect)) {
5098c2ecf20Sopenharmony_ci		core_err = make_core(iforce, effect->id,
5108c2ecf20Sopenharmony_ci			mod1_chunk->start, mod2_chunk->start,
5118c2ecf20Sopenharmony_ci			type, 0xc0,
5128c2ecf20Sopenharmony_ci			effect->replay.length, effect->replay.delay,
5138c2ecf20Sopenharmony_ci			effect->trigger.button, effect->trigger.interval,
5148c2ecf20Sopenharmony_ci			effect->direction);
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* If the parameter creation failed, we already returned an
5188c2ecf20Sopenharmony_ci	 * error code.
5198c2ecf20Sopenharmony_ci	 * If the core creation failed, we return its error code.
5208c2ecf20Sopenharmony_ci	 * Else: if a parameter  was created, we return 0
5218c2ecf20Sopenharmony_ci	 *       else we return 1;
5228c2ecf20Sopenharmony_ci	 */
5238c2ecf20Sopenharmony_ci	return core_err < 0 ? core_err : param_err;
5248c2ecf20Sopenharmony_ci}
525