summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/pcm/isa/mss.c66
-rw-r--r--sys/dev/pcm/isa/sb.c297
-rw-r--r--sys/dev/pcm/isa/sb.h9
-rw-r--r--sys/dev/sound/isa/mss.c66
-rw-r--r--sys/dev/sound/isa/sb.c297
-rw-r--r--sys/dev/sound/isa/sb.h9
-rw-r--r--sys/dev/sound/isa/sb16.c297
-rw-r--r--sys/dev/sound/isa/sb8.c297
-rw-r--r--sys/i386/isa/snd/CARDS178
-rw-r--r--sys/i386/isa/snd/README342
-rw-r--r--sys/i386/isa/snd/ad1848.c66
-rw-r--r--sys/i386/isa/snd/dmabuf.c27
-rw-r--r--sys/i386/isa/snd/doc/Makefile40
-rw-r--r--sys/i386/isa/snd/doc/sound.ps.gzbin0 -> 45365 bytes
-rw-r--r--sys/i386/isa/snd/doc/sound.tex917
-rw-r--r--sys/i386/isa/snd/misc/README82
-rw-r--r--sys/i386/isa/snd/misc/audio-voxware.cc435
-rw-r--r--sys/i386/isa/snd/misc/auvoxware.c1301
-rw-r--r--sys/i386/isa/snd/misc/files.i386240
-rw-r--r--sys/i386/isa/snd/misc/linux.patch41
-rw-r--r--sys/i386/isa/snd/misc/linux_a.c238
-rw-r--r--sys/i386/isa/snd/misc/mmap_test.c278
-rw-r--r--sys/i386/isa/snd/misc/pcmio.c236
-rw-r--r--sys/i386/isa/snd/misc/soundbyte.c275
-rw-r--r--sys/i386/isa/snd/misc/test.c84
-rw-r--r--sys/i386/isa/snd/sb_dsp.c297
-rw-r--r--sys/i386/isa/snd/sbcard.h9
-rw-r--r--sys/i386/isa/snd/sound.c282
-rw-r--r--sys/i386/isa/snd/sound.h38
-rw-r--r--sys/i386/isa/snd/soundcard.h2
30 files changed, 5774 insertions, 972 deletions
diff --git a/sys/dev/pcm/isa/mss.c b/sys/dev/pcm/isa/mss.c
index a6bf624..26eaee5 100644
--- a/sys/dev/pcm/isa/mss.c
+++ b/sys/dev/pcm/isa/mss.c
@@ -94,7 +94,7 @@ snddev_info mss_op_desc = {
NULL /* mss_read */,
NULL /* mss_write */,
mss_ioctl,
- sndpoll /* mss_poll */,
+ sndselect /* mss_select */,
mss_intr,
mss_callback ,
@@ -133,8 +133,8 @@ mss_probe(struct isa_device *dev)
bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) );
if (dev->id_iobase == -1) {
dev->id_iobase = 0x530;
- printf("mss_probe: no address supplied, try default 0x%x\n",
- dev->id_iobase);
+ BVDDB(printf("mss_probe: no address supplied, try default 0x%x\n",
+ dev->id_iobase));
}
if (snd_conflict(dev->id_iobase))
return 0 ;
@@ -150,13 +150,13 @@ mss_probe(struct isa_device *dev)
tmp = inb(dev->id_iobase + 3);
if (tmp == 0xff) { /* Bus float */
- DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
+ BVDDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
dev->id_flags &= ~DV_F_TRUE_MSS ;
goto mss_probe_end;
}
tmp &= 0x3f ;
if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) {
- DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
+ BVDDB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
dev->id_iobase, inb(dev->id_iobase + 3)));
return 0;
}
@@ -276,10 +276,12 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->flags |= SND_F_BUSY_DSP ;
else if (dev == SND_DEV_DSP16)
d->flags |= SND_F_BUSY_DSP16 ;
- if ( ! (d->flags & SND_F_BUSY) ) {
+ if ( d->flags & SND_F_BUSY )
+ splx(s); /* device was already set, no need to reinit */
+ else {
/*
* device was idle. Do the necessary initialization,
- * but no need keep interrupts blocked since this device
+ * but no need keep interrupts blocked.
* will not get them
*/
@@ -313,7 +315,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
}
ask_init(d); /* and reset buffers... */
}
- splx(s);
return 0 ;
}
@@ -343,14 +344,15 @@ mss_close(dev_t dev, int flags, int mode, struct proc * p)
d->flags &= ~SND_F_BUSY_DSP ;
else if (dev == SND_DEV_DSP16)
d->flags &= ~SND_F_BUSY_DSP16 ;
- if ( ! (d->flags & SND_F_BUSY_ANY) ) { /* last one ... */
+ if ( d->flags & SND_F_BUSY_ANY ) /* still some device open */
+ splx(s);
+ else { /* last one */
d->flags |= SND_F_CLOSING ;
splx(s); /* is this ok here ? */
snd_flush(d);
outb(io_Status(d), 0); /* Clear interrupt status */
d->flags = 0 ;
}
- splx(s);
return 0 ;
}
@@ -435,6 +437,7 @@ mss_callback(snddev_info *d, int reason)
ad_write_cnt(d, 30, cnt);
break ;
+
case SND_CB_STOP :
case SND_CB_ABORT : /* XXX check this... */
m = ad_read(d,9) ;
@@ -548,11 +551,11 @@ again:
c=mc11 = FULL_DUPLEX(d) ? opti_read(d->conf_base, 11) : 0xc ;
mc11 &= 0x0c ;
if (c & 0x10) {
- printf("Warning: CD interrupt\n");
+ DEB(printf("Warning: CD interrupt\n");)
mc11 |= 0x10 ;
}
if (c & 0x20) {
- printf("Warning: MPU interrupt\n");
+ DEB(printf("Warning: MPU interrupt\n");)
mc11 |= 0x20 ;
}
if (mc11 & masked)
@@ -578,7 +581,7 @@ again:
}
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
- printf("xxx too many loops\n");
+ DEB(printf("xxx too many loops\n");)
}
/*
@@ -637,13 +640,13 @@ gus_readw(int io_base, u_char reg)
static int
AD_WAIT_INIT(snddev_info *d, int x)
{
- int n = 0; /* to shut up the compiler... */
+ int arg=x, n = 0; /* to shut up the compiler... */
for (; x-- ; )
if ( (n=inb(io_Index_Addr(d))) & IA_BUSY)
DELAY(10);
else
return n ;
- printf("AD_WAIT_INIT FAILED 0x%02x\n", n);
+ printf("AD_WAIT_INIT FAILED %d 0x%02x\n", arg, n);
return n ;
}
@@ -1061,7 +1064,7 @@ mss_detect(struct isa_device *dev)
else
break ;
if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */
- DEB(printf("mss_detect error, busy still set (0x%02x)\n",
+ BVDDB(printf("mss_detect error, busy still set (0x%02x)\n",
inb(io_Index_Addr(d))));
return 0;
}
@@ -1076,7 +1079,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 0) ;
tmp2 = ad_read(d, 1) ;
if ( tmp1 != 0xaa || tmp2 != 0x45) {
- DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
+ BVDDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
tmp1, tmp2));
return 0;
}
@@ -1087,7 +1090,7 @@ mss_detect(struct isa_device *dev)
tmp2 = ad_read(d, 1) ;
if (tmp1 != 0x45 || tmp2 != 0xaa) {
- DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
+ BVDDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
return 0;
}
@@ -1101,7 +1104,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 12);
if ((tmp & 0x0f) != (tmp1 & 0x0f)) {
- DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
+ BVDDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
tmp1, tmp));
return 0;
}
@@ -1112,7 +1115,7 @@ mss_detect(struct isa_device *dev)
* 0x0A=RevC. also CS4231/CS4231A and OPTi931
*/
- printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);
+ BVDDB(printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);)
/*
* The original AD1848/CS4248 has just 16 indirect registers. This
@@ -1125,7 +1128,7 @@ mss_detect(struct isa_device *dev)
for (i = 0; i < 16; i++)
if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) {
- DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
+ BVDDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
i, tmp1, tmp2));
/*
* note - this seems to fail on the 4232 on I11. So we just break
@@ -1148,7 +1151,7 @@ mss_detect(struct isa_device *dev)
name = "CS4248" ; /* Our best knowledge just now */
}
if ((tmp1 & 0xf0) == 0x00) {
- printf("this should be an OPTi931\n");
+ BVDDB(printf("this should be an OPTi931\n");)
} else if ((tmp1 & 0xc0) == 0xC0) {
/*
* The 4231 has bit7=1 always, and bit6 we just set to 1.
@@ -1162,14 +1165,13 @@ mss_detect(struct isa_device *dev)
ad_write(d, 0, 0xaa);
if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */
- DEB(printf("mss_detect error - step H(%x)\n", tmp1));
+ BVDDB(printf("mss_detect error - step H(%x)\n", tmp1));
return 0;
}
/*
* Verify that some bits of I25 are read only.
*/
- DEB(printf("mss_detect() - step I\n"));
tmp1 = ad_read(d, 25); /* Original bits */
ad_write(d, 25, ~tmp1); /* Invert all bits */
if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) {
@@ -1240,7 +1242,7 @@ mss_detect(struct isa_device *dev)
break ;
default: /* Assume CS4231 */
- printf("unknown id 0x%02x, assuming CS4231\n", id);
+ BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);)
d->bd_id = MD_CS4231;
}
@@ -1249,7 +1251,7 @@ mss_detect(struct isa_device *dev)
}
}
- DEB(printf("mss_detect() - Detected %s\n", name));
+ BVDDB(printf("mss_detect() - Detected %s\n", name));
strcpy(d->name, name);
dev->id_flags &= ~DV_F_DEV_MASK ;
dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ;
@@ -1390,7 +1392,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
return ;
}
snddev_last_probed = &tmp_d;
- if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
+ if (d.flags) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
if (vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) {
@@ -1455,6 +1457,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
+ tmp_d.synth_base = d.port[1]; /* XXX check this for yamaha */
pcmattach(dev);
}
@@ -1508,7 +1511,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
enable_pnp_card();
snddev_last_probed = &tmp_d;
- tmp_d = d.flags & DV_PNP_SBCODEC ? sb_op_desc : mss_op_desc ;
+ tmp_d = d.flags ? sb_op_desc : mss_op_desc ;
strcpy(tmp_d.name, name);
@@ -1526,10 +1529,11 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */
tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */
tmp_d.alt_base = d.port[2];
+ tmp_d.synth_base = d.port[1];
opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ );
ad_write (&tmp_d, 10, 2); /* enable interrupts */
- if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
+ if (d.flags) { /* 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...
@@ -1598,7 +1602,6 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
read_pnp_parms ( &d , 0 ) ;
/* d.irq[1] = d.irq[0] ; */
- printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) );
pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */
write_pnp_parms ( &d , 0 );
@@ -1623,7 +1626,6 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
DELAY(1000 * 30);
/* release reset and enable DAC */
gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 );
- printf("resetting the gus...\n");
DELAY(1000 * 30);
/* end of reset */
@@ -1645,7 +1647,7 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
/* enable access to hidden regs */
tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ );
gus_write(tmp_d.conf_base, 0x5b , tmp | 1 );
- printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );
+ BVDDB(printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );)
strcpy(tmp_d.name, name);
diff --git a/sys/dev/pcm/isa/sb.c b/sys/dev/pcm/isa/sb.c
index 09440e1..3d75aea 100644
--- a/sys/dev/pcm/isa/sb.c
+++ b/sys/dev/pcm/isa/sb.c
@@ -92,7 +92,7 @@ snddev_info sb_op_desc = {
NULL /* use generic sndread */,
NULL /* use generic sndwrite */,
sb_dsp_ioctl,
- sndpoll,
+ sndselect,
sbintr,
sb_callback,
@@ -123,7 +123,7 @@ sb_probe(struct isa_device *dev)
bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) );
if (dev->id_iobase == -1) {
dev->id_iobase = 0x220;
- printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");
+ BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");)
if (snd_conflict(dev->id_iobase))
dev->id_iobase = 0x240;
}
@@ -196,6 +196,10 @@ 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)
+ d->rec_fmt = 0 ;
+ if ( (flags & FWRITE) == 0)
+ d->play_fmt = 0 ;
d->flags |= SND_F_BUSY ;
d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
@@ -250,6 +254,8 @@ sb_dsp_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* for the remaining functions, use the default handler.
+ * ENOSYS means that the default handler should take care
+ * of implementing the ioctl.
*/
return ENOSYS ;
@@ -292,7 +298,7 @@ again:
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( reason & 1 ) { /* possibly a write interrupt */
if ( d->dbuf_out.dl )
- dsp_wrintr(d);
+ dsp_wrintr(d);
else {
if (d->bd_flags & BD_F_SB16)
printf("WARNING: wrintr but write DMA inactive!\n");
@@ -300,7 +306,7 @@ again:
}
if ( reason & 2 ) {
if ( d->dbuf_in.dl )
- dsp_rdintr(d);
+ dsp_rdintr(d);
else {
if (d->bd_flags & BD_F_SB16)
printf("WARNING: rdintr but read DMA inactive!\n");
@@ -340,16 +346,11 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_INIT : /* called with int enabled and no pending io */
dsp_speed(d);
snd_set_blocksize(d);
- if (d->play_fmt & AFMT_MU_LAW)
+ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
- reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
- reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
- return 1;
- break;
- case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
@@ -363,42 +364,40 @@ sb_callback(snddev_info *d, int reason)
*/
int swap = 1 ; /* default... */
- if (rd) {
- /* need not to swap if channel is already correct */
- if ( d->rec_fmt == AFMT_S16_LE && b->chan > 4 )
+ if (d->play_fmt == 0) {
+ /* do whatever the read channel wants */
+ if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 )
swap = 0;
- if ( d->rec_fmt != AFMT_S16_LE && b->chan < 4 )
+ if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 )
swap = 0;
- if (swap && (d->flags & SND_F_WRITING || d->dbuf_out.dl)) {
- /* cannot swap if writing is already active */
- DDB(printf("sorry, DMA channel unavailable\n"));
- swap = 0;
- break; /* XXX should return an error */
- }
} else {
- /* need not to swap if channel is already correct */
- if ( d->play_fmt == AFMT_S16_LE && b->chan > 4 )
+ /* privilege the write channel */
+ if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 )
swap = 0;
- if ( d->play_fmt != AFMT_S16_LE && b->chan < 4 )
+ if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
- if (swap && ( d->flags & SND_F_READING || d->dbuf_in.dl)) {
- /* cannot swap if reading is already active */
- DDB(printf("sorry, DMA channel unavailable\n"));
- swap = 0;
- break ; /* XXX should return an error */
+ if ( d->rec_fmt ) {
+ /* check for possible config errors. */
+ if (d->rec_fmt == d->play_fmt) {
+ DDB(printf("sorry, read DMA channel unavailable\n"));
+ }
}
}
-
+ DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n",
+ d->play_fmt, d->rec_fmt, swap);)
if (swap) {
int c = d->dbuf_in.chan ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
- reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
- reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
- DEB(printf("START dma chan: play %d, rec %d\n",
- d->dbuf_out.chan, d->dbuf_in.chan));
+ }
}
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ break ;
+ case SND_CB_START : /* called with int disabled */
+ if (d->bd_flags & BD_F_SB16) {
+ u_char c, c1 ;
/*
* 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
@@ -408,7 +407,7 @@ sb_callback(snddev_info *d, int reason)
if ( b->chan > 4 ) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
- l /= 2 ;
+ l /= 2 ;
} else {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA8 ;
c1 = 0 ;
@@ -421,28 +420,25 @@ sb_callback(snddev_info *d, int reason)
sb_cmd3(d->io_base, c1 , l - 1) ;
} else if (d->bd_flags & BD_F_ESS) {
/* XXX this code is still incomplete */
- sb_cmd2(d->io_base, 0xb8, rd ? 0x0e : 0x04 ); /* auto dma */
- sb_cmd2(d->io_base, 0xa8, 2 /* chans */ );
- sb_cmd2(d->io_base, 0xb9, 2); /* demand mode */
- /*
- input: 0xb8 -> 0x0e ;
- 0xa8 -> channels
- 0xb9 -> 2
- mono,U8: 51, d0
- mono,S16 71, f4
- st, U8 51, 98
- st, S16 71, bc
- */
- } else { /* SBPro */
+ } else { /* SBPro -- stereo not supported */
u_char c ;
if (!rd)
sb_cmd(d->io_base, DSP_CMD_SPKON);
- /* code for the SB2 and SB3 */
+ /* code for the SB2 and SB3, only MONO */
if (d->bd_flags & BD_F_HISPEED)
- c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
+ c = (rd) ? 0x98 : 0x90 ;
else
- c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
- sb_cmd3(d->io_base, c , l - 1) ;
+ c = (rd) ? 0x2c : 0x1c ;
+ /*
+ * some ESS extensions -- they can do 16 bits
+ */
+ if ( (rd && d->rec_fmt == AFMT_S16_LE) ||
+ (!rd && d->play_fmt == AFMT_S16_LE) ) {
+ c |= 1;
+ l /= 2 ;
+ }
+ sb_cmd3(d->io_base, 0x48 , l - 1) ;
+ sb_cmd(d->io_base, c ) ;
}
break;
@@ -451,7 +447,7 @@ sb_callback(snddev_info *d, int reason)
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
- cmd = DSP_CMD_DMAPAUSE_16 ;
+ cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
d->flags |= SND_F_INIT ;
@@ -485,9 +481,9 @@ sb_reset_dsp(int io_base)
{
int loopc;
- outb(DSP_RESET, 1);
+ outb(io_base + SBDSP_RST, 1);
DELAY(100);
- outb(DSP_RESET, 0);
+ outb(io_base + SBDSP_RST, 0);
for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++)
DELAY(30);
@@ -568,7 +564,9 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
d->name, dev->id_unit, d->irq);
else
sb_setmixer(io_base, IRQ_NR, x);
-
+ if (d->dbuf_out.chan == d->dbuf_in.chan) {
+ printf("WARNING: sb: misconfigured secondary DMA channel\n");
+ }
sb_setmixer(io_base, DMA_NR, (1 << d->dbuf_out.chan) | (1 << d->dbuf_in.chan));
break ;
@@ -599,41 +597,22 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
DELAY(20);
}
- if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
+ if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) {
+ /* the ESS488 can be treated as an SBPRO */
printf("ESS488 (rev %d)\n", ess_minor & 0x0f);
- else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
- if ( (ess_minor & 0xf) >= 8 )
- printf("ESS1688 (rev %d)\n", ess_minor & 0x0f);
- else
- printf("ESS688 (rev %d)\n", ess_minor & 0x0f);
- } else
break ;
- d->bd_id = (ess_major << 8) | ess_minor ;
- d->bd_flags |= BD_F_ESS;
- /*
- * ESS-specific initialization, taken from OSSFree 3.8
- */
- {
- static u_char irqs[16] = {
- 0, 0, 0x50, 0, 0, 0x54, 0, 0x58,
- 0, 0x50, 0x5c, 0, 0, 0, 0, 0 };
- static u_char drqs[8] = {
- 0x51, 0x52, 0, 0x53, 0, 0, 0, 0 };
- x = irqs[d->irq & 0xf];
- if (x == 0)
- printf("ESS : invalid IRQ %d\n", d->irq);
- else {
- sb_cmd(io_base, 0xb1 ); /* set IRQ */
- sb_cmd(io_base, x );
-
- x = drqs[ d->dbuf_out.chan & 0x7 ];
- if (x == 0)
- printf("ESS : invalid DRQ %d\n", d->dbuf_out.chan);
- else {
- sb_cmd(io_base, 0xb2 ); /* set DRQ */
- sb_cmd(io_base, x );
- }
- }
+ } else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
+ int rev = ess_minor & 0xf ;
+ if ( rev >= 8 )
+ printf("ESS1868 (rev %d)\n", rev);
+ else
+ printf("ESS688 (rev %d)\n", rev);
+ d->audio_fmt |= AFMT_S16_LE; /* in fact it is U16_LE */
+ break ; /* XXX */
+ } else {
+ printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
+ ess_major, ess_minor);
+ break ;
}
}
@@ -679,8 +658,8 @@ sb_cmd(int io_base, u_char val)
int i;
for (i = 0; i < 1000 ; i++) {
- if ((inb(DSP_STATUS) & 0x80) == 0) {
- outb(DSP_COMMAND, val);
+ if ((inb(io_base + SBDSP_STATUS) & 0x80) == 0) {
+ outb(io_base + SBDSP_CMD, val);
return 1;
}
if (i > 10)
@@ -712,32 +691,23 @@ sb_cmd2(int io_base, u_char cmd, int val)
return 0;
}
+/*
+ * in the SB, there is a set of indirect "mixer" registers with
+ * address at offset 4, data at offset 5
+ */
void
sb_setmixer(int io_base, u_int port, u_int value)
{
u_long flags;
flags = spltty();
- outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- outb(MIXER_DATA, (u_char) (value & 0xff));
+ outb(io_base + 5, (u_char) (value & 0xff));
DELAY(10);
splx(flags);
}
-u_int
-sb_get_byte(int io_base)
-{
- int i;
-
- for (i = 1000; i; i--)
- if (inb(DSP_DATA_AVAIL) & 0x80)
- return inb(DSP_READ);
- else
- DELAY(20);
- return 0xffff;
-}
-
int
sb_getmixer(int io_base, u_int port)
{
@@ -745,15 +715,29 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
- outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- val = inb(MIXER_DATA);
+ val = inb(io_base + 5);
DELAY(10);
splx(flags);
return val;
}
+u_int
+sb_get_byte(int io_base)
+{
+ int i;
+
+ for (i = 1000; i; i--)
+ if (inb(DSP_DATA_AVAIL) & 0x80)
+ return inb(DSP_READ);
+ else
+ DELAY(20);
+ return 0xffff;
+}
+
+
/*
* various utility functions for the DSP
@@ -806,11 +790,12 @@ dsp_speed(snddev_info *d)
}
/*
- * only some models can do stereo, and only if not
+ * This is code for the SB3.x and lower.
+ * Only some models can do stereo, and only if not
* simultaneously using midi.
+ * At the moment we do not support either...
*/
- if ( (d->bd_id & 0xff00) < 0x300 || d->bd_flags & BD_F_MIDIBUSY)
- d->flags &= ~SND_F_STEREO;
+ d->flags &= ~SND_F_STEREO;
/*
* here enforce speed limitations.
@@ -830,26 +815,12 @@ dsp_speed(snddev_info *d)
RANGE(speed, 4000, max_speed);
- /*
- * Logitech SoundMan Games and Jazz16 cards can support 44.1kHz
- * stereo
- */
-#if !defined (SM_GAMES)
- /*
- * Max. stereo speed is 22050
- */
- if (d->flags & SND_F_STEREO && speed > 22050 && !(d->bd_flags & BD_F_JAZZ16))
- speed = 22050;
-#endif
-
- if (d->flags & SND_F_STEREO)
+ if (d->flags & SND_F_STEREO) /* really unused right now... */
speed *= 2;
/*
* Now the speed should be valid. Compute the value to be
* programmed into the board.
- *
- * XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
@@ -878,7 +849,7 @@ dsp_speed(snddev_info *d)
speed = (1000000 + tmp / 2) / tmp;
}
- if (d->flags & SND_F_STEREO)
+ if (d->flags & SND_F_STEREO) /* really unused right now... */
speed /= 2;
d->play_speed = d->rec_speed = speed;
@@ -1024,6 +995,79 @@ sb_mixer_set(snddev_info *d, int dev, int value)
*/
#if NPNP > 0
+static char *ess1868_probe(u_long csn, u_long vend_id);
+static void ess1868_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device ess1868 = {
+ "ESS1868",
+ ess1868_probe,
+ ess1868_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, ess1868);
+
+static char *
+ess1868_probe(u_long csn, u_long vend_id)
+{
+ /*
+ * pnp X 1 os enable drq0 3 irq0 12 port0 0x240
+ */
+ if (vend_id == 0x68187316) {
+ struct pnp_cinfo d ;
+ read_pnp_parms ( &d , 1 ) ;
+ if (d.enable == 0) {
+ printf("This is an ESS1868, but LDN 1 is disabled\n");
+ return NULL;
+ }
+ return "ESS1868" ;
+ }
+ return NULL ;
+}
+
+static void
+ess1868_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;
+
+#if 0
+ read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
+ 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 , 0 ); /* read config base */
+ tmp_d.conf_base = d.port[0];
+ write_pnp_parms ( &d , 0 );
+#endif
+
+ read_pnp_parms ( &d , 1 ) ;
+ dev->id_iobase = d.port[0];
+ d.port[1] = 0 ;
+ d.port[2] = 0 ;
+ write_pnp_parms ( &d , 1 );
+ enable_pnp_card();
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = 0 /* DV_F_DUAL_DMA | (d.drq[1] ) */;
+
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+ 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);
@@ -1155,6 +1199,7 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
read_pnp_parms ( &d , 0 ) ;
d.port[1] = 0 ; /* only the first address is used */
dev->id_iobase = d.port[0];
+ tmp_d.synth_base = d.port[2];
write_pnp_parms ( &d , 0 );
enable_pnp_card();
diff --git a/sys/dev/pcm/isa/sb.h b/sys/dev/pcm/isa/sb.h
index 1735c14..0bd4210 100644
--- a/sys/dev/pcm/isa/sb.h
+++ b/sys/dev/pcm/isa/sb.h
@@ -11,18 +11,21 @@ extern int sbc_major, sbc_minor ;
* sound blaster registers
*/
-#define DSP_RESET (io_base + 0x6)
+#define SBDSP_RST 0x6
#define DSP_READ (io_base + 0xA)
#define DSP_WRITE (io_base + 0xC)
-#define DSP_COMMAND (io_base + 0xC)
-#define DSP_STATUS (io_base + 0xC)
+#define SBDSP_CMD 0xC
+#define SBDSP_STATUS 0xC
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
+
+#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)
+#endif
/*
* DSP Commands. There are many, and in many cases they are used explicitly
diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c
index a6bf624..26eaee5 100644
--- a/sys/dev/sound/isa/mss.c
+++ b/sys/dev/sound/isa/mss.c
@@ -94,7 +94,7 @@ snddev_info mss_op_desc = {
NULL /* mss_read */,
NULL /* mss_write */,
mss_ioctl,
- sndpoll /* mss_poll */,
+ sndselect /* mss_select */,
mss_intr,
mss_callback ,
@@ -133,8 +133,8 @@ mss_probe(struct isa_device *dev)
bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) );
if (dev->id_iobase == -1) {
dev->id_iobase = 0x530;
- printf("mss_probe: no address supplied, try default 0x%x\n",
- dev->id_iobase);
+ BVDDB(printf("mss_probe: no address supplied, try default 0x%x\n",
+ dev->id_iobase));
}
if (snd_conflict(dev->id_iobase))
return 0 ;
@@ -150,13 +150,13 @@ mss_probe(struct isa_device *dev)
tmp = inb(dev->id_iobase + 3);
if (tmp == 0xff) { /* Bus float */
- DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
+ BVDDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
dev->id_flags &= ~DV_F_TRUE_MSS ;
goto mss_probe_end;
}
tmp &= 0x3f ;
if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) {
- DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
+ BVDDB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
dev->id_iobase, inb(dev->id_iobase + 3)));
return 0;
}
@@ -276,10 +276,12 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->flags |= SND_F_BUSY_DSP ;
else if (dev == SND_DEV_DSP16)
d->flags |= SND_F_BUSY_DSP16 ;
- if ( ! (d->flags & SND_F_BUSY) ) {
+ if ( d->flags & SND_F_BUSY )
+ splx(s); /* device was already set, no need to reinit */
+ else {
/*
* device was idle. Do the necessary initialization,
- * but no need keep interrupts blocked since this device
+ * but no need keep interrupts blocked.
* will not get them
*/
@@ -313,7 +315,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
}
ask_init(d); /* and reset buffers... */
}
- splx(s);
return 0 ;
}
@@ -343,14 +344,15 @@ mss_close(dev_t dev, int flags, int mode, struct proc * p)
d->flags &= ~SND_F_BUSY_DSP ;
else if (dev == SND_DEV_DSP16)
d->flags &= ~SND_F_BUSY_DSP16 ;
- if ( ! (d->flags & SND_F_BUSY_ANY) ) { /* last one ... */
+ if ( d->flags & SND_F_BUSY_ANY ) /* still some device open */
+ splx(s);
+ else { /* last one */
d->flags |= SND_F_CLOSING ;
splx(s); /* is this ok here ? */
snd_flush(d);
outb(io_Status(d), 0); /* Clear interrupt status */
d->flags = 0 ;
}
- splx(s);
return 0 ;
}
@@ -435,6 +437,7 @@ mss_callback(snddev_info *d, int reason)
ad_write_cnt(d, 30, cnt);
break ;
+
case SND_CB_STOP :
case SND_CB_ABORT : /* XXX check this... */
m = ad_read(d,9) ;
@@ -548,11 +551,11 @@ again:
c=mc11 = FULL_DUPLEX(d) ? opti_read(d->conf_base, 11) : 0xc ;
mc11 &= 0x0c ;
if (c & 0x10) {
- printf("Warning: CD interrupt\n");
+ DEB(printf("Warning: CD interrupt\n");)
mc11 |= 0x10 ;
}
if (c & 0x20) {
- printf("Warning: MPU interrupt\n");
+ DEB(printf("Warning: MPU interrupt\n");)
mc11 |= 0x20 ;
}
if (mc11 & masked)
@@ -578,7 +581,7 @@ again:
}
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
- printf("xxx too many loops\n");
+ DEB(printf("xxx too many loops\n");)
}
/*
@@ -637,13 +640,13 @@ gus_readw(int io_base, u_char reg)
static int
AD_WAIT_INIT(snddev_info *d, int x)
{
- int n = 0; /* to shut up the compiler... */
+ int arg=x, n = 0; /* to shut up the compiler... */
for (; x-- ; )
if ( (n=inb(io_Index_Addr(d))) & IA_BUSY)
DELAY(10);
else
return n ;
- printf("AD_WAIT_INIT FAILED 0x%02x\n", n);
+ printf("AD_WAIT_INIT FAILED %d 0x%02x\n", arg, n);
return n ;
}
@@ -1061,7 +1064,7 @@ mss_detect(struct isa_device *dev)
else
break ;
if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */
- DEB(printf("mss_detect error, busy still set (0x%02x)\n",
+ BVDDB(printf("mss_detect error, busy still set (0x%02x)\n",
inb(io_Index_Addr(d))));
return 0;
}
@@ -1076,7 +1079,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 0) ;
tmp2 = ad_read(d, 1) ;
if ( tmp1 != 0xaa || tmp2 != 0x45) {
- DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
+ BVDDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
tmp1, tmp2));
return 0;
}
@@ -1087,7 +1090,7 @@ mss_detect(struct isa_device *dev)
tmp2 = ad_read(d, 1) ;
if (tmp1 != 0x45 || tmp2 != 0xaa) {
- DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
+ BVDDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
return 0;
}
@@ -1101,7 +1104,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 12);
if ((tmp & 0x0f) != (tmp1 & 0x0f)) {
- DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
+ BVDDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
tmp1, tmp));
return 0;
}
@@ -1112,7 +1115,7 @@ mss_detect(struct isa_device *dev)
* 0x0A=RevC. also CS4231/CS4231A and OPTi931
*/
- printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);
+ BVDDB(printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);)
/*
* The original AD1848/CS4248 has just 16 indirect registers. This
@@ -1125,7 +1128,7 @@ mss_detect(struct isa_device *dev)
for (i = 0; i < 16; i++)
if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) {
- DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
+ BVDDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
i, tmp1, tmp2));
/*
* note - this seems to fail on the 4232 on I11. So we just break
@@ -1148,7 +1151,7 @@ mss_detect(struct isa_device *dev)
name = "CS4248" ; /* Our best knowledge just now */
}
if ((tmp1 & 0xf0) == 0x00) {
- printf("this should be an OPTi931\n");
+ BVDDB(printf("this should be an OPTi931\n");)
} else if ((tmp1 & 0xc0) == 0xC0) {
/*
* The 4231 has bit7=1 always, and bit6 we just set to 1.
@@ -1162,14 +1165,13 @@ mss_detect(struct isa_device *dev)
ad_write(d, 0, 0xaa);
if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */
- DEB(printf("mss_detect error - step H(%x)\n", tmp1));
+ BVDDB(printf("mss_detect error - step H(%x)\n", tmp1));
return 0;
}
/*
* Verify that some bits of I25 are read only.
*/
- DEB(printf("mss_detect() - step I\n"));
tmp1 = ad_read(d, 25); /* Original bits */
ad_write(d, 25, ~tmp1); /* Invert all bits */
if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) {
@@ -1240,7 +1242,7 @@ mss_detect(struct isa_device *dev)
break ;
default: /* Assume CS4231 */
- printf("unknown id 0x%02x, assuming CS4231\n", id);
+ BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);)
d->bd_id = MD_CS4231;
}
@@ -1249,7 +1251,7 @@ mss_detect(struct isa_device *dev)
}
}
- DEB(printf("mss_detect() - Detected %s\n", name));
+ BVDDB(printf("mss_detect() - Detected %s\n", name));
strcpy(d->name, name);
dev->id_flags &= ~DV_F_DEV_MASK ;
dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ;
@@ -1390,7 +1392,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
return ;
}
snddev_last_probed = &tmp_d;
- if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
+ if (d.flags) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
if (vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) {
@@ -1455,6 +1457,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
+ tmp_d.synth_base = d.port[1]; /* XXX check this for yamaha */
pcmattach(dev);
}
@@ -1508,7 +1511,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
enable_pnp_card();
snddev_last_probed = &tmp_d;
- tmp_d = d.flags & DV_PNP_SBCODEC ? sb_op_desc : mss_op_desc ;
+ tmp_d = d.flags ? sb_op_desc : mss_op_desc ;
strcpy(tmp_d.name, name);
@@ -1526,10 +1529,11 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */
tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */
tmp_d.alt_base = d.port[2];
+ tmp_d.synth_base = d.port[1];
opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ );
ad_write (&tmp_d, 10, 2); /* enable interrupts */
- if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
+ if (d.flags) { /* 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...
@@ -1598,7 +1602,6 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
read_pnp_parms ( &d , 0 ) ;
/* d.irq[1] = d.irq[0] ; */
- printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) );
pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */
write_pnp_parms ( &d , 0 );
@@ -1623,7 +1626,6 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
DELAY(1000 * 30);
/* release reset and enable DAC */
gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 );
- printf("resetting the gus...\n");
DELAY(1000 * 30);
/* end of reset */
@@ -1645,7 +1647,7 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
/* enable access to hidden regs */
tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ );
gus_write(tmp_d.conf_base, 0x5b , tmp | 1 );
- printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );
+ BVDDB(printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );)
strcpy(tmp_d.name, name);
diff --git a/sys/dev/sound/isa/sb.c b/sys/dev/sound/isa/sb.c
index 09440e1..3d75aea 100644
--- a/sys/dev/sound/isa/sb.c
+++ b/sys/dev/sound/isa/sb.c
@@ -92,7 +92,7 @@ snddev_info sb_op_desc = {
NULL /* use generic sndread */,
NULL /* use generic sndwrite */,
sb_dsp_ioctl,
- sndpoll,
+ sndselect,
sbintr,
sb_callback,
@@ -123,7 +123,7 @@ sb_probe(struct isa_device *dev)
bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) );
if (dev->id_iobase == -1) {
dev->id_iobase = 0x220;
- printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");
+ BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");)
if (snd_conflict(dev->id_iobase))
dev->id_iobase = 0x240;
}
@@ -196,6 +196,10 @@ 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)
+ d->rec_fmt = 0 ;
+ if ( (flags & FWRITE) == 0)
+ d->play_fmt = 0 ;
d->flags |= SND_F_BUSY ;
d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
@@ -250,6 +254,8 @@ sb_dsp_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* for the remaining functions, use the default handler.
+ * ENOSYS means that the default handler should take care
+ * of implementing the ioctl.
*/
return ENOSYS ;
@@ -292,7 +298,7 @@ again:
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( reason & 1 ) { /* possibly a write interrupt */
if ( d->dbuf_out.dl )
- dsp_wrintr(d);
+ dsp_wrintr(d);
else {
if (d->bd_flags & BD_F_SB16)
printf("WARNING: wrintr but write DMA inactive!\n");
@@ -300,7 +306,7 @@ again:
}
if ( reason & 2 ) {
if ( d->dbuf_in.dl )
- dsp_rdintr(d);
+ dsp_rdintr(d);
else {
if (d->bd_flags & BD_F_SB16)
printf("WARNING: rdintr but read DMA inactive!\n");
@@ -340,16 +346,11 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_INIT : /* called with int enabled and no pending io */
dsp_speed(d);
snd_set_blocksize(d);
- if (d->play_fmt & AFMT_MU_LAW)
+ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
- reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
- reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
- return 1;
- break;
- case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
@@ -363,42 +364,40 @@ sb_callback(snddev_info *d, int reason)
*/
int swap = 1 ; /* default... */
- if (rd) {
- /* need not to swap if channel is already correct */
- if ( d->rec_fmt == AFMT_S16_LE && b->chan > 4 )
+ if (d->play_fmt == 0) {
+ /* do whatever the read channel wants */
+ if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 )
swap = 0;
- if ( d->rec_fmt != AFMT_S16_LE && b->chan < 4 )
+ if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 )
swap = 0;
- if (swap && (d->flags & SND_F_WRITING || d->dbuf_out.dl)) {
- /* cannot swap if writing is already active */
- DDB(printf("sorry, DMA channel unavailable\n"));
- swap = 0;
- break; /* XXX should return an error */
- }
} else {
- /* need not to swap if channel is already correct */
- if ( d->play_fmt == AFMT_S16_LE && b->chan > 4 )
+ /* privilege the write channel */
+ if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 )
swap = 0;
- if ( d->play_fmt != AFMT_S16_LE && b->chan < 4 )
+ if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
- if (swap && ( d->flags & SND_F_READING || d->dbuf_in.dl)) {
- /* cannot swap if reading is already active */
- DDB(printf("sorry, DMA channel unavailable\n"));
- swap = 0;
- break ; /* XXX should return an error */
+ if ( d->rec_fmt ) {
+ /* check for possible config errors. */
+ if (d->rec_fmt == d->play_fmt) {
+ DDB(printf("sorry, read DMA channel unavailable\n"));
+ }
}
}
-
+ DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n",
+ d->play_fmt, d->rec_fmt, swap);)
if (swap) {
int c = d->dbuf_in.chan ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
- reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
- reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
- DEB(printf("START dma chan: play %d, rec %d\n",
- d->dbuf_out.chan, d->dbuf_in.chan));
+ }
}
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ break ;
+ case SND_CB_START : /* called with int disabled */
+ if (d->bd_flags & BD_F_SB16) {
+ u_char c, c1 ;
/*
* 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
@@ -408,7 +407,7 @@ sb_callback(snddev_info *d, int reason)
if ( b->chan > 4 ) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
- l /= 2 ;
+ l /= 2 ;
} else {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA8 ;
c1 = 0 ;
@@ -421,28 +420,25 @@ sb_callback(snddev_info *d, int reason)
sb_cmd3(d->io_base, c1 , l - 1) ;
} else if (d->bd_flags & BD_F_ESS) {
/* XXX this code is still incomplete */
- sb_cmd2(d->io_base, 0xb8, rd ? 0x0e : 0x04 ); /* auto dma */
- sb_cmd2(d->io_base, 0xa8, 2 /* chans */ );
- sb_cmd2(d->io_base, 0xb9, 2); /* demand mode */
- /*
- input: 0xb8 -> 0x0e ;
- 0xa8 -> channels
- 0xb9 -> 2
- mono,U8: 51, d0
- mono,S16 71, f4
- st, U8 51, 98
- st, S16 71, bc
- */
- } else { /* SBPro */
+ } else { /* SBPro -- stereo not supported */
u_char c ;
if (!rd)
sb_cmd(d->io_base, DSP_CMD_SPKON);
- /* code for the SB2 and SB3 */
+ /* code for the SB2 and SB3, only MONO */
if (d->bd_flags & BD_F_HISPEED)
- c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
+ c = (rd) ? 0x98 : 0x90 ;
else
- c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
- sb_cmd3(d->io_base, c , l - 1) ;
+ c = (rd) ? 0x2c : 0x1c ;
+ /*
+ * some ESS extensions -- they can do 16 bits
+ */
+ if ( (rd && d->rec_fmt == AFMT_S16_LE) ||
+ (!rd && d->play_fmt == AFMT_S16_LE) ) {
+ c |= 1;
+ l /= 2 ;
+ }
+ sb_cmd3(d->io_base, 0x48 , l - 1) ;
+ sb_cmd(d->io_base, c ) ;
}
break;
@@ -451,7 +447,7 @@ sb_callback(snddev_info *d, int reason)
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
- cmd = DSP_CMD_DMAPAUSE_16 ;
+ cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
d->flags |= SND_F_INIT ;
@@ -485,9 +481,9 @@ sb_reset_dsp(int io_base)
{
int loopc;
- outb(DSP_RESET, 1);
+ outb(io_base + SBDSP_RST, 1);
DELAY(100);
- outb(DSP_RESET, 0);
+ outb(io_base + SBDSP_RST, 0);
for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++)
DELAY(30);
@@ -568,7 +564,9 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
d->name, dev->id_unit, d->irq);
else
sb_setmixer(io_base, IRQ_NR, x);
-
+ if (d->dbuf_out.chan == d->dbuf_in.chan) {
+ printf("WARNING: sb: misconfigured secondary DMA channel\n");
+ }
sb_setmixer(io_base, DMA_NR, (1 << d->dbuf_out.chan) | (1 << d->dbuf_in.chan));
break ;
@@ -599,41 +597,22 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
DELAY(20);
}
- if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
+ if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) {
+ /* the ESS488 can be treated as an SBPRO */
printf("ESS488 (rev %d)\n", ess_minor & 0x0f);
- else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
- if ( (ess_minor & 0xf) >= 8 )
- printf("ESS1688 (rev %d)\n", ess_minor & 0x0f);
- else
- printf("ESS688 (rev %d)\n", ess_minor & 0x0f);
- } else
break ;
- d->bd_id = (ess_major << 8) | ess_minor ;
- d->bd_flags |= BD_F_ESS;
- /*
- * ESS-specific initialization, taken from OSSFree 3.8
- */
- {
- static u_char irqs[16] = {
- 0, 0, 0x50, 0, 0, 0x54, 0, 0x58,
- 0, 0x50, 0x5c, 0, 0, 0, 0, 0 };
- static u_char drqs[8] = {
- 0x51, 0x52, 0, 0x53, 0, 0, 0, 0 };
- x = irqs[d->irq & 0xf];
- if (x == 0)
- printf("ESS : invalid IRQ %d\n", d->irq);
- else {
- sb_cmd(io_base, 0xb1 ); /* set IRQ */
- sb_cmd(io_base, x );
-
- x = drqs[ d->dbuf_out.chan & 0x7 ];
- if (x == 0)
- printf("ESS : invalid DRQ %d\n", d->dbuf_out.chan);
- else {
- sb_cmd(io_base, 0xb2 ); /* set DRQ */
- sb_cmd(io_base, x );
- }
- }
+ } else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
+ int rev = ess_minor & 0xf ;
+ if ( rev >= 8 )
+ printf("ESS1868 (rev %d)\n", rev);
+ else
+ printf("ESS688 (rev %d)\n", rev);
+ d->audio_fmt |= AFMT_S16_LE; /* in fact it is U16_LE */
+ break ; /* XXX */
+ } else {
+ printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
+ ess_major, ess_minor);
+ break ;
}
}
@@ -679,8 +658,8 @@ sb_cmd(int io_base, u_char val)
int i;
for (i = 0; i < 1000 ; i++) {
- if ((inb(DSP_STATUS) & 0x80) == 0) {
- outb(DSP_COMMAND, val);
+ if ((inb(io_base + SBDSP_STATUS) & 0x80) == 0) {
+ outb(io_base + SBDSP_CMD, val);
return 1;
}
if (i > 10)
@@ -712,32 +691,23 @@ sb_cmd2(int io_base, u_char cmd, int val)
return 0;
}
+/*
+ * in the SB, there is a set of indirect "mixer" registers with
+ * address at offset 4, data at offset 5
+ */
void
sb_setmixer(int io_base, u_int port, u_int value)
{
u_long flags;
flags = spltty();
- outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- outb(MIXER_DATA, (u_char) (value & 0xff));
+ outb(io_base + 5, (u_char) (value & 0xff));
DELAY(10);
splx(flags);
}
-u_int
-sb_get_byte(int io_base)
-{
- int i;
-
- for (i = 1000; i; i--)
- if (inb(DSP_DATA_AVAIL) & 0x80)
- return inb(DSP_READ);
- else
- DELAY(20);
- return 0xffff;
-}
-
int
sb_getmixer(int io_base, u_int port)
{
@@ -745,15 +715,29 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
- outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- val = inb(MIXER_DATA);
+ val = inb(io_base + 5);
DELAY(10);
splx(flags);
return val;
}
+u_int
+sb_get_byte(int io_base)
+{
+ int i;
+
+ for (i = 1000; i; i--)
+ if (inb(DSP_DATA_AVAIL) & 0x80)
+ return inb(DSP_READ);
+ else
+ DELAY(20);
+ return 0xffff;
+}
+
+
/*
* various utility functions for the DSP
@@ -806,11 +790,12 @@ dsp_speed(snddev_info *d)
}
/*
- * only some models can do stereo, and only if not
+ * This is code for the SB3.x and lower.
+ * Only some models can do stereo, and only if not
* simultaneously using midi.
+ * At the moment we do not support either...
*/
- if ( (d->bd_id & 0xff00) < 0x300 || d->bd_flags & BD_F_MIDIBUSY)
- d->flags &= ~SND_F_STEREO;
+ d->flags &= ~SND_F_STEREO;
/*
* here enforce speed limitations.
@@ -830,26 +815,12 @@ dsp_speed(snddev_info *d)
RANGE(speed, 4000, max_speed);
- /*
- * Logitech SoundMan Games and Jazz16 cards can support 44.1kHz
- * stereo
- */
-#if !defined (SM_GAMES)
- /*
- * Max. stereo speed is 22050
- */
- if (d->flags & SND_F_STEREO && speed > 22050 && !(d->bd_flags & BD_F_JAZZ16))
- speed = 22050;
-#endif
-
- if (d->flags & SND_F_STEREO)
+ if (d->flags & SND_F_STEREO) /* really unused right now... */
speed *= 2;
/*
* Now the speed should be valid. Compute the value to be
* programmed into the board.
- *
- * XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
@@ -878,7 +849,7 @@ dsp_speed(snddev_info *d)
speed = (1000000 + tmp / 2) / tmp;
}
- if (d->flags & SND_F_STEREO)
+ if (d->flags & SND_F_STEREO) /* really unused right now... */
speed /= 2;
d->play_speed = d->rec_speed = speed;
@@ -1024,6 +995,79 @@ sb_mixer_set(snddev_info *d, int dev, int value)
*/
#if NPNP > 0
+static char *ess1868_probe(u_long csn, u_long vend_id);
+static void ess1868_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device ess1868 = {
+ "ESS1868",
+ ess1868_probe,
+ ess1868_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, ess1868);
+
+static char *
+ess1868_probe(u_long csn, u_long vend_id)
+{
+ /*
+ * pnp X 1 os enable drq0 3 irq0 12 port0 0x240
+ */
+ if (vend_id == 0x68187316) {
+ struct pnp_cinfo d ;
+ read_pnp_parms ( &d , 1 ) ;
+ if (d.enable == 0) {
+ printf("This is an ESS1868, but LDN 1 is disabled\n");
+ return NULL;
+ }
+ return "ESS1868" ;
+ }
+ return NULL ;
+}
+
+static void
+ess1868_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;
+
+#if 0
+ read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
+ 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 , 0 ); /* read config base */
+ tmp_d.conf_base = d.port[0];
+ write_pnp_parms ( &d , 0 );
+#endif
+
+ read_pnp_parms ( &d , 1 ) ;
+ dev->id_iobase = d.port[0];
+ d.port[1] = 0 ;
+ d.port[2] = 0 ;
+ write_pnp_parms ( &d , 1 );
+ enable_pnp_card();
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = 0 /* DV_F_DUAL_DMA | (d.drq[1] ) */;
+
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+ 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);
@@ -1155,6 +1199,7 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
read_pnp_parms ( &d , 0 ) ;
d.port[1] = 0 ; /* only the first address is used */
dev->id_iobase = d.port[0];
+ tmp_d.synth_base = d.port[2];
write_pnp_parms ( &d , 0 );
enable_pnp_card();
diff --git a/sys/dev/sound/isa/sb.h b/sys/dev/sound/isa/sb.h
index 1735c14..0bd4210 100644
--- a/sys/dev/sound/isa/sb.h
+++ b/sys/dev/sound/isa/sb.h
@@ -11,18 +11,21 @@ extern int sbc_major, sbc_minor ;
* sound blaster registers
*/
-#define DSP_RESET (io_base + 0x6)
+#define SBDSP_RST 0x6
#define DSP_READ (io_base + 0xA)
#define DSP_WRITE (io_base + 0xC)
-#define DSP_COMMAND (io_base + 0xC)
-#define DSP_STATUS (io_base + 0xC)
+#define SBDSP_CMD 0xC
+#define SBDSP_STATUS 0xC
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
+
+#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)
+#endif
/*
* DSP Commands. There are many, and in many cases they are used explicitly
diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c
index 09440e1..3d75aea 100644
--- a/sys/dev/sound/isa/sb16.c
+++ b/sys/dev/sound/isa/sb16.c
@@ -92,7 +92,7 @@ snddev_info sb_op_desc = {
NULL /* use generic sndread */,
NULL /* use generic sndwrite */,
sb_dsp_ioctl,
- sndpoll,
+ sndselect,
sbintr,
sb_callback,
@@ -123,7 +123,7 @@ sb_probe(struct isa_device *dev)
bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) );
if (dev->id_iobase == -1) {
dev->id_iobase = 0x220;
- printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");
+ BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");)
if (snd_conflict(dev->id_iobase))
dev->id_iobase = 0x240;
}
@@ -196,6 +196,10 @@ 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)
+ d->rec_fmt = 0 ;
+ if ( (flags & FWRITE) == 0)
+ d->play_fmt = 0 ;
d->flags |= SND_F_BUSY ;
d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
@@ -250,6 +254,8 @@ sb_dsp_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* for the remaining functions, use the default handler.
+ * ENOSYS means that the default handler should take care
+ * of implementing the ioctl.
*/
return ENOSYS ;
@@ -292,7 +298,7 @@ again:
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( reason & 1 ) { /* possibly a write interrupt */
if ( d->dbuf_out.dl )
- dsp_wrintr(d);
+ dsp_wrintr(d);
else {
if (d->bd_flags & BD_F_SB16)
printf("WARNING: wrintr but write DMA inactive!\n");
@@ -300,7 +306,7 @@ again:
}
if ( reason & 2 ) {
if ( d->dbuf_in.dl )
- dsp_rdintr(d);
+ dsp_rdintr(d);
else {
if (d->bd_flags & BD_F_SB16)
printf("WARNING: rdintr but read DMA inactive!\n");
@@ -340,16 +346,11 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_INIT : /* called with int enabled and no pending io */
dsp_speed(d);
snd_set_blocksize(d);
- if (d->play_fmt & AFMT_MU_LAW)
+ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
- reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
- reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
- return 1;
- break;
- case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
@@ -363,42 +364,40 @@ sb_callback(snddev_info *d, int reason)
*/
int swap = 1 ; /* default... */
- if (rd) {
- /* need not to swap if channel is already correct */
- if ( d->rec_fmt == AFMT_S16_LE && b->chan > 4 )
+ if (d->play_fmt == 0) {
+ /* do whatever the read channel wants */
+ if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 )
swap = 0;
- if ( d->rec_fmt != AFMT_S16_LE && b->chan < 4 )
+ if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 )
swap = 0;
- if (swap && (d->flags & SND_F_WRITING || d->dbuf_out.dl)) {
- /* cannot swap if writing is already active */
- DDB(printf("sorry, DMA channel unavailable\n"));
- swap = 0;
- break; /* XXX should return an error */
- }
} else {
- /* need not to swap if channel is already correct */
- if ( d->play_fmt == AFMT_S16_LE && b->chan > 4 )
+ /* privilege the write channel */
+ if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 )
swap = 0;
- if ( d->play_fmt != AFMT_S16_LE && b->chan < 4 )
+ if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
- if (swap && ( d->flags & SND_F_READING || d->dbuf_in.dl)) {
- /* cannot swap if reading is already active */
- DDB(printf("sorry, DMA channel unavailable\n"));
- swap = 0;
- break ; /* XXX should return an error */
+ if ( d->rec_fmt ) {
+ /* check for possible config errors. */
+ if (d->rec_fmt == d->play_fmt) {
+ DDB(printf("sorry, read DMA channel unavailable\n"));
+ }
}
}
-
+ DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n",
+ d->play_fmt, d->rec_fmt, swap);)
if (swap) {
int c = d->dbuf_in.chan ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
- reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
- reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
- DEB(printf("START dma chan: play %d, rec %d\n",
- d->dbuf_out.chan, d->dbuf_in.chan));
+ }
}
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ break ;
+ case SND_CB_START : /* called with int disabled */
+ if (d->bd_flags & BD_F_SB16) {
+ u_char c, c1 ;
/*
* 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
@@ -408,7 +407,7 @@ sb_callback(snddev_info *d, int reason)
if ( b->chan > 4 ) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
- l /= 2 ;
+ l /= 2 ;
} else {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA8 ;
c1 = 0 ;
@@ -421,28 +420,25 @@ sb_callback(snddev_info *d, int reason)
sb_cmd3(d->io_base, c1 , l - 1) ;
} else if (d->bd_flags & BD_F_ESS) {
/* XXX this code is still incomplete */
- sb_cmd2(d->io_base, 0xb8, rd ? 0x0e : 0x04 ); /* auto dma */
- sb_cmd2(d->io_base, 0xa8, 2 /* chans */ );
- sb_cmd2(d->io_base, 0xb9, 2); /* demand mode */
- /*
- input: 0xb8 -> 0x0e ;
- 0xa8 -> channels
- 0xb9 -> 2
- mono,U8: 51, d0
- mono,S16 71, f4
- st, U8 51, 98
- st, S16 71, bc
- */
- } else { /* SBPro */
+ } else { /* SBPro -- stereo not supported */
u_char c ;
if (!rd)
sb_cmd(d->io_base, DSP_CMD_SPKON);
- /* code for the SB2 and SB3 */
+ /* code for the SB2 and SB3, only MONO */
if (d->bd_flags & BD_F_HISPEED)
- c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
+ c = (rd) ? 0x98 : 0x90 ;
else
- c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
- sb_cmd3(d->io_base, c , l - 1) ;
+ c = (rd) ? 0x2c : 0x1c ;
+ /*
+ * some ESS extensions -- they can do 16 bits
+ */
+ if ( (rd && d->rec_fmt == AFMT_S16_LE) ||
+ (!rd && d->play_fmt == AFMT_S16_LE) ) {
+ c |= 1;
+ l /= 2 ;
+ }
+ sb_cmd3(d->io_base, 0x48 , l - 1) ;
+ sb_cmd(d->io_base, c ) ;
}
break;
@@ -451,7 +447,7 @@ sb_callback(snddev_info *d, int reason)
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
- cmd = DSP_CMD_DMAPAUSE_16 ;
+ cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
d->flags |= SND_F_INIT ;
@@ -485,9 +481,9 @@ sb_reset_dsp(int io_base)
{
int loopc;
- outb(DSP_RESET, 1);
+ outb(io_base + SBDSP_RST, 1);
DELAY(100);
- outb(DSP_RESET, 0);
+ outb(io_base + SBDSP_RST, 0);
for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++)
DELAY(30);
@@ -568,7 +564,9 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
d->name, dev->id_unit, d->irq);
else
sb_setmixer(io_base, IRQ_NR, x);
-
+ if (d->dbuf_out.chan == d->dbuf_in.chan) {
+ printf("WARNING: sb: misconfigured secondary DMA channel\n");
+ }
sb_setmixer(io_base, DMA_NR, (1 << d->dbuf_out.chan) | (1 << d->dbuf_in.chan));
break ;
@@ -599,41 +597,22 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
DELAY(20);
}
- if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
+ if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) {
+ /* the ESS488 can be treated as an SBPRO */
printf("ESS488 (rev %d)\n", ess_minor & 0x0f);
- else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
- if ( (ess_minor & 0xf) >= 8 )
- printf("ESS1688 (rev %d)\n", ess_minor & 0x0f);
- else
- printf("ESS688 (rev %d)\n", ess_minor & 0x0f);
- } else
break ;
- d->bd_id = (ess_major << 8) | ess_minor ;
- d->bd_flags |= BD_F_ESS;
- /*
- * ESS-specific initialization, taken from OSSFree 3.8
- */
- {
- static u_char irqs[16] = {
- 0, 0, 0x50, 0, 0, 0x54, 0, 0x58,
- 0, 0x50, 0x5c, 0, 0, 0, 0, 0 };
- static u_char drqs[8] = {
- 0x51, 0x52, 0, 0x53, 0, 0, 0, 0 };
- x = irqs[d->irq & 0xf];
- if (x == 0)
- printf("ESS : invalid IRQ %d\n", d->irq);
- else {
- sb_cmd(io_base, 0xb1 ); /* set IRQ */
- sb_cmd(io_base, x );
-
- x = drqs[ d->dbuf_out.chan & 0x7 ];
- if (x == 0)
- printf("ESS : invalid DRQ %d\n", d->dbuf_out.chan);
- else {
- sb_cmd(io_base, 0xb2 ); /* set DRQ */
- sb_cmd(io_base, x );
- }
- }
+ } else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
+ int rev = ess_minor & 0xf ;
+ if ( rev >= 8 )
+ printf("ESS1868 (rev %d)\n", rev);
+ else
+ printf("ESS688 (rev %d)\n", rev);
+ d->audio_fmt |= AFMT_S16_LE; /* in fact it is U16_LE */
+ break ; /* XXX */
+ } else {
+ printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
+ ess_major, ess_minor);
+ break ;
}
}
@@ -679,8 +658,8 @@ sb_cmd(int io_base, u_char val)
int i;
for (i = 0; i < 1000 ; i++) {
- if ((inb(DSP_STATUS) & 0x80) == 0) {
- outb(DSP_COMMAND, val);
+ if ((inb(io_base + SBDSP_STATUS) & 0x80) == 0) {
+ outb(io_base + SBDSP_CMD, val);
return 1;
}
if (i > 10)
@@ -712,32 +691,23 @@ sb_cmd2(int io_base, u_char cmd, int val)
return 0;
}
+/*
+ * in the SB, there is a set of indirect "mixer" registers with
+ * address at offset 4, data at offset 5
+ */
void
sb_setmixer(int io_base, u_int port, u_int value)
{
u_long flags;
flags = spltty();
- outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- outb(MIXER_DATA, (u_char) (value & 0xff));
+ outb(io_base + 5, (u_char) (value & 0xff));
DELAY(10);
splx(flags);
}
-u_int
-sb_get_byte(int io_base)
-{
- int i;
-
- for (i = 1000; i; i--)
- if (inb(DSP_DATA_AVAIL) & 0x80)
- return inb(DSP_READ);
- else
- DELAY(20);
- return 0xffff;
-}
-
int
sb_getmixer(int io_base, u_int port)
{
@@ -745,15 +715,29 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
- outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- val = inb(MIXER_DATA);
+ val = inb(io_base + 5);
DELAY(10);
splx(flags);
return val;
}
+u_int
+sb_get_byte(int io_base)
+{
+ int i;
+
+ for (i = 1000; i; i--)
+ if (inb(DSP_DATA_AVAIL) & 0x80)
+ return inb(DSP_READ);
+ else
+ DELAY(20);
+ return 0xffff;
+}
+
+
/*
* various utility functions for the DSP
@@ -806,11 +790,12 @@ dsp_speed(snddev_info *d)
}
/*
- * only some models can do stereo, and only if not
+ * This is code for the SB3.x and lower.
+ * Only some models can do stereo, and only if not
* simultaneously using midi.
+ * At the moment we do not support either...
*/
- if ( (d->bd_id & 0xff00) < 0x300 || d->bd_flags & BD_F_MIDIBUSY)
- d->flags &= ~SND_F_STEREO;
+ d->flags &= ~SND_F_STEREO;
/*
* here enforce speed limitations.
@@ -830,26 +815,12 @@ dsp_speed(snddev_info *d)
RANGE(speed, 4000, max_speed);
- /*
- * Logitech SoundMan Games and Jazz16 cards can support 44.1kHz
- * stereo
- */
-#if !defined (SM_GAMES)
- /*
- * Max. stereo speed is 22050
- */
- if (d->flags & SND_F_STEREO && speed > 22050 && !(d->bd_flags & BD_F_JAZZ16))
- speed = 22050;
-#endif
-
- if (d->flags & SND_F_STEREO)
+ if (d->flags & SND_F_STEREO) /* really unused right now... */
speed *= 2;
/*
* Now the speed should be valid. Compute the value to be
* programmed into the board.
- *
- * XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
@@ -878,7 +849,7 @@ dsp_speed(snddev_info *d)
speed = (1000000 + tmp / 2) / tmp;
}
- if (d->flags & SND_F_STEREO)
+ if (d->flags & SND_F_STEREO) /* really unused right now... */
speed /= 2;
d->play_speed = d->rec_speed = speed;
@@ -1024,6 +995,79 @@ sb_mixer_set(snddev_info *d, int dev, int value)
*/
#if NPNP > 0
+static char *ess1868_probe(u_long csn, u_long vend_id);
+static void ess1868_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device ess1868 = {
+ "ESS1868",
+ ess1868_probe,
+ ess1868_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, ess1868);
+
+static char *
+ess1868_probe(u_long csn, u_long vend_id)
+{
+ /*
+ * pnp X 1 os enable drq0 3 irq0 12 port0 0x240
+ */
+ if (vend_id == 0x68187316) {
+ struct pnp_cinfo d ;
+ read_pnp_parms ( &d , 1 ) ;
+ if (d.enable == 0) {
+ printf("This is an ESS1868, but LDN 1 is disabled\n");
+ return NULL;
+ }
+ return "ESS1868" ;
+ }
+ return NULL ;
+}
+
+static void
+ess1868_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;
+
+#if 0
+ read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
+ 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 , 0 ); /* read config base */
+ tmp_d.conf_base = d.port[0];
+ write_pnp_parms ( &d , 0 );
+#endif
+
+ read_pnp_parms ( &d , 1 ) ;
+ dev->id_iobase = d.port[0];
+ d.port[1] = 0 ;
+ d.port[2] = 0 ;
+ write_pnp_parms ( &d , 1 );
+ enable_pnp_card();
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = 0 /* DV_F_DUAL_DMA | (d.drq[1] ) */;
+
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+ 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);
@@ -1155,6 +1199,7 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
read_pnp_parms ( &d , 0 ) ;
d.port[1] = 0 ; /* only the first address is used */
dev->id_iobase = d.port[0];
+ tmp_d.synth_base = d.port[2];
write_pnp_parms ( &d , 0 );
enable_pnp_card();
diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c
index 09440e1..3d75aea 100644
--- a/sys/dev/sound/isa/sb8.c
+++ b/sys/dev/sound/isa/sb8.c
@@ -92,7 +92,7 @@ snddev_info sb_op_desc = {
NULL /* use generic sndread */,
NULL /* use generic sndwrite */,
sb_dsp_ioctl,
- sndpoll,
+ sndselect,
sbintr,
sb_callback,
@@ -123,7 +123,7 @@ sb_probe(struct isa_device *dev)
bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) );
if (dev->id_iobase == -1) {
dev->id_iobase = 0x220;
- printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");
+ BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");)
if (snd_conflict(dev->id_iobase))
dev->id_iobase = 0x240;
}
@@ -196,6 +196,10 @@ 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)
+ d->rec_fmt = 0 ;
+ if ( (flags & FWRITE) == 0)
+ d->play_fmt = 0 ;
d->flags |= SND_F_BUSY ;
d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
@@ -250,6 +254,8 @@ sb_dsp_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* for the remaining functions, use the default handler.
+ * ENOSYS means that the default handler should take care
+ * of implementing the ioctl.
*/
return ENOSYS ;
@@ -292,7 +298,7 @@ again:
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( reason & 1 ) { /* possibly a write interrupt */
if ( d->dbuf_out.dl )
- dsp_wrintr(d);
+ dsp_wrintr(d);
else {
if (d->bd_flags & BD_F_SB16)
printf("WARNING: wrintr but write DMA inactive!\n");
@@ -300,7 +306,7 @@ again:
}
if ( reason & 2 ) {
if ( d->dbuf_in.dl )
- dsp_rdintr(d);
+ dsp_rdintr(d);
else {
if (d->bd_flags & BD_F_SB16)
printf("WARNING: rdintr but read DMA inactive!\n");
@@ -340,16 +346,11 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_INIT : /* called with int enabled and no pending io */
dsp_speed(d);
snd_set_blocksize(d);
- if (d->play_fmt & AFMT_MU_LAW)
+ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
- reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
- reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
- return 1;
- break;
- case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
@@ -363,42 +364,40 @@ sb_callback(snddev_info *d, int reason)
*/
int swap = 1 ; /* default... */
- if (rd) {
- /* need not to swap if channel is already correct */
- if ( d->rec_fmt == AFMT_S16_LE && b->chan > 4 )
+ if (d->play_fmt == 0) {
+ /* do whatever the read channel wants */
+ if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 )
swap = 0;
- if ( d->rec_fmt != AFMT_S16_LE && b->chan < 4 )
+ if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 )
swap = 0;
- if (swap && (d->flags & SND_F_WRITING || d->dbuf_out.dl)) {
- /* cannot swap if writing is already active */
- DDB(printf("sorry, DMA channel unavailable\n"));
- swap = 0;
- break; /* XXX should return an error */
- }
} else {
- /* need not to swap if channel is already correct */
- if ( d->play_fmt == AFMT_S16_LE && b->chan > 4 )
+ /* privilege the write channel */
+ if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 )
swap = 0;
- if ( d->play_fmt != AFMT_S16_LE && b->chan < 4 )
+ if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
- if (swap && ( d->flags & SND_F_READING || d->dbuf_in.dl)) {
- /* cannot swap if reading is already active */
- DDB(printf("sorry, DMA channel unavailable\n"));
- swap = 0;
- break ; /* XXX should return an error */
+ if ( d->rec_fmt ) {
+ /* check for possible config errors. */
+ if (d->rec_fmt == d->play_fmt) {
+ DDB(printf("sorry, read DMA channel unavailable\n"));
+ }
}
}
-
+ DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n",
+ d->play_fmt, d->rec_fmt, swap);)
if (swap) {
int c = d->dbuf_in.chan ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
- reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
- reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
- DEB(printf("START dma chan: play %d, rec %d\n",
- d->dbuf_out.chan, d->dbuf_in.chan));
+ }
}
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ break ;
+ case SND_CB_START : /* called with int disabled */
+ if (d->bd_flags & BD_F_SB16) {
+ u_char c, c1 ;
/*
* 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
@@ -408,7 +407,7 @@ sb_callback(snddev_info *d, int reason)
if ( b->chan > 4 ) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
- l /= 2 ;
+ l /= 2 ;
} else {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA8 ;
c1 = 0 ;
@@ -421,28 +420,25 @@ sb_callback(snddev_info *d, int reason)
sb_cmd3(d->io_base, c1 , l - 1) ;
} else if (d->bd_flags & BD_F_ESS) {
/* XXX this code is still incomplete */
- sb_cmd2(d->io_base, 0xb8, rd ? 0x0e : 0x04 ); /* auto dma */
- sb_cmd2(d->io_base, 0xa8, 2 /* chans */ );
- sb_cmd2(d->io_base, 0xb9, 2); /* demand mode */
- /*
- input: 0xb8 -> 0x0e ;
- 0xa8 -> channels
- 0xb9 -> 2
- mono,U8: 51, d0
- mono,S16 71, f4
- st, U8 51, 98
- st, S16 71, bc
- */
- } else { /* SBPro */
+ } else { /* SBPro -- stereo not supported */
u_char c ;
if (!rd)
sb_cmd(d->io_base, DSP_CMD_SPKON);
- /* code for the SB2 and SB3 */
+ /* code for the SB2 and SB3, only MONO */
if (d->bd_flags & BD_F_HISPEED)
- c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
+ c = (rd) ? 0x98 : 0x90 ;
else
- c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
- sb_cmd3(d->io_base, c , l - 1) ;
+ c = (rd) ? 0x2c : 0x1c ;
+ /*
+ * some ESS extensions -- they can do 16 bits
+ */
+ if ( (rd && d->rec_fmt == AFMT_S16_LE) ||
+ (!rd && d->play_fmt == AFMT_S16_LE) ) {
+ c |= 1;
+ l /= 2 ;
+ }
+ sb_cmd3(d->io_base, 0x48 , l - 1) ;
+ sb_cmd(d->io_base, c ) ;
}
break;
@@ -451,7 +447,7 @@ sb_callback(snddev_info *d, int reason)
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
- cmd = DSP_CMD_DMAPAUSE_16 ;
+ cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
d->flags |= SND_F_INIT ;
@@ -485,9 +481,9 @@ sb_reset_dsp(int io_base)
{
int loopc;
- outb(DSP_RESET, 1);
+ outb(io_base + SBDSP_RST, 1);
DELAY(100);
- outb(DSP_RESET, 0);
+ outb(io_base + SBDSP_RST, 0);
for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++)
DELAY(30);
@@ -568,7 +564,9 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
d->name, dev->id_unit, d->irq);
else
sb_setmixer(io_base, IRQ_NR, x);
-
+ if (d->dbuf_out.chan == d->dbuf_in.chan) {
+ printf("WARNING: sb: misconfigured secondary DMA channel\n");
+ }
sb_setmixer(io_base, DMA_NR, (1 << d->dbuf_out.chan) | (1 << d->dbuf_in.chan));
break ;
@@ -599,41 +597,22 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
DELAY(20);
}
- if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
+ if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) {
+ /* the ESS488 can be treated as an SBPRO */
printf("ESS488 (rev %d)\n", ess_minor & 0x0f);
- else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
- if ( (ess_minor & 0xf) >= 8 )
- printf("ESS1688 (rev %d)\n", ess_minor & 0x0f);
- else
- printf("ESS688 (rev %d)\n", ess_minor & 0x0f);
- } else
break ;
- d->bd_id = (ess_major << 8) | ess_minor ;
- d->bd_flags |= BD_F_ESS;
- /*
- * ESS-specific initialization, taken from OSSFree 3.8
- */
- {
- static u_char irqs[16] = {
- 0, 0, 0x50, 0, 0, 0x54, 0, 0x58,
- 0, 0x50, 0x5c, 0, 0, 0, 0, 0 };
- static u_char drqs[8] = {
- 0x51, 0x52, 0, 0x53, 0, 0, 0, 0 };
- x = irqs[d->irq & 0xf];
- if (x == 0)
- printf("ESS : invalid IRQ %d\n", d->irq);
- else {
- sb_cmd(io_base, 0xb1 ); /* set IRQ */
- sb_cmd(io_base, x );
-
- x = drqs[ d->dbuf_out.chan & 0x7 ];
- if (x == 0)
- printf("ESS : invalid DRQ %d\n", d->dbuf_out.chan);
- else {
- sb_cmd(io_base, 0xb2 ); /* set DRQ */
- sb_cmd(io_base, x );
- }
- }
+ } else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
+ int rev = ess_minor & 0xf ;
+ if ( rev >= 8 )
+ printf("ESS1868 (rev %d)\n", rev);
+ else
+ printf("ESS688 (rev %d)\n", rev);
+ d->audio_fmt |= AFMT_S16_LE; /* in fact it is U16_LE */
+ break ; /* XXX */
+ } else {
+ printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
+ ess_major, ess_minor);
+ break ;
}
}
@@ -679,8 +658,8 @@ sb_cmd(int io_base, u_char val)
int i;
for (i = 0; i < 1000 ; i++) {
- if ((inb(DSP_STATUS) & 0x80) == 0) {
- outb(DSP_COMMAND, val);
+ if ((inb(io_base + SBDSP_STATUS) & 0x80) == 0) {
+ outb(io_base + SBDSP_CMD, val);
return 1;
}
if (i > 10)
@@ -712,32 +691,23 @@ sb_cmd2(int io_base, u_char cmd, int val)
return 0;
}
+/*
+ * in the SB, there is a set of indirect "mixer" registers with
+ * address at offset 4, data at offset 5
+ */
void
sb_setmixer(int io_base, u_int port, u_int value)
{
u_long flags;
flags = spltty();
- outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- outb(MIXER_DATA, (u_char) (value & 0xff));
+ outb(io_base + 5, (u_char) (value & 0xff));
DELAY(10);
splx(flags);
}
-u_int
-sb_get_byte(int io_base)
-{
- int i;
-
- for (i = 1000; i; i--)
- if (inb(DSP_DATA_AVAIL) & 0x80)
- return inb(DSP_READ);
- else
- DELAY(20);
- return 0xffff;
-}
-
int
sb_getmixer(int io_base, u_int port)
{
@@ -745,15 +715,29 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
- outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- val = inb(MIXER_DATA);
+ val = inb(io_base + 5);
DELAY(10);
splx(flags);
return val;
}
+u_int
+sb_get_byte(int io_base)
+{
+ int i;
+
+ for (i = 1000; i; i--)
+ if (inb(DSP_DATA_AVAIL) & 0x80)
+ return inb(DSP_READ);
+ else
+ DELAY(20);
+ return 0xffff;
+}
+
+
/*
* various utility functions for the DSP
@@ -806,11 +790,12 @@ dsp_speed(snddev_info *d)
}
/*
- * only some models can do stereo, and only if not
+ * This is code for the SB3.x and lower.
+ * Only some models can do stereo, and only if not
* simultaneously using midi.
+ * At the moment we do not support either...
*/
- if ( (d->bd_id & 0xff00) < 0x300 || d->bd_flags & BD_F_MIDIBUSY)
- d->flags &= ~SND_F_STEREO;
+ d->flags &= ~SND_F_STEREO;
/*
* here enforce speed limitations.
@@ -830,26 +815,12 @@ dsp_speed(snddev_info *d)
RANGE(speed, 4000, max_speed);
- /*
- * Logitech SoundMan Games and Jazz16 cards can support 44.1kHz
- * stereo
- */
-#if !defined (SM_GAMES)
- /*
- * Max. stereo speed is 22050
- */
- if (d->flags & SND_F_STEREO && speed > 22050 && !(d->bd_flags & BD_F_JAZZ16))
- speed = 22050;
-#endif
-
- if (d->flags & SND_F_STEREO)
+ if (d->flags & SND_F_STEREO) /* really unused right now... */
speed *= 2;
/*
* Now the speed should be valid. Compute the value to be
* programmed into the board.
- *
- * XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
@@ -878,7 +849,7 @@ dsp_speed(snddev_info *d)
speed = (1000000 + tmp / 2) / tmp;
}
- if (d->flags & SND_F_STEREO)
+ if (d->flags & SND_F_STEREO) /* really unused right now... */
speed /= 2;
d->play_speed = d->rec_speed = speed;
@@ -1024,6 +995,79 @@ sb_mixer_set(snddev_info *d, int dev, int value)
*/
#if NPNP > 0
+static char *ess1868_probe(u_long csn, u_long vend_id);
+static void ess1868_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device ess1868 = {
+ "ESS1868",
+ ess1868_probe,
+ ess1868_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, ess1868);
+
+static char *
+ess1868_probe(u_long csn, u_long vend_id)
+{
+ /*
+ * pnp X 1 os enable drq0 3 irq0 12 port0 0x240
+ */
+ if (vend_id == 0x68187316) {
+ struct pnp_cinfo d ;
+ read_pnp_parms ( &d , 1 ) ;
+ if (d.enable == 0) {
+ printf("This is an ESS1868, but LDN 1 is disabled\n");
+ return NULL;
+ }
+ return "ESS1868" ;
+ }
+ return NULL ;
+}
+
+static void
+ess1868_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;
+
+#if 0
+ read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
+ 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 , 0 ); /* read config base */
+ tmp_d.conf_base = d.port[0];
+ write_pnp_parms ( &d , 0 );
+#endif
+
+ read_pnp_parms ( &d , 1 ) ;
+ dev->id_iobase = d.port[0];
+ d.port[1] = 0 ;
+ d.port[2] = 0 ;
+ write_pnp_parms ( &d , 1 );
+ enable_pnp_card();
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = 0 /* DV_F_DUAL_DMA | (d.drq[1] ) */;
+
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+ 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);
@@ -1155,6 +1199,7 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
read_pnp_parms ( &d , 0 ) ;
d.port[1] = 0 ; /* only the first address is used */
dev->id_iobase = d.port[0];
+ tmp_d.synth_base = d.port[2];
write_pnp_parms ( &d , 0 );
enable_pnp_card();
diff --git a/sys/i386/isa/snd/CARDS b/sys/i386/isa/snd/CARDS
new file mode 100644
index 0000000..af6048e
--- /dev/null
+++ b/sys/i386/isa/snd/CARDS
@@ -0,0 +1,178 @@
+In this file I will try to build a database of cards supported by this
+driver. I also include the command to use for manual configuration of
+the card in case your BIOS is not PnP-aware. Of course it is your
+responsibility to pick up free prot ranges and irq and drq channels.
+
+For PnP cards, I also include the vendor_id and serial numbers of
+cards I have encountered. Underscores in the PnP id mean that there is
+a large variety of values in those positions.
+
+Finally, where available, I have put the URL where you can find the
+data sheets of the chip.
+
+
+CS4236: PnP id 0x36__630e
+CS4237: PnP id 0x37__630e
+
+ http://www.crystal.com/ 4237full.pdf
+
+ pnp 1 0 os enable port0 0x534 port2 0x220 irq0 5 drq0 1 drq1 3
+
+ work like a charm. All modes, including full duplex, supported in
+ MSS mode.
+
+ NOTE: cards based on the 4237, and possibly the 4236 as well,
+ connect the CD to AUX2. When used in MODE1/2 (as this driver
+ does) there is no route in the input mixer from AUX2 to the
+ ADC. So, to record directly from the CD, the only way is
+ to set the input to SOUND_MASK_IMIX.
+
+CS4232: PnP id 0x3242630e
+
+ pnp 1 0 os enable port0 0x534 port2 0x220 irq0 5 drq0 1 drq1 3
+
+ This chip is reported as broken in the OSS documentation. As a
+ matter of fact, on my Intel Zappa motherboard, I have problems in
+ make it use the secondary DMA channel. I have it working in
+ half duplex (both capture and playback) in SB3.2 emulation,
+ and working in playback mode in MSS emulation.
+ Also have reports from some user that it works ok.
+
+OPTi931: PnP id 0x3109143e
+
+ http://www.opti.com/ opti931_21.pdf
+
+ pnp 1 1 os enable port0 0x534 port2 0x220 port3 0xe0d irq0 10 drq0 1 drq1 6
+
+ 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.
+ In SB emulation mode the driver does not work yet (maybe I do
+ not initialize it the right way). It is not a major concern
+ anyways.
+ I am strongly convinced of a couple of bugs in the chip. I have
+ sent email to OPTI but got no reply so far. The bugs are:
+ - you cannot set both playback and capture format to use
+ a companded mode (ULAW, ALAW). If you do, the card will screw
+ up on the capture section.
+ The driver has a partial fix in software: when using ULAW, it
+ programs ULAW on the playback section, U8 on the capture, and
+ 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.
+
+SB16 PnP: PnP id 0x__008c0e
+
+ http://www.creative.com sbhwpg.pdf or SBHWPG.EXE
+
+ 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.
+
+ 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
+ the new set of ioctl to use separate data formats on the two
+ channels (the vat driver does this). Of course, quality in 8-bit
+ 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
+ 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
+ positive reports.
+
+ 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.
+
+ASound:
+ Avance Logic makes SB clones, and apparently documentation
+ is available at
+
+ http://www.realtek.com.tw/cp/cp.html
+
+ these card should be recognised as SB16 clones.
+
+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.)
+
+ pnp 1 0 os enable port0 0x220 port1 0x530 irq0 5 drq0 1 drq1 5
+
+ 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)
+
+Ensoniq Soundscape VIVO
+
+ pnp 1 0 os enable port0 0x220 port1 0x530 irq0 5 drq0 1 drq1 5
+
+ this card emulates a WSS or SB. Have reports that it works.
+
+GusPnP: PnP id 0x0100561e
+
+ pnp 1 0 os enable port0 0x220 port1 0x320 port2 0x32c irq0 5 drq0 7 drq1 5
+
+ It works in U8 and S16 mode, ulaw support still not working.
+
+
+NOT WORKING YET:
+
+OPTI925: PnP id 0x2509143e
+
+ 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
+
+ I have this card but it is still unsupported.
+
+OPTI930:
+
+ should work as an MSS clone, but support for it is not implemented
+ yet.
+
+ESS1868
+ESS688
+
+ http://www.esstech.com
+
+ pnp 1 1 os enable irq0 7 drq0 1 port1 0x220
+
+ There used to be documentation for the 1868 on the ESS site
+ (files ds1868b1.pdf and ds1868b2.pdf) but I am unable to find
+ it now (980122). I have asked ESS but no reply so far.
+
+ partly supported in SB emulation.
+
+ (the ESS688 is used on many notebooks. It is probably able to do 8
+ and 16-bit, half duplex).
+
+PCI cards:
+
+ 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.
+
diff --git a/sys/i386/isa/snd/README b/sys/i386/isa/snd/README
index ac45b22..4c8119e 100644
--- a/sys/i386/isa/snd/README
+++ b/sys/i386/isa/snd/README
@@ -1,126 +1,288 @@
--- A new FreeBSD audio driver ---
by Luigi Rizzo (luigi@iet.unipi.it)
+This is a new, completely rewritten, audio driver for FreeBSD.
+Only "soundcard.h" has remained largely similar to the original
+OSS/Voxware header file, mostly for compatibility with existing
+applications.
+
+This driver tries to cover those areas where the Voxware 3.0 driver
+is mostly lacking: full-duplex, audio applications, modern (mostly
+PnP) cards. For backward compatibility, the driver implements most
+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.
+
+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).
+Such modified driver modules are present in the misc/ directory
+for several applications.
+
+This file gives quick information on how to install the driver. For
+more info you are invited to look at the doc/ directory where you
+can find more documentation (in Latex and Postscript) on the driver
+and how to extend it. I am afraid there is not yet a complete set of
+manual pages for using the driver. The files in doc/ should give a
+good idea of what the driver is supposed to do and how, the Voxware
+documentation should be of some help, and the various driver modules
+in misc/ should also give some help.
-This is an experimental version of the audio driver for FreeBSD.
-I have almost completely rewritten the main parts of the code,
-starting from the Voxware 3.5-alpha release with patches from
-Amancio Hasty. The only file which is largely similar to the original
-ones is "soundcard.h" (for compatibility reasons, since it contains
-the definition of all the ioctls).
+Also note that you might need the PnP code (pnp971020.tgz), which
+as of January 1998 has been incorporated in -current and -stable
+versions of FreeBSD.
-Visit http://www.iet.unipi.it/~luigi/FreeBSD.html for the latest
-information on the drivers. There you can obtain the latest source
-package that includes documentation on the design of the driver.
+Updated versions of this code will available at the following URL:
+ http://www.iet.unipi.it/~luigi/FreeBSD.html
-CARD SUPPORT INFORMATION:
+Please READ CAREFULLY this file (and possibly the LaTeX documentation)
+to build a working kernel. The configuration is DIFFERENT (and
+hopefully much simpler) from the original Voxware driver. The
+relevant steps are indicated at "---INSTALLATION---".
-For PnP cards, I also include the vendor_id and serial numbers of
-cards I have encountered.
+This code (and the associated patches) should work unmodified on 2.2.5
+and in general on 2.2.X versions of FreeBSD. Minor modifications will be
+necessary to make the code work on 2.1.X. Finally, depending on the
+version of their system, 3.0 users might need to uncomment the
+following line in snd/sound.h:
+
+ /* #define USE_POLL */
+
+This archive includes:
+* the main files for the driver, in this directory, which must be
+ moved to /sys/i386/isa/snd;
+* miscellaneous, but important, files in the "misc" subdirectory;
+* documentation, in the "doc" subdirectory;
+
+CARDS:
+
+ The driver supports most clones of WSS, SB16 and SBPro cards.
+ This includes those based on the Crystal CS423x, OPTI931, GUSPnP,
+ Yamaha, SB16/32 (both plain ISA, PnP, and the various AWExx).
+ Many PnP cards are directly recognised, for others you might need
+ manual configuration. See the file "CARDS" for more details.
-CS4236: PnP id 0x3642630e
-CS4237: PnP id 0x3742630e
+APPLICATIONS:
+
+ In general, most applications which use /dev/audio or /dev/dsp
+ work unmodified or with a specially-supplied module.
+
+ UNMODIFIED:
+ - raplayer (Real Audio Player), rvplayer (linux version)
+ - xboing
+ - xanim
+ - various mpeg players (mpg123, amp, ...);
- works like a charm. All modes, including full duplex, supported in
- MSS mode.
+ 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 (driver included);
+ - nas, full duplex (driver included);
+ - timidity, a software midi-to-pcm converter;
-CS4232: PnP id 0x3242630e
+ NOT WORKING
+ - xquake (we do not support mmapped buffers yet);
- this chip is reported as broken in the OSS documentation. As a
- matter of fact, on my Intel Zappa motherboard, I have problems in
- make it use the secondary DMA channel. I have it working in
- half duplex (both capture and playback) in SB3.2 emulation,
- and working in playback mode in MSS emulation.
-OPTi931: PnP id 0x3109143e
+ ---INSTALLATION---
- 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.
- In SB emulation mode the driver does not work yet (maybe I do
- not initialize it the right way). It is not a major concern
- anyways.
- I am strongly convinced of a couple of bugs in the chip. I have
- sent email to OPTI but got no reply so far. The bugs are:
- - you cannot set both playback and capture format to use
- a companded mode (ULAW, ALAW). If you do, the card will screw
- up on the capture section.
- The driver has a partial fix in software: when using ULAW, it
- programs ULAW on the playback section, U8 on the capture, and
- 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.
+In order to use this driver, you need FreeBSD 2.2 or above (I have
+tested this code on 2.2.1 and 2.2.5, and have many reports of the
+driver working on various vintages of 3.0 ). The enclosed patch
+file refers to FreeBSD 2.2.5. Your mileage may vary -- e.g. both
+-stable and -current at the time of this writing (Jan.98) have
+included the modifications to isa.c .
-SB16 PnP: PnP id 0xXX008c0e
+Installation requires the following steps:
- 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.
+ * install the PnP support files (pnp971020.tgz). This is optional,
+ in case you don't you might need to do some simple modifications
+ to the file because pnp.h might not exist.
- 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
- the new set of ioctl to use separate data formats on the two
- channels (the vat driver does this). Of course, quality in 8-bit
- is much lower than in 16-bit.
+ * unpack the content of this archive in /sys/i386/isa/snd/
+
+ * if you are running a version of 2.2 earlies than 2.2.5-RELEASE,
+ apply the patches in "patches.22x" to isa.c
- Full duplex operation is unsupported by Creative. It seems to
- work, although on my Vibra16 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
- positive reports.
+ * patch the following system files using the patches in
+ "patches.225" :
-Yamaha SA2
+ /sys/i386/isa/isa.c
+ a couple of new functions have been added;
+
+ /sys/i386/conf/files.i386
+ lines related to this audio driver have been added;
- this card emulates a WSS or SB. Have reports that it works, although
- it has mixer problems (maybe the driver has the wrong set of
- parameters).
+ * update file soundcard.h by copying the one in this directory into:
-Ensoniq Soundscape VIVO
+ /sys/i386/include/soundcard.h
+ /usr/include/machine/soundcard.h
- this card emulates a WSS or SB. Have reports that it works.
+ The new file should be compatible with the old one, but has
+ the definition of new ioctl() calls which are implemented by
+ this driver.
-GusPnP: PnP id 0x0100561e
+ * add the following lines to your kernel configuration file:
- I have code to recognize the board as MSS, but some initializations
- of the IW chip is still missing, so the card is not supported yet.
- I am working on this problem...
+ controller pnp0 # this is required for PnP support
-OPTI924: PnP
+ device pcm0 at isa ? port? tty irq N drq D flags F vector pcmintr
- I have this card but it is still unsupported.
+ where
-OPTI925: PnP id 0x2509143e
+ N is the IRQ address used by the sound card,
+ D is the primary DMA channel used by the sound card,
+ F is used to specify a number of options, in particular:
+ bit 2..0 secondary DMA channel;
+ bit 4 set if the board uses two dma channels;
+ bit 15..8 board type, overrides autodetection; leave it
+ zero if don't know what to put in (and you don't,
+ since this is unsupported at the moment...).
- there is code to recognize it as a SB clone. I have reports that
- it probes ok, but not sure if it works.
+ The code will probe for common port addresses (0x220, 0x240
+ for SB and clonse, 0x530 for WSS and clones), so you don't need
+ to specify them if your system uses one of them. In case you
+ do, note that for WSS cards the code assumes that the board
+ occupies 8 IO addresses, the first four used to configure
+ IRQ/DRQ, and the other four to access codec registers. Many
+ boards (including all the ones I have) do not have registers
+ to program IRQ and DRQ, so they really start at 0x534... yet
+ I decided to use the old convention for historical reasons.
-OPTI930:
+ You can use multiple sound cards, in which case you need more
+ lines like
- should work as an MSS clone, but support for it is not implemented
- yet.
+ device pcm1 at isa ? port? tty irq N drq D flags F vector pcmintr
+ device pcm2 at isa ? port? tty irq N drq D flags F vector pcmintr
+ ...
-ESS1868
+ EXAMPLES: a typical "device" line for the SB16 (full duplex) is
- this card is not supported yet. It might work in SB emulation but
- am not sure.
+ device pcm0 at isa ? port? tty irq 5 drq 1 flags 0x15 vector pcmintr
-ESS688
+ The driver will check at the default addresses (or the one you
+ specify) which type of SoundBlaster you have (1.5, 2.0, 3.X
+ aka SBPro, 4.X aka SB16) and use the correct commands. You
+ _do_not_ need to specify different drivers (sb,sbpro,sbxvi) as
+ it was the case (and a big source of confusion) in the previous
+ sound driver.
- this card is used on many notebook. I don't have docs on this card
- so I cannot support it. Pointers to the data sheets are welcome.
+ For a WSS-compatible codec (non PnP) working in full duplex using
+ dma channels 1 and 3, you can specify:
-PCI cards:
+ device pcm0 at isa ? port 0x530 tty irq 7 drq 1 flags 0x13 vector pcmintr
- 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.
+ (0x530 is a default if no port address is specified). The
+ "flags 0x13" specifies that you have a dual dma board with
+ channel 3 as secondary DMA channel.
+
+ * build the kernel using the following steps
+
+ config MYKERNEL
+ cd /sys/compile/MYKERNEL
+ make depend
+ make
+
+ * PnP support:
+
+ For PnP cards, only the line for "pcm0" is needed (the code
+ will allocate entries for more cards if found), but IT MUST
+ INCLUDE ALL FIELDS, including "vector pcmintr". You can use
+ the following line:
+
+ device pcm0 at isa ? port? tty irq 7 drq 1 vector pcmintr
+
+ NOTE that:
+ - the parameters for the PnP device(s) will be read from the
+ configuration of the card(s); they are usually assigned by
+ the bios, and there is no way (at the moment) to override
+ them, so if you have a broken (or no) PnP bios your only
+ chance is to patch the pnp attach code in the driver for your
+ card (ad1848.c, sb_dsp.c, clones.c) and write there the
+ parameters you want;
+ - The driver will assign unit numbers to the PnP cards starting
+ from the next free one (e.g. 1, 2, ...) same as it is done
+ with PCI devices which are clones of ISA devices.
+
+ The driver assumes a working PnP bios, which will assign correct
+ addresses and IO and DMA channels to the devices. If you do not
+ have a PnP-aware BIOS, you must boot with the -c option and assign
+ addresses manually the first time. The general commands are described in
+ the pnp README file. For the card-specific commands check in the
+ file CARDS.
+
+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:
+
+ - relevant lines in your config file;
+ - dmesg output
+ - possibly, pnpinfo output
+
+Please send all the above in plain text, not as a mime attachment.
+
+Common mistakes:
+
+* you are trying to use /dev/audio0 instead of /dev/audio1
+ For technical reasons, a PnP device is attached as unit 1 instead
+ of unit 0 -- most applications are programmed to access the audio
+ hardware through /dev/audio, /dev/dsp, /dev/mixer which are in turn
+ symlinks to the correct device entries. Check them and possibly fix
+ these symlinks in /dev
+
+* you have used a wrong config line
+
+ The configuration of this driver is different from the Voxware one.
+ Please read the information in this file carefully.
+
+* your BIOS is leaving the card disabled.
+
+ Some BIOSes do not initialize the card, or leave it disabled. At the
+ moment, the fix is to use the pnp code booting with "-c" and set the
+ correct port, irq, drq etc for the card. See the PnP documentation.
+
+* your card is not recognized.
+
+ This driver supports a large, but still limited, number of cards,
+ mostly SB and WSS clones. Other cards may or may not work depending
+ on how closely they emulate these devices. In case, send me an email
+ with the info indicated above.
+
+* the mixer does not work well
+
+ Different cards have different connections to the mixer, so it might
+ well be that to control the volume of your CD you have to use the FM
+ port, etc. Also, on some cards the volume might be lower than you
+ expect. The mixer code still does not try to exploit the features of
+ each card, and it just provides basic functionalities.
+
+--- ACKNWOLEDGEMENTS ---
+
+Several people helped, directly or indirectly, in the development of
+this driver. In particular I would like to thank:
+
+ * Hannu Savolainen (the Voxware author) for making his code
+ available. It was a very good source of technical info;
+ * Amancio Hasty for continuous support and his work on guspnp code;
+ * 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.
+ * and many people who had the patience to try the driver
+ on their cards and report success/fauilure and useful
+ information.
+
+It was certainly helpful to have the data sheets for some of the
+devices I support available on the net, especially in the (unfortunately
+rare) cases where the data sheets matched the actual behaviour of
+the product. Too bad that no one of the chip/card manufacturers I
+have contacted by email regarding missing or inconsistent documentation
+on their products did even care to reply to my messages.
diff --git a/sys/i386/isa/snd/ad1848.c b/sys/i386/isa/snd/ad1848.c
index a6bf624..26eaee5 100644
--- a/sys/i386/isa/snd/ad1848.c
+++ b/sys/i386/isa/snd/ad1848.c
@@ -94,7 +94,7 @@ snddev_info mss_op_desc = {
NULL /* mss_read */,
NULL /* mss_write */,
mss_ioctl,
- sndpoll /* mss_poll */,
+ sndselect /* mss_select */,
mss_intr,
mss_callback ,
@@ -133,8 +133,8 @@ mss_probe(struct isa_device *dev)
bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) );
if (dev->id_iobase == -1) {
dev->id_iobase = 0x530;
- printf("mss_probe: no address supplied, try default 0x%x\n",
- dev->id_iobase);
+ BVDDB(printf("mss_probe: no address supplied, try default 0x%x\n",
+ dev->id_iobase));
}
if (snd_conflict(dev->id_iobase))
return 0 ;
@@ -150,13 +150,13 @@ mss_probe(struct isa_device *dev)
tmp = inb(dev->id_iobase + 3);
if (tmp == 0xff) { /* Bus float */
- DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
+ BVDDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
dev->id_flags &= ~DV_F_TRUE_MSS ;
goto mss_probe_end;
}
tmp &= 0x3f ;
if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) {
- DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
+ BVDDB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
dev->id_iobase, inb(dev->id_iobase + 3)));
return 0;
}
@@ -276,10 +276,12 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
d->flags |= SND_F_BUSY_DSP ;
else if (dev == SND_DEV_DSP16)
d->flags |= SND_F_BUSY_DSP16 ;
- if ( ! (d->flags & SND_F_BUSY) ) {
+ if ( d->flags & SND_F_BUSY )
+ splx(s); /* device was already set, no need to reinit */
+ else {
/*
* device was idle. Do the necessary initialization,
- * but no need keep interrupts blocked since this device
+ * but no need keep interrupts blocked.
* will not get them
*/
@@ -313,7 +315,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p)
}
ask_init(d); /* and reset buffers... */
}
- splx(s);
return 0 ;
}
@@ -343,14 +344,15 @@ mss_close(dev_t dev, int flags, int mode, struct proc * p)
d->flags &= ~SND_F_BUSY_DSP ;
else if (dev == SND_DEV_DSP16)
d->flags &= ~SND_F_BUSY_DSP16 ;
- if ( ! (d->flags & SND_F_BUSY_ANY) ) { /* last one ... */
+ if ( d->flags & SND_F_BUSY_ANY ) /* still some device open */
+ splx(s);
+ else { /* last one */
d->flags |= SND_F_CLOSING ;
splx(s); /* is this ok here ? */
snd_flush(d);
outb(io_Status(d), 0); /* Clear interrupt status */
d->flags = 0 ;
}
- splx(s);
return 0 ;
}
@@ -435,6 +437,7 @@ mss_callback(snddev_info *d, int reason)
ad_write_cnt(d, 30, cnt);
break ;
+
case SND_CB_STOP :
case SND_CB_ABORT : /* XXX check this... */
m = ad_read(d,9) ;
@@ -548,11 +551,11 @@ again:
c=mc11 = FULL_DUPLEX(d) ? opti_read(d->conf_base, 11) : 0xc ;
mc11 &= 0x0c ;
if (c & 0x10) {
- printf("Warning: CD interrupt\n");
+ DEB(printf("Warning: CD interrupt\n");)
mc11 |= 0x10 ;
}
if (c & 0x20) {
- printf("Warning: MPU interrupt\n");
+ DEB(printf("Warning: MPU interrupt\n");)
mc11 |= 0x20 ;
}
if (mc11 & masked)
@@ -578,7 +581,7 @@ again:
}
opti_write(d->conf_base, 11, ~mc11); /* ack */
if (--loops) goto again;
- printf("xxx too many loops\n");
+ DEB(printf("xxx too many loops\n");)
}
/*
@@ -637,13 +640,13 @@ gus_readw(int io_base, u_char reg)
static int
AD_WAIT_INIT(snddev_info *d, int x)
{
- int n = 0; /* to shut up the compiler... */
+ int arg=x, n = 0; /* to shut up the compiler... */
for (; x-- ; )
if ( (n=inb(io_Index_Addr(d))) & IA_BUSY)
DELAY(10);
else
return n ;
- printf("AD_WAIT_INIT FAILED 0x%02x\n", n);
+ printf("AD_WAIT_INIT FAILED %d 0x%02x\n", arg, n);
return n ;
}
@@ -1061,7 +1064,7 @@ mss_detect(struct isa_device *dev)
else
break ;
if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */
- DEB(printf("mss_detect error, busy still set (0x%02x)\n",
+ BVDDB(printf("mss_detect error, busy still set (0x%02x)\n",
inb(io_Index_Addr(d))));
return 0;
}
@@ -1076,7 +1079,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 0) ;
tmp2 = ad_read(d, 1) ;
if ( tmp1 != 0xaa || tmp2 != 0x45) {
- DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
+ BVDDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
tmp1, tmp2));
return 0;
}
@@ -1087,7 +1090,7 @@ mss_detect(struct isa_device *dev)
tmp2 = ad_read(d, 1) ;
if (tmp1 != 0x45 || tmp2 != 0xaa) {
- DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
+ BVDDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
return 0;
}
@@ -1101,7 +1104,7 @@ mss_detect(struct isa_device *dev)
tmp1 = ad_read(d, 12);
if ((tmp & 0x0f) != (tmp1 & 0x0f)) {
- DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
+ BVDDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
tmp1, tmp));
return 0;
}
@@ -1112,7 +1115,7 @@ mss_detect(struct isa_device *dev)
* 0x0A=RevC. also CS4231/CS4231A and OPTi931
*/
- printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);
+ BVDDB(printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);)
/*
* The original AD1848/CS4248 has just 16 indirect registers. This
@@ -1125,7 +1128,7 @@ mss_detect(struct isa_device *dev)
for (i = 0; i < 16; i++)
if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) {
- DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
+ BVDDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
i, tmp1, tmp2));
/*
* note - this seems to fail on the 4232 on I11. So we just break
@@ -1148,7 +1151,7 @@ mss_detect(struct isa_device *dev)
name = "CS4248" ; /* Our best knowledge just now */
}
if ((tmp1 & 0xf0) == 0x00) {
- printf("this should be an OPTi931\n");
+ BVDDB(printf("this should be an OPTi931\n");)
} else if ((tmp1 & 0xc0) == 0xC0) {
/*
* The 4231 has bit7=1 always, and bit6 we just set to 1.
@@ -1162,14 +1165,13 @@ mss_detect(struct isa_device *dev)
ad_write(d, 0, 0xaa);
if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */
- DEB(printf("mss_detect error - step H(%x)\n", tmp1));
+ BVDDB(printf("mss_detect error - step H(%x)\n", tmp1));
return 0;
}
/*
* Verify that some bits of I25 are read only.
*/
- DEB(printf("mss_detect() - step I\n"));
tmp1 = ad_read(d, 25); /* Original bits */
ad_write(d, 25, ~tmp1); /* Invert all bits */
if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) {
@@ -1240,7 +1242,7 @@ mss_detect(struct isa_device *dev)
break ;
default: /* Assume CS4231 */
- printf("unknown id 0x%02x, assuming CS4231\n", id);
+ BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);)
d->bd_id = MD_CS4231;
}
@@ -1249,7 +1251,7 @@ mss_detect(struct isa_device *dev)
}
}
- DEB(printf("mss_detect() - Detected %s\n", name));
+ BVDDB(printf("mss_detect() - Detected %s\n", name));
strcpy(d->name, name);
dev->id_flags &= ~DV_F_DEV_MASK ;
dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ;
@@ -1390,7 +1392,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
return ;
}
snddev_last_probed = &tmp_d;
- if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
+ if (d.flags) { /*** use sb-compatible codec ***/
dev->id_alive = 16 ; /* number of io ports ? */
tmp_d = sb_op_desc ;
if (vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) {
@@ -1455,6 +1457,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
dev->id_intr = pcmintr ;
dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
+ tmp_d.synth_base = d.port[1]; /* XXX check this for yamaha */
pcmattach(dev);
}
@@ -1508,7 +1511,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
enable_pnp_card();
snddev_last_probed = &tmp_d;
- tmp_d = d.flags & DV_PNP_SBCODEC ? sb_op_desc : mss_op_desc ;
+ tmp_d = d.flags ? sb_op_desc : mss_op_desc ;
strcpy(tmp_d.name, name);
@@ -1526,10 +1529,11 @@ opti931_attach(u_long csn, u_long vend_id, char *name,
dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */
tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */
tmp_d.alt_base = d.port[2];
+ tmp_d.synth_base = d.port[1];
opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ );
ad_write (&tmp_d, 10, 2); /* enable interrupts */
- if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */
+ if (d.flags) { /* 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...
@@ -1598,7 +1602,6 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
read_pnp_parms ( &d , 0 ) ;
/* d.irq[1] = d.irq[0] ; */
- printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) );
pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */
write_pnp_parms ( &d , 0 );
@@ -1623,7 +1626,6 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
DELAY(1000 * 30);
/* release reset and enable DAC */
gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 );
- printf("resetting the gus...\n");
DELAY(1000 * 30);
/* end of reset */
@@ -1645,7 +1647,7 @@ guspnp_attach(u_long csn, u_long vend_id, char *name,
/* enable access to hidden regs */
tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ );
gus_write(tmp_d.conf_base, 0x5b , tmp | 1 );
- printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );
+ BVDDB(printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );)
strcpy(tmp_d.name, name);
diff --git a/sys/i386/isa/snd/dmabuf.c b/sys/i386/isa/snd/dmabuf.c
index c6b1c57..8c647cb 100644
--- a/sys/i386/isa/snd/dmabuf.c
+++ b/sys/i386/isa/snd/dmabuf.c
@@ -162,9 +162,8 @@ dsp_wrintr(snddev_info *d)
b->dl, b->rp, b->rl, b->fp, b->fl));
/*
* start another dma operation only if have ready data in the buffer,
- * there is no pending abort, have a full-duplex device
- * or have half duplex device
- * and there is no * pending op on the other side.
+ * there is no pending abort, have a full-duplex device, or have a
+ * half duplex device and there is no pending op on the other side.
*
* Force transfers to be aligned to a boundary of 4, which is
* needed when doing stereo and 16-bit. We could make this
@@ -176,12 +175,24 @@ dsp_wrintr(snddev_info *d)
int l = min(b->rl, d->play_blocksize ); /* avoid too large transfer */
l &= DMA_ALIGN_MASK ; /* realign things */
- if (l != b->dl) {
+ /*
+ * check if we need to reprogram the DMA on the sound card.
+ * This happens if the size has changed _and_ the new size
+ * is smaller, or it matches the blocksize.
+ */
+ if (l != b->dl && (l < b->dl || l == d->play_blocksize) ) {
/* 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));
- b->dl = l; /* record previous transfer size */
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
+ */
+ dsp_wr_dmaupdate(b) ;
+ l = min(b->rl, d->play_blocksize );
+ l &= DMA_ALIGN_MASK ; /* realign things */
+ b->dl = l; /* record previous transfer size */
d->callback(d, SND_CB_WR | SND_CB_START );
}
} else {
@@ -193,7 +204,7 @@ dsp_wrintr(snddev_info *d)
d->callback(d, SND_CB_WR | SND_CB_STOP ); /* stop dma */
if (d->flags & SND_F_WRITING)
DEB(printf("Race! got wrint while reloading...\n"));
- else
+ else if (b->rl <= 0) /* XXX added 980110 lr */
reset_dbuf(b, SND_CHAN_WR);
}
/*
@@ -236,7 +247,7 @@ dsp_write_body(snddev_info *d, struct uio *buf)
* the previous operation.
*/
bsz = b->dl ? MIN_CHUNK_SIZE : b->bufsize ;
- while (( n = buf->uio_resid )) {
+ while ( n = buf->uio_resid ) {
l = min (n, bsz); /* at most n bytes ... */
s = spltty(); /* no interrupts here ... */
dsp_wr_dmaupdate(b);
@@ -750,7 +761,7 @@ 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, cnt 0x%x flags 0x%08x\n",
b->rl, d->flags);
break;
}
diff --git a/sys/i386/isa/snd/doc/Makefile b/sys/i386/isa/snd/doc/Makefile
new file mode 100644
index 0000000..355a77f
--- /dev/null
+++ b/sys/i386/isa/snd/doc/Makefile
@@ -0,0 +1,40 @@
+# Makefile for the sound documentation
+
+ARTICLE=sound
+INPUTS=
+STYLES=
+.SUFFIXES: .fig .ps
+OTHERSTUFF=
+FIGURES=
+PSS=
+ALLFIG= $(FIGURES)
+COMPAT= psfigps.tex
+
+ALLPS= $(ALLFIG:.fig=.ps)
+
+ALLSRCS= $(ARTICLE).tex $(STYLES) $(ALLFIG) $(PSS) $(INPUTS) \
+ $(COMPAT) $(OTHERSTUFF) Makefile
+
+.fig.ps:
+ fig2dev -L ps -m 0.5 $*.fig > $*.ps
+
+all: $(ARTICLE).dvi pnp.dvi ioctl.dvi Makefile
+
+$(ARTICLE).dvi: allfig $(ARTICLE).tex $(INPUTS) $(STYLES) $(PSS)
+ latex $(ARTICLE).tex
+
+pnp.dvi: allfig pnp.tex $(INPUTS) $(STYLES) $(PSS)
+ latex pnp.tex
+
+ioctl.dvi: allfig pnp.tex $(INPUTS) $(STYLES) $(PSS)
+ latex ioctl.tex
+
+allfig: $(ALLPS)
+
+clean:
+ rm -f $(ALLPS) a c *.dvi *.aux *.log rl
+
+tgz: $(ALLSRCS)
+ tar cvf - $(ALLSRCS) | gzip -9 > $(ARTICLE).tgz
+
+
diff --git a/sys/i386/isa/snd/doc/sound.ps.gz b/sys/i386/isa/snd/doc/sound.ps.gz
new file mode 100644
index 0000000..12beb0a
--- /dev/null
+++ b/sys/i386/isa/snd/doc/sound.ps.gz
Binary files differ
diff --git a/sys/i386/isa/snd/doc/sound.tex b/sys/i386/isa/snd/doc/sound.tex
new file mode 100644
index 0000000..d8e7059
--- /dev/null
+++ b/sys/i386/isa/snd/doc/sound.tex
@@ -0,0 +1,917 @@
+\documentclass[11pt]{article}
+
+\textwidth 6in
+\textheight 9in
+\oddsidemargin 0in
+\evensidemargin 0in
+\topmargin 0in
+
+% \input{psfigps.tex}
+
+\begin{document}
+\author{Luigi Rizzo\\
+ Dipartimento di Ingegneria dell'Informazione -- Universit\`a di Pisa\\
+ via Diotisalvi 2 -- 56126 Pisa (Italy)\\
+ email: {\tt l.rizzo@iet.unipi.it}}
+\title{The FreeBSD audio driver}
+\maketitle
+
+\begin{abstract}
+In this paper we describe the architecture of the new FreeBSD audio
+driver. We also give detailed information on the internal data
+structures used by the driver, and examples on how to add support
+for new devices.
+
+{\bf WARNING:} since the code is rapidly evolving, there might be some
+minor differences between this paper and the source code. In doubt,
+the source code is the ultimate reference.
+
+\end{abstract}
+
+\section{Introduction}
+
+The audio driver is the part of the kernel which handles audio-related
+peripherals. In FreeBSD, historically, there has been not much support
+for these devices. The original FreeBSD audio driver has been based on
+the Voxware 3.0 release, originally developed for Linux and ported to
+FreeBSD at 1.1 times. The driver in the source tree has been
+essentially unsupported since then.
+
+In parallel, some volunteers have been using a driver derived from
+Voxware 3.5, which had support for full-duplex audio cards which have
+become popular in more recent times.
+
+Both versions of the Voxware code are extremely complex and not very
+much in line with BSD device drivers. There are several motivations
+for that, the main one (we believe) was that the code has evolved
+through the years to support a number of boards which are sufficiently
+similar to suggest not to write a completely different driver, and
+sufficiently different to require the addition of state variables, new
+support routines, and feature enahncements, to the existing driver.
+The unfortunate result of this evolution was that the code in our hand
+was extremely difficult to configure, follow, and in many cases unable to
+support multiple devices. Furthermore, the complexity of the code,
+prevented proper support for it.
+
+The desire for a functional audio driver for FreeBSD, together with
+the lack of support for the code currently in the source tree, and
+its complexity of the original driver, were for us sufficient
+motivations to start a large rewrite of the code.
+
+From a user's point of view, we were especially concerned with the
+complexity of the configuration process, which, to be done properly,
+required a number of different devices to be compiled into the kernel,
+with (at times) subtle dependencies among the modules and the options
+to be used. So, one of our goal was to simplify the configuration
+process, a goal which we believe to have achieved.
+
+From the kernel developer's point of view, the Voxware driver was hard
+to understand for two reasons. First, its structure was not much in
+line with BSD driver. This obviously derives from the original
+development on a different system. The second reason is the evolution
+of the driver during time, which, we believe, has degraded the quality of
+the code from a carefully designed original version.
+
+We have started our work basing on Voxware 3.5. The Voxware code
+served mainly as a reference on what the features of the various
+boards were, and how problems had been dealt with. Our code
+then evolved into a fairly complete rewrite of the main modules,
+including the main configuration mechanisms, the DMA support
+routines, and, in many cases, also the board-specific drivers which
+could make use of many of the simplifications we have introduced.
+
+\section{What the audio driver does}
+
+The audio driver is intended to control all peripherals related to
+audio handling. This includes digital audio sampling and generation
+({\tt pcm}), communication with MIDI devices ({\tt midi}) and the
+synthesizers ({\tt synth}) which several audio cards provide. In
+addition to these devices, which have explicit entries in the kernel
+configuration file, audio cards generally provide a {\em mixer}
+device, which controls audio paths within the board.
+Our devices currently has only support for {\tt pcm} and mixer
+devices.
+
+\subsection{The {\tt pcm} device}
+
+The {\tt pcm} device is in charge of the audio sampling and
+generation. Such device is generally implemented by a CODEC, a piece
+of hardware containing one or two A/D and D/A converters. Depending on
+the board, the minimum and maximum sampling rate, the number of bits
+per sample, and the data format (e.g. the availability of compressed
+audio formats) varies. At a minimum, all boards seem to be capable of
+sampling mono audio with 8-bit unsigned values and 8 KHz resolutions.
+Most boards (especially the Windows-Sound-System ones, known as WSS or
+MSS) are also capable of full duplex stereo with 16-bit and 44.1KHz
+sampling rate (corresponding to CD-quality).
+
+The {\tt pcm} device implements the {\tt ioctl()} calls to select the
+desired data format, and {\tt read()} and {\tt write()} functions to
+allow a smooth exchange of data with the codec, and {\tt select()} to
+notify the application when I/O is possible.
+
+
+\subsection{The mixer}
+
+The mixer controls audio data paths, i.e. which sources are directed
+to the ADC input, which ones are directed to the audio output, and the
+attenuation on the various channels. There is a wide variety of
+features in this subsystem, as some boards do provide only on-off
+control on a limited number of sources, whereas some boards provide
+many sources and fine-grained volume controls.
+
+We believe that a sufficiently general model of the {\tt mixer}
+is one actually using {\em two} mixers, one to connect sources to the
+ADC part of the codec, and one to connect sources to the physical
+output (usually the speakers). Some early boards often implement the
+mixer leading to the ADC only as a multiplexer with one active line,
+but that can be modeled by thinking of a very coarse (1 bit) volume
+control, and a restriction on the volumes on the various sources.
+
+
+\section{Kernel Configuration}
+
+The configuration of the driver is done through the following
+statements included in the system configuration file:
+\begin{verbatim}
+device pcm0 at isa? port? tty irq N drq D flags F vector pcmintr
+\end{verbatim}
+The {\tt device pcm0} statement is in charge of bringing into the
+kernel all modules related to audio support.
+
+The main audio subsystem is configured by using the {\tt device
+pcm0} statement. This line supplies the main parameters related to the
+configuration of a single sound card, which are:
+\begin{itemize}
+\item {\bf port} the I/O address for the codec (and generally mixer)
+ subsystem. If no address is supplied, the driver looks at
+ different addresses for the most common boards.
+\item {\bf irq} the interrupt line used by the board.
+\item {\bf drq} the first DMA channel used by the board. Modern
+ sound boards supporting full-duplex can use two DMA channels,
+ so we use the {\tt flags} field to specify the second channel.
+\item {\bf flags} used to specify the second DMA channel, the device
+ type (overriding the autoconfiguration mechanism), and
+ other information.
+ In particular, the flags have the following use:\\
+ \begin{center}
+ \begin{tabular}{|c|l|}
+ \hline Bits & Meaning \\
+ \hline \hline
+ 2..0 & secondary (read) DMA channel for dual-dma boards. \\
+ \hline
+ 4 & Enable secondary dma channel \\
+ \hline
+ 15..8 & Specific device type/class. 0 means autoconfigure\\
+ \hline
+ \end{tabular}
+ \end{center}
+\end{itemize}
+A sound board often has more than the codec, and uses more than one
+I/O address block. Since the {\tt device} line in the config file
+does not permit to
+specify more than one address (except, perhaps, by abusing the {\tt
+flags} field, which we have already used though), we will
+use two additional {\tt device} entries to specify the i/o address
+for the MIDI ({\tt midi}) and Synthesizer ({\tt synth}) devices,
+respectively. These entries do not correspond to actual devices
+recognized by the kernel (in the sense that their probe routine will
+always fail, and their attach routine will do nothing and will never
+be called); rather, the parameters specified in these lines are used
+to extend the description of the board provided by the {\tt device
+pcmX} line.
+There is no support at the moment for {\tt midi} and {\tt synth}
+devices.
+
+\subsection{Plug and Play support}
+
+As a side effect of our work on the audio driver, we have implemented
+support for Plug and Play (PnP) device configuration. This was a
+necessity since many modern audio boards are configured through
+PnP. PnP support is described in a separate document.
+
+In order to support PnP boards, one device entry is still necessary
+(i.e. {\tt pcm0}), to let the
+configure program include all the necessary parameters. The entry must
+include all fields, e.g.
+\begin{verbatim}
+device pcm0 at isa? port? tty irq 7 drq 1 vector pcmintr
+\end{verbatim}
+although
+the actual configuration of PnP devices (i.e. port addresses, IRQ,
+DMA, etc. will be auto detected, and unit numbers will be assigned
+starting from the next free one (e.g. {\tt pcm1, pcm2, ...}).
+
+\section{Code structure}
+
+The following table details the various file which make up the code.
+All files reside in the directory {\tt /sys/i386/isa/snd} .
+
+\begin{center}
+\begin{tabular}{|l|p{4in}|}
+\hline
+File Name & Description \\
+\hline \hline
+{\tt sound.c} & main device routines and autoconfiguration.\\
+\hline
+{\tt sound.h} & generic header for all kernel files.\\
+\hline
+{\tt soundcard.h} & generic header for all userland sound apps.\\
+\hline
+{\tt dmabuf.c} & DMA support. \\
+\hline
+{\tt sb\_dsp.c} & SoundBlaster driver \\
+\hline
+{\tt ad1848.c} & MSS-compatible driver \\
+\hline
+{\tt clones.c} & Miscellaneous code to enable SB/MSS emulation in
+ various boards. \\
+\hline
+\end{tabular}
+\end{center}
+
+\subsection{The {\tt snddev\_info} structure}
+
+Each known board is fully described by a {\tt snddev\_info}
+structure. The structure contains information of use at initialization
+time (such as board type, name, probe and attach routines), and at
+runtime. The latter include all board-specific functions, and the
+generic status information about the board when in use.
+
+{\tt snddev\_info} is made of two parts, the first one which is
+generally initialized from a template which univoquely identifies the
+board, the second one which contains run-time parameters. The first part
+of the structure is:
+
+\begin{verbatim}
+typedef struct _snddev_info snddev_info ;
+struct _snddev_info {
+ char name[64];
+ int type ; /* board type/class, search key during probes */
+ int (*probe)(struct isa_device * dev);
+ int (*attach)(struct isa_device * dev) ;
+ d_open_t *open ;
+ d_close_t *close ;
+ d_read_t *read ;
+ d_write_t *write ;
+ d_ioctl_t *ioctl ;
+ d_select_t *select ;
+ irq_proc_t *isr ;
+ snd_callback_t *callback;
+
+ int bufsize; /* DMA buffer size */
+ u_long audio_fmt ; /* bitmap of supported audio formats */
+\end{verbatim}
+where the names of the fields clearly identify the functions.
+Board-specific code generally supplies, in the template, initializers
+for all the above fields. At attach time, and before calling the
+board-specific attach routines, the descriptor associated with the
+device, {\tt pcm\_info[dev->id\_unit]} is bzeroed, initialized with
+the template, then the fields in the second part of the structure
+(shown next) are initialized with default values (partly taken from
+the device config line, as in the case of i/o, irq and dma addresses),
+and the required buffers are allocated.
+
+\begin{verbatim}
+ int io_base, alt_base, conf_base,
+ mix_base, midi_base, synth_base;
+
+ int bd_id; /* board-specific id */
+ snd_dbuf dbuf_out, dbuf_in;
+ int status_len, status_ptr ; /* to implement sndstat */
+
+ volatile u_long flags; /* generic flags */
+
+ u_long bd_flags; /* board-specific flags */
+ int play_speed, rec_speed;
+
+ int play_blocksize, rec_blocksize;
+ u_long play_fmt, rec_fmt ;
+ u_long mix_devs, mix_rec_devs, mix_recsrc;
+ u_short mix_levels[32];
+
+ u_long interrupts;
+ void *device_data;
+\end{verbatim}
+The structure provides all generic fields for audio support, plus a
+couple of board-specific fields, {\tt bd\_id} and {\tt bd\_flags}. The
+former should be used to distinguish different versions of the same
+device (e.g. major/minor id in case of the SoundBlaster, codec type
+for MSS-compatible boards, etc.), the latter can be used to hold
+board-specific flags. Should these info not be sufficient, room is
+provided for a pointer to device-specific data so that the structure
+can be extended at will.
+
+Note that the descriptor has room for several base addresses. One of
+this ({\tt io\_base}) is derived from the the kernel config line, and
+corresponds to the base address for codec access. The others can be
+derived using various mechanisms, e.g. hardwired info (some boards are
+known to have a given subsystem at a given address), additional lines
+in the config file, or PnP autoconfiguration info. These addresses are
+used to access the subsystems, and to check for conflicts during the
+configuration process.
+
+Of particular importance is the {\tt flags} fields, which holds many
+flags used to determine the status of the device. Among them:
+\begin{verbatim}
+#define SND_F_BUSY 0x0001 /* has been opened (for write) */
+#define SND_F_BUSY_AUDIO 0x10000000
+#define SND_F_BUSY_DSP 0x20000000
+#define SND_F_BUSY_DSP16 0x40000000
+\end{verbatim}
+These flags indicate the busy status of the board. They are of
+exclusive use of the device-specific {\tt open()} and {\tt close()}
+routines. The intended purpose is to allow only one or more opens
+on the device. For the way the kernel operates, when multiple
+descriptors are open on the same device (e.g. because the process
+{\tt fork()}'ed after opening the device), only the last {\tt
+close()} will result in the invokation of the device-specific close.
+But, since in the Voxware mode, a device is accessed using three
+minor numbers (corresponding to {\tt /dev/dsp, /dev/audio, /dev/dspW}),
+we have to keep track of opens issued on the different devices and
+only call the close routines on the very last close.
+
+\begin{verbatim}
+#define SND_F_READING 0x0004 /* have a pending read */
+#define SND_F_WRITING 0x0008 /* have a pending write */
+\end{verbatim}
+Allowing a single open does not suffice to exclude the possibility
+that a process forks and then multiple calls are issued concurrently.
+These two flags are used in the generic read/write routines to
+prevent concurrent calls of the same type, and serialize calls of
+different types for half-duplex devices. These two flags are
+generally used in the generic read/write code, and there should be
+no need to use them in the device-specific code, unless the read/write
+routines are reimplemented.
+
+\begin{verbatim}
+#define SND_F_CLOSING 0x0040 /* a pending close */
+#define SND_F_ABORTING 0x1000 /* a pending abort on write */
+\end{verbatim}
+These flags mark special conditions, which should be known to the
+DMA routines to shut down the device properly. The {\tt CLOSING} flags
+are generally set by a call to {\tt close()} so that DMA transfers are
+shut down at the end of the current operation. The {\tt ABORTING}
+flags are set in response to user breaks or special {\tt ioctl}'s
+such as {\tt PAUSE}, and cause immediate suspension of the output.
+
+\begin{verbatim}
+#define SND_F_STEREO 0x0100
+#define SND_F_XLAT8 0x0400 /* u-law <--> 8-bit unsigned */
+#define SND_F_XLAT16 0x0800 /* u-law <--> 16-bit signed */
+\end{verbatim}
+These are generic flags, marking the use of stereo audio and the
+need for special translations in order to support {\tt /dev/audio}
+on boards which do not have native $\mu$LAW support. In practice,
+the latter flags are only used for the SoundBlaster; furthermore,
+conversion between $\mu$LAW and 16-bit signed is currently not
+implemented (but required to implement full-duplex through {\tt
+/dev/audio} on the SB16).
+
+\begin{verbatim}
+#define SND_F_INIT 0x4000 /* changed parameters. need init */
+\end{verbatim}
+Finally, this flag marks the need for board reinitialization at the
+next convenient time. This is because some parameters (e.g. speed,
+channels, etc.) might be changed while an I/O operation is in progress
+(e.g. a previous write is being flushed), and the board can only be
+reprogrammed at the end of a transfer. This flag helps in supporting
+the functionality. In the generic {\tt ioctl} handler, as an example,
+all calls which set a parameter update the descriptor, set the flag,
+and then invoke the callback function to apply the change. If successful,
+the flag will be cleared, otherwise it will stay set for the update to
+take place at the next convenient time.
+
+\subsection{Autoconfiguration}
+
+The main component of the driver is the file {\tt sound.c} which
+contains the autoconfiguration stuff and the main device routines.
+
+The configuration process occurs as follows (see the following
+diagram).
+
+\begin{verbatim}
+pcmprobe(dev) {
+ foreach (p in supported devices)
+ if (p->probe(dev) is successful) {
+ snddev_last_probed = p ;
+ return 1 ;
+ }
+ return 0 ; /* fail */
+}
+
+pcmattach(dev) {
+ initialize pcm_info[dev->id_unit] with data from the template,
+ from snddev_last_probed, and from dev;
+ call snddev_last_probed->attach(dev);
+}
+
+xxx_probe(dev) {
+ try to identify the board, possibly updating dev; can use
+ pcm_info[dev->id_unit] as temporary storage (reset on exit);
+ return 1 on success, 0 on failure.
+}
+
+xxx_attach(dev) {
+ initialize board and do the necessary allocation, complete the
+ info in pcm_info[dev->id_unit] (initialized from the template
+ and dev)
+}
+\end{verbatim}
+The kernel invokes {\tt pcmprobe()} to detect a board, passing it
+the parameters from the configuration line. The routine then scans
+the set of supported devices to see if the device is compatible
+with the configured unit, and in case invokes the probe routine,
+which is passed a pointer to the {\tt isa\_device} structure
+describing the device.
+
+Compatibility is checked using the following rules. If a device
+type is specified, only that device is checked for. If the ``device
+type'' refers to a class of devices (e.g. all SoundBlaster-like
+boards, all MSS-like boards), then all known devices in that class
+are probed. Finally, if no device type is specified, then all
+devices are probed. Internally, a {\tt snddev\_info} structure is
+defined for each known board. For each board class, an array holds
+pointers to the descriptors of each board. Finally, an array holds
+pointers to the arrays describing each board class.
+
+The probe routine is not supposed to do any resource allocation,
+just to check for the presence of the board. The only allowed side
+effect of the probe routine is to modify the content of the {\tt
+isa\_device} structure, in case a generic parameter was passed, to
+instantiate it to the correct value. If the board-specific probe
+routine is successful, so is {\tt pcmprobe()}. This will result in
+the invokation of {\tt pcmattach()}, with a pointer to the same
+{\tt isa\_device} structure that was used at probe time.
+
+{\tt pcmattach()} will now copy the {\tt snddev\_info} structure
+describing the successfully-probed device into a permanent device
+descriptor, fill it up with run-time information, and call the
+board-specific attach routine. All the above is only possible
+because {\tt pcmprobe()} saves a pointer ({\tt last\_probed}) to
+the {\tt snddev\_info} corresponding to the successfully probed
+device before returning.
+
+The board-specific attach routines has the task of configuring the
+boards using the supplied parameters. The only parameter passed
+to the attach routine is the {\tt isa\_device} pointer. This
+structure includes the unit number, so the board-specific can
+identify the permanent device descriptor as {\tt pcm\_info[unit]}
+(use {\tt midi\_info}, {\tt synth\_info} for other devices).
+
+Although the return type is {\tt int}, the return value from the
+board-specific attach routine is not checked, similarly to what is
+done for the generic ISA attach routines. The only action that the
+kernel does on a successful attach is to register an interrupt for
+the device. As a consequence, a failed attach can be marked by
+setting {\tt dev->id\_irq = 0 ;} which has the consequence of not
+registering an interrupt for the device.
+
+\subsection{Main device routines}
+
+Upon a successful attach, {\tt pcmattach()} registers a {\tt cdevsw}
+entry for the audio device. All audio devices share the same routines,
+{\tt sndopen, sndclose, sndread, sndwrite, sndioctl, sndselect},
+which perform some generic actions and then invoke the board-specific
+routines, if available. The board-specific routines, as well as
+the probe and attach routines, have the same interface of a BSD
+device driver. This was a design choice meant to simplify development:
+one can, in principle, develop a driver outside the audio driver,
+and then move it under the audio driver with minimal effort.
+Additionally, this approach simplifies life to a kernel developer
+since there are no new interfaces to be learned.
+
+\subsubsection{{\tt sndopen()} and {\tt sndclose()} }
+
+The generic open and close routines do very little, they basically
+check that the unit and device number correspond to a supported
+device, and then invoke the (required) open/close routines for
+the specific board. There is little more the generic routines can
+do, since board vary widely and the open and close actions are very
+board-specific.
+
+\subsubsection{{\tt sndread()} and {\tt sndwrite()} }
+
+On the contrary, the generic read and write actions are very similar
+across all boards. In all cases, checks must be done for valid unit
+and device numbers. Then, concurrent operations of the same type
+must be denied. Finally, operations of different types on half-duplex
+devices must be properly sequenced. After these checks have been
+done, the generic read/write code requires to move data between the
+user buffer and the DMA buffer, in blocks of appropriate size.
+
+\subsection{{\tt sndioctl()}}
+
+The audio driver uses a very large number of {\tt ioctl}'s. Many
+of these simply require to read, or set, parameters in the device
+descriptor, so they can be implemented by the generic routine for
+all boards. Other ioctl calls, instead, require board-specific
+actions. We have devised a mechanism to allow board-specific ioctl
+to handle the desired set of ioctl calls, leaving the generic
+routine the task to deal with other, unhandled, calls.
+
+The generic routine, after checking device and unit numbers, passes
+control to the board-specific routine, if existing. This can do its
+own processing and return the error status. If the return value is
+{\tt ENOSYS} (error number 78, ``Function not implemented''), then
+the generic ioctl {\tt switch} statement is invoked which performs
+the generic actions.
+
+The generic routine tries to implement as much as possible of the
+sound calls, leaving little processing to the user code. In
+particular, it is assumed that all calls which just read the status
+of the device can fetch it directly from the audio descriptor.
+Updates to the parameters are done in the generic ioctl by setting
+the parameter in the descriptor (when possible, by checking that
+values are acceptable), setting the {\tt SND\_F\_INIT} flag and
+calling the device-specific {\tt callback()} function, if available.
+This in turn will try to perform the necessary action or, if not
+possible, leave the flag set for later operation.
+
+
+\subsection{{\tt sndselect()}}
+
+A properly working select routine is fundamental for a audio device,
+because often an application has to handle multiple data streams.
+While it is true that an application can determine how long
+a read or write call will take to complete, knowing buffer sizes and
+sample rates, it is certainly more convenient and efficient to have a
+working {\tt select()}. The generic select routine should cover all
+needs for devices using DMA. The body of the routine is very
+standard. The only noticeable thing is that, when the user specified a
+preferred block size through one of the available {\tt ioctl()} calls,
+then {\tt select()} will return only when at least one whole block
+can be transferred. In other cases (default) one byte will suffice to
+make {\tt select()} return (although the system will still choose a blocksize
+for DMA transfers, corresponding to 0.25~s of data).
+
+\subsection{{\tt /dev/sndstat}}
+
+In the Voxware driver, a special, readonly, status device ({\tt
+/dev/sndstat}) was defined (minor number 6) which returned information
+on the audio system, including supported devices and configured
+devices. In our code, {\tt /dev/sndstat} is supported directly in
+the generic routines (in file {\tt sound.c}) and is defined for
+every unit returning the same information. In particular, the
+status device returns data from a statically allocated 4~KB buffer
+({\tt status\_buf}) which is filled-up at the first open with the
+function {\tt init\_status()}.
+
+\section{DMA support}
+
+DMA handling routines are an important module of the audio code.
+All the DMA-related code is in file {\tt dmabuf.c} and is completely
+new for this release.
+
+At the time of this writing, DMA support does not use AUTOMODE.
+
+Using DMA mode, the sound card requests the transfer of data by
+issuing DMA requests to the ISA DMA controller, which in turn
+satisfies the request. The ISA DMA controller can be programmed with a
+count, so that a signal (TC) is generated after the required amount of data
+has been transferred. In ``AUTO'' mode, the ISA DMA controller
+reinitializes itself at the end of the transfer. Otherwise, requests
+are not served anymore until the controller is reprogrammed.
+
+In principle, the codec could just forward the TC signal to the
+appropriate interrupt line when enabled for DMA transfers. However,
+many codec usually have a DMA count register themselves, and generate
+an interrupt after the programmed number of bytes is transferred,
+in many cases without looking at the value of TC. This makes it
+possible to program the ISA DMA controller to use a large buffer,
+while letting the codec generate interrupts on smaller (and perhaps
+variable size) blocks.
+
+There are several problems to be dealt with by the DMA code in the
+audio driver. First, and most important, we need to avoid overruns
+or underruns in transferring data from/to the audio board. This is
+more important for (old) boards which do not have a properly sized
+on-board FIFO. The second problem is to minimize the latency in
+all i/o functions, something which is especially important for
+full-duplex applications.
+
+In the Voxware code, the DMA routines used a buffer partitioned into
+a number of fixed-size fragments. The programmer should use ioctls to
+select the fragment size which was best suited to his needs,
+generally in terms of latency.
+
+In our code, we use a completely different approach. The DMA buffer
+is a single memory block divided into two, variable-size, areas:
+{\em READY} and {\em FREE}. Each area is identified by an
+offset into the buffer and a length. The data structure describing a
+DMA buffer is the following:
+\begin{verbatim}
+typedef struct _snd_dbuf {
+ char *buf; /* pointer to the data buffer */
+ int bufsize ; /* total buffer size */
+ volatile int rp, fp; /* pointers to the ready and free area */
+ volatile int dl; /* transfer size */
+ volatile int rl, fl; /* length of ready and free areas. */
+ int int_count; /* how many interrupts on this channel */
+ int chan; /* dma channel */
+ int sample_size ; /* 1, 2, 4 */
+ struct selinfo sel; /* support for select */
+ u_long total; /* total bytes processed */
+ u_long prev_total; /* copy of the above when GETxPTR called */
+} snd_dbuf ;
+\end{verbatim}
+The READY area contains
+data ready to be transferred to the codec or to the user, depending
+on the direction of the transfer. The FREE area is empty and
+available for use. Both READY and FREE can wrap through the end of
+the buffer. When the dma engine is in use, it transfers {\tt dl} bytes
+from the beginning of the READY area (play) or to the beginning of the
+FREE area (record).
+
+The status of a dma transfer can in many cases be detected from the
+value of the length components of the structures. If {\tt dl == 0},
+then the DMA engine is not active. If {\em fl == 0}, a user write will
+be blocking or a new DMA read cannot be started.
+If {\em rl == 0}, a user read will be blocking, or a new DMA write
+cannot be started. A idle dma descriptor has {\tt dl = rl = 0, fl
+= bufsize}.
+
+\subsection{Handling block sizes}
+
+The purpose of the subdivision of buffers in three areas is that we
+can have one pending DMA operation using the DMA area, some space for
+the next DMA operation (the READY area for a write, the FREE area for
+a READ), and a pending read or write on the third area.
+
+Obviously we should avoid each of these areas to become too large and
+eat all the available buffer space, or too small and make operations
+inefficient. We will analize the details in the following two
+sections.
+
+\subsection{DMA write}
+
+In write operations, the boundary between READY and FREE is advanced
+to make room for user data. When possible, a DMA operation is
+started by advancing the boundary between DMA and READY by the
+amount of data which must be transferred. At the end of the DMA
+transfer, the DMA area is shrunk to zero length by extending the
+FREE area. These actions occur in the user write routine {\tt
+dsp\_write\_body()}, and in the write interrupt service routine
+{\tt dsp\_wrintr()}.
+
+By using a straightforward implementation, the time required for the
+user write routine to make data available to the DMA engine would be
+proportional to the block size used in the write operation. This is
+mainly because of the use of the {\tt uiomove} function in the
+routine, possibly followed by a format conversion for translating
+between $\mu$-LAW and the native format supported by the codec.
+In order to minimize this latency, we do the transfer in blocks of
+increasing size, doubling it at each pass. This gives us very low
+latency, while at the same time enables the use of large blocks when
+needed, without requesting the user to specify a block size.
+
+
+\subsection{DMA read}
+
+In read operations, a DMA operation needs to be started first, by
+advancing the boundary between DMA and FREE by the amount of required
+data. When the DMA transfer is complete, the boundary between READY
+and DMA is advanced. User reads can be served by removing data from
+the READY area and advancing the boundary between FREE and READY
+accordingly. These actions occur in the user read routine {\tt
+dsp\_read\_body()} and in the read interrupt service routine {\tt
+dsp\_rdintr()}.
+
+Even for DMA read, we move data to user space in blocks of increasing
+size, so as to minimize latency in freeing space in the buffer.
+However, implementing the read poses another difficulty in deciding
+when to return data to a requesting application. Typically,
+a read request should return when the requested number of bytes is
+available. If data are already available, we can simply copy them
+and return. If no data is available and the DMA is inactive, we
+can start it with the requested amount of data, and then wait for
+the request to complete. However, we are in trouble if the read
+request arrives when a DMA operation is already scheduled (a normal
+situation) and the transfer length is (possibly much) larger than
+the requested amount of data. In this case, the interrupt will only
+arrive at the end of the operation, which might take a long time.
+This is not a problem with writes, since data are already buffered
+for the whole operation.
+
+\subsection{Why this does not always work...}
+
+Using single DMA mode avoids that, because of slow interrupt
+response, samples are played beyond the end of the buffer, or
+capture overwrites some old, still unread, data at the other end
+of the buffer.
+Single DMA mode has the problem of requiring fast interrupt
+response to ensure a smooth data transfer. If the system is not quick
+to reinitialize the DMA controller, some samples might be missed. To
+mitigate the problem, some controllers provide an internal FIFO which
+increases the allowable interrupt response time. As an example, a
+codec with 16-samples FIFO at 8~KHz gives 2~ms to reinitialize the
+codec itself, which is a reasonable time.
+This of course assumes that
+the codec makes good use of the FIFO e.g. in the play queue generates
+an interrupt when the count goes to 0 even when the FIFO is full. Some
+don't.
+
+Another problem of using single DMA is that the same count is
+programmed into the two devices. It appears that some codecs when
+working in full duplex have the bad habit of forgetting to count some
+cycles, resulting in interrupts not being generated.
+
+So, to sum up, single DMA mode works very well with well-behaved
+codecs and fast systems. When the system becomes slow, late interrupt
+response might cause ``clicks'' in the output, or missing samples in
+the input. When the codec is buggy, there might be deadlocks because
+of missing interrupts (we have only seen these in full duplex on some
+cards).
+
+\subsection{Auto-mode DMA}
+
+Auto-mode DMA refers to a special operating mode of the DMA engine
+which does not require the CPU to reinitialize the channel at the
+end of a transfer. Auto DMA can be enabled independently in the
+ISA DMA controller and in the sound card. The advantage of auto
+mode is that data can be transferred continuously and do not require
+the interrupt service routine to have a low latency (if AUTO DMA
+is used on both the ISA DMA controller and in the sound card).
+
+Enabling AUTO DMA in the ISA DMA controller only saves the small time
+needed to reinitialize the DMA controller, and can help preventing the
+deadlock with broken codecs, since the ISA DMA controller will always
+be available to serve DMA requests. A drawback of this approach is
+that the codec might try to transfer data beyond the allowed space.
+
+Enabling AUTO DMA in the codec only does not make any sense.
+
+To overcome the problem of DMA transfers going beyond the allowed
+space, the following strategy can be used. For playback, we must
+insure that the region beyond the end of valid data contains data
+which do not produce audible effects. As an example, it can be
+initialized with replicas of the last sample (1, 2 or 4 bytes
+depending on the operating mode). The amount of space to be
+initialized in this way should correspond to the expected maximum
+interrupt latency.
+
+For record, the whole buffer should not be used to acquire data, so
+that any overrun will just fill a guard region in the buffer, and not
+overwrite old data. Again the size of the guard region should be
+computed depending on the expected maximum interrupt latency.
+
+\section{Board-specific routines}
+
+All board-specific routines are contained in one or more board-specific
+source files, and possibly some include files. The board-specific
+file(s) should provide as a minimum a template {\tt snddev\_info}
+describing the board, and the various functions referenced in the
+template and not defined elsewhere. This generally includes the
+probe, attach, open, close, the callback, and the interrupt
+dispatcher.
+
+In order to make board-specific files easily readable, we suggest
+to use a standard structure for them. As a reference, one can
+look at file {\tt sb\_dsp.c} which contains the driver for the
+SoundBlaster board.
+
+The standard structure begins with the copyright. Then the body of the
+file is enclosed in the following lines:
+\begin{verbatim}
+#include <i386/isa/snd/sound.h>
+#if NPCM > 0
+... body of the file
+#endif /* NPCM > 0 */
+\end{verbatim}
+where the {\tt "sound.h"} contains all the generic include files and
+definitions for kernel audio modules. It also includes the
+config-generated file {\tt "snd.h"} which defines the number of
+statically-configured audio devices, NPCM (note that one is required
+but often suffices, since PnP devices will use additional unit numbers
+up to the maximum of {\tt NPCM\_MAX} which is currently defined as 8.
+The total number of audio devices, which can be used and should be
+incremented when PnP devices are detected, is held in the global
+variable {\tt u\_long nsnd} which is initialized with NPCM.
+
+Next come the {\tt \#include} for all board-specific include files,
+followed by the prototype declaration for all static functions which go
+into the {\tt snddev\_info}, and then prototypes for all static
+functions in this module. We encourage to make functions static if not
+used by other modules, to reduce pollution of the name space.
+
+After function prototypes, put the (initialized) descriptors for the
+board(s) supported by this file, e.g.
+\begin{verbatim}
+snddev_info sb_op_desc = {
+ "SoundBlaster", SNDCARD_SB,
+ sb_probe, sb_attach,
+ sb_open, sb_close, NULL /* rd */, NULL /* wr */,
+ sb_dsp_ioctl, sndselect, sbintr, sb_callback,
+ DSP_BUFFSIZE,
+ AFMT_U8,
+}
+\end{verbatim}
+
+\subsection{{\tt xxx\_probe()}}
+
+The board-specific probe routine is passed a pointer to a {\tt struct
+isa\_device} which contains all information collected from the {\tt
+device pcmX} line in the kernel configuration file. The probe code
+should do the minimum action to detect the board type and see if
+parameters are compatible with the board's features. It returns 1 in
+case of success, 0 in case of failure.
+
+The probe code is
+not supposed to do any allocation. If some temporary storage is
+necessary, the probe code can use {\tt pcm\_info[dev->id\_unit]},
+which is available, although not initialized at this stage. This
+is convenient since, often, the probe and attach routine share the
+same code for determining the board type, and can store the detected
+information right there. Note however that {\tt
+pcm\_info[dev->id\_unit]} will be reinitialized at attach time, so that
+data structure should not be regarded as persistent information, but
+only as a convenient storage area.
+
+\subsection{{\tt xxx\_attach()}}
+
+Same as the probe routine, the attach routine is passed a pointer
+to a {\tt struct isa\_device} with info from the configuration
+file. This time, however, the generic attach routine will also
+initialize {\tt pcm\_info[dev->id\_unit]} copying the template
+corresponding to the device identified by the probe routine;
+moreover, values from the kernel config line are also copied in
+the correct places (in particular, this involves the various fields
+which depend on the value of {\tt flags}.
+
+\subsection{Interrupt dispatcher}
+
+All audio devices register the same interrupt service routine ({\em
+ISR}), {\tt pcmintr}. This merely calls the ISR which is specified
+in the device descriptor, passing it the unit number for which the
+interrupt has arrived (in practice, one is not aware of the presence
+of {\tt pcmintr}, which could also disappear in the future, and
+should write the ISR routine as a standard ISR for BSD systems.
+
+The ISR mainly has to find out the interrupt reason (accessing {\tt
+pcm\_info[unit]} to collect board specific parameters) and perform
+whatever action is necessary. Usually, it is only necessary to call
+{\tt dsp\_wrintr()} or {\tt dsp\_rdintr()}, and possibly clear the
+interrupt flag in the codec.
+
+\subsection{Callback routine}
+
+The main interface between the generic audio code and the device
+specific code is the callback routine which is specified in the device
+descriptor. The callback routine is passed an {\tt int} argument which
+describes the reason of the callback. The argument is made of a mode
+(read and/or write) and a reason. Typically the callback is invoked
+from the dma code, upon user writes or interrupt routines. The code in
+the callback routine should typically perform codec-specific actions
+to start/stop a dma transfer in the desired direction.
+As an extension, the callback function can also be called by the
+generic routines at open and close times. This only happens if
+device-specific open and close are not implemented, and the
+functionalities are implemented here.
+
+Again this is done for convenience, since in many cases the open and
+close routines share a common part (e.g. to check for device busy
+conditions, etc.) which this way need not to be replicated in
+different drivers.
+
+\subsection{Mixer}
+
+Sound cards generally have a mixer device, which is in charge of
+controlling signal flow, attenuation and filtering on the various
+paths. We mutuate the Voxware scheme in this driver, both because it
+is reasonably flexible, and for compatibility with existing
+applications. In this scheme, each of the possible mixer channels is
+associated to a bit in a bitmask.
+
+Setting levels for a mixer channel generally just requires to write a
+value (associated to the level) in some location. Since the number of
+bits used by various mixer devices largely differs from board to
+board, the Voxware driver uses a description table to map channels
+into the appropriate addresses. A table is made of an array of {\tt
+mixer\_ent} entries, which are set with the {\tt PMIX\_ENT} and
+{\tt MIX\_ENT} macros (all defined in {\tt sound.h}):
+entries:
+
+\begin{verbatim}
+struct mixer_def {
+ u_int regno:7;
+ u_int polarity:1;
+ u_int bitoffs:4;
+ u_int nbits:4;
+};
+typedef struct mixer_def mixer_ent;
+typedef struct mixer_def mixer_tab[32][2];
+
+#define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \
+ {{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}}
+#define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \
+ {{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}}
+
+void
+change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval);
+\end{verbatim}
+The table is used by {\tt change\_bits()}, which accepts a pointer
+to a table, a pointer to the old value of the register, device
+(0..31), channel (left or right), and the new value to be set, in
+the range 0..100.
+
+\end{document}
diff --git a/sys/i386/isa/snd/misc/README b/sys/i386/isa/snd/misc/README
new file mode 100644
index 0000000..e50b041
--- /dev/null
+++ b/sys/i386/isa/snd/misc/README
@@ -0,0 +1,82 @@
+Various support files for the new sound driver for FreeBSD.
+
+ audio-voxware.cc
+
+ this is the driver for "vat" (tested with vat 4.0b2 in the
+ ports distribution) to make things work in full duplex. It
+ does _not_ use non-blocking i/o, otherwise vat will happily
+ eat most of your CPU time since select will often return true
+ even if less than one frame is ready.
+
+ The environment variable AUDIODEV will let you use different
+ sound cards if you have more than one. It can contain the unit
+ number (0, 1, 2, ...) or the full pathname.
+
+ As an additional bonus, you can select a fourth input which is
+ a ulaw file which is written to the network instead of the
+ input audio data.
+
+ CPU usage on my p5/133 in full duplex, local feedback and
+ silence suppression (most demanding configuration I believe):
+ PCM 3.5%
+ DVI 4.8%
+ LPC 11 %
+ GSM 23 %
+
+ auvoxware.c
+
+ this is the driver for "nas" (tested with nas 1.2b5). Works
+ fine in full duplex.
+
+ linux_a.c
+
+ driver for timidity, a midi-to-pcm converter. Using this program
+ (and if you have spare cpu cycles) you can play midi files to
+ /dev/audio without the need for a synthesis device.
+
+ linux.patch
+
+ this is a patch for 2.2.X users for the linux emulator. The
+ files to correct are in /sys/i386/linux, and this patch
+ implements a few ioctl which are used by the realvideo player
+ for linux (rvplayer). In order to use these patches you have
+ to recompile the linux_mod.o and install it in place as follows
+
+ cd /usr/src/lkm/linux
+ make
+ mv linux_mod.o /lkm/linux_mod.o
+
+ pcmio.c
+
+ a simple program to do i/o with the audio device. You can set
+ on the command line the device, the speed, data format, stereo
+ or mono... And if you use a bidirectional device, you can play
+ and record at the same time.
+
+ E.g.
+
+ pcmio -f 1 +rec,stereo,44100,s16 song
+
+ will record from /dev/audio1 stereo,16-bit data to the file "song"
+
+ pcmio +play,stereo,44100,s16,loop 50s:20s song
+
+ will continuosly play 50s from "song" (skipping the first 20s)
+ to /dev/audio
+
+ pcmio +rec,ulaw - | tee tapefile > /dev/audio
+
+ will record using ulaw (mono at 8KHz, default) to stdout, and
+ the pipe will both dump things to "tapefile" and to /dev/audio
+ to listen what you are recording (you could do the same with
+ "cat...").
+
+ soundbyte.c
+
+ the audio module for speak_freely
+
+ test.c
+
+ lets you monitor the status of the device (/dev/audio1) by
+ mmapping the descriptor and dumping to screen the interesting
+ fields.
diff --git a/sys/i386/isa/snd/misc/audio-voxware.cc b/sys/i386/isa/snd/misc/audio-voxware.cc
new file mode 100644
index 0000000..16feb46
--- /dev/null
+++ b/sys/i386/isa/snd/misc/audio-voxware.cc
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 1991-1993 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+/*
+ * Full Duplex audio module for the new sound driver and full duplex
+ * cards. Luigi Rizzo, from original sources supplied by Amancio Hasty.
+ *
+ * This includes some enhancements:
+ * - the audio device to use can be in the AUDIODEV env. variable.
+ * It can be either a unit number or a full pathname;
+ * - use whatever format is available from the card (included split
+ * format e.g. for the sb16);
+ * - limit the maximum size of the playout queue to approx 4 frames;
+ * this is necessary if the write channel is slower than expected;
+ * the fix is based on two new ioctls, AIOGCAP and AIONWRITE,
+ * but the code should compile with the old driver as well.
+ */
+
+#include <osfcn.h>
+#include <machine/soundcard.h>
+#include "audio.h"
+#include "mulaw.h"
+#include "Tcl.h"
+
+#define ULAW_ZERO 0x7f
+
+extern const u_char lintomulawX[];
+
+class VoxWare : public Audio {
+ public:
+ VoxWare();
+ virtual int FrameReady();
+ virtual u_char* Read();
+ virtual void Write(u_char *);
+ virtual void SetRGain(int);
+ virtual void SetPGain(int);
+ virtual void OutputPort(int);
+ virtual void InputPort(int);
+ virtual void Obtain();
+ virtual void Release();
+ virtual void RMute();
+ virtual void RUnmute();
+ virtual int HalfDuplex() const;
+ protected:
+ int ext_fd; /* source for external file */
+
+ u_char* readbuf;
+ u_short *s16_buf;
+
+ int play_fmt ;
+#if defined(AIOGCAP) /* new sound driver */
+ int rec_fmt ; /* the sb16 has split format... */
+ snd_capabilities soundcaps;
+#endif
+};
+
+static class VoxWareMatcher : public Matcher {
+public:
+ VoxWareMatcher() : Matcher("audio") {}
+ TclObject* match(const char* fmt) {
+ if (strcmp(fmt, "voxware") == 0)
+ return (new VoxWare);
+ return (0);
+ }
+} linux_audio_matcher;
+
+VoxWare::VoxWare()
+{
+ readbuf = new u_char[blksize];
+ s16_buf = new u_short[blksize];
+
+ memset(readbuf, ULAW_ZERO, blksize);
+
+ ext_fd = -1 ; /* no external audio */
+ iports = 4; /* number of input ports */
+}
+
+void
+VoxWare::Obtain()
+{
+ char *thedev;
+ char buf[64];
+ int d = -1;
+
+ if (HaveAudio())
+ abort();
+ thedev=getenv("AUDIODEV");
+ if (thedev==NULL)
+ thedev="/dev/audio";
+ else if ( thedev[0] >= '0' && thedev[0] <= '9' ) {
+ d = atoi(thedev);
+ sprintf(buf,"/dev/audio%d", d);
+ thedev = buf ;
+ }
+ fd = open(thedev, O_RDWR );
+ thedev=getenv("MIXERDEV");
+ if (thedev == NULL)
+ if (d < 0)
+ thedev = "/dev/mixer";
+ else {
+ sprintf(buf,"/dev/mixer%d", d);
+ thedev = buf ;
+ }
+
+ if (fd >= 0) {
+ int i = -1 ;
+#if defined(AIOGCAP) /* new sound driver */
+ i = ioctl(fd, AIOGCAP, &soundcaps);
+ if (i == 0) {
+ snd_chan_param pa;
+ struct snd_size sz;
+
+ pa.play_rate = pa.rec_rate = 8000 ;
+ pa.play_format = pa.rec_format = AFMT_MU_LAW ;
+ switch (soundcaps.formats & (AFMT_FULLDUPLEX | AFMT_WEIRD)) {
+ case AFMT_FULLDUPLEX :
+ /*
+ * this entry for cards with decent full duplex. Use s16
+ * preferably (some are broken in ulaw) or ulaw or u8 otherwise.
+ */
+ if (soundcaps.formats & AFMT_S16_LE)
+ pa.play_format = pa.rec_format = AFMT_S16_LE ;
+ else if (soundcaps.formats & AFMT_MU_LAW)
+ pa.play_format = pa.rec_format = AFMT_MU_LAW ;
+ else if (soundcaps.formats & AFMT_U8)
+ pa.play_format = pa.rec_format = AFMT_U8 ;
+ else {
+ printf("sorry, no supported formats\n");
+ close(fd);
+ fd = -1 ;
+ return;
+ }
+ break ;
+ case AFMT_FULLDUPLEX | AFMT_WEIRD :
+ /* this is the sb16... */
+ if (soundcaps.formats & AFMT_S16_LE) {
+ pa.play_format = AFMT_U8 ;
+ pa.rec_format = AFMT_S16_LE;
+ } else {
+ printf("sorry, no supported formats\n");
+ close(fd);
+ fd = -1 ;
+ return;
+ }
+ break ;
+ default :
+ printf("sorry don't know how to deal with this card\n");
+ close (fd);
+ fd = -1;
+ break;
+ }
+ ioctl(fd, AIOSFMT, &pa);
+ play_fmt = pa.play_format ;
+ rec_fmt = pa.rec_format ;
+ sz.play_size = (play_fmt == AFMT_S16_LE) ? 2*blksize : blksize;
+ sz.rec_size = (rec_fmt == AFMT_S16_LE) ? 2*blksize : blksize;
+ ioctl(fd, AIOSSIZE, &sz);
+ } else
+#endif
+ { /* setup code for old voxware driver */
+ }
+ Audio::Obtain();
+ }
+}
+
+/*
+ * note: HalfDuplex() uses a modified function of the new driver,
+ * which will return AFMT_FULLDUPLEX set in SNDCTL_DSP_GETFMTS
+ * for full-duplex devices. In the old driver this was 0 so
+ * the default is to use half-duplex for them. Note also that I have
+ * not tested half-duplex operation.
+ */
+int
+VoxWare::HalfDuplex() const
+{
+ int i;
+ ioctl(fd, SNDCTL_DSP_GETFMTS, &i);
+#if 0
+ printf("SNDCTL_DSP_GETFMTS returns 0x%08x %s duplex\n",
+ i, i & AFMT_FULLDUPLEX ? "full":"half");
+#endif
+ return (i & AFMT_FULLDUPLEX) ? 0 : 1 ;
+}
+
+void VoxWare::Release()
+{
+ if (HaveAudio()) {
+ Audio::Release();
+ }
+}
+
+void VoxWare::Write(u_char *cp)
+{
+ int i = blksize, l;
+ static int peak = 0;
+
+ if (play_fmt == AFMT_S16_LE) {
+ for (i=0; i< blksize; i++)
+ s16_buf[i] = mulawtolin[cp[i]] ;
+ cp = (u_char *)s16_buf;
+ i = 2 *blksize ;
+ }
+ else if (play_fmt == AFMT_S8) {
+ for (i=0; i< blksize; i++) {
+ int x = mulawtolin[cp[i]] ;
+ x = (x >> 8 ) & 0xff;
+ cp[i] = (u_char)x ;
+ }
+ i = blksize ;
+ } else if (play_fmt == AFMT_U8) {
+ /*
+ * when translating to 8-bit formats, need to implement AGC
+ * to avoid loss of resolution in the conversion.
+ * The peak is multiplied by 2^13
+ */
+ for (i=0; i< blksize; i++) {
+ int x = mulawtolin[cp[i]] ;
+#if 0 /* AGC -- still not complete... */
+ if (x < 0) x = -x ;
+ if (x > peak) peak = ( peak*16 + x - peak ) / 16 ;
+ else peak = ( peak*8192 + x - peak ) / 8192 ;
+ if (peak < 128) peak = 128 ;
+ /* at this point peak is in the range 128..32k
+ * samples can be scaled and clipped consequently.
+ */
+ x = x * 32768/peak ;
+ if (x > 32767) x = 32767;
+ else if (x < -32768) x = -32768;
+#endif
+ x = (x >> 8 ) & 0xff;
+ x = (x ^ 0x80) & 0xff ;
+ cp[i] = (u_char)x ;
+ }
+ i = blksize ;
+ }
+#if 0 && defined(AIOGCAP)
+ int queued;
+ ioctl(fd, AIONWRITE, &queued);
+ queued = soundcaps.bufsize - queued ;
+ if (play_fmt == AFMT_S16_LE) {
+ if (queued > 8*blksize)
+ i -= 8 ;
+ } else {
+ if (queued > 4*blksize)
+ i -= 4 ;
+ }
+#endif
+ for ( ; i > 0 ; i -= l) {
+ l = write(fd, cp, i);
+ cp += l;
+ }
+}
+
+u_char* VoxWare::Read()
+{
+ u_char* cp;
+ int l=0, l0 = blksize, i = blksize;
+ static int smean = 0 ; /* smoothed mean to remove DC */
+ static int loops = 20 ;
+
+ cp = readbuf;
+
+ if (rec_fmt == AFMT_S16_LE) {
+ cp = (u_char *)s16_buf;
+ l0 = i = 2 *blksize ;
+ }
+ for ( ; i > 0 ; i -= l ) {
+ l = read(fd, cp, i);
+ cp += l ;
+ }
+ if (rec_fmt == AFMT_S16_LE) {
+ for (i=0; i< blksize; i++) {
+#if 1 /* remove DC component... */
+ int mean = smean >> 13;
+ int dif = ((short) s16_buf[i]) - mean;
+ smean += dif ;
+ readbuf[i] = lintomulawX[ dif & 0x1ffff ] ;
+#else
+ readbuf[i] = lintomulaw[ s16_buf[i] ] ;
+#endif
+ }
+ }
+ else if (rec_fmt == AFMT_S8) {
+ for (i=0; i< blksize; i++)
+ readbuf[i] = lintomulaw[ readbuf[i]<<8 ] ;
+ }
+ else if (rec_fmt == AFMT_U8) {
+ for (i=0; i< blksize; i++)
+ readbuf[i] = lintomulaw[ (readbuf[i]<<8) ^ 0x8000 ] ;
+ }
+ if (iport == 3) {
+ l = read(ext_fd, readbuf, blksize);
+ if (l < blksize) {
+ lseek(ext_fd, (off_t) 0, 0);
+ read(ext_fd, readbuf+l, blksize - l);
+ }
+ }
+ return readbuf;
+}
+
+/*
+ * should check that I HaveAudio() before trying to set gain.
+ *
+ * In most mixer devices, there is only a master volume control on
+ * the capture channel, so the following code does not really work
+ * as expected. The only (partial) exception is the MIC line, where
+ * there is generally a 20dB boost which can be enabled or not
+ * depending on the type of device.
+ */
+void VoxWare::SetRGain(int level)
+{
+ double x = level;
+ level = (int) (x/2.56);
+ int foo = (level<<8) | level;
+ if (!HaveAudio())
+ Obtain();
+ switch (iport) {
+ case 2:
+ case 1:
+ break;
+ case 0:
+ if (ioctl(fd, MIXER_WRITE(SOUND_MIXER_MIC), &foo) == -1)
+ printf("failed to set mic volume \n");
+ break;
+ }
+ if (ioctl(fd, MIXER_WRITE(SOUND_MIXER_IGAIN), &foo) == -1)
+ printf("failed set input line volume \n");
+ rgain = level;
+}
+
+void VoxWare::SetPGain(int level)
+{
+ float x = level;
+ level = (int) (x/2.56);
+ int foo = (level<<8) | level;
+ if (ioctl(fd, MIXER_WRITE(SOUND_MIXER_PCM), &foo) == -1) {
+ printf("failed to output level %d \n", level);
+ }
+ pgain = level;
+}
+
+void VoxWare::OutputPort(int p)
+{
+ oport = p;
+}
+
+void VoxWare::InputPort(int p)
+{
+ int src = 0;
+
+ if (ext_fd >=0 && p != 3) {
+ close(ext_fd);
+ ext_fd = -1 ;
+ }
+
+ switch(p) {
+ case 3:
+ fprintf(stderr,"input from file %s\n", ext_fname);
+ if (ext_fd == -1)
+ ext_fd = open(ext_fname, 0);
+ if (ext_fd != -1)
+ lseek(ext_fd, (off_t) 0, 0);
+ break;
+ case 2:
+ src = 1 << SOUND_MIXER_LINE;
+ break;
+ case 1: /* cd ... */
+ src = 1 << SOUND_MIXER_CD;
+ break;
+ case 0 :
+ src = 1 << SOUND_MIXER_MIC;
+ break;
+ }
+ if ( ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &src) == -1 ) {
+ printf("failed to select input \n");
+ p = 0;
+ }
+ iport = p;
+}
+
+void VoxWare::RMute()
+{
+ rmute |= 1;
+}
+
+void VoxWare::RUnmute()
+{
+ rmute &=~ 1;
+}
+
+/*
+ * FrameReady must return 0 every so often, or the system will keep
+ * processing mike data and not other events.
+ */
+int VoxWare::FrameReady()
+{
+ int i = 0;
+ int lim = blksize;
+
+ ioctl(fd, FIONREAD, &i );
+ if (rec_fmt == AFMT_S16_LE) lim = 2*blksize;
+ return (i >= lim) ? 1 : 0 ;
+}
+/*** end of file ***/
diff --git a/sys/i386/isa/snd/misc/auvoxware.c b/sys/i386/isa/snd/misc/auvoxware.c
new file mode 100644
index 0000000..1ae9316
--- /dev/null
+++ b/sys/i386/isa/snd/misc/auvoxware.c
@@ -0,0 +1,1301 @@
+/*
+ SCCS: @(#) auvoxware.c 11.4 95/04/14
+*/
+/*-------------------------------------------------------------------------
+
+Copyright (C) 1995 The Santa Cruz Operation, Inc.
+All Rights Reserved.
+
+Permission to use, copy, modify and distribute this software
+for any purpose is hereby granted without fee, provided that the
+above copyright notice and this notice appear in all copies
+and that both the copyright notice and this notice appear in
+supporting documentation. SCO makes no representations about
+the suitability of this software for any purpose. It is provided
+"AS IS" without express or implied warranty.
+
+SCO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL SCO BE LIABLE FOR ANY SPECIAL, INDIRECT,
+PUNITIVE, CONSEQUENTIAL OR INCIDENTAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, LOSS OF DATA OR LOSS OF
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+-------------------------------------------------------------------------*/
+/*
+ AUVoxConfig additions (sysseh@devetir.qld.gov.au)
+ 96-01-15
+ Put the following keywords in -
+ minrate - Minimum sampling rate
+ maxrate - Maximum sampling rate
+ fragsize - The fragment size
+ minfrags - Minimum number of frags in queue
+ maxfrags - Maximum fragments in queue
+ wordsize - 8 or 16 bit samples
+ device - What device file to use
+ numchans - Mono (1) or stereo (2)
+ debug - Output messages during operation
+ verbose - Be chatty about config
+ inputsection - Next lot of specs are for input
+ outputsection - Next specs are for output
+ end - End an input or output section
+*/
+/*
+ SCO Modification History:
+ S005, 24-Apr-95, shawnm@sco.com
+ base # of driver buffer fragments on data rate
+ S004, 12-Apr-95, shawnm@sco.com
+ finish integration of ausco.c, fix setitimer calls
+ S003, 28-Mar-95, shawnm@sco.com, sysseh@devetir.qld.gov.au
+ incorporate patch for stereo/mono mixing from Stephen Hocking
+ S002, 21-Mar-95, shawnm@sco.com
+ incorporate signal handling and audio block/unblock from ausco.c
+ S001, 21-Mar-95, shawnm@sco.com, sysseh@devetir.qld.gov.au
+ SYSSEH incorporate parts of patch from Stephen Hocking
+*/
+/*
+ * Copyright 1993 Network Computing Devices, Inc. Copyright (C) Siemens
+ * Nixdorf Informationssysteme AG 1993
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name Network Computing Devices, Inc. or
+ * Siemens Nixdorf Informationssysteme AG not be used in advertising or
+ * publicity pertaining to distribution of this software without specific,
+ * written prior permission.
+ *
+ * THIS SOFTWARE IS PROVIDED `AS-IS'. NETWORK COMPUTING DEVICES, INC. AND
+ * SIEMENS NIXDORF INFORMATIONSSYSTEME AG DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+ * NONINFRINGEMENT. IN NO EVENT SHALL NETWORK COMPUTING DEVICES, INC. NOR
+ * SIEMENS NIXDORF INFORMATIONSSYSTEME AG BE LIABLE FOR ANY DAMAGES
+ * WHATSOEVER, INCLUDING SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
+ * INCLUDING LOSS OF USE, DATA, OR PROFITS, EVEN IF ADVISED OF THE
+ * POSSIBILITY THEREOF, AND REGARDLESS OF WHETHER IN AN ACTION IN CONTRACT,
+ * TORT OR NEGLIGENCE, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $NCDId: @(#)auvoxware.c,v 1.10 1996/04/24 17:04:19 greg Exp $
+ *
+ * Copyright (C) Siemens Nixdorf Informationssysteme AG 1993 All rights reserved
+ */
+
+/*
+ * Originally from the merge of auvoxware by Amancio Hasty (hasty@netcom.com)
+ * & auvoxsvr4 by Stephen Hocking (sysseh@devetir.qld.gov.au).
+ * 16bit fixes and Linux patches supplied by Christian
+ * Schlichtherle (s_schli@ira.uka.de).
+ *
+ * BUGS:
+ * - When the soundcard can do only 8 bit recording, "aurecord" records
+ * twice as long as it should. Is this our fault?
+ *
+ * TODO:
+ * - Adapt the buffer sizes to the current sampling rate,
+ * so that we can record/play high quality audio samples without
+ * swallows/pauses.
+ * Note that setting the buffer size to a fixed maximum will not work,
+ * because it causes playing at slow sample rate to pause. :-(
+ * I already tried to do this, but it seems that the rest of the server
+ * code doesn't recognize the changed buffer sizes. Any help in doing
+ * this is welcome!
+ * [chris]
+ * - Support a second input channel for stereo sampling,
+ * so that microphone sampling is done on the mono channel
+ * while line sampling is done on the stereo channel.
+ * [chris]
+ *
+ * CHANGELOG:
+ * - 94/7/2:
+ * Completely rewrote this file. Features:
+ * + Makes use of two sound cards if available.
+ * So you can concurrently record and play samples.
+ * + Tested to work with all combinations of 8/16 bit, mono/stereo
+ * sound card sampling modes.
+ * + Uses a stereo input channel if hardware supports this.
+ * + Can play stereo samples on mono sound cards (but who cares?).
+ * + Always uses the highest possible audio quality, i.e. 8/16 bit and
+ * mono/stereo parameters are fixed while only the sampling rate is
+ * variable. This reduces swallows and pauses to the (currently)
+ * unavoidable minimum while consuming a little bit more cpu time.
+ * + Format conversion stuff is pushed back to the rest of the server code.
+ * Only mono/stereo conversion is done here.
+ * + Debugging output uses indentation.
+ * [chris]
+ */
+
+#include <stdio.h>
+#include "config.h"
+
+FILE *yyin;
+
+static char *searchpath = CONFSEARCHPATH;
+
+#if defined(DEBUGDSPOUT) || defined(DEBUGDSPIN)
+int dspin, dspout;
+#endif
+
+# define PRMSG(x, a, b) \
+ if (doDebug) { \
+ fprintf(stderr, "auvoxware.c: %*s", debug_msg_indentation, ""); \
+ fprintf(stderr, (x), (a), (b)); \
+ fflush(stderr); \
+ }
+# define IDENTMSG (debug_msg_indentation += 2)
+# define UNIDENTMSG (debug_msg_indentation -= 2)
+
+static int doDebug = 0;
+int beVerbose = 0;
+static int debug_msg_indentation = 0;
+
+#include <errno.h>
+#include "dixstruct.h" /* for RESTYPE */
+#include "os.h" /* for xalloc/xfree and NULL */
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <assert.h>
+
+#ifdef __FreeBSD__
+#include <machine/soundcard.h>
+#include <machine/pcaudioio.h>
+#else
+#include <sys/soundcard.h>
+#endif
+
+#include <audio/audio.h>
+#include <audio/Aproto.h>
+#include "au.h"
+
+#ifdef sco
+static AuBool scoAudioBlocked = AuFalse;
+#endif /* sco */
+
+static AuBool processFlowEnabled;
+
+#define SERVER_CLIENT 0
+
+#define MIN_MINIBUF_SAMPLES 256
+#define MAX_MINIBUF_SAMPLES 1024 /* Must be a power of 2 */
+
+#define auDefaultInputGain AuFixedPointFromSum(50, 0)
+#define auDefaultOutputGain AuFixedPointFromSum(50, 0)
+
+#define PhysicalOneTrackBufferSize \
+ PAD4(auMinibufSamples * auNativeBytesPerSample * 1)
+#define PhysicalTwoTrackBufferSize \
+ PAD4(auMinibufSamples * auNativeBytesPerSample * 2)
+
+/* VOXware sound driver mixer control variables */
+
+static int Letsplay;
+static int level[100];
+static int mixerfd; /* The mixer device */
+static int devmask = 0; /* Bitmask for supported mixer devices */
+static int recsrc = 0; /* Currently selected recording sources */
+static int recmask = 0; /* Supported recording sources */
+static int stereodevs = 0; /* Channels supporting stereo */
+
+static char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
+
+/* end of VOXware driver mixer control variables */
+
+int ParseError = 0; /* Did we fail to parse conf. file? */
+
+SndStat *confStat;
+
+SndStat sndStatIn =
+{
+ -1,
+ 16,
+ 2,
+ 0,
+ 4000,
+ 44100,
+ 256,
+ 3,
+ 32,
+ "/dev/dsp",
+ 0
+}, sndStatOut =
+{
+ -1,
+ 16,
+ 2,
+ 0,
+ 4000,
+ 44100,
+ 256,
+ 3,
+ 32,
+ "/dev/dsp",
+ 0
+};
+
+static AuUint8 *auOutputMono,
+ *auOutputStereo,
+ *auInput;
+
+static ComponentPtr monoInputDevice,
+ stereoInputDevice,
+ monoOutputDevice,
+ stereoOutputDevice;
+
+extern AuInt32 auMinibufSamples;
+
+
+#define auPhysicalOutputChangableMask AuCompDeviceGainMask
+
+#define auPhysicalOutputValueMask \
+ (AuCompCommonAllMasks \
+ | AuCompDeviceMinSampleRateMask \
+ | AuCompDeviceMaxSampleRateMask \
+ | AuCompDeviceGainMask \
+ | AuCompDeviceLocationMask \
+ | AuCompDeviceChildrenMask)
+
+#define auPhysicalInputChangableMask \
+ (AuCompDeviceGainMask | AuCompDeviceLineModeMask)
+
+#define auPhysicalInputValueMask \
+ (AuCompCommonAllMasks \
+ | AuCompDeviceMinSampleRateMask \
+ | AuCompDeviceMaxSampleRateMask \
+ | AuCompDeviceLocationMask \
+ | AuCompDeviceGainMask \
+ | AuCompDeviceChildrenMask)
+
+static void setPhysicalOutputGain();
+static void setPhysicalInputGainAndLineMode();
+
+void setDebugOn ()
+{
+ doDebug = 1;
+}
+
+void setVerboseOn ()
+{
+ beVerbose = 1;
+}
+
+#ifdef sco
+
+AuBlock
+AuBlockAudio()
+{
+ scoAudioBlocked = AuTrue;
+ return 0;
+}
+
+void
+AuUnBlockAudio(AuBlock id)
+{
+ scoAudioBlocked = AuFalse;
+}
+
+#endif /* sco */
+
+static int createServerComponents(auServerDeviceListSize,
+ auServerBucketListSize,
+ auServerRadioListSize,
+ auServerMinRate,
+ auServerMaxRate)
+AuUint32 *auServerDeviceListSize,
+ *auServerBucketListSize,
+ *auServerRadioListSize,
+ *auServerMinRate,
+ *auServerMaxRate;
+{
+ AuDeviceID stereo, mono;
+ ComponentPtr d, *p;
+ int i;
+ AuUint8 formatIn,
+ formatOut;
+ AuUint32 bytesPerSampleIn,
+ bytesPerSampleOut;
+ static AuBool initialized = AuFalse;
+ extern RESTYPE auComponentType;
+ extern ComponentPtr *auServerDevices, /* array of devices */
+ *auServerBuckets, /* array of server owned buckets */
+ *auServerRadios, /* array of server owned radios */
+ auDevices, /* list of all devices */
+ auBuckets, /* list of all buckets */
+ auRadios; /* list of all radios */
+ extern AuUint32 auNumServerDevices, /* number of devices */
+ auNumActions, /* number of defined actions */
+ auNumServerBuckets, /* number of server owned buckets */
+ auNumServerRadios; /* number of server owned radios */
+
+ PRMSG("createServerComponents(...);\n", 0, 0);
+ IDENTMSG;
+
+ *auServerMinRate = aumax(sndStatIn.minSampleRate,
+ sndStatOut.minSampleRate);
+ *auServerMaxRate = aumax(sndStatIn.maxSampleRate,
+ sndStatOut.maxSampleRate);
+
+ auNumServerDevices = *auServerDeviceListSize
+ = *auServerBucketListSize
+ = *auServerRadioListSize
+ = 0;
+
+ formatIn = (sndStatIn.wordSize == 16) ? AuFormatLinearSigned16LSB
+ : AuFormatLinearUnsigned8;
+ formatOut = (sndStatOut.wordSize == 16) ? AuFormatLinearSigned16LSB
+ : AuFormatLinearUnsigned8;
+
+ bytesPerSampleIn = sndStatIn.wordSize / 8;
+ bytesPerSampleOut = sndStatOut.wordSize / 8;
+
+ AU_ALLOC_DEVICE(d, 1, 0);
+ d->id = FakeClientID(SERVER_CLIENT);
+ d->changableMask = auPhysicalOutputChangableMask;
+ d->valueMask = auPhysicalOutputValueMask;
+ d->kind = AuComponentKindPhysicalOutput;
+ d->use = AuComponentUseExportMask;
+ d->access = AuAccessExportMask | AuAccessListMask;
+ d->format = formatOut;
+ d->numTracks = 1;
+ d->description.type = AuStringLatin1;
+ d->description.string = "Mono Channel Output";
+ d->description.len = strlen(d->description.string);
+ d->minSampleRate = sndStatOut.minSampleRate;
+ d->maxSampleRate = sndStatOut.maxSampleRate;
+ d->location = AuDeviceLocationCenterMask | AuDeviceLocationInternalMask;
+ d->numChildren = 0;
+ d->minibuf = auOutputMono;
+ d->minibufSize = d->numTracks * bytesPerSampleOut * auMinibufSamples;
+ d->physicalDeviceMask = PhysicalOutputMono;
+ AU_ADD_DEVICE(d);
+
+ monoOutputDevice = d;
+
+ AU_ALLOC_DEVICE(d, 2, 1);
+ d->id = FakeClientID(SERVER_CLIENT);
+ d->changableMask = auPhysicalOutputChangableMask;
+ d->valueMask = auPhysicalOutputValueMask;
+ d->kind = AuComponentKindPhysicalOutput;
+ d->use = AuComponentUseExportMask;
+ d->access = AuAccessExportMask | AuAccessListMask;
+ d->format = formatOut;
+ d->numTracks = 2;
+ d->description.type = AuStringLatin1;
+ d->description.string = "Stereo Channel Output";
+ d->description.len = strlen(d->description.string);
+ d->minSampleRate = sndStatOut.minSampleRate;
+ d->maxSampleRate = sndStatOut.maxSampleRate;
+ d->location = AuDeviceLocationCenterMask | AuDeviceLocationInternalMask;
+ d->numChildren = 1;
+ d->children = (AuID *) ((AuUint8 *) d + PAD4(sizeof(ComponentRec)));
+ d->childSwap = (char *) (d->children + d->numChildren);
+ d->children[0] = monoOutputDevice->id;
+ d->minibuf = auOutputStereo;
+ d->minibufSize = d->numTracks * bytesPerSampleOut * auMinibufSamples;
+ d->physicalDeviceMask = PhysicalOutputStereo;
+ AU_ADD_DEVICE(d);
+
+ stereoOutputDevice = d;
+
+ AU_ALLOC_DEVICE(d, (sndStatIn.isStereo + 1), 0);
+ d->id = FakeClientID(SERVER_CLIENT);
+ d->changableMask = auPhysicalInputChangableMask;
+ d->valueMask = auPhysicalInputValueMask;
+ d->kind = AuComponentKindPhysicalInput;
+ d->use = AuComponentUseImportMask;
+ d->access = AuAccessImportMask | AuAccessListMask;
+ d->format = formatIn;
+ d->numTracks = sndStatIn.isStereo + 1;
+ d->description.type = AuStringLatin1;
+ d->description.string = (sndStatIn.isStereo) ? "Stereo Channel Input"
+ : "Mono Channel Input";
+ d->description.len = strlen(d->description.string);
+ d->minSampleRate = sndStatOut.minSampleRate;
+ d->maxSampleRate = sndStatOut.maxSampleRate;
+ d->location = AuDeviceLocationRightMask | AuDeviceLocationLeftMask
+ | AuDeviceLocationExternalMask;
+ d->numChildren = 0;
+ d->gain = auDefaultInputGain;
+ d->minibuf = auInput;
+ d->minibufSize = d->numTracks * bytesPerSampleIn * auMinibufSamples;
+ d->physicalDeviceMask = (sndStatIn.isStereo) ? PhysicalInputStereo
+ : PhysicalInputMono;
+ AU_ADD_DEVICE(d);
+
+ monoInputDevice = d; /* Should have two input devices - FIXME */
+ stereoInputDevice = d;
+
+ /* set the array of server devices */
+ if (!(auServerDevices = (ComponentPtr *) aualloc(sizeof(ComponentPtr)
+ * auNumServerDevices))) {
+ UNIDENTMSG;
+ return AuBadAlloc;
+ }
+
+ p = auServerDevices;
+ d = auDevices;
+
+ while (d) {
+ *p++ = d;
+ d = d->next;
+ }
+
+ if (!initialized) {
+ initialized = AuTrue;
+ setPhysicalOutputGain(auDefaultOutputGain);
+ setPhysicalInputGainAndLineMode(auDefaultInputGain, 0);
+ }
+
+ UNIDENTMSG;
+
+ return AuSuccess;
+}
+
+static AuInt32 setTimer(rate)
+AuInt32 rate;
+{
+ AuInt16 timer_ms;
+ AuInt32 foo;
+ struct itimerval ntval, otval;
+
+ PRMSG("setTimer(rate = %d);\n", rate, 0);
+ IDENTMSG;
+
+ /* change timer according to new sample rate */
+ if (rate == 0) { /* Disable timer case */
+ ntval.it_value.tv_sec = ntval.it_value.tv_usec = 0;
+ ntval.it_interval.tv_sec = ntval.it_interval.tv_usec = 0;
+ timer_ms = 0x7fff;
+ } else {
+ timer_ms = (auMinibufSamples * 500) / rate;
+ ntval.it_interval.tv_sec = 0;
+ ntval.it_interval.tv_usec = timer_ms * 1000;
+ ntval.it_value.tv_sec = 0;
+ ntval.it_value.tv_usec = timer_ms *1000 / 2 ;
+ }
+ foo = setitimer(ITIMER_REAL, &ntval, &otval);
+
+ UNIDENTMSG;
+
+ return timer_ms;
+}
+
+
+#ifdef sco
+static void
+oneMoreTick()
+{
+ struct itimerval ntval, otval;
+ int foo;
+
+ ntval.it_interval.tv_sec = 0;
+ ntval.it_interval.tv_usec = 0;
+ ntval.it_value.tv_sec = 0;
+ ntval.it_value.tv_usec = 10;
+ foo = setitimer(ITIMER_REAL, &ntval, &otval);
+}
+#endif /* sco */
+
+
+static void
+setFragmentSize(sndStatPtr)
+SndStat *sndStatPtr;
+{
+ int fragarg, i, j;
+ int datarate, numfrags;
+
+ datarate = sndStatPtr->curSampleRate;
+ if (sndStatPtr->isStereo)
+ datarate *= 2;
+ if (sndStatPtr->wordSize == 16)
+ datarate *= 2;
+ datarate /= 2; /* half second */
+ numfrags = datarate / MAX_MINIBUF_SAMPLES;
+ if (numfrags < sndStatPtr->minFrags)
+ numfrags = sndStatPtr->minFrags;
+ else if (numfrags > sndStatPtr->maxFrags)
+ numfrags = sndStatPtr->maxFrags;
+
+ j = MAX_MINIBUF_SAMPLES;
+ for (i = 0; j; i++) /* figure out what power of 2 MAX_MINIBUF_SAMPLES is */
+ j = j >> 1;
+ fragarg = (numfrags << 16) | i; /* numfrags of size MAX_MINIBUF_SAMPLES */
+ ioctl(sndStatPtr->fd, SNDCTL_DSP_SETFRAGMENT, &fragarg);
+}
+
+
+static AuUint32 setSampleRate(rate)
+AuUint32 rate;
+{
+ int numSamplesIn, numSamplesOut;
+ AuBlock l;
+
+ PRMSG("setSampleRate(rate = %d);\n", rate, 0);
+ IDENTMSG;
+
+ l = AuBlockAudio();
+
+ if (sndStatOut.curSampleRate != rate) {
+ sndStatOut.curSampleRate = rate;
+
+#if defined(SNDCTL_DSP_SETFRAGMENT)
+ setFragmentSize(&sndStatOut);
+#endif
+ ioctl(sndStatOut.fd, SNDCTL_DSP_SYNC, NULL);
+ ioctl(sndStatOut.fd, SNDCTL_DSP_SPEED, &(sndStatOut.curSampleRate));
+ }
+
+ if (sndStatIn.fd == sndStatOut.fd)
+ sndStatIn = sndStatOut;
+ else if (sndStatIn.curSampleRate != rate) {
+ sndStatIn.curSampleRate = rate;
+
+#if defined(SNDCTL_DSP_SETFRAGMENT)
+ setFragmentSize(&sndStatIn);
+#endif
+ ioctl(sndStatIn.fd, SNDCTL_DSP_SYNC, NULL);
+ ioctl(sndStatIn.fd, SNDCTL_DSP_SPEED, &(sndStatIn.curSampleRate));
+ }
+
+#if defined(AUDIO_DRAIN)
+ if (sndStatOut.isPCSpeaker)
+ ioctl (sndStatOut.fd, AUDIO_DRAIN, 0);
+#endif
+
+ AuUnBlockAudio(l);
+
+ setTimer(rate);
+
+ UNIDENTMSG;
+
+ return sndStatOut.curSampleRate;
+}
+
+
+static void serverReset()
+{
+ PRMSG("serverReset();\n", 0, 0);
+ IDENTMSG;
+
+ setTimer(0);
+#ifndef sco
+ signal(SIGALRM, SIG_IGN);
+#endif /* sco */
+
+ PRMSG("Audio Synchronisation...", 0, 0);
+
+#if defined(AUDIO_DRAIN)
+ if (sndStatOut.isPCSpeaker)
+ ioctl(sndStatOut.fd,AUDIO_DRAIN,0);
+ else {
+#endif
+
+ ioctl(sndStatIn.fd, SNDCTL_DSP_SYNC, NULL);
+ if (sndStatOut.fd != sndStatIn.fd)
+ ioctl(sndStatOut.fd, SNDCTL_DSP_SYNC, NULL);
+
+#if defined(AUDIO_DRAIN)
+ }
+#endif
+
+ if (doDebug) {
+ fputs(" done.\n", stderr);
+ fflush(stderr);
+ }
+ UNIDENTMSG;
+}
+
+
+static void
+#ifdef sco
+intervalProc(int i)
+#else
+intervalProc()
+#endif /* sco */
+{
+ extern void AuProcessData();
+
+#ifndef sco
+ signal(SIGALRM, SIG_IGN);
+
+ if (processFlowEnabled)
+ {
+ AuProcessData();
+ signal(SIGALRM, intervalProc);
+ }
+#else
+ if (!scoAudioBlocked && processFlowEnabled)
+ AuProcessData();
+#endif /* sco */
+}
+
+/**
+ * Gains are mapped thusly:
+ *
+ * Software s 0 - 100
+ * Hardware h 0 - 100
+**/
+
+static void
+setPhysicalOutputGain(gain)
+ AuFixedPoint gain;
+{
+ AuInt32 g = AuFixedPointIntegralAddend(gain);
+ AuInt32 i, gusvolume;
+
+
+ if (g > 100)
+ g = 100;
+ if (g < 0)
+ g = 0;
+ Letsplay = g;
+ gusvolume = g | (g << 8);
+ if (mixerfd != -1)
+ i = ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_PCM), &gusvolume);
+}
+
+static AuFixedPoint
+getPhysicalOutputGain()
+{
+ AuInt16 outputGain;
+
+ outputGain = Letsplay;
+
+ return AuFixedPointFromSum(outputGain, 0);
+}
+
+static void
+setPhysicalInputGainAndLineMode(gain, lineMode)
+ AuFixedPoint gain;
+ AuUint8 lineMode;
+{
+ AuInt16 g = AuFixedPointIntegralAddend(gain);
+ AuInt16 inputAttenuation;
+ AuInt16 zero = 0;
+ AuInt32 params[4];
+
+ if (g < 100)
+ inputAttenuation = g;
+ else
+ inputAttenuation = 100;
+
+ inputAttenuation = inputAttenuation << 8 | inputAttenuation;
+
+ if (lineMode == AuDeviceLineModeHigh) {
+ ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_MIC), &inputAttenuation);
+ ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_LINE), &zero);
+ }
+
+ if (lineMode == AuDeviceLineModeLow) {
+ ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_LINE), &inputAttenuation);
+ ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_MIC), &zero);
+ }
+}
+
+static void enableProcessFlow()
+{
+ AuUint8 *p;
+
+ PRMSG("enableProcessFlow();\n", 0, 0);
+
+#ifdef sco
+ if (!processFlowEnabled)
+ {
+#endif /* sco */
+
+ processFlowEnabled = AuTrue;
+
+#ifndef sco
+ signal(SIGALRM, intervalProc);
+#else
+ setTimer(sndStatOut.curSampleRate);
+#endif /* sco */
+
+#ifdef sco
+ }
+#endif /* sco */
+}
+
+static void disableProcessFlow()
+{
+
+#ifndef sco
+ signal(SIGALRM, SIG_IGN);
+#endif /* sco */
+
+ PRMSG("disableProcessFlow();\n", 0, 0);
+
+#ifdef sco
+ if (processFlowEnabled)
+ {
+#endif /* sco */
+
+ ioctl(sndStatOut.fd, SNDCTL_DSP_SYNC, NULL);
+#ifndef sco
+ ioctl(sndStatOut.fd, SNDCTL_DSP_SPEED, &sndStatOut.curSampleRate);
+#endif /* sco */
+
+ if (sndStatOut.fd != sndStatIn.fd)
+ {
+ ioctl(sndStatIn.fd, SNDCTL_DSP_SYNC, NULL);
+#ifndef sco
+ ioctl(sndStatIn.fd, SNDCTL_DSP_SPEED, &sndStatIn.curSampleRate);
+#endif /* sco */
+ }
+
+#ifdef sco
+ oneMoreTick();
+#endif
+
+ processFlowEnabled = AuFalse;
+
+#ifdef sco
+ }
+#endif /* sco */
+}
+
+
+#if defined(__GNUC__) && !defined(linux)
+inline
+#endif
+static void monoToStereoLinearSigned16LSB(numSamples)
+AuUint32 numSamples;
+{
+ AuInt16 *s = (AuInt16*)monoOutputDevice->minibuf;
+ AuInt16 *d = (AuInt16*)stereoOutputDevice->minibuf;
+
+ while (numSamples--) {
+ *d++ = *s;
+ *d++ = *s++;
+ }
+}
+
+#if defined(__GNUC__) && !defined(linux)
+inline
+#endif
+static void monoToStereoLinearUnsigned8(numSamples)
+AuUint32 numSamples;
+{
+ AuUint8 *s = (AuUint8*)monoOutputDevice->minibuf;
+ AuUint8 *d = (AuUint8*)stereoOutputDevice->minibuf;
+
+ while (numSamples--) {
+ *d++ = *s;
+ *d++ = *s++;
+ }
+}
+
+static void writePhysicalOutputsMono()
+{
+ AuBlock l;
+ void* buf;
+ int bufSize;
+
+ if (sndStatOut.isStereo) {
+ switch (monoOutputDevice->format) {
+ case AuFormatLinearSigned16LSB:
+ monoToStereoLinearSigned16LSB(monoOutputDevice->minibufSamples);
+ break;
+
+ case AuFormatLinearUnsigned8:
+ monoToStereoLinearUnsigned8(monoOutputDevice->minibufSamples);
+ break;
+
+ default:
+ /* check createServerComponents(...)! */
+ assert(0);
+ }
+
+ buf = stereoOutputDevice->minibuf;
+ bufSize = stereoOutputDevice->bytesPerSample
+ * monoOutputDevice->minibufSamples;
+ } else {
+ buf = monoOutputDevice->minibuf;
+ bufSize = monoOutputDevice->bytesPerSample
+ * monoOutputDevice->minibufSamples;
+ }
+
+ l = AuBlockAudio();
+ { int b, w;
+ char *p = buf ;
+ for (b=bufSize; b>0; b -= w) {
+ w = write(sndStatOut.fd, p, b);
+ p += w ;
+ }
+ }
+
+#ifdef DEBUGDSPOUT
+ {
+ char tempbuf[80];
+
+ sprintf(tempbuf, "\nwriteMono buf: %d size: %d\n", buf, bufSize);
+ write(dspout, tempbuf, strlen(tempbuf));
+ write(dspout, buf, bufSize);
+ }
+#endif /* DEBUGDSPOUT */
+
+ AuUnBlockAudio(l);
+}
+
+#if defined(__GNUC__) && !defined(linux)
+inline
+#endif
+static void stereoToMonoLinearSigned16LSB(numSamples)
+AuUint32 numSamples;
+{
+ AuInt16 *s = (AuInt16*)stereoOutputDevice->minibuf;
+ AuInt16 *d = (AuInt16*)monoOutputDevice->minibuf;
+
+ while (numSamples--) {
+ *d++ = (s[0] + s[1]) / 2;
+ s += 2;
+ }
+}
+
+#if defined(__GNUC__) && !defined(linux)
+inline
+#endif
+static void stereoToMonoLinearUnsigned8(numSamples)
+AuUint32 numSamples;
+{
+ AuUint8 *s = (AuUint8*)stereoOutputDevice->minibuf;
+ AuUint8 *d = (AuUint8*)monoOutputDevice->minibuf;
+
+ while (numSamples--) {
+ *d++ = (s[0] + s[1]) / 2;
+ s += 2;
+ }
+}
+
+static void writePhysicalOutputsStereo()
+{
+ AuBlock l;
+ void* buf;
+ int bufSize;
+
+ if (sndStatOut.isStereo) {
+ buf = stereoOutputDevice->minibuf;
+ bufSize = stereoOutputDevice->bytesPerSample
+ * stereoOutputDevice->minibufSamples;
+ } else {
+ switch (stereoOutputDevice->format) {
+ case AuFormatLinearSigned16LSB:
+ stereoToMonoLinearSigned16LSB(stereoOutputDevice->minibufSamples);
+ break;
+
+ case AuFormatLinearUnsigned8:
+ stereoToMonoLinearUnsigned8(stereoOutputDevice->minibufSamples);
+ break;
+
+ default:
+ /* check createServerComponents(...)! */
+ assert(0);
+ }
+
+ buf = monoOutputDevice->minibuf;
+ bufSize = monoOutputDevice->bytesPerSample
+ * stereoOutputDevice->minibufSamples;
+ }
+
+ l = AuBlockAudio();
+ { int b, w;
+ char *p = buf ;
+ for (b=bufSize; b>0; b -= w) {
+ w = write(sndStatOut.fd, p, b);
+ p += w ;
+ }
+ }
+
+#ifdef DEBUGDSPOUT
+ {
+ char tempbuf[80];
+
+ sprintf(tempbuf, "\nwriteStereo buf: %d size: %d\n", buf, bufSize);
+ write(dspout, tempbuf, strlen(tempbuf));
+ write(dspout, buf, bufSize);
+ }
+#endif /* DEBUGDSPOUT */
+
+ AuUnBlockAudio(l);
+}
+
+static void writePhysicalOutputsBoth()
+{
+ AuInt16 *m = (AuInt16 *) monoOutputDevice->minibuf;
+ AuInt16 *p, *s;
+ AuUint8 *m8 = (AuUint8 *) monoOutputDevice->minibuf;
+ AuUint8 *p8, *s8;
+ AuUint32 max = aumax(monoOutputDevice->minibufSamples,
+ stereoOutputDevice->minibufSamples);
+ AuUint32 i;
+
+ switch (stereoOutputDevice->format) {
+ case AuFormatLinearSigned16LSB:
+ p = s = (AuInt16 *) stereoOutputDevice->minibuf;
+
+ for (i = 0; i < max; i++) {
+ *p++ = (*m + *s++) / 2;
+ *p++ = (*m++ + *s++) / 2;
+ }
+ break;
+
+ case AuFormatLinearUnsigned8:
+ p8 = s8 = (AuUint8 *) stereoOutputDevice->minibuf;
+
+ for (i = 0; i < max; i++) {
+ *p8++ = (*m8 + *s8++) / 2;
+ *p8++ = (*m8++ + *s8++) / 2;
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+
+ stereoOutputDevice->minibufSamples = max;
+
+ writePhysicalOutputsStereo();
+}
+
+static void readPhysicalInputs()
+{
+ AuBlock l;
+ char *p = stereoInputDevice->minibuf ;
+ int b,r ;
+
+ /* Should make use of two input devices - FIXME */
+ l = AuBlockAudio();
+ for (b = stereoInputDevice->bytesPerSample * auMinibufSamples;
+ b > 0 ; b -= r) {
+ r = read(sndStatIn.fd, p, b);
+ p += r ;
+ }
+
+#ifdef DEBUGDSPIN
+ {
+ char tempbuf[80];
+ sprintf(tempbuf, "\nreadInputs buf: %d size: %d\n",
+ stereoInputDevice->minibuf,
+ stereoInputDevice->bytesPerSample * auMinibufSamples);
+ write(dspin, tempbuf, strlen(tempbuf));
+ write(dspin, stereoInputDevice->minibuf,
+ stereoInputDevice->bytesPerSample * auMinibufSamples);
+ }
+#endif /* DEBUGDSPIN */
+
+ AuUnBlockAudio(l);
+}
+
+static void
+noop()
+{
+}
+
+static void
+setWritePhysicalOutputFunction(flow, funct)
+ CompiledFlowPtr flow;
+ void (**funct) ();
+{
+ AuUint32 mask = flow->physicalDeviceMask;
+
+ if ((mask & (PhysicalOutputMono | PhysicalOutputStereo)) ==
+ (PhysicalOutputMono | PhysicalOutputStereo))
+ *funct = writePhysicalOutputsBoth;
+ else if (mask & PhysicalOutputMono)
+ *funct = writePhysicalOutputsMono;
+ else if (mask & PhysicalOutputStereo)
+ *funct = writePhysicalOutputsStereo;
+ else
+ *funct = noop;
+}
+
+/*
+ * Setup soundcard at maximum audio quality.
+ */
+
+#ifdef __FreeBSD__
+#define NO_16_BIT_SAMPLING
+#endif
+
+static void setupSoundcard(sndStatPtr)
+SndStat* sndStatPtr;
+{
+ int samplesize;
+
+ PRMSG("setupSoundcard(...);\n", 0, 0);
+ IDENTMSG;
+
+ if (beVerbose)
+ if (sndStatPtr == &sndStatOut)
+ fprintf (stderr, "++ Setting up Output device\n");
+ else
+ fprintf (stderr, "++ Setting up Input device\n");
+
+ if (sndStatPtr->isPCSpeaker) {
+ if (beVerbose)
+ fprintf(stderr, "+++ Device is a PC speaker\n");
+ sndStatPtr->curSampleRate = sndStatPtr->maxSampleRate
+ = sndStatPtr->minSampleRate = 8000;
+ sndStatPtr->isStereo = 0;
+ sndStatPtr->wordSize = 8;
+ }
+ else {
+ if (beVerbose)
+ fprintf(stderr, "+++ requesting wordsize of %d, ", sndStatPtr->wordSize);
+ if (ioctl(sndStatPtr->fd, SNDCTL_DSP_SAMPLESIZE, &sndStatPtr->wordSize)
+ || sndStatPtr->wordSize != 16) {
+ sndStatPtr->wordSize = 8;
+ ioctl(sndStatPtr->fd, SNDCTL_DSP_SAMPLESIZE, &sndStatPtr->wordSize);
+ }
+ if (beVerbose)
+ fprintf(stderr, "got %d\n", sndStatPtr->wordSize);
+
+ if (beVerbose)
+ fprintf(stderr, "+++ requesting %d channel(s), ", sndStatPtr->isStereo + 1);
+ if (ioctl(sndStatPtr->fd, SNDCTL_DSP_STEREO, &sndStatPtr->isStereo) == -1
+ || !sndStatPtr->isStereo) {
+ sndStatPtr->isStereo = 0;
+ ioctl(sndStatPtr->fd, SNDCTL_DSP_STEREO, &sndStatPtr->isStereo);
+ }
+ if (beVerbose)
+ fprintf(stderr, "got %d channel(s)\n", sndStatPtr->isStereo + 1);
+
+ if (beVerbose)
+ fprintf (stderr, "+++ Requesting minimum sample rate of %d, ", sndStatPtr->minSampleRate);
+ ioctl(sndStatPtr->fd, SNDCTL_DSP_SPEED, &sndStatPtr->minSampleRate);
+ if (beVerbose)
+ fprintf (stderr, "got %d\n", sndStatPtr->minSampleRate);
+
+ if (beVerbose)
+ fprintf (stderr, "+++ Requesting maximum sample rate of %d, ", sndStatPtr->maxSampleRate);
+ ioctl(sndStatPtr->fd, SNDCTL_DSP_SPEED, &sndStatPtr->maxSampleRate);
+ if (beVerbose)
+ fprintf (stderr, "got %d\n", sndStatPtr->maxSampleRate);
+
+ sndStatPtr->curSampleRate = sndStatPtr->maxSampleRate;
+
+ }
+
+#if defined(SNDCTL_DSP_SETFRAGMENT)
+ setFragmentSize(sndStatPtr);
+#endif
+
+ UNIDENTMSG;
+}
+
+
+#ifdef sco
+static AuBool
+scoInterrupts()
+{
+ struct sigaction act;
+
+ act.sa_handler = intervalProc;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGALRM);
+ if (sigaction(SIGALRM, &act, (struct sigaction *)NULL) == -1)
+ {
+ ErrorF("sigaction SIGALRM");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif /* sco */
+
+/*
+* Find the config file
+*/
+
+static FILE *openConfigFile (char *path)
+{
+ static char buf[1024];
+ FILE *config;
+
+ strcat (buf, path);
+ strcat (buf, "AUVoxConfig");
+ if ((config = fopen (buf, "r")) != NULL)
+ return config;
+ else
+ return NULL;
+}
+
+
+AuBool AuInitPhysicalDevices()
+{
+ static AuBool AL_initialized = AuFalse;
+ static AuUint8 *physicalBuffers;
+ int fd;
+ AuUint32 physicalBuffersSize;
+ extern AuUint8 *auPhysicalOutputBuffers;
+ extern AuUint32 auPhysicalOutputBuffersSize;
+ extern void AuProcessData();
+#if defined(AUDIO_GETINFO)
+ audio_info_t spkrinf;
+#endif
+
+ PRMSG("AuInitPhysicalDevices();\n", 0, 0);
+ IDENTMSG;
+
+ /*
+ * create the input and output ports
+ */
+ if (!AL_initialized) {
+ int fd;
+ AuInt32 i;
+
+ AL_initialized = AuTrue;
+
+ if ((yyin = openConfigFile (CONFSEARCHPATH)) != NULL)
+ yyparse();
+
+ if ((fd = open(sndStatOut.device, O_RDWR, 0)) == -1) {
+ UNIDENTMSG;
+ return AuFalse;
+ }
+#if defined(AUDIO_GETINFO)
+ if (sndStatOut.isPCSpeaker) {
+ ioctl(fd, AUDIO_GETINFO, &spkrinf);
+ spkrinf.play.encoding = AUDIO_ENCODING_RAW;
+ ioctl(fd, AUDIO_SETINFO, &spkrinf);
+ }
+#endif
+
+#ifdef DEBUGDSPOUT
+ dspout = open("/tmp/dspout", O_RDWR | O_CREAT, 00666);
+#endif
+#ifdef DEBUGDSPIN
+ dspin = open("/tmp/dspin", O_RDWR | O_CREAT, 00666);
+#endif
+
+ sndStatOut.fd = fd;
+
+ if ((fd = open(sndStatIn.device, O_RDONLY, 0)) != -1)
+ sndStatIn.fd = fd;
+ else
+ sndStatIn.fd = sndStatOut.fd;
+
+ setupSoundcard(&sndStatOut);
+ if (sndStatOut.fd != sndStatIn.fd)
+ setupSoundcard(&sndStatIn);
+
+ if (!sndStatOut.isPCSpeaker) {
+ if ((mixerfd = open("/dev/mixer", O_RDONLY, 0)) == -1) {
+ UNIDENTMSG;
+ return AuFalse;
+ }
+
+ if (ioctl(mixerfd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
+ close(mixerfd);
+ mixerfd = -1;
+ } else {
+ if (ioctl(mixerfd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) {
+ return AuFalse;
+ }
+
+ {
+ /* Enable all used recording sources ( mic & line ) and
+ * control which is active via level setting later. There
+ * should be a better way to do this!
+ */
+ int mask = SOUND_MASK_MIC | SOUND_MASK_LINE; /* enable these */
+ mask &= recmask; /* if supported */
+ if (ioctl(mixerfd, SOUND_MIXER_WRITE_RECSRC, &mask) == -1) {
+ return AuFalse;
+ }
+ }
+
+ if (ioctl(mixerfd, SOUND_MIXER_READ_RECSRC, &recsrc) == -1) {
+ UNIDENTMSG;
+ return AuFalse;
+ }
+
+ if (ioctl(mixerfd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1) {
+ UNIDENTMSG;
+ return AuFalse;
+ }
+
+ /* get all sound levels */
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ if ((1 << i) & devmask) {
+
+ if (ioctl(mixerfd, MIXER_READ(i), &level[i]) == -1) {
+ UNIDENTMSG;
+ return AuFalse;
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ if (physicalBuffers)
+ aufree(physicalBuffers);
+
+ auMinibufSamples = MAX_MINIBUF_SAMPLES;
+
+ /* the output buffers need to be twice as large for output range checking */
+ physicalBuffersSize =
+ PhysicalTwoTrackBufferSize + /* mono/stereo input */
+ PhysicalOneTrackBufferSize * 2 + /* mono output */
+ PhysicalTwoTrackBufferSize * 2; /* stereo output */
+
+ if (!(physicalBuffers = (AuUint8 *) aualloc(physicalBuffersSize))) {
+ UNIDENTMSG;
+ return AuFalse;
+ }
+
+ auInput = physicalBuffers;
+ auOutputMono = auInput + PhysicalTwoTrackBufferSize;
+ auOutputStereo = auOutputMono + 2 * PhysicalOneTrackBufferSize;
+
+ auPhysicalOutputBuffers = auOutputMono;
+ auPhysicalOutputBuffersSize = physicalBuffersSize -
+ PhysicalTwoTrackBufferSize;
+
+ /*
+ * Call AuProcessData() in signal handler often enough to drain the
+ * input devices and keep the output devices full at the current
+ * sample rate.
+ */
+
+ processFlowEnabled = AuFalse;
+
+#ifdef sco
+ if (!scoInterrupts())
+ {
+ return AuFalse;
+ }
+#else
+ signal(SIGALRM, intervalProc);
+#endif /* sco */
+
+ setTimer(0);
+
+ AuRegisterCallback(AuCreateServerComponentsCB, createServerComponents);
+ AuRegisterCallback(AuSetPhysicalOutputGainCB, setPhysicalOutputGain);
+ AuRegisterCallback(AuGetPhysicalOutputGainCB, getPhysicalOutputGain);
+ AuRegisterCallback(AuSetPhysicalInputGainAndLineModeCB,
+ setPhysicalInputGainAndLineMode);
+ AuRegisterCallback(AuEnableProcessFlowCB, enableProcessFlow);
+ AuRegisterCallback(AuDisableProcessFlowCB, disableProcessFlow);
+ AuRegisterCallback(AuReadPhysicalInputsCB, readPhysicalInputs);
+ AuRegisterCallback(AuSetWritePhysicalOutputFunctionCB,
+ setWritePhysicalOutputFunction);
+
+ AuRegisterCallback(AuSetSampleRateCB, setSampleRate);
+
+ /* bogus resource so we can have a cleanup function at server reset */
+ AddResource(FakeClientID(SERVER_CLIENT),
+ CreateNewResourceType(serverReset), 0);
+
+ UNIDENTMSG;
+
+ return AuTrue;
+}
diff --git a/sys/i386/isa/snd/misc/files.i386 b/sys/i386/isa/snd/misc/files.i386
new file mode 100644
index 0000000..bc12938
--- /dev/null
+++ b/sys/i386/isa/snd/misc/files.i386
@@ -0,0 +1,240 @@
+# This file tells config what files go into building a kernel,
+# files marked standard are always included.
+#
+# $Id: files.i386,v 1.141.2.11 1997/04/04 04:35:46 gibbs Exp $
+#
+aic7xxx_asm optional ahc device-driver \
+ dependency "$S/dev/aic7xxx/*.[chyl]" \
+ compile-with "cd $S/dev/aic7xxx; make obj; make BINDIR=${.CURDIR} all install" \
+ no-obj no-implicit-rule \
+ clean "aic7xxx_asm"
+#
+aic7xxx_{seq,reg}.h optional ahc device-driver \
+ compile-with "./aic7xxx_asm ${INCLUDES} -o aic7xxx_seq.h -r aic7xxx_reg.h $S/dev/aic7xxx/aic7xxx.seq" \
+ no-obj no-implicit-rule before-depend \
+ clean "aic7xxx_seq.h aic7xxx_reg.h" \
+ dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} aic7xxx_asm"
+#
+linux_genassym optional compat_linux \
+ dependency "$S/i386/linux/linux_genassym.c $S/i386/linux/linux.h" \
+ compile-with "${CC} ${CFLAGS} ${PARAM} -UKERNEL -o $@ $<" \
+ no-obj no-implicit-rule \
+ clean "linux_genassym"
+#
+linux_assym.h optional compat_linux \
+ dependency "linux_genassym" \
+ compile-with "./linux_genassym > $@" \
+ no-obj no-implicit-rule before-depend \
+ clean "linux_assym.h"
+#
+i386/scsi/93cx6.c optional ahc device-driver
+i386/apm/apm.c optional apm device-driver
+i386/apm/apm_setup.s optional apm
+i386/eisa/3c5x9.c optional ep device-driver
+i386/eisa/aic7770.c optional ahc device-driver \
+ dependency "aic7xxx_reg.h $S/i386/eisa/aic7770.c"
+i386/eisa/aha1742.c optional ahb device-driver
+i386/eisa/bt74x.c optional bt device-driver
+i386/eisa/eisaconf.c optional eisa
+i386/eisa/if_vx_eisa.c optional vx device-driver
+i386/eisa/if_fea.c optional fea device-driver
+i386/i386/autoconf.c standard device-driver
+i386/i386/cons.c standard
+i386/i386/db_disasm.c optional ddb
+i386/i386/db_interface.c optional ddb
+i386/i386/db_trace.c optional ddb
+i386/i386/i386-gdbstub.c optional ddb
+i386/i386/exception.s standard
+i386/i386/identcpu.c standard
+i386/i386/in_cksum.c optional inet
+# locore.s needs to be handled in Makefile to put it first. Otherwise it's
+# now normal.
+# i386/i386/locore.s standard
+i386/i386/machdep.c standard
+i386/i386/math_emulate.c optional math_emulate
+i386/i386/mem.c standard
+i386/i386/microtime.s standard
+i386/i386/perfmon.c optional perfmon profiling-routine
+i386/i386/perfmon.c optional perfmon
+i386/i386/pmap.c standard
+i386/i386/procfs_machdep.c standard
+i386/i386/support.s standard
+i386/i386/swtch.s standard
+i386/i386/sys_machdep.c standard
+i386/i386/trap.c standard
+i386/i386/userconfig.c optional userconfig
+i386/i386/vm_machdep.c standard
+i386/ibcs2/ibcs2_fcntl.c optional ibcs2
+i386/ibcs2/ibcs2_stat.c optional ibcs2
+i386/ibcs2/ibcs2_ipc.c optional ibcs2
+i386/ibcs2/ibcs2_msg.c optional ibcs2
+i386/ibcs2/ibcs2_misc.c optional ibcs2
+i386/ibcs2/ibcs2_other.c optional ibcs2
+i386/ibcs2/ibcs2_signal.c optional ibcs2
+i386/ibcs2/ibcs2_ioctl.c optional ibcs2
+i386/ibcs2/ibcs2_socksys.c optional ibcs2
+i386/ibcs2/ibcs2_sysi86.c optional ibcs2
+i386/ibcs2/ibcs2_util.c optional ibcs2
+i386/ibcs2/ibcs2_isc.c optional ibcs2
+i386/ibcs2/ibcs2_isc_sysent.c optional ibcs2
+i386/ibcs2/ibcs2_xenix.c optional ibcs2
+i386/ibcs2/ibcs2_xenix_sysent.c optional ibcs2
+i386/ibcs2/ibcs2_errno.c optional ibcs2
+i386/ibcs2/ibcs2_sysent.c optional ibcs2
+i386/ibcs2/ibcs2_sysvec.c optional ibcs2
+i386/ibcs2/imgact_coff.c optional ibcs2
+i386/isa/aha1542.c optional aha device-driver
+i386/isa/aic6360.c optional aic device-driver
+i386/isa/b004.c optional bqu device-driver
+i386/isa/bt5xx-445.c optional bt device-driver
+i386/isa/clock.c standard
+i386/isa/cronyx.c optional cx device-driver
+i386/isa/ctx.c optional ctx device-driver
+i386/isa/cx.c optional cx device-driver
+i386/isa/cy.c optional cy device-driver
+i386/isa/diskslice_machdep.c standard
+i386/isa/elink.c optional ep device-driver
+i386/isa/elink.c optional ie device-driver
+i386/isa/fd.c optional fd device-driver
+i386/isa/ft.c optional ft device-driver
+i386/isa/gpib.c optional gp device-driver
+i386/isa/asc.c optional asc device-driver
+i386/isa/gsc.c optional gsc device-driver
+i386/isa/if_ar.c optional ar device-driver
+i386/isa/if_cx.c optional cx device-driver
+i386/isa/if_ed.c optional ed device-driver
+i386/isa/if_eg.c optional eg device-driver
+i386/isa/if_el.c optional el device-driver
+i386/isa/if_ep.c optional ep device-driver
+i386/isa/if_ex.c optional ex device-driver
+i386/isa/if_fe.c optional fe device-driver
+i386/isa/if_ie.c optional ie device-driver
+i386/isa/if_ix.c optional ix device-driver
+i386/isa/if_le.c optional le device-driver
+i386/isa/if_lnc.c optional lnc device-driver
+i386/isa/if_sr.c optional sr device-driver
+i386/isa/if_ze.c optional ze device-driver
+i386/isa/if_zp.c optional zp device-driver
+i386/isa/isa.c optional isa device-driver
+i386/isa/istallion.c optional stli device-driver
+i386/isa/joy.c optional joy device-driver
+i386/isa/kbdio.c optional psm device-driver
+i386/isa/kbdio.c optional sc device-driver
+i386/isa/kbdio.c optional vt device-driver
+i386/isa/lpt.c optional lpt device-driver
+i386/isa/labpc.c optional labpc device-driver
+i386/isa/mcd.c optional mcd device-driver
+i386/isa/mse.c optional mse device-driver
+i386/isa/ncr5380.c optional nca device-driver
+i386/isa/npx.c optional npx device-driver
+i386/isa/pcaudio.c optional pca device-driver
+i386/isa/matcd/matcd.c optional matcd device-driver
+i386/isa/pcibus.c optional pci device-driver
+i386/isa/pcicx.c optional ze device-driver
+i386/isa/pcicx.c optional zp device-driver
+i386/isa/pcvt/pcvt_drv.c optional vt device-driver
+i386/isa/pcvt/pcvt_ext.c optional vt device-driver
+i386/isa/pcvt/pcvt_kbd.c optional vt device-driver
+i386/isa/pcvt/pcvt_out.c optional vt device-driver
+i386/isa/pcvt/pcvt_sup.c optional vt device-driver
+i386/isa/pcvt/pcvt_vtf.c optional vt device-driver
+i386/isa/pnp.c optional pnp device-driver
+i386/isa/prof_machdep.c optional profiling-routine
+i386/isa/psm.c optional psm device-driver
+i386/isa/qcam.c optional qcam device-driver
+i386/isa/qcamio.c optional qcam device-driver
+i386/isa/random_machdep.c standard
+i386/isa/rc.c optional rc device-driver
+i386/isa/scd.c optional scd device-driver
+i386/isa/seagate.c optional sea device-driver
+i386/isa/si.c optional si device-driver
+i386/isa/si_code.c optional si device-driver
+i386/isa/sio.c optional sio device-driver
+i386/isa/snd/sound.c optional pcm device-driver
+i386/isa/snd/dmabuf.c optional pcm device-driver
+i386/isa/snd/ad1848.c optional pcm device-driver
+i386/isa/snd/sb_dsp.c optional pcm device-driver
+i386/isa/snd/clones.c optional pcm device-driver
+i386/isa/spigot.c optional spigot device-driver
+i386/isa/spkr.c optional speaker device-driver
+i386/isa/stallion.c optional stl device-driver
+i386/isa/syscons.c optional sc device-driver
+i386/isa/tw.c optional tw device-driver
+i386/isa/ultra14f.c optional uha device-driver
+i386/isa/wd.c optional wdc device-driver
+i386/isa/wd.c optional wd device-driver
+i386/isa/atapi.c optional atapi device-driver
+i386/isa/wcd.c optional wcd device-driver
+i386/isa/wd7000.c optional wds device-driver
+i386/isa/wt.c optional wt device-driver
+i386/linux/imgact_linux.c optional compat_linux
+i386/linux/linux_dummy.c optional compat_linux
+i386/linux/linux_file.c optional compat_linux
+i386/linux/linux_ioctl.c optional compat_linux
+i386/linux/linux_ipc.c optional compat_linux
+i386/linux/linux_locore.s optional compat_linux \
+ dependency "linux_assym.h"
+i386/linux/linux_misc.c optional compat_linux
+i386/linux/linux_signal.c optional compat_linux
+i386/linux/linux_socket.c optional compat_linux
+i386/linux/linux_stats.c optional compat_linux
+i386/linux/linux_sysent.c optional compat_linux
+i386/linux/linux_sysvec.c optional compat_linux
+i386/linux/linux_util.c optional compat_linux
+i386/scsi/aic7xxx.c optional ahc device-driver \
+ dependency "aic7xxx_{reg,seq}.h"
+i386/scsi/bt.c optional bt device-driver
+libkern/bcd.c standard
+libkern/divdi3.c standard
+libkern/inet_ntoa.c standard
+libkern/index.c standard
+libkern/mcount.c optional profiling-routine
+libkern/moddi3.c standard
+libkern/qdivrem.c standard
+libkern/qsort.c standard
+libkern/random.c standard
+libkern/scanc.c standard
+libkern/skpc.c standard
+libkern/strcat.c standard
+libkern/strcmp.c standard
+libkern/strcpy.c standard
+libkern/strlen.c standard
+libkern/strncmp.c standard
+libkern/strncpy.c standard
+libkern/udivdi3.c standard
+libkern/umoddi3.c standard
+gnu/i386/fpemul/div_small.s optional gpl_math_emulate
+gnu/i386/fpemul/errors.c optional gpl_math_emulate
+gnu/i386/fpemul/fpu_arith.c optional gpl_math_emulate
+gnu/i386/fpemul/fpu_aux.c optional gpl_math_emulate
+gnu/i386/fpemul/fpu_entry.c optional gpl_math_emulate
+gnu/i386/fpemul/fpu_etc.c optional gpl_math_emulate
+gnu/i386/fpemul/fpu_trig.c optional gpl_math_emulate
+gnu/i386/fpemul/get_address.c optional gpl_math_emulate
+gnu/i386/fpemul/load_store.c optional gpl_math_emulate
+gnu/i386/fpemul/poly_2xm1.c optional gpl_math_emulate
+gnu/i386/fpemul/poly_atan.c optional gpl_math_emulate
+gnu/i386/fpemul/poly_div.s optional gpl_math_emulate
+gnu/i386/fpemul/poly_l2.c optional gpl_math_emulate
+gnu/i386/fpemul/poly_mul64.s optional gpl_math_emulate
+gnu/i386/fpemul/poly_sin.c optional gpl_math_emulate
+gnu/i386/fpemul/poly_tan.c optional gpl_math_emulate
+gnu/i386/fpemul/polynomial.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_add_sub.c optional gpl_math_emulate
+gnu/i386/fpemul/reg_compare.c optional gpl_math_emulate
+gnu/i386/fpemul/reg_constant.c optional gpl_math_emulate
+gnu/i386/fpemul/reg_div.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_ld_str.c optional gpl_math_emulate
+gnu/i386/fpemul/reg_mul.c optional gpl_math_emulate
+gnu/i386/fpemul/reg_norm.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_round.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_u_add.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_u_div.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_u_mul.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_u_sub.s optional gpl_math_emulate
+gnu/i386/fpemul/wm_shrx.s optional gpl_math_emulate
+gnu/i386/fpemul/wm_sqrt.s optional gpl_math_emulate
+gnu/i386/isa/dgb.c optional dgb device-driver
+gnu/i386/isa/nic3008.c optional nic device-driver
+gnu/i386/isa/nic3009.c optional nnic device-driver
+pci/wd82371.c optional wd device-driver
diff --git a/sys/i386/isa/snd/misc/linux.patch b/sys/i386/isa/snd/misc/linux.patch
new file mode 100644
index 0000000..aacfcbc
--- /dev/null
+++ b/sys/i386/isa/snd/misc/linux.patch
@@ -0,0 +1,41 @@
+--- linux.h.orig Tue Dec 3 16:47:28 1996
++++ linux.h Mon Nov 17 00:05:29 1997
+@@ -491,6 +491,9 @@
+ #define LINUX_SNDCTL_DSP_GETOSPACE 0x500C
+ #define LINUX_SNDCTL_DSP_GETISPACE 0x500D
+ #define LINUX_SNDCTL_DSP_NONBLOCK 0x500E
++#define LINUX_SNDCTL_DSP_GETCAPS 0x500F
++#define LINUX_SNDCTL_DSP_GETIPTR 0x5011
++#define LINUX_SNDCTL_DSP_GETOPTR 0x5012
+ #define LINUX_SOUND_MIXER_WRITE_VOLUME 0x4d00
+ #define LINUX_SOUND_MIXER_WRITE_BASS 0x4d01
+ #define LINUX_SOUND_MIXER_WRITE_TREBLE 0x4d02
+--- linux_ioctl.c.orig Sat Nov 9 22:10:15 1996
++++ linux_ioctl.c Mon Nov 17 10:20:14 1997
+@@ -691,6 +691,26 @@
+ args->cmd = SNDCTL_DSP_NONBLOCK;
+ return ioctl(p, (struct ioctl_args *)args, retval);
+
++ case LINUX_SNDCTL_DSP_GETCAPS:
++ args->cmd = SNDCTL_DSP_GETCAPS;
++ return ioctl(p, (struct ioctl_args *)args, retval);
++
++ case LINUX_SNDCTL_DSP_GETIPTR:
++ args->cmd = SNDCTL_DSP_GETIPTR;
++ return ioctl(p, (struct ioctl_args *)args, retval);
++
++ case LINUX_SNDCTL_DSP_GETOPTR:
++ args->cmd = SNDCTL_DSP_GETOPTR;
++ { int a= ioctl(p, (struct ioctl_args *)args, retval);
++ struct count_info *p = (struct count_info *)(args->arg);
++#if 0
++ p->bytes += 128 ;
++ uprintf("GETOPTR bytes %d blk %d ptr %d\n",
++ p->bytes, p->blocks, p->ptr);
++#endif
++ return a;
++ }
++
+ case LINUX_SOUND_MIXER_WRITE_VOLUME:
+ args->cmd = SOUND_MIXER_WRITE_VOLUME;
+ return ioctl(p, (struct ioctl_args *)args, retval);
diff --git a/sys/i386/isa/snd/misc/linux_a.c b/sys/i386/isa/snd/misc/linux_a.c
new file mode 100644
index 0000000..3a86aad
--- /dev/null
+++ b/sys/i386/isa/snd/misc/linux_a.c
@@ -0,0 +1,238 @@
+/*
+ * TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka
+ * Toivonen <toivonen@clinet.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * linux_audio.c
+ *
+ * Functions to play sound on the VoxWare audio driver (Linux or FreeBSD)
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef linux
+#include <sys/ioctl.h> /* new with 1.2.0? Didn't need this under
+ * 1.1.64 */
+#include <linux/soundcard.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <stdio.h>
+#include <machine/soundcard.h>
+#endif
+
+#include "config.h"
+#include "output.h"
+#include "controls.h"
+
+static int open_output(void); /* 0=success, 1=warning, -1=fatal
+ * error */
+static void close_output(void);
+static void output_data(int32 * buf, int32 count);
+static void flush_output(void);
+static void purge_output(void);
+
+/* export the playback mode */
+
+#define dpm linux_play_mode
+
+PlayMode dpm = {
+ DEFAULT_RATE, PE_16BIT | PE_SIGNED,
+ -1,
+ {0}, /* default: get all the buffer fragments you
+ * can */
+ "Linux dsp device", 'd',
+ "/dev/dsp",
+ open_output,
+ close_output,
+ output_data,
+ flush_output,
+ purge_output
+};
+
+
+/*************************************************************************/
+/*
+ * We currently only honor the PE_MONO bit, the sample rate, and the number
+ * of buffer fragments. We try 16-bit signed data first, and then 8-bit
+ * unsigned if it fails. If you have a sound device that can't handle either,
+ * let me know.
+ */
+
+static int
+open_output(void)
+{
+ int fd, tmp, i, warnings = 0;
+
+ /* Open the audio device */
+ fd = open(dpm.name, O_RDWR /* | O_NDELAY */);
+ if (fd < 0) {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+ dpm.name, sys_errlist[errno]);
+ return -1;
+ }
+ /* They can't mean these */
+ dpm.encoding &= ~(PE_ULAW | PE_BYTESWAP);
+
+
+ /*
+ * Set sample width to whichever the user wants. If it fails, try the
+ * other one.
+ */
+
+ i = tmp = (dpm.encoding & PE_16BIT) ? 16 : 8;
+ if (dpm.encoding & PE_16BIT) {
+ int fmt = AFMT_S16_LE ;
+
+ if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0 || fmt != AFMT_S16_LE) {
+ fmt = AFMT_U8 ;
+ if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0 || fmt != AFMT_U8) {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s doesn't support 16- or 8-bit sample width",
+ dpm.name);
+ close(fd);
+ return -1;
+ }
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
+ "Sample width adjusted to %d bits", tmp);
+ dpm.encoding ^= PE_16BIT;
+ warnings = 1;
+ }
+ }
+ if (dpm.encoding & PE_16BIT)
+ dpm.encoding |= PE_SIGNED;
+ else
+ dpm.encoding &= ~PE_SIGNED;
+
+
+ /*
+ * Try stereo or mono, whichever the user wants. If it fails, try the
+ * other.
+ */
+
+ i = tmp = (dpm.encoding & PE_MONO) ? 0 : 1;
+ if ((ioctl(fd, SNDCTL_DSP_STEREO, &tmp) < 0) || tmp != i) {
+ i = tmp = (dpm.encoding & PE_MONO) ? 1 : 0;
+
+ if ((ioctl(fd, SNDCTL_DSP_STEREO, &tmp) < 0) || tmp != i) {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s doesn't support mono or stereo samples",
+ dpm.name);
+ close(fd);
+ return -1;
+ }
+ if (tmp == 0)
+ dpm.encoding |= PE_MONO;
+ else
+ dpm.encoding &= ~PE_MONO;
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Sound adjusted to %sphonic",
+ (tmp == 0) ? "mono" : "stereo");
+ warnings = 1;
+ }
+ /* Set the sample rate */
+
+ tmp = dpm.rate;
+ if (ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0) {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s doesn't support a %d Hz sample rate",
+ dpm.name, dpm.rate);
+ close(fd);
+ return -1;
+ }
+ if (tmp != dpm.rate) {
+ dpm.rate = tmp;
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
+ "Output rate adjusted to %d Hz", dpm.rate);
+ warnings = 1;
+ }
+ /* Older VoxWare drivers don't have buffer fragment capabilities */
+#ifdef SNDCTL_DSP_SETFRAGMENT
+ /* Set buffer fragments (in extra_param[0]) */
+
+ tmp = 2+ AUDIO_BUFFER_BITS ;
+ if (!(dpm.encoding & PE_MONO))
+ tmp++;
+ if (dpm.encoding & PE_16BIT)
+ tmp++;
+ tmp |= (dpm.extra_param[0] << 16);
+ i = tmp;
+ if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &tmp) < 0) {
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "%s doesn't support %d-byte buffer fragments", dpm.name, (1 << i));
+ /*
+ * It should still work in some fashion. We should use a secondary
+ * buffer anyway -- 64k isn't enough.
+ */
+ warnings = 1;
+ }
+#else
+ if (dpm.extra_param[0]) {
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "%s doesn't support buffer fragments", dpm.name);
+ warnings = 1;
+ }
+#endif
+
+ dpm.fd = fd;
+
+ return warnings;
+}
+
+static void
+output_data(int32 * buf, int32 count)
+{
+ char *p;
+ int res, l;
+
+ if (!(dpm.encoding & PE_MONO))
+ count *= 2; /* Stereo samples */
+
+ if (dpm.encoding & PE_16BIT) {
+ /* Convert data to signed 16-bit PCM */
+ s32tos16(buf, count);
+ res = count*2;
+ } else {
+ /* Convert to 8-bit unsigned and write out. */
+ s32tou8(buf, count);
+ res = count ;
+ }
+ for (p = buf ; res > 0 ; res -= l ) {
+ l = write(dpm.fd, p, res);
+ if (l < 0) return ;
+ p += l ;
+ }
+}
+
+static void
+close_output(void)
+{
+ close(dpm.fd);
+}
+
+static void
+flush_output(void)
+{
+ ioctl(dpm.fd, SNDCTL_DSP_SYNC);
+}
+
+static void
+purge_output(void)
+{
+ ioctl(dpm.fd, SNDCTL_DSP_RESET);
+}
diff --git a/sys/i386/isa/snd/misc/mmap_test.c b/sys/i386/isa/snd/misc/mmap_test.c
new file mode 100644
index 0000000..f9e8b3e
--- /dev/null
+++ b/sys/i386/isa/snd/misc/mmap_test.c
@@ -0,0 +1,278 @@
+/*
+ * This is a simple program which demonstrates use of mmapped DMA buffer
+ * of the sound driver directly from application program.
+ *
+ * This sample program works (currently) only with Linux, FreeBSD and BSD/OS
+ * (FreeBSD and BSD/OS require OSS version 3.8-beta16 or later.
+ *
+ * Note! Don't use mmapped DMA buffers (direct audio) unless you have
+ * very good reasons to do it. Programs using this feature will not
+ * work with all soundcards. GUS (GF1) is one of them (GUS MAX works).
+ *
+ * This program requires version 3.5-beta7 or later of OSS
+ * (3.8-beta16 or later in FreeBSD and BSD/OS).
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/soundcard.h>
+#include <sys/time.h>
+
+main()
+{
+ int fd, sz, fsz, i, tmp, n, l, have_data=0, nfrag;
+ int caps;
+
+ int sd, sl=0, sp;
+
+ unsigned char data[500000], *dp = data;
+
+ struct buffmem_desc imemd, omemd;
+ caddr_t buf;
+ struct timeval tim;
+
+ unsigned char *op;
+
+ struct audio_buf_info info;
+
+ int frag = 0xffff000c; /* Max # fragments of 2^13=8k bytes */
+
+ fd_set writeset;
+
+ close(0);
+ if ((fd=open("/dev/dsp", O_RDWR, 0))==-1)
+ {
+ perror("/dev/dsp");
+ exit(-1);
+ }
+/*
+ * Then setup sampling parameters. Just sampling rate in this case.
+ */
+
+ tmp = 48000;
+ ioctl(fd, SNDCTL_DSP_SPEED, &tmp);
+ printf("Speed set to %d\n", tmp);
+
+/*
+ * Load some test data.
+ */
+
+ sl = sp = 0;
+ if ((sd=open("smpl", O_RDONLY, 0))!=-1)
+ {
+ sl = read(sd, data, sizeof(data));
+ printf("%d bytes read from file.\n", sl);
+ close(sd);
+ }
+ else perror("smpl");
+
+ if (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps)==-1)
+ {
+ perror("/dev/dsp");
+ fprintf(stderr, "Sorry but your sound driver is too old\n");
+ exit(-1);
+ }
+
+/*
+ * Check that the device has capability to do this. Currently just
+ * CS4231 based cards will work.
+ *
+ * The application should also check for DSP_CAP_MMAP bit but this
+ * version of driver doesn't have it yet.
+ */
+/* ioctl(fd, SNDCTL_DSP_SETSYNCRO, 0); */
+
+/*
+ * You need version 3.5-beta7 or later of the sound driver before next
+ * two lines compile. There is no point to modify this program to
+ * compile with older driver versions since they don't have working
+ * mmap() support.
+ */
+ if (!(caps & DSP_CAP_TRIGGER) ||
+ !(caps & DSP_CAP_MMAP))
+ {
+ fprintf(stderr, "Sorry but your soundcard can't do this\n");
+ exit(-1);
+ }
+
+/*
+ * Select the fragment size. This is propably important only when
+ * the program uses select(). Fragment size defines how often
+ * select call returns.
+ */
+
+ ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
+
+/*
+ * Compute total size of the buffer. It's important to use this value
+ * in mmap() call.
+ */
+
+ if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info)==-1)
+ {
+ perror("GETOSPACE");
+ exit(-1);
+ }
+
+ sz = info.fragstotal * info.fragsize;
+ fsz = info.fragsize;
+
+/*
+ * Call mmap().
+ *
+ * IMPORTANT NOTE!!!!!!!!!!!
+ *
+ * Full duplex audio devices have separate input and output buffers.
+ * It is not possible to map both of them at the same mmap() call. The buffer
+ * is selected based on the prot argument in the following way:
+ *
+ * - PROT_READ (alone) selects the input buffer.
+ * - PROT_WRITE (alone) selects the output buffer.
+ * - PROT_WRITE|PROT_READ together select the output buffer. This combination
+ * is required in BSD to make the buffer accessible. With just PROT_WRITE
+ * every attempt to access the returned buffer will result in segmentation/bus
+ * error. PROT_READ|PROT_WRITE is also permitted in Linux with OSS version
+ * 3.8-beta16 and later (earlier versions don't accept it).
+ *
+ * Non duplex devices have just one buffer. When an application wants to do both
+ * input and output it's recommended that the device is closed and re-opened when
+ * switching between modes. PROT_READ|PROT_WRITE can be used to open the buffer
+ * for both input and output (with OSS 3.8-beta16 and later) but the result may be
+ * unpredictable.
+ */
+
+ if ((buf=mmap(NULL, sz, PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0))==(caddr_t)-1)
+ {
+ perror("mmap (write)");
+ exit(-1);
+ }
+ printf("mmap (out) returned %08x\n", buf);
+ op=buf;
+
+/*
+ * op contains now a pointer to the DMA buffer
+ */
+
+/*
+ * Then it's time to start the engine. The driver doesn't allow read() and/or
+ * write() when the buffer is mapped. So the only way to start operation is
+ * to togle device's enable bits. First set them off. Setting them on enables
+ * recording and/or playback.
+ */
+
+ tmp = 0;
+ ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);
+ printf("Trigger set to %08x\n", tmp);
+
+/*
+ * It might be usefull to write some data to the buffer before starting.
+ */
+
+ tmp = PCM_ENABLE_OUTPUT;
+ ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);
+ printf("Trigger set to %08x\n", tmp);
+
+/*
+ * The machine is up and running now. Use SNDCTL_DSP_GETOPTR to get the
+ * buffer status.
+ *
+ * NOTE! The driver empties each buffer fragmen after they have been
+ * played. This prevents looping sound if there are some performance problems
+ * in the application side. For similar reasons it recommended that the
+ * application uses some amout of play ahead. It can rewrite the unplayed
+ * data later if necessary.
+ */
+
+ nfrag = 0;
+ while (1)
+ {
+ struct count_info count;
+ int p, l, extra;
+
+ FD_ZERO(&writeset);
+ FD_SET(fd, &writeset);
+
+ tim.tv_sec = 10;
+ tim.tv_usec= 0;
+
+ select(fd+1, &writeset, &writeset, NULL, NULL);
+/*
+ * SNDCTL_DSP_GETOPTR (and GETIPTR as well) return three items. The
+ * bytes field returns number of bytes played since start. It can be used
+ * as a real time clock.
+ *
+ * The blocks field returns number of fragment transitions (interrupts) since
+ * previous GETOPTR call. It can be used as a method to detect underrun
+ * situations.
+ *
+ * The ptr field is the DMA pointer inside the buffer area (in bytes from
+ * the beginning of total buffer area).
+ */
+
+ if (ioctl(fd, SNDCTL_DSP_GETOPTR, &count)==-1)
+ {
+ perror("GETOPTR");
+ exit(-1);
+ }
+
+ nfrag += count.blocks;
+
+#ifdef VERBOSE
+
+ printf("\rTotal: %09d, Fragment: %03d, Ptr: %06d",
+ count.bytes, nfrag, count.ptr);
+ fflush(stdout);
+#endif
+
+/*
+ * Caution! This version doesn't check for bounds of the DMA
+ * memory area. It's possible that the returned pointer value is not aligned
+ * to fragment boundaries. It may be several samples behind the boundary
+ * in case there was extra delay between the actual hardware interrupt and
+ * the time when DSP_GETOPTR was called.
+ *
+ * Don't just call memcpy() with length set to 'fragment_size' without
+ * first checking that the transfer really fits to the buffer area.
+ * A mistake of just one byte causes seg fault. It may be easiest just
+ * to align the returned pointer value to fragment boundary before using it.
+ *
+ * It would be very good idea to write few extra samples to next fragment
+ * too. Otherwise several (uninitialized) samples from next fragment
+ * will get played before your program gets chance to initialize them.
+ * Take in count the fact thaat there are other processes batling about
+ * the same CPU. This effect is likely to be very annoying if fragment
+ * size is decreased too much.
+ */
+
+/*
+ * Just a minor clarification to the above. The following line alings
+ * the pointer to fragment boundaries. Note! Don't trust that fragment
+ * size is always a power of 2. It may not be so in future.
+ */
+ count.ptr = (count.ptr/fsz)*fsz;
+
+#ifdef VERBOSE
+ printf(" memcpy(%6d, %4d)", (dp-data), fsz);
+ fflush(stdout);
+#endif
+
+/*
+ * Set few bytes in the beginning of next fragment too.
+ */
+ if ((count.ptr+fsz+16) < sz) /* Last fragment? */
+ extra = 16;
+ else
+ extra = 0;
+
+ memcpy(op+count.ptr, dp, fsz+extra);
+
+ dp += fsz;
+ if (dp > (data+sl-fsz))
+ dp = data;
+ }
+
+ exit(0);
+}
diff --git a/sys/i386/isa/snd/misc/pcmio.c b/sys/i386/isa/snd/misc/pcmio.c
new file mode 100644
index 0000000..5bab10e
--- /dev/null
+++ b/sys/i386/isa/snd/misc/pcmio.c
@@ -0,0 +1,236 @@
+/*
+ * pcmio.c -- a simple utility for controlling audio I/O
+ * (rate, channels, resolution...)
+ *
+ * (C) Luigi Rizzo 1998
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <machine/soundcard.h>
+
+char * usage_string =
+"Usage: %s [-f device] [+parameters] [len:skip] file { file }\n"
+"where device is the device to be used (default /dev/audio)\n"
+"[len:skip] is the subsection of the file to be played\n"
+"with a '-' indicating the default\n"
+"and parameters is a comma-separated list containing one or more of\n"
+" N the sampling speed\n"
+" stereo|mono \n"
+" cd|u8|alaw|ulaw|s16 data format\n"
+" loop loop (play cyclically)\n"
+;
+
+#define BLKSZ 32768
+int format=AFMT_MU_LAW;
+int rate=8000 ;
+int stereo = 0 ;
+char dev[128];
+int audiodev;
+int play = 1 ; /* default */
+int loop = 0 ;
+int skip = 0;
+int size = -1 ;
+
+extern char *optarg;
+extern int optind, optopt, opterr, optreset;
+
+int
+usage(char *s)
+{
+ printf(usage_string, s);
+ exit(0);
+}
+
+int
+parse_len(char *s)
+{
+ int par = -1;
+ int mul = 1;
+ int p = strlen(s);
+
+ if (p == 0)
+ return -1 ;
+
+ if (*s != '-')
+ par = atoi(s);
+ else
+ return -1 ;
+ switch(s[p-1]) {
+ case 'k':
+ case 'K':
+ mul = 1024;
+ break;
+ case 'm':
+ case 'M':
+ mul = 1024*1024;
+ break;
+ case 's':
+ mul = rate * (stereo+1);
+ if (format == AFMT_S16_LE)
+ mul += mul;
+ break;
+ }
+ return par*mul;
+}
+
+void
+parse_fmt(char *fmt)
+{
+ char *s;
+ int v, last = 0 ;
+
+again:
+ while (*fmt && (*fmt == ' ' || *fmt == '\t')) fmt++;
+ s = fmt;
+ while (*s && ! (*s == ',' || *s == ':' || *s == ';') ) s++;
+ if (*s)
+ *s='\0';
+ else
+ last = 1 ;
+ v = atoi(fmt) ;
+ if (v > 0 && v < 1000000)
+ rate = v ;
+ else {
+ if (!strcmp(fmt, "ulaw")) format = AFMT_MU_LAW;
+ else if (!strcmp(fmt, "alaw")) format = AFMT_A_LAW;
+ else if (!strcmp(fmt, "u8")) format = AFMT_U8 ;
+ else if (!strcmp(fmt, "s16")) format = AFMT_S16_LE ;
+ else if (!strcmp(fmt, "mono")) stereo = 0;
+ else if (!strcmp(fmt, "stereo")) stereo = 1;
+ else if (!strcmp(fmt, "loop")) loop = 1;
+ else if (!strcmp(fmt, "rec")) play = 0;
+ else if (!strcmp(fmt, "play")) play = 1;
+ else if (!strcmp(fmt, "cd")) {
+ stereo = 1 ;
+ format = AFMT_S16_LE ;
+ rate = 44100;
+ }
+ }
+ if (last == 0) {
+ fmt = s+1;
+ goto again;
+ }
+}
+
+char buf[BLKSZ];
+
+int
+main(int argc, char *argv[])
+{
+ int i,c;
+ int ac = argc;
+ char *p;
+
+ strcpy(dev, "/dev/audio");
+
+ while ( (c= getopt(argc, argv, "f:") ) != EOF ) {
+ switch (c) {
+ case 'f' :
+ if (optarg[0] >='0' && optarg[0] <='9')
+ sprintf(dev, "/dev/audio%s", optarg);
+ else
+ strcpy(dev, optarg);
+ break;
+ }
+ }
+ if (*argv[optind] == '+') {
+ parse_fmt(argv[optind]+1);
+ optind++;
+ }
+ /*
+ * assume a string with a "," and no "/" as a command
+ */
+ if (strstr(argv[optind],",") && !strstr(argv[optind],"/") ) {
+ parse_fmt(argv[optind]);
+ optind++;
+ }
+ /*
+ * assume a string with a ":" and no "/" as a time limit
+ */
+ if ( (p = strstr(argv[optind] , ":")) && !strstr(argv[optind],"/") ) {
+ *p = '\0';
+ size = parse_len(argv[optind]);
+ skip = parse_len(p+1);
+ optind++;
+ }
+ printf("Using device %s, speed %d, mode 0x%08x, %s\n",
+ dev, rate, format, stereo ? "stereo":"mono");
+ printf("using files: ");
+ for (i=optind; i< argc ; i++)
+ printf("[%d] %s, ",i, argv[i]);
+ printf("\n");
+
+ audiodev = open(dev, play ? 1 : 0);
+ if (audiodev < 0) {
+ printf("failed to open %d\n", dev);
+ exit(2);
+ }
+ ioctl(audiodev, SNDCTL_DSP_SETFMT, &format);
+ ioctl(audiodev, SNDCTL_DSP_STEREO, &stereo);
+ ioctl(audiodev, SNDCTL_DSP_SPEED, &rate);
+ printf("-- format %d,%s,0x%08x, len %d skip %d\n",
+ rate, stereo? "stereo":"mono",format, size, skip);
+ if (play) {
+ off_t ofs;
+ int limit;
+again:
+ for (i=optind; i< argc ; i++) {
+ int l = -2;
+ int f = open(argv[i], O_RDONLY);
+ int sz ;
+
+ printf("opened %s returns %d\n", argv[i], f);
+ if (f < 0)
+ continue;
+ limit = size;
+ if (skip > 0) {
+ ofs = skip;
+ lseek(f, ofs, 0 /* begin */ );
+ }
+ sz = BLKSZ;
+ if (limit > 0 && limit < sz)
+ sz = limit ;
+ while ( (l = read(f, buf, sz) ) > 0 ) {
+ write(audiodev, buf, l);
+ if (limit > 0) {
+ limit -= l ;
+ if (limit > 0 && limit < sz)
+ sz = limit ;
+ if (limit <= 0 )
+ break;
+ if (limit < sz)
+ sz = limit ;
+ }
+ }
+ close(f);
+ }
+ if (loop)
+ goto again;
+ } else { /* record */
+ int l = -2;
+ int f ;
+ if (!strcmp(argv[optind], "-") )
+ f = 1;
+ else
+ f = open(argv[optind], O_WRONLY | O_CREAT | O_TRUNC, 0664);
+ fprintf(stderr,"open %s returns %d\n", argv[optind], f);
+
+ while ( size > 0 && (l = read(audiodev, buf, BLKSZ) ) > 0 ) {
+ if (l <= skip) {
+ skip -= l ; /* at most skip = 0 */
+ continue;
+ } else { /* l > skip */
+ l -= skip ;
+ if (l > size)
+ l = size ;
+ write(f, buf+skip, l);
+ skip = 0 ;
+ size -= l ;
+ }
+ }
+ close(f);
+ }
+}
diff --git a/sys/i386/isa/snd/misc/soundbyte.c b/sys/i386/isa/snd/misc/soundbyte.c
new file mode 100644
index 0000000..2e8d8cc
--- /dev/null
+++ b/sys/i386/isa/snd/misc/soundbyte.c
@@ -0,0 +1,275 @@
+/*
+ * Sound interface for Speak Freely for Unix
+ *
+ * Designed and implemented in July of 1990 by John Walker
+ *
+ * FreeBSD / voxware version
+ */
+
+#define BUFL 8000
+
+#include "speakfree.h"
+
+#include <sys/dir.h>
+#include <sys/file.h>
+
+/* #include <math.h> */
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#ifdef LINUX
+#include <linux/soundcard.h>
+#else
+#include <machine/soundcard.h>
+#endif
+#define AUDIO_MIN_GAIN 0
+#define AUDIO_MAX_GAIN 255
+static int abuf_size;
+
+#define SoundFile "/dev/audio"
+#define AUDIO_CTLDEV "/dev/mixer"
+
+#define MAX_GAIN 100
+
+struct sound_buf {
+ struct sound_buf *snext; /* Next sound buffer */
+ int sblen; /* Length of this sound buffer */
+ unsigned char sbtext[2]; /* Actual sampled sound */
+};
+
+/* Local variables */
+
+static int audiof = -1; /* Audio device file descriptor */
+static int Audio_fd; /* Audio control port */
+struct sound_buf *sbchain = NULL, /* Sound buffer chain links */
+ *sbtail = NULL;
+static int sbtotal = 0; /* Total sample bytes in memory */
+static int playing = FALSE;/* Replay in progress ? */
+/* static int playqsize; *//* Output queue size */
+static int playlen = 0; /* Length left to play */
+static unsigned char *playbuf = NULL; /* Current play pointer */
+static int squelch = 0; /* Squelch value */
+
+/* Convert local gain into device parameters */
+
+static unsigned
+scale_gain(unsigned g)
+{
+ return (AUDIO_MIN_GAIN + (unsigned)
+ ((int) ((((double) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) *
+ ((double) g / (double) MAX_GAIN)) + 0.5)));
+}
+
+#ifdef HALF_DUPLEX
+static int oldvol = -1;
+#endif
+
+/*
+ * SOUNDINIT -- Open the sound peripheral and initialise for access. Return
+ * TRUE if successful, FALSE otherwise.
+ */
+
+int
+soundinit(int iomode)
+{
+ int attempts = 3;
+
+ assert(audiof == -1);
+ while (attempts-- > 0) {
+ if ((audiof = open(SoundFile, iomode)) >= 0) {
+
+ if ((Audio_fd = open(AUDIO_CTLDEV, O_RDWR)) < 0) {
+ perror(AUDIO_CTLDEV);
+ return FALSE;
+ }
+ /* fcntl(audiof, F_SETFL, O_NDELAY); */
+#ifndef AUDIO_BLOCKING
+ if (ioctl(audiof, SNDCTL_DSP_NONBLOCK, NULL) < 0) {
+ perror("SNDCTL_DSP_NONBLOCK");
+ return FALSE;
+ }
+ if (ioctl(audiof, SNDCTL_DSP_GETBLKSIZE, &abuf_size) < 0) {
+ perror("SNDCTL_DSP_GETBLKSIZE");
+ return FALSE;
+ }
+#endif
+#ifdef HALF_DUPLEX
+ if (iomode == O_RDONLY) {
+ if (oldvol == -1)
+ oldvol = soundgetvol();
+ soundplayvol(0);
+ } else if (iomode == O_WRONLY && oldvol != -1 ) {
+ if (soundgetvol() == 0)
+ soundplayvol(oldvol);
+ oldvol = -1;
+ }
+#endif
+ return TRUE;
+ }
+ if (errno != EINTR)
+ break;
+ fprintf(stderr, "Audio open: retrying EINTR attempt %d\n", attempts);
+ }
+ return FALSE;
+}
+
+/* SOUNDTERM -- Close the sound device. chan=1 for play, 2:capture */
+
+void
+soundterm(int chan)
+{
+ if (audiof >= 0) {
+ int arg;
+#ifdef AIOSTOP /* FreeBSD */
+ if (chan == 2) {
+ arg = AIOSYNC_CAPTURE;
+ ioctl(audiof, AIOSTOP, &arg);
+ }
+#endif
+#ifdef SNDCTL_DSP_SYNC
+ if (chan == 1)
+ ioctl(audiof, SNDCTL_DSP_SYNC);
+#endif
+#ifdef HALF_DUPLEX
+ if (oldvol != -1) {
+ if (soundgetvol() == 0)
+ soundplayvol(oldvol);
+ oldvol = -1;
+ }
+#endif
+ if (close(audiof) < 0)
+ perror("closing audio device");
+ if (close(Audio_fd) < 0)
+ perror("closing audio control device");
+ audiof = -1;
+ }
+}
+
+/* SOUNDPLAY -- Begin playing a sound. */
+
+void
+soundplay(int len, unsigned char *buf)
+{
+ int ios;
+
+ assert(audiof != -1);
+ while (TRUE) {
+ ios = write(audiof, buf, len);
+ if (ios == -1)
+ sf_usleep(100000);
+ else {
+ if (ios < len) {
+ buf += ios;
+ len -= ios;
+ } else
+ break;
+ }
+ }
+}
+
+/* SOUNDPLAYVOL -- Set playback volume from 0 (silence) to 100 (full on). */
+
+void
+soundplayvol(int value)
+{
+ int arg;
+
+ arg = (value << 8) | value;
+
+ if (ioctl(Audio_fd, SOUND_MIXER_WRITE_PCM, &arg) < 0)
+ perror("SOUND_MIXER_WRITE_PCM");
+}
+
+#ifdef HALF_DUPLEX
+
+/* SOUNDGETVOL -- Get current playback volume. */
+
+int
+soundgetvol()
+{
+ int arg, v1, v2;
+
+ if (ioctl(Audio_fd, SOUND_MIXER_READ_PCM, &arg) < 0) {
+ perror("SOUND_MIXER_READ_PCM");
+ return -1;
+ }
+ v1 = arg & 0xFF;
+ v2 = (arg >> 8) & 0xFF;
+ return (v1 > v2) ? v1 : v2;
+}
+#endif
+
+/* SOUNDRECGAIN -- Set recording gain from 0 (minimum) to 100 (maximum). */
+
+void
+soundrecgain(int value)
+{
+ int arg;
+
+ arg = (value << 8) | value;
+
+ if (ioctl(Audio_fd, SOUND_MIXER_WRITE_RECLEV, &arg) < 0)
+ perror("SOUND_MIXER_WRITE_RECLEV");
+}
+
+/*
+ * SOUNDDEST -- Set destination for generated sound. If "where" is 0,
+ * sound goes to the built-in speaker; if 1, to the audio output jack.
+ */
+
+void
+sounddest(int where)
+{
+}
+
+/* SOUNDGRAB -- Return audio information in the record queue. */
+
+int
+soundgrab(char *buf, int len)
+{
+ long read_size;
+ int c;
+
+ read_size = len;
+#ifndef AUDIO_BLOCKING
+ if (read_size > abuf_size) {
+ read_size = abuf_size;
+ }
+#endif
+ while (TRUE) {
+ c = read(audiof, buf, read_size);
+ if (c < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno == EAGAIN) {
+ c = 0;
+ }
+ }
+ break;
+ }
+ if (c < 0) {
+ perror("soundgrab");
+ }
+ return c;
+}
+
+/* SOUNDFLUSH -- Flush any queued sound. */
+
+void
+soundflush()
+{
+ char sb[BUFL];
+ int c;
+
+#ifndef AUDIO_BLOCKING
+ while (TRUE) {
+ c = read(audiof, sb, BUFL < abuf_size ? BUFL : abuf_size);
+ if (c < 0 && errno == EAGAIN)
+ c = 0;
+ if (c < 0)
+ perror("soundflush");
+ if (c <= 0)
+ break;
+ }
+#endif
+}
diff --git a/sys/i386/isa/snd/misc/test.c b/sys/i386/isa/snd/misc/test.c
new file mode 100644
index 0000000..2725dcc
--- /dev/null
+++ b/sys/i386/isa/snd/misc/test.c
@@ -0,0 +1,84 @@
+/*
+ * test.c -- a simple utility for testing audio I/O
+ *
+ * (C) Luigi Rizzo 1997
+ *
+ * This code mmaps the io descriptor, then every second dumps the
+ * relevant data structures.
+ *
+ * call it as "test unit" where unit is the unit number you want
+ * to see displayed.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/select.h>
+
+caddr_t r, w, d;
+
+#include </sys/i386/isa/snd/sound.h>
+
+void
+print_d(u_long *p, int unit)
+{
+ snddev_info *d;
+ int i;
+ for (i=0; i<2000; i++) {
+ d = (snddev_info *)&(p[i]);
+ if ( d->magic == MAGIC(unit) )
+ break;
+ }
+ if (i == 2000) {
+ printf("desc not found\n");
+ return;
+ }
+ printf("device type %d name %s\n", d->type, d->name);
+ for (i=0;;i++) {
+ if (i%20 == 0)
+ printf("flags... fmt speed .bsz. c in-rl:in-dl:in-fl.ints "
+ " c ou-fl:ou_dl:ou-rl.ints |\n");
+ printf("%08x %3x %5d %5d %d %5d %5d %5d %4d %d %5d %5d %5d %4d |\n",
+ d->flags, d->play_fmt, d->play_speed, d->play_blocksize,
+ d->dbuf_in.chan,
+ d->dbuf_in.rl,
+ d->dbuf_in.dl,
+ d->dbuf_in.fl,
+ d->dbuf_in.int_count,
+
+ d->dbuf_out.chan,
+ d->dbuf_out.fl,
+ d->dbuf_out.dl,
+ d->dbuf_out.rl,
+ d->dbuf_out.int_count);
+ sleep(1);
+ }
+}
+
+main(int argc, char *argv[])
+{
+ int fd ;
+ int unit = 0;
+ char devn[64];
+
+ if (argc>1) unit=atoi(argv[1]);
+ sprintf(devn,"/dev/mixer%d", unit);
+ fd = open (devn, O_RDWR);
+ printf("open returns %d\n", fd);
+
+ w = mmap(NULL, 0x10000, PROT_READ, 0, fd, 0); /* play */
+ r = mmap(NULL, 0x10000, PROT_READ, 0, fd, 1<<24); /* rec */
+ d = mmap(NULL, 0x2000, PROT_READ, 0, fd, 2<<24); /* desc */
+
+ printf("mmap: w 0x%08lx, r 0x%08lx, d 0x%08lx\n", w, r, d);
+ if (d && (int)d != -1 ) {
+ print_d((u_long *)d, unit);
+ }
+ if (w && (int)w != -1) munmap(w, 0x10000);
+ if (r && (int)r != -1) munmap(r, 0x10000);
+ if (d && (int)d != -1) munmap(d, 0x2000);
+ return 0;
+}
diff --git a/sys/i386/isa/snd/sb_dsp.c b/sys/i386/isa/snd/sb_dsp.c
index 09440e1..3d75aea 100644
--- a/sys/i386/isa/snd/sb_dsp.c
+++ b/sys/i386/isa/snd/sb_dsp.c
@@ -92,7 +92,7 @@ snddev_info sb_op_desc = {
NULL /* use generic sndread */,
NULL /* use generic sndwrite */,
sb_dsp_ioctl,
- sndpoll,
+ sndselect,
sbintr,
sb_callback,
@@ -123,7 +123,7 @@ sb_probe(struct isa_device *dev)
bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) );
if (dev->id_iobase == -1) {
dev->id_iobase = 0x220;
- printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");
+ BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");)
if (snd_conflict(dev->id_iobase))
dev->id_iobase = 0x240;
}
@@ -196,6 +196,10 @@ 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)
+ d->rec_fmt = 0 ;
+ if ( (flags & FWRITE) == 0)
+ d->play_fmt = 0 ;
d->flags |= SND_F_BUSY ;
d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
@@ -250,6 +254,8 @@ sb_dsp_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
/*
* for the remaining functions, use the default handler.
+ * ENOSYS means that the default handler should take care
+ * of implementing the ioctl.
*/
return ENOSYS ;
@@ -292,7 +298,7 @@ again:
DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason));
if ( reason & 1 ) { /* possibly a write interrupt */
if ( d->dbuf_out.dl )
- dsp_wrintr(d);
+ dsp_wrintr(d);
else {
if (d->bd_flags & BD_F_SB16)
printf("WARNING: wrintr but write DMA inactive!\n");
@@ -300,7 +306,7 @@ again:
}
if ( reason & 2 ) {
if ( d->dbuf_in.dl )
- dsp_rdintr(d);
+ dsp_rdintr(d);
else {
if (d->bd_flags & BD_F_SB16)
printf("WARNING: rdintr but read DMA inactive!\n");
@@ -340,16 +346,11 @@ sb_callback(snddev_info *d, int reason)
case SND_CB_INIT : /* called with int enabled and no pending io */
dsp_speed(d);
snd_set_blocksize(d);
- if (d->play_fmt & AFMT_MU_LAW)
+ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) )
d->flags |= SND_F_XLAT8 ;
else
d->flags &= ~SND_F_XLAT8 ;
- reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
- reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
- return 1;
- break;
- case SND_CB_START : /* called with int disabled */
if (d->bd_flags & BD_F_SB16) {
u_char c, c1 ;
@@ -363,42 +364,40 @@ sb_callback(snddev_info *d, int reason)
*/
int swap = 1 ; /* default... */
- if (rd) {
- /* need not to swap if channel is already correct */
- if ( d->rec_fmt == AFMT_S16_LE && b->chan > 4 )
+ if (d->play_fmt == 0) {
+ /* do whatever the read channel wants */
+ if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 )
swap = 0;
- if ( d->rec_fmt != AFMT_S16_LE && b->chan < 4 )
+ if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 )
swap = 0;
- if (swap && (d->flags & SND_F_WRITING || d->dbuf_out.dl)) {
- /* cannot swap if writing is already active */
- DDB(printf("sorry, DMA channel unavailable\n"));
- swap = 0;
- break; /* XXX should return an error */
- }
} else {
- /* need not to swap if channel is already correct */
- if ( d->play_fmt == AFMT_S16_LE && b->chan > 4 )
+ /* privilege the write channel */
+ if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 )
swap = 0;
- if ( d->play_fmt != AFMT_S16_LE && b->chan < 4 )
+ if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 )
swap = 0;
- if (swap && ( d->flags & SND_F_READING || d->dbuf_in.dl)) {
- /* cannot swap if reading is already active */
- DDB(printf("sorry, DMA channel unavailable\n"));
- swap = 0;
- break ; /* XXX should return an error */
+ if ( d->rec_fmt ) {
+ /* check for possible config errors. */
+ if (d->rec_fmt == d->play_fmt) {
+ DDB(printf("sorry, read DMA channel unavailable\n"));
+ }
}
}
-
+ DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n",
+ d->play_fmt, d->rec_fmt, swap);)
if (swap) {
int c = d->dbuf_in.chan ;
d->dbuf_in.chan = d->dbuf_out.chan;
d->dbuf_out.chan = c ;
- reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
- reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
- DEB(printf("START dma chan: play %d, rec %d\n",
- d->dbuf_out.chan, d->dbuf_in.chan));
+ }
}
+ reset_dbuf(& (d->dbuf_in), SND_CHAN_RD );
+ reset_dbuf(& (d->dbuf_out), SND_CHAN_WR );
+ break ;
+ case SND_CB_START : /* called with int disabled */
+ if (d->bd_flags & BD_F_SB16) {
+ u_char c, c1 ;
/*
* 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
@@ -408,7 +407,7 @@ sb_callback(snddev_info *d, int reason)
if ( b->chan > 4 ) {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ;
c1 = DSP_F16_SIGNED ;
- l /= 2 ;
+ l /= 2 ;
} else {
c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA8 ;
c1 = 0 ;
@@ -421,28 +420,25 @@ sb_callback(snddev_info *d, int reason)
sb_cmd3(d->io_base, c1 , l - 1) ;
} else if (d->bd_flags & BD_F_ESS) {
/* XXX this code is still incomplete */
- sb_cmd2(d->io_base, 0xb8, rd ? 0x0e : 0x04 ); /* auto dma */
- sb_cmd2(d->io_base, 0xa8, 2 /* chans */ );
- sb_cmd2(d->io_base, 0xb9, 2); /* demand mode */
- /*
- input: 0xb8 -> 0x0e ;
- 0xa8 -> channels
- 0xb9 -> 2
- mono,U8: 51, d0
- mono,S16 71, f4
- st, U8 51, 98
- st, S16 71, bc
- */
- } else { /* SBPro */
+ } else { /* SBPro -- stereo not supported */
u_char c ;
if (!rd)
sb_cmd(d->io_base, DSP_CMD_SPKON);
- /* code for the SB2 and SB3 */
+ /* code for the SB2 and SB3, only MONO */
if (d->bd_flags & BD_F_HISPEED)
- c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ;
+ c = (rd) ? 0x98 : 0x90 ;
else
- c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ;
- sb_cmd3(d->io_base, c , l - 1) ;
+ c = (rd) ? 0x2c : 0x1c ;
+ /*
+ * some ESS extensions -- they can do 16 bits
+ */
+ if ( (rd && d->rec_fmt == AFMT_S16_LE) ||
+ (!rd && d->play_fmt == AFMT_S16_LE) ) {
+ c |= 1;
+ l /= 2 ;
+ }
+ sb_cmd3(d->io_base, 0x48 , l - 1) ;
+ sb_cmd(d->io_base, c ) ;
}
break;
@@ -451,7 +447,7 @@ sb_callback(snddev_info *d, int reason)
{
int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */
if ( d->bd_flags & BD_F_SB16 && b->chan > 4 )
- cmd = DSP_CMD_DMAPAUSE_16 ;
+ cmd = DSP_CMD_DMAPAUSE_16 ;
if (d->bd_flags & BD_F_HISPEED) {
sb_reset_dsp(d->io_base);
d->flags |= SND_F_INIT ;
@@ -485,9 +481,9 @@ sb_reset_dsp(int io_base)
{
int loopc;
- outb(DSP_RESET, 1);
+ outb(io_base + SBDSP_RST, 1);
DELAY(100);
- outb(DSP_RESET, 0);
+ outb(io_base + SBDSP_RST, 0);
for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++)
DELAY(30);
@@ -568,7 +564,9 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
d->name, dev->id_unit, d->irq);
else
sb_setmixer(io_base, IRQ_NR, x);
-
+ if (d->dbuf_out.chan == d->dbuf_in.chan) {
+ printf("WARNING: sb: misconfigured secondary DMA channel\n");
+ }
sb_setmixer(io_base, DMA_NR, (1 << d->dbuf_out.chan) | (1 << d->dbuf_in.chan));
break ;
@@ -599,41 +597,22 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev)
DELAY(20);
}
- if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
+ if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) {
+ /* the ESS488 can be treated as an SBPRO */
printf("ESS488 (rev %d)\n", ess_minor & 0x0f);
- else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
- if ( (ess_minor & 0xf) >= 8 )
- printf("ESS1688 (rev %d)\n", ess_minor & 0x0f);
- else
- printf("ESS688 (rev %d)\n", ess_minor & 0x0f);
- } else
break ;
- d->bd_id = (ess_major << 8) | ess_minor ;
- d->bd_flags |= BD_F_ESS;
- /*
- * ESS-specific initialization, taken from OSSFree 3.8
- */
- {
- static u_char irqs[16] = {
- 0, 0, 0x50, 0, 0, 0x54, 0, 0x58,
- 0, 0x50, 0x5c, 0, 0, 0, 0, 0 };
- static u_char drqs[8] = {
- 0x51, 0x52, 0, 0x53, 0, 0, 0, 0 };
- x = irqs[d->irq & 0xf];
- if (x == 0)
- printf("ESS : invalid IRQ %d\n", d->irq);
- else {
- sb_cmd(io_base, 0xb1 ); /* set IRQ */
- sb_cmd(io_base, x );
-
- x = drqs[ d->dbuf_out.chan & 0x7 ];
- if (x == 0)
- printf("ESS : invalid DRQ %d\n", d->dbuf_out.chan);
- else {
- sb_cmd(io_base, 0xb2 ); /* set DRQ */
- sb_cmd(io_base, x );
- }
- }
+ } else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
+ int rev = ess_minor & 0xf ;
+ if ( rev >= 8 )
+ printf("ESS1868 (rev %d)\n", rev);
+ else
+ printf("ESS688 (rev %d)\n", rev);
+ d->audio_fmt |= AFMT_S16_LE; /* in fact it is U16_LE */
+ break ; /* XXX */
+ } else {
+ printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n",
+ ess_major, ess_minor);
+ break ;
}
}
@@ -679,8 +658,8 @@ sb_cmd(int io_base, u_char val)
int i;
for (i = 0; i < 1000 ; i++) {
- if ((inb(DSP_STATUS) & 0x80) == 0) {
- outb(DSP_COMMAND, val);
+ if ((inb(io_base + SBDSP_STATUS) & 0x80) == 0) {
+ outb(io_base + SBDSP_CMD, val);
return 1;
}
if (i > 10)
@@ -712,32 +691,23 @@ sb_cmd2(int io_base, u_char cmd, int val)
return 0;
}
+/*
+ * in the SB, there is a set of indirect "mixer" registers with
+ * address at offset 4, data at offset 5
+ */
void
sb_setmixer(int io_base, u_int port, u_int value)
{
u_long flags;
flags = spltty();
- outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- outb(MIXER_DATA, (u_char) (value & 0xff));
+ outb(io_base + 5, (u_char) (value & 0xff));
DELAY(10);
splx(flags);
}
-u_int
-sb_get_byte(int io_base)
-{
- int i;
-
- for (i = 1000; i; i--)
- if (inb(DSP_DATA_AVAIL) & 0x80)
- return inb(DSP_READ);
- else
- DELAY(20);
- return 0xffff;
-}
-
int
sb_getmixer(int io_base, u_int port)
{
@@ -745,15 +715,29 @@ sb_getmixer(int io_base, u_int port)
u_long flags;
flags = spltty();
- outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
- val = inb(MIXER_DATA);
+ val = inb(io_base + 5);
DELAY(10);
splx(flags);
return val;
}
+u_int
+sb_get_byte(int io_base)
+{
+ int i;
+
+ for (i = 1000; i; i--)
+ if (inb(DSP_DATA_AVAIL) & 0x80)
+ return inb(DSP_READ);
+ else
+ DELAY(20);
+ return 0xffff;
+}
+
+
/*
* various utility functions for the DSP
@@ -806,11 +790,12 @@ dsp_speed(snddev_info *d)
}
/*
- * only some models can do stereo, and only if not
+ * This is code for the SB3.x and lower.
+ * Only some models can do stereo, and only if not
* simultaneously using midi.
+ * At the moment we do not support either...
*/
- if ( (d->bd_id & 0xff00) < 0x300 || d->bd_flags & BD_F_MIDIBUSY)
- d->flags &= ~SND_F_STEREO;
+ d->flags &= ~SND_F_STEREO;
/*
* here enforce speed limitations.
@@ -830,26 +815,12 @@ dsp_speed(snddev_info *d)
RANGE(speed, 4000, max_speed);
- /*
- * Logitech SoundMan Games and Jazz16 cards can support 44.1kHz
- * stereo
- */
-#if !defined (SM_GAMES)
- /*
- * Max. stereo speed is 22050
- */
- if (d->flags & SND_F_STEREO && speed > 22050 && !(d->bd_flags & BD_F_JAZZ16))
- speed = 22050;
-#endif
-
- if (d->flags & SND_F_STEREO)
+ if (d->flags & SND_F_STEREO) /* really unused right now... */
speed *= 2;
/*
* Now the speed should be valid. Compute the value to be
* programmed into the board.
- *
- * XXX stereo init is still missing...
*/
if (speed > 22050) { /* High speed mode on 2.01/3.xx */
@@ -878,7 +849,7 @@ dsp_speed(snddev_info *d)
speed = (1000000 + tmp / 2) / tmp;
}
- if (d->flags & SND_F_STEREO)
+ if (d->flags & SND_F_STEREO) /* really unused right now... */
speed /= 2;
d->play_speed = d->rec_speed = speed;
@@ -1024,6 +995,79 @@ sb_mixer_set(snddev_info *d, int dev, int value)
*/
#if NPNP > 0
+static char *ess1868_probe(u_long csn, u_long vend_id);
+static void ess1868_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device ess1868 = {
+ "ESS1868",
+ ess1868_probe,
+ ess1868_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, ess1868);
+
+static char *
+ess1868_probe(u_long csn, u_long vend_id)
+{
+ /*
+ * pnp X 1 os enable drq0 3 irq0 12 port0 0x240
+ */
+ if (vend_id == 0x68187316) {
+ struct pnp_cinfo d ;
+ read_pnp_parms ( &d , 1 ) ;
+ if (d.enable == 0) {
+ printf("This is an ESS1868, but LDN 1 is disabled\n");
+ return NULL;
+ }
+ return "ESS1868" ;
+ }
+ return NULL ;
+}
+
+static void
+ess1868_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;
+
+#if 0
+ read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
+ 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 , 0 ); /* read config base */
+ tmp_d.conf_base = d.port[0];
+ write_pnp_parms ( &d , 0 );
+#endif
+
+ read_pnp_parms ( &d , 1 ) ;
+ dev->id_iobase = d.port[0];
+ d.port[1] = 0 ;
+ d.port[2] = 0 ;
+ write_pnp_parms ( &d , 1 );
+ enable_pnp_card();
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = 0 /* DV_F_DUAL_DMA | (d.drq[1] ) */;
+
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+ 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);
@@ -1155,6 +1199,7 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name,
read_pnp_parms ( &d , 0 ) ;
d.port[1] = 0 ; /* only the first address is used */
dev->id_iobase = d.port[0];
+ tmp_d.synth_base = d.port[2];
write_pnp_parms ( &d , 0 );
enable_pnp_card();
diff --git a/sys/i386/isa/snd/sbcard.h b/sys/i386/isa/snd/sbcard.h
index 1735c14..0bd4210 100644
--- a/sys/i386/isa/snd/sbcard.h
+++ b/sys/i386/isa/snd/sbcard.h
@@ -11,18 +11,21 @@ extern int sbc_major, sbc_minor ;
* sound blaster registers
*/
-#define DSP_RESET (io_base + 0x6)
+#define SBDSP_RST 0x6
#define DSP_READ (io_base + 0xA)
#define DSP_WRITE (io_base + 0xC)
-#define DSP_COMMAND (io_base + 0xC)
-#define DSP_STATUS (io_base + 0xC)
+#define SBDSP_CMD 0xC
+#define SBDSP_STATUS 0xC
#define DSP_DATA_AVAIL (io_base + 0xE)
#define DSP_DATA_AVL16 (io_base + 0xF)
+
+#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)
+#endif
/*
* DSP Commands. There are many, and in many cases they are used explicitly
diff --git a/sys/i386/isa/snd/sound.c b/sys/i386/isa/snd/sound.c
index 3f5007ca..ebb5374 100644
--- a/sys/i386/isa/snd/sound.c
+++ b/sys/i386/isa/snd/sound.c
@@ -50,13 +50,7 @@
*
*/
-#include "opt_devfs.h"
-
#include <i386/isa/snd/sound.h>
-#ifdef DEVFS
-#include <sys/devfsext.h>
-#endif /* DEVFS */
-
#if NPCM > 0 /* from "snd.h" */
@@ -76,7 +70,7 @@ static d_mmap_t sndmmap;
static struct cdevsw snd_cdevsw = {
sndopen, sndclose, sndread, sndwrite,
sndioctl, nxstop, nxreset, nxdevtotty,
- sndpoll, sndmmap, nxstrategy, "snd",
+ sndselect, sndmmap, nxstrategy, "snd",
NULL, -1,
};
@@ -187,7 +181,8 @@ pcmattach(struct isa_device * dev)
d->io_base = dev->id_iobase ;
d->irq = ffs(dev->id_irq) - 1 ;
d->dbuf_out.chan = dev->id_drq ;
- if (dev->id_flags != -1 && dev->id_flags & DV_F_DUAL_DMA) /* enable dma2 */
+ if (dev->id_flags != -1 && dev->id_flags & DV_F_DUAL_DMA &&
+ (dev->id_flags & DV_F_DRQ_MASK) != 4 ) /* enable dma2 */
d->dbuf_in.chan = dev->id_flags & DV_F_DRQ_MASK ;
else
d->dbuf_in.chan = d->dbuf_out.chan ;
@@ -220,23 +215,6 @@ pcmattach(struct isa_device * dev)
isadev = makedev(CDEV_MAJOR, 0);
cdevsw_add(&isadev, &snd_cdevsw, NULL);
-#ifdef DEVFS
- /*
- * XXX remember to store the returned tokens if you want to
- * be able to remove the device later
- */
- devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_DSP,
- DV_CHR, UID_ROOT, GID_WHEEL, 0600, "dsp%n", dev->id_unit);
- devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_DSP16,
- DV_CHR, UID_ROOT, GID_WHEEL, 0600, "dspW%n", dev->id_unit);
- devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_AUDIO,
- DV_CHR, UID_ROOT, GID_WHEEL, 0600, "audio%n", dev->id_unit);
- devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_CTL,
- DV_CHR, UID_ROOT, GID_WHEEL, 0600, "mixer%n", dev->id_unit);
- devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_STATUS,
- DV_CHR, UID_ROOT, GID_WHEEL, 0600, "status%n", dev->id_unit);
-#endif
-
/*
* should try and find a suitable value for id_id, otherwise
* the interrupt is not registered and dispatched properly.
@@ -253,36 +231,24 @@ pcmattach(struct isa_device * dev)
d->magic = MAGIC(dev->id_unit); /* debugging... */
/*
* and finally, call the device attach routine
+ * XXX I should probably use d->attach(dev)
*/
stat = snddev_last_probed->attach(dev);
+#if 0
+ /*
+ * XXX hooks for synt support. Try probe and attach...
+ */
+ if (d->synth_base && opl3_probe(dev) ) {
+ opl3_attach(dev);
+ }
+#endif
snddev_last_probed = NULL ;
return stat ;
}
-int
-midiattach(struct isa_device * dev)
-{
-#if 0
-#ifdef DEVFS
- devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_MIDIN,
- DV_CHR, UID_ROOT, GID_WHEEL, 0600, "status%n", dev->id_unit);
-#endif /* DEVFS */
-#endif
- return 0 ;
-}
-
-int
-synthattach(struct isa_device * dev)
-{
-#if 0
-#ifdef DEVFS
- devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_SYNTH,
- DV_CHR, UID_ROOT, GID_WHEEL, 0600, "status%n", dev->id_unit);
-#endif /* DEVFS */
-#endif
- return 0 ;
-}
+int midiattach(struct isa_device * dev) { return 0 ; }
+int synthattach(struct isa_device * dev) { return 0 ; }
struct isa_driver pcmdriver = { pcmprobe, pcmattach, "pcm" } ;
@@ -296,10 +262,12 @@ pcmintr(int unit)
pcm_info[unit].interrupts++;
if (pcm_info[unit].isr)
pcm_info[unit].isr(unit);
+#if 0 /* these do not exist at the moment. */
if (midi_info[unit].isr)
midi_info[unit].isr(unit);
if (synth_info[unit].isr)
synth_info[unit].isr(unit);
+#endif
}
static snddev_info *
@@ -342,8 +310,19 @@ get_snddev_info(dev_t dev, int *unit)
if (unit)
*unit = u ;
- if (u > NPCM_MAX)
+ if (u >= NPCM_MAX ||
+ ( pcm_info[u].io_base == 0 && (dev & 0x0f) != SND_DEV_STATUS)) {
+ int i;
+ for (i = 0 ; i < NPCM_MAX ; i++)
+ if (pcm_info[i].io_base)
+ break ;
+ if (i != NPCM_MAX)
+ printf("pcm%d: unit not configured, perhaps you want pcm%d ?\n",
+ u, i);
+ else
+ printf("no pcm units configured\b");
return NULL ;
+ }
switch(dev & 0x0f) {
case SND_DEV_CTL : /* /dev/mixer handled by pcm */
case SND_DEV_STATUS : /* /dev/sndstat handled by pcm */
@@ -351,16 +330,13 @@ get_snddev_info(dev_t dev, int *unit)
case SND_DEV_DSP :
case SND_DEV_DSP16 :
case SND_DEV_AUDIO :
+ case SND_DEV_SEQ : /* XXX goes here... */
d = & pcm_info[u] ;
break ;
- case SND_DEV_SEQ :
case SND_DEV_SEQ2 :
- d = & synth_info[u] ;
- break ;
case SND_DEV_MIDIN:
- d = & midi_info[u] ;
- break ;
default:
+ printf("unsupported subdevice %d\n", dev & 0xf);
return NULL ;
}
return d ;
@@ -386,13 +362,18 @@ sndopen(dev_t i_dev, int flags, int mode, struct proc * p)
DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
unit, dev & 0xf, flags, mode));
- if (d == NULL) {
- printf("open: unit %d dev %d not configured, perhaps you want unit %d ?\n",
- unit, dev & 0xf, unit + 1 );
+ if (d == NULL)
return (ENXIO) ;
- }
switch(dev & 0x0f) {
+ case SND_DEV_SEQ: /* sequencer. Hack... */
+#if 0 /* XXX hook for opl3 support */
+ if (d->synth_base)
+ return opl3_open(i_dev, flags, mode, p);
+ else
+#endif
+ return ENXIO ;
+
case SND_DEV_CTL : /* mixer ... */
return 0 ; /* always succeed */
@@ -423,17 +404,21 @@ sndclose(dev_t i_dev, int flags, int mode, struct proc * p)
DEB(printf("close snd%d subdev %d\n", unit, dev & 0xf));
- if (d == NULL) {
- printf("close: unit %d dev %d not configured\n", unit, dev & 0xf );
+ if (d == NULL)
return (ENXIO) ;
- }
- switch(dev & 0x0f) { /* only those for which close makes sense */
+
+ switch(dev & 0xf) { /* only those for which close makes sense */
+ case SND_DEV_SEQ:
+#if 0 /* XXX hook for opl3 support */
+ if (d->synth_base)
+ return opl3_close(i_dev, flags, mode, p);
+ else
+#endif
+ return ENXIO ;
+
case SND_DEV_AUDIO :
case SND_DEV_DSP :
case SND_DEV_DSP16 :
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- case SND_DEV_MIDIN:
if (d->close)
return d->close(i_dev, flags, mode, p);
}
@@ -452,10 +437,9 @@ sndread(dev_t i_dev, struct uio * buf, int flag)
d = get_snddev_info(dev, &unit);
DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
- if (d == NULL) {
- printf("read: unit %d not configured\n", unit );
+ if (d == NULL)
return ENXIO ;
- }
+
if ( (dev & 0x0f) == SND_DEV_STATUS ) {
int l, c;
u_char *p;
@@ -470,11 +454,9 @@ sndread(dev_t i_dev, struct uio * buf, int flag)
p = status_buf + d->status_ptr ;
d->status_ptr += l ;
splx(s);
- ret = uiomove(p, l, buf) ;
- if (ret)
- printf("pcm-stat: bad copyout\n");
- return ret ;
+ return uiomove(p, l, buf) ;
}
+
if (d->read) /* device-specific read */
return d->read(i_dev, buf, flag);
@@ -487,6 +469,10 @@ sndread(dev_t i_dev, struct uio * buf, int flag)
/* another reader is in, deny request */
splx(s);
DDB(printf("read denied, another reader is in\n"));
+ /*
+ * sleep for a while to avoid killing the machine.
+ */
+ tsleep( (void *)s, PZERO, "sndar", hz ) ;
return EBUSY ;
}
if ( ! FULL_DUPLEX(d) ) { /* half duplex */
@@ -494,6 +480,7 @@ sndread(dev_t i_dev, struct uio * buf, int flag)
/* another writer is in, deny request */
splx(s);
DDB(printf("read denied, half duplex and a writer is in\n"));
+ tsleep( (void *)s, PZERO, "sndaw", hz ) ;
return EBUSY ;
}
while ( d->dbuf_out.dl ) {
@@ -530,10 +517,9 @@ sndwrite(dev_t i_dev, struct uio * buf, int flag)
DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
- if (d == NULL) {
- printf("write: unit %d not configured\n", unit );
+ if (d == NULL)
return (ENXIO) ;
- }
+
switch( dev & 0x0f) { /* only writeable devices */
case SND_DEV_MIDIN: /* XXX is this writable ? */
case SND_DEV_SEQ :
@@ -558,13 +544,15 @@ sndwrite(dev_t i_dev, struct uio * buf, int flag)
/* another writer is in, deny request */
splx(s);
DDB(printf("write denied, another writer is in\n"));
+ tsleep( (void *)s, PZERO , "sndaw", hz ) ;
return EBUSY ;
}
if ( ! FULL_DUPLEX(d) ) { /* half duplex */
if ( d->flags & SND_F_READING ) {
- DDB(printf("write denied, half duplex and a reader is in\n"));
/* another reader is in, deny request */
splx(s);
+ DDB(printf("write denied, half duplex and a reader is in\n"));
+ tsleep( (void *)s, PZERO, "sndar", hz ) ;
return EBUSY ;
}
while ( d->dbuf_in.dl ) {
@@ -613,9 +601,17 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
dev = minor(i_dev);
d = get_snddev_info(dev, &unit);
- if (d == NULL) {
- printf("ioctl: unit %d not configured\n", unit );
+ if (d == NULL)
return (ENXIO) ;
+
+ if ( (dev & 0x0f) == SND_DEV_SEQ ) {
+ /* sequencer. Hack... */
+#if 0
+ if (d->synth_base)
+ return opl3_ioctl(i_dev, cmd, arg, mode, p) ;
+ else
+#endif
+ return ENXIO ;
}
if (d->ioctl)
ret = d->ioctl(dev, cmd, arg, mode, p);
@@ -745,7 +741,7 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
break ;
case AIOSYNC:
- printf("AIOSYNC chan 0x%03lx pos %ld unimplemented\n",
+ printf("AIOSYNC chan 0x%03lx pos %d unimplemented\n",
((snd_sync_parm *)arg)->chan,
((snd_sync_parm *)arg)->pos);
break;
@@ -812,10 +808,6 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
break ;
case SNDCTL_DSP_STEREO:
- if ( *(int *)arg == -1) {
- *(int *)arg = (d->flags & SND_F_STEREO) ? 1 : 0 ;
- break;
- }
if ( *(int *)arg == 0 )
d->flags &= ~SND_F_STEREO ; /* mono */
else if ( *(int *)arg == 1 )
@@ -856,7 +848,15 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
break ;
case SNDCTL_DSP_SETFMT: /* sets _one_ format */
- d->play_fmt = d->rec_fmt = *(int *)arg ;
+ /*
+ * when some card (SB16) is opened RDONLY or WRONLY,
+ * only one of the fields is set, the other becomes 0.
+ * This makes it possible to select DMA channels at runtime.
+ */
+ if (d->play_fmt)
+ d->play_fmt = *(int *)arg ;
+ if (d->rec_fmt)
+ d->rec_fmt = *(int *)arg ;
splx(s);
if (ask_init(d))
*(int *)arg = d->play_fmt ;
@@ -864,7 +864,7 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
case SNDCTL_DSP_SUBDIVIDE:
/* XXX watch out, this is RW! */
- printf("SNDCTL_DSP_SUBDIVIDE yet unimplemented\n");
+ DEB(printf("SNDCTL_DSP_SUBDIVIDE yet unimplemented\n");)
break;
case SNDCTL_DSP_SETFRAGMENT:
@@ -874,15 +874,23 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
int bytes, count;
bytes = *(int *)arg & 0xffff ;
count = ( *(int *)arg >> 16) & 0xffff ;
- if (bytes < 7)
- bytes = 7 ;
if (bytes > 15)
bytes = 15 ;
- d->play_blocksize =
- d->rec_blocksize = min ( 1<< bytes, d->dbuf_in.bufsize) ;
+ bytes = 1 << bytes ;
+ if (bytes <= 1) { /* means no blocks */
+ d->flags &= ~SND_F_HAS_SIZE ;
+ } else {
+ RANGE (bytes, 40, d->dbuf_out.bufsize /4);
+ d->play_blocksize =
+ d->rec_blocksize = bytes & ~3 ; /* align to multiple of 4 */
+ d->flags |= SND_F_HAS_SIZE ;
+ }
+ splx(s);
+ ask_init(d);
+#if 0
+ /* XXX todo: set the buffer size to the # of fragments */
count = d->dbuf_in.bufsize / d->play_blocksize ;
bytes = ffs(d->play_blocksize) - 1;
-#if 0
/*
* don't change arg, since it's fake anyways and some
* programs might fail if we do.
@@ -941,12 +949,12 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
if (b->dl)
dsp_wr_dmaupdate( b );
a->bytes = b->total;
- a->blocks = (b->total - b->prev_total +
- d->play_blocksize -1 ) / d->play_blocksize ;
+ a->blocks = (b->total - b->prev_total
+ /* +d->play_blocksize -1*/ ) / d->play_blocksize ;
a->ptr = b->rp ; /* XXX not sure... */
b->prev_total = b->total ;
}
- break ;
+ break;
case SNDCTL_DSP_GETCAPS :
*(int *) arg = 0x0 ; /* revision */
@@ -979,14 +987,6 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
*(int *)arg = d->mix_recsrc ;
break;
- case SOUND_MIXER_READ_CAPS :
- /*
- * XXX - This needs to be fixed when the sound driver actually
- * supports multiple inputs.
- */
- *(int *)arg = SOUND_CAP_EXCL_INPUT ;
- break;
-
default:
DEB(printf("default ioctl snd%d subdev %d fn 0x%08x fail\n",
unit, dev & 0xf, cmd));
@@ -998,10 +998,11 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
}
/*
- * function to poll what is currently available. Used to be select.
+ * we use the name 'select', but the new "poll" interface this is
+ * really sndpoll. Second arg is not "rw" but "events"
*/
int
-sndpoll(dev_t i_dev, int events, struct proc *p)
+sndselect(dev_t i_dev, int rw, struct proc * p)
{
int dev, unit, c = 1 /* default: success */ ;
snddev_info *d ;
@@ -1009,15 +1010,22 @@ sndpoll(dev_t i_dev, int events, struct proc *p)
dev = minor(i_dev);
d = get_snddev_info(dev, &unit);
- DEB(printf("sndpoll dev 0x%04x events 0x%08x\n",i_dev, events));
+ DEB(printf("sndselect dev 0x%04x rw 0x%08x\n",i_dev, rw));
if (d == NULL ) {
- printf("poll: unit %d not configured\n", unit );
- return ( (events & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP);
+#ifdef USE_POLL
+ return ( (rw & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP);
+#else
+ return (ENXIO) ;
+#endif
}
- if (d->poll == NULL)
- return ( (events & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP);
- else if (d->poll != sndpoll )
- return d->poll(i_dev, events, p);
+ if (d->select == NULL)
+#ifdef USE_POLL
+ return ( (rw & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP);
+#else
+ return 1 ; /* always success ? */
+#endif
+ else if (d->select != sndselect )
+ return d->select(i_dev, rw, p);
else {
/* handle it here with the generic code */
@@ -1031,7 +1039,11 @@ sndpoll(dev_t i_dev, int events, struct proc *p)
* In all other cases, select will return when 1 byte is ready.
*/
lim = 1;
- if (events & (POLLOUT | POLLWRNORM) ) {
+#ifdef USE_POLL
+ if (rw & (POLLOUT | POLLWRNORM) ) {
+#else
+ if (rw == FWRITE) {
+#endif
if ( d->flags & SND_F_HAS_SIZE )
lim = d->play_blocksize ;
/* XXX fix the test here for half duplex devices */
@@ -1042,12 +1054,19 @@ sndpoll(dev_t i_dev, int events, struct proc *p)
c = d->dbuf_out.fl ;
if (c < lim) /* no space available */
selrecord(p, & (d->wsel));
+#ifdef USE_POLL
else
- revents |= events & (POLLOUT | POLLWRNORM);
+ revents |= rw & (POLLOUT | POLLWRNORM);
+#endif
splx(flags);
}
+#ifdef USE_POLL
}
- if (events & (POLLIN | POLLRDNORM)) {
+ if (rw & (POLLIN | POLLRDNORM)) {
+#else
+ return c < lim ? 0 : 1 ;
+ } else if (rw == FREAD) {
+#endif
if ( d->flags & SND_F_HAS_SIZE )
lim = d->rec_blocksize ;
/* XXX fix the test here */
@@ -1060,15 +1079,24 @@ sndpoll(dev_t i_dev, int events, struct proc *p)
c = d->dbuf_in.rl ;
if (c < lim) /* no data available */
selrecord(p, & (d->rsel));
+#ifdef USE_POLL
else
- revents |= events & (POLLIN | POLLRDNORM);
+ revents |= rw & (POLLIN | POLLRDNORM);
+#endif
splx(flags);
}
- DEB(printf("sndpoll on read: %d >= %d flags 0x%08x\n",
+ DEB(printf("sndselect on read: %d >= %d flags 0x%08x\n",
c, lim, d->flags));
return c < lim ? 0 : 1 ;
+#ifdef USE_POLL
}
return revents;
+#else
+ } else {
+ DDB(printf("select on exceptions, unimplemented\n"));
+ return 1;
+ }
+#endif
}
return ENXIO ; /* notreached */
}
@@ -1163,8 +1191,6 @@ init_status(snddev_info *d)
/*
* Write the status information to the status_buf and update
* status_len. There is a limit of SNDSTAT_BUF_SIZE bytes for the data.
- * put_status handles this and returns 0 in case of failure. Since
- * it never oveflows the buffer, we do not care to check.
*/
int i;
@@ -1172,7 +1198,7 @@ init_status(snddev_info *d)
if (status_len != 0) /* only do init once */
return ;
sprintf(status_buf,
- "FreeBSD Audio Driver (971117) " __DATE__ " " __TIME__ "\n"
+ "FreeBSD Audio Driver (980123) " __DATE__ " " __TIME__ "\n"
"Installed devices:\n");
for (i = 0; i < NPCM_MAX; i++) {
@@ -1188,12 +1214,18 @@ init_status(snddev_info *d)
i, midi_info[i].name, midi_info[i].io_base,
midi_info[i].irq,
midi_info[i].dbuf_out.chan, midi_info[i].dbuf_in.chan);
- if (synth_info[i].open)
+ if (pcm_info[i].synth_base) {
+ char *s = "???";
+ switch (pcm_info[i].synth_type) {
+ case 2 : s = "OPL2"; break;
+ case 3 : s = "OPL3"; break;
+ case 4 : s = "OPL4"; break;
+ }
+
sprintf(status_buf + strlen(status_buf),
- "synth%d: <%s> at 0x%x irq %d dma %d:%d\n",
- i, synth_info[i].name, synth_info[i].io_base,
- synth_info[i].irq,
- synth_info[i].dbuf_out.chan, synth_info[i].dbuf_in.chan);
+ "sequencer%d: <%s> at 0x%x (not functional)\n",
+ i, s, pcm_info[i].synth_base);
+ }
}
status_len = strlen(status_buf) ;
}
@@ -1233,8 +1265,8 @@ snd_conflict(int io_base)
(io_base == pcm_info[i].mix_base ) ||
(io_base == pcm_info[i].midi_base) ||
(io_base == pcm_info[i].synth_base) ) {
- printf("device at 0x%x already attached as unit %d\n",
- io_base, i);
+ BVDDB(printf("device at 0x%x already attached as unit %d\n",
+ io_base, i);)
return 1 ;
}
}
diff --git a/sys/i386/isa/snd/sound.h b/sys/i386/isa/snd/sound.h
index 5ab9986..93dd406 100644
--- a/sys/i386/isa/snd/sound.h
+++ b/sys/i386/isa/snd/sound.h
@@ -1,3 +1,5 @@
+/* uncomment the next line for -current with select->poll changes */
+#define USE_POLL
/*
* sound.h
*
@@ -62,10 +64,13 @@
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/buf.h>
-#include <sys/poll.h>
#include <i386/isa/isa_device.h>
#include <machine/clock.h> /* for DELAY */
+#ifdef USE_POLL
+#include <sys/poll.h>
+#define d_select_t d_poll_t
+#endif
#else
struct isa_device { int dummy ; } ;
@@ -74,7 +79,7 @@ struct isa_device { int dummy ; } ;
#define d_read_t void
#define d_write_t void
#define d_ioctl_t void
-#define d_poll_t void
+#define d_select_t void
#endif /* KERNEL */
typedef void (irq_proc_t) (int irq);
@@ -125,7 +130,7 @@ struct _snddev_info {
d_read_t *read ;
d_write_t *write ;
d_ioctl_t *ioctl ;
- d_poll_t *poll ;
+ d_select_t *select ;
irq_proc_t *isr ;
snd_callback_t *callback;
@@ -152,6 +157,7 @@ struct _snddev_info {
/*
* whereas from here, parameters are set at runtime.
+ * io_base == 0 means that the board is not configured.
*/
int io_base ; /* primary I/O address for the board */
@@ -159,7 +165,6 @@ struct _snddev_info {
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 */
- int synth_base ; /* base for the synth */
int irq ;
int bd_id ; /* used to hold board-id info, eg. sb version,
@@ -202,6 +207,7 @@ struct _snddev_info {
#define SND_F_BUSY_DSP 0x20000000
#define SND_F_BUSY_DSP16 0x40000000
#define SND_F_BUSY_ANY 0x70000000
+#define SND_F_BUSY_SYNTH 0x80000000
/*
* the next two are used to allow only one pending operation of
* each type.
@@ -279,6 +285,8 @@ struct _snddev_info {
u_long interrupts; /* counter of interrupts */
u_long magic;
#define MAGIC(unit) ( 0xa4d10de0 + unit )
+ int synth_base ; /* base for the synth */
+ int synth_type ; /* type of synth */
void *device_data ; /* just in case it is needed...*/
} ;
@@ -434,14 +442,17 @@ typedef struct mixer_def mixer_tab[32][2];
#define MIX_NONE(name) MIX_ENT(name, 0,0,0,0, 0,0,0,0)
+/*
+ * some macros for debugging purposes
+ * DDB/DEB to enable/disable debugging stuff
+ * BVDDB to enable debugging when bootverbose
+ */
#define DDB(x) x /* XXX */
+#define BVDDB(x) if (bootverbose) x
#ifndef DEB
#define DEB(x)
#endif
-#ifndef DDB
-#define DDB(x)
-#endif
extern snddev_info pcm_info[NPCM_MAX] ;
extern snddev_info midi_info[NPCM_MAX] ;
@@ -458,6 +469,12 @@ int midiattach(struct isa_device * dev);
int synthattach(struct isa_device * dev);
/*
+ * functions in isa.c
+ */
+
+int isa_dmastatus(int chan);
+int isa_dmastop(int chan);
+/*
* DMA buffer calls
*/
@@ -483,7 +500,7 @@ int dsp_rdabort(snddev_info *d, int restart);
void dsp_wr_dmaupdate(snd_dbuf *b);
void dsp_rd_dmaupdate(snd_dbuf *b);
-d_poll_t sndpoll;
+d_select_t sndselect;
/*
* library functions (in sound.c)
@@ -527,9 +544,4 @@ int sb_getmixer (int io_base, u_int port);
* so it is better to make this the default behaviour
*/
-/*
- * the following flags are for PnP cards only and are undocumented
- */
-#define DV_PNP_SBCODEC 0x1
-
#endif
diff --git a/sys/i386/isa/snd/soundcard.h b/sys/i386/isa/snd/soundcard.h
index 726cbb5..2dc1a42 100644
--- a/sys/i386/isa/snd/soundcard.h
+++ b/sys/i386/isa/snd/soundcard.h
@@ -866,7 +866,7 @@ typedef struct copr_msg {
#define SOUND_DEVICE_LABELS { \
"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \
- "Mic ", "CD ", "Mix ", "Pcm2 ", "Rec ", "IGain", "OGain", \
+ "Mic ", "CD ", "Mix ", "Pcm2 ", "Rec ", "IGain", "OGain", \
"Line1", "Line2", "Line3", "Digital1", "Digital2", "Digital3", \
"PhoneIn", "PhoneOut", "Video", "Radio", "Monitor"}
OpenPOWER on IntegriCloud