Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * IWFFFF - AMD InterWave (tm) - Instrument routines | |
c1017a4c | 3 | * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> |
1da177e4 LT |
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> | |
1da177e4 LT |
23 | #include <linux/slab.h> |
24 | #include <sound/core.h> | |
25 | #include <sound/ainstr_iw.h> | |
26 | #include <sound/initval.h> | |
27 | #include <asm/uaccess.h> | |
28 | ||
c1017a4c | 29 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
1da177e4 LT |
30 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support."); |
31 | MODULE_LICENSE("GPL"); | |
32 | ||
33 | static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format) | |
34 | { | |
35 | unsigned int result = size; | |
36 | ||
37 | if (format & IWFFFF_WAVE_16BIT) | |
38 | result <<= 1; | |
39 | if (format & IWFFFF_WAVE_STEREO) | |
40 | result <<= 1; | |
41 | return result; | |
42 | } | |
43 | ||
19ac31e8 TI |
44 | static void snd_seq_iwffff_copy_lfo_from_stream(struct iwffff_lfo *fp, |
45 | struct iwffff_xlfo *fx) | |
1da177e4 LT |
46 | { |
47 | fp->freq = le16_to_cpu(fx->freq); | |
48 | fp->depth = le16_to_cpu(fx->depth); | |
49 | fp->sweep = le16_to_cpu(fx->sweep); | |
50 | fp->shape = fx->shape; | |
51 | fp->delay = fx->delay; | |
52 | } | |
53 | ||
54 | static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype, | |
19ac31e8 TI |
55 | struct iwffff_layer *lp, |
56 | struct iwffff_env *ep, | |
57 | struct iwffff_xenv *ex, | |
1da177e4 LT |
58 | char __user **data, |
59 | long *len, | |
dd0fc66f | 60 | gfp_t gfp_mask) |
1da177e4 LT |
61 | { |
62 | __u32 stype; | |
19ac31e8 TI |
63 | struct iwffff_env_record *rp, *rp_last; |
64 | struct iwffff_xenv_record rx; | |
65 | struct iwffff_env_point *pp; | |
66 | struct iwffff_xenv_point px; | |
1da177e4 LT |
67 | int points_size, idx; |
68 | ||
69 | ep->flags = ex->flags; | |
70 | ep->mode = ex->mode; | |
71 | ep->index = ex->index; | |
72 | rp_last = NULL; | |
73 | while (1) { | |
74 | if (*len < (long)sizeof(__u32)) | |
75 | return -EINVAL; | |
76 | if (copy_from_user(&stype, *data, sizeof(stype))) | |
77 | return -EFAULT; | |
78 | if (stype == IWFFFF_STRU_WAVE) | |
79 | return 0; | |
80 | if (req_stype != stype) { | |
81 | if (stype == IWFFFF_STRU_ENV_RECP || | |
82 | stype == IWFFFF_STRU_ENV_RECV) | |
83 | return 0; | |
84 | } | |
85 | if (*len < (long)sizeof(rx)) | |
86 | return -EINVAL; | |
87 | if (copy_from_user(&rx, *data, sizeof(rx))) | |
88 | return -EFAULT; | |
89 | *data += sizeof(rx); | |
90 | *len -= sizeof(rx); | |
91 | points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16); | |
92 | if (points_size > *len) | |
93 | return -EINVAL; | |
ecca82b4 | 94 | rp = kzalloc(sizeof(*rp) + points_size, gfp_mask); |
1da177e4 LT |
95 | if (rp == NULL) |
96 | return -ENOMEM; | |
97 | rp->nattack = le16_to_cpu(rx.nattack); | |
98 | rp->nrelease = le16_to_cpu(rx.nrelease); | |
99 | rp->sustain_offset = le16_to_cpu(rx.sustain_offset); | |
100 | rp->sustain_rate = le16_to_cpu(rx.sustain_rate); | |
101 | rp->release_rate = le16_to_cpu(rx.release_rate); | |
102 | rp->hirange = rx.hirange; | |
19ac31e8 | 103 | pp = (struct iwffff_env_point *)(rp + 1); |
1da177e4 LT |
104 | for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { |
105 | if (copy_from_user(&px, *data, sizeof(px))) | |
106 | return -EFAULT; | |
107 | *data += sizeof(px); | |
108 | *len -= sizeof(px); | |
109 | pp->offset = le16_to_cpu(px.offset); | |
110 | pp->rate = le16_to_cpu(px.rate); | |
111 | } | |
112 | if (ep->record == NULL) { | |
113 | ep->record = rp; | |
114 | } else { | |
115 | rp_last = rp; | |
116 | } | |
117 | rp_last = rp; | |
118 | } | |
119 | return 0; | |
120 | } | |
121 | ||
19ac31e8 TI |
122 | static int snd_seq_iwffff_copy_wave_from_stream(struct snd_iwffff_ops *ops, |
123 | struct iwffff_layer *lp, | |
1da177e4 LT |
124 | char __user **data, |
125 | long *len, | |
126 | int atomic) | |
127 | { | |
19ac31e8 TI |
128 | struct iwffff_wave *wp, *prev; |
129 | struct iwffff_xwave xp; | |
5a0f217d | 130 | int err; |
1ef64e67 | 131 | gfp_t gfp_mask; |
1da177e4 LT |
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); | |
ecca82b4 | 141 | wp = kzalloc(sizeof(*wp), gfp_mask); |
1da177e4 LT |
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 | ||
19ac31e8 TI |
188 | static void snd_seq_iwffff_env_free(struct snd_iwffff_ops *ops, |
189 | struct iwffff_env *env, | |
1da177e4 LT |
190 | int atomic) |
191 | { | |
19ac31e8 | 192 | struct iwffff_env_record *rec; |
1da177e4 LT |
193 | |
194 | while ((rec = env->record) != NULL) { | |
195 | env->record = rec->next; | |
196 | kfree(rec); | |
197 | } | |
198 | } | |
199 | ||
19ac31e8 TI |
200 | static void snd_seq_iwffff_wave_free(struct snd_iwffff_ops *ops, |
201 | struct iwffff_wave *wave, | |
1da177e4 LT |
202 | int atomic) |
203 | { | |
204 | if (ops->remove_sample) | |
205 | ops->remove_sample(ops->private_data, wave, atomic); | |
206 | kfree(wave); | |
207 | } | |
208 | ||
19ac31e8 TI |
209 | static void snd_seq_iwffff_instr_free(struct snd_iwffff_ops *ops, |
210 | struct iwffff_instrument *ip, | |
1da177e4 LT |
211 | int atomic) |
212 | { | |
19ac31e8 TI |
213 | struct iwffff_layer *layer; |
214 | struct iwffff_wave *wave; | |
1da177e4 LT |
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 | ||
19ac31e8 | 228 | static int snd_seq_iwffff_put(void *private_data, struct snd_seq_kinstr *instr, |
1da177e4 LT |
229 | char __user *instr_data, long len, int atomic, |
230 | int cmd) | |
231 | { | |
19ac31e8 TI |
232 | struct snd_iwffff_ops *ops = private_data; |
233 | struct iwffff_instrument *ip; | |
234 | struct iwffff_xinstrument ix; | |
235 | struct iwffff_layer *lp, *prev_lp; | |
236 | struct iwffff_xlayer lx; | |
5a0f217d | 237 | int err; |
1ef64e67 | 238 | gfp_t gfp_mask; |
1da177e4 LT |
239 | |
240 | if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) | |
241 | return -EINVAL; | |
242 | gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; | |
243 | /* copy instrument data */ | |
244 | if (len < (long)sizeof(ix)) | |
245 | return -EINVAL; | |
246 | if (copy_from_user(&ix, instr_data, sizeof(ix))) | |
247 | return -EFAULT; | |
248 | if (ix.stype != IWFFFF_STRU_INSTR) | |
249 | return -EINVAL; | |
250 | instr_data += sizeof(ix); | |
251 | len -= sizeof(ix); | |
19ac31e8 | 252 | ip = (struct iwffff_instrument *)KINSTR_DATA(instr); |
1da177e4 LT |
253 | ip->exclusion = le16_to_cpu(ix.exclusion); |
254 | ip->layer_type = le16_to_cpu(ix.layer_type); | |
255 | ip->exclusion_group = le16_to_cpu(ix.exclusion_group); | |
256 | ip->effect1 = ix.effect1; | |
257 | ip->effect1_depth = ix.effect1_depth; | |
258 | ip->effect2 = ix.effect2; | |
259 | ip->effect2_depth = ix.effect2_depth; | |
260 | /* copy layers */ | |
261 | prev_lp = NULL; | |
262 | while (len > 0) { | |
19ac31e8 | 263 | if (len < (long)sizeof(struct iwffff_xlayer)) { |
1da177e4 LT |
264 | snd_seq_iwffff_instr_free(ops, ip, atomic); |
265 | return -EINVAL; | |
266 | } | |
267 | if (copy_from_user(&lx, instr_data, sizeof(lx))) | |
268 | return -EFAULT; | |
269 | instr_data += sizeof(lx); | |
270 | len -= sizeof(lx); | |
271 | if (lx.stype != IWFFFF_STRU_LAYER) { | |
272 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
273 | return -EINVAL; | |
274 | } | |
ecca82b4 | 275 | lp = kzalloc(sizeof(*lp), gfp_mask); |
1da177e4 LT |
276 | if (lp == NULL) { |
277 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
278 | return -ENOMEM; | |
279 | } | |
280 | if (prev_lp) { | |
281 | prev_lp->next = lp; | |
282 | } else { | |
283 | ip->layer = lp; | |
284 | } | |
285 | prev_lp = lp; | |
286 | lp->flags = lx.flags; | |
287 | lp->velocity_mode = lx.velocity_mode; | |
288 | lp->layer_event = lx.layer_event; | |
289 | lp->low_range = lx.low_range; | |
290 | lp->high_range = lx.high_range; | |
291 | lp->pan = lx.pan; | |
292 | lp->pan_freq_scale = lx.pan_freq_scale; | |
293 | lp->attenuation = lx.attenuation; | |
294 | snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo); | |
295 | snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato); | |
296 | lp->freq_scale = le16_to_cpu(lx.freq_scale); | |
297 | lp->freq_center = lx.freq_center; | |
298 | err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP, | |
299 | lp, | |
300 | &lp->penv, &lx.penv, | |
301 | &instr_data, &len, | |
302 | gfp_mask); | |
303 | if (err < 0) { | |
304 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
305 | return err; | |
306 | } | |
307 | err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV, | |
308 | lp, | |
309 | &lp->venv, &lx.venv, | |
310 | &instr_data, &len, | |
311 | gfp_mask); | |
312 | if (err < 0) { | |
313 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
314 | return err; | |
315 | } | |
316 | while (len > (long)sizeof(__u32)) { | |
317 | __u32 stype; | |
318 | ||
319 | if (copy_from_user(&stype, instr_data, sizeof(stype))) | |
320 | return -EFAULT; | |
321 | if (stype != IWFFFF_STRU_WAVE) | |
322 | break; | |
323 | err = snd_seq_iwffff_copy_wave_from_stream(ops, | |
324 | lp, | |
325 | &instr_data, | |
326 | &len, | |
327 | atomic); | |
328 | if (err < 0) { | |
329 | snd_seq_iwffff_instr_free(ops, ip, atomic); | |
330 | return err; | |
331 | } | |
332 | } | |
333 | } | |
334 | return 0; | |
335 | } | |
336 | ||
19ac31e8 TI |
337 | static void snd_seq_iwffff_copy_lfo_to_stream(struct iwffff_xlfo *fx, |
338 | struct iwffff_lfo *fp) | |
1da177e4 LT |
339 | { |
340 | fx->freq = cpu_to_le16(fp->freq); | |
341 | fx->depth = cpu_to_le16(fp->depth); | |
342 | fx->sweep = cpu_to_le16(fp->sweep); | |
343 | fp->shape = fx->shape; | |
344 | fp->delay = fx->delay; | |
345 | } | |
346 | ||
347 | static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype, | |
19ac31e8 TI |
348 | struct iwffff_layer *lp, |
349 | struct iwffff_xenv *ex, | |
350 | struct iwffff_env *ep, | |
1da177e4 LT |
351 | char __user **data, |
352 | long *len) | |
353 | { | |
19ac31e8 TI |
354 | struct iwffff_env_record *rp; |
355 | struct iwffff_xenv_record rx; | |
356 | struct iwffff_env_point *pp; | |
357 | struct iwffff_xenv_point px; | |
1da177e4 LT |
358 | int points_size, idx; |
359 | ||
360 | ex->flags = ep->flags; | |
361 | ex->mode = ep->mode; | |
362 | ex->index = ep->index; | |
363 | for (rp = ep->record; rp; rp = rp->next) { | |
364 | if (*len < (long)sizeof(rx)) | |
365 | return -ENOMEM; | |
366 | memset(&rx, 0, sizeof(rx)); | |
367 | rx.stype = req_stype; | |
368 | rx.nattack = cpu_to_le16(rp->nattack); | |
369 | rx.nrelease = cpu_to_le16(rp->nrelease); | |
370 | rx.sustain_offset = cpu_to_le16(rp->sustain_offset); | |
371 | rx.sustain_rate = cpu_to_le16(rp->sustain_rate); | |
372 | rx.release_rate = cpu_to_le16(rp->release_rate); | |
373 | rx.hirange = cpu_to_le16(rp->hirange); | |
374 | if (copy_to_user(*data, &rx, sizeof(rx))) | |
375 | return -EFAULT; | |
376 | *data += sizeof(rx); | |
377 | *len -= sizeof(rx); | |
378 | points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); | |
379 | if (*len < points_size) | |
380 | return -ENOMEM; | |
19ac31e8 | 381 | pp = (struct iwffff_env_point *)(rp + 1); |
1da177e4 LT |
382 | for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { |
383 | px.offset = cpu_to_le16(pp->offset); | |
384 | px.rate = cpu_to_le16(pp->rate); | |
385 | if (copy_to_user(*data, &px, sizeof(px))) | |
386 | return -EFAULT; | |
387 | *data += sizeof(px); | |
388 | *len -= sizeof(px); | |
389 | } | |
390 | } | |
391 | return 0; | |
392 | } | |
393 | ||
19ac31e8 TI |
394 | static int snd_seq_iwffff_copy_wave_to_stream(struct snd_iwffff_ops *ops, |
395 | struct iwffff_layer *lp, | |
1da177e4 LT |
396 | char __user **data, |
397 | long *len, | |
398 | int atomic) | |
399 | { | |
19ac31e8 TI |
400 | struct iwffff_wave *wp; |
401 | struct iwffff_xwave xp; | |
1da177e4 LT |
402 | int err; |
403 | unsigned int real_size; | |
404 | ||
405 | for (wp = lp->wave; wp; wp = wp->next) { | |
406 | if (*len < (long)sizeof(xp)) | |
407 | return -ENOMEM; | |
408 | memset(&xp, 0, sizeof(xp)); | |
409 | xp.stype = IWFFFF_STRU_WAVE; | |
410 | xp.share_id[0] = cpu_to_le32(wp->share_id[0]); | |
411 | xp.share_id[1] = cpu_to_le32(wp->share_id[1]); | |
412 | xp.share_id[2] = cpu_to_le32(wp->share_id[2]); | |
413 | xp.share_id[3] = cpu_to_le32(wp->share_id[3]); | |
414 | xp.format = cpu_to_le32(wp->format); | |
415 | if (wp->format & IWFFFF_WAVE_ROM) | |
416 | xp.offset = cpu_to_le32(wp->address.memory); | |
417 | xp.size = cpu_to_le32(wp->size); | |
418 | xp.start = cpu_to_le32(wp->start); | |
419 | xp.loop_start = cpu_to_le32(wp->loop_start); | |
420 | xp.loop_end = cpu_to_le32(wp->loop_end); | |
421 | xp.loop_repeat = cpu_to_le32(wp->loop_repeat); | |
422 | xp.sample_ratio = cpu_to_le32(wp->sample_ratio); | |
423 | xp.attenuation = wp->attenuation; | |
424 | xp.low_note = wp->low_note; | |
425 | xp.high_note = wp->high_note; | |
426 | if (copy_to_user(*data, &xp, sizeof(xp))) | |
427 | return -EFAULT; | |
428 | *data += sizeof(xp); | |
429 | *len -= sizeof(xp); | |
430 | real_size = snd_seq_iwffff_size(wp->size, wp->format); | |
431 | if (!(wp->format & IWFFFF_WAVE_ROM)) { | |
432 | if (*len < (long)real_size) | |
433 | return -ENOMEM; | |
434 | } | |
435 | if (ops->get_sample) { | |
436 | err = ops->get_sample(ops->private_data, wp, | |
437 | *data, real_size, atomic); | |
438 | if (err < 0) | |
439 | return err; | |
440 | } | |
441 | if (!(wp->format & IWFFFF_WAVE_ROM)) { | |
442 | *data += real_size; | |
443 | *len -= real_size; | |
444 | } | |
445 | } | |
446 | return 0; | |
447 | } | |
448 | ||
19ac31e8 | 449 | static int snd_seq_iwffff_get(void *private_data, struct snd_seq_kinstr *instr, |
1da177e4 LT |
450 | char __user *instr_data, long len, int atomic, int cmd) |
451 | { | |
19ac31e8 TI |
452 | struct snd_iwffff_ops *ops = private_data; |
453 | struct iwffff_instrument *ip; | |
454 | struct iwffff_xinstrument ix; | |
455 | struct iwffff_layer *lp; | |
456 | struct iwffff_xlayer lx; | |
1da177e4 LT |
457 | char __user *layer_instr_data; |
458 | int err; | |
459 | ||
460 | if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) | |
461 | return -EINVAL; | |
462 | if (len < (long)sizeof(ix)) | |
463 | return -ENOMEM; | |
464 | memset(&ix, 0, sizeof(ix)); | |
19ac31e8 | 465 | ip = (struct iwffff_instrument *)KINSTR_DATA(instr); |
1da177e4 LT |
466 | ix.stype = IWFFFF_STRU_INSTR; |
467 | ix.exclusion = cpu_to_le16(ip->exclusion); | |
468 | ix.layer_type = cpu_to_le16(ip->layer_type); | |
469 | ix.exclusion_group = cpu_to_le16(ip->exclusion_group); | |
470 | ix.effect1 = cpu_to_le16(ip->effect1); | |
471 | ix.effect1_depth = cpu_to_le16(ip->effect1_depth); | |
472 | ix.effect2 = ip->effect2; | |
473 | ix.effect2_depth = ip->effect2_depth; | |
474 | if (copy_to_user(instr_data, &ix, sizeof(ix))) | |
475 | return -EFAULT; | |
476 | instr_data += sizeof(ix); | |
477 | len -= sizeof(ix); | |
478 | for (lp = ip->layer; lp; lp = lp->next) { | |
479 | if (len < (long)sizeof(lx)) | |
480 | return -ENOMEM; | |
481 | memset(&lx, 0, sizeof(lx)); | |
482 | lx.stype = IWFFFF_STRU_LAYER; | |
483 | lx.flags = lp->flags; | |
484 | lx.velocity_mode = lp->velocity_mode; | |
485 | lx.layer_event = lp->layer_event; | |
486 | lx.low_range = lp->low_range; | |
487 | lx.high_range = lp->high_range; | |
488 | lx.pan = lp->pan; | |
489 | lx.pan_freq_scale = lp->pan_freq_scale; | |
490 | lx.attenuation = lp->attenuation; | |
491 | snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo); | |
492 | snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato); | |
493 | layer_instr_data = instr_data; | |
494 | instr_data += sizeof(lx); | |
495 | len -= sizeof(lx); | |
496 | err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP, | |
497 | lp, | |
498 | &lx.penv, &lp->penv, | |
499 | &instr_data, &len); | |
500 | if (err < 0) | |
501 | return err; | |
502 | err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV, | |
503 | lp, | |
504 | &lx.venv, &lp->venv, | |
505 | &instr_data, &len); | |
506 | if (err < 0) | |
507 | return err; | |
508 | /* layer structure updating is now finished */ | |
509 | if (copy_to_user(layer_instr_data, &lx, sizeof(lx))) | |
510 | return -EFAULT; | |
511 | err = snd_seq_iwffff_copy_wave_to_stream(ops, | |
512 | lp, | |
513 | &instr_data, | |
514 | &len, | |
515 | atomic); | |
516 | if (err < 0) | |
517 | return err; | |
518 | } | |
519 | return 0; | |
520 | } | |
521 | ||
19ac31e8 | 522 | static long snd_seq_iwffff_env_size_in_stream(struct iwffff_env *ep) |
1da177e4 LT |
523 | { |
524 | long result = 0; | |
19ac31e8 | 525 | struct iwffff_env_record *rp; |
1da177e4 LT |
526 | |
527 | for (rp = ep->record; rp; rp = rp->next) { | |
19ac31e8 | 528 | result += sizeof(struct iwffff_xenv_record); |
1da177e4 LT |
529 | result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); |
530 | } | |
531 | return 0; | |
532 | } | |
533 | ||
19ac31e8 | 534 | static long snd_seq_iwffff_wave_size_in_stream(struct iwffff_layer *lp) |
1da177e4 LT |
535 | { |
536 | long result = 0; | |
19ac31e8 | 537 | struct iwffff_wave *wp; |
1da177e4 LT |
538 | |
539 | for (wp = lp->wave; wp; wp = wp->next) { | |
19ac31e8 | 540 | result += sizeof(struct iwffff_xwave); |
1da177e4 LT |
541 | if (!(wp->format & IWFFFF_WAVE_ROM)) |
542 | result += wp->size; | |
543 | } | |
544 | return result; | |
545 | } | |
546 | ||
19ac31e8 | 547 | static int snd_seq_iwffff_get_size(void *private_data, struct snd_seq_kinstr *instr, |
1da177e4 LT |
548 | long *size) |
549 | { | |
550 | long result; | |
19ac31e8 TI |
551 | struct iwffff_instrument *ip; |
552 | struct iwffff_layer *lp; | |
1da177e4 LT |
553 | |
554 | *size = 0; | |
19ac31e8 TI |
555 | ip = (struct iwffff_instrument *)KINSTR_DATA(instr); |
556 | result = sizeof(struct iwffff_xinstrument); | |
1da177e4 | 557 | for (lp = ip->layer; lp; lp = lp->next) { |
19ac31e8 | 558 | result += sizeof(struct iwffff_xlayer); |
1da177e4 LT |
559 | result += snd_seq_iwffff_env_size_in_stream(&lp->penv); |
560 | result += snd_seq_iwffff_env_size_in_stream(&lp->venv); | |
561 | result += snd_seq_iwffff_wave_size_in_stream(lp); | |
562 | } | |
563 | *size = result; | |
564 | return 0; | |
565 | } | |
566 | ||
567 | static int snd_seq_iwffff_remove(void *private_data, | |
19ac31e8 | 568 | struct snd_seq_kinstr *instr, |
1da177e4 LT |
569 | int atomic) |
570 | { | |
19ac31e8 TI |
571 | struct snd_iwffff_ops *ops = private_data; |
572 | struct iwffff_instrument *ip; | |
1da177e4 | 573 | |
19ac31e8 | 574 | ip = (struct iwffff_instrument *)KINSTR_DATA(instr); |
1da177e4 LT |
575 | snd_seq_iwffff_instr_free(ops, ip, atomic); |
576 | return 0; | |
577 | } | |
578 | ||
579 | static void snd_seq_iwffff_notify(void *private_data, | |
19ac31e8 | 580 | struct snd_seq_kinstr *instr, |
1da177e4 LT |
581 | int what) |
582 | { | |
19ac31e8 | 583 | struct snd_iwffff_ops *ops = private_data; |
1da177e4 LT |
584 | |
585 | if (ops->notify) | |
586 | ops->notify(ops->private_data, instr, what); | |
587 | } | |
588 | ||
19ac31e8 | 589 | int snd_seq_iwffff_init(struct snd_iwffff_ops *ops, |
1da177e4 | 590 | void *private_data, |
19ac31e8 | 591 | struct snd_seq_kinstr_ops *next) |
1da177e4 LT |
592 | { |
593 | memset(ops, 0, sizeof(*ops)); | |
594 | ops->private_data = private_data; | |
595 | ops->kops.private_data = ops; | |
19ac31e8 | 596 | ops->kops.add_len = sizeof(struct iwffff_instrument); |
1da177e4 LT |
597 | ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE; |
598 | ops->kops.put = snd_seq_iwffff_put; | |
599 | ops->kops.get = snd_seq_iwffff_get; | |
600 | ops->kops.get_size = snd_seq_iwffff_get_size; | |
601 | ops->kops.remove = snd_seq_iwffff_remove; | |
602 | ops->kops.notify = snd_seq_iwffff_notify; | |
603 | ops->kops.next = next; | |
604 | return 0; | |
605 | } | |
606 | ||
607 | /* | |
608 | * Init part | |
609 | */ | |
610 | ||
611 | static int __init alsa_ainstr_iw_init(void) | |
612 | { | |
613 | return 0; | |
614 | } | |
615 | ||
616 | static void __exit alsa_ainstr_iw_exit(void) | |
617 | { | |
618 | } | |
619 | ||
620 | module_init(alsa_ainstr_iw_init) | |
621 | module_exit(alsa_ainstr_iw_exit) | |
622 | ||
623 | EXPORT_SYMBOL(snd_seq_iwffff_init); |