📄 sb16_csp.c
字号:
/* * Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si> * Takashi Iwai <tiwai@suse.de> * * SB16ASP/AWE32 CSP control * * CSP microcode loader: * alsa-tools/sb16_csp/ * * 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/delay.h>#include <linux/init.h>#include <linux/slab.h>#include <sound/core.h>#include <sound/control.h>#include <sound/info.h>#include <sound/sb16_csp.h>#include <sound/initval.h>MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor");MODULE_LICENSE("GPL");#ifdef SNDRV_LITTLE_ENDIAN#define CSP_HDR_VALUE(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))#else#define CSP_HDR_VALUE(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))#endif#define RIFF_HEADER CSP_HDR_VALUE('R', 'I', 'F', 'F')#define CSP__HEADER CSP_HDR_VALUE('C', 'S', 'P', ' ')#define LIST_HEADER CSP_HDR_VALUE('L', 'I', 'S', 'T')#define FUNC_HEADER CSP_HDR_VALUE('f', 'u', 'n', 'c')#define CODE_HEADER CSP_HDR_VALUE('c', 'o', 'd', 'e')#define INIT_HEADER CSP_HDR_VALUE('i', 'n', 'i', 't')#define MAIN_HEADER CSP_HDR_VALUE('m', 'a', 'i', 'n')/* * RIFF data format */struct riff_header { __u32 name; __u32 len;};struct desc_header { struct riff_header info; __u16 func_nr; __u16 VOC_type; __u16 flags_play_rec; __u16 flags_16bit_8bit; __u16 flags_stereo_mono; __u16 flags_rates;};/* * prototypes */static void snd_sb_csp_free(snd_hwdep_t *hw);static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file);static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg);static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file);static int csp_detect(sb_t *chip, int *version);static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val);static int set_register(sb_t *chip, unsigned char reg, unsigned char val);static int read_register(sb_t *chip, unsigned char reg);static int set_mode_register(sb_t *chip, unsigned char mode);static int get_version(sb_t *chip);static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t __user * code);static int snd_sb_csp_unload(snd_sb_csp_t * p);static int snd_sb_csp_load_user(snd_sb_csp_t * p, const unsigned char __user *buf, int size, int load_flags);static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode);static int snd_sb_csp_check_version(snd_sb_csp_t * p);static int snd_sb_csp_use(snd_sb_csp_t * p);static int snd_sb_csp_unuse(snd_sb_csp_t * p);static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels);static int snd_sb_csp_stop(snd_sb_csp_t * p);static int snd_sb_csp_pause(snd_sb_csp_t * p);static int snd_sb_csp_restart(snd_sb_csp_t * p);static int snd_sb_qsound_build(snd_sb_csp_t * p);static void snd_sb_qsound_destroy(snd_sb_csp_t * p);static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p);static int init_proc_entry(snd_sb_csp_t * p, int device);static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);/* * Detect CSP chip and create a new instance */int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep){ snd_sb_csp_t *p; int version, err; snd_hwdep_t *hw; if (rhwdep) *rhwdep = NULL; if (csp_detect(chip, &version)) return -ENODEV; if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0) return err; if ((p = kzalloc(sizeof(*p), GFP_KERNEL)) == NULL) { snd_device_free(chip->card, hw); return -ENOMEM; } p->chip = chip; p->version = version; /* CSP operators */ p->ops.csp_use = snd_sb_csp_use; p->ops.csp_unuse = snd_sb_csp_unuse; p->ops.csp_autoload = snd_sb_csp_autoload; p->ops.csp_start = snd_sb_csp_start; p->ops.csp_stop = snd_sb_csp_stop; p->ops.csp_qsound_transfer = snd_sb_csp_qsound_transfer; init_MUTEX(&p->access_mutex); sprintf(hw->name, "CSP v%d.%d", (version >> 4), (version & 0x0f)); hw->iface = SNDRV_HWDEP_IFACE_SB16CSP; hw->private_data = p; hw->private_free = snd_sb_csp_free; /* operators - only write/ioctl */ hw->ops.open = snd_sb_csp_open; hw->ops.ioctl = snd_sb_csp_ioctl; hw->ops.release = snd_sb_csp_release; /* create a proc entry */ init_proc_entry(p, device); if (rhwdep) *rhwdep = hw; return 0;}/* * free_private for hwdep instance */static void snd_sb_csp_free(snd_hwdep_t *hwdep){ snd_sb_csp_t *p = hwdep->private_data; if (p) { if (p->running & SNDRV_SB_CSP_ST_RUNNING) snd_sb_csp_stop(p); kfree(p); }}/* ------------------------------ *//* * open the device exclusively */static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file){ snd_sb_csp_t *p = hw->private_data; return (snd_sb_csp_use(p));}/* * ioctl for hwdep device: */static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg){ snd_sb_csp_t *p = hw->private_data; snd_sb_csp_info_t info; snd_sb_csp_start_t start_info; int err; snd_assert(p != NULL, return -EINVAL); if (snd_sb_csp_check_version(p)) return -ENODEV; switch (cmd) { /* get information */ case SNDRV_SB_CSP_IOCTL_INFO: *info.codec_name = *p->codec_name; info.func_nr = p->func_nr; info.acc_format = p->acc_format; info.acc_channels = p->acc_channels; info.acc_width = p->acc_width; info.acc_rates = p->acc_rates; info.csp_mode = p->mode; info.run_channels = p->run_channels; info.run_width = p->run_width; info.version = p->version; info.state = p->running; if (copy_to_user((void __user *)arg, &info, sizeof(info))) err = -EFAULT; else err = 0; break; /* load CSP microcode */ case SNDRV_SB_CSP_IOCTL_LOAD_CODE: err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? -EBUSY : snd_sb_csp_riff_load(p, (snd_sb_csp_microcode_t __user *) arg)); break; case SNDRV_SB_CSP_IOCTL_UNLOAD_CODE: err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? -EBUSY : snd_sb_csp_unload(p)); break; /* change CSP running state */ case SNDRV_SB_CSP_IOCTL_START: if (copy_from_user(&start_info, (void __user *) arg, sizeof(start_info))) err = -EFAULT; else err = snd_sb_csp_start(p, start_info.sample_width, start_info.channels); break; case SNDRV_SB_CSP_IOCTL_STOP: err = snd_sb_csp_stop(p); break; case SNDRV_SB_CSP_IOCTL_PAUSE: err = snd_sb_csp_pause(p); break; case SNDRV_SB_CSP_IOCTL_RESTART: err = snd_sb_csp_restart(p); break; default: err = -ENOTTY; break; } return err;}/* * close the device */static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file){ snd_sb_csp_t *p = hw->private_data; return (snd_sb_csp_unuse(p));}/* ------------------------------ *//* * acquire device */static int snd_sb_csp_use(snd_sb_csp_t * p){ down(&p->access_mutex); if (p->used) { up(&p->access_mutex); return -EAGAIN; } p->used++; up(&p->access_mutex); return 0;}/* * release device */static int snd_sb_csp_unuse(snd_sb_csp_t * p){ down(&p->access_mutex); p->used--; up(&p->access_mutex); return 0;}/* * load microcode via ioctl: * code is user-space pointer */static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t __user * mcode){ snd_sb_csp_mc_header_t info; unsigned char __user *data_ptr; unsigned char __user *data_end; unsigned short func_nr = 0; struct riff_header file_h, item_h, code_h; __u32 item_type; struct desc_header funcdesc_h; unsigned long flags; int err; if (copy_from_user(&info, mcode, sizeof(info))) return -EFAULT; data_ptr = mcode->data; if (copy_from_user(&file_h, data_ptr, sizeof(file_h))) return -EFAULT; if ((file_h.name != RIFF_HEADER) || (le32_to_cpu(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) { snd_printd("%s: Invalid RIFF header\n", __FUNCTION__); return -EINVAL; } data_ptr += sizeof(file_h); data_end = data_ptr + le32_to_cpu(file_h.len); if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) return -EFAULT; if (item_type != CSP__HEADER) { snd_printd("%s: Invalid RIFF file type\n", __FUNCTION__); return -EINVAL; } data_ptr += sizeof (item_type); for (; data_ptr < data_end; data_ptr += le32_to_cpu(item_h.len)) { if (copy_from_user(&item_h, data_ptr, sizeof(item_h))) return -EFAULT; data_ptr += sizeof(item_h); if (item_h.name != LIST_HEADER) continue; if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) return -EFAULT; switch (item_type) { case FUNC_HEADER: if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h))) return -EFAULT; func_nr = le16_to_cpu(funcdesc_h.func_nr); break; case CODE_HEADER: if (func_nr != info.func_req) break; /* not required function, try next */ data_ptr += sizeof(item_type); /* destroy QSound mixer element */ if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { snd_sb_qsound_destroy(p); } /* Clear all flags */ p->running = 0; p->mode = 0; /* load microcode blocks */ for (;;) { if (data_ptr >= data_end) return -EINVAL; if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) return -EFAULT; /* init microcode blocks */ if (code_h.name != INIT_HEADER) break; data_ptr += sizeof(code_h); err = snd_sb_csp_load_user(p, data_ptr, le32_to_cpu(code_h.len), SNDRV_SB_CSP_LOAD_INITBLOCK); if (err) return err; data_ptr += le32_to_cpu(code_h.len); } /* main microcode block */ if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) return -EFAULT; if (code_h.name != MAIN_HEADER) { snd_printd("%s: Missing 'main' microcode\n", __FUNCTION__); return -EINVAL; } data_ptr += sizeof(code_h); err = snd_sb_csp_load_user(p, data_ptr, le32_to_cpu(code_h.len), 0); if (err) return err; /* fill in codec header */ strlcpy(p->codec_name, info.codec_name, sizeof(p->codec_name));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -