📄 msnd_pinnacle.c
字号:
/********************************************************************* * * Turtle Beach MultiSound Sound Card Driver for Linux * Linux 2.0/2.2 Version * * msnd_pinnacle.c / msnd_classic.c * * -- If MSND_CLASSIC is defined: * * -> driver for Turtle Beach Classic/Monterey/Tahiti * * -- Else * * -> driver for Turtle Beach Pinnacle/Fiji * * Copyright (C) 1998 Andrew Veliath * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: msnd_pinnacle.c,v 1.8 2000/12/30 00:33:21 sycamore Exp $ * * 12-3-2000 Modified IO port validation Steve Sycamore * * * $$$: msnd_pinnacle.c,v 1.75 1999/03/21 16:50:09 andrewtv $$$ $ * ********************************************************************/#include <linux/kernel.h>#include <linux/config.h>#include <linux/version.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <asm/irq.h>#include <asm/io.h>#include "sound_config.h"#include "sound_firmware.h"#ifdef MSND_CLASSIC# ifndef __alpha__# define SLOWIO# endif#endif#include "msnd.h"#ifdef MSND_CLASSIC# ifdef CONFIG_MSNDCLAS_HAVE_BOOT# define HAVE_DSPCODEH# endif# include "msnd_classic.h"# define LOGNAME "msnd_classic"#else# ifdef CONFIG_MSNDPIN_HAVE_BOOT# define HAVE_DSPCODEH# endif# include "msnd_pinnacle.h"# define LOGNAME "msnd_pinnacle"#endif#ifndef CONFIG_MSND_WRITE_NDELAY# define CONFIG_MSND_WRITE_NDELAY 1#endif#define get_play_delay_jiffies(size) ((size) * HZ * \ dev.play_sample_size / 8 / \ dev.play_sample_rate / \ dev.play_channels)#define get_rec_delay_jiffies(size) ((size) * HZ * \ dev.rec_sample_size / 8 / \ dev.rec_sample_rate / \ dev.rec_channels)static multisound_dev_t dev;#ifndef HAVE_DSPCODEHstatic char *dspini, *permini;static int sizeof_dspini, sizeof_permini;#endifstatic int dsp_full_reset(void);static void dsp_write_flush(void);static __inline__ int chk_send_dsp_cmd(multisound_dev_t *dev, register BYTE cmd){ if (msnd_send_dsp_cmd(dev, cmd) == 0) return 0; dsp_full_reset(); return msnd_send_dsp_cmd(dev, cmd);}static void reset_play_queue(void){ int n; LPDAQD lpDAQ; dev.last_playbank = -1; isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wHead); isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wTail); for (n = 0, lpDAQ = dev.base + DAPQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { isa_writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart); isa_writew(0, lpDAQ + DAQDS_wSize); isa_writew(1, lpDAQ + DAQDS_wFormat); isa_writew(dev.play_sample_size, lpDAQ + DAQDS_wSampleSize); isa_writew(dev.play_channels, lpDAQ + DAQDS_wChannels); isa_writew(dev.play_sample_rate, lpDAQ + DAQDS_wSampleRate); isa_writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg); isa_writew(n, lpDAQ + DAQDS_wFlags); }}static void reset_record_queue(void){ int n; LPDAQD lpDAQ; unsigned long flags; dev.last_recbank = 2; isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DARQ + JQS_wHead); isa_writew(PCTODSP_OFFSET(dev.last_recbank * DAQDS__size), dev.DARQ + JQS_wTail); /* Critical section: bank 1 access */ spin_lock_irqsave(&dev.lock, flags); outb(HPBLKSEL_1, dev.io + HP_BLKS); isa_memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); outb(HPBLKSEL_0, dev.io + HP_BLKS); spin_unlock_irqrestore(&dev.lock, flags); for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { isa_writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart); isa_writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize); isa_writew(1, lpDAQ + DAQDS_wFormat); isa_writew(dev.rec_sample_size, lpDAQ + DAQDS_wSampleSize); isa_writew(dev.rec_channels, lpDAQ + DAQDS_wChannels); isa_writew(dev.rec_sample_rate, lpDAQ + DAQDS_wSampleRate); isa_writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg); isa_writew(n, lpDAQ + DAQDS_wFlags); }}static void reset_queues(void){ if (dev.mode & FMODE_WRITE) { msnd_fifo_make_empty(&dev.DAPF); reset_play_queue(); } if (dev.mode & FMODE_READ) { msnd_fifo_make_empty(&dev.DARF); reset_record_queue(); }}static int dsp_set_format(struct file *file, int val){ int data, i; LPDAQD lpDAQ, lpDARQ; lpDAQ = dev.base + DAPQ_DATA_BUFF; lpDARQ = dev.base + DARQ_DATA_BUFF; switch (val) { case AFMT_U8: case AFMT_S16_LE: data = val; break; default: data = DEFSAMPLESIZE; break; } for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { if (file->f_mode & FMODE_WRITE) isa_writew(data, lpDAQ + DAQDS_wSampleSize); if (file->f_mode & FMODE_READ) isa_writew(data, lpDARQ + DAQDS_wSampleSize); } if (file->f_mode & FMODE_WRITE) dev.play_sample_size = data; if (file->f_mode & FMODE_READ) dev.rec_sample_size = data; return data;}static int dsp_get_frag_size(void){ int size; size = dev.fifosize / 4; if (size > 32 * 1024) size = 32 * 1024; return size;}static int dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ int val, i, data, tmp; LPDAQD lpDAQ, lpDARQ; audio_buf_info abinfo; unsigned long flags; lpDAQ = dev.base + DAPQ_DATA_BUFF; lpDARQ = dev.base + DARQ_DATA_BUFF; switch (cmd) { case SNDCTL_DSP_SUBDIVIDE: case SNDCTL_DSP_SETFRAGMENT: case SNDCTL_DSP_SETDUPLEX: case SNDCTL_DSP_POST: return 0; case SNDCTL_DSP_GETIPTR: case SNDCTL_DSP_GETOPTR: case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: return -EINVAL; case SNDCTL_DSP_GETOSPACE: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; spin_lock_irqsave(&dev.lock, flags); abinfo.fragsize = dsp_get_frag_size(); abinfo.bytes = dev.DAPF.n - dev.DAPF.len; abinfo.fragstotal = dev.DAPF.n / abinfo.fragsize; abinfo.fragments = abinfo.bytes / abinfo.fragsize; spin_unlock_irqrestore(&dev.lock, flags); return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; case SNDCTL_DSP_GETISPACE: if (!(file->f_mode & FMODE_READ)) return -EINVAL; spin_lock_irqsave(&dev.lock, flags); abinfo.fragsize = dsp_get_frag_size(); abinfo.bytes = dev.DARF.n - dev.DARF.len; abinfo.fragstotal = dev.DARF.n / abinfo.fragsize; abinfo.fragments = abinfo.bytes / abinfo.fragsize; spin_unlock_irqrestore(&dev.lock, flags); return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; case SNDCTL_DSP_RESET: dev.nresets = 0; reset_queues(); return 0; case SNDCTL_DSP_SYNC: dsp_write_flush(); return 0; case SNDCTL_DSP_GETBLKSIZE: tmp = dsp_get_frag_size(); if (put_user(tmp, (int *)arg)) return -EFAULT; return 0; case SNDCTL_DSP_GETFMTS: val = AFMT_S16_LE | AFMT_U8; if (put_user(val, (int *)arg)) return -EFAULT; return 0; case SNDCTL_DSP_SETFMT: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_WRITE) data = val == AFMT_QUERY ? dev.play_sample_size : dsp_set_format(file, val); else data = val == AFMT_QUERY ? dev.rec_sample_size : dsp_set_format(file, val); if (put_user(data, (int *)arg)) return -EFAULT; return 0; case SNDCTL_DSP_NONBLOCK: if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags) && file->f_mode & FMODE_WRITE) dev.play_ndelay = 1; if (file->f_mode & FMODE_READ) dev.rec_ndelay = 1; return 0; case SNDCTL_DSP_GETCAPS: val = DSP_CAP_DUPLEX | DSP_CAP_BATCH; if (put_user(val, (int *)arg)) return -EFAULT; return 0; case SNDCTL_DSP_SPEED: if (get_user(val, (int *)arg)) return -EFAULT; if (val < 8000) val = 8000; if (val > 48000) val = 48000; data = val; for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { if (file->f_mode & FMODE_WRITE) isa_writew(data, lpDAQ + DAQDS_wSampleRate); if (file->f_mode & FMODE_READ) isa_writew(data, lpDARQ + DAQDS_wSampleRate); } if (file->f_mode & FMODE_WRITE) dev.play_sample_rate = data; if (file->f_mode & FMODE_READ) dev.rec_sample_rate = data; if (put_user(data, (int *)arg)) return -EFAULT; return 0; case SNDCTL_DSP_CHANNELS: case SNDCTL_DSP_STEREO: if (get_user(val, (int *)arg)) return -EFAULT; if (cmd == SNDCTL_DSP_CHANNELS) { switch (val) { case 1: case 2: data = val; break; default: val = data = 2; break; } } else { switch (val) { case 0: data = 1; break; default: val = 1; case 1: data = 2; break; } } for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { if (file->f_mode & FMODE_WRITE) isa_writew(data, lpDAQ + DAQDS_wChannels); if (file->f_mode & FMODE_READ) isa_writew(data, lpDARQ + DAQDS_wChannels); } if (file->f_mode & FMODE_WRITE) dev.play_channels = data; if (file->f_mode & FMODE_READ) dev.rec_channels = data; if (put_user(val, (int *)arg)) return -EFAULT; return 0; } return -EINVAL;}static int mixer_get(int d){ if (d > 31) return -EINVAL; switch (d) { case SOUND_MIXER_VOLUME: case SOUND_MIXER_PCM: case SOUND_MIXER_LINE: case SOUND_MIXER_IMIX: case SOUND_MIXER_LINE1:#ifndef MSND_CLASSIC case SOUND_MIXER_MIC: case SOUND_MIXER_SYNTH:#endif return (dev.left_levels[d] >> 8) * 100 / 0xff | (((dev.right_levels[d] >> 8) * 100 / 0xff) << 8); default: return 0; }}#define update_volm(a,b) \ isa_writew((dev.left_levels[a] >> 1) * \ isa_readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \ dev.SMA + SMA_##b##Left); \ isa_writew((dev.right_levels[a] >> 1) * \ isa_readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \ dev.SMA + SMA_##b##Right);#define update_potm(d,s,ar) \ isa_writeb((dev.left_levels[d] >> 8) * \ isa_readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \ dev.SMA + SMA_##s##Left); \ isa_writeb((dev.right_levels[d] >> 8) * \ isa_readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \ dev.SMA + SMA_##s##Right); \ if (msnd_send_word(&dev, 0, 0, ar) == 0) \ chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);#define update_pot(d,s,ar) \ isa_writeb(dev.left_levels[d] >> 8, \ dev.SMA + SMA_##s##Left); \ isa_writeb(dev.right_levels[d] >> 8, \ dev.SMA + SMA_##s##Right); \ if (msnd_send_word(&dev, 0, 0, ar) == 0) \ chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);static int mixer_set(int d, int value){ int left = value & 0x000000ff; int right = (value & 0x0000ff00) >> 8; int bLeft, bRight; int wLeft, wRight; int updatemaster = 0; if (d > 31) return -EINVAL; bLeft = left * 0xff / 100; wLeft = left * 0xffff / 100; bRight = right * 0xff / 100; wRight = right * 0xffff / 100; dev.left_levels[d] = wLeft; dev.right_levels[d] = wRight; switch (d) { /* master volume unscaled controls */ case SOUND_MIXER_LINE: /* line pot control */ /* scaled by IMIX in digital mix */ isa_writeb(bLeft, dev.SMA + SMA_bInPotPosLeft); isa_writeb(bRight, dev.SMA + SMA_bInPotPosRight); if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); break;#ifndef MSND_CLASSIC case SOUND_MIXER_MIC: /* mic pot control */ /* scaled by IMIX in digital mix */ isa_writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft); isa_writeb(bRight, dev.SMA + SMA_bMicPotPosRight); if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0) chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); break;#endif case SOUND_MIXER_VOLUME: /* master volume */ isa_writew(wLeft, dev.SMA + SMA_wCurrMastVolLeft); isa_writew(wRight, dev.SMA + SMA_wCurrMastVolRight); /* fall through */ case SOUND_MIXER_LINE1: /* aux pot control */ /* scaled by master volume */ /* fall through */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -