2 * IWFFFF - AMD InterWave (tm) - Instrument routines
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <sound/driver.h>
22 #include <linux/init.h>
23 #include <linux/sched.h>
24 #include <linux/slab.h>
25 #include <sound/core.h>
26 #include <sound/ainstr_iw.h>
27 #include <sound/initval.h>
28 #include <asm/uaccess.h>
30 MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
31 MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support.");
32 MODULE_LICENSE("GPL");
34 static unsigned int snd_seq_iwffff_size(unsigned int size
, unsigned int format
)
36 unsigned int result
= size
;
38 if (format
& IWFFFF_WAVE_16BIT
)
40 if (format
& IWFFFF_WAVE_STEREO
)
45 static void snd_seq_iwffff_copy_lfo_from_stream(iwffff_lfo_t
*fp
,
48 fp
->freq
= le16_to_cpu(fx
->freq
);
49 fp
->depth
= le16_to_cpu(fx
->depth
);
50 fp
->sweep
= le16_to_cpu(fx
->sweep
);
51 fp
->shape
= fx
->shape
;
52 fp
->delay
= fx
->delay
;
55 static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype
,
64 iwffff_env_record_t
*rp
, *rp_last
;
65 iwffff_xenv_record_t rx
;
66 iwffff_env_point_t
*pp
;
67 iwffff_xenv_point_t px
;
70 ep
->flags
= ex
->flags
;
72 ep
->index
= ex
->index
;
75 if (*len
< (long)sizeof(__u32
))
77 if (copy_from_user(&stype
, *data
, sizeof(stype
)))
79 if (stype
== IWFFFF_STRU_WAVE
)
81 if (req_stype
!= stype
) {
82 if (stype
== IWFFFF_STRU_ENV_RECP
||
83 stype
== IWFFFF_STRU_ENV_RECV
)
86 if (*len
< (long)sizeof(rx
))
88 if (copy_from_user(&rx
, *data
, sizeof(rx
)))
92 points_size
= (le16_to_cpu(rx
.nattack
) + le16_to_cpu(rx
.nrelease
)) * 2 * sizeof(__u16
);
93 if (points_size
> *len
)
95 rp
= kcalloc(1, sizeof(*rp
) + points_size
, gfp_mask
);
98 rp
->nattack
= le16_to_cpu(rx
.nattack
);
99 rp
->nrelease
= le16_to_cpu(rx
.nrelease
);
100 rp
->sustain_offset
= le16_to_cpu(rx
.sustain_offset
);
101 rp
->sustain_rate
= le16_to_cpu(rx
.sustain_rate
);
102 rp
->release_rate
= le16_to_cpu(rx
.release_rate
);
103 rp
->hirange
= rx
.hirange
;
104 pp
= (iwffff_env_point_t
*)(rp
+ 1);
105 for (idx
= 0; idx
< rp
->nattack
+ rp
->nrelease
; idx
++) {
106 if (copy_from_user(&px
, *data
, sizeof(px
)))
110 pp
->offset
= le16_to_cpu(px
.offset
);
111 pp
->rate
= le16_to_cpu(px
.rate
);
113 if (ep
->record
== NULL
) {
123 static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t
*ops
,
129 iwffff_wave_t
*wp
, *prev
;
132 unsigned int real_size
;
134 gfp_mask
= atomic
? GFP_ATOMIC
: GFP_KERNEL
;
135 if (*len
< (long)sizeof(xp
))
137 if (copy_from_user(&xp
, *data
, sizeof(xp
)))
141 wp
= kcalloc(1, sizeof(*wp
), gfp_mask
);
144 wp
->share_id
[0] = le32_to_cpu(xp
.share_id
[0]);
145 wp
->share_id
[1] = le32_to_cpu(xp
.share_id
[1]);
146 wp
->share_id
[2] = le32_to_cpu(xp
.share_id
[2]);
147 wp
->share_id
[3] = le32_to_cpu(xp
.share_id
[3]);
148 wp
->format
= le32_to_cpu(xp
.format
);
149 wp
->address
.memory
= le32_to_cpu(xp
.offset
);
150 wp
->size
= le32_to_cpu(xp
.size
);
151 wp
->start
= le32_to_cpu(xp
.start
);
152 wp
->loop_start
= le32_to_cpu(xp
.loop_start
);
153 wp
->loop_end
= le32_to_cpu(xp
.loop_end
);
154 wp
->loop_repeat
= le16_to_cpu(xp
.loop_repeat
);
155 wp
->sample_ratio
= le32_to_cpu(xp
.sample_ratio
);
156 wp
->attenuation
= xp
.attenuation
;
157 wp
->low_note
= xp
.low_note
;
158 wp
->high_note
= xp
.high_note
;
159 real_size
= snd_seq_iwffff_size(wp
->size
, wp
->format
);
160 if (!(wp
->format
& IWFFFF_WAVE_ROM
)) {
161 if ((long)real_size
> *len
) {
166 if (ops
->put_sample
) {
167 err
= ops
->put_sample(ops
->private_data
, wp
,
168 *data
, real_size
, atomic
);
174 if (!(wp
->format
& IWFFFF_WAVE_ROM
)) {
180 while (prev
->next
) prev
= prev
->next
;
188 static void snd_seq_iwffff_env_free(snd_iwffff_ops_t
*ops
,
192 iwffff_env_record_t
*rec
;
194 while ((rec
= env
->record
) != NULL
) {
195 env
->record
= rec
->next
;
200 static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t
*ops
,
204 if (ops
->remove_sample
)
205 ops
->remove_sample(ops
->private_data
, wave
, atomic
);
209 static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t
*ops
,
210 iwffff_instrument_t
*ip
,
213 iwffff_layer_t
*layer
;
216 while ((layer
= ip
->layer
) != NULL
) {
217 ip
->layer
= layer
->next
;
218 snd_seq_iwffff_env_free(ops
, &layer
->penv
, atomic
);
219 snd_seq_iwffff_env_free(ops
, &layer
->venv
, atomic
);
220 while ((wave
= layer
->wave
) != NULL
) {
221 layer
->wave
= wave
->next
;
222 snd_seq_iwffff_wave_free(ops
, wave
, atomic
);
228 static int snd_seq_iwffff_put(void *private_data
, snd_seq_kinstr_t
*instr
,
229 char __user
*instr_data
, long len
, int atomic
,
232 snd_iwffff_ops_t
*ops
= (snd_iwffff_ops_t
*)private_data
;
233 iwffff_instrument_t
*ip
;
234 iwffff_xinstrument_t ix
;
235 iwffff_layer_t
*lp
, *prev_lp
;
239 if (cmd
!= SNDRV_SEQ_INSTR_PUT_CMD_CREATE
)
241 gfp_mask
= atomic
? GFP_ATOMIC
: GFP_KERNEL
;
242 /* copy instrument data */
243 if (len
< (long)sizeof(ix
))
245 if (copy_from_user(&ix
, instr_data
, sizeof(ix
)))
247 if (ix
.stype
!= IWFFFF_STRU_INSTR
)
249 instr_data
+= sizeof(ix
);
251 ip
= (iwffff_instrument_t
*)KINSTR_DATA(instr
);
252 ip
->exclusion
= le16_to_cpu(ix
.exclusion
);
253 ip
->layer_type
= le16_to_cpu(ix
.layer_type
);
254 ip
->exclusion_group
= le16_to_cpu(ix
.exclusion_group
);
255 ip
->effect1
= ix
.effect1
;
256 ip
->effect1_depth
= ix
.effect1_depth
;
257 ip
->effect2
= ix
.effect2
;
258 ip
->effect2_depth
= ix
.effect2_depth
;
262 if (len
< (long)sizeof(iwffff_xlayer_t
)) {
263 snd_seq_iwffff_instr_free(ops
, ip
, atomic
);
266 if (copy_from_user(&lx
, instr_data
, sizeof(lx
)))
268 instr_data
+= sizeof(lx
);
270 if (lx
.stype
!= IWFFFF_STRU_LAYER
) {
271 snd_seq_iwffff_instr_free(ops
, ip
, atomic
);
274 lp
= kcalloc(1, sizeof(*lp
), gfp_mask
);
276 snd_seq_iwffff_instr_free(ops
, ip
, atomic
);
285 lp
->flags
= lx
.flags
;
286 lp
->velocity_mode
= lx
.velocity_mode
;
287 lp
->layer_event
= lx
.layer_event
;
288 lp
->low_range
= lx
.low_range
;
289 lp
->high_range
= lx
.high_range
;
291 lp
->pan_freq_scale
= lx
.pan_freq_scale
;
292 lp
->attenuation
= lx
.attenuation
;
293 snd_seq_iwffff_copy_lfo_from_stream(&lp
->tremolo
, &lx
.tremolo
);
294 snd_seq_iwffff_copy_lfo_from_stream(&lp
->vibrato
, &lx
.vibrato
);
295 lp
->freq_scale
= le16_to_cpu(lx
.freq_scale
);
296 lp
->freq_center
= lx
.freq_center
;
297 err
= snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP
,
303 snd_seq_iwffff_instr_free(ops
, ip
, atomic
);
306 err
= snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV
,
312 snd_seq_iwffff_instr_free(ops
, ip
, atomic
);
315 while (len
> (long)sizeof(__u32
)) {
318 if (copy_from_user(&stype
, instr_data
, sizeof(stype
)))
320 if (stype
!= IWFFFF_STRU_WAVE
)
322 err
= snd_seq_iwffff_copy_wave_from_stream(ops
,
328 snd_seq_iwffff_instr_free(ops
, ip
, atomic
);
336 static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t
*fx
,
339 fx
->freq
= cpu_to_le16(fp
->freq
);
340 fx
->depth
= cpu_to_le16(fp
->depth
);
341 fx
->sweep
= cpu_to_le16(fp
->sweep
);
342 fp
->shape
= fx
->shape
;
343 fp
->delay
= fx
->delay
;
346 static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype
,
353 iwffff_env_record_t
*rp
;
354 iwffff_xenv_record_t rx
;
355 iwffff_env_point_t
*pp
;
356 iwffff_xenv_point_t px
;
357 int points_size
, idx
;
359 ex
->flags
= ep
->flags
;
361 ex
->index
= ep
->index
;
362 for (rp
= ep
->record
; rp
; rp
= rp
->next
) {
363 if (*len
< (long)sizeof(rx
))
365 memset(&rx
, 0, sizeof(rx
));
366 rx
.stype
= req_stype
;
367 rx
.nattack
= cpu_to_le16(rp
->nattack
);
368 rx
.nrelease
= cpu_to_le16(rp
->nrelease
);
369 rx
.sustain_offset
= cpu_to_le16(rp
->sustain_offset
);
370 rx
.sustain_rate
= cpu_to_le16(rp
->sustain_rate
);
371 rx
.release_rate
= cpu_to_le16(rp
->release_rate
);
372 rx
.hirange
= cpu_to_le16(rp
->hirange
);
373 if (copy_to_user(*data
, &rx
, sizeof(rx
)))
377 points_size
= (rp
->nattack
+ rp
->nrelease
) * 2 * sizeof(__u16
);
378 if (*len
< points_size
)
380 pp
= (iwffff_env_point_t
*)(rp
+ 1);
381 for (idx
= 0; idx
< rp
->nattack
+ rp
->nrelease
; idx
++) {
382 px
.offset
= cpu_to_le16(pp
->offset
);
383 px
.rate
= cpu_to_le16(pp
->rate
);
384 if (copy_to_user(*data
, &px
, sizeof(px
)))
393 static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t
*ops
,
402 unsigned int real_size
;
404 for (wp
= lp
->wave
; wp
; wp
= wp
->next
) {
405 if (*len
< (long)sizeof(xp
))
407 memset(&xp
, 0, sizeof(xp
));
408 xp
.stype
= IWFFFF_STRU_WAVE
;
409 xp
.share_id
[0] = cpu_to_le32(wp
->share_id
[0]);
410 xp
.share_id
[1] = cpu_to_le32(wp
->share_id
[1]);
411 xp
.share_id
[2] = cpu_to_le32(wp
->share_id
[2]);
412 xp
.share_id
[3] = cpu_to_le32(wp
->share_id
[3]);
413 xp
.format
= cpu_to_le32(wp
->format
);
414 if (wp
->format
& IWFFFF_WAVE_ROM
)
415 xp
.offset
= cpu_to_le32(wp
->address
.memory
);
416 xp
.size
= cpu_to_le32(wp
->size
);
417 xp
.start
= cpu_to_le32(wp
->start
);
418 xp
.loop_start
= cpu_to_le32(wp
->loop_start
);
419 xp
.loop_end
= cpu_to_le32(wp
->loop_end
);
420 xp
.loop_repeat
= cpu_to_le32(wp
->loop_repeat
);
421 xp
.sample_ratio
= cpu_to_le32(wp
->sample_ratio
);
422 xp
.attenuation
= wp
->attenuation
;
423 xp
.low_note
= wp
->low_note
;
424 xp
.high_note
= wp
->high_note
;
425 if (copy_to_user(*data
, &xp
, sizeof(xp
)))
429 real_size
= snd_seq_iwffff_size(wp
->size
, wp
->format
);
430 if (!(wp
->format
& IWFFFF_WAVE_ROM
)) {
431 if (*len
< (long)real_size
)
434 if (ops
->get_sample
) {
435 err
= ops
->get_sample(ops
->private_data
, wp
,
436 *data
, real_size
, atomic
);
440 if (!(wp
->format
& IWFFFF_WAVE_ROM
)) {
448 static int snd_seq_iwffff_get(void *private_data
, snd_seq_kinstr_t
*instr
,
449 char __user
*instr_data
, long len
, int atomic
, int cmd
)
451 snd_iwffff_ops_t
*ops
= (snd_iwffff_ops_t
*)private_data
;
452 iwffff_instrument_t
*ip
;
453 iwffff_xinstrument_t ix
;
456 char __user
*layer_instr_data
;
459 if (cmd
!= SNDRV_SEQ_INSTR_GET_CMD_FULL
)
461 if (len
< (long)sizeof(ix
))
463 memset(&ix
, 0, sizeof(ix
));
464 ip
= (iwffff_instrument_t
*)KINSTR_DATA(instr
);
465 ix
.stype
= IWFFFF_STRU_INSTR
;
466 ix
.exclusion
= cpu_to_le16(ip
->exclusion
);
467 ix
.layer_type
= cpu_to_le16(ip
->layer_type
);
468 ix
.exclusion_group
= cpu_to_le16(ip
->exclusion_group
);
469 ix
.effect1
= cpu_to_le16(ip
->effect1
);
470 ix
.effect1_depth
= cpu_to_le16(ip
->effect1_depth
);
471 ix
.effect2
= ip
->effect2
;
472 ix
.effect2_depth
= ip
->effect2_depth
;
473 if (copy_to_user(instr_data
, &ix
, sizeof(ix
)))
475 instr_data
+= sizeof(ix
);
477 for (lp
= ip
->layer
; lp
; lp
= lp
->next
) {
478 if (len
< (long)sizeof(lx
))
480 memset(&lx
, 0, sizeof(lx
));
481 lx
.stype
= IWFFFF_STRU_LAYER
;
482 lx
.flags
= lp
->flags
;
483 lx
.velocity_mode
= lp
->velocity_mode
;
484 lx
.layer_event
= lp
->layer_event
;
485 lx
.low_range
= lp
->low_range
;
486 lx
.high_range
= lp
->high_range
;
488 lx
.pan_freq_scale
= lp
->pan_freq_scale
;
489 lx
.attenuation
= lp
->attenuation
;
490 snd_seq_iwffff_copy_lfo_to_stream(&lx
.tremolo
, &lp
->tremolo
);
491 snd_seq_iwffff_copy_lfo_to_stream(&lx
.vibrato
, &lp
->vibrato
);
492 layer_instr_data
= instr_data
;
493 instr_data
+= sizeof(lx
);
495 err
= snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP
,
501 err
= snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV
,
507 /* layer structure updating is now finished */
508 if (copy_to_user(layer_instr_data
, &lx
, sizeof(lx
)))
510 err
= snd_seq_iwffff_copy_wave_to_stream(ops
,
521 static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t
*ep
)
524 iwffff_env_record_t
*rp
;
526 for (rp
= ep
->record
; rp
; rp
= rp
->next
) {
527 result
+= sizeof(iwffff_xenv_record_t
);
528 result
+= (rp
->nattack
+ rp
->nrelease
) * 2 * sizeof(__u16
);
533 static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t
*lp
)
538 for (wp
= lp
->wave
; wp
; wp
= wp
->next
) {
539 result
+= sizeof(iwffff_xwave_t
);
540 if (!(wp
->format
& IWFFFF_WAVE_ROM
))
546 static int snd_seq_iwffff_get_size(void *private_data
, snd_seq_kinstr_t
*instr
,
550 iwffff_instrument_t
*ip
;
554 ip
= (iwffff_instrument_t
*)KINSTR_DATA(instr
);
555 result
= sizeof(iwffff_xinstrument_t
);
556 for (lp
= ip
->layer
; lp
; lp
= lp
->next
) {
557 result
+= sizeof(iwffff_xlayer_t
);
558 result
+= snd_seq_iwffff_env_size_in_stream(&lp
->penv
);
559 result
+= snd_seq_iwffff_env_size_in_stream(&lp
->venv
);
560 result
+= snd_seq_iwffff_wave_size_in_stream(lp
);
566 static int snd_seq_iwffff_remove(void *private_data
,
567 snd_seq_kinstr_t
*instr
,
570 snd_iwffff_ops_t
*ops
= (snd_iwffff_ops_t
*)private_data
;
571 iwffff_instrument_t
*ip
;
573 ip
= (iwffff_instrument_t
*)KINSTR_DATA(instr
);
574 snd_seq_iwffff_instr_free(ops
, ip
, atomic
);
578 static void snd_seq_iwffff_notify(void *private_data
,
579 snd_seq_kinstr_t
*instr
,
582 snd_iwffff_ops_t
*ops
= (snd_iwffff_ops_t
*)private_data
;
585 ops
->notify(ops
->private_data
, instr
, what
);
588 int snd_seq_iwffff_init(snd_iwffff_ops_t
*ops
,
590 snd_seq_kinstr_ops_t
*next
)
592 memset(ops
, 0, sizeof(*ops
));
593 ops
->private_data
= private_data
;
594 ops
->kops
.private_data
= ops
;
595 ops
->kops
.add_len
= sizeof(iwffff_instrument_t
);
596 ops
->kops
.instr_type
= SNDRV_SEQ_INSTR_ID_INTERWAVE
;
597 ops
->kops
.put
= snd_seq_iwffff_put
;
598 ops
->kops
.get
= snd_seq_iwffff_get
;
599 ops
->kops
.get_size
= snd_seq_iwffff_get_size
;
600 ops
->kops
.remove
= snd_seq_iwffff_remove
;
601 ops
->kops
.notify
= snd_seq_iwffff_notify
;
602 ops
->kops
.next
= next
;
610 static int __init
alsa_ainstr_iw_init(void)
615 static void __exit
alsa_ainstr_iw_exit(void)
619 module_init(alsa_ainstr_iw_init
)
620 module_exit(alsa_ainstr_iw_exit
)
622 EXPORT_SYMBOL(snd_seq_iwffff_init
);