Update the pcm driver to the most recent version. This should

add support for Vibra16X, OPTi925, and bring in several assorted
fixes to the code and documentation.
Also present here are apm hooks so that laptops can properly
reconfigure the hardware after suspend (tested on the Libretto50).
Reviewed by: jordan
This commit is contained in:
luigi 1998-10-02 17:26:37 +00:00
parent 5df5f0b107
commit 8d4824c432
19 changed files with 1607 additions and 617 deletions

@ -4,7 +4,7 @@
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
* AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
* AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others.
*
* Copyright Luigi Rizzo, 1997,1998
* Copyright by Hannu Savolainen 1994, 1995
@ -239,8 +239,32 @@ mss_attach(struct isa_device *dev)
outb(dev->id_iobase, bits );
}
}
if (1) { /* machine-specific code for the Toshiba Libretto */
u_char r6, r9;
outb( 0x370, 6 /* dma config */ );
outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */);
r6 = inb( 0x371 /* read */ );
outb( 0x370, 0xa /* version */ );
r9 = inb( 0x371 /* read */ );
DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);)
/*
* yamaha - set volume to max
*/
outb( 0x370, 7 /* volume left */ );
outb( 0x371, 0 /* max level */ );
outb( 0x370, 8 /* volume right */ );
outb( 0x371, 0 /* max level */ );
}
if ( FULL_DUPLEX(d) )
d->audio_fmt |= AFMT_FULLDUPLEX ;
if (d->bd_id == MD_YM0020) {
DDB(printf("setting up yamaha registers\n"));
outb(0x370, 6 /* dma config */ ) ;
if (FULL_DUPLEX(d))
outb(0x371, 0xa9 ); /* use both dma chans */
else
outb(0x371, 0x8b ); /* use low dma chan */
}
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@ -401,7 +425,7 @@ mss_callback(snddev_info *d, int reason)
/*
* perform all necessary initializations for i/o
*/
d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
d->rec_fmt = d->play_fmt ; /* no split format on the MSS */
snd_set_blocksize(d);
mss_reinit(d);
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
@ -561,15 +585,21 @@ again:
if (mc11 & masked)
printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked);
masked |= mc11 ;
/*
* the nice OPTi931 sets the IRQ line before setting the bits in
* mc11. So, on some occasions I have to retry (max 10 times).
*/
if ( mc11 == 0 ) { /* perhaps can return ... */
reason = inb(io_Status(d));
if (reason & 1) {
printf("one more try...\n");
goto again;
DEB(printf("one more try...\n");)
if (--loops)
goto again;
else
DDB(printf("opti_intr: irq but mc11 not set!...\n");)
}
if (loops==10) {
if (loops==10)
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
}
return;
}
@ -580,7 +610,8 @@ again:
dsp_wrintr(d);
}
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
if (--loops)
goto again;
DEB(printf("xxx too many loops\n");)
}
@ -657,7 +688,7 @@ ad_read(snddev_info *d, int reg)
int x;
flags = spltty();
AD_WAIT_INIT(d, 100);
AD_WAIT_INIT(d, 201);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
x = inb(io_Indexed_Data(d));
@ -672,7 +703,7 @@ ad_write(snddev_info *d, int reg, u_char data)
int x ;
flags = spltty();
AD_WAIT_INIT(d, 100);
AD_WAIT_INIT(d, 1002);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
outb(io_Indexed_Data(d), data);
@ -731,7 +762,7 @@ ad_enter_MCE(snddev_info *d)
int prev;
d->bd_flags |= BD_F_MCE_BIT;
AD_WAIT_INIT(d, 100);
AD_WAIT_INIT(d, 203);
prev = inb(io_Index_Addr(d));
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev | IA_MCE ) ;
@ -853,10 +884,11 @@ mss_mixer_set(snddev_info *d, int dev, int value)
d->mix_levels[dev] = left | (right << 8);
#if 0
/* Scale volumes */
left = mix_cvt[left];
right = mix_cvt[right];
#endif
/*
* Set the left channel
*/
@ -919,6 +951,16 @@ ad1848_mixer_reset(snddev_info *d)
ad_write(d, 20, 0x88);
ad_write(d, 21, 0x88);
break;
case MD_YM0020:
/* set master volume to max */
DDB(printf("set yamaha master volume to max"); )
outb(0x370, 7) ;
outb(0x371, 0) ;
outb(0x370, 8) ;
outb(0x371, 0) ;
break;
case MD_GUSPNP:
/* this is only necessary in mode 3 ... */
ad_write(d, 22, 0x88);
@ -997,7 +1039,7 @@ mss_format(snddev_info *d)
arg = AFMT_U8 ;
/* ulaw/alaw seems broken on the opti931... */
if (d->bd_id == MD_OPTI931) {
if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) {
if (arg == AFMT_MU_LAW) {
arg = AFMT_U8 ;
d->flags |= SND_F_XLAT8 ;
@ -1233,6 +1275,13 @@ mss_detect(struct isa_device *dev)
d->bd_id = MD_AD1845;
}
ad_write(d, 23, tmp); /* Restore */
DDB(printf("... try to identify the yamaha\n") ;)
tmp = inb(0x370) ;
outb(0x370, 6 /* dma config */ ) ;
if (inb(0x370) != 6 ) /* not a yamaha... restore. */
outb(0x370, tmp ) ;
else
d->bd_id = MD_YM0020 ;
break;
case 0x83: /* CS4236 */
@ -1244,7 +1293,6 @@ mss_detect(struct isa_device *dev)
default: /* Assume CS4231 */
BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);)
d->bd_id = MD_CS4231;
}
}
ad_write(d, 25, tmp1); /* Restore bits */
@ -1354,7 +1402,7 @@ cs423x_probe(u_long csn, u_long vend_id)
u_long id = vend_id & 0xff00ffff;
if ( id == 0x3700630e )
s = "CS4237" ;
else if ( id == 0x3600630e )
else if ( id == 0x3500630e || id == 0x3600630e )
s = "CS4236" ;
else if ( id == 0x3500630e )
s = "CS4236B" ;
@ -1364,7 +1412,7 @@ cs423x_probe(u_long csn, u_long vend_id)
s = "Yamaha SA2";
else if ( id == 0x3000a865)
s = "Yamaha SA3";
else if ( id == 0x0000a865)
else if (vend_id == 0x0000a865)
s = "Yamaha YMF719 OPL-SA3";
else if (vend_id == 0x8140d315)
s = "SoundscapeVIVO";
@ -1399,7 +1447,8 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
if (vend_id == 0x0008a865 || vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) {
if (vend_id==0x2000a865 || vend_id==0x3000a865 ||
vend_id==0x0008a865 || vend_id==0x8140d315) {
/* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */
dev->id_iobase = d.port[0] ;
tmp_d.alt_base = d.port[1] ;
@ -1418,7 +1467,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
case 0x2000a865: /* Yamaha SA2 */
case 0x3000a865: /* Yamaha SA3 */
case 0x0000a865: /* Yamaha YMF719 */
case 0x0000a865: /* Yamaha TMF719 SA3 */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.conf_base = d.port[4];
@ -1436,6 +1485,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
tmp_d.bd_id = MD_CS4237 ;
break;
case 0x3500630e: /* CS4236 */
case 0x3600630e: /* CS4236 */
case 0x3500630e: /* CS4236B */
tmp_d.bd_id = MD_CS4236 ;
@ -1451,7 +1501,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
write_pnp_parms( &d, ldn );
enable_pnp_card();
if ( (vend_id & 0x2000ffff) == 0x2000a865 ) {
if ( (vend_id & 0x0000ffff) == 0x0000a865 ) {
/* special volume setting for the Yamaha... */
outb(tmp_d.conf_base, 7 /* volume, left */);
outb(tmp_d.conf_base+1, 0 );
@ -1542,10 +1592,10 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
/*
* the 931 is not a real SB, it has important pieces of
* hardware controlled by both the WSS and the SB port...
* hardware controlled by both the MSS and the SB port...
*/
printf("--- opti931 in sb mode ---\n");
opti_write(p, 6, 1); /* MCIR6 wss disable, sb enable */
opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */
/*
* swap the main and alternate iobase address since we want
* to work in sb mode.
@ -1555,7 +1605,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
} else { /* mss-compatible codec */
tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */
opti_write(p, 6 , 2); /* MCIR6: wss enable, sb disable */
opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */
opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */
@ -1567,6 +1617,76 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static char *opti925_probe(u_long csn, u_long vend_id);
static void opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device opti925 = {
"opti925",
opti925_probe,
opti925_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, opti925);
static char *
opti925_probe(u_long csn, u_long vend_id)
{
if (vend_id == 0x2509143e) {
struct pnp_cinfo d ;
read_pnp_parms ( &d , 1 ) ;
if (d.enable == 0) {
printf("This is an OPTi925, but LDN 1 is disabled\n");
return NULL;
}
return "OPTi925" ;
}
return NULL ;
}
static void
opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
int the_irq = 0 ;
tmp_d = mss_op_desc;
snddev_last_probed = &tmp_d;
read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
the_irq = d.irq[0];
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 3 );
read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 2 );
read_pnp_parms ( &d , 1 ) ;
d.irq[0] = the_irq ;
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
write_pnp_parms ( &d , 1 );
enable_pnp_card();
tmp_d.conf_base = d.port[3];
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
pcmattach(dev);
}
static void gus_mem_cfg(snddev_info *tmp);
static char *guspnp_probe(u_long csn, u_long vend_id);

@ -150,7 +150,7 @@ ahead.
/*
* Table of mixer registers. There is a default table for the
* AD1848/CS423x clones, and one for the OPTI931. As more WSS
* AD1848/CS423x clones, and one for the OPTI931. As more MSS
* clones come out, there ought to be more tables.
*
* Fields in the table are : polarity, register, offset, bits

@ -3,7 +3,7 @@
*
* driver for the SoundBlaster and clones.
*
* Copyright 1997 Luigi Rizzo.
* Copyright 1997,1998 Luigi Rizzo.
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
@ -74,6 +74,8 @@ 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
@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev)
* 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 int
sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
{
@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_U8 ;
break ;
}
if ( (flags & FREAD) == 0)
/*
* 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 ;
if ( (flags & FWRITE) == 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 ;
@ -275,7 +294,10 @@ sb_intr(int unit)
* 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);
@ -284,25 +306,26 @@ again:
*/
reason = 0 ;
if ( c & 1 ) { /* 8-bit dma */
if (d->dbuf_out.chan < 4)
if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )
reason |= 1;
if (d->dbuf_in.chan < 4)
if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )
reason |= 2;
}
if ( c & 2 ) { /* 16-bit dma */
if (d->dbuf_out.chan >= 4)
if (d->play_fmt == AFMT_S16_LE)
reason |= 1;
if (d->dbuf_in.chan >= 4)
if (d->rec_fmt == AFMT_S16_LE)
reason |= 2;
}
}
/* XXX previous location of ack... */
DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason));
DEB(printf("sb_intr, 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);
else {
if (d->bd_flags & BD_F_SB16)
if (PLAIN_SB16(d->bd_flags))
printf("WARNING: wrintr but write DMA inactive!\n");
}
}
@ -310,7 +333,7 @@ again:
if ( d->dbuf_in.dl )
dsp_rdintr(d);
else {
if (d->bd_flags & BD_F_SB16)
if (PLAIN_SB16(d->bd_flags))
printf("WARNING: rdintr but read DMA inactive!\n");
}
}
@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason)
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 ;
if (d->bd_flags & BD_F_SB16) {
/*
* 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 SB16 can do full duplex using one 16-bit channel
/* 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.
* We use the following algorithm:
* 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.
@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason)
if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
if ( d->rec_fmt ) {
/* check for possible config errors. */
/* 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"));
}
@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason)
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
}
} else if (d->bd_flags & BD_F_ESS) {
u_char c ;
if (d->play_fmt == 0) {
/* initialize for record */
static u_char cmd[] = {
0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc
};
ess_write(d->io_base, 0xb8, 0x0e);
c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ;
if (d->flags & SND_F_STEREO)
c++ ;
ess_write(d->io_base, 0xa8, c);
ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */
/*
* set format in b6, b7
*/
} else {
/* initialize for play */
static u_char cmd[] = {
0x80,0x51,0xd0,0x00,0x71,0xf4,
0x80,0x51,0x98,0x00,0x71,0xbc
};
}
}
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
if (d->bd_flags & BD_F_SB16X) {
/* just a guess: on the Vibra16X, the first
* op started takes the first dma channel,
* the second one takes the next...
* The default is to be ready for play.
*/
int swap = 0 ;
DEB(printf("start %s -- now dma %d:%d\n",
rd ? "rd" : "wr",
d->dbuf_out.chan, d->dbuf_in.chan););
/* swap only if both channels are idle
* play: dl=0, since there is no pause;
* rec: rl=0
*/
if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) {
/* must swap channels, but also save dl */
int c = d->dbuf_in.chan ;
int dl = d->dbuf_in.dl ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
d->dbuf_in.dl = dl ;
printf("swapped -- now dma %d:%d\n",
d->dbuf_out.chan, d->dbuf_in.chan);
}
}
/*
* XXX note: c1 and l should be set basing on d->rec_fmt,
* but there is no choice once a 16 or 8-bit channel
* is assigned. This means that if the application
* tries to use a bad format, the sound will not be nice.
*/
if ( b->chan > 4 ) {
if ( b->chan > 4
|| (rd && d->rec_fmt == AFMT_S16_LE)
|| (!rd && d->play_fmt == AFMT_S16_LE)
) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
l /= 2 ;
@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_STOP :
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
if ( b->chan > 4
|| (rd && d->rec_fmt == AFMT_S16_LE)
|| (!rd && d->play_fmt == AFMT_S16_LE)
)
cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
if (d->bd_flags & BD_F_SB16X) {
/* restore possible swapped channels.
* The default is to be ready for play.
* XXX right now, it kills all input on overflow
*/
if ( rd && d->dbuf_out.dl == 0 ) {
/* must swap channels ? */
int c = d->dbuf_in.chan ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
printf("restored -- now dma %d:%d\n",
d->dbuf_out.chan, d->dbuf_in.chan);
}
}
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
printf("ESS1868 (rev %d)\n", rev);
else
printf("ESS688 (rev %d)\n", rev);
/* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */
break ; /* XXX */
} else {
printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d)
/*
* Common code for the midi and pcm functions
*
* sb_cmd write a single byte to the CMD port.
* sb_cmd2 write a CMD + 1 byte arg
* sb_cmd3 write a CMD + 2 byte arg
* sb_get_byte returns a single byte from the DSP data port
*
* ess_write is actually sb_cmd2
* ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
*/
int
@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
val = inb(io_base + 5);
val = inb(io_base + SB_MIX_DATA);
DELAY(10);
splx(flags);
@ -747,6 +874,19 @@ sb_get_byte(int io_base)
return 0xffff;
}
int
ess_write(int io_base, u_char reg, int val)
{
return sb_cmd2(io_base, reg, val);
}
int
ess_read(int io_base, u_char reg)
{
if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) )
return 0xffff ;
return sb_get_byte(io_base);
}
/*
@ -785,17 +925,21 @@ dsp_speed(snddev_info *d)
*/
if (d->bd_flags & BD_F_ESS) {
int t;
RANGE (speed, 4000, 48000);
RANGE (speed, 5000, 49000);
if (speed > 22000) {
t = (795500 + speed / 2) / speed;
speed = (795500 + t / 2) / t ;
t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ;
t = (256 - t ) | 0x80 ;
} else {
t = (397700 + speed / 2) / speed;
speed = (397700 + t / 2) / t ;
t = 128 - (397700 + speed / 2) / speed ;
t = 128 - t ;
}
sb_cmd2(d->io_base, 0xa1, t); /* set time constant */
ess_write(d->io_base, 0xa1, t); /* set time constant */
d->play_speed = d->rec_speed = speed ;
speed = (speed * 9 ) / 20 ;
t = 256-7160000/(speed*82);
ess_write(d->io_base,0xa2,t);
return speed ;
}
@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static char *opti925_probe(u_long csn, u_long vend_id);
static void opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device opti925 = {
"opti925",
opti925_probe,
opti925_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, opti925);
static char *
opti925_probe(u_long csn, u_long vend_id)
{
if (vend_id == 0x2509143e) {
struct pnp_cinfo d ;
read_pnp_parms ( &d , 1 ) ;
if (d.enable == 0) {
printf("This is an OPTi925, but LDN 1 is disabled\n");
return NULL;
}
return "OPTi925" ;
}
return NULL ;
}
static void
opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
int the_irq = 0 ;
tmp_d = sb_op_desc;
snddev_last_probed = &tmp_d;
read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
the_irq = d.irq[0];
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 3 );
read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 2 );
read_pnp_parms ( &d , 1 ) ;
d.irq[0] = the_irq ;
dev->id_iobase = d.port[0];
write_pnp_parms ( &d , 1 );
enable_pnp_card();
tmp_d.conf_base = d.port[3];
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
pcmattach(dev);
}
/*
* A driver for some SB16pnp and compatibles...
*
* Avance Asound 100 -- 0x01009305
* Avance Logic ALS100+ -- 0x10019305
* xxx -- 0x2b008c0e
*
*/
@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id)
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
* Vibra16X: 0xf0008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
if (vend_id == 0xf0008c0e)
s = "Vibra16X" ;
else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
else if (vend_id == 0x10019305)
s = "Avance Logic 100+" ; /* Vibra16X-class */
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
pcm_info[dev->id_unit] = tmp_d;
pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) {
/*
* XXX please add here the vend_id for other vibra16X cards...
* And remember, must change tmp_d, not
*/
tmp_d.bd_flags |= BD_F_SB16X ;
}
pcmattach(dev);
}
#endif /* NPNP */

@ -19,9 +19,9 @@ extern int sbc_major, sbc_minor ;
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
#define SB_MIX_ADDR 0x4
#define SB_MIX_DATA 0x5
#if 0
#define MIXER_ADDR (io_base + 0x4)
#define MIXER_DATA (io_base + 0x5)
#define OPL3_LEFT (io_base + 0x0)
#define OPL3_RIGHT (io_base + 0x2)
#define OPL3_BOTH (io_base + 0x8)
@ -138,9 +138,14 @@ extern int sbc_major, sbc_minor ;
#define BD_F_MIX_CT1745 0x0030 /* CT1745 */
#define BD_F_SB16 0x0100 /* this is a SB16 */
#define BD_F_NOREC 0x0200 /* recording not supported on this board */
#define BD_F_SB16X 0x0200 /* this is a vibra16X or clone */
#define BD_F_MIDIBUSY 0x0400 /* midi busy */
#define BD_F_ESS 0x0800 /* this is an ESS chip */
/*
* on some SB16 cards, at times I swap DMA channels. Remember this
* so that they can be restored later.
*/
#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */
/*

@ -4,7 +4,7 @@
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
* AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
* AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others.
*
* Copyright Luigi Rizzo, 1997,1998
* Copyright by Hannu Savolainen 1994, 1995
@ -239,8 +239,32 @@ mss_attach(struct isa_device *dev)
outb(dev->id_iobase, bits );
}
}
if (1) { /* machine-specific code for the Toshiba Libretto */
u_char r6, r9;
outb( 0x370, 6 /* dma config */ );
outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */);
r6 = inb( 0x371 /* read */ );
outb( 0x370, 0xa /* version */ );
r9 = inb( 0x371 /* read */ );
DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);)
/*
* yamaha - set volume to max
*/
outb( 0x370, 7 /* volume left */ );
outb( 0x371, 0 /* max level */ );
outb( 0x370, 8 /* volume right */ );
outb( 0x371, 0 /* max level */ );
}
if ( FULL_DUPLEX(d) )
d->audio_fmt |= AFMT_FULLDUPLEX ;
if (d->bd_id == MD_YM0020) {
DDB(printf("setting up yamaha registers\n"));
outb(0x370, 6 /* dma config */ ) ;
if (FULL_DUPLEX(d))
outb(0x371, 0xa9 ); /* use both dma chans */
else
outb(0x371, 0x8b ); /* use low dma chan */
}
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@ -401,7 +425,7 @@ mss_callback(snddev_info *d, int reason)
/*
* perform all necessary initializations for i/o
*/
d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
d->rec_fmt = d->play_fmt ; /* no split format on the MSS */
snd_set_blocksize(d);
mss_reinit(d);
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
@ -561,15 +585,21 @@ again:
if (mc11 & masked)
printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked);
masked |= mc11 ;
/*
* the nice OPTi931 sets the IRQ line before setting the bits in
* mc11. So, on some occasions I have to retry (max 10 times).
*/
if ( mc11 == 0 ) { /* perhaps can return ... */
reason = inb(io_Status(d));
if (reason & 1) {
printf("one more try...\n");
goto again;
DEB(printf("one more try...\n");)
if (--loops)
goto again;
else
DDB(printf("opti_intr: irq but mc11 not set!...\n");)
}
if (loops==10) {
if (loops==10)
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
}
return;
}
@ -580,7 +610,8 @@ again:
dsp_wrintr(d);
}
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
if (--loops)
goto again;
DEB(printf("xxx too many loops\n");)
}
@ -657,7 +688,7 @@ ad_read(snddev_info *d, int reg)
int x;
flags = spltty();
AD_WAIT_INIT(d, 100);
AD_WAIT_INIT(d, 201);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
x = inb(io_Indexed_Data(d));
@ -672,7 +703,7 @@ ad_write(snddev_info *d, int reg, u_char data)
int x ;
flags = spltty();
AD_WAIT_INIT(d, 100);
AD_WAIT_INIT(d, 1002);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
outb(io_Indexed_Data(d), data);
@ -731,7 +762,7 @@ ad_enter_MCE(snddev_info *d)
int prev;
d->bd_flags |= BD_F_MCE_BIT;
AD_WAIT_INIT(d, 100);
AD_WAIT_INIT(d, 203);
prev = inb(io_Index_Addr(d));
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev | IA_MCE ) ;
@ -853,10 +884,11 @@ mss_mixer_set(snddev_info *d, int dev, int value)
d->mix_levels[dev] = left | (right << 8);
#if 0
/* Scale volumes */
left = mix_cvt[left];
right = mix_cvt[right];
#endif
/*
* Set the left channel
*/
@ -919,6 +951,16 @@ ad1848_mixer_reset(snddev_info *d)
ad_write(d, 20, 0x88);
ad_write(d, 21, 0x88);
break;
case MD_YM0020:
/* set master volume to max */
DDB(printf("set yamaha master volume to max"); )
outb(0x370, 7) ;
outb(0x371, 0) ;
outb(0x370, 8) ;
outb(0x371, 0) ;
break;
case MD_GUSPNP:
/* this is only necessary in mode 3 ... */
ad_write(d, 22, 0x88);
@ -997,7 +1039,7 @@ mss_format(snddev_info *d)
arg = AFMT_U8 ;
/* ulaw/alaw seems broken on the opti931... */
if (d->bd_id == MD_OPTI931) {
if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) {
if (arg == AFMT_MU_LAW) {
arg = AFMT_U8 ;
d->flags |= SND_F_XLAT8 ;
@ -1233,6 +1275,13 @@ mss_detect(struct isa_device *dev)
d->bd_id = MD_AD1845;
}
ad_write(d, 23, tmp); /* Restore */
DDB(printf("... try to identify the yamaha\n") ;)
tmp = inb(0x370) ;
outb(0x370, 6 /* dma config */ ) ;
if (inb(0x370) != 6 ) /* not a yamaha... restore. */
outb(0x370, tmp ) ;
else
d->bd_id = MD_YM0020 ;
break;
case 0x83: /* CS4236 */
@ -1244,7 +1293,6 @@ mss_detect(struct isa_device *dev)
default: /* Assume CS4231 */
BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);)
d->bd_id = MD_CS4231;
}
}
ad_write(d, 25, tmp1); /* Restore bits */
@ -1354,7 +1402,7 @@ cs423x_probe(u_long csn, u_long vend_id)
u_long id = vend_id & 0xff00ffff;
if ( id == 0x3700630e )
s = "CS4237" ;
else if ( id == 0x3600630e )
else if ( id == 0x3500630e || id == 0x3600630e )
s = "CS4236" ;
else if ( id == 0x3500630e )
s = "CS4236B" ;
@ -1364,7 +1412,7 @@ cs423x_probe(u_long csn, u_long vend_id)
s = "Yamaha SA2";
else if ( id == 0x3000a865)
s = "Yamaha SA3";
else if ( id == 0x0000a865)
else if (vend_id == 0x0000a865)
s = "Yamaha YMF719 OPL-SA3";
else if (vend_id == 0x8140d315)
s = "SoundscapeVIVO";
@ -1399,7 +1447,8 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
if (vend_id == 0x0008a865 || vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) {
if (vend_id==0x2000a865 || vend_id==0x3000a865 ||
vend_id==0x0008a865 || vend_id==0x8140d315) {
/* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */
dev->id_iobase = d.port[0] ;
tmp_d.alt_base = d.port[1] ;
@ -1418,7 +1467,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
case 0x2000a865: /* Yamaha SA2 */
case 0x3000a865: /* Yamaha SA3 */
case 0x0000a865: /* Yamaha YMF719 */
case 0x0000a865: /* Yamaha TMF719 SA3 */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.conf_base = d.port[4];
@ -1436,6 +1485,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
tmp_d.bd_id = MD_CS4237 ;
break;
case 0x3500630e: /* CS4236 */
case 0x3600630e: /* CS4236 */
case 0x3500630e: /* CS4236B */
tmp_d.bd_id = MD_CS4236 ;
@ -1451,7 +1501,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
write_pnp_parms( &d, ldn );
enable_pnp_card();
if ( (vend_id & 0x2000ffff) == 0x2000a865 ) {
if ( (vend_id & 0x0000ffff) == 0x0000a865 ) {
/* special volume setting for the Yamaha... */
outb(tmp_d.conf_base, 7 /* volume, left */);
outb(tmp_d.conf_base+1, 0 );
@ -1542,10 +1592,10 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
/*
* the 931 is not a real SB, it has important pieces of
* hardware controlled by both the WSS and the SB port...
* hardware controlled by both the MSS and the SB port...
*/
printf("--- opti931 in sb mode ---\n");
opti_write(p, 6, 1); /* MCIR6 wss disable, sb enable */
opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */
/*
* swap the main and alternate iobase address since we want
* to work in sb mode.
@ -1555,7 +1605,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
} else { /* mss-compatible codec */
tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */
opti_write(p, 6 , 2); /* MCIR6: wss enable, sb disable */
opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */
opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */
@ -1567,6 +1617,76 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static char *opti925_probe(u_long csn, u_long vend_id);
static void opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device opti925 = {
"opti925",
opti925_probe,
opti925_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, opti925);
static char *
opti925_probe(u_long csn, u_long vend_id)
{
if (vend_id == 0x2509143e) {
struct pnp_cinfo d ;
read_pnp_parms ( &d , 1 ) ;
if (d.enable == 0) {
printf("This is an OPTi925, but LDN 1 is disabled\n");
return NULL;
}
return "OPTi925" ;
}
return NULL ;
}
static void
opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
int the_irq = 0 ;
tmp_d = mss_op_desc;
snddev_last_probed = &tmp_d;
read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
the_irq = d.irq[0];
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 3 );
read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 2 );
read_pnp_parms ( &d , 1 ) ;
d.irq[0] = the_irq ;
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
write_pnp_parms ( &d , 1 );
enable_pnp_card();
tmp_d.conf_base = d.port[3];
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
pcmattach(dev);
}
static void gus_mem_cfg(snddev_info *tmp);
static char *guspnp_probe(u_long csn, u_long vend_id);

@ -150,7 +150,7 @@ ahead.
/*
* Table of mixer registers. There is a default table for the
* AD1848/CS423x clones, and one for the OPTI931. As more WSS
* AD1848/CS423x clones, and one for the OPTI931. As more MSS
* clones come out, there ought to be more tables.
*
* Fields in the table are : polarity, register, offset, bits

@ -3,7 +3,7 @@
*
* driver for the SoundBlaster and clones.
*
* Copyright 1997 Luigi Rizzo.
* Copyright 1997,1998 Luigi Rizzo.
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
@ -74,6 +74,8 @@ 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
@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev)
* 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 int
sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
{
@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_U8 ;
break ;
}
if ( (flags & FREAD) == 0)
/*
* 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 ;
if ( (flags & FWRITE) == 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 ;
@ -275,7 +294,10 @@ sb_intr(int unit)
* 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);
@ -284,25 +306,26 @@ again:
*/
reason = 0 ;
if ( c & 1 ) { /* 8-bit dma */
if (d->dbuf_out.chan < 4)
if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )
reason |= 1;
if (d->dbuf_in.chan < 4)
if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )
reason |= 2;
}
if ( c & 2 ) { /* 16-bit dma */
if (d->dbuf_out.chan >= 4)
if (d->play_fmt == AFMT_S16_LE)
reason |= 1;
if (d->dbuf_in.chan >= 4)
if (d->rec_fmt == AFMT_S16_LE)
reason |= 2;
}
}
/* XXX previous location of ack... */
DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason));
DEB(printf("sb_intr, 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);
else {
if (d->bd_flags & BD_F_SB16)
if (PLAIN_SB16(d->bd_flags))
printf("WARNING: wrintr but write DMA inactive!\n");
}
}
@ -310,7 +333,7 @@ again:
if ( d->dbuf_in.dl )
dsp_rdintr(d);
else {
if (d->bd_flags & BD_F_SB16)
if (PLAIN_SB16(d->bd_flags))
printf("WARNING: rdintr but read DMA inactive!\n");
}
}
@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason)
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 ;
if (d->bd_flags & BD_F_SB16) {
/*
* 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 SB16 can do full duplex using one 16-bit channel
/* 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.
* We use the following algorithm:
* 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.
@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason)
if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
if ( d->rec_fmt ) {
/* check for possible config errors. */
/* 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"));
}
@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason)
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
}
} else if (d->bd_flags & BD_F_ESS) {
u_char c ;
if (d->play_fmt == 0) {
/* initialize for record */
static u_char cmd[] = {
0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc
};
ess_write(d->io_base, 0xb8, 0x0e);
c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ;
if (d->flags & SND_F_STEREO)
c++ ;
ess_write(d->io_base, 0xa8, c);
ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */
/*
* set format in b6, b7
*/
} else {
/* initialize for play */
static u_char cmd[] = {
0x80,0x51,0xd0,0x00,0x71,0xf4,
0x80,0x51,0x98,0x00,0x71,0xbc
};
}
}
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
if (d->bd_flags & BD_F_SB16X) {
/* just a guess: on the Vibra16X, the first
* op started takes the first dma channel,
* the second one takes the next...
* The default is to be ready for play.
*/
int swap = 0 ;
DEB(printf("start %s -- now dma %d:%d\n",
rd ? "rd" : "wr",
d->dbuf_out.chan, d->dbuf_in.chan););
/* swap only if both channels are idle
* play: dl=0, since there is no pause;
* rec: rl=0
*/
if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) {
/* must swap channels, but also save dl */
int c = d->dbuf_in.chan ;
int dl = d->dbuf_in.dl ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
d->dbuf_in.dl = dl ;
printf("swapped -- now dma %d:%d\n",
d->dbuf_out.chan, d->dbuf_in.chan);
}
}
/*
* XXX note: c1 and l should be set basing on d->rec_fmt,
* but there is no choice once a 16 or 8-bit channel
* is assigned. This means that if the application
* tries to use a bad format, the sound will not be nice.
*/
if ( b->chan > 4 ) {
if ( b->chan > 4
|| (rd && d->rec_fmt == AFMT_S16_LE)
|| (!rd && d->play_fmt == AFMT_S16_LE)
) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
l /= 2 ;
@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_STOP :
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
if ( b->chan > 4
|| (rd && d->rec_fmt == AFMT_S16_LE)
|| (!rd && d->play_fmt == AFMT_S16_LE)
)
cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
if (d->bd_flags & BD_F_SB16X) {
/* restore possible swapped channels.
* The default is to be ready for play.
* XXX right now, it kills all input on overflow
*/
if ( rd && d->dbuf_out.dl == 0 ) {
/* must swap channels ? */
int c = d->dbuf_in.chan ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
printf("restored -- now dma %d:%d\n",
d->dbuf_out.chan, d->dbuf_in.chan);
}
}
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
printf("ESS1868 (rev %d)\n", rev);
else
printf("ESS688 (rev %d)\n", rev);
/* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */
break ; /* XXX */
} else {
printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d)
/*
* Common code for the midi and pcm functions
*
* sb_cmd write a single byte to the CMD port.
* sb_cmd2 write a CMD + 1 byte arg
* sb_cmd3 write a CMD + 2 byte arg
* sb_get_byte returns a single byte from the DSP data port
*
* ess_write is actually sb_cmd2
* ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
*/
int
@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
val = inb(io_base + 5);
val = inb(io_base + SB_MIX_DATA);
DELAY(10);
splx(flags);
@ -747,6 +874,19 @@ sb_get_byte(int io_base)
return 0xffff;
}
int
ess_write(int io_base, u_char reg, int val)
{
return sb_cmd2(io_base, reg, val);
}
int
ess_read(int io_base, u_char reg)
{
if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) )
return 0xffff ;
return sb_get_byte(io_base);
}
/*
@ -785,17 +925,21 @@ dsp_speed(snddev_info *d)
*/
if (d->bd_flags & BD_F_ESS) {
int t;
RANGE (speed, 4000, 48000);
RANGE (speed, 5000, 49000);
if (speed > 22000) {
t = (795500 + speed / 2) / speed;
speed = (795500 + t / 2) / t ;
t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ;
t = (256 - t ) | 0x80 ;
} else {
t = (397700 + speed / 2) / speed;
speed = (397700 + t / 2) / t ;
t = 128 - (397700 + speed / 2) / speed ;
t = 128 - t ;
}
sb_cmd2(d->io_base, 0xa1, t); /* set time constant */
ess_write(d->io_base, 0xa1, t); /* set time constant */
d->play_speed = d->rec_speed = speed ;
speed = (speed * 9 ) / 20 ;
t = 256-7160000/(speed*82);
ess_write(d->io_base,0xa2,t);
return speed ;
}
@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static char *opti925_probe(u_long csn, u_long vend_id);
static void opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device opti925 = {
"opti925",
opti925_probe,
opti925_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, opti925);
static char *
opti925_probe(u_long csn, u_long vend_id)
{
if (vend_id == 0x2509143e) {
struct pnp_cinfo d ;
read_pnp_parms ( &d , 1 ) ;
if (d.enable == 0) {
printf("This is an OPTi925, but LDN 1 is disabled\n");
return NULL;
}
return "OPTi925" ;
}
return NULL ;
}
static void
opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
int the_irq = 0 ;
tmp_d = sb_op_desc;
snddev_last_probed = &tmp_d;
read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
the_irq = d.irq[0];
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 3 );
read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 2 );
read_pnp_parms ( &d , 1 ) ;
d.irq[0] = the_irq ;
dev->id_iobase = d.port[0];
write_pnp_parms ( &d , 1 );
enable_pnp_card();
tmp_d.conf_base = d.port[3];
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
pcmattach(dev);
}
/*
* A driver for some SB16pnp and compatibles...
*
* Avance Asound 100 -- 0x01009305
* Avance Logic ALS100+ -- 0x10019305
* xxx -- 0x2b008c0e
*
*/
@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id)
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
* Vibra16X: 0xf0008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
if (vend_id == 0xf0008c0e)
s = "Vibra16X" ;
else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
else if (vend_id == 0x10019305)
s = "Avance Logic 100+" ; /* Vibra16X-class */
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
pcm_info[dev->id_unit] = tmp_d;
pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) {
/*
* XXX please add here the vend_id for other vibra16X cards...
* And remember, must change tmp_d, not
*/
tmp_d.bd_flags |= BD_F_SB16X ;
}
pcmattach(dev);
}
#endif /* NPNP */

@ -19,9 +19,9 @@ extern int sbc_major, sbc_minor ;
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
#define SB_MIX_ADDR 0x4
#define SB_MIX_DATA 0x5
#if 0
#define MIXER_ADDR (io_base + 0x4)
#define MIXER_DATA (io_base + 0x5)
#define OPL3_LEFT (io_base + 0x0)
#define OPL3_RIGHT (io_base + 0x2)
#define OPL3_BOTH (io_base + 0x8)
@ -138,9 +138,14 @@ extern int sbc_major, sbc_minor ;
#define BD_F_MIX_CT1745 0x0030 /* CT1745 */
#define BD_F_SB16 0x0100 /* this is a SB16 */
#define BD_F_NOREC 0x0200 /* recording not supported on this board */
#define BD_F_SB16X 0x0200 /* this is a vibra16X or clone */
#define BD_F_MIDIBUSY 0x0400 /* midi busy */
#define BD_F_ESS 0x0800 /* this is an ESS chip */
/*
* on some SB16 cards, at times I swap DMA channels. Remember this
* so that they can be restored later.
*/
#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */
/*

@ -3,7 +3,7 @@
*
* driver for the SoundBlaster and clones.
*
* Copyright 1997 Luigi Rizzo.
* Copyright 1997,1998 Luigi Rizzo.
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
@ -74,6 +74,8 @@ 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
@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev)
* 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 int
sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
{
@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_U8 ;
break ;
}
if ( (flags & FREAD) == 0)
/*
* 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 ;
if ( (flags & FWRITE) == 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 ;
@ -275,7 +294,10 @@ sb_intr(int unit)
* 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);
@ -284,25 +306,26 @@ again:
*/
reason = 0 ;
if ( c & 1 ) { /* 8-bit dma */
if (d->dbuf_out.chan < 4)
if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )
reason |= 1;
if (d->dbuf_in.chan < 4)
if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )
reason |= 2;
}
if ( c & 2 ) { /* 16-bit dma */
if (d->dbuf_out.chan >= 4)
if (d->play_fmt == AFMT_S16_LE)
reason |= 1;
if (d->dbuf_in.chan >= 4)
if (d->rec_fmt == AFMT_S16_LE)
reason |= 2;
}
}
/* XXX previous location of ack... */
DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason));
DEB(printf("sb_intr, 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);
else {
if (d->bd_flags & BD_F_SB16)
if (PLAIN_SB16(d->bd_flags))
printf("WARNING: wrintr but write DMA inactive!\n");
}
}
@ -310,7 +333,7 @@ again:
if ( d->dbuf_in.dl )
dsp_rdintr(d);
else {
if (d->bd_flags & BD_F_SB16)
if (PLAIN_SB16(d->bd_flags))
printf("WARNING: rdintr but read DMA inactive!\n");
}
}
@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason)
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 ;
if (d->bd_flags & BD_F_SB16) {
/*
* 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 SB16 can do full duplex using one 16-bit channel
/* 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.
* We use the following algorithm:
* 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.
@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason)
if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
if ( d->rec_fmt ) {
/* check for possible config errors. */
/* 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"));
}
@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason)
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
}
} else if (d->bd_flags & BD_F_ESS) {
u_char c ;
if (d->play_fmt == 0) {
/* initialize for record */
static u_char cmd[] = {
0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc
};
ess_write(d->io_base, 0xb8, 0x0e);
c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ;
if (d->flags & SND_F_STEREO)
c++ ;
ess_write(d->io_base, 0xa8, c);
ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */
/*
* set format in b6, b7
*/
} else {
/* initialize for play */
static u_char cmd[] = {
0x80,0x51,0xd0,0x00,0x71,0xf4,
0x80,0x51,0x98,0x00,0x71,0xbc
};
}
}
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
if (d->bd_flags & BD_F_SB16X) {
/* just a guess: on the Vibra16X, the first
* op started takes the first dma channel,
* the second one takes the next...
* The default is to be ready for play.
*/
int swap = 0 ;
DEB(printf("start %s -- now dma %d:%d\n",
rd ? "rd" : "wr",
d->dbuf_out.chan, d->dbuf_in.chan););
/* swap only if both channels are idle
* play: dl=0, since there is no pause;
* rec: rl=0
*/
if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) {
/* must swap channels, but also save dl */
int c = d->dbuf_in.chan ;
int dl = d->dbuf_in.dl ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
d->dbuf_in.dl = dl ;
printf("swapped -- now dma %d:%d\n",
d->dbuf_out.chan, d->dbuf_in.chan);
}
}
/*
* XXX note: c1 and l should be set basing on d->rec_fmt,
* but there is no choice once a 16 or 8-bit channel
* is assigned. This means that if the application
* tries to use a bad format, the sound will not be nice.
*/
if ( b->chan > 4 ) {
if ( b->chan > 4
|| (rd && d->rec_fmt == AFMT_S16_LE)
|| (!rd && d->play_fmt == AFMT_S16_LE)
) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
l /= 2 ;
@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_STOP :
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
if ( b->chan > 4
|| (rd && d->rec_fmt == AFMT_S16_LE)
|| (!rd && d->play_fmt == AFMT_S16_LE)
)
cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
if (d->bd_flags & BD_F_SB16X) {
/* restore possible swapped channels.
* The default is to be ready for play.
* XXX right now, it kills all input on overflow
*/
if ( rd && d->dbuf_out.dl == 0 ) {
/* must swap channels ? */
int c = d->dbuf_in.chan ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
printf("restored -- now dma %d:%d\n",
d->dbuf_out.chan, d->dbuf_in.chan);
}
}
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
printf("ESS1868 (rev %d)\n", rev);
else
printf("ESS688 (rev %d)\n", rev);
/* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */
break ; /* XXX */
} else {
printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d)
/*
* Common code for the midi and pcm functions
*
* sb_cmd write a single byte to the CMD port.
* sb_cmd2 write a CMD + 1 byte arg
* sb_cmd3 write a CMD + 2 byte arg
* sb_get_byte returns a single byte from the DSP data port
*
* ess_write is actually sb_cmd2
* ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
*/
int
@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
val = inb(io_base + 5);
val = inb(io_base + SB_MIX_DATA);
DELAY(10);
splx(flags);
@ -747,6 +874,19 @@ sb_get_byte(int io_base)
return 0xffff;
}
int
ess_write(int io_base, u_char reg, int val)
{
return sb_cmd2(io_base, reg, val);
}
int
ess_read(int io_base, u_char reg)
{
if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) )
return 0xffff ;
return sb_get_byte(io_base);
}
/*
@ -785,17 +925,21 @@ dsp_speed(snddev_info *d)
*/
if (d->bd_flags & BD_F_ESS) {
int t;
RANGE (speed, 4000, 48000);
RANGE (speed, 5000, 49000);
if (speed > 22000) {
t = (795500 + speed / 2) / speed;
speed = (795500 + t / 2) / t ;
t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ;
t = (256 - t ) | 0x80 ;
} else {
t = (397700 + speed / 2) / speed;
speed = (397700 + t / 2) / t ;
t = 128 - (397700 + speed / 2) / speed ;
t = 128 - t ;
}
sb_cmd2(d->io_base, 0xa1, t); /* set time constant */
ess_write(d->io_base, 0xa1, t); /* set time constant */
d->play_speed = d->rec_speed = speed ;
speed = (speed * 9 ) / 20 ;
t = 256-7160000/(speed*82);
ess_write(d->io_base,0xa2,t);
return speed ;
}
@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static char *opti925_probe(u_long csn, u_long vend_id);
static void opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device opti925 = {
"opti925",
opti925_probe,
opti925_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, opti925);
static char *
opti925_probe(u_long csn, u_long vend_id)
{
if (vend_id == 0x2509143e) {
struct pnp_cinfo d ;
read_pnp_parms ( &d , 1 ) ;
if (d.enable == 0) {
printf("This is an OPTi925, but LDN 1 is disabled\n");
return NULL;
}
return "OPTi925" ;
}
return NULL ;
}
static void
opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
int the_irq = 0 ;
tmp_d = sb_op_desc;
snddev_last_probed = &tmp_d;
read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
the_irq = d.irq[0];
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 3 );
read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 2 );
read_pnp_parms ( &d , 1 ) ;
d.irq[0] = the_irq ;
dev->id_iobase = d.port[0];
write_pnp_parms ( &d , 1 );
enable_pnp_card();
tmp_d.conf_base = d.port[3];
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
pcmattach(dev);
}
/*
* A driver for some SB16pnp and compatibles...
*
* Avance Asound 100 -- 0x01009305
* Avance Logic ALS100+ -- 0x10019305
* xxx -- 0x2b008c0e
*
*/
@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id)
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
* Vibra16X: 0xf0008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
if (vend_id == 0xf0008c0e)
s = "Vibra16X" ;
else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
else if (vend_id == 0x10019305)
s = "Avance Logic 100+" ; /* Vibra16X-class */
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
pcm_info[dev->id_unit] = tmp_d;
pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) {
/*
* XXX please add here the vend_id for other vibra16X cards...
* And remember, must change tmp_d, not
*/
tmp_d.bd_flags |= BD_F_SB16X ;
}
pcmattach(dev);
}
#endif /* NPNP */

@ -3,7 +3,7 @@
*
* driver for the SoundBlaster and clones.
*
* Copyright 1997 Luigi Rizzo.
* Copyright 1997,1998 Luigi Rizzo.
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
@ -74,6 +74,8 @@ 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
@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev)
* 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 int
sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
{
@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_U8 ;
break ;
}
if ( (flags & FREAD) == 0)
/*
* 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 ;
if ( (flags & FWRITE) == 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 ;
@ -275,7 +294,10 @@ sb_intr(int unit)
* 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);
@ -284,25 +306,26 @@ again:
*/
reason = 0 ;
if ( c & 1 ) { /* 8-bit dma */
if (d->dbuf_out.chan < 4)
if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )
reason |= 1;
if (d->dbuf_in.chan < 4)
if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )
reason |= 2;
}
if ( c & 2 ) { /* 16-bit dma */
if (d->dbuf_out.chan >= 4)
if (d->play_fmt == AFMT_S16_LE)
reason |= 1;
if (d->dbuf_in.chan >= 4)
if (d->rec_fmt == AFMT_S16_LE)
reason |= 2;
}
}
/* XXX previous location of ack... */
DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason));
DEB(printf("sb_intr, 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);
else {
if (d->bd_flags & BD_F_SB16)
if (PLAIN_SB16(d->bd_flags))
printf("WARNING: wrintr but write DMA inactive!\n");
}
}
@ -310,7 +333,7 @@ again:
if ( d->dbuf_in.dl )
dsp_rdintr(d);
else {
if (d->bd_flags & BD_F_SB16)
if (PLAIN_SB16(d->bd_flags))
printf("WARNING: rdintr but read DMA inactive!\n");
}
}
@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason)
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 ;
if (d->bd_flags & BD_F_SB16) {
/*
* 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 SB16 can do full duplex using one 16-bit channel
/* 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.
* We use the following algorithm:
* 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.
@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason)
if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
if ( d->rec_fmt ) {
/* check for possible config errors. */
/* 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"));
}
@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason)
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
}
} else if (d->bd_flags & BD_F_ESS) {
u_char c ;
if (d->play_fmt == 0) {
/* initialize for record */
static u_char cmd[] = {
0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc
};
ess_write(d->io_base, 0xb8, 0x0e);
c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ;
if (d->flags & SND_F_STEREO)
c++ ;
ess_write(d->io_base, 0xa8, c);
ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */
/*
* set format in b6, b7
*/
} else {
/* initialize for play */
static u_char cmd[] = {
0x80,0x51,0xd0,0x00,0x71,0xf4,
0x80,0x51,0x98,0x00,0x71,0xbc
};
}
}
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
if (d->bd_flags & BD_F_SB16X) {
/* just a guess: on the Vibra16X, the first
* op started takes the first dma channel,
* the second one takes the next...
* The default is to be ready for play.
*/
int swap = 0 ;
DEB(printf("start %s -- now dma %d:%d\n",
rd ? "rd" : "wr",
d->dbuf_out.chan, d->dbuf_in.chan););
/* swap only if both channels are idle
* play: dl=0, since there is no pause;
* rec: rl=0
*/
if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) {
/* must swap channels, but also save dl */
int c = d->dbuf_in.chan ;
int dl = d->dbuf_in.dl ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
d->dbuf_in.dl = dl ;
printf("swapped -- now dma %d:%d\n",
d->dbuf_out.chan, d->dbuf_in.chan);
}
}
/*
* XXX note: c1 and l should be set basing on d->rec_fmt,
* but there is no choice once a 16 or 8-bit channel
* is assigned. This means that if the application
* tries to use a bad format, the sound will not be nice.
*/
if ( b->chan > 4 ) {
if ( b->chan > 4
|| (rd && d->rec_fmt == AFMT_S16_LE)
|| (!rd && d->play_fmt == AFMT_S16_LE)
) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
l /= 2 ;
@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_STOP :
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
if ( b->chan > 4
|| (rd && d->rec_fmt == AFMT_S16_LE)
|| (!rd && d->play_fmt == AFMT_S16_LE)
)
cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
if (d->bd_flags & BD_F_SB16X) {
/* restore possible swapped channels.
* The default is to be ready for play.
* XXX right now, it kills all input on overflow
*/
if ( rd && d->dbuf_out.dl == 0 ) {
/* must swap channels ? */
int c = d->dbuf_in.chan ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
printf("restored -- now dma %d:%d\n",
d->dbuf_out.chan, d->dbuf_in.chan);
}
}
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
printf("ESS1868 (rev %d)\n", rev);
else
printf("ESS688 (rev %d)\n", rev);
/* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */
break ; /* XXX */
} else {
printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d)
/*
* Common code for the midi and pcm functions
*
* sb_cmd write a single byte to the CMD port.
* sb_cmd2 write a CMD + 1 byte arg
* sb_cmd3 write a CMD + 2 byte arg
* sb_get_byte returns a single byte from the DSP data port
*
* ess_write is actually sb_cmd2
* ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
*/
int
@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
val = inb(io_base + 5);
val = inb(io_base + SB_MIX_DATA);
DELAY(10);
splx(flags);
@ -747,6 +874,19 @@ sb_get_byte(int io_base)
return 0xffff;
}
int
ess_write(int io_base, u_char reg, int val)
{
return sb_cmd2(io_base, reg, val);
}
int
ess_read(int io_base, u_char reg)
{
if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) )
return 0xffff ;
return sb_get_byte(io_base);
}
/*
@ -785,17 +925,21 @@ dsp_speed(snddev_info *d)
*/
if (d->bd_flags & BD_F_ESS) {
int t;
RANGE (speed, 4000, 48000);
RANGE (speed, 5000, 49000);
if (speed > 22000) {
t = (795500 + speed / 2) / speed;
speed = (795500 + t / 2) / t ;
t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ;
t = (256 - t ) | 0x80 ;
} else {
t = (397700 + speed / 2) / speed;
speed = (397700 + t / 2) / t ;
t = 128 - (397700 + speed / 2) / speed ;
t = 128 - t ;
}
sb_cmd2(d->io_base, 0xa1, t); /* set time constant */
ess_write(d->io_base, 0xa1, t); /* set time constant */
d->play_speed = d->rec_speed = speed ;
speed = (speed * 9 ) / 20 ;
t = 256-7160000/(speed*82);
ess_write(d->io_base,0xa2,t);
return speed ;
}
@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static char *opti925_probe(u_long csn, u_long vend_id);
static void opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device opti925 = {
"opti925",
opti925_probe,
opti925_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, opti925);
static char *
opti925_probe(u_long csn, u_long vend_id)
{
if (vend_id == 0x2509143e) {
struct pnp_cinfo d ;
read_pnp_parms ( &d , 1 ) ;
if (d.enable == 0) {
printf("This is an OPTi925, but LDN 1 is disabled\n");
return NULL;
}
return "OPTi925" ;
}
return NULL ;
}
static void
opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
int the_irq = 0 ;
tmp_d = sb_op_desc;
snddev_last_probed = &tmp_d;
read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
the_irq = d.irq[0];
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 3 );
read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 2 );
read_pnp_parms ( &d , 1 ) ;
d.irq[0] = the_irq ;
dev->id_iobase = d.port[0];
write_pnp_parms ( &d , 1 );
enable_pnp_card();
tmp_d.conf_base = d.port[3];
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
pcmattach(dev);
}
/*
* A driver for some SB16pnp and compatibles...
*
* Avance Asound 100 -- 0x01009305
* Avance Logic ALS100+ -- 0x10019305
* xxx -- 0x2b008c0e
*
*/
@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id)
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
* Vibra16X: 0xf0008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
if (vend_id == 0xf0008c0e)
s = "Vibra16X" ;
else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
else if (vend_id == 0x10019305)
s = "Avance Logic 100+" ; /* Vibra16X-class */
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
pcm_info[dev->id_unit] = tmp_d;
pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) {
/*
* XXX please add here the vend_id for other vibra16X cards...
* And remember, must change tmp_d, not
*/
tmp_d.bd_flags |= BD_F_SB16X ;
}
pcmattach(dev);
}
#endif /* NPNP */

@ -39,12 +39,20 @@ CS4232: PnP id 0x3242630e
and working in playback mode in MSS emulation.
Also have reports from some user that it works ok.
OPTi931: PnP id 0x3109143e
--------------------------
CHIPSET:
OPTi931: PnP id 0x3109143e
http://www.opti.com/ opti931_21.pdf
MANUFACTURER:
ExpertColor MED931 (europe)
DATA SHEETS:
http://www.opti.com/ opti931_21.pdf
PNP CONFIG:
pnp 1 1 os enable port0 0x534 port2 0x220 port3 0xe0d irq0 10 drq0 1 drq1 6
COMMENTS:
The data sheets of this chip are very cryptic and do not match
what the cards I have seem to do. I have it working
in WSS emulation, in full duplex and all modes.
@ -61,21 +69,45 @@ OPTi931: PnP id 0x3109143e
does a conversion in software (much like the SBPro). Of course
you lose 4-5 bits of dynamic range in the process.
- in full duplex (and single DMA mode), the card occasionally
misses interrupts, or generates spurious ones. Spurious ints are
not problematic since they can be ignored, but missed ones are
as you can imagine... This is fixed by auto-dma mode.
misses interrupts, or generates spurious ones, or generates
interrupts before setting the status bits in the registers.
Spurious ints are not problematic since they can be easily
ignored, but missing interrupts are a nightmare to handle...
The only way to have this card work semi-reliably is to use
auto-dma mode (which is the default mode of operation in the
driver).
In any case, these cards are very cheap and widely available and
are a reasonable solution if you cannot find some other decent WSS
device.
SB16 PnP: PnP id 0x__008c0e
-------------------------------
CHIPSET:
SB16: PnP id 0x__008c0e
Vibra16X: PnP id 0xf0008c0e
http://www.creative.com sbhwpg.pdf or SBHWPG.EXE
MANUFACTURER:
CreativeLabs
DATA SHEETS:
http://www.creative.com sbhwpg.pdf or SBHWPG.EXE
PNP CONFIG:
pnp 1 0 os enable port0 0x220 irq0 5 drq0 1 drq1 5
There are many such cards (plain SB16 PnP, AWE32, AWE64, Vibra16,
etc.) all differing in the PnP id. They have different synthesis
devices, which we do not support, so we are not affected by these
differences. Don't worry if the driver identifies the card as a
different SB16 than the one you have.
COMMENTS:
SB16 really refers to a large number of different cards, starting
from the original, non-PnP SB16, to more modern cards (SB16PnP,
Vibra16C) and other including Wavetable Synthesis (AWE32, AWE64,
etc.). All these cards have different PnP ID. They have
different synthesis devices, which we do not support, so we
are not affected by these differences. Don't worry if the driver
identifies the card as a different SB16 than the one you have.
BEWARE -- all recent (1998 ?) SB16 use a new codec, Vibra16X,
which is sufficiently different from the old one to not work with
this driver in capture mode or in full duplex. Documentation is
not available from Creative. I suggest to stay away from these
cards (and from SB16 in general).
Full duplex support of this card is tricky since one channel can
work in 16-bit and the other in 8-bit mode. You will need to use
@ -84,7 +116,7 @@ SB16 PnP: PnP id 0x__008c0e
is much lower than in 16-bit.
Full duplex operation is unsupported by Creative. It seems to
work, although on my Vibra16 the command to stop DMA transfer
work, although on my Vibra16C the command to stop DMA transfer
seems to erroneously affect both channels instead of the one
they are issued for. The driver has a workaround, but I cannot
guarantee that it works for everybody. I have had several
@ -93,67 +125,111 @@ SB16 PnP: PnP id 0x__008c0e
Some docs on how to use this card with the voxware driver
are at http://multiverse.com/~rhh/awedrv
NOTE: 980122 I have had a couple of reports from people using
the Vibra16X (and clones, e.g. based on the Avance Logic
chipsets) which appears to use two 8-bit DMA channels. It
might be possible that new SB16 are able to do true full duplex!
The driver _does not_ support these boards although the fix
should be relatively easy.
--------------------------
CHIPSET:
ALS100, ALS110, ...
Avance Asound 100: PnP id 0x01009305
ASound:
Avance Logic makes SB clones, and apparently documentation
is available at
MANUFACTURER:
Realtek (also Avance Asound and possibly other names)
http://www.realtek.com.tw/cp/cp.html
DOCUMENTATION:
http://www.realtek.com.tw/cp/cp.html
but not very useful or detailed.
these card should be recognised as SB16 clones.
COMMENTS:
These card should be recognised as SB16 clones. Some of them
emulate the Vibra16X, so the comments above apply.
Yamaha SA2/SA3
http://www.yamaha.com ? YM711.pdf
(this is a huge file containing a few pages scanned and converted
to pdf. Not very useful or detailed.)
------------------------------
CHIPSET:
Yamaha SA2/SA3 . Both PnP and non-PnP versions.
OPL3-SA2 Sound Chip: PnP id 0x2000a865
OPL/SA3 : PnP id 0x3000a865
pnp 1 0 os enable port0 0x220 port1 0x530 irq0 5 drq0 1 drq1 5
MANUFACTURER:
no-name cards, and also used in various laptops, e.g. Toshiba
Libretto and others. I
DATA SHEETS:
http://www.yamaha.com ? YM711.pdf
This is a huge file containing a few pages scanned and converted
to pdf. Not very detailed. Luckily, this chipset appears to do a
good (i.e. bug-free) emulation of the WSS, so it is fully
supported by the driver.
pnp 1 0 os enable port0 0x220 port1 0x530 port2 0x388 port3 0x370 irq0 5 drq0 1 drq1 0
this card emulates a WSS or SB. Have reports that it works, although
it has incomplete mixer support (Yamaha defines an additional set
of registers to control some mixer functions, such as the master
output volume). Currently we set the master volume to 100%.
Driver reported to work also on Toshiba DynaBook Portege 300CT
with OPL3SA-3(non-PNP)
output volume -- this is at 0x370 or 0x310). Currently we set
the master volume to 100% for the PnP card. Will put in code to do the
same for the non PnP card as soon as I find out how to tell
the Yamaha from other cards.
Driver reported to work also on
Toshiba DynaBook Portege 300CT with OPL3SA-3(non-PNP), and on the
Libretto50CT (has the non-PnP device).
Ensoniq Soundscape VIVO
------------------------
CHIPSET:
ENSONIQ SoundscapeVIVO ENS4081: PnP id 0x8140d315
MANUFACTURER:
Ensoniq Soundscape VIVO
PNP CONFIG:
pnp 1 0 os enable port0 0x220 port1 0x530 irq0 5 drq0 1 drq1 5
COMMENTS:
this card emulates a WSS or SB. Have reports that it works.
GusPnP: PnP id 0x0100561e
-------------------------
CHIPSET:
AMD...
MANUFACTURER:
GusPnP: PnP id 0x0100561e
PNP CONFIG:
pnp 1 0 os enable port0 0x220 port1 0x320 port2 0x32c irq0 5 drq0 7 drq1 5
COMMENTS:
It works in U8 and S16 mode, ulaw support still not working.
The Gus has been discontinued, so support for this card is only
there for historical reasons (the Gus used to be the only card
well supported in full duplex mode).
NOT WORKING YET:
---- THE FOLLOWING CARDS ARE NOT FULLY SUPPORTED: ----
OPTI925: PnP id 0x2509143e
CHIPSET:
OPTI925: PnP id 0x2509143e
COMMENTS:
there is code to recognize it as a SB clone. I have reports that
it probes ok, but not sure if it works.
OPTI924: PnP
-------------------------
CHIPSET:
OPTI924: PnP
COMMENT:
I have this card but it is still unsupported.
OPTI930:
-------------------------
CHIPSET:
OPTI930:
should work as an MSS clone, but support for it is not implemented
yet.
ESS1868
ESS688
CHIPSET:
ESS1868
ESS688
http://www.esstech.com
@ -168,12 +244,13 @@ ESS688
(the ESS688 is used on many notebooks. It is probably able to do 8
and 16-bit, half duplex).
PCI cards:
-------------------------
CHIPSET:
various PCI cards from Ensoniq, OPTI, CreativeLabs.
some vendors have PCI cards. This code _cannot_ work on these
cards as it is now, since they cannot obviously use the ISA DMA
controller. As there are no data sheets available for these PCI
cards, none of them is supported at the moment, although support
should be easy as soon as I can put my hands on the data sheets
and cards.
This code _cannot_ work on these cards as it is now, since I
don't think they can use the ISA DMA controller. As there are
no data sheets available for these PCI cards, none of them is
supported at the moment.
--------------------------------------------------------------------

@ -13,15 +13,18 @@ of the Voxware ioctl() audio calls, so that many applications --
even commercial ones -- will run unmodified with this driver. On
the other hand, at the moment this driver does not support /dev/midi
and /dev/synth, or some ioctl() used in xquake. Do not expect
/dev/synth to be supported before summer'98.
/dev/synth to be supported anytime soon.
I also have implemented a new software interface with an independent
set of ioctl(), to support some functions which were not easy to
express with the existing software interface (e.g. full duplex on
the SB16). To make an effective use of the new functionalities you
need to recompile applications by replacing the audio module(s).
old SB16 cards). To make an effective use of the new functionalities
you need to recompile applications by replacing the audio module(s).
Such modified driver modules are present in the misc/ directory
for several applications.
Updated versions of this code will available at the following URL:
This file gives quick information on how to install the driver.
Updated versions of this code will be available at the following URL:
http://www.iet.unipi.it/~luigi/FreeBSD.html
@ -49,10 +52,18 @@ APPLICATIONS:
- xanim
- various mpeg players (mpg123, amp, ...);
WITH SPECIAL DRIVER MODULE (supplied)
- speak_freely, full duplex (requires removing the definition of
HALF_DUPLEX in the Makefile);
- the realaudio player (3.0, dynamically linked);
- vat, full duplex (the version in ports is already modified);
- timidity, a software midi-to-pcm converter;
NOT WORKING
- xquake (we do not support mmapped buffers yet);
---INSTALLATION---
---INSTALLATION---
* add the following lines to your kernel configuration file:
@ -108,14 +119,14 @@ APPLICATIONS:
"flags 0x13" specifies that you have a dual dma board with
channel 3 as secondary DMA channel.
* build the kernel using the following steps
* build the kernel using the standard steps
config MYKERNEL
cd /sys/compile/MYKERNEL
make depend
make
* PnP support:
* note for PnP cards:
For PnP cards, only the line for "pcm0" is needed (the code
will allocate entries for more cards if found), but IT MUST
@ -145,8 +156,8 @@ APPLICATIONS:
WHAT IF THIS DRIVER DOES NOT WORK:
If the driver does not work with your hardware, I am willing to
help but I need the following:
If the driver does not work with your hardware, I might to help
but I need the following information:
- relevant lines in your config file;
- dmesg output
@ -200,8 +211,8 @@ this driver. In particular I would like to thank:
* Jim Lowe for his suggestion on the block-mode select;
* Allison Mankin and Brad Karp at ISI-East for supplying a GUS PnP
which allowed me to support this card;
* Eric J. Schwertfeger for donating sn ES1868 card for writing the
driver.
* Eric J. Schwertfeger for donating an ES1868 card for writing the
driver (which i haven't done yet...).
* and many people who had the patience to try the driver
on their cards and report success/fauilure and useful
information.

@ -4,7 +4,7 @@
* Driver for Microsoft Sound System/Windows Sound System (mss)
* -compatible boards. This includes:
*
* AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others.
* AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others.
*
* Copyright Luigi Rizzo, 1997,1998
* Copyright by Hannu Savolainen 1994, 1995
@ -239,8 +239,32 @@ mss_attach(struct isa_device *dev)
outb(dev->id_iobase, bits );
}
}
if (1) { /* machine-specific code for the Toshiba Libretto */
u_char r6, r9;
outb( 0x370, 6 /* dma config */ );
outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */);
r6 = inb( 0x371 /* read */ );
outb( 0x370, 0xa /* version */ );
r9 = inb( 0x371 /* read */ );
DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);)
/*
* yamaha - set volume to max
*/
outb( 0x370, 7 /* volume left */ );
outb( 0x371, 0 /* max level */ );
outb( 0x370, 8 /* volume right */ );
outb( 0x371, 0 /* max level */ );
}
if ( FULL_DUPLEX(d) )
d->audio_fmt |= AFMT_FULLDUPLEX ;
if (d->bd_id == MD_YM0020) {
DDB(printf("setting up yamaha registers\n"));
outb(0x370, 6 /* dma config */ ) ;
if (FULL_DUPLEX(d))
outb(0x371, 0xa9 ); /* use both dma chans */
else
outb(0x371, 0x8b ); /* use low dma chan */
}
mss_reinit(d);
ad1848_mixer_reset(d);
return 0;
@ -401,7 +425,7 @@ mss_callback(snddev_info *d, int reason)
/*
* perform all necessary initializations for i/o
*/
d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
d->rec_fmt = d->play_fmt ; /* no split format on the MSS */
snd_set_blocksize(d);
mss_reinit(d);
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
@ -561,15 +585,21 @@ again:
if (mc11 & masked)
printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked);
masked |= mc11 ;
/*
* the nice OPTi931 sets the IRQ line before setting the bits in
* mc11. So, on some occasions I have to retry (max 10 times).
*/
if ( mc11 == 0 ) { /* perhaps can return ... */
reason = inb(io_Status(d));
if (reason & 1) {
printf("one more try...\n");
goto again;
DEB(printf("one more try...\n");)
if (--loops)
goto again;
else
DDB(printf("opti_intr: irq but mc11 not set!...\n");)
}
if (loops==10) {
if (loops==10)
printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
}
return;
}
@ -580,7 +610,8 @@ again:
dsp_wrintr(d);
}
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
if (--loops)
goto again;
DEB(printf("xxx too many loops\n");)
}
@ -657,7 +688,7 @@ ad_read(snddev_info *d, int reg)
int x;
flags = spltty();
AD_WAIT_INIT(d, 100);
AD_WAIT_INIT(d, 201);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
x = inb(io_Indexed_Data(d));
@ -672,7 +703,7 @@ ad_write(snddev_info *d, int reg, u_char data)
int x ;
flags = spltty();
AD_WAIT_INIT(d, 100);
AD_WAIT_INIT(d, 1002);
x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
outb(io_Indexed_Data(d), data);
@ -731,7 +762,7 @@ ad_enter_MCE(snddev_info *d)
int prev;
d->bd_flags |= BD_F_MCE_BIT;
AD_WAIT_INIT(d, 100);
AD_WAIT_INIT(d, 203);
prev = inb(io_Index_Addr(d));
prev &= ~IA_TRD ;
outb(io_Index_Addr(d), prev | IA_MCE ) ;
@ -853,10 +884,11 @@ mss_mixer_set(snddev_info *d, int dev, int value)
d->mix_levels[dev] = left | (right << 8);
#if 0
/* Scale volumes */
left = mix_cvt[left];
right = mix_cvt[right];
#endif
/*
* Set the left channel
*/
@ -919,6 +951,16 @@ ad1848_mixer_reset(snddev_info *d)
ad_write(d, 20, 0x88);
ad_write(d, 21, 0x88);
break;
case MD_YM0020:
/* set master volume to max */
DDB(printf("set yamaha master volume to max"); )
outb(0x370, 7) ;
outb(0x371, 0) ;
outb(0x370, 8) ;
outb(0x371, 0) ;
break;
case MD_GUSPNP:
/* this is only necessary in mode 3 ... */
ad_write(d, 22, 0x88);
@ -997,7 +1039,7 @@ mss_format(snddev_info *d)
arg = AFMT_U8 ;
/* ulaw/alaw seems broken on the opti931... */
if (d->bd_id == MD_OPTI931) {
if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) {
if (arg == AFMT_MU_LAW) {
arg = AFMT_U8 ;
d->flags |= SND_F_XLAT8 ;
@ -1233,6 +1275,13 @@ mss_detect(struct isa_device *dev)
d->bd_id = MD_AD1845;
}
ad_write(d, 23, tmp); /* Restore */
DDB(printf("... try to identify the yamaha\n") ;)
tmp = inb(0x370) ;
outb(0x370, 6 /* dma config */ ) ;
if (inb(0x370) != 6 ) /* not a yamaha... restore. */
outb(0x370, tmp ) ;
else
d->bd_id = MD_YM0020 ;
break;
case 0x83: /* CS4236 */
@ -1244,7 +1293,6 @@ mss_detect(struct isa_device *dev)
default: /* Assume CS4231 */
BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);)
d->bd_id = MD_CS4231;
}
}
ad_write(d, 25, tmp1); /* Restore bits */
@ -1354,7 +1402,7 @@ cs423x_probe(u_long csn, u_long vend_id)
u_long id = vend_id & 0xff00ffff;
if ( id == 0x3700630e )
s = "CS4237" ;
else if ( id == 0x3600630e )
else if ( id == 0x3500630e || id == 0x3600630e )
s = "CS4236" ;
else if ( id == 0x3500630e )
s = "CS4236B" ;
@ -1364,7 +1412,7 @@ cs423x_probe(u_long csn, u_long vend_id)
s = "Yamaha SA2";
else if ( id == 0x3000a865)
s = "Yamaha SA3";
else if ( id == 0x0000a865)
else if (vend_id == 0x0000a865)
s = "Yamaha YMF719 OPL-SA3";
else if (vend_id == 0x8140d315)
s = "SoundscapeVIVO";
@ -1399,7 +1447,8 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
if (vend_id == 0x0008a865 || vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) {
if (vend_id==0x2000a865 || vend_id==0x3000a865 ||
vend_id==0x0008a865 || vend_id==0x8140d315) {
/* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */
dev->id_iobase = d.port[0] ;
tmp_d.alt_base = d.port[1] ;
@ -1418,7 +1467,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
case 0x2000a865: /* Yamaha SA2 */
case 0x3000a865: /* Yamaha SA3 */
case 0x0000a865: /* Yamaha YMF719 */
case 0x0000a865: /* Yamaha TMF719 SA3 */
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
tmp_d.conf_base = d.port[4];
@ -1436,6 +1485,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
tmp_d.bd_id = MD_CS4237 ;
break;
case 0x3500630e: /* CS4236 */
case 0x3600630e: /* CS4236 */
case 0x3500630e: /* CS4236B */
tmp_d.bd_id = MD_CS4236 ;
@ -1451,7 +1501,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
write_pnp_parms( &d, ldn );
enable_pnp_card();
if ( (vend_id & 0x2000ffff) == 0x2000a865 ) {
if ( (vend_id & 0x0000ffff) == 0x0000a865 ) {
/* special volume setting for the Yamaha... */
outb(tmp_d.conf_base, 7 /* volume, left */);
outb(tmp_d.conf_base+1, 0 );
@ -1542,10 +1592,10 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
/*
* the 931 is not a real SB, it has important pieces of
* hardware controlled by both the WSS and the SB port...
* hardware controlled by both the MSS and the SB port...
*/
printf("--- opti931 in sb mode ---\n");
opti_write(p, 6, 1); /* MCIR6 wss disable, sb enable */
opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */
/*
* swap the main and alternate iobase address since we want
* to work in sb mode.
@ -1555,7 +1605,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
} else { /* mss-compatible codec */
tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */
opti_write(p, 6 , 2); /* MCIR6: wss enable, sb disable */
opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */
opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */
@ -1567,6 +1617,76 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static char *opti925_probe(u_long csn, u_long vend_id);
static void opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device opti925 = {
"opti925",
opti925_probe,
opti925_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, opti925);
static char *
opti925_probe(u_long csn, u_long vend_id)
{
if (vend_id == 0x2509143e) {
struct pnp_cinfo d ;
read_pnp_parms ( &d , 1 ) ;
if (d.enable == 0) {
printf("This is an OPTi925, but LDN 1 is disabled\n");
return NULL;
}
return "OPTi925" ;
}
return NULL ;
}
static void
opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
int the_irq = 0 ;
tmp_d = mss_op_desc;
snddev_last_probed = &tmp_d;
read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
the_irq = d.irq[0];
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 3 );
read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 2 );
read_pnp_parms ( &d , 1 ) ;
d.irq[0] = the_irq ;
dev->id_iobase = d.port[1];
tmp_d.alt_base = d.port[0];
write_pnp_parms ( &d , 1 );
enable_pnp_card();
tmp_d.conf_base = d.port[3];
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
pcmattach(dev);
}
static void gus_mem_cfg(snddev_info *tmp);
static char *guspnp_probe(u_long csn, u_long vend_id);

@ -184,7 +184,9 @@ dsp_wrintr(snddev_info *d)
/* for any reason, size has changed. Stop and restart */
DEB(printf("wrintr: bsz change from %d to %d, rp %d rl %d\n",
b->dl, l, b->rp, b->rl));
d->callback(d, SND_CB_WR | SND_CB_STOP );
DEB(printf("wrintr: dl %d -> %d\n", b->dl, l);)
if (b->dl != 0)
d->callback(d, SND_CB_WR | SND_CB_STOP );
/*
* at high speed, it might well be that the count
* changes in the meantime. So we try to update b->rl
@ -270,10 +272,10 @@ dsp_write_body(snddev_info *d, struct uio *buf)
else
timeout = 1 ;
ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout);
if (ret == EINTR || ret == ERESTART)
if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s);
if (ret == EINTR)
if (ret == EINTR || ret == ERESTART)
break ;
continue;
}
@ -457,10 +459,12 @@ dsp_rdintr(snddev_info *d)
( FULL_DUPLEX(d) || (d->flags & SND_F_WRITING) == 0 ) ) {
int l = min(b->fl - 0x100, d->rec_blocksize);
l &= DMA_ALIGN_MASK ; /* realign sizes */
DEB(printf("rdintr: dl %d -> %d\n", b->dl, l);)
if (l != b->dl) {
/* for any reason, size has changed. Stop and restart */
if (b->dl > 0 )
d->callback(d, SND_CB_RD | SND_CB_STOP );
b->dl = l ;
d->callback(d, SND_CB_RD | SND_CB_STOP );
d->callback(d, SND_CB_RD | SND_CB_START );
}
} else {
@ -553,10 +557,10 @@ dsp_read_body(snddev_info *d, struct uio *buf)
else
timeout = 1; /* maybe data will be ready earlier */
ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ;
if (ret == EINTR || ret == ERESTART)
if (ret == EINTR)
d->flags |= SND_F_ABORTING ;
splx(s);
if (ret == EINTR)
if (ret == EINTR || ret == ERESTART)
break ;
continue;
}
@ -593,7 +597,7 @@ dsp_read_body(snddev_info *d, struct uio *buf)
s = spltty(); /* no interrupts here ... */
d->flags &= ~SND_F_READING ;
if (d->flags & SND_F_ABORTING) {
d->flags |= ~SND_F_ABORTING;
d->flags &= ~SND_F_ABORTING; /* XXX */
splx(s);
dsp_rdabort(d, 1 /* restart */);
/* XXX return EINTR ? */
@ -761,7 +765,8 @@ snd_flush(snddev_info *d)
return -1 ;
}
if ( ret && --count == 0) {
printf("timeout flushing dbuf_out.chan, cnt 0x%x flags 0x%08lx\n",
printf("timeout flushing dbuf_out, chan %d cnt 0x%x flags 0x%08lx\n",
b->chan,
b->rl, d->flags);
break;
}

@ -150,7 +150,7 @@ ahead.
/*
* Table of mixer registers. There is a default table for the
* AD1848/CS423x clones, and one for the OPTI931. As more WSS
* AD1848/CS423x clones, and one for the OPTI931. As more MSS
* clones come out, there ought to be more tables.
*
* Fields in the table are : polarity, register, offset, bits

@ -3,7 +3,7 @@
*
* driver for the SoundBlaster and clones.
*
* Copyright 1997 Luigi Rizzo.
* Copyright 1997,1998 Luigi Rizzo.
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
@ -74,6 +74,8 @@ 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
@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev)
* 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 int
sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
{
@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
d->play_fmt = d->rec_fmt = AFMT_U8 ;
break ;
}
if ( (flags & FREAD) == 0)
/*
* 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 ;
if ( (flags & FWRITE) == 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 ;
@ -275,7 +294,10 @@ sb_intr(int unit)
* 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);
@ -284,25 +306,26 @@ again:
*/
reason = 0 ;
if ( c & 1 ) { /* 8-bit dma */
if (d->dbuf_out.chan < 4)
if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW )
reason |= 1;
if (d->dbuf_in.chan < 4)
if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW )
reason |= 2;
}
if ( c & 2 ) { /* 16-bit dma */
if (d->dbuf_out.chan >= 4)
if (d->play_fmt == AFMT_S16_LE)
reason |= 1;
if (d->dbuf_in.chan >= 4)
if (d->rec_fmt == AFMT_S16_LE)
reason |= 2;
}
}
/* XXX previous location of ack... */
DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason));
DEB(printf("sb_intr, 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);
else {
if (d->bd_flags & BD_F_SB16)
if (PLAIN_SB16(d->bd_flags))
printf("WARNING: wrintr but write DMA inactive!\n");
}
}
@ -310,7 +333,7 @@ again:
if ( d->dbuf_in.dl )
dsp_rdintr(d);
else {
if (d->bd_flags & BD_F_SB16)
if (PLAIN_SB16(d->bd_flags))
printf("WARNING: rdintr but read DMA inactive!\n");
}
}
@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason)
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 ;
if (d->bd_flags & BD_F_SB16) {
/*
* 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 SB16 can do full duplex using one 16-bit channel
/* 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.
* We use the following algorithm:
* 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.
@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason)
if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
if ( d->rec_fmt ) {
/* check for possible config errors. */
/* 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"));
}
@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason)
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
}
} else if (d->bd_flags & BD_F_ESS) {
u_char c ;
if (d->play_fmt == 0) {
/* initialize for record */
static u_char cmd[] = {
0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc
};
ess_write(d->io_base, 0xb8, 0x0e);
c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ;
if (d->flags & SND_F_STEREO)
c++ ;
ess_write(d->io_base, 0xa8, c);
ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */
/*
* set format in b6, b7
*/
} else {
/* initialize for play */
static u_char cmd[] = {
0x80,0x51,0xd0,0x00,0x71,0xf4,
0x80,0x51,0x98,0x00,0x71,0xbc
};
}
}
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
if (d->bd_flags & BD_F_SB16X) {
/* just a guess: on the Vibra16X, the first
* op started takes the first dma channel,
* the second one takes the next...
* The default is to be ready for play.
*/
int swap = 0 ;
DEB(printf("start %s -- now dma %d:%d\n",
rd ? "rd" : "wr",
d->dbuf_out.chan, d->dbuf_in.chan););
/* swap only if both channels are idle
* play: dl=0, since there is no pause;
* rec: rl=0
*/
if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) {
/* must swap channels, but also save dl */
int c = d->dbuf_in.chan ;
int dl = d->dbuf_in.dl ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
d->dbuf_in.dl = dl ;
printf("swapped -- now dma %d:%d\n",
d->dbuf_out.chan, d->dbuf_in.chan);
}
}
/*
* XXX note: c1 and l should be set basing on d->rec_fmt,
* but there is no choice once a 16 or 8-bit channel
* is assigned. This means that if the application
* tries to use a bad format, the sound will not be nice.
*/
if ( b->chan > 4 ) {
if ( b->chan > 4
|| (rd && d->rec_fmt == AFMT_S16_LE)
|| (!rd && d->play_fmt == AFMT_S16_LE)
) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
l /= 2 ;
@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_STOP :
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
if ( b->chan > 4
|| (rd && d->rec_fmt == AFMT_S16_LE)
|| (!rd && d->play_fmt == AFMT_S16_LE)
)
cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason)
sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ?
0xd6 : 0xd4); /* continue other dma */
}
if (d->bd_flags & BD_F_SB16X) {
/* restore possible swapped channels.
* The default is to be ready for play.
* XXX right now, it kills all input on overflow
*/
if ( rd && d->dbuf_out.dl == 0 ) {
/* must swap channels ? */
int c = d->dbuf_in.chan ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
printf("restored -- now dma %d:%d\n",
d->dbuf_out.chan, d->dbuf_in.chan);
}
}
}
DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */
break ;
@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
printf("ESS1868 (rev %d)\n", rev);
else
printf("ESS688 (rev %d)\n", rev);
/* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */
break ; /* XXX */
} else {
printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d)
/*
* Common code for the midi and pcm functions
*
* sb_cmd write a single byte to the CMD port.
* sb_cmd2 write a CMD + 1 byte arg
* sb_cmd3 write a CMD + 2 byte arg
* sb_get_byte returns a single byte from the DSP data port
*
* ess_write is actually sb_cmd2
* ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
*/
int
@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
val = inb(io_base + 5);
val = inb(io_base + SB_MIX_DATA);
DELAY(10);
splx(flags);
@ -747,6 +874,19 @@ sb_get_byte(int io_base)
return 0xffff;
}
int
ess_write(int io_base, u_char reg, int val)
{
return sb_cmd2(io_base, reg, val);
}
int
ess_read(int io_base, u_char reg)
{
if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) )
return 0xffff ;
return sb_get_byte(io_base);
}
/*
@ -785,17 +925,21 @@ dsp_speed(snddev_info *d)
*/
if (d->bd_flags & BD_F_ESS) {
int t;
RANGE (speed, 4000, 48000);
RANGE (speed, 5000, 49000);
if (speed > 22000) {
t = (795500 + speed / 2) / speed;
speed = (795500 + t / 2) / t ;
t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ;
t = (256 - t ) | 0x80 ;
} else {
t = (397700 + speed / 2) / speed;
speed = (397700 + t / 2) / t ;
t = 128 - (397700 + speed / 2) / speed ;
t = 128 - t ;
}
sb_cmd2(d->io_base, 0xa1, t); /* set time constant */
ess_write(d->io_base, 0xa1, t); /* set time constant */
d->play_speed = d->rec_speed = speed ;
speed = (speed * 9 ) / 20 ;
t = 256-7160000/(speed*82);
ess_write(d->io_base,0xa2,t);
return speed ;
}
@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name,
pcmattach(dev);
}
static char *opti925_probe(u_long csn, u_long vend_id);
static void opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev);
static struct pnp_device opti925 = {
"opti925",
opti925_probe,
opti925_attach,
&nsnd, /* use this for all sound cards */
&tty_imask /* imask */
};
DATA_SET (pnpdevice_set, opti925);
static char *
opti925_probe(u_long csn, u_long vend_id)
{
if (vend_id == 0x2509143e) {
struct pnp_cinfo d ;
read_pnp_parms ( &d , 1 ) ;
if (d.enable == 0) {
printf("This is an OPTi925, but LDN 1 is disabled\n");
return NULL;
}
return "OPTi925" ;
}
return NULL ;
}
static void
opti925_attach(u_long csn, u_long vend_id, char *name,
struct isa_device *dev)
{
struct pnp_cinfo d ;
snddev_info tmp_d ; /* patched copy of the basic snddev_info */
int the_irq = 0 ;
tmp_d = sb_op_desc;
snddev_last_probed = &tmp_d;
read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
the_irq = d.irq[0];
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 3 );
read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
d.port[0] = 0 ;
d.enable = 0 ;
write_pnp_parms ( &d , 2 );
read_pnp_parms ( &d , 1 ) ;
d.irq[0] = the_irq ;
dev->id_iobase = d.port[0];
write_pnp_parms ( &d , 1 );
enable_pnp_card();
tmp_d.conf_base = d.port[3];
dev->id_drq = d.drq[0] ; /* primary dma */
dev->id_irq = (1 << d.irq[0] ) ;
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
pcmattach(dev);
}
/*
* A driver for some SB16pnp and compatibles...
*
* Avance Asound 100 -- 0x01009305
* Avance Logic ALS100+ -- 0x10019305
* xxx -- 0x2b008c0e
*
*/
@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id)
* Reported values are:
* SB16 Value PnP: 0x2b008c0e
* SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
* Vibra16X: 0xf0008c0e
*/
if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
if (vend_id == 0xf0008c0e)
s = "Vibra16X" ;
else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
s = "SB16 PnP";
else if (vend_id == 0x01009305)
s = "Avance Asound 100" ;
else if (vend_id == 0x10019305)
s = "Avance Logic 100+" ; /* Vibra16X-class */
if (s) {
struct pnp_cinfo d;
read_pnp_parms(&d, 0);
@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
pcm_info[dev->id_unit] = tmp_d;
pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */
snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) {
/*
* XXX please add here the vend_id for other vibra16X cards...
* And remember, must change tmp_d, not
*/
tmp_d.bd_flags |= BD_F_SB16X ;
}
pcmattach(dev);
}
#endif /* NPNP */

