📄 cs4281.c
字号:
//*****************************************************************************//// "cs4281.c" -- Cirrus Logic-Crystal CS4281 linux audio driver.//// Copyright (C) 2000 Cirrus Logic Corp. // -- adapted from drivers by Thomas Sailer, // -- but don't bug him; Problems should go to:// -- gw boynton (wesb@crystal.cirrus.com) or// -- tom woller (twoller@crystal.cirrus.com).//// 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.//// Module command line parameters:// none//// Supported devices:// /dev/dsp standard /dev/dsp device, (mostly) OSS compatible// /dev/mixer standard /dev/mixer device, (mostly) OSS compatible// /dev/midi simple MIDI UART interface, no ioctl//// Modification History// 08/20/00 trw - silence and no stopping DAC until release// 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop.// 09/18/00 trw - added 16bit only record with conversion // 09/24/00 trw - added Enhanced Full duplex (separate simultaneous // capture/playback rates)// 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin // libOSSm.so)// 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal)// 11/03/00 trw - fixed interrupt loss/stutter, added debug.// 11/10/00 bkz - added __devinit to cs4281_hw_init()//// *****************************************************************************#include <linux/config.h>#include <linux/version.h>#include <linux/module.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/sound.h>#include <linux/malloc.h>#include <linux/soundcard.h>#include <linux/pci.h>#include <linux/bitops.h>#include <linux/spinlock.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/smp_lock.h>#include <linux/wrapper.h>#include <asm/uaccess.h>#include <asm/hardirq.h>//#include <linux/vmalloc.h>#include "dm.h"#include "cs4281_hwdefs.h"EXPORT_NO_SYMBOLS;#undef OSS_DOCUMENTED_MIXER_SEMANTICS// --------------------------------------------------------------------- #ifndef PCI_VENDOR_ID_CIRRUS#define PCI_VENDOR_ID_CIRRUS 0x1013#endif#ifndef PCI_DEVICE_ID_CRYSTAL_CS4281#define PCI_DEVICE_ID_CRYSTAL_CS4281 0x6005#endif#define CS4281_MAGIC ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS)// Turn on/off debugging compilation by using 1/0 respectively for CSDEBUG//#define CSDEBUG_INTERFACE 1#define CSDEBUG 1//// CSDEBUG is usual mode is set to 1, then use the// cs_debuglevel and cs_debugmask to turn on or off debugging.// Debug level of 1 has been defined to be kernel errors and info// that should be printed on any released driver.//#if CSDEBUGextern unsigned long cs_debugmask;extern unsigned long cs_debuglevel;#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;}#else#define CS_DBGOUT(mask,level,x)#endif//// cs_debugmask areas//#define CS_INIT 0x00000001 // initialization and probe functions#define CS_ERROR 0x00000002 // tmp debugging bit placeholder#define CS_INTERRUPT 0x00000004 // interrupt handler (separate from all other)#define CS_FUNCTION 0x00000008 // enter/leave functions#define CS_WAVE_WRITE 0x00000010 // write information for wave#define CS_WAVE_READ 0x00000020 // read information for wave#define CS_MIDI_WRITE 0x00000040 // write information for midi#define CS_MIDI_READ 0x00000080 // read information for midi#define CS_MPU401_WRITE 0x00000100 // write information for mpu401#define CS_MPU401_READ 0x00000200 // read information for mpu401#define CS_OPEN 0x00000400 // all open functions in the driver#define CS_RELEASE 0x00000800 // all release functions in the driver#define CS_PARMS 0x00001000 // functional and operational parameters#define CS_IOCTL 0x00002000 // ioctl (non-mixer)#define CS_TMP 0x10000000 // tmp debug mask bit#if CSDEBUGstatic unsigned long cs_debuglevel = 1; // levels range from 1-9static unsigned long cs_debugmask = CS_INIT | CS_ERROR; // use CS_DBGOUT with various mask values#if MODULEMODULE_PARM(cs_debuglevel, "i");MODULE_PARM(cs_debugmask, "i");#endif#endif// MIDI buffer sizes #define MIDIINBUF 500#define MIDIOUTBUF 500#define FMODE_MIDI_SHIFT 3#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT)#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)#define RSRCISIOREGION(dev,num) ((dev)->resource[(num)].start != 0 && \ ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)#define RSRCISMEMORYREGION(dev,num) ((dev)->resource[(num)].start != 0 && \ ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY)#define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start)#define CS4281_MAJOR_VERSION 1#define CS4281_MINOR_VERSION 1#ifdef __ia64__#define CS4281_ARCH 64 //architecture key#else#define CS4281_ARCH 32 //architecture key#endif#define CS_TYPE_ADC 0#define CS_TYPE_DAC 1struct cs4281_state { // magic unsigned int magic; // we keep the cards in a linked list struct cs4281_state *next; // pcidev is needed to turn off the DDMA controller at driver shutdown struct pci_dev *pcidev; // soundcore stuff int dev_audio; int dev_mixer; int dev_midi; // hardware resources unsigned int pBA0phys, pBA1phys; char *pBA0, *pBA1; unsigned int irq; // mixer registers struct { unsigned short vol[10]; unsigned int recsrc; unsigned int modcnt; unsigned short micpreamp; } mix; // wave stuff struct properties { unsigned fmt; unsigned fmt_original; // original requested format unsigned channels; unsigned rate; unsigned char clkdiv; } prop_dac, prop_adc; unsigned conversion:1; // conversion from 16 to 8 bit in progress void *tmpbuff; // tmp buffer for sample conversions unsigned ena; spinlock_t lock; struct semaphore open_sem; struct semaphore open_sem_adc; struct semaphore open_sem_dac; mode_t open_mode; wait_queue_head_t open_wait; wait_queue_head_t open_wait_adc; wait_queue_head_t open_wait_dac; dma_addr_t dmaaddr_tmpbuff; unsigned buforder_tmpbuff; // Log base 2 of 'rawbuf' size in bytes.. struct dmabuf { void *rawbuf; // Physical address of dma_addr_t dmaaddr; unsigned buforder; // Log base 2 of 'rawbuf' size in bytes.. unsigned numfrag; // # of 'fragments' in the buffer. unsigned fragshift; // Log base 2 of fragment size. unsigned hwptr, swptr; unsigned total_bytes; // # bytes process since open. unsigned blocks; // last returned blocks value GETOPTR unsigned wakeup; // interrupt occurred on block int count; unsigned error; // over/underrun wait_queue_head_t wait; // redundant, but makes calculations easier unsigned fragsize; // 2**fragshift.. unsigned dmasize; // 2**buforder. unsigned fragsamples; // OSS stuff unsigned mapped:1; // Buffer mapped in cs4281_mmap()? unsigned ready:1; // prog_dmabuf_dac()/adc() successful? unsigned endcleared:1; unsigned type:1; // adc or dac buffer (CS_TYPE_XXX) unsigned ossfragshift; int ossmaxfrags; unsigned subdivision; } dma_dac, dma_adc; // midi stuff struct { unsigned ird, iwr, icnt; unsigned ord, owr, ocnt; wait_queue_head_t iwait; wait_queue_head_t owait; struct timer_list timer; unsigned char ibuf[MIDIINBUF]; unsigned char obuf[MIDIOUTBUF]; } midi;};#if CSDEBUG// DEBUG ROUTINES#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int)#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int)#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int)#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int)#define SNDCTL_DSP_CS_GETDBGLEVEL _SIOWR('P', 50, int)#define SNDCTL_DSP_CS_SETDBGLEVEL _SIOWR('P', 51, int)#define SNDCTL_DSP_CS_GETDBGMASK _SIOWR('P', 52, int)#define SNDCTL_DSP_CS_SETDBGMASK _SIOWR('P', 53, int)static void printioctl(unsigned int x){ unsigned int i; unsigned char vidx; // Index of mixtable1[] member is Device ID // and must be <= SOUND_MIXER_NRDEVICES. // Value of array member is index into s->mix.vol[] static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_PCM] = 1, // voice [SOUND_MIXER_LINE1] = 2, // AUX [SOUND_MIXER_CD] = 3, // CD [SOUND_MIXER_LINE] = 4, // Line [SOUND_MIXER_SYNTH] = 5, // FM [SOUND_MIXER_MIC] = 6, // Mic [SOUND_MIXER_SPEAKER] = 7, // Speaker [SOUND_MIXER_RECLEV] = 8, // Recording level [SOUND_MIXER_VOLUME] = 9 // Master Volume }; switch (x) { case SOUND_MIXER_CS_GETDBGMASK: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGMASK:\n")); break; case SOUND_MIXER_CS_GETDBGLEVEL: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGLEVEL:\n")); break; case SOUND_MIXER_CS_SETDBGMASK: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGMASK:\n")); break; case SOUND_MIXER_CS_SETDBGLEVEL: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGLEVEL:\n")); break; case OSS_GETVERSION: CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n")); break; case SNDCTL_DSP_SYNC: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n")); break; case SNDCTL_DSP_SETDUPLEX: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n")); break; case SNDCTL_DSP_GETCAPS: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n")); break; case SNDCTL_DSP_RESET: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n")); break; case SNDCTL_DSP_SPEED: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n")); break; case SNDCTL_DSP_STEREO: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n")); break; case SNDCTL_DSP_CHANNELS: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n")); break; case SNDCTL_DSP_GETFMTS: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n")); break; case SNDCTL_DSP_SETFMT: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n")); break; case SNDCTL_DSP_POST: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n")); break; case SNDCTL_DSP_GETTRIGGER: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n")); break; case SNDCTL_DSP_SETTRIGGER: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n")); break; case SNDCTL_DSP_GETOSPACE: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n")); break; case SNDCTL_DSP_GETISPACE: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n")); break; case SNDCTL_DSP_NONBLOCK: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n")); break; case SNDCTL_DSP_GETODELAY: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n")); break; case SNDCTL_DSP_GETIPTR: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n")); break; case SNDCTL_DSP_GETOPTR: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n")); break; case SNDCTL_DSP_GETBLKSIZE: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n")); break; case SNDCTL_DSP_SETFRAGMENT: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFRAGMENT:\n")); break; case SNDCTL_DSP_SUBDIVIDE: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n")); break; case SOUND_PCM_READ_RATE: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n")); break; case SOUND_PCM_READ_CHANNELS: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_CHANNELS:\n")); break; case SOUND_PCM_READ_BITS: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n")); break; case SOUND_PCM_WRITE_FILTER: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_WRITE_FILTER:\n")); break; case SNDCTL_DSP_SETSYNCRO: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n")); break; case SOUND_PCM_READ_FILTER: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n")); break; case SNDCTL_DSP_CS_GETDBGMASK: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CS_GETDBGMASK:\n")); break; case SNDCTL_DSP_CS_GETDBGLEVEL: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CS_GETDBGLEVEL:\n")); break; case SNDCTL_DSP_CS_SETDBGMASK: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CS_SETDBGMASK:\n")); break; case SNDCTL_DSP_CS_SETDBGLEVEL: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CS_SETDBGLEVEL:\n")); break; case SOUND_MIXER_PRIVATE1: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n")); break; case SOUND_MIXER_PRIVATE2: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n")); break; case SOUND_MIXER_PRIVATE3: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n")); break; case SOUND_MIXER_PRIVATE4: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n")); break; case SOUND_MIXER_PRIVATE5: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n")); break; case SOUND_MIXER_INFO: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n")); break; case SOUND_OLD_MIXER_INFO: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n")); break; default: switch (_IOC_NR(x)) { case SOUND_MIXER_VOLUME: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_VOLUME:\n")); break; case SOUND_MIXER_SPEAKER: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SPEAKER:\n")); break; case SOUND_MIXER_RECLEV: CS_DBGOUT(CS_IOCTL, 4,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -