⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sb_dsp.c

📁 freebsd v4.4内核源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * sound/sb_dsp.c *  * driver for the SoundBlaster and clones. *  * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright * conditions. *  * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in *    the documentation and/or other materials provided with the *    distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. *  *//* * use this as a template file for board-specific drivers. * The next two lines (and the final #endif) are in all drivers: */#include <i386/isa/snd/sound.h>#if NPCM > 0/* * Begin with the board-specific include files... */#define __SB_MIXER_C__	/* XXX warning... */#include  <i386/isa/snd/sbcard.h>/* * then prototypes of functions which go in the snddev_info * (usually static, unless they are shared by other modules)... */static	int sb_probe(struct isa_device *dev);static	int sb_attach(struct isa_device *dev);static	d_open_t	sb_dsp_open;static	d_close_t	sb_dsp_close;static	d_ioctl_t	sb_dsp_ioctl;static	irq_proc_t	sbintr;static	snd_callback_t	sb_callback;/* * and prototypes for other private functions defined in this module. */static	void sb_dsp_init(snddev_info *d, struct isa_device *dev);static	void sb_mix_init(snddev_info *d);static int sb_mixer_set(snddev_info *d, int dev, int value);static int dsp_speed(snddev_info *d);static void sb_mixer_reset(snddev_info *d);u_int sb_get_byte(int io_base);int ess_write(int io_base, u_char reg, int val);int ess_read(int io_base, u_char reg);/* * Then put here the descriptors for the various boards supported * by this module, properly initialized. */snddev_info sb_op_desc = {    "basic soundblaster",    SNDCARD_SB,    sb_probe,    sb_attach,    sb_dsp_open,    sb_dsp_close /* sb_close */,    NULL /* use generic sndread */,    NULL /* use generic sndwrite */,    sb_dsp_ioctl,    sndselect,    sbintr,    sb_callback,    DSP_BUFFSIZE,	/* bufsize */    AFMT_STEREO | AFMT_U8,		/* audio format */} ;/* * Then the file continues with the body of all functions * directly referenced in the descriptor. *//* * the probe routine for the SoundBlaster only consists in * resetting the dsp and testing if it is there. * Version detection etc. will be done at attach time. * * Remember, ISA probe routines are supposed to return the * size of io space used. */static intsb_probe(struct isa_device *dev){    bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) );    if (dev->id_iobase == -1) {	dev->id_iobase = 0x220;	BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");)        if (snd_conflict(dev->id_iobase))	    dev->id_iobase = 0x240;    }    if (snd_conflict(dev->id_iobase))	return 0 ;    if (sb_reset_dsp(dev->id_iobase))	return 16 ; /* the SB uses 16 registers... */    else	return 0;}static intsb_attach(struct isa_device *dev){    snddev_info *d = &pcm_info[dev->id_unit] ;    dev->id_alive = 16 ; /* number of io ports */    /* should be already set but just in case... */    sb_dsp_init(d, dev);    return 0 ;}/* * here are the main routines from the switches. *//* * Unlike MSS, the sb only supports a single open (does not mean * that only a single process is using it, since it can fork * afterwards, or pass the descriptor to another process). * */static intsb_dsp_open(dev_t dev, int flags, int mode, struct proc * p){    snddev_info *d;    int unit ;    dev = minor(dev);    unit = dev >> 4 ;    d = &pcm_info[unit] ;    DEB(printf("<%s>%d : open\n", d->name, unit));    if (d->flags & SND_F_BUSY) {	printf("<%s>%d open: device busy\n", d->name, unit);	return EBUSY ;    }    d->wsel.si_pid = 0;    d->wsel.si_flags = 0;    d->rsel.si_pid = 0;    d->rsel.si_flags = 0;    d->dbuf_out.total = d->dbuf_out.prev_total = 0 ;    d->dbuf_in.total = d->dbuf_in.prev_total = 0 ;    d->flags = 0 ;    d->bd_flags &= ~BD_F_HISPEED ;    switch ( dev & 0xf ) {    case SND_DEV_DSP16 :	if ((d->audio_fmt & AFMT_S16_LE) == 0) {	    printf("sorry, 16-bit not supported on SB %d.%02d\n",		(d->bd_id >>8) & 0xff, d->bd_id & 0xff);	    return ENXIO;	}	d->play_fmt = d->rec_fmt = AFMT_S16_LE ;	break;    case SND_DEV_AUDIO :	d->play_fmt = d->rec_fmt = AFMT_MU_LAW ;	break ;    case SND_DEV_DSP :	d->play_fmt = d->rec_fmt = AFMT_U8 ;	break ;    }    /*     * since the SB is not simmetric, I use the open mode to select     * which channel should be privileged, and disable I/O in the     * other direction.     * In case the board is opened RW, we don't have enough     * information on what to do. Temporarily, privilege the     * playback channel, which is used more often, and set the other     * one to U8.     */    if ( (flags & FREAD) == 0)		/* opened write only	*/	d->rec_fmt = 0 ;    else if ( (flags & FWRITE) == 0)	/* opened read only	*/	d->play_fmt = 0 ;    else				/* opened read/write	*/	d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ;    d->flags |= SND_F_BUSY ;    d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;    if (flags & O_NONBLOCK)	d->flags |= SND_F_NBIO ;    sb_reset_dsp(d->io_base);    if (d->bd_flags & BD_F_ESS)	sb_cmd(d->io_base, 0xc6 ); /* enable extended ESS mode */    ask_init(d);    return 0;}static intsb_dsp_close(dev_t dev, int flags, int mode, struct proc * p){    int unit;    snddev_info *d;    u_long s;    dev = minor(dev);    unit = dev >> 4 ;    d = &pcm_info[unit] ;    s = spltty();    d->flags |= SND_F_CLOSING ;    splx(s);    snd_flush(d);    sb_cmd(d->io_base, DSP_CMD_SPKOFF ); /* XXX useless ? */    d->flags = 0 ;    return 0 ;}static intsb_dsp_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p){    int unit;    snddev_info *d;    dev = minor(dev);    unit = dev >> 4 ;    d = &pcm_info[unit] ;    /*     * handle mixer calls first. Reads are in the default handler,     * so do not bother about them.     */    if ( (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0) )	return sb_mixer_set(d, cmd & 0xff, *(int *)arg) ;    /*     * for the remaining functions, use the default handler.     * ENOSYS means that the default handler should take care     * of implementing the ioctl.     */    return ENOSYS ;}static voidsbintr(int unit){    snddev_info *d = &pcm_info[unit];    int reason = 3, c=1, io_base = d->io_base;    DEB(printf("got sbintr for unit %d, flags 0x%08lx\n", unit, d->flags));    /*     * SB < 4.0 is half duplex and has only 1 bit for int source,     * so we fake it. SB 4.x (SB16) has the int source in a separate     * register.     * The Vibra16X has separate flags for 8 and 16 bit transfers, but     * I have no idea how to tell capture from playback interrupts...     */#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16)again:    if (d->bd_flags & BD_F_SB16) {	c = sb_getmixer(io_base, IRQ_STAT);	/* this tells us if the source is 8-bit or 16-bit dma. We	 * have to check the io channel to map it to read or write...	 */	    reason = 0 ;	    if ( c & 1 ) { /* 8-bit dma */	    if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )		    reason |= 1;	    if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )		    reason |= 2;	    }	    if ( c & 2 ) { /* 16-bit dma */	    if (d->play_fmt == AFMT_S16_LE)		    reason |= 1;	    if (d->rec_fmt == AFMT_S16_LE)		    reason |= 2;	    }	}    /* XXX previous location of ack... */    DEB(printf("sbintr, flags 0x%08lx reason %d c 0x%x\n",	d->flags, reason, c));    if ( reason & 1 ) { /* possibly a write interrupt */	if ( d->dbuf_out.dl )	    dsp_wrintr(d);    }    if ( reason & 2 ) {	if ( d->dbuf_in.dl )	    dsp_rdintr(d);    }    if ( c & 2 )	inb(DSP_DATA_AVL16); /* 16-bit int ack */    if (c & 1)	inb(DSP_DATA_AVAIL);	/* 8-bit int ack */    /*     * the sb16 might have multiple sources etc.     */    if ((d->bd_flags & BD_F_SB16) && (c & 3))	goto again;}/* * device-specific function called back from the dma module. * The reason of the callback is the second argument. * NOTE: during operations, some ioctl can be called to change * settings (e.g. speed, channels, format), and the default * ioctl handler will just record the change and set the * flag SND_F_INIT. The callback routine is in charge of applying * the changes at the next convenient time (typically, at the * start of operations). For full duplex devices, in some cases the * init requires both channels to be idle. */static intsb_callback(snddev_info *d, int reason){    int rd = reason & SND_CB_RD ;    snd_dbuf *b = (rd) ? & (d->dbuf_in) : & (d->dbuf_out) ;    int l = b->dl ;    switch (reason & SND_CB_REASON_MASK) {    case SND_CB_INIT : /* called with int enabled and no pending io */	/*	 * set the speed	 */	dsp_speed(d);	/*	 * set the desired DMA blocksize (influences select behaviour)	 */	snd_set_blocksize(d);	/*	 * since native mulaw is not present, emulate it.	 */	if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )	    d->flags |= SND_F_XLAT8 ;	else	    d->flags &= ~SND_F_XLAT8 ;	/*	 * there are too many flavours of SB for my taste... here i try to do	 * the proper initialization for each one.	 */	if (PLAIN_SB16(d->bd_flags)) {	    u_char c, c1 ;	    /* the original SB16 (non-PnP, or PnP, or Vibra16C)	     * can do full duplex using one 16-bit channel	     * and one 8-bit channel. It needs to be programmed to	     * use split format though.	     * I DON'T do this for the Vibra16X because I have no idea	     * of what needs to be done there...	     *	     * I use the following algorithm:	     * 1. check which direction(s) are active;	     * 2. check if we should swap dma channels	     * 3. check if we can do the swap.	     */	    int swap = 1 ; /* default... */	    if (d->play_fmt == 0) {		/* do whatever the read channel wants */		if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 )		    swap = 0;		if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 )		    swap = 0;	    } else {		/* privilege the write channel */		if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 )		    swap = 0;		if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )		    swap = 0;		if ( d->rec_fmt ) {		    /* check for possible config errors.		     * This cannot happen at open time since even in		     * case of opening rw we privilege the play		     * channel.		     */		    if (d->rec_fmt == d->play_fmt) {			DDB(printf("sorry, read DMA channel unavailable\n"));		    }		}	    }	    DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n",		d->play_fmt, d->rec_fmt, swap);)	    if (swap) {	        int c = d->dbuf_in.chan ;		d->dbuf_in.chan = d->dbuf_out.chan;		d->dbuf_out.chan = c ;	    }	}	else if (d->bd_flags & BD_F_ESS) {		u_char c;		DEB(printf("SND_CB_INIT, play_fmt == 0x%x, rec_fmt == 0x%x\n",			(int) d->play_fmt, (int) d->rec_fmt));		/* autoinit DMA mode */		if (d->play_fmt)			ess_write(d->io_base, 0xb8, 0x04);		else			ess_write(d->io_base, 0xb8, 0x0e);		c = (ess_read(d->io_base, 0xa8) & ~0x03) | 0x01;		if ((d->flags & SND_F_STEREO) == 0)			c++;		ess_write(d->io_base, 0xa8, c);	/* select mono/stereo */		ess_write(d->io_base, 0xb9, 2);	/* demand 4 bytes/transfer */		switch (d->play_fmt ? d->play_fmt : d->rec_fmt) {		case AFMT_S16_LE:			if (d->flags & SND_F_STEREO) {				/* 16 bit stereo */				if (d->play_fmt)					ess_write(d->io_base, 0xb6, 0x00);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -