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, | |
dd0fc66f | 61 | gfp_t gfp_mask) |
1da177e4 LT |
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; | |
ecca82b4 | 95 | rp = kzalloc(sizeof(*rp) + points_size, gfp_mask); |
1da177e4 LT |
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; | |
5a0f217d VF |
131 | int err; |
132 | unsigned int gfp_mask; | |
1da177e4 LT |
133 | unsigned int real_size; |
134 | ||
135 | gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; | |
136 | if (*len < (long)sizeof(xp)) | |
137 | return -EINVAL; | |
138 | if (copy_from_user(&xp, *data, sizeof(xp))) | |
139 | return -EFAULT; | |
140 | *data += sizeof(xp); | |
141 | *len -= sizeof(xp); | |
ecca82b4 | 142 | wp = kzalloc(sizeof(*wp), gfp_mask); |
1da177e4 LT |
143 | if (wp == NULL) |
144 | return -ENOMEM; | |
145 | wp->share_id[0] = le32_to_cpu(xp.share_id[0]); | |
146 | wp->share_id[1] = le32_to_cpu(xp.share_id[1]); | |
147 | wp->share_id[2] = le32_to_cpu(xp.share_id[2]); | |
148 | wp->share_id[3] = le32_to_cpu(xp.share_id[3]); | |
149 | wp->format = le32_to_cpu(xp.format); | |
150 | wp->address.memory = le32_to_cpu(xp.offset); | |
151 | wp->size = le32_to_cpu(xp.size); | |
152 | wp->start = le32_to_cpu(xp.start); | |
153 | wp->loop_start = le32_to_cpu(xp.loop_start); | |
154 | wp->loop_end = le32_to_cpu(xp.loop_end); | |
155 | wp->loop_repeat = le16_to_cpu(xp.loop_repeat); | |
156 | wp->sample_ratio = le32_to_cpu(xp.sample_ratio); | |
157 | wp->attenuation = xp.attenuation; | |
158 | wp->low_note = xp.low_note; | |
159 | wp->high_note = xp.high_note; | |
160 | real_size = snd_seq_iwffff_size(wp->size, wp->format); | |
161 | if (!(wp->format & IWFFFF_WAVE_ROM)) { | |
162 | if ((long)real_size > *len) { | |
163 | kfree(wp); | |
164 | return -ENOMEM; | |
165 | } | |
166 | } | |
167 | if (ops->put_sample) { | |
168 | err = ops->put_sample(ops->private_data, wp, | |
169 | *data, real_size, atomic); | |
170 | if (err < 0) { | |
171 | kfree(wp); | |
172 | return err; | |
173 | } | |
174 | } | |
175 | if (!(wp->format & IWFFFF_WAVE_ROM)) { | |
176 | *data += real_size; | |
177 | *len -= real_size; | |
178 | } | |
179 | prev = lp->wave; | |
180 | if (prev) { | |
181 | while (prev->next) prev = prev->next; | |
182 | prev->next = wp; | |
183 | } else { | |
184 | lp->wave = wp; | |
185 | } | |
186 | return 0; | |
187 | } | |
188 | ||
189 | static void snd_seq_iwffff_env_free(snd_iwffff_ops_t *ops, | |
190 | iwffff_env_t *env, | |
191 | int atomic) | |
192 | { | |
193 | iwffff_env_record_t *rec; | |
194 | ||
195 | while ((rec = env->record) != NULL) { | |
196 | env->record = rec->next; | |
197 | kfree(rec); | |
198 | } | |
199 | } | |
200 | ||
201 | static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t *ops, | |
202 | iwffff_wave_t *wave, | |
203 | int atomic) | |
204 | { | |
205 | if (ops->remove_sample) | |
206 | ops->remove_sample(ops->private_data, wave, atomic); | |
207 | kfree(wave); | |
208 | } | |
209 | ||
210 | static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t *ops, | |
211 | iwffff_instrument_t *ip, | |
212 | int atomic) | |
213 | { | |
214 | iwffff_layer_t *layer; | |
215 | iwffff_wave_t *wave; | |
216 | ||
217 | while ((layer = ip->layer) != NULL) { | |
218 | ip->layer = layer->next; | |
219 | snd_seq_iwffff_env_free(ops, &layer->penv, atomic); | |
220 | snd_seq_iwffff_env_free(ops, &layer->venv, atomic); | |
221 | while ((wave = layer->wave) != NULL) { | |
222 | layer->wave = wave->next; | |
223 | snd_seq_iwffff_wave_free(ops, wave, atomic); | |
224 | } | |
225 | kfree(layer); | |
226 | } | |
227 | } | |
228 | ||
229 | static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr, | |
230 | char __user *instr_data, long len, int atomic, | |
231 | int cmd) | |
232 | { | |
233 | snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; | |
234 | iwffff_instrument_t *ip; | |
235 | iwffff_xinstrument_t ix; | |
236 | iwffff_layer_t *lp, *prev_lp; | |
237 | iwffff_xlayer_t lx; | |
5a0f217d VF |
238 | int err; |
239 | unsigned int gfp_mask; | |
1da177e4 LT |
240 | |
241 | if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) | |
242 | return -EINVAL; | |
243 | gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; | |
244 | /* copy instrument data */ | |
245 | if (len < (long)sizeof(ix)) | |
246 | return -EINVAL; | |
247 | if (copy_from_user(&ix, instr_data, sizeof(ix))) | |
248 | return -EFAULT; | |
249 | if (ix.stype != IWFFFF_STRU_INSTR) | |
250 | return -EINVAL; | |
251 | instr_data += sizeof(ix); | |
252 | len -= sizeof(ix); | |
253 | ip = (iwffff_instrument_t *)KINSTR_DATA(instr); | |
254 | ip->exclusion = le16_to_cpu(ix.exclusion); | |
255 | ip->layer_type = le16_to_cpu(ix.layer_type); | |
256 | ip->exclusion_group = le16_to_cpu(ix.exclusion_group); | |
257 | ip->effect1 = ix.effect1; | |
258 | ip->effect1_depth = ix.effect1_depth; | |
259 | ip->effect2 = ix.effect2; | |
260 | ip->effect2_depth = ix.effect2_depth; | |
261 | /* copy layers */ | |
262 | prev_lp = NULL; | |
263 | while (len > 0) { | |
264 | if (len < (long)sizeof(iwffff_xlayer_t)) { | |
265 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
266 | return -EINVAL; | |
267 | } | |
268 | if (copy_from_user(&lx, instr_data, sizeof(lx))) | |
269 | return -EFAULT; | |
270 | instr_data += sizeof(lx); | |
271 | len -= sizeof(lx); | |
272 | if (lx.stype != IWFFFF_STRU_LAYER) { | |
273 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
274 | return -EINVAL; | |
275 | } | |
ecca82b4 | 276 | lp = kzalloc(sizeof(*lp), gfp_mask); |
1da177e4 LT |
277 | if (lp == NULL) { |
278 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
279 | return -ENOMEM; | |
280 | } | |
281 | if (prev_lp) { | |
282 | prev_lp->next = lp; | |
283 | } else { | |
284 | ip->layer = lp; | |
285 | } | |
286 | prev_lp = lp; | |
287 | lp->flags = lx.flags; | |
288 | lp->velocity_mode = lx.velocity_mode; | |
289 | lp->layer_event = lx.layer_event; | |
290 | lp->low_range = lx.low_range; | |
291 | lp->high_range = lx.high_range; | |
292 | lp->pan = lx.pan; | |
293 | lp->pan_freq_scale = lx.pan_freq_scale; | |
294 | lp->attenuation = lx.attenuation; | |
295 | snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo); | |
296 | snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato); | |
297 | lp->freq_scale = le16_to_cpu(lx.freq_scale); | |
298 | lp->freq_center = lx.freq_center; | |
299 | err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP, | |
300 | lp, | |
301 | &lp->penv, &lx.penv, | |
302 | &instr_data, &len, | |
303 | gfp_mask); | |
304 | if (err < 0) { | |
305 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
306 | return err; | |
307 | } | |
308 | err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV, | |
309 | lp, | |
310 | &lp->venv, &lx.venv, | |
311 | &instr_data, &len, | |
312 | gfp_mask); | |
313 | if (err < 0) { | |
314 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
315 | return err; | |
316 | } | |
317 | while (len > (long)sizeof(__u32)) { | |
318 | __u32 stype; | |
319 | ||
320 | if (copy_from_user(&stype, instr_data, sizeof(stype))) | |
321 | return -EFAULT; | |
322 | if (stype != IWFFFF_STRU_WAVE) | |
323 | break; | |
324 | err = snd_seq_iwffff_copy_wave_from_stream(ops, | |
325 | lp, | |
326 | &instr_data, | |
327 | &len, | |
328 | atomic); | |
329 | if (err < 0) { | |
330 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
331 | return err; | |
332 | } | |
333 | } | |
334 | } | |
335 | return 0; | |
336 | } | |
337 | ||
338 | static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t *fx, | |
339 | iwffff_lfo_t *fp) | |
340 | { | |
341 | fx->freq = cpu_to_le16(fp->freq); | |
342 | fx->depth = cpu_to_le16(fp->depth); | |
343 | fx->sweep = cpu_to_le16(fp->sweep); | |
344 | fp->shape = fx->shape; | |
345 | fp->delay = fx->delay; | |
346 | } | |
347 | ||
348 | static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype, | |
349 | iwffff_layer_t *lp, | |
350 | iwffff_xenv_t *ex, | |
351 | iwffff_env_t *ep, | |
352 | char __user **data, | |
353 | long *len) | |
354 | { | |
355 | iwffff_env_record_t *rp; | |
356 | iwffff_xenv_record_t rx; | |
357 | iwffff_env_point_t *pp; | |
358 | iwffff_xenv_point_t px; | |
359 | int points_size, idx; | |
360 | ||
361 | ex->flags = ep->flags; | |
362 | ex->mode = ep->mode; | |
363 | ex->index = ep->index; | |
364 | for (rp = ep->record; rp; rp = rp->next) { | |
365 | if (*len < (long)sizeof(rx)) | |
366 | return -ENOMEM; | |
367 | memset(&rx, 0, sizeof(rx)); | |
368 | rx.stype = req_stype; | |
369 | rx.nattack = cpu_to_le16(rp->nattack); | |
370 | rx.nrelease = cpu_to_le16(rp->nrelease); | |
371 | rx.sustain_offset = cpu_to_le16(rp->sustain_offset); | |
372 | rx.sustain_rate = cpu_to_le16(rp->sustain_rate); | |
373 | rx.release_rate = cpu_to_le16(rp->release_rate); | |
374 | rx.hirange = cpu_to_le16(rp->hirange); | |
375 | if (copy_to_user(*data, &rx, sizeof(rx))) | |
376 | return -EFAULT; | |
377 | *data += sizeof(rx); | |
378 | *len -= sizeof(rx); | |
379 | points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); | |
380 | if (*len < points_size) | |
381 | return -ENOMEM; | |
382 | pp = (iwffff_env_point_t *)(rp + 1); | |
383 | for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { | |
384 | px.offset = cpu_to_le16(pp->offset); | |
385 | px.rate = cpu_to_le16(pp->rate); | |
386 | if (copy_to_user(*data, &px, sizeof(px))) | |
387 | return -EFAULT; | |
388 | *data += sizeof(px); | |
389 | *len -= sizeof(px); | |
390 | } | |
391 | } | |
392 | return 0; | |
393 | } | |
394 | ||
395 | static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t *ops, | |
396 | iwffff_layer_t *lp, | |
397 | char __user **data, | |
398 | long *len, | |
399 | int atomic) | |
400 | { | |
401 | iwffff_wave_t *wp; | |
402 | iwffff_xwave_t xp; | |
403 | int err; | |
404 | unsigned int real_size; | |
405 | ||
406 | for (wp = lp->wave; wp; wp = wp->next) { | |
407 | if (*len < (long)sizeof(xp)) | |
408 | return -ENOMEM; | |
409 | memset(&xp, 0, sizeof(xp)); | |
410 | xp.stype = IWFFFF_STRU_WAVE; | |
411 | xp.share_id[0] = cpu_to_le32(wp->share_id[0]); | |
412 | xp.share_id[1] = cpu_to_le32(wp->share_id[1]); | |
413 | xp.share_id[2] = cpu_to_le32(wp->share_id[2]); | |
414 | xp.share_id[3] = cpu_to_le32(wp->share_id[3]); | |
415 | xp.format = cpu_to_le32(wp->format); | |
416 | if (wp->format & IWFFFF_WAVE_ROM) | |
417 | xp.offset = cpu_to_le32(wp->address.memory); | |
418 | xp.size = cpu_to_le32(wp->size); | |
419 | xp.start = cpu_to_le32(wp->start); | |
420 | xp.loop_start = cpu_to_le32(wp->loop_start); | |
421 | xp.loop_end = cpu_to_le32(wp->loop_end); | |
422 | xp.loop_repeat = cpu_to_le32(wp->loop_repeat); | |
423 | xp.sample_ratio = cpu_to_le32(wp->sample_ratio); | |
424 | xp.attenuation = wp->attenuation; | |
425 | xp.low_note = wp->low_note; | |
426 | xp.high_note = wp->high_note; | |
427 | if (copy_to_user(*data, &xp, sizeof(xp))) | |
428 | return -EFAULT; | |
429 | *data += sizeof(xp); | |
430 | *len -= sizeof(xp); | |
431 | real_size = snd_seq_iwffff_size(wp->size, wp->format); | |
432 | if (!(wp->format & IWFFFF_WAVE_ROM)) { | |
433 | if (*len < (long)real_size) | |
434 | return -ENOMEM; | |
435 | } | |
436 | if (ops->get_sample) { | |
437 | err = ops->get_sample(ops->private_data, wp, | |
438 | *data, real_size, atomic); | |
439 | if (err < 0) | |
440 | return err; | |
441 | } | |
442 | if (!(wp->format & IWFFFF_WAVE_ROM)) { | |
443 | *data += real_size; | |
444 | *len -= real_size; | |
445 | } | |
446 | } | |
447 | return 0; | |
448 | } | |
449 | ||
450 | static int snd_seq_iwffff_get(void *private_data, snd_seq_kinstr_t *instr, | |
451 | char __user *instr_data, long len, int atomic, int cmd) | |
452 | { | |
453 | snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; | |
454 | iwffff_instrument_t *ip; | |
455 | iwffff_xinstrument_t ix; | |
456 | iwffff_layer_t *lp; | |
457 | iwffff_xlayer_t lx; | |
458 | char __user *layer_instr_data; | |
459 | int err; | |
460 | ||
461 | if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) | |
462 | return -EINVAL; | |
463 | if (len < (long)sizeof(ix)) | |
464 | return -ENOMEM; | |
465 | memset(&ix, 0, sizeof(ix)); | |
466 | ip = (iwffff_instrument_t *)KINSTR_DATA(instr); | |
467 | ix.stype = IWFFFF_STRU_INSTR; | |
468 | ix.exclusion = cpu_to_le16(ip->exclusion); | |
469 | ix.layer_type = cpu_to_le16(ip->layer_type); | |
470 | ix.exclusion_group = cpu_to_le16(ip->exclusion_group); | |
471 | ix.effect1 = cpu_to_le16(ip->effect1); | |
472 | ix.effect1_depth = cpu_to_le16(ip->effect1_depth); | |
473 | ix.effect2 = ip->effect2; | |
474 | ix.effect2_depth = ip->effect2_depth; | |
475 | if (copy_to_user(instr_data, &ix, sizeof(ix))) | |
476 | return -EFAULT; | |
477 | instr_data += sizeof(ix); | |
478 | len -= sizeof(ix); | |
479 | for (lp = ip->layer; lp; lp = lp->next) { | |
480 | if (len < (long)sizeof(lx)) | |
481 | return -ENOMEM; | |
482 | memset(&lx, 0, sizeof(lx)); | |
483 | lx.stype = IWFFFF_STRU_LAYER; | |
484 | lx.flags = lp->flags; | |
485 | lx.velocity_mode = lp->velocity_mode; | |
486 | lx.layer_event = lp->layer_event; | |
487 | lx.low_range = lp->low_range; | |
488 | lx.high_range = lp->high_range; | |
489 | lx.pan = lp->pan; | |
490 | lx.pan_freq_scale = lp->pan_freq_scale; | |
491 | lx.attenuation = lp->attenuation; | |
492 | snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo); | |
493 | snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato); | |
494 | layer_instr_data = instr_data; | |
495 | instr_data += sizeof(lx); | |
496 | len -= sizeof(lx); | |
497 | err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP, | |
498 | lp, | |
499 | &lx.penv, &lp->penv, | |
500 | &instr_data, &len); | |
501 | if (err < 0) | |
502 | return err; | |
503 | err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV, | |
504 | lp, | |
505 | &lx.venv, &lp->venv, | |
506 | &instr_data, &len); | |
507 | if (err < 0) | |
508 | return err; | |
509 | /* layer structure updating is now finished */ | |
510 | if (copy_to_user(layer_instr_data, &lx, sizeof(lx))) | |
511 | return -EFAULT; | |
512 | err = snd_seq_iwffff_copy_wave_to_stream(ops, | |
513 | lp, | |
514 | &instr_data, | |
515 | &len, | |
516 | atomic); | |
517 | if (err < 0) | |
518 | return err; | |
519 | } | |
520 | return 0; | |
521 | } | |
522 | ||
523 | static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t *ep) | |
524 | { | |
525 | long result = 0; | |
526 | iwffff_env_record_t *rp; | |
527 | ||
528 | for (rp = ep->record; rp; rp = rp->next) { | |
529 | result += sizeof(iwffff_xenv_record_t); | |
530 | result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); | |
531 | } | |
532 | return 0; | |
533 | } | |
534 | ||
535 | static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t *lp) | |
536 | { | |
537 | long result = 0; | |
538 | iwffff_wave_t *wp; | |
539 | ||
540 | for (wp = lp->wave; wp; wp = wp->next) { | |
541 | result += sizeof(iwffff_xwave_t); | |
542 | if (!(wp->format & IWFFFF_WAVE_ROM)) | |
543 | result += wp->size; | |
544 | } | |
545 | return result; | |
546 | } | |
547 | ||
548 | static int snd_seq_iwffff_get_size(void *private_data, snd_seq_kinstr_t *instr, | |
549 | long *size) | |
550 | { | |
551 | long result; | |
552 | iwffff_instrument_t *ip; | |
553 | iwffff_layer_t *lp; | |
554 | ||
555 | *size = 0; | |
556 | ip = (iwffff_instrument_t *)KINSTR_DATA(instr); | |
557 | result = sizeof(iwffff_xinstrument_t); | |
558 | for (lp = ip->layer; lp; lp = lp->next) { | |
559 | result += sizeof(iwffff_xlayer_t); | |
560 | result += snd_seq_iwffff_env_size_in_stream(&lp->penv); | |
561 | result += snd_seq_iwffff_env_size_in_stream(&lp->venv); | |
562 | result += snd_seq_iwffff_wave_size_in_stream(lp); | |
563 | } | |
564 | *size = result; | |
565 | return 0; | |
566 | } | |
567 | ||
568 | static int snd_seq_iwffff_remove(void *private_data, | |
569 | snd_seq_kinstr_t *instr, | |
570 | int atomic) | |
571 | { | |
572 | snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; | |
573 | iwffff_instrument_t *ip; | |
574 | ||
575 | ip = (iwffff_instrument_t *)KINSTR_DATA(instr); | |
576 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
577 | return 0; | |
578 | } | |
579 | ||
580 | static void snd_seq_iwffff_notify(void *private_data, | |
581 | snd_seq_kinstr_t *instr, | |
582 | int what) | |
583 | { | |
584 | snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; | |
585 | ||
586 | if (ops->notify) | |
587 | ops->notify(ops->private_data, instr, what); | |
588 | } | |
589 | ||
590 | int snd_seq_iwffff_init(snd_iwffff_ops_t *ops, | |
591 | void *private_data, | |
592 | snd_seq_kinstr_ops_t *next) | |
593 | { | |
594 | memset(ops, 0, sizeof(*ops)); | |
595 | ops->private_data = private_data; | |
596 | ops->kops.private_data = ops; | |
597 | ops->kops.add_len = sizeof(iwffff_instrument_t); | |
598 | ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE; | |
599 | ops->kops.put = snd_seq_iwffff_put; | |
600 | ops->kops.get = snd_seq_iwffff_get; | |
601 | ops->kops.get_size = snd_seq_iwffff_get_size; | |
602 | ops->kops.remove = snd_seq_iwffff_remove; | |
603 | ops->kops.notify = snd_seq_iwffff_notify; | |
604 | ops->kops.next = next; | |
605 | return 0; | |
606 | } | |
607 | ||
608 | /* | |
609 | * Init part | |
610 | */ | |
611 | ||
612 | static int __init alsa_ainstr_iw_init(void) | |
613 | { | |
614 | return 0; | |
615 | } | |
616 | ||
617 | static void __exit alsa_ainstr_iw_exit(void) | |
618 | { | |
619 | } | |
620 | ||
621 | module_init(alsa_ainstr_iw_init) | |
622 | module_exit(alsa_ainstr_iw_exit) | |
623 | ||
624 | EXPORT_SYMBOL(snd_seq_iwffff_init); |