diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/core/seq/instr/ainstr_iw.c | |
download | op-kernel-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip op-kernel-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/core/seq/instr/ainstr_iw.c')
-rw-r--r-- | sound/core/seq/instr/ainstr_iw.c | 622 |
1 files changed, 622 insertions, 0 deletions
diff --git a/sound/core/seq/instr/ainstr_iw.c b/sound/core/seq/instr/ainstr_iw.c new file mode 100644 index 0000000..39ff72b --- /dev/null +++ b/sound/core/seq/instr/ainstr_iw.c @@ -0,0 +1,622 @@ +/* + * IWFFFF - AMD InterWave (tm) - Instrument routines + * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <sound/driver.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/ainstr_iw.h> +#include <sound/initval.h> +#include <asm/uaccess.h> + +MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support."); +MODULE_LICENSE("GPL"); + +static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format) +{ + unsigned int result = size; + + if (format & IWFFFF_WAVE_16BIT) + result <<= 1; + if (format & IWFFFF_WAVE_STEREO) + result <<= 1; + return result; +} + +static void snd_seq_iwffff_copy_lfo_from_stream(iwffff_lfo_t *fp, + iwffff_xlfo_t *fx) +{ + fp->freq = le16_to_cpu(fx->freq); + fp->depth = le16_to_cpu(fx->depth); + fp->sweep = le16_to_cpu(fx->sweep); + fp->shape = fx->shape; + fp->delay = fx->delay; +} + +static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype, + iwffff_layer_t *lp, + iwffff_env_t *ep, + iwffff_xenv_t *ex, + char __user **data, + long *len, + int gfp_mask) +{ + __u32 stype; + iwffff_env_record_t *rp, *rp_last; + iwffff_xenv_record_t rx; + iwffff_env_point_t *pp; + iwffff_xenv_point_t px; + int points_size, idx; + + ep->flags = ex->flags; + ep->mode = ex->mode; + ep->index = ex->index; + rp_last = NULL; + while (1) { + if (*len < (long)sizeof(__u32)) + return -EINVAL; + if (copy_from_user(&stype, *data, sizeof(stype))) + return -EFAULT; + if (stype == IWFFFF_STRU_WAVE) + return 0; + if (req_stype != stype) { + if (stype == IWFFFF_STRU_ENV_RECP || + stype == IWFFFF_STRU_ENV_RECV) + return 0; + } + if (*len < (long)sizeof(rx)) + return -EINVAL; + if (copy_from_user(&rx, *data, sizeof(rx))) + return -EFAULT; + *data += sizeof(rx); + *len -= sizeof(rx); + points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16); + if (points_size > *len) + return -EINVAL; + rp = kcalloc(1, sizeof(*rp) + points_size, gfp_mask); + if (rp == NULL) + return -ENOMEM; + rp->nattack = le16_to_cpu(rx.nattack); + rp->nrelease = le16_to_cpu(rx.nrelease); + rp->sustain_offset = le16_to_cpu(rx.sustain_offset); + rp->sustain_rate = le16_to_cpu(rx.sustain_rate); + rp->release_rate = le16_to_cpu(rx.release_rate); + rp->hirange = rx.hirange; + pp = (iwffff_env_point_t *)(rp + 1); + for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { + if (copy_from_user(&px, *data, sizeof(px))) + return -EFAULT; + *data += sizeof(px); + *len -= sizeof(px); + pp->offset = le16_to_cpu(px.offset); + pp->rate = le16_to_cpu(px.rate); + } + if (ep->record == NULL) { + ep->record = rp; + } else { + rp_last = rp; + } + rp_last = rp; + } + return 0; +} + +static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t *ops, + iwffff_layer_t *lp, + char __user **data, + long *len, + int atomic) +{ + iwffff_wave_t *wp, *prev; + iwffff_xwave_t xp; + int err, gfp_mask; + unsigned int real_size; + + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + if (*len < (long)sizeof(xp)) + return -EINVAL; + if (copy_from_user(&xp, *data, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + wp = kcalloc(1, sizeof(*wp), gfp_mask); + if (wp == NULL) + return -ENOMEM; + wp->share_id[0] = le32_to_cpu(xp.share_id[0]); + wp->share_id[1] = le32_to_cpu(xp.share_id[1]); + wp->share_id[2] = le32_to_cpu(xp.share_id[2]); + wp->share_id[3] = le32_to_cpu(xp.share_id[3]); + wp->format = le32_to_cpu(xp.format); + wp->address.memory = le32_to_cpu(xp.offset); + wp->size = le32_to_cpu(xp.size); + wp->start = le32_to_cpu(xp.start); + wp->loop_start = le32_to_cpu(xp.loop_start); + wp->loop_end = le32_to_cpu(xp.loop_end); + wp->loop_repeat = le16_to_cpu(xp.loop_repeat); + wp->sample_ratio = le32_to_cpu(xp.sample_ratio); + wp->attenuation = xp.attenuation; + wp->low_note = xp.low_note; + wp->high_note = xp.high_note; + real_size = snd_seq_iwffff_size(wp->size, wp->format); + if (!(wp->format & IWFFFF_WAVE_ROM)) { + if ((long)real_size > *len) { + kfree(wp); + return -ENOMEM; + } + } + if (ops->put_sample) { + err = ops->put_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) { + kfree(wp); + return err; + } + } + if (!(wp->format & IWFFFF_WAVE_ROM)) { + *data += real_size; + *len -= real_size; + } + prev = lp->wave; + if (prev) { + while (prev->next) prev = prev->next; + prev->next = wp; + } else { + lp->wave = wp; + } + return 0; +} + +static void snd_seq_iwffff_env_free(snd_iwffff_ops_t *ops, + iwffff_env_t *env, + int atomic) +{ + iwffff_env_record_t *rec; + + while ((rec = env->record) != NULL) { + env->record = rec->next; + kfree(rec); + } +} + +static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t *ops, + iwffff_wave_t *wave, + int atomic) +{ + if (ops->remove_sample) + ops->remove_sample(ops->private_data, wave, atomic); + kfree(wave); +} + +static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t *ops, + iwffff_instrument_t *ip, + int atomic) +{ + iwffff_layer_t *layer; + iwffff_wave_t *wave; + + while ((layer = ip->layer) != NULL) { + ip->layer = layer->next; + snd_seq_iwffff_env_free(ops, &layer->penv, atomic); + snd_seq_iwffff_env_free(ops, &layer->venv, atomic); + while ((wave = layer->wave) != NULL) { + layer->wave = wave->next; + snd_seq_iwffff_wave_free(ops, wave, atomic); + } + kfree(layer); + } +} + +static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr, + char __user *instr_data, long len, int atomic, + int cmd) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + iwffff_xinstrument_t ix; + iwffff_layer_t *lp, *prev_lp; + iwffff_xlayer_t lx; + int err, gfp_mask; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + /* copy instrument data */ + if (len < (long)sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != IWFFFF_STRU_INSTR) + return -EINVAL; + instr_data += sizeof(ix); + len -= sizeof(ix); + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + ip->exclusion = le16_to_cpu(ix.exclusion); + ip->layer_type = le16_to_cpu(ix.layer_type); + ip->exclusion_group = le16_to_cpu(ix.exclusion_group); + ip->effect1 = ix.effect1; + ip->effect1_depth = ix.effect1_depth; + ip->effect2 = ix.effect2; + ip->effect2_depth = ix.effect2_depth; + /* copy layers */ + prev_lp = NULL; + while (len > 0) { + if (len < (long)sizeof(iwffff_xlayer_t)) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -EINVAL; + } + if (copy_from_user(&lx, instr_data, sizeof(lx))) + return -EFAULT; + instr_data += sizeof(lx); + len -= sizeof(lx); + if (lx.stype != IWFFFF_STRU_LAYER) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -EINVAL; + } + lp = kcalloc(1, sizeof(*lp), gfp_mask); + if (lp == NULL) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -ENOMEM; + } + if (prev_lp) { + prev_lp->next = lp; + } else { + ip->layer = lp; + } + prev_lp = lp; + lp->flags = lx.flags; + lp->velocity_mode = lx.velocity_mode; + lp->layer_event = lx.layer_event; + lp->low_range = lx.low_range; + lp->high_range = lx.high_range; + lp->pan = lx.pan; + lp->pan_freq_scale = lx.pan_freq_scale; + lp->attenuation = lx.attenuation; + snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo); + snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato); + lp->freq_scale = le16_to_cpu(lx.freq_scale); + lp->freq_center = lx.freq_center; + err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP, + lp, + &lp->penv, &lx.penv, + &instr_data, &len, + gfp_mask); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV, + lp, + &lp->venv, &lx.venv, + &instr_data, &len, + gfp_mask); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + while (len > (long)sizeof(__u32)) { + __u32 stype; + + if (copy_from_user(&stype, instr_data, sizeof(stype))) + return -EFAULT; + if (stype != IWFFFF_STRU_WAVE) + break; + err = snd_seq_iwffff_copy_wave_from_stream(ops, + lp, + &instr_data, + &len, + atomic); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + } + } + return 0; +} + +static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t *fx, + iwffff_lfo_t *fp) +{ + fx->freq = cpu_to_le16(fp->freq); + fx->depth = cpu_to_le16(fp->depth); + fx->sweep = cpu_to_le16(fp->sweep); + fp->shape = fx->shape; + fp->delay = fx->delay; +} + +static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype, + iwffff_layer_t *lp, + iwffff_xenv_t *ex, + iwffff_env_t *ep, + char __user **data, + long *len) +{ + iwffff_env_record_t *rp; + iwffff_xenv_record_t rx; + iwffff_env_point_t *pp; + iwffff_xenv_point_t px; + int points_size, idx; + + ex->flags = ep->flags; + ex->mode = ep->mode; + ex->index = ep->index; + for (rp = ep->record; rp; rp = rp->next) { + if (*len < (long)sizeof(rx)) + return -ENOMEM; + memset(&rx, 0, sizeof(rx)); + rx.stype = req_stype; + rx.nattack = cpu_to_le16(rp->nattack); + rx.nrelease = cpu_to_le16(rp->nrelease); + rx.sustain_offset = cpu_to_le16(rp->sustain_offset); + rx.sustain_rate = cpu_to_le16(rp->sustain_rate); + rx.release_rate = cpu_to_le16(rp->release_rate); + rx.hirange = cpu_to_le16(rp->hirange); + if (copy_to_user(*data, &rx, sizeof(rx))) + return -EFAULT; + *data += sizeof(rx); + *len -= sizeof(rx); + points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); + if (*len < points_size) + return -ENOMEM; + pp = (iwffff_env_point_t *)(rp + 1); + for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { + px.offset = cpu_to_le16(pp->offset); + px.rate = cpu_to_le16(pp->rate); + if (copy_to_user(*data, &px, sizeof(px))) + return -EFAULT; + *data += sizeof(px); + *len -= sizeof(px); + } + } + return 0; +} + +static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t *ops, + iwffff_layer_t *lp, + char __user **data, + long *len, + int atomic) +{ + iwffff_wave_t *wp; + iwffff_xwave_t xp; + int err; + unsigned int real_size; + + for (wp = lp->wave; wp; wp = wp->next) { + if (*len < (long)sizeof(xp)) + return -ENOMEM; + memset(&xp, 0, sizeof(xp)); + xp.stype = IWFFFF_STRU_WAVE; + xp.share_id[0] = cpu_to_le32(wp->share_id[0]); + xp.share_id[1] = cpu_to_le32(wp->share_id[1]); + xp.share_id[2] = cpu_to_le32(wp->share_id[2]); + xp.share_id[3] = cpu_to_le32(wp->share_id[3]); + xp.format = cpu_to_le32(wp->format); + if (wp->format & IWFFFF_WAVE_ROM) + xp.offset = cpu_to_le32(wp->address.memory); + xp.size = cpu_to_le32(wp->size); + xp.start = cpu_to_le32(wp->start); + xp.loop_start = cpu_to_le32(wp->loop_start); + xp.loop_end = cpu_to_le32(wp->loop_end); + xp.loop_repeat = cpu_to_le32(wp->loop_repeat); + xp.sample_ratio = cpu_to_le32(wp->sample_ratio); + xp.attenuation = wp->attenuation; + xp.low_note = wp->low_note; + xp.high_note = wp->high_note; + if (copy_to_user(*data, &xp, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + real_size = snd_seq_iwffff_size(wp->size, wp->format); + if (!(wp->format & IWFFFF_WAVE_ROM)) { + if (*len < (long)real_size) + return -ENOMEM; + } + if (ops->get_sample) { + err = ops->get_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) + return err; + } + if (!(wp->format & IWFFFF_WAVE_ROM)) { + *data += real_size; + *len -= real_size; + } + } + return 0; +} + +static int snd_seq_iwffff_get(void *private_data, snd_seq_kinstr_t *instr, + char __user *instr_data, long len, int atomic, int cmd) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + iwffff_xinstrument_t ix; + iwffff_layer_t *lp; + iwffff_xlayer_t lx; + char __user *layer_instr_data; + int err; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < (long)sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + ix.stype = IWFFFF_STRU_INSTR; + ix.exclusion = cpu_to_le16(ip->exclusion); + ix.layer_type = cpu_to_le16(ip->layer_type); + ix.exclusion_group = cpu_to_le16(ip->exclusion_group); + ix.effect1 = cpu_to_le16(ip->effect1); + ix.effect1_depth = cpu_to_le16(ip->effect1_depth); + ix.effect2 = ip->effect2; + ix.effect2_depth = ip->effect2_depth; + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + instr_data += sizeof(ix); + len -= sizeof(ix); + for (lp = ip->layer; lp; lp = lp->next) { + if (len < (long)sizeof(lx)) + return -ENOMEM; + memset(&lx, 0, sizeof(lx)); + lx.stype = IWFFFF_STRU_LAYER; + lx.flags = lp->flags; + lx.velocity_mode = lp->velocity_mode; + lx.layer_event = lp->layer_event; + lx.low_range = lp->low_range; + lx.high_range = lp->high_range; + lx.pan = lp->pan; + lx.pan_freq_scale = lp->pan_freq_scale; + lx.attenuation = lp->attenuation; + snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo); + snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato); + layer_instr_data = instr_data; + instr_data += sizeof(lx); + len -= sizeof(lx); + err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP, + lp, + &lx.penv, &lp->penv, + &instr_data, &len); + if (err < 0) + return err; + err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV, + lp, + &lx.venv, &lp->venv, + &instr_data, &len); + if (err < 0) + return err; + /* layer structure updating is now finished */ + if (copy_to_user(layer_instr_data, &lx, sizeof(lx))) + return -EFAULT; + err = snd_seq_iwffff_copy_wave_to_stream(ops, + lp, + &instr_data, + &len, + atomic); + if (err < 0) + return err; + } + return 0; +} + +static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t *ep) +{ + long result = 0; + iwffff_env_record_t *rp; + + for (rp = ep->record; rp; rp = rp->next) { + result += sizeof(iwffff_xenv_record_t); + result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); + } + return 0; +} + +static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t *lp) +{ + long result = 0; + iwffff_wave_t *wp; + + for (wp = lp->wave; wp; wp = wp->next) { + result += sizeof(iwffff_xwave_t); + if (!(wp->format & IWFFFF_WAVE_ROM)) + result += wp->size; + } + return result; +} + +static int snd_seq_iwffff_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + long result; + iwffff_instrument_t *ip; + iwffff_layer_t *lp; + + *size = 0; + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + result = sizeof(iwffff_xinstrument_t); + for (lp = ip->layer; lp; lp = lp->next) { + result += sizeof(iwffff_xlayer_t); + result += snd_seq_iwffff_env_size_in_stream(&lp->penv); + result += snd_seq_iwffff_env_size_in_stream(&lp->venv); + result += snd_seq_iwffff_wave_size_in_stream(lp); + } + *size = result; + return 0; +} + +static int snd_seq_iwffff_remove(void *private_data, + snd_seq_kinstr_t *instr, + int atomic) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + snd_seq_iwffff_instr_free(ops, ip, atomic); + return 0; +} + +static void snd_seq_iwffff_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + + if (ops->notify) + ops->notify(ops->private_data, instr, what); +} + +int snd_seq_iwffff_init(snd_iwffff_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + ops->private_data = private_data; + ops->kops.private_data = ops; + ops->kops.add_len = sizeof(iwffff_instrument_t); + ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE; + ops->kops.put = snd_seq_iwffff_put; + ops->kops.get = snd_seq_iwffff_get; + ops->kops.get_size = snd_seq_iwffff_get_size; + ops->kops.remove = snd_seq_iwffff_remove; + ops->kops.notify = snd_seq_iwffff_notify; + ops->kops.next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_iw_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_iw_exit(void) +{ +} + +module_init(alsa_ainstr_iw_init) +module_exit(alsa_ainstr_iw_exit) + +EXPORT_SYMBOL(snd_seq_iwffff_init); |