Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 | 2 | /* |
1da177e4 | 3 | * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz> |
598972d4 | 4 | * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com> |
1da177e4 LT |
5 | * |
6 | * USB/RS232 I-Force joysticks and wheels. | |
7 | */ | |
8 | ||
9 | /* | |
1da177e4 LT |
10 | */ |
11 | ||
12 | #include "iforce.h" | |
13 | ||
14 | /* | |
15 | * Set the magnitude of a constant force effect | |
16 | * Return error code | |
17 | * | |
18 | * Note: caller must ensure exclusive access to device | |
19 | */ | |
20 | ||
21 | static int make_magnitude_modifier(struct iforce* iforce, | |
22 | struct resource* mod_chunk, int no_alloc, __s16 level) | |
23 | { | |
24 | unsigned char data[3]; | |
25 | ||
26 | if (!no_alloc) { | |
72ba9f0c | 27 | mutex_lock(&iforce->mem_mutex); |
1da177e4 LT |
28 | if (allocate_resource(&(iforce->device_memory), mod_chunk, 2, |
29 | iforce->device_memory.start, iforce->device_memory.end, 2L, | |
30 | NULL, NULL)) { | |
72ba9f0c | 31 | mutex_unlock(&iforce->mem_mutex); |
fe65b97a | 32 | return -ENOSPC; |
1da177e4 | 33 | } |
72ba9f0c | 34 | mutex_unlock(&iforce->mem_mutex); |
1da177e4 LT |
35 | } |
36 | ||
37 | data[0] = LO(mod_chunk->start); | |
38 | data[1] = HI(mod_chunk->start); | |
39 | data[2] = HIFIX80(level); | |
40 | ||
41 | iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data); | |
42 | ||
305180bc | 43 | iforce_dump_packet(iforce, "magnitude", FF_CMD_MAGNITUDE, data); |
1da177e4 LT |
44 | return 0; |
45 | } | |
46 | ||
47 | /* | |
48 | * Upload the component of an effect dealing with the period, phase and magnitude | |
49 | */ | |
50 | ||
51 | static int make_period_modifier(struct iforce* iforce, | |
52 | struct resource* mod_chunk, int no_alloc, | |
53 | __s16 magnitude, __s16 offset, u16 period, u16 phase) | |
54 | { | |
55 | unsigned char data[7]; | |
56 | ||
57 | period = TIME_SCALE(period); | |
58 | ||
59 | if (!no_alloc) { | |
72ba9f0c | 60 | mutex_lock(&iforce->mem_mutex); |
1da177e4 LT |
61 | if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c, |
62 | iforce->device_memory.start, iforce->device_memory.end, 2L, | |
63 | NULL, NULL)) { | |
72ba9f0c | 64 | mutex_unlock(&iforce->mem_mutex); |
fe65b97a | 65 | return -ENOSPC; |
1da177e4 | 66 | } |
72ba9f0c | 67 | mutex_unlock(&iforce->mem_mutex); |
1da177e4 LT |
68 | } |
69 | ||
70 | data[0] = LO(mod_chunk->start); | |
71 | data[1] = HI(mod_chunk->start); | |
72 | ||
73 | data[2] = HIFIX80(magnitude); | |
74 | data[3] = HIFIX80(offset); | |
75 | data[4] = HI(phase); | |
76 | ||
77 | data[5] = LO(period); | |
78 | data[6] = HI(period); | |
79 | ||
80 | iforce_send_packet(iforce, FF_CMD_PERIOD, data); | |
81 | ||
82 | return 0; | |
83 | } | |
84 | ||
85 | /* | |
86 | * Uploads the part of an effect setting the envelope of the force | |
87 | */ | |
88 | ||
89 | static int make_envelope_modifier(struct iforce* iforce, | |
90 | struct resource* mod_chunk, int no_alloc, | |
91 | u16 attack_duration, __s16 initial_level, | |
92 | u16 fade_duration, __s16 final_level) | |
93 | { | |
94 | unsigned char data[8]; | |
95 | ||
96 | attack_duration = TIME_SCALE(attack_duration); | |
97 | fade_duration = TIME_SCALE(fade_duration); | |
98 | ||
99 | if (!no_alloc) { | |
72ba9f0c | 100 | mutex_lock(&iforce->mem_mutex); |
1da177e4 LT |
101 | if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e, |
102 | iforce->device_memory.start, iforce->device_memory.end, 2L, | |
103 | NULL, NULL)) { | |
72ba9f0c | 104 | mutex_unlock(&iforce->mem_mutex); |
fe65b97a | 105 | return -ENOSPC; |
1da177e4 | 106 | } |
72ba9f0c | 107 | mutex_unlock(&iforce->mem_mutex); |
1da177e4 LT |
108 | } |
109 | ||
110 | data[0] = LO(mod_chunk->start); | |
111 | data[1] = HI(mod_chunk->start); | |
112 | ||
113 | data[2] = LO(attack_duration); | |
114 | data[3] = HI(attack_duration); | |
115 | data[4] = HI(initial_level); | |
116 | ||
117 | data[5] = LO(fade_duration); | |
118 | data[6] = HI(fade_duration); | |
119 | data[7] = HI(final_level); | |
120 | ||
121 | iforce_send_packet(iforce, FF_CMD_ENVELOPE, data); | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | /* | |
127 | * Component of spring, friction, inertia... effects | |
128 | */ | |
129 | ||
130 | static int make_condition_modifier(struct iforce* iforce, | |
131 | struct resource* mod_chunk, int no_alloc, | |
132 | __u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center) | |
133 | { | |
134 | unsigned char data[10]; | |
135 | ||
136 | if (!no_alloc) { | |
72ba9f0c | 137 | mutex_lock(&iforce->mem_mutex); |
1da177e4 LT |
138 | if (allocate_resource(&(iforce->device_memory), mod_chunk, 8, |
139 | iforce->device_memory.start, iforce->device_memory.end, 2L, | |
140 | NULL, NULL)) { | |
72ba9f0c | 141 | mutex_unlock(&iforce->mem_mutex); |
fe65b97a | 142 | return -ENOSPC; |
1da177e4 | 143 | } |
72ba9f0c | 144 | mutex_unlock(&iforce->mem_mutex); |
1da177e4 LT |
145 | } |
146 | ||
147 | data[0] = LO(mod_chunk->start); | |
148 | data[1] = HI(mod_chunk->start); | |
149 | ||
f6a01c85 AH |
150 | data[2] = (100 * rk) >> 15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */ |
151 | data[3] = (100 * lk) >> 15; /* This code is incorrect on cpus lacking arith shift */ | |
1da177e4 | 152 | |
f6a01c85 | 153 | center = (500 * center) >> 15; |
1da177e4 LT |
154 | data[4] = LO(center); |
155 | data[5] = HI(center); | |
156 | ||
f6a01c85 | 157 | db = (1000 * db) >> 16; |
1da177e4 LT |
158 | data[6] = LO(db); |
159 | data[7] = HI(db); | |
160 | ||
f6a01c85 AH |
161 | data[8] = (100 * rsat) >> 16; |
162 | data[9] = (100 * lsat) >> 16; | |
1da177e4 LT |
163 | |
164 | iforce_send_packet(iforce, FF_CMD_CONDITION, data); | |
305180bc | 165 | iforce_dump_packet(iforce, "condition", FF_CMD_CONDITION, data); |
1da177e4 LT |
166 | |
167 | return 0; | |
168 | } | |
169 | ||
170 | static unsigned char find_button(struct iforce *iforce, signed short button) | |
171 | { | |
172 | int i; | |
f6a01c85 | 173 | |
1da177e4 LT |
174 | for (i = 1; iforce->type->btn[i] >= 0; i++) |
175 | if (iforce->type->btn[i] == button) | |
176 | return i + 1; | |
177 | return 0; | |
178 | } | |
179 | ||
180 | /* | |
181 | * Analyse the changes in an effect, and tell if we need to send an condition | |
182 | * parameter packet | |
183 | */ | |
1817b169 GKH |
184 | static int need_condition_modifier(struct iforce *iforce, |
185 | struct ff_effect *old, | |
186 | struct ff_effect *new) | |
1da177e4 | 187 | { |
f6a01c85 | 188 | int ret = 0; |
1da177e4 LT |
189 | int i; |
190 | ||
191 | if (new->type != FF_SPRING && new->type != FF_FRICTION) { | |
1817b169 GKH |
192 | dev_warn(&iforce->dev->dev, "bad effect type in %s\n", |
193 | __func__); | |
f6a01c85 | 194 | return 0; |
1da177e4 LT |
195 | } |
196 | ||
f6a01c85 | 197 | for (i = 0; i < 2; i++) { |
1da177e4 LT |
198 | ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation |
199 | || old->u.condition[i].left_saturation != new->u.condition[i].left_saturation | |
200 | || old->u.condition[i].right_coeff != new->u.condition[i].right_coeff | |
201 | || old->u.condition[i].left_coeff != new->u.condition[i].left_coeff | |
202 | || old->u.condition[i].deadband != new->u.condition[i].deadband | |
203 | || old->u.condition[i].center != new->u.condition[i].center; | |
204 | } | |
205 | return ret; | |
206 | } | |
207 | ||
208 | /* | |
209 | * Analyse the changes in an effect, and tell if we need to send a magnitude | |
210 | * parameter packet | |
211 | */ | |
1817b169 GKH |
212 | static int need_magnitude_modifier(struct iforce *iforce, |
213 | struct ff_effect *old, | |
214 | struct ff_effect *effect) | |
1da177e4 | 215 | { |
1da177e4 | 216 | if (effect->type != FF_CONSTANT) { |
1817b169 GKH |
217 | dev_warn(&iforce->dev->dev, "bad effect type in %s\n", |
218 | __func__); | |
f6a01c85 | 219 | return 0; |
1da177e4 LT |
220 | } |
221 | ||
f6a01c85 | 222 | return old->u.constant.level != effect->u.constant.level; |
1da177e4 LT |
223 | } |
224 | ||
225 | /* | |
226 | * Analyse the changes in an effect, and tell if we need to send an envelope | |
227 | * parameter packet | |
228 | */ | |
1817b169 GKH |
229 | static int need_envelope_modifier(struct iforce *iforce, struct ff_effect *old, |
230 | struct ff_effect *effect) | |
1da177e4 | 231 | { |
1da177e4 LT |
232 | switch (effect->type) { |
233 | case FF_CONSTANT: | |
234 | if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length | |
235 | || old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level | |
236 | || old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length | |
237 | || old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level) | |
f6a01c85 | 238 | return 1; |
1da177e4 LT |
239 | break; |
240 | ||
241 | case FF_PERIODIC: | |
242 | if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length | |
243 | || old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level | |
244 | || old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length | |
245 | || old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level) | |
f6a01c85 | 246 | return 1; |
1da177e4 LT |
247 | break; |
248 | ||
249 | default: | |
1817b169 GKH |
250 | dev_warn(&iforce->dev->dev, "bad effect type in %s\n", |
251 | __func__); | |
1da177e4 LT |
252 | } |
253 | ||
f6a01c85 | 254 | return 0; |
1da177e4 LT |
255 | } |
256 | ||
257 | /* | |
258 | * Analyse the changes in an effect, and tell if we need to send a periodic | |
259 | * parameter effect | |
260 | */ | |
1817b169 GKH |
261 | static int need_period_modifier(struct iforce *iforce, struct ff_effect *old, |
262 | struct ff_effect *new) | |
1da177e4 | 263 | { |
1da177e4 | 264 | if (new->type != FF_PERIODIC) { |
1817b169 GKH |
265 | dev_warn(&iforce->dev->dev, "bad effect type in %s\n", |
266 | __func__); | |
f6a01c85 | 267 | return 0; |
1da177e4 | 268 | } |
1da177e4 LT |
269 | return (old->u.periodic.period != new->u.periodic.period |
270 | || old->u.periodic.magnitude != new->u.periodic.magnitude | |
271 | || old->u.periodic.offset != new->u.periodic.offset | |
272 | || old->u.periodic.phase != new->u.periodic.phase); | |
273 | } | |
274 | ||
275 | /* | |
276 | * Analyse the changes in an effect, and tell if we need to send an effect | |
277 | * packet | |
278 | */ | |
f6a01c85 | 279 | static int need_core(struct ff_effect *old, struct ff_effect *new) |
1da177e4 | 280 | { |
1da177e4 LT |
281 | if (old->direction != new->direction |
282 | || old->trigger.button != new->trigger.button | |
283 | || old->trigger.interval != new->trigger.interval | |
284 | || old->replay.length != new->replay.length | |
285 | || old->replay.delay != new->replay.delay) | |
f6a01c85 | 286 | return 1; |
1da177e4 | 287 | |
f6a01c85 | 288 | return 0; |
1da177e4 LT |
289 | } |
290 | /* | |
291 | * Send the part common to all effects to the device | |
292 | */ | |
293 | static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2, | |
294 | u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button, | |
295 | u16 interval, u16 direction) | |
296 | { | |
297 | unsigned char data[14]; | |
298 | ||
299 | duration = TIME_SCALE(duration); | |
300 | delay = TIME_SCALE(delay); | |
301 | interval = TIME_SCALE(interval); | |
302 | ||
303 | data[0] = LO(id); | |
304 | data[1] = effect_type; | |
305 | data[2] = LO(axes) | find_button(iforce, button); | |
306 | ||
307 | data[3] = LO(duration); | |
308 | data[4] = HI(duration); | |
309 | ||
310 | data[5] = HI(direction); | |
311 | ||
312 | data[6] = LO(interval); | |
313 | data[7] = HI(interval); | |
314 | ||
315 | data[8] = LO(mod_id1); | |
316 | data[9] = HI(mod_id1); | |
317 | data[10] = LO(mod_id2); | |
318 | data[11] = HI(mod_id2); | |
319 | ||
320 | data[12] = LO(delay); | |
321 | data[13] = HI(delay); | |
322 | ||
323 | /* Stop effect */ | |
324 | /* iforce_control_playback(iforce, id, 0);*/ | |
325 | ||
326 | iforce_send_packet(iforce, FF_CMD_EFFECT, data); | |
327 | ||
328 | /* If needed, restart effect */ | |
329 | if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) { | |
330 | /* BUG: perhaps we should replay n times, instead of 1. But we do not know n */ | |
331 | iforce_control_playback(iforce, id, 1); | |
332 | } | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | /* | |
338 | * Upload a periodic effect to the device | |
339 | * See also iforce_upload_constant. | |
340 | */ | |
f6a01c85 | 341 | int iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) |
1da177e4 LT |
342 | { |
343 | u8 wave_code; | |
344 | int core_id = effect->id; | |
345 | struct iforce_core_effect* core_effect = iforce->core_effects + core_id; | |
346 | struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk); | |
347 | struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk); | |
348 | int param1_err = 1; | |
349 | int param2_err = 1; | |
350 | int core_err = 0; | |
351 | ||
1817b169 | 352 | if (!old || need_period_modifier(iforce, old, effect)) { |
1da177e4 | 353 | param1_err = make_period_modifier(iforce, mod1_chunk, |
f6a01c85 | 354 | old != NULL, |
1da177e4 LT |
355 | effect->u.periodic.magnitude, effect->u.periodic.offset, |
356 | effect->u.periodic.period, effect->u.periodic.phase); | |
f6a01c85 AH |
357 | if (param1_err) |
358 | return param1_err; | |
1da177e4 LT |
359 | set_bit(FF_MOD1_IS_USED, core_effect->flags); |
360 | } | |
361 | ||
1817b169 | 362 | if (!old || need_envelope_modifier(iforce, old, effect)) { |
1da177e4 | 363 | param2_err = make_envelope_modifier(iforce, mod2_chunk, |
f6a01c85 | 364 | old !=NULL, |
1da177e4 LT |
365 | effect->u.periodic.envelope.attack_length, |
366 | effect->u.periodic.envelope.attack_level, | |
367 | effect->u.periodic.envelope.fade_length, | |
368 | effect->u.periodic.envelope.fade_level); | |
f6a01c85 AH |
369 | if (param2_err) |
370 | return param2_err; | |
1da177e4 LT |
371 | set_bit(FF_MOD2_IS_USED, core_effect->flags); |
372 | } | |
373 | ||
374 | switch (effect->u.periodic.waveform) { | |
375 | case FF_SQUARE: wave_code = 0x20; break; | |
376 | case FF_TRIANGLE: wave_code = 0x21; break; | |
377 | case FF_SINE: wave_code = 0x22; break; | |
378 | case FF_SAW_UP: wave_code = 0x23; break; | |
379 | case FF_SAW_DOWN: wave_code = 0x24; break; | |
380 | default: wave_code = 0x20; break; | |
381 | } | |
382 | ||
f6a01c85 | 383 | if (!old || need_core(old, effect)) { |
1da177e4 LT |
384 | core_err = make_core(iforce, effect->id, |
385 | mod1_chunk->start, | |
386 | mod2_chunk->start, | |
387 | wave_code, | |
388 | 0x20, | |
389 | effect->replay.length, | |
390 | effect->replay.delay, | |
391 | effect->trigger.button, | |
392 | effect->trigger.interval, | |
393 | effect->direction); | |
394 | } | |
395 | ||
396 | /* If one of the parameter creation failed, we already returned an | |
397 | * error code. | |
398 | * If the core creation failed, we return its error code. | |
399 | * Else: if one parameter at least was created, we return 0 | |
400 | * else we return 1; | |
401 | */ | |
402 | return core_err < 0 ? core_err : (param1_err && param2_err); | |
403 | } | |
404 | ||
405 | /* | |
406 | * Upload a constant force effect | |
407 | * Return value: | |
408 | * <0 Error code | |
409 | * 0 Ok, effect created or updated | |
410 | * 1 effect did not change since last upload, and no packet was therefore sent | |
411 | */ | |
f6a01c85 | 412 | int iforce_upload_constant(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) |
1da177e4 LT |
413 | { |
414 | int core_id = effect->id; | |
415 | struct iforce_core_effect* core_effect = iforce->core_effects + core_id; | |
416 | struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk); | |
417 | struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk); | |
418 | int param1_err = 1; | |
419 | int param2_err = 1; | |
420 | int core_err = 0; | |
421 | ||
1817b169 | 422 | if (!old || need_magnitude_modifier(iforce, old, effect)) { |
1da177e4 | 423 | param1_err = make_magnitude_modifier(iforce, mod1_chunk, |
f6a01c85 | 424 | old != NULL, |
1da177e4 | 425 | effect->u.constant.level); |
f6a01c85 AH |
426 | if (param1_err) |
427 | return param1_err; | |
1da177e4 LT |
428 | set_bit(FF_MOD1_IS_USED, core_effect->flags); |
429 | } | |
430 | ||
1817b169 | 431 | if (!old || need_envelope_modifier(iforce, old, effect)) { |
1da177e4 | 432 | param2_err = make_envelope_modifier(iforce, mod2_chunk, |
f6a01c85 | 433 | old != NULL, |
1da177e4 LT |
434 | effect->u.constant.envelope.attack_length, |
435 | effect->u.constant.envelope.attack_level, | |
436 | effect->u.constant.envelope.fade_length, | |
437 | effect->u.constant.envelope.fade_level); | |
f6a01c85 AH |
438 | if (param2_err) |
439 | return param2_err; | |
1da177e4 LT |
440 | set_bit(FF_MOD2_IS_USED, core_effect->flags); |
441 | } | |
442 | ||
f6a01c85 | 443 | if (!old || need_core(old, effect)) { |
1da177e4 LT |
444 | core_err = make_core(iforce, effect->id, |
445 | mod1_chunk->start, | |
446 | mod2_chunk->start, | |
447 | 0x00, | |
448 | 0x20, | |
449 | effect->replay.length, | |
450 | effect->replay.delay, | |
451 | effect->trigger.button, | |
452 | effect->trigger.interval, | |
453 | effect->direction); | |
454 | } | |
455 | ||
456 | /* If one of the parameter creation failed, we already returned an | |
457 | * error code. | |
458 | * If the core creation failed, we return its error code. | |
459 | * Else: if one parameter at least was created, we return 0 | |
460 | * else we return 1; | |
461 | */ | |
462 | return core_err < 0 ? core_err : (param1_err && param2_err); | |
463 | } | |
464 | ||
465 | /* | |
466 | * Upload an condition effect. Those are for example friction, inertia, springs... | |
467 | */ | |
f6a01c85 | 468 | int iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) |
1da177e4 LT |
469 | { |
470 | int core_id = effect->id; | |
471 | struct iforce_core_effect* core_effect = iforce->core_effects + core_id; | |
472 | struct resource* mod1_chunk = &(core_effect->mod1_chunk); | |
473 | struct resource* mod2_chunk = &(core_effect->mod2_chunk); | |
474 | u8 type; | |
475 | int param_err = 1; | |
476 | int core_err = 0; | |
477 | ||
478 | switch (effect->type) { | |
f6a01c85 AH |
479 | case FF_SPRING: type = 0x40; break; |
480 | case FF_DAMPER: type = 0x41; break; | |
1da177e4 LT |
481 | default: return -1; |
482 | } | |
483 | ||
1817b169 | 484 | if (!old || need_condition_modifier(iforce, old, effect)) { |
1da177e4 | 485 | param_err = make_condition_modifier(iforce, mod1_chunk, |
f6a01c85 | 486 | old != NULL, |
1da177e4 LT |
487 | effect->u.condition[0].right_saturation, |
488 | effect->u.condition[0].left_saturation, | |
489 | effect->u.condition[0].right_coeff, | |
490 | effect->u.condition[0].left_coeff, | |
491 | effect->u.condition[0].deadband, | |
492 | effect->u.condition[0].center); | |
f6a01c85 AH |
493 | if (param_err) |
494 | return param_err; | |
1da177e4 LT |
495 | set_bit(FF_MOD1_IS_USED, core_effect->flags); |
496 | ||
497 | param_err = make_condition_modifier(iforce, mod2_chunk, | |
f6a01c85 | 498 | old != NULL, |
1da177e4 LT |
499 | effect->u.condition[1].right_saturation, |
500 | effect->u.condition[1].left_saturation, | |
501 | effect->u.condition[1].right_coeff, | |
502 | effect->u.condition[1].left_coeff, | |
503 | effect->u.condition[1].deadband, | |
504 | effect->u.condition[1].center); | |
f6a01c85 AH |
505 | if (param_err) |
506 | return param_err; | |
1da177e4 LT |
507 | set_bit(FF_MOD2_IS_USED, core_effect->flags); |
508 | ||
509 | } | |
510 | ||
f6a01c85 | 511 | if (!old || need_core(old, effect)) { |
1da177e4 LT |
512 | core_err = make_core(iforce, effect->id, |
513 | mod1_chunk->start, mod2_chunk->start, | |
514 | type, 0xc0, | |
515 | effect->replay.length, effect->replay.delay, | |
516 | effect->trigger.button, effect->trigger.interval, | |
517 | effect->direction); | |
518 | } | |
519 | ||
520 | /* If the parameter creation failed, we already returned an | |
521 | * error code. | |
522 | * If the core creation failed, we return its error code. | |
523 | * Else: if a parameter was created, we return 0 | |
524 | * else we return 1; | |
525 | */ | |
526 | return core_err < 0 ? core_err : param_err; | |
527 | } |