📄 ainstr_iw.c
字号:
/* * 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, gfp_t 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 = kzalloc(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_t 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 = kzalloc(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_t 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 = kzalloc(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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -