Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * IWFFFF - AMD InterWave (tm) - Instrument routines | |
3 | * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> | |
4 | * | |
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. | |
9 | * | |
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. | |
14 | * | |
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 | |
18 | * | |
19 | */ | |
20 | ||
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> | |
29 | ||
30 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | |
31 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support."); | |
32 | MODULE_LICENSE("GPL"); | |
33 | ||
34 | static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format) | |
35 | { | |
36 | unsigned int result = size; | |
37 | ||
38 | if (format & IWFFFF_WAVE_16BIT) | |
39 | result <<= 1; | |
40 | if (format & IWFFFF_WAVE_STEREO) | |
41 | result <<= 1; | |
42 | return result; | |
43 | } | |
44 | ||
45 | static void snd_seq_iwffff_copy_lfo_from_stream(iwffff_lfo_t *fp, | |
46 | iwffff_xlfo_t *fx) | |
47 | { | |
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; | |
53 | } | |
54 | ||
55 | static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype, | |
56 | iwffff_layer_t *lp, | |
57 | iwffff_env_t *ep, | |
58 | iwffff_xenv_t *ex, | |
59 | char __user **data, | |
60 | long *len, | |
61 | int gfp_mask) | |
62 | { | |
63 | __u32 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; | |
68 | int points_size, idx; | |
69 | ||
70 | ep->flags = ex->flags; | |
71 | ep->mode = ex->mode; | |
72 | ep->index = ex->index; | |
73 | rp_last = NULL; | |
74 | while (1) { | |
75 | if (*len < (long)sizeof(__u32)) | |
76 | return -EINVAL; | |
77 | if (copy_from_user(&stype, *data, sizeof(stype))) | |
78 | return -EFAULT; | |
79 | if (stype == IWFFFF_STRU_WAVE) | |
80 | return 0; | |
81 | if (req_stype != stype) { | |
82 | if (stype == IWFFFF_STRU_ENV_RECP || | |
83 | stype == IWFFFF_STRU_ENV_RECV) | |
84 | return 0; | |
85 | } | |
86 | if (*len < (long)sizeof(rx)) | |
87 | return -EINVAL; | |
88 | if (copy_from_user(&rx, *data, sizeof(rx))) | |
89 | return -EFAULT; | |
90 | *data += sizeof(rx); | |
91 | *len -= sizeof(rx); | |
92 | points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16); | |
93 | if (points_size > *len) | |
94 | return -EINVAL; | |
95 | rp = kcalloc(1, sizeof(*rp) + points_size, gfp_mask); | |
96 | if (rp == NULL) | |
97 | return -ENOMEM; | |
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))) | |
107 | return -EFAULT; | |
108 | *data += sizeof(px); | |
109 | *len -= sizeof(px); | |
110 | pp->offset = le16_to_cpu(px.offset); | |
111 | pp->rate = le16_to_cpu(px.rate); | |
112 | } | |
113 | if (ep->record == NULL) { | |
114 | ep->record = rp; | |
115 | } else { | |
116 | rp_last = rp; | |
117 | } | |
118 | rp_last = rp; | |
119 | } | |
120 | return 0; | |
121 | } | |
122 | ||
123 | static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t *ops, | |
124 | iwffff_layer_t *lp, | |
125 | char __user **data, | |
126 | long *len, | |
127 | int atomic) | |
128 | { | |
129 | iwffff_wave_t *wp, *prev; | |
130 | iwffff_xwave_t xp; | |
131 | int err, gfp_mask; | |
132 | unsigned int real_size; | |
133 | ||
134 | gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; | |
135 | if (*len < (long)sizeof(xp)) | |
136 | return -EINVAL; | |
137 | if (copy_from_user(&xp, *data, sizeof(xp))) | |
138 | return -EFAULT; | |
139 | *data += sizeof(xp); | |
140 | *len -= sizeof(xp); | |
141 | wp = kcalloc(1, sizeof(*wp), gfp_mask); | |
142 | if (wp == NULL) | |
143 | return -ENOMEM; | |
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) { | |
162 | kfree(wp); | |
163 | return -ENOMEM; | |
164 | } | |
165 | } | |
166 | if (ops->put_sample) { | |
167 | err = ops->put_sample(ops->private_data, wp, | |
168 | *data, real_size, atomic); | |
169 | if (err < 0) { | |
170 | kfree(wp); | |
171 | return err; | |
172 | } | |
173 | } | |
174 | if (!(wp->format & IWFFFF_WAVE_ROM)) { | |
175 | *data += real_size; | |
176 | *len -= real_size; | |
177 | } | |
178 | prev = lp->wave; | |
179 | if (prev) { | |
180 | while (prev->next) prev = prev->next; | |
181 | prev->next = wp; | |
182 | } else { | |
183 | lp->wave = wp; | |
184 | } | |
185 | return 0; | |
186 | } | |
187 | ||
188 | static void snd_seq_iwffff_env_free(snd_iwffff_ops_t *ops, | |
189 | iwffff_env_t *env, | |
190 | int atomic) | |
191 | { | |
192 | iwffff_env_record_t *rec; | |
193 | ||
194 | while ((rec = env->record) != NULL) { | |
195 | env->record = rec->next; | |
196 | kfree(rec); | |
197 | } | |
198 | } | |
199 | ||
200 | static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t *ops, | |
201 | iwffff_wave_t *wave, | |
202 | int atomic) | |
203 | { | |
204 | if (ops->remove_sample) | |
205 | ops->remove_sample(ops->private_data, wave, atomic); | |
206 | kfree(wave); | |
207 | } | |
208 | ||
209 | static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t *ops, | |
210 | iwffff_instrument_t *ip, | |
211 | int atomic) | |
212 | { | |
213 | iwffff_layer_t *layer; | |
214 | iwffff_wave_t *wave; | |
215 | ||
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); | |
223 | } | |
224 | kfree(layer); | |
225 | } | |
226 | } | |
227 | ||
228 | static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr, | |
229 | char __user *instr_data, long len, int atomic, | |
230 | int cmd) | |
231 | { | |
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; | |
236 | iwffff_xlayer_t lx; | |
237 | int err, gfp_mask; | |
238 | ||
239 | if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) | |
240 | return -EINVAL; | |
241 | gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; | |
242 | /* copy instrument data */ | |
243 | if (len < (long)sizeof(ix)) | |
244 | return -EINVAL; | |
245 | if (copy_from_user(&ix, instr_data, sizeof(ix))) | |
246 | return -EFAULT; | |
247 | if (ix.stype != IWFFFF_STRU_INSTR) | |
248 | return -EINVAL; | |
249 | instr_data += sizeof(ix); | |
250 | len -= 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; | |
259 | /* copy layers */ | |
260 | prev_lp = NULL; | |
261 | while (len > 0) { | |
262 | if (len < (long)sizeof(iwffff_xlayer_t)) { | |
263 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
264 | return -EINVAL; | |
265 | } | |
266 | if (copy_from_user(&lx, instr_data, sizeof(lx))) | |
267 | return -EFAULT; | |
268 | instr_data += sizeof(lx); | |
269 | len -= sizeof(lx); | |
270 | if (lx.stype != IWFFFF_STRU_LAYER) { | |
271 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
272 | return -EINVAL; | |
273 | } | |
274 | lp = kcalloc(1, sizeof(*lp), gfp_mask); | |
275 | if (lp == NULL) { | |
276 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
277 | return -ENOMEM; | |
278 | } | |
279 | if (prev_lp) { | |
280 | prev_lp->next = lp; | |
281 | } else { | |
282 | ip->layer = lp; | |
283 | } | |
284 | prev_lp = lp; | |
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; | |
290 | lp->pan = lx.pan; | |
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, | |
298 | lp, | |
299 | &lp->penv, &lx.penv, | |
300 | &instr_data, &len, | |
301 | gfp_mask); | |
302 | if (err < 0) { | |
303 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
304 | return err; | |
305 | } | |
306 | err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV, | |
307 | lp, | |
308 | &lp->venv, &lx.venv, | |
309 | &instr_data, &len, | |
310 | gfp_mask); | |
311 | if (err < 0) { | |
312 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
313 | return err; | |
314 | } | |
315 | while (len > (long)sizeof(__u32)) { | |
316 | __u32 stype; | |
317 | ||
318 | if (copy_from_user(&stype, instr_data, sizeof(stype))) | |
319 | return -EFAULT; | |
320 | if (stype != IWFFFF_STRU_WAVE) | |
321 | break; | |
322 | err = snd_seq_iwffff_copy_wave_from_stream(ops, | |
323 | lp, | |
324 | &instr_data, | |
325 | &len, | |
326 | atomic); | |
327 | if (err < 0) { | |
328 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
329 | return err; | |
330 | } | |
331 | } | |
332 | } | |
333 | return 0; | |
334 | } | |
335 | ||
336 | static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t *fx, | |
337 | iwffff_lfo_t *fp) | |
338 | { | |
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; | |
344 | } | |
345 | ||
346 | static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype, | |
347 | iwffff_layer_t *lp, | |
348 | iwffff_xenv_t *ex, | |
349 | iwffff_env_t *ep, | |
350 | char __user **data, | |
351 | long *len) | |
352 | { | |
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; | |
358 | ||
359 | ex->flags = ep->flags; | |
360 | ex->mode = ep->mode; | |
361 | ex->index = ep->index; | |
362 | for (rp = ep->record; rp; rp = rp->next) { | |
363 | if (*len < (long)sizeof(rx)) | |
364 | return -ENOMEM; | |
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))) | |
374 | return -EFAULT; | |
375 | *data += sizeof(rx); | |
376 | *len -= sizeof(rx); | |
377 | points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); | |
378 | if (*len < points_size) | |
379 | return -ENOMEM; | |
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))) | |
385 | return -EFAULT; | |
386 | *data += sizeof(px); | |
387 | *len -= sizeof(px); | |
388 | } | |
389 | } | |
390 | return 0; | |
391 | } | |
392 | ||
393 | static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t *ops, | |
394 | iwffff_layer_t *lp, | |
395 | char __user **data, | |
396 | long *len, | |
397 | int atomic) | |
398 | { | |
399 | iwffff_wave_t *wp; | |
400 | iwffff_xwave_t xp; | |
401 | int err; | |
402 | unsigned int real_size; | |
403 | ||
404 | for (wp = lp->wave; wp; wp = wp->next) { | |
405 | if (*len < (long)sizeof(xp)) | |
406 | return -ENOMEM; | |
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))) | |
426 | return -EFAULT; | |
427 | *data += sizeof(xp); | |
428 | *len -= 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) | |
432 | return -ENOMEM; | |
433 | } | |
434 | if (ops->get_sample) { | |
435 | err = ops->get_sample(ops->private_data, wp, | |
436 | *data, real_size, atomic); | |
437 | if (err < 0) | |
438 | return err; | |
439 | } | |
440 | if (!(wp->format & IWFFFF_WAVE_ROM)) { | |
441 | *data += real_size; | |
442 | *len -= real_size; | |
443 | } | |
444 | } | |
445 | return 0; | |
446 | } | |
447 | ||
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) | |
450 | { | |
451 | snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; | |
452 | iwffff_instrument_t *ip; | |
453 | iwffff_xinstrument_t ix; | |
454 | iwffff_layer_t *lp; | |
455 | iwffff_xlayer_t lx; | |
456 | char __user *layer_instr_data; | |
457 | int err; | |
458 | ||
459 | if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) | |
460 | return -EINVAL; | |
461 | if (len < (long)sizeof(ix)) | |
462 | return -ENOMEM; | |
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))) | |
474 | return -EFAULT; | |
475 | instr_data += sizeof(ix); | |
476 | len -= sizeof(ix); | |
477 | for (lp = ip->layer; lp; lp = lp->next) { | |
478 | if (len < (long)sizeof(lx)) | |
479 | return -ENOMEM; | |
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; | |
487 | lx.pan = lp->pan; | |
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); | |
494 | len -= sizeof(lx); | |
495 | err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP, | |
496 | lp, | |
497 | &lx.penv, &lp->penv, | |
498 | &instr_data, &len); | |
499 | if (err < 0) | |
500 | return err; | |
501 | err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV, | |
502 | lp, | |
503 | &lx.venv, &lp->venv, | |
504 | &instr_data, &len); | |
505 | if (err < 0) | |
506 | return err; | |
507 | /* layer structure updating is now finished */ | |
508 | if (copy_to_user(layer_instr_data, &lx, sizeof(lx))) | |
509 | return -EFAULT; | |
510 | err = snd_seq_iwffff_copy_wave_to_stream(ops, | |
511 | lp, | |
512 | &instr_data, | |
513 | &len, | |
514 | atomic); | |
515 | if (err < 0) | |
516 | return err; | |
517 | } | |
518 | return 0; | |
519 | } | |
520 | ||
521 | static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t *ep) | |
522 | { | |
523 | long result = 0; | |
524 | iwffff_env_record_t *rp; | |
525 | ||
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); | |
529 | } | |
530 | return 0; | |
531 | } | |
532 | ||
533 | static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t *lp) | |
534 | { | |
535 | long result = 0; | |
536 | iwffff_wave_t *wp; | |
537 | ||
538 | for (wp = lp->wave; wp; wp = wp->next) { | |
539 | result += sizeof(iwffff_xwave_t); | |
540 | if (!(wp->format & IWFFFF_WAVE_ROM)) | |
541 | result += wp->size; | |
542 | } | |
543 | return result; | |
544 | } | |
545 | ||
546 | static int snd_seq_iwffff_get_size(void *private_data, snd_seq_kinstr_t *instr, | |
547 | long *size) | |
548 | { | |
549 | long result; | |
550 | iwffff_instrument_t *ip; | |
551 | iwffff_layer_t *lp; | |
552 | ||
553 | *size = 0; | |
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); | |
561 | } | |
562 | *size = result; | |
563 | return 0; | |
564 | } | |
565 | ||
566 | static int snd_seq_iwffff_remove(void *private_data, | |
567 | snd_seq_kinstr_t *instr, | |
568 | int atomic) | |
569 | { | |
570 | snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; | |
571 | iwffff_instrument_t *ip; | |
572 | ||
573 | ip = (iwffff_instrument_t *)KINSTR_DATA(instr); | |
574 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
575 | return 0; | |
576 | } | |
577 | ||
578 | static void snd_seq_iwffff_notify(void *private_data, | |
579 | snd_seq_kinstr_t *instr, | |
580 | int what) | |
581 | { | |
582 | snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; | |
583 | ||
584 | if (ops->notify) | |
585 | ops->notify(ops->private_data, instr, what); | |
586 | } | |
587 | ||
588 | int snd_seq_iwffff_init(snd_iwffff_ops_t *ops, | |
589 | void *private_data, | |
590 | snd_seq_kinstr_ops_t *next) | |
591 | { | |
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; | |
603 | return 0; | |
604 | } | |
605 | ||
606 | /* | |
607 | * Init part | |
608 | */ | |
609 | ||
610 | static int __init alsa_ainstr_iw_init(void) | |
611 | { | |
612 | return 0; | |
613 | } | |
614 | ||
615 | static void __exit alsa_ainstr_iw_exit(void) | |
616 | { | |
617 | } | |
618 | ||
619 | module_init(alsa_ainstr_iw_init) | |
620 | module_exit(alsa_ainstr_iw_exit) | |
621 | ||
622 | EXPORT_SYMBOL(snd_seq_iwffff_init); |