@ -19,9 +19,9 @@ extern int sbc_major, sbc_minor ;
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
#define SB_MIX_ADDR 0x4
#define SB_MIX_DATA 0x5
#if 0
#define MIXER_ADDR (io_base + 0x4)
#define MIXER_DATA (io_base + 0x5)
#define OPL3_LEFT (io_base + 0x0)
#define OPL3_RIGHT (io_base + 0x2)
#define OPL3_BOTH (io_base + 0x8)
@ -138,9 +138,14 @@ extern int sbc_major, sbc_minor ;
#define BD_F_MIX_CT1745 0x0030 /* CT1745 */
#define BD_F_SB16 0x0100 /* this is a SB16 */
#define BD_F_NOREC 0x0200 /* recording not supported on this board */
#define BD_F_SB16X 0x0200 /* this is a vibra16X or clone */
#define BD_F_MIDIBUSY 0x0400 /* midi busy */
#define BD_F_ESS 0x0800 /* this is an ESS chip */
/*
* on some SB16 cards, at times I swap DMA channels. Remember this
* so that they can be restored later.
*/
#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */
/*

@ -57,10 +57,10 @@
#include <sys/devfsext.h>
#endif /* DEVFS */
#if NPCM > 0 /* from "pcm.h" via disgusting #include in snd/sound.h */
extern struct isa_driver pcmdriver;
extern struct isa_driver pcmdriver ;
#define SNDSTAT_BUF_SIZE 4000
static char status_buf[SNDSTAT_BUF_SIZE] ;
static int status_len = 0 ;
@ -91,6 +91,75 @@ snddev_info synth_info[NPCM_MAX] ;
u_long nsnd = NPCM ; /* total number of sound devices */
/*
* Hooks for APM support, but code not operational yet.
*/
#include "apm.h"
#include <i386/include/apm_bios.h>
#if NAPM > 0
static int
sound_suspend(void *arg)
{
/*
* I think i can safely do nothing here and
* reserve all the work for wakeup time
*/
printf("Called APM sound suspend hook for unit %d\n", (int)arg);
return 0 ;
}
static int
sound_resume(void *arg)
{
snddev_info *d = NULL ;
d = &pcm_info[(int)arg] ;
/*
* reinitialize card registers.
* Flush buffers and reinitialize DMA channels.
* If a write was pending, pretend it is done
* (and issue any wakeup we need).
* If a read is pending, restart it.
*/
if (d->bd_id == MD_YM0020) {
DDB(printf("setting up yamaha registers\n"));
outb(0x370, 6 /* dma config */ ) ;
if (FULL_DUPLEX(d))
outb(0x371, 0xa9 ); /* use both dma chans */
else
outb(0x371, 0x8b ); /* use low dma chan */
}
printf("Called APM sound resume hook for unit %d\n", (int)arg);
return 0 ;
}
static void
init_sound_apm(int unit)
{
struct apmhook *ap;
ap = malloc(sizeof *ap, M_DEVBUF, M_NOWAIT);
bzero(ap, sizeof *ap);
ap->ah_fun = sound_resume;
ap->ah_arg = (void *)unit;
ap->ah_name = "pcm resume handler";
ap->ah_order = APM_MID_ORDER;
apm_hook_establish(APM_HOOK_RESUME, ap);
ap = malloc(sizeof *ap, M_DEVBUF, M_NOWAIT);
bzero(ap, sizeof *ap);
ap->ah_fun = sound_suspend;
ap->ah_arg = (void *)unit;
ap->ah_name = "pcm suspend handler";
ap->ah_order = APM_MID_ORDER;
apm_hook_establish(APM_HOOK_SUSPEND, ap);
}
#endif /* NAPM */
/*
* the probe routine can only return an int to the upper layer. Hence,
* it leaves the pointer to the last successfully
@ -121,7 +190,7 @@ static snddev_info *sb_devs[] = { /* all SB clones */
NULL,
} ;
static snddev_info *mss_devs[] = { /* all WSS clones */
static snddev_info *mss_devs[] = { /* all MSS clones */
&mss_op_desc,
NULL,
} ;
@ -194,7 +263,10 @@ pcmattach(struct isa_device * dev)
d->dbuf_in.chan = dev->id_flags & DV_F_DRQ_MASK ;
else
d->dbuf_in.chan = d->dbuf_out.chan ;
/* XXX should also set bd_id from flags ? */
#if 1 /* does this cause trouble with PnP cards ? */
if (d->bd_id == 0)
d->bd_id = (dev->id_flags & DV_F_DEV_MASK) >> DV_F_DEV_SHIFT ;
#endif
d->status_ptr = 0;
/*
@ -224,7 +296,11 @@ pcmattach(struct isa_device * dev)
cdevsw_add(&isadev, &snd_cdevsw, NULL);
#ifdef DEVFS
#define GID_SND GID_GAMES
#ifndef GID_GAMES
#define GID_SND UID_ROOT
#else
#define GID_SND GID_GAMES /* i am not really sure this is a good one. */
#endif
#define UID_SND UID_ROOT
#define PERM_SND 0660
/*
@ -294,6 +370,9 @@ pcmattach(struct isa_device * dev)
#endif
snddev_last_probed = NULL ;
#if NAPM > 0
init_sound_apm(dev->id_unit);
#endif
return stat ;
}
@ -738,7 +817,7 @@ sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
snd_chan_param *p = (snd_chan_param *)arg;
d->play_speed = p->play_rate;
d->rec_speed = p->play_rate; /* XXX one speed allowed */
if (p->play_format & SND_F_STEREO)
if (p->play_format & AFMT_STEREO)
d->flags |= SND_F_STEREO ;
else
d->flags &= ~SND_F_STEREO ;
@ -805,7 +884,7 @@ sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
break;
case FIOASYNC: /*set/clear async i/o */
printf("FIOASYNC\n");
DEB( printf("FIOASYNC\n") ; )
break;
case SNDCTL_DSP_NONBLOCK :
@ -1055,7 +1134,7 @@ sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
* really sndpoll. Second arg for poll is not "rw" but "events"
*/
int
sndselect(dev_t i_dev, int rw, struct proc *p)
sndselect(dev_t i_dev, int rw, struct proc * p)
{
int dev, unit, c = 1 /* default: success */ ;
snddev_info *d ;
@ -1220,7 +1299,7 @@ init_status(snddev_info *d)
if (status_len != 0) /* only do init once */
return ;
sprintf(status_buf,
"FreeBSD Audio Driver (980215) " __DATE__ " " __TIME__ "\n"
"FreeBSD Audio Driver (981002) " __DATE__ " " __TIME__ "\n"
"Installed devices:\n");
for (i = 0; i < NPCM_MAX; i++) {

@ -65,7 +65,10 @@
#include <i386/isa/isa_device.h>
#include <machine/clock.h> /* for DELAY */
/* To minimize changes with the code in 2.2.X */
/*
* the following assumes that FreeBSD 3.X uses poll(2) instead of select(2).
* This change dates to late 1997.
*/
#include <sys/poll.h>
#define d_select_t d_poll_t
@ -158,7 +161,7 @@ struct _snddev_info {
*/
int io_base ; /* primary I/O address for the board */
int alt_base ; /* some codecs are accessible as SB+WSS... */
int alt_base ; /* some codecs are accessible as SB+MSS... */
int conf_base ; /* and the opti931 also has a config space */
int mix_base ; /* base for the mixer... */
int midi_base ; /* base for the midi */