📄 ad1816.c
字号:
/* * * AD1816 lowlevel sound driver for Linux 2.2.0 and above * * Copyright (C) 1998 by Thorsten Knabe <tek@rbg.informatik.tu-darmstadt.de> * * Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996 * * * version: 1.3.1 * status: experimental * date: 1999/4/18 * * Changes: * Oleg Drokin: Some cleanup of load/unload functions. 1998/11/24 * * Thorsten Knabe: attach and unload rewritten, * some argument checks added 1998/11/30 * * Thorsten Knabe: Buggy isa bridge workaround added 1999/01/16 * * David Moews/Thorsten Knabe: Introduced options * parameter. Added slightly modified patch from * David Moews to disable dsp audio sources by setting * bit 0 of options parameter. This seems to be * required by some Aztech/Newcom SC-16 cards. 1999/04/18 * * Christoph Hellwig: Adapted to module_init/module_exit. 2000/03/03 * * Christoph Hellwig: Added isapnp support 2000/03/15 */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/isapnp.h>#include <linux/stddef.h>#include "sound_config.h"#define DEBUGNOISE(x)#define DEBUGINFO(x)#define DEBUGLOG(x)#define DEBUGWARN(x)#define CHECK_FOR_POWER { int timeout=100; \ while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\ timeout--; \ } \ if (timeout==0) {\ printk("ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \ } \}/* structure to hold device specific information */typedef struct{ int base; /* set in attach */ int irq; int dma_playback; int dma_capture; int speed; /* open */ int channels; int audio_format; unsigned char format_bits; int audio_mode; int opened; int recmask; /* setup */ int supported_devices; int supported_rec_devices; unsigned short levels[SOUND_MIXER_NRDEVICES]; int dev_no; /* this is the # in audio_devs and NOT in ad1816_info */ int irq_ok; int *osp; } ad1816_info;static int nr_ad1816_devs = 0;static int ad1816_clockfreq=33000;static int options=0;/* for backward mapping of irq to sound device */static volatile char irq2dev[17] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};/* supported audio formats */static int ad_format_mask =AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW;/* array of device info structures */static ad1816_info dev_info[MAX_AUDIO_DEV];/* ------------------------------------------------------------------- *//* functions for easier access to inderect registers */static int ad_read (ad1816_info * devc, int reg){ unsigned long flags; int result; CHECK_FOR_POWER; save_flags (flags); /* make register access atomic */ cli (); outb ((unsigned char) (reg & 0x3f), devc->base+0); result = inb(devc->base+2); result+= inb(devc->base+3)<<8; restore_flags (flags); return (result);}static void ad_write (ad1816_info * devc, int reg, int data){ unsigned long flags; CHECK_FOR_POWER; save_flags (flags); /* make register access atomic */ cli (); outb ((unsigned char) (reg & 0xff), devc->base+0); outb ((unsigned char) (data & 0xff),devc->base+2); outb ((unsigned char) ((data>>8)&0xff),devc->base+3); restore_flags (flags);}/* ------------------------------------------------------------------- *//* function interface required by struct audio_driver */static void ad1816_halt_input (int dev){ unsigned long flags; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; unsigned char buffer; DEBUGINFO (printk("ad1816: halt_input called\n")); save_flags (flags); cli (); if(!isa_dma_bridge_buggy) { disable_dma(audio_devs[dev]->dmap_in->dma); } buffer=inb(devc->base+9); if (buffer & 0x01) { /* disable capture */ outb(buffer & ~0x01,devc->base+9); } if(!isa_dma_bridge_buggy) { enable_dma(audio_devs[dev]->dmap_in->dma); } /* Clear interrupt status */ outb (~0x40, devc->base+1); devc->audio_mode &= ~PCM_ENABLE_INPUT; restore_flags (flags);}static void ad1816_halt_output (int dev){ unsigned long flags; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; unsigned char buffer; DEBUGINFO (printk("ad1816: halt_output called!\n")); save_flags (flags); cli (); /* Mute pcm output */ ad_write(devc, 4, ad_read(devc,4)|0x8080); if(!isa_dma_bridge_buggy) { disable_dma(audio_devs[dev]->dmap_out->dma); } buffer=inb(devc->base+8); if (buffer & 0x01) { /* disable capture */ outb(buffer & ~0x01,devc->base+8); } if(!isa_dma_bridge_buggy) { enable_dma(audio_devs[dev]->dmap_out->dma); } /* Clear interrupt status */ outb ((unsigned char)~0x80, devc->base+1); devc->audio_mode &= ~PCM_ENABLE_OUTPUT; restore_flags (flags);}static void ad1816_output_block (int dev, unsigned long buf, int count, int intrflag){ unsigned long flags; unsigned long cnt; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; DEBUGINFO(printk("ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); cnt = count/4 - 1; save_flags (flags); cli (); /* set transfer count */ ad_write (devc, 8, cnt & 0xffff); devc->audio_mode |= PCM_ENABLE_OUTPUT; restore_flags (flags);}static void ad1816_start_input (int dev, unsigned long buf, int count, int intrflag){ unsigned long flags; unsigned long cnt; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; DEBUGINFO(printk("ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); cnt = count/4 - 1; save_flags (flags); /* make register access atomic */ cli (); /* set transfer count */ ad_write (devc, 10, cnt & 0xffff); devc->audio_mode |= PCM_ENABLE_INPUT; restore_flags (flags);}static int ad1816_prepare_for_input (int dev, int bsize, int bcount){ unsigned long flags; unsigned int freq; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; unsigned char fmt_bits; DEBUGINFO (printk ("ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount)); save_flags (flags); cli (); fmt_bits= (devc->format_bits&0x7)<<3; /* set mono/stereo mode */ if (devc->channels > 1) { fmt_bits |=0x4; } /* set Mono/Stereo in playback/capture register */ outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); /* If compiled into kernel, AD1816_CLOCK is defined, so use it */#ifdef AD1816_CLOCK ad1816_clockfreq=AD1816_CLOCK;#endif /* capture/playback frequency correction for soundcards with clock chips != 33MHz (allowed range 5 - 100 kHz) */ if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { ad1816_clockfreq=33000; } freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; /* write playback/capture speeds */ ad_write (devc, 2, freq & 0xffff); ad_write (devc, 3, freq & 0xffff); restore_flags (flags); ad1816_halt_input(dev); return 0;}static int ad1816_prepare_for_output (int dev, int bsize, int bcount){ unsigned long flags; unsigned int freq; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; unsigned char fmt_bits; DEBUGINFO (printk ("ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount)); save_flags (flags); /* make register access atomic */ cli (); fmt_bits= (devc->format_bits&0x7)<<3; /* set mono/stereo mode */ if (devc->channels > 1) { fmt_bits |=0x4; } /* write format bits to playback/capture registers */ outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); #ifdef AD1816_CLOCK ad1816_clockfreq=AD1816_CLOCK;#endif /* capture/playback frequency correction for soundcards with clock chips != 33MHz (allowed range 5 - 100 kHz)*/ if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { ad1816_clockfreq=33000; } freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; /* write playback/capture speeds */ ad_write (devc, 2, freq & 0xffff); ad_write (devc, 3, freq & 0xffff); restore_flags (flags); ad1816_halt_output(dev); return 0;}static void ad1816_trigger (int dev, int state) { unsigned long flags; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; DEBUGINFO (printk("ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base)); /* mode may have changed */ save_flags (flags); /* make register access atomic */ cli (); /* mask out modes not specified on open call */ state &= devc->audio_mode; /* setup soundchip to new io-mode */ if (state & PCM_ENABLE_INPUT) { /* enable capture */ outb(inb(devc->base+9)|0x01, devc->base+9); } else { /* disable capture */ outb(inb(devc->base+9)&~0x01, devc->base+9); } if (state & PCM_ENABLE_OUTPUT) { /* enable playback */ outb(inb(devc->base+8)|0x01, devc->base+8); /* unmute pcm output */ ad_write(devc, 4, ad_read(devc,4)&~0x8080); } else { /* mute pcm output */ ad_write(devc, 4, ad_read(devc,4)|0x8080); /* disable capture */ outb(inb(devc->base+8)&~0x01, devc->base+8); } restore_flags (flags);}/* halt input & output */static void ad1816_halt (int dev){ ad1816_halt_input(dev); ad1816_halt_output(dev);}static void ad1816_reset (int dev){ ad1816_halt (dev);}/* set playback speed */static int ad1816_set_speed (int dev, int arg){ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; if (arg == 0) { return devc->speed; } /* range checking */ if (arg < 4000) { arg = 4000; } if (arg > 55000) { arg = 55000; } devc->speed = arg; return devc->speed;}static unsigned int ad1816_set_bits (int dev, unsigned int arg){ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; static struct format_tbl { int format; unsigned char bits; } format2bits[] = { { 0, 0 }, { AFMT_MU_LAW, 1 }, { AFMT_A_LAW, 3 }, { AFMT_IMA_ADPCM, 0 }, { AFMT_U8, 0 }, { AFMT_S16_LE, 2 }, { AFMT_S16_BE, 6 }, { AFMT_S8, 0 }, { AFMT_U16_LE, 0 }, { AFMT_U16_BE, 0 } }; int i, n = sizeof (format2bits) / sizeof (struct format_tbl); /* return current format */ if (arg == 0) return devc->audio_format; devc->audio_format = arg; /* search matching format bits */ for (i = 0; i < n; i++) if (format2bits[i].format == arg) { devc->format_bits = format2bits[i].bits; devc->audio_format = arg; return arg; } /* Still hanging here. Something must be terribly wrong */ devc->format_bits = 0; return devc->audio_format = AFMT_U8;}static short ad1816_set_channels (int dev, short arg){ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; if (arg != 1 && arg != 2) return devc->channels; devc->channels = arg; return arg;}/* open device */static int ad1816_open (int dev, int mode) { ad1816_info *devc = NULL; unsigned long flags; /* is device number valid ? */ if (dev < 0 || dev >= num_audiodevs) return -(ENXIO); /* get device info of this dev */ devc = (ad1816_info *) audio_devs[dev]->devc; /* make check if device already open atomic */ save_flags (flags); cli (); if (devc->opened) { restore_flags (flags); return -(EBUSY); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